/* Journal styles.
   Every size, space, radius, shadow and duration comes from the design tokens in src/tokens.ts, and
   every color from the active palette in src/themes.ts; both are served at /tokens.css.
   tests/design.test.ts fails on a raw value here. Three breakpoints: 40rem (roomy), 48rem (wide: the
   top bar) and 64rem (desktop: the workspace).
   Layout spacing: a parent spaces its children (.page, .stack, flex gaps); components don't set
   outer margins. A value that must be exact carries an "exact" comment saying why. */

/* Values derived from the tokens */
:root {
  /* Things fixed to the bottom of a phone screen (the nav bar, sheets) follow Chrome's edge-to-edge
     guidance: Chrome on Android draws the page under the gesture bar once its bottom "chin" retracts,
     and env(safe-area-inset-bottom) grows as it does. So they reserve the most it can be as padding
     and slide down with `bottom` while it's smaller; their size never changes. Using the inset itself
     as padding leaves an empty band in the bar when Chrome keeps the chin.
     https://developer.chrome.com/docs/css-ui/edge-to-edge
     Other browsers (Firefox, Safari) have no safe-area-max-inset-bottom; there the most is the inset
     itself, so the bar simply sits at the bottom with the inset as padding. (A guessed maximum would
     push the bar up by that much, with an empty band under it.) */
  --safe-bottom-max: env(safe-area-max-inset-bottom, env(safe-area-inset-bottom, 0px)); /* exact: no inset is 0 */
  --page-top: var(--space-5);
  --page-bottom: calc(var(--tabbar-height) + var(--safe-bottom-max) + var(--space-6)); /* clears the phone bottom bar */
  --panebar-height: calc(var(--tap) + 2 * var(--space-2) + 1px); /* a button, its padding, the rule */
}
@media (min-width: 48rem) {
  :root { --page-bottom: var(--space-12); }
}

@view-transition { navigation: auto; }

/* ---------- Base ---------- */
*, *::before, *::after { box-sizing: border-box; }
/* Nothing may make the page wider than the screen: Firefox on Android zooms the whole tab out to fit
   it, and misplaces the bottom bar while zoomed. Long words and links wrap (below); this is the
   backstop. */
html { -webkit-text-size-adjust: 100%; overflow-x: hidden; }
body {
  margin: 0;
  background-color: var(--desk);
  color: var(--ink);
  font: var(--text-body) / var(--leading-body) var(--serif);
  min-height: 100dvh;
}
a { color: var(--accent); text-underline-offset: 0.18em; text-decoration-thickness: 1px; }
mark { background-color: var(--mark); color: var(--mark-ink); padding: 0 0.12em; border-radius: 0.15em; }
img, svg { display: block; max-width: 100%; }
h1, h2, h3 { line-height: var(--leading-tight); font-weight: 600; text-wrap: balance; }
/* What you write (titles, excerpts, entries) may hold a long link or word: it breaks rather than
   overflowing, even in a row beside something else (a title and its time). */
:where(h1, h2, h3, .t, .x, .meta, .prose) { overflow-wrap: anywhere; }
p { text-wrap: pretty; }
summary { cursor: pointer; }

.muted { color: var(--muted); }
.small { font-size: var(--text-sm); }
/* For screen readers only. A month's title is too, on phones, where the list says which month it is. */
.visually-hidden, .monthtitle {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
/* Inline text for narrow screens only, or roomy ones only: a long date and its shorter phone version. */
.roomy-only { display: none; }
@media (min-width: 40rem) {
  .roomy-only { display: inline; }
  .narrow-only { display: none; }
}

/* ---------- Layout ---------- */
/* The page column. Its children are spaced by the gap, so pages are lists of blocks. */
.page {
  width: min(100% - 2 * var(--gutter), var(--measure));
  margin-inline: auto;
  padding-block: var(--page-top) var(--page-bottom);
  display: flex; flex-direction: column; gap: var(--space-4);
}
.page.wide { width: min(100% - 2 * var(--gutter), var(--measure-wide)); }
.page.public { --page-bottom: var(--space-8); }

/* Children one under another. The stack sets the gap on its children, not on itself, so a stack
   nested inside another keeps its parent's spacing around it and its own spacing inside. */
.stack > * { --stack-gap: var(--space-4); }
.stack.tight > * { --stack-gap: var(--space-2); }
.stack.loose > * { --stack-gap: var(--space-8); }
.stack > * + * { margin-top: var(--stack-gap); }

/* Components have no outer margins: the layout around them spaces them. These resets have no
   specificity, so .stack and the other layout rules always win over them. */
:where(.hint, .check, .pagetitle, .banner, .onthisday h2, .postcard h2, .sg-h, .sg-prose, .sg-toolbar, .list > li > p, .results .meta) {
  margin: 0;
}

/* The paper card. Its padding is exposed so edge-to-edge children (the save bar, the
   "On this day" scroller) can reach the card's edges. */
.paper {
  --paper-pad-y: var(--space-6);
  --paper-pad-x: var(--space-5);
  background-color: var(--paper);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow);
  padding: var(--paper-pad-y) var(--paper-pad-x);
}
@media (min-width: 40rem) {
  .paper { --paper-pad-y: var(--space-10); --paper-pad-x: var(--space-12); }
}

/* A row with a title on the left and something (a date, a badge) on the right. A long title wraps;
   the thing on the right stays where it is. */
.rowhead { display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-3); }
.rowhead > :first-child { flex: 1 1 auto; min-width: 0; }
.rowhead > :last-child:not(:first-child) { flex: none; }

/* ---------- Private nav: bottom bar on phones, top bar on wider screens ---------- */
.appnav {
  position: fixed; inset: auto 0 calc(env(safe-area-inset-bottom, 0px) - var(--safe-bottom-max)) 0; z-index: var(--z-nav);
  background-color: color-mix(in oklab, var(--paper) 92%, transparent);
  backdrop-filter: blur(8px);
  border-top: 1px solid var(--rule);
  font-family: var(--sans);
  padding-bottom: var(--safe-bottom-max);
}
.appnav ul {
  list-style: none; margin: 0 auto; padding: var(--space-1);
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
  max-width: 32rem; align-items: center;
}
.appnav a, .appnav button {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-0-5);
  min-height: 52px; justify-content: center;
  color: var(--muted); text-decoration: none; font: 500 var(--text-xs) var(--sans);
  background-color: transparent; border: 0; border-radius: var(--radius-md); width: 100%; cursor: pointer;
}
.appnav svg { width: var(--icon-nav); height: var(--icon-nav); }
.appnav [aria-current="page"] { color: var(--ink); font-weight: 600; }
.appnav .write {
  background-color: var(--accent-solid); color: var(--accent-ink);
  border-radius: var(--radius-pill); width: 56px; height: 56px; min-height: 0;
  margin: -18px auto 0; /* exact: lifts the Write button out of the bar */
  box-shadow: var(--shadow);
}
.appnav .write span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }

