/* ============================================================
   Estudio Malacara — shared shell (loaded on every deck page)
   fonts, reset, stage/rail, reveal system, dog-glitch, curtain,
   pillnav, menu overlay, cross-document View Transitions, mobile
   ============================================================ */
@font-face{font-family:'Druk Wide';src:url('fonts/DrukWide-Super.woff2') format('woff2');font-weight:800;font-style:normal;font-display:swap}
@font-face{font-family:'Druk Wide';src:url('fonts/DrukWide-Heavy.woff2') format('woff2');font-weight:700;font-style:normal;font-display:swap}
@font-face{font-family:'Druk XCond';src:url('fonts/DrukXCond-Super.woff2') format('woff2');font-weight:800;font-style:normal;font-display:swap}
:root{
  --cream:#F1EEE6; --blue:#5E9A92; --ink:#000719; --lime:#D0FA8B;
  --lavender:#CBA995; --grey:#8A8B84; --black:#000719; --white:#FFFFFF;
  --offwhite:#F1EEE6; --offblack:#000719;
  --font:"Helvetica Neue",Helvetica,"Inter",Arial,sans-serif;
  --druk:'Druk Wide',"Helvetica Neue",Arial,sans-serif;
  --drukx:'Druk XCond',"Helvetica Neue",Arial,sans-serif;
  --ease:cubic-bezier(.19,1,.22,1);
  --radius:2.3cqw;
}
*{margin:0;padding:0;box-sizing:border-box}
html,body{height:100%}
body{font-family:var(--font);background:var(--black);color:var(--ink);
  -webkit-font-smoothing:antialiased;overflow:hidden}

/* ---------- cross-document View Transitions: real page loads that still feel like one
   continuous cover deck. Both the leaving and arriving page must opt in for the browser
   to animate the swap instead of an instant cut. ---------- */
@view-transition{navigation:auto}
::view-transition-group(root){animation-duration:.9s;animation-timing-function:cubic-bezier(.76,0,.24,1)}
::view-transition-old(root){animation:none}
::view-transition-new(root){animation:mc-cover-in .9s cubic-bezier(.76,0,.24,1)}
@keyframes mc-cover-in{from{transform:translateY(100%)}to{transform:translateY(0)}}
/* going BACKWARD flips which side moves: the page you're LEAVING stays on top and slides away
   (down, off-screen) instead of the incoming page sliding up to cover it — revealing the
   previous page sitting static underneath, matching how the old single-page deck handled
   stepping back. mc-back is set by the destination's own blocking <head> script (before it
   paints), same mechanism as mc-noc — see navigateTo() in shell.js for how the direction is
   decided. Both pseudo-elements need an explicit z-index override here since by default the
   browser always paints the incoming snapshot over the outgoing one, which is backwards for
   this case (the outgoing one needs to stay on top while IT moves). */
html.mc-back::view-transition-old(root){animation:mc-cover-out .9s cubic-bezier(.76,0,.24,1);z-index:2}
html.mc-back::view-transition-new(root){animation:none;z-index:1}
@keyframes mc-cover-out{from{transform:translateY(0)}to{transform:translateY(100%)}}
.pillnav{view-transition-name:pillnav}
@media(prefers-reduced-motion:reduce){
  ::view-transition-group(root),::view-transition-old(root),::view-transition-new(root){animation:none!important}
}

/* ---------- single-slide stage: this page's one section (or two, cover+gallery) COVERS
   the previous the same way it always did, just scoped to a max of 2 local slides now ---------- */
.deck{position:fixed;inset:0;overflow:hidden}
.slide{position:absolute;inset:0;display:grid;place-items:center;overflow:hidden;
  transition:transform .9s cubic-bezier(.76,0,.24,1);will-change:transform;
  box-shadow:0 0 7vh rgba(0,0,0,.45)}
