/* ── Typefaces ─────────────────────────────────────────────────────────────
   Self-hosted: `style-src 'self'; font-src 'self'` rules out a font CDN, and
   a CDN would observe the IP of every person using this application on every
   page load — an unnecessary third-party disclosure for a system holding
   privileged matters. See static/fonts/NOTICE.

   Public Sans is the typeface of the US Web Design System, so the operational
   voice of the application is the civic-institutional one by construction
   rather than by coincidence. `swap` so text is readable before the font
   arrives; the fallback stack is chosen to be close in width.

   ONE DOWNLOADED FACE, AND IT IS THE SANS. The serif is whatever the machine
   already has -- Iowan Old Style, Palatino, Georgia -- so the names of things
   are drawn on the first paint with nothing to fetch and nothing to swap. */
/* One variable file covers every weight: Google serves the same woff2 for
   each weight requested, so three static files would be three copies. */
@font-face {
  font-display: swap;
  font-family: "Public Sans";
  font-style: normal;
  font-weight: 100 900;
  src: url("/static/fonts/publicsans-var.woff2") format("woff2-variations");
}

/* THE SERIF, SERVED FROM THIS ORIGIN AND NOT FROM A FONT CDN. Two reasons,
   and the second is the one that will later look like a simplification worth
   making: the CSP is style-src 'self'; font-src 'self', which a third-party
   stylesheet and third-party font files cannot satisfy; and a CDN is told
   when every page here loads, which in legal software means telling somebody
   else when a lawyer opens a case file. static/fonts/NOTICE carries the
   argument, the judgment it rests on, and what was dropped -- read it before
   replacing these three files with one <link>, which is the thing not to do.

   THREE SUBSETS, WHICH IS A DECISION ABOUT NAMES: unicode-range fallback is
   per GLYPH, so latin alone draws an accented name in two different faces
   mid-word. Variable weight 400-700, one file per subset. */
@font-face {
  font-display: swap;
  font-family: "Source Serif 4";
  font-style: normal;
  font-weight: 400 700;
  src: url("/static/fonts/sourceserif4-latin.woff2") format("woff2-variations");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-display: swap;
  font-family: "Source Serif 4";
  font-style: normal;
  font-weight: 400 700;
  src: url("/static/fonts/sourceserif4-latin-ext.woff2") format("woff2-variations");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-display: swap;
  font-family: "Source Serif 4";
  font-style: normal;
  font-weight: 400 700;
  src: url("/static/fonts/sourceserif4-vietnamese.woff2") format("woff2-variations");
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1,
    U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329,
    U+1EA0-1EF9, U+20AB;
}

:root {
  color-scheme: light;

  /* Cool paper. The change that carries the whole redesign. */
  --paper:        #f6f7f9;
  --paper-raised: #ffffff;
  --paper-quiet:  #fafbfc;

  /* Blue-black: fountain pen and court stamp, never neutral black. */
  --ink:          #141b26;
  --ink-muted:    #545e6e;
  --ink-faint:    #8a94a3;

  --rule:         #e4e8ed;
  --rule-strong:  #d2d8e0;
  --rule-firm:    #b4bdc9;

  /*
   * NAVY, kept from the cream experiment of 2026-09-04 when the paper went
   * back to grey. Not "warm on cool" any more, which is what this line used
   * to say: the accent now sits in the same cool family as the paper and is
   * one deliberate note against it rather than a contrast.
   *
   * Deep and muted are derived from it, because one value cannot serve
   * three roles. On white a link reads 13.4 against the oxblood's 8.9.
   */
  --accent:       #1c2e52;
  --accent-deep:  #15223e;
  --accent-muted: #636b7c;
  --wash:         #eef1f5;

  /* SEMANTIC ONLY, never decorative. --alert means overdue and nothing else,
     which is what lets a red row be believed on sight. */
  --alert:        #b3261e;
  --alert-wash:   #fbeeec;

  --green: #2c6e49;
  --brown: #6a5326;
  --gold:  #8a6508;
  --olive: #5f6b3a;

  /* The system stack stays behind the downloaded face as the fallback while
     it loads and if it fails. static/fonts/NOTICE carries the history. */
  --serif: "Source Serif 4", "Iowan Old Style", "Palatino Linotype", Palatino,
    "Book Antiqua", Georgia, "Times New Roman", serif;
  --sans: "Public Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, "Liberation Mono", monospace;
}

*, *::before, *::after { box-sizing: border-box; }

/* THE OPERATIONAL VOICE IS SANS. It used to be serif at 18px, which reads as
   a printed book rather than an instrument somebody works in all day — and at
   small sizes a serif in a dense table goes muddy. The serif now carries only
   the NAMES OF THINGS: page titles, matter names, the wordmark. A case name is
   a proper noun and setting it as one is what keeps the character. */
body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.55;
  margin: 0;
  -webkit-font-smoothing: antialiased;
}

/* ── Masthead ─────────────────────────────────────────────────────────────
   Sticky, 68px, its own wider bound so the wordmark, descriptor and nav sit
   on one line. One hairline beneath it and nothing else. */
.masthead {
  position: sticky; top: 0; z-index: 10;
  background: var(--paper);
  border-bottom: 1px solid var(--rule-strong);
}
.masthead-inner {
  align-items: center;
  display: flex;
  /* Back to 68px: it grew to 80 to hold the person's three links on a second
     row, and those live in a menu now, so the right-hand side is one row of
     name-and-title again. */
  gap: 1.5rem;
  height: 68px;
  justify-content: space-between;
  margin: 0 auto;
  max-width: 1500px;
  padding: 0 1.5rem;
  position: relative;
}
/* Whose system this is: the firm's own mark, its name, then the product. */
.brand { align-items: center; display: flex; gap: 1rem; }
/* Between the firm's name and the firm's work. It used to divide the name
   from the product's mark; the mark is gone from inside a firm and the
   navigation stands there now, so the divider still has something on both
   sides. It is drawn only when there IS a nav -- on a portal page or a
   signed-out one it would hang off the end of a name. */
.rule-v { background: var(--rule-strong); height: 1.5rem; width: 1px; }
.wordmark {
  color: var(--ink);
  font-family: var(--serif);
  font-size: 1.25rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  text-decoration: none;
  white-space: nowrap;
}
.descriptor {
  align-items: center;
  color: var(--ink-faint);
  display: flex;
  font-family: var(--sans);
  font-size: 0.72rem;
  gap: 0.4rem;
  letter-spacing: 0.1em;
  text-decoration: none;
  text-transform: uppercase;
  white-space: nowrap;
}
a.descriptor:hover { color: var(--ink); }
/* The mark now STANDS IN FOR the first word of the product name rather than
   decorating it, so it is sized to the descriptor's optical weight rather
   than to the 68px bar: a mark that fills the masthead's height competes with
   the firm's name instead of sitting beside it.
   The word itself is not written here, because the stylesheet must carry no
   firm or product name at all -- html.test.ts checks, and it caught this. display:block because an inline image sits on the text baseline and
   would hang below the flex centre.
   NO BACKTICKS IN THIS COMMENT -- the stylesheet is a template literal, and a
   backtick here ends it. That has now happened three times. */
.mark { display: block; height: 20px; width: 20px; }

/* THE NAVIGATION BEGINS WHERE THE FIRM'S NAME ENDS, and drifts with it by
   design -- it belongs to the firm, so it starts where the firm's name stops.
   It used to be centred on the page by absolute positioning so that it would
   NOT drift, which is why it needed pointer-events on the links only: the
   empty box it drew across the whole bar would otherwise swallow clicks. In
   the flow it needs neither. The auto right margin takes the slack, so the
   firm and its sections sit together on the left and the person sits hard
   right. (No firm name is written out here: the stylesheet must carry none,
   and html.test.ts caught the first attempt.) */
.masthead nav {
  display: flex;
  flex-wrap: wrap;
  gap: 1.4rem;
  margin-right: auto;
}
.masthead nav a, .masthead .settings-link, .masthead .logout button {
  color: var(--ink-faint);
  font-family: var(--sans);
  font-size: 0.74rem;
  letter-spacing: 0.08em;
  text-decoration: none;
  text-transform: uppercase;
}
.masthead nav a:hover, .masthead .settings-link:hover,
.masthead .logout button:hover { color: var(--ink); }
/* A button that sits with the links beside it. It is a form because signing
   out revokes the session server-side and a state-changing GET is reachable by
   anything that prefetches a page — see the page function in this file. */
.masthead .logout { margin: 0; }
.masthead .logout button {
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
}
/* The section you are in, marked by the one accent and nothing else. */
.masthead nav a.here, .masthead .settings-link.here {
  border-bottom: 2px solid var(--accent); color: var(--ink); padding-bottom: 2px;
}

/* Who you are, and the way to your settings. */
/* The person, then the button that holds what is addressed to them. */
.account { align-items: center; display: flex; gap: 0.9rem; }

/* THE MENU. Closed it is three bars; open it is a panel hanging under them,
   right-aligned to the button so it never runs off the edge of the window.
   It overlays rather than pushing the bar taller, which is why the panel is
   absolute and the details is the thing it positions against. */
.account-menu { position: relative; }
.account-menu > summary {
  align-items: center;
  color: var(--ink-faint);
  cursor: pointer;
  display: flex;
  height: 22px;
  list-style: none;
  padding: 0 2px;
}
.account-menu > summary::-webkit-details-marker { display: none; }
.account-menu > summary::marker { content: ""; }
.account-menu > summary:hover, .account-menu[open] > summary { color: var(--ink); }
/* Drawn rather than typed: a hamburger character renders differently in every
   font on the stack, and one of them would have had no glyph for it. */
.account-menu .bars, .account-menu .bars::before, .account-menu .bars::after {
  background: currentColor; display: block; height: 2px; width: 18px;
}
.account-menu .bars { position: relative; }
.account-menu .bars::before { content: ""; position: absolute; top: -6px; }
.account-menu .bars::after { content: ""; position: absolute; top: 6px; }
/* The one place a current page can show itself while the menu is shut. */
.account-menu > summary.here { color: var(--ink); }
.account-menu > summary.here .bars,
.account-menu > summary.here .bars::before,
.account-menu > summary.here .bars::after { background: var(--accent); }

.account-links {
  background: var(--paper-raised);
  border: 1px solid var(--rule-strong);
  border-radius: 3px;
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
  padding: 0.85rem 1.1rem;
  position: absolute;
  right: 0;
  top: calc(100% + 0.6rem);
  white-space: nowrap;
  z-index: 20;
}
/* In the panel each item is its own line, so the underline a current page
   draws would sit oddly across a whole row: it marks the text instead. */
.masthead .account-links .settings-link.here {
  border-bottom: 0; color: var(--accent); padding-bottom: 0;
}
.masthead .who {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
  text-align: right;
}
/* THE PERSON, IN THE SAME VOICE AS THE FIRM (Michael, 2026-09-18): the serif
   the wordmark uses, at 1rem against its 1.25rem, so it reads as a name
   rather than as a label and still sits below the firm whose masthead it is.
   AS THEY TYPED IT -- the small sans caps are gone, and with them the
   tracking that existed to keep capitals apart. A name is not a label, and
   every other surface in the application has always rendered it this way. */
