:root {
  --color-bg: #14161a;
  --color-surface: #1d2029;
  --color-text: #f4f1ea;
  --color-text-muted: #a8a9ad;
  /* "Marigold & Twilight" — finalized in Phase 11 from a 4-way visual comparison. Every value
     here and in the light-theme override below is WCAG AA-verified (>=4:1 against this theme's
     bg/surface — see specs/2026-08-19-visual-brand-polish/validation.md for the full table);
     re-verify against that same bar before changing any of them. */
  --color-golden: #f2a52c;
  --color-blue: #6584e6;
  --font-sans: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  /* Deliberately fixed regardless of light/dark theme — the "featured" card is meant to read
     as a spotlight, consistently vivid either way, not to blend with the ambient theme. Reuses
     the base dark-theme gold and light-theme blue directly (one fewer bespoke value to keep in
     sync) — both independently verified as strong pairings with their fixed text colours
     (~8.8:1 dark text on gold, ~7.5:1 light text on blue). */
  --color-golden-featured-bg: #f2a52c;
  --color-golden-featured-text: #14161a;
  --color-golden-featured-text-muted: rgba(20, 22, 26, 0.75);
  --color-blue-featured-bg: #31469e;
  --color-blue-featured-text: #f4f1ea;
  --color-blue-featured-text-muted: rgba(244, 241, 234, 0.75);

  /* Depth scale (Phase 11) — shared shadow tokens so elevation reads consistently across cards,
     the popover, and buttons instead of each surface inventing its own box-shadow values. */
  --shadow-sm: 0 0.2rem 0.6rem rgba(0, 0, 0, 0.18);
  --shadow-md: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.22);

  /* Button surface treatment — a shallow top-to-bottom gradient plus a paired inset
     highlight/shade reads as a physically raised, pressable surface instead of a flat outline.
     --button-shadow-rest is the resting bevel (light rim top, shaded edge bottom); on :active it
     swaps for --button-shadow-pressed, a deeper, top-only inner shadow with the outer
     --shadow-sm/--shadow-md dropped, so the button visibly pushes in rather than just recolouring. */
  --button-gradient: linear-gradient(to bottom, rgba(255, 255, 255, 0.07), rgba(0, 0, 0, 0.15));
  --button-shadow-rest: inset 0 1px 0 rgba(255, 255, 255, 0.12), inset 0 -2px 3px rgba(0, 0, 0, 0.25);
  --button-shadow-pressed: inset 0 2px 4px rgba(0, 0, 0, 0.4), inset 0 -1px 0 rgba(255, 255, 255, 0.03);
}

@media (prefers-color-scheme: light) {
  :root {
    --color-bg: #f7f5f0;
    --color-surface: #ffffff;
    --color-text: #14161a;
    --color-text-muted: #55575c;
    --color-golden: #a8640a;
    --color-blue: #31469e;
    /* Shadows read heavier against a light ground than a dark one at the same opacity — lighten
       both tokens here so elevation looks equivalent in both themes rather than muddier in
       light mode. */
    --shadow-sm: 0 0.2rem 0.6rem rgba(20, 22, 26, 0.1);
    --shadow-md: 0 0.5rem 1.5rem rgba(20, 22, 26, 0.14);
    /* Same bevel idea against a white surface: the top highlight is now the shaded side (near-
       invisible on white) and the bottom edge does the work of separating the button from the
       page. */
    --button-gradient: linear-gradient(to bottom, rgba(255, 255, 255, 0.9), rgba(20, 22, 26, 0.05));
    --button-shadow-rest: inset 0 1px 0 rgba(255, 255, 255, 0.8), inset 0 -1px 2px rgba(20, 22, 26, 0.12);
    --button-shadow-pressed: inset 0 2px 4px rgba(20, 22, 26, 0.2), inset 0 -1px 0 rgba(255, 255, 255, 0.5);
  }
}

* {
  box-sizing: border-box;
}

.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;
}

html,
body {
  margin: 0;
  padding: 0;
}

body {
  min-height: 100vh;
  background-color: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-sans);
}

/* Ambient background: physically gold/blue during the actual hour, with a livelier pulse;
   a fainter neutral pulse during the 20-minute run-up/run-down either side; nothing at all
   the rest of the time. Driven by body[data-ambient], set in js/app.js. Layered behind all
   content via a fixed, negative-z-index pseudo-element so it never affects text contrast on
   the opaque cards sitting above it. */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(ellipse 120% 65% at 50% -5%, var(--ambient-color, var(--color-text-muted)), transparent 70%);
}

