/* Ledger, not app. Cool paper, near-black ink, one red mark used for "done"
   and nothing else. Hairline rules, generous white space, system fonts, no
   webfont, no build step — this file is the stylesheet, not a compiled copy of
   one, so it cannot go missing on a fresh boot.

   Deliberately close in hand to the author's other ledger, which is where the
   job-hunt items came from: same restraint, so the two do not read as two
   different tools when they are open side by side. */

* { box-sizing: border-box; }

/* --- said, not drawn --------------------------------------------------------

   Off the page, still in the accessibility tree, and still focusable — which is
   the difference between this and `hidden`, and the reason the import file
   picker could not be reached by keyboard at all. `display: none` would take it
   out of both, and a live region that is display:none when it is written is
   never announced.

   Not `clip` and not a negative left: those either move focus off-screen and
   scroll the page chasing it, or are dropped by browsers that have stopped
   supporting them. */
.offscreen {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Every control here has a focus ring; links had only the browser's own, which
   is the one thing on the page that does not follow the theme. Same accent, the
   same offset, on anything that takes focus and does not draw its own. */
a:focus-visible,
[data-import-label]:has(:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 2px;
}

/* --- icons ------------------------------------------------------------------

   Inline SVG, drawn in src/core/render.js. There is no icon font and no icon
   stylesheet to fetch — see the comment on ICONS there for why the licensed one
   had to go and why fetching one at deploy time would have cost offline.

   Sized like text on purpose: 1.25em wide and 1em tall, the box an icon font
   occupies, so `font-size` on the surrounding button is still the only thing
   that sets an icon's size and every rule below keeps working untouched. */
.icon {
  display: inline-block;
  width: 1.25em;
  height: 1em;
  vertical-align: -0.125em;
}

/* Duotone in a one-colour ledger: the same ink twice, once faint. Nothing here
   introduces a second hue. */
.icon--dim { opacity: 0.35; }

html { background: var(--bg); }

body {
  margin: 0;
  color: var(--ink);
  background: var(--bg);
  font-family: var(--sans);
  line-height: 1.6;
}

.sheet {
  max-width: var(--sheet);
  margin: 0 auto;
  padding: clamp(1.4rem, 4vw, 2.4rem) clamp(1.1rem, 4vw, 2rem) 3rem;
}

h1 {
  font-family: var(--display);
  font-weight: 700;
  letter-spacing: -0.035em;
  line-height: 1.05;
  font-size: clamp(1.9rem, 5vw, 2.5rem);
  margin: 0 0 0.35rem;
}

/* The front page's h1 is the date, and the date is not what the page is for.
   At display size it was the first and largest thing on a phone, above a list
   of numbers set smaller than the words beside them — the loudest thing on the
   screen answering a question nobody opened the app to ask. It stays the h1,
   because it is the document's heading and a tab left open overnight has to be
   able to say which day it is showing; it just stops shouting. Still four
   times the size it was when it lived in the masthead corner, which is the
   problem that put it here. */
.dateline {
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.3;
  color: var(--muted);
  margin: 0 0 0.7rem;
}

/* Small uppercase mono rule — the only section heading here. Faint, not red:
   red is spent on "done" alone, and spending it on headings spends it. */
h2 {
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--faint);
  font-weight: 500;
  margin: 2.2rem 0 0.55rem;
  padding-bottom: 0.3rem;
  border-bottom: 1px solid var(--line);
}

p { font-size: 0.89rem; line-height: 1.6; margin: 0 0 0.7rem; }

.lede {
  font-size: 1.04rem;
  color: var(--muted);
  max-width: 62ch;
  margin: 0 0 2.6rem;
}
.lede b { color: var(--ink); font-weight: 600; }

/* "5h 50m of work left, finishing 1:50 AM" is the most useful sentence on this
   page, and it was set in the faint tier — a step BELOW the sentence it
   finishes, and the palest thing on screen. It sits at the same weight as the
   rest of the lede now, with the two numbers in ink; there is no third tier
   here, because the tail of a sentence is not a footnote. */
.lede__rest { color: var(--muted); }

/* The drift lede belongs to the list directly under it, so it does not get the
   full stop the day's lede gets before a whole new section.

   It is also a step smaller than the day's lede, which looks backwards and is
   not. The list under it is the answer; this sentence is the same answer in
   prose, for the reader who wants it in words. Two things saying the same
   thing at the same size is two things to read, and the budget for this screen
   is about four seconds. */
.lede--drift {
  font-size: 0.95rem;
  margin-bottom: 0;
  max-width: 46ch;
}

/* --- every area, longest untouched first ----------------------------------

   This is the product. Not a section of the front page — the front page. It
   gets designed for the screen it is read on, which is a phone held in one
   hand, in about four seconds, by somebody carrying a child, and the desktop
   follows from that rather than the other way round.

   THE SCAN. Five rows have to resolve to "that one" without reading. There is
   no colour available to do it with and that is deliberate: one accent, and it
   means "done" — the moment a second thing is red the red stops meaning
   anything, and a drift list is exactly where a product reaches for red. So
   the notation is the one this app already uses everywhere else. A row inside
   its cadence RECEDES, the way a ticked item recedes. A row past it simply
   does not fade. Absence of fading is the whole signal.

   What carries it is typography:

     SIZE puts the numeral at the top of the hierarchy. It used to be 0.78rem
     — smaller than the name beside it — so the one quantity on the page was
     the quietest thing on it. It is 1.75rem now and it is the largest thing on
     the screen, including the date.

     ONE COLUMN, ONE SIZE, TABULAR FIGURES. Every numeral is the same size and
     shares a right edge, so 17 and 6 are two lengths rather than two words.
     Nothing is scaled by how bad it is: the moment a numeral's size means
     something, the column stops being comparable and starts being a chart.

     TIER carries the state. Drifted: full ink, bold. Inside its cadence:
     muted, regular. The unit and the cadence are a tier below both, in the
     faint grey the rest of the page uses for footnotes.

     BLANK SPACE says "nothing to see". Today, yesterday and not-yet get no
     numeral at all — a numeral means a gap somebody might want to look at, so
     an area touched this morning leaves a hole in the column. That is a signal
     nobody had to be taught and nothing had to be coloured to say.

   THE TARGET. The name is the link and it is stretched over the entire row by
   the ::after below, so the tap target is the full width of the phone and
   around 70px tall. A one-handed tap at arm's length does not find a 16-pixel
   run of underlined text. */
.drift {
  list-style: none;
  padding: 0;
  border-top: 1px solid var(--line);
  /* Air above the list, and it is doing a job. The top row is the worst-drifted
     area and therefore the one most likely to be pressed, and the top of a tall
     phone is the one part of it a thumb cannot reach. This drops the whole list
     into the lower two-thirds of the first screen without pushing any of it
     under the fold. vh rather than a fixed value so a small phone does not
     spend the room it has not got. */
  margin: clamp(1rem, 6vh, 2.6rem) 0 0;
}

/* Two columns at every width: the name and its cadence stacked on the left,
   the numeral alone on the right. The cadence used to sit between them on wide
   screens and drop underneath on narrow ones, which meant the desktop and the
   phone were two layouts to keep true. One layout, one rhythm, and the numeral
   column is in the same place whatever the screen is. */
.drift__row {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-rows: auto auto;
  column-gap: 1.1rem;
  align-items: center;
  padding: 0.8rem 0;
  /* One height for every row, whether it carries a numeral or the word
     "today". Two reasons and both are the brief: a column of numbers is only a
     column if the rows under it are evenly spaced, and a tap target that
     changes size depending on how long ago you last tended to the area is a
     tap target somebody misses. */
  min-height: 4.4rem;
  /* Every row the same height whatever it carries. The numeral is 1.75rem and
     the word states are a third of that, so without this the list breathes
     unevenly and the numerals stop reading as one column. */
  grid-template-rows: 1.9rem 1.5rem;
  border-bottom: 1px solid var(--line);
}

.drift__name {
  grid-area: 1 / 1;
  align-self: end;
  font-family: var(--display);
  font-size: 1.12rem;
  font-weight: 400;
  letter-spacing: -0.015em;
  line-height: 1.25;
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid transparent;
}
.drift__name:hover { border-bottom-color: var(--line2); }


/* The cadence is a <select>, and it has to not look like one.

   It is the one control on this page that is read a hundred times for every
   time it is touched — so it stays the small grey word it was, with no arrow,
   no box, no chrome and nothing to draw the eye, and grows an underline only
   under a pointer or a focus ring. Anything more and the page acquires a row
   of form controls, which is a settings screen; anything less and there is no
   way to tell it can be pressed at all.

   `font: inherit` first because a <select> does not inherit the page's face on
   its own, and the size after it: without both, one row in the list is set in
   the system UI font at the system UI size and the column stops lining up. */
.drift__meta {
  grid-area: 2 / 1;
  align-self: start;
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  min-width: 0;
  position: relative;
  z-index: 1;
}
/* A middot between them, the same separator the rest of the app uses. */
.drift__meta > a::before { content: '·'; margin-right: 0.5rem; color: var(--line2); }

/* A <select> is as wide as its LONGEST option, always — so a row reading
   "weekly" reserved the width of "a couple of times a week" and left a hole
   between it and the link beside it. The visible word is a span that sizes to
   itself; the real control lies over it at zero opacity, so the platform's own
   wheel-on-a-phone, listbox-on-a-keyboard behaviour is untouched and there is
   nothing to reimplement. field-sizing: content would do this in one line and
   is Chrome-only. */
.drift__cad { position: relative; display: inline-block; }
.drift__cadword {
  font-size: 0.72rem;
  line-height: 1.35;
  color: var(--faint);
  border-bottom: 1px solid transparent;
  white-space: nowrap;
}
.drift__cad:hover .drift__cadword { color: var(--muted); border-bottom-color: var(--line2); }
.drift__cad:focus-within .drift__cadword { outline: 2px solid var(--accent); outline-offset: 2px; }

