/* ==========================================================================
   Sofia Sagel Sanchez — Portfolio
   Home page styles
   ========================================================================== */

:root {
  --color-bg: #ffffff;
  --color-text: #1a1a1a;
  --color-text-soft: rgba(0, 0, 0, 0.6);
  --color-text-faint: rgba(0, 0, 0, 0.45);
  --color-border: rgba(0, 0, 0, 0.1);
  --color-accent: #0080ff;
  --color-accent-soft: #a9d8ff;
  --color-accent-line: #0066e9;
  --color-cta: #0d6fff;

  --panel-radius: 15px;
  --panel-shadow: 0 5px 20px rgba(0, 0, 0, 0.15);

  --font-sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro Display",
    "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-serif: "Instrument Serif", "Times New Roman", serif;

  --section-pad-x: clamp(24px, 8vw, 120px);
  --section-pad-y: clamp(48px, 8vw, 64px);
  --content-max-width: 1360px;
}

* {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
  scroll-padding-top: 84px;
}

body {
  margin: 0;
  background: transparent;
  color: var(--color-text);
  font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img {
  max-width: 100%;
  display: block;
}

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

h1, h2, h3, p, ul, figure {
  margin: 0;
}

ul {
  padding: 0;
  list-style: none;
}

/* ---------- fixed viewport background ---------- */

.site-bg {
  position: fixed;
  inset: 0;
  z-index: -1;
  background: url("../images/grid-bg.webp") center / cover no-repeat;
  opacity: 0.7;
}

/* ---------- shared bits ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 42px;
  padding: 0 27px;
  border-radius: 10px;
  font-size: 20px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  border: none;
}

.btn--cta {
  background: var(--color-cta);
  color: #ffffff;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.btn--cta:hover,
.btn--cta:focus-visible {
  transform: translateY(-1px);
  box-shadow: 0 4px 14px rgba(13, 111, 255, 0.35);
}

.section {
  padding: var(--section-pad-y) var(--section-pad-x);
}

.section-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: clamp(32px, 6vw, 80px);
  max-width: var(--content-max-width);
  margin-left: auto;
  margin-right: auto;
}

.section-title h2 {
  font-size: 28px;
  font-weight: 700;
}

.window-titlebar {
  position: relative;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-bottom: 1px solid var(--color-border);
  padding-bottom: 3px;
}

.traffic-lights {
  position: absolute;
  left: 10px;
  top: 6px;
  display: flex;
  align-items: center;
  gap: 7px;
}

.dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: 0.5px solid rgba(0, 0, 0, 0.1);
}

.dot--close { background: #ff736a; }
.dot--min { background: #febc2e; }
.dot--zoom { background: #19c332; }

.window-title {
  font-size: 12px;
  font-weight: 700;
  color: rgba(0, 0, 0, 0.6);
  display: flex;
  align-items: center;
  gap: 4px;
}

/* ==========================================================================
   Nav — glass effect is always on, not just after scrolling
   ========================================================================== */

.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  display: flex;
  justify-content: center;
  padding: 16px 24px;
  background: rgba(255, 255, 255, 0.7);
  backdrop-filter: blur(16px) saturate(180%);
  -webkit-backdrop-filter: blur(16px) saturate(180%);
}

.nav__inner {
  display: flex;
  align-items: center;
  gap: clamp(16px, 6vw, 60px);
  flex-wrap: wrap;
  justify-content: center;
  max-width: var(--content-max-width);
}

.nav__link {
  height: 32px;
  display: inline-flex;
  align-items: center;
  padding: 0 21px;
  border-radius: 8px;
  font-size: 15px;
  font-weight: 510;
  color: var(--color-text);
  transition: background-color 0.15s ease, color 0.15s ease;
}

.nav__link:hover {
  background: rgba(0, 0, 0, 0.05);
}

.nav__link.is-active {
  background: rgba(0, 128, 255, 0.1);
  color: var(--color-accent);
}

/* ---------- mobile hamburger menu (hidden on desktop) ---------- */

.nav__hamburger {
  display: none;
  position: relative;
  z-index: 100;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  width: 24px;
  height: 24px;
  padding: 0;
  background: none;
  border: none;
  cursor: pointer;
}

.nav__hamburger-line {
  display: block;
  width: 100%;
  height: 2px;
  border-radius: 2px;
  background: var(--color-text);
  transition: transform 0.25s ease, opacity 0.2s ease;
}

.nav__hamburger[aria-expanded="true"] .nav__hamburger-line:nth-child(1) {
  transform: translateY(7px) rotate(45deg);
}

.nav__hamburger[aria-expanded="true"] .nav__hamburger-line:nth-child(2) {
  opacity: 0;
}

.nav__hamburger[aria-expanded="true"] .nav__hamburger-line:nth-child(3) {
  transform: translateY(-7px) rotate(-45deg);
}

.nav__mobile {
  display: none;
  position: fixed;
  top: 64px;
  left: 20px;
  z-index: 99;
  flex-direction: column;
  align-items: flex-start;
  gap: 24px;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-8px);
  transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s;
}

.nav__mobile.is-open {
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 100dvh;
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  background: #ffffff;
  padding: 88px 24px 24px;
  border-radius: 0;
  box-shadow: none;
}

.nav__mobile-link {
  height: 32px;
  display: inline-flex;
  align-items: center;
  padding: 0 16px;
  border-radius: 8px;
  font-size: 15px;
  font-weight: 510;
  color: var(--color-text);
  background: rgba(255, 255, 255, 0.7);
  backdrop-filter: blur(16px) saturate(180%);
  -webkit-backdrop-filter: blur(16px) saturate(180%);
}

.nav__mobile-link.is-active {
  background: rgba(0, 128, 255, 0.1);
  color: var(--color-accent);
}

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

.hero {
  position: relative;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  padding: clamp(96px, 14vh, 160px) clamp(24px, 8vw, 194px) 48px;
}

.hero__stage {
  position: relative;
  width: 100%;
  max-width: 1052px;
  aspect-ratio: 805 / 410;
  margin: 0 auto;
}

.hero__window {
  position: absolute;
  left: 32.85%;
  top: 5.11%;
  width: 64.6%;
  height: 86.83%;
  background: #fff;
  border-radius: var(--panel-radius);
  box-shadow: var(--panel-shadow);
  overflow: hidden;
}

.hero__window .window-titlebar {
  padding: 4px 0 3px;
}

.hero__window .window-title {
  font-size: 13px;
}

.hero__window .window-body {
  height: calc(100% - 24px);
}

/* .hero__card is a purely structural wrapper on desktop — it stays
   invisible to layout so .hero__window/.hero__photobooth/.hero__pitch/
   .hero__search position themselves against .hero__stage exactly as
   before. On mobile it becomes the real "card" box: .hero__name flows
   above it (see responsive section) instead of overlapping it. */
.hero__card {
  display: contents;
}

.hero__photobooth {
  position: absolute;
  left: 80.99%;
  top: 0%;
  width: 14.03%;
}

.hero__photobooth img {
  width: 100%;
  height: auto;
}

.hero__name {
  position: absolute;
  left: 17.5%;
  top: 17.66%;
  z-index: 2;
  font-family: var(--font-serif);
  font-weight: 400;
  font-size: clamp(38px, 5.6vw, 78px);
  line-height: 1;
  color: var(--color-text);
  letter-spacing: -0.008em;
}

