/* ==========================================================================
   LUXE — the shared kit
   Four very different demos, one studio. Everything that must not drift
   between them lives here: the palette, the motion system, the type floor and
   the accessibility floor. A demo file may restyle; it may not re-time.
   ========================================================================== */

:root{
  /* --- palette, taken from Lindsay's own printed collateral --------------- */
  --cream:      #F5F0EC;
  --paper:      #EDE5DC;
  --linen:      #E3D9CD;
  --olive:      #2B3419;
  --olive-deep: #1B2110;
  --ink:        #14170D;
  --ink-soft:   #4F5443;   /* 6.1:1 on cream */
  --gold:       #B08E5D;
  --gold-lift:  #C6A374;
  --gold-deep:  #7C5D30;   /* the only gold allowed on small text */
  --rule:       rgba(43,52,25,.16);
  --rule-2:     rgba(43,52,25,.08);

  /* --- motion: three curves, five durations, one stagger ------------------
     Stricter than any of the luxury sites we measured. This is what makes
     four different designs read as one hand. */
  --ease:      cubic-bezier(0.16, 1, 0.3, 1);     /* everything ENTERING     */
  --ease-exit: cubic-bezier(0.5, 0, 0.8, 0.25);   /* only things LEAVING     */
  --ease-swap: cubic-bezier(0.84, 0, 0.16, 1);    /* full-screen changes     */

  --t-ui:      200ms;      /* hover, focus, colour, panel toggle             */
  --t-elem:    400ms;      /* a card, a line of type, a small image          */
  --t-block:   800ms;      /* a section, an image frame, a chapter           */
  --t-inner:  1600ms;      /* the photograph inside the frame — exactly 2x   */
  --t-curtain: 900ms;      /* gate lift, door swing, seam split              */

  --stagger:    80ms;      /* per item, capped at six                        */
  --split:      42ms;      /* per line, capped at 620ms total                */
  --ambient: 16000ms;      /* one slow loop per demo, so it is never dead    */

  --rise:       32px;      /* vertical reveal travel. Never more.            */
  --slide:      48px;      /* lateral reveal travel. Never more.             */
  --settle:     1.06;      /* image start scale, settling to 1               */

  /* --- rhythm ------------------------------------------------------------ */
  --shell: 1440px;
  --gutter: clamp(20px, 4vw, 48px);
  --sec-y: clamp(72px, 8vw, 128px);

  /* --- accessibility floor. Do not lower. --------------------------------- */
  --body-size: 18px;
  --tap: 48px;
  --tap-lg: 56px;
}

@media (max-width:900px){ :root{ --body-size:17px } }

/* One switch turns the whole system off. Every demo stays complete without
   motion — nothing is only legible once it has animated. */
@media (prefers-reduced-motion: reduce){
  :root{
    --t-ui:1ms; --t-elem:1ms; --t-block:1ms; --t-inner:1ms; --t-curtain:1ms;
    --stagger:0ms; --split:0ms; --rise:0px; --slide:0px; --settle:1;
  }
  *,*::before,*::after{
    animation-duration:1ms !important; animation-iteration-count:1 !important;
    transition-duration:1ms !important; scroll-behavior:auto !important;
  }
}

/* ==========================================================================
   reset
   ========================================================================== */
*,*::before,*::after{box-sizing:border-box}
html,body,h1,h2,h3,h4,p,figure,blockquote,dl,dd,ul,ol{margin:0}
ul[class],ol[class]{list-style:none;padding:0}
html{-webkit-text-size-adjust:100%}
body{
  font-size:var(--body-size); line-height:1.65; color:var(--ink-soft);
  background:var(--cream); overflow-x:hidden;
  -webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale;
}
img,svg,video,canvas{display:block;max-width:100%}
img{height:auto}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
h1,h2,h3,h4{font-weight:400;color:var(--ink);line-height:1.06;
  font-variant-numeric:lining-nums tabular-nums;font-feature-settings:"lnum" 1}

:focus{outline:none}
:focus-visible{outline:3px solid var(--gold-deep);outline-offset:3px;border-radius:2px}
.on-dark :focus-visible{outline-color:var(--gold-lift)}

/* Focus is moved into the page when the gate lifts, so a screen reader lands on
   the heading rather than on the element that has just been removed. Script
   gives those targets tabindex="-1", which makes them focusable but never
   tabbable — so the only way a ring can land on one is that programmatic move,
   where it reads as a stray box drawn round the hero. Suppressing it costs
   keyboard users nothing: Tab cannot reach these elements, and everything that
   IS tabbable keeps its ring. */
