/* Shell, columns and breakpoints. Colours, type and spacing values live in
   components.css/tokens.css; this file only decides where things sit. The
   same markup serves every width — only the rules below change. */

.app-shell {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  padding-left: env(safe-area-inset-left, 0);
  padding-right: env(safe-area-inset-right, 0);
}

.sky {
  position: relative;
  background: linear-gradient(180deg, var(--sky-top), var(--sky-bottom));
}
html.sky-ready .sky { transition: background var(--d-scheme) ease; }
.sky .gauges-host,
.sky .verdict,
.sky .view-control,
.sky .view-log,
.sky .view-analytics {
  background: var(--ground);
}

.app-header {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: var(--z-header);
  /* The extra --s-2 is deliberate, not just the safe area: installed as a
     home-screen app, iPadOS draws a translucent status-bar strip over the
     page (unlike a browser tab, where the inset is 0 and this just adds a
     small, constant bit of breathing room above the title). */
  padding-top: calc(var(--s-4) + env(safe-area-inset-top, 0) + var(--s-2));
  padding-bottom: var(--s-2);
}

.app-main {
  flex: 1;
  /* Room for the fixed tab bar below, sized from tokens rather than a magic
     pixel figure: one tap target plus a section of breathing room. */
  padding-bottom: calc(var(--tap) + var(--s-5) + env(safe-area-inset-bottom, 0));
}

/* Phone: the space reserved behind the fixed tab bar is ground, not sky. Without
   this the sky gradient shows as a pale band above the tab bar at night. The
   literal below mirrors --bp-mid (custom properties cannot be used in media queries). */
@media (max-width: 44.99rem) {
  .app-main {
    background: linear-gradient(to top, var(--ground) calc(var(--tap) + var(--s-5) + env(safe-area-inset-bottom, 0px)), transparent 0);
  }
  /* The header is absolute over the sky. Now clears it with its own sky field;
     Control and Log are plain panels, so they start below it, allowing for the
     notch the header's own top padding already makes room for. The mock banner
     sits above .sky, so it needs no allowance here. Tablet and desktop set the
     same clearance in their own rules below. */
  .view-control,
  .view-log,
  .view-analytics {
    margin-top: calc(var(--header-overlay-h) + env(safe-area-inset-top, 0px));
  }
}

/* Phone (below 720 px): one column, views switched by the tab bar, fixed to
   the viewport bottom. */
.view {
  display: none;
}
.view.is-active {
  display: block;
}
.header-panel-button,
.panel-head {
  display: none;
}

.tab-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: var(--z-tab-bar);
}

@media (prefers-reduced-motion: no-preference) {
  .view.is-active {
    animation: view-fade-in var(--d-ui) ease;
  }
}
@keyframes view-fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* Tablet and desktop: Now owns the page; the other views are dialogs. */
@media (min-width: 45rem) {
  .sky {
    flex: 1 0 auto;
  }
  .tab-bar {
    display: none;
  }
  .app-main {
    display: block;
    padding: 0 var(--gutter) var(--s-6);
  }
  /* .view.is-active sets display:block with two classes; match it to win. */
  .view.view-now {
    display: grid;
    gap: var(--s-4);
    align-items: start;
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
    grid-template-rows: auto 1fr auto auto;
    grid-template-areas: "story verdict" "story gauges" "strip strip" "weather weather";
  }
  .now-panel {
    display: contents;
  }
  .now-side {
    display: contents;
  }
  /* The energy story remains open sky beneath the overlaid header. */
  .energy-story {
    grid-area: story;
  }
  .strip-host {
    grid-area: strip;
    border-radius: var(--r-panel);
    overflow: hidden;
    box-shadow: var(--shadow);
  }
  .verdict {
    grid-area: verdict;
    margin-top: var(--header-overlay-h);
    border-radius: var(--r-panel);
    box-shadow: var(--shadow);
    padding-bottom: var(--s-5);
  }
  .gauges-host {
    grid-area: gauges;
    border-radius: var(--r-panel);
    overflow: hidden;
    box-shadow: var(--shadow);
  }
  .weather-host {
    grid-area: weather;
    border-radius: var(--r-panel);
    overflow: hidden;
    box-shadow: var(--shadow);
  }
  .weather-days {
    overflow: visible;
  }
  .header-panel-button {
    display: inline-flex;
    align-items: center;
  }
  .view-control,
  .view-log,
  .view-analytics {
    display: block;
    position: fixed;
    inset: 0 0 0 auto;
    z-index: var(--z-dialog);
    width: min(26rem, 88vw);
    margin: 0;
    background: var(--surface);
    border-radius: var(--r-panel) 0 0 var(--r-panel);
    box-shadow: var(--shadow);
    overflow-y: auto;
    overscroll-behavior: contain;
    padding-right: env(safe-area-inset-right, 0px);
    padding-bottom: env(safe-area-inset-bottom, 0px);
    transform: translateX(105%);
    visibility: hidden;
    transition: transform var(--d-ui) ease, visibility 0s linear var(--d-ui);
  }
  .view-control.is-panel-open,
  .view-log.is-panel-open,
  .view-analytics.is-panel-open {
    transform: none;
    visibility: visible;
    transition: transform var(--d-ui) ease;
  }
  /* Analytics needs room for its charts: wider than Control and Log, always
     leaving a strip of the Now page visible on the left. */
  .view-analytics {
    width: min(52rem, calc(100vw - 4rem));
  }
  .panel-head {
    display: flex;
    position: sticky;
    top: 0;
    z-index: 1;
    align-items: center;
    justify-content: space-between;
    padding: calc(var(--s-4) + env(safe-area-inset-top, 0px)) calc(var(--s-5) + env(safe-area-inset-right, 0px)) var(--s-4) var(--s-5);
    background: var(--surface);
    border-top-left-radius: var(--r-panel);
    border-bottom: 1px solid var(--line);
  }
  .panel-head h2 {
    margin: 0;
  }
  .panel-scrim {
    position: fixed;
    inset: 0;
    z-index: var(--z-scrim);
    background: var(--scrim);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--d-ui) ease;
  }
  .panel-scrim.is-visible {
    opacity: 1;
    pointer-events: auto;
  }
  html.has-panel-open {
    overflow: hidden;
    overscroll-behavior: contain;
  }
  .sky:has(.alert-banner:not([hidden])) .view-now .strip-host,
  .sky:has(.alert-banner:not([hidden])) .view-now .verdict {
    margin-top: var(--s-4);
  }
}

