/* skin-glass.css - liquid glass and champagne gold. The site palette and texture layer. */

/* Self-hosted: the exhibition must not depend on the visitor happening to have
   these installed. Both are licensed for web embedding here.

   Backsteal is declared across a wide weight range on purpose. Only its Regular
   exists, and the UI asks for 600 in several places; without the range the
   browser would synthesise a fake bold, which reads as a smeared version of the
   real face. Claiming the range makes it use the one face it has. */
@font-face {
  font-family: "Backsteal";
  src: url("../assets/fonts/Backsteal-Regular.otf") format("opentype");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Futura Std";
  src: url("../assets/fonts/FuturaStd-Book.otf") format("opentype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Futura Std";
  src: url("../assets/fonts/FuturaStd-Medium.otf") format("opentype");
  font-weight: 500 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Futura Std";
  src: url("../assets/fonts/FuturaStd-Bold.otf") format("opentype");
  font-weight: 700 900;
  font-style: normal;
  font-display: swap;
}

:root {
  --bg-0: #08080a;
  --bg-1: #141417;

  --fg-0: #f5f5f7;
  --fg-1: #c6c6ca;
  --fg-2: #8e8e93;
  --fg-3: #5c5c61;

  --accent: #c9a06a;
  --accent-bright: #e0c39a;
  --accent-deep: #8f6f45;

  --surface: rgba(255, 255, 255, 0.045);
  --surface-hi: rgba(255, 255, 255, 0.10);
  --line: rgba(255, 255, 255, 0.09);
  --line-hi: rgba(255, 255, 255, 0.18);

  /* Scrollbar. Left to the browser it is a light grey trough with a stepper
     button at each end, which on this palette reads as a piece of another
     program left lying on the screen. base.css restyles it with these.
     Deliberately faint: a scrollbar only has to be findable, not seen. */
  --scroll-thumb: rgba(255, 255, 255, 0.15);
  --scroll-thumb-hi: rgba(255, 255, 255, 0.30);

  /* Liquid glass. Thinner and clearer than a plain frosted panel: Apple's let the
     backdrop through, and it is the rim, not the fill, that reads. */
  --card-bg: rgba(22, 21, 24, 0.34);
  --card-line: rgba(255, 255, 255, 0.09);
  --card-line-c: rgba(201, 160, 106, 0.55);

  --glass-blur: 33px;
  --glass-sat: 1.7;
  --glass-bright: 1.10;
  --glass-contrast: 0.94;

  /* The grain layer's opacity, so this is its peak coverage; the mean lands at
     about half that, because the tile's own alpha averages 128/255. Worth
     knowing: this stacks across overlapping cards, so a value that looks right
     on one looks heavy on three. */
  --frost: 0.01;

  /* The colour of every glass highlight - sheen, catchlight, rim, inset glow.
     A light grey rather than pure white: white at these alphas puts more light on
     the card than the glass should carry, and greying it down costs nothing that
     `filter: brightness()` would not also cost the model inside. Change this one
     triple to move all of them together. */
  --glass-light: 214, 212, 208;

  /* The backdrop mark's opacity. It has to be strong enough that a 55px blur
     still leaves visible structure through the cards - a fine dot grid at low
     opacity blurs away to nothing, which is the trap on a dark ground. */
  --mark: 0.28;

  /* What turns a plain dark panel into a frosted one - the milk lifts whatever
     is behind the panel, the catchlight catches its top edge. Held as a list so
     the selected card can swap the tint underneath without losing either. */
  --glass-surface:
    linear-gradient(168deg, rgba(var(--glass-light), 0.30) 0%, rgba(var(--glass-light), 0.12) 28%,
                            rgba(var(--glass-light), 0) 50%),
    linear-gradient(180deg, rgba(var(--glass-light), 0.48), rgba(var(--glass-light), 0) 3px),
    linear-gradient(rgba(var(--glass-light), 0.05), rgba(var(--glass-light), 0.05));

  /* A slab has thickness: a lit top edge, a dimmer bottom edge, light bleeding
     down from that top edge, and a bloom creeping in all round. Then the drop. */
  --card-shadow:
    inset 0 1px 2px rgba(var(--glass-light), 0.38),
    inset 0 -1px 2px rgba(var(--glass-light), 0.22),
    inset 0 0 3px rgba(var(--glass-light), 0.06),
    inset 0 0 46px rgba(var(--glass-light), 0.12),
    0 20px 48px rgba(0, 0, 0, 0.52);
  --card-shadow-c:
    inset 0 1px 2px rgba(var(--glass-light), 0.48),
    inset 0 -1px 2px rgba(var(--glass-light), 0.28),
    inset 0 0 3px rgba(var(--glass-light), 0.08),
    inset 0 0 54px rgba(var(--glass-light), 0.15),
    0 24px 60px rgba(0, 0, 0, 0.58),
    0 0 46px rgba(201, 160, 106, 0.28);

  --bar-bg: rgba(14, 14, 17, 0.62);
  --track: rgba(var(--glass-light), 0.09);
  --sheen: rgba(var(--glass-light), 0.055);
  --viewer-bg: rgba(8, 8, 10, 0.86);

  /* Apple rounds their panels close to half the shortest side; that is a large
     part of why theirs read as poured rather than cut. */
  --radius: 28px;
  --ease: cubic-bezier(0.16, 1, 0.3, 1);

  --font-display: "Backsteal", "Palatino Linotype", Palatino, Georgia, serif;
  /* Both faces are self-hosted below, so the visitor's own fonts no longer
     decide this; the named fallbacks only matter if the files fail to load. */
  --font-body: "Futura Std", "Futura", "Century Gothic", "Segoe UI",
               "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-mono: "SF Mono", "JetBrains Mono", Consolas, monospace;

  --cf-persp: 1200px;
  --cf-w: 200px;
  --cf-h: 248px;
}

/* Background: flat, plus the mark sitting behind everything.

   There used to be two drifting blooms of colour here. The warm one filled the
   header with an orange smudge; the cool one was only a 13%-alpha wash, but on a
   near-black ground its ramp was 17 levels of blue spread over 580px in plateaus
   up to 64px wide, and those plateau edges read as contour rings - Mach banding,
   not a drawn ring. Both are gone.

   What is here instead is the mark itself, centred and very faint. It is a dot
   matrix, so it is high-frequency detail rather than a gradient: no banding. It
   also gives the frosted panels something to sit over - though note that the
   cover-flow cards cannot blur it (see the liquid glass block below); it reaches
   them softened only by its own low opacity and the milk over it.

   The URL carries no content hash (prep_models.py only rewrites HTML attributes,
   and this one is inside a stylesheet) - the mark joins the fonts in the small set
   of assets whose URL has to be changed by hand if the file changes. */
.bg-layer {
  background: var(--bg-0);
  overflow: hidden;
}
.bg-layer::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: min(620px, 92vmin);
  aspect-ratio: 1136 / 943;          /* the mark's own proportions */
  transform: translate(-50%, -50%);
  background: url("../assets/logo.svg") center / contain no-repeat;
  opacity: var(--mark);
}