/* Posts, Trash and Settings sit in the Menu on phones (the bar has five slots). Wider screens show
   them in the top bar, which spans the window like the timeline below it, and need no Menu. */
.appnav li.spacer, .appnav li.wide-only { display: none; }
@media (min-width: 48rem) {
  .appnav { position: sticky; inset: 0 0 auto 0; padding-bottom: 0; border-top: 0; border-bottom: 1px solid var(--rule); }
  .appnav ul {
    width: calc(100% - 2 * var(--gutter)); max-width: none;
    display: flex; gap: var(--space-1); padding-inline: 0;
  }
  .appnav li.spacer { display: block; flex: 1; order: 1; }
  .appnav li.wide-only { display: block; }
  .appnav li.phone-only { display: none; }
  .appnav li.right { order: 2; }
  .appnav li.writeitem { order: 10; }
  .appnav a, .appnav button {
    flex-direction: row; gap: var(--space-2); font-size: var(--text-sm);
    padding-inline: var(--space-3); min-height: var(--tap);
  }
  .appnav .write {
    width: auto; height: 40px; margin: 0; padding-inline: var(--space-4);
    border-radius: var(--radius-md); box-shadow: none;
  }
  .appnav .write span { position: static; width: auto; height: auto; overflow: visible; clip-path: none; }
}

/* ---------- Menu ----------
   Phones only (wider screens have everything in the top bar). A bottom sheet, from a native
   popover, no JS: tap outside or press Esc to close. */
.menu {
  position: fixed; inset: auto 0 calc(env(safe-area-inset-bottom, 0px) - var(--safe-bottom-max)) 0;
  margin: 0; width: 100%; max-width: none;
  max-height: 85dvh; overflow-y: auto; overscroll-behavior: contain;
  border: 0; border-radius: var(--radius-xl) var(--radius-xl) 0 0;
  padding: var(--space-2) var(--space-3) calc(var(--space-3) + var(--safe-bottom-max));
  background-color: var(--paper); color: var(--ink); font-family: var(--sans);
  box-shadow: var(--shadow-sheet);
  translate: 0 100%;
  transition: translate var(--duration-slow) ease, overlay var(--duration-slow) allow-discrete,
    display var(--duration-slow) allow-discrete;
}
.menu::before { /* grab handle */
  content: ""; display: block; width: 36px; height: 4px; margin: var(--space-1) auto var(--space-2);
  border-radius: var(--radius-pill); background-color: var(--rule);
}
.menu:popover-open { translate: 0 0; }
@starting-style { .menu:popover-open { translate: 0 100%; } }
.menu::backdrop {
  background-color: transparent;
  transition: background-color var(--duration-slow), overlay var(--duration-slow) allow-discrete,
    display var(--duration-slow) allow-discrete;
}
.menu:popover-open::backdrop { background-color: var(--scrim); }
@starting-style { .menu:popover-open::backdrop { background-color: transparent; } }

/* Plain menu items (links, Log out); the light/dark toggle styles its own buttons. */
/* Bigger tap targets in the sheet, for every item including the toggle's buttons. */
.menu > a, .menu > form > button { min-height: 48px; }
.menu > a, .menu > form:not(.segmented) > button {
  display: block; width: 100%; text-align: left;
  padding: var(--space-3); border-radius: var(--radius-sm);
  color: var(--ink); text-decoration: none; background-color: transparent; border: 0; font: inherit; cursor: pointer;
}
.menu hr { border: 0; border-top: 1px solid var(--rule); margin: var(--space-2) 0; }
.menu > .segmented { margin: var(--space-1); }

@media (prefers-reduced-motion: reduce) {
  .menu, .menu::backdrop { transition: none; }
}

/* ---------- Toggles: light/dark/auto and the palette picker ---------- */
.segmented {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-0-5);
  background-color: var(--paper-edge); border-radius: var(--radius-md); padding: var(--space-0-5);
}
.segmented.narrow { max-width: 20rem; }
.segmented button {
  min-height: 36px; padding: var(--space-2); border: 0; border-radius: var(--radius-sm); text-align: center;
  background-color: transparent; color: var(--muted); font: var(--text-sm) var(--sans); cursor: pointer;
}
.segmented button[aria-pressed="true"] { color: var(--ink); background-color: var(--paper); box-shadow: var(--shadow-raised); }

.palettes { display: grid; gap: var(--space-0-5); }
.palettes button {
  display: flex; align-items: center; gap: var(--space-2); width: 100%; min-height: 40px; text-align: left;
  padding: var(--space-2); border: 1px solid transparent; border-radius: var(--radius-md);
  background-color: transparent; color: var(--ink); font: var(--text-sm) var(--sans); cursor: pointer;
}
.palettes button[aria-pressed="true"] { border-color: var(--accent); background-color: var(--selected); }
.swatches {
  display: inline-grid; grid-template-columns: repeat(4, 12px);
  border-radius: var(--radius-sm); overflow: hidden; box-shadow: 0 0 0 1px var(--rule);
}
.swatches span { height: 18px; }

/* ---------- Public header and footer ---------- */
.sitehead {
  width: min(100% - 2 * var(--gutter), var(--measure)); margin: 0 auto; padding-block: var(--space-6) var(--space-2);
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--space-2) var(--space-4);
}
.sitehead .name { font-size: var(--text-xl); font-weight: 600; color: var(--ink); text-decoration: none; }
.sitehead nav { display: flex; gap: var(--space-4); font-family: var(--sans); font-size: var(--text-sm); }
.sitehead nav a { color: var(--muted); text-decoration: none; }
.sitehead nav a[aria-current="page"] { color: var(--ink); }
.sitefoot {
  width: min(100% - 2 * var(--gutter), var(--measure)); margin: 0 auto; padding-block: var(--space-8) var(--space-12);
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-3);
  font: var(--text-sm) var(--sans); color: var(--muted);
}
/* A hairline around the track: in light mode it's the same color as the page around it. */
.footer-theme { width: 13rem; font-family: var(--sans); border: 1px solid var(--rule); }
.footer-theme button { font-size: var(--text-xs); min-height: var(--tap-sm); color: var(--ink); }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  min-height: var(--tap); padding: 0 var(--space-4); border-radius: var(--radius-md);
  font: 600 var(--text-ui) var(--sans); text-decoration: none; cursor: pointer;
  background-color: var(--paper); color: var(--ink); border: 1px solid var(--rule);
}
.btn.primary { background-color: var(--accent-solid); color: var(--accent-ink); border-color: transparent; }
.btn.quiet { border-color: transparent; background-color: transparent; color: var(--muted); }
/* After .quiet so a quiet danger button ("Move to trash") stays red. */
.btn.danger { color: var(--danger); }
/* Destructive confirmations (trash, unpublish): danger-colored fill. */
.btn.destructive { background-color: var(--danger); color: var(--paper); border-color: transparent; }
.btn svg { width: var(--icon); height: var(--icon); }
.linkbutton { background-color: transparent; border: 0; padding: 0; color: var(--accent); font: inherit; text-decoration: underline; cursor: pointer; }

