/* The screens — specs/ux.md §4, from specs/design/grouped.html.
 *
 * The arrangement is the platform's grouped list, and it is four rules:
 *
 *  · the page is sunk and the content floats on it, as cards with a gutter
 *    rather than bands running edge to edge;
 *  · the section label sits above its card, in the page — that is what makes a
 *    list read as a group instead of as a banded table;
 *  · separators start where the text starts, not at the edge of the card;
 *  · one tint, on anything that can be touched. Everything else is ink and grey.
 *
 * The palette is tokens.css and is not restated here. What this file owns is
 * density, which is the thing that deliberately did not travel from the Mac:
 * 44 px is the floor for anything tappable, and titles get the full width
 * because they are what is actually read.
 */

/* ── The safe area ───────────────────────────────────────────────────────────
 *
 * Named once so that the rules below read as layout rather than as a list of
 * `env()` calls, and with a fallback of zero so a desktop browser — where none
 * of these exist — computes the same arithmetic and gets the plain number.
 *
 * The application is drawn edge to edge (`viewport-fit=cover` in the shell, and
 * `contentInset: never` in `capacitor.config.json`) and each bar spends its own
 * inset. That is the fix for a bottom bar that stopped short of the bottom:
 * insetting the whole application pushes the bar up and paints the sunk page in
 * the gap, and no amount of colour makes that read as anything but a bar in the
 * wrong place. */
:root {
  --safe-t: env(safe-area-inset-top, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);
  --safe-r: env(safe-area-inset-right, 0px);

  /* The gutter a bar spends: the page's, plus the four pixels a section label is
   * inset by inside its card. That is what puts a navigation title and the label
   * under it on the same vertical. */
  --bar-x: calc(var(--gutter) + 4px);
}

/* ── On a Mac ────────────────────────────────────────────────────────────────
 *
 * `data-runtime` is stamped on the root by `js/runtime.js` from the same signal
 * `devicePlatform()` uses — touch points, because the same binary runs on an
 * Apple silicon Mac as "Designed for iPad" and calls itself iOS there
 * (docs/ios.md §5b). **Everything the Mac changes is in this one block**, so
 * there is one place to read to find out how the two machines differ, and four
 * things in it.
 *
 * **The safe area is nothing, and that is the fix that matters most.** A Mac has
 * no notch and no home indicator, and the WebView says otherwise — it carries
 * the phone's insets onto the desktop, where they come out as fifty points of
 * empty above the navigation title and a tab bar padded off the bottom edge of
 * its own window. Every bar spends its own inset by design, so zeroing the four
 * of them here fixes all of them at once and no bar has to know where it is.
 *
 * **The type is a fifth bigger.** Not a redesign: the ranks stay in the
 * proportions the phone settled and `--scale` moves all of them together, which
 * is the whole reason that token exists.
 *
 * **The column has a maximum on any window and not only a wide one.** The rule
 * further down keys on `min-width: 700px`, which is the right question for a
 * browser and the wrong one here — a Mac window is dragged to whatever size, and
 * a list of 44 pt rows a metre wide is not a wider list, it is the same list
 * with a metre of nothing after each name. */
:root[data-runtime="mac"] {
  /* **A rank and a half over the phone — S3 in specs/design/mac-view.html**, and
   * it is the default rather than the only value. It was 1.2, chosen when the
   * Mac layout was written on the reading that a desk is further away than a
   * hand; the report says 1.2 is not enough.
   *
   * **1.5 and not 1.35, because the step between them is free.** Driven at both:
   * the same six rows fit a 900 px window either way, so the larger costs
   * nothing in what is on screen. 1.7 is where it starts costing — five rows —
   * which is why that is the value behind a press rather than the one everybody
   * gets. */
  --scale: 1.5;

  --safe-t: 0px;
  --safe-b: 0px;
  --safe-l: 0px;
  --safe-r: 0px;
}

/* **Text size, and it is the Mac's alone — F1 in specs/design/mac-view.html.**
 * Three values under Theme in Appearance, written through `js/textsize.js`, and
 * `--scale` is the one number all three move: every rank in `tokens.css` is
 * drawn from it, so a size is one token and never a second copy of the scale.
 *
 * **`large` is both the default and the absence.** The attribute is stamped for
 * all three, and a root that has not been stamped yet — the frame before
 * `applyRuntime` runs — reads as `large` off this block rather than as nothing.
 *
 * **Gated on `data-runtime="mac"`, and the gate is the point.** A phone has
 * Dynamic Type and Display Zoom, which already scale this application through
 * this same token; a second control competing with the system's is one that will
 * come to disagree with it. The setting is not offered there and could not apply
 * there if it were. */
:root[data-runtime="mac"][data-textsize="regular"] { --scale: 1.2; }
:root[data-runtime="mac"][data-textsize="larger"] { --scale: 1.7; }

:root[data-runtime="mac"] #app {
  /* 460 px was the phone's measure on a desktop; at `--scale` it is this, so the
   * line length is the one the design was drawn against either way. */
  max-width: 560px;
  margin: 0 auto;
  border-left: 1px solid var(--line);
  border-right: 1px solid var(--line);
  box-shadow: var(--shadow);

  /* **`100%` and not `100dvh`, and this is what was cutting the tab bar in
   * half.** `dvh` exists for a browser whose toolbars come and go, and in a
   * WebView there are none — what it resolves against there is the *scene*,
   * which on a Mac includes the strip the window's title bar occupies. So `#app`
   * came out about fifty points taller than the window can show, the column is
   * bars-then-scroller-then-bar, and the bar at the far end is what fell off the
   * bottom. `height: 100%` resolves against the WebView's own frame, which is
   * the measurement that is true here.
   *
   * It survived the first round because zeroing the safe area fixed the top of
   * the same symptom and looked like it had fixed all of it.
   *
   * **And `100%` was not the end of it either**: the bar goes on disappearing
   * when the window is *resized*, which is the report this third round answers.
   * `100%` and `dvh` are both percentages of a viewport the WebView reports, and
   * a percentage cannot be more right than the number under it. `--app-h` is the
   * visible viewport measured in `runtime.js` and re-read on every resize — the
   * other question rather than a fourth phrasing of the same one. The `100%`
   * stays as the floor, for the frame before a module has run. */
  height: 100%;
  height: var(--app-h, 100%);
}

/* The window's own title bar is directly above, where a status bar used to be,
 * and eight pixels under a piece of system chrome reads as a title that has been
 * shoved up against it. */
:root[data-runtime="mac"] .navbar { padding-top: 14px; }

/* ── The rail and the split ──────────────────────────────────────────────────
 *
 * S1a and S2b in specs/design/mac-window-2.html, off `data-layout` — three bands
 * stamped by `watchLayout` in `runtime.js`, and everything either band changes
 * is here.
 *
 * **The surplus is spent by dividing and never by stretching.** A window at
 * 1076 px had 516 px of page painted either side of a 560 px column and a
 * thousand more below a filtered list; the cap that produced that is *right* —
 * a list of 44 pt rows a metre wide is the same list with a metre of nothing
 * after each name — and what it never decided is what happens to the room it
 * declines to use. So the column stays 560 and there are simply more of them.
 *
 * **The rail is the same markup as the tab bar.** `renderTabBar` is untouched:
 * a `<nav class="tabbar">` of five `<button class="tab">`, restyled from a row
 * along the bottom into a column down the side. Nothing in the client learns a
 * second word for its own navigation, which is what keeps the phone's path
 * exactly as it was — and it deletes the component that has now been reported
 * cut off four times, which is a better outcome than a fifth fix. */

:root[data-layout="rail"] #app {
  flex-direction: row;
  /* The cap belongs to the *screen* once there is a rail beside it: capping the
   * whole application would cap the rail and the screen together and put the
   * page back on the outside of both. */
  max-width: none;
  border-left: 0;
  border-right: 0;
  box-shadow: none;
}

:root[data-layout="rail"] .tabbar {
  /* First in the row, and stated as `order` rather than by moving the node:
   * `paint()` appends the bar last on every layout, so the DOM order is the
   * reading order and the rail is a presentation of it. */
  order: -1;
  display: flex;
  flex-direction: column;
  gap: 4px;
  /* **The width follows the type, because the type is a setting now** — S3 · F1
   * in specs/design/mac-view.html. It was 104 px, a measured constant: T1 found
   * `Tasks/Topics` at the rail's 14.4 px to be 87.1 px in a rail with no
   * horizontal padding, and widened 88 to 104 for it. That number was right
   * about one `--scale` and there are three of them now, so a constant could
   * only ever be right about one and clip on another.
   *
   * `73px * --scale + 17px` is the same measurement stated as a rule: at 1.2 it
   * is 104.6 — the number T1 arrived at — and it holds the label with the same
   * slack at 1.5 and at 1.7. The 17 is the padding either side, which is
   * furniture and does not grow with the words. */
  width: calc(73px * var(--scale) + 17px);
  padding: 16px 0 16px;
  border-top: 0;
  border-right: 1px solid var(--line);
}
:root[data-layout="rail"] .tab {
  flex: none;
  width: calc(73px * var(--scale) - 15px);
  margin: 0 auto;
  border-radius: 11px;
  padding: 11px 2px;
}
:root[data-layout="rail"] .tab[aria-current="page"] { background: var(--ac-bg); }

/* **The gear is at the foot of the rail on the Mac — R2 in mac-view.html** —
 * where a preferences item goes on every desktop there has ever been, and it is
 * the same button the phone carries beside the sync dot.
 *
 * There *was* a `.tab:last-child { margin-top: auto }` here doing this, written
 * when Settings was the fifth tab. 1.1.10 moved Settings into the gear and
 * `:last-child` silently changed who it was about: **Work inherited the spacer
 * and sat 526 px below Meetings**, which is what was reported. The lesson is one
 * this repository keeps meeting — *a selector that names a position rather than
 * a thing goes on matching after the thing has left* — so the rule names the
 * gear, and a sixth destination could never inherit it.
 *
 * **What R2 buys, and it is not what it was chosen for:** the gear is out of the
 * navigation bar entirely here, so the measurement that keeps it off a person's
 * screen — 326 px of title with the dot alone, 286 with a second control, and
 * twelve more names truncating of 203 — does not apply on this machine. On the
 * Mac it is on every screen, a person's included. */
:root[data-layout="rail"] .navgear {
  margin: auto auto 0;
  flex: none;
}
/* On the phone the gear sits in `.navtools` beside the dot; in the rail it is a
 * child of the bar itself, so it takes a tab's own footprint rather than the
 * navigation row's. */
:root[data-layout="rail"] .tabbar .navgear { width: calc(73px * var(--scale) - 15px); }

/* **The screen runs from the rail to the edge of the window — W1.**
 *
 * It was `max-width: 560px; margin: 0 auto`, and that cap was right for as long
 * as the question was what to do with a surplus: `mac-window-2.html` answered
 * *the surplus is spent by dividing and never by stretching*, because a list of
 * 44 pt rows a metre wide is the same list with a metre of nothing after each
 * name. Driven, that is not a figure of speech — at 1580 the longest title in
 * the live record ends **970 px** before the row does.
 *
 * The ask reverses it in as many words, and the cost is stated rather than
 * discovered: a row is pressable across its whole width, so at 1096 px it is a
 * target whose name is at one end. What it buys is that the page either side —
 * 268 px at 1200, 458 at 1580 — is gone, and a seventh row fits where six did.
 *
 * **`.sheet` is named alongside `.screen`, and forgetting it is a regression
 * this file has already had once**: Settings is the one root drawn as a sheet
 * rather than a screen, and a selector naming only screens left it at 1492 px on
 * a 1580 px window. The two travel together in both directions. */
:root[data-layout="rail"] #app > .screen,
:root[data-layout="rail"] #app > .sheet {
  max-width: none;
  margin: 0;
  border-left: 0;
  border-right: 0;
}

/* ── What the split was, and why it is gone ──────────────────────────────────
 *
 * P1 in specs/design/mac-view.html. Above 980 px there was a third band: the
 * list stayed standing at a draggable 420 px and a person or an initiative
 * opened beside it, capped at 560 and centred in what was left. `split.js`, the
 * divider, its stored width, `pane-list`/`pane-detail` and the root-scoping
 * every binder had to learn all existed for it.
 *
 * **It went because it made the width two questions.** With a second pane
 * standing there is no single answer to *how wide is the main list* — there is
 * the list pane's answer and the detail's, and they disagree at every width. Two
 * bands where there were three, and "as wide as the window" means one thing.
 *
 * What it costs is stated: a person is opened, read and left, rather than picked
 * off a list that stays. The one preference in this application went with it. */

/* ── A `Said` line, and the swipe it now has ─────────────────────────────────
 *
 * E2 in specs/design/three-refinements.html. It reuses `.item` whole — the
 * layering, the slide, the two verbs behind it, the Mac's `⋯` — so there is one
 * swipe in this application and not two that have to agree. What is left here is
 * only what a note is *shaped* like, which is prose with a date under it rather
 * than a title with an age beside it.
 *
 * **The verbs were words at 12 px and are a gesture now**, which reverses C5's
 * placement and keeps its decision. C5 put them on the row rather than behind a
 * swipe so they would be found, and then drew them as the quietest thing on the
 * card at about 34 x 16 px — under the 44 pt floor, beside a date and a title in
 * 11 px mono. The guard against one kind of invisibility walked into the other,
 * and the report that followed is what says so. */
.item.said { min-height: 0; }
.item.said .itembody {
  display: block;
  width: 100%;
  text-align: left;
  padding: 11px 14px;
  border: 0;
  font: inherit;
  /* Not a button and not tappable — E3 was refused for this. A `Said` card is
   * prose you are reading, and a thumb resting on a sentence must not open an
   * editor. `revealable` is handed no `onTap` at all rather than one that
   * decides, so there is nothing here for a tap to reach. */
  cursor: default;
}
.item.said .itembody:active { background: var(--card); }
/* One hairline between notes, from the row and not from the text inside it. */
.item.said + .item.said { border-top: 1px solid var(--hair); }

/* ── What the Mac needed, and no longer does ─────────────────────────────────
 *
 * There were three rules here: a `⋯` drawn only on the desktop, the room it
 * took off every title, and the verbs behind the row drawn a fifth larger. All
 * three existed because a row's actions were behind a gesture that machine
 * could not reliably perform — three rounds of it, the last of which found the
 * `wheel` had been arriving all along with its sign inverted by a *preference*.
 *
 * A row opens on a tap now, which every machine can do, so the desktop needs no
 * way in of its own and this file goes back to not knowing which machine it is
 * drawing for. The one rule left is the type scale, which is `--scale` and is
 * not written here at all. */

/* The column is navigation bar · scroll · tab bar, and the middle one is the
 * only part that scrolls. Nothing overlaps because nothing overlays: the bars
 * are `flex: none` and the scroller takes what is left, so a list of any length
 * ends above the tab bar and starts below the navigation bar. `min-height: 0`
 * is what makes that true — without it a long list grows the column instead of
 * scrolling inside it, and the far end of the list goes under the bar. */
.screen {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
  /* **`min-width: 0` beside `min-height: 0`, and it was the missing half.** A
   * flex item defaults to `min-width: auto`, so it refuses to shrink below its
   * *content's* minimum — and on the Mac the rail makes `#app` a **row**, which
   * puts the screen on the main axis where that default bites. One
   * `white-space: nowrap` line of names inside a meeting row measured 2483 px
   * at `--scale: 1.5`, and took the whole screen to **2648 px inside a 1200 px
   * window**: the card, every row and the `✕` on each of them were drawn past
   * the right-hand edge, and the ellipsis never fired because there was nothing
   * to clip against.
   *
   * On the phone `#app` is a column, so width is the *cross* axis and the
   * screen stretches to the window whatever its content wants — which is why
   * this was invisible for as long as nobody looked at a Mac. *A layout rule
   * exercised on one axis is a rule tested on one machine.* */
  background: var(--bg);
  /* So anything a screen floats over itself is measured against the screen and
   * not against the window. The rail goes one level further in, to `.band`. */
  position: relative;
}

/* ── Navigation bar ─────────────────────────────────────────────────────── */

.navbar {
  /* The status bar is not ours to draw under, and the bar carries that itself
   * rather than being pushed down by a padded body. */
  padding: calc(8px + var(--safe-t)) calc(var(--bar-x) + var(--safe-r)) 10px
           calc(var(--bar-x) + var(--safe-l));
  display: flex;
  flex-direction: column;
  gap: 8px;
  border-bottom: 1px solid var(--line);
  background: var(--bg);
  flex: none;
}
.navrow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-height: 32px;
}
.navback {
  font-size: var(--t-body);
  color: var(--ac);
  background: none;
  border: 0;
  padding: 6px 8px 6px 0;
  cursor: pointer;
}
/* **A heading sets its own ground, because this stylesheet has no reset.**
 * Bento writes its title as an `<h1>` — right, since it is the screen's
 * heading and the outline should say so — and an `<h1>` carries the browser's
 * own `margin: 0.67em 0`. At `--t-title` on a Mac that is **30 px above and
 * 30 below**: measured, Bento's title began at y=44 where Meetings' begins at
 * 14, and the bar came out **249 px against 161**. Reported as *the title in
 * bento needs to be closer to the top, like in meeting*. The same shape as the
 * day cells coming out as grey buttons, met on a heading rather than a
 * control: the fix belongs on the class, not on the tag, so the next screen
 * that writes its title as a heading inherits it. */
.navtitle {
  font-size: var(--t-title);
  font-weight: 700;
  letter-spacing: -0.5px;
  color: var(--tx);
  margin: 0;
}
/* **The title is the control** — A1 in specs/design/inbox-readings.html. A
 * button wearing the title's own rank, in the accent because it can be
 * touched, with a chevron the size of the label rank saying so. The button
 * reset is local, because this stylesheet has no global one. */
.navpick {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  border: 0;
  background: none;
  padding: 0;
  color: var(--ac);
  font-family: var(--ios);
  cursor: pointer;
  min-width: 0;
  max-width: 100%;
}
.navpick > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.navpick .navchev { font-size: var(--t-label); color: var(--ac); flex: none; }
.navpick:active { opacity: .7; }
/* The menu: the picker's own rows, a head over the categories, the current
 * reading in the accent, the count in the mono rank at the far end. */
.mailpicker.readings .rows button.on { color: var(--ac); font-weight: 600; }
.mailpicker.readings .rowshead {
  padding: 10px 14px 4px;
  font-family: var(--mono);
  font-size: var(--t-meta);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--tx4);
  border-top: 1px solid var(--hair);
}
.navtools { display: flex; align-items: center; gap: 12px; }
.navgear {
  background: none;
  border: 0;
  color: var(--ac);
  font-size: 21px;
  line-height: 1;
  padding: 10px 0 10px 4px;
  min-width: 44px;
  min-height: 44px;
  cursor: pointer;
}

/* ── The person's breadcrumb ─────────────────────────────────────────────── */
/* E1 in specs/design/person-bar.html. One row, and the same row every other
 * screen has: the bar over a person used to be two — a back button, then an
 * avatar with the name and `8 open · 2 dormant` under it — which made it 98 px
 * where Contacts' is 50, so the bar changed height as you moved between screens
 * and the list under it jumped.
 *
 * The icon is the only thing that goes back. The name is an `h1` and is inert:
 * an iOS back label names the destination, and this names where you already
 * are. */
/* Everything before the name is spent twice over: it is chrome, and it is width
 * the name does not get. At 30 px a title is about eighteen characters wide on a
 * 390 pt screen, so the icon, the separator and the gaps are the difference
 * between "Michael Seiger" fitting and not. The back control keeps its 44 px of
 * height — the part that is actually aimed at — and gives up the width it was
 * padding out around a 23 px glyph. */
.crumb {
  display: flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
  flex: 1;
}
.crumbback {
  border: 0;
  background: none;
  padding: 0;
  margin: 0 -2px 0 -4px;
  width: 30px;
  min-height: 44px;
  display: grid;
  place-items: center;
  color: var(--ac);
  cursor: pointer;
  flex: none;
}
.crumbicon { width: 23px; height: 23px; display: block; }
/* A separator and not a chevron: it is punctuation between two names, so it is
 * set at the size punctuation is rather than at the size of what it separates. */
.crumb .sep {
  color: var(--tx4);
  font-size: var(--t-task);
  line-height: 1;
  flex: none;
}
/* The name is a **title**, at the size `Topics` and `Contacts` are, because a
 * person is a place in this application and not a label on one. It was a rank
 * below that, which is what the drawn alternative E1 proposed and what E3
 * argued against; E3 lost at the time only because the create button was still
 * in this row and 30 px would not fit beside it. The button floats now, so the
 * row has the width, and the argument that was right all along can be taken.
 *
 * Ellipsis and never a second line: a bar that grows a line for some people is
 * the height problem this row exists to have solved. */
.crumb .who {
  margin: 0;
  font-size: var(--t-title);
  font-weight: 700;
  letter-spacing: -0.5px;
  color: var(--tx);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

/* The sync dot: three states and no others — specs/ux.md §6,
 * specs/design/topics-refinements.html part four.
 *
 * It was a pill and said `up to date 09:12` beside the title, which was true and
 * almost never news, and cost a third of the navigation row on every screen. The
 * state that matters is legible from the colour; the words are behind a press.
 *
 * A 44 px target around a 9 px dot: the dot is what is read and the button is
 * what is aimed at, and those are not the same size. */
.syncdot {
  border: 0;
  background: none;
  padding: 0;
  min-width: 44px;
  min-height: 44px;
  display: grid;
  place-items: center;
  cursor: pointer;
}
.syncdot .dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--tx4);
  transition: box-shadow 140ms ease-out;
}
.syncdot[data-state="up-to-date"] .dot { background: var(--ok); }
.syncdot[data-state="pending"] .dot { background: var(--warn); }
/* Working: a halo rather than a spinner. A spinner says the same thing for a
 * stalled request and a finished one, and the line underneath is already
 * narrating — this only has to say that the press landed. */
.syncdot.busy .dot { box-shadow: 0 0 0 4px var(--ac-bg); }

/* ── Settings, in the navigation row — H2 ───────────────────────────────────
 *
 * The sync dot's own 44 pt target around a 22 px drawing, so the two controls
 * beside each other weigh the same and neither is the one you miss. This is not
 * the 19 px gear in the corner of one screen that this application reversed
 * when it made Settings a tab: that was one screen and this is every screen
 * with a `.navtools`, which is all of them but a person's. */
.navgear {
  border: 0;
  background: none;
  padding: 0;
  min-width: 44px;
  min-height: 44px;
  display: grid;
  place-items: center;
  color: var(--tx3);
  cursor: pointer;
}
.navgear .gearico { width: 22px; height: 22px; display: block; }
/* Open, it is the accent — the one thing on the row saying you are inside
 * something — and it is a cross, because the button says what pressing it does
 * and what it does here is leave. */
.navgear.open { color: var(--ac); }
.navgear .gearx { font-size: 23px; line-height: 1; }

/* What it says while it works, and for four seconds after. Absent the rest of
 * the time, which is the whole point of having taken the words off the dot. */
.progressline {
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx3);
  display: flex;
  align-items: baseline;
  gap: 7px;
  letter-spacing: 0.02em;
  padding-top: 2px;
}


/* **The verb on that line, where what it said can be taken back.** Asked for
 * as an undo on Close. It is the accent because it can be touched, and it is
 * on this line rather than in a bar of its own because this is already where
 * the application speaks — a second surface saying one word is a second thing
 * to learn. 44 pt is reached with a pseudo-element rather than a box, so a
 * line that says nothing undoable is exactly the height it was. */
.progressact {
  position: relative;
  margin-left: auto;
  flex: none;
  background: none;
  border: 0;
  padding: 0 2px;
  color: var(--ac);
  font: inherit;
  font-size: var(--t-meta);
  font-weight: 600;
  cursor: pointer;
}
.progressact::after { content: ""; position: absolute; top: -14px; right: -10px; bottom: -14px; left: -10px; }
.progressact:active { opacity: 0.6; }

.progressline .mark { color: var(--ok); flex: none; }
.progressline.busy .mark { color: var(--ac); }
.progressline .mark { transition: color 140ms ease-out; }

/* ── The bar ────────────────────────────────────────────────────────────── */

/* Three roots and therefore a bar — specs/ux.md §4, specs/design/bottom-bar.html.
 * The active tab is filled rather than tinted, because at 25 pt two shades of
 * the same teal is a weak signal and for some eyes no signal at all.
 *
 * It arrived with two tabs and said so: "a bet on the third tab, and if the
 * third never arrives the honest move is to take the bar back out". Settings is
 * the third, and it was the one thing in the application reachable only through
 * a 19 px gear in the corner of one screen — which is to say the one thing you
 * had to already know where to find.
 *
 * It runs to the physical bottom of the screen and carries the home indicator's
 * inset as its own padding, so the card colour goes under the indicator and the
 * labels stay clear of it. Insetting the application instead left a band of the
 * sunk page below the bar, which is a bar that has not reached the bottom.
 */
.tabbar {
  flex: none;
  display: grid;
  /* One equal column per tab, however many there are. This read `repeat(3, 1fr)`
   * and the fourth tab wrapped the bar onto a second row — a number written
   * where the children already carry it. */
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  background: var(--card);
  border-top: 1px solid var(--line);
  padding: 0 var(--safe-r) var(--safe-b) var(--safe-l);
}
.tab {
  min-height: 44px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  padding: 7px 4px 6px;
  border: 0;
  background: none;
  font: inherit;
  font-size: var(--t-label);
  color: var(--tx4);
  cursor: pointer;
}
.tabicon { width: 25px; height: 25px; display: block; }
.tab[aria-current="page"] { color: var(--ac); }

/* ── Contacts ───────────────────────────────────────────────────────────── */

/* Search on Topics as well as on Contacts, and the segmented sort under it —
 * A1 in specs/design/topics-refinements.html. Both always visible: the screen is
 * opened to find a person, and a control that has to be discovered is a control
 * that is not used. It costs about 96 pt of the first screenful, which is a
 * person and a half, and buys back a list that can be aimed at.
 *
 * The segmented control is Appearance's, unchanged, so the two agree — but at
 * `--t-task` here rather than filling a row of Settings. */
.navbar .segmented { margin-top: 2px; }
.navbar .segment { min-height: 34px; font-size: var(--t-task); }

