/* =============================================================================
   DARSHI SHAH — PORTFOLIO BASE & SHARED PRIMITIVES
   Reset, fonts, the shared "atmosphere" (orbs / grain / fade / cursor orb), and
   reusable primitives (bold emphasis, serif accent, glass surface, buttons,
   cards, nav). Everything here reads from tokens.css — NO hardcoded values.

   Load order in every page:
     1) Google Fonts <link> (or self-hosted @font-face)
     2) tokens.css
     3) base.css
     4) page-specific styles (which should also only use tokens)
   ========================================================================== */

/* ───────────────────────── RESET ───────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }

html {
  /* smooth in-page anchor scrolling, but never override a reduced-motion user */
  scroll-behavior: smooth;
  /* avoid iOS auto-inflating text on rotate; respect user zoom */
  -webkit-text-size-adjust: 100%;
}

body {
  background: var(--color-paper);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-weight: var(--weight-body);
  font-size: var(--text-body);
  line-height: var(--leading-relaxed);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
  /* never let a stray wide element force a sideways scroll on small screens */
  max-width: 100vw;
}
::selection { background: var(--color-selection); }

a { color: inherit; }

/* media is fluid by default — scales down with the viewport, never overflows */
img, svg, video, canvas, iframe { max-width: 100%; height: auto; }
img, video { display: block; }


/* ───────────────────────── ACCESSIBILITY ─────────────────────────
   Site-wide rules for keyboard, screen-reader, and touch users. Every page
   inherits these — don't remove focus outlines in page CSS. */

/* Visible keyboard focus on anything interactive. Mouse clicks stay clean
   (:focus-visible only paints for keyboard / programmatic focus). */
:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring);
  outline-offset: var(--focus-ring-offset);
  border-radius: var(--radius-xs);
}
/* kill the default ring only where :focus-visible has replaced it */
:focus:not(:focus-visible) { outline: none; }

/* In-page anchors land below the header instead of flush to the top edge. */
:target { scroll-margin-top: var(--scroll-offset); }

/* Skip-to-content link: visually hidden until focused, first thing in <body>.
   Add  <a class="skip-link" href="#main">Skip to content</a>  as the first
   child of <body>, and give the main content  id="main". */
.skip-link {
  position: absolute;
  left: var(--space-4); top: calc(-1 * var(--space-12));
  z-index: var(--z-tooltip);
  padding: var(--space-2) var(--space-4);
  background: var(--color-ink);
  color: var(--color-surface);
  border-radius: var(--radius-xs);
  font-family: var(--font-display);
  font-size: var(--text-small);
  text-decoration: none;
  transition: top var(--dur-fast) var(--ease-std);
}
.skip-link:focus { top: var(--space-4); }

/* Screen-reader-only text (labels for icon-only controls, etc.). */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}

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


/* ───────────────────────── TYPOGRAPHY PRIMITIVES ───────────────────────── */
.h1, .h2, .h3 {
  font-family: var(--font-display);
  color: var(--color-ink);
  margin: 0;
}
.h1 { font-weight: var(--weight-hero); font-size: var(--text-hero); line-height: var(--leading-tight); letter-spacing: var(--tracking-hero); text-wrap: balance; }
.h2 { font-weight: var(--weight-h2);   font-size: var(--text-h2);   line-height: var(--leading-snug); letter-spacing: var(--tracking-tight); }
.h3 { font-weight: var(--weight-h2);   font-size: var(--text-h3);   line-height: var(--leading-snug); letter-spacing: var(--tracking-tight); }

.lead {
  font-size: var(--text-lead);
  line-height: var(--leading-relaxed);
  letter-spacing: var(--tracking-normal);
  color: var(--color-ink-sub);
  max-width: var(--maxw-text);
  text-wrap: pretty;
}
.body { color: var(--color-ink-sub); max-width: var(--maxw-text); }

/* Case-study intro paragraph: the lead that follows the case title spans the
   full content width instead of the narrow readable measure. Case studies only. */
.case-summary { max-width: var(--maxw-content); }

.eyebrow {
  font-family: var(--font-mono);
  font-size: var(--text-eyebrow);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--color-ink-muted);
}

/* publication / institution accent — Noto Serif italic, heavier */
.serif-em {
  font-family: var(--font-serif);
  font-weight: var(--weight-h2);
  font-style: italic;
  color: var(--color-ink);
  letter-spacing: 0;
}
.mono { font-family: var(--font-mono); }