[tabindex="-1"]:focus,
[tabindex="-1"]:focus-visible{outline:none}

.sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* The skip link is the first focusable thing in the document, so a keyboard
   user is never trapped behind a gate. */
.skip{position:fixed;top:10px;left:10px;z-index:2000;padding:14px 20px;
  background:var(--ink);color:var(--cream);border-radius:2px;
  transform:translateY(-160%);transition:transform var(--t-ui) var(--ease)}
.skip:focus{transform:none}

/* ==========================================================================
   THE GATE
   Demos 2, 3 and 4 open on a full-screen entry. The rules are the same for
   all three:
     - the real page is in the DOM from the first byte, for crawlers
     - a head script decides BEFORE first paint, so there is no flash
     - it shows once per session, and never to a bot or an automated browser
     - ?intro=0 skips it, ?intro=1 forces it back for the client to re-watch
     - a 4s watchdog lifts it whatever happens, so it can never trap anyone
   ========================================================================== */
.gate{position:fixed;inset:0;z-index:1000;overflow:hidden;
  background:var(--cream);will-change:transform}
.gate[hidden]{display:none}
html.gate-on,html.gate-on body{overflow:hidden;height:100%}
.gate-skip{position:absolute;right:clamp(16px,3vw,40px);bottom:clamp(16px,3vw,40px);
  z-index:12;min-height:var(--tap);padding:0 22px;display:inline-flex;align-items:center;
  font-size:14px;letter-spacing:.16em;text-transform:uppercase;color:var(--ink-soft);
  border-bottom:1px solid var(--rule);transition:color var(--t-ui) var(--ease)}
.gate-skip:hover{color:var(--ink)}
.gate.is-going{pointer-events:none}

/* the page underneath */
/* will-change:transform on #page promoted it for the curtain, but it also
   makes #page the containing block for every position:fixed descendant, so
   demos 3 and 4 lost their fixed header on scroll. It is only needed while
   the intro is actually up. */
.gate-on #page{will-change:transform;visibility:hidden}

/* ==========================================================================
   REVEALS
   Every reveal is transform + opacity only, and the fade is linear — an eased
   fade is a fade with a wobble in it.
   ========================================================================== */
.rv{opacity:0;transform:translate3d(0,var(--rise),0);
  transition:opacity var(--t-block) linear, transform var(--t-block) var(--ease)}
.rv.in{opacity:1;transform:none}
.rv-x{opacity:0;transform:translate3d(var(--slide),0,0);
  transition:opacity var(--t-block) linear, transform var(--t-block) var(--ease)}
.rv-x.in{opacity:1;transform:none}
[data-d="1"]{transition-delay:80ms}
[data-d="2"]{transition-delay:160ms}
[data-d="3"]{transition-delay:240ms}
[data-d="4"]{transition-delay:320ms}
[data-d="5"]{transition-delay:400ms}
[data-d="6"]{transition-delay:480ms}

/* type that rises out of a mask, one mask per rendered line */
/* Each line rises out of its own mask, and the mask has to be deep enough to
   clear the descenders or every g, y and p gets its tail cut off. The negative
   margin takes the extra depth back out so the leading is unchanged. */
.split{display:block}
.split .ln{display:block;overflow:hidden;padding-bottom:.24em;margin-bottom:-.24em}
.split .ln > i{display:block;font-style:inherit;
  transform:translate3d(0,110%,0);transition:transform 560ms var(--ease)}
.in .split .ln > i,.split.in .ln > i{transform:none}
@media (prefers-reduced-motion: reduce){
  .split .ln > i{transform:none;transition:none}
}

/* an image settling inside its frame — the frame runs 800ms, the picture
   inside runs 1600ms, and that offset is the depth */
.frame{overflow:hidden;position:relative}
.frame img{transform:scale(var(--settle));transition:transform var(--t-inner) var(--ease)}
.frame.in img{transform:none}

/* a rule that draws itself from the centre */
.draw{transform:scaleX(0);transform-origin:center;
  transition:transform 640ms var(--ease)}
.in .draw,.draw.in{transform:none}

/* ==========================================================================
   shared components
   ========================================================================== */
.shell{width:100%;max-width:var(--shell);margin-inline:auto;padding-inline:var(--gutter)}
.measure{max-width:62ch}

.btn{display:inline-flex;align-items:center;justify-content:center;gap:12px;
  min-height:var(--tap-lg);padding:0 32px;border:1px solid transparent;border-radius:2px;
  font-size:14px;font-weight:500;letter-spacing:.16em;text-transform:uppercase;
  transition:background var(--t-ui) var(--ease),color var(--t-ui) var(--ease),
             border-color var(--t-ui) var(--ease)}