.masthead .who .name {
  color: var(--ink);
  font-family: var(--serif);
  font-size: 1rem;
  font-weight: 600;
  white-space: nowrap;
}
/* The JOB TITLE, never the role — see the page function in this file. Muted and small:
   it identifies a colleague, it is not a status. */
.masthead .who .title {
  color: var(--ink-faint);
  font-family: var(--sans);
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}

@media (max-width: 62rem) {
  /* Nothing is centred any more, so the bar simply wraps and grows rather
     than squeezing three groups onto one 80px line. */
  .masthead-inner { flex-wrap: wrap; gap: 1rem; height: auto; padding: 0.75rem 1.5rem; }
  .masthead nav { gap: 1rem; margin-right: 0; }
}

/* ── Measure ──────────────────────────────────────────────────────────────
   Content is wide so a table can be wide. Prose is capped independently at
   68ch, which is what lets both be true on one page. */
.content { margin: 0 auto; max-width: 1260px; padding: 2.5rem 1.5rem 4rem; }
/* THE SAME WIDTH AS THE MASTHEAD, for a page that is a grid rather than a
   column of prose. Paragraphs inside it stay capped at 68ch by the rule above,
   so widening the frame widens the thing that needed it and nothing else. */
.content.wide { max-width: 1500px; }
/* THE SEARCH BAR, beneath the page's title and above its rule. A page with no
   heading carries it at the top of its content instead, with the space below
   it a heading's rule would have given. */
.searchbar { margin: 1rem 0 0; }
.content > .searchbar:first-child { margin: 0 0 2rem; }
.searchbar form.search { align-items: center; display: flex; gap: .5rem; margin: 0; max-width: 34rem; }
.searchbar input[type="search"] { flex: 1; margin: 0; min-width: 0; }
p, .prose { max-width: 68ch; }

/* ── Headings ─────────────────────────────────────────────────────────────
   Serif, and now explicitly so: the body is sans, and a title is a name. */
/* THE MEASURE IS 46ch AND IT WAS 20ch, WHICH IS A GLOBAL CONSTRAINT AND WAS
   REPORTED AS A TASK-PAGE PROBLEM. A heading wants a measure -- a long line at
   this size is hard to track -- but 20 characters is not one: it is shorter
   than almost every record this application names. Counted against a real
   tenant rather than guessed at: 20 of 21 task titles exceed it, 28 of 33
   contact names, 17 of 33 obligations, 11 of 20 documents, and every matter
   heading. So seven pages wrapped their title with the right half of the line
   empty, and widening the task page alone would have fixed one of the seven.

   46ch puts every task, document, contact, obligation and communication title
   in that tenant on one line -- the longest is 41 -- and holds a matter's
   number plus title, median 52 and max 65, to two lines rather than letting it
   run the full 1260px column.

   text-wrap: balance is the other half, and it is what the complaint was
   actually about. When a heading DOES wrap, the browser's default fills the
   first line and leaves a stub on the second; balance splits it evenly. The
   dead .caption h1 rule below reached the same conclusion at 30ch and never
   shipped, because nothing emits that class. */
h1 {
  font-family: var(--serif);
  font-size: clamp(1.7rem, 3.2vw, 2.3rem);
  font-weight: 600;
  letter-spacing: -0.02em;
  /* 1.25 because a heading wraps and a serif's line box is taller than 1.15.
     The measurement it came from, and the fact that it was NOT re-taken for
     the current face, are in static/fonts/NOTICE -- this file is served to
     every firm and is not where an argument belongs. */
  line-height: 1.25;
  margin: 0 0 .75rem;
  max-width: 46ch;
  text-wrap: balance;
}
/* The deck: a serif sentence under the title saying what this page is. Not a
   label, so not sans — it is content, and it reads as content. */
.deck { color: var(--ink-muted); font-family: var(--serif); font-size: 1.05rem;
  margin: 0 0 1.25rem; max-width: 56ch; }

/* A section heading is a small uppercase label with a hairline under it,
   spanning its column. The rule is what makes a dense page navigable without
   boxes or colour. */
h2 {
  align-items: baseline;
  border-bottom: 1px solid var(--rule);
  color: var(--ink);
  display: flex;
  font-family: var(--sans);
  font-size: 0.74rem;
  font-weight: 600;
  gap: 1rem;
  justify-content: space-between;
  letter-spacing: 0.1em;
  margin: 2.75rem 0 1rem;
  padding-bottom: .4rem;
  text-transform: uppercase;
}
h2 a { font-family: var(--serif); font-size: .9rem; font-weight: 400; letter-spacing: 0; }
h2 .count { color: var(--ink-faint); font-weight: 400; }

/* The head of a page: eyebrow, title, deck, a metadata row, then a rule. */
.page-head { border-bottom: 1px solid var(--rule-strong); margin-bottom: 2rem; padding-bottom: 1.5rem; }
/* THE TITLE ROW WITH ONE ACTION AT ITS FAR RIGHT (Home, Messages). Only a
   page that passes an action gets this row, so every other head is unchanged.
   The action sits above the rule and above the rail, level with the title,
   and drops beneath the title when the row is too narrow for both. */
.head-row { align-items: flex-start; display: flex; flex-wrap: wrap; gap: .75rem 1.5rem;
  justify-content: space-between; }
.head-row .head-title { flex: 1 1 auto; min-width: 0; }
.head-action {
  background: var(--ink);
  border: 1px solid var(--ink);
  border-radius: 2px;
  color: var(--paper);
  flex: 0 0 auto;
  font-family: var(--sans);
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  padding: .55rem 1.1rem;
  text-decoration: none;
  text-transform: uppercase;
}
.head-action:hover { background: var(--accent); border-color: var(--accent); color: var(--paper); }
/* A QUIETER HEAD, for a page whose content is already dense.
   The calendar's grid carries its own dates in every column heading, so a
   full-size headline restating the week was saying a second time what the
   columns say better -- and spending a third of the first screen to do it.
   The name of the page is still worth having; the size was not. */
.page-head.compact { margin-bottom: 1.35rem; padding-bottom: .85rem; }
.page-head.compact h1 { font-size: 1.2rem; letter-spacing: -0.01em; margin-bottom: .35rem;
  max-width: none; }
.meta-row { align-items: center; display: flex; flex-wrap: wrap; gap: .55rem; margin: 0; }
.meta-row .item { color: var(--ink-muted); font-size: .85rem; }
.meta-row .item.mono { font-size: .8rem; }
/* A middot between facts, so a run of them reads as one caption rather than
   as two sentences that happen to be adjacent. On the ::before of the SECOND
   and later items, so the first never carries a leading mark. */
.meta-row .item + .item::before { color: var(--rule-firm); content: "·";
  margin-right: .55rem; }
/* The facts line reads as one caption rather than as a row of chips, so the
   separators are hairline slashes and not gaps alone. */
.meta-row .sep { color: var(--rule-firm); }
.meta-row a { font-size: .85rem; }

/* A row of figures: the number in serif at size, its label in small sans
   beneath. Numbers are tabular so a row of them aligns. */
.figures { display: flex; flex-wrap: wrap; gap: 2.5rem; margin: 1.5rem 0 0; }
.figure .n { display: block; font-family: var(--serif); font-size: 2rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em; line-height: 1.1; }
.figure .l { color: var(--ink-faint); font-family: var(--sans); font-size: .72rem;
  letter-spacing: .08em; text-transform: uppercase; }

/* WHAT PRODUCED THE FIGURE ABOVE IT. A run of labelled amounts under a card's
   headline number, separated the way the head's facts line is -- because it is
   the same thing in a different place: several facts read as one caption. */
/* Dated events under a card's figure: when, how much, what happened. Money and
   dates are tabular so a column of them lines up, and the rows are quiet --
   the figure above is the answer and these are what produced it. */
.settled { margin-top: 1rem; width: 100%; }
.settled td { border: 0; font-size: .82rem; padding: .2rem 0; vertical-align: baseline; }
.settled td.when { color: var(--ink-muted); white-space: nowrap; width: 7.5rem; }
.settled td.right { padding-right: 1rem; white-space: nowrap; width: 6rem; }

.amounts { margin-top: .9rem; }
.amounts .pair + .pair::before { color: var(--rule-firm); content: "\00b7";
  margin: 0 .5rem; }
.amounts .n { font-variant-numeric: tabular-nums; }

h3 { font-size: 1.15rem; font-weight: 600; letter-spacing: -0.02em; line-height: 1.15; margin: 1.5rem 0 .5rem; }

.eyebrow {
  color: var(--ink-faint);
  font-family: var(--sans);
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  margin: 0 0 .5rem;
  text-transform: uppercase;
}

/* ── The one accent ───────────────────────────────────────────────────────*/
a { color: var(--accent); text-decoration: underline; text-decoration-thickness: 1px;
  text-underline-offset: 2px; }
a:hover { color: var(--ink); }
.provenance a { color: var(--accent-muted); }

/* ── Tables: the workhorse ────────────────────────────────────────────────
   Horizontal hairlines only, never vertical rules. Uppercase sans headers
   above a heavier line. Tabular figures so columns of numbers line up. */
table { border-collapse: collapse; font-variant-numeric: tabular-nums; width: 100%; }
th {
  border-bottom: 1px solid var(--rule-strong);
  color: var(--ink-faint);
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  padding: 0 .75rem .5rem 0;
  text-align: left;
  text-transform: uppercase;
  white-space: nowrap;
}
td {
  border-bottom: 1px solid var(--rule);
  padding: .6rem .75rem .6rem 0;
  vertical-align: top;
}
tbody tr:hover { background: var(--wash); }
.mono, .tabular { font-family: var(--mono); font-size: .875rem;
  font-variant-numeric: tabular-nums; }

/* ── Key/value: a definition list, not a table ────────────────────────────*/
dl { display: grid; gap: .6rem 1.5rem; grid-template-columns: max-content 1fr; margin: 0; }
dt {
  color: var(--ink-faint);
  font-family: var(--sans);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  padding-top: .2rem;
  text-transform: uppercase;
}
dd { font-variant-numeric: tabular-nums; margin: 0; }

/* A grid of cards, sharing hairlines the way a table shares rules. */
.cards { border: 1px solid var(--rule); border-radius: 3px; display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); }
.cards > * { border-bottom: 1px solid var(--rule); border-right: 1px solid var(--rule);
  padding: .9rem 1rem; }
.cards .name { display: block; font-size: 1.05rem; font-weight: 600; letter-spacing: -0.02em;
  margin-bottom: .25rem; }

/* ── Cards and the right rail ─────────────────────────────────────────────*/
.detail { display: grid; gap: 2.5rem; grid-template-columns: minmax(0, 1fr) 21rem; }
/* A COLUMN OF CARDS, for a page with enough sections that the hairline under
   each heading stops being enough to separate them.

   THE HOUSE STYLE IS FLAT and stays flat for most pages -- the rule under a
   heading is what makes a dense page navigable without boxes or colour, and
   that argument holds for a page with three sections. It stops holding at
   seven: the matter page ran Details into Participants into Obligations with
   nothing but whitespace between, and a person scrolling could not see where
   one ended. The hearing page has been carded since it was written and is
   what that was measured against.

   The gap comes from the grid rather than from margins, because .card > h2
   deliberately has none and a section's spacing should not depend on what its
   first child happens to be. */