/* Liquid glass.

   Apple's edge is a lens: it bends the backdrop as light crosses it. CSS cannot
   do that - backdrop-filter blurs, saturates and brightens, but it never
   displaces - so the edge is built out of light instead: layered inset shadows
   for the thickness, and an iridescent band for the rainbow the real thing shows
   where the glass thickens.

   The band is a CONIC gradient, and it has to be. A linear one puts each hue at a
   single point along the card's diagonal, so across any one short edge the colour
   barely changes and the edge reads as flat white - which is exactly what an
   earlier attempt looked like. A conic gradient cycles the hues around the
   perimeter, so every edge picks up a run of them, which is how the real
   reflection behaves. It is masked down to a 2px band on the border box.

   The cover-flow cards DO blur for real now, and that took finding. They sit in a
   CSS 3D rendering context (.cf-stage and .cf-item both carry transform-style:
   preserve-3d, under .cf's perspective) and were silently getting nothing. The
   cause was not the 3D at all - it was `.cf-item { will-change: transform,
   opacity }`. will-change makes an element its own backdrop root when it names a
   property that would create one, and opacity is on that list, so every card was
   a backdrop root with nothing behind it. Dropping `opacity` from that
   declaration fixed it and cost the cover flow nothing.

   Proven by measurement, not by eye: with the fix, switching a card's blur
   between 0px and 55px changes 246,886 pixels. Before it, 0px vs 200px changed
   56 of 1.3M - antialiasing noise. An isolation page (_bftest.html) pinned it
   down: a plain parent and one with overflow:clip both blurred fine; a parent
   with perspective + preserve-3d + rotateY did not.

   Worth knowing when tuning: on a near-black ground a big blur averages the
   backdrop away, so the glass reads as smoked rather than frosted. The backdrop
   mark is what gives it something to work on - see --mark.

   The border itself is dropped to transparent and the rim drawn on the border
   box (inset -1px, since an absolutely positioned child is placed against the
   padding box), so the ring and not the border is the edge. */