/* A row of buttons. Forms inside it (e.g. "Publish…") line up with the other buttons. */
.actions { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.actions form { display: contents; }
/* The actions at the end of an article, below a rule. */
.actions.ruled { margin-top: var(--space-8); padding-top: var(--space-4); border-top: 1px solid var(--rule); }

/* ---------- Forms ---------- */
label { font: 600 var(--text-sm) var(--sans); color: var(--muted); display: block; margin-bottom: var(--space-1); }
input[type="text"], input[type="search"], input[type="password"], input[type="datetime-local"], input[type="email"], textarea {
  width: 100%; min-height: var(--tap); font: inherit; color: var(--ink); background-color: var(--paper);
  border: 1px solid var(--rule); border-radius: var(--radius-md); padding: var(--space-3);
}
input:focus, textarea:focus { outline: 2px solid var(--accent); outline-offset: -1px; border-color: transparent; }
::placeholder { color: var(--muted); opacity: 1; }
.field { margin-bottom: var(--space-4); }
.hint { font: var(--text-sm) var(--sans); color: var(--muted); }
input + .hint { margin-top: var(--space-1); }
/* Checkbox with its label, e.g. "Include trash" under the search box. */
.check { display: inline-flex; align-items: center; gap: var(--space-2); min-height: var(--tap); font-weight: 500; cursor: pointer; }
.check input { width: 18px; height: 18px; accent-color: var(--accent-solid); }
/* The search box, with its submit button as an icon at the field's end. */
.searchform { position: relative; }
.searchform input { padding-right: var(--tap); }
/* Inset in the field, so its hover tint is a rounded square like a toolbar button's. */
.searchgo {
  position: absolute; inset: var(--space-1) var(--space-1) var(--space-1) auto; width: calc(var(--tap) - 2 * var(--space-1));
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: var(--radius-sm);
  background-color: transparent; color: var(--muted); cursor: pointer;
}
.searchgo svg { width: var(--icon); height: var(--icon); }
.searchgo:focus-visible { outline-offset: -2px; }
/* The clear button Chrome and Safari put in a search field: an × in the palette's muted color. */
input[type="search"]::-webkit-search-cancel-button {
  -webkit-appearance: none; appearance: none; width: var(--icon); height: var(--icon); cursor: pointer;
  background-color: var(--muted);
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M6 6l12 12M18 6 6 18'/%3E%3C/svg%3E") center / contain no-repeat;
}
/* Under the box: the label filter, and Include trash. */
.searchfilters { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4); margin-top: var(--space-2); }
.labelfilter select {
  min-height: var(--tap); padding: 0 var(--space-3); border: 1px solid var(--rule); border-radius: var(--radius-md);
  background-color: var(--paper); color: var(--ink); font: var(--text-sm) var(--sans);
}
.searchhint { margin-top: var(--space-2); }

/* ---------- Text roles ---------- */
.eyebrow {
  font: 600 var(--text-xs) var(--sans); letter-spacing: var(--tracking-caps); text-transform: uppercase;
  color: var(--muted); margin: 0 0 var(--space-2);
}
.meta { font: var(--text-sm) var(--sans); color: var(--muted); }
.pagetitle { font-size: var(--text-2xl); }
.badge {
  display: inline-block; font: 600 var(--text-xs) var(--sans); letter-spacing: var(--tracking-caps); text-transform: uppercase;
  padding: var(--space-0-5) var(--space-2); border-radius: var(--radius-pill); border: 1px solid var(--rule);
  color: var(--muted); vertical-align: middle;
}
.badge.live { color: var(--accent); border-color: currentColor; }
.banner { font: var(--text-sm) var(--sans); padding: var(--space-3) var(--space-4); border-radius: var(--radius-md); background-color: var(--paper-edge); }
.banner strong { color: var(--ink); }

/* A page's heading: optional back link, the title with an optional action beside it, an optional subtitle. */
.pagehead {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
  gap: var(--space-2) var(--space-3); margin-bottom: var(--space-5);
}
.pagehead .eyebrow, .pagehead .meta { flex-basis: 100%; margin: 0; }

/* ---------- Journal home ----------
   Today's page: the Write card, this month's calendar (desktop) and On this day. The timeline is the
   workspace's list, beside it on desktop and under it on phones. */
