/* Guester Cinema, the hero photograph.
 *
 * The homepage hero renders over a photograph. Loaded only on the front
 * page, by novamira-sandbox/hero-photo.php, which also puts the `hp` class
 * on <body> — so every rule here is inert everywhere else.
 *
 * This began as hero-preview.css, the stylesheet behind /preview-1..3/,
 * where the treatment was judged by eye against a control panel. The
 * settings were chosen and are now declared once, at the top of this file.
 * The panel, the light reading and the per-preview plumbing are gone; the
 * material itself is unchanged, which is the point — what shipped is what
 * was judged.
 *
 * hero.php cannot be edited (theme PHP is not adapter-writable), so the
 * photograph arrives without touching the hero at all. Two facts make that
 * clean: the hero's ground is the single utility .bg-background, which
 * compiles to background-color: var(--background); and room tokens are
 * declared on the class rather than :root. So re-pointing --background to
 * transparent opens the section up through the token layer, and the dark
 * treatment is the design's own .room-editorial token set rather than an
 * invented palette.
 *
 * The room CLASS itself is swapped server-side, in hero-photo.php, rather
 * than from script. Room tokens are declared on the class, and liquid-glass.css
 * still keys the satellites' ring off .room-paper, so the class has to change
 * and not just the tokens — and doing it in PHP means there is no frame in
 * which a paper-room hero paints over a dark photograph.
 */

/* ------------------------------------------------------------- settings */

/* Chosen on /preview-1/ at
   ?mode=dark&veil=0&focal=0&glass=0.1&bend=-120&chroma=14&band=0.07
   and frozen here. bend, chroma and band are geometry rather than colour,
   so they live in hero-photo.js with the code that draws the displacement.

   veil 0 is a deliberate choice and it is worth recording what it costs.
   Measured against this photograph, in the band where the hero copy lands:
   the median contrast is 8-12:1, but the brightest pixels — the chandelier
   and the window behind the headline — leave about 4-6% of the headline's
   band under 3:1 and about 16% of the sub-headline's band under 4.5:1. A
   global veil is a poor instrument against it: even 40% only takes that 16%
   down to 4-6%, at the cost of the photograph entirely. This is recorded
   rather than corrected, at the site owner's decision. */
body.hp {
  --hp-veil: 0;
  --hp-focal: 0%;
  --hp-glass: 0.1;
  --hp-veil-color: #0a0908;
}

/* --------------------------------------------------------------- layers */

/* The photograph sits fixed, not inside the section. The desktop hero is a
   240vh runway with a sticky inner stage; a layer scoped to the section
   would scroll away while the composition stayed pinned. Fixed holds the
   photograph still while the hero scrubs over it, and one rule covers both
   of the hero's subtrees — the 3D one at lg with motion allowed, and the
   flat one everywhere else. */
body.hp::before {
  background-image: var(--hp-photo, none);
  background-position: 50% var(--hp-focal, 0%);
  background-repeat: no-repeat;
  background-size: cover;
  content: "";
  inset: 0;
  position: fixed;
  z-index: -2;
}

/* The veil. At the chosen setting it is fully transparent and paints
   nothing; it is kept because the reduced-transparency branch at the foot
   of this file drives it, and because it is the one dial that can push the
   photograph back without touching anything else. */
body.hp::after {
  background: var(--hp-veil-color, #0a0908);
  content: "";
  inset: 0;
  opacity: var(--hp-veil, 0);
  position: fixed;
  z-index: -1;
}

/* Nothing between the fixed layers and the hero may paint an opaque ground.
   Scoped to body and #main only: every section on the homepage carries
   .bg-background and its own room, so each one paints its own opaque ground
   over these layers and the photograph cannot leak below the hero. */
body.hp,
body.hp #main {
  background: transparent;
}

/* The tokens that uncover the photograph. --background is the ground itself.
   The two orbs matter just as much: the paper room's --orb-2 is a 55%-alpha
   white wash 46vw across, which fogs a photograph badly, and --orb-1 is a
   slate pool doing the same job more gently. The ivory room already sets
   both to none for the same reason. */
body.hp #top {
  --background: transparent;
  --orb-1: none;
  --orb-2: none;
}

/* ----------------------------------------------------- the dark reading */

/* The design system's own editorial room, applied to the hero. hero-photo.php
   swaps the class, so the design system, liquid-glass.css and the nav
   observer all agree; these token writes are what the class cannot carry.
   Two are deliberately withheld: --background stays transparent so the
   photograph shows, and --font-display-face is held below. */