/* Positioned exactly like the two lines in the reference artwork: the
   second line is anchored to the first via an em-based diagonal offset
   (2.125em right, 0.833em down) so the cascade — and its overlap with
   the window behind it — scales correctly at every font-size/breakpoint. */
.hero__name-line {
  position: absolute;
  left: 0;
  top: 0;
  white-space: nowrap;
}

.hero__name-line--2 {
  left: 2.125em;
  top: 0.833em;
}

.hero__name em {
  font-style: italic;
  font-family: var(--font-serif);
}

.hero__pitch {
  position: absolute;
  left: 37.1%;
  top: 61.25%;
  width: 32.73%;
  min-width: 200px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 25px;
}

.hero__tagline {
  font-size: clamp(15px, 1.4vw, 19.5px);
  line-height: 1.35;
  letter-spacing: -0.02em;
}

.hero__search {
  position: absolute;
  left: 0.68%;
  top: 55.85%;
  width: 23.23%;
  min-width: 140px;
}

.hero__search img {
  width: 100%;
  height: auto;
}

.hero__search-blink {
  position: absolute;
  left: 63.6%;
  top: 18%;
  width: 2.4%;
  height: 63%;
  background: #ffffff;
  animation: cursorBlink 1.1s step-start infinite;
}

@keyframes cursorBlink {
  50% { opacity: 0; }
}

.hero__scroll {
  display: inline-block;
  font-size: 18px;
  letter-spacing: -0.02em;
  color: var(--color-text);
  text-align: center;
  animation: bob 2.4s ease-in-out infinite;
}

@keyframes bob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(6px); }
}

/* ---------- hero entrance animation ---------- */
/* Apple-style: fade + gentle rise, all hero elements appear together. */

.hero-anim {
  opacity: 0;
  animation: heroReveal 0.9s cubic-bezier(0.16, 1, 0.3, 1) forwards;
  animation-delay: 0.1s;
}

