/* ============================================================================
   Stinson CRM -- Daylight: the live wallpaper, and the sleep screen
   ----------------------------------------------------------------------------
   Two surfaces over one renderer (crm/js/daylight.js), mounted by
   crm/js/daylight_boot.js.

   1. THE WALLPAPER reuses the picture wallpaper's whole machinery. When it is
      on, sas_theme.js sets `data-sas-calbg="1"` exactly as a photograph does,
      so every glass rule already written for the wallpaper -- the calendar's
      toolbar capsules, the Board's hero, chat's log pane, the Flights rails --
      fires unchanged, at the densities each of those was solved for. It also
      sets `data-sas-daylight="1"`, which is what this file keys off, and which
      is the ONLY new attribute. If you are hunting a wallpaper bug, check both.

      The scene mounts as a real element rather than as `--cal-bg-image`,
      because a canvas cannot be a background-image. It sits at z-index -2, one
      layer BELOW the `body::before` those four pages already declare, so the
      order does not depend on where in <body> the element is appended. The
      picture layer is blanked in the same rule, so the two can never stack.

      THE SCRIM IS NOT DUPLICATED. `body::after` still carries it, still at the
      strength slider's alpha, in the theme's own `--body-bg`. Daylight paints
      under exactly the same veil a photograph does, which is what keeps the
      contrast arithmetic in all four page stylesheets valid.

   2. THE SLEEP SCREEN is a full-viewport overlay on <body>. It is a
      screensaver and NOT a lock: no password, no barrier, and any input at all
      dismisses it. That is deliberate on a system carrying crew-safety data --
      see the exclusion list in daylight_boot.js for the pages it must never
      cover at all.

   Bump ASSET_VERSION in core/admin_site.py after editing this file.
   ========================================================================= */

/* ══════════════════════════════════════════════════════════════════════
   The scene itself -- shared by both surfaces
   ----------------------------------------------------------------------
   The stage is a fixed 3:2 box scaled to cover its host and centred; its
   width/height are written by daylight.js. Everything here assumes that,
   because the sky mask has to stay in register with the ridge in the
   photograph: stretching either one puts the skyline out of alignment with
   the mask it is cut by, and the sun then sets through solid rock.
   ══════════════════════════════════════════════════════════════════════ */
.sas-dl-stage {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  isolation: isolate;
  pointer-events: none;
  background: #05070a;
}
.sas-dl-stage > * {
  position: absolute;
  inset: 0;
  display: block;
}
.sas-dl-ph {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: brightness(var(--ph-b, 1)) saturate(var(--ph-s, 1)) contrast(var(--ph-c, 1));
}
/* Ground light. Multiply, so the dark forest stays dark and the mid-tone rock
   takes the colour -- which is how light actually falls off. */