.stack { display: grid; gap: 1.25rem; }
/* A collapsed section inside a card loses the top margin it carries when it
   is standing on its own. */
.stack > .card > details.section, .stack > details.section { margin: 0; }
/* Except from the section before it in the same card: the portal card's two
   parts ran summary into summary with nothing between (Michael, 2026-09-13). */
.stack > .card > details.section + details.section { margin-top: 1.5rem; }
/* Notices and the way out are not cards and are not spaced as though they
   were. */
.stack > .nextup, .stack > .danger { margin: 0; }
/* A CARD IS SPACED FROM WHATEVER COMES BEFORE IT, on every page -- not only
   inside a .stack. Only the matter page and home wrapped their cards in one,
   so everywhere else a card sat border to border with the card or the button
   above it: the hearing page's Mark billable touching Where, and Where
   touching Parties. A rule every page gets cannot be forgotten by the next
   page. Inside a stack or the rail the grid's gap already does it. */
* + .card { margin-top: 1.25rem; }
.stack > .card, .rail > .card { margin-top: 0; }
.rail { align-self: start; display: grid; gap: 1.25rem; position: sticky; top: 92px; }
/* ── The client portal ─────────────────────────────────────────────────────
   BLOCKS, NOT TABLES. A table is for comparing rows; a list of blocks is for
   reading one at a time, and on a phone nobody compares rows. Everything here
   is a single column that is already correct at 375px, so there is no
   breakpoint hiding anything and nothing scrolls sideways. */

.case-block { border-bottom: 1px solid var(--rule); padding: 1.1rem 0; }
.case-block h2 { font-size: 1.15rem; margin: 0; }
.case-block .sub { color: var(--ink-muted); font-family: var(--sans); font-size: .82rem;
  margin-top: .25rem; }

.latest-block { border: 1px solid var(--rule-firm); border-radius: 2px;
  margin: 1.4rem 0 2rem; padding: 1.2rem 1.1rem; }
.update h2, .update h3 { margin: 0 0 .2rem; }
.update .when { color: var(--ink-muted); font-family: var(--sans); font-size: .78rem;
  margin: 0 0 .6rem; }
.update .body p { margin: 0 0 .7rem; }
.update { margin-bottom: 1.6rem; }

/* The checklist sits under the update it belongs to, and is ABSENT rather
   than empty when there is nothing to do: an empty box reads as a section
   that failed to load. */
.checklist { border-top: 1px solid var(--rule); margin-top: 1rem; padding-top: 1rem; }
.checklist h3 { font-family: var(--sans); font-size: .8rem; letter-spacing: .04em;
  margin: 0 0 .7rem; text-transform: uppercase; }
.checklist .item { align-items: flex-start; display: flex; gap: 1rem;
  justify-content: space-between; padding: .55rem 0; }
.checklist .item .sub { color: var(--ink-muted); font-family: var(--sans); font-size: .8rem;
  margin-top: .15rem; }
.checklist .item.done .what { color: var(--ink-muted); }
.checklist .done-items { border-top: 1px dotted var(--rule); margin-top: .6rem;
  padding-top: .6rem; }
form.tick { flex: 0 0 auto; margin: 0; }
form.tick button { font-size: .78rem; padding: .3rem .7rem; white-space: nowrap; }
form.tick button.done { background: transparent; color: var(--ink-muted); font-weight: 400; }
/* ON HOME'S DAY ROWS, THE SIZE OF A BADGE: the Mark done at the right of an
   entry sits on the same line as a Double booked badge, and matches it
   (Michael, 2026-09-16). Scoped to the rows; the portal's checklist tick
   keeps its own size. */
.slot form.tick button { font-size: 0.68rem; letter-spacing: 0.08em; line-height: 1.6;
  padding: 0 .4rem; }

.when-block { border-bottom: 1px solid var(--rule); display: flex; gap: 1.1rem;
  padding: .8rem 0; }
.when-block .d { color: var(--ink-muted); flex: 0 0 7.5rem; font-family: var(--sans);
  font-size: .82rem; }
.when-block .sub { color: var(--ink-muted); font-family: var(--sans); font-size: .8rem;
  margin-top: .15rem; }

.message { border-bottom: 1px solid var(--rule); padding: .9rem 0; }
.message.mine { padding-left: 1rem; border-left: 2px solid var(--rule-firm); }
.message .sub { color: var(--ink-muted); font-family: var(--sans); font-size: .78rem; }
.message .body p { margin: .3rem 0 0; }
/* Withdrawn: the firm's copy is struck; the client's is a dated line with no words. */
.message.withdrawn .body { color: var(--ink-faint); text-decoration: line-through; }
.message .remove { margin-left: .5rem; }
/* BOTH STATES ARE SHOWN, because the blank is what teaches (portal.md §8.1). */
.receipt { color: var(--ink-muted); font-family: var(--sans); font-size: .78rem; }

.doc-block { border-bottom: 1px solid var(--rule); padding: .8rem 0; }
.doc-block .sub { color: var(--ink-muted); font-family: var(--sans); font-size: .8rem;
  margin-top: .2rem; }

/* The outstanding figure is the number a client came to find, so it is the
   largest thing in the block. The breakdown is a dl, which stacks by nature. */
.invoice-block { border: 1px solid var(--rule); border-radius: 2px; margin-bottom: 1rem;
  padding: 1rem; }
.invoice-block .head { font-family: var(--sans); font-size: .85rem; }
.invoice-block .figure { margin: .5rem 0 .2rem; }
.invoice-block .sub { color: var(--ink-muted); font-family: var(--sans); font-size: .8rem; }
.invoice-block dl { margin-top: .7rem; }

/* The firm's own mark, beside its name. Height fixed in the markup as well,
   because the masthead is sticky and an image whose box is unknown until it
   loads reflows the bar out from under the pointer. */
.wordmark .firm-mark { height: 28px; margin-right: .5rem; vertical-align: -6px;
  width: auto; }

.sign-off { border-top: 1px solid var(--rule); color: var(--ink-muted);
  font-family: var(--sans); font-size: .82rem; margin-top: 3rem; padding-top: 1.1rem; }
.sign-off p { margin: 0 0 .15rem; }

@media (max-width: 34rem) {
  /* 375px. The date column in a dates block stops being a column and becomes
     a line, which is the only place the portal has two columns at all. */
  .when-block { display: block; }
  .when-block .d { margin-bottom: .2rem; }
  .checklist .item { flex-direction: column; gap: .5rem; }
}

@media (max-width: 60rem) {
  .detail { grid-template-columns: minmax(0, 1fr); }
  .rail { position: static; }
}
.card {
  background: var(--paper-raised);
  border: 1px solid var(--rule);
  border-radius: 3px;
  padding: 1rem 1.1rem 1.15rem;
}
.card > h2, .card-title {
  color: var(--ink-faint);
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  margin: 0 0 .75rem;
  text-transform: uppercase;
}
.card dl { gap: .5rem 1rem; }
.card p { margin: 0; }

/* ── Home: today as one sequence ──────────────────────────────────────────
   A WORK GROUP IS A TEAM AND ITS DAY IS ONE DAY. Not a column per attorney:
   parallel calendars make you scan three lists to find nine o'clock. That
   only reads if every entry carries whose it is, which is what the wash and
   the name are for.

   THE TIME IS A FIXED COLUMN the eye runs down, because eleven entries at
   overlapping times is an ordinary Wednesday at a firm with three attorneys
   and eleven ragged rows is unreadable. */
.slots { display: grid; gap: .1rem; list-style: none; margin: 0; padding: 0; }
/* Later this week on Home: one line of days and counts, each opening that day. */
.daystrip { display: flex; flex-wrap: wrap; font-family: var(--sans); font-size: .85rem;
  gap: .35rem 1.25rem; margin: .2rem 0 0; }
.daystrip a { font-variant-numeric: tabular-nums; white-space: nowrap; }
.daystrip a.week { color: var(--ink-faint); }
/* The link under a capped list on Home, to the page that holds all of it. */
p.rest { font-family: var(--sans); font-size: .82rem; margin: .8rem 0 0; }
.slot { align-items: baseline; border-radius: 2px; display: flex;
  gap: .8rem; padding: .4rem .6rem; }
.slot .at { color: var(--ink-muted); flex: 0 0 5.5rem; font-family: var(--sans);
  font-size: .78rem; }
.slot .what { flex: 1 1 auto; min-width: 0; }
.slot .sub { color: var(--ink-faint); display: block; font-size: .75rem; }
.slots.quiet .slot { padding: .25rem .6rem; }
.slots.quiet .at { flex-basis: 7rem; font-size: .72rem; }
/* The wash comes from .whoN, which the calendar already generates and keys on
   the same profile order -- so a person is one colour on both pages rather
   than one colour per screen.

   A DOUBLE BOOKING IS THE SAME PERSON TWICE, never two people at one hour --
   that is an ordinary Tuesday. It overrides the wash because it is the one
   thing on this page somebody must resolve rather than attend, and two class
   names beat one. */
.slot.clash { background: var(--alert-wash); box-shadow: inset 3px 0 0 var(--alert); }

/* One consolidated queue of somebody-did-something-respond. */
.queue { display: grid; gap: .4rem; list-style: none; margin: 0; padding: 0; }
.queue li { border-bottom: 1px solid var(--rule); padding-bottom: .4rem; }
.queue li:last-child { border-bottom: 0; }
.queue .k { color: var(--ink-faint); font-family: var(--sans); font-size: .66rem;
  letter-spacing: .1em; margin-right: .5rem; text-transform: uppercase; }
.queue .sub { color: var(--ink-faint); font-size: .75rem; margin-left: .4rem; }

/* How long is left, which is the fact this page exists to carry. */
td.left { color: var(--ink-muted); font-family: var(--sans); font-size: .78rem;
  white-space: nowrap; }
td.left.late { color: var(--alert); font-weight: 700; }

/* ── The inbox: notes, read one at a time ─────────────────────────────────
   BLOCKS, NOT A TABLE. A note has a body, and a body is read rather than
   compared across rows. The staff pages keep their tables where somebody
   scans fifty rows for the odd one out; nobody scans their own post. */
.notes { display: grid; gap: 1rem; }
.note { background: var(--paper-raised); border: 1px solid var(--rule);
  border-radius: 3px; padding: .9rem 1.1rem 1rem; }
/* Unread carries a heavier left edge rather than a colour wash: the alert
   treatment means overdue on every other page and must keep meaning it. */
.note.unread { border-left: 3px solid var(--rule-firm); }
.note-head { align-items: baseline; display: flex; flex-wrap: wrap; gap: .6rem; }
.note-head .from { font-family: var(--sans); font-size: .8rem; font-weight: 700; }
.note-head .when { color: var(--ink-faint); font-family: var(--sans); font-size: .72rem; }
.note .on { margin: .3rem 0 0; }
.note .subject { font-family: var(--serif); font-size: 1.05rem; margin: .5rem 0 0; }
.note .body { color: var(--ink-muted); margin-top: .4rem; }
.note .body p { margin: 0 0 .4rem; }
/* The receipt, in the same register as the portal's: read with a time, or the
   button that makes it so. Both states are shown -- the blank is what teaches
   somebody that receipts exist here at all (portal.md 8.1). */