.drift__cadence {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  /* Its own place on the grid, under the name rather than between the name and
     the number: one column of words on the left, one column of numerals on the
     right, at every width. justify-self keeps the control as narrow as the
     word it is showing, so the rest of the row still belongs to the link.

     Lifted above .tend, the row-wide button. Without this the button eats every
     press meant for the cadence, and the row's one deliberate control becomes
     unreachable by touch. (This used to say .drift__name::after — the name was
     the stretched link before the row became a button.) */
  font: inherit;
  font-size: 0.72rem;
  line-height: 1.35;
  /* Was --line2, which is the HAIRLINE tier: about 1.4:1 against this paper,
     so the one thing on the row that says what the number was measured against
     was, for practical purposes, not drawn. --faint is the quietest colour on
     this page that is still a colour you can read — 4.8:1 light, 5.8:1 dark. */
  color: var(--faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  appearance: none;
  -webkit-appearance: none;
  max-width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  border-bottom: 1px solid transparent;
  border-radius: 0;
  background: none;
  cursor: pointer;
  /* The list that drops out of it is drawn by the platform, so it needs to be
     told which theme it is in or it comes back white on a dark page. Nothing
     is set here on purpose: `color-scheme` inherits, and tokens.css already
     puts the right one on :root for both themes and the manual toggle. */
}
.drift__cadence:hover { color: var(--muted); border-bottom-color: var(--line2); }
.drift__cadence:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* A word set at 0.72rem is a fine thing to read and a poor thing to hit: on a
   phone the box is fifteen pixels tall, which is half a thumb. Keyed on the
   POINTER rather than the width — the problem is the finger, and a finger on a
   tablet at 900px has it too. The negative margin gives the padding back to
   the layout, so the target grows and the row does not move. */
@media (pointer: coarse) {
  .drift__cadence { padding: 0.45rem 0; margin: -0.45rem 0; }
}

.drift__since {
  grid-area: 1 / 2;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: center;
  min-width: 0;
  white-space: nowrap;
}

.drift__n {
  font-family: var(--mono);
  /* Both, and on purpose. Tabular so a 1 occupies a 6's width and the column
     stays a column; lining so no face with old-style figures drops a 4 below
     the baseline and breaks the one line every numeral here shares. */
  font-variant-numeric: tabular-nums lining-nums;
  font-size: 1.75rem;
  font-weight: 400;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--muted);
}

/* The unit, and the three states that have no number. Same tiny mono label in
   both, so the right-hand column has one voice under the numerals: "DAYS" and
   "TODAY" are the same kind of thing said about the same column. */
.drift__unit,
.drift__word {
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  line-height: 1.2;
  color: var(--faint);
}
.drift__unit { margin-top: 0.28rem; }

/* Inside the gap set for it: recede. Same move as .item--done, same reason —
   the eye should land on what is left. The numeral goes a tier further down
   than the name it belongs to, which looks backwards written out and is not:
   on a row that is fine the number is the least interesting thing on it, and
   the whole right-hand column then reads as one pale field with the ink
   numerals standing out of it. --faint is 4.8:1 on this paper and 5.3:1 on the
   dark one — quiet, and still a colour you can read. */
.drift__row--ok .drift__name { color: var(--muted); }
.drift__row--ok .drift__n { color: var(--faint); }

/* Past it: no mark, no colour, no icon, no badge. It simply does not fade, and
   the numeral is the one thing on the page in full ink at full weight. */
.drift__row--drifting .drift__name { font-weight: 600; }
.drift__row--drifting .drift__n { color: var(--ink); font-weight: 700; }

/* Never ticked is not a drift and must not read as the worst thing here. The
   name stays in full ink because the question is unanswered, but the words are
   "not yet" and there is no number to make large. */
.drift__row--never .drift__name { color: var(--ink); }

/* Wider than a phone, the same layout with more air in it. Nothing moves
   column; the type simply gets the room a desk has. */
@media (min-width: 40rem) {
  .drift__row { padding: 0.95rem 0; column-gap: 2rem; }
  .drift__name { font-size: 1.2rem; }
  .drift__n { font-size: 2rem; }
}

/* The one link that opens a panel from inside a view, rather than from the
   foot row. Same small mono capitals it had as a fold trigger; what changed is
   what it opens.

   The fold itself is gone. `:target` was the right mechanism for an inline
   disclosure on a page that redraws itself — the URL is the one piece of state
   a redraw cannot reach — but an inline disclosure was the wrong shape: it grew
   the page by a screen when opened and left a "close" link somewhere below the
   fold. A dialog leaves the page exactly as it was and takes Escape for free. */
.tuck__open a,
.tuck__open .linky--panel {
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--faint);
  font-weight: 500;
  text-decoration: none;
  border-bottom: 1px solid var(--line);
}
.tuck__open a:hover,
.tuck__open .linky--panel:hover { border-bottom-color: var(--line2); }

.tuck__open {
  margin: 1.5rem 0 0;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--line);
}

/* Cloned into the dialog when opened; never drawn where it sits. */
.panel { display: none; }

.note {
  color: var(--faint);
  font-size: 0.8rem;
  line-height: 1.55;
  margin: 0.8rem 0 0;
  max-width: 68ch;
}
.note b { color: var(--muted); }
.note i { font-style: italic; }

.stamp {
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
}

a { color: inherit; text-decoration: none; }

.masthead {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  padding-bottom: 0.6rem;
  border-bottom: 1px solid var(--line);
  margin-bottom: 1.4rem;
}

.mast {
  font-family: var(--display);
  font-size: 0.9rem;
  font-weight: 700;
  letter-spacing: -0.03em;
}
.mast i { font-style: normal; color: var(--faint); font-weight: 400; }

.mast__right { display: flex; align-items: center; gap: 0.7rem; }

/* Quiet until you look for it, like every other control here — but --line2 is
   the hairline colour, and an icon that is the only control in the masthead
   cannot be drawn at 1.6:1. --faint is the same quiet tier as the date beside
   it and clears the 3:1 a control has to make to be seen at all. */
.theme {
  appearance: none;
  border: 0;
  background: none;
  padding: 0.1rem;
  font-size: 0.8rem;
  line-height: 1;
  color: var(--faint);
  cursor: pointer;
}

.theme:hover { color: var(--ink); }
.theme:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }
.mast a:hover { color: var(--accent); }

/* --- index: every block, every checkbox, one view -------------------------- */

/* The rule between blocks, and the air around it.
   -----------------------------------------------------------------------
   It used to hang off "add an item" — 0.5rem of space above it and 1.8rem
   below — so it hugged the block it was ending and floated away from the one
   it was starting. A separator that is closer to one side is not reading as a
   separator, it is reading as an underline on whatever it is touching. Equal
   space both sides now, and it belongs to the block rather than to the last
   row in it.

   And it fades out to the right rather than running the full width at one
   weight. A hard edge-to-edge rule is a stronger statement than "these are
   two different areas of your life" needs — it draws a box, and boxes are
   what this page is trying not to be. The fade says the same thing and stops
   competing with the numerals on that side. */
/* The whole day view is one grid.
   -----------------------------------------------------------------------
   The tracks were on each block, and a block is one container among five —
   two blocks, a day-break list, and so on — so every `auto` track was sized by
   whatever that one container happened to hold. The clock column matched by
   luck (it is the last thing before the gutter) and the duration did not: a
   day-break staple's "30m" sat fourteen pixels off the "30m" in the block
   above it. Pinning widths would have been treating the symptom.

   Owned here and inherited all the way down — block, heading, list, row,
   button — a column is a column across the entire page. */
.blocks {
  display: grid;
  /* The two numeric tracks are stated rather than left to `auto`. An auto
     track is sized by its content, and with a 1fr neighbour taking the slack
     it collapsed to the width of the SHORTEST duration on the page — 20px,
     the size of "1h" — so every longer one overflowed its own column and the
     right edges stopped agreeing. Stated widths hold whatever the plan
     contains, and "45m left" is the widest thing either column ever carries. */
  grid-template-columns: auto auto minmax(0, 1fr) 4.8rem 5.6rem auto;
  column-gap: 0.85rem;
}

.block {
  margin: 0 0 2rem;
  padding-bottom: 2rem;
  position: relative;
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
}
.block::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: linear-gradient(to right, var(--line) 0%, var(--line) 45%, transparent 100%);
}
/* Nothing to separate after the last one. */
.block:last-of-type::after { content: none; }

/* No rule under the heading: the whitespace above it already groups the list,
   and a third hairline in the same weight made every section transition read
   as four evenly-spaced lines. */
/* A row of the block's grid, not a line of text with numbers pushed to the end
   of it. "0/3" now sits over the descriptions and "1h 45m" over the durations,
   which is what makes the header read as a total OF the column under it rather
   than as a caption that happens to be right-aligned. */
.block__head {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
  align-items: baseline;
  padding-bottom: 0.5rem;
}
/* The heading's cells live in a wrapper spanning 1..5, exactly as an item's
   cells live inside the button that spans 1..5. Sitting them directly in the
   block grid instead put them in the same TRACKS but not the same box, and the
   header's time came out sixteen pixels right of every time under it. Same
   container shape, same geometry. */
.block__cells {
  display: grid;
  grid-column: 1 / 6;
  grid-template-columns: subgrid;
  align-items: baseline;
}
.block__h { grid-column: 1 / 3; }
.block__done { grid-column: 3; }
.block__left { grid-column: 4; }
.block__head > .ctl { grid-column: 6; }

