/* ═══════════════════════════════════════════════════════════════════════
   YASH TEMBHURNIKAR — boilerlab.ai replica
   Theme: their near-black monochrome (#05050a, warm white type), with a
   single restrained accent tying back to the outer galaxy scene.
   Motion: pinned camera-flight scenes (hero → mission → numbers) where
   scroll drives ZOOM toward/past the viewer, then the camera lands at
   Teams and normal document scroll resumes. Fixed bottom navbar.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── Scroll-driven variables, registered as NON-INHERITED ───────────────
   These four are rewritten from JS on every scroll frame. Custom properties
   inherit by default, so setting --pq on a .product invalidated the computed
   style of that card's ENTIRE subtree — every heading, tag, paragraph and
   image inside it — six stacked cards over, every frame. That style recalc
   was the bulk of the scroll cost in the projects stack.

   Declaring them with inherits:false confines each change to the element
   that owns it, so transform/opacity updates stay on the compositor instead
   of dragging a subtree restyle behind them. Purely a performance
   declaration: the animations and their values are unchanged, and browsers
   without @property simply keep the previous behaviour. */
@property --pq { syntax: "<number>"; inherits: false; initial-value: 0; }
@property --sz { syntax: "<number>"; inherits: false; initial-value: 1; }
@property --so { syntax: "<number>"; inherits: false; initial-value: 1; }
@property --mp { syntax: "<number>"; inherits: false; initial-value: 0; }
@property --px { syntax: "<number>"; inherits: false; initial-value: 0; }

:root{
  --bg:      #05050a;
  --text:    #f4f3f0;
  /* Both raised for WCAG contrast against --bg (#05050a): --muted was
     ~5.7:1, now ~8:1; --faint was ~2.6:1 (failed even large-text
     minimums), now ~6:1. Still two clearly distinct steps below --text,
     just no longer illegible at the bottom of the scale. */
  --muted:   rgba(244,243,240,0.72);
  --faint:   rgba(244,243,240,0.58);
  --line:    rgba(244,243,240,0.12);
  --accent:  #ff6ad5;
  --accent2: #b57bff;

  --display: "Space Grotesk", Inter, system-ui, sans-serif;
  --body:    Inter, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

  --wrap: min(90vw, 1160px);
  --ease: cubic-bezier(0.16, 1, 0.3, 1);

  /* Every project card is exactly this tall — see .product. The leftover
     (100vh − this) is split above and below when the card parks in the
     middle of the screen, so the 190px reserved here becomes a fixed 95px
     of clearance top and bottom — comfortably more than the fixed bottom
     navbar needs (~66px), at any viewport height, rather than a percentage
     that goes under the bar on a short screen. */
  --pcard-h: clamp(400px, calc(100vh - 190px), 780px);

  /* Experience role card. Short by design: it holds a summary and the
     headline outcome, and "Read more" opens the rest. Raised from
     clamp(300,42vh,430) — the readability pass grew the type inside it. */
  --exp-card-h: clamp(360px, 48vh, 490px);
}

*{ box-sizing: border-box; margin: 0; padding: 0; }
html{ scroll-behavior: smooth; }
body{
  font-family: var(--body);
  background: var(--bg);
  color: var(--text);
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}
a{ color: inherit; text-decoration: none; }
::selection{ background: rgba(255,106,213,0.35); color: #fff; }

#stars{ position: fixed; inset: 0; width: 100vw; height: 100vh; z-index: 0; pointer-events: none; }
main{ position: relative; z-index: 1; }

/* ═══ TOP CHROME ═══ */
.top{
  position: fixed; top: 0; left: 0; right: 0; z-index: 100;
  display: flex; justify-content: space-between; align-items: center;
  padding: 20px clamp(18px, 4vw, 44px);
  background: linear-gradient(180deg, rgba(5,5,10,0.85), rgba(5,5,10,0));
}
.top__logo{
  display: flex; align-items: center; gap: 11px;
  font-family: var(--display); font-weight: 700; font-size: 15.5px;
}
.top__mark{
  display: grid; place-items: center;
  width: 34px; height: 34px; border-radius: 10px;
  font-size: 14px; color: #0a0510;
  background: linear-gradient(135deg, var(--accent), var(--accent2));
}
/* Fullscreen toggle (laptop only — JS hides it on phones / small-screen mode) */
.top__fullscreen{
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--display); font-weight: 500; font-size: 14px;
  color: var(--muted);
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 8px 16px;
  cursor: pointer;
  transition: color 200ms ease, border-color 200ms ease, background 200ms ease;
}
.top__fullscreen:hover{
  color: var(--text);
  border-color: rgba(255,255,255,0.35);
  background: rgba(255,255,255,0.08);
}
.top__fullscreen[hidden]{ display: none; }
.top__fsicon{ display: block; }
/* icon swaps between expand/compress depending on state */
.top__fullscreen .fs-compress{ display: none; }
.top__fullscreen.is-fullscreen .fs-expand{ display: none; }
.top__fullscreen.is-fullscreen .fs-compress{ display: block; }

/* ═══ BOTTOM NAVBAR ═══ */
.bnav{
  position: fixed; bottom: 18px; left: 50%;
  transform: translateX(-50%);
  z-index: 100;
  display: flex; gap: 4px;
  padding: 7px;
  border-radius: 999px;
  border: 1px solid var(--line);
  /* A fixed element with backdrop-filter forces the browser to re-sample and
     re-blur everything behind it on EVERY frame the page scrolls underneath —
     and this bar is on screen for the whole site. That single property was
     among the most expensive things running during a scroll. A near-opaque
     background reads almost identically against this near-black page and
     costs nothing. Restore the two backdrop-filter lines (and drop the alpha
     back to 0.72) if the translucency is worth the frame time. */
  background: rgba(10,10,18,0.94);
  max-width: 94vw;
  overflow-x: auto;
  scrollbar-width: none;
}
.bnav::-webkit-scrollbar{ display: none; }
.bnav a{
  font-family: var(--display); font-weight: 500;
  /* Was 12px/0.08em — raised to the 14px floor; letter-spacing trimmed
     slightly so the wider glyphs don't feel looser than before. Pill
     padding unchanged, so the compact floating shape is preserved; on
     phones the bar already scrolls horizontally if items don't all fit. */
  font-size: 14px; letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--muted);
  padding: 9px 15px; border-radius: 999px;
  white-space: nowrap;
  transition: color 200ms ease, background 200ms ease;
}
.bnav a:hover{ color: var(--text); }
.bnav a.active{
  color: var(--text);
  background: rgba(244,243,240,0.1);
}

/* ═══ CAMERA FLIGHT: one pinned stage, stacked scene layers ═══
   .flight height = (scenes − 1) × beat + 100vh. Progress through it
   drives each layer's zoom (--sz) and fade (--so): the incoming scene
   rises from far away WHILE the outgoing one flies past the camera. */
.flight{ position: relative; height: calc(4 * 115vh + 100vh); }
.flight__pin{
  position: sticky; top: 0;
  height: 100vh;
  overflow: hidden;
}
.fscene{
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  text-align: center;
  padding: 0 6vw;
  transform: scale(var(--sz, 1));
  opacity: var(--so, 0);
  will-change: transform, opacity;
  pointer-events: none;
}
.fscene.active{ pointer-events: auto; }
.fscene#top{
  align-items: flex-start;
  text-align: left;
  padding: 0 clamp(24px, 9vw, 150px);
}

/* hero content */
/* hero kicker — terminal-style warning line */
.hero__kicker{
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-weight: 500;
  font-size: clamp(14px, 1.3vw, 15.5px);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 20px;
  animation: kickerflicker 3.2s steps(1) infinite;
}
@keyframes kickerflicker{
  0%, 92%, 96%, 100%{ opacity: 1; }
  94%{ opacity: 0.35; }
}

.hero__h{
  font-family: var(--display); font-weight: 700;
  font-size: clamp(42px, 8.6vw, 104px);
  line-height: 1.04; letter-spacing: -0.03em;
}
.hero__h em{
  font-style: normal;
  background: linear-gradient(94deg, #ffffff 20%, #e8ddff 70%, #ffd2f1 100%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
}
.hero__sub{
  margin-top: 28px;
  max-width: 520px;
  padding-left: 16px;
  border-left: 2px solid rgba(255, 106, 213, 0.55);
  font-size: clamp(16px, 1.9vw, 19px);
  line-height: 1.55;
  color: var(--muted);
}
.hero__hint{
  position: absolute; bottom: 92px; left: 50%;
  transform: translateX(-50%);
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  font-size: 14px; letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--faint); white-space: nowrap;
}
.hero__hintline{
  width: 1px; height: 44px;
  background: linear-gradient(180deg, transparent, var(--accent));
  animation: hintdrop 1.8s ease-in-out infinite;
}
@keyframes hintdrop{
  0%{ transform: scaleY(0); transform-origin: top; }
  55%{ transform: scaleY(1); transform-origin: top; }
  100%{ transform: scaleY(0); transform-origin: bottom; }
}

