/* ============================================================================
   gallery-frame.css — the immersive gallery's 3-layer framed-plate render.
   SHARED single source of truth, imported by BOTH the live portfolio gallery
   (portfolio/index.html, via the `pictures` symlink) and the admin frame-studio
   preview (admin/, as ../pictures/gallery-frame.css). Keep the two consumers
   pixel-identical: edit the plate's look HERE, never inline.

   These rules were extracted verbatim from portfolio/index.html (the
   `.g-plate / .g-mat / .g-window / .g-art / .g-frame-img / .g-window::after`
   block) as part of CMS Phase 3.1; the only change is moving them to this file.

   The plate is these stacked layers inside `.g-plate`:
     1. .g-mat        — a CSS passe-partout board that IS the entire mat. The frame
                        PNGs are wood-only: their transparent window is cut all the way
                        to the WOOD'S INNER EDGE (the old baked-in photographed mat ring
                        was erased from the PNGs), so this one continuous CSS board reads
                        from the wood lip right up to the artwork with NO seam. Every
                        side is adjustable per-work → CMS.
     2. .g-mount      — the MAT BOUNDING BOX: a transparent box at the four --mat-*
                        insets (the CMS knobs = the MINIMUM mat reveal on each side).
                        It centres its child (place-items:center).
     3. .g-window     — the actual cut OPENING, sized to the ARTWORK'S aspect ratio
                        (aspect-ratio:var(--art-ar)) and capped to the bounding box
                        (max-width/height:100%). The browser fits the largest
                        art-aspect rectangle inside .g-mount and centres it. Holds
                        img.g-art (which fills it exactly). overflow:hidden clips zoom/pan.
     4. .g-frame-img  — the real photographed frame PNG (transparent window) on top.

   HUG-THE-ART MODEL (no letterbox gap): the OPENING hugs the artwork — its aspect
   ratio EQUALS the artwork's, so the full artwork fills it with NO leftover gap on
   either axis. There is no inner rectangle / bevel / second layer. The four --mat-*
   sliders now define a MINIMUM-reveal bounding box (.g-mount); the opening is the
   largest art-aspect rectangle that fits inside it, CENTRED — so when the art's
   aspect matches the box the reveal == the sliders, and when it doesn't the reveal
   grows SYMMETRICALLY on the deficient axis (the old gap is absorbed into continuous
   mat, split evenly to both sides → "more passe-partout on top & bottom"). The one
   .g-mat board (its --mat-color + gradient) reads continuously right up to the art's
   edge; the art sits flush-mounted on it with NO drop-shadow / halo. (The frame's
   wall cast shadow is a box-shadow on .g-plate, NOT a drop-shadow on .g-frame-img: a
   drop-shadow followed the frame PNG's transparent-window alpha and cast the inner
   opening edge onto the mat, which read as a grey band.)
   `.g-dim` is the wall's resting overlay. The CSS custom properties
   (--frame-ar, --wood-*, --mat-color [per-work gallery.matColor, else the frame's], --mat-*, --art-ar, --art-fit/pos/zoom/ox/oy)
   are set per-work by gallery-frame.js (--art-ar from the loaded image's natural
   size, with the declared `ratio` as a pre-load fallback so there's no layout jump).
   --pan-x/--pan-y/--zoom are the live inspect knobs.
   ========================================================================== */

/* the framed print: the real photographed frame (clean-cut, true transparent rectangular window) over the work.
   The "hung on the wall" cast shadow lives HERE as a box-shadow (the plate's OUTER rectangle), NOT as a
   drop-shadow on .g-frame-img: a drop-shadow follows the PNG's alpha shape — a ring with a transparent
   window — so it also cast the inner opening edge's shadow DOWN onto the cream mat, which read as a grey
   "middle layer" band in the letterbox gap. A box-shadow casts only from the outer box, never inward. */
.g-plate{position:relative;
  --plate-w:min(var(--pw-vw,46vw),calc(var(--pw-vh,80vh) * var(--frame-ar,0.761)),var(--pw-max,560px));
  width:var(--plate-w);height:calc(var(--plate-w) / var(--frame-ar,0.761));
  box-shadow:0 36px 50px rgba(0,0,0,0.6),0 10px 18px rgba(0,0,0,0.5);}
/* MAT (passe-partout): a CSS board that IS the whole mat, sitting at the wood's inner edge. The
   frame PNGs are wood-only (their window is cut to the wood's inner edge — the baked photographed
   mat ring was erased), so this one continuous board shows from the wood lip to the artwork with no
   seam. Because it's CSS, every side is adjustable per-work (→ CMS) and we never re-cut the frame
   PNG. The 0.9% pull tucks it under the wood ring so there's no gap at the wood/mat join. */