.block__done,
.block__left {
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* Both numerals right-aligned. A count sitting flush left under a column of
   descriptions reads as the first word of one. */
.block__done,
.block__left { text-align: right; }

/* The block name is an h2 now, so a screen reader's heading list has the five
   sections this page is made of in it. It must not LOOK like the section rules
   further down — small mono capitals with a hairline under them — so every
   inherited h2 declaration is put back here. The look is entirely .block__title
   below, exactly as it was. */
.block__h {
  margin: 0;
  padding: 0;
  border: 0;
  font: inherit;
  letter-spacing: inherit;
  text-transform: none;
  color: inherit;
}

.block__title {
  margin-right: 1rem;
  font-family: var(--display);
  font-size: 1.02rem;
  font-weight: 600;
  letter-spacing: -0.015em;
  border-bottom: 1px solid transparent;
}
/* The hover underline belongs to the link, not to the face. A block's title
   goes somewhere; the Staples heading is a name and nothing else, so it takes
   the same type and none of the affordance. */
a.block__title:hover { border-bottom-color: var(--line2); }

.block__meta {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.04em;
  color: var(--faint);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.block__meta b { color: var(--ink); font-weight: 500; }
.block__meta i { font-style: normal; color: var(--line2); margin: 0 0.15rem; }

/* One grid for the whole block, so the columns line up DOWN the list rather
   than being re-measured on every row.
   -----------------------------------------------------------------------
   The description used to follow the name inline, and the name is
   content-width, so it began at a different x on every row: a ragged edge the
   eye has to re-find on each line, which is the opposite of scanning. Flex
   cannot fix that — each row is its own flex container and knows nothing about
   the row above it.

   Subgrid can. The list owns the four columns; the row and the button both
   inherit them, so the button stays a real <button> with a real box rather
   than being flattened with display: contents, which is where this would
   otherwise end up and where screen readers have historically lost the
   control entirely.

   The name column is `auto`: as wide as the longest name IN THIS BLOCK and no
   wider. Nothing is truncated and nothing wraps to a second line, which were
   the two prices of doing this with a fixed width. Alignment is per block,
   which is the right scope — a block is what somebody reads down. */
.items {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
}

/* No rule between rows. A hairline under every item turns a block into a
   ladder, and a ladder is read rung by rung — which is the opposite of what a
   list of four short rows wants. The rows are checkbox, name, duration now:
   three things at three fixed x positions, which the eye tracks down without
   help. Space groups them; the one rule left in a block is the one that ends
   it. Rules were doing two jobs, separating rows and separating blocks, and
   the second job is the one worth a line. */
.item {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
  align-items: center;
  padding: 0.1rem 0;
}

/* A row that is not one of the five-column items — "add an item", the fold —
   spans the lot and lays itself out normally. */
.items > .empty, .items > .more {
  grid-column: 1 / -1;
}

/* A staple and a continuation are rows of the same table as an item, not
   strips laid across it. They used to place the clock on the LEFT, where an
   item puts its name, and the duration on the right — so a block with a staple
   in it had two different column orders running down it and neither number
   lined up with the rows above or below. Same six columns, same order. */
.items > .staple, .items > .cont {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
  align-items: center;
}
/* The box column is held open so the names line up; a staple has nothing to
   tick and a continuation is the tail of the row above it. */
.check__box--none { visibility: hidden; }

/* The whole row is the hit target: this gets tapped on a phone, half asleep,
   before coffee. */
.check {
  appearance: none;
  border: 0;
  background: none;
  min-width: 0;
  display: grid;
  grid-column: 1 / 6;
  grid-template-columns: subgrid;
  align-items: center;
  padding: 0.5rem 0;
  cursor: pointer;
  text-align: left;
  font: inherit;
  color: inherit;
}

.check__box {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  border: 1px solid var(--line2);
  border-radius: 3px;
  background: var(--panel);
  font-size: 0.82rem;
  line-height: 1;
  color: var(--accent);
}


/* When, not how long. This is the column you scan, so it is the darker of the
   two and it holds its width whether or not the row has a time in it. */
/* Three flex leftovers lived here and one of them cost thirteen pixels. The
   `margin-right: 0.85rem` was the gutter this cell used to need when the row
   was a flex line; under the grid it is the gap's job, so the margin simply
   pushed the clock thirteen pixels left of its own column — which is exactly
   how far the heading's time missed the times under it by. `flex: none` and
   the hard width went with it: the track sets the width now. */
.check__at {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.02em;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.check__label { font-size: 0.92rem; }

/* The gist, on the same line as the name: quieter, and clipped rather than
   allowed to push the duration off the right edge. It is a glance, not a read
   — the whole sentence lives on the block page. */
/* Its own column now, starting at the same x on every row of the block. Empty
   on rows that have no description — the cell still exists, which is what
   keeps the duration in one place down the list. */
.check__sum {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.8rem;
  color: var(--faint);
}


.item--done .check__sum { color: var(--line2); }

/* Flush right, in its own column, and it stays there.
   -----------------------------------------------------------------------
   A review asked for this to move up beside the name: on a row with no gist,
   a short label leaves a wide gap before its duration and the two stop reading
   as a pair. It is not moving, and the gap is not a defect.

   This is a ledger line — what, on the left; how much, on the right — and the
   whole value of the right-hand column is that fifteen of them stack into one
   scannable edge. Put the number after the label and it lands in a different
   place on every row, which is the arrangement that actually costs you the
   ability to read the column. The middle is not empty by accident either: it
   is where the gist goes, and the rows that feel wide are the ones that have
   not been given one. On the narrow page, where this is read on a phone, the
   gist is hidden and the gap does not exist at all.

   What the complaint was right about is that the column was only nearly a
   column: proportional digits made "1h 30m" and "15m" ragged against each
   other. Tabular figures, so it is a real one. */
.check__min {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 0.68rem;
  color: var(--faint);
  letter-spacing: 0.02em;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.check:hover .check__box { border-color: var(--ink); }
.check:focus-visible { outline: none; }
.check:focus-visible .check__box { outline: 2px solid var(--accent); outline-offset: 2px; }
.check[aria-pressed='true'] .check__box { border-color: var(--accent); background: var(--panel2); }

/* Done recedes rather than celebrates — the eye should land on what is left.
   No colour change, no animation, nothing that reads as congratulation. */
.item--done .check__label {
  color: var(--faint);
  text-decoration: line-through;
  text-decoration-color: var(--line2);
}
.item--done .check__min { color: var(--line2); }

/* The next thing to do.
   -----------------------------------------------------------------------
   "Completed things recede" was only half the promise — the eye landed on
   fifteen identical rows and the first unticked one had no more claim on it
   than the last. This is the other half, and it is the same idea: the row the
   page is an answer to gets the ink tier and a heavier name.

   Weight and ink, never the accent. There is one loud colour here and it means
   "done"; a red "next" would be the second red thing, and then neither means
   anything. Nothing moves, nothing is boxed, nothing animates — it is the same
   row, set at the weight the answer deserves. */
.item--next .check__at { color: var(--ink); }
.item--next .check__label { font-weight: 600; }

/* --- running order and editing ------------------------------------------- */

/* Hidden until you go looking, and holding their space either way so the rows
   do not jump sideways as the pointer runs down the list. */
.ctl {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.4rem;
  width: 5.2rem;
  opacity: 0;
  transition: opacity 90ms linear;
}

.item:hover .ctl,
.staple:hover .ctl,
.block__head:hover .ctl,
.item:has(:focus-visible) .ctl,
.block__head:has(:focus-visible) .ctl,
.ctl:has(:focus-visible) { opacity: 1; }

.ctl__arrows {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

/* The touch handle, absent wherever a pointer exists: there the controls
   already come out under it, and a permanent mark in every row would be noise
   bought for nothing. Everything that turns it on lives in one place, at the
   foot of this file. */
.ctl__grip { display: none; }

.ctl__edit,
.ctl__btn,
.ctl__grip {
  appearance: none;
  border: 1px solid transparent;
  border-radius: 2px;
  background: none;
  color: var(--line2);
  cursor: pointer;
  font-family: var(--mono);
}

.ctl__edit {
  padding: 0.12rem 0.4rem;
  font-size: 0.78rem;
}

.ctl__btn {
  padding: 0.02rem 0.3rem;
  font-size: 0.6rem;
  line-height: 1.25;
}

.ctl__edit:hover,
.ctl__btn:hover,
.ctl__grip:hover { color: var(--ink); border-color: var(--line); }
.ctl__edit:focus-visible,
.ctl__btn:focus-visible,
.ctl__grip:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Two sentences for the same fact, one per input device. Neither is ever shown
   next to the other. */
.touch-only { display: none; }

/* --- dragging a row ------------------------------------------------------- */

/* The one element on the page a touch must not scroll from. It is the touch
   handle already, so a finger that starts here has said which of the two it
   means — which is how a drag is told apart from a swipe with no long-press
   delay to discover. The list itself keeps scrolling exactly as it did, because
   this declaration is on the grip and nowhere near it. */
.ctl__grip { touch-action: none; }

/* Held, rather than dropped in a new place: the rows around it do not move and
   their times do not change, because nothing has moved yet. Above them so it
   reads as picked up off the page, and on the panel colour so the ruled line it
   is passing over does not run through the middle of it. */
.item.is-lifted,
.block__head.is-lifted {
  position: relative;
  z-index: 3;
  background: var(--panel2);
  box-shadow: 0 0 0 1px var(--line);
  border-radius: 2px;
}

/* In the air it has no start time. The column keeps its width so nothing
   shifts, and it says nothing rather than the time the row had before it was
   picked up — which is the one reading on this page that is certainly about to
   be wrong. Every other clock on the page is still true and stays put; they all
   change together on the drop. */
.is-lifted .check__at { visibility: hidden; }

/* Where it will land. Fixed, appended to the body, and never a child of the
   sheet — the sheet is rewritten by morph() and this has to outlive that.
   --ink, not the accent: there is one loud colour here and it means "done". */
.caret {
  position: fixed;
  z-index: 5;
  height: 2px;
  margin-top: -1px;
  background: var(--ink);
  border-radius: 1px;
  pointer-events: none;
}

/* grabbing wins over the row's own pointer cursor for as long as the drag
   lasts, and the text under the finger stops trying to be a selection. */
.is-dragging body { user-select: none; }
.is-dragging,
.is-dragging .check,
.is-dragging .block__title,
.is-dragging .linky { cursor: grabbing; }

/* A row that will not be picked up, refusing where the finger is.
   -----------------------------------------------------------------------
   A staple is a fixed commitment at a clock time and is not in the running
   order at all; a continuation is the tail of the item above it. Neither
   moves, and the failure this avoids is the one that looks like it worked: a
   row that appears to lift and then snaps back says the app tried and could
   not, which is a bug report. Nothing lifts. Instead the row is marked for a
   beat and its CLOCK TIME goes to full ink, because the time is the actual
   reason — change that and it moves. */
.row--fixed {
  outline: 1px dashed var(--line2);
  outline-offset: -1px;
  animation: refused 260ms ease-in-out;
}

.row--fixed .check__at { color: var(--ink); }

@keyframes refused {
  25% { transform: translateX(-3px); }
  75% { transform: translateX(3px); }
}

/* The outline and the ink still say it; only the shake goes. */
@media (prefers-reduced-motion: reduce) {
  .row--fixed { animation: none; }
}

/* The plan on disk is broken and the page is showing the last good copy. This
   is the one thing here allowed to shout. */
.alarm {
  border: 1px solid var(--accent);
  border-left-width: 3px;
  border-radius: 3px;
  padding: 0.8rem 1rem;
  margin: 0 0 1.4rem;
  font-size: 0.84rem;
  color: var(--ink);
}

.alarm ul { margin: 0.4rem 0 0; padding-left: 1.1rem; color: var(--muted); }

/* "add an item" and "add a block" were set at the size and weight of the items
   themselves, so a two-item block read as three lines of which one happened to
   be furniture. Adding is rare — the plan is meant to be small and fixed — and
   a control used once a month should not hold the same rank on the page as the
   thing used every day. It keeps its underline, so it is still plainly
   something you press. */
.empty .linky, .adds .linky {
  font-size: 0.78rem;
  color: var(--faint);
}
.empty .linky:hover, .adds .linky:hover { color: var(--muted); }

.adds {
  margin-top: 1.8rem;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem;
}
.adds__sep { color: var(--line2); }

/* .adds is a ROW — "show 3 more areas · add a block" — and its flex gap is what
   spaces that row. Put the same class on a sentence with a button in it and
   every text node becomes a flex item with half a rem around it, which reads as
   broken justification. This keeps the button's styling and drops the row. */
.adds--say {
  display: block;
  margin-top: 1.2rem;
}

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

/* --- the first minute ------------------------------------------------------ */

/* Nothing new in the vocabulary: the same hairlines, the same mono for machine
   data, the same single red for "done" — which is why none of it appears here.
   A first screen that introduced a colour or a card shadow would be teaching
   the reader a language the rest of the app does not speak. */

.starters { list-style: none; margin: 0 0 1.6rem; padding: 0; }

.starter { padding: 1.1rem 0 1.2rem; border-top: 1px solid var(--line); }
.starter:last-child { border-bottom: 1px solid var(--line); }

.starter__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.2rem 0.8rem;
}

/* An h3 under the "Start from one of these" h2, so three cards a stranger is
   choosing between can be moved through by heading. The margin is the only
   thing the tag brings that this design does not want. */
.starter__name {
  margin: 0;
  font-family: var(--display);
  font-weight: 700;
  letter-spacing: -0.025em;
  font-size: 1.1rem;
  line-height: inherit;
}

/* Counted rather than described: three blocks, nine items and 2h 15m is the
   fact a person actually weighs before clicking. */
.starter__meta {
  font-family: var(--mono);
  font-size: 0.68rem;
  color: var(--faint);
  white-space: nowrap;
}
.starter__meta i { font-style: normal; color: var(--line2); margin: 0 0.15rem; }

.starter__say { color: var(--muted); max-width: 62ch; margin: 0.3rem 0 0.8rem; }

.starter__blocks { list-style: none; margin: 0 0 0.9rem; padding: 0; }

/* The whole plan, printed. One click is about to become somebody's day, and a
   card showing only a name would be asking for trust nothing has earned yet. */
.starter__block { padding: 0 0 0.7rem; }

/* The area's name, set like a block heading on the day — same face, same
   weight, one step down in size so the card's own name still leads. It was
   small mono capitals in a left-hand column, which reads as a field label and
   left the items looking louder than the thing they belong to. */
.starter__bt {
  margin: 0 0 0.15rem;
  font-family: var(--display);
  font-size: 0.92rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.starter__items {
  display: grid;
  gap: 0.1rem;
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 0.86rem;
  color: var(--muted);
  min-width: 0;
}

.starter__item {
  display: flex;
  align-items: baseline;
  gap: 1rem;
}

.starter__item i {
  margin-left: auto;
  flex: none;
  font-style: normal;
  font-family: var(--mono);
  font-size: 0.68rem;
  color: var(--faint);
  font-variant-numeric: tabular-nums;
}

.starter__foot { display: flex; align-items: center; flex-wrap: wrap; gap: 0.6rem 0.8rem; margin: 0; }
.starter__then { color: var(--faint); font-size: 0.8rem; }

/* Short claims with a line under each, not a bullet list.
   -----------------------------------------------------------------------
   They were identical rows separated by hairlines, which is a table of
   contents for an argument nobody asked to read. The claim carries the display
   face at a size that stops the eye; the line under it is small and grey and
   explains itself in one breath. Rhythm does the work a bullet was doing.

   Still not a feature grid — a grid of ticks is exactly the thing being
   refused — and still no colour: the emphasis is size and weight. */
.refusals {
  list-style: none;
  margin: 0 0 1.4rem;
  padding: 0;
  display: grid;
  gap: 1.5rem;
  max-width: 60ch;
}

.refusal { margin: 0; }

.refusal__say {
  margin: 0;
  font-family: var(--display);
  font-size: 1.14rem;
  font-weight: 600;
  letter-spacing: -0.015em;
  line-height: 1.3;
  color: var(--ink);
}

.refusal__why {
  margin: 0.35rem 0 0;
  font-size: 0.84rem;
  line-height: 1.6;
  color: var(--faint);
}

/* The first one is the hook and gets a size of its own. A list where every
   entry is weighted the same has no first sentence. */
.refusal:first-child .refusal__say { font-size: 1.45rem; }
.refusal:first-child .refusal__why { font-size: 0.9rem; color: var(--muted); }

@media (max-width: 33rem) {
  .refusals { gap: 1.2rem; }
  .refusal__say { font-size: 1.05rem; }
  .refusal:first-child .refusal__say { font-size: 1.25rem; }
}
.refusal i { font-style: italic; }

/* Links inside a note were indistinguishable from the prose around them: the
   global rule strips them, and .linky's underline is what a control looks like
   here. Same treatment, so a link reads as one. */
.note a, .lede a {
  border-bottom: 1px solid var(--line2);
}
.note a:hover, .lede a:hover { color: var(--accent); border-bottom-color: var(--accent); }

/* --- the staples line ------------------------------------------------------ */

/* It used to be one grey paragraph with its own little mono label inside it,
   sitting in a stack of five other grey paragraphs that all looked the same.
   The label was an h2 in everything but name — same face, same size, same
   tracking — so it is one now, and the strip below it is the section's
   content rather than more note. Its explanation is a plain note underneath,
   like the streak rule under the month. */
.staples { margin: 0.2rem 0 0.5rem; }

.staples__sep { color: var(--line2); margin: 0 0.35rem; }

.linky {
  appearance: none;
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  color: var(--muted);
  cursor: pointer;
  border-bottom: 1px solid var(--line2);
}

.linky i { font-style: normal; font-family: var(--mono); font-size: 0.9em; color: var(--faint); }
.linky:hover { color: var(--accent); border-bottom-color: var(--accent); }
.linky:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.linky--off { color: var(--faint); border-bottom-style: dotted; }

/* --- staples and split items ---------------------------------------------- */

/* A staple is not a task and must never look like one: no checkbox, no rule of
   its own, no weight. It is the day happening to you, drawn as the gap it is.
   The rule that said exactly that also declared `border-bottom`, left over from
   when every row in a block carried one — so a staple was the only row on the
   page still drawing a line under itself, directly under "Lunch". The flex
   layout went too: these are grid rows now and it was dead the moment they
   became one. */
.staple,
.cont {
  padding: 0.35rem 0;
}

.staple__label,
.cont__label {
  font-size: 0.86rem;
  color: var(--faint);
}

/* A staple that belongs to the day rather than to any one block — the school
   run before the first thing, dinner after the last, lunch on the seam between
   two — is drawn in the gap BETWEEN the sections instead of inside one.

   It used to be handed to the block of the next item along, which put "Lunch"
   directly under the first heading on the page as its opening row, where a
   blind tester read it as one of the Job hunt tasks. Out here it has no heading
   over it and nothing above it in a list, so it can only read as what it is:
   the day interrupting, between one block and the next. No new vocabulary —
   the same row, in the gap the blocks already leave. */
/* The list inside the Staples section. Same tracks as every other list, so
   "Lunch" lands in the same columns as every other row. */
.daybreak {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
}

/* The heading is the section's name and nothing else — no count, no total,
   because a staple is never ticked and a "0/1" there would be inventing a
   score for the school run. It is otherwise a block heading like any other: it
   was set smaller and greyer on the reasoning that a block is work you chose
   and this is the day happening to you, which put a second kind of section
   heading on a page that has one. Two weights of heading is a hierarchy, and
   there is no hierarchy here — these are just the things at fixed times. */

.staple__label { font-style: italic; }
.staple .ctl { justify-self: end; }
.cont__label .icon { color: var(--line2); margin-right: 0.15rem; }

/* --faint, not --line2. These two carry real numbers — how long lunch takes,
   how much of a split item is still to come — and --line2 is the hairline
   colour: 1.6:1 on light paper, 2.2:1 on dark. That is not quiet, it is a
   duration nobody can read. The done rows below keep --line2 on purpose; a
   ticked thing receding is the product. */
/* The numbers on these two rows sit in the shared columns now, so they only
   need their voice set, not their place. --faint rather than --line2: these
   carry real numbers — how long lunch takes, how much of a split item is still
   to come — and --line2 is the hairline colour, 1.6:1 on light paper. That is
   not quiet, it is a duration nobody can read. */
.staple .check__min,
.cont .check__min,
.staple .check__at,
.cont .check__at { color: var(--faint); }

/* "12:20 AM" under a heading that says TUESDAY is a lie by omission. */
.check__at i {
  font-style: normal;
  font-size: 0.62em;
  vertical-align: 0.35em;
  color: var(--faint);
  margin-left: 0.15em;
}

/* --- block page: the same items, with what they mean ---------------------- */

.pitems {
  list-style: none;
  margin: 1.6rem 0 2.4rem;
  padding: 0;
  border-top: 1px solid var(--line);
}

.pitem {
  display: grid;
  grid-template-columns: 3.4rem 1fr;
  gap: 1.2rem;
  padding: 1.4rem 0;
  border-bottom: 1px solid var(--line);
  align-items: start;
}

/* The same rule as .item--done, and it has to be: "completed items recede"
   is a promise the whole app makes, not one the index makes alone. */
.pitem--done .pitem__head,
.pitem--done .pitem__what {
  color: var(--faint);
  text-decoration: line-through;
  text-decoration-color: var(--line2);
}

.check--bare { flex: none; width: auto; padding: 0.2rem; display: block; }
.check--bare .check__box { width: 2.6rem; height: 2.6rem; font-size: 1.4rem; }

.pitem__body { min-width: 0; }

.pitem__head {
  margin: 0 0 0.3rem;
  font-family: var(--display);
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.pitem__n {
  display: inline-block;
  min-width: 1.4em;
  color: var(--faint);
  font-variant-numeric: tabular-nums;
}

.pitem__what { margin: 0 0 0.4rem; font-size: 0.92rem; line-height: 1.5; }

.pitem__why { margin: 0; font-size: 0.82rem; line-height: 1.55; color: var(--muted); }

/* --- the fold -------------------------------------------------------------- */

/* Everything past the next couple of things is here but not shown. The page has
   to answer "what now" in one screen; the rest is one click away and never a
   different page. */
/* A folded row is hidden; an unfolded one is a GRID row, like every other row
   in the list. It said `flex` — the layout these rows had before the columns
   moved to the grid — so unfolding a block laid its rows out as flex
   containers inside a grid that was no longer placing them: name, description,
   duration and clock stacked vertically, one under another, in a column of
   ragged left edges. */
.is-more { display: none; }
.block--open .is-more { display: grid; }

.block--folded { display: none; }
/* Same again, and this one is the whole block: `display: block` replaced the
   grid that owns the six columns, so every row inside a revealed block fell
   back to one column. "Show 3 more areas" therefore produced three areas that
   looked nothing like the two above them. */
.blocks--open .block--folded { display: grid; }

.more-blocks { margin-top: 0.2rem; }

.empty {
  display: flex;
  padding: 0.55rem 0 0;
}

.more {
  display: flex;
  padding: 0.45rem 0 0.2rem;
}

.linky--more {
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
  border-bottom-color: transparent;
}

.linky--more:hover { color: var(--accent); border-bottom-color: var(--accent); }

/* --- the month ------------------------------------------------------------ */

.grid {
  display: grid;
  grid-template-columns: repeat(28, 1fr);
  gap: 4px;
  margin: 1rem 0 0.8rem;
}

/* A readout, not a control: no border, no rounding, flatter than anything you
   can click. Twenty-eight of these under a full-width rule used to stop short
   of it; now they stretch to meet it. */
.gcell {
  height: 1.15rem;
  /* An empty cell has to be visible as a cell. --panel2 against --bg is four
     points apart in dark, which reads as nothing at all. */
  background: color-mix(in srgb, var(--ink) 9%, var(--bg));
}

/* Three steps of one colour, so a partial day looks partial without a second hue. */
.gcell.g1 { background: color-mix(in srgb, var(--accent) 28%, var(--bg)); }
.gcell.g2 { background: color-mix(in srgb, var(--accent) 60%, var(--bg)); }
.gcell.g3 { background: var(--accent); }

/* --- the foot of the page --------------------------------------------------

   Everything below the last block used to be one undifferentiated grey: a
   control ("add a block"), a status line ("saved in this browser only"), the
   staples and their explanation, the month, the streak rule, and a paragraph
   of instructions — six things at one size in one colour, with nothing saying
   which was which.

   The fix is structure, not colour. Staples got the heading they were already
   pretending to have, so the page reads as two headed sections and then this:
   a rule, and under it the things that are documentation rather than today.
   The instructions are learnt in a week and re-read every morning after,
   and the account line is a fact about storage, not a thing to do.

   Not hidden. A paragraph explaining the only two gestures this page has must
   stay findable by someone who has forgotten them, and every fold here is
   state the browser has to keep — one more thing to be wrong after a refresh,
   bought to save four lines. Demoted, which is what "read once" earns. */
.colophon {
  margin-top: 2.6rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--line);
}

.colophon .note { font-size: 0.74rem; margin: 0 0 0.5rem; max-width: 74ch; }
.colophon .note:last-child { margin-bottom: 0; }

/* A tick that did not reach disk must never look like one that did.

   The sentence used to live in `content:` here, on a class the page set and
   never cleared. That made it one fixed sentence — "the server did not answer"
   — shown for a full browser, a refused document and a revoked token alike, and
   it stayed up for the rest of the session once anything had gone wrong. A
   banner that is always slightly wrong and never goes away is one people learn
   to read past, which is the same as never having shown it. So it is a real
   element now: boot.js writes what actually failed into it and empties it when
   that thing works. */
.trouble {
  margin: 0;
  padding: 0.6rem 1rem;
  background: var(--accent);
  color: var(--on-accent);
  font-family: var(--mono);
  font-size: 0.7rem;
  line-height: 1.5;
  letter-spacing: 0.04em;
  /* It has to survive scrolling: the failure is usually reported while the
     person is halfway down the page acting on something. */
  position: sticky;
  top: 0;
  z-index: 5;
}

/* --- taking it back ---------------------------------------------------------

   Fixed to the foot rather than inserted at the top of the page. A strip in the
   flow would shove the whole list down the moment something is deleted and let
   it snap back two minutes later, which is a second thing going wrong while
   somebody is working out what happened to the first.

   Quiet panel and hairline, never the accent: there is one loud colour here and
   it means "done". An undo offer is not an error and not an achievement.

   It is FIRST in the document, before [data-sheet], so anyone who has lost
   their place — focus dropped to <body> by a row that went away under it —
   reaches the offer with one Tab rather than fifty. boot.js also puts focus
   straight onto it after a delete, because the dialog hands focus back to a
   pen that morph has since given to the row underneath. The visual position
   and the tab position disagree on purpose; the offer is on screen either way. */
.undo {
  position: fixed;
  left: 50%;
  bottom: 1rem;
  transform: translateX(-50%);
  z-index: 10;
  display: flex;
  align-items: baseline;
  gap: 0.8rem;
  width: min(var(--sheet), calc(100vw - 2rem));
  padding: 0.6rem 0.9rem;
  border: 1px solid var(--line2);
  border-radius: 4px;
  background: var(--panel);
  box-shadow: 0 2px 12px var(--shadow-ink);
}

/* display:flex would otherwise beat the hidden attribute, and the bar would sit
   there empty all day. */
.undo[hidden] { display: none; }

.undo__say {
  flex: 1;
  color: var(--muted);
  font-size: 0.82rem;
  line-height: 1.45;
}

/* The salvage offer uses the undo bar's shape and sits at the other end of the
   page. Both can be on screen at once — delete something, then have the browser
   copy go — and the one about a month of history must not be the one covered. */
.salvage { top: 1rem; bottom: auto; }
.salvage__no { font-size: 0.82rem; color: var(--faint); }

.undo__go { font-size: 0.82rem; }
.undo__key { font-family: var(--mono); font-size: 0.72rem; color: var(--faint); }

/* --- the narrow page ------------------------------------------------------- */

/* Layout only. Everything about how the controls are REACHED is keyed on the
   input device instead, in the block below — a 400px window on a desktop still
   has a pointer, and a tablet at 800px still has none. */
@media (max-width: 520px) {
  .sheet { padding-bottom: 2rem; }
  .pitem { grid-template-columns: 3rem 1fr; gap: 0.9rem; }
  /* The block name stops being a column and becomes a line above its items. */
  .starter__block { grid-template-columns: 1fr; gap: 0.1rem; }
  .starter__items { gap: 0.05rem 0.9rem; }

  /* Twenty-eight cells across 340px is 10px each, so they are read as a strip
     and not as squares. A fixed width here used to force the row to 556px and
     push the whole page sideways — the one thing a phone must never do. */
  .grid { gap: 2px; }
  .gcell { height: 1.5rem; }

  .block__head { flex-wrap: wrap; row-gap: 0.1rem; }
  .check__sum { display: none; }

  /* The name gets whatever the fixed columns leave and wraps inside it. Left
     as `flex: none` it pushed the duration off the right edge instead. */
  .check__label { min-width: 0; }

  /* Tighter columns so the NAME keeps a line to itself. Every 5px taken off the
     gutters here is 5px that "Automated outreach" does not wrap on. The widths
     are the grid's now — these only set the voice. */
  .check__at { font-size: 0.68rem; }
}

/* "4:45 PM" broken across two lines is not a time, and it makes the row it is
   in taller than its neighbours. Nothing in either numeric column ever wraps —
   one rule now that staples and continuations use the same two cells. */
.check__at,
.check__min { white-space: nowrap; }

/* --- no pointer ------------------------------------------------------------ */

/* A phone has no hover and no tab key, so both existing ways into the reorder
   controls are gone. This is where the grip is turned on and where every target
   is grown to a fingertip. Keyed on the input device, with a width fallback for
   the browsers that lie about it; a desktop with a mouse never sees any of it.

   44px is the smallest thing a thumb hits reliably. */
@media (hover: none), (max-width: 520px) {
  .desk-only { display: none; }
  .touch-only { display: inline; }

  /* No pointer means no 300ms wait for a second tap, and no grey flash where
     this page has spent a lot of care not putting one. :active below is the
     feedback that replaces it. */
  button, a { touch-action: manipulation; -webkit-tap-highlight-color: transparent; }

  .check:active .check__box,
  .ctl__grip:active,
  .ctl__edit:active,
  .ctl__btn:active { color: var(--ink); border-color: var(--ink); }

  /* Rows are one height whatever is in them: a checkbox row, a staple, the tail
     of a split item. Uneven rows are what make a list feel like it is moving
     under you while you aim at it. */
  .check { min-height: 44px; }
  /* Padding on top of the minimum would make a staple taller than the item
     above it for no reason a reader could name. */
  .staple, .cont { min-height: 44px; padding-block: 0; }
  .block__title { padding: 0.55rem 0; }

  .theme {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    font-size: 1rem;
  }

  /* Every "show 3 more", every staple, "add a block": text-sized links that a
     finger cannot land on. They keep their look and gain a box.

     The rule under them has to become a real underline to come along: a
     border-bottom draws at the foot of the new 44px box, which leaves the mark
     floating a line below the word it belongs to. The gap replaces the space
     that flex layout eats between "Lunch" and "12:30 PM". */
  .linky,
  .note a { display: inline-flex; align-items: center; min-height: 44px; }

  .linky {
    gap: 0.35em;
    border-bottom: 0;
    text-decoration: underline;
    text-decoration-color: var(--line2);
    text-underline-offset: 4px;
  }

  /* Same reason as the linkys above, and it was missed: a link inside a note
     kept its border-bottom, which draws at the foot of the new 44px box and
     leaves a hairline stranded a line below the words it belongs to. It reads
     as a rule under nothing. */
  .note a {
    border-bottom: 0;
    text-decoration: underline;
    text-decoration-color: var(--line2);
    text-underline-offset: 4px;
  }

  .linky--off { text-decoration-style: dotted; }
  .linky--more { text-decoration: none; }
  .linky:hover,
  .linky:active { text-decoration-color: var(--accent); }

  .more { padding: 0; }

  /* Once every staple is an underlined block of its own the dots between them
     are doing no work, and a wrap that starts a line with a stranded "·" looks
     like a mistake. The dot was also the only thing keeping them apart, though:
     without it "School drop-off 8:45 AM" and "Lunch" ran together into one
     underlined word, because inline-flex eats the whitespace between them. */
  .staples__sep { display: none; }
  .staples .linky { margin-right: 0.9rem; }

  /* The masthead is the one place a box would show: padding on an inline link
     grows what you can hit without growing the line it sits on. */
  .mast a { padding: 0.95rem 0.3rem; }

  /* The controls stop hiding — there is nothing to reveal them with. One
     gutter width for every kind of row, so the durations to the left of it
     stay in a column. */
  .ctl,
  .staple .ctl { opacity: 1; width: 44px; gap: 0.35rem; }

  /* On a pointer these sit at --line2 and are invisible until hover lifts them
     to --ink, which is exactly the intent. Here nothing hovers: they are
     permanently on screen as the only way into moving or editing a row, and a
     control drawn at 1.6:1 against the paper is not a quiet one. */
  .ctl__edit,
  .ctl__btn,
  .ctl__grip { color: var(--faint); }
  .cont__min { padding-right: 44px; }
  /* A staple's row has a gap where an item's row has none, which left its
     duration nine pixels short of the column every other row keeps. */
  .staple__min { margin-right: -0.6rem; }

  /* A continuation is the tail of the item directly above it, and that item is
     never folded away while this row is shown — so its name is already legible
     on screen and one clipped line beats two squeezed ones. */
  .cont__label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .ctl__grip { display: inline-flex; }

  .ctl__edit,
  .ctl__grip {
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    padding: 0;
    font-size: 0.85rem;
  }

  /* One control in the gutter, not two: the grip is the way to BOTH of them.
     A staple or a token row has nothing to reorder, so there its pen is the
     control and stays where it is. */
  .ctl:has(.ctl__grip) .ctl__edit,
  .ctl:has(.ctl__grip) .ctl__arrows { display: none; }

  /* --- picked up --- */

  /* The row is held: it takes a second line and the three buttons it has always
     had unfold across it at full size. Panel grey, not the accent — the accent
     means "done" and spending it here would spend it everywhere. */
  .item.is-held,
  .block__head.is-held { flex-wrap: wrap; background: var(--panel2); }

  /* The row itself has to claim a whole line before the bar can be pushed onto
     the next one: left to flex from zero it simply squashes to nothing and the
     two share a line. */
  .item.is-held .check { flex-basis: 100%; }

  .is-held .ctl {
    width: 100%;
    gap: 0.35rem;
    padding: 0.15rem 0 0.5rem;
  }

  .is-held .ctl:has(.ctl__grip) .ctl__edit,
  .is-held .ctl:has(.ctl__grip) .ctl__arrows { display: flex; }

  /* Markup order is grip, edit, arrows, because that is the order they matter
     in when they are icons in a gutter. Held, they read left to right as the
     thing you came to do and then the way out. */
  .is-held .ctl__arrows { order: 1; flex: 2; flex-direction: row; gap: 0.35rem; }
  .is-held .ctl__edit { order: 2; }
  .is-held .ctl__grip { order: 3; }

  .is-held .ctl__edit,
  .is-held .ctl__btn,
  .is-held .ctl__grip {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.3rem;
    flex: 1;
    min-height: 44px;
    padding: 0;
    border-color: var(--line2);
    border-radius: 3px;
    background: var(--panel);
    color: var(--muted);
    font-size: 0.66rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
  }

  /* The words are here rather than in the markup because the buttons already
     carry the same thing as an aria-label, and a visible copy would have to be
     hidden again on every screen that draws them as icons. */
  .is-held .ctl__btn[data-dir='-1']::after { content: 'Up'; }
  .is-held .ctl__btn[data-dir='1']::after { content: 'Down'; }
  .is-held .ctl__edit::after { content: 'Edit'; }
  .is-held .ctl__grip::after { content: 'Done'; }
  .is-held .ctl__grip i { display: none; }
}

/* --- the door and settings ------------------------------------------------- */

.gate {
  max-width: 26rem;
  margin: 2rem 0 1.4rem;
  padding-top: 1.2rem;
  border-top: 1px solid var(--line);
}

.gate--inline { margin-top: 1rem; padding-top: 0; border-top: 0; }

.gate__foot {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
  margin-top: 1rem;
}

.fresh code {
  display: block;
  margin-top: 0.4rem;
  padding: 0.5rem 0.6rem;
  background: var(--panel2);
  border-radius: 3px;
  word-break: break-all;
  color: var(--ink);
}

/* Recovery codes are read off a screen and copied onto paper, so they are set
   in the mono face at a size that survives that. Numbered, because "I typed
   the fourth one" is how someone works out which they have already spent. */
.codes {
  margin: 0.6rem 0 0;
  padding-left: 1.6rem;
  color: var(--faint);
}

.codes li { margin: 0.15rem 0; }

.codes code {
  font-family: var(--mono);
  font-size: 1rem;
  letter-spacing: 0.04em;
  color: var(--ink);
  user-select: all;
}

/* --- the editor ------------------------------------------------------------ */

.modal {
  width: min(34rem, calc(100vw - 2rem));
  padding: 0;
  border: 1px solid var(--line2);
  border-radius: 4px;
  background: var(--panel);
  color: var(--ink);
}

.modal::backdrop { background: var(--scrim); }

.modal__form { padding: 1.4rem 1.5rem 1.2rem; }

.modal__title {
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0 0 1rem;
  padding-bottom: 0.4rem;
  border-bottom: 1px solid var(--line);
}

/* Label left, control right, on one grid down the form — the same ledger
   alignment as the page behind it. */
.f {
  display: grid;
  grid-template-columns: 5.5rem 1fr;
  gap: 0.9rem;
  align-items: center;
  margin: 0 0 0.7rem;
}

.f--tall { align-items: start; }

.f__k {
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
  padding-top: 0.15rem;
}

.f__v {
  width: 100%;
  font-family: var(--sans);
  font-size: 0.88rem;
  line-height: 1.5;
  color: var(--ink);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 0.4rem 0.55rem;
}

.f__v:focus-visible { outline: 2px solid var(--ink); outline-offset: -1px; border-color: transparent; }
.f__v:user-invalid { border-color: var(--accent); }

/* The editor's own refusals, which are not the browser's: the name field is no
   longer `required`, because that check fires before the submit handler and
   drew exactly this border with no sentence anywhere. boot.js sets the
   attribute and writes the words into [data-err] at the same moment, so the
   mark and the reason always arrive together. */
.f__v[aria-invalid='true'],
.day[aria-invalid='true'] { border-color: var(--accent); }

.f__v--time { width: auto; font-family: var(--mono); font-size: 0.8rem; }

textarea.f__v { resize: vertical; }

/* Left with the platform's own arrow and popup. Drawing a caret would mean a
   second ink somewhere, and `color-scheme` in tokens.css already makes the
   native one follow the theme. */
select.f__v { cursor: pointer; }

.f__note {
  grid-column: 2;
  color: var(--faint);
  font-size: 0.76rem;
  line-height: 1.5;
  margin: -0.3rem 0 0.9rem 6.4rem;
}

.days { display: flex; gap: 3px; }

.day {
  appearance: none;
  width: 1.8rem;
  height: 1.8rem;
  border: 1px solid var(--line);
  border-radius: 3px;
  background: var(--bg);
  font-family: var(--mono);
  font-size: 0.68rem;
  color: var(--faint);
  cursor: pointer;
}

.day[aria-pressed='true'] { border-color: var(--ink); background: var(--ink); color: var(--bg); }
.day:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.modal__err {
  color: var(--accent);
  font-size: 0.8rem;
  margin: 0.6rem 0 0;
}

.modal__foot {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 1.2rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--line);
}

.modal__gap { flex: 1; }

.btn {
  appearance: none;
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 0.45rem 0.8rem;
  border: 1px solid var(--line2);
  border-radius: 3px;
  background: var(--panel);
  color: var(--muted);
  cursor: pointer;
}

.btn:hover { color: var(--ink); border-color: var(--ink); }
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.btn--go { background: var(--ink); border-color: var(--ink); color: var(--bg); }
.btn--go:hover { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }

.btn--quiet { border-color: transparent; color: var(--faint); }
.btn--quiet:hover { color: var(--accent); border-color: var(--accent); }
.btn--armed { color: var(--on-accent); background: var(--accent); border-color: var(--accent); }
.btn--armed:hover { color: var(--on-accent); }

[data-block-only] .btn { margin-top: 0.2rem; }

@media (max-width: 520px) {
  .f { grid-template-columns: 1fr; gap: 0.3rem; }
  .f__note { grid-column: 1; margin-left: 0; }
}

/* The same fingertip rule as the page behind it. Separate from the block above
   because these three are declared further down the file and would otherwise
   simply win. */
@media (hover: none), (max-width: 520px) {
  .btn { min-height: 44px; padding: 0.45rem 0.9rem; }

  /* Seven day toggles sharing the width evenly beats seven 29px squares that
     all mean something different. */
  .days { gap: 4px; }
  .day { flex: 1; width: auto; height: 2.75rem; font-size: 0.8rem; }

  /* Under 16px iOS zooms the whole page in on focus and does not zoom back. */
  .f__v { font-size: 1rem; padding: 0.6rem 0.55rem; }
  .f__v--time { font-size: 1rem; }
}

/* --- the row as a control -------------------------------------------------
   Grafted from the branch that made the row a button, onto the layout above.
   The two were designed against each other: that branch stretched nothing,
   this one stretched the NAME across the row as the link. The name is not a
   link any more, so that overlay is gone (see below) and the button is the
   full-row target instead. The cadence keeps its own box above it. */
/* The row is the button, and the button is nothing to look at.

   It carries no box and no tick. A checkbox on an area of somebody's life says
   "complete this", which is the checklist framing this product exists to
   refuse — an area is never done, it is only more or less recently tended. So
   the control is the row itself: an invisible overlay across all of it, and
   the confirmation is the number changing to "today" and the row receding the
   way a ticked item recedes. That is the language the rest of the page already
   speaks, and it costs no ink.

   Underneath everything (z-index 0) so the cadence and the link keep their own
   hit boxes; those are lifted above it. */
.drift__row > .tend {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
.drift__row > .tend:focus-visible { outline: 2px solid var(--accent); outline-offset: -3px; border-radius: 3px; }
/* A press has to be felt before the redraw lands, or the row looks inert. */
.drift__row:hover .drift__name { border-bottom-color: var(--line2); }



/* The way to what the area's items say, which is the other half of what this
   row was before it became a button. Quiet, because it is the second thing you
   would ever want from this row and the first is the press. */
.drift__menu {
  flex: none;
  padding: 0.2rem 0;
  font-size: 0.66rem;
  letter-spacing: 0.02em;
  color: var(--line2);
  text-decoration: none;
  border-bottom: 1px solid transparent;
}
.drift__menu:hover, .drift__menu:focus-visible { color: var(--muted); }


/* The whole row, not just the box: a ring around a 1.5rem square at the far
   left of a row-wide control tells a keyboard user nothing about how much of
   the page they are about to press. */
.tend:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* Same box, same accent, same meaning as a ticked item — "done", and nothing
   else in this app is allowed that colour. An area tended today is done today,
   which is exactly what the tick in it says. */
.tend__box {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  border: 1px solid var(--line2);
  border-radius: 3px;
  background: var(--panel);
  font-size: 0.8rem;
  line-height: 1;
  color: var(--accent);
}


/* Same box, same accent, same meaning as a ticked item — "done", and nothing
   else in this app is allowed that colour. An area tended today is done today,
   which is exactly what the tick in it says. */


.tend--solo:hover .tend__box { border-color: var(--ink); }
.tend--solo[aria-pressed='true'] .tend__box { border-color: var(--accent); background: var(--panel2); }

.tend--solo {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.7rem 1rem;
  border: 1px solid var(--line2);
  border-radius: 4px;
  background: var(--panel);
  color: var(--ink);
}


.tend__say { font-size: 0.9rem; }

.drift__name {
  font-size: 1rem;
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid transparent;
}


/* The same control on the block's own page, where there is no row to be and
   the title is already the heading above it. */
.tendbar { margin: 1rem 0 0; }
.tend--solo {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.7rem 1rem;
  border: 1px solid var(--line2);
  border-radius: 4px;
  background: var(--panel);
  color: var(--ink);
}


/* The way to what the area's items say, which is the other half of what this
   row was before it became a button. Quiet, because it is the second thing you
   would ever want from this row and the first is the press. */
.drift__menu {
  flex: none;
  align-self: center;
  padding: 0.6rem 0;
  font-size: 0.72rem;
  color: var(--faint);
  text-decoration: none;
  border-bottom: 1px solid transparent;
}


.drift__menu:hover { color: var(--ink); border-bottom-color: var(--line2); }

/* The same control on the block's own page, where there is no row to be and
   the title is already the heading above it. */
.tendbar { margin: 1rem 0 0; }
.tend--solo {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.7rem 1rem;
  border: 1px solid var(--line2);
  border-radius: 4px;
  background: var(--panel);
  color: var(--ink);
}



/* --- two views, one page --------------------------------------------------

   Today is the working surface and the default. "What's drifting" is the
   reference, one press away. The tabs are words rather than a control with
   chrome: this is a choice made rarely and read constantly, and a segmented
   button would be the loudest thing on a page whose whole argument is that it
   is quiet. The pressed one is simply the one in ink. */
/* The date and the tabs on one line: what day it is on the left, where you are
   on the right. They were stacked, and with the status line under them that
   made three rows of nearly the same weight with no hierarchy between them —
   the eye had to read all three to find out which one mattered. */
.topline {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.6rem 1.4rem;
}

.views {
  display: flex;
  gap: 1.2rem;
  margin: 0;
  padding: 0;
}
/* The underline sat 8px under the word, far enough to read as a stray dash
   rather than as a mark on the tab it belongs to. */
.views__tab {
  padding: 0 0 0.15rem;
  border: 0;
  border-bottom: 1px solid transparent;
  background: none;
  font: inherit;
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  color: var(--faint);
  cursor: pointer;
}
.views__tab:hover { color: var(--muted); }
.views__tab[aria-pressed='true'] { color: var(--ink); border-bottom-color: var(--ink); }
.views__tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }


/* One quiet row of words where a screen and a half of reference used to sit. */
.tucks {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem;
  margin: 2.2rem 0 0;
  font-size: 0.72rem;
}
.tucks a { color: var(--faint); text-decoration: none; border-bottom: 1px solid transparent; }
.tucks a:hover, .tucks a:focus-visible { color: var(--muted); border-bottom-color: var(--line2); }
.tucks__sep { color: var(--line2); }


/* Narrow: the description column goes rather than everything squeezing.
   Six columns — box, name, description, duration, clock, controls — do not fit
   375px without the numerals colliding, and of the six the description is the
   only one whose absence costs nothing structural. The name is the row and the
   two numerals are why anybody is reading it; the sentence is on the block
   page. Above this width all six fit and the description is the reason the
   grid exists. */
@media (max-width: 33rem) {
  /* On the BLOCK, which is where the tracks are defined now. Re-declaring
     them on .items would replace its `subgrid` and cut the list loose from the
     heading above it, which is the alignment this all exists for. */
  /* On .blocks, which owns the tracks. Re-declaring them lower down would
     replace a `subgrid` and cut that container loose from the rest. */
  .blocks { grid-template-columns: auto auto 0 4.4rem 5.2rem auto; column-gap: 0.7rem; }
  .check__sum { display: none; }
}


/* Each cell names its own column.
   -----------------------------------------------------------------------
   Without this the cells are placed in source order, and hiding one shifts
   every cell after it up a track: the description was set to display: none on
   a phone and the duration slid into its slot, landing under the clock —
   "20m1:35 AM". Naming the column makes hiding a cell leave a hole rather
   than pull the row apart. */
.check__box   { grid-column: 1; }
.check__label { grid-column: 2; }
.check__sum   { grid-column: 3; }
/* justify-self rather than text-align. These two cells sit in the same tracks
   but not the same boxes — the item's are inside the row button, the heading's
   are inside its own wrapper — and a stretched box measured sixteen pixels
   wider on one than the other, so the digits did not stack. Pinning the cell
   to the END of its track makes the edge the track's, not the box's. */
.check__min   { grid-column: 4; justify-self: end; }
.check__at    { grid-column: 5; justify-self: end; }
.block__left  { justify-self: end; }
.item > .ctl  { grid-column: 6; }


/* --- where the clock has got to ------------------------------------------- */

/* The current time, on the heading line of the block that holds the next thing
   not done — after the total, in the clock column, above the times it is meant
   to be compared against.
   -----------------------------------------------------------------------
   It was a row of its own above that item. Two things were wrong with that: it
   cost a whole row of vertical space to say one number, and the heading beside
   it ended at "1h 45m" with an empty cell where a time should be, so the
   header looked cut off and the time below it looked orphaned.

   In the header cell it costs no row at all, it completes a line that was
   visibly missing its last column, and it sits directly above the column of
   projections it is meant to be compared against. In ink, while those stay
   muted: that is the whole difference between a fact and a projection, and it
   needs no label saying "now".

   Once per page, because only one block holds the next undone thing. */
.block__now {
  grid-column: 5;
  justify-self: end;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
  /* In ink while the projections under it stay muted. That is the whole
     difference between a fact and a guess, and it needs no label saying now. */
  color: var(--ink);
  white-space: nowrap;
}



/* The state of the data, in the masthead. One quiet word: it is read on the way
   in, answered once, and then not thought about again. The sentence behind it
   is the title. */
.mast__state {
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--faint);
  cursor: default;
}
/* Only the two that mean something is wrong get any weight at all, and even
   then it is ink rather than colour: this page has one accent and it means
   "done". */
.mast__state[data-state="offline"],
.mast__state[data-state="stalled"] { color: var(--ink); }

/* The two foot rows, one line: what else is on the page on the left, what is
   behind the page on the right. They were stacked, which made four words of
   navigation look like two separate sections. */
.footrow {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.6rem 1.4rem;
  margin-top: 1.8rem;
}
.footrow .adds, .footrow .tucks { margin: 0; }


/* --- the foot panels ------------------------------------------------------ */

/* Staples, the month of history, how this works, and the drift list's own note.
   -----------------------------------------------------------------------
   Styled as the editor dialog is styled, because there is no reason for this
   app to have two kinds of dialog. The editor's padding lives on its <form>,
   which this has not got, so the first version had text against the glass on
   every edge.

   The heading takes the same small mono capitals the editor's title takes. The
   h2 would otherwise arrive wearing the SECTION-rule styling it has in the
   sheet — a hairline the full width of the page — which inside a 34rem box
   reads as a divider slammed under the first line. */
.panel__body {
  padding: 1.4rem 1.5rem 0.2rem;
}

.panel__body > h2 {
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--faint);
  margin: 0 0 1.1rem;
  padding: 0;
  border: 0;
}

.panel__body > .note { margin: 0.9rem 0 0; max-width: none; }
.panel__body > :last-child { margin-bottom: 0; }

/* The staples line inside the panel: it is the one panel with controls in it,
   and they are the same quiet links they are everywhere else. */
.panel__body .staples { margin: 0; line-height: 2.1; }

/* The grid gets room to be a grid rather than being squeezed by the padding. */
.panel__body .grid { margin: 0; }

/* Close is a word, not a bordered box. A dialog whose only control is a button
   with a border and a background makes the button the loudest thing in a panel
   that exists to be read — and this one can also be shut with Escape or by
   pressing the page behind it, so it is the third way out rather than the
   only one. */
.modal__foot {
  display: flex;
  justify-content: flex-end;
  padding: 1rem 1.5rem 1.2rem;
}
.modal__foot button {
  appearance: none;
  border: 0;
  background: none;
  padding: 0;
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--faint);
  border-bottom: 1px solid var(--line);
  cursor: pointer;
}
.modal__foot button:hover { color: var(--muted); border-bottom-color: var(--line2); }
.modal__foot button:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* A word in the foot row, set exactly like the links beside it — the fact that
   some open a dialog and one is an ordinary link is not a difference worth
   drawing. */
.linky--panel {
  font-size: inherit;
  color: var(--faint);
  border-bottom: 1px solid transparent;
}
.linky--panel:hover, .linky--panel:focus-visible { color: var(--muted); border-bottom-color: var(--line2); }


/* The streak, as a fact and then the rule behind it.
   -----------------------------------------------------------------------
   One paragraph ran the two together, so the number — the only part that
   changes from one day to the next — had to be found inside a sentence about
   policy. The fact gets the line and the weight; the rule sits under it,
   smaller, where something read once belongs. */
.streak__said {
  display: block;
  color: var(--ink);
  font-weight: 600;
}
.streak__rule {
  display: block;
  margin-top: 0.3rem;
  font-size: 0.74rem;
  color: var(--faint);
}


/* The staples, down the panel rather than across it.
   -----------------------------------------------------------------------
   They were a middot-separated run — "School drop-off 8:45 AM · Lunch 12:30 PM
   · Dinner 6:30 PM" — which reads left to right, the one direction a day does
   not go. These are moments you pass through a day, so they are a column: name
   on the left, time on the right, in time order, the way every other list of
   times in this app is set. */
.stapled__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: 1.4rem;
}
.stapled {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
  align-items: baseline;
  padding: 0.45rem 0.5rem;
  margin: 0 -0.5rem;
  border-bottom: 1px solid var(--line);
}
.stapled:last-child { border-bottom: 0; }
/* No underline on hover, and no accent. The row already carries a line — the
   separator between it and the next staple — so an underline under the name
   put two rules a few pixels apart and neither meant anything. And .linky
   turns accent on hover, which is right for a link in a sentence and wrong
   here: the one accent in this app means "done", and a staple going red under
   the pointer reads as an alarm about the school run.

   The row lifts instead. It is the same surface the panel is made of, one step
   up, which says "this is the thing you are pointing at" without saying
   anything else. */
