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

:root {
  --bg: #9f91cf;
  /* One source of truth for the *real* visible viewport height. body{overflow:hidden}
     means the page never scrolls, so the mobile URL bar never collapses and `100vh`
     resolves to the tall (bar-hidden) viewport while the visible area is the short
     one — clipping edge/bottom content (magnified ×3 by the 300vh world). `100dvh`
     tracks the visible area; main.ts refines it to visualViewport.height in px.
     dvh == vh on desktop, so desktop renders identically. */
  --app-h: 100dvh;
}
@supports not (height: 100dvh) {
  :root {
    --app-h: 100vh;
  }
}

html {
  font-family: 'Fredoka', system-ui, -apple-system, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

html,
body {
  height: 100%;
}

body {
  background: var(--bg);
  color: #fff;
  overflow: hidden;
}

/* ---------- Two-view slider (landing → portfolio) ---------- */

#app {
  position: relative;
  height: var(--app-h);
  overflow: hidden;
}

#slider {
  display: flex;
  flex-direction: column;
  height: calc(var(--app-h) * 2);
  will-change: transform;
}

#slider > * {
  flex: 0 0 var(--app-h);
}

/* Shared gentle float — used by both views. Animates the independent
   `translate` property so it composes with hover/entrance `transform`s.
   Per-element amplitude via --floatY. */
@keyframes float-y {
  0%, 100% { translate: 0 0; }
  50% { translate: 0 var(--floatY, -7px); }
}

/* ---------- Portfolio hub: 2D world of directional subpages ---------- */

.world-frame {
  position: relative;
  width: 100vw;
  height: var(--app-h);
  overflow: hidden;
}

.world {
  display: grid;
  grid-template-columns: repeat(3, 100vw);
  grid-template-rows: repeat(3, var(--app-h));
  width: 300vw;
  height: calc(var(--app-h) * 3);
  /* sits above .world-bg, the fixed dream-haze backdrop behind the whole world */
  position: relative;
  z-index: 1;
  /* GSAP centres this on the hub (cell col2/row2) on load and pans it on nav.
     No CSS transform here — GSAP's x and xPercent are additive, so a CSS
     translate would be double-counted with the xPercent centering. */
  will-change: transform;
}

.stage {
  grid-column: 2;
  grid-row: 2;
}
.subpage--top {
  grid-column: 2;
  grid-row: 1;
}
.subpage--left {
  grid-column: 1;
  grid-row: 2;
}
.subpage--right {
  grid-column: 3;
  grid-row: 2;
}
.subpage--bottom {
  grid-column: 2;
  grid-row: 3;
}

.subpage {
  position: relative;
  display: grid;
  place-items: center;
  padding: 2rem;
  text-align: center;
}

.subpage__title {
  font-weight: 700;
  font-size: clamp(2.25rem, 8vw, 5.5rem);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
  text-shadow: 0 2px 12px rgba(70, 45, 110, 0.3);
}

/* ---------- Sobre mí (about) subpage ---------- */
/* The top cell holds a softly-framed card — sticker portrait + intro text —
   echoing the reference slide. Its decorative .spark twinkles live inside the
   card so they ride along as it floats, over the shared .world-bg haze that
   stays bloomed behind this cell too. Layout is a 2-row grid: row 1 is photo | text
   (role + the "Combino" line, justified); row 2 is the "Integro" line spanning
   the full width below, also justified. */

.subpage--top {
  padding: clamp(1.25rem, 4vh, 3rem) clamp(1rem, 4vw, 3rem);
}

/* Sobre mí wraps its card in this scroll wrapper. On desktop it's transparent
   (display:contents) so .about is the grid item exactly as before — desktop is
   unchanged. On mobile/short-landscape (below) it becomes the scroll container,
   while .subpage--top stays a non-scrolling, hard-clipped positioning context. */
.subpage__scroll {
  display: contents;
}

.about {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas:
    'photo text'
    'bio   bio';
  align-items: center;
  gap: clamp(1rem, 2.6vh, 1.9rem) clamp(1.5rem, 4vw, 3.75rem);
  width: min(1080px, 94vw);
  padding: clamp(1.4rem, 3.5vw, 2.75rem) clamp(1.5rem, 4vw, 3.25rem);
  border-radius: 30px;
  border: 1.5px solid rgba(253, 247, 234, 0.45);
  /* frosted glass so the sun + grid read softly behind the card without
     fighting the text in front */
  background: rgba(86, 70, 142, 0.34);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow:
    0 18px 50px rgba(60, 40, 95, 0.34),
    0 0 0 6px rgba(159, 145, 207, 0.22);
}

/* photo + text + closing line ride above the decorative sparkles */
.about__photo,
.about__text,
.about__bio--wide {
  position: relative;
  z-index: 1;
}
.about > .spark {
  z-index: 0;
}

/* ---------- Sobre mí backdrop: vaporwave sun + scanlines ---------- */
/* The retro grid is NOT here — it's the shared .world-bg__grid, a sibling of
   the panning .world, so it stays fixed at the viewport bottom across both the
   hub and Sobre mí instead of sweeping in (and seaming) with this cell. This
   layer only adds Sobre mí's own sun + scanlines, behind the card (z-index 0
   vs the card's 1). */
.about-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

/* horizontal scanlines behind the sun, fading out toward the corners */
.about-bg__lines {
  position: absolute;
  left: 50%;
  top: clamp(0.5rem, 3vh, 2.5rem);
  width: clamp(480px, 72vw, 1000px);
  height: clamp(300px, 50vh, 560px);
  transform: translateX(-50%);
  background: repeating-linear-gradient(
    to bottom,
    rgba(247, 220, 246, 0.5) 0 3px,
    transparent 3px 16px
  );
  -webkit-mask-image: radial-gradient(66% 56% at 50% 40%, #000 14%, transparent 76%);
  mask-image: radial-gradient(66% 56% at 50% 40%, #000 14%, transparent 76%);
}

/* vaporwave sun rising from behind the card */
.about-bg__sun {
  position: absolute;
  left: 50%;
  top: clamp(1.5rem, 6vh, 5rem);
  width: clamp(240px, 30vw, 390px);
  aspect-ratio: 1;
  transform: translateX(-50%);
  filter: drop-shadow(0 0 26px rgba(236, 199, 238, 0.55));

  --floatY: -10px;
  animation: float-y 7s ease-in-out infinite;
}
.about-bg__sun-disc {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: linear-gradient(180deg, #fff3d9 0%, #ffe0ef 38%, #ecc7ee 70%, #cf9fe6 100%);
  /* slice horizontal gaps across the lower half — gaps widen toward the bottom
     for the classic retro-sun look */
  -webkit-mask-image: linear-gradient(
    to bottom,
    #000 0 50%,
    transparent 50% 58%,
    #000 58% 67%,
    transparent 67% 76%,
    #000 76% 84%,
    transparent 84% 93%,
    #000 93% 100%
  );
  mask-image: linear-gradient(
    to bottom,
    #000 0 50%,
    transparent 50% 58%,
    #000 58% 67%,
    transparent 67% 76%,
    #000 76% 84%,
    transparent 84% 93%,
    #000 93% 100%
  );
}

/* the portrait sits in a mini retro browser window (echoing the landing chrome);
   the figure just sizes it and carries the gentle float */
.about__photo {
  grid-area: photo;
  margin: 0;
  width: clamp(190px, 23vw, 310px);

  --floatY: -12px;
  animation: float-y 5s ease-in-out infinite;
}

.photo-window {
  display: flex;
  flex-direction: column;
  background: #a4e7dd;
  border-radius: 14px;
  padding: clamp(4px, 0.6vw, 6px);
  box-shadow:
    0 16px 30px rgba(60, 40, 95, 0.42),
    0 0 0 3px #9a83d3,
    0 3px 0 rgba(255, 255, 255, 0.5) inset;
}

.photo-window__bar {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: clamp(6px, 0.9vw, 10px);
  padding: clamp(5px, 0.7vw, 8px) clamp(7px, 0.9vw, 10px);
  background: linear-gradient(180deg, #f6dcf6, #ecc7ee);
  border: 2px solid #9a83d3;
  border-radius: 10px 10px 0 0;
}

.photo-window__dots {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: clamp(6px, 0.8vw, 9px);
}
/* min / max / close glyphs (mirror the landing browser controls) */
.pw-ctrl {
  position: relative;
  width: clamp(10px, 1.1vw, 14px);
  height: clamp(10px, 1.1vw, 14px);
  color: #41565f;
}
.pw-ctrl--min::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 18%;
  height: 2px;
  background: currentColor;
  border-radius: 2px;
}
.pw-ctrl--max {
  border: 2px solid currentColor;
  border-radius: 2px;
}
.pw-ctrl--close::before,
.pw-ctrl--close::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: 2px;
  background: currentColor;
  border-radius: 2px;
}
.pw-ctrl--close::before {
  transform: rotate(45deg);
}
.pw-ctrl--close::after {
  transform: rotate(-45deg);
}

.photo-window__screen {
  margin-top: clamp(4px, 0.6vw, 6px);
  aspect-ratio: 1 / 1;
  border: 2px solid #9a83d3;
  border-radius: 10px;
  overflow: hidden;
  background: #efeaf7;
}
.photo-window__screen img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* her face sits high in the portrait — keep it in view when cropping square */
  object-position: 50% 28%;
}

.about__text {
  grid-area: text;
  text-align: left;
}

/* pixel-font greeting with a soft holographic glow */
.about__hi {
  font-family: 'Jersey 10', system-ui, monospace;
  font-weight: 400;
  font-size: clamp(2.6rem, 6.8vw, 5.4rem);
  line-height: 1;
  letter-spacing: 0.02em;
  color: #a4e7dd;
  text-shadow:
    0 2px 0 rgba(116, 22, 86, 0.5),
    0 4px 12px rgba(96, 16, 70, 0.42),
    0 0 20px rgba(255, 138, 218, 0.6);
}

.about__name {
  margin-top: 0.25em;
  text-align: right;
  font-weight: 700;
  font-size: clamp(1.35rem, 3.3vw, 2.5rem);
  line-height: 1.05;
  color: #fdf7ea;
  text-shadow: 0 2px 10px rgba(70, 45, 110, 0.3);
}

/* role + both bio paragraphs are justified (flush to both edges) */
.about__role {
  margin-top: clamp(1rem, 2.6vh, 1.9rem);
  font-weight: 600;
  font-size: clamp(1.1rem, 2.5vw, 1.95rem);
  line-height: 1.2;
  color: #fdf7ea;
  text-align: justify;
}

.about__bio {
  margin-top: clamp(0.85rem, 1.9vh, 1.35rem);
  font-weight: 500;
  font-size: clamp(1.1rem, 2.1vw, 1.6rem);
  line-height: 1.5;
  color: #fdf7ea;
  text-align: justify;
}
.about__bio strong {
  font-weight: 700;
}

/* the closing "Integro" line spans the full width below the photo + text */
.about__bio--wide {
  grid-area: bio;
  margin-top: 0;
}

/* stack vertically on narrow screens (unified 760px breakpoint) */
@media (max-width: 760px) {
  .about {
    grid-template-columns: 1fr;
    grid-template-areas:
      'photo'
      'text'
      'bio';
    justify-items: center;
    gap: clamp(1.1rem, 3vw, 2rem);
    text-align: center;
  }
  .about__text {
    text-align: center;
  }
  .about__name,
  .about__role,
  .about__bio {
    text-align: center;
  }
  .about__bio {
    max-width: 48ch;
    margin-inline: auto;
  }
}

/* ---------- Back-to-hub buttons (one per subpage) ---------- */
/* Same holographic artwork as the hub's glitter buttons, just smaller. Each sits
   on the edge nearest the hub — the edge it travelled in from. Positioned with auto
   margins (not a transform) so the float `translate` and hover transform compose
   cleanly; explicit width + height (same 799:274 ratio as the artwork) let
   margin:auto centre it on every edge. */
.back-btn {
  position: absolute;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: clamp(130px, 15vw, 185px);
  height: clamp(45px, 5.15vw, 63px);
  padding: 0 0.6em;
  border: none;
  background-color: transparent;
  cursor: pointer;

  font-family: inherit;
  font-weight: 700;
  font-size: clamp(0.72rem, 1.4vw, 1.05rem);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #fff;
  text-shadow:
    0 1px 2px rgba(80, 10, 50, 0.95),
    0 2px 5px rgba(80, 10, 50, 0.75),
    0 0 11px rgba(110, 15, 70, 0.6);
  white-space: nowrap;

  /* the same holographic button artwork as the glitter buttons */
  background-image: url('/button.webp');
  background-size: 100% 100%;
  background-repeat: no-repeat;
  background-position: center;

  transition: transform 0.18s ease, filter 0.18s ease;

  --floatY: -9px;
  animation: float-y 3.6s ease-in-out infinite;
}

/* label rides above the moving sheen */
.back-btn__label {
  position: relative;
  z-index: 2;
  transform: translateY(-0.1em);
}

/* moving shine, clipped to the button artwork */
.back-btn::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  mix-blend-mode: screen;
  background: linear-gradient(
    115deg,
    transparent 38%,
    rgba(255, 255, 255, 0.5) 50%,
    transparent 62%
  );
  background-size: 250% 100%;
  background-position: 150% 0;
  -webkit-mask: url('/button.webp') center / 100% 100% no-repeat;
  mask: url('/button.webp') center / 100% 100% no-repeat;
  animation: glitter-sheen 5s ease-in-out infinite;
}