.btn--ink{background:var(--ink);color:var(--cream)}
.btn--ink:hover{background:var(--gold-deep)}
.btn--gold{background:var(--gold);color:#1A1208}
.btn--gold:hover{background:var(--ink);color:var(--cream)}
.btn--ghost{border-color:currentColor}
.btn--ghost:hover{background:var(--ink);color:var(--cream);border-color:var(--ink)}
.on-dark .btn--ghost:hover{background:var(--cream);color:var(--ink);border-color:var(--cream)}

.eyebrow{font-size:15px;letter-spacing:.20em;text-transform:uppercase;color:var(--gold-deep)}
.on-dark .eyebrow{color:var(--gold-lift)}

/* the disclaimer that has to sit under every concern map */
.model-note{font-size:15px;letter-spacing:.10em;text-transform:uppercase;
  color:var(--ink-soft);opacity:.75}
.on-dark .model-note{color:rgba(245,240,236,.7)}

/* the floating demo switcher — standard on every TRS demo build */
.dswitch{position:fixed;left:50%;bottom:16px;transform:translateX(-50%);z-index:900;
  transition:opacity 500ms var(--ease) 300ms}
/* the switcher is our own furniture: it stays out of the intro */
html.gate-on .dswitch{opacity:0;pointer-events:none;transition-delay:0ms}
.dswitch-in{display:flex;align-items:center;gap:2px;padding:6px;border-radius:999px;
  background:rgba(20,23,13,.74);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);
  border:1px solid rgba(255,255,255,.14);box-shadow:0 18px 44px rgba(0,0,0,.34)}
.dswitch a{min-height:38px;display:inline-flex;align-items:center;padding:0 15px;
  border-radius:999px;font-size:12.5px;letter-spacing:.12em;text-transform:uppercase;
  color:rgba(255,255,255,.72);transition:background var(--t-ui),color var(--t-ui)}