body.hp #top {
  --background: transparent;
  /* The editorial room sets --muted-foreground to #f3ede2a3, 64% ink, which
     is tuned for a flat ground. Over a photograph the hero's sub-headline
     lands at 3.1:1 against a blown gold highlight, under the 4.5:1 body
     minimum. Raised to 92% ink. Same colour, more of it. */
  --muted-foreground: #f3ede2eb;
}

/* The floating nav reads data-nav-theme from the section beneath it.
   hero-photo.php rewrites that attribute to "editorial" along with the room
   class, so the observer does the right thing on its own; this block holds
   the pill correct in the frames before the observer has run.

   Scoped to a header that carries NO room class yet — and that scope is
   load-bearing. Custom properties written on the pill itself outrank
   anything a room class on the HEADER (the pill's parent) can say: that is
   inheritance, not specificity, so an unconditional version of this block
   pinned the whole pill — logo ink, link colors, CTA tokens, the glass
   material — to editorial across every light section of the homepage, and
   the logo never flipped to the light rooms' ink. The :not() chain releases
   the pin the moment main.js assigns any room class; until then the
   classless header resolves to these editorial values, which is correct
   over the photograph. Over the night sections the header is also classless
   so this block stays live there — editorial and night are both dark,
   white-ink rooms and the difference is imperceptible; recorded rather than
   fought. Editorial glass, verbatim. */
body.hp [data-fx="nav"]:not(.room-paper):not(.room-ivory):not(.room-editorial) [data-fx="nav-pill"] {
  --foreground: #f3ede2;
  --muted-foreground: #f3ede2a3;
  --border: #f3ede224;
  --brand-lime: #f4d24a;
  --brand-ink: #171307;
  --glass-chrome-bg: linear-gradient(135deg, #ffffff14, #ffffff05);
  --glass-chrome-fill: #0a090899;
  --glass-chrome-border: #f3ede21f;
  --glass-chrome-inner: #ffffff0f;
  --glass-chrome-top: #ffffff14;
  --glass-chrome-shadow: 0 20px 60px #00000080;
  --glass-spec: transparent;
}

/* The hero's spotlight is a slate wash written as an inline style attribute
   and tuned for a flat paper ground; over a photograph it only muddies the
   middle. It has to be killed on `background`, not `opacity`: main.js writes
   opacity inline on this element every scrub frame, so an opacity rule here
   would simply lose. It never writes background, so !important is stable. */
body.hp [data-fx="hero-spotlight"] {
  background: none !important;
}

/* The editorial room brings the editorial serif with it. The hero's headline
   is the brand sans on the homepage and the photograph is meant to be the
   only change, so hold the display face. */
body.hp #top {
  --font-display-face: var(--font-body-face);
}

/* ------------------------------------------------- the satellite cards */

/* The hero's floating cards are real liquid glass: a clear pane with the
   backdrop bending at its rim, dressed with the upstream demo's own material.

   TWO SETS, because hero.php ships two heroes and only one of them is on the
   page at a time:

     [data-fx="hero-sat"] > div   the four 3D cards, `hidden motion-safe:lg:block`
     .hp-flat-sat                 the flat hero's two corner crops, tagged by
                                  hero-photo.js, shown below lg and under
                                  reduced motion at any width

   Neither needs a media query. The subtree that is not in play is
   display:none, so its rules are inert, and the JS skips any card whose
   offsetWidth reads 0.

   WHY NOT backdrop-filter. This is measured, not reasoned. The 3D cards sit
   inside the hero's 3D rendering context: [perspective:1400px] over
   [transform-style:preserve-3d], with main.js writing
   translate3d/rotateY/rotateZ on every card on every frame. In Chromium a
   backdrop-filter anywhere inside a preserve-3d subtree resolves against a
   backdrop root that holds nothing, and the whole declaration becomes a
   no-op — plain blur() included. Reproduced in a headless Chromium against
   this exact ancestor chain: with preserve-3d, backdrop-filter: blur(16px)
   saturate(1.5) on the card changes not one pixel; drop preserve-3d alone
   and it blurs. That is why liquidGlass(el, {}) read as "nothing changed"
   here: the module's sampled output could not reach these cards at all.

   So the refraction is DRAWN rather than SAMPLED. hero-photo.js gives each
   card a .hp-refract layer that repaints the backdrop the page itself paints,
   aligned to the card's own place on screen, and runs the module's
   displacement chain over it with plain `filter:` — which no 3D ancestor can
   switch off, and which Safari and Firefox honour too.

   Getting the artwork out of the way. The card is one opaque screenshot and
   hero.php cannot be edited, so its white ground has to be removed at render
   time. `mix-blend-mode: multiply` was the obvious tool and it does not
   work: the Porter-Duff formula is Co = (1-ab)*Cs + ab*B(Cb,Cs), so where
   the backdrop alpha ab is below 1 the source survives in proportion, and
   at ab = 0 the blend is a no-op. Over glass the card therefore stayed
   opaque white at every tint. This was measured, not reasoned.

   #hp-sat-ink-light does it properly: alpha from inverse luminance with the
   editorial room's own --foreground as the constant RGB, so white goes fully
   transparent and ink goes opaque ivory. The artwork inverts rather than
   gaining a white pane behind it, which is what let the tint come down.
   Both heroes use the same crop assets, so both get the same treatment.

   The filter is defined in hero-photo.php and emitted immediately before the
   hero, so the reference always resolves. A url() that resolves to nothing
   would drop the element entirely.

   No new markup is possible from the template, so the two tint/edge layers
   are the wrapper's own pseudo-elements and the refraction layer is a child
   hero-photo.js injects. */

