@import url('https://fonts.googleapis.com/css2?family=Archivo:wght@400;500;600;700&display=swap');

:root {
  --blue: #1665ff;
  --off-white: #f3f1f1;
  --black: #282828;
  --white: #ffffff;
  --accent: #eba702;
  --ink: #282828;
  --muted: rgba(40, 40, 40, 0.55);
  --muted-on-dark: rgba(255, 255, 255, 0.55);
  --border-on-dark: rgba(255, 255, 255, 0.16);
  --border-on-light: rgba(40, 40, 40, 0.14);

  --font: 'Archivo', 'Helvetica Neue', Arial, sans-serif;

  --nav-h: 76px;
  --container: 1440px;
  --edge: clamp(20px, 4vw, 56px);
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }

body {
  margin: 0;
  font-family: var(--font);
  background: var(--off-white);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
ul { list-style: none; margin: 0; padding: 0; }

.wrap {
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 var(--edge);
}

/* ---------- Nav ---------- */
/* Recreates paulacarbonell.de's navbar1_navlink: uppercase links that
   fade to 50% opacity and shift to brand blue on hover — no underline. */
.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 2rem 2rem 0;
  color: var(--white);
  mix-blend-mode: difference;
}

.nav__brand {
  display: flex;
  align-items: baseline;
  gap: clamp(10px, 1.6vw, 20px);
}

.nav__logo {
  font-weight: 700;
  font-size: 15px;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  transition: opacity 0.2s ease, color 0.2s ease;
}

.nav__logo:hover {
  opacity: 0.5;
  color: var(--blue);
}

.nav__tagline {
  font-weight: 400;
  font-size: 13px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  opacity: 0.85;
}

.nav__links {
  display: flex;
  gap: clamp(18px, 3vw, 40px);
}

.nav__links a {
  font-size: 12px;
  font-weight: 400;
  text-transform: uppercase;
  transition: opacity 0.2s ease, color 0.2s ease;
}

.nav__links a:hover,
.nav__links a[aria-current='page'] {
  opacity: 0.5;
  color: var(--blue);
}

/* Below this, the brand group's logo + tagline and the three nav links
   no longer fit on one row — the tagline (the widest, least essential
   piece) was wrapping onto four lines and pushing "Projects" off the
   right edge of the viewport, out of reach. Drop the tagline and tighten
   everything else instead of wrapping the row. */
@media (max-width: 640px) {
  .nav {
    padding: 1.25rem 1.25rem 0;
  }
  .nav__tagline {
    display: none;
  }
  .nav__logo {
    font-size: 13px;
  }
  .nav__links {
    gap: 14px;
  }
  .nav__links a {
    font-size: 11px;
  }
}

/* ---------- Home: solely the carousel ---------- */
.playbook {
  position: relative;
  height: 100svh;
  background: var(--off-white);
  color: var(--blue);
  display: flex;
  align-items: center;
  overflow: hidden;
}

.playbook__overlay {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 20;
  text-align: center;
  pointer-events: none;
  color: var(--white);
  mix-blend-mode: difference;
}

.playbook__overlay .line1 {
  font-size: clamp(14px, 1.6vw, 18px);
  font-weight: 500;
  margin: 0 0 6px;
}

.playbook__overlay .line2 {
  font-size: clamp(38px, 7vw, 96px);
  font-weight: 700;
  line-height: 1;
  margin: 0;
  letter-spacing: -0.01em;
  white-space: nowrap;
}

.carousel {
  width: 100%;
  overflow: hidden;
  cursor: grab;
  user-select: none;
}

.carousel:active { cursor: grabbing; }

.carousel__track {
  display: flex;
  align-items: center;
  gap: clamp(16px, 2vw, 28px);
  padding: 0 var(--edge);
  will-change: transform;
  --tilt: 0deg;
}

.carousel__card {
  position: relative;
  flex: 0 0 auto;
  width: clamp(200px, 20vw, 300px);
  display: block;
  perspective: 2400px;
}

/* Vary each card's height in a repeating rhythm, like the reference.
   Keyed to a per-card class (assigned by generate.py from each project's
   own position, not nth-child) so the same project keeps the same shape
   in every repeated copy of the infinite-loop track. */
.carousel__card.ratio-1 { aspect-ratio: 2 / 3; }
.carousel__card.ratio-2 { aspect-ratio: 1 / 1; }
.carousel__card.ratio-3 { aspect-ratio: 3 / 4.6; }
.carousel__card.ratio-4 { aspect-ratio: 4 / 5; }
.carousel__card.ratio-5 { aspect-ratio: 3 / 4; }

/* Delivery Hero Austria Brand Refresh gets its own square shape instead
   of sharing ratio-5 (which Rice 'N' Rise already uses), so that card
   alone changes without affecting anything else in the rhythm. */
.carousel__card.ratio-square { aspect-ratio: 1 / 1; }

