/*
 * style.css — high-contrast, phone first, light and dark (handoff.md §2, §6).
 *
 * Blocks are the whole visual language: no tables, no axes. The day strip is an
 * absolutely-positioned layer where 1 minute = --ppm pixels, so every distance
 * on screen is a real duration, and every empty pixel is time nobody logged.
 *
 * Layout: on a phone the five state buttons are a fixed bar at the bottom,
 * inside thumb reach, and the strip owns the scroll. From 880px the strip moves
 * into its own scroll pane on the left and quick add + totals sit beside it.
 *
 * --- theming ---------------------------------------------------------------
 *
 * Two palettes, both fully worked through — not one palette and a filter. Light
 * is the base declaration in :root and dark overrides it under
 * `[data-theme='dark']`. There is deliberately no `prefers-color-scheme` query
 * here: theme-init.js resolves the three-state preference (system / light /
 * dark) into that one attribute synchronously in <head>, so the stylesheet
 * needs two palettes instead of three copies, and there is no flash of the
 * wrong theme. `data-theme-pref` carries the raw preference and is only used to
 * show which face of the toggle is active.
 *
 * Every colour is a token, and the tokens carry their *job*, not their shade,
 * because the same job needs opposite shades in the two themes:
 *
 *   --c        the state's accent: bars, the block's left rail, key swatches
 *   --c-ink    text in that state's colour — light-on-dark or dark-on-light,
 *              always >= 4.5:1 on both the page and that state's fill
 *   --c-fill   the tinted surface a block or state button is made of
 *   --c-edge   its 1px border, >= 3:1 against the page so the block's boundary
 *              is what identifies it (WCAG 1.4.11) and the fill is free to stay
 *              a tint rather than a slab
 *
 * Same split for the three status colours (--ok / --bad / --warn and their
 * -ink variants). Values were solved against the WCAG contrast formula rather
 * than eyeballed, and measured back out of the rendered page: every piece of
 * text clears 4.5:1 on whatever it actually sits on (the tightest is 4.7:1),
 * control borders clear 3:1, and the dark fills sit at 2.15:1 against the page
 * — as strong as they can get before the secondary text on top of them drops
 * under 4.5:1. In light the fill is a pale tint by design and the 3:1 --c-edge
 * is what makes the block a block.
 */

:root {
  color-scheme: light;

  --bg: #f6f7f9;
  --panel: #ffffff;
  --raise: #dde2ea;
  --fg: #14161b;
  --muted: #555d6b;
  --faint: #666e7c;
  --line: #868e9c;
  --grid: #c9d1dc;
  --input-bg: #ffffff;
  --backdrop: rgba(20, 22, 27, 0.45);
  /* Text on a solid --ok / --bad / --warn block, which flips with the theme. */
  --on-solid: #ffffff;

  --ok: #0a7c45;
  --ok-ink: #0a6d3d;
  --bad: #c92121;
  --bad-ink: #b41c1c;
  --warn: #a35c00;
  --warn-ink: #844e00;

  /* One accent per state (§6), each with the ink / fill / edge it needs. */
  --focus: #07875c;
  --focus-ink: #046143;
  --focus-fill: #bde7d7;
  --focus-edge: #3ca07f;
  --drift: #d81a43;
  --drift-ink: #ab1135;
  --drift-fill: #fccdd8;
  --drift-edge: #e26783;
  --break: #2563eb;
  --break-ink: #1c4ed8;
  --break-fill: #c9dcfc;
  --break-edge: #608cef;
  --eating: #a96500;
  --eating-ink: #844e00;
  --eating-fill: #f4dab0;
  --eating-edge: #ba8537;
  --morning: #7433e0;
  --morning-ink: #5c25b8;
  --morning-fill: #ded1fb;
  --morning-edge: #a37ae9;

  /* Fixed vertical scale for the day strip: pixels per minute. */
  --ppm: 1.5;
  /* The week's scale. The log view's 1.5 would make a 22-hour window over three
     thousand pixels tall, which is a scroll, not the "whole week as one
     picture" §6 asks for. Read by week.js; the breakpoint lives here so the
     layout decision stays with the rest of the layout. */
  --week-ppm: 0.5;
  /* Gutter the hour labels live in, shared by the head / grid / ratio rows so
     all three line up on the same seven columns. */
  --gutter: 2.25rem;
}

