/* ==========================================================================
   Arshit Vaghasiya — Portfolio
   Design system inherited from the Geek Updates Media Kit:
   monochrome, sharp corners, hairline borders, Space Grotesk + Archivo.
   ========================================================================== */

/* --- Fonts ----------------------------------------------------------------
   Self-hosted, so the first paint waits on no other server: one variable file
   per family (Latin only, all the site uses), preloaded in each page's <head>.
   Until a file arrives its text shows in Arial, resized to the same widths
   and line heights (size-adjust and the overrides, from the fonts' metrics
   against Arial and Arial Bold), so nothing jumps when the real font swaps in.
   Space Grotesk is mostly set in bold capitals, so its bold stand-in is sized
   for those. */
@font-face {
  font-family: "Archivo";
  src: url("assets/fonts/archivo-latin.woff2") format("woff2");
  font-weight: 100 900;
  font-display: swap;
}
@font-face {
  font-family: "Space Grotesk";
  src: url("assets/fonts/space-grotesk-latin.woff2") format("woff2");
  font-weight: 300 700;
  font-display: swap;
}
@font-face {
  font-family: "Archivo Fallback";
  src: local("Arial"), local("ArialMT");
  font-weight: 100 549;
  size-adjust: 97.89%;
  ascent-override: 89.69%;
  descent-override: 21.45%;
  line-gap-override: 0%;
}
@font-face {
  font-family: "Archivo Fallback";
  src: local("Arial Bold"), local("Arial-BoldMT");
  font-weight: 550 900;
  size-adjust: 93.97%;
  ascent-override: 93.44%;
  descent-override: 22.35%;
  line-gap-override: 0%;
}
@font-face {
  font-family: "Space Grotesk Fallback";
  src: local("Arial"), local("ArialMT");
  font-weight: 100 549;
  size-adjust: 110.69%;
  ascent-override: 88.89%;
  descent-override: 26.38%;
  line-gap-override: 0%;
}
@font-face {
  font-family: "Space Grotesk Fallback";
  src: local("Arial Bold"), local("Arial-BoldMT");
  font-weight: 550 900;
  size-adjust: 89.5%;
  ascent-override: 109.94%;
  descent-override: 32.63%;
  line-gap-override: 0%;
}

:root {
  --bg-primary: #131313;
  --bg-secondary: #1a1a1a;
  --bg-tertiary: #212121;
  --text-primary: #ffffff;
  --text-secondary: #e0e0e0;
  --text-tertiary: #8a8a8a;
  --grid-line: rgba(255, 255, 255, 0.08);
  --accent-color: #ffffff;
  --link-hover-bg: rgba(255, 255, 255, 0.05);
  --highlight-border: rgba(255, 255, 255, 0.28);

  --nav-bg: rgba(19, 19, 19, 0.82);
  --skeleton: #1d1d1d;
  --backdrop: rgba(0, 0, 0, 0.72);
  --scrollbar-thumb: rgba(255, 255, 255, 0.16);
  --scrollbar-thumb-hover: rgba(255, 255, 255, 0.34);

  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --maxw: 1280px; /* less the gutters, 1152px of content: the hero and the filter row fit one line */
  /* Spacing, fluid so phones get proportionally less without feeling cramped:
     the side gutters (nav and content line up on them), the room above and
     below each section, and the gap from a section's title to its content */
  --gutter: clamp(1.5rem, 5vw, 4rem);
  --section-space: clamp(4rem, 9vw, 7.5rem);
  --head-gap: clamp(2rem, 4vw, 3.25rem);
}

[data-theme="light"] {
  --bg-primary: #ffffff;
  --bg-secondary: #f5f5f5;
  --bg-tertiary: #ececec;
  --text-primary: #131313;
  --text-secondary: #2c2c2c;
  --text-tertiary: #666666;
  --grid-line: rgba(0, 0, 0, 0.1);
  --accent-color: #131313;
  --link-hover-bg: rgba(0, 0, 0, 0.05);
  --highlight-border: rgba(0, 0, 0, 0.3);

  --nav-bg: rgba(255, 255, 255, 0.85);
  --skeleton: #e8e8e8;
  --backdrop: rgba(20, 20, 20, 0.5);
  --scrollbar-thumb: rgba(0, 0, 0, 0.2);
  --scrollbar-thumb-hover: rgba(0, 0, 0, 0.4);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  scroll-padding-top: 80px;
  /* Always reserve the scrollbar's width, so the page doesn't shift sideways
     when the scrollbar comes and goes (short pages, or an open dialog). */
  scrollbar-gutter: stable;
  -webkit-text-size-adjust: 100%;
}
html:has(dialog[open]) { overflow: hidden; }

body {
  font-family: "Archivo", "Archivo Fallback", system-ui, -apple-system, sans-serif;
  background-color: var(--bg-primary);
  color: var(--text-primary);
  line-height: 1.65;
  min-height: 100vh;
  overflow-x: hidden;
  transition: background-color 0.3s ease, color 0.3s ease;
}

/* --- Page and theme transitions -------------------------------------------
   Going from one page to the next crossfades instead of flashing blank, and
   switching the theme crossfades the whole page as one (theme() in
   js/app.js). While the theme swaps, .theme-swap turns the elements' own
   colour transitions off, so none lags behind the fade. A page prerendered
   ahead of a click (the speculation rules in each <head>) holds its
   animations until it's shown (.is-prerendering, set in the <head>), so the
   brand mark still draws in when you arrive. No crossfades under reduced
   motion. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
}
::view-transition-old(root),
::view-transition-new(root) { animation-duration: 0.3s; }
.theme-swap *,
.theme-swap *::before,
.theme-swap *::after { transition: none !important; }
.is-prerendering *,
.is-prerendering *::before,
.is-prerendering *::after { animation-play-state: paused !important; }

img { max-width: 100%; display: block; }
button { font: inherit; color: inherit; }
:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--accent-color);
  outline-offset: 3px;
}

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

/* --- Scrollbars ----------------------------------------------------------
   Square, monochrome thumb on a transparent track. The transparent border +
   padding-box clip insets the thumb so it reads as a slim bar.
   Chromium ignores ::-webkit-scrollbar once scrollbar-color/-width is set,
   so the standard properties are scoped to browsers without the pseudo
   (Firefox). */
::-webkit-scrollbar { width: 12px; height: 12px; }
::-webkit-scrollbar-track,
::-webkit-scrollbar-corner { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--scrollbar-thumb);
  background-clip: padding-box;
  border: 3px solid transparent;
  border-radius: 0;
}
::-webkit-scrollbar-thumb:hover,
::-webkit-scrollbar-thumb:active {
  background: var(--scrollbar-thumb-hover);
  background-clip: padding-box;
}
::-webkit-scrollbar-button { display: none; }

@supports not selector(::-webkit-scrollbar) {
  html { scrollbar-color: var(--scrollbar-thumb) transparent; }
}

/* --- Skip link ----------------------------------------------------------- */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 200;
  background: var(--accent-color);
  color: var(--bg-primary);
  padding: 0.75rem 1.25rem;
  font-weight: 600;
  text-decoration: none;
}
.skip-link:focus { left: 1rem; top: 1rem; }

/* ==========================================================================
   Navigation
   ========================================================================== */
.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  background: var(--nav-bg);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color 0.3s ease;
}
.nav.scrolled { border-bottom-color: var(--grid-line); }

.nav-inner {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0.75rem var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

.nav-brand {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  color: var(--text-primary);
  text-decoration: none;
  font-family: "Space Grotesk", "Space Grotesk Fallback", sans-serif;
  font-weight: 700;
  letter-spacing: -0.02em;
  white-space: nowrap;
}

/* The mark beside the name: the Geek Updates logo redrawn as line art on a
   24-unit grid, in the text colour so it follows the theme. It's hinted to
   stay sharp at its small size: one unit is one pixel, and the viewBox starts
   half a unit out (with 2.5 units of room for the mitred tips at the apex and
   the feet), so every straight line falls on a pixel's centre. The straight
   lines (box, crossbar, centre line) draw with anti-aliasing off and --hv a
   whole number of screen pixels wide, so they come out solid, never a grey
   smear across two pixels. The A and its diagonals keep anti-aliasing (the
   staircase without it looks pixelated), a little heavier to match. On
   high-density screens anti-aliasing is sharp anyway, so it's on for all.
   On the home page it draws itself in, stroke by stroke; hovering the name
   replays it on any page (brandMark() in js/app.js). --d / --t are each
   stroke's delay and duration, in seconds; the script reads them too. */
.brand-mark {
  --hv: 1px;
  width: 29px;
  height: 29px;
  flex-shrink: 0;
  stroke-miterlimit: 10;
}
/* Windows display scaling: 125% (1 screen pixel), 150% and 175% (2) */
@media (min-resolution: 1.2dppx) and (max-resolution: 1.39dppx) { .brand-mark { --hv: 0.8px; } }
@media (min-resolution: 1.4dppx) and (max-resolution: 1.79dppx) { .brand-mark { --hv: 1.3333px; } }
.brand-mark path { stroke-width: var(--hv); shape-rendering: crispEdges; }
.brand-mark path:first-child { stroke-width: calc(var(--hv) * 1.35); shape-rendering: geometricPrecision; }
@media (min-resolution: 1.8dppx) { .brand-mark path { shape-rendering: geometricPrecision; } }
.brand-mark path { --ease: cubic-bezier(0.65, 0, 0.35, 1); --d: 0s; --t: 0.9s; }
.brand-mark path:nth-child(2) { --d: 0.35s; --t: 0.6s; }
.brand-mark path:nth-child(3) { --d: 0.75s; --t: 0.3s; }
.brand-mark path:nth-child(4) { --d: 0.85s; --t: 0.3s; }
.nav-brand[aria-current="page"] .brand-mark path {
  animation: brand-draw var(--t) var(--ease) var(--d) backwards;
}
@keyframes brand-draw {
  from { stroke-dasharray: 1 1.2; stroke-dashoffset: 1.1; }
  to { stroke-dasharray: 1 1.2; stroke-dashoffset: 0; }
}

.nav-actions,
.nav-links { display: flex; align-items: center; gap: 0.25rem; }

.nav-link {
  padding: 0.5rem 0.7rem;
  color: var(--text-tertiary);
  text-decoration: none;
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  transition: color 0.2s ease;
}
.nav-link:hover { color: var(--text-primary); }
/* The page you're on */
.nav-link[aria-current="page"] {
  color: var(--text-primary);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.5em;
}

.icon-btn {
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-secondary);
  border: 1px solid var(--grid-line);
  color: var(--text-primary);
  cursor: pointer;
  transition: border-color 0.2s ease, background-color 0.2s ease, opacity 0.2s ease;
}
.icon-btn:hover { border-color: var(--accent-color); }
.icon-btn svg { width: 18px; height: 18px; }
.theme-toggle { margin-left: 0.5rem; }

