/* ============================================================
   Charles Ley — UI/UX Portfolio
   Editorial-noir: pure black canvas, warm off-white type,
   a single cognac accent lifted from the film in the backdrop.
   ============================================================ */

:root {
  --bg-black: #000000;
  --text-primary: #F3EFE8;
  --text-muted: #8C877D;
  --text-dim: #55524B;
  --accent: #C9A26B;
  --accent-dim: #8C6F45;

  --font-display: 'Fraunces', serif;
  --font-body: 'Instrument Sans', sans-serif;

  --side-pad: 5vw;
  --side-width: 40vw;

  --reveal-radius: clamp(120px, 9vw, 220px);
  --reveal-x: 50%;
  --reveal-y: 45%;
}

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

html { background: var(--bg-black); overflow-x: hidden; }

body {
  background: var(--bg-black);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-weight: 400;
  overflow-x: hidden;
  cursor: default;
}

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

a { color: inherit; text-decoration: none; }

/* subtle grain to keep pure black from feeling flat/dead on screen */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  opacity: 0.035;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ============================================================
   Scroll progress
   ============================================================ */

#scroll-progress-track {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 2px;
  z-index: 90;
  background: rgba(243, 239, 232, 0.06);
}

#scroll-progress-bar {
  height: 100%;
  width: 100%;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left center;
}

/* ============================================================
   Custom cursor (desktop only - gated by js/interactions.js)
   ============================================================ */

.cursor {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 95;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.3s ease;
}

.cursor.is-visible { opacity: 1; }

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

.cursor-dot {
  position: absolute;
  top: -3px;
  left: -3px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  transition: opacity 0.25s ease;
}

.cursor-ring {
  position: absolute;
  top: -20px;
  left: -20px;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid rgba(243, 239, 232, 0.35);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: width 0.35s cubic-bezier(0.19, 1, 0.22, 1), height 0.35s cubic-bezier(0.19, 1, 0.22, 1),
    top 0.35s cubic-bezier(0.19, 1, 0.22, 1), left 0.35s cubic-bezier(0.19, 1, 0.22, 1),
    border-color 0.3s ease, background 0.3s ease;
  mix-blend-mode: difference;
}

.cursor.is-hover .cursor-ring {
  width: 64px;
  height: 64px;
  top: -32px;
  left: -32px;
  border-color: rgba(243, 239, 232, 0.6);
}

.cursor.is-hover .cursor-dot { opacity: 0; }

@media (max-width: 768px), (hover: none), (pointer: coarse) {
  .cursor { display: none; }
}

/* ============================================================
   Loader
   ============================================================ */

#loader {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: var(--bg-black);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: opacity 0.7s ease, visibility 0.7s ease;
}

#loader.hidden { opacity: 0; visibility: hidden; }

.loader-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.loader-logo-stage {
  position: relative;
  /* Shrunk from min(36.8vw, 208px): with the progress readout now sharing
     the composition, a smaller mark reads more refined - the logo is an
     accent here, not the whole show. */
  width: min(26vw, 148px);
  aspect-ratio: 534 / 329;
  /* fill-mode backwards, NOT both/forwards: a held final keyframe would
     permanently pin `transform` and silently block .is-settled's scale
     settle below (same CSS-animation-vs-later-styles trap documented in
     js/interactions.js). backwards only covers the pre-start state. */
  animation: loader-stage-in 0.9s cubic-bezier(0.22, 1, 0.36, 1) backwards;
  transition: filter 1.1s ease, transform 1.1s cubic-bezier(0.22, 1, 0.36, 1);
}

@keyframes loader-stage-in {
  from { opacity: 0; transform: translateY(14px) scale(0.96); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

/* Added by js/loader-logo.js the moment the mark has fully formed: a soft
   warm exhale - faint cognac glow plus a barely-there scale settle. */
.loader-logo-stage.is-settled {
  filter: drop-shadow(0 0 22px rgba(201, 162, 107, 0.22));
  transform: scale(1.015);
}

/* ---- Loading progress readout (percent) ---- */

.loader-progress {
  margin-top: 2.1rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  animation: loader-stage-in 0.9s cubic-bezier(0.22, 1, 0.36, 1) 0.25s both;
}

.loader-progress-readout {
  font-family: var(--font-body);
  font-size: 0.74rem;
  font-weight: 500;
  letter-spacing: 0.24em;
  color: var(--text-muted);
  /* Fixed-width digits so the number counts up without wobbling sideways. */
  font-variant-numeric: tabular-nums;
}

.loader-progress-sign {
  color: var(--text-dim);
  margin-left: 0.1em;
}

.loader-logo-svg {
  position: relative;
  z-index: 2;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.loader-logo-svg path {
  fill: var(--text-primary);
  fill-opacity: 0;
  stroke: var(--text-primary);
  stroke-opacity: 0.9;
  stroke-width: 1.5px;
  vector-effect: non-scaling-stroke;
}

/* The transition itself (including each path's stagger delay) is set as a
   full inline `transition` shorthand string per-path in js/loader-logo.js,
   not here - keeping duration/delay/property together in one inline
   assignment avoids ambiguity between a shorthand rule here and a
   separately-overridden longhand delay, and inline style is also required
   since it must win over the inline starting values set moments earlier. */

/* ============================================================
   Header
   ============================================================ */

.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 40;
  padding: 2rem var(--side-pad);
  mix-blend-mode: difference;
}

.site-header nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.site-logo {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.site-logo-mark {
  width: 22px;
  height: auto;
  color: var(--text-primary);
  flex-shrink: 0;
}

.site-logo-text {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.78rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-primary);
  white-space: nowrap;
}

.nav-links {
  display: flex;
  gap: 2.5rem;
}

.nav-links a {
  font-size: 0.8rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-primary);
  position: relative;
  padding-bottom: 0.2rem;
}

.nav-links a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 100%;
  bottom: 0;
  height: 1px;
  background: var(--accent);
  transition: right 0.35s cubic-bezier(0.65, 0, 0.35, 1);
}