/* The field and the cross that clears it — B1 in
 * specs/design/refinements-after-0150.html. Relative so the button can sit
 * inside the field's trailing end rather than beside it, which would make the
 * field change width as text arrived. */
.searchwrap { padding-top: 8px; position: relative; }
/* **The native one is hidden so that both machines get the same control.**
 * WebKit renders this on macOS and nowhere else, so without this line the Mac
 * would have two crosses and the phone would still have none. */
.searchfield::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
.searchclear {
  position: absolute;
  top: 8px;
  right: 0;
  /* 40 px tall to match the field and wide enough to be a target, with the
   * glyph drawn at the size it is legible and the rest taken as reach. */
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border: 0;
  background: none;
  padding: 0;
  color: var(--tx4);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}
.searchclear:active { color: var(--tx2); }
.searchfield {
  width: 100%;
  min-height: 40px;
  /* The trailing padding is the clear button's width, always — not only when it
   * is there. A field whose text shifts as the first character is typed is one
   * that looks like it lost its place. */
  padding: 8px 40px 8px 12px;
  border: 0;
  border-radius: 9px;
  background: var(--inset);
  color: var(--tx);
  font: inherit;
  font-size: var(--t-body);
}
.searchfield::placeholder { color: var(--tx4); }

/* The band between the two bars, and the only region a screen has that is
 * neither of them. It exists so the rail has something to be positioned against
 * that is exactly the list's own height: measured against the screen, a rail
 * long enough to hold twenty-six letters reaches up over the navigation bar. */
.band {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  position: relative;
}
.band > .scroll { flex: 1; }


/* A contact that arrived from a mention and has never been filled in. It is the
 * cockpit's flag; this is the screen where it can be acted on. */
.chip.bare { color: var(--warn); border-color: var(--warn); }


/* ── The page and its cards ─────────────────────────────────────────────── */

/* The gutter is `--gutter` and not the 13 px it was. Cards that close to the
 * edge make the page a frame around a table rather than something the content
 * floats on, which is the whole of the arrangement; at 18 px the card sits where
 * the platform's own grouped list puts it, and a row's text lands 34 px in,
 * where iOS puts that too.
 *
 * The bottom padding is what keeps the last card off the tab bar. It is a
 * margin and not a fix for an overlap — the column already guarantees there is
 * none — but a list that ends flush against a bar reads as one that has been
 * cut off. */
.scroll {
  flex: 1;
  /* The same default one level in. It is the box every card measures itself
   * against, so it is pinned here rather than left to inherit whatever the
   * screen happens to settle on. */
  min-width: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  padding: 0 calc(var(--gutter) + var(--safe-r)) 16px calc(var(--gutter) + var(--safe-l));
  background: var(--bg);
}

/* A screen with no tab bar under it — the gate, and Person before the bar
 * existed — is the last thing in the column, so the home indicator's inset is
 * its scroller's to spend. Written as a position rather than as a class so that
 * adding a screen cannot forget it. */
.screen:last-child .scroll { padding-bottom: calc(16px + var(--safe-b)); }

/* ── The floating create button ──────────────────────────────────────────── */
/* P2 in specs/design/person-bar.html. One shape, one place, on both screens
 * that make something.
 *
 * Positioned against `.screen`, which ends where the tab bar begins, so it sits
 * *above* the bar rather than over it and needs no arithmetic about the bar's
 * height. What it does cover is a row — that is the honest cost of the shape,
 * and the scroller spends enough at the bottom that the last row can always be
 * scrolled clear of it. */
/* **On a screen with a jump rail the button steps inwards.**
 *
 * Contacts kept its `+` in the navigation bar for exactly one reason: this
 * corner was the rail's. The obvious fix was to shift the rail up so it centres
 * above the button, and it does not work — twenty-six letters are 329 px, the
 * band on a handset is under 400, and there is no 74 px strip to give away. It
 * came out overlapping by 3 px on an iPhone 14 and by 19 on a shorter screen,
 * which is the kind of number that is fine until somebody's phone is not one of
 * the two you measured.
 *
 * So the rail keeps the extreme edge — it is an index you run a thumb down, and
 * it has to be reachable without aiming — and the button moves in by the rail's
 * own width plus a gap. Nothing vertical to get wrong, and it holds at any
 * height. */

.fab {
  position: absolute;
  right: calc(20px + var(--safe-r));
  bottom: 20px;
  z-index: 3;
  width: 54px;
  height: 54px;
  border: 0;
  border-radius: 50%;
  background: var(--ac);
  color: var(--bg);
  font-family: var(--ios);
  font-size: 30px;
  font-weight: 300;
  line-height: 1;
  display: grid;
  place-items: center;
  box-shadow: var(--shadow);
  cursor: pointer;
}
.fab:active { background: var(--ac-lite); }
/* **A pill to the left of the circle** — H2 in
 * specs/design/meetings-rows-and-the-bar.html. The same height and the same
 * ground; the stopwatch is the tab's own and the word says the rest. Placed
 * off the first button's own right, so the two move together wherever that
 * one goes. */
.fab.meet {
  right: calc(20px + var(--safe-r) + 54px + 12px);
  position: absolute;
  width: auto;
  border-radius: 27px;
  padding: 0 18px 0 14px;
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: inherit;
  font-size: var(--t-body);
  font-weight: 600;
}
.fab.meet .fabicon { width: 24px; height: 24px; display: block; stroke-width: 1.9; }
.screen:has(.fab) .scroll { padding-bottom: 88px; }

/* The label above a card, in two forms: a `.lbl` and a count on Topics and
 * Person, and on Contacts a bare heading carrying its letter as its own text.
 * The metrics therefore sit here rather than on `.lbl`, and `.lbl` restates only
 * what it changes — otherwise the letters inherit nothing, come out as a
 * paragraph at the browser's own size, and bring a paragraph's margins with
 * them, which is what they had been doing. */
.sechead {
  display: flex;
  align-items: baseline;
  gap: 9px;
  margin: 0;
  padding: var(--sec-space) 4px 6px;
  font-size: var(--t-label);
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--tx3);
}
.sechead .lbl { flex: 1; }
/* In the label and in parentheses — `TOPICS (8)`. Pushed to the far edge it read
 * as the second column of a table with no other columns, and it was the only
 * thing on the screen a label had to be scanned across to. Beside the word it is
 * part of the phrase, which is what it always meant. Mono for the digits, so a
 * count going from 9 to 10 does not shift the word before it. */
.sechead .ct {
  font-family: var(--mono);
  font-size: var(--t-label);
  font-variant-numeric: tabular-nums;
  color: var(--tx4);
}

/* **A section header that is a name rather than a label** — A1 in
 * specs/design/two-levels.html.
 *
 * Reported from the Mac as *the double grouping is hard to read*, and measured
 * before it was changed: the header and the band inside it were the same size,
 * the same weight and the same case on both machines — 12 px / 600 / uppercase
 * on the phone, 14.4 px on the Mac, letter-spacing within a tenth of a point.
 * The only strong separation was the fill, and the fill was on the **inner**
 * one, so the hierarchy was drawn upside down: the tinted bar read as the start
 * of a section and the person's name read as a caption belonging to the block
 * above it.
 *
 * So the outer level stops speaking the label's dialect. Body size, sentence
 * case, full ink — a name, set the way this application sets a name everywhere
 * else. **It costs no height**: the header was already a 44 px box and this
 * changes what is in it. The count stays in parentheses and stays mono, which
 * is the decision `TOPICS (8)` already settled and this does not reopen. */
.sechead.named {
  font-family: var(--ios);
  font-size: var(--t-head);
  letter-spacing: -0.01em;
  text-transform: none;
  color: var(--tx);
}
.sechead.named .ct { font-size: var(--t-label); }
/* The note goes with the count — a name's rank is for the name, and a fact
   about it set at the same size is a second name. Without this the Inbox's
   meeting folds ran the date and the room at 20 px and took three lines. */
.sechead.named .ro { font-size: var(--t-label); }
/* The mark grows with the name it sits before, or it reads as a speck. */
.sechead.named .hdrmark { width: 14px; height: 14px; border-color: var(--tx3); }

/* **The star on a row at rest** — S3 · M1 in specs/design/hitlist.html.
 *
 * A sibling of the row's body, because the body is a `<button>` and nothing may
 * nest inside one — which is also why it is on the trailing edge rather than at
 * the end of the flowing line M1 drew it in. It does M1's job from here: a
 * starred topic says so on every screen that draws it.
 *
 * **Drawn on every row and filled only on the starred ones**, which is what one
 * press costs: a control visible only where the state already is cannot be used
 * to add the state. The outline is `--tx4`, the quietest rank that can be read.
 *
 * The 44 pt is taken in a pseudo-element that bleeds past the 24 px box — the
 * share glyph's arithmetic. A real 44 px box would push every title in. */
.rowstar {
  position: absolute;
  right: 12px;
  /* **On the first line, not on the row's middle**, and by the same arithmetic
   * the mark uses — a title runs to four lines here, and anything centred on
   * the row drifts away from the words as they grow. Held in one expression so
   * the two follow the type scale onto the Mac together. */
  top: calc(var(--row-pad) + (var(--t-row) * 1.4 - 20px) / 2);
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--tx4);
  display: grid;
  place-items: center;
  cursor: pointer;
  z-index: 2;
}
.rowstar::after { content: ""; position: absolute; inset: -10px; }
.rowstar svg { width: 20px; height: 20px; display: block; }
/* **The accent and not amber**, which is this application's own rule rather
 * than a preference. Amber survives only where it is about the *application* —
 * the sync dot with something queued, a rejection — and a star is about the
 * data. What the accent means here is *you can touch this*, and the star is the
 * one mark on a row at rest that can be. */
.rowstar.on { color: var(--ac); }
.rowstar:active { opacity: .6; }
/* **The title stops before the star starts, with a gap.** Measured rather than
 * guessed: at 34 px the text box ended at 338 and the star began at 336, so a
 * long title ran under it by two pixels. 44 leaves eight. */
.item:has(.rowstar) .itembody { padding-right: 44px; }
/* The trailing edge is spoken for in the modes that own it, and behind a swipe
 * the body slides over the star — so it stands down rather than showing
 * through. */
.item.editing .rowstar,
.item.picked .rowstar,
.reordering .rowstar { display: none; }

.card {
  background: var(--card);
  border-radius: var(--radius);
  overflow: hidden;
}

/* ── People ─────────────────────────────────────────────────────────────── */

.row {
  display: flex;
  gap: 12px;
  padding: 10px 16px;
  align-items: center;
  min-height: 56px;
  background: transparent;
  width: 100%;
  text-align: left;
  border: 0;
  color: inherit;
  font: inherit;
  cursor: pointer;
  position: relative;
}
/* Past the avatar, which is where the text starts. */
.row + .row::before {
  content: "";
  position: absolute;
  top: 0;
  left: 60px;
  right: 0;
  border-top: 1px solid var(--hair);
}
.row:active { background: var(--hover); }
.row .body { flex: 1; min-width: 0; }
.row .ttl {
  display: block;
  font-size: var(--t-body);
  color: var(--tx);
  letter-spacing: -0.1px;
}
.row .meta {
  display: block;
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx3);
}
/* A person with nothing open says nothing, and still spends the line.
 *
 * `.meta` is a block, so an empty one generates no line box and the body loses
 * its second line — and `.row` centres its body, so the name rises to the middle
 * and sits above every name around it. The row's `min-height` cannot help: it
 * keeps the row's height and is exactly what lets the content move inside it.
 *
 * A non-breaking space through `::before` rather than in the text, so the thing
 * holding the space is in the stylesheet where the height is decided, and
 * `screens.js` writes an honestly empty string. It is `:empty`, so a row that
 * has something to say is untouched. */
.row .meta:empty::before { content: "\00a0"; }
.row .chev { color: var(--tx4); font-size: var(--t-task); flex: none; }

/* ── Topics ─────────────────────────────────────────────────────────────── */
/* Title, age and co-participants are one flowing block rather than two lines.
 * The age reads as part of the sentence — "…for FY26 plan, 6 d" — and the row
 * loses the line it used to spend on a meta strip. */

/* A row is now two layers: the body, which slides, and the two verbs behind it.
 * `overflow: hidden` is what keeps the buttons out of sight until they are
 * wanted. */
.item {
  min-height: 44px;
  position: relative;
  overflow: hidden;
  background: var(--card);
  /* **The row's own vertical padding is `--row-pad` in `tokens.css`.** The ring
   * is a *sibling* of the body — nothing may nest inside a `<button>` — so it
   * cannot inherit the body's padding and has to be told where the first line
   * starts. It was told with a hand-computed `21px`, which was right for exactly
   * one of the three readings this list has; the token is what made it right for
   * all of them, and moving it to the root is what lets one block hold every
   * value the type-and-space tuner moves. `.item.tight` still overrides it here,
   * because that is a fact about a reading rather than about the application. */
}
/* 20 px top and bottom rather than 12 — option S3 in
 * specs/design/topic-spacing.html, chosen over the smaller S2.
 *
 * The rows were tight against each other: at 12 px a two-line title and the
 * note under it left about eight points of air between one topic and the next,
 * which is *less than the gap inside a single row* between its title and its
 * note — so four lines of similar text read as one block with a hairline in it
 * rather than as two topics.
 *
 * S2's 16 px was the smallest step that inverts that. 20 px is the one that
 * makes it unmistakable, and it costs a row: four topics on a screenful where
 * 12 px showed five. That is the trade taken deliberately — a list you can read
 * at a glance is worth more than one more row you have to. */
.itembody {
  position: relative;
  z-index: 1;
  display: block;
  width: 100%;
  padding: var(--row-pad) 16px;
  border: 0;
  background: var(--card);
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.itembody:active { background: var(--hover); }

/* **Compact is 10 px and at most two lines** — B3 in
 * specs/design/mail-title-and-compact.html, reported as *a lot of space between
 * topics*.
 *
 * The 20 px above is right and is not being reopened: S3 chose it over 16 for
 * the *full* row, where a title, a summary and a note stack and the gap between
 * two topics has to beat the gap inside one. **Compact has one line per row, so
 * there is no gap inside one to beat** — the argument that bought the 20 px does
 * not carry into the reading that drops everything it was separating.
 *
 * 10 px puts the row at the platform's 44 pt floor exactly, which is where it
 * stops: a topic row is tapped, and a tap target under 44 pt is one you miss.
 * Measured at 390 pt — 64 px a row before, 44 after, and a 669 px scroller goes
 * from ten rows to fifteen.
 *
 * The clamp is the half that answers the *unevenness* rather than the height. A
 * wrapping title cost a whole extra line, so the list read as blocks of
 * different sizes; two lines is the ceiling, and past it the title ends in an
 * ellipsis. Two rather than one because a title cut at four words is a row you
 * cannot identify, which is the one thing compact must not cost — it is the
 * reading you use to *find* a row. */
.item.tight { --row-pad: 10px; }
.item.tight .ttl {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* **Dense is the padding and not the clamp** — the Today round. Compact answers
 * *how many titles fit*, which is why it cuts the row's content down to one; a
 * day's screen answers *what is on today*, so nothing may leave the row and
 * what is left to give back is the air around it. 14 px to 10 is the same
 * arithmetic compact already did and lands on the same 44 pt floor. */
.item.dense { --row-pad: 10px; }

/* ── A meeting on Today — A1 · B1 · C1 · D1 · E1 ────────────────────────────
 *
 * **The clock is a column**, which is the whole of what the reported spill was
 * about: it used to be a span inside the title, so a wrapped line started back
 * at the card's padding and ran 37.6 px underneath it at 390 pt. Nothing shares
 * its column now, so nothing can wrap under it.
 *
 * The row is a container with a button inside it rather than a button, because
 * the `✕` is a control and nothing may nest inside a `<button>` — the rule the
 * star on a topic row already pays for. */
.evrow { position: relative; display: flex; align-items: stretch; }
.evrow + .evrow::before {
  content: "";
  position: absolute;
  top: 0;
  left: 16px;
  right: 0;
  border-top: 1px solid var(--hair);
}
.evbody {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px 4px 12px 16px;
  min-height: 56px;
  background: transparent;
  border: 0;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.evbody:active { background: var(--hover); }
.evbody .body { flex: 1; min-width: 0; }
.evbody .ttl {
  display: block;
  font-size: var(--t-row);
  color: var(--tx);
  letter-spacing: -0.1px;
}
/* One line of names, cut from the far end — B1. The row's height stops
 * depending on how many people were invited, which is what it was spending 24
 * px of every four-person meeting on. */
.evwho {
  display: block;
  margin-top: 2px;
  font-size: var(--t-task);
  color: var(--tx3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.evbody .chev { align-self: center; color: var(--tx4); font-size: var(--t-task); flex: none; }

/* The clock keeps the mono rank the meta line under it used to be, so the time
 * still reads as a fact about the day rather than as part of the subject. It is
 * `tabular-nums` because it is now a column of them. */
.evclock {
  flex: none;
  font-family: var(--mono);
  font-size: var(--t-meta);
  font-variant-numeric: tabular-nums;
  color: var(--tx3);
  padding-top: 2px;
}

/* **Now** — C1. The row takes a step of ground and the clock alone takes the
 * accent; nothing moves, so the hour turning does not shuffle the list. Not a
 * left stripe: that is the shape the dormancy border wore, and it went because
 * a mark saying *this one, though* is the application deciding what matters.
 * Whether it is 14:07 is a fact rather than a judgement, which is why this one
 * is allowed at all. */
.evrow[data-now] { background: var(--inset); }
.evrow[data-now] .evclock { color: var(--ac); font-weight: 600; }
.evrow[data-now] .ttl { font-weight: 600; }

/* **Running** — D1, and the dot is the merge bar's own beat: the one mark this
 * application already has for *something is live right now*. It is the state
 * you started, where `data-now` is the state the clock is in, so the two are
 * deliberately different marks. */
.evlive { color: var(--ac); font-weight: 600; }
.evlive .beat {
  display: inline-block;
  width: 7px;
  height: 7px;
  margin-right: 6px;
  border-radius: 50%;
  background: var(--ac);
  animation: beat 1.25s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) { .evlive .beat { animation: none; } }

/* **The dismiss control** — E1's box, **A3's mark**. 30 px drawn and 44 pt
 * reached, the share glyph's arithmetic, so the row does not grow eleven pixels
 * for a control it mostly does not use. Drawn at rest and never on hover: a
 * control that appears once the pointer is already on it is one found by
 * accident.
 *
 * The glyph inside it is a crossed-out eye rather than the typed `✕` it wore
 * for two rounds — a character in the system font beside a screen of drawn
 * strokes. 18 px inside the 30, and **fixed like the box it sits in**: the type
 * ranks scale and this control's box has never been one of them, so a scaled
 * glyph in an unscaled box is the mismatch `.av.sm` paid for. */
/* **A meeting's facts, as a line rather than a band** — A1 in
 * specs/design/meeting-bar.html, built as the floor.
 *
 * What it replaced was 42 px of a 123 px navigation bar at 390 pt — a third of
 * it, spent on three facts, never scrolling away, and wearing the accent on a
 * row where nothing could be touched. This is the slot the sync line already
 * writes in, at the rank every other fact in this application is written in,
 * and it costs about fifteen pixels.
 *
 * The separator is the meta line's own, so a field that is absent takes its
 * `·` with it and a meeting with two facts reads as two facts. */
.meetfacts {
  padding: 2px var(--gutter) 0;
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx4);
}
.meetfacts span + span::before { content: " · "; }
/* **A2: the source keeps the pill it wears on the day row.** One fact drawn
 * two ways is one fact somebody has to learn twice, so `.src` is the same
 * object on both screens — the day list's rule, restated here rather than
 * written again. The separator in front of it goes for the reason it goes
 * there: a `·` before a pill reads as though the pill were one more item in
 * the list rather than a mark at the end of it. */
.meetfacts .src {
  display: inline-block;
  margin-left: 7px;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--ac-bg);
  color: var(--src-ink);
  font-size: calc(var(--t-meta) * 0.9);
  letter-spacing: .06em;
  text-transform: uppercase;
  vertical-align: 1px;
}
.meetfacts span + span.src::before { content: none; }
/* The live dot leads the line rather than joining the list of facts: it is not
 * one of them, it is what the first one is about. */
.meetfacts .beat + span::before { content: none; }
.meetfacts .beat { margin-right: 6px; }

.evx {
  flex: none;
  align-self: center;
  width: 30px;
  height: 30px;
  /* Asked for as *a bit more margin behind the eye*: at 8 px the glyph sat
   * against the edge of the card, which reads as a control that ran out of
   * room rather than one placed there. The 44 pt reach is a pseudo-element
   * that bleeds outwards, so this is the drawn box moving and never the
   * target. */
  margin-right: var(--gutter);
  display: grid;
  place-items: center;
  background: none;
  border: 0;
  border-radius: 8px;
  color: var(--tx4);
  font-size: var(--t-task);
  line-height: 1;
  cursor: pointer;
  position: relative;
}
.evx::after {
  content: "";
  position: absolute;
  inset: -7px;
}
.evx:active { background: var(--hover); color: var(--tx2); }
.evxicon { width: 18px; height: 18px; display: block; }

/* The ones waved off, a rank quieter and behind a fold. Nothing is destroyed —
 * pressing one puts it back — so the quiet is what says *dealt with* rather
 * than an absence. */
.card.dismissed .ttl { color: var(--tx3); }
.card.dismissed .evclock, .card.dismissed .evwho { color: var(--tx4); }

.said { display: block; min-width: 0; }


.cornerglyph { width: 18px; height: 18px; display: block; }

/* **Which kind of proposal a read-back row is** — W3, and it is a word rather
 * than a colour because there are two kinds and which one this is decides what
 * the verb under it says. Nothing here is colour-coded; a kind is a fact about
 * the row rather than a judgement on it. */
.kindmark {
  font-family: var(--mono);
  font-size: var(--t-meta);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ac);
  margin-right: 7px;
}

.verbglyph { width: 19px; height: 19px; display: block; flex: none; }
.actglyph { width: 23px; height: 23px; display: block; }

/* The two verbs a `Said` line has, back on the row — and this is their third
 * position. Twelve-pixel words beside an eleven-pixel date was one kind of
 * invisible; behind the swipe, at a proper size with nothing saying they were
 * there, was the other. 30 px drawn and 44 pt tapped, which is the trade the
 * section header's share glyph already makes. */
.saidacts { display: flex; align-items: flex-start; gap: 2px; flex: none; padding-top: 2px; }
.saidact {
  position: relative;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border: 0;
  border-radius: 8px;
  background: none;
  color: var(--tx3);
  cursor: pointer;
}
/* The rest of the 44 pt, taken in a pseudo-element so the row does not grow by
 * fourteen pixels to hold a target nobody can see. */
.saidact::after { content: ""; position: absolute; top: -7px; right: -7px; bottom: -7px; left: -7px; }
.saidact:active { background: var(--hover); }
.saidact.drop { color: var(--bad); }

/* ── Meeting Mode ───────────────────────────────────────────────────────── */
/* **The session bar is gone** — A1 in specs/design/meeting-bar.html. It was
 * the accent under the navigation bar and it carried the whole of Meeting
 * Mode's announcement, which was the right shape while a session was a mode
 * you entered. It stopped being one, and what was left was 42 px of a 123 px
 * navigation bar at 390 pt, spent on three facts and wearing a tint that in
 * this application means *you can touch this* — of everything on it only
 * `End` could be. `.meetfacts` is the line that replaced it, and `.endit`
 * below is the verb that moved rather than went. */
.livedot {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ac);
  animation: beat 1.7s ease-in-out infinite;
}
@keyframes beat { 0%, 100% { opacity: 1 } 50% { opacity: .25 } }
@media (prefers-reduced-motion: reduce) { .livedot { animation: none } }
.sesslabel { text-transform: uppercase; }
/* Fixed width, so the bar does not twitch every second as the digits change. */
.sessclock { font-variant-numeric: tabular-nums; }
.sessof { color: var(--tx3); }
.sessend {
  margin-left: auto;
  flex: none;
  padding: 4px 11px;
  border: 1px solid var(--line-hard);
  border-radius: 7px;
  background: transparent;
  color: var(--tx2);
  font: inherit;
  font-size: var(--t-meta);
  cursor: pointer;
}
.sessend:active { background: var(--hover); }

/* The two small buttons a session puts on every row — B1.
 *
 * A sibling of the body and never a child: the body is a `<button>` and a button
 * may not contain another one. Absolutely positioned rather than laid out beside
 * it, so the body keeps being the full-width sliding layer the swipe needs — the
 * width it gives up is bought back with padding, which is the one way to have
 * both. */
/* Aligned to the **top** and not centred, which is the whole of B1's argument
 * made good: a control read once is then carried by its position, and a pair
 * that drifts down as a row grows a second line and a note has no position to be
 * carried by. 14 px puts the 30 px box on the middle of the first line, whose
 * own 20 px of padding starts it. */
.rowverdicts {
  position: absolute;
  z-index: 2;
  top: 0;
  right: 16px;
  bottom: 0;
  display: flex;
  align-items: flex-start;
  padding-top: 14px;
  gap: 6px;
  pointer-events: none;
}
.item .rowverdicts > * { pointer-events: auto; }
/* 66 px: two 30 px boxes and the gap. Paid by every title, and only while a
 * session runs — specs/meeting-mode.md §4. */
.session .item .itembody { padding-right: 82px; }
.verdict {
  position: relative;
  flex: none;
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--line-hard);
  border-radius: 8px;
  background: var(--card);
  color: var(--tx3);
  cursor: pointer;
}
/* 30 px drawn, 44 pt tapped. The rest of the target is taken in a pseudo-element
 * that bleeds into the gap, which is what `Share` does in the section header and
 * for the same reason: a real 44 px box takes the row from 52 to 60 and costs a
 * visible topic on every screenful. */
.verdict::after {
  content: "";
  position: absolute;
  left: -7px;
  right: -7px;
  top: -7px;
  bottom: -7px;
}
.verdict.discussed { color: var(--ac); border-color: var(--ac); }
.verdict:active { background: var(--hover); }
.verdict.on { color: var(--on-ok); background: var(--ac); border-color: var(--ac); }
.verdict.close.on { background: var(--tx3); border-color: var(--tx3); color: var(--card); }
/* The partner of a given verdict dims rather than disappearing, so the pair keeps
 * its width and no title reflows under the thumb. */