.theme-toggle .icon-moon { display: none; }
[data-theme="light"] .theme-toggle .icon-moon { display: block; }
[data-theme="light"] .theme-toggle .icon-sun { display: none; }

.nav-menu { display: none; margin-left: 0.25rem; }
.nav-menu .icon-close,
.nav.is-open .nav-menu .icon-menu { display: none; }
.nav.is-open .nav-menu .icon-close { display: block; }

/* Narrow screens: the page links drop down under the bar from the menu button.
   Above 680px all five links fit beside the name and the theme toggle */
@media (max-width: 680px) {
  .nav-menu { display: inline-flex; }
  .nav.is-open { border-bottom-color: var(--grid-line); }
  .nav-links {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 0 var(--gutter) 1rem;
    background: var(--bg-primary);
    border-bottom: 1px solid var(--grid-line);
  }
  .nav.is-open .nav-links { display: flex; }
  .nav-link {
    padding: 1rem 0;
    font-size: 0.85rem;
    border-top: 1px solid var(--grid-line);
  }
}

/* ==========================================================================
   Layout primitives
   ========================================================================== */
.container {
  max-width: var(--maxw);
  margin: 0 auto;
  padding-inline: var(--gutter);
  position: relative;
  z-index: 2;
}

.section { padding-block: var(--section-space); }

/* The opening block of work, creator and talks: clear of the fixed nav, level
   with the hero's greeting on the home page */
.page-first { padding-top: 9.5rem; }

.eyebrow {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-tertiary);
  margin-bottom: 1rem;
}

.section-title {
  font-family: "Space Grotesk", "Space Grotesk Fallback", sans-serif;
  font-size: clamp(1.8rem, 4.5vw, 2.85rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  text-transform: uppercase;
  line-height: 1.1;
}

/* A readable measure; pretty wrapping keeps a lone word off the last line */
.section-sub {
  max-width: 40em;
  color: var(--text-tertiary);
  margin-top: 0.85rem;
  text-wrap: pretty;
}
.section-sub strong { color: var(--text-primary); font-weight: 600; }

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.75rem 1.2rem;
  font-family: "Space Grotesk", "Space Grotesk Fallback", sans-serif;
  font-size: 0.8rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  text-decoration: none;
  cursor: pointer;
  border: 1px solid var(--grid-line);
  background: var(--bg-secondary);
  color: var(--text-primary);
  transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}
.btn svg { width: 16px; height: 16px; flex-shrink: 0; }
.btn:hover {
  background: var(--accent-color);
  color: var(--bg-primary);
  border-color: var(--accent-color);
}
.btn-primary {
  background: var(--accent-color);
  color: var(--bg-primary);
  border-color: var(--accent-color);
}
.btn-primary:hover { opacity: 0.85; }

/* ==========================================================================
   Hero
   ========================================================================== */
.hero { padding-top: 9.5rem; padding-bottom: 1rem; }

.hero-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2rem;
}

/* Phones: the words come first so the name is on the first screen, and the
   photo follows. Wide screens: side by side, photo on the right. */
.hero-photo {
  width: 100%;
  max-width: 360px;
  height: auto;
  aspect-ratio: 23 / 32;
  object-fit: cover;
  border: 1px solid var(--grid-line);
  background: var(--bg-secondary);
}

@media (min-width: 960px) {
  .hero-grid { grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 4rem; }
  .hero-photo { width: clamp(300px, 28vw, 380px); max-width: none; }
}

/* The greeting is a quiet lead-in to the name, in the same voice as "I'm" */
.hero-hello {
  font-family: "Space Grotesk", "Space Grotesk Fallback", sans-serif;
  font-size: clamp(1.15rem, 2.6vw, 1.5rem);
  font-weight: 500;
  letter-spacing: -0.02em;
  line-height: 1.25;
  color: var(--text-tertiary);
}

.hero-name {
  font-family: "Space Grotesk", "Space Grotesk Fallback", sans-serif;
  font-size: clamp(2.4rem, 8vw, 4.5rem);
  font-weight: 700;
  letter-spacing: -0.045em;
  text-transform: uppercase;
  line-height: 0.95;
  margin-top: 1.25rem;
}

/* "I'm" leads the name on the same line, at the size of the hello and on the
   name's baseline. It's kept with "Arshit" so it never sits alone on a line
   when the name wraps. 500 is the lightest Space Grotesk weight loaded. */
.hero-name-lead { white-space: nowrap; }
.hero-im {
  font-size: clamp(1.15rem, 2.6vw, 1.5rem);
  font-weight: 500;
  letter-spacing: -0.02em;
  text-transform: none;
  color: var(--text-tertiary);
}

/* Binary flip (binaryName() in js/app.js): now and then a letter crossfades
   to a 0 or 1 and back. The letter only fades out and the digit is drawn over
   it from a separate layer, so nothing in the line changes: no reflow, and
   the kerning between letters (YA, VA) stays intact. Fade times match FADE. */
.hero-name { position: relative; }
.hero-ch { transition: opacity 0.12s ease; }
.hero-ch[data-bit] { opacity: 0; }
.hero-bits { position: absolute; top: 0; left: 0; }
.hero-bit {
  position: absolute;
  transform: translateX(-50%);
  color: var(--text-tertiary);
  opacity: 0;
  transition: opacity 0.12s ease;
  pointer-events: none;
  user-select: none;
}
.hero-bit.is-on { opacity: 1; }

/* Plain copy of the name for screen readers; the split letters are aria-hidden */
.hero-name-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  user-select: none; /* copying the name copies the visible letters only */
}

.hero-lede {
  font-size: clamp(1.05rem, 2.2vw, 1.25rem);
  color: var(--text-secondary);
  margin-top: 1.75rem;
  max-width: 32em;
}