/* The About card sits at a fixed spot in every lap (always right after
   the 10th project), so it needs its own shape rather than sharing the
   1-5 rhythm — a bit taller than ratio-1 (3/4), which the first project
   also uses, so the two don't read as identical next to each other.
   Also sized a touch smaller than every other card. */
.carousel__card.ratio-about {
  width: clamp(180px, 18vw, 270px);
  aspect-ratio: 3 / 4.4;
}

.carousel__frame {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: var(--black);
  transform: rotateY(var(--tilt));
}

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

/* On hover, an opaque mask rises from the bottom to visually shrink the
   frame — the image element itself never resizes, moves, or rescales.
   Hover-in: mask and caption both start immediately with no delay, so
   the title appears as the mask rises instead of after a blank pause.
   Hover-out keeps a short mask delay so the caption (fading out fast,
   no delay) clears before the mask retracts — without it, the caption
   briefly outlived the mask and sat on bare image for a few frames. */
.carousel__mask {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 0;
  background: var(--off-white);
  transition: height 0.2s ease 0.08s;
}

.carousel__card:hover .carousel__mask {
  height: 46px;
  transition: height 0.25s ease-out;
}

.carousel__caption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 46px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 0.15s ease, transform 0.15s ease;
  pointer-events: none;
}

.carousel__card:hover .carousel__caption {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 0.2s ease, transform 0.2s ease;
}

.carousel__caption h3 {
  font-size: 14px;
  font-weight: 600;
  margin: 0;
  color: var(--blue);
}

.carousel__caption span {
  font-size: 11px;
  color: rgba(22, 101, 255, 0.7);
}

/* ---------- Section heading shared ---------- */
.section-head {
  padding: clamp(70px, 9vw, 120px) 0 clamp(30px, 4vw, 50px);
}

.section-head h2 {
  font-size: clamp(30px, 4.5vw, 56px);
  margin: 0;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.section-head__label {
  font-size: 13px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
  margin: 0 0 14px;
}

/* ---------- About ---------- */
/* ---------- Projects grid ---------- */
.projects-grid {
  padding-bottom: clamp(60px, 8vw, 100px);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(20px, 2.4vw, 32px);
}

.project-card__frame {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  background: var(--border-on-light);
}

.project-card__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.3s ease-out;
}

.project-card:hover .project-card__frame img {
  transform: scale(1.05);
}

.project-card__index {
  position: absolute;
  top: 14px;
  left: 14px;
  font-size: 13px;
  color: var(--white);
  background: var(--ink);
  padding: 3px 8px;
}

.project-card__meta {
  padding-top: 14px;
  display: flex;
  justify-content: space-between;
  gap: 10px;
}

.project-card__meta h3 {
  font-size: 17px;
  font-weight: 600;
  margin: 0;
  text-transform: uppercase;
  color: var(--blue);
}

.project-card__meta span {
  font-size: 12px;
  color: var(--ink);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  white-space: nowrap;
}

/* ---------- Project detail ---------- */
.project-hero {
  padding-top: calc(var(--nav-h) + clamp(30px, 4vw, 50px));
}

.project-hero__label {
  font-size: 13px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--blue);
  margin: 0 0 16px;
}

.project-hero__title {
  font-size: clamp(34px, 6vw, 84px);
  font-weight: 700;
  margin: 0 0 28px;
  letter-spacing: -0.01em;
  max-width: 70%;
  color: var(--ink);
}

.project-meta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 24px 48px;
  border-top: 1px solid var(--blue);
  padding: 22px 0;
  margin-bottom: clamp(30px, 5vw, 56px);
}

.project-meta-row div span:first-child {
  display: block;
  font-size: 12px;
  color: var(--blue);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 6px;
}

.project-meta-row div span:last-child {
  font-size: 15px;
  font-weight: 600;
  text-transform: uppercase;
}

.project-cover {
  width: 100%;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: var(--border-on-light);
  margin-bottom: clamp(40px, 6vw, 70px);
}

.project-cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.project-body {
  display: grid;
  grid-template-columns: 1fr 1.4fr;
  gap: clamp(30px, 5vw, 80px);
  padding-bottom: clamp(50px, 7vw, 90px);
}

.project-body p {
  font-size: 16px;
  line-height: 1.75;
  color: var(--muted);
  margin: 0 0 22px;
}

.project-body .project-body__excerpt {
  color: var(--blue);
  font-weight: 700;
}

.project-body .project-body__description p {
  color: var(--ink);
}

.project-extra {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(24px, 4vw, 48px);
  border-top: 1px solid var(--blue);
  padding: clamp(30px, 5vw, 56px) 0;
  margin-bottom: clamp(50px, 7vw, 90px);
}

.project-extra--single {
  grid-template-columns: 1fr;
  padding-bottom: 0;
}

.project-extra--pair {
  grid-template-columns: repeat(2, 1fr);
  border-top: none;
  padding-top: 0;
  padding-bottom: 0;
}

