/* ============================================================================
   daily-report.css — the sheet's layout.

   ── ⚠ THE ALIGNMENT CONTRACT IS THE GRID, NOT SIX CONSTANTS ───────────────
   The app pins kBarBoxHeight, kAvgRowHeight, kGustRowHeight, kDirRowHeight,
   kLetterRowHeight and kBarGap, and its own comment calls them "the entire
   alignment contract": the frozen left rail rebuilds the same stack of boxes
   the columns use, so the labels land beside the rows they name. Those numbers
   exist because a FROZEN Flutter rail and a SCROLLING column list are separate
   widget subtrees — across that boundary "font metrics are not an alignment
   mechanism but fixed boxes are".

   There is no such boundary here. Each row is ONE grid row whose first cell is
   `position: sticky`, so the label and its columns are literally the same box
   and share their height because they ARE the height. Porting the six numbers
   would have baked constants nothing reads.

   Do not "restore" fixed row heights to make this match the app's source. The
   app's numbers are a workaround for a constraint CSS does not have.
   ========================================================================== */

/* ⚠ TOKEN NAMES ARE theme-tokens.css's, NOT INVENTED ONES. A first draft used
   --muted / --hairline / --band, none of which exist — and an undefined custom
   property does not throw, it silently resolves to nothing, so the rules would
   have shipped with transparent rules and unreadable labels rather than
   failing. Checked against the file, not assumed:
     --text-muted, --line, --line-strong, --surface-sunken, --accent,
     --accent-text, --surface, --text.
   Both themes come free: theme-tokens.css redefines these per theme, so this
   file never names a colour. */
.dr {
  display: flex;
  flex-direction: column;
  gap: 14px;
  color: var(--text);
  background: var(--surface);
}

.dr__head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.dr__title { margin: 0; font-size: 17px; font-weight: 700; letter-spacing: -0.01em; }
.dr__model {
  font-size: 10px; font-weight: 700; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--accent-text); border: 1px solid var(--line-strong);
  border-radius: 2px; padding: 1px 5px;
}

.dr__blurb { display: flex; flex-direction: column; gap: 2px; }
.dr__blurbh {
  margin: 0 0 2px; font-size: 10px; font-weight: 700; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--text-muted);
}
.dr__sum { margin: 0; font-size: 14px; line-height: 1.45; }
.dr__line { margin: 0; font-size: 12px; line-height: 1.45; color: var(--text-muted); }

.dr__loading, .dr__err { margin: 0; font-size: 13px; color: var(--text-muted); }

/* ── the scrolling grid ──────────────────────────────────────────────────── */

.dr__scroll {
  /* ⚠ flex: none IS LOAD-BEARING. #dr-body is both the sheet's vertical
     scroller AND a flex column, so this scroller is a flex ITEM — and a flex
     item shrinks by default. Shrunk, its `overflow-y: hidden` clipped every
     row below the first: the DOM held all nine rows and the screen showed
     Wind alone. A presence check cannot see that, which is why it took a
     screenshot to find. It scrolls SIDEWAYS only; height is content. */
  flex: none;
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  border-top: 1px solid var(--line-strong);
  border-bottom: 1px solid var(--line-strong);
}
.dr__scroll:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.dr__grid { position: relative; width: max-content; min-width: 100%; }

/* Bands sit BEHIND every row rather than being a property of each cell — the
   app draws them with one painter for the same reason: at 112 columns x 9 rows
   a per-cell background is ~1000 decorated boxes. */
.dr__bands { position: absolute; inset: 0; left: var(--rail); pointer-events: none; z-index: 0; }
.dr__band { position: absolute; top: 0; bottom: 0; background: var(--surface-sunken); }
.dr__rule { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--line-strong); }

.dr__row { display: flex; align-items: stretch; position: relative; z-index: 1; }
.dr__row + .dr__row { border-top: 1px solid var(--line); }

/* ⚠ THE STICKY RAIL IS THE CONTRACT. It scrolls vertically with its row and
   freezes horizontally, so a label can never drift off the row it names. */
.dr__rail {
  position: sticky; left: 0; z-index: 2;
  flex: 0 0 var(--rail); width: var(--rail);
  display: flex; align-items: center;
  padding: 6px 10px 6px 2px;
  font-size: 11px; font-weight: 600; letter-spacing: 0.02em;
  color: var(--text-muted);
  background: var(--surface);
  border-right: 1px solid var(--line-strong);
}