.nav-links a:hover::after { right: 0; }

/* ============================================================
   Hero
   ============================================================ */

.hero-standalone {
  position: relative;
  z-index: 10;
  height: 100vh;
  width: 100%;
  background: var(--bg-black);
  display: grid;
  grid-template-columns: 1.05fr 0.85fr 0.8fr;
  column-gap: 2.5vw;
  align-items: center;
  padding: 0 var(--side-pad);
}

.hero-text {
  /* Grid items default to min-width: auto, which lets a child's intrinsic
     content width (e.g. an unbreakable scrambled string in
     .hero-heading-solid - see js/hero-scramble-decode.js) override this
     column's 1.05fr share and blow out the grid, shoving .hero-visual/
     .hero-stats sideways. min-width: 0 makes this column actually respect
     its assigned fr-space and clip/wrap overflowing content instead. */
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
}

.hero-visual {
  position: relative; /* positioning context for the .hero-orbit overlay */
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ------------------------------------------------------------
   Hero tool orbit — constellation of daily-tool logos circling
   the portrait on a tilted 3D ring (js/hero-orbit.js owns all
   geometry + motion; ring/glow/particles/trails render on two
   canvases, only the six logo chips are DOM). Occlusion is a
   three-layer sandwich: --back (z1) under the portrait card (z2,
   set below) under --front (z3); chips and ring segments hop
   between the two layers as their orbit depth changes sign, so
   the ring threads behind the body and re-emerges in front.
   Chips are positioned each frame in projected coordinates and
   never rotated, so they stay upright by construction.
   ------------------------------------------------------------ */

.hero-orbit {
  position: absolute;
  pointer-events: none;
  perspective: 1000px;
}

.hero-orbit--back { z-index: 1; }
.hero-orbit--front { z-index: 3; }

/* Between the two orbit layers, so the ring's rear arc passes behind the
   photo (the body/chair acting as the mask) and the front arc crosses
   over it. */
.hero-portrait-card { z-index: 2; }

.orbit-fx {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.orbit-logo {
  position: absolute;
  top: 0;
  left: 0;
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: rgba(16, 16, 18, 0.82);
  border: 1px solid rgba(243, 239, 232, 0.14);
  box-shadow: 0 6px 18px -6px rgba(0, 0, 0, 0.6);
  opacity: 0;
  pointer-events: auto;
  cursor: default;
  will-change: transform, opacity;
}

.orbit-logo svg {
  width: 22px;
  height: 22px;
  display: block;
  fill: var(--text-muted);
  transition: fill 0.35s ease;
}

/* Soft halo - faintly present at rest (ambient glow), full on hover */
.orbit-logo::after {
  content: "";
  position: absolute;
  inset: -14px;
  border-radius: 50%;
  background: radial-gradient(circle, var(--orbit-glow, rgba(243, 239, 232, 0.22)) 0%, transparent 70%);
  opacity: 0.18;
  transition: opacity 0.35s ease;
  pointer-events: none;
}

.orbit-logo.is-hot { border-color: rgba(243, 239, 232, 0.35); }
.orbit-logo.is-hot svg { fill: var(--brand, var(--text-primary)); }
.orbit-logo.is-hot::after { opacity: 1; }

/* The orbit needs a real dedicated image column - the mobile hero folds the
   portrait into an atmospheric background layer behind the text, where a
   logo ring would tangle with the copy. */
@media (max-width: 768px) {
  .hero-orbit { display: none; }
}

/* ------------------------------------------------------------
   Hero ambient shader background
   ------------------------------------------------------------ */

.hero-shader-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  display: block;
  opacity: 0.4;
  mix-blend-mode: screen;
  pointer-events: none;
  /* The shader's light rays otherwise run at full brightness into the
     canvas boundary, leaving a visible cut line along every edge of the
     hero. A radial vignette mask dissolves the rays and smoke well before
     they reach any edge or corner. */
  -webkit-mask-image: radial-gradient(ellipse 68% 60% at 50% 50%, #000 45%, transparent 96%);
  mask-image: radial-gradient(ellipse 68% 60% at 50% 50%, #000 45%, transparent 96%);
}

.hero-text,
.hero-visual,
.hero-stats {
  position: relative;
  z-index: 1;
}

.hero-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.55rem 1.1rem;
  border: 0;
  font-family: var(--font-body);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 1.8rem;
  opacity: 0;
}

.hero-badge-icon { width: 13px; height: 13px; flex-shrink: 0; }

.hero-heading {
  font-family: 'Roboto', sans-serif;
  font-weight: 800;
  text-transform: uppercase;
  line-height: 0.9;
  letter-spacing: -0.01em;
}

.hero-heading-outline,
.hero-heading-solid {
  display: block;
  overflow: hidden;
  font-size: clamp(2.6rem, 5.4vw, 5rem);
  /* .hero-heading-solid's text content is swapped/scrambled at runtime
     (js/hero-scramble-decode.js) between words of different lengths -
     nowrap keeps a wide intermediate string from ever wrapping to a
     second line, which would change this block's height and shift
     everything below it (divider/tagline/CTA). */
  white-space: nowrap;
}

.hero-heading-outline {
  color: transparent;
  -webkit-text-stroke: 1.5px var(--text-primary);
  opacity: 0;
}

.hero-heading-solid {
  color: var(--text-primary);
  opacity: 0;
}

.hero-divider {
  width: 56px;
  height: 2px;
  background: var(--accent);
  margin: 1.7rem 0;
  opacity: 0;
}

.hero-tagline {
  max-width: 36ch;
  font-size: clamp(0.95rem, 1.1vw, 1.05rem);
  font-weight: 400;
  line-height: 1.6;
  color: var(--text-muted);
  opacity: 0;
}

.hero-cta {
  margin-top: 2.4rem;
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  border: 0;
  font-family: var(--font-body);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: underline;
  text-underline-offset: 5px;
  color: var(--text-primary);
  transition: color 0.3s ease;
  opacity: 0;
}

.hero-cta:hover { color: var(--accent); }

.hero-cta-arrow {
  width: 14px;
  height: 14px;
  animation: cta-arrow-bounce 1.8s ease-in-out infinite;
}

@keyframes hero-fade-up {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes cta-arrow-bounce {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(3px); }
}

/* Entrance choreography only plays once the loader hides (JS adds .is-ready) -
   otherwise these fire immediately on page load and finish before the loader
   fades, and the reveal loses its stagger. */
.hero-standalone.is-ready .hero-badge {
  animation: hero-fade-up 0.8s cubic-bezier(0.19, 1, 0.22, 1) 0.15s forwards;
}
.hero-standalone.is-ready .hero-heading-outline {
  animation: hero-fade-up 0.9s cubic-bezier(0.19, 1, 0.22, 1) 0.35s forwards;
}
.hero-standalone.is-ready .hero-heading-solid {
  animation: hero-fade-up 0.9s cubic-bezier(0.19, 1, 0.22, 1) 0.5s forwards;
}
.hero-standalone.is-ready .hero-divider {
  animation: hero-fade-up 0.8s cubic-bezier(0.19, 1, 0.22, 1) 0.7s forwards;
}
.hero-standalone.is-ready .hero-tagline {
  animation: hero-fade-up 0.9s cubic-bezier(0.19, 1, 0.22, 1) 0.9s forwards;
}
.hero-standalone.is-ready .hero-cta {
  animation: hero-fade-up 0.8s cubic-bezier(0.19, 1, 0.22, 1) 1.05s forwards;
}
.hero-standalone.is-ready .hero-portrait-card {
  animation: hero-visual-in 1.1s cubic-bezier(0.19, 1, 0.22, 1) 0.3s forwards;
}
.hero-standalone.is-ready .hero-stat {
  animation: hero-fade-up 0.8s cubic-bezier(0.19, 1, 0.22, 1) forwards;
}
.hero-standalone.is-ready .hero-stat:nth-child(1) { animation-delay: 1.2s; }
.hero-standalone.is-ready .hero-stat:nth-child(2) { animation-delay: 1.35s; }
.hero-standalone.is-ready .hero-stat:nth-child(3) { animation-delay: 1.5s; }

/* ------------------------------------------------------------
   Hero stats sidebar
   ------------------------------------------------------------ */

.hero-stats {
  display: flex;
  flex-direction: column;
}

.hero-stat {
  padding: 1.4rem 0;
  border-bottom: 1px solid rgba(243, 239, 232, 0.1);
  opacity: 0;
}

.hero-stat:first-child { padding-top: 0; }
.hero-stat:last-child { border-bottom: none; }

.hero-stat-value {
  font-family: 'Roboto', sans-serif;
  font-weight: 700;
  font-size: clamp(1.6rem, 2.2vw, 2.1rem);
  color: var(--text-primary);
  line-height: 1.1;
}

.hero-stat-suffix { color: var(--accent); }

.hero-stat-label {
  display: block;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-primary);
  margin-top: 0.2rem;
}

.hero-stat-desc {
  display: block;
  font-size: 0.78rem;
  line-height: 1.4;
  color: var(--text-muted);
  margin-top: 0.3rem;
}

/* ------------------------------------------------------------
   Hero portrait card
   ------------------------------------------------------------ */

.hero-portrait-card {
  position: relative;
  opacity: 0;
}

.hero-portrait-card::before {
  content: "";
  position: absolute;
  inset: -15%;
  z-index: -1;
  background: radial-gradient(circle, rgba(201, 162, 107, 0) 0%, rgba(201, 162, 107, 0) 70%);
  pointer-events: none;
}

/* ------------------------------------------------------------
   Hero image stage — cursor-follow reveal
   ------------------------------------------------------------ */

.hero-image-stage {
  position: relative;
  height: clamp(576px, 94.8vh, 1037px);
  aspect-ratio: 817 / 1228;
}

.hero-image-clip {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: 16px;
}

.hero-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center bottom;
  pointer-events: none;
  user-select: none;
}