/* This section also carries .wrap for centering/max-width, but
   .project-extra's own padding shorthand (padding: ... 0) zeroes out
   the horizontal sides on the same element — same class of bug as the
   gallery image that ended up with grey bars either side of it.
   Reinstating .wrap's own left/right padding formula here keeps Role
   flush with the image column above it. */
.project-extra--facts {
  grid-template-columns: repeat(2, 1fr);
  border-top: none;
  padding-top: 0;
  padding-bottom: 0;
  padding-left: var(--edge);
  padding-right: var(--edge);
}

.project-extra__block h3 {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--blue);
  margin: 0 0 10px;
  font-weight: 600;
}

.project-extra__block p {
  font-size: 15px;
  line-height: 1.6;
  color: var(--ink);
  margin: 0;
}

.project-gallery-single {
  width: 100%;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: var(--border-on-light);
  margin-bottom: clamp(50px, 7vw, 90px);
}

.project-gallery-single img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Optional video slot right after the first gallery image. Framed like
   .project-gallery img below (aspect-ratio + object-fit + background set
   on the media element itself) rather than like the single hero blocks
   above (aspect-ratio on an overflow:hidden wrapper) — video/iframe get
   the same crop treatment as the rest of the gallery's images. Only the
   margin-bottom carries over from the single blocks, to keep this row's
   vertical rhythm matched to its immediate neighbors above and below. */
.project-video {
  width: 100%;
  margin-bottom: clamp(50px, 7vw, 90px);
}

.project-video video,
.project-video iframe {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  background: var(--border-on-light);
  border: 0;
}

/* Sits between the second gallery image and the rest of the gallery,
   same single-column width as the images around it. Text styling is
   shared with .projectb-intro__excerpt (the project description) via
   that class on the element itself, not duplicated here — only the
   spacing differs, so this needs the extra class in the selector to
   outrank .projectb-intro__excerpt's own margin regardless of source
   order (both are single-class selectors, otherwise a tie). */
.project-highlight.projectb-intro__excerpt {
  margin-bottom: clamp(50px, 7vw, 90px);
}

.project-gallery {
  display: grid;
  gap: clamp(16px, 2vw, 28px);
  padding-bottom: clamp(60px, 8vw, 100px);
}

.project-gallery img {
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  background: var(--border-on-light);
}

/* Same grid rhythm and edge padding as the rest of the project page's
   content sections (e.g. .project-extra), rather than a section-specific
   layout — this also carries plain .wrap, so its padding and the
   border-top's width already match everything above it. */
.project-nav {
  border-top: 1px solid var(--blue);
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(24px, 4vw, 48px);
}

.project-nav__case {
  padding: clamp(30px, 5vw, 56px) 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;
}

.project-nav__case:last-child {
  text-align: right;
  align-items: flex-end;
}

/* Two stacked images per case: the base cover sits still, and the
   project's own first gallery shot slides up from the bottom to cover
   it on hover — same "next case" reveal used on paulacarbonell.de. */
.project-nav__frame {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: var(--border-on-light);
}

.project-nav__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.5s ease;
}

.project-nav__img--hover {
  transform: translateY(100%);
}

.project-nav__case:hover .project-nav__img--hover {
  transform: translateY(0);
}

/* The label doubles as the case's headline (the project's own name has
   been dropped from this section), sized like the old title text, and
   carries the same left-to-right underline reveal on hover that the
   About page's Experience/Projects/Contact links use — no arrow icon,
   the underline alone signals it's a link. */
.project-nav .label {
  position: relative;
  display: inline-block;
  font-size: clamp(20px, 3vw, 34px);
  font-weight: 700;
  color: var(--ink);
}

.project-nav .label::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -4px;
  height: 2px;
  background: var(--ink);
  transform-origin: left;
  transform: scaleX(0);
  transition: transform 0.3s ease;
}

.project-nav__case:hover .label::after {
  transform: scaleX(1);
}

/* ---------- Project template B ---------- */
/* Cover image sits corner-to-corner above the title/excerpt block,
   like andwalsh.com/work/advertising/rad-future — <main> here carries
   no .wrap of its own, so this image is the only full-bleed element;
   the intro and nav sections opt back into .wrap individually. */
.projectb-cover {
  width: 100%;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: var(--border-on-light);
}

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

/* A Vimeo/Drive hero (an iframe, not a video element) can't be object-fit
   cropped — oversize it and let the wrapper's overflow:hidden crop it,
   same visual result as object-fit: cover on the other two media types. */
.projectb-cover--video iframe {
  width: 100%;
  height: 100%;
  min-height: 100%;
  border: 0;
  display: block;
  transform: scale(1.01);
}

.projectb-intro {
  padding-top: clamp(40px, 6vw, 70px);
  padding-bottom: clamp(50px, 7vw, 90px);
}