/*
 * Dark. Not the old palette: every value here was raised until it measured.
 * The page itself is lighter (#15171c, was #0e0f12), the two secondary greys
 * went from 5.9:1 / 3.0:1 to 10.7:1 / 7.7:1, borders from 1.25:1 to 3.4:1, and
 * the state fills from a 1.4:1 whisper to 2.15:1 — the fills were the real
 * complaint, since a block you cannot see is a block you cannot read.
 */
:root[data-theme='dark'] {
  color-scheme: dark;

  --bg: #15171c;
  --panel: #1e2128;
  --raise: #2b2f39;
  --fg: #f2f4f8;
  --muted: #c2c8d4;
  --faint: #a3aab8;
  --line: #666d7c;
  --grid: #3a3f4c;
  --input-bg: #1a1d24;
  --backdrop: rgba(0, 0, 0, 0.66);
  --on-solid: #14161b;

  --ok: #4ade80;
  --ok-ink: #6de79b;
  --bad: #f87171;
  --bad-ink: #ff9d9d;
  --warn: #fbbf24;
  --warn-ink: #fbc84a;

  --focus: #34d399;
  --focus-ink: #84eec5;
  --focus-fill: #205847;
  --focus-edge: #257a5e;
  --drift: #fb7185;
  --drift-ink: #ffadb9;
  --drift-fill: #763d48;
  --drift-edge: #a8515f;
  --break: #60a5fa;
  --break-ink: #a3cdff;
  --break-fill: #335075;
  --break-edge: #436ea3;
  --eating: #fbbf24;
  --eating-ink: #f8d477;
  --eating-fill: #5d4c1f;
  --eating-edge: #856820;
  --morning: #a78bfa;
  --morning-ink: #cbbaff;
  --morning-fill: #524778;
  --morning-edge: #7261a9;
}

@media (min-width: 700px) {
  :root {
    --week-ppm: 0.75;
    --gutter: 3.25rem;
  }
}

.state-focus {
  --c: var(--focus);
  --c-ink: var(--focus-ink);
  --c-fill: var(--focus-fill);
  --c-edge: var(--focus-edge);
}
.state-drift {
  --c: var(--drift);
  --c-ink: var(--drift-ink);
  --c-fill: var(--drift-fill);
  --c-edge: var(--drift-edge);
}
.state-break {
  --c: var(--break);
  --c-ink: var(--break-ink);
  --c-fill: var(--break-fill);
  --c-edge: var(--break-edge);
}
.state-eating {
  --c: var(--eating);
  --c-ink: var(--eating-ink);
  --c-fill: var(--eating-fill);
  --c-edge: var(--eating-edge);
}
.state-morning {
  --c: var(--morning);
  --c-ink: var(--morning-ink);
  --c-fill: var(--morning-fill);
  --c-edge: var(--morning-edge);
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--fg);
}

body {
  font: 16px/1.5 -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
  min-height: 100dvh;
}

body.sheet-open {
  overflow: hidden;
}

h1 {
  font-size: 1.125rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin: 0;
}

.muted {
  color: var(--muted);
}

/* --- buttons ------------------------------------------------------------- */

button {
  font: inherit;
  font-weight: 600;
  color: var(--bg);
  background: var(--fg);
  border: 0;
  border-radius: 8px;
  padding: 0.625rem 1rem;
  min-height: 2.75rem;
  cursor: pointer;
}

button:disabled {
  opacity: 0.4;
  cursor: default;
}