/* mission content — live auto-typing */
.mission__p{
  max-width: min(88vw, 880px);
  font-family: var(--display); font-weight: 500;
  font-size: clamp(21px, 3.3vw, 34px);
  line-height: 1.5; letter-spacing: -0.01em;
}
.mission__sign{
  margin-top: 34px;
  font-family: var(--display); font-weight: 700;
  font-size: clamp(17px, 2.2vw, 23px);
}
.mission__p .ch, .mission__sign .ch{ opacity: 0; }
.mission__p .ch.on, .mission__sign .ch.on{ opacity: 1; }
.mission__sign .ch.on{ color: var(--accent); }
.type-caret{
  display: inline-block;
  width: 0.55em; height: 1em;
  margin-left: 2px;
  vertical-align: -0.12em;
  background: var(--accent);
  animation: caret-blink 0.9s steps(1) infinite;
}
@keyframes caret-blink{ 50%{ opacity: 0; } }

/* numbers content — supporting lines around the giant central figure.
   Were clamp(15,1.8vw,18) for both: fine on paper, but next to a
   52–128px number they read as an afterthought. Upper line gets a touch
   more presence (it's the "setup" line) and a slightly stronger color;
   lower line can sit a shade quieter as the explanation, but neither is
   metadata-small any more. */
.num__pre{
  font-size: clamp(19px, 2.4vw, 24px);
  font-weight: 500;
  color: rgba(244,243,240,0.92);
}
.num__post{
  font-size: clamp(18px, 2.1vw, 21px);
  font-weight: 500;
  color: rgba(244,243,240,0.8);
}
.num__big{
  margin: 14px 0;
  font-family: var(--display); font-weight: 700;
  font-size: clamp(52px, 10vw, 128px);
  line-height: 1; letter-spacing: -0.03em;
  color: var(--text);
}

/* ═══ LANDED SECTIONS ═══ */
.sec{ width: var(--wrap); margin: 0 auto; padding: clamp(90px, 14vh, 150px) 0; position: relative; }

.reveal{
  opacity: 0; transform: translateY(44px);
  transition: opacity 0.9s ease, transform 1s var(--ease);
  transition-delay: var(--d, 0ms);
}
.reveal.in{ opacity: 1; transform: translateY(0); }

/* Skills — heading + category cards + marquee */
.partners{
  min-height: 100vh;
  display: flex; flex-direction: column; justify-content: center;
  padding-top: 10px;
  padding-bottom: 120px;
     /* clears fixed header + bottom navbar */
}
.partners__label{
  font-size: 15px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--muted); margin-bottom: 10px; text-align: center;
}
.skills__h{
  font-family: var(--display); font-weight: 700;
  font-size: clamp(30px, 2vw + 2.6vh, 54px);
  line-height: 1.06; letter-spacing: -0.032em;
  text-align: center;
  margin-bottom: clamp(16px, 3vh, 36px);
}


/* ── Skills: capability index ───────────────────────────────────────────
   Monochrome by intent — the rest of the site carries the colour, so this
   section works through type, spacing and motion instead.

   Type: Inter for anything to be READ (the summary, the facts), Space
   Grotesk for names and figures. Weights are pinned to the ones actually
   loaded — Space Grotesk ships 400/500/700 only, so asking for 600 made
   the browser synthesise a smeared fake bold.

   It runs itself: categories advance on a timer with the rail beneath the
   active row as the clock, and it yields the moment a visitor takes over. */
.skills__sub{
  text-align: center;
  color: var(--muted);
  font-size: clamp(16px, 1.15vw, 18px);
  line-height: 1.5;
  margin: 2px auto clamp(26px, 4vh, 46px);
  max-width: 52ch;
}

.cap{
  width: min(100%, 1120px);
  margin: 0 auto;
  display: grid;
  grid-template-columns: minmax(0, 0.86fr) minmax(0, 1.14fr);
  gap: clamp(28px, 4vw, 64px);
  align-items: stretch;
}

/* ── category list ── */
.cap__nav{
  list-style: none;
  display: flex; flex-direction: column;
  justify-content: center;
}
.cap__tab{
  position: relative;
  width: 100%;
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: 14px;
  padding: clamp(15px, 1.9vh, 21px) 2px;
  background: none;
  border: 0;
  border-top: 1px solid var(--line);
  color: var(--faint);
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: color 0.45s var(--ease);
}
.cap__nav li:last-child .cap__tab{ border-bottom: 1px solid var(--line); }
.cap__tabname{
  font-family: var(--display); font-weight: 500;
  font-size: clamp(19px, 1.65vw, 25px);
  letter-spacing: -0.018em;
  /* Visual feedback pass: 0.42 read as "faded" next to full-white
     active/hover text — this needs to look like "soft white", not a step
     down toward gray. */
  color: rgba(244,243,240,0.8);
  transition: color 0.45s var(--ease);
}
.cap__tabmeta{
  font-size: 16px; font-weight: 500; letter-spacing: 0.01em;
  /* Dedicated color rather than var(--faint) — this is the "5 core /
     6 tools" count, real information, not a decorative aside. */
  color: rgba(244,243,240,0.74);
  transition: color 0.45s var(--ease);
}
.cap__tab:hover .cap__tabname{ color: rgba(244,243,240,0.92); }
.cap__tab.is-active .cap__tabname{ color: var(--text); font-weight: 700; }
.cap__tab.is-active .cap__tabmeta{ color: rgba(244,243,240,0.86); }

/* The rail is also the auto-advance clock: it fills across the active row,
   then hands on. Transform-driven, so it stays on the compositor. */
.cap__rail{
  position: absolute; left: 0; right: 0; bottom: -1px;
  height: 1px;
  overflow: hidden;
  pointer-events: none;
}
.cap__rail i{
  display: block; height: 100%; width: 100%;
  background: rgba(244,243,240,0.9);
  transform: scaleX(0);
  transform-origin: 0 50%;
}
.cap__tab.is-active .cap__rail i{
  transform: scaleX(1);
  transition: transform var(--cap-dwell, 5s) linear;
}
.cap__tab.is-paused .cap__rail i{ transition: none; }

/* ── detail panel ── */
.cap__panel{
  position: relative;
  display: flex; align-items: center;
  min-height: clamp(300px, 42vh, 400px);
  padding-left: clamp(0px, 1.5vw, 22px);
  border-left: 1px solid var(--line);
}
.cap__pane{
  position: absolute;
  left: clamp(0px, 1.5vw, 22px); right: 0;
  opacity: 0;
  transform: translate3d(0, 12px, 0);
  transition: opacity 0.55s var(--ease), transform 0.55s var(--ease);
  pointer-events: none;
}
.cap__pane.is-active{ opacity: 1; transform: none; pointer-events: auto; }

.cap__summary{
  font-family: var(--body);
  font-weight: 400;
  font-size: clamp(19px, 1.75vw, 27px);
  line-height: 1.42;
  letter-spacing: -0.014em;
  color: var(--text);
  max-width: 34ch;
  margin-bottom: clamp(20px, 3vh, 30px);
}
.cap__tags{
  list-style: none;
  display: flex; flex-wrap: wrap; gap: 8px;
  margin-bottom: clamp(22px, 3.4vh, 34px);
}
.cap__tags li{
  font-size: 16.5px; font-weight: 500; line-height: 1;
  padding: 10px 16px;
  border-radius: 9px;
  color: rgba(244,243,240,0.84);
  border: 1px solid var(--line);
  background: rgba(244,243,240,0.055);
  opacity: 0;
  transform: translate3d(0, 7px, 0);
}
.cap__pane.is-active .cap__tags li{
  animation: capchip 0.5s var(--ease) forwards;
  animation-delay: calc(var(--i, 0) * 45ms + 130ms);
}
@keyframes capchip{ to{ opacity: 1; transform: none; } }

/* Was 3 equal-width columns, one per fact — looked ragged because "Primary"
   and "Client persistence" don't take the same width, so values never
   lined up with each other and short values left large dead gaps. This is
   a proper label/value table instead: dt and dd are direct children (see
   portfolio.html — no wrapping <div> per pair any more), so a single
   2-column grid gives every label the SAME column width and every value
   the SAME starting x-position, regardless of which row it's in. */
.cap__facts{
  display: grid;
  grid-template-columns: minmax(120px, max-content) 1fr;
  column-gap: clamp(16px, 2vw, 28px);
  row-gap: clamp(10px, 1.6vh, 14px);
  padding-top: clamp(16px, 2.4vh, 22px);
  border-top: 1px solid var(--line);
}
.cap__facts dt{
  font-size: 16px; font-weight: 500; letter-spacing: 0.03em; text-transform: uppercase;
  /* Dedicated color rather than var(--faint) — these labels are read, not
     decorative. */
  color: rgba(244,243,240,0.76);
  align-self: baseline;
}
.cap__facts dd{
  font-family: var(--display); font-weight: 500;
  font-size: clamp(16px, 1.2vw, 19px);
  line-height: 1.32;
  letter-spacing: -0.008em;
  color: var(--text);
}

