/* Redwood City Astronomy Dashboard — fixed 1920×1080 canvas, HamClock-style look. */

:root {
  --bg: #000;
  --panel-bg: #000;
  --border: #4a4a4a;
  --text: #e8e6df;
  --dim: #a2a2a2;          /* labels and inactive values; keep clearly below --text */
  --orange: #f5a53a;
  --yellow: #f2d24b;
  --green: #7ee07a;
  --cyan: #4fd6e6;
  --white: #f4f4f4;
  --tip: #7aa2f7;           /* dotted underline marking hover tips */

  --gap: 8px;
  --font-head: "Oswald", "Arial Narrow", sans-serif;
  --font-body: "Inter", system-ui, "Helvetica Neue", Arial, sans-serif;   /* data text; Andy chose Inter 2026-09-28 */
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-variant-numeric: tabular-nums;   /* equal-width digits, so changing numbers don't jiggle */
  overflow: hidden;
}

/* The stage fills the window; the canvas is scaled to fit it (see app.js). */
#stage {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

#canvas {
  position: relative;
  flex: none;
  width: 1920px;
  height: 1080px;
  background: var(--bg);
  transform-origin: center center;
}

/* ---- Panel geometry (measured from docs/wireframe.png) ---- */
.panel {
  position: absolute;
  background: var(--panel-bg);
  border: 1px solid var(--border);
  padding: 16px 18px;
  overflow: hidden;
}

/* Top row: left identity panel and centre clock panel both y 8–158 so the top row lines up (Andy, 2026-10-18) */
#p-identity { padding: 0 18px; left:    8px; top:   8px; width: 464px; height: 150px; }   /* left column: 464 wide throughout */
#p-clock    { left:  480px; top:   8px; width: 756px; height: 150px; }   /* centre column: 756 wide throughout */
#p-sun .body-row, #p-moon .body-row { gap: 12px; }   /* 374 px wide here: 2 px spare for the longest Moon line */
#p-weather  { left: 1244px; top:   8px; width: 668px; height: 256px; }

/* Bottom-left column */
#p-tonight  { left:    8px; top: 166px; width: 464px; height: 590px; }
#p-passes   { left:    8px; top: 764px; width: 464px; height: 308px; }

/* Bottom-center column */
#p-allsky   { left:  480px; top: 166px; width: 756px; height: 598px; }   /* +48 px from the wireframe (Andy: shrink telescope, enlarge AllSky) */
/* Sun and Moon at full size along the bottom of the centre column. */
#p-sun      { left:  480px; top: 772px; width: 374px; height: 300px; }
#p-moon     { left:  862px; top: 772px; width: 374px; height: 300px; }

/* Right column */
#p-radar    { left: 1244px; top: 272px; width: 668px; height: 450px; }
#p-csc      { left: 1244px; top: 730px; width: 668px; height: 342px; }

/* ---- Panel chrome ---- */
.panel header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 10px;
}

.panel h2 {
  margin: 0;
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 26px;
  letter-spacing: 0.2px;
}

.meta {
  font-size: 13px;
  color: var(--dim);
}

.c-orange { color: var(--orange); }
.c-yellow { color: var(--yellow); }
.c-green  { color: var(--green); }
.c-cyan   { color: var(--cyan); }
.c-white  { color: var(--white); }
.c-text   { color: var(--text); }
.lbl      { color: var(--dim); }                /* labels and inactive text */
.stale    { color: var(--dim) !important; }   /* feed hasn't updated recently */

.placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #3a3a3a;
  font-size: 14px;
  pointer-events: none;
}

/* ---- Identity panel ---- */
.id-head {
  display: flex;
  align-items: center;
  gap: 20px;
  height: 100%;
}

/* Logo: Redwood House Observatory (supplied by Andy, 2026-10-18), white on black. */
.logo-box { flex: none; }
.logo { display: block; height: 148px; width: auto; }   /* touches top and bottom; 18 px from the left like the text in the panels below */   /* the logo is 934 x 976 px */

/* Street, town, lat/long beside the logo (Andy, 2026-09-30), as two groups:
   the address (street + town, tight) and, 12 px below, the coordinates. The
   last line's baseline sits on the baseline of the logo's "OBSERVATORY": the
   negative margin removes the half-leading and descent below it. */
