/* Copyright 2026 by John A Kline.  See LICENSE.txt for your rights.
   COPIED from weewx-celestial skins/Celestial/celestial.css (v9.4) -- that
   repo is the source of truth; re-copy this file when upgrading the
   weewx-celestial extension (the xtide.js/sky.js convention).
   This is the PANEL stylesheet: it styles only what the $celestial panels
   render, every class prefixed cel-, scoped so nothing reaches the host
   page.  Its sibling celestial-page.css is that report's OWN page chrome
   (bare body/header/h1/section rules) and must NEVER be copied here -- it
   would repaint the whole site.  Loaded AFTER css/celestial.css so the
   panels win inside their own subtree. */
/* Copyright (C)2022-2026 by John A Kline (john@johnkline.com)
 * Distributed under the terms of the GNU Public License (GPLv3)
 *
 * Stylesheet for the Celestial live report.  ALL colors live here: Cheetah
 * owns '#' in the .tmpl/.inc files, so hex literals must never appear there
 * -- the javascript reaches every color through a class in this file (the
 * SVG marks through the per-body fill- and stroke- classes).
 * The palette and type system are ported from the weewx-skyfield "Sky"
 * page (same author), so the two pages read as one family. */

/* The panels' own box model.  The bundled page's `*` reset lives in
 * celestial-page.css now, and a host skin setting none (or content-box)
 * would lay these out wrong: every width and padding here was authored
 * under border-box. */
[data-celestial], [data-celestial] *{box-sizing:border-box}

/* The night plate.  The two fragment selectors beside :root scope the
 * same tokens to the SVG of a dome fragment or a pass chart drawn on the
 * night plate INSIDE a light page: skyfield colors its charts inline but
 * its labels by class only, and those classes read the tokens (the
 * chart-label ones through --skylab/--conlab below), so without this a
 * night dome in a light page (a fragment set on `theme = dark` in a
 * light report) would wear the page's dark ink.  The fragment's own
 * data-dome-palette / data-pass-palette says which plate it is.  ONLY
 * on that mismatch: a fragment on the page's own plate takes its tokens
 * from :root like everything else, so a consumer's `:root{--brass:...}`
 * override reaches the charts too (the contract in the manual).  The
 * SVG only: the pass chart's head line (.passhead, HTML beside the SVG)
 * sits on the PAGE's surface and keeps the page's tokens. */