/* ── Skills on a phone ──
   The tabbed index is a desktop idea: it shows ONE of the six categories and
   rotates on a timer, so five sixths of the information was never on screen
   unless you waited for it to come around. On a phone every category is
   simply listed, all six expanded at once, with the tab strip dropped
   entirely. The heading comes from data-name on each pane, since the names
   live in the (now hidden) tab list. portfolio.js skips the auto-advance at
   this width so nothing is toggling classes behind the scenes. */
@media (max-width: 860px){
  .cap{ display: block; }
  .cap__nav{ display: none; }
  .cap__panel{
    display: block;          /* it is a centred flex row on desktop */
    min-height: 0;
    border-left: 0; padding-left: 0;
    border-top: 0; padding-top: 0;
  }
  .cap__pane{
    position: static;
    display: block;
    left: 0;
    opacity: 1;
    transform: none;
    pointer-events: auto;
    padding: 22px 0;
    border-top: 1px solid var(--line);
  }
  .cap__pane::before{
    content: attr(data-name);
    display: block;
    font-family: var(--display); font-weight: 700;
    font-size: clamp(20px, 5.4vw, 25px);
    letter-spacing: -0.02em;
    color: var(--text);
    margin-bottom: 12px;
  }
  .cap__summary{
    font-size: clamp(16px, 4vw, 18px);
    line-height: 1.5;
    max-width: none;
    margin-bottom: 16px;
  }
  .cap__tags{ gap: 7px; margin-bottom: 18px; }
  .cap__tags li{
    font-size: 15.5px; padding: 8px 14px;
    /* the desktop entrance animation is per-pane; all six are open here */
    opacity: 1; transform: none; animation: none !important;
  }
  /* Same label/value 2-column grid as desktop — it already reads fine at
     full width on a phone, just with a narrower label column. */
  .cap__facts{
    grid-template-columns: minmax(100px, max-content) 1fr;
    column-gap: 14px;
    row-gap: 10px;
    padding-top: 16px;
  }
  .cap__facts dt{ font-size: 15px; }
  .cap__facts dd{ font-size: 16.5px; }
}

.partners__marquee{
  overflow: hidden;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  padding: clamp(12px, 2vh, 18px) 0;
  width: 100vw; margin-left: calc(50% - 50vw);
  /* clear the capability list above it */
  margin-top: clamp(28px, 5vh, 56px);
}
.partners__track{
  display: flex; gap: clamp(40px, 6vw, 80px);
  width: max-content;
  /* skills track is ~4× longer than the old teams track — a longer
     duration keeps the px/s drift speed the same */
  animation: marquee 70s linear infinite;
}
.partners__track span{
  font-family: var(--display); font-weight: 500;
  font-size: clamp(14px, 1.5vw, 18px);
  white-space: nowrap;
  color: var(--faint);
  transition: color 250ms ease;
}
.partners__track span:hover{ color: var(--text); }
@keyframes marquee{ from{ transform: translateX(0); } to{ transform: translateX(-50%); } }

/* ── Products — a continuous run of equal cards ────────────────────────
   No pinning, no stacking, no snapping: the section scrolls like ordinary
   document content. The card pitch used to be exactly one viewport, because
   the engine moved one card per gesture and the two had to agree. Nothing
   snaps now, so that huge gap is just dead space — the cards sit a normal
   distance apart and scroll past continuously.

   Every card is EXACTLY --pcard-h tall, so they are all the same size no
   matter how much copy a project has (the overflow lives behind Read more). */
.products{ width: var(--wrap); margin: 0 auto; padding: 40px 0 120px; }

/* Section title. Centred like Skills so the two landed sections share a
   rhythm, rather than left-aligned like the pinned Experience/Milestones
   headings, which sit inside their own full-bleed stages. */
.products__head{
  text-align: center;
  max-width: 62ch;
  margin: 0 auto clamp(34px, 6vh, 72px);
  padding-top: clamp(20px, 5vh, 60px);
}
.products__label{
  font-size: 14px; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--muted); margin-bottom: 10px;
}
.products__h{
  font-family: var(--display); font-weight: 700;
  font-size: clamp(30px, 2vw + 2.6vh, 54px);
  line-height: 1.06; letter-spacing: -0.032em;
  margin-bottom: clamp(12px, 2vh, 20px);
}
.products__sub{
  color: var(--muted);
  font-size: clamp(15px, 1.15vw, 17.5px);
  line-height: 1.55;
  max-width: 52ch;
  margin: 0 auto;
}
.product{
  position: relative;
  height: var(--pcard-h);
  margin-bottom: clamp(26px, 5vh, 64px);
  border-radius: 30px;
  border: 1px solid var(--line);
  background: linear-gradient(165deg, #101019, #08080f);
  box-shadow: 0 -20px 80px rgba(0,0,0,0.6);
  overflow: hidden;   /* the panels slide in from outside the card */
  isolation: isolate;
}

/* The default two-part view: information left, imagery right.
   No padding on the face itself — the info column carries its own, so the
   imagery runs full-bleed to the card's edges and is clipped by its radius. */
.product__face{
  position: absolute; inset: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: minmax(0, 1fr);
  gap: clamp(12px, 1.3vw, 22px);
  align-items: stretch;          /* both columns own the full card height */
  padding: 0;
  transition: transform 0.62s var(--ease), opacity 0.34s var(--ease);
}
.product.is-open .product__face{
  transform: translate3d(-7%, 0, 0);
  opacity: 0;
  pointer-events: none;
}

/* Full-card overlays. Both sit off the right edge and slide in over the
   face, so the expand/collapse is transform + opacity only — no layout,
   nothing the compositor cannot handle on its own. */
.product__panel{
  position: absolute; inset: 0;
  z-index: 2;
  display: flex; flex-direction: column;
  padding: clamp(26px, 3.4vw, 52px);
  background: linear-gradient(165deg, #101019, #08080f);
  overflow-y: auto;
  overflow-x: hidden;
  transform: translate3d(100%, 0, 0);
  opacity: 0;
  visibility: hidden;
  transition: transform 0.62s var(--ease), opacity 0.34s var(--ease),
              visibility 0s linear 0.62s;
}
.product.is-reading .product__panel--read,
.product.is-viewing .product__panel--shots{
  transform: none;
  opacity: 1;
  visibility: visible;
  transition: transform 0.62s var(--ease), opacity 0.34s var(--ease),
              visibility 0s linear 0s;
}

/* ── left column: brand row at the top, the pitch pinned to the bottom ── */
.product__info{
  display: flex; flex-direction: column;
  padding: clamp(24px, 2.9vw, 46px);
  min-height: 0;
}
.product__head{
  display: flex; align-items: center; justify-content: space-between;
  gap: clamp(12px, 1.4vw, 20px);
}
.product__brand{ display: flex; align-items: center; gap: 13px; min-width: 0; }
.product__logo{
  flex: none;
  display: grid; place-items: center;
  width: 50px; height: 50px; border-radius: 14px;
  font-family: var(--display); font-weight: 700; font-size: 16px;
  color: #0a0510;
  background: linear-gradient(135deg, var(--h1), var(--h2));
}
/* The brand line, not the headline — the pitch below carries the size. These
   project names are long, so it is capped at two lines rather than pushing
   into the button beside it. */
.product__name{
  font-family: var(--display); font-weight: 700;
  font-size: clamp(16.5px, 1.5vw, 21px);
  letter-spacing: -0.022em; line-height: 1.2;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden;
}
/* Solid pill, the one high-contrast element on the card. */
.product__visit{
  flex: none;
  display: inline-flex; align-items: center;
  font-family: var(--display); font-weight: 700; font-size: 14px;
  letter-spacing: -0.005em;
  color: #08080f;
  background: var(--text);
  border-radius: 999px;
  padding: 11px 19px;
  white-space: nowrap;
  transition: background 220ms ease, transform 220ms var(--ease);
}
.product__visit:hover{ background: #fff; transform: translateY(-1px); }
/* Not every project has a public repo. This one is a plain label, not a
   link — it must not look clickable, because there is nothing to click. */
.product__visit--private{
  background: none;
  color: var(--faint);
  border: 1px dashed var(--line);
  cursor: default;
}
.product__visit--private:hover{ background: none; transform: none; }
/* The short label frees up room for the project name on a narrow screen. */
.visit__short{ display: none; }

/* Only shown on a project that is actually running somewhere. The rest are
   builds, and saying so on each of them would be noise. */
.product__status{
  display: inline-flex; align-items: center; gap: 8px;
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-weight: 500;
  font-size: 14px; letter-spacing: 0.06em; text-transform: uppercase;
  color: #6affc8;
  margin-bottom: 14px;
}
.product__status i{
  width: 7px; height: 7px; border-radius: 50%;
  background: #6affc8;
  box-shadow: 0 0 0 3px rgba(106,255,200,0.16);
}

/* Swipe is invisible without a prompt; the buttons are the discoverable
   route and this just says the shortcut exists. Phones only. */
.product__hint{
  display: none;
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-weight: 500;
  font-size: 14px; letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--faint);
}

/* Tags and headline with proper spacing from the title. */
.product__lede{
  margin-top: clamp(12px, 1.5vh, 18px);
  flex: 1;
  min-height: 0;   /* keeps .actions pinned at the same spot on every card, however long the copy is — see .product__d below */
  display: flex;
  flex-direction: column;
  width: 100%;
}

.product__tags{
  list-style: none; display: flex; flex-wrap: wrap; gap: 9px;
  margin-top: 0;
  margin-bottom: clamp(18px, 2.6vh, 30px);
}
.product__tags li{
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-weight: 500;
  font-size: 14px; letter-spacing: 0.04em; text-transform: uppercase;
  padding: 9px 14px; border-radius: 10px;
  border: 1px solid var(--line);
  background: rgba(244,243,240,0.04);
  color: var(--muted);
}
.product__tag{
  font-family: var(--display); font-weight: 700;
  font-size: clamp(27px, 3.2vw, 44px);
  letter-spacing: -0.028em; line-height: 1.08;
  margin-bottom: clamp(14px, 2vh, 22px); color: var(--text);
  max-width: 100%;
}
/* Fills available space until Read more button. */
/* flex:1 + min-height:0 lets this shrink to whatever room is actually left
   in .product__lede instead of pushing .product__actions further down when
   a description runs long (was the cause of the Read-more button sitting
   lower on some cards than others). The mask fades the cut-off line instead
   of clipping it mid-word. */
.product__d{
  font-size: clamp(16px, 1.25vw, 18px); line-height: 1.62;
  color: var(--muted);
  flex: 1;
  min-height: 0;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 26px), transparent 100%);
          mask-image: linear-gradient(to bottom, #000 calc(100% - 26px), transparent 100%);
}

/* ── the two Read more / View more controls ── */
.product__actions{
  display: flex; align-items: center; flex-wrap: wrap;
  gap: clamp(14px, 1.6vw, 24px);
  margin-top: clamp(18px, 2.6vh, 28px);
  padding-top: clamp(18px, 2.6vh, 28px);
  border-top: 1px solid var(--line);   /* the rule under the copy */
}
.product__more{
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--display); font-weight: 500; font-size: 14.5px;
  letter-spacing: 0.01em;
  color: var(--text);
  padding: 11px 20px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: rgba(244,243,240,0.05);
  cursor: pointer;
  transition: background 240ms ease, border-color 240ms ease;
}
.product__more:hover{
  background: rgba(244,243,240,0.1);
  border-color: rgba(244,243,240,0.32);
}
.product__more i{ font-style: normal; transition: transform 320ms var(--ease); }
.product__more:hover i{ transform: translateX(4px); }
/* Sits over the imagery instead of below it, so the image can run full-bleed. */
.product__more--media{
  position: absolute;
  right: clamp(16px, 1.5vw, 24px);
  bottom: clamp(16px, 1.5vw, 24px);
  z-index: 2;
  background: rgba(6,6,12,0.82);
  border-color: rgba(244,243,240,0.2);
}
.product__more--media:hover{
  background: rgba(6,6,12,0.94);
  border-color: rgba(244,243,240,0.38);
}