body[data-ambient]::before {
  animation: ambient-pulse 9s ease-in-out infinite alternate;
}

body[data-ambient="active"] {
  --ambient-color: var(--color-text-muted);
  --ambient-min: 0.06;
  --ambient-max: 0.16;
}

body[data-ambient="gold"] {
  --ambient-color: var(--color-golden);
  --ambient-min: 0.22;
  --ambient-max: 0.43;
}

body[data-ambient="blue"] {
  --ambient-color: var(--color-blue);
  --ambient-min: 0.22;
  --ambient-max: 0.43;
}

@keyframes ambient-pulse {
  0% {
    opacity: var(--ambient-min);
    transform: scale(1) translateY(0);
  }
  100% {
    opacity: var(--ambient-max);
    transform: scale(1.12) translateY(1.5%);
  }
}

@media (prefers-reduced-motion: reduce) {
  body[data-ambient]::before {
    animation: none;
    opacity: var(--ambient-max);
  }
}

header {
  text-align: center;
  padding: 1.5rem 1rem 0.5rem;
}

header h1 {
  margin: 0;
}

.logo {
  display: inline-block;
  width: min(90%, 18rem);
  height: auto;
}

/* Reclaims the desktop title-bar row for the logo on installs that support window-controls-overlay
   (manifest display_override). Scoped entirely to this media query so every other context —
   mobile, browser tab, unsupported desktop browsers — renders the header exactly as above,
   satisfying the roadmap's "with fallback" requirement by construction. */
@media (display-mode: window-controls-overlay) {
  header {
    position: fixed;
    top: env(titlebar-area-y, 0);
    left: env(titlebar-area-x, 0);
    width: env(titlebar-area-width, 100%);
    height: env(titlebar-area-height, 2.25rem);
    padding: 0 0.75rem;
    display: flex;
    align-items: center;
    justify-content: flex-start;
    background: var(--color-bg);
    -webkit-app-region: drag;
    app-region: drag;
    z-index: 10;
  }

  header h1 {
    margin: 0;
    line-height: 0;
  }

  .logo {
    /* A fixed unit, not a percentage — percentage height can't resolve here since none of
       header's descendants (h1 > picture > img) have a definite height of their own to
       inherit from, so it would silently fall back to the image's natural, oversized height. */
    width: auto;
    height: 1.5rem;
    max-width: 10rem;
  }

  main {
    margin-top: env(titlebar-area-height, 2.25rem);
  }
}

main {
  max-width: 36rem;
  margin: 0 auto;
  padding: 1rem 1rem 3rem;
  display: flex;
  flex-direction: column;
  gap: 2rem;
}

/* A modest widening for tablet viewports — just a more generous reading measure than the
   mobile-first 36rem once there's clearly room for it. The full multi-column reflow only kicks
   in at the wider desktop breakpoint below. */
@media (min-width: 48rem) {
  main {
    max-width: 42rem;
  }

  .transition-diagram {
    max-width: 26rem;
  }
}

/* Same stack as the single-column layout — a div, not a section, since it's a layout grouping
   with no heading of its own; the three sections inside keep their own headings and semantics. */
.windows-grid {
  display: flex;
  flex-direction: column;
  gap: 2rem;
}

/* Desktop: the hero/location content above stays at the same 42rem reading measure (so it
   doesn't stretch into an awkwardly wide strip), while the Sun/Golden hour/Blue hour sections —
   three parallel, same-shape groups — spread into real columns instead of just growing taller. */
@media (min-width: 64rem) {
  main {
    max-width: 68rem;
  }

  #transition-section,
  #featured-section,
  #location-section {
    max-width: 42rem;
    width: 100%;
    margin-left: auto;
    margin-right: auto;
  }

  .windows-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    align-items: start;
    gap: 1.5rem;
  }
}

.offline-indicator {
  margin: 0;
  padding: 0.6rem 0.9rem;
  border: 1px solid var(--color-text-muted);
  border-radius: 0.5rem;
  background: var(--color-surface);
  color: var(--color-text-muted);
  font-size: 0.85rem;
  text-align: center;
}