.item.decided .verdict:not(.on) { opacity: .35; }
.verdictglyph { width: 18px; height: 18px; display: block; }

/* A decided row stays until the session ends — three reasons in
 * specs/meeting-mode.md §4, the first being that a row which vanishes under the
 * thumb hands the next tap to whatever slides up into it. */
.item.decided .ttl { text-decoration: line-through; color: var(--tx4); }
.item.decided .said { color: var(--tx4); }
.item.focused { background: var(--hover); box-shadow: inset 3px 0 0 var(--ac); }
.item.focused .itembody { background: transparent; padding-bottom: 6px; }

/* What was said, on the row, for this conversation only. The whole thread is in
 * the edit sheet; a row carrying a year of it would be a screen of history on a
 * list whose job is what is outstanding. */
.notesaid,

/* What was said with somebody, across every topic they are on — the section
 * that answers the half of "my notes disappear" the row cannot: only OPEN
 * reaches the phone, so a topic with a verdict takes its thread off every list.
 *
 * Read as prose and not as a table: the text first at the rank a note is, and
 * the day and the topic under it in mono, which is where this application puts
 * anything that is a fact about a line rather than the line. */
/* The row is `.item.said` now and its layering lives with the other rows, in the
 * swipe block further down — E2. What is here is only the shape of a note. */
.saidrow { padding: 16px; display: flex; flex-direction: column; gap: 5px; }
.saidtext { font-size: var(--t-task); line-height: 1.4; color: var(--tx); }
.saidwhen {
  display: flex;
  gap: 8px;
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx4);
  min-width: 0;
}
/* The day is a fixed width and never gives: squeezed, it wrapped to `2026-08-`
 * and `01`, which is a date that has to be reassembled by the reader. The title
 * is the part that can be any length, so it is the part that truncates. */
.saidday { flex: none; white-space: nowrap; }
.saidtopic { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }

/* **A commitment row and its three verbs** — B1 · A3 in
 * specs/design/knowledge-loose-ends.html. The row is `Said`'s shape — prose
 * over a mono fact line, opening nothing — and the verbs are words at the
 * task rank because they are pressed rarely and read every time: `File` and
 * `Done` in the accent's ink (they act), the ✕ in the closing red the `Said`
 * bin already wears. 44 pt reached through the same bled pseudo-element. */
.item.claim { display: block; min-width: 0; }
.claimrow { padding: 14px 16px 12px; display: flex; flex-direction: column; gap: 5px; }
.claimtext { font-size: var(--t-task); line-height: 1.4; color: var(--tx); }
.claimmeta {
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx4);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.claimacts { display: flex; gap: 18px; padding: 0 16px 12px; }
.claimact {
  position: relative;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-size: var(--t-task);
  font-weight: 600;
  color: var(--ac);
  cursor: pointer;
}
.claimact::after { content: ""; position: absolute; top: -10px; right: -9px; bottom: -10px; left: -9px; }
.claimact:disabled { color: var(--tx4); }
.claimact.drop { color: var(--bad); font-weight: 500; }
/* A decision is a fact, not a task: the same row a rank quieter, no verbs. */
.item.claim.decided .claimtext { color: var(--tx2); }

/* **The room, as chips** — the mail proposal's own row on a third noun, so a
   name you can press off looks exactly like a name you can press off
   anywhere else in this application. `.roomwho` only spends the card's own
   padding; everything else is `.mailwho`'s. */
.roomwho { padding: 12px 14px; }
/* While the record re-derives behind a press, the chip says so and stops
   taking another — a correction that reaches 499 meetings is not instant. */
.mailchip.busy { opacity: .55; pointer-events: none; }

/* The upcoming screen's one press — C1: the prep is asked for, never fired by
   arrival. The beat is the merge bar's own, for the same wait. */
.prepask { display: flex; align-items: center; gap: 10px; padding: 14px 16px; }

/* What a refusal was carrying. Amber is right here and is the only place on the
 * data side it is: a rejection is about the application rather than about the
 * work — CLAUDE.md, and `.banner` already carries the colour. */
.kept {
  display: block;
  margin-top: 8px;
  padding-left: 10px;
  border-left: 2px solid var(--warn);
  font-size: var(--t-task);
  line-height: 1.4;
  color: var(--tx);
}
.keepcopy {
  align-self: flex-start;
  margin-top: 10px;
  min-height: 32px;
  padding: 6px 14px;
  border: 1px solid var(--line-hard);
  border-radius: 8px;
  background: transparent;
  color: var(--tx2);
  font: inherit;
  font-size: var(--t-task);
  cursor: pointer;
}
.keepcopy:active { background: var(--hover); }

/* The thread, read back in the editor — specs/meeting-mode.md §5. */
.thread {
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-height: 220px;
  overflow-y: auto;
}
.threadline {
  display: block;
  padding-left: 10px;
  border-left: 2px solid var(--lift-line);
  font-size: var(--t-task);
  line-height: 1.4;
  color: var(--tx2);
}
.threadwhen {
  display: block;
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx4);
  margin-bottom: 2px;
}

/* ── Writing a topic in the list ────────────────────────────────────────── */
/* specs/design/inline-create.html, C3. The floating button is off this screen
 * and this row is what replaced it: first in the card, because that is where a
 * created topic lands, so where you type is where it appears. */

/* **The band that names a group inside a card** — B1 in
 * specs/design/tasks-topics-and-subgroups.html.
 *
 * A row at the mono uppercase rank every label in this application wears, one
 * level in from the section header six pixels above it, on a **step of grey and
 * never the accent** — `--band`, C1 in specs/design/band-colour.html.
 *
 * It was `--ac-bg` for one version and that was wrong twice over. Measured: the
 * name came out at 2.62:1 in the light theme against 6.24 in dark, and the fill
 * was 1.08:1 against the sunk page — its own colour, near enough, so the band
 * read as a hole punched through the card. Argued: **the accent in this
 * application means *you can touch this***, and a band cannot be touched, so it
 * was borrowing the touch tint for a heading.
 *
 * What tells the two levels apart is therefore ground and case rather than hue,
 * and `pwa/test/contrast.test.js` holds the pair to a number so the next one
 * chosen against a dark card cannot land on a light one unlooked-at.
 *
 * It is not 44 pt and does not need to be: it is a heading and not a control,
 * which is also why it is a band rather than a chip — a rounded pill on a row
 * is a chip here, and every chip in this application is pressed. */
.subband {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 7px 16px;
  background: var(--band);
  font-family: var(--mono);
  font-size: var(--t-label);
  letter-spacing: .1em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--band-ink);
}
/* The first band is the top of the card, so it carries no line above it; every
 * later one divides two groups and does. */
.subband + .item::before,
.item + .subband { border-top: 0; }
.subband:not(:first-child) { border-top: 1px solid var(--line-hard); }
.subname { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.subcount { flex: none; color: var(--band-ct); letter-spacing: .04em; }

/* **The composer's kind, in the slot the `+` used to have** — D1. Drawn at the
 * mark's own 20 px, in the accent, because it is the one thing in the row that
 * can be pressed and is not the field. The 44 pt is taken in a pseudo-element
 * that bleeds into the row's own padding, which is the share glyph's
 * arithmetic: a 44 px box here would push the field off its line. */
.newkind {
  position: relative;
  flex: none;
  width: 22px;
  height: 22px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ac);
  display: grid;
  place-items: center;
  cursor: pointer;
}
.newkind::after {
  content: "";
  position: absolute;
  inset: -11px;
}
.newkind svg { width: 20px; height: 20px; stroke-width: 1.5; display: block; }
.newkind:active { color: var(--ac-lite); }

.newrow {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 16px;
  min-height: 44px;
}
.newplus {
  flex: none;
  color: var(--ac);
  font-size: var(--t-person);
  line-height: 1;
}
.newfield {
  flex: 1;
  min-width: 0;
  border: 0;
  background: none;
  padding: 0;
  color: var(--tx);
  font-family: var(--ios);
  font-size: var(--t-body);
}
.newfield:focus { outline: none; }
/* The placeholder is the accent and not the usual grey: at rest this row *is*
 * the create button, and one tint on anything that can be touched is the rule
 * the whole screen is drawn to. Once there is a topic in it the text is ink,
 * because by then it is a topic and not an invitation. */
.newfield::placeholder { color: var(--ac); opacity: 1; }
/* Who an `@` has already resolved to, as initials and never as names. The
 * mention is consumed out of the line — the same resolution the sheet's title
 * makes — so this is the evidence that the text went somewhere rather than
 * nowhere. Names would be the widest thing on a row whose whole job is to be one
 * line, which is the argument compact settled on the rows below. */
/* ── The editor, in the row ──────────────────────────────────────────────── */
/* B1 in specs/design/one-row-and-inline.html. The sheet it replaces was a card
 * at the foot of the screen: it capped at 88 % and scrolled inside itself, and
 * it opened *away* from the row it was about. This opens where the row was.
 *
 * The plane is the one the sheet used — `--lift-*` redefined so every field and
 * separator inside recomputes one step up and keeps the relief it had. That one
 * rule is the whole reason this was cheap: nothing inside had to be restyled.
 *
 * A hard rule above and below rather than a shadow: it is a *row of this card*
 * that has grown, not a thing floating over it, and a shadow would say the
 * second. */
.inlinecard {
  --bg:   var(--lift-bg);
  --card: var(--lift-card);
  --line: var(--lift-line);
  --hair: var(--lift-line);
  /* `--inset` and `--hover` are part of the lift too, and were missed when it
   * was written: in the dark theme `--inset` and `--lift-bg` are one point a
   * channel apart, so a sunk control inside a lifted plane had no ground at
   * all. */
  --inset: var(--lift-inset);
  --hover: var(--lift-hover);

  background: var(--lift-bg);
  border-top: 1px solid var(--line-hard);
  border-bottom: 1px solid var(--line-hard);
  padding: 12px 14px 10px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
/* First and last in a card have nothing above or below to be separated from. */
.card > .inlinehost:first-child .inlinecard { border-top: 0; }
.card > .inlinehost:last-child .inlinecard { border-bottom: 0; }
.item + .inlinehost .inlinecard { border-top: 1px solid var(--line-hard); }

/* The verbs go **under** the fields here, which is the opposite of the sheet and
 * is right for the opposite reason. A sheet's footer is not seen until it has
 * been scrolled to; an inline editor has no scroller of its own and ends where
 * its fields end, so the row under the last field is always visible — and that
 * is where a form's buttons belong when you can see them. */
.inlineacts { display: flex; align-items: center; gap: 10px; }
.inlinesp { flex: 1; }
.inlineacts .ghost,
.inlineacts .pill { flex: none; }
.inlinebody { display: flex; flex-direction: column; gap: 10px; }

/* Making a group, from the list groups are in. The same row the composer is,
 * and a button rather than a field: a group with no members is a label, so the
 * picker is not an optional second step and there is nothing to type here. */
.newrow.newgroup {
  width: 100%;
  border: 0;
  background: none;
  font-family: var(--ios);
  text-align: left;
  cursor: pointer;
}
.newgroup .newlabel { flex: 1; color: var(--ac); font-size: var(--t-body); }
.newgroup:active { background: var(--hover); }

/* A group's avatar is a rounded square and a person's is a circle. One shape
 * for one person and another for several is the difference read before the name
 * is, which is what a list of a hundred and eighty-six rows needs. */
.av[data-kind="group"] { border-radius: 9px; }

/* What is still missing, in the sheet that is waiting for it. A dead verb with
 * no sentence beside it is one you press twice and then give up on. */
.sheetnote {
  margin: 12px 4px 0;
  color: var(--tx3);
  font-size: var(--t-task);
  line-height: 1.45;
}
.sheetnote[hidden] { display: none; }
/* Not a refusal but a consequence: the verb is live and this is what pressing
 * it will do. Amber is what this application spends on itself rather than on
 * the data — the sync dot with something queued, and a rejection. */
.sheetnote.warn { color: var(--warn); }
.pill.off { background: var(--line-hard); color: var(--tx4); cursor: default; }

.newwith { flex: none; display: flex; align-items: center; }
.newwith .av.sm + .av.sm { margin-left: calc(-3px * var(--scale)); }

/* **The picker floats over the list rather than opening a hole in it** — asked
 * for as *in the topics screen, when choosing someone, the popup scrolls the
 * screen; can it be an overlay instead?*
 *
 * **This reverses the round that made it flush, and the reversal is what use
 * settled.** That round's argument still reads well: `.suggest` is drawn for
 * the sheet, where it floats under a field on a sunk page, and dropped into the
 * topics card with its own border it read as a second card inside the first —
 * so it was made rows of the card, and what is under the caret was a
 * continuation of the line. What it did not weigh is that those rows are *in
 * the flow*: six of them are some 264 px pushed into the middle of the list, so
 * the page grows, the browser scrolls to keep the caret in view, and the line
 * you are typing in moves under your hand while you type. On a Mac window it
 * takes the whole list with it.
 *
 * So it is a panel again, anchored to the row it belongs to — `.newrow` has
 * carried `position: relative` since it was written — and the rows underneath
 * simply stay where they are. It takes the sheet's own plane back for the
 * reason it once gave it up: floating over a list, a box with no edge of its
 * own is a list of names with no boundary, and the lift is what says the two
 * are different planes rather than one interleaved one.
 *
 * **The capture card keeps the flow**, which is why this names `.newrow` rather
 * than the card: that row *is* the whole card — field, note and tools — and its
 * own bottom is the keyboard, so a panel hung under it would open off the foot
 * of the screen. There the list resuming below the line is right, and there is
 * nothing below it to cover. */
.newrow > .suggest {
  position: absolute;
  top: calc(100% - 6px);
  left: 8px;
  right: 8px;
  z-index: 6;
  margin: 0;
  max-height: 320px;
  overflow-y: auto;
  box-shadow: var(--lift);
  background: var(--lift-card);
}
/* The capture card's own row is the card, so its picker stays in the flow. */
.caprow > .suggest {
  position: static;
  flex: 1 0 100%;
  max-height: none;
  box-shadow: none;
  background: var(--card);
}
.newmore {
  flex: none;
  border: 0;
  background: none;
  padding: 4px 2px;
  color: var(--ac);
  font-family: var(--ios);
  font-size: var(--t-task);
  cursor: pointer;
}
.newmore[hidden] { display: none; }

/* **The separator that says this is not a topic.**
 *
 * A row separator in this application starts where the text starts, because the
 * rows either side of it are the same kind of thing. This one runs the whole
 * width of the card and is drawn in `--line-hard` rather than `--hair`: the
 * composer and the list are two different kinds of row sharing one card, and a
 * hairline inset like all the others would have said they were the same.
 *
 * It is on the composer rather than on the first topic, so it is there when the
 * list is empty too — which is the one time the row needs most to look like
 * something rather than like a topic that has lost its title. */
.newrow::after {
  content: "";
  position: absolute;
  z-index: 2;
  left: 0;
  right: 0;
  bottom: 0;
  border-bottom: 1px solid var(--line-hard);
}
/* And not the hairline the next row would otherwise draw as well, which would
 * be two lines four pixels apart. */
.newrow + .item::before { display: none; }

/* When there is nothing open with this person, the card is the composer and one
 * line saying so — rather than a card and a separate empty state below it,
 * which read as two things having gone wrong. */
.emptyrow {
  margin: 0;
  padding: 20px 16px;
  color: var(--tx3);
  font-size: var(--t-task);
}

/* Above the body, which is opaque and carries a z-index of its own so it can
 * slide over the buttons. Without this the separator is painted underneath it
 * and the list loses every hairline it has — which is what it did. It stays on
 * the row rather than on the body, so it does not travel with the swipe: the
 * separator belongs to the list, and the platform keeps it still. */
.item + .item::before {
  content: "";
  position: absolute;
  z-index: 2;
  top: 0;
  left: 16px;
  right: 0;
  border-top: 1px solid var(--hair);
}
.item .ttl {
  display: block;
  font-size: var(--t-row);
  line-height: 1.4;
  color: var(--tx);
  letter-spacing: -0.1px;
}

/* The note, when a topic has one — C1 in specs/design/topics-refinements.html.
 * Two points down and one rank of grey down, which is exactly the relationship a
 * role has to a name on Contacts, so the ladder is one the screen already
 * teaches. Not `--tx4`: that is the age's colour, and a sentence and a number
 * are not the same kind of thing. */
.item .desc {
  display: block;
  margin-top: 3px;
  font-size: var(--t-task);
  line-height: 1.42;
  color: var(--tx3);
}
.item .age {
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx4);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  margin-left: 6px;
}
/* No dormancy stripe and no amber age. A topic more than thirty days unraised
 * used to carry a 3 px amber edge and an amber number — the only colour-coded
 * state in the application, and the only thing that argued with the order the
 * person had chosen. The list is manual and per person precisely because the
 * user decides what matters; a stripe saying "this one, though" is the
 * application deciding instead. The age is still there, because that is a fact.
 * What it means is not the phone's to say. */

/* ── Who else is on a topic ─────────────────────────────────────────────── */
/* A name if it fits on the line the title already occupies, and initials if it
 * does not. Which one applies is measured after layout, in app.js: a name is
 * worth more than an avatar, and an extra line is worth less than either. */

.with { display: inline; margin-left: 6px; }
.with .avs { display: none; }
.with[data-mode="avatars"] .pills { display: none; }
.with[data-mode="avatars"] .avs { display: inline-flex; vertical-align: calc(-6px * var(--scale)); }

/* The other half of the same row, and the same rule: a name pill whose type
 * grows and whose padding does not is a pill that closes on its own words. */
.chip {
  display: inline-block;
  font-size: calc(13px * var(--scale));
  line-height: 1.5;
  color: var(--ac-lite);
  background: var(--ac-bg);
  border-radius: calc(10px * var(--scale));
  padding: calc(1px * var(--scale)) calc(8px * var(--scale));
  white-space: nowrap;
  vertical-align: 1px;
}
.chip + .chip { margin-left: calc(4px * var(--scale)); }

/* **The box scales with the initials in it, and this is where that was learnt
 * the expensive way.** The font was `calc(9px * var(--scale))` and the circle a
 * flat 22 px, which is right on a phone — `--scale` is 1 there — and comes apart
 * the moment a Mac raises it. Reported from a Mac at Large as a row of initials
 * printed on top of each other, and measured: at 1.5 the initials are 13.5 px,
 * so `CSC` wants **31 px of ink in a 22 px circle** and spills 9 px — straight
 * onto the neighbour, which is drawn 6 px *under* it by design.
 *
 * So every length here is a function of `--scale`, the same as the type it
 * holds: the box, the overlap, the ring around it. **The phone is untouched by
 * construction** rather than by a threshold — `--scale` is 1 there, so all four
 * of these evaluate to exactly the numbers they were.
 *
 * The general shape, and this file has met it twice now: *a fixed length beside
 * a scaled one is a layout that works at one scale and is a defect at every
 * other.* The rail's width was the same mistake a version ago. */
.av {
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-weight: 700;
  letter-spacing: 0.3px;
  background: var(--ac-bg);
  color: var(--ac-lite);
  flex-shrink: 0;
  width: calc(44px * var(--scale));
  height: calc(44px * var(--scale));
  font-size: var(--t-task);
}
.av.sm {
  width: calc(22px * var(--scale));
  height: calc(22px * var(--scale));
  /* **The rank is untouched at 9**, and that is deliberate rather than timid.
   * It was raised to 10 while this was being fixed and put back: 9 in a 22 px
   * circle is the ratio the phone has always drawn and nobody has reported, so
   * what was wrong was never the rank — it was the circle not growing with it.
   * Scaling the box and leaving the rank alone means the phone renders exactly
   * what it rendered before, to the pixel, and the Mac gets the same proportion
   * at its own size. */
  font-size: calc(9px * var(--scale));
  border: calc(1.5px * var(--scale)) solid var(--card);
}
/* Each in front of the next rather than behind it: stacked the other way the
 * leftmost initials are the ones clipped, and initials are all an avatar has.
 *
 * **The overlap is 3 and not 6, and the number is the ink rather than a
 * preference.** Two initials at 9 px are 16 px of ink in a 22 px circle, so
 * there are exactly 3 px of clear space either side — and a 6 px overlap spent
 * all of it and then took 3 px of the letter. Every avatar after the first was
 * drawn missing a slice of its first character, at every scale, which is what
 * *MS DS MP AP :SC* is a picture of. At 3 the circles still read as a stack and
 * nothing is bitten.
 *
 * It costs width: five avatars go from 86 px to 98 px. That is paid out of a
 * measurement rather than out of the title, because `fitCoParticipants` picks
 * whichever of names-or-initials makes the shorter row — a wider initials block
 * simply means names win more often, which is the preference this application
 * already states. */
.avs { display: inline-flex; align-items: center; }
.avs .av.sm { position: relative; }
.avs .av.sm + .av.sm { margin-left: calc(-3px * var(--scale)); }

/* ── To do ──────────────────────────────────────────────────────────────── */
/* A task is a topic that knows it is one — specs/design/tasks-after-the-mac.html,
 * A2 — and its row is the topic row wearing a ring. The `.task` card this block
 * used to style was the Mac's read-only feed and went with D2's cut. */

/* The ring: `Done` in one press, a sibling of the body because the body is a
 * `<button>`. A circle and never a square with a tick — a square is what a
 * selection wears in the Edit mode, and this is being finished, not chosen.
 * The hit is the full 44 pt column; the drawn circle is 20 px, top-aligned to
 * the title's own line the way the age is. */
.item.tick { display: flex; align-items: stretch; }
/* **A flex box with its content at the top, and that is the whole of the
 * alignment fix.**
 *
 * A `<button>` centres its own content vertically, whatever the content's
 * margin — so a mark told to sit `--row-pad` from the top of a 111 px row was
 * centred first and offset second, and came out half a line low per line of
 * title: 6 px at one line, 18 at two, 35 at three. The previous round measured
 * where the circle was *told* to be rather than where it *rendered*, which is
 * how it shipped reading correct and looking wrong.
 *
 * `align-items: flex-start` is what makes the margin mean what it says. */
.item.tick .mark {
  flex: none;
  width: 46px;
  display: flex;
  align-items: flex-start;
  border: 0;
  background: var(--card);
  color: var(--line-hard);
  cursor: pointer;
  position: relative;
  z-index: 1;
  padding: 0;
}
/* **The circle sits on the first line of the title, whatever the reading.**
 *
 * Reported as *fix the vertical alignment on tasks*, and it was measured before
 * it was changed: on a compact row the circle came out 11 px below the line it
 * belongs to, and on a dense one 9 px. The old `21px` was not arbitrary — it is
 * exactly right for the full row, which is the only one that existed when it was
 * written — and that is the shape of the fault: a number computed by hand from
 * one set of values, in a file where two more later arrived.
 *
 * The arithmetic is the row's own padding plus half the difference between a
 * line box and the circle, so it follows the type scale onto the Mac as well as
 * following the reading. **The first line and not the row's middle**: a title
 * runs to four lines here, and a mark centred on the row drifts away from the
 * words as they grow — which is what a column of mixed-height rows must not do. */
.item.tick .mark svg {
  display: block;
  width: 20px;
  height: 20px;
  stroke-width: 1.5px;
  stroke-linecap: round;
  stroke-linejoin: round;
  margin: calc(var(--row-pad) + (var(--t-row) * 1.4 - 20px) / 2) 0 0 16px;
}
.item.tick .mark:active { color: var(--ac); }
.item.tick .itembody { flex: 1; min-width: 0; padding-left: 4px; }
/* The hairline still starts where the text starts, which on a ringed row is
 * past the ring's column. */
.item.tick + .item.tick::before { left: 50px; }

/* The due, in the age's own mono rank — B2 — and amber only once it is past:
 * amber is what this application spends on itself and on the one date that
 * stopped being a plan. */
.item .age.due { color: var(--tx3); }
.item .age.due.over { color: var(--warn); }

/* What a `!` resolved to, on the composer row — the initials' own slot, in the
 * rank a date always gets. */
.newdue {
  flex: none;
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx3);
  white-space: nowrap;
}

/* The `!` picker's dates, right-aligned in the row the label owns. */
.sugg .suggdate {
  margin-left: auto;
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx4);
}

/* The editor's date field — the platform's own picker, dressed as every other
 * field in the card. */
.duefield {
  border: 0;
  background: none;
  padding: 0;
  color: var(--tx);
  font-family: var(--ios);
  font-size: var(--t-body);
}
.duefield:focus { outline: none; }

/* ── Nothing there ──────────────────────────────────────────────────────── */

.empty {
  padding: 18px 16px;
  color: var(--tx3);
  font-size: var(--t-task);
  background: var(--card);
  border-radius: var(--radius);
}

/* A rejected change surfaces here, with the server's reason. The optimistic
 * interface showed it saved, so it has to be told — specs/ux.md §6. */
.banner {
  margin-top: 12px;
  padding: 12px 16px;
  background: var(--card);
  border-left: 3px solid var(--warn);
  border-radius: var(--radius);
  color: var(--tx);
  font-size: var(--t-task);
  line-height: 1.4;
}
.banner .reason {
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--warn);
  display: block;
}

.footer {
  padding: 14px 4px 22px;
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx4);
  display: flex;
  justify-content: space-between;
  gap: 12px;
}

:focus-visible {
  outline: 2px solid var(--ac);
  outline-offset: -2px;
}
@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* ── Settings ───────────────────────────────────────────────────────────── */
/* One control in the navigation bar, and what is behind it belongs to the
 * installation rather than to the screen you were on — the arrangement comes
 * from garciadoral-ops. Sections are folded because this is a list of things
 * almost nobody touches. */

.sheet {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: var(--bg);
}
.sheethead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: calc(12px + var(--safe-t)) calc(var(--bar-x) + var(--safe-r)) 12px
           calc(var(--bar-x) + var(--safe-l));
  border-bottom: 1px solid var(--line);
  flex: none;
}
.sheettitle {
  font-size: var(--t-person);
  font-weight: 700;
  letter-spacing: -0.3px;
  color: var(--tx);
}
.sheetbody {
  flex: 1;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 0 calc(var(--gutter) + var(--safe-r)) 24px calc(var(--gutter) + var(--safe-l));
}
/* Settings is a tab now, so the bar under it spends the home indicator's inset
 * and this scroller does not. The `:last-child` form is the same rule the
 * screens use, and for the same reason: written as a position, adding a screen
 * cannot forget it. */