.obs-loc { color: var(--dim); width: max-content; margin-left: auto; }   /* hugs the right edge: name and values end at the same x, labels sit close to values */
#obs-lat, #obs-lon, #obs-elev { display: flex; justify-content: space-between; gap: 10px; }
#obs-street { color: var(--text); font-size: 18px; line-height: 1.2; }
#obs-place {
  color: var(--text);
  font-family: var(--font-head);
  font-weight: 500;
  font-size: 29px;
  line-height: 1.1;
  letter-spacing: 0.3px;
  white-space: nowrap;
  text-align: right;
  margin-bottom: 8px;
}
#obs-lat, #obs-lon, #obs-elev { font-size: 22px; white-space: nowrap; line-height: 1.4; }
.obs-k { display: inline-block; }

/* Clocks: big local HH:MM with seconds + zone beside it, UTC/LST/JD to the right. */
.clock-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 4px;
}
/* Middle zone: next moonrise, dark and moonset (app.js renderNext). */
.clock-left { flex: none; }
.clock-main { white-space: nowrap; }
.next-up { font-size: 22px; order: -1; }   /* left: Moon up / Dark / Moon down; the clock sits in the middle, UTC / LST / JD on the right (Andy, 2026-10-19); same type size and row spacing as the UTC / LST / JD block */
#next-up { border-spacing: 0; }
#next-up td { padding: 0 10px 3px 0; white-space: nowrap; }
#next-up td.n-name { color: var(--dim); }
#next-up td.n-at { color: var(--yellow); font-variant-numeric: tabular-nums; }


.clock-main {
  display: flex;
  align-items: flex-start;
  gap: 8px;
}

#clk-hm {
  font-family: var(--font-head);
  font-weight: 500;
  font-size: 80px;
  line-height: 0.95;
  color: var(--text);
  /* Normal digit shapes (not the body's tabular ones), so app.js alignClock()
     can measure the first digit with canvas measureText, which ignores tabular-nums. */
  font-variant-numeric: proportional-nums;
}

/* Each digit gets a fixed slot (a "1" is 0.38em, a "0" 0.54em) to keep the
   clock from shifting sideways as the digits change. */
.dg {
  display: inline-block;
  width: 0.54em;
  text-align: center;
}

.clock-side {
  display: flex;
  flex-direction: column;
  padding-top: 5px;
}

#clk-sec {
  font-family: var(--font-head);
  font-weight: 500;
  font-size: 30px;
  line-height: 1;
  color: var(--white);
}

#clk-tz {
  font-size: 18px;
  margin-top: 4px;
}

.clock-aux {
  display: grid;
  grid-template-columns: auto auto;
  column-gap: 12px;
  row-gap: 3px;
  margin: 0;
  font-size: 22px;
}
.clock-aux dt { color: var(--dim); }
.clock-aux dd { margin: 0; font-variant-numeric: tabular-nums; }

.clock-date {
  font-family: var(--font-head);
  font-weight: 500;
  font-size: 26px;
  color: var(--text);
  margin-top: 4px;
}
/* The date line shows a warning in its place when the data stopped updating or the
   server can't be reached (staleMessage in app.js). Night Mode keeps everything red,
   so there it can't change colour: white becomes the brightest red, which stands out
   from the dimmer red of normal text. */
