/* Lucas López · capturesluk. "Film".
   Warm black with a quiet animated grain and a faint warm vignette. Both live on a fixed layer
   under the content, so they show on the black only and never over a photo. Dark only, on purpose:
   his stage work is shot on black and his couples glow at golden hour; one warm ground carries both.
   General Sans 400/500 (Fontshare, ITF Free Font License), kept small; the cover's tagline alone is set in Clash Grotesk 500
   (same foundry and licence): the one line that has to read as a cover (amendment 6). */

@font-face { font-family: "General Sans"; font-style: normal; font-weight: 400; font-display: swap; src: url(../fonts/general-sans-400.woff2) format("woff2"); }
@font-face { font-family: "General Sans"; font-style: normal; font-weight: 500; font-display: swap; src: url(../fonts/general-sans-500.woff2) format("woff2"); }
@font-face { font-family: "Clash Grotesk"; font-style: normal; font-weight: 500; font-display: swap; src: url(../fonts/clash-grotesk-500.woff2) format("woff2"); }

:root {
  --ink: #0b0a09;
  --ink-2: #14110e;
  --lift: rgba(44, 35, 26, .44); /* a lifted warm surface (about #181410 over the ink), translucent so the grain still shows */
  --paper: #ece6dc;
  --text: #d3ccc1;          /* reading text, 12.4:1 */
  --mute: #9a9286;          /* 6.4:1 on --ink */
  --line: rgba(236, 230, 220, .14);
  --line-2: rgba(236, 230, 220, .32);
  --amber: #d9a86a;         /* the one accent: focus, current page, active rules */
  --ease: cubic-bezier(.22, 1, .36, 1);
  --font: "General Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --g: max(40px, env(safe-area-inset-left, 0px), env(safe-area-inset-right, 0px)); /* side gutter */
  --gap: 20px;              /* photo gap */
  --hh: 72px;               /* header height */
  color-scheme: dark;
}
@media (max-width: 760px) { :root { --g: max(20px, env(safe-area-inset-left, 0px), env(safe-area-inset-right, 0px)); --gap: 10px; --hh: 60px; } }