/* display serif numeral — publication indices, pillar numbers (TYPOGRAPHY.md 1.3) */
.numeral {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: var(--weight-serif-display);
  font-size: var(--text-numeral);
  line-height: 1;
  letter-spacing: var(--tracking-numeral);
  color: var(--color-ink);
}


/* ───────────────────────── LAYOUT HELPERS ───────────────────────── */
.section {
  position: relative;
  z-index: var(--z-content);
  padding: var(--space-9) var(--gutter) var(--space-12);
}
.container { max-width: var(--maxw-content); margin: 0 auto; }

/* Readable text measures — three tiers (see tokens.css → LAYOUT). Any
   max-width on a text block uses one of these classes (or its token);
   never a raw px/ch value in page CSS. */
.measure         { max-width: var(--maxw-text); }
.measure--narrow { max-width: var(--maxw-text-narrow); }
.measure--wide   { max-width: var(--maxw-text-wide); }


/* ───────────────────────── STAGE & ATMOSPHERE ─────────────────────────
   Wrap a page (or a run of sections) in .stage and drop the .atmos block in
   first. The warm orb field, grain, and white fade then bleed across it. */
.stage { position: relative; }

.atmos {
  position: absolute; inset: 0;
  z-index: var(--z-atmos);
  overflow: hidden;
  pointer-events: none;
}
.orb {
  position: absolute;
  border-radius: 50%;
  filter: blur(var(--blur-orb));
  will-change: transform;
}
.orb--a {
  width: 62vmax; height: 62vmax;
  top: -20vmax; left: -16vmax;
  background: radial-gradient(circle at 28% 30%, var(--color-orb-a) 0%, color-mix(in oklab, var(--color-orb-a) 40%, transparent) 42%, transparent 70%);
  animation: orbA var(--orb-dur-a) linear infinite;
}
.orb--b {
  width: 54vmax; height: 54vmax;
  top: -12vmax; right: -18vmax;
  background: radial-gradient(circle at 70% 32%, var(--color-orb-b) 0%, color-mix(in oklab, var(--color-orb-b) 38%, transparent) 44%, transparent 72%);
  animation: orbB var(--orb-dur-b) linear infinite;
}
.orb--c {
  width: 58vmax; height: 58vmax;
  top: 52vh; left: 14vmax;
  background: radial-gradient(circle at 40% 56%, var(--color-orb-c) 0%, color-mix(in oklab, var(--color-orb-c) 36%, transparent) 46%, transparent 72%);
  animation: orbC var(--orb-dur-c) linear infinite;
}

@keyframes orbA {
  0%   { transform: translate(0,0)            rotate(0deg)   scale(1);    opacity: 0.82; }
  20%  { transform: translate(13vmax, 9vmax)  rotate(72deg)  scale(1.5);  opacity: 1;    }
  40%  { transform: translate(23vmax, 21vmax) rotate(144deg) scale(1.0);  opacity: 0.85; }
  60%  { transform: translate(9vmax, 29vmax)  rotate(216deg) scale(0.6);  opacity: 0.5;  }
  80%  { transform: translate(-7vmax, 13vmax) rotate(288deg) scale(1.08); opacity: 0.82; }
  100% { transform: translate(0,0)            rotate(360deg) scale(1);    opacity: 0.82; }
}
@keyframes orbB {
  0%   { transform: translate(0,0)             rotate(0deg)    scale(1);    opacity: 0.8;  }
  20%  { transform: translate(-15vmax, 8vmax)  rotate(-72deg)  scale(1.45); opacity: 1;    }
  40%  { transform: translate(-24vmax, 20vmax) rotate(-144deg) scale(0.95); opacity: 0.82; }
  60%  { transform: translate(-10vmax, -6vmax) rotate(-216deg) scale(0.6);  opacity: 0.48; }
  80%  { transform: translate(2vmax, -14vmax)  rotate(-288deg) scale(1.1);  opacity: 0.85; }
  100% { transform: translate(0,0)             rotate(-360deg) scale(1);    opacity: 0.8;  }
}
@keyframes orbC {
  0%   { transform: translate(0,0)             rotate(0deg)   scale(1);    opacity: 0.82; }
  20%  { transform: translate(16vmax, -12vmax) rotate(60deg)  scale(1.5);  opacity: 1;    }
  40%  { transform: translate(24vmax, -22vmax) rotate(140deg) scale(1.0);  opacity: 0.82; }
  60%  { transform: translate(8vmax, 8vmax)    rotate(210deg) scale(0.58); opacity: 0.5;  }
  80%  { transform: translate(-14vmax, 4vmax)  rotate(300deg) scale(1.08); opacity: 0.84; }
  100% { transform: translate(0,0)             rotate(360deg) scale(1);    opacity: 0.82; }
}