.home { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
.calendarcard { display: none; }
@media (min-width: 64rem) {
  .home { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; gap: var(--space-5); }
  .home > .writecta { grid-column: 1 / -1; }
  .calendarcard { display: block; }
  /* On this day: a list down its card, rather than cards to scroll sideways. */
  .home .otd-list {
    grid-auto-flow: row; grid-auto-columns: auto; margin: var(--space-3) 0 0; padding: 0;
    overflow: visible; scroll-snap-type: none;
  }
}

/* A month as a calendar (desktop). Days you wrote on are links to them. */
.calendarcard h2 { font: 600 var(--text-body) var(--sans); }
.calendarcard h2 a { color: var(--ink); text-decoration: none; }
.calendar { width: 100%; margin-top: var(--space-3); border-collapse: collapse; table-layout: fixed; font: var(--text-sm) var(--sans); text-align: center; }
.calendar th { font-size: var(--text-xs); font-weight: 600; color: var(--muted); padding-bottom: var(--space-1); }
.calendar td { padding: var(--space-0-5); }
.calendar td > * {
  display: flex; align-items: center; justify-content: center; aspect-ratio: 1; border-radius: var(--radius-pill);
  color: var(--muted); font-variant-numeric: tabular-nums;
}
.calendar a { background-color: var(--selected); color: var(--ink); font-weight: 600; text-decoration: none; }
.calendar [aria-current="date"] { box-shadow: inset 0 0 0 2px var(--accent); color: var(--ink); }
.writecta {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
  text-decoration: none; color: var(--ink);
}
.writecta .prompt { font-size: var(--text-xl); font-style: italic; color: var(--muted); }
.writecta .btn { flex: none; }

.onthisday h2 { font-family: var(--sans); font-size: var(--text-body); }
.otd-list {
  list-style: none; margin: var(--space-3) calc(-1 * var(--paper-pad-x)) 0; padding: 0 var(--paper-pad-x) var(--space-1);
  display: grid; grid-auto-flow: column; grid-auto-columns: min(78%, 16rem); gap: var(--space-3);
  overflow-x: auto; scroll-snap-type: x mandatory; scroll-padding-inline: var(--paper-pad-x);
}
.otd-list li { scroll-snap-align: start; }
.otd-card {
  display: block; height: 100%; padding: var(--space-3) var(--space-4); border-radius: var(--radius-md);
  background-color: var(--paper-edge); color: var(--ink); text-decoration: none;
}
.otd-card .yrs { font: 600 var(--text-xs) var(--sans); color: var(--accent); }
.otd-card .t { font-weight: 600; margin: var(--space-1) 0; }
.otd-card .x {
  font-size: var(--text-ui); color: var(--muted); margin: 0;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}

/* ---------- Timeline: months, then days, then entries ---------- */
.timeline { display: flex; flex-direction: column; gap: var(--space-5); }
.month {
  position: sticky; top: 0; z-index: var(--z-sticky);
  font: 600 var(--text-xs) var(--sans); letter-spacing: var(--tracking-caps); text-transform: uppercase; color: var(--muted);
  background-color: var(--desk); padding: var(--space-2) var(--space-1); margin: 0 0 var(--space-1);
}
@media (min-width: 48rem) { .month { top: var(--appbar-height); } }
.day {
  display: grid; grid-template-columns: 3.25rem minmax(0, 1fr); gap: var(--space-3);
  padding: var(--space-4) 0; border-top: 1px solid var(--rule);
}
/* The card's padding is the space above the first day and below the last. */
.day:first-child { border-top: 0; padding-top: 0; }
.day:last-child { padding-bottom: 0; }
.daydate { text-align: center; font-family: var(--sans); line-height: var(--leading-tight); padding-top: var(--space-0-5); }
.daydate b { display: block; font-size: var(--text-2xl); font-family: var(--serif); }
.daydate span { font-size: var(--text-xs); text-transform: uppercase; letter-spacing: var(--tracking-caps); color: var(--muted); }
.entries { list-style: none; margin: 0; padding: 0; }
.entries li + li { margin-top: var(--space-3); }
.entrylink {
  display: block; color: var(--ink); text-decoration: none; border-radius: var(--radius-md);
  margin: calc(-1 * var(--space-2)) calc(-1 * var(--space-3)); padding: var(--space-2) var(--space-3);
}
.entrylink .t { font-weight: 600; }
/* Title on the left, time on the right of the same line (a long title wraps; the time doesn't). */
.entrylink .head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); }
.entrylink .time { flex: none; font: var(--text-xs) var(--sans); color: var(--muted); font-variant-numeric: tabular-nums; }
.entrylink .x {
  margin: var(--space-0-5) 0 0; color: var(--muted); font-size: var(--text-ui);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.loadmore { display: flex; justify-content: center; }

/* ---------- Workspace (views/journal.tsx) ----------
   Desktop: the list (the timeline, search results, a month) on the left and the open page on the
   right (#detail), each scrolling on its own under the top bar. Opening something from the list swaps
   only the right side, so the list keeps its place. Phones: the page alone, with the list under it
   where the page is about the list (home, search, a month). */
.workspace { display: flex; flex-direction: column; gap: var(--space-4); }
.wslist { display: none; }
.workspace:has(#detail[data-phone-list]) .wslist { display: grid; gap: var(--space-4); }
.panebar, .paneempty { display: none; }
.listtop { margin: 0; font: var(--text-sm) var(--sans); }

@media (min-width: 64rem) {
  .page.workspacepage { width: 100%; padding: 0; }
  .workspace {
    position: fixed; inset: var(--appbar-height) 0 0 0; gap: 0;
    display: grid; grid-template-columns: var(--list-width) minmax(0, 1fr); grid-template-areas: "list detail";
  }
  .wslist, .workspace:has(#detail[data-phone-list]) .wslist {
    grid-area: list; display: block; overflow-y: auto; overscroll-behavior: contain;
    background-color: var(--paper); border-right: 1px solid var(--rule);
  }
  .wsdetail { grid-area: detail; overflow-y: auto; overscroll-behavior: contain; container-type: inline-size; }

  /* The list: compact rows, months as headings that stick to the top of the list. */
  .wslist .timeline { gap: 0; }
  .wslist .month {
    top: 0; margin: 0; padding: var(--space-2) var(--space-5);
    background-color: var(--paper); border-bottom: 1px solid var(--rule);
  }
  /* Cards lie flat in the workspace: the list and the pane are the surfaces. */
  .wslist .timeline .paper, .wslist > .paper, .wsdetail .readcol, .wsdetail .yearpane, .wsdetail .labelpane {
    border: 0; border-radius: 0; box-shadow: none; background-color: transparent;
  }
  .wslist .timeline .paper { padding: 0; }
  .wslist > .paper { --paper-pad-y: var(--space-5); --paper-pad-x: var(--space-5); }
  .wslist .day { grid-template-columns: 2.25rem minmax(0, 1fr); padding: var(--space-3) var(--space-5); }
  .wslist .day:first-child, .wslist .day:last-child { padding-block: var(--space-3); }
  .wslist .daydate b { font-size: var(--text-xl); }
  .wslist .entrylink .t, .wslist .list .t { font-size: var(--text-ui); }
  .wslist .entrylink .x, .wslist .list .x { font-size: var(--text-sm); }
  .wslist .entrylink[aria-current="page"], .wslist .list li:has(.t[aria-current="page"]) { background-color: var(--selected); }
  .wslist .list li:has(.t[aria-current="page"]) { margin-inline: calc(-1 * var(--space-5)); padding-inline: var(--space-5); }
  /* On the open row's tint, quiet text (badges, dates) needs the full ink color to stay readable. */
  .wslist .list li:has(.t[aria-current="page"]) :is(.badge, .meta, .meta a) { color: var(--ink); border-color: currentColor; }
  .wslist .listtop { padding: var(--space-3) var(--space-5); border-bottom: 1px solid var(--rule); }
  .wslist .loadmore, .wslist > .pager, .wslist > .muted { padding: var(--space-4) var(--space-5); }
  .wslist .pagehead { margin-bottom: var(--space-3); }
  /* Every row has room around it in the list, the first too, for the open one's highlight. */
  .wslist .list > li:first-child { padding-top: var(--space-4); }
  .wslist .pagetitle { font-size: var(--text-xl); }
  .wsdetail > .actions.readcol { padding: 0 var(--space-8) var(--space-12); }

  /* The open page. A bar along its top holds the date and what you can do. */
  .panebar {
    display: flex; align-items: center; gap: var(--space-2); position: sticky; top: 0; z-index: var(--z-sticky);
    min-height: var(--panebar-height); padding: var(--space-2) var(--space-6);
    background-color: color-mix(in oklab, var(--desk) 92%, transparent); backdrop-filter: blur(8px);
    border-bottom: 1px solid var(--rule); font-family: var(--sans);
  }
  .panebar .grow { flex: 1; color: var(--muted); font-size: var(--text-sm); }
  .panebar .toparrows { margin: 0 var(--space-2) 0 0; }
  .panebar form { display: contents; }
  .paneempty { display: grid; place-items: center; height: 100%; margin: 0; color: var(--muted); font-family: var(--sans); }

  /* An entry and the editor share one column, its type growing with the pane: lines stay about 70
     characters wide, and both wrap alike, so Edit doesn't move the text. */
  .wsdetail .readcol {
    font-size: var(--text-read); width: min(100%, 36em); margin-inline: auto; padding: 0;
    --paper-pad-x: 0px; --paper-pad-y: 0px; /* exact: the column has no padding of its own */
  }
  .wsdetail .entrypage { min-height: 0; padding: var(--space-10) var(--space-8) var(--space-12); }
  .wsdetail .readcol .prose { font-size: 1em; }
  .wsdetail .readcol .readhead h1, .wsdetail .readcol .titleinput { font-size: 2em; }
  /* The bar has the date, arrows and actions; the list does what the pager does. */
  .wsdetail .readtop, .wsdetail .entrypage .actions.ruled, .wsdetail .entrypage > .pager { display: none; }

  .wsdetail .editor { display: flex; flex-direction: column; padding: 0 var(--space-8) var(--space-12); width: min(100%, calc(36em + 2 * var(--space-8))); }
  .wsdetail .editor .writing { font-size: 1em; --line: 1.75em; }
  .wsdetail .editor .toolbar { top: var(--panebar-height); background-color: var(--desk); }
  /* The save bar is the editor's top bar, across the whole pane like the entry's. */
  .wsdetail .editor .savebar {
    order: -1; position: sticky; top: 0; flex-wrap: nowrap; min-height: var(--panebar-height);
    margin: 0 calc((100% - 100cqi) / 2) var(--space-10); padding: var(--space-2) var(--space-6);
    border-top: 0; border-bottom: 1px solid var(--rule); border-radius: 0;
    background-color: color-mix(in oklab, var(--desk) 92%, transparent); backdrop-filter: blur(8px);
  }

  /* Today's page and a month, beside the list. */
  .wsdetail .home { width: min(100%, 64rem); margin-inline: auto; padding: var(--space-10) var(--space-8); }
  .wsdetail .home .paper { --paper-pad-y: var(--space-6); --paper-pad-x: var(--space-6); }
  .wsdetail .monthpane { width: min(100%, 32rem); margin-inline: auto; padding: var(--space-10) var(--space-8); }
  .monthtitle { position: static; width: auto; height: auto; overflow: visible; clip-path: none; white-space: normal; font-size: var(--text-2xl); margin: 0 0 var(--space-5); }
}

/* ---------- Editor ---------- */
/* One row that grows as the title wraps, like the heading it becomes (editor.js grows it where
   field-sizing isn't supported). */
.editor .titleinput {
  display: block; font: 600 var(--text-2xl) / var(--leading-tight) var(--serif);
  border: 0; padding: 0; background-color: transparent; min-height: 0;
  resize: none; overflow: hidden; field-sizing: content;
}
.editor .titleinput:focus { outline: none; }
.editor details { margin: var(--space-2) 0 0; font-family: var(--sans); }
.editor summary {
  font-size: var(--text-sm); color: var(--muted); min-height: var(--tap-sm);
  display: inline-flex; align-items: center; gap: var(--space-1);
}
.editor details[open] summary { margin-bottom: var(--space-2); }
/* One row from edge to edge of the card. Where it doesn't fit (a phone) it scrolls sideways, and the
   button cut off at the edge shows there are more. */
.toolbar {
  display: flex; gap: var(--space-0-5);
  margin: var(--space-4) calc(-1 * var(--paper-pad-x)) var(--space-2);
  padding: var(--space-1) var(--paper-pad-x);
  overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none;
  border-block: 1px solid var(--rule);
  position: sticky; top: 0; background-color: var(--paper); z-index: var(--z-sticky);
}
.toolbar > * { flex: none; }
@media (min-width: 48rem) { .toolbar { top: var(--appbar-height); } }
.toolbar button {
  min-width: var(--tap); min-height: 40px; border: 0; border-radius: var(--radius-sm); cursor: pointer;
  background-color: transparent; color: var(--ink); font: 600 var(--text-ui) var(--sans);
  display: inline-flex; align-items: center; justify-content: center;
}
.toolbar i { font-family: var(--serif); }
.toolbar mark { padding: 0 var(--space-1); }
.toolbar .sep { width: 1px; background-color: var(--rule); margin: var(--space-2) var(--space-1); }
.toolbar svg { width: var(--icon); height: var(--icon); }
/* Ruled paper: the lines are one text line apart and scroll with the text. */
.writing {
  --line: 2rem; /* exact: the ruled lines' spacing, matched by the gradient below */
  border: 0; padding: 0; min-height: 55dvh; resize: vertical;
  font: var(--text-body) / var(--line) var(--serif);
  background-color: transparent;
  background-image: linear-gradient(to bottom, transparent calc(var(--line) - 1px), var(--rule) calc(var(--line) - 1px));
  background-size: 100% var(--line); background-attachment: local;
  field-sizing: content;
}
.writing:focus { outline: none; }
/* It grows with the text, so a resize grip would only look unfinished. */
@supports (field-sizing: content) { .writing { resize: none; } }
/* The save bar is the editor's bottom edge: it spans the card edge to edge, and sticks above the
   phone's bottom bar (or the screen's bottom edge) while you write. */
.savebar {
  position: sticky; bottom: calc(var(--tabbar-height) + env(safe-area-inset-bottom)); z-index: var(--z-savebar);
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3);
  margin: var(--space-4) calc(-1 * var(--paper-pad-x)) calc(-1 * var(--paper-pad-y));
  padding: var(--space-3) var(--paper-pad-x);
  background-color: var(--paper); border-top: 1px solid var(--rule);
  border-radius: 0 0 var(--radius-md) var(--radius-md);
  font-family: var(--sans);
}
.savebar .status { flex: 1 1 100%; font-size: var(--text-sm); color: var(--muted); }
.savebar .status:empty { display: none; }
.savebar .actions { flex: 1; justify-content: flex-end; }
/* Phones: the main button fills the row, easy to reach with a thumb. */
.savebar .actions .primary { flex: 1; }
@media (min-width: 40rem) {
  .savebar .actions .primary { flex: none; }
}
@media (min-width: 48rem) {
  .savebar { bottom: 0; }
  .savebar .status { flex: 1 1 auto; }
}

/* ---------- Writing mode ----------
   On a phone, the editor gets the screen while it has focus: the bottom bar steps aside, and the save
   bar drops to the bottom edge, which is just above the keyboard while it's up (the viewport meta in
   views/layout.tsx asks for that). Focus leaving the editor brings the bar back. Wider screens keep
   their top bar. */
body:has(.editor:focus-within) .appnav { display: none; }
body:has(.editor:focus-within) .savebar { bottom: env(safe-area-inset-bottom, 0px); }
@media (min-width: 48rem) {
  body:has(.editor:focus-within) .appnav { display: block; }
  body:has(.editor:focus-within) .savebar { bottom: 0; }
}

/* ---------- Reading (entries and posts) ---------- */
.prose { font-size: var(--text-lg); line-height: var(--leading-prose); }
/* Reset browser margins first, so the blank-line spacing below is the only spacing.
   (:where keeps this low-specificity, so the rules after it win.) */
.prose :where(p, ul, ol, blockquote, pre, h2, h3, h4) { margin: 0; }
/* Blocks are one blank line apart, and each extra blank line written adds one more. */
.prose > * + *, .prose blockquote > * + * { margin-top: 1.75em; margin-top: 1lh; }
.prose .space { height: 1.75em; height: 1lh; margin-top: 0; }
.prose h2 { font-size: var(--text-xl); margin-top: 1.8em; }
.prose h3 { font-size: var(--text-lg); margin-top: 1.6em; }
.prose blockquote { margin-inline: 0; padding-left: var(--space-4); border-left: 3px solid var(--rule); color: var(--muted); font-style: italic; }
.prose ul, .prose ol { padding-left: var(--space-5); }
.prose li + li { margin-top: 0.3em; }
.prose hr { border: 0; text-align: center; margin-block: 2em; }
.prose hr::after { content: "\2022  \2022  \2022"; color: var(--muted); letter-spacing: 0.5em; }
.prose pre { overflow-x: auto; overflow-wrap: normal; } /* a code block scrolls on its own */
.prose code { font: 0.9em var(--mono); background-color: var(--paper-edge); padding: 0.1em 0.3em; border-radius: var(--radius-sm); }
.prose s { color: var(--muted); }
.prose .task { list-style: none; margin-left: calc(-1 * var(--space-5)); }

.readhead { margin-bottom: var(--space-6); }
.readhead h1 { font-size: var(--text-3xl); margin: 0 0 var(--space-2); }
@media (min-width: 40rem) { .readhead h1 { font-size: var(--text-4xl); } }
.readtop { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.readtop .eyebrow { margin: 0; }
.readtop + h1 { margin-top: var(--space-2); }
.toparrows { display: flex; flex: none; margin: calc(-1 * var(--space-3)) calc(-1 * var(--space-3)) calc(-1 * var(--space-3)) 0; }
.toparrows a, .toparrows span {
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--tap); height: var(--tap); border-radius: var(--radius-pill); color: var(--muted);
}
.toparrows span { opacity: 0.3; }
.toparrows svg { width: 20px; height: 20px; }
.publishednote {
  display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap;
  font: var(--text-sm) var(--sans); padding: var(--space-2) var(--space-3); border-radius: var(--radius-md);
  background-color: var(--paper-edge); margin-bottom: var(--space-5);
}

/* An entry fills the screen, so its pager sits at the bottom on short entries. */
.entrypage {
  display: flex; flex-direction: column; gap: var(--space-6);
  min-height: calc(100dvh - var(--page-top) - var(--page-bottom));
}
.entrypage .pager { margin-top: auto; }
@media (min-width: 48rem) {
  .entrypage { min-height: calc(100dvh - var(--appbar-height) - var(--page-top) - var(--page-bottom)); }
}
.pager { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); font-family: var(--sans); }
.pager a {
  display: block; min-height: var(--tap); padding: var(--space-3) var(--space-4); border-radius: var(--radius-md);
  background-color: var(--paper); box-shadow: var(--shadow); border: 1px solid var(--card-border);
  color: var(--ink); text-decoration: none;
}
.pager .dir { display: block; font-size: var(--text-xs); color: var(--muted); }
.pager .t { font: 600 var(--text-ui) var(--serif); }
.pager .next { text-align: right; grid-column: 2; }