.stapled .linky {
  border-bottom: 0;
  text-align: left;
  color: var(--ink);
}
.stapled:hover { background: var(--panel2); }
.stapled:hover .linky,
.stapled .linky:hover { color: var(--ink); }
.stapled .linky:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.stapled__at {
  font-family: var(--mono);
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
  color: var(--faint);
  white-space: nowrap;
}
/* A staple that does not apply today: still listed, because this is where you
   go to edit it, but plainly not part of today. */
.stapled--off .linky { color: var(--faint); }
.stapled--off .stapled__at i { font-style: normal; color: var(--line2); }


/* --- the two figures on /about -------------------------------------------- */

/* A page that argues about what you can SEE at a glance has to show the glance.
   Both of these carry an argument a paragraph is bad at: a month with a hole in
   it, and the list that finds the hole. Neither is decoration and neither is a
   screenshot — the second is the real row markup in miniature, so it cannot go
   stale the day the row changes. */
.fig {
  margin: 1.6rem 0 0;
  padding: 1.2rem 1.3rem 1rem;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--panel);
  max-width: 60ch;
}

.fig__svg { display: block; width: 100%; height: auto; color: var(--ink); }
.fig__svg text { font-family: var(--mono); }

.fig__cap {
  margin: 0.9rem 0 0;
  font-size: 0.76rem;
  line-height: 1.5;
  color: var(--faint);
}

