/**
 * Daily Uplift
 *
 * The visual job is to feel like a calm place to land for ninety seconds. Generous space,
 * one clear focal point (the message), soft edges, nothing that flashes or demands.
 *
 * Behind all of it is the sky at the hour it actually is where the visitor is standing —
 * night, the twilights, a low warm sunrise, high midday, and back down again. Its colours
 * are not written here: `sky-scene.js` computes them from the sun's real altitude and sets
 * the custom properties below on `:root`. This file only says what to do with them.
 *
 * Both light and dark are first-class. In a dark interface the same scene still travels
 * through the day, dimmed toward night, so it is never a lit window in a dark room.
 */

/*
 * Registering the scene properties as real types is what lets a change of sky *fade*.
 * An unregistered custom property is a string as far as the engine is concerned, so a
 * gradient built from one jumps between values; registered as <color>, it interpolates.
 * Browsers without `@property` simply cut between skies instead, which is a fine thing
 * to lose given the page repaints once a minute.
 */
@property --sky-top { syntax: '<color>'; inherits: true; initial-value: #23384f; }
@property --sky-mid { syntax: '<color>'; inherits: true; initial-value: #4c6486; }
@property --sky-low { syntax: '<color>'; inherits: true; initial-value: #9b8595; }
@property --glow { syntax: '<color>'; inherits: true; initial-value: rgb(255 178 122 / 0.45); }
@property --sun-color { syntax: '<color>'; inherits: true; initial-value: #ffbe80; }
@property --scrim { syntax: '<color>'; inherits: true; initial-value: rgb(5 9 18 / 0.34); }
@property --star-opacity { syntax: '<number>'; inherits: true; initial-value: 0.2; }
@property --sun-x { syntax: '<percentage>'; inherits: true; initial-value: 50%; }
@property --sun-y { syntax: '<percentage>'; inherits: true; initial-value: 96%; }
@property --sun-opacity { syntax: '<number>'; inherits: true; initial-value: 0.9; }
@property --glow-x { syntax: '<percentage>'; inherits: true; initial-value: 50%; }
@property --moon-x { syntax: '<percentage>'; inherits: true; initial-value: 50%; }
@property --moon-y { syntax: '<percentage>'; inherits: true; initial-value: 118%; }
@property --moon-opacity { syntax: '<number>'; inherits: true; initial-value: 0; }

:root {
  --ink: #23303d;
  --ink-soft: #52657a;
  --ink-faint: #6f8394;
  --surface: rgba(255, 255, 255, 0.78);
  --surface-solid: #fdfcfa;
  --edge: rgba(35, 48, 61, 0.1);
  --accent: #b47d4a;
  --moon-lit: #f6efe2;
  --moon-dark: rgba(35, 48, 61, 0.16);
  --shadow: 0 1px 2px rgba(35, 48, 61, 0.04), 0 12px 32px rgba(35, 48, 61, 0.07);

  --measure: 34rem;
  --radius: 1.25rem;
  --step: clamp(1.5rem, 4vw, 2.75rem);

  /*
   * How far above the foot of the screen the horizon line sits. Small, and only there so a
   * body exactly on the horizon is still a whole disc rather than half of one clipped by
   * the bottom edge; below the line the sky simply carries on and a setting sun leaves.
   */
  --horizon: clamp(2.5rem, 7vh, 5rem);
}

/*
 * The dark-sky scheme. `app.js` sets `data-ink` from the brightness of the sky it just
 * painted, so the text flips once, around sunrise and again around sunset, rather than at
 * a clock hour that has nothing to do with the light. A viewer whose system asks for a
 * dark interface stays here all day.
 */
body[data-ink='light'] {
  --ink: #e8eef5;
  --ink-soft: #a7b6c6;
  --ink-faint: #90a2b3;
  --surface: rgba(20, 28, 42, 0.66);
  --surface-solid: #141c29;
  --edge: rgba(232, 238, 245, 0.14);
  --accent: #d8b083;
  --moon-dark: rgba(232, 238, 245, 0.12);
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 16px 40px rgba(0, 0, 0, 0.35);
}

/*
 * The same scheme again for the frame before the script runs, so a dark-mode visitor never
 * sees a flash of dark text. Everything after that is driven by `data-ink`.
 */
@media (prefers-color-scheme: dark) {
  :root {
    --ink: #e8eef5;
    --ink-soft: #a7b6c6;
    --ink-faint: #90a2b3;
    --surface: rgba(20, 28, 42, 0.66);
    --surface-solid: #141c29;
    --edge: rgba(232, 238, 245, 0.14);
    --accent: #d8b083;
    --moon-dark: rgba(232, 238, 245, 0.12);
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 16px 40px rgba(0, 0, 0, 0.35);
  }
}

* {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;

  /*
   * The whole scene crossfades from here. The properties are set on this element, so this
   * is where the transition has to live; everything below simply inherits values that are
   * already in motion. Slow on purpose — the sky is meant to be something you notice has
   * changed, not something you can watch changing.
   */
  transition:
    --sky-top 1.8s ease, --sky-mid 1.8s ease, --sky-low 1.8s ease,
    --glow 1.8s ease, --sun-color 1.8s ease, --scrim 1.8s ease,
    --star-opacity 2.4s ease,
    --sun-x 1.8s ease, --sun-y 1.8s ease, --sun-opacity 1.8s ease, --glow-x 1.8s ease,
    --moon-x 1.8s ease, --moon-y 1.8s ease, --moon-opacity 1.8s ease;
}

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--step);

  padding: clamp(2rem, 7vh, 4.5rem) 1.25rem clamp(2rem, 6vh, 4rem);

  font-family: ui-serif, Georgia, 'Iowan Old Style', 'Palatino Linotype', serif;
  color: var(--ink);
  line-height: 1.6;
  transition: color 1.2s ease;
}

/* ---------- The scene ---------- */

.sky {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  background: linear-gradient(
    to bottom,
    var(--sky-top) 0%,
    var(--sky-mid) 54%,
    var(--sky-low) 100%
  );
}

/*
 * Everything celestial lives in here rather than in `.sky`, so that "on the horizon" means
 * the bottom of this box. That is the whole coordinate system `sky-scene.js` works in: a
 * body's vertical position is a percentage of the sky, and a setting sun passes 100%.
 */
.sky-field {
  position: absolute;
  inset: 0 0 var(--horizon);
}

/* The wash of light along the horizon, sitting under wherever the sun is. */
.glow {
  position: absolute;
  left: var(--glow-x);
  bottom: -16vmin;
  width: min(170vw, 95rem);
  height: min(95vw, 48rem);
  transform: translateX(-50%);
  background: radial-gradient(ellipse at center, var(--glow) 0%, rgb(0 0 0 / 0) 68%);
}

/*
 * The sun: one radial gradient doing the work of three things — a bright core, the bloom
 * around it, and the way both dissolve as it nears the horizon. `--sun-opacity` fades it
 * out across the degree either side of setting, so it goes into its own light rather than
 * switching off.
 */
.orb {
  position: absolute;
  transform: translate(-50%, -50%);
}

.orb-sun {
  left: var(--sun-x);
  top: var(--sun-y);
  width: clamp(8rem, 26vmin, 15rem);
  aspect-ratio: 1;
  opacity: var(--sun-opacity);
  background: radial-gradient(
    circle,
    var(--sun-color) 0 13%,
    var(--glow) 32%,
    rgb(0 0 0 / 0) 66%
  );
}

/*
 * The moon, drawn at its real phase by the same geometry as the panel below, and placed
 * where the moon actually is. It is faint in daylight and absent when it is new, which is
 * how it behaves.
 */
.orb-moon {
  left: var(--moon-x);
  top: var(--moon-y);
  width: clamp(1.75rem, 4.5vmin, 2.75rem);
  aspect-ratio: 1;
  opacity: var(--moon-opacity);
  filter: drop-shadow(0 0 0.75rem rgb(226 236 255 / 0.4));
}

.orb-moon svg {
  width: 100%;
  height: 100%;
  display: block;
}

/* Mirrored below the equator, for the same reason the panel's moon is. */
.orb-moon svg.mirrored {
  transform: scaleX(-1);
}

.scene-moon-disc {
  fill: rgb(255 255 255 / 0.06);
}

.scene-moon-lit {
  fill: #eef3fb;
}

/*
 * A faint starfield, revealed as the sun goes down. Pure CSS — no image requests.
 *
 * Fourteen stars, tiled. `background-size` cycles over the layers when the list is the
 * shorter of the two, so three tile sizes that do not divide into each other are enough to
 * fill the sky without the eye finding the repeat.
 */
.stars {
  position: absolute;
  inset: 0;
  opacity: var(--star-opacity);
  background-size: 43% 37%, 61% 53%, 79% 67%;
  background-image:
    radial-gradient(1.5px 1.5px at 12% 18%, rgba(255, 255, 255, 0.85), transparent),
    radial-gradient(1px 1px at 28% 42%, rgba(255, 255, 255, 0.6), transparent),
    radial-gradient(1.5px 1.5px at 47% 12%, rgba(255, 255, 255, 0.75), transparent),
    radial-gradient(1px 1px at 63% 31%, rgba(255, 255, 255, 0.5), transparent),
    radial-gradient(1.5px 1.5px at 78% 8%, rgba(255, 255, 255, 0.8), transparent),
    radial-gradient(1px 1px at 88% 38%, rgba(255, 255, 255, 0.55), transparent),
    radial-gradient(1px 1px at 35% 62%, rgba(255, 255, 255, 0.45), transparent),
    radial-gradient(1.5px 1.5px at 71% 55%, rgba(255, 255, 255, 0.6), transparent),
    radial-gradient(1px 1px at 6% 52%, rgba(255, 255, 255, 0.4), transparent),
    radial-gradient(1.5px 1.5px at 21% 71%, rgba(255, 255, 255, 0.5), transparent),
    radial-gradient(1px 1px at 42% 26%, rgba(255, 255, 255, 0.42), transparent),
    radial-gradient(1px 1px at 56% 66%, rgba(255, 255, 255, 0.38), transparent),
    radial-gradient(1.5px 1.5px at 92% 20%, rgba(255, 255, 255, 0.65), transparent),
    radial-gradient(1px 1px at 83% 61%, rgba(255, 255, 255, 0.44), transparent);
}


/*
 * A soft wash at the top and foot of the scene, in whichever direction the page's text is
 * going. It buys back the contrast that a saturated sky costs the masthead and the footer,
 * the only text here that does not sit on a card of its own, and it reads as haze.
 */
.sky::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(to bottom, var(--scrim) 0%, rgb(0 0 0 / 0) 32%),
    linear-gradient(to top, var(--scrim) 0%, rgb(0 0 0 / 0) 28%);
}

/* ---------- Masthead ---------- */

.masthead {
  text-align: center;
  max-width: var(--measure);
}

.today {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: baseline;
  gap: 0.2rem 0.55rem;
  margin: 0 0 0.35rem;
  font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/*
  Tabular figures so a minute ticking over does not nudge the date sideways, and a separator
  carried by the clock rather than typed into the markup so nothing is left dangling on the
  line above when the two wrap apart on a narrow screen.
*/
.clock {
  font-variant-numeric: tabular-nums;
}

.clock::before {
  content: '·';
  margin-right: 0.55rem;
  letter-spacing: 0;
}

.wordmark {
  margin: 0;
  font-size: clamp(1.75rem, 5vw, 2.5rem);
  font-weight: 400;
  letter-spacing: 0.02em;
}

/* ---------- Message ---------- */

main {
  width: 100%;
  max-width: var(--measure);
  display: flex;
  flex-direction: column;
  gap: var(--step);
}

.message-card {
  background: var(--surface);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: clamp(1.75rem, 5vw, 2.75rem);
}

.message {
  margin: 0;
  font-size: clamp(1.2rem, 3.4vw, 1.5rem);
  line-height: 1.55;
  text-wrap: pretty;
  opacity: 0;
  transition: opacity 0.9s ease;
}

.message.is-loaded {
  opacity: 1;
}

.message p {
  margin: 0;
}

.message-loading {
  color: var(--ink-faint);
  font-style: italic;
}

/* Opening quote mark, set outside the text so it does not disturb the measure. */
.message.is-loaded::before {
  content: '';
  display: block;
  width: 2rem;
  height: 1px;
  margin-bottom: 1.25rem;
  background: var(--accent);
  opacity: 0.6;
}

/* ---------- Daily extras ---------- */

/*
  The enigma sits between the message and the sky, and is deliberately quieter than the
  message: the same `.panel` chrome the cycles wear, so the page still reads as one thing
  with the message at the top of it. Only existing tokens are used here, which is what
  keeps both themes correct with no new colour to get wrong.
*/
.daily {
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
}

.enigma-question {
  text-wrap: pretty;
}

.enigma-answer {
  margin-top: 0.75rem;
  font-size: 0.95rem;
}

.enigma-answer > summary {
  display: inline-block;
  padding: 0.3rem 0.7rem;
  border: 1px solid var(--edge);
  border-radius: 999px;
  font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
  cursor: pointer;
}

.enigma-answer > summary:hover {
  color: var(--ink);
  border-color: var(--accent);
}

/* The same ring every other focusable control here gets. */
.enigma-answer > summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.enigma-answer[open] > summary {
  color: var(--ink-soft);
  border-color: var(--edge);
}

.enigma-answer > p {
  margin: 0.8rem 0 0;
  color: var(--ink-soft);
  text-wrap: pretty;
}

/*
  The fact sits under the enigma in the same chrome, and its source is the first link the
  site has ever had. It is deliberately quiet — the fact is the thing being read, the
  citation is there to be checked — and it is drawn from the same tokens as everything
  else, so both themes stay correct without a new colour to get wrong.
*/
.fact-text {
  text-wrap: pretty;
}

.fact-source {
  margin: 0.75rem 0 0;
  font-size: 0.85rem;
  color: var(--ink-faint);
  text-wrap: pretty;
}

.fact-source a {
  color: var(--ink-soft);
  text-decoration-color: var(--edge);
  text-underline-offset: 0.2em;
}

.fact-source a:hover {
  color: var(--ink);
  text-decoration-color: var(--accent);
}

/* The same ring every other focusable control here gets. */
.fact-source a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* ---------- Cycles ---------- */

.cycles {
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
}

.section-heading {
  margin: 0 0 0.25rem;
  font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.panel {
  display: flex;
  align-items: flex-start;
  gap: 1.25rem;
  padding: 1.35rem clamp(1.25rem, 4vw, 1.75rem);
  background: var(--surface);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
}

.panel-body {
  flex: 1;
  min-width: 0;
}

.panel-label {
  margin: 0 0 0.2rem;
  font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.panel-headline {
  margin: 0;
  font-size: 1.2rem;
}

.panel-detail {
  margin: 0.4rem 0 0;
  font-size: 0.95rem;
  color: var(--ink-soft);
  text-wrap: pretty;
}

/* ---------- Moon ---------- */

.moon-visual {
  flex: none;
  width: 5rem;
  height: 5rem;
}

.moon-visual svg {
  width: 100%;
  height: 100%;
  overflow: visible;
}

/* Southern-hemisphere viewers see the moon mirrored; show them their own sky. */
.moon-visual svg.mirrored {
  transform: scaleX(-1);
}

.moon-disc {
  fill: var(--moon-dark);
}

/* The `d` attribute is not CSS-transitionable when set via setAttribute, so the phase
   change is instantaneous by design rather than animated. */
.moon-lit {
  fill: var(--moon-lit);
  filter: drop-shadow(0 0 12px rgba(246, 239, 226, 0.35));
}

/*
  The lunation timeline: where today sits in the current lunar month.

  The track is not a progress bar. It is shaded the way the moon is lit across the month —
  dark at new, bright at full, dark again at new — so a marker three quarters of the way
  along cannot be misread as "three quarters lit". The position along the track is the only
  thing that means anything; the shading underneath is there to say what the position means.
*/
.lunation {
  margin: 0.95rem 0 0;
}

.lunation-track {
  position: relative;
  height: 0.45rem;
  border: 1px solid var(--edge);
  border-radius: 999px;
  background: linear-gradient(
    to right,
    var(--moon-dark) 0%,
    var(--moon-lit) 50%,
    var(--moon-dark) 100%
  );
}

/* The quarters, unlabelled: structure the eye can pick up without another row of words. */
.lunation-tick {
  position: absolute;
  top: -0.2rem;
  bottom: -0.2rem;
  width: 1px;
  background: var(--ink-faint);
  opacity: 0.35;
}

.lunation-tick.at-first-quarter {
  left: 25%;
}

/* Full gets a touch more weight: it is the one the eye looks for, and the one we label. */
.lunation-tick.at-full {
  left: 50%;
  opacity: 0.55;
}

.lunation-tick.at-last-quarter {
  left: 75%;
}

/*
  Today. `--lunation-position` is set from the lunation fraction in app.js; at the very start
  or end of a month the marker sits half outside the track, which is exactly where it belongs,
  so nothing here clips.
*/
.lunation-marker {
  position: absolute;
  top: 50%;
  left: var(--lunation-position, 0%);
  width: 0.62rem;
  height: 0.62rem;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: var(--accent);
  /* A halo of the panel's own surface, so the dot reads against dark and bright track alike. */
  box-shadow: 0 0 0 0.22rem var(--surface);
  transition: left 0.8s ease;
}

.lunation-scale {
  display: flex;
  justify-content: space-between;
  margin: 0.4rem 0 0;
  font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: 0.68rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

@media (prefers-reduced-motion: reduce) {
  .lunation-marker {
    transition: none;
  }
}

/* ---------- Fact lists ---------- */

.mini-facts {
  margin: 0.85rem 0 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.3rem 1rem;
  font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: 0.85rem;
}

.mini-facts:empty {
  margin: 0;
}

.mini-facts dt {
  color: var(--ink-faint);
  white-space: nowrap;
}

.mini-facts dd {
  margin: 0;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

/* ---------- Location ---------- */

.location {
  text-align: center;
  padding: 0 0.5rem;
}

.location-status {
  margin: 0 0 1.1rem;
  font-size: 0.95rem;
  color: var(--ink-soft);
  text-wrap: pretty;
}

/* ---------- Place picker ---------- */

/*
 * Deliberately two plain `<select>`s. They come with keyboard support, type-ahead and a
 * native picker sheet on phones, all of which a hand-rolled dropdown would have to
 * reimplement worse. Only the resting state is restyled — the chevron and the open list
 * stay native, and follow `color` and the page's `color-scheme` into dark mode on their own.
 */
.picker {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.75rem;
  margin: 0 auto 1.1rem;
  max-width: 30rem;
}

.field {
  flex: 1 1 12rem;
  max-width: 16rem;
  margin: 0;
  text-align: left;
}

.field-label {
  display: block;
  margin-bottom: 0.35rem;
  font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.field-input {
  width: 100%;
  font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: 0.9rem;
  color: var(--ink);
  background: var(--surface-solid);
  border: 1px solid var(--edge);
  border-radius: 0.85rem;
  padding: 0.55rem 0.7rem;
  box-shadow: var(--shadow);
  cursor: pointer;
  transition: box-shadow 0.2s ease, opacity 0.2s ease;
}

.field-input:disabled {
  opacity: 0.55;
  cursor: default;
}

.field-input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.location-actions {
  display: flex;
  justify-content: center;
  gap: 0.6rem;
  flex-wrap: wrap;
}

.button {
  font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: 0.9rem;
  color: var(--ink);
  background: var(--surface-solid);
  border: 1px solid var(--edge);
  border-radius: 999px;
  padding: 0.55rem 1.35rem;
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.2s ease, opacity 0.2s ease;
  box-shadow: var(--shadow);
}

.button:hover:not(:disabled) {
  transform: translateY(-1px);
}

.button:disabled {
  opacity: 0.55;
  cursor: default;
}

.button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.button-quiet {
  background: transparent;
  box-shadow: none;
  color: var(--ink-faint);
}

.location-note {
  margin: 0.75rem 0 0;
  font-size: 0.85rem;
  color: var(--ink-faint);
  min-height: 1.2em;
  text-wrap: pretty;
}

/* ---------- Footer ---------- */

.footer {
  max-width: var(--measure);
  text-align: center;
  color: var(--ink-faint);
  font-size: 0.85rem;
}

.footer p {
  margin: 0 0 0.6rem;
}

.fine-print {
  font-size: 0.78rem;
  line-height: 1.5;
  opacity: 0.85;
  text-wrap: pretty;
}

/* ---------- Utility ---------- */

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

@media (max-width: 26rem) {
  .panel {
    flex-direction: column;
    align-items: center;
    text-align: center;
  }

  .mini-facts {
    justify-content: center;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
  }
}