.sheet:last-child .sheetbody { padding-bottom: calc(24px + var(--safe-b)); }

.scrim {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  z-index: 20;
  /* A modal that lets what is beneath scroll is not one: the scrim takes
   * every touch and chains no scroll to the list under it. */
  touch-action: none;
  overscroll-behavior: contain;
}
/* The two acts a topic's editor carries, under the sheet's bar — the inline
 * card's verbs, in the inline card's dress. */
.sheetverbs { flex: none; display: flex; align-items: center; gap: 8px; padding: 2px 16px 8px; }
/* The meeting a topic came from, as a press: on the row under the summary,
 * starting where the text starts, and in the editor under the dates. */
.item.fromrow { flex-wrap: wrap; }
.item .fromline {
  flex: 1 0 100%;
  border: 0;
  background: none;
  text-align: left;
  padding: 0 16px 12px;
  margin-top: -4px;
  font: inherit;
  font-size: var(--t-task);
  font-weight: 600;
  color: var(--ac);
  cursor: pointer;
}
.item.tick .fromline { padding-left: 50px; }
.sheetscroll .editfrom {
  border: 0;
  background: none;
  text-align: left;
  padding: 12px 4px 0;
  font: inherit;
  font-size: var(--t-task);
  font-weight: 600;
  color: var(--ac);
  cursor: pointer;
}

.group {
  background: var(--card);
  border-radius: var(--radius);
  overflow: hidden;
  margin-top: 14px;
}
.group > summary {
  padding: 13px 16px;
  min-height: 44px;
  display: flex;
  align-items: center;
  font-size: var(--t-body);
  color: var(--tx);
  cursor: pointer;
  list-style: none;
}
.group > summary::-webkit-details-marker { display: none; }
.group > summary::after {
  content: "›";
  margin-left: auto;
  color: var(--tx3);
  transform: rotate(90deg);
}
.group[open] > summary::after { transform: rotate(-90deg); }
.groupbody {
  padding: 4px 16px 16px;
  border-top: 1px solid var(--hair);
}

/* There was a `.group.open > .grouptitle` here, giving Appearance the card of a
 * group without its disclosure — it was the one section that was neither folded
 * nor foldable, and a triangle that never turns would have been a control lying
 * about what it does. Appearance folds now like everything else, so the rule has
 * no element left to style and the `<summary>` above does the work. */

/* Three mutually exclusive values that fit one row, so all three are shown at
 * once rather than hidden behind a picker. */
.segmented {
  display: flex;
  width: 100%;
  background: var(--inset);
  border-radius: 9px;
  padding: 2px;
  gap: 2px;
}
.segment {
  flex: 1;
  min-height: 40px;
  border: 0;
  border-radius: 7px;
  background: none;
  color: var(--tx3);
  font: inherit;
  font-size: var(--t-task);
  cursor: pointer;
}
/* One track, two controls — A1 in specs/design/one-row-and-inline.html. The
 * hairline is what keeps it reading as two questions rather than as five things
 * in a line; it is a flex item of zero width so it takes no segment's share. */
.segline {
  flex: 0 0 1px;
  align-self: stretch;
  margin: 7px 4px;
  background: var(--line-hard);
}
/* A glyph segment takes only what it needs, so the three orders keep the width
 * they were measured against. */
.segment.glyph { flex: 0 0 auto; padding: 0 14px; display: grid; place-items: center; }
.segglyph { width: 21px; height: 21px; display: block; }

.segment[data-on] {
  background: var(--card);
  color: var(--tx);
  font-weight: 600;
  box-shadow: var(--shadow);
}

/* **A stack spends its width, and only a button sizes to itself.**
 *
 * It was `align-items: flex-start` — right for a button, wrong for everything
 * else that has ever been put in one — and four things were patched to stretch
 * one at a time as somebody noticed: the card, the heading, the roadmap list,
 * the tone box. Each patch was correct and none of them was the rule.
 *
 * Measured on the Mac before this: Settings' own field rows came out at
 * **332, 457, 471, 381, 371 and 554 px** inside a 1008 px card — six controls
 * with six different right edges — and the paragraphs came out at eleven
 * widths, each as wide as its own longest line. Reported as *the width of the
 * text box is not correct*, which is what a URL field 319 px wide on a 1200 px
 * window looks like from the outside.
 *
 * So the default is inverted and the exception is named: a `<button>` is the
 * one child that should be the width of its own word, because a Save the width
 * of the card is a control claiming to be a section. It is the `min-width: 0`
 * lesson a third time — a default about the cross axis that shows only once
 * something short is put on it — and taking it the other way round is what
 * stops the fifth patch. */
.stack { display: flex; flex-direction: column; gap: 10px; align-items: stretch; padding-top: 12px; }
.stack > button { align-self: flex-start; }
/* **Appearance holds two controls now, so each says which it is** — F1 in
 * specs/design/mac-view.html. One segmented control needed no label, because the
 * section's own name was the label; two of them stacked without one is a pair of
 * identical tracks and a guess about which is which.
 *
 * The rank is the section label's — mono, uppercase, `--tx4` — because that is
 * what this application already uses for *the name of the thing under it*, and a
 * second rank invented for a settings row would be one more thing to keep
 * agreeing with the first. */
.setlbl {
  font-family: var(--mono);
  font-size: var(--t-meta);
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--tx4);
}
.stack > .setlbl + .segmented { margin-top: -4px; }
/* A hint belongs to the control above it and not to the label below it. */
.stack > .hint + .setlbl { margin-top: 6px; }

.hint {
  margin: 0;
  font-size: var(--t-task);
  color: var(--tx2);
  line-height: 1.5;
}
.hint.problem {
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--warn);
}

/* The courier's progress — Settings → Quill. A thin track in the hairline's
   grey with the accent filling it, because the bar is about the application
   doing work, and the accent is the one colour that means the application. */
.meter {
  height: 4px;
  border-radius: 2px;
  background: var(--line);
  overflow: hidden;
}
.meter > div {
  height: 100%;
  width: 0;
  background: var(--ac);
  transition: width 0.3s ease;
}

/* Each phase closes the one before it, so this reads as what has happened and
 * not as a promise of what will. */
.progress {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 5px;
  width: 100%;
}
.progress li {
  display: flex;
  gap: 8px;
  align-items: baseline;
  font-size: var(--t-task);
  color: var(--tx2);
}
.progress .mark {
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx4);
  width: 1em;
  flex: none;
}
.progress li[data-step="done"] .mark { color: var(--ok); }
.progress li[data-step="failed"] { color: var(--bad); }
.progress li[data-step="failed"] .mark { color: var(--bad); }

.action {
  min-height: 44px;
  padding: 0 18px;
  border-radius: var(--radius);
  border: 1px solid var(--line-hard);
  background: var(--bg);
  color: var(--ac);
  font-family: var(--ios);
  font-size: var(--t-body);
  cursor: pointer;
}
.action:disabled { color: var(--tx4); }
.action.danger { color: var(--bad); }

/* ── Roadmap ────────────────────────────────────────────────────────────── */
/* A list of ideas about the application, on the machine the ideas arrive on. It
 * wears Contacts' card of rows rather than a shape of its own — a list is a list
 * — and needs exactly three things said about it. */


/* The window's own measurements — js/measure.js.
 *
 * A `<pre>` and mono, because every line of it is a number that has to line up
 * with the number under it: this block exists to be compared down a column and
 * then screenshotted, and proportional digits make two different heights look
 * like the same height. `tabular-nums` for the same reason one rank down.
 *
 * It scrolls sideways rather than wrapping. A wrapped measurement reads as two
 * measurements, which on the one screen whose whole job is to be unambiguous is
 * the only failure worth designing against — and the longest line here is wider
 * than a phone by construction. */
.measure {
  align-self: stretch;
  margin: 0;
  padding: 12px 14px;
  background: var(--inset);
  border: 1px solid var(--line);
  border-radius: 9px;
  font-family: var(--mono);
  font-size: var(--t-meta);
  font-variant-numeric: tabular-nums;
  line-height: 1.65;
  color: var(--tx2);
  overflow-x: auto;
  white-space: pre;
  -webkit-user-select: text;
  user-select: text;
}
.stack > .said { font-size: var(--t-meta); color: var(--tx3); font-family: var(--mono); }

/* The separator starts where the text starts, which on every other row means
 * past the avatar and here means past nothing: there is no avatar, so 60 px of
 * inset would be a hairline hanging in space. */
.card.roadmap .row + .row::before { left: 16px; }
/* An idea is prose and the row exists to show it: it wraps to as many lines as
 * it takes, and the newlines somebody typed are the ones they get back. Aligned
 * to the top, so the chevron sits with the first line rather than in the middle
 * of a paragraph. */
.card.roadmap .row { align-items: flex-start; padding: 12px 16px; }
.card.roadmap .row .ttl { white-space: pre-wrap; }
.card.roadmap .row .chev { margin-top: 2px; }

.sheetbody > .hint { padding: 16px 4px 0; }
/* The same line, in the sheet that replaced the panel it used to sit in. */
.sheetscroll > .hint { padding: 14px 4px 0; }
/* The buttons that go *inside* a sheet rather than at the title's height — Copy
 * and Delete, on an idea. They take the air the hint under them takes, or they
 * read as part of the field above; and the gap between them is stated rather
 * than left to whatever whitespace the markup happens to carry, because two
 * buttons that touch read as one control with a seam down it. */
.sheetscroll > .action { margin-top: 14px; }
.sheetscroll > .action + .action { margin-left: 8px; }

/* ── The gate ───────────────────────────────────────────────────────────── */

.gate {
  padding: 48px 4px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  align-items: flex-start;
}
.action.apple {
  align-self: stretch;
  background: var(--tx);
  color: var(--bg);
  border-color: var(--tx);
  font-weight: 600;
}

/* ── On a screen that is not a phone ─────────────────────────────────────── */
/* The layout is designed at 390 pt and stays that way. What a wide window gets
 * is the same application drawn as a surface on a page, which is the device the
 * design documents already use for exactly this.
 *
 * This is the *browser's* version of the rule and it keys on the window, which
 * is all a browser can be asked. The Mac block at the top of this file does the
 * same thing on the machine rather than on the window, at the machine's scale,
 * and outranks this one wherever both apply. */

@media (min-width: 700px) {
  #app {
    max-width: 460px;
    margin: 0 auto;
    border-left: 1px solid var(--line);
    border-right: 1px solid var(--line);
    box-shadow: var(--shadow);
  }
}

/* A mouse can hover and a finger cannot, so the feedback each one gets is
 * different: `:active` is the whole story on the phone, and on a Mac a row that
 * does not answer the pointer reads as not being a control at all. */
@media (hover: hover) and (pointer: fine) {
  .row:hover,
  .itembody:hover,
  .item.tick .mark:hover::before { border-color: var(--ac); }
  .navback:hover,
  .navgear:hover { color: var(--ac-lite); }
  .action:hover:not(:disabled) { background: var(--hover); }
  .action.apple:hover:not(:disabled) { background: var(--tx2); }
  .group > summary:hover { background: var(--hover); }
}

/* ── Writing ────────────────────────────────────────────────────────────── */
/* Everything below belongs to stage 3: the two gestures on the Topics list, and
 * the sheet that captures a topic. Nothing here blocks — a write lands locally
 * and queues, and the pill is the only thing that mentions the network. */

.navgear.plus { font-size: 29px; line-height: 1; font-weight: 300; }

/* ── Reordering ──────────────────────────────────────────────────────────── */
/* R1 in specs/design/person-bar.html. A mode, turned on by `Reorder` in the
 * section header, and inside it the handle is the only draggable pixel.
 *
 * It is on the **trailing** edge, which is where the swipe's buttons come from —
 * and those are off in this mode, so the two never want the same pixel at the
 * same time. What that buys over the leading edge is that **no title moves**:
 * every one starts where it started, so the list is still scannable while it is
 * being rearranged. A long title does re-wrap, because the handle takes its
 * width from the row — that was worth saying plainly rather than claiming
 * nothing changes at all. Reserving the width at rest would cost every title
 * 31 px for a mode almost never in use. */
.item .rowgrip {
  color: var(--tx3);
  font-size: var(--t-person);
  line-height: 1.3;
  flex: none;
  cursor: grab;
  /* The browser must not scroll with a drag that starts here, or the handle
   * moves the list instead of the row. */
  touch-action: none;
}
.item .rowgrip:active { cursor: grabbing; color: var(--ac); }

/* Nothing on a topic row is worth selecting, and holding one long enough to
 * drag it used to raise the system's blue selection and the loupe over the
 * title — which reads as the application having broken rather than as a gesture
 * in progress. */
.item { touch-action: pan-y; user-select: none; -webkit-user-select: none; }
.topics.editing .item { touch-action: pan-y; }
/* The row being dragged **follows the finger** — M3 in
 * specs/design/reorder-motion.html — and the rows it displaces glide out of its
 * way rather than teleporting.
 *
 * Both halves are needed and they are independent. Before this the row stayed in
 * its slot while the list reordered underneath, so the finger and the thing it
 * was holding were never in the same place; and the displaced rows were moved by
 * the layout engine, which no transition can animate. The dragged row is
 * translated in `gestures.js`, and the others are caught at their old positions
 * and released — first, last, invert, play — which is what `sliding` is for. */
/* The card stops clipping while the mode is on, or a row dragged past the top
 * row is cut off at the card's edge — which looks like the row being eaten
 * rather than lifted. The first and last rows take the radius themselves so the
 * card still reads as a card with its corners on. */
.card.topics.editing { overflow: visible; }
.card.topics.editing .item:first-child { border-radius: var(--radius) var(--radius) 0 0; }
.card.topics.editing .item:last-child { border-radius: 0 0 var(--radius) var(--radius); }

.item.lifted {
  box-shadow: var(--lift);
  z-index: 5;
  border-radius: var(--radius);
}
.item.lifted .itembody { background: var(--hover); }
.item.sliding { transition: transform 220ms cubic-bezier(.2, .7, .3, 1); }
/* The reduced-motion rule at the foot of this file turns the transition off; the
 * dragged row goes on tracking the finger, because that is position feedback
 * rather than decoration. */

/* The section's own control. It sits in the header rather than the navigation
 * bar because it is about this list and not about the screen — and the plus,
 * which is about the screen, floats over it instead. */
.secact {
  border: 0;
  background: none;
  padding: 0 2px;
  font-family: var(--ios);
  font-size: var(--t-task);
  color: var(--ac);
  text-transform: none;
  letter-spacing: 0;
  cursor: pointer;
}
.secact.on { font-weight: 600; }
/* A word says it is on by getting heavier; a glyph cannot, so `Merge` fills
 * while its mode runs — the same thing the row's own verdict buttons do, and the
 * only way a mark on a header row can say "this is the state you are in". */
.secact.glyph.on {
  background: var(--ac);
  color: var(--on-ok);
  border-radius: 8px;
}
/* `Undo` with nothing to undo. It stays in the row rather than disappearing:
 * the header would otherwise gain and lose a control on every verdict, which is
 * a list that jumps sideways while you are reading it. */
.secact.off { color: var(--tx4); }

/* **Share is drawn** — specs/design/share-placement.html, P2. The square-and-
 * arrow is the one glyph on this platform that needs no legend, and it is the
 * shape of the sheet it opens.
 *
 * The glyph is 21 px and the button is 30, which is the width the word used to
 * spend twice over. **The rest of the 44 pt comes from the padding rather than
 * from the box**, so the touch target is the platform's minimum while the row
 * stays the height a section label sets — a 44 px box here would push the
 * header down eleven pixels on every screen that has one. */
.secact.glyph {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  padding: 0;
  /* Bleeds into the gap and the row above and below, where there is nothing to
   * hit but the header itself. `position: relative` keeps the glyph centred on
   * the box and not on the target. */
  position: relative;
}
.secact.glyph::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
}
.secglyph { width: 21px; height: 21px; display: block; }
/* A fifth larger here like every other glyph, through the one Mac block's
 * scale rather than a second number. */
:root[data-runtime="mac"] .secglyph { width: 25px; height: 25px; }
:root[data-runtime="mac"] .secact.glyph { width: 34px; height: 34px; }

/* Two verbs on the same header row — a drawn Share and a written Reorder — with
 * the gap a section label is inset by, so neither reads as attached to the
 * other. `center` and not `baseline`: a glyph has no baseline to sit on, and on
 * `baseline` it hangs a few pixels below the word beside it. */
.secacts { margin-left: auto; display: flex; align-items: center; gap: 14px; }
/* A `.sechead` aligns on the baseline, and a segmented control does not have
 * one — so the box itself has to be centred or it hangs off the label's. */
.sechead .secacts { align-self: center; }
/* **The gutter belongs to whichever of the two is carrying it.** A folding
 * header has `padding: 0` and its button spends the gutter, so the action
 * beside it has to spend its own; a plain one already has padding, and the same
 * rule there is the gutter charged twice — 22 px of the row, which is what took
 * the control off the card's right edge on the hitlist. */
.sechead:not(.fold) .segmented.density { margin-right: 0; }
/* Which is the same sentence for any action beside a folding header, and the
   Inbox's meeting count is the first one to be a bare mark rather than a
   button with padding of its own — it came out flush against the card's edge.
   Measured at 390 pt: right edge 373 of a 373 px card, before. */
.sechead.fold .secacts { margin-right: var(--gutter); }
/* And the same header takes the folding one's vertical arithmetic, so a screen
 * carrying one of each does not step. A `.sechead.fold` is 44 px because its
 * button is; a plain one is `16 + content + 6`, which with a 30 px control in it
 * came out 52 — eight pixels that only show up beside the other kind. */
.sechead:not(.fold):has(.segmented.density) { padding-top: 8px; }

/* ── The sheet, for every editor there is ───────────────────────────────── */
/* specs/design/sheets.html, decided as S2 · F2. One presentation for New topic,
 * Edit topic and Edit contact, where there used to be two that had never met —
 * a bottom sheet with a footer verb, and a panel over the whole application for
 * four short fields. `sheet.js` is the module; this is what it looks like. */

/* **The lift, and it is one rule rather than a restyling of every piece.**
 * `--bg`, `--card` and `--line` become their lifted values here, so a card, a
 * field, a token or a separator inside the sheet recomputes one plane up and
 * keeps exactly the relief it had. Lifted from garciadoral-ops's `.hoja`, where
 * it is the line that made the whole arrangement cheap. Without it a sheet drawn
 * in `--bg` over a page drawn in `--bg` is visible only by its shadow, and the
 * card inside it is the same colour as the rows behind. */
.sheetcard {
  --bg:   var(--lift-bg);
  --card: var(--lift-card);
  --line: var(--lift-line);
  --hair: var(--lift-line);
  /* `--inset` and `--hover` are part of the lift too, and were missed when it
   * was written: in the dark theme `--inset` and `--lift-bg` are one point a
   * channel apart, so a sunk control inside a lifted plane had no ground at
   * all. */
  --inset: var(--lift-inset);
  --hover: var(--lift-hover);

  position: fixed;
  z-index: 21;
  left: calc(12px + var(--safe-l));
  right: calc(12px + var(--safe-r));
  /* **Above the keyboard, not behind it.** `position: fixed` is measured
   * against the window, and the window does not shrink for a keyboard — so a
   * sheet that ends 12 px from the bottom of the *screen* ends somewhere in the
   * middle of the keyboard, with the rest of its card and most of its scroller
   * underneath. The reported symptom was not "the keyboard is in the way": it
   * was **"the modal will not let me scroll"**, because what was left to drag
   * on was a strip a few rows tall.
   *
   * `--kb` is published by `watchKeyboard` and is zero when there is no
   * keyboard, so this is the same rule on every machine. */
  bottom: calc(12px + var(--safe-b) + var(--kb, 0px));
  /* The same column the application is in, so an editor opened from a list does
   * not turn out to be twice its width. */
  max-width: 460px;
  margin: 0 auto;
  /* Ends where the content ends, and never past this. A four-field contact is a
   * short card; New topic with the picker open and a keyboard up is a tall one,
   * and the cap is what makes those the same sheet rather than two. */
  /* And the cap is 88 % of what is *left*, for the same reason: a card capped
   * against the window is a card taller than the room it has. */
  /* **What it is 88 % *of* is the measured height and not a percentage.** A
   * percentage on a fixed element resolves against the viewport the engine
   * reports, and in a WKWebView on a Mac that is the **scene** — which
   * includes the strip the title bar occupies. That is the same quantity
   * `#app` stopped trusting when the tab bar was reported cut for the fourth
   * time, and `--app-h` is the answer already in the building: the visible
   * height, measured by `watchHeight` and re-read on every resize.
   *
   * `--kb` is *not* subtracted from it, deliberately — the visual viewport
   * already shrinks for a keyboard, so taking it off twice is a card half a
   * keyboard short. It is subtracted only on the fallback path, where the
   * percentage is all there is. */
  --sheet-h: var(--app-h, calc(100% - var(--kb, 0px)));
  max-height: calc(var(--sheet-h) * 0.88);
  min-height: 0;
  display: flex;
  flex-direction: column;
  background: var(--lift-bg);
  border: 1px solid var(--lift-line);
  border-radius: 20px;
  box-shadow: var(--shadow);
  animation: rise 220ms cubic-bezier(.22, .8, .3, 1);
}
@keyframes rise { from { transform: translateY(16px); opacity: .5; } }

/* A sheet that came out of a row grows from that row instead of rising from the
 * bottom of the screen. `--from-y` is the row's centre expressed inside the
 * card, written by `openSheet` from a measurement, so the card is anchored to
 * the line the caret was on: `Details` reads as the sentence being given more
 * room rather than as a form having opened over it.
 *
 * Scale and not a slide, because a slide from the row would still be a sheet
 * arriving — what says "this is that line" is the card being *small at that
 * point* and growing. `prefers-reduced-motion` gets the plain fade both ways. */
.sheetcard.fromrow {
  transform-origin: 50% var(--from-y, 100%);
  animation: unfold 240ms cubic-bezier(.22, .8, .3, 1);
}
@keyframes unfold {
  from { transform: scale(.86); opacity: 0; }
  55%  { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .sheetcard,
  .sheetcard.fromrow { animation: fadein 120ms linear; }
  @keyframes fadein { from { opacity: 0; } }
}

/* A sign, not a control — it says the sheet came from below and goes back down.
 * It does not drag: a handle you can pull is a promise, and the drag it promises
 * fights a body that scrolls. */
.grab {
  flex: none;
  width: 38px;
  height: 4px;
  margin: 8px auto 0;
  border-radius: 2px;
  background: var(--lift-line);
}

/* The verbs at the title's height, which is where they are looked for. A sheet's
 * footer is not seen until it has been scrolled to the end, and on a form of
 * five fields that is a verb nobody finds without being told the sheet scrolls. */
/* 8 px of gap and 12 on the trailing edge, and both numbers are the difference
 * between a name fitting and not. `Michael Seiger` at 22 px needs 179 px and the
 * bar was leaving it 178: one pixel, and the sheet opened on `Michael Seig…`.
 * The pill carries its own visual padding, so the room comes off the parts that
 * were only ever air. */
.sheetbar {
  flex: none;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px 6px 16px;
}
.sheetname {
  flex: 1;
  min-width: 0;
  margin: 0;
  font-size: var(--t-person);
  font-weight: 700;
  letter-spacing: -0.3px;
  color: var(--tx);
  /* A long name truncates rather than wrapping: a bar that grows a line for some
   * people is the height problem the person bar already had once. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sheetscroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 2px 16px 16px;
}

.ghost {
  flex: none;
  border: 0;
  background: none;
  color: var(--tx3);
  font-family: var(--ios);
  font-size: var(--t-body);
  padding: 8px 2px;
  min-height: 44px;
  cursor: pointer;
}
.pill {
  flex: none;
  border: 0;
  border-radius: 999px;
  background: var(--ac);
  color: var(--on-ok);
  font-family: var(--ios);
  font-size: var(--t-task);
  font-weight: 600;
  padding: 9px 16px;
  min-height: 36px;
  cursor: pointer;
}
.pill:disabled { background: var(--card); color: var(--tx4); cursor: default; }

/* ── The agenda's own settings ──────────────────────────────────────────── */
/* The verbs on one row, and the trace under them. */

.verbs { display: flex; flex-wrap: wrap; gap: 8px; padding-top: 4px; }
.verbs .action { flex: 1 1 auto; }

/* What the test answers with: the model's own words above, and one line per
 * attempt below in the mono the identifiers are set in — a trace is read for
 * its numbers rather than for its prose. */
.trace { display: flex; flex-direction: column; gap: 4px; padding-top: 8px; }
.traceout {
  margin: 0 0 6px;
  padding: 12px 14px;
  background: var(--card);
  border-radius: var(--radius);
  color: var(--tx2);
  font-size: var(--t-task);
  line-height: 1.5;
  white-space: pre-wrap;
}
.traceline {
  margin: 0;
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx3);
  overflow-wrap: anywhere;
}
.traceline.bad { color: var(--warn); }

/* ── The ways of sharing ────────────────────────────────────────────────── */
/* specs/design/agenda.html, W1. One button and a sheet, after garciadoral-ops:
 * every way gets a sentence, which is more than a glyph can say, and there is
 * room for a fourth the day it is wanted. */

.ways { display: flex; flex-direction: column; gap: 8px; padding-top: 6px; }
.way {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 13px 12px;
  min-height: 44px;
  border: 0;
  border-radius: var(--radius);
  background: var(--card);
  font-family: var(--ios);
  text-align: left;
  cursor: pointer;
}
.way:disabled { opacity: .5; cursor: default; }
.wayic {
  position: relative;
  flex: none;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: 10px;
  background: var(--ac-bg);
  color: var(--ac);
  font-size: var(--t-body);
}
/* Outside the glyph and rimmed in the row's own colour, so it reads as a mark
 * on the icon rather than as another stroke of it — garciadoral-ops's
 * `.icono-insignia`, which learnt that the hard way. */
.spark {
  position: absolute;
  top: -4px;
  right: -4px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--ac);
  color: var(--on-ok);
  font-size: 9px;
  border: 2px solid var(--card);
}
.waytx { flex: 1; min-width: 0; }
.wayname { display: block; font-size: var(--t-body); color: var(--tx); }
.wayhint { display: block; font-size: var(--t-task); color: var(--tx3); }