@media (hover: hover) and (pointer: fine) {
  .back-btn:hover {
    transform: translateY(-2px) scale(1.05);
    filter: brightness(1.06) drop-shadow(0 6px 16px rgba(255, 120, 210, 0.55));
  }
}
.back-btn:active {
  transform: translateY(0) scale(0.98);
}
.back-btn:focus-visible {
  outline: 3px solid #fff;
  outline-offset: 4px;
  border-radius: 20px;
}

/* placement on the edge nearest the hub */
.back-btn--down {
  bottom: clamp(20px, 4.5vh, 48px);
  left: 0;
  right: 0;
  margin-inline: auto;
}
.back-btn--up {
  top: clamp(20px, 4.5vh, 48px);
  left: 0;
  right: 0;
  margin-inline: auto;
}
.back-btn--right {
  right: clamp(20px, 4vw, 48px);
  top: 0;
  bottom: 0;
  margin-block: auto;
}
.back-btn--left {
  left: clamp(20px, 4vw, 48px);
  top: 0;
  bottom: 0;
  margin-block: auto;
}

/* ---------- Stage / diamond layout ---------- */

.stage {
  min-height: var(--app-h);
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  grid-template-rows: 1fr auto 1fr;
  grid-template-areas:
    '.    top    .'
    'left face right'
    '.   bottom  .';
  place-items: center;
  gap: clamp(0.75rem, 3vh, 2.25rem) clamp(0.75rem, 4vw, 3.5rem);
  padding: clamp(1.5rem, 5vh, 4rem) clamp(1.25rem, 5vw, 5rem);
}

.glitter-btn--top {
  grid-area: top;
}
.glitter-btn--left {
  grid-area: left;
}
.glitter-btn--right {
  grid-area: right;
}
.glitter-btn--bottom {
  grid-area: bottom;
}

/* ---------- Face frame ---------- */

.face-frame {
  grid-area: face;
  width: clamp(280px, 38vw, 480px);
  aspect-ratio: 480 / 270;
  border-radius: 10px;
  overflow: hidden;
  background: #e6e3f3;
  box-shadow:
    0 14px 34px rgba(60, 40, 95, 0.32),
    0 0 0 1px rgba(255, 255, 255, 0.25) inset;

  --floatY: -14px;
  animation: float-y 4.5s ease-in-out -2s infinite;
}

.face-frame svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* ---------- World background: vaporwave dream haze ---------- */
/* A fixed full-viewport backdrop behind the whole 3×3 world. It lives in
   .world-frame (not in .world), so it stays put while .world pans between
   cells — moving from the hub to a subpage never reveals a seam. */

.world-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

/* soft pastel glows blooming in from the corners over the purple base */
.world-bg::before {
  content: '';
  position: absolute;
  inset: -12%;
  background:
    radial-gradient(38% 42% at 10% 8%, rgba(164, 231, 221, 0.55), transparent 70%),
    radial-gradient(40% 44% at 90% 6%, rgba(236, 199, 238, 0.62), transparent 72%),
    radial-gradient(42% 46% at 6% 94%, rgba(236, 199, 238, 0.5), transparent 72%),
    radial-gradient(46% 50% at 96% 96%, rgba(164, 231, 221, 0.5), transparent 72%);
  animation: haze-breathe 16s ease-in-out infinite;
}

@keyframes haze-breathe {
  0%, 100% { opacity: 0.85; scale: 1; }
  50% { opacity: 1; scale: 1.05; }
}