main section {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

main section h2 {
  margin: 0;
  font-size: 1.1rem;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

#transition-section {
  gap: 0.4rem;
}

.transition-diagram {
  width: 100%;
  max-width: 22rem;
  margin: 0 auto;
}

.transition-svg {
  display: block;
  width: 100%;
  height: auto;
}

.transition-summary {
  margin: 0;
  text-align: center;
  font-size: 1rem;
}

.weather-warning {
  margin: 0;
  text-align: center;
  font-size: 0.85rem;
  color: var(--color-text-muted);
}

.weather-warning[data-tier="caution"],
.weather-warning[data-tier="warning"] {
  color: var(--color-text);
  font-weight: 600;
}

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

.notify-toggle,
.share-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.85rem;
  padding: 0.4rem 0.8rem;
}

/* The class above's unconditional `display: inline-flex` otherwise beats the browser's built-in
   `[hidden] { display: none }` rule (same specificity, author origin wins), so a JS-set `hidden`
   attribute alone doesn't visually hide either toggle. */
.notify-toggle[hidden],
.share-toggle[hidden] {
  display: none;
}

/* fill/stroke via currentColor — matches whatever text colour the button already resolves to
   (default, gold-filled "on" state, disabled, any theme), so the icon never needs its own
   colour rules. Decorative only: the button's own text already carries its accessible name. */
.btn-icon {
  flex-shrink: 0;
  fill: currentColor;
}

.btn-icon-node {
  fill: currentColor;
}

.btn-icon-line {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.3;
  stroke-linecap: round;
}

/* Filled, not just outlined — gold text/border on the light-theme white surface only reaches
   ~2.25:1 contrast, under WCAG AA's 3:1 minimum for UI component boundaries. Reusing the
   featured-card's gold pairing (already verified ~8.45:1 dark-on-gold) sidesteps that instead
   of inventing a new low-contrast combination. */
.notify-toggle[aria-pressed="true"] {
  background: linear-gradient(to bottom, rgba(255, 255, 255, 0.2), rgba(0, 0, 0, 0.1)), var(--color-golden-featured-bg);
  border-color: var(--color-golden-featured-bg);
  color: var(--color-golden-featured-text);
  box-shadow: var(--button-shadow-pressed);
}

.notify-toggle:disabled {
  cursor: not-allowed;
  opacity: 0.6;
}

.notify-status {
  margin: 0;
  text-align: center;
  font-size: 0.8rem;
  color: var(--color-text-muted);
}

.transition-label {
  fill: var(--color-text-muted);
  font-family: var(--font-sans);
  font-size: 9px;
}

.transition-sun-icon line,
.transition-sun-icon path {
  stroke: var(--color-text);
  stroke-width: 1.3;
  stroke-linecap: round;
}

/* The marker is a stable, reused DOM node across renders within the same combined window (see
   js/transition-diagram.js) — that continuity is what lets these transitions actually animate
   from one position/colour to the next, rather than jumping between unrelated elements.
   Segments themselves practically never change colour mid-window, but the transition is kept
   here defensively/for correctness in case that ever isn't true. */
.transition-marker {
  transition: cx 0.6s linear, cy 0.6s linear, stroke 0.6s ease;
}

.transition-segment,
.transition-segment--interactive {
  transition: stroke 0.4s ease;
}

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

.transition-segment--interactive {
  cursor: pointer;
}

.transition-segment--interactive:focus-visible {
  outline: 2px solid var(--color-text);
  outline-offset: 2px;
}

/* Visibility is class-driven (.transition-popover--open), not the hidden attribute — the
   hidden attribute's UA stylesheet rule uses `display: none !important`, which would fight
   the discrete `display` transition below. A plain author-controlled class sidesteps that
   entirely. Falls back to an instant, correct show/hide on any browser that doesn't understand
   @starting-style/allow-discrete: the display flip itself still works, just without motion. */
.transition-popover {
  position: fixed;
  z-index: 10;
  max-width: 14rem;
  background: var(--color-surface);
  color: var(--color-text);
  border: 1px solid var(--color-text-muted);
  border-radius: 0.5rem;
  padding: 0.75rem 0.9rem;
  box-shadow: var(--shadow-md);
  display: none;
  opacity: 0;
  transform: scale(0.94);
  transition: opacity 0.18s ease, transform 0.18s ease, display 0.18s allow-discrete;
}

.transition-popover--open {
  display: block;
  opacity: 1;
  transform: scale(1);
}

@starting-style {
  .transition-popover--open {
    opacity: 0;
    transform: scale(0.94);
  }
}

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

/* A dismiss icon, not a primary action — exempt from the raised button treatment below so it
   stays a plain glyph rather than gaining a bevel with no visible surface to bevel. */