/* ── right column: full-bleed imagery ──
   No padding, no radius of its own — the card's overflow:hidden and 30px
   radius clip it, so it meets the card's top, right and bottom edges. */
.product__media{ position: relative; min-height: 0; }
.product__shot{
  position: absolute; inset: 0;
  overflow: hidden;
  /* Flex centering here only matters for .shot__phone below — .shot__ui,
     .shot__glyph and .shot__window are all position:absolute, so they're
     out of flow and unaffected by this either way. */
  display: flex; align-items: center; justify-content: center;
  background:
    radial-gradient(110% 120% at 18% 0%, color-mix(in srgb, var(--h1) 20%, transparent), transparent 58%),
    radial-gradient(110% 120% at 88% 100%, color-mix(in srgb, var(--h2) 17%, transparent), transparent 58%),
    linear-gradient(160deg, #12121c, #07070d);
  border-left: 1px solid var(--line);
}
/* A real screenshot dropped in here takes the whole panel, edge to edge. */
.product__shot img{ width: 100%; height: 100%; object-fit: cover; display: block; }

/* A screenshot floats as a framed browser window over .product__shot's own
   gradient instead of filling the panel edge to edge — the gradient shows
   as a border of color around it, the same "app window on a colored
   background" look product-landing-page mockups use. */
.shot__window{
  position: absolute; inset: 9%;
  display: flex; flex-direction: column;
  border-radius: 12px;
  overflow: hidden;
  background: #0c0c14;
  border: 1px solid rgba(255,255,255,0.12);
  box-shadow: 0 30px 70px rgba(0,0,0,0.55), 0 0 0 1px rgba(0,0,0,0.25);
}
.shot__windowbar{
  flex: none;
  height: 24px;
  display: flex; align-items: center; gap: 6px;
  padding: 0 11px;
  background: rgba(255,255,255,0.05);
  border-bottom: 1px solid rgba(255,255,255,0.08);
}
.shot__windowbar span{
  width: 8px; height: 8px; border-radius: 50%;
  background: rgba(255,255,255,0.22);
}
.shot__windowbar span:nth-child(1){ background: #ff6159; }
.shot__windowbar span:nth-child(2){ background: #ffbd2e; }
.shot__windowbar span:nth-child(3){ background: #28c840; }
.shot__window img{
  flex: 1; min-height: 0;
  width: 100%; height: 100%;
  /* Fills the frame edge to edge — these screenshots are wider than the
     window, so a centered cover crop trims the left/right sides evenly
     rather than leaving letterboxed gaps or cutting off the page's top. */
  object-fit: cover; object-position: center;
  display: block;
}

/* A device mockup (phone frame already baked into the image, background
   already removed) floats centered over .product__shot's own gradient —
   contain, not cover, since cropping would cut into the phone's own edges
   rather than just trimming empty space. */
/* Wrapper gets a definite box (inset against .product__shot, which itself
   has a definite absolute-positioned height) — percentage max-height on
   the IMG directly was resolving unreliably against the flex container's
   cross size. object-fit:contain inside a definitely-sized box is the one
   technique that structurally cannot crop, the same one .shot__window
   already uses successfully elsewhere on this page (with cover instead of
   contain there, since that one is allowed to crop). */
.shot__phoneWrap{
  position: absolute;
  inset: 6%;
}
/* Two classes (0,2,0) so this reliably beats .product__shot img's
   (0,1,1) — that base rule's object-fit:cover was winning on specificity
   regardless of source order and silently cropping this. */
.shot__phoneWrap .shot__phone{
  width: 100%; height: 100%;
  object-fit: contain;
  display: block;
  filter: drop-shadow(0 30px 55px rgba(0,0,0,0.5));
}
/* Flex column rather than percentage heights: the panel is now as tall as
   the card allows, and percentages turned these into slabs. The rows keep a
   fixed weight and the last block absorbs whatever height is left. */
.shot__ui{
  position: absolute; inset: 10% 9%;
  display: flex; flex-direction: column; gap: 16px;
}
.shot__ui i{
  display: block; flex: none; height: 15px;
  border-radius: 8px;
  background: linear-gradient(90deg, rgba(255,255,255,0.12), rgba(255,255,255,0.03));
}
.shot__ui i:nth-child(1){ width: 46%; }
.shot__ui i:nth-child(2){ width: 82%; }
.shot__ui i:nth-child(3){ width: 68%; }
.shot__ui i:nth-child(4){
  width: 90%;
  flex: 1 1 auto; min-height: 64px;
  background: linear-gradient(90deg, color-mix(in srgb, var(--h1) 22%, transparent), rgba(255,255,255,0.04));
}
/* Bottom-LEFT: the View more control now lives in the bottom-right of this
   same panel, and the two were sitting on top of each other. */
.shot__glyph{
  position: absolute; left: 9%; bottom: 6%;
  font-family: var(--display); font-weight: 700;
  font-size: clamp(26px, 3.4vw, 44px);
  letter-spacing: -0.02em;
  background: linear-gradient(120deg, var(--h1), var(--h2));
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
  opacity: 0.9;
}

/* ── expanded panel contents ──
   Sized to fit the fixed card height rather than scroll: an inner scroller
   inside the frame would fight the page's own wheel handling. */
.product__back{
  align-self: flex-start;
  position: sticky; top: 0;
  z-index: 3;
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--display); font-weight: 500; font-size: 14px;
  letter-spacing: 0.02em;
  color: var(--text);
  padding: 9px 16px 9px 13px;
  border-radius: 999px;
  border: 1px solid rgba(255,106,213,0.4);
  background: rgba(20,14,20,0.92);
  cursor: pointer;
  transition: color 240ms ease, border-color 240ms ease, box-shadow 240ms ease;
  /* Stays sticky at the top of a long, scrolled panel — the glow (rather
     than just a border) is what actually catches your eye against a wall
     of scrolled-past text instead of blending into it. */
  box-shadow: 0 0 0 1px rgba(255,106,213,0.15), 0 0 18px 2px rgba(255,106,213,0.38);
  animation: backGlowPulse 2.6s ease-in-out infinite;
}
.product__back:hover{
  color: var(--text); border-color: rgba(255,106,213,0.7);
  box-shadow: 0 0 0 1px rgba(255,106,213,0.25), 0 0 26px 4px rgba(255,106,213,0.55);
}
.product__back i{ font-style: normal; transition: transform 320ms var(--ease); }
.product__back:hover i{ transform: translateX(-4px); }
@keyframes backGlowPulse{
  0%, 100%{ box-shadow: 0 0 0 1px rgba(255,106,213,0.15), 0 0 18px 2px rgba(255,106,213,0.38); }
  50%{ box-shadow: 0 0 0 1px rgba(255,106,213,0.22), 0 0 26px 5px rgba(255,106,213,0.58); }
}
@media (prefers-reduced-motion: reduce){
  .product__back{ animation: none; }
}

.panel__label{
  display: block;
  margin-top: clamp(16px, 2.6vh, 26px);
  font-family: var(--display); font-weight: 500;
  font-size: 14px; letter-spacing: 0.13em; text-transform: uppercase;
  color: var(--faint);
}
.panel__h{
  font-family: var(--display); font-weight: 700;
  font-size: clamp(26px, 3vw, 38px);
  letter-spacing: -0.02em; line-height: 1.1;
  margin: 8px 0 clamp(16px, 2.4vh, 26px);
}
.panel__body{
  /* .product__panel is the scroller now (overflow-y:auto), so this sizes
     to its natural content height like any normal flex child instead of
     being compressed into whatever space was "left over" — that old
     flex:1;min-height:0 let long copy spill silently past this box's own
     bottom edge (nothing here clipped it) and overlap .panel__stack below,
     since .panel__stack was still positioned at the box's ALLOCATED height,
     not its actual overflowed height. */
  display: grid;
  grid-template-columns: 1.15fr 0.85fr;
  gap: clamp(24px, 3.4vw, 52px);
  align-content: start;
}
.panel__copy p{
  font-size: clamp(16px, 1.32vw, 19px);
  line-height: 1.62;
  color: var(--muted);
  margin-bottom: 15px;
}
.panel__copy p:last-child{ margin-bottom: 0; }
.panel__points{
  list-style: none;
  border-left: 1px solid var(--line);
  padding-left: clamp(18px, 1.8vw, 28px);
}
.panel__points li{
  position: relative;
  font-family: var(--display); font-weight: 500;
  font-size: clamp(15px, 1.18vw, 17.5px);
  line-height: 1.4;
  color: var(--text);
  padding-bottom: 16px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--line);
}
.panel__points li:last-child{ border-bottom: 0; padding-bottom: 0; margin-bottom: 0; }
.panel__stack{ margin: clamp(16px, 2.4vh, 26px) 0 0; }

/* image gallery */
.gshots{
  flex: 1; min-height: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-rows: repeat(2, minmax(0, 1fr));
  gap: clamp(12px, 1.4vw, 20px);
}
/* Diagrams-AI has 6 real screenshots rather than the usual 4 mocked ones —
   3×2 instead of 2×2. Scoped to this modifier only, so every other
   project's gallery keeps its original 2×2 layout. */
.gshots--six{
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-template-rows: repeat(2, minmax(0, 1fr));
}
/* Performance Monitoring has 3 real screenshots rather than the usual 4 —
   one row of 3 rather than a 2×2 grid with an empty fourth cell. */
.gshots--three{
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-template-rows: minmax(0, 1fr);
}
.gshot{ display: flex; flex-direction: column; min-height: 0; }
.gshot__frame{
  position: relative;
  flex: 1; min-height: 0;
  border-radius: 16px; overflow: hidden;
  border: 1px solid var(--line);
  background:
    radial-gradient(110% 130% at 20% 0%, color-mix(in srgb, var(--h1) 16%, transparent), transparent 58%),
    linear-gradient(160deg, #12121c, #07070d);
}
/* a real screenshot dropped in here takes over the frame as-is */
.gshot__frame img{ width: 100%; height: 100%; object-fit: cover; display: block; }
.gshot__frame i{
  position: absolute; left: 11%; right: 11%;
  height: 7%;
  border-radius: 5px;
  background: linear-gradient(90deg, rgba(255,255,255,0.13), rgba(255,255,255,0.03));
}
.gshot__frame i:nth-of-type(1){ top: 18%; right: 46%; }
.gshot__frame i:nth-of-type(2){ top: 33%; }
.gshot__frame i:nth-of-type(3){ top: 48%; right: 28%; }
/* Vary the four frames so the gallery does not read as one tile repeated.
   These are placeholders — dropping an <img> into .gshot__frame replaces
   the whole mock, since the image covers the frame. */
.gshot:nth-child(2) .gshot__frame i:nth-of-type(1){ right: 24%; }
.gshot:nth-child(2) .gshot__frame i:nth-of-type(3){ right: 52%; }
.gshot:nth-child(3) .gshot__frame i:nth-of-type(1){ right: 62%; }
.gshot:nth-child(3) .gshot__frame i:nth-of-type(2){ top: 33%; right: 38%; }
.gshot:nth-child(3) .gshot__frame i:nth-of-type(3){ top: 48%; right: 11%; }
.gshot:nth-child(4) .gshot__frame i:nth-of-type(1){ right: 34%; }
.gshot:nth-child(4) .gshot__frame i:nth-of-type(2){ top: 33%; right: 58%; }
.gshot:nth-child(4) .gshot__frame i:nth-of-type(3){ right: 40%; }
.gshot:nth-child(even) .gshot__frame{
  background:
    radial-gradient(110% 130% at 80% 100%, color-mix(in srgb, var(--h2) 16%, transparent), transparent 58%),
    linear-gradient(160deg, #12121c, #07070d);
}
/* Numbered rather than the card's glyph repeated four times — the repeat
   read as one tile pasted over and over. */
.gshot__glyph{
  position: absolute; right: 9%; bottom: 9%;
  font-family: var(--display); font-weight: 700;
  font-size: clamp(18px, 2vw, 26px);
  letter-spacing: -0.02em;
  background: linear-gradient(120deg, var(--h1), var(--h2));
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
}
.gshot figcaption{
  margin-top: 9px;
  font-family: var(--display); font-weight: 500;
  font-size: 14px; letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--faint);
}

/* Experience — pinned horizontal-scroll runway.
   Runway height = 100vh + horizontal distance (set by JS). The pin stays
   stuck while vertical scroll translates the strip sideways. */
.pressrun{ position: relative; }
.pressrun__pin{
  position: sticky; top: 0;
  height: 100vh;
  display: flex; flex-direction: column;
  justify-content: center;
  gap: clamp(22px, 4vh, 40px);
  /* Insets reserve the two pieces of fixed chrome: the top bar (~74px) and
     the bottom navbar (~66px plus breathing room). The heading and cards are
     centred in what is left, so the title is always visible above and the
     navbar never sits on top of a card. */
  padding: 78px calc((100vw - var(--wrap)) / 2) 96px;
  overflow: hidden;
}
.press__h{
  font-family: var(--display); font-weight: 700;
  font-size: clamp(30px, 4.4vw, 52px);
  line-height: 1.06; letter-spacing: -0.025em;
  color: var(--text);
}
.press__strip{
  display: flex; gap: 18px;
  width: max-content;
  transform: translate3d(calc(var(--px, 0) * -1px), 0, 0);
  will-change: transform;
}
/* Experience cards now carry a full role write-up — title, employer, dates,
   responsibilities, an outcome where there is one, and the stack — so they
   are wider and taller than the one-paragraph cards they replaced. */
.pcard{
  flex: 0 0 min(86vw, 430px);
  /* Every role card is exactly this tall whatever its copy — the face is a
     summary, and the full write-up lives in the enlarged view instead of
     stretching the card. Raised from clamp(300,42vh,430) so the larger
     type below actually has room — see the readability pass. */
  height: var(--exp-card-h);
  display: flex; flex-direction: column;
  padding: clamp(20px, 2.2vw, 26px);
  border-radius: 22px;
  border: 1px solid var(--line);
  background: linear-gradient(165deg, rgba(255,255,255,0.05), rgba(255,255,255,0.012));
}
.pcard__head{ display: block; margin-bottom: 6px; }
.pcard__head b{
  display: block;
  font-family: var(--display); font-weight: 700;
  font-size: clamp(21px, 1.75vw, 25px);
  letter-spacing: -0.016em; line-height: 1.2;
  margin-bottom: 5px;
}
.pcard__org{
  display: block;
  font-size: clamp(17px, 1.05vw, 18px); font-weight: 500; line-height: 1.35;
  color: var(--accent);
}
.pcard__meta{
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-weight: 500;
  font-size: 16px; letter-spacing: 0.02em; text-transform: uppercase;
  /* Dedicated color rather than var(--faint) — dates/location are read, not skimmed. */
  color: rgba(244,243,240,0.76);
  margin-bottom: 13px;
  padding-bottom: 11px;
  border-bottom: 1px solid var(--line);
}
/* "Current" marker on the role that is ongoing. */
.pcard__now{
  display: inline-block;
  margin-left: 6px;
  padding: 3px 9px;
  border-radius: 999px;
  font-size: 15px; letter-spacing: 0.04em; font-weight: 500;
  color: #6affc8;
  border: 1px solid rgba(106,255,200,0.3);
  background: rgba(106,255,200,0.08);
}

/* ── card face: a summary, not the write-up ── */
.pcard__sum{
  font-size: clamp(17px, 1.05vw, 18px); line-height: 1.55;
  color: rgba(244,243,240,0.82);
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 4;
  overflow: hidden;
}
.pcard__win{
  margin-top: 14px;
  padding: 11px 13px;
  border-radius: 12px;
  border: 1px solid rgba(106,255,200,0.26);
  background: rgba(106,255,200,0.055);
}
/* The clamp lives on the paragraph, not the box: with the label inside a
   -webkit-box the label counted as one of the clamped lines and the last
   line of text spilled past the border. */
.pcard__win p{
  font-size: 17px; line-height: 1.5;
  color: var(--text);
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3;
  overflow: hidden;
}
.pcard__win span{
  display: block;
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-weight: 500;
  font-size: 15.5px; letter-spacing: 0.06em; text-transform: uppercase;
  color: #6affc8;
  margin-bottom: 4px;
}
.pcard__win strong, .pcard__sum strong{ color: var(--text); font-weight: 600; }
.pcard__more{
  align-self: flex-start;
  margin-top: auto;                 /* pinned to the bottom of every card */
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--display); font-weight: 500; font-size: 16px;
  color: var(--text);
  padding: 10px 17px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: rgba(244,243,240,0.05);
  cursor: pointer;
  transition: background 240ms ease, border-color 240ms ease;
}
.pcard__more:hover{ background: rgba(244,243,240,0.1); border-color: rgba(244,243,240,0.32); }
.pcard__more i{ font-style: normal; transition: transform 320ms var(--ease); }
.pcard__more:hover i{ transform: translateX(4px); }

/* ── enlarged view ──
   The full STAR write-up, at a size meant to be read rather than scanned.
   It is a fixed overlay over the whole frame, so it is not constrained by
   the card it came from. */
.rolebig{ position: fixed; inset: 0; z-index: 200; }
.rolebig[hidden]{ display: none; }
.rolebig__scrim{
  position: absolute; inset: 0;
  background: rgba(3,3,7,0.72);
  opacity: 0;
  transition: opacity 300ms ease;
}
.rolebig__card{
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%) scale(0.96);
  width: min(92vw, 780px);
  max-height: min(86vh, 760px);
  overflow-y: auto;
  overflow-x: hidden;
  padding: clamp(24px, 3vw, 38px);
  border-radius: 24px;
  border: 1px solid rgba(244,243,240,0.16);
  background: linear-gradient(165deg, #14141f, #0a0a12);
  box-shadow: 0 40px 120px rgba(0,0,0,0.7);
  opacity: 0;
  transition: opacity 300ms ease, transform 380ms var(--ease);
}
.rolebig.is-open .rolebig__scrim{ opacity: 1; }
.rolebig.is-open .rolebig__card{ opacity: 1; transform: translate(-50%, -50%) scale(1); }
.rolebig__close{
  position: sticky; top: 0; float: right;
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--display); font-weight: 500; font-size: 14px;
  color: var(--muted);
  padding: 8px 14px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: rgba(20,20,31,0.9);
  cursor: pointer;
  transition: color 200ms ease, border-color 200ms ease;
  z-index: 10;
  margin-bottom: 16px;
}
.rolebig__body{
  clear: both;
}
.rolebig__close:hover{ color: var(--text); border-color: rgba(244,243,240,0.34); }

/* ── Certificate preview overlay ──
   Same shell language as .rolebig (scrim + centered glass card, identical
   fade/scale timing) so a milestone credential opens with the exact motion
   a visitor already learned from Experience → Read more. The PDF renders
   inline in an iframe rather than the browser navigating to it, so nothing
   on the card itself is a direct downloadable link. */
.certbig{ position: fixed; inset: 0; z-index: 200; }
.certbig[hidden]{ display: none; }
.certbig__scrim{
  position: absolute; inset: 0;
  background: rgba(3,3,7,0.72);
  opacity: 0;
  transition: opacity 300ms ease;
}
.certbig__card{
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%) scale(0.96);
  width: min(92vw, 860px);
  height: min(88vh, 840px);
  display: flex; flex-direction: column;
  padding: clamp(20px, 2.6vw, 30px);
  border-radius: 24px;
  border: 1px solid rgba(244,243,240,0.16);
  background: linear-gradient(165deg, #14141f, #0a0a12);
  box-shadow: 0 40px 120px rgba(0,0,0,0.7);
  opacity: 0;
  transition: opacity 300ms ease, transform 380ms var(--ease);
}
.certbig.is-open .certbig__scrim{ opacity: 1; }
.certbig.is-open .certbig__card{ opacity: 1; transform: translate(-50%, -50%) scale(1); }
.certbig__head{
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px;
  margin-bottom: clamp(14px, 2vh, 20px);
}
.certbig__title{
  min-width: 0;   /* lets a long title actually wrap instead of squeezing the close button */
  overflow-wrap: break-word;
  font-family: var(--display); font-weight: 700;
  font-size: clamp(16px, 1.7vw, 20px);
  letter-spacing: -0.015em; line-height: 1.25;
  color: var(--text);
}
.certbig__close{
  flex: none;
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--display); font-weight: 500; font-size: 14px;
  color: var(--muted);
  padding: 8px 14px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: rgba(20,20,31,0.9);
  cursor: pointer;
  transition: color 200ms ease, border-color 200ms ease;
}
.certbig__close:hover{ color: var(--text); border-color: rgba(244,243,240,0.34); }
.certbig__frame{
  flex: 1; min-height: 0;
  display: grid; place-items: center;
  border-radius: 14px;
  overflow: hidden;
  border: 1px solid var(--line);
  background: #16161f;
}
.certbig__img{
  max-width: 100%; max-height: 100%;
  object-fit: contain;
  display: block;
  border-radius: 6px;
  -webkit-user-drag: none; user-drag: none;
  -webkit-touch-callout: none;
  user-select: none;
  pointer-events: none;   /* no right-click "Save image as" on the image itself */
}

