/* css/shell.css
 *
 * The window frame, the three-pane grid, the sidebar, the toolbar and the
 * sign-in card -- the chrome that stays put while views change inside it.
 */

.wrap { max-width: 640px; margin: 0 auto; padding: 0 18px 120px; }

/* While a stored refresh token is being exchanged (see the pre-paint script in
   index.html), the sign-in card would be a lie — the person is almost
   certainly still signed in. `visibility` rather than `display`: the wrap
   keeps its size, so when the exchange fails and the card does appear,
   nothing on the page moves. */
html.resuming #authWrap { visibility: hidden; }

/* ======================================================================
 * Window frame + three-pane shell
 *
 * `.win` is the app's own chrome: a grid of sidebar (218px) · main column ·
 * digest rail (292px). The whole thing fills the viewport and scrolls INSIDE
 * its panes, so the page itself never scrolls and the toolbar stays put while
 * a month grid moves under it.
 *
 * There is no titlebar. A fake macOS strip with three dead dots cost 36px of
 * every screen to imitate a window the browser already draws, and the date it
 * carried belongs in the day header where it can be read at a glance.
 * ==================================================================== */
/* The window is a WINDOW, not the viewport. On a wide display a full-bleed
 * app stretches the day column to a line length nobody reads comfortably and
 * loses the framed look the design is built around, so the frame is capped
 * and centred with the page showing around it. Below the cap it simply fills
 * what it has, and under 900px the frame dissolves into the screen. */
/* `align-items: start` is what stops the frame stretching: as a flex child of
   a full-height box the window would otherwise be stretched to the viewport,
   leaving a screen of empty canvas under three tasks. It now takes the height
   of what is in it, up to the viewport, and the page shows above and below. */