/* Title and excerpt sit side by side as two columns, like
   andwalsh.com/work/advertising/rad-future — title takes the narrower
   left column, excerpt + the info toggle take the wider right one. */
.projectb-intro__row {
  display: grid;
  grid-template-columns: 1fr 2fr;
  gap: clamp(24px, 4vw, 64px);
  align-items: start;
}

.projectb-intro__title {
  font-size: clamp(24px, 2.8vw, 46px);
  font-weight: 700;
  margin: 0;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.projectb-intro__excerpt {
  font-size: clamp(16px, 1.8vw, 26px);
  line-height: 1.5;
  font-weight: 400;
  color: var(--blue);
  margin: 0 0 24px;
}

/* "+ SEE MORE" toggle — small, bold and underlined, not a prominent
   button; the reveal itself does the work. It hides once expanded (see
   .is-hidden, toggled in JS) — closing happens via the separate
   "CLOSE EXPANDED VIEW" button at the bottom of the revealed content. */
.projectb-more-toggle {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: none;
  border: none;
  padding: 0 0 9px;
  font: inherit;
  cursor: pointer;
}

.projectb-more-toggle.is-hidden {
  display: none;
}

/* Both buttons share one glyph (the close button's own ✕) instead of
   two different characters ("+" and "✕") that render with slightly
   different weight/proportions — rotating it 45° turns the X into a
   plus, so the two states are geometrically the exact same mark. Sized
   up to be legible as an actual icon rather than a stray character. */
/* Drawn in CSS rather than relying on two text characters ("+" and "✕")
   or rotating one glyph — a font's × and its own bounding box don't
   necessarily occupy their em-square the same way once rotated, so a
   rotated ✕ kept reading as a slightly smaller +. Two identical bars,
   rotated as a pair, guarantees the two states are pixel-identical. */
.projectb-more-icon {
  position: relative;
  display: inline-block;
  width: 12px;
  height: 12px;
  color: var(--ink);
  transition: color 0.2s ease;
}

/* Nudged to sit better against the "SEE MORE" text baseline — scoped to
   the toggle button specifically, not the close button's rotated ✕. */
.projectb-more-toggle .projectb-more-icon {
  top: -1px;
}

.projectb-more-icon::before,
.projectb-more-icon::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  background: currentColor;
}

.projectb-more-icon::before {
  width: 12px;
  height: 1.5px;
  transform: translate(-50%, -50%);
}

.projectb-more-icon::after {
  width: 1.5px;
  height: 12px;
  transform: translate(-50%, -50%);
}

.projectb-more-icon--x {
  transform: rotate(45deg);
}

.projectb-more-toggle__text {
  font-size: clamp(12px, 1.1vw, 14px);
  font-weight: 700;
  text-transform: uppercase;
  color: var(--ink);
  transition: color 0.2s ease;
}

.projectb-more-toggle:hover .projectb-more-toggle__text,
.projectb-more-toggle:hover .projectb-more-icon {
  color: var(--blue);
}

/* The underline is a permanent solid bar, not a hover-reveal — it sits
   on the button itself (not just the text span) so it runs under the
   full width of the control, crosses included. Two stacked bars: a
   black one that's always there, and a blue one on top of it that
   grows from left to right on hover — so the colour itself appears to
   sweep left-to-right from black to blue, rather than swapping in one
   flat step or blending as a static gradient. */
.projectb-more-toggle::before,
.projectb-more-close::before,
.projectb-more-toggle::after,
.projectb-more-close::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1.5px;
}

.projectb-more-toggle::before,
.projectb-more-close::before {
  background: var(--ink);
}

.projectb-more-toggle::after,
.projectb-more-close::after {
  background: var(--blue);
  transform-origin: left;
  transform: scaleX(0);
  transition: transform 0.3s ease;
}

.projectb-more-toggle:hover::after,
.projectb-more-close:hover::after {
  transform: scaleX(1);
}

.projectb-more {
  max-height: 0;
  overflow: hidden;
  opacity: 0;
  margin-top: 0;
  transition: max-height 0.5s ease, opacity 0.4s ease, margin-top 0.5s ease;
}

.projectb-more.is-open {
  opacity: 1;
  margin-top: 40px;
}

/* "CLOSE EXPANDED VIEW" — sits as the last thing inside the revealed
   content, so it's clipped along with everything else while collapsed
   and only becomes visible once the content is actually open. */
.projectb-more-close {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: none;
  border: none;
  padding: 0 0 9px;
  margin-top: 32px;
  font: inherit;
  cursor: pointer;
  color: var(--ink);
  transition: color 0.2s ease;
}

.projectb-more-close:hover {
  color: var(--blue);
}

.projectb-more-close__text {
  font-size: clamp(12px, 1.1vw, 14px);
  font-weight: 700;
  text-transform: uppercase;
}