.note .receipt { color: var(--ink-faint); font-family: var(--sans); font-size: .72rem;
  letter-spacing: .06em; margin: .6rem 0 0; text-transform: uppercase; }
.note form.inline { margin: .6rem 0 0; }

/* ── Chips and badges: small, outlined, sparing ───────────────────────────*/
.chip, .badge {
  border: 1px solid var(--rule-strong);
  border-radius: 2px;
  color: var(--ink-faint);
  display: inline-block;
  font-family: var(--sans);
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  line-height: 1.6;
  padding: 0 .4rem;
  text-transform: uppercase;
  white-space: nowrap;
}
.badge-active   { border-color: var(--green); color: var(--green); }
.badge-second   { border-color: var(--brown); color: var(--brown); }
.badge-top      { border-color: var(--gold);  color: var(--gold); }
.badge-derived  { border-color: var(--olive); color: var(--olive); }

/* Sourcing and caveats: a chip, then a sentence of small sans in muted grey. */
.provenance {
  color: var(--ink-muted);
  font-family: var(--sans);
  font-size: 0.78rem;
  letter-spacing: 0.01em;
  margin: .75rem 0 0;
  max-width: 68ch;
}
.provenance .chip { margin-right: .5rem; }

/* ── Small structural text ────────────────────────────────────────────────*/
/* The judge, shown beside the location field: context, not a control. */
.context { background: var(--wash); border: 1px solid var(--rule); border-radius: 3px;
  grid-column: 1 / -1; padding: .5rem .65rem; }
.context .label { margin-right: .4rem; }
.context .ctx { color: var(--ink-muted); display: block; font-family: var(--sans);
  font-size: .74rem; margin-top: .15rem; }
details.rare { grid-column: 1 / -1; margin: 0; }

.label, .sub, .empty, .meta {
  color: var(--ink-faint);
  font-family: var(--sans);
  font-size: 0.76rem;
  letter-spacing: 0.02em;
}
.sub { display: block; margin-top: .15rem; }
/* A SECOND LINE THAT KEEPS ITS OWN TYPE. The .sub class is the faint sans
   one; this is for a value that belongs under another and stays as legible as
   it was -- a telephone number beneath the name of the person who answers it.
   NO BACKTICKS IN THIS FILE'S COMMENTS: the stylesheet is a template literal,
   and one ends it. */
.stacked { display: block; margin-top: .1rem; }
/* A MATTER NAMED UNDER ANOTHER TITLE: sans, so the two lines read as a thing
   and the case it belongs to rather than as one heading that wrapped. The
   title above it stays serif, and the matter keeps its own link. */
.under a.case { font-family: var(--sans); font-size: .85rem; font-weight: 500;
  letter-spacing: 0; }
.empty { font-style: normal; }
.muted { color: var(--ink-muted); }

/* THE SAME CALLOUT, IN INK RATHER THAN ACCENT. A voided invoice is a state a
   reader must not miss, and it is not a refusal -- rendering it in the colour
   the refusals use would say something went wrong when nothing did. */
.state {
  border: 1px solid var(--rule-firm);
  border-left-width: 3px;
  border-radius: 2px;
  font-family: var(--sans);
  font-size: .8rem;
  max-width: 68ch;
  padding: .5rem .7rem;
}

.error {
  border: 1px solid var(--accent);
  border-left-width: 3px;
  border-radius: 2px;
  color: var(--accent);
  font-family: var(--sans);
  font-size: .8rem;
  max-width: 68ch;
  padding: .5rem .7rem;
}

/* ── Forms ────────────────────────────────────────────────────────────────*/
form { display: grid; gap: 1rem; max-width: 26rem; }
label { display: grid; gap: .3rem; }
label, legend {
  color: var(--ink-faint);
  font-family: var(--sans);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
input, select, textarea {
  background: var(--paper-raised);
  border: 1px solid var(--rule-strong);
  border-radius: 2px;
  color: var(--ink);
  font-family: var(--sans);
  font-size: .95rem;
  padding: .45rem .55rem;
  width: 100%;
}
input:focus, select:focus, textarea:focus { border-color: var(--ink-muted); outline: none; }
/* A box you tick is never as wide as a box you type in. The full width above
   is for text; on a checkbox it pushes the box away from its own name. */
input[type="checkbox"], input[type="radio"] { width: auto; }
button {
  background: var(--wash);
  border: 1px solid var(--rule-strong);
  border-radius: 2px;
  color: var(--ink);
  cursor: pointer;
  font-family: var(--sans);
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  padding: .5rem 1rem;
  text-transform: uppercase;
}
button:hover { background: var(--ink); border-color: var(--ink); color: var(--paper); }
button.secondary { background: transparent; font-weight: 400; }
/* A HOVER THAT CHANGES ONE HALF OF A COLOUR PAIR MUST CHANGE BOTH.
   button:hover sets an ink ground and paper text; button.secondary then
   took the ground back to transparent and left the text paper-coloured -- so
   an unselected scope button's label turned near-white on a near-white page
   and simply vanished under the pointer. It rendered, it passed everything,
   and it was found by moving a mouse.
   The hover fills lightly rather than solid: the selected button in a
   segmented control is already solid ink, and a hover that matched it would
   read as "this is selected" while the pointer sat on it. */
button.secondary:hover { background: var(--wash); border-color: var(--rule-firm);
  color: var(--ink); }

/* The quick-entry row: name, phone, email, kind, add — one glance, one tab run. */
form.quick { align-items: end; display: flex; flex-wrap: wrap; gap: .6rem; max-width: none;
  margin-bottom: 1.5rem; }
form.quick label { flex: 1 1 9rem; }
/* The address, under the quick row and collapsed: the common case is still
   one row, and the address is one click away rather than a second visit. */
form.quick details.address { flex-basis: 100%; margin: 0; }
form.quick details.address .fields { display: flex; flex-wrap: wrap; gap: .6rem; margin-top: .6rem; }
form.filter { display: flex; flex-wrap: wrap; gap: .6rem; max-width: none; margin-bottom: 1.5rem; }
form.filter input { flex: 1 1 12rem; }
form.filter select { flex: 0 1 12rem; }
/* The shared list filter (list-controls.ts): a visible label around each
   control with its text hidden, so the label is sized rather than the input. */
form.filter label.search { flex: 1 1 14rem; }
form.filter label.pick { flex: 0 1 15rem; min-width: 0; }
form.filter label.search input, form.filter label.pick select { width: 100%; }
form.filter a.clear { align-self: center; color: var(--ink-muted); font-size: .8rem; }
/* Status chips with their counts: one click, and the number before the click. */
nav.chips { display: flex; flex-wrap: wrap; gap: .45rem; margin: 0 0 1rem; }
nav.chips a.chip-link {
  border: 1px solid var(--rule-strong); border-radius: 2px; color: var(--ink);
  font-family: var(--sans); font-size: .72rem; letter-spacing: .06em; padding: .3rem .7rem;
  text-decoration: none; text-transform: uppercase; }
nav.chips a.chip-link .n { color: var(--ink-faint); margin-left: .25rem; }
nav.chips a.chip-link:hover { background: var(--wash); }
nav.chips a.chip-link.on { background: var(--ink); border-color: var(--ink); color: var(--paper); }
nav.chips a.chip-link.on .n { color: var(--paper); opacity: .75; }
/* A heading that sorts its column: the heading's own look, and a link. */
th a.sort { color: inherit; text-decoration: none; }
th a.sort:hover { color: var(--ink); text-decoration: underline; }
th a.sort.on { color: var(--ink); }
/* Mark done beside a status badge in a list, the badge's size. */
td.msg-status form.tick { display: inline-block; margin-left: .4rem; vertical-align: middle; }
td.msg-status form.tick button { font-size: 0.68rem; letter-spacing: 0.08em; line-height: 1.6;
  padding: 0 .4rem; }
/* Past the first page of a long list. */
nav.pager { display: flex; gap: 1.25rem; justify-content: flex-end; margin: 1rem 0 0;
  font-family: var(--sans); font-size: .85rem; }
/* A due date already passed, in the alert color beside its badge. */
td.late { color: var(--alert); }
/* A matter's name above its run of rows, when a list is arranged by matter. */
tr.group th { background: var(--paper-quiet); border-top: 1px solid var(--rule-strong);
  color: var(--ink); font-family: var(--sans); font-size: .82rem; letter-spacing: 0;
  padding-top: .9rem; text-align: left; text-transform: none; }
form.edit { max-width: none; }
form.edit .fields { display: grid; gap: 1rem 1.5rem;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); }
form.edit .fields.one-column { grid-template-columns: minmax(0, 24rem); }
/* A checkbox reads left-to-right, so its label sits beside it, not above it. */
form.edit label.check { align-items: center; display: flex; flex-direction: row; gap: .5rem;
  font-family: var(--sans); font-size: 0.74rem; letter-spacing: 0.08em;
  text-transform: uppercase; }
form.edit label.check input { width: auto; }
/* Somebody not in the address book, typed where the picker is (§4.3). It is a
   cell of the grid rather than a row below it, so it sits beside the field it
   is about; the default disclosure margin would drop it out of line. No
   backtick in this comment: the whole sheet is one template literal. */
form.edit .fields details.newparty { margin: 0; }
form.edit .fields details.newparty label { margin: .6rem 0 0; }
/* A convention the field cannot state itself — end of day, staff-only. */
.hint { color: var(--ink-faint); font-size: 0.82rem; margin: -0.5rem 0 0; }

