/* Porta Productions — cinematic motion layer (v3).
   Sections A…F map to the base pass; G-series is the "all-out" layer.
   Every hidden/offset state is scoped under html.mjs, which motion.js sets only
   when it can animate. No JS -> no .mjs -> page renders fully visible. */

:root{
  --m1:cubic-bezier(.2,.7,.2,1);        /* house ease */
  --m2:cubic-bezier(.16,1,.3,1);        /* expo-out, big moments */
}

/* A1 · page-load curtain (skipped when the G1 letterbox plays instead) */
html.mjs body{opacity:0;}
html.mjs.mjs-ready body{opacity:1;transition:opacity .45s var(--m1);}
html.mjs.mlbx body{opacity:1;transition:none;}

/* A1b · jump arrival. Held for a single frame while revealTo() force-shows
   everything above the fold, so a deep link lands on a finished page. Scoped
   to what this layer animates rather than `*`, which would recalc the world. */
html.mjs.mnx .reveal,html.mjs.mnx .mli,html.mjs.mnx .mrule,html.mjs.mnx .mclip,
html.mjs.mnx .mwipe,html.mjs.mnx .kicker,html.mjs.mnx .steps .step::before,
html.mjs.mnx #hero .mhi,html.mjs.mnx #hero .mhs{
  transition-duration:0s!important;transition-delay:0s!important;}

/* A2 · scroll progress hairline */
.mprog{position:fixed;top:0;left:0;right:0;height:2px;z-index:110;pointer-events:none;
  background:linear-gradient(90deg,var(--accent,#9AA0A7),var(--hi,#C8CDD3));
  transform:scaleX(var(--mp,0));transform-origin:0 50%;will-change:transform;}

/* A3 · film grain (desktop only, very subtle).
   Deliberately transform-driven on an oversized fixed tile and with NO blend
   mode: animating background-position over a full-viewport mix-blend layer
   repaints and re-blends the whole screen every frame, which measured as the
   single largest cost in the layer. A stepped transform is compositor-only,
   and on a navy ground plain low-opacity noise reads the same. */
.mgrain{display:none;}
@media (pointer:fine) and (min-width:1080px){
  .mgrain{display:block;position:fixed;top:-50%;left:-50%;width:200%;height:200%;
    z-index:95;pointer-events:none;opacity:.045;
    background-repeat:repeat;background-size:128px 128px;
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='128' height='128'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='128' height='128' filter='url(%23n)'/%3E%3C/svg%3E");
    will-change:transform;animation:mgrain 1.1s steps(1,end) infinite;}
}
@keyframes mgrain{
  0%{transform:translate3d(0,0,0);}
  20%{transform:translate3d(-3%,2%,0);}
  40%{transform:translate3d(2%,-3%,0);}
  60%{transform:translate3d(-2%,-2%,0);}
  80%{transform:translate3d(3%,1%,0);}
  100%{transform:translate3d(0,0,0);}
}

/* B0 · baseline reveal (pages without their own .reveal rule) */
.mjs .reveal{opacity:0;transform:translateY(20px);
  transition:opacity .5s var(--m1),transform .5s var(--m1);}
.mjs .reveal.in{opacity:1;transform:none;}

/* B1 · headline line-mask reveal */
.mjs .ml{display:inline-block;overflow:hidden;vertical-align:bottom;
  padding-bottom:.16em;margin-bottom:-.16em;}
.mjs .mli{display:inline-block;transform:translateY(112%) rotate(.5deg);opacity:0;
  transition:transform .6s var(--m2),opacity .6s var(--m2);will-change:transform;}
.mjs .m-in .mli{transform:none;opacity:1;}

/* B2 · kicker tracking-in (initial state set inline by motion.js) */
.mjs .kicker{transition:opacity .8s var(--m1),letter-spacing .9s var(--m2);}

/* B3 · rule draw */
.mjs .mrule{transform:scaleX(0);transform-origin:0 50%;transition:transform .8s var(--m2);}
.mjs .mrule.m-in{transform:none;}

/* B5 · image clip reveal (used where a curtain wipe would be wrong) */
.mjs .mclip{clip-path:inset(0 0 100% 0);transition:clip-path 1.1s var(--m2);}
.mjs .mclip.mclip-in{clip-path:inset(0 0 0 0);}

/* C1 · hero entrance sequence */
.mjs #hero .mhi{opacity:0;transform:translateY(20px);
  transition:opacity 1s var(--m2),transform 1.1s var(--m2);}
.mjs.mjs-ready #hero .mhi{opacity:1;transform:none;}

/* C1b · static-image hero (the city pages). Legible at first paint — this is
   a settle, not an entrance. Never opacity 0: these are landing pages. */
.mjs #hero .mhs{opacity:.95;transform:translateY(7px);
  transition:opacity .45s var(--m1),transform .55s var(--m1);}