/* The wash is 0.10 here, where the preview's own default was 0.32 — at 0.32
   the two stops below resolve to rgba(14,14,22,.18) and rgba(14,14,22,.32),
   the upstream demo's material verbatim. 0.10 is a clearer card than that
   material was drawn for, and it was chosen by eye against this photograph.

   The --hp-glass -> --sat-glass indirection is kept: it is what lets the
   reduced-transparency block at the foot of this file win. An inline custom
   property on <body> would outrank any rule, and this file no longer writes
   one, but the seam is cheap and the override is the reason it exists. */
body.hp {
  --sat-glass: var(--hp-glass, 0.1);
  /* the demo's ink wash */
  --sat-tint: 14 14 22;
  --sat-spec: #ffffffbf;
  --sat-spec-low: #ffffff2e;
  --sat-ambient: #ffffff14;
  --sat-ring: #ffffff38;
  /* the veil, as one colour the refraction layer can paint. Same two layers
     body.hp::after and ::before compose, so the glass shows the ground the
     rest of the page shows rather than a brighter copy of the photograph.
     At veil 0 this resolves to transparent and the layer paints the
     photograph alone, which is correct. */
  --hp-veil-mix: transparent;
}

/* color-mix() is guarded on the PROPERTY, not on the background-image that
   uses it. An invalid custom property is invalid at computed-value time and
   would drop the whole background-image declaration — photograph included —
   leaving an empty filtered box. Guarding here keeps the layer count constant,
   which matters because paint() writes two-value background-size and
   background-position strings against it. At the shipped veil of 0 both
   branches resolve to transparent, so this is a no-op today and a correctness
   fix for the reduced-transparency branch. */