.hero-links { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 2.5rem; }
.btn-count { opacity: 0.7; margin-left: -0.25rem; }
/* Phones: Instagram across the top, the other four as an even two by two */
@media (max-width: 520px) {
  .hero-links { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .hero-links .btn:first-child { grid-column: 1 / -1; }
}

/* ==========================================================================
   Product strip — a full-width band under the hero where shipped products
   scroll past. The track holds the list twice and slides by half its width,
   so the loop is seamless; items are spaced with padding, not gap, to keep
   the two halves identical. Icons are grey until hovered.
   ========================================================================== */
.strip {
  margin-top: clamp(4rem, 7vw, 5.5rem);
  padding-block: 2.25rem 2rem;
  border-block: 1px solid var(--grid-line);
  text-align: center;
}
/* One line at every width: under 600px it drops "and shipped", and on phones
   the tracking tightens and the text shrinks a touch instead of wrapping */
.strip-title {
  padding-inline: 1rem;
  margin-bottom: 1.25rem;
  white-space: nowrap;
}
@media (max-width: 600px) {
  .strip-title-more { display: none; }
}
@media (max-width: 480px) {
  .strip-title { font-size: min(0.7rem, 2.6vw); letter-spacing: 0.1em; }
}

.strip-viewport {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}

.strip-track {
  display: flex;
  width: max-content;
  list-style: none;
  animation: strip var(--strip-time, 45s) linear infinite;
}
/* Pause for keyboard focus only: a clicked link keeps focus after opening its
   new tab, and :focus-within would leave the strip frozen until a click elsewhere */
.strip-viewport:hover .strip-track { animation-play-state: paused; }
.strip-viewport:has(.strip-item:focus-visible) .strip-track { animation-play-state: paused; }
@keyframes strip { to { transform: translateX(-50%); } }

.strip-item {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  padding: 0.6rem 1.6rem;
  font-family: "Space Grotesk", "Space Grotesk Fallback", sans-serif;
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  white-space: nowrap;
  color: var(--text-tertiary);
  text-decoration: none;
  transition: color 0.2s ease;
}
.strip-item img {
  width: 28px;
  height: 28px;
  object-fit: contain;
  border-radius: 22.5%;
  filter: grayscale(1);
  opacity: 0.7;
  transition: filter 0.25s ease, opacity 0.25s ease;
}
.strip-item:hover,
.strip-item:focus-visible { color: var(--text-primary); }
.strip-item:hover img,
.strip-item:focus-visible img { filter: none; opacity: 1; }
.strip-item:focus-visible { outline-offset: -2px; }

@media (max-width: 640px) {
  .strip { margin-top: 3.5rem; }
  .strip-item { padding-inline: 1.1rem; font-size: 0.95rem; }
  .strip-item img { width: 24px; height: 24px; }
}

/* ==========================================================================
   Showcases —"Every scale" and "Keep it simple": each a screen of its own,
   with the title above, one large line drawing in the centre and the
   description below
   ========================================================================== */
.showcase {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  min-height: 100svh;
  /* The top padding also clears the fixed nav (4rem), so the content centres
     in the screen below it and still fits one screen on a short laptop */
  padding-block: calc(4rem + clamp(1.25rem, 3vh, 3rem)) clamp(1.25rem, 3vh, 3rem);
  text-align: center;
}

/* Clear room between the drawing (and its labels) and the words under it;
   in vh, so a short screen still fits the title, drawing and text */
.showcase-text {
  max-width: 40em;
  margin-top: clamp(3rem, 7vh, 4.5rem);
  font-size: clamp(1.05rem, 2.2vw, 1.25rem);
  color: var(--text-secondary);
}
.showcase-note {
  margin-top: 0.85rem;
  font-size: 0.95rem;
  color: var(--text-tertiary);
}

/* The principle is the description of "Keep it simple": centred under the
   drawing, a size up from body text, balanced over two lines on desktop */
.principle {
  max-width: 46em;
  font-family: "Space Grotesk", "Space Grotesk Fallback", sans-serif;
  font-size: clamp(1.1rem, 1.9vw, 1.35rem);
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.45;
  color: var(--text-primary);
  text-wrap: balance;
}

/* "Every scale" line drawing: one person, a team, a city, nationwide on one
   ground line. People, buildings and pins in the text colour; the ground
   (which connects the stages), the city's back row and the map field in the
   muted tone. It's capped at 45rem wide so it reads at a glance, and
   smaller still on a short screen so the title, the drawing and its labels
   fit under the nav (on a short laptop screen the text runs just below):
   2.55 is the drawing's aspect ratio (960 × 376) and 19rem roughly the nav,
   the title and the gaps. It's two svgs at one
   scale, the people and the places, side by side: each half's flex share is
   its viewBox width. On phones they stack, further down. Its strokes are
   sized from its width further down too. */
.work-scale {
  container: work-scale / inline-size;
  display: flex;
  align-items: flex-end;
  width: 100%;
  max-width: min(45rem, max(18rem, (100vh - 19rem) * 2.55));
  max-width: min(45rem, max(18rem, (100svh - 19rem) * 2.55));
  margin-top: clamp(1.25rem, 3.5vh, 2.5rem);
}
.work-scale-part { flex: 424 1 0; min-width: 0; }
.work-scale-part + .work-scale-part { flex: 536 1 0; }
.work-scale svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
  color: var(--text-primary);
}
.work-scale .m { stroke: var(--text-tertiary); --ease: cubic-bezier(0.65, 0, 0.35, 1); }

/* Labels sit under each stage's centre (--x, a share of the drawing's width) */
.work-scale-labels,
.work-simple-labels { position: relative; height: 0.65rem; margin-top: 0.9rem; }
.work-scale-labels span,
.work-simple-labels span {
  position: absolute;
  top: 0;
  left: var(--x);
  transform: translateX(-50%);
  padding-left: 0.14em; /* balances the trailing letter-spacing */
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  line-height: 1;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--text-tertiary);
}

/* Complex to simple: a cluttered control panel simplifies into one button
   while the user's face cheers up. The device and the face are two svgs at
   the same scale (their viewBoxes are 600 and 300 wide, so the columns are
   2fr 1fr), side by side, stacked on phones. The drawing takes the height
   left on screen under the nav and the title (19rem, as for "Every scale";
   on a short laptop screen the text runs just below), up to 22rem tall
   (about 48rem wide, near "Every scale"). Shapes gone by the last stage
   (data-v ending in 0) stay hidden, so the finished drawing is what shows
   without JS. simpleSketch() in js/app.js sets every dash and opacity
   inline, so no class-dependent [pathLength] rules are involved (see the
   Chrome note under the draw-in below). */
.work-simple {
  --ws-gap: clamp(1rem, 3vw, 3rem);
  --ws-h: clamp(13rem, 100vh - 19rem, 22rem);
  display: grid;
  grid-template-columns: 2fr 1fr;
  column-gap: var(--ws-gap);
  width: min(100%, var(--ws-h) * 900 / 440 + var(--ws-gap));
  margin-top: 1.5rem;
}
@supports (height: 100svh) {
  .work-simple { --ws-h: clamp(13rem, 100svh - 19rem, 22rem); }
}
.work-simple-device,
.work-simple-user { min-width: 0; container-type: inline-size; }
.work-simple svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
  color: var(--text-primary);
}
/* Thin at any size: a stroke's width in user units is its on-screen width
   times the viewBox width over the rendered width (100cqw; tan(atan2()) is
   that ratio as a number), so lines stay 1.75px and the muted ones 1.4px.
   The plain numbers are the fallback. Higher specificity than the shared
   small-screen stroke rules below, which this drawing doesn't use. */
.work-simple .work-simple-device svg { stroke-width: 1.5; stroke-width: calc(tan(atan2(1050px, 100cqw)) * 1px); }
.work-simple .work-simple-device .m { stroke-width: 1.2; stroke-width: calc(tan(atan2(840px, 100cqw)) * 1px); }
.work-simple .work-simple-user svg { stroke-width: 1.5; stroke-width: calc(tan(atan2(525px, 100cqw)) * 1px); }
.work-simple .work-simple-user .m { stroke-width: 1.2; stroke-width: calc(tan(atan2(420px, 100cqw)) * 1px); }
.work-simple .m { stroke: var(--text-tertiary); }
.work-simple .dot { fill: currentColor; stroke: none; }
.work-simple .dot.m { fill: var(--text-tertiary); }
.work-simple [data-v$="0"] { opacity: 0; }
.work-simple.is-armed { cursor: pointer; } /* hover or click replays it */

/* Its labels: the eyebrow style, a touch larger. Both svgs keep
   a strip at the bottom for the cables; the labels sit up in it, 24 units
   under the art (cqw: each column's own width). */
.work-simple .work-simple-labels {
  height: 1em;
  margin-top: 1rem;
  font-size: clamp(0.66rem, 0.4rem + 0.45vw, 0.72rem);
}
.work-simple .work-simple-labels span { font-size: inherit; letter-spacing: 0.2em; padding-left: 0.2em; }
.work-simple .work-simple-device .work-simple-labels { margin-top: -4cqw; }
.work-simple .work-simple-user .work-simple-labels { margin-top: -8cqw; }

/* Phones: the face goes under the device, tucked into the empty edges of its
   svg, at the device's scale (half its width, as side by side), and the lines
   are a touch finer (1.5px, muted 1.2px). On a short screen the pair narrows
   so the device and the face both show under the title while they play out
   together: 13rem is about the nav, the title and the labels, and 1.42 the
   pair's height over its width. */
@media (max-width: 600px) {
  .work-simple {
    grid-template-columns: minmax(0, 1fr);
    width: min(100%, (100vh - 13rem) / 1.42);
    width: min(100%, (100svh - 13rem) / 1.42);
  }
  .work-simple-user { justify-self: center; width: 50%; }
  .work-simple-user svg { margin-top: -10cqw; }
  .work-simple .work-simple-user .work-simple-labels { margin-top: -20cqw; }
  .work-simple .work-simple-device svg { stroke-width: calc(tan(atan2(900px, 100cqw)) * 1px); }
  .work-simple .work-simple-device .m { stroke-width: calc(tan(atan2(720px, 100cqw)) * 1px); }
  .work-simple .work-simple-user svg { stroke-width: calc(tan(atan2(450px, 100cqw)) * 1px); }
  .work-simple .work-simple-user .m { stroke-width: calc(tan(atan2(360px, 100cqw)) * 1px); }
}


/* The drawings scale with their width; thicker strokes keep them near 1.5px */
@media (max-width: 420px) {
  .work-scale svg, .work-simple svg { stroke-width: 1.9; }
  .work-scale .m, .work-simple .m { stroke-width: 1.55; }
}
@media (max-width: 360px) {
  .work-scale svg, .work-simple svg { stroke-width: 2.2; }
  .work-scale .m, .work-simple .m { stroke-width: 1.8; }
  .work-scale-labels span,
  .work-simple-labels span { font-size: 0.6rem; letter-spacing: 0.08em; padding-left: 0.08em; }
}

