*,
*::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: 92vw;
    gap: clamp(10px, 2.6vw, 18px);
    padding: clamp(9px, 1.4vh, 15px);
    padding-left: clamp(20px, 5vw, 32px);
  }
  .search-pill__text {
    font-size: clamp(1.35rem, 6.2vw, 2rem);
  }
  .search-pill__btn {
    width: clamp(46px, 11vw, 58px);
    height: clamp(42px, 10vw, 52px);
  }
}

@media (max-width: 440px) {
  .search-pill {
    gap: clamp(8px, 2.4vw, 14px);
    padding-left: clamp(16px, 4.5vw, 26px);
  }
  /* bigger, but stay on one line — the pill grows wider instead of wrapping */
  .search-pill__text {
    font-size: clamp(1.2rem, 5.8vw, 1.75rem);
  }
}

/* ---------- 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;
    /* short landscape has little vertical room — keep the pill compact here, overriding
       the bigger portrait-phone sizing above (this block wins as the last match) */
    gap: clamp(8px, 1.4vw, 14px);
    padding: clamp(6px, 1vh, 10px);
    padding-left: clamp(16px, 2.2vw, 26px);
  }
  .search-pill__text {
    font-size: clamp(1.15rem, 2.8vw, 1.7rem);
  }
  .search-pill__btn {
    width: clamp(38px, 4.4vw, 50px);
    height: clamp(34px, 3.9vw, 44px);
  }
}

@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;
}

/* ---------- 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 on pale cream, so sit the close on its top-right corner and
   paint it the Párchese brand red (#e4016b — the case name/QR colour); a plain white
   button vanished against the cream. Applies on every viewport (the card is cream on
   desktop too). */
.works-lightbox--case .works-lightbox__close {
  top: clamp(12px, 1.6vw, 18px);
  right: clamp(12px, 1.6vw, 18px);
  background: #e4016b;
  border-color: #fff;
  color: #fff;
}
.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-landscape phones are handled together with narrow portrait in the unified
   mobile block below — the same pinned-Volver + inner-scroll + gallery-grid.) */

/* 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 */
  }
}

/* ============================================================
   MOBILE + SHORT-LANDSCAPE — unified with the other subpages.
   Pin "Volver" on the non-scrolling cell, make .works the scroll
   container (so Volver never scrolls away), clean the horizontal
   tab strip, and drop the collage to a 2-column gallery grid.
   Desktop (>=761 & tall, and the portrait-tablet tier) is untouched.
   ============================================================ */
