/*
 * The one stylesheet (ADR-0004). Tokens are copied from the design system's
 * colors/shape tokens; the card internals keep the design files' inline styles
 * in app.js so the two never drift apart in two places at once.
 */

:root {
  /* ── Base neutrals — the light instrument surface ── */
  --plane:      #eef1f4;
  --panel:      #ffffff;
  --panel-2:    #f7f9fb;
  --ink:        #0f2233;
  --ink-soft:   #45596a;
  --muted:      #8090a0;
  --hairline:   #dbe2e8;
  --hairline-2: #eaeef2;

  /* ── Brand blues ── */
  --navy:     #0f2e44;
  --tide:     #1e88c4;
  --tide-ink: #14688f;

  /* ── Status inks used by the windows ── */
  --caution-ink: #946200;
  --stop-ink:    #b23636;

  /* Night: a saturated deep blue, not navy and not black. The brand navy is
     only faintly blue, and thinned to a wash over white it reads as grey — the
     hue has to carry the colour at these alphas, so night gets a blue of its
     own. Full strength through the small hours, easing off across civil
     twilight so full light lands exactly on sunrise and sunset. --night lies on
     the white card, --night-deep on the tide gradient in the mobile hero bar;
     --night-0 is the same blue at zero alpha, named so the gradients fade to
     clear blue rather than through grey. */
  --night:      rgba(23,80,156,.17);
  --night-deep: rgba(23,80,156,.6);
  --night-0:    rgba(23,80,156,0);

  --link:       var(--tide-ink);
  --link-hover: var(--navy);

  --font-sans:   "Nunito Sans", system-ui, sans-serif;
  --font-figure: "Nunito Sans", system-ui, sans-serif;
  --radius-panel: 14px;

  /* One curve for everything that moves, so the page moves like one hand. */
  --ease: cubic-bezier(.2,.6,.3,1);

  /* wind trace — base load, pale gust envelope. Paler than it would be on its own: the
     trace runs the full height of the weather strip, behind the icons and temperatures,
     and has to read as ground rather than compete with them.

     Both edges of the band are drawn, and are meant to be read. They carry the link to the
     figures below: --wind-line is the wind itself and takes the ink of the bold figure
     printed under it, --wind-gust-line the envelope above and the tone of the fainter
     figure. Weight for weight, so which number is which can be seen rather than learnt. */
  --wind-line:      rgba(15,46,68,.7);
  --wind-base:      rgba(15,46,68,.13);
  --wind-gust:      rgba(15,46,68,.06);
  --wind-gust-line: rgba(15,46,68,.3);

  /* The temperature is printed straight onto the wind trace, and the trace runs right
     through it. A knock-out alone only cut a hole for it to sit in; this is the same move
     .wg-lift makes for the icons, so the two rows float off the graph together instead of
     one being lifted and the other embedded. The white passes come first and so paint over
     the dark one — that is what keeps the shadow to a soft edge below the figure rather
     than a grey fringe all round it. Doubled because one pass of --panel is not opaque
     enough to bury the gust line where it crosses a thin stroke of a numeral. */
  --temp-lift: 0 0 2px var(--panel), 0 0 2px var(--panel), 0 0 3px var(--panel),
               0 1.5px 2px rgba(15,46,68,.34);
}

body {
  margin: 0;
  background: #e3eef6;
  color: var(--ink);
  font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased;
}
a { color: var(--link); text-decoration: none; }
a:hover { color: var(--link-hover); }

/* ── Glyph-layer motion; the instrument layer stays still ──
   All of it is CSS rather than SMIL: every card is written with innerHTML, and
   CSS animations start reliably on injected markup where SMIL does not. Phase
   offsets ride on an inline animation-delay so each drop, flake and cloud runs
   on its own clock. */
@keyframes wg-spin  { to { transform: rotate(360deg); } }
@keyframes wg-drift { 0%,100% { transform: translateX(-3px); } 50% { transform: translateX(3px); } }
@keyframes wg-drift-dark { 0%,100% { transform: translateX(-2.1px); } 50% { transform: translateX(2.1px); } }
@keyframes wg-fog   { 0%,100% { transform: translateX(-4px); } 50% { transform: translateX(4px); } }
@keyframes wg-drop  { 0% { transform: translate(1px,-5px); opacity: 0; }
                      25%,72% { opacity: 1; }
                      100% { transform: translate(-2px,10px); opacity: 0; } }
@keyframes wg-fall-a { 0% { transform: translate(-1px,-6px); opacity: 0; }
                       12%,86% { opacity: 1; }
                       100% { transform: translate(1px,12px); opacity: 0; } }
@keyframes wg-fall-b { 0% { transform: translate(1px,-6px); opacity: 0; }
                       12%,86% { opacity: 1; }
                       100% { transform: translate(-1px,12px); opacity: 0; } }
@keyframes wg-flash { 0%,54% { opacity: 1; } 56% { opacity: .1; } 58% { opacity: 1; }
                      60% { opacity: .1; } 62%,100% { opacity: 1; } }
@keyframes wg-twinkle { 0%,100% { opacity: .45; } 50% { opacity: 1; } }