@keyframes heroReveal {
  from {
    opacity: 0;
    transform: translateY(16px) scale(0.98);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

@media (prefers-reduced-motion: reduce) {
  .hero-anim {
    animation: none;
    opacity: 1;
  }
  .hero__scroll,
  .hero__search-blink {
    animation: none;
  }
}

/* ==========================================================================
   Work
   ========================================================================== */

/* ---------- other projects: static row of mini project-detail cards
   (image + title + meta + CTA), reusing the same building blocks as the
   work-section .work-panel — but laid out as a plain side-by-side row
   (desktop) / swipeable horizontal carousel (mobile) instead of the
   pinned, animated single-panel view. Lives on the project detail page,
   in the "see other projects" section. ---------- */

.other-projects {
  display: flex;
  gap: clamp(24px, 3vw, 40px);
  max-width: var(--content-max-width);
  margin: 0 auto;
  align-items: stretch;
}

.other-project {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  background: #fff;
  border-radius: var(--panel-radius);
  box-shadow: var(--panel-shadow);
  overflow: hidden;
}

.other-project__image {
  display: block;
  width: 100%;
  height: 200px;
  object-fit: cover;
  background: #000;
}

.other-project__image--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgba(255, 255, 255, 0.35);
  font-size: 13px;
  letter-spacing: 0.02em;
  background: linear-gradient(135deg, #1a1a1a, #2b2b2b);
}

.other-project__body {
  padding: 22px 24px 26px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 1 1 auto;
}

.other-project__title {
  margin: 0;
  font-size: 18px;
  font-weight: 600;
  line-height: 1.35;
  color: var(--color-text);
  /* Same 2-line cap used for .work-panel__title / .work-folder__name. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

.other-project__meta {
  display: grid;
  grid-template-columns: minmax(76px, auto) 1fr;
  column-gap: 14px;
  row-gap: 8px;
  margin: 6px 0 0;
}

.other-project__meta dt {
  margin: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--color-text-faint);
  white-space: nowrap;
}

.other-project__meta dd {
  margin: 0;
  font-size: 14px;
  line-height: 1.45;
  color: var(--color-text);
}

.other-project__cta {
  /* Push the CTA to the bottom of the card so buttons line up across
     cards of equal height, whatever the length of the description. */
  margin-top: auto;
  align-self: flex-start;
}

.other-project__meta {
  margin-bottom: 12px;
}

/* ---------- work: pinned folder switcher + project panel ----------
   .work__scroll is a tall wrapper (N viewport-heights, N = --steps) so
   that scrolling through it pins .work__stage in place while js/main.js
   advances which folder/panel pair is active — one step per viewport,
   Apple-product-page style. The panels themselves sit stacked in the
   same grid cell (.work__panels) and cross-fade + scale between one
   another, echoing the macOS "window grows out of its folder" open
   animation from the reference recording. Mobile collapses all of this
   to a normal, non-pinned block — see the responsive section. ---------- */

@media (min-width: 901px) {
  /* Tighter gap between the "work" title and the folder/panel area on
     desktop only — overrides the shared .section-title clamp(...) that
     every other section still uses. */
  #work .section-title {
    margin-bottom: 24px;
  }
}

.work__scroll {
  position: relative;
  height: calc(var(--steps, 4) * 100vh);
}

.work__stage {
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  align-items: center;
  padding: 96px 0 40px;
}

.work__inner {
  width: 100%;
  max-width: var(--content-max-width);
  margin: 0 auto;
  display: flex;
  align-items: flex-start;
  gap: clamp(32px, 5vw, 64px);
}

.work__folders {
  flex: 0 0 200px;
  display: flex;
  flex-direction: column;
  gap: 32px;
}

.work-folder {
  all: unset;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  cursor: pointer;
  border-radius: 12px;
}

/* Two real icon assets (rather than a CSS filter) — one drawn for the
   active state, one for inactive — swapped via display so only one ever
   takes up layout space. */
.work-folder__icon {
  width: 100%;
  max-width: 160px;
  height: auto;
  transition: transform 0.2s ease;
}

.work-folder__icon--active {
  display: none;
}

.work-folder__icon--inactive {
  display: block;
}

.work-folder.is-active .work-folder__icon--active {
  display: block;
}

.work-folder.is-active .work-folder__icon--inactive {
  display: none;
}

.work-folder__name {
  font-size: 16px;
  text-align: center;
  color: #808080;
  transition: color 0.2s ease;
  /* Hard cap at 2 lines regardless of how long a project name ends up
     being — overflow truncates with an ellipsis rather than a 3rd line. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

.work-folder.is-active .work-folder__name {
  color: var(--color-text);
  font-weight: 600;
}

.work-folder:not(.is-active):hover .work-folder__icon {
  transform: translateY(-2px);
}

.work-folder:not(.is-active):hover .work-folder__name {
  color: var(--color-text-soft);
}

.work__panels {
  flex: 1 1 auto;
  min-width: 0;
  display: grid;
}

.work-panel {
  grid-area: 1 / 1;
  display: flex;
  flex-direction: column;
  min-height: 600px;
  background: #fff;
  border-radius: var(--panel-radius);
  box-shadow: var(--panel-shadow);
  overflow: hidden;
  opacity: 0;
  /* Exaggerated "grows out of its folder" scale — js/main.js sets
     transform-origin per panel to its own folder's on-screen position,
     so each panel visibly expands out of (and collapses back into) the
     folder that opened it, echoing the macOS reference recording. This
     base rule is what governs a panel LOSING .is-active (the transition
     used for a state change is read from the ending style) — quick and
     eased-in, like a window snapping shut into its folder — so the
     close reads as a distinct, finished beat before the next one opens. */
  transform: scale(0.1);
  transform-origin: left center;
  pointer-events: none;
  transition: opacity 0.32s cubic-bezier(0.4, 0, 1, 1),
    transform 0.32s cubic-bezier(0.4, 0, 1, 1);
  z-index: 1;
}

.work-panel.is-active {
  opacity: 1;
  transform: scale(1);
  pointer-events: auto;
  z-index: 2;
  /* GAINING .is-active reads its transition from this rule instead: a
     short delay lets the outgoing panel's close mostly finish first, then
     this one grows out of its own folder — slower and eased-out, like a
     window opening — so open and close read as two separate moments
     rather than a single crossfade. */
  transition: opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0.22s,
    transform 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0.22s;
}

@media (prefers-reduced-motion: reduce) {
  .work-panel,
  .work-panel.is-active {
    transition: opacity 0.15s linear;
    transform: none !important;
  }
}

.work-panel__image {
  display: block;
  flex: 0 0 360px;
  width: 100%;
  height: 360px;
  object-fit: cover;
  background: #000;
}

.work-panel__image--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgba(255, 255, 255, 0.35);
  font-size: 14px;
  letter-spacing: 0.02em;
  background: linear-gradient(135deg, #1a1a1a, #2b2b2b);
}

.work-panel__body {
  padding: 28px 32px 32px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

.work-panel__title {
  margin: 0;
  font-size: 22px;
  font-weight: 600;
  line-height: 1.35;
  color: var(--color-text);
  /* Same 2-line cap as .work-folder__name. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

.work-panel__meta {
  display: grid;
  grid-template-columns: minmax(100px, auto) 1fr;
  column-gap: 20px;
  row-gap: 12px;
  margin: 8px 0 0;
}

.work-panel__meta dt {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  color: var(--color-text-faint);
  white-space: nowrap;
}

.work-panel__meta dd {
  margin: 0;
  font-size: 16px;
  line-height: 1.45;
  color: var(--color-text);
}

.work-panel__cta {
  /* body already has `gap: 10px` before every child, including this one;
     14px more makes the total space above the CTA an even 24px. */
  margin-top: 14px;
  align-self: flex-start;
}

/* ---------- testimonials (part of the work section) ---------- */

.testimonials {
  max-width: var(--content-max-width);
  margin: 80px auto 0;
}

/* Now its own top-level section (moved between "about" and the logo
   parade) rather than a continuation of the work section, so the
   section's own top padding already provides the breathing room. */
.testimonials-section .testimonials {
  margin-top: 0;
}

.testimonials__heading {
  font-size: clamp(22px, 2.4vw, 32px);
  line-height: 1.3;
}

/* Full-bleed marquee: escapes the section's side padding (--section-pad-x)
   so the track always runs edge-to-edge of the viewport, regardless of
   where this section sits on the page. */
.testimonials-marquee {
  position: relative;
  left: 50%;
  width: 100vw;
  transform: translateX(-50%);
  overflow-x: hidden;
  overflow-y: visible;
  margin-top: 32px;
  padding: 24px 0 28px;
}

.testimonials-marquee__track {
  display: flex;
  width: max-content;
  animation: marquee-scroll 46s linear infinite;
}

/* Hovering (or focusing) anywhere on the strip pauses the whole track,
   not just the card under the pointer. */
.testimonials-marquee:hover .testimonials-marquee__track,
.testimonials-marquee:focus-within .testimonials-marquee__track {
  animation-play-state: paused;
}

.testimonials-marquee__group {
  display: flex;
  margin: 0;
  padding: 0;
  list-style: none;
}

.testimonial-card {
  /* Fixed footprint so every card reads as the same size no matter how
     long its quote is. --card-w shrinks on narrow viewports so at least
     one card is always fully visible in the mobile viewport. */
  --card-w: min(320px, 82vw);
  --card-h: 220px;
  flex: 0 0 auto;
  width: var(--card-w);
  min-width: var(--card-w);
  min-height: var(--card-h);
  margin-right: 24px;
  background: #fff;
  border-radius: 18px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.12);
  padding: 20px 21px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 20px;
  position: relative;
}

.testimonial-card__quote {
  margin: 0;
  font-size: 16px;
  font-weight: 500;
  line-height: 1.4;
  color: #000;
  display: -webkit-box;
  -webkit-line-clamp: 6;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.testimonial-card__author {
  display: flex;
  align-items: center;
  gap: 10px;
}

.testimonial-card__avatar {
  flex: 0 0 auto;
  display: block;
  width: 40px;
  height: 40px;
  border-radius: 100px;
  object-fit: cover;
}

.testimonial-card__meta {
  margin: 0;
  font-size: 14px;
  line-height: 1.4;
  color: #000;
}

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

@media (prefers-reduced-motion: reduce) {
  .testimonials-marquee__track {
    animation: none;
  }
  .testimonials-marquee__group[aria-hidden="true"] {
    display: none;
  }
}

/* ---------- logo parade ---------- */

.logo-parade {
  padding-top: 0;
}

.logo-parade__heading {
  max-width: var(--content-max-width);
  margin: 0 auto;
  font-size: clamp(22px, 2.4vw, 32px);
  line-height: 1.3;
}

.logo-parade__marquee {
  position: relative;
  left: 50%;
  width: 100vw;
  transform: translateX(-50%);
  overflow-x: hidden;
  overflow-y: visible;
  margin-top: 32px;
  padding: 24px 0;
}

.logo-parade__track {
  display: flex;
  width: max-content;
  /* same animation as the testimonials marquee, just reversed so the
     logos travel left-to-right instead of right-to-left. */
  animation: marquee-scroll 32s linear infinite reverse;
}

.logo-parade__marquee:hover .logo-parade__track,
.logo-parade__marquee:focus-within .logo-parade__track {
  animation-play-state: paused;
}

.logo-parade__group {
  display: flex;
  align-items: center;
  margin: 0;
  padding: 0;
  list-style: none;
}

.logo-parade__group li {
  margin-right: clamp(16px, 2.5vw, 32px);
}

.logo-parade__logo {
  display: inline-flex;
  align-items: center;
  transition: transform 0.2s ease;
}

.logo-parade__logo:hover,
.logo-parade__logo:focus-visible {
  transform: translateY(-3px);
}

.logo-parade__logo-img {
  display: block;
  height: 64px;
  width: auto;
  border-radius: var(--panel-radius);
  box-shadow: var(--panel-shadow);
}

@media (prefers-reduced-motion: reduce) {
  .logo-parade__track {
    animation: none;
  }
  .logo-parade__group[aria-hidden="true"] {
    display: none;
  }
}
/* ==========================================================================
   About
   ========================================================================== */

.about__content {
  display: flex;
  gap: clamp(32px, 6vw, 80px);
  align-items: flex-start;
  max-width: var(--content-max-width);
  margin: 0 auto;
}

.about__panel {
  flex: 0 0 240px;
  width: 240px;
  background: #fff;
  border-radius: 24px;
}

.about__panel img {
  width: 100%;
  height: auto;
  border-radius: inherit;
}

.about__collage {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  aspect-ratio: 880 / 700;
}

.collage-item {
  position: absolute;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}

.collage-item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 12px;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15);
}

.collage-item figcaption {
  font-size: clamp(11px, 2.6vw, 14.4px);
  color: var(--color-text);
}