/* The revealed content is a single column: creative idea, deliverables
   and results (both as arrow bullets, all three sharing the excerpt's
   type style), then Client / Category / Year as single "Label: value"
   lines — each separated by its own divider line. */
.projectb-more__divider {
  height: 1px;
  background: var(--blue);
  margin: 24px 0;
}

.projectb-more__bullets {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.projectb-more__bullets-item {
  position: relative;
  padding-left: 44px;
  margin: 0;
}

.projectb-more__bullets-item::before {
  content: '↗';
  display: inline-block;
  position: absolute;
  left: 0;
  top: 0.02em;
  transform: rotate(45deg) scale(1.15);
  transform-origin: 50% 55%;
}

.projectb-more__line {
  font-size: 15px;
  font-weight: 600;
  line-height: 1.6;
  color: var(--blue);
  margin: 0;
}

/* Only the label ("CLIENT", "YEAR", "DURATION") is capitalised — the
   value after the colon stays exactly as written in the source data. */
.projectb-more__line-label {
  text-transform: uppercase;
}

/* ---------- Footer ---------- */
.footer {
  background: var(--blue);
  color: var(--white);
  padding: clamp(60px, 9vw, 120px) 0 clamp(30px, 4vw, 50px);
}

.footer__label {
  font-size: 13px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 20px;
}

.footer__title {
  font-size: clamp(32px, 7vw, 96px);
  text-transform: uppercase;
  font-weight: 700;
  margin: 0 0 40px;
  letter-spacing: -0.01em;
}

.footer__row {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: 20px;
  border-top: 1px solid var(--blue);
  padding-top: 24px;
  margin-top: clamp(30px, 5vw, 60px);
}

.footer__row span,
.footer__row a {
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: rgba(255, 255, 255, 0.75);
}

.footer__row a:hover { color: var(--white); }

/* ---------- About: recreates paulacarbonell.de's structure ---------- */
.about-hero {
  position: relative;
  height: 100svh;
  background: var(--blue);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

/* Image wrapper is exactly 25% of viewport width on desktop, matching
   paulacarbonell.de's .parallax2_image-width rule precisely, with the
   same 60vw / 70vw step-ups on tablet and mobile. */
.about-hero__frame {
  position: relative;
  width: 25vw;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  background: var(--black);
  will-change: transform;
}

.about-hero__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@media (max-width: 767px) {
  .about-hero__frame { width: 60vw; }
}

@media (max-width: 479px) {
  .about-hero__frame { width: 70vw; }
}

/* Statement + keyword line share one section and one type size, exactly
   as on the reference (both render from the same .h-blue / .heading-h1
   scale there) — plain wrapped text, no scrolling. */
.about-intro-text {
  background: var(--off-white);
  padding: 10vh 0;
}

.about-intro-text p {
  margin: 0;
  font-size: clamp(24px, 3.6vw, 44px);
  line-height: 1.15;
  letter-spacing: -0.01em;
}

.about-intro-text .statement {
  max-width: 30ch;
  font-weight: 500;
  color: var(--ink);
  margin-bottom: 2.2em;
}

.about-intro-text .keywords {
  font-weight: 500;
  color: var(--blue);
}

.about-grid {
  background: var(--off-white);
  border-top: 1px solid var(--blue);
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(24px, 4vw, 48px);
  padding: clamp(40px, 6vw, 70px) 0 clamp(70px, 10vw, 120px);
}

.about-grid__label {
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 20px;
}

.about-grid__entry {
  padding-bottom: 18px;
  margin-bottom: 18px;
  border-bottom: 1px solid var(--blue);
}

.about-grid__entry:last-child {
  border-bottom: none;
}

.about-grid__entry .role {
  display: block;
  font-size: 14px;
  font-weight: 700;
  text-transform: uppercase;
}

.about-grid__entry .years,
.about-grid__entry .org {
  display: block;
  font-size: 13px;
  color: var(--muted);
}

.about-grid__list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.about-grid__list a,
.about-grid__list span {
  font-size: 14px;
  padding: 9px 0;
  border-bottom: 1px solid var(--blue);
  text-transform: uppercase;
}

.about-grid__list a {
  transition: opacity 0.2s ease, color 0.2s ease;
}

.about-grid__list a:hover {
  opacity: 0.5;
  color: var(--blue);
}

.about-grid__sublabel {
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 32px 0 12px;
}

.about-grid__contact {
  font-size: 14px;
  text-transform: uppercase;
  margin: 0 0 6px;
}

.about-grid__contact a {
  transition: opacity 0.2s ease, color 0.2s ease;
}

.about-grid__contact a:hover {
  opacity: 0.5;
  color: var(--blue);
}

.about-footer {
  position: relative;
  z-index: 2;
  background: var(--off-white);
  border-top: 1px solid var(--blue);
  padding: clamp(24px, 3vw, 32px) 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 16px;
}

.about-footer div {
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  line-height: 1.6;
}

.about-footer strong {
  display: block;
  color: var(--ink);
  font-weight: 700;
}

.about-footer__social {
  display: flex;
  color: var(--ink);
  transition: opacity 0.2s ease, color 0.2s ease;
}

.about-footer__social svg {
  width: 18px;
  height: 18px;
}

.about-footer__social:hover {
  opacity: 1;
  color: var(--blue);
}

/* Sits behind the page and locks in place once reached — the footer
   (solid background, higher stacking) scrolls up and off first,
   uncovering this section, which then pins instead of continuing to
   scroll away, like paulacarbonell.de's closing reveal. */
.about-sneak {
  position: sticky;
  top: 0;
  z-index: 1;
  height: 100svh;
  background: var(--black);
  overflow: hidden;
}

.about-sneak__heading {
  position: absolute;
  top: clamp(90px, 12vw, 140px);
  left: 0;
  right: 0;
  z-index: 5;
  text-align: center;
  margin: 0;
  font-size: clamp(28px, 5vw, 56px);
  font-weight: 700;
  text-transform: uppercase;
  color: var(--white);
}

.about-sneak__slide {
  position: absolute;
  inset: 0;
  display: block;
  opacity: 0;
  pointer-events: none;
  transition: opacity 1s ease;
}

.about-sneak__slide.is-active {
  opacity: 1;
  pointer-events: auto;
}

.about-sneak__slide img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.75;
}

/* About's own Sneak Peek: heading left-aligned above a horizontal frame,
   matching paulacarbonell.de's fixed-height slider (full container width,
   500px tall) instead of a full-bleed background image. */
.aboutb-sneak {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: center;
  gap: clamp(24px, 4vw, 48px);
  padding-left: max(var(--edge), calc((100% - 1440px) / 2 + var(--edge)));
  padding-right: max(var(--edge), calc((100% - 1440px) / 2 + var(--edge)));
}

.aboutb-sneak .about-sneak__heading {
  position: static;
  text-align: left;
  font-size: 18px;
  font-weight: 400;
  letter-spacing: -0.01em;
}

.aboutb-sneak .about-sneak__slide img {
  opacity: 1;
}

.about-sneak__frame {
  position: relative;
  width: 100%;
  height: clamp(240px, 42vw, 500px);
  overflow: hidden;
}

/* ---------- About B: bureaunine.framer.website/about structure,
   rendered with the same tokens/typography as the About A page ---------- */
/* Full-bleed blue behind the statement + split sections; everything
   after this wrapper goes back to the site's normal off-white. */
.aboutb-blue {
  background: var(--blue);
  overflow: hidden;
}

.aboutb-blue__inner {
  will-change: transform;
}

.aboutb-hero {
  padding: calc(var(--nav-h) + 10vh) var(--edge) 4vh;
}

/* Font-size scale copied exactly from bureaunine's intro heading
   (32px / 28px / 22px across its own three breakpoints). */
.aboutb-hero p {
  margin: 0;
  max-width: 46ch;
  font-size: 32px;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: -0.04em;
  color: #282828;
}

@media (max-width: 1199px) {
  .aboutb-hero p { font-size: 28px; letter-spacing: -0.03em; }
}

@media (max-width: 809px) {
  .aboutb-hero p { font-size: 22px; letter-spacing: -0.02em; }
}

/* Image pinned to the left margin, parallax like the About A hero;
   placeholder copy fills the remaining space on the right. */
.aboutb-split {
  display: flex;
  align-items: flex-start;
  gap: clamp(32px, 7vw, 96px);
  padding: 4vh var(--edge) 10vh;
}

.aboutb-split__media {
  flex: 0 0 auto;
  width: 25vw;
}

/* Below this, the media column's own vw-based width plus the text
   column's minimum readable width no longer both fit on one row —
   stack them instead of letting the row overflow. */
@media (max-width: 767px) {
  .aboutb-split { flex-direction: column; }
  .aboutb-split__media { width: 60vw; }
}

@media (max-width: 479px) {
  .aboutb-split__media { width: 70vw; }
}

.aboutb-split__frame {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  background: var(--black);
  will-change: transform;
}

.aboutb-split__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.aboutb-split__text {
  flex: 1;
}

.aboutb-split__text p {
  margin: 0;
  max-width: 80%;
  font-size: clamp(14px, 1.2vw, 16px);
  line-height: 1.7;
  color: rgba(255, 255, 255, 0.85);
}

/* Services / Partners & Friends / Recognition, right under the split's
   placeholder copy — same list component as elsewhere, recolored for
   the blue background here. 3-column grid, no divider lines. */
.aboutb-split__lists {
  margin-top: 48px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px 32px;
}

.aboutb-split__lists .aboutb-lists__label {
  font-size: 12px;
  color: #282828;
  margin: 0 0 8px;
}

.aboutb-split__lists .aboutb-lists__label::before {
  content: '// ';
}

.aboutb-split__lists .aboutb-lists__list {
  gap: 2px;
}

.aboutb-split__lists .aboutb-lists__list span,
.aboutb-split__lists .aboutb-lists__list a {
  font-size: 13px;
  color: var(--white);
  border-bottom: none;
  padding: 3px 0;
}

.aboutb-split__lists .aboutb-lists__list a:hover {
  opacity: 0.6;
  color: var(--accent);
}

.aboutb-lists {
  position: relative;
  z-index: 2;
  background: var(--off-white);
  border-top: 1px solid var(--blue);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(24px, 4vw, 48px);
  /* Background stays full-bleed (no max-width on this element); this
     keeps the grid content visually centered within the same 1440px
     zone as the rest of the page once the viewport exceeds it. */
  padding: clamp(50px, 8vw, 90px) max(var(--edge), calc((100% - 1440px) / 2 + var(--edge)));
}

.aboutb-lists__label {
  font-size: 13px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 22px;
}

.aboutb-lists__list {
  display: flex;
  flex-direction: column;
}

.aboutb-lists__list span,
.aboutb-lists__list a {
  font-size: 16px;
  text-transform: uppercase;
  padding: 12px 0;
  border-bottom: 1px solid var(--blue);
}

.aboutb-lists__list a {
  transition: opacity 0.2s ease, color 0.2s ease;
}

.aboutb-lists__list a:hover {
  opacity: 0.5;
  color: var(--blue);
}

.aboutb-lists .about-grid__label::before,
.aboutb-lists .about-grid__sublabel::before {
  content: '// ';
}

/* Experience / Projects / Contact hover, matching paulacarbonell.de's
   own link-hover component (checked its live CSS directly): its
   .link-hover_underline sits inside a wrap clipped to the link's own
   text width and slides in from translateX(-101%) to 0 — a left-to-right
   wipe, not the vertical slide-up this used to do. The underline itself
   stays a fixed dark color — it does NOT follow the text to blue on the
   real site (.link-hover_underline uses a fixed --color--text, not
   currentColor), which was the earlier mismatch. */
.aboutb-lists .about-grid__list {
  align-items: flex-start;
}

.aboutb-lists .about-grid__list a {
  display: inline-block;
  width: fit-content;
}

/* The list's 9px top padding (kept for row spacing) would otherwise push
   the first project's text 9px below Experience/Contact's first line,
   which have no padding of their own — zero it out on just the first
   row so all three columns' opening lines land on the same baseline. */
.aboutb-lists .about-grid__list a:first-child {
  padding-top: 0;
}

.aboutb-lists .about-grid__list a,
.aboutb-lists .about-grid__contact a {
  position: relative;
}

/* No row dividers in the Projects column, matching the real site
   (its equivalent links carry no border at all). */
.aboutb-lists .about-grid__list a,
.aboutb-lists .about-grid__list span {
  border-bottom: none;
}

.aboutb-lists .about-grid__list a::after,
.aboutb-lists .about-grid__contact a::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: var(--ink);
  transform-origin: left;
  transform: scaleX(0);
  transition: transform 0.3s ease;
}