.dr__track { display: flex; }
.dr__track--full { position: relative; }

.dr__cell {
  flex: 0 0 var(--slot); width: var(--slot);
  display: flex; align-items: center; justify-content: center;
  padding: 5px 0;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  text-align: center;
}
.dr__cell--chart { align-items: flex-end; padding: 4px 0 6px; }

/* ── ELAPSED HOURS ────────────────────────────────────────────────────────
   ⚠ THE BAR DIMS, THE TEXT DOES NOT, and that is a WCAG constraint rather
   than a style choice. The app fades the whole cell (`_dim`) and Flutter is
   not audited by axe; here, measured:

     opacity 0.45 on the cell     .dr__val   2.9:1 light, 3.85:1 dark
     opacity 0.82 + 0.6 nested    .dr__gust  1.9:1 light, 2.13:1 dark

   The second attempt was WORSE because nested opacities MULTIPLY — 0.82 x 0.6
   is 0.49 — which is the trap in dimming a container and then its children.
   Small text needs 4.5:1 and there is no dim that both reads as faded and
   clears that on --text-muted, which starts near the floor.

   So the signal moves to the BAR, which is a graphic and needs 3:1, and where
   height is already the channel the eye reads first. The numbers stay at full
   contrast. Nothing is lost: an elapsed column is still visibly paler, and it
   is the column that reads as past rather than the digits. */
.dr__cell.is-elapsed .dr__barfill { opacity: 0.34; }
.dr__cell.is-elapsed .dr__arrow path { opacity: 0.5; }

/* Locked columns render EMPTY: no bar, no number. The unlock copy sits below. */
.dr__cell.is-locked { opacity: 1; }

/* ── the axis header ─────────────────────────────────────────────────────── */

.dr__row--axis { position: sticky; top: 0; z-index: 3; background: var(--surface); }
.dr__rail--axis { background: var(--surface); }
/* ⚠ ONE BASELINE FOR EVERY HOUR, WITH THE DAY NAME ABOVE IT.
   The cell was a plain flex column, so a first column that HAS a day name got
   two children and every other column got one — the day name pushed its own
   hour down and that hour sat below the rest of the row. Eight columns level
   and one dropped, once per day.

   The fix is to reserve the day name's line in EVERY cell rather than only
   where one is drawn: a fixed-height row on top, whether or not it is filled,
   and the hour underneath. The day name is then positioned in a lane of its
   own and the hours share a baseline by construction — there is nothing above
   an hour that varies. */
.dr__cell--axis {
  flex-direction: column;
  gap: 0;
  padding: 4px 0 5px;
  font-size: 10px;
  color: var(--text-muted);
  justify-content: flex-end;
}
.dr__cell--axis.is-daystart { border-left: 1px solid var(--line-strong); }

/* The reserved lane. Present in every axis cell; empty in most of them. */
.dr__cell--axis::before {
  content: '';
  display: block;
  height: 13px;
  flex: 0 0 13px;
}
/* ⚠ ABSOLUTE, AND THAT IS THE WHOLE ALIGNMENT CONTRACT. Out of flow means a
   longer label cannot widen its column or push the hour beneath it — the
   reserved ::before lane above keeps every hour on one baseline whether a day
   name is drawn or not. Adding the date to this label therefore costs the
   contract nothing, which is why it could be done at all.

   The width is its DAY's span, not its column's: a date does not fit 46 px,
   and left-aligning it across the day is what the app does
   (daily_report_sheet.dart draws one date cell per day, spanning that day's
   columns). --dayspan is set per label in js, because the first and last days
   of a horizon are partial. */
.dr__day {
  position: absolute;
  top: 4px; left: 3px;
  width: calc(var(--dayspan, 1) * var(--slot) - 6px);
  text-align: left;
  font-size: 10px; font-weight: 700; color: var(--text); letter-spacing: 0.02em;
  white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;   /* never spill into the next day */
  pointer-events: none;
}
.dr__cell--axis { position: relative; }
.dr__hour { font-size: 10px; line-height: 13px; }