/* The drift list, small. Same three columns, same greying, same right edge, so
   somebody who reads this and then opens the app recognises it. */
.mock {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: 1.2rem;
}
.mock__row {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
  align-items: baseline;
  padding: 0.45rem 0;
  border-bottom: 1px solid var(--line);
}
.mock__row:last-child { border-bottom: 0; }
.mock__name { font-size: 0.92rem; color: var(--ink); }
.mock__cad {
  grid-row: 2;
  grid-column: 1;
  font-size: 0.68rem;
  color: var(--faint);
}
.mock__since {
  grid-row: 1 / 3;
  grid-column: 2;
  align-self: center;
  justify-self: end;
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.mock__since b { font-size: 1.3rem; font-weight: 600; color: var(--ink); }
.mock__since i { font-style: normal; font-size: 0.66rem; color: var(--faint); }
/* Inside its gap: recede, exactly as the real row does. */
.mock__row--ok .mock__name { color: var(--muted); }
.mock__row--ok .mock__since b { color: var(--faint); font-weight: 400; }

/* The three claims. Figure one side, sentence the other, alternating, so the
   eye has to cross the page three times instead of running down a list of
   seven and taking in none of them.

   Below 44rem the two-column swap stops meaning anything, so the drawing goes
   above its sentence in every section and the `order` rule is not applied at
   all — an alternating stack reads as a mistake, not a rhythm. */
.show {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1rem;
  margin: 1.8rem 0 0;
  max-width: 60ch;
}

.show__art {
  margin: 0;
  padding: 1rem 1.1rem;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--panel);
}