/* retro perspective grid receding to a horizon along the bottom */
.world-bg__grid {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 260%;
  height: 48%;
  translate: -50% 0;
  transform: perspective(440px) rotateX(64deg);
  transform-origin: 50% 100%;
  background-image:
    linear-gradient(rgba(164, 231, 221, 0.5) 2px, transparent 2px),
    linear-gradient(90deg, rgba(164, 231, 221, 0.5) 2px, transparent 2px);
  background-size: 66px 66px;
  -webkit-mask-image: linear-gradient(to top, #000 4%, transparent 78%);
  mask-image: linear-gradient(to top, #000 4%, transparent 78%);
  animation: grid-flow 6s linear infinite;
}

@keyframes grid-flow {
  to { background-position: 0 66px; }
}

/* twinkling sparkles scattered through the "sky" */
.spark {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--s, 12px);
  aspect-ratio: 1;
  translate: -50% -50%;
  background: var(--c, #fff);
  clip-path: polygon(
    50% 0%, 60% 40%, 100% 50%, 60% 60%,
    50% 100%, 40% 60%, 0% 50%, 40% 40%
  );
  filter: drop-shadow(0 0 4px rgba(255, 255, 255, 0.85));
  animation: twinkle 3.6s ease-in-out var(--d, 0s) infinite;
}

@keyframes twinkle {
  0%, 100% { opacity: 0.22; scale: 0.65; }
  50% { opacity: 1; scale: 1; }
}

/* ---------- Holographic glitter button ---------- */

.glitter-btn {
  position: relative;
  display: grid;
  place-items: center;
  border: none;
  background-color: transparent;
  cursor: pointer;

  width: clamp(230px, 30vw, 380px);
  aspect-ratio: 799 / 274;
  /* horizontal inset keeps the (nowrap, centred) label off the artwork's rounded
     ends. em (not %) per the gotcha: % padding resolves against the containing
     block (the grid track, wide for the left/right 1fr columns), ballooning it. */
  padding: 0 1.1em;

  font-family: inherit;
  font-weight: 700;
  font-size: clamp(1.15rem, 2.4vw, 2.3rem);
  line-height: 1.4;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-align: center;
  color: #fff;
  text-shadow:
    0 1px 2px rgba(80, 10, 50, 0.95),
    0 2px 5px rgba(80, 10, 50, 0.75),
    0 0 11px rgba(110, 15, 70, 0.6);
  white-space: nowrap;

  /* the original holographic button artwork */
  background-image: url('/button.webp');
  background-size: 100% 100%;
  background-repeat: no-repeat;
  background-position: center;

  transition:
    transform 0.18s ease,
    filter 0.18s ease;

  --floatY: -14px;
  animation: float-y 4.5s ease-in-out infinite;
}

/* per-button float timing — placed after the base rule so these longhands
   override the base `animation` shorthand; negative delays desync them */
.glitter-btn--top {
  animation-duration: 3.8s;
  animation-delay: -0.5s;
}
.glitter-btn--left {
  animation-duration: 4.6s;
  animation-delay: -2.5s;
  --floatY: -13px;
}
.glitter-btn--right {
  animation-duration: 4.2s;
  animation-delay: -1.5s;
  --floatY: -16px;
}
.glitter-btn--bottom {
  animation-duration: 5s;
  animation-delay: -3.5s;
}

/* keep the label crisp and above the moving sheen; nudge up to optically
   center it in the pill (the artwork sits slightly high in its canvas) */
.glitter-btn__label {
  position: relative;
  z-index: 2;
  transform: translateY(-0.18em);
}

/* "Sobre mí" has an accent that pokes above the caps, making the whole word
   read as too high — sit it a touch lower so the full word looks balanced */
.glitter-btn--top .glitter-btn__label {
  transform: translateY(-0.1em);
}

/* subtle moving shine, clipped to the button's exact shape */
.glitter-btn::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  mix-blend-mode: screen;
  background: linear-gradient(
    115deg,
    transparent 38%,
    rgba(255, 255, 255, 0.5) 50%,
    transparent 62%
  );
  background-size: 250% 100%;
  background-position: 150% 0;
  -webkit-mask: url('/button.webp') center / 100% 100% no-repeat;
  mask: url('/button.webp') center / 100% 100% no-repeat;
  animation: glitter-sheen 5.5s ease-in-out infinite;
}

/* decorative hover gated to real pointers so a tap doesn't leave it stuck "hovered"
   on touch; :active + :focus-visible below cover touch/keyboard feedback */
@media (hover: hover) and (pointer: fine) {
  .glitter-btn:hover {
    transform: translateY(-2px) scale(1.04);
    filter: brightness(1.06) drop-shadow(0 7px 20px rgba(255, 120, 210, 0.55));
  }
}

.glitter-btn:active {
  transform: translateY(0) scale(0.99);
}

.glitter-btn:focus-visible {
  outline: 3px solid #fff;
  outline-offset: 4px;
  border-radius: 26px;
}

@keyframes glitter-sheen {
  0% {
    background-position: 150% 0;
  }
  55%,
  100% {
    background-position: -60% 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .glitter-btn::after,
  .glitter-btn,
  .face-frame,
  .about__photo,
  .about-bg__sun,
  .world-bg::before,
  .world-bg__grid,
  .spark,
  .back-btn,
  .back-btn::after {
    animation: none;
  }
  .spark {
    opacity: 0.7;
  }
}

/* ---------- Mid widths (tablet / small laptop): the desktop diamond's fixed
   ~1470px of content (2 buttons + face + gaps + padding) overflows viewports
   below ~1440, clipping the right (Contacto) button. Scale the face + buttons
   with vw so the diamond fits with balanced margins. Gated ABOVE the 760px mobile
   stack and ABOVE 600px tall (below that, the short-landscape block owns the
   sizing), and capped at 1439px so >=1440px stays PIXEL-IDENTICAL: the min()'s vw
   term only dips below the existing clamp result once the viewport is < ~1440. ---------- */
@media (min-width: 761px) and (max-width: 1439px) and (min-height: 601px) {
  .face-frame {
    width: min(clamp(280px, 38vw, 480px), 30vw);
  }
  .glitter-btn {
    width: min(clamp(230px, 30vw, 380px), 24vw);
  }
}

/* ---------- Narrow screens: stack vertically (unified 760px breakpoint) ---------- */

@media (max-width: 760px) {
  .stage {
    grid-template-columns: 1fr;
    grid-template-rows: repeat(5, auto);
    grid-template-areas:
      'face'
      'top'
      'left'
      'right'
      'bottom';
    gap: 1.1rem;
    align-content: center;
  }
  /* lift the face away from the button column so it doesn't crowd "Sobre mí" */
  .stage .face-frame {
    margin-bottom: clamp(0.8rem, 3.5vh, 2.2rem);
  }
}

/* ---------- Short landscape (wide-but-short phones): keep the DIAMOND, scale it
   to fit the short height instead of restacking (which clips top/bottom buttons).
   Declared AFTER the 760px stack so it wins where both match (e.g. 667×375). ---------- */
@media (orientation: landscape) and (max-height: 600px) and (max-width: 1439px) {
  .stage {
    grid-template-columns: 1fr auto 1fr;
    grid-template-rows: 1fr auto 1fr;
    grid-template-areas:
      '.    top    .'
      'left face right'
      '.   bottom  .';
    align-content: normal;
    gap: clamp(0.35rem, 2vh, 1rem) clamp(0.5rem, 2.5vw, 1.75rem);
    padding: clamp(0.5rem, 3vh, 1.4rem) clamp(0.75rem, 3vw, 2.5rem);
  }
  /* min() lets the height (vh) beat the clamp *min* on short screens, so the
     diamond can shrink below the desktop 280/230px floors and fit the viewport. */
  .face-frame {
    width: min(clamp(280px, 38vw, 480px), 44vh);
  }
  /* 41vh (up from 33vh) keeps the button's 799:274 art >=44px tall — the tap-target
     floor — even on the shortest landscape phones (320px tall → ~45px), while the
     diamond still fits both axes at every short-landscape size. */
  .glitter-btn {
    width: min(clamp(230px, 30vw, 380px), 41vh);
  }
}

/* ---------- Sobre mí: scroll container on mobile + short-landscape ----------
   The card has no scroll fallback and is centre-clipped in a full-height cell
   (worst in landscape). Move the scroll onto the inner .subpage__scroll wrapper
   and keep .subpage--top a non-scrolling, hard-clipped positioning context, so
   the "Volver" button (an absolute sibling on the bottom edge) never overlaps the
   scrolling intro text and the card can't bleed into the hub below. */
@media (max-width: 760px), (orientation: landscape) and (max-height: 600px) and (max-width: 1439px) {
  /* The SECTION is NOT the scroll element — it's a hard-clipped, non-scrolling
     positioning context. This keeps .back-btn--down pinned to the visible bottom
     edge (it never scrolls up over the intro text), AND clips the card's overflow
     so it can't bleed across the cell's bottom into the hub (the top-edge seam).
     Its padding-bottom carves out the button's zone (offset + height + float +
     gap) so the inner scroll viewport ends ABOVE the button — text can never
     scroll under it at ANY scroll position. (The button, being absolutely
     positioned, still sits on the padding-box bottom edge, i.e. the hub-facing
     edge.) */
  .subpage--top {
    display: block;
    overflow: hidden;
    padding: 0 0
      calc(
        clamp(14px, 3vh, 28px) + clamp(45px, 5.15vw, 63px) +
          env(safe-area-inset-bottom, 0px) + 18px
      );
  }
  /* The INNER wrapper is the scroll container: it fills the cell ABOVE the button
     (height:100% resolves against the section's content box, already shortened by
     the padding-bottom), top-anchors the card, and scrolls everything else. */
  /* generic scroll mechanics — shared by Sobre mí, Hoja de vida & Contacto wrappers.
     width:100% is required: Hoja de vida's .cv is a container-type:inline-size query
     container (≈0 intrinsic width), which would otherwise shrink-wrap the flex
     wrapper to just its padding and collapse the collage. */
  .subpage__scroll {
    display: flex;
    flex-direction: column;
    width: 100%;
    height: 100%;
    overflow-y: auto;
    overflow-x: hidden;
    overscroll-behavior: contain; /* don't scroll-chain to #app */
    -webkit-overflow-scrolling: touch;
  }
  /* Sobre mí card: centre it; generous bottom pad so the card's 0 18px 50px
     drop-shadow isn't clipped where the "Volver" zone begins. */
  .subpage--top .subpage__scroll {
    align-items: center;
    padding: max(1.25rem, env(safe-area-inset-top, 0px)) clamp(1rem, 4vw, 2rem)
      clamp(2.5rem, 6vh, 3.75rem);
  }
  /* fit the card to the wrapper's content width (no side clipping / h-scroll) and
     use a lighter frost on mobile GPUs (heavy blur is costly, barely visible small) */
  .about {
    width: min(1080px, 100%);
    backdrop-filter: blur(7px);
    -webkit-backdrop-filter: blur(7px);
  }
}

/* ---------- Back buttons: keep the hub-facing edge, pad into the safe area on
   mobile so notches/rounded corners never clip "Volver". Each owning view pads
   its content to clear the button (Foundation owns Sobre mí's above). ---------- */
@media (max-width: 760px), (orientation: landscape) and (max-height: 600px) and (max-width: 1439px) {
  /* one shared, bigger "Volver" size for ALL four back-buttons (consistency) */
  .back-btn {
    width: clamp(165px, 46vw, 205px);
    height: clamp(52px, 14.5vw, 62px);
    font-size: clamp(0.85rem, 3.4vw, 1.12rem);
  }
  .back-btn--down {
    bottom: calc(clamp(14px, 3vh, 28px) + env(safe-area-inset-bottom, 0px));
  }
  .back-btn--up {
    top: calc(clamp(14px, 3vh, 28px) + env(safe-area-inset-top, 0px));
  }
  /* Hoja de vida & Contacto: move "Volver" to the TOP-centre on mobile (was on the
     right / left edge). Pinned to the non-scrolling cell so it never scrolls away. */
  .subpage--left .back-btn--right,
  .subpage--right .back-btn--left {
    top: calc(clamp(14px, 3vh, 28px) + env(safe-area-inset-top, 0px));
    bottom: auto;
    left: 0;
    right: 0;
    margin-inline: auto;
    margin-block: 0;
  }
}
/* ============================================================
   Retro browser landing — Y2K / vaporwave kawaii
   ============================================================ */

.landing {
  --ink: #41565f; /* dark slate-teal: window icons */
  --mint: #a4e7dd;
  --mint-deep: #7fd6c7;
  --pink: #ecc7ee;
  --pink-soft: #f6dcf6;
  --lilac: #d8c2f0;
  --cream: #fdf7ea;
  --line: #9a83d3; /* periwinkle sticker outline */
  --line-deep: #6f5ba8;
  --pill-ink: #2c2c3e;

  height: var(--app-h);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(0.5rem, 2vh, 1.5rem);
  padding: clamp(1rem, 4vh, 3rem) clamp(1rem, 4vw, 3rem);
  overflow: hidden;
}

/* ---------- Browser window ---------- */

.browser {
  position: relative;
  width: min(1080px, 92vw);
  /* keep the whole window + base band inside one viewport; --app-h tracks the
     visible (URL-bar-aware) height so mobile never clips behind the browser chrome */
  max-height: calc(var(--app-h) * 0.72);
  display: flex;
  flex-direction: column;
  background: var(--mint);
  border-radius: clamp(14px, 1.6vw, 22px);
  padding: clamp(5px, 0.7vw, 9px);
  box-shadow:
    0 22px 48px rgba(70, 45, 110, 0.32),
    0 3px 0 rgba(255, 255, 255, 0.5) inset;
}

/* Title bar */
.browser__bar {
  position: relative;
  height: clamp(34px, 4.4vh, 52px);
  display: flex;
  align-items: flex-end;
  justify-content: flex-end;
  padding: 0 clamp(10px, 1.2vw, 18px);
  background: linear-gradient(180deg, var(--pink-soft), var(--pink));
  border: 2.5px solid var(--line);
  border-radius: clamp(10px, 1.1vw, 16px) clamp(10px, 1.1vw, 16px) 0 0;
}

/* the tab tucked into the top-left */
.browser__tab {
  position: absolute;
  left: clamp(14px, 2vw, 30px);
  bottom: -2.5px;
  width: clamp(120px, 18vw, 215px);
  height: clamp(26px, 3.4vh, 42px);
  background: linear-gradient(180deg, #f3d2f3, var(--pink));
  border: 2.5px solid var(--line);
  border-bottom: none;
  border-radius: 12px 12px 0 0;
}

.browser__controls {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: clamp(11px, 1.4vw, 20px);
  padding-bottom: clamp(7px, 1vh, 12px);
}

.browser__control {
  position: relative;
  width: clamp(15px, 1.6vw, 21px);
  height: clamp(15px, 1.6vw, 21px);
  color: var(--ink);
}
.browser__control--min::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 18%;
  height: 2.6px;
  background: currentColor;
  border-radius: 2px;
}
.browser__control--max {
  border: 2.6px solid currentColor;
  border-radius: 3px;
}
.browser__control--close::before,
.browser__control--close::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: 2.6px;
  background: currentColor;
  border-radius: 2px;
}
.browser__control--close::before {
  transform: rotate(45deg);
}
.browser__control--close::after {
  transform: rotate(-45deg);
}

/* Nav / address row */
.browser__nav {
  display: flex;
  align-items: center;
  gap: clamp(8px, 1.2vw, 18px);
  padding: clamp(7px, 1vh, 13px) clamp(10px, 1.3vw, 20px);
  background: var(--pink);
  border: 2.5px solid var(--line);
  border-top: none;
}

.browser__actions {
  display: flex;
  align-items: center;
  gap: clamp(6px, 0.9vw, 13px);
  color: var(--ink);
  flex-shrink: 0;
}
.browser__icon {
  width: clamp(20px, 2.2vw, 30px);
  height: clamp(20px, 2.2vw, 30px);
}
.browser__icon svg {
  width: 100%;
  height: 100%;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.1;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.browser__url {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  height: clamp(28px, 3.4vh, 42px);
  padding: 0 clamp(12px, 1.4vw, 20px);
  background: var(--cream);
  border: 2.5px solid var(--line);
  border-radius: 999px;
  box-shadow: 0 2px 5px rgba(70, 45, 110, 0.12) inset;
}
.browser__mag {
  width: clamp(15px, 1.7vw, 21px);
  height: clamp(15px, 1.7vw, 21px);
  fill: none;
  stroke: var(--line-deep);
  stroke-width: 2.2;
  stroke-linecap: round;
}

.browser__avatar {
  flex-shrink: 0;
  width: clamp(30px, 3.6vw, 46px);
  height: clamp(30px, 3.6vw, 46px);
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #c4f1e9, var(--mint-deep));
  border: 2.5px solid var(--line);
}

/* Content area */
.browser__content {
  position: relative;
  flex: 1;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: clamp(5px, 0.7vw, 9px);
  padding: clamp(6px, 1.2vw, 16px);
  background: linear-gradient(180deg, #fffdf6, var(--cream));
  border: 2.5px solid var(--line);
  border-radius: clamp(8px, 1vw, 14px);
  overflow: hidden;
}

/* ---------- Clickable PORTAFOLIO ---------- */

.portfolio-link {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  border: none;
  background: transparent;
  padding: 0;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.portfolio-img {
  display: block;
  /* grow past the content box (centered) to enlarge the wordmark in place;
     any bleed is clipped by .browser__content's overflow:hidden */
  width: 120%;
  height: 100%;
  object-fit: contain;
  filter: drop-shadow(0 6px 22px rgba(228, 150, 220, 0.55));
  transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1), filter 0.4s ease;
  animation: portfolio-float 5.5s ease-in-out infinite;
}
.portfolio-link:focus-visible .portfolio-img {
  transform: scale(1.03);
  filter: drop-shadow(0 10px 30px rgba(232, 130, 224, 0.85))
    drop-shadow(0 0 14px rgba(190, 170, 255, 0.7));
}
/* decorative hover gated to real pointers so it can't stick after a tap */
@media (hover: hover) and (pointer: fine) {
  .portfolio-link:hover .portfolio-img {
    transform: scale(1.03);
    filter: drop-shadow(0 10px 30px rgba(232, 130, 224, 0.85))
      drop-shadow(0 0 14px rgba(190, 170, 255, 0.7));
  }
}
.portfolio-link:focus-visible {
  outline: 3px dashed var(--line-deep);
  outline-offset: -6px;
  border-radius: 12px;
}
.portfolio-link:active .portfolio-img {
  transform: scale(0.99);
}

@keyframes portfolio-float {
  0%, 100% { translate: 0 0; }
  50% { translate: 0 -10px; }
}

/* pixel cursor accent near the text */
.pixel-cursor {
  position: absolute;
  right: clamp(14px, 4.2vw, 64px);
  bottom: clamp(-46px, -2.9vw, -24px);
  width: clamp(40px, 4.2vw, 56px);
  height: auto;
  transform: rotate(8deg);
  filter: drop-shadow(2px 3px 2px rgba(40, 30, 70, 0.35));
  pointer-events: none;
}

/* ---------- Grid band + name search pill ---------- */

.landing__base {
  position: relative;
  width: min(1080px, 92vw);
  height: clamp(74px, 13vh, 128px);
  border-radius: 8px;
  background-image:
    linear-gradient(rgba(120, 206, 196, 0.85) 2px, transparent 2px),
    linear-gradient(90deg, rgba(120, 206, 196, 0.85) 2px, transparent 2px);
  background-size: clamp(26px, 3vw, 42px) clamp(26px, 3vw, 42px);
  background-position: -1px -1px;
  -webkit-mask-image: linear-gradient(90deg, #000 62%, transparent);
  mask-image: linear-gradient(90deg, #000 62%, transparent);
}

.search-pill {
  position: absolute;
  left: clamp(8px, 3vw, 56px);
  top: 50%;
  transform: translateY(-50%);
  --floatY: -12px;
  animation: float-y 4.2s ease-in-out 1.2s infinite;
  display: flex;
  align-items: center;
  gap: clamp(8px, 1vw, 16px);
  padding: clamp(6px, 0.8vh, 10px);
  padding-left: clamp(16px, 1.8vw, 28px);
  background: #fff;
  border: 2.5px solid var(--mint-deep);
  border-radius: 14px;
  box-shadow: 0 0 0 2.5px var(--pill-ink), 0 8px 18px rgba(50, 35, 80, 0.28);
}
.search-pill__text {
  font-family: 'Jersey 10', 'Fredoka', monospace;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  font-size: clamp(1.15rem, 2.3vw, 2rem);
  color: var(--pill-ink);
  white-space: nowrap;
}

/* blinking caret for the typewriter (main.ts types the name in char by char) */
.search-pill__text::after {
  content: '';
  display: inline-block;
  width: 0.45em;
  height: 1.05em;
  margin-left: 0.12em;
  vertical-align: -0.15em;
  background: var(--pill-ink);
  animation: caret-blink 1.05s infinite;
}

@keyframes caret-blink {
  0%, 50% { opacity: 1; }
  50.01%, 100% { opacity: 0; }
}

.search-pill__btn {
  display: grid;
  place-items: center;
  width: clamp(34px, 3.6vw, 50px);
  height: clamp(30px, 3.2vw, 44px);
  background: radial-gradient(circle at 40% 30%, #c4f1e9, var(--mint-deep));
  border: 2.5px solid var(--pill-ink);
  border-radius: 10px;
}
.search-pill__btn svg {
  width: 56%;
  height: 56%;
  fill: none;
  stroke: var(--pill-ink);
  stroke-width: 2.6;
  stroke-linecap: round;
}

/* ---------- Entrance choreography ---------- */

.browser {
  --floatY: -13px;
  animation: rise-in 0.8s cubic-bezier(0.22, 1, 0.36, 1) both,
    float-y 4.8s ease-in-out 1s infinite;
}
.portfolio-img {
  animation: portfolio-float 5.5s ease-in-out infinite,
    pop-in 0.7s cubic-bezier(0.34, 1.56, 0.64, 1) 0.35s both;
}
.pixel-cursor {
  /* its own quicker bob, on top of riding the window's float */
  --floatY: -13px;
  animation: cursor-in 0.5s ease 0.75s both,
    float-y 2.5s ease-in-out 1.1s infinite;
}
.landing__base {
  animation: rise-in 0.7s cubic-bezier(0.22, 1, 0.36, 1) 0.2s both;
}

@keyframes rise-in {
  from { opacity: 0; transform: translateY(26px) scale(0.97); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes pop-in {
  from { opacity: 0; scale: 0.8; }
  to { opacity: 1; scale: 1; }
}
@keyframes cursor-in {
  from { opacity: 0; transform: rotate(8deg) translate(10px, 10px); }
  to { opacity: 1; transform: rotate(8deg) translate(0, 0); }
}

/* ---------- Narrow screens (portrait phones + small tablets) ---------- */

@media (max-width: 760px) {
  /* keep the window clear of any notch on every edge */
  .landing {
    padding-top: max(clamp(1rem, 4vh, 3rem), env(safe-area-inset-top, 0px));
    padding-right: max(clamp(1rem, 4vw, 3rem), env(safe-area-inset-right, 0px));
    padding-bottom: max(clamp(1rem, 4vh, 3rem), env(safe-area-inset-bottom, 0px));
    padding-left: max(clamp(1rem, 4vw, 3rem), env(safe-area-inset-left, 0px));
  }

  .browser {
    width: 94vw;
    /* a real floor so the wide 3.5:1 wordmark isn't a lonely sliver adrift in
       empty purple; --app-h is the visible (URL-bar-aware) height */
    min-height: calc(var(--app-h) * 0.56);
    max-height: calc(var(--app-h) * 0.66);
  }

  .landing__base {
    width: 94vw;
    -webkit-mask-image: none;
    mask-image: none;
  }

  /* lift the pixel cursor so it stops diving into the grid band / pill */
  .pixel-cursor {
    right: clamp(10px, 4vw, 40px);
    bottom: -16px;
  }

  .search-pill {
    left: 50%;
    transform: translate(-50%, -50%);
    max-width: 90vw;
  }
  .search-pill__text {
    font-size: clamp(1rem, 4.6vw, 1.5rem);
  }
}

@media (max-width: 440px) {
  .search-pill {
    gap: clamp(6px, 2vw, 12px);
    padding-left: clamp(12px, 3vw, 20px);
  }
  /* let the name shrink, and wrap (centered) only if it truly can't fit */
  .search-pill__text {
    font-size: clamp(0.9rem, 4vw, 1.25rem);
    white-space: normal;
    text-align: center;
  }
}

/* ---------- Short landscape: wide-but-short phones (844×390, 915×412, 740×360) ----------
   The old width-only query missed these (width > 760) and left the desktop layout on a
   ~360–430px-tall viewport, clipping the tab + pill. Route them into a compact, notch-safe
   layout that fits without scrolling. Placed last so it overrides the max-width blocks for
   phones that match both (e.g. 740×360). */
@media (orientation: landscape) and (max-height: 600px) {
  .landing {
    gap: clamp(0.4rem, 1.5vh, 1rem);
    padding-top: max(clamp(0.6rem, 3vh, 1.5rem), env(safe-area-inset-top, 0px));
    padding-right: max(clamp(1rem, 4vw, 3rem), env(safe-area-inset-right, 0px));
    padding-bottom: max(clamp(0.6rem, 3vh, 1.5rem), env(safe-area-inset-bottom, 0px));
    padding-left: max(clamp(1rem, 4vw, 3rem), env(safe-area-inset-left, 0px));
  }

  .browser {
    /* 88vw keeps the window clear of a landscape notch even at its widest */
    width: 88vw;
    min-height: calc(var(--app-h) * 0.42);
    max-height: calc(var(--app-h) * 0.6);
  }

  .landing__base {
    width: 88vw;
    -webkit-mask-image: none;
    mask-image: none;
  }

  /* not enough vertical room to straddle the window edge — retire it here */
  .pixel-cursor {
    display: none;
  }

  .search-pill {
    left: 50%;
    transform: translate(-50%, -50%);
    max-width: 80vw;
  }
}

@media (prefers-reduced-motion: reduce) {
  .browser,
  .portfolio-img,
  .pixel-cursor,
  .landing__base,
  .search-pill {
    animation: none;
  }
  .portfolio-img {
    transition: none;
  }
  /* no typing animation: caret hidden, full name shown immediately (via main.ts) */
  .search-pill__text::after {
    display: none;
  }
}
/* ============================================================
   Hoja de vida — retro-window CV collage (Y2K / vaporwave)
   ============================================================
   A self-scaling "poster": .cv is a size container and every window plus all of
   its text/chrome are sized in one unit (--u = 1% of the collage width), so the
   whole arrangement — overlaps included — scales as a block and never reflows.
   On narrow screens it drops to a plain vertical stack (--u switches to vw). */

/* reserve a right gutter so the centred collage clears the floating "Volver"
   button, which lives on the right edge (the edge nearest the hub) */
.subpage--left {
  padding: clamp(1rem, 3vh, 2.5rem) clamp(1rem, 4vw, 3rem);
  padding-right: clamp(118px, 14vw, 210px);
}

.cv {
  position: relative;
  z-index: 1; /* the collage sits above its .cv-bg backdrop */
  width: min(100%, 132vh);
  aspect-ratio: 200 / 119;
  container-type: size;

  --u: 1cqw; /* the single scaling unit: 1% of the collage width */
  --cv-line: #8b84c6; /* periwinkle-blue window outline + chrome */
  --cv-body: #efeafb; /* light lavender window body */
  --cv-shadow: rgba(124, 121, 188, 0.5); /* sticker drop-shadow */
  --cv-ink: #6f5ba8; /* body text */
  --cv-title: #6a5cbf; /* pixel-font titles */
}

/* ---------- cell backdrop: graph-paper grid ----------
   Hoja de vida's ONLY non-shared backdrop. The corner glows + sparkles are the
   shared .world-bg, kept bloomed for this cell like Sobre mí (see main.ts) — not
   duplicated here. main.ts owns this layer's opacity: it fades the grid in on
   arrival at Hoja de vida and out on the way back to the hub. Pinned with inset:0
   so it fills the whole cell (the right "Volver" gutter doesn't carve it); sits
   behind the collage (.cv is z-index:1). Same flat-grid recipe as Contacto's
   .contact-grid, so the two purple-based subpages read as siblings. */
.cv-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

/* flat graph-paper grid on the purple base */
.cv-bg__grid {
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.1) 2px, transparent 2px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.1) 2px, transparent 2px);
  background-size: clamp(40px, 4.2vw, 60px) clamp(40px, 4.2vw, 60px);
}

/* ---------- window shell ---------- */
.win {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--w);
  display: flex;
  flex-direction: column;
  text-align: left;
  border: calc(var(--u) * 0.3) solid var(--cv-line);
  border-radius: calc(var(--u) * 1.6);
  background: var(--cv-body);
  box-shadow: calc(var(--u) * 0.7) calc(var(--u) * 0.9) 0 var(--cv-shadow);
  overflow: hidden;

  --floatY: calc(var(--u) * -0.9);
  animation: float-y 5s ease-in-out infinite;
}

/* ---------- title bar ---------- */
.win__bar {
  display: flex;
  align-items: center;
  gap: calc(var(--u) * 0.8);
  padding: calc(var(--u) * 0.7) calc(var(--u) * 1);
  border-bottom: calc(var(--u) * 0.3) solid var(--cv-line);
}
.win--mint .win__bar {
  background: linear-gradient(180deg, #bdf0e8, #a4e7dd);
}
.win--pink .win__bar {
  background: linear-gradient(180deg, #f6dcf6, #ecc7ee);
}

.win__title {
  flex: 1;
  font-family: 'Jersey 10', system-ui, monospace;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: calc(var(--u) * 3.6);
  line-height: 1;
  color: var(--cv-title);
}
.win--mint .win__title {
  text-align: center;
}
.win--pink .win__title {
  text-align: left;
}
/* Experiencia and Idiomas use the pink chrome but keep a centered title like the mint windows */
.cv-win--exp .win__title,
.cv-win--idiomas .win__title {
  text-align: center;
}

/* ---------- window controls (✕ ▢ _) ---------- */
.win__controls {
  display: flex;
  flex-shrink: 0;
  gap: calc(var(--u) * 0.55);
  color: var(--cv-line);
}
.win__ctrl {
  position: relative;
  width: calc(var(--u) * 1.7);
  height: calc(var(--u) * 1.7);
  border: calc(var(--u) * 0.26) solid currentColor;
  border-radius: calc(var(--u) * 0.35);
}
.win__ctrl--min::before {
  content: '';
  position: absolute;
  left: 22%;
  right: 22%;
  bottom: 26%;
  height: calc(var(--u) * 0.26);
  background: currentColor;
}
.win__ctrl--max::before {
  content: '';
  position: absolute;
  inset: 26%;
  border: calc(var(--u) * 0.24) solid currentColor;
}
.win__ctrl--close::before,
.win__ctrl--close::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 24%;
  right: 24%;
  height: calc(var(--u) * 0.24);
  background: currentColor;
}
.win__ctrl--close::before {
  transform: rotate(45deg);
}
.win__ctrl--close::after {
  transform: rotate(-45deg);
}

/* ---------- body ---------- */
.win__body {
  position: relative;
  flex: 1;
  padding: calc(var(--u) * 1.6) calc(var(--u) * 1.8);
  color: var(--cv-ink);
}
.win--left .win__body {
  padding-left: calc(var(--u) * 4.4);
}
.win--right .win__body {
  padding-right: calc(var(--u) * 4.4);
}

.win__list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: calc(var(--u) * 1.5);
  font-weight: 500;
  font-size: calc(var(--u) * 2);
  line-height: 1.25;
}
.win__list--idiomas {
  gap: calc(var(--u) * 0.6);
  font-size: calc(var(--u) * 2.1);
}


/* ---------- software logos ---------- */
.win__logos {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: calc(var(--u) * 1.6) calc(var(--u) * 1.4);
  place-items: center;
}
.win__logos img {
  width: calc(var(--u) * 6);
  height: calc(var(--u) * 6);
  object-fit: contain;
  border-radius: calc(var(--u) * 1.3); /* rounded tiles, matching the Photoshop icon */
  filter: drop-shadow(
    0 calc(var(--u) * 0.3) calc(var(--u) * 0.5) rgba(60, 40, 95, 0.28)
  );
}

/* ---------- decorative scrollbar ---------- */
.win__scroll {
  position: absolute;
  top: calc(var(--u) * 1.2);
  bottom: calc(var(--u) * 1.2);
  width: calc(var(--u) * 1.9);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(var(--u) * 0.6);
  color: var(--cv-line);
}
.win--left .win__scroll {
  left: calc(var(--u) * 1.2);
}
.win--right .win__scroll {
  right: calc(var(--u) * 1.2);
}

.win__arrow {
  width: 0;
  height: 0;
  border-left: calc(var(--u) * 0.85) solid transparent;
  border-right: calc(var(--u) * 0.85) solid transparent;
}
.win__arrow--up {
  border-bottom: calc(var(--u) * 1) solid currentColor;
}
.win__arrow--down {
  border-top: calc(var(--u) * 1) solid currentColor;
}

.win__track {
  position: relative;
  flex: 1;
  width: calc(var(--u) * 1.5);
  border-radius: calc(var(--u) * 1);
  background: rgba(139, 132, 198, 0.22);
}
.win__thumb {
  position: absolute;
  left: 0;
  right: 0;
  top: 4%;
  height: 46%;
  border-radius: calc(var(--u) * 1);
  background: linear-gradient(180deg, #a4e7dd, #ecc7ee);
}

/* ---------- CV download window (cream "action" window) ----------
   The 5th window: cream/peach chrome to set it apart from the four info
   windows, holding a single holographic "Descargar" link to the PDF. */
.win--cream .win__bar {
  background: linear-gradient(180deg, #fdf7ea, #f7e8c8);
}
.win--cream .win__title {
  text-align: center;
}

/* the download link: a candy-holographic pill that pops */
.cv-dl {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: calc(var(--u) * 0.9);
  padding: calc(var(--u) * 1.5) calc(var(--u) * 1.8);
  border: calc(var(--u) * 0.3) solid var(--cv-line);
  border-radius: calc(var(--u) * 5);
  background: linear-gradient(120deg, #a4e7dd 0%, #ecc7ee 52%, #fdf7ea 100%);
  box-shadow:
    0 calc(var(--u) * 0.4) calc(var(--u) * 0.8) rgba(124, 121, 188, 0.4),
    inset 0 calc(var(--u) * 0.3) 0 rgba(255, 255, 255, 0.6);
  font-family: 'Fredoka', system-ui, sans-serif;
  font-weight: 600;
  font-size: calc(var(--u) * 2.4);
  letter-spacing: 0.01em;
  color: var(--cv-title);
  text-decoration: none;
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}
/* decorative hover gated to real pointers so a tap doesn't leave the pill stuck
   "lifted" on touch; :focus-visible below covers keyboard feedback, and the <a>'s
   navigation to the PDF is the tap feedback on touch.
   Desktop matches (hover:hover)+(pointer:fine), so it renders identically. */
@media (hover: hover) and (pointer: fine) {
  .cv-dl:hover {
    transform: translateY(calc(var(--u) * -0.4)) scale(1.03);
    box-shadow:
      0 calc(var(--u) * 0.7) calc(var(--u) * 1.1) rgba(124, 121, 188, 0.5),
      inset 0 calc(var(--u) * 0.3) 0 rgba(255, 255, 255, 0.7);
  }
}
.cv-dl:focus-visible {
  outline: calc(var(--u) * 0.35) solid var(--cv-title);
  outline-offset: calc(var(--u) * 0.5);
}
.cv-dl__icon {
  font-size: calc(var(--u) * 2.8);
  line-height: 1;
}

/* ---------- collage placement (overlapping, staggered floats) ---------- */
.cv-win--edu {
  --x: 4%;
  --y: 11%;
  --w: calc(var(--u) * 31);
  z-index: 1;
  animation-duration: 5.2s;
  animation-delay: -0.4s;
}
.cv-win--exp {
  --x: 31%;
  --y: 44%;
  --w: calc(var(--u) * 31);
  z-index: 3;
  --floatY: calc(var(--u) * -1.1);
  animation-duration: 4.6s;
  animation-delay: -2.1s;
}
.cv-win--soft {
  --x: 58%;
  --y: 13%;
  --w: calc(var(--u) * 28);
  z-index: 2;
  animation-duration: 5.6s;
  animation-delay: -1.3s;
}
.cv-win--idiomas {
  --x: 65%;
  --y: 53%;
  --w: calc(var(--u) * 28);
  z-index: 1;
  --floatY: calc(var(--u) * -1);
  animation-duration: 4.9s;
  animation-delay: -3.2s;
}
.cv-win--descargar {
  --x: 4%;
  --y: 64%;
  --w: calc(var(--u) * 24);
  z-index: 4;
  --floatY: calc(var(--u) * -1.05);
  animation-duration: 5s;
  animation-delay: -1.8s;
}

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

/* ---------- narrow screens + short landscape: plain vertical stack ----------
   Both portrait phones AND wide-but-short landscape phones drop the art-directed
   collage for a scrollable vertical stack. Routing short-landscape here (instead of
   leaving it on the desktop cqw collage) fixes the "marooned" tiny collage that
   `min(100%, 132vh)` produced at e.g. 844×390 — ~514px wide adrift in empty purple
   with a ~5px --u. The stack is a top-anchored scroll container that reserves a
   right rail so the vertically-centred "Volver" (back-btn--right, in its safe-area
   position) never overlaps a card — restoring the clearance the old mobile query
   dropped when it reset the desktop right gutter. */
@media (max-width: 760px), (orientation: landscape) and (max-height: 600px) {
  /* NON-scrolling, hard-clipped positioning context: pins .cv-bg to the cell (grid
     backdrop always covers the viewport, never scrolls away) and pins the top
     "Volver". The inner .subpage__scroll scrolls; padding-top carves the button zone. */
  .subpage--left {
    display: block;
    overflow: hidden;
    padding: calc(
        clamp(14px, 3vh, 28px) + clamp(52px, 14.5vw, 62px) +
          env(safe-area-inset-top, 0px) + 16px
      ) 0 0;
  }
  .subpage--left .subpage__scroll {
    align-items: center;
    padding: clamp(0.5rem, 2vh, 1rem) clamp(0.9rem, 4vw, 1.75rem)
      calc(2.5rem + env(safe-area-inset-bottom, 0px));
  }
  .cv {
    width: 100%;
    aspect-ratio: auto;
    /* keep an inline-size container so --u tracks the (rail-reduced) column width,
       not the viewport: a vw unit would over-size the window chrome and clip the
       pixel titles in the narrower stacked windows. Same cqw system as desktop. */
    container-type: inline-size;
    display: flex;
    flex-direction: column;
    gap: 1.4rem;
    --u: clamp(6px, 3.4cqw, 12px);
  }
  .win {
    position: static;
    width: 100%;
    animation: none; /* no per-card bobbing while the stack scrolls */
  }
  /* the CV download link is a real control — keep a ≥44px touch target (§5) */
  .cv-dl {
    min-height: 44px;
  }
}
/* ============================================================
   Contacto — retro browser window + contact card (Y2K / vaporwave)
   ============================================================
   The Contacto cell (.subpage--right) rebuilds the landing's browser chrome in
   mint, fills its content panel with a pixel headline, and sits a contact card
   (clickable email + phone) and a sad kitten beneath it, over a graph-paper grid.
   Self-contained like cv.css; imported once in main.ts. */

.subpage--right {
  --ink: #41565f; /* dark slate-teal: window glyphs */
  --mint: #a4e7dd;
  --mint-soft: #bdf0e8;
  --mint-deep: #7fd6c7;
  --pink: #ecc7ee;
  --pink-soft: #f6dcf6;
  --cream: #fdf7ea;
  --lav: #f3e6f8; /* content-panel fill */
  --line: #9a83d3; /* periwinkle sticker outline */
  --line-deep: #6f5ba8;
  --label: #f1ebfa; /* cool-white contact text */

  /* reserve a left lane for the floating "Volver" button (it lives on the left
     edge — the edge Contacto travelled in from) so the window clears it */
  padding: clamp(1rem, 4vh, 3rem) clamp(1rem, 4vw, 3.25rem);
  padding-left: clamp(140px, 14.5vw, 205px);
}

/* ---------- full-bleed graph-paper grid ----------
   Lighter lines on the purple base, filling the whole cell. main.ts owns its
   opacity: it cross-fades with the shared dream-haze's perspective grid as you
   move between the hub and Contacto. Like Hoja de vida, Contacto keeps the
   haze's glows + sparkles but swaps this flat grid in for the perspective one. */
.contact-grid {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.1) 2px, transparent 2px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.1) 2px, transparent 2px);
  background-size: clamp(40px, 4.2vw, 60px) clamp(40px, 4.2vw, 60px);
}

/* ---------- centred column: window over contact card ---------- */
.contact {
  position: relative;
  z-index: 1;
  width: min(960px, 100%);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: clamp(1.2rem, 5.5vh, 3.5rem);
}

/* ---------- browser window ---------- */
.contact-window {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--mint);
  border-radius: clamp(14px, 1.6vw, 22px);
  padding: clamp(5px, 0.7vw, 9px);
  box-shadow:
    0 22px 48px rgba(70, 45, 110, 0.32),
    0 0 0 2.5px var(--line),
    0 3px 0 rgba(255, 255, 255, 0.5) inset;

  --floatY: -12px;
  animation: float-y 5s ease-in-out infinite;
}

/* tab strip: mint (blends with the frame), holding the pink tab + window controls */
.cwin__bar {
  position: relative;
  height: clamp(30px, 4vh, 46px);
  display: flex;
  align-items: flex-end;
  justify-content: flex-end;
  padding: 0 clamp(10px, 1.2vw, 18px);
  background: linear-gradient(180deg, var(--mint-soft), var(--mint));
  border: 2.5px solid var(--line);
  border-radius: clamp(9px, 1vw, 15px) clamp(9px, 1vw, 15px) 0 0;
}

/* the pink tab tucked into the top-left, connecting down into the toolbar */
.cwin__tab {
  position: absolute;
  left: clamp(12px, 1.8vw, 26px);
  bottom: -2.5px;
  width: clamp(110px, 17vw, 215px);
  height: clamp(22px, 3vh, 36px);
  background: linear-gradient(180deg, #f3d2f3, var(--pink));
  border: 2.5px solid var(--line);
  border-bottom: none;
  border-radius: 11px 11px 0 0;
}

.cwin__controls {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: clamp(11px, 1.4vw, 20px);
  padding-bottom: clamp(6px, 0.9vh, 11px);
  color: var(--ink);
}
.cwin__control {
  position: relative;
  width: clamp(14px, 1.5vw, 20px);
  height: clamp(14px, 1.5vw, 20px);
}
.cwin__control--min::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 18%;
  height: 2.6px;
  background: currentColor;
  border-radius: 2px;
}
.cwin__control--max {
  border: 2.6px solid currentColor;
  border-radius: 3px;
}
.cwin__control--close::before,
.cwin__control--close::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: 2.6px;
  background: currentColor;
  border-radius: 2px;
}
.cwin__control--close::before {
  transform: rotate(45deg);
}
.cwin__control--close::after {
  transform: rotate(-45deg);
}

/* toolbar: back / forward / refresh + url pill + avatar (pink, like the landing) */
.cwin__nav {
  display: flex;
  align-items: center;
  gap: clamp(8px, 1.2vw, 18px);
  padding: clamp(7px, 1vh, 13px) clamp(10px, 1.3vw, 20px);
  background: var(--pink);
  border: 2.5px solid var(--line);
  border-top: none;
}

.cwin__actions {
  display: flex;
  align-items: center;
  gap: clamp(6px, 0.9vw, 13px);
  color: var(--ink);
  flex-shrink: 0;
}
.cwin__icon {
  width: clamp(19px, 2.1vw, 29px);
  height: clamp(19px, 2.1vw, 29px);
}
.cwin__icon svg {
  width: 100%;
  height: 100%;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.1;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.cwin__url {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  height: clamp(26px, 3.3vh, 40px);
  padding: 0 clamp(12px, 1.4vw, 20px);
  background: var(--cream);
  border: 2.5px solid var(--line);
  border-radius: 999px;
  box-shadow: 0 2px 5px rgba(70, 45, 110, 0.12) inset;
}
.cwin__mag {
  width: clamp(14px, 1.6vw, 20px);
  height: clamp(14px, 1.6vw, 20px);
  fill: none;
  stroke: var(--line-deep);
  stroke-width: 2.2;
  stroke-linecap: round;
}

.cwin__avatar {
  flex-shrink: 0;
  width: clamp(28px, 3.4vw, 44px);
  height: clamp(28px, 3.4vw, 44px);
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #c4f1e9, var(--mint-deep));
  border: 2.5px solid var(--line);
}

/* content panel: light-lavender, mint border, with the centred pixel headline */
.cwin__content {
  display: grid;
  place-items: center;
  margin-top: clamp(5px, 0.7vw, 9px);
  padding: clamp(2rem, 5.2vw, 4.5rem) clamp(1.1rem, 3.4vw, 2.6rem);
  background: var(--lav);
  border: clamp(3px, 0.42vw, 5px) solid var(--mint-deep);
  border-radius: clamp(8px, 1vw, 14px);
}

.contact__headline {
  margin: 0;
  font-family: 'Jersey 10', system-ui, monospace;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  line-height: 1.05;
  text-align: center;
  font-size: clamp(2.2rem, 6.9vw, 5.7rem);
  color: #1a1622;
}

/* ---------- contact card (clickable email + phone) ---------- */
.contact__info {
  align-self: start;
  display: flex;
  flex-direction: column;
  gap: clamp(0.7rem, 1.9vh, 1.35rem);
  padding-left: clamp(0.25rem, 1.5vw, 1.5rem);
}

.contact__row {
  display: flex;
  align-items: center;
  /* ≥44px tap target for the mailto:/tel: links at EVERY width. Inert on desktop
     (the badge is already ~56px there) but it lifts the rows to 44px on tablets,
     which fall between the mobile breakpoints below (768/1024 → 38/41px otherwise). */
  min-height: 44px;
  gap: clamp(0.7rem, 1.5vw, 1.25rem);
  text-decoration: none;
  color: var(--label);
  transition: transform 0.18s ease, filter 0.18s ease;
}
/* decorative hover nudge — gated so a tap on touch never leaves it stuck */
@media (hover: hover) and (pointer: fine) {
  .contact__row:hover {
    transform: translateX(4px);
    filter: drop-shadow(0 3px 12px rgba(255, 255, 255, 0.3));
  }
}
.contact__row:focus-visible {
  outline: 3px solid #fff;
  outline-offset: 5px;
  border-radius: 16px;
}

.contact__badge {
  flex-shrink: 0;
  display: grid;
  place-items: center;
  width: clamp(38px, 4vw, 56px);
  height: clamp(38px, 4vw, 56px);
  border-radius: 50%;
  background: var(--cream);
  color: #6e56b3;
  box-shadow: 0 4px 12px rgba(60, 40, 95, 0.28);
}
.contact__badge--at {
  font-family: 'Fredoka', system-ui, sans-serif;
  font-weight: 600;
  font-size: clamp(1.45rem, 2.5vw, 2.2rem);
  line-height: 1;
}
.contact__badge svg {
  width: 54%;
  height: 54%;
  fill: currentColor;
}

.contact__label {
  font-weight: 500;
  font-size: clamp(1.05rem, 2.3vw, 1.95rem);
  letter-spacing: 0.01em;
  color: var(--label);
}

/* ---------- the sad kitten, bottom-right ---------- */
.contact__cat {
  position: absolute;
  right: clamp(-22px, -1.6vw, 0px);
  bottom: clamp(-22px, -1.6vw, 0px);
  z-index: 2;
  width: clamp(160px, 24vw, 330px);
  height: auto;
  pointer-events: none;
  filter: drop-shadow(0 12px 24px rgba(60, 40, 95, 0.3));

  --floatY: -10px;
  animation: float-y 5.6s ease-in-out -1.5s infinite;
}

@media (prefers-reduced-motion: reduce) {
  .contact-window,
  .contact__cat {
    animation: none;
  }
}

/* ---------- Narrow screens + short landscape: turn the cell into a scroll
   container so the email/phone rows (the whole point of the page) are always
   reachable, shrink to fit, top-anchor the column, and keep it clear of the
   vertically-centred left-edge "Volver". Short landscape is folded in because at
   e.g. 844×390 the width-only query misses and the desktop-centred layout clips
   the rows below the fold with no way to scroll to them. ---------- */
@media (max-width: 760px), (orientation: landscape) and (max-height: 600px) {
  /* NON-scrolling, hard-clipped positioning context: pins .contact-grid, the corner
     cat and the top "Volver" to the cell; the inner .subpage__scroll scrolls +
     centres. padding-top carves the top button zone. */
  .subpage--right {
    display: block;
    overflow: hidden;
    padding: calc(
        clamp(14px, 3vh, 28px) + clamp(52px, 14.5vw, 62px) +
          env(safe-area-inset-top, 0px) + 16px
      ) 0 0;
  }
  /* centre the window + rows on BOTH axes; `safe` keeps the top reachable / scrollable
     when content is taller than the cell (short landscape). Bottom pad reserves the
     corner-cat zone so the centred content never lands on the cat. */
  .subpage--right .subpage__scroll {
    align-items: center;
    justify-content: safe center;
    padding: clamp(0.5rem, 2vh, 1rem)
      max(clamp(1rem, 4vw, 2rem), env(safe-area-inset-right, 0px))
      calc(clamp(105px, 31vw, 160px) + env(safe-area-inset-bottom, 0px))
      max(clamp(1rem, 4vw, 2rem), env(safe-area-inset-left, 0px));
  }
  .contact {
    width: 100%;
    min-width: 0;
    gap: clamp(1rem, 3vh, 1.6rem);
  }
  /* belt-and-braces for the narrowest phones: flex/grid children default to
     min-width:auto (won't shrink below their content), so pin them to 0 and cap
     width so the long email / wide window can never push past the cell. */
  .contact-window,
  .cwin__nav,
  .cwin__content,
  .contact__info,
  .contact__row {
    min-width: 0;
    max-width: 100%;
  }
  .cwin__content {
    padding: clamp(1.4rem, 6vw, 2.5rem) clamp(0.9rem, 3vw, 1.6rem);
  }
  .contact__headline {
    font-size: clamp(1.45rem, 6.6vw, 2.6rem);
  }
  /* The full-width window sits ABOVE the vertically-centred left-edge "Volver",
     so it never collides with it — but these rows sit at its centre band. Indent
     them past the button (rather than shrinking the whole window) so it can never
     overlap the email / phone. Short landscape shifts the whole cell instead and
     resets this below. */
  .contact__info {
    align-self: stretch;
  }
  /* ≥44px tap targets on the real controls (the mailto: / tel: rows) */
  .contact__row {
    min-height: 44px;
    padding-block: 0.4rem;
  }
  .contact__label {
    min-width: 0; /* let the long label shrink inside the flex row instead of pushing wide */
    font-size: clamp(0.9rem, 3.8vw, 1.35rem);
    overflow-wrap: anywhere; /* last-resort net: wrap rather than clip if space vanishes */
  }
  /* The email is one long unbroken token, so in the tight indented lane (~139px at
     360px, since the rows must clear the centred left-edge "Volver") it wrapped
     MID-WORD — "…@g" / "mail.com". Size it off the available width so it stays on a
     single line across the portrait widths (measured ~13.1px of run per 1px of
     font). The phone rule above (shorter, and it breaks only at its spaces) keeps
     the larger size; only the email shrinks. */
  .contact__row[href^='mailto:'] .contact__label {
    font-size: clamp(0.6rem, calc((100vw - 226px) / 13.4), 1.35rem);
  }
  .contact__badge {
    width: clamp(38px, 9vw, 46px);
    height: clamp(38px, 9vw, 46px);
  }
  .contact__cat {
    width: clamp(95px, 30vw, 155px);
  }
}

/* ---------- Short landscape (wide-but-short phones) only: the full-width window
   would run under the vertically-centred left-edge "Volver", so re-reserve a left
   lane (there's plenty of width here), swap the tall portrait cat-gutter for
   compact bottom padding, and shrink the corner cat so it never overlaps the
   column. Declared AFTER the block above so it wins where both match. ---------- */
@media (orientation: landscape) and (max-height: 600px) {
  /* short landscape: the cat is compact, so the wrapper needs only a small bottom
     pad — let the window + rows centre in the short cell. */
  .subpage--right .subpage__scroll {
    padding-bottom: calc(clamp(1rem, 5vh, 2rem) + env(safe-area-inset-bottom, 0px));
  }
  .contact__cat {
    width: clamp(80px, 12vw, 140px);
  }
}

/* ---------- Narrowest short landscape (e.g. 568×320) only: here the centred email
   row spans almost the full width, so even the compact corner cat above still
   covers its last characters ("gmail.co[m]"). Pull the cat into a small, fully
   on-screen bottom-right peek whose left edge clears the email row. Wider
   landscapes (≥~667px) already have room and keep the size above. ---------- */
@media (orientation: landscape) and (max-height: 600px) and (max-width: 640px) {
  .contact__cat {
    width: clamp(30px, 6vw, 46px);
    right: 0; /* no off-screen bleed — keep the whole (small) cat in the corner */
    bottom: 0;
  }
}
/* ============================================================
   Mis trabajos — config-driven collage gallery (Y2K / vaporwave)
   ============================================================
   The .subpage--bottom cell becomes a scroll container holding a sticky
   filing-cabinet tab strip + one themed panel per folder. Each panel's colors
   come from inline --f-* custom properties set by the renderer. Collage items
   are absolutely placed on a --u stage (the cv.css engine, generalized).
   Self-contained; imported once in main.ts. */

.subpage--bottom {
  /* net-new palette extras (only here) */
  --magenta: #e84fb0;
  --grey: #d9d6e0;

  /* the cell scrolls; content starts at the top, clearing the floating Volver
     (top edge) + the sticky tab strip */
  display: block;
  align-items: stretch;
  justify-items: stretch; /* neutralise the base .subpage place-items:center */
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain; /* don't scroll-chain to #app on mobile/short-landscape */
  -webkit-overflow-scrolling: touch;
  text-align: left;
  padding: 0;
  scrollbar-width: thin;
}

.works {
  position: relative;
  isolation: isolate; /* local stacking context — keeps the tab/panel z-indices contained */
  width: 100%; /* fill the full cell — never shrink-wrap to content */
  min-height: 100%;
  display: flex;
  flex-direction: column;
}

/* ---------- sticky filing-cabinet tab strip ---------- */
.works__tabs {
  /* desktop: in normal flow (not sticky) so each tab's z-index can interleave with
     the panel — the active tab sits IN FRONT of the panel (sharing one outline)
     while inactive tabs tuck BEHIND it. Re-stickied above the panel on mobile
     (which actually scrolls) for scroll-masking. */
  position: relative;
  top: 0;
  display: flex;
  align-items: flex-end;
  gap: 0;
  /* clear the floating Volver (top-centre): reserve height + a little side room.
     Volver sits top-centre and the tabs are left-aligned, so they never collide
     horizontally — keep this just tall enough to sit the tabs below the button. */
  padding: clamp(40px, 6vh, 64px) clamp(0.8rem, 3vw, 2.5rem) 0;
  /* opaque page-purple fill so the sticky strip masks content scrolling beneath
     it — the stair-step tabs alone leave transparent gaps the header bled through */
  background: var(--bg);
  /* pull the panel up so the tab bottoms overlap its top edge — the tabs read as
     attached to the cabinet, not floating above it */
  margin-bottom: -3px;
  pointer-events: none; /* re-enabled on the tabs themselves */
}

/* the tab strip owns the top-left→centre, so move Volver to the top-right corner
   (clear of the tabs) and keep it above the now-opaque sticky strip */
.subpage--bottom .back-btn--up {
  z-index: 6;
  left: auto;
  right: clamp(16px, 2.5vw, 40px);
  margin-inline: 0;
}

.works__tab {
  pointer-events: auto;
  position: relative;
  /* fixed stacking order: Ilustraciones (tab-index 0) sits in front, then Posts,
     Empaques, UI/UX toward the back — never re-ordered by which tab is active */
  /* all below the panel (z5) and ordered so Ilustraciones is front-most among the
     tucked-behind tabs; the active tab overrides this to sit in front of the panel */
  z-index: calc(4 - var(--tab-index, 0));
  /* a gentle uniform overlap (cascade) with more distance than the old
     progressive overlap that crammed the right-hand tabs together */
  margin-left: -0.5rem;
  padding: clamp(0.5rem, 1.4vh, 0.85rem) clamp(1rem, 2.4vw, 2rem)
           clamp(0.75rem, 1.5vh, 1.1rem);
  border: 2.5px solid var(--f-line);
  border-bottom: none;
  border-radius: 14px 14px 0 0;
  background: var(--f-tab); /* flat fill — same colour as the folder's panel body */
  color: var(--f-tab-ink, var(--f-title));
  font-family: 'Fredoka', system-ui, sans-serif;
  font-weight: 700;
  font-size: clamp(0.95rem, 1.9vw, 1.5rem);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  cursor: pointer;
  filter: brightness(0.9) saturate(0.92); /* inactive tabs recede behind the active one */
  transition: transform 0.18s ease, filter 0.18s ease;
}
.works__tab:first-child { margin-left: 0; } /* the leftmost tab aligns with the panel edge */
/* connection skirt: a tab-coloured tongue just below the tab. 0-height at rest
   (the -3px strip overlap already fuses the tab to the panel); it grows on hover
   to fill the gap the lift opens — so the tab rises AND stays attached. */
.works__tab::after {
  content: '';
  position: absolute;
  left: -2.5px;
  right: -2.5px;
  top: 100%;
  height: 0;
  background: var(--f-tab);
  transition: height 0.18s ease;
  pointer-events: none;
}
/* hover: an INACTIVE tab rises a little, the skirt bridging the gap (no colour
   change). The active tab never lifts — it's merged with the panel, so raising it
   would tear their shared border. */
/* Gate the lift behind fine-pointer/hover so a tap doesn't leave a tab stuck
   raised on touch (:focus-visible below still covers keyboard). */
@media (hover: hover) and (pointer: fine) {
  .works__tab:not(.works__tab--active):hover { transform: translateY(-4px); }
  .works__tab:not(.works__tab--active):hover::after { height: 9px; }
}
.works__tab:focus-visible { outline: 3px solid #fff; outline-offset: 3px; z-index: 5; }
.works__tab--active {
  z-index: 6;   /* IN FRONT of the panel (z5): the active tab's bottom covers the panel's
                   top border, so the tab + window read as one continuous outline */
  filter: none; /* full colour; the active tab matches the panel body it flows into */
}

/* ---------- panels ---------- */
.works__panels {
  position: relative;
  flex: 1;
  margin: 0 clamp(0.8rem, 3vw, 2.5rem) clamp(0.6rem, 2vh, 1.4rem);
}

.works__panel {
  display: none;
  position: relative;
  z-index: 5; /* in front of the tucked inactive tabs (z<=4), behind the active tab (z6) */
  border: 2.5px solid var(--f-line);
  border-radius: 0 clamp(14px, 1.6vw, 22px) clamp(14px, 1.6vw, 22px);
  background: var(--f-bg);
  box-shadow: 0 22px 48px var(--f-shadow); /* follow the per-folder theme, not a fixed purple */
  overflow: hidden;
}
.works__panel--active { display: block; }

.works__panel--bg-graph-grid::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.1) 2px, transparent 2px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.1) 2px, transparent 2px);
  background-size: clamp(40px, 4.2vw, 60px) clamp(40px, 4.2vw, 60px);
}