.collage-item--travel {
  left: 0%;
  top: 13.5%;
  width: 19.4%;
  height: 34.5%;
}

.collage-item--dog {
  left: 45.1%;
  top: 0%;
  width: 20.6%;
  height: 33.8%;
}

.collage-item--sunset {
  left: 72.7%;
  top: 43.6%;
  width: 27.3%;
  height: 42.1%;
}

.collage-item--pottery {
  left: 24.3%;
  top: 63.4%;
  width: 16.5%;
  height: 34.5%;
}

.collage-heading {
  position: absolute;
  left: 30.7%;
  top: 39.4%;
  width: 35%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
}

.collage-heading__title {
  font-size: clamp(22px, 2.4vw, 32px);
}

.selection {
  position: relative;
  display: inline-flex;
  align-items: center;
}

.selection__dot {
  position: absolute;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--color-accent-line);
  z-index: 1;
}

.selection__dot--start {
  left: -5px;
  top: -5px;
}

.selection__dot--end {
  right: -5px;
  bottom: -5px;
}

.collage-tag {
  display: inline-block;
  background: var(--color-accent-soft);
  border-left: 2.5px solid var(--color-accent-line);
  border-right: 2.5px solid var(--color-accent-line);
  padding: 5px 8px;
  font-size: clamp(20px, 2.2vw, 30px);
  line-height: 1;
  color: #000;
}

.collage-quote {
  position: absolute;
  font-style: italic;
  font-size: clamp(12px, 3vw, 18px);
  line-height: 1.5;
  color: var(--color-text);
  max-width: 190px;
}

.collage-quote--origin {
  left: 75%;
  top: 25.9%;
  text-align: right;
}

.collage-quote--art {
  left: 48.1%;
  top: 75.6%;
}

.collage-quote--why {
  left: 22.2%;
  top: 14.7%;
  max-width: 160px;
}

.collage-quote--matcha {
  left: 1.3%;
  top: 58.4%;
  max-width: 170px;
}

/* ==========================================================================
   Contact
   ========================================================================== */

.contact {
  display: flex;
  justify-content: center;
}

.contact__card {
  width: 100%;
  max-width: 708px;
  background: rgba(255, 255, 255, 0.6);
  backdrop-filter: blur(20px);
  border: 1px solid rgba(255, 255, 255, 0.6);
  border-radius: 24px;
  box-shadow: 0 8px 40px rgba(0, 0, 0, 0.1);
  padding: clamp(20px, 4vw, 32px) clamp(20px, 5vw, 40px);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
}

.contact__text h2 {
  font-size: 28px;
  font-weight: 700;
  margin-bottom: 8px;
}

.contact__text p {
  font-size: 16px;
  color: var(--color-text-soft);
  max-width: 40ch;
}

.contact__icons {
  display: flex;
  gap: 12px;
}

.app-icon {
  display: block;
  width: 41px;
  height: 41px;
  transition: transform 0.15s ease;
}

.app-icon:hover {
  transform: translateY(-2px);
}

.app-icon img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

@media (min-width: 901px) {
  .app-icon {
    width: 52px;
    height: 52px;
  }
}

/* ==========================================================================
   Project detail page
   ========================================================================== */

.project {
  padding-top: clamp(96px, 14vh, 150px);
}

.project-back {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  max-width: 800px;
  margin: 0 auto;
  font-size: 15px;
  font-weight: 510;
  color: var(--color-text);
  transition: gap 0.15s ease;
}

.project-back:hover,
.project-back:focus-visible {
  gap: 12px;
}

.project-hero-image {
  display: block;
  width: 100%;
  max-width: 800px;
  height: auto;
  margin: 24px auto 0;
  border-radius: var(--panel-radius);
  box-shadow: var(--panel-shadow);
}

.project-content {
  max-width: 800px;
  margin: 0 auto;
}

.project-title {
  margin-top: clamp(28px, 5vw, 44px);
  font-size: clamp(28px, 4vw, 40px);
  font-weight: 700;
  line-height: 1.2;
  color: var(--color-text);
}

.project-subtitle {
  margin-top: 12px;
  font-size: 16px;
  line-height: 1.5;
  color: var(--color-text-soft);
}

/* Small "role • client • platform • year" line above the hero image —
   same color as .project-subtitle, so it reads as a kicker rather than
   a heading. */
.project-kicker {
  max-width: 800px;
  margin: 16px auto 0;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--color-text-soft);
}

/* Takeaways/impact grid — replaces the old role/client/platform/year
   tiles in the same spot. Same card language as .project-callout /
   .project-keyfindings (white, panel-radius, panel-shadow), laid out as
   a 2x2 grid with thin dividers, each cell a big number (+ optional
   status badge) over a small caps label. */
.project-impact {
  margin-top: 20px;
  width: 100%;
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: 12px;
  box-shadow: var(--panel-shadow);
  overflow: hidden;
  display: grid;
  grid-template-columns: 1fr 1fr;
}

.project-impact__item {
  padding: 14px 16px;
  border-right: 1px solid var(--color-border);
  border-bottom: 1px solid var(--color-border);
}

.project-impact__item:nth-child(2n) {
  border-right: none;
}

.project-impact__item:nth-last-child(-n + 2) {
  border-bottom: none;
}

.project-impact__value {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px;
}

.project-impact__number {
  font-size: 17px;
  font-weight: 700;
  line-height: 1.1;
  color: var(--color-text);
}

.project-impact__badge {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--color-border);
  border-radius: 5px;
  padding: 1px 6px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--color-text-soft);
}

.project-impact__label {
  margin: 3px 0 0;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--color-text-faint);
}

/* Success-metrics table ("status & next steps" -> "How we will know
   we succeeded"). A plain white card matching .project-impact's
   language (border, panel-radius, panel-shadow); the wrapper carries
   the radius/shadow/scroll and the table itself stays borderless so
   corners clip cleanly. Horizontal scroll is the mobile fallback
   rather than a stacked-card rewrite, since the data reads as a table
   first. */
.metrics-table-wrap {
  margin-top: 20px;
  width: 100%;
  overflow-x: auto;
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: 16px;
  box-shadow: var(--panel-shadow);
}

.metrics-table {
  width: 100%;
  min-width: 640px;
  border-collapse: collapse;
}

.metrics-table th,
.metrics-table td {
  padding: 12px 18px;
  text-align: left;
  font-size: 13.5px;
  line-height: 1.4;
  color: var(--color-text);
  border-bottom: 1px solid var(--color-border);
  white-space: nowrap;
}

.metrics-table th:first-child,
.metrics-table td:first-child {
  white-space: normal;
  min-width: 240px;
}

.metrics-table thead th {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--color-text-faint);
  background: rgba(0, 0, 0, 0.02);
}

.metrics-table tbody tr:last-child td {
  border-bottom: none;
}

.metrics-table td:nth-child(4) {
  font-weight: 600;
}

.metrics-table__tag {
  display: inline-flex;
  align-items: center;
  border-radius: 5px;
  padding: 2px 8px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  white-space: nowrap;
}

.metrics-table__tag--decision {
  background: var(--color-accent-soft);
  color: #003a7a;
}

.metrics-table__tag--okr {
  background: #ffe4b8;
  color: #7a4b00;
}

.metrics-table__tag--informative {
  background: rgba(0, 0, 0, 0.06);
  color: var(--color-text-soft);
}

.project-callout {
  margin-top: 28px;
  width: 100%;
  background: #fff;
  border-radius: 16px;
  padding: clamp(18px, 3vw, 26px) clamp(20px, 3.5vw, 30px);
  box-shadow: var(--panel-shadow);
}

