/* ==========================================================================
   EXTRAFRESHBINS OFFICE — the application.

   Owner, 2026-08-09, four times of asking: "everything still looks like crap
   amateur", "LOOKS PATHETIC AMATUER". The previous three attempts moved
   spacing, split a card into four tasks and put a sidebar on it, and every one
   of them left the same three things in place. Those three things are what
   "amateur" was pointing at, and this file changes all of them:

   1. IT WAS A PAGE, IN A COLUMN, ON A DESK MONITOR. `#office` was capped at
      78rem and sat inside the body padding app.css writes for a phone, so a
      27-inch screen showed a 1250px strip of cards floating in slate. An
      application does not have a page width. It has a rail, a header, and a
      content pane that scrolls under them.

   2. IT WORE THE MARKETING TYPE SCALE. `--step-0` is `clamp(1rem … 1.1rem)`
      and `--step-1` reaches 1.46rem — a scale built for a headline read from a
      sofa. At those sizes a table row is 44px tall and eleven of them fill a
      viewport. The palette is the owner's ruling and is untouched; the SIZES
      are not the ruling, and a desk tool needs its own, which is what
      `--o-t-*` below is: a fixed 11/13/15/20/32 ramp, no clamping, tuned for a
      mouse at 60cm rather than a thumb at arm's length.

   3. EVERY SURFACE WAS THE SAME SURFACE. The palette gives the dark side two
      values, `--slate-deep` and `--slate`, and app.css derives one more. Three
      greys cannot express ground / rail / card / inset / well, so everything
      was the same card with the same 1px border and nothing had any weight.
      The `--o-*` ramp below is SIX steps on the brand's own slate-to-ink axis
      — no new hue, nothing borrowed from outside the token file — and that is
      what lets a card sit ON something instead of beside it.

   WHAT IS NOT NEGOTIABLE: the two faces, and the rule that a number on this
   screen comes from a row somebody can open. (The hues were on this list
   until 2026-09-27; see THE PALETTE MOVED below.)

   THE FIELD APP IS DELIBERATELY UNTOUCHED. Its 56px targets are correct for a
   phone in a glove in July sun. This is a desk with a mouse.

   ---------------------------------------------------------------------------
   MATTE BLACK, NOT GLASS. Owner, 2026-08-09: "keep it minimalistic but
   futuristic pro like Elon Musk's X app for admin experience/office dash.
   glass looks cheap/half assed" — and then, on seeing it: "too glossy".

   The first attempt ported the marketing site's frosted header onto every
   surface: blurred translucent panels, a five-layer ring, a 102 degree
   specular streak, and an ambient radial field behind them for the blur to
   refract. That is the right treatment for a MARKETING header, which is one
   floating element over a photograph. Applied to thirty surfaces at once it is
   thirty competing highlights, and the verdict was correct — a diagonal shine
   on a data panel is decoration pretending to be depth.

   What replaced it is the opposite discipline, and it is X's:

     · TRUE BLACK, #000000. Not a dark grey pretending to be black. On an OLED
       panel the ground is genuinely off, so a hairline reads as a hairline.
     · NO ELEVATION AT ALL. No shadow, no blur, no gradient, no inner
       highlight. A panel is the ground plus a one-pixel border. Hierarchy
       comes from type weight, spacing and position — the things that survive
       being screenshotted, printed, or looked at for eight hours.
     · ONE ACCENT, USED WHEN IT MEANS SOMETHING. The brand sage marks the
       current section and a good state; the warn amber marks the one number
       that is costing money. Everywhere else is a three-step grey.
     · HAIRLINES DO THE DIVIDING. #2F3336 at one pixel, and a 1px grid gap
       over a line-coloured ground where a run of cells needs separating.

   THE PALETTE MOVED, 2026-09-27 (owner: "the outdated purple theme"). The
   office follows tkati.com's current monochrome system through tokens.css:
   the public #070707 canvas rather than true black, the public hairlines
   composited on it, and an accent that is white. Colour is reserved for
   state — green healthy, amber needs attention, red failed or overdue. The
   discipline above stands; the values quoted in it (#000000, #2F3336, the
   sage, the #C4703A warn) are its history, not its current palette.

   THE SIGN-IN SCREEN IS UNTOUCHED, by owner instruction. Everything here is
   scoped to the three signed-in roots, and app.css's #signin block is not read
   by this file.
   ========================================================================== */

/* ==========================================================================
   1. THE PALETTE AND SCALE

   Owner, 2026-08-09: "make the entire /admin logged-in experience dark black
   glass". THREE ROOTS, NOT ONE — and finding that out is what stopped this
   being a regression. `owner.html` and `portal.html` both `<link>` this
   stylesheet and build themselves out of its classes: .ofc-head, .ofc-lede,
   .ofc-tally, .ofc-card--wide and .ofc-list are all theirs as much as they
   were ever the office's. Rewriting this file around `#office` alone deleted
   nine classes those two pages depend on and scoped the new buttons where
   neither could reach them. Both pages would have rendered with an unstyled
   header, no lede band and 56px field-app buttons.

   So the scope is the five signed-in roots — #ebay joined them when the eBay
   console was built, #ads when TKATI Ads was.

   AND EVERY LIST HAS TO CARRY EVERY ROOT. #ads was added to the block below
   and to nothing else, so all twenty `:is(...)` lists in this file excluded
   it: every .ofc-primary, .ofc-ghost, .ofc-quiet and .ofc-link on those
   screens fell back to app.css's 56px purple field-app slab, and the
   prefers-reduced-motion block did not reach them at all — an accessibility
   failure nothing would have reported. tools/verify.mjs now fails the build
   if any list here is missing a root that loads this stylesheet.
   `#signin` is deliberately NOT
   among them — the owner asked for the sign-in screen to stay exactly as it
   is, and it is styled entirely by app.css, which this file never touches.
   ========================================================================== */

#office, #portal, #ebay, #ads {
  /* ---- surfaces ----
     tkati.com's canvas, not true black: #070707 is the public site's ground,
     and the office is the same company one click on. An "inset" is the
     public L1 level and a hover is the public hover step, both white. The
     cool blue-grey greys that were here (#0B0D0E, rgba(231,233,234,…)) are
     gone with the purple: the public ramp is true neutral. */
  --o-bg:     #070707;
  --o-panel:  #070707;   /* a panel is the ground plus a border */
  --o-inset:  #101010;   /* a well, a chip, a nested block: L1 on the canvas */
  --o-raised: #131313;   /* a popover or a menu, above the page */
  --o-hover:  rgba(255, 255, 255, 0.05);
  --o-press:  rgba(255, 255, 255, 0.08);

  /* ---- lines. Solid hex, not alpha: over one ground an alpha line is a
     different colour on every surface it crosses, and the whole point of a
     hairline system is that one weight looks like one weight. So each is the
     public hairline COMPOSITED on #070707 — the same pixel tkati.com draws,
     written down once. ---- */
  --o-line:    #161616;   /* hair-1 (.06): a divider inside a panel */
  --o-line-2:  #202020;   /* hair-2 (.10): the edge of a panel */
  /* THE ONLY LINE ALLOWED TO BE A CONTROL'S SOLE BOUNDARY (WCAG 1.4.11):
     3.22:1 on the canvas, 3.05:1 against a field's own L1 fill. A field drawn
     in --o-line-2 measured 1.65:1 before and 1.23:1 now, which is a box
     nobody can find. */
  --o-line-3:  #606060;   /* hair-3 (.36) */
  --o-line-4:  #8F8F8F;   /* hair-4 (.55): a control's edge on hover — 6.26:1 */

  /* ---- ink. Four steps of the public neutral ramp, measured on the canvas:
     16.14:1, 7.99:1, 5.91:1, and --o-neutral 4.96:1 for marks. The faint step
     stays at 5.06:1 even on L3, which is why it is #8B8B8B and not the public
     floor. ---- */
  --o-text:    #E6E6E6;
  --o-dim:     #A3A3A3;
  --o-faint:   #8B8B8B;

  /* ---- the accent, which is white ----
     tkati.com took the hue out of its accent, so the office does too: the
     focus ring, the current section's icon and a link are WHITE, and the one
     filled control is white with a black label. Nothing here is a colour any
     more; a state that needs one reads the three below. The accent WASH is
     composed from the channel triple so it follows the token, never typed as
     an rgba() the next palette cannot reach. */
  --o-accent:      var(--aqua);         /* #FFFFFF — focus, active, link */
  --o-accent-deep: var(--teal-soft);    /* #FFFFFF — the filled control */
  --o-accent-wash: rgb(var(--aqua-rgb) / 0.08);
  --o-on-accent:   var(--on-accent);    /* #0A0A0A — its label, 19.80:1 */

  /* ---- state. Reserved meanings, never a series colour and never
     decoration. INK for text, MARK for a dot, edge or bar, BG for a ground
     (each wash composited on the canvas, solid for the reason the lines are).
     The word is always printed too: the three hues sit 1.41-1.60:1 apart. */
  --o-ok:       var(--ok);        /* #63B78C — healthy, live, done: 8.33:1 */
  --o-ok-mark:  var(--ok-mark);   /* #2E8B63 — 4.79:1 */
  --o-ok-bg:    #0C1914;          /* ok ink on it 7.42:1 */
  --o-warn:     var(--attn-mark); /* #C9973F — needs attention, a MARK: 7.66:1 */
  --o-warn-ink: var(--attn);      /* #E8C07A — the same, for TEXT: 11.75:1 */
  --o-warn-bg:  #1E1A13;          /* warn ink on it 10.16:1 */
  --o-bad:      var(--bad);       /* #F2837A — failed, overdue, destructive: 7.96:1 */
  --o-bad-mark: var(--bad-mark);  /* #E5534B — 5.44:1 */
  --o-bad-bg:   #22100F;          /* bad ink on it 7.23:1 */
  --o-neutral:  var(--inactive);  /* #7E7E7E — not applicable, cancelled, unknown */

  /* ---- type. Fixed, not clamped: see the header. ---- */
  --o-t-xs:  0.6875rem;   /* 11px — micro labels, chips, badges */
  --o-t-sm:  0.8125rem;   /* 13px — tables, lists, form text */
  --o-t-md:  0.875rem;    /* 14px — body, card titles */
  --o-t-lg:  1rem;        /* 16px — section headings */
  --o-t-xl:  1.25rem;     /* 20px — the view title */
  --o-t-num: 2rem;        /* 32px — a stat value */
  --o-t-hero: 3.25rem;    /* 52px — the one hero figure per view */

  /* ---- geometry. 16px panels and fully-round controls: the two radii that
     read as an application rather than as a web page. ---- */
  --o-r:     12px;
  --o-r-sm:  8px;
  --o-r-lg:  16px;
  --o-pill:  999px;
  --o-rail-w: 15.5rem;
  --o-pad:   1.25rem;

  /* ---- THE OLD NAMES, POINTED AT THE NEW VALUES ----
     owner.css and portal.css are written against `--app-*` and `--step-*`, and
     they are not wrong — they were written against the palette that existed.
     Re-pointing the names inside these three roots brings both files onto the
     new ground and the new density without editing either, and without any
     risk of the two drifting: there is one definition, here.

     The alternative was find-and-replace across 236 lines of two stylesheets,
     which is how a colour gets missed and one card keeps the old slate. */
  --app-bg:      var(--o-bg);
  --app-surface: var(--o-panel);
  --app-raised:  var(--o-inset);
  --app-text:    var(--o-text);
  --app-mute:    var(--o-dim);
  --app-line:    var(--o-line);
  --app-accent:  var(--o-accent);

  /* The marketing type scale is built for a headline read from a sofa. Same
     reasoning as --o-t-* above, applied to the two pages that ask for the
     steps by their old names. */
  --step--1: 0.8125rem;
  --step-0:  0.875rem;
  --step-1:  1rem;
  --step-2:  1.25rem;
  --step-3:  2rem;
  --step-4:  3.25rem;
}

/* PHASE VI — THE OPERATING SYSTEM PASS (owner brief, 2026-09-30).
   The admin roots only: the client portal keeps its tuning. Same discipline —
   matte, monochrome, colour only for state — with three corrections the
   brief names and the screenshot wall confirmed:
   · THE TYPE WAS ZOOMED OUT. 13px tables and 11px labels read as a developer
     tool squinting to fit. One step up everywhere: body 14, rows 13.5, meta
     11.5, titles 22. Density comes from layout, not from small type.
   · ONE BLACK CANNOT HOLD A HIERARCHY. Chrome (the rail) sits a shade darker
     than the work, so the work comes forward without a single bright card;
     surfaces step up in four small increments.
   · SOFT 12–16px RADII EVERYWHERE READ AS A TEMPLATE. Precision radii: 6 for
     controls, 8 for surfaces, 12 for the rare floating panel. */
#office, #portal, #ebay, #ads {
  --o-chrome: #050506;
  --o-bg:     #08090A;
  --o-panel:  #08090A;
  --o-inset:  #0F1012;
  --o-raised: #15161A;
  --o-hover:  rgba(255, 255, 255, 0.045);
  --o-press:  rgba(255, 255, 255, 0.075);
  --o-line:   #16171A;
  --o-line-2: #212327;
  --o-line-3: #3B3E43;
  --o-text:   #ECECEC;
  --o-dim:    #B4B5B8;
  --o-faint:  #8E9095;
  --o-t-xs:  0.71875rem;  /* 11.5px — meta, labels, badges */
  --o-t-sm:  0.84375rem;  /* 13.5px — rows, lists, form text */
  --o-t-md:  0.875rem;    /* 14px — body */
  --o-t-lg:  1rem;        /* 16px — section headings */
  --o-t-xl:  1.375rem;    /* 22px — the page title */
  --o-r:     8px;
  --o-r-sm:  6px;
  --o-r-lg:  12px;
  --o-rail-w: 14.5rem;    /* 232px */
  --o-row:   44px;        /* a list row */
  --o-ease:  cubic-bezier(.2, .7, .2, 1);
}

/* BUTTONS ARE PRECISE, NOT CAPSULES (Phase VI): the admin's controls take the
   6px control radius; the portal keeps its own. */