.transition-popover-close {
  position: absolute;
  top: 0.25rem;
  right: 0.25rem;
  border: none;
  background: none;
  padding: 0.25rem 0.5rem;
  font-size: 1.1rem;
  line-height: 1;
}

.transition-popover-close,
.transition-popover-close:hover,
.transition-popover-close:active {
  box-shadow: none;
  transform: none;
}

.transition-popover-title {
  margin: 0 1.25rem 0.5rem 0;
  font-weight: 600;
}

.transition-popover-details {
  margin: 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.2rem 0.6rem;
  font-size: 0.85rem;
}

.transition-popover-details dt {
  color: var(--color-text-muted);
}

.transition-popover-details dd {
  margin: 0;
}

#location-section {
  gap: 0.6rem;
}

.location-card {
  border: 1px solid var(--color-text-muted);
  border-radius: 0.85rem;
  background: var(--color-surface);
  box-shadow: var(--shadow-sm);
}

/* Animates the native <details> content reveal — block-size and opacity, through the
   ::details-content pseudo-element (the spec-correct, JS-free way to do this) — instead of the
   browser's instant native toggle. Unsupported browsers simply don't apply these rules at all,
   so they keep today's instant (but fully correct) open/close: graceful degradation by
   construction, not a separate fallback path. */
.location-card::details-content {
  overflow: hidden;
  transition: content-visibility 0.25s allow-discrete, opacity 0.25s ease, block-size 0.25s ease;
  opacity: 0;
  block-size: 0;
}

.location-card[open]::details-content {
  opacity: 1;
  block-size: auto;
}

@starting-style {
  .location-card[open]::details-content {
    opacity: 0;
    block-size: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .location-card::details-content {
    transition: none;
  }
}

.location-card summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  padding: 0.7rem 0.9rem;
}

.location-card summary::-webkit-details-marker {
  display: none;
}

.location-summary-text {
  font-size: 0.95rem;
}

.location-summary-text strong {
  color: var(--color-text);
}

.location-chevron {
  flex-shrink: 0;
  width: 0.6rem;
  height: 0.6rem;
  border-right: 2px solid var(--color-text-muted);
  border-bottom: 2px solid var(--color-text-muted);
  transform: rotate(45deg);
  transition: transform 0.15s ease;
}

.location-card[open] .location-chevron {
  transform: rotate(-135deg);
}

.location-picker {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding: 0 0.9rem 0.9rem;
}

.location-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
}

.location-hint {
  font-size: 0.8rem;
  color: var(--color-text-muted);
}

button {
  font: inherit;
  color: var(--color-text);
  background: var(--button-gradient), var(--color-surface);
  border: 1px solid var(--color-text-muted);
  border-radius: 0.65rem;
  padding: 0.5rem 0.9rem;
  cursor: pointer;
  box-shadow: var(--shadow-sm), var(--button-shadow-rest);
  transition: box-shadow 0.15s ease, border-color 0.15s ease, transform 0.1s ease;
}

button:hover {
  border-color: var(--color-text);
  box-shadow: var(--shadow-md), var(--button-shadow-rest);
}

/* Pushed-in feedback: drop the outer elevation entirely and deepen the inset so the surface
   reads as pressed rather than just darker, with a 1px nudge to sell the motion. */
button:active {
  box-shadow: var(--button-shadow-pressed);
  transform: translateY(1px);
}

button:disabled {
  cursor: not-allowed;
  box-shadow: none;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  button {
    transition: none;
  }
}

.location-search {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.location-search label {
  font-size: 0.8rem;
  color: var(--color-text-muted);
}

.location-search-row {
  display: flex;
  gap: 0.5rem;
}

.location-search-row input {
  flex: 1;
  min-width: 0;
  font: inherit;
  color: var(--color-text);
  background: var(--color-surface);
  border: 1px solid var(--color-text-muted);
  border-radius: 0.4rem;
  padding: 0.5rem 0.7rem;
}

.location-results {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.location-results button {
  width: 100%;
  text-align: left;
}

.location-message {
  margin: 0;
  font-size: 0.85rem;
  color: var(--color-text-muted);
}

footer {
  text-align: center;
  padding: 1.5rem 1rem 2.5rem;
}

footer p {
  margin: 0;
  font-size: 0.8rem;
  color: var(--color-text-muted);
}

footer a {
  color: var(--color-text-muted);
}