:root, .theme-dark, .theme-light .domefrag[data-dome-palette="night"] svg, .theme-light .passfrag[data-pass-palette="night"] svg{
  --night:#0A0F22;   /* page background */
  --vault:#111834;   /* cards and chips */
  --ink:#E9E4D4;     /* primary text */
  --muted:#C0C5D9;   /* secondary text */
  --brass:#E0C27F;   /* accent */
  --line:#2A3358;    /* borders, hairlines */
  /* Chart furniture -- the dial's rings, ticks and rim.  NOT --line: that
   * is the color of a section border, and the dial is drawn ON the surface
   * that border surrounds, where it measures 1.20-1.42:1.  This is
   * weewx-skyfield 2.2's night `grid` token (same value, same reason: it
   * fixed the identical inheritance on the dome's altitude rings and
   * meridian cross), so the two pages still read as one family.  --line
   * stays exactly as it is for the borders and hairlines it is for. */
  --grid:#6E7DBA;
  /* The sky charts' small labels (skyfield's sky.css values, kept in
   * step by test).  The star and ring labels are --muted on both plates;
   * the constellation names carry their own value. */
  --skylab:var(--muted); --conlab:#BBC5E3;
  /* Per-body identity colors (weewx-skyfield's night-plate values --
   * the traditional astronomy scheme: yellow sun, silver moon, gray
   * Mercury, pearly Venus).  Earth (the dial's center) and Proxima
   * (silver, like the stars) are celestial's own. */
  --c-sun:#FFD75E; --c-moon:#C9D0DA; --c-mercury:#9CA0AC; --c-venus:#F0E3BE;
  --c-mars:#D06C56; --c-jupiter:#D89A56; --c-saturn:#AC8F3E;
  --c-uranus:#35A8BE; --c-neptune:#5F85E6;
  --c-earth:#7FB4A2; --c-proxima:#C9D0DA;
  /* The halo: the color a mark is outlined in so it stays separate from
   * whatever it sits on, and the fill of the hollow inversions (a
   * satellite in shadow, a comet too faint to see).  skyfield's palettes
   * carry it under the same name; on the night plate it is the page
   * black, which is why this was spelled --night through 8.2. */
  --halo:#0A0F22;
  /* The three marks whose treatment DIFFERS IN KIND between the
   * plates, carried as tokens so a panel on its own plate inside a
   * page of the other still gets its own.  A custom property
   * resolves from the nearest ancestor that sets it, so the panel's
   * own theme class answers; a `.theme-light .cel-x` rule could not,
   * because a descendant selector matches on ANCESTRY ALONE and the
   * page would win however close the panel sat.  --chip-ring carries
   * the on/off, not a color: on night the chips wear no ring at all. */
  --chip-ring:0;
  --geo-ring-sun:var(--halo); --geo-ring-venus:var(--halo);
  --moon-dark:#1E2745; --moon-lit:#DDD8C4;
  --moon-rim:var(--c-moon); --moon-rim-op:.55;
  /* A comet below the horizon dims its tail no further than the plate
   * lets a mark stay findable (3.0:1 and APCA Lc 30 on the card). */
  --tail-below-op:.6;
  /* The three pale bodies need no edge on the night plate: their own
   * color IS the bright mark.  On paper they do -- see the light block.
   * Earth's --e- value is its center LABEL, text, so it is lifted from the
   * dot's green in the same hue: the dot's value is WCAG 7.44 but APCA
   * Lc -54.5 on the card, and text needs Lc 60 (8.56 / Lc -61.3 here). */
  --e-sun:#FFD75E; --e-moon:#C9D0DA; --e-venus:#F0E3BE; --e-earth:#92BFB0;
}
/* The light theme (theme = light, or auto with the sun up): the
 * paper-atlas plate, matching the 'light' palette the dome and the Next
 * Visible Pass chart are rendered with.  $celestial.theme_class resolves
 * the theme at generation time into this class on <html> -- it must sit
 * on the root element so color-scheme (and the canvas backdrop it
 * governs) flips with it, and it CANNOT be a browser-side toggle: the
 * embedded charts arrive as skyfield SVG with their colors already
 * inside them, and the javascript refetches them every 60 s.  The two
 * fragment selectors scope the same tokens to a light dome or chart
 * inside a dark page, and only there (see the night block above).
 *
 * Every value here is weewx-skyfield's PALETTES['light'], copied in --
 * the same cross-repo rule as the night values above, and the same
 * reason: two shades of one thing on one page is the failure mode.
 * Two values are this page's own to choose.  Proxima takes the light
 * plate's moon RING silver -- skyfield has no Proxima, and the stars on
 * paper are that color.  EARTH is the deliberate exception to the
 * copy-the-palette rule (8.3): skyfield draws Earth only in
 * its orrery, a panel this page does not embed, so the dial's center dot
 * is the one mark with no counterpart anywhere on this page to disagree
 * with -- and it keeps its own green identity on BOTH plates rather than
 * turning blue on paper as skyfield's earth_fill would.  The night green
 * is 2.35:1 on white, so paper takes a darker one of the same family:
 * 4.96:1 for the dot, 6.38:1 for the label.  Every OTHER body's light
 * value is skyfield's, and a test keeps it that way. */
.theme-light, .theme-dark .domefrag[data-dome-palette="light"] svg, .theme-dark .passfrag[data-pass-palette="light"] svg, :root:not(.theme-light) .domefrag[data-dome-palette="light"] svg, :root:not(.theme-light) .passfrag[data-pass-palette="light"] svg{
  --night:#EFECE2; --vault:#FFFFFF; --ink:#1D2C4E; --muted:#5C6672;
  --brass:#A44A08; --line:#C9CFD8; --grid:#7A899F;
  /* The paper plate hands the ring and star labels straight back to
   * --muted (sky.css's light rule), and the constellation names their
   * own value. */
  --skylab:var(--muted); --conlab:#57637B;
  --c-sun:#FACC15; --c-moon:#D6DAE0; --c-mercury:#52525B; --c-venus:#F0E4BE;
  --c-mars:#B23A24; --c-jupiter:#B06F2E; --c-saturn:#8F7524;
  --c-uranus:#20808F; --c-neptune:#3A63C4;
  --c-earth:#2E7D64;
  /* Proxima wears the stars' silver, which on paper is the light plate's
   * moon RING (#767E8A) -- its moon fill (#D6DAE0) is a 1.40:1 ghost on
   * white, and a star that cannot be seen is not a star. */
  --c-proxima:#767E8A;
  --halo:#FFFFFF;
  /* The paper plate's versions of the three: the chips take an inset
   * ring, the pale bodies' dial dots take their edge color instead of
   * a white halo that would outline them in nothing, and the moon's
   * rim goes full strength because its lit limb is nearly the page. */
  --chip-ring:.09375rem;
  --geo-ring-sun:var(--e-sun); --geo-ring-venus:var(--e-venus);
  --tail-below-op:.75;
  --moon-dark:#26314F; --moon-lit:#F2ECD8;
  --moon-rim:#868686; --moon-rim-op:1;
  /* Pale on paper: sun 1.53:1, moon 1.40, venus 1.27 against the white
   * panel -- as fills they are nearly not there.  skyfield's light
   * palette answers this with a `ring` per pale body, a darker edge in
   * the body's own hue, and these are those three values.  They serve
   * every EDGE the body draws: the dot's outline, its dashed
   * below-horizon outline, its motion trail and its roster chip.  Earth
   * is not pale -- its --e- value is the darker green its center LABEL
   * takes, the dot itself being --c-earth. */
  --e-sun:#BC7800; --e-moon:#767E8A; --e-venus:#97864A; --e-earth:#1F6B54;
}
.cel-mono{font-family:ui-monospace,'SF Mono',Menlo,Consolas,monospace; font-variant-numeric:tabular-nums}
/* skyfield writes `mono` on its own chart labels inside the fragments
 * (33 of them in a dome), so the bare class survives HERE ONLY -- scoped
 * to the fragment, where it styles skyfield's markup and cannot collide
 * with a host skin's own .mono. */