/* Reading progress bar: a scroll-driven animation, no JS. */
.progress {
  position: fixed; inset: 0 0 auto 0; height: 3px; background-color: var(--accent-solid);
  transform-origin: left; transform: scaleX(0); z-index: var(--z-progress);
}
@supports (animation-timeline: scroll()) {
  .progress { animation: progress linear both; animation-timeline: scroll(root); }
  @keyframes progress { to { transform: scaleX(1); } }
}

/* ---------- Lists (posts, search, trash, versions, the blog) ---------- */
.list { list-style: none; margin: 0; padding: 0; }
.list > li { display: flex; flex-direction: column; gap: var(--space-1); padding: var(--space-4) 0; border-top: 1px solid var(--rule); }
.list > li:first-child { border-top: 0; padding-top: 0; }
.list > li > form { margin-top: var(--space-1); }
.list .t { font-weight: 600; font-size: var(--text-lg); color: var(--ink); text-decoration: none; }
.list .t.trashed { color: var(--muted); }
.list .x { color: var(--muted); }
/* Something after a list, like "Create the About page". */
.list + .actions { margin-top: var(--space-6); }
.list + .hint { margin-top: var(--space-4); }
/* The search count and sort chips: the chips drop below the count when there isn't room. */
.results { flex-wrap: wrap; margin: var(--space-4) 0 var(--space-5); }