.project-callout h3 {
  font-size: 17px;
  font-weight: 700;
  color: #000000;
}

.project-callout p {
  margin-top: 10px;
  font-size: 15px;
  line-height: 1.55;
  color: var(--color-text-soft);
}

/* Blue background reserved for tl;dr — set apart from the plain white
   callouts (e.g. "the goal") so it doesn't compete with the impact
   grid right above it. All its text stays pure black rather than the
   accent-blue heading other callouts might use. */
.project-callout--accent {
  background: #dce8fc;
}

.project-callout--accent h3,
.project-callout--accent p {
  color: #000000;
}

.project-section {
  margin-top: clamp(48px, 8vw, 72px);
}

.project-section h2 {
  font-size: 28px;
  font-weight: 700;
  color: var(--color-text);
}

/* Inline sub-heading for a labelled aside within a project-section
   (e.g. discovery's "Hypothesis: quality over quantity") — plain text,
   no card/background, just a step down in weight from the section h2.
   Scoped to direct children only so it never touches an h3 nested
   inside .project-callout or .project-decisions__item, which have
   their own rules below. */
.project-section > h3 {
  margin-top: 24px;
  font-size: 17px;
  font-weight: 700;
  color: var(--color-text);
}

.project-section > p {
  margin-top: 16px;
  font-size: 16px;
  line-height: 1.6;
  color: var(--color-text);
  max-width: 78ch;
}

.project-section > p + p {
  margin-top: 14px;
}

.project-block-image {
  display: block;
  width: 100%;
  height: auto;
  margin-top: 28px;
  border-radius: var(--panel-radius);
  box-shadow: var(--panel-shadow);
}

/* "problem" section screenshot grid — built from real markup (image +
   caption per screen) instead of one flattened annotated screenshot.
   A dark card (echoing the Figma frame's black background) holding N
   phone-screenshot cards that wrap responsively: one row on desktop,
   two rows of two on narrower widths, with no separate breakpoint
   needed. */
.problem-screens {
  margin-top: 28px;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: center;
  gap: clamp(20px, 4vw, 32px) clamp(16px, 3vw, 28px);
  background: #000;
  border-radius: var(--panel-radius);
  box-shadow: var(--panel-shadow);
  padding: clamp(20px, 5vw, 32px) clamp(20px, 7vw, 64px);
}

.problem-screens__item {
  flex: 1 1 140px;
  max-width: 180px;
  display: flex;
  flex-direction: column;
}

.problem-screens__frame {
  width: 100%;
  aspect-ratio: 347 / 754;
  border-radius: 9px;
  overflow: hidden;
  box-shadow: 0 5px 12px rgba(0, 0, 0, 0.05), 0 0 1.5px rgba(0, 0, 0, 0.25);
}

.problem-screens__frame img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.problem-screens__caption {
  margin: 10px 0 0;
  font-size: 12px;
  font-weight: 510;
  line-height: 1.4;
  color: #ffffff;
}

/* "discovery" section research board — another flattened screenshot
   rebuilt as real markup. Sharp corners, no per-image shadow: the
   source design shows these as flat artifacts pinned to a dark board,
   not cards. Desktop: one wide image on top, two proportionally-split
   images in a row below (their widths follow the same ratio as the
   Figma frame, via flex-grow). Mobile: all three stack in a column
   (see the media query below), matching the separate mobile frame. */
.research-board {
  margin-top: 28px;
  display: flex;
  flex-direction: column;
  gap: 24px;
  background: #000;
  border-radius: var(--panel-radius);
  box-shadow: var(--panel-shadow);
  /* No bottom padding: the data-table image is deliberately cropped
     flush with the board's bottom edge in the source design. */
  padding: 24px 12% 0;
}

.research-board__wide {
  display: block;
  width: 100%;
  height: auto;
}

.research-board__row {
  display: flex;
  gap: clamp(20px, 5vw, 54px);
  align-items: flex-start;
}

.research-board__phones {
  display: block;
  height: auto;
  flex: 327 1 0;
  /* Without this, the img's own intrinsic width (as a replaced
     element) sets an automatic minimum size that overrides flex-grow's
     ratio and forces it to the row's full width. */
  min-width: 0;
  aspect-ratio: 981 / 369;
  object-fit: cover;
}

.research-board__table {
  display: block;
  height: auto;
  flex: 227 1 0;
  min-width: 0;
  aspect-ratio: 661 / 552;
  object-fit: cover;
}

/* "design decisions" section — the two hand-off screens, rebuilt as
   real markup instead of a flattened screenshot. A dark card holds two
   groups: group A is the intent-routing screen with an explanatory
   note beside it; group B is the two opt-out screens, each with its
   own caption below. Proportions follow the Figma frame's ratios
   (via flex-grow on a zero basis) so the split stays fluid at any
   width; stacks to a single column below the mobile breakpoint. */
.decision-screens {
  margin-top: 28px;
  background: #000;
  border-radius: var(--panel-radius);
  box-shadow: var(--panel-shadow);
  padding: clamp(24px, 5vw, 42px) clamp(24px, 6vw, 50px);
}

.decision-screens__row {
  display: flex;
  align-items: flex-start;
  gap: clamp(24px, 8vw, 79px);
}

.decision-screens__group {
  display: flex;
  align-items: flex-start;
  min-width: 0;
}

.decision-screens__group--a {
  flex: 300 1 0;
  gap: clamp(12px, 3vw, 20px);
}

.decision-screens__group--b {
  flex: 321 1 0;
  gap: clamp(16px, 4vw, 32px);
}

.decision-screens__card {
  display: flex;
  flex-direction: column;
  flex: 1 1 0;
  min-width: 0;
  gap: 12px;
}

.decision-screens__frame {
  flex: 1 1 0;
  min-width: 0;
  aspect-ratio: 140 / 303;
}

.decision-screens__frame img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.decision-screens__note {
  flex: 1 1 0;
  min-width: 0;
  margin: 0;
  font-size: 12px;
  font-weight: 510;
  line-height: 1.4;
  color: #ffffff;
}

.decision-screens__caption {
  margin: 0;
  font-size: 8px;
  font-weight: 510;
  line-height: 1.4;
  color: #ffffff;
}

/* Closing case-study "video" — same grid pattern as .problem-screens
   (a dark card holding N screen-recording cards that wrap responsively:
   one row on desktop, two rows of two on narrower widths, no separate
   breakpoint needed) but with autoplaying muted video clips instead of
   static screenshots. Playback is sequenced in js/main.js: one clip
   plays at a time, in order, looping back to the first when the last
   one ends. */
.video-screens {
  margin-top: 28px;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: center;
  gap: clamp(20px, 4vw, 29px) clamp(16px, 3vw, 28px);
  background: #000;
  border-radius: var(--panel-radius);
  box-shadow: var(--panel-shadow);
  padding: clamp(20px, 5vw, 32px) clamp(20px, 7vw, 64px);
}

.video-screens__item {
  flex: 1 1 140px;
  max-width: 180px;
  display: flex;
  flex-direction: column;
}

.video-screens__frame {
  width: 100%;
  aspect-ratio: 144 / 312;
  border-radius: 9px;
  overflow: hidden;
  box-shadow: 0 5px 12px rgba(0, 0, 0, 0.05), 0 0 1.5px rgba(0, 0, 0, 0.25);
}