.clock-date.warning { color: #ff5b4d; font-weight: 600; white-space: nowrap; }
body.night .clock-date.warning { color: #fff; }

/* ---- Sun & Moon panels (same size, parallel layout) ---- */
.body-row {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 8px;
}

/* Image slot; the SDO / NASA SVS images arrive in Step 2b. */
.body-img {
  flex: none;
  width: 140px;
  height: 140px;
  border-radius: 50%;
  border: 1px dashed var(--border);
  background: center / cover no-repeat;
}
.body-img.has-img { border: none; }
/* Tints a grayscale Sun picture orange with a plain orange layer multiplied over it (the picture's own
   shading is kept, black stays black), then a brightness lift. The two numbers come from measuring NASA's
   own orange picture against Helioviewer's gray one at the disc centre and at 50 / 80 / 90 % of the radius:
   NASA's red is saturated (255) everywhere and only its green falls toward the limb (170, 161, 129, 111),
   about 0.73 of the gray value, with almost no blue. brightness 1.45 saturates the red the same way and
   multiply (255,129,6) puts green at 0.73 of the gray and blue near 0; the result is within about 10 of
   NASA's values at all four places. (A filter chain of sepia + saturate + hue-rotate was tried first and
   blew out to yellow.) */
.body-img.orange-tint { position: relative; isolation: isolate; filter: brightness(1.45); }
.body-img.orange-tint::after {
  content: "";
  position: absolute;
  inset: 1px;               /* stays inside the circle's anti-aliased edge, which showed as a faint ring */
  border-radius: 50%;
  background: rgb(255, 129, 6);
  mix-blend-mode: multiply;
}
/* HMI disk fills 92% of its frame; enlarge so the disk fills the circle. */
#sun-img { background-size: 108.5%; }
#moon-img { background-size: contain; }   /* app.js scales this to fill the circle */

.facts {
  margin: 0;
  font-size: 15px;
  display: flex;
  flex-direction: column;
  gap: 7px;
}
.facts > div { display: flex; gap: 8px; white-space: nowrap; }
.facts dt { color: var(--dim); }
.facts dd { margin: 0 6px 0 0; }
.facts dd:last-child { margin-right: 0; }
.facts dd:not([class]) { color: var(--text); }   /* colored values keep their .c-* color */

.panel-foot {
  position: absolute;
  left: 18px;
  right: 18px;
  bottom: 18px;
}

.foot-note {
  display: flex;
  justify-content: space-between;
  font-size: 13px;
  color: var(--dim);
  margin-bottom: 6px;
}

.foot-line { font-size: 16px; }
.foot-line .lbl { color: var(--dim); }
.foot-line .lbl + span { margin-right: 10px; }

/* Tonight, sunset → sunrise: twilight, astronomical dark, moon-up overlay. */
.night-bar {
  position: relative;
  height: 16px;
  margin-bottom: 8px;
  background: #34405c;          /* twilight */
  border: 1px solid var(--border);
  overflow: hidden;
}
.night-bar > span { position: absolute; top: 0; bottom: 0; }
.night-bar .dark { background: #0b1122; }
.night-bar .moon { background: rgba(242, 210, 75, 0.30); }   /* Moon above horizon */
.night-bar .edge { width: 2px; background: var(--cyan); }
.night-bar .now  { width: 2px; background: var(--white); }

/* ---- Tables (Up Tonight, Visible Passes) ---- */
.tbl {
  width: 100%;
  border-collapse: collapse;
  font-size: 15px;
}
.tbl th {
  text-align: left;
  font-weight: 400;
  color: var(--dim);
  padding: 0 0 5px;
}
.tbl td { padding: 3px 0; white-space: nowrap; }
.tbl .num { text-align: right; }
.tbl tr.down td { color: var(--dim); }   /* planets: not visible now (below the horizon or 10°, or daytime); see renderPlanets */
.tbl .name { max-width: 320px; overflow: hidden; text-overflow: ellipsis; }

#planets th:nth-child(1) { width: 22%; }   /* the five number columns share the rest equally, all right-aligned */
#planets th:nth-child(n+2) { width: 15.6%; }
#dsos th:nth-child(1), #dsos td:nth-child(1) { width: 1%; padding-right: 0; }
#dsos th { white-space: nowrap; }
#dsos th:nth-child(n+3), #dsos td:nth-child(n+3) { width: 1%; padding-left: 14px; }
#dsos td.name .con, #dsos th .con { display: inline-block; width: 62px; text-align: center; color: var(--dim); }   /* constellation abbreviation, centred in a fixed-width slot between the ID and the name (equal gaps), gray (a darker cyan was hard to read, Andy 2026-10-19) */
#dsos td.name { max-width: 0; width: 100%; }   /* the name takes what is left (cut with an ellipsis if too long) so all five columns fit */

.tbl-caption {
  margin: 14px 0 8px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
  font-size: 13px;
  color: var(--dim);
}

/* ---- Satellite Passes ---- */
/* Object takes the rest; each other column is as wide as its widest value, with an equal 22 px gap before it. */
#passes th:nth-child(n+2), #passes td:nth-child(n+2) { width: 1%; padding-left: 22px; }
#passes tr.now td { color: var(--green); }        /* visible right now */
#passes td { padding: 2px 0; }                    /* 5 rows + the ISS countdown fit at 15 px */
.pass-countdown {
  position: absolute;
  left: 18px;
  right: 18px;
  bottom: 16px;
  font-size: 15px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---- AllSky ---- The whole camera frame (4:3), scaled to fit the panel. */
.allsky-frame { position: absolute; left: 18px; right: 18px; top: 58px; bottom: 10px; }
#allsky-img { width: 100%; height: 100%; object-fit: contain; }
#allsky-img.stale { opacity: 0.35; }   /* the picture is old (an hour or more): dimmed so it does not pass for a live view */
/* Direction key, lower right corner of the frame; drawn by drawRose() in app.js. */
#allsky-rose { position: absolute; right: 0; bottom: 0; width: 108px; height: 108px; z-index: 2;
  box-sizing: border-box; padding: 8px;   /* room between the letters and the rim */
  background: #000; border: 2px solid var(--dim); border-radius: 50%;
  opacity: 0.5; }   /* a round dark disc behind the key (readable over bright sky, trees and glare); the whole key is 50 % opaque (Andy, 2026-10-19; was 75 %) */
#allsky-rose[hidden] { display: none; }
#allsky-rose .rose-star { fill: #262626; stroke: var(--dim); stroke-width: 1; stroke-linejoin: round; }
#allsky-rose .rose-n { fill: var(--orange); }
#allsky-rose text { font-family: var(--font-body); text-anchor: middle; dominant-baseline: central; }
#allsky-rose .rose-l { fill: var(--text); font-size: 12px; font-weight: 600; }
#allsky-rose .rose-north { fill: var(--orange); }
#allsky-rose .rose-d { fill: var(--dim); font-size: 8px; }

/* ---- Upcoming Events ---- */

/* ---- Local Weather (NWS observations) ---- */
.wx-status {
  display: flex;
  align-items: center;
  gap: 18px;
  margin: 2px 0 14px;
}
.wx-big {
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 60px;
  line-height: 1;
  text-transform: uppercase;
  white-space: nowrap;   /* never wrap: a long unsafe list wraps beside it instead */
  flex: none;
}
.wx-note { font-size: 15px; line-height: 1.35; }   /* 3 wrapped lines still fit beside the 60 px word */
.wx-grid {
  display: grid;
  grid-template-columns: 96px 1fr 92px 1fr;
  column-gap: 12px;
  row-gap: 7px;
  margin: 0;
  font-size: 15px;
}
.wx-grid dt { color: var(--dim); }
.wx-grid dd { margin: 0; text-align: right; padding-right: 18px; white-space: nowrap; }
.wx-grid .unit { color: var(--dim); }
.wx-grid.offline dd { color: var(--dim) !important; }   /* last known values, not current */

/* ---- Regional Radar ----
   Three layers in the same 632×370 box: base map (land, lakes, state line),
   the radar frame, and an overlay (range rings, towns, the site). */
.radar-map {
  position: absolute;
  left: 18px;
  top: 60px;
  width: 632px;
  height: 370px;
  overflow: hidden;
  background: #0b111c;          /* land */
  border: 1px solid var(--border);
}
.radar-map > * { position: absolute; inset: 0; width: 100%; height: 100%; }
.radar-frames img { position: absolute; inset: 0; width: 100%; height: 100%; visibility: hidden; }
.radar-frames img.show { visibility: visible; }
/* USGS shaded relief, darkened and tinted to suit the black theme. */
.radar-base .relief { filter: brightness(0.3) contrast(1.5) sepia(0.5) hue-rotate(175deg) saturate(1.6); }
.radar-base .lake { fill: #0f2847; stroke: #2a5687; stroke-width: 1; }
.radar-base .border { fill: none; stroke: #8a8a8a; stroke-width: 1.2; stroke-dasharray: 7 5; }
.radar-overlay .ring { fill: none; stroke: var(--orange); stroke-width: 1.2; stroke-dasharray: 3 5; opacity: 0.75; }
.radar-overlay .town { fill: var(--white); stroke: #000; stroke-width: 1.5; }
.radar-overlay .site { fill: var(--orange); }
.radar-overlay text { font-family: var(--font-body); font-size: 14px; fill: var(--text); }
.radar-overlay .label-bg { fill: rgba(0, 0, 0, 0.6); }   /* box behind labels: readable on any map */
.radar-overlay text.site-label { fill: var(--orange); }
.radar-overlay text.note { font-size: 11px; fill: var(--dim); }
.radar-time {
  inset: 6px 8px auto auto !important;
  width: auto !important;
  height: auto !important;
  font-size: 12px;
  color: var(--text);
  padding: 1px 5px;
  background: rgba(0, 0, 0, 0.6);   /* readable on any base map */
  border-radius: 2px;
}

/* Colour key: NOAA MRMS reflectivity colours from 5 to 70 dBZ (sampled from
   NOAA's own legend), i.e. from light rain to hail. */
.radar-key {
  inset: auto 8px 7px auto !important;
  width: 210px !important;
  height: auto !important;
  padding: 4px 6px 3px;
  background: rgba(0, 0, 0, 0.55);
  border-radius: 3px;
}
.radar-key-bar {
  height: 8px;
  background: linear-gradient(to right,
    #536aa3 0%, #5085b7 7.7%, #58c1b8 15.4%, #30d65b 23.1%, #0caf11 30.8%, #0a730c 38.5%,
    #84a004 46.2%, #f4ca17 53.8%, #f4b217 61.5%, #d00808 69.2%, #a90808 76.9%,
    #f1b9fd 84.6%, #f174fd 92.3%, #8200e7 100%);
}
.radar-key-labels {
  display: flex;
  justify-content: space-between;
  margin-top: 2px;
  font-size: 10px;
  color: var(--dim);
}

/* ---- Clear Sky Chart ----
   Drawn by app.js (renderClearSky) from clearsky.py's forecast numbers:
   absolutely placed 10 px hour cells, row labels on the left, a yellow line
   at each local midnight, and the colour keys underneath. */
.csc-verdict {
  font-size: 15px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-bottom: 6px;
}
.csc-verdict b { color: var(--yellow); font-weight: 600; }
.csc-chart {
  position: absolute;
  left: 18px;
  right: 18px;
  top: 84px;
  bottom: 8px;
}
.csc-chart > span { position: absolute; }
.csc-label { left: 0; font-size: 14px; color: var(--text); white-space: nowrap; }
.csc-cell { width: 10px; }
.csc-day { top: 8px; font-size: 14px; color: var(--cyan); }
.csc-hour { top: 28px; font-size: 13px; color: var(--dim); transform: translateX(-50%); }
.csc-day.mid, .csc-hour.mid { color: var(--yellow); font-weight: 700; }
.csc-day.mid { transform: translateX(-3px); }
.csc-midnight { width: 2px; background: var(--yellow); }
.csc-keys {
  position: absolute;
  left: 0;
  right: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  row-gap: 4px;
  font-size: 13px;
  color: var(--dim);
}
.csc-key { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.csc-key .sw { display: inline-flex; }
.csc-key i { display: inline-block; width: 21px; height: 12px; }
.csc-key:first-child i { width: 28px; }

/* ---- Hover tips ----
   Anything with a tip (a title attribute) gets a "help" pointer, and its key
   text (.tip) a dotted underline. Only on screens with a mouse: the kiosk
   has none, so there the markers would just be clutter. */
@media (hover: hover) {
  #canvas [title] { cursor: help; }
  .tip {
    text-decoration: underline dotted;
    text-decoration-color: var(--tip);
    text-decoration-thickness: 1.5px;
    text-underline-offset: 3px;
  }
}

/* ---- Night Mode ----
   Each panel (text, borders, and the live images added in later steps) is
   flattened to grayscale, then a pure-red layer over the canvas multiplies
   it: red-on-black only. The red layer must sit outside the grayscaled
   panels, or it would be grayscaled too. */
body.night .panel {
  filter: grayscale(1);
}
body.night #canvas::after {
  content: "";
  position: absolute;
  inset: 0;
  background: #ff0000;
  mix-blend-mode: multiply;
  pointer-events: none;
  z-index: 10;
}
#events-tbd { font-size: 30px; margin-top: 60px; text-align: center; }