.hero-img-base { z-index: 1; }

.hero-img-reveal {
  z-index: 2;
  opacity: 0;
  transition: opacity 0.5s ease;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-image: radial-gradient(
    circle var(--reveal-radius) at var(--reveal-x) var(--reveal-y),
    rgba(0, 0, 0, 1) 0%,
    rgba(0, 0, 0, 1) 55%,
    rgba(0, 0, 0, 0) 100%
  );
  mask-image: radial-gradient(
    circle var(--reveal-radius) at var(--reveal-x) var(--reveal-y),
    rgba(0, 0, 0, 1) 0%,
    rgba(0, 0, 0, 1) 55%,
    rgba(0, 0, 0, 0) 100%
  );
}

.hero-reveal-glow {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  opacity: 0;
  mix-blend-mode: screen;
  filter: blur(20px);
  transition: opacity 0.5s ease;
  background: radial-gradient(
    circle var(--reveal-radius) at var(--reveal-x) var(--reveal-y),
    rgba(201, 162, 107, 0) 0%,
    rgba(201, 162, 107, 0) 45%,
    rgba(201, 162, 107, 0.4) 58%,
    rgba(201, 162, 107, 0.15) 72%,
    rgba(201, 162, 107, 0) 85%
  );
}

.hero-image-stage.is-active .hero-img-reveal,
.hero-image-stage.is-active .hero-reveal-glow {
  opacity: 1;
}