.domefrag .mono, .passfrag .mono{font-family:ui-monospace,'SF Mono',Menlo,Consolas,monospace; font-variant-numeric:tabular-nums}
/* THE TEXT FLOOR (9.6), which is in two halves and needs both.
 *
 * The first half is each panel's ROOT, here: text that sets no size of
 * its own inherits the host's, and a roster's name chip inheriting a
 * 5.5px root renders at 5.5px however carefully the small rules are
 * floored.  The second half is those small rules, below.  Neither half
 * covers the other: a root floored to 11px still leaves a .75em
 * sub-line at 8.25px, and a floored sub-line still sits beside an
 * unfloored chip.
 *
 * A panel's install hint is a sibling BEFORE its root, so it is named
 * here too rather than inherited. */
.cel-count, .cel-geo-body, #dome-wrap, #pass-wrap, .cel-roster,
.cel-skyhint{font-size:max(11px,1em)}

/* Every rule below asking for less than 1em
 * carries max(11px, ...): a ONE-WAY floor, never a size.  These panels
 * drop into somebody else's page, and a host is free to scale its root
 * font with the viewport -- one does, to about 5.5px at 390 -- which
 * silently takes every em-relative label here to a third of what it
 * reads at on this page and puts it under any legibility standard.  A
 * plain px size would be the wrong cure: it would also override a host
 * that scales type UP, for a reader who asked for it.  max() refuses
 * only the bottom, so a host scaling up still wins and a host scaling
 * down stops at 11px.  The drawn text inside the charts is not covered
 * by this -- an SVG's type is in user units and scales with the
 * drawing, which is what the narrow frame is for. */
.cel-sub{color:var(--muted); margin-top:.375rem; font-size:max(11px,.95em)}
#live-label{color:var(--brass); font-variant-caps:small-caps; letter-spacing:.1em}

/* Countdown central: the ticking event chips under the header.  The chip
 * family (.countdown/.count and the k/v/d cells) is borrowed from
 * weewx-skyfield's sky.css -- names and values kept in step, the same
 * cross-repo rule as the dome typography below.  The [hidden] rule is
 * celestial's own: display:flex would otherwise defeat the hidden
 * attribute the javascript toggles on the windowed chips. */
.cel-countdown{display:flex; gap:.625rem; flex-wrap:wrap}
.cel-count{border:.0625rem solid var(--line); padding:.4375rem .75rem; display:flex; gap:.5rem;
       align-items:baseline; background:var(--vault)}
.cel-count .cel-k{color:var(--muted); font-variant-caps:small-caps; letter-spacing:.1em; font-size:max(11px,.8em)}
.cel-count .cel-v{font-size:max(11px,.9em)}
.cel-count .cel-d{color:var(--brass); font-size:max(11px,.8em)}
/* Celestial's chips are two lines by design (a deliberate divergence
 * from sky.css's one-line chip): the label and the ticking value on
 * the first line, the event's clock time / date detail underneath.
 * Grid, not wrapped flex: a flex container's shrink-to-fit width is
 * computed as if every item sat on one line, so forcing the detail
 * onto a second line with flex-basis:100% left the detail's width as
 * dead space on the chip's right.  The grid sizes each chip to its
 * wider LINE.  justify-content:start keeps the auto columns at their
 * natural widths when the detail line is the wider one.  An empty
 * detail must not open a blank second line. */
.cel-count{display:grid; grid-template-columns:auto auto; row-gap:.125rem;
       justify-content:start}
.cel-count .cel-d{grid-column:1/-1}
.cel-count .cel-d:empty{display:none}
.cel-count[hidden]{display:none}
.cel-eyebrow{font-variant-caps:small-caps; letter-spacing:.22em; color:var(--brass); font-weight:400;
         font-size:max(11px,.85em); margin:0 0 .25rem}
.cel-caption{color:var(--muted); font-size:max(11px,.85em); margin:.625rem 0 0; max-width:68ch}
.cel-dialcaption{text-align:center; max-width:none}

/* The Geocentric: the dial beside its roster; one column below 1000px. */
.cel-geo-body{display:grid; grid-template-columns:minmax(0,1fr) 26.875rem;
          gap:1.625rem; align-items:center; margin-top:.5rem}
@media (max-width: 1165px){
  .cel-geo-body{display:flex; flex-direction:column; align-items:stretch}
}
#dial{width:100%; max-width:40rem; display:block; margin:0 auto}