.aboutb-lists .about-grid__list a:hover,
.aboutb-lists .about-grid__contact a:hover {
  opacity: 1;
  color: var(--blue);
}

.aboutb-lists .about-grid__list a:hover::after,
.aboutb-lists .about-grid__contact a:hover::after {
  transform: scaleX(1);
}

/* About B's own Sneak Peek reveal — inspecting paulacarbonell.de
   directly showed its sticky black section isn't wrapped in an empty
   spacer; it's nested as the LAST child inside one big wrapper that
   also contains the Experience/Projects/Contact grid and the footer.
   That real content IS the scroll slack the sticky element needs, so
   there's never blank space — the grid/footer scroll away above it
   while it stays pinned to the bottom of the viewport. Scoped to
   About B only; About A keeps its original sticky-top behavior. */
.aboutb-sneak-wrap {
  position: relative;
}

.aboutb-sneak {
  top: auto;
  bottom: 0;
}

/* About B's footer: smaller and all-black, matching
   paulacarbonell.de exactly, with the closing "made with love" block
   right-aligned like the reference. Scoped so About A's footer (still
   using --muted grey) is untouched. */
/* Background stays full-bleed (the wrap class was removed from this
   element in the markup); this restores the same edge padding wrap
   used to provide, so the content position doesn't visibly shift. */