@keyframes hero-visual-in {
  from { opacity: 0; transform: scale(0.96) translateY(12px); }
  to   { opacity: 1; transform: scale(1) translateY(0); }
}

/* ============================================================
   Canvas + overlay
   ============================================================ */

.canvas-wrap {
  position: fixed;
  inset: 0;
  z-index: 1;
  width: 100vw;
  height: 100vh;
  clip-path: circle(0% at 50% 50%);
  background: var(--bg-black);
}

#canvas {
  display: block;
  width: 100%;
  height: 100%;
}

#dark-overlay {
  position: fixed;
  inset: 0;
  z-index: 2;
  background: var(--bg-black);
  opacity: 0;
  pointer-events: none;
}

/* ============================================================
   Marquee
   ============================================================ */

.marquee-wrap {
  position: fixed;
  top: 50%;
  left: 0;
  width: 100%;
  z-index: 5;
  transform: translateY(-50%);
  opacity: 0;
  pointer-events: none;
  overflow: hidden;
  white-space: nowrap;
  mix-blend-mode: exclusion;
}

.marquee-text {
  display: inline-block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 11vw;
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: 0.01em;
  color: var(--text-primary);
  will-change: transform;
}

/* ============================================================
   Scroll container + sections
   ============================================================ */

#scroll-container {
  position: relative;
  z-index: 10;
  /* Grown from 900vh, then 1200vh, to make room for later panels to start
     later (see js/app.js) without cramping their own reveal timelines -
     since every ScrollTrigger here reads *percentage* progress on this
     same container, earlier sections' data-enter/leave keep the exact
     same meaning at any height; only the physical scroll distance
     changes. Latest bump (1200 -> 1500vh) opened up room after the Apps
     gallery for the WeCards showcase panel (see .app-showcase). */
  height: 1500vh;
  width: 100%;
}

.scroll-section {
  position: absolute;
  left: 0;
  width: 100%;
  transform: translateY(-50%);
  opacity: 0;
  pointer-events: none;
}

.scroll-section.is-active { pointer-events: auto; }

.section-inner { max-width: var(--side-width); }

.align-left { padding-left: var(--side-pad); padding-right: 55vw; }
.align-right { padding-left: 55vw; padding-right: var(--side-pad); display: flex; justify-content: flex-end; }
.align-right .section-inner { text-align: left; }

.section-label {
  display: block;
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 1.1rem;
}

.section-heading {
  font-family: 'Roboto', sans-serif;
  font-weight: 700;
  font-size: clamp(2.1rem, 3.6vw, 3.8rem);
  line-height: 1.08;
  color: var(--text-primary);
  margin-bottom: 1.3rem;
}

.section-note {
  display: block;
  font-size: 0.8rem;
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  margin-bottom: 1rem;
  margin-top: -0.6rem;
}

.section-body {
  font-size: 1.08rem;
  line-height: 1.65;
  color: var(--text-muted);
  max-width: 36ch;
}

/* About: thin accent rule GSAP draws in (scaleX 0 -> 1) between the
   heading's masked-line reveal and the body copy - see the
   "about-reveal" case in buildSectionTimeline (js/app.js). */
.about-rule {
  width: 72px;
  height: 2px;
  background: var(--accent);
  margin: 0.1rem 0 1.4rem;
}

/* Toolkit: tool chips that pop in with random spin + scale and idle-float
   while the section is active - a page-side echo of the holographic app
   icons drifting around Charles in the backdrop film at this scroll depth
   (see "toolkit-pop" + initToolkitFloat in js/app.js). */
.toolkit-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 1.7rem;
  max-width: 36ch;
}

.toolkit-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.55em;
  padding: 0.45em 1em;
  border: 1px solid rgba(243, 239, 232, 0.16);
  border-radius: 999px;
  font-size: 0.82rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  will-change: transform;
}

.toolkit-chip::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
}