/* The roster: one row per body -- name chip, the distance odometer, then
 * rate / raw AU / altitude on the sub-line. */
.cel-roster{border-top:.0625rem solid var(--line)}
.cel-row{display:grid; grid-template-columns:7.2em 1fr; gap:.1em .6em;
     padding:.42em 0 .38em; border-bottom:.0625rem solid var(--line);
     align-items:baseline}
.cel-bname{font-variant-caps:small-caps; letter-spacing:.1em; white-space:nowrap}
.cel-bname .cel-chip{display:inline-block; width:.62em; height:.62em; border-radius:50%;
             margin-right:.5em}
/* A body below the horizon steps down to the muted tier.  A color, not
 * opacity: every opacity that looks dimmed puts text under APCA Lc 60. */
.cel-row.cel-below .cel-bname, .cel-row.cel-below .cel-odo{color:var(--muted)}
.cel-odo{text-align:right; font-size:max(11px,.9em); letter-spacing:.02em}
.cel-odo .cel-unit{color:var(--muted); font-size:max(11px,.8667em)}
.cel-rsub{grid-column:1 / -1; display:flex; justify-content:space-between; gap:.45em;
      color:var(--muted); font-size:max(11px,.75em); flex-wrap:wrap}
.cel-rsub .cel-arr{color:var(--brass)}
/* The odometer flash: freshly changed digits ease from brass to ink. */
.cel-chg{animation:chgflash .9s ease-out}
@keyframes chgflash{from{color:var(--brass)}}
@media (prefers-reduced-motion: reduce){
  .cel-chg{animation:none}
}

/* Per-body identity classes: the roster chips and the SVG marks.  Proxima
 * wears the moon's silver, like the stars on the Sky page. */
.cel-chip-sun{background:var(--c-sun)} .cel-chip-moon{background:var(--c-moon)}
.cel-chip-mercury{background:var(--c-mercury)} .cel-chip-venus{background:var(--c-venus)}
.cel-chip-mars{background:var(--c-mars)} .cel-chip-jupiter{background:var(--c-jupiter)}
.cel-chip-saturn{background:var(--c-saturn)} .cel-chip-uranus{background:var(--c-uranus)}
.cel-chip-neptune{background:var(--c-neptune)}
.cel-chip-proxima_centauri{background:var(--c-proxima)}
.cel-fill-sun{fill:var(--c-sun)} .cel-fill-moon{fill:var(--c-moon)}
.cel-fill-mercury{fill:var(--c-mercury)} .cel-fill-venus{fill:var(--c-venus)}
.cel-fill-mars{fill:var(--c-mars)} .cel-fill-jupiter{fill:var(--c-jupiter)}
.cel-fill-saturn{fill:var(--c-saturn)} .cel-fill-uranus{fill:var(--c-uranus)}
.cel-fill-neptune{fill:var(--c-neptune)}
.cel-fill-proxima_centauri{fill:var(--c-proxima)}
.cel-fill-earth{fill:var(--c-earth)}
.cel-stroke-sun{stroke:var(--e-sun)} .cel-stroke-moon{stroke:var(--e-moon)}
.cel-stroke-mercury{stroke:var(--c-mercury)} .cel-stroke-venus{stroke:var(--e-venus)}
.cel-stroke-mars{stroke:var(--c-mars)} .cel-stroke-jupiter{stroke:var(--c-jupiter)}
.cel-stroke-saturn{stroke:var(--c-saturn)} .cel-stroke-uranus{stroke:var(--c-uranus)}
.cel-stroke-neptune{stroke:var(--c-neptune)}
.cel-stroke-proxima_centauri{stroke:var(--c-proxima)}
/* On paper the sun, moon and Venus cannot carry their own mark (1.53,
 * 1.40, 1.27:1 as fills on white), so every EDGE they draw switches to
 * the light plate's ring color for that body: the roster chip takes it
 * as an inset ring -- the same treatment skyfield gives its own roster
 * dots on this plate -- and the trails and dashed below-horizon
 * outlines take it as their stroke.  The night plate needs none of
 * this, so --e-* there is just the body color and these rules are the
 * only place the two plates diverge in kind rather than in value. */
.cel-chip-sun{box-shadow:inset 0 0 0 var(--chip-ring) var(--e-sun)}
.cel-chip-moon{box-shadow:inset 0 0 0 var(--chip-ring) var(--e-moon)}
.cel-chip-venus{box-shadow:inset 0 0 0 var(--chip-ring) var(--e-venus)}

