/* css/sheets.css
 *
 * Sheets: settings, the task editor, the day sheet. They share the note
 * editor's chrome, which is what makes the app read as one surface.
 */

.setsheet { width: min(420px, 100%) !important; max-height: min(88vh, 720px) !important; }
/* A segmented row fills its row edge to edge; a labelled row splits left and
   right. `justify-content` on the row can only serve one of them, so the
   segment is what stretches and the row keeps the default packing. */
.setsheet .ne-r > .seg { flex: 1 1 auto; min-width: 0; }
/* The body is the one scroller, and a settings list is short rows rather than
   prose — so the blocks sit closer than the note editor's do. */
.setsheet .ne-body { gap: 18px; padding: 16px 16px 20px; }
.setsheet .set-grp { display: flex; flex-direction: column; }
/* Sentence case at reading weight. A 9px uppercase caption over each group is
   the strongest "old form" tell left on this sheet — the note editor's own
   caption is shouted because it labels a field, but a settings sheet is a
   stack of six of them and the shouting compounds. */
.setsheet .ne-cap {
  margin: 0 0 7px 2px; font-size: 0.8em; letter-spacing: 0;
  text-transform: none; font-weight: 590; color: var(--text-2);
}
.setsheet .ne-foot { margin: 8px 2px 0; line-height: 1.4; }
.setsheet .msg:empty { display: none; }
.setsheet .msg { margin-top: 8px; }

/* Two pages in one pane: the settings list, and the typeface picker behind
   the disclosure row. Only one body is in the flow at a time, which also
   takes the hidden page's buttons out of the focus trap's reach — the trap
   queries the DOM live and `display: none` is what makes it agree with what
   is on screen. */
.setsheet .ne-body[data-body="font"],
.setsheet .ne-body[data-body="lang"],
.setsheet[data-page="font"] .ne-body[data-body="main"],
.setsheet[data-page="lang"] .ne-body[data-body="main"] { display: none; }
.setsheet[data-page="font"] .ne-body[data-body="font"],
.setsheet[data-page="lang"] .ne-body[data-body="lang"] {
  display: flex; flex-direction: column;
}
/* Back is a bar button like Done, mirrored: chevron then the parent page's
   name, the shape every settings sheet uses to say where the row came from. */
.setsheet .ne-back {
  grid-column: 1; justify-self: start; display: none;
  align-items: center; gap: 2px;
}
.setsheet[data-page="font"] .ne-back,
.setsheet[data-page="lang"] .ne-back { display: inline-flex; }
.setsheet .ne-back::before {
  content: ""; width: 7px; height: 11px; flex: none;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='11' viewBox='0 0 7 11'%3E%3Cpath d='M5.8 1.2 1.6 5.5 5.8 9.8' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat center/contain;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='11' viewBox='0 0 7 11'%3E%3Cpath d='M5.8 1.2 1.6 5.5 5.8 9.8' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat center/contain;
}

/* A disclosure row: the setting on the left, its current value and a chevron
   on the right. It is the whole reason the sheet is short — the answer most
   visits want is already on the row, and only a change costs a page. */