.postcard h2 { font-size: var(--text-2xl); }
.postcard h2 a { color: var(--ink); text-decoration: none; }

.revision > h2 { font-size: var(--text-lg); margin: var(--space-4) 0 var(--space-2); }
.revision > .prose { font-size: var(--text-body); }
.revision > form { margin-top: var(--space-3); }
.revisions .current { color: var(--accent); }

/* The archive (views/journal.tsx): years in the list, a year's months beside it. */
.yearspaper { padding: 0; overflow: hidden; }
.yearspaper .listtop { padding: var(--space-4) var(--space-5) var(--space-2); color: var(--muted); }
.years { list-style: none; margin: 0; padding: 0; }
.years a {
  display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-3);
  min-height: var(--tap); padding: var(--space-4) var(--space-5); border-top: 1px solid var(--rule);
  font: 600 var(--text-xl) var(--serif); color: var(--ink); text-decoration: none;
}
.years a .n { font: var(--text-sm) var(--sans); color: var(--muted); }
.years a[aria-current="page"] { background-color: var(--selected); }
.months {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr)); gap: var(--space-2);
}
.months a {
  display: flex; justify-content: space-between; align-items: center; min-height: var(--tap);
  padding: var(--space-2) var(--space-3); border-radius: var(--radius-md);
  background-color: var(--paper-edge); color: var(--ink); text-decoration: none; font-family: var(--sans); font-size: var(--text-sm);
}
.months a span { color: var(--muted); }
.yeargrid { display: none; }
@media (min-width: 64rem) {
  .years a { font-size: var(--text-lg); padding-block: var(--space-3); }
  /* Desktop shows the year as calendars instead of the grid of months. */
  .wsdetail .yearpane { width: min(100%, 72rem); margin-inline: auto; padding: var(--space-10) var(--space-8); }
  .wsdetail .labelpane { width: min(100%, 36rem); margin-inline: auto; padding: var(--space-10) var(--space-8); }
  .yearpane .months { display: none; }
  .yeargrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); gap: var(--space-4); align-items: start; }
  .yeargrid .calendarcard { --paper-pad-y: var(--space-4); --paper-pad-x: var(--space-4); }
  .yeargrid .calendar { font-size: var(--text-xs); }
}

/* ---------- Labels (views/labels.tsx) ----------
   A chip mixes its label's color (tokens: labelColors) with the palette's paper and ink, so every label
   reads well in every palette, light or dark. */