/* cursor-following orb — accent colour, denser core, less blur */
.orb--cursor {
  position: fixed;
  top: 0; left: 0;
  width: 12vmax; height: 12vmax;
  margin: -6vmax 0 0 -6vmax;
  border-radius: 50%;
  z-index: var(--z-cursor);
  filter: blur(var(--blur-cursor));
  opacity: 0.425;
  pointer-events: none;
  will-change: transform;
  transform: translate(-50vw, -50vh);
  background: radial-gradient(circle at 50% 46%,
    var(--color-accent) 0%,
    color-mix(in oklab, var(--color-accent) 78%, transparent) 26%,
    color-mix(in oklab, var(--color-accent) 32%, transparent) 52%,
    transparent 70%);
}

/* one continuous fade resolving to white below the hero */
.fade {
  position: absolute; left: 0; right: 0; top: 0;
  height: 100%; z-index: var(--z-fade); pointer-events: none;
  background: linear-gradient(
    to bottom,
    rgba(255,255,255,0) 0vh,
    rgba(255,255,255,0) 24vh,
    rgba(255,255,255,0.45) 64vh,
    rgba(255,255,255,0.82) 96vh,
    var(--color-surface) 124vh
  );
}

/* film grain to break gradient banding + warm the page */
.grain {
  position: absolute; inset: 0; z-index: var(--z-grain); pointer-events: none;
  opacity: var(--grain-opacity); mix-blend-mode: multiply;
  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='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* photographic vignette concentrated in the upper area */
.vignette {
  position: absolute; inset: 0; z-index: var(--z-atmos); pointer-events: none;
  mix-blend-mode: multiply;
  background: radial-gradient(125% 115% at 50% 30%, transparent 46%, rgba(74,48,22, var(--vignette-opacity)) 100%);
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 30%, transparent 70%);
          mask-image: linear-gradient(to bottom, #000 0%, #000 30%, transparent 70%);
}

/* Inner pages: freeze the three background blobs so they don't drift over
   the content. The cursor orb keeps moving; only the home page animates. */
body.static-atmos .orb--a,
body.static-atmos .orb--b,
body.static-atmos .orb--c {
  animation: none;
}
/* soften the two foreground blobs just slightly on inner pages */
body.static-atmos .orb--a,
body.static-atmos .orb--b {
  opacity: 0.85;
}

/* Case studies: swap the three animated ambient blobs for a static captured
   image of the same field. Cheaper than animating three blur(100px) orbs on
   these long, image-heavy pages; the cursor orb stays live. First-fold only —
   the image sits at the top and the .fade resolves to white below it.
   (url is relative to THIS file at the repo root — not "../Assets" like the
   retired design-system/base.css copy that lived one level down.) */
body.static-orbs .orb--a,
body.static-orbs .orb--b,
body.static-orbs .orb--c { display: none; }
body.static-orbs .atmos {
  background-image: url("Assets/atmosphere/orb-field.jpg");
  background-repeat: no-repeat;
  background-position: top center;
  background-size: 100% auto;
}

@media (prefers-reduced-motion: reduce) {
  .orb { animation: none !important; }
  .orb--cursor { display: none; }
}


/* ───────────────────────── ENTRANCE ANIMATION ───────────────────────── */
.rise { opacity: 0; animation: rise var(--dur-slow) var(--ease-rise) both; }
.rise-1 { animation-delay: 0.1s; }
.rise-2 { animation-delay: 0.3s; }
.rise-3 { animation-delay: 0.45s; }
.rise-4 { animation-delay: 0.5s; }
.rise-5 { animation-delay: 0.62s; }
@keyframes rise {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .rise { animation: none !important; opacity: 1 !important; }
}


/* ───────────────────────── NAV / WORDMARK ───────────────────────── */
.topbar {
  position: sticky; top: 0; z-index: 40;
  width: 100vw;                                                   /* full-bleed out of the content column */
  margin: 0 calc(50% - 50vw);                                     /* full-bleed: span the viewport (like the home hero nav) */
  padding: 36px 56px;                              /* the original home-page nav bar: 36px tall, content inset 56px */
  transition: transform .4s cubic-bezier(.2,.7,.2,1);
  will-change: transform;
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-5);
}
.topbar.nav--scrolled {
  background: color-mix(in oklab, var(--color-paper) 82%, transparent);
  backdrop-filter: saturate(140%) blur(14px);
  -webkit-backdrop-filter: saturate(140%) blur(14px);
  box-shadow: 0 1px 0 rgba(31,31,31,0.06);
}
.topbar.nav--hidden { transform: translateY(-110%); }
.wordmark {
  font-family: var(--font-display);
  font-weight: var(--weight-head);
  font-size: var(--text-body);
  letter-spacing: var(--tracking-tight);
  color: var(--color-ink);
  text-decoration: none;
}
.nav { display: flex; align-items: center; gap: var(--space-6); }
.nav a {
  display: inline-flex; align-items: center; gap: 7px;
  font-family: var(--font-display);
  font-weight: var(--weight-head);
  font-size: var(--text-small);
  letter-spacing: var(--tracking-tight);
  color: var(--color-ink-sub);
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease-std);
}
.nav a svg { width: 15px; height: 15px; stroke: currentColor; stroke-width: 1.6; fill: none; stroke-linecap: round; stroke-linejoin: round; opacity: 0.85; }
.nav a:hover { color: var(--color-ink); }