/* ---------- header (title + app icons, no background bar) ---------- */
.works__header {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 1rem;
  padding: clamp(0.6rem, 1.8vh, 1.1rem) clamp(1.2rem, 3vw, 2.4rem);
  /* no title — the active tab names the folder; header only holds the tools */
}

.works__title {
  margin: 0;
  font-family: 'Jersey 10', system-ui, monospace;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  line-height: 1;
  font-size: clamp(1.8rem, 5vw, 3.6rem);
  color: var(--f-title);
}
.works__title--rounded {
  font-family: 'Fredoka', system-ui, sans-serif;
  font-weight: 700;
}

.works__tools {
  display: flex;
  align-items: center;
  gap: clamp(8px, 1.4vw, 14px);
  flex-shrink: 0;
}
/* identical tile treatment to the /hoja-de-vida software logos (.win__logos img):
   rounded squircle (radius ≈ 0.217 × side), object-fit:contain, and the exact
   rgba(60,40,95,.28) sticker drop-shadow — just sized in px (header is off-stage,
   not on the --u collage) */
.works__tools img {
  width: clamp(28px, 3.4vw, 48px);
  height: clamp(28px, 3.4vw, 48px);
  object-fit: contain;
  border-radius: clamp(6px, 0.74vw, 10px);
  filter: drop-shadow(0 2px 3px rgba(60, 40, 95, 0.28));
}
/* "Herramientas:" caption sitting just left of the icon tiles (row gap spaces it) */
.works__tools-label {
  font-family: 'Fredoka', system-ui, sans-serif;
  font-weight: 600;
  font-size: clamp(1rem, 2.3vw, 1.6rem);
  letter-spacing: 0.01em;
  color: var(--f-title);
  white-space: nowrap;
}
/* tiny phones: drop the label so a long title (ILUSTRACIONES) can't crowd the icons */
@media (max-width: 440px) {
  .works__tools-label { display: none; }
}