.cf-card,
.tile,
.cf-item.center .cf-card {
  border-color: transparent;
}

/* A flat dimmer over the whole card. `filter` rather than knocking each white
   layer down by hand: there are seven of them (tint, sheen, catchlight, milk,
   grain, rim, inset halos) and they would drift apart the moment one is retuned.
   It dims what is inside the card too, which is the point of "overall".
   The backdrop blur is unaffected - it samples what is *behind* the card, and an
   element's own filter does not change its backdrop. Verified by measurement. */
.cf-card,
.tile {
  filter: brightness(0.9);
}

.cf-card,
.tile,
.panel,
.video-frame {
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat)) brightness(var(--glass-bright)) contrast(var(--glass-contrast));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat)) brightness(var(--glass-bright)) contrast(var(--glass-contrast));
}

.cf-card::before,
.tile::before {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(180deg,
      rgba(var(--glass-light), 0.72) 0%,
      rgba(var(--glass-light), 0.36) 24%,
      rgba(var(--glass-light), 0.23) 50%,
      rgba(var(--glass-light), 0.38) 76%,
      rgba(var(--glass-light), 0.66) 100%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  pointer-events: none;
  z-index: 2;
}

/* The grain, on its own layer.

   Frosted glass scatters light, and most of that work is done by the milk and
   the blur - see --glass-surface and --glass-blur. The grain is only here to keep
   the surface from being optically perfect: it breaks up the banding a large flat
   fill shows on a near-black ground and gives the panel something to read as
   etched. It sits on its own layer so its whole strength is one number,
   --frost. An earlier pass at 6% turned the cards into concrete; the tile is
   uncoloured, only its coverage varies, so it can never tint what is behind it. */
.cf-card::after,
.tile::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: url("../assets/frost.png");
  background-size: 96px 96px;
  opacity: var(--frost);
}

/* The card surfaces. The milk and the catchlight are layers, so they have to be
   repeated wherever the tint underneath changes. */
.cf-card,
.tile {
  background: var(--glass-surface), var(--card-bg);
}

/* Amber glow on the selected card */
.cf-item.center .cf-card {
  background: var(--glass-surface), rgba(34, 32, 30, 0.50);
}

.tile-num { text-shadow: 0 0 18px rgba(201, 160, 106, 0.5); }

.bar-fill {
  background: linear-gradient(90deg, var(--accent-deep), var(--accent-bright));
  box-shadow: 0 0 12px rgba(201, 160, 106, 0.5);
}

/* (The reduced-motion rule that used to switch off the aurora drift is gone with
   the aurora: nothing in the background layer moves any more.) */