.labelchip {
  --c: var(--label-gray);
  display: inline-flex; align-items: center; gap: var(--space-1);
  padding: var(--space-0-5) var(--space-2); border-radius: var(--radius-pill);
  font: 600 var(--text-xs) / var(--leading-tight) var(--sans); text-decoration: none; white-space: nowrap;
  background-color: color-mix(in oklab, var(--c) 18%, var(--paper));
  color: color-mix(in oklab, var(--c) 40%, var(--ink));
}
.labelchip::before {
  content: ""; flex: none; width: 0.5em; height: 0.5em; border-radius: var(--radius-pill); background-color: var(--c);
}
a.labelchip { min-height: calc(var(--tap-sm) - var(--space-2)); } /* a 24px target */
[data-color="blue"] { --c: var(--label-blue); }
[data-color="green"] { --c: var(--label-green); }
[data-color="orange"] { --c: var(--label-orange); }
[data-color="purple"] { --c: var(--label-purple); }
[data-color="teal"] { --c: var(--label-teal); }
[data-color="red"] { --c: var(--label-red); }
[data-color="pink"] { --c: var(--label-pink); }
[data-color="yellow"] { --c: var(--label-yellow); }
[data-color="gray"] { --c: var(--label-gray); }

/* An entry's labels, under its title and in the editor, with the button that opens the picker. */
.entrylabels { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin: var(--space-2) 0; }
.labeladd {
  min-height: calc(var(--tap-sm) - var(--space-2)); padding: 0 var(--space-2); border: 1px dashed var(--rule);
  border-radius: var(--radius-pill); background-color: transparent; color: var(--muted);
  font: var(--text-xs) var(--sans); cursor: pointer;
}
/* In a row of the list: under the excerpt. */
.rowlabels { display: flex; flex-wrap: wrap; gap: var(--space-1); margin: var(--space-1) 0 0; }

/* The picker: tick labels, or name a new one. */
.labelpickerhead { margin-bottom: var(--space-3); }
.labelpickerhead p { margin: 0; }
.labelchoices { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-3); margin: 0; padding: 0; border: 0; }
.labeloption, .coloroption {
  display: inline-flex; align-items: center; gap: var(--space-2); min-height: var(--tap); margin: 0;
  font-weight: normal; color: var(--ink); cursor: pointer;
}
.labeloption input, .coloroption input { width: 18px; height: 18px; margin: 0; accent-color: var(--accent-solid); }
.labelnew { display: flex; gap: var(--space-2); margin-top: var(--space-4); }
.labelnew input { flex: 1; min-width: 0; }

/* The Labels page: every label with its count. Each row spans the rules between rows, its text inset. */
.labellist { list-style: none; margin: 0; padding: 0; }
.labellist li + li { border-top: 1px solid var(--rule); }
.labellist a {
  display: flex; justify-content: space-between; align-items: center; gap: var(--space-3);
  min-height: var(--tap); padding: var(--space-2) var(--space-3); margin-block: var(--space-0-5);
  border-radius: var(--radius-md); color: var(--ink); text-decoration: none;
}
.labellist a[aria-current="page"] { background-color: var(--selected); }
.labellist .n { font: var(--text-sm) var(--sans); color: var(--muted); }
/* A label's page: its name and color to change. */
.labelpane .pagetitle .labelchip { font-size: var(--text-lg); }
.colorpicker { display: flex; flex-wrap: wrap; gap: 0 var(--space-3); margin: 0; padding: 0; border: 0; }
.colorpicker legend { padding: 0; font: 600 var(--text-sm) var(--sans); color: var(--muted); }

/* Search sort chips */
.sorts { display: flex; gap: var(--space-1); font: var(--text-sm) var(--sans); }
.sorts a {
  display: inline-flex; align-items: center; min-height: var(--tap-sm); padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill); color: var(--muted); text-decoration: none;
}
.sorts a[aria-current] { background-color: var(--selected); color: var(--ink); font-weight: 600; }

/* ---------- Sign in ---------- */
.login { width: 100%; max-width: 24rem; margin: 15vh auto 0; }
.login .pagetitle { text-align: center; margin-bottom: var(--space-4); }
.login .btn { width: 100%; }
.login .banner { margin-bottom: var(--space-4); }

/* ---------- Confirmation sheets ----------
   A bottom sheet on phones, a centered dialog on wider screens. Native popover, so no JavaScript:
   tap outside, press Esc or Cancel to close. */
.sheet {
  position: fixed; inset: auto 0 calc(env(safe-area-inset-bottom, 0px) - var(--safe-bottom-max)) 0;
  margin: 0; width: 100%; max-width: none; height: auto;
  border: 0; border-radius: var(--radius-xl) var(--radius-xl) 0 0;
  padding: var(--space-6) var(--space-5) calc(var(--space-5) + var(--safe-bottom-max));
  background-color: var(--paper); color: var(--ink); font-family: var(--sans);
  box-shadow: var(--shadow-sheet);
  translate: 0 100%;
  transition: translate var(--duration-slow) ease, overlay var(--duration-slow) allow-discrete,
    display var(--duration-slow) allow-discrete;
}
.sheet:popover-open { translate: 0 0; }
@starting-style { .sheet:popover-open { translate: 0 100%; } }
.sheet::backdrop {
  background-color: transparent;
  transition: background-color var(--duration-slow), overlay var(--duration-slow) allow-discrete,
    display var(--duration-slow) allow-discrete;
}
.sheet:popover-open::backdrop { background-color: var(--scrim); }
@starting-style { .sheet:popover-open::backdrop { background-color: transparent; } }
.sheet p { margin: 0 0 var(--space-3); line-height: var(--leading-ui); }
.sheet .actions { flex-direction: column-reverse; gap: var(--space-2); margin-top: var(--space-5); }
.sheet .actions .btn { width: 100%; min-height: 48px; }

@media (min-width: 40rem) {
  .sheet {
    inset: 0; margin: auto; width: min(26rem, 100% - 2rem); height: fit-content;
    border-radius: var(--radius-lg); padding: var(--space-6); translate: none; opacity: 0;
    box-shadow: var(--shadow-overlay);
    transition: opacity var(--duration-fast), overlay var(--duration-fast) allow-discrete,
      display var(--duration-fast) allow-discrete;
  }
  .sheet:popover-open { opacity: 1; }
  @starting-style { .sheet:popover-open { opacity: 0; } }
  .sheet .actions { flex-direction: row; justify-content: flex-end; }
  .sheet .actions .btn { width: auto; min-height: var(--tap); }
}
@media (prefers-reduced-motion: reduce) {
  .sheet, .sheet::backdrop { transition: none; }
}

/* ---------- Interaction states ----------
   One system for every control, after Material Design's "state layers": hover and press tint a
   control with a translucent layer of its own text color, so the same rule works on any surface,
   in every palette and mode. Controls opt in by painting the layer (the list below); the hover and
   press rules can then be broad, since anything without the layer ignores --layer. Hover applies only
   on devices that can hover, so a tap on a phone doesn't leave things looking stuck. */