/* ── Fields, as grouped rows ────────────────────────────────────────────── */
/* F2. The label is a column you read down rather than eight things stacked, and
 * a long value gets the whole width. */

.fields {
  background: var(--card);
  border-radius: var(--radius);
  overflow: hidden;
  margin-top: 12px;
}
.frow {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 14px;
  min-height: 44px;
}
/* The separator starts where the text starts, which is the same rule the lists
 * follow — so the margin moves the row and the padding gives it back. */
.frow + .frow {
  border-top: 1px solid var(--hair);
  margin-left: 14px;
  padding-left: 0;
}
/* 112 px and not 96: `Organisation` is the longest label the contact editor has,
 * and at 96 it wrapped — so its value started to the left of the other three and
 * the column you read down, which is the whole argument for this arrangement,
 * was the thing it lost. */
.fkey {
  flex: none;
  /* **A scaled length beside a scaled font**, which is 1.1.13's own sentence on
   * a fourth element: 112 px was measured once against `--t-task` at scale 1,
   * so on a Mac at 1.5 the words are half again bigger and the column is not —
   * `Ignore under` wrapped to two lines and took the row to 66 px. */
  width: calc(112px * var(--scale));
  font-size: var(--t-task);
  color: var(--tx3);
}
.fval {
  flex: 1;
  min-width: 0;
  border: 0;
  background: none;
  padding: 0;
  color: var(--tx);
  font-family: var(--ios);
  font-size: var(--t-body);
  line-height: 1.4;
  resize: none;
}
.fval::placeholder { color: var(--tx4); }
/* There is no border to outline, so the row itself is the focus ring. Without
 * this a keyboard has no way of saying where it is. */
.frow:focus-within { background: var(--hover); }
/* **What a model wrote says so on the label, not in the value** — A1 in
 * specs/design/mail-title-and-compact.html. Everywhere else the ✦ sits inside
 * the text it marks; here the text becomes a record field, and a title reading
 * `✦ DACH frame renewal terms` would be in the row on the phone and in the
 * register on the Mac for ever. It goes the moment the field is typed in,
 * because from then on the words are the owner's. */
.frow.wrote > .fkey::after { content: " \2726"; color: var(--ac); }
/* The same mark for the one model-written thing on this card that is not a
 * field — the initiative (I2). Same rule and the same reason: the name becomes
 * a row in `initiatives` on both machines. */
.maildetail .lbl.wrote::after { content: " \2726"; color: var(--ac); }
.fval:focus { outline: none; }

/* Stacked, for the one field that is a paragraph rather than a value: a note in
 * a row 112 px narrower than the card is a note nobody would type in. */
.frow.stacked { display: block; }
.frow.stacked > .fkey {
  display: block;
  width: auto;
  font-size: var(--t-label);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  font-weight: 600;
}
.frow.stacked > .fval {
  display: block;
  width: 100%;
  margin-top: 4px;
}
/* Except the one "field" that is a row of people rather than a control: it is a
 * flex box and `display: block` on the stacked value would flatten it. */
.frow.stacked > .fval.tokens { display: flex; }
/* The textareas are grown to their content by `grows()`, so the scrollbar they
 * would otherwise flash on the frame the height is being measured is one nobody
 * needs to see. */
.frow.stacked > textarea.fval { overflow: hidden; }

/* What the models are told, shown to be read — Settings → AI. It is a field
 * that is not a field, so it says so in the rank facts get here rather than by
 * looking disabled: greyed-out text reads as a control waiting to be unlocked,
 * and there is no unlocking it. The caret is still allowed to land, because
 * selecting a sentence to look at it closely is the one thing anybody wants to
 * do with a prompt they cannot change. */
textarea.readonly {
  color: var(--tx2);
  background: var(--bg);
  cursor: default;
}

/* The distilled tone — Settings → Getting my tone.
 *
 * The five above sit in `field()`, which gives them the card's own width. This
 * one is a child of `.stack`, which used to align its children to the start —
 * what that did to a textarea was leave it at the browser's default twenty
 * columns, a paragraph wrapped inside a box half the width of the sentence
 * explaining it. The stack stretches now, so `width: 100%` is what is left to
 * say: a `<textarea>` carries `cols` and would otherwise measure itself.
 *
 * It is inset and bordered rather than bare, because it is now something you
 * press: a block that copies itself has to look like a block. `pointer` says
 * so on the machine that has a cursor, and on the one that does not the line
 * under it says it in words. */
.stack > .tonebox {
  width: 100%;
  box-sizing: border-box;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 9px;
  background: var(--inset);
  font-family: var(--ios);
  font-size: var(--t-task);
  line-height: 1.5;
  /* Grown to its content by `grows()`, so the scrollbar it would flash on the
   * frame the height is measured is one nobody needs to see. */
  overflow: hidden;
  resize: none;
  cursor: pointer;
}

:root[data-runtime="mac"] .sheetcard { max-width: 560px; bottom: 24px; }

.tokens { display: flex; flex-wrap: wrap; gap: 6px; }
.token {
  border: 1px solid var(--line-hard);
  background: var(--card);
  color: var(--tx);
  border-radius: 999px;
  padding: 6px 12px;
  min-height: 36px;
  font-family: var(--ios);
  font-size: var(--t-task);
  cursor: pointer;
}
.token.add { color: var(--ac); border-style: dashed; }

/* The picker, in both fields, resolving differently in each — specs/ux.md §5. */
.suggest {
  margin-top: 8px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.suggest[hidden] { display: none; }
/* The picker's own search, for when it was opened by `+ add` and there is no
 * field carrying the query. It sits inside the box rather than above it, so what
 * is being typed and what is answering are one object. */
.finder {
  display: block;
  width: 100%;
  min-height: 44px;
  padding: 10px 12px;
  border: 0;
  border-bottom: 1px solid var(--hair);
  background: none;
  color: var(--tx);
  font-family: var(--ios);
  font-size: var(--t-body);
}
.finder[hidden] { display: none; }
.finder::placeholder { color: var(--tx4); }
.finder:focus { outline: none; }
/* The browser's own clear button, which WebKit draws in its blue and not in
 * ours — the one tint on this screen is the accent, and a second one arriving
 * from the platform reads as a control belonging to something else. Closing the
 * picker empties the field anyway. */
.finder::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; display: none; }
.suggest:has(.finder:focus) { outline: 2px solid var(--ac); outline-offset: -1px; }
.sugg {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  min-height: 44px;
  padding: 8px 12px;
  border: 0;
  background: none;
  color: var(--tx);
  font-family: var(--ios);
  font-size: var(--t-body);
  text-align: left;
  cursor: pointer;
}
.sugg + .sugg { border-top: 1px solid var(--hair); }
.sugg:active { background: var(--hover); }
/* **Where the arrows are** — asked for as *I want to use the cursor arrows to
 * select the person, so I can manage without the mouse*.
 *
 * The ground a hover and a press already use, **and an accent edge on top of
 * it**, which the first pass did not have and driving is what said so: on a Mac
 * there is a hover as well, so a keyboard mark drawn as one is a mark that
 * cannot be told from the pointer resting somewhere. The edge is the accent
 * because the accent means *you can touch this* and this is the row a press
 * would take; it is an edge rather than accent text, because `.sugg.make`
 * already spends that ink on the creation and two meanings on one colour in one
 * list is the thing this application is careful about.
 *
 * `data-on` is what every live segment here already wears, so the walk in
 * `picker.js` needed no attribute of its own. */
.sugg[data-on],
.mailpicker .rows button[data-on] {
  background: var(--hover);
  box-shadow: inset 3px 0 0 var(--ac);
}
/* Create is last on purpose, so a near-miss is always offered first. */
.sugg.make { color: var(--ac); }

@media (hover: hover) and (pointer: fine) {
  .sugg:hover,
  .token:hover { background: var(--hover); }
  .secact:hover { color: var(--ac-lite); }
}

/* ── Double tap ─────────────────────────────────────────────────────────── */
/* A double tap on a phone means zoom, and on an interface of 44 pt rows it
 * means a mistake: the second tap of an impatient double is read as a gesture
 * and the screen jumps.
 *
 * `manipulation` turns off double-tap-to-zoom and the 300 ms wait that comes
 * with it. **Pinch to zoom is deliberately left alone** — it is how somebody
 * with poor sight reads a screen, and taking it away is the version of this fix
 * that gets written when nobody thinks about who was using it. `user-scalable=no`
 * in the viewport would have been one line and would have done exactly that. */
html { touch-action: manipulation; }

/* ── Choosing several, and the bar that counts ───────────────────────────── */
/* specs/design/list-density-and-merge.html, M1. The mode reuses the row and
 * changes what touching it means: the swipe, the tap-to-edit and the handle are
 * all off, and the only pixel that writes anything is in the bar at the foot. */

.itembody.picking {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  /* The row is a button in this mode too, so it keeps the reset the others get. */
  width: 100%;
  text-align: left;
  border: 0;
  color: inherit;
  font: inherit;
  cursor: pointer;
}
.pickbox {
  flex: none;
  width: 22px;
  height: 22px;
  margin-top: 2px;
  border-radius: 50%;
  border: 1.6px solid var(--line-hard);
  /* A circle and not a square: a square with a tick is what a *task* wears two
   * sections below, and a topic has never been something you tick off. */
}
.pickbox.on {
  background: var(--ac);
  border-color: var(--ac);
  position: relative;
}
.pickbox.on::after {
  content: "✓";
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: var(--on-ok);
  font-size: 13px;
  font-weight: 700;
}

/* Over the tab bar rather than above it: while a mode is on, the bar is what
 * you are answering, and the tabs are where you would go to abandon it. It
 * carries the home indicator's inset itself, like every other bar here. */
.modebar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 15;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px calc(var(--gutter) + var(--safe-r)) calc(12px + var(--safe-b))
           calc(var(--gutter) + var(--safe-l));
  background: var(--lift-bg);
  border-top: 1px solid var(--lift-line);
  box-shadow: var(--sheet-shadow);
}
:root[data-runtime="mac"] .modebar { max-width: 560px; margin: 0 auto; }
.modebar .said { flex: 1; font-size: var(--t-task); color: var(--tx3); }
/* The wait, said where the press was made.
 *
 * The model takes seconds and the bar used to go on offering a live `Merge` for
 * every one of them, which reads as a screen that has stopped — and the honest
 * response to a stopped screen is to press the thing again. The button is dead
 * here and the sentence says why.
 *
 * The dot is the only animated thing in this application, and it earns that:
 * words that do not change for eight seconds are exactly what frozen looks
 * like, and everything else here that waits either has a sheet open saying so
 * or is over before it can be doubted. `prefers-reduced-motion` takes the
 * movement away and leaves the sentence, which was always the part carrying the
 * meaning. */

/* **A line that says something is being fetched** — asked for as *pon algo
 * para ver el progreso*. The `emptyrow`'s place and rank with the merge bar's
 * own pulsing dot in front of it, so a wait with no sheet over it looks the
 * same wherever it happens. The dot's animation is the one below, shared
 * rather than copied. */
.busyrow {
  display: flex;
  align-items: center;
  gap: 9px;
  margin: 0;
  padding: 20px 16px;
  color: var(--tx3);
  font-size: var(--t-task);
}
.busyrow .beat {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ac);
  animation: beat 1.25s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) { .busyrow .beat { animation: none; } }
.modebar .said.work { display: flex; align-items: center; gap: 9px; color: var(--tx2); }
.modebar .beat {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ac);
  animation: beat 1.25s ease-in-out infinite;
}
@keyframes beat { 50% { opacity: .22; transform: scale(.82); } }
@media (prefers-reduced-motion: reduce) { .modebar .beat { animation: none; } }
.modebar .go {
  flex: none;
  min-height: 44px;
  padding: 0 20px;
  border: 0;
  border-radius: 999px;
  background: var(--ac);
  color: var(--on-ok);
  font-family: var(--ios);
  font-size: var(--t-body);
  font-weight: 600;
  cursor: pointer;
}
.modebar .go.off { background: var(--line-hard); color: var(--tx4); cursor: default; }
/* The last row has to clear the bar, the same way the scroller clears the
 * floating button on the screens that have one. */
.screen:has(.modebar) .scroll { padding-bottom: 96px; }

/* ── The merge preview ──────────────────────────────────────────────────── */
/* P1: what it will say before it says it, and the plain truth about the rest. */

.mergefacts { padding: 14px 4px 0; display: flex; flex-direction: column; gap: 10px; }
.mergefacts .k {
  display: block;
  font-family: var(--mono);
  font-size: var(--t-meta);
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--tx3);
  margin-bottom: 4px;
}
.mergefacts .r { display: block; font-size: var(--t-task); color: var(--tx3); line-height: 1.5; }
.mergefacts .r s { color: var(--tx4); }
/* The people who are not the person whose screen this is: the union reaches
 * past it, and that is the one thing nobody should discover afterwards. */
.mergefacts .other { color: var(--tx2); font-weight: 600; }

/* ── The Ask thread — specs/design/chat-thread.html, B1 · C3 · H3 ─────────
 *
 * Bar · thread · composer · tab bar, and the composer is an ordinary flex
 * child of `.screen` rather than anything fixed: the screen ends where the tab
 * bar begins, so the bar under it carries the safe area and this one spends
 * none. The merge bar had to be fixed because it *covers* the tab bar; this
 * one sits above it, which is the whole difference.
 */

.askscroll {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding-top: 14px;
}

.daymark {
  align-self: center;
  font-family: var(--mono);
  font-size: calc(11px * var(--scale));
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--tx4);
}

/* Your line is a bubble and the answer is not: a question is yours and brief,
 * right-aligned in the tinted plane that already means "yours"; an answer is
 * read the way the record is read, full width on the page. */
.askmine {
  align-self: flex-end;
  max-width: 78%;
  background: var(--ac-bg);
  color: var(--tx);
  font-size: calc(16px * var(--scale));
  line-height: 1.4;
  padding: 10px 14px;
  border-radius: 16px 16px 4px 16px;
  white-space: pre-wrap;
  overflow-wrap: break-word;
}

.asktheirs {
  align-self: stretch;
  display: flex;
  flex-direction: column;
  gap: 10px;
  font-size: var(--t-task);
  line-height: 1.5;
  color: var(--tx2);
}
.asktext { overflow-wrap: break-word; }
.asktext b { color: var(--tx); font-weight: 600; }
.askcaret { color: var(--ac); }

/* The activity line: the trace, read aloud while it happens. */
.askact {
  display: flex;
  align-items: baseline;
  gap: 7px;
  font-family: var(--mono);
  font-size: calc(11px * var(--scale));
  letter-spacing: .05em;
  color: var(--tx4);
}
.askact .askactstar { color: var(--ac); }

/* Receipts, drawn as the rows the lists already taught — B1. */
.recrows {
  background: var(--card);
  border-radius: var(--radius);
  overflow: hidden;
}
.recrow {
  display: block;
  width: 100%;
  text-align: left;
  padding: 12px 14px;
  background: none;
  border: 0;
  font: inherit;
  color: var(--tx);
  cursor: pointer;
}
.recrow + .recrow { border-top: 1px solid var(--hair); }
.recrow:active { background: var(--hover); }
.recrow .rectitle { font-size: var(--t-body); line-height: 1.35; }
.recrow .age {
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx4);
  margin-left: 6px;
}
.recrow .recquote {
  display: block;
  font-size: var(--t-task);
  font-style: italic;
  color: var(--tx2);
  line-height: 1.45;
}
.recrow .recsrc {
  display: block;
  font-family: var(--mono);
  font-size: calc(11px * var(--scale));
  color: var(--tx4);
  margin-top: 4px;
}

/* The breath before the first event; reduced motion gets the words instead. */
.askbeat { display: flex; gap: 5px; padding: 4px 0; }
.askbeat i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--tx4);
  animation: askbeat 1.2s ease-in-out infinite;
}
.askbeat i:nth-child(2) { animation-delay: .2s; }
.askbeat i:nth-child(3) { animation-delay: .4s; }
@keyframes askbeat { 0%, 100% { opacity: .35 } 50% { opacity: 1 } }
@media (prefers-reduced-motion: reduce) {
  .askbeat i { animation: none; }
  .askbeat::after { content: "Writing it…"; font-size: var(--t-task); color: var(--tx3); }
}

.askproblem { font-size: var(--t-task); color: var(--tx3); }

/* A proposal is a card with the change spelled out and two verbs. Amber label,
 * because a proposal is the application talking about itself, which is the one
 * thing amber is spent on. */
.askproposal {
  display: flex;
  flex-direction: column;
  gap: 6px;
  background: var(--card);
  border: 1px solid var(--line-hard);
  border-radius: var(--radius);
  padding: 13px 15px;
}
.askproposal .plabel {
  font-family: var(--mono);
  font-size: calc(11px * var(--scale));
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--warn);
}
.askproposal .ptext { font-size: var(--t-task); color: var(--tx); line-height: 1.45; }
.askproposal .pverbs { display: flex; gap: 18px; margin-top: 4px; }
.askproposal .pgo,
.askproposal .pno {
  background: none;
  border: 0;
  padding: 6px 0;
  font: inherit;
  font-size: var(--t-task);
  cursor: pointer;
}
.askproposal .pgo { color: var(--ac); font-weight: 600; }
.askproposal .pno { color: var(--tx3); }
.askproposal .pqueued {
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx3);
}

.askempty { padding: 26px 6px; color: var(--tx3); font-size: var(--t-task); line-height: 1.55; }
.askempty p { margin: 0 0 10px; }
.askempty .askhint { color: var(--tx4); }

/* ── The composer — C3 ──────────────────────────────────────────────────── */

.askbar {
  display: flex;
  flex-direction: column;
  padding: 10px calc(var(--gutter) + var(--safe-r)) 10px calc(var(--gutter) + var(--safe-l));
  border-top: 1px solid var(--line);
  background: var(--card);
}
.askrow { display: flex; align-items: center; gap: 10px; }
.askfield {
  flex: 1;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  background: var(--inset);
  border-radius: 18px;
  padding: 7px 14px;
  min-height: 36px;
}
.askfieldbox {
  flex: 1;
  min-width: 120px;
  border: 0;
  background: none;
  resize: none;
  font: inherit;
  font-size: calc(16px * var(--scale));
  line-height: 1.35;
  color: var(--tx);
  outline: none;
  padding: 2px 0;
}
.askfieldbox::placeholder { color: var(--tx4); }
.asksend {
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 0;
  background: var(--ac);
  color: var(--on-ok);
  font-size: 16px;
  display: grid;
  place-items: center;
  cursor: pointer;
}
.asksend.off { background: var(--inset); color: var(--tx4); }
.asksend.stop { background: var(--bad); color: #fff; }
.asksend svg { width: 17px; height: 17px; display: block; }

/* The picker, above the field it belongs to: the sheet's rows, repositioned
 * for a bar that grows upward rather than a field that looks down. */
.asksuggest {
  position: static;
  margin-bottom: 8px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}

/* ── Work ───────────────────────────────────────────────────────────────── */
/* specs/initiatives.md §5, decided as B3 · G2.
 *
 * Almost nothing here, and that is the point of A6 having its own record but
 * none of its own furniture: the list is `.card` under `.sechead`, the row is
 * `.row`, the mark is the rounded square a group's avatar already wears, and
 * the breadcrumb is the person bar's. What is left is the three controls the
 * editor needs and one line of prose on the initiative's own screen. */

/* What it is, above what is open on it — one line, one rank of grey down, which
 * is the ladder a role under a name on Contacts already teaches. */
.aboutline {
  margin: 0;
  padding: 12px 16px;
  font-size: var(--t-task);
  color: var(--tx3);
}

/* A label over a row of choices, where `.fkey` would be a column beside one.
 * The categories are pills and a pill row is wider than a field's value column,
 * so the label goes above rather than shrinking the choices to fit beside it. */
.fieldlbl {
  padding: 16px 4px 0;
  font-size: var(--t-label);
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--tx3);
}

/* The category, and the state. One choice, always exactly one taken — which is
 * the F5 decision drawn: a control you can leave with two on would be a bucket
 * that no longer sums. `Uncategorised` is the first pill and is a value. */
.pillrow {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 8px 4px 0;
}
.chipopt {
  border: 1px solid var(--line-hard);
  background: var(--card);
  color: var(--tx2);
  border-radius: 999px;
  padding: 7px 14px;
  min-height: 36px;
  font-family: var(--ios);
  font-size: var(--t-task);
  cursor: pointer;
}
.chipopt.on {
  background: var(--ac-bg);
  border-color: var(--ac-bg);
  color: var(--ac);
  font-weight: 600;
}

/* The initiative a topic is part of, on the row — E1 in
 * specs/design/refinements-after-0150.html, and D1 for the colour.
 *
 * **A second hue, and it is the first this application has had.** The rules it
 * runs into are real and are worth restating: nothing is colour-coded here, and
 * the accent goes on anything that can be touched. This is neither an opinion
 * about the data — which is what dormancy's amber was and why it went — nor a
 * touch target. It says which of two kinds of thing a mark is, exactly as the
 * rounded square already does silently for a group, and two is a distinction
 * anybody holds where three would be a legend.
 *
 * Teal is people. Violet is work. */
.onchip {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--work-bg);
  color: var(--work);
  font-size: var(--t-meta);
  font-weight: 600;
  vertical-align: 1px;
  /* **140 px, and the number was argued down and then back up.** 86 made the
   * two-initiative row two lines instead of three — and truncated `DACH
   * transfer` to `DACH tr…` on every row that had it, which is the *common*
   * case paying for the rare one. A name you cannot read is not a mark.
   *
   * At 140 an ordinary name shows whole (`DACH transfer` sits at 92) and only a
   * genuinely long one gives. A title with two initiatives and a co-participant
   * still wraps, and that is E1's stated cost rather than something to tune
   * away: it cannot be one line at 390 pt whatever this number is. The whole
   * name is the `title` attribute, and E4's short name is the round that would
   * make even the wrap rare.
   *
   * **Scaled, which it was not, and that is 1.1.13's own lesson on a third
   * element.** `--t-meta` is `calc(10px * var(--scale))` and this cap was a
   * bare 140 — so on a Mac at 1.5 the text is drawn half again as large in a
   * box that never grew, and `Europe GTM Zephyr` came out as `Europe GTM …`
   * with a metre of empty row after it. A fixed length beside a scaled one is
   * a layout that works at exactly one scale. */
  max-width: calc(140px * var(--scale));
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* **Which group a topic came from, on the one list that mixes them.**
 *
 * Asked for from use: a meeting's topics are everybody in the room's *and their
 * groups'*, so the flat list had rows that belong to the leadership group
 * sitting among rows that belong to Michael with nothing saying which. On a
 * person's screen the group's name is the section label six points above, which
 * is why the chip is not drawn there — it would be the same word twice.
 *
 * **The mark is a shape and not a second colour**, which is this application's
 * rule about groups everywhere else: a person is a circle and a group is the
 * rounded square its avatar already is, learnt from the content rather than
 * from a legend. So the initiative chip stays the pill it was and this is the
 * square one, in the neutral ink — nothing on this phone is colour-coded, and a
 * group is a fact about the row rather than a judgement on it. */
.onchip.fromgroup {
  border-radius: 7px;
  background: var(--line);
  color: var(--tx2);
  font-weight: 500;
}

/* `@` and `#`, in the composer — A1 in
 * specs/design/refinements-after-0150.html.
 *
 * They share their slot with `Details`: the keys show while the line is empty
 * and `Details` once it is not, because `Details` means nothing before anything
 * is typed and the keys mean most there. That swap is what makes them free
 * rather than 74 px off a field whose only job is to be a line long enough to
 * write a topic on.
 *
 * 30 px drawn and 44 pt reached, which is the trade the section-header glyphs
 * already make: a 44 px box would push the row eleven pixels taller and the
 * composer is a row of a list. */
.newkeys { display: flex; gap: 6px; flex: none; }
.newkeys[hidden] { display: none; }
.newkey {
  position: relative;
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 8px;
  background: var(--inset);
  color: var(--ac);
  font-family: var(--ios);
  font-size: var(--t-body);
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
}
/* The rest of the 44 pt, taken in a pseudo-element that bleeds into the gap
 * rather than in the box — `share-placement.html`'s rule, on a second control. */
.newkey::after { content: ""; position: absolute; inset: -7px; }
.newkey:active { background: var(--ac-bg); }

/* ── The mail, as rows of the Inbox — specs/design/capture-inbox.html, C1 ──
 *
 * A flagged mail is a row of the same list the unplaced topics are in, wearing
 * the tab's own envelope where a topic wears its mark. It borrows `.item` for
 * the card's hairline and ground and **not** `data-topic`, so `attachRows`
 * binds nothing on it: there is nothing to discuss or close about a thing that
 * is not yet record. The body opens the composer card; the `✕` is a sibling —
 * the body is a `<button>`, and nothing may nest inside one. */
.item.mailitem { display: flex; align-items: stretch; }
.mailbody {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: flex-start;
  gap: 10px;
}
.mailbody .said { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.mailbody .ttl { font-size: var(--t-row); line-height: 1.4; color: var(--tx); letter-spacing: -0.1px; }
/* The envelope sits where a row's mark sits — 20 px on the first line — so
 * the two kinds of row read as one list with two marks. */
.mailmark {
  flex: none;
  width: 20px;
  height: 20px;
  margin-top: calc((var(--t-row) * 1.4 - 20px) / 2);
  color: var(--line-hard);
  stroke-width: 1.5px;
}
.item.mailitem .evx { align-self: center; }
.item.mailitem + .item::before, .item + .item.mailitem::before { left: 16px; }

.mailmeta {
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx4);
}

/* The people on the proposal sheet: a chip per matched person, pressed to
 * keep or drop — one tint, on anything that can be touched. The dashed one is
 * an address the record does not know — dashed because it is not settled, and
 * it opens the picker that settles it (P1). It read *inert* here, and was, for
 * exactly one round. */
.mailwho { display: flex; flex-wrap: wrap; gap: 6px; }
.mailchip {
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 4px 12px;
  background: none;
  font-family: var(--ios);
  font-size: var(--t-task);
  color: var(--tx3);
  cursor: pointer;
}
.mailchip.on {
  background: var(--ac-bg);
  border-color: var(--ac-bg);
  color: var(--ac-lite);
}
.mailchip.miss {
  border-style: dashed;
  color: var(--tx4);
}