@media (max-width: 760px), (orientation: landscape) and (max-height: 600px) and (max-width: 1439px) {
  /* The CELL stops scrolling: a hard-clipped positioning context whose top padding
     carves the pinned-"Volver" zone (offset + button height + safe-area + gap), so
     .works (and its sticky tab strip) begins cleanly below the button. */
  .subpage--bottom {
    display: block;
    overflow: hidden;
    padding: calc(
        clamp(14px, 3vh, 28px) + clamp(52px, 14.5vw, 62px) +
          env(safe-area-inset-top, 0px) + 16px
      ) 0 0;
  }
  /* "Volver" back to TOP-CENTRE (the global right-corner rule is desktop-only now) —
     identical placement + shared .back-btn size as the other three pages. Pinned to
     the non-scrolling cell (above .works' stacking context) so it never scrolls. */
  .subpage--bottom .back-btn--up {
    left: 0;
    right: 0;
    margin-inline: auto;
    z-index: 6;
  }

  /* .works is THE scroll container, filling the padded cell. */
  .works {
    height: 100%;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    overscroll-behavior: contain; /* don't scroll-chain to #app */
    -webkit-overflow-scrolling: touch;
  }

  /* ---- floating pill tabs: wrap to multiple rows, no horizontal scroll ---- */
  .works__tabs {
    position: sticky;
    top: 0;
    z-index: 5; /* own stacking context, sitting above the panel scrolling beneath it */
    display: flex;
    flex-wrap: wrap; /* rows wrap instead of scrolling — every folder stays on screen */
    align-items: center;
    justify-content: center;
    gap: clamp(0.4rem, 1.8vw, 0.6rem);
    padding: clamp(10px, 2.5vh, 16px) 0.7rem clamp(12px, 2.4vh, 16px);
    overflow: visible; /* no scroll; the row wraps */
    margin-bottom: 0;  /* pills float above the panel — drop the filing-cabinet overlap */
    /* background: var(--bg) (inherited) stays opaque so content scrolls hidden behind it */
  }
  /* the horizontal-scroll edge fades go away with the scroll */
  .works__tabs::before,
  .works__tabs::after { content: none; }

  .works__panel {
    z-index: 0; /* below the sticky strip, so content scrolls under it */
    /* detached pills → the card rounds all four corners (the base squares the top-left
       to seat the desktop filing-cabinet tab, which no longer attaches here) */
    border-radius: clamp(14px, 1.6vw, 22px);
  }

  /* each tab is a self-contained pill (full outline + radius) tinted with its folder
     theme; inactive ones recede (base filter), the active one pops (scale + deeper lift) */
  .works__tab {
    margin: 0;
    flex: 0 1 auto; /* size to the label, wrap when the row fills */
    min-height: 44px; /* >=44px tap target; flex-centre the label */
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0.5rem clamp(0.95rem, 3.8vw, 1.35rem);
    border: 2.5px solid var(--f-line); /* full outline — base drops the bottom edge */
    border-radius: 14px;               /* match the desktop tab's 14px corner (all four,
                                          since the floating button is detached) */
    font-size: clamp(0.8rem, 3.4vw, 1.05rem);
    box-shadow: 0 3px 9px var(--f-shadow); /* lift off the purple bg → "floating" */
    transition: transform 0.16s ease, box-shadow 0.16s ease, filter 0.16s ease;
  }
  .works__tab--active {
    transform: scale(1.06); /* the selected pill pops forward */
    box-shadow: 0 5px 15px var(--f-shadow);
    /* filter:none (base rule) → full colour against the dimmed siblings */
  }

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

  /* ---- 2-column gallery grid: small thumbnails, minimal scroll, gallery order ---- */
  .works__stage,
  .works__stage--auto {
    container-type: normal;
    aspect-ratio: auto;
    min-height: 0;
    width: 100%;
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-auto-flow: dense; /* pack thumbnails tightly — a later thumb backfills the cell
                              a taller piece leaves beside it (no lone gaps in the grid) */
    align-items: start;
    gap: clamp(0.6rem, 2.5vw, 1rem);
    padding: clamp(0.8rem, 3vw, 1.2rem);
    /* frame chrome (border/shadow/caption) scales with the thumbnail, not the stage */
    --u: clamp(4px, 1.7vw, 8px);
  }
  .works__item,
  .works__blob,
  .works__sticker,
  .works__label,
  .works__featured,
  .works__vermas,
  .works__qr {
    position: static;
    left: auto;
    top: auto;
    width: 100%;
    rotate: 0deg;
  }
  /* hero/banner pieces (w>=40u) + the featured video + the "Ver más" card span the
     full row; a lone Empaques board (w:84) is wide too, so it fills the width. */
  .works__item--wide,
  .works__featured,
  .works__vermas {
    grid-column: 1 / -1;
  }
  .works__qr {
    grid-column: 1 / -1;
    justify-self: center;
    width: clamp(120px, 40vw, 170px);
  }
  /* The UI/UX panel ends on the QR; pad the stage bottom so the QR has breathing room
     before the panel edge. (A grid item's own margin-bottom overflows the track-based
     grid height instead of extending it, so the container is padded instead.) */
  .works__panel[data-folder='uiux'] .works__stage {
    padding-bottom: clamp(2rem, 9vw, 3.5rem);
  }
  .works__blob,
  .works__sticker,
  .works__label { display: none; } /* backdrop decorations make no sense in a gallery grid */

  /* ---- lightbox: full-width image, arrows overlaid on the edges (no swipe) ---- */
  .works-lightbox__panel { width: 94vw; }
  .works-lightbox__stage { width: 100%; }
  .works-lightbox__nav {
    position: absolute;
    top: 50%;
    margin-top: -22px; /* half the 44px target — centre without a transform */
    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; }
}

/* Wide-but-short landscape phones (e.g. 844×390) have room for 3 columns — keeps
   images from towering past the short viewport. After the unified block so it wins. */
@media (orientation: landscape) and (max-height: 600px) and (max-width: 1439px) {
  .works__stage,
  .works__stage--auto {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* 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; }
}