/* "Every scale" sizing. It comes after the shared rules above, which it
   overrides. The strokes scale with the svg, so they're set in user units:
   --k is user units per screen pixel (the viewBox's 960 / the drawing's
   width), stepped by the drawing's own width, which holds them at about
   1.75px on screen (the muted ones 1.4px) at any size. The mask outlines
   are the gaps around the front shapes. The labels grow with the drawing. */
.work-scale svg { --k: 0.91; stroke-width: calc(1.75px * var(--k)); }
.work-scale .m { stroke-width: calc(1.4px * var(--k)); }
.work-scale mask g { stroke-width: calc(6px * var(--k)); }
@container work-scale (max-width: 960px) { .work-scale svg { --k: 1.1; } }
@container work-scale (max-width: 800px) { .work-scale svg { --k: 1.31; } }
@container work-scale (max-width: 667px) { .work-scale svg { --k: 1.58; } }
@container work-scale (max-width: 556px) { .work-scale svg { --k: 1.89; } }
@container work-scale (max-width: 463px) { .work-scale svg { --k: 2.27; } }
@container work-scale (max-width: 386px) { .work-scale svg { --k: 2.72; } }
@container work-scale (max-width: 322px) { .work-scale svg { --k: 3.27; } }
.work-scale-labels {
  height: 1em;
  margin-top: clamp(0.75rem, 1.6cqi, 1.25rem);
  font-size: clamp(0.65rem, 1.3cqi, 0.9rem);
}
.work-scale-labels span { font-size: inherit; letter-spacing: 0.16em; padding-left: 0.16em; }
@container work-scale (max-width: 420px) {
  .work-scale-labels span { letter-spacing: 0.08em; padding-left: 0.08em; }
}

/* Phones: the people above the places, both at one scale, so each stage is
   about twice the size it would be on one line. The drawing is now 536 units
   wide, so the strokes come from that: 938px is 1.75px × 536 (muted 1.4px,
   the mask gaps 6px), over the drawing's width (100cqw; tan(atan2()) makes
   the ratio a number). The plain numbers are the fallback. */
@media (max-width: 600px) {
  .work-scale { flex-direction: column; align-items: center; row-gap: 1.75rem; }
  .work-scale-part,
  .work-scale-part + .work-scale-part { flex: none; width: 100%; }
  .work-scale-part:first-child { width: calc(100% * 424 / 536); }
  .work-scale svg { stroke-width: 2.6; stroke-width: calc(tan(atan2(938px, 100cqw)) * 1px); }
  .work-scale .m { stroke-width: 2.1; stroke-width: calc(tan(atan2(750px, 100cqw)) * 1px); }
  .work-scale mask g { stroke-width: 9; stroke-width: calc(tan(atan2(3216px, 100cqw)) * 1px); }
  .work-scale-labels { font-size: 0.65rem; }
  .work-scale-labels span { letter-spacing: 0.16em; padding-left: 0.16em; }
}

/* Draw-in. pathLength="1" on the shapes: a dash of 1 is the whole line, and
   the gap and start offset run past the end so no round cap shows before the
   draw. scaleSketch() in js/app.js adds .is-armed (hidden) only when motion is allowed, then
   .is-drawing once the drawing scrolls into view. Without JS it shows whole.
   The strokes are the shapes in the svg and its groups; the mask shapes in
   <defs> stay solid. (Not [pathLength]: Chrome doesn't restyle elements
   matched by that camelCase attribute when an ancestor's class changes.) */
.work-scale.is-armed svg > :not(defs, g),
.work-scale.is-armed svg > g > * { stroke-dasharray: 1 1.2; stroke-dashoffset: 1.1; }
.work-scale.is-armed .work-scale-labels span { opacity: 0; }
.work-scale.is-drawing svg > :not(defs, g),
.work-scale.is-drawing svg > g > * {
  animation: work-scale-draw var(--t, 0.45s) var(--ease, var(--ease-out)) var(--d, 0s) both;
}
.work-scale.is-drawing .work-scale-labels span { animation: work-scale-fade 0.6s ease var(--d, 0s) both; }
@keyframes work-scale-draw {
  from { stroke-dashoffset: 1.1; }
  to { stroke-dashoffset: 0; }
}
@keyframes work-scale-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* ==========================================================================
   Project grid
   ========================================================================== */
.work-title,
.work-sub { margin-bottom: var(--head-gap); }

/* Every project, on the projects page and the few on the home page, as a
   compact card: the app's icon, then its name over what it's for. Three
   across, two on tablets, one on phones. On the home page the way to all of
   them follows. */
.picks {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1rem, 1.6vw, 1.25rem);
}
.pick {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas: "icon name" "icon use";
  grid-template-rows: auto 1fr;
  column-gap: 1.15rem;
  row-gap: 0.25rem;
  padding: 1.5rem;
  border: 1px solid var(--grid-line);
  background: var(--bg-secondary);
  text-align: left;
  cursor: pointer;
  transition: border-color 0.2s ease;
}
/* Mid-glide between two filters' heights (app.js); cards past the edge of a
   growing grid are clipped until it reaches them. An in-out curve, as the
   sharp --ease-out covers half the distance in the first few frames and
   reads as a jump. */
.picks.is-gliding {
  overflow: hidden;
  transition: height 0.45s cubic-bezier(0.4, 0, 0.2, 1);
}
.pick[hidden] { display: none; } /* filtered out */
.pick:hover,
.pick:focus-visible { border-color: var(--highlight-border); }
/* App icons get the launcher's rounded corners, so the ones drawn as plain
   squares (Brizeet, NotiTodo, …) match the ones that come rounded */
.pick-icon { grid-area: icon; width: 48px; height: 48px; object-fit: contain; border-radius: 22.5%; }
/* No icon: the project's initial in a hairline square */
.pick-initial {
  display: grid;
  place-items: center;
  border: 1px solid var(--highlight-border);
  font-family: "Space Grotesk", "Space Grotesk Fallback", sans-serif;
  font-size: 1.4rem;
  font-weight: 700;
  color: var(--text-primary);
}
.pick-name {
  grid-area: name;
  font-family: "Space Grotesk", "Space Grotesk Fallback", sans-serif;
  font-size: 1.1rem;
  font-weight: 600;
  line-height: 1.3;
  color: var(--text-primary);
}
.pick-use { grid-area: use; font-size: 0.92rem; line-height: 1.5; color: var(--text-tertiary); }
@media (max-width: 900px) {
  .picks { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .picks { grid-template-columns: minmax(0, 1fr); }
}

.featured-more { margin-top: var(--head-gap); }

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-bottom: 2rem;
}

.filter-btn {
  padding: 0.45rem 0.9rem;
  background: var(--bg-primary);
  border: 1px solid var(--grid-line);
  color: var(--text-tertiary);
  font-family: "Space Grotesk", "Space Grotesk Fallback", sans-serif;
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease, background-color 0.2s ease;
}
.filter-btn:hover { color: var(--text-primary); border-color: var(--highlight-border); }
.filter-btn[aria-pressed="true"] {
  background: var(--accent-color);
  color: var(--bg-primary);
  border-color: var(--accent-color);
}

/* Open source · Free · Work in progress …, in the detail dialog, and the
   status on each card */
.pd-tags span,
.pick-tag {
  font-family: "Space Grotesk", "Space Grotesk Fallback", sans-serif;
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1.4;
  padding: 0.2rem 0.45rem;
  color: var(--text-primary);
}
/* Ready to use · Publishing in progress · Work in progress, tucked into the
   card's top-right corner: the card's own edges are its top and right, and
   it's gray so it doesn't pull focus from the name */
.pick-tag {
  position: absolute;
  top: 0;
  right: 0;
  white-space: nowrap;
  border-left: 1px solid;
  border-bottom: 1px solid;
  border-color: inherit;
  color: var(--text-tertiary);
  transition: border-color 0.2s ease;
}

/* ==========================================================================
   Geek Updates
   ========================================================================== */
/* What I post and why, under the audience size */
.creator-bio {
  max-width: 38em;
  margin-top: 1.25rem;
  line-height: 1.65;
  color: var(--text-secondary);
}

/* The intro and the follower counts, with my photo: below them on phones,
   on the right once there's room, like the home page's hero. The brands run
   the full width underneath */
.creator-head {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2.5rem;
  margin-bottom: clamp(3.5rem, 7vw, 5rem);
}
.creator-head .platforms { margin-bottom: 0; }
.creator-photo {
  display: block;
  width: 100%;
  max-width: 260px;
  height: auto;
  border: 1px solid var(--grid-line);
  background: var(--bg-secondary);
}
@media (min-width: 720px) {
  .creator-head { grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: clamp(3rem, 6vw, 5rem); }
  .creator-photo { width: clamp(200px, 22vw, 280px); max-width: none; }
}