.dswitch a:hover{color:#fff}
.dswitch a.on{background:var(--gold);color:#1A1208}
.dswitch .sep{width:1px;height:18px;background:rgba(255,255,255,.2);margin:0 4px}
@media (max-width:560px){
  .dswitch a{padding:0 9px;font-size:11px;letter-spacing:.04em;white-space:nowrap}
  .dswitch a .lbl{display:none}
  .dswitch-in{padding:5px;gap:0}
}


/* ==== v3 ambient kit ==================================================== */
/* parallax wrapper — the JS moves the inner image a few px against scroll */
.plx{display:block;overflow:hidden;height:100%}
.plx>img{will-change:transform;transform:scale(1.08)}

/* the sheen that crosses a button once on hover */
.btn{position:relative;overflow:hidden}
.btn::after{content:"";position:absolute;top:-6px;bottom:-6px;left:-46%;width:34%;
  background:linear-gradient(100deg, transparent, rgba(255,255,255,.35), transparent);
  transform:skewX(-16deg);pointer-events:none;transition:transform .9s var(--ease)}
.btn:hover::after{transform:skewX(-16deg) translateX(430%)}
@media (prefers-reduced-motion: reduce){
  .btn::after{display:none}
  .plx>img{transform:none}
}


/* ==== v3 effects ======================================================== */
/* An image that is uncovered rather than faded in: a panel in the section's
   own ground slides off it. Pairs with .rv on the same element. */
.mask{position:relative;overflow:hidden!important}
.mask::after{
  content:"";position:absolute;inset:0;z-index:2;pointer-events:none;
  background:var(--ground,var(--cream));
  transform:none;transition:transform 1100ms var(--ease-swap) 120ms;
}
.mask.in::after{transform:translate3d(101%,0,0)}

/* the light that follows the cursor across a hero photograph */
.hero-bg::before{
  content:"";position:absolute;inset:0;z-index:1;pointer-events:none;opacity:0;
  background:radial-gradient(26% 32% at var(--mx,50%) var(--my,42%),
    rgba(255,241,215,.34), transparent 72%);
  mix-blend-mode:screen;transition:opacity 700ms var(--ease);
}
.hero:hover .hero-bg::before{opacity:1}

/* buttons are nudged toward the cursor by initTouches, so they need a
   transform transition to come back smoothly */
.btn{transition:background var(--t-ui) var(--ease), color var(--t-ui) var(--ease),
     border-color var(--t-ui) var(--ease), transform 300ms var(--ease)}

@media (prefers-reduced-motion: reduce){
  .mask::after{display:none}
  .hero-bg::before{display:none}
}


/* ==== v4: light across the photographs ================================== */
/* A soft sheen crosses each image once, just after its reveal panel clears. */
.mask::before,.band-media::before{
  content:"";position:absolute;inset:0;z-index:3;pointer-events:none;opacity:0;
  background:linear-gradient(104deg, transparent 34%, rgba(255,252,244,.32) 50%, transparent 66%);
  mix-blend-mode:screen;
}
.mask.in::before{animation:sheenPass 1500ms var(--ease) 900ms 1}
.band.in .band-media::before{animation:sheenPass 1700ms var(--ease) 500ms 1}
@keyframes sheenPass{
  0%{opacity:0;transform:translate3d(-55%,0,0)}
  28%{opacity:1}
  100%{opacity:0;transform:translate3d(55%,0,0)}
}

/* headings that rise line by line need the mask to breathe for descenders */
.split{padding-bottom:.08em}

@media (prefers-reduced-motion: reduce){
  .mask::before,.band-media::before{display:none}
}


/* ==== v5: surface ======================================================
   Flat CSS colour is the tell that a page is cheap. A whisper of grain over
   everything makes the creams read as paper rather than as a hex value. It
   sits above the page, never intercepts a click, and is a transparent tile of
   light and dark specks rather than a grey one under soft-light, which on a
   near-white ground is close to a no-op by definition. */
body::after{
  content:"";position:fixed;inset:0;z-index:60;pointer-events:none;
  background-image:url(/assets/images/grain.png);
  background-size:220px 220px;
  opacity:.55;
}


/* ==== v5: typesetting, depth and foil ================================== */

/* Headings are balanced rather than left to the browser, so no heading ends
   on a single orphaned word. This is the difference between type that is set
   and type that is merely styled. */
h1,h2,h3,.lf-q,.band-line,.tq-quote{text-wrap:balance}
p,.lede,.measure{text-wrap:pretty}

/* Proper typography from the fonts we are already paying to load. */
body{font-kerning:normal;font-variant-ligatures:common-ligatures contextual}
/* Prices and durations line up in a column when the figures are tabular. */
.tp-meta,.ti-meta,.tc-meta,.tl-price,.ct-rows dd,.ct-hours dd,.cs-fact dd{
  font-variant-numeric:tabular-nums lining-nums}

/* Photographs sit on the page like plates, with one deep soft shadow rather
   than the thin default. */
.prac-media,.wt-media,.hero-still,.fm-stage{
  box-shadow:0 30px 70px -28px rgba(42,33,24,.30)}
.lf-card,.tc-card,.cs-side,.ct-side{
  box-shadow:0 18px 44px -22px rgba(42,33,24,.22)}

/* Gold behaves like foil: it catches light across its length instead of
   being one flat value. */
:root{
  --foil:linear-gradient(90deg,
    rgba(176,142,93,.28) 0%, #C6A87C 16%, #8C6C3E 48%, #C6A87C 82%, rgba(176,142,93,.28) 100%);
}
.hero-rule,.wd-rule,.pl-rule{background-image:var(--foil)}


/* ==== v5: the small furniture ==========================================
   The details that separate an expensive page from a competent one. */

/* Navigation underlines draw from the left rather than switching on. */
.navlink{
  background-image:linear-gradient(currentColor,currentColor);
  background-repeat:no-repeat;background-position:0 100%;background-size:0% 1px;
  border-bottom-color:transparent !important;
  transition:color var(--t-ui) var(--ease), background-size 420ms var(--ease);
}
.navlink:hover,.navlink:focus-visible{background-size:100% 1px}

/* The header condenses as you leave the top, instead of only changing colour. */
.hdr-in{transition:padding-block 420ms var(--ease)}
.hdr.is-stuck .hdr-in{padding-block:10px}
.hdr .brand-mark{transition:height 420ms var(--ease)}
.hdr.is-stuck .brand-mark{height:34px}

/* One focus ring, in the brand's own gold, rather than the browser default. */
:focus-visible{outline:2px solid var(--gold);outline-offset:3px;border-radius:2px}

/* Captions under photographs sit clear of the plate. */
.prac-media figcaption,.wt-media figcaption{padding-top:2px}

@media (prefers-reduced-motion: reduce){
  .navlink{transition:none}
  .hdr-in,.hdr .brand-mark{transition:none}
}


/* The hero type buys its own legibility with a whisper of glow, so the scrim
   no longer has to paint over the photograph to earn it. */
.hero h1,.hero .hero-sub,.hero .eyebrow{
  text-shadow:0 1px 0 rgba(250,246,240,.6), 0 0 30px rgba(250,246,240,.55)}