@media (max-width: 860px){
  /* A long credential title (e.g. "IBM Full Stack Software Developer —
     Professional Certificate") sitting beside a "Close" text button had no
     room to wrap safely on a phone-width card — align to the top instead of
     centering so the button stays pinned in the corner while the title
     wraps to 2-3 lines below it, and drop the button to icon-only so it
     doesn't fight the title for horizontal space. */
  .certbig__card{ width: 94vw; height: min(90vh, 760px); padding: 16px; }
  .certbig__head{ align-items: flex-start; gap: 10px; }
  .certbig__title{ font-size: 16px; padding-top: 6px; }
  .certbig__close{ padding: 8px; }
  .certbig__closeLabel{ display: none; }
}

.rolefull__role{
  font-family: var(--display); font-weight: 700;
  font-size: clamp(21px, 2.2vw, 27px);
  letter-spacing: -0.02em; line-height: 1.18;
  margin-bottom: 6px;
}
.rolefull__org{ font-size: 17px; font-weight: 500; color: var(--accent); margin-bottom: 8px; }
.rolefull__meta{
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-weight: 500;
  font-size: 16px; letter-spacing: 0.02em; text-transform: uppercase;
  /* Dedicated color rather than var(--faint) — dates/location in the
     expanded modal are read carefully, not skimmed. */
  color: rgba(244,243,240,0.78);
  margin-bottom: 20px; padding-bottom: 18px;
  border-bottom: 1px solid var(--line);
}
.rolefull__skills{
  margin-top: 20px; padding-top: 16px;
  border-top: 1px solid var(--line);
  font-size: 16.5px; line-height: 1.6; color: rgba(244,243,240,0.78);
}