.platforms {
  display: flex;
  flex-wrap: wrap;
  gap: 0.65rem;
  margin: 2rem 0 2.5rem;
}
.platform {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.7rem 1.05rem;
  border: 1px solid var(--grid-line);
  background: var(--bg-secondary);
  color: var(--text-primary);
  text-decoration: none;
  font-size: 0.85rem;
  transition: border-color 0.2s ease;
}
.platform:hover { border-color: var(--accent-color); }
.platform svg { width: 16px; height: 16px; }
.platform b { font-family: "Space Grotesk", "Space Grotesk Fallback", sans-serif; font-weight: 600; }
.platform span { color: var(--text-tertiary); }

/* Each brand: its logo with its name beside it, linking to the reel. Three
   columns, two below 760px where three would wrap the names, and on phones a
   smaller logo so the longest name still fits. */
.brands {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.75rem;
}
@media (max-width: 760px) {
  .brands { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.brand {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  min-width: 0;
  padding: 0.9rem 1.2rem;
  border: 1px solid var(--grid-line);
  background: var(--bg-secondary);
  color: var(--text-primary);
  text-decoration: none;
  transition: border-color 0.2s ease;
}
.brand:hover { border-color: var(--accent-color); }
.brand-name {
  min-width: 0;
  font-family: "Space Grotesk", "Space Grotesk Fallback", sans-serif;
  font-weight: 600;
  overflow-wrap: break-word;
  hyphens: auto; /* only the smallest phones need it, for Wondershare */
}
@media (max-width: 520px) {
  .brand { gap: 0.6rem; padding: 0.6rem 0.7rem; }
  .brand .brand-logo { width: 36px; height: 36px; }
  .brand-name { font-size: 0.9rem; }
}
@media (max-width: 380px) {
  .brand { padding: 0.55rem 0.6rem; }
  .brand .brand-logo { width: 30px; height: 30px; }
  .brand-name { font-size: 0.85rem; }
}
.brand-logo {
  flex-shrink: 0;
  width: 48px;
  height: 48px;
  display: grid;
  place-items: center;
  overflow: hidden;
}
.brand-logo img { width: 100%; height: 100%; object-fit: contain; padding: 4px; }
.brand-logo.needs-white-bg { background: #ffffff; border-radius: 50%; }
.brand-logo.logo-padded img { padding: 9px; }
[data-theme="light"] .brand-logo.needs-dark-bg { background: #1a1a1a; border-radius: 50%; }

/* "And many more", after the last brand: the words centered and quieter, no
   hover as it goes nowhere. Its empty logo box has no width but keeps the
   logo's height at every size, so the tile is as tall as the others even
   alone on a row */
.brand-more { justify-content: center; gap: 0; color: var(--text-tertiary); }
.brand-more:hover { border-color: var(--grid-line); }
.brand-more .brand-logo { width: 0; }

/* ==========================================================================
   Personal endeavors — creative side projects as image cards that link out.
   Four columns, two on smaller screens; the tall card (the phone wallpaper)
   spans two rows, so the seven cards fill the grid exactly at either width.
   Images are absolutely placed: the media box gets its size from its aspect
   ratio, or for the tall card from the space the two rows leave.
   ========================================================================== */
.endeavors {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  grid-auto-flow: dense;
  gap: clamp(2.5rem, 4.5vw, 3.25rem) clamp(1.25rem, 2.5vw, 2rem);
  margin-top: var(--head-gap);
}

.endeavor {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  min-width: 0;
  color: inherit;
  text-decoration: none;
}
.endeavor.is-tall { grid-row: span 2; }

.endeavor-media {
  position: relative;
  aspect-ratio: 1;
  margin-bottom: 0.85rem;
  overflow: hidden;
  background: var(--skeleton);
  border: 1px solid var(--grid-line);
  transition: border-color 0.25s ease;
}
.endeavor.is-tall .endeavor-media { flex: 1; aspect-ratio: auto; min-height: 12rem; }
.endeavor-media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.6s var(--ease-out), opacity 0.4s ease;
}
.endeavor-alt { opacity: 0; }
.endeavor:hover .endeavor-media,
.endeavor:focus-visible .endeavor-media { border-color: var(--highlight-border); }
.endeavor:hover .endeavor-media img { transform: scale(1.035); }
.endeavor:hover .endeavor-alt,
.endeavor:focus-visible .endeavor-alt { opacity: 1; }
.endeavor:focus-visible { outline: none; }
.endeavor:focus-visible .endeavor-media { outline: 2px solid var(--accent-color); outline-offset: 3px; }

.endeavor-kind {
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}
.endeavor-name {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-family: "Space Grotesk", "Space Grotesk Fallback", sans-serif;
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text-primary);
}
.endeavor-name svg {
  width: 13px;
  height: 13px;
  flex-shrink: 0;
  color: var(--text-tertiary);
  transition: color 0.2s ease, transform 0.2s ease;
}
.endeavor:hover .endeavor-name svg { color: var(--text-primary); transform: translate(1px, -1px); }
.endeavor-use { font-size: 0.88rem; line-height: 1.5; color: var(--text-tertiary); }

@media (max-width: 900px) {
  .endeavors { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2.25rem 1.25rem; }
}

/* ==========================================================================
   Talks — seminars & workshops as photo cards, like the career ones: the
   first photo, the kind of session, the name and what it covered. On six
   columns the first two run large side by side and the other three share the
   row under them. A card opens the detail dialog with that session's photos.
   ========================================================================== */
.talk-grid {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: clamp(2.75rem, 5vw, 3.75rem) clamp(1.25rem, 2.5vw, 2rem);
  margin-top: var(--head-gap);
}
.talk-grid > li { display: flex; grid-column: span 2; min-width: 0; }
.talk-grid > li:nth-child(-n + 2) { grid-column: span 3; }

.talk {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  width: 100%;
  min-width: 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.talk-media {
  position: relative;
  aspect-ratio: 16 / 10;
  margin-bottom: 0.85rem;
  overflow: hidden;
  background: var(--skeleton);
  border: 1px solid var(--grid-line);
  transition: border-color 0.25s ease;
}
.talk-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.6s var(--ease-out);
}
.talk-count,
.career-count {
  position: absolute;
  right: 0.4rem;
  bottom: 0.4rem;
  padding: 0.15rem 0.45rem;
  font-family: "Space Grotesk", "Space Grotesk Fallback", sans-serif;
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-primary);
  background: var(--nav-bg);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid var(--grid-line);
}
.talk:hover .talk-media,
.talk:focus-visible .talk-media { border-color: var(--highlight-border); }
.talk:hover .talk-media img { transform: scale(1.035); }
.talk:focus-visible { outline: none; }
.talk:focus-visible .talk-media { outline: 2px solid var(--accent-color); outline-offset: 3px; }

.talk-kind {
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}
.talk-name {
  font-family: "Space Grotesk", "Space Grotesk Fallback", sans-serif;
  font-size: 1.2rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.3;
  color: var(--text-primary);
}
.talk-use { font-size: 0.92rem; line-height: 1.5; color: var(--text-tertiary); }
.talk-grid > li:nth-child(-n + 2) .talk-name { font-size: 1.45rem; }
.talk-grid > li:nth-child(-n + 2) .talk-use { font-size: 1rem; }

/* Phones: one card per row, all the same size */
@media (max-width: 640px) {
  .talk-grid { grid-template-columns: minmax(0, 1fr); gap: 2.5rem; }
  .talk-grid > li,
  .talk-grid > li:nth-child(-n + 2) { grid-column: auto; }
  .talk-grid > li:nth-child(-n + 2) .talk-name { font-size: 1.2rem; }
  .talk-grid > li:nth-child(-n + 2) .talk-use { font-size: 0.92rem; }
}

/* ==========================================================================
   Career projects — employer work on the home page: the projects as photo
   cards, two across (one on phones), each opening the detail dialog. A card
   with no photos (its details stay in-house) shows a lock on hairline
   hatching instead.
   ========================================================================== */
.career-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(2.5rem, 4.5vw, 3.25rem) clamp(1.25rem, 2.5vw, 2rem);
  margin-top: var(--head-gap);
}