.video-screens__frame video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.video-screens__caption {
  margin: 10px 0 0;
  font-size: 8px;
  font-weight: 510;
  line-height: 1.4;
  color: #ffffff;
}

/* Validation section research board — a single Figma-exported graphic
   (its own dark rounded card, transparent corners already baked in)
   rather than markup built from real elements like the other boards
   on this page, so no extra radius/shadow is applied here. Two crops
   exist for two aspect ratios rather than one asset reflowing, so the
   swap between them is a plain show/hide at the mobile breakpoint
   (see the max-width: 900px block) instead of a responsive layout. */
.validation-board {
  margin-top: 28px;
}

.validation-board__image {
  display: block;
  width: 100%;
  height: auto;
}

.validation-board__image--mobile {
  display: none;
}

.project-keyfindings {
  margin-top: 24px;
  width: 100%;
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: 16px;
  padding: clamp(20px, 3vw, 28px) clamp(22px, 3.5vw, 32px);
  box-shadow: var(--panel-shadow);
}

.project-keyfindings__label {
  margin-bottom: 20px;
}

.project-bullet-list {
  list-style: disc;
  padding-left: 20px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  font-size: 15px;
  line-height: 1.55;
  color: var(--color-text);
}

.project-decisions {
  display: flex;
  flex-direction: column;
  gap: 26px;
  margin-top: 20px;
}

.project-decisions__item {
  display: flex;
  align-items: flex-start;
  gap: 14px;
}

.project-decisions__bullet {
  flex: 0 0 auto;
  width: 6px;
  height: 6px;
  margin-top: 10px;
  border-radius: 50%;
  background: var(--color-text);
}

.project-decisions__item h3 {
  font-size: 16px;
  font-weight: 700;
  color: var(--color-text);
}

.project-decisions__item p {
  margin-top: 6px;
  font-size: 15px;
  line-height: 1.55;
  color: var(--color-text-soft);
}

/* ==========================================================================
   Responsive
   ========================================================================== */