/* ───────────────────────── BUTTONS ───────────────────────── */
.btn {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-family: var(--font-display);
  font-weight: var(--weight-head);
  font-size: var(--text-small);
  letter-spacing: var(--tracking-tight);
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-pill);
  border: 1px solid transparent;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-std), color var(--dur-fast) var(--ease-std), border-color var(--dur-fast) var(--ease-std);
}
.btn--primary { background: var(--color-ink); color: var(--color-surface); }
.btn--primary:hover { background: var(--color-ink-strong); }
.btn--ghost { background: transparent; color: var(--color-ink); border-color: var(--color-card-border); }
.btn--ghost:hover { border-color: var(--color-ink); }


/* ───────────────────────── GLASS SURFACE ─────────────────────────
   Liquid-glass panel (the pronunciation tooltip uses this). Reusable for
   callouts, popovers, pull-quotes. */
.glass {
  background: color-mix(in oklab, var(--color-accent) 16%, rgba(255,255,255,0.40));
  -webkit-backdrop-filter: blur(var(--blur-glass)) saturate(1.7);
          backdrop-filter: blur(var(--blur-glass)) saturate(1.7);
  border: 1px solid rgba(255,255,255,0.55);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-glass);
  position: relative;
  overflow: hidden;
}
.glass::before {
  content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  background: linear-gradient(135deg, rgba(255,255,255,0.55), rgba(255,255,255,0) 42%);
}


/* ───────────────────────── CONTENT IMAGES ─────────────────────────
   Every standalone content image (case-study shots, about-page figures) sits
   in an .img-frame. The frame owns the corner radius and clips the image; a
   width variant declares how much of the content column it spans. Never set
   border-radius or width on an image in page CSS — pick a variant instead.

     <figure class="img-frame img--3q">
       <img src="…" alt="…" loading="lazy">
     </figure>

   Radius tiers:  .img-frame (12px, the rounded case-study look)
                  .img-frame--sm (4px, subtle — small inline shots)
   Width tiers:   .img--full (100% of the column)
                  .img--3q  (75%, centred; relaxes to full width on phones)
   Fit:           default — image keeps its aspect ratio, frame grows to fit.
                  .img-frame--cover — crop to fill a frame whose height the
                  layout sets (an aspect-ratio or fixed height on the figure). */
.img-frame {
  margin: 0;                          /* <figure> ships with browser margins */
  border-radius: var(--radius-img);
  overflow: hidden;
}
.img-frame--sm { border-radius: var(--radius-img-sm); }
.img-frame img,
.img-frame picture { display: block; width: 100%; height: auto; border-radius: inherit; }
.img-frame--cover picture { height: 100%; }
.img-frame--cover img     { height: 100%; object-fit: cover; }

.img--full { width: 100%; }
.img--3q   { width: var(--img-width-3q); margin-inline: auto; }