/* The chip that adds anybody at all — P2. Dashed like the unmatched name
 * beside it, because both mean *this one is not settled yet* and both open the
 * same picker; a solid one would read as a person already on the topic. It is
 * last in the row for the reason the composer is first in a topics card: where
 * you press is where the thing appears. */
.mailchip.add { border-style: dashed; color: var(--ac); }
/* **The wait, and the press where there is no wait** — reported as *either show
 * a button to request the AI-summarised version, or if it is working, show the
 * progress*. Directly under the two fields it writes, because that is what it
 * is about; absent once they are written, because the ✦ on their labels is what
 * says a model wrote them.
 *
 * The dot is the merge bar's own, and it is here for the merge bar's reason:
 * words that do not move for eight seconds are what frozen looks like. */
/* **What was done to a flag** — Settings → Mail. A log is read down rather
 * than across, so the date leads in the rank a date always gets here and the
 * sentence follows it. A refusal is amber, which is what this application
 * spends on itself rather than on the data. */
.maillog { margin-top: 14px; display: flex; flex-direction: column; gap: 2px; }
.maillog .lbl {
  font-family: var(--mono);
  font-size: var(--t-meta);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--tx4);
  margin: 0 0 6px;
}
.logline { margin: 0; font-size: var(--t-task); color: var(--tx3); line-height: 1.5; }
.logline.odd { color: var(--warn); }
.logwhen { font-family: var(--mono); font-size: var(--t-meta); color: var(--tx4); }
.logwhy { display: block; font-size: var(--t-meta); color: var(--tx4); }

.mailwrite:empty { display: none; }
.mailwrite { padding: 10px 4px 0; }
.mailwrite .said.work {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--ios);
  font-size: var(--t-task);
  color: var(--tx3);
}
.mailwrite .beat {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ac);
  animation: beat 1.25s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) { .mailwrite .beat { animation: none; } }
.askwrite {
  min-height: 44px;
  padding: 0 14px;
  border: 1px dashed var(--line-hard);
  border-radius: 999px;
  background: none;
  font-family: var(--ios);
  font-size: var(--t-task);
  color: var(--ac);
  cursor: pointer;
}
.askwrite:active { background: var(--hover); }
.mailwrite .hint.problem { margin-top: 8px; }

.maildetail { display: flex; flex-direction: column; gap: 8px; padding: 14px 4px 0; }
.maildetail .lbl {
  font-family: var(--mono);
  font-size: var(--t-meta);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--tx4);
}

.card.mailempty { padding: 16px; display: flex; flex-direction: column; gap: 6px; }

/* The footer line: whose mailbox, and the one press that asks again now. A
 * button drawn as the quiet line it replaces, because "check now" is read
 * once and pressed rarely. */
.mailfoot {
  padding: 12px 4px 0;
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx4);
}
.mailfoot.asbutton {
  border: 0;
  background: none;
  text-align: left;
  cursor: pointer;
}
.mailfoot.asbutton:active { color: var(--ac); }

/* The waiting line went with the device-code flow it narrated; the native
 * sheet needs no code on this screen at all. */

/* ── Chat refinements — specs/design/chat-refinement.html, K2 · P3+P4 · L1 ─ */

/* ≡ — the one conversation control left, drawn at the bar's own weight. */
.chatctl {
  background: none;
  border: 0;
  padding: 0 4px;
  font: inherit;
  font-size: calc(22px * var(--scale));
  line-height: 1;
  color: var(--ac);
  cursor: pointer;
}
.chatctl.on { color: var(--tx); }

/* The model, as a speed — D1: bolt or hourglass, tap to cycle. */
.askmodel {
  flex: none;
  display: flex;
  align-items: center;
  gap: 5px;
  background: none;
  border: 1px solid var(--line);
  border-radius: 10px;
  min-height: 28px;
  padding: 0 7px;
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--ac);
  cursor: pointer;
}
.askmodel svg { width: 16px; height: 16px; display: block; }

/* ≡ — the conversations, newest first. */
.chathist { display: flex; flex-direction: column; padding-top: 8px; }
.chatpast {
  display: flex;
  flex-direction: column;
  gap: 3px;
  text-align: left;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--hair);
  padding: 12px 4px;
  font: inherit;
  cursor: pointer;
}
.chatpast .chatpastt { font-size: var(--t-body); color: var(--tx); line-height: 1.3; }
.chatpast.on .chatpastt { color: var(--ac); }
.chatpast .chatpasts {
  font-family: var(--mono);
  font-size: calc(10px * var(--scale));
  letter-spacing: .06em;
  color: var(--tx4);
}
.chatpast:active { background: var(--hover); }

/* L1 — a retrieval is one folded line until it is asked to be more. */
.recfold {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  background: var(--card);
  border: 0;
  border-radius: var(--radius);
  padding: 9px 12px;
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx3);
  cursor: pointer;
}
.recfold .foldchev { margin-left: auto; color: var(--tx4); font-family: var(--ios); }
.recfold:active { background: var(--hover); }

/* The starters — never a blank page. */
.starter {
  display: block;
  width: 100%;
  text-align: left;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 11px 14px;
  margin: 0 0 8px;
  font: inherit;
  font-size: var(--t-task);
  color: var(--tx2);
  cursor: pointer;
}
.starter:active { background: var(--hover); }

/* P4 — a batch is rows with ticks, and the verb counts what survives. */
.pbrow {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  text-align: left;
  background: none;
  border: 0;
  padding: 4px 0;
  font: inherit;
  font-size: var(--t-task);
  color: var(--tx);
  line-height: 1.35;
  cursor: pointer;
}
.ptick {
  width: 16px;
  height: 16px;
  border: 1.5px solid var(--ac);
  border-radius: 8px;
  background: var(--ac);
  flex: none;
}
.ptick.off { background: none; border-color: var(--line-hard); }
.pedit {
  background: none;
  border: 0;
  padding: 6px 0;
  font: inherit;
  font-size: var(--t-task);
  color: var(--ac);
  cursor: pointer;
}

/* ── Chat polish + the Discussed view — the second use round ─────────────── */

/* B1 — the held room under the thread that lets a question reach the top. */
.askspace { flex: none; }

/* A1 — the tab bar stands down while the composer holds focus, on the machine
 * whose keyboard halves the screen. The Mac keeps its bar: no keyboard eats
 * the window there, and in the rail and split layouts it is not at the bottom
 * at all. :has() carries the state, so nothing can be left stuck hidden. */
:root:not([data-runtime="mac"]) #app:has(.askfieldbox:focus) .tabbar { display: none; }

/* **And the same for every other field, which the chat's rule had to itself
 * until the column started measuring the visible viewport.**
 *
 * Before 0.1.83 `#app` was the height of the whole screen on the phone, so the
 * tab bar spent the keyboard's time *behind* it and nobody saw it. Now the
 * column ends where the keyboard begins — which is the fix, and it brought the
 * bar up with it, to sit in a 50 px strip immediately above the keys: a row of
 * destinations nobody can be going to while they are mid-sentence, taking the
 * space from the list they are writing into. It was reported the round after
 * that height landed, on the composer.
 *
 * One rule for every text field rather than a class per composer: what decides
 * this is a keyboard being up, and a keyboard is up exactly when something
 * takes the caret. `:has()` carries the state, so nothing can be left stuck
 * hidden — which is the reason the chat's own rule is written this way.
 *
 * `[readonly]` is excluded: Settings shows the five instructions in fields that
 * take a caret and raise no keyboard, and taking the bar away there would be a
 * bar that vanishes when you tap a paragraph to read it. */
:root:not([data-runtime="mac"]) #app:has(input:focus:not([readonly]):not([type="button"])) .tabbar,
:root:not([data-runtime="mac"]) #app:has(textarea:focus:not([readonly])) .tabbar { display: none; }

/* The first row of ≡ starts a clean conversation — an action among records,
 * so it wears the tint the rows only earn when open. */
.chatnew .chatpastt { color: var(--ac); }

/* The glyph alone on the phone, the word beside it on a Mac — one DOM, the
 * runtime's CSS shows one, exactly as the co-participants choose their form. */
.askmodel .mlong { display: none; }
:root[data-runtime="mac"] .askmodel .mlong { display: inline; }

/* A fold that is open keeps its line with the chevron turned, so what a tap
 * opened a tap can put away. */
.recstack { display: flex; flex-direction: column; gap: 6px; }
.recfold.open .foldchev { display: inline-block; transform: rotate(90deg); }

/* The Open · Discussed selector on a person's screen. */
.personview { margin-top: 8px; }

/* ── A proposal faces you — specs/design/mail-refinements.html, M2 ─────────
 *
 * The centered card is the sheetcard with its feet off the floor: same lift,
 * same anatomy, vertically centered and arriving with a settle rather than a
 * rise, because it did not come from below. The translate lives in the base
 * transform, so the animation scales around the settled position. */
.sheetcard.centered {
  bottom: auto;
  /* **Centred in what is visible, measured rather than taken as a
   * percentage.** `top: 50%` is half of whatever box the engine calls the
   * viewport, and on a Mac that box is the scene — so the card's middle sat
   * below the window's middle by half the title bar, and a card near the cap
   * had its head off the top. Reported as *the modal is super small and not
   * centered*, over a screenshot where the count and the clock were cut.
   * Driven at 1200×900, 1440×800 and 1000×640 in Chromium, where the two
   * boxes are the same and the card comes out centred either way — so this is
   * the fix for a machine this side cannot open, and it costs nothing on the
   * ones it can. `--sheet-h` carries the keyboard's own reasoning. */
  top: calc(var(--sheet-h) / 2);
  transform: translateY(-50%);
  animation: settle 200ms cubic-bezier(.22, .8, .3, 1);
}
@keyframes settle {
  from { transform: translateY(-50%) scale(.94); opacity: 0; }
  to   { transform: translateY(-50%) scale(1); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .sheetcard.centered { animation: fadecenter 120ms linear; }
  @keyframes fadecenter { from { opacity: 0; } }
}
:root[data-runtime="mac"] .sheetcard.centered { bottom: auto; }

/* ── The section header's refresh — R2 ─────────────────────────────────────
 *
 * The circular arrows at the right end of FLAGGED (n), the place Topics
 * teaches a section's own controls live. Red while the lineage is dead,
 * which is B1's sentence said where the eye already is; spinning only while
 * a press is being answered, because the one animation here has to mean
 * something is actually happening. */
.mailrefresh {
  border: 0;
  background: none;
  padding: 2px;
  cursor: pointer;
  color: var(--ac);
  display: grid;
  place-items: center;
}
.mailrefresh svg { width: 17px; height: 17px; }
.mailrefresh.bad { color: var(--bad); }
.mailrefresh.busy svg { animation: refspin 800ms linear infinite; }
@keyframes refspin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .mailrefresh.busy svg { animation: none; opacity: .5; }
}

/* ── The dates, in the rank facts get — D1 ─────────────────────────────────
 *
 * One quiet mono line at the editor's foot and the proposal's: the age's own
 * grey, reading as a caption about the record rather than as a field of it. */
.editmeta {
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx4);
  padding: 10px 4px 0;
}

/* ── The picker a dashed chip opens — P1 ───────────────────────────────────
 *
 * A small centered card of rows: New contact first, then the ranked
 * directory under a search field. Rows are buttons the full width, the
 * list's own hairline between them. */
.mailpicker { display: flex; flex-direction: column; gap: 10px; }
.mailpicker .rows { background: var(--card); border-radius: 12px; overflow: hidden; max-height: 320px; overflow-y: auto; }
.mailpicker .rows button {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 11px 14px;
  border: 0;
  background: none;
  text-align: left;
  font-family: var(--ios);
  font-size: var(--t-body);
  color: var(--tx);
  cursor: pointer;
}
.mailpicker .rows button + button { border-top: 1px solid var(--hair); }
.mailpicker .rows .mini {
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx4);
  margin-left: auto;
}
.mailpicker .rows .new { color: var(--ac); }

/* The suggested initiative: the rounded square the mark already means, drawn
 * but unpressed until confirmed — a guess is confirmed, never automatic. */
.mailchip.initiative { border-radius: 8px; }
.mailchip.initiative.on { background: var(--ac-bg); border-color: var(--ac-bg); color: var(--ac-lite); }

/* ── Release notes — the carousel on Sync and version ──────────────────────
 *
 * The current version and the three before it, one card each, scrolled
 * sideways with a snap. Cards are 86% wide so the next one peeks, which is
 * the whole affordance — a carousel whose first card fills the row reads as
 * a single card. The prose is js/notes.js; the colours are all tokens, so
 * the two themes come for free. */
.relnotes {
  display: flex;
  gap: 10px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  padding: 2px 2px 6px;
  scrollbar-width: none;
}
.relnotes::-webkit-scrollbar { display: none; }
.relcard {
  flex: 0 0 86%;
  scroll-snap-align: start;
  background: var(--inset);
  border-radius: 10px;
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.relcard .relmeta {
  margin: 0;
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx4);
}
.relcard .reltitle {
  margin: 0;
  font-size: var(--t-task);
  color: var(--tx);
}
.relcard .hint { margin: 0; }

/* ── The bar at five, and the chat re-read ─────────────────────────────────
 * specs/design/contacts-and-chat.html — C1 · E3 · K1 · S4 · A2 · T2. */

/* E3: who they are, under their name. The rank facts get everywhere here —
 * mono, `--tx4` — and a button, because tapping it is what corrects them. */
.personmeta {
  display: block;
  width: 100%;
  background: none;
  border: 0;
  padding: 1px 0 2px;
  text-align: left;
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx4);
  letter-spacing: .01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}
.personmeta:active { color: var(--ac); }

/* A2: the steps, while they run. They stack under each other at the quiet
 * rank — this is the machinery narrating itself, never the answer. */
.asksteps { display: flex; flex-direction: column; gap: 4px; }
.askstep {
  display: flex;
  align-items: baseline;
  gap: 7px;
  font-size: var(--t-label);
  color: var(--tx3);
}
.askstepmark { color: var(--ok); font-size: 11px; }
.askstep.live .askstepmark { color: var(--ac); animation: stepspin 1.1s linear infinite; }
@keyframes stepspin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .askstep.live .askstepmark { animation: none; }
}
/* The folded state is a button now — the same line it always was, plus a way
 * back into the trace. */
button.askact {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  cursor: pointer;
  text-align: left;
}
.askactmore { color: var(--tx4); margin-left: 2px; }

/* S4: the way down. A sibling of the scroller — see the merge bar — sitting
 * just above the composer, and gone the moment there is nothing below. */
.askjump {
  align-self: center;
  margin: 0 auto -4px;
  position: relative;
  z-index: 2;
  background: var(--card);
  border: 1px solid var(--line-hard);
  border-radius: 14px;
  padding: 4px 12px;
  font-size: var(--t-label);
  color: var(--tx2);
  box-shadow: var(--shadow);
  cursor: pointer;
}
.askjump[hidden] { display: none; }
.askjump:active { color: var(--ac); }

/* T2: the answer is the document. It was a rank below the topic titles it
 * describes, which is the one place in this application that is nothing but
 * reading — 17 px is what iOS calls body, and this is body. */
.asktext {
  font-size: var(--t-body);
  line-height: 1.55;
  color: var(--tx);
}

/* The marks an answer carries — bullets, numbers, a subject, a heading, a
 * literal. Spacing is the block's own, so paragraphs stop needing `pre-wrap`
 * and a list stops being a run of `·` characters in a paragraph. */
.askpara { margin: 0; }
.asktext > * + * { margin-top: 10px; }
.asklist {
  margin: 0;
  padding-left: 22px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.asklist li { padding-left: 2px; }
.asklist li::marker { color: var(--tx4); }
.askh {
  margin: 0;
  font-size: var(--t-task);
  font-weight: 600;
  color: var(--tx);
  letter-spacing: .01em;
}
.asktext code.askcode {
  font-family: var(--mono);
  font-size: .88em;
  background: var(--inset);
  border-radius: 4px;
  padding: 1px 4px;
}
.asktext i { font-style: italic; }

/* A `<select>` in a form row. `.fval` strips the border and the ground for an
 * input; a select also arrives with the platform's own chrome, which on a Mac
 * is a bevelled box that reads as a different kind of control from every other
 * row in the same card. The chevron is drawn back on as a background so the
 * row still says it can be opened — a picker with no mark is a field that
 * refuses to take a caret. */
select.fval {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 18px;
  background-image: linear-gradient(45deg, transparent 50%, var(--tx4) 50%),
    linear-gradient(135deg, var(--tx4) 50%, transparent 50%);
  background-position: right 7px top 50%, right 2px top 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  text-align: right;
  direction: rtl;
}
/* The list itself is the platform's, and on a dark theme it is drawn by the
 * system: the option's own colours have to be said or a Mac renders black on
 * black. */
select.fval option { direction: ltr; color: var(--tx); background: var(--card); }
.frow.stacked select.fval { text-align: left; direction: ltr; padding-right: 18px; }


/* ── What this round added ──────────────────────────────────────────────────
 * specs/design/ux-rework.html — the opened row's note line, the Edit mode's
 * tick and handle, the second selector, and the two marks A1 asked for. */


/* **Two questions in one row, a hairline apart** — H1. The gap is what keeps
 * them two controls rather than five segments in a line. */
.selectorrow {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 10px var(--gutter) 0;
}
/* `.segmented` is `width: 100%` because everywhere else it is the only control
 * on its row. Two of them on one row need to be told to share it instead —
 * `min-width: 0` is the half that is easy to leave out, and without it a flex
 * item refuses to shrink below its content and the second control runs off the
 * edge of the screen. Which is what it did. */
.selectorrow .segmented { margin: 0; flex: 1 1 auto; width: auto; min-width: 0; }
.selectorrow .segmented.density { flex: 0 0 auto; }

/* **The same control in a section header, at the header's scale.**
 *
 * A person's screen has a row of selectors under the name and Today has none,
 * so the day's Due section carries this in its header instead — beside the
 * share glyph's own treatment, and for the share glyph's reason: a 44 px box
 * here would push the label down eleven pixels on every screen that has one, so
 * the box is 30 and the rest of the 44 pt is taken in the padding, where there
 * is nothing to hit but the header. `flex: none`, or a control drawn for a full
 * row takes the whole of it. */
.sechead .segmented.density {
  flex: none;
  width: auto;
  margin-right: var(--gutter);
  padding: 1px;
  border-radius: 8px;
}
.sechead .segmented.density .segment {
  flex: none;
  min-height: 0;
  width: 34px;
  height: 28px;
  border-radius: 7px;
}
.sechead .segmented.density .segglyph { width: 16px; height: 16px; }
.selectorrow .personview { margin-top: 0; }
.segglyph { width: 19px; height: 19px; display: block; }

/* The Edit mode's box is a **square**, where the merge that preceded it was a
 * circle: a square is what a selection wears and a circle is what a completion
 * does, and nothing in this mode is being completed. */
.pickbox.sq { border-radius: 6px; }

/* The handle is a sibling of the body in this mode, so it is positioned rather
 * than laid out — the body is a button and a button may hold nothing. */
.card.topics.editing .item { display: flex; align-items: flex-start; }
.card.topics.editing .itembody { flex: 1; min-width: 0; }
.card.topics.editing .item > .rowgrip {
  align-self: center;
  padding: 10px 16px 10px 6px;
  cursor: grab;
}

/* The read-only card went with the Mac's tasks feed — D2's cut. What survives
 * of A1's authorship ladder is the `· from the …` note on a section label. */
.sechead .lbl .ro { color: var(--tx4); font-weight: 400; }

/* **A sentence a model wrote** — A1. The star is the mark this application
 * already spends on exactly that claim, and the ink is the rank it spends on
 * anything that is a fact about a topic rather than the topic itself. */
.item .desc.wrote { color: var(--tx3); }
.item .desc.wrote .star { color: var(--ac); margin-right: 5px; font-size: .85em; }

/* Close, in the Edit mode's bar. Red here and not on the opened row: this one
 * acts on several rows at once and on rows you are not reading, which is the
 * one place in this application a second look is worth a colour. */
.modebar .go.shut { background: var(--bad); color: #fff; }

/* **The picker, as rows of the card the field is in** — C1. The same argument
 * `.card.topics > .suggest` already makes for the composer: `.suggest` is drawn
 * as a box for the sheet, where it floats under a field on a sunk page, and a
 * box inside a card reads as a second card. Flush, with the card's own
 * hairlines, so what is under the caret is a continuation of the line. */
.fields > .suggest {
  margin: 0;
  border: 0;
  border-radius: 0;
  border-top: 1px solid var(--hair);
  background: none;
}
.fields > .suggest .sugg { padding-left: 14px; padding-right: 14px; }

/* ── Meetings ───────────────────────────────────────────────────────────────
 * specs/design/ux-rework.html, J1. Almost nothing new: the list is `.item`
 * rows, the bar is the session bar's own rule, and the topics are the topics
 * card. What is here is the four things that had no class yet. */

.meetmeta {
  display: block;
  margin-top: 3px;
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx4);
}
.meetmeta span + span::before { content: " · "; }

/* **Where the meeting came from, as a pill** — B1. The word `quill` used to sit
 * in this line as one more `·`-separated fact, which is what made it
 * indistinguishable from the duration beside it; a meeting started by hand now
 * says nothing at all, so what the eye reads is presence.
 *
 * It is last in the line and takes no separator, because a `·` in front of a
 * pill reads as though the pill were one more item in the list rather than a
 * mark on the row. Uppercase and tracked, at the rank the line already uses —
 * the pill is small enough that a lowercase word inside it reads as a word
 * somebody forgot to finish. */
.meetmeta .src {
  display: inline-block;
  margin-left: 7px;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--ac-bg);
  color: var(--src-ink);
  font-size: calc(var(--t-meta) * 0.9);
  letter-spacing: .06em;
  text-transform: uppercase;
  vertical-align: 1px;
}
.meetmeta span + span.src::before { content: none; }
/* A meta line whose only content is the pill spends no left margin on it. */
.meetmeta .src:first-child { margin-left: 0; }
/* A meeting that is still running says so where the title is, because that is
 * the row you came back to the tab to press. */
.livedot {
  display: inline-block;
  width: 7px;
  height: 7px;
  margin-left: 7px;
  border-radius: 50%;
  background: var(--ac);
  vertical-align: middle;
}
.meetingrow { display: flex; align-items: center; }
/* Who is here: the name takes the row and the way out sits on the trailing
 * edge. Without the flex the `×` wrapped under every name — the row is a `div`
 * with two children and nothing had told it they share a line. */
.personrow { display: flex; align-items: center; }
.personrow .itembody { flex: 1; min-width: 0; padding-right: 8px; }
.personrow .saidact { margin-right: 12px; font-size: var(--t-person); }
.meetingrow .itembody { flex: 1; min-width: 0; }
.meetprocess { flex: none; margin-right: 12px; }

/* **B1: the verb, in the navigation row.** It was in the band and the band has
 * gone; a verb cannot become a line of text, so this moved rather than went.
 * Beside the sync dot, where every control that belongs to a whole screen
 * lives, and drawn only while a meeting is running. */
.endit {
  flex: none;
  border: 0;
  background: none;
  color: var(--bad);
  font-family: var(--ios);
  font-size: var(--t-body);
  font-weight: 600;
  cursor: pointer;
  padding: 0;
}

/* The preparation: prose in a card, folded once the meeting is under way. */
.prepcard { padding: 4px 0; }
.prepline { margin: 0; padding: 8px 16px; font-size: var(--t-task); line-height: 1.45; color: var(--tx2); }
.prepline + .prepline { border-top: 1px solid var(--hair); }
.prepline.work { display: flex; align-items: center; gap: 9px; color: var(--tx3); }
.prepask {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  min-height: 44px;
  padding: 12px 16px;
  border: 0;
  background: none;
  color: var(--ac);
  font-family: var(--ios);
  font-size: var(--t-body);
  text-align: left;
  cursor: pointer;
}
.prepask .star { color: var(--ac); }

/* **What a model wrote is a rank down and wears the ✦** — A1, the same pair the
 * topic row's summary wears, because it is the same claim: these are not your
 * words. The star sits in the body's own padding rather than on the first line,
 * so the mark is about the passage and not about one sentence of it. */
.prepbody.wrote { position: relative; padding-top: 8px; }
.prepbody.wrote > .star {
  position: absolute;
  top: 12px;
  left: 16px;
  color: var(--ac);
  font-size: .85em;
}
/* Every line takes the gutter and not only the first. A hanging indent says the
 * mark belongs to the sentence it is beside; what is being marked is the
 * passage, and the whole of it is somebody else's writing. */
.prepbody.wrote .prepline { padding-left: 32px; color: var(--tx3); }
/* Why it is the plain one. Mono, like every other line in this application that
 * is about the application rather than about the record. */
.prepwhy {
  margin: 0;
  padding: 8px 16px;
  border-top: 1px solid var(--hair);
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx3);
}

/* The leftovers. A field that is the whole card, which is the one place in this
 * application that is true — everything else in a card is a row. */
.meetnotes {
  display: block;
  width: 100%;
  min-height: 88px;
  padding: 14px 16px;
  border: 0;
  border-radius: var(--radius);
  background: none;
  color: var(--tx);
  font-family: var(--ios);
  font-size: var(--t-body);
  line-height: 1.45;
  resize: none;
  overflow: hidden;
}
.meetnotes:focus { outline: none; }
.meetnotes::placeholder { color: var(--tx4); }

/* ── A section that folds ───────────────────────────────────────────────────
 * Asked for on the meeting screen. The **grouped arrangement is kept**, which
 * is why this is not Settings' `<details>`: there the label is a summary inside
 * the card, and here it sits above a card floating on a sunk page. So the
 * header becomes the control and the chevron is the one this application
 * already spends on disclosure — `⌄` opens every section of Settings. */
/* **Two tracks, ten pixels apart** — S2 in specs/design/row-mark-and-groups.html.
 * Measured rather than estimated: the grouping track sizes to its content, so a
 * sort word keeps the 79 px it had when a hairline stood between them — the air
 * is free. Each track keeps its own sunk background, which is what makes the
 * pressed segment read as on *within its question* rather than within a row of
 * five. */