.show__art svg { display: block; width: 100%; height: auto; color: var(--ink); }
.show__art svg text { font-family: var(--mono); }

.show__say { align-self: center; }

/* The same small mono capitals the section rules use, which is what a label
   looks like everywhere else in this app. */
.show__n {
  margin: 0 0 0.35rem;
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
}

.show__no {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 600;
  line-height: 1.3;
}

.show__why {
  margin: 0.45rem 0 0;
  font-size: 0.86rem;
  line-height: 1.55;
  color: var(--muted);
}

@media (min-width: 44rem) {
  .show {
    grid-template-columns: 1fr 1fr;
    gap: 1.6rem;
    align-items: center;
  }

  .show--flip .show__art { order: 2; }
}

/* The claims that did not earn a drawing, as one paragraph. A second list under
   the three would put back the thing the three were built to replace. */
.rest { max-width: 60ch; margin-top: 1.8rem; }

/* The shape, then the shape filled in. Same card twice so the eye maps one onto
   the other without being told to; the left one is greyed because it is a
   diagram of a block and the right one is a block. */
.struct__pair {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.9rem;
  align-items: center;
}

.struct__card {
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 0.7rem 0.85rem;
  background: var(--bg);
}

.struct__card--ghost { color: var(--faint); font-family: var(--mono); }
.struct__card--ghost .struct__name { font-weight: 500; font-size: 0.85rem; }