/* ── STAR, only ever rendered inside the enlarged view ── */
.star{ display: flex; flex-direction: column; gap: 18px; }
.star__row{ padding-left: 15px; border-left: 2px solid var(--line); }
.star__k{
  display: block;
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-weight: 500;
  font-size: 16px; letter-spacing: 0.06em; text-transform: uppercase;
  color: rgba(244,243,240,0.76);
  margin-bottom: 8px;
}
.star__row p{ font-size: 18px; line-height: 1.62; color: rgba(244,243,240,0.85); }
.star__row ul{ list-style: none; display: flex; flex-direction: column; gap: 9px; }
.star__row li{
  position: relative;
  padding-left: 17px;
  font-size: 18px; line-height: 1.6;
  color: rgba(244,243,240,0.85);
}
.star__row li::before{
  content: "";
  position: absolute; left: 0; top: 10px;
  width: 5px; height: 5px; border-radius: 50%;
  background: rgba(244,243,240,0.32);
}
.star__row:has(.star__result){ border-left-color: rgba(106,255,200,0.42); }
.star__row:has(.star__result) .star__k{ color: #6affc8; }
.star__result li{ color: var(--text); }
.star__result li::before{ background: #6affc8; }
.star strong{ color: var(--text); font-weight: 600; }

/* In Press cards — whole card is a link; same shell as .pcard */
.pcard--press{
  display: flex; flex-direction: column;
  cursor: pointer;
  transition: transform 300ms var(--ease), border-color 300ms ease, background 300ms ease;
}
.pcard--press p{ flex: 1; }
.pcard--press:hover{
  transform: translateY(-6px);
  border-color: rgba(255,106,213,0.4);
  background: linear-gradient(165deg, rgba(255,106,213,0.07), rgba(255,255,255,0.015));
}
.pcard__more{
  color: var(--accent);
  border-bottom: 1px solid transparent;
  transition: border-color 200ms ease;
}
.pcard--press:hover .pcard__more{ border-color: var(--accent); }

/* A full STAR write-up is a lot of copy for a card that has to fit inside one
   pinned viewport. On a shorter screen the heading, the gap under it and the
   body all come down together so the tallest role still fits without the card
   being clipped. Threshold is the frame's own height, not the browser's — the
   portfolio renders in an iframe the parent sizes independently. */
/* Short frames: the heading and the card face come down together so the
   title, the cards and the navbar all still have room. The enlarged view is
   an overlay sized to the viewport, so it needs its own smaller step only at
   the very shortest heights. */
/* Short frames: the heading and the card face come down together so the
   title, the cards and the navbar all still have room. The enlarged view is
   an overlay sized to the viewport, so it needs its own smaller step only at
   the very shortest heights. */
@media (max-height: 1000px){
  .press__h{ font-size: clamp(28px, 4.4vw, 44px); }
  .pressrun__pin{ gap: clamp(14px, 2vh, 24px); padding-top: 74px; padding-bottom: 88px; }
  /* Was also shrinking every font here (title to 16.5px, body to 14px) —
     removed per the readability pass: a short viewport gets tighter
     padding and fewer clamped lines (both pure layout), never smaller
     professional-experience text. --exp-card-h already grew to make
     room for this. */
  .pcard{ padding: clamp(17px, 1.8vw, 22px); }
  .pcard__meta{ margin-bottom: 11px; padding-bottom: 9px; }
  .pcard__sum{ line-height: 1.5; -webkit-line-clamp: 3; }
  .pcard__win{ margin-top: 11px; padding: 9px 11px; }
  .pcard__win p{ -webkit-line-clamp: 2; }
  .pcard__more{ padding: 9px 15px; }
}
@media (max-height: 780px){
  /* Was also shrinking STAR body text to 14px here — no longer needed: the
     enlarged view (.rolebig__card) already scrolls when content is tall,
     so a short viewport doesn't need smaller text, just the same content
     scrolled a bit further. Only the layout gaps still tighten. */
  .star{ gap: 14px; }
  .rolefull__meta{ margin-bottom: 14px; padding-bottom: 12px; }
}

/* Short viewports — the card height is driven by the screen, so on a laptop
   with little vertical room the type has to come down with it or the longest
   project spills out of a card it is no longer allowed to grow. */
@media (max-height: 800px){
  .product__logo{ width: 42px; height: 42px; font-size: 14px; border-radius: 12px; }
  .product__name{ font-size: clamp(16px, 1.35vw, 18px); }
  .product__visit{ font-size: 14px; padding: 9px 16px; }
  .product__tags{ margin-bottom: 14px; }
  .product__tags li{ font-size: 14px; padding: 7px 12px; }
  .product__tag{ font-size: clamp(22px, 2.5vw, 32px); margin-bottom: 11px; }
  .product__d{ font-size: 16px; line-height: 1.58; -webkit-line-clamp: 2; }
  .product__actions{ margin-top: 16px; padding-top: 16px; }

  .panel__label{ margin-top: 12px; }
  .panel__h{ font-size: clamp(21px, 2.4vw, 28px); margin-bottom: 14px; }
  .panel__copy p{ font-size: 16px; line-height: 1.55; margin-bottom: 11px; }
  .panel__points li{ font-size: 14.5px; padding-bottom: 12px; margin-bottom: 12px; }
  .gshot figcaption{ margin-top: 6px; font-size: 14px; }
}

/* ═══ IN PRESS — float stage (boilerlab press composition) ═══════════════
   A normal one-viewport section: the heading sits at the exact center of
   the screen, cards composed around it in two side columns anchored to
   screen center (px offsets from 50%, so the layout is stable on any
   viewport height).
   AUTO-PLAY: nothing here is driven by scroll. When the section comes into
   view, JS adds .is-live and the whole choreography runs on its own —
   heading fades in, then each card rises into its fixed spot on a small
   time stagger (--stag), while the slow bounded drift loops on the INNER
   element. Scrolling away removes .is-live so it replays on the next visit. */
.floatstage{ position: relative; height: 100vh; }
.floatstage__pin{
  position: relative;
  height: 100%;
  overflow: hidden;
}
.floatstage__h{
  position: absolute; left: 50%; top: 50%;
  text-align: center;
  z-index: 1;                                   /* cards float above it */
  transform: translate(-50%, -50%) scale(0.92);
  opacity: 0;
  transition: opacity 0.7s ease, transform 0.9s var(--ease);
  will-change: transform, opacity;
}
.floatstage.is-live .floatstage__h{
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}
.fcard{
  position: absolute;
  display: block;
  width: clamp(250px, 23vw, 340px);
  z-index: var(--z, 2);
  transform: translate3d(0, 110px, 0) rotate(var(--rot, 0deg)) scale(0.85);
  opacity: 0;
  transition: transform 0.9s var(--ease), opacity 0.7s ease;
  transition-delay: var(--stag, 0ms);
  will-change: transform, opacity;
}
.floatstage.is-live .fcard{
  transform: translate3d(0, 0, 0) rotate(var(--rot, 0deg)) scale(1);
  opacity: 1;
}
.fcard__in{
  display: flex; flex-direction: column;
  padding: 22px 22px 18px;
  border-radius: 20px;
  border: 1px solid var(--line);
  /* near-opaque so an overlapped card can't bleed through the one on top */
  background: linear-gradient(165deg, rgba(26,26,38,0.97), rgba(10,10,17,0.97));
  box-shadow: 0 24px 60px rgba(0,0,0,0.45);
  transition: border-color 300ms ease, background 300ms ease;
}
.fcard__in header{ display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; margin-bottom: 12px; }
.fcard__in header b{ font-family: var(--display); font-size: 18px; }
.fcard__in header span{ font-size: 13.5px; color: var(--accent); }
.fcard__in p{ flex: 1; font-size: 15.5px; line-height: 1.6; color: var(--muted); margin-bottom: 14px; }
.fcard__in footer{ font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--faint); }
.fcard:hover{
  z-index: calc(var(--z, 2) + 10) !important;
}
.fcard:hover .fcard__in{
  border-color: rgba(255,106,213,0.4);
  background: linear-gradient(165deg, rgba(44,26,44,0.97), rgba(12,10,18,0.97));
}
.fcard:hover .pcard__more{ border-color: var(--accent); }

/* Slow bounded drift — a small closed loop (±12px, ±0.6deg), so every card
   floats gently inside its own fixed zone and never wanders off. Unique
   --dur + negative --del per card keep them out of sync. It runs on the
   INNER element the whole time the stage is live, so the cards are already
   drifting as they fade in — no visible hand-off. */
.floatstage.is-live .fcard__in{
  animation: fdrift var(--dur, 30s) ease-in-out infinite;
  animation-delay: var(--del, 0s);
}
@keyframes fdrift{
  0%, 100%{ transform: translate3d(0, 0, 0) rotate(0deg); }
  25%     { transform: translate3d(10px, -13px, 0) rotate(0.6deg); }
  50%     { transform: translate3d(-8px, -5px, 0) rotate(-0.5deg); }
  75%     { transform: translate3d(-12px, 11px, 0) rotate(0.5deg); }
}

/* Narrow or very short viewports: the spatial composition can't fit, so the
   section becomes a plain readable vertical stack — no pin, no drift. */
@media (max-width: 960px), (max-height: 640px){
  .floatstage{ height: auto; }
  .floatstage__pin{
    position: static; height: auto; overflow: visible;
    display: flex; flex-direction: column; gap: 16px;
    padding: clamp(70px, 10vh, 110px) 6vw;
  }
  .floatstage__h{
    position: static !important; transform: none !important; opacity: 1 !important;
    text-align: left; margin-bottom: 14px;
  }
  .fcard{
    position: static !important; width: 100%;
    /* One card centers itself on desktop via an inline margin-left (see
       portfolio.html) — that inline style survives into this stacked
       layout and would otherwise drag a full-width card left by half its
       old desktop width, pushing it off-screen. */
    margin-left: 0 !important;
    transform: none !important; opacity: 1 !important;
    transition: none !important;
  }
  .fcard__in{ animation: none !important; }
}

/* ═══ MOON FOOTER ═══ */
/* ═══ FOOTER ═══ */
.endfoot{
  position: relative;
  padding: clamp(60px, 10vh, 110px) 6vw 96px;  /* clearance for bottom navbar */
}
.foot{
  position: relative;
  padding-top: 26px;
  border-top: 1px solid var(--line);
  display: flex; flex-wrap: wrap; gap: 26px;
  justify-content: space-between;
  text-align: left;
  font-size: 14px; color: var(--muted);
}
.foot__col p{ margin-bottom: 5px; }
.foot__col--links{ display: flex; flex-direction: column; gap: 6px; }
.foot__col--links a:hover{ color: var(--accent); }

/* ═══ RESPONSIVE ═══ */
@media (max-width: 860px){
  .flight{ height: calc(4 * 105vh + 100vh); }

  /* No scroll-driven entrance on a phone. Projects, Experience and
     Milestones are all plain vertical lists here, so the cards should just
     be there as you scroll past — nothing fading, rising or staggering in.
     (Milestones already drops its float composition and drift at 960px; this
     covers the fade that ran on top of it.) */
  .reveal{
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .fcard, .fcard__in, .pcard{
    transition: none !important;
    animation: none !important;
  }

  /* ── project cards ──
     The sliding panels stay on phones (they were flattened before, which
     made every card a wall of text). Read more / View more are reachable by
     button OR by swiping the card sideways — see productPanels() in
     portfolio.js. The card keeps a fixed height so the panels have a box to
     fill, and the imagery becomes a band across the top. */
  .product{
    height: clamp(500px, 76vh, 660px);
    margin-bottom: 22px;
    overflow: hidden;
  }
  /* A fixed 38/62 split rather than an aspect ratio: aspect-ratio ties the
     band's height to the card's WIDTH, so on a 768px tablet it grew to 432px
     of a 660px card and squeezed the text out the bottom. A row fraction
     keeps the same proportions on every screen. */
  .product__face{
    grid-template-columns: 1fr;
    grid-template-rows: 38% minmax(0, 1fr);
    gap: 0;
  }
  .product__media{ order: -1; }
  .product__shot{ border-left: 0; border-bottom: 1px solid var(--line); }
  .product__more--media{ right: 14px; bottom: 14px; font-size: 14px; padding: 9px 15px; }
  .product__info{ padding: 20px; }
  .product__head{ gap: 10px; }
  .product__logo{ width: 40px; height: 40px; border-radius: 12px; font-size: 14px; }
  /* Title must stay clearly above body text (.product__d is 16px below) —
     it was previously smaller than the body copy under it on phones. */
  .product__name{ font-size: 17px; -webkit-line-clamp: 2; }
  .product__visit{ font-size: 14px; padding: 9px 15px; }
  .visit__long{ display: none; }
  .visit__short{ display: inline; }
  .product__hint{ display: inline; }
  .product__lede{ margin-top: clamp(14px, 3vh, 26px); }
  .product__tags{ gap: 7px; margin-bottom: 14px; }
  .product__tags li{ font-size: 14px; padding: 7px 11px; border-radius: 8px; }
  .product__tag{ font-size: clamp(21px, 5.6vw, 27px); margin-bottom: 10px; }
  .product__d{ font-size: 16px; line-height: 1.55; -webkit-line-clamp: 3; }
  .product__actions{ margin-top: 16px; padding-top: 16px; }
  .product__more{ font-size: 14px; padding: 10px 17px; }

  /* panels: single column, and the highlight list is dropped — at this
     width it pushed the copy out of the card */
  .product__panel{ padding: 20px; }
  .panel__label{ margin-top: 10px; font-size: 14px; }
  .panel__h{ font-size: clamp(20px, 5.2vw, 26px); margin-bottom: 14px; }
  .panel__body{ grid-template-columns: 1fr; gap: 0; align-content: start; }
  .panel__points{ display: none; }
  .panel__copy p{ font-size: 16px; line-height: 1.58; margin-bottom: 11px; }
  .panel__stack{ margin-top: 16px; }
  .gshots{ gap: 10px; }
  .gshots--six{ grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: repeat(3, minmax(0, 1fr)); }
  .gshots--three{ grid-template-columns: minmax(0, 1fr); grid-template-rows: repeat(3, minmax(0, 1fr)); }
  .gshot figcaption{ margin-top: 6px; font-size: 14px; letter-spacing: 0.05em; }
  .gshot__glyph{ font-size: 16px; }

  /* ── Experience ──
     The pinned horizontal runway does not work on a phone: vertical scroll
     drove the strip sideways, so cards 2-4 sat off-screen with no way to
     reach them. It becomes a plain vertical stack instead. !important is
     required — layoutPress() writes the runway height as an inline style. */
  .pressrun{ height: auto !important; }
  .pressrun__pin{
    position: static !important;
    height: auto !important;
    overflow: visible !important;
    /* keep the section's own side gutters — the horizontal-run padding is a
       full-bleed calc that would put the cards against the screen edges */
    padding: 80px calc((100vw - var(--wrap)) / 2) 40px;
  }
  .press__strip{
    transform: none !important;
    width: 100% !important;
    flex-direction: column;
    gap: 16px;
  }
  /* the strip's cards are sized with a flex basis for the horizontal run —
     that has to be released or they keep their fixed track width */
  .pcard{ flex: 0 0 auto; width: 100%; }
  .pcard--press{ width: 100%; min-height: 0; }

  .top__logo{ font-size: 0; gap: 0; } /* logo mark only on phones */
  /* phones: let the section flow normally instead of filling a screen */
  .partners{ min-height: 0; display: block; padding: 70px 0 60px; }
  .cap{ margin-bottom: 30px; }
  /* The navbar is fixed over the page, so the last section needs to clear it.
     Seven items do not fit on a phone, so the bar scrolls sideways — the
     right-edge fade is what says so, since Contact is the item off the end.
     (The Contact section is also just the bottom of the page, so it is
     reachable by scrolling even if the bar's own scroll is missed.) */
  .bnav{ bottom: 12px; position: fixed; }
  .bnav::after{
    content: "";
    position: fixed;
    right: calc(50% - 47vw); bottom: 12px;
    width: 34px; height: 46px;
    pointer-events: none;
    border-radius: 0 999px 999px 0;
    background: linear-gradient(90deg, rgba(10,10,18,0), rgba(10,10,18,0.96) 72%);
  }
  .floatstage__pin{ padding-bottom: 96px; }
}

/* Short phones (an SE is 667px tall, so its card is only ~500px): the same
   layout with one less line of teaser and tighter rhythm. */
@media (max-width: 860px) and (max-height: 740px){
  .product__info{ padding: 16px 18px; }
  .product__lede{ margin-top: 12px; }
  .product__tag{ font-size: clamp(19px, 5.2vw, 23px); margin-bottom: 8px; }
  /* Was also dropping to 14px here — .product__d already clips via the
     flex/mask technique above, not this line-clamp, so shrinking the font
     bought nothing but harder-to-read body text on the shortest phones. */
  .product__d{ -webkit-line-clamp: 2; }
  .product__tags{ margin-bottom: 11px; }
  .product__actions{ margin-top: 12px; padding-top: 12px; }
  .product__more{ font-size: 14px; padding: 9px 15px; }
  .product__panel{ padding: 16px 18px; }
  .panel__h{ margin-bottom: 11px; }
  .panel__copy p{ line-height: 1.55; margin-bottom: 9px; }
  .panel__stack{ margin-top: 12px; }
}

/* ═══ REDUCED MOTION ═══ */
@media (prefers-reduced-motion: reduce){
  .flight{ height: auto !important; }
  .flight__pin{ position: static !important; height: auto !important; overflow: visible !important; }
  .fscene{
    position: static !important;
    min-height: 100vh;
    transform: none !important; opacity: 1 !important;
    pointer-events: auto !important;
  }
  .mission__p .ch, .mission__sign .ch{ opacity: 1 !important; }
  .type-caret{ display: none !important; }
  .pressrun{ height: auto !important; }
  .pressrun__pin{ position: static !important; height: auto !important; overflow: visible !important; padding-top: 90px; padding-bottom: 90px; }
  .press__strip{ transform: none !important; width: 100% !important; overflow-x: auto; }
  .floatstage{ height: auto !important; }
  .floatstage__pin{
    position: static !important; height: auto !important; overflow: visible !important;
    display: flex; flex-direction: column; gap: 16px; padding: 90px 6vw;
  }
  .floatstage__h{ position: static !important; transform: none !important; opacity: 1 !important; text-align: left; transition: none !important; }
  .fcard{ position: static !important; transform: none !important; opacity: 1 !important; width: 100%; margin-left: 0 !important; transition: none !important; }
  .fcard__in{ animation: none !important; }
  .reveal{ opacity: 1 !important; transform: none !important; transition: none !important; }
  .product{ transform: none !important; opacity: 1 !important; }
  .partners__track, .hero__hintline, .hero__kicker{ animation: none !important; }
}