/* `html` lifts this over §3's capsule rule, which comes later at the same weight. */
html :is(#office, #portal, #ebay, #ads) :is(.ofc-primary, .ofc-ghost, .ofc-quiet, .ofc-link) { border-radius: var(--o-r-sm); }

/* ---- THE PAGE'S PRIMARY ACTION, AND DRAWERS (Phase VI) -------------------
   A view's one create action lives in the location bar, shown only on its
   view. Creation opens a drawer from the right: the page stays visible behind
   it (dimmed), so nobody loses their place to fill in a name. */
.ofc-pageact { display: none !important; }
#office[data-view="clients"] .ofc-pageact[data-for~="clients"],
#office[data-view="jobs"] .ofc-pageact[data-for~="jobs"],
#office[data-view="mail"] .ofc-pageact[data-for~="mail"],
#ads .ofc-pageact[data-for~="ads"] { display: inline-flex !important; }
.ofc-pageact::before { content: "+"; margin-right: 0.4rem; font-weight: 500; font-size: 1.05em; }

dialog.ofc-drawer {
  position: fixed;
  inset: 0 0 0 auto;
  width: min(30rem, 100vw);
  height: 100dvh;
  max-height: none;
  max-width: none;
  margin: 0;
  padding: 0;
  border: 0;
  border-left: 1px solid var(--o-line-2);
  background: var(--o-raised, #15161A);
  color: var(--o-text);
  box-shadow: -24px 0 60px -30px rgba(0, 0, 0, 0.9);
  overflow: hidden;
}
dialog.ofc-drawer[open] { display: grid; grid-template-rows: auto minmax(0, 1fr); animation: ofc-drawer-in .22s var(--o-ease, ease) both; }
dialog.ofc-drawer::backdrop { background: rgba(0, 0, 0, 0.55); }
@keyframes ofc-drawer-in { from { transform: translateX(24px); opacity: 0; } to { transform: none; opacity: 1; } }
.ofc-drawer__head {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  min-height: 64px; padding: 0 0.9rem 0 1.4rem;
  border-bottom: 1px solid var(--o-line-2);
}
.ofc-drawer__head h2 { margin: 0; font: 650 var(--o-t-lg)/1.2 var(--display); letter-spacing: -0.012em; }
:is(#office, #portal, #ebay, #ads) .ofc-drawer__x { min-width: 34px; padding: 0; justify-content: center; font-size: 1.25rem; }
.ofc-drawer__body { overflow-y: auto; padding: 1.25rem 1.4rem 2rem; }
.ofc-drawer__body .ofc-fields { grid-template-columns: minmax(0, 1fr) !important; }
@media (prefers-reduced-motion: reduce) { dialog.ofc-drawer[open] { animation: none; } }

/* ---- Clients (Phase VI) ----------------------------------------------------
   The search sits on the tab row, right-aligned, so the view bar is one line:
   tabs on the left, search on the right. The row actions never wrap. */
#ofc-clients-card { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: end; column-gap: 1.5rem; }
#ofc-clients-card > * { grid-column: 1 / -1; }
#ofc-clients-card > .ofc-card__bar { grid-column: 2; grid-row: 1; margin: 0 0 1rem; align-self: end; }
#ofc-clients-card > .ofc-addr { grid-column: 1; grid-row: 1; }
#ofc-clients-card > .ofc-card__bar .ofc-filter { min-width: 16rem; }
#ofc-clients-table td a, #ofc-clients-table td .ofc-linkish { white-space: nowrap; }
#ofc-clients-table td:last-child { white-space: nowrap; }
#ofc-clients-table td:last-child > * { display: inline-flex; vertical-align: middle; margin-right: 0.5rem; }
.ofc-opt { font-style: normal; font-weight: 450; color: var(--o-faint); margin-left: 0.25rem; }
/* A ZERO IS NOT NEWS. A summary figure that reads 0 steps back (office-quiet.mjs
   marks it), so a real number is the one the eye lands on. */
.ofc-stat.is-zero .ofc-stat__n { color: var(--o-faint); }
.ofc-stat.is-zero .ofc-stat__s { color: #6E7076; }

/* ---- Projects (Phase VI) ---------------------------------------------------
   Tabs over the table; the site under a project's name is a quiet line, not a
   second link competing with the name; a healthy row says so with a dash. */
#ofc-jb-tabs { margin-bottom: 0.25rem; }
#ofc-jb-table td > .ofc-none { margin: 0; color: var(--o-line-3); }
#ofc-jb-table td small { display: block; margin-top: 0.15rem; font-size: var(--o-t-xs); color: var(--o-faint); font-weight: 450; }
#ofc-jb-table td small .ofc-linkish { color: var(--o-faint); text-decoration: none; border: 0; }
#ofc-jb-table td small .ofc-linkish:hover { color: var(--o-text); text-decoration: underline; }
#ofc-jb-table td:first-child { font-weight: 600; color: var(--o-text); }
#ofc-jb-table td:nth-child(5) { font-variant-numeric: tabular-nums; white-space: nowrap; }
#ofc-jb-table .ofc-jb-act .ofc-quiet { min-height: 0; padding: 0 0.3rem; line-height: inherit; }

/* ---- Requests (Phase VI) ---------------------------------------------------
   Tabs over the list. A row is the request, whose it is, and one quiet meta
   line; the due day and "Write to them" are words that act, not boxed fields;
   the state select is the one control at the row's end. */
#ofc-req-tabs { margin-bottom: 0.25rem; }
#office .ofc-req { padding: 0.85rem 0.25rem; align-items: center; }
#office .ofc-req:hover { background: var(--o-hover); }
#office .ofc-req__m { gap: 0.25rem 0.9rem; align-items: center; margin-top: 0.15rem; }
#office .ofc-req__link { text-decoration: none; }
#office .ofc-req__link:hover { text-decoration: underline; }
#office .ofc-req__write,
.ofc-req__duebtn { min-height: 24px; padding: 0; border: 0; background: none; border-radius: 0;
  color: var(--o-dim); font: 500 var(--o-t-xs)/24px var(--body, inherit); cursor: pointer; }
#office .ofc-req__write:hover, .ofc-req__duebtn:hover { color: var(--o-text); text-decoration: underline; text-underline-offset: 3px; }
.ofc-req__duebtn:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.ofc-req__duebtn.is-empty { color: var(--o-faint); }
.ofc-req__due.is-late .ofc-req__duebtn { color: var(--o-warn-ink); }
.ofc-req__due.is-late .ofc-req__duebtn::before { content: '● '; color: var(--o-warn); }
#office .ofc-req > .ofc-input--sm { min-width: 8.5rem; background-color: transparent; border-color: var(--o-line-2); }

/* ---- Money (Phase VI) -------------------------------------------------------
   States are upright words with a mark, not italics; a $0 steps back; the row's
   way out to Stripe is a quiet link (.ofc-rowlink), not a capsule per row. */
#office .ofc-lines td.is-overdue, #office .ofc-lines td.is-unpriced { font-style: normal; }
#office .ofc-lines td.is-overdue::before { content: '● '; font-size: 0.7em; vertical-align: 0.15em; }
#office .ofc-lines td.num.is-zero { color: var(--o-line-3); }
#ofc-inv-tabs { margin-bottom: 0.25rem; }
.ofc-rowlink { display: inline-flex; align-items: center; min-height: 24px; color: var(--o-dim);
  font-size: var(--o-t-sm); font-weight: 500; text-decoration: none; white-space: nowrap; }
.ofc-rowlink::after { content: '\2197'; margin-left: 0.3rem; font-size: 0.85em; color: var(--o-faint); }
.ofc-rowlink:hover { color: var(--o-text); text-decoration: underline; text-underline-offset: 3px; }
.ofc-rowlink:focus-visible { outline: 2px solid #fff; outline-offset: 2px; border-radius: 2px; }
#ofc-inv-table td:last-child, #ofc-plans-table td:last-child { text-align: right; }
.ofc-foot { margin: 1rem 0 0; font-size: var(--o-t-xs); color: var(--o-faint); max-width: 70ch; }
.ofc-foot a { color: var(--o-dim); }

/* ---- Mailbox (Phase VI) -----------------------------------------------------
   One bar: address tabs left; filter, unread, mark-all and help right. Compose
   docks bottom-right over the page, non-modal — the list and the message you
   are answering stay readable and usable beside it. */
.ofc-pageact[data-for="mail"]::before { content: none; }
.ofc-mail__tools .ofc-filter { width: 15rem; min-height: 32px; }
#office .ofc-mail__tools .ofc-ghost, #office .ofc-mail__tools .ofc-quiet { min-height: 32px; }
#office .ofc-addr__n.is-unread { color: var(--o-text); font-weight: 650; }
.ofc-split__item .ofc-mail__to { font-weight: 500; }
#office .ofc-mail__day { padding: 0.75rem 0.6rem 0.3rem; background: var(--o-bg); }
#office #ofc-mail-count { border: 0; padding: 0; margin-top: 0.75rem; font-size: var(--o-t-xs); color: var(--o-faint); }
#office .ofc-compose {
  position: fixed; right: 1.5rem; bottom: 1.5rem; z-index: 60;
  width: min(34rem, calc(100vw - 2rem)); max-height: calc(100vh - 3rem); overflow-y: auto;
  margin: 0; padding: 0 1.1rem 1.1rem;
  background: var(--o-raised); border: 1px solid var(--o-line-2); border-radius: var(--o-r-lg);
  box-shadow: 0 24px 64px rgba(0, 0, 0, 0.6);
}
#office .ofc-compose__head { position: sticky; top: 0; align-items: center; min-height: 48px; margin: 0 -1.1rem; padding: 0 0.6rem 0 1.1rem;
  background: var(--o-raised); border-bottom: 1px solid var(--o-line-2); }
#office .ofc-compose input, #office .ofc-compose textarea { background: var(--o-bg); border-color: var(--o-line-2); }
@media (max-width: 40rem) { #office .ofc-compose { right: 0.5rem; left: 0.5rem; bottom: 0.5rem; width: auto; } }

/* ---- Settings (Phase VI) ----------------------------------------------------
   Facts as a key/value list on hairlines, not three boxed pills; the limits
   behind a summary. */
#office .ofc-kv { gap: 0; border-top: 1px solid var(--o-line); }
#office .ofc-kv li { display: grid; grid-template-columns: minmax(9rem, 14rem) minmax(0, 1fr); gap: 1rem; padding: 0.7rem 0;
  border: 0; border-bottom: 1px solid var(--o-line); border-radius: 0; background: none; font-weight: 500; }
#office .ofc-kv li > span { color: var(--o-faint); }
#office .ofc-kv li > b { font-weight: 550; color: var(--o-text); overflow-wrap: anywhere; }
#ofc-settings-card .ofc-actions { margin-top: 1rem; }
#ofc-sample-wrap { margin-top: 1.75rem; }
#office .ofc-limits, #ebay .eby-limits { border: 0; padding: 0; }
#office .ofc-limits > summary { list-style: none; cursor: pointer; display: flex; align-items: center; gap: 0.5rem; }
#office .ofc-limits > summary::-webkit-details-marker { display: none; }
#office .ofc-limits > summary h2 { margin: 0; font-size: var(--o-t-md); color: var(--o-dim); font-weight: 600; }
#office .ofc-limits > summary::after { content: '+'; color: var(--o-faint); }
#office .ofc-limits[open] > summary::after { content: '\2212'; }
#office .ofc-limits > summary:hover h2 { color: var(--o-text); }
#office .ofc-limits[open] > .ofc-cannot { margin-top: 1rem; }
@media (max-width: 40rem) { #office .ofc-kv li { grid-template-columns: minmax(0, 1fr); gap: 0.15rem; } }

/* ---- the palette, with records (Phase VI) ---------------------------------- */
.ofc-palette ul { max-height: min(60vh, 28rem); overflow-y: auto; }
.ofc-palette li { display: flex; align-items: baseline; gap: 0.5rem; }
.ofc-palette li > span { flex: none; max-width: 60%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ofc-palette li > em { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ofc-palette__kind { flex: none; min-width: 4.6rem; font-size: var(--o-t-xs); font-weight: 600; letter-spacing: 0.04em; color: var(--o-faint); }
.ofc-palette__note { margin: 0.5rem 0.25rem 0; font-size: var(--o-t-xs); color: var(--o-faint); }
.ofc-palette__note:empty { display: none; }
.ofc-palette li.ofc-palette__none { color: var(--o-faint); cursor: default; }

/* ---- lists by keyboard (Phase VI; office-keys.mjs) -------------------------
   The row with focus carries the Office's white 2px ring; ? opens the keys the
   list answers, in the Mailbox help's own shape. */
#office :is(#ofc-clients-body, #ofc-jb-body, #ofc-inv-body, #ofc-plans-body) tr:focus { outline: none; }
#office :is(#ofc-clients-body, #ofc-jb-body, #ofc-inv-body, #ofc-plans-body) tr:focus-visible,
#office .ofc-req:focus-visible { outline: 2px solid #fff; outline-offset: -2px; background: var(--o-hover); }
#office .ofc-req:focus { outline: none; }
#office .ofc-req:focus-visible { outline: 2px solid #fff; }
.ofc-keys { margin: 0 0 1rem; padding: 0.8rem 1rem; border: 1px solid var(--o-line-2); border-radius: var(--o-r); background: var(--o-inset); }
.ofc-keys dl { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: 0.4rem 1.5rem; margin: 0; font-size: var(--o-t-sm); }
.ofc-keys dl > div { display: flex; align-items: baseline; gap: 0.6rem; }
.ofc-keys dt { flex: 0 0 auto; display: flex; gap: 0.25rem; }
.ofc-keys dd { margin: 0; color: var(--o-dim); }
.ofc-keys kbd { min-width: 1.5rem; padding: 0.1rem 0.35rem; border: 1px solid var(--o-line); border-bottom-width: 2px; border-radius: 5px;
  background: var(--o-bg); font: 650 0.72rem/1.5 var(--mono, ui-monospace, monospace); color: var(--o-text); text-align: center; }

/* A CLEAR HERO IS NOT A HEADLINE (Phase VI). "0 overdue" in 64px green was the
   biggest thing on a healthy page. Clear keeps its green dot — the state is
   still said — and the figure drops to the size of its neighbours. */
#office .ofc-stat--hero.is-clear .ofc-stat__n { font-size: 1.625rem; color: var(--o-faint); }
.ofc-stat--hero.is-clear { flex: 0 1 auto; }

section[data-view="crm"] > .ofc-grid.is-bare > :is(#ofc-lead-card, #ofc-work-lead-card, #ofc-speed-card) { display: none; }
section[data-view="crm"] > .ofc-grid.is-bare .ofc-mode { display: none; }
section[data-view="crm"] > .ofc-grid.is-bare #ofc-crm-q { display: none; }
@media (min-width: 62.01rem) { section[data-view="crm"] > .ofc-grid.is-bare { grid-template-columns: minmax(0, 1fr); } }
/* ---- Enquiries: list + inspector (Phase VI) --------------------------------
   The list on the left; on the right the inspector: the open enquiry, the
   form aimed at it, and speed-to-lead as a small insight. Below 62rem the
   grid's own rule stacks them (list, then inspector). */
@media (min-width: 62.01rem) {
  section[data-view="crm"] > .ofc-grid { grid-template-columns: minmax(0, 1fr) minmax(20rem, 26rem); grid-template-rows: auto auto auto 1fr; column-gap: 2rem; row-gap: 1rem; }
  #ofc-crm-card { grid-column: 1; grid-row: 1 / span 4; }
  #ofc-lead-card { position: sticky; top: 0; }
  #ofc-lead-card, #ofc-work-lead-card, #ofc-speed-card { grid-column: 2; }
  #ofc-lead-card { grid-row: 1; }
  #ofc-work-lead-card { grid-row: 2; }
  #ofc-speed-card { grid-row: 3; }
}
#ofc-lead-card, #ofc-work-lead-card, #ofc-speed-card {
  padding: 1rem 1.1rem;
  border: 1px solid var(--o-line-2);
  border-radius: var(--o-r);
  background: var(--o-inset);
}
#ofc-lead-card > .ofc-sub, #ofc-speed-card > .ofc-sub, #ofc-work-lead-card > .ofc-sub { display: none; }
/* The page header already names the view; the list needs no second title.
   Its view bar is one line: the List/Pipeline switch, then search at the right. */
#ofc-crm-card { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; column-gap: 1rem; }
#ofc-crm-card > * { grid-column: 1 / -1; }
#ofc-crm-card > .ofc-card__bar { grid-column: 2; grid-row: 1; margin: 0 0 0.85rem; justify-content: flex-end; }
#ofc-crm-card > .ofc-card__bar > div { display: none; }
#ofc-crm-card > .ofc-mode { grid-column: 1; grid-row: 1; }
#ofc-crm-card > .ofc-card__bar .ofc-filter { min-width: 16rem; }
/* The inspector names the enquiry itself; "The enquiry" above it said nothing.
   Focus lands on the panel when a row opens it; the row's selection is the
   indicator, so the panel draws no second ring. */
#ofc-lead-card > h2 { display: none; }
#ofc-lead-detail:focus { outline: none; }
#ofc-work-lead-card .ofc-fields { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) !important; }
#ofc-speed-card .ofc-stats { margin: 0; padding: 0; border: 0; gap: 1.5rem; }
#ofc-speed-card .ofc-stat__n { font-size: 1.25rem; }

/* The mode switch: a two-way segmented control, quiet. */
.ofc-mode { display: inline-flex; gap: 2px; padding: 2px; margin: 0 0 0.85rem; border: 1px solid var(--o-line-2); border-radius: var(--o-r); background: var(--o-inset); }
:is(#office, #portal, #ebay, #ads) .ofc-mode__b {
  min-height: 28px; padding: 0 0.75rem; border: 0; border-radius: var(--o-r-sm);
  background: transparent; box-shadow: none; color: var(--o-faint);
  font: 550 var(--o-t-xs)/1 var(--body); cursor: pointer;
}
:is(#office, #portal, #ebay, #ads) .ofc-mode__b[aria-pressed="true"] { background: var(--o-press); color: #FFFFFF; }

/* LIST MODE: stages become group headings, cards become rows. */
.ofc-board[data-mode="list"] { display: block !important; overflow: visible !important; }
.ofc-board[data-mode="list"] .ofc-board__col {
  display: block; width: auto; min-width: 0; max-height: none; padding: 0; margin: 0 0 0.25rem;
  border: 0; background: none; overflow: visible;
}
.ofc-board[data-mode="list"] .ofc-board__h {
  display: flex; align-items: baseline; gap: 0.5rem; margin: 0.9rem 0 0; padding: 0.4rem 0;
  border-bottom: 1px solid var(--o-line-2);
  font: 600 var(--o-t-xs)/1.4 var(--body); letter-spacing: 0.06em; text-transform: uppercase; color: var(--o-faint);
}
.ofc-board[data-mode="list"] .ofc-board__col:first-child .ofc-board__h { margin-top: 0; }
.ofc-board[data-mode="list"] .ofc-board__col.is-hot .ofc-board__h { color: var(--o-warn-ink); }
.ofc-board[data-mode="list"] .ofc-board__h span + span { color: var(--o-faint); font-weight: 500; }
.ofc-board[data-mode="list"] .ofc-board__sum { margin: 0.35rem 0 0; font-size: var(--o-t-xs); }
.ofc-board[data-mode="list"] .ofc-board__empty { margin: 0.4rem 0 0; color: #6E7076; font-size: var(--o-t-xs); }
.ofc-board[data-mode="list"] .ofc-board__card {
  display: grid;
  grid-template-columns: minmax(9rem, 1.1fr) minmax(0, 2fr) auto auto;
  align-items: center;
  column-gap: 1rem;
  min-height: var(--o-row, 44px);
  margin: 0;
  padding: 0.35rem 0.6rem;
  border: 0;
  border-bottom: 1px solid var(--o-line);
  border-radius: 0;
  background: none;
}
.ofc-board[data-mode="list"] .ofc-board__card:hover { background: var(--o-hover); }
.ofc-board[data-mode="list"] .ofc-board__card.is-on { background: var(--o-press); box-shadow: inset 2px 0 0 var(--o-accent); }
.ofc-board[data-mode="list"] .ofc-board__card > strong { grid-column: 1; grid-row: 1; font-size: var(--o-t-sm); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ofc-board[data-mode="list"] .ofc-board__card > em { grid-column: 4; grid-row: 1; justify-self: end; font-style: normal; font-size: var(--o-t-xs); color: var(--o-faint); white-space: nowrap; }
.ofc-board[data-mode="list"] .ofc-board__what { grid-column: 2; grid-row: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--o-dim); font-size: var(--o-t-sm); }
.ofc-board[data-mode="list"] .ofc-board__attn { grid-column: 3; grid-row: 1; justify-self: end; white-space: nowrap; }
.ofc-board[data-mode="list"] .ofc-board__reach,
.ofc-board[data-mode="list"] .ofc-chip--unchecked { display: none; }
.ofc-board[data-mode="list"] :is(.ofc-board__money, .ofc-board__why, .ofc-board__revisit) { grid-column: 2 / -1; font-size: var(--o-t-xs); color: var(--o-faint); }

/* Table links read as names, not as underlined hyperlinks (Phase VI): the
   row is the affordance, the underline arrives on hover. */
:is(#office, #portal, #ebay, #ads) :is(.ofc-lines, .ofc-table, table) a:not(.ofc-primary):not(.ofc-ghost) {
  text-decoration: none;
}
:is(#office, #portal, #ebay, #ads) :is(.ofc-lines, .ofc-table, table) a:not(.ofc-primary):not(.ofc-ghost):hover {
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* ==========================================================================
   2. THE FRAME

   Full viewport, three panes, and the CONTENT is the only thing that scrolls.
   A dashboard whose rail scrolls away is a dashboard where navigation is a
   scroll-to-top away, which is a menu bar with extra steps.
   ========================================================================== */

/* app.css writes phone padding onto <body> and it is wrong here. Same :has()
   mechanism the sign-in split already uses, so the field app is unaffected. */
body:has(#office), body:has(#portal), body:has(#ebay), body:has(#ads) {
  padding: 0;
  /* tkati.com's canvas, the same #070707 the shell roots paint, so nothing
     at the edge of a short screen shows a second black. It was #000000, a
     true black the public site does not use. */
  background: var(--paper);
  /* THE BASE IS NOT SET HERE, AND IT LOOKED AS THOUGH IT WERE.
     `font-size: var(--o-t-md)` sat on this rule from `2b5dcd8` (2026-08-09)
     until 2026-08-15 and was dead every one of those six days — the date came
     from `git log -S`, not from an impression. The `--o-*` tokens are declared
     on the shell roots, and custom properties inherit DOWNWARDS, so
     the parent of those roots cannot see a token its own child defines. The
     declaration resolved to nothing and was dropped, exactly as `--o-t-s` was
     in `.ofc-board__money`.
     Everything else in this rule worked, which is what made it invisible —
     `line-height: 1.45` computed against the browser's 16px and the background
     painted, so the rule plainly matched and only the one declaration died.
     The base now lives on the roots below, where the token is in scope.

     AND REMOVING IT MOVED `body` ITSELF, which is worth stating because it is
     not obvious and was measured rather than predicted: 16px → 17.6px. A
     `var()` naming an undefined property is invalid at computed-value time,
     and for an inherited property that means the declaration computes to
     `inherit` — it does NOT fall through to the next rule in the cascade. So
     the dead line was still MASKING `app.css`'s `body { font-size:
     var(--step-0) }` and pinning body to the browser default. With it gone,
     app.css applies, which is what every other page in this app already gets.
     Nothing visible rides on it: these three pages carry only their root
     `<main>`, a `<noscript>` and two `<script>`s, nothing is ever appended to
     `body`, and `rem` is relative to `html` regardless — so the whole
     observable difference is the size of "This app needs JavaScript." on a
     page with JavaScript turned off. */
  line-height: 1.45;
}

#office, #portal, #ebay, #ads {
  /* 14px, not --step-0's clamp. Every size in this file is relative to it —
     true here, and not true where this line used to be. Moving it is a no-op
     on today's markup and was measured rather than assumed: 447 visible text
     elements across the six office views, and the static chrome of the owner
     and portal pages, all resolve their own size from the rem ramp, so 0 of
     them changed. It matters for the NEXT element that does not set a size —
     which is what `.ofc-board__money` turned out to be. `app-tokens --check`
     now fails any `html`/`body` rule that references an `--o-*` token, so this
     particular hole cannot be dug again. */
  font-size: var(--o-t-md);
}

/* #ads IS THE SAME SHELL. ads.html is a sibling of office.html — same rail,
   same grid, same views — and duplicating these rules under a second id is how
   two screens of one product start drifting apart a pixel at a time. The id
   list above is the extension point; this joins it. */
#office, #ads, #ebay {
  display: grid;
  grid-template-columns: var(--o-rail-w) minmax(0, 1fr);
  /* dvh, so the rail is not cut off by a mobile browser's own chrome. */
  height: 100dvh;
  max-width: none;
  margin: 0;
  padding: 0;
  color: var(--o-text);
  overflow: hidden;
}

/* THE OTHER THREE ARE PAGES. They have no rail and no section router — the
   owner screen is one long read, the portal is one customer's project, and the
   eBay console is one shop — so they get a measure and the document scrolls,
   which is what they had before body.padding went to zero for the office's
   sake.

   ADDING A ROOT MEANS ADDING IT TO EVERY LIST IN THIS FILE, and that is the
   trap #ebay walked into first: the `--o-*` and `--app-*` tokens are declared
   on these roots and custom properties inherit DOWNWARDS, so a new root that
   is not on the list resolves every var() to nothing. The page renders — at
   the browser's own font size, on the browser's own white — which reads as a
   missing stylesheet rather than as a missing selector. */
#portal {
  max-width: 78rem;
  margin: 0 auto;
  padding: 1.25rem clamp(1rem, 3vw, 2rem) 4rem;
  color: var(--o-text);
}

/* The content pane. `minmax(0, 1fr)` above and `min-width: 0` here are both
   load-bearing: without them a wide table inside a grid child refuses to
   shrink and pushes the whole layout sideways. */
.ofc-main {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  min-width: 0;
  overflow: hidden;
}

.ofc-scroll {
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 1.15rem clamp(1rem, 2.2vw, 1.9rem) 3rem;
  scrollbar-color: var(--o-line-2) transparent;
}

/* ---- the rail ---------------------------------------------------------- */

/* One hairline is the whole treatment. A rail that needs a shadow to separate
   itself from the content is a rail whose content is not black. */
.ofc-rail {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  min-height: 0;
  /* Chrome recedes: the rail is a shade darker than the work beside it. */
  background: var(--o-chrome, var(--o-bg));
  border-right: 1px solid var(--o-line-2);
}

.ofc-rail__brand {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.85rem 0.9rem;
  border-bottom: 1px solid var(--o-line);
  color: var(--o-text);
  text-decoration: none;
}

/* THE PUBLIC MARK, NOT THE APP-ICON TILE. This was /admin/img/mark-192.png, a
   violet monogram on an opaque near-black square, set on a plate — the last
   purple object in the shell. It is now /assets/mark.png, the same mark on
   transparency that the sign-in, the client account and the Cloudflare record
   use, crushed to white and faded below the name beside it exactly as
   tkati.com's top bar does (tokens.css --mark-filter / --mark-opacity). The
   artwork is 512x444, so the box keeps its size and the glyph is contained. */
.ofc-logo {
  width: 30px;
  height: 30px;
  flex: none;
  object-fit: contain;
  filter: var(--mark-filter);
  opacity: var(--mark-opacity);
}

.ofc-rail__name {
  display: grid;
  gap: 0.05rem;
  min-width: 0;
}
.ofc-rail__name b {
  font-family: var(--display);
  font-size: var(--o-t-md);
  font-weight: 750;
  letter-spacing: -0.01em;
  line-height: 1.1;
}
.ofc-rail__name span {
  font-size: var(--o-t-xs);
  font-weight: 650;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--o-faint);
}

.ofc-rail__nav {
  min-height: 0;
  overflow-y: auto;
  padding: 0.6rem 0.55rem 1rem;
}

.ofc-side__g {
  margin: 1.15rem 0 0.35rem;
  padding: 0 0.7rem;
  font-size: 0.65625rem;          /* 10.5px: the group is a quiet label, not a heading */
  font-weight: 650;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #6B6E74;
}
.ofc-side__g:first-child { margin-top: 0.15rem; }

/* A HAIRLINE, NOT A SECOND COLOUR. The break between what is finished and what
   is not has to be visible at a glance, and the palette has one accent that is
   spent on things that cost money — an "unfinished" tint would be a second one
   competing with it. The rule does the separating; the label does the saying. */
.ofc-side__g--soon {
  margin-top: 1.15rem;
  padding-top: 0.85rem;
  border-top: 1px solid var(--o-line);
}

.ofc-side__a {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.65rem;
  min-height: 32px;
  padding: 0 0.7rem;
  border-radius: var(--o-r-sm);
  color: var(--o-dim);
  text-decoration: none;
  font-size: var(--o-t-sm);
  font-weight: 500;
  /* The highlight is drawn with an inset shadow rather than a border so the
     row does not change size when it becomes current — a menu whose items
     jump 2px on selection is a menu that feels broken. */
  transition: background-color .12s ease, color .12s ease;
}
.ofc-side__a:hover { background: var(--o-hover); color: var(--o-text); }

/* The current section is a filled pill, not a tinted strip with a coloured
   edge. One shape change reads at a glance and survives being colour-blind —
   it also carries aria-current, which is what a screen reader gets. */
/* SELECTED: a tonal field and a 2px accent rail at the left edge — never a
   bright capsule. The label steps up to full white; the icon takes the accent. */
.ofc-side__a.is-on {
  background: var(--o-press);
  color: #FFFFFF;
  font-weight: 600;
}
.ofc-side__a.is-on::before {
  content: "";
  position: absolute;
  left: -0.55rem;
  top: 7px;
  bottom: 7px;
  width: 2px;
  border-radius: 0 2px 2px 0;
  background: var(--o-accent);
}
/* Stated, not implied by colour — it also carries aria-current. A third of
   people cannot rely on the accent, and a screen reader gets nothing at all
   from a box-shadow. */
.ofc-side__a.is-on .ofc-i { color: var(--o-accent); }
/* Pages whose router marks the row with aria-current (Ads) get the same
   selected state as .is-on — it had none, so Ads never said where you were. */
.ofc-side__a[aria-current="page"] { background: var(--o-press); color: #FFFFFF; font-weight: 600; }
.ofc-side__a[aria-current="page"]::before {
  content: ""; position: absolute; left: -0.55rem; top: 7px; bottom: 7px; width: 2px;
  border-radius: 0 2px 2px 0; background: var(--o-accent);
}
.ofc-side__a[aria-current="page"] .ofc-i { color: var(--o-accent); }

/* A WORLD OPEN IN PLACE (Phase VI). The parent row (Ads) reads as "you are
   inside this", its views hang beneath it on a guide line, and the current
   view carries the selection. Sub-labels are quieter still than group labels. */
.ofc-side__parent.is-open { color: var(--o-text); font-weight: 600; }
.ofc-side__parent.is-open .ofc-i { color: var(--o-text); }
.ofc-side__sub {
  position: relative;
  margin: 0.1rem 0 0.35rem 1.15rem;
  padding-left: 0.55rem;
  border-left: 1px solid var(--o-line-2);
}
.ofc-side__sub .ofc-side__a { min-height: 30px; padding-left: 0.6rem; font-size: 0.8125rem; }
.ofc-side__sub .ofc-side__a .ofc-i { width: 14px; height: 14px; }
.ofc-side__sub .ofc-side__a[aria-current="page"]::before { left: calc(-0.55rem - 1px); }
.ofc-side__sg {
  margin: 0.6rem 0 0.15rem;
  padding: 0 0.6rem;
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #5C5F65;
}
.ofc-side__sg:first-child { margin-top: 0.2rem; }

/* The sprite itself is a definitions block, not a picture. Taken out of flow
   rather than `display: none`, because a `<use>` referencing a symbol inside a
   display:none tree still resolves but some engines skip painting it. */
.ofc-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

.ofc-i {
  width: 16px;
  height: 16px;
  flex: none;
  color: var(--o-faint);
  stroke: currentColor;
  fill: none;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.ofc-side__a:hover .ofc-i { color: var(--o-dim); }

.ofc-side__t { flex: 1 1 auto; min-width: 0; }

/* The count beside a section. Only ever the number that COSTS something —
   see the badge comments in page-office.mjs. */
/* A BADGE MEANS SOMEONE HAS TO ACT. Quiet figures at rest; the ones that
   need action (.is-hot) carry the warn mark — a dot and the number, not a
   filled lozenge competing with the label. */
.ofc-side__n {
  min-width: 1.25rem;
  padding: 0;
  background: none;
  color: var(--o-faint);
  font-size: var(--o-t-xs);
  font-weight: 600;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.ofc-side__n:empty { display: none; }
/* `--o-warn-bg`, not a second near-black: #17100B differed from the token by a
   luminance of 0.0003 — invisible, and a separate value to keep in step. */
.ofc-side__n.is-hot { background: none; color: var(--o-warn-ink); }
.ofc-side__n.is-hot::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-right: 6px;
  vertical-align: 1px;
  border-radius: 50%;
  background: var(--o-warn);
}

.ofc-rail__foot {
  padding: 0.75rem 0.8rem;
  border-top: 1px solid var(--o-line-2);
  display: grid;
  gap: 0.6rem;
}
/* One row, evenly weighted, no wrap: three controls on two ragged lines is
   what a footer looks like when nobody laid it out. */
.ofc-rail__foot .ofc-actions {
  gap: 0.15rem;
  margin-inline: -0.55rem;
}
.ofc-rail__foot .ofc-actions > * { flex: 0 1 auto; }

.ofc-me { display: flex; align-items: center; gap: 0.55rem; min-width: 0; }

/* Initials, not a photograph nobody has uploaded. */
.ofc-me__mark {
  width: 28px;
  height: 28px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 999px;
  background: var(--o-accent-wash);
  border: 1px solid rgb(var(--aqua-rgb) / 0.24);
  color: var(--o-accent);
  font-family: var(--display);
  font-size: var(--o-t-xs);
  font-weight: 750;
  letter-spacing: 0.02em;
}

.ofc-me__id { display: grid; min-width: 0; gap: 0.05rem; }
.ofc-me__id b {
  font-size: var(--o-t-sm);
  font-weight: 650;
  /* An email is longer than a 15rem rail and must not wrap it wider. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ofc-me__id span {
  font-size: var(--o-t-xs);
  font-weight: 650;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--o-faint);
}

/* ---- the header -------------------------------------------------------- */

.ofc-top {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 1rem;
  min-height: 64px;
  padding: 0.75rem clamp(1rem, 2.4vw, 2rem);
  background: var(--o-bg);
  border-bottom: 1px solid var(--o-line-2);
}

.ofc-top__where { min-width: 0; flex: 1 1 auto; }
.ofc-top__where h1 {
  margin: 0;
  font-family: var(--display);
  font-size: var(--o-t-xl);
  font-weight: 650;
  letter-spacing: -0.022em;
  line-height: 1.15;
}
.ofc-top__where p {
  margin: 0.2rem 0 0;
  font-size: var(--o-t-sm);
  font-weight: 450;
  color: var(--o-faint);
}

.ofc-top__acts { display: flex; align-items: center; gap: 0.5rem; flex: none; }

/* ⌘K. A section list you aim at with a mouse is a menu; one you type at is a
   tool. */
.ofc-jump {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  min-height: 34px;
  min-width: 15rem;
  justify-content: space-between;
  padding: 0 0.45rem 0 0.75rem;
  border: 1px solid var(--o-line-2);
  border-radius: var(--o-r);
  background: var(--o-inset);
  background-image: none;
  box-shadow: none;
  color: var(--o-faint);
  font: 600 var(--o-t-sm)/1 var(--body);
  cursor: pointer;
}
.ofc-jump:hover { color: var(--o-text); border-color: var(--o-line-2); background: var(--o-hover); }
.ofc-jump:active { transform: none; }
.ofc-jump kbd {
  padding: 0.12rem 0.32rem;
  border: 1px solid var(--o-line-2);
  border-radius: 5px;
  background: var(--o-inset);
  font: 650 var(--o-t-xs)/1 var(--body);
  color: var(--o-faint);
}

/* ==========================================================================
   3. CONTROLS

   app.css gives every <button> a 56px filled green treatment, which is right
   for a thumb in a glove and wrong for a toolbar. Everything below is scoped
   to #office so the field app keeps its targets.
   ========================================================================== */

:is(#office, #portal, #ebay, #ads) .ofc-primary,
:is(#office, #portal, #ebay, #ads) .ofc-ghost,
:is(#office, #portal, #ebay, #ads) .ofc-quiet {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  min-height: 34px;
  padding: 0 0.9rem;
  border-radius: var(--o-pill);
  font: 600 var(--o-t-sm)/1 var(--body);
  letter-spacing: 0.005em;
  cursor: pointer;
  /* These are class-shaped, not element-shaped, and the mailbox's Reply and
     Forward are <a> because they hand off to a mail client rather than doing
     something here. Without this they arrive underlined: button geometry
     wearing link decoration, which reads as neither. */
  text-decoration: none;
  transition: background-color .12s ease, border-color .12s ease, color .12s ease;
}
:is(#office, #portal, #ebay, #ads) .ofc-primary:active,
:is(#office, #portal, #ebay, #ads) .ofc-ghost:active,
:is(#office, #portal, #ebay, #ads) .ofc-quiet:active { transform: none; }

/* Filled, in the brand green, with the site's own gradient — the one flourish
   worth carrying, because it is what makes a DZT button look like one. */
:is(#office, #portal, #ebay, #ads) .ofc-primary {
  justify-self: start;
  border: 1px solid var(--o-accent-deep);
  background: var(--o-accent-deep);
  /* FLAT. The site's button gradient is right on a photograph and wrong here:
     it is the same specular idea as the glass, one control at a time. */
  background-image: none;
  box-shadow: none;
  /* BLACK ON WHITE, tkati.com's one filled control: 19.80:1. It was white on
     #9A5CF3, which measured 4.04:1 — under the floor on the control every
     form ends with (the 5.34:1 once written here was measured on an older
     green). */
  color: var(--o-on-accent);
  font-weight: 700;
}
/* HOVER IS ONE STEP DARKER, AND IT IS A TOKEN: --teal is #EDEDED, the middle
   stop of the public button ramp, and keeps the label at 16.91:1. An earlier
   hover (`#128e62`) took its own label under 4.5:1 at the moment somebody was
   about to click it; a token measured once cannot drift like that. */
:is(#office, #portal, #ebay, #ads) .ofc-primary:hover:not(:disabled) {
  background-color: var(--teal);
}
:is(#office, #portal, #ebay, #ads) .ofc-primary:disabled {
  background: var(--o-inset);
  background-image: none;
  border-color: var(--o-line);
  box-shadow: none;
  color: var(--o-faint);
  cursor: default;
}

:is(#office, #portal, #ebay, #ads) .ofc-ghost {
  /* A control's only edge, so it is the 3:1 hairline, as tkati.com's chips
     are; --o-line-2 is a container's edge and measures 1.23:1. */
  border: 1px solid var(--o-line-3);
  background: transparent;
  background-image: none;
  box-shadow: none;
  color: var(--o-dim);
}
:is(#office, #portal, #ebay, #ads) .ofc-ghost:hover:not(:disabled) { background: var(--o-hover); border-color: var(--o-line-4); color: var(--o-text); }
:is(#office, #portal, #ebay, #ads) .ofc-ghost:disabled { opacity: 0.5; cursor: default; }

:is(#office, #portal, #ebay, #ads) .ofc-quiet {
  min-height: 28px;
  padding: 0 0.7rem;
  border: 1px solid transparent;
  background: transparent;
  background-image: none;
  box-shadow: none;
  color: var(--o-dim);
  font-size: var(--o-t-xs);
  font-weight: 650;
}
:is(#office, #portal, #ebay, #ads) .ofc-quiet:hover:not(:disabled) { background: var(--o-hover); color: var(--o-text); }
:is(#office, #portal, #ebay, #ads) .ofc-quiet:disabled { opacity: 0.5; cursor: default; }

:is(#office, #portal, #ebay, #ads) .ofc-link {
  display: inline-flex;
  align-items: center;
  min-height: 28px;
  padding: 0 0.55rem;
  border-radius: var(--o-pill);
  color: var(--o-accent);
  font-size: var(--o-t-sm);
  font-weight: 650;
  text-decoration: none;
}
:is(#office, #portal, #ebay, #ads) .ofc-link:hover { background: var(--o-hover); }

/* :where AND NOT :is, on purpose — zero specificity, so a component that
   sets its own focus style wins without !important. It omitted #ads until
   2026-09-22, which means the entire Ads screen had no focus ring: a keyboard
   user could not see where they were. Nothing reported it, because
   verify.mjs's scope-list guard matched only `:is(` and never looked inside
   a `:where(`. The guard reads both now. */
:where(#office, #portal, #ebay, #ads) :focus-visible {
  outline: 2px solid var(--o-accent);
  outline-offset: 2px;
  border-radius: 4px;
}

/* ==========================================================================
   4. CARDS AND GRID
   ========================================================================== */

.ofc-grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 2rem 2.25rem;
  align-items: start;
}
/* One column until there is room for real columns. */
@media (max-width: 62rem) {
  .ofc-grid { grid-template-columns: minmax(0, 1fr); }
  .ofc-grid > * { grid-column: 1 / -1 !important; }
}

/* A PANEL IS THE GROUND PLUS ONE HAIRLINE. No fill, no shadow, no gradient —
   the panel is not a different material from the page, it is a boundary drawn
   on it. That is the whole difference between this and the version before it. */
/* NOT A BOX AROUND EVERY PAGE (Phase VI). A section of a view is a heading
   and its content on the canvas; sections are told apart by space and, when
   one follows another in the same column, a hairline. A drawn boundary is
   kept for what is genuinely separate — .ofc-card--panel, the inspector, a
   drawer, an alert — so a border means something again. */
.ofc-card {
  grid-column: span 12;
  min-width: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
}
.ofc-card--panel {
  padding: var(--o-pad);
  border: 1px solid var(--o-line-2);
  border-radius: var(--o-r);
  background: var(--o-inset);
}
.ofc-card--half { grid-column: span 6; }
.ofc-card--third { grid-column: span 4; }
.ofc-card--twothirds { grid-column: span 8; }
/* The honest half of the screen: a note ABOUT the screen rather than a panel
   of it, so it gets the dashed edge and no fill. */
.ofc-card--muted { border-style: dashed; }

.ofc-card__bar {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 0.85rem;
}
.ofc-card__bar > div { min-width: 0; }

.ofc-card h2 {
  margin: 0;
  font-family: var(--display);
  font-size: var(--o-t-lg);
  font-weight: 650;
  letter-spacing: -0.012em;
  line-height: 1.2;
}

.ofc-sub {
  margin: 0.25rem 0 0.9rem;
  max-width: 68ch;
  color: var(--o-faint);
  font-size: var(--o-t-sm);
  font-weight: 500;
  line-height: 1.5;
}
.ofc-card__bar .ofc-sub { margin-bottom: 0; }

/* A refusal is a sentence, not an empty box: "your role cannot read leads" and
   "no leads yet" are opposite facts and the screen says which. */
.ofc-state {
  margin: 0 0 0.75rem;
  color: var(--o-dim);
  font-size: var(--o-t-sm);
  font-weight: 600;
}
.ofc-state:empty { display: none; }

.ofc-note {
  /* BLOCK, ALWAYS. A note carries a left border and vertical margin, which an
     inline box cannot lay out — as a <span> in a table cell three of them drew
     on top of one another, the permit flags column overlapping itself into an
     unreadable stack. It renders as a <p> in the markup and as a <span> from
     the renderers, and it has to behave the same either way. */
  display: block;
  margin: 0.85rem 0 0;
  padding: 0 0 0 0.65rem;
  border-left: 2px solid var(--o-line-2);
  color: var(--o-dim);
  font-size: var(--o-t-sm);
  font-weight: 500;
}
/* Stacked in a cell, the first needs no lead-in and the rest need less. */
.ofc-lines td > .ofc-note { margin-top: 0.35rem; }
.ofc-lines td > .ofc-note:first-child { margin-top: 0; }
/* A note that is a PROBLEM has to read differently from one that is an
   observation. It was toggled by four renderers and styled by none. */
.ofc-note.is-warn {
  padding: 0.55rem 0.75rem;
  border-radius: var(--o-r-sm);
  background: var(--o-warn-bg);
  border-left: 2px solid var(--o-warn);
  /* `--o-warn-ink`, not a second cream. This was #F3D8C4 in two rules — a
     fourth warn colour with no token and no measured contrast, which drifts the
     moment one of the two is edited. The token is 9.20:1 on this ground. */
  color: var(--o-warn-ink);
  font-weight: 600;
}

/* ==========================================================================
   5. THE STAT ROW

   A few readings, one of them the hero. Exactly one hero figure per view: on
   Invoices it is what is overdue (the three tiles there are the row's only
   visible use; the inherited dashboard's tiles are drawn only behind the
   wired home, and never while it shows). Updated 2026-09-28, product map
   discovery 30: this said "four numbers" with the unanswered enquiry as the
   hero, which no screen draws any more.
   ========================================================================== */

/* ONE PANEL, NOT FOUR. Four separate boxes with four gaps between them is four
   objects; one panel divided by hairlines is an instrument, and it is what the
   row actually is — four readings of the same morning.

   THE GROUND IS THE PAGE, AND EACH CELL DRAWS ITS OWN HAIRLINE. This panel
   used to paint its ground in the line colour and let the 1px gaps show it
   through as dividers. That drew every divider correctly, and it also painted
   every EMPTY track of the last row solid line-grey: six tiles with a
   two-column hero need seven tracks, so below ~1,840px the row never filled,
   and the owner saw a large grey slab inside the metric block (678x151 at
   1440, 1,182x133 at 1728: product map §4). The cause was the ground, so the
   fix is the ground: it is the page colour now, and each cell's outline sits
   in the 1px gap beside it (two neighbours' outlines share the same pixel,
   and overflow:hidden trims the ones on the panel's own edge, where the
   border already is). An empty track is now simply the page. */
/* AND NO EMPTY TRACK AT ALL (review, 2026-09-28). Taking the grey out of the
   ground left the region itself: six tiles over five 13.5rem tracks at 1440
   still ended in a 680px hole beside "Builds in flight". A wrapping flex
   row cannot leave one: every line's tiles grow to fill it, so the last
   line is two wide tiles instead of two tiles and an empty field. The hero
   is a double-width tile by basis, not by span. */
/* A SUMMARY LINE, NOT A ROW OF TILES (Phase VI). Passive figures sit in one
   compact strip under the header; they are context for the list below, not
   the page's subject, so they get a number and a label and no box. */
.ofc-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 2.75rem;
  margin-bottom: 1.25rem;
  padding-bottom: 1.1rem;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--o-line);
  border-radius: 0;
  overflow: visible;
}

.ofc-stat {
  flex: 0 1 auto;
  max-width: 22rem;
  min-width: 0;
  display: grid;
  align-content: start;
  gap: 0.2rem;
  padding: 0;
  background: none;
  outline: 0;
}

.ofc-stat__k {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: var(--o-t-xs);
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--o-faint);
}

/* PROPORTIONAL figures, deliberately. tabular-nums gives every digit the width
   of a zero, which at 32px makes "121" read loose and badly spaced. Tabular is
   for COLUMNS of numbers that have to line up — see the tables below. */
.ofc-stat__n {
  margin: 0.1rem 0 0;
  font-family: var(--display);
  font-size: 1.625rem;
  font-weight: 750;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--o-text);
}

.ofc-stat__s {
  margin: 0;
  font-size: var(--o-t-sm);
  font-weight: 500;
  color: var(--o-faint);
  line-height: 1.4;
}

/* THE HERO. Spans two cells. "4 people are waiting" and "nobody is waiting"
   are not the same news and must not look alike — but the difference is
   carried by the FIGURE and a dot beside its label, not by painting a quarter
   of the screen amber. A tinted panel is what a dashboard does when it does
   not trust its own typography. */
.ofc-stat--hero { flex: 2 1 27rem; }
.ofc-stat--hero .ofc-stat__k::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--o-warn);
  flex: none;
}
.ofc-stat--hero .ofc-stat__n {
  font-size: var(--o-t-hero);
  color: var(--o-warn-ink);
}
.ofc-stat--hero .ofc-stat__k { color: var(--o-warn-ink); }

/* Clear is its own state and is SAID, not left as an absence: the dot turns
   green and the figure with it. (It was the purple accent for a month, which
   made "healthy" the brand colour rather than a state.) */
.ofc-stat--hero.is-clear .ofc-stat__k::before { background: var(--o-ok-mark); }
.ofc-stat--hero.is-clear .ofc-stat__n { color: var(--o-ok); }
.ofc-stat--hero.is-clear .ofc-stat__k { color: var(--o-faint); }

/* The invoices hero is labelled OVERDUE, and overdue money is the red state,
   not the amber one: past its date is past attention. Every other hero keeps
   the amber, which is what "needs you" means. */
#ofc-inv-lede:not(.is-clear):not(.is-unknown) .ofc-stat__k::before { background: var(--o-bad-mark); }
#ofc-inv-lede:not(.is-clear):not(.is-unknown) .ofc-stat__k,
#ofc-inv-lede:not(.is-clear):not(.is-unknown) .ofc-stat__n { color: var(--o-bad); }

@media (max-width: 62rem) {
  .ofc-stat--hero { flex-basis: 13.5rem; }
  .ofc-stat--hero .ofc-stat__n { font-size: var(--o-t-num); }
}

/* ---- the dashboard's own two columns -------------------------------------
   A 12-column grid put Pipeline (8) beside Waiting on us (4) and then dropped
   the variance chart onto a second row — and because a grid row is as tall as
   its tallest cell, the short Pipeline card left a half-screen of dead space
   above the chart. Two real columns that each stack their own cards is the
   shape a dashboard actually wants: nothing waits for the column beside it. */
.ofc-dash { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0.9rem; }
.ofc-dash__main, .ofc-dash__side { display: grid; gap: 0.9rem; align-content: start; min-width: 0; }
@media (min-width: 62.0625rem) {
  .ofc-dash { grid-template-columns: minmax(0, 2.1fr) minmax(0, 1fr); align-items: start; }
}

/* ==========================================================================
   6. THE PIPELINE BAR

   This replaced a wall of tiles. Nine stages rendered as nine equal boxes is
   nine facts with no shape; a stacked bar is one fact — how work is
   distributed — and it is the same nine numbers.

   THE COLOUR CARRIES THE ORDER, because the stages ARE an order: sold →
   scheduled → in progress → punch → substantially complete → complete →
   closed. That makes it an ORDINAL scale, so it is one hue in monotone
   lightness steps rather than nine unrelated colours. The hue is the brand
   green (OKLCH ~163°, the hue of --forest-soft and --sage); the seven steps
   were generated at even lightness and validated: monotone L, every adjacent
   gap ≥ 0.06, light end 2.21:1 on the card, hue spread 9°.

   ON HOLD AND CANCELLED ARE NOT STAGES, they are states, so they wear the
   reserved status colours and sit outside the ramp. A held job is not "further
   along" than a scheduled one and must not be coloured as though it were.
   ========================================================================== */

/* THE RAMP RUNS BRIGHT-TO-DARK ALONG THE STAGES, and the first version had it
   backwards. Built light-end-last, it made `closed` the palest, widest band on
   a dark ground — so the eye landed on the nine finished jobs and slid past the
   nineteen live ones, which is the opposite of what an office opens this screen
   for. Seen in a screenshot, not reasoned about.

   Reversing costs nothing: the validated property is that the steps are one hue
   with monotone lightness and ≥ 0.06 between them, and a monotone ramp read
   backwards is still monotone. Both ends were re-checked against the glass
   surface — the dark end at 2.79:1, above the 2.0 floor. */
#office {
  --st-sold:      #b0eed6;
  --st-scheduled: #8fd8bc;
  --st-progress:  #6fc2a2;
  --st-punch:     #50ad89;
  --st-subst:     #309772;
  --st-complete:  #00815b;
  --st-closed:    #006b45;
  --st-hold:      var(--o-warn);
  --st-cancelled: var(--o-neutral);
}

.ofc-bar {
  display: flex;
  align-items: stretch;
  /* THE 2px GAP IS THE SEPARATOR, not a border. A stroke around each segment
     would add ink that is not data; the ground showing through does the same
     job and stays quiet. */
  gap: 2px;
  height: 14px;
  margin: 0.2rem 0 0.9rem;
  border-radius: 4px;
  overflow: hidden;
  background: var(--o-inset);
}
.ofc-bar__seg {
  /* Set by page-office.mjs as a flex-grow weight — the count itself, so the
     widths are the numbers and not a rounded percentage of them. */
  flex: var(--seg, 1) 1 0;
  min-width: 3px;
  background: var(--seg-c, var(--o-neutral));
}
/* THE BREAK BETWEEN THE RAMP AND THE STATES. `on_hold` and `cancelled` are not
   points on the journey, and drawing them as segments of an ordinal ramp put
   the reserved warn colour between two green steps — the lightness progression
   reversed in the middle of the bar and resumed after it. This is the gap that
   says the run has ended: fixed width, no growth, the ground showing through.
   The ground, not a border — a 1px rule inside a rounded, clipped track is the
   one place a hairline goes astray. */
.ofc-bar__break {
  flex: 0 0 6px;
  background: var(--o-bg);
}
/* And the same break in the legend, in words, because the bar's gap is not
   available to a screen reader reading a list. */
.ofc-legend__sep {
  width: 100%;
  margin-top: 0.15rem;
  padding-top: 0.35rem;
  border-top: 1px solid var(--o-line);
}
.ofc-legend__sep .ofc-legend__t { color: var(--o-faint); }
.ofc-bar--empty { background: var(--o-inset); }

/* The legend is always present: identity is never colour alone. */
.ofc-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 1rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
.ofc-legend li {
  display: flex;
  align-items: baseline;
  gap: 0.45rem;
  font-size: var(--o-t-sm);
}
.ofc-legend__d {
  width: 9px;
  height: 9px;
  flex: none;
  border-radius: 3px;
  transform: translateY(1px);
  background: var(--seg-c, var(--o-neutral));
}
/* Text wears TEXT tokens, never the series colour — a light green is
   illegible as type. The swatch beside it carries the identity. */
.ofc-legend__t { color: var(--o-dim); font-weight: 500; }
.ofc-legend__n { color: var(--o-text); font-weight: 700; font-variant-numeric: tabular-nums; }

/* The stages with nothing in them, as one line rather than nine empty boxes.
   Still REPORTED — "no jobs on hold" is a fact, and hiding it would be the
   screen deciding what you may know — just reported at what a zero is worth. */
.ofc-none {
  margin: 0.65rem 0 0;
  font-size: var(--o-t-sm);
  font-weight: 500;
  color: var(--o-faint);
}
.ofc-none:empty { display: none; }

/* ==========================================================================
   7. THE VARIANCE CHART

   Estimated against actual, by cost code. DIVERGING, because the question is
   which side of the estimate a code fell on: two hues away from a neutral
   centre line, equal steps per arm.

   THE PALE PEACH IS NOT USED AS A FILL. #E8A87C against the green arm
   separates at ΔE 4.8 under protanopia — below the floor of 6 — so it is text
   only, and the fill is #C4703A, which clears at 9.5. Measured, not judged.

   Direction is also encoded by SIDE and by a signed number on every row, so
   the chart still reads with no colour at all.
   ========================================================================== */

.ofc-var { display: grid; gap: 0.15rem; }

.ofc-var__row {
  display: grid;
  grid-template-columns: minmax(7rem, 1.4fr) minmax(9rem, 2fr) 4.5rem;
  align-items: center;
  gap: 0.75rem;
  padding: 0.32rem 0.5rem;
  border-radius: var(--o-r-sm);
}
.ofc-var__row:hover { background: var(--o-hover); }

.ofc-var__k {
  font-size: var(--o-t-sm);
  font-weight: 650;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ofc-var__k small {
  display: block;
  font-size: var(--o-t-xs);
  font-weight: 500;
  color: var(--o-faint);
  font-variant-numeric: tabular-nums;
}

/* Two half-tracks either side of a hairline centre. The bar grows FROM the
   centre, so an overrun and an underrun of the same size are mirror images. */
.ofc-var__track {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  height: 10px;
  border-left: 0;
  position: relative;
}
.ofc-var__track::before {
  content: '';
  position: absolute;
  left: 50%;
  top: -2px;
  bottom: -2px;
  width: 1px;
  background: var(--o-line-2);
}
.ofc-var__under, .ofc-var__over { height: 10px; }
.ofc-var__under {
  justify-self: end;
  width: var(--w, 0%);
  background: var(--st-punch);
  border-radius: 4px 0 0 4px;   /* rounded data-end, square at the baseline */
}
.ofc-var__over {
  justify-self: start;
  width: var(--w, 0%);
  background: var(--o-warn);
  border-radius: 0 4px 4px 0;
}

.ofc-var__n {
  text-align: right;
  font-size: var(--o-t-sm);
  font-weight: 700;
  /* TABULAR here: this is a column of numbers that has to line up. */
  font-variant-numeric: tabular-nums;
}
.ofc-var__n.over  { color: var(--o-warn-ink); }
.ofc-var__n.under { color: var(--o-ok); }

.ofc-var__head {
  display: grid;
  grid-template-columns: minmax(7rem, 1.4fr) minmax(9rem, 2fr) 4.5rem;
  gap: 0.75rem;
  padding: 0 0.5rem 0.4rem;
  margin-bottom: 0.25rem;
  border-bottom: 1px solid var(--o-line);
  font-size: var(--o-t-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--o-faint);
}
.ofc-var__head span:last-child { text-align: right; }
.ofc-var__head span:nth-child(2) { text-align: center; }

/* ==========================================================================
   8. LISTS AND TABLES
   ========================================================================== */

.ofc-table { width: 100%; border-collapse: collapse; font-size: var(--o-t-sm); }

.ofc-table th {
  position: sticky;
  top: 0;
  z-index: 1;
  text-align: left;
  padding: 0.4rem 0.6rem;
  background: var(--o-bg);
  border-bottom: 1px solid var(--o-line-2);
  color: var(--o-faint);
  font-size: var(--o-t-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.ofc-table td {
  padding: 0.42rem 0.6rem;
  border-bottom: 1px solid var(--o-line);
  font-weight: 500;
  color: var(--o-dim);
}
.ofc-table tbody tr:hover td { background: var(--o-hover); color: var(--o-text); }
.ofc-table tbody tr:last-child td { border-bottom: 0; }
.ofc-table td:first-child { color: var(--o-text); font-weight: 650; }
.ofc-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
/* SAID, NOT BLANK. An empty cell reads as "not loaded yet"; a customer with no
   current property is a customer nothing can be attached to. */
.ofc-table td.is-missing { color: var(--o-faint); font-style: italic; }
/* A SECOND LINE IN A CELL, on the model of `.ofc-tot dd small`. Inline it read
   "none now77 Industrial Way, Lincoln — owned until 2026-03-01" — two facts
   with no space between them, which is worse than the omission it replaced.
   Upright, because the qualifier is the concrete half: the cell above it is
   italic to mark an absence, and a date is not an absence. */
.ofc-table td small {
  display: block;
  margin-top: 0.15rem;
  font-size: var(--o-t-xs);
  font-style: normal;
  color: var(--o-faint);
}

.ofc-tablewrap { overflow-x: auto; margin: 0 -0.35rem; padding: 0 0.35rem; }

/* ---- the waiting list --------------------------------------------------
   `.ofc-feed`, NOT `.ofc-list`. This is a three-column row — monogram, who,
   age chip — and `.ofc-list` was already spoken for by the owner's waiver list
   and the portal's invoices, which are `<b><span><em>` stacked. Taking the
   name would have laid a 3-column grid over their 3 children and shuffled
   every one of those rows sideways. */
.ofc-feed { list-style: none; margin: 0; padding: 0; display: grid; }

.ofc-feed li {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.7rem;
  padding: 0.5rem 0.55rem;
  border-radius: var(--o-r-sm);
  border-bottom: 1px solid var(--o-line);
}
.ofc-feed li:last-child { border-bottom: 0; }
.ofc-feed li:hover { background: var(--o-hover); }

.ofc-mono {
  width: 26px;
  height: 26px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 8px;
  background: var(--o-inset);
  color: var(--o-dim);
  font-family: var(--display);
  font-size: var(--o-t-xs);
  font-weight: 750;
}

.ofc-feed__who { display: grid; gap: 0.05rem; min-width: 0; }
.ofc-feed__who b {
  font-size: var(--o-t-sm);
  font-weight: 650;
  color: var(--o-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ofc-feed__who span {
  font-size: var(--o-t-xs);
  font-weight: 500;
  color: var(--o-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---- chips ------------------------------------------------------------- */

.ofc-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.14rem 0.45rem;
  border-radius: 999px;
  background: var(--o-inset);
  color: var(--o-dim);
  font-size: var(--o-t-xs);
  font-weight: 650;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
/* Age, as severity. A status colour ships with a word beside it, never alone:
   the chip always says the age too. */
/* NOT AN ALARM. An unchecked lead is a real lead and must not be dressed as a
   problem — it sits in the same NEW column as every other one and is worked the
   same way. This says "read the number twice", not "something went wrong", so
   it borrows the warm wash rather than the warning one. */
.ofc-chip--unchecked { background: var(--o-warn-bg); color: var(--o-warn-ink); }
/* HOT IS PAST ATTENTION: 72 hours unanswered, a card declined, an invoice
   unpaid. Those are the overdue and failed states, so they take the red, and
   the amber is left meaning "look at this" rather than "this has gone wrong". */
.ofc-chip--hot  { background: var(--o-bad-bg); color: var(--o-bad); }
.ofc-chip--warm { background: var(--o-warn-bg); color: var(--o-warn-ink); }
/* OK is a state, so it is green: an active plan, an approved change, a
   delivery received. It was the brand purple, which said "ours", not "fine". */
.ofc-chip--ok   { background: var(--o-ok-bg); color: var(--o-ok); }

/* ---- the roster -------------------------------------------------------- */

.ofc-roster { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.3rem; }
.ofc-roster li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  padding: 0.45rem 0.7rem;
  border: 1px solid var(--o-line);
  border-radius: var(--o-r-sm);
  background: var(--o-inset);
  font-size: var(--o-t-sm);
  font-weight: 600;
}
.ofc-roster button:first-of-type { margin-left: auto; }

/* A waiver's reason sits under its count rather than beside it — a sentence
   in a numeric column pushes every cell after it out of line. */
.ofc-lines td > small {
  display: block;
  margin-top: 0.15rem;
  font-size: var(--o-t-xs);
  font-weight: 400;
  color: var(--o-dim);
  line-height: 1.35;
}

/* An armed control states what the next click does. The BORDER takes the mark
   colour and the LABEL takes the ink one — §7 above: --o-warn is a mark, and
   putting it on text is the contrast failure that section exists to stop. */
.ofc-ghost.is-armed {
  border-color: var(--o-warn);
  color: var(--o-warn-ink);
}

/* THE SAMPLE-CONTENT BANNER.
 *
 * This is the one place on the screen where the warn colour is spent on
 * something that is not a number costing money, and it earns it: every figure
 * below it is fabricated, and the entire risk of the feature is somebody
 * reading one as real. It sits above the content rather than in a corner, it
 * is solid rather than a hairline, and it cannot be dismissed while the mode
 * is on — the button turns the MODE off, not the banner.
 */
.ofc-sample-banner {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.35rem 0.6rem;
  margin: 0 0 1.25rem;
  padding: 0.45rem 0.5rem 0.45rem 0.85rem;
  border-radius: var(--o-r-sm);
  font-size: var(--o-t-xs) !important;
  background: var(--o-warn-bg);
  border: 1px solid var(--o-warn);
  color: var(--o-warn-ink);
  font-size: var(--o-t-sm);
  font-weight: 600;
}
.ofc-sample-banner strong { color: var(--o-warn-ink); }
.ofc-sample-banner button { margin-left: auto; }
/* On the amber ground the ordinary control hairline measures 2.75:1, under
   the 3:1 a control's edge needs; the .55 step measures 5.4:1 there. */
:is(#office, #portal, #ebay, #ads) .ofc-sample-banner .ofc-ghost { border-color: var(--o-line-4); color: var(--o-text); }

/* ---- the CDTFA split, under the line editor ----------------------------
 *
 * A SUMMARY, NOT A SECOND TABLE. The lines above already carry the treatment
 * per row; this answers the one question the rows cannot — what each side is
 * worth — so it is a short list rather than a grid competing with the table it
 * sits under.
 */
.ofc-taxsplit { margin-top: 1rem; }
.ofc-taxsplit h4 {
  margin: 0 0 0.5rem;
  font-size: var(--o-t-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--o-faint);
}
.ofc-taxsplit__l { list-style: none; margin: 0; padding: 0; display: grid; gap: 1px;
  background: var(--o-line); border-radius: var(--o-r-sm); overflow: hidden; }
.ofc-taxsplit__l li {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  flex-wrap: wrap;
  padding: 0.45rem 0.7rem;
  background: var(--o-inset);
  font-size: var(--o-t-sm);
  font-weight: 600;
}
.ofc-taxsplit__l li b { margin-left: auto; font-variant-numeric: tabular-nums; }
.ofc-taxsplit__l li small {
  flex: 1 0 100%;
  font-size: var(--o-t-xs);
  font-weight: 500;
  color: var(--o-dim);
}
/* An unclassified line is the one that costs something. */
.ofc-taxsplit__l li.is-unpriced { color: var(--o-warn-ink); }

/* A select inside a table row: no field-grid label above it, and it must not
   force the column wider than the words it holds. */
.ofc-inline {
  max-width: 100%;
  min-width: 0;
  padding: 0.2rem 0.35rem;
  border: 1px solid var(--o-line-2);
  border-radius: var(--o-r-sm);
  background: var(--o-inset);
  color: var(--o-text);
  font-size: var(--o-t-xs);
  font-weight: 600;
}
.ofc-inline:disabled { opacity: 0.55; }

/* A LIST THAT IS NOT SHOWING EVERYTHING SAYS SO, and it is not a warning.
   Nothing is wrong when a table is capped — the screen is doing what it was
   asked. Warn amber here would make an ordinary large table look like a fault
   and would spend the one accent this design reserves for a number that is
   costing money. Dim ink and the same hairline every other note uses. */
.ofc-state.is-partial {
  color: var(--o-dim);
  border-left: 2px solid var(--o-line-2);
  padding-left: 0.65rem;
}

/* A checkbox reads left-to-right, not stacked. `.ofc-fields label` puts its
   text above the control, which is right for a text field and wrong here — the
   box belongs beside the words it qualifies. */
.ofc-fields label.ofc-check {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  text-transform: none;
  letter-spacing: 0;
  font-size: var(--o-t-sm);
  font-weight: 600;
  color: var(--o-text);
  align-self: end;
  min-height: 2.4rem;
}
.ofc-fields label.ofc-check input {
  width: 1.05rem;
  height: 1.05rem;
  min-height: 0;
  accent-color: var(--o-accent);
  flex: 0 0 auto;
}

/* ---- a magnitude bar inside a table row --------------------------------
 *
 * ONE FILL FOR EVERY ROW. Length carries the magnitude; a ramp would be a
 * second encoding of the same number and the reader would hunt for a meaning
 * that is not there. The colour is a step from the stage ramp rather than the
 * accent, because the accent means "the current section" and "a good state"
 * and a landing page booking less than another one is neither.
 *
 * THE ROUNDED END IS ON THE DATA END ONLY. A bar rounded at both ends starts
 * somewhere other than the baseline, which reads as a floating range.
 */

.ofc-mag { min-width: 7rem; }
.ofc-mag__track {
  display: block;
  height: 10px;
  border-radius: 2px;
  background: var(--o-inset);
}
.ofc-mag__fill {
  display: block;
  height: 10px;
  width: var(--w, 0%);
  min-width: 2px;                 /* a real, tiny value still draws something */
  background: var(--st-progress);
  border-radius: 2px 4px 4px 2px;
}
.ofc-mag__none {
  font-size: var(--o-t-xs);
  color: var(--o-faint);
  font-style: italic;
}

/* ---- photographs of what got buried ------------------------------------
 *
 * A FIXED ASPECT AND A CONTAINED FIT. Field photographs come off phones in
 * both orientations and at wildly different sizes; letting each set its own
 * height gives a ragged wall, and `cover` would crop the very thing being
 * evidenced — the edge of the trench, the depth mark on the sleeve. So the
 * frame is fixed and the image is contained inside it, letterboxed rather
 * than cut.
 */

.ofc-shots {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
  gap: 0.7rem;
}
.ofc-shot { min-width: 0; }
.ofc-shot__f {
  position: relative;
  aspect-ratio: 4 / 3;
  display: grid;
  place-items: center;
  overflow: hidden;
  border: 1px solid var(--o-line);
  border-radius: var(--o-r-sm);
  background: var(--o-inset);
}
.ofc-shot__f img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.ofc-shot__ph {
  padding: 0 0.5rem;
  color: var(--o-faint);
  font-size: var(--o-t-xs);
  text-align: center;
  overflow-wrap: anywhere;
}
.ofc-shot__t {
  margin: 0.35rem 0 0;
  font-size: var(--o-t-sm);
  font-weight: 600;
  color: var(--o-text);
  overflow-wrap: anywhere;
}
.ofc-shot__m {
  margin: 0.1rem 0 0;
  font-size: var(--o-t-xs);
  color: var(--o-dim);
  overflow-wrap: anywhere;
}

/* ---- counted rows (Database) ------------------------------------------- */

.ofc-counts {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: 0.3rem;
}
.ofc-counts li {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  padding: 0.4rem 0.65rem;
  border: 1px solid var(--o-line);
  border-radius: var(--o-r-sm);
  background: var(--o-inset);
  font-size: var(--o-t-sm);
  font-weight: 600;
}
.ofc-counts span { color: var(--o-dim); }
.ofc-counts b {
  font-family: var(--display);
  font-size: var(--o-t-md);
  font-variant-numeric: tabular-nums;
  color: var(--o-text);
}
/* REFUSED IS NOT ZERO. Three different answers, never one dash. */
.ofc-counts li.is-empty b { color: var(--o-faint); }
.ofc-counts li.is-refused b { font-family: var(--body); font-size: var(--o-t-xs); color: var(--o-warn-ink); }

/* ---- what this cannot tell you ----------------------------------------- */

.ofc-cannot { margin: 0; display: grid; gap: 0.2rem; }
.ofc-cannot dt {
  margin-top: 0.9rem;
  font-family: var(--display);
  font-size: var(--o-t-md);
  font-weight: 700;
}
.ofc-cannot dt:first-child { margin-top: 0; }
.ofc-cannot dd {
  margin: 0;
  max-width: 70ch;
  color: var(--o-faint);
  font-size: var(--o-t-sm);
  font-weight: 500;
  line-height: 1.5;
}
/* What it would take to know. Brighter than the explanation above it, and
   not a colour: it is not a problem to act on today, and six amber lines down
   the eBay screen were exactly the orange-as-decoration the owner objected to. */
.ofc-cannot dd.ofc-needs { color: var(--o-text); margin-top: 0.15rem; }

/* ==========================================================================
   8b. THE SPLIT — a list, and the one thing you are looking at.

   Built for the mailbox and then wanted immediately by the estimate editor,
   which is the same shape for the same reason: you work a RUN of these, and a
   screen that opens each one on its own page is a screen where you lose your
   place every time you go back.

   `.ofc-split` is the layout; `.ofc-mail__*` and `.ofc-est__*` are what goes
   INSIDE it. Generalised at the second caller rather than the fifth, while it
   was still two rules and a rename.
   ========================================================================== */

.ofc-split {
  display: grid;
  grid-template-columns: minmax(17rem, 22rem) minmax(0, 1fr);
  gap: 0;
  align-items: start;
  /* The panes scroll independently and the page does not: a reading pane that
     pushes the list off the bottom is a single pane with extra steps. */
  height: min(62vh, 40rem);
}
@media (max-width: 68rem) {
  .ofc-split { grid-template-columns: minmax(0, 1fr); height: auto; }
  /* Stacked, the divider has to turn ninety degrees with the layout. */
  .ofc-split__list {
    max-height: 24rem;
    border-right: 0;
    border-bottom: 1px solid var(--o-line-2);
  }
  .ofc-split__pane { padding: 1rem 0 0; }
}

.ofc-split__list {
  list-style: none;
  margin: 0;
  padding: 0.25rem;
  height: 100%;
  overflow-y: auto;
  /* The only edge either pane gets. */
  border-right: 1px solid var(--o-line-2);
  scrollbar-color: var(--o-line-2) transparent;
}

.ofc-split__item {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.1rem 0.6rem;
  width: 100%;
  padding: 0.5rem 0.6rem;
  border: 0;
  border-radius: var(--o-r-sm);
  background: transparent;
  background-image: none;
  box-shadow: none;
  color: inherit;
  text-align: left;
  font: inherit;
  min-height: 0;
  cursor: pointer;
}
.ofc-split__item:hover { background: var(--o-hover); }
.ofc-split__item:active { transform: none; }
.ofc-split__item.is-on { background: var(--o-press); }

.ofc-mail__from {
  font-size: var(--o-t-sm);
  font-weight: 600;
  color: var(--o-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ofc-mail__when {
  font-size: var(--o-t-xs);
  font-weight: 550;
  color: var(--o-faint);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.ofc-mail__subj {
  grid-column: 1 / -1;
  font-size: var(--o-t-sm);
  font-weight: 500;
  color: var(--o-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* UNREAD IS WEIGHT AND A DOT, never colour alone. */
.ofc-split__item.is-unread .ofc-mail__from { color: var(--o-text); font-weight: 750; }
.ofc-split__item.is-unread .ofc-mail__subj { color: var(--o-dim); font-weight: 600; }
.ofc-mail__dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-right: 0.4rem;
  border-radius: 50%;
  background: var(--o-accent);
  transform: translateY(-1px);
}

/* Which @extrafreshbins.com address it landed on. info@ and ap@ are different
   queues to a person even when they share one screen. */
.ofc-mail__to {
  grid-column: 1 / -1;
  margin-top: 0.15rem;
  font-size: var(--o-t-xs);
  font-weight: 650;
  color: var(--o-faint);
  font-variant-numeric: tabular-nums;
}

.ofc-split__pane {
  height: 100%;
  overflow-y: auto;
  padding: 0.2rem 0 0.2rem 1.25rem;
  scrollbar-color: var(--o-line-2) transparent;
}
.ofc-split__pane:focus-visible { outline: 2px solid var(--o-accent); outline-offset: -2px; }

.ofc-mail__h { margin: 0 0 0.15rem; font-family: var(--display); font-size: var(--o-t-lg); font-weight: 700; letter-spacing: -0.012em; }
.ofc-mail__meta {
  display: grid;
  gap: 0.1rem;
  margin: 0 0 0.9rem;
  padding-bottom: 0.75rem;
  border-bottom: 1px solid var(--o-line);
  font-size: var(--o-t-sm);
  color: var(--o-faint);
}
.ofc-mail__meta b { color: var(--o-dim); font-weight: 650; }

/* The body is a stranger's text. Pre-wrap so the sender's own line breaks
   survive, and a measure so a 200-character line is still readable. */
.ofc-mail__body {
  margin: 0;
  max-width: 78ch;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  font-size: var(--o-t-sm);
  font-weight: 450;
  line-height: 1.6;
  color: var(--o-dim);
}

/* ---- which enquiry this message belongs to (0044) -------------------------
   ABOVE THE ACTIONS AND BELOW THE BODY, because it is a statement about the
   message rather than a thing to do with it — and it is read after the text
   that tells you which enquiry it is. Separated by a rule so it does not read
   as the last line of a stranger's email. */
.ofc-mail__filing {
  margin-top: 1rem;
  padding-top: 0.85rem;
  border-top: 1px solid var(--o-line-2);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem 0.75rem;
  font-size: var(--o-t-xs);
  color: var(--o-dim);
}
.ofc-mail__filing label {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
}
.ofc-mail__lead {
  font: inherit;
  color: var(--o-text);
  background: var(--o-bg-2, transparent);
  border: 1px solid var(--o-line-3);
  border-radius: 6px;
  padding: 0.28rem 0.5rem;
  max-width: 26ch;
}
.ofc-mail__lead:disabled { opacity: 0.5; cursor: not-allowed; }
.ofc-mail__filing-msg { margin: 0; }

/* ---- and the enquiry that does not exist yet ------------------------------
   ON ITS OWN ROW, FULL WIDTH. The filing row is a flex line of small print and
   dropping a primary button into it made the two read as one control — "Filed
   against [picker] [Put on the board]" invites the reading that the button
   commits the picker, which is the one thing it does not do. `flex-basis: 100%`
   breaks the line before it so the button owns its own row and its reason sits
   under it.

   It is the only PRIMARY control in this pane, and deliberately: it is the one
   thing here that creates a record, and everything beside it — reply, forward,
   mark unread — leaves the database as it found it. */
.ofc-mail__promote {
  flex-basis: 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.7rem;
  margin-top: 0.2rem;
}
.ofc-mail__promote-why {
  font-size: var(--o-t-xs);
  color: var(--o-dim);
  max-width: 52ch;
}

/* ---- the messages filed against one enquiry (the other half of 0044) ------
   A LIST OF BUTTONS, because each one goes to the message. Styled as text
   rather than as controls: five buttons stacked in a detail pane read as a
   toolbar, and these are a thread. */
.ofc-lead__mail {
  margin: 0.4rem 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 0.4rem;
  font-size: var(--o-t-xs);
}
.ofc-lead__mail li { line-height: 1.45; }
.ofc-linkish {
  appearance: none;
  background: none;
  border: 0;
  /* app.css gives every <button> min-height:56px for a touch target. This one
     is a LINK — it resets background, border and padding but never that, so
     the text sat at the top of an invisible 56px box with the underline
     stranded at the bottom of it, nowhere near the words it belongs to. The
     target stays reachable through padding instead: ~30px, comfortably past
     WCAG 2.5.8's 24px, and inline controls are exempt from the 44px AAA
     figure that 56 was reaching for. */
  min-height: 0;
  padding: 0.3rem 0;
  font: inherit;
  color: var(--o-text);
  text-align: left;
  cursor: pointer;
  border-bottom: 1px solid var(--o-line-2);
}
.ofc-linkish:hover { border-bottom-color: var(--teal); }
.ofc-linkish:focus-visible { outline: 2px solid var(--o-accent); outline-offset: 2px; }

/* ONE ROW, NOT THREE. The address tabs, the toolbar and a paragraph of prose
   were three stacked bands between the card heading and the first message —
   five rows of chrome before any mail, which is how a screen reads as assembled
   rather than designed. The tabs choose WHICH mail; the buttons narrow and act
   on it. Same job, same line: tabs left, actions right. */
.ofc-mail__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin: 0.9rem 0 0.25rem;
}
.ofc-mail__bar .ofc-addr { margin: 0; flex: 1 1 auto; }

.ofc-mail__tools {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex: 0 0 auto;
  margin-left: auto;
}
/* The help toggle is a single glyph, so it wants to be square rather than a
   pill with one character rattling around in it. */
.ofc-mail__keys {
  min-width: 34px;
  padding: 0;
  font-size: 0.95rem;
  font-weight: 700;
}
.ofc-mail__help-note {
  grid-column: 1 / -1;
  border-top: 1px solid var(--o-line-2);
  padding-top: 0.6rem;
  margin-top: 0.15rem;
}
.ofc-mail__help-note dd { margin: 0; color: var(--o-faint, var(--o-dim)); }
/* The pressed state of "Unread only" has to be visible without colour alone —
   it changes what the list contains, and a filter you cannot tell is on is how
   somebody concludes the mailbox has lost their mail. */
.ofc-mail__tools .ofc-ghost.is-on {
  background: var(--o-inset);
  border-color: var(--o-accent-deep);
  color: var(--o-text);
}
.ofc-mail__hint {
  margin-left: auto;
  font-size: var(--o-t-xs, 0.75rem);
  color: var(--o-faint, var(--o-dim));
  letter-spacing: 0.02em;
}
/* Below the rail's collapse the hint is the first thing worth losing: it
   describes keys a phone does not have. */
@media (max-width: 62rem) { .ofc-mail__hint { display: none; } }

/* M-04 — WHAT THE FIRST SCREEN IS SPENT ON. At 390px the card's orientation
   paragraph and four address pills wrapping onto two rows pushed the first
   message most of the way down the viewport: three-quarters of a phone screen
   explaining the mailbox to somebody who opened the mailbox.

   The paragraph is orientation and orientation belongs where there is room for
   it. The pills stay — they are a filter, not decoration — but they scroll on
   one row instead of stacking, which is the same trade the rail makes. */
@media (max-width: 48rem) {
  .ofc-view[data-view="mail"] .ofc-card__bar .ofc-sub { display: none; }
  .ofc-mail__bar { gap: 0.5rem; }
  .ofc-addr {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    scroll-snap-type: x proximity;
  }
  .ofc-addr::-webkit-scrollbar { display: none; }
  .ofc-addr > * { flex: none; scroll-snap-align: start; }
  .ofc-mail__tools { width: 100%; margin-left: 0; }
}

/* A-06 — off-canvas until focused, then a real target. Moved with a transform
   and never `display:none` or `visibility:hidden`, both of which take it out
   of the tab order — a skip link that cannot be reached by tabbing is a skip
   link that does not exist. */
.ofc-skip {
  position: absolute;
  left: 0.6rem; top: 0.6rem;
  z-index: 100;
  padding: 0.6rem 0.9rem;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  border-radius: 8px;
  background: var(--o-accent-deep);
  color: var(--o-on-accent);
  font: 650 var(--o-t-sm)/1 var(--body);
  text-decoration: none;
  transform: translateY(-160%);
}
/* BOTH, and `:focus` is the load-bearing one. `:focus-visible` alone leaves
   the link off-canvas for anything that focuses it without the browser
   deciding a keyboard did it — which is how a screen-reader user gets a
   control that is announced and not drawn. */
.ofc-skip:focus,
.ofc-skip:focus-visible {
  transform: none;
  outline: 2px solid var(--o-accent);
  outline-offset: 2px;
}
@media (prefers-reduced-motion: no-preference) {
  .ofc-skip { transition: transform .14s ease; }
}

/* WHERE IT LANDS YOU HAS TO SHOW. The target is a container the size of the
   screen, so this is the one place an outline is drawn round everything on
   purpose: pressing the link otherwise looks like nothing happened, and the
   only proof would be the next tab press. `:focus-visible` so it appears for
   the keyboard and not for a script. */
#ofc-grid:focus { outline: none; }
#ofc-grid:focus-visible {
  outline: 2px solid var(--o-accent);
  outline-offset: 6px;
  border-radius: 10px;
}

/* D-01 — the waiting list is now a list of doors, so it has to look like one.

   `grid-column: 1 / -1` IS THE LOAD-BEARING LINE. The row is a three-column
   grid — monogram, who, chip — and those three moved inside the link, so the
   link became a single grid item sitting in column one: 237px of a 361px row
   at phone width, with the rest of the row looking exactly as pressable as the
   part that was. Spanning the whole track is what makes the target the row.
   Measured in the browser at 390, not reasoned about. */
.ofc-feed__open {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  width: 100%;
  padding: 0.5rem 0.6rem;
  margin: -0.5rem -0.6rem;
  border-radius: 9px;
  color: inherit;
  text-decoration: none;
}
.ofc-feed__open:hover { background: var(--o-hover); }
.ofc-feed__open:focus-visible { outline: 2px solid var(--o-accent-deep); outline-offset: 1px; }
.ofc-feed__open .ofc-feed__who { flex: 1 1 auto; min-width: 0; }
.ofc-feed__more { padding-top: 0.5rem; }
.ofc-feed__more a { color: var(--o-accent); font-size: var(--o-t-sm); font-weight: 620; }

/* Day headings inside the list. Sticky so the run you are scrolling through
   keeps saying which day it is — the whole point is not having to scroll back
   up to find out. */
.ofc-mail__day {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: 0.5rem 0.9rem 0.3rem;
  background: var(--o-bg);
  font-size: var(--o-t-xs, 0.72rem);
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--o-faint, var(--o-dim));
}
.ofc-mail__day:first-child { padding-top: 0.2rem; }

/* The conversation count, on the subject line where the eye already is. */
.ofc-mail__thread {
  margin-left: 0.5rem;
  padding: 0.05rem 0.4rem;
  border: 1px solid var(--o-line-2);
  border-radius: var(--o-pill);
  font-size: var(--o-t-xs, 0.7rem);
  font-weight: 650;
  color: var(--o-faint, var(--o-dim));
  white-space: nowrap;
}

.ofc-mail__help {
  margin: 0.35rem 0 0;
  padding: 0.9rem 1rem;
  border: 1px solid var(--o-line-2);
  border-radius: var(--o-r, 10px);
  background: var(--o-inset);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 0.45rem 1.5rem;
  font-size: var(--o-t-sm);
}
.ofc-mail__help > div { display: flex; align-items: baseline; gap: 0.6rem; }
.ofc-mail__help dt { flex: 0 0 auto; display: flex; gap: 0.25rem; }
.ofc-mail__help dd { margin: 0; color: var(--o-dim); }
.ofc-mail__help kbd {
  min-width: 1.5rem;
  padding: 0.1rem 0.35rem;
  border: 1px solid var(--o-line);
  border-bottom-width: 2px;
  border-radius: 5px;
  background: var(--o-bg);
  font: 650 0.72rem/1.5 var(--mono, ui-monospace, monospace);
  color: var(--o-text);
  text-align: center;
}

/* Compose sits between the toolbar and the list rather than over them: a modal
   would cover the message you are half-replying to, and this screen's whole
   shape is list-beside-message. */
.ofc-compose {
  display: grid;
  gap: 0.7rem;
  margin: 0.6rem 0 0.2rem;
  padding: 1rem 1.1rem 1.1rem;
  border: 1px solid var(--o-line);
  border-radius: var(--o-r, 10px);
  background: var(--o-inset);
}
.ofc-compose__head {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
}
.ofc-compose__head b { font-size: var(--o-t-sm); }
.ofc-compose__from {
  color: var(--o-faint, var(--o-dim));
  font-size: var(--o-t-xs, 0.75rem);
}
.ofc-compose__head .ofc-quiet { margin-left: auto; }
.ofc-compose label {
  display: grid;
  gap: 0.3rem;
  font-size: var(--o-t-xs, 0.75rem);
  font-weight: 650;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--o-faint, var(--o-dim));
}
.ofc-compose input,
.ofc-compose textarea {
  width: 100%;
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--o-line-3);
  border-radius: 8px;
  background: var(--o-bg);
  color: var(--o-text);
  font: 450 var(--o-t-sm)/1.55 var(--body);
  text-transform: none;
  letter-spacing: 0;
}
.ofc-compose textarea { resize: vertical; min-height: 8rem; }
.ofc-compose input:focus-visible,
.ofc-compose textarea:focus-visible {
  outline: 2px solid var(--o-accent-deep);
  outline-offset: 1px;
}
.ofc-compose .ofc-actions { align-items: center; }
.ofc-compose .ofc-actions p { margin: 0; font-size: var(--o-t-sm); color: var(--o-dim); }

.ofc-mail__clip {
  width: 0.85em; height: 0.85em;
  margin-right: 0.3rem;
  vertical-align: -0.08em;
  fill: currentColor;
  opacity: 0.75;
}

/* Named for a screen reader, removed from sight. The icon above carries the
   same fact visually. */
.ofc-sr {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* A rule above the actions, because a pre-wrap body ends wherever the sender
   stopped typing and a button sitting directly under the last line reads as
   part of the message. */
/* FOUR BUTTONS IN FOUR WEIGHTS, IN A ROW, READ AS FOUR UNRELATED CONTROLS.
   They are not: two of them act on the message and two are utilities. Reply is
   the primary and Forward sits beside it; the utilities are pushed to the far
   end so the eye lands on the thing you came here to do. */
.ofc-mail__actions {
  margin-top: 1.5rem;
  padding-top: 1rem;
  border-top: 1px solid var(--o-line-2);
  gap: 0.5rem;
}
.ofc-mail__actions .ofc-quiet:first-of-type { margin-left: auto; }

.ofc-split__empty {
  display: grid;
  place-content: center;
  gap: 0.3rem;
  height: 100%;
  text-align: center;
  color: var(--o-faint);
  font-size: var(--o-t-sm);
}

/* ==========================================================================
   9. THE CRM BOARD
   ========================================================================== */

.ofc-board {
  display: grid;
  grid-auto-flow: column;
  /* SIX COLUMNS, NOT FIVE — new, contacted, quoted, won, lost AND spam. Six at
     a width where a name, a phone number and a line of scope are all readable
     does not fit a laptop, so this genuinely has to scroll. It always did; what
     it lacked was any sign of it, so the sixth column sat sliced down its
     middle by the card edge and read as broken rather than as more. Hence the
     fade below rather than columns squeezed until they fit. */
  grid-auto-columns: minmax(12rem, 1fr);
  gap: 0.7rem;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  padding-bottom: 0.4rem;
}
/* THE EDGE SAYS THERE IS MORE. Applied only when the board actually overflows
   and removed once it is scrolled to the end, so the fade is information rather
   than decoration — a permanent gradient on a board with nothing past it is a
   promise of content that is not there. */
.ofc-board.is-overflowing {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 3rem), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 3rem), transparent);
}
.ofc-board.is-overflowing.is-scroll-end {
  -webkit-mask-image: none;
  mask-image: none;
}
@media (prefers-reduced-motion: reduce) { .ofc-board { scroll-behavior: auto; } }

.ofc-board__col {
  display: grid;
  /* ONE TRACK THAT MAY SHRINK BELOW ITS CONTENT: a long unbroken address used to widen a card past its column
     and over the next column's heading (seen in the WON column, 2026-09-28). */
  grid-template-columns: minmax(0, 1fr);
  min-width: 0;
  align-content: start;
  gap: 0.35rem;
  padding: 0.5rem;
  border-radius: var(--o-r);
  min-height: 6rem;
  /* A column is a shallower pane than a card — it sits INSIDE one, and two
     full-strength rings nested is what makes a UI look bevelled. */
  border: 1px solid var(--o-line-2);
}
.ofc-board__h {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin: 0 0 0.2rem;
  padding: 0 0.15rem;
  font-size: var(--o-t-xs);
  font-weight: 750;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--o-faint);
}
/* THE COLUMN THAT COSTS MONEY WHILE YOU READ IT. */
.ofc-board__col.is-hot { border-color: rgb(var(--attn-rgb) / 0.34); }
.ofc-board__col.is-hot .ofc-board__h { color: var(--o-warn-ink); }

.ofc-board__card {
  display: grid;
  gap: 0.15rem;
  min-width: 0;
  overflow-wrap: anywhere;
  padding: 0.5rem 0.6rem;
  border: 1px solid var(--o-line);
  border-radius: var(--o-r-sm);
  background: var(--o-inset);
}
.ofc-board__card strong { font-size: var(--o-t-sm); font-weight: 650; }
.ofc-board__card em {
  font-style: normal;
  font-size: var(--o-t-xs);
  font-weight: 500;
  color: var(--o-faint);
}
.ofc-board__empty { margin: 0; padding: 0.2rem 0.15rem; color: var(--o-faint); font-size: var(--o-t-xs); }

/* ==========================================================================
   10. FORMS

   A task with a name, a rule above it, and its own button. Four unlabelled
   forms in one card was a wall of controls where nothing said where one job
   ended and the next began.
   ========================================================================== */

.ofc-task {
  display: grid;
  gap: 0.7rem;
  margin-top: 1.1rem;
  padding-top: 1rem;
  border-top: 1px solid var(--o-line);
}
/* The first task follows the card's own subtitle, which is already a
   separator; a second rule there reads as a mistake. */
.ofc-card > .ofc-sub + .ofc-task,
.ofc-card > .ofc-state + .ofc-task,
.ofc-card > .ofc-card__bar + .ofc-task { border-top: 0; padding-top: 0; margin-top: 0.4rem; }

.ofc-task__h {
  margin: 0;
  font-family: var(--display);
  font-size: var(--o-t-md);
  font-weight: 700;
  letter-spacing: -0.005em;
}
.ofc-task__why {
  margin: -0.45rem 0 0;
  max-width: 64ch;
  color: var(--o-faint);
  font-size: var(--o-t-sm);
  font-weight: 500;
}

.ofc-fields {
  display: grid;
  /* A field is as wide as its content needs, not as wide as the card: a single
     "Crew A" box stretched across 1300px was one of the things that read as
     unfinished. */
  grid-template-columns: repeat(auto-fit, minmax(11rem, 18rem));
  gap: 0.65rem 0.8rem;
}
.ofc-fields label {
  display: grid;
  gap: 0.25rem;
  font-size: var(--o-t-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--o-faint);
}
/* The pairs that genuinely need the room keep it — a customer-and-property
   line truncated into a half-width control is how somebody picks the wrong
   one. */
.ofc-field--wide { grid-column: 1 / -1; max-width: 38rem; }

/* `.ofc-input` IS A FIELD OUTSIDE A FORM: the Requests board's per-row state
   <select> carried it with no rule anywhere, so it rendered as the browser's
   own full-width control (review, 2026-09-28). */
.ofc-input,
.ofc-fields input,
.ofc-fields select,
/* A TEXTAREA IS A FIELD TOO, and left off this list it kept the browser
   default: white ground, black text, grey bevel, in the middle of a near-
   black card. Nothing warns about that — the element is simply never styled,
   so it renders exactly as the user agent intends. */
.ofc-fields textarea {
  min-height: 34px;
  padding: 0 0.6rem;
  /* The 3:1 control hairline: at --o-line-2 an empty field was a box nobody
     could find (1.65:1 before this palette, 1.23:1 after it). */
  border: 1px solid var(--o-line-3);
  border-radius: var(--o-r-sm);
  /* An input is a WELL: darker than the card behind it. A pale raised box on a
     dark ground reads as a button. */
  background: var(--o-inset);
  color: var(--o-text);
  font: 500 var(--o-t-sm)/1 var(--body);
  text-transform: none;
  letter-spacing: normal;
}
/* THREE INK STEPS, ALL MEASURED ON BLACK — so a placeholder is `--o-faint`
   and not an alpha wash. `rgba(237,241,239,0.28)` resolves over black to
   #424343, which is 2.12:1: a fourth step, invented, and less legible than the
   dimmest sanctioned one at 4.58:1. */
.ofc-fields input::placeholder,
.ofc-fields textarea::placeholder { color: var(--o-faint); }
.ofc-fields textarea { padding: 0.45rem 0.6rem; line-height: 1.5; resize: vertical; }
/* A FILE INPUT'S BUTTON IS A SEPARATE, UNSTYLED CONTROL and it arrives white
   on the same dark card. It is the one part of a form the page cannot draw
   itself, so it has to be reached through its pseudo-element. */
.ofc-fields input[type="file"] { padding: 0.3rem 0.4rem; }
.ofc-fields input[type="file"]::file-selector-button {
  min-height: 0;
  margin-right: 0.6rem;
  padding: 0.28rem 0.7rem;
  border: 1px solid var(--o-line-2);
  border-radius: var(--o-r-sm);
  background: var(--o-raised);
  color: var(--o-text);
  font: 600 var(--o-t-xs)/1.2 var(--body);
  cursor: pointer;
}
.ofc-fields input[type="file"]::file-selector-button:hover { border-color: var(--o-line-4); }
/* A SELECT SIZES TO ITS WIDEST OPTION, AND THAT PUSHED THE PAGE SIDEWAYS.
   Found at 390px the first time this screen was ever rendered that narrow:
   "INV-2026-0007 — Vance — rear yard, phase 1 — $14,400 owing" made the
   control 437px in a 390px viewport, and the whole document scrolled
   horizontally. `min-width: 0` is the half people forget — a grid item's
   default `min-width: auto` refuses to shrink below its content, so
   `max-width: 100%` alone does nothing here.

   The option text is not shortened: a payment posted against the wrong invoice
   is worse than a truncated line, so the control keeps the whole label and
   clips it. */
.ofc-fields select {
  padding-right: 0.4rem;
  min-width: 0;
  max-width: 100%;
  text-overflow: ellipsis;
}
.ofc-fields label { min-width: 0; }
.ofc-fields select:disabled { opacity: 0.55; }
/* THE PUBLIC FOCUS: a 2px white outline and a brighter edge, never a glow.
   A 3px wash built from the accent was a purple halo, and with the accent
   white it would be fog; an outline also survives forced-colours mode, which
   drops box-shadow entirely. :focus, not :focus-visible — the field you are
   typing in is the orientation, and a click should show it too. */
.ofc-input:focus,
.ofc-fields input:focus,
.ofc-fields select:focus {
  outline: 2px solid var(--o-accent);
  outline-offset: 1px;
  border-color: var(--o-line-4);
}
/* The compact one, sized to its options rather than to its row. */
.ofc-input--sm {
  width: auto;
  max-width: 14rem;
  min-height: 28px;
  justify-self: start;
  align-self: start;
}
.ofc-input:disabled { opacity: 0.55; }

/* ==========================================================================
   10b. THE REQUESTS BOARD (lead decision A8, 2026-09-28)

   One request per row, compact: the title and whose it is on one line, what
   they asked in at most two lines under it, when and how urgent in small
   type, and the state select at the row's end, sized to its options. The rows
   used to wear the forms' `.ofc-task` (a rule, a 1rem gap and a heading's
   margin per row) plus four classes with no rule anywhere, so a board of
   five requests filled the screen and each state control ran the width of
   the card.
   ========================================================================== */
.ofc-reqs {
  list-style: none;
  margin: 0.4rem 0 0;
  padding: 0;
}
.ofc-req {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.2rem 1rem;
  align-items: start;
  padding: 0.6rem 0;
  border-top: 1px solid var(--o-line);
}
.ofc-req:first-child { border-top: 0; }
.ofc-req__main { display: grid; gap: 0.15rem; min-width: 0; }
.ofc-req__h {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.15rem 0.6rem;
  margin: 0;
  min-width: 0;
}
.ofc-req__t {
  font-size: var(--o-t-md);
  font-weight: 650;
  color: var(--o-text);
  overflow-wrap: anywhere;
}
.ofc-req__who { font-size: var(--o-t-sm); color: var(--o-dim); }
.ofc-req__b {
  margin: 0;
  max-width: 72ch;
  font-size: var(--o-t-sm);
  line-height: 1.45;
  color: var(--o-dim);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  overflow-wrap: anywhere;
}
.ofc-req__m {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0;
  font-size: var(--o-t-xs);
  color: var(--o-faint);
}
/* Urgent is attention, so amber, and said in the word as well. */
.ofc-req__urgent { color: var(--o-warn-ink); font-weight: 700; }
/* A closed request is history: it stays, quieter. */
.ofc-req[data-state="done"] .ofc-req__t,
.ofc-req[data-state="declined"] .ofc-req__t { color: var(--o-dim); font-weight: 550; }
.ofc-req > .ofc-input--sm { align-self: center; }
/* MILESTONE E: a project changed where it is read, in a well under its own row. */
.ofc-jb-act { white-space: nowrap; text-align: right; }
.ofc-jb-edit > td { background: var(--o-inset); }
.ofc-edit { display: grid; grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr)); gap: 0.6rem 1rem; align-items: end; padding: 0.35rem 0; }
.ofc-edit label { display: grid; gap: 0.25rem; font-size: var(--o-t-xs); color: var(--o-dim); }
.ofc-edit__acts { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }
.ofc-edit__msg { margin: 0; font-size: var(--o-t-sm); color: var(--o-dim); }
.ofc-edit__msg[data-tone="bad"] { color: var(--o-bad); }
/* MILESTONE D: an enquiry card says what the home says about it, in the home's words and with a mark. */
.ofc-board__attn { display: inline-flex; align-items: center; gap: 0.35rem; font-size: 0.75rem; font-weight: 600; }
.ofc-board__attn::before { content: ''; flex: none; width: 0.45rem; height: 0.45rem; border-radius: 50%; background: currentColor; }
.ofc-board__attn--bad { color: var(--o-bad); }
.ofc-board__attn--attn { color: var(--o-warn); }
/* THE RECORD AN ADDRESS NAMED (#requests/<id>, #invoices/<id>; lead decision A3): marked by a bar and a
   well, not by colour alone, and focused so the next Tab starts from it. */
.ofc-req.is-target,
#office #ofc-inv-body tr.is-target { background: var(--o-inset); box-shadow: inset 3px 0 0 var(--o-ink, #fff); }
.ofc-req.is-target:focus,
#office #ofc-inv-body tr.is-target:focus { outline: none; }
.ofc-req.is-target:focus-visible,
#office #ofc-inv-body tr.is-target:focus-visible { outline: 2px solid #fff; outline-offset: -2px; }
/* On a phone the select goes under the words rather than squeezing them. */
@media (max-width: 40rem) {
  .ofc-req { grid-template-columns: minmax(0, 1fr); }
  .ofc-req > .ofc-input--sm { align-self: start; min-height: 36px; }
}

.ofc-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }

/* A form's own status line, everywhere rather than per-id. #nc-msg had this
   and the five forms added after it did not. */
.ofc-task p[role="status"] {
  margin: 0;
  min-height: 1.25em;
  font-size: var(--o-t-sm);
  font-weight: 550;
  color: var(--o-dim);
}
.ofc-task p[role="status"].is-warn { color: var(--o-warn-ink); }

.ofc-filter {
  min-height: 30px;
  min-width: 15rem;
  padding: 0 0.6rem;
  border: 1px solid var(--o-line-3);
  border-radius: var(--o-r-sm);
  background: var(--o-inset);
  color: var(--o-text);
  font: 500 var(--o-t-sm)/1 var(--body);
}
.ofc-filter::placeholder { color: var(--o-faint); }
.ofc-filter:focus {
  outline: 2px solid var(--o-accent);
  outline-offset: 1px;
  border-color: var(--o-line-4);
}

/* ==========================================================================
   11. BANNERS AND THE PALETTE
   ========================================================================== */

/* #ofc-boot says the screen stopped or failed to load, which is the red
   state; #ofc-stale below is a quiet grey note about old numbers. */
#ofc-boot, #ofc-stale {
  margin: 0 0 0.9rem;
  padding: 0.7rem 0.9rem;
  border-radius: var(--o-r);
  background: var(--o-bad-bg);
  border-left: 3px solid var(--o-bad-mark);
  color: var(--o-bad);
  font-size: var(--o-t-sm);
  font-weight: 600;
}
#ofc-stale {
  background: var(--o-inset);
  border-left-color: var(--o-line-2);
  color: var(--o-dim);
}

.ofc-palette {
  width: min(32rem, 92vw);
  margin: 12vh auto auto;
  padding: 0.45rem;
  border: 1px solid var(--o-line-2);
  border-radius: var(--o-r-lg);
  background: var(--o-bg);
  color: var(--o-text);
}
/* ONE RULE. There were two of these on consecutive lines, so the first was
   dead the moment the second was written — and the second used rgb(10,14,18),
   a near-black sitting 0.0003 in luminance from `--o-inset`. A fourth
   near-black nobody can see is still a fourth value to keep in step. */
.ofc-palette::backdrop { background: rgb(0 0 0 / 0.62); }
.ofc-palette input {
  width: 100%;
  min-height: 38px;
  padding: 0 0.7rem;
  border: 1px solid var(--o-line-3);
  border-radius: var(--o-r-sm);
  background: var(--o-inset);
  color: var(--o-text);
  font: 500 var(--o-t-md)/1 var(--body);
}
.ofc-palette input:focus { outline: 2px solid var(--o-accent); outline-offset: 1px; border-color: var(--o-line-4); }
.ofc-palette ul { list-style: none; margin: 0.4rem 0 0; padding: 0; display: grid; gap: 0.1rem; }
.ofc-palette li {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.45rem 0.6rem;
  border-radius: var(--o-r-sm);
  font-size: var(--o-t-sm);
  font-weight: 650;
  cursor: pointer;
}
.ofc-palette li.is-on { background: var(--o-press); }
.ofc-palette li em { font-style: normal; color: var(--o-faint); font-weight: 500; }

.ofc-view[hidden] { display: none; }

/* ==========================================================================
   12. NARROW

   Below 62rem the rail stops being a rail. It becomes a scrolling strip under
   the header, the frame stops owning the viewport height, and the page scrolls
   the way a page does — because a 15rem fixed column on a 380px screen is
   40% of the screen spent on navigation.
   ========================================================================== */

@media (max-width: 62rem) {
  #office, #ads, #ebay {
    display: block;
    height: auto;
    overflow: visible;
  }
  .ofc-main { display: block; overflow: visible; }
  .ofc-scroll { overflow: visible; padding: 0.9rem 0.9rem 3rem; }

  .ofc-rail {
    display: block;
    border-right: 0;
    border-bottom: 1px solid var(--o-line);
  }
  /* THE STRIP SCROLLS AND NOTHING SAID SO. Measured at 390px: 1028px of nav
     inside a 390px window with the scrollbar hidden, so three of the six work
     sections were visible and Clients, Estimates, Contracts, Database and
     Settings sat off the right edge with no arrow and no fade. Half the
     application was unreachable on a phone and looked complete.
  
     The fade is the device the CRM board already uses, and for the same
     reason: it is information, so it is removed at the end of the scroll
     rather than sitting there promising content that is not there. */
  .ofc-rail__nav {
    display: flex;
    gap: 0.3rem;
    overflow-x: auto;
    padding: 0.5rem 0.7rem;
    scrollbar-width: none;
    scroll-snap-type: x proximity;
    overscroll-behavior-x: contain;
  }
  .ofc-rail__nav::-webkit-scrollbar { display: none; }
  .ofc-rail__nav.is-overflowing {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 2.5rem), transparent);
    mask-image: linear-gradient(to right, #000 calc(100% - 2.5rem), transparent);
  }
  .ofc-rail__nav.is-overflowing.is-scroll-end {
    -webkit-mask-image: none;
    mask-image: none;
  }
  .ofc-side__a { scroll-snap-align: start; }
  
  /* 44px, NOT 35. This is the width at which the rail becomes a thumb
     target, on the app a foreman opens on site in gloves, on a strip where
     a mis-tap lands on the wrong section. The desktop rail is pointer-driven
     and keeps its tighter rhythm. */
  .ofc-side__a,
  .ofc-side__g--soon { min-height: 44px; }
  .ofc-side__g { display: none; }
  /* EXCEPT THE ONE THAT DIVIDES FINISHED FROM UNFINISHED. The rail becomes a
     horizontal scroller here and every group label is dropped, which at 390px
     left Mailbox and Warranty sitting in one undifferentiated row — the exact
     confusion the grouping was added to remove. This one stays, inline, as the
     marker you scroll past. */
  .ofc-side__g--soon {
    display: inline-flex;
    align-items: center;
    margin: 0 0.15rem;
    padding: 0 0.6rem 0 0.75rem;
    border-top: 0;
    border-left: 1px solid var(--o-line);
    white-space: nowrap;
    flex: none;
  }
  .ofc-side__a { flex: none; white-space: nowrap; }
  .ofc-side__a.is-on { background: var(--o-press); }
  .ofc-rail__foot { display: none; }

  .ofc-top { position: sticky; top: 0; z-index: 20; }
  .ofc-jump { display: none; }

  .ofc-var__row, .ofc-var__head { grid-template-columns: minmax(6rem, 1.2fr) minmax(6rem, 1.6fr) 3.6rem; }
}

/* ---- PHONE (Phase VI) --------------------------------------------------------
   Measured at 390px on the wall: the page action covered the page title
   ("Cli[+ New client]"), summary figures stacked into a screen of their own,
   the Clients view bar fell into a column, and Ads' nested rail stood 480px
   tall. On a phone: the title has its own line and the actions a row under
   it; figures sit three to a row without their sentences; the Ads strip is
   Ads' own views. */
@media (max-width: 40rem) {
  .ofc-top { flex-wrap: wrap; row-gap: 0.55rem; min-height: 0; padding: 0.7rem 1rem; }
  .ofc-top__where { flex: 1 1 100%; }
  .ofc-top__acts { flex: 1 1 100%; flex-wrap: wrap; }
  #office .ofc-clientview { display: none; }
  .ofc-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.75rem 1rem; }
  .ofc-stat { max-width: none; }
  .ofc-stat__s { display: none; }
  .ofc-stat__n, #office .ofc-stat--hero .ofc-stat__n { font-size: 1.375rem; }
  .ofc-stat--hero:not(.is-clear) { grid-column: 1 / -1; }
  .ofc-stat--hero:not(.is-clear) .ofc-stat__s { display: block; }
  .ofc-stat__k { font-size: 0.65625rem; letter-spacing: 0.06em; }
  #ofc-clients-card { display: block; }
  #ofc-clients-card > .ofc-card__bar { margin: 0 0 0.75rem; }
  #ofc-clients-card > .ofc-card__bar .ofc-filter { min-width: 0; width: 100%; }
  .ofc-mail__tools { flex-wrap: wrap; }
  .ofc-mail__tools :is(button, .ofc-filter) { white-space: nowrap; }
  .ofc-mail__tools .ofc-filter { flex: 1 1 100%; width: auto; }
}
@media (max-width: 62rem) {
  #ads .ofc-rail__nav > :is(.ofc-side__a, .ofc-side__g) { display: none; }
  #ads .ofc-side__sub { display: contents; }
  #ads .ofc-side__sg { display: none; }
  #ads .ofc-side__sub > .ofc-side__a { display: inline-flex; }
}

/* The sign-out in the rail foot is the one on wide screens; the header carries
   the other, for when the foot is hidden. */
/* THE CHROME'S SIGN OUT, AND WHO ELSE IS OFFERING ONE.
 *
 * Above 62rem the rail is a column with its own Sign out in the foot, so the
 * top-bar one is redundant and hidden. Below that the rail collapses to a
 * strip with no foot, so the top-bar one is the only chrome copy and stays.
 *
 * EXCEPT ON SETTINGS, which offers Sign out inside its own card because that
 * is what the card is for — "this session, and what this screen cannot tell
 * you". At phone width that made TWO identical pills visible a few hundred
 * pixels apart, which is the same defect this project already shipped once
 * when `#office .ofc-ghost` beat `.ofc-top__signout { display: none }` on
 * specificity. Found this time by sweeping every section at both widths and
 * counting visible controls by label, not by looking at a screenshot — two
 * pills that far apart do not read as a duplicate until you count them.
 *
 * The desktop pair — rail foot and settings card — is left alone. They are
 * persistent chrome and page content, at opposite corners, which is the
 * ordinary shape of a settings page. */
@media (min-width: 62.0625rem) { #office .ofc-top__signout { display: none; } }
#office[data-view="settings"] .ofc-top__signout { display: none; }

@media (prefers-reduced-motion: reduce) {
  :is(#office, #portal, #ebay, #ads) *,
  :is(#office, #portal, #ebay, #ads) *::before,
  :is(#office, #portal, #ebay, #ads) *::after { transition: none !important; }
}

/* ==========================================================================
   13. THE OWNER SCREEN AND THE CUSTOMER PORTAL

   Both `<link>` this stylesheet and build themselves out of its classes. The
   rewrite above gave the office a rail, a header and a router; these two have
   none of that and should not — the owner screen is one long read, the portal
   is one customer looking at one project. What they share is the MATERIAL:
   the same glass, the same ink, the same controls, the same density.

   Every class below is one those two pages were already using and the rewrite
   dropped. They are restored as the new design, not as the old one.
   ========================================================================== */

/* ---- the page header ----
   A masthead is wrong for the office, which is why it became a rail — but it
   is right here: neither page has sections to move between, so the header's
   whole job is to say whose screen this is and offer the way out.

   ONE HAIRLINE UNDER IT, nothing else — the same rule the office's top bar
   gets. A masthead that is also a floating panel is two ideas. */
.ofc-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding: 0.9rem 1.1rem;
  margin-bottom: 1.1rem;
  padding-inline: 0;
  border-bottom: 1px solid var(--o-line-2);
}

.ofc-head__id { display: flex; align-items: center; gap: 0.75rem; min-width: 0; }
.ofc-head h1 {
  margin: 0;
  font-family: var(--display);
  font-size: var(--o-t-xl);
  font-weight: 750;
  letter-spacing: -0.018em;
  line-height: 1.15;
}
.ofc-head__id p {
  margin: 0.1rem 0 0;
  color: var(--o-faint);
  font-size: var(--o-t-sm);
  font-weight: 500;
}

/* The header's own links and buttons. `.ofc-nav` was the name before the
   office grew a rail, and it stays the name here — a class is an API, and
   these two pages are its callers. */
.ofc-nav { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.ofc-who { color: var(--o-faint); font-size: var(--o-t-sm); font-weight: 500; }

/* The header logo is the one place a 40px mark is right — it is the only
   brand element on the page, where the rail's 30px sits above a wordmark. */
.ofc-head .ofc-logo { width: 38px; height: 38px; }

/* ---- the lede band ----
   One number and the decision it implies, at the top of the screen: lien
   clocks needing an answer on the owner screen, the project's stage on the
   portal. The office's equivalent is `.ofc-stat--hero` and they are
   deliberately the same figure size — it is the same idea. */
.ofc-lede {
  display: flex;
  align-items: center;
  gap: 1.1rem;
  padding: 0.95rem 1.2rem;
  margin-bottom: 1.1rem;
  border-radius: var(--o-r-lg);
  border: 1px solid var(--o-line-2);
  /* The one place a colour edge earns its keep: this band exists to say
     something needs a decision today. One 2px rule, not a tinted panel. */
  border-left: 2px solid var(--o-warn);
}

.ofc-lede__n {
  margin: 0;
  font-family: var(--display);
  font-size: var(--o-t-hero);
  font-weight: 750;
  line-height: 0.95;
  letter-spacing: -0.03em;
  /* Proportional figures: tabular gives every digit the width of a zero, which
     at 52px makes a number like 121 read loose. Tabular is for COLUMNS. */
  color: var(--o-warn-ink);
}
.ofc-lede__label { margin: 0; font-size: var(--o-t-lg); font-weight: 700; color: var(--o-text); }
.ofc-lede__sub { margin: 0.15rem 0 0; color: var(--o-faint); font-size: var(--o-t-sm); font-weight: 500; }

/* CLEAR IS ITS OWN STATE AND IS SAID, not left as an absence — and it is worth
   a line rather than a headline, because "nothing needs you" is good news and
   good news does not need 52 pixels. */
.ofc-lede.is-clear {
  padding: 0.6rem 1rem;
  border-left-color: var(--o-ok-mark);
}
.ofc-lede.is-clear .ofc-lede__n { font-size: var(--o-t-lg); color: var(--o-ok); }
.ofc-lede.is-clear .ofc-lede__label { font-size: var(--o-t-sm); font-weight: 650; }

/* ---- the tally ----
   A TALLY SHOWS WHAT EXISTS; the zeros go in a sentence underneath. Seventeen
   equal tiles, fifteen of them reading 0, is what the owner called amateur on
   the office dashboard — the office answered it with a stacked bar, and these
   two pages answer it with tiles that size to their own labels. A tile is only
   here because there is something in it. */
.ofc-tally {
  list-style: none;
  margin: 0 0 0.75rem;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}
.ofc-tally li {
  display: flex;
  align-items: baseline;
  gap: 0.45rem;
  padding: 0.35rem 0.65rem;
  border-radius: var(--o-r-sm);
  background: var(--o-inset);
  border: 1px solid var(--o-line);
}
/* The number and its name on one line, so a long stage name simply makes a
   longer tile instead of overrunning its neighbour. */
.ofc-tally b {
  font-family: var(--display);
  font-size: var(--o-t-lg);
  font-weight: 750;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--o-text);
}
.ofc-tally li span {
  font-size: var(--o-t-xs);
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--o-faint);
  white-space: nowrap;
}
/* The zeros, as one quiet line rather than fifteen loud boxes. */
.ofc-tally__none {
  flex: 1 1 100%;
  padding: 0.2rem 0 0;
  background: none;
  box-shadow: none;
  color: var(--o-faint);
  font-size: var(--o-t-sm);
  font-weight: 500;
}
.ofc-tally li.is-zero { opacity: 0.45; }

/* ---- a plain stacked list ----
   The owner's released-waiver rows and the portal's invoices and permits: a
   line of `<b><span><em>`, read down. Not the office's `.ofc-feed`, which is a
   three-column row built around a monogram. */
.ofc-list { list-style: none; margin: 0.6rem 0 0; padding: 0; display: grid; gap: 0.35rem; }
.ofc-list li {
  display: grid;
  gap: 0.1rem;
  padding: 0.5rem 0.7rem;
  border-radius: var(--o-r-sm);
  background: var(--o-inset);
  border: 1px solid var(--o-line);
  /* A marker, not a state, so it is a neutral edge rather than the accent:
     a column of white rules reads as emphasis on every row, i.e. on none. */
  border-left: 2px solid var(--o-line-3);
}
.ofc-list b { font-size: var(--o-t-sm); font-weight: 700; color: var(--o-text); }
.ofc-list span { font-size: var(--o-t-sm); font-weight: 500; color: var(--o-dim); }
.ofc-list em {
  font-style: normal;
  font-size: var(--o-t-xs);
  font-weight: 650;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--o-faint);
}

/* ---- the full-width card ----
   `--wide` is what owner.html and portal.html say; the office says `--half`,
   `--third` and `--twothirds`. Both spellings land on the same 12-column grid. */
.ofc-card--wide { grid-column: 1 / -1; }

/* The owner screen is a single column of wide cards on any screen that can
   hold two, so its grid does not need the office's twelve tracks — but it uses
   the same class, so the tracks are what it gets, and `--wide` is what makes
   that correct rather than accidental. */
#portal .ofc-grid { gap: 0.9rem; }


/* ==========================================================================
   14. PRICING AN ESTIMATE  (roadmap phase 1)

   The catalogue measured 0% reachable — the only area in the schema with no
   reader at all — and that one gap is why every estimate in this system is
   unpriced. Nothing below needed a migration: 0004 granted the office insert
   and update on estimate_lines the day it created the table.
   ========================================================================== */

/* The totals strip. Four figures across the top of the pane, divided by the
   gap over a line-coloured ground — the same instrument treatment as the
   dashboard's stat row, at a smaller size. */
.ofc-tot {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
  gap: 1px;
  margin: 0.85rem 0;
  background: var(--o-line-2);
  border: 1px solid var(--o-line-2);
  border-radius: var(--o-r);
  overflow: hidden;
}
.ofc-tot > div { background: var(--o-bg); padding: 0.6rem 0.75rem; }
.ofc-tot dt {
  margin: 0 0 0.15rem;
  font-size: var(--o-t-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--o-faint);
}
.ofc-tot dd {
  margin: 0;
  font-family: var(--display);
  font-size: var(--o-t-xl);
  font-weight: 750;
  letter-spacing: -0.02em;
  line-height: 1.05;
  color: var(--o-text);
}
.ofc-tot dd small {
  display: block;
  margin-top: 0.1rem;
  font-family: var(--body);
  font-size: var(--o-t-xs);
  font-weight: 500;
  letter-spacing: 0;
  color: var(--o-faint);
}
/* A margin that has gone negative is not a small number, it is a different
   fact. It is the one figure here allowed a colour. */
.ofc-tot dd.is-loss { color: var(--o-warn-ink); }

/* ---- the lines ---- */
.ofc-lines { width: 100%; border-collapse: collapse; font-size: var(--o-t-sm); }
.ofc-lines th {
  position: sticky;
  top: 0;
  z-index: 1;
  text-align: left;
  padding: 0.35rem 0.5rem;
  background: var(--o-bg);
  border-bottom: 1px solid var(--o-line-2);
  color: var(--o-faint);
  font-size: var(--o-t-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
}
.ofc-lines td {
  padding: 0.35rem 0.5rem;
  border-bottom: 1px solid var(--o-line);
  color: var(--o-dim);
  font-weight: 500;
  vertical-align: top;
}
.ofc-lines tbody tr:hover td { background: var(--o-hover); }
.ofc-lines td:first-child { color: var(--o-text); font-weight: 600; }
.ofc-lines td.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* A LINE NOBODY HAS PRICED IS NOT A LINE WORTH ZERO, and it must not read as
   one — the same rule the variance report follows with unpaired clock events. */
.ofc-lines td.is-unpriced { color: var(--o-warn-ink); font-style: italic; }
.ofc-lines td.is-overdue { color: var(--o-bad); font-style: italic; }
.ofc-lines tr.is-optional td:first-child::after {
  content: 'optional';
  display: inline-block;
  margin-left: 0.45rem;
  padding: 0.02rem 0.35rem;
  border-radius: var(--o-pill);
  background: var(--o-inset);
  color: var(--o-faint);
  font-size: var(--o-t-xs);
  font-weight: 650;
  font-style: normal;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.ofc-lines td.act { width: 1%; text-align: right; }

/* ---- the catalogue picker ---- */
.ofc-pick { display: grid; gap: 0.4rem; margin-top: 0.5rem; }
.ofc-pick__results {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 14rem;
  overflow-y: auto;
  display: grid;
  gap: 1px;
  scrollbar-color: var(--o-line-2) transparent;
}
.ofc-pick__results li { display: grid; }
.ofc-pick__b {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 0.2rem 0.75rem;
  width: 100%;
  min-height: 0;
  padding: 0.4rem 0.55rem;
  border: 0;
  border-radius: var(--o-r-sm);
  background: transparent;
  background-image: none;
  box-shadow: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.ofc-pick__b:hover { background: var(--o-hover); }
.ofc-pick__b:active { transform: none; }
.ofc-pick__b b { font-size: var(--o-t-sm); font-weight: 650; color: var(--o-text); }
.ofc-pick__b em {
  grid-column: 1 / -1;
  font-style: normal;
  font-size: var(--o-t-xs);
  font-weight: 500;
  color: var(--o-faint);
}
.ofc-pick__b span {
  font-size: var(--o-t-sm);
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  color: var(--o-dim);
  white-space: nowrap;
}
/* An item with no cost on file is the one thing in this list that must not
   look like a price of nothing. */
.ofc-pick__b span.is-unpriced { color: var(--o-warn-ink); font-weight: 500; }

/* ---- the estimate row in the list ---- */
.ofc-est__n { font-size: var(--o-t-sm); font-weight: 700; color: var(--o-text); font-variant-numeric: tabular-nums; }
.ofc-est__who {
  grid-column: 1 / -1;
  font-size: var(--o-t-sm);
  font-weight: 500;
  color: var(--o-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ofc-est__meta {
  grid-column: 1 / -1;
  margin-top: 0.15rem;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: var(--o-t-xs);
  font-weight: 650;
  color: var(--o-faint);
}

/* The pane's own header: what this document is, and what state it is in. */
.ofc-est__h { margin: 0 0 0.1rem; font-family: var(--display); font-size: var(--o-t-xl); font-weight: 750; letter-spacing: -0.018em; }
.ofc-est__sub { margin: 0 0 0.5rem; font-size: var(--o-t-sm); color: var(--o-faint); }
.ofc-est__bar { display: flex; align-items: center; flex-wrap: wrap; gap: 0.4rem; margin-bottom: 0.5rem; }

/* The row of things that can happen to this estimate version next. Spaced off
   the table above it, because "Send" is the one irreversible control on this
   screen and it must not sit flush against the last line of data. */
.ofc-actions--next { margin-top: 0.8rem; }

/* ---- the change-order chain --------------------------------------------
   A change order is an AMENDMENT, so it is drawn as a block hanging off the
   contract rather than a table row: it carries prose (what changed), money and
   time, and a row would flatten the first into a cell nobody reads. */
.ofc-co {
  display: grid;
  gap: 0.3rem;
  margin: 0.5rem 0 0;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--o-line-2);
  border-radius: var(--o-r);
}
.ofc-co__h { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.ofc-co__h b { font-size: var(--o-t-sm); font-weight: 750; color: var(--o-text); }
.ofc-co__n {
  margin-left: auto;
  font-family: var(--display);
  font-size: var(--o-t-lg);
  font-weight: 750;
  font-variant-numeric: tabular-nums;
  color: var(--o-text);
}
.ofc-co__scope { margin: 0; font-size: var(--o-t-sm); color: var(--o-dim); line-height: 1.5; }
.ofc-co__meta {
  margin: 0;
  font-size: var(--o-t-xs);
  font-weight: 650;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--o-faint);
}
.ofc-co .ofc-actions { margin-top: 0.3rem; }

/* A SENTENCE inside a roster row, not a label.
   `.ofc-co__meta` uppercases, which is right for "CSLB LICENCE IS EXPIRED" and
   wrong for the two paragraphs the Subcontracts card has to say — a statute
   explained in twenty-six words of block capitals is a statute nobody finishes
   reading, which defeats the point of saying it before the database does.
   Same faint ink, same size; sentence case and normal tracking. */
.ofc-co__why {
  display: block;
  margin: 0.25rem 0 0;
  font-size: var(--o-t-xs);
  line-height: 1.45;
  color: var(--o-faint);
  text-wrap: pretty;
}

/* One date, so one column — the change-order card is already narrow and the
   default two-column field grid would leave the box stranded beside a gap. */
.ofc-co__start {
  grid-template-columns: minmax(0, 12rem);
  margin-top: 0.6rem;
}

/* THE UNSIGNED CASE IS NOT AN OBSERVATION. Faint ink is right for "signed
   first, which is the order the statute requires" and wrong for "nothing may
   start on this — work before the signature cannot be billed", which is money
   the owner will not recover. Same rule `.ofc-note.is-warn` follows, and the
   same tokens, because a second warning colour is how a palette stops meaning
   anything. */
.ofc-co__why.is-warn {
  padding: 0.55rem 0.75rem;
  margin-top: 0.5rem;
  border-radius: var(--o-r-sm);
  background: var(--o-warn-bg);
  border-left: 2px solid var(--o-warn);
  color: var(--o-warn-ink);
}

/* A qualifier under a figure in a table cell — "$450 disputed" beneath the
   amount it is part of. Without `display:block` it ran straight on from the
   number and read as one value: "$3,100$450 disputed".
 *
 * IT WRAPS. This carried `white-space: nowrap`, which is right for a two-word
 * qualifier under a number and catastrophic for a sentence: the lien-waiver
 * exposure column put one explanation on one line, forced the cell to 941px,
 * the table to 1463px inside an 1100px card, and clipped every explanation
 * mid-word — "if that money never ar…". Several cells already carried
 * sentences and were one narrower card away from the same thing.
 *
 * Numbers still do not wrap: `.ofc-lines td.num` sets nowrap on the cell
 * itself, which is where it belongs — a figure broken across two lines is
 * unreadable, a sentence broken across two lines is a sentence. */
.ofc-lines td small {
  display: block;
  margin-top: 0.1rem;
  font-size: var(--o-t-xs);
  font-weight: 500;
  color: var(--o-faint);
  line-height: 1.35;
  /* Long enough for a clause, short enough that the column cannot run away. */
  max-width: 34rem;
  overflow-wrap: anywhere;
}
/* A qualifier under a FIGURE is short by nature and reads better on one line. */
.ofc-lines td.num small { white-space: nowrap; max-width: none; }

/* UNKNOWN IS A THIRD STATE, and it must look like neither of the other two.
   A refused read wearing the warn colour says "people are waiting"; wearing
   the clear colour says "nobody is". Both are claims the screen cannot make,
   so the figure goes to the muted ink and the dot goes grey. */
.ofc-stat--hero.is-unknown .ofc-stat__n { color: var(--o-faint); }
.ofc-stat--hero.is-unknown .ofc-stat__k { color: var(--o-faint); }
.ofc-stat--hero.is-unknown .ofc-stat__k::before { background: var(--o-neutral); }

/* NONE IS NOT ATTENTION. Zero subscribers with nothing wrong was drawn as the
   52px amber hero, and amber means "needs you" (review, 2026-09-28). A count
   of nobody is quiet: faint figure, grey dot, the sentence says it. */
.ofc-stat--hero.is-none .ofc-stat__n { color: var(--o-faint); font-size: var(--o-t-num); }
.ofc-stat--hero.is-none .ofc-stat__k { color: var(--o-faint); }
.ofc-stat--hero.is-none .ofc-stat__k::before { background: var(--o-neutral); }

/* A FIGURE THAT IS WORDS. "None" where the tile used to print $0: no plan
   billing through Stripe is not revenue of zero. Set smaller than a number
   so it never reads as one. */
.ofc-stat__n.is-words {
  font-size: var(--o-t-xl);
  font-weight: 650;
  letter-spacing: -0.01em;
  color: var(--o-dim);
}

/* An expansion header: the row the CUSTOMER sees, above the components the
   office sees. Not a heavier row — a quieter one, because the components under
   it are the data and this is a label for them. */
.ofc-lines__group td {
  padding-top: 0.7rem;
  border-bottom: 0;
  color: var(--o-text);
  font-weight: 700;
}
.ofc-lines__group td small {
  display: block;
  margin-top: 0.1rem;
  font-size: var(--o-t-xs);
  font-weight: 500;
  color: var(--o-faint);
}
/* Its components are indented so the recipe reads as belonging to it. */
.ofc-lines tr.is-expansion td:first-child { padding-left: 1.4rem; color: var(--o-dim); }

/* ==========================================================================
   15. KEEPING THE CATALOGUE CURRENT

   A CONTROL INSIDE A TABLE CELL IS A DIFFERENT PROBLEM FROM A CONTROL IN A
   FORM. The form control has a label above it, a whole row of width, and one
   job. This one sits in a column beside six others, is repeated once per row,
   and must not make the table wider than the screen — so it is sized to the
   number it holds rather than to a comfortable minimum.
   ========================================================================== */

.ofc-mini {
  min-height: 26px;
  padding: 0.15rem 0.45rem;
  border: 1px solid var(--o-line-3);
  border-radius: var(--o-r-sm);
  background: var(--o-inset);
  color: var(--o-text);
  font: 600 var(--o-t-xs)/1.2 var(--body);
}
/* SIZED TO THE FIGURE, not to a default input width. A text input defaults to
   about twenty characters, and seven of those across one row is a table that
   scrolls sideways on a laptop for no reason — the widest thing any of these
   holds is `12345.67`. */
input.ofc-mini { width: 6.5rem; max-width: 100%; }
input.ofc-mini.num { text-align: right; font-variant-numeric: tabular-nums; }
button.ofc-mini {
  display: block;
  margin-top: 0.25rem;
  border-radius: var(--o-pill);
  cursor: pointer;
}
button.ofc-mini:hover { background: var(--o-hover); color: var(--o-text); }
button.ofc-mini:disabled { opacity: 0.5; cursor: default; }
.ofc-mini:focus-visible {
  outline: 2px solid var(--o-accent);
  outline-offset: 1px;
  border-color: var(--o-line-4);
}

/* RETIRED IS NOT DELETED, and it must not read as an error either. The row is
   still true — it is what a past estimate was priced from — so it recedes
   rather than going amber. Amber is reserved for a number costing money now. */
.ofc-lines tr.is-retired td { color: var(--o-faint); }
.ofc-lines tr.is-retired td:first-child { color: var(--o-dim); font-weight: 500; }

/* The standalone version of the checkbox row. `.ofc-fields label.ofc-check`
   only applies inside a field grid; this one sits alone under a card bar. */
label.ofc-check--loose {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 0.6rem;
  font-size: var(--o-t-sm);
  font-weight: 600;
  color: var(--o-dim);
  cursor: pointer;
}
label.ofc-check--loose input {
  width: 1.05rem;
  height: 1.05rem;
  accent-color: var(--o-accent);
  flex: 0 0 auto;
}

/* A RETIRED RECIPE IS STILL TRUE — it is what past estimates were expanded
   from — so it recedes rather than going amber, the same rule the retired
   catalogue row follows. */
.ofc-roster li.is-retired { color: var(--o-faint); }
.ofc-roster li.is-retired > .ofc-co__meta { color: var(--o-faint); }


/* A DEAD CONTROL HAS TO LOOK DEAD. Choosing "lost" disables the revisit date,
   and the box was styled identically to a live one — so it read as a field that
   simply would not open, which is indistinguishable from a broken page. */
.ofc-fields input:disabled,
.ofc-fields select:disabled,
.ofc-fields textarea:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* ---- the CRM board card, which carried a name and an age and nothing else -- */
.ofc-board__what {
  display: block;
  margin-top: 0.3rem;
  font-size: var(--o-t-xs);
  line-height: 1.4;
  color: var(--o-dim);
}
.ofc-board__reach {
  margin: 0.35rem 0 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.15rem 0.6rem;
  font-size: var(--o-t-xs);
}
.ofc-board__reach a { color: var(--o-text); text-decoration: none; border-bottom: 1px solid var(--o-line-2); }
.ofc-board__reach a:hover { border-bottom-color: var(--teal); }

/* ---- the outcome, which the board never drew ------------------------------
   MONEY IS THE LOUDEST THING ON A WON CARD, because it is the only figure on
   this screen that is revenue. It gets weight and the full text colour, which
   nothing else on the card has — the name is bold, but this is bold AND
   bright, and there is exactly one per card so it cannot become noise. */
.ofc-board__money {
  display: block;
  margin-top: 0.4rem;
  font-size: var(--o-t-sm);
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  color: var(--o-text);
}
/* AND THE ABSENCE OF IT IS A DIFFERENT THING FROM A SMALL NUMBER. Drawn as a
   prompt rather than as a figure: no tabular numerals, no weight, and the
   dimmed colour the rest of the card's secondary text uses, so the eye reads
   it as "still to do" rather than misreading it as an amount. */
.ofc-board__money--none {
  font-weight: 400;
  font-style: italic;
  color: var(--o-dim);
}
.ofc-board__why {
  display: block;
  margin-top: 0.4rem;
  font-size: var(--o-t-xs);
  color: var(--o-dim);
}
/* The lost reason reads as a label rather than as more description, so it is
   set apart from `__what` above it by a rule rather than by size alone. */
.ofc-board__why::before {
  content: "Lost: ";
  color: var(--o-dim-2, var(--o-dim));
}
/* THE COLUMN TOTAL sits under the heading and above the cards, so it is read
   before the column rather than discovered after scrolling it. */
.ofc-board__sum {
  margin: 0 0 0.5rem;
  font-size: var(--o-t-xs);
  font-variant-numeric: tabular-nums;
  color: var(--o-text);
}
.ofc-board__sum .ofc-dim { font-variant-numeric: normal; }
/* "AT LEAST" IS A QUALIFIER, NOT PART OF THE FIGURE, so it takes the dim step
   and leaves the money the brightest thing on the line. It sits INSIDE the
   total rather than in a note underneath on purpose: the truncation is already
   stated on the card's state line, and that is exactly where it failed to be
   read — a caveat one element away from the number it qualifies is a caveat
   nobody connects to it. `--o-dim` rather than `--o-dim-2`, which is not
   defined anywhere and only ever resolves to its fallback. */
.ofc-board__atleast {
  font-variant-numeric: normal;
  color: var(--o-dim);
}

/* PARKED IS QUIET, DUE IS NOT. A revisit date in the future is a note; one that
   has arrived is the enquiry asking to be picked up today, which is the single
   thing this board exists to surface. */
.ofc-board__revisit {
  display: block;
  margin-top: 0.35rem;
  font-size: var(--o-t-xs);
  color: var(--o-faint);
}
.ofc-board__revisit.is-due { color: var(--o-warn-ink); font-weight: 650; }
.ofc-board__card.is-due { border-left: 2px solid var(--o-warn); }


/* WHAT THE PICKER IS NOT SHOWING. Twenty results out of what was found, out of
   what the catalogue holds — a list that shows some of the rows must say so,
   and the same rule the tables follow applies to a dropdown of results. */
.ofc-pick__more {
  padding: 0.4rem 0.6rem;
  font-size: var(--o-t-xs);
  color: var(--o-faint);
  border-top: 1px solid var(--o-line);
}


/* AN EDITABLE FIGURE IN A TABLE CELL. It has to look typeable without turning
   a dense table into a form: no filled well, just the hairline underneath that
   says the number can be touched. `text-align: right` because it sits in a
   numeric column and a right-aligned column with one left-aligned input in it
   reads as a misprint. */
.ofc-inline--num {
  width: 100%;
  min-width: 3.5rem;
  max-width: 6rem;
  padding: 0.15rem 0.25rem;
  text-align: right;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--o-line-2);
  border-radius: 0;
  color: var(--o-text);
  font: 500 var(--o-t-sm)/1.3 var(--body);
}
.ofc-inline--num::placeholder { color: var(--o-faint); }
.ofc-inline--num:hover { border-bottom-color: var(--o-line-3, var(--o-line-2)); }
.ofc-inline--num:focus {
  outline: none;
  border-bottom-color: var(--teal);
  background: var(--o-inset);
}
.ofc-inline--num:disabled { opacity: 0.5; }


/* ---- the mailbox addresses -----------------------------------------------
   A row of what mail has actually arrived for. Pills, like every other control
   on this screen, and the count carries the weight rather than a colour: an
   address with something waiting says so in words. */
/* A VIEW BAR, NOT A ROW OF BUBBLES (Phase VI). The same buttons, the same
   aria-pressed, the same counts — drawn as an integrated tab strip on a
   hairline: the current view underlined in the accent, the rest plain text.
   Pills stay for what they are for: a tag or a state on a row. */
.ofc-addr {
  display: flex;
  flex-wrap: wrap;
  gap: 0 1.35rem;
  margin: 0.1rem 0 1rem;
  border-bottom: 1px solid var(--o-line);
}
:is(#office, #portal, #ebay, #ads) .ofc-addr__b {
  position: relative;
  display: inline-flex;
  align-items: baseline;
  gap: 0.4rem;
  min-height: 0;
  margin: 0 0 -1px;
  padding: 0.55rem 0 0.6rem;
  border: 0;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  color: var(--o-faint);
  font: 550 var(--o-t-sm)/1.2 var(--body);
  cursor: pointer;
  transition: color .12s ease, border-color .12s ease;
}
:is(#office, #portal, #ebay, #ads) .ofc-addr__b:hover { color: var(--o-text); background: transparent; }
:is(#office, #portal, #ebay, #ads) .ofc-addr__b.is-on {
  background: transparent;
  border-bottom-color: var(--o-accent);
  color: #FFFFFF;
}
.ofc-addr__t { letter-spacing: 0; }
.ofc-addr__n { color: var(--o-faint); font-weight: 500; font-variant-numeric: tabular-nums; }
.ofc-addr__b.is-on .ofc-addr__n { color: var(--o-dim); }
.ofc-addr__n.is-unread { color: var(--o-warn-ink); font-weight: 650; }


/* ---- one enquiry, opened -------------------------------------------------
   The board card is now a control, so it has to say so on hover and focus. */
.ofc-board__card[role="button"] { cursor: pointer; }
.ofc-board__card[role="button"]:hover { border-color: var(--o-line-4); }
.ofc-board__card[role="button"]:focus-visible {
  outline: 2px solid var(--o-accent);
  outline-offset: 2px;
}
.ofc-board__card.is-on { background: var(--o-press); }

.ofc-lead__h { margin: 0 0 0.2rem; font-size: var(--o-t-lg); }
.ofc-lead__scope {
  margin: 0 0 0.9rem;
  color: var(--o-dim);
  line-height: 1.5;
  text-wrap: pretty;
}
.ofc-lead__sub {
  margin: 1.1rem 0 0.35rem;
  font-size: var(--o-t-xs);
  font-weight: 750;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--o-faint);
}
.ofc-lead__reach {
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem 0.9rem;
  margin: 0.6rem 0 0;
  font-size: var(--o-t-sm);
}
.ofc-lead__reach a { color: var(--o-text); text-decoration: none; border-bottom: 1px solid var(--o-line-2); }
.ofc-lead__reach a:hover { border-bottom-color: var(--teal); }

/* WHAT SOMEBODY WROTE, in their own words — so it is set apart from the
   screen's own sentences rather than reading as more chrome. */
.ofc-lead__notes {
  margin: 0;
  padding: 0.6rem 0.8rem;
  border-left: 2px solid var(--o-line-2);
  background: var(--o-inset);
  border-radius: 0 var(--o-r-sm) var(--o-r-sm) 0;
  color: var(--o-text);
  line-height: 1.55;
  white-space: pre-wrap;
  text-wrap: pretty;
}

.ofc-journey { margin: 0.5rem 0 0; padding: 0; list-style: none; }
.ofc-journey li {
  display: flex;
  flex-wrap: wrap;
  gap: 0.15rem 0.6rem;
  align-items: baseline;
  padding: 0.4rem 0;
  border-top: 1px solid var(--o-line);
  font-size: var(--o-t-xs);
}
.ofc-journey__meta { color: var(--o-faint); }


/* ---- "Other" as a disclosure ---------------------------------------------
   Fourteen unfinished sections sitting between the five that are worked daily
   and the two system screens turned the rail back into a scroll — the thing
   the grouping was added to fix. It collapses, and the caret is the only
   moving part. */
button.ofc-side__g--soon {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  width: 100%;
  background: none;
  cursor: pointer;
  text-align: left;
  font: inherit;
  font-size: var(--o-t-xs);
  font-weight: 750;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--o-faint);
}
button.ofc-side__g--soon:hover { color: var(--o-dim); }
button.ofc-side__g--soon:focus-visible {
  outline: 2px solid var(--o-accent);
  outline-offset: 2px;
  border-radius: var(--o-r-sm);
}
.ofc-side__caret {
  width: 12px;
  height: 12px;
  flex: none;
  transition: transform 0.16s var(--ease, ease);
}
button.ofc-side__g--soon[aria-expanded="true"] .ofc-side__caret { transform: rotate(90deg); }
/* THE COUNT IS WHAT MAKES A COLLAPSED GROUP HONEST. Hiding fourteen sections
   behind a word is fine; hiding them behind a word that does not say how many
   is a menu pretending the rest of the application is not there. */
.ofc-side__gn {
  margin-left: auto;
  font-weight: 500;
  letter-spacing: normal;
  text-transform: none;
  color: var(--o-faint);
}
.ofc-side__soon { display: contents; }
.ofc-side__soon[hidden] { display: none; }

@media (max-width: 62rem) {
  /* The rail is a horizontal strip here and a collapsed group would leave the
     unfinished tabs unreachable, so it is always open — the inline divider
     already marks where the finished work stops. */
  button.ofc-side__g--soon { display: none; }
  .ofc-side__soon[hidden] { display: contents; }
}


/* AN ESTIMATE WHOSE DATE HAS PASSED. The chip printed the expiry the same way
   whether it was next month or last year, so a price the customer can no
   longer hold the company to looked identical to one still open. */
.ofc-chip--warn {
  background: var(--o-warn-bg);
  border-color: var(--o-warn);
  color: var(--o-warn-ink);
}


/* The read-only notice sits above the address row and under the sub-line, so
   it reads as part of the card's explanation rather than as a warning. */
.ofc-mail__ro { margin: 0 0 0.6rem; }

/* ==========================================================================
   THE ROUND — a list you work down, not a table you read
   ==========================================================================
   Owner, 2026-08-19: "organize client lists within neighborhood/locations
   together for easier sessions."

   NOT A TABLE, and that is the whole layout decision. Every other list on this
   screen is a table because every other list is READ — you scan a column and
   find a row. This one is WORKED: somebody goes down it in a truck, tapping
   each house as the bins come back. So the hit target is the row rather than a
   cell in it, the heading is a street rather than a column name, and the state
   that matters (due, done) is carried by the row itself.

   THE TICK IS A LABEL WRAPPING A CHECKBOX, so the whole of the name and the
   address is the target — 44px of it, which is what a thumb needs and what a
   16px checkbox is not. Nothing here draws its own box: the checkbox is the
   browser's, accent-coloured, because a re-drawn one is a control that stops
   announcing itself to a screen reader for a difference nobody asked for.
   ========================================================================== */

.ofc-round__g { margin-top: 1.1rem; }
.ofc-round__g:first-child { margin-top: 0.35rem; }

/* The street heading. A hairline under it rather than a filled bar: this is a
   divider inside a panel, which §5 says is one solid hex line. */
.ofc-round__gh {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  padding-bottom: 0.4rem;
  border-bottom: 1px solid var(--o-line);
}
.ofc-round__gh h3 {
  margin: 0;
  font-size: var(--o-t-md);
  font-weight: 700;
  letter-spacing: 0.01em;
  color: var(--o-text);
}
.ofc-round__gn {
  font-size: var(--o-t-xs);
  font-weight: 650;
  color: var(--o-faint);
  white-space: nowrap;
}

.ofc-round__l { list-style: none; margin: 0; padding: 0; }

.ofc-round__i {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem 0.85rem;
  padding: 0.5rem 0.6rem;
  margin: 0 -0.6rem;
  border-radius: var(--o-r-sm);
  border-bottom: 1px solid var(--o-line);
}
.ofc-round__i:last-child { border-bottom: 0; }
.ofc-round__i:hover { background: var(--o-hover); }

/* DUE IS MARKED ON THE EDGE, NOT AS A FILL. A filled row would be forty
   coloured bands on a busy morning, which is a list with no hierarchy at all;
   a 2px rule down the left says the same thing once per row and disappears the
   moment the row is ticked.

   A BORDER THAT IS ALWAYS THERE AND USUALLY TRANSPARENT, not a shadow and not
   a border added on the due rows only. design-check rejects `box-shadow` on
   any signed-in surface — the rule the owner ruled on twice — and adding the
   border only when due would move every row 2px sideways the moment it was
   ticked, on a list somebody is working down with their thumb. */
.ofc-round__i { border-left: 2px solid transparent; }
.ofc-round__i.is-due { border-left-color: var(--o-warn); }

.ofc-round__tick {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  flex: 1 1 15rem;
  min-width: 0;
  min-height: 44px;
  cursor: pointer;
}
.ofc-round__tick input[type="checkbox"] {
  width: 20px;
  height: 20px;
  flex: none;
  accent-color: var(--o-accent-deep);
  cursor: pointer;
}
.ofc-round__who { display: grid; gap: 0.1rem; min-width: 0; }
.ofc-round__who b {
  font-size: var(--o-t-sm);
  font-weight: 650;
  color: var(--o-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The address is the line that is actually navigated by, so it is not the
   quietest thing on the row — one ink step down, not two. */
.ofc-round__addr {
  font-size: var(--o-t-xs);
  color: var(--o-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ofc-round__facts {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.4rem 0.6rem;
  flex: 0 1 auto;
  margin-left: auto;
}
.ofc-round__plan { font-size: var(--o-t-xs); color: var(--o-faint); white-space: nowrap; }
.ofc-round__last { font-size: var(--o-t-xs); color: var(--o-dim); white-space: nowrap; }
/* Due is the one thing on the row that asks for action, and it takes the
   attention INK rather than the mark colour (text 11.75:1; the mark is for
   edges and dots). */
.ofc-round__last.is-due { color: var(--o-warn-ink); font-weight: 650; }

/* What the last tap did. Blank almost always, so it takes no space until it
   has something to say. */
.ofc-round__say {
  flex: 0 0 100%;
  font-size: var(--o-t-xs);
  color: var(--o-faint);
}
.ofc-round__say:empty { display: none; }

@media (max-width: 40rem) {
  .ofc-round__facts { margin-left: 0; flex: 1 1 100%; }
}

/* ---- can this client log in (0009) ---------------------------------------
   A WORD AND A COLOUR, and the colour is doing the same job as the word rather
   than replacing it — the states are read down a column at a glance, and
   "invited" against "has a login" is a distinction somebody has to be able to
   make without hovering. `is-unknown` is deliberately the dimmest: a read that
   failed must not look like an answer. */
.ofc-login {
  display: inline-block;
  font-size: var(--o-t-xs);
  padding: 0.12rem 0.45rem;
  border: 1px solid var(--o-line-2);
  border-radius: 999px;
  white-space: nowrap;
}
/* --o-ok was read here before it was declared anywhere, so "has a login"
   rendered in the fallback #2f7d55 at 4.19:1. Both states now take their
   MARK for the edge and their INK for the word. */
.ofc-login.is-live    { border-color: var(--o-ok-mark); color: var(--o-ok); }
.ofc-login.is-invited { border-color: var(--o-warn); color: var(--o-warn-ink); }
.ofc-login.is-lapsed  { border-color: var(--o-line-2); color: var(--o-dim); }
.ofc-login.is-none    { border: 0; padding-left: 0; color: var(--o-dim); }
.ofc-login.is-unknown { border: 0; padding-left: 0; color: var(--o-dim); opacity: 0.7; }

/* The Invite control sits AFTER the state rather than replacing it, so the
   cell still says what is true while offering the thing to do about it. */
.ofc-login + .ofc-linkish { margin-left: 0.5rem; }

.ofc-invite { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-top: 0.4rem; }
.ofc-invite__in {
  font: inherit;
  color: var(--o-text);
  background: var(--o-bg-2, transparent);
  border: 1px solid var(--o-line-3);
  border-radius: 6px;
  padding: 0.28rem 0.5rem;
  min-width: 20ch;
  flex: 1 1 20ch;
}
.ofc-invite__in:focus-visible { outline: 2px solid var(--o-accent); outline-offset: 1px; }
.ofc-invite__msg { margin: 0.35rem 0 0; font-size: var(--o-t-xs); color: var(--o-dim); max-width: 40ch; }

/* ---- "Client view" in the header ------------------------------------------
   A popover rather than a <select>, because the list is names and a select in
   a header reads as a filter for the screen behind it. Anchored to the button
   and right-aligned so it cannot run off the edge on a narrow window. */
.ofc-clientview { position: relative; display: inline-block; }
.ofc-clientview__menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 40;
  min-width: 15rem;
  max-width: min(24rem, 80vw);
  max-height: 60vh;
  overflow-y: auto;
  padding: 0.35rem;
  /* The popover level. It read an undeclared --o-bg-2 and so always painted
     its fallback, a lilac #14131a. */
  background: var(--o-raised);
  border: 1px solid var(--o-line-2);
  border-radius: 10px;
  box-shadow: 0 18px 40px -18px rgba(0, 0, 0, 0.8);
}
.ofc-clientview__menu a,
.ofc-clientview__menu p {
  display: block;
  margin: 0;
  padding: 0.5rem 0.6rem;
  border-radius: 6px;
  font-size: var(--o-t-sm);
  color: var(--o-text);
  text-decoration: none;
}
.ofc-clientview__menu p { color: var(--o-dim); }
/* Hover is the white step. Focus is the white ring as well as the step: it
   was a 14% violet wash with the outline removed, 1.16:1, which is a focus
   indicator nobody can see. */
.ofc-clientview__menu a:hover { background: var(--o-hover); }
.ofc-clientview__menu a:focus-visible {
  background: var(--o-press);
  outline: 2px solid var(--o-accent);
  outline-offset: -2px;
}
.ofc-clientview__menu a small { display: block; color: var(--o-dim); font-size: var(--o-t-xs); }


/* Project A, Phase 9: the one link to every client's Cloudflare infrastructure, drawn only where
   tkati-infra is configured (page-office.mjs drawClients). */
.ofc-infra-link{
  display:inline-flex; align-items:center; min-height:36px; white-space:nowrap;
  color:var(--ofc-text-2, #C8C8C8); text-decoration:none; border-bottom:1px solid rgba(255,255,255,.36);
  font:500 13px/1.4 "Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
}
.ofc-infra-link:hover{color:#FFFFFF}

/* PLANS (Phase I, milestone H): a plan's state is a word with a mark, never colour alone. */
.ofc-plan__st { display: inline-flex; align-items: center; gap: 0.4rem; white-space: nowrap; }
.ofc-plan__st::before { content: ''; flex: none; width: 0.45rem; height: 0.45rem; border-radius: 50%; border: 1px solid currentColor; }
.ofc-plan__st--bad { color: var(--o-bad); }
.ofc-plan__st--bad::before, .ofc-plan__st--attn::before { background: currentColor; }
.ofc-plan__st--attn { color: var(--o-warn); }
.ofc-plan__st--unknown { color: var(--o-neutral); }
#office #ofc-plans-sum .oh-kv { max-width: 34rem; margin: 0 0 1rem; }
/* A NAME IN A TABLE IS A LINK IN THE PAGE'S INK, not the browser's default blue and visited purple
   (the Plans and Clients lists open the client's record from the name). */
#office .ofc-lines td > a:not([class]),
#office .ofc-table td > a:not([class]) { color: var(--o-text); text-decoration: underline; text-decoration-color: var(--o-line-4);
  text-underline-offset: 3px; }
#office .ofc-lines td > a:not([class]):hover,
#office .ofc-table td > a:not([class]):hover { color: #fff; text-decoration-color: currentColor; }
#office #ofc-plans-body tr.is-target { background: var(--o-inset); box-shadow: inset 3px 0 0 var(--o-ink, #fff); }
#office #ofc-plans-body tr.is-target:focus { outline: none; }
#office #ofc-plans-body tr.is-target:focus-visible { outline: 2px solid #fff; outline-offset: -2px; }
#office #ofc-plans-sum .oh-kv--rec { margin-top: 0; border-top: 0; }
/* MILESTONE J: a request names its client (a link to their record) and its project, and offers the Mailbox. */
.ofc-req__link { color: var(--o-text); text-decoration: underline; text-decoration-color: var(--o-line-4); text-underline-offset: 3px;
  display: inline-block; min-height: 24px; line-height: 24px; }
.ofc-req__link:hover { color: #fff; text-decoration-color: currentColor; }
.ofc-req__write { min-height: 24px; padding: 0 0.6rem; border: 1px solid var(--o-line-3); border-radius: var(--o-pill);
  background: transparent; color: var(--o-text); font: 600 var(--o-t-xs)/1 var(--body, inherit); cursor: pointer; }
.ofc-req__write:hover { border-color: var(--o-line-4); }
.ofc-req__write:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
/* 0037: a project's next move, under its standing; a day that has passed is said ("was due") and marked. */
.ofc-jb-next { display: block; margin-top: 0.2rem; font-size: var(--o-t-xs); color: var(--o-dim); overflow-wrap: anywhere; }
.ofc-jb-next.is-late { color: var(--o-warn-ink); }
.ofc-jb-next.is-late::before { content: '● '; color: var(--o-warn); }
#office #ofc-jb-body tr.is-target { background: var(--o-inset); box-shadow: inset 3px 0 0 var(--o-ink, #fff); }
#office #ofc-jb-body tr.is-target:focus { outline: none; }
#office #ofc-jb-body tr.is-target:focus-visible { outline: 2px solid #fff; outline-offset: -2px; }
/* 0037: a request's due day, set in place; past it, said ("Was due") and marked. */
.ofc-req__due label { display: inline-flex; align-items: center; gap: 0.35rem; color: var(--o-dim); }
.ofc-req__due .ofc-input--sm { min-height: 28px; padding: 0 0.4rem; width: auto; }
.ofc-req__due.is-late label { color: var(--o-warn-ink); }
.ofc-req__due.is-late label::before { content: '●'; color: var(--o-warn); }
/* 0037: how long a project has been at its stage. */
.ofc-jb-age { display: block; margin-top: 0.15rem; font-size: var(--o-t-xs); color: var(--o-dim); }
/* A link inside a card's subtitle (Plans → Ads): the page's ink, underlined, a 24px target. */
#office .ofc-sub a:not([class]) { color: var(--o-text); text-decoration: underline; text-decoration-color: var(--o-line-4);
  text-underline-offset: 3px; display: inline-block; min-height: 24px; line-height: 24px; }
#office .ofc-sub a:not([class]):hover { color: #fff; text-decoration-color: currentColor; }

/* ON A PHONE, A LIST IS CARDS, NOT A TABLE THAT SCROLLS (the wall of 2026-09-29: Plans, Projects and
   Invoices pushed their state and next-step columns off a 390px screen). Each cell says its column's name
   (data-label, set from the header by page-office.mjs labelCells); a cell whose header has no words (an
   action) shows no name. The header stays for screen readers, visually hidden. */
@media (max-width: 640px) {
  #office :is(.ofc-lines, .ofc-table).is-cards { display: block; min-width: 0; }
  #office :is(.ofc-lines, .ofc-table).is-cards thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  #office :is(.ofc-lines, .ofc-table).is-cards tbody { display: block; }
  #office :is(.ofc-lines, .ofc-table).is-cards tr { display: block; padding: 0.55rem 0; border-top: 1px solid var(--o-line); }
  #office :is(.ofc-lines, .ofc-table).is-cards tbody tr:first-child { border-top: 0; }
  #office :is(.ofc-lines, .ofc-table).is-cards td { display: grid; grid-template-columns: 6.5rem minmax(0, 1fr); gap: 0.6rem; align-items: baseline;
    padding: 0.15rem 0; border: 0; text-align: left; white-space: normal; }
  #office :is(.ofc-lines, .ofc-table).is-cards td::before { content: attr(data-label); color: var(--o-faint); font-size: var(--o-t-xs);
    font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; }
  #office :is(.ofc-lines, .ofc-table).is-cards td:not([data-label]) { display: block; }
  #office :is(.ofc-lines, .ofc-table).is-cards td:not([data-label])::before { content: none; }
  #office :is(.ofc-lines, .ofc-table).is-cards td[colspan] { display: block; }
  #office :is(.ofc-lines, .ofc-table).is-cards tbody tr:hover td { background: transparent; }
  /* Every element in a named cell sits in the value column (Phase VI): a name
     and the line under it were being dealt into both columns of the card. */
  #office :is(.ofc-lines, .ofc-table).is-cards td[data-label] > * { grid-column: 2; justify-self: start; }
}