#appView {
  padding: 22px; height: 100%;
  display: flex; justify-content: center; align-items: start;
}
@media (max-width: 900px) { #appView { padding: 0; } }

.win {
  border-radius: 18px; overflow: hidden; background: var(--canvas);
  box-shadow: var(--shadow-lg);
  /* Fills the viewport. This used to hug its content — the argument being
     that a three-task day should not be as tall as a hundred-task one — but
     that was drawn at 1180px wide. Widened, a half-height panel stranded in
     grey stops reading as "compact" and starts reading as a layout that
     failed: the sidebar ends mid-air and the day's own scroller never
     reaches the bottom of the screen. An app window holds its frame and lets
     the panes inside it be as empty as they happen to be. */
  height: 100%; display: flex; flex-direction: column;
  /* 1180 was drawn for a laptop and never revisited. On a 1500px+ display it
     left the app as a small panel marooned in grey, which is the single
     loudest "unfinished web page" signal here — more than any control. 1560
     fills a 16" comfortably while still stopping the middle column from
     stretching into an unreadable line length on an ultrawide. */
  width: 100%; max-width: 1560px;
}
@media (max-width: 900px) { .win { border-radius: 0; box-shadow: none; } }

/* Fixed rails, fluid middle. `minmax(0, 1fr)` on the middle track is what
   stops a long task title from widening the grid past the window — without
   it a grid item's min-content width wins and the whole page scrolls. */
.app {
  flex: 1 1 auto; min-height: 0;
  display: grid; grid-template-columns: 218px minmax(0, 1fr) 292px;
  background: var(--canvas);
}
/* Only the day has a digest to show. Every other view drops the rail's track
   entirely rather than leaving a 292px hole. */
body:not(.view-day) .app { grid-template-columns: 218px minmax(0, 1fr); }
/* The narrow-screen collapse lives at the END of this stylesheet, AFTER
   `.side` and `.digest` are defined. Placed here it lost every tie: a media
   query carries no extra specificity, so a later `.side { display: flex }`
   simply overwrote the `display: none` inside it and the sidebar stayed on
   screen at 820px, squashed to a 24px strip. */

/* ---- sidebar ----------------------------------------------------------- */
.side {
  background: var(--chrome); border-right: .5px solid var(--sep);
  padding: 12px 0; display: flex; flex-direction: column; gap: 16px;
  overflow-y: auto; min-height: 0;
}
.brandrow { display: flex; align-items: center; gap: 8px; padding: 0 14px 2px; }
/* The app icon, same file the sign-in header and the favicon use, so the
   sidebar carries the mark the user tapped on their phone. Square in the file;
   the squircle is rounded here. */
.glyph-brand {
  width: 22px; height: 22px; border-radius: 5px; flex: none;
  object-fit: cover; display: block;
}
.brandrow b { font-size: 1.0em; font-weight: 590; letter-spacing: -.01em; }

.grp { display: flex; flex-direction: column; gap: 1px; }
/* The seam between the calendar rows and Notes / "last time". Inset to the
   rows' own edges, with margin doing the breathing room. */
.navsep { height: .5px; background: var(--sep); margin: 6px 14px; flex: none; }
/* NOT `.row` — that class already exists (auth form rows) with different
   semantics, and NOT `.navrow` colliding with anything: grepped, unique. */
.navrow {
  display: flex; align-items: center; gap: 8px; width: calc(100% - 14px);
  margin: 0 7px; padding: 5px 7px; border-radius: 6px;
  font: inherit; font-size: 0.9286em; color: var(--text); text-align: left;
  background: transparent; border: 0; box-shadow: none; cursor: pointer;
}
.navrow > svg { width: 15px; height: 15px; flex: none; color: var(--text-2); }
.navrow .nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.navrow .n {
  margin-left: auto; font-size: 0.8214em; color: var(--text-3);
  font-variant-numeric: tabular-nums; flex: none;
}
.navrow:hover { background: var(--chrome-2); filter: none; }
.navrow[aria-current="true"] { background: var(--accent); color: var(--accent-ink); }
.navrow[aria-current="true"] > svg,
.navrow[aria-current="true"] .n { color: var(--accent-ink); opacity: .85; }
.navrow:focus-visible { outline: 2px solid var(--accent); outline-offset: -1px; }

/* Identity, freshness and the appearance switch sit at the foot of the rail,
   the way a desktop app puts its account row there. `margin-top: auto` keeps
   them pinned to the bottom however short the nav above is. */
.side-foot {
  margin-top: auto; padding: 12px 14px 0; border-top: .5px solid var(--sep);
  display: flex; flex-direction: column; gap: 6px; align-items: flex-start;
}
.side-foot .themepick { margin-top: 2px; }
.side-acts { display: flex; gap: 4px; flex-wrap: wrap; margin-top: 2px; }
.side-acts button { font-size: 0.8571em; padding: 4px 9px; }

/* ---- main column ------------------------------------------------------- */
.main { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
/* The only scroll container for the working surface. Bounded so the toolbar
   above and the add bar below stay fixed while it moves. */
/* Capped, not full-bleed. A task list stretched across a wide monitor puts the
   checkbox and the delete button a screen apart and leaves the text floating
   in the middle of nothing; the cap keeps a readable measure and the column
   centres in whatever space is left. The BACKGROUND still fills the pane —
   only the content is bounded. */
.mainscroll {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 16px 20px 20px;
}
/* DAY ONLY. A month grid is seven columns that must divide the full pane —
   capping it at a reading measure squeezes the cells and clips the week. The
   cap belongs to prose-shaped content, which is the day and the list. */
body.view-day .mainscroll > *,
body.view-list .mainscroll > * { max-width: 720px; margin-inline: auto; }
/* Switching view or stepping a day replaces this subtree wholesale, which
   landed as a hard cut — the app looked like it was reloading rather than
   moving. A short fade-and-rise gives the change a direction.

   Gated on `.stepped`, which `render` sets ONLY when the view or the anchor
   date actually changed. Every render rebuilds this subtree, including the
   8-second poll, so an ungated enter-animation would flash the whole page
   twice a minute while the user was reading it — considerably worse than the
   hard cut it was meant to smooth. */
.mainscroll.stepped > * { animation: content-in .26s cubic-bezier(.32,.72,0,1) both; }
@keyframes content-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

/* Toolbar. `flex-wrap` plus a scrollable overflow are what keep it from
   forcing the window wider than the viewport on a laptop. */
.appbar {
  display: flex; align-items: center; gap: 10px; min-height: 52px;
  padding: 0 16px; border-bottom: .5px solid var(--sep); flex: 0 0 auto;
  flex-wrap: wrap; row-gap: 6px;
}
.appbar h3.periodtitle {
  margin: 0; font-size: 1.2143em; font-weight: 620; letter-spacing: -.014em;
  font-variant-numeric: tabular-nums; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; min-width: 0;
}
.appbar .count {
  font-size: 0.8571em; color: var(--text-3); font-variant-numeric: tabular-nums;
  white-space: nowrap; flex: none;
}
.appbar .segmented { margin-left: auto; }
/* The view picker is the sidebar's nav a second time, in the same eyeline, so
   while the sidebar is on screen it is not drawn at all. It comes back below
   1100px, where `.side` is `display: none` and this becomes the only way to
   change view — see css/narrow.css. `#densebar` (month density) is NOT a
   duplicate of anything and stays. */
#viewbar { display: none; }
/* Two segmented controls only ever show together in the month view; the
   second must not claim the auto margin as well or they separate. */
.appbar .segmented + .segmented { margin-left: 0; }
/* `margin-left: auto` of its own, on top of the one the segmented group
   carries: the free space splits between the two gaps, so search hugs the
   right edge — and stays there in the views (notes, "last time") where every
   segmented control is hidden and nothing else would push it right. */
.appbar .searchbox { flex: 0 1 168px; margin-left: auto; }

/* ---- header ------------------------------------------------------------ */
header.top {
  display: flex; align-items: center; gap: 10px;
  padding: 18px 2px 18px;
}
/* The iOS app icon, square in the file — the squircle is rounded here so the
   web wears the same mark as the phone. `object-fit: cover` keeps it honest
   if a differently-proportioned export ever replaces it. */
.logo {
  width: 40px; height: 40px; border-radius: 9px; flex: 0 0 auto;
  object-fit: cover; display: block;
}
/* The name and the tagline are one block: `line-height: 1.25` on both and no
   margin between them, so the pair centres against the 40px icon instead of
   the h1 alone sitting high. */
header.top .title { min-width: 0; }
header.top .title h1 {
  font-size: 1.2143em; margin: 0; font-weight: 620; letter-spacing: -.015em;
  line-height: 1.25;
}
header.top .title .sub {
  font-size: 0.8571em; color: var(--text-3); line-height: 1.3; margin-top: 1px;
}
header.top .env { margin-left: auto; text-align: right; font-size: 0.8214em; }
.conn {
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--text-2); font-weight: 500;
}
.dot { width: 7px; height: 7px; border-radius: 50%; background: var(--text-3); }
.dot.on  { background: var(--ok); }
.dot.off { background: var(--danger); }
.whoami {
  color: var(--text-3); margin-top: 1px; font-size: 0.8214em;
  max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ---- theme switcher ---------------------------------------------------- */
.themepick {
  display: inline-flex; padding: 2px; gap: 2px;
  background: var(--fill); border-radius: 7px;
}
.themepick button {
  font: inherit; font-size: 0.8571em; font-weight: 500;
  padding: 3px 10px; border-radius: 5px; border: 0; cursor: pointer;
  background: transparent; color: var(--text-2); box-shadow: none;
}
.themepick button[aria-pressed="true"] {
  background: var(--canvas); color: var(--text); box-shadow: var(--shadow);
}
.themepick button:hover:not([aria-pressed="true"]) { color: var(--text); filter: none; }

/* ---- cards / auth ------------------------------------------------------ */
.card {
  background: var(--canvas); border-radius: var(--radius);
  padding: 22px; box-shadow: var(--shadow-lg);
}
.card + .card { margin-top: 14px; }
.card h2 { margin: 0 0 4px; font-size: 1.2143em; font-weight: 620; letter-spacing: -.014em; }
.card p.lead { margin: 0 0 16px; color: var(--text-2); font-size: 0.9643em; }

label {
  display: block; font-size: 0.7857em; color: var(--text-3);
  margin: 0 0 6px 2px; font-weight: 590; letter-spacing: .04em;
  text-transform: uppercase;
}
/* Date and time are in the list deliberately: left to the UA they keep the
   platform's own bevel and fill, which sat as two foreign boxes beside the
   app-styled text fields in the task editor — and the shared focus rule below
   was recolouring a border this sheet had never drawn. The tokens carry both
   themes. */
input[type=email], input[type=text], input[type=date], input[type=time] {
  width: 100%; font: inherit; font-size: 1.1429em; padding: 10px 12px;
  border-radius: var(--radius-sm);
  border: .5px solid var(--sep); background: var(--canvas); color: var(--text);
  transition: border-color .12s, box-shadow .12s;
}
input::placeholder { color: var(--text-3); }
input:focus-visible {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}
#code {
  letter-spacing: .3em; font-family: var(--mono); text-align: center;
  font-size: 1.5em; font-variant-numeric: tabular-nums;
}

button {
  font: inherit; font-weight: 500; font-size: 1.0em; padding: 9px 16px;
  border-radius: var(--radius-sm); border: 0; cursor: pointer;
  background: var(--accent); color: var(--accent-ink);
  transition: filter .12s; -webkit-tap-highlight-color: transparent;
}
button:hover:not(:disabled) { filter: brightness(1.07); }
button:active:not(:disabled) { filter: brightness(.94); }
button:disabled { opacity: .4; cursor: default; }
button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
button.ghost {
  background: var(--canvas); color: var(--text);
  box-shadow: 0 0 0 .5px var(--sep);
}
button.ghost:hover:not(:disabled) { background: var(--fill); filter: none; }
button.link {
  background: none; color: var(--accent); padding: 7px 6px;
  font-weight: 400; font-size: 0.9643em; box-shadow: none;
}
button.link:hover:not(:disabled) { filter: brightness(1.15); }

.row { display: flex; gap: 10px; align-items: center; }
.row.mt { margin-top: 16px; }
.grow { flex: 1 1 auto; min-width: 0; }

.msg { font-size: 0.9286em; margin-top: 12px; min-height: 18px; color: var(--text-2); }
.msg.err { color: var(--danger); }
.msg.ok  { color: var(--ok); }
.mono { font-family: var(--mono); }

/* Sync freshness, in the sidebar foot. */
.synced { font-size: 0.7857em; color: var(--text-3); font-variant-numeric: tabular-nums; }

/* ---- scope picker ------------------------------------------------------ */
.scopebar {
  display: flex; gap: 2px; padding: 2px; margin: 0 0 14px;
  background: var(--fill); border-radius: 8px;
}
.scopebar button {
  flex: 1; font: inherit; font-size: 0.9286em; font-weight: 500;
  padding: 5px 10px; border-radius: 6px; border: 0; cursor: pointer;
  background: transparent; color: var(--text-2); box-shadow: none;
}
.scopebar button[aria-pressed="true"] {
  background: var(--canvas); color: var(--text); box-shadow: var(--shadow);
}
.scopebar button:hover:not([aria-pressed="true"]) { color: var(--text); filter: none; }

/* ---- mini calendar ------------------------------------------------------ */
/* The sidebar's month map. Quiet by design: the sidebar is chrome, and a loud
   grid here would compete with the calendar it navigates. Today wears a ring,
   the anchored day the accent fill, a task-bearing day a 3px dot. */
.minical { margin: 2px 4px 14px; user-select: none; }
.minical .mc-head { display: flex; align-items: center; margin: 0 2px 4px; }
.minical .mc-title {
  border: 0; background: none; box-shadow: none; padding: 3px 6px; margin-left: -6px;
  font: inherit; font-size: 0.82em; font-weight: 640; color: var(--text);
  border-radius: 7px; cursor: pointer;
}
.minical .mc-title:hover { background: var(--fill); filter: none; }
.minical .mc-steps { margin-left: auto; display: flex; gap: 2px; }
.minical .mc-steps button {
  width: 22px; height: 22px; display: grid; place-items: center;
  border: 0; border-radius: 6px; background: none; box-shadow: none;
  color: var(--text-3); cursor: pointer; padding: 0;
}
.minical .mc-steps button:hover { background: var(--fill); color: var(--text); filter: none; }
.minical .mc-steps svg { width: 12px; height: 12px; }
.minical .mc-wd {
  display: grid; grid-template-columns: repeat(7, 1fr); margin-bottom: 2px;
}
.minical .mc-wd span {
  text-align: center; font-size: 0.62em; font-weight: 600;
  color: var(--text-3); text-transform: uppercase; letter-spacing: .02em;
}
.minical .mc-grid { display: grid; grid-template-columns: repeat(7, 1fr); }
.minical .mc-d {
  position: relative; aspect-ratio: 1; min-width: 0; padding: 0;
  border: 0; background: none; box-shadow: none; cursor: pointer;
  display: grid; place-items: center; border-radius: 7px;
  font: inherit; font-size: 0.72em; color: var(--text);
  font-variant-numeric: tabular-nums;
}
.minical .mc-d:hover { background: var(--fill); filter: none; }
.minical .mc-d.out { color: var(--text-3); opacity: .55; }
.minical .mc-d.today { box-shadow: inset 0 0 0 1.5px var(--accent); }
.minical .mc-d.sel { background: var(--accent); color: var(--accent-ink); font-weight: 650; }
.minical .mc-d.sel.today { box-shadow: none; }
.minical .mc-d.range { background: color-mix(in srgb, var(--accent) 10%, transparent); border-radius: 0; }
.minical .mc-d.range:first-of-type, .minical .mc-d.sel { border-radius: 7px; }
/* One dot per pending task (max 3), each wearing its task's highlight colour;
   an uncoloured task keeps the neutral grey. On the selected (accent-filled)
   day the dots all go accent-ink — coloured specks on the accent fill read as
   dirt, not information. */
.minical .mc-d .mc-dots {
  position: absolute; left: 50%; bottom: 2px; transform: translateX(-50%);
  display: flex; gap: 2px; pointer-events: none;
}
.minical .mc-d .mc-dots i {
  width: 3px; height: 3px; border-radius: 50%; background: var(--text-3);
}
.minical .mc-d.sel .mc-dots i { background: var(--accent-ink) !important; }

/* ---- keyboard shortcut help (?) ----------------------------------------- */
.kbdhelp {
  position: fixed; inset: 0; z-index: 90; display: grid; place-items: center;
  background: color-mix(in srgb, var(--text) 22%, transparent);
}
.kbdhelp .kh-pane {
  width: min(340px, 92vw); background: var(--chrome); border-radius: 16px;
  padding: 18px 20px 16px;
  box-shadow: 0 0 0 .5px color-mix(in srgb, var(--sep) 70%, transparent),
              0 24px 60px -12px rgba(0,20,40,.34);
}
.kbdhelp h3 { margin: 0 0 12px; font-size: 1.02em; font-weight: 650; letter-spacing: -.01em; }
.kbdhelp .kh-row {
  display: flex; align-items: baseline; gap: 14px; padding: 6px 0;
  font-size: 0.9em; color: var(--text-2);
}
.kbdhelp .kh-row + .kh-row { border-top: .5px solid var(--sep-soft); }
.kbdhelp .kh-keys {
  flex: 0 0 108px; font-weight: 620; color: var(--text);
  font-variant-numeric: tabular-nums;
}