@media (max-width: 900px) {
  /* ---- nav: swap the pill row for a hamburger menu, per the dedicated
     mWeb nav component (default = icon only, active = vertical pill list). ---- */

  .nav {
    justify-content: flex-start;
    padding: 20px;
  }

  .nav__inner {
    display: none;
  }

  .nav__hamburger {
    display: flex;
  }

  .nav__mobile {
    display: flex;
  }

  /* ---- hero: on mobile this switches from the desktop's overlapping
     "artwork" collage to a real stacked layout — .hero__name flows in
     normal document flow above the card (not on top of it), and
     .hero__card sizes itself to its own content (titlebar + tagline +
     button) via .hero__pitch's real flow height, instead of a
     hand-tuned aspect-ratio box. .hero__card stays `display:contents`
     on desktop (set in the base rules above), so window/photobooth/
     pitch/search keep positioning against .hero__stage exactly like
     before; here it becomes the real, content-sized card box. ---- */

  .hero {
    min-height: 100vh;
    /* svh = the viewport height with the mobile browser's toolbars
       showing, so "scroll down" is never hidden behind them. */
    min-height: 100svh;
    /* nav = 20px pad + 24px hamburger + 20px pad = 64px tall;
       + the requested 40px gap before the name = 104px. */
    padding-top: 104px;
    padding-bottom: calc(64px + env(safe-area-inset-bottom, 0px));
  }

  .hero__stage {
    aspect-ratio: auto;
    max-width: 460px;
  }

  /* Real flow now (not absolutely positioned against the stage), so it
     reserves genuine height and .hero__card below sits exactly
     `margin-top` away — no more guessing at percentages. The height is
     the two cascading name lines' actual extent (1em + the second
     line's 0.833em drop + its own 1em), so it always matches the
     rendered text regardless of where the font-size clamp lands. */
  .hero__name {
    position: relative;
    left: 0;
    top: 0;
    width: 58%;
    height: 1.833em;
    font-size: clamp(36px, 11vw, 44px);
  }

  /* The requested 64px gap between the name and the card. */
  .hero__card {
    display: block;
    position: relative;
    width: 100%;
    margin-top: 64px;
  }

  .hero__window {
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    width: auto;
    height: auto;
  }

  .hero__window .window-titlebar {
    height: 16px;
    padding: 3px 0 2px;
  }

  .hero__window .window-title {
    font-size: 9px;
  }

  .hero__window .traffic-lights {
    left: 4px;
    top: 4px;
    gap: 5px;
  }

  .hero__window .dot {
    width: 7px;
    height: 7px;
  }

  .hero__photobooth {
    left: auto;
    right: 3%;
    top: 0%;
    width: 21.23%;
  }

  /* Real flow content now — it drives .hero__card's height so the card
     always hugs the titlebar + tagline + button exactly, instead of
     being absolutely positioned at a hand-tuned percentage. Top padding
     clears the 16px titlebar plus breathing room; right padding keeps
     the text clear of the photo strip. */
  .hero__pitch {
    position: relative;
    left: auto;
    top: auto;
    width: auto;
    min-width: 0;
    min-height: 240px;
    justify-content: flex-end;
    gap: 12px;
    padding: 20px 30% 20px 20px;
  }

  .hero__tagline {
    font-size: clamp(11px, 3.2vw, 13px);
    line-height: 1.3;
  }

  .hero__pitch .btn--cta {
    height: 28px;
    padding: 0 18px;
    font-size: 16px;
    border-radius: 7px;
  }

  .hero__search {
    display: none;
  }

  .hero__scroll {
    font-size: 16px;
  }

  /* ---- about: same fix — keep the percentage/absolute collage system,
     just re-anchored to the mobile proportions from the Figma frame.
     The scattered italic quotes are dropped on mobile per the new
     reference (they're what was overlapping the photos before). ---- */

  .about__content {
    flex-direction: column;
    align-items: center;
  }

  .about__panel {
    display: none;
  }

  .about__collage {
    width: 100%;
    max-width: 400px;
    aspect-ratio: 268.3 / 442.4;
  }

  .collage-item--travel {
    left: 0%;
    top: 0%;
    width: 29.5%;
    height: 26.8%;
  }

  .collage-item--dog {
    left: 60.8%;
    top: 8%;
    width: 31.3%;
    height: 25.3%;
  }

  .collage-item--sunset {
    left: 58.5%;
    top: 66.5%;
    width: 41.5%;
    height: 33.5%;
  }

  .collage-item--pottery {
    left: 9.7%;
    top: 69.8%;
    width: 25%;
    height: 26.8%;
  }

  .collage-heading {
    left: 4.8%;
    top: 39.2%;
    width: 60%;
    gap: 10px;
  }

  .collage-heading__title {
    font-size: clamp(18px, 6.2vw, 24px);
  }

  .collage-tag {
    font-size: clamp(16px, 5.4vw, 22px);
  }

  .collage-quote {
    display: none;
  }

  /* ---- work ---- */

  /* Collapse the pinned "scrolly" stage to a normal, non-pinned block:
     folders row first, then the active panel below — no scroll-jacking
     on small screens, click-to-select only (see js/main.js). */
  .work__scroll {
    height: auto;
  }

  .work__stage {
    position: static;
    height: auto;
    padding: 0;
  }

  .work__inner {
    flex-direction: column;
    gap: 24px;
  }

  /* Panel above folders on mobile (item 1): folders sit lower on the
     screen, closer to where a thumb naturally rests. DOM order stays
     folders-then-panels (desktop layout + js/main.js indexing depend on
     it) — this is a purely visual reorder. */
  .work__panels {
    order: 1;
  }

  .work__folders {
    order: 2;
  }

  /* All 4 folders share the row width equally (instead of sizing to
     their own content and overflowing into a horizontal scroller), so
     every project is visible without scrolling; names wrap to 2 lines
     rather than truncating. */
  .work__folders {
    flex: 0 0 auto;
    flex-direction: row;
    gap: 10px;
    overflow-x: visible;
    padding-bottom: 0;
  }

  .work-folder {
    flex: 1 1 0;
    min-width: 0;
    /* Tighter than the desktop 12px — less vertical space wasted
       between the icon and the name on the narrow mobile row. */
    gap: 6px;
  }

  .work-folder__icon {
    max-width: 100%;
  }

  .work-folder__name {
    width: 100%;
    font-size: 10.5px;
    line-height: 1.25;
  }

  /* Shorter card overall: a smaller preview image plus smaller text
     throughout, rather than just trimming the outer padding. */
  .work-panel {
    min-height: 0;
  }

  .work-panel__image {
    flex-basis: 150px;
    height: 150px;
  }

  .work-panel__image--placeholder {
    font-size: 12px;
  }

  .work-panel__body {
    padding: 16px 16px 20px;
    gap: 6px;
  }

  .work-panel__title {
    font-size: 17px;
  }

  .work-panel__meta {
    /* Stack label above value for every field, instead of the desktop
       side-by-side columns. */
    grid-template-columns: 1fr;
    row-gap: 4px;
    margin-top: 4px;
  }

  .work-panel__meta dd {
    /* Extra breathing room before the next field's label. */
    margin-bottom: 6px;
  }

  .work-panel__meta dd:last-of-type {
    margin-bottom: 0;
  }

  .work-panel__meta dt {
    font-size: 13px;
  }

  .work-panel__meta dd {
    font-size: 13.5px;
    line-height: 1.4;
  }

  .work-panel__cta {
    margin-top: 10px;
    /* Match .hero__pitch .btn--cta's mobile size exactly. */
    height: 28px;
    padding: 0 18px;
    font-size: 16px;
    border-radius: 7px;
  }

  .testimonials {
    margin-top: 56px;
  }

  .testimonials__heading {
    max-width: 100%;
    text-align: center;
    font-size: 24px;
    margin: 0 auto;
  }

  .testimonials-marquee {
    margin-top: 24px;
    padding: 8px 0 24px;
  }

  .testimonial-card {
    --card-w: min(300px, 84vw);
    --card-h: 200px;
    padding: 18px 19px;
  }

  .testimonial-card__quote {
    font-size: 14px;
    -webkit-line-clamp: 7;
  }

  .testimonial-card__meta {
    font-size: 12px;
  }

  .logo-parade__heading {
    max-width: 100%;
    text-align: center;
    font-size: 24px;
    margin: 0 auto;
  }

  .logo-parade__marquee {
    margin-top: 24px;
  }

  .logo-parade__logo-img {
    height: 48px;
  }

  .logo-parade__group li {
    margin-right: 20px;
  }

  .section-title {
    flex-wrap: wrap;
    gap: 16px;
  }

  /* ---- contact ---- */

  .contact__card {
    flex-direction: column;
    text-align: center;
    align-items: center;
  }

  .contact__icons {
    justify-content: center;
  }

  /* ---- project detail page ---- */

  .project {
    padding-top: 128px;
  }

  .project-hero-image {
    margin-top: 16px;
    border-radius: 12px;
  }

  .project-title {
    font-size: 26px;
  }

  .project-kicker {
    font-size: 13px;
  }

  /* Tighter padding/gap/item size so two screenshots fit per row
     (matching the Figma mobile reference) instead of one wide column
     per row. */
  .problem-screens {
    padding: 18px 16px;
    gap: 16px 14px;
  }

  .problem-screens__item {
    flex: 1 1 120px;
    max-width: 140px;
  }

  .problem-screens__caption {
    font-size: 11px;
  }

  /* Mobile research board: stack all three images in one column
     (matching the separate mobile Figma frame) instead of a wide
     image plus a two-up row, and pad symmetrically since nothing is
     deliberately cropped flush against an edge here. */
  .research-board {
    padding: 20px 6%;
  }

  .research-board__row {
    flex-direction: column;
    align-items: center;
  }

  .research-board__phones {
    flex: none;
    width: 92%;
  }

  .research-board__table {
    flex: none;
    width: 64%;
  }

  /* Mobile decision screens: stack group A above group B instead of
     a side-by-side row, matching the separate mobile Figma frame. */
  .decision-screens {
    padding: 20px;
  }

  .decision-screens__row {
    flex-direction: column;
    gap: 24px;
  }

  .decision-screens__group--a,
  .decision-screens__group--b {
    flex: none;
    width: 100%;
  }

  /* Tighter padding/gap/item size so two clips fit per row (matching
     .problem-screens' mobile treatment) instead of one per row. */
  .video-screens {
    padding: 18px 16px;
    gap: 16px 14px;
  }

  .video-screens__item {
    flex: 1 1 120px;
    max-width: 140px;
  }

  .validation-board__image--desktop {
    display: none;
  }

  .validation-board__image--mobile {
    display: block;
  }

  .project-section {
    margin-top: 44px;
  }

  .project-section h2 {
    font-size: 24px;
  }

  /* ---- see other projects: swipeable horizontal carousel ---- */

  .other-projects {
    gap: 16px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    /* Hide the scrollbar — swiping is the intended interaction, the
       bar itself is just visual noise on a touch carousel. */
    scrollbar-width: none;
    -ms-overflow-style: none;
    /* The clip region for overflow-x: auto is this element's own box,
       so each card's shadow needs room inside it on every edge or it
       gets hard-cropped. Padding here adds that room; the matching
       negative margin cancels it back out so the row still lines up
       with the section's edges and the "see other projects" title. */
    padding: 10px 12px 18px;
    margin: -10px -12px -18px;
    /* Without this, scroll-snap silently scrolls the padding away on
       load/settle (it aligns the container's own padding edge, not its
       border edge, with the snapped card) — this keeps scrollLeft: 0
       actually showing the left padding instead of eating it. */
    scroll-padding-left: 12px;
    scroll-padding-right: 12px;
  }

  .other-projects::-webkit-scrollbar {
    display: none;
  }

  .other-project {
    flex: 0 0 82%;
    scroll-snap-align: start;
    /* Slightly smaller than --panel-shadow so the padding above can
       fully contain it without needing to grow further — same soft,
       dark drop shadow used everywhere else, just tighter. */
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.14);
  }

  .other-project__image {
    height: 150px;
  }

  .other-project__body {
    padding: 16px 16px 20px;
  }

  .other-project__title {
    font-size: 16px;
  }

  .other-project__meta {
    grid-template-columns: 68px 1fr;
    row-gap: 6px;
  }

  .other-project__meta dt {
    font-size: 12px;
  }

  .other-project__meta dd {
    font-size: 12.5px;
  }

  .other-project__cta {
    /* Match .work-panel__cta / .hero__pitch .btn--cta's mobile size. */
    height: 28px;
    padding: 0 18px;
    font-size: 16px;
    border-radius: 7px;
  }
}


