/* ============================================================
   site.css — generated by tools/build-css.mjs. DO NOT EDIT.
   Edit the source files below, re-run the script, and bump the
   ?v= query on the site.css <link> tags.
   Sources: progressive-blur.css, sticky-nav.css, figtree.css, page-transitions.css, marquee.css, footer.css, interactions.css, frame-margins.css, mobile.css
   ============================================================ */


/* ================== source: progressive-blur.css ================== */
/* ============================================================
   Progressive blur — fixed strip at the bottom of the viewport.
   3 stacked layers (was 8) — each backdrop-filter layer forces a
   separate GPU compositing pass over the page, so fewer layers
   means dramatically cheaper scrolling. Wide overlapping
   bell-shaped masks cross-fade the blur amounts so the ramp
   still reads as continuous across the 96px strip.
   The bottom layer anchors fully opaque to 100% so the maximum
   blur reaches the viewport edge.
   ============================================================ */

.pb-stack {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  height: 96px;
  pointer-events: none;
  z-index: 60;
  isolation: isolate;
}

.pb-stack > .pb-layer {
  position: absolute;
  inset: 0;
}

/* --- Layer 1 — gentle blur easing in from the top edge --- */
.pb-stack > .pb-layer:nth-child(1) {
  -webkit-backdrop-filter: blur(0.5px);
  backdrop-filter: blur(0.5px);
  -webkit-mask-image: linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0) 0%,
    rgba(0, 0, 0, 0.5) 8%,
    rgba(0, 0, 0, 1) 16%,
    rgba(0, 0, 0, 1) 26%,
    rgba(0, 0, 0, 0.5) 36%,
    rgba(0, 0, 0, 0) 48%
  );
  mask-image: linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0) 0%,
    rgba(0, 0, 0, 0.5) 8%,
    rgba(0, 0, 0, 1) 16%,
    rgba(0, 0, 0, 1) 26%,
    rgba(0, 0, 0, 0.5) 36%,
    rgba(0, 0, 0, 0) 48%
  );
}

/* --- Layer 2 — medium blur through the middle of the strip --- */
.pb-stack > .pb-layer:nth-child(2) {
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
  -webkit-mask-image: linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0) 28%,
    rgba(0, 0, 0, 0.5) 40%,
    rgba(0, 0, 0, 1) 52%,
    rgba(0, 0, 0, 1) 64%,
    rgba(0, 0, 0, 0.5) 76%,
    rgba(0, 0, 0, 0) 88%
  );
  mask-image: linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0) 28%,
    rgba(0, 0, 0, 0.5) 40%,
    rgba(0, 0, 0, 1) 52%,
    rgba(0, 0, 0, 1) 64%,
    rgba(0, 0, 0, 0.5) 76%,
    rgba(0, 0, 0, 0) 88%
  );
}

/* --- Layer 3 — anchors the strongest blur to the viewport edge --- */
.pb-stack > .pb-layer:nth-child(3) {
  -webkit-backdrop-filter: blur(6.5px);
  backdrop-filter: blur(6.5px);
  -webkit-mask-image: linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0) 58%,
    rgba(0, 0, 0, 0.5) 78%,
    rgba(0, 0, 0, 1) 100%
  );
  mask-image: linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0) 58%,
    rgba(0, 0, 0, 0.5) 78%,
    rgba(0, 0, 0, 1) 100%
  );
}

/* Soft white tint — eases the blurred content toward the page bg.
   Curve uses extra stops so it doesn't kink. */
.pb-stack > .pb-tint {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to bottom,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.02) 30%,
    rgba(255, 255, 255, 0.07) 55%,
    rgba(255, 255, 255, 0.15) 80%,
    rgba(255, 255, 255, 0.25) 100%
  );
}

@media print {
  .pb-stack { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .pb-stack > .pb-layer:nth-child(1),
  .pb-stack > .pb-layer:nth-child(2) {
    display: none;
  }
  .pb-stack > .pb-layer:nth-child(3) {
    -webkit-backdrop-filter: blur(5px);
    backdrop-filter: blur(5px);
  }
}


/* ================== source: sticky-nav.css ================== */
/* ============================================================
   Sticky nav pill — appears after the original Framer nav
   scrolls past the viewport top. Floats centered at the top
   with glass treatment, morphs in smoothly.

   Motion rules:
   - GPU-only (transform + opacity + filter:blur)
   - Custom ease-out cubic-bezier (stronger than CSS default)
   - Asymmetric enter (240ms) / exit (180ms)
   - transform-origin: top center (morphs from where the original nav was)
   - scale starts at 0.96, never 0
   - blur(2px) during transit masks the visual swap
   - prefers-reduced-motion: opacity only
   - hover gated to fine pointer
   ============================================================ */

:root {
  --snav-ease: cubic-bezier(0.23, 1, 0.32, 1);
}

.snav {
  position: fixed;
  top: 14px;
  left: 50%;
  z-index: 110;
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 6px 8px 6px 6px;
  border-radius: 999px;
  background-color: rgba(255, 255, 255, 0.62);
  -webkit-backdrop-filter: blur(18px) saturate(1.6);
  backdrop-filter: blur(18px) saturate(1.6);
  box-shadow:
    0 0.5px 0 rgba(255, 255, 255, 0.7) inset,
    0 -0.5px 0 rgba(0, 0, 0, 0.05) inset,
    0 8px 24px -12px rgba(0, 0, 0, 0.18),
    0 2px 6px -3px rgba(0, 0, 0, 0.08);
  /* Hidden state */
  opacity: 0;
  transform: translate3d(-50%, -10px, 0) scale(0.96);
  filter: blur(2px);
  pointer-events: none;
  transform-origin: top center;
  /* Exit transition (180ms, snappy) */
  transition:
    opacity 180ms var(--snav-ease),
    transform 180ms var(--snav-ease),
    filter 180ms var(--snav-ease),
    background-color 320ms var(--snav-ease),
    box-shadow 320ms var(--snav-ease);
}

.snav[data-visible="1"] {
  opacity: 1;
  transform: translate3d(-50%, 0, 0) scale(1);
  filter: blur(0);
  pointer-events: auto;
  /* Enter transition (240ms, more presence) */
  transition:
    opacity 240ms var(--snav-ease),
    transform 240ms var(--snav-ease),
    filter 240ms var(--snav-ease),
    background-color 320ms var(--snav-ease),
    box-shadow 320ms var(--snav-ease);
}

/* Subtle top sheen on the pill itself (glass craft) */
.snav::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.45) 0%,
    rgba(255, 255, 255, 0) 45%
  );
  mix-blend-mode: overlay;
  opacity: 0.9;
}

/* Logo button */
.snav-logo {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 999px;
  color: #1a1a1a;
  text-decoration: none;
  transition: background-color 160ms var(--snav-ease),
              transform 140ms var(--snav-ease),
              color 320ms var(--snav-ease);
  flex-shrink: 0;
  position: relative;
  z-index: 1;
}
.snav-logo svg {
  width: 34px;
  height: 34px;
  display: block;
}
.snav-logo:active { transform: scale(0.94); }

/* Vertical divider after the logo */
.snav-sep {
  width: 1px;
  height: 16px;
  margin: 0 4px;
  background: rgba(0, 0, 0, 0.08);
  flex-shrink: 0;
  transition: background-color 320ms var(--snav-ease);
}