.career-card {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  min-width: 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.career-media {
  position: relative;
  aspect-ratio: 16 / 10;
  margin-bottom: 0.85rem;
  overflow: hidden;
  background: var(--skeleton);
  border: 1px solid var(--grid-line);
  transition: border-color 0.25s ease;
}
.career-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.6s var(--ease-out);
}
.career-media.is-locked {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.85rem;
  color: var(--text-tertiary);
  background:
    repeating-linear-gradient(135deg, transparent 0 13px, var(--grid-line) 13px 14px),
    var(--bg-secondary);
}
.career-media.is-locked svg { width: 44px; height: 44px; }
.career-lock {
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.career-card:hover .career-media,
.career-card:focus-visible .career-media { border-color: var(--highlight-border); }
.career-card:hover .career-media img { transform: scale(1.035); }
.career-card:hover .career-media.is-locked { color: var(--text-secondary); }
.career-card:focus-visible { outline: none; }
.career-card:focus-visible .career-media { outline: 2px solid var(--accent-color); outline-offset: 3px; }

.career-kind {
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}
.career-name {
  font-family: "Space Grotesk", "Space Grotesk Fallback", sans-serif;
  font-size: 1.2rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.3;
  color: var(--text-primary);
}
.career-use { font-size: 0.92rem; line-height: 1.5; color: var(--text-tertiary); }

@media (max-width: 640px) {
  .career-grid { grid-template-columns: minmax(0, 1fr); gap: 2.5rem; }
}

/* ==========================================================================
   Both sides
   ========================================================================== */
/* The question and the cards: a section of their own (.pick-side), the last
   one before the email they lead to, so it sits close above it */
.pick-side { padding-bottom: 0; }
.sides-ask {
  font-family: "Space Grotesk", "Space Grotesk Fallback", sans-serif;
  font-size: clamp(1.3rem, 3vw, 1.9rem);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.25;
}

.sides-pick {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.75rem;
  max-width: 880px;
  margin-top: clamp(1.5rem, 3vw, 2rem);
}

/* Icon, name and line stacked in a card; side by side on phones */
.side {
  display: grid;
  grid-template-areas: "icon" "name" "sub";
  justify-items: start;
  row-gap: 0.25rem;
  padding: 1.5rem;
  text-align: left;
  background: var(--bg-secondary);
  border: 1px solid var(--grid-line);
  cursor: pointer;
  transition: border-color 0.2s ease, background-color 0.2s ease, color 0.2s ease;
}
.side svg {
  grid-area: icon;
  width: 22px;
  height: 22px;
  margin-bottom: 1.1rem;
  color: var(--text-tertiary);
  transition: color 0.2s ease;
}
.side-name {
  grid-area: name;
  font-family: "Space Grotesk", "Space Grotesk Fallback", sans-serif;
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.side-sub {
  grid-area: sub;
  font-size: 0.8rem;
  color: var(--text-tertiary);
  transition: color 0.2s ease;
}
.side:hover { border-color: var(--accent-color); }
.side:hover svg { color: var(--text-primary); }
.side[aria-pressed="true"] {
  background: var(--accent-color);
  color: var(--bg-primary);
  border-color: var(--accent-color);
}
.side[aria-pressed="true"] svg,
.side[aria-pressed="true"] .side-sub { color: inherit; }

@media (max-width: 640px) {
  .sides-pick { grid-template-columns: minmax(0, 1fr); }
  .side {
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas: "icon name" "icon sub";
    column-gap: 0.9rem;
    align-items: center;
  }
  .side svg { margin-bottom: 0; }
}

/* Creator, then developer: each a screen of its own, a paragraph beside a
   carousel of its five steps (the carousel on the right for the creator and
   on the left for the developer; on phones the text comes first), so only
   one is on screen, and playing, at a time. Each rises in the first time it
   comes on screen (sidesReels() in js/app.js adds .is-in). */
.sides-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  column-gap: clamp(3rem, 8vw, 8rem);
  align-items: center;
  min-height: 100vh;
  min-height: 100svh;
  padding-block: clamp(3rem, 8vh, 6rem);
}
/* The first shares its screen with the section's title */
.sides-row:first-child {
  min-height: calc(100vh - 7rem);
  min-height: calc(100svh - 7rem);
  padding-top: clamp(1.5rem, 4vh, 3rem);
}
.sides-row-dev .sides-reel { grid-column: 1; grid-row: 1; }
.sides-row-dev .sides-text { grid-column: 2; }
.sides-text { max-width: 30em; }
.sides-role {
  font-family: "Space Grotesk", "Space Grotesk Fallback", sans-serif;
  font-size: clamp(1.75rem, 3.4vw, 2.75rem);
  font-weight: 600;
  letter-spacing: -0.03em;
  line-height: 1.1;
  color: var(--text-primary);
}
.sides-text p {
  margin-top: clamp(1.25rem, 2.5vw, 1.75rem);
  font-size: clamp(1.05rem, 1.5vw, 1.25rem);
  line-height: 1.7;
  color: var(--text-secondary);
}
@media (max-width: 760px) {
  .sides-row { grid-template-columns: minmax(0, 1fr); row-gap: clamp(2.5rem, 7vh, 4rem); align-content: center; }
  .sides-row-dev .sides-reel,
  .sides-row-dev .sides-text { grid-column: auto; grid-row: auto; }
}
.sides-rows.is-armed .sides-row > * {
  opacity: 0;
  transform: translateY(1.75rem);
  transition: opacity 0.9s ease, transform 1.1s var(--ease-out);
}
.sides-rows.is-armed .sides-row > .sides-reel { transition-delay: 0.15s; }
.sides-rows.is-armed .sides-row.is-in > * { opacity: 1; transform: none; }

/* The carousel: each slide a 160-unit-wide line drawing over its step's
   name. Without JS (or under reduced motion) it's the five side by side,
   each a fifth of the width. sidesReels() turns it into a carousel
   (.is-carousel): one slide at a time, the drawing 72% of the width and the
   edges fading out as the slides pass. Strokes stay 1.75px (muted 1.4px;
   2.25px and 1.75px in the carousel, where the drawings are large): 1400px
   is 1.75px × 800 units (five drawings) over the carousel's width (100cqw;
   tan(atan2()) makes it a number), as for "Keep it simple". */
.sides-reel { min-width: 0; overflow: hidden; container-type: inline-size; }
.sides-reel-track { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); }
.sides-slide svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
  color: var(--text-primary);
  stroke-width: 1.5;
  stroke-width: calc(tan(atan2(1400px, 100cqw)) * 1px);
}
.sides-slide .m { stroke: var(--text-tertiary); stroke-width: 1.2; stroke-width: calc(tan(atan2(1120px, 100cqw)) * 1px); }
.sides-slide .dot { fill: currentColor; stroke: none; }
.sides-slide .dot.m { fill: var(--text-tertiary); }
.sides-slide .fill { fill: var(--bg-primary); } /* the pencil and the playhead cover what's under them */
.sides-slide .pulse { opacity: 0; stroke-dasharray: 0.06 1.2; }
.sides-name {
  margin-top: 0.9rem;
  padding-left: 0.14em; /* balances the trailing letter-spacing */
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  line-height: 1;
  text-align: center;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--text-tertiary);
}

.sides-reel.is-carousel {
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
}
.sides-reel.is-carousel .sides-reel-track { display: flex; }
.sides-reel.is-moving .sides-reel-track { transition: transform 0.9s cubic-bezier(0.65, 0, 0.35, 1); }
.sides-reel.is-carousel .sides-slide { flex: 0 0 100%; }
.sides-reel.is-carousel .sides-slide svg { width: 72cqw; margin-inline: auto; stroke-width: calc(tan(atan2(500px, 100cqw)) * 1px); }
.sides-reel.is-carousel .sides-slide .m { stroke-width: calc(tan(atan2(389px, 100cqw)) * 1px); }
.sides-reel.is-carousel .sides-slide .pulse { stroke-width: calc(tan(atan2(1000px, 100cqw)) * 1px); } /* 4.5px packets */
.sides-reel.is-carousel .sides-name { margin-top: clamp(1.75rem, 3vw, 2.5rem); font-size: 0.8rem; letter-spacing: 0.24em; padding-left: 0.24em; }
/* The timeline under the carousel, so it's clear more is coming: a ruler
   with a tick per step (finer ones between) and a dot for the step that's
   on. As the next slide comes in, the ruler moves one step along under the
   dot (.is-moving, with the slides); it repeats every step, so it jumps back
   unseen. A step is a quarter of the width, so two upcoming steps show to the
   right of the dot, and the carousel's faded edges fade it out too. */
.sides-timeline { display: none; }
.sides-reel.is-carousel .sides-timeline {
  --step: 25cqw;
  display: block;
  position: relative;
  height: 14px;
  margin-top: clamp(1.75rem, 3.5vw, 2.5rem);
}
.sides-reel.is-carousel .sides-timeline::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--highlight-border);
}
.sides-ticks {
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(50% - 3 * var(--step));
  width: calc(8 * var(--step));
  background:
    linear-gradient(90deg, var(--text-tertiary) 1px, transparent 1px) 0 50% / var(--step) 14px repeat-x,
    linear-gradient(90deg, var(--highlight-border) 1px, transparent 1px) 0 50% / calc(var(--step) / 5) 6px repeat-x;
}
.sides-reel.is-moving .sides-ticks {
  transform: translateX(calc(-1 * var(--step)));
  transition: transform 0.9s cubic-bezier(0.65, 0, 0.35, 1);
}
.sides-reel.is-carousel .sides-timeline::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 9px;
  height: 9px;
  margin: -4.5px 0 0 -4.5px;
  border-radius: 50%;
  background: var(--text-primary);
  box-shadow: 0 0 0 4px var(--bg-primary); /* clears the ticks around it */
}
/* A slide stays for its --hold: when this (invisible) timer ends, the next
   one comes in. Off screen or under the mouse, everything holds still. */
.sides-slide.is-drawing { animation: sides-hold var(--hold) linear both; }
@keyframes sides-hold {
  from, to { opacity: 1; }
}
.sides-reel.is-paused * { animation-play-state: paused !important; }

/* Draw-in, as for "Every scale": sidesReels() adds .is-armed (every drawing
   hidden) only when motion is allowed, then .is-drawing to the slide that's
   coming in, and takes it off (resetting it) once it's gone. Names, dots,
   hearts, rays and puffs fade in instead, and the pulses only show while they
   run. Each action's start is --a, inherited from its group. The base rules
   are the finished drawing, so that's what shows without JS. */