/* ─────────────────── CASE-STUDY FIGURE WIDTH VARIANTS ───────────────────
   Case studies use their own `.figure` wrapper (defined per-page): the block
   spans the full content column and `.figure img/video { width:100% }` fills
   it, while body TEXT is capped at the readable measure. Two width variants
   for standalone figures / GIFs inside a case study:

     .figure--full   — spans the whole content column (the big product GIFs).
                        Already defined per-page; the canonical full-bleed.
     .figure--small  — caps the figure at the body-text measure (--maxw-text,
                        660px) and sits FLUSH-LEFT, so the image lines up with
                        the paragraph column instead of the wider container.

   Only `--small` lives here (shared, so every case study gets it from one
   place). It uses a two-class selector on purpose: each page defines `.figure`
   in its own <style> AFTER base.css loads, so a single-class rule here would
   lose the cascade. `.figure.figure--small` (specificity 0-2-0) beats the
   page's `.figure` (0-1-0) regardless of source order. The inner
   `.figure img/video { width:100% }` already fills the capped block, and at
   viewports below the measure it is naturally full-width. */
.figure.figure--small {
  max-width: var(--maxw-text);
  margin-inline-start: 0;      /* flush-left with the body-text column … */
  margin-inline-end: auto;     /* … not centred in the wider container   */
}


/* ───────────────────────── CARDS ───────────────────────── */
.card-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--grid-gap);
  max-width: var(--maxw-content);
}
.card { display: flex; flex-direction: column; }
.card-shot {
  aspect-ratio: 16 / 11;
  border-radius: var(--radius-img-sm);   /* subtle image tier — see tokens.css */
  border: 1px solid var(--color-card-border);
  background-color: var(--color-card);
  background-image: repeating-linear-gradient(135deg, var(--color-hatch) 0 1px, transparent 1px 11px);
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
.card-shot img { width: 100%; height: 100%; object-fit: cover; }
.card-meta { padding: var(--space-4) 2px 0; }
.card-title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--weight-head);
  font-size: var(--text-title);
  letter-spacing: var(--tracking-tight);
  color: var(--color-ink);
}
.card-sub {
  margin: 6px 0 0;
  font-weight: var(--weight-body);
  font-size: var(--text-small);
  color: var(--color-ink-sub);
}


/* ───────────────────────── BOLD EMPHASIS ─────────────────────────
   Wrap a word or phrase in <strong> to emphasise it inside body copy or a
   heading. Weight comes from the type scale (semibold), never a literal. This
   replaces the old marker-highlight primitive. */
strong, b {
  font-weight: var(--weight-h2);
}


/* ───────────────────────── RESPONSIVE ─────────────────────────
   880px is the primary mobile break for the whole site (see tokens.css
   BREAKPOINTS). Reuse this literal width in page-level @media blocks so
   everything collapses at the same point. */
@media (max-width: 880px) {
  .topbar  { padding: 28px 32px; gap: 18px; }   /* original home-nav mobile padding */
  .nav     { gap: var(--space-4); }
  .section { padding: var(--space-7) var(--gutter-mobile) var(--space-11); }
}

/* Nav collapses to icon-only below iPad-portrait width (≥768px keeps text labels,
   so every iPad shows the full nav and only phones / small tablets get icons).
   Matches the home, publications and how-i-use-ai pages, which collapse at 760. */
@media (max-width: 760px) {
  /* hide the label VISUALLY but keep it for screen readers, so each link still
     has an accessible name. */
  .nav a span {
    position: absolute; width: 1px; height: 1px;
    padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0,0,0,0); white-space: nowrap; border: 0;
  }
  .nav a { min-width: var(--tap-min); min-height: var(--tap-min); justify-content: center; }
}

/* Phone: multi-column grids stack. 2-up card grids stay 2-up through tablet
   (better use of iPad width) and collapse here, matching the home projects grid. */
@media (max-width: 600px) {
  .card-grid { grid-template-columns: 1fr; gap: var(--space-8); }

  /* 3/4-width images rarely survive phone widths — go full column. */
  .img--3q { width: 100%; }
}

/* Touch-pointer devices: every button / nav link gets a comfortable hit area,
   regardless of viewport width. */
@media (pointer: coarse) {
  .btn, .nav a { min-height: var(--tap-min); }

  /* PERF: the ambient orbs animate a huge blur(var(--blur-orb)) layer every
     frame. On mobile GPUs that pins the compositor during load and scroll and
     is the main cause of the page feeling slow. Freeze them (the static warm
     gradient stays), drop the blur radius, and release the promoted layer.
     The cursor orb is meaningless without a mouse, so remove it entirely
     (cursor-orb.js also bails on coarse pointers). */
  .orb { animation: none !important; filter: blur(60px) !important; will-change: auto !important; }
  .orb--cursor { display: none !important; }
}