.struct__name {
  margin: 0;
  font-size: 0.95rem;
  font-weight: 600;
  line-height: 1.3;
}

.struct__rows { list-style: none; margin: 0.55rem 0 0; padding: 0; }

.struct__row {
  display: flex;
  align-items: baseline;
  gap: 0.55rem;
  padding: 0.28rem 0;
  font-size: 0.84rem;
}

.struct__row::before {
  content: "";
  width: 0.72rem;
  height: 0.72rem;
  flex: none;
  border: 1px solid var(--line);
  border-radius: 2px;
}

.struct__row span { flex: 1 1 auto; min-width: 0; }

.struct__row i {
  margin-left: auto;
  font-style: normal;
  font-family: var(--mono);
  font-size: 0.74rem;
  color: var(--faint);
}

.struct__to {
  display: block;
  text-align: center;
  font-size: 1.1rem;
  color: var(--faint);
  transform: rotate(90deg);
}

@media (min-width: 34rem) {
  .struct__pair { grid-template-columns: 1fr auto 1fr; gap: 1rem; }
  .struct__to { transform: none; }
}

/* The second footer line: a copyright and the pages you leave the app for.
   Separated from the panels above it because a link that navigates and a button
   that opens a dialog were sitting on one line looking identical, and the only
   way to tell them apart was to press one.

   The whole line is faded into the paper as one band rather than the copyright
   alone. Half-fading one word of a line reads as a rendering fault; fading the
   line reads as a decision, and the decision is that this row is boilerplate —
   nobody arrives here to read it, and the row above is the one being used.
   Hover and keyboard focus bring a link back to full --faint, so anybody
   actually going to Privacy gets a legible target the moment they aim at it. */
