/* Sana Shah Studio — sana-website2.
 * ============================================================================================
 * Design copied from ponarsher.com with Murtaza's approval (Vader, 2026-09-09), recoloured to
 * HER beige. The brief, verbatim: "the main thing is the parallax effects and scrolling
 * animation and page transitions. without these, the website is useless."
 *
 * *** MOTION LIVES IN TWO PLACES AND ONLY TWO. ***
 *   - This file owns the STATES (what "hidden" and "shown" look like) and all timing.
 *   - assets/js/site.js owns the TRIGGERS (when to flip between them).
 * Nothing else animates. If a new effect is added, it is added here, with its numbers written
 * down, because "why is this 700ms" is the question nobody can answer six months later.
 *
 * TIMING IS LIFTED FROM TILDA, WHICH IS WHAT PONARSHER RUNS. Read out of
 * tilda-animation-2.0.min.css so the feel matches the site Murtaza actually approved:
 *     easing        cubic-bezier(.19,1,.22,1)   (easeOutExpo - fast out, long glide to rest)
 *     headings      1.2s      body/text  0.7s      small UI  0.5s
 *     fade-up       translateY(100px)
 *     fade-in-blur  blur(12px)
 *     zoom-in       scale(.9)
 * Where this build differs from Tilda it is deliberate and noted at the rule.
 *
 * MOBILE FIRST. Base rules ARE the phone; every media query is min-width and only ADDS.
 * House breakpoints: 480 / 768 / 1024 / 1280.
 */

/* *** MANGO NEW - SELF-HOSTED, FILES SUPPLIED BY VADER (2026-09-11). ***
   Vader: "replace all with mango font on sana3". He downloaded these himself from Mango's own CSS;
   they live in assets/fonts/. Mango's brand typefaces - see the licence note in inc/header.php.
   Mango ships 400, 500 and 600 only. The CSS here also asks for 700 (bold) in three places, so the
   SemiBold face DECLARES THE RANGE 600-700: bold text uses the real SemiBold instead of the browser
   smearing a fake bold out of the 600, which looks cheap. The -en files are English-only subsets. */
@font-face {
    font-family: 'Mango New';
    src: url('../fonts/MangoNew-Regular-en.woff2') format('woff2');
    font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
    font-family: 'Mango New';
    src: url('../fonts/MangoNewMedium-s.p.0443lq596jigi.woff2') format('woff2');
    font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
    font-family: 'Mango New';
    src: url('../fonts/MangoNew-SemiBold-en.woff2') format('woff2');
    font-weight: 600 700; font-style: normal; font-display: swap;
}
/* Mango's display face - defined, not yet applied anywhere. Fetched only if something uses it. */
@font-face {
    font-family: 'Mango Heritage Headline';
    src: url('../fonts/MangoHeritageHeadline-Regular-en.woff2') format('woff2');
    font-weight: 400; font-style: normal; font-display: swap;
}


/* ── PALETTE ─────────────────────────────────────────────────────────────────────────────────
   Sampled from sanashahstudio.com with getComputedStyle on 2026-09-09, not invented, so this
   site and her live Wix site are the same beige rather than two beiges that nearly match. */
:root {
    /* *** ONE GROUND, EVERYWHERE. NO BANDING. ***
       Vader asked twice. The first time I moved the ground one step and KEPT a deeper band under
       the alternating sections - so the colour he had pointed at was still only on some of the
       page. He then marked the two bands directly: cross on the lighter one, tick on the deeper.
       "i said the bottom color i need across all website."

       So --bg-2 is now the SAME value as --bg. It still exists as a token because .section-alt
       and .band reference it; collapsing them to one value is what removes the banding, and it
       means any future decision to reintroduce a band is one line rather than a hunt.

       *** --bg-rgb MUST MOVE WITH --bg. *** The hero and header gradients need the ground as raw
       channels for rgba(). It was a literal once and a colour change left cream banding across
       the bottom of every hero; it is a token now precisely so the two cannot drift apart. */
    --bg:        #e7e0d9;   /* the ground Vader ticked */
    --bg-rgb:    231, 224, 217;
    --bg-2:      #e7e0d9;   /* deliberately identical - no alternating band anywhere */
    --bg-3:      #ddd4cb;   /* wells, inputs, and the WRITE A MESSAGE band - the ONLY step below the ground */

    /* *** TEXT IS BLACK OR WHITE. NOTHING ELSE. ***
       Vader, 2026-09-09: "only use text white and black, no colored text anywhere" and "even the
       button borders white and black". Every one of these was a warm brown sampled from her
       canvases - #2c2621 ink, #54433a, #6b5a4e muted, #c8763f burnt sienna for eyebrows and link
       hovers. Warm greys read as a colour scheme, and against the paintings that is exactly the
       "so colorful" note the client kept giving.
       Now: pure black, neutral greys derived from it, and white. The BEIGE GROUND is the only
       colour on the site; everything drawn on top of it is achromatic, so the paintings are the
       only thing with hue on the page. --accent is kept as a token but resolves to black, so any
       rule still pointing at it cannot reintroduce a colour. */
    /* *** EVERY TEXT COLOUR IS PURE BLACK OR PURE WHITE. NOTHING IN BETWEEN. ***
       Vader, 2026-09-10: "no text dimming. please use black and white text ONLY where its
       needed / no colored text anywhere". These were black at 78% and 62% - not a hue, but still
       a grey, and on the beige ground they read as washed-out type rather than as hierarchy.
       All three now resolve to #000; --ink-soft and --soft are KEPT as names so the many rules
       pointing at them do not have to change, and so a future decision to reintroduce a muted
       tone is one line rather than a hunt through the file.
       Hierarchy is carried by SIZE, WEIGHT and LETTER-SPACING from here on, not by opacity. */
    --ink:       #000000;
    --ink-soft:  #000000;
    --soft:      #000000;
    --rule:      rgba(0,0,0,.18);
    --accent:    #000000;   /* deliberately NOT a colour any more */

    --on-art:    #ffffff;   /* type over a painting - pure white, not a cream tint */
    --on-dark:   #ffffff;   /* type on a black fill */

    /* MOTION TOKENS. Every duration in the build resolves to one of these three. */
    --ease:   cubic-bezier(.19, 1, .22, 1);
    --ease-io: cubic-bezier(.65, 0, .35, 1);
    --d-slow: 1.2s;
    --d:      0.7s;
    --d-fast: 0.5s;
    /* *** AXISMAIL'S MOTION, ON EVERYTHING (2026-09-11). *** Vader, before showing the site to Murtaza: "what
       murtaza likes.. is fade in and ease in on everything ... just like we did on axismail and accounts".
       AxisMail's entrance curve (its --ease-out-quart: long tail, no overshoot) and its entrance length. */
    --ease-reveal: cubic-bezier(.165, .84, .44, 1);
    --d-reveal:    1.4s;
    --d-reveal-slow: 1.85s;   /* AxisMail's landing entrance, for the big display type */

    /* *** SQUARE. THIS SITE OVERRIDES THE 8px HOUSE RULE, ON VADER'S INSTRUCTION. ***
       2026-09-10: "yes make all buttons and images square". The standing rule across GrandAxis
       UIs is 8px radius, never pills - it is written down and it still holds everywhere else.
       Here it is deliberately set to 0 because ponarsher.com, the reference the client approved,
       is square-cornered throughout, and rounded cards were reading as a different design.
       Do NOT "correct" this back to 8px on the assumption it was an oversight. The circular
       elements - the floating burger disc, the "more photo" sticker, the gallery arrow - keep
       their 50% and are separately documented; they are discs, not rounded rectangles. */
    --radius: 0;
    --wrap:   1280px;
    /* *** ONE HEADER, EVERY PAGE. ***
       Vader, 2026-09-10: "header should be same on all pages". It was not. The inner pages had
       been rebuilt to the reference's pattern - a short nav row up top with the wordmark dropped
       below it - while the home page still carried the ORIGINAL layout, a 100px bar with the mark
       sitting inline beside the links. Measured at 1440px before the fix:
           home    header 100px,  nav row y=30,  wordmark y=28   <- mark level with the links
           inner   header  69px,  nav row y=15,  wordmark y=93   <- mark dropped below them
       *** THE HOME PAGE'S HEADER IS THE ONE THAT WINS. ***
       Vader, 2026-09-10, after I first unified them the WRONG way round: "the header on the home
       page was better... i wanted that header on all other pages. instead you changed the home
       page header." So the 100px bar with the wordmark sitting level with the links is the design,
       and the inner pages adopted it - not the other way about. The body.has-band overrides that
       used to shrink the bar and drop the mark are deleted; do not re-introduce either. */
    --head-h: 60px;         /* MANGO, measured: 60 on a phone, 56 from 980px up - see the header block */
    /* THE GAP BETWEEN THE HEADER AREA AND THE CONTENT, on every page (Vader, 2026-09-11: "on all pages content
       is very close to header.. give some space"). It was 16px under the white strip and 26px on About; the
       overview's 64px, which he had accepted, is now the one value everywhere - 32px on a phone, 64 at 1440. */
    --gap-content: clamp(32px, 4.5vw, 64px);
}

*, *::before, *::after { box-sizing: border-box; }

/* overflow-x: CLIP not hidden — `hidden` on html/body silently creates a scroll container, which
   kills position:sticky, and this whole build is stacked sticky panels. That bug cost a full
   session on AxisMail; it is not being repeated on the one feature the client is paying for. */
html { overflow-x: clip; -webkit-text-size-adjust: 100%; }
body {
    overflow-x: clip; margin: 0;
    background: var(--bg); color: var(--ink);
    font-family: 'Mango New', -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;
    font-size: 16px; line-height: 1.7; -webkit-font-smoothing: antialiased;
}
img, video { max-width: 100%; height: auto; display: block; }
a { color: inherit; text-decoration: none; transition: color var(--d-fast) var(--ease); }
/* House law (Vader, 2026-08-30): NO underline on hover, anywhere.
   That law says "colour only" - but with an all-black palette a colour change has nothing to
   change TO. Opacity is the honest substitute: still no underline, still a clear hover state. */
a { transition: color var(--d-fast) var(--ease), opacity var(--d-fast) var(--ease); }
a:hover { opacity: .62; }
/* ...EXCEPT the Mango pieces (sana3, 2026-09-11). Vader: "no dimming plz" - the whole hero is ONE link,
   so the fade above took CONTEMPORARY ART and DISCOVER MORE to 62% under the pointer. The bar, the MENU
   panel and the MY WORK strip are exempt for the same reason: they change COLOUR on hover (the hovered
   item black, the rest #5b5b5b), and a 62% fade turns the black the hovered item is meant to be into grey. */
.hero-copy:hover, .site-head a:hover, .mega a:hover, .list-head a:hover, .enq a:hover { opacity: 1; }

.wrap { width: 100%; max-width: var(--wrap); margin: 0 auto; padding: 0 22px; }
.narrow { max-width: 720px; margin-inline: auto; }

/* *** ONE FAMILY, LIKE THE REFERENCE. ***
   Headings are Mango New at 600 (was Roboto at 600, the weight ponarsher.com uses; font swapped
   to Mango New on sana3, 2026-09-11 - see the @font-face block at the top of this file).
   There is no display serif any more; if a heading needs emphasis it gets weight and letter-
   spacing, not a second typeface. */
h1, h2, h3, .display {
    font-family: 'Mango New', -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;
    font-weight: 600; line-height: 1.22; margin: 0 0 0.5em;
}
p { margin: 0 0 1.1rem; }

/* ════════════════════════════════════════════════════════════════════════════════════════════
   1. REVEAL STATES  —  the "before" and "after" of every scroll animation.
   JS adds .is-in when the element enters view; that is the ONLY thing JS does to these.
   Written as [data-anim="..."] so the markup declares its own motion and nothing is hardcoded
   per-section. Default (no data-anim) is a plain fade-up.
   ════════════════════════════════════════════════════════════════════════════════════════════ */
/* *** EVERY HIDDEN STATE IS SCOPED TO html.js. ***
   site.js adds that class as its first act. Without it - script blocked, failed to load, an old
   browser, a CDN hiccup - none of these rules match and the page is simply the page, unanimated
   and fully readable. Content must never depend on a script to be visible; the motion is the
   enhancement, not the page.
   The .is-in rule must carry the same prefix or it cannot beat the hidden state it is meant to
   undo - they are one system and have to be scoped together. */
html.js [data-anim] {
    /* AxisMail's entrance: out of an 8px BLUR as well as a fade, on its curve, over 1.4s - it arrives and
       sharpens rather than sliding into place (2026-09-11). .is-in clears the filter and frees the layer. */
    opacity: 0; filter: blur(8px);
    transition-property: opacity, transform, filter;
    transition-duration: var(--d-reveal);
    transition-timing-function: var(--ease-reveal);
    /* backface-visibility stops the 1px text shimmer Safari/Chrome show mid-transform */
    -webkit-backface-visibility: hidden; backface-visibility: hidden;
    will-change: opacity, transform;
}
/* Travel is AxisMail's 14px - the blur carries the entrance, so a long slide is no longer needed (was 64px). */
html.js [data-anim="up"]    { transform: translateY(14px); }
html.js [data-anim="down"]  { transform: translateY(-14px); }
html.js [data-anim="left"]  { transform: translateX(24px); }
html.js [data-anim="right"] { transform: translateX(-24px); }
html.js [data-anim="zoom"]  { transform: scale(.92); }
html.js [data-anim="blur"]  { filter: blur(12px); }
/* *** fade CHANGES OPACITY ONLY - NO TRANSFORM. ***
   For anything whose GEOMETRY must not move: the full-bleed tile grid is edge to edge, so a
   scale(.92) reveal shrinks each tile inside its column and opens a ~51px gap between
   neighbours for the length of the animation. Layout-affecting reveals do not belong on a
   seamless grid. */