.sas-dl-land {
  mix-blend-mode: multiply;
  background: var(--c-land, #fff);
}
/* The whole sky -- gradient, stars, Milky Way, meteors, moon, sun -- is one
   canvas, masked to the sky region so the ridge occludes all of it. The
   mask-image URL is set inline per instance, because it comes from
   {% static %} and this stylesheet cannot know it. */
.sas-dl-sky {
  width: 100%;
  height: 100%;
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}
/* Moonlight and low sun catching the snow. Land highlights only -- no
   luminance cut separates snow from overcast sky, so the sky was excluded by
   mask at build time. */
.sas-dl-spec {
  background-position: center;
  background-size: 100% 100%;
  background-repeat: no-repeat;
  mix-blend-mode: screen;
  opacity: var(--spec-a, 0);
}
.sas-dl-warm {
  background: radial-gradient(72% 62% at var(--sun-x, 50%) var(--sun-y, 100%),
    var(--c-warm, transparent) 0%, transparent 66%);
  mix-blend-mode: soft-light;
  opacity: var(--warm-a, 0);
}
.sas-dl-vig {
  background: radial-gradient(120% 96% at 50% 44%, transparent 42%, #000 128%);
  opacity: var(--vig-a, 0.12);
}

/* ══════════════════════════════════════════════════════════════════════
   1. Wallpaper
   ══════════════════════════════════════════════════════════════════════ */

/* The layer the scene mounts into. Bleeds past the viewport by twice the blur
   radius for the same reason `body::before` does: a blurred box samples
   nothing outside itself, so a flush layer fades to transparent at all four
   edges and the scrim reads as a bright border. */
.sas-dl-bg {
  position: fixed;
  inset: calc(-2 * var(--cal-bg-blur, 0px) - 4px);
  z-index: -2;
  overflow: hidden;
  pointer-events: none;
  /* The scene is a leaf as far as the page is concerned -- it has no bearing
     on anything's layout and paints nowhere else. Saying so keeps a redraw
     off the critical path of a page that may have a year-wide gantt or ten
     Console cards in it. */
  contain: layout paint;
  filter: blur(var(--cal-bg-blur, 0px));
  /* Mounted after the body is parsed, so it arrives a beat after the theme.
     A short fade makes that read as the page settling rather than a flash. */
  animation: sas-dl-in 320ms ease-out both;
}

/* Blank the picture layer. Specificity, not order: this file loads in
   base_site.html's extrastyle and the four page stylesheets load after it, so
   file order cannot win. `html[data-sas-calbg][data-sas-daylight] body::before`
   is (0,2,2) against their (0,1,2), which decides it wherever they sit. The
   body-class list is deliberately restated rather than using a bare `body`:
   these four are the only pages that declare the pseudo-elements at all. */
html[data-sas-calbg="1"][data-sas-daylight="1"] body.sas-calendar-page::before,
html[data-sas-calbg="1"][data-sas-daylight="1"] body.sas-board-page::before,
html[data-sas-calbg="1"][data-sas-daylight="1"] body.sas-chat-page::before,
html[data-sas-calbg="1"][data-sas-daylight="1"] body.sas-flights-page::before,
html[data-sas-calbg="1"][data-sas-daylight="1"] body.sas-field-tracking-page::before,
html[data-sas-calbg="1"][data-sas-daylight="1"] body.sas-clients-page::before,
html[data-sas-calbg="1"][data-sas-daylight="1"] body.sas-ops-page::before,
html[data-sas-calbg="1"][data-sas-daylight="1"] body.sas-airspace-page::before,
html[data-sas-calbg="1"][data-sas-daylight="1"] body.sas-blu-page::before,
html[data-sas-calbg="1"][data-sas-daylight="1"] body.sas-userform-page::before,
html[data-sas-calbg="1"][data-sas-daylight="1"] body.sas-settings-page::before {
  background-image: none;
  animation: none;
}

/* THE SCRIM BACKS OFF AT NIGHT. --sas-dl-scrim-k is published on <html> by
   the renderer: 1 while the sun is up, ~0.34 at full night, smoothly between.

   The scrim exists to keep the interface legible over a BRIGHT photograph. At
   night the scene is already darker than the page colour it is being veiled
   with, so the veil protects nothing and only crushes what is left -- measured,
   a solar-midnight sky composites to rgb(17,20,34) against a page of
   rgb(16,16,20), which is the "black screen". Daytime is untouched: k is 1
   there and the full strength the sliders were solved against still applies.

   It is `opacity` on the scrim layer, not a recomputed colour, because
   body::after carries nothing but that flat fill -- there is no text in it to
   fade and no other property to disturb. */
html[data-sas-calbg="1"][data-sas-daylight="1"] body.sas-calendar-page::after,
html[data-sas-calbg="1"][data-sas-daylight="1"] body.sas-board-page::after,
html[data-sas-calbg="1"][data-sas-daylight="1"] body.sas-chat-page::after,
html[data-sas-calbg="1"][data-sas-daylight="1"] body.sas-flights-page::after,
html[data-sas-calbg="1"][data-sas-daylight="1"] body.sas-field-tracking-page::after,
html[data-sas-calbg="1"][data-sas-daylight="1"] body.sas-clients-page::after,
html[data-sas-calbg="1"][data-sas-daylight="1"] body.sas-ops-page::after,
html[data-sas-calbg="1"][data-sas-daylight="1"] body.sas-airspace-page::after,
html[data-sas-calbg="1"][data-sas-daylight="1"] body.sas-blu-page::after,
html[data-sas-calbg="1"][data-sas-daylight="1"] body.sas-userform-page::after,
html[data-sas-calbg="1"][data-sas-daylight="1"] body.sas-settings-page::after {
  opacity: var(--sas-dl-scrim-k, 1);
}

/* ══════════════════════════════════════════════════════════════════════
   The theme cross-fades with the sky
   ----------------------------------------------------------------------
   `.sas-dl-fade` is put on <html> by sas_theme.js immediately BEFORE it
   writes the new theme attributes, and taken off once the sweep has landed.
   The order matters: a CSS transition only starts if transition-property is
   already in force when the computed value changes, so adding this after the
   theme had been applied would do nothing at all.

   Custom properties themselves do not transition. What transitions is every
   USE of them -- `background-color: var(--body-bg)` interpolates perfectly
   well when --body-bg changes underneath it -- which is why this targets the
   handful of colour properties rather than the tokens.

   PSEUDO-ELEMENTS ARE LISTED SEPARATELY BECAUSE `body *` DOES NOT MATCH THEM,
   and that omission was most of what still looked like a hard cut (fixed
   2026-07-25). The two biggest surfaces on a wallpapered page are both
   pseudo-elements: `body::after` is the full-viewport scrim, which goes from
   near-black to near-white with the theme, and `.sas-settings-head::before` /
   the other hero panes are the glass. Everything else faded around them while
   the largest thing on the screen snapped. `body::before`/`::after` need their
   own two selectors on top of `body *::before`/`::after`, which do not cover
   body itself.

   box-shadow is in the list for the same reason -- panels and the hero pane
   carry theme-dependent shadows that were popping.

   `background-image` IS DELIBERATELY NOT IN THE LIST, and that is a measured
   negative result rather than an oversight. THE TOOLBAR STILL HARD-CUTS AND
   CANNOT BE FIXED FROM HERE. `#header` is painted with
   `linear-gradient(180deg, var(--sas-chrome-grad-a), var(--sas-chrome-grad-b))`,
   so its fill is a background-IMAGE, not a background-color -- and Chrome does
   not interpolate background-image between two gradients at all. Verified with
   a probe on this page: two 2-stop `linear-gradient`s differing only in their
   colours, changed by class, produce ZERO transitions -- with plain `rgb()`
   stops as well as `color(srgb ...)` ones, so it is not the colour syntax.
   Listing the property therefore bought nothing and only claimed to.

   The toolbar's border and ink DO fade with everything else; it is the fill
   that jumps. Fixing it means painting `#header` with a background-COLOUR
   instead of a two-stop gradient -- a design change in chrome.css, which is
   contended and not this file's to make. The gradient is white to near-white,
   so it would be close to imperceptible, but it is somebody's call, not a
   side effect of a transition fix.

   DO NOT ADD `opacity`, AND DO NOT REACH FOR `all`. The renderer writes
   --sas-dl-scrim-k onto <html> every frame and `body::after` consumes it as
   opacity -- so a transition on that property would fight the scene's own
   night ramp and smear it. The list is deliberately every property that
   carries the THEME and none that carries the SKY.

   SCOPED AWAY FROM THE GANTT ON PURPOSE. The crew calendar's table is a year
   wide and tens of thousands of cells; putting a 1.6s colour transition on
   every one of them is the one place this would be felt. It is excluded, and
   the table simply swaps -- it sits on an opaque card, so it is not what the
   eye is following during a sky sweep anyway.
   ══════════════════════════════════════════════════════════════════════ */
html.sas-dl-fade body,
html.sas-dl-fade body *,
html.sas-dl-fade body::before,
html.sas-dl-fade body::after,
html.sas-dl-fade body *::before,
html.sas-dl-fade body *::after {
  /* Duration AND DELAY both come from the renderer, via
     SAS_DAYLIGHT.sweepTiming(): the delay holds the interface still while the
     sky is still night, and the fade is centred on the moment the sky itself
     crosses over. Without the delay the cards were fully light while the sky
     was still black, because a sweep spends most of its length in deep night
     or full day and crosses between them in about a tenth of it.

     The curve is a smoothstep, which is the same shape the scene's own
     day/night ramp uses -- so the interface is not merely synchronised with
     the sky, it changes along the same curve. */
  transition:
    background-color var(--sas-dl-fade-ms, 700ms) cubic-bezier(0.5, 0, 0.5, 1) var(--sas-dl-fade-delay, 0ms),
    border-color var(--sas-dl-fade-ms, 700ms) cubic-bezier(0.5, 0, 0.5, 1) var(--sas-dl-fade-delay, 0ms),
    box-shadow var(--sas-dl-fade-ms, 700ms) cubic-bezier(0.5, 0, 0.5, 1) var(--sas-dl-fade-delay, 0ms),
    color var(--sas-dl-fade-ms, 700ms) cubic-bezier(0.5, 0, 0.5, 1) var(--sas-dl-fade-delay, 0ms),
    fill var(--sas-dl-fade-ms, 700ms) cubic-bezier(0.5, 0, 0.5, 1) var(--sas-dl-fade-delay, 0ms),
    stroke var(--sas-dl-fade-ms, 700ms) cubic-bezier(0.5, 0, 0.5, 1) var(--sas-dl-fade-delay, 0ms) !important;
}
/* Opted out, as a SECOND rule rather than a :not() on the one above. A
   `:not(.stinson-crew-table *)` has to be evaluated against every element on
   the page, and the page it would cost most on is exactly the one being
   excluded. Two flat rules are far cheaper to match than one clever one.

   The gantt: a year-wide table of tens of thousands of cells, and the one
   place a global colour transition would actually be felt. It sits on an
   opaque card, so it is not what the eye follows during a sky sweep anyway.

   The scene: driven frame by frame by the renderer, so a CSS transition on top
   would fight it and smear the sweep. */
html.sas-dl-fade .stinson-crew-table,
html.sas-dl-fade .stinson-crew-table *,
html.sas-dl-fade .stinson-crew-table *::before,
html.sas-dl-fade .stinson-crew-table *::after,
html.sas-dl-fade .sas-dl-bg,
html.sas-dl-fade .sas-dl-bg *,
html.sas-dl-fade .sas-dl-stage,
html.sas-dl-fade .sas-dl-stage *,
html.sas-dl-fade .sas-dl-stage *::before,
html.sas-dl-fade .sas-dl-stage *::after {
  transition: none !important;
}
/* Reduced motion zeroes it -- UNLESS Sky motion is set to "Always animate".
   That control forces SAS_DAYLIGHT.setForceMotion(), which is JS and reaches
   only the sun's sweep; this transition is CSS and was zeroed independently of
   it. The result was the worst of both: a sun travelling across four seconds
   and an interface snapping black-to-white in one frame, for exactly the user
   who had gone and asked for the animation. sas_theme.js mirrors the setting
   onto <html> as data-sas-skymotion so this query can see it.

   The default (Auto) still defers to the OS, which is not ours to overrule
   silently -- the override is opt-in and lives on the same row that describes
   what it does. */
@media (prefers-reduced-motion: reduce) {
  html.sas-dl-fade:not([data-sas-skymotion="always"]) body,
  html.sas-dl-fade:not([data-sas-skymotion="always"]) body *,
  html.sas-dl-fade:not([data-sas-skymotion="always"]) body::before,
  html.sas-dl-fade:not([data-sas-skymotion="always"]) body::after,
  html.sas-dl-fade:not([data-sas-skymotion="always"]) body *::before,
  html.sas-dl-fade:not([data-sas-skymotion="always"]) body *::after {
    transition-duration: 1ms !important;
    transition-delay: 0ms !important;
  }
}

@keyframes sas-dl-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .sas-dl-bg { animation: none; }
}

/* ══════════════════════════════════════════════════════════════════════
   2. Sleep screen
   ----------------------------------------------------------------------
   z-index is above every overlay in this admin (the palette sits at 9000, the
   chat modal at 400, Leaflet's panes below 1000) but deliberately below the
   integer ceiling, so a genuine alert can still be put over it later.
   ══════════════════════════════════════════════════════════════════════ */
.sas-dl-sleep {
  position: fixed;
  top: 0;
  left: 0;
  /* 100vw, not `right: 0`, so the screen also covers the CLASSIC scrollbar
     gutter. `inset: 0` stops at the layout viewport, which on Windows and
     Linux leaves a ~15px strip of the working page showing down the right
     edge of an otherwise full-screen photograph. (macOS overlay scrollbars
     hide the problem locally, which is exactly why it is worth stating.)

     The obvious alternative -- `overflow: hidden` on <html> while asleep --
     reflows the whole page underneath to 15px wider and back again on wake,
     which on the crew calendar means re-laying out a year-wide gantt for a
     cosmetic gain. A fixed element contributes no scrollable overflow, so
     100vw cannot introduce a horizontal scrollbar of its own. */
  width: 100vw;
  height: 100vh;
  height: 100dvh;
  z-index: 2147480000;
  overflow: hidden;
  contain: layout paint;
  background: #05070a;
  cursor: none;
  opacity: 0;
  transition: opacity var(--sas-dl-fade-in, 900ms) cubic-bezier(0.22, 1, 0.36, 1);
  /* Ink here is on a photograph, not on a themed surface, so it does NOT use
     the --sas-fg-* scale: those are solved against --sas-bg-1 and this is an
     arbitrary sky. Everything is near-white over a shadow, the same treatment
     the plate's own HUD uses. */
  color: #f2f4f7;
  font-family: var(--stinson-font, "Space Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif);
  text-shadow: 0 1px 3px rgb(0 0 0 / 0.7), 0 0 18px rgb(0 0 0 / 0.45);
}
.sas-dl-sleep.is-in { opacity: 1; }
.sas-dl-sleep.is-out {
  opacity: 0;
  transition-duration: var(--sas-dl-fade-out, 260ms);
}
.sas-dl-sleep[hidden] { display: none !important; }

/* The scrim under the readout.

   THIS IS CENTRED BECAUSE THE CONTENT IS. The two edge gradients it replaces
   were placed for a clock in the top-left corner and an almanac in the
   bottom-right, and between them they left the middle of the frame entirely
   unprotected -- which is exactly where the wordmark and the clock sit now.

   Contrast is solved against the worst sky, not the pretty one. The sun is in
   this frame ~3.4h a day at altitude 14-45 deg, which projects to y ~115-481
   at 1440x900 -- straight through the wordmark and the clock -- so the
   backdrop there really does reach white and is solved against white. A black
   veil needs alpha 0.561 for #f2f4f7 to clear 4.5:1 on white; the core is
   0.60 and every sample point lands 4.65:1 or better. The per-glyph
   text-shadow on .sas-dl-sleep is on top of that and is not counted.
   `node check_sleep_contrast.js` is the arithmetic -- re-run it before
   moving any stop, and read the SEQUENCE of stops, not just the core: the
   ellipse is large (72% x 60%) so the falloff reads as a vignette rather than
   a smudge behind the logo, which means the core alpha alone tells you very
   little about what is actually behind a glyph.

   THE STRENGTH TRACKS THE SKY, via --sas-dl-scrim-k -- the same number the
   wallpaper's own scrim uses, published on <html> by the renderer every
   frame: 1 while the sun is up, 0.34 at full night. A veil heavy enough for a
   white midday sky is ruinous at solar midnight, where the scene already
   composites darker than the ink and the only thing the veil can take away is
   the stars. Element opacity applies to the composited group, so this scales
   all three layers together and the checker's `veil * k` is exact. The
   fallback is 1 (full protection) because the token is unset until the first
   frame lands. */
.sas-dl-sleep-shade {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: var(--sas-dl-scrim-k, 1);
  background:
    radial-gradient(ellipse 72% 60% at 50% 46%,
      rgb(0 0 0 / 0.60) 0%,
      rgb(0 0 0 / 0.56) 42%,
      rgb(0 0 0 / 0.26) 72%,
      transparent 100%),
    linear-gradient(180deg, rgb(0 0 0 / 0.16) 0%, transparent 24%),
    linear-gradient(0deg, rgb(0 0 0 / 0.30) 0%, transparent 26%);
}

.sas-dl-hud {
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* ONE cell with two occupants, not a `1fr auto` row split: the stack takes
     the true optical centre of the viewport and the hint pins to the bottom
     of that same cell. Splitting the rows instead centres the stack in the
     space left over *above* the hint, lifting it off centre by half the
     hint's height -- a few pixels, and visible the moment the thing being
     centred is a large wordmark. */
  display: grid;
  place-items: center;
  padding: clamp(20px, 4vw, 54px);
  font-variant-numeric: tabular-nums;
  text-align: center;
}

.sas-dl-stack {
  grid-area: 1 / 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* The stack must never grow the grid: `place-items: center` would otherwise
     let a tall stack on a short screen push its own centre off the viewport,
     and the overlay clips rather than scrolls. */
  max-width: 100%;
}

/* The wordmark is white in this file, which is why it can sit on the plate in
   either theme -- unlike the toolbar, which swaps in the dark-ink lockup on
   the light-bar presets. Here the backdrop is always a photograph.

   Sized by WIDTH, not height. The lockup is 1200x364, so it is read as a
   horizontal mark: a height clamp scaled by vw sets its length indirectly and
   the mark drifts wider than intended on a wide screen. */
.sas-dl-brand {
  width: clamp(190px, 25vw, 420px);
  height: auto;
  opacity: 0.94;
  filter: drop-shadow(0 2px 7px rgb(0 0 0 / 0.55));
  margin-bottom: clamp(16px, 2.6vh, 34px);
}

.sas-dl-clock {
  display: flex;
  align-items: baseline;
  font-size: clamp(3.2rem, 12vw, 9.5rem);
  font-weight: 200;
  letter-spacing: -0.03em;
  line-height: 0.94;
}
.sas-dl-clock .sec {
  /* ZERO WIDTH, so the hours and minutes are what gets centred. Left in flow
     at its natural width the browser centres `12:40 52` as one group, which
     puts 12:40 about half the seconds' width left of true centre -- ~23px at
     the largest clamp -- while the wordmark directly above is centred
     exactly. The two then visibly disagree, and it reads as the LOGO being
     off-centre rather than the clock.

     Taking it out of flow with position:absolute solves the centring and
     breaks the alignment: `bottom` on the seconds resolves em against the
     SECONDS' own font-size (0.22em of the clock), so every value that looks
     like a baseline nudge is roughly a quarter the size it appears to be,
     and the digits hang about 19px low -- they read as a subscript. Staying
     in flow keeps `align-items: baseline` doing that job off the real font
     metrics, at any clamp, in any face the theme picks. text-indent shifts
     the glyphs without contributing width, which padding or margin would. */
  flex: 0 0 0;
  width: 0;
  white-space: nowrap;
  text-indent: 0.42em;
  font-size: 0.22em;
  opacity: 0.55;
  letter-spacing: 0.06em;
  font-weight: 400;
}

.sas-dl-date {
  margin-top: clamp(12px, 1.9vh, 24px);
  font-size: clamp(0.72rem, 1.5vw, 0.95rem);
  letter-spacing: 0.24em;
  /* Letter-spacing is added AFTER the last glyph too, so centred tracked-out
     type sits half a space left of centre. The indent puts it back. */
  text-indent: 0.24em;
  text-transform: uppercase;
  opacity: 0.82;
}

.sas-dl-hint {
  grid-area: 1 / 1;
  align-self: end;
  justify-self: center;
  font-size: clamp(0.62rem, 1.1vw, 0.72rem);
  letter-spacing: 0.18em;
  text-indent: 0.18em;
  text-transform: uppercase;
  opacity: 0.5;
  padding-bottom: clamp(10px, 3vh, 34px);
  animation: sas-dl-breathe 4.2s ease-in-out infinite;
}
@keyframes sas-dl-breathe {
  0%, 100% { opacity: 0.5; }
  50% { opacity: 0.24; }
}

@media (prefers-reduced-motion: reduce) {
  .sas-dl-hint { animation: none; }
  .sas-dl-sleep { transition-duration: 1ms; }
}

/* The wordmark is the point of this screen now, so it is SCALED on a small
   frame rather than hidden -- which is what the rule it replaces did, back
   when it was a 30px mark in a corner and the almanac beside it was the thing
   really competing for the space. Width and height are asked separately: a
   phone in portrait is short of width, a laptop with a short window is short
   of height, and the clock is what has to give in the second case. */
@media (max-width: 620px) {
  .sas-dl-brand { width: clamp(150px, 46vw, 250px); }
  .sas-dl-clock { font-size: clamp(2.9rem, 17vw, 5rem); }
}
@media (max-height: 480px) {
  .sas-dl-brand { width: clamp(130px, 20vw, 230px); margin-bottom: 10px; }
  .sas-dl-clock { font-size: clamp(2.4rem, 15vh, 4.4rem); }
  .sas-dl-date { margin-top: 8px; }
}

/* ══════════════════════════════════════════════════════════════════════
   3. Settings -> Appearance -> Wallpaper
   ----------------------------------------------------------------------
   Two rules, kept HERE rather than in admin_settings.css so that file is not
   a fifth contended one to patch. Both are inert on every other page.

   The picture tile goes away when Daylight is the source -- it is not a
   choice that means anything then. Hidden, not disabled: a greyed-out drop
   target still reads as somewhere to drop a file. The strength and blur rows
   below it stay, because they still drive the same scrim over the same four
   pages.
   ══════════════════════════════════════════════════════════════════════ */
.sas-panel.is-daylight .sas-set-row:has([data-sas-bg-drop]) { display: none; }

/* Auto's explanation of itself, and the "not the real sky" disclosure. Both
   use the page's own ink tokens -- unlike the sleep screen's marker, these sit
   on a themed panel, not on a photograph, so --sas-fg-2 is the solved value
   here and a hardcoded amber would be the one that failed a preset. */
.sas-dl-autonote {
  margin: -4px 0 0;
  font-size: 0.78rem;
  color: var(--sas-fg-2);
}
.sas-dl-pinnote {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin: 0 0 4px;
  font-size: 0.82rem;
  color: var(--sas-fg-2);
}
.sas-dl-pinnote > span { flex: 1 1 22ch; }
.sas-dl-autonote[hidden],
.sas-dl-pinnote[hidden] { display: none; }

/* SAS-DL-FADE2-BEGIN */
/* ══════════════════════════════════════════════════════════════════════
   Fade polish (2026-07-26) — appended, and it overrides the base fade rule
   above on source order + !important. Two changes, both CSS-only:

   1) TWO-PHASE FADE. The base rule ran every property at one duration, so at
      the midpoint of the flip both the surface and the ink on it were
      mid-grey — panels spent the middle of every fade at their lowest
      contrast, which reads as murk rather than as a transition. Ink (color /
      fill / stroke) now crosses in 55% of the window, CENTRED in it, so text
      commits to its destination side while the surfaces are still gliding.
      Both phases share the smoothstep and both are derived from the same two
      tokens the renderer publishes, so the sky-synced midpoint is unchanged:
      ink midpoint = surface midpoint = the sky's own crossover.

   2) THE GRADIENT SURFACES FADE NOW. The negative result documented above is
      still true — background-image never interpolates between two gradients —
      but a custom property REGISTERED with @property `syntax: "<color>"`
      interpolates when transitioned, and every USE of it, including inside a
      gradient or a color-mix(), repaints per frame from the interpolating
      value. So the tokens themselves are transitioned, on exactly the
      elements whose fill is a token-built gradient: #header (the toolbar
      fill, both the themes.css gradient and the chrome.css glass variant
      resolve through --sas-chrome-grad-a/b) and the two hero panes
      (.sas-con-hero-bg on the Board, .sas-settings-head::before on Settings,
      both painted from --sas-bg-0/2/3 when no wallpaper is on). A browser
      without @property ignores the registration and keeps today's snap.

      SCOPED TO THOSE ELEMENTS ON PURPOSE — do not add the token transitions
      to the global rule. On an element whose background-color ALSO resolves
      through the same token, the background-color transition would chase an
      already-interpolating input and the two easings compound into a slower,
      laggier curve. These elements consume the tokens only through gradients,
      so there is nothing to compound. The small tinted composites
      (.sas-con-tool[aria-pressed] and the amber env note) still snap for
      exactly that reason — both paint the token through background-color AND
      background-image — and they are a few hundred px² apiece.

      initial-value is a fallback for a token never defined anywhere; every
      admin page defines all six via borealis.css/themes.css, so the values
      here (the Dark preset's) should never be seen.
   ══════════════════════════════════════════════════════════════════════ */
@property --sas-chrome-grad-a { syntax: "<color>"; inherits: true; initial-value: #131316; }
@property --sas-chrome-grad-b { syntax: "<color>"; inherits: true; initial-value: #0d0d0f; }
@property --sas-chrome-line-strong { syntax: "<color>"; inherits: true; initial-value: rgba(255, 255, 255, 0.16); }
@property --sas-bg-0 { syntax: "<color>"; inherits: true; initial-value: #0a0a0b; }
@property --sas-bg-2 { syntax: "<color>"; inherits: true; initial-value: #16161a; }
@property --sas-bg-3 { syntax: "<color>"; inherits: true; initial-value: #1e1e23; }

/* The four timing values, derived once from the two the renderer publishes.
   Ink: 55% of the window, delayed 22.5% in — so it is centred (0.225 + 0.55 +
   0.225 = 1) and always finishes before the surfaces do, which keeps it
   inside the removal timeout sas_theme.js already arms. */
html.sas-dl-fade {
  --sas-dl-fade-srf: var(--sas-dl-fade-ms, 700ms);
  --sas-dl-fade-srfd: var(--sas-dl-fade-delay, 0ms);
  --sas-dl-fade-ink: calc(var(--sas-dl-fade-srf) * 0.55);
  --sas-dl-fade-inkd: calc(var(--sas-dl-fade-srfd) + var(--sas-dl-fade-srf) * 0.225);
}

/* The base rule restated as longhands with per-property timing. Same
   selectors, later in the file, !important — it wins on order. The gantt and
   scene opt-outs above are (0,2,1) against this rule's (0,1,2)/(0,1,3), so
   they keep winning on specificity with no restatement. */
html.sas-dl-fade body,
html.sas-dl-fade body *,
html.sas-dl-fade body::before,
html.sas-dl-fade body::after,
html.sas-dl-fade body *::before,
html.sas-dl-fade body *::after {
  transition-property: background-color, border-color, box-shadow, color, fill, stroke !important;
  transition-timing-function: cubic-bezier(0.5, 0, 0.5, 1) !important;
  transition-duration:
    var(--sas-dl-fade-srf), var(--sas-dl-fade-srf), var(--sas-dl-fade-srf),
    var(--sas-dl-fade-ink), var(--sas-dl-fade-ink), var(--sas-dl-fade-ink) !important;
  transition-delay:
    var(--sas-dl-fade-srfd), var(--sas-dl-fade-srfd), var(--sas-dl-fade-srfd),
    var(--sas-dl-fade-inkd), var(--sas-dl-fade-inkd), var(--sas-dl-fade-inkd) !important;
}

/* The toolbar. (1,1,2)/(1,1,3) beats both the base rule above and chrome.css's
   own transition on #header; box-shadow/border-color are restated so the
   is-scrolled treatment loses nothing. --sas-chrome-line-strong rides along
   for the nav separator's gradient, which lives on a child. */
html.sas-dl-fade body #header,
html.sas-dl-fade body #header * {
  transition-property:
    --sas-chrome-grad-a, --sas-chrome-grad-b, --sas-chrome-line-strong,
    background-color, border-color, box-shadow, color, fill, stroke !important;
  transition-timing-function: cubic-bezier(0.5, 0, 0.5, 1) !important;
  transition-duration:
    var(--sas-dl-fade-srf), var(--sas-dl-fade-srf), var(--sas-dl-fade-srf),
    var(--sas-dl-fade-srf), var(--sas-dl-fade-srf), var(--sas-dl-fade-srf),
    var(--sas-dl-fade-ink), var(--sas-dl-fade-ink), var(--sas-dl-fade-ink) !important;
  transition-delay:
    var(--sas-dl-fade-srfd), var(--sas-dl-fade-srfd), var(--sas-dl-fade-srfd),
    var(--sas-dl-fade-srfd), var(--sas-dl-fade-srfd), var(--sas-dl-fade-srfd),
    var(--sas-dl-fade-inkd), var(--sas-dl-fade-inkd), var(--sas-dl-fade-inkd) !important;
}

/* The two hero panes — gradient-painted only while no wallpaper is on; with
   one on, their glass variant is a background-color and the list below still
   covers it. No ink properties: both are empty layers holding no text. */
html.sas-dl-fade .sas-con-hero-bg,
html.sas-dl-fade .sas-con-hero-bg::after,
html.sas-dl-fade .sas-settings-head::before {
  transition-property:
    --sas-bg-0, --sas-bg-2, --sas-bg-3,
    background-color, border-color, box-shadow !important;
  transition-timing-function: cubic-bezier(0.5, 0, 0.5, 1) !important;
  transition-duration:
    var(--sas-dl-fade-srf), var(--sas-dl-fade-srf), var(--sas-dl-fade-srf),
    var(--sas-dl-fade-srf), var(--sas-dl-fade-srf), var(--sas-dl-fade-srf) !important;
  transition-delay:
    var(--sas-dl-fade-srfd), var(--sas-dl-fade-srfd), var(--sas-dl-fade-srfd),
    var(--sas-dl-fade-srfd), var(--sas-dl-fade-srfd), var(--sas-dl-fade-srfd) !important;
}

/* Reduced motion re-zeroed for EVERY selector this block adds, restated here
   because this block comes after the original reduced-motion rule and an
   override written only against the base selectors would leave the #header
   and hero rules running at full duration for exactly the users who asked
   for less. 1ms, never 0 — the same rule the original block follows. Keep
   this last in the block. */
@media (prefers-reduced-motion: reduce) {
  html.sas-dl-fade:not([data-sas-skymotion="always"]) body,
  html.sas-dl-fade:not([data-sas-skymotion="always"]) body *,
  html.sas-dl-fade:not([data-sas-skymotion="always"]) body::before,
  html.sas-dl-fade:not([data-sas-skymotion="always"]) body::after,
  html.sas-dl-fade:not([data-sas-skymotion="always"]) body *::before,
  html.sas-dl-fade:not([data-sas-skymotion="always"]) body *::after,
  html.sas-dl-fade:not([data-sas-skymotion="always"]) body #header,
  html.sas-dl-fade:not([data-sas-skymotion="always"]) body #header *,
  html.sas-dl-fade:not([data-sas-skymotion="always"]) .sas-con-hero-bg,
  html.sas-dl-fade:not([data-sas-skymotion="always"]) .sas-con-hero-bg::after,
  html.sas-dl-fade:not([data-sas-skymotion="always"]) .sas-settings-head::before {
    transition-duration: 1ms !important;
    transition-delay: 0ms !important;
  }
}
/* SAS-DL-FADE2-END */