.footrow--legal { margin-top: 0.9rem; }

/* Not mono: at 0.72rem it draws more eye than the sans links beside it, which
   is a strange contest for a copyright to win.

   And below --faint, which the tokens file otherwise treats as the floor. That
   floor is about text somebody has to read — section headings, durations, every
   note on the page. This is the one string here that nobody ever needs to read
   and that has to be present anyway, so it is faded toward the paper rather
   than given a literal of its own. The plain --faint declaration above it is
   what a browser without color-mix gets. */
.copy { margin: 0; font-size: 0.72rem; }

.copy, .footrow--legal .tucks a {
  color: var(--faint);
  color: color-mix(in srgb, var(--faint) 55%, var(--bg));
}

.footrow--legal .tucks a:hover, .footrow--legal .tucks a:focus-visible {
  color: var(--faint);
}

.footrow--legal .tucks__sep { color: color-mix(in srgb, var(--line2) 60%, var(--bg)); }

/* Settings, in four groups. The tabs are the day's own tabs — same control,
   same underline — with room under them, because inside the dialog they are a
   header rather than a thing beside a heading. */
.views--tabs {
  margin: 1.4rem 0 0;
  padding-bottom: 0.2rem;
  border-bottom: 1px solid var(--line);
}

.tabbed > h2:first-child { margin-top: 1.2rem; }

/* The /api field list: a name and what it means, on one line each. */
.fields { list-style: none; margin: 0.9rem 0 0; padding: 0; max-width: 60ch; }

.fields li {
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem 0.7rem;
  padding: 0.34rem 0;
  border-top: 1px solid var(--line);
  font-size: 0.84rem;
}

.fields li span { flex: 1 1 22ch; color: var(--muted); }

/* The reading pages — about, api, terms, privacy, templates — as one centred
   column rather than a ragged left edge against an empty right half.

   The sheet is 780px and these pages' widest element was 605px, so everything
   sat hard left with 150px of nothing beside it. One width for the whole page
   and the children take it, instead of four different max-widths stacked down
   the same left margin: the measure is a touch wider than the 60ch the notes
   used to hold, and a shared edge is worth more here than the last few
   characters of it. */
.prose {
  max-width: 38rem;
  margin-inline: auto;
}

.prose .note,
.prose .lede,
.prose .fig,
.prose .show,
.prose .rest,
.prose .fields,
.prose .starters { max-width: none; }

/* Terms and privacy, as rows you can answer a question from.

   Both pages used to be four headings over four paragraphs, and nobody reads
   either one top to bottom — they arrive wanting one answer (what do you keep,
   what happens if I delete) and a wall of prose makes them hunt for it. The
   label takes the same small mono capitals the section rules take, so the left
   column scans as labels rather than as more sentences. */
.facts { margin: 1.4rem 0 0; }

.facts__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.25rem 1.4rem;
  padding: 0.85rem 0;
  border-top: 1px solid var(--line);
}

.facts dt {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
  padding-top: 0.2rem;
}

.facts dd {
  margin: 0;
  font-size: 0.86rem;
  line-height: 1.6;
  color: var(--muted);
}

@media (min-width: 34rem) {
  .facts__row { grid-template-columns: 7.5rem minmax(0, 1fr); }
}

/* The code variant of a fact row: the left column is something you type, so it
   keeps its case and its monospace instead of taking the small-capitals a
   label takes. Wider, because an endpoint is longer than a label. */
.facts--code dt {
  font-size: 0.78rem;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
  padding-top: 0.05rem;
}

@media (min-width: 34rem) {
  .facts--code .facts__row { grid-template-columns: 8.5rem minmax(0, 1fr); }
}

/* A code sample as the art of a .show section. The border, the panel and the
   alternating column are the figure's; the padding is the pre's, so the code
   can scroll under the label instead of taking the page sideways with it. */
.show__art--code { padding: 0; overflow: hidden; }

.sample__k {
  padding: 0.5rem 0.85rem;
  border-bottom: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 0.64rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
}

.sample__code {
  margin: 0;
  padding: 0.85rem;
  overflow-x: auto;
  font-family: var(--mono);
  font-size: 0.72rem;
  line-height: 1.65;
  color: var(--ink);
  tab-size: 2;
}

.sample__code code { font: inherit; color: inherit; }

@media (min-width: 40rem) {
  .samples { grid-template-columns: 1fr 1fr; align-items: start; }
}

/* --- /api, as documentation ------------------------------------------------

   Not .prose. The reading pages are one narrow centred column because they are
   read start to finish; this one is looked things up in, so it takes the full
   sheet and spends the left of it on a nav that says where you are. */
.docs { max-width: none; }

.docs__body {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1.4rem;
  margin-top: 1.6rem;
}

.docs__nav ul { list-style: none; margin: 0; padding: 0; }
.docs__nav li { margin: 0; }

.docs__nav a {
  display: block;
  padding: 0.3rem 0 0.3rem 0.7rem;
  border-left: 2px solid var(--line);
  font-size: 0.78rem;
  line-height: 1.4;
  color: var(--faint);
  text-decoration: none;
}

.docs__nav a:hover { color: var(--muted); }

/* Set by the spy in boot.js, re-asserted after every morph. */
.docs__nav a[aria-current='true'] {
  border-left-color: var(--ink);
  color: var(--ink);
}

.docs__nav a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.doc {
  /* So a jump from the nav does not put the heading against the top edge. */
  scroll-margin-top: 1.5rem;
  margin: 0 0 2.4rem;
}

.doc__h {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem;
}

.doc__h code {
  font-size: 0.9rem;
  color: var(--ink);
  text-transform: none;
  letter-spacing: 0;
}

.doc__verb {
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.1em;
  padding: 0.15rem 0.4rem;
  border: 1px solid var(--line2);
  border-radius: 3px;
  color: var(--faint);
}

.doc .note, .doc .facts { max-width: 60ch; }

@media (min-width: 46rem) {
  .docs__body { grid-template-columns: 11rem minmax(0, 1fr); gap: 2.2rem; }

  .docs__nav {
    position: sticky;
    top: 1.5rem;
    align-self: start;
  }
}

/* --- code samples ---------------------------------------------------------- */

.sample {
  margin: 1rem 0 0;
  min-width: 0;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--panel);
  overflow: hidden;
  max-width: 60ch;
}

.tok-key { color: var(--tok-key); }
.tok-str { color: var(--tok-str); }
.tok-num { color: var(--tok-num); }
.tok-lit { color: var(--tok-lit); }
.tok-var { color: var(--tok-var); }
.tok-url { color: var(--tok-url); }
.tok-flag { color: var(--tok-flag); }
.tok-cmd { color: var(--tok-cmd); font-weight: 600; }
.tok-punct { color: var(--tok-punct); }

/* The starter deck: four cards in a panel, one at a time.

   The cards keep their own .starter styling — this is the same card the
   /templates page prints, not a second design of it — so all this adds is the
   hiding and the row of arrows under it. */
.deck { margin: 1.3rem 0 0; }
.deck .starter { border-top: 0; padding-top: 0; }

.deck__nav {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 1.2rem;
  margin: 1.2rem 0 0;
  padding-top: 0.9rem;
  border-top: 1px solid var(--line);
}

.deck__nav .linky {
  font-size: 1rem;
  line-height: 1;
  padding: 0.2rem 0.5rem;
}

.deck__at {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  color: var(--faint);
  min-width: 5ch;
  text-align: center;
}