/* Nav links */
.snav-link {
  display: inline-block;
  padding: 8px 12px;
  border-radius: 999px;
  font-family: "Figtree", "Space Grotesk", "Space Grotesk Placeholder", sans-serif;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.3px;
  color: rgba(0, 0, 0, 0.62);
  text-decoration: none;
  background: transparent;
  position: relative;
  z-index: 1;
  transition:
    color 160ms var(--snav-ease),
    background-color 160ms var(--snav-ease),
    transform 140ms var(--snav-ease);
  white-space: nowrap;
}
.snav-link[aria-current="page"] {
  color: #e8503a;
  font-weight: 700;
}
.snav-link:active { transform: scale(0.96); }

/* ------------------------------------------------------------------
   Top nav — consistent inactive dimming across every page.
   Home dims inactive tab wrappers to .5 (so the dark link text reads
   gray) and brings them to full on hover; inner pages shipped them at
   full opacity (black). Replicate home everywhere. "Inactive" = a
   wrapper whose link isn't the current page — which leaves the active
   tab (accent <p>, or home's <a data-…current>) untouched.
   ------------------------------------------------------------------ */
nav.framer-WLHpg .framer-1gxxlpg > div { transition: opacity 0.2s ease; }
nav.framer-WLHpg .framer-1gxxlpg > div:has(a:not([data-framer-page-link-current])) {
  opacity: 0.5 !important;
}
nav.framer-WLHpg .framer-1gxxlpg > div:has(a:not([data-framer-page-link-current])):hover {
  opacity: 1 !important;
}

/* ------------------------------------------------------------------
   Global nav order — Work · AI Labs · About · Photography · YouTube ·
   Substack, on every page. Framer bakes a per-variant `order` keyed to
   framer-classes that rotate per page (and AI Labs shares Substack's
   class, so Framer pins it late). Override by href so the row is fixed
   regardless of Framer's classes or DOM order. The current-page item
   renders as a link-less <p> (no href to match), so pin it per desktop
   variant. #main + !important beats Framer's (unlayered) variant CSS.
   (/ai-labs's own #main pins in ai-labs.css set its current <p> to 2.)
   ------------------------------------------------------------------ */
#main nav.framer-WLHpg .framer-1gxxlpg > div:has(a[href="./"])            { order: 1 !important; }
#main nav.framer-WLHpg .framer-1gxxlpg > div:has(a[href="./ai-labs"])     { order: 2 !important; }
#main nav.framer-WLHpg .framer-1gxxlpg > div:has(a[href="./about"])       { order: 3 !important; }
#main nav.framer-WLHpg .framer-1gxxlpg > div:has(a[href="./photography"]) { order: 4 !important; }
#main nav.framer-WLHpg .framer-1gxxlpg > div:has(a[href="./youtube"])     { order: 5 !important; }
#main nav.framer-WLHpg .framer-1gxxlpg > div:has(a[href="./#substack"])   { order: 6 !important; }
#main nav[data-framer-name="About"] .framer-1gxxlpg > div:has(p):not(:has(a))       { order: 3 !important; }
#main nav[data-framer-name="Photography"] .framer-1gxxlpg > div:has(p):not(:has(a)) { order: 4 !important; }
#main nav[data-framer-name="YouTube"] .framer-1gxxlpg > div:has(p):not(:has(a))     { order: 5 !important; }

/* ------------------------------------------------------------------
   "AI Labs" tab — a "new" cue. CSS-only (::after), scoped to the link
   in the top nav + sticky pill, AND to the active <p> on /ai-labs
   itself (.al-nav-current) so the badge persists there too.
   (The /skills page + its badge were retired 2026-07 — the skills
   list now lives in a tab on /ai-labs.)
   ------------------------------------------------------------------ */
nav.framer-WLHpg a[href="./ai-labs"]::after,
nav.framer-WLHpg .al-nav-current::after,
.snav .snav-link[href="./ai-labs"]::after {
  content: "new";
  font-size: 8px;
  line-height: 1;
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  vertical-align: super;
  margin-left: 3px;
  color: #e8503a;       /* brand/accent, full opacity */
  -webkit-text-fill-color: #e8503a;
  opacity: 1;
}

/* Keep that "new" badge BRIGHT while the AI Labs tab is inactive. The inactive-item
   dim (block above) greys the whole item wrapper via opacity, and opacity on a parent
   can't be undone by a child — so the ::after badge gets muted too. Fix: undim just
   this wrapper, and move the dim onto the LABEL TEXT via a translucent colour (which
   the badge overrides with its own colour). Net: page name stays dimmed, badge is full
   brand-red. Hover restores the full label. Only the top nav (#main … framer-WLHpg) is
   affected — the sticky pill dims via colour already, so its badge was never muted. */
#main nav.framer-WLHpg .framer-1gxxlpg > div:has(a[href="./ai-labs"]) { opacity: 1 !important; }
#main nav.framer-WLHpg .framer-1gxxlpg > div a[href="./ai-labs"] {
  color: rgba(30, 30, 30, 0.5);
  -webkit-text-fill-color: rgba(30, 30, 30, 0.5);
  transition: color 0.2s ease, -webkit-text-fill-color 0.2s ease;
}

@media (hover: hover) and (pointer: fine) {
  .snav-logo:hover { background-color: rgba(0, 0, 0, 0.04); }
  .snav-link:hover { background-color: rgba(0, 0, 0, 0.04); color: rgba(0, 0, 0, 1); }
  #main nav.framer-WLHpg .framer-1gxxlpg > div:has(a[href="./ai-labs"]):hover a[href="./ai-labs"] {
    color: rgba(30, 30, 30, 1);
    -webkit-text-fill-color: rgba(30, 30, 30, 1);
  }
}

/* Hide on small screens — the original nav still works on mobile */
@media (max-width: 720px) {
  .snav { display: none !important; }
}

/* Reduced motion: opacity-only, no transform, no blur */
@media (prefers-reduced-motion: reduce) {
  .snav {
    transform: translate3d(-50%, 0, 0);
    filter: none;
    transition: opacity 160ms linear;
  }
  .snav[data-visible="1"] {
    transform: translate3d(-50%, 0, 0);
    filter: none;
    transition: opacity 160ms linear;
  }
}


/* ============================================================
   Auto dark-mode — engaged by sticky-nav.js (data-theme="dark")
   while the pill floats over the dark "AI Lab & Experiments"
   section, so the glass reads correctly on dark surfaces.
   ============================================================ */
