/* Vanity Ink — tokens + base.
   v2 "flow" (2026-09-23): no banded sections, metallic (chrome) display type that matches the logo.
   Since 09-29 the page is solid black and each marble section carries its own river image (the old repeating
   canvas lives in prototype-archive/v2-flow-2026-09-29). v1 lives in prototype-archive/v1-variant-a. */
:root{
  --purple:#8858B8; --purple-dark:#6F45A0; --purple-light:#A98AD6; --purple-glow:#4800A0; --purple-pale:#C3A6EA;
  --silver:#C8C8CC;
  --black:#060607; --ink-raised:#131216; --line:#26242B; --line-soft:rgba(255,255,255,.08);
  --text:#E4E4E8; --text-muted:#A9A9B1;
  --marble-deep:#2E1D42; --marble-mid:#402850;
  --display:"Cinzel",Georgia,serif; --sans:"Montserrat",Helvetica,Arial,sans-serif;
  --container:1240px; --gutter:24px; --section-y:84px;   /* 10-13 audit (Tannis: gaps "huge"): 168px between sections, was 240 */ --card-gap:24px;
  --h1:64px; --h2:clamp(30px,3.2vw,46px); --h2-sm:clamp(24px,2.2vw,30px); --h3:24px; --h3-sm:20px; --h4:19px; --body:17px; --body-sm:16px; --small:14px;
  /* type scale (Tannis 10-13 audit): every section H2 is --h2, every H2 inside a card, form or sidebar --h2-sm; body
     text --body, text in cards, rows, steps and answers --body-sm. Elementor: Site Settings > Typography (H2, H2 small
     as a global font, Text, Text small). H3: --h3 for steps and reasons, --h3-sm for cards, notes and answers. */
  --track-eyebrow:.3em; --track-btn:.16em; --track-display:.04em;
  /* chrome: bright top, sharp dark horizon just under the middle, lighter base (sampled from the V+I logo) */
  /* horizon stop kept at #8A8795 or lighter: 3.7:1 even over #402850 marble (research: references/fable-references/flow-build-notes.md 2.5) */
  --chrome:linear-gradient(180deg,#FFFFFF 0%,#F1F1F4 20%,#CFCED6 38%,#9A97A4 48%,#8A8795 52%,#B9B7C1 60%,#ECEBF0 76%,#FBFBFD 88%,#C3C2CA 100%);
  --brushed:repeating-linear-gradient(90deg,rgba(255,255,255,.05) 0 1px,rgba(0,0,0,.04) 1px 3px);
  /* chrome for surfaces (button hover): the headings' metal, with its horizon moved below the words (Carlos, 09-29:
     the band through the middle of the label looked off); a crisp line at 64-67%, then the lighter base */
  --chrome-fill:linear-gradient(180deg,#FFFFFF 0%,#F3F2F6 46%,#DFDEE5 64%,#C2C0CA 67%,#D8D7DE 82%,#F4F4F7 100%);
  --ease:cubic-bezier(.16,1,.3,1);
}
*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
/* The page is solid black (Elementor: Site Settings > Background #060607). The marble lives in .marble sections. */
body{margin:0;color:var(--text-muted);font:400 var(--body)/1.55 var(--sans);   /* 1.7 until 10-15 (Tannis: a little tighter) */ -webkit-font-smoothing:antialiased;position:relative;isolation:isolate;background:var(--black)}

/* ===== Marble sections (09-29; replaces the one repeating canvas) =====
   Each marble section (or a wrapper around two sections the river runs through) carries its own background image,
   never repeated within a page; the pre-footer shares one "finale" image on every page, on purpose. Round 2 (09-29):
   the images are cut from the original canvas river (tools/imagegen/cut_canvas.py), the look Carlos preferred, instead
   of the generated ribbons, which read as "crunchy". The image shows at full width, top-aligned, and fades into black at
   the top and bottom with an eased mask (--mfade, or a section's own --mmask). Stage sections (photos on black) can
   carry a dim underlay (.m-under, on ::after) so the flow still shows faintly beneath them.
   Knobs: --mimg (the image), --mpos, --msize, --mop (opacity), --mt / --mb (fade lengths) or a whole --mmask,
   --mlift (start the river that much higher, inside the hero's fade-out, so it flows out of the hero photo);
   .m-flip / .m-flop mirror the image. A wrapper (an outer div.marble around two sections) lets one river run through
   both; the sections inside stay transparent, photo sections included, so the flow shows faintly beneath them.
   Elementor: container > Background: image, position top center, size 100% auto (the wrapper = an outer container
   around both inner containers), no repeat, on #060607; the fades as a background overlay gradient (same stops). */
/* The river's two levels (10-02, Home's "less dark" pass, now on every page): --m-dim, the river where a flow dims
   beneath photos (Home's Meet, the inner flows' dimmed stops), and --m-under, the opacity of an underlay (.m-under).
   On the inner pages --m-dim is a mask alpha; on Meet it is a black overlay of 1 - --m-dim over an opaque layer. Over
   the black page the two are the same. Elementor: the overlay gradient's stops; the underlay's Background Overlay. */
:root{--m-dim:.72;--m-under:.75}
/* the default eased fade, top and bottom; --mt / --mb set its lengths per section (default 230px; 0px = no fade) */
.marble,.m-under{position:relative;isolation:isolate;
  --mfade:linear-gradient(180deg,transparent 0,rgba(0,0,0,.12) calc(var(--mt,230px)*.26),rgba(0,0,0,.5) calc(var(--mt,230px)*.52),rgba(0,0,0,.88) calc(var(--mt,230px)*.78),#000 var(--mt,230px),#000 calc(100% - var(--mb,230px)),rgba(0,0,0,.88) calc(100% - var(--mb,230px)*.78),rgba(0,0,0,.5) calc(100% - var(--mb,230px)*.52),rgba(0,0,0,.12) calc(100% - var(--mb,230px)*.26),transparent 100%)}
.marble::before,.m-under::after{content:"";position:absolute;top:calc(0px - var(--mlift,0px));right:0;bottom:0;left:0;z-index:-1;pointer-events:none;
  background:var(--mimg) no-repeat var(--mpos,center top)/var(--msize,100% auto);opacity:var(--mop,1);transform:scale(var(--mx,1),var(--my,1));
  -webkit-mask-image:var(--mmask,var(--mfade));mask-image:var(--mmask,var(--mfade))}
.m-flip{--mx:-1}.m-flop{--my:-1}
/* the generated set, re-exported with the canvas grade (only the finale is on the pages; the rest are spares) */
.m-r1{--mimg:url(../assets/marble/r1.webp)}.m-r2{--mimg:url(../assets/marble/r2-b.webp)}.m-r3{--mimg:url(../assets/marble/r3.webp)}
.m-r4{--mimg:url(../assets/marble/r4.webp)}.m-r5{--mimg:url(../assets/marble/r5.webp)}
.m-finale{--mimg:url(../assets/marble/finale-b.webp);--mpos:center}
/* cuts of the original canvas river (round 2): Home's own, then one broad stretch per inner-page section */
.c-home-idea{--mimg:url(../assets/marble/c-home-idea.webp)}
.c-home-flow{--mimg:url(../assets/marble/c-home-flow.webp)}
.c-home-cover{--mimg:url(../assets/marble/c-home-cover.webp)}
.c-about-flow{--mimg:url(../assets/marble/c-about-flow-b.webp)}.c-about-people{--mimg:url(../assets/marble/c-about-people-b.webp)}
.c-artists-flow{--mimg:url(../assets/marble/c-artists-flow-b.webp)}.c-book-flow{--mimg:url(../assets/marble/c-book-flow-b.webp)}.c-book-under{--mimg:url(../assets/marble/c-book-under-b.webp)}
.c-aftercare-flow{--mimg:url(../assets/marble/c-aftercare-flow-b.webp)}.c-aftercare-time{--mimg:url(../assets/marble/c-aftercare-time-b.webp)}
.c-faq-under{--mimg:url(../assets/marble/c-faq-under-b.webp)}.c-contact-flow{--mimg:url(../assets/marble/c-contact-flow-b.webp)}
.c-jeremy{--mimg:url(../assets/marble/c-jeremy-b.webp)}.c-jeremy-work{--mimg:url(../assets/marble/c-jeremy-work-b.webp)}
img,svg{display:block;max-width:100%}
a{color:var(--purple-light);text-decoration:none;transition:color .2s}
a:hover{color:var(--purple-pale)}
:focus-visible{outline:2px solid var(--purple-light);outline-offset:3px}
::selection{background:var(--purple);color:#fff;-webkit-text-fill-color:#fff}

/* ===== Type ===== */
h1,h2,h3,h4{font-family:var(--display);font-weight:400;color:var(--text);margin:0;text-transform:uppercase;letter-spacing:var(--track-display);line-height:1.15;text-wrap:balance}
h1{font-size:var(--h1)}h2{font-size:var(--h2);line-height:1.1}h3{font-size:var(--h3)}h4{font-size:var(--h4)}
/* Metallic display type: the chrome gradient repeats once per line (background-size = 1 line box),
   so every line of a wrapped heading gets the full highlight. Needs vertical padding 0 on the heading. */
h1,h2,.metal{
  background-image:var(--chrome),var(--brushed);
  background-size:100% 1.15em,auto;background-size:100% 1lh,auto;background-repeat:repeat-y,repeat;
  -webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;color:#DADAE0;
  filter:drop-shadow(0 1px 0 rgba(0,0,0,.6)) drop-shadow(0 10px 24px rgba(0,0,0,.55))}
/* Windows High Contrast drops background images, so give the text a real color back */
@media (forced-colors:active){h1,h2,.metal,.hero-tag{background:none!important;-webkit-text-fill-color:currentColor!important;filter:none!important}}
p{margin:0 0 1em}p:last-child{margin-bottom:0}
p a{border-bottom:1px solid var(--purple);padding-bottom:1px}
ul,ol{margin:0;padding:0;list-style:none}
button{font:inherit;color:inherit;background:none;border:0;padding:0;cursor:pointer}

/* pools and bleeding photos may poke past the viewport; clip sideways without breaking sticky */
main{overflow-x:clip}
.container{width:100%;max-width:calc(var(--container) + var(--gutter)*2);margin:0 auto;padding:0 var(--gutter)}
.section{position:relative;padding:var(--section-y) 0}
.section>.container{position:relative;z-index:2}
/* v1 used .band for alternating grey sections. In the flow design nothing is banded. */
.band{background:transparent}
.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
.eyebrow{display:block;font:500 12px/1 var(--sans);letter-spacing:var(--track-eyebrow);text-transform:uppercase;color:var(--purple-light);margin-bottom:20px}
.smallcaps{font-family:var(--display);text-transform:none;font-variant-caps:small-caps}

/* Ink pool: a feathered dark pool behind a text block so it reads over the marble.
   Elementor: container background = radial gradient, black 90% to transparent. */
.pool{position:relative;isolation:isolate}
.pool::before{content:"";position:absolute;inset:-18% -14%;z-index:-1;pointer-events:none;
  background:radial-gradient(closest-side,rgba(6,6,7,.94),rgba(6,6,7,.82) 55%,rgba(6,6,7,0))}

/* divider ornament: retired (v1 option); markup on inner pages stays hidden */
.divider{display:none;align-items:center;gap:14px;width:240px;height:14px;margin:24px 0 32px}
.divider::before,.divider::after{content:"";flex:1;height:1px;background:linear-gradient(90deg,transparent,var(--silver))}
.divider::after{background:linear-gradient(90deg,var(--silver),transparent)}
.divider .orn{width:14px;height:14px;flex:none;background:var(--purple);clip-path:polygon(50% 0,60% 40%,100% 50%,60% 60%,50% 100%,40% 60%,0 50%,40% 40%)}
.divider.center{margin-left:auto;margin-right:auto}
h2{margin-bottom:24px}
/* separator: the line and gem from under the "VI" in the client's logo (assets/brand/separator-2x.png, exported from
   a vector trace of the logo; never cut, recoloured or restyled). It sits under a heading, flush with the heading's
   left edge and as wide as the heading's longest line, so the gem lands under that line's centre; each heading sets
   its own width per breakpoint (About's is in pages.css). Decorative: alt="".
   Elementor: an Image widget (the PNG) under the Heading widget, width per breakpoint, alignment left, margin -6 0 26
   (phone -8 0 22). */
.sep{display:block;width:240px;height:auto;margin:-6px 0 26px}  /* 240 by default, so a page that forgets its own width never gets a column-wide mark */
/* centred under a centred heading, the logo's own layout (Carlos 10-02: "the one that is centered... looks sick") */
.sep.center{margin-left:auto;margin-right:auto}

/* ===== Buttons =====
   Outline at rest; on hover the button turns to polished chrome (the headings' metal) with near-black text.
   The chrome is a layer that fades in, so the change is smooth. Elementor: Button > Hover: background gradient
   (the stops above), text #0B0B0D, border #EDECF1, transition 0.3s. */
.btn{position:relative;isolation:isolate;overflow:hidden;display:inline-flex;align-items:center;justify-content:center;padding:15px 30px;border:1px solid var(--silver);color:var(--text);font:500 13px/1 var(--sans);letter-spacing:var(--track-btn);text-transform:uppercase;transition:background .3s var(--ease),border-color .3s var(--ease),color .3s,transform .15s;white-space:nowrap;background:rgba(6,6,7,.35)}
.btn::before{content:"";position:absolute;inset:0;z-index:-1;background:var(--chrome-fill);opacity:0;transition:opacity .3s var(--ease)}
.btn:hover{border-color:#EDECF1;color:#0B0B0D}
.btn:hover::before{opacity:1}
.btn:active{transform:scale(.98)}
.btn.sm{padding:11px 20px;font-size:11px}

/* ===== Photos =====
   Every photo in full colour (Tannis 10-13: no grey treatment; hover only lifts the photo a little). Never darkened (Tannis 10-13: no dark overlay, no brightness cut on any
   photo in the page body; heroes keep their own veil for the words). Elementor: Image widget CSS Filters, saturation 0. */
.shot{margin:0;position:relative;overflow:hidden;background:var(--ink-raised)}
.shot img{width:100%;height:100%;object-fit:cover;display:block;transition:transform 1.2s var(--ease)}
.shot.is-color img{filter:none}
a.shot:hover img,.shot.hover-lift:hover img{filter:none;transform:scale(1.03)}
/* Photos keep clean edges: a hairline and a soft shadow, never a fade into the marble (fades read as an opacity bug).
   A photo may fade only into solid black (the heroes). Elementor: Image > Border 1px rgba(255,255,255,.08) + Box Shadow. */
.framed{position:relative;box-shadow:0 30px 80px rgba(0,0,0,.6)}
.framed::before{content:"";position:absolute;inset:0;z-index:2;pointer-events:none;border:1px solid rgba(255,255,255,.1)}

/* ===== Stages =====
   A stage is a section that carries photos. Since the page is black (09-29) it paints nothing itself: the old black
   gradient only hid the river of a surrounding wrapper, and Carlos wants to "somewhat see underneath the flow" there.
   The class stays as a marker (and for the section joins in pages.css). Elementor: a plain container. */
.stage{position:relative;isolation:isolate;--feather:140px}

/* ===== Honeycomb accent (Carlos, 09-29) =====
   The studio's own wall stencils (violet hexagon clusters that thin out at the edge; tools/brand/honeycomb.py), used as
   a quiet accent on black "wall" areas only: never over the marble (that's the floor) or on photos, faint, one per
   area. Not a logo mark. Elementor: an Image widget (the SVG), Position Absolute, opacity, z-index under the text. */
.hex-accent{position:absolute;z-index:0;pointer-events:none;user-select:none;opacity:var(--hex-op,.3);width:var(--hex-w,440px);height:auto}
/* labeled placeholder (for slots that still have no photo) */
.ph{position:relative;background:rgba(19,18,22,.7);border:1px solid var(--line);display:flex;align-items:flex-end;padding:14px;overflow:hidden;
  background-image:repeating-linear-gradient(135deg,transparent 0 14px,rgba(255,255,255,.018) 14px 15px)}
.ph::before{content:"";position:absolute;inset:0;background:radial-gradient(ellipse at 50% 60%,rgba(136,88,184,.10),transparent 65%)}
.ph .lbl{position:relative;font:400 11px/1.4 ui-monospace,Menlo,monospace;color:var(--text-muted);opacity:.8}
.r-45{aspect-ratio:4/5}.r-32{aspect-ratio:3/2}.r-11{aspect-ratio:1/1}.r-169{aspect-ratio:16/9}

/* textures kept from v1 (hex cluster; unused); marble lives in .marble sections */
.tex{position:absolute;pointer-events:none;z-index:1}
.tex-hex{color:var(--purple);opacity:.2}
.tex-marble{display:none}

/* ===== Reveal on scroll (IntersectionObserver adds .in). Elementor: Entrance Animation "Fade In Up". ===== */
@media (prefers-reduced-motion:no-preference){
  .js .reveal{opacity:0;transform:translateY(28px);transition:opacity .9s var(--ease),transform 1.1s var(--ease)}
  .js .reveal.in{opacity:1;transform:none}
  .js .reveal.d1{transition-delay:.08s}.js .reveal.d2{transition-delay:.16s}.js .reveal.d3{transition-delay:.24s}
}

@media (max-width:1023px){
  :root{--section-y:68px;--h1:48px;--h3:22px}
}
@media (max-width:767px){
  :root{--section-y:52px;--h1:36px;--h3:20px;--h4:17px;--body:16px;--body-sm:15px}
  /* phones: the river at 180% of the width (its middle 56%), with the generic fades. 10-02 (Carlos: mobile first): it
     was 250%, the middle 40%, 1.5 device px per pixel of the cut on a 3x phone (as on a 1512 laptop at 2x); 180% is
     about 1.1, so the marble stays crisp; each cut covers 28% less height (the baked end fades close it). --msize-m is
     set here so Home's Meet layer follows; the studio sets its own size. Elementor: the Mobile background size, 180%. */
  :root{--msize-m:180% auto}
  .marble::before,.m-under::after{background-size:var(--msize-m,180% auto);-webkit-mask-image:var(--mmask-m,var(--mfade));mask-image:var(--mmask-m,var(--mfade))}
  .tex-hex.lg{display:none}
  .sep{margin:-8px 0 22px}
}
/* small phones (readiness sweep 09-30): a button never pushes past the screen. Buttons stay on one line where they fit
   (no change for those); a label too long for the screen wraps instead of being cut off. Home's "Book a Cover-Up
   Consultation" (.long) was 357px on one line, wider than a 375 phone, so its column was cut at the right edge:
   slightly tighter tracking and padding keep it on one line down to 375, and it wraps below that. */
@media (max-width:480px){
  .btn{max-width:100%;white-space:normal}
  .btn.long{padding:12.7px 20px;letter-spacing:.1em;line-height:1.35;text-align:center}
}

/* ===== Button hover chrome, smoothed (10-06, Carlos: the hover "turns white... not as metallic"; fine on the headings) =====
   The 09-29 fill kept the horizon below the label but stayed near-white above it and dropped to a light grey in ~1px,
   so it read as white with a scratch. Now the headings' metal: white top, the label on gently graded silver, a soft
   horizon (#8A8795, the headings' own stop) just under the baseline, a bright reflection, a grey base; brushed like the
   headings. Text #060607 (about 10:1 behind the label, 5.8:1 even on the horizon stop). Chrome layer, label and border share one 0.28s ease.
   Elementor: Button > Hover > Background Type Gradient, 180deg, the stops below; Text #060607; Border #EDECF1; Transition 0.3s. */
/* 10-06 later (Carlos: the line under the words still looked hard; "soften that line so it's more spread out and looks
   more shiny metallic"): the horizon is now a broad, shallow band (its darkest stop #AFACB9 at 66%, eased in from 38% and
   out by 88%) instead of a #8A8795 line in a 15% span; label contrast about 9:1 at the darkest point. */
:root{--chrome-fill:linear-gradient(180deg,#FFFFFF 0%,#F6F6F9 10%,#EAE9EE 24%,#DAD9E0 38%,#C9C7D0 50%,#B8B5C1 60%,#AFACB9 66%,#B7B4C1 72%,#CBC9D2 80%,#E5E4EA 88%,#F2F1F5 93%,#D8D7DE 100%)}
.btn{transition:background-color .28s ease,border-color .28s ease,color .28s ease,transform .15s}
.btn::before{background:var(--chrome-fill),var(--brushed);transition:opacity .28s ease}
.btn:hover{color:#060607}
/* the booking bar's purple button wore the chrome with white words (white on white); dark label like every button */
.book-bar .btn:hover{color:#060607;border-color:#EDECF1}

/* ===== 10-06 "too much black" (Contact, FAQ, Artists): three new canvas cuts, the brighter step with a 300-row fade baked into
   the bottom (tools/imagegen/cut_canvas.py CUTS + cut_brighter.py `inner`). Contact's details: strip 3200-5440
   (the end of A and C; not B, the stretch c-contact-flow showed; c-contact-flow is retired from the page).
   FAQ's body: strip 2560-6400 (A, C, D: the longest stretch from there with no repeat inside it), replacing
   c-faq-under (A, B), which ran out at 1280 and below. Artists (Our Team + Guest): c-artists-flow's own stretch
   (strip 5120 on) run longer, to 8960 (D, A, B): the same opening view, but it no longer runs out at 1440 and below
   now that the cards are taller. Rules in pages.css ("10-06 black"). */
.c-contact-details{--mimg:url(../assets/marble/c-contact-details-b.webp)}
.c-faq-body{--mimg:url(../assets/marble/c-faq-body-b.webp)}
.c-artists-team{--mimg:url(../assets/marble/c-artists-team-b.webp)}

/* ===== 10-06 legible (Carlos: "where the background is slightly brighter... the white text" gets lost) =====
   One pool, everywhere text meets the river: the house pool now holds its depth further out (.86 to --pool-hold, was
   .82 to 55%), so a heading on a block's top edge or a link in a strip's far cell sits on the pool, not on its feather.
   Measured: every text block over marble at 1512, 1280 and 390, the 99th percentile of the ground behind it (text
   hidden) against 4.5:1 body / 3:1 chrome. --pool is the one recipe; .pool, the closing strip and the blocks named in
   pages.css ("10-06 legible") all paint it. Contact's lighter pool (pages.css, "10-06 black") stays its own.
   --pool-hold is read once, here on :root (a section setting it changes nothing; a local pool restates the gradient).
   Elementor: the text container's Background > Gradient, Radial, closest-side: #060607 at 94% opacity 0%, 86% at the
   hold stop, 0% at 100%. */
:root{--pool-hold:70%;--pool:radial-gradient(closest-side,rgba(6,6,7,.94),rgba(6,6,7,.86) var(--pool-hold),rgba(6,6,7,0))}
.pool::before{background:var(--pool)}