*, *::before, *::after { box-sizing: border-box; }
/* the ground is painted on html only: body stays transparent so the fixed grain layer (z-index -1) shows between photos */
html { -webkit-text-size-adjust: 100%; background: var(--ink); scroll-padding-top: calc(var(--hh) + 40px); }
html:focus-within { scroll-behavior: smooth; } /* in-page links glide; arriving at /#trabajo from another page jumps */
html:has(dialog[open]), html.menu-open { overflow: hidden; }
/* moving between pages (and between ES and EN) cross-fades instead of flashing: native cross-document view transition */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
  ::view-transition-group(root) { animation-duration: .3s; animation-timing-function: var(--ease); }
}
body {
  margin: 0; color: var(--paper);
  font: 400 15px/1.55 var(--font); letter-spacing: .005em;
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-rendering: optimizeLegibility;
  overflow-x: clip;
  text-wrap: pretty; /* no one-word last lines */
}
img { display: block; max-width: 100%; height: auto; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; letter-spacing: inherit; }
h1, h2, h3, p, figure, ul, ol { margin: 0; padding: 0; }
h1, h2, h3 { font-weight: 400; }
ul, ol { list-style: none; }
main:focus { outline: 0; }
:focus-visible { outline: 1px solid var(--amber); outline-offset: 4px; }
::selection { background: var(--amber); color: var(--ink); }
.vh { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

.skip {
  position: fixed; left: var(--g); top: 12px; z-index: 40;
  background: var(--paper); color: var(--ink); padding: 10px 16px; font-size: 14px; font-weight: 500;
  transform: translateY(-180%); transition: transform .3s var(--ease);
}
.skip:focus { transform: none; }
.sentinel { position: absolute; top: 0; left: 0; width: 1px; height: 40px; pointer-events: none; }

/* ---------- the touch: darkroom vignette + grain, under everything ---------- */
.room { position: fixed; inset: 0; z-index: -1; pointer-events: none; overflow: hidden; background: radial-gradient(130% 95% at 50% 42%, transparent 52%, rgba(28, 15, 6, .34) 100%); }
.room::after, .cover__grain {
  content: ""; position: absolute; inset: -180px; pointer-events: none;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.92' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 .94 0 0 0 0 .86 1.7 0 0 0 -.62'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
.room::after { opacity: .055; animation: grain 1s steps(1) infinite; }
@keyframes grain {
  0% { transform: translate(0, 0); } 17% { transform: translate(-121px, 67px); }
  33% { transform: translate(83px, -157px); } 50% { transform: translate(-49px, 139px); }
  67% { transform: translate(163px, 31px); } 83% { transform: translate(-139px, -89px); }
}

/* ---------- links and buttons ---------- */
/* underline that draws in, never snaps */
.u { position: relative; transition: color .35s var(--ease); }
.u::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -3px; height: 1px; background: currentColor;
  transform: scaleX(0); transform-origin: right; transition: transform .45s var(--ease), opacity .4s var(--ease);
}
.u:hover::after, .u[aria-current]::after { transform: scaleX(1); transform-origin: left; }
/* a 44 px tall hit area around every text link, invisible: the look and the line box stay as they are */
.u::before { content: ""; position: absolute; left: -6px; right: -6px; top: 50%; height: 44px; translate: 0 -50%; }
.u[aria-current]::after { background: var(--amber); }
.btn {
  display: inline-flex; align-items: center; gap: 10px; white-space: nowrap;
  font: 500 14px/1 var(--font); padding: 13px 20px; border-radius: 2px; min-height: 44px;
  background: var(--paper); color: var(--ink);
  transition: background-color .4s var(--ease);
}
.btn:hover { background: #fff; }
.btn svg { flex: none; transition: transform .45s var(--ease); }
.btn:hover svg { transform: translateX(3px); }
.btn[target]:hover svg { transform: translate(2px, -2px); }
/* the booking button leaves the site: a small «nueva pestaña» cue rises above it on hover and focus only */
.btn[target] { position: relative; }
.cue { position: absolute; left: 0; top: calc(100% + 8px); font: 400 12px/1 var(--font); color: var(--mute); white-space: nowrap; pointer-events: none; opacity: 0; transform: translateY(-4px); transition: opacity .35s var(--ease), transform .45s var(--ease); }
.btn[target]:is(:hover, :focus-visible) .cue { opacity: 1; transform: none; }
.cap .cue { color: rgba(236, 230, 220, .78); text-shadow: 0 1px 2px rgba(8, 6, 5, .5), 0 3px 12px rgba(8, 6, 5, .4); }
.ctas { display: flex; flex-wrap: wrap; align-items: center; gap: 16px 26px; font-size: 14px; }
.ctas .u::after { transform: scaleX(1); opacity: .45; }
.ctas .u:hover::after { opacity: 1; background: var(--amber); }

/* ---------- header: one quiet line ---------- */
.top {
  position: fixed; inset: 0 0 auto; z-index: 30;
  display: flex; align-items: center; gap: 30px;
  height: var(--hh); padding: 0 var(--g); font-size: 14px;
}
/* the blur lives on a pseudo-element, and the phone menu is a sibling of the header, not a child:
   a backdrop-filter parent would become the containing block of a fixed menu and shrink it to a strip */
.top::after {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none; opacity: 0;
  background: rgba(11, 10, 9, .8); -webkit-backdrop-filter: blur(16px) saturate(1.1); backdrop-filter: blur(16px) saturate(1.1);
  border-bottom: 1px solid var(--line);
  transition: opacity .5s var(--ease);
}
.top.is-solid::after { opacity: 1; }
.menu-open .top::after { opacity: 0; }
/* over the cover, a soft shade keeps the quiet header readable on bright frames (sunset sky) */
.p-home .top:not(.is-solid) { text-shadow: 0 0 14px rgba(8, 6, 5, .45); }
.p-home .top:not(.is-solid) .lang a:not([aria-current]) { color: rgba(236, 230, 220, .72); }
.brand { margin-right: auto; padding: 10px 0 13px; transform-origin: 0 0; } /* a 42 px box: the logo sits on whole pixels and the name's baseline on the menu's */ /* the Frame lockup: 16 px mark, 19 px box (the «p» descender) */
.logo { display: block; overflow: visible; } /* ivory and the one amber point, no shadow */
/* hover (fine pointers) and keyboard focus: the frame breathes, its two L's ease about 3 px apart each, and the amber point
   grows, brightens and leans up to 2 px toward the pointer (site.js, lerped). translate/scale only, so the title sequence's
   transform animations on the same shapes are untouched; reduced motion keeps the colour change alone */
.logo :is(.lv, .lh) { transition: translate .5s var(--ease); }
.logo .pt { transform-origin: 50% 50%; transition: scale .5s var(--ease), fill .4s var(--ease); }
@media (hover: hover) and (pointer: fine) { .brand:hover .logo :is(.lv, .lh):not(.lr) { translate: -3px 1.5px; } .brand:hover .logo .lr { translate: 3px -1.5px; } .brand:hover .logo .pt { scale: 1.5; fill: #f4cd96; } }
.brand:focus-visible .logo :is(.lv, .lh):not(.lr) { translate: -3px 1.5px; }
.brand:focus-visible .logo .lr { translate: 3px -1.5px; }
.brand:focus-visible .logo .pt { scale: 1.5; fill: #f4cd96; }
.nav ul { display: flex; gap: 30px; }
.nav a { display: inline-block; padding: 10px 0; white-space: nowrap; }
.nav a::after { bottom: 7px; }
.lang { display: flex; gap: 2px; font-size: 13px; }
.lang a { padding: 10px 4px; color: var(--mute); transition: color .35s var(--ease); }
.lang a:hover, .lang a[aria-current] { color: var(--paper); }
.menu-t { display: none; font-size: 14px; padding: 12px 0 12px 12px; }
.menu-t .c, .menu-t[aria-expanded="true"] .o { display: none; }
.menu-t[aria-expanded="true"] .c { display: inline; }
.sheet { display: none; }

@media (max-width: 760px) {
  .js .top > .nav, .js .top > .lang { display: none; }
  .js .menu-t { display: block; }
  .js .sheet {
    display: flex; flex-direction: column; justify-content: center; gap: 44px;
    position: fixed; inset: 0; z-index: 25; overflow-y: auto;
    padding: calc(var(--hh) + 24px) var(--g) max(40px, env(safe-area-inset-bottom, 0px));
    background: var(--ink);
    opacity: 0; visibility: hidden; transition: opacity .5s var(--ease), visibility 0s .5s;
  }
  .js .sheet.is-open { opacity: 1; visibility: visible; transition: opacity .5s var(--ease); }
  .sheet ul { display: grid; gap: 6px; }
  .sheet__a { display: inline-block; font-size: 28px; line-height: 1.3; letter-spacing: -.01em; padding: 4px 0; }
  .sheet__a[aria-current]::after { bottom: 2px; }
  .sheet .lang { font-size: 14px; margin-left: -4px; }
  .sheet li, .sheet .lang, .sheet .ctas { opacity: 0; transform: translateY(8px); transition: opacity .5s var(--ease), transform .7s var(--ease); }
  .sheet.is-open li, .sheet.is-open .lang, .sheet.is-open .ctas { opacity: 1; transform: none; }
  .sheet.is-open li:nth-child(2) { transition-delay: 50ms; }
  .sheet.is-open li:nth-child(3) { transition-delay: 100ms; }
  .sheet.is-open .lang { transition-delay: 150ms; }
  .sheet.is-open .ctas { transition-delay: 200ms; }
  /* no JS: the links wrap under the name and take their own space */
  html:not(.js) .top { position: relative; flex-wrap: wrap; height: auto; row-gap: 0; padding-top: 8px; padding-bottom: 8px; background: var(--ink); }
  html:not(.js) .nav ul { gap: 20px; }
  html:not(.js) :is(.sh, .ct, .nf) { padding-top: 32px; }
  html:not(.js) .cover { height: calc(100dvh - 60px); min-height: 480px; } /* the header takes its own row: keep the buttons in the first screen */
}

/* ---------- photos ---------- */
.ph { display: block; position: relative; overflow: hidden; aspect-ratio: var(--r, auto); background: var(--c, var(--ink-2)); }
.ph img { width: 100%; height: 100%; object-fit: cover; object-position: var(--pos, 50% 50%); transition: transform .5s var(--ease), opacity .7s var(--ease); }
.js .ph img[loading="lazy"]:not(.in) { opacity: 0; } /* lazy photos fade in instead of popping (site.js adds .in) */
a.ph { cursor: zoom-in; }
a.ph:hover img, a.ph:focus-visible img { transform: scale(1.025); }

/* ---------- 1. cover ---------- */
/* no background of its own: before the print arrives the tray is the page's warm black, and the bottom edge
   feathers into it, so the cover meets the page without a seam. The feather is a 28 px ink gradient on top, not a
   mask: a mask made the whole cover one render surface, redrawn in full on the first frame of the develop */
.cover { position: relative; height: 100dvh; min-height: 560px; overflow: hidden; isolation: isolate; }
.cover::before { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 28px; z-index: 2; pointer-events: none; background: linear-gradient(to top, var(--ink), rgba(11, 10, 9, 0)); }
.frames, .fr, .fr picture, .fr img { position: absolute; inset: 0; width: 100%; height: 100%; }
.frames { z-index: 1; }
.fr img { object-fit: cover; object-position: var(--pos, 50% 50%); }
/* the loop: the incoming frame fades in on top while the outgoing one ('was') holds underneath */
.js .fr { opacity: 0; z-index: 0; transition: opacity 1.8s cubic-bezier(.45, 0, .25, 1); }
.js .fr.on { opacity: 1; z-index: 2; }
.js .fr.was { opacity: 1; z-index: 1; transition: none; }
.cover.dev .fr.on, .cover.now .fr { transition: none; }
/* the two couple frames (drop) hold their subject low and left of centre on wide screens, where the photo fills the width and
   object-position has only 60 px of play at 16:10: a soft shade of their own, under the caption only, so the type sits on
   shadow rather than on the couple, and it fades in and out with its frame (phones: off, the line sits on black there) */
.fr.drop::after { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(ellipse 46% 44% at 0% 100%, rgba(8, 6, 5, .5), rgba(8, 6, 5, .22) 55%, rgba(8, 6, 5, 0)); }
/* first frame: painted from the first frame (elementtiming="cover" is its paint metric) but held black, then it develops
   like a print in the tray: the latent highlights come up first, hard and sepia, then the full tones */
@media (prefers-reduced-motion: no-preference) {
  .js .cover:not(.lit) .fr:first-child picture { filter: brightness(0) contrast(2.4) saturate(.25) sepia(.4); }
}
.cover.pre .fr:first-child picture { will-change: filter; }
.cover.pre .cover__grain { will-change: transform, opacity; }
.cover.dev .fr:first-child picture { animation: print 2.4s var(--ease) both; }
@keyframes print {
  0% { filter: brightness(0) contrast(2.4) saturate(.25) sepia(.4); }
  40% { filter: brightness(.34) contrast(2.4) saturate(.25) sepia(.4); }
  100% { filter: brightness(1) contrast(1) saturate(1) sepia(0); }
}
.cover.dev .cover__grain { animation: grain 1s steps(1) infinite, develop 2.6s var(--ease) both; }
.cover__grain { z-index: 1; opacity: 0; } /* over the print only while it develops, then gone */
@keyframes develop { 0% { opacity: .2; } 55% { opacity: .1; } 100% { opacity: 0; } }
.cover::after { /* legibility for the header and the caption only: no vignette over his photos */
  content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background:
    linear-gradient(to bottom, rgba(8, 6, 5, .74), rgba(8, 6, 5, .62) 80px, rgba(8, 6, 5, .2) 150px, transparent 220px),
    linear-gradient(to top, rgba(11, 10, 9, .9), rgba(11, 10, 9, .55) 20%, transparent 46%);
}
.cap { position: absolute; left: var(--g); right: var(--g); bottom: 44px; z-index: 3; }
.cap .who { font-family: "Clash Grotesk", var(--font); font-size: 20px; font-weight: 500; line-height: 1.3; letter-spacing: .01em; color: var(--paper); text-wrap: balance; }
/* always two lines on desktop: one line at 1440x900 ran onto the dancer in frame 3 (_source/proto/cover-type, winner b) */
.cap h1 { font-family: "Clash Grotesk", var(--font); font-size: clamp(38px, 3.3vw, 48px); font-weight: 500; line-height: 1.02; letter-spacing: -.012em; margin: 12px 0 30px; max-width: 10em; text-wrap: balance; }
.js .p-home:not(.live) .fr:first-child picture { animation: print 2.4s var(--ease) 2.5s both; } /* safety net if site.js never runs */
/* type over a photo: a soft, layered shadow lifts it off the print without a box; static, so it costs nothing per frame */
.cap .ctas .u { text-shadow: 0 1px 2px rgba(8, 6, 5, .42), 0 3px 12px rgba(8, 6, 5, .34), 0 10px 40px rgba(8, 6, 5, .5); }
.play { margin-left: auto; padding: 12px 4px; margin-block: -12px; font-size: 13px; color: rgba(236, 230, 220, .72); transition: color .35s var(--ease); }
.play:hover { color: var(--paper); }
html:not(.js) .play { display: none; }
/* a phone held sideways (390 px tall): the cover is the screen, no taller, and the caption tightens so the h1 and the
   buttons stay in the first view */
@media (max-height: 500px) and (orientation: landscape) {
  .cover { min-height: 0; }
  .cover .cap { bottom: 22px; }
  .cover .cap .who { font-size: 16px; }
  .cover .cap h1 { font-size: 30px; margin: 6px 0 16px; }
}
/* frame 1's skirt (dance-07) against the caption, on landscape screens up to 1140 px wide (judges, round 11):
   - the caption one step smaller (not on short phones held sideways, which have their own sizes above);
   - 4:3 tablets (1024x768, 1112x834) crop the frame's sides, so it slides to its left edge: the skirt moves 60 px right;
   - wider, short screens (1024x600, 844x390) fill the width, so it grows 10% from its left edge instead */
@media (min-width: 761px) and (max-width: 1140px) and (orientation: landscape) and (min-height: 501px) {
  .cap .who { font-size: 18px; }
  .cap h1 { font-size: 34px; }
}
@media (min-width: 761px) and (max-width: 1140px) and (min-aspect-ratio: 6/5) and (max-aspect-ratio: 3/2) { .fr:first-child img { object-position: 0% 66%; } }
@media (min-width: 761px) and (max-width: 1140px) and (min-aspect-ratio: 3/2) and (max-height: 700px) { .fr:first-child img { scale: 1.1; transform-origin: 0 60%; } }
@media (min-width: 761px) and (max-width: 1140px) and (max-height: 500px) and (orientation: landscape) { .fr:first-child img { scale: 1.15; } .cover .cap .who { font-size: 15px; } }
/* the caption: light from above, so the shadow falls under the letters (contact, near, mid, far) */
.cap .who, .cap h1 { text-shadow: 0 1px 1px rgba(8, 6, 5, .55), 0 3px 6px rgba(8, 6, 5, .36), 0 10px 24px rgba(8, 6, 5, .42), 0 24px 64px rgba(8, 6, 5, .55); }
.cap .play { text-shadow: 0 1px 2px rgba(8, 6, 5, .42), 0 3px 12px rgba(8, 6, 5, .34); }
/* the tagline's last word in the safelight amber (the logo's point), with a soft glow of its own over the same fall shadow */
.cap h1 .am { color: var(--amber); text-shadow: 0 0 .36em rgba(217, 168, 106, .5), 0 0 1em rgba(198, 82, 34, .42), 0 1px 1px rgba(8, 6, 5, .55), 0 10px 24px rgba(8, 6, 5, .42); }
/* the cover's load sequence (BRIEF amendment 8), after the logo on a first visit and with the print on a repeat one:
   1. «La fotografía como forma de» clears glyph by glyph in plain ivory: each glyph is the sharp print plus a soft ivory
      ghost (its blur baked into a text-shadow) that comes up out of focus and gives way, while each word rises .3 em;
   2. «mirar.» develops like the closing's «¿Hablamos?»: the amber latent image first, then the print, the word settling
      from a touch larger; it stays amber with its glow;
   3. just after it, the who line rises out of its own baseline, one sentence after the other (a clip per line);
   4. then the buttons and «Pausa».
   Opacity and transform only; the blurred pieces sit on layers of their own, so their blur is rastered once. The glyphs are
   plain inline spans in one inline-block per word, so the kerning holds; what a positioned box inside the line would break
   lives in copies of the line laid exactly over it (site.js): .tl, the ghosts and latent prints, and .ts, the fall shadow,
   one layer per word that comes up with its word (as one shadow under the whole line, as the plain text draws it).
   site.js sets .tg-on at once, then --t0 and .run on the caption and .tg-dev on the h1 to start (the sequence's clock on a
   first visit, the print's on a repeat one); when «mirar.» is done it swaps the plain text back into .tg, drops both copies
   and sets .tg-end (the line takes its own shadow back, on the same layer); when the buttons are up, or at any input, .done.
   Clock: glyph i of the ivory words at t0 + i x 24 ms; «mirar.» from --a; the who line from --e, as «mirar.» settles */
.cap { --a: calc(var(--t0, 0s) + var(--m) * 24ms + 60ms); --e: calc(var(--a) + (var(--mn) - 1) * 65ms + .95s); }
.cap h1 { position: relative; }
.wd { display: inline-block; }
.am.wd { transform-origin: 0 80%; }
.tg-on { will-change: transform; } /* one stable layer from the first develop frame to after the swap: no re-snapping */
.tl, .ts { position: absolute; left: 0; top: 0; width: 100%; pointer-events: none; }
.tl { z-index: 2; color: rgba(236, 230, 220, .3); text-shadow: 0 0 .07em rgba(236, 230, 220, .8), 0 0 .24em rgba(236, 230, 220, .42); }
.cap h1 .tl .am { color: rgba(217, 168, 106, .5); text-shadow: 0 0 .14em rgb(217, 140, 70), 0 0 .05em rgb(198, 110, 50), 0 0 .3em rgba(198, 82, 34, .5); }
.ts, .cap h1 .ts .am { color: transparent; }
.tg-on .tg { position: relative; z-index: 1; }
.tg-on:not(.tg-end) .tg, .cap h1.tg-on:not(.tg-end) .tg .am { text-shadow: none; } /* while it develops, the shadow is .ts */
.tl .cg, .ts .wd { opacity: 0; }
@media (prefers-reduced-motion: no-preference) {
  .js .tg .cg { opacity: .01; } /* .01, not 0: the h1 is painted from the first frame (LCP) */
  .tg-on .tl .cg { will-change: opacity; } /* the blurred pieces on layers, rastered once; the sharp paper glyphs repaint cheaply */
  .tg-on .ts .wd { will-change: opacity, transform; }
  .tg-dev .wd { animation: rise-w calc(.75s + (var(--n) - 1) * 24ms) var(--ease) calc(var(--t0) + var(--i) * 24ms) both; }
  .tg-dev .tg .cg { animation: clear-c .75s var(--ease) calc(var(--t0) + var(--i) * 24ms) both; }
  .tg-dev .tl .cg { animation: ghost-c .75s var(--ease) calc(var(--t0) + var(--i) * 24ms) both; }
  .tg-dev .ts .wd { animation-name: rise-w, clear-c; }
  .tg-dev .am.wd { animation: settle-w calc(.95s + (var(--n) - 1) * 65ms) var(--ease) var(--a) both; }
  .tg-dev .tg .am .cg { animation: paper-c .95s var(--ease) calc(var(--a) + var(--j) * 65ms) both; }
  .tg-dev .tl .am .cg { animation: latent-c .95s var(--ease) calc(var(--a) + var(--j) * 65ms) both; }
  .tg-dev .ts .am.wd { animation-name: settle-w, paper-c; }
  .js .p-home:not(.live) .tg .cg { animation: paper-c 1.2s var(--ease) 2.5s both; } /* safety net if site.js never runs */
  /* the who line and the buttons wait for the tagline (safety net: they show at 3.2 s if site.js never starts it). Until
     then they sit at .01, not 0, so they are painted with the first frame and their late rise never becomes the LCP */
  .js .p-home .cap :is(.who, .ctas) { animation: seqin-t .6s var(--ease) 3.2s both; }
  .p-home .cap.run .who { animation: none; }
  .cap.run:not(.done) .wl { clip-path: inset(-.5em -1em 0 -1em); }
  .cap.run .wl > span { animation: who-up .7s var(--ease) calc(var(--e) + 40ms) both; }
  .cap.run .wl ~ .wl > span { animation-delay: calc(var(--e) + 140ms); }
  .p-home .cap.run .ctas { animation: capup .6s var(--ease) calc(var(--e) + 330ms) both; }
  .p-home .cap.done :is(.who, .wl > span, .ctas) { animation: none; }
}
@keyframes rise-w { from { transform: translateY(.3em); } }
@keyframes clear-c { 0% { opacity: .01; } 35% { opacity: .3; } 100% { opacity: 1; } }
@keyframes ghost-c { 0% { opacity: 0; } 30% { opacity: .9; } 100% { opacity: 0; } }
@keyframes settle-w { from { transform: translateY(.05em) scale(1.04); } }
@keyframes paper-c { 0% { opacity: .01; } 45% { opacity: .08; } 100% { opacity: 1; } }
@keyframes latent-c { 0% { opacity: 0; } 35% { opacity: .95; } 100% { opacity: 0; } }
@keyframes who-up { from { transform: translateY(110%); opacity: 0; } 50% { opacity: 1; } }
.cap .wl { display: block; }
.cap .wl > span { display: block; }
.cap .btn { box-shadow: 0 1px 2px rgba(8, 6, 5, .3), 0 12px 32px -10px rgba(8, 6, 5, .7); }

/* title sequence, first fresh visit of a session only (the inline gate in <head> sets .ttl-seq before paint; site.js adds
   .ttl-run and plays it, about 2.1 s): on black the amber point comes on at the centre, the two L's draw out from their
   corners, «Lucas López» develops beside them while its tracking closes; a hairline ghost of the L's opens to the screen's
   corners as crop marks while frame 1 develops inside them (site.js), then the lockup glides into the header as one piece
   (FLIP, site.js) and the caption lines and the menu rise in. Any pointer, key, wheel or touch finishes it at once.
   Without site.js everything simply shows at 3 s. The h1 never drops below .01 opacity, so it is painted from the first frame. */
.ttl-seq .brand, .ttl-seq .top > :not(.brand) { animation: seqin .6s var(--ease) 3s backwards; }
@keyframes seqin { from { opacity: 0; } }
@keyframes seqin-t { from { opacity: .01; } }
.ttl-run .brand { animation: none; }
.logo :is(rect, path) { transform-box: fill-box; }
.lv { transform-origin: 0 100%; }
.lh { transform-origin: 0 0; }
.lr { transform-origin: 100% 0; } /* the second L is the first turned 180 deg: it grows from the top-right corner */
.ttl-run .logo .pt { animation: seqin .3s linear backwards; }
.ttl-run .logo .lv { animation: draw-v .45s var(--ease) .1s backwards; }
.ttl-run .logo .lh { animation: draw-h .4s var(--ease) .2s backwards; }
@keyframes draw-v { from { transform: scaleY(0); } }
@keyframes draw-h { from { transform: scaleX(0); } }
/* the closing's develop curve on each glyph (opacity and transform only), 40 ms apart; the name sits .12 em open and closes */
.ttl-run .logo .lg { transform-origin: 50% 100%; animation: print-g .4s var(--ease) calc(var(--i) * 40ms + .35s) backwards, track .95s var(--ease) .35s backwards; }
@keyframes print-g { 0% { opacity: 0; transform: translateY(.03em) scale(1.02); } 40% { opacity: .85; } }
@keyframes track { from { translate: calc(var(--i) * 1.78px) 0; } }
.ttl-run .top > :not(.brand) { animation: seqin .7s var(--ease) 1.65s backwards; }
@keyframes capup { from { opacity: 0; transform: translateY(12px); } }
/* the ghost: two L's as 1 px hairlines (crop marks), built by site.js; each corner is a zero-size anchor at its vertex */
.vf { position: absolute; inset: 0; z-index: 3; pointer-events: none; }
.vf i { position: absolute; left: 0; top: 0; opacity: 0; }
.vf b { position: absolute; left: 0; bottom: 0; background: var(--paper); transform-origin: 0 100%; }

/* the who line's two sentences always stack: on one line it ran across the dancer's shirt (frame 3) and the swimmer's
   arm (frame 4) at 820x1180, and the longer English line came within 50 px of the dancer at 1440x900 */
.cap .who span { display: block; }

/* phones (the 9:19.5 crops, same query as PHONE_MQ in build.mjs): frames whose subject sits high start lower
   and feather into the black, so the line above them sits on black, never on a face or a bright sky */
@media (max-aspect-ratio: 3/4) and (max-width: 600px) {
  .fr.drop { background: var(--ink); }
  .fr.drop::after { content: none; }
  .fr.drop picture { top: 21%; -webkit-mask-image: linear-gradient(transparent, #000 22%); mask-image: linear-gradient(transparent, #000 22%); }
}
/* frame 3 (dance-08, its phone cut runs past the stage floor into ink, _build/fotos.py) on shorter phones (16:9 and so on):
   the whole cut shows at full height, not its middle, so the dancer's feet stay clear of the buttons; its sides feather into the ink */
@media (max-aspect-ratio: 3/4) and (min-aspect-ratio: 1/2) and (max-width: 600px) {
  .fr:nth-child(3) { background: var(--ink); }
  .fr:nth-child(3) img { width: auto; left: 50%; right: auto; transform: translateX(-50%);
    -webkit-mask-image: linear-gradient(to right, transparent, #000 14%, #000 86%, transparent); mask-image: linear-gradient(to right, transparent, #000 14%, #000 86%, transparent); }
}
@media (max-aspect-ratio: 3/4) and (max-width: 600px) {
  .fr img { object-position: 50% 50%; }
  /* phones: the line sits in the black above the subject, the buttons stand on the floor */
  .cap { top: calc(var(--hh) + 28px); bottom: max(30px, env(safe-area-inset-bottom, 0px)); display: flex; flex-direction: column; pointer-events: none; }
  .cap > * { pointer-events: auto; }
  .cap h1 { margin-bottom: 0; }
  .cap .ctas { margin-top: auto; }
  .cover::after {
    background:
      linear-gradient(to bottom, rgba(11, 10, 9, .66), rgba(11, 10, 9, .32) 210px, rgba(11, 10, 9, 0) 330px),
      linear-gradient(to top, rgba(11, 10, 9, .8), rgba(11, 10, 9, 0) 26%);
  }
}
@media (max-width: 760px) { .cap .who { font-size: 17px; } .cap .ctas { column-gap: 20px; } .cap h1 { font-size: 34px; margin-top: 8px; max-width: 10em; } }
/* phones: «Pausa» leaves the buttons' line (it read as a third CTA) for a quiet corner of its own under it, at the right,
   smaller and dimmer; still a 44 px target, clear of «Escríbeme»'s */
@media (max-aspect-ratio: 3/4) and (max-width: 600px) {
  .cap { bottom: max(62px, calc(env(safe-area-inset-bottom, 0px) + 50px)); }
  .cap .play { position: absolute; right: -4px; top: 100%; margin: 6px 0 0; min-height: 44px; padding: 0 4px; font-size: 12px; color: rgba(236, 230, 220, .56); }
}
/* the narrowest phones: a tighter gap keeps the two CTAs on one line (320 px: 197 + 12 + 68 = 277 of 280) */
@media (max-width: 340px) { .cover .cap .ctas { column-gap: 12px; } }

/* ---------- 2. intro ---------- */
.intro { position: relative; z-index: 1; display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 0 var(--gap); align-items: center; padding: 140px var(--g) 128px; }
.intro__eye { grid-column: 1 / 5; }
.intro__eye .ph { aspect-ratio: .8; }
.intro__eye img { object-position: 46% 28%; }
.intro__txt { grid-column: 6 / 12; color: var(--mute); font-size: 16px; max-width: 34em; }
.intro__lead { font-size: 20px; line-height: 1.5; color: var(--paper); letter-spacing: -.004em; }
.intro__txt p + p { margin-top: 16px; }
.intro__txt .u { color: var(--paper); font-size: 14px; }
.intro__txt .u::after { transform: scaleX(1); opacity: .45; }
.intro__txt .u:hover::after { opacity: 1; background: var(--amber); }

/* ---------- 3. work ---------- */
.work { position: relative; isolation: isolate; padding: 0 0 128px; }
.h2 { font-size: 28px; line-height: 1.2; letter-spacing: -.01em; padding: 0 var(--g); margin-bottom: 36px; }
/* the series index, the same on every device: photo rows, not a table, no rules; each row is a strip of its series'
   photo that fades out under the subtitle. Capable desktops add the WebGL ink (reveal.js) under it; touch devices light
   the row crossing the middle of the screen instead (site.js adds .lit) */
.idx { margin: 0 var(--g); }
.idx li { position: relative; }
.idx li + li { margin-top: 8px; }
.idx a { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 22rem) 6rem; align-items: baseline; gap: 2rem; padding: 48px var(--g) 46px; } /* names inset one gutter into their photo strip */
/* each row carries a dim still of its series behind the name, so the index is never words only; the ink (reveal.js)
   opens the same photo at the same crop, extended past the strip */
.idx__still { position: absolute; inset: 0 auto 0 0; width: 58%; z-index: -2; background: none; pointer-events: none;
  -webkit-mask-image: linear-gradient(90deg, #000 30%, transparent); mask-image: linear-gradient(90deg, #000 30%, transparent); }
.idx__still img { filter: saturate(.75); transition: opacity .7s var(--ease), filter .7s var(--ease); }
.idx__still img.in, html:not(.js) .idx__still img { opacity: .2; }
.idx li:has(a:is(:hover, :focus-visible)) .idx__still img { opacity: .3; }
.idx a > span { text-shadow: 0 0 .5em rgba(11, 10, 9, .75), 0 0 1.6em rgba(11, 10, 9, .5); } /* names stay readable over the revealed photo */
.idx .t { justify-self: start; font-size: 22px; line-height: 1.2; letter-spacing: -.01em; }
.idx .t::before { content: none; } /* the whole row is the link and its hit area */
.idx a:is(:hover, :focus-visible, :active) .t::after, .idx .lit .t::after { transform: scaleX(1); transform-origin: left; } /* the nav's line, drawn by the row (hover, focus, press, or lit) */
.idx .s { color: var(--mute); font-size: 14px; transition: color .4s var(--ease); }
.idx .c { color: var(--mute); font-size: 13px; text-align: right; font-variant-numeric: tabular-nums; transition: color .4s var(--ease); }
.idx a:is(:hover, :focus-visible) :is(.s, .c), .idx .lit :is(.s, .c) { color: var(--paper); } /* the glimpse can sit under them too */
/* lit (touch): the still lifts from .2 to .36, still under the reveal's text-contrast ceiling (0.137 linear, REVEAL.md) */
.idx .lit .idx__still img.in { opacity: .36; filter: saturate(.9); }
.work > canvas { position: absolute; left: 0; top: -120px; width: 100%; height: calc(100% + 240px); z-index: -1; pointer-events: none; opacity: .8; } /* reveal.js; a little quieter than the prototype */
/* phones: the same rows on a narrower measure; the count stays on the name's line, the subtitle goes under them,
   and the still runs wider (the strip is short) and aims at the subject (reveal.m in content.mjs) */
@media (max-width: 760px) {
  .idx a { grid-template-columns: minmax(0, 1fr) auto; gap: 6px 16px; padding: 34px var(--g) 32px; }
  .idx .s { grid-row: 2; grid-column: 1 / -1; }
  .idx .c { grid-area: 1 / 2; }
  .idx__still { width: 88%; -webkit-mask-image: linear-gradient(90deg, #000 45%, transparent); mask-image: linear-gradient(90deg, #000 45%, transparent); }
  .idx__still img { object-position: var(--mpos, var(--pos)); }
}
/* touch under reduced motion: no light-up (site.js), a steady, slightly brighter still instead */
@media (hover: none) and (prefers-reduced-motion: reduce) { .idx__still img.in { opacity: .28; } }

/* ---------- services: the one lifted band, a calm two-part block (heading and facts | the three services), no rules ---------- */
.svc { position: relative; z-index: 1; display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 0 var(--gap); padding: 128px var(--g); background: var(--lift); }
.svc__head { grid-column: 1 / 5; align-self: start; position: sticky; top: calc(var(--hh) + 40px); }
.svc__head .h2 { padding: 0; margin: 0; }
.svc ul { grid-column: 6 / 12; display: grid; gap: 56px; }
.svc h3 { font-size: 22px; line-height: 1.25; letter-spacing: -.01em; }
/* a title with a series of its own links there: the index's line draws under it, the buttons' arrow nudges on */
.svc__t { display: inline-flex; align-items: center; gap: 12px; }
.svc__t svg { flex: none; color: var(--mute); transition: transform .45s var(--ease), color .35s var(--ease); }
.svc__t:is(:hover, :focus-visible) svg { transform: translateX(3px); color: var(--paper); }
.svc__t:is(:hover, :focus-visible) .u::after { transform: scaleX(1); transform-origin: left; }
.svc li p { color: var(--mute); font-size: 16px; max-width: 36em; margin-top: 10px; text-wrap: pretty; }
.svc li .svc__go { display: flex; flex-wrap: wrap; gap: 8px 28px; margin-top: 24px; font-size: 14px; } /* every block: copy, then «Escríbeme» 24 px under it (44 px hit areas never meet) */
.svc__go .u { white-space: nowrap; }
.svc .svc__to { font-size: 14px; }
.svc__to .u { white-space: nowrap; } /* a link split over two lines splits its 44 px hit area too */
.svc li p .u::after { transform: scaleX(1); opacity: .45; }
.svc li p .u:hover::after { opacity: 1; background: var(--amber); }
.nw { white-space: nowrap; } /* «600-photo» never breaks at its hyphen */
.where { color: var(--mute); font-size: 14px; max-width: 26em; }
.svc__head .where:first-of-type { margin-top: 32px; }
.svc__head .where + .where { margin-top: 12px; }

/* ---------- full-bleed frames: pauses with nothing written on them, like turning a page in a book of prints ----------
   Each one feathers out of the ground above and into the one below (a mask on the print over a matching gradient
   on the figure), so the blue stage dissolves into the lifted services band and the golden couple rises out of it.
   As one arrives it settles (opacity and scale, scroll-driven), then drifts a few px while it passes. */
.bleed { position: relative; z-index: 1; background: linear-gradient(var(--above, transparent), transparent 35% 65%, var(--below, transparent)); }
.bleed--1 { --below: var(--lift); margin-top: -96px; } /* into the services band; its black top rises into the index's air, so the dancers arrive right under it */
.bleed--2 { --above: var(--lift); }
.bleed picture { display: contents; }
.bleed .ph { width: 100%; aspect-ratio: var(--r); max-height: calc(100svh - var(--hh)); /* whole under the header; the width stays full when the height caps */
  -webkit-mask-image: linear-gradient(transparent, rgba(0, 0, 0, .55) 9%, #000 20%, #000 80%, rgba(0, 0, 0, .55) 91%, transparent);
  mask-image: linear-gradient(transparent, rgba(0, 0, 0, .55) 9%, #000 20%, #000 80%, rgba(0, 0, 0, .55) 91%, transparent); }
/* the caption: the series it comes from; its link covers the whole frame, so the photo is a doorway into the series */
.bleed figcaption { display: flex; align-items: baseline; gap: 12px; padding: 4px var(--g) 0; font-size: 14px; }
.bleed__n { color: var(--mute); font-size: 13px; }
.bleed__a::before { content: ""; position: absolute; inset: 0; z-index: 1; }
.bleed__a:focus-visible { outline: 0; }
.bleed:has(.bleed__a:focus-visible) .u { outline: 1px solid var(--amber); outline-offset: 4px; }
.bleed:is(:hover, :has(.bleed__a:focus-visible)) .u::after { transform: scaleX(1); transform-origin: left; }
.bleed:hover img { filter: brightness(1.04); }
.bleed img { transition: filter .5s var(--ease); }
@media (max-width: 760px) {
  .bleed .ph { aspect-ratio: 4 / 5; } .bleed img { object-position: var(--mpos); } .bleed--1 { margin-top: -40px; } /* phones: the subjects at a size you can see */
  .bleed--cut .ph { aspect-ratio: var(--mr); } .bleed--cut img { object-position: 50% 50%; } /* a pre-cut phone crop, shown whole */
}
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .bleed .ph { animation: settle-o linear both; animation-timeline: view(); animation-range: entry 0% cover 45%; }
    .bleed img { scale: 1.08; animation: drift linear both, settle-s linear both; animation-timeline: view(), view(); animation-range: normal, entry 0% cover 45%; }
  }
}
@keyframes drift { from { translate: 0 -3.5%; } to { translate: 0 3.5%; } }
@keyframes settle-o { from { opacity: .25; } }
@keyframes settle-s { from { scale: 1.16; } to { scale: 1.08; } }

/* ---------- closing «¿Hablamos?»: "Safelight" (_source/CONTACT.md) ---------- */
/* the one large type moment of the site; one closing screen frames heading, rows and base line */
.talk {
  --safe: 198, 82, 34;      /* darkroom safelight, amber-red */
  --safe-2: 140, 44, 20;    /* its falloff */
  position: relative; isolation: isolate; overflow: clip;
  /* a full closing screen on landscape screens; on portrait tablets the screen is far taller than the content, so
     there it is sized to its content, never under three quarters of the width */
  display: flex; flex-direction: column;
  padding: clamp(88px, 13vh, 136px) var(--g) 0;
  view-timeline: --talk block;
}
/* the safelight: rises with the scroll, breathes once settled, leans toward a fine pointer (site.js sets transform) */
.glow { position: absolute; left: -25%; right: -25%; bottom: -20%; height: 120%; z-index: -1; pointer-events: none; transform-origin: 50% 100%; transition: translate .9s var(--ease), opacity .9s var(--ease); }
.glow::before {
  content: ""; position: absolute; inset: 0; transform-origin: 50% 100%;
  background: radial-gradient(44% 70% at 50% 83.3%, rgba(var(--safe), .36), rgba(var(--safe-2), .17) 40%, rgba(var(--safe-2), .06) 66%, transparent 86%);
}
.on .glow::before { animation: breathe 7.2s ease-in-out 3.2s infinite alternate; }
@keyframes breathe { from { scale: 1; opacity: 1; } to { scale: 1.035 1.06; opacity: .84; } }
@supports (animation-timeline: view()) {
  .glow { transition: none; animation: rise linear both; animation-timeline: --talk; animation-range: entry 0% entry 92%; }
}
@keyframes rise { from { translate: 0 55%; opacity: 0; } 30% { opacity: .5; } to { translate: 0 0; opacity: 1; } }

/* the heading develops glyph by glyph like a print in the tray, a sheen of light passes once, then site.js
   swaps the glyphs back for the plain kerned word */
.ttl { font-weight: 500; font-size: clamp(56px, 9vw, 144px); line-height: .96; letter-spacing: -.045em; margin-left: -.05em; }
html:not(.js) .ttl .vh { position: static; width: auto; height: auto; clip-path: none; white-space: normal; }
.ttl .w { display: inline-block; white-space: nowrap; }
/* opacity and transform only, so the whole develop runs on the compositor: each glyph is three pre-tinted prints of
   itself stacked in one box (the copper latent image, soft as if out of focus, baked into a text-shadow; the paper
   print; the warm sheen) that cross-fade, while the box settles. No animated filters: those ran on the main thread
   every frame of the arrival (Chrome refuses to composite blur) */
.g { display: inline-block; position: relative; }
.g__p { display: block; }
.js .g__p { opacity: 0; }
.on .g { animation: settle-g 2.4s var(--ease) calc(var(--i) * 95ms + 120ms) backwards; }
.on .g__p { opacity: 1; animation: paper-g 2.4s var(--ease) calc(var(--i) * 95ms + 120ms) backwards; }
.on .g::before, .on .g::after { content: attr(data-c); position: absolute; inset: 0; opacity: 0; pointer-events: none; }
.on .g::before { color: transparent; text-shadow: 0 0 .13em rgb(122, 60, 24), 0 0 .06em rgb(104, 54, 22); animation: latent-g 2.4s var(--ease) calc(var(--i) * 95ms + 120ms); }
.on .g::after { color: #fffbd2; animation: sheen 1.2s ease-in-out calc(var(--n) * 95ms + 3.3s + var(--i) * 70ms); }
@keyframes settle-g { from { transform: translateY(.03em) scale(1.02); } }
@keyframes paper-g { 0% { opacity: 0; } 40% { opacity: .06; } }
@keyframes latent-g { 0% { opacity: 0; } 40% { opacity: .85; } 100% { opacity: 0; } }
@keyframes sheen { 0%, 100% { opacity: 0; } 45% { opacity: 1; } }

.sub { margin-top: 26px; max-width: 32em; font-size: 16px; color: var(--mute); text-wrap: pretty; }
.js .rv { opacity: 0; transform: translateY(14px); }
.on .rv { opacity: 1; transform: none; transition: opacity .9s var(--ease) var(--d, 0s), transform 1.1s var(--ease) var(--d, 0s); } /* the line and the rows come up while the heading develops, not after it */

/* the two actions: large text rows on hairlines, on the same full measure as the work index and the services */
.acts { margin: clamp(48px, 8vh, 80px) 0 clamp(56px, 9vh, 96px); border-top: 1px solid var(--line); }
.act {
  position: relative; isolation: isolate; display: flex; align-items: center; gap: 20px;
  padding: 30px 0 31px; border-bottom: 1px solid var(--line);
  font-size: clamp(26px, 2.9vw, 40px); line-height: 1.1; letter-spacing: -.02em;
  -webkit-tap-highlight-color: transparent;
}
.act:focus-visible { outline: 0; }
.act__t { position: relative; transition: transform .45s var(--ease); }
.act__m { margin-left: auto; font-size: 14px; letter-spacing: .01em; color: var(--mute); }
.act__i { position: relative; flex: none; width: 26px; height: 26px; overflow: hidden; color: var(--amber); }
.act__m + .act__i { margin-left: 4px; }
.act__t + .act__i { margin-left: auto; }
.act__i svg { position: absolute; inset: 0; width: 100%; height: 100%; transition: transform .45s var(--ease); }
.act__i svg + svg { transform: translateX(-110%); }
.act--out .act__i svg + svg { transform: translate(-110%, 110%); }
/* amber hairline that draws across, and the light that wakes behind the row */
.act::after { content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px; background: var(--amber); transform: scaleX(0); transform-origin: left; transition: transform .5s var(--ease), opacity .45s var(--ease); }
.act::before {
  content: ""; position: absolute; z-index: -1; left: 0; right: 20%; top: -50%; bottom: -1px; pointer-events: none;
  background: radial-gradient(40% 110% at 36% 100%, rgba(var(--safe), .34), rgba(var(--safe-2), .13) 50%, transparent 82%);
  opacity: 0; transform: scaleY(.6); transform-origin: 50% 100%; transition: opacity .45s var(--ease), transform .5s var(--ease);
}
.act:is(:hover, :focus-visible)::after { transform: scaleX(1); }
.act:is(:hover, :focus-visible)::before { opacity: 1; transform: none; }
.act:is(:hover, :focus-visible) .act__t { transform: translateX(12px); }
.act:is(:hover, :focus-visible, :active) .act__i svg:first-child { transform: translateX(110%); }
.act--out:is(:hover, :focus-visible, :active) .act__i svg:first-child { transform: translate(110%, -110%); }
.act:is(:hover, :focus-visible, :active) .act__i svg + svg { transform: none; }
/* keyboard: a heavier rule and a small amber mark, so focus never reads as a passive hover */
.act:focus-visible::after { height: 2px; bottom: -1.5px; }
.act:focus-visible .act__t { transform: translateX(22px); }
.act__t::before { content: ""; position: absolute; left: -20px; top: 50%; width: 8px; height: 8px; margin-top: -4px; border-radius: 50%; background: var(--amber); opacity: 0; transform: scale(.3); transition: opacity .4s var(--ease), transform .5s var(--ease); }
.act:focus-visible .act__t::before { opacity: 1; transform: none; }
/* press: the light flares and the label settles back */
.act:active::before { opacity: 1; transform: scale(1.08, 1.25); transition-duration: .15s; }
.act:active .act__t { transform: translateX(6px); transition-duration: .2s; }

/* touch: nothing to hover, so each row shows a short lit amber line over a dim one as it arrives, and a press does
   the rest; the release waits 180 ms so a quick tap is still seen before the page changes */
@media (hover: none) {
  .act::after { background: linear-gradient(90deg, var(--amber) 56px, rgba(217, 168, 106, .45) 56px); }
  .on .act::after { transform: scaleX(1); opacity: .6; transition: transform 1.4s var(--ease) calc(var(--d, 0s) + .5s), opacity .5s var(--ease) .18s; }
  .on .act:active::after { opacity: 1; transition-delay: 0s; }
  .act::before, .act .act__t, .act__i svg { transition-delay: .18s; }
  .act:active::before, .act:active .act__t, .act:active .act__i svg { transition-delay: 0s; }
  .act:active .act__t { transform: translateX(8px); }
}

/* base line: closes every page; 28 px tap targets, an underline that draws in and turns amber on focus */
.end--min { padding: 0 var(--g); }
.end--min .foot { margin-top: 128px; }
.foot { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 4px 24px; margin-top: auto; padding: 20px 0 max(26px, env(safe-area-inset-bottom, 0px)); border-top: 1px solid var(--line); font-size: 13px; color: var(--mute); }
.foot ul { display: flex; flex-wrap: wrap; gap: 16px 24px; } /* wrapped rows: 28 px line + 16 px gap keeps each 44 px hit area clear of the next */
.foot a { position: relative; display: inline-block; padding: 12px 0; margin-block: -8px; transition: color .35s var(--ease); } /* 44 px hit area, same line as before */
.foot a:hover, .foot a:focus-visible { color: var(--paper); }
.foot a:focus-visible { outline: 0; }
.foot a::after { content: ""; position: absolute; left: 0; right: 0; bottom: 10px; height: 1px; background: currentColor; transform: scaleX(0); transform-origin: right; transition: transform .45s var(--ease); }
.foot a:is(:hover, :focus-visible)::after { transform: scaleX(1); transform-origin: left; }
.foot a:focus-visible::after { transform: scaleX(1) scaleY(2); background: var(--amber); }
/* email: a click copies it (site.js); the address flips to "Copiado"; the hint sits above, never clipped at the edge */
.foot .mail { display: inline-grid; text-align: left; }
.mail > span { grid-area: 1 / 1; transition: transform .5s var(--ease), opacity .4s var(--ease); }
.mail .ok { color: var(--amber); opacity: 0; transform: translateY(80%); }
.mail.is-copied .addr { opacity: 0; transform: translateY(-80%); }
.mail.is-copied .ok { opacity: 1; transform: none; }
.mail.is-copied::after { transform: scaleX(0); }
.js .mail::before { content: attr(data-hint); position: absolute; right: 0; bottom: calc(100% - 8px); font-size: 12px; color: var(--mute); opacity: 0; transform: translateY(4px); transition: opacity .35s var(--ease), transform .45s var(--ease); pointer-events: none; }
.js .mail:is(:hover, :focus-visible):not(.is-copied)::before { opacity: 1; transform: none; }

/* ---------- series pages ---------- */
.sh { padding: calc(var(--hh) + 88px) var(--g) 56px; }
.sh h1 { font-size: 40px; line-height: 1.1; letter-spacing: -.015em; }
.sh p { color: var(--mute); margin-top: 14px; max-width: 56ch; }
.sh__n { margin-left: .5em; font-size: 13px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.sh .sh__book { display: flex; flex-wrap: wrap; gap: 8px 28px; margin-top: 20px; color: var(--paper); font-size: 14px; }
:is(.sh__book, .read__go) .u::after { transform: scaleX(1); opacity: .45; }
/* the booking links and the pager: a 44 px tall box of their own (same line, same underline), not only the ::before hit area */
:is(.sh__book, .read__go, .pager) .u { display: inline-block; padding-block: 11px; margin-block: -11px; }
:is(.sh__book, .read__go, .pager) .u::after { bottom: 8px; }
:is(.sh__book, .read__go) .u:hover::after { opacity: 1; background: var(--amber); }
.rows { display: grid; gap: var(--gap); padding: 0 var(--g); }
.pager > :nth-child(2) { text-align: center; }
.pager > :last-child { text-align: right; }
.row { display: flex; gap: var(--gap); }
.row > .ph { flex: var(--r) 1 0; min-width: 0; }
.pager { position: relative; z-index: 1; display: flex; justify-content: space-between; align-items: flex-start; gap: 24px; padding: 72px var(--g) 0; font-size: 14px; } /* above the closing, so the links' 44 px hit areas are not covered */
.pager .u::after { transform: scaleX(1); opacity: .45; }
.pager .u:hover::after { opacity: 1; background: var(--amber); }

/* ---------- about ---------- */
.about { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 0 var(--gap); align-items: start; padding: 0 var(--g); }
.about__ph { grid-column: 1 / 6; position: sticky; top: calc(var(--hh) + 24px); }
.about__ph .ph { aspect-ratio: .6667; max-height: calc(100svh - var(--hh) - 48px); margin-right: auto; }
.read { grid-column: 7 / 12; max-width: 36em; font-size: 16px; line-height: 1.7; color: var(--text); }
.read p + p { margin-top: 1.1em; }
.read .lead { font-size: 20px; line-height: 1.5; color: var(--paper); }
.read__ph { margin: 40px 0; }
.read .read__where { margin-top: 2.2em; color: var(--mute); font-size: 14px; }
.read .read__go { display: flex; flex-wrap: wrap; gap: 8px 28px; margin-top: 28px; color: var(--paper); font-size: 14px; line-height: 1.55; } /* the page closes on the way to write */

/* ---------- contact ---------- */
.ct { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); grid-template-rows: auto 1fr; gap: 0 var(--gap); align-items: start; padding: calc(var(--hh) + 64px) var(--g) 0; }
/* the DOM runs intro, form, other channels (the phone order); here the channels sit under the intro, beside the form */
.ct__intro { grid-column: 1 / 6; grid-row: 1; }
.ct__ch { grid-column: 1 / 6; grid-row: 2; }
.ct h1 { font-size: 40px; line-height: 1.1; letter-spacing: -.015em; }
.ct__intro > p { color: var(--mute); margin-top: 12px; max-width: 34em; }
.ct__intro > .ct__lead { color: var(--paper); font-size: 16px; margin-top: 16px; }
.ct__intro > .ctas { margin-top: 28px; }
.ct__ch { margin-top: 48px; }
.ct__ch h2 { font-size: 14px; color: var(--mute); margin-bottom: 10px; }
.ch { border-top: 1px solid var(--line); }
.ch a { display: flex; justify-content: space-between; align-items: baseline; gap: 16px; padding: 15px 0; border-bottom: 1px solid var(--line); }
.ch__k { white-space: nowrap; }
.ch__v { color: var(--mute); font-size: 14px; text-align: right; overflow-wrap: anywhere; transition: color .35s var(--ease); }
.ch a:hover .ch__v { color: var(--paper); }
.form { grid-column: 7 / 13; grid-row: 1 / 3; display: grid; gap: 22px; padding-top: 12px; } /* the form title sits on the h1's baseline */ /* no panel: the fields sit on the same hairlines as the rest of the site */
.form__t { font-size: 20px; }
.form__pair { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 20px; }
.field { display: grid; gap: 6px; align-content: start; }
.field label { font-size: 14px; }
.field__h { font-size: 13px; color: var(--mute); margin-top: -2px; }
.field input, .field select, .field textarea {
  width: 100%; font: 400 16px/1.4 var(--font); color: var(--paper);
  background: transparent; border: 0; border-bottom: 1px solid rgba(236, 230, 220, .5); border-radius: 0;
  padding: 8px 0; transition: border-color .3s var(--ease), box-shadow .3s var(--ease); appearance: none;
}
.field select { padding-right: 24px; cursor: pointer; background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239a9286' stroke-width='1.4'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") right center / 16px no-repeat; }
.field select option { background: var(--ink-2); color: var(--paper); }
.field textarea { resize: vertical; min-height: 104px; }
.field input:focus, .field select:focus, .field textarea:focus { outline: 0; border-color: var(--amber); box-shadow: 0 1px 0 var(--amber); }
.field [aria-invalid="true"] { border-color: var(--amber); }
.field__e { font-size: 13px; line-height: 1.45; min-height: 1.45em; color: var(--amber); }
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.form__end { display: flex; flex-wrap: wrap; align-items: center; gap: 14px 24px; margin-top: 4px; }
.form .btn { border: 0; cursor: pointer; }
.form__status { font-size: 14px; max-width: 42ch; scroll-margin: 24px; }
.form__status:focus { outline: 0; } /* focused by site.js after sending, so it is read and in view; not a control */
.form__status[data-state="error"] { color: var(--amber); }
.form__status a { text-decoration: underline; text-underline-offset: 3px; overflow-wrap: anywhere; }
.form button[aria-disabled="true"] { opacity: .6; cursor: progress; }

/* ---------- 404 ---------- */
.nf { padding: calc(var(--hh) + 120px) var(--g) 0; min-height: 70svh; }
.nf h1 { font-size: 40px; line-height: 1.1; letter-spacing: -.015em; }
.nf > p:first-of-type { margin-top: 16px; color: var(--mute); }
.nf ul { display: flex; flex-wrap: wrap; gap: 8px 28px; margin: 28px 0 40px; font-size: 16px; }
.nf .u::after { transform: scaleX(1); opacity: .45; }
.nf .u:hover::after { opacity: 1; background: var(--amber); }

/* ---------- lightbox ---------- */
.lb { position: fixed; inset: 0; width: 100%; height: 100%; max-width: none; max-height: none; margin: 0; padding: 0; border: 0; background: #050404; /* as deep as the stage blacks: a dark frame sinks into it */ color: var(--paper); overflow: hidden; }
.lb[open] { display: grid; grid-template-rows: auto minmax(0, 1fr) auto; animation: lbin .4s var(--ease); }
@keyframes lbin { from { opacity: 0; } }
.lb::backdrop { background: rgba(5, 4, 4, .94); }
.lb.closing, .lb.closing::backdrop { opacity: 0; transition: opacity .28s cubic-bezier(.45, 0, .25, 1); } /* in-out, like the cover: the whole 280 ms reads */
.lb__bar, .lb__foot { display: flex; justify-content: space-between; align-items: center; gap: 32px; padding: 14px var(--g); }
.lb__bar { padding-top: max(14px, env(safe-area-inset-top, 0px)); }
.lb__foot { padding-bottom: max(14px, env(safe-area-inset-bottom, 0px)); }
.lb__count { font-size: 13px; color: var(--mute); font-variant-numeric: tabular-nums; }
.lb__close { min-height: 44px; font-size: 14px; color: var(--mute); transition: color .35s var(--ease); }
.lb__close:hover { color: var(--paper); }
.lb__fig { position: relative; display: grid; place-items: center; min-height: 0; padding: 0 var(--g); }
.lb__img { grid-area: 1 / 1; max-width: 100%; max-height: calc(100svh - 150px); width: auto; height: auto; object-fit: contain; touch-action: pan-y; transition: opacity .5s var(--ease); }
.lb__img.out { opacity: 0; transition: opacity .4s var(--ease) .12s; } /* the outgoing photo leaves a beat later: no dip to black */
.lb.cut .lb__img { transition: none; }
.lb__cap { font-size: 14px; line-height: 1.5; color: var(--mute); max-width: 70ch; }
.lb__nav { display: flex; gap: 8px; flex: none; }
.lb__nav button { width: 44px; height: 44px; border-radius: 2px; border: 1px solid var(--line-2); display: grid; place-items: center; transition: border-color .35s var(--ease), background-color .35s var(--ease); }
.lb__nav button:hover { border-color: rgba(236, 230, 220, .6); background: rgba(236, 230, 220, .05); }
.lb__prev svg { transform: scaleX(-1); }

/* ---------- small screens ---------- */
@media (max-width: 999px) {
  .intro__eye { grid-column: 1 / 6; }
  .intro__txt { grid-column: 7 / 13; }
  .ct { grid-template-rows: none; }
  .ct__intro, .ct__ch { grid-column: 1 / 13; grid-row: auto; }
  .form { grid-column: 1 / 13; grid-row: auto; margin-top: 48px; }
}
@media (max-width: 760px) {
  /* phones: the words first, then his portrait at under half the width beside the second paragraph */
  .intro { grid-template-columns: minmax(0, 44fr) minmax(0, 56fr); grid-template-rows: auto auto 1fr; gap: 0 16px; align-items: start; padding: 88px var(--g) 96px; }
  .intro__txt { display: contents; font-size: 15px; }
  .intro__lead { grid-area: 1 / 1 / 2 / -1; font-size: 18px; margin-bottom: 28px; }
  .intro__eye { grid-area: 2 / 1 / 4 / 2; }
  .intro__txt p:nth-child(2) { grid-area: 2 / 2; margin-top: 0; font-size: 15px; line-height: 1.5; }
  .intro__txt p:nth-child(3) { grid-area: 3 / 2; margin-top: 14px; } /* «Más sobre mí» closes the paragraph; the portrait's extra height goes to the 1fr row under it */
  .h2 { font-size: 24px; margin-bottom: 28px; }
  .work { padding-bottom: 96px; }
  .svc { display: flex; flex-direction: column; padding: 88px var(--g); }
  .svc ul { gap: 40px; margin-top: 32px; }
  .svc h3 { font-size: 20px; }
  /* phones: the three services come straight after the heading, the delivery fine print and the place after them */
  .svc__head { display: contents; }
  .svc ul { order: 1; }
  .svc .where { order: 2; }
  .svc__head { position: static; }
  .svc__head .where:first-of-type { margin-top: 56px; }
  .talk { padding-top: 88px; }
  .acts { margin-bottom: 72px; }
  .act { padding: 24px 0 25px; gap: 14px; }
  .act__m { font-size: 13px; }
  .sh { padding: calc(var(--hh) + 56px) var(--g) 40px; }
  .sh h1 { font-size: 32px; }
  .row { flex-direction: column; }
  .row > .ph { flex: none; width: 100%; }
  /* the series run as one column; from 390 px, pairs of portraits (build.mjs) sit side by side */
  .rows { display: flex; flex-wrap: wrap; }
  .rows > .row { display: contents; }
  /* phones: previous and next side by side, the series name under a small label; «Todo el trabajo» centred below */
  .pager { padding-top: 56px; display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 36px 16px; align-items: end; }
  .pager > :first-child { grid-area: 1 / 1; justify-self: start; }
  .pager > :last-child { grid-area: 1 / 2; justify-self: end; }
  .pager > :nth-child(2) { grid-area: 2 / 1 / 3 / 3; justify-self: center; }
  .pager .u > span:first-child { display: block; margin-bottom: 4px; font-size: 12px; color: var(--mute); }
  .about { display: flex; flex-direction: column; gap: 40px; }
  .about__ph { position: static; }
  .about__ph .ph { max-height: none; }
  .read .lead { font-size: 18px; }
  .ct { display: flex; flex-direction: column; align-items: stretch; padding-top: calc(var(--hh) + 48px); }
  .ct h1 { font-size: 32px; }
  .form { margin-top: 40px; }
  .form__pair { grid-template-columns: minmax(0, 1fr); }
  .nf h1 { font-size: 32px; }
  .end--min .foot { margin-top: 88px; }
  .foot { flex-direction: column; }
  .lb__foot { flex-direction: column-reverse; align-items: stretch; gap: 14px; }
  .lb__nav { justify-content: space-between; }
  .lb__img { max-height: calc(100svh - 190px); }
  .lb__fig { padding: 0; }
}

@media (min-width: 390px) and (max-width: 760px) { .rows .ph.half { width: calc(50% - var(--gap) / 2); } }

/* «Pausa» on the cover (WCAG 2.2.2) holds everything that moves on its own: the grain, the develop's grain, the closing
   glow's breathing; «Reanudar» lets them run on. Last in the file and specific enough to win over each animation shorthand */
html.still .room::after, html.still .cover.dev .cover__grain, html.still .on .glow::before { animation-play-state: paused; }

@media (prefers-reduced-motion: reduce) {
  html, html:focus-within { scroll-behavior: auto; } /* must outrank the smooth html:focus-within rule */
  *, *::before, *::after { animation: none !important; transition-duration: .01ms !important; transition-delay: 0s !important; }
  .js .fr:first-child, .js .p-home .cap, .js .ph img[loading="lazy"]:not(.in) { opacity: 1; transform: none; }
  .cover__grain { display: none; }
  .logo rect { translate: none !important; scale: none !important; } /* the logo's hover keeps its colour change only */
}