/* The dial's static furniture.  The per-ring stroke-opacity (0.5 inside
 * 10 au, 0.4 outside) is set by the javascript, which draws them.  The
 * grid is deliberately recessive -- see the note there -- but the RIM is
 * not scale furniture: it is the dial's outer boundary, one circle rather
 * than eight, so it stays a notch firmer (2.50:1) and gives the receding
 * decades something to be contained by.  The ticks follow the rings.
 * ONE set of weights serves both plates: with the light plate's own grid
 * (#7A899F on white) the same opacities measure dL* 21.1 / 16.7 / 26.3
 * against the night plate's settled 23.4 / 18.9 / 28.8 -- the same
 * furniture at the same recessive distance, which is what the eye reads
 * here, not the WCAG ratio (dark-plate rule, 8.2). */
.cel-geo-ring{fill:none; stroke:var(--grid)}
.cel-geo-rim{fill:none; stroke:var(--grid); stroke-opacity:.62; stroke-width:1.4}
.cel-geo-tick{stroke:var(--grid); stroke-opacity:.4; stroke-width:1}
.cel-geo-tick-major{stroke-width:1.6}
.cel-geo-earth{stroke:var(--halo); stroke-width:1}
/* Body dots: above the horizon they wear a night-colored ring; below they
 * dim and their outline goes dashed in their own color.  That dashed
 * outline is what CARRIES a down body: it is the body's own color at full
 * strength (4.72:1 even for Mars, the darkest) where the dimmed fill
 * reaches 1.90, so the weight belongs on the outline.  1.5px, not the
 * hairline it was through 8.1. */
.cel-geodot.cel-ring{stroke:var(--halo); stroke-width:1}
/* The paper halo is the panel itself, so a pale body would be outlined
 * in nothing; sun and Venus take their edge instead.  (The moon's dial
 * mark is the phase disc below, not a dot, and Proxima's light silver
 * already reads at 4.10:1.) */
.cel-geodot.cel-ring.cel-fill-sun{stroke:var(--geo-ring-sun)}
.cel-geodot.cel-ring.cel-fill-venus{stroke:var(--geo-ring-venus)}
.cel-geodot.cel-below{fill-opacity:.45; stroke-width:1.5; stroke-dasharray:2.5 2.5}
.cel-sunglow{filter:blur(6px); opacity:.55}
/* The moon's true-phase disc, silver-rimmed so a new moon never vanishes. */
.cel-moon-dark{fill:var(--moon-dark)}
.cel-moon-lit{fill:var(--moon-lit)}
.cel-moon-rim{fill:none; stroke:var(--moon-rim); stroke-opacity:var(--moon-rim-op);
              stroke-width:1}
/* Below the horizon the disc dims and the rim goes dashed.  The rim keeps
 * its plate's own strength (--moon-rim-op): it is what carries a down moon,
 * and dimming the whole mark would take it past findable on either plate.
 * The opacity sits on the disc's own group, so the dark disc and its lit
 * limb dim as one image -- on the two shapes separately, the lit limb
 * would show the dark disc through it. */
.cel-geomoon.cel-below .cel-moon-disc{opacity:.55}
.cel-geomoon.cel-below .cel-moon-rim{stroke-dasharray:2.5 2.5}
/* Motion trails: per-segment opacity is set by the javascript, which ramps
 * it from a visible floor (the old ramp started from nothing: the oldest
 * segment landed at 1.02:1, ink spent where no eye can find it). */
.cel-trail{stroke-width:1.5; stroke-linecap:round}
/* Comet marks (javascript-drawn on the dial): a brass diamond with an
 * anti-sunward tail fan, solid when naked-eye bright (mag <= 6), the
 * hollow inversion when fainter or when magnitude is honestly absent --
 * the satdot/shadow convention.  One shared color: comet tags are
 * dynamic, so per-tag classes cannot exist (the satellite precedent). */
.cel-cometdot{fill:var(--brass); stroke:var(--halo); stroke-width:1.5}
.cel-cometdot.cel-faint{fill:var(--halo); stroke:var(--brass); stroke-width:2}
.cel-cometdot.cel-below{fill-opacity:.45; stroke-dasharray:2.5 2.5}
.cel-comet-tail{stroke:var(--brass)}
.cel-geocomet.cel-below .cel-comet-tail{opacity:var(--tail-below-op)}
.cel-stroke-comet{stroke:var(--brass)}
.cel-chip-comet{background:var(--brass)}

/* SVG typography.  cardinal/bodylab/gridlab keep the same names and values
 * as weewx-skyfield's sky.css, so the two pages read as one family. */
svg{width:100%; height:auto; display:block}
svg text{fill:var(--ink); font-family:Charter, Georgia, serif}
.cardinal{font-size:min(calc(14px + var(--tp-svg-step)), max(14px, var(--tp-svg-cap))); fill:var(--brass); letter-spacing:.1em}
.bodylab{font-size:min(calc(11px + var(--tp-svg-step)), max(11px, var(--tp-svg-cap))); fill:var(--ink); font-family:Georgia, serif}
.bodylab.cel-dim{fill:var(--muted)}
.gridlab{font-size:min(calc(10px + var(--tp-svg-step)), max(10px, var(--tp-svg-cap))); fill:var(--muted)}
.cel-earthlab{font-size:min(calc(11px + var(--tp-svg-step)), max(11px, var(--tp-svg-cap))); fill:var(--e-earth); font-variant-caps:small-caps;
          letter-spacing:.12em; text-anchor:middle}