.twotracks { display: flex; align-items: center; gap: 10px; }
.twotracks .segmented { flex: 1; }
.twotracks .segmented.kinds { flex: none; width: auto; }
/* **Gutter to gutter** — asked for as *make sure the bar is covering side by
 * side, with the margins*, and drawn in specs/design/meetings-rows-and-the-bar.html.
 * The words' track sized to its three words and left 161 px of a 340 px row
 * to the air, which read as a bar that stopped short. It takes the row now and
 * the density pair stays pinned at the trailing end; the air is the gap. */
.twotracks .segmented.kinds { flex: 1; }
/* **Words now, so the segment sizes to its own word** rather than to a glyph's
 * box. It stays `flex: none` on the track so the question keeps only the width
 * it needs and the air after it does the rest.
 *
 * **Ten and no longer fourteen, which is what a third value costs** — B1 in
 * specs/design/topics-rest-state.html, and **the smallest screen is what
 * decides it**. Driven in the running application at three widths: the row is
 * 348 px at 390 pt, **333 at 375** and 388 at 430, and it spends two 10 px gaps
 * of its own before any control has been drawn. At fourteen,
 * `Mine · People · Work` is 212.2 and comes to 342 — which fits an iPhone 14
 * and is **over by 9 on an SE**. At ten it is 188.2 and fits all three, with
 * 39.8 px of air at 390 and 24.8 at 375.
 *
 * Ten is the documented floor rather than a number found by shrinking until it
 * fit: it is *the 10 px a side that keeps a segment from reading as text in a
 * box*, which is the sentence A1 chose the words with. `Initiative` is what
 * this already cost: at ten it is 213.9, which fits 390 and is **over by 11 at
 * 375** — so the word is the difference between fitting the fleet and fitting
 * most of it.
 *
 * **Every number here was taken twice**, because a parallel round moved the
 * type under it between the build and the merge: the first set was 346/331 with
 * three words at 351. Nothing about the decision changed and every figure did.
 * *A measurement is about the version it was taken on, and a merge is a new
 * version.* */
.twotracks .segmented.kinds .segment { flex: 1 1 auto; padding: 0 10px; }
/* **The air, asked for in as many words** — *with some space*. It is the
 * flexible part of the row rather than a margin on either control, so what is
 * left over after two tracks that both size to their content is spent here and
 * the density pair stays pinned to the trailing edge whatever the type scale
 * does to the glyphs. */
.twotracks .trackair { flex: 1; min-width: 10px; }
/* With the words' track stretching, the air is the gap and no longer the
 * flexible part of the row — both at `flex: 1` split the surplus in half. */
.twotracks .trackair { flex: 0 0 0; min-width: 0; }
.twotracks .segmented.density { flex: none; width: auto; }
.twotracks .segmented.density .segment { flex: 0 0 47px; }

/* **A section header says when it is about a group** — G1. The rounded square a
 * group's avatar already wears, at label size, so the mark is learnt from the
 * content rather than from a legend. Not a colour: nothing is colour-coded on
 * this phone.
 *
 * **An initiative had one and no longer does.** It was this square with a cross
 * in it, and nothing else in the application draws that — so unlike the group's
 * square it was a mark you could only learn from a legend there is no room for.
 * Under `Work` every heading is an initiative, which makes a mark on each of
 * them furniture rather than information. */
.hdrmark {
  display: inline-block;
  width: 11px;
  height: 11px;
  border: 1.5px solid currentColor;
  border-radius: 3px;
  margin-right: 7px;
  vertical-align: -1px;
  opacity: .75;
}

/* Which section the pane beside the list is showing — the row's own tint, on a
 * header now that the sections are the list. */

.sechead.fold { padding: 0; }
.sechead.fold .foldbtn {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  /* **`--sec-space` and not 14, and it is the folding header that makes this
   * worth saying.** A `.sechead.fold` has `padding: 0` — the button spends the
   * gutter, so the target is the whole row rather than the words in it — which
   * means the air above a *group* is this padding and never the one on
   * `.sechead`. Every section of the grouped list folds, so the plain header's
   * padding reaches almost nothing on the one screen the space was being asked
   * about. Found by measuring both kinds rather than the one that was reported:
   * the token was moving and the screen was not. */
  padding: var(--sec-space) var(--gutter) 6px;
  border: 0;
  background: none;
  text-align: left;
  cursor: pointer;
  /* **A button does not inherit its font**, and the section label is the one
   * place in this application where that matters most: without this the mono
   * uppercase rank the label has had since `grouped.html` came out as the
   * body font in sentence case, which reads as a different kind of thing
   * altogether. Seen in a screenshot, which is the only place it could be. */
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  color: inherit;
}
/* **A folding label may shrink, and until it could the chevron was off the
 * screen.** `flex: none` was right while a folding header was a word and a
 * count — the label sizes to its own text and the chevron follows it. It
 * became wrong the moment one carried a note: `WHO WAS IN THE ROOM (1) ·
 * press a name to correct it` is **446 px of max-content in a 356 px button**
 * at 390 pt, so the label ran 90 px past the edge of the screen and took the
 * `⌄` with it — the one control that opens and shuts the section, drawn at
 * x=501 on a 390 pt phone.
 *
 * Identical at 375, 390 and 430, which is the tell: a flex item that refuses
 * to shrink is not measuring the window at all. It is the `min-width: 0`
 * lesson on a third element, and the honest fix is the same one — let it
 * shrink, and let a note that does not fit take a second line rather than
 * being cut. The header grows about ten pixels on the one section that has a
 * long note, which is cheaper than a hint nobody can read. */
.sechead.fold .lbl { flex: 0 1 auto; min-width: 0; }
/* **The chevron sits in a box, or the rotation turns about a baseline.**
 * Reported as not well aligned, and that is what it was: `⌄` is a text glyph
 * whose ink sits low in its line box, so rotating it -90° swung the whole box
 * and left the mark off-centre against the label beside it. A fixed square
 * with the glyph centred in it turns about its own middle, which is the only
 * point that stays put. */
.sechead.fold .foldchev {
  flex: none;
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  color: var(--tx4);
  font-size: var(--t-task);
  line-height: 1;
  /* Pointing right when shut and down when open, which is the direction
   * Settings' own triangle turns. */
  transform: rotate(-90deg);
  transition: transform 140ms ease;
}
.sechead.fold.open .foldchev { transform: none; }
.sechead.fold .secact { margin-right: var(--gutter); }
/* A folded section has no card under it, so the next label would sit against
 * this one. */
.sechead.fold + .sechead { padding-top: 6px; }

/* The people picker on a meeting is `.suggest` now, flush in the card it is
 * asked from — the treatment `.fields > .suggest` gives it in an editor. */
.card > .suggest {
  margin: 0;
  border: 0;
  border-radius: 0;
  border-top: 1px solid var(--hair);
  background: none;
}
.card > .suggest .sugg { padding-left: 16px; padding-right: 16px; }

/* ── The simplification round — specs/design/ux-simplification.html ──────────
 *
 * A1 · B2 · C1 · D1. Almost everything Today and the sweep draw is a shape
 * this file already had — the composer row, the topic row, the section head,
 * the row verbs — which is the round's whole bet. What is new is the guess:
 * a dashed chip, unpressed by construction, on a just-filed row and on a
 * stray being swept. Dashed because the mail proposal's unmatched chip
 * already taught that dashes mean "not yet decided", and accent because it
 * can be touched. */
/* **The whole width, under the title.** A row that wears a mark is a flex box,
 * so this line — appended after the body — became its third column and sat
 * beside the title with the title wrapped around it. Found by looking, not by
 * a guard: the chips were there, pressable and wrong. It wraps to its own line
 * and starts where the text starts, past the mark's column. */
.item.tick { flex-wrap: wrap; }
.item.tick .guessline { flex: 1 0 100%; padding-left: 50px; }
.item .guessline {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 0 16px 16px;
  margin-top: -6px;
}
.guesschip {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  padding: 4px 13px;
  border: 1px dashed var(--ac);
  border-radius: 999px;
  background: none;
  color: var(--ac);
  font-family: var(--ios);
  font-size: var(--t-task);
  cursor: pointer;
}
.guesschip:active { background: var(--ac-bg); }
/* `Not this` and `Skip`: a decline is quiet — it writes nothing and should
 * not be dressed like the half that does. */
.guesschip.mut { border-color: transparent; background: var(--inset); color: var(--tx3); }

/* The sweep's head row — the count is a fact and stays ink; the verb at the
 * end is the accent, exactly the composer row's division of labour. */
.sweeprow .newlabel { color: var(--tx2); }
.sweepgo { flex: none; color: var(--ac); font-size: var(--t-body); }

/* ── The composer card — specs/design/capture-inbox.html, A1 · B1 ──────────
 *
 * Todoist's shape on the sheet's bones: `.sheetcard` already spends `--kb`, so
 * the card rises over the keyboard on every machine, and `.capture` takes the
 * bar off it — no title, no Cancel, no verb at the title's height. The line,
 * the note and the tools are rows of one `.newrow`, because that is the box
 * the composer's binder reads; `.newrow::after`'s hard separator is the row's
 * own and is not wanted on a card that has no list under it. */
.sheetcard.capture { border-radius: 18px; }
.sheetcard.capture .sheetscroll { padding: 12px 14px 10px; }
.newrow.caprow { flex-wrap: wrap; align-items: stretch; gap: 8px; padding: 0; min-height: 0; }
.newrow.caprow::after { display: none; }
.capline { flex: 1 0 100%; font-size: var(--t-body); padding: 6px 2px; }
.capline::placeholder { color: var(--tx4); }
.capnote {
  flex: 1 0 100%;
  border: 0;
  background: none;
  padding: 0 2px 4px;
  color: var(--tx2);
  font-family: var(--ios);
  font-size: var(--t-task);
  line-height: 1.45;
  resize: none;
  min-height: 0;
}
.capnote:focus { outline: none; }
.capnote::placeholder { color: var(--tx4); }
/* The tools, in one row: the three keys, the kind, the chips and the arrow.
 * The chips take what is left and wrap; the arrow is pinned to the trailing
 * end, which is where Todoist's is and where the thumb already is. */
.captools {
  flex: 1 0 100%;
  display: flex;
  align-items: center;
  gap: 8px;
  padding-top: 4px;
  border-top: 1px solid var(--hair);
}
.caprow .newkeys { flex: none; gap: 6px; }
.caprow .newkeys[hidden] { display: flex; }
.capkind { flex: none; display: grid; place-items: center; width: 30px; height: 30px; }
/* The star on the card — the row's own star at rest, the accent while it holds. */
.capstarslot { flex: none; display: grid; place-items: center; width: 30px; height: 30px; }
.capstar { width: 30px; height: 30px; border: 0; background: none; border-radius: 8px; color: var(--tx4); display: grid; place-items: center; cursor: pointer; position: relative; }
.capstar::after { content: ""; position: absolute; inset: -7px; }
.capstar svg { width: 20px; height: 20px; display: block; }
.capstar.on { color: var(--ac); }
.capstar:active { background: var(--hover); }
.capmarks { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; gap: 6px; }
/* A chip is where this will land, and pressing it takes it off — the `@`
 * never typed. One tint, on anything that can be touched; the `Mine` chip is
 * a fact and wears the ink facts get. */
.capchip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: 30px;
  padding: 3px 10px;
  border: 0;
  border-radius: 8px;
  background: var(--ac-bg);
  color: var(--ac);
  font-family: var(--ios);
  font-size: var(--t-task);
  line-height: 1.2;
  max-width: 100%;
  cursor: pointer;
}
.capchip > span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.capchip .capsigil { font-weight: 700; opacity: .8; }
.capchip:active { background: var(--inset); }
.capchip.mut { background: var(--inset); color: var(--tx3); cursor: default; }
/* The chip that asks — C1 in specs/design/mine-by-category.html. Dashed and
 * amber, the mail proposal's grammar for *not settled*: a category cannot be
 * a chip, so this one asks which of its initiatives, and pressing it opens
 * the picker rather than taking it off. */
.capchip.ask { background: none; border: 1px dashed var(--warn); color: var(--warn); }
/* **The chips on a detected topic are controls** — A1 · B1 · C1 in
 * specs/design/detected-topic-chips.html.
 *
 * The two that *add* are dashed in the accent rather than in amber: amber is
 * the mail proposal's *this is a guess, check it*, and these are not guesses —
 * they are empty slots asking to be filled. `.ask.add` and `.ask.work` are the
 * same shape as the guess chip and a different colour, which is the one thing
 * that separates *not settled* from *nothing chosen yet*.
 *
 * **The line wraps and nothing truncates — C1, and it is a measurement.**
 * Driven at 390 pt: the line is 310 px and the two chips already there want
 * 276; a third is 226 on the longest live initiative name, so three want 507
 * at worst and 388 at best, and 388 is over even at 430. Every chip keeps its
 * whole name and the line takes a second row where it must, which is
 * `flex-wrap` doing what it already did. */
.capchip.ask.add, .capchip.ask.work { border-color: var(--ac); color: var(--ac); }
.capchip .capoff { margin-left: 2px; font-size: var(--t-meta); opacity: .7; }
/* The chips are a sibling of the body now, not children of it — nothing may
 * nest inside a `<button>` — so the gutter is theirs to spend. */
.claimcard > .claimchips { display: flex; flex-wrap: wrap; gap: 6px; padding: 7px 13px 0; }
/* The cross in the title, a rank quieter than the reading. */
.navpick .navwho { color: var(--tx3); font-weight: 500; }
/* The who row at the head of the menu — the application's own segmented
 * control, in the picker's card. */
/* **The controls sit on the page now** — specs/design/inbox-controls.html,
 * A1 · B1 · C1 — so the who row is a track of the navigation bar rather than a
 * row of a card, and the arrangement rides `.twotracks`, which is the row
 * Topics already draws and needs nothing new. */
.navbar > .segmented.who { display: flex; margin: 2px 0 0; }
/* **No margin of its own, and `width: auto`.** Driven for
 * specs/design/meetings-rows-and-the-bar.html: the navigation bar already
 * spends the gutter as padding, and this row spent it again as a margin while
 * keeping the 100 % width every `.segmented` carries — so it began at 38 px and
 * ended at 386 on a 390 pt screen, one gutter of air on the left and four
 * pixels on the right, which is the *not covering side by side* that was
 * reported. It sits where the arrange row under it sits now, 21 to 369. */
.navbar > .segmented.who { width: auto; }
.navbar > .segmented.who .segment { flex: 1; }
/* The star segment sizes to its glyph — G1 — so the three words keep the row. */
.navbar > .segmented.who .segment.glyph.star { flex: 0 0 auto; }
/* **A picked name has to fit the segment it is drawn in.** Measured at 375,
   390 and 430 pt: three segments split the row into a third each, where a full
   name wants 117 to 147 — so `shortWho` gives the first name and a surname
   initial where the first name is shared, and this is the floor under that
   arithmetic for a name nobody measured. */
.navbar > .segmented.who .segment {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* **The row becomes a field** — B1. 59 people carry an open topic and a rail
 * of them is 7.8 screens wide even as bare initials, so *right there* is the
 * control turning into the one thing that narrows 59 to two rather than all of
 * them laid out at once. The hits hang under it in the same track, so nothing
 * is covered but the top of the list it is narrowing. */
/* **The walk-in list's own arrangement, in the section head's action slot** —
 * H1. It is the Topics row's control at the header's rank: the head already
 * spends that slot on share, `Edit` and `Open ›`, so nothing had to be made
 * room for. */
/* Its own row under the section it arranges — measured; see `arrangeRow`. The
   gutter is the card's, so the control lines up with the rows below it. */
.twotracks.arrange { margin: 2px var(--gutter) 6px; }
.segmented.kinds.walkin { display: flex; gap: 0; }
.segmented.kinds.walkin .segment { padding: 3px 9px; font-size: var(--t-meta); }

/* The same inset as the row it replaces — none of its own, the bar's padding is the gutter. */
.whoask { display: flex; flex-direction: column; gap: 6px; margin: 2px 0 0; }
.whofieldrow { display: flex; align-items: center; gap: 8px; }
.whofield {
  flex: 1;
  min-width: 0;
  background: var(--card);
  color: var(--tx);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 8px 12px;
  font: inherit;
  font-size: var(--t-row);
}
.whofield:focus-visible { outline: 2px solid var(--ac); outline-offset: 1px; }
.whoclose {
  flex: none;
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  background: none;
  border: 0;
  border-radius: 8px;
  color: var(--tx3);
  font-size: var(--t-task);
  cursor: pointer;
}
.whoclose:active { background: var(--hover); }
.whohits { display: flex; flex-direction: column; background: var(--card); border: 1px solid var(--line); border-radius: 10px; overflow: hidden; }
.whohit {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  padding: 9px 12px;
  min-height: 40px;
  background: none;
  border: 0;
  color: var(--tx);
  font: inherit;
  font-size: var(--t-row);
  text-align: left;
  cursor: pointer;
}
.whohit + .whohit { border-top: 1px solid var(--line); }
.whohit:active { background: var(--hover); }
.whohit .mini { font-family: var(--mono); font-size: var(--t-meta); color: var(--tx4); flex: none; }

.capsend {
  flex: none;
  width: 34px;
  height: 34px;
  border: 0;
  border-radius: 50%;
  background: var(--ac);
  color: var(--bg);
  display: grid;
  place-items: center;
  cursor: pointer;
}
.capsend svg { width: 18px; height: 18px; }
.capsend:active { background: var(--ac-lite); }
/* The mail's card carries Dismiss at the other end of the same row. */
.captools.mailtools { justify-content: space-between; margin-top: 14px; }
/* The mail's picker lands under the card's own line, styled for the lifted plane. */
.sheetcard.capture .suggest { margin: 0; }

/* ── The look-and-feel tuner — A3 · B1 · C2 · D1 · E1 ───────────────────────
 *
 * **A panel and not a screen**, because there is no second window: Capacitor is
 * one WebView on both machines, and what this control needs is to be *over* the
 * thing it is tuning. It floats, drags by its head, collapses to that head and
 * closes with a ✕ — so the preview is the real record rather than a specimen of
 * it, which is the whole reason for building this when a page in a browser
 * already exists.
 *
 * `position: fixed` and a child of `body`, exactly as a sheet is: the screens
 * here are replaced wholesale, and a control holding a drag cannot be one of
 * the things being replaced. */
.lookpanel {
  position: fixed;
  z-index: 60;
  /* **It opens at the foot and not at the head, which driving decided.** Pinned
   * top-right it landed exactly on the navigation bar — the search field and
   * the pills — which on a 390 pt screen are how you walk to the list you came
   * to tune. Covering the end of a list costs a row; covering the controls
   * costs the gesture the panel exists to support. It still drags anywhere. */
  bottom: calc(var(--safe-b) + 64px);
  right: 12px;
  width: min(330px, calc(100vw - 24px));
  max-height: calc(100dvh - var(--safe-t) - var(--safe-b) - 96px);
  display: flex;
  flex-direction: column;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 14px;
  box-shadow: var(--shadow);
  overflow: hidden;
}
/* Collapsed to its own head, which is what a panel over a list has to be able
 * to become: the thing you are looking at is underneath it. */
.lookpanel[data-shut] .lookchips,
.lookpanel[data-shut] .lookbody,
.lookpanel[data-shut] .lookmeter,
.lookpanel[data-shut] .lookfoot { display: none; }

.lookhead {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 10px 10px 14px;
  border-bottom: 1px solid var(--line);
  cursor: grab;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}
.lookhead:active { cursor: grabbing; }
.looktitle { flex: 1; font-size: var(--t-task); font-weight: 600; color: var(--tx); }
.lookfold,
.lookx {
  flex: none;
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  background: none;
  border: 0;
  border-radius: 8px;
  color: var(--tx3);
  font: inherit;
  font-size: var(--t-task);
  line-height: 1;
  cursor: pointer;
}
.lookfold:active,
.lookx:active { background: var(--hover); color: var(--tx); }

/* **The presets, with `Design` first and never editable** — D1. The uneditable
 * first entry is the load-bearing part: it is what makes every experiment
 * reversible without anybody remembering ten numbers. */
.lookchips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--line);
}
.lookchip {
  padding: 5px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--inset);
  color: var(--tx2);
  font: inherit;
  font-size: var(--t-meta);
  cursor: pointer;
}
.lookchip.on { background: var(--ac-bg); border-color: var(--ac); color: var(--ac-lite); font-weight: 600; }

.lookbody { overflow-y: auto; padding: 4px 12px 8px; }
.lookgroup {
  margin: 10px 0 4px;
  font-family: var(--mono);
  font-size: var(--t-meta);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--tx4);
}
.lookrow { display: flex; align-items: center; gap: 10px; padding: 3px 0; }
.looklbl { flex: 0 0 88px; font-size: var(--t-meta); color: var(--tx3); }
.lookrange { flex: 1; min-width: 0; accent-color: var(--ac); }
.lookval {
  flex: 0 0 44px;
  text-align: right;
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--t-meta);
  color: var(--tx2);
}

/* **E1's line, and it is read off the rendering** — the row height these
 * sliders actually produced, against the floor. A number computed from the
 * inputs is not a measurement, which is the lesson the marks round paid for. */
.lookmeter {
  margin: 0;
  padding: 8px 12px;
  border-top: 1px solid var(--line);
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx3);
}

.lookfoot {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  border-top: 1px solid var(--line);
}
.lookname {
  flex: 1;
  min-width: 0;
  padding: 7px 9px;
  background: var(--inset);
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--tx);
  font: inherit;
  font-size: var(--t-meta);
}
.looksave,
.lookdel {
  flex: none;
  padding: 7px 11px;
  border: 0;
  border-radius: 8px;
  background: var(--inset);
  color: var(--tx2);
  font: inherit;
  font-size: var(--t-meta);
  cursor: pointer;
}
.looksave { background: var(--ac-bg); color: var(--ac-lite); font-weight: 600; }
.lookdel:active,
.looksave:active { filter: brightness(1.15); }

/* The button that opens it, in Settings beside the theme and the text size. */
.lookopen {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  min-height: 44px;
  padding: 0 2px;
  background: none;
  border: 0;
  color: var(--ac);
  font: inherit;
  font-size: var(--t-task);
  cursor: pointer;
}

/* ── The Meetings tab: a week, a day, and what is on it ──────────────────────
 * specs/design/meetings-tab.html — A1 · B1 · C1 · E2 · F3 · G1+G2 · H2.
 *
 * The strip lives in the navigation bar rather than at the head of the
 * scroller, and that is the decision the rest of this block hangs off: it is
 * the control that says *what the list is of*, and a control that scrolls away
 * from the list it governs is one you have to scroll back for. The bar already
 * spends `--safe-t`, so nothing here learns where the top of the screen is. */
.weekstrip {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 2px var(--gutter) 10px;
}
/* **Every control here resets itself**, because this stylesheet has no global
 * button rule — each component has always set its own ground, and a control
 * that forgets gets the browser's grey box with a border round it. Which is
 * exactly what the first build of this strip shipped: seven day cells drawn as
 * seven little buttons, on a screen whose whole language is flat rows. */
.weekarrow, .weekday, .weekpick, .mcell {
  background: transparent;
  border: 0;
  font: inherit;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}
.weekarrow {
  flex: none;
  width: 26px;
  padding: 8px 0;
  color: var(--tx3);
  font-size: calc(17px * var(--scale));
  line-height: 1;
}
.weekdays { display: flex; flex: 1; min-width: 0; gap: 1px; }
/* Seven equal columns, so the day you want is where it was last time — which
 * is the whole of A1 and the reason an arrow is a week and not a day. */
.weekday {
  flex: 1 1 0;
  min-width: 0;
  padding: 5px 0 6px;
  border-radius: 8px;
  color: var(--tx);
}
.wdn {
  display: block;
  font-family: var(--mono);
  font-size: calc(9.5px * var(--scale));
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--tx4);
}
.wdd { display: block; font-size: var(--t-row); font-weight: 600; margin-top: 1px; }
/* Counted in the record: five days in fourteen carried anything at all, so
 * most cells are empty and the dot is what the eye lands on. Reserved rather
 * than conditional — a row of cells that changes height as you walk through
 * the weeks is a strip that never sits still. */
.wdot {
  display: block;
  width: 4px; height: 4px;
  margin: 3px auto 0;
  border-radius: 99px;
  background: var(--tx4);
}
.wdot.none { background: transparent; }
.weekday.istoday .wdd { color: var(--ac); }
.weekday.sel { background: var(--ac); }
.weekday.sel .wdn, .weekday.sel .wdd { color: var(--bg); }
.weekday.sel .wdot { background: var(--bg); }
.weekpick {
  flex: none;
  width: 30px; height: 30px;
  margin-left: 4px;
  display: grid;
  place-items: center;
  color: var(--ac);
  position: relative;
}
/* 30 px drawn and 44 pt reached, the share glyph's own arithmetic — a 44 px
 * box would push the strip's own height up by eleven pixels. */
.weekpick::before {
  content: "";
  position: absolute;
  inset: -7px;
}
.pickglyph { width: 20px; height: 20px; }
/* ── The connection mark at the end of Bento's strip ───────────────────────
 *
 * The Microsoft Graph connection the push spends, where the eye already is:
 * the accent while it is good — the one meaning that colour has here, *you
 * can touch this* — and red the moment it is not, which is the mail glyph's
 * own rule (`.mailrefresh.bad`). The picker's box and the picker's reach,
 * so the strip's height does not move for it. */
.graphmark {
  flex: none;
  width: 30px; height: 30px;
  margin-left: 4px;
  display: grid;
  place-items: center;
  color: var(--ac);
  position: relative;
  background: transparent;
  border: 0;
  font: inherit;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}
.graphmark::before { content: ""; position: absolute; inset: -7px; }
.gglyph { width: 20px; height: 20px; }
.graphmark.expired, .graphmark.dead, .graphmark.off { color: var(--bad); }
.graphmark.unknown { color: var(--tx4); }
.graphmark.busy .gglyph { animation: refspin 800ms linear infinite; }
.graphmark:disabled { cursor: default; }
@media (prefers-reduced-motion: reduce) {
  .graphmark.busy .gglyph { animation: none; opacity: .5; }
}

/* The month sheet — B1. A grid and not two rolling lists, because the dots are
 * the reason to open it: a list reaches any date and says nothing about which
 * days had anything on them. */