/* ---------- the --u collage stage ---------- */
.works__stage {
  position: relative;
  z-index: 1;
  /* Cap the collage width by the height the cell leaves after the tab strip +
     header (cv.css's trick: it caps at 132vh; we reserve more for the chrome
     stacked above us). Both --u and the stage height derive from this width, so
     the whole block scales to fit the viewport instead of overflowing. */
  --reserved: 17rem;
  width: min(100%, calc((var(--app-h, 100vh) - var(--reserved)) * var(--stage-aspect, calc(200 / 119))));
  margin-inline: auto;
  container-type: size;
  --u: 1cqw;
  aspect-ratio: var(--stage-aspect, calc(200 / 119));
}
.works__stage--auto {
  container-type: inline-size;
  aspect-ratio: auto;
  min-height: 60cqw;
}

@media (prefers-reduced-motion: reduce) {
  .works__tab { transition: none; }
}

/* ---------- collage item base + float ---------- */
.works__item,
.works__blob,
.works__sticker,
.works__label {
  position: absolute;
  left: var(--x, 0%);
  top: var(--y, 0%);
  width: var(--w, auto);
  z-index: var(--z, 1);
  rotate: var(--rotate, 0deg);   /* tilt on the independent `rotate` channel */
  transform-origin: center;
  --floatY: -12px;
  animation: float-y 5s ease-in-out infinite;
}
.works__item--still { animation: none; }