button.small {
  min-height: 2rem;
  padding: 0.25rem 0.625rem;
  font-size: 0.8125rem;
  font-weight: 500;
}

button.ghost {
  background: transparent;
  color: var(--fg);
  border: 1px solid var(--line);
}

button.ghost:hover:not(:disabled) {
  border-color: var(--muted);
}

button.danger {
  background: transparent;
  color: var(--bad-ink);
  border: 1px solid var(--bad);
}

button.danger.armed {
  background: var(--bad);
  color: var(--on-solid);
}

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

/* --- top bar ------------------------------------------------------------- */

.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.5rem 0.75rem;
  padding-top: calc(0.5rem + env(safe-area-inset-top));
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
}

.daynav {
  display: flex;
  align-items: center;
  gap: 0.25rem;
}

.daytitle {
  min-width: 8.5rem;
  text-align: center;
}

.daymeta {
  margin: 0;
  font-size: 0.75rem;
  color: var(--muted);
  display: flex;
  gap: 0.375rem;
  justify-content: center;
}

#day-today {
  color: var(--ok-ink);
}

#day-today[hidden] {
  display: none;
}

button.nav {
  background: transparent;
  color: var(--fg);
  border: 1px solid var(--line);
  font-size: 1.25rem;
  line-height: 1;
  padding: 0 0.75rem;
  min-height: 2.5rem;
}

.topmeta {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-left: auto;
}

/* §4.3: the current zone is always visible, so it is never a guess which zone
   a new block lands in. */
.zonechip {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  font-size: 0.75rem;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.25rem 0.625rem;
  white-space: nowrap;
}

.zonechip.fixed {
  color: var(--warn-ink);
  border-color: var(--warn);
}

/* --- theme toggle -------------------------------------------------------- */
/*
 * Three states, one button, cycling system → light → dark. Which face shows is
 * decided here rather than in JS: theme-init.js has already written
 * data-theme-pref before the first paint, so the label is right on frame one
 * and never flickers while theme.js loads. The accessible name is the one thing
 * theme.js sets, because it has to name the *next* state as well as this one.
 */

.themebtn {
  display: inline-flex;
  align-items: center;
  gap: 0.3125rem;
  padding-left: 0.5rem;
  padding-right: 0.5rem;
}

.themebtn > span {
  display: none;
  align-items: center;
  gap: 0.3125rem;
}

:root:not([data-theme-pref]) .t-system,
:root[data-theme-pref='system'] .t-system,
:root[data-theme-pref='light'] .t-light,
:root[data-theme-pref='dark'] .t-dark {
  display: inline-flex;
}

/* The glyph carries it on a phone, where the header is already four items
   wide; the word comes back as soon as there is room for it. */
.themeword {
  display: none;
}

@media (min-width: 560px) {
  .themeword {
    display: inline;
  }
}

/* --- banners ------------------------------------------------------------- */

.banner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin: 0.5rem 0.75rem 0;
  padding: 0.625rem 0.75rem;
  border: 1px solid var(--line);
  border-left: 3px solid var(--warn);
  border-radius: 8px;
  background: var(--panel);
  font-size: 0.875rem;
}

.banner[hidden] {
  display: none;
}

.banner.bad {
  border-left-color: var(--bad);
  color: var(--bad-ink);
}

.banner-actions {
  display: flex;
  gap: 0.375rem;
  margin-left: auto;
}

/* --- layout -------------------------------------------------------------- */

main {
  max-width: 62rem;
  margin: 0 auto;
  padding: 0.75rem 0.75rem 6.5rem;
}

.stripsection {
  /* Room for the first hour label, which hangs half a line above its gridline. */
  padding-top: 0.6rem;
  padding-bottom: 1rem;
}

/* --- quick add ----------------------------------------------------------- */
/* Fixed to the bottom of the phone screen: five thumb-sized targets that are
   always one tap away, which is the whole point of quick add (§6). */