html.js [data-anim="fade"]  { transform: none; filter: none; }   /* edge-to-edge tiles: a blur would halo into the neighbours */
/* Slow, long-travel variant for big display type - Tilda's 1.2s heading tier. */
html.js [data-anim="rise"]  { transform: translateY(20px); transition-duration: var(--d-reveal-slow); }

/* *** THE COMPOSITING LAYER IS RELEASED WHEN THE ANIMATION ENDS. ***
   Vader, 2026-09-10: "WHY IS IT BLURRED AND DIMMED? AND NOT CLEAR BLACK?" - and he was looking at
   text whose colour computed to pure black with opacity 1. The cause was not colour at all.

   [data-anim] sets will-change: opacity, transform AND backface-visibility: hidden. Both promote
   the element onto its own GPU layer. On a promoted layer the browser turns OFF subpixel
   antialiasing (it cannot know what is behind the layer) and rasterises the text once - so glyphs
   render soft and thin, which reads exactly as "blurred and dimmed". Those two properties are
   there for the DURATION of the transition; I left them on permanently, so every animated block
   on the site was rendering its text on a compositor layer forever after.

   Releasing them in .is-in hands the text back to the normal painting path the moment the reveal
   finishes. Nothing about the animation changes - only what happens after it. */
html.js [data-anim].is-in {
    opacity: 1; transform: none; filter: none;
    will-change: auto;
    -webkit-backface-visibility: visible; backface-visibility: visible;
}

/* Stagger. JS sets --i on each child of a [data-stagger] group; 90ms reads as a considered
   cascade, while 150ms+ starts to look like the page is loading slowly rather than performing. */
[data-stagger] > * { transition-delay: calc(var(--i, 0) * 90ms); }

/* *** ACCESSIBILITY IS NOT OPTIONAL, EVEN WHEN MOTION IS THE PRODUCT. ***
   Someone with vestibular disorder gets the site, not a blank page: everything lands in its
   final state instantly. Parallax is switched off in JS by the same query. */
@media (prefers-reduced-motion: reduce) {
    html.js [data-anim], html.js [data-anim].is-in {
        opacity: 1 !important; transform: none !important; filter: none !important;
        transition: none !important;
    }
    html { scroll-behavior: auto !important; }
}

/* ════════════════════════════════════════════════════════════════════════════════════════════
   2. PAGE TRANSITION  —  the curtain.
   A fixed beige sheet that wipes over the page on leaving and lifts on arriving. It is beige,
   not black, because a black flash on a cream site reads as a fault, not as a transition.
   ════════════════════════════════════════════════════════════════════════════════════════════ */
.curtain {
    position: fixed; inset: 0; z-index: 200;
    background: var(--bg);
    pointer-events: none;
    opacity: 1;
    transition: opacity 620ms var(--ease);
}
/* Arriving: JS removes .is-closed on load, so the sheet lifts and reveals the page. */
.curtain.is-open   { opacity: 0; }
.curtain.is-closed { opacity: 1; pointer-events: all; }

/* ── PAGE TO PAGE: AXISMAIL'S BLUR CROSS-DISSOLVE (2026-09-11) ──────────────────────────────────────────────
   Where the browser does cross-document View Transitions (Chrome 126+, Safari 18.2+ - the @supports test below
   and the VT flag in site.js are the same test), the old page leaves out of focus and the new one arrives
   into focus - AxisMail's exact pair: out .45s ease, blurring 8px and lifting 4px; in .65s on its curve, from a
   10px blur, 6px low. The beige curtain is then hidden BEFORE the first paint: if it were in the new page's
   first frame, the transition would dissolve into a flat beige sheet. Everywhere else the curtain carries on. */
@view-transition { navigation: auto; }
@supports (view-transition-class: none) { .curtain { display: none; } }
@keyframes ss-vt-out { to   { opacity: 0; filter: blur(8px);  transform: translateY(-4px); } }
@keyframes ss-vt-in  { from { opacity: 0; filter: blur(10px); transform: translateY(6px);  } }
::view-transition-old(root) { animation: ss-vt-out .45s ease both; }
::view-transition-new(root) { animation: ss-vt-in  .65s var(--ease-reveal) both; }
/* A click inside a page (the 1 / 2 / 3 view switch): AxisMail's action transition - a .25s fade + blur, no travel. */
@keyframes ss-vt-quick-out { to   { opacity: 0; filter: blur(6px); } }
@keyframes ss-vt-quick-in  { from { opacity: 0; filter: blur(6px); } }
html.vt-quick::view-transition-old(root) { animation: ss-vt-quick-out .25s ease both; }
html.vt-quick::view-transition-new(root) { animation: ss-vt-quick-in  .25s ease both; }
@media (prefers-reduced-motion: reduce) {
    ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
}

/* ════════════════════════════════════════════════════════════════════════════════════════════
   3. HEADER  —  transparent, sits over the hero, and SCROLLS AWAY WITH THE PAGE.
   ════════════════════════════════════════════════════════════════════════════════════════════ */

/* === HEADER - MANGO'S, MEASURED (sana3, 2026-09-11) ==========================================
   Vader: "exact same header from mango", "same header height, same font size, everything same,
   same spacing in text, same margin", "as if i swap chrome tabs, without any difference".
   Read off shop.mango.com/gb/en/h/men with getComputedStyle - not by eye:
     DESKTOP (1440)  position:sticky, top:0, 56px tall, #fff, no border; page content starts at 56
                     links 12px / 600 / line-height 16px / letter-spacing normal / uppercase / #131313
                     20px between links, 32px from each edge, text centred on the bar (y=20)
                     active link: a 1px rule 4px below the text
                     wordmark 96x16, dead centre
     PHONE (414)     sticky, 60px, #fff
                     menu button 36x48 at x=10, its icon 24x20; wordmark 16px tall straight after it
                     icons 20px tall on a 36px pitch, 10px from the right edge
   Ours differ only in WHAT the links say: MY WORK / ABOUT ME / CONTACT, then SEARCH / INSTAGRAM.
   This REPLACES the transparent Pon-style bar and its floating white burger disc on sana3 only;
   sana2 keeps those. The white bar never inverts, so the old body.head-light rules are gone
   (site.js still toggles the class - harmless, nothing styles it in the header now). */
@media (min-width: 980px) { :root { --head-h: 56px; } }

.site-head {
    position: sticky; top: 0; z-index: 100;
    height: var(--head-h);
    background: #fff; color: #131313;
    display: flex; align-items: center;
}
.head-inner {
    position: relative; width: 100%; height: 100%;
    padding: 0 10px;
    display: flex; align-items: center; justify-content: space-between;
}
@media (min-width: 980px) { .head-inner { padding: 0 32px; } }

/* One type spec for every text link in the bar. */
.nav a, .head-link {
    font-size: 12px; font-weight: 600; line-height: 16px; letter-spacing: normal;
    text-transform: uppercase; color: #131313;
}

.nav { display: none; gap: 20px; align-items: center; }
@media (min-width: 980px) { .nav { display: flex; } }
/* 4px above and below the text: that is where Mango's active rule sits - 4px under the letters -
   and the text itself stays on the same line as every other link in the bar. */
.nav a { position: relative; padding: 4px 0; }
.nav a::after {
    content: ''; position: absolute; left: 0; bottom: 0; width: 100%; height: 1px;
    background: currentColor; transform: scaleX(0); transform-origin: left;
    transition: transform var(--d-fast) var(--ease);
}
.nav a:hover::after, .nav a[aria-current="page"]::after { transform: scaleX(1); }

/* Wordmark: 16px tall, like theirs. Phone - in the row straight after the menu button, with
   margin-right:auto pushing the icons to the far edge. Desktop - taken out of the row and centred
   on both axes. Centred with insets + margin:auto, NOT translate: nothing here animates now, but
   the transform rule stays banned on .brand so the old intro bug cannot come back. */
/* CENTRED AT EVERY WIDTH. Vader, 2026-09-11: "on mobile center the logo" (Mango's phone header puts it on
   the left; he wants it centred). Out of the flex row, so the burger and the icons keep the corners.
   *** THE PHONE LIFT IS OPTICAL, AND MEASURED. *** The file's letters do not sit in the middle of the image -
   the brush stroke fills the top - so their centre is 45.5px below the image centre in a 429px-tall file:
   2.3px low at 22px tall. A box-centred logo therefore LOOKED low against the icons. inset-top -4px moves
   the margin:auto centre up 2px, so the letters, not the box, line up with the burger and search icons.
   Desktop keeps plain centring (inset 0) - untouched, as asked. */
.brand {
    display: block; line-height: 0;
    position: absolute; inset: -4px 0 0 0; margin: auto;
    width: -moz-fit-content; width: fit-content; height: 22px;   /* must equal the img height to centre */
}
.brand img {
    height: 22px; width: auto;   /* Mango 16 -> 18 -> 22: Vader "tiny bit bigger", then "bit more bigger". The bar
                                    stays 56/60: its height is --head-h, and the mark is centred inside it. */
    /* *** NEUTRAL INK, NOT THE FILE'S BROWN. *** The PNG is drawn in a warm dark brown-black. brightness(0)
       flattens every pixel to black WITHOUT touching alpha, so the brush-stroke texture survives as tones of
       one colour; invert(.0745) then lifts that black to #131313 - the same ink as every link in the bar,
       and Mango's own brand colour. Pure #000 would be brightness(0) alone. */
    filter: brightness(0) invert(.0745);
}
@media (min-width: 980px) { .brand { inset: 0; } }

/* INSTAGRAM - an icon on a phone (36x48, 20px glyph), the word from 980px up. SEARCH removed 2026-09-11. */
.head-actions { display: flex; align-items: center; }
.head-link { display: grid; place-items: center; width: 36px; height: 48px; }
.head-link .hl-text { display: none; }
.head-link .hl-icon { width: 20px; height: 20px; display: block; }
@media (min-width: 980px) {
    .head-actions { gap: 20px; }
    .head-link { display: inline-block; width: auto; height: auto; }
    .head-link .hl-text { display: inline; }
    .head-link .hl-icon { display: none; }
}

/* MENU + a down chevron (Vader, 2026-09-11). In the bar's own type - 12px / 600 / uppercase - not an icon.
   The button sits at x=10 with 6px padding, so the word starts at 16px: the panel's phone inset. */
/* *** THE DOWN ARROW ALONE (2026-09-11). *** Client feedback via Vader (Chandu): "Take out the word menu. Just
   leave the down arrow." A 20px line chevron, 1.5 stroke, in a 36x48 target - the Instagram icon's exact twin
   on the other side: same size, same stroke, the same 18px from its edge of the screen. */
.burger {
    display: grid; place-items: center;
    width: 36px; height: 48px; padding: 0; border: 0; background: none; color: #131313; cursor: pointer;
}
.burger-chev { width: 20px; height: 20px; display: block; }
@media (min-width: 980px) { .burger { display: none; } }

/* === THE MENU - Mango's panel, measured, ONE element at every size. Reasoning + numbers in inc/header.php ===
   PHONE (below 980px): full screen over the bar, opened by MENU. 16px insets, tabs 16px apart, a 48x48 close
   flush right, list rows 54px starting 8px under the bar (Mango's phone menu, measured 2026-09-11).
   DESKTOP (980px+): the 416px panel from the left, scrim over the page, 32px insets, 20px gaps, 48px rows. */
.mega-backdrop { display: none; }
.mega {
    position: fixed; top: 0; left: 0; bottom: 0; z-index: 110;
    width: 0; overflow-x: hidden; overflow-y: auto;   /* y: the form can outgrow a short screen */
    background: #fff; color: #131313; visibility: hidden;
    transition: width .5s cubic-bezier(.3, .85, .5, 1), visibility 0s linear .3s;
}
body.mega-open .mega {
    width: 100%; min-width: 320px; visibility: visible;   /* Mango's phone menu: 0 -> 100%, min 320px */
    transition: width .5s cubic-bezier(.3, .85, .5, 1), visibility 0s;
}
/* Fixed width inside: the panel REVEALS its content as it grows instead of squashing it. min-height (not
   height) so the footer sits on the bottom edge when there is room, and scrolls with the rest when not. */
.mega-inner { width: 100vw; min-width: 320px; min-height: 100%; display: flex; flex-direction: column; }
@media (max-width: 979px) { body.mega-open { overflow: hidden; } }   /* a full-screen menu: the page must not scroll under it */