/* ==========================================================================
   Image lightbox — click-to-zoom viewer
   Shared modal (markup at the end of the project page's <body>) for the
   case-study hero image and the screenshots inside .problem-screens,
   .research-board and .decision-screens. Any element tagged
   .js-zoomable opens it (see js/main.js); hidden by default, JS toggles
   .lightbox.is-open. Sits above the nav (z-index 100) and the mobile
   menu (z-index 99).
   ========================================================================== */

.js-zoomable {
  cursor: zoom-in;
  transition: opacity 0.15s ease;
}

.js-zoomable:hover {
  opacity: 0.9;
}

.js-zoomable:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 3px;
}

body.lightbox-open {
  overflow: hidden;
}

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.92);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.2s ease, visibility 0.2s ease;
}

.lightbox.is-open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}

.lightbox__viewport {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  padding: clamp(24px, 6vw, 64px);
}

.lightbox__media {
  display: block;
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  border-radius: 6px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
  cursor: zoom-in;
  touch-action: none;
  transition: transform 0.2s ease;
  user-select: none;
  -webkit-user-drag: none;
}

/* The image and video elements share the sizing/zoom rules above via
   .lightbox__media; only one is ever shown at a time (see js/main.js),
   the other sits at display:none until it becomes the active one. */
#lightboxImage.is-hidden,
#lightboxVideo.is-hidden {
  display: none;
}

/* Native video controls don't make sense mid-zoom-drag, and a video
   naturally invites a click-to-play tap rather than a click-to-zoom
   one, so the click-to-zoom shortcut is skipped for video (wheel/pinch
   zoom and drag-to-pan still work — see js/main.js). */
#lightboxVideo {
  cursor: default;
}

/* Once zoomed in, the transition is dropped so drag-to-pan and pinch
   tracking feel 1:1 with the pointer instead of lagging behind it. */
.lightbox__viewport.is-zoomed .lightbox__media {
  cursor: grab;
  transition: none;
}

.lightbox__viewport.is-zoomed #lightboxVideo {
  cursor: default;
}

.lightbox__media.is-panning {
  cursor: grabbing;
}

.lightbox__close {
  position: absolute;
  top: clamp(16px, 3vw, 28px);
  right: clamp(16px, 3vw, 28px);
  z-index: 1;
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
  font-size: 26px;
  line-height: 1;
  cursor: pointer;
  transition: background 0.15s ease;
}

.lightbox__close:hover,
.lightbox__close:focus-visible {
  background: rgba(255, 255, 255, 0.24);
  outline: none;
}


/* Image placeholder for case studies whose assets aren't in yet.
   Swap each one for the real <img>/<video> block when images arrive. */
.project-image-placeholder {
  margin-top: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: var(--panel-radius);
  box-shadow: var(--panel-shadow);
  background: linear-gradient(135deg, #1a1a1a, #2b2b2b);
  color: rgba(255, 255, 255, 0.35);
  font-size: 14px;
  letter-spacing: 0.02em;
}

.project-image-placeholder--hero {
  max-width: 800px;
  margin: 24px auto 0;
}

/* Inline text link inside case-study copy (e.g. cross-links between projects). */
.project-inline-link {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}

.project-inline-link:hover,
.project-inline-link:focus-visible {
  text-decoration-thickness: 2px;
}

/* Case-study figures (single image or a pair). Rounded card like the
   hero; click-to-zoom via .js-zoomable. Pairs sit side by side on
   desktop and stack on mobile. */
.project-figure {
  margin-top: 28px;
  display: grid;
  align-items: start;
  gap: clamp(16px, 3vw, 24px);
}

.project-figure--pair {
  grid-template-columns: 1fr 1fr;
}

.project-figure__image {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--panel-radius);
  box-shadow: var(--panel-shadow);
}

@media (max-width: 900px) {
  .project-figure--pair {
    grid-template-columns: 1fr;
  }
}

/* ---- Cookie consent banner (injected by js/main.js) ---- */
.cookie-banner {
  position: fixed;
  left: 50%;
  bottom: 20px;
  transform: translateX(-50%);
  z-index: 200; /* above nav (100), below lightbox (300) */
  width: calc(100% - 32px);
  max-width: 640px;
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 16px 20px;
  background: rgba(255, 255, 255, 0.85);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid rgba(255, 255, 255, 0.6);
  border-radius: 18px;
  box-shadow: 0 8px 40px rgba(0, 0, 0, 0.15);
}

.cookie-banner[hidden] {
  display: none;
}

.cookie-banner__text {
  flex: 1 1 auto;
  font-size: 13px;
  line-height: 1.45;
  color: var(--color-text-soft);
}

.cookie-banner__actions {
  display: flex;
  gap: 8px;
  flex: 0 0 auto;
}

.cookie-banner__btn {
  height: 36px;
  padding: 0 18px;
  border-radius: 10px;
  border: none;
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}

.cookie-banner__btn--primary {
  background: var(--color-cta);
  color: #ffffff;
}

.cookie-banner__btn--secondary {
  background: rgba(0, 0, 0, 0.06);
  color: var(--color-text);
}

.cookie-banner__btn:hover,
.cookie-banner__btn:focus-visible {
  filter: brightness(0.95);
}

.cookie-settings {
  display: flex;
  justify-content: center;
  padding: 0 0 32px;
}

.cookie-settings__link {
  background: none;
  border: none;
  padding: 4px 8px;
  font: inherit;
  font-size: 12px;
  color: var(--color-text-faint);
  cursor: pointer;
}

.cookie-settings__link:hover,
.cookie-settings__link:focus-visible {
  color: var(--color-text);
  text-decoration: underline;
}

@media (max-width: 600px) {
  .cookie-banner {
    flex-direction: column;
    align-items: stretch;
    gap: 12px;
    bottom: 16px;
  }

  .cookie-banner__actions .cookie-banner__btn {
    flex: 1 1 0;
  }
}

/* ---- Page-load animation ----
   Subtle fade + lift on every page load/reload so it's clear the page
   refreshed. Only opacity on the fixed nav (a transform there would
   break position: fixed), and fill-mode "backwards" so no transform is
   left on <main> once it finishes (keeps sticky/fixed children working). */
@keyframes page-enter {
  from {
    opacity: 0;
    transform: translateY(12px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

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

main {
  animation: page-enter 0.6s cubic-bezier(0.22, 1, 0.36, 1) backwards;
}

.nav {
  animation: page-fade 0.5s ease-out backwards;
}

@media (prefers-reduced-motion: reduce) {
  main,
  .nav {
    animation: none;
  }
}

/* ---- Swipeable marquees (testimonials + logo parade) ----
   js/main.js takes over the auto-scroll from the CSS animation (adds
   .is-js-marquee) so the strips can also be dragged/swiped, then glide
   back into their auto-scroll after a few idle seconds. pan-y keeps
   vertical page scrolling native on touch devices. */
.is-js-marquee {
  touch-action: pan-y;
  cursor: grab;
  -webkit-user-select: none;
  user-select: none;
}

.is-js-marquee.is-dragging {
  cursor: grabbing;
}

.is-js-marquee [data-marquee-track] {
  animation: none;
  will-change: transform;
}

.is-js-marquee img {
  -webkit-user-drag: none;
  pointer-events: none;
}

/* Reduced motion: no auto-scroll; the strip is a plain swipeable row. */
@media (prefers-reduced-motion: reduce) {
  [data-marquee] {
    overflow-x: auto;
    scrollbar-width: none;
  }
  [data-marquee]::-webkit-scrollbar {
    display: none;
  }
}