.quickadd {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 25;
  padding: 0.5rem 0.75rem;
  padding-bottom: calc(0.5rem + env(safe-area-inset-bottom));
  background: color-mix(in srgb, var(--bg) 94%, transparent);
  backdrop-filter: blur(10px);
  border-top: 1px solid var(--line);
}

.statebar {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 0.375rem;
  max-width: 40rem;
  margin: 0 auto;
}

.statebtn {
  min-height: 3rem;
  padding: 0.375rem 0.25rem;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--c-ink);
  background: var(--c-fill);
  border: 1px solid var(--c-edge);
  border-radius: 10px;
}

/* Hover strengthens the border rather than the fill. Deepening the fill is the
   obvious move and it is the wrong one: it drags the label's contrast down by
   half a point in whichever theme is already tightest, for a state a phone
   never even shows. */
.statebtn:hover {
  border-color: var(--c);
  box-shadow: inset 0 0 0 1px var(--c);
}

/* --- day strip ----------------------------------------------------------- */

.strip {
  position: relative;
  border-radius: 10px;
}

.gridline {
  position: absolute;
  left: 0;
  right: 0;
  height: 0;
  border-top: 1px solid var(--grid);
}

.gridlabel {
  position: absolute;
  top: -0.6em;
  left: 0;
  width: 3rem;
  font-size: 0.6875rem;
  font-variant-numeric: tabular-nums;
  color: var(--faint);
  background: var(--bg);
}

.lanes {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 3.25rem;
  right: 0;
}

.gap {
  position: absolute;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

.gaplabel {
  font-size: 0.6875rem;
  letter-spacing: 0.05em;
  color: var(--faint);
}

.emptyday {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  color: var(--faint);
  font-size: 0.875rem;
  pointer-events: none;
}

.block {
  position: absolute;
  overflow: hidden;
  /* Top-aligned, not centred: the label belongs next to the block's start time,
     which is where the eye reads it against the gridline. */
  display: flex;
  align-items: flex-start;
  text-align: left;
  min-height: 14px;
  padding: 0.25rem 0.5rem;
  font-weight: 500;
  color: var(--fg);
  background: var(--c-fill);
  border: 1px solid var(--c-edge);
  border-left: 4px solid var(--c);
  border-radius: 7px;
}

/* Same reasoning as .statebtn:hover — the boundary brightens, the surface the
   text sits on does not move. */
.block:hover {
  border-color: var(--c);
  box-shadow: inset 0 0 0 1px var(--c);
}

.binner {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.125rem 0.5rem;
  font-size: 0.8125rem;
  line-height: 1.25;
}

.block.tiny {
  padding: 0 0.5rem;
}

.block.tiny .binner {
  flex-wrap: nowrap;
  white-space: nowrap;
  font-size: 0.6875rem;
}

.block.tiny .bnote {
  display: none;
}

.btime {
  font-variant-numeric: tabular-nums;
  color: var(--fg);
}

.bstate {
  color: var(--c-ink);
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  font-weight: 700;
}

.bdur,
.bnote {
  color: var(--muted);
  font-weight: 400;
}

.bnote {
  font-size: 0.75rem;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* §4: on a travel day every block says which zone it was lived in, so the
   timeline cannot read as silently wrong. */
.bzone {
  font-size: 0.6875rem;
  color: var(--fg);
  background: var(--raise);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0 0.375rem;
}

.bwarn {
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--warn-ink);
}

/* §3: overlaps are allowed but flagged — a mislogged evening should be visible
   at a glance and still editable. The border goes fully to the warning colour
   in both themes; the stripe below has to stay legible against every one of the
   five fills, which is why --warn is a solid amber and not a tint of one. */
.block.overlaps {
  border-color: var(--warn);
}

.block.overlaps::after {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 9px;
  background: repeating-linear-gradient(
    135deg,
    var(--warn) 0 3px,
    transparent 3px 8px
  );
  opacity: 0.9;
  pointer-events: none;
}

/* Week columns: the same block, at a width where only a start time fits. State
   is carried by colour alone; the full detail is in the title and one tap away
   in the log view. */
.block.micro {
  /* Every pixel of padding is a character of the duration, at 44px wide. */
  padding: 0 0.125rem;
  border-left-width: 3px;
  border-radius: 5px;
}

.block.micro .binner {
  flex-direction: column;
  flex-wrap: nowrap;
  gap: 0;
  font-size: 0.625rem;
  line-height: 1.15;
  overflow: hidden;
}

.block.micro .btime,
.block.micro .bdur {
  white-space: nowrap;
}

/* The start time is what a column-width block is for; the duration is a bonus
   and gets the leftover pixels. */
.block.micro .bdur {
  font-size: 0.5625rem;
}

.block.micro.overlaps::after {
  width: 5px;
  background: repeating-linear-gradient(135deg, var(--warn) 0 2px, transparent 2px 5px);
}

.gap.micro .gaplabel {
  font-size: 0.625rem;
  letter-spacing: 0;
}

/* --- totals (step 6) ----------------------------------------------------- */

.totals h2 {
  margin: 0 0 0.5rem;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
}

.totalsline {
  margin: 0 0 0.75rem;
  font-size: 0.9375rem;
}

.totalsgrid {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
}

.totalrow {
  display: grid;
  grid-template-columns: 4.75rem minmax(0, 1fr) 4rem;
  align-items: center;
  gap: 0.625rem;
  font-size: 0.875rem;
}

.totalrow.zero {
  opacity: 0.45;
}

.totallabel {
  color: var(--c-ink);
  font-weight: 600;
}

.totalbar {
  height: 10px;
  border-radius: 3px;
  background: var(--raise);
  overflow: hidden;
}

.totalfill {
  display: block;
  height: 100%;
  background: var(--c);
}

.totalvalue {
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--fg);
}