.aboutb-footer {
  padding-left: max(var(--edge), calc((100% - 1440px) / 2 + var(--edge)));
  padding-right: max(var(--edge), calc((100% - 1440px) / 2 + var(--edge)));
}

.aboutb-footer div {
  font-size: 11px;
  color: var(--ink);
}

/* A plain flex row with justify-content:space-between splits its free
   space into equal gaps — growing the "made with love" item to push it
   toward the social icon ate into that same shared pool, which dragged
   the contact block (Berlin/Barcelona/Everywhere) back out of its
   original position too. Grid tracks are independent: the two `1fr`
   gap-tracks reproduce the original spacing (contact block stays put),
   while the wider second one lets the right-aligned block's text still
   land next to the icon. */
.aboutb-footer {
  display: grid;
  grid-template-columns: auto 1fr auto 2.91fr auto;
  column-gap: 16px;
}

.aboutb-footer > *:nth-child(1) { grid-column: 1; }
.aboutb-footer > *:nth-child(2) { grid-column: 3; }
.aboutb-footer > *:nth-child(3) { grid-column: 4; }
.aboutb-footer > *:nth-child(4) { grid-column: 5; }

.aboutb-footer__right {
  text-align: right;
}

.aboutb-footer__link {
  position: relative;
  color: inherit;
  transition: color 0.2s ease;
}