details { margin: 1rem 0; }
summary {
  color: var(--ink-faint);
  cursor: pointer;
  font-family: var(--sans);
  font-size: 0.74rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
summary:hover { color: var(--ink); }
/* A SUMMARY STANDING IN FOR AN h2, which the small faint one above is not:
   it is a section heading that happens to open, so it takes the heading's
   weight, rule and rhythm and adds an affordance saying it can be opened.
   The default triangle is removed and a plus/minus put in its place, because
   the triangle sits on the text baseline and pulls the heading off the left
   margin every other section is aligned to. */
details.section { margin: 2.75rem 0 0; }
details.section > summary {
  align-items: baseline;
  border-bottom: 1px solid var(--rule);
  color: var(--ink);
  display: flex;
  font-size: 0.74rem;
  font-weight: 600;
  gap: 1rem;
  justify-content: space-between;
  letter-spacing: 0.1em;
  padding-bottom: .4rem;
}
details.section > summary::-webkit-details-marker { display: none; }
details.section > summary::marker { content: ""; }
details.section > summary::after { color: var(--ink-faint); content: "+"; font-weight: 400; }
/* The literal minus character, not a CSS unicode escape. A backslash followed
   by digits is a valid CSS escape and an INVALID TypeScript one, and this
   stylesheet is a template literal -- so the comment saying so cannot contain
   one either, which is how this line came to be worded around it. Unlike the
   backtick, tsc names the problem exactly, so it needs no guard. */
details.section[open] > summary::after { content: "−"; }
details.section > summary:hover { color: var(--accent); }
details.section > summary:hover::after { color: var(--accent); }
details.section .edit { margin-top: 1rem; }
/* A TABLE DIRECTLY UNDER A SUMMARY GETS THE SAME AIR A FORM ALREADY DOES.
   The supervision list butted against the summary's rule, so the section
   heading and the table's own header rule sat two lines apart with nothing
   between them and read as one block of rules. .edit above has had this
   since it was written; a bare table was the case nobody had. */
details.section > table { margin-top: 1rem; }
/* The count in a collapsed section's summary. It sits between the heading and
   the +/- because the summary is a space-between flex row, so it reads as part
   of the heading rather than as a control. Muted until there is something
   waiting -- there is no zero state, because a section with nothing waiting
   renders no tally at all. */
details.section > summary .tally {
  background: var(--alert-wash);
  border-radius: 2px;
  color: var(--alert);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .04em;
  margin-left: auto;
  margin-right: .8rem;
  padding: .1rem .45rem;
  text-transform: none;
}
/* A collapsed section whose summary carries a FACT rather than a task: the
   firm's current timezone, so nobody has to open it to see what it is set to.
   Same slot as .tally, none of its urgency -- an alert-coloured badge on a
   setting that is correct would be a permanent false alarm. */
details.section > summary .tally.quiet {
  background: transparent;
  color: var(--ink-faint);
  font-weight: 400;
  letter-spacing: 0;
  padding: 0;
}
/* ── The read state: what is stored, above the form that would change it ──
   A definition list because these are keys and values, not rows and columns,
   and because it stacks by nature at 375px. */
dl.record { display: grid; gap: .1rem .9rem; grid-template-columns: 9.5rem minmax(0, 1fr);
  margin: .2rem 0 0; }
dl.record dt { color: var(--ink-faint); font-family: var(--sans); font-size: .78rem;
  padding: .3rem 0; }
dl.record dd { margin: 0; padding: .3rem 0; }
/* AN UNSET VALUE IS AN EM-DASH AND IT IS MUTED, so a page of dashes reads as
   absence rather than as content. It is still THERE, which is the point: a row
   that vanished when empty is what made "not recorded" and "not shown"
   indistinguishable. */
@media (max-width: 34rem) {
  dl.record { grid-template-columns: minmax(0, 1fr); }
  dl.record dt { padding-bottom: 0; }
  dl.record dd { padding-top: 0; }
}
/* The edit disclosure sits under its own record, tighter than a page section. */
details.section.edit-block { margin: .9rem 0 0; }
details.section.edit-block > summary { border-bottom: none; font-size: .72rem;
  justify-content: flex-start; gap: .5rem; padding-bottom: 0; }
details.section.edit-block > summary::after { margin-left: 0; }
/* A person in the People section: a name, then their record, then the seat
   action if there is one. Bordered rather than boxed — a list of people is
   read one at a time and a card each would make five people look like five
   sections. */
.person { border-top: 1px solid var(--rule); padding: 1rem 0 .4rem; }
.person:first-of-type { border-top: none; }
/* ── A PERSON IS ONE LINE ──────────────────────────────────────────────────
   A staff row took five lines to say a name and a role — the name, then Role,
   Job title, Office phone and Email as a definition list, then an Edit
   disclosure — so a firm of nine filled the screen with what it already knew.
   The two facts somebody scans a staff list for are who and what they may do,
   and both are in the summary; everything that changes either is one click in.

   NOT details.section, which is a section HEADING that opens: uppercase,
   letterspaced, ruled. A person is a row, so the name keeps the serif it has
   everywhere else and only the affordance is borrowed. */
details.person { margin: 0; padding: 0; }
details.person > summary {
  align-items: baseline;
  color: var(--ink);
  display: flex;
  font-family: var(--serif);
  font-size: .95rem;
  gap: .5rem;
  letter-spacing: 0;
  padding: .45rem 0;
  text-transform: none;
}
details.person > summary::-webkit-details-marker { display: none; }
details.person > summary::marker { content: ""; }
/* Pushed right by margin-left auto, so the +/- lines up down the list
   however long the names are. */
details.person > summary::after {
  color: var(--ink-faint); content: "+"; font-family: var(--sans);
  font-size: .8rem; margin-left: auto; }
details.person[open] > summary::after { content: "−"; }
details.person > summary:hover { color: var(--accent); }
details.person > summary:hover::after { color: var(--accent); }
details.person[open] { padding-bottom: .9rem; }
/* The role beside the name: present on every row, so it is quiet. */
details.person > summary .role {
  color: var(--ink-muted); font-family: var(--sans); font-size: .78rem; }
/* An image standing in for a fact in a collapsed summary — the firm's logo,
   which answers "is one set" without opening the section. */
details.section > summary .tally.quiet img { display: block; }
.person-head { font-family: var(--sans); font-size: .92rem; margin-bottom: .3rem; }
.person-head .sub { color: var(--ink-muted); font-size: .8rem; }
.person dl.record { grid-template-columns: 7rem minmax(0, 1fr); }
/* A profile with no membership: shown, never hidden — it means the control
   plane and the tenant disagree about who exists. */
.warn-inline { color: var(--alert); font-family: var(--sans); font-size: .8rem; }
/*
 * A URL handed to a person to copy. WRAPPED RATHER THAN SCROLLED: an
 * invitation link is one unbroken token and a horizontal scroller would put
 * half of a credential behind a gesture people do not discover (CLAUDE.md §5),
 * which for a value that must be copied whole is not a degraded experience but
 * a broken one.
 */
.handover {
  font-family: var(--mono);
  font-size: .82rem;
  overflow-wrap: anywhere;
  background: var(--wash);
  padding: .5rem .6rem;
  border: 1px solid var(--rule);
}
/* ── Landing on a section rather than the top of the page ─────────────────
   A save posts, the handler redirects to a fragment, and the browser jumps
   there. scroll-margin-top keeps the heading clear of the STICKY masthead,
   which would otherwise cover the thing just landed on — the anchor works and
   the heading is behind the bar, which reads as the anchor being wrong.
   scroll-behavior is deliberately NOT smooth: it needs no script either way,
   and an animated jump after every save is motion nobody asked for. */
h2[id], h3[id], details.section[id] { scroll-margin-top: 84px; }
details.section h3 {
  color: var(--ink-faint);
  font-family: var(--sans);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .08em;
  margin: 1.6rem 0 .4rem;
  text-transform: uppercase;
}

td form { display: inline; margin: 0; }
td button { font-size: .68rem; padding: .15rem .45rem; }

/* In the heading's own row it is one item among the rest, not a line of its
   own above the first thing on the page. */
.meta-row .back { margin: 0; }
.back {
  color: var(--ink-faint);
  display: inline-block;
  font-family: var(--sans);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  margin-bottom: 1.25rem;
  text-decoration: none;
  text-transform: uppercase;
}
.back:hover { color: var(--ink); }

/* ── Calendar ─────────────────────────────────────────────────────────────*/
form.scopes { display: flex; flex-wrap: wrap; gap: 0; margin: 0 0 1.25rem; max-width: none; }
form.scopes button { border-radius: 0; margin-left: -1px; }
form.scopes button:first-of-type { border-radius: 2px 0 0 2px; margin-left: 0; }
form.scopes button:last-of-type { border-radius: 0 2px 2px 0; }
form.scopes button.on { background: var(--ink); border-color: var(--ink); color: var(--paper); }
nav.calnav { display: flex; flex-wrap: wrap; gap: 1.5rem; margin: 0 0 2rem; }
nav.calnav a {
  color: var(--ink-faint);
  font-family: var(--sans);
  font-size: 0.74rem;
  letter-spacing: 0.08em;
  text-decoration: none;
  text-transform: uppercase;
}
nav.calnav a:hover { color: var(--ink); }
/* ── The week: a grid where an hour is an hour ────────────────────────────
   A four-hour trial occupies four hours and a one-hour review hearing
   occupies one, which is the difference between a picture of a day and a list
   of times.

   POSITIONED BY GENERATED CLASSES, NOT INLINE STYLES. 'style-src 'self''
   forbids a style attribute (identity.md §5.2), so the row a block starts on
   and the number of rows it spans are classes — '.s{n}' and '.n{n}' — emitted
   below. The CSP picked the mechanism; it did not cost anything.

   One row is fifteen minutes. An hour is 3.5rem, which leaves a half-hour
   block tall enough for one line and an hour tall enough for three. */
/* THE WEEK IS A FIXED SEVEN COLUMNS AND DOES NOT RESIZE ITSELF AROUND ITS
   CONTENT. A min-width of max-content was doing exactly that: a nowrap line
   inside one block set the max-content width of its column, every column is
   1fr so they all matched it, and a week with one hearing in it was five times
   the width of an empty one. A calendar whose shape depends on what is in it
   is not a calendar you can scan.

   10.5rem x 7 is 73.5rem, which fits the 1260px measure without scrolling on a
   desktop and scrolls honestly below that. */
.calscroll { margin: 0 -1.5rem; overflow-x: auto; padding: 0 1.5rem 1rem; }
.calgrid { display: grid; gap: 0; }
.calgrid.cols-5 { grid-template-columns: repeat(5, minmax(13rem, 1fr)); min-width: 65rem; }
.weekendnote { display: flex; gap: 1.25rem; font-family: var(--sans); font-size: .72rem;
  letter-spacing: .06em; margin: -1rem 0 1.25rem; text-transform: uppercase; }

.daysingle section.daycol { border: 0; padding: 0; }
section.daycol { border-left: 1px solid var(--rule); min-width: 0; padding: 0 .4rem 1rem; }
section.daycol:first-child { border-left: 0; }
section.daycol.today { background: var(--wash); }
section.daycol > h2 {
  border-bottom: 1px solid var(--rule-strong);
  display: flex; justify-content: space-between; align-items: baseline;
  margin: 0 0 .4rem; padding-bottom: .35rem;
}
section.daycol h2 a { color: inherit; font-family: var(--sans); font-size: .74rem;
  font-weight: 600; letter-spacing: .08em; text-decoration: none; }
section.daycol h2 a:hover { color: var(--accent); }
.daydate { color: var(--ink-faint); font-family: var(--sans); font-size: .68rem;
  font-weight: 400; letter-spacing: .04em; }

/* An all-day obligation is the whole day, not an hour in it, so it sits above
   the clock rather than being drawn somewhere inside it. */
.allday { border-bottom: 1px solid var(--rule); margin-bottom: .4rem; padding-bottom: .35rem; }
/* An all-day block is not on the clock, so it is not constrained to a row
   span and its height is simply what it holds. It takes the same padding as a
   timed block so the two read as one family. */
.allday-ent { min-height: 0; }
.overflow { display: block; font-family: var(--sans); font-size: .68rem; letter-spacing: .06em;
  margin-bottom: .3rem; text-transform: uppercase; }

/* The clock: the hour gutter and the entries, sharing one row track so a label
   and the blocks beside it cannot drift apart. */
/* STAGGER, DON'T DIVIDE (week-layout.ts). Every block runs the full width of
   its day. One that starts while another is still running is laid over it and
   stepped in from the left (d1-d3), so the earlier block's top lines stay in
   view; entries that start together share the width (g1of2 ... g3of3). Six
   tracks, because two and three both divide six evenly. */
/* The gutter is as wide as its widest label and no wider: 12 PM at .66rem in
   tabular sans. It was 3.1rem when the label carried its minutes, and the
   three characters that went are the horizontal space the entries gained --
   five times over, in a week (Michael, 2026-09-18).
   NO BACKTICKS IN HERE: this stylesheet is a template literal, and one ends
   it. The file says so twice already. */
.clock { display: grid; grid-template-columns: 2.2rem repeat(6, minmax(0, 1fr)); }
.hourline { border-top: 1px dotted var(--rule); grid-column: 1 / -1; }
/* WHERE NOW IS, on today's column only (Michael, 2026-09-18). In the deep
   navy rather than the alert colour, which is reserved for an overdue date
   and for a refusal -- the time of day is neither. Drawn at page
   load and never moved, because there is no script on any page: it marks the
   moment the grid was drawn, which is why its accessible name carries that
   time. The dot sits in the hour gutter so the line itself starts where the
   entries do. */
.nowline { grid-column: 1 / -1; height: 0; position: relative; z-index: 4; }
.nowline::before { background: var(--accent-deep); content: ""; height: 1px; left: 2.2rem;
  position: absolute; right: 0; top: 0; }
.nowline::after { background: var(--accent-deep); border-radius: 50%; content: ""; height: .42rem;
  left: 1.99rem; position: absolute; top: -.19rem; width: .42rem; }
.hourmark { color: var(--ink-faint); font-family: var(--sans); font-size: .66rem;
  font-variant-numeric: tabular-nums; grid-column: 1; padding-top: .1rem; white-space: nowrap; }
.g1of1 { grid-column: 2 / 8; }
.g1of2 { grid-column: 2 / 5; }
.g2of2 { grid-column: 5 / 8; }
.g1of3 { grid-column: 2 / 4; }
.g2of3 { grid-column: 4 / 6; }
.g3of3 { grid-column: 6 / 8; }
/* Later entries paint over earlier ones in page order; a covered one comes to
   the front while it is pointed at or tabbed to. The paper-coloured ring is
   what keeps a laid-over edge readable against the block beneath it. */
.clock .ent { position: relative; z-index: 1; }
.clock .ent:hover, .clock .ent:focus-visible { z-index: 5; }
.clock .ent.d1 { margin-left: 1.45rem; box-shadow: 0 0 0 1px var(--paper); }
.clock .ent.d2 { margin-left: 2.7rem; box-shadow: 0 0 0 1px var(--paper); }
.clock .ent.d3 { margin-left: 3.95rem; box-shadow: 0 0 0 1px var(--paper); }

/* min-width: 0 is load-bearing. A grid item defaults to min-width: auto, which
   means it refuses to shrink below its own content — so the nowrap lines inside
   pushed their track wider instead of ellipsising, which is the other half of
   why a week with entries changed shape. */
/* MIN-HEIGHT: 0, AND IT IS THE SAME BUG AS MIN-WIDTH WAS.
   A grid item's automatic minimum size is its CONTENT, on both axes, so
   overflow:hidden cannot clip it — the box grows instead and its text
   spills past the border it was supposed to be inside. min-width:0 was
   added here when the week changed shape once populated; the height half was
   left, and it showed the moment an entry was short enough for its title not
   to fit: a 30-minute hearing is two slots tall, and the second line of
   a long title was drawn below the card.
   A short entry now clips, which is the intended trade: the week draws
   DURATIONS as durations, and the day view carries the whole card. */
/* SPACING IS DERIVED, NOT TYPED. Every number below comes from BLOCK, which
   calendar.ts reads to decide how many lines fit -- so the box cannot be
   padded without the arithmetic knowing. */
.ent { background: var(--paper-raised); border: 1px solid var(--rule); border-radius: 4px;
  margin: 0.1rem 0.2rem 0.15rem 0.2rem;
  min-height: 0; min-width: 0; overflow: hidden;
  padding: 0.4rem 0.55rem 0.35rem; }
/* THE WEEK'S BLOCK IS AN ANCHOR, so it needs the reset an anchor wants and the
   grid placement it already had. Colour is inherited because the block's ink is
   decided by the wash behind it, not by link colour. */
a.ent { color: inherit; display: block; text-decoration: none; }
.ent:hover { border-color: var(--rule-firm); }
.ent.done { opacity: .5; }
.ent.done .enthead { text-decoration: line-through; }
/* The title is BOLD, as v1's is. It was not, and a block of uniform weight is
   what made the week read as thinner than the card it replaces. */
/* One line, ending in an ellipsis rather than cut through a word. A clipped
   word reads as broken; an ellipsis reads as "there is more here". */
.enthead { font-size: .8rem; font-weight: 600; letter-spacing: -0.01em;
  line-height: 1.1rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The gap under the title exists only when something follows it. A title-only
   block -- a 30-minute hearing -- would otherwise spend a tenth of a rem it
   does not have on the space beneath a line that is not drawn, and clip
   itself. That was the overflow bug once already. */
.enthead:not(:last-child) { margin-bottom: 0.1rem; }
.enthead .at { font-family: var(--mono); font-size: .7rem; font-variant-numeric: tabular-nums;
  font-weight: 400; margin-right: .3rem; }
/* The .ent .zone rule went with the zone labels themselves (CLAUDE.md §5). */
.ent .line { color: var(--ink-muted); font-size: .7rem;
  line-height: 0.95rem; margin-top: 0.1rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ent .line .k { color: var(--ink-faint); font-family: var(--sans); font-size: .6rem;
  letter-spacing: .06em; margin-right: .25rem; text-transform: uppercase; }

/* ── The day: room for the whole card ─────────────────────────────────────
   Time and title on one line, then labelled rows — the case, the client, the
   judge, where you go, and who has it.

   LABELS RATHER THAN ICONS. v1 puts a small glyph beside each row; this design
   carries structure in small uppercase sans and no ornament at all, so the
   label does the same work with nothing added to the page. */
ul.daylist { list-style: none; margin: 0; padding: 0; }
ul.daylist > li { border: 1px solid var(--rule); border-radius: 3px; display: grid;
  gap: 1.25rem; grid-template-columns: 7rem minmax(0, 1fr); margin-bottom: .5rem;
  padding: .8rem .9rem; }
ul.daylist > li:hover { border-color: var(--rule-strong); }
ul.daylist > li.done { opacity: .55; }
/* THE CHAIN A HEARING MOVED THROUGH, oldest first. Dates only -- the titles
   are the same hearing, and repeating one three times says nothing three
   times. The entry being read is not a link to itself. */
ol.chain { list-style: none; margin: 0; padding: 0; }
ol.chain > li { color: var(--ink-muted); padding: .15rem 0; }
/* The literal arrow, not a CSS escape: a backslash followed by digits is a
   valid CSS escape and an INVALID TypeScript one, and this stylesheet is a
   template literal -- the same trap the minus in details.section records. */
ol.chain > li + li::before { color: var(--rule-firm); content: "↓"; display: block;
  font-size: .8rem; line-height: 1; }
ol.chain > li.here { color: var(--ink); font-weight: 600; }
/* A portal date that came off. Struck rather than removed: the client took the
   day off for it, and a page that simply drops it is the telephone call. */
.when-block.off .d, .when-block.off strong { text-decoration: line-through; }
.when-block.off { opacity: .7; }
.off-note { font-weight: 600; }
ul.daylist > li.done .cardtitle { text-decoration: line-through; }
.cardwhen { font-family: var(--mono); font-size: .8rem; font-variant-numeric: tabular-nums;
  padding-top: .1rem; }
.cardwhen .span { color: var(--ink-faint); display: block; font-family: var(--sans);
  font-size: .68rem; letter-spacing: .04em; }
.cardtitle { font-size: 1.05rem; font-weight: 600; letter-spacing: -0.01em; margin-bottom: .35rem; }
/* The day card's title is the link into the entry. The card cannot be one --
   it already contains anchors to the matter and the contacts. */
.cardtitle a { color: inherit; text-decoration: none; }
.cardtitle a:hover { text-decoration: underline; text-underline-offset: 2px; }
.cardrows { display: grid; gap: .15rem 1rem; grid-template-columns: max-content minmax(0, 1fr); }
.cardrows dt { color: var(--ink-faint); font-family: var(--sans); font-size: .66rem;
  letter-spacing: .08em; padding-top: .15rem; text-transform: uppercase; }
.cardrows dd { font-size: .9rem; margin: 0; }

ul.plain { list-style: none; margin: 0; padding: 0; }
ul.plain li { border-bottom: 1px solid var(--rule); padding: .6rem 0; }

/* ── The attention strip: summary before detail ───────────────────────────
   Nobody opens a matter list to read it. They open it to find out what needs
   them, so that answer comes before the table rather than being buried in a
   column of it — and each cell is also the filter that acts on it, so the
   answer is the way in.

   Hairlines and space, no cards and no shadows: a row of boxes here would be
   the dashboard convention this design is deliberately not. */
.attention { border-bottom: 1px solid var(--rule); border-top: 1px solid var(--rule);
  display: flex; flex-wrap: wrap; margin: 0 0 1.25rem; }
.attention a { border-left: 1px solid var(--rule); flex: 0 0 auto; min-width: 9.5rem;
  padding: .8rem 1.4rem .85rem; text-decoration: none; }
.attention a:first-child { border-left: 0; padding-left: 0; }
.attention a:hover { background: var(--paper-quiet); }
.attention .n { color: var(--ink); display: block; font-family: var(--serif); font-size: 1.6rem;
  font-variant-numeric: tabular-nums; font-weight: 500; line-height: 1.1; }
.attention .l { color: var(--ink-faint); display: block; font-size: .66rem; font-weight: 700;
  letter-spacing: .1em; margin-top: .25rem; text-transform: uppercase; }
/* Red here means overdue and only overdue, the same as everywhere else. */
.attention a.urgent .n, .attention a.urgent .l { color: var(--alert); }

/* ── Segmented filter: the status column, turned into a control ───────────
   Status was a column in which every visible value read "Open" — a column
   carrying no information. As a filter it carries counts instead, which is
   what somebody actually wanted to know. */
.seg { display: flex; margin-bottom: .75rem; }
.seg a { background: var(--paper-raised); border: 1px solid var(--rule-strong);
  color: var(--ink-muted); font-size: .8rem; font-weight: 600; margin-left: -1px;
  padding: .4rem .8rem; text-decoration: none; }
.seg a:first-child { border-radius: 2px 0 0 2px; margin-left: 0; }
.seg a:last-child { border-radius: 0 2px 2px 0; }
.seg a:hover { background: var(--paper-quiet); color: var(--ink); }
.seg a.on { background: var(--ink); border-color: var(--ink); color: var(--paper-raised);
  position: relative; z-index: 1; }
.seg a .c { font-family: var(--mono); font-size: .72rem; margin-left: .45rem; opacity: .65; }

/* ── A matter in a list ───────────────────────────────────────────────────
   The name in serif because it is a proper noun; the number beneath it in
   mono because it is an identifier. The number used to be a column of its own
   and was so narrow that "2026-0100" broke across two lines. */
a.case { color: var(--ink); display: block; font-family: var(--serif); font-size: 1rem;
  font-weight: 500; letter-spacing: -0.005em; text-decoration: none; }
a.case:hover { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
.docket { color: var(--ink-faint); display: block; font-family: var(--mono); font-size: .72rem;
  margin-top: .15rem; }
/* The same identifier where there is only one line to put it on. */
.docket.inline { display: inline; margin-left: .4rem; margin-top: 0; }
.party .more { color: var(--ink-faint); font-size: .75rem; margin-left: .25rem; }

/* When the next thing is, and WHAT KIND of thing it is. A date alone does not
   tell you whether to clear a morning. */
.when { font-variant-numeric: tabular-nums; font-weight: 600; }
.when .kind { color: var(--ink-muted); display: block; font-size: .76rem; font-weight: 400;
  margin-top: .1rem; }
.when.none { color: var(--ink-faint); font-weight: 400; }
/* STATE IN FORM AS WELL AS COLOUR. The stripe survives greyscale printing and
   colour vision deficiency; for a system whose purpose is that deadlines are
   not missed, that is not a nicety.

   THE GUTTER IS WHAT THE STRIPE LIVES IN. An inset shadow is drawn inside the
   cell, so with no left padding it lands ON the first characters — the matter
   number was rendering with its leading digit under the stripe. Every first
   cell reserves the space, whether or not it is striped, so a row does not
   shift sideways when a deadline passes. */
table td:first-child, table th:first-child { padding-left: .65rem; }
tr.overdue td:first-child { box-shadow: inset 3px 0 0 var(--alert); }
tr.overdue .when, tr.overdue .when .kind { color: var(--alert); }

/* ── A matter's own page ──────────────────────────────────────────────────
   The caption block: the name, then the facts that identify it on one line,
   the way a pleading caption does. */
.caption { border-bottom: 1px solid var(--rule-strong); margin-bottom: 0; padding-bottom: 1.1rem; }
.caption h1 { margin-bottom: .5rem; max-width: 30ch; text-wrap: balance; }
.facts { align-items: center; color: var(--ink-muted); display: flex; flex-wrap: wrap;
  font-size: .85rem; gap: .6rem; }
.facts .sep { color: var(--rule-firm); }
.facts .num { font-family: var(--mono); font-size: .8rem; }

/* The single most useful thing on the page, so it is the first thing on it. */
.nextup { background: var(--paper-quiet); border-left: 3px solid var(--rule-firm);
  margin: 1.5rem 0 0; padding: .8rem 1rem; }
.nextup.late { background: var(--alert-wash); border-left-color: var(--alert); }
.nextup .l { color: var(--ink-faint); font-size: .66rem; font-weight: 700; letter-spacing: .11em;
  text-transform: uppercase; }
.nextup.late .l { color: var(--alert); }
.nextup .t { font-family: var(--serif); font-size: 1.1rem; font-weight: 500; margin-top: .2rem; }
.nextup .m { color: var(--ink-muted); font-size: .82rem; margin-top: .15rem; }
/* THREE FACTS IN ONE BANNER: the soonest thing, the next hearing, and how
   much is past its date. A row each, because they answer different questions
   and a run-on line makes you parse which half is which.

   ONLY THE OVERDUE ROW TAKES THE ALERT COLOUR when the banner as a whole is
   not late. An alert wash behind a hearing three weeks out is the boy who
   cried wolf, and the whole value of this treatment is that it means overdue
   and nothing else. */
.nextup-line + .nextup-line { border-top: 1px solid var(--rule); margin-top: .55rem;
  padding-top: .55rem; }
.nextup-line .l { display: block; }
.nextup-line .t { display: block; }
.nextup-line .m { display: block; }
.nextup-line.late .l { color: var(--alert); }

/* -- Calendar search --------------------------------------------------------
   A GET form, so a search is a URL. It sits above the grid and above its own
   results, because the second thing you do with a search is narrow it. */
form.find { align-items: center; display: flex; gap: .5rem; margin-bottom: 1.1rem; }

/* The calendar's one action row: search on the left, the way to add an entry
   on the right. Aligned to the start so the disclosure does not stretch to
   the search box's height, and the search form's own bottom margin is dropped
   because the row carries the spacing now. */
.calbar { align-items: start; display: flex; flex-wrap: wrap; gap: 1rem;
  justify-content: space-between; margin-bottom: 1.1rem; }
.calbar form.find { flex: 1 1 22rem; margin-bottom: 0; }

/* The disclosure itself. Closed it is a control the width of its words; open
   it is an ordinary edit form, so it takes the full row and pushes the search
   box up rather than sitting in a narrow column beside it. */
.newentry { flex: 0 0 auto; }
.newentry > summary { background: var(--paper-raised); border: 1px solid var(--rule-strong);
  border-radius: 2px; color: var(--ink); cursor: pointer; font-size: .8rem;
  font-weight: 600; list-style: none; padding: .45rem .9rem; user-select: none; }
.newentry > summary::-webkit-details-marker { display: none; }
.newentry > summary::before { content: '+'; font-weight: 700; margin-right: .45rem; }
.newentry > summary:hover { background: var(--paper-quiet); }
.newentry[open] { flex: 1 1 100%; }
.newentry[open] > summary { background: var(--ink); border-color: var(--ink);
  color: var(--paper-raised); margin-bottom: .9rem; }
.newentry[open] > summary::before { content: '\2212'; }
.newentry .fields { display: grid; gap: .75rem 1rem;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); }
.newentry .error { margin: 0 0 .75rem; }
form.find label { flex: 1 1 auto; margin: 0; max-width: 30rem; }
form.find input { margin: 0; width: 100%; }
form.find button { flex: 0 0 auto; }
form.find .clear { color: var(--ink-faint); font-family: var(--sans); font-size: .74rem;
  letter-spacing: .06em; text-decoration: none; text-transform: uppercase; }
form.find .clear:hover { color: var(--ink); }
/* Visible to a screen reader, not to the eye. The box is self-evident by
   position and placeholder; a label above it would be a line of furniture on
   every calendar page. A placeholder is NOT a label, so the label exists. */
.sr { clip: rect(0 0 0 0); clip-path: inset(50%); height: 1px; overflow: hidden;
  position: absolute; white-space: nowrap; width: 1px; }

/* -- One entry: the parties -------------------------------------------------
   Two or three columns, because which side somebody is on is the fact you need
   before walking into a room, and a single list makes you read every role to
   work it out. */
.sides { display: grid; gap: 1.4rem; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); }
.side h3 { color: var(--ink-faint); font-family: var(--sans); font-size: .68rem; font-weight: 700;
  letter-spacing: .11em; margin: 0 0 .5rem; text-transform: uppercase; }
ul.parties { list-style: none; margin: 0; padding: 0; }
ul.parties li { border-bottom: 1px solid var(--rule); padding: .4rem 0; }
ul.parties li:last-child { border-bottom: 0; }
ul.parties .role { color: var(--ink-faint); font-family: var(--sans); font-size: .68rem;
  letter-spacing: .06em; margin-left: .4rem; text-transform: uppercase; }
/* The court's word for the person, beside the role label: quieter than the
   name, not shouting in capitals like the role, so the two read as two facts. */
ul.parties .designation { color: var(--ink-muted); font-family: var(--sans); font-size: .76rem;
  margin-left: .45rem; }
/* Whom they act for, on its own line under the name. It wraps rather than
   truncating: an attorney for four children is four names. */
ul.parties .for { color: var(--ink-muted); display: block; font-family: var(--sans);
  font-size: .8rem; margin-top: .2rem; }
ul.parties .for a { color: inherit; }
/* An attorney's own details, quieter than whom they act for: the firm reads
   as text, the bar number as the reference it is (Michael, 2026-09-18). */
ul.parties .practice { color: var(--ink-faint); font-family: var(--sans); font-size: .76rem;
  margin-top: .15rem; }
ul.parties .practice a { color: inherit; }
ul.parties .practice .bar { font-family: var(--mono); font-size: .72rem; }
/* The edit row: role, designation and Save on one line, the counsel ticks
   under them when opened. */
form.participant { align-items: center; display: flex; flex-wrap: wrap; gap: .4rem; }
form.participant input[name="designation"] { max-width: 15rem; }
/* On the add form as well as the edit row: one field list, one look. */
form.participant details.counsel, form.quick details.counsel { flex-basis: 100%; }
details.counsel > summary { color: var(--ink-muted); cursor: pointer;
  font-family: var(--sans); font-size: .78rem; }
/* ONE PERSON PER LINE, THE BOX BESIDE THE NAME. These inherited two things
   meant for a field label and a text box: the label's small grey capitals,
   which made people's names read as disabled options, and the input's full
   width, which pushed each box far from its name. Laid out inline they
   wrapped into a ragged grid with names broken across lines. */
details.counsel label { align-items: center; color: var(--ink); display: flex;
  font-size: .9rem; gap: .5rem; letter-spacing: 0; margin: .35rem 0 0; text-transform: none; }
details.counsel input[type="checkbox"] { flex: 0 0 auto; margin: 0; width: auto; }

/* A table cell naming a kind or a channel: secondary to the title beside it. */
td.kind { color: var(--ink-muted); font-family: var(--sans); font-size: .74rem;
  letter-spacing: .06em; text-transform: uppercase; white-space: nowrap; }
/* A muted standalone sentence: what a section would say if it had nothing to
   show, or what a search did and did not look at. Distinct from .hint, which
   is pulled up under a field it belongs to. */
.note { color: var(--ink-muted); font-size: .84rem; max-width: 68ch; }
tr.done { opacity: .55; }
tr.done a.case { text-decoration: line-through; }

/* -- Destructive actions ----------------------------------------------------
   THE ONE RED THAT IS NOT OVERDUE. Deliberate, and narrow: a link at the foot
   of a record, and the button on its confirmation page. Red anywhere else in
   this design means a deadline has passed, so this is the only borrowing and
   it stays small enough not to dilute it. */
/* An invoice's total line: the number a client checks first, so it carries a
   rule above it and the weight the lines do not. */
tr.total td { border-top: 1px solid var(--rule-firm); font-weight: 600;
  padding-top: .7rem; }
p.danger { border-top: 1px solid var(--rule); margin-top: 2.5rem; padding-top: 1.1rem; }
p.danger a { color: var(--alert); font-family: var(--sans); font-size: .78rem;
  letter-spacing: .04em; text-decoration: none; }
p.danger a:hover { text-decoration: underline; text-underline-offset: 3px; }
form.danger { align-items: center; display: flex; gap: 1.2rem; margin-top: 1.6rem; }
form.danger button { background: var(--alert); border-color: var(--alert); color: #fff; }
form.danger button:hover { background: #8f1e17; border-color: #8f1e17; }
form.danger .back { margin: 0; }
/* A remove/delete link inside a table row: present, not shouting. The row is
   already dense, so this is the muted sans of a label until it is hovered. */
a.remove { color: var(--ink-faint); font-family: var(--sans); font-size: .7rem;
  letter-spacing: .06em; text-decoration: none; text-transform: uppercase; }
a.remove:hover { color: var(--alert); }
/* The matter delete warning's inventory: the counts are the content, so they
   are set large enough to be read rather than skimmed past. */
ul.inventory { list-style: none; margin: .8rem 0 1.4rem; padding: 0; }
ul.inventory li { border-bottom: 1px solid var(--rule); font-family: var(--serif);
  font-size: 1.05rem; padding: .45rem 0; }
ul.inventory li:last-child { border-bottom: 0; }
ul.inventory strong { font-variant-numeric: tabular-nums; }
/* A figure in the rail: one number somebody reads at a glance, and a label
   under it. The alarm variant is the only place a balance turns red, because a
   trust account below zero is a condition rather than a validation message. */
.figure { margin: 0; }
.figure .n { display: block; font-family: var(--serif); font-size: 2rem; font-weight: 600;
  font-variant-numeric: tabular-nums; letter-spacing: -0.02em; line-height: 1.1; }
.figure .l { color: var(--ink-faint); font-family: var(--sans); font-size: .68rem;
  letter-spacing: .11em; text-transform: uppercase; }
.figure.alarm .n { color: var(--alert); }
td.right, th.right { text-align: right; }
/* AN ARRANGEMENT WRITTEN AS A SENTENCE, with the controls as its words:
   [ who ] supports [ whom ] [ Add ]. The supervision section is the only user,
   and the point is that no control carries a label -- the sentence is the
   label, and naming the two halves as nouns is exactly the wording it
   replaced. Baseline-aligned rather than centred, so the verb sits on the
   text baseline of the selects instead of floating between them. */
.sentence {
  align-items: baseline;
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  /* BOTH OF THESE UNDO the form rule, and both are load-bearing. Without the flex
     the controls stack; without releasing the 26rem the flex row wraps inside
     a column narrower than the sentence, which stacks them again and looks
     identical. The first version set only the display and rendered as four
     stacked rows. */
  margin: 1rem 0 0;
  max-width: none;
}
/* THE CONTROLS SIZE TO THEIR CONTENT. the select rule is width: 100% globally,
   which is right in a column of stacked fields and is exactly what turns a
   flex row into a stack: a 100%-wide item leaves no room for the next one. */
.sentence select { margin: 0; width: auto; }
/* The verb is not a control, so it must not sit on a control's line height. */
.sentence .verb { white-space: nowrap; }
/* The act is not part of the sentence, so it gets the gap doubled -- close
   enough to belong to this form, far enough not to read as its last word. */
.sentence button { margin-left: .5rem; }
/* The verb, and the same word inside a row of the table below the form. Quiet,
   because the names are the content and the verb is the grammar holding them
   together. */
.verb { color: var(--ink-muted); }

/* An inline control inside a dense table cell, and now in a page's facts line
   too: a select and its button on one line, sized down so a row of them does
   not become the page. The task header uses it for the billable toggle --
   one boolean, flipped where it is read, rather than inside an edit form. */
form.inline { align-items: center; display: flex; gap: .3rem; margin: 0; max-width: none; }
/* A ROW OF ONE-PRESS CONTROLS, SIDE BY SIDE WITH AIR BETWEEN THEM. Each of
   these is its own form -- one press, one column -- and a form is block level,
   so Mark done and Mark billable stacked flush against each other with their
   borders touching (Michael, 2026-09-21). The same .55rem the meta row uses,
   so the two pages that carry such a row agree. An open Time block takes a
   line of its own: it is a form with fields, not another button. */
.controls { align-items: center; display: flex; flex-wrap: wrap; gap: .55rem; }
.controls > details[open] { flex-basis: 100%; }
form.inline select { font-size: .78rem; margin: 0; padding: .15rem .3rem; }
form.inline button { font-size: .68rem; padding: .2rem .5rem; }
/* What was said, quoted back on a delete confirmation. A communication's body
   never appears in a URL, a log or an audit row, so this is the only place it
   can be read before it goes. */
blockquote.said { border-left: 3px solid var(--rule-firm); color: var(--ink-muted);
  font-family: var(--serif); margin: 1.4rem 0; max-width: 68ch; padding: .2rem 0 .2rem 1.1rem;
  white-space: pre-wrap; }
.s1{grid-row-start:1}.s2{grid-row-start:2}.s3{grid-row-start:3}.s4{grid-row-start:4}.s5{grid-row-start:5}.s6{grid-row-start:6}.s7{grid-row-start:7}.s8{grid-row-start:8}.s9{grid-row-start:9}.s10{grid-row-start:10}.s11{grid-row-start:11}.s12{grid-row-start:12}.s13{grid-row-start:13}.s14{grid-row-start:14}.s15{grid-row-start:15}.s16{grid-row-start:16}.s17{grid-row-start:17}.s18{grid-row-start:18}.s19{grid-row-start:19}.s20{grid-row-start:20}.s21{grid-row-start:21}.s22{grid-row-start:22}.s23{grid-row-start:23}.s24{grid-row-start:24}.s25{grid-row-start:25}.s26{grid-row-start:26}.s27{grid-row-start:27}.s28{grid-row-start:28}.s29{grid-row-start:29}.s30{grid-row-start:30}.s31{grid-row-start:31}.s32{grid-row-start:32}.s33{grid-row-start:33}.s34{grid-row-start:34}.s35{grid-row-start:35}.s36{grid-row-start:36}.s37{grid-row-start:37}.s38{grid-row-start:38}.s39{grid-row-start:39}.s40{grid-row-start:40}.s41{grid-row-start:41}.s42{grid-row-start:42}.s43{grid-row-start:43}.s44{grid-row-start:44}.s45{grid-row-start:45}.s46{grid-row-start:46}.s47{grid-row-start:47}.s48{grid-row-start:48}.s49{grid-row-start:49}.s50{grid-row-start:50}.s51{grid-row-start:51}.s52{grid-row-start:52}.s53{grid-row-start:53}.s54{grid-row-start:54}.s55{grid-row-start:55}.s56{grid-row-start:56}.s57{grid-row-start:57}.s58{grid-row-start:58}.s59{grid-row-start:59}.s60{grid-row-start:60}.s61{grid-row-start:61}.s62{grid-row-start:62}.s63{grid-row-start:63}.s64{grid-row-start:64}.s65{grid-row-start:65}.s66{grid-row-start:66}.s67{grid-row-start:67}.s68{grid-row-start:68}.s69{grid-row-start:69}.s70{grid-row-start:70}.s71{grid-row-start:71}.s72{grid-row-start:72}.s73{grid-row-start:73}.s74{grid-row-start:74}.s75{grid-row-start:75}.s76{grid-row-start:76}.s77{grid-row-start:77}.s78{grid-row-start:78}.s79{grid-row-start:79}.s80{grid-row-start:80}.s81{grid-row-start:81}.s82{grid-row-start:82}.s83{grid-row-start:83}.s84{grid-row-start:84}.s85{grid-row-start:85}.s86{grid-row-start:86}.s87{grid-row-start:87}.s88{grid-row-start:88}.s89{grid-row-start:89}.s90{grid-row-start:90}.s91{grid-row-start:91}.s92{grid-row-start:92}.s93{grid-row-start:93}.s94{grid-row-start:94}.s95{grid-row-start:95}.s96{grid-row-start:96}.n1{grid-row-end:span 1}.n2{grid-row-end:span 2}.n3{grid-row-end:span 3}.n4{grid-row-end:span 4}.n5{grid-row-end:span 5}.n6{grid-row-end:span 6}.n7{grid-row-end:span 7}.n8{grid-row-end:span 8}.n9{grid-row-end:span 9}.n10{grid-row-end:span 10}.n11{grid-row-end:span 11}.n12{grid-row-end:span 12}.n13{grid-row-end:span 13}.n14{grid-row-end:span 14}.n15{grid-row-end:span 15}.n16{grid-row-end:span 16}.n17{grid-row-end:span 17}.n18{grid-row-end:span 18}.n19{grid-row-end:span 19}.n20{grid-row-end:span 20}.n21{grid-row-end:span 21}.n22{grid-row-end:span 22}.n23{grid-row-end:span 23}.n24{grid-row-end:span 24}.n25{grid-row-end:span 25}.n26{grid-row-end:span 26}.n27{grid-row-end:span 27}.n28{grid-row-end:span 28}.n29{grid-row-end:span 29}.n30{grid-row-end:span 30}.n31{grid-row-end:span 31}.n32{grid-row-end:span 32}.n33{grid-row-end:span 33}.n34{grid-row-end:span 34}.n35{grid-row-end:span 35}.n36{grid-row-end:span 36}.n37{grid-row-end:span 37}.n38{grid-row-end:span 38}.n39{grid-row-end:span 39}.n40{grid-row-end:span 40}.n41{grid-row-end:span 41}.n42{grid-row-end:span 42}.n43{grid-row-end:span 43}.n44{grid-row-end:span 44}.n45{grid-row-end:span 45}.n46{grid-row-end:span 46}.n47{grid-row-end:span 47}.n48{grid-row-end:span 48}.n49{grid-row-end:span 49}.n50{grid-row-end:span 50}.n51{grid-row-end:span 51}.n52{grid-row-end:span 52}.n53{grid-row-end:span 53}.n54{grid-row-end:span 54}.n55{grid-row-end:span 55}.n56{grid-row-end:span 56}.n57{grid-row-end:span 57}.n58{grid-row-end:span 58}.n59{grid-row-end:span 59}.n60{grid-row-end:span 60}.n61{grid-row-end:span 61}.n62{grid-row-end:span 62}.n63{grid-row-end:span 63}.n64{grid-row-end:span 64}.n65{grid-row-end:span 65}.n66{grid-row-end:span 66}.n67{grid-row-end:span 67}.n68{grid-row-end:span 68}.n69{grid-row-end:span 69}.n70{grid-row-end:span 70}.n71{grid-row-end:span 71}.n72{grid-row-end:span 72}.n73{grid-row-end:span 73}.n74{grid-row-end:span 74}.n75{grid-row-end:span 75}.n76{grid-row-end:span 76}.n77{grid-row-end:span 77}.n78{grid-row-end:span 78}.n79{grid-row-end:span 79}.n80{grid-row-end:span 80}.n81{grid-row-end:span 81}.n82{grid-row-end:span 82}.n83{grid-row-end:span 83}.n84{grid-row-end:span 84}.n85{grid-row-end:span 85}.n86{grid-row-end:span 86}.n87{grid-row-end:span 87}.n88{grid-row-end:span 88}.n89{grid-row-end:span 89}.n90{grid-row-end:span 90}.n91{grid-row-end:span 91}.n92{grid-row-end:span 92}.n93{grid-row-end:span 93}.n94{grid-row-end:span 94}.n95{grid-row-end:span 95}.n96{grid-row-end:span 96}.clock{grid-auto-rows:1.125rem}.mm0{transform:translateY(0.000rem)}.mm1{transform:translateY(0.075rem)}.mm2{transform:translateY(0.150rem)}.mm3{transform:translateY(0.225rem)}.mm4{transform:translateY(0.300rem)}.mm5{transform:translateY(0.375rem)}.mm6{transform:translateY(0.450rem)}.mm7{transform:translateY(0.525rem)}.mm8{transform:translateY(0.600rem)}.mm9{transform:translateY(0.675rem)}.mm10{transform:translateY(0.750rem)}.mm11{transform:translateY(0.825rem)}.mm12{transform:translateY(0.900rem)}.mm13{transform:translateY(0.975rem)}.mm14{transform:translateY(1.050rem)}.who0{background:#EBEBEB}.who1{background:#EEF4FB}.who2{background:#E6EEEF}.who3{background:#F2F0ED}.who4{background:#EBEFEB}.who5{background:#EFEBF0}.who6{background:#F5F0F0}