/* === the erg — scoped under .face-ornament ===

   The scene's whole stylesheet, and a PUBLIC file: anyone can fetch it, with
   or without an account, and nothing strips a CSS comment on the way out. So
   every note below describes the picture and nothing else. A word that must
   not ship belongs in the fragment, as a parser-level comment, and
   ErgStylesheetTest reads this file to hold that line. It was inline in the
   fragment until 2026-09-15; out here it is fetched once and cached, and the
   version parameter on its link moves with every deploy. */
.face-ornament {
  display: block; width: auto;
  /* bleed the scene through the page gutter — a place, not a plate */
  margin-inline: calc(var(--pad-x, 48px) * -1);
  /* …and on the phone through the shell's top padding too, so the scene
     starts flush with the screen's top edge instead of floating below a
     36px band. 36px is .ss-main's phone padding-top (samo.css); above
     760px the home rule in the desktop block zeroes the margin again.

     The cancellation is exact because that 36px is the whole of the
     column's top padding on a phone. It used to be exact only here: pages
     with a control in the top corner were padded further, and this page
     was left out of that by name. Those controls float at the foot of the
     screen now and the extra padding is gone, so this is a copy of one
     value kept in one place — change .ss-main's phone padding and move
     this number with it. */
  margin-top: -36px;
  /* the recurring pigments; dune bands and one-off tints stay literal */
  --co-slab:       #1a1410;   /* darkest rock: openings and silhouettes  */
  --co-ink:        #2b2118;   /* line work                               */
  --co-sand:       #e8dcc3;   /* starlight and pale sand                 */
  --co-sand-lit:   #f2e8d0;   /* the moon and everything it touches     */
  --co-stone-lit:  #d9c9a3;
  --co-stone:      #c9b489;
  --co-stone-deep: #a98f63;   /* the foreground sand                    */
  --co-ember:      #c2571f;   /* fire, banners and the one warm accent   */
  --co-cold:       #2f5fa3;   /* the tear in the sky                     */
  --co-cold-deep:  #1f3d6e;
  --co-night:      #0d1322;   /* zenith — also the label chips' glass    */
  --co-ayat:      #6f8a62;   /* the quiet green                         */
  --co-sun:        #ffe7a8;   /* the disc itself, never the light it casts */
  --co-gold:       #ff9d52;   /* what a low sun does to the air around it  */

  /* ── THE DAY ──
     One number turns the whole picture over. The template writes
     `--daylight` inline, 0 at night through 1 in full sun, smoothstepped
     across civil twilight so dawn arrives as a dawn and not as a switch
     thrown slowly. Everything below is the same pigment stated twice and
     mixed on that one number, so there is no second stylesheet to keep in
     step and no class to forget to toggle.

     THIS AMENDS THE FRAGMENT'S OLD PALETTE RULE. The palette used to be
     pinned — "a lit night, it carries its own light and must not invert
     with the page" — and that is still true of the PAGE: none of this
     follows the site's light/dark theme. It follows the sky over Piešťany,
     which is a different master, and the only one the drawing was ever
     pretending to have.

     WHAT DOES NOT TURN: the key light. Every crest's rim gradient and the
     bone shading are drawn for a source at the upper left, and re-lighting
     fourteen objects to chase the disc across the sky would cost the scene
     its internal agreement to buy a realism nobody checks for. The disc
     travels. The light it is supposed to cast does not. Deliberate.

     The rim gradients are also left at their night colour on purpose: a
     pale rim on a pale midday dune all but vanishes, which is what a real
     midday does to a rim. Free accuracy — don't "fix" it. */
  --daylight: 0;
  --dl: calc(var(--daylight) * 100%);

  /* THE LID FOLLOWS THE SAME RULE. `--cloud` is written inline beside
     `--daylight`, 0 for a clear sky up to the cap for a closed one, and its
     only job out here is the letterbox: the picture goes overcast inside
     its own frame while the strip above and below it stays the colour of a
     fine day, and on any window that is not 16:9 that seam is the first
     thing the eye finds. One token, mixed into the one stop that shows.

     62 is not a free number: it is the `overcast-lid` rect's own opacity
     inside the SVG, so the strip lands on exactly the colour the top of the
     picture lands on. Keep the two in step — if that rect's opacity moves,
     this moves with it. */
  --cloud: 0;
  --cl: calc(var(--cloud) * 62%);

  /* zenith down to the horizon */
  --sky-1: color-mix(in srgb, #2f6fb5 var(--dl), #070b16);
  --sky-2: color-mix(in srgb, #5a97d0 var(--dl), #101c37);
  --sky-3: color-mix(in srgb, #93bfe0 var(--dl), #1d3050);
  --sky-4: color-mix(in srgb, #c8d5d8 var(--dl), #3b4152);
  --sky-5: color-mix(in srgb, #e3cfa6 var(--dl), #6d5a45);

  /* four bands of sand, far to near — lifted and warmed, same order */
  --dune-a1: color-mix(in srgb, #dcc396 var(--dl), #6d5941);
  --dune-a2: color-mix(in srgb, #b99a6c var(--dl), #4c3e2e);
  --dune-b1: color-mix(in srgb, #cbaf7e var(--dl), #574833);
  --dune-b2: color-mix(in srgb, #a38851 var(--dl), #3c3023);
  --dune-c1: color-mix(in srgb, #b3925e var(--dl), #433628);
  --dune-c2: color-mix(in srgb, #8a6d3d var(--dl), #2b2219);
  --dune-d1: color-mix(in srgb, #96784b var(--dl), #302619);
  --dune-d2: color-mix(in srgb, #6d5432 var(--dl), #17110a);

  /* The names in the tall composition invert rather than fade: pale ink
     with a dark outline is unreadable at noon, and dark ink with a pale
     one is unreadable at midnight. Dedicated tokens, NOT the shared sand
     and night above — those two are load-bearing for the wind, the stars,
     the ruin and the chips' glass, and crossfading them would drag half
     the drawing along with the captions. */
  --tl-fill: color-mix(in srgb, #241d14 var(--dl), var(--co-sand));
  --tl-halo: color-mix(in srgb, #fbf3de var(--dl), var(--co-night));

  /* Same idea, for the desktop chips. They used to be pale ink over a dark
     glow, which held at night and went to washed-out-white-on-sand by noon
     — legible, because the glow was doing all the work, but reading as a
     rendering fault rather than as a caption. Ink and glow both turn. */
  --fl-ink:  color-mix(in srgb, #241d14 var(--dl), var(--co-sand-lit));
  --fl-halo: color-mix(in srgb, #fbf3de var(--dl), var(--co-night));
}

/* Stars and the milky band do not survive a sunrise. Each keeps its own
   resting opacity in --star-base and loses it as the sky fills. */
.face-ornament .stars { opacity: calc(var(--star-base, 1) * (1 - var(--daylight))); }

/* Scenery: both discs are drawn, neither is a control. Whatever tap target
   sits over them lives elsewhere and must receive the press itself. */
.face-ornament .sky-body { pointer-events: none; }

.face-ornament .plate { position: relative; width: 100%; }

/* ── THE SCENE'S TWO CORNERS ──
     An overlay across the whole plate that lets every tap through to the
     picture, with the two controls themselves opting back in. Last child
     of the plate, so it paints over the chip layer without a z-index.
     The env() inset is 0 everywhere except a notched phone, where the
     scene runs under the status bar. See the corners note in
     fragments/face.html. */
.face-ornament .scene-corners {
  position: absolute; inset: 0;
  pointer-events: none;
}
.face-ornament .scene-corners > * {
  position: absolute;
  top: calc(14px + env(safe-area-inset-top, 0px));
  pointer-events: auto;
  display: flex; align-items: center; justify-content: center;
  /* 44, the smallest target a thumb finds without aiming */
  width: 44px; height: 44px; border-radius: 50%;
}
/* the mark stands on the sky unframed — it is the identity, not a button.
   It is also INERT, so it hands the pointer back: the rule above turns
   pointer-events on for both corners because the one opposite is a link,
   but this one is a decoration lying on top of live scene objects, and a
   44px disc of dead zone over the moon's tap target is a bug you only
   find with a thumb. */
.face-ornament .scene-nw { left: 14px; pointer-events: none; }
.face-ornament .scene-nw img { display: block; width: 30px; height: 30px; }
/* the control opposite it does need an edge to be findable on a night sky */
.face-ornament .scene-ne {
  right: 14px;
  color: var(--co-sand-lit);
  background: color-mix(in srgb, var(--co-night) 46%, transparent);
  border: 1px solid color-mix(in srgb, var(--co-sand) 26%, transparent);
  transition: background .15s ease, border-color .15s ease, transform .15s ease;
}
.face-ornament .scene-ne:hover,
.face-ornament .scene-ne:focus-visible {
  background: color-mix(in srgb, var(--co-night) 74%, transparent);
  border-color: color-mix(in srgb, var(--co-sand) 58%, transparent);
  text-decoration: none;
}
.face-ornament .scene-ne:active { transform: scale(.94); }

/* ── the idea bulb, left of the control above ──
     The fixed disc in layout.html is hidden on `/` along with the rest of
     the chrome, so the home page was the one page that had lost it. Back on
     2026-08-08 by explicit instruction, and deliberately NOT as a third
     corner: it takes the SAME disc as the control beside it and sits one
     width plus a hair to its left, so the two read as one cluster instead of
     two competing buttons. Only the ink differs — the bulb keeps the gold it
     has always worn, which is what makes it findable next to an otherwise
     identical disc without being louder than it. Left of, not right of,
     because the control opposite the mark is the one that holds the corner.

     Keep this note free of the vocabulary ErgStylesheetTest bans: this file
     is public, and CSS is not markup, so nothing strips a word of it. */
.face-ornament .scene-ne.scene-idea {
  right: 64px;
  color: var(--samo-sun);
}

/* ── the hourglass, left of the bulb ──
     The same disc again, one more width to the left, so the cluster reads as
     three of a kind. Inside: two narrow bars, each filled as far as its sand has
     run, and a short ember tick where it should have run to by now. When either
     bar has left its tick behind (or run past it) the fill itself turns ember;
     on its line the glass stays the colour of lit sand. */
.face-ornament .scene-ne.scene-hg { right: 114px; }
.face-ornament .scene-hg .hg-frame {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.1;
  opacity: .55;
}
.face-ornament .scene-hg .hg-fill { fill: currentColor; }
.face-ornament .scene-hg .hg-fill.hg-off { fill: var(--samo-ember); }
.face-ornament .scene-hg .hg-tick {
  stroke: var(--samo-sun);
  stroke-width: 1.6;
  stroke-linecap: round;
}
.face-ornament .scene-hg.hg-alert { border-color: color-mix(in srgb, var(--samo-ember) 70%, transparent); }

/* On the phone the scene runs to all four edges, so a corner that ignores
   a notch or a rounded screen corner (a small phone turned on its side stays
   in this layout) parks its button under it. Above 760px the picture is a
   16:9 plate, narrower than any phone on its side, and the letterbox already
   clears the edge. */
@media (max-width: 760px), (orientation: portrait) and (max-width: 1199px) {
  .face-ornament .scene-nw { left: calc(14px + env(safe-area-inset-left, 0px)); }
  .face-ornament .scene-ne { right: calc(14px + env(safe-area-inset-right, 0px)); }
  .face-ornament .scene-ne.scene-idea { right: calc(64px + env(safe-area-inset-right, 0px)); }
  .face-ornament .scene-ne.scene-hg { right: calc(114px + env(safe-area-inset-right, 0px)); }
}

/* ── THE GLANCE LINE ──
     One line of what is waiting, written on the sky at rest: no box, no
     panel, the chips' own ink and halo so it turns with the daylight like
     they do. On the desk it sits in the top row between the mark and the
     right-hand cluster; on the phone that row is full, so it drops to a
     second row under it, centred, and wraps rather than crowding. Only the
     words take taps — the strip itself lets every press through to the
     picture under it. */
.face-ornament .scene-glance {
  position: absolute;
  top: calc(14px + env(safe-area-inset-top, 0px));
  left: 72px; right: 172px; min-height: 44px;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  column-gap: 22px; row-gap: 2px;
  pointer-events: none;
  text-shadow: 0 1px 2px var(--fl-halo), 0 0 9px var(--fl-halo), 0 0 18px var(--fl-halo);
  transition: opacity .12s ease;
}
.face-ornament .scene-glance a {
  pointer-events: auto;
  display: inline-flex; align-items: baseline; gap: 7px;
  padding: 6px 2px; min-width: 0;
  color: var(--fl-ink); text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}
.face-ornament .scene-glance .sg-k {
  font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0.14em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--fl-ink) 72%, transparent);
}
.face-ornament .scene-glance .sg-v {
  font-size: 14px; letter-spacing: -0.005em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
}
.face-ornament .scene-glance a:hover .sg-v,
.face-ornament .scene-glance a:focus-visible .sg-v,
.face-ornament .scene-glance a:active .sg-v { color: var(--ss-accent); }
.face-ornament.is-flying .scene-glance { opacity: 0; }
@media (max-width: 760px), (orientation: portrait) and (max-width: 1199px) {
  .face-ornament .scene-glance {
    top: calc(62px + env(safe-area-inset-top, 0px));
    left: calc(14px + env(safe-area-inset-left, 0px));
    right: calc(14px + env(safe-area-inset-right, 0px));
    min-height: 0; column-gap: 16px;
  }
  .face-ornament .scene-glance a { padding: 5px 2px; }
  .face-ornament .scene-glance .sg-k { font-size: 9.5px; letter-spacing: 0.12em; }
  .face-ornament .scene-glance .sg-v { font-size: 13px; }
}

/* ── THE STAGE — ON THE PHONE THE SCENE OWNS THE SCREEN ──
     Edge to edge, and the whole of it: below 761px there is no site bar
     above this, so there is nothing left to subtract. It used to read
     `100svh - 59px`, that bar's measured height (16 + 26 + 16 + a 1px
     hairline); if a bar ever comes back at these widths, the subtraction
     comes back with it.

     There is no letterbox and no plate: the portrait canvas is authored
     430×920 — the viewBox further down is the record — precisely so that
     it fills a modern phone, and `slice` (set on the svg) absorbs the
     remainder on a differently-shaped phone by trimming a few px rather
     than parking the picture inside a frame. The crop is bottom-anchored,
     so the height the bar used to take is given back as sky, not as
     stretch, and the thumb zone stays pinned to the bottom edge.

     100svh, not 100dvh: dvh grows when the browser's URL bar retracts, so
     a dvh-tall scene visibly resizes mid-scroll. svh is the small — always
     available — height, so the scene is stable and at worst leaves the
     strip the URL bar was occupying.

     The -36px pull-up above still cancels .ss-main's phone padding-top
     exactly; both numbers are unchanged, and nothing adds top clearance
     back on a phone any more, on this page or any other.

     Above 760px the plate carries the 16:9 ratio and the stage simply
     fills it; see the desktop block. */
.face-ornament .stage {
  position: relative;
  width: 100%;
  height: 100svh;
}

.face-ornament .face-portrait {
  position: absolute; inset: 0;
  transform-origin: center center;
}
.face-ornament .diagram {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  display: block; overflow: visible;
}
.face-ornament .scene-wide { display: none; }

/* THE HANDLE AT THE FOOT OF THE SCREEN. The layout asks for the whole screen
   (viewport-fit=cover), and where a browser grants it the bottom strip of the
   page runs under the system's gesture handle — which is exactly where this
   composition keeps its thumb zone. So the drawing stops above the handle,
   and the strip left under it is painted the nearest sand's own colour. The
   inset is 0 wherever nothing covers the page, which leaves the picture
   exactly as it was. */
@media (max-width: 760px), (orientation: portrait) and (max-width: 1199px) {
  .face-ornament .stage { background: var(--dune-d2); }
  .face-ornament .face-portrait { bottom: env(safe-area-inset-bottom, 0px); }
}

/* ── THE LEDGER (.face-labels) — PHONE FIRST ──
     Two columns of tap targets flowing UNDER the full-screen scene, each
     one a drawing plus its name. Above 760px the same cards become
     annotation chips over the landscape.

     NO BOXES. Every card used to be a bordered, filled panel, which made
     the ledger read as a stock component parked under an illustration.
     The separation now comes from each drawing carrying its own sand
     backdrop — which is exactly why those drawings were authored with
     one — so the cards need no frame of their own and get none. If you
     are about to add `border` or `background` back to .flabel: don't.

     STRUCTURAL RULE — .face-labels is a SIBLING of .stage, never a
     child. As a child it sat under the absolutely-positioned scene and
     every card was visible but dead (positioned boxes paint above
     in-flow content) while the stage's preferred ratio stretched to fit
     the list. Do not move it back inside .stage. */
.face-ornament .face-labels {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 4px 14px;
  margin: 26px var(--pad-x, 48px) 0;
}
/* …AND ON A PHONE IT IS NOT SHOWN AT ALL (2026-08-08). The operator's
   words: "phone has a good graphics and below are listed buttons in old
   graphics, remove that from the phone version, but make sure phone has
   accessible all modules." The list is redundant there — the portrait
   composition names the SAME objects this grid does (do not write the
   attribute they agree on here; ErgStylesheetTest bans it from this file)
   and every one of them is a thumb-sized target, so the drawing can be the
   only navigation, which the colossus before it could not be: its targets
   fell under the 48px Android minimum. Nothing is deleted: the rows are
   still the no-JS list here and, above 760px, the chips over the landscape.

   GATED ON .scene-live, which face.js adds only once it has actually
   wired an organ. Without JS the drawings are inert, so the list must
   stay — that is the documented no-JS fallback and it still works. No
   flash: the ledger begins below a 100svh scene, off-screen at paint.

   And with the list gone, .ss-main's 64px of bottom padding became 64px of
   empty page under a scene that fills the screen exactly — enough to raise
   a scrollbar and promise something below the fold that isn't there. It
   goes with the list, under the same gate, so the no-JS render keeps its
   gutter beneath the real cards. */
@media (max-width: 760px), (orientation: portrait) and (max-width: 1199px) {
  body.scene-live .face-labels { display: none; }
  body.erg-home.scene-live .ss-main { padding-bottom: 0; }
  /* Until face.js gets there the list is still in the page, below the fold,
     and every card clones a whole drawing: laying them out cost the first
     paint ~160ms on a throttled phone (2026-10-19, median of 5). Skip that
     work while it is off-screen; the no-JS reader who scrolls down still
     gets every card, rendered on arrival. */
  .face-ornament .face-labels { content-visibility: auto; contain-intrinsic-size: auto 1100px; }
}
.face-ornament .flabel {
  border: 0; border-radius: 0; background: none;
  line-height: 1.25;
}
/* The whole card is the target: the <a> fills it, so there is no dead
   padding a thumb can land in. The drawing spans every text row, so the
   card is as tall as its content and never shorter than the 48px Android
   minimum. */
.face-ornament .flabel .fl-go {
  display: grid;
  grid-template-columns: 46px minmax(0, 1fr);
  grid-auto-rows: min-content;
  align-content: center; column-gap: 12px;
  min-height: 74px; padding: 9px 0;
  text-decoration: none; color: inherit;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}
.face-ornament .fl-mark {
  grid-column: 1; grid-row: 1 / -1; align-self: center;
  width: 46px; height: 46px; display: block; border-radius: 3px;
  transition: transform .15s ease;
}
.face-ornament .flabel .fl-name,
.face-ornament .flabel .fl-sense,
.face-ornament .flabel .fl-now { grid-column: 2; }
/* With the frames gone the feedback is the name and the drawing itself —
   no panel to light up. */
.face-ornament .flabel:hover .fl-name { color: var(--ss-accent); }
.face-ornament .flabel:hover .fl-mark { transform: scale(1.06); }
/* Ring the CARD, not the inner link — samo.css puts a global
   :focus-visible outline on every <a>, which would otherwise draw a
   second ring inside this one. Both halves share the :has() condition so
   a browser without :has() keeps the normal link ring. */
.face-ornament .flabel:has(.fl-go:focus-visible) { outline: 2px solid var(--ss-accent); outline-offset: 2px; }
.face-ornament .flabel:has(.fl-go:focus-visible) .fl-go { outline: none; }
.face-ornament .flabel .fl-name {
  font-size: 17px; font-weight: 500; letter-spacing: -0.02em; color: var(--fg);
}
.face-ornament .flabel .fl-sense {
  font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--muted);
}
/* Wraps on the phone grid and is clamped to two lines so cards stay the
   same height. The desktop chips put the nowrap ellipsis back. */
.face-ornament .flabel .fl-now {
  margin-top: 2px; font-size: 12.5px; line-height: 1.3; font-style: italic; color: var(--muted);
  max-width: 100%; overflow: hidden;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
}
.face-ornament .flabel .fl-now .what { color: var(--fg); font-style: normal; }

/* ── no card carries a scene any more ──
   One of them used to: a 16:9 drawing absolutely positioned under it, faded
   in on :hover and :focus-within. It came out on 2026-08-08, on the
   operator's instruction — it was a tooltip hanging directly over the very
   object in the landscape it was a picture OF, so the drawing went into the
   scene and took that object's place. Nothing here replaces it; the rules
   that opened it, and the `position: relative` that anchored it, are gone
   with the markup. The drawing lives in fragments/erg-marks.html.

   KEEP THIS PARAGRAPH even though its rules are gone. This stylesheet is
   PUBLIC — a stranger can fetch it even though the ledger markup never
   reaches one — so a selector naming a module, or a comment explaining such
   a selector, hands that name to anyone who asks. The guard caught three
   spellings of it while these rules still sat inline in the page, the last
   one in this very block, and ErgStylesheetTest reads this file for the same
   thing now. Select through a drawing's own class, never through an
   attribute selector, and name nothing here. */
/* the marked group — a quiet suffix so those cards read as distinct. The
   selector itself names the group; this note deliberately does not. */
.face-ornament .flabel.fl-owner .fl-name::after {
  content: "· private"; margin-left: 6px;
  font-family: var(--font-mono); font-size: 9px; font-weight: 400;
  letter-spacing: 0.1em; color: var(--faint); vertical-align: 1px;
}

/* organs: clickable, hover/active feedback */
.face-ornament .organ { cursor: pointer; transition: filter .25s ease; }
/* .is-hot is the same glow arrived at from the other end of the pair: the
   written name is hovered or focused, and its drawing answers. face.js sets
   a CLASS and not an inline filter, which is not a tidiness preference — an inline
   style outranks every stylesheet rule, including the one (kept beside that
   group's own script) that has to CANCEL this glow on the one group holding
   three separate controls. Inline, a press on the middle control lit all three; that bug
   shipped, was reported from a phone, and survived its first fix because
   the fix was written in CSS and the bug was written in JS. */
.face-ornament .organ:hover,
.face-ornament .organ:focus-visible,
.face-ornament .organ.is-hot {
  filter: drop-shadow(0 0 6px color-mix(in oklch, var(--ss-accent) 55%, transparent));
  outline: none;
}
/* ── names drawn INSIDE the tall composition ──
   The wide scene names its parts with absolutely-positioned chips laid
   over the picture in percentages. That trick cannot be carried across:
   the tall scene is `slice`, so it scales to COVER its box and crops the
   overflow, and the crop moves with the viewport ratio. A percentage
   overlay therefore drifts against the art the moment a phone is not the
   exact shape of the viewBox — and a caption that walks off the thing it
   captions is worse than no caption. So these are <text> in the scene's
   own coordinate space: they crop when their drawing crops, together,
   because they are the same object.

   Always on, and quiet. Above 760px a name can hide and be recovered by
   moving the pointer onto the picture; here there is no pointer to move.
   The drawing IS the navigation, so a name that hides has hidden for
   good, and what is left is a picture of twelve things you must already
   know to use. Quiet is the whole budget: no box, no second line, no
   live value — one word per drawing, at seventy percent of the sand.

   The halo is a stroke painted UNDER the fill (paint-order), not a
   shadow: these words cross a lit crest, a black opening and a moonlit
   stone face within one screen, and only an outline holds all three. */
.face-ornament .tl-name {
  font-family: var(--font-mono, monospace);
  /* 9.5 units drew 8 px on a 360-wide phone at 70 % — the 5.11.2026
     first-visit test: a 71-year-old could not read one name, a 16-year-old
     called them "tiny grey labels". 11.5 units at 86 % is ~10 px, the
     smallest the halo still carries over the crest and the black doorway. */
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  fill: color-mix(in srgb, var(--tl-fill) 86%, transparent);
  paint-order: stroke fill;
  stroke: var(--tl-halo);
  stroke-width: 2.8px;
  stroke-linejoin: round;
  stroke-opacity: .72;
  /* the drawing under it is the tap target; the word must never eat a tap
     meant for the picture, nor one meant for a neighbour it overhangs */
  pointer-events: none;
  transition: fill .18s ease;
}
/* a finger on a drawing brings its own name up to the accent — this is the
   phone's entire substitute for a pointer, which can rest on something
   without committing to it. face.js already sets .is-hot on pointerdown. */
.face-ornament .organ.is-hot .tl-name { fill: var(--ss-accent); }
/* PLAIN WORDS under a code name (6.11.2026, Samo's 2D on the audit): five
   first-time visitors, 9 to 71, could not tell what the invented names
   meant. Only those get a line; a name that is already a plain word does not.
   Lower case and quieter than the name, so the name stays the landmark. */
.face-ornament .tl-what {
  font-family: var(--font-mono, monospace);
  font-size: 9px;
  letter-spacing: 0.04em;
  fill: color-mix(in srgb, var(--tl-fill) 72%, transparent);
  paint-order: stroke fill;
  stroke: var(--tl-halo);
  stroke-width: 2.4px;
  stroke-linejoin: round;
  stroke-opacity: .72;
  pointer-events: none;
}

/* The one name whose drawing leaves. By day, and on any night with the moon
   down, that name stands in an empty patch of sky, and a word alone there
   reads as a caption for nothing rather than as something to press. So while
   the disc is down its pad carries a small crescent above the word, in the
   name's own ink and halo: the drawing's mark, kept for the hours the drawing
   is away. The pad under it still takes the press. */
.face-ornament .tl-mark {
  fill: color-mix(in srgb, var(--tl-fill) 62%, transparent);
  paint-order: stroke fill;
  stroke: var(--tl-halo);
  stroke-width: 2.4px;
  stroke-linejoin: round;
  stroke-opacity: .62;
  pointer-events: none;
  transition: fill .18s ease;
}
.face-ornament .organ.is-hot .tl-mark { fill: var(--ss-accent); }

/* ── THE WEEK, AS A PANE IN THE SKY ──
   Eight days in two blocks: today and the two after it, then the rest of
   the week set back behind them. The gap is not spacing. SHMU publishes two
   forecasts, a three-day model and a ten-day one, and each block opens the
   page that runs its own — so where the blocks part is where the models do,
   and the picture says that without a word of explanation.

   No colours of its own, on purpose. --tl-fill is dark ink by day and pale
   sand by night; --tl-halo is exactly the opposite. A plate of the one
   behind marks of the other is therefore legible at noon and at three in
   the morning, and not one rule here has to know which it is. */
.face-ornament .wx-plate {
  fill: var(--tl-halo);
  opacity: .28;
  transition: opacity .18s ease;
}
.face-ornament .wx-edge {
  fill: none;
  stroke: var(--tl-fill);
  stroke-opacity: .22;
  transition: stroke-opacity .18s ease;
}
.face-ornament .wx-block { cursor: pointer; outline: none; }
/* On the phone the pane and the tap target are two elements with a moon
   between them. The target is invisible and early; the pane is late and must
   not eat the tap it is lying on top of — neither its own, nor the moon's.
   The focus ring has to be drawn on the target, because that is the half
   that takes focus, and it shows through the pane above it. */
.face-ornament .wx-ink { pointer-events: none; }
.face-ornament .wx-tap { fill: transparent; }
.face-ornament .wx-block:focus-visible .wx-tap {
  fill: color-mix(in srgb, var(--ss-accent) 30%, transparent);
}
.face-ornament .wx-block:hover .wx-plate,
.face-ornament .wx-block:focus-visible .wx-plate { opacity: .42; }
.face-ornament .wx-block:focus-visible .wx-edge { stroke: var(--ss-accent); stroke-opacity: .9; }

/* the marks are currentColor all the way down, so the whole far block
   recedes on this one line */
.face-ornament .wx-mark { color: var(--tl-fill); }
/* set back into the haze — the same seven marks one step quieter, which is
   what "later in the week" looks like in a picture that already spends
   distance on everything else */
.face-ornament .wx-far .wx-mark,
.face-ornament .wx-far .wx-day,
.face-ornament .wx-far .wx-hi,
.face-ornament .wx-far .wx-lo { opacity: .84; }
.face-ornament .wx-far .wx-plate { opacity: .2; }
.face-ornament .wx-far .wx-edge { stroke-opacity: .09; }
.face-ornament .wx-far:hover .wx-plate,
.face-ornament .wx-far:focus-visible .wx-plate { opacity: .34; }

/* THE HALO IS NOT DECORATION. Both blocks lie in the corridor the sun and
   the moon climb through on the phone, and the far one sits low enough to
   spend every dawn and every dusk on top of the horizon glow — pale ink on
   pale light, which is the one pairing this palette cannot fix by turning
   over, because both ends turned together. An outline in the opposite
   token does fix it: dark rim at night, pale rim by day, and the numbers
   keep their edge over a rising disc. Same trick as .tl-name, same reason.

   The far block strokes thinner because its type is smaller — a 2px rim
   around 9.5px glyphs closes the counters and the day reads as a smudge. */
.face-ornament .wx-day,
.face-ornament .wx-hi,
.face-ornament .wx-lo {
  font-family: var(--font-mono, monospace);
  paint-order: stroke fill;
  stroke: var(--tl-halo);
  stroke-width: 2px;
  stroke-opacity: .5;
  stroke-linejoin: round;
}
.face-ornament .wx-far .wx-day,
.face-ornament .wx-far .wx-hi,
.face-ornament .wx-far .wx-lo { stroke-width: 1.3px; }

.face-ornament .wx-day {
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  fill: color-mix(in srgb, var(--tl-fill) 74%, transparent);
}
.face-ornament .wx-hi {
  font-weight: 600;
  fill: var(--tl-fill);
}
.face-ornament .wx-lo {
  fill: color-mix(in srgb, var(--tl-fill) 64%, transparent);
}
/* the day being lived, marked rather than named: every other cell holds two
   letters, and a word long enough to say "today" would not fit in one */
.face-ornament .wx-now { fill: var(--ss-accent); }

/* The far day letters are the one thing on the strip that was genuinely hard
   to read, and not by accident: the moon's disc never leaves x 48–216, so it
   is over the first cell or two of the far block EVERY time it is in the
   band — never the fourth, never the fifth. Two dimmings stacked (74% fill,
   then the .84 the whole far block takes) left them at ~62% against the
   brightest thing in the scene. This puts them back level with the near
   block's letters; the recession is carried by size, by the fainter pane and
   by the marks, which is plenty, and none of that goes soft over a crescent
   the way a pale glyph does. */
.face-ornament .wx-far .wx-day {
  fill: color-mix(in srgb, var(--tl-fill) 88%, transparent);
}

/* tap feedback — touch devices get no :hover, so every press must respond
   the instant a finger lands */
.face-ornament .organ:active,
.face-ornament .organ.is-picked {
  filter: drop-shadow(0 0 10px color-mix(in oklch, var(--ss-accent) 80%, transparent));
}
.face-ornament .flabel:active .fl-name { color: var(--ss-accent); }
.face-ornament .flabel:active .fl-mark { transform: scale(0.95); }

/* Pads for a finger on the landscape. Its smallest objects (the carved rows,
   the board between the two players, the tent, the green light) are drawn at
   a desk's scale, and on a phone turned on its side the whole picture is
   about a third of that: a mouse still finds them, a thumb does not. Each
   carries a transparent pad reaching into the empty ground around it and
   stopping at its neighbours' own targets, shown only where there is no
   pointer. */
.face-ornament .tap-pad { display: none; fill: transparent; }
@media (hover: none) {
  .face-ornament .tap-pad { display: inline; }
}

/* ── FLY-TO-CORNER ── face.js sets the transform inline; CSS owns the
     easing. While flying, the labels fade and the scene is inert. */
.face-ornament .face-portrait {
  transition: transform .52s cubic-bezier(.6, 0, .2, 1), opacity .52s ease;
}
.face-ornament.is-flying { pointer-events: none; }
/* A layer for the flight, and only for the flight. It used to be asked for all
   the time, which held the whole screen-sized picture on a layer of its own
   through every idle minute on the page. face.js adds .is-flying a frame before
   it sets the transform, which is exactly the notice a browser needs. */
.face-ornament.is-flying .face-portrait { will-change: transform; }
.face-ornament.is-flying .face-labels { opacity: 0; transition: opacity .12s ease; }

/* ── IDLE MOTION — the place breathes but never performs ── */
/* the fire deep in the doorway: banked coals, not an indicator */
@keyframes hearth-glow { 0%, 100% { opacity: 0.4; } 50% { opacity: 0.95; } }
.face-ornament .hearth { animation: hearth-glow 5.6s ease-in-out infinite; }

/* the ayat: mostly dark with one soft green breath — deliberately dim */
@keyframes ayat-blink { 0%, 82%, 96%, 100% { opacity: 0.3; } 88%, 91% { opacity: 0.9; } }
.face-ornament .ayat-cap { animation: ayat-blink 6.4s linear infinite; }
@keyframes ayat-halo { 0%, 82%, 96%, 100% { opacity: 0.08; } 88%, 91% { opacity: 0.3; } }
.face-ornament .ayat-halo { animation: ayat-halo 6.4s linear infinite; }

/* ── HOW HARD THINGS BURN — the same two objects, told what to feel.
      Modifier classes only; every colour and rate above stays the default,
      so an unmodified scene is exactly the scene as drawn. ── */
/* the coals: banked when little is waiting, up when a lot is */
@keyframes hearth-quiet { 0%, 100% { opacity: 0.18; } 50% { opacity: 0.42; } }
@keyframes hearth-busy  { 0%, 100% { opacity: 0.62; } 50% { opacity: 1; } }
.face-ornament .hearth.hx-quiet { animation: hearth-quiet 8.6s ease-in-out infinite; }
.face-ornament .hearth.hx-busy  { animation: hearth-busy 3.4s ease-in-out infinite; }

/* the ayat's mood. Green is the whole point of the object — it stays
   soft and slow unless something is actually wrong, and only the loud case
   is allowed to be red and fast. */
@keyframes ayat-alarm { 0%, 100% { opacity: 0.35; } 50% { opacity: 1; } }
.face-ornament .tp-warn .ayat-cap  { fill: #c9a227; }
.face-ornament .tp-warn .ayat-halo { stroke: #c9a227; }
.face-ornament .tp-fail .ayat-cap {
  fill: #d0402c; animation: ayat-alarm 1.6s ease-in-out infinite;
}
.face-ornament .tp-fail .ayat-halo {
  stroke: #d0402c; animation: ayat-alarm 1.6s ease-in-out infinite;
}
/* nothing to report from: dimmed to stone, never blinking a colour it hasn't earned */
.face-ornament .tp-unknown .ayat-cap,
.face-ornament .tp-none .ayat-cap  { fill: var(--co-stone-deep); }
.face-ornament .tp-unknown .ayat-halo,
.face-ornament .tp-none .ayat-halo { stroke: var(--co-stone-deep); }

/* the wind drifts; the inner group is nested so CSS transform does not
   clobber an SVG positioning attribute */
@keyframes wind-drift { 0%, 100% { transform: translateX(0); } 50% { transform: translateX(16px); } }
.face-ornament .wind { animation: wind-drift 24s ease-in-out infinite; }

/* THE WEATHER, which is real: what the air over Piešťany is doing right now.

   Rain and snow are tiled patterns, and the loop is a LATTICE VECTOR of the
   tile — rain's tile is 26x60 and it travels (-26,+60), snow's is 34x34 and
   it travels (-8,+34) after four steps. Land anywhere else and the sheet
   visibly jumps at the end of every cycle. The strokes inside each tile are
   drawn parallel to that vector, so the drops fall along the line they are
   drawn on rather than sliding sideways down their own length.

   The sheets overhang the plate by one period on the leading edges, which is
   what the translate eats.

   A sheet is in the page only while its dial is above zero; the fragment
   leaves it out otherwise, and the cloud bank and the blowing sand with it.
   An animation on a transparent sheet still runs: rain used to tick every
   0.85 seconds, all day, over a clear sky. */
@keyframes erg-rain { from { transform: translate(0, 0); } to { transform: translate(-26px, 60px); } }
@keyframes erg-snow { from { transform: translate(0, 0); } to { transform: translate(-8px, 34px); } }
.face-ornament .rainfall { animation: erg-rain 0.85s linear infinite; }
.face-ornament .snowfall { animation: erg-snow 5.6s linear infinite; }

/* cloud drifts on the same slow breath as the wind, half a period apart so
   the two are never in step */
@keyframes cloud-drift { 0%, 100% { transform: translateX(0); } 50% { transform: translateX(30px); } }
.face-ornament .cloudbank { animation: cloud-drift 52s ease-in-out infinite; }

/* the fork is invisible between strikes — twice in a row, as they come */
@keyframes storm-strike {
  0%, 92%, 100% { opacity: 0; }
  93% { opacity: .95; } 94% { opacity: .1; } 95% { opacity: .75; } 96.5% { opacity: 0; }
}
.face-ornament .bolt { animation: storm-strike 13s linear infinite; opacity: 0; }

/* Sand off the crest when it is actually blowing over Piešťany. A separate
   class from `.wind` on purpose — that one belongs to a gated organ, so
   borrowing it would take the weather away from a visitor and would turn a
   tap on the weather into a tap on something else. Each streak fades in as
   it enters and out as it leaves, so the loop has no seam to notice. */
@keyframes sand-blow {
  0%   { transform: translateX(-70px); opacity: 0; }
  16%  { opacity: 1; }
  84%  { opacity: 1; }
  100% { transform: translateX(140px); opacity: 0; }
}
.face-ornament .sandblow { animation: sand-blow 11s linear infinite; }

/* the tear flickers once in a while */
@keyframes glitch-flicker { 0%, 90%, 100% { opacity: 1; } 92% { opacity: 0.65; } 94% { opacity: 0.95; } 96% { opacity: 0.75; } }
.face-ornament .glitch { animation: glitch-flicker 8s linear infinite; }

/* the radiant hovers */
@keyframes radiant-float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-4px); } }
.face-ornament .radiant { animation: radiant-float 9s ease-in-out infinite; }

/* the projector's pale blue band shimmers */
@keyframes beam-shimmer { 0%, 100% { opacity: 0.25; } 50% { opacity: 0.45; } }
.face-ornament .beam { animation: beam-shimmer 5s ease-in-out infinite; }

/* ── THE TWO RARE THINGS ──
     Everything above is a loop you notice only if you stare. These two are
     events: they happen, then they are over, and if you looked away you
     missed one. Both are long-period animations whose whole active window
     sits at the START of the timeline, so the rest of the cycle is dead air
     — 72 seconds of empty sky between one streak and the next. The delay
     keeps either from firing the instant the page opens, which would read
     as a loading animation rather than as weather.

     CSS and not script, because face.js does not load for everyone: a
     signed-out visitor's desert has to be able to be alive too.

     Both animate an INNER group. A CSS transform replaces an element's
     transform attribute outright, so the outer group keeps the placement
     and the inner one does the moving.

     `backwards` holds the first frame through the delay, and the first frame
     is invisible. Without it the delay showed each drawing at its authored
     pose: a streak standing still in the sky for the first nine seconds, and
     a ridge parked past the picture's left edge for twenty-four — out in the
     letterbox, on a wide screen. */
@keyframes shooting-star {
  0%,  0.5% { opacity: 0;    transform: translate(0, 0); }
  1.2%      { opacity: 0.95; }
  3.2%      { opacity: 0;    transform: translate(190px, 96px); }
  100%      { opacity: 0;    transform: translate(190px, 96px); }
}
.face-ornament .shooting {
  animation: shooting-star 74s linear infinite;
  animation-delay: 9s;
  animation-fill-mode: backwards;
}

/* something big moving under the sand, seen only as the ridge it pushes up */
@keyframes wormsign-cross {
  0%,  2%  { opacity: 0;    transform: translateX(0); }
  5%       { opacity: 0.5;  }
  15%      { opacity: 0.5;  }
  18%      { opacity: 0;    transform: translateX(var(--worm-run, 300px)); }
  100%     { opacity: 0;    transform: translateX(var(--worm-run, 300px)); }
}
.face-ornament .wormsign {
  animation: wormsign-cross 110s ease-in-out infinite;
  animation-delay: 24s;
  animation-fill-mode: backwards;
}

/* the pilot light on the dish's feed horn */
@keyframes feed-lamp { 0%, 100% { opacity: 0.55; } 50% { opacity: 1; } }
.face-ornament .feed-lamp { animation: feed-lamp 4.4s ease-in-out infinite; }

/* ── the .erg-sprite filter lived here and is deliberately not replaced ──
   It existed to hold ONE rendered object — the skull — down to the drawn
   sand's value, because a render carries its own light and this scene's
   light is a CSS variable. Four numbers of brightness/contrast/sepia could
   match the value; nothing in a filter could match the register. The skull
   came out on 2026-09-04 and the scene holds no rendered object at all
   now (the grain is a data-URI texture over everything, not a picture of
   something), so there is nothing left for it to correct. If a render is
   ever wanted here the answer is not this rule — see
   docs/specs/homepage/erg-material-rule.md. */

/* ── DESKTOP (>760px) — the landscape scene with annotation chips ── */
@media (min-width: 1200px), (min-width: 761px) and (orientation: landscape) {
  /* ── HOME: THE SCENE OWNS THE VIEWPORT ──
       The picture keeps its authored 1600×900 EXACTLY, and that is not a
       preference. Every chip below is placed as a percentage of this box,
       and the outermost of them sit 2% and 3% in from the edges, so a crop
       of even a few percent walks them off the objects they annotate — a
       16:10 laptop would need 5% a side and would eat two of them outright.
       So the box is CONTAINED, the largest 16:9 that fits, and the leftover
       strip is PAINTED rather than left as a bar.

       The paint is the picture's own edge, MEASURED — the artwork was
       rasterised to a canvas and its two outermost columns read off, five
       pixels at a time so a single rim line or a star could not set a
       stop. Not derived from the gradient defs above, and that distinction
       is the whole reason it works: the two edges are not the same colour
       anywhere between 15% and 90%. The moon's haze sits at x=0.19, which
       warms the left of the sky and not the right; the cloud crosses at a
       tilt, so it meets the two edges at different heights; and each dune
       path ends higher on the right than it starts on the left, so the
       crest and its shade land in different places. One symmetric gradient
       looked close and read as a join. Hence a left profile and a right
       profile, each covering its own half, meeting under the picture where
       nothing can see them.

       What the strips carry is only what is CONTINUOUS: the sky's fall to
       the horizon, the horizon haze, and each sand plane's body tone. Two
       things were tried here and taken back out, both for the same reason.

       The cloud, first: it crosses the sky at a tilt, so reproducing it as
       a flat band puts a 320x48 rectangle of lighter blue beside the
       picture — the tilt is the whole shape of it, and a horizontal band
       keeps none of that.

       Then the dune crest rim-lines, which looked like the opposite case
       and are not. Their peaks were measured per edge (left ~60.4/68.2/
       77.7/90.1%, much brighter than the right's, the light being upper
       left) and painted in as narrow spikes, and the seam lined up exactly.
       At 320px wide they still read as STRIPES. A crest is a curve; the
       horizontal extension of its endpoint is only true for a few pixels,
       and past that the eye is being shown a straight line where it has
       just been told there is an arc. The horizon survives the same
       treatment because it is a broad tonal transition, not a drawn edge.

       So: a tone can be continued, a line cannot. Anything with a SHAPE
       stops at the frame; only gradients cross it.

       The flat layer underneath is for the rarer taller-than-16:9 case,
       where the strip falls above the sky and below the last dune instead:
       it carries the two colours the picture ends on, split at the middle,
       which is where the plate's own centre always sits.

       If the artwork's edges change, re-measure. Do not hand-edit these. */
  /* THE LETTERBOX FOLLOWS THE SKY TOO — or the picture spends the daytime
     in a black frame, which is what it did until this split.

     The flat layer is the one that shows on any window TALLER than 16:9
     (every phone, most laptop windows), and it is two tokens, so it turns
     with everything else for free. The two measured edge gradients are a
     different animal: 54 stops sampled off the NIGHT artwork, and there is
     no arithmetic that turns a measurement of one picture into a
     measurement of another. So they fade out as the sun comes up and hand
     the wide case back to the flat layer.

     Not as fine as measured day edges would be — the wide letterbox goes
     from a continuation of the picture to a plain two-tone by noon — but it
     is honest at every hour, it keeps the night craft exactly as measured,
     and it crossfades rather than switching. Re-measuring against the
     daylight artwork is the upgrade, and it is a measuring job.

     The lower half is NOT flat --dune-d2, even though that is the token the
     nearest dune band ends on. A grain pattern is laid over everything below
     y=500 in overlay blend, which lifts the artwork's last row well clear of
     the raw token — matching the token exactly puts a visible step right at
     the plate's edge. So the slab starts where the grain leaves the picture
     and darkens to the token at the window's floor, which also reads as the
     dunes running on into shadow rather than as a border. */
  body.erg-home .face-ornament {
    margin: 0;
    min-height: 100svh;
    position: relative;
    display: flex; align-items: center; justify-content: center;
    --erg-plate-h: min(100svh, calc(100vw / 1.7778));
    background-image: linear-gradient(to bottom,
      color-mix(in srgb, var(--co-stone-deep) var(--cl), var(--sky-1)) 50%,
      color-mix(in srgb, var(--dune-d2) 70%, var(--dune-d1)) 50%,
      var(--dune-d2) 100%);
  }
  /* Positioned, z-index auto, and earlier in paint order than .plate (which
     is position:relative), so it sits over the flat layer and under the
     picture without a stacking context of its own. */
  body.erg-home .face-ornament::before {
    content: ""; position: absolute; inset: 0; pointer-events: none;
    /* Faded by the cloud on the same grounds as by the sun: these 54 stops
       are a measurement of the CLEAR night artwork, and a closed lid is no
       more that picture than noon is. Under one, they hand the wide case
       back to the flat layer above, which does follow the lid. */
    opacity: calc((1 - var(--daylight)) * (1 - var(--cloud)));
    background-image:
      linear-gradient(to bottom,
        #070b16 0%,    #0a1021 6.7%,  #0d162b 13.3%, #0e182f 15.6%,
        #13203c 24.4%, #162542 28.9%,
        #1c2c4a 33.3%, #243551 37.8%, #313d53 42.2%, #3e4555 46.7%,
        #4c4e58 51.1%, #665e58 55.6%, #726557 57.8%, #6d5a45 60.0%,
        #614f3a 62.2%, #534432 66.7%, #443727 68.9%, #4d3f2d 71.1%,
        #443728 75.6%, #3a2e22 80.0%, #352b1f 84.4%, #2f261c 88.9%,
        #261e14 91.1%, #20180f 95.6%, #17110a 100%),
      linear-gradient(to bottom,
        #070b16 0%,    #0a1121 6.7%,  #0d162c 13.3%, #101c36 20.0%,
        #152340 26.7%, #182846 31.1%,
        #1b2d4c 35.6%, #203251 40.0%, #2b3851 44.4%, #353e52 48.9%,
        #46464f 53.3%, #5e5349 57.8%, #4a3c2b 60.0%, #614f3a 62.2%,
        #5b4a36 64.4%, #372c1f 66.7%, #453827 68.9%, #4d3f2d 71.1%,
        #2b2218 75.6%, #3a2e22 80.0%, #362b20 84.4%, #221a11 88.9%,
        #271f14 91.1%, #20180f 95.6%, #17110a 100%);
    background-size: 50% var(--erg-plate-h), 50% var(--erg-plate-h);
    background-position: left center, right center;
    background-repeat: no-repeat;
  }
  /* the plate IS the picture box here, which is also what finally lines the
     chip layer up with the artwork: .face-labels is inset:0 on the PLATE,
     so while the plate was a full-width column and the stage a narrower
     box inside it, every chip was measured against the wrong rectangle */
  body.erg-home .face-ornament .plate {
    width: 100%;
    max-width: calc(100svh * 1.7778);
    aspect-ratio: 1600 / 900;
  }
  body.erg-home .face-ornament .stage {
    width: 100%; height: 100%;
    max-width: none; margin: 0;
    aspect-ratio: auto;
  }
  .face-ornament .scene-wide { display: block; }
  .face-ornament .scene-tall { display: none; }
  /* the chips annotate the landscape; a 46px drawing inside each one
     would compete with the scene it is pointing at */
  .face-ornament .fl-mark { display: none; }

  .face-ornament .face-labels {
    position: absolute; inset: 0;
    display: block; gap: 0;
    margin: 0; padding: 0; border: 0;
    pointer-events: none;               /* let taps through to the scene … */
  }
  /* ── NO GLASS, AND NOT ON UNTIL ASKED ──
       These were filled dark plates at full contrast, thirteen of them, and
       over flat vector art that was merely busy. Over a rendered object it
       is worse than busy: a solid box at full contrast sitting on a
       photograph is the visual grammar of a debug overlay, and it makes the
       whole picture read as instrumented rather than directed. The first
       render landed on 2026-08-08 and the effect was immediate.

       So the plate goes and the type carries itself — letterspaced mono on
       a soft dark halo, sitting ON the sand rather than on a card laid over
       it. The halo is a text-shadow and it is load-bearing: these sit over
       a scene whose value runs from near-black sky to a lit crest, and
       nothing else keeps the same string legible across both.

       And they start hidden. The picture is the page; the names are what
       you ask for. */
  /* The chips grow with the picture past a notebook. At a fixed 11.5px they
     were a third of their notebook size against the drawings on a 2560
     screen (2026-10-19). Measured against the plate, not the window: the
     plate stops at 16:9 of the height, so an ultrawide is not a bigger
     picture. 1366 and below keep the sizes the notes further down argue
     for; each factor below is its notebook size over 1850px. */
  .face-ornament { --fl-w: min(100vw, 177.78svh); }
  .face-ornament .flabel {
    position: absolute; pointer-events: auto;   /* … but not through the chips */
    max-width: clamp(220px, calc(var(--fl-w) * .1189), 330px);
    transform: translateY(-50%);
    padding: 0; border: 0; border-radius: 0;
    background: none;
    /* The wide outer glow is a NIGHT tool: over a dark sky it is invisible
       and only lifts the letters, but the same glow inverted to near-white
       over a blue one is a fog bank with a word in it. It fades out as the
       sky fills; the two tight ones stay, and they are enough once the ink
       itself has gone dark. */
    text-shadow: 0 1px 2px var(--fl-halo), 0 0 9px var(--fl-halo),
                 0 0 22px color-mix(in srgb, var(--fl-halo) calc(75% * (1 - var(--daylight))), transparent);
  }
  .face-ornament .flabel .fl-go { display: flex; flex-direction: column; gap: 1px; min-height: 0; padding: 0; }
  /* The floors went up a pixel each (11.5 → 12.5, 9.5 → 10.5) and the gloss
     from 68 % to 78 % after the 5.11.2026 first-visit test, where two adults
     on a laptop called the second lines faint. The multipliers are untouched,
     so 1850 px and up draw exactly as before. */
  .face-ornament .flabel .fl-name {
    font-family: var(--font-mono);
    font-size: clamp(12.5px, calc(var(--fl-w) * .006216), 17px); font-weight: 500;
    letter-spacing: 0.17em; text-transform: uppercase;
    color: var(--fl-ink);
  }
  /* Two stacked mono uppercase lines would read as one muddy block, so the
     sense drops a size and most of its contrast — it is the gloss, not the
     name. */
  /* 68%, not the 55% this started at. Over the night sky 55% was elegant;
     over the dark warm sand of the lower third it was gone. One value has
     to serve both grounds and it is set by the harder one. */
  .face-ornament .flabel .fl-sense {
    font-size: clamp(10.5px, calc(var(--fl-w) * .005135), 14px); letter-spacing: 0.13em;
    color: color-mix(in srgb, var(--fl-ink) 78%, transparent);
  }
  /* The moon's chip while the disc is away: the phone's answer (a crescent in
     the name's own ink) for the hours the drawing is gone, so the name is a
     caption for something rather than for an empty patch of sky. */
  .face-ornament .flabel.fl-away .fl-name::before {
    content: ""; display: inline-block; width: .95em; height: .95em;
    margin-right: .45em; vertical-align: -.1em; background: currentColor;
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M14.5 2.5a9.5 9.5 0 1 0 7 15.9A8 8 0 0 1 14.5 2.5z'/%3E%3C/svg%3E") center / contain no-repeat;
            mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M14.5 2.5a9.5 9.5 0 1 0 7 15.9A8 8 0 0 1 14.5 2.5z'/%3E%3C/svg%3E") center / contain no-repeat;
  }
  /* Stays italic (the base rule's doing, and right — it is the human aside
     of §3, not a label), which is also what keeps it from colliding with a
     mono uppercase name one line above it. A size under the name so the
     name still leads: the tracking makes mono LOOK bigger without being
     taller, and at equal size the italic won. */
  .face-ornament .flabel .fl-now {
    display: block; -webkit-line-clamp: none;
    white-space: nowrap; text-overflow: ellipsis;
    margin-top: 1px;
    font-size: clamp(11px, calc(var(--fl-w) * .005946), 16px); color: color-mix(in srgb, var(--fl-ink) 78%, transparent);
  }
  .face-ornament .flabel .fl-now .what { color: var(--fl-ink); }
  .face-ornament .flabel.fl-owner .fl-name::after { color: color-mix(in srgb, var(--fl-ink) 40%, transparent); }

  /* ── THREE VOLUMES: SILENT · WHISPERED · SPOKEN ──

     Silent is the resting state, and it is the whole reason for the rest:
     a landscape with thirteen permanent captions is a diagram, and this
     one has stopped being a diagram.

     Whispered is the scene under the pointer. Not each chip on its own
     hover — the ornament's. A chip that only appears once you are already
     on it cannot be found, because you would have to know it was there to
     go looking, and a hidden box with pointer-events:none cannot be
     hovered at all. Approaching the picture is the gesture; the names
     surface together and you read them from a distance.

     Spoken is the one you mean, plus the arrival glance and any keyboard
     focus. Those all deserve full contrast; the other twelve stay behind.

     THE HIDE IS GATED ON .scene-live, and that is not an implementation
     detail. Same gate the phone ledger uses, for the same reason: face.js
     sets it only once every listed module has actually wired, so a visitor
     with no JS — whose drawings are inert, and who therefore navigates by
     these labels and nothing else — keeps every drawn one on  exactly
     as before. The hiding is an enhancement and it fails in the safe
     direction.

     pointer-events travel with the opacity. A 220px box at opacity 0 is
     still a box, and one per drawn object floats over the artwork eating the
     clicks meant for the objects underneath. */
  body.scene-live .face-ornament .flabel {
    opacity: 0; pointer-events: none;
    transition: opacity .28s ease, color .18s ease;
  }
  /* A WHISPER IS NAMES ONLY. The first cut faded all three lines up
     together at .55 and it failed twice over: thirteen names each with a
     subtitle and a live line is the ledger again, which is the thing just
     taken off the picture — and .55 of an already-quiet sense line simply
     vanishes over the dark warm sand in the lower third, so the half that
     survived was the half over the sky. Names carry the map; the other two
     lines are what you get for pointing. */
  body.scene-live .face-ornament .flabel .fl-sense,
  body.scene-live .face-ornament .flabel .fl-now {
    opacity: 0; transition: opacity .28s ease;
  }
  body.scene-live .face-ornament:hover .flabel {
    opacity: .8; pointer-events: auto;
  }
  /* A SCREEN WITH NO POINTER HAS NO WHISPER. A tablet, or a phone turned on
     its side — wider than 760px, so it gets this landscape — cannot rest a
     finger over the picture to ask for the names, so the resting state would
     be silent for good and every object a guess. There the names stay up at
     the whisper's volume, and the two lines under each stay out of the way:
     at a third of a desk's size they would turn the picture back into the
     ledger. The portrait composition cannot stand in on its side: its crop
     is anchored to the bottom, and on a screen wider than tall it would keep
     only the camp. */
  @media (hover: none) {
    body.scene-live .face-ornament .flabel { opacity: .8; pointer-events: auto; }
    body.scene-live .face-ornament .flabel :is(.fl-sense, .fl-now) { display: none; }
  }
  /* Listed after the whisper on purpose — identical specificity, so source
     order is what decides, and full contrast has to be able to win. */
  body.scene-live.erg-reveal .face-ornament .flabel,
  body.scene-live .face-ornament .flabel:is(.is-hot, :hover, :focus-within) {
    opacity: 1; pointer-events: auto;
  }
  body.scene-live.erg-reveal .face-ornament .flabel :is(.fl-sense, .fl-now),
  body.scene-live .face-ornament .flabel:is(.is-hot, :hover, :focus-within) :is(.fl-sense, .fl-now) {
    opacity: 1;
  }
  /* The chip has no panel left to light up, so the colour shift IS the
     feedback — and it is the half of the pair that says WHICH drawing the
     name belongs to, the drawing's own glow being the other half. */
  .face-ornament .flabel.is-hot .fl-name,
  .face-ornament .flabel:hover .fl-name,
  .face-ornament .flabel:focus-within .fl-name { color: var(--ss-accent); }
}

/* ── A TABLET HELD UPRIGHT ──
     Until 2026-10-19 the scene was chosen by width alone, so a portrait
     tablet (761-1199 wide) got the landscape plate: a 16:9 strip a third of
     the screen tall, floating in paint. It now gets the portrait composition,
     like a phone — but NOT the phone's crop. That one is `slice` from the
     bottom on a screen about as tall as the canvas, and on a 3:4 tablet the
     same crop takes a third of the sky, which is where the moon, the wind
     and the tear live. So the drawing is sized to its own 430:920 here, full
     height and centred, and the margins either side are painted the sky
     falling to the sand rather than left black. */
@media (min-width: 761px) and (max-width: 1199px) and (orientation: portrait) {
  .face-ornament { margin: 0; }
  .face-ornament .stage {
    background-image: linear-gradient(to bottom,
      var(--sky-1) 0%, var(--sky-3) 26%, var(--sky-5) 44%,
      var(--dune-a2) 46.5%, var(--dune-b2) 62%, var(--dune-c2) 80%, var(--dune-d2) 100%);
  }
  .face-ornament .scene-tall {
    inset: 0 auto 0 50%;
    width: auto; height: 100%;
    aspect-ratio: 430 / 920;
    translate: -50% 0;
  }
}

/* ── THE EMPTY DESERT'S FRONT DOOR ──
   A scene with nothing awake in it draws its name in the sky and a way in
   under it. Both used to be a caption at the foot of the ruin, eleven units
   high at sixty percent, which read as a watermark, and the way in was a word
   rather than a link. The ink follows the names in the portrait and the week:
   dark by day and pale by night, on a halo of the opposite, so one set of
   rules holds at noon and at three in the morning. Font sizes are in the
   drawing's own units, so the halos are in em and follow whichever of the two
   compositions the text stands in. */
.face-ornament .erg-front text {
  font-family: var(--font-mono, monospace);
  paint-order: stroke fill;
  stroke: var(--tl-halo);
  stroke-linejoin: round;
}
.face-ornament .erg-name {
  font-weight: 500;
  letter-spacing: .06em;
  fill: var(--tl-fill);
  stroke-width: .12em;
  stroke-opacity: .5;
}
.face-ornament .erg-fullname {
  letter-spacing: .16em;
  fill: color-mix(in srgb, var(--tl-fill) 86%, transparent);
  stroke-width: .24em;
  stroke-opacity: .55;
}
.face-ornament .erg-tagline {
  letter-spacing: .12em;
  fill: color-mix(in srgb, var(--tl-fill) 70%, transparent);
  stroke-width: .24em;
  stroke-opacity: .5;
}
.face-ornament .erg-enter { cursor: pointer; }
.face-ornament .erg-enter-pad { fill: transparent; }
.face-ornament .erg-enter-rim {
  fill: color-mix(in srgb, var(--tl-halo) 42%, transparent);
  stroke: color-mix(in srgb, var(--tl-fill) 60%, transparent);
  transition: fill .15s ease, stroke .15s ease;
}
.face-ornament .erg-enter-word {
  font-weight: 500;
  letter-spacing: .18em;
  fill: var(--tl-fill);
  stroke-width: .2em;
  stroke-opacity: .35;
}
.face-ornament .erg-enter:hover .erg-enter-rim,
.face-ornament .erg-enter:focus-visible .erg-enter-rim {
  fill: color-mix(in srgb, var(--tl-halo) 72%, transparent);
  stroke: var(--ss-accent);
}
.face-ornament .erg-enter:active .erg-enter-rim {
  fill: color-mix(in srgb, var(--ss-accent) 28%, transparent);
}
/* The doorway as a way in: the same faint ember rim the signed-in doorway
   wears, brighter under a pointer or a finger. */
.face-ornament .erg-door-in { cursor: pointer; }
.face-ornament .erg-door-rim {
  stroke: var(--co-ember);
  opacity: .35;
  transition: opacity .15s ease;
}
.face-ornament .erg-door-in:is(:hover, :focus-visible, :active) .erg-door-rim { opacity: .9; }

/* reduced motion — still navigates, just no flight or idle animation */
@media (prefers-reduced-motion: reduce) {
  .face-ornament .hearth, .face-ornament .ayat-cap, .face-ornament .ayat-halo,
  .face-ornament .wind, .face-ornament .glitch, .face-ornament .radiant,
  .face-ornament .beam, .face-ornament .feed-lamp { animation: none !important; }
  /* the rare pair have to be HIDDEN, not merely stopped: frozen at 0% they
     would park a streak and a sand ridge in the scene permanently */
  .face-ornament .shooting, .face-ornament .wormsign { animation: none !important; opacity: 0 !important; }
  /* the weather is INFORMATION, so stopping it must not delete it: rain and
     snow stand still and the fork stays struck. A frozen sheet still says
     "it is raining in Piešťany", which is the whole point of drawing it. */
  .face-ornament .rainfall, .face-ornament .snowfall,
  .face-ornament .cloudbank, .face-ornament .sandblow { animation: none !important; }
  .face-ornament .bolt { animation: none !important; opacity: .55 !important; }
  .face-ornament .face-portrait { transition: none !important; }
}