/* ------------------------------------------------------------
   Partners — lives inside #work-gallery's own fixed panel (see
   .work-gallery-stage below) instead of being its own scroll-
   triggered section, so it plays as the opening beat of ONE
   continuous GSAP timeline (initWorkGallery in js/app.js) rather
   than crossfading in from a separately-timed element - that
   two-element handoff was the source of the rough transition this
   replaces. .partners-inline and .work-heading share the same
   stage box (stacked via position:absolute) and swap places as
   part of that single timeline.
   ------------------------------------------------------------ */

.partners-inline {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.82fr);
  align-items: center;
  gap: clamp(2rem, 5vw, 5rem);
  text-align: left;
}

.partners-copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: clamp(1.4rem, 3.4vw, 2.6rem);
  min-width: 0;
}

/* Framed ambient clip beside the copy. A crisp rounded frame (border +
   shadow) reads as "framed", while .partners-media-scrim melts the video's
   inner edges toward the copy so it still blends into the panel's black. */
.partners-media {
  position: relative;
  width: 100%;
  height: clamp(200px, 33vh, 360px);
  border-radius: clamp(14px, 1.5vw, 22px);
  overflow: hidden;
  border: 1px solid rgba(243, 239, 232, 0.1);
  background: var(--bg-black);
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.06),
    0 50px 90px -40px rgba(0, 0, 0, 0.9);
  pointer-events: none;
  will-change: transform, opacity;
}

.partners-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Muted, toned toward the editorial-noir palette so it sits with the
     rest of the panel instead of reading as a bright stock clip. */
  filter: grayscale(0.28) contrast(1.03) brightness(0.9) saturate(0.9);
  will-change: transform;
}

.partners-media-scrim {
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* Feather the left edge (toward the copy) and lower edge into black, plus
     a soft corner vignette, so the framed clip dissolves into the scene. */
  background:
    linear-gradient(90deg, var(--bg-black) 0%, rgba(0, 0, 0, 0) 34%),
    linear-gradient(0deg, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0) 42%),
    radial-gradient(120% 120% at 70% 40%, rgba(0, 0, 0, 0) 55%, rgba(0, 0, 0, 0.45) 100%);
}

.brands__inner {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  flex-wrap: nowrap;
  column-gap: clamp(0.9rem, 3.2vw, 3.2rem);
  max-width: 100%;
}

.brand-logo {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  /* The clip box for the gsap.com-style idle loop (initWorkGallery in
     js/app.js): the img slides fully out of this box on one axis and back
     in on the other, so the wrapper has to swallow the overflow. */
  overflow: hidden;
}

.brand-logo img {
  display: block;
  width: auto;
  height: clamp(14px, 2.6vw, 32px);
  max-width: 100%;
  object-fit: contain;
  filter: grayscale(1);
  /* Hidden until the idle loop's first slide-in keyframe reveals it
     (autoAlpha) - starting visible would flash a slide-out at load.
     The loop's reduced-motion branch un-hides it for non-animating users. */
  visibility: hidden;
}

.partners-heading {
  font-family: 'Roboto', sans-serif;
  font-weight: 800;
  font-size: clamp(2.1rem, 4.6vw, 4.2rem);
  line-height: 1.12;
  color: var(--text-primary);
  max-width: 24ch;
}

.partners-heading .word {
  will-change: transform, opacity;
}

/* Below this width the two-line partners heading needs the whole stage, and
   the short stage height leaves no room to stack the clip cleanly, so the
   frame steps aside and the copy reverts to its original full-width column. */
@media (max-width: 900px) {
  .partners-inline { grid-template-columns: 1fr; }
  .partners-media { display: none; }
}

@media (max-width: 768px) {
  .brands__inner { column-gap: clamp(0.5rem, 2.4vw, 1.6rem); }
  .brand-logo img { height: clamp(11px, 4vw, 22px); }
}

/* ------------------------------------------------------------
   Work gallery — fixed, horizontal scroll-driven card pan.
   Opacity + translateX are scrubbed continuously to scroll
   progress; the heading and each card's "lie flat -> flip up"
   entrance is a one-shot GSAP timeline played/reversed on the
   same active-state toggle (see initWorkGallery in js/app.js -
   both live there, not here, since GSAP owns the transform/
   opacity of these elements entirely once the timeline exists).
   ------------------------------------------------------------ */

.work-gallery {
  position: fixed;
  inset: 0;
  /* Above #scroll-container/.hero-standalone (z-index: 10) so the cards -
     which already visually render on top of them once active - also
     receive pointer events instead of silently losing clicks to the
     (invisible at this point) scroll container sitting in front of them
     in stacking order. Stays below .site-header (z-index: 40) so nav
     remains clickable while the gallery is active. */
  z-index: 20;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3.5rem;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
  padding: 0 var(--side-pad);
  /* #dark-overlay no longer goes opaque until scroll progress 0.55 (see
     initDarkOverlay in js/app.js - delayed so the frame sequence's final
     reveal pose gets real dwell time before it's covered), but this panel's
     own active window starts at 0.40 - well before that. Carrying its own
     solid backdrop keeps Partners/Work's dwell reading as a clean black
     panel instead of the canvas's held reveal frame showing through. */
  background: var(--bg-black);
}

.work-gallery-stage {
  position: relative;
  width: 100%;
  /* Tall enough for .partners-inline's three-line display heading, not just
     the smaller work heading it shares this box with. */
  min-height: clamp(260px, 40vh, 400px);
}