.aboutb-footer__link::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -2px;
  height: 1px;
  background: var(--ink);
  transform-origin: left;
  transform: scaleX(0);
  transition: transform 0.3s ease;
}

.aboutb-footer__link:hover {
  color: var(--blue);
}

.aboutb-footer__link:hover::after {
  transform: scaleX(1);
}

/* Projects page: same sticky-reveal trick as About B's Sneak Peek —
   the black contact footer is normal flow content that provides scroll
   slack, while the blue CTA footer after it pins to the bottom of the
   viewport and gets progressively uncovered as that content scrolls
   away above it. Scoped to this wrapper so other pages' plain .footer
   (project detail pages) keep their default static placement. */
.projects-sneak-wrap {
  position: relative;
}

.projects-sneak-wrap .projects-footer-sticky {
  position: sticky;
  bottom: 0;
  z-index: 1;
}

/* The blue CTA's own copy is quieter now that the contact/copyright row
   moved into the plain footer above: "Curious?" borrows About B's intro
   paragraph type (same size/weight/letter-spacing scale as .aboutb-hero p,
   just in white for contrast on blue) instead of the small uppercase
   eyebrow label, and "Get in touch" drops the shouty bold-caps treatment
   for a normal-weight, normal-case heading. Scoped to this page's footer
   only — project detail pages keep the original label/title styling. */
.projects-footer-sticky .footer__label {
  max-width: 46ch;
  font-size: 32px;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: -0.04em;
  text-transform: none;
  color: var(--ink);
}

@media (max-width: 1199px) {
  .projects-footer-sticky .footer__label { font-size: 28px; letter-spacing: -0.03em; }
}

@media (max-width: 809px) {
  .projects-footer-sticky .footer__label { font-size: 22px; letter-spacing: -0.02em; }
}

.projects-footer-sticky .footer__title {
  text-transform: none;
  font-weight: 500;
  margin-bottom: 0;
}

/* Same left-to-right animated underline as the footer's contact link
   (.aboutb-footer__link), just white for the blue background here. */
.footer__title-link {
  position: relative;
  display: inline-block;
  color: inherit;
}

.footer__title-link::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -0.06em;
  height: 2px;
  background: var(--white);
  transform-origin: left;
  transform: scaleX(0);
  transition: transform 0.3s ease;
}

.footer__title-link:hover::after {
  transform: scaleX(1);
}

/* Matches .footer's top padding so the gap after "Get in touch" reads the
   same as the gap before "Curious?" above it — optically balanced now that
   there's no contact row weighing down the bottom anymore. */
.projects-footer-sticky {
  padding-bottom: clamp(60px, 9vw, 120px);
}

/* ---------- Responsive ---------- */
@media (max-width: 900px) {
  .projects-grid { grid-template-columns: repeat(2, 1fr); }
  .project-body { grid-template-columns: 1fr; }
  .project-extra { grid-template-columns: repeat(2, 1fr); }
  .project-gallery { grid-template-columns: 1fr; }
  .projectb-intro__row { grid-template-columns: 1fr; }
  .about-grid { grid-template-columns: repeat(2, 1fr); }
  .aboutb-lists { grid-template-columns: repeat(2, 1fr); }
  .aboutb-split__lists { grid-template-columns: repeat(2, 1fr); }
  .aboutb-footer {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
  }
  .aboutb-footer > *:nth-child(1),
  .aboutb-footer > *:nth-child(2),
  .aboutb-footer > *:nth-child(3),
  .aboutb-footer > *:nth-child(4) {
    grid-column: auto;
  }
  .aboutb-footer__right { text-align: left; }
}

@media (max-width: 560px) {
  .projects-grid { grid-template-columns: 1fr; }
  .project-nav { grid-template-columns: 1fr; }
  .project-nav__case:last-child { text-align: left; align-items: flex-start; }
  .about-grid { grid-template-columns: 1fr; }
  .aboutb-lists { grid-template-columns: 1fr; }
  .aboutb-split__lists { grid-template-columns: 1fr; }
}