.mjs.mjs-ready #hero .mhs{opacity:1;transform:none;}

/* C2 · slow drift (Ken Burns). --py comes from the rAF loop (C3); keyframes
   re-read it each frame so drift and parallax never fight over `transform`. */
.mjs #hero video,.mjs #hero .herobg{animation:mkb 20s ease-in-out infinite alternate;}
@keyframes mkb{
  from{transform:translate3d(0,var(--py,0px),0) scale(1);}
  to{transform:translate3d(0,var(--py,0px),0) scale(1.06);}
}

/* C3 · hero scroll parallax + fade */
.mjs #hero .mhero{transform:translate3d(0,var(--py,0px),0);opacity:var(--po,1);will-change:transform;}

/* C4 · scroll cue */
.mcue{position:absolute;left:50%;bottom:26px;transform:translateX(-50%);z-index:3;
  display:flex;flex-direction:column;align-items:center;gap:10px;pointer-events:none;
  opacity:0;transition:opacity .9s var(--m1) 1.8s;}
.mjs.mjs-ready .mcue{opacity:1;}
.mcue.mcue-off{opacity:0;transition:opacity .5s var(--m1);}
.mcue i{width:1px;height:28px;background:linear-gradient(180deg,var(--accent,#9AA0A7),transparent);
  transform-origin:50% 0;animation:mcue 1.8s var(--m1) infinite;}
.mcue b{font-family:var(--sans,sans-serif);font-weight:300;font-size:9px;letter-spacing:.34em;
  text-transform:uppercase;color:var(--silver,#71767C);}
@keyframes mcue{0%{transform:scaleY(.25);opacity:.35;}45%{transform:scaleY(1);opacity:1;}100%{transform:scaleY(.25);opacity:.35;}}
@media (max-height:640px){.mcue{display:none;}}

/* D1 · section media parallax (extra scale so ±translate never shows an edge) */
.mjs .svc-feat img{transform:translate3d(0,var(--py,0px),0) scale(1.07);}
.mjs .svc-feat:hover img{transform:translate3d(0,var(--py,0px),0) scale(1.12);}
.mjs .why .wph{transform:translate3d(0,var(--py,0px),0) scale(1.14);}

/* E1 · magnetic buttons */
.mjs .mmag{transform:translate3d(var(--mmx,0px),var(--mmy,0px),0);
  transition:transform .14s linear,background-size .5s var(--m2),color .3s,border-color .3s;}
.mjs .mmag.mrest{transition:transform .45s var(--m1),background-size .5s var(--m2),color .3s,border-color .3s;}
.mjs .btn.mmag:hover{transform:translate3d(var(--mmx,0px),calc(var(--mmy,0px) - 2px),0);}

/* E2 · card tilt + glare */
.mjs .mtilt{position:relative;transform:perspective(900px) translate3d(0,var(--ml,0px),0)
  rotateX(var(--rx,0deg)) rotateY(var(--ry,0deg));transition:transform .55s var(--m1);}
.mjs .mtilt.mtilt-a{transition:transform .12s linear;}
.mjs .ytlite.mtilt:hover{--ml:-4px;}
.mglare{position:absolute;inset:0;z-index:5;pointer-events:none;opacity:0;transition:opacity .45s var(--m1);
  background:radial-gradient(260px circle at var(--gx,50%) var(--gy,50%),rgba(255,255,255,.13),transparent 62%);}
.mjs .mtilt:hover .mglare{opacity:1;}

/* E3 · cursor glow. No blend mode here either — `screen` over a moving
   full-page backdrop forces a re-blend every frame for a difference you can't
   see against #121A25. */
.mcur{position:fixed;top:0;left:0;width:340px;height:340px;margin:-170px 0 0 -170px;z-index:60;
  pointer-events:none;opacity:.06;will-change:transform;
  background:radial-gradient(circle,var(--hi,#C8CDD3) 0%,rgba(200,205,211,.35) 38%,transparent 68%);
  transform:translate3d(var(--cx,-999px),var(--cy,-999px),0);}

/* E4 · marquee pause on hover (CSS fallback; G6 takes over when active) */
.mjs .marquee:hover .track,.mjs .bk:hover .bk-track{animation-play-state:paused;}

/* E5 · lightbox */
.mjs .lb{transition:opacity .4s var(--m1);}
.mjs .lb.open img{animation:mlb .5s var(--m2) both;}
@keyframes mlb{from{opacity:0;transform:scale(.94);clip-path:inset(6% 0 6% 0);}
  to{opacity:1;transform:none;clip-path:inset(0 0 0 0);}}
.mjs .lb.open .lb-cap{animation:mlbc .6s var(--m2) .12s both;}
@keyframes mlbc{from{opacity:0;transform:translateY(14px);}to{opacity:1;transform:none;}}
.mjs .lb button{transition:transform .35s var(--m1),border-color .3s,background .3s;}
.mjs .lb .lb-prev:hover{transform:translateY(-50%) translateX(-4px);}
.mjs .lb .lb-next:hover{transform:translateY(-50%) translateX(4px);}

/* E6 · FAQ */
.mjs .qa{transition:background .45s var(--m1);}
.mjs .qa.open{background:rgba(18,26,37,.04);}
.mjs .darker .qa.open,.mjs section:not(.paper) .qa.open{background:rgba(255,255,255,.03);}

/* E7 · form focus */
.mjs form input,.mjs form select,.mjs form textarea{transition:border-color .35s var(--m1),box-shadow .35s var(--m1),background .35s var(--m1);}
.mjs form input:focus,.mjs form select:focus,.mjs form textarea:focus{outline:none;
  border-color:var(--accent,#9AA0A7);box-shadow:0 0 0 1px rgba(154,160,167,.35);background:rgba(255,255,255,.05);}

/* F4 · 404 letter-spacing drift */
.mjs [data-mdrift]{animation:mdrift 9s ease-in-out 1 forwards;}
@keyframes mdrift{from{letter-spacing:.06em;}to{letter-spacing:.13em;}}


/* ==========================================================================
   G — the all-out layer
   ========================================================================== */

/* G1 · cinematic letterbox open. Two bars close the frame, a hairline draws
   across the seam, then the bars retract like a title card. Once per session. */
.mlbx-wrap{position:fixed;inset:0;z-index:200;pointer-events:none;}
.mlbx-wrap i{position:absolute;left:0;right:0;height:50.2%;background:#090D13;
  transition:transform 1.05s var(--m2);}
.mlbx-wrap i:first-child{top:0;transform:translateY(0);}
.mlbx-wrap i:last-child{bottom:0;transform:translateY(0);}
.mlbx-wrap b{position:absolute;left:50%;top:50%;width:0;height:1px;margin-top:-.5px;
  background:linear-gradient(90deg,transparent,var(--hi,#C8CDD3),transparent);
  transform:translateX(-50%);transition:width .62s var(--m2),opacity .4s var(--m1) .5s;}
.mlbx-wrap.mlbx-draw b{width:min(280px,42vw);}
.mlbx-wrap.mlbx-open b{opacity:0;}
.mlbx-wrap.mlbx-open i:first-child{transform:translateY(-100%);}
.mlbx-wrap.mlbx-open i:last-child{transform:translateY(100%);}

/* G2 · contextual cursor label */
.mcurl{position:fixed;top:0;left:0;width:82px;height:82px;margin:-41px 0 0 -41px;z-index:115;
  pointer-events:none;border-radius:50%;display:grid;place-items:center;
  border:1px solid rgba(200,205,211,.5);background:rgba(9,13,19,.42);
  font-family:var(--sans,sans-serif);font-weight:300;font-size:9px;letter-spacing:.28em;
  text-transform:uppercase;color:#fff;text-indent:.28em;
  opacity:0;transform:translate3d(var(--lx,-999px),var(--ly,-999px),0) scale(.4);
  transition:opacity .3s var(--m1),scale .45s var(--m2);will-change:transform;}
.mcurl.on{opacity:1;scale:1;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);}

/* G3 · spotlight — hovering one card dims its siblings */
@media (pointer:fine) and (min-width:1080px){
  .mjs .gal .tile,.mjs .svc-feat-grid .svc-feat,.mjs .films .ytlite,
  .mjs .reels .ytlite,.mjs .rev-grid .quote,.mjs .ig .igt{
    transition:opacity .5s var(--m1),filter .5s var(--m1),transform .55s var(--m1);}
  .mjs .gal:hover .tile:not(:hover),.mjs .svc-feat-grid:hover .svc-feat:not(:hover),
  .mjs .films:hover .ytlite:not(:hover),.mjs .reels:hover .ytlite:not(:hover),
  .mjs .rev-grid:hover .quote:not(:hover),.mjs .ig:hover .igt:not(:hover){
    opacity:.42;filter:saturate(.55);}
}

/* G4 · curtain wipe — a solid panel slides off the frame */
.mwipe{position:absolute;inset:0;z-index:3;pointer-events:none;background:#090D13;
  transform:translateY(0);transition:transform .7s var(--m2);}
/* Gallery tiles past the eighth have no curtain, so a lazy image that hasn't
   decoded leaves an empty box. A ground tone makes that read as loading rather
   than broken. Curtained hosts are deliberately excluded: they already have an
   opaque panel, and one of them parallaxes its image every frame. */
.mjs .gal .tile{background-color:#1A2432;}
.mjs .paper .gal .tile{background-color:#E2E5E9;}
.paper .mwipe{background:#E7E9EC;}
.mwipe.mwipe-go{transform:translateY(-101%);}

/* G5 · scroll-velocity skew — one var on <html>, read by a handful of blocks */
.mjs .msk{transform:skewY(var(--msk,0deg));transition:transform .18s linear;}

/* G6 · velocity-coupled marquees (JS-driven; kills the CSS keyframes) */
.mjs .mmq{animation:none!important;transform:translate3d(var(--mx,0px),0,0);will-change:transform;}

/* G7 · statement illumination — light sweeps the lead paragraph as it scrolls */
.mjs .mlit{--mlit-dim:rgba(201,203,206,.26);--mlit-brt:#D8DBDE;
  background-image:linear-gradient(90deg,var(--mlit-brt) 0%,var(--mlit-brt) var(--lit,0%),
    var(--mlit-dim) var(--lit,0%),var(--mlit-dim) 100%);
  -webkit-background-clip:text;background-clip:text;
  color:transparent;-webkit-text-fill-color:transparent;}
.mjs .paper .mlit{--mlit-dim:rgba(65,72,81,.28);--mlit-brt:#414851;}
.mjs .mlit *{color:inherit;-webkit-text-fill-color:transparent;}
::selection{background:rgba(200,205,211,.28);color:#fff;-webkit-text-fill-color:#fff;}

/* G8 · mobile menu choreography */
@media(max-width:1080px){
  .mjs nav.links a{opacity:0;transform:translateY(16px);
    transition:opacity .5s var(--m1),transform .6s var(--m2),color .3s;}
  .mjs nav.links.open a{opacity:1;transform:none;}
  .mjs nav.links.open a:nth-child(1){transition-delay:.14s;}
  .mjs nav.links.open a:nth-child(2){transition-delay:.19s;}
  .mjs nav.links.open a:nth-child(3){transition-delay:.24s;}
  .mjs nav.links.open a:nth-child(4){transition-delay:.29s;}
  .mjs nav.links.open a:nth-child(5){transition-delay:.34s;}
  .mjs nav.links.open a:nth-child(6){transition-delay:.39s;}
  .mjs nav.links.open a:nth-child(7){transition-delay:.44s;}
  .mjs nav.links.open a:nth-child(8){transition-delay:.49s;}
  .mjs nav.links{transition:transform .6s var(--m2);}
}
.mjs .burger span{transition:transform .45s var(--m2),opacity .3s var(--m1);}
.mjs body.menu-open .burger span:nth-child(1){transform:translateY(6.5px) rotate(45deg);}
.mjs body.menu-open .burger span:nth-child(2){opacity:0;transform:scaleX(.2);}
.mjs body.menu-open .burger span:nth-child(3){transform:translateY(-6.5px) rotate(-45deg);}

/* G9 · buttons fill from the bottom instead of snapping */
.mjs .btn{--bfill:var(--accent,#9AA0A7);
  background-image:linear-gradient(var(--bfill),var(--bfill));
  background-repeat:no-repeat;background-position:50% 100%;background-size:100% 0%;}
.mjs .btn:hover,.mjs .btn:focus-visible{background-color:transparent;color:var(--navy,#121A25);
  background-image:linear-gradient(var(--bfill),var(--bfill));background-size:100% 100%;}
.mjs .btn.solid{--bfill:var(--hi,#C8CDD3);background-color:var(--paper,#F5F6F7);}
.mjs .btn.solid:hover,.mjs .btn.solid:focus-visible{background-color:var(--paper,#F5F6F7);}
.mjs .btn:not(.mmag){transition:background-size .5s var(--m2),color .35s var(--m1),border-color .35s var(--m1),transform .35s var(--m1);}

/* G10 · smart header — retracts on the way down, returns on the way up */
.mjs header{transition:transform .55s var(--m2),background .4s,padding .4s,border-color .4s;}
.mjs header.mhide{transform:translateY(-102%);}
.mjs body.menu-open header{transform:none;}

/* G16 · section rail */
.mrail{display:none;}
@media (pointer:fine) and (min-width:1240px){
  .mjs .mrail{display:flex;position:fixed;right:24px;top:50%;transform:translateY(-50%);
    z-index:70;flex-direction:column;gap:12px;align-items:center;
    opacity:0;transition:opacity .6s var(--m1);}
  .mjs.mjs-ready .mrail{opacity:1;}
  .mrail.mrail-hide{opacity:0!important;pointer-events:none;}
}
.mrail a{width:22px;height:22px;display:grid;place-items:center;position:relative;}
.mrail a::before{content:"";width:5px;height:5px;border-radius:50%;
  background:rgba(160,163,168,.34);transition:background .4s var(--m1),transform .45s var(--m2);}
.mrail a:hover::before{background:rgba(200,205,211,.8);}
.mrail a.on::before{background:var(--hi,#C8CDD3);transform:scale(1.9);}
.mrail.on-paper a::before{background:rgba(65,72,81,.3);}
.mrail.on-paper a:hover::before{background:rgba(30,36,44,.75);}
.mrail.on-paper a.on::before{background:#2C333C;}
.mrail a b{position:absolute;right:24px;white-space:nowrap;font-family:var(--sans,sans-serif);
  font-weight:300;font-size:8.5px;letter-spacing:.26em;text-transform:uppercase;
  color:var(--platinum,#A0A3A8);opacity:0;transform:translateX(6px);
  transition:opacity .35s var(--m1),transform .35s var(--m1);}
.mrail.on-paper a b{color:#566069;}
.mrail a:hover b{opacity:1;transform:none;}

/* G18 · process steps draw their divider */
.mjs .steps .step{position:relative;border-top-color:transparent;}
.mjs .steps .step::before{content:"";position:absolute;top:-1px;left:0;right:0;height:1px;
  background:var(--line-d,rgba(27,26,23,.12));transform:scaleX(0);transform-origin:0 50%;
  transition:transform .9s var(--m2);}
.mjs .steps .step.in::before{transform:none;}

/* G14 · page-exit fade. Selector is weighted above the A1 `.mlbx` rule, which
   otherwise pins transition:none for the rest of a letterbox page load and
   would snap the page to blank instead of fading it out. */
.mjs body{transition:opacity .7s var(--m1);}
html.mjs body.mout{opacity:0;transition:opacity .32s var(--m1);}

/* Reduced motion: everything static, decorations off */
@media (prefers-reduced-motion:reduce){
  html.mjs body{opacity:1!important;}
  .mgrain,.mcur,.mcurl,.mcue,.mprog,.mrail,.mlbx-wrap,.mwipe{display:none!important;}
  .reveal{opacity:1!important;transform:none!important;}
  .mjs .ml,.mjs .mli,.mjs .mrule,.mjs #hero .mhi,.mjs #hero .mhs,.mjs .mclip,.mjs .msk{
    opacity:1!important;transform:none!important;clip-path:none!important;letter-spacing:inherit;}
  .mjs .mlit{background-image:none!important;color:inherit!important;-webkit-text-fill-color:currentColor!important;}
  .mjs .mlit *{-webkit-text-fill-color:currentColor!important;}
  .mjs .steps .step{border-top-color:var(--line-d,rgba(27,26,23,.12));}
  .mjs .steps .step::before{transform:none!important;}
  .mjs #hero video,.mjs #hero .herobg,.mjs .svc-feat img,.mjs .why .wph,.mjs .mtilt,.mjs .mmag{
    animation:none!important;transform:none!important;}
  .mjs .svc-feat img{transform:scale(1.02)!important;}
  .mjs .why .wph{transform:scale(1.05)!important;}
  .mjs nav.links a{opacity:1!important;transform:none!important;}
  .mjs header.mhide{transform:none!important;}
}