.sides-reel.is-armed svg :is(path, circle, rect, ellipse):not(.dot, .whole, .pulse) { stroke-dasharray: 1 1.2; stroke-dashoffset: 1.1; }
.sides-reel.is-armed :is(.sides-name, .dot, .sd-rays, .sd-heart, .sd-puff) { opacity: 0; }
.sides-slide.is-drawing svg :is(path, circle, rect, ellipse):not(.dot, .whole, .pulse) {
  animation: work-scale-draw var(--t, 0.4s) var(--ease, var(--ease-out)) var(--d, 0s) both;
}
.sides-slide.is-drawing :is(.sides-name, .dot) { animation: work-scale-fade 0.5s ease var(--d, 0s) both; }

/* brainstorm: the rays flicker on */
.sides-slide.is-drawing .sd-rays { animation: sides-flicker 0.9s linear var(--a) both; }
@keyframes sides-flicker {
  0% { opacity: 0; }
  12% { opacity: 1; }
  24% { opacity: 0.15; }
  36% { opacity: 1; }
  52% { opacity: 0.35; }
  64%, 100% { opacity: 1; }
}
/* engineer: a packet runs down each link, twice */
.sides-slide.is-drawing .pulse { animation: sides-pulse 0.7s ease-in-out var(--a) 2; }
@keyframes sides-pulse {
  0% { opacity: 0; stroke-dashoffset: 0.06; }
  10%, 90% { opacity: 1; }
  100% { opacity: 0; stroke-dashoffset: -1; }
}
/* shoot: the reels spin; the record light (and the caret, below) blinks on */
.sides-reel .sd-reel { transform-box: fill-box; transform-origin: center; }
.sides-slide.is-drawing .sd-reel { animation: sides-spin 1.6s cubic-bezier(0.45, 0, 0.25, 1) var(--a) both; }
@keyframes sides-spin { to { transform: rotate(720deg); } }
.sides-slide.is-drawing .sd-blink { animation: sides-blink 1.1s step-end calc(var(--a) + var(--b, 0s)) infinite; }
@keyframes sides-blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0; }
}
/* debug: the glass hunts the bug down while its legs twitch */
.sides-slide.is-drawing .sd-lens { animation: sides-hunt 1.5s cubic-bezier(0.45, 0, 0.25, 1) var(--a) both; }
@keyframes sides-hunt {
  0% { transform: translate(30px, 10px); }
  35% { transform: translate(-16px, 6px); }
  70% { transform: translate(8px, -4px); }
  100% { transform: none; }
}
.sides-reel .sd-legs-l { transform-box: fill-box; transform-origin: 100% 50%; }
.sides-reel .sd-legs-r { transform-box: fill-box; transform-origin: 0 50%; }
.sides-slide.is-drawing .sd-legs { animation: sides-twitch 0.32s linear var(--a) 4; }
.sides-slide.is-drawing .sd-legs-r { animation-delay: calc(var(--a) + 0.16s); }
@keyframes sides-twitch {
  0%, 100% { transform: none; }
  25% { transform: rotate(-10deg); }
  75% { transform: rotate(10deg); }
}
/* edit: the playhead scrubs through the clips */
.sides-reel .sd-playhead { transform: translateX(16px); }
.sides-slide.is-drawing .sd-playhead { animation: sides-scrub 1.5s ease-in-out var(--a) both; }
@keyframes sides-scrub {
  0% { transform: translateX(-50px); }
  70% { transform: translateX(50px); }
  100% { transform: translateX(16px); }
}
/* fix: the wrench turns the nut twice (it turns about the nut, its origin) */
.sides-reel .sd-turn { transform-box: view-box; transform-origin: 0 0; }
.sides-slide.is-drawing .sd-turn { animation: sides-turn 1.3s ease-in-out var(--a) both; }
@keyframes sides-turn {
  0%, 50%, 100% { transform: none; }
  25%, 75% { transform: rotate(24deg); }
}
/* publish: hearts float up from the phone */
.sides-slide.is-drawing .sd-heart { animation: sides-float 0.8s var(--ease-out) var(--a) both; }
@keyframes sides-float {
  from { opacity: 0; transform: translate(-22px, 26px); }
  to { opacity: 1; transform: none; }
}
/* ship: the rocket rumbles, then lifts off in a puff of smoke */
.sides-reel .sd-rocket { transform: translateY(-10px); }
.sides-slide.is-drawing .sd-rocket { animation: sides-launch 1.4s ease-in var(--a) both; }
@keyframes sides-launch {
  0%, 40% { transform: none; }
  5%, 15%, 25%, 35% { transform: translateX(-1px); }
  10%, 20%, 30% { transform: translateX(1px); }
  100% { transform: translateY(-10px); }
}
.sides-reel .sd-puff { transform-box: fill-box; transform-origin: 50% 100%; }
.sides-slide.is-drawing .sd-puff { animation: sides-puff 0.7s var(--ease-out) var(--a) both; }
@keyframes sides-puff {
  from { opacity: 0; transform: scale(0.3); }
  to { opacity: 1; transform: none; }
}
/* script and code: the pencil and the caret follow the writing. Each stop
   is where a line's stroke starts or ends, at the time it does (the lines'
   --d / --t in index.html, from the action's start) */
@keyframes sides-pencil {
  0% { transform: translate(-28px, -84px); }
  12.71% { transform: translate(2px, -84px); }
  18.47% { transform: translate(-28px, -72px); }
  37.12% { transform: translate(16px, -72px); }
  42.88% { transform: translate(-28px, -62px); }
  53.05% { transform: translate(-4px, -62px); }
  58.81% { transform: translate(-14px, -49px); }
  65.59% { transform: translate(2px, -49px); }
  71.36% { transform: translate(-22px, -39px); }
  84.92% { transform: translate(10px, -39px); }
  90.68% { transform: translate(-22px, -29px); }
  100% { transform: translate(0px, -29px); }
}
.sides-reel .sd-pencil { transform: translate(0px, -29px); }
.sides-slide.is-drawing .sd-pencil { animation: sides-pencil 1.39s linear var(--a) both; }
@keyframes sides-caret {
  0% { transform: translate(-41px, 15px); }
  20.76% { transform: translate(9px, 15px); }
  26.87% { transform: translate(-33px, 25px); }
  46.8% { transform: translate(15px, 25px); }
  52.91% { transform: translate(-25px, 35px); }
  73.67% { transform: translate(25px, 35px); }
  79.78% { transform: translate(-33px, 45px); }
  92.23% { transform: translate(-3px, 45px); }
  98.34% { transform: translate(-41px, 55px); }
  100% { transform: translate(-37px, 55px); }
}
.sides-reel .sd-caret { transform: translate(-37px, 55px); }
.sides-slide.is-drawing .sd-caret { animation: sides-caret 1.15s linear var(--a) both; }
/* the caret blinks once the typing is done */
.sides-slide.is-drawing .sd-caret .sd-blink { --b: 1.15s; }

/* ==========================================================================
   Contact / footer
   ========================================================================== */
.footer {
  padding-top: var(--section-space);
  padding-bottom: clamp(3rem, 5vw, 4rem);
}

/* The note for the side picked above, pointing down at the email. A new
   bubble is inserted on every pick, so the entrance replays. */
.contact-bubble {
  position: relative;
  max-width: 34em;
  margin-bottom: 1.6rem;
  padding: 1rem 1.2rem;
  background: var(--bg-secondary);
  border: 1px solid var(--highlight-border);
  color: var(--text-secondary);
  font-size: 0.95rem;
  line-height: 1.5;
  animation: bubbleIn 0.45s var(--ease-out) 0.3s both;
}
.contact-bubble b { color: var(--text-primary); font-weight: 600; }
.contact-bubble::after {
  content: "";
  position: absolute;
  left: 1.5rem;
  bottom: -6px;
  width: 10px;
  height: 10px;
  background: var(--bg-secondary);
  border-right: 1px solid var(--highlight-border);
  border-bottom: 1px solid var(--highlight-border);
  transform: rotate(45deg);
}
@keyframes bubbleIn { from { opacity: 0; transform: translateY(8px); } }