.mega-row {
    position: relative; height: var(--head-h); padding-left: 16px;
    display: flex; align-items: center; gap: 16px;
}
.mega-row a {
    font-size: 12px; font-weight: 600; line-height: 16px; letter-spacing: normal;
    text-transform: uppercase; color: #5b5b5b;
    transition: color .3s cubic-bezier(.3, .85, .5, 1);
}
.mega-row a.is-on { color: #131313; }
.mega-close {
    margin-left: auto; width: 48px; height: 48px; padding: 0; border: 0; background: none;
    display: grid; place-items: center; color: #131313; cursor: pointer;
}
.mega-close svg { width: 20px; height: 20px; display: block; }
/* THE SLIDE: one line, repositioned by JS; the transition is what makes it travel. 39px down at both sizes. */
.mega-underline {
    position: absolute; top: 39px; left: 0; width: 0; height: 0;
    border-bottom: 1px solid #131313;
    transition: left .5s cubic-bezier(.3, .85, .5, 1), width .5s cubic-bezier(.3, .85, .5, 1);
}

.mega-list { display: none; list-style: none; margin: 8px 0 0; padding: 0 16px; }
.mega-list.is-on { display: block; animation: mega-in .3s cubic-bezier(.3, .85, .5, 1); }
.mega-list li { height: 54px; display: flex; align-items: center; }
.mega-list a {
    position: relative; font-size: 12px; font-weight: 600; line-height: 16px;
    text-transform: uppercase; color: #131313;
    transition: color .3s cubic-bezier(.3, .85, .5, 1);
}

/* CONTACT = the enquiry form. Global form styles; this only fits it to the panel's insets. */
.mega-form { padding: 0 16px; }
.mega-form form { width: 100%; }

/* Panel footer: copyright, a hairline, then INSTAGRAM left / Powered by GrandAxis right. */
.mega-foot { margin-top: auto; padding: 24px 16px 24px; }
.mega-copy { margin: 0 0 12px; font-size: 12px; font-weight: 400; line-height: 16px; color: #131313; }
.mega-foot-row {
    display: flex; align-items: center; justify-content: space-between;
    padding-top: 12px; border-top: 1px solid var(--rule);
}
.mega-ig {
    font-size: 12px; font-weight: 600; line-height: 16px; text-transform: uppercase; color: #131313;
    transition: color .3s cubic-bezier(.3, .85, .5, 1);
}
.mega-foot .powered { display: inline-flex; align-items: center; gap: 8px; color: #131313; }
.mega-foot .powered span { font-size: 11px; line-height: 1; }
.mega-foot .powered img { height: 22px; width: auto; display: block; }

@media (min-width: 980px) {
    /* Scrim over the page only - it starts BELOW the bar. Vader, 2026-09-11: "on hover no need to dim logo". */
    .mega-backdrop {
        display: block; position: fixed; inset: var(--head-h) 0 0 0; z-index: 105;
        background: rgba(66, 66, 66, 0); pointer-events: none;
        transition: background-color .5s cubic-bezier(.3, .85, .5, 1);
    }
    body.mega-open .mega-backdrop { background: rgba(66, 66, 66, .25); pointer-events: auto; }
    body.mega-open .mega { width: 416px; min-width: 0; }
    .mega-inner { width: 416px; min-width: 0; }
    /* The row lands EXACTLY on the header's own nav - same height, 32px inset, 20px gap. */
    .mega-row { padding-left: 32px; gap: 20px; }
    .mega-close { margin-left: 0; width: 32px; }            /* X sits 20px after the last tab, as on Mango */
    .mega-close svg { width: 16px; height: 16px; }
    .mega-list { margin-top: 20px; padding: 0 0 0 32px; }
    .mega-list li { height: 48px; }
    .mega-form { padding: 0 32px; }
    .mega-foot { padding: 0 32px 24px; }
}

/* Hover behaviour only where there is a real pointer - on touch :hover sticks after a tap. */
@media (hover: hover) {
    .mega-list a::after {
        content: ''; position: absolute; left: 0; right: 0; bottom: -4px;
        border-bottom: 1px solid transparent;
        transition: border-color .3s cubic-bezier(.5, 0, 0, 1);   /* Mango's --motion-easing-accent */
    }
    .mega-list a:hover::after { border-color: currentColor; }
    /* Vader, 2026-09-11: hovered stays black, the rest of that list dims to the row's grey. */
    .mega-list:has(a:hover) a:not(:hover) { color: #5b5b5b; }
    .mega-ig:hover { color: #5b5b5b; }
}
@keyframes mega-in { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
    .mega, .mega-backdrop, .mega-underline, .mega-row a { transition: none !important; }
    .mega-list.is-on { animation: none; }
}

/* The old beige phone drawer (.drawer) was REMOVED 2026-09-11 - the menu panel above replaces it. */

/* ════════════════════════════════════════════════════════════════════════════════════════════
   4. HERO  —  full-bleed painting, wordmark, chevron.
   The image is oversized and moved by JS (parallax); the wrapper clips it.
   ════════════════════════════════════════════════════════════════════════════════════════════ */
/* *** THE HERO GROUND IS BLACK. THE GREEN IS FOR INNER PAGES ONLY. ***
   Vader, 2026-09-10: "why does the hero has green overlay? the green was only for inside pages,
   hero was black overlay". I had put #26393d here when the painting switched to `contain` and
   the sides of the frame became visible - wrong, and my addition, not his. The hero's own veil
   is and always was the black gradient in .hero-media::after; the ground behind the canvas has
   to be the same black or the two read as different surfaces.
   #26393d belongs to .page-head and .foot. Do not bring it onto the home page. */
.hero { position: relative; height: calc(100svh - var(--head-h)); min-height: 560px; overflow: hidden;
        background: #000; }
/* *** THE OVERHANG MUST EXCEED THE PARALLAX TRAVEL, OR THE PAGE SHOWS THROUGH. ***
   Travel is (viewportH + sectionH) / 2 * rate - see tick() in site.js. This section is 100svh
   and runs at rate .22, so it moves up to ~22% of its own height each way; 26% gives margin.
   Change one of these three numbers and you must check the other two. */
/* Travel is (viewportH + sectionH) / 2 * rate — see tick() in site.js. 100svh at rate .22 moves
   up to ~22% each way; 26% leaves margin. Change one of these three and check the other two. */
/* *** THE HERO IS FULL SCREEN. THE PAINTING IS CROPPED TO FILL, AND THAT IS THE CHOICE. ***
   Vader, 2026-09-10: "make hero image full screen as it was". I had briefly fitted the whole
   canvas (object-fit: contain) because he said it looked "zoomed in too much" - that showed all
   of it but put ~250px of ground down each side, and he wants the edge-to-edge picture back.
   So: cover, full bleed, and the crop is accepted. The zoom is answered by LIGHTENING the veil
   instead - see .hero-media::after below - not by shrinking the picture.

   THREE NUMBERS MOVE TOGETHER: this inset, the rate in index.php's data-parallax, and .hero's
   height. Travel is roughly rate x section height and the inset must exceed it, or the black
   ground shows through at the edge. .22 x 100svh = 22%; 26% leaves margin. */
.hero-media { position: absolute; inset: -26% 0; }
/* *** THE PAINTING IS DESATURATED, NOT JUST DARKENED. ***
   Client feedback, 2026-09-09, three times over: "Again so colorful", "the painting u have put in
   background are too bright", "Why color coming in your face like you will die". A darker veil
   alone does not fix that — it makes a loud picture dim AND loud. Pulling the saturation back is
   what actually quietens it, and it is why the reference reads calm: their hero is a muted grey
   photograph, not a high-chroma canvas.
   The artwork itself is NEVER filtered anywhere else on the site — in the grid and on a work page
   every painting is at full strength. This applies only where a picture sits BEHIND type. */
.hero-media img {
    width: 100%; height: 100%; object-fit: cover;
    filter: saturate(.42) brightness(.82) contrast(.96);
}

/* *** ON A WIDE SCREEN THE PAINTING IS SHOWN WHOLE, NOT CROPPED TO FILL. ***
   Vader, 2026-09-10: "image is not fitting properly, its zoomed in too much". He is right and it
   is arithmetic, not taste. hero-2.jpg is 1269x1240 - practically square - while a desktop hero
   is a wide, short window. `cover` scales the image until it fills the WIDTH, so on a 1440px
   viewport it renders about 1440x1408 while only ~900px of that is ever visible: you see the
   middle two-thirds of the canvas and the composition's top and bottom are gone. No amount of
   object-position fixes that; the image is simply enlarged past the frame.

   `contain` fits the whole canvas inside the frame instead, with the black ground at the sides.
   That is the right trade for a PAINTING - a photograph can be cropped, a finished artwork
   should not be - and it matches how a gallery site presents work.

   MOBILE KEEPS `cover`. A phone hero is tall and narrow (414x800), so `contain` there would put
   the canvas in a small band with deep black above and below it. Cropping is the lesser evil at
   that shape, and it is also where "no empty spaces" was asked for. */
/* NOTE FOR ANYONE TEMPTED BY `contain` HERE: it was tried on 2026-09-10 and rejected. It does
   show the whole canvas, but hero-2.jpg is near-square (1269x1240) against a wide hero, so
   fitting it leaves about 250px of black down each side. Vader chose the full-bleed crop. */

/* A soft ink wash so white type holds over her paint, plus a fade to page colour at the very
   bottom so the hero resolves into the beige with no visible horizon. */
/* *** BLACK OVERLAY: HEAVIEST AT THE TOP, LIGHTEST AT THE BOTTOM. ***
   Vader, 2026-09-09: "give it black gradient overlay thicker at top thiner at bottom".
   One gradient, exactly that - no radial pool under the title any more. Weighting it to the top
   also happens to be what the transparent header needs, since the white nav sits up there and
   has nothing else to hold against; the bottom stays light so the painting keeps its colour
   where the eye actually rests.
   The photograph still runs edge to edge - there is deliberately NO fade to page colour.

   DARKENED 2026-09-09 on Vader's second pass ("decrease opactiy on overlay make it more darker").
   Every stop went up in one decisive step rather than a timid nudge - the GoGreen hero took three
   rounds of small adjustments before landing, and small adjustments are what caused that. The
   top-heavy SHAPE is unchanged; only the weight moved.
       was  .72 / .56 / .38 / .24 / .14
       then .90 / .80 / .66 / .52 / .40

   *** LIGHTENED AGAIN 2026-09-10: "lighten the overlay". ***
   Back to the lighter set he lived with before the darkening pass - one decisive step, not a
   timid nudge. The GoGreen hero took three small adjustments to land and the smallness is
   exactly why it took three; this goes straight to a weight that is known to work.
       now  .72 / .56 / .38 / .24 / .14
   The top stop is what the white nav holds against, so it is the one to watch if this is
   lightened further - below about .55 the links start to struggle over a pale passage of paint. */
/* *** THE HERO VEIL WAS REMOVED, 2026-09-10: "remove overlay from hero". ***
   It was a black top-heavy gradient (.72 / .56 / .38 / .24 / .14) whose job was to let the white
   nav and wordmark hold over the painting. With it gone the header type has nothing behind it
   but the canvas, so IF a future hero image is pale across its top strip the nav will disappear
   into it - check that before swapping the file, not after.
   The desaturating filter on .hero-media img is a SEPARATE thing and is still there; it answers
   the client's "again so colorful", not the type contrast. */
.hero-inner {
    position: relative; z-index: 2; height: 100%;
    display: flex; flex-direction: column; align-items: center; justify-content: flex-end;
    padding-bottom: 12vh; text-align: center; color: var(--on-art);
}
.hero-title {
    font-size: clamp(2rem, 1.2rem + 3.4vw, 4.2rem);
    color: var(--on-art); margin: 0 0 10px; letter-spacing: 0.01em;
    text-shadow: 0 2px 40px rgba(0,0,0,.45);
}
.hero-sub {
    font-size: 0.72rem; letter-spacing: 0.34em; text-transform: uppercase;
    color: #fff; margin: 0;
}

/* ── HERO TEXT, MANGO'S (sana3, 2026-09-11) - see index.php. Measured off shop.mango.com's NEW NOW:
       title   40px / 40px on a phone, 68px / 72px from 980px; 600, uppercase, #fdfdfd, centred
       link    DISCOVER MORE - 13px / 20px / 600 / uppercase, in a 48px-tall target, 12px under the title
       place   the pair sits in the vertical middle of the hero (on Mango: 478 in a 60-896 hero)
   The whole hero is the link (inset 0), under the chevron (z 3) so the chevron still scrolls.
   A soft shadow behind the letters only - the hero veil is gone, and white type needs something
   when the paint behind it is pale. */
.hero-copy {
    position: absolute; inset: 0; z-index: 2;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    padding: 0 16px; text-align: center; color: #fdfdfd;
}
/* ONE LINE ON A PHONE - Vader, 2026-09-11: "on mobile view it should be on 1 line". Mango's 40px suits
   NEW NOW; CONTEMPORARY ART is ~10.8em wide, so on a phone the size follows the screen: (width - 32px of
   padding) / 11, capped at Mango's 40px - 414 -> 34.7px, 375 -> 31.2px. nowrap is the guarantee.
   CHANGE THE WORDS AND RE-CHECK THIS DIVISOR. */
.hero-head {
    margin: 0; font-family: inherit; font-size: min(40px, calc((100vw - 32px) / 11)); line-height: 1;
    font-weight: 600; letter-spacing: normal; text-transform: uppercase; color: #fdfdfd; white-space: nowrap;
    text-shadow: 0 2px 24px rgba(0, 0, 0, .35);
}
.hero-cta {
    display: flex; align-items: center; height: 48px; margin-top: 12px;
    font-size: 13px; line-height: 20px; font-weight: 600; letter-spacing: normal; text-transform: uppercase;
    text-shadow: 0 1px 12px rgba(0, 0, 0, .35);
    transition: color .3s cubic-bezier(.3, .85, .5, 1);
}
/* No hover change at all on the hero - Vader, 2026-09-11: "no dimming plz". The text stays #fdfdfd. */
@media (min-width: 980px) { .hero-head { font-size: 68px; line-height: 72px; } }

/* Centred with insets, NOT translateX - see the note on .brand. This element carries data-anim
   AND a keyframe animation, so transform is spoken for twice over already. */
.chev {
    position: absolute; left: 0; right: 0; margin-inline: auto; bottom: 4vh; z-index: 3;
    width: 46px; height: 46px; display: grid; place-items: center;
    color: var(--on-art);
}
.chev.is-in { animation: chev-bob 2.6s var(--ease-io) infinite; }
@keyframes chev-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(9px); } }
@media (prefers-reduced-motion: reduce) { .chev { animation: none; } }

/* ════════════════════════════════════════════════════════════════════════════════════════════
   5. STICKY STACK  —  the effect Vader pointed at ("then scroll effect goes up").
   Each .panel pins while the NEXT section rides up over it. Pure CSS: the panel is sticky at
   top:0 and the following section has a higher stacking context and its own background, so it
   occludes the pinned one. No scroll-jacking, so the wheel/trackpad still behaves natively.
   ════════════════════════════════════════════════════════════════════════════════════════════ */
.panel { position: sticky; top: 0; height: 100svh; overflow: hidden; }
/* rate .14-.18 over a 100svh panel => up to ~18% travel each way; 22% gives margin. */
.panel-media { position: absolute; inset: -22% 0; }
.panel-media img { width: 100%; height: 100%; object-fit: cover; }
.panel-media::after {
    content: ''; position: absolute; inset: 0;
    background: linear-gradient(180deg, rgba(0,0,0,.34) 0%, rgba(0,0,0,.12) 45%, rgba(0,0,0,.46) 100%);
}
.panel-inner {
    position: relative; z-index: 2; height: 100%;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    text-align: center; color: var(--on-art); padding: 0 22px;
}
.panel-inner h2 { color: var(--on-art); font-size: clamp(1.7rem, 1.1rem + 2.4vw, 3rem); }
.panel-inner p { color: #fff; max-width: 56ch; }

/* Anything after a sticky panel must paint OVER it. */
.over { position: relative; z-index: 3; background: var(--bg); }

/* ════════════════════════════════════════════════════════════════════════════════════════════
   6. SECTION FURNITURE
   ════════════════════════════════════════════════════════════════════════════════════════════ */
.section { padding: clamp(70px, 11vw, 140px) 0; }
.section-alt { background: var(--bg-2); }
.band {
    text-align: center; padding: clamp(54px, 8vw, 96px) 0;
    background: var(--bg-2);
}
/* *** MEASURED TWICE, BECAUSE THE FIRST READING WAS OF A HIDDEN ELEMENT. ***
   "WORKS is too big" was correct - it was maxing at 6rem / 96px. But I then measured the
   reference's heading at 30px and shrank this to ~42px, and 30px turned out to be a HIDDEN
   mobile variant that ponarsher.com keeps in the DOM. The VISIBLE heading, checked at a 1280px
   viewport with a visibility test, is 64px at weight 600; at 414px it is ~30px.
   So the clamp spans those two real numbers. Lesson worth keeping: querySelectorAll returns
   elements the user cannot see, and a responsive site often carries two copies of the same
   heading - always confirm the one being measured has a non-zero box. */
/* *** SHRUNK AGAIN 2026-09-10: "MY WORK and little size font small". ***
   Second reduction on this heading. It was 6rem/96px, then the clamp above (max 4rem / 64px,
   matching ponarsher's real visible heading), and he still reads it as too big on this beige -
   their 64px sits on a near-white ground where heavy type carries better.
       was  clamp(1.9rem, 1.1rem + 3.6vw, 4rem)    -> 30px at 414, 64px at 1280+
       now  clamp(1.2rem, .6rem  + 2vw,   2.2rem)  -> 19px at 414, 35px at 1280+
   All three terms scaled by the same ~0.55 so the growth curve between the two ends is
   unchanged; only the size moved. */
.band h2 {
    font-size: clamp(1.2rem, .6rem + 2vw, 2.2rem);
    letter-spacing: 0.06em; text-transform: uppercase; margin: 0;
}
.eyebrow {
    font-size: 0.68rem; letter-spacing: 0.3em; text-transform: uppercase;
    color: var(--soft); margin: 0 0 14px;
}
.section-head { text-align: center; max-width: 62ch; margin: 0 auto clamp(34px, 5vw, 60px); }
.section-head h2 { font-size: clamp(1.6rem, 1.1rem + 1.8vw, 2.6rem); }
.section-head p { color: var(--soft); margin: 0; }

/* ════════════════════════════════════════════════════════════════════════════════════════════
   7. WORKS GRID  —  edge to edge, two up, with the hover reveal Vader screenshotted
   ("off hover and on hover": image darkens, title and a small link fade in centred).
   ════════════════════════════════════════════════════════════════════════════════════════════ */
/* *** FULL BLEED, NO GUTTERS - AS THE REFERENCE DOES IT. ***
   This went back and forth, and the history matters. The client's note was "Why over sized
   pictures coming out of frame without any gaps", which I read as "add gaps" - so I contained the
   grid and put 26px gutters in. Vader then compared it against ponarsher.com directly and settled
   it: "make tiles full bleed like pon, no gaps".
   Re-reading the original note with that answer in hand, the complaint was the OVERSIZED and
   OUT-OF-FRAME part - images overflowing the page - not the absence of gutters. That overflow is
   fixed separately and independently (page overflow measures 0), so the tiles can be edge to edge
   without bringing the actual fault back. */
.grid {
    display: grid; grid-template-columns: 1fr; gap: 0;
    width: 100%; max-width: none; margin-inline: 0; padding: 0;
}
@media (min-width: 768px) { .grid { grid-template-columns: 1fr 1fr; } }

/* Square corners and no radius: a rounded corner on a full-bleed tile leaves a sliver of page
   colour in the gap between neighbours, which is precisely the seam this layout removes. */
/* *** 4/3, MEASURED OFF THE REFERENCE. ***
   Vader, 2026-09-09: "image height is not same as Pon". Read with getComputedStyle at a 414px
   viewport: ponarsher.com's works tiles are 414 x 311 - ratio 1.33, landscape. Mine were 3/4
   portrait (0.75), so each tile stood nearly twice as tall and the grid scrolled forever.

   This crops these portrait canvases hard, and that is the deliberate trade: the tile is a
   TEASER, and the reference's proportion is what the client keeps measuring us against. The works
   page still shows every painting WHOLE - .card-media is 3/4 with object-fit:contain - so nothing
   is ever cropped where the work is actually the subject.
   Square corners: a radius on a seamless tile leaves a sliver of page colour between neighbours. */
.tile {
    position: relative; display: block; overflow: hidden;
    aspect-ratio: 4 / 3; background: var(--bg-3);
}
/* cover, not contain: edge-to-edge tiles must fill their box or the letterboxing shows as bands
   of page colour between neighbours. At 3/4 - the middle of the actual 0.69-0.81 range of these
   canvases - the crop is a few percent. The WORKS PAGE still shows every painting whole
   (.card-media uses contain); this is the teaser grid, not the catalogue. */
.tile img {
    width: 100%; height: 100%; object-fit: cover;
    transition: transform 1.1s var(--ease);
}
/* *** INVERTED 2026-09-10. THE OVERLAY IS THE RESTING STATE. ***
   Vader: "in my work... invert the black overlay.. off hover should be overlay and on hover no
   overlay and zoom effect". It used to be the other way round - clean painting at rest, darken
   and label on hover. Now the grid reads as a set of titled, quieted tiles, and pointing at one
   uncovers the painting at full strength and pushes in slightly.
   opacity 1 here, 0 on :hover - the transition is unchanged, only its direction. */
.tile::after {
    content: ''; position: absolute; inset: 0;
    background: rgba(0, 0, 0, .52);
    opacity: 1; transition: opacity var(--d) var(--ease);
}
.tile-cap {
    position: absolute; inset: 0; z-index: 2;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
    color: var(--on-art); text-align: center; padding: 20px;
    /* Visible at rest, because the overlay it sits on is now the resting state. It fades OUT on
       hover: with the veil gone the painting underneath is at full strength, and white type over
       a bright canvas is unreadable - so uncovering the work and keeping the label on top of it
       would undo the very thing the hover is for. */
    opacity: 1; transform: none;
    transition: opacity var(--d) var(--ease), transform var(--d) var(--ease);
}
.tile-cap h3 { font-size: clamp(1.15rem, 0.9rem + 1vw, 1.9rem); margin: 0; color: var(--on-art);
               text-transform: uppercase; }   /* Vader, 2026-09-11: "need all CAPS" (STILLPOINT, SUBMERGENCE) */
.tile-cap span { font-size: 0.68rem; letter-spacing: 0.24em; text-transform: uppercase; }

.tile:hover img, .tile:focus-visible img { transform: scale(1.05); }   /* the zoom he asked for */
.tile:hover::after, .tile:focus-visible::after { opacity: 0; }         /* veil OFF on hover */
.tile:hover .tile-cap, .tile:focus-visible .tile-cap { opacity: 0; }   /* label out of the way */

/* *** THE TOUCH OVERRIDE IS GONE, AND THAT IS BECAUSE OF THE INVERSION. ***
   It used to pin the hover state on phones, since there is no hover to reach it with. Now the
   overlay and the caption ARE the default, so a phone already shows exactly what that block used
   to force - and a rule that restates the default is one more thing to keep in step by hand.
   Touch simply never reaches the uncovered state, which is correct: there is nothing to point
   at. Tapping opens the works page. */

/* ════════════════════════════════════════════════════════════════════════════════════════════
   8. BUTTONS / FORMS
   ════════════════════════════════════════════════════════════════════════════════════════════ */
.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 10px;
    min-height: 50px; padding: 14px 30px; border-radius: var(--radius);
    border: 1px solid var(--ink); background: var(--ink); color: var(--on-dark);
    font: inherit; font-size: 0.74rem; letter-spacing: 0.2em; text-transform: uppercase;
    cursor: pointer; -webkit-tap-highlight-color: transparent;
    transition: background var(--d-fast) var(--ease), color var(--d-fast) var(--ease),
                border-color var(--d-fast) var(--ease), transform var(--d-fast) var(--ease);
}
.btn:hover { background: #000; color: #fff; transform: translateY(-2px); }
.btn-ghost { background: none; color: var(--ink); border-color: rgba(0,0,0,.42); }
.btn-ghost:hover { background: rgba(0,0,0,.06); color: var(--ink); border-color: var(--ink); }
.btn-light { background: #fff; border-color: #fff; color: #000; }
.btn-light:hover { background: #fff; color: #000; }

/* *** .btn-wide WAS USED IN THE MARKUP BUT NEVER DEFINED. ***
   The Send button carries class="btn btn-wide" and always has, but the rule was lost when this
   section was rewritten - so the class did nothing and the button sat at content width while the
   reference's spans the whole panel. A class that exists in the HTML and not in the CSS fails
   SILENTLY, which is how it survived several passes unnoticed. */
.btn-wide { width: 100%; }

label { display: block; margin-bottom: 7px; font-size: 0.68rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--soft); }
input[type="text"], input[type="email"], input[type="tel"], textarea {
    width: 100%; font: inherit; font-size: 16px;   /* 16px pinned: iOS zooms a focused field below it */
    padding: 14px 16px; min-height: 50px;
    background: #fff; color: var(--ink);
    border: 1px solid var(--rule); border-radius: var(--radius);
    transition: border-color var(--d-fast) var(--ease), box-shadow var(--d-fast) var(--ease);
}
input:focus, textarea:focus { outline: 0; border-color: var(--ink); box-shadow: 0 0 0 3px rgba(0,0,0,.10); }
textarea { min-height: 140px; resize: vertical; }
.field { margin-bottom: 18px; }
.note { font-size: .88rem; color: var(--soft); }
/* Achromatic like everything else - a tinted success/error block would be the only colour on
   the site. Meaning is carried by the border and the wording, not by green vs red. */
.form-ok  { padding: 14px 16px; border-radius: var(--radius); background: rgba(0,0,0,.05); color: #000; border-left: 3px solid #000; margin-bottom: 18px; }
.form-err { padding: 14px 16px; border-radius: var(--radius); background: rgba(0,0,0,.05); color: #000; border-left: 3px solid #000; margin-bottom: 18px; }

/* A select dressed exactly like the text fields beside it, with the site's line chevron. */
select {
    width: 100%; font: inherit; font-size: 16px; padding: 14px 44px 14px 16px; min-height: 50px;
    background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12'%3E%3Cpath d='M2 4.5l4 4 4-4' fill='none' stroke='%23131313' stroke-width='1.5'/%3E%3C/svg%3E") no-repeat right 16px center;
    color: var(--ink); border: 1px solid var(--rule); border-radius: var(--radius);
    -webkit-appearance: none; appearance: none; cursor: pointer;
    transition: border-color var(--d-fast) var(--ease), box-shadow var(--d-fast) var(--ease);
}
select:focus { outline: 0; border-color: var(--ink); box-shadow: 0 0 0 3px rgba(0,0,0,.10); }

/* ── PURCHASE ENQUIRY PANEL (sana3, 2026-09-11) - markup and reasoning: purchase_form() in inc/series.php.
   PHONE (the base): Mango's bottom sheet, read off Vader's screen recording frame by frame -
     scrim first: black at ~36% (a 160-brightness patch went to 103), fading in over .2s
     then the sheet rises from the bottom in .3s, strong ease-out (it covered 159 -> 103 -> 71 -> 44 -> 30
     -> 15 -> 7 -> 2 -> 0 px of its travel at 33ms steps); square corners; a small handle bar on top;
     as tall as its content, never taller than the screen less 48px of scrim to tap, scrolling inside.
   DESKTOP (980px+): the menu's mirror - from the RIGHT, the menu's 416px, sliding and fading in over
     the menu's .5s calm easing, over the menu's own scrim (#424242 at 25%). */
.enq-scrim {
    position: fixed; inset: 0; z-index: 120; background: rgba(0, 0, 0, .36);
    opacity: 0; visibility: hidden; transition: opacity .2s linear, visibility 0s linear .3s;
}
.enq {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 121;
    display: flex; flex-direction: column;   /* so the footer can sit on the bottom edge */
    max-height: calc(100svh - 48px); overflow-y: auto; overscroll-behavior: contain;
    background: #fff; color: #131313;
    transform: translateY(100%); visibility: hidden;
    transition: transform .3s cubic-bezier(.3, .85, .5, 1), visibility 0s linear .3s;
}
body.enq-open .enq-scrim { opacity: 1; visibility: visible; transition: opacity .2s linear, visibility 0s; }
body.enq-open .enq {
    transform: none; visibility: visible;
    transition: transform .3s cubic-bezier(.3, .85, .5, 1) .06s, visibility 0s;   /* the scrim leads by a beat */
}
body.enq-open { overflow: hidden; }
.enq-handle { display: block; width: 24px; height: 2px; margin: 8px auto 0; background: #131313; }
.enq-head { display: flex; align-items: center; justify-content: space-between; height: 48px; padding-left: 16px; }
.enq-title { margin: 0; font-size: 12px; line-height: 16px; font-weight: 600; letter-spacing: normal; text-transform: uppercase; color: #131313; }
.enq-close { width: 48px; height: 48px; padding: 0; border: 0; background: none; color: #131313; cursor: pointer; display: grid; place-items: center; }
.enq-close svg { width: 16px; height: 16px; display: block; }
.enq-body { padding: 8px 16px 24px; }
/* ── PANELS SLIDE, THEN THEIR CONTENTS COME IN OUT OF A BLUR - AxisMail's sign-in card (2026-09-11) ───────
   The MENU and the PURCHASE ENQUIRY keep their slides; what is inside each now fades up into focus a beat
   after the slide starts, on the entrance curve, and leaves quickly (.2s) when the panel closes. */
.mega-inner, .enq > * { transition: opacity .2s ease, filter .2s ease; }
body:not(.mega-open) .mega-inner, body:not(.enq-open) .enq > * { opacity: 0; filter: blur(8px); }
body.mega-open .mega-inner {
    opacity: 1; filter: none;
    transition: opacity .6s var(--ease-reveal) .12s, filter .6s var(--ease-reveal) .12s;
}
body.enq-open .enq > * {
    opacity: 1; filter: none;
    transition: opacity .6s var(--ease-reveal) .15s, filter .6s var(--ease-reveal) .15s;
}
@media (prefers-reduced-motion: reduce) {
    .mega-inner, .enq > * { transition: none !important; }
    body.mega-open .mega-inner, body.enq-open .enq > * { opacity: 1; filter: none; }
}
/* The menu's footer, reused - at least 32px of air above it when the form runs long. */
.enq .mega-foot { padding-top: 32px; }
/* The split: the comment on the left, the painting on the right. The picture's ::before is a blank line the
   height of a label, so its top meets the top of the comment box, not the "COMMENT" label. */
.enq-split { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; }
/* The comment box is exactly the painting's height - Vader, 2026-09-11: "make the comment box same height as
   image". Both halves are the same width and the picture is 3:4, so the box takes the same shape; its top
   already meets the picture's (the label spacer on .enq-art), so the bottoms meet too. No resize handle -
   dragging it would break the pair. */
.enq-split textarea { min-height: 0; aspect-ratio: 3 / 4; resize: none; }
.enq-art { margin: 0 0 18px; }
.enq-art::before { content: '\a0'; display: block; margin-bottom: 7px; font-size: 0.68rem; letter-spacing: 0.16em; }
.enq-img { display: block; width: 100%; aspect-ratio: 3 / 4; object-fit: cover; background: var(--bg-3); }
@media (min-width: 980px) {
    .enq-scrim { background: rgba(66, 66, 66, .25); transition: opacity .5s cubic-bezier(.3, .85, .5, 1), visibility 0s linear .5s; }
    body.enq-open .enq-scrim { transition: opacity .5s cubic-bezier(.3, .85, .5, 1), visibility 0s; }
    .enq {
        left: auto; top: 0; width: 416px; max-height: none;
        transform: translateX(100%); opacity: 0;
        transition: transform .5s cubic-bezier(.3, .85, .5, 1), opacity .5s cubic-bezier(.3, .85, .5, 1), visibility 0s linear .5s;
    }
    body.enq-open .enq {
        transform: none; opacity: 1;
        transition: transform .5s cubic-bezier(.3, .85, .5, 1), opacity .5s cubic-bezier(.3, .85, .5, 1), visibility 0s;
    }
    .enq-handle { display: none; }
    .enq-head { height: var(--head-h); padding-left: 32px; padding-right: 16px; }
    .enq-body { padding: 8px 32px 32px; }
}
@media (prefers-reduced-motion: reduce) {
    .enq, .enq-scrim, body.enq-open .enq, body.enq-open .enq-scrim { transition: none !important; }
}

/* The right-hand message panel (.msg-panel) was REMOVED 2026-09-11 - the form lives in the menu's CONTACT section. */

/* ════════════════════════════════════════════════════════════════════════════════════════════
   10. WORK DETAIL
   ════════════════════════════════════════════════════════════════════════════════════════════ */
.work-hero { padding-top: var(--gap-content); }   /* About: header -> content (a piece page's comes from its strip) */
.work-layout { display: grid; gap: 34px; }
@media (min-width: 980px) { .work-layout { grid-template-columns: 1.1fr 1fr; gap: 56px; align-items: start; } }

/* The stage shows the WHOLE painting - contain, never cover. Same reason as the cards: all 19
   works are portrait and cropping them is showing the client's work wrong. */
.work-stage { position: relative; background: var(--bg-3); border-radius: var(--radius); overflow: hidden; }
/* *** THE STAGE IS A STACK - THE TOP PHOTO SLIDES OFF AND UNCOVERS THE NEXT (Vader, 2026-09-11). ***
   "reveal effect is the top image slides left or right uncovering the next or previous image". Only the
   top photo (.is-top) moves; the one it uncovers (.is-under) lies still beneath it; the rest are hidden.
     - the FIRST photo stays in the flow and sizes the stage; the others lie over it (inset 0), contained
       on the stage ground - every slide is OPAQUE, or the photo beneath would show through its margins
     - NOTHING MOVES BUT THE EDGE. Read off Vader's screen recording of Mango at 30fps: both photos stay
       exactly where they are and the boundary between them follows the finger, cutting the top photo
       away IN PLACE (clip-path inset). No shadow, no slide. The first build translated the top photo -
       close, but the painting itself travelled, which Mango's never does.
     - touch-action: pan-y - a vertical finger still scrolls the page; a sideways one is the reveal (JS)
   The round next-image button and the side-by-side strip were both REMOVED (2026-09-11). */
.work-stage { touch-action: pan-y; }
.work-stage.can-drag { cursor: grab; }
.work-stage.is-dragging { cursor: grabbing; user-select: none; }
/* position: relative IS LOAD-BEARING. z-index does nothing on an unpositioned box, and the first photo -
   the one left in the flow to size the stage - had none. So whenever photo 1 was on top, the photo
   "beneath" it (absolute, positioned) painted OVER it: the first touch jumped straight to photo 2 and
   the reveal only worked from photo 2 on (Vader on his iPhone, 2026-09-11). Every slide is positioned now. */
.stage-slide {
    position: relative;
    display: flex; align-items: center; justify-content: center;
    background: var(--bg-3); visibility: hidden; z-index: 1;
    clip-path: inset(0 0 0 0);   /* an inset, never none - none -> inset() cannot animate */
}
.stage-slide + .stage-slide { position: absolute; inset: 0; }
.stage-slide.is-top   { visibility: visible; z-index: 3; }
.stage-slide.is-under { visibility: visible; z-index: 2; }
.stage-slide img {
    width: 100%; height: auto; max-height: 76vh; object-fit: contain; display: block;
    -webkit-user-drag: none; user-select: none; pointer-events: none;
}
.stage-slide + .stage-slide img { height: 100%; max-height: none; }   /* fill the stage the first photo set */

/* A WRAPPING ROW of small squares, not a fixed 4-column grid - the reference lets them run on and
   wrap, which matters here because one work has 6 thumbnails and several have none. */
.work-thumbs { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.work-thumbs button {
    padding: 0; border: 1px solid transparent; border-radius: 0; overflow: hidden;
    background: var(--bg-3); cursor: pointer;
    width: 62px; height: 62px; opacity: .72;
    transition: border-color var(--d-fast) var(--ease), opacity var(--d-fast) var(--ease);
}
/* ONE marker for the photo on the stage: the border. Vader, 2026-09-11: "the thumbnail focus is not working on
   which image is active". Hover used the SAME border, and on a phone :hover sticks to the last thumbnail
   tapped - so two looked active. Now hover only lifts the opacity, and only where there is a real pointer. */
.work-thumbs button[aria-current="true"] { border-color: var(--ink); opacity: 1; }
@media (hover: hover) { .work-thumbs button:hover { opacity: 1; } }
.work-thumbs img { width: 100%; height: 100%; object-fit: cover; }

/* *** THE SPEC LIST HAS NO RULES. ***
   It was a bordered <table>; the reference is a quiet two-column list - label, value, nothing
   else. On a gallery page the horizontal rules read as a spreadsheet and pull the eye away from
   the painting. Grid keeps the values in one column without any of that furniture. */
/* *** THESE NUMBERS ARE READ OFF ponarsher.com's PRODUCT PAGE, NOT CHOSEN. ***
   Vader, 2026-09-10: "DONT INVENT DESIGN... MAKE TEXT LIKE THIS". Measured with getComputedStyle
   on their "Fear of letting go" page:
       title        24px / weight 700 / letter-spacing NORMAL / line-height 32.4
       spec text    14px / weight 300 / line-height 21.7
       figures      14px / weight 700
   Mine had been 30.4px at weight 600 with 1.2px tracking, and body text at 14.7/400 - close
   enough to look deliberate, wrong enough to read as a different page. The tracking in
   particular was my house habit, not the reference. */
.work-title {
    font-size: 24px; font-weight: 700; line-height: 1.35;
    letter-spacing: normal; text-transform: uppercase; margin: 0 0 22px;
}
.spec { display: grid; grid-template-columns: auto 1fr; gap: 4px 22px; margin: 0 0 26px; }
/* *** WEIGHT 400, NOT THE REFERENCE'S 300. ***
   Vader, 2026-09-10: "WHAT THE FUCK IS THIS COLOR OF THIS TEXT?" - and the colour was already
   pure black, opacity 1, no tint anywhere in the chain. The greyness was the WEIGHT. A 300-weight
   stroke at 14px is roughly one pixel wide, so anti-aliasing blends most of it into the
   background and the result genuinely is grey pixels, whatever the declared colour says.
   ponarsher.com gets away with 300 because its ground is near-white (#f1f1f1) and the blend has
   almost nowhere to go. On this beige it reads as washed-out - which is the exact thing he has
   asked to remove three times now, so the weight wins over matching the reference here. */
.spec dt,
.spec dd { font-size: 14px; font-weight: 400; line-height: 21.7px; color: var(--ink); }
.spec dd { margin: 0; }

/* *** THE PRICE ROW: SAME COLUMNS AS THE FACTS, EQUAL AIR ABOVE AND BELOW. ***
   Vader, 2026-09-10: "price align from top and bottom plz". The figure is a row of the .spec
   grid now (see work.php), so its label and value inherit the same two columns as Series /
   Mediums / Dimensions and cannot fall out of line.
   *** 22px, NOT 26. *** The gap below the block is .spec's 26px margin-bottom. Above the price
   row the grid ALREADY contributes its 4px row-gap, and padding stacks on top of that - so 26
   here would have given 30 above against 26 below. 22 + 4 = 26. Measured, not guessed.
   If .spec's margin-bottom or its row-gap ever changes, this number changes with them.
   .is-money goes on the FIRST money row only; a second row (estimated value AND price) follows
   at the list's ordinary 4px rhythm, because the pair should read as one group. */
.spec dt.is-money,
.spec dd.is-money { padding-top: 22px; }
.spec dd strong { font-weight: 700; color: var(--ink); }

/* .spec-value is GONE - the price and estimated-value rows moved into the .spec grid on
   2026-09-10 and nothing renders that class any more. */

.work-note { font-size: 14px; font-weight: 400; line-height: 21.7px; color: var(--ink); }
/* The reference makes this link blue. Ours stays black - Vader's "no colored text anywhere"
   outranks matching the reference on that one detail. */
.work-note a { font-weight: 700; text-decoration: underline; text-underline-offset: 3px; }

/* ── SEE ALSO — CAROUSEL ─────────────────────────────────────────────────────────────────────
   Vader, 2026-09-10, pointing at ponarsher.com's slider: "yes yes.. four is good on carousel".

   CSS scroll-snap, not a carousel library. It swipes natively on a phone, keyboard arrows work
   because the rail is focusable, it degrades to a plain scrolling row with JS off, and there is
   no dependency to reinstall when this moves to shared hosting. The arrows just call scrollBy.

   ONE DEPARTURE FROM THE REFERENCE, and it is deliberate: theirs shows a single slide at a time,
   which suits their wide room-shots. These are PORTRAIT canvases - one slide across a 1265px
   page would stand roughly 1600px tall. So the rail shows 1 on a phone, 2 on a tablet and 4 on
   desktop, and pages by a full view. */
.also-wrap { position: relative; }

.also {
    display: flex; gap: 18px;
    overflow-x: auto; scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    -ms-overflow-style: none; scrollbar-width: none;   /* the arrows are the affordance */
    padding-bottom: 4px;
}
.also::-webkit-scrollbar { display: none; }

.also .card {
    flex: 0 0 calc((100% - 0px) / 1);
    width: auto; margin-inline: 0;
    scroll-snap-align: start;
}
@media (min-width: 620px)  { .also .card { flex-basis: calc((100% - 18px) / 2); } }
@media (min-width: 980px)  { .also .card { flex-basis: calc((100% - 54px) / 4); } }

.also .card-title { font-size: .82rem; margin-top: 12px; }

/* Arrows sit outside the rail on desktop and overlay it on a phone, where there is no room. */
.also-arrow {
    position: absolute; top: 38%; z-index: 3;
    width: 40px; height: 40px; border-radius: 50%;
    background: rgba(255,255,255,.92); border: 0; color: var(--ink);
    display: grid; place-items: center; cursor: pointer;
    box-shadow: 0 2px 12px rgba(0,0,0,.16);
    transition: background var(--d-fast) var(--ease), opacity var(--d-fast) var(--ease);
}
.also-arrow:hover { background: #fff; }
/* No [disabled] state: the rail loops, so there is no end to disable at. */
.also-prev { left: 4px; }
.also-next { right: 4px; }
@media (min-width: 1360px) { .also-prev { left: -26px; } .also-next { right: -26px; } }

/* ════════════════════════════════════════════════════════════════════════════════════════════
   11. FOOTER
   ════════════════════════════════════════════════════════════════════════════════════════════ */
/* *** THE FOOTER GROUND IS THE SAME GREEN AS THE HEADER BAND. ***
   Vader, 2026-09-10: "use this color on footer BG i wanna see", with the #26393d swatch - the
   same dark desaturated teal the .page-head gradient is built from. It was #000. Using the one
   value top and bottom is what makes the page read as bookended rather than as a black slab
   under a green one. If the two ever need to differ, change them together and say why.
   White type and the white GrandAxis mark both sit on this at 10.9:1, so nothing below needs
   adjusting. */
.foot { background: #26393d; color: var(--on-dark); padding: clamp(50px, 7vw, 84px) 0 34px; }
.foot h2 { color: var(--on-dark); text-align: center; font-size: clamp(1.4rem, 1rem + 1.6vw, 2.2rem); }
.foot a { color: #fff; }
.foot a:hover { color: var(--on-dark); }
.foot-rule { width: 64px; height: 1px; background: rgba(255,255,255,.34); margin: 22px auto 26px; }
.foot-social { display: flex; justify-content: center; gap: 16px; margin-bottom: 34px; }
/* *** NO BOX AROUND THE ICON. ***
   Vader, 2026-09-09: "this does not has to be inside a box, the logo in a box itself, no need for
   another border". The Instagram mark is a rounded square in its own right, so the outlined
   container drew a second frame around a frame. The link is now just the glyph; hover dims it,
   the same treatment every other link on the site uses. */
.foot-social a {
    display: grid; place-items: center;
    transition: opacity var(--d-fast) var(--ease);
}
.foot-social a:hover { opacity: .78; }
/* ONE rule for this. There were briefly two - 34px then 18px on the next line - and the later
   one silently won, so the icon rendered at 18px however the first was edited. Duplicated
   selectors at equal specificity are decided by order, and the loser leaves no trace. */
.foot-social svg { width: 34px; height: 34px; }
.foot-bar {
    border-top: 1px solid rgba(255,255,255,.16); padding-top: 24px;
    display: flex; flex-direction: column; align-items: center; gap: 16px; text-align: center;
    font-size: .78rem; color: #fff;
}
/* *** GRID, NOT space-between. ***
   Vader, 2026-09-09: "these 3 links are not center liagned". They were not, and space-between is
   why: it distributes the GAPS evenly, so the middle item only lands on the page centre when the
   two flanking items happen to be the same width. The copyright line is roughly three times the
   width of the credit beside it, so the nav was pushed right by half that difference.
   1fr auto 1fr puts the middle column on the true centre regardless of what flanks it. */
@media (min-width: 768px) {
    .foot-bar {
        display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
        text-align: left;
    }
    .foot-bar > :last-child { justify-self: end; }
}
.foot-nav { display: flex; gap: 22px; font-size: .7rem; letter-spacing: .18em; text-transform: uppercase; }

.skip { position: absolute; left: -9999px; top: 0; z-index: 300; background: var(--ink); color: var(--on-dark); padding: 12px 18px; border-radius: var(--radius); }
.skip:focus { left: 12px; top: 12px; }

/* ════════════════════════════════════════════════════════════════════════════════════════════
   12. WORK CARDS  —  the products layout Vader screenshotted: painting, title beneath, and a
   small round "more photo" badge for pieces that carry extra shots.
   The badge is the one intentional exception to the 8px-radius house rule: it is a sticker, not
   a control, and it is copied from the reference Murtaza signed off on.
   ════════════════════════════════════════════════════════════════════════════════════════════ */
.cards { display: grid; grid-template-columns: 1fr; gap: clamp(40px, 6vw, 76px) 28px; }
@media (min-width: 620px)  { .cards { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 980px) { .cards { grid-template-columns: repeat(3, 1fr); } }

/* *** 15% SMALLER, VIA THE WHOLE CARD, NOT JUST THE IMAGE. ***
   Vader, 2026-09-10: "reduce image box size by 15%". Shrinking only .card-media would have left
   the "more photo" badge behind - it is positioned against .card, not against the image, so it
   would have floated off the corner it is meant to sit on. Scaling the card itself keeps the
   badge, the image and the caption in the same relationship, and the grid columns are unchanged
   so the layout does not reflow - each card simply sits centred in a slightly roomier cell. */
.card {
    position: relative; text-align: center;
    /* 85% on the first pass, then "reduce image box size 10% more" (Vader, 2026-09-10).
       Each pass is 10% OF THE CURRENT SIZE, not 10 points off the number:
           100%  ->  85%     (first reduction, 15%)
            85%  ->  76.5%   (10% of 85)
          76.5%  ->  68.85%  (10% of 76.5)
       Rounding to 75% / 65% instead would have compounded into a visibly different result by the
       third pass. */
    width: 68.85%; margin-inline: auto;
}

/* The medium/size line and the rule that bottom-aligned it across a row were REMOVED with the
   markup on 2026-09-10 - the tile is name + description only now. Nothing here needs a stretched
   column any more, so `.cards > .card { display:flex }` went with it rather than being left
   behind doing nothing. */
/* *** THE IMAGE FILLS THE CARD. NO LETTERBOXING. ***
   Vader, 2026-09-10: "stretch all images to cover the box, no empty spaces from up down or left
   right". This was `contain`, which guaranteed no painting was ever cropped - but because the 19
   works range from 0.69 to 0.81 wide-to-tall while the card is 0.75, anything off that ratio left
   visible bands of ground colour above and below, and the grid read as ragged.

   `cover` is the trade he chose: every card is now flush, at the cost of a few percent cropped
   from whichever edge is long. The crop is SMALL precisely because 3/4 sits in the middle of the
   real range - a square or landscape card would have cut far more.
   Where the work is the subject rather than a thumbnail - the piece page stage - it is still
   `contain`, so the full canvas is always available one click away. */
.card-media {
    display: block; overflow: hidden; border-radius: var(--radius);
    background: var(--bg-3); aspect-ratio: 3 / 4;
}
.card-media img {
    width: 100%; height: 100%; object-fit: cover;
    transition: transform 1.1s var(--ease), opacity var(--d) var(--ease);
}
.card:hover .card-media img { transform: scale(1.06); opacity: .82; }
.card-title { font-size: 1rem; letter-spacing: .1em; text-transform: uppercase; margin: 18px 0 4px; }

/* Sana's line for the piece, under the title. Sentence case and a normal weight so it reads
   as her writing rather than as another label — the title above it is the uppercase element,
   and two uppercase blocks in a row would flatten the tile.
   max-width is in ch, not px: it caps the MEASURE (characters per line, ~46) so the text
   stays readable at any tile width instead of stretching into a single long line on desktop. */
.card-desc  { font-size: .84rem; line-height: 1.55; color: var(--ink); margin: 0 0 7px;
              max-width: 46ch; }


/* ── MY WORK = MANGO'S LISTING PAGE (sana3, 2026-09-11) ───────────────────────────────────────
   Vader sent Mango's "New for men" page: desktop and phone, in each of its three views. Measured off
   shop.mango.com at 1440 and 414:
       row 1        their NEW FOR MEN, ours MY WORK + chevron = a dropdown of the four pages:
                    16px / 24px / 600 / uppercase, 24px under the bar and 32px in (phone: 16 and 16)
       row 2        their FILTER AND ORDER, ours the SERIES NAME (the h1): 13px / 20px / 600 / uppercase,
                    16px under row 1, 28px tall, with the three 20px view icons at its right end -
                    24px targets 12px apart on desktop, 34px targets touching on a phone; the chosen
                    one underlined 1px, 4px below the icon
                    (Vader, 2026-09-11: "where it says NEW FOR MEN, we will have MY WORK V / where it
                    says FILTER AND ORDER, we will have Series name")
       grid         the white strip ends with the same inset it starts with; paintings 16px below it
       text         8px below the image and 8px in: a 9px / 12px uppercase tag (theirs NEW NOW, ours the
                    series), then the name at 13px / 20px / 400
   *** THE IMAGES ARE SANA'S, NOT MANGO'S. *** Vader: "YOU WILL NOT CHANGE THE IMAGE SIZE, KEEP THEM AS
   IS WE HAVE ON SANA". The tiles keep their 3:4 frame, the 68.85% card and the .wrap they sit in, and
   with no choice made each width shows exactly the grid it always did (1 / 2 / 3 across). The switch
   only changes how many sit in a row. No FILTER AND ORDER - "there will no filter now". */
.section.listing { padding-top: 0; }
/* WHITE, like the bar above it - Vader, 2026-09-11: "this part should be white too since its navigation".
   Header and these two rows read as one white navigation block; the paintings start 16px below on the
   beige. The strip ends with the same inset it starts with. */
.list-head { padding: 16px; margin-bottom: var(--gap-content); background: #fff; }   /* series + piece pages */
@media (min-width: 980px) { .list-head { padding: 24px 32px; } }

/* Row 1 - MY WORK + chevron and its dropdown - was REMOVED 2026-09-11 (Vader: "remove MY WORK V, just says
   VEIL SERIES"); its rules went with it. The strip is the one row below. */

/* The strip's one row: the series name (or SERIES > TITLE on a piece) left, the view icons right. */
/* min-height, not height: a long piece title (SERIES - TITLE on a piece page) may wrap on a phone. */
.list-bar { display: flex; align-items: center; justify-content: space-between; gap: 16px; min-height: 28px; }
.list-series {
    margin: 0; font-size: 13px; line-height: 20px; font-weight: 600; letter-spacing: normal;
    text-transform: uppercase; color: #131313;
}
/* On a piece page row 2 is SERIES - TITLE, and the series links back to its page. Colour only on hover. */
.list-series a { color: inherit; transition: color .3s cubic-bezier(.3, .85, .5, 1); }
.list-series a:hover { color: #5b5b5b; }
/* VEIL > SILENCE SITS - the same 12px line chevron as MENU and MY WORK, pointing right, with room either side. */
.crumb-sep { display: inline-block; width: 12px; height: 12px; margin: 0 10px; vertical-align: -1px; }
/* The piece page: the painting starts 16px under the strip, as the grid does on the listing pages. */
.list-head + .work-hero { padding-top: 0; }

.view-switch { display: flex; align-items: center; }
.view-switch[hidden] { display: none; }            /* JS reveals it - without JS there is nothing to switch */
/* margin:0 - the site's form rule gives every <label> a 7px bottom margin, which lifted the icons 3.5px. */
.view-item { position: relative; display: grid; place-items: center; width: 34px; height: 34px; margin: 0; color: #121212; cursor: pointer; }
.view-item input { position: absolute; width: 1px; height: 1px; opacity: 0; margin: 0; }   /* still focusable */
.view-icon { position: relative; display: block; width: 20px; height: 20px; }
.view-icon svg { width: 20px; height: 20px; display: block; }
.view-icon::after { content: ''; position: absolute; left: 0; right: 0; bottom: -4px; height: 1px; background: transparent; }
.view-item input:checked ~ .view-icon::after { background: #2a2a2a; }
.view-item input:focus-visible ~ .view-icon { outline: 1px solid #121212; outline-offset: 3px; }
@media (min-width: 980px) {
    .view-switch { gap: 12px; }
    .view-item { width: 24px; height: 24px; }
}
.sr-only {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* The three views. [data-view] only appears once someone chooses; until then the .cards rules above
   (1 / 2 / 3 by width) are the layout, untouched. */
.cards[data-view="1"] { grid-template-columns: 1fr; }
.cards[data-view="2"] { grid-template-columns: repeat(2, 1fr); }
.cards[data-view="3"] { grid-template-columns: repeat(3, 1fr); }
/* Desktop, one across: ONE painting at exactly the size it has in the two-across view - Vader, 2026-09-11:
   "this size for single image on desktop i want.. currently single image on desktop is too big" (it was
   Mango's 41vw - 590px at 1440, more on a wide monitor). A two-across card is 68.85% of half the grid less
   its 28px column gap; one across uses that same width on its one column, centred - 416px at 1440, and equal
   to two-across at every width. (The 28px is .cards' column gap - change one, change both.) */
@media (min-width: 980px) { .cards[data-view="1"] .card { width: calc((100% - 28px) / 2 * .6885); } }
/* PHONES: every card takes its whole column. One across is ONE IMAGE the width of the content column -
   Vader, 2026-09-11: "when clicking on 1 grid it should show 1 image on mobile" (the 68.85% tile looked
   like a thumbnail, and tapping 1 changed nothing because the phone already showed it). In two or three
   across a 68.85% card would be a thumbnail too, and in three across the text goes - Mango's phone grid
   is images only. Tablet and desktop keep Sana's 68.85% tiles. */
@media (max-width: 619px) {
    .listing .card { width: 100%; }
    /* ONE ACROSS ON A PHONE: one painting the full width of the content column, the next following straight
       after - Vader, 2026-09-11: "1 grid should show 1 image on mobile.. that created a space below the image,
       bring it close up again, its ok if its showing the second image at bottom". The one-piece-per-screen
       min-height that made that space is GONE.
       *** THE WHOLE FIRST PIECE STILL FITS THE FIRST SCREEN, CAPTION INCLUDED. *** Vader on an iPhone XR:
       "i cant read the description, its below the display area" - Safari's bars take ~180px of its 896. So
       the card is no wider than the height left over allows, keeping the painting 3:4:
           (screen - bar - 66px strip - the content gap - 116px caption) x 3/4
       66px = the white strip on a phone (16 + the 34px view targets + 16); the gap under it is --gap-content
       (32px on a phone), so this follows it if it ever changes. 116px = tag 20 +
       name 20 + three lines of description 60 + 8 above + 8 spare (The Gathering of Strangers' first
       paragraph is three lines). Tall phones get the full column width. svh = the screen height with the
       address bar showing. */
    .listing .cards:not([data-view="2"]):not([data-view="3"]) .card {
        width: min(100%, calc((100svh - var(--head-h) - 66px - var(--gap-content) - 116px) * .75));
    }
    .cards[data-view="2"], .cards[data-view="3"] { gap: 24px 8px; }
    .cards[data-view="3"] .card-info { display: none; }
}

/* ── THE OVERVIEW (works.php, 2026-09-11) - one SERIES tile per column. Vader: "i need just 2 images 1 from
   Veil and 1 from Stimulation", then "top center of image VEIL SERIES / bottom center of image VIEW ALL" -
   outside the painting, on the page - and "no need for image name and its description".
   One row of two columns at every width. From 620px each column's content is Sana's 68.85% tile width,
   centred, so the painting is exactly the size it was; on a phone it takes the whole column. The name and
   VIEW ALL are centred on the painting, 16px above and below it - Mango's row rhythm. The painting gets the
   grid tiles' hover (zoom + lift); it and VIEW ALL both open the series page. */
/* EQUAL AIR ABOVE AND BELOW THE TILES - Vader, 2026-09-11: "fix the spacing", arrows on bar -> tiles and on
   tiles -> WRITE A MESSAGE. It was 24px above and ~120px below (this section's bottom padding PLUS the contact
   block's own top padding). Now one value on both sides: the section pads top and bottom by it, and on this
   page the contact block below drops its top padding, so VIEW ALL -> the button equals the bar -> the series
   names. 32px on a phone, 64px at 1440. (There is no white strip here - "now we dont need this".) */
.section.listing.overview { padding-top: var(--gap-content); padding-bottom: var(--gap-content); }
/* (The contact block below now has its own darker band and padding - see .contact-call.) */
.ov-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 28px; }
@media (min-width: 620px) { .ov-col { width: 68.85%; margin-inline: auto; } }
@media (max-width: 619px) { .ov-grid { gap: 8px; } }
.ov-col { text-align: center; }
.ov-name {
    margin: 0 0 16px; font-size: 13px; line-height: 20px; font-weight: 600; letter-spacing: normal;
    text-transform: uppercase; color: #131313;
}
.ov-col .card-media:hover { opacity: 1; }                                   /* no link fade - the image zooms instead */
.ov-col:hover .card-media img { transform: scale(1.06); opacity: .82; }     /* the grid tile's hover */
.ov-more {
    /* a BLOCK at its own width, centred - as an inline-block it sat on a text line whose descender space added
       4px under it, so the gap below the tiles came out 4px bigger than the gap above them */
    display: block; width: -moz-fit-content; width: fit-content; margin: 16px auto 0;
    font-size: 12px; line-height: 16px; font-weight: 600; text-transform: uppercase; color: #131313;
    transition: color .3s cubic-bezier(.3, .85, .5, 1);
}
.ov-more:hover { color: #5b5b5b; opacity: 1; }   /* colour only - no fade, no underline */

/* The text under each piece, Mango's: left-aligned, 8px in, 8px below the image. */
.listing .card { text-align: left; }
.listing .card-info { padding: 8px 8px 0; }
.listing .card-tag {
    margin: 3px 0 5px; font-size: 9px; line-height: 12px; font-weight: 400;
    text-transform: uppercase; color: #121212;
}
/* BOLD - Vader, 2026-09-11: "make the names BOLD so they standout". 600, the weight MY WORK and the
   series name carry, so the name reads as the tile's title and the description as its line. */
.listing .card-title {
    margin: 0; font-size: 13px; line-height: 20px; font-weight: 600; letter-spacing: normal;
    text-transform: none; color: #121212;
}
.listing .card-desc { margin: 0; font-size: 13px; line-height: 20px; color: #121212; max-width: none; }

/* The "more photo" badge was removed 2026-09-10 at Vader's request ("no need"). Its rules
   are deleted rather than left orphaned - dead CSS is how a class quietly comes back. */

/* ════════════════════════════════════════════════════════════════════════════════════════════
   13. WORK MODAL  —  full-screen, X top-right, "back to more" top-left.
   ════════════════════════════════════════════════════════════════════════════════════════════ */
.work-modal {
    position: fixed; inset: 0; z-index: 170;
    background: var(--bg); overflow-y: auto;
    opacity: 0; transition: opacity var(--d) var(--ease);
}
.work-modal[hidden] { display: none !important; }
.work-modal.is-open { opacity: 1; }
.work-modal-bar {
    position: sticky; top: 0; z-index: 2;
    display: flex; align-items: center; justify-content: space-between;
    padding: 16px clamp(16px, 3vw, 34px);
    background: var(--bg); border-bottom: 1px solid var(--rule);
}
.wm-back, .wm-close {
    background: none; border: 0; color: var(--ink); cursor: pointer; font: inherit;
    padding: 8px 10px; border-radius: var(--radius);
    transition: background var(--d-fast) var(--ease), color var(--d-fast) var(--ease);
}
.wm-back:hover, .wm-close:hover { background: var(--bg-3); }
.work-modal-body { padding: clamp(24px, 4vw, 54px) 22px 70px; }
body.wm-open { overflow: hidden; }

/* ── "Powered by <logo>" footer credit ───────────────────────────────────────────────────────
   Vader, 2026-09-09: "Powered by 'logo'" — and then, fairly: "how many times we have been
   throught this pratice? you even saved it in axisbrain". He is right; the rule was already
   written down from ScrapWala (2026-09-03) and I shipped plain text anyway. Recalled and applied
   verbatim rather than re-derived.

   *** HEIGHT IS MEASURED, NOT EYEBALLED. ***
   The GRANDAXIS wordmark occupies only ~36% of the PNG's box height (the ring around the X
   overshoots the letters top and bottom). At height:18px the caps render 6.5px against 8.8px for
   the words beside them, so the logo reads SMALLER than the text it sits next to. 25px puts the
   caps at ~9px, level with it.
   MATCH CAP HEIGHT TO CAP HEIGHT. Never match the image box to the font size.

   Scoped to .foot deliberately: a bare .powered loses to any broader footer anchor rule on
   specificity, and when that happened on ScrapWala the words stacked above the logo. */
.foot .powered {
    display: inline-flex; align-items: center; gap: 9px;
    white-space: nowrap; padding: 0;
}
.foot .powered span { font-size: 12.5px; line-height: 1; }
.foot .powered img {
    height: 25px; width: auto; display: block; opacity: .85;
    transition: opacity var(--d-fast) var(--ease);
}
.foot .powered:hover img { opacity: 1; }

/* *** THE OLDER .spec-value BLOCK WAS DELETED FROM HERE. ***
   It defined the same selectors as the rules further up - flex row, a bordered underline, an
   uppercase letterspaced label and a 1.02rem/600 figure - and being LATER in the file it won on
   order, so the reference-matched 14px/700 above never applied. That is the third duplicate-
   selector bug in this stylesheet today; the value you SET is not the value that renders, and
   only the rendered one counts. The definition that survives is the measured one, up at
   .spec-value / .spec-value strong. */

/* ── PLAIN BASE ──────────────────────────────────────────────────────────────────────────────
   Client feedback, 2026-09-09: "Again so colorful" / "the painting u have put in background are
   too bright" / Asher: "Plain base. Then we add images."
   The home page's story section is now plain ground — no painting behind the copy at all. Where
   a full-bleed panel IS still used (the About page), [data-quiet] pushes a much heavier scrim
   over it so the canvas reads as atmosphere rather than as a competing picture. */
.story-plain { background: var(--bg); }

.panel-media[data-quiet]::after {
    background: linear-gradient(180deg, rgba(0,0,0,.72) 0%, rgba(0,0,0,.62) 50%, rgba(0,0,0,.74) 100%);
}

/* ── ARTIST PORTRAIT ─────────────────────────────────────────────────────────────────────────
   "There is no sana picture" (client, 2026-09-09). Portrait beside the copy, on the plain
   ground — not behind text, which is the treatment they rejected. */
.story-split { display: grid; gap: 30px; align-items: center; }

/* *** THE PORTRAIT COLUMN IS 20% NARROWER, WHICH IS HOW ITS HEIGHT COMES DOWN. ***
   Vader, 2026-09-10: "make the image 20% smaller its too tall". sana.jpg is 809x1400, so its
   height is set entirely by how wide its column is - shrinking the COLUMN is what shortens the
   picture, and the image keeps filling it with no gap beside it.
       0.85fr -> 0.68fr   (0.85 x 0.8, i.e. 20% off the current width, not 20 points)
   The freed space goes to the copy, which now carries the whole bio and wants it. */
@media (min-width: 900px) { .story-split { grid-template-columns: 0.68fr 1fr; gap: 56px; } }

.story-portrait img {
    width: 100%; height: auto; display: block;
    border-radius: var(--radius);
}

/* The bio moved in here on 2026-09-10 ("bring the about text up next to the picture"), so this
   column now holds four paragraphs rather than one line. Paragraphs need their own rhythm - the
   section's default spacing was tuned for a lede, not for running copy. */
.story-copy { text-align: left; }
.story-copy p + p { margin-top: 14px; }

/* ── THE ARTIST BAND ─────────────────────────────────────────────────────────────────────────
   Full-bleed portrait with a single outline control over it, per the reference. The scrim is
   mild — this is a photograph, not one of the high-chroma canvases the client asked to calm
   down, and the only thing that has to hold on top of it is one button. */
/* *** 560px - MEASURED OFF THE REFERENCE. ***
   Vader, 2026-09-09: "match MY STORY image height". Read off ponarsher.com at a 414px viewport:
   their hero is 896 (full screen) and the MY STORY band below it is 414 x 560. Mine was 78svh
   (~699), noticeably taller, which is why the two pages did not line up down the scroll. */
/* 560px matches ponarsher.com's band at 414px. Above that the band grows, because the source is
   a PORTRAIT photograph (809 x 1400) in a landscape box: at 1265 wide, cover scales it by width
   and shows only ~26% of its height, which is how Sana ended up cropped to a sliver.
   Vader: "is it possible to zoom out a little on the MY STORY image of sana, so she is showing
   more?" - a taller band on wide screens is the honest way to do that. Letting the image scale
   down instead (contain) would letterbox a full-bleed section, and cropping the source tighter
   would show LESS of her, not more. */
/* *** PINNED REVEAL - THE IMAGE IS STATIC, THE SECTION IS A WINDOW OVER IT. ***
   Vader, 2026-09-09: "on Pon website my story has a reveal effect, where the image is static
   behind". Exactly right, and it is a different effect from parallax: parallax moves the image
   SLOWER than the page; this pins it to the viewport entirely, so the section slides over a
   stationary photograph and uncovers it.

   How it works: clip-path on this section makes it the containing block for position:fixed
   descendants, so .story-hero-media is fixed to the VIEWPORT but clipped to THIS band. Nothing
   else in the section can be transformed, or that ancestor would capture the fixed child instead
   - which is why the parallax attribute was removed from the markup.

   clip-path rather than background-attachment:fixed, which iOS Safari has never supported
   properly and would have made this the one effect that dies on a phone. */
.story-hero {
    position: relative; height: 560px; overflow: hidden;
    clip-path: inset(0);
}
@media (min-width: 980px)  { .story-hero { height: 680px; } }
@media (min-width: 1280px) { .story-hero { height: 760px; } }
/* Fixed to the viewport, clipped by the section above. inset:0 = exactly the viewport, so the
   photograph never drifts and never exposes an edge. */
.story-hero-media { position: fixed; inset: 0; z-index: 0; }
/* *** THE CROP IS ANCHORED ON SANA, NOT ON THE CENTRE OF THE FRAME. ***
   Vader: "make sure sana is in picture". Measured on the source (809 x 1400): she sits at about
   79% across - right of centre - and her figure occupies the middle-to-lower band.
   A default 50% 50% crop is safe on a phone, where the band is exactly the image width and
   nothing is cut horizontally. On a wide screen it is not: cover then scales by WIDTH, throws
   away most of the height, and takes its slice from the middle of the frame - which is the
   studio wall beside her, not her. 68% 40% keeps her in shot at every width. */
/* 68% across keeps her in shot - measured on the source, she sits at ~79% of the frame width.
   *** THE VERTICAL VALUE IS COUNTER-INTUITIVE. *** Vader: "bring her up a little". To move the
   SUBJECT up in the frame you move the crop window DOWN the source, which means a LARGER
   percentage, not a smaller one.
   Bracketed by eye across two passes rather than guessed: 32% put her head on the BOTTOM edge,
   58% cut her head off at the TOP. 45% is the midpoint of a known-good bracket, which is a more
   reliable way to land this than nudging in one direction and hoping. */
/* *** RE-MEASURED 2026-09-10 FOR story-sana.jpg. ***
   The image behind MY STORY changed from the 809x1400 portrait to a 1448x1086 LANDSCAPE, and the
   old 68% 45% went with the old file - she sat at ~79% across in that one and sits at ~37% in
   this one, so keeping the old anchor would have cropped her off the left edge on a phone.
       horizontal 34%  - on mobile the frame is much narrower than the image, so cover crops
                         ~650px of width; 34% puts the window at 222-636 against her figure at
                         221-559, i.e. she is fully inside rather than half cut.
       vertical   42%  - on desktop cover crops only ~180px of height, so this barely moves; 42%
                         keeps her head clear of the top edge.
   Larger vertical % moves the SUBJECT UP, which is counter-intuitive and cost two passes to
   learn on the last photograph: you move the crop window DOWN the source to raise the subject. */
.story-hero-media img {
    width: 100%; height: 100%; object-fit: cover; object-position: 34% 42%;
}

/* *** MOBILE SEES THE PHOTOGRAPH 25% SMALLER. ***
   Vader, 2026-09-10: "only on mobile view can you zoom out this picture a bit? like 25%?".
   The image is landscape (1448x1086, ratio 1.33) and a phone panel is tall and narrow (~0.52),
   so `cover` scales it to fill the HEIGHT - at 414x800 it renders about 1066 wide and throws
   away 650px of it. She ends up magnified and tightly cropped.

   Shrinking the IMG BOX to 75% of the panel height is what zooms out: cover then fits the image
   to 600px tall instead of 800, i.e. it renders 800 wide instead of 1066 - 25% smaller - and the
   414px window now shows 52% of the frame's width rather than 39%.

   *** A transform: scale(.75) here would NOT have worked *** and it is the obvious wrong answer:
   it shrinks the already-cropped result, so you see the SAME crop, just smaller, with gaps on all
   four sides. Resizing the box before the fit is calculated is what actually reveals more image.

   *** THE OFFSET IS `top`, NOT `margin-top`, AND THE DIFFERENCE IS NOT COSMETIC. ***
   A PERCENTAGE MARGIN RESOLVES AGAINST THE CONTAINING BLOCK'S WIDTH - even margin-top. On a
   414x800 panel `margin-top: 12.5%` is 12.5% of 414 = 52px, so the picture sat 52px down with a
   148px band beneath it: visibly off-centre. Measured, not guessed.
   A percentage `top` on a relatively positioned box DOES resolve against the container's height,
   so 12.5% of 800 = 100px and the two bands match.

   Those bands are black (see the background above) so they read as letterboxing rather than as a
   hole, and the ::after veil covers them too, so they sit in the same darkness as the photo. */
@media (max-width: 899px) {
    .story-hero-media { background: #000; }
    .story-hero-media img { height: 75%; position: relative; top: 12.5%; }
}
/* *** THE OVERLAY IS EVEN AND STRONG, NOT A THIN TOP-AND-TAIL. ***
   Vader, 2026-09-09: "pon image has overlay, give overlay on sana's picture too". Mine was a
   gradient that dropped to .16 through the middle - effectively no overlay exactly where the
   button sits. The reference darkens the WHOLE frame to roughly half, evenly, which is what
   makes a white hairline button read cleanly on top of a busy studio photograph.
   Held at .52 across the centre with a little more at the edges, so it reads as one deliberate
   veil rather than a gradient. */
/* DEEPENED 2026-09-10: "increase overlay on sana picture little bit". One step up across every
   stop, keeping the flat-through-the-middle shape that makes the outline button read cleanly.
       was  .60 / .52 / .52 / .62
       now  .70 / .62 / .62 / .72 */
.story-hero-media::after {
    content: ''; position: absolute; inset: 0;
    background:
      linear-gradient(180deg, rgba(0,0,0,.70) 0%, rgba(0,0,0,.62) 30%,
                              rgba(0,0,0,.62) 70%, rgba(0,0,0,.72) 100%);
}
.story-hero-inner {
    position: relative; z-index: 2; height: 100%;
    display: flex; align-items: center; justify-content: center; padding: 0 22px;
}

/* The reference's control: a wide hairline rectangle, white on the photograph. */
.btn-outline {
    background: none; border: 1px solid rgba(255,255,255,.9); color: #fff;
    min-width: min(78vw, 340px);
}
.btn-outline:hover { background: rgba(255,255,255,.14); border-color: #fff; color: #fff; }

/* ── CONTACT CALL ────────────────────────────────────────────────────────────────────────────
   The enquiry button and address, on the page ground above the dark footer - the arrangement the
   reference uses. The address sits between two hairlines, which is what gives it the weight of a
   printed detail rather than another line of body copy. */
/* TIGHTENED 2026-09-10 ("reduce this extra space"): this used to stack on the preceding
   section's 140px. Since 2026-09-11 the ALL WORKS section above it is gone entirely, so on the
   home page this 70px is the whole gap between the grid and the button. */
.contact-call { background: var(--bg); padding: clamp(44px, 5vw, 70px) 0 clamp(50px, 7vw, 84px); }
/* *** WRITE A MESSAGE IS ITS OWN BAND NOW, ONE STEP DARKER. *** Vader, 2026-09-11: "maybe give this section a
   little bit darker biege, so it makes sense like a CTA?" and "eualizie this spacing on all pages". Measured
   before: 84px under the button everywhere, but 64 / 70 / 210 / 218px above it depending on the page - the
   last section's own bottom padding stacked on this block's top padding.
   Now: the band is --bg-3 (the palette's one step below the ground, already used for wells and inputs - --bg-2
   stays identical to the ground, so nothing else changes colour), padded by --gap-content above AND below, so
   the button sits dead centre in it; and a page that ends on CONTENT ends --gap-content above the band (the
   same air as under the header). Home and About end on a full-bleed picture (the tiles; the pinned panel), and
   the band meets those edge to edge - a strip of plain ground between a picture and a band would read as a
   stray stripe. */
.contact-call { background: var(--bg-3); padding: var(--gap-content) 0; }
main > section.section:last-of-type { padding-bottom: var(--gap-content); }
/* .contact-mail was deleted 2026-09-11 with its markup - see inc/footer.php. */

/* The green inner-page band (.page-head) was REMOVED 2026-09-11 - Vader: "remove this green gradient from sana3". */

/* ── A 20% WHITE WASH OVER EVERY PAINTING ────────────────────────────────────────────────────
   Vader, 2026-09-10: "a white overlay reduced all the way down to 20% visible", then halved to 10%. Colours
   read duller. This runs the whole thread of client feedback about the site being too vivid, but
   applied to the ARTWORK rather than to the page: the paintings themselves are the saturated
   thing, and knocking 20% of white over them settles them into the beige.

   A real overlay, not a filter. `filter: brightness()` multiplies (0.8c) where a white wash is a
   blend towards white (0.8c + 51) - they diverge badly in the shadows, which on these canvases is
   most of the picture. CSS filters have no additive term, so a painted layer is the only way to
   get the actual effect.

   Applied on the CONTAINERS, above the image and below any caption:
     .card-media   works grid          -> ::after  (needs position:relative, set here)
     .tile         home + see-also     -> ::before (its ::after is already the hover scrim)
     .work-stage   piece page main     -> ::after
     .work-thumbs  piece page strip    -> ::after
   The artist photo and the hero are deliberately NOT included; both already carry their own
   treatment and stacking a second wash on them would grey them out. */
.card-media { position: relative; }
/* *** NOT ON THE PIECE PAGE. ***
   Vader, 2026-09-10: "on single image page no need for white wash". Correct - the wash exists to
   settle the grid's colours into the beige when nineteen paintings are seen at once. On a piece
   page there is one painting and it IS the subject; washing it there would be showing the work
   dimmer than it is to someone who came to look at exactly that canvas.
   So .work-stage and .work-thumbs are excluded; the grid and the tiles keep it. */
.card-media::after,
.tile::before {
    content: ''; position: absolute; inset: 0;
    background: rgba(255, 255, 255, .10);   /* was .20 - halved on Vader's call, 2026-09-10 */
    pointer-events: none;
    z-index: 1;
}
/* The hover scrim and the caption must still sit above the wash. */
.tile::after   { z-index: 2; }
.tile-cap      { z-index: 3; }

/* ── PRESS-AND-HOLD SEND ─────────────────────────────────────────────────────────────────────
   The fill sweeps left-to-right across the button over the hold duration, so the wait is visible
   and the button does not look broken while nothing appears to happen. */
.hold-hint {
    font-size: 12px; font-weight: 400; color: var(--ink);
    margin: 0 0 8px; text-align: center;
}
.btn-hold { position: relative; overflow: hidden; }
.btn-hold-label { position: relative; z-index: 2; }
.btn-hold-fill {
    position: absolute; inset: 0; z-index: 1;
    background: rgba(255,255,255,.30);
    transform: scaleX(0); transform-origin: left center;
}
/* The duration is set from the button's data-hold in JS, so the two can never disagree. */
.btn-hold.is-holding .btn-hold-fill { transform: scaleX(1); }
.btn-hold.is-armed  .btn-hold-fill  { transform: scaleX(1); transition: none; }