.slide[data-dir="v"]{transform:translateY(100%)}
.deck.noanim .slide{transition:none}
.g-black{background:var(--black)}
.g-cream{background:var(--cream)}
.g-blue{background:var(--blue)}
.g-lime{background:var(--lime)}
.g-lav{background:var(--lavender)}
.g-white{background:#FFFFFF}
.g-ink{background:#000719}

/* ---------- 16:9 stage; children sized in cqw/cqh ---------- */
.stage{position:relative;aspect-ratio:16/9;
  width:min(100vw,calc(100svh*16/9));height:min(100svh,calc(100vw*9/16));
  container-type:size;overflow:hidden}

/* ---------- header rail (absolute, Figma proportions) ---------- */
.rail{position:absolute;top:3.6cqh;left:0;right:0;height:2cqh;z-index:8;
  font-size:.76cqw;font-weight:500;letter-spacing:-.03em}
.rail .ri{position:absolute;top:0;white-space:nowrap}
.rail .ri-mark{position:absolute;left:50%;top:-.9cqh;transform:translateX(-50%);
  width:1.85cqw;height:auto;z-index:9;cursor:pointer;pointer-events:auto}
/* text-shadow + drop-shadow keep the rail legible over full-bleed photography whose local
   brightness can vary under the thin header strip (a light theme's bg image can still have a
   dark corner, and vice versa) — the shadow is subtle so it's invisible on a clean flat bg. */
.rail--onblack{color:#E7E4DC;text-shadow:0 1px 3px rgba(0,0,0,.4)}
.rail--onblack .dim{color:var(--grey)}.rail--onblack .lav{color:#E7E4DC}
.rail--onblack .ri-mark{filter:drop-shadow(0 1px 3px rgba(0,0,0,.4))}
.rail--oncream{color:var(--ink);text-shadow:0 1px 2px rgba(255,255,255,.5)}
.rail--oncream .dim{color:rgba(33,31,27,.42)}.rail--oncream .lav{color:var(--ink)}
.rail--oncream .ri-mark{filter:drop-shadow(0 1px 2px rgba(255,255,255,.5))}

/* Automotive (Category 06) — client wants flat white rail text/monogram, no legibility shadow.
   Panels are flipped to data-theme="dark" in automotriz.html so updateRail() always lands on
   rail--onblack; this strips that variant's shadow/glow for this section only. */
[data-group="Category 06"] .rail{text-shadow:none}
[data-group="Category 06"] .rail .ri-mark{filter:none}
/* Services & Other (Category 07) — same white-flat-header treatment as Automotive above. */
[data-group="Category 07"] .rail{text-shadow:none}
[data-group="Category 07"] .rail .ri-mark{filter:none}
/* Organizations & Schools (Category 01) — same white-flat-header treatment as above. */
[data-group="Category 01"] .rail{text-shadow:none}
[data-group="Category 01"] .rail .ri-mark{filter:none}

/* ---------- reveal tiers (fire when slide gets .is-in) ---------- */
[data-reveal]{will-change:transform,opacity,clip-path}
[data-reveal="large"]{clip-path:inset(0 0 105% 0);transform:translateY(.6cqh);
  transition:clip-path 1.05s var(--ease),transform 1.05s var(--ease)}
[data-reveal="info"]{opacity:0;transform:translateY(2.2cqh);
  transition:opacity .9s var(--ease),transform .9s var(--ease)}
[data-reveal="row"]{opacity:0;transform:translateX(-1.4cqw);
  transition:opacity .8s var(--ease),transform .8s var(--ease)}
[data-reveal="btn"]{opacity:0;transform:translateY(1.4cqh) scale(.98);
  transition:opacity .8s var(--ease),transform .8s var(--ease)}
[data-reveal="rule"]{transform:scaleX(0);transform-origin:left;transition:transform 1s var(--ease)}
[data-reveal="img"]{opacity:0;transform:scale(1.05);
  transition:opacity 1.1s var(--ease),transform 1.4s var(--ease)}
.word{display:inline-block;overflow:hidden;vertical-align:top}
.word i{display:inline-block;transform:translateY(110%);opacity:0;font-style:inherit;
  transition:transform .8s var(--ease),opacity .8s var(--ease)}
[data-reveal],.word i{transition-delay:calc(var(--i,0)*70ms + var(--d,0ms))}
.is-in [data-reveal="large"]{clip-path:inset(0 0 -4% 0);transform:none}
.is-in [data-reveal="info"]{opacity:1;transform:none}
.is-in [data-reveal="row"]{opacity:1;transform:none}
.is-in [data-reveal="btn"]{opacity:1;transform:none}
.is-in [data-reveal="rule"]{transform:scaleX(1)}
.is-in [data-reveal="img"]{opacity:1;transform:none}
.mockbox[data-reveal="img"]{opacity:1;transform:scale(1.035);transition:transform .8s var(--ease)}
.is-in .mockbox[data-reveal="img"]{opacity:1;transform:scale(1)}
.is-in .word i{transform:none;opacity:1}
[data-reveal="lines"] .ln{position:relative;display:block;width:fit-content;overflow:hidden;padding-bottom:.14em;margin-bottom:-.14em}
.rc-head[data-reveal="lines"] .ln,.cl-head[data-reveal="lines"] .ln,.st-head[data-reveal="lines"] .ln,.cx-head[data-reveal="lines"] .ln,.ct-title[data-reveal="lines"] .ln{margin-inline:auto}
[data-reveal="lines"] .ln-i{display:block;opacity:0}
[data-reveal="lines"] .ln::after{content:"";position:absolute;inset:0;background:var(--lime);
  transform:scaleX(0);transform-origin:left center}
.is-in [data-reveal="lines"] .ln-i{animation:lnTxt .5s steps(1,end) forwards;animation-delay:calc(var(--li,0)*420ms)}
.is-in [data-reveal="lines"] .ln::after{animation:lnBar .5s cubic-bezier(.7,0,.2,1) forwards;animation-delay:calc(var(--li,0)*420ms)}
@keyframes lnBar{
  0%{transform:scaleX(0);transform-origin:left center}
  42%{transform:scaleX(1);transform-origin:left center}
  58%{transform:scaleX(1);transform-origin:right center}
  100%{transform:scaleX(0);transform-origin:right center}}
@keyframes lnTxt{0%,42%{opacity:0}43%,100%{opacity:1}}
@media(prefers-reduced-motion:reduce){
  [data-reveal="lines"] .ln-i{opacity:1;animation:none}[data-reveal="lines"] .ln::after{display:none}}
[data-reveal="peel"]{opacity:0;transform:perspective(560px) rotateX(-88deg) rotate(-3deg);
  transform-origin:bottom center;
  transition:transform .85s cubic-bezier(.34,1.56,.5,1),opacity .4s var(--ease);
  transition-delay:calc(var(--d,0ms));filter:drop-shadow(0 .4cqh .8cqh rgba(0,0,0,.25))}
.is-in [data-reveal="peel"]{opacity:1;transform:perspective(560px) rotateX(0) rotate(-3deg)}
[data-reveal="blur"]{opacity:0;filter:blur(12px);transform:translateY(1.4cqh);
  transition:opacity 1s var(--ease),filter 1s var(--ease),transform 1s var(--ease);
  transition-delay:calc(var(--d,0ms))}
.is-in [data-reveal="blur"]{opacity:1;filter:blur(0);transform:none}

/* dog-glitch (RGB-split hover glitch) removed 2026-08-26 at user request — the exact recipe is
   preserved in memory (feedback_dog_glitch_recipe_preserved.md) for future reuse. Dogs keep their
   ambient pointer-parallax float (--pxx/--pxy, see the .cv-dog/.dg-dog/.mt-dog/.cl-dog rule below
   and shell.js) — only the hover-triggered tilt + ghost-channel layer was removed. */

/* =========================================================
   COVER
========================================================= */
.cv-wm{position:absolute;left:2cqw;top:12cqh;width:96cqw;height:auto;z-index:4}
.cv-meta{position:absolute;left:58.6cqw;top:45cqh;height:22cqh;
  display:flex;flex-direction:column;justify-content:center;
  font-size:1.6cqw;line-height:1.18;letter-spacing:-.04em;color:var(--white);
  font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:400;z-index:6}
/* the fixed "+128px" nudge only matched its intended desktop position at a ~1600px-wide .stage
   (128px == 8cqw there); on the shrunk mobile letterbox it doesn't scale down with everything
   else, so it eats a much bigger share of the width and pushes the dog into .cv-meta's text —
   clamp() reproduces the same 128px at desktop width while scaling proportionally below it. */
.cv-dog{position:absolute;left:calc(25.4cqw + clamp(20px,8cqw,128px));bottom:6cqh;width:31.2cqw;height:auto;z-index:7;
  filter:drop-shadow(0 1.2cqh 2.2cqh rgba(0,0,0,.45))}
.cv-meta .mline{display:block;font-variant-numeric:tabular-nums;
  clip-path:inset(0 0 112% 0);transform:translateY(18%);
  transition:clip-path .8s var(--ease),transform .8s var(--ease);transition-delay:calc(var(--i,0)*260ms)}
.is-in .cv-meta .mline{clip-path:inset(0 0 -8% 0);transform:none}

/* =========================================================
   THIS WE ARE / THIS WE DO — big Druk headline left, standing dog right, lime pill
========================================================= */
.diag .dg-pill{position:absolute;left:2.5cqw;top:15.5cqh;z-index:4;
  background:var(--lime);color:var(--ink);font-weight:700;font-size:1.33cqw;letter-spacing:-.02em;
  padding:.63cqh 1.2cqw;border-radius:100px;white-space:nowrap}
.diag .dg-head{position:absolute;left:2cqw;top:22.5cqh;width:57cqw;z-index:2;
  font-family:var(--druk);font-weight:700;font-size:5cqw;line-height:.92;
  letter-spacing:-.01em;color:var(--ink)}
.diag .dg-copy{position:absolute;left:2cqw;top:59cqh;width:46cqw;font-size:1.4cqw;
  line-height:1.32;color:var(--ink);z-index:2}
.diag .dg-copy .em{display:block;margin-top:1.5cqh;font-weight:700}
.diag .dg-dog{position:absolute;right:3cqw;bottom:0;height:80cqh;width:auto;z-index:3;
  filter:drop-shadow(0 1cqh 2cqh rgba(0,0,0,.18))}
.diag.inv .dg-dog{height:96cqh}
.diag.inv .dg-head{color:var(--cream)}
.diag.inv .dg-copy{color:var(--cream)}
.diag.inv .dg-pill{background:var(--lime);color:var(--ink)}

/* =========================================================
   CASE COVER — logo is the sole hero (title text removed as redundant with the
   logo mark itself, per client feedback), service pills bottom, navy
========================================================= */
.casetitle .ct-logo{position:absolute;left:0;right:0;top:45cqh;transform:translateY(-50%);
  z-index:4;text-align:center}
/* text-only case cover — the original pre-logo case-cover treatment (Druk Wide + Druk XCond
   two-weight headline), revived for the grouped "Other Clients" cover which has no single
   brand mark of its own. Restored verbatim from the pre-logo build. */
.casetitle .ct-title{position:absolute;left:0;right:0;top:47cqh;transform:translateY(-50%);
  text-align:center;white-space:nowrap;line-height:.82;color:var(--white);z-index:4;text-transform:uppercase}
.casetitle .ct-title .w1{font-family:var(--druk);font-weight:800;font-size:7cqw;letter-spacing:-.01em}
.casetitle .ct-title .w2{font-family:var(--drukx);font-weight:800;font-size:7cqw;letter-spacing:.01em;margin-left:.7cqw}
/* height-capped, not width-capped: source logos vary from wide/short wordmarks (Powerful,
   Cisco) to square badges (UTEL, Tropical Organic Growers) — a fixed WIDTH let the squarer
   ones render much taller and physically overlap the pills below. A shared max-height keeps
   every logo the same optical weight regardless of its native aspect ratio. Vertically centered
   (rather than pinned to a fixed top) so the height difference between the wordmark tier and
   the taller badge tier never throws off the slide's visual balance. */
.casetitle .ct-logo img{width:auto;max-width:46cqw;height:9cqh;display:inline-block}
/* a few source badges (Taitto, Tropical Organic Growers, MG) are a near-circular seal with
   fine detail inside — at the shared 9cqh height they'd still read as small. Bump just these
   via an opt-in modifier rather than raising the shared cap (would blow out the wordmarks). */
.casetitle .ct-logo--badge img{height:15cqh}
.casetitle .ct-logo--gwm img{height:15cqh}
.casetitle .ct-logo--ilsb img{height:16cqh}
.casetitle .ct-logo--brugal img{height:13cqh}
.casetitle .ct-logo--heineken img{height:12cqh}
.casetitle .ct-logo--powerful img{height:12cqh}
.casetitle .ct-logo--guerlain img{height:7cqh}
.casetitle .ct-logo--sephora img{height:7cqh}
.casetitle .ct-logo--volvo img{height:5cqh}
.casetitle .ct-logo--mary-kay img{height:7cqh}
.casetitle .ct-logo--la-nacion img{height:6.5cqh}
.casetitle .ct-logo--utel img{height:23cqh}
.casetitle .ct-logo--la-lupita img{height:13cqh}
.casetitle .ct-pills{position:absolute;left:0;right:0;top:57cqh;z-index:4;
  display:flex;flex-wrap:wrap;gap:.7cqw;justify-content:center;padding:0 6cqw}
.casetitle .ct-pill{border:.1cqw solid rgba(241,238,230,.55);color:var(--cream);border-radius:100px;
  padding:.82cqh 1.5cqw;font-size:.96cqw;font-weight:700;white-space:nowrap;text-align:center}
.casetitle .ct-pill.on{background:var(--lime);color:var(--ink);border-color:var(--lime)}

/* =========================================================
   PROJECT MOCKUP GALLERY — horizontal filmstrip track of panels within ONE page
========================================================= */
.track{position:absolute;inset:0;display:flex;width:100%;height:100%;will-change:transform}
.panel{position:relative;flex:0 0 100%;width:100%;height:100%;display:grid;place-items:center;overflow:hidden}
.grail-stage{z-index:7;pointer-events:none}
.grail,.grail .ri,.grail .dim,.grail .lav{transition:color .45s ease}
.panel .full{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:1}
/* v2: mockbox is full-bleed now — no padded/rounded frame. Images run edge-to-edge behind the
   header; each panel sets data-theme="dark"|"light" so the shared .grail header (updateRail() in
   shell.js) switches rail--onblack/rail--oncream to stay legible over whatever's underneath. */
.panel .mockbox{position:absolute;inset:0;overflow:hidden;z-index:1}
.panel .mockbox img{position:absolute;inset:-3%;width:106%;height:106%;object-fit:cover;
  translate:calc(var(--pxx,0px) + var(--sxx,0px)) var(--pxy,0px)}
.panel .mockbox--fit{overflow:visible}
.panel .mockbox--fit img{inset:0;width:100%;height:100%;object-fit:contain;object-position:center}
/* video creative (ad footage, not ambient/looping background) — always contained, never cropped,
   native controls left fully interactive so play/pause/scrub/fullscreen all work as expected */
.panel .mockbox video{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;
  background:#000;cursor:pointer}
/* multi-cell collage — reproduces a source Keynote slide's own layout of mixed photos/videos at
   their real positions (percentages taken straight from the slide's 1920x1080 canvas), instead of
   flattening everything to one static export or breaking video cells out into their own panels */
.panel .mockbox .cgrid{position:absolute;inset:0}
.panel .mockbox .cgrid video,.panel .mockbox .cgrid img{position:absolute;object-fit:cover;
  background:#000}
/* cgrid-contain — for a cgrid image with real alpha transparency and edge-hugging content (e.g.
   text near the top/bottom of each frame): .cgrid's own box isn't locked to 16:9 (gallery panels
   fill the raw viewport, unlike .stage), so plain % width/height sized against a 16:9 assumption
   only avoids object-fit:cover cropping at exactly that aspect ratio. contain guarantees the full
   image is always visible, and dropping the shared #000 lets the real bgtex show through instead
   of a solid black bar in the transparent gaps. */
.panel .mockbox .cgrid img.cgrid-contain{object-fit:contain;background:transparent}
/* JS (shell.js) wraps every video in a .vid-wrap to host the big-play-button overlay without
   disturbing whatever positioning the video already had — full-mockbox fill for a lone video,
   or the .cgrid cell's own inline left/top/width/height (copied onto the wrap) for a collage cell. */
.panel .mockbox .vid-wrap{position:absolute}
.panel .mockbox>.vid-wrap{inset:0;width:100%;height:100%}
.panel .mockbox .vid-wrap video{position:absolute;inset:0;width:100%;height:100%}
.panel .mockbox .vid-play{position:absolute;inset:0;margin:auto;width:15%;aspect-ratio:1;
  min-width:44px;min-height:44px;max-width:74px;max-height:74px;border-radius:50%;
  background:rgba(0,0,0,.4);border:none;display:flex;align-items:center;justify-content:center;
  cursor:pointer;padding:0;z-index:2;transition:background .2s ease,transform .2s ease}
.panel .mockbox .vid-play:hover{background:rgba(0,0,0,.55);transform:scale(1.08)}
.panel .mockbox .vid-play svg{width:38%;height:38%;fill:#fff;margin-left:8%}
.panel .mock-desc{position:absolute;right:2cqw;bottom:7.5cqh;width:33cqw;z-index:3;
  background:var(--lime);color:var(--ink);padding:2.6cqh 2.1cqw;
  border-radius:1.35cqw 1.35cqw 1.04cqw 1.35cqw;
  box-shadow:0 0 0 1.5px var(--lime);max-height:40cqh;overflow:hidden;
  display:flex;flex-direction:column;
  transition:width .4s cubic-bezier(.4,0,.2,1),max-height .4s cubic-bezier(.4,0,.2,1),
    padding .4s cubic-bezier(.4,0,.2,1)}
.panel .mock-desc .idx{font-size:.95cqw;font-weight:700;letter-spacing:.02em;opacity:.55;
  margin-bottom:.9cqh;transition:margin-bottom .4s cubic-bezier(.4,0,.2,1),opacity .25s ease}
.panel .mock-desc h3{font-family:var(--font);font-weight:700;font-size:2.3cqw;line-height:1.08;
  letter-spacing:-.02em;margin-bottom:1.3cqh;flex-shrink:0;
  transition:opacity .2s ease,max-height .4s cubic-bezier(.4,0,.2,1),margin-bottom .4s cubic-bezier(.4,0,.2,1)}
.panel .mock-desc p{font-size:1.02cqw;line-height:1.44;font-weight:500;flex-shrink:0;
  transition:opacity .2s ease,max-height .4s cubic-bezier(.4,0,.2,1)}
/* auto-minimize: a few seconds after a stop settles (armMintCollapse() in shell.js), the
   description box shrinks to a small round pill so it stops covering the video/image behind
   it. Reopen is JS-driven (mouseenter/mouseleave with a short linger on leave in shell.js), not
   pure CSS :hover — since the hover TARGET is also the element that resizes itself, plain :hover
   can re-evaluate mid-transition and flicker; a JS-held class avoids that entirely. border-radius
   is intentionally NOT in the transition list — animating a large fixed radius against a rapidly
   changing width reads as a wobble/bounce, so it snaps instantly instead. */
.panel .mock-desc.collapsed{width:3.6cqw;max-height:3.6cqw;padding:0;
  border-radius:50%;cursor:pointer;align-items:center;justify-content:center;aspect-ratio:1}
.panel .mock-desc.collapsed .idx{margin-bottom:0;opacity:.9;text-align:center}
.panel .mock-desc.collapsed h3,
.panel .mock-desc.collapsed p{opacity:0;max-height:0;margin-bottom:0}
.panel .mock-desc.collapsed.force-open{width:33cqw;max-height:40cqh;padding:2.6cqh 2.1cqw;
  border-radius:1.35cqw 1.35cqw 1.04cqw 1.35cqw;align-items:stretch;justify-content:flex-start;
  aspect-ratio:auto}
.panel .mock-desc.collapsed.force-open .idx{margin-bottom:.9cqh;opacity:.55;text-align:left}
.panel .mock-desc.collapsed.force-open h3{opacity:1;max-height:100cqh;margin-bottom:1.3cqh}
.panel .mock-desc.collapsed.force-open p{opacity:1;max-height:100cqh}
.panel .mock{position:absolute;left:50%;top:53%;transform:translate(-50%,-50%);
  height:84cqh;width:auto;max-width:93cqw;z-index:1}
.panel .contain{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;z-index:1}
.panel .laptop-big{position:absolute;left:50%;top:53%;transform:translate(-50%,-50%);width:78cqw;z-index:2}
.panel .card{position:absolute;right:2.4cqw;bottom:8cqh;width:33cqw;z-index:5;
  background:var(--lime);border-radius:var(--radius);padding:2.8cqh 2.2cqw}
.panel .card .n{font-size:1.05cqw;font-weight:700;color:var(--ink);opacity:.6}
.panel .card h2{font-size:2.4cqw;font-weight:800;letter-spacing:-.03em;color:var(--ink);
  margin:.5cqh 0 1.2cqh;line-height:1.02}
.panel .card p{font-size:.98cqw;line-height:1.5;color:var(--ink)}
.panel .plabel{position:absolute;left:2.4cqw;bottom:7cqh;font-size:1.15cqw;font-weight:600;z-index:5}
.panel--dark .plabel{color:var(--cream)} .panel--light .plabel{color:var(--ink)}

/* ---------- opening curtain (Vigneri-style) — only played on the deck's true entry point.
   mc-noc is set by a BLOCKING inline script in <head>, before first paint — a page-load JS
   removal (at end of body) runs too late: a cross-document View Transition captures the
   incoming page's snapshot around first paint, so a curtain still visible at that moment gets
   swept into the transition itself (the "green flash, then the real page" bug). Hiding it via
   CSS before anything renders is the only way to keep it out of that snapshot entirely. ---------- */
html.mc-noc .curtain{display:none}
.curtain{position:fixed;inset:0;z-index:999;background:var(--lime);pointer-events:none;
  display:flex;align-items:center;justify-content:center;
  clip-path:polygon(0 0,140% 0,100% 100%,0 100%);
  animation:curtainOff 1s cubic-bezier(.7,0,.28,1) 1.35s forwards}
.curtain .mc-mark{width:min(11vw,150px);height:auto;opacity:0;
  animation:mcFade 1.15s cubic-bezier(.19,1,.22,1) .15s forwards}
@keyframes mcFade{0%{opacity:0;transform:scale(.82)}100%{opacity:1;transform:scale(1)}}
.curtain .csig{width:min(50vw,640px);aspect-ratio:2897/414;background:var(--cream);
  -webkit-mask:url(brand/wordmark-malacara-white.png) center/contain no-repeat;
  mask:url(brand/wordmark-malacara-white.png) center/contain no-repeat;
  clip-path:inset(0 100% 0 0);animation:sigDraw 1s cubic-bezier(.5,0,.2,1) .25s forwards}
@keyframes sigDraw{to{clip-path:inset(0 0 0 0)}}
@keyframes curtainOff{to{clip-path:polygon(140% 0,140% 0,100% 100%,100% 100%)}}
@media (prefers-reduced-motion:reduce){.curtain{display:none}}

/* ---------- grain ---------- */
.grain{position:fixed;inset:0;pointer-events:none;z-index:52;opacity:.05;mix-blend-mode:overlay}

/* ---------- pill nav ---------- */
.pillnav{position:fixed;left:50%;bottom:3.2vh;transform:translateX(-50%);z-index:60;
  display:flex;align-items:center;gap:14px;background:rgba(4,10,28,.72);
  backdrop-filter:blur(14px);border:1px solid rgba(242,242,234,.16);border-radius:100px;
  padding:9px 12px;color:var(--cream);box-shadow:0 10px 40px rgba(0,0,0,.4)}
.pillnav button{background:none;border:0;color:var(--cream);cursor:pointer;width:38px;height:38px;
  border-radius:50%;display:grid;place-items:center;transition:background .3s,transform .3s}
.pillnav button:hover{background:rgba(242,242,234,.14)}
.pillnav button:active{transform:scale(.9)}
.pillnav .nav-arrow svg{width:16px;height:16px;stroke:currentColor;stroke-width:2.4;fill:none}
.pillnav .home{font-weight:800;font-size:12px;letter-spacing:.06em;width:auto;padding:0 12px;border-radius:100px}
.pillnav .count{font-size:13px;font-weight:600;letter-spacing:.08em;padding:0 6px;
  font-variant-numeric:tabular-nums;min-width:32px;text-align:center}
.pillnav .count b{color:var(--cream)}
.pillnav .count .count-total{display:none}
.pillnav [data-act="menu"]{color:var(--lime)}
.burger{width:20px;height:14px;position:relative}
.burger span{position:absolute;left:0;width:100%;height:2px;background:currentColor;border-radius:2px;
  transition:transform .4s var(--ease),opacity .3s,top .3s}
.burger span:nth-child(1){top:0}.burger span:nth-child(2){top:6px}.burger span:nth-child(3){top:12px}
.menu-open .burger span:nth-child(1){top:6px;transform:rotate(45deg)}
.menu-open .burger span:nth-child(2){opacity:0}
.menu-open .burger span:nth-child(3){top:6px;transform:rotate(-45deg)}

/* ---------- menu overlay: thumbnail matrix ---------- */
.menu{position:fixed;inset:0;z-index:55;background:#EFEFEF;color:var(--ink);
  padding:6.5vh 4vw 6vh;opacity:0;visibility:hidden;transition:opacity .5s var(--ease),visibility .5s;overflow-y:auto}
.menu.open{opacity:1;visibility:visible}
.menu-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:1.4vw}
.menu-grid .card{position:relative;cursor:pointer;overflow:hidden;border-radius:14px;aspect-ratio:16/9;
  background:#111;opacity:0;transform:translateY(28px);
  box-shadow:0 8px 16px -2px rgba(0,0,0,.28);
  transition:opacity .6s var(--ease),transform .5s var(--ease),box-shadow .35s var(--ease);
  transition-delay:calc(min(var(--i,0),26)*16ms)}
.menu-grid.animate-in .card{opacity:1;transform:translateY(0)}
.menu-grid .card:hover,.menu-grid .card.active{transition-delay:0s}
.menu-grid .card:hover{transform:translateY(-6px) scale(1.02);
  box-shadow:0 26px 50px -10px rgba(0,0,0,.55),0 6px 18px rgba(0,0,0,.35)}
.menu-grid .card.active{box-shadow:0 0 0 2px var(--lime),0 20px 40px -8px rgba(0,0,0,.45)}
.menu-grid .card img{width:100%;height:100%;object-fit:cover;display:block}

/* =========================================================
   STUDIO SLIDES — Method / Steps / Recipe / Closing
========================================================= */
.eyebrow{font-size:1.05cqw;font-weight:700;letter-spacing:.02em;text-transform:uppercase;color:var(--lime)}
.stamp{display:inline-block;background:var(--lime);color:var(--ink);font-weight:700;
  font-size:1.33cqw;letter-spacing:-.02em;padding:.6cqh 1.15cqw;border-radius:100px;white-space:nowrap}

/* CLIENTS — logo matrix */
.clients{background:var(--cream)}
.clients .stage{color:var(--ink)}
.cx-head{position:absolute;left:8.25cqw;right:8.25cqw;top:14cqh;text-align:center;font-family:var(--druk);
  font-weight:700;font-size:3.6cqw;line-height:.92;letter-spacing:-.01em;color:var(--ink)}
.cx-body{position:absolute;left:8.25cqw;right:8.25cqw;top:20cqh;bottom:9cqh;
  display:flex;flex-direction:column;justify-content:center}
.cx-tag{align-self:flex-start}
.cx-tag--active{margin-bottom:2.6cqh}
.cx-tag--past{margin-top:7.5cqh;margin-bottom:2.8cqh}
/* flex + space-between so every row (full or partial) stretches edge-to-edge — first logo flush
   left, last logo flush right — matching how the top row reads. cx-item's flex-basis is fixed to
   a shared 9-column width (not content-hugging, not section-specific) so a same-length row lands
   at the same x-positions whether it's Active or Past; the explicit row-break markers keep both
   sections split into the identical two row lengths so their rows stay comparable. */
.cx-grid{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;width:100%;column-gap:0}
.cx-row-break{flex-basis:100%;height:0}
.cx-grid--active{row-gap:4.5cqh}
.cx-grid--past{row-gap:4.4cqh}
.cx-item{flex:0 0 10.2%;border:0;background:none;padding:.3cqh .4cqw;border-radius:1cqw;
  cursor:pointer;display:flex;align-items:center;justify-content:center;
  transition:transform .3s var(--ease),background-color .3s var(--ease)}
.cx-item:hover{background-color:rgba(0,7,25,.06)}
.cx-item img{display:block;width:auto;max-width:94%;object-fit:contain;filter:none}
.cx-grid--active .cx-item img{height:3.3cqh}
.cx-grid--past .cx-item img{height:3.4cqh}
/* GWM's source is a very wide/short wordmark (4.75:1) — the shared height cap that keeps
   square-ish badges consistent reads as noticeably smaller for it specifically. */
.cx-grid--active .cx-item--gwm img{height:5.5cqh}
/* same class of fix as .ct-logo--badge on the case covers: these client-wall marks are
   compact/near-square seals (or just visually thin), so the shared 3.4cqh past-row height
   reads as illegible. Bumped per-logo, scaled to how far each sits from a normal wordmark. */
.cx-grid--past .cx-item--tog img{height:4.6cqh}
.cx-grid--past .cx-item--mg img{height:5.4cqh}
.cx-grid--past .cx-item--astrazeneca img{height:5.6cqh}
.cx-grid--past .cx-item--happy-planet img{height:4.9cqh}
.cx-grid--past .cx-item--ilsb img{height:5.4cqh}
.cx-grid--past .cx-item--utel img{height:6.4cqh}
.cx-grid--past .cx-item--brugal img{height:4.48cqh}
.cx-grid--active .cx-item--generacion img{height:4.5cqh}
.cx-grid--active .cx-item--hidrozono img{height:4.8cqh}
/* Sephora, Guerlain, Cisco, Macallan, La Nación read noticeably larger than their neighbors
   at the shared active-row height — shrunk in place, position unchanged. */
.cx-grid--active .cx-item--sephora img{height:1.9cqh}
.cx-grid--active .cx-item--guerlain img{height:1.9cqh}
.cx-grid--active .cx-item--cisco img{height:2.3cqh}
.cx-grid--active .cx-item--macallan img{height:2.3cqh}
.cx-grid--active .cx-item--la-nacion img{height:2.0cqh}
.cx-grid--active .cx-item--mary-kay img{height:2.4cqh}
.cx-grid--active .cx-item--la-lupita img{height:5.4cqh}
[data-reveal].cx-item{transition-delay:calc(var(--i,0)*35ms)}
.cx-logo{position:relative;display:inline-block;transition:transform .35s var(--ease);
  transform-origin:center;will-change:transform}
.cx-logo img:first-child{position:relative}
.cx-logo .gl{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;
  opacity:0;pointer-events:none;mix-blend-mode:multiply}
.cx-item:hover .cx-logo{transform:perspective(600px) rotateY(-7deg) rotateX(3deg) scale(1.06)}
.cx-item:hover .cx-logo .gl-r{opacity:.55;filter:sepia(1) saturate(9) hue-rotate(-38deg) brightness(1.1);
  animation:glchR .22s steps(2,end) infinite}
.cx-item:hover .cx-logo .gl-c{opacity:.55;filter:sepia(1) saturate(9) hue-rotate(150deg) brightness(1.1);
  animation:glchC .28s steps(2,end) infinite}
@media(prefers-reduced-motion:reduce){.cx-item:hover .cx-logo{transform:none}.cx-logo .gl{display:none}}

/* ---------- want-more (annex teaser, malacara-studio clone only) — title + thumbnail grid on
   one slide, same shape as the Clients slide (.cx-head/.cx-body/.cx-grid) but sized for
   photographic case-page thumbnails instead of logo marks. Light (cream) ground and the
   menu overlay's own card treatment (shadow/bevel/hover-lift) so it reads like the hamburger
   menu's thumbnail matrix rather than a dark case cover. */
.wm-head{position:absolute;left:8.25cqw;right:8.25cqw;top:15cqh;text-align:center;font-family:var(--druk);
  font-weight:700;font-size:5.4cqw;line-height:.92;letter-spacing:-.01em;color:var(--ink)}
.wm-head[data-reveal="lines"] .ln{margin-inline:auto}
.wm-body{position:absolute;left:8.25cqw;right:8.25cqw;top:26cqh;bottom:8cqh;
  display:flex;flex-direction:column;justify-content:center}
.wm-grid{display:grid;grid-template-columns:repeat(4,1fr);column-gap:1.6cqw;row-gap:3.6cqh;width:100%}
.wm-item{border:0;background:none;padding:0;cursor:pointer;text-align:left;
  transition:transform .3s var(--ease)}
.wm-item:hover{transform:translateY(-6px) scale(1.02)}
/* display:block is load-bearing, NOT cosmetic. .wm-thumb is a <span>, so without it the element
   computes to display:inline — and an inline box paints its shadow (and background, and radius)
   around its own inline fragments, of which it has none: its only child is a display:block <img>.
   Result: box-shadow/background/border-radius/aspect-ratio all silently no-op'd while
   getComputedStyle still reported them, and getBoundingClientRect returned the img's box, so the
   card looked correct with no shadow at any value. That's the real cause of the "shadow won't
   render" behaviour the old comment here blamed on overflow:hidden + border-radius, and why the
   values had been escalated to 2.2cqw/3.6cqw at .65 alpha chasing a shadow that never painted.
   Verified by sampling ground pixels below the card: flat cream before, proper falloff after. */
/* shadow itself is lifted from the hamburger menu's .menu-grid .card (rest + hover + the .35s
   box-shadow transition) so the two thumbnail matrices read as one component. Offsets are scaled
   1.37x from the menu's px values because these cards are correspondingly bigger at the same
   viewport (336px vs 245px at 1728w) — matching the ratio is what makes them LOOK matched;
   copying the px verbatim reads ~27% weaker. No inset hairline: the menu cards don't have one. */
.wm-item .wm-thumb{display:block;position:relative;border-radius:14px;aspect-ratio:16/9;
  background:#111;box-shadow:0 11px 22px -3px rgba(0,0,0,.28);
  transition:box-shadow .35s var(--ease)}
.wm-item:hover .wm-thumb{box-shadow:0 36px 68px -14px rgba(0,0,0,.55),0 8px 25px rgba(0,0,0,.35)}
.wm-item .wm-thumb img{width:100%;height:100%;object-fit:cover;display:block;
  border-radius:inherit}
.wm-item .wm-label{display:block;margin-top:1.1cqh;font-size:.76cqw;font-weight:500;
  color:var(--ink);letter-spacing:-.03em;text-transform:uppercase}
[data-reveal].wm-item{transition-delay:calc(var(--i,0)*45ms)}
@media(max-width:640px){.wm-grid{grid-template-columns:repeat(2,1fr)}}

/* METHOD */
.method{background:var(--ink)}
.method .stage{color:var(--cream)}
.mt-eyebrow{position:absolute;left:2cqw;top:16.5cqh}
.mt-title{position:absolute;left:2cqw;top:22cqh;width:60cqw;font-family:var(--druk);font-weight:700;
  font-size:6.4cqw;line-height:.9;letter-spacing:-.01em;color:var(--cream)}
.mt-title .w2{font-family:var(--drukx);font-weight:800;letter-spacing:.01em}
.mt-copy{position:absolute;left:2cqw;top:56cqh;width:46cqw;font-size:1.45cqw;line-height:1.42;color:var(--cream)}
.mt-copy .em{color:var(--lime);font-weight:700}
.method .mt-dog{position:absolute;right:3cqw;bottom:0;height:80cqh;width:auto;z-index:3;
  filter:drop-shadow(0 1.6cqh 2.6cqh rgba(0,0,0,.4))}

/* FOUR STEPS */
.steps{background:var(--ink)}
.steps .stage{color:var(--cream)}
.st-head{position:absolute;left:2cqw;right:2cqw;top:18.5cqh;text-align:center;font-family:var(--druk);font-weight:700;
  font-size:3.3cqw;line-height:1;letter-spacing:-.01em;color:var(--cream)}
.st-grid{position:absolute;left:2cqw;right:2cqw;top:33cqh;bottom:6.5cqh;
  display:grid;grid-template-columns:repeat(4,1fr);gap:1.5cqw}
.st-card{min-width:0;border-radius:1.7cqw;padding:2.6cqh 1.3cqw 0;display:flex;flex-direction:column;
  align-items:center;text-align:center;position:relative;overflow:hidden;
  transition:transform .45s var(--ease),box-shadow .45s var(--ease);will-change:transform}
.st-card:hover{transform:translateY(-1.8cqh) rotate(var(--rot,0deg)) scale(1.03);
  box-shadow:0 2.4cqh 4cqh rgba(0,0,0,.42);z-index:6}
.st-card.lime{background:var(--lime);color:var(--ink)}
.st-card.dark{background:#0e1c38;color:var(--cream)}
.st-card .n{font-family:var(--font);font-weight:700;font-size:1.5cqw;letter-spacing:.02em;line-height:1;opacity:.6}
.st-card.lime .n{color:var(--ink)}
.st-card.dark .n{color:var(--lime)}
.st-card h4{font-family:var(--font);font-weight:700;font-size:1.9cqw;line-height:1;letter-spacing:-.02em;margin:1cqh 0 1.1cqh}
.st-card p{font-size:1.04cqw;line-height:1.4}
.st-card.lime p{color:rgba(0,7,25,.8)}
.st-card.dark p{color:rgba(241,238,230,.76)}
.st-hand{width:auto;height:38cqh;margin-top:auto;object-fit:contain;object-position:bottom center;pointer-events:none}

/* OUR RECIPE */
.recipe{background:var(--ink)}
.recipe .stage{color:var(--cream)}
.rc-fork{position:absolute;left:6.5cqw;bottom:0;height:83cqh;width:auto;z-index:1}
.rc-spoon{position:absolute;right:6.5cqw;bottom:0;height:83cqh;width:auto;z-index:1}
.rc-head{position:absolute;left:0;right:0;top:14cqh;text-align:center;z-index:3;
  font-family:var(--druk);font-weight:700;font-size:4.6cqw;line-height:.92;letter-spacing:-.01em;color:var(--cream)}
.rc-grid{position:absolute;left:25cqw;right:25cqw;top:31cqh;bottom:17.5cqh;z-index:3;
  display:grid;grid-template-columns:1fr 1fr;grid-template-rows:1fr 1fr;gap:1.3cqw}
.rc-card{background:var(--lime);color:var(--ink);border-radius:1.3cqw;padding:2.2cqh 1.7cqw;
  display:flex;flex-direction:column;overflow:hidden;
  transition:transform .45s var(--ease),box-shadow .45s var(--ease);will-change:transform}
.rc-card:hover{transform:translateY(-1.2cqh) rotate(var(--rot,0deg)) scale(1.03);
  box-shadow:0 2cqh 3.6cqh rgba(0,0,0,.42);z-index:5}
.rc-card .arw{display:block;width:2.3cqw;height:2.3cqw;stroke:var(--ink);opacity:.6;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
.rc-card p{margin-top:auto;font-family:var(--font);font-weight:700;font-size:1.5cqw;line-height:1.15;
  letter-spacing:-.01em;color:var(--ink)}
.rc-pills{position:absolute;left:0;right:0;bottom:9.5cqh;z-index:3;display:flex;flex-wrap:wrap;gap:.6cqw;
  justify-content:center;padding:0 4cqw}
.rc-pill{border:.1cqw solid rgba(241,238,230,.5);color:var(--cream);border-radius:100px;
  padding:.72cqh 1.3cqw;font-size:.9cqw;font-weight:700;white-space:nowrap}
.rc-pill.on{background:var(--lime);color:var(--ink);border-color:var(--lime)}

/* CLOSING */
.closing{background:var(--ink)}
.closing .stage{color:var(--cream)}
.cl-head{position:absolute;left:0;right:0;top:31cqh;text-align:center;z-index:4;
  font-family:var(--drukx);font-weight:800;font-size:6.4cqw;line-height:1;letter-spacing:.015em;
  text-transform:uppercase;color:var(--white)}
.cl-wm{position:absolute;left:2cqw;bottom:6cqh;width:96cqw;height:auto;z-index:2}
.cl-dog{position:absolute;left:29cqw;bottom:1.5cqh;width:40cqw;height:auto;z-index:3;
  filter:drop-shadow(0 1.4cqh 2.6cqh rgba(0,0,0,.5))}

/* ---------- mobile reflow ---------- */
@media (max-width:820px){
  .slide{box-shadow:none}
  .pillnav{bottom:max(2.4vh,env(safe-area-inset-bottom,0px))}
  .menu{padding:6vh 4vw 6vh}
  .menu-grid{grid-template-columns:repeat(auto-fill,minmax(42vw,1fr));gap:2.6vw}
  /* closing slide: the dog running across the wordmark is a deliberate desktop flourish, but at
     the shrunk mobile letterbox scale it just blocks the text — stack them instead of overlapping. */
  .cl-dog{left:38cqw;bottom:auto;top:38cqh;width:22cqw}
  .cl-wm{bottom:2cqh;top:auto;width:90cqw;left:5cqw}
  /* cover slide: same problem — the dog overlapping MEXICO CITY/BUENOS AIRES/etc. is a legible
     flourish at desktop scale (large text, dog covers only part of a word) but at mobile scale
     the meta list is already small, and the same relative overlap makes it unreadable. Center the
     dog under the wordmark (matches the rest of the deck's mobile stacking) and move the meta
     list above it instead of beside it, clear of the dog's own vertical band. */
  /* dog +20% (16.8 -> 20.16cqw, kept centered on 50cqw). At the old 4-line meta stack there was
     no room for it: the stage is exactly full at the old size (dog bottom landed on 99.4cqh) and
     the extra 9.5cqh of height either clipped the paws on the stage edge or drove the head into
     the city list. The room comes from the meta instead — see below. */
  .cv-dog{left:39.92cqw;width:20.16cqw;bottom:auto;top:42.5cqh}
  /* meta reflows from a 4-line column to one centered dot-separated line on mobile: same words,
     same size, ~10cqh of vertical space handed back to the dog. Separators are generated so the
     four .mline spans keep their staggered clip-path reveal untouched. */
  .cv-meta{left:0;right:0;top:38cqh;height:auto;text-align:center;
    flex-direction:row;flex-wrap:wrap;justify-content:center;align-items:baseline;column-gap:.5em}
  .cv-meta .mline+.mline::before{content:"\00B7";margin-right:.5em;opacity:.6}
}
.rotate-hint{display:none}
/* cover dog, second +20% (20.16 -> 24.19cqw), PORTRAIT ONLY — and it has to be portrait only.
   At the previous size the dog's feet already landed on 98.8cqh, i.e. the stage floor, so this
   bump needs ~11cqh that simply is not there: the stack above it (rail, wordmark, city line) is
   already flush. In portrait that is fine, because the 16:9 stage is a letterbox with ~312px of
   dead navy beneath it and .slide carries the same background, so the dog can grow past the
   stage edge and the seam is invisible — that is what overflow:visible buys, and nothing else on
   this slide extends beyond the stage. In landscape the stage IS the viewport, there is no dead
   space, and the same growth would crop the dog's feet against the screen edge — so landscape
   keeps the previous size. */
@media (max-width:820px) and (orientation:portrait){
  .cover .stage{overflow:visible}
  .cv-dog{left:37.91cqw;width:24.19cqw}
}
@media (max-width:820px) and (orientation:portrait){
  .rotate-hint{display:flex;position:fixed;left:0;right:0;bottom:calc(14vh + env(safe-area-inset-bottom,0px));
    z-index:40;justify-content:center;pointer-events:none}
  .rotate-hint span{background:var(--lime);color:var(--ink);font-size:12px;font-weight:700;
    letter-spacing:-.01em;padding:7px 14px;border-radius:100px}
}
@media (prefers-reduced-motion:reduce){
  [data-reveal],.word i{transition:none!important}.deck{scroll-behavior:auto}
}
.cv-dog,.dg-dog,.mt-dog,.cl-dog{translate:var(--pxx,0) var(--pxy,0)}
.cv-dog video,.dg-dog video,.mt-dog video,.cl-dog video{position:absolute;inset:0;width:100%;height:100%;
  object-fit:contain;object-position:center bottom}
/* poster stand-in — see the ambient-dog block in shell.js. Framed exactly like the video it
   covers, and later in the DOM, so it paints on top. While merely LOADING the video stays visible
   underneath (hiding it would stall WebKit's own fetch); only when autoplay is REFUSED do we hide
   it, because WebKit's start-playback chrome would otherwise show through the poster's alpha. */
.dog-still{position:absolute;inset:0;width:100%;height:100%;
  object-fit:contain;object-position:center bottom;pointer-events:none}
.cv-dog.is-refused video,.dg-dog.is-refused video,
.mt-dog.is-refused video,.cl-dog.is-refused video{visibility:hidden}