@property --layer { syntax: "<color>"; inherits: false; initial-value: transparent; }

[data-palette] {
  --selected: color-mix(in oklab, var(--accent-solid) 20%, transparent);
}

:where(.btn, .appnav a, .appnav button, .menu > a, .menu > form > button, .segmented button, .palettes button,
  .toolbar button, .searchgo, .calendar a, .toparrows a, .sorts a, .entrylink, .otd-card, .pager a, .months a, .writecta, .years a, a.labelchip, .labeladd, .labellist a) {
  background-image: linear-gradient(var(--layer), var(--layer));
  transition: --layer var(--duration-fast), background-color var(--duration-fast), color var(--duration-fast),
    border-color var(--duration-fast), scale var(--duration-fast);
  -webkit-tap-highlight-color: transparent;
}
@media (hover: hover) {
  :where(a, button, summary):hover { --layer: color-mix(in oklab, currentColor 8%, transparent); }
  /* Text links and titles: color and underline only */
  a:hover { text-decoration-thickness: 2px; }
  .entrylink:hover .t, .list .t:hover, .postcard h2 a:hover { color: var(--accent); }
  .sitehead nav a:hover { color: var(--ink); }
  .btn.quiet:hover, .toparrows a:hover, .segmented button:hover, .searchgo:hover { color: var(--ink); }
  .btn.quiet.danger:hover { color: var(--danger); }
}
:where(a, button, summary):active { --layer: color-mix(in oklab, currentColor 14%, transparent); }
:where(.btn, .appnav .write, .toolbar button, .toparrows a):active { scale: 0.97; }

/* Focus: one ring for keyboard users, in the palette's accent (checked at 3:1 against every
   background in tests/themes.test.ts). Cards and rows get the ring right on their edge. */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
:where(.entrylink, .otd-card, .pager a, .months a, .writecta, .palettes button):focus-visible { outline-offset: 0; }
:where(input, textarea):focus-visible { outline-offset: -1px; }

/* Selected: the current nav item gets a pill behind its icon on phones, an underline on wider screens. */
.appnav a[aria-current="page"]:not(.write) svg {
  box-sizing: content-box; padding: var(--space-1) var(--space-4); margin-bottom: calc(-1 * var(--space-1));
  border-radius: var(--radius-pill); background-color: var(--selected);
}
@media (min-width: 48rem) {
  .appnav a[aria-current="page"]:not(.write) svg { padding: 0; margin: 0; background-color: transparent; }
  /* The current item is underlined along the bar's bottom edge, clear of its rounded hover tint. */
  .appnav a[aria-current="page"]:not(.write) { position: relative; }
  .appnav a[aria-current="page"]:not(.write)::after {
    content: ""; position: absolute; inset: auto 0 calc(-1 * var(--space-1)); height: 2px;
    background-color: var(--accent);
  }
}

/* Disabled */
:where(button, .btn)[disabled], [aria-disabled="true"] { opacity: 0.45; cursor: not-allowed; pointer-events: none; }

@media (prefers-reduced-motion: reduce) {
  :where(.btn, .appnav .write, .toolbar button, .toparrows a):active { scale: none; }
}

/* ---------- Page swaps (htmx) ---------- */
/* A thin bar at the top while a request runs. It fades in only if the wait is noticeable. */
.loading {
  position: fixed; inset: 0 0 auto 0; z-index: var(--z-notice); height: 3px; pointer-events: none;
  background: linear-gradient(90deg, transparent, var(--accent-solid), transparent) 0 0 / 40% 100% no-repeat;
  opacity: 0; transition: opacity var(--duration-fast);
}
.loading.htmx-request {
  opacity: 1; transition-delay: var(--duration-slow);
  animation: loading 1s ease-in-out infinite;
}
@keyframes loading { from { background-position: -40% 0; } to { background-position: 140% 0; } }
@media (prefers-reduced-motion: reduce) {
  .loading.htmx-request { animation: none; background-size: 100% 100%; }
}

/* A short message when something couldn't be done, e.g. no connection. */
.notice {
  position: fixed; z-index: var(--z-notice); left: 50%; translate: -50% 0;
  bottom: calc(var(--tabbar-height) + env(safe-area-inset-bottom) + var(--space-3));
  width: max-content; max-width: min(32rem, 100% - 2 * var(--gutter));
  padding: var(--space-3) var(--space-4); border-radius: var(--radius-md); box-shadow: var(--shadow-overlay);
  background-color: var(--ink); color: var(--paper); font: var(--text-sm) / var(--leading-ui) var(--sans);
}
.notice:empty { display: none; }
/* Undo sits inline after the message, underlined in the notice's own color. */
.notice-action { display: inline; margin-left: var(--space-2); }
.notice .linkbutton { color: inherit; font-weight: 600; }
@media (min-width: 48rem) { .notice { bottom: var(--space-6); } }

/* After a swap, focus lands on the page's heading so screen readers announce it (static/app.js).
   It's a landing point, not a control, so it doesn't get a focus ring. */
h1[tabindex="-1"]:focus { outline: none; }

/* ---------- Style guide (/settings/styleguide) ---------- */
.sg-grid { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fill, minmax(min(100%, 30rem), 1fr)); }
.sg-card { background-color: var(--desk); color: var(--ink); border-radius: var(--radius-lg); padding: var(--space-4); font-family: var(--serif); }
.sg-title { font: 600 var(--text-sm) var(--sans); margin: 0 0 var(--space-3); }
.sg-h { font-size: var(--text-2xl); }
.sg-prose { font-size: var(--text-body); }
.sg-row { display: flex; flex-wrap: wrap; gap: var(--space-3); align-items: center; }
.sg-row .segmented { min-width: 14rem; }
.sg-row input[type="text"] { flex: 1; min-width: 10rem; }
.sg-entries { list-style: none; padding: 0; margin: 0; }
.sg-toolbar { position: static; }
/* Foundations: one row per token, with a sample drawn from it. */
.sg-tokens { display: grid; gap: var(--space-8); }
.sg-tokens h2 { font: 600 var(--text-sm) var(--sans); margin: 0 0 var(--space-2); }
.sg-token {
  display: grid; grid-template-columns: minmax(0, 11rem) minmax(0, 1fr); gap: var(--space-1) var(--space-4);
  align-items: center; padding: var(--space-3) 0; border-top: 1px solid var(--rule); font-family: var(--sans);
}
.sg-token code { font: var(--text-sm) var(--mono); }
.sg-token .use { font-size: var(--text-sm); color: var(--muted); grid-column: 1 / -1; }
.sg-bar { height: var(--space-3); background-color: var(--accent-solid); border-radius: var(--radius-sm); }
.sg-box { width: var(--space-12); height: var(--space-10); background-color: var(--paper-edge); border: 1px solid var(--rule); }
@media (min-width: 40rem) {
  .sg-token .use { grid-column: auto; }
  .sg-token { grid-template-columns: 11rem minmax(0, 1fr) minmax(0, 16rem); }
}