.setsheet .ne-nav {
  width: 100%; background: none; box-shadow: none; border: 0; cursor: pointer;
  font: inherit; color: var(--text); text-align: left;
  /* The row's own padding-right leaves the gap; the chevron is drawn into it
     rather than added as a box, so the value can ellipsis into the space a
     long font name needs instead of pushing the arrow off the edge. */
  padding-right: 32px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='11' viewBox='0 0 7 11'%3E%3Cpath d='M1.2 1.2 5.4 5.5 1.2 9.8' fill='none' stroke='%238e8e93' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 14px center;
}
.setsheet .ne-nav:hover { filter: none; background-color: var(--fill); }
.setsheet .ne-nav .val {
  margin-left: auto; color: var(--text-3); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.setsheet .ne-nav:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* The account actions are rows in a group like everything else, so the sheet
   has one row shape rather than a list and a pair of loose buttons. Tinted
   ink is what marks them, not a filled button — the friction that matters is
   the typed word below, and a red slab this far up the sheet only shouts. */
.setsheet .ne-danger {
  width: 100%; background: none; box-shadow: none; border: 0; cursor: pointer;
  font: inherit; color: var(--danger); text-align: left;
}
.setsheet .ne-danger:hover {
  filter: none; background: color-mix(in srgb, var(--danger) 8%, transparent);
}
.setsheet .ne-danger:focus-visible { outline: 2px solid var(--danger); outline-offset: -2px; }
/* First and last rows have to clip to the group's corners, which they cannot
   inherit: `.ne-rows` rounds itself but a button paints its own background
   square over it. */
.setsheet .ne-rows > :first-child { border-radius: 14px 14px 0 0; }
.setsheet .ne-rows > :last-child { border-radius: 0 0 14px 14px; }
.setsheet .ne-rows > :only-child { border-radius: 14px; }

/* Segmented control — one track, the selection a raised pill that slides
   over it. The iOS control, and the reason the old three bare text buttons
   read as unstyled: nothing said they were one group or which was live. */
.setsheet .seg {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  gap: 2px; width: 100%; padding: 2px; border-radius: 9px;
  background: var(--fill); box-shadow: none;
}
.setsheet .seg button {
  font: inherit; font-size: 0.88em; font-weight: 500; cursor: pointer;
  padding: 6px 10px; border: 0; border-radius: 7px; background: none;
  color: var(--text); box-shadow: none; white-space: nowrap;
  transition: background .18s ease, box-shadow .18s ease;
}
.setsheet .seg button:hover { filter: none; }
/* The selected pill must read as RAISED off the track. In light that means a
   lighter fill than the groove; naively reusing the same token in dark made
   it DARKER than the track — a hole, not a pill, which is the classic
   inverted-shadow mistake. Dark lifts instead: a lighter surface and a
   hairline, since a drop shadow is nearly invisible on a dark ground. */
.setsheet .seg button[aria-checked="true"] {
  background: var(--canvas); font-weight: 590;
  box-shadow: 0 0 0 .5px rgba(0,0,0,.04), 0 1px 3px rgba(0,0,0,.12);
}
/* Elevation has to INVERT with the theme, not just darken.
   `--chrome` is #f5f5f7 on a #ffffff sheet in light — a panel slightly
   recessed from its surface. In dark it is #000000 on a #1c1c1e sheet, so the
   same panels read as black holes punched through the sheet: the opposite
   arrangement, not a dark version of it. Re-pointing the token inside the
   sheet lifts every interior surface at once (`.ne-rows`, `.ne-group`,
   `.fontgrid button`, `.ne-del`) instead of patching each rule and missing
   the next one someone adds. The task editor's pane, the day sheet's panel
   and the slot composer's popover are the same arrangement — floating
   surfaces whose base IS `--chrome` — so they take the same repoint, or they
   read as black holes over the dark canvas. */
@media (prefers-color-scheme: dark) {
  .pane.noteedit, .taskedit .pane, .daysheet .panel, .slotcompose .pop {
    --chrome: #2c2c2e; --chrome-2: #3a3a3c;
  }
  .setsheet .seg { background: rgba(120,120,128,.20); }
  .setsheet .seg button[aria-checked="true"] {
    background: #636366;
    box-shadow: 0 0 0 .5px rgba(255,255,255,.10), 0 1px 3px rgba(0,0,0,.40);
  }
}
:root[data-force="dark"] .pane.noteedit,
:root[data-force="dark"] .taskedit .pane,
:root[data-force="dark"] .daysheet .panel,
:root[data-force="dark"] .slotcompose .pop { --chrome: #2c2c2e; --chrome-2: #3a3a3c; }
/* An explicit light choice must beat the dark media query above. */
:root[data-force="light"] .pane.noteedit,
:root[data-force="light"] .taskedit .pane,
:root[data-force="light"] .daysheet .panel,
:root[data-force="light"] .slotcompose .pop { --chrome: #f5f5f7; --chrome-2: #ebebed; }
:root[data-force="dark"] .setsheet .seg { background: rgba(120,120,128,.20); }
:root[data-force="dark"] .setsheet .seg button[aria-checked="true"] {
  background: #636366;
  box-shadow: 0 0 0 .5px rgba(255,255,255,.10), 0 1px 3px rgba(0,0,0,.40);
}
/* An explicit light choice must beat the dark media query above. */
:root[data-force="light"] .setsheet .seg { background: var(--fill); }
:root[data-force="light"] .setsheet .seg button[aria-checked="true"] {
  background: var(--canvas);
  box-shadow: 0 0 0 .5px rgba(0,0,0,.04), 0 1px 3px rgba(0,0,0,.12);
}
.setsheet .seg button:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* The typed-word confirm block. The two buttons that OPEN this are rows in
   the account group above (`.ne-danger`); `.delacct` is now only the filled
   button at the end of the confirmation, which is the one place in the app a
   destructive action is presented as a solid red slab — by then the user has
   typed the word and the button is the last thing left to mean it. */
.setsheet .delacct {
  font: inherit; font-size: 0.9286em; font-weight: 500; cursor: pointer;
  padding: 6px 12px; border-radius: 7px; border: 0; box-shadow: none;
  color: var(--danger);
}
.setsheet .delconfirm { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.setsheet .delconfirm .warn { margin: 0; font-size: 0.8571em; color: var(--danger); line-height: 1.45; }
.setsheet .delconfirm input {
  font: inherit; font-size: 0.9286em; padding: 7px 10px; border-radius: 7px;
  border: .5px solid var(--sep); background: var(--fill); color: var(--text);
  outline: none; box-shadow: none;
}
.setsheet .delconfirm input:focus { box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 20%, transparent); }
.setsheet .delacct.go { background: var(--danger); color: #fff; align-self: flex-start; }
.setsheet .delacct.go:hover { background: var(--danger); }
.setsheet .delacct.go:disabled { opacity: .4; cursor: default; }

/* Typeface picker. Each tile renders its OWN face — the sample IS the
   control, which is the only way to choose a font honestly. */
.fontgrid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
  gap: 8px;
}
.fontgrid button {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  padding: 9px 11px; cursor: pointer; text-align: left;
  background: var(--chrome); border: 0; border-radius: 11px; color: var(--text);
  box-shadow: 0 0 0 .5px var(--sep-soft);
  transition: box-shadow .15s ease, background .15s ease;
}
.fontgrid button:hover { filter: none; background: var(--chrome-2); }
.fontgrid button[aria-checked="true"] {
  box-shadow: 0 0 0 2px var(--accent);
  background: color-mix(in srgb, var(--accent) 8%, var(--chrome));
}
.fontgrid button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* The NAME stays in the UI font — set in the face it describes, a display
   face like Eva 12 is decorative enough to be unreadable at this size. The
   sample below it is what carries the face. */
.fontgrid .fname {
  font-family: var(--ui); font-size: 0.74em; font-weight: 590;
  letter-spacing: .03em; text-transform: uppercase; color: var(--text-3);
}
.fontgrid .fsample {
  font-size: 1.18em; line-height: 1.25; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; max-width: 100%;
}
/* The language page reuses the font tiles, but a language row has only its
   native name — no caption above it, so the tile centres its single line. */
.langgrid button { justify-content: center; }
.langgrid .fsample { font-size: 1em; font-weight: 570; }
/* Delete is destructive and belongs at the bottom, alone, full width — the
   placement iOS reserves for it so it can never be hit while reaching for a
   neighbouring control. */
.noteedit .ne-del {
  width: 100%; background: var(--chrome); box-shadow: 0 0 0 .5px var(--sep-soft);
  color: var(--danger); border-radius: 14px; min-height: 44px; cursor: pointer;
  font-size: 0.95em;
}
.noteedit .ne-del:hover { filter: none; background: color-mix(in srgb, var(--danger) 10%, var(--chrome)); }
/* Round chips here, the shape iOS uses wherever a colour is being picked
   rather than displayed. Scoped to the note editor so the task editor's
   square swatches — a different, older surface — are left alone. */
/* Two classes deep, because `.taskedit .sw` is declared LATER in this sheet
   and would otherwise win the tie on source order alone. */
.taskedit .pane.noteedit .swatches {
  margin-bottom: 0; gap: 9px; grid-template-columns: repeat(auto-fill, minmax(32px, 1fr));
}
/* A hairline on every chip. The sheet is `--canvas`, so the white swatch —
   one of the 25 real colours — was a white circle on white and simply
   disappeared; the ring is what gives it an edge without tinting it. */
.taskedit .pane.noteedit .sw {
  border-radius: 50%;
  box-shadow: inset 0 0 0 .5px rgba(0,0,0,.16);
}
.taskedit .pane.noteedit .sw.on {
  border-color: var(--canvas);
  box-shadow: 0 0 0 2px var(--accent), inset 0 0 0 .5px rgba(0,0,0,.16);
}

/* The task editor. A centred modal rather than a popover anchored to the
   row: it carries several fields, and the row it edits can sit at the very
   bottom of a scrolled list where an anchored panel has nowhere to go. */
/* Sheets fade their scrim and rise slightly, rather than appearing between
   one frame and the next. A modal that pops in gives no sense of where it
   came from, which is most of why this app read as older than it is. The
   curve is the iOS sheet easing — fast out of the gate, long settle. */
.taskedit {
  position: fixed; inset: 0; z-index: 60; background: rgba(16,20,28,.30);
  display: flex; align-items: center; justify-content: center; padding: 16px;
  animation: scrim-in .22s ease both;
  /* The scrim is a blur as well as a dim: it pushes the app behind the sheet
     optically, so the sheet reads as floating above a surface rather than
     pasted onto a screenshot. Dim LESS and blur MORE than a flat 34% black —
     a heavy dim reads as a grey sheet of glass and throws away the sense of
     the app still being there behind the panel. */
  backdrop-filter: blur(8px) saturate(120%);
  -webkit-backdrop-filter: blur(8px) saturate(120%);
}
.taskedit .pane {
  width: min(420px, 100%); max-height: min(88vh, 720px); overflow-y: auto;
  background: var(--chrome); border-radius: 20px; padding: 18px 20px 20px;
  /* A hairline plus one soft, offset shadow, rather than a single dense drop.
     The hairline is what actually separates the panel from a light backdrop;
     the shadow only has to suggest height. */
  box-shadow: 0 0 0 .5px color-mix(in srgb, var(--sep) 70%, transparent),
              0 24px 60px -12px rgba(0,20,40,.34);
  animation: sheet-in .34s cubic-bezier(.32,.72,0,1) both;
}
@keyframes scrim-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes sheet-in {
  from { opacity: 0; transform: translateY(12px) scale(.985); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .taskedit, .taskedit .pane { animation: none; }
}
.taskedit .ehead { display: flex; align-items: center; gap: 8px; margin-bottom: 16px; }
.taskedit .ehead .who { font-weight: 650; font-size: 1.08em; letter-spacing: -.01em; }
.taskedit .eclose {
  margin-left: auto; background: none; box-shadow: none; color: var(--text-3);
  font-size: 1.35em; line-height: 1; padding: 0 5px; border-radius: 6px; cursor: pointer;
}
.taskedit .eclose:hover { background: var(--fill); color: var(--text); filter: none; }
.taskedit label.fld { display: block; margin-bottom: 14px; }
/* Sentence case at reading weight. A 9px uppercase label with letter-spacing
   over a bare input is the strongest "old form" tell on the panel — it dates
   the sheet more than any radius does, and it is harder to read besides. */
.taskedit .lbl {
  display: block; font-size: 0.82em; letter-spacing: 0; text-transform: none;
  font-weight: 500; color: var(--text-2); margin-bottom: 6px;
}
.taskedit input[type="text"], .taskedit input[type="time"], .taskedit input[type="date"] {
  width: 100%; font-size: 0.98em; padding: 9px 12px; border-radius: 9px;
}
.taskedit .row2 { display: flex; gap: 10px; }
.taskedit .row2 > * { flex: 1 1 0; min-width: 0; }

/* ---- time field ---------------------------------------------------------
   A button + popover instead of <input type="time">: the native picker is
   unstylable and, on desktop Chrome, a bare spinner list. The popover offers
   the times tasks actually get as chips, then hour/minute columns for the
   rest. Tokens throughout, so dark mode and the Things skin come for free. */
.taskedit .timefld { position: relative; margin-bottom: 14px; }
/* Dressed as the inputs beside it (shell.css input rule), not as a button —
   it answers the same question a field does, it just opens the picker. */
.taskedit .etimebtn {
  width: 100%; text-align: left; font: inherit; font-size: 0.98em;
  padding: 9px 12px; border-radius: 9px; cursor: pointer;
  color: var(--text); background: var(--canvas);
  border: .5px solid var(--sep); box-shadow: none;
  font-variant-numeric: tabular-nums;
}
.taskedit .etimebtn.empty { color: var(--text-3); }
.taskedit .etimebtn:hover { filter: none; border-color: var(--accent); }
.taskedit .etimebtn:focus-visible { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent); }
.taskedit .tpick {
  padding: 12px; border-radius: 12px; margin: -4px 0 14px;
  background: var(--canvas);
  box-shadow: inset 0 0 0 .5px var(--sep-soft);
}
.taskedit .tpick[hidden] { display: none; }
.taskedit .tp-presets { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
.taskedit .tp-chip {
  font: inherit; font-size: 0.85em; font-weight: 500; line-height: 1;
  padding: 7px 10px; border-radius: 8px; border: 0; cursor: pointer;
  color: var(--text); background: var(--fill); box-shadow: none;
  font-variant-numeric: tabular-nums;
}
.taskedit .tp-chip:hover { filter: none; background: color-mix(in srgb, var(--fill) 75%, var(--text-3) 10%); }
.taskedit .tp-chip.on { background: var(--accent); color: var(--accent-ink); }
.taskedit .tp-none { color: var(--text-2); }
.taskedit .tp-none.on { background: var(--accent); color: var(--accent-ink); }
/* Two columns, hours beside minutes, each scrolling on its own. 176px shows
   five rows — enough to say "this is a list" and to keep the selection's
   neighbours in reach without the popover swallowing the sheet. */
.taskedit .tp-cols { display: flex; gap: 8px; }
.taskedit .tp-col {
  /* `relative` so the open-at-selection math (`offsetTop` in sheets.js) is
     measured from the column, not from the pane. */
  position: relative;
  flex: 1 1 0; height: 176px; overflow-y: auto; overscroll-behavior: contain;
  display: flex; flex-direction: column; gap: 2px;
  scrollbar-width: thin;
}
.taskedit .tp-col button {
  flex: 0 0 auto; font: inherit; font-size: 0.92em; line-height: 1;
  padding: 8px 0; text-align: center; border: 0; border-radius: 8px;
  cursor: pointer; color: var(--text); background: none; box-shadow: none;
  font-variant-numeric: tabular-nums;
}
.taskedit .tp-col button:hover { filter: none; background: var(--fill); }
.taskedit .tp-col button.on { background: var(--accent); color: var(--accent-ink); font-weight: 600; }
/* The palette. A grid rather than a scroller so all 25 are visible at once —
   hunting sideways for a colour you can already name is worse than a taller
   panel. */
.taskedit .swatches {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(30px, 1fr));
  gap: 8px; margin-bottom: 16px;
}
.taskedit .sw {
  aspect-ratio: 1; border-radius: 9px; cursor: pointer; padding: 0;
  border: 2px solid transparent; box-shadow: none; position: relative;
}
.taskedit .sw:hover { filter: none; transform: scale(1.08); }
/* The selected swatch needs to read at a glance against 25 neighbours. A
   border alone lost the fight: on a mid-tone chip it is a 2px line of
   near-background colour, and the eye lands on the "no colour" chip instead,
   which looks marked because of its diagonal. So: a ring that clears the
   chip, plus a tick drawn on top of it. */
.taskedit .sw.on {
  border-color: var(--chrome);
  box-shadow: 0 0 0 2px var(--accent);
}
.taskedit .sw.on::after {
  content: ""; position: absolute; inset: 0;
  background: no-repeat center/58% url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12l5 5L20 6'/%3E%3C/svg%3E");
  filter: drop-shadow(0 0 1.5px rgba(0,0,0,.55));
}
/* White and the empty chip need a dark tick — a white one vanishes. The task
   editor's swatches carry `data-hl`, the note editor's carry `data-sw`; both
   spellings of White need the override or one editor keeps the white tick. */
.taskedit .sw.on[data-hl="white"]::after,
.taskedit .sw.on[data-sw="white"]::after,
.taskedit .sw.on.none::after {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23111' stroke-width='3.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12l5 5L20 6'/%3E%3C/svg%3E");
  filter: none;
}
/* "No colour" needs to read as empty, not as a white swatch — white is
   itself one of the 25. */
.taskedit .sw.none {
  background: var(--fill);
  background-image: linear-gradient(135deg, transparent 44%, var(--text-3) 44%, var(--text-3) 56%, transparent 56%);
}
.taskedit .eactions { display: flex; gap: 10px; align-items: center; margin-top: 6px; }
.taskedit .eactions .save { flex: 1 1 auto; min-height: 38px; border-radius: 10px; }
.taskedit .edel {
  flex: none; background: none; box-shadow: none; color: var(--danger);
  padding: 8px 12px; border-radius: 10px; cursor: pointer; font-size: 0.9em;
  min-height: 38px;
}
.taskedit .edel:hover { background: color-mix(in srgb, var(--danger) 12%, transparent); filter: none; }
.taskedit .ro {
  font-size: 0.84em; color: var(--text-3); margin: 0 0 14px;
  padding: 10px 12px; background: var(--fill); border-radius: 10px; line-height: 1.45;
}
/* The row body opens the editor, so it has to look like it does. Both row
   shapes get it: `.task` is the list/sheet row, `.trow` the day and week one
   — they are separate renderers, and styling only the first left the day
   view, the most-used screen of all, with no visible affordance. */
.task .body.editable, .trow .body.editable { cursor: pointer; border-radius: 8px; }
.task .body.editable:hover .title,
.trow .body.editable:hover .name { text-decoration: underline; text-underline-offset: 2px; }
.task .body.editable:focus-visible,
.trow .body.editable:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.wcol { border-right: .5px solid var(--sep-soft); position: relative; min-width: 0; }
.wcol:last-child { border-right: 0; }
.wcol.now { background: color-mix(in srgb, var(--accent) 4%, transparent); }
.hourline { height: 64px; border-bottom: .5px solid var(--sep-soft); }
.ev {
  position: absolute; left: 2px; right: 2px; border-radius: 5px;
  /* These three are load-bearing against MIN_EV_H (20px): the line box plus
     vertical padding has to stay UNDER it, or `overflow: hidden` slices the
     descenders off every title. At 3px/1.25 it came to 21.9px once the root
     type scale went to 17px and every glyph lost its tail. */
  padding: 2px 6px; font-size: 0.72em; line-height: 1.15; overflow: hidden;
  color: var(--text); box-shadow: 0 0 0 .5px rgba(0,0,0,.06);
  text-align: left; font-family: inherit; border: 0; cursor: pointer;
  display: block; width: auto;
  /* The renderer paints the task's raw highlight hex as an INLINE background,
     which hardcoded white ink could not survive: White was white-on-white,
     Lemon and Blush unreadable. The month chips already solved this — colour
     at ~16% over the canvas, normal ink, the full colour kept as an edge —
     and the week now matches, but the inline background cannot be re-declared
     from a stylesheet. So the raw colour stays and a near-opaque canvas veil
     is layered over it, thinning it to the month's tint; `--canvas` keeps it
     right in both themes. `!important` throughout, because the inline
     background SHORTHAND also resets image and clip at inline priority.
     The second (empty) layer exists only to carry `border-box` in the clip
     list: the colour under all layers clips like the bottom layer, so it
     alone reaches under the transparent left border — the one strip where
     the raw colour still shows, the month chip's edge drawn from beneath. */
  --ev-veil: color-mix(in srgb, var(--canvas) 84%, transparent);
  background-image: linear-gradient(var(--ev-veil), var(--ev-veil)),
                    linear-gradient(transparent, transparent) !important;
  background-clip: padding-box, border-box !important;
  border-left: 3px solid transparent;
}
.ev:hover { filter: brightness(1.06); }
/* One line, clipped at the box edge. Two lines do not fit in 20px and were
   being cut through the middle of the glyphs. */
.ev { white-space: nowrap; text-overflow: ellipsis; }
/* Except where the renderer found empty grid under the event and gave it the
   taller box (see EV_TALL_H): there the second line is paid for, so the title
   wraps into it instead of ending at an ellipsis the column had room to avoid.
   Clamped at 2 so the text cannot outgrow the height the renderer reserved —
   the box is positioned and a third line would spill over the next hour. */
.ev.tall {
  /* 1px vertical padding, not 2: the box is EV_TALL_H = 30px and two lines at
     .72em × 1.12 come to 27.4px, so 2px each side would clip the descenders
     of the second line. */
  padding-top: 1px; padding-bottom: 1px; line-height: 1.12;
  white-space: normal; overflow-wrap: break-word;
  display: -webkit-box; -webkit-box-orient: vertical;
  -webkit-line-clamp: 2; line-clamp: 2;
}
.ev b { font-weight: 550; }
.ev span { opacity: .8; font-variant-numeric: tabular-nums; margin-right: 1px; }
/* Done, same reasoning as the month/week chips: a 50% wash on 11px text over
   an already-tinted block put the title under the contrast floor, and the rule
   through it turned what was left into a smear. Step down, stay readable. */
.ev.done { opacity: .74; }
.ev.done b { text-decoration: none; font-weight: 500; color: var(--text-2); }
.nowline { position: absolute; left: 0; right: 0; height: 1px; background: var(--danger); z-index: 3; }
.nowline::before {
  content: ""; position: absolute; left: -3px; top: -2.5px;
  width: 6px; height: 6px; border-radius: 50%; background: var(--danger);
}

/* A day opened from the grid. Same rows as the list view, in a sheet. */
.daysheet {
  position: fixed; inset: 0; z-index: 40; display: grid; place-items: center;
  padding: 20px; background: rgba(16,20,28,.30);
  -webkit-backdrop-filter: blur(8px) saturate(120%);
  backdrop-filter: blur(8px) saturate(120%);
}
/* A column, not one scrolling box: the head and the add field are pinned and
   only the rows scroll. Appending the field after a long day's rows put it
   below the fold, where adding a task meant scrolling past everything first. */
.daysheet .panel {
  background: var(--chrome); border-radius: 20px;
  box-shadow: 0 0 0 .5px color-mix(in srgb, var(--sep) 70%, transparent),
              0 24px 60px -12px rgba(0,20,40,.34);
  width: min(460px, 100%); max-height: 80vh; padding: 18px 20px 18px;
  display: flex; flex-direction: column; min-height: 0;
}
/* `1 1 auto` would stretch this to fill the panel, turning an empty day into
   a tall grey slab with a dove floating in the middle of it. It shrinks to
   its rows and only scrolls once there are more than fit. */
.daysheet .sheet-body { overflow: auto; min-height: 0; flex: 0 1 auto; }
/* Separated from the rows it files into, and out of the scroll. */
.daysheet .sheet-add {
  display: flex; gap: 8px; margin-top: 12px; padding-top: 12px;
  border-top: 1px solid var(--sep); flex: none;
}
.daysheet .sheet-add input { flex: 1 1 auto; min-width: 0; font-size: 1.0em; padding: 8px 11px; }
.daysheet .sheet-add button { flex: none; }
.daysheet #sheetMsg:empty { display: none; }
.daysheet #sheetMsg { margin-top: 8px; flex: none; }
.daysheet .sheet-head { display: flex; align-items: baseline; gap: 9px; margin-bottom: 16px; flex: none; }
.daysheet .sheet-head h3 { margin: 0; font-size: 1.2143em; font-weight: 660; letter-spacing: -.015em; }
.daysheet .sheet-head .sub { font-size: 0.8571em; color: var(--text-3); font-variant-numeric: tabular-nums; }
.daysheet .sheet-close {
  margin-left: auto; background: none; box-shadow: none; color: var(--text-2);
  font-size: 1.5em; line-height: 1; padding: 0 6px; border-radius: 6px; cursor: pointer;
}
.daysheet .sheet-close:hover { background: var(--fill); color: var(--text); filter: none; }

/* Markers head the day rather than sitting in a section: a holiday is a fact
   about the date, not a thing you do. Same placement as the phone's banner
   row under the day header. */
.daymarkers { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 12px; }
.daymarkers .dm {
  font-size: 0.8571em; padding: 3px 9px; border-radius: 999px;
  background: var(--fill); color: var(--text-2);
}

/* Part-of-day section headers, as in the phone's day view. */
.daysheet .sec-head {
  display: flex; align-items: center; gap: 7px; margin: 16px 2px 6px;
}
.daysheet .sec-head:first-child { margin-top: 0; }
.daysheet .sec-head .ic { font-size: 0.9286em; line-height: 1; }
.daysheet .sec-head .nm { font-size: 0.9286em; font-weight: 590; color: var(--text-2); }
.daysheet .sec-head .ct {
  margin-left: auto; font-size: 0.8214em; color: var(--text-3);
  font-variant-numeric: tabular-nums;
}
/* A day sheet is a reading surface — rows should not shift under the cursor
   the way they do in the editable list. */
.daysheet ul.tasks + .sec-head { margin-top: 16px; }

/* Seven columns do not survive a phone. Below that the grids scroll
   horizontally inside their own shell rather than squeezing the page. */