/* ── bar columns ─────────────────────────────────────────────────────────── */

.dr__col { display: flex; flex-direction: column; align-items: center; gap: 1px; width: 100%; }
.dr__bar { display: block; }
.dr__barfill { fill: var(--accent); opacity: 0.78; }
.dr__val { font-size: 11px; font-weight: 600; font-variant-numeric: tabular-nums; }
.dr__gust { font-size: 10px; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.dr__arrow { display: block; }
.dr__arrow path { stroke: var(--text-muted); stroke-width: 1.6; fill: none; stroke-linecap: round; }
.dr__dir { font-size: 9px; color: var(--text-muted); letter-spacing: 0.02em; }

.dr__sky { display: block; color: var(--text); }

/* ── tide ────────────────────────────────────────────────────────────────── */

.dr__tideline { stroke: var(--accent); stroke-width: 2; fill: none; }
.dr__tidefill { fill: var(--accent); fill-opacity: 0.16; }
.dr__tidedot { fill: var(--accent); }
.dr__tidelbl { font-size: 8px; fill: var(--text-muted); }

/* ── sun & moon ──────────────────────────────────────────────────────────── */

.dr__sun {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(88px, 1fr));
  gap: 8px 12px;
  padding: 10px 12px;
  border: 1px solid var(--line-strong); border-radius: 3px;
}
.dr__sun dt {
  font-size: 9.5px; font-weight: 700; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--text-muted);
}
.dr__sun dd { margin: 1px 0 0; font-size: 13px; font-variant-numeric: tabular-nums; }

.dr__locked { margin: 0; font-size: 12px; color: var(--text-muted); }
.dr__locked a { color: var(--accent-text); }
/* ⚠ NOT SELECTABLE. Tapping a coordinate on a touch screen was highlighting it
   blue — the browser treating a label as text to copy, on a surface where every
   other tap pans a map. The same applies to the sheet's own coordinate title. */
.dr__foot, .drsheet__where {
  -webkit-user-select: none; user-select: none;
}
.dr__foot {
  margin: 0; font-size: 11px; color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* ── the sheet shell on the map ──────────────────────────────────────────── */

.drsheet {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
  max-height: 82vh;
  display: flex; flex-direction: column;
  background: var(--surface);
  border-top: 1px solid var(--line-strong);
  box-shadow: 0 -8px 28px -14px rgba(0, 0, 0, 0.45);
}
.drsheet[hidden] { display: none; }
.drsheet__bar {
  display: flex; align-items: center; justify-content: space-between;
  padding: 8px 14px; border-bottom: 1px solid var(--line);
}
.drsheet__grip {
  width: 34px; height: 4px; border-radius: 2px; background: var(--line-strong);
  margin: 6px auto 0;
}
.drsheet__close {
  appearance: none; border: 0; background: none; cursor: pointer;
  color: var(--text-muted); font-size: 13px; padding: 4px 6px; border-radius: 3px;
}
.drsheet__close:hover { color: var(--text); }
.drsheet__close:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.drsheet__body { overflow-y: auto; padding: 12px 14px 20px; }

@media (min-width: 900px) {
  .drsheet { left: auto; right: 12px; bottom: 12px; width: min(760px, 62vw);
    max-height: 78vh; border: 1px solid var(--line-strong); border-radius: 4px; }
}

@media (prefers-reduced-motion: reduce) {
  .dr__scroll { scroll-behavior: auto; }
}

/* ⚠ THE SEARCH PIN OUTLIVED THE SEARCH BOX. The inline map control is gone —
   it covered the mode row — and the NAV's search now drives the map. The pin it
   drops is still map chrome and still needs a style. */
.mapsearch__pin {
  width: 14px; height: 14px; border-radius: 50% 50% 50% 0;
  transform: rotate(-45deg);
  background: var(--accent);
  border: 2px solid var(--surface);
  box-shadow: 0 1px 5px -1px rgba(0, 0, 0, 0.6);
}

/* The tide row's stated absence — see the three causes in js/daily-report.js.
   Sits in the row's own track so it lines up with the rail label rather than
   floating in the grid. */
.dr__tidenote {
  margin: 0; padding: 8px 10px;
  font-size: 12px; color: var(--text-muted);
  font-style: italic;
}