.monthpick { display: flex; flex-direction: column; gap: 8px; }
.monthhead { display: flex; align-items: center; justify-content: space-between; }
.monthname { font-size: var(--t-head); font-weight: 650; }
.monthdow, .monthgrid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.monthdow span {
  text-align: center;
  font-family: var(--mono);
  font-size: calc(10px * var(--scale));
  color: var(--tx4);
}
.mcell {
  aspect-ratio: 1;
  border-radius: 8px;
  display: grid;
  place-content: center;
  color: var(--tx);
  min-height: 38px;
}
.mcell.out { visibility: hidden; }
.mdd { font-size: var(--t-row); }
.mcell.sel { background: var(--ac); }
.mcell.sel .mdd { color: var(--bg); }
.mcell.sel .wdot { background: var(--bg); }

/* A row on the day. The clock is a column of its own and nothing shares it,
 * which is the meetings row's own finding on Today: a clock inside the title
 * meant every wrapped line ran back underneath it. */
.mclock {
  flex: none;
  width: calc(46px * var(--scale));
  font-family: var(--mono);
  font-size: var(--t-task);
  color: var(--tx4);
  padding-top: 2px;
}
.meetingrow .itembody { display: flex; gap: 10px; align-items: flex-start; }
.meetingrow .mwho {
  display: block;
  color: var(--tx4);
  font-size: var(--t-task);
  margin-top: 2px;
  /* One line, cut from the far end: a fourteen-person meeting is in this
   * record, and the height belongs to what the meeting was rather than to the
   * guest list. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.meetingrow { flex-wrap: wrap; }

/* G2 — a reason to press, rather than a badge asking to be cleared. */
.torai {
  display: block;
  margin-top: 6px;
  color: var(--ac);
  font-family: var(--mono);
  font-size: var(--t-task);
}

/* H2 — the prep arrives in place. The line says which of the three states it
 * is in, and the dot is the merge bar's own: words that do not change for eight
 * seconds are what frozen looks like. */
.prepline {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 6px;
  color: var(--ac);
  font-size: var(--t-task);
}
/* The dot is the merge bar's own, and it is here for the merge bar's reason:
 * words that do not change for eight seconds are what a stopped application
 * looks like. It is drawn only while the model is out — a `hidden` attribute
 * would have been defeated by the `display` this rule sets, which is how an
 * empty span came to hold a line's worth of gap open. */
.prepline .beat {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ac);
  animation: beat 1.4s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) { .prepline .beat { animation: none; } }
.prepbody {
  padding: 0 var(--gutter) var(--row-pad) calc(var(--gutter) + 46px * var(--scale));
  color: var(--tx2);
  font-size: var(--t-task);
}
.prepbody p, .transcript p { margin: 0 0 8px; }
.prepbody ul, .transcript ul { margin: 0 0 8px; padding-left: 18px; }
.prepbody li, .transcript li { margin: 0 0 5px; }
.prepbody h4, .transcript h4 {
  margin: 12px 0 5px;
  font-size: var(--t-row);
  font-weight: 650;
  color: var(--tx);
}
.prepbody h4:first-child, .transcript h4:first-child { margin-top: 0; }

/* E2 — the whole transcript, flowing. `--t-task` and not body: a transcript is
 * long, and the rank the topic titles use would make an hour of speech a
 * morning of scrolling. */
.transcript {
  padding: var(--row-pad) var(--gutter);
  color: var(--tx2);
  font-size: var(--t-task);
  line-height: 1.55;
}
.transcript b { color: var(--tx); font-weight: 600; }
.transcript i { color: var(--tx2); }
.transcript code { font-family: var(--mono); font-size: .92em; color: var(--tx); }
.transcript p { margin: 0 0 10px; }
.transcript ul, .transcript ol { margin: 0 0 10px; padding-left: 20px; }
.transcript li { margin: 3px 0; }
/* A nested list is tighter than the one it hangs under: the indent is what
 * says it is nested, and a second gap says it twice. */
.transcript li > ul, .transcript li > ol { margin: 2px 0 4px; }

/* **Three heading ranks and no more.** `#` is the write-up's own title, `##`
 * its numbered sections, and anything deeper is one quiet label — a fourth
 * would be a size nobody could tell from the third. They are `<p>` with a
 * class rather than `<h3>`/`<h4>`/`<h5>` for the reason the sheet's fields
 * are: a heading element brings the browser's own size and margins with it,
 * which is what made every level of this come out identical and heavy. */
.transcript .mdh { font-weight: 660; color: var(--tx); }
.transcript .mdh1 { font-size: var(--t-head); margin: 4px 0 8px; letter-spacing: -0.01em; }
.transcript .mdh2 { font-size: var(--t-body); margin: 18px 0 6px; }
.transcript .mdh3 {
  font-size: var(--t-meta);
  margin: 14px 0 4px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--tx3);
}
.transcript .mdh1:first-child, .transcript .mdh2:first-child { margin-top: 0; }

/* `---` is a rule, not a paragraph reading `---`. Nineteen of the twenty
 * write-ups carry one. */
.transcript hr { border: 0; border-top: 1px solid var(--line); margin: 16px 0; }

/* **A table drags inside its own box** — 17 of 20 write-ups carry one, and a
 * three-column grid at 390 pt is wider than the phone. `min-width: 0` is what
 * keeps the scroll here rather than on the screen, which is the lesson this
 * repository has now learnt on four elements. */
.transcript .tablewrap { overflow-x: auto; margin: 0 0 12px; min-width: 0; }
.transcript table { border-collapse: collapse; font-size: var(--t-meta); min-width: 100%; }
.transcript th, .transcript td {
  border-bottom: 1px solid var(--line);
  padding: 6px 12px 6px 0;
  text-align: left;
  vertical-align: top;
}
.transcript th { color: var(--tx); font-weight: 620; white-space: nowrap; }
.transcript td { color: var(--tx2); }

/* The clocks nobody stopped — the sweep's own dress on a second noun. Quiet,
 * at the foot, and gone the moment it has done its work: it is a repair the
 * application is offering, not a list you work. */
.sweeprow {
  display: flex;
  align-items: baseline;
  gap: 10px;
  width: 100%;
  margin-top: var(--sec-space);
  padding: var(--row-pad) var(--gutter);
  background: transparent;
  border: 0;
  border-radius: var(--radius);
  text-align: left;
  font: inherit;
  cursor: pointer;
}
.sweepwhat { color: var(--tx4); font-size: var(--t-task); flex: 1; min-width: 0; }
.sweepdo { color: var(--ac); font-size: var(--t-task); flex: none; }

/* ── The day's dump — specs/design/the-day-dump.html ───────────────────────
 *
 * A2's row at the head of the Meetings tab, and the page it opens. The row is
 * `.newrow`'s geometry with a count and a chevron on the end; the page is one
 * card holding one field, because the field *is* the screen.
 */
.dumprow { width: 100%; text-align: left; background: none; border: 0; cursor: pointer; }
.dumpmeta {
  margin-left: auto;
  flex: none;
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx4);
}
.dumpgo { flex: none; color: var(--tx4); font-size: var(--t-row); line-height: 1; }
/* With no count the chevron still needs pushing to the end. */
.dumprow .dumpmeta + .dumpgo { margin-left: 8px; }
.dumprow > .dumpgo:nth-child(3) { margin-left: auto; }

.dumpcard { padding: var(--row-pad) var(--gutter); }
/* **Tall before anything is typed**, which is what "un campo de texto grande"
 * asks for: a three-line box says *jot something*, and this says *the day went
 * like this*. `grows` takes it from here. */
.dumpfield {
  width: 100%;
  min-height: 320px;
  border: 0;
  background: none;
  padding: 0;
  resize: none;
  color: var(--tx);
  font-family: var(--ios);
  font-size: var(--t-body);
  line-height: 1.5;
}
.dumpfield:focus { outline: none; }
.dumpfield::placeholder { color: var(--tx4); opacity: 1; }
.dumpverbs {
  display: flex;
  gap: 10px;
  align-items: center;
  padding: 0 var(--gutter);
  margin-top: 12px;
  flex-wrap: wrap;
}
.dumphint {
  padding: 0 var(--gutter);
  margin: 14px 0 0;
  color: var(--tx3);
  font-size: var(--t-task);
  line-height: 1.4;
}

/* Paste and distill — specs/design/paste-and-distill.html, A2 · B2 · C1 · D1.
 * The dump's page in the Inbox's words: a shorter box, because a paste is
 * pasted rather than written and eight lines say "drop it here"; the cards
 * under it are the meeting's own block, inside the page's gutter. */
/* A fixed box that scrolls inside itself: a long paste must never grow the
 * page under the cards, so the page's scroller is left to the cards alone. */
.pastefield { height: 240px; min-height: 0; overflow-y: auto; resize: none; }
.pasteverbs { display: flex; gap: 10px; align-items: center; padding: 0 var(--gutter); margin-top: 12px; flex-wrap: wrap; }
.pasteverb { border: 0; border-radius: 999px; padding: 10px 20px; font: inherit; font-size: var(--t-task); font-weight: 600; background: var(--inset); color: var(--tx2); cursor: pointer; }
.pasteverb.go { background: var(--ac); color: var(--on-ok); }
.pasteverb.warn { background: var(--bad); color: var(--on-ok); }
.pasteverb:disabled { opacity: .4; cursor: default; }
.pastereason { color: var(--warn); }
.pasteproposals { margin: 14px var(--gutter) 0; }
.claimcard.noteon .detected { color: var(--tx3); }
.claimcard.noteon .claimbody { cursor: default; }
.claimcard.noteon .claimbody:active { background: none; }
.capchip.on { color: var(--ac); background: var(--ac-bg); }

/* The preview — D1, and it is the mail proposal's card with ticks on it. */
.dumpplan { margin-top: 4px; }
.dumphead {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 12px 14px 8px;
}
.dumpwhy { font-family: var(--mono); font-size: var(--t-meta); color: var(--tx4); }
.dumpline {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  width: 100%;
  text-align: left;
  background: none;
  border: 0;
  padding: 8px 14px;
  cursor: pointer;
  font: inherit;
  color: var(--tx);
}
.dumpline.out { color: var(--tx4); }
.dumpline:disabled { cursor: default; }
/* **A square with a tick and never a circle**: a circle is what a to-do wears
 * and nothing here is being completed — the merge mode's own distinction, on
 * the other side of it. */
.dumptick {
  flex: none;
  width: 18px;
  height: 18px;
  margin-top: 2px;
  border: 1.5px solid var(--line-hard);
  border-radius: 5px;
}
.dumptick.on {
  background: var(--ac);
  border-color: var(--ac);
  box-shadow: inset 0 0 0 3px var(--card);
}
.dumptitle { flex: 1; min-width: 0; font-size: var(--t-row); line-height: 1.35; }
.dumpnone {
  padding: 4px 14px 12px;
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx4);
}
/* What is missing, said where the count is — the merge bar's own sentence. */
.dumpnote {
  margin: 10px 2px 0;
  font-size: var(--t-task);
  color: var(--tx3);
  min-height: 1em;
}

/* ── The star on the Topics bar — G1 in specs/design/meetings-rows-and-the-bar.html ──
 * A glyph segment first in the who row, behind the hairline the order row
 * already uses between two questions. Filled while it holds and in the accent,
 * which is the row's own star at rest. */
.segment.glyph.star { padding: 0 12px; color: var(--tx3); }
.segment.glyph.star[data-on] { color: var(--ac); }
.starmark svg { width: calc(18px * var(--scale)); height: calc(18px * var(--scale)); display: block; }

/* ── A meeting in the Inbox — A1 · C1 in specs/design/meetings-rows-and-the-bar.html ──
 * The day row's shape with three marks at the right: the ✕, the ⓘ and the
 * chevron, each the `.evx` box (30 px drawn, 44 pt reached) so nothing on the
 * row is a new size. The chevron turns as every fold's does. */
.item.inboxmeet { flex-wrap: nowrap; align-items: center; }
.item.inboxmeet .itembody { flex: 1; min-width: 0; }
.inboxmarks { display: flex; align-items: center; flex: none; gap: 0; margin-right: 4px; }
.inboxmarks .evx { margin-right: 0; }
.inboxmarks .foldmark .foldchev { font-size: var(--t-body); transition: transform .15s ease; display: inline-block; width: 20px; height: 20px; line-height: 20px; text-align: center; }
.item.inboxmeet.open .foldmark .foldchev { transform: rotate(180deg); }
.item.inboxmeet .torai { font-size: var(--t-meta); margin-top: 4px; }
/* The claim rows under it: a body that is a press, and the ✕ at the far end
 * of the verb line — right-aligned, where every other ✕ here sits. */
.card.inmeeting .claimrow { width: 100%; border: 0; background: none; text-align: left; font: inherit; color: inherit; cursor: pointer; }
.card.inmeeting .claimrow:active { background: var(--hover); }
.card.inmeeting .claimacts .drop { margin-left: auto; padding: 0 6px; }
.card.inmeeting { border-top: 1px solid var(--line-hard); }
/* B2 in specs/design/decided-rows-and-the-door.html: the door into the meeting
 * is the block's own title — the meeting's name in the accent and a chevron at
 * the head of the grey block, where the eye enters it — and there is no line
 * at the foot any more. */
.meetdoor {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  border: 0;
  background: none;
  text-align: left;
  padding: 8px 6px 2px;
  color: var(--ac);
  font: inherit;
  font-size: var(--t-task);
  font-weight: 600;
  cursor: pointer;
}
.meetdoor .meetdoorname { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.meetdoor .meetdoormark { flex: none; }
.meetdoor:active { opacity: .7; }
/* A second door, a rank quieter — Bento's *the document as it would go*,
 * at the foot of the block where it is about the file and not the room. */
.meetdoor.quiet { color: var(--tx3); font-weight: 500; padding: 8px 6px 6px; }
/* **The room's chips under an unfolded row** — the topic chips' own grammar
 * on the meeting, so who was in it is chosen the way who a topic is with is
 * chosen on the card under it. They sit between the door and the cards,
 * spending the cards' own inset. */
.claimcards > .roomchips { display: flex; flex-wrap: wrap; gap: 6px; padding: 4px 6px 10px; }
.roomchips .capchip.busy { opacity: .55; pointer-events: none; }
/* A1: a decision is the proposals' own card, read-only and a rank quieter. */
.claimcard.decided .claimbody { padding-bottom: 11px; cursor: default; }
.claimcard.decided .claimbody:active { background: none; }
.claimcard.decided .detected { color: var(--tx4); }
.claimcard.decided .claimtitle { color: var(--tx2); }

/* ── The row's second line — E1 in specs/design/meetings-rows-and-the-bar.html ──
 * Who it is with and what it is part of, at one height under the title. The
 * `.with` keeps its two readings (names or initials, measured after layout);
 * what changed is that it no longer flows inside the title's sentence. */
.item .tline { display: flex; align-items: center; flex-wrap: wrap; gap: calc(6px * var(--scale)); margin-top: calc(4px * var(--scale)); }
/* E2: the chips at the line's end, together, so they column down the card. */
.item .tline .tchips { margin-left: auto; display: inline-flex; flex-wrap: wrap; gap: calc(6px * var(--scale)); justify-content: flex-end; }
.item .tline .with { margin-left: 0; }
.item .tline .onchip { margin-left: 0; }
.item .tline .with[data-mode="avatars"] .avs { vertical-align: 0; }
/* The editor's own verbs — D1. The accent one is the topic's first verb. */
.inlineverb {
  border: 0;
  border-radius: 999px;
  padding: 7px 14px;
  font: inherit;
  font-size: var(--t-task);
  font-weight: 600;
  background: var(--inset);
  color: var(--tx2);
  cursor: pointer;
}
.inlineverb.go { background: var(--ac); color: var(--on-ok); }
.inlineverb + .inlineverb { margin-left: 8px; }
.sayfield::placeholder { color: var(--tx4); }

/* ── A proposal card per claim under a meeting — A2 in specs/design/detected-topics-and-the-actions-bar.html ──
 * The mail proposal's grammar: a lifted card inside the meeting, a `✦ Detected`
 * label, the person and the due as the card's own chips, the context in the
 * summary rank, and the verbs as pills with `Dismiss` in red at the far end. */
/* **Each proposal stands apart** — reported from the phone: at 6 px the cards
 * read as one white block with lines across it, and where one topic ended and
 * the next began had to be worked out from the `✦ Detected` label. Fourteen
 * pixels of the page between them — the gap two cards on a list already keep —
 * and the block becomes a column of cards. */
.card.claimcards { background: var(--bg); padding: 8px 10px 12px; border-top: 1px solid var(--line-hard); }
.claimcard { background: var(--card); border-radius: 10px; box-shadow: var(--shadow); margin-top: 14px; overflow: hidden; }
.claimcard:first-child { margin-top: 4px; }
.claimbody { display: block; width: 100%; text-align: left; border: 0; background: none; padding: 11px 13px 6px; font: inherit; color: inherit; cursor: pointer; }
.claimbody:active { background: var(--hover); }
.claimbody .detected { display: block; font-family: var(--mono); font-size: var(--t-meta); letter-spacing: .06em; text-transform: uppercase; color: var(--ac); }
.claimbody .claimtitle { display: block; font-size: var(--t-row); line-height: 1.35; margin-top: 3px; color: var(--tx); }
/* The chip line inside the body is a person's `Committed` section's, where the
   chips are still labels and still children of the row. */
.claimbody .claimchips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 7px; }
.claimctx { display: block; color: var(--tx3); font-size: var(--t-task); line-height: 1.4; margin-top: 6px; }
.claimverbs { display: flex; align-items: center; gap: 8px; padding: 4px 13px 12px; }
.claimpill { border: 0; border-radius: 999px; padding: 6px 14px; font: inherit; font-size: var(--t-task); font-weight: 600; background: var(--inset); color: var(--tx2); cursor: pointer; }
.claimpill.go { background: var(--ac); color: var(--on-ok); }
/* The third verb is a filled red pill and reads `Dismiss` — the same shape as
 * `File`, in the colour that says what it does, at the far end. It was a bare
 * ✕ in red type, and on the phone that was a control found rather than read. */
.claimpill.drop { margin-left: auto; background: var(--bad); color: var(--on-ok); font-weight: 600; padding: 6px 14px; }
.claimpill:disabled { opacity: .5; }
/* The context on a person's Committed row, a rank under the claim. */
.claimrow .claimctx { margin-top: 2px; }

/* ── The day's changes, folded at the foot of the page — C2 · D1 ──
 * The fold is `foldable`'s own; the rows are the log's sentence with the clock
 * in the column and the verbs at the trailing end. */
.card.recent { margin-bottom: 8px; }
.item.recentrow { display: flex; align-items: baseline; gap: 10px; padding: 10px var(--gutter); min-height: 44px; }
.item.recentrow + .item.recentrow { border-top: 1px solid var(--hair); }
.recentrow .rwhen { flex: none; width: calc(44px * var(--scale)); font-family: var(--mono); font-size: var(--t-meta); color: var(--tx4); }
.recentrow .rwhat { flex: 1; min-width: 0; font-size: var(--t-task); color: var(--tx2); line-height: 1.4; }
.recentrow .rwhat b { color: var(--tx); font-weight: 600; }
.recentrow .rverb { flex: none; border: 0; background: none; padding: 0 4px; font: inherit; font-size: var(--t-task); font-weight: 600; color: var(--ac); cursor: pointer; position: relative; }
.recentrow .rverb::after { content: ""; position: absolute; inset: -10px -6px; }
.recentrow .rverb:disabled { color: var(--tx4); }

/* ── Bento — specs/design/bento-start-again.html, A1 · B1 · C2 · D1 ──────── */

/* **The sixth tab is drawn everywhere and shown on the Mac.** The bar carries
 * it with `data-mac`; the phone's five columns never learn it is there,
 * because a hidden grid child takes no column. One attribute, one rule:
 * `screens.js` may not ask which machine it is on. */
:root:not([data-runtime="mac"]) .tab[data-mac] { display: none; }

.bentonote { padding: var(--row-pad) var(--gutter); display: flex; flex-direction: column; gap: 8px; }
/* The refusal is two ranks because it is two things: Microsoft's own reason,
 * in the warn ink this application spends on itself rather than on the data,
 * and under it the one press that answers it, a shade quieter. It spends no
 * inset of its own: the scroller's gutter is what the day's name, the list
 * and every row are measured from. */
.bentodead { margin: 0 0 8px; }
.bentodead .deadwhy { margin: 0; color: var(--warn); font-size: var(--t-task); }
.bentodead .deadcure { margin: 3px 0 0; color: var(--tx3); font-size: var(--t-task); }
.bentofacts .mono { font-family: var(--mono); }

/* **A meeting stands apart from the next one.** Each is its own plane with
 * 14 px of page between — the gap a proposal card already keeps, one number
 * rather than a second invented here — and no separator, there being nothing
 * left for a line to separate. */
.bentolist {
  background: none;
  border: 0;
  border-radius: 0;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.bentolist .item + .item::before { content: none; }

/* ── A1 · the row ──────────────────────────────────────────────────────────
 *
 * Three columns and never a wrap: the clock, the body, the marks. A grid
 * rather than a flex row because the Mac's WebView broke a wrapping flex row
 * one word per line a few rounds ago, and a grid is a statement about
 * placement rather than a consequence of how a line breaks. `min-width: 0`
 * on the body, or a long title pushes the marks off the plane. */
.bentolist .item.brow {
  --row-pad: 0px;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 0 14px;
  align-items: center;
  padding: 12px var(--gutter);
  min-height: 44px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.brow .bclock {
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx4);
  letter-spacing: .02em;
  min-width: calc(40px * var(--scale));
}
.brow .bbody { min-width: 0; }
/* **B1 — the line.** The lead in the row rank at full ink, the rest a shade
 * quieter and lighter: a name on a 1:1 with the title after it, or the title
 * alone. One line, cut from the far end, because a row that grows a line for
 * some meetings is a day that moves under the hand. */
.brow .bline {
  margin: 0;
  font-size: var(--t-row);
  line-height: 1.3;
  color: var(--tx);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.brow .blead { font-weight: 600; }
.brow .brest { color: var(--tx3); font-weight: 400; }
/* The facts in the mono rank — the people, `x of y`, the length — and the
 * same line is where a refresh says its steps, a push its wait, and a refusal
 * its reason. Nothing is added to the row for any of them. */
.brow .bfacts {
  margin: 3px 0 0;
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx4);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.brow .bfacts .bwarn { color: var(--warn); }
.brow .bfacts .bbad { color: var(--bad); }
.brow .bfacts .bsent { color: var(--ok); }
.brow .bfacts .bok { color: var(--tx2); }
.brow .bfacts .bon { color: var(--ac); }
.brow .bfacts .bsep { color: var(--tx4); }
/* **The pulsing dot** — the merge bar's own, the one animation here: words
 * that do not change for a minute are precisely what frozen looks like. */
.brow .bdot {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--ac);
  margin: 0 5px 1px 0;
  vertical-align: middle;
  animation: bentopulse 1.2s ease-in-out infinite;
}
@keyframes bentopulse { 0%, 100% { opacity: .25; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .brow .bdot { animation: none; } }

/* **The marks at the trailing edge** — Quill, refresh, push, and the ✕ a
 * step apart. 30 px drawn and 44 pt reached through a pseudo-element that
 * bleeds into the gap, the share glyph's own arithmetic; the push wears the
 * accent because it is the press that ends the loop, and only that one. */
.brow .bmarks {
  display: flex;
  align-items: center;
  gap: 8px;
}
.brow .bmark {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: calc(30px * var(--scale));
  height: calc(30px * var(--scale));
  border: 0;
  border-radius: 999px;
  padding: 0;
  margin: 0;
  background: var(--inset);
  color: var(--tx2);
  cursor: pointer;
  text-decoration: none;
  font: inherit;
}
.brow .bmark::before {
  content: "";
  position: absolute;
  inset: -7px;
}
.brow .bmark .bglyph { width: 60%; height: 60%; display: block; }
.brow .bmark.go { background: var(--ac); color: var(--on-ok); }
.brow .bmark.x { background: none; color: var(--tx3); margin-left: 6px; }
.brow .bmark.x .evxicon { width: 60%; height: 60%; }
.brow .bmark:disabled { opacity: .35; cursor: default; }
.brow .bmark:hover:not(:disabled) { color: var(--tx); }
.brow .bmark.go:hover:not(:disabled) { color: var(--on-ok); }
/* **D1 — a sent meeting recedes where it stands.** The whole plane, at the
 * opacity a named voice wore — this tab's own word for *answered* — and it
 * comes back on a hover so a sent meeting can still be read at full strength.
 * Bento is the Mac's tab, so a pointer is always there to do it with. */
.bentolist .item.brow.sent { opacity: .55; }
.bentolist .item.brow.sent:hover,
.bentolist .item.brow.sent:focus-within { opacity: 1; }

/* The fold at the foot, under one rule, and the rows behind it: quieter,
 * with the way back at the trailing end. */
.bentofoot {
  margin-top: 14px;
  padding-top: 4px;
  border-top: 1px solid var(--line-hard);
}
.bentolist.away { gap: 0; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); }
.bentolist.away .item + .item::before { content: ""; }
.bentogone { display: flex; align-items: center; gap: 14px; }
.bentogone .bclock { font-family: var(--mono); font-size: var(--t-meta); color: var(--tx4); min-width: calc(40px * var(--scale)); }
.bentogone .itembody { min-width: 0; flex: 1; }
.bentogone .title { color: var(--tx3); }
.bentoverb {
  flex: none;
  border: 0;
  border-radius: 999px;
  padding: 6px 14px;
  min-height: 32px;
  font: inherit;
  font-size: var(--t-task);
  font-weight: 600;
  cursor: pointer;
  background: var(--inset);
  color: var(--tx2);
}
.bentoverb.restore { margin-right: var(--gutter); }

/* The meeting page's line — the owner's *something to force a push*: what is
 * up and since when, and the press at the trailing end. */
.bentoline {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin: 0 0 8px;
  padding: 8px var(--gutter);
  background: var(--card);
  border-radius: var(--radius, 12px);
  font-family: var(--mono);
  font-size: var(--t-meta);
  color: var(--tx4);
}
.bentoline[data-bento-state="changed"] .bentowords { color: var(--warn); }
.bentoline[data-bento-state="refused"] .bentowords { color: var(--bad); }
.bentoline[data-bento-state="sent"] .bentowords { color: var(--ok); }
.bentoline .bentopush { flex: none; }