/* ---- Wide landscape: iPad landscape and desktop ----
   The page is a fixed budget (100dvh), divided top-down: header band, upper
   region, timeline band, bottom gutter. The flow diagram is the only elastic
   element; everything else is either fixed or stretched to the region, so
   nothing above the timeline can ever size the page. Same cutoff as the old
   three-column layout: no device is landscape between 45rem and 1024 px, and
   a desktop window at least 73.75rem wide gets it regardless of shape. */
@media (min-width: 64rem) and (orientation: landscape), (min-width: 73.75rem) {
  .app-shell {
    max-width: var(--page-max);
    margin: 0 auto;
    position: relative;
    height: 100dvh;
  }
  .sky {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
  }

  /* The header floats over the top band of the diagram column (its title on
     the arc's empty top-left sky, its buttons over the right column's
     reserved band); the Now grid reserves that band as its first row, sized
     from the same token, so nothing is ever measured. */
  .app-header {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    box-sizing: border-box;
    height: var(--wide-header-h);
    padding: calc(env(safe-area-inset-top, 0px) + var(--wide-status-clearance)) var(--gutter) 0;
    align-items: flex-start;
  }
  /* With the alert banner showing the header goes back into flow (the
     global rule below the breakpoints), so the band must size to content. */
  .sky:has(.alert-banner:not([hidden])) .app-header {
    height: auto;
    padding-bottom: var(--s-2);
  }

  .app-main {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    padding: 0 var(--gutter) calc(var(--s-4) + env(safe-area-inset-bottom, 0px));
  }

  .view.view-now {
    flex: 1 1 auto;
    min-height: 0;
    display: grid;
    column-gap: var(--s-4);
    row-gap: var(--s-3);
    align-items: stretch;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    /* head: the header band (header is absolute; this row is its shadow).
       Row 2 is the elastic upper region; row 3 the verdict at a fixed three-line height; row 4
       the timeline band. Only row 2 is flexible, so only the diagram moves. */
    grid-template-rows: var(--wide-header-h) minmax(0, 1fr) var(--wide-verdict-h) var(--wide-strip-h);
    grid-template-areas:
      "story head"
      "story side"
      "verdict side"
      "strip strip";
  }
  /* Alert banner showing: the header is in flow above the banner, so the
     reserved band is not needed and the diagram column starts under the
     banner like everything else. */
  .sky:has(.alert-banner:not([hidden])) .view.view-now {
    grid-template-rows: 0 minmax(0, 1fr) var(--wide-verdict-h) var(--wide-strip-h);
  }
  .sky:has(.alert-banner:not([hidden])) .view-now .sky-field {
    margin-top: 0;
  }
  /* The narrower breakpoint's alert-banner rule (above, in the 45rem block)
     still applies here (same or higher specificity, but earlier in source
     order): without this override its leftover top margin pushes the
     already-tightly-budgeted strip and verdict past the bottom edge, since
     both are sized to their row's exact height here, not auto-shrunk by a
     stretch. Same source-order-wins idiom as the sky-field override above. */
  .sky:has(.alert-banner:not([hidden])) .view-now .strip-host,
  .sky:has(.alert-banner:not([hidden])) .view-now .verdict {
    margin-top: 0;
  }

  /* Left column: diagram (elastic) over the compact verdict. */
  .energy-story {
    grid-area: story;
    display: flex;
    flex-direction: column;
    align-items: center;
    min-height: 0;
    min-width: 0;
  }
  .energy-story .sky-field {
    flex: 1 1 0;
    min-height: 0;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0;
  }
  /* The drawing is sized by its cell's HEIGHT and centred; the column is
     always wider than the cell is tall, so width is never the constraint.
     The 390x432 viewBox is shown from row 48 (the sun's halo at solar noon
     peaks at y=50), giving a 390:384 window. */
  .energy-story .arc {
    position: relative;
    height: 100%;
    width: auto;
    max-width: 100%;
    aspect-ratio: 390 / 384;
    margin: 0;
    overflow: hidden;
  }
  .energy-story .arc-svg {
    position: absolute;
    left: 0;
    width: 100%;
    height: auto;
    aspect-ratio: 390 / 432;
    top: calc(-100% * 48 / 384);
  }
  .verdict {
    grid-area: verdict;
    /* Fills its fixed row, so a longer sentence (a preview's) neither moves
       the card's edge nor squeezes the diagram above it. */
    align-self: stretch;
    overflow: hidden;
    margin: 0;
    padding: var(--s-2) var(--s-3);
    border-radius: var(--r-panel);
  }
  /* Under the diagram the verdict is a caption, not a headline: one running
     line or two, the sentence bold and the reason muted, small type. The
     two paragraphs flow inline so a short reason shares the sentence's line. */
  .verdict-title,
  .verdict-reason {
    display: inline;
    margin: 0;
    font-size: var(--t-small);
    line-height: var(--lh-body);
  }
  .verdict-title {
    font-weight: var(--w-bold);
  }
  .verdict-reason {
    color: var(--ink-2);
  }
  .verdict-reason::before {
    content: " ";
  }
  .verdict-icon {
    vertical-align: text-bottom;
  }
  .verdict-icon .icon {
    width: 1em;
    height: 1em;
  }

  /* Right column: two cards stretched to the region so the bars card's top
     meets the header band and the forecast card's bottom meets the verdict's.
     flex: 1 1 auto (not 1 1 0) grows each from its own content, so the
     forecast card, which has more in it, stays the taller of the two. */
  .now-side {
    grid-area: side;
    display: flex;
    flex-direction: column;
    gap: var(--s-3);
    min-height: 0;
    min-width: 0;
  }
  .view-now .gauges-host,
  .view-now .weather-host {
    flex: 1 1 auto;
    min-height: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: var(--s-4) var(--gutter);
    border-top: 0;
  }
  /* The cards stretch to the region, so on taller screens their spacing
     grows with the viewport rather than leaving a hole in the middle. */
  /* Spare height goes 1:2 to the forecast, which has more in it to space. */
  .view-now .weather-host {
    flex-grow: 2;
  }
  .view-now .gauges-host {
    gap: clamp(var(--s-3), 6dvh - 2rem, var(--s-7));
  }
  .weather-heading {
    margin-bottom: clamp(var(--s-2), 4dvh - 1.5rem, var(--s-6));
  }
  .view-now .weather-day {
    gap: clamp(var(--s-1), 1.5dvh - 0.5rem, var(--s-3));
  }
  /* The idle "Select a day…" line is a reserved line the tightest card can't
     spare here; weather.js shows the tapped day's summary as a floating
     panel instead (.weather-float), so the inline line is never shown. */
  .view-now .weather-detail {
    display: none;
  }

  /* Timeline band: strip.js sizes its chart from its container, so handing
     it the row's height is enough to grow the chart into it. */
  .strip-host {
    grid-area: strip;
    margin: 0;
    min-height: 0;
  }
  .strip-host,
  .strip {
    height: 100%;
  }
  .strip-scroll {
    flex: 1 1 auto;
    height: auto;
    min-height: 0;
    margin-bottom: 0;
  }
}

/* Alert showing: the header is normally absolute over the sky, and the banner is
   the first thing in the flow, so they collide. Give the header its own row above
   the banner instead (it stays on the sky, where its text colours are checked for
   contrast, not on the pink banner). The mock banner sits above .sky and is
   unaffected. */
.sky:has(.alert-banner:not([hidden])) .app-header {
  position: static;
}
/* The sky field also keeps the top of its drawing clear for the overlaid header.
   With the header in its own row that band is empty sky under the banner, so pull
   the field up by most of it (the sun's halo still clears the banner). */
.sky:has(.alert-banner:not([hidden])) .view-now .sky-field {
  margin-top: calc(-1 * var(--tap));
}
/* Tablet and desktop must still fit one screen with the banner showing, so the
   field gives back the whole header band there. */
@media (min-width: 45rem) {
  .sky:has(.alert-banner:not([hidden])) .view-now .sky-field {
    margin-top: calc(-1 * var(--header-overlay-h));
  }
}