/* THE DIAL'S NARROW FRAME (9.6).  Below the breakpoint celestial.js
 * rebuilds the dial in a 360-unit frame -- about a unit to the pixel on
 * a phone -- and marks it data-frame="narrow".  These are the sizes that
 * frame was laid out around, and they are weewx-skyfield's own
 * NARROW_TYPE_PX for the same class names, so the dial and the dome
 * beside it carry one size of type; a test pins them to the sibling.
 * Sizes, not a scale factor: the script thins the ring labels to every
 * other decade precisely because type this size needs the room, so
 * geometry and type are one decision and neither may move alone.
 *
 * The WIDE frame's are this skin's own too, and bigger than the bare
 * classes: the dial is capped at 640px, so a 10px grid label rendered
 * 9.7px on every desktop there has ever been.  12 units puts it at
 * 11.6px at the cap and 11.0px at the width the frame switch lets it
 * fall to.  The bare .gridlab/.bodylab values are untouched, because
 * weewx-skyfield's charts wear those class names too and their sizes
 * are its own. */
#dial[data-frame="wide"] .gridlab{font-size:min(calc(.75rem + var(--tp-step)), max(.75rem, var(--tp-cap)))}
#dial[data-frame="wide"] .bodylab{font-size:min(calc(.75rem + var(--tp-step)), max(.75rem, var(--tp-cap)))}
#dial[data-frame="wide"] .cel-earthlab{font-size:min(calc(.75rem + var(--tp-step)), max(.75rem, var(--tp-cap)))}
#dial[data-frame="narrow"] .gridlab{font-size:min(calc(15px + var(--tp-svg-step)), max(15px, var(--tp-svg-cap)))}
#dial[data-frame="narrow"] .bodylab{font-size:16px}
#dial[data-frame="narrow"] .cardinal{font-size:17px}
#dial[data-frame="narrow"] .cel-earthlab{font-size:16px}

/* THE TWO DRAWINGS (9.6).  weewx-skyfield 2.7 draws each sky chart
 * either for a desk (680 units across, the frame it has always drawn)
 * or for a phone (`narrow`: 360 units, its own larger type, its star
 * census thinned so the bigger names have room).  A report runs with no
 * screen in front of it, so the fragment carries BOTH and this rule
 * shows the one that fits -- which makes turning a phone a style
 * recalculation, not a refetch, and leaves the fetch path and every
 * fragment filename exactly as they were.
 *
 * What is switched is each frame's BOX, not its <svg>: a pass chart is
 * its drawing plus the pass's dated head line, and hiding only the
 * drawing would print that line twice, once from each frame.
 *
 * Scoped to the two fragment wrappers, not to the bare attribute: a
 * panel dropped into somebody else's page may not reach for a selector
 * that a host's own markup could satisfy, which is the same rule every
 * cel- prefix in this file follows.
 *
 * Scoped to data-frames="both", which a wrapper claims only when it
 * really holds two drawings: if one frame failed to draw, the other is
 * emitted bare and must show at every width rather than be hidden by a
 * rule meant for its missing twin.
 *
 * WHICH frame is asked of the DRAWING'S OWN WIDTH, not the viewport's,
 * and that is the whole point of the rule below being a @container
 * query rather than a @media one.  The viewport does not determine how
 * wide a chart renders: on the bundled page a 1000px window draws it
 * 640px and a 1001px window draws it 459px, because at 1001 the panel
 * becomes a grid and the chart shares the row with a fixed roster; and
 * in a consuming skin the chart's width depends on that skin's chrome,
 * which nothing here can know.  Measured on this page, a viewport rule
 * put 6.95px labels on a 1001px desktop.
 *
 * THE THRESHOLD IS DERIVED, not chosen.  The wide drawing is used only
 * where it is legible: its smallest label is 10 units scaled by the
 * set's label_scale, 12 units at the 1.2 default, in a 680 unit frame
 * -- so it needs 11 x 680 / 12 = 623.3px of glass to put that label at
 * 11px.  Below that the narrow drawing is the better drawing, at any
 * viewport.  celestial.js gives the dial this same number, rounded up,
 * so that an uncustomized page changes all of its drawings at one
 * width rather than showing a desk dial beside a phone-sized dome; a
 * test derives both.
 *
 * KNOWN, DELIBERATE: the Nu validator's CSS checker does not yet know
 * container-type or @container and reports both as errors.  They are
 * not defects -- every engine has shipped them since 2023 -- and a
 * browser too old to know the at-rule ignores it and keeps the wide
 * drawing, which is what it had before.  The release gate pins those
 * two errors rather than expecting none, so a third one still fails. */