@supports (color: color-mix(in srgb, red 50%, transparent)) {
  body.hp {
    --hp-veil-mix: color-mix(in srgb, var(--hp-veil-color, #0a0908) calc(var(--hp-veil, 0) * 100%), transparent);
  }
}

/* How deep the wash actually lands. The 3D cards take the value straight:
   they float over open photograph and carry the inverted ivory ink.

   The flat hero's crops cannot use that value. They are tucked into the
   screen-frame's corners, so most of each one sits over a near-white product
   screenshot rather than over the photograph, and each card straddles both.
   Neither ink works across that: over a 251-ish dashboard at a thin wash the
   ivory numeral measures about 2:1, and swapping to black ink would read on
   the dashboard and vanish on the photograph. Doubling the wash is what makes
   the card's own interior — rather than whatever is behind it — the surface
   the ink sits on, which is what dark liquid glass does over mixed ground.
   Capped so the reduced-transparency branch cannot drive it opaque. */
body.hp {
  --sat-wash: var(--sat-glass, 0.1);
}

body.hp .hp-flat-sat {
  --sat-wash: min(0.75, calc(var(--sat-glass, 0.1) * 2));
}

/* Four stacked layers, in paint order:
     .hp-refract  z 0  the bent backdrop
     ::before     z 1  the tint wash
     img          z 2  the artwork
     ::after      z 3  the glass edge
   The edge has to be its own layer rather than an inset box-shadow on the
   wrapper: an inset shadow paints above the element's background but BELOW
   its children, so it would have sat under the refraction layer and the card
   would have had no visible rim at all.

   liquid-glass.css zeroes these cards' ring in the light rooms, and in every
   room the only thing drawing a card's boundary today is the opaque white of
   the screenshot — once that ground goes translucent the card would have no
   edge left. `isolation: isolate` keeps the stack self-contained so a z-index
   here can never reach past the card. */
body.hp [data-fx="hero-sat"] > div,
body.hp .hp-flat-sat {
  background: none;
  box-shadow: var(--glass-chrome-shadow);
  isolation: isolate;
  position: relative;
}

body.hp .hp-flat-sat {
  /* the theme places these two with `absolute`; relative would take them out
     of their corners */
  position: absolute;
}

body.hp [data-fx="hero-sat"] > div::before,
body.hp [data-fx="hero-sat"] > div::after,
body.hp .hp-flat-sat::before,
body.hp .hp-flat-sat::after {
  border-radius: inherit;
  content: "";
  inset: 0;
  pointer-events: none;
  position: absolute;
}

body.hp [data-fx="hero-sat"] > div::before,
body.hp .hp-flat-sat::before {
  background: linear-gradient(
    180deg,
    rgb(var(--sat-tint) / calc(var(--sat-wash, 0.1) * 0.56)),
    rgb(var(--sat-tint) / calc(var(--sat-wash, 0.1) * 1))
  );
  z-index: 1;
}

/* The demo's specular set: a crisp top line, a faint bottom return, the
   frosted ambient reflection and the 1px glass ring. Values are the upstream
   demo's own frosted dressing, kept here verbatim. */
body.hp [data-fx="hero-sat"] > div::after,
body.hp .hp-flat-sat::after {
  box-shadow:
    inset 0 1.5px 1px var(--sat-spec),
    inset 0 -1px 1px var(--sat-spec-low),
    inset 0 -10px 24px var(--sat-ambient),
    inset 0 0 0 1px var(--sat-ring);
  z-index: 3;
}

/* The refraction layer. hero-photo.js owns the geometry on this element and
   its two children — they are a function of where the card currently sits on
   screen, so they cannot be written as static CSS. Everything that is NOT
   per-frame lives here.

   inset: 0 and no bleed: the displacement is a magnifying bulge (a negative
   scale), so every sample it takes is INWARD from the rim. It never needs
   content from outside the card, which is also why the module's own filter
   region of 0..100% is correct unchanged. */
body.hp .hp-refract {
  border-radius: inherit;
  inset: 0;
  /* The clip box, kept one level up from the filtered box: clipping a
     FILTERED box directly is an unreliable combination in WebKit, which is
     why this is a level of its own rather than one element doing both
     jobs. */
  overflow: hidden;
  pointer-events: none;
  position: absolute;
  z-index: 0;
}

body.hp .hp-refract__fx {
  inset: 0;
  position: absolute;
}

body.hp .hp-refract__ground {
  background-image:
    linear-gradient(var(--hp-veil-mix), var(--hp-veil-mix)),
    var(--hp-photo, none);
  background-repeat: no-repeat;
  inset: 0;
  position: absolute;
}

/* The flat hero's crops do not float over open photograph the way the 3D
   cards do — they are tucked into the screen-frame's corners and sit largely
   on top of an opaque dashboard screenshot. Painting only the photograph
   behind them would punch a hole through the product shot. So the frame is
   rebuilt into the same filtered layer at its own place, radius and hairline
   ring, and the displacement bends the whole composite — including the
   frame's edge, which is the most convincing part of the effect. The 3D
   cards never get this child. */
body.hp .hp-refract__frame {
  background-repeat: no-repeat;
  background-size: 100% 100%;
  position: absolute;
}

/* Ivory ink, not black: on the dark reading the black-ink filter lands on a
   dark photograph through clear glass and cannot be read. Same alpha ramp,
   the editorial room's --foreground as the constant RGB. */
body.hp [data-fx="hero-sat"] > div > img,
body.hp .hp-flat-sat > img {
  filter: url("#hp-sat-ink-light");
  position: relative;
  z-index: 2;
}

/* ------------------------------------------------------------- fallbacks */

/* A photograph behind a pinned, scrubbing composition is exactly the sort of
   large moving backdrop reduced motion is meant to calm. Hold it still. */
@media (prefers-reduced-motion: reduce) {
  body.hp::before {
    background-attachment: scroll;
  }
}

/* Both values below were tuned when the veil defaulted to 0.65 and the wash
   to 0.32, and both are further from the chosen setting than they were from
   that default. They are kept deliberately rather than re-derived: this
   branch is an accommodation, not the design. Someone who has asked their
   system for less transparency gets a solid editorial ground with the
   photograph withdrawn, and cards with enough wash to carry the ivory ink
   over it. */
@media (prefers-reduced-transparency: reduce) {
  body.hp::after {
    opacity: 1;
  }

  /* A concession, not a deletion. Forcing --sat-glass to 1 would turn the
     cards fully opaque and remove the material entirely. The wash is
     deepened instead and the refraction stays, so the cards calm down
     without disappearing. */
  body.hp {
    --sat-glass: 0.62;
  }
}