.g-mat{position:absolute;z-index:1;
  left:calc(var(--wood-l,5%) - 0.9%);right:calc(var(--wood-r,5%) - 0.9%);
  top:calc(var(--wood-t,5%) - 0.9%);bottom:calc(var(--wood-b,5%) - 0.9%);
  background-color:var(--mat-color,#EAD1B6);
  /* TOP layer = a fine paper TOOTH (inline feTurbulence, desaturated + contrast-compressed),
     soft-light-blended into the cream so the board reads as cotton-rag MUSEUM MAT, not flat fill.
     BOTTOM layer = the gentle top-lit lighting gradient. Tile kept small → fine grain at any size. */
  background-image:
    url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%27220%27%20height%3D%27220%27%3E%3Cfilter%20id%3D%27p%27%20x%3D%270%27%20y%3D%270%27%20width%3D%27100%25%27%20height%3D%27100%25%27%3E%3CfeTurbulence%20type%3D%27fractalNoise%27%20baseFrequency%3D%270.7%27%20numOctaves%3D%273%27%20seed%3D%277%27%20stitchTiles%3D%27stitch%27%2F%3E%3CfeColorMatrix%20type%3D%27saturate%27%20values%3D%270%27%2F%3E%3CfeComponentTransfer%3E%3CfeFuncR%20type%3D%27linear%27%20slope%3D%270.62%27%20intercept%3D%270.19%27%2F%3E%3CfeFuncG%20type%3D%27linear%27%20slope%3D%270.62%27%20intercept%3D%270.19%27%2F%3E%3CfeFuncB%20type%3D%27linear%27%20slope%3D%270.62%27%20intercept%3D%270.19%27%2F%3E%3C%2FfeComponentTransfer%3E%3C%2Ffilter%3E%3Crect%20width%3D%27100%25%27%20height%3D%27100%25%27%20filter%3D%27url%28%23p%29%27%2F%3E%3C%2Fsvg%3E"),
    linear-gradient(176deg,rgba(255,255,255,0.05),rgba(0,0,0,0.03) 62%,rgba(0,0,0,0.06));
  background-size:220px 220px,auto;
  background-blend-mode:soft-light,normal;
  /* seated in the wood rabbet: a top catch + a faint all-round inner vignette toward the wood */
  box-shadow:inset 0 1px 2px rgba(0,0,0,0.10),inset 0 0 26px -8px rgba(20,18,12,0.12);}
/* the MAT BOUNDING BOX — its four insets ARE the mat-border widths (the CMS knobs),
   now read as the MINIMUM reveal on each side. TRANSPARENT (the .g-mat board shows
   through). It is the containing block for the centred opening (.g-window). */
.g-mount{position:absolute;z-index:1;background:transparent;
  left:var(--mat-l,13%);right:var(--mat-r,13%);top:var(--mat-t,9%);bottom:var(--mat-b,10%);}
/* the actual cut OPENING — sized to the ARTWORK'S aspect ratio so it HUGS the art with
   NO letterbox gap (opening AR == art AR). Its centring insets (--win-l/r/t/b, % of
   .g-mount) are computed in JS by gallery-frame.js (layoutOpening) — pure ratio math,
   NOT CSS aspect-ratio/flex/max-* (that pattern letterboxed the art in some Chrome
   versions). Extra mat splits evenly to both sides → continuous, symmetric reveal.
   TRANSPARENT so the mat reads through any sub-pixel edge; overflow:hidden so zoom/pan
   still crop the art at the opening edge. The opening carries a real bevel-cut MAT LIP
   (.g-window::before) so the cut edge reads as a raised board, not a flat sticker. */
.g-window{position:absolute;overflow:hidden;background:transparent;
  left:var(--win-l,0%);right:var(--win-r,0%);top:var(--win-t,0%);bottom:var(--win-b,0%);}
.g-frame.is-active .g-window{cursor:zoom-in;}
/* MAT LIP — the passe-partout's bevel-cut edge against the print (DESIGN.md "Mat Lip", kept a
   whisper). A real mat is ~1.4mm board cut at 45°, so it (a) reveals a fine LIGHTER core at the
   cut and (b) sits a hair PROUD of the print, casting a soft, top-weighted contact shadow onto it.
   Both are drawn as inset box-shadows on an overlay sitting OVER the art (z-index:2) — deliberately
   NOT a `filter:drop-shadow` (that follows the frame PNG's window alpha and re-creates the old grey
   "band" on the mat) and NOT a flat outline (that read as a fake printed border). Hugs the opening
   exactly (inset:0 on .g-window, which already hugs the art); pointer-events:none so inspect
   zoom/pan still work; below the missing-label ::after (z-index:3). Tunable via the --lip-* vars. */
.g-window::before{content:"";position:absolute;inset:0;z-index:2;pointer-events:none;
  box-shadow:
    inset 0 0 0 1.5px var(--lip-core,rgba(251,250,247,0.55)),  /* bevel CORE reveal (lighter cut), reads on dark art */
    inset 0 3px 5px -2px var(--lip-cast,rgba(22,19,13,0.30)),   /* top cast shadow — mat sits proud, light from above */
    inset 2px 0 4px -2px rgba(22,19,13,0.16),                   /* lighter left cast (top-left key) */
    inset 0 0 9px rgba(22,19,13,0.10);}                         /* faint ambient occlusion around the opening */
/* artwork — base framing (--art-fit/pos/zoom/ox/oy) is a DATA-DRIVEN crop; the source file is never touched.
   TRANSPARENT background so a contained image's letterbox shows the mat through it.
   NO drop-shadow: the artwork sits flush-mounted on the one continuous passe-partout. A drop-shadow
   here cast a soft dark halo onto the cream mat in the letterbox gap (a second "middle layer" tint);
   the requirement is the artwork on the mat with NO halo / no middle layer, so it is removed. */
.g-art{position:absolute;inset:0;width:100%;height:100%;object-fit:var(--art-fit,contain);
  object-position:var(--art-pos,center);display:block;background:transparent;z-index:1;
  transform:translate3d(calc(var(--art-ox,0%) + var(--pan-x,0px)),calc(var(--art-oy,0%) + var(--pan-y,0px)),0) scale(calc(var(--art-zoom,1) * var(--zoom,1)));
  transition:transform .26s var(--ease-out-expo);will-change:transform;user-select:none;-webkit-user-drag:none;}
/* No drop-shadow filter here: it followed the frame PNG's alpha (the transparent window included), so it
   cast the inner opening edge's shadow onto the cream mat → the grey "middle layer" band. The frame's
   wall shadow now lives as a box-shadow on .g-plate (outer rectangle only). */
.g-frame-img{position:absolute;inset:0;width:100%;height:100%;z-index:3;display:block;pointer-events:none;}
/* UNFRAMED (gallery.frame:"none") — oil on canvas, digital prints: hung as-is, no frame, no mat.
   gallery-frame.js builds `.g-plate.is-unframed > .g-window > img.g-art` and sets the plate's own
   --frame-ar to the ARTWORK's aspect, so the window (inset 0) is the whole plate and the art fills
   it. Scaled a step below a framed plate: a framed plate's footprint includes wood + mat, so the
   same cap would make bare works read oversized next to their framed neighbours. The canvas sits a
   little closer to the wall than a frame → a slightly tighter cast shadow. The mat lip is replaced by
   a whisper of canvas edge: a hairline crisp edge + a faint top-lit falloff across the surface. */
.g-plate.is-unframed{
  --plate-w:calc(min(var(--pw-vw,46vw),calc(var(--pw-vh,80vh) * var(--frame-ar,0.8)),var(--pw-max,560px)) * var(--unframed-scale,0.86));
  box-shadow:0 28px 40px rgba(0,0,0,0.55),0 8px 14px rgba(0,0,0,0.45);}
.g-plate.is-unframed .g-window::before{
  box-shadow:inset 0 0 0 1px rgba(0,0,0,0.22),inset 0 1px 0 rgba(255,255,255,0.10);
  background:linear-gradient(172deg,rgba(255,255,255,0.045),transparent 36%,rgba(0,0,0,0.07));}
.g-dim{position:absolute;inset:0;z-index:4;pointer-events:none;background:rgba(9,7,4,0.6);opacity:1;transition:opacity .55s ease;}
.g-frame.is-active .g-dim{opacity:0;}
/* missing-image fallback: paint the message panel in the mat colour (was #ECD5BA) so
   it matches the now-continuous board instead of a different tan rectangle. */
.g-window.is-missing::after{content:attr(data-missing-label);position:absolute;inset:0;display:grid;place-items:center;padding:18px;z-index:3;
  text-align:center;font-family:var(--sans);font-size:12px;line-height:1.5;color:#6b6052;background:var(--mat-color,#EAD1B6);box-shadow:none;}