.domefrag, .passfrag{container-type:inline-size}
/* A drawing is never stretched past the size it was laid out for.  Each
 * of these caps is its frame's own width: 640px for the 660 and 680 unit
 * desk drawings (the caps this skin has always had, below), 360px for
 * the 360 unit phone one.  Without this last one a phone drawing handed
 * a 544px column -- which is what a host's two-column panel does on a
 * desktop -- is blown up to 544px, with 26px labels and a thinned star
 * census: legible, and obviously wrong.  Capped and centered it reads as
 * what it is, a chart drawn for a narrow column.
 *
 * It is the frame's BOX that is capped, not the drawing inside it: a
 * pass chart is its drawing plus the pass's dated head line, and capping
 * only the drawing would leave that line spanning a column twice its
 * width. */
/* The box when there are two drawings, and the drawing itself when
 * there is only one: a frame that draws alone is emitted bare, with no
 * box for the rule above to find, and #dome-wrap svg would otherwise
 * stretch a 360 unit drawing to 640px -- the very thing this cap is
 * here to stop. */
.cel-frame[data-frame="narrow"],
#dome-wrap svg.sky-narrow, #pass-chart svg.sky-narrow{max-width:360px; margin:0 auto}
#dial[data-frame="narrow"]{max-width:360px}
.domefrag[data-frames="both"] .cel-frame[data-frame="narrow"],
.passfrag[data-frames="both"] .cel-frame[data-frame="narrow"]{display:none}
@container (max-width: 623.29px){
  .domefrag[data-frames="both"] .cel-frame[data-frame="narrow"],
  .passfrag[data-frames="both"] .cel-frame[data-frame="narrow"]{display:block}
  .domefrag[data-frames="both"] .cel-frame[data-frame="wide"],
  .passfrag[data-frames="both"] .cel-frame[data-frame="wide"]{display:none}
}
/* AND THE SET'S OWN ANSWER WINS.  The width above is the DEFAULT label
 * scale's; a set that declares another changes frame somewhere else
 * entirely -- 935px at 0.8, 534px at 1.4 -- because a chart's smallest
 * label is 10 units TIMES that scale.  A container query cannot be told
 * a number per fragment (its condition takes no variable, and a <style>
 * element is not valid inside a fragment), so each fragment carries its
 * own width as data-frame-at and celestial.js writes the verdict here
 * as data-frame-fit.  These rules therefore carry one more attribute
 * than the ones above, which is what lets them win.
 *
 * A page with no javascript keeps the query above, which is right for
 * every set on the default scale -- including this skin's own. */
.domefrag[data-frames="both"][data-frame-fit="wide"] .cel-frame[data-frame="narrow"],
.passfrag[data-frames="both"][data-frame-fit="wide"] .cel-frame[data-frame="narrow"]{display:none}
.domefrag[data-frames="both"][data-frame-fit="wide"] .cel-frame[data-frame="wide"],
.passfrag[data-frames="both"][data-frame-fit="wide"] .cel-frame[data-frame="wide"]{display:block}
.domefrag[data-frames="both"][data-frame-fit="narrow"] .cel-frame[data-frame="narrow"],
.passfrag[data-frames="both"][data-frame-fit="narrow"] .cel-frame[data-frame="narrow"]{display:block}
.domefrag[data-frames="both"][data-frame-fit="narrow"] .cel-frame[data-frame="wide"],
.passfrag[data-frames="both"][data-frame-fit="narrow"] .cel-frame[data-frame="wide"]{display:none}

/* The sky dome: weewx-skyfield's dome_svg embedded beside the satellite
 * roster; one column below 1000px, like the Geocentric.  The dome is
 * sky-chart orientation (north top, east LEFT) -- deliberately opposite
 * the dial; do not harmonize them.  NOTE: "dome-body" is taken -- it is
 * the class on the embedded SVG's per-body mark groups (skyfield 2.0's
 * data-body consumer contract), so the panel wrapper is "domepanel". */
.cel-domepanel{display:grid; grid-template-columns:minmax(0,1fr) 26.875rem;
           gap:1.625rem; align-items:center; margin-top:.5rem}
@media (max-width: 1165px){
  .cel-domepanel{display:flex; flex-direction:column; align-items:stretch}
}
#dome-wrap svg{max-width:40rem; margin:0 auto}
.cel-roster .cel-eyebrow{margin-top:.125rem}
.cel-chip-sat{background:var(--brass)}
.cel-satline{font-size:max(11px,.8em)}
/* Dome typography borrowed from weewx-skyfield's sky.css -- names and
 * values kept in step, same cross-repo rule as cardinal/bodylab/gridlab
 * above -- the light-theme variants included, as of 8.3.
 * The dome and next-visible-pass charts put these small labels on the
 * dome gradient, whose rim is lighter than the panel surface.  .skylab is
 * the dome's scoped opt-in, worn by the ring-degree labels as
 * "mono gridlab skylab"; the axis labels on every other panel sit on the
 * panel surface and keep plain .gridlab.  Equal specificity, so .skylab
 * MUST stay after .gridlab above -- source order is all that makes it win.
 * The values are sky.css's: --muted clears the dome's rim on both plates,
 * so the ring and star labels take it, and only .conlab carries a value
 * of its own.  The constellation names draw at full strength; opacity is
 * part of a text color and would put them under the text bars.
 * Both plates' values are the --skylab/--conlab tokens in the two token
 * blocks above (sky.css writes them as literals plus a :root.theme-light
 * rule; the test resolves both to the same values), so that a fragment
 * on the other plate from the page -- whose SVG the token blocks scope
 * by its own data-dome-palette / data-pass-palette -- takes its labels'
 * colors from the same place as every other class: one rule per class,
 * no plate variants to keep in step. */