.contact-row { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
.contact-email {
  font-family: "Space Grotesk", "Space Grotesk Fallback", sans-serif;
  font-size: clamp(1.4rem, 5vw, 2.6rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  color: var(--text-primary);
  text-decoration: none;
  overflow-wrap: anywhere;
  border-bottom: 2px solid transparent;
  transition: border-color 0.2s ease;
}
.contact-email:hover { border-bottom-color: var(--accent-color); }

.copy-btn { position: relative; }
.copy-btn .copy-icon-done { display: none; }
.copy-btn.copied { background: var(--accent-color); color: var(--bg-primary); border-color: var(--accent-color); }
.copy-btn.copied .copy-icon-default { display: none; }
.copy-btn.copied .copy-icon-done { display: block; }

/* Instagram, LinkedIn and X under the email: each a card with its logo,
   then a name over a line on what you'll find there */
.socials {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.75rem;
  max-width: 66rem;
  margin-top: 2.25rem;
}
.social {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas: "icon name" "icon sub";
  align-items: center;
  align-content: center;
  column-gap: 1rem;
  padding: 1rem 1.2rem;
  border: 1px solid var(--grid-line);
  background: var(--bg-secondary);
  color: var(--text-primary);
  text-decoration: none;
  transition: border-color 0.2s ease;
}
.social:hover { border-color: var(--highlight-border); }
.social svg { grid-area: icon; width: 22px; height: 22px; }
.social-name {
  grid-area: name;
  font-family: "Space Grotesk", "Space Grotesk Fallback", sans-serif;
  font-weight: 600;
  line-height: 1.3;
}
.social-sub { grid-area: sub; font-size: 0.85rem; line-height: 1.4; color: var(--text-tertiary); }
@media (max-width: 720px) {
  .socials { grid-template-columns: minmax(0, 1fr); }
}

/* Copyright on the left, the pages and GitHub on the right */
.footer-bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem 1.5rem;
  margin-top: clamp(3.5rem, 7vw, 5rem);
}
.footer-fine,
.footer-nav a {
  font-size: 0.72rem;
  color: var(--text-tertiary);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.footer-nav { display: flex; flex-wrap: wrap; gap: 0 1.25rem; }
.footer-nav a {
  padding-block: 0.35rem;
  text-decoration: none;
  transition: color 0.2s ease;
}
.footer-nav a:hover,
.footer-nav a[aria-current="page"] { color: var(--text-primary); }

/* ==========================================================================
   Project detail dialog
   ========================================================================== */
/* A large modal: the screen less a 24px margin (up to 1680px wide). The
   gallery takes all the room beside a details column, with the screenshot as
   big as fits; the details scroll on their own when they run long. */
.pd {
  width: min(1680px, calc(100vw - 48px));
  max-width: none;
  height: calc(100dvh - 48px);
  max-height: none;
  margin: auto;
  padding: 0;
  border: 1px solid var(--grid-line);
  background: var(--bg-primary);
  color: var(--text-primary);
  overflow: hidden;
  overscroll-behavior: contain;
}
.pd[open] { animation: pdIn 0.35s var(--ease-out); }
.pd::backdrop { background: var(--backdrop); backdrop-filter: blur(4px); }
@keyframes pdIn { from { opacity: 0; transform: translateY(12px) scale(0.985); } }
/* Where a closing dialog can stay on screen for a moment (overlay), it eases
   out as well as in, with transitions in place of the keyframes above: in
   over 0.35s, out a little quicker */
@supports (overlay: auto) {
  .pd[open] { animation: none; }
  .pd,
  .pd::backdrop {
    transition: opacity 0.2s ease-in, transform 0.2s ease-in,
      overlay 0.2s allow-discrete, display 0.2s allow-discrete;
  }
  .pd[open],
  .pd[open]::backdrop { transition-duration: 0.35s; transition-timing-function: var(--ease-out); }
  .pd:not([open]) { opacity: 0; transform: translateY(8px) scale(0.985); }
  .pd:not([open])::backdrop { opacity: 0; }
  @starting-style {
    .pd[open] { opacity: 0; transform: translateY(12px) scale(0.985); }
    .pd[open]::backdrop { opacity: 0; }
  }
}

.pd-inner {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) clamp(320px, 26vw, 420px);
  height: 100%;
}

.pd-close { position: absolute; top: 0.75rem; right: 0.75rem; z-index: 3; }

/* The stage grows to fill the column above the thumbnails */
.pd-gallery {
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding: 1rem;
  border-right: 1px solid var(--grid-line);
  background: var(--bg-secondary);
}

.pd-stage { position: relative; display: grid; flex: 1 1 0; min-height: 0; }

.pd-track {
  display: flex;
  min-height: 0;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  background: var(--bg-primary);
  border: 1px solid var(--grid-line);
}
.pd-track::-webkit-scrollbar { display: none; }
.pd-track img {
  flex: 0 0 100%;
  width: 100%;
  height: 100%;
  object-fit: contain;
  scroll-snap-align: center;
}

.pd-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  background: var(--nav-bg);
  backdrop-filter: blur(8px);
}
.pd-prev { left: 0.6rem; }
.pd-next { right: 0.6rem; }
.pd-nav:disabled { opacity: 0; pointer-events: none; }

.pd-count {
  position: absolute;
  bottom: 0.6rem;
  right: 0.6rem;
  padding: 0.2rem 0.5rem;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  font-variant-numeric: tabular-nums;
  background: var(--nav-bg);
  color: var(--text-secondary);
}

.pd-thumbs {
  display: flex;
  gap: 0.4rem;
  margin-top: 0.6rem;
  overflow-x: auto;
}
.pd-thumbs::-webkit-scrollbar { height: 6px; }
.pd-thumbs::-webkit-scrollbar-thumb { border-width: 1px 0; }
@supports not selector(::-webkit-scrollbar) {
  .pd-thumbs { scrollbar-width: thin; }
}
.pd-thumb {
  flex: 0 0 auto;
  width: 72px;
  aspect-ratio: 16 / 10;
  padding: 0;
  border: 1px solid var(--grid-line);
  background: var(--bg-primary);
  cursor: pointer;
  opacity: 0.55;
  transition: opacity 0.2s ease, border-color 0.2s ease;
}
.pd-thumb img { width: 100%; height: 100%; object-fit: cover; }
/* An animated shot comes in a <picture> (a still for reduced motion); it
   steps aside so the img sizes and lays out as if it weren't there */
.pd-track picture,
.pd-thumb picture { display: contents; }
.pd-thumb:hover { opacity: 0.85; }
.pd-thumb[aria-current="true"] { opacity: 1; border-color: var(--accent-color); }

.pd-body {
  padding: 2rem 1.75rem 1.75rem;
  display: flex;
  flex-direction: column;
  gap: 1.15rem;
  min-height: 0;
  overflow-y: auto;
}

.pd-head { display: flex; align-items: center; gap: 0.85rem; padding-right: 3rem; }
.pd-icon { width: 44px; height: 44px; object-fit: contain; flex-shrink: 0; border-radius: 22.5%; }
.pd-icon[hidden] { display: none; }
.pd-name {
  font-family: "Space Grotesk", "Space Grotesk Fallback", sans-serif;
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.15;
}
.pd-meta {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-tertiary);
  margin-top: 0.2rem;
}

.pd-tags { display: flex; flex-wrap: wrap; gap: 0.35rem; }
.pd-tags:empty { display: none; }
.pd-tags span { border: 1px solid var(--highlight-border); }

.pd-use {
  font-size: 1.15rem;
  line-height: 1.4;
  color: var(--text-primary);
}
.pd-about { font-size: 0.9rem; color: var(--text-tertiary); }

.pd-tech { display: flex; flex-wrap: wrap; gap: 0.35rem; }
.pd-tech span {
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  padding: 0.25rem 0.5rem;
  border: 1px solid var(--grid-line);
  color: var(--text-tertiary);
}

.pd-links { display: flex; flex-wrap: wrap; gap: 0.5rem; padding-top: 0.25rem; }
.pd-links:empty { display: none; }

/* Mobile: the dialog becomes a full-screen sheet that scrolls as one, with
   the gallery at 16:10 above the details */
@media (max-width: 820px) {
  .pd {
    width: 100vw;
    height: 100dvh;
    max-height: 100dvh;
    border: 0;
    overflow: auto;
  }
  .pd-inner { grid-template-columns: minmax(0, 1fr); height: auto; }
  .pd-gallery { display: block; border-right: 0; border-bottom: 1px solid var(--grid-line); padding: 3.75rem 0.75rem 0.75rem; }
  .pd-stage { display: block; }
  .pd-track { aspect-ratio: 16 / 10; }
  .pd-body { padding: 1.25rem 1rem 2rem; overflow: visible; }
  .pd-head { padding-right: 0; }
  .pd-nav { display: none; }
}

/* ==========================================================================
   Responsive tuning + motion
   ========================================================================== */
/* Press feedback: buttons and cards dip the moment they're pressed, so a tap
   answers at once, before whatever it opens has loaded. The scale property
   stacks on any transform an element already has (the dialog's arrows). */
:is(.btn, .icon-btn, .filter-btn):active { scale: 0.96; }
:is(.pick, .platform, .brand, .social, .talk, .career-card, .endeavor):active { scale: 0.985; }

@media (max-width: 720px) {
  .hero, .page-first { padding-top: 7.25rem; }
}

@media (max-width: 420px) {
  .container, .nav-inner, .nav-links { padding-inline: 1rem; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
  }
  /* The product strip stands still: one copy, wrapped into centred rows */
  .strip-track { animation: none; width: auto; flex-wrap: wrap; justify-content: center; }
  .strip-track [aria-hidden="true"] { display: none; }
  .strip-viewport { -webkit-mask-image: none; mask-image: none; }
  .work-scale.is-armed svg > :not(defs, g),
  .work-scale.is-armed svg > g > * { animation: none; stroke-dashoffset: 0; }
  .work-scale.is-armed .work-scale-labels span { animation: none; opacity: 1; }
  .nav-brand[aria-current="page"] .brand-mark path { animation: none; }
}