.work-heading {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  text-align: left;
}

.work-heading .section-heading,
.work-heading .work-subtitle { max-width: 46ch; }

.work-heading .section-label { margin-bottom: 0.9rem; }
.work-heading .section-heading { margin-bottom: 0.7rem; }

.work-subtitle {
  font-size: 1.05rem;
  line-height: 1.6;
  color: var(--text-muted);
}

/* Card strip layout from GreenSock's "Horizontal scrolling gallery"
   (demos.gsap.com / codepen dydpJzY): wide fixed-width cards in one
   nowrap flex strip that overflows the viewport, and the strip itself is
   panned horizontally in initWorkGallery (js/app.js) as the master
   scroll scrubs - vertical scrolling reads as riding along the row.
   align-self keeps the overflowing strip pinned to the panel's left
   padding edge instead of being centered by .work-gallery's
   align-items: center (which would start the view mid-strip). */
.work-track {
  display: flex;
  align-items: flex-start;
  align-self: flex-start;
  gap: 1.5vw;
  width: max-content;
  will-change: transform;
}

.work-card {
  flex: 0 0 auto;
  width: clamp(300px, 32vw, 520px);
  aspect-ratio: 4 / 3;
  border-radius: 10px;
  overflow: hidden;
  position: relative;
  box-shadow: 0 40px 80px -30px rgba(0, 0, 0, 0.75);
}