.skylab{fill:var(--skylab)}
.starlab{font-size:min(calc(10px + var(--tp-svg-step)), max(10px, var(--tp-svg-cap))); fill:var(--skylab); font-family:Georgia, serif; font-style:italic}
.conlab{font-size:min(calc(10px + var(--tp-svg-step)), max(10px, var(--tp-svg-cap))); fill:var(--conlab); font-family:Georgia, serif; font-style:italic;
        letter-spacing:.14em; text-transform:uppercase}
.satlab{fill:var(--brass); font-family:Georgia, serif}
.nowlab{font-size:min(calc(10px + var(--tp-svg-step)), max(10px, var(--tp-svg-cap))); fill:var(--brass)}
/* The live satellite layer (javascript-drawn): the marker dot and its
 * name, each with a faint variant for a satellite that is up but not
 * visible to the eye (daylight, or in Earth's shadow). */
.cel-satdot{fill:var(--brass); stroke:var(--halo); stroke-width:1.5}
/* In Earth's shadow: the fill/stroke inversion of weewx-skyfield's static
   dome marker (hollow ring, background fill so hover still lands). */
.cel-satdot.cel-shadow{fill:var(--halo); stroke:var(--brass); stroke-width:2}
/* In a bright sky (sun >= -6): dimmed but drawn.  Whole-element opacity so
   the hollow ring dims too (fill-opacity would leave its stroke bright);
   .8 is the least that stays findable on the light dome's rim.  The name
   steps to the muted tier instead: text takes no opacity. */
.cel-satdot.cel-faint{opacity:.8}
.satlab.cel-faint{fill:var(--muted)}

/* The Next Visible Pass panel (weewx-skyfield's pass_chart_html embedded beside
 * the visible-pass roster): the same two-column shape as the dome panel
 * above, chart left, roster right, one column on narrow screens.  The
 * passhead trio is borrowed from sky.css -- names and values kept in
 * step, the same cross-repo rule as the dome typography above. */
.cel-passpanel{display:grid; grid-template-columns:minmax(0,1fr) 26.875rem;
           gap:1.625rem; align-items:center; margin-top:.5rem}
@media (max-width: 1165px){
  .cel-passpanel{display:flex; flex-direction:column; align-items:stretch}
}
#pass-chart{max-width:40rem; margin:0 auto}
.cel-passcaption{max-width:40rem; margin-left:auto; margin-right:auto}
.passhead{display:flex; justify-content:space-between; align-items:baseline;
          gap:.75rem; margin-bottom:.625rem; flex-wrap:wrap}
.passname{font-variant-caps:small-caps; letter-spacing:.1em}
.passwhen{font-size:max(11px,.85em); color:var(--brass)}

/* Shown in the panel when no extended almanac serves the report.  The
   dome's hint arrives inside the .domepanel grid (the panel renders it
   where the dome would be), so it spans the grid's columns. */
.cel-skyhint{color:var(--muted); font-style:italic; text-align:center;
         border:.0625rem dashed var(--line); padding:2.375rem 1.25rem; margin:.5rem 0 .375rem}
.cel-skyhint a{color:var(--brass)}
.cel-domepanel > .cel-skyhint{grid-column:1 / -1}

/* The dome's health line: shown only when the backdrop has stopped
   advancing and the dome has frozen.  A caption-sized line rather than
   the install hint's dashed box -- it sits under a chart that is still
   worth reading, just no longer current.  Deliberately NO display
   property: the javascript hides it with the hidden attribute, and a
   display here would defeat it (the .count lesson). */
.cel-stalehint{color:var(--brass); font-style:italic; text-align:center;
           font-size:max(11px,.82em); margin:.125rem 0 .375rem}
.cel-stalehint a{color:inherit}

/* The tap tooltip (sky.js): the same text as a mark's hover <title>,
   shown as a floating chip when a finger has no hover to offer. */
.skytip{position:fixed; display:none; z-index:10; max-width:min(340px, 90vw);
        background:var(--vault); color:var(--ink); border:.0625rem solid var(--brass);
        padding:.375rem .625rem; font-size:min(calc(.85rem + var(--tp-step)), max(.85rem, var(--tp-cap))); pointer-events:none;
        box-shadow:0 .125rem .625rem rgba(0,0,0,.4)}