.ratio {
  margin-top: 1.25rem;
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
}

.ratiotitle {
  font-size: 0.75rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
}

.ratiobar {
  display: flex;
  height: 12px;
  border-radius: 3px;
  overflow: hidden;
  background: var(--raise);
}

.ratiofocus {
  background: var(--focus);
}

/* Focus green and drift rose are hue-separated but close in luminance in both
   themes, so where one ends and the other begins is carried by a hairline in
   the page colour rather than by the colour change alone. Invisible when drift
   is zero, since the span has no width. */
.ratiodrift {
  background: var(--drift);
  box-shadow: inset 1px 0 0 var(--bg);
}

.ratiotext {
  font-size: 0.875rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* --- week view (steps 7 + 8) --------------------------------------------- */
/*
 * Seven columns and an hour gutter, on a 390px phone. The hard call was
 * horizontal scrolling versus shrinking to fit: scrolling would let each column
 * hold a readable label, but you would never see the week at once, which is the
 * only reason the screen exists (§6). So all seven fit — ~44px each — and the
 * per-block detail moves to the title and to the log view a tap away.
 *
 * Head, grid and ratio rows share --gutter and the same seven-column template,
 * so a column is one vertical line from its name down to its focus ratio.
 */

.weekview main {
  padding-bottom: 2rem;
}

.daytitle.wide {
  min-width: 11rem;
}

/* A link that has to sit in a row of buttons. */
.btnlink {
  display: inline-flex;
  align-items: center;
  border-radius: 8px;
  border: 1px solid var(--line);
  color: var(--fg);
  text-decoration: none;
  font-weight: 500;
  padding: 0.25rem 0.625rem;
  min-height: 2rem;
  font-size: 0.8125rem;
}

.btnlink:hover {
  border-color: var(--muted);
}

.week {
  margin-bottom: 1.75rem;
}

/* §4 in words: seven columns against one set of hour labels would otherwise
   read as one clock, and on a travel week it is not one clock. */
.zonenote {
  margin: 0 0 0.75rem;
  padding: 0.5rem 0.625rem;
  border: 1px solid var(--line);
  border-left: 3px solid var(--morning);
  border-radius: 8px;
  background: var(--panel);
  font-size: 0.75rem;
  color: var(--muted);
}

.zonenote[hidden] {
  display: none;
}

.weekhead,
.weekcols,
.weekratios {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 3px;
}

.weekhead,
.weekratios {
  padding-left: var(--gutter);
}

.dayhead {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.0625rem;
  padding: 0.25rem 0.125rem;
  border-radius: 7px 7px 0 0;
  border-bottom: 1px solid var(--line);
  color: var(--fg);
  text-decoration: none;
  overflow: hidden;
}

.dayhead:hover {
  background: var(--panel);
}

.dayhead.today {
  background: color-mix(in srgb, var(--ok) 14%, transparent);
  border-bottom-color: var(--ok);
}

.dayname {
  font-size: 0.625rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.daynum {
  font-size: 1rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}

/* nowrap: '10h 45m' breaking over two lines makes one column's header taller
   than the rest, and grid rows align, so every header grows with it. */
.daytotal {
  font-size: 0.5625rem;
  white-space: nowrap;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

@media (min-width: 700px) {
  .daytotal {
    font-size: 0.6875rem;
  }
}

.dayzones {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 1px;
  width: 100%;
  margin-top: 0.125rem;
}

/* One chip per zone lived that day: two chips is what a travel day looks like,
   even when the names themselves are clipped on a phone. */
.zonemark {
  font-size: 0.5625rem;
  line-height: 1.4;
  text-align: center;
  color: var(--fg);
  background: var(--raise);
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 0 0.125rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.weekgrid {
  position: relative;
  padding-left: var(--gutter);
  margin-top: 0.5rem;
}

/* One set of gridlines behind all seven columns — the hour is read once for the
   whole picture, not seven times. */
.weekgrid-lines {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.weekgrid-lines .gridlabel {
  width: var(--gutter);
  font-size: 0.625rem;
}

.weekcols {
  height: 100%;
}

.daycol {
  position: relative;
  height: 100%;
  border-radius: 0 0 7px 7px;
}

.daycol.today {
  background: color-mix(in srgb, var(--ok) 8%, transparent);
}

/* The shared .lanes is inset by the log view's gutter; inside a column the
   column *is* the lane space. */
.daycol .lanes {
  left: 0;
  right: 0;
}

.weekratios {
  margin-top: 0.625rem;
}

.dayratio {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.1875rem;
}

.dayratio.zero {
  opacity: 0.4;
}

.dayratio .ratiobar {
  height: 6px;
}

.ratiopct {
  font-size: 0.6875rem;
  text-align: center;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.ratiokey {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 0.625rem;
  margin: 0.625rem 0 0;
  padding-left: var(--gutter);
  font-size: 0.6875rem;
  color: var(--muted);
}

.key {
  display: inline-flex;
  align-items: center;
  gap: 0.3125rem;
}

.key::before {
  content: '';
  width: 9px;
  height: 9px;
  border-radius: 2px;
  background: var(--c, var(--fg));
}

.key.focus {
  --c: var(--focus);
}

.key.drift {
  --c: var(--drift);
}

/* --- drift by hour (step 8) ---------------------------------------------- */
/*
 * Blocks, not a chart (§6). Height is minutes against the week's worst hour and
 * the fill intensity says the same thing again, so a real-but-short hour still
 * reads in a 28px-wide phone cell. All 24 hours are always drawn: the empty
 * ones are the shape of the day, and without them "40 minutes at 15:00" carries
 * no idea whether that is your worst hour or a normal one.
 */

.driftpanel {
  margin-top: 2rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--line);
}

.driftpanel h2 {
  margin: 0 0 0.5rem;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
}

.driftlede {
  margin: 0 0 0.25rem;
  font-size: 1rem;
}

.driftlede strong {
  color: var(--drift-ink);
  font-variant-numeric: tabular-nums;
}

.driftsecond {
  margin: 0 0 0.875rem;
  font-size: 0.875rem;
  color: var(--muted);
}

.driftsecond strong {
  color: var(--fg);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* 12 across wraps into a morning row and an afternoon row, which is a split the
   day already has. From 700px all 24 fit on one line. */
.driftgrid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 0.5rem 2px;
  margin-top: 0.75rem;
}

@media (min-width: 700px) {
  .driftgrid {
    grid-template-columns: repeat(24, minmax(0, 1fr));
  }
}

.drifthour {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.25rem;
  min-width: 0;
}

.driftwell {
  display: flex;
  align-items: flex-end;
  height: 64px;
  border-bottom: 1px solid var(--line);
}

.driftblock {
  display: block;
  width: 100%;
  border-radius: 3px 3px 0 0;
  background: color-mix(in srgb, var(--drift) calc(30% + var(--f, 0) * 70%), var(--raise));
}

/* A visible floor, not nothing: an hour with no drift is a result, and it has to
   look like one rather than like a cell that failed to render. */
.drifthour.empty .driftblock {
  background: var(--grid);
  border: 1px solid var(--line);
  border-bottom: 0;
  border-radius: 2px 2px 0 0;
}

.drifthour.peak .driftblock {
  outline: 1px solid var(--drift);
  outline-offset: 1px;
}

.drifthourlabel {
  font-size: 0.625rem;
  text-align: center;
  color: var(--faint);
  font-variant-numeric: tabular-nums;
}

.drifthour:not(.empty) .drifthourlabel {
  color: var(--muted);
}

/* 00 / 06 / 12 / 18 as the places the eye lands. */
.drifthour.anchor .drifthourlabel {
  color: var(--fg);
  font-weight: 600;
}

.drifthour.peak .drifthourlabel {
  color: var(--drift-ink);
  font-weight: 700;
}

.driftnote {
  margin: 0.875rem 0 0;
  font-size: 0.75rem;
  color: var(--muted);
}

/* Desktop: the week is wide, so give the columns the room and let the totals
   and drift-by-hour sit side by side underneath rather than one long scroll. */
@media (min-width: 880px) {
  .weekmain {
    display: grid;
    grid-template-columns: 22rem minmax(0, 1fr);
    grid-template-rows: auto auto;
    gap: 0 2rem;
    align-items: start;
    max-width: 76rem;
  }

  .weekmain .week {
    grid-column: 1 / 3;
    grid-row: 1;
  }

  /* Both columns of row 2 carry the same rule, so the divider reads as one line
     across the page rather than half a line over drift-by-hour. */
  .weekmain .totals {
    grid-column: 1;
    grid-row: 2;
    margin-top: 2rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--line);
  }

  .weekmain .driftpanel {
    grid-column: 2;
    grid-row: 2;
  }
}

/* --- sheet (quick add + edit) -------------------------------------------- */

.sheet {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}

.sheet[hidden] {
  display: none;
}

.sheet-backdrop {
  position: absolute;
  inset: 0;
  background: var(--backdrop);
}

.sheet-panel {
  position: relative;
  width: 100%;
  max-width: 30rem;
  max-height: 92dvh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding: 1rem 1rem calc(1rem + env(safe-area-inset-bottom));
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px 14px 0 0;
}

.sheet-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.sheet-head h2 {
  margin: 0;
  font-size: 1rem;
}

.chiprow {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
}

.chip {
  min-height: 2.5rem;
  padding: 0.375rem 0.75rem;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--muted);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 999px;
}

.chip.on {
  color: var(--c-ink);
  background: var(--c-fill);
  border-color: var(--c);
}

/* Stacked by default: a native time input in a 12-hour locale plus two nudge
   buttons does not fit twice across a 390px phone without clipping the picker
   icon. Side by side only once there is room for it. */
.timerow {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.5rem;
}

@media (min-width: 430px) {
  .timerow {
    grid-template-columns: 1fr 1fr;
  }
}

.timefield,
.field {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
}

.timefield > span:first-child,
.field > span:first-child {
  font-size: 0.75rem;
  color: var(--muted);
}

.field em {
  font-style: normal;
  color: var(--faint);
}

.timeinput {
  display: flex;
  align-items: stretch;
  gap: 0.25rem;
}

button.nudge {
  flex: 0 0 auto;
  width: 2.25rem;
  min-height: 2.75rem;
  padding: 0;
  font-size: 1.125rem;
  color: var(--fg);
  background: var(--raise);
  border: 1px solid var(--line);
  border-radius: 8px;
}

input[type='time'],
input[type='text'],
input[type='password'] {
  font: inherit;
  color: var(--fg);
  background: var(--input-bg);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 0.5rem 0.625rem;
  min-height: 2.75rem;
  min-width: 0;
}

input[type='time'] {
  flex: 1 1 auto;
  font-variant-numeric: tabular-nums;
}

input:focus {
  outline: none;
  border-color: var(--muted);
}

.sheet-hint {
  margin: -0.25rem 0 0;
  font-size: 0.75rem;
  color: var(--muted);
}

.sheet-error {
  margin: 0;
  font-size: 0.8125rem;
  color: var(--bad-ink);
}

.sheet-error[hidden] {
  display: none;
}

.sheet-actions {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.25rem;
}

.sheet-actions #sheet-save {
  flex: 1 1 auto;
}

#sheet-delete[hidden],
#sheet-delete-cancel[hidden] {
  display: none;
}

/* --- desktop ------------------------------------------------------------- */

/* Scoped away from the week view, which is a main element too but lays out on
   its own grid — see .weekmain above. */
@media (min-width: 880px) {
  body:not(.weekview) main {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 20rem;
    /* min-content on the first row matters: the strip spans both rows, and with
       two auto rows the grid hands its extra height to each of them equally,
       which pushes the totals half a screen down the sidebar for no reason. */
    grid-template-rows: min-content 1fr;
    gap: 1.5rem;
    align-items: start;
    padding-bottom: 2rem;
  }

  .stripsection {
    grid-column: 1;
    grid-row: 1 / 3;
    max-height: calc(100dvh - 7rem);
    overflow-y: auto;
    padding-right: 0.5rem;
  }

  .quickadd {
    position: static;
    grid-column: 2;
    grid-row: 1;
    padding: 0;
    background: none;
    backdrop-filter: none;
    border: 0;
  }

  .statebar {
    grid-template-columns: 1fr 1fr;
  }

  body:not(.weekview) .totals {
    grid-column: 2;
    grid-row: 2;
  }

  .sheet {
    align-items: center;
  }

  .sheet-panel {
    border-radius: 14px;
  }
}

/* --- login page ---------------------------------------------------------- */

#login {
  margin-top: 1.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  max-width: 20rem;
}

main.narrow {
  max-width: 40rem;
  margin: 0 auto;
  padding: 2rem 1.25rem;
  display: block;
}

main.narrow h1 {
  font-size: 1.5rem;
  margin-bottom: 0.25rem;
}

/* The login page has no topbar, but the theme toggle still has to be reachable
   from it — someone who has set the app to light should not be handed a dark
   password field on the way in. */
.pagehead {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
}

.sub {
  color: var(--muted);
  font-size: 0.875rem;
  margin: 0 0 0.5rem;
}

.status {
  margin-top: 1.5rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--line);
  border-left-width: 3px;
  border-radius: 6px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.8125rem;
  word-break: break-word;
}

.status[hidden] {
  display: none;
}

.status.pending {
  border-left-color: var(--warn);
  color: var(--muted);
}

.status.ok {
  border-left-color: var(--ok);
  color: var(--ok-ink);
}

.status.bad {
  border-left-color: var(--bad);
  color: var(--bad-ink);
}