.work-card-media {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.work-card-media img,
.work-card-media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.work-card-media.is-empty img,
.work-card-media.is-empty video { display: none; }

.work-card:nth-child(1) .work-card-media.is-empty {
  background:
    radial-gradient(120% 120% at 15% 15%, rgba(201, 162, 107, 0.28), transparent 60%),
    linear-gradient(160deg, #18140f, #0a0a0a);
}

.work-card:nth-child(2) .work-card-media.is-empty {
  background:
    radial-gradient(120% 120% at 85% 15%, rgba(111, 214, 224, 0.2), transparent 60%),
    linear-gradient(160deg, #0e1414, #0a0a0a);
}

.work-card:nth-child(3) .work-card-media.is-empty {
  background:
    radial-gradient(120% 120% at 50% 85%, rgba(201, 162, 107, 0.22), transparent 60%),
    linear-gradient(160deg, #15110c, #0a0a0a);
}

.work-card:nth-child(4) .work-card-media.is-empty {
  background:
    radial-gradient(120% 120% at 20% 85%, rgba(201, 162, 107, 0.2), transparent 60%),
    linear-gradient(160deg, #131019, #0a0a0a);
}

.work-card:nth-child(5) .work-card-media.is-empty {
  background:
    radial-gradient(120% 120% at 80% 20%, rgba(111, 214, 224, 0.16), transparent 60%),
    linear-gradient(160deg, #0f1412, #0a0a0a);
}

/* ------------------------------------------------------------
   Product & Mobile App gallery — fixed panel after Work, same
   activation pattern (opacity + scrubbed reveal off shared scroll
   progress, see initAppsGallery in js/app.js). The phone parade
   uses GreenSock's infinite-card-slider layout: all cards stacked
   absolute in one phone-sized box at stage center; the loop's
   xPercent/scale tweens fan them out at runtime.
   ------------------------------------------------------------ */

.apps-gallery {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
  padding: 12vh var(--side-pad) 0;
}

.apps-heading { max-width: 52ch; }

.apps-heading .section-heading { max-width: 22ch; margin-bottom: 0.7rem; }

.apps-heading .work-subtitle { max-width: 60ch; }

.apps-stage {
  position: relative;
  flex: 1;
  perspective: 1200px;
}

.phone-cards {
  --phone-w: clamp(180px, 15.5vw, 240px);
  position: absolute;
  top: 44%;
  left: 50%;
  width: var(--phone-w);
  aspect-ratio: 9 / 19;
  transform: translate(-50%, -50%);
  list-style: none;
  transform-style: preserve-3d;
}

.phone-card {
  position: absolute;
  inset: 0;
  will-change: transform, opacity;
}

.phone-frame {
  position: relative;
  width: 100%;
  height: 100%;
  padding: clamp(7px, 0.65vw, 10px);
  border-radius: clamp(30px, 2.8vw, 42px);
  background: linear-gradient(160deg, #2a2a2e 0%, #101013 55%, #1d1d21 100%);
  border: 1px solid rgba(243, 239, 232, 0.14);
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.12),
    0 2px 6px rgba(0, 0, 0, 0.6),
    0 40px 90px -30px rgba(0, 0, 0, 0.85);
}

.phone-screen {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: clamp(24px, 2.2vw, 34px);
  overflow: hidden;
  background: #0a0a0a;
}

.phone-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.4s ease;
}

.phone-screen.has-video .phone-video { opacity: 1; }

/* Styled screen placeholder - visible until its video can play (and the
   permanent fallback if a clip ever fails to load). */
.phone-placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(201, 162, 107, 0.18), transparent 60%),
    linear-gradient(170deg, #17130e 0%, #0a0a0a 70%);
}

.phone-placeholder span {
  font-size: 0.8rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.phone-island {
  position: absolute;
  top: clamp(14px, 1.3vw, 20px);
  left: 50%;
  transform: translateX(-50%);
  width: 26%;
  height: clamp(14px, 1.2vw, 18px);
  border-radius: 999px;
  background: #000;
  box-shadow: inset 0 0 2px rgba(255, 255, 255, 0.15);
}

@media (max-width: 768px) {
  .apps-gallery { padding-top: 14vh; }
  .apps-heading .section-heading { max-width: 100%; }
  .phone-cards { --phone-w: clamp(150px, 44vw, 190px); top: 40%; }
}

/* ------------------------------------------------------------
   WeCards app showcase — fixed panel right after the Product &
   Mobile App gallery, same activation pattern (opacity + scale
   scrubbed off shared scroll progress, see initAppShowcase in
   js/app.js). Video plays on the left/top in a rounded, bordered
   media card echoing .phone-frame/.work-card's language; copy +
   App Store CTA sit alongside.
   ------------------------------------------------------------ */

.app-showcase {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
  padding: 0 var(--side-pad);
  /* Collaborate (right after this panel) is data-persist, so once the user
     has scrolled past it once it never opacity:0's again - scrolling back
     up even slightly re-activates this panel on top of it, and without a
     solid backdrop of its own (like .work-gallery has, for the same
     reason) Collaborate's now-permanently-visible text bled straight
     through this panel's empty space around the video/copy. */
  background: var(--bg-black);
}

.app-showcase-inner {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(2.5rem, 6vw, 5.5rem);
  width: 100%;
  max-width: 1320px;
}

.app-showcase-media {
  position: relative;
  aspect-ratio: 16 / 10;
  border-radius: clamp(16px, 1.6vw, 24px);
  overflow: hidden;
  background: linear-gradient(160deg, #1c1c20 0%, #0a0a0a 60%);
  border: 1px solid rgba(243, 239, 232, 0.14);
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.1),
    0 60px 120px -40px rgba(0, 0, 0, 0.85);
}

.app-showcase-placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(201, 162, 107, 0.18), transparent 60%),
    linear-gradient(170deg, #17130e 0%, #0a0a0a 70%);
}

.app-showcase-placeholder span {
  font-size: 0.85rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.app-showcase-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.4s ease;
}

.app-showcase-media.has-video .app-showcase-video { opacity: 1; }

.app-showcase-sound {
  position: absolute;
  right: clamp(12px, 1.6vw, 20px);
  bottom: clamp(12px, 1.6vw, 20px);
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: rgba(10, 10, 10, 0.55);
  backdrop-filter: blur(8px);
  border: 1px solid rgba(243, 239, 232, 0.22);
  color: var(--text-primary);
  cursor: pointer;
  transition: border-color 0.3s ease, background 0.3s ease, transform 0.3s ease;
}

.app-showcase-sound:hover { border-color: var(--accent); transform: scale(1.06); }

.app-showcase-sound svg { width: 20px; height: 20px; }

.app-showcase-sound .icon-unmuted { display: none; }
.app-showcase-sound.is-on .icon-muted { display: none; }
.app-showcase-sound.is-on .icon-unmuted { display: block; }
.app-showcase-sound.is-on { border-color: var(--accent); color: var(--accent); }

.app-showcase-copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
}

.app-showcase-copy .section-label { margin-bottom: 1.1rem; }
.app-showcase-copy .section-heading {
  font-size: clamp(1.9rem, 2.8vw, 2.8rem);
  max-width: 14ch;
  margin-bottom: 1rem;
}
.app-showcase-copy .work-subtitle { max-width: 42ch; margin-bottom: 2.2rem; }

.app-showcase-download { gap: 0.65rem; }
.app-store-icon { width: 20px; height: 20px; flex-shrink: 0; }

@media (max-width: 1000px) {
  .app-showcase-inner {
    grid-template-columns: 1fr;
    max-width: 560px;
    gap: 1.5rem;
  }
  .app-showcase-copy { align-items: center; text-align: center; }
}

@media (max-width: 768px) {
  /* Shorter than the desktop 16/10 and tighter vertical rhythm throughout -
     stacked video + full copy block + CTA all need to fit one screen with
     no scroll (this panel is a fixed, non-scrolling overlay). */
  .app-showcase-media { aspect-ratio: 16 / 9; }
  .app-showcase-copy .section-heading { max-width: 100%; margin-bottom: 0.6rem; }
  .app-showcase-copy .section-label { margin-bottom: 0.7rem; }
  .app-showcase-copy .work-subtitle { margin-bottom: 1.3rem; font-size: 0.95rem; }
}

/* ============================================================
   CTA
   ============================================================ */

.section-cta {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  overflow: hidden;
}

/* Same ambient shader as the hero (js/shader-hero-bg.js), bookending the
   page with matching atmosphere. Dimmer than the hero's (0.4 -> 0.22)
   since this section is far more text-dense and needs more contrast. */
.collab-shader-bg { opacity: 0.22; }

.section-cta .section-inner {
  position: relative;
  z-index: 1;
  max-width: 46rem;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.section-cta .section-label { margin-bottom: 1.4rem; }

.section-cta .section-heading {
  font-size: clamp(2.6rem, 5.5vw, 5.5rem);
  margin-bottom: 1.5rem;
}

.cta-heading-accent { color: var(--accent); }

.section-cta .section-body {
  max-width: 40ch;
  margin: 0 auto 3rem;
}

.cta-skills {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  width: 100%;
  margin: 0 auto 3rem;
}

.cta-skill {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.8rem;
  padding: 0 1.2rem;
  border-right: 1px solid rgba(243, 239, 232, 0.12);
}

.cta-skill:last-child { border-right: none; }

.cta-skill-icon {
  width: 24px;
  height: 24px;
  flex-shrink: 0;
  color: var(--accent);
}

.cta-skill span {
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  line-height: 1.35;
  color: var(--text-muted);
  text-align: center;
}

.cta-button {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  padding: 1.1rem 2.6rem;
  border: 1px solid var(--accent);
  border-radius: 999px;
  font-size: 0.85rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-primary);
  position: relative;
  overflow: hidden;
  transition: color 0.4s ease;
}

.cta-button::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--accent);
  transform: translateX(-101%);
  transition: transform 0.45s cubic-bezier(0.65, 0, 0.35, 1);
  z-index: -1;
}

.cta-button:hover { color: #0a0a0a; }
.cta-button:hover::before { transform: translateX(0); }

.about-learn-more {
  margin-top: 2.2rem;
  padding: 0.9rem 2rem;
  font-size: 0.78rem;
}

.cta-footer {
  margin-top: 3.5rem;
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  color: var(--text-dim);
}

/* ============================================================
   Mobile (<768px)
   ============================================================ */

@media (max-width: 768px) {
  :root {
    --side-pad: 6vw;
    --reveal-radius: clamp(70px, 18vw, 130px);
  }

  #scroll-container { height: 1000vh; }

  .hero-heading-outline,
  .hero-heading-solid { font-size: clamp(2.6rem, 13vw, 3.8rem); }

  .hero-standalone {
    position: relative;
    grid-template-columns: 1fr;
    grid-template-rows: auto auto;
    height: auto;
    min-height: 100vh;
    row-gap: 2rem;
    padding: 7rem var(--side-pad) 2.4rem;
    align-items: start;
    overflow: hidden;
  }

  /* Image becomes a right-aligned background layer behind the text/stats -
     there's no hover on mobile, so this reads as atmosphere, not an
     interactive element (js/hero-reveal.js auto-sweeps the reveal instead). */
  .hero-visual {
    position: absolute;
    inset: 0;
    height: 100%;
    z-index: 0;
    align-items: flex-end;
    justify-content: flex-end;
  }

  .hero-text,
  .hero-stats {
    position: relative;
    z-index: 1;
  }

  .hero-image-stage {
    height: 100%;
    width: 100%;
    aspect-ratio: auto;
    -webkit-mask-image: linear-gradient(to right, transparent 0%, transparent 12%, black 55%);
    mask-image: linear-gradient(to right, transparent 0%, transparent 12%, black 55%);
  }

  .hero-portrait-card { margin: 0; width: 100%; height: 100%; }

  .hero-img { object-position: right bottom; }

  /* Keep body copy inside the faded/dark zone of the mask so it never sits
     directly over the clearly-visible portrait - the bold display heading is
     fine to overlap (decorative, not read as running text). */
  .hero-tagline { max-width: 58vw; }

  .hero-cta { margin-top: 2rem; }

  .hero-stats {
    flex-direction: row;
    max-width: 100%;
    margin: 0;
  }

  .hero-stat {
    flex: 1;
    min-width: 0;
    padding: 0 0.7rem;
    border-bottom: none;
    border-right: 1px solid rgba(243, 239, 232, 0.1);
  }

  .hero-stat:first-child { padding-left: 0; }
  .hero-stat:last-child { padding-right: 0; border-right: none; }

  .hero-stat-value { font-size: clamp(1.2rem, 5.5vw, 1.6rem); }
  .hero-stat-label { font-size: 0.68rem; }
  .hero-stat-desc { display: none; }

  .site-header { padding: 1.4rem var(--side-pad); }
  .nav-links { gap: 1.4rem; }
  .nav-links a { font-size: 0.68rem; }
  .site-logo-text { font-size: 0.62rem; }
  .site-logo-mark { width: 18px; }

  .align-left,
  .align-right {
    padding-left: 6vw;
    padding-right: 6vw;
    display: block;
    text-align: center;
  }

  .section-inner {
    max-width: 100%;
    margin: 0 auto;
    background: rgba(0, 0, 0, 0.8);
    padding: 1.6rem 1.2rem;
    border-radius: 4px;
  }

  .align-right .section-inner { text-align: center; }

  .section-body { max-width: 100%; margin: 0 auto; }
  .section-note { margin-top: 0; }

  /* Same horizontal strip mechanism as desktop (see .work-track above) -
     cards just take most of the viewport width so one project reads at a
     time while the strip pans. */
  .work-track { gap: 3vw; }
  .work-card { width: 64vw; }

  .marquee-text { font-size: 16vw; }

  .section-cta .section-inner { background: transparent; backdrop-filter: none; }

  .cta-skills { flex-wrap: wrap; row-gap: 1.8rem; }
  .cta-skill {
    flex: 1 1 33%;
    padding: 0 0.6rem;
    border-right: none;
  }
}

/* Narrow phones: logo + full nav no longer both fit on one line even at the
   768px tier's sizing - drop the surname rather than shrink text further. */
@media (max-width: 480px) {
  .site-logo-surname { display: none; }
  .nav-links { gap: 0.9rem; }
  .nav-links a { font-size: 0.58rem; }
}