.wg-spin       { transform-box: fill-box; transform-origin: center; animation: wg-spin 12s linear infinite; }
.wg-flake-spin { transform-box: fill-box; transform-origin: center; animation: wg-spin 4s linear infinite; }
.wg-drift      { animation: wg-drift 4s ease-in-out infinite; }
.wg-drift-dark { animation: wg-drift-dark 4s ease-in-out infinite; }
.wg-fog        { animation: wg-fog 3.5s ease-in-out infinite; }
.wg-drizzle    { animation: wg-drop 1.1s linear infinite; }
.wg-rain       { animation: wg-drop .55s linear infinite; }
.wg-fall-a     { animation: wg-fall-a 2.6s linear infinite; }
.wg-fall-b     { animation: wg-fall-b 2.6s linear infinite; }
.wg-flash      { animation: wg-flash 1.6s linear infinite; }
.wg-twinkle    { animation: wg-twinkle 2.2s ease-in-out infinite; }

/* A white cloud on a white card needs more than an outline to sit above it.
   The shadow is on the cloud path rather than the icon, so the drops and
   flakes animating alongside it are outside the filtered subtree and cannot
   force it to re-rasterise every frame. Being inside the SVG, the lengths are
   user units on the 64 viewBox — about 1px once the icon is drawn at 28. */
.wg-lift { filter: drop-shadow(0 2.3px 2.3px rgba(15,46,68,.22)); }

@media (prefers-reduced-motion: reduce) {
  [class*="wg-"] { animation: none !important; }
}

/* ── Banner ── */
header {
  background: var(--panel);
  border-bottom: 1px solid var(--hairline);
  position: sticky;
  top: 0;
  z-index: 10;
}
.hwrap { max-width: 940px; margin: 0 auto; padding: 11px 16px 10px; }

/* ── The day-card column ── */
.wrap  { max-width: 940px; margin: 0 auto; padding: 12px 16px 0; }
.cards { display: flex; flex-direction: column; gap: 12px; }
.foot  { padding-top: 12px; padding-bottom: 40px; font-family: var(--font-figure);
         font-size: 10.5px; color: var(--muted); line-height: 1.6; }

/* White instrument field: hairline border, no shadow (design-system Panel, radius 14). */
.card {
  position: relative;
  background: var(--panel);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-panel);
  box-shadow: none;
  overflow: hidden;
}

/* Today is the card with the tide-blue rim. The ring is drawn as a shadow, not
   a thicker border, so today's card is exactly the same size as every other. */
.card.today {
  border-color: var(--tide);
  box-shadow: 0 0 0 1px var(--tide);
}

/* ── The now-line ──
   A head on the line, so the live mark begins at a point rather than at a cut
   edge and reads as a mark rather than a rule. Drawn from the line itself: the
   line owns the position, and this rides along with it. */
.nowline::before {
  content: "";
  position: absolute;
  left: 50%;
  top: -2px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--navy);
  transform: translateX(-50%);
}

/* One ring off the head, slowly, every three seconds. Not a breathing glow — an
   emission, so it reads as the card being live rather than as something merely
   highlighted.
   The ring takes about a second to go and then there is nothing for two. That
   ratio is the whole point: a mark that never stops has to be at rest most of
   the time or it stops being a signal and becomes a thing to look away from.
   Opacity is driven entirely by the animation, so with motion turned off the
   ring simply is not there and the solid head is the whole mark. */
@keyframes now-ping {
  0%           { transform: translateX(-50%) scale(1);   opacity: .4; }
  36%          { opacity: 0; }
  36.01%, 100% { transform: translateX(-50%) scale(3.4); opacity: 0; }
}
.nowline::after {
  content: "";
  position: absolute;
  left: 50%;
  top: -2px;
  width: 6px;
  height: 6px;
  box-sizing: border-box;
  border: 1.5px solid var(--navy);
  border-radius: 50%;
  opacity: 0;
  transform: translateX(-50%);
  animation: now-ping 3s cubic-bezier(.25,.6,.35,1) infinite;
}


/* ── Pull down to reload ──
   Only built when the page is running as an installed app (`wirePullToRefresh`),
   where there is no address bar to pull on. It sits a layer below the banner and
   starts tucked behind it, so the pull draws it out from under the wordmark. */
#pull {
  position: fixed;
  top: 0;
  left: 50%;
  z-index: 9;
  width: 30px;
  height: 30px;
  margin-left: -15px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--panel);
  border: 1px solid var(--hairline);
  box-shadow: 0 2px 8px rgba(15,34,51,.16);
  pointer-events: none;
  transform: translateY(-40px);
}
/* Only while it is finding its way home — during the drag it tracks the finger. */
#pull.settling { transition: transform 260ms var(--ease); }
/* Past the commit point: the mark goes navy, so the release is committed before
   the finger lifts rather than being a guess about how far is far enough. */
#pull.armed { border-color: var(--tide); }
#pull.armed svg { stroke: var(--navy); }
#pull.spinning svg { animation: wg-spin .7s linear infinite; }

/* The document must not bounce under a gesture the page is drawing itself. */
html.standalone, html.standalone body { overscroll-behavior-y: contain; }

@media (prefers-reduced-motion: reduce) {
  .nowline::after { animation: none; }
  #pull.spinning svg { animation: none; }
}

/* The mobile design takes over below the ~700px breakpoint (ADR-0008). */
@media (max-width: 700px) {
  /* The whole card is a scrub surface on a phone, so the browser has to be
     told it still owns vertical panning — otherwise the page cannot scroll
     anywhere a card covers, which is nearly everywhere. Horizontal sweeps
     come to us. */
  .card { touch-action: pan-y; }

  .hwrap { max-width: 480px; padding: 11px 14px 10px; }
  .wrap  { max-width: 480px; padding: 10px 10px 0; }
  .cards { gap: 10px; }
  .foot  { padding-left: 14px; padding-right: 14px; }
}