.snav[data-theme="dark"] {
  background-color: rgba(28, 28, 30, 0.72);
  box-shadow:
    0 0.5px 0 rgba(255, 255, 255, 0.10) inset,
    0 -0.5px 0 rgba(0, 0, 0, 0.45) inset,
    0 10px 28px -12px rgba(0, 0, 0, 0.55),
    0 2px 6px -3px rgba(0, 0, 0, 0.35);
}
.snav[data-theme="dark"]::before {
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.12) 0%,
    rgba(255, 255, 255, 0) 45%
  );
  opacity: 0.6;
}
.snav[data-theme="dark"] .snav-sep {
  background: rgba(255, 255, 255, 0.14);
}
.snav[data-theme="dark"] .snav-logo {
  color: #fff;
}
.snav[data-theme="dark"] .snav-link {
  color: rgba(255, 255, 255, 0.95) !important;
}
.snav[data-theme="dark"] .snav-link[aria-current="page"] {
  color: #ff6f59 !important;
}
@media (hover: hover) and (pointer: fine) {
  .snav[data-theme="dark"] .snav-logo:hover { background-color: rgba(255, 255, 255, 0.08); }
  .snav[data-theme="dark"] .snav-link:hover { background-color: rgba(255, 255, 255, 0.08); color: #fff !important; }
}


/* ============================================================
   Mobile menu — hamburger injected into the top "Phone" nav by
   sticky-nav.js (mountMobileMenu). The button lives inside the
   phone-only nav variant, so it's automatically hidden on desktop
   (≥1440px) where the full horizontal nav shows.
   ============================================================ */
.mnav-host { position: relative; }   /* containing block for the absolute button */

.mnav-burger {
  position: absolute;
  top: 50%;
  right: 22px;
  transform: translateY(-50%);
  z-index: 5;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: #1a1a1a;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
/* solo mount (pages with only the desktop nav variant, e.g.
   work-physicswallah): burger exists in the always-visible nav, so gate
   it to the widths where mobile.css hides the desktop links */
.mnav-host-solo .mnav-burger { display: none; }
@media (max-width: 809.98px) {
  .mnav-host-solo .mnav-burger { display: inline-flex; }
}

.mnav-box { position: relative; width: 22px; height: 14px; }
.mnav-box > span {
  position: absolute;
  left: 0;
  right: 0;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
  transition: transform 0.28s var(--snav-ease), opacity 0.18s var(--snav-ease), top 0.28s var(--snav-ease);
}
.mnav-box > span:nth-child(1) { top: 0; }
.mnav-box > span:nth-child(2) { top: 6px; }
.mnav-box > span:nth-child(3) { top: 12px; }
.mnav-burger.is-open .mnav-box > span:nth-child(1) { top: 6px; transform: rotate(45deg); }
.mnav-burger.is-open .mnav-box > span:nth-child(2) { opacity: 0; }
.mnav-burger.is-open .mnav-box > span:nth-child(3) { top: 6px; transform: rotate(-45deg); }

/* dim/blur backdrop */
.mnav-overlay {
  position: fixed;
  inset: 0;
  z-index: 120;
  background: rgba(20, 20, 22, 0.30);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
  opacity: 0;
  transition: opacity 0.24s var(--snav-ease);
}
.mnav-overlay.is-open { opacity: 1; }

/* dropdown card */
.mnav-panel {
  position: fixed;
  top: 14px;
  right: 14px;
  z-index: 130;
  min-width: 224px;
  max-width: calc(100vw - 28px);
  padding: 8px;
  background: rgba(255, 255, 255, 0.92);
  -webkit-backdrop-filter: blur(20px) saturate(1.6);
  backdrop-filter: blur(20px) saturate(1.6);
  border-radius: 0;                       /* sharp corners — consistent with the rest of the site */
  box-shadow:
    0 0.5px 0 rgba(255, 255, 255, 0.7) inset,
    0 12px 40px -12px rgba(0, 0, 0, 0.32),
    0 2px 8px -4px rgba(0, 0, 0, 0.12);
  transform-origin: top right;
  opacity: 0;
  transform: translateY(-8px) scale(0.96);
  transition: opacity 0.24s var(--snav-ease), transform 0.24s var(--snav-ease);
}
.mnav-panel.is-open { opacity: 1; transform: translateY(0) scale(1); }

.mnav-list { list-style: none; margin: 0; padding: 0; }
.mnav-link {
  display: block;
  padding: 13px 16px;
  border-radius: 0;
  font-family: "Figtree", "Space Grotesk", sans-serif;
  font-size: 16px;
  font-weight: 500;
  letter-spacing: 0.2px;
  color: #1a1a1a;
  text-decoration: none;
  transition: background-color 0.16s var(--snav-ease), color 0.16s var(--snav-ease);
}
.mnav-link[aria-current="page"] { color: #e8503a; font-weight: 700; }
.mnav-link[href="./ai-labs"]::after {
  content: "new";
  font-size: 9px;
  line-height: 1;
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  vertical-align: super;
  margin-left: 5px;
  color: #e8503a;
  -webkit-text-fill-color: #e8503a;
}
@media (hover: hover) { .mnav-link:hover { background: rgba(0, 0, 0, 0.05); } }
.mnav-link:active { background: rgba(0, 0, 0, 0.08); }

.mnav-overlay[hidden], .mnav-panel[hidden] { display: none !important; }
html.mnav-lock { overflow: hidden; }

@media (prefers-reduced-motion: reduce) {
  .mnav-box > span,
  .mnav-overlay,
  .mnav-panel { transition-duration: 0.001ms; }
  .mnav-panel { transform: none; }
  .mnav-panel.is-open { transform: none; }
}

/* Top nav: extend the 1px bottom border out to the edge rails while the nav content
   stays centered — but ONLY at the widths where the rails are actually shown
   (>=1480px; see frame-margins.css). Below that the rails are hidden and the border
   must stay at its natural centered 1200px width: calc(100vw - 248px) would be
   NARROWER than the content there, floating a short line inside it. Framer draws the
   border on ::after (absolute, inset:0 => 1200px); re-anchor that strip to meet the
   124px inner stroke rail on each side, centered. */
@media (min-width: 1480px) {
  #main nav.framer-WLHpg { overflow: visible; }
  #main nav.framer-WLHpg::after {
    left: 50%;
    right: auto;
    width: calc(100vw - 248px);
    transform: translateX(-50%);
  }

  /* Also extend the nav border LINE element (.framer-f81d2g, the actual 8% border)
     to full width so the visible line is one uniform colour edge-to-edge (it plus the
     ::after together read as the same shade over content and out in the margins). */
  #main nav.framer-WLHpg .framer-f81d2g {
    left: 50%;
    right: auto;
    width: calc(100vw - 248px);
    transform: translateX(-50%);
  }
}


/* ================== source: figtree.css ================== */
/* ============================================================
   Figtree global font override
   ------------------------------------------------------------
   Loads Figtree from Google Fonts and routes every existing
   typeface reference (Inter, Space Grotesk, Switzer, JetBrains
   Mono, Geist, Geist Mono, Instrument Serif, Mona Sans, Kalam,
   Kode Mono, Satoshi, Wasted Year, and their *Placeholder*
   metric-shim siblings) through Figtree without touching the
   inline Framer styles in the HTML.

   How it works:
   1. Self-hosted @font-face blocks load the Figtree variable
      font (300–900, italic + roman) from /assets/fonts/figtree
      so the family is available everywhere — no external
      Google Fonts request.
   2. @font-face aliases redeclare each existing family with
      src: local("Figtree"). Because these declarations come
      after the inline <style data-framer-font-css> block, the
      browser uses them when resolving font-family lookups —
      so "Inter", "Space Grotesk", etc. all render Figtree.
   3. A * { font-family: ... !important } rule + Framer CSS
      variable overrides act as a belt-and-braces safety net
      for any inline style="font-family:..." declarations.
   ============================================================ */

/* ---- Figtree variable font, self-hosted (was: Google Fonts @import) ---- */
/* latin */
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 300 900;
  font-display: swap;
  src: url('/assets/fonts/figtree/figtree-roman-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 300 900;
  font-display: swap;
  src: url('/assets/fonts/figtree/figtree-roman-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* italic latin */
@font-face {
  font-family: 'Figtree';
  font-style: italic;
  font-weight: 300 900;
  font-display: swap;
  src: url('/assets/fonts/figtree/figtree-italic-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* italic latin-ext */
@font-face {
  font-family: 'Figtree';
  font-style: italic;
  font-weight: 300 900;
  font-display: swap;
  src: url('/assets/fonts/figtree/figtree-italic-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ---- Font-family aliases: redirect every existing family to Figtree ---- */
/* The browser picks the LAST matching @font-face in document order, so as
   long as figtree.css is linked after Framer's inline font block these win. */

@font-face { font-family: "Inter";                       src: local("Figtree"), local("Figtree-Regular"); font-weight: 100 900; font-style: normal; font-display: swap; }
@font-face { font-family: "Inter";                       src: local("Figtree Italic"), local("Figtree-Italic"); font-weight: 100 900; font-style: italic; font-display: swap; }
@font-face { font-family: "Inter Placeholder";           src: local("Figtree"), local("Figtree-Regular"); font-weight: 100 900; font-style: normal; font-display: swap; }
@font-face { font-family: "Inter Placeholder";           src: local("Figtree Italic"), local("Figtree-Italic"); font-weight: 100 900; font-style: italic; font-display: swap; }

@font-face { font-family: "Space Grotesk";               src: local("Figtree"), local("Figtree-Regular"); font-weight: 100 900; font-style: normal; font-display: swap; }
@font-face { font-family: "Space Grotesk";               src: local("Figtree Italic"), local("Figtree-Italic"); font-weight: 100 900; font-style: italic; font-display: swap; }
@font-face { font-family: "Space Grotesk Placeholder";   src: local("Figtree"), local("Figtree-Regular"); font-weight: 100 900; font-style: normal; font-display: swap; }
@font-face { font-family: "Space Grotesk Placeholder";   src: local("Figtree Italic"), local("Figtree-Italic"); font-weight: 100 900; font-style: italic; font-display: swap; }

@font-face { font-family: "Switzer";                     src: local("Figtree"), local("Figtree-Regular"); font-weight: 100 900; font-style: normal; font-display: swap; }
@font-face { font-family: "Switzer";                     src: local("Figtree Italic"), local("Figtree-Italic"); font-weight: 100 900; font-style: italic; font-display: swap; }
@font-face { font-family: "Switzer Placeholder";         src: local("Figtree"), local("Figtree-Regular"); font-weight: 100 900; font-style: normal; font-display: swap; }
@font-face { font-family: "Switzer Placeholder";         src: local("Figtree Italic"), local("Figtree-Italic"); font-weight: 100 900; font-style: italic; font-display: swap; }

@font-face { font-family: "Geist";                       src: local("Figtree"), local("Figtree-Regular"); font-weight: 100 900; font-style: normal; font-display: swap; }
@font-face { font-family: "Geist";                       src: local("Figtree Italic"), local("Figtree-Italic"); font-weight: 100 900; font-style: italic; font-display: swap; }
@font-face { font-family: "Geist Placeholder";           src: local("Figtree"), local("Figtree-Regular"); font-weight: 100 900; font-style: normal; font-display: swap; }
@font-face { font-family: "Geist Placeholder";           src: local("Figtree Italic"), local("Figtree-Italic"); font-weight: 100 900; font-style: italic; font-display: swap; }
@font-face { font-family: "Geist Mono";                  src: local("Figtree"), local("Figtree-Regular"); font-weight: 100 900; font-style: normal; font-display: swap; }
@font-face { font-family: "Geist Mono";                  src: local("Figtree Italic"), local("Figtree-Italic"); font-weight: 100 900; font-style: italic; font-display: swap; }

@font-face { font-family: "JetBrains Mono";              src: local("Figtree"), local("Figtree-Regular"); font-weight: 100 900; font-style: normal; font-display: swap; }
@font-face { font-family: "JetBrains Mono";              src: local("Figtree Italic"), local("Figtree-Italic"); font-weight: 100 900; font-style: italic; font-display: swap; }

@font-face { font-family: "Mona Sans";                   src: local("Figtree"), local("Figtree-Regular"); font-weight: 100 900; font-style: normal; font-display: swap; }
@font-face { font-family: "Mona Sans";                   src: local("Figtree Italic"), local("Figtree-Italic"); font-weight: 100 900; font-style: italic; font-display: swap; }
@font-face { font-family: "Mona Sans Placeholder";       src: local("Figtree"), local("Figtree-Regular"); font-weight: 100 900; font-style: normal; font-display: swap; }
@font-face { font-family: "Mona Sans Placeholder";       src: local("Figtree Italic"), local("Figtree-Italic"); font-weight: 100 900; font-style: italic; font-display: swap; }

@font-face { font-family: "Instrument Serif";            src: local("Figtree"), local("Figtree-Regular"); font-weight: 100 900; font-style: normal; font-display: swap; }
@font-face { font-family: "Instrument Serif";            src: local("Figtree Italic"), local("Figtree-Italic"); font-weight: 100 900; font-style: italic; font-display: swap; }
@font-face { font-family: "Instrument Serif Placeholder";src: local("Figtree"), local("Figtree-Regular"); font-weight: 100 900; font-style: normal; font-display: swap; }
@font-face { font-family: "Instrument Serif Placeholder";src: local("Figtree Italic"), local("Figtree-Italic"); font-weight: 100 900; font-style: italic; font-display: swap; }

@font-face { font-family: "Kalam";                       src: local("Figtree"), local("Figtree-Regular"); font-weight: 100 900; font-style: normal; font-display: swap; }
@font-face { font-family: "Kode Mono";                   src: local("Figtree"), local("Figtree-Regular"); font-weight: 100 900; font-style: normal; font-display: swap; }
@font-face { font-family: "Satoshi";                     src: local("Figtree"), local("Figtree-Regular"); font-weight: 100 900; font-style: normal; font-display: swap; }
@font-face { font-family: "Wasted Year";                 src: local("Figtree"), local("Figtree-Regular"); font-weight: 100 900; font-style: normal; font-display: swap; }

/* ---- Belt-and-braces: force Figtree on every element ---- */

html, body, button, input, select, textarea, optgroup {
  font-family: 'Figtree', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
}

* {
  font-family: 'Figtree', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
}

/* NOTE: deliberately NOT ::first-letter / ::first-line here. Setting
   font-family on ::first-line repaints the first formatted line of EVERY
   element in Figtree at paint time — silently overriding any element that
   wants a different font (e.g. the Space Mono install commands on /skills),
   while getComputedStyle still reports the element's own font. That made the
   mono look applied in the inspector but render as Figtree. The element's own
   font-family already cascades to its text, so these pseudos add nothing. */
*::before,
*::after,
*::placeholder,
*::selection {
  font-family: 'Figtree', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
}

/* ---- Override every Framer font-family CSS variable ---- */
:root, html, body, [data-framer-component-type], [data-framer-component-type] * {
  --framer-font-family: 'Figtree', sans-serif !important;
  --framer-font-family-bold: 'Figtree', sans-serif !important;
  --framer-font-family-italic: 'Figtree', sans-serif !important;
  --framer-font-family-bold-italic: 'Figtree', sans-serif !important;
  --framer-blockquote-font-family: 'Figtree', sans-serif !important;
  --framer-blockquote-font-family-bold: 'Figtree', sans-serif !important;
  --framer-blockquote-font-family-italic: 'Figtree', sans-serif !important;
  --framer-blockquote-font-family-bold-italic: 'Figtree', sans-serif !important;
  --framer-link-font-family: 'Figtree', sans-serif !important;
  --framer-link-current-font-family: 'Figtree', sans-serif !important;
  --framer-link-hover-font-family: 'Figtree', sans-serif !important;
  --framer-code-font-family: 'Figtree', sans-serif !important;
  --framer-font-family-preview: 'Figtree', sans-serif !important;
  --font-family: 'Figtree', sans-serif !important;
}

/* ============================================================
   Component-level typography spec (Figma → CSS)
   ============================================================ */

/* ---- Navbar: "Available for work" badge ---- */
/* Figtree 12 / kerning 1 / rgb(138,138,138) */
[data-framer-name="Available for work"],
[data-framer-name="Available for work"] p,
[data-framer-name="Available for work"] p * {
  font-size: 12px !important;
  letter-spacing: 1px !important;
  color: rgb(138, 138, 138) !important;
  --framer-font-size: 12px !important;
  --framer-letter-spacing: 1px !important;
  --framer-text-color: rgb(138, 138, 138) !important;
  --extracted-r6o4lv: rgb(138, 138, 138) !important;
}

/* ---- Navbar links (original Framer nav) ---- */
/* Inactive: Figtree 15 / Medium / kerning 0.4 / rgb(153,153,153)
   Active:   Figtree 15 / Bold   / kerning 0.4 / rgb(232, 80, 58) */
[data-framer-name="Links"] p.framer-text {
  font-size: 15px !important;
  letter-spacing: 0.4px !important;
  --framer-font-size: 15px !important;
  --framer-letter-spacing: 0.4px !important;
}
[data-framer-name="Links"] p.framer-text a {
  font-size: 15px !important;
  font-weight: 500 !important;
  letter-spacing: 0.4px !important;
  color: #1e1e1e !important;
}
[data-framer-name="Links"] p.framer-text a[data-framer-page-link-current="true"] {
  font-weight: 700 !important;
  color: rgb(232, 80, 58) !important;
}

/* ---- Sticky-nav pill (custom): same spec ---- */
.snav-link {
  font-size: 15px !important;
  font-weight: 500 !important;
  letter-spacing: 0.4px !important;
  color: #1e1e1e !important;
}
.snav-link[aria-current="page"] {
  font-weight: 700 !important;
  color: rgb(232, 80, 58) !important;
}

/* ============================================================
   Project category cards (Work / Photography listings)
   Card = button[role="listitem"] with an aria-label
   ============================================================ */

/* ---- Tag chip (PRD, SYS, APL, FIG, A.I) ---- */
/* Figtree 9 / Medium / line-height 12 / letter-spacing 1.5px */
button[role="listitem"][aria-label] > div > div[style*="top:0"][style*="right:0"] {
  font-size: 9px !important;
  font-weight: 500 !important;
  letter-spacing: 1.5px !important;
  line-height: 12px !important;
}

/* ---- Card title (e.g. "Figma Plugins") ---- */
/* Figtree 17 / Bold / line-height 19.2 */
button[role="listitem"][aria-label] > div > div[style*="left:0"][style*="bottom:0"] > div:not(.cc-subtitle) {
  font-size: 17px !important;
  font-weight: 700 !important;
  line-height: 19.2px !important;
  letter-spacing: -0.02em !important;
}

/* ---- Card subtitle (e.g. "3 projects") ---- */
/* Figtree 13 / Medium / line-height 13.5 */
button[role="listitem"][aria-label] .cc-subtitle {
  font-size: 13px !important;
  font-weight: 500 !important;
  line-height: 13.5px !important;
  letter-spacing: 0 !important;
}

/* ============================================================
   Hero block (replaces the old PNG with coded markup).
   Centered flex column inside the parent Framer container.
   ============================================================ */
.hero-coded {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 6px 13px;
  box-sizing: border-box;
}
.hero-wordmark {
  display: block;
  width: 100%;
  height: auto;
  max-height: 70%;
  flex: 0 1 auto;
}
.hero-tagline {
  margin: 0;
  font-family: 'Figtree', system-ui, -apple-system, BlinkMacSystemFont, sans-serif !important;
  font-size: clamp(13px, 1.18vw, 17px);
  font-weight: 400;
  line-height: 1.2;
  text-align: center;
  color: rgba(0, 0, 0, 0.5);
  flex: 0 0 auto;
}
.hero-tagline * {
  font-family: 'Figtree', system-ui, -apple-system, BlinkMacSystemFont, sans-serif !important;
}
.hero-tagline .hero-experience {
  font-weight: 500 !important;
  background: linear-gradient(90deg, #F5C19E, #FA1616);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent !important;
}
.hero-tagline .hero-users {
  color: rgba(30, 30, 30, 0.25) !important;
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}



/* ================== source: page-transitions.css ================== */
/* ============================================================
   Page transitions — soft fade between full-page navigations
   ------------------------------------------------------------
   Replaces Framer's SPA cross-page transition after the Framer
   runtime is removed. Pure CSS fade driven by two classes on
   <html> that page-transitions.js toggles:
     .pt-in  → fade the page in on load / bfcache restore
     .pt-out → fade the page out just before navigating away
   No-JS / reduced-motion: no classes are added, page shows
   instantly (graceful fallback).
   ============================================================ */

@media (prefers-reduced-motion: no-preference) {
  html.pt-in #main {
    animation: pt-page-in 300ms ease both;
  }
  html.pt-out #main {
    opacity: 0 !important;
    transition: opacity 260ms ease;
  }
}

@keyframes pt-page-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}


/* ================== source: marquee.css ================== */
/* ============================================================
   Marquee — CSS replacement for Framer's Ticker component
   ------------------------------------------------------------
   Framer rendered horizontal tickers as a <ul> with an inline
   transform:translateX(...) (and often an opacity:0 wrapper),
   then drove the scroll + reveal with JS. marquee.js strips
   those placeholders, duplicates the items for a seamless loop,
   and adds .fx-marquee — the scroll itself is this CSS keyframe.
   Used on: index (video ticker), about, work-physicswallah,
   youtube (image marquees).
   ============================================================ */

.fx-marquee {
  width: max-content !important;
  opacity: 1 !important;
  transform: translateX(0);
  will-change: transform;
  animation: fx-marquee-scroll var(--fx-marquee-dur, 50s) linear infinite;
}

.fx-marquee:hover {
  animation-play-state: paused;
}

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

@media (prefers-reduced-motion: reduce) {
  .fx-marquee { animation: none; transform: none; }
}


/* ================== source: footer.css ================== */
/* ============================================================
   Footer — shared across every page that renders the site footer
   (index, about, photography, youtube, and the work case studies).
   Page-agnostic selectors (data-framer-name / colour tokens) since
   the Framer hash classes differ per page.
   ============================================================ */

/* Tagline: "Always open to conversations about design…" → 14px */
[data-framer-name^="Always open to conversations"] p.framer-text {
  --framer-font-size: 14px !important;
  font-size: 14px !important;
}

/* Credit line: "© 2026 · Built with ❤ and cmd+z · Bengaluru, KA"
   → 12px + ALL CAPS. The 10px / rgb(200,200,200) token is unique
   to these credit parts (social labels are rgb(26,26,26)). */
p.framer-text[style*="--framer-font-size:10px"][style*="rgb(200, 200, 200)"] {
  --framer-font-size: 12px !important;
  font-size: 12px !important;
  text-transform: uppercase !important;
}

/* Social containers (Mail, X, GitHub, LinkedIn, Substack, YouTube,
   Figma) — the only `motion.a` elements on the page. Hover fills the
   container with #FAFAFA, with a smooth transition.
   Equal padding on all four sides (Framer ships `0 12px` + fixed
   height); `height:auto` lets the symmetric padding size the box
   (8px ≈ the original 33px height). */
[data-framer-name="motion.a"] {
  transition: background-color 0.2s ease !important;
  padding: 8px !important;
  height: auto !important;
}
[data-framer-name="motion.a"]:hover {
  background-color: #FAFAFA !important;
}

/* External link labels (Mail, LinkedIn, Twitter/X, …) → 12px */
[data-framer-name="motion.a"] p.framer-text {
  --framer-font-size: 12px !important;
  font-size: 12px !important;
}

/* ------------------------------------------------------------------
   Rebuilt social row (footer-links.js replaces the broken ≥1440 row on
   inner pages with these clean canonical pills — icons lifted from the
   home footer). Matches the home pills, but hovers to PURE WHITE since
   the inner-page footer sits on a #FAFAFA background (home stays on
   white + #FAFAFA hover via the rule above).
   ------------------------------------------------------------------ */
.ftx-row {
  display: flex !important;          /* beat the inherited Framer row class */
  flex-wrap: nowrap !important;
  align-items: center !important;
  justify-content: flex-start !important;
  gap: 8px !important;               /* matches the home row gap */
  width: auto !important;
  height: auto !important;
}
.ftx-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;                          /* home: icon→label gap */
  padding: 8px;                      /* home: 8px all sides */
  border: 1px solid rgba(26, 26, 26, 0.1);  /* home border colour */
  border-radius: 0;                  /* home: sharp corners, not rounded */
  font-size: 12px;
  line-height: 1;
  color: rgb(26, 26, 26);
  text-decoration: none;
  background: transparent;
  white-space: nowrap;
  transition: background-color 0.2s ease;
}
.ftx-pill:hover { background-color: #fff; }
.ftx-pill .ftx-ico { width: 11px; height: 11px; flex: none; display: inline-flex; }  /* home icon size */
.ftx-pill .ftx-ico svg { width: 100%; height: 100%; display: block; fill: currentColor; }
.ftx-pill .ftx-label { white-space: nowrap; }


/* ================== source: interactions.css ================== */
/* ============================================================
   Cross-page interaction polish (bundled into site.css).
   - Work-page hero banner hover zoom
   - Nav logo "water fill" hover (replaces Framer's dead runtime
     hover variant)
   ============================================================ */
/* ------------------------------------------------------------------
   Work-page hero zoom — the 386px collage banner at the top of the
   three case studies (work-newtonschool / work-physicswallah /
   work-grauity). The frame is named "Frame 1171279536" on exactly
   those pages and nowhere else; it already clips overflow, so the
   image can scale inside it. Feel matches the homepage card zoom
   (.pfr-card / app-card-zoom): 1.06 over .55s, hover devices only.
   ------------------------------------------------------------------ */
[data-framer-name="Frame 1171279536"] img {
  transition: transform 0.55s cubic-bezier(0.16, 1, 0.3, 1);
  transform-origin: center;
}
@media (hover: hover) and (pointer: fine) {
  [data-framer-name="Frame 1171279536"]:hover img {
    transform: scale(1.03);
    will-change: transform;
  }
}
@media (prefers-reduced-motion: reduce) {
  [data-framer-name="Frame 1171279536"] img { transition: none; }
  [data-framer-name="Frame 1171279536"]:hover img { transform: none; }
}

/* ------------------------------------------------------------------
   Nav logo hover — "glass filling with water"
   Replaces Framer's runtime hover variant (.framer-v-7plc7a.hover set
   height:36px;top:0 via JS, dead since the runtime was removed). The
   orange bar (.framer-1e96t86) anchors to the BOTTOM of the 36px logo
   box and its height springs 0 -> 100% on hover, filling upward.
   Global here (the nav is on every page); previously this lived in an
   index.html-only inline block, so all other pages were broken.
   !important beats both Framer's base rule (top:35px;height:1px) and
   the dead .hover variant rule if anything ever re-adds that class.
   ------------------------------------------------------------------ */
:root { --logo-spring: linear(0, 0.0702, 0.2262, 0.4081, 0.5808, 0.7266, 0.8394, 0.9202, 0.9736, 1.0056, 1.022, 1.0279, 1.0276, 1.0239, 1.0189, 1.0138, 1.0093, 1.0057, 1.0031, 1.0013, 1.0001, 0.9995, 0.9992, 0.9992, 0.9993); }
.framer-UYKSu.framer-7plc7a { position: relative; overflow: hidden; }
.framer-UYKSu .framer-1e96t86 {
  top: auto !important;
  bottom: 0 !important;
  left: 0 !important;
  right: 0 !important;
  width: auto !important;
  height: 0 !important;
  transition: height 0.5s var(--logo-spring, ease);
  /* The markup paints this div with an inline 34x1 SVG background. An SVG
     with a viewBox and default preserveAspectRatio (xMidYMid meet)
     LETTERBOXES instead of stretching, so at 36px tall it rendered as a
     1px orange line floating at the vertical centre — the element grew
     but the paint didn't. Solid colour fills at any height. */
  background-image: none !important;
  background-color: rgb(232, 80, 58) !important;
}
.framer-UYKSu:hover .framer-1e96t86 { height: 100% !important; }
@media (prefers-reduced-motion: reduce) {
  .framer-UYKSu .framer-1e96t86 { transition-duration: 0.18s; transition-timing-function: ease-out; }
}


/* ================== source: frame-margins.css ================== */
/* ============================================================
   Page-edge "blueprint" margins — Figma node 7317:103576.
   A fixed decorative rail on each side of the page, full height:
     · outer  — a 100px band of 45° hatching (black @ 6%, 1px lines,
                ~8px apart); mirrored left "/" vs right "\"
     · inner  — a 24px spaced container carrying just a left + right
                stroke (black @ 12%)
   Pure overlay (pointer-events:none), built from html/body ::before
   and ::after so it needs no markup and rides on every page.
   Desktop-only: the content is a fixed 1200px centered column, so each
   side gutter is (100vw - 1200)/2. The rail needs 124px; shown only at
   >=1480px, where the gutter is >=140px — the 124px rail plus ~16px of
   breathing air. Below that the gutter can't fit the rail without
   overlapping the content, so it's hidden entirely (kept in sync with
   the nav-border extension in sticky-nav.css, gated at the same width).
   ============================================================ */
@media (min-width: 1480px) {
  html::before,
  html::after,
  body::before,
  body::after {
    content: "";
    position: fixed;
    top: 0;
    height: 100vh;
    pointer-events: none;
    z-index: 999;
    opacity: 1;
    transition: opacity 0.6s ease;
  }

  /* Don't show the rails while the intro preloader is covering the page; once
     it reveals (html gets .pl-done), they fade in over 0.6s — in step with the
     overlay's own fade-out — so there's no flicker or pop-in on first load. */
  html.pl-show:not(.pl-done)::before,
  html.pl-show:not(.pl-done)::after,
  html.pl-show:not(.pl-done) body::before,
  html.pl-show:not(.pl-done) body::after {
    opacity: 0;
  }

  /* --- outer hatch bands (100px wide) --- */
  html::before,
  html::after { width: 100px; }
  html::before {
    left: 0;
    background-color: #fafafa;
    background-image: repeating-linear-gradient(
      -45deg,
      rgba(0, 0, 0, 0.06) 0, rgba(0, 0, 0, 0.06) 1px,
      transparent 1px, transparent 6.5px
    );
  }
  html::after {
    right: 0;
    background-color: #fafafa;
    background-image: repeating-linear-gradient(
      45deg,
      rgba(0, 0, 0, 0.06) 0, rgba(0, 0, 0, 0.06) 1px,
      transparent 1px, transparent 6.5px
    );
  }

  /* --- inner spaced container: a 24px box carrying just its 2 strokes --- */
  body::before,
  body::after {
    width: 24px;
    box-sizing: border-box;
    border-left: 1px solid rgba(0, 0, 0, 0.12);
    border-right: 1px solid rgba(0, 0, 0, 0.12);
  }
  body::before { left: 100px; }
  body::after  { right: 100px; }
}


/* ================== source: mobile.css ================== */
/* ============================================================
   mobile.css — mobile/tablet responsive fixes for the whole site.
   The Framer export ships only two breakpoints (>=1440 desktop and a
   sub-1440 "phone" SSR variant that was designed much wider than a real
   phone), so fixed-width containers, baked desktop paddings and
   absolutely-positioned side captions overflow or collide on small
   screens. Rules are page-scoped via each page's data-framer-root class:
     index .framer-fLWmP | about .framer-sUYoE | youtube .framer-13VTf
     grauity .framer-qeADE | newtonschool .framer-c5iXM
     physicswallah .framer-ILkxA | fgma .framer-CGioe
   Everything is !important on purpose: it has to beat Framer's inline
   styles and equally-specific exported rules.
   ============================================================ */

/* ============================================================
   work-physicswallah — the page used to ship <meta viewport width=1440>
   (whole desktop layout zoomed to ~27% on phones). The meta is now
   device-width; these rules make the desktop-designed DOM flow.
   Structure fixes apply <=1239.98px (the fixed 1200px column would clip
   any narrower window); phone-only tweaks below at <=809.98px.
   ============================================================ */
@media (max-width: 1239.98px) {
  /* --- fixed-width containers -> fluid (from the page's own rules) --- */
  .framer-ILkxA.framer-9itdqv,
  .framer-ILkxA .framer-15t9c9e-container,
  .framer-ILkxA .framer-1qz8sdu,
  .framer-ILkxA .framer-7wy1lp,
  .framer-ILkxA .framer-emtdi2,
  .framer-ILkxA .framer-1ljuwk2,
  .framer-ILkxA .framer-17g5mj1,
  .framer-ILkxA .framer-1mb4ks1,
  .framer-ILkxA .framer-1cf19gs,
  .framer-ILkxA .framer-106lmmb,
  .framer-ILkxA .framer-ed6t1q,
  .framer-ILkxA .framer-1n5bfq8,
  .framer-ILkxA .framer-1bizi7y,
  .framer-ILkxA .framer-11qy47g,
  .framer-ILkxA .framer-1jar7px,
  .framer-ILkxA .framer-11e6df9,
  .framer-ILkxA .framer-1m3we6z,
  .framer-ILkxA .framer-139v72l,
  .framer-ILkxA .framer-1wbfybg,
  .framer-ILkxA .framer-WLHpg.framer-1gksnpj {
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
  }
  /* the 996px case-study text family */
  .framer-ILkxA .framer-1lvil8,
  .framer-ILkxA .framer-jdh9vi,
  .framer-ILkxA .framer-1pk9zjp,
  .framer-ILkxA .framer-chu8rj,
  .framer-ILkxA .framer-bsu652,
  .framer-ILkxA .framer-8x1l94,
  .framer-ILkxA .framer-1bv1lwb,
  .framer-ILkxA .framer-iqyq2x,
  .framer-ILkxA .framer-1vw4i4m,
  .framer-ILkxA .framer-1siznyv,
  .framer-ILkxA .framer-fh1swa,
  .framer-ILkxA .framer-kxntd1,
  .framer-ILkxA .framer-131xo6i,
  .framer-ILkxA .framer-1f03rx9 {
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
  }

  /* --- auto-width single-line texts (hero title, closing notes,
         footer next-project block): wrap within the viewport --- */
  .framer-ILkxA .framer-1v2wc0h,
  .framer-ILkxA .framer-xz4z7k,
  .framer-ILkxA .framer-1d8icf0,
  .framer-ILkxA .framer-dtvt4g,
  .framer-ILkxA .framer-1c0ixwh,
  .framer-ILkxA .framer-jnqby4 {
    white-space: pre-wrap !important;
    width: 100% !important;
    max-width: 100% !important;
  }
  .framer-ILkxA .framer-zfzn3n {
    width: 100% !important;
    min-width: 0 !important;
    padding-left: 16px !important;
    padding-right: 16px !important;
    box-sizing: border-box !important;
  }

  /* --- project meta row (role/company/platform/timeline): wrap,
         left-align so centered values don't spill past the edge --- */
  .framer-ILkxA .framer-u1qetq {
    flex-wrap: wrap !important;
    width: 100% !important;
    justify-content: flex-start !important;
    gap: 20px !important;
  }
  .framer-ILkxA .framer-u1qetq > div {
    align-items: flex-start !important;
    width: auto !important;
    max-width: 100% !important;
  }

  /* --- text+image side-by-side row -> stack --- */
  .framer-ILkxA .framer-1b1susa {
    flex-direction: column !important;
    align-items: stretch !important;
  }
  .framer-ILkxA .framer-dzmhha {
    width: 100% !important;
    flex: none !important;
  }
  .framer-ILkxA .framer-43ytmo {
    width: 100% !important;
    height: auto !important;
  }

  /* --- key-insights + impact-metrics bands: 64px side paddings are
         desktop-scale, and their card rows must stack --- */
  .framer-ILkxA .framer-kd9h8c,
  .framer-ILkxA .framer-1kv3j0d {
    padding: 24px 20px !important;
  }
  .framer-ILkxA .framer-jh4i7c,
  .framer-ILkxA .framer-152oa1n {
    flex-direction: column !important;
    width: 100% !important;
  }
  .framer-ILkxA .framer-jh4i7c > div,
  .framer-ILkxA .framer-152oa1n > div {
    width: 100% !important;
    max-width: 100% !important;
  }

  /* --- numbered list rows: let the fill-width text child flex instead
         of hard 100% (it overflows by its marker offset otherwise) --- */
  .framer-ILkxA .framer-f4ouby > .framer-text,
  .framer-ILkxA .framer-f4ouby > div {
    min-width: 0 !important;
  }
  .framer-ILkxA .framer-f4ouby > div {
    flex: 1 1 0 !important;
    width: auto !important;
  }

  /* --- flow ribbons ("Onboarding Nudge ... → Study Path"): desktop
         bakes ~230px side paddings; relax so the label can breathe --- */
  .framer-ILkxA .framer-i2vek9,
  .framer-ILkxA .framer-6d0his,
  .framer-ILkxA .framer-1wcveff,
  .framer-ILkxA .framer-1n9rnws,
  .framer-ILkxA .framer-1i3u05q {
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    left: 0 !important;
    transform: none !important;
    padding-left: 20px !important;
    padding-right: 20px !important;
  }
  .framer-ILkxA .framer-1n9rnws,
  .framer-ILkxA .framer-1i3u05q,
  .framer-ILkxA .framer-1wcveff {
    padding-left: 0 !important;
    padding-right: 0 !important;
  }
  .framer-ILkxA .framer-15eqg4n,
  .framer-ILkxA .framer-g18m2p,
  .framer-ILkxA .framer-v9ydgo {
    white-space: pre-wrap !important;
    width: 100% !important;
    min-width: 0 !important;
  }

  /* --- marquee side-captions ("Our goal was to convert...") are
         absolutely positioned into desktop margin space; let them flow
         above the strip --- */
  .framer-ILkxA .framer-106lmmb,
  .framer-ILkxA .framer-ed6t1q,
  .framer-ILkxA .framer-1n5bfq8 {
    position: static !important;
    transform: none !important;
    margin-bottom: 16px !important;
  }

  /* --- content column sections: desktop relies on a wide canvas for
         side margins — give text sections real padding --- */
  .framer-ILkxA .framer-u0oilq > div,
  .framer-ILkxA .framer-b301w9,
  .framer-ILkxA .framer-1g0fsvd {
    padding-left: 16px !important;
    padding-right: 16px !important;
    box-sizing: border-box !important;
  }

  /* --- mockup video bands: 64px paddings + fixed five-up rows ->
         wrap 2-up --- */
  .framer-ILkxA .framer-1d3grns,
  .framer-ILkxA .framer-sww1am,
  .framer-ILkxA .framer-7okjsb {
    padding-left: 16px !important;
    padding-right: 16px !important;
  }
  .framer-ILkxA .framer-37xh5r,
  .framer-ILkxA .framer-oglkr,
  .framer-ILkxA .framer-d9g2xl {
    flex-wrap: wrap !important;
    width: 100% !important;
    justify-content: center !important;
    gap: 12px !important;
  }
  .framer-ILkxA .framer-37xh5r > div,
  .framer-ILkxA .framer-oglkr > div,
  .framer-ILkxA .framer-d9g2xl > div {
    width: calc(50% - 8px) !important;
    height: auto !important;
  }
  .framer-ILkxA .framer-37xh5r video,
  .framer-ILkxA .framer-oglkr video,
  .framer-ILkxA .framer-d9g2xl video {
    width: 100% !important;
    height: auto !important;
  }

  /* --- footer: pill row wraps, bottom row (pills | copyright) stacks --- */
  .framer-ILkxA .framer-1wbfybg {
    flex-wrap: wrap !important;
    row-gap: 10px !important;
  }
  .framer-ILkxA .framer-15logfx {
    flex-direction: column !important;
    align-items: flex-start !important;
    gap: 16px !important;
  }
}

@media (max-width: 809.98px) {
  /* phone-only: calmer hero size, hide the desktop nav links
     (sticky-nav.js mounts its burger fallback on this page) */
  .framer-ILkxA .framer-1v2wc0h .framer-text {
    font-size: 32px !important;
  }
  .framer-ILkxA .framer-WLHpg .framer-1gxxlpg {
    display: none !important;
  }
}

/* ============================================================
   index (home) — the sub-1440 "phone" SSR variant of the Testimonials
   code component ships min-width:1200px inline, so the quote card runs
   off screen; a few card internals keep desktop geometry.
   ============================================================ */
@media (max-width: 1249.98px) {
  /* testimonials: the inline 1200px floor is wrong at every width the
     phone variant can show */
  .framer-fLWmP .ssr-variant.hidden-72rtr7 section[aria-label="Testimonials"] {
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    padding: 0 16px !important;
    box-sizing: border-box !important;
  }
}

@media (max-width: 809.98px) {
  /* testimonials: quote card + people rail side-by-side -> stack */
  .framer-fLWmP .ssr-variant.hidden-72rtr7 section[aria-label="Testimonials"] > div > div {
    flex-direction: column !important;
  }
  .framer-fLWmP .ssr-variant.hidden-72rtr7 section[aria-label="Testimonials"] aside {
    width: 100% !important;
    flex: none !important;
  }
  /* the quote card: fixed 341px height + 48px padding are desktop-scale,
     and the inner text block is a hard 680px */
  .framer-fLWmP .ssr-variant.hidden-72rtr7 section[aria-label="Testimonials"] div[style*="height:341px"] {
    height: auto !important;
    min-height: 0 !important;
    padding: 32px 22px !important;
  }
  .framer-fLWmP .ssr-variant.hidden-72rtr7 section[aria-label="Testimonials"] div[style*="max-width:680px"] {
    width: 100% !important;
    max-width: 100% !important;
  }

  /* satchel app-stats row: hard 528px; let it fill and wrap */
  .framer-fLWmP .framer-ijb475 {
    width: 100% !important;
    min-width: 0 !important;
    flex-wrap: wrap !important;
    gap: 16px !important;
  }

  /* ai-lab card tag chips poke past the card edge; wrap them */
  .framer-fLWmP .framer-nb5agx,
  .framer-fLWmP .framer-184pdbx {
    flex-wrap: wrap !important;
    row-gap: 8px !important;
  }
}

/* ============================================================
   about — decorative divider rows are hard 1200x1px, poking past the
   viewport; harmless but let them size to their padded parent.
   ============================================================ */
@media (max-width: 809.98px) {
  .framer-sUYoE .framer-1nilwvd,
  .framer-sUYoE .framer-1s2y9ig,
  .framer-sUYoE .framer-f4yjrk,
  .framer-sUYoE .framer-boikmf,
  .framer-sUYoE .framer-1hqxuie {
    width: 100% !important;
  }
}

/* ============================================================
   work-grauity — two absolutely-positioned type/spacing specimens are
   anchored for a desktop-wide band, so phones see mostly blank crop;
   re-anchor them to the center like the neighbouring specimens.
   ============================================================ */
@media (max-width: 809.98px) {
  /* "A quick brown fox jumps" oversized type specimen (1026px wide) */
  .framer-qeADE .framer-15jfgag {
    left: 50% !important;
    transform: translateX(-50%) !important;
  }
  /* spacing-token strip (1213px wide, anchored right:-83px) */
  .framer-qeADE .framer-sob8c {
    right: auto !important;
    left: 50% !important;
    transform: translate(-50%, -50%) !important;
  }

  /* the four specimen bands are a fixed 805px tall around small
     content islands — compact them and center their artwork */
  .framer-qeADE .framer-1x6ofzg {
    height: 400px !important;
  }
  .framer-qeADE .framer-q8fzo8 {
    top: 50% !important;
    transform: translateY(-50%) !important;
  }
  .framer-qeADE .framer-1tpnh3d {
    height: 420px !important;
  }
  .framer-qeADE .framer-15jfgag {
    top: 96px !important;
  }
  .framer-qeADE .framer-1fzqbbm {
    top: auto !important;
    bottom: 96px !important;
  }
  .framer-qeADE .framer-1o9h9sk {
    height: 360px !important;
  }
  .framer-qeADE .framer-1yu16tx {
    top: 50% !important;
    transform: translateY(-50%) !important;
  }
  .framer-qeADE .framer-1bg4tkn {
    height: 240px !important;
  }
}

/* ============================================================
   youtube — video-card marquee rows are hard 1200x260 with 480x270
   cards, so phones only ever see a cropped slice of one card.
   ============================================================ */
@media (max-width: 809.98px) {
  /* row/section wrappers around each marquee strip (every youtube class
     that hard-codes width >= 800px) */
  .framer-13VTf .framer-149hlnx,
  .framer-13VTf .framer-1liurdp,
  .framer-13VTf .framer-53vb5y,
  .framer-13VTf .framer-15kj7pr,
  .framer-13VTf .framer-fygsoy,
  .framer-13VTf .framer-1r0ia9e,
  .framer-13VTf .framer-6tweih,
  .framer-13VTf .framer-yl0xim,
  .framer-13VTf .framer-xb6aie,
  .framer-13VTf .framer-12q0s9z,
  .framer-13VTf .framer-15822md,
  .framer-13VTf .framer-1978251,
  .framer-13VTf .framer-19mlzyj,
  .framer-13VTf .framer-1sq2al1,
  .framer-13VTf .framer-1suansa,
  .framer-13VTf .framer-ec2450,
  .framer-13VTf .framer-o816yt,
  .framer-13VTf .framer-r3472k,
  .framer-13VTf .framer-WLHpg.framer-1gksnpj,
  .framer-13VTf.framer-1y5o0jh {
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
  }
  .framer-13VTf .framer-13rnqcf-container,
  .framer-13VTf .framer-159ti01-container,
  .framer-13VTf .framer-6e2hsr-container {
    width: 100% !important;
    height: auto !important;
  }
  /* the 480x270 video cards: scale to the viewport, keep 16:9 */
  .framer-13VTf .fx-marquee > li > div {
    width: min(480px, 82vw) !important;
    height: auto !important;
    aspect-ratio: 16 / 9 !important;
  }
}