.works__item {
  display: block;
  padding: 0;
  border: none;
  background: transparent;
  cursor: zoom-in;
  transition: transform 0.2s ease, filter 0.2s ease;
  animation: none; /* collage images stay still — no up/down bob (overrides the shared float) */
}
.works__media {
  display: block;
  width: 100%;
  height: auto;
  /* No own radius. The framed wrappers (sticker/window) clip via overflow:hidden
     to their *inner* radius (outer − border width); inheriting the outer radius
     rounded the image more than the border's inner curve, leaving a matte-colored
     gap at each corner. Let the clip do the rounding so it hugs the border. */
  border-radius: 0;
}

/* hover: lift + straighten upright + glow + raise (composes: translate=float,
   transform=lift, rotate=straighten) */
@media (hover: hover) and (pointer: fine) {
  .works__item:hover {
    transform: translateY(-4px) scale(1.04);
    filter: brightness(1.05) drop-shadow(0 8px 20px rgba(255, 120, 210, 0.5));
    z-index: 50;
  }
  .works__item:not(.works__item--no-straighten):hover { rotate: 0deg; }
}
.works__item:focus-visible { outline: 3px solid #fff; outline-offset: 4px; }

/* ---------- frames ---------- */
.frame--sticker {
  border-radius: var(--radius, calc(var(--u) * 1.6));
  border: calc(var(--u) * 0.3) solid var(--f-line);
  background: var(--matte, var(--f-bg));
  box-shadow: calc(var(--u) * 0.7) calc(var(--u) * 0.9) 0 var(--f-shadow);
  overflow: hidden;
}
.frame--plain {
  border-radius: 0;
  background: var(--matte, #fff);
  padding: calc(var(--u) * 0.8);
}
.frame--frameless {
  background: transparent;
  filter: drop-shadow(calc(var(--u) * 0.3) calc(var(--u) * 0.5) calc(var(--u) * 0.4) rgba(40, 30, 70, 0.35));
}
.frame--window {
  border-radius: calc(var(--u) * 1.4);
  border: calc(var(--u) * 0.3) solid var(--f-line);
  background: var(--matte, #efeaf7);
  box-shadow: calc(var(--u) * 0.7) calc(var(--u) * 0.9) 0 var(--f-shadow);
  overflow: hidden;
}

/* ---------- captions ---------- */
.works__caption {
  display: block;
  margin-top: calc(var(--u) * 0.6);
  font-family: 'Fredoka', system-ui, sans-serif;
  font-weight: 500;
  font-size: calc(var(--u) * 1.8);
  color: var(--f-ink);
}
.works__caption--pixel { font-family: 'Jersey 10', system-ui, monospace; text-transform: uppercase; }
.works__caption--overlay {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  margin: 0;
  padding: calc(var(--u) * 0.6) calc(var(--u) * 1);
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.55));
  color: #fff;
}

/* ---------- decorative blob ---------- */
.works__blob {
  background: var(--blob-color, var(--f-accent));
  aspect-ratio: 1.15 / 1;
  animation-duration: 7s;
}
.works__blob--blob-1 { border-radius: 47% 53% 63% 37% / 42% 38% 62% 58%; }
.works__blob--blob-2 { border-radius: 62% 38% 41% 59% / 56% 47% 53% 44%; }
.works__blob--ellipse { border-radius: 50%; }

/* ---------- decorative sticker (reuse the .spark sparkle silhouette) ---------- */
.works__sticker {
  width: var(--sticker-size, calc(var(--u) * 2.4));
  aspect-ratio: 1;
  background: var(--sticker-color, #fff);
  filter: drop-shadow(0 0 4px color-mix(in srgb, var(--sticker-color, #fff) 70%, transparent));
}
.works__sticker--sparkle {
  clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%);
}
.works__sticker--star {
  clip-path: polygon(50% 0, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);
}
.works__sticker--heart { border-radius: 50% 50% 0 0 / 60% 60% 0 0; rotate: 45deg; }

/* ---------- decorative label ---------- */
.works__label {
  font-family: 'Fredoka', system-ui, sans-serif;
  font-weight: 600;
  font-size: calc(var(--u) * 2.4);
  color: var(--f-ink);
  white-space: nowrap;
  animation-duration: 6s;
}
.works__label--pixel { font-family: 'Jersey 10', system-ui, monospace; text-transform: uppercase; }

/* ---------- featured video + "Ver más" card ---------- */
.works__featured { cursor: pointer; }
.works__featured .works__media { aspect-ratio: 886 / 666; object-fit: cover; }

/* case-study QR — small framed white square on the right of the panel (static, no float) */
.works__qr {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--w);
  z-index: var(--z, 2);
  display: block;
  aspect-ratio: 1;
  object-fit: contain;
  background: #fff;
  border-radius: calc(var(--u) * 1.2);
  border: calc(var(--u) * 0.3) solid var(--f-line);
  padding: calc(var(--u) * 0.8);
  box-shadow: calc(var(--u) * 0.7) calc(var(--u) * 0.9) 0 var(--f-shadow);
}

.works__vermas {
  position: absolute;
  left: var(--x); top: var(--y); width: var(--w);
  z-index: var(--z, 3);
  display: flex;
  flex-direction: column;
  gap: calc(var(--u) * 1.2);
  color: var(--f-ink);
}
.works__vermas-name {
  margin: 0;
  font-family: 'Jersey 10', system-ui, monospace;
  font-weight: 400;
  text-transform: uppercase;
  font-size: calc(var(--u) * 5);
  color: var(--f-title);
}
.works__vermas-blurb {
  margin: 0;
  font-weight: 500;
  font-size: calc(var(--u) * 1.9);
  line-height: 1.4;
}
.works__vermas-btn {
  align-self: start;
  padding: calc(var(--u) * 0.8) calc(var(--u) * 2.2);
  border: calc(var(--u) * 0.26) solid var(--f-title);
  border-radius: 999px;
  background: var(--f-title);
  color: #fff;
  font-family: 'Fredoka', system-ui, sans-serif;
  font-weight: 700;
  font-size: calc(var(--u) * 1.9);
  cursor: pointer;
  transition: transform 0.18s ease, filter 0.18s ease;
}
@media (hover: hover) and (pointer: fine) {
  .works__vermas-btn:hover { transform: translateY(-2px) scale(1.04); filter: brightness(1.08); }
}
.works__vermas-btn:focus-visible { outline: 3px solid #fff; outline-offset: 3px; }
/* UI/UX: the Párchese title + Ver más button use the brand magenta */
.works__panel[data-folder="uiux"] .works__vermas-name { color: #e4016b; }
.works__panel[data-folder="uiux"] .works__vermas-btn { background: #e4016b; border-color: #e4016b; }

@media (prefers-reduced-motion: reduce) {
  .works__item, .works__blob, .works__sticker, .works__label { animation: none; }
  .works__item:hover { transform: none; }
}

/* ---------- freeze collage floats behind an open overlay ---------- */
.works--frozen .works__item,
.works--frozen .works__blob,
.works--frozen .works__sticker,
.works--frozen .works__label { animation-play-state: paused; }

/* ---------- lightbox ---------- */
.works-lightbox {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: none;
  place-items: center;
}
.works-lightbox--open { display: grid; }

.works-lightbox__backdrop {
  position: absolute;
  inset: 0;
  background: radial-gradient(60% 60% at 50% 45%, rgba(60, 40, 95, 0.7), rgba(30, 20, 50, 0.88));
  backdrop-filter: blur(6px);
  animation: works-fade 0.3s ease both;
}
.works-lightbox__panel {
  position: relative;
  display: flex;
  align-items: center;
  gap: clamp(0.5rem, 2vw, 1.5rem);
  width: min(92vw, 1100px);
  max-height: 88vh;
  animation: works-pop 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
.works-lightbox__stage {
  flex: 1;
  display: grid;
  place-items: center;
  min-width: 0;
}
.works-lightbox__stage img,
.works-lightbox__stage video {
  max-width: 100%;
  max-height: 88vh;
  border-radius: clamp(10px, 1.4vw, 18px);
  border: 3px solid #fff;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
}
.works-lightbox__close {
  position: absolute;
  top: clamp(-46px, -5vh, -10px);
  right: 0;
  width: clamp(36px, 4vw, 48px);
  height: clamp(36px, 4vw, 48px);
  border-radius: 50%;
  border: 2.5px solid #fff;
  background: rgba(255, 255, 255, 0.15);
  color: #fff;
  font-size: 1.2rem;
  cursor: pointer;
}
.works-lightbox__nav {
  flex-shrink: 0;
  width: clamp(40px, 5vw, 60px);
  height: clamp(40px, 5vw, 60px);
  border-radius: 50%;
  border: 2.5px solid #fff;
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
  font-size: 1.8rem;
  line-height: 1;
  cursor: pointer;
  transition: transform 0.18s ease, background 0.18s ease;
}
@media (hover: hover) and (pointer: fine) {
  .works-lightbox__nav:hover { transform: scale(1.08); background: rgba(255, 255, 255, 0.25); }
}
.works-lightbox__close:focus-visible,
.works-lightbox__nav:focus-visible { outline: 3px solid #fff; outline-offset: 3px; }

@keyframes works-fade { from { opacity: 0; } to { opacity: 1; } }
/* used by the lightbox panel (and only here — the collage entrance is JS/WAA) */
@keyframes works-pop { 0% { opacity: 0; scale: 0.9; } 100% { opacity: 1; scale: 1; } }

@media (prefers-reduced-motion: reduce) {
  .works-lightbox__backdrop, .works-lightbox__panel { animation: none; }
}

/* ---------- case-study mode ---------- */
.works-lightbox--case .works-lightbox__panel { display: block; }
/* case mode = a big card, so sit the close on its top-right corner (not floating above) */
.works-lightbox--case .works-lightbox__close { top: clamp(12px, 1.6vw, 18px); right: clamp(12px, 1.6vw, 18px); }
.works-case {
  width: min(92vw, 1100px);
  max-height: 88vh;
  overflow-y: auto;
  padding: clamp(1.5rem, 4vw, 3rem);
  background: #fdf7ea;
  border-radius: clamp(12px, 1.6vw, 22px);
  border: 3px solid #fff;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
}
.works-case__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: clamp(1rem, 3vw, 2.5rem);
  margin-bottom: clamp(1rem, 2.5vw, 2rem);
  padding-right: clamp(40px, 5vw, 56px); /* clear the top-right close button */
}
.works-case__text { min-width: 0; }
.works-case__name {
  margin: 0 0 0.4em;
  font-family: 'Jersey 10', system-ui, monospace;
  font-weight: 400;
  text-transform: uppercase;
  font-size: clamp(2rem, 6vw, 4rem);
  color: #e4016b;
}
.works-case__blurb {
  margin: 0;
  font-weight: 500;
  font-size: clamp(1rem, 2vw, 1.4rem);
  line-height: 1.5;
  color: #2a2630;
  max-width: 60ch;
}
.works-case__shots {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
  gap: clamp(0.8rem, 2vw, 1.5rem);
}
.works-case__shots img {
  width: 100%;
  border-radius: 10px;
  box-shadow: none; /* override the stage-img shadow that leaks onto the nested case shots */
}
.works-case__qr {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  flex-shrink: 0;
  text-decoration: none;
  color: #e4016b;
  font-family: 'Jersey 10', system-ui, monospace;
  text-transform: uppercase;
  font-size: clamp(1rem, 2vw, 1.4rem);
}
.works-case__qr img { width: clamp(120px, 18vw, 180px); border-radius: 8px; box-shadow: none; }

/* Short-wide landscape phones (e.g. 844×390, 915×412, 932×430) are wider than 760
   so they miss the stack breakpoint, and the height-capped width formula collapses
   the collage to a sliver on a short viewport. Decouple: drive the stage from the
   panel WIDTH and let the cell scroll vertically — keeping the art-directed collage
   where the width allows (decision #1). Placed BEFORE the ≤760 stack block so a
   phone that is BOTH ≤760 and short-landscape (667×375, 740×360) still restacks. */
@media (orientation: landscape) and (max-height: 600px) {
  .works__stage,
  .works__stage--auto {
    width: min(100%, 1000px);
  }
}

/* Portrait tablets (e.g. 768×1024, 820×1180, 1024×1366): the collage is
   width-limited (the tall viewport never caps it) so it fills only the top ~45%
   of the cell, leaving a large purple void below. Let the active panel fill the
   cell height and centre the collage stage in its body — the leftover space
   becomes the window's own themed interior instead of a purple void.
   Scoped to portrait AND width 768–1439 so it never touches phones (<768),
   any landscape view (incl. 1024×768 and desktop), or the >=1440 desktop. */
@media (min-width: 761px) and (max-width: 1439px) and (orientation: portrait) {
  .works__panels {
    display: flex;
    flex-direction: column;
  }
  .works__panel--active {
    flex: 1;
    display: flex;
    flex-direction: column;
  }
  .works__stage {
    margin-block: auto; /* centre in the panel body, below the header row */
  }
}

/* ---------- narrow screens: tab row + stacked collage ---------- */
@media (max-width: 760px) {
  .works__tabs {
    position: sticky; /* mobile scrolls — re-stick + raise above the panel to mask content */
    padding: clamp(48px, 11vh, 72px) 0.6rem 0;
    overflow-x: auto;
    overflow-y: hidden;
    flex-wrap: nowrap;
    scrollbar-width: none;
    z-index: 5;
  }
  /* edge-fade affordance: the row scrolls horizontally with its scrollbar hidden,
     so the 5th tab (UI/UX) sits off-screen with no hint. A sticky gradient pinned
     to the right edge fades the row out there, signalling "more →". Being a
     net-zero-width sticky flex item, it parks at the content end (invisibly, over
     the purple strip) when the tabs fit, and pins to the visible edge only once the
     row overflows — so it never shows a false hint. */
  .works__tabs::after {
    content: '';
    position: sticky;
    right: 0;
    align-self: stretch;
    flex: 0 0 1.75rem;
    margin-left: -1.75rem; /* overlay the edge tab; add no scroll width */
    z-index: 7;            /* above the active tab (z6) */
    background: linear-gradient(to left, var(--bg), transparent);
    pointer-events: none;
  }
  .works__panel { z-index: 0; }
  .works__tab {
    margin-left: 0;
    margin-bottom: 0;
    flex-shrink: 0;
    font-size: clamp(0.8rem, 3.4vw, 1.1rem);
    /* >=44px tap target on narrow/short phones (the base padding alone leaves the
       tabs ~37px). Flex-centre the label so the taller box stays balanced; the row
       still scrolls horizontally (flex-shrink:0 + the nowrap parent + edge fade). */
    min-height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .works__tab--active { transform: none; }

  .works__panels { margin: 0 0.6rem 1.5rem; }
  .works__header { clip-path: none; padding: 0.8rem 1rem; }
  .works__title { font-size: clamp(1.5rem, 7vw, 2.4rem); }

  /* the --u collage drops to a plain vertical stack (cv.css's mobile flip) */
  .works__stage,
  .works__stage--auto {
    container-type: normal;
    aspect-ratio: auto;
    min-height: 0;
    /* fill the panel width — never inherit the height-capped formula, which
       collapses the stack to a narrow column on short landscape (≤760, e.g.
       667×375) where the height term wins the min() */
    width: 100%;
    display: flex;
    flex-direction: column;
    gap: 1.2rem;
    padding: 1.2rem 1rem;
    --u: clamp(7px, 2.7vw, 12px);
  }
  .works__item,
  .works__blob,
  .works__sticker,
  .works__label,
  .works__featured,
  .works__vermas,
  .works__qr {
    position: static;
    left: auto; top: auto;
    width: 100%;
    rotate: 0deg;
  }
  .works__qr { width: clamp(120px, 40vw, 170px); align-self: center; }
  .works__blob,
  .works__sticker { display: none; }         /* backdrop decorations make no sense in a stack */
  .works__vermas { width: 100%; }

  .works-lightbox__panel { width: 94vw; }
  .works-lightbox__stage { width: 100%; }
  /* overlay the nav arrows on the image edges instead of flanking it in the row,
     so a narrow-screen image keeps the full panel width (no swipe — decision #2) */
  .works-lightbox__nav {
    position: absolute;
    top: 50%;
    margin-top: -22px; /* half of the 44px target — centre without a transform so
                          it composes with the hover scale */
    z-index: 2;
    background: rgba(20, 12, 40, 0.5);
    font-size: 1.5rem;
  }
  .works-lightbox__nav--prev { left: 0.35rem; }
  .works-lightbox__nav--next { right: 0.35rem; }
  .works-lightbox__close { top: -42px; font-size: 1.25rem; }
}

/* Touch polish shared by mobile and short-landscape: ≥44px lightbox targets and a
   wrappable case-study head so the QR drops below the blurb instead of crushing it.
   After the ≤760 block so it wins the nav/close sizing in both. */
@media (max-width: 760px), (orientation: landscape) and (max-height: 600px) {
  .works-lightbox__nav,
  .works-lightbox__close { width: 44px; height: 44px; }
  .works-case__head { flex-wrap: wrap; }
}

@media (prefers-reduced-motion: reduce) {
  .works__featured .works__media { animation: none; }
  .works__vermas-btn, .works-lightbox__nav { transition: none; }
}
