/* Yarnball CDS 1.0.2 app surface extensions — ONLY tokens from the design system.
   Owned by the central design system under /_cds/. App code must NOT ship
   parallel stylesheets or invent colors/type outside tokens. */
body { margin: 0; }

/* Ben 2026-08-08: serif type across the entire product — every UI and display face.
   The ONLY exception is the Yarnball wordmark (.word), which keeps its original face.
   Token-level override so all var(--ui)/var(--display) consumers follow at once;
   the enclave theme's mono faces (defined per-theme, higher specificity) still win. */
:root {
  --ui: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua", Georgia, "Times New Roman", serif;
  --display: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua", Georgia, "Times New Roman", serif;
}
.word,
.yumyum-admin .brand .word {
  font-family: "Inter", "Helvetica Neue", -apple-system, system-ui, sans-serif;
}

/* R1 density fix (chat.js, both the empty-state and the real-conversation branch —
   see the comment above ChatScreen's return in chat.js for the full bk-8260f178
   history). Scoped to these two modifier classes ONLY: .yarnball-main itself (frozen,
   _ds_bundle.css) keeps justify-content:safe center for every other screen routed
   through it (Settings, History, Connections, Usage), which the centering genuinely
   helps. Chat specifically measured ~70% dead viewport at 1280x720 with content
   centered inside the full flex:1 height — this packs it from the top instead. No
   effect once real content overflows the container (flex-start === safe center once
   content is taller than available height), so long transcripts are unchanged. */
.yarnball-main-populated {
  justify-content: flex-start;
}
/* bk-yumi5-usage-security-density (2026-07-18): re-measured live at 1280x720 — the
   flex-start override above still left a 379.7px (52.7%) dead gap between the last
   starter-chip and the composer, because .yarnball-main-compact inherited the frozen
   .yarnball-main's flex:1 (_ds_bundle.css) — the box itself still claims the FULL
   remaining height under the top bar, so no justify-content value fixes it: centering
   split the void above+below, flex-start just collapsed it to one gap below the
   content and above the composer (which sits OUTSIDE .yarnball-main as a sibling, so
   nothing inside .yarnball-main can pull it closer). The only change that actually closes
   that gap is letting the box SHRINK to its real content height (short hero +
   starter-chips) so .compwrap — the very next flex child in .yarnball-shell's column —
   sits right below it. (An earlier attempt at flex:0 1 auto was reverted per the
   comment on .yarnball-main-compact in _ds_bundle.css; re-tried here as the SCOPED
   app-extensions.css override in the SAME direction with the density math re-checked:
   any leftover height now trails after the composer as ordinary bottom page margin,
   not a void wedged between two functional controls — that's the actual defect this
   closes, not a global "zero whitespace anywhere" requirement.) */
.yarnball-main-compact {
  flex: 1;
  justify-content: flex-start;
}
/* YUM-243: opt this scroll container into the browser's own scroll-anchoring assist
   (kept where content is actually appended below the viewport, which is exactly the
   growing-conversation case) as a native-layer complement to the atBottomRef guard in
   chat.js's ChatScreen — the JS guard is what stops the hard scrollTop jump on every
   streamed delta/re-render; this keeps sub-pixel layout shifts (e.g. a tool-step chip
   changing height) from nudging the view even between those JS-driven scroll writes. */
.yarnball-main-populated, .yarnball-main-compact {
  overflow-anchor: auto;
}

/* YUMI-B-012: serif body for AI-served content (Ben's dense/serif/no-symbols standing
   rule) — this must apply to every Yarnball chat reply, not a gated "gui-mode" ancestor class
   that nothing in the app ever sets (that gate made this rule dead: the live chat bubble
   rendered sans-serif Inter via --ui regardless). Selector matches the actual DOM ChatRow
   renders for assistant messages (className "row " + from, from === 'yarnball' — see
   public/_cds/1.0.2/_ds_bundle.js ChatRow()). Does not touch .row.me (the user's own text). */
.row.yarnball .bub {
  font-family: var(--font-body-serif);
  font-size: 14.5px;
  line-height: 1.4;
}
/* composer-chat: assistant rows flush-left, aligned with the composer */
.row.yarnball { justify-content: flex-start; }
.row.yarnball .col { align-self: flex-start; flex: 0 1 auto; }
.box-compact { gap: 4px; padding: 5px 5px 5px 10px; }
.box-compact .clip { width: 30px; height: 30px; }
.box-compact .send { width: 30px; height: 30px; }
@media (max-width: 720px) {
  .box { gap: 4px; padding: 5px 5px 5px 10px; }
  .box .clip { width: 30px; height: 30px; }
  .box .send { width: 30px; height: 30px; }
}
.gui-items {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  overflow: hidden;
  margin: 0 0 8px;
}
.gui-item {
  border-bottom: 1px solid var(--line-soft);
  background: var(--surface);
}
.gui-item:last-child { border-bottom: 0; }
.gui-item-hd {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  width: 100%;
  text-align: left;
  border: 0;
  background: transparent;
  padding: 7px 10px;
  cursor: pointer;
  font: inherit;
  color: var(--ink-2);
}
.gui-item-hd:hover { background: var(--surface-2); }
.gui-item.marked {
  outline: 1px solid var(--accent);
  outline-offset: -1px;
}
.gui-item-title {
  font-weight: 600;
  font-size: 13px;
  line-height: 1.25;
}
.gui-item-sub {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .03em;
  color: var(--faint);
  margin-top: 2px;
}
/* The body of a stream item — the few real rows a widget shows under its header
   (YUM-999 shipped the Email and Files widgets naming .gui-item-bd/.feed-* with nothing
   behind them, so the markup existed and the styling did not; authored here rather than
   stripped from the widgets, 2026-08-12). Indented to the header's text column so the
   rows read as belonging to it. */
.gui-item-bd {
  padding: 0 10px 8px 10px;
  border-top: 1px solid var(--line-soft);
}
.feed-list {
  list-style: none;
  margin: 0;
  padding: 6px 0 0;
  display: flex;
  flex-direction: column;
}
.feed-item {
  padding: 5px 0;
  border-bottom: 1px solid var(--line-soft);
  min-width: 0;
}
.feed-item:last-child { border-bottom: 0; }
.feed-item-title {
  font-size: var(--t-small);
  line-height: 1.3;
  color: var(--ink-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.feed-item-sub {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .03em;
  color: var(--faint);
  margin-top: 2px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.gui-item-note {
  padding: 0 10px 8px;
  display: flex;
  gap: 6px;
}
.gui-item-note input {
  flex: 1;
  border: 1px solid var(--line);
  border-radius: var(--r-xs);
  padding: 5px 8px;
  font-family: Georgia, serif;
  font-size: 12px;
}
.gui-markers {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  padding: 0 0 6px;
  font-family: var(--mono);
  font-size: 11px;
}
.gui-marker {
  border: 1px solid var(--line);
  background: var(--surface);
  padding: 3px 7px;
  border-radius: var(--r-pill);
  cursor: pointer;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.gui-marker:hover { background: var(--surface-2); }
.mode-toggle {
  appearance: none;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink-2);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .06em;
  text-transform: uppercase;
  padding: 6px 10px;
  border-radius: var(--r-pill);
  cursor: pointer;
  white-space: nowrap;
}
.mode-toggle.on {
  border-color: var(--ink);
  color: var(--ink);
  font-weight: 700;
}

/* Chat empty-state hero: tagline only, smaller than the default 40px Greeting */
.greet {
  font-size: 22px;
  line-height: 1.28;
  font-weight: 700;
  letter-spacing: -0.03em;
  margin: 10px 0 0;
  max-width: 22em;
}
@media (max-width: 720px) {
  .greet { font-size: 18px; line-height: 1.3; }
}
@media (max-width: 380px) {
  .greet { font-size: 16px; }
}

/* ITEM G (2026-07-24): the YUMI-R-064 floating "Feedback" pill overlapped the composer's
   send button (the mobile media-query lift below was a patch on that same defect, never a
   real fix) — removed entirely. Feedback is now a Settings root row (settings.js RootView)
   that opens the exact same feedback modal via YarnballApp.openModal('feedback'); no
   floating affordance anywhere in the shell any more. */

/* YUMI-R-065: small persistent alpha disclaimer on the main (home) page — dense one-line
   notice, tokens only. flex: 0 0 auto keeps it out of the shell column's flex-grow so the
   chat Composer's bottom-pin is unaffected. The link opens the feedback modal (Settings ->
   Feedback, since ITEM G / 2026-07-24 removed the floating Feedback affordance). */
.alpha-disclaimer {
  flex: 0 0 auto;
  padding: 4px 12px;
  border-bottom: 1px solid var(--line);
  background: var(--surface-2);
}
.alpha-disclaimer-text {
  margin: 0;
  font-size: 11px;
  line-height: 1.4;
  color: var(--mute);
}
.alpha-disclaimer-link {
  border: 0;
  background: transparent;
  padding: 0;
  font: inherit;
  font-weight: 600;
  color: var(--accent);
  text-decoration: underline;
  cursor: pointer;
}

/* Shared app-shell chrome (main.js) — moved off inline style props onto DS-owned
   classes, tokens only. This chrome wraps the top bar shown on every screen,
   including Projects (YUMI-B-003). */
.not-loaded-note {
  font-family: var(--mono);
  font-size: 13px;
  color: var(--mute);
}
.brand-home-link {
  display: flex;
  align-items: center;
  cursor: pointer;
}
.app-footer { flex: 0 0 auto; padding: 3px 12px; text-align: center; border-top: 1px solid var(--line); }
.app-footer-text { margin: 0; font-family: var(--mono); font-size: 11px; letter-spacing: .02em; color: var(--faint); }
/* C1 rework (Ben 2026-07-22): lock is now a standalone element, separate from the model
   pill, red, with clear spacing from it. */
.model-toggle-wrap {
  display: inline-flex;
  align-items: center;
  gap: 18px;
}
.model-lock-standalone {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border: none;
  background: transparent;
  color: var(--accent, #B23A34);
  cursor: pointer;
  padding: 0;
}
/* YUM-1470 (T8a): the lock's hover is the shared top-bar background highlight, declared once
   near the end of this file — no opacity fade here, so the icon keeps its red/green state
   colour while hovered. */
/* YUM-369: the shell adds .is-ok when exact-turn TEE evidence is authenticated or the
   latest live check verified every fact — the same scoped --trust-green the settings
   lock rows already use, never a new colour. */
.model-lock-standalone.is-ok { color: var(--trust-green); }
/* Compact pill TOGGLE SWITCH holding only the two models -- narrower than the old slider.
   Clicking either icon switches the model directly (no drag needed); the knob position
   reflects which side is current. Model identity/pricing is never painted as visible text --
   only via the native `data-tip` tooltip on hover (per-side, set in main.js). */
.model-toggle-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--line);
  background: var(--surface);
  border-radius: var(--r-pill);
  padding: 4px 8px;
  box-shadow: var(--shadow-sm);
}
.model-toggle-side {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: var(--faint);
  cursor: pointer;
  padding: 0;
}
.model-toggle-side:disabled { cursor: wait; opacity: .6; }
.model-toggle-side.is-current { color: var(--accent, #B23A34); }
.model-toggle-track {
  position: relative;
  display: inline-block;
  width: 30px;
  height: 14px;
  border-radius: 7px;
  background: var(--line);
}
.model-toggle-knob {
  position: absolute;
  top: 1px;
  left: 1px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--accent, #B23A34);
  transition: left .15s ease;
}
.model-toggle-knob.is-right { left: 17px; }
/* ITEM C (2026-07-24): think-mode moved out of the composer (.think-toggle, removed) into
   the header's model pill — a small add-on control right next to the smart-model bulb.
   Same accent-fill idea the old composer toggle used ("on" = solid accent fill), plus a
   subtle ring (the "rays") so a boosted state reads as a glow, not just a color swap. */
/* Deep-thinking toggle (top nav). Its own control with its own glyph — the state has to
   be readable without hunting: off is a quiet outline like its neighbours, on is an accent
   fill with a soft halo, matching how the composer marks an active mode. */
.think-toggle-btn.on {
  background: var(--accent, #B23A34);
  border-color: var(--accent, #B23A34);
  color: var(--on-accent, #fff);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent, #B23A34) 14%, transparent);
}
.think-toggle-btn.on svg { color: var(--on-accent, #fff); }

.memory-vault-btn {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
}
.memory-vault-btn {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
}

/* YUMI-R-014 — guided first-run empty-state starter chips (dense, functional; no icons,
   no decorative chrome). Each chip either sends a real prompt immediately or opens a real
   Settings sub-view — never a placeholder/no-op. */
.starter-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 14px 0 4px;
  max-width: 40em;
}
.starter-chip {
  display: inline-flex;
  align-items: center;
  height: 34px;
  padding: 0 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--ink-2);
  font-family: var(--ui);
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: border-color .15s, color .15s, background-color .15s;
}
.starter-chip:hover { border-color: var(--accent); color: var(--ink); background: var(--surface-2); }
.starter-chip:disabled { opacity: .5; cursor: default; }

/* ITEM E (2026-07-24): mobile start page lost all brand color once the header wordmark
   hides at phone widths (.top .word{display:none}, mobile-ergonomics.css) — chat.js renders
   these only when isMobile, so desktop (which still has the header wordmark) is untouched.
   Restrained: a small red wordmark above the tagline, and an accent-tinted (not full-color)
   rest-state border on the starter chips instead of the plain hairline. */
.empty-state-brandmark {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--accent, #B23A34);
  margin: 0 0 4px;
}
.starter-chip-accent {
  border-color: color-mix(in srgb, var(--accent, #B23A34) 38%, var(--line));
}

/* YUMI-B-006: .attestation-chip was used in chat.js with no DS rule backing it
   (allowlist-gate violation) — defining it here closes that gap. */
.attestation-chip {
  margin-top: 6px;
}
.attestation-lock { display: inline-flex; align-items: center; justify-content: center; background: none; border: 0; padding: 2px; margin: 0; cursor: pointer; color: var(--mute); line-height: 0; }
.attestation-lock:hover, .attestation-lock[aria-expanded="true"] { color: var(--accent); }

/* Memory galaxy — full-screen vault explorer (Settings list remains the control surface).
   Tokens only. Classes consumed by public/app/memory-space.js. */
.memory-space {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  overflow: hidden;
}
.memory-space-canvas {
  position: absolute;
  inset: 0;
  z-index: 0;
}
.memory-space-canvas canvas {
  display: block;
  width: 100%;
  height: 100%;
}
.memory-space-hud {
  position: absolute;
  left: 16px;
  bottom: 16px;
  z-index: 4;
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-width: min(320px, calc(100vw - 32px));
  pointer-events: auto;
}
.memory-space-card {
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow-sm);
  backdrop-filter: blur(12px);
  padding: 12px 14px;
}
.memory-space-kicker {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0 0 6px;
}
.memory-space-title {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--ink);
  letter-spacing: -0.02em;
  margin: 0 0 4px;
}
.memory-space-sub {
  font-size: 12px;
  color: var(--mute);
  line-height: 1.45;
  margin: 0;
}
.memory-space-sub kbd {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink-2);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 1px 5px;
}
.memory-space-modes {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.memory-space-modes .btn {
  padding: 7px 12px;
  font-size: 11.5px;
  box-shadow: none;
}
.memory-space-modes .btn:hover {
  transform: none;
}
.memory-space-sheet {
  position: absolute;
  top: 16px;
  right: 16px;
  z-index: 5;
  width: min(360px, calc(100vw - 32px));
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  box-shadow: var(--shadow);
  display: none;
  flex-direction: column;
  max-height: min(70vh, 520px);
  overflow: hidden;
}
.memory-space-sheet.open {
  display: flex;
}
.memory-space-sheet-h {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 16px 16px 12px;
  border-bottom: 1px solid var(--line-soft);
}
.memory-space-sheet-t {
  flex: 1;
  font-family: var(--display);
  font-weight: 700;
  font-size: 15px;
  letter-spacing: -0.02em;
  color: var(--ink);
}
.memory-space-sheet-x {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--faint);
  cursor: pointer;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  font-size: 18px;
  line-height: 1;
  padding: 0;
}
.memory-space-sheet-x:hover {
  background: var(--surface-2);
  color: var(--ink);
}
.memory-space-sheet-b {
  padding: 14px 16px 16px;
  overflow-y: auto;
}
.memory-space-body {
  font-family: var(--font-body-serif);
  font-size: 14.5px;
  line-height: 1.45;
  color: var(--ink);
  margin: 0 0 12px;
  white-space: pre-wrap;
  word-break: break-word;
}
.memory-space-meta {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  color: var(--faint);
  text-transform: uppercase;
  margin-bottom: 12px;
}
.memory-space-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 14px;
}
.memory-space-tag {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  font-weight: 500;
  color: var(--accent);
  background: var(--accent-soft);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  border-radius: var(--r-pill);
  padding: 4px 9px;
}
.memory-space-actions {
  display: flex;
  gap: 8px;
  align-items: center;
}
.memory-space-actions .btn {
  box-shadow: none;
}
.memory-space-actions .memory-space-danger {
  margin-left: auto;
  border-color: color-mix(in srgb, var(--ink) 20%, var(--line));
  color: var(--ink-2);
  background: var(--surface-2);
}
.memory-space-actions .memory-space-danger:hover {
  border-color: var(--ink);
  color: var(--ink);
  transform: none;
}
.memory-space-empty,
.memory-space-status {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 28px;
  text-align: center;
  pointer-events: none;
}
.memory-space-empty-card,
.memory-space-status-card {
  pointer-events: auto;
  max-width: 28em;
  background: color-mix(in srgb, var(--surface) 94%, transparent);
  border: 1px solid var(--line);
  border-radius: var(--r);
  box-shadow: var(--shadow);
  /* R1 density: tightened from 22px 24px — real, small improvement, but honestly minor:
     this full-screen 3D vault view is a canvas visualization, not a list, so the actual
     "dead whitespace" here is the canvas background itself (by design, not a bug). The
     surface most people mean by "vault list" is Settings -> FlowState vault, which is
     settings.js-owned (excluded from this claim) and was returning a live backend error
     ("vault temporarily unavailable") during R1 testing, unrelated to this change. */
  padding: 18px 20px;
}
.memory-space-empty-card h2,
.memory-space-status-card h2 {
  margin: 0 0 8px;
  font-size: 18px;
  font-weight: 800;
  letter-spacing: -0.03em;
  color: var(--ink);
}
.memory-space-empty-card p,
.memory-space-status-card p {
  margin: 0 0 14px;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--mute);
}
.memory-space-label {
  color: var(--ink);
  font: 600 11px/1.25 var(--ui);
  padding: 5px 9px;
  border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--surface) 94%, transparent);
  border: 1px solid var(--line);
  white-space: nowrap;
  max-width: 180px;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
  user-select: none;
  transform: translate(-50%, -160%);
  opacity: 0;
  transition: opacity 0.15s ease;
  box-shadow: var(--shadow-sm);
  letter-spacing: -0.01em;
}
.memory-space-label.show {
  opacity: 1;
}
.memory-space-label.hot {
  border-color: color-mix(in srgb, var(--accent) 55%, var(--line));
  background: color-mix(in srgb, var(--accent-soft) 85%, var(--surface));
}
.memory-space-list-link {
  margin-top: 4px;
}
@media (max-width: 720px) {
  .memory-space-hud {
    left: 12px;
    bottom: 12px;
  }
  .memory-space-sheet {
    top: auto;
    bottom: 0;
    right: 0;
    left: 0;
    width: 100%;
    border-radius: var(--r) var(--r) 0 0;
    max-height: 55vh;
  }
}
@media (prefers-reduced-motion: reduce) {
  .memory-space-label {
    transition: none;
  }
}

/* bk-yumi5-mobile-header (2026-07-18): the mobile .top header (MobileTopRow, main.js)
   measured 156px tall, split into 3 stacked row-bands at 390x844. Two real causes,
   both fixed here:
   1) Stylesheet load-order collision, not a missing rule: _ds_bundle.css already
      hides the model-name/tier button at <=600px via `.model-badge-label{display:none}`
      (loads FIRST, see index.html), but this file's own unconditional
      `.model-badge-label-btn{display:inline-flex}` (same element, second class,
      EQUAL specificity, loads SECOND) wins the cascade and re-shows it — confirmed
      live: the badge-wrap measured 224px wide at 390px, containing the full
      "DeepSeek V4 Flash · Fast · Secure" button. That alone forced the icon row to
      overflow onto a 3rd line. Re-declared here, later in the same cascade position
      as the rule it needs to beat, so mobile keeps icon-only as originally intended.
   2) By design .top .sp gets flex-basis:100% at mobile (_ds_bundle.css) to force the
      brand onto its own row above the icon cluster — that alone is 2 rows, and the
      task's AC calls for ONE. With the label-btn fix above, the full icon cluster
      (lock + 4 round icon buttons) measures ~200px — comfortably under the ~362px
      available at 390px width alongside the ~95px brand mark — so collapsing back to
      a single flex row (no forced sp break, no wrap) fits with room to spare instead
      of needing a scroll/wrap fallback. */
@media (max-width: 600px) {
  .yarnball-app .top .sp {
    flex-basis: auto;
    height: auto;
  }
  /* YUM-UX-B13 item 8 (2026-07-22): the 2026-07-18 fix above forced flex-wrap:nowrap on
     the assumption the icon cluster measured ~200px total. Since then more red icon
     buttons were added to .top (now brand ~123px + model-toggle ~148px + 4x36px icon
     buttons + gaps, well over the ~362px available at 390px width) -- forcing nowrap on
     a row that no longer fits pushed the last icon buttons off-viewport, growing
     document.scrollWidth past the viewport width and leaving a dead gray strip on the
     right (content column not actually filling 100% width at 390px). Restore wrap so
     the row folds onto a second line instead of overflowing off-screen. */
  .top {
    flex-wrap: wrap;
  }
}

/* =============================================================================
   UNISON semantic surface roles (YUMI-CDS-STRICT, 2026-07-19)
   -----------------------------------------------------------------------------
   100% strict CDS: app/local files are ONLY pointers to the CDS. NO inline styles
   (not even --var handoffs), NO atomic/utility classes. Every app element carries a
   SEMANTIC role class defined here (or an existing DS component). Layout, spacing,
   geometry, and responsive behaviour live in the DS — never in app code.
   Dynamic geometry uses native semantic elements (<progress>) or CSS-only structure
   (a relative wrapper + absolutely-positioned child), never a JS-computed inline value.
   ========================================================================== */

/* Forms — inputs / textareas */
.form-field {
  width: 100%; font: inherit; box-sizing: border-box;
  border: 1px solid var(--line); background: var(--bg);
  border-radius: 7px; padding: 8px 10px; margin-bottom: 6px;
}
.form-field.is-surface { background: var(--surface); padding: 11px 13px; margin-bottom: 11px; }
.form-textarea {
  width: 100%; font: inherit; box-sizing: border-box; color: var(--ink);
  border: 1px solid var(--line); background: var(--bg);
  border-radius: 7px; padding: 8px 10px; margin-bottom: 8px;
  font-size: 13px; resize: vertical;
}
/* DS focus language, not the browser's blue ring */
.form-textarea:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 12%, transparent); }

/* Ben 2026-08-08: the explainer "i" markers were 13px circles with transparent 9px
   glyphs — effectively invisible. Readable at rest, still quiet. */
.tip-anchor .tip-info {
  width: 16px;
  height: 16px;
  font-size: 11px;
  color: var(--mute);
  border-color: var(--line);
  opacity: 0.75;
}
.field-stack { display: flex; flex-direction: column; gap: 8px; }
.field-grow { flex: 1 1 100%; min-width: 0; }
.field-grow.is-120 { flex: 1 1 auto; min-width: 120px; }
.field-grow.is-160 { flex: 1 1 auto; min-width: 160px; }

/* Rows of chips / actions / meta */
.chip-row { display: flex; gap: 6px; flex-wrap: wrap; }
.action-row { display: flex; gap: 8px; flex-wrap: wrap; }
.action-row.is-end { align-items: center; justify-content: flex-end; gap: 10px; }
.auth-waitlist-button {
  appearance: none;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--accent);
  font-family: var(--ui);
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  padding: 6px 11px;
  border-radius: var(--r-pill);
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: background-color .15s, border-color .15s, color .15s;
}
.auth-waitlist-button:hover {
  border-color: var(--accent);
  background: var(--accent-soft);
}
.auth-waitlist-button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.meta-line { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--mute); }

/* Inline text action (accent mono link-button) */
.inline-action {
  color: var(--accent); font-family: var(--mono); font-size: 11px;
  white-space: nowrap; background: none; border: none; padding: 0; cursor: pointer;
}
.inline-action.is-disabled { cursor: default; opacity: .5; }

/* YUM-UX-B13 item 11: real <a> hyperlinks (mailto:, Contact sales, etc.) were rendering
   with the browser's default blue-underline UA styling -- no DS class covered a plain
   inline text link. One semantic class, accent-red, no underline at rest (matches the
   rest of the UNISON accent-link treatment), underline on hover/focus for affordance. */
.link-accent { color: var(--accent); text-decoration: none; }
.link-accent:hover, .link-accent:focus-visible { text-decoration: underline; }


/* A section separated from the one above by a hairline */
.section-divided { margin-top: 14px; border-top: 1px solid var(--line); padding-top: 12px; }

/* Settings surface */
.settings-wrap { max-width: 584px; margin-inline: auto; width: 100%; padding-bottom: 32px; }
@media (max-width: 600px) { .settings-wrap { padding-inline: 16px; } }
.settings-row-pad { padding: 10px 14px; min-width: 0; }
.settings-row-pad.is-divided { border-top: 1px solid var(--line-soft); }
.settings-narrow { max-width: 380px; }

/* YUM-1476 (SB 2026-09-29): on a phone the Appearance colour-scheme Tabs (8 schemes) needed
   622px inside a 343px row and dragged the whole Settings pane 115px sideways. Fixed once in
   the two shared rules every Settings Tabs control already uses, not on the Scheme row alone,
   so any row that carries a Tabs is covered. Scoped with :has() so an ordinary label/value
   row keeps its one-line layout and only a row with a tab strip stacks. Do NOT hide this with
   overflow-x:hidden on main — that hides the scroll instead of fitting the content. */
@media (max-width: 600px) {
  .setrow:has(.atabs) { flex-wrap: wrap; align-items: stretch; gap: 6px; }
  .setrow:has(.atabs) > label { flex: 1 1 100%; }
  .setrow .atabs { flex: 1 1 100%; flex-wrap: wrap; margin: 0; }
  .setrow .atab { min-height: 44px; display: flex; align-items: center; justify-content: center; }
}

/* "This computer" (desktop app Settings screen, desktop-view.js) — 2026-08-11 design pass.
   Every row on this screen is a SettingsRow: a label on the left, one control on the
   right. Two of the DS's own controls carry a margin meant for a different context — Tabs
   for a standalone filter strip, Field for a vertically stacked form — and that borrowed
   margin read as an uneven gap under just those rows while every plain-text row kept the
   same tight rhythm. These wrappers neutralize only the borrowed margin; the components
   underneath are unchanged. */
.desktop-onoff .atabs { margin: 0; }
.desktop-field-inline .fld { margin-bottom: 0; }
/* "Add a folder": the project picker and the button that submits it are one action, so
   they cluster together, right-aligned against the row's label, instead of the picker
   sitting in its own divided row with the button floating in a second row below it. */
.desktop-add-folder-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; justify-content: flex-end; min-width: 0; }
/* A numeric settings-recipe row: the number field and its Apply button read as one
   control, same clustering as .desktop-add-folder-row. */
.desktop-recipe-numeric-row { display: flex; align-items: center; gap: 10px; min-width: 0; }
.desktop-recipe-numeric-row .desktop-field-inline { flex: 1; min-width: 0; }

/* Voice surface */
.voice-screen { display: flex; flex-direction: column; align-items: center; gap: 14px; position: relative; padding: 34px 0 30px; }
.voice-ring { width: 116px; height: 116px; border-radius: 50%; border: 2px solid var(--accent); display: flex; align-items: center; justify-content: center; }
.voice-error { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.voice-gear { position: absolute; top: 0; right: 0; }

/* Usage meter — native <progress>, value set by the value/max attributes (NO inline style) */
.usage-meter { width: 100%; height: 6px; border: 0; border-radius: 3px; background: var(--line-soft); appearance: none; -webkit-appearance: none; }
.usage-meter::-webkit-progress-bar { background: var(--line-soft); border-radius: 3px; }
.usage-meter::-webkit-progress-value { background: var(--accent); border-radius: 3px; }
.usage-meter::-moz-progress-bar { background: var(--accent); border-radius: 3px; }
/* C3 item 9: Storage usage as ONE dense line (text + inline meter), replacing the old
   SecurityBox-wrapped text + separate meter-row block. */
.storage-usage-line { display: flex; align-items: center; gap: 10px; }
.storage-usage-text { font-family: var(--mono); font-size: 11.5px; color: var(--ink-2); white-space: nowrap; }
.storage-usage-meter { flex: 1; }
/* C3 item 9: storage file row -- name/kind/size on the left, hide/delete/add-to-chat actions
   (same .x link-action style ServerRow already uses) grouped on the right. */
.storage-row-actions { display: flex; gap: 10px; flex: 0 0 auto; }
.storage-file-row.is-hidden { opacity: .55; }

/* Modal shell — shared close button + its positioning context */
.mcard-anchor { position: relative; }
.modal-close-btn { position: absolute; top: 12px; right: 12px; width: 30px; height: 30px; padding: 0; display: flex; align-items: center; justify-content: center; z-index: 10; }
/* Maximize/restore, immediately left of the close button on modals that opt in
   (ModalShell maximizable). Same size and vertical position so the two read as one
   window-control cluster rather than two unrelated buttons. */
.modal-maximize-btn { position: absolute; top: 12px; right: 48px; width: 30px; height: 30px; padding: 0; display: flex; align-items: center; justify-content: center; z-index: 10; }
/* A maximized modal fills the viewport. max-width/max-height must be cleared explicitly:
   the windowed card caps at 96vw/94vh, which would otherwise leave a visible margin and
   make "maximize" look like it had not worked. Resize is dropped while maximized because a
   drag handle would fight the maximized size on the next reflow. */


/* Resizable modals, popups, and full-screen editor windows (Ben 2026-08-15).
   overflow-y, never overflow: a modal scrolls DOWN. `overflow: auto` set both axes,
   so one long unbreakable string — a deep Windows path in the folder list — put a
   horizontal scrollbar across the bottom of the card (Ben 2026-08-17). */
.mcard {
  overflow-x: hidden;
  overflow-y: auto;
  min-width: 320px;
  width: 100%;
  max-width: 480px;
  max-height: 90vh;
}
.mcard.wide {
  min-width: 480px;
  width: 100%;
  max-width: 680px;
  max-height: 90vh;
}
/* …and the thing that overflowed: long paths/values wrap instead of pushing the card
   wider. Applies to every settings row, so no other surface can reintroduce this. */
.setlink .sl-l,
.setlink .sl-v,
.setrow .sl-l,
.setrow .sl-v {
  overflow-wrap: anywhere;
  word-break: break-word;
  min-width: 0;
}
.mcard:has(.simple-canvas-viewer-body) {
  min-width: 520px;
  min-height: 420px;
  max-width: 96vw;
  max-height: 94vh;
  resize: both;
  overflow: auto;
}
/* .canvas-popup-dialog is defined ONCE, in canvas.css. It used to be declared here too, and
   the two disagreed: this block asked for resize:both + overflow:auto, canvas.css (loaded
   after, so it wins) set overflow:hidden and fixed width/height. The result was a dialog
   that advertised a resize handle and had none. Sizing and resize now live with the rest of
   the dialog's rules rather than being split across two files that override each other. */

/* Pricing surface */
/* 30px next to 15px body was a shout. The price is the loudest thing on the card either
   way; it does not need to be a display size (Ben, 2026-08-12: "the font is too big"). */
.plan-price { font-size: var(--t-title); margin-top: 2px; margin-bottom: 4px; }
.plan-cta { margin-top: 10px; width: 100%; }
.pricing-top { padding-top: 4px; max-width: none; }
.pricing-retry-btn { margin-top: 8px; }
/* Pricing had been borrowing .collab-grid/.collab-card from the collaboration screen,
   with the Team plan marked `wide` so it spanned the full row — which is why the live
   page showed two cards and then one stretched card orphaned underneath. Plans are their
   own thing: equal cards, one row while they fit, no special case for one tier. */
.pricing-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(232px, 1fr)); gap: 14px; margin-top: 18px; align-items: stretch; }
.pricing-card { border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface); padding: 20px; display: flex; flex-direction: column; gap: 10px; }
.pricing-card h2 { margin: 0; color: var(--ink); font-size: var(--t-title-s); }
.pricing-card p { margin: 0; color: var(--mute); font-size: var(--t-body-s); line-height: var(--lh-dense); }
.pricing-card ul { margin: 0; padding-left: 17px; color: var(--mute); }
.pricing-card .plan-cta { margin-top: auto; }
/* The two cards were identical chrome with identical black buttons, so nothing on the page
   said which plan to take, and the shorter card carried ~170px of dead space under its last
   bullet (design review, 2026-08-12). The featured card takes the accent as a top rule — the
   one saturated moment on the page — and both lists centre in the space they are given so
   neither card reads as unfinished. */
.pricing-card--featured { box-shadow: inset 0 3px 0 0 var(--accent); }
.pricing-card ul { flex: 1; display: flex; flex-direction: column; justify-content: center; gap: 4px; font-size: var(--t-body-s); }
.pricing-mark { font-family: var(--mono); font-size: var(--t-label); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--accent); }
.pricing-card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
/* The accent belongs at the moment of conversion too, not only in decoration. */
.pricing-card .plan-cta:hover { box-shadow: inset 0 -2px 0 0 var(--accent); }
.pricing-footnote { margin: 18px auto 0; max-width: 640px; }
.pricing-free-line { margin: 14px 0 0; color: var(--mute); font-size: var(--t-body-s); line-height: var(--lh-dense); }
.pricing-free-line strong { color: var(--ink); font-weight: 600; }
.pricing-free-line a, .pricing-free-line a:visited { color: var(--accent); }
/* Every link on a public surface takes the accent in both states. Without the :visited
   half, a link a reader has already followed renders in the browser's default purple —
   which is exactly how "How your data is protected" looked on the live pricing page. */
.pricing-footnote a,
.pricing-footnote a:visited,
.pricing-notes a:visited,
.legal-doc a:visited,
.legal-lede a:visited,
.public-security-more:visited { color: var(--accent); }
.brand-home-link { text-decoration: none; }

/* Auth surface */
.auth-page { min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: flex-start; padding: 0 0 40px; position: relative; overflow-x: hidden; overflow-y: auto; background: var(--bg); }
.auth-page > .settings-narrow { position: relative; z-index: 2; width: 100%; max-width: 440px; margin: 32px auto 48px; }

/* Public landing page (signed-out /) — Unison v1: paper canvas, ink text, the brick accent
   used once per view. YUM-424 (2026-08-10): replaced a dark hero + emoji feature icons + a
   raw auth card stranded mid-scroll (shipped in PR #315 for Google OAuth verification) with
   this hero+auth split so the sign-in card is never orphaned between the feature grid and
   the footer, and every surface matches the signed-in app's light theme instead of forking
   its own dark one. */
/* ==========================================================================
   Public-surface type scale
   --------------------------------------------------------------------------
   The public pages had grown 40 separate font-size declarations across roughly
   twenty different values — 0.62, 0.64, 0.68, 0.72, 0.78, 0.82, 0.83, 0.84,
   0.85, 0.86, 0.88, 0.90, 0.92, 0.95, 0.98, 1, 1.02, 1.15, 1.32, 1.4rem — so a
   section heading on one page was a different size from the same rank on the
   next, and labels drifted by a pixel or two everywhere. That reads as
   carelessness however good each page is on its own (Ben, 2026-08-12).
   Seven steps, one line-height per role. Every public rule below takes its size
   from here; nothing on these surfaces declares a raw font-size again.
   ========================================================================== */
:root {
  --t-label:    0.6875rem;  /* 11px — mono eyebrows, meta, table headers */
  --t-small:    0.8125rem;  /* 13px — nav, footer, captions */
  --t-body-s:   0.875rem;   /* 14px — dense body inside cards */
  --t-body:     0.9375rem;  /* 15px — standard body copy */
  --t-lead:     1.0625rem;  /* 17px — ledes and intros */
  --t-title-s:  1.125rem;   /* 18px — card titles, h3, brand */
  --t-title:    1.375rem;   /* 22px — section headings, h2 */
  --t-display-s: clamp(1.75rem, 2.6vw, 2.125rem);  /* document + page h1 */
  --t-display:   clamp(2rem, 3.4vw, 2.75rem);      /* hero h1 only */

  --lh-display: 1.12;
  --lh-title:   1.3;
  --lh-body:    1.65;
  --lh-dense:   1.5;

  --ls-label:   0.09em;   /* every uppercase mono label tracks the same */
  --ls-title:   -0.012em;
  --ls-display: -0.022em;

  /* One page frame: the brand, the nav and the first column must not shift as a
     reader moves between the landing, pricing and the legal documents. */
  /* The document measure: text column + gap + index rail, as ONE fixed width.
     It was calc(62ch + 60px + 236px). A custom property is substituted as text and its
     units resolve at the USE site, so `ch` still measured against each element's own
     font — the security page's intro, set at --t-lead, computed a wider box and started
     15px left of the title above it. 790px is what the legal pages already render
     (1032px frame, 121px gutters), so nothing moves; it just cannot drift again. */
  --doc-measure: 790px;
  --page-max: 1080px;
  --page-pad: 24px;
}

.public-landing-container { position: relative; width: 100%; max-width: var(--page-max); padding: 0 var(--page-pad); box-sizing: border-box; }
/* The letterbox band: a full-bleed strip pinned to the top of the window, with the nav
   inside it held to the same measure as the page (Ben, 2026-08-12: "have the top navigation
   permanently visible, like in the letterbox format"). The rule and the paper run edge to
   edge; the links line up with the text below them. The band carries the hairline so the
   bar itself does not draw a second one when it scrolls under content. */
/* The band bleeds to the window like the hero footage does. Held to the page frame it ended
   short of the video's right edge and left a visible step in the top bar (Ben's screenshot,
   2026-08-12) — the footage uses 100vw, which includes the scrollbar, and the band did not. */
.public-nav-band {
  position: sticky;
  top: 0;
  z-index: 20;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}
/* Nothing may scroll sideways because of the two 100vw breakouts above. */
html, body { overflow-x: clip; }
.public-nav-bar { display: flex; align-items: center; justify-content: space-between; height: 68px; width: 100%; max-width: var(--page-max); margin: 0 auto; padding: 0 var(--page-pad); box-sizing: border-box; }
/* Anchored sections must not land under the pinned band when the index is clicked. */
.legal-section { scroll-margin-top: 84px; }
.public-nav-brand { display: flex; align-items: center; gap: 10px; text-decoration: none; color: var(--ink); font-family: var(--display); font-weight: 700; font-size: var(--t-title-s); letter-spacing: var(--ls-title); }
.public-nav-brand img { width: 26px; height: 26px; }
.public-nav-links { display: flex; align-items: center; gap: 22px; }
/* The phone menu button. Hidden entirely above 640px — desktop keeps the flat link row. */
.public-nav-menu-toggle { display: none; background: transparent; border: 1px solid var(--line); border-radius: var(--r-pill); color: var(--ink); font-family: var(--ui); font-size: var(--t-body); line-height: 1; padding: 9px 14px; min-width: 44px; min-height: 44px; cursor: pointer; }
.public-nav-menu-toggle:hover { border-color: var(--mute); background: var(--surface-2); }
.public-nav-link { color: var(--mute); text-decoration: none; font-size: var(--t-small); font-weight: 500; transition: color 0.15s; }
.public-nav-link:hover { color: var(--ink); }
.public-nav-cta { background: var(--ink); color: var(--on-ink); padding: 8px 18px; border-radius: var(--r-pill); font-weight: 600; text-decoration: none; font-size: var(--t-small); font-family: var(--ui); border: none; cursor: pointer; transition: background 0.15s, transform 0.15s; }
.public-nav-cta:hover { background: var(--ink-2); color: var(--on-ink); transform: translateY(-1px); }
/* The header's sign-in is a secondary route to the hero's action, not a second primary: a
   border reads as a control without competing with the filled button below it. */
.public-nav-signin { background: transparent; color: var(--ink); border: 1px solid var(--line); padding: 8px 18px; border-radius: var(--r-pill); font-weight: 600; text-decoration: none; font-size: var(--t-small); font-family: var(--ui); cursor: pointer; transition: border-color 0.15s, background 0.15s; }
.public-nav-signin:hover { border-color: var(--mute); background: var(--surface-2); }

/* Hero: pitch on the left, the sign-in card on the right — the fix for "the login page is
   dumped mid-page". Stacks (copy, then card) under 860px; see the media query below. */
/* The hero carries the yarnball thread behind it, full bleed (Ben, 2026-08-12, option C).
   position:relative + a veil above the video and below the content: the copy has to stay
   legible on top of moving footage, so the paper is laid back over it at 62% rather than the
   video being faded — fading the video greys the thread instead of keeping it red. */
/* Ben, 2026-08-18: "needs space" / "needs gap", twice — the hero's grey band ended and the
   next section's heading started against it with nothing in between, so the two read as one
   block. The breathing room belongs below the hero, not as a margin on the heading, which
   would also push the heading away from its own subtitle. */
.public-hero-split { position: relative; display: grid; grid-template-columns: 1fr 1.15fr; align-items: center; gap: 56px; padding: 48px 0 44px; margin-bottom: 72px; isolation: isolate; }
/* Full bleed means the window's edges, not the page frame's — constrained to the frame the
   footage draws two hard vertical seams down the hero. Both layers break out to 100vw. */
.public-hero-video, .public-hero-veil { position: absolute; top: -8px; bottom: -8px; left: calc(50% - 50vw); width: 100vw; pointer-events: none; }
.public-hero-video { object-fit: cover; height: calc(100% + 16px); z-index: -2; }
.public-hero-veil { background: rgba(244,244,242,.62); z-index: -1; }
/* SB 2026-09-25: in dark mode the veil was still a light wash, so the hero text (light on
   dark) sat on near-white. Tint it from the page ground instead, for every colour scheme. */
[data-mode="dark"] .public-hero-veil { background: color-mix(in srgb, var(--bg) 72%, transparent); }
.public-hero-split > .public-hero-copy, .public-hero-split > .public-hero-auth { position: relative; }
/* Inside the desktop or phone app, signed out is ONE thing: the sign-in card, centred on
   the video (Ben, 2026-08-17). The marketing sections are not rendered at all, so the hero
   is no longer a two-column split sharing the screen with a pitch — it IS the screen, and
   the footage runs the full height of the window instead of a band inside a scrolling page. */
.public-landing-container.is-shell { display: flex; min-height: 100dvh; padding: 0; }
.public-landing-container.is-shell .public-hero-split {
  grid-template-columns: 1fr;
  place-items: center;
  flex: 1;
  padding: 24px 16px;
  min-height: 100dvh;
}
.public-landing-container.is-shell .public-hero-video,
.public-landing-container.is-shell .public-hero-veil { top: 0; bottom: 0; height: 100%; }
/* The card keeps a comfortable measure on a desktop window and goes edge-to-edge on a
   phone, where 420px of card in a 390px viewport would otherwise clip. */
.public-landing-container.is-shell .public-hero-auth { width: 100%; max-width: 420px; }
/* Somebody who asked their system to calm down gets the still frame, not a loop — the frame
   is painted behind the hero so hiding the player leaves a picture, not an empty rectangle. */
@media (prefers-reduced-motion: reduce) {
  .public-hero-video { display: none; }
  .public-hero-split { background-image: url("data:image/jpeg;base64,/9j//gAQTGF2YzYyLjExLjEwMAD/2wBDAAgICAkICQsLCwsLCw0MDQ0NDQ0NDQ0NDQ0ODg4REREODg4NDQ4OEBARERITEhERERETExQUFBgYFxccHB0iIin/xACUAAEBAQEBAQEBAAAAAAAAAAAAAQIEBQMGBwEBAQEBAQEAAAAAAAAAAAAAAAECAwQFEAEAAgECAwUFBQgCAQUBAQAAARECAyExBBJBgZFxEzJRYSJyUgUjobGSwWLRgkIUM6JTQ9Lw4RUG8bIRAQEAAgECBQQCAwEAAAAAAAABAhEhAzFREgRBcWGBIvDBsdGhkUL/wAARCAUAAtADASIAAhEAAxEA/9oADAMBAAIRAxEAPwD+qAAAAAAAAiiAqKAhPEQCBWQUAAABJVJBAAAAAAAARe1ABQBAQAAAAEFAEAEAAAAVAGZRZAO1AAAABAAAAAEUAEAEVAFRQQEBWWmQAAJZWUAABEVAAAAAEABFQHrAAAAAAAAigAIAMy0AiKAAAAAIIAAAqAAAIogKgAqKgIKgAAAAAACKAyKgAAAHaDMiygFAgAKCIqAAAAAAAgqAIoCKIAAAiKCAAkoqAAgCKgAACKAgqAioA9YAAAAAAAAAAABFQEFAQJQFAARUBAAAAAAQAAFBBQGQAAAAAAAAAQEABQQSQCUJAJABBUAAABAAAAAEVAVFQBFlAVABEUAQSQQAAABFARFQAABFQEVFB6oAKgACoAAAAACAogCSBQASgKAAyoCAAAAAAAgKIoIAAioCoqAAAAAAAiKgCgCIqAkqgAB2gqAAgAAgAAAACKgKCASiygAqAgqAgEgyLSAAAIoCAAgACKAiKA9ZABUAAAAAAAAAEUAQRoGaZbZBUFBBQGWk7VBkAAAAABFQFAAQAQAAVAAAAAEVAQFBEUBAOAIRAAqCAoigkgAgqAAAIqAqCgiKgKgAIqAMyoCQi8EAAABAAAQAAAEAB6oqAKIAAAAAAACAoAAiAqSMg1BKQoIKgAqAgAAAAAIKgAoCCoCCoAAAAAAAioCAAAAAnaCFL2kgyogAAAAIKgAAIKgAqAgAAAAIAioCSSsJIIKgAAIKgBIAgAIiqD1UAAAAAAAAAEUAAARFkBDgqSBuFlgAAgIAAAAAACKAAICoqAIoAioAAAigIAAigIAACSAhICKgAACoqAIAAACAAACAAKIAACJxWQGSQBAAAAEUBEVAEUBlpAHqgAoiggAIoAAAAAAAhbICyTCAKAAACBIBKKgAAAAAAIogAqAAgKIAAAASCAgKIoICgiSoDIICoKCKiggAIAAAAigICAAAAAIqAqKgJwQlAAAAAQVAEUBBUBFRQeoAAKgAAAAAAAAISACABSKgKgtAAAnaioAAAAAIoAACAAKgAAIAAAAioACAAAKIAi7IAyqAKAIqAAAIAAAAioAKgAICoACKgKCSDIgAsiAAAIqAIqAAAioA9ZAAAAAAAARUBQAZS2qSgRewOwEVFBFZUFEpQEJQAAAAAAAAAABFQEAAABFQAABBUAVFBGV7QEa4IgLIigyL2oAAAACAAAAIqAKgAioAACCgCSqAypKAgAAACKgCKgKioCCoD1QAAAAAAAAAEAAEA7QARUUEFQBUAJRSQRUUEFgBAABFBBUARpkAAAAAABFQBFQAAAAEkolQZRSgUSiQQAAoAOCKgAAAAIKAyKgAAAACCgzLLUsgAAsIACKAgIAKgAID1RFAAAAAAARWYBVQBBUBBQEFABCwBAFJQAAAAAAAEUEABBQEAAAAAARUBFAAEAABJVFBIAARbZAWUAEUBBUAAAAARUBAAAAAAAQElloBlewAQAAAERUAAABAeqAAAAAACArKoAAAAACSCoACNFAyAAAAAAAAioAKAgAAIAAAAAACAAAgAAAAAAIEgIiyAgAIqAKgAAAAgKgAIqAKiggJ2AAAhAQCSEoAAAACIqAAAAgPVAAABmWkUBFQEF4s0DQyoFgAIAFFCgiKmeUYxczERHbIaBxY/eHK5ZdPqb/Hh4uxJZWrjlj3ligNMgCAioCoigAAAAgAAAAAAAIqAIKgAAAACdhJ2AkCACh2ggACAAAAAAioAAAACAAIoAioCJaoCAAAAIqAIAAAIKA9QAAABBQQVABKQBUUEVFBFFBkgAV5P3zlljy0VdTlEZeT1ny1NPHVxnHKImJ4wmU3NN9PKYZzKzeq/B5Ts/X/AHdzGOpy+nE5fPEVMTtlt8JcXMfc/LVOWM6mMxvERO1/G3x9ecqx1MMZra6/RywwuNez1HXw60kks0/SsvI09TOPYzn6cvmj+f5urHmcsf8AZpz9WHzR4e1Ds8fl8K7R88NXDU9nKJfQZ1oAEQFBBUAEAAAAQFAAABAAEAAAAEAFQGVRQOCAAABKAAAAACKgAKgIAAAAigCSqAlhSUAACCoACAHYEggAAAPUAAABBFAQAJRTsABQZRUBVZAVBQRUAYzw6oefnysXwemkwK8/T0el0xg+3SA5s9HHLjjv742nxjdmI1sPZz6o+zqb/wDKN/G3WnTEpprzOeOZ6f8AZhlh8Y+bHxjfxh0YamGpF45Y5eUszi+GehhlNzFT9rG8Z8YRPxv0dY4ojX0/ZyjOPdntP7UfvhqOaxj/AGY5afnvj+1G3jS7Xy+HLrVjHKMouJiY+E20rCAACgIAACAAoIKgIKgAACKlAQdhaAgKCAsggIAAAAAACCgCKgIAAAAAAgAMtJAJRIgAACKgAAIAAAD1AAAARFSQAAVCigFRAVlQEULBFQAAAAAABAAGabZBJxYnF9kBxzy+N3jeE+/HbxjhJ1a+n9nVj9nL+U/k6qShrzePL4481pzNZXpz7s9vCeDofHLCMtpiJ83w/wAfo/15ZaflN4/szcMr+N+jtVxxra2Ht4dcfaw4/sz+6X109fT1Nsct/dO0+ErtLjfl9gFZAAAAEEBUkAAAASwVmFQBBQEFBAARRAAAAAQVACVQEFQAAAAAEkEQARZACkVAAQBFQAABFQHqI0AgUAMy0kwAACLKAKACKgCogAAAAAAAACAgKAACAqAAlKA+c4vjnpYantYxl+seU8XQULuxyRhq6fsZ7fZz+aPHjH5txzPTtqYTh/FHzY+McO+H3nFmkXzb7zbeOeOcXjlGUe+Jtpx5cvhM3EdM/axmp/L96Xr6fbGrH8Xy5eMbT4Inll7X/rtHLHNaczWV6eXuz28MvZnxdK7LLBFFRFQBUAGbFpARaAAAEUpkAAFQAFRQRRAAAEAEFAQVAAAEEBFAEAkEAARUARQEAAAB6gAIFoCoAAAIoACKAADIAAAAACACoAAAAACAAqAAACKgICgzSU2oPhOMTG8RMe6Yc/oThvpZTp/Djj+zP7nbTMwNTKxy+vq6ft4dcfa0/wB+M7+Fvvp62lq+zlEz7uE+E7rT5amjhqcYifj2x5TxRd43vNfDpHFGOvpexn1x9nU38Mo38bfSOaxjbUxy0/jO+P7UbeJtPL4cukImJi43hm1ZatBAAAAAQABFQAAAAAAAQAEUAABAABKABAAkBIRUAAARUABABFAAB6QAAACLTIKqAFo0gCoAgqAAAAAAgAAAAAACKgAAAAAACCAoICiKAigJTNNAMTDMw+qTAOOdDHGbwvTn+Hh348CNXWw9vHrj34bT+zM/o6ZRNNebx5Y09fS1fZzi/dO2XhO77ObU0cM+OMT+vjxY6dbT9jPrj7Opv/yjfxsNY3tdfSu1HLHNYxtqxOlP8W+M+WcbeNOiJjKNpiYVLLFsRRAEBpEAAAAAAAEVAAARUAAAEVABO0kEICAEAAAEABAAAAAAejYoAIoDLVIBRQAKgCpISCAAAAAAIqAAAAAAAIAAAAAIAAAAACAAAAAAAgJxKDsBhGloHzmL2lzf4sYzelM6U+7HfGfPGdvCnWiaWW49q5o1tbT/ANmn1x9rT49+M/umX309fS1fZyiZ93CfCd2nw1OX09TjjE/HhPjA1vG95r4dI4+jmNL2NT1I+zqce7ON/G245rGNtSJ05/i9nuyjb9Bnyb7XbpEiYnhuqogqAoAAr5aurpcvhOeplGOMdsnZZLbqcvojzNH745HmdT08NSsp4RlFX5PTSWXs1n08sLrKWfKiIrCoAAICiKDPaTaoAytgIACAvYCAAgqAAAiooPRUAZa4IAilAFCAAACooIAAACKACACKAAqAIAAAAAAACACKACKgFAAAAAASDMgp2IAyooIioCUjQDCTDaA5Z5eMZvSmdKf4fZnzxnZY1tXDbPDrj7WH/pn90ugGvN48pp62lq+zlF+6dp8J3bfHU0cNTjF/Htjyni+XRzGn/r1OuPs6m/hnG/jadl1jffXy7B5ur944aMT6mGWOcf2+/wApj+Tzv/van5tOIj4Zbs3PGO2PpetnNzHb9JD8r/8Apc8sctGL+WcZ2/iv+T24y5nmIjprQwmI+afm1Jv7McI85vyed94/d/L6mh0TOeernMRhqZZzllE8cp41URHCmc5vGx09LZ0etMstce059u7+fampOOVxNTE3Hn2P6ryurOehpTn7U4YzPnT8hofdGlo6kamXVq9M3EZVEXHbUPcjm2Olhcd793T1/qMOvcZj7b5e7cSjz9LW6odsZW9D5zYAAAJIAIioCKgAAAioACAAAAAiooPRVFsBAAC0AAAAAAAAAAAQAAAAAAAQAAAAAAAAAAAEBAAAAAAAZoXZAAAQVABYQBFQBFAYGgGWgB4f3zoZZYxq4xwisvhHY/O6HKa/M6uOMYZVcTMzExER5v2nNR6kael/2Zxf047y6cqxioiIhyvT3Xu6fq8ul0pjrx0+M5xjFPIy1fX1sso4Yfh4+c75T+kPvzmp0YZ5dtVHnO0OXldKcYiPdxn+Kd5/Np5pxjb73h6UacdFODPl93p4p0Nue3Jo4Ti7sUjBuIEfSJaZUFAAQQCUkQAAAAAAEAAQAEVAFRQegAAAALZYIAACAogAAAqKCAAAAAgKIAAAAAAAIoAgoIACAAAAAAAAz2rJsbAgKDKgCKigyKgCKACKCCvjrZxp6eWc/wBsTIsm3z0vn5jUz/64jTjznfL90Ptm+fLYTp6OOM8avL6st5byqIueEMxb31Ph4nOT6mthpxww/Ez8+GEeNz3OnQwreXPoYTqZTnMb6uXX5Y8MY/Z373qY6dLGs/aeH9+7UQ3SxDdK5sdK00AzSqACAIkqgBSoCCoAAAipQAAILwQAQAUSQeiAA0yAAABwAEAAAAAAAAAAEBUAAAAAAAAABFQBUAEUBFQBUAASQEteJxJgCkFBAQACgFpAEAAAAAAcXMx6melpfay68/px/nNO1yaMepq6up8fTx8seNf1X4I1j73wdLj5vO8cdKP/ACe18MI9rx4d7ql5WGXra2pn8fTx8sfamPPL9Crjxu+H9u3Qx/udMQmMdMRDSsFAAAAAAISoIzaoBBxVAEAARQAQBFlAAAEVAUQB6IAAAAgAAAqKCKgAAACAqACoAAAAAAAAIAAAABYAAgAAAAAisg0lovAEFtJBAAAAEUBAAAAAAfHX1PS0c8o41UR78p2iPGWtLD0tPDD7OMR/OXw1PxOY0sOzC9XLu2x/OZ8HUy12k+rk5rUnS0spj2prHD6stofHldOMajsxiIj41xnvlnmcvU14xjhpRf8AXntHhjc98O3Sx6cVjV4xk8eb/D6gK5gAAAAAIozcgMqWBZaLQIACAAAAgAAACKgAAPRAAABFQAAAVFBAAAAQABUAAAAAAAEUAAARUABAVFQAAAABUAELJBCVQEUQF4IAAICoAAAAADMzERc9jTk5zKfS6I9rVmMI7+M90WLJunKXljnqz/5suqPojbH8t+90Z5xhjllltGMTMz8IMcYxiIjhEREeUOPnM+ro0vtfPn9GHZ/VlUeLLWvNl9P4j48vhllN5e1lM6mUe6cuEd2NQ9KHw0MduqeMuhYmV3dgCsgAAAAIACAiKgAKCAgAAAIAAAigCKgCCg9AAAFBAAAAAAAQAAAAAAAAAAAAEUARUABFAEAAAAAAAATsAJAEsEAAACwEAAAAAAAAcU/i81/Do4f88/5Y/q7MpjGJynhETM9zn5fCcdPqy9rUmc8vPLf9KStTiW/vL7PJwn1tTLP7c1j9GG0eM3Lr53OcdLpiay1J6MfhfGe6LljlsIiduERGMeUJ3anGPy7sYqFBpzBQEFQFQSQEUBklUkFZAAABFQBRAQAAAEFQBFARUUHoBsWAAAAAAACAAAAAAAAAAAAAIoAioCgAggAAAKAgqAAd4IFQoM8BUAAqAAQAABAAAABJBUAHNzPz9Gj/ANmXzfRjvPjtDplzaP4mtq6nZjWlj/Tvl+c13Prq5+lp5Z+6PGeyO+Ubs7Y/u687Wn1NfL3afyR9eXtT3RUd8u7Tx6cYcmhpbxHHp3mfflO+U+LvhIZ3nXg0A0wKigiiAASCKy0DPYikgyAAUAAIAAAgAAAgqAAAiooO9UAVAAABRAAFBAAAAAAAQFAAEAVAAAABAFQAABRAARAUZAWltAARQBABUAWhlQQAAABAAfHX1PS0s8/dE159keL7ODmPxdfS0uyL1c/LH2f+X6JWsZuvvy+HpaWGPujfzneZ8Xw5nPq1McOzD8TLz4YR43Pc67rj2ODSvUnqnjqZdfljwxjw370+iy98v3l1aOExjfbL7kbI0w0qKAgAoIAipIIABVJJZYIKgAAIqAAgCoAAIAAAgAjSAPREUEAAVAAAAABUAAAAASQUEVAAQBQAAAQAAAACwVEtAWWVQBSgAEBUABRAAQAAAAAAARQRxcr886ut/wBmVY/RhtHjvL78zlOOjlGPtZ1hj557flxbwwjTxjGOGMREdyXu3OMflyc5l8saccdSan4YRvlPht3roRczlX/8c2WXqa2efZH4WHdN5z3zt3PRwx6YSLlxJP8Av3bhQacwADZUAAAE4qgIqEgCACFgAACWqAgqAAAAAgIAAAAD0AAAAAAAQFAAAAEAAAAAAAEAFEAAAAABFAEAQEBUAFEUEVFBKABUFBkAAAAAAGQUEsHPP4nNYxxjSxnOfry2j/jc976cxqelp5ZRx4Yx78p2iPF8eSvLDLVnjrZTn/Twx/4xDHMZderGPZpR1z9eW2PhFz4Muuvy+kfLQ04xmI7MIq/fPbPfLvh8tHDpxfdpzt3dqAIAAAAiWtFQATwSTsBIUQEapEAkAAAElFQAABFAQVAEVAAAAAegAAAAAAAAAAAAIAAAAAAgAACgCNMqCFgAIAqWAMioAKAIqAAAAgKCAAAAAAAIqAOLnMpjS6Mfa1ZjTx/q4z3RbtcNerzWWXZo49MfXlvM90VHelbw778HV8ulp+7HDHwjGHBpYzNX7WU9efnPCO6Kh9Oay6ujS+1PVn9GG/5zUN8vHHKe1C8Y/W/06YaRWmAAFRUAAARUBFlFBLSwsAQABAVFQBFQAAAABFQAAEAABmwekAACAogAqACoAAgKCAojQMgAKgCgAgAJKWpsBYlKAHBAW0QBUUoEUpAEUBAAAABUAAAAARUAAB89XONPTyznhjEz4Q+PK6c6elF+1l8+X1Zbycx+Jlp6X2p6svpx9/nNJzWc46fTj7WpPRj39vdFyjpJ2ni48Z9TLPP7c9OP0YT++bl6OGNQ5NLGL24Y1EeUO6CM5XdVQVkAAAAABkVAFGeIKiAAAAICoAIKAIAAACAAACAACSm4PSQUEVFABAAAAAAARUUAEALUBAACwBLNl2ZBGkAWgAJRFARdwAQABAAAAAAAAAAABFBAAAfDmNT0tHPKONVj9WW2P5yLOaxofiamtq+/Lox+nDafHK3wzz9TXyy/t0o6I+vKpynuio73325Tl/f0Y9+U/wDzLm09OcYwwnjvOc+/LLeZ8WW7dbv2jr0caxfUiKhWnNQAAAAAEJUEJQBF4ACAgAqAAAgAAAIAAAAioAAACAJaID0xFARUAAAAAEAFRdgBAFQAAANwQDsBAUQsARQVFQBFQBZQAVEBRAFEUBAAABFAAQAUQBFQEcWp+LzOnp8Y049TLznbGP1l3OHlN8M9bLjqZTlv2YRtj+UX3pW8eJb9p81OZy9TW09P+3D8XPu2wx8d+59NKL3ly6V5xOc8dbLq8sOGEeG/e9DDGoIZ+08H0VFVgAAAAEARRAEtUBQ2ZAAADggAAIogAAAAIqAKioAIoIlqgIoA9CwABAFAAVEAUQANwC03XgAm5CoAWigrKoAIAoFAIABS0gAgAKAgAAAAKCAACKCKigAgAgAioDm5uZ9Loiay1ZjTjyy9qe7G3y5rbTw0MdvUrHy08Y+afDbvbj8Tmp92jjEf157z4YxHi5+r1NXV1eyPwsPKPanvnbuZdcePtz972fbCOrK+yODsiHw0sah92nJVEBUAFABi2hJBUAAZUEFQAACQQAEBUFBAAAQFEUAEoEVCQSUWgEVJaB3AAAAAAAgKMrcgonFAW0UBlqGQFBQQRQRUoBRAAAFRUAEAVAAAAAAAAQAFABFQAEBUABjPONPDLKeGMTMtOXmfnnT0f+zK8vow3mO+ahK1jN2PhjllocrOcx+JqTOVfx5z8sd37l09OMIx044YRXnPbPfLWtl6nMRHZox1z9eW2Md0XLelF7pGs7/vn/DojaGkaacxQAQoBUEoACABCgQADcAAEAEUBAAAAABFEBQQBLVAElUBWQBGtmVB6GybMqCqytgthZYCCAKgCloAAgAqAAAAAtIICqyoC0yoFIqAAAAACKAAAACKgArKgAAIqAAAODSzjLW19bL2cPw8fLHfLxy/R0cxq+jo55xxjHb45TtH5uDLD09LQ5W+O+p9Mb5ftTt3sV1wnH72929PGeiJn2tWfUy+HVwjuiod2EVD4afzZTLqhuMZXdVUUZQUAQAAAQACNmWkkBAABAVAABAAAAAEVAUEBU3ACihAKQkBFCdgRAB3opQDTCg0lpsAogAFgAIAAAoWCAACADTIAqKCKgAFgAAAAAAAIAAAAAqKAyoAgAAA4eZn1NbR0uyJnVy8seF+eUvjp5epOprfbnow+jDa++bl8JznU9bUifm1svR0fpi4vy9rLudOGEXjhj7OERjHlEM+7tl+OMn2/murSx6YfZiIbacVgVAAKAQk2gAtUkBkUBAA2AABAAABAAAAAEVFABAAAQAAoSQElFA2EAd8qhQCoAqWWAQsiACAKUIC0JIAqAAIChSAogAAAAAAAAACAKgAqAIqNAggCiAAAKgAObmtSdLRznH2p+TD6stodLzOb1YjWw7Y0Y9SY9+c/LhHndpWunjvKPnjjEam3scvh6ePx1Mo+ae6Nu916GOznw05www054x82c+/PLfKfF3YRsReplu/D6QqLSsCS0gJCoAgoAigIgAAAIAAAAIAAAAACAKgCKACKgEotJICAAigIADuUUERUAVAF4JYAAAIKAACoAAgAAAqKCCoAAAioAKgAAAICoAKqAAAAICoAAqAnDeXiYT6+v1Twv1svpjbSxn/AP07ef1ejQ6Ymp1JjC/dE+1PdjEubl4/B6qqdWeqvdhwwx7sYhm93bD8cLl48OnSi5mXZD56eNQ+rTkohIhMhPAAJRQAQARoGQAEWQBAAABAAAAAAAQAAAEAmRDeQVBAAKAtAABAegIAoWgAICiKCAoCCggAAACoAKgCiAAAAAIogCoAAAgAAqAqKAgqAAAAAIMameOlhlnlwxicp7oFk28PnZ/yucx0Y4YxWXlO+pP7PTj/AFPRw+fL4RwedyeMz6uvlxzynDHun5578vyiHq6WNQzHbq2TWM/8z/bohpFacBFATiHABFAARAAABFBBUABAAAAAAABAFQAAQFZUALEAEABAFQAQFB2qAKy1KAgAAAC7oAAAAgKACoAAAAIBSgAACKIAioAAAKAIoCAAAgCooqAArx/vbVmMNPQx9rWziO6P3XVvYh4HV/k/eOrq/wBnLxGnh7uueM9zNdelOble2M3/AIduOnjhGGljwwxjHw/nLtxinNo49rrhpyt2oIIoAJZYgKqQAUCWAAAgAAgKWgAqACKgKUIAIoAAIqICiABIm4AvYgAgCAAqADuVFBAAAAAAAAAAAAAABFAABFAARQQAC1AECwEAABQQFBEUAQAVCQFZaeV9783nymhHRtlnNRPuj3+aW6m2+nhepnMZ3rs5jXjl9HU1J/txmfOeyPF53KaM6PL4Y5e3n+Jn9WX8n4vU1tWZ3zyne95n3v133Xzn+Zp9Wcx14zUuWHUmde31HpcvT9PvLLef4exhFQ+rMUrs+epucAC0UBFRQANgEAARQEABFTYAAAAARQRUAAAAABABlpAE8QABAO0AAQAAB3AAAAAgKAAIoAigCFAogAqKAAAIAqAAAABQCCgAgBQAqAAACASCKkQoK8f770fV5OcriJ056ovt+Hm9d4nN5/5OvGnx0tCs9T3Zan9uHdxlnLmaduhbjnMpx5eX4DVy1dPbLDLHzh2/dPN5Y6vTF1M7v1fMchHMY3lFuTl/urDQyuI3c8enMOXp9R6zLr4+XWo9/l9Tqxh1249HHph1w7PA0IACoCoAKgAAAgAArIAAAAAAIqAAAAigCSAl7lgAliAqKgC0WlACAAACKA7gAAAAAAABAFEUEVFAQAAASlAFQABFAAAEUAEAAARpAAAEFBAQFSRkGdXOcMZqLynbGPfPZ3POw0JxrG736sp+1lPGXpsUNb1LPFYximPTfWFGXzjGmwBUAF2QWwQVAEAAFBAAQAAAAAAEAAAUAZUTsApNlAQEAs4hYCAAgvABFQAABAB3gAAgAKCKIACAqoAqAAIAqKAAgKgAqACoAKgAAAIKCWogKIoIogCbQACKgAgotoqIKgAAAKgAigIAAigIAAAAACCoAAAACBcqCSi2AggCpsSgKgAIKAioAAAgoICA9AEABQRRAAAEUAAABAVAAAAAAAAAAuCwAsEAABRAFENgBAAuBAVDcAQAAAAQFEUAAAQAABFQBUVAAAAAAQAAAAAAEBATdeCbr5giiASBQIioAqAKgACAKioAIA9ASlAQAAAAQFEAVABUAAAAAAAAS1AAkAQoBUALRUADgAAAKjILIigAgAAKIACAKioAHFQZVFBBUBRAARQEVAAAFQARUAVAAABABAAEVANiwBFKlAAAAKAQAAAQVAd4AAACBYACgAgAASm4oAAJwA4gKcABNzigKlLaAqSAAAAICk0i7AGyALYmwCoIAAAAAAAIAAAIqAAACoAAAkWoAACAAioAogACAqbCACLsAIAAAIAAAAICoqSAAAioAADu3DY2A3EAURQBADcFAQUGVsIANziAbpwW0BeKFgJa0AHA80AJABU3LLALEBUGgZXdFBBpkARQDYARQAEAAAAssBAAAAABFEBRFBATcFRUAAALAGRUAVloECwEVAEUQBUAAAAAEUBAAEVABAHeCA0iUcAVABLBQW0SVALkssEXZEBQABF38gFSvigArINDK0CAAAAFgAqAKcDdALFtkFEAUSAFBAAABAAAAEBQABFBBQEAAAAEAVAkBEXYBAA3FSgEWkABAVABaEAAABNwAAAQAAAEssHclqgLZaFAtiKCAAKgAADLSWoBugC7iWgDTK8QQAAAAABFAAAAAAQFQAAtAUEAAAAAAABAVFAEAFQARUUAAAGQAQGkEkAQBS0UCxFoEsRQQXigAAAICoAJapuoCKgAICiKAMgO8ZUACwAANwABlQUAEtFQAAAAAAEUAAAEFBAUBAAVAFQARUAVWQAVAAAAAAARUUBAAAAAAEUAQAlFFEQNkBFAQAAnYJAQUEAAAAEUBAAAAQAVAAABBQENhAdwyoKCAbLsgCpSpYAgChYCAAAAAACACooCAAAAAAACoAFSAAgoIKgAWAKgACAAoIqd4AqAIqKAACCoAi2AIIAUACAAAAACAAAAFfFFAQAEUBAAAASRQEUQAspKA4oAO/YtlQUZAUQBUVAUQAEUAAAEAUAKQAURQQEBRFAQAUQAAAAAAABAURQBFAQAFQAEAFEsBUgBUVAAs4AIJYKlqAlipO4GyJSgWAAIAAACAKCAHagAqKCQqAAACKAnFQBJN0AZAB3AAAApsgAIoAigAAyoAAAIqAoiiVURRRAAVAFABAAAAAAAAQAAVAFQAEUAQBRACihAaRAFlDtNgAZBQAW0AEABFITiCggAAAAIAAAAgoIAAWACKAIIC2WhQIWFAgLYO0RQAAAAEUBAAAAFRAVFQAAAABUAAAVFQAAAAAEBQAAQAAAAAAEFQFEsASZEBGmVAFgASgAkTZbAC0AABFQAABZQAAQFQAAOICSqAFACoFgkhdgAm4Csy0kgUUloAogAAO6wABEBpFQAAEVFAEUEKAAAAAFQAAAAAAAAARUAVAAFQFQpQZUABLpAUsoAsEBSWQAEAUAFlLQA4gBsIoIoAgAAAAbICggFgAAAAgCoAAAJaoBsbAAgk7yCpMLwZAAA2AAQQHoCLQIrICiAKIoIqACoAogAAAAAgAAAAAqAAAAAAFKCAAAAgAFAAVZQgFgWCLbKgAoIi2gAAIAAAAAAACKigIAC7IAAAqCAAAAAJBRwAkEBREuQWbQACkpZBBAAAEAB3AAAAAAAAAAAACKAIoIKgCjIKAAIoCACiKCCgCKgKCAqKgHEpUsAkQEhdkAVEAAAUtNgAmRABeCUAAAAAACKigAgAAICgIKCAACALwEACw2AT80LBpEAQpbQAAEAABAAAdwAAAIqAKAAgAogAogKgACoAAAIoCKAlAAAAKiWDQyA0lAAIAolICyhubgsMraAAAAAIqAAAAAAAAgKgAogAAAAAgACpMghsqcQAlKBbogAEskBLCwFQpKABAUQAAsAQAAB3IUoCACiAKgAAAAAWCggAIbrQAIoCAAqKCAAKiAtsqgC8EUEULBAQAAFstFBWRdwQEAVAAFA2QkAABFEBUABUAAABAFRUBFVAAARUJA2NhkC1SloAQA2EAK+IACAAACAAAACAO8QBUsAUQBaEOACWcVABQQQBUAFQAVBAURQCwBO8KQGkQBUAFRQGVEBRAFQAFRAFQAAAFtAAAAQFEAFQAAAAAEUE4gAAAqCQCkjHEF4qkFgtpQAbJwCwO8EsCkW0ALVAAAEUAQAAAQAHcACoAIoAAAgqAqKAzYIDQigFobAk5VFuSOf5XLPojVx6v8A328HH976s46WOEbdc8fJ+Xy0+3GZiYcs87Hu9P6THq4XLLKzwf0Ghz8tnOehpZTxnDGZ86fd1eKzVsEEEVUABAAAFsEARZATdTdAUEBUAAAAAAAABQAQEABFARRAANgAQClQBQQBOCpxBexIoQCVKABAAikAAQAAAEAVABUAAAQAELUB3KgAhxWgEtUAUZsFEUALARFAAARdkmkByc7ykc1pdPDKN8ZeDH3Xrx7fTEe+Jt+pfLVi8WbhMnbp9fqdOaxvDy/8uNOouojZ2afM9TwdfSyjJ08t1RTTl3e9jlb6W5NPJ0wI2IAAAAoILbICsqCKLQHcboAAAIoAAAIAqAoKggAACKAIoCAAIoIABaSHEAEoFElYBBdkkEVAAQBRAAABAAAABAUTdQQABCwHaC0AIAmzVsKClooHEAEAARQAQAWkAVMt4AHm6mhEsY6VPQyxY6QYxxdEMRFNwCqAAAAAAIAogKACCm4AICoKAgACoAQAKgAAgKioAAAis7ApagInYFAikgAmy1AJ5lmwCWbESTICKAgoCAgAAAAICgm4AAAAAIFJQO7YQAEWwNy0AaQssAEABQQPABRKAFQAEUERQEFAQFAABdkEAVFABAWwABAA2AAAAAAE2AUAAAQUBAAAQFSABQQBJVAQlZQCFndlQOHagAXAgAAAACAAAAAACKCAAIpcAJapxASIJS5B2i7AItiAogC0MgKIAKgCiKAgAoAIAAAACAoAAICiAKipQAAAAAAAAKggKIAAoAgACUCglgAAqCbgpSbgLSSJYC8ELA4osSk0AIAogAAAACAAAAgAKIAAAqAAlAAIoDNgDuEUBCiQAXcGS1AQC4AVAAADiAAACoAAAIqAKIAqKgKIAAAKgAgAUqAKioAAAACoGwIqKCKigFQlgKzcKkgcVtCwBCZALRQBmloAQBUAAABAAAARUsAAFEAAAEVAFQAABJkNiZBNkAHdcggKeaAKgAAAAAAAAAAACALYAIWACoAAAAAAAIKCAAAAG6oAIAUoAAAgKCIoCEqlALIlAdqFWoIACC2gKgAIqAAAgAAAKgAAgAqAogAAAIAKy0CIABIgAuyAgig7QLAAANhAFQBUUmQBABUABUBRAA71QFEAAAAQBRAURQQAARQRRAUQA3CDcFZu+CgAcDYEKLgmQBOKgJwN14AV8U2EBfI3SywXdAANyywQUBCwARUAAAAAsAE2AA7AABFAQAQsgsFstDsBKWeBRUAiKAko1XvQEAB2qgCoAAgCqgAAAAAAAAAAAAAAACAqCggqAAAAgKAAlgBsJXwAVNyviApdACcRUBKAmJBTZF7gLiGVOIIGxQAICggGxQAAAIAAJYKCAqAAqAAAAICpRdJYKJUgFpRS2BwSwAZUsEW6RQQVJkBAB3FgACAoICgAqBwAEUAQAVAFABFAAEAAAAABAUQBQATcoAVJlaQEvctZQDb4m6gJUneJQAbQAbqiALYkgAABYCAAAAAAgAAAAAAACUAAAAIAqWAJcLsgAJALEFEpYAgACAAAAgAAO0AAVAAUAQAAAVAFGVAAAAAAoAABFQBSkADvSgL9yiWAtQlFAWWuybAm3uXYtJAVFAEgmbBbLlkAkAARQATcAKABAFsQAVAFQAAQAFBBUABAURQBABUAEDaAEVLBUAEAAtFQAAAABAARUB3iJYLO6KAIoCWoAcATYFELBRLhQBJuVARSgQWgEOAAcSgAooAFRAVkLAAsAQAKAARQBCgVABUAFQ3QFEUC0UBFEBYEoBUAAAEFAQOIACAoAAIBZxNkBdoZVkGq96CAogCoAIpYCAAAgAWAIqAAA7jZFAAAAARQEXYQBd0UEsAFBACwASy7VRLXdBALRQE4qzINV8UAEW0UEDYABAUmUXYEFAQXYBFQABKBRABULALAAAAAAAEAAABLLVNwUEBWbU2AtFSwSTgtACEoBZAtSAgWAIACbqCAAAgKIAqAACA77EsAFABAFEAEaARFAIKDYATf3gLaCAoEQAABSAAIAqAAACsqgKEoCiKAlgAAAAAgAoICoGwAAAAAigIAAgCgAIqAohQEpUBsBaNM7SCyyoBaGwCoqAIqAAAB2oCiAASACAIogKCA7bguiAAEBURoBeCAAUAAoJQkrAEhPBAU2+JZICWEgioAqACiUAAACALYgAAAAAAAIoIAAAAACoVYAAAIAAAAAAgKIgKbCdoLJSACKgKCAJuXIAAAgAAAIqAASAHYgKigIAAgAqBAP//Z"); background-size: cover; background-position: center; }
}
.public-hero-copy { min-width: 0; }
.public-hero-title { font-family: var(--display); font-size: var(--t-display); line-height: 1.12; font-weight: 800; color: var(--ink); margin: 0 0 16px; letter-spacing: var(--ls-display); }
.public-hero-subtitle { font-size: var(--t-lead); line-height: var(--lh-body); color: var(--mute); margin: 0 0 26px; max-width: 46ch; }
.public-hero-actions { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
.public-hero-auth { display: flex; justify-content: center; min-width: 0; }
/* The sign-in form lives in its own section under the capabilities on the web; the hero shows
   the product instead. scroll-margin keeps #signin clear of the pinned nav. */
.public-signin-section { margin: 0 0 56px; scroll-margin-top: 84px; }
.public-signin-section .public-hero-auth { scroll-margin-top: 84px; }
/* Product frame: a shared session beside its document, drawn from the app's own bubbles,
   canvas and composer. Quiet on purpose: the only accent is the send button and the caret,
   the live path. */
.public-hero-frame { position: relative; margin: 0; min-width: 0; text-align: left; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-sm); box-shadow: var(--shadow-sm); overflow: hidden; }
.pf-bar { display: flex; justify-content: space-between; gap: 12px; padding: 10px 16px; border-bottom: 1px solid var(--line-soft); }
.pf-label { font-family: var(--mono); font-size: var(--t-label); letter-spacing: 0.05em; text-transform: uppercase; color: var(--faint); }
.pf-body { display: grid; grid-template-columns: 1fr 1fr; }
.pf-chat { display: flex; flex-direction: column; gap: 14px; padding: 18px 16px; min-width: 0; }
.pf-turn { display: flex; flex-direction: column; gap: 5px; max-width: 94%; }
.pf-turn-user { align-self: flex-end; align-items: flex-end; }
.pf-msg { margin: 0; padding: 10px 13px; border-radius: var(--r-sm); font-size: var(--t-small); line-height: 1.5; }
.pf-msg-user { background: var(--ink); color: var(--on-ink); font-family: var(--ui); }
.pf-msg-ai { background: var(--surface); border: 1px solid var(--line); color: var(--ink-2); font-family: var(--font-body-serif); }
.pf-canvas { display: flex; flex-direction: column; gap: 8px; padding: 18px 18px 22px; border-left: 1px solid var(--line-soft); background: var(--surface-2); min-width: 0; }
.pf-doc-title { margin: 4px 0 2px; font-family: var(--display); font-size: var(--t-title-s); font-weight: 700; color: var(--ink); letter-spacing: var(--ls-title); }
.pf-doc-line { margin: 0; font-family: var(--font-body-serif); font-size: var(--t-small); line-height: 1.55; color: var(--ink-2); }
.pf-caret { display: inline-block; width: 2px; height: 1em; margin-left: 3px; vertical-align: text-bottom; background: var(--accent); }
.pf-composer { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 0 16px 16px; padding: 6px 6px 6px 14px; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface); }
.pf-composer-hint { font-size: var(--t-small); color: var(--faint); }
.pf-send { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: var(--r-pill); background: var(--accent); color: var(--on-accent); font-size: var(--t-small); }
@media (max-width: 640px) {
  .pf-bar .pf-label + .pf-label { display: none; }
  .pf-body { grid-template-columns: 1fr; }
  .pf-canvas { border-left: 0; border-top: 1px solid var(--line-soft); }
}

.public-section-title { font-family: var(--display); font-size: var(--t-title); font-weight: 700; color: var(--ink); text-align: center; margin: 4px 0 10px; letter-spacing: var(--ls-title); }
.public-section-sub { font-size: var(--t-body); color: var(--mute); text-align: center; max-width: 68ch; margin: 0 auto 28px; }

.public-features-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 16px; margin: 0 0 40px; }
.public-feature-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-sm); padding: 20px; transition: border-color 0.15s, transform 0.15s; }
.public-feature-card:hover { border-color: var(--accent); transform: translateY(-1px); }
.public-feature-icon { display: block; color: var(--accent); margin-bottom: 12px; }
.public-feature-title { font-size: var(--t-title-s); font-weight: 600; color: var(--ink); margin: 0 0 6px; }
.public-feature-desc { font-size: var(--t-body-s); color: var(--mute); line-height: var(--lh-dense); margin: 0; }

.public-security-banner { background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-sm); padding: 22px 26px; margin: 0 0 40px; text-align: left; }
.public-security-title { font-size: var(--t-title-s); font-weight: 600; color: var(--ink); margin: 0 0 8px; display: flex; align-items: center; gap: 8px; }
.public-security-title svg { color: var(--accent); flex: 0 0 auto; }
.public-security-desc { font-size: var(--t-body-s); color: var(--mute); line-height: var(--lh-dense); margin: 0; }

/* Downloads — the public list of installable Yarnball apps. Same card geometry as
   .public-feature-card so the page reads as one system; the row of meta (kind, version,
   size) is what turns a feature tile into something a user can act on. Rendered only for
   platforms with a real published artifact (public/downloads.json), so there is no
   disabled/"coming soon" variant to style. */
/* Capped column width, centered: the number of published installers changes over time, and
   a 1fr auto-fit grid stretches a single card the full page width, which reads as an empty
   panel rather than a download. Cards keep feature-card proportions at any count. */
/* Four across at desktop, two at tablet, one on a phone. An auto-fit 3-up grid orphaned the
   fourth platform onto its own row beside two empty column-widths. */
.public-downloads-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px; margin: 0 0 40px; }
@media (max-width: 900px) { .public-downloads-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .public-downloads-grid { grid-template-columns: 1fr; } }
.public-download-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-sm); padding: 20px; display: flex; flex-direction: column; gap: 10px; text-decoration: none; transition: border-color 0.15s, transform 0.15s; }
.public-download-card:hover { border-color: var(--accent); transform: translateY(-1px); }
.public-download-title { font-size: var(--t-title-s); font-weight: 600; color: var(--ink); margin: 0; }
.public-download-meta { font-size: var(--t-label); color: var(--mute); margin: 0; letter-spacing: 0.03em; }
.public-download-meta-part { white-space: nowrap; }
.public-download-note { font-size: var(--t-body-s); color: var(--mute); line-height: var(--lh-dense); margin: 0; }
/* The signing caveats ("not notarised yet", "SmartScreen will ask you to confirm") were set
   in the same weight as the product copy around them, so honesty read as amateurism (design
   review, 2026-08-12). Demoted to a footnote: still there, no longer competing. */
.public-download-caveat { font-size: var(--t-small); color: var(--mute); line-height: var(--lh-dense); margin: 6px 0 0; }
.public-download-action { font-size: var(--t-body-s); font-weight: 600; color: var(--accent); margin-top: auto; }

/* Signed-in visitors reach this page deliberately (/home), so the header offers a way back
   into the workspace instead of the sign-in call to action they do not need. The hero then
   has no auth card beside it and runs as a single column rather than half an empty grid. */
.public-nav-return { display: inline-flex; align-items: center; gap: 6px; text-decoration: none; }
/* Signed-in visitors get the answer specimen in the hero's second column where the
   auth card sits for everyone else, so the grid stays two-up in both states. */


/* --- legal documents (privacy, terms) ------------------------------------
   These are long numbered documents, not marketing pages: a reader arrives
   looking for one clause. So the layout is a document column at a real reading
   measure with a sticky index beside it, and the section number carries as a
   mono eyebrow rather than being buried in the heading text. Below 1000px the
   index drops out entirely — a sticky rail on a phone is a nuisance, and the
   document is what the reader came for.

   They previously shipped their own hardcoded dark stylesheet, so the two pages
   a customer reads before trusting us with data looked like a different
   company's site (found in the live audit, 2026-08-11). */
.legal-page { background: var(--bg); color: var(--ink); min-height: 100vh; }
/* The single frame every public page sits in — landing, pricing, privacy, terms, 404.
   Pricing used to sit in .card (1040px/28px) while the legal pages used their own
   shell (1080px/24px), so moving between them shifted the whole page sideways and the
   site read as two sites (found in the live browser check, 2026-08-12). One name, one
   measurement, no exceptions. */
/* width:100% is load-bearing, not defensive: on the legal pages the shell is a block in
   normal flow and fills on its own, but on /pricing it is a child of the flex column
   .yarnball-main, where a bare max-width shrinks it to its content and the page came out
   NARROWER than the documents — the same inconsistency from the other side (measured in
   the browser, 2026-08-12). */
.public-shell { width: 100%; max-width: var(--page-max); margin: 0 auto; padding: 0 var(--page-pad); box-sizing: border-box; }

/* The title block must start on the same left edge as the document below it, or the page
   reads as two different left margins stacked. Same width as the document + index pair. */
.legal-head { padding: 44px 0 0; max-width: var(--doc-measure); margin: 0 auto; }
.legal-head h1 { font-family: var(--display); font-size: var(--t-display-s); letter-spacing: var(--ls-display); margin: 0 0 10px; }
.legal-updated { font-family: var(--mono); font-size: var(--t-label); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--mute); margin: 0; }
.legal-lede { color: var(--ink-2); font-size: var(--t-lead); line-height: var(--lh-body); max-width: 68ch; margin: 20px 0 0; }

.legal-layout { display: grid; grid-template-columns: minmax(0, 62ch) minmax(180px, 236px); gap: 60px; justify-content: center; align-items: start; padding: 36px 0 72px; }

/* The index: a reader's map of a numbered document, quiet enough not to compete. */
/* top must clear the pinned nav band (68px) or the rail's own title and first entry
   scroll underneath it and are simply not there for the reader — seen on /privacy and
   /security in the browser, 2026-08-12. */
.legal-index { position: sticky; top: calc(68px + 28px); border-left: 1px solid var(--line); padding-left: 18px; }
.legal-index-title { font-family: var(--mono); font-size: var(--t-label); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--mute); margin: 0 0 12px; }

/* ==========================================================================
   Security page: the route diagram
   --------------------------------------------------------------------------
   Drawn as SVG with design-system classes rather than presentation attributes,
   so the boxes take the same paper, hairline and accent as the rest of the site
   and follow the theme instead of carrying baked-in colours. The one accent on
   the page is the sealed enclave — it is the point of the picture.
   ========================================================================== */
/* The diagram is the argument of the page, so it gets the full frame rather than the
   62-character reading measure — squeezed into the document column its labels were
   unreadable (seen on the lane, 2026-08-12). */
/* The one line that introduces the diagram sits with it, above the reading columns —
   it used to be stranded in a numbered section BELOW the picture it describes. */
/* Both take the SAME measure as .legal-head and the document below, or the page stacks two
   different left margins: the intro line started at the frame edge while every other line
   on the page started 120px further in (seen in the browser, 2026-08-12). */
.legal-lede-tight { color: var(--ink-2); font-size: var(--t-lead); line-height: var(--lh-body); max-width: var(--doc-measure); margin: 22px auto 0; }
.sec-figure { max-width: var(--doc-measure); margin: 18px auto 34px; }
.sec-diagram { width: 100%; height: auto; display: block; }
.sec-node { fill: var(--surface); stroke: var(--line); stroke-width: 1; }
.sec-node-ours { fill: var(--surface-2); }
.sec-node-sealed { fill: var(--accent-soft); stroke: var(--accent); }
.sec-inner { fill: var(--surface); stroke: var(--line-soft); stroke-width: 1; }
.sec-node-title { fill: var(--ink); font-family: var(--display); font-size: 15px; text-anchor: middle; }
.sec-figure figcaption { margin-left: auto; margin-right: auto; text-align: center; }
.sec-node-sub { fill: var(--mute); font-size: 11px; text-anchor: middle; }
.sec-inner-title { fill: var(--ink-2); font-size: 11.5px; text-anchor: middle; }
.sec-inner-sub { fill: var(--mute); font-family: var(--mono); font-size: 11px; letter-spacing: .02em; text-anchor: middle; }
.sec-wire { stroke: var(--ink-2); stroke-width: 1.5; }
/* A flat line says "connected"; the page needs it to say "your words travel this way".
   Ink, not accent — the red stays reserved for the sealed endpoint (design review). */
.sec-arrow { fill: none; stroke: var(--ink-2); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.sec-wire-label { fill: var(--ink-2); font-family: var(--mono); font-size: 11px; letter-spacing: var(--ls-label); text-anchor: middle; }
.sec-wire-sub { fill: var(--mute); font-size: 11px; text-anchor: middle; }
.sec-figure figcaption { color: var(--mute); font-size: var(--t-small); line-height: var(--lh-dense); margin-top: 12px; max-width: 62ch; }

/* Pricing: the prose under the cards that explains what the money buys. */
.pricing-notes { margin: 34px auto 0; max-width: 640px; }
.pricing-notes h2 { font-family: var(--display); font-size: var(--t-title); line-height: var(--lh-title); letter-spacing: var(--ls-title); margin: 0 0 10px; }
.pricing-notes p { color: var(--ink-2); font-size: var(--t-body); line-height: var(--lh-body); margin: 0 0 12px; }
.pricing-notes a { color: var(--accent); }

/* ==========================================================================
   Contact form — the public way to reach a person. Replaces the mailto: links,
   one of which pointed at an address that never existed.
   ========================================================================== */
/* display:flex outranks the UA rule for [hidden], so hiding the form after a successful
   send left it on screen underneath the confirmation — both were visible at once (seen in
   the browser, 2026-08-12). */
.contact-form[hidden], .contact-done[hidden] { display: none; }
.contact-form { max-width: 640px; margin: 26px auto 0; display: flex; flex-direction: column; gap: 14px; }
.contact-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
@media (max-width: 620px) { .contact-row { grid-template-columns: 1fr; } }
.contact-form .fld { display: flex; flex-direction: column; gap: 5px; }
.contact-form .fld > span { font-family: var(--mono); font-size: var(--t-label); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--mute); }
.contact-form input, .contact-form select, .contact-form textarea {
  font-family: inherit; font-size: var(--t-body); color: var(--ink); background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r-sm); padding: 10px 12px; width: 100%; box-sizing: border-box;
}
.contact-form textarea { resize: vertical; line-height: var(--lh-body); }
.contact-form input:focus, .contact-form select:focus, .contact-form textarea:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
.contact-actions { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.contact-privacy { color: var(--mute); font-size: var(--t-small); }
.contact-note { margin: 0; font-size: var(--t-body-s); color: var(--mute); min-height: 1.2em; }
.contact-note.is-bad { color: var(--accent); }
.contact-done { max-width: 640px; margin: 26px auto 0; }
.contact-done h2 { font-family: var(--display); font-size: var(--t-title); letter-spacing: var(--ls-title); margin: 0 0 8px; }
.contact-done p { color: var(--ink-2); font-size: var(--t-body); line-height: var(--lh-body); margin: 0 0 10px; }
.contact-done a { color: var(--accent); }
/* The landing's security block ends in a way through to the full explanation. */
.public-security-more { display: inline-block; margin-top: 10px; color: var(--accent); font-size: var(--t-small); }
.legal-index ol { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 9px; }
.legal-index a { color: var(--mute); text-decoration: none; font-size: var(--t-small); line-height: var(--lh-title); display: block; }
.legal-index a:hover { color: var(--accent); }
/* The section a reader is actually in. The tick sits in the rail's own border line. */
.legal-index a.is-active { color: var(--ink); }
.legal-index a.is-active::before { content: ""; position: absolute; left: -19px; width: 2px; height: 1.05em; background: var(--accent); }
.legal-index a { position: relative; }

.legal-doc { max-width: 62ch; }
.legal-section { padding: 0 0 4px; }
.legal-section + .legal-section { margin-top: 34px; padding-top: 30px; border-top: 1px solid var(--line-soft); }
.legal-section h2 { font-family: var(--display); font-size: var(--t-title); letter-spacing: var(--ls-title); margin: 0 0 12px; color: var(--ink); }
.legal-section-no { display: block; font-family: var(--mono); font-size: var(--t-label); letter-spacing: var(--ls-label); color: var(--accent); margin-bottom: 7px; }
.legal-doc h3 { font-size: var(--t-title-s); font-weight: 600; margin: 20px 0 8px; color: var(--ink); }
.legal-doc p { color: var(--ink-2); line-height: var(--lh-body); margin: 0 0 14px; }
.legal-doc ul, .legal-doc ol { margin: 0 0 16px; padding-left: 20px; color: var(--ink-2); }
.legal-doc li { margin-bottom: 8px; line-height: var(--lh-body); }
/* Links in the lede take the same treatment as links in the document body. Without this
   the 404 page's contact address rendered in the browser's default blue and underline —
   the one piece of the public site that was not on the palette (seen on the lane,
   2026-08-12). */
.legal-doc a,
.legal-lede a { color: var(--accent); }
.legal-doc strong { color: var(--ink); font-weight: 600; }
.legal-doc table { border-collapse: collapse; width: 100%; margin: 0 0 18px; }
.legal-doc th, .legal-doc td { text-align: left; padding: 10px 14px 10px 0; border-bottom: 1px solid var(--line); vertical-align: top; color: var(--ink-2); }

@media (max-width: 1000px) {
  .legal-layout { grid-template-columns: 1fr; gap: 0; padding-top: 28px; }
  .legal-index { display: none; }
  .legal-doc { max-width: none; }
}

/* --- landing: what people ask -------------------------------------------
   Replaces the four abstract capability cards. A visitor learns more from the
   sentences people actually type than from a noun like "Cross-Platform Parity",
   so these are quoted asks with a plain answer underneath, in a dense two- or
   three-column list rather than tiles with icons. */
.public-asks { display: grid; grid-template-columns: repeat(auto-fit, minmax(285px, 1fr)); gap: 0 40px; margin: 0 0 40px; }
.public-ask { display: grid; grid-template-columns: auto 1fr; gap: 14px; padding: 16px 0; border-bottom: 1px solid var(--line); align-items: start; }
.public-ask-mark { font-family: var(--mono); font-size: var(--t-small); color: var(--accent); padding-top: 2px; }
.public-ask h3 { font-size: var(--t-body); font-weight: 600; color: var(--ink); margin: 0 0 3px; }
.public-ask p { font-size: var(--t-body-s); color: var(--mute); line-height: var(--lh-dense); margin: 0; }

/* --- landing: the answer specimen ---------------------------------------
   A rendered example of a real exchange, including the memory line the product
   actually emits, so the hero shows the thing working instead of describing it. */
.public-specimen { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-sm); overflow: hidden; }
.public-specimen-head { display: flex; align-items: center; gap: 8px; padding: 11px 16px; border-bottom: 1px solid var(--line-soft); font-family: var(--mono); font-size: var(--t-label); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--mute); }
.public-specimen-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); flex: 0 0 auto; }
.public-specimen-body { padding: 18px 16px 20px; display: flex; flex-direction: column; gap: 14px; }
.public-specimen-label { display: block; font-family: var(--mono); font-size: var(--t-label); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--mute); margin-bottom: 5px; }
.public-specimen-label.is-reply { color: var(--accent); }
.public-specimen-ask { font-size: var(--t-body); color: var(--mute); margin: 0; }
.public-specimen-reply { font-size: var(--t-body); color: var(--ink); margin: 0; }
.public-specimen-reply ul { margin: 9px 0 0; padding-left: 18px; color: var(--ink-2); }
.public-specimen-reply li { margin-bottom: 4px; }
.public-specimen-step { display: inline-flex; align-items: center; gap: 7px; align-self: flex-start; font-family: var(--mono); font-size: 0.68rem; color: var(--mute); background: var(--surface-2); border: 1px solid var(--line-soft); border-radius: var(--r-pill); padding: 5px 11px; }
.public-specimen-step svg { color: var(--accent); flex: 0 0 auto; }
.public-specimen-divider { border-top: 1px solid var(--line-soft); padding-top: 14px; }

/* --- landing: the enclave, with the product's own footage ----------------
   The sign-up screen's clip — pins and red thread drawing a graph on a pale
   wall — carried onto the marketing page. It is light already, so it needs no
   scrim: a radial mask fades its edges into the paper ground. Reduced-motion
   viewers get the poster frame, which is the same picture without the draw. */
.public-enclave { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 0.78fr); gap: 44px; align-items: center; margin: 0 0 40px; }
.public-enclave-copy p { color: var(--ink-2); font-size: var(--t-body); line-height: var(--lh-body); margin: 0 0 14px; max-width: 54ch; }
.public-enclave-pull { font-family: var(--display); font-size: var(--t-title-s); color: var(--ink); margin-top: 18px; }
.public-enclave-stage { position: relative; border: 1px solid var(--line); border-radius: var(--r-sm); overflow: hidden; background: var(--surface); aspect-ratio: 1 / 1; width: 100%; }
.public-enclave-stage video { width: 100%; height: 100%; object-fit: cover; display: block; object-position: center 62%; }
.public-enclave-stage::after { content: ""; position: absolute; inset: 0; pointer-events: none; background: radial-gradient(120% 82% at 50% 45%, transparent 54%, var(--surface) 100%); }
.public-enclave-cap { font-family: var(--mono); font-size: var(--t-label); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--mute); margin: 10px 0 0; text-align: right; }
@media (prefers-reduced-motion: reduce) { .public-enclave-stage video { display: none; } }

/* --- landing: where your words go ---------------------------------------
   The security story as checkable statements. Replaces a banner titled after a
   consent notice ("Google OAuth & Data Privacy Protection"), which described
   the smallest part of it. */
.public-facts { width: 100%; border-collapse: collapse; font-size: var(--t-body); margin: 0 0 40px; }
.public-facts th, .public-facts td { text-align: left; vertical-align: top; padding: 14px 18px 14px 0; border-bottom: 1px solid var(--line); }
.public-facts th { font-family: var(--mono); font-weight: 500; font-size: var(--t-label); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--mute); width: 210px; }
.public-facts td { color: var(--ink-2); line-height: var(--lh-body); }
.public-facts tr:last-child th, .public-facts tr:last-child td { border-bottom: 0; }

@media (max-width: 860px) {
  .public-enclave { grid-template-columns: 1fr; gap: 28px; }
  .public-enclave-stage { max-width: 420px; margin: 0 auto; }
  .public-facts th { width: auto; display: block; padding-bottom: 4px; border-bottom: 0; }
  .public-facts td { display: block; padding-top: 0; }
}


/* --- mark environment: widget dashboard ----------------------------------
   Ported out of app code (2026-08-12). mark-desktop.js shipped its whole layout
   as inline styles, with its own comment saying it did so "to avoid immediately
   extending the CDS" — which failed the design-system gate and blocked every
   image build in the repo, not just its own lane. The patterns are fine; they
   just belong here. Colours come from tokens rather than the hardcoded #1a1a1a
   / #333 / #aaa fallbacks the prototype carried, so the screen follows the theme. */
.mark-desktop-container { padding: 2rem; height: 100%; box-sizing: border-box; overflow-y: auto; display: flex; gap: 2rem; flex-wrap: wrap; align-content: flex-start; background: var(--bg); }
.mark-widget { flex: 1 1 300px; background: var(--surface-2); border: 1px solid var(--line); border-radius: 16px; padding: 1.5rem; box-shadow: var(--shadow-sm); display: flex; flex-direction: column; gap: 1rem; }
.mark-widget-title { margin: 0; font-size: var(--t-title-s); font-weight: 600; display: flex; align-items: center; gap: 8px; color: var(--ink); }
.mark-widget-body { flex: 1; color: var(--mute); font-size: var(--t-body); line-height: var(--lh-dense); }
.mark-widget-line { margin-bottom: 1rem; }
.mark-widget-actions { display: flex; gap: 0.5rem; }
.mark-widget-focus { flex: 2 1 100%; background: var(--surface); border: 1px dashed var(--line); border-radius: 16px; padding: 2rem; min-height: 400px; display: flex; align-items: center; justify-content: center; flex-direction: column; color: var(--mute); }
.mark-widget-focus-icon { margin-bottom: 1rem; opacity: 0.5; }
.mark-widget-focus h2 { margin: 0; font-weight: 400; font-size: var(--t-title); color: var(--ink); }


/* --- telepathy: memory-grant dialog ---------------------------------------
   Ported out of app code (2026-08-12). telepathy.js carried its entire layout
   inline and named classes from a different design system (cds-card, cds-badge,
   cds-group), with hardcoded sky-blues (#38bdf8, #0284c7, #94a3b8, #0f172a) that
   exist nowhere in Unison — so the dialog rendered in another product's palette
   and failed the design-system gate, blocking every image build in the repo.
   Same markup and behaviour, on tokens. */
.telepathy-modal-body { display: flex; flex-direction: column; gap: 16px; }
.telepathy-grant-card { padding: 16px; border: 1px solid var(--accent); border-radius: var(--r-sm); background: var(--accent-soft); }
.telepathy-grant-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; gap: 12px; }
.telepathy-grant-title { font-weight: 600; font-size: var(--t-body); color: var(--ink); }
.telepathy-badge { background: var(--ink); color: var(--on-ink); padding: 2px 8px; border-radius: var(--r-xs); font-family: var(--mono); font-size: var(--t-label); letter-spacing: var(--ls-label); text-transform: uppercase; white-space: nowrap; }
.telepathy-grant-sub { font-size: var(--t-body-s); color: var(--mute); }
.telepathy-meta-row { display: flex; gap: 12px; margin-top: 10px; font-size: var(--t-small); color: var(--mute); flex-wrap: wrap; }
.telepathy-controls { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.telepathy-group { display: flex; flex-direction: column; gap: 10px; }
.telepathy-group-title { margin: 0; font-size: var(--t-body-s); font-weight: 600; color: var(--ink); }
.telepathy-row { display: flex; gap: 12px; align-items: center; }
.telepathy-field { margin-top: 8px; }
.telepathy-check { display: flex; align-items: center; gap: 8px; cursor: pointer; font-size: var(--t-body-s); color: var(--ink-2); }
.telepathy-input { padding: 8px 12px; border-radius: var(--r-xs); border: 1px solid var(--line); background: var(--surface); color: var(--ink); font: inherit; width: 100%; box-sizing: border-box; }
.telepathy-people { max-height: 140px; overflow-y: auto; display: flex; flex-direction: column; gap: 6px; }
.telepathy-person { padding: 8px 12px; border-radius: var(--r-xs); cursor: pointer; background: var(--surface-2); border: 1px solid transparent; display: flex; justify-content: space-between; align-items: center; gap: 12px; }
.telepathy-person:hover { border-color: var(--line); }
.telepathy-person.is-selected { background: var(--accent-soft); border-color: var(--accent); }
.telepathy-person-name { font-weight: 500; font-size: var(--t-body-s); color: var(--ink); }
.telepathy-person-mail { font-size: var(--t-label); color: var(--mute); }
.telepathy-person-flag { font-size: var(--t-label); font-family: var(--mono); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--accent); white-space: nowrap; }
.telepathy-preview { padding: 12px; background: var(--surface-2); border-radius: var(--r-xs); border: 1px solid var(--line); }
.telepathy-preview-title { font-family: var(--mono); font-size: var(--t-label); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--faint); margin-bottom: 6px; }
.telepathy-preview-body { margin: 0; font-family: var(--mono); font-size: var(--t-small); color: var(--ink-2); white-space: pre-wrap; }
.telepathy-result { padding: 10px 14px; border-radius: var(--r-xs); background: var(--surface-2); border: 1px solid var(--ok, var(--line)); color: var(--ink-2); font-size: var(--t-body-s); }
.telepathy-actions { display: flex; justify-content: flex-end; gap: 12px; margin-top: 8px; }
@media (max-width: 700px) { .telepathy-controls { grid-template-columns: 1fr; } }

.public-footer { border-top: 1px solid var(--line); padding: 28px 0 44px; display: flex; flex-direction: column; align-items: center; gap: 14px; width: 100%; text-align: center; }
.public-footer-links { display: flex; gap: 22px; flex-wrap: wrap; justify-content: center; }
.public-footer-link { color: var(--mute); text-decoration: none; font-size: var(--t-small); transition: color 0.15s; }
.public-footer-link:hover { color: var(--ink); }
.public-footer-copy { color: var(--mute); font-size: var(--t-small); }

@media (max-width: 860px) {
  /* The hero used to centre only on a phone, so the badge and the paragraph stayed flush left
     while the headline between them sat centred — three stacked elements, two edges. Everything
     keeps the one left edge at every width. */
  .public-hero-split { grid-template-columns: 1fr; gap: 30px; padding: 32px 0 36px; }
  .public-hero-subtitle { margin-inline: 0; }
  .public-hero-actions { justify-content: flex-start; }
}
@media (max-width: 640px) {
  .public-nav-bar { height: auto; flex-wrap: wrap; row-gap: 10px; padding: 12px 0; }
  /* Ben, 2026-08-18 (phone review): the grid below was still seven boxes tall enough to fill
     the first screen of every page before a word of content. The links now live behind the
     menu button and open on demand; closed is the default, so what a phone shows first is the
     page itself. */
  /* Scoped to .has-nav-menu — a band whose toggle is really there. Without the class (script
     blocked, or a page not yet carrying public-nav.js) the links keep the old stacked grid
     rather than vanishing with no way to open them. */
  .public-nav-menu-toggle { display: inline-flex; align-items: center; justify-content: center; order: 2; }
  .public-nav-links { order: 3; width: 100%; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 4px 10px; justify-content: stretch; }
  .public-nav-links .public-nav-link { display: flex; align-items: center; justify-content: center; min-height: 40px; text-align: center; font-size: var(--t-small); line-height: 1.15; }
  .has-nav-menu .public-nav-links { display: none; }
  .has-nav-menu .public-nav-links.is-open { order: 3; width: 100%; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 4px 10px; justify-content: stretch; }
  .has-nav-menu .public-nav-links.is-open .public-nav-link { min-height: 44px; }
}

/* The consent gate sits immediately above the button it gates, with room of its own so it
   reads as a decision rather than as a caption on the field above. */
.auth-consent-row { display: flex; gap: 8px; align-items: flex-start; font-size: 12px; line-height: 1.45; margin: 14px 0 10px; }
/* A right-aligned quiet action bound to the field directly above it (forgot password). */
.auth-field-action { display: flex; justify-content: flex-end; margin: -4px 0 12px; }
/* One quiet closing line for the least-frequent route out of this card (join the waitlist),
   sitting after the sign-in options rather than interrupting the form. */
.auth-footnote { display: flex; justify-content: center; align-items: baseline; gap: 6px; margin: 18px 0 2px; font-size: 12px; color: var(--mute); }
.auth-footnote + .auth-legal { margin-top: 16px; }
.auth-consent-check { margin-top: 2px; }
.auth-trust-row { justify-content: center; margin-top: 14px; }
/* Card definition against the paper hero background: the card's own opaque surface +
   default DS shadow already separate it from the page, but the hairline border needed a
   touch more weight to read clearly. Token-based (--line-soft -> a slightly stronger
   ink-mixed line), not a one-off hex. */
.auth-page .authc { border-color: color-mix(in srgb, var(--ink) 12%, var(--line)); }

/* Chat surface */
.memory-extras { margin: 2px 0 20px; }
.msg-icon-line { display: inline-flex; align-items: center; gap: 6px; }
.attestation-detail { margin-top: 6px; font-family: var(--mono); font-size: 11px; white-space: pre-wrap; }
.file-input-hidden { display: none; }

/* YUM-UX-1: upload bubble in-place status chip (uploading -> uploaded/error) */
.upload-state-chip {
  font-family: var(--mono); font-size: 11px; margin-left: 6px; padding: 1px 6px;
  border-radius: 999px; border: 1px solid var(--line); color: var(--mute); white-space: nowrap;
}
.upload-state-uploading { color: var(--mute); }
.upload-state-uploaded { color: var(--accent, #B23A34); border-color: var(--accent, #B23A34); }
.upload-state-error { color: #b3261e; border-color: #b3261e; }

/* YUM-UX-2: quick-reply chips under a yes/no (or "A or B?") assistant question */
.quick-reply-row { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 8px; }
.quick-reply-chip {
  font-family: var(--mono); font-size: 12px; padding: 5px 12px; border-radius: 999px;
  border: 1.5px solid var(--accent, #B23A34); color: var(--accent, #B23A34); background: none;
  cursor: pointer; transition: background-color .15s, color .15s;
}
.quick-reply-chip:hover { background: var(--accent, #B23A34); color: var(--surface, #fff); }
.quick-reply-chip:disabled { opacity: .5; cursor: default; }

/* C2 item 12: smart option pills -- INLINE inside the bubble, right after the answer text
   (same visual language as .quick-reply-chip, different placement/trigger). */
.inline-option-row { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 8px; }
.inline-option-pill {
  font-family: var(--mono); font-size: 12px; padding: 5px 12px; border-radius: 999px;
  border: 1.5px solid var(--accent, #B23A34); color: var(--accent, #B23A34); background: none;
  cursor: pointer; transition: background-color .15s, color .15s;
}
.inline-option-pill:hover { background: var(--accent, #B23A34); color: var(--surface, #fff); }
.inline-option-pill:disabled { opacity: .5; cursor: default; }

/* Follow-up item (Ben 2026-07-22): web search consent banner -- dense, inline in the bubble,
   not a native dialog. Reuses .inline-option-pill for its three buttons. */
.websearch-consent-banner {
  margin-top: 8px; padding: 10px 12px; border: 1.5px solid var(--accent, #B23A34);
  border-radius: 10px; background: var(--accent-soft, rgba(178,58,52,.06));
}
.websearch-consent-text { margin: 0 0 8px; font-size: 13px; }
.websearch-consent-actions { display: flex; gap: 6px; flex-wrap: wrap; }

/* Follow-up item 2 (Ben 2026-07-22): file-mention pills inside a reply bubble -- opens the
   file via the SAME relative /api/files|uploads path Storage's own "open" link uses. */
.file-mention-pill {
  display: inline-flex; align-items: center; gap: 4px;
  font-family: var(--mono); font-size: 11.5px; padding: 2px 9px 2px 7px; border-radius: 999px;
  border: 1.5px solid var(--accent, #B23A34); color: var(--accent, #B23A34);
  text-decoration: none; white-space: nowrap;
}
.file-mention-pill:hover { background: var(--accent, #B23A34); color: var(--surface, #fff); }
.file-mention-pill-icon { flex: 0 0 auto; }

/* Follow-up item 3 (Ben 2026-07-22): model-switch modals (replaces window.confirm/alert). */
.model-switch-modal-body p { margin: 0 0 14px; }
.model-switch-modal-actions { display: flex; gap: 8px; justify-content: flex-end; }

/* YUM-UX-3: meta-row Copy/Retry icon buttons — brand red, ordered lock -> what-yarnball-did
   -> context chip -> icons by the JSX order in chat.js, this rule only owns their look. */
.meta-icon-row { display: flex; align-items: center; gap: 2px; margin-left: auto; }
.meta-icon-btn {
  display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px;
  border-radius: 8px; border: 1px solid var(--line); background: var(--surface); padding: 0; cursor: pointer;
  color: var(--accent); line-height: 0;
}
.meta-icon-btn:hover { background: var(--surface-2); border-color: var(--accent); }
.meta-icon-btn:disabled { opacity: .4; cursor: default; }
.meta-icon-btn.is-copied { color: var(--mute); border-color: var(--line-soft); }

/* YUM-UX-4: the attestation lock is the first, most prominent meta-row icon.
   YUM-206: recolored from the brand-red accent to the scoped --trust-green exception
   (same token the header model-lock/security-claim rows use) — a lock + a green tick
   reads as "secure", not as an urgent/error color. A small round tick badge sits on the
   lock's corner so the mark is unambiguous at a glance, with the full technical receipt
   staying one click away in the modal (unchanged). */
.attestation-lock-prominent { position: relative; color: var(--trust-green); width: 26px; height: 26px; }
.attestation-lock-prominent:hover, .attestation-lock-prominent[aria-expanded="true"] { color: var(--trust-green); opacity: .75; }
.attestation-lock-tick {
  position: absolute; right: -1px; bottom: -1px; width: 11px; height: 11px; border-radius: 50%;
  background: var(--trust-green); display: inline-flex; align-items: center; justify-content: center;
  box-shadow: 0 0 0 2px var(--surface, #fff); color: var(--surface, #fff);
}
.attestation-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.attestation-table td, .attestation-table th { padding: 5px 10px; border-bottom: 1px solid var(--line-soft); text-align: left; vertical-align: top; }
.attestation-table td:first-child, .attestation-table th:first-child { color: var(--mute); white-space: nowrap; font-family: var(--mono); font-size: 11px; }

/* YUM-UX-5: composer paperclip/mic icons in brand red (attach/mic). ITEM C (2026-07-24):
   the think-toggle button that used to live here (and its .on rule) moved to the header's
   model pill — see .model-toggle-think above. */
.composer-icon-red { color: var(--accent, #B23A34); }
.composer-icon-red:hover { opacity: .8; }
/* YUM-261: attach/voice/brain are too large at the base .clip size (34px) — this compact
   modifier (28px, dropping to 26px on the mobile box-compact form) is applied to all 3 so
   they stay equal-sized. Icon glyph size is reduced to match at the call sites (16 instead
   of 20). */
.clip.clip--sm { width: 28px; height: 28px; }
.box-compact .clip.clip--sm { width: 26px; height: 26px; }
/* Redline p01#5 (Ben, 2026-09-26): "make this more prominent" — the composer's "+" is the
   entry point to attach/think/remember, not a minor icon; it goes back above the base .clip
   size (34px) instead of the YUM-261 compact 28px, plus a subtle accent-soft fill so it
   reads as the primary control at the front of the composer, not an afterthought. */
.composer-plus-prominent { width: 40px; height: 40px; background: var(--accent-soft); }
.composer-plus-prominent:hover { background: var(--accent-soft); opacity: 1; color: var(--accent); }
.box-compact .composer-plus-prominent { width: 34px; height: 34px; }
/* Brain (think) toggle "on" state — same accent-fill + soft halo the header's own
   think-toggle-btn.on uses (ITEM C, above), so the two entry points for the identical
   sticky flag read as the same control. */
.composer-icon-red.is-on {
  background: var(--accent, #B23A34);
  color: var(--on-accent, #fff);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent, #B23A34) 14%, transparent);
}
.composer-icon-red.is-on:hover { opacity: 1; }
/* C1 item 4 (Ben 2026-07-22): top-nav equivalent of .composer-icon-red -- every top-nav icon
   is red by default; `.active`/`aria-current` state still layers accent-soft background via
   the existing `.icbtn.active` DS rule, unaffected by this. */
.nav-icon-red { color: var(--accent, #B23A34); }
/* YUM-1470 (T8a): the old `:hover { opacity: .8 }` fade is removed. Hovering a top-bar icon
   now paints the shared soft-red background highlight (declared once near the end of this
   file), which is the same treatment the current page gets, drawn lighter. */

/* Redline p01#3 (Ben, 2026-09-26): "remove the frames around all the icons/symbols" — every
   RoundIconButton (.icbtn: Work, Chief of staff, Settings) and the labelled Chat stop
   (.topnav-chat, defined in layouts.css) in the persistent top bar loses its visible box
   border and drop shadow, active state included — a background tint is enough to show
   state without drawing a frame. Scoped to `.top` only: every other .icbtn elsewhere in the
   app (mail toolbars, settings rows, etc.) is untouched, and the XP theme's titlebar
   caption-button skin (xp.css `[data-theme="xp"] .top .icbtn`) still wins there on
   selector specificity, so that deliberate skin is unaffected. The standalone security
   lock (.model-lock-standalone, above) was already frameless. */
.top .icbtn,
.top .topnav-chat {
  border-color: transparent;
  background: transparent;
  box-shadow: none;
}
.top .icbtn:hover,
.top .topnav-chat:hover {
  background: var(--surface-2);
  border-color: transparent;
}
.top .icbtn.active,
.top .topnav-chat.active {
  background: var(--accent-soft);
  border-color: transparent;
}

/* Redline p01#1 (Ben, 2026-09-26): "remove this line" — the masthead rule letterpress-app.css
   draws under `.top` (that file is not owned by this pass; letterpress-app.css's own comment
   calls it "the top bar is a masthead: a rule under it, nothing else" — Ben's live redline
   supersedes that). Same selector specificity as that rule and it loads after this file, so
   only !important reliably wins the cascade without editing that stylesheet. */
[data-theme="letterpress"] .top { border-bottom: none !important; }

/* Redline p10#2 (Ben, 2026-09-26): "Logo always in the top left corner" — `.top` already
   sits outside the one scrollable region (.yarnball-main, in the shell's fixed-height flex
   column — see _ds_bundle.css .yarnball-shell/.yarnball-main), so it never scrolls out of
   view today. Made explicit/robust here rather than left as an implicit consequence of
   layout order, so no future screen's own content can push it off. z-index kept low
   (well under the attach-menu backdrop's 45 and modal layers) so dropdowns/modals still
   layer correctly above it. */
.yarnball-shell > .top {
  position: sticky;
  top: 0;
  z-index: 5;
  background: var(--bg);
}

/* Redline p01#6/#7 (Ben, 2026-09-26): "make this one input window" + "remove frames" — in
   the letterpress theme (letterpress-app.css, not owned by this pass) the composer is an
   underline field with a separate top rule above it and a squared-off send button, not one
   framed box. These overrides restore the single bordered "window" (same shape the base,
   non-themed .box already uses in _ds_bundle.css) so +, the text field, mic and send all
   read as one control, drop the .compwrap top rule, and round the send button off from a
   square back to the base circular shape. `.yarnball-app[data-theme="letterpress"]` beats
   letterpress-app.css's plain `[data-theme="letterpress"]` prefix on specificity regardless
   of stylesheet load order, so no !important is needed here. */
.yarnball-app[data-theme="letterpress"] .box {
  border: 2px solid var(--ink);
  border-radius: var(--r-pill);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  padding: 6px 6px 6px 14px;
}
.yarnball-app[data-theme="letterpress"] .box:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 12%, transparent), var(--shadow-sm);
}
/* Critique 2026-10-07: the borderless composer from redline 2026-10-01 read as bare
   placeholder text floating on wide desktop screens. The composer is a visible input again,
   framed like the other text inputs (.fld input in _ds_bundle.css: 1px line border, small
   radius, surface fill) and spanning the full width of the .compwrap content column. Phones
   keep the frameless field (mobile-ergonomics.css, max-width 600px). The letterpress prefix
   is repeated so its underline treatment above does not win. */
.yarnball-app .box,
.yarnball-app[data-theme="letterpress"] .box {
  width: 100%;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
  box-shadow: inset 0 1px 2px rgba(14,17,22,.03);
  padding: 6px 6px 6px 14px;
}
.yarnball-app .box:focus-within,
.yarnball-app[data-theme="letterpress"] .box:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 12%, transparent);
}

.yarnball-app[data-theme="letterpress"] .compwrap {
  border-top: 0;
}
.yarnball-app[data-theme="letterpress"] .send {
  border-radius: 50%;
}

/* ITEM A (2026-07-24): single-paperclip attach menu — compact anchored popover (not a full
   RecordModal; this is a quick 4-row tap menu). .attach-menu-anchor is the positioning
   context (wraps the paperclip IconButton); the backdrop is a transparent full-viewport
   click-catcher that closes the menu on any outside click/tap. Opens UPWARD (bottom-anchored)
   since the composer sits at the bottom of the viewport on every screen size. */
/* Critique 2026-10-07: the composer now has a full-width frame, and a menu anchored on the
   "+" button sat indented by the frame's left padding. The form (.box) is the positioning
   context instead, so the menu's left edge lines up with the frame's left edge (left:-1px
   covers the 1px border) and sits a small gap above it. The "+" button keeps its own
   context for the deep-thinking corner dot. */
.box { position: relative; }
.composer-plus-prominent { position: relative; }
.attach-menu-anchor { position: static; display: inline-flex; }
.attach-menu-backdrop { position: fixed; inset: 0; z-index: 45; background: transparent; }
.attach-menu {
  position: absolute;
  bottom: calc(100% + 6px);
  left: -1px;
  z-index: 46;
  display: flex;
  flex-direction: column;
  min-width: 200px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow);
  padding: 6px;
  gap: 2px;
}
.attach-menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: left;
  border: 0;
  background: transparent;
  padding: 8px 10px;
  border-radius: var(--r-xs, 6px);
  font: inherit;
  font-size: 13px;
  color: var(--ink-2);
  cursor: pointer;
}
.attach-menu-item:hover { background: var(--surface-2); color: var(--ink); }
.attach-menu-icon { color: var(--accent, #B23A34); flex: none; }

/* ITEM D (2026-07-24): the merged "Chat" nav icon opens a small anchored panel — New chat
   action + recent-history list (same /api/personal/conversations data History's Settings
   view uses). Same anchored-popover pattern as .attach-menu above, right-aligned since the
   trigger icon sits at the right end of the top nav row. */
.chat-nav-anchor { position: relative; display: inline-flex; }
.chat-nav-backdrop { position: fixed; inset: 0; z-index: 45; background: transparent; }
.chat-nav-panel {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 46;
  display: flex;
  flex-direction: column;
  width: min(320px, calc(100vw - 32px));
  max-height: min(70vh, 480px);
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow);
  padding: 6px;
  gap: 2px;
}
.chat-nav-panel-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: left;
  border: 0;
  background: transparent;
  padding: 8px 10px;
  border-radius: var(--r-xs, 6px);
  font: inherit;
  font-size: 13px;
  color: var(--ink-2);
  cursor: pointer;
}
.chat-nav-panel-item:hover { background: var(--surface-2); color: var(--ink); }
.chat-nav-panel-new { font-weight: 700; color: var(--accent, #B23A34); }
.chat-nav-panel-icon { flex: none; color: var(--accent, #B23A34); }
.chat-nav-panel-label {
  font-family: var(--mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--faint);
  padding: 8px 10px 4px;
}
.chat-nav-panel-item-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-nav-panel-item-time { flex: none; font-family: var(--mono); font-size: 11px; color: var(--faint); }
.chat-nav-panel-empty { padding: 10px; font-size: 12.5px; color: var(--mute); }
/* Which project a recent chat belongs to, shown on its row in the chats menu so a project
   conversation can be picked up from there (Ben, 2026-08-03). */
.chat-nav-panel-item-project {
  flex: none;
  max-width: 40%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--accent);
}

/* YUM-UX-6: voice-connecting cancel control */
.voice-cancel-btn {
  font-family: var(--mono); font-size: 12px; padding: 7px 16px; border-radius: 999px;
  border: 1.5px solid var(--accent, #B23A34); color: var(--accent, #B23A34); background: none;
  cursor: pointer; margin-top: 10px;
}
.voice-cancel-btn:hover { background: var(--accent, #B23A34); color: var(--surface, #fff); }

/* YUM-UX-7: files-for-this-chat popup table */
.files-popup-table th { font-family: var(--mono); font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--faint); }
/* C2 item 10: "From other chats" section + its per-row "Add to this chat" action */
.files-popup-section-label { font-family: var(--mono); font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--faint); margin: 14px 0 4px; }
.files-popup-attach-btn {
  font-family: var(--mono); font-size: 11px; padding: 3px 9px; border-radius: 999px;
  border: 1.5px solid var(--accent, #B23A34); color: var(--accent, #B23A34); background: none;
  cursor: pointer; white-space: nowrap;
}
.files-popup-attach-btn:hover { background: var(--accent, #B23A34); color: var(--surface, #fff); }
.files-popup-attach-btn:disabled { opacity: .5; cursor: default; }

/* Feedback surface */
.feedback-status { font-family: var(--mono); font-size: 11px; color: var(--faint); margin-bottom: 10px; }
.feedback-preview-box { border: 1px solid var(--line); border-radius: 7px; padding: 8px 10px; margin-bottom: 10px; max-height: 150px; overflow: auto; }
.feedback-kicker { font-family: var(--mono); font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--faint); margin-bottom: 6px; }
.feedback-preview-line { font-size: 12px; margin-bottom: 4px; }
.feedback-console-tail { font-size: 11px; margin: 0; white-space: pre-wrap; color: var(--faint); }
.feedback-empty-note { font-size: 11px; color: var(--faint); margin: 0; }
.feedback-consent-row { display: flex; gap: 6px; align-items: flex-start; font-size: 12px; margin-bottom: 10px; }
.feedback-consent-checkbox { margin-top: 2px; }

/* Modals surface (automations / schedule / webhooks / notifications / invites) */
.recap-results { margin-top: 12px; }
.recap-list { margin: 0; padding-left: 18px; line-height: 1.7; }
.recap-meta { margin-top: 10px; }
.select-inherit { font: inherit; }
.btn-compact { padding: 4px 10px; }
.srv-stack { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
.srv-stack.is-tight { gap: 4px; }
.field-row { display: flex; align-items: center; gap: 8px; width: 100%; }
.webhook-url-field { font-family: var(--mono); font-size: 11px; }
.run-row-clickable { cursor: pointer; }
.field-full { width: 100%; margin-bottom: 6px; }
.automation-schedule-row { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
/* Time-of-day input for a scheduled automation. Narrow by content: a time field sized like
   a text field reads as free text and invites typing prose into it. */
.schedule-time { width: 88px; font: inherit; }
/* Weekday picker for a schedule set to specific days. Semantic component rather than a row
   of generic buttons, so day selection looks the same everywhere it appears. */
.weekday-picker { display: inline-flex; gap: 2px; }
.weekday-picker > button {
  font: inherit; font-size: 11px; line-height: 1; padding: 4px 6px; min-width: 26px;
  border: 1px solid var(--line); background: transparent; color: var(--mute);
  border-radius: 3px; cursor: pointer;
}
.weekday-picker > button.is-on { color: var(--ink); border-color: var(--ink); font-weight: 600; }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%; }
.notif-prefs-row { display: flex; gap: 14px; }
.invite-channel-email { display: flex; align-items: center; gap: 7px; font-size: 12px; color: var(--mute); margin: 4px 0 6px; }
.invite-channel-sms { display: flex; align-items: center; gap: 7px; font-size: 12px; color: var(--mute); margin-bottom: 6px; }
.invite-channel-whatsapp { display: flex; align-items: center; gap: 7px; font-size: 12px; color: var(--mute); margin-bottom: 10px; }
.invite-result { margin-top: 10px; font-size: 12px; }
.invite-link-text { font-family: var(--mono); word-break: break-all; }
.invite-list-wrap { margin-top: 14px; }
.invite-row-label { min-width: 0; }
.invite-status-open { color: var(--teal); }
.invite-code { font-family: var(--mono); font-size: 11px; color: var(--faint); flex: none; margin-left: 10px; }

/* Settings surface (detailed sub-views) */
.select-native {
  font: inherit;
  border: 1px solid var(--line);
  background: var(--surface);
  border-radius: var(--r-sm);
  padding: 10px 12px;
  color: var(--ink);
  outline: none;
  box-shadow: inset 0 1px 2px rgba(14,17,22,.03);
}
.select-native:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 12%, transparent);
}
.range-row { display: flex; align-items: center; gap: 8px; }
.range-slider { width: 100px; }
.truncate-inline { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 2026-07-30 (YUM design audit): .truncate-inline alone cannot take effect on a flex child of
   .setrow — see the min-width:0 fix on .setrow itself in _ds_bundle.css (the SettingsRow
   component), which is the actual reason a truncated value was hard-clipping instead of
   ellipsising. Left here as a pointer since this is where the class most people will look. */
/* The signed-in account's own email address, shown as a settings-row value. It is never
   abbreviated: Ben, 2026-08-15, reported the address in the app looking "outdated/partial",
   and .truncate-inline is the reason a long address reads as partial — an email has no spaces
   to break at, so white-space:nowrap plus a narrow row ellipsises it mid-address and the user
   cannot tell a clipped address from a wrong one. This wraps instead, at any character, and
   keeps the whole thing on screen. */
.settings-value-email { overflow-wrap: anywhere; word-break: break-word; font-size: calc(0.92rem * var(--app-font-scale, 1)); font-weight: 500; color: var(--ink); }
.vault-caption { margin-bottom: 8px; font-family: var(--mono); font-size: 11px; }
.field-flush { margin-bottom: 0; }
.row-split-start { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; }
.clamp-2-lines { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.meta-sub { margin-top: 2px; font-size: 11px; }
.vault-frame { display: block; width: 100%; height: min(78vh, 900px); min-height: 480px; border: 1px solid var(--line); border-radius: 8px; background: var(--surface); }
.vault-actions-row { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.inline-checkbox { margin-left: 8px; }
/* YUM-UX-B13 item 5: brand-red checked state for skill checkboxes (Connections toggles already use .conn-switch-input:checked -- same accent, checkbox form factor here since these are dense list rows, not switches). One control family: both consume var(--accent). */
.skill-checkbox { accent-color: var(--accent); }
.field-block { width: 100%; }
.disclosure-body { margin-top: 8px; display: flex; flex-direction: column; gap: 6px; }
.usage-meter-row { margin-top: 10px; }
.section-header-row { display: flex; justify-content: space-between; align-items: center; gap: 8px; margin-bottom: 8px; }
.conn-empty-row { display: flex; justify-content: center; padding: 6px 0; }
.picker-row-btn { width: 100%; text-align: left; }
/* Ben 2026-08-08 (explicit): "+ Connect another account" must be the loudest control in
   Accounts — solid brand accent, the screen's single filled action. */
.section-gap-18 .picker-row-btn {
  background: var(--accent, #B23A34);
  border-color: var(--accent, #B23A34);
  color: var(--on-accent, #fff);
  font-weight: 700;
  font-size: 13.5px;
  text-align: center;
  padding: 10px 16px;
  border-radius: 10px;
}
.section-gap-18 .picker-row-btn:hover:not(:disabled) {
  background: color-mix(in srgb, var(--accent) 88%, var(--ink));
  border-color: color-mix(in srgb, var(--accent) 88%, var(--ink));
  color: var(--on-accent, #fff);
}
.field-row-6 { display: flex; gap: 6px; }
.field-basis-90 { flex: 0 0 90px; }
.file-pick-row { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 10px; }
.field-grow-140 { flex: 1 1 auto; min-width: 140px; }
.stack-gap-top { margin-top: 8px; }
.pairing-row { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 6px; }
.srv-stack-stretch { flex-direction: column; align-items: stretch; gap: 6px; }
.row-split { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
.truncatable-flex { min-width: 0; }
.flex-static { flex: none; }
.connector-fields { display: flex; flex-direction: column; gap: 6px; margin-top: 9px; }
.inline-edit-row { display: flex; gap: 8px; align-items: center; }
.row-actions-tight { display: flex; gap: 10px; align-items: center; flex: none; }
.instructions-row { display: flex; gap: 8px; align-items: center; margin-top: 6px; }
.section-gap-18 { margin-top: 18px; }
.settings-wrap-wide { max-width: 1200px; margin-inline: auto; width: 100%; padding-bottom: 24px; padding-inline: 16px; }
@media (max-width: 600px) { .settings-wrap-wide { padding-inline: 12px; } }
.mt-4-block { margin-top: 4px; }

/* Security-path surface */
.sec-panel { padding: 14px 16px; }
.sec-verify.is-verifying { margin-right: 6px; }
.sec-err { margin-top: 8px; }
.sec-rows { margin-top: 12px; }
/* Redline p02#3 (Ben, 2026-09-26): "less space" between the e2e-only checkbox panel and
   this row — margin-top down from 16px. */
.sec-actions { margin-top: 8px; display: flex; justify-content: space-between; align-items: center; gap: 12px; }
/* Redline p02#1: the "Check again" button (Button variant="solid" -> .btn.solid) matches
   the dialog's 6px radius instead of the base pill shape (--r-pill). */
.sec-actions .btn { border-radius: 6px; }
.sec-privacy-link-btn {
  background: transparent;
  border: 0;
  color: var(--mute);
  font-family: var(--ui);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  padding: 6px 10px;
  border-radius: var(--r-sm, 6px);
  transition: color 0.15s ease, background-color 0.15s ease;
}
.sec-privacy-link-btn:hover {
  color: var(--ink);
  background: var(--surface-2);
}
.sec-privacy-link-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ============================================================================
   YumYum admin console (admin.js) — semantic role classes (YUMI-CDS-ADMINJS) */
.admin-create-row { display: flex; gap: 8px; align-items: center; padding: 12px 15px; flex-wrap: wrap; }
.admin-add-row { display: flex; gap: 8px; margin-top: 12px; align-items: center; }
.admin-ask-row { display: flex; gap: 8px; padding: 12px 15px; align-items: center; }
.admin-field-row-top { display: flex; gap: 8px; align-items: flex-start; }
.admin-comment-row { display: flex; gap: 8px; align-items: flex-start; margin: 4px 0 8px; }
.admin-modal-actions { display: flex; gap: 8px; margin-top: 12px; }
.admin-field-w80 { width: 80px; }
.admin-field-w90 { width: 90px; }
.admin-field-w100 { width: 100px; }
.admin-field-w110 { width: 110px; }
.admin-field-w120 { width: 120px; }
.admin-field-w130 { width: 130px; }
.admin-field-w140 { width: 140px; }
.admin-field-w150 { width: 150px; }
.admin-field-w160 { width: 160px; }
.admin-field-w180 { width: 180px; }
.admin-field-w200 { width: 200px; }
.admin-field-w220 { width: 220px; }
.admin-field-w240 { width: 240px; }
.admin-field-w260 { width: 260px; }
.admin-field-w300 { width: 300px; }
.admin-field-w320 { width: 320px; }
.admin-field-w340 { width: 340px; }
.admin-field-w360 { width: 360px; }
.admin-field-flex { flex: 1; }
.admin-textarea-full { width: 100%; min-width: 320px; font: inherit; }
.admin-textarea-full-pre { width: 100%; min-width: 320px; font: inherit; white-space: pre-wrap; }
.admin-textarea-flex { flex: 1; min-width: 320px; font: inherit; }
.admin-subhead { margin-top: 18px; margin-bottom: 6px; font-weight: 600; }
.admin-note { margin: 0 0 10px; }
.admin-note-indented { margin: 2px 0 10px; }
.admin-note-loose { margin: 0 0 14px; }
.admin-note-tight { margin: 0 0 6px; }
.admin-note-flush { margin: 0; }
.admin-note-top { margin: 10px 0 0; }
.admin-note-mt10 { margin-top: 10px; }
.admin-note-mt4 { margin-top: 4px; }
.admin-note-xs { margin: 0 0 4px; }
.admin-note-inset { margin: 0 15px 8px; }
.admin-legend-note { margin: 0 0 10px; font-size: 12px; line-height: 1.5; }
.admin-panel-note { margin: 0; padding: 12px 15px; }
.admin-panel-pad-bottom { padding: 0 15px 12px; }
.admin-geo-note { font-size: 11px; margin: 6px 0 10px; }
.admin-inline-note { margin: 2px 0; }
.admin-pre-flush { margin: 0; white-space: pre-wrap; }
.admin-block-gap { margin: 4px 0 8px; }
.admin-action-spacer { margin-top: 14px; }
.admin-delete-row { margin-top: 12px; }
.admin-label-gap { margin-right: 14px; }
.admin-inline-hint { margin-left: 8px; }
.admin-links-block { margin-top: 4px; }
.admin-link-block { display: block; word-break: break-all; }
.admin-cell-sm { font-size: 11px; }
.admin-cell-wrap { white-space: pre-wrap; max-width: 420px; }
.admin-cell-pre { white-space: pre-wrap; }
.admin-feedback-shot { max-width: 360px; max-height: 240px; border: 1px solid var(--line); border-radius: 6px; }
.admin-pre-block { max-height: 200px; overflow: auto; font-size: 11px; white-space: pre-wrap; }
.admin-pre-block-lg { max-height: 240px; overflow: auto; font-size: 11px; white-space: pre-wrap; }
.admin-marketing-subnav { position: sticky; top: 0; z-index: 5; display: flex; gap: 12px; flex-wrap: wrap; padding: 8px 0; margin-bottom: 10px; background: var(--bg); border-bottom: 1px solid var(--line); font-size: 12px; }
.admin-invite-preview-frame { width: 100%; height: 320px; border: 1px solid var(--line); border-radius: 4px; background: var(--surface); margin-top: 10px; }
.admin-invite-check-label { display: flex; gap: 4px; align-items: center; font-size: 12px; }
.admin-invite-result { margin: 0 15px 12px; font-size: 12px; }
.admin-table-scroll { overflow-x: auto; }
.admin-login-page { min-height: 100dvh; display: flex; align-items: center; justify-content: center; padding: 28px; }
.admin-login-box { width: 400px; }
.admin-login-footer { margin-top: 12px; font-size: 12px; text-align: center; }
.admin-login-back-link { color: var(--mute); }

.auth-legal { margin: 14px 0 0; text-align: center; font-size: 11px; letter-spacing: 0.02em; color: var(--muted, #8a8f98); }

/* C1: connector memory-bank ingestion controls */
.conn-memory-row { display: inline-flex; align-items: center; gap: 8px; margin-left: 10px; font-size: 12px; color: var(--mute, #8a8f98); }
.conn-memory-toggle { display: inline-flex; align-items: center; gap: 4px; cursor: pointer; }

/* Shell rail align (2026-08-05): reserve the scrollbar gutter symmetrically on the
   real scrollport. Non-scrolling rails ignore scrollbar-gutter, so compensating there
   does not move the actual content column. */
.yarnball-shell > .yarnball-main {
  scrollbar-gutter: stable both-edges;
}

/* alignfix 2026-07-21: chat column must span the full card width so assistant rows
   start at the composer's left edge and user rows end at its right edge. */
.yarnball-main > .card { width: 100%; max-width: 1040px; }
.yarnball-main .card .row { width: 100%; }

/* fixwave 2026-07-21 */
.model-slider-label { font-family: var(--mono); font-size: 11px; letter-spacing: .02em; color: var(--faint); white-space: nowrap; }
.model-slider-label.is-current { color: var(--ink); font-weight: 700; }
.skill-desc { color: var(--mute); font-size: 12px; margin-right: 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 46ch; }

/* YUM-243/269/275 (2026-07-21): single bottom meta row inside assistant bubbles; hover
   bar (user/memory rows) lives out of flow so appearing never shifts the transcript. */
/* C2 item 11 regression fix: this row was wrapping onto 2+ lines and left-aligning once it
   picked up enough chips (lock + what-yarnball-did + ctx chip + copy/retry) -- ONE line,
   centered within the row, consistent gap. nowrap + overflow-x auto is the safety net for a
   row that's genuinely too narrow (e.g. a long What-Yarnball-Did label on mobile) rather than
   letting it wrap and break the one-line contract. */
.msg-meta-row { display: flex; align-items: center; justify-content: center; gap: 12px; flex-wrap: nowrap; overflow-x: auto; margin-top: 6px; scrollbar-width: none; }
.msg-meta-row::-webkit-scrollbar { display: none; }
/* Ben 2026-08-08: every meta element (lock, what-yarnball-did steps, context chip,
   copy/retry) rides one 28px centerline — no more staggered heights. */
.msg-meta-row > * { min-width: 0; flex: 0 0 auto; display: inline-flex; align-items: center; min-height: 28px; }
.msg-meta-row .attestation-lock-prominent { width: 28px; height: 28px; }
.msg-meta-row .attestation-chip { margin: 0; }
/* Hover actions sit OUTSIDE the bubble on a surface chip — never over the prompt text. */
.msg-hoverwrap { position: relative; margin-bottom: 36px; }
.msg-hoverbar {
  position: absolute;
  right: 0;
  bottom: -34px;
  opacity: 0;
  pointer-events: none;
  transition: opacity .12s ease;
  z-index: 6;
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface);
  box-shadow: var(--shadow);
}
.msg-hoverbar.on { opacity: 1; pointer-events: auto; }
.msg-hoverwrap.is-me .msg-hoverbar { right: 0; left: auto; }

/* Queue + stop (Ben 2026-07-21): queued messages wait in a dense strip above the
   composer; Stop replaces Send while a turn streams. */
.queued-msgs { display: flex; flex-direction: column; gap: 4px; margin: 0 auto 6px; max-width: 1040px; width: 100%; padding: 0 28px; box-sizing: border-box; }
.queued-row { display: flex; align-items: center; gap: 10px; border: 1px dashed var(--line); border-radius: var(--r-sm); background: var(--surface-2); padding: 5px 10px; font-size: 12.5px; color: var(--ink-2); }
.queued-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.queued-btn { font: inherit; font-size: 11.5px; font-weight: 600; border: 1px solid var(--line); background: var(--surface); color: var(--ink-2); border-radius: var(--r-pill); padding: 3px 10px; cursor: pointer; flex: none; }
.queued-btn:hover { border-color: var(--accent); color: var(--accent); }
@media(max-width:600px){.queued-msgs{padding-left:14px;padding-right:14px}}
/* YUM-UX-B13 item 13: a queued row rendered as plain unlabeled text next to the composer --
   easy to mistake for a second live composer/reply. Explicit small "Queued" badge, visually
   nested under the in-flight reply via the dashed queued-row border it already sits inside. */
.queued-badge { flex: none; font-family: var(--mono); font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--accent); background: var(--accent-soft); border-radius: var(--r-pill); padding: 2px 7px; }

.send-stop { background: var(--ink); color: var(--bg); }

/* YUM-273 (2026-07-21): attachments are what people open mail FOR — highlighted chips,
   real links; a compact flag marks rows that carry files before they are expanded. */
.email-attach-flag { font-family: var(--mono); font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--accent); border: 1px solid var(--accent); border-radius: var(--r-pill); padding: 1px 6px; flex: none; }
.email-attach-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 2px; }
.email-attach-chip { display: inline-flex; align-items: center; font-size: 12.5px; font-weight: 600; color: var(--accent); background: var(--accent-soft); border: 1px solid var(--accent); border-radius: var(--r-pill); padding: 4px 12px; text-decoration: none; max-width: 34ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.email-attach-chip:hover { background: var(--accent); color: var(--on-accent, #fff); }

/* Item 21 (2026-07-21): real context-window usage per reply — the final hop's
   prompt_tokens from the gateway, never an estimate. */
.ctx-chip { font-family: var(--mono); font-size: 11px; font-weight: 600; letter-spacing: .03em; color: var(--mute); border: 1px solid var(--line); border-radius: var(--r-pill); padding: 2px 8px; flex: none; }

/* First-login welcome (Ben 2026-07-25 — YUM-306 minimal wizard; fields reuse
   .onboarding-profile-grid/.onboarding-profile-field, see app-extensions.css below). */
.welcome-actions { display: flex; gap: 10px; align-items: center; }

/* Connector memory controls (Ben 2026-07-22): real toggle switch, styled window select,
   Sync-now button — replaces the raw native checkbox. */
.conn-memory-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 8px; }
.conn-switch { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; user-select: none; }
.conn-switch-input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.conn-switch-track { position: relative; width: 34px; height: 20px; border-radius: 999px; background: var(--line); transition: background .15s; flex: none; }
.conn-switch-knob { position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%; background: var(--surface); box-shadow: var(--shadow-sm); transition: transform .15s; }
.conn-switch-input:checked + .conn-switch-track { background: var(--accent); }
.conn-switch-input:checked + .conn-switch-track .conn-switch-knob { transform: translateX(14px); }
.conn-switch-input:focus-visible + .conn-switch-track { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 30%, transparent); }
.conn-switch-label { font-size: 13px; color: var(--ink-2); }
.conn-memory-days { font-size: 12.5px; padding: 6px 10px; }
.conn-sync-btn { font: inherit; font-size: 12px; font-weight: 600; color: var(--ink-2); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-pill); padding: 5px 13px; cursor: pointer; flex: none; transition: border-color 0.15s ease, color 0.15s ease; }
.conn-sync-btn:hover:not(:disabled) { border-color: var(--ink); color: var(--ink); }
.conn-sync-btn:disabled { opacity: .6; cursor: wait; }


/* ---- YUM-332/333/334 feature tips + getting-started wizard + security CTA (2026-07-26) ---- */
.feature-tips-strip {
  position: fixed; left: 50%; bottom: 72px; transform: translateX(-50%);
  z-index: 70; max-width: min(520px, calc(100vw - 24px));
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  padding: 10px 14px; border-radius: 12px;
  background: var(--surface, #fff); color: var(--ink, #0e1116);
  border: 1px solid var(--line, #e4e4df);
  box-shadow: 0 8px 24px rgba(14, 17, 22, 0.08);
  font: inherit; font-size: 13px;
}
.feature-tip-text { flex: 1 1 auto; min-width: 0; font-weight: 600; color: var(--ink-2, #2a2f36); }
.feature-tip-actions { display: flex; gap: 8px; flex: none; }
.feature-tip-dismiss, .feature-tip-disable {
  font: inherit; font-size: 12px; font-weight: 600; cursor: pointer;
  border-radius: 999px; padding: 4px 10px; border: 1px solid var(--line, #e4e4df);
  background: var(--surface, #fff); color: var(--ink-2, #2a2f36);
}
.feature-tip-dismiss { border-color: var(--accent, #b23a34); color: var(--accent, #b23a34); }
.feature-tip-dismiss:hover { background: var(--accent, #b23a34); color: #fff; }
.feature-tip-disable:hover { border-color: var(--ink-2); }

/* B8 item 1: in-chat connector-ingest offer bubble + its checkbox row. Dense, one bubble,
   no dashboard chrome — five inline checkboxes + two buttons. */
.ingest-offer-bubble { display: flex; flex-direction: column; gap: 10px; max-width: 420px; }
.ingest-offer-text { font-size: 13.5px; line-height: 1.45; color: var(--ink); }
.ds-checkbox-row { display: flex; flex-wrap: wrap; gap: 6px 14px; }
.ds-checkbox-item { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; user-select: none; font-size: 12.5px; color: var(--ink-2); }
.ds-checkbox-input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.ds-checkbox-box { width: 16px; height: 16px; flex: none; border-radius: 4px; border: 1.5px solid var(--line); background: var(--surface); position: relative; transition: background .12s, border-color .12s; }
.ds-checkbox-box::after { content: ''; position: absolute; left: 4px; top: 1px; width: 4px; height: 8px; border: solid var(--on-accent, #fff); border-width: 0 2px 2px 0; transform: rotate(45deg); opacity: 0; }
.ds-checkbox-input:checked + .ds-checkbox-box { background: var(--accent); border-color: var(--accent); }
.ds-checkbox-input:checked + .ds-checkbox-box::after { opacity: 1; }
.ds-checkbox-input:focus-visible + .ds-checkbox-box { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 30%, transparent); }
.ds-checkbox-label { line-height: 1; }
.ingest-offer-actions { display: flex; gap: 8px; }
.ingest-offer-btn { font: inherit; font-size: 12.5px; font-weight: 600; padding: 6px 14px; border-radius: var(--r-pill); border: 1px solid var(--line); background: var(--surface); color: var(--ink-2); cursor: pointer; }
.ingest-offer-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.ingest-offer-btn:disabled { opacity: .6; cursor: wait; }
.ingest-offer-btn-primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent, #fff); }
.ingest-offer-btn-primary:hover:not(:disabled) { opacity: .88; color: var(--on-accent, #fff); }

/* B8 item 2: sync-in-progress indicator — a small pulsing chip near the top of chat, and a
   compact status row per job in the Connections tab. Both read the same GET
   /api/connect/sync-status; no separate/fake progress source. */
.sync-status-chip { display: inline-flex; align-items: center; gap: 7px; align-self: flex-start; margin: 0 0 8px; padding: 4px 11px; border-radius: var(--r-pill); background: var(--accent-soft); font-size: 12px; font-weight: 600; color: var(--accent); }
.sync-status-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); flex: none; animation: sync-status-pulse 1.4s ease-in-out infinite; }
@keyframes sync-status-pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: .35; transform: scale(.7); } }
.sync-status-list { display: flex; flex-direction: column; gap: 6px; margin-top: 10px; }
.sync-status-row { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--ink-2); }
.sync-status-row-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; background: var(--line); }
.sync-status-row-dot.is-active { background: var(--accent); animation: sync-status-pulse 1.4s ease-in-out infinite; }
/* 2026-07-30 (YUM design audit): was an off-palette invented green (#3a9d5f) — sibling states
   on this same row (.is-active, .is-error) already use var(--accent); done now matches them. */
.sync-status-row-dot.is-done { background: var(--accent); }
.sync-status-row-dot.is-error { background: var(--accent); }

/* Connections org-policy + alpha hint (Ben 2026-07-22): dense inline notice, not a panel. */
.ds-notice { display: flex; flex-direction: column; gap: 4px; margin-top: 10px; padding: 8px 12px; border-radius: var(--r-md, 8px); background: var(--surface-2, var(--accent-soft)); font-size: 12px; line-height: 1.45; color: var(--ink-2); }

/* B3 (Ben 2026-07-22): Connections capability groups — granted vs missing, per connector,
   sourced from GET /api/connect/permissions allCapabilities (real granted-scope data, never
   invented client-side). Dense list, no dashboard framing. */
.conn-cap-groups { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
.conn-cap-group { display: flex; flex-direction: column; gap: 3px; }
.conn-cap-group-title { font-size: 11px; font-weight: 600; letter-spacing: .02em; text-transform: uppercase; color: var(--mute, #8a8f98); }
.conn-cap-list { display: flex; flex-direction: column; gap: 2px; }
.conn-cap-item { display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--ink-2); }
/* 2026-07-30 (YUM design audit): was an off-palette invented green (#3a9d5f) — the granted/
   missing pair now uses the same accent/line contrast every other on/off dot in this file uses. */
.conn-cap-item-dot { width: 6px; height: 6px; border-radius: 50%; flex: none; background: var(--faint, #A2A6AB); }
.conn-cap-item.is-missing .conn-cap-item-dot { background: var(--line); }
.conn-cap-item.is-missing { color: var(--mute, #8a8f98); }
/* WS-03: the aside on a permission nobody but an administrator can grant, kept quiet so the
   list still reads as a list rather than as a warning. */
.conn-cap-item-note {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.03em;
  color: var(--accent);
}
.conn-cap-reconnect-link { font-size: 12px; font-weight: 600; color: var(--accent); background: none; border: none; padding: 2px 0; cursor: pointer; text-align: left; align-self: flex-start; }

/* Onboarding wizard (2026-07-22): compact dismissible card at the top of chat home, gets
   real user data into memory as early as possible. Dense checklist, no modal, no dashboard —
   one card, four rows, each row is either a live tick or an action. */
/* YUM-306 (2026-07-25): the chat-home "Get more out of Yarnball" banner (.onboarding-card/
   .onboarding-steps/.onboarding-step/-tick/-body/-label) is removed — see chat.js. The
   Settings > Getting started subpage (settings.js) uses its own .wizard-step-* classes
   instead; .onboarding-step-optional stays, still used there. */
.onboarding-step-optional { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .02em; color: var(--mute, #8a8f98); margin-left: 5px; }

/* YUM-306 (2026-07-25): rotating loading-time tips, chat.js WaitTips. Fixed-height slot so
   swapping between a one-line and two-line tip never pushes the message list or composer —
   min-height sized for two lines at this font/line-height, no growth beyond it. No pulse/
   fade animation (Ben: "no animation excess") — a plain text swap on each rotation. */
.wait-tip-slot { display: flex; align-items: center; justify-content: center; text-align: center; min-height: 32px; margin: 2px 0 0; }
.wait-tip { font-size: 12px; line-height: 1.5; color: var(--mute, #8a8f98); }
/* Ben 2026-08-08: quiet neutral step actions — five red-outline pills competed with the
   accent-scarce rule; the done-glyphs carry the brand red, the buttons stay ink. */
.onboarding-step-action { font: inherit; font-size: 12px; font-weight: 600; color: var(--ink-2); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-pill); padding: 4px 12px; cursor: pointer; align-self: flex-start; transition: border-color 0.15s ease, color 0.15s ease; }
.onboarding-step-action:hover:not(:disabled) { border-color: var(--ink); color: var(--ink); }
.onboarding-step-action:disabled { opacity: .6; cursor: wait; }
.onboarding-profile-box { display: flex; flex-direction: column; gap: 10px; }
.onboarding-section-title { font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--mute, #8a8f98); border-bottom: 1px solid var(--line, #E4E4DF); padding-bottom: 4px; margin-bottom: 4px; }
.onboarding-section-title-margin { margin-top: 12px; }
.onboarding-field-group { display: flex; flex-direction: column; gap: 4px; width: 100%; }
.onboarding-field-group-margin { margin-top: 8px; }
.onboarding-field-label { font-size: 11.5px; font-weight: 600; color: var(--ink-2, #a0a0a0); }
.onboarding-profile-box textarea { font: inherit; font-size: 12.5px; padding: 7px 9px; border-radius: 6px; border: 1px solid var(--line); background: var(--surface); color: var(--ink); resize: vertical; min-height: 52px; width: 100%; box-sizing: border-box; }
.onboarding-profile-box textarea:focus-visible { outline: none; border-color: var(--accent); }
.onboarding-profile-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.onboarding-profile-grid-prefs { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
.onboarding-profile-field { font: inherit; font-size: 12.5px; padding: 6px 9px; border-radius: 6px; border: 1px solid var(--line); background: var(--surface); color: var(--ink); min-width: 0; width: 100%; box-sizing: border-box; }
.onboarding-profile-field:focus-visible { outline: none; border-color: var(--accent); }
.onboarding-repeat-block { display: flex; flex-direction: column; gap: 5px; }
.onboarding-repeat-title { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .02em; color: var(--mute, #8a8f98); }
.onboarding-repeat-row { display: grid; grid-template-columns: 1fr 1fr auto; gap: 6px; align-items: center; }
.onboarding-repeat-remove { font: inherit; font-size: 14px; line-height: 1; width: 24px; height: 24px; flex: none; border-radius: 50%; border: 1px solid var(--line); background: var(--surface); color: var(--ink-2); cursor: pointer; }
.onboarding-repeat-remove:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.onboarding-repeat-add { font: inherit; font-size: 12px; font-weight: 600; color: var(--accent); background: none; border: none; padding: 2px 0; cursor: pointer; text-align: left; align-self: flex-start; }
.onboarding-repeat-add:hover:not(:disabled) { text-decoration: underline; }
.onboarding-profile-actions { margin-top: 10px; display: flex; justify-content: flex-end; }
.onboarding-err-hint { color: var(--accent); }
.btn-save-profile { font: inherit; font-size: 13px; font-weight: 700; color: var(--bg, #F4F4F2); background: var(--ink, #0E1116); border: 1px solid var(--ink, #0E1116); border-radius: 8px; padding: 7px 18px; cursor: pointer; transition: opacity 0.15s ease, transform 0.15s ease; align-self: auto; }
.btn-save-profile:hover:not(:disabled) { opacity: 0.9; }
.btn-save-profile:disabled { opacity: 0.5; cursor: not-allowed; }
@media (max-width: 600px) { .onboarding-profile-grid, .onboarding-profile-grid-prefs { grid-template-columns: 1fr; } }

/* b15-wizard: permanent "Getting started" Settings subpage — dense step rows, no cards,
   no whitespace bloat. Reuses .onboarding-step-action / .onboarding-profile-* / .hint from
   the wizard components above; adds only the row chrome this page itself needs. */
.onboarding-upload-box { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
.onboarding-upload-input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.wizard-progress { font-size: 12px; font-weight: 600; color: var(--ink-2); }
/* Ben 2026-08-08: visible progress meter for Getting started — count alone read as flat text. */
.wizard-progress-meter { height: 4px; border-radius: 999px; background: var(--line-soft, var(--line)); margin-top: 6px; overflow: hidden; }
.wizard-progress-meter-fill { display: block; height: 100%; border-radius: 999px; background: var(--accent, #B23A34); transition: width 0.3s ease; width: 0; }
.wizard-progress-meter-fill.is-1of4 { width: 25%; }
.wizard-progress-meter-fill.is-2of4 { width: 50%; }
.wizard-progress-meter-fill.is-3of4 { width: 75%; }
.wizard-progress-meter-fill.is-4of4 { width: 100%; }
/* YUM-1259: required completion is connect + ingest + profile (3 steps); the optional
   upload no longer sits in the denominator, so the meter tracks thirds. */
.wizard-progress-meter-fill.is-1of3 { width: 33.333%; }
.wizard-progress-meter-fill.is-2of3 { width: 66.666%; }
.wizard-progress-meter-fill.is-3of3 { width: 100%; }
/* YUM-1259: optional steps are listed in their own section, not counted as required. */
.wizard-optional-section { border-top: 1px solid var(--line); margin-top: 6px; padding-top: 4px; }
.wizard-optional-heading { font-size: 10.5px; font-weight: 600; text-transform: uppercase; letter-spacing: .02em; color: var(--mute, #8a8f98); padding: 8px 0 0; }
.wizard-steps { display: flex; flex-direction: column; }
.wizard-step-row { display: flex; align-items: flex-start; gap: 10px; padding: 10px 0; }
.wizard-step-glyph { width: 17px; height: 17px; flex: none; margin-top: 2px; border-radius: 50%; border: 1.5px solid var(--line); background: var(--surface); position: relative; }
/* 2026-07-30 (YUM design audit): #3a9d5f was an off-palette invented green — the locked
   palette bans invented hues, and the DS convention elsewhere for a done/verified state is
   var(--accent) (see secnode.ok, ywd-tick), never a one-off red/green. Remapped onto the
   locked palette, no new color introduced. */
.wizard-step-glyph.is-done { background: var(--accent); border-color: var(--accent); }
.wizard-step-glyph.is-done::after { content: ''; position: absolute; left: 4.5px; top: 1.5px; width: 5px; height: 8.5px; border: solid var(--on-accent, #fff); border-width: 0 2px 2px 0; transform: rotate(45deg); }
.wizard-step-text { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.wizard-step-title { font-size: 13px; font-weight: 600; color: var(--ink); }
.wizard-step-desc { font-size: 11.5px; color: var(--mute, #8a8f98); }
.wizard-step-done-label { font-size: 11.5px; font-weight: 600; color: var(--accent); align-self: center; }
.wizard-step-body { padding: 0 0 12px 27px; }
.wizard-step-sep { height: 1px; background: var(--line); margin: 0; }
@media (max-width: 600px) {
  .onboarding-profile-grid { grid-template-columns: 1fr; }
  .onboarding-repeat-row { grid-template-columns: 1fr 1fr; }
  .wizard-step-body { padding-left: 0; }
  .feature-tips-strip { bottom: 88px; }
}
@media (max-width: 600px) { .wizard-step-body { padding-left: 0; } }

/* MMDB monitoring embed (2026-07-23): Operations overview "Production monitoring" section —
   embeds the build01 Grafana "Yarnball Production" dashboard (yb-prod-exporter -> Prometheus
   -> Grafana, kiosk+dark) as a read-only iframe. Semantic wrapper, zero inline styles. */
.dash-embed-section { display: flex; flex-direction: column; gap: 6px; margin-top: 4px; }
.dash-embed-frame { width: 100%; height: 780px; border: 1px solid var(--line); border-radius: 8px; background: var(--surface); }
.dash-embed-caption { font-size: 11px; color: var(--mute, #8a8f98); }
@media (max-width: 900px) { .dash-embed-frame { height: 560px; } }

/* ---------------------------------------------------------------------------
   PromptDialog — ask ONE question, get on with it.
   Authored 2026-07-25 after the first-run name dialog was improvised from
   `.onboarding-profile-grid` (a two-column grid for field PAIRS) and `.submit`
   (a full-width FORM submit), which read as a stretched button beside a stray
   field. A single-question dialog is its own pattern, so it is its own component:
   narrower card than the multi-field modal, one full-width field, and an action
   pair where the primary is sized to its own words and the secondary stays quiet.
   Consumed as semantic classes only — app code composes nothing.
--------------------------------------------------------------------------- */
.mcard:has(.prompt-dialog) { max-width: 400px; }
.prompt-dialog { display: grid; gap: 14px; margin-top: 4px; }
.prompt-dialog-field {
  width: 100%;
  box-sizing: border-box;
  font-family: var(--ui);
  font-size: 16px;              /* also the iOS no-zoom floor */
  line-height: 1.4;
  padding: 13px 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface);
  color: var(--ink);
}
.prompt-dialog-field::placeholder { color: var(--faint); }
.prompt-dialog-field:focus-visible {
  /* Focus reads as attention, never as error: the border darkens to ink and the
     accent appears only as a soft halo. A solid red border on an empty field is
     the universal signal for "you got this wrong", which is not what is happening. */
  outline: none;
  border-color: var(--ink);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 12%, transparent);
}
.prompt-dialog-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
}
.prompt-dialog-primary {
  border: 1px solid var(--ink);
  background: var(--ink);
  color: var(--on-ink);
  font-weight: 700;
  min-width: 104px;
  justify-content: center;
}
.prompt-dialog-primary:hover { opacity: .9; }
.prompt-dialog-secondary {
  border-color: transparent;
  background: transparent;
  box-shadow: none;
  color: var(--ink-2);
}
.prompt-dialog-secondary:hover { color: var(--ink); }

@media (max-width: 600px) {
  /* Thumb reach: the primary action owns the bottom edge, the quiet one sits under it. */
  .prompt-dialog-actions { flex-direction: column-reverse; align-items: stretch; gap: 6px; }
  .prompt-dialog-actions .btn { width: 100%; min-height: 46px; }
}

/* ---------------------------------------------------------------------------
   Security facts (Settings → Verify security) and the confirm row.
   Authored 2026-07-25. The security view stopped being a probe graph labelled with
   internal hop names and became four plain facts — Model, Encryption, Location,
   Other details — each a lock mark, a label, and one or two words of answer, opening
   a detail page. Also: an inline confirm for destructive-ish rows (sign out).
--------------------------------------------------------------------------- */
/* Summary line above the facts. One line, no card: the facts below are the content,
   the summary is a caption for them — a boxed panel around a boxed list read as two
   competing surfaces and pushed the facts below the fold on a phone. */
.sec-summary {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  font-family: var(--ui); font-size: 13px; color: var(--mute);
  padding: 2px 2px 0;
}
.sec-summary-state { color: var(--ink); font-weight: 600; }
.sec-summary.is-fail .sec-summary-state { color: var(--accent); }

/* YUM-369 formatting: an open modal owns the screen. Feature tips (.tip-bubble, z 67)
   and the tips strip (z 70) painted OVER the security modal (frozen .modal z 50) —
   lift the modal layer above every tip surface. */
.modal { z-index: 90; }
.app-dialog-host .modal { z-index: 220; }
.mail-phone-compose { position: fixed; right: 16px; bottom: calc(72px + env(safe-area-inset-bottom)); z-index: 40; }
html .mail-phone-compose .new-menu-split .new-menu-main { min-width: 56px; width: 56px; height: 56px; }
html .mail-phone-compose .new-menu-split .new-menu-caret { flex: 0 0 44px; min-width: 44px; width: 44px; height: 56px; }
html .mail-phone-compose .new-menu-main svg { width: 22px; height: 22px; }
html .mail-phone-compose .new-menu-list { inset: auto 16px calc(136px + env(safe-area-inset-bottom)) auto !important; grid-template-columns: repeat(5, 44px); max-width: calc(100vw - 32px); max-height: calc(100dvh - 168px); overflow-y: auto; }
html .mail-phone-compose .new-menu-list .new-menu-item { width: 44px; height: 44px; }

.secfact-list { display: grid; gap: 10px; margin: 14px 0 4px; }
/* 2×2 content grid: lock spans rows, label row1, value row2, chevron spans — never smash. */
.secfact {
  display: grid;
  grid-template-columns: 56px minmax(0, 1fr) 18px;
  grid-template-rows: auto auto;
  column-gap: 14px;
  row-gap: 2px;
  align-items: center;
  width: 100%;
  text-align: left;
  font-family: var(--ui);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 14px 16px;
  cursor: pointer;
}
.secfact:hover { border-color: var(--accent); }
.secfact .sec-lock { grid-column: 1; grid-row: 1 / span 2; justify-self: center; }
.secfact-label {
  grid-column: 2; grid-row: 1;
  display: block;
  font-weight: 700;
  font-size: 14px;
  line-height: 1.3;
  color: var(--ink);
}
.secfact-value {
  grid-column: 2; grid-row: 2;
  display: block;
  font-weight: 400;
  font-size: 12.5px;
  line-height: 1.4;
  color: var(--mute);
}
.secfact-chev {
  grid-column: 3; grid-row: 1 / span 2;
  color: var(--faint);
  font-size: 16px;
  align-self: center;
}
.secfact.is-fail .secfact-value { color: var(--accent); }
.secfact.is-ok .secfact-value { color: var(--ink-2); }

/* Large pure-CSS padlock — open (grey) while pending, slowly closes + greens on ok. */
.sec-lock {
  position: relative;
  display: inline-block;
  width: 44px;
  height: 52px;
  color: var(--faint);
  flex: none;
}
.sec-lock-body {
  position: absolute;
  left: 4px; right: 4px; bottom: 0;
  height: 30px;
  border: 3px solid currentColor;
  border-radius: 7px;
  background: var(--surface);
  box-sizing: border-box;
}
.sec-lock-keyhole {
  position: absolute;
  left: 50%; top: 9px;
  width: 6px; height: 6px;
  margin-left: -3px;
  border-radius: 50%;
  background: currentColor;
}
.sec-lock-keyhole::after {
  content: '';
  position: absolute;
  left: 2px; top: 5px;
  width: 2px; height: 7px;
  background: currentColor;
  border-radius: 1px;
}
.sec-lock-shackle {
  position: absolute;
  left: 10px; right: 10px; top: 2px;
  height: 22px;
  border: 3px solid currentColor;
  border-bottom: 0;
  border-radius: 14px 14px 0 0;
  box-sizing: border-box;
  transform-origin: 0% 100%;
  transition: transform 1.8s cubic-bezier(.22, .8, .2, 1), color 1.8s ease;
}
.sec-lock.is-pending,
.sec-lock.is-fail {
  color: var(--faint);
}
.sec-lock.is-pending .sec-lock-shackle,
.sec-lock.is-fail .sec-lock-shackle {
  transform: rotate(-42deg) translate(-2px, -3px);
}
.sec-lock.is-fail { color: var(--accent); }
.sec-lock.is-ok {
  color: var(--trust-green);
  animation: sec-lock-pulse 1.8s ease both;
}
.sec-lock.is-ok .sec-lock-shackle {
  transform: rotate(0deg) translate(0, 0);
}
@keyframes sec-lock-pulse {
  0% { transform: scale(1.18); filter: drop-shadow(0 0 0 transparent); }
  40% { transform: scale(0.96); filter: drop-shadow(0 0 10px color-mix(in srgb, var(--trust-green) 45%, transparent)); }
  100% { transform: scale(1); filter: drop-shadow(0 0 0 transparent); }
}
@media (prefers-reduced-motion: reduce) {
  .sec-lock-shackle { transition: none; }
  .sec-lock.is-ok { animation: none; }
}

.secfact-detail { padding: 4px 2px 8px; display: grid; gap: 12px; }
/* Ben 2026-08-08: the bare red text "‹ Back" read as broken — proper quiet pill,
   consistent with every other control. */
.secfact-back {
  justify-self: start;
  display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid var(--line); background: var(--surface);
  border-radius: var(--r-pill); padding: 5px 14px 5px 10px;
  font-family: var(--ui); font-size: 12.5px; font-weight: 600; color: var(--ink-2);
  cursor: pointer; transition: border-color .15s ease, color .15s ease;
}
.secfact-back:hover { border-color: var(--ink); color: var(--ink); }
.secfact-detail-head {
  display: grid;
  grid-template-columns: 56px minmax(0, 1fr);
  gap: 14px;
  align-items: center;
}
.secfact-detail-head-text { display: grid; gap: 4px; min-width: 0; }
.secfact-detail-title { font-family: var(--display); font-size: 19px; font-weight: 800; margin: 0; letter-spacing: -.02em; }
.secfact-detail-answer { font-family: var(--ui); font-size: 14px; font-weight: 600; color: var(--ink); }
.secfact-detail-answer.is-fail { color: var(--accent); }
.secfact-detail-answer.is-ok { color: var(--trust-green); }
.secfact-detail-note { margin: 0; font-size: 13.5px; line-height: 1.55; color: var(--ink-2); }
.secfact-detail-card {
  border: 1px solid var(--line-soft);
  border-radius: 10px;
  background: var(--surface-2);
  padding: 10px 12px;
  display: grid;
  gap: 6px;
}
.secfact-detail-card-label {
  font-family: var(--mono); font-size: 11px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--faint);
}
.secfact-detail-lines { display: grid; gap: 2px; }
.secfact-line { display: flex; justify-content: space-between; gap: 12px; padding: 6px 0; font-size: 13px; border-bottom: 1px solid var(--line-soft); }
.secfact-line:last-child { border-bottom: 0; }
.secfact-line-k { color: var(--mute); }
.secfact-line-v { color: var(--ink); text-align: right; font-weight: 500; }
.secfact-line-v.is-ok { color: var(--trust-green); }
.secfact-line-v.is-fail { color: var(--accent); }
.secfact-hop-block { padding: 4px 0 8px; }
.secfact-ev { font-size: 12.5px; color: var(--ink-2); padding: 2px 0; word-break: break-word; }
.secfact-receipt-toggle {
  border: 0; background: none; padding: 0;
  font-family: var(--ui); font-size: 13px; color: var(--accent); cursor: pointer;
}
.secfact-receipt {
  margin: 8px 0 0; padding: 10px 12px; max-height: 260px; overflow: auto;
  background: var(--surface-2); border: 1px solid var(--line-soft); border-radius: 8px;
  font-family: var(--mono); font-size: 11px; white-space: pre-wrap; word-break: break-all;
}
.sec-summary-when { color: var(--mute); }

/* Privacy — separate section under live checks, title and subtitle never smash. */
.sec-privacy {
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid var(--line-soft);
  display: grid;
  gap: 8px;
}
.sec-privacy-kicker {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--faint);
  padding: 0 2px;
}
.sec-privacy-open {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 18px;
  grid-template-rows: auto auto;
  column-gap: 12px;
  row-gap: 4px;
  width: 100%;
  text-align: left;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--surface);
  padding: 14px 16px;
  cursor: pointer;
  font-family: var(--ui);
  color: var(--ink);
}
.sec-privacy-open:hover { border-color: var(--accent); }
.sec-privacy-open-title {
  grid-column: 1;
  grid-row: 1;
  display: block;
  font-weight: 700;
  font-size: 14px;
  line-height: 1.3;
}
.sec-privacy-open-sub {
  grid-column: 1;
  grid-row: 2;
  display: block;
  font-size: 12.5px;
  line-height: 1.4;
  color: var(--mute);
}
.sec-privacy-open .secfact-chev {
  grid-column: 2;
  grid-row: 1 / span 2;
  align-self: center;
}
.sec-privacy-list { margin: 0; padding: 0 0 0 1.15em; display: grid; gap: 8px; }
.sec-privacy-item { font-size: 13.5px; line-height: 1.5; color: var(--ink-2); }
.sec-privacy-footer { margin: 8px 0 0; font-size: 12.5px; line-height: 1.5; color: var(--mute); }

.confirm-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.confirm-row-question { font-size: 14px; color: var(--ink); }
.confirm-row-actions { display: flex; gap: 8px; }
.confirm-row-confirm { border-color: var(--accent); color: var(--accent); }

/* Operator-console field widths. Replaces 18 invented per-pixel classes
   (admin-field-w80 … admin-field-w360) that app code had been minting locally — the exact
   pattern the design-system gate now refuses. Four named sizes; a form picks the size that
   fits the value, not an arbitrary pixel count. */
.admin-field-xs { width: 96px; }
.admin-field-sm { width: 132px; }
.admin-field-md { width: 190px; }
.admin-field-lg { width: 300px; }

/* Classes that app markup referenced but the design system never defined — found by the
   gate on its first run, 2026-07-25. They were silently doing nothing. */
.model-toggle-side-cheap { /* fast-model side of the model pill — geometry from .model-toggle-side */ }
.model-toggle-side-smart { /* smart-model side of the model pill */ }
.sync-status-chip-text { font-size: 12px; color: var(--ink-2); }
.sync-status-row-text { font-size: 13px; color: var(--ink-2); }
.confirm-row-cancel { color: var(--ink-2); }

/* Quiet secondary action on auth surfaces (forgot password, waitlist). Deliberately not a
   button shape: the filled ink button is the one action that submits the card, and a second
   bordered control next to it competes with it. Authored 2026-07-25 after the sign-in card
   shipped a `.mini` MiniButton whose only rule is scoped to `.yumyum-admin`, so the browser
   painted its default button on the login page. */
.auth-textlink {
  border: 0;
  background: none;
  padding: 0;
  margin: 0 0 14px;          /* room before the action it must not compete with */
  align-self: flex-end;       /* trails the field it belongs to, not the button below */
  font-family: var(--ui);
  font-size: 12.5px;
  color: var(--mute);         /* accent is reserved for the one action on this card */
  cursor: pointer;
}
.auth-textlink:hover { color: var(--accent); text-decoration: underline; }

/* Classes the app uses everywhere but the bundle only defined inside one container, so
   outside that container they styled nothing. Found by the design-system gate's scope check
   on 2026-07-25, the same defect class as the sign-in card's raw `.mini` button.
   `.ywd-chev` is already global in the bundle; these are the rest, promoted verbatim. */
.s { color: var(--mute); font-size: 13px; }
.x { color: var(--accent); cursor: pointer; font-family: var(--mono); font-size: 11px; }
.ywd-tick { color: var(--accent); font-weight: 700; }
.yls-label { color: var(--ink-2); }
.ywd-chev { font-size: 11px; transition: transform .2s ease; }

/* === Settings > Security: explain, never verify (YUM-340, Ben 2026-07-26) ===
   Semantic objects for the security-claim list. App code points at these classes and
   supplies no geometry, colour, or inline style of its own. The lock uses the DS's own
   --trust-green token (the scoped "verified lock" exception, already defined per theme in
   tokens.css) rather than any new colour. */
.sec-lock-row .sl-ic { color: var(--trust-green); }
.sec-claim-list { display: flex; flex-direction: column; }
.sec-claim {
  display: flex; align-items: flex-start; gap: 10px; width: 100%;
  font: inherit; text-align: left; background: none; color: var(--ink);
  border: 0; border-top: 1px solid var(--line-soft); padding: 11px 14px; cursor: pointer;
}
.sec-claim:first-child { border-top: 0; }
.sec-claim:hover { background: var(--surface-2); }
.sec-claim:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.sec-claim-lock { color: var(--trust-green); flex: none; display: flex; margin-top: 1px; }
.sec-claim-body { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.sec-claim-title { font-size: 13px; font-weight: 600; color: var(--ink); }
.sec-claim-line { font-size: 11.5px; color: var(--mute); }
.sec-claim-more { color: var(--faint); flex: none; display: flex; margin-top: 2px; }
.sec-detail { display: flex; flex-direction: column; gap: 9px; text-align: left; }
.sec-detail-p { font-size: 13px; line-height: 1.5; color: var(--ink-2); margin: 0; text-align: left; text-indent: 0; }
.sec-detail-live { font-size: 11.5px; line-height: 1.45; color: var(--mute); margin: 0; text-align: left; text-indent: 0; }

/* YUM-239 — [[ref:type:id|label]] reference chips inside an answer's prose. Inline pill,
   deliberately quieter than .file-mention-pill (that one's an open-a-real-document action;
   this one just continues the conversation) — a small dot + label, accent on hover only. */
.ref-chip {
  display: inline-flex; align-items: center; gap: 5px; border: 1px solid var(--line);
  background: var(--surface-2); color: var(--ink-2); border-radius: var(--r-pill);
  padding: 1px 9px 1px 7px; font: inherit; font-size: 12.5px; line-height: 1.6; cursor: pointer;
  margin: 0 1px;
}
.ref-chip:hover { border-color: var(--accent); color: var(--ink); }
.ref-chip-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--accent, #B23A34); flex: none; }

/* YUM-239 — right-click / long-press context menu for web links and file reference chips.
   Same anchored-popover visual language as .attach-menu, but positioned at the pointer
   (fixed, not relative to a trigger element) since it can open from anywhere in the
   scrollable message list. */
.link-context-menu {
  position: fixed; z-index: 60; display: flex; flex-direction: column; min-width: 200px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-sm);
  box-shadow: var(--shadow); padding: 6px; gap: 2px;
}
.link-context-menu-item {
  display: flex; align-items: center; width: 100%; text-align: left; border: 0;
  background: transparent; padding: 8px 10px; border-radius: var(--r-xs, 6px);
  font: inherit; font-size: 13px; color: var(--ink-2); cursor: pointer;
}
.link-context-menu-item:hover { background: var(--surface-2); color: var(--ink); }

/* YUM-240 — hover mini-preview popover for a web link. Server-rendered screenshot when
   available; an honest title/host skeleton while loading, and a plain "preview
   unavailable" note (never a stale/substitute image) if the capture fails. Fixed-position,
   pointer-anchored like the context menu above; never intercepts pointer events itself so
   it can't trap a hover it doesn't own. */
.link-hover-preview {
  position: fixed; z-index: 59; width: 280px; background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r-sm); box-shadow: var(--shadow);
  padding: 8px; pointer-events: none; overflow: hidden;
}
.link-hover-preview-img { display: block; width: 100%; height: 140px; object-fit: cover; border-radius: var(--r-xs, 6px); margin-bottom: 6px; background: var(--surface-2); }
.link-hover-preview-title { font-size: 12.5px; font-weight: 600; color: var(--ink); line-height: 1.35; margin-bottom: 2px; }
.link-hover-preview-host { font-family: var(--mono); font-size: 11px; color: var(--faint); }
.link-hover-preview-fallback { display: flex; flex-direction: column; gap: 2px; }
.link-hover-preview-note { font-size: 11.5px; color: var(--mute); margin-top: 2px; }
.link-hover-preview-skeleton { display: flex; flex-direction: column; gap: 2px; }
.link-hover-preview-title-skel { height: 12px; width: 70%; border-radius: 3px; background: var(--surface-2); margin-bottom: 6px; animation: link-hover-preview-pulse 1.1s ease-in-out infinite; }
@keyframes link-hover-preview-pulse { 0%, 100% { opacity: .5; } 50% { opacity: 1; } }

/* YUM-258 — staged file chips above the composer. A picked file no longer uploads/sends
   immediately (the old behavior); it sits here as a removable chip until Send actually
   attaches it. Same pill language as .ref-chip above, scoped to its own classes since this
   sits above the composer, not inline in message text. */
.composer-pending-files {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 0 2px 6px;
}
.composer-file-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  max-width: 220px;
  padding: 4px 6px 4px 9px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface-2);
  color: var(--ink-2);
  font-size: 12.5px;
}
.composer-file-chip-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.composer-file-chip-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border: none;
  background: transparent;
  color: var(--faint);
  border-radius: 50%;
  cursor: pointer;
  padding: 0;
}
.composer-file-chip-remove:hover {
  color: var(--ink);
  background: color-mix(in srgb, var(--surface) 70%, var(--ink) 8%);
}

/* YUM-360 — chat answer formatting: a consistent type ladder + block rhythm for assistant
   replies, layered over the base rules at _ds_bundle.css:148-162 (frozen; overrides live
   here per the CDS override convention already used throughout this file). Headings read
   as a deliberate hierarchy (weight + full-ink color) rather than same-weight bold text at
   slightly different sizes, top margin scales with level so a heading always reads as a
   fresh section rather than crowding the text above it, and the first block in a reply
   never carries dead leading space. */
.row.yarnball .bub h1,
.row.yarnball .bub h2,
.row.yarnball .bub h3,
.row.yarnball .bub h4,
.row.yarnball .bub h5,
.row.yarnball .bub h6 {
  font-family: var(--font-body-serif);
  color: var(--ink);
  font-weight: 600;
}
.row.yarnball .bub h1 { margin: 14px 0 5px; }
.row.yarnball .bub h2 { margin: 12px 0 5px; }
.row.yarnball .bub h3,
.row.yarnball .bub h4,
.row.yarnball .bub h5,
.row.yarnball .bub h6 { margin: 10px 0 4px; }
.row.yarnball .bub > h1:first-child,
.row.yarnball .bub > h2:first-child,
.row.yarnball .bub > h3:first-child,
.row.yarnball .bub > h4:first-child,
.row.yarnball .bub > h5:first-child,
.row.yarnball .bub > h6:first-child { margin-top: 0; }
/* Blockquotes (chat.js md() now converts "> quoted text" into a real <blockquote> instead
   of leaving the raw "&gt;" character visible) get their own quiet, indented treatment —
   distinct from a table or a code block so a quoted email/passage reads as quoted. */
.row.yarnball .bub blockquote {
  margin: 6px 0;
  padding: 2px 12px;
  border-left: 2px solid var(--line);
  color: var(--ink-2);
  font-style: italic;
}
/* A dense mixed answer (prose + list + table) reads as one designed surface when the table
   itself is visually boxed, not a naked HTML table sitting flush against the prose above
   and below it. */
.row.yarnball .bub .md-table-wrap {
  border: 1px solid var(--line);
  margin: 8px 0;
}

/* YUM-363 — "Ask Yarnball about this email" / "Reply" now sit side by side instead of one
   stacked lone button (.email-ask-yarnball's own margin-top:10px, _ds_bundle.css:507,
   already spaces this row from the body/attachments above it). */
.email-detail-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}
.email-detail-actions .email-ask-yarnball {
  margin-top: 0;
}

/* YUM-363 — reply-from-chat composer, nested inside an expanded EmailRow. Same visual
   language as the rest of the email detail block (email-detail-*), a lighter-weight
   version of the main .box composer scoped to this one row instead of reusing it (the
   main composer is a fixed, page-level singleton; this is a per-message, per-row control). */
.email-reply-box {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--line);
}
.email-reply-textarea {
  width: 100%;
  box-sizing: border-box;
  min-height: 84px;
  resize: vertical;
  font: inherit;
  font-size: 13.5px;
  line-height: 1.5;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--ink);
}
.email-reply-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
}
.email-reply-status {
  font-size: 12.5px;
  color: var(--ink-2);
}
.email-reply-status.is-error { color: var(--accent); }
.email-reply-status.is-ok { color: var(--trust-green, #2f7a4d); }

/* Unscoped secondary text for product surfaces (settings, canvas engines).
   Admin already had .yumyum-admin .muted; PR #87 used .muted outside admin and the
   Docker design-system gate failed scoped-class on main. Defining unscoped .muted
   so app code that names it actually styles. Token: --mute. */
.muted { color: var(--mute); }

/* Screen-reader-only boot status. The shell must announce that it is loading (assistive
   tech has no other cue while main.js boots) WITHOUT putting a sentence about our own
   startup in front of a sighted person — Ben 2026-07-31. A semantic design-system class,
   not a utility: this is the one accessible-status treatment, defined once. */
.a11y-status-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Work board (Projects > Work, "Board" view) — YUM-409, 2026-08-03.
   A named semantic component in the design system, NOT a per-screen stylesheet: the
   Projects screen consumes these classes and owns no CSS of its own. Columns scroll
   horizontally inside their own container so the page body never scrolls sideways. */
.wboard {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(200px, 1fr);
  gap: 10px;
  overflow-x: auto;
  padding-bottom: 6px;
}
.wboard-col {
  background: var(--surface-2);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
  padding: 10px;
  min-height: 120px;
}
.wboard-col > h4 {
  margin: 0 0 8px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--mute);
  display: flex;
  justify-content: space-between;
  gap: 8px;
}
.wboard-card {
  display: block;
  width: 100%;
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-xs);
  padding: 9px 10px;
  margin-bottom: 8px;
  color: var(--ink);
  font: inherit;
  cursor: pointer;
}
.wboard-card:hover { border-color: var(--accent); }
.wboard-card > strong { display: block; font-size: 13px; line-height: 1.35; }
.sec-panel {
  max-height: 80vh;
  overflow-y: auto;
  box-sizing: border-box;
  padding: 4px 6px;
}

/* Sequential yarnball security path (lock popup / Settings security). */
.sec-path {
  position: relative;
  margin: 10px 0 8px;
  padding: 4px 0;
}
.sec-path-live {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
}
.sec-path-track {
  position: relative;
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0;
}
.sec-path-track::before {
  content: '';
  position: absolute;
  left: 29px;
  top: 18px;
  bottom: 18px;
  width: 2px;
  border-radius: 2px;
  background: var(--line);
  z-index: 0;
}
.sec-path-thread {
  position: absolute;
  left: 29px;
  top: 18px;
  bottom: 18px;
  width: 2px;
  border-radius: 2px;
  background: linear-gradient(
    to bottom,
    color-mix(in srgb, var(--accent) 85%, #c41e3a),
    var(--trust-green)
  );
  transform-origin: top center;
  transform: scaleY(0);
  transition: transform 0.45s cubic-bezier(0.22, 0.8, 0.2, 1);
  z-index: 1;
  pointer-events: none;
}
.sec-path-thread-step-0 { transform: scaleY(0); }
.sec-path-thread-step-1 { transform: scaleY(.25); }
.sec-path-thread-step-2 { transform: scaleY(.5); }
.sec-path-thread-step-3 { transform: scaleY(.75); }
.sec-path-thread-step-4 { transform: scaleY(1); }
.sec-path-ball {
  position: absolute;
  left: 14px;
  top: 12px;
  width: 32px;
  height: 32px;
  margin-top: -2px;
  z-index: 3;
  border-radius: 50%;
  background: var(--surface);
  box-shadow:
    0 0 0 2px color-mix(in srgb, var(--accent) 35%, transparent),
    0 4px 14px color-mix(in srgb, var(--ink) 12%, transparent);
  display: grid;
  place-items: center;
  transform: translateY(0);
  transition: transform 0.45s cubic-bezier(0.22, 0.8, 0.2, 1);
  will-change: transform;
  pointer-events: none;
}
.sec-path-ball-step-0 { transform: translateY(0); }
.sec-path-ball-step-1 { transform: translateY(64px); }
.sec-path-ball-step-2 { transform: translateY(128px); }
.sec-path-ball-step-3 { transform: translateY(192px); }
.sec-path-ball-step-4 { transform: translateY(256px); }
.sec-path-ball img {
  width: 22px;
  height: 22px;
  object-fit: contain;
  display: block;
  border-radius: 50%;
}
.sec-path-ball.is-moving {
  box-shadow:
    0 0 0 3px color-mix(in srgb, var(--accent) 45%, transparent),
    0 6px 18px color-mix(in srgb, var(--accent) 20%, transparent);
}
.mcard:has(.sec-panel),
.mcard:has(.secfact-detail) {
  max-width: 540px;
}
/* Redline p02#1: the Security dialog itself moves to the same 6px radius as its contents. */
.mcard:has(.sec-panel) { border-radius: 6px; }

.sec-path-node {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 12px;
  align-items: center;
  width: 100%;
  box-sizing: border-box;
  min-height: 56px;
  padding: 10px 14px;
  margin: 0 0 8px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--surface);
  text-align: left;
  font-family: var(--ui);
  color: var(--ink);
  cursor: pointer;
  transition: border-color 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
}
.sec-path-node:last-child { margin-bottom: 0; }
.sec-path-node:hover { border-color: var(--accent); }
.sec-path-node:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.sec-path-node.is-active {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 14%, transparent);
}
.sec-path-node.is-ok {
  border-color: color-mix(in srgb, var(--trust-green) 45%, var(--line));
}
.sec-path-node.is-fail {
  border-color: color-mix(in srgb, var(--accent) 55%, var(--line));
}
.sec-path-node-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 8px;
  background: var(--surface-2);
  color: var(--mute);
  flex: none;
  transition: color 0.2s ease, background 0.2s ease;
}
.sec-path-node.is-ok .sec-path-node-icon {
  color: var(--trust-green);
  background: color-mix(in srgb, var(--trust-green) 14%, transparent);
}
.sec-path-node.is-fail .sec-path-node-icon {
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}
.sec-path-node.is-active .sec-path-node-icon {
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}
.sec-path-node-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.sec-path-node-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.sec-path-node-label {
  font-weight: 700;
  font-size: 14px;
  line-height: 1.3;
}
.sec-path-node-badge {
  flex: none;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--mute);
  padding: 2px 8px;
  min-width: 72px;
  text-align: center;
  box-sizing: border-box;
  border-radius: 999px;
  background: color-mix(in srgb, var(--mute) 12%, transparent);
}
.sec-path-node.is-active .sec-path-node-badge {
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}
.sec-path-node.is-ok .sec-path-node-badge {
  color: var(--trust-green);
  background: color-mix(in srgb, var(--trust-green) 14%, transparent);
}
.sec-path-node.is-fail .sec-path-node-badge {
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}
.sec-path-node-status {
  margin-top: 2px;
  font-size: 12px;
  line-height: 1.4;
  color: var(--mute);
  font-weight: 400;
}
.sec-path-node.is-ok .sec-path-node-status { color: var(--ink-2); }
.sec-path-node.is-fail .sec-path-node-status { color: var(--accent); }
.sec-path-node.is-active .sec-path-node-status { color: var(--ink); }
.sec-fact-strip {
  display: grid;
  gap: 10px;
  margin: 16px 0 10px;
}
.sec-fact-chip {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 4px 10px;
  align-items: center;
  width: 100%;
  text-align: left;
  font-family: var(--ui);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 12px 14px;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.sec-fact-chip:hover { border-color: var(--accent); background: var(--surface-2); }
.sec-fact-chip-label {
  font-weight: 700;
  font-size: 13px;
  grid-column: 1;
}
.sec-fact-chip-value {
  font-size: 12px;
  color: var(--mute);
  grid-column: 1;
  line-height: 1.35;
}
.sec-fact-chip-chev {
  grid-column: 2;
  grid-row: 1 / span 2;
  color: var(--faint);
  font-size: 16px;
}
.sec-fact-chip.is-ok .sec-fact-chip-value { color: var(--ink-2); }
.sec-fact-chip.is-fail .sec-fact-chip-value { color: var(--accent); }
.sec-fact-chip.is-pending .sec-fact-chip-value { color: var(--mute); }
@media (prefers-reduced-motion: reduce) {
  .sec-path-ball,
  .sec-path-thread {
    transition: none;
  }
  .sec-path-ball.is-moving {
    box-shadow:
      0 0 0 2px color-mix(in srgb, var(--accent) 35%, transparent),
      0 4px 14px color-mix(in srgb, var(--ink) 12%, transparent);
  }
}

/* ── Co-browse window (Live Browser) ────────────────────────────────────────
   A floating window, not a modal: it has no backdrop, so the conversation stays
   visible and usable beside it — which is the only way "ask Yarnball to take
   this page over" can work at all.

   Position and size are the one thing that cannot be a static rule, so they
   arrive as custom properties the surface sets at runtime (--cbw-x/y/w/h), the
   same handoff canvas.css already uses for --viewer-side / --chat-side. Drag and
   resize rewrite those four values and nothing else. */
.cobrowse-window {
  position: fixed;
  left: var(--cbw-x, 40px);
  top: var(--cbw-y, 40px);
  width: var(--cbw-w, 900px);
  height: var(--cbw-h, 620px);
  max-width: none;
  max-height: none;
  padding: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  z-index: 60;
}
/* The floating window has no .modal backdrop, so it must paint its own opaque
   surface. The Letterpress theme restyles every bare .mcard as a transparent,
   padded ledger row (letterpress-app.css), which let the page behind show
   through the bar and left 10px of padding. :root lifts this rule above that
   theme selector whatever the load order. */
:root .mcard.cobrowse-window {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  box-shadow: 0 12px 40px color-mix(in srgb, var(--ink) 18%, transparent);
  padding: 0;
}
/* The close button is positioned against the whole window, so at the base 12px it hangs below
   the thin title bar and crowds the Go button under it. Centre it inside the bar. */
:root .cobrowse-window > .modal-close-btn {
  top: 4px;
  right: 8px;
  width: 30px;
  height: 30px;
}
:root .setrow.cobrowse-bar {
  margin: 0;
  gap: 10px;
  padding: 10px 44px 10px 14px;
  justify-content: flex-start;
  align-items: center;
  cursor: move;
  touch-action: none;
  border-bottom: 1px solid var(--line);
}
.cobrowse-here {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The labels (here, the extension name, the note) wear .muted and .s beside their own
   class, and later stylesheets restyle both of those bare. Pairing them here keeps these
   labels' size no matter which stylesheet lands last (load order must not decide). */
.cobrowse-here.muted, .muted.cobrowse-here,
.cobrowse-ext.muted, .muted.cobrowse-ext,
.cobrowse-note.muted, .muted.cobrowse-note,
.cobrowse-here.s, .s.cobrowse-here,
.cobrowse-ext.s, .s.cobrowse-ext,
.cobrowse-note.s, .s.cobrowse-note { font-size: 11.5px; }
:root .setrow.cobrowse-nav {
  margin: 0;
  gap: 8px;
  padding: 10px 14px;
  align-items: center;
  border-bottom: 0;
}
/* Field wraps its input in .fld; the wrapper is the flex child, so it is the
   one that has to take the free width. */
:root .cobrowse-nav .fld {
  flex: 1 1 auto;
  min-width: 0;
  margin-bottom: 0;
}
/* Compound on purpose. The stage keeps .canvas-frame's page chrome (border,
   radius, white backdrop) but must NOT keep its `min-height: 60vh` — inside a
   window the user can shrink, a 60vh floor blows the layout open. canvas.css
   loads after this file, so a single-class rule here would lose the cascade;
   .canvas-frame.cobrowse-stage outranks it whatever the load order. */
.canvas-frame.cobrowse-stage {
  flex: 1 1 auto;
  box-sizing: border-box;
  width: auto;
  min-height: 0;
  margin: 0 14px;
  overflow: hidden;
  display: flex;
  align-items: flex-start;
  justify-content: center;
}
.cobrowse-shot {
  width: 100%;
  height: 100%;
  object-fit: contain;
  cursor: crosshair;
}
/* One line under the address bar saying whose browser the agent is using. The bare rules define
   the classes; the :root rules below win over the .mcard and Letterpress rules that also apply. */
.cobrowse-ext { margin: 0; font-size: 12px; }
.linklike { cursor: pointer; }
:root .mcard .cobrowse-ext {
  margin: 0;
  padding: 6px 14px;
  border-bottom: 1px solid var(--line);
  font-size: 12px;
  line-height: 1.4;
}
:root .mcard .cobrowse-ext .linklike {
  padding: 0;
  border: 0;
  background: none;
  color: var(--accent);
  font: inherit;
  text-decoration: underline;
  cursor: pointer;
}
:root .mcard .cobrowse-ext .linklike:disabled { opacity: 0.6; cursor: default; }
:root .mcard .cobrowse-ext .linklike:focus-visible,
:root .mcard .cobrowse-ext a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
:root .mcard .cobrowse-note {
  margin: 8px 14px 12px;
}
:root .mcard .cobrowse-err {
  margin: 8px 14px 0;
}
/* Corner grip. Sized for a comfortable pointer target without covering the
   status line it sits beside. */
.cobrowse-grip {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 18px;
  height: 18px;
  cursor: nwse-resize;
  touch-action: none;
}

/* Attachment Modal & Simple Canvas Viewer (Ben 2026-08-08) */
.attachment-modal-content {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-height: 70vh;
  overflow: auto;
  padding: 4px;
}
.attachment-modal-header-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 6px;
}
.attachment-search-input {
  flex: 1;
  padding: 8px 12px;
  border-radius: 6px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
}
.attach-upload-btn {
  background: var(--ink, #0E1116);
  color: var(--bg, #F4F4F2);
  border: 1px solid var(--ink, #0E1116);
  padding: 8px 14px;
  border-radius: 8px;
  cursor: pointer;
  font-weight: 600;
  font-size: 13px;
  white-space: nowrap;
  transition: opacity 0.15s ease;
}
.attach-upload-btn:hover:not(:disabled) { opacity: 0.85; }
.attach-upload-btn:disabled { opacity: 0.5; cursor: wait; }
/* Secondary header action — quiet outline so the modal carries ONE filled action. */
.attach-create-btn {
  background: var(--surface, #fff);
  color: var(--ink-2, #2A2F36);
  border: 1px solid var(--line, #E4E4DF);
}
.attach-create-btn:hover:not(:disabled) { border-color: var(--ink, #0E1116); color: var(--ink, #0E1116); opacity: 1; }
.attachment-folder-tree {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.attachment-folder-group {
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--bg);
  overflow: hidden;
}
.attachment-folder-label {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
}
.attachment-file-list {
  display: flex;
  flex-direction: column;
}
.attachment-file-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 14px;
  border-bottom: 1px solid var(--line-soft, var(--line));
  cursor: pointer;
  transition: background 0.12s ease;
}
.attachment-file-row:hover { background: var(--surface-2, #F7F7F5); }
.attachment-file-row:last-child {
  border-bottom: none;
}
.attachment-file-info {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
.attachment-file-name {
  font-weight: 500;
  font-size: 13px;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.attachment-file-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}
.attach-action-btn {
  padding: 4px 12px;
  font-size: 12px;
  font-weight: 600;
  border-radius: 999px;
  cursor: pointer;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink-2);
  transition: all 0.15s ease;
  white-space: nowrap;
}
.attach-action-btn:hover {
  border-color: var(--ink);
  color: var(--ink);
}
.attach-action-btn.add-btn.is-attached {
  background: var(--ink, #0E1116);
  border-color: var(--ink, #0E1116);
  color: var(--bg, #F4F4F2);
}

/* Simple Canvas Viewer */
.simple-canvas-viewer-body {
  max-height: 75vh;
  overflow: auto;
  padding: 8px;
}
.canvas-viewer-image-wrap {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 12px;
}
.canvas-viewer-img {
  max-width: 100%;
  max-height: 70vh;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.canvas-viewer-pdf-wrap { display: flex; }
.canvas-viewer-pdf { width: 100%; height: 68vh; border: 0; border-radius: 8px; }
.canvas-viewer-text-wrap {
  background: var(--surface);
  border-radius: 8px;
  padding: 16px;
  border: 1px solid var(--line);
  font-family: var(--mono, monospace);
  font-size: 13px;
  line-height: 1.5;
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 65vh;
  overflow: auto;
}

/* SPEC 2.3 (2026-08-26) + the 2026-10-10 greeting rework: the start-page greeting is the
   empty state's hero: one sentence at the user's own hour (public/app/greeting.js) over
   the quiet day-and-time line naming the clock it followed. Display serif, ink for the
   sentence, mute for the clock line: no caps, no labels, no extra chrome. */
.chat-hello { margin: 22px 0 4px; }
.chat-hello-line {
  margin: 0;
  font-family: var(--display);
  font-size: 34px;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.15;
  color: var(--ink);
}
.chat-hello-clock {
  margin: 10px 0 0;
  font-family: var(--ui);
  font-size: 13px;
  font-weight: 450;
  line-height: 1.4;
  color: var(--mute);
}

/* Tooltip Cursor Offset & Positioning Fix for Model Selector Pill — SUPERSEDED (SPEC C3,
   2026-08-26). The [data-tip]::after mechanism these rules styled is neutralised in
   _ds_bundle.css (display:none on the pseudo-element) and replaced by a body-level portal
   tooltip (_ds_bundle.js). The model pill's "real product information, not a hint" larger
   left-aligned treatment survives as .ds-tip-layer-rich, and data-tip-pos above/below is
   read directly by the portal's placement code — nothing here still applies, kept only as
   a pointer for anyone searching by the old selector names. */

/* Larger File Type Icons (36px x 36px badge containers with file extension badges) */
.files-type-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  min-width: 32px;
  min-height: 32px;
  position: relative;
  flex-shrink: 0;
  border-radius: 8px;
  background: var(--surface-2, #F7F7F5);
  border: 1px solid var(--line-soft, var(--line));
  color: var(--mute, #6B7076);
}
.files-type-icon svg {
  width: 18px;
  height: 18px;
}
.files-type-icon-wrap {
  display: inline-flex;
  align-items: center;
  position: relative;
}
.file-ext-badge,
.files-type-badge {
  position: absolute;
  bottom: -3px;
  right: -5px;
  font-size: 11px;
  font-weight: 800;
  font-family: var(--mono, monospace);
  line-height: 1;
  padding: 1.5px 3.5px;
  /* A three-letter badge next to a four-letter one made the tag jitter width row to row,
     which reads as a list that was never finished. One width for every badge. */
  min-width: 26px;
  box-sizing: border-box;
  text-align: center;
  border-radius: 4px;
  background: var(--accent, #B23A34);
  color: #ffffff;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  box-shadow: 0 1px 3px rgba(0,0,0,0.3);
}

/* (pdf viewer geometry lives with the Simple Canvas Viewer block above — 68vh) */
.btn-link { color: var(--accent, #B23A34); font-weight: 600; text-decoration: underline; cursor: pointer; }
.btn-link:hover { opacity: 0.85; }

/* Project Creation Modal, Inputs & Modal Tree View */
.project-modal,
.project-creation-modal {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 4px;
}
.project-modal-input,
.project-creation-input,
.project-input {
  width: 100%;
  box-sizing: border-box;
  padding: 8px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm, 6px);
  background: var(--surface);
  color: var(--ink);
  font-size: 13.5px;
  outline: none;
  transition: border-color 0.15s ease;
}
.project-modal-input:focus,
.project-creation-input:focus,
.project-input:focus {
  border-color: var(--accent, #B23A34);
}
.project-modal-tree,
.attachment-folder-tree {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 8px;
}
.project-tree-item,
.attachment-folder-group {
  border: 1px solid var(--line);
  border-radius: var(--r-sm, 6px);
  background: var(--surface);
  padding: 10px 14px;
  transition: background 0.15s ease;
}
.attachment-folder-label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  margin-bottom: 8px;
}
.attachment-file-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding-left: 12px;
}
/* (attachment row geometry + attached state are defined once in the Attachment Modal
   block above; the soft row background survives here as the single extra touch) */
.attachment-file-row {
  border-radius: 6px;
  background: var(--surface-2, #F7F7F5);
}
.attachment-file-row:hover {
  background: color-mix(in srgb, var(--ink) 5%, var(--surface-2));
}
.attachment-file-info {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  flex: 1;
}
/* Attach-modal file icons stay neutral — canvas.css paints .files-type-icon accent for the
   canvas drawer; this modal is a working list, not a brand surface. */
.attachment-file-info .files-type-icon {
  background: var(--surface, #FFFFFF);
  border-color: var(--line-soft, var(--line));
  color: var(--mute, #6B7076);
}
.attachment-file-info .files-type-icon svg { width: 18px; height: 18px; }

/* Security Path Diagram Layout */
.sec-path-diagram,
.security-path-diagram {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface);
}
.sec-path-diagram-step {
  display: flex;
  align-items: center;
  gap: 12px;
  position: relative;
}
.sec-path-diagram-connector {
  width: 2px;
  height: 20px;
  background: var(--line);
  margin-left: 14px;
}

/* Account Cards */
.account-card,
.account-cards {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.account-card {
  border: 1px solid var(--line);
  border-radius: var(--r-sm, 8px);
  background: var(--surface);
  padding: 14px 16px;
  box-shadow: var(--shadow-sm);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.account-card:hover {
  border-color: var(--accent, #B23A34);
}
.account-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.account-card-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
}
.account-card-body {
  font-size: 12.5px;
  color: var(--ink-2);
  margin-top: 6px;
}
.account-card-avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--surface-2);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  color: var(--ink);
}
.account-card-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
}

/* Profile Dropdowns */
.profile-dropdown {
  position: relative;
  display: inline-block;
}
.profile-dropdown-menu {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  min-width: 200px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm, 8px);
  background: var(--surface);
  box-shadow: var(--shadow);
  padding: 6px 0;
  z-index: 100;
  display: flex;
  flex-direction: column;
}
.profile-dropdown-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  font-size: 13px;
  color: var(--ink);
  cursor: pointer;
  border: none;
  background: transparent;
  text-align: left;
  width: 100%;
  box-sizing: border-box;
  transition: background 0.12s ease;
}
.profile-dropdown-item:hover {
  background: var(--surface-2, rgba(0,0,0,0.04));
  color: var(--accent, #B23A34);
}
.profile-dropdown-header {
  padding: 10px 14px;
  border-bottom: 1px solid var(--line);
  font-size: 12px;
  color: var(--mute);
}
.profile-dropdown-divider {
  height: 1px;
  background: var(--line);
  margin: 4px 0;
}

/* (Removed 2026-08-08: the force-centered chat action bars detached the copy/edit bar
   from the user's own message — it floated mid-screen and vanished on mouse-out. The
   bar anchors to its message again via the base .msg-hoverbar rules.) */

/* ============ Security diagram (Ben 2026-08-08 designer pass) ============ */
.sec-diagram { display: flex; flex-direction: column; margin: 12px 0 4px; }
.sec-diagram-row {
  display: grid; grid-template-columns: 40px minmax(0, 1fr) 24px;
  column-gap: 14px; align-items: center; width: 100%; box-sizing: border-box;
  padding: 6px 8px; border: 0; border-radius: 6px; background: transparent;
  text-align: left; font-family: var(--ui); color: var(--ink); cursor: pointer;
  transition: background .15s ease;
  /* Redline p02#2 (Ben, 2026-09-26): "equal spacing and size" — a fixed min-height so
     every one of the 5 rows is the same size whether its .sec-diagram-sub is one line
     (the ok steps) or wraps (the failing steps' error text), which is what made the
     rows and the connector gaps between them read as uneven. */
  min-height: 64px;
}
.sec-diagram-row:hover { background: var(--surface-2); }
.sec-diagram-row:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.sec-diagram-node {
  /* Redline p02#1: "make all objects with 6px corner radius" — was a circle (50%). */
  width: 40px; height: 40px; border-radius: 6px; display: inline-flex;
  align-items: center; justify-content: center; flex: none;
  background: var(--surface); border: 1.5px solid var(--line); color: var(--mute);
  transition: border-color .2s ease, color .2s ease, background .2s ease;
}
.sec-diagram-row.is-ok .sec-diagram-node { border-color: var(--trust-green); color: var(--trust-green); background: color-mix(in srgb, var(--trust-green) 8%, var(--surface)); }
.sec-diagram-row.is-fail .sec-diagram-node { border-color: var(--accent); color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, var(--surface)); }
.sec-diagram-row.is-active .sec-diagram-node { border-color: var(--accent); color: var(--accent); box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 12%, transparent); }
.sec-diagram-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; justify-content: center; }
.sec-diagram-label { font-size: 14px; font-weight: 700; line-height: 1.3; }
/* Redline p02#2: clamped to 2 lines so a long error string cannot grow one row taller
   than the rest — same equal-size fix as the row min-height above. */
.sec-diagram-sub {
  font-size: 12px; color: var(--mute); line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.sec-diagram-row.is-fail .sec-diagram-sub { color: var(--accent); }
.sec-diagram-state {
  /* Redline p02#1: 6px corner radius, was a circle (50%). */
  display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border-radius: 6px; flex: none;
}
.sec-diagram-state.is-ok { background: var(--trust-green); color: #fff; }
.sec-diagram-state.is-fail { background: var(--accent); color: #fff; }
.sec-diagram-spin {
  width: 14px; height: 14px; border-radius: 50%;
  border: 2px solid var(--line); border-top-color: var(--accent);
  animation: sec-diagram-rot .8s linear infinite;
}
@keyframes sec-diagram-rot { to { transform: rotate(360deg); } }
.sec-diagram-link {
  /* Redline p02#2: the connector fills the row's own min-height now that rows are equal
     size, so every gap between steps measures the same instead of varying with content. */
  width: 2px; flex: 1; min-height: 10px; margin-left: 27px; border-radius: 2px;
  background: var(--line); transition: background .3s ease;
}
.sec-diagram-link.is-done { background: var(--trust-green); }
.sec-diagram-link.is-fail { background: var(--accent); }
.sec-e2e-only-row {
  display: flex; align-items: flex-start; gap: 10px; margin: 10px 0 2px;
  /* Redline p02#1: 6px corner radius, was 10px. */
  padding: 10px 12px; border: 1px solid var(--line-soft); border-radius: 6px;
  background: var(--surface-2); cursor: pointer;
}
.sec-e2e-only-check { margin: 3px 0 0; accent-color: var(--accent); width: 15px; height: 15px; flex: none; cursor: pointer; }
.sec-e2e-only-text { display: flex; flex-direction: column; gap: 2px; font-size: 12.5px; color: var(--ink-2); line-height: 1.4; }
.sec-e2e-only-text b { font-size: 13px; color: var(--ink); }

/* Detail pages (Encryption etc): consistent centered column, aligned rows. */
.secfact-detail { max-width: 460px; margin: 0 auto; width: 100%; }
.secfact-detail-head { grid-template-columns: 48px minmax(0, 1fr); }
.secfact-line { display: flex; justify-content: space-between; gap: 14px; align-items: baseline; }
.secfact-line-k { color: var(--mute); font-size: 12.5px; flex: none; }
.secfact-line-v { text-align: right; font-size: 12.5px; font-weight: 600; color: var(--ink); min-width: 0; overflow-wrap: anywhere; }

/* ============ Model-switch choice cards ============ */
.model-switch-choice-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.model-switch-choice {
  display: flex; flex-direction: column; align-items: flex-start; gap: 6px;
  padding: 14px; border: 1px solid var(--line); border-radius: 12px;
  background: var(--surface); cursor: pointer; text-align: left; font-family: var(--ui);
  color: var(--ink-2); transition: border-color .15s ease, background .15s ease;
}
.model-switch-choice:hover { border-color: var(--ink); }
.model-switch-choice.is-primary { border-color: var(--ink); background: var(--surface-2); }
.model-switch-choice-title { font-size: 13.5px; font-weight: 700; color: var(--ink); }
.model-switch-choice-desc { font-size: 12px; line-height: 1.45; color: var(--mute); }
@media (max-width: 600px) { .model-switch-choice-row { grid-template-columns: 1fr; } }

/* Model pill: the selected side must read selected at a glance (beyond color). */
.model-toggle-side.is-current { background: color-mix(in srgb, var(--accent) 10%, var(--surface)); border-radius: 999px; }
.model-toggle-track { cursor: pointer; }

/* ============ Chats menu: archive affordance ============ */
.chat-nav-panel-item-archive {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 6px; flex: none;
  color: var(--faint); transition: color .12s ease, background .12s ease;
}
/* Row actions (pin, rename, delete, archive) stay visible on every row, not only on
   hover (owner request 2026-10-07). The row tightens its gap and the pin's padding so
   the title keeps the remaining width and ellipsises. */
.chat-nav-panel-item { gap: 6px; }
.chat-nav-panel-item .yb-chats-pin { padding: 3px 4px; }
.chat-nav-panel-item-archive:hover { color: var(--ink); background: var(--line-soft); }

/* ============ Viewer additions ============ */
.canvas-viewer-downloads { display: flex; gap: 8px; justify-content: flex-end; margin-bottom: 8px; }
.canvas-viewer-dl { text-decoration: none; display: inline-flex; align-items: center; }
.canvas-viewer-office-note { display: flex; align-items: center; gap: 14px; padding: 26px 18px; }
.canvas-viewer-office-text { display: flex; flex-direction: column; gap: 4px; font-size: 13px; color: var(--ink-2); }
.canvas-viewer-sheets { display: flex; flex-direction: column; gap: 14px; }
.canvas-viewer-sheet-name { font-family: var(--mono); font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--mute); margin-bottom: 4px; }
.canvas-viewer-table-wrap { overflow: auto; max-height: 60vh; border: 1px solid var(--line); border-radius: 8px; }
.canvas-viewer-table { border-collapse: collapse; font-size: 12.5px; min-width: 100%; }
.canvas-viewer-table th { position: sticky; top: 0; background: var(--surface-2); font-weight: 700; text-align: left; }
.canvas-viewer-table th, .canvas-viewer-table td { padding: 5px 10px; border-bottom: 1px solid var(--line-soft); border-right: 1px solid var(--line-soft); white-space: nowrap; max-width: 320px; overflow: hidden; text-overflow: ellipsis; }

/* ============ Projects & Files: legible, aligned size column ============ */
.attachment-file-info .files-row-meta {
  font-family: var(--mono); font-size: 11px; color: var(--mute);
  padding-left: 0; margin-left: auto; flex: none;
}
.attachment-file-info { gap: 10px; }
.attachment-file-name { flex: 0 1 auto; }
.canvas-viewer-sheet { display: flex; flex-direction: column; }

/* ============ Projects & Files browser (two panes + drag-drop) ============ */
/* At the shared 680px dialog width, five files whose names share a prefix all truncated to
   the same string — a list where different rows look identical is not a list. The file
   browser gets the width a file browser needs. */
.mcard.wide:has(.attachment-browser) { max-width: min(900px, 94vw); }
/* 190px left a connected folder reading "Business-…" — a real folder name plus its
   connected state does not fit there. 240px shows enough of the name to tell two folders
   apart, which is the only reason the column exists. */
.attachment-browser { display: grid; grid-template-columns: 240px minmax(0, 1fr); gap: 12px; min-height: 300px; }
.attachment-browser-folders { display: flex; flex-direction: column; gap: 2px; border-right: 1px solid var(--line-soft); padding-right: 10px; }
.attachment-browser-folder {
  display: flex; align-items: center; gap: 8px; width: 100%; box-sizing: border-box;
  padding: 7px 10px; border: 1px solid transparent; border-radius: 8px; background: transparent;
  font-family: var(--ui); font-size: 13px; color: var(--ink-2); cursor: pointer; text-align: left;
  transition: background .12s ease, border-color .12s ease;
}
.attachment-browser-folder:hover { background: var(--surface-2); }
.attachment-browser-folder.is-active { background: var(--surface-2); color: var(--ink); font-weight: 700; }
.attachment-browser-folder.is-drop { border-color: var(--accent); background: var(--accent-soft); }
.attachment-browser-folder-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.attachment-browser-folder-count { font-family: var(--mono); font-size: 11px; color: var(--faint); flex: none; }
/* A connected folder on this computer says so where the other rows show a file count. It is
   a state, not a number, so it is coloured like one — the row otherwise looks like a project
   whose count failed to load. */
.attachment-browser-folder-state {
  font-family: var(--mono); font-size: 11px; letter-spacing: .02em; flex: none;
  color: var(--trust-green, #2E7D5B);
}
.attachment-browser-files { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.attachment-file-project { font-size: 11px; color: var(--faint); border: 1px solid var(--line-soft); border-radius: 999px; padding: 1px 8px; flex: none; white-space: nowrap; }
@media (max-width: 640px) { .attachment-browser { grid-template-columns: 1fr; } .attachment-browser-folders { flex-direction: row; overflow-x: auto; border-right: 0; border-bottom: 1px solid var(--line-soft); padding: 0 0 8px; } }

/* ============ Accounts row: provider glyph + icon actions ============ */
.conn-row-head { display: flex; align-items: center; gap: 8px; min-width: 0; width: 100%; }
.conn-provider-glyph { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: 1px solid var(--line-soft); border-radius: 8px; background: var(--surface); flex: none; }
.conn-row-actions { display: inline-flex; align-items: center; gap: 6px; margin-left: auto; flex: none; }
.conn-icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 8px; border: 1px solid var(--line);
  background: var(--surface); color: var(--ink-2); cursor: pointer; padding: 0;
  transition: border-color .15s ease, color .15s ease;
}
.conn-icon-btn:hover:not(:disabled) { border-color: var(--ink); color: var(--ink); }
.conn-icon-btn:disabled { opacity: .55; cursor: wait; }
.conn-sync-icon.is-busy svg { animation: sec-diagram-rot 1s linear infinite; }
.conn-sync-icon.is-done { border-color: var(--trust-green); color: var(--trust-green); }
.conn-sync-icon.is-error { border-color: var(--accent); color: var(--accent); }
.conn-disconnect-icon:hover { border-color: var(--accent); color: var(--accent); }

/* ============ Getting started wizard: contained, aligned ============ */
.wizard-steps { padding: 0 14px; }
.wizard-step-row { align-items: center; gap: 12px; }
.wizard-step-glyph { margin-top: 0; }
.wizard-step-row .onboarding-step-action { align-self: center; flex: none; }
.usage-spend-meter { margin: 2px 0 4px; }
.usage-spend-meter-fill.is-p0 { width: 2%; }
.usage-spend-meter-fill.is-p10 { width: 10%; }
.usage-spend-meter-fill.is-p20 { width: 20%; }
.usage-spend-meter-fill.is-p30 { width: 30%; }
.usage-spend-meter-fill.is-p40 { width: 40%; }
.usage-spend-meter-fill.is-p50 { width: 50%; }
.usage-spend-meter-fill.is-p60 { width: 60%; }
.usage-spend-meter-fill.is-p70 { width: 70%; }
.usage-spend-meter-fill.is-p80 { width: 80%; }
.usage-spend-meter-fill.is-p90 { width: 90%; }
.usage-spend-meter-fill.is-p100 { width: 100%; }

/* ============ Profile form polish ============ */
.onboarding-profile-grid { gap: 12px 16px; }
.onboarding-field-label { font-size: 12px; color: var(--ink-2); }
.onboarding-section-title-margin { margin-top: 16px; }

/* ============ Usage & plan: plan rows (Ben 2026-08-08 design pass) ============ */
/* Every plan is the same box (Ben, 2026-08-12). Row height used to follow whichever
   description the catalog happened to carry, so a three-word plan sat next to a two-line one
   and the stack read as ragged rather than as a set of comparable products. Fixed height,
   description clamped to two lines, so the eye compares price and action down a straight edge. */
.plan-row {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 12px 14px; margin: 8px 14px 0; box-sizing: border-box;
  min-height: 74px;
  border: 1px solid var(--line); border-radius: 10px; background: var(--surface);
}
.plan-row.is-current { border-color: var(--ink); }
.plan-row-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.plan-row-name { font-size: 14px; font-weight: 700; color: var(--ink); }
.plan-row-desc { font-size: 12px; color: var(--mute); line-height: 1.4; overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 1; line-clamp: 1; }
.plan-row-side { display: flex; align-items: center; gap: 12px; flex: none; }
.plan-row-price { font-size: 12.5px; font-weight: 600; color: var(--ink-2); white-space: nowrap; }
.plan-row-cta { font-size: 12.5px; font-weight: 600; color: var(--accent); text-decoration: none; white-space: nowrap; }
.plan-row-cta:hover { text-decoration: underline; }

/* ============ Profile form (Ben 2026-08-08 Claude-design pass) ============ */
.profile-form { display: flex; flex-direction: column; gap: 18px; }
.profile-form-section { display: flex; flex-direction: column; }
.profile-form-heading {
  font-family: var(--display); font-size: 15px; font-weight: 700; color: var(--ink);
  letter-spacing: -0.01em; padding-bottom: 6px; border-bottom: 1px solid var(--line);
  margin-bottom: 2px;
}
.profile-field-row {
  display: flex; align-items: center; justify-content: space-between; gap: 18px;
  padding: 11px 0; border-bottom: 1px solid var(--line-soft, var(--line));
}
.profile-field-row:last-child { border-bottom: 0; }
.profile-field-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.profile-field-label { font-size: calc(13.5px * var(--app-font-scale, 1)); font-weight: 600; color: var(--ink); }
.profile-field-helper { font-size: calc(11.5px * var(--app-font-scale, 1)); color: var(--mute); }
.profile-field-control { flex: none; display: flex; justify-content: flex-end; }
.profile-field-control .atabs {
  display: inline-flex;
  background: var(--surface-2, #ebebe7);
  padding: 3px;
  border-radius: var(--r-pill, 9999px);
  gap: 2px;
}
.profile-field-control .atab {
  appearance: none;
  border: 0;
  background: transparent;
  padding: 5px 12px;
  border-radius: var(--r-pill, 9999px);
  font-family: var(--sans);
  font-size: calc(12px * var(--app-font-scale, 1));
  font-weight: 500;
  color: var(--ink-2);
  cursor: pointer;
  transition: all 0.15s ease;
  white-space: nowrap;
}
.profile-field-control .atab:hover:not(.is-active) {
  color: var(--ink);
}
.profile-field-control .atab.is-active {
  background: var(--surface, #ffffff);
  color: var(--ink, #0e1116);
  font-weight: 600;
  box-shadow: 0 1px 3px rgba(0,0,0,0.08);
}
.profile-select { width: 240px; box-sizing: border-box; }
.profile-instructions { width: 100%; box-sizing: border-box; margin-top: 8px; resize: vertical; min-height: 64px; }
@media (max-width: 600px) {
  .profile-field-row { flex-direction: column; align-items: stretch; gap: 6px; }
  .profile-field-control { width: 100%; justify-content: flex-start; }
  .profile-select { width: 100%; }
}

/* ============ Round 6 (Ben 2026-08-08): browser polish, viewer media, uniform profile ============ */
/* Panes scroll independently inside the modal */
.attachment-browser-files { max-height: 56vh; overflow-y: auto; padding-right: 4px; }
.attachment-browser-folders { max-height: 56vh; overflow-y: auto; }
/* A file row is a flex item in a column list, so its width defaults to its CONTENT — a long
   name plus the size and both buttons pushed the whole list wider than the modal and put a
   horizontal scrollbar under it (seen in the desktop window, which is narrower than a
   maximised browser). min-width:0 lets the row shrink so the name ellipsis does its job;
   the gap keeps the truncated name off the size, which they were touching. */
/* Opening a document from Projects & Files leaves the list open behind it, so two dim
   layers ended up on top of each other and the card underneath went almost black — it
   read as broken rather than as "still there". The surface on top already darkens the
   page for both, so any surface with another one above it stops darkening. */
.modal.on:has(~ .modal.on) { background: transparent; }
.attachment-file-row { min-width: 0; gap: 12px; }
.attachment-file-list { min-width: 0; }
/* The list scrolls DOWN, never sideways. Declaring overflow-y alone is not enough: CSS
   promotes the other axis from visible to auto, so a single stray pixel put a horizontal
   scrollbar under the files — visible in the desktop window, which is narrower than a
   maximised browser. */
.attachment-browser-files { overflow-x: hidden; }
/* A document editor needs the whole viewer body — it brings its own toolbars and rulers,
   and anything less than full height turns into two nested scrollbars. */
.canvas-viewer-office-editor { flex: 1; min-height: 72vh; display: flex; }
.canvas-viewer-office-editor > * { flex: 1; min-width: 0; }
/* The editor mounts an iframe into .canvas-onlyoffice-host, and that host is only given a
   height by canvas.css rules scoped to the canvas pane and the right dock. In this modal
   it had none, so the toolbar rendered and the page area below it was blank white — the
   document was loaded the whole time, with nowhere to draw. */
.canvas-viewer-office-editor .canvas-onlyoffice-wrap,
.canvas-viewer-office-editor .canvas-onlyoffice-host { flex: 1; display: flex; min-height: 0; height: 100%; }
/* DocsAPI REPLACES the mount div with its own iframe, which inherits the id but not the
   class — so every rule written for `.canvas-onlyoffice-mount > iframe` misses it, and the
   iframe keeps the inline height DocsAPI gave it (a couple of hundred pixels). Hence the
   sliver of toolbar over a blank page. Matched by descendant here, and !important because
   an inline height cannot be outranked any other way. */
.canvas-viewer-office-editor .canvas-onlyoffice-host iframe,
.canvas-viewer-office-editor .canvas-onlyoffice-wrap iframe {
  flex: 1; width: 100% !important; height: 100% !important; min-height: 68vh !important; border: 0;
}
/* And the modal body has to be tall enough to give it that height in the first place. */
.simple-canvas-viewer-body:has(.canvas-viewer-office-editor) { min-height: 78vh; }
/* A document being edited is not a dialog: at the shared 680px the page was cut off at the
   right margin and had to be scrolled sideways to read a letter. When the viewer holds an
   editor it takes the room a document needs. */
.mcard.wide:has(.canvas-viewer-office-editor),
/* Reading a document needs the same room as editing one: at the shared 680px a page was
   squeezed into a column narrower than the paper it was written on. */
.mcard.wide:has(.canvas-viewer-pdf-wrap) {
  max-width: min(1240px, 96vw);
  max-height: 94vh;
  padding: 18px 18px 12px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.mcard.wide:has(.canvas-viewer-pdf-wrap) .simple-canvas-viewer-body { flex: 1; min-height: 78vh; display: flex; flex-direction: column; }
/* A frame with no definite height falls back to about 150px, and the page inside it was
   cut off just under the letterhead — the rest of the card read as blank paper. Both the
   pane and the frame are given the height outright rather than inheriting one that never
   resolves. */
.mcard.wide:has(.canvas-viewer-pdf-wrap) .canvas-viewer-pdf-wrap { flex: 1; min-height: 76vh; }
.mcard.wide:has(.canvas-viewer-pdf-wrap) .canvas-viewer-pdf { height: 100%; min-height: 76vh; }
.mcard.wide:has(.canvas-viewer-office-editor) .simple-canvas-viewer-body {
  flex: 1; min-height: 0; display: flex; flex-direction: column;
}
/* On a phone the search field plus two buttons do not fit on one line: the row ran off the
   right edge and pushed the Add button clean off the screen, so files could not be added at
   all. The search takes its own line and the buttons share the next one. */
@media (max-width: 640px) {
  /* The project rail turns into a horizontal strip on a phone. Cut off mid-word with no
     edge treatment it reads as broken content rather than "there is more this way", so the
     last chip fades out under a mask — the standard way a scrollable strip announces itself. */
  /* The project strip was a horizontal scroller clipped mid-word; a fade told nobody it
     scrolled. Chips wrap instead — every project readable, nothing to discover. */
  .attachment-browser-folders {
    flex-wrap: wrap;
    overflow-x: visible;
    row-gap: 6px;
  }
  .attachment-browser-folder { flex: 0 0 auto; }
  .attachment-modal-header-actions { flex-wrap: wrap; }
  .attachment-search-input { flex: 1 0 100%; }
  .attachment-modal-header-actions .attach-upload-btn { flex: 1 1 auto; }
  /* On a phone the meta line was first hidden whole (taking the date with it, on the very
     screen where telling two files apart is hardest), then kept beside the name — which
     squeezed names down to "fn", "si", "bu". Neither is a file list. The row stacks
     instead: name on its own full-width line, size and date under it, actions to the
     right. Nothing is hidden and nothing is squeezed. */
  .attachment-file-row { flex-wrap: wrap; row-gap: 2px; }
  .attachment-file-info { flex: 1 1 100%; min-width: 0; }
  .attachment-file-name { font-size: 13px; }
  .attachment-file-actions { flex: 1 1 100%; justify-content: flex-end; gap: 8px; }
  .attachment-file-actions .files-row-meta { font-size: 11px; margin-right: auto; padding-left: 36px; }
  /* A document opened on a phone ran off both edges: the file name was cut mid-word, the
     Edit button sat entirely off-screen to the left, and the editor itself extended past
     the right edge — the one screen where a horizontal scroll makes a document unreadable.
     The card is held to the viewport, its own content is allowed to shrink, and the action
     row wraps instead of pushing its first button out of view. */
  .mcard.wide:has(.canvas-viewer-office-editor) {
    max-width: 100%;
    max-height: 92vh;
    padding: 14px 12px 10px;
  }
  .simple-canvas-viewer-body,
  .canvas-viewer-office-editor,
  .canvas-viewer-office-editor > *,
  .canvas-viewer-office-editor .canvas-onlyoffice-wrap,
  .canvas-viewer-office-editor .canvas-onlyoffice-host { min-width: 0; max-width: 100%; }
  .canvas-viewer-downloads { flex-wrap: wrap; justify-content: flex-start; row-gap: 6px; }
}
/* Which folder on this computer is being listed, and how much of it. Without this the
   right-hand pane is a bare list of paths with nothing saying where they live. */
.attachment-local-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding: 0 4px 8px; min-width: 0;
}
.attachment-local-path {
  font-family: var(--mono); font-size: 11px; color: var(--mute);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; direction: rtl; text-align: left;
}
.attachment-local-count { font-family: var(--mono); font-size: 11px; color: var(--faint); flex: none; }
/* The name takes the room between the icon and the size instead of stopping short and
   leaving a gap, so more of it is readable before the ellipsis. */
.attachment-file-name { flex: 1 1 auto; }
/* Size column rides with the actions — no more overlap with Attach; ext badge accent + larger */
.attachment-file-actions { gap: 10px; }
.attachment-file-actions .files-row-meta { font-family: var(--mono); font-size: 11px; color: var(--mute); white-space: nowrap; padding-left: 0; }
.file-ext-badge,
.files-type-badge {
  font-size: 11px;
  padding: 2px 5px;
  background: var(--accent, #B23A34);
}
.attachment-file-delete {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; border-radius: 6px; flex: none;
  color: var(--faint); cursor: pointer; transition: color .12s ease, background .12s ease;
}
.attachment-file-delete:hover { color: var(--accent); background: var(--accent-soft); }
.attachment-browser-folder-plus {
  font-size: 15px; font-weight: 800; color: var(--accent); flex: none; line-height: 1;
}
/* Viewer media + doc rendering */
.canvas-viewer-video { max-width: 100%; max-height: 66vh; border-radius: 8px; background: #000; }
.canvas-viewer-img.is-rot-90 { transform: rotate(90deg); }
.canvas-viewer-img.is-rot-180 { transform: rotate(180deg); }
.canvas-viewer-img.is-rot-270 { transform: rotate(270deg); }
.canvas-viewer-delete { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); }
.canvas-viewer-delete:hover { background: var(--accent-soft); }
.canvas-viewer-doc { max-height: 62vh; overflow: auto; padding: 4px 8px; }
.canvas-viewer-doc-p { margin: 0 0 10px; font-size: 13.5px; line-height: 1.6; color: var(--ink); }
/* Sheets: larger, roomier */
.canvas-viewer-table { font-size: 13px; }
.canvas-viewer-table th, .canvas-viewer-table td { padding: 6px 12px; }
.canvas-viewer-table-wrap { max-height: 62vh; }
/* Viewer title: smaller than a page headline. The close button sits in the card's corner,
   so the title has to stop short of it — without this the name ran underneath the button
   and its last characters simply vanished, with even the ellipsis hidden behind the × . */
.mcard:has(.simple-canvas-viewer-body) h3 {
  font-size: 16px; letter-spacing: -0.01em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  min-width: 0; padding-right: 44px;
}
/* On a phone there is no room to shorten a file name to nothing: a name with no hyphen or
   space in it — FlowMaster_Editorial_Letter.docx — had everything past the fold cut off.
   It wraps instead, breaking mid-word if that is what it takes to stay readable. This
   follows the rule above deliberately; the two have equal weight and the later one wins. */
@media (max-width: 640px) {
  .mcard:has(.simple-canvas-viewer-body) h3 {
    font-size: 15px;
    white-space: normal;
    overflow-wrap: anywhere;
    overflow: visible;
  }
}
/* Profile: every control identical footprint */
.profile-field-control .profile-select,
.profile-field-control .onboarding-profile-field { height: 38px; font-size: 13.5px; padding: 8px 10px; }

/* ============ Round 7 (Ben 2026-08-08) ============ */
/* Form controls match the page type size everywhere */
.onboarding-profile-field, .select-native, .profile-select { font-size: 14.5px; }
.profile-field-control .profile-select,
.profile-field-control .onboarding-profile-field { height: 40px; font-size: 14.5px; }
/* Getting started: clear air between the two cards */
.wizard-steps + * { margin-top: 0; }
#root .setgroup + .setgroup { margin-top: 18px; }
/* Profile as three separate cards */
.profile-form--cards { gap: 16px; }
.profile-form--cards .profile-form-section {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r, 12px);
  padding: 14px 18px; box-shadow: var(--shadow-sm);
}
.profile-form--cards .onboarding-profile-actions { margin-top: 0; }
/* Location picker glyph */
.profile-loc-wrap { display: flex; align-items: center; gap: 8px; width: 100%; }
.profile-loc-glyph { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: 1px solid var(--line-soft); border-radius: 8px; color: var(--accent); flex: none; background: var(--surface); }
.profile-loc-wrap .profile-select { flex: 1; min-width: 0; }
/* Accounts: accent frame, accent header, accent controls */
.conn-account-card {
  border: 1.5px solid color-mix(in srgb, var(--accent) 55%, var(--line));
  border-radius: 12px; padding: 4px 12px; margin-bottom: 12px;
  background: var(--surface);
}
.conn-account-card .conn-row-head b { color: var(--accent); font-size: 14.5px; }
.conn-account-card .conn-icon-btn { border-color: var(--accent); color: var(--accent); }
.conn-account-card .conn-icon-btn:hover:not(:disabled) { background: var(--accent); color: var(--on-accent, #fff); }
.conn-account-card .srv { border: 0; box-shadow: none; }
/* YUM-1476 (SB 2026-09-29): on a phone the Connections account card grew past its row and
   the Storage usage line made main 417px on a 411px viewport. Constrain both to the row and
   let their long text (a login email, the usage sentence) wrap instead of forcing the page
   sideways. On a phone the storage line also wraps the meter and Open Files button under
   the sentence rather than running off the right edge. */
.conn-account-card { max-width: 100%; min-width: 0; box-sizing: border-box; }
.conn-account-card .conn-row-head { flex-wrap: wrap; }
.conn-account-card .conn-row-head .s { min-width: 0; overflow-wrap: anywhere; }
/* YUM-1487 — the connected account card on a phone. The row was two cramped columns of mono
   uppercase with the account crammed beside the provider word, and its two controls were
   28x28 icon boxes, the disconnect one an X that reads as "close" while it deletes the
   account. On a phone the card becomes one column: provider name, the account, then the
   status as its own chip, with the two labelled controls on their own full-width row and at
   least 48px tall. Desktop keeps the compact row (AC5); the labelled buttons themselves are
   already drawn by settings.js (conn-text-btn). */
@media (max-width: 600px) {
  .conn-account-card .conn-row-head { flex-direction: column; align-items: flex-start; gap: 6px; }
  .conn-account-card .conn-row-head b { font-size: 15px; }
  .conn-account-card .conn-row-head .conn-row-account {
    font-size: 13.5px; text-transform: none; letter-spacing: normal; font-family: var(--ui);
    white-space: normal; overflow-wrap: anywhere; text-overflow: clip; overflow: visible;
  }
  .conn-account-card .conn-row-head .conn-row-status {
    display: inline-flex; align-items: center; padding: 3px 10px; border-radius: var(--r-pill);
    background: var(--surface-2); border: 1px solid var(--line);
    text-transform: none; letter-spacing: normal; font-size: 12px;
  }
  .conn-account-card .conn-row-actions { margin-left: 0; width: 100%; gap: 8px; }
  .conn-account-card .conn-icon-btn.conn-text-btn { min-height: 48px; padding: 0 16px; font-size: 13px; }
}
.storage-usage-line { flex-wrap: wrap; }
.storage-usage-text { white-space: normal; overflow-wrap: anywhere; min-width: 0; }
@media (max-width: 600px) {
  /* The empty Connections state's provider buttons are .btn, which is min-width:max-content
     + white-space:nowrap; on a phone "Microsoft 365 · Mail, calendar, OneDrive" is wider than
     the row. Let them shrink and wrap so the empty state fits too, not only a screen with a
     connected account (YUM-1476's guard opens every Settings view, including this one). */
  .picker-row-btn { min-width: 0; white-space: normal; overflow-wrap: anywhere; }
}
/* Usage & plan redesign */
.usage-hero { display: flex; align-items: center; justify-content: space-between; gap: 14px; }
.usage-hero-plan { font-family: var(--display); font-size: 22px; font-weight: 800; letter-spacing: -0.02em; color: var(--ink); }
.usage-hero-sub { font-size: 12.5px; color: var(--mute); margin-top: 2px; }
.usage-meter-block { display: flex; flex-direction: column; gap: 6px; }
.usage-meter-caption { display: flex; justify-content: space-between; align-items: baseline; }
.usage-meter-spent { font-size: 13.5px; font-weight: 700; color: var(--ink); }
.usage-meter-left { font-size: 12px; color: var(--mute); }
.usage-spend-meter { height: 6px; }
.usage-stat-strip { display: flex; align-items: center; gap: 12px; }
.usage-stat { font-size: 12.5px; color: var(--mute); }
.usage-stat b { color: var(--ink); font-weight: 700; }
.usage-stat-sep { width: 1px; height: 14px; background: var(--line); flex: none; }
/* Security modal: fixed frame, scrolling content, aligned rails, accent icons */
.mcard:has(.sec-panel), .mcard:has(.secfact-detail) { width: 560px; max-width: 92vw; }
.sec-panel { min-height: 500px; display: flex; flex-direction: column; }
.sec-diagram { flex: 1; }
.secfact-detail { min-height: 500px; max-height: 66vh; overflow-y: auto; padding-right: 6px; }
.sec-diagram-row { grid-template-columns: 40px minmax(0, 1fr) 28px; }
.sec-diagram-state { width: 24px; height: 24px; justify-self: center; }
.sec-diagram-node { color: var(--accent); }
.sec-diagram-row.is-ok .sec-diagram-node { border-color: var(--trust-green); color: var(--trust-green); }
/* Security SETTINGS list: icon and chevron on one centerline, icons in accent */
.setlink .sl-ic { display: inline-flex; align-items: center; justify-content: center; color: var(--accent); }
.sec-lock-row .sl-ic { color: var(--trust-green); }
.setlink .sl-ch { display: inline-flex; align-items: center; }
/* Viewer: rotation stays inside its box */
.canvas-viewer-image-wrap { overflow: hidden; max-height: 62vh; }
.canvas-viewer-img.is-rot-90, .canvas-viewer-img.is-rot-270 { max-height: 54vh; max-width: 54vh; }
/* Structured doc rendering */
.canvas-viewer-doc-h1 { font-family: var(--display); font-size: 19px; font-weight: 800; margin: 14px 0 6px; letter-spacing: -0.015em; }
.canvas-viewer-doc-h2 { font-family: var(--display); font-size: 16px; font-weight: 700; margin: 12px 0 5px; }
.canvas-viewer-doc-h3 { font-size: 14px; font-weight: 700; margin: 10px 0 4px; }
.canvas-viewer-doc-li { margin: 0 0 4px 18px; font-size: 13.5px; line-height: 1.6; }
/* Delete affordances read as delete */
.attachment-file-delete { width: 26px; height: 26px; color: var(--mute); border: 1px solid var(--line-soft); border-radius: 7px; }
.attachment-file-delete:hover { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
.canvas-viewer-delete { display: inline-flex; align-items: center; gap: 5px; }
.usage-hero-main { display: flex; flex-direction: column; min-width: 0; }
.plan-row-main { flex: 1; }
/* Getting started: real air between the two cards (SettingsGroup wraps in a plain div) */
div:has(> .setgroup) + div:has(> .setgroup) { margin-top: 20px; }
.plan-row-desc { white-space: normal; }

/* 2026-08-09: docx reads as a DOCUMENT — white sheet, margins, book typography. */
.canvas-viewer-doc-sheet {
  background: var(--surface, #fff);
  border: 1px solid var(--line);
  border-radius: 4px;
  box-shadow: var(--shadow-sm);
  max-width: 640px;
  margin: 0 auto;
  padding: 42px 52px;
  max-height: 64vh;
  overflow-y: auto;
}
.canvas-viewer-doc-sheet .canvas-viewer-doc-p { font-size: 14px; line-height: 1.7; margin: 0 0 12px; }
.canvas-viewer-doc-sheet .canvas-viewer-doc-h1 { font-size: 22px; margin: 4px 0 14px; }
.canvas-viewer-doc-sheet .canvas-viewer-doc-h2 { font-size: 17px; margin: 18px 0 8px; }
.canvas-viewer-doc-tablewrap { margin: 10px 0 14px; max-height: none; }
@media (max-width: 700px) { .canvas-viewer-doc-sheet { padding: 22px 20px; } }

/* ============ Round 9 (Ben 2026-08-09) ============ */
/* Text-size control (Settings > Display) — scale classes live on <body> */
/* CDS type is px-based, so percentage font-size alone is inert — zoom scales the real
   rendered UI (supported Chromium + Firefox 126+). */
body.ds-fontscale-90 { font-size: 90%; zoom: 0.9; }
body.ds-fontscale-112 { font-size: 112%; zoom: 1.12; }
body.ds-fontscale-125 { font-size: 125%; zoom: 1.25; }
.fontscale-row { display: inline-flex; gap: 6px; }
.fontscale-btn {
  min-width: 40px; padding: 6px 10px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--surface); color: var(--ink-2); font-family: var(--ui); font-size: 13px;
  font-weight: 700; cursor: pointer; transition: border-color .15s ease, color .15s ease;
}
.fontscale-btn:hover { border-color: var(--ink); color: var(--ink); }
.fontscale-btn.is-on { background: var(--ink); border-color: var(--ink); color: var(--bg); }
/* Per-message security lock (own messages; replies keep the attestation lock+receipt) */
.msg-secure-lock {
  display: inline-flex; align-items: center; margin-left: 6px; vertical-align: middle;
  color: var(--trust-green); opacity: .8;
}
.row.me .msg-secure-lock { color: color-mix(in srgb, var(--trust-green) 70%, #fff); }
/* Per-connector live progress bar (Accounts) */
.sync-status-row-body { display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 0; }
.sync-progress-track { display: block; height: 4px; border-radius: 999px; background: var(--line-soft, var(--line)); overflow: hidden; position: relative; }
.sync-progress-runner {
  position: absolute; left: -35%; top: 0; bottom: 0; width: 35%; border-radius: 999px;
  background: var(--accent, #B23A34); animation: sync-runner 1.4s ease-in-out infinite;
}
@keyframes sync-runner { 0% { left: -35%; } 60% { left: 100%; } 100% { left: 100%; } }
body.ds-fontscale-100 { font-size: 100%; }
/* mark-desktop.css — Spatial desktop environment (mark) grid layout */
.mark-desktop-shell {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  background: var(--surface-2);
}

.mark-desktop-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(96px, 120px));
  gap: 32px;
  padding: 48px;
  width: 100%;
  max-width: 900px;
  justify-content: center;
}

.mark-app-icon {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 12px;
  padding: 16px 8px;
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: all 0.2s ease;
  border: 1px solid transparent;
}

.mark-app-icon:hover {
  background: var(--surface);
  border-color: var(--line);
  box-shadow: var(--shadow-sm);
  transform: translateY(-2px);
}

.mark-app-icon-glyph {
  width: 72px;
  height: 72px;
  border-radius: 18px;
  background: var(--accent);
  color: #ffffff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 32px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}

.mark-app-icon-label {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  text-align: center;
  text-wrap: balance;
  line-height: 1.3;
}

/* Dynamic appearance density & text scaling rules */
:root {
  --app-font-scale: 1;
  --density-gap: 8px;
  --density-pad: 12px;
}

/* Global Font Scaling across all screens, settings, navigation, and modals */
body, .yarnball-shell, .yarnball-app, .settings-screen, .settings-wrap, .chat-screen, .modal-shell {
  font-size: calc(1rem * var(--app-font-scale, 1));
}
.setlink, .setlink-title, .setgroup-title, .conn-account-card, .plan-card, .ds-card, .card, .mcard {
  font-size: calc(0.95rem * var(--app-font-scale, 1));
}
.page-title, .title-serif, .heading, h1 {
  font-size: calc(1.75rem * var(--app-font-scale, 1));
}
.section-title, h2 {
  font-size: calc(1.25rem * var(--app-font-scale, 1));
}
.group-heading, .ds-subhead, .setgroup-hdr, h3 {
  font-size: calc(0.85rem * var(--app-font-scale, 1));
}
p, .muted, .ds-notice, .field-label, .field-hint {
  font-size: calc(0.9rem * var(--app-font-scale, 1));
}
button, .btn, .ds-btn {
  font-size: calc(0.9rem * var(--app-font-scale, 1));
}

[data-density="tight"], body[data-density="tight"], .yarnball-shell[data-density="tight"] {
  --density-gap: 4px;
  --density-pad: 8px;
  --lh-body: 1.25;
}
[data-density="tight"] .setlink { padding: 6px 10px; }
[data-density="tight"] .setgroup { margin-bottom: 8px; }
[data-density="tight"] .row { margin-bottom: 4px; }
[data-density="tight"] .bub { padding: 8px 12px; font-size: calc(0.92rem * var(--app-font-scale, 1)); }
[data-density="tight"] .card, [data-density="tight"] .mcard { padding: 10px; gap: 6px; }

[data-density="roomy"], body[data-density="roomy"], .yarnball-shell[data-density="roomy"] {
  --density-gap: 16px;
  --density-pad: 20px;
  --lh-body: 1.6;
}
[data-density="roomy"] .setlink { padding: 14px 18px; }
[data-density="roomy"] .setgroup { margin-bottom: 20px; }
[data-density="roomy"] .row { margin-bottom: 16px; }
[data-density="roomy"] .bub { padding: 16px 22px; font-size: calc(1.05rem * var(--app-font-scale, 1)); }
[data-density="roomy"] .card, [data-density="roomy"] .mcard { padding: 22px; gap: 14px; }

/* ============================================================================
   Settings window redline pass (Ben, 2026-09-26 — pages 5-9/33-39): remove
   every horizontal divider line in Settings, compress every subpage, keep
   the list/body scrollable, allow resizing the window. Everything below is
   scoped to .yb-win — main.js's SettingsWindow (public/app/main.js) is the
   ONLY consumer of that class app-wide (see layouts.css ~L200), so nothing
   outside Settings is touched no matter how broadly .setlink/.setgroup/
   .ptitle/etc. are reused elsewhere. ========================================= */

/* p5.3 "allow resizing" — main.js's own comment calls this window "not a
   window: no drag, no resize, no minimise" by design (2026-08-12 note). This
   redline overrides that decision. A native corner-drag resize is a real,
   working control (not UI that pretends) and is all that settings CSS
   ownership can reach without touching main.js's SettingsWindow component. */
.yb-winlayer .yb-win {
  resize: both;
  overflow: auto;
  min-width: 420px;
  min-height: 360px;
  max-width: 96vw;
  max-height: 92vh;
}

/* p5.2 "ensure scrolling" — .yb-win-body (layouts.css) already scrolls; this
   just keeps a manually-resized-short window's own content scrollable too. */
.yb-win .settings-wrap,
.yb-win .settings-wrap-wide { min-height: 0; }

/* Design review 2026-10-07: the Settings dialog sliced its last visible row in half at
   the bottom edge with no hint that more rows sit below. The scroll area now fades out
   over its bottom 32px, and its bottom padding is taller than that fade, so the last row
   ends fully opaque once you reach the end. Where scroll-driven animations exist, the fade
   also shrinks to nothing as the scroll reaches the bottom. */
@property --yb-scroll-fade { syntax: '<length>'; inherits: false; initial-value: 32px; }
.yb-win-body .yarnball-main {
  padding-bottom: 40px;
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - var(--yb-scroll-fade)), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - var(--yb-scroll-fade)), transparent);
}
@supports (animation-timeline: scroll()) {
  .yb-win-body .yarnball-main {
    animation: yb-scroll-fade-out linear both;
    animation-timeline: scroll(self block);
    animation-range: calc(100% - 48px) 100%;
  }
}
@keyframes yb-scroll-fade-out { from { --yb-scroll-fade: 32px; } to { --yb-scroll-fade: 0px; } }

/* p6.1 "move back into header for every screen" — every sub-view's own
   title row (.ptitle desktop / .mhead mobile) now stays pinned to the top of
   the scroll area instead of scrolling away, so Back is reachable the whole
   time you're on a screen. (The literal ask — moving Back into the app's
   global lock/chat/mail/folder/sliders top bar — is main.js territory, out
   of Settings-file ownership; see HANDOFF in the report.) */
.yb-win .ptitle,
.yb-win .mhead {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--surface);
}

/* p5.1 / p33.1 / p39.1 "remove all horizontal lines" — every divider in the
   Settings list and every sub-page, root DS classes included. */
.yb-win .setlink,
.yb-win .setlink:last-child { border-bottom: 0; }
.yb-win .setgroup { border: 0; box-shadow: none; }
.yb-win .ptitle { border-bottom: 0; }
.yb-win .settings-row-pad.is-divided { border-top: 0; }
.yb-win .profile-form-heading,
.yb-win .profile-field-row { border-bottom: 0; }
.yb-win .sec-privacy,
.yb-win .sec-claim { border-top: 0; }
.yb-win .sec-claim:hover { background: var(--surface-2); }
.yb-win .section-divided { border-top: 0; padding-top: 0; }
.yb-win .wizard-step-sep { background: transparent; height: 8px; }

/* Compression pass — dense by default across every Settings sub-page (Ben's
   "Compress"/"Compress & redesign" notes on pages 8/34/36/37, applied
   consistently everywhere rather than patched screen by screen). */
.yb-win .setgh { margin: 10px 2px 6px; }
.yb-win #root .setgroup + .setgroup,
.yb-win div:has(> .setgroup) + div:has(> .setgroup) { margin-top: 10px; }
.yb-win .settings-row-pad { padding: 6px 12px; }
.yb-win .setlink { padding: 7px 10px; }
.yb-win .setgroup { margin-bottom: 8px; }
.yb-win .ptitle { margin: 6px 0 8px; padding-bottom: 6px; }
.yb-win .usage-hero { margin-top: 2px; }
.yb-win .usage-meter-block,
.yb-win .usage-stat-strip { margin-top: 6px; }
.yb-win .custom-skill-card { padding: 8px 12px; margin-bottom: 6px; }
.yb-win .mail-sig-row { padding: 6px 8px; }
.yb-win .sec-claim { padding: 8px 4px; }
.yb-win .sec-claim-list { gap: 0; }

/* p35.1 "much much smaller" — the GRANTED/Not granted permission bullet
   list under each Connections account card. */
.yb-win .conn-cap-item { font-size: 11px; line-height: 1.3; gap: 5px; }
.yb-win .conn-cap-group-title { font-size: 11px; }
.yb-win .conn-cap-groups { gap: 4px; margin-top: 5px; }
.yb-win .conn-cap-list { gap: 1px; }
.yb-win .conn-cap-item-dot { width: 5px; height: 5px; }

/* p9.1 "align properly" — a real fixed-column table for the Model Speed
   Test popup instead of free-text SettingsRow values, so latency/throughput
   numbers line up instead of drifting with each row's label length. */
.speed-test-table { display: flex; flex-direction: column; gap: 1px; width: 100%; }
.speed-test-row {
  display: grid;
  grid-template-columns: 1fr 96px minmax(120px, auto);
  align-items: center;
  gap: 10px;
  padding: 6px 4px;
  font-size: 12.5px;
}
.speed-test-row.is-head {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--mute);
}
.speed-test-model { color: var(--ink); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.speed-test-ms { text-align: right; font-variant-numeric: tabular-nums; color: var(--ink-2); }
.speed-test-result { color: var(--ink-2); font-variant-numeric: tabular-nums; }
.speed-test-result.is-error { color: var(--mute); }
.speed-test-result.is-unavailable { color: var(--mute); font-style: italic; }

/* p38.2 "fix the design" — the schedule <select> in the Automations modal
   (public/app/modals.js) had zero custom styling (.select-inherit was
   font-only). Same border/radius/padding language as .mail-lang-select
   elsewhere in this file, so it reads as one designed control, not a bare
   native dropdown next to a real DS Button. */
.automation-select {
  font: inherit;
  font-size: var(--t-small);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 4px 8px;
}
/* p38.1/38.3 "make this actually functional" / "proper formatting, design,
   buttons, UX" — scoped to the Automations modal only
   (.mcard:has(.automations-modal-body), same :has() scoping convention
   already used elsewhere in this file for other individual modals). */
.mcard:has(.automations-modal-body) { max-width: 560px; }
.automations-modal-body .srv { border-radius: var(--r-sm); }
.automation-form-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-2);
}

.row.yarnball .bub, .row.me .bub {
  font-size: calc(0.95rem * var(--app-font-scale, 1));
}

/* Uniform Settings Row & Segmented Controls (Selectors) */
.srow {
  display: flex !important;
  align-items: center !important;
  justify-content: space-between !important;
  min-height: 40px !important;
  gap: 16px !important;
  width: 100% !important;
}
.srow-lbl {
  font-size: calc(0.95rem * var(--app-font-scale, 1)) !important;
  font-weight: 500 !important;
  color: var(--ink) !important;
  flex: 0 0 auto !important;
}
.srow-val {
  display: flex !important;
  align-items: center !important;
  justify-content: flex-end !important;
  flex: 1 1 auto !important;
}
.srow .atabs {
  margin: 0 !important;
  display: inline-flex !important;
  background: var(--surface-2, rgba(0, 0, 0, 0.05)) !important;
  padding: 3px !important;
  border-radius: var(--r-pill, 999px) !important;
  gap: 2px !important;
}
.srow .atab {
  padding: 5px 14px !important;
  font-size: calc(0.85rem * var(--app-font-scale, 1)) !important;
  font-weight: 500 !important;
  color: var(--ink-secondary, #666) !important;
  border-radius: var(--r-pill, 999px) !important;
  cursor: pointer !important;
  user-select: none !important;
  transition: all 0.15s ease !important;
  white-space: nowrap !important;
}
.srow .atab.on,
.srow .atab.is-active,
.atab.on,
.atab.is-active,
.atab.is-selected,
.profile-field-control .atab.is-active,
.profile-field-control .atab.on,
.form-srow .srow-val .atab.is-active,
.form-srow .srow-val .atab.on,
.scheduled-task-controls .atab.on,
.scheduled-task-controls .atab.is-active,
.scheduled-task-controls .atab.is-active:nth-child(2),
.scheduled-task-controls .atab.on:nth-child(2) {
  background: var(--accent, #B23A34) !important;
  color: #ffffff !important;
  font-weight: 600 !important;
  box-shadow: 0 1px 3px rgba(178, 58, 52, 0.35) !important;
}
.range-row {
  display: flex !important;
  align-items: center !important;
  gap: 12px !important;
  width: 100% !important;
  max-width: 220px !important;
  justify-content: flex-end !important;
}
.range-slider {
  flex: 1 !important;
  accent-color: var(--brand, #c8372d) !important;
  cursor: pointer !important;
}

/* Horizontal alignment of assistant response footer elements */
.msg-meta-row {
  display: flex !important;
  flex-direction: row !important;
  align-items: center !important;
  justify-content: flex-start !important;
  gap: 8px !important;
  flex-wrap: nowrap !important;
  margin-top: 6px !important;
}
.msg-meta-row > * {
  display: inline-flex !important;
  align-items: center !important;
  vertical-align: middle !important;
  height: 26px !important;
  margin: 0 !important;
}

/* Compact MCP Directory Grid with Authentic Brand Logos */
.mcp-directory-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
  gap: 8px;
  margin-top: 8px;
}
.mcp-symbol-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 10px 6px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-sm, 8px);
  cursor: pointer;
  text-align: center;
  gap: 6px;
  transition: all 0.15s ease;
  min-height: 64px;
}
.mcp-symbol-card:hover {
  border-color: var(--ink);
  background: var(--surface);
  transform: translateY(-1px);
}
.mcp-symbol-icon {
  width: 22px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.mcp-symbol-icon svg {
  width: 20px;
  height: 20px;
  display: block;
}
.mcp-symbol-name {
  font-size: 11.5px;
  font-weight: 500;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
/* A restricted directory entry this account holds no key for: visible, but plainly not offered. */
.mcp-symbol-card[data-no-access="true"] { opacity: .45; cursor: default; filter: grayscale(1); }
.mcp-symbol-card[data-no-access="true"]:hover { border-color: var(--line); transform: none; }
.mcp-symbol-note { margin-top: 3px; font-size: 9px; letter-spacing: .06em; text-transform: uppercase; color: var(--mute); }

/* In-form segmented rows (Scheduled tasks, etc.) */
.form-srow {
  display: flex !important;
  align-items: center !important;
  justify-content: space-between !important;
  gap: 12px !important;
  margin: 10px 0 !important;
  padding: 10px 0 !important;
  border-top: 1px solid var(--line-soft, var(--line)) !important;
  border-bottom: 1px solid var(--line-soft, var(--line)) !important;
}
.form-srow .srow-lbl {
  display: flex !important;
  flex-direction: column !important;
  gap: 2px !important;
  min-width: 0 !important;
  flex: 1 !important;
}
.srow-title {
  font-size: calc(13px * var(--app-font-scale, 1)) !important;
  font-weight: 600 !important;
  color: var(--ink) !important;
}
.form-srow .srow-val {
  flex: none !important;
  display: flex !important;
  justify-content: flex-end !important;
}
.form-srow .srow-val .atabs {
  display: inline-flex !important;
  background: var(--surface-2, #ebebe7) !important;
  padding: 3px !important;
  border-radius: var(--r-pill, 9999px) !important;
  gap: 2px !important;
}
.form-srow .srow-val .atab {
  appearance: none !important;
  border: 0 !important;
  background: transparent !important;
  padding: 5px 10px !important;
  border-radius: var(--r-pill, 9999px) !important;
  font-family: var(--sans) !important;
  font-size: calc(11.5px * var(--app-font-scale, 1)) !important;
  font-weight: 500 !important;
  color: var(--ink-2) !important;
  cursor: pointer !important;
  transition: all 0.15s ease !important;
  white-space: nowrap !important;
}
.form-srow .srow-val .atab.is-active {
  background: var(--surface, #ffffff) !important;
  color: var(--ink, #0e1116) !important;
  font-weight: 600 !important;
  box-shadow: 0 1px 3px rgba(0,0,0,0.08) !important;
}

/* Email Reader enhancements: action badges, summaries & proposed reply actions */
.email-badge {
  display: inline-flex;
  align-items: center;
  font-size: 11px;
  font-weight: 700;
  padding: 2px 7px;
  border-radius: var(--r-pill, 9999px);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  flex: none;
}
.email-badge.is-urgent {
  background: color-mix(in srgb, var(--accent) 18%, var(--surface));
  color: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
}
.email-badge.is-action {
  background: color-mix(in srgb, var(--accent) 12%, var(--surface));
  color: var(--accent);
}
.email-badge.is-info {
  background: var(--surface-2);
  color: var(--mute);
}
.email-row.is-urgent .email-row-head {
  border-left: 3px solid var(--accent);
}
.email-row-action-summary {
  font-size: 12px;
  color: var(--ink-2);
  padding: 0 12px 6px 36px;
  font-weight: 500;
}
.email-summary-box {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm, 6px);
  padding: 10px 12px;
  margin-bottom: 12px;
}
.email-summary-title {
  font-size: 11.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--mute);
  margin-bottom: 4px;
}
.email-summary-text {
  font-size: 13px;
  color: var(--ink);
  line-height: 1.4;
}
.email-intervention-alert {
  margin-top: 6px;
  font-size: 12px;
  font-weight: 600;
  color: var(--accent);
}
.email-fyi-alert {
  margin-top: 6px;
  font-size: 12px;
  color: var(--mute);
}
.email-propose-btn {
  appearance: none;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  font-size: 12.5px;
  font-weight: 600;
  padding: 6px 12px;
  border-radius: var(--r-pill, 9999px);
  cursor: pointer;
  transition: all 0.15s ease;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.email-propose-btn:hover {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
}
.email-custom-reply-btn {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--mute);
  font-size: 12px;
  cursor: pointer;
  padding: 6px 8px;
  text-decoration: underline;
}
.email-custom-reply-btn:hover {
  color: var(--ink);
}

/* Task plan — a quiet strip, not a panel (Ben, 2026-08-18: "displayed very discreetly").
   What was here was a 360px fixed side panel with a progress meter, a percentage, status
   pills and per-step detail cards. It has no replacement at that size on purpose: a plan is
   context for the answer, not a second application to look at. */
.plan-strip {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: 92px;
  z-index: 100;
  max-width: min(680px, 92vw);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-xs, 4px);
  box-shadow: var(--shadow);
  font-size: 12px;
  overflow: hidden;
}
.plan-strip-summary {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 6px 10px;
  background: transparent;
  border: 0;
  font: inherit;
  color: var(--mute);
  cursor: pointer;
  text-align: left;
}
.plan-strip-count { font-family: var(--mono); color: var(--ink); }
/* One line, truncated: a long step label must not grow the strip into a block of text. */
.plan-strip-step { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.plan-strip-flag { margin-left: auto; color: var(--ink); border: 1px solid var(--line); border-radius: 3px; padding: 0 5px; }
.plan-strip-steps {
  list-style: none;
  margin: 0;
  padding: 4px 10px 8px;
  border-top: 1px solid var(--line);
  max-height: 40vh;
  overflow-y: auto;
}
.plan-strip-row { display: flex; align-items: center; gap: 8px; padding: 3px 0; color: var(--mute); }
.plan-strip-row.is-in_progress .plan-strip-row-label,
.plan-strip-row.is-waiting_approval .plan-strip-row-label { color: var(--ink); }
.plan-strip-row.is-done .plan-strip-row-label { text-decoration: line-through; }
.plan-strip-row-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.plan-strip-row-state { font-family: var(--mono); font-size: 11px; }
.plan-strip-action { font: inherit; font-size: 11px; padding: 1px 7px; border: 1px solid var(--line); background: transparent; color: var(--ink); border-radius: 3px; cursor: pointer; }
.account-email-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  background: var(--surface);
  border-radius: var(--r-card, 10px);
  border: 1px solid var(--line);
  margin-bottom: 8px;
}
.account-email-icon {
  color: var(--accent);
  flex-shrink: 0;
}
.account-email-text {
  font-size: calc(0.92rem * var(--app-font-scale, 1));
  font-weight: 500;
  color: var(--ink);
  overflow-wrap: anywhere;
  word-break: break-word;
}
.scheduled-task-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-card, 10px);
  padding: 12px 14px;
  margin-bottom: 10px;
  transition: border-color 0.15s ease;
}
.scheduled-task-card:hover {
  border-color: var(--line-strong, #ccc);
}
.scheduled-task-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 6px;
}
.scheduled-task-title-group {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1;
  min-width: 0;
}
.scheduled-task-icon {
  color: var(--accent);
  flex-shrink: 0;
}
.scheduled-task-title {
  font-weight: 600;
  font-size: calc(0.92rem * var(--app-font-scale, 1));
  color: var(--ink);
  margin-right: 8px;
}
.scheduled-task-recurrence {
  font-size: 11.5px;
  color: var(--mute);
}
.scheduled-task-controls {
  display: flex;
  align-items: center;
  gap: 10px;
}
.scheduled-task-controls .atabs {
  margin: 0 !important;
}
.scheduled-task-delete-btn {
  appearance: none;
  background: transparent;
  border: 0;
  color: var(--mute);
  padding: 4px;
  cursor: pointer;
  border-radius: var(--r-sm, 6px);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: color 0.15s ease, background 0.15s ease;
}
.scheduled-task-delete-btn:hover {
  color: var(--danger, #d32f2f);
  background: var(--danger-soft, rgba(211, 47, 47, 0.08));
}
.scheduled-task-prompt {
  font-size: calc(0.82rem * var(--app-font-scale, 1));
  color: var(--ink-secondary, #666);
  line-height: 1.4;
}
.custom-skill-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-card, 10px);
  padding: 12px 14px;
  margin-bottom: 10px;
}
.custom-skill-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 6px;
}
.custom-skill-name {
  font-weight: 600;
  font-size: calc(0.92rem * var(--app-font-scale, 1));
  color: var(--ink);
}
.custom-skill-tag {
  font-size: 11px;
  font-weight: 600;
  padding: 2px 7px;
  border-radius: var(--r-pill, 9999px);
  background: var(--surface-2);
  color: var(--mute);
}
.custom-skill-desc {
  font-size: calc(0.82rem * var(--app-font-scale, 1));
  color: var(--ink-secondary, #666);
  line-height: 1.4;
}

/* Compact Email Reader Symbol Badges & Single-line rows */
.email-symbol-badge {
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  width: 18px !important;
  height: 18px !important;
  padding: 0 !important;
  font-size: 11px !important;
  font-weight: 700 !important;
  border-radius: 50% !important;
  flex: none !important;
  line-height: 1 !important;
}
.email-symbol-flag {
  font-size: 13px !important;
  margin-right: 4px !important;
  flex: none !important;
}
.email-row-head:hover {
  background: var(--surface-2) !important;
}

/* Danger variant for modals and delete actions */
.btn-danger,
button.btn-danger {
  background: var(--danger, #d32f2f) !important;
  color: #ffffff !important;
  border-color: transparent !important;
}
.btn-danger:hover,
button.btn-danger:hover {
  background: color-mix(in srgb, var(--danger, #d32f2f) 85%, black) !important;
}

/* Frequency tabs compact & brand accents */
.atabs.atabs-compact {
  gap: 4px;
  padding: 3px;
}
.atabs.atabs-compact .atab {
  padding: 4px 8px;
  font-size: 11.5px;
}
.atab.is-active,
.atab.on,
.atabs-compact .atab.is-active,
.atabs-compact .atab.on {
  background: var(--accent, #B23A34) !important;
  color: #ffffff !important;
  font-weight: 600 !important;
  box-shadow: 0 1px 3px rgba(178, 58, 52, 0.35) !important;
}

/* Slider value label */
.range-val-lbl {
  font-weight: 600;
  color: var(--ink);
  min-width: 50px;
  text-align: right;
}




/* Mail rows — Ben's 2026-08-17 review: one line per mail, the action verdict as a word, the
   summary on hover instead of under the row, and the proposals leading the opened mail.
   Authored here in the design-system extension layer (the CDS source is not in this repo, see
   design skill) so app code keeps pointing at semantic classes only. */
.email-row-shell { position: relative; }
.email-status {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  border-radius: var(--r-pill, 9999px);
  padding: 2px 7px;
  flex: none;
  white-space: nowrap;
}
.email-status.is-action { background: var(--accent-soft); color: var(--accent); border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent); }
.email-status.is-info { background: var(--surface-2); color: var(--mute); border: 1px solid var(--line-soft); }
.email-status.is-unknown { background: transparent; color: var(--faint); border: 1px dashed var(--line); }
.email-hover-summary {
  position: absolute;
  left: 34px;
  right: 10px;
  top: calc(100% - 2px);
  z-index: 8;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 8px 10px;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--ink-2);
  box-shadow: 0 8px 22px rgba(14, 17, 22, 0.10);
}
.email-proposals { display: flex; flex-direction: column; gap: 7px; padding: 10px 12px 12px; border-bottom: 1px solid var(--line-soft); }
.email-proposals-summary { font-size: 13px; line-height: 1.5; color: var(--ink); }
.email-proposals-verdict { font-family: var(--mono); font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--mute); }
.email-proposals-list { display: flex; flex-wrap: wrap; gap: 6px; }
.email-proposals-status { font-size: 12.5px; color: var(--mute); }
.email-proposals-status.is-error { color: var(--accent); }

/* ============================================================================
   Translator mode — mail client surface (screen module: public/app/mail.js).
   Three panes at desk width (folders | message list | reading pane), one pane at
   phone width. Dense by intent: an inbox is a working surface, so rows are tight
   and the reading pane starts at the top of the fold, not below a hero.
   Every value below is a design-system token — no app file may restate any of it.
   ============================================================================ */
/* ---------------------------------------------------------------- Work: contrast (WS-15)
   The Work surface reads all day, so the two columns a person steers with have to be
   something you can see from across the desk, and every word on them has to clear AA. The
   rail and the directory column beside it therefore have their OWN ground — a real step
   darker than the page rather than the near-white --surface-2 they used to share with the
   list header — with ink, a muted grey and an eyebrow grey measured against that ground,
   and a selected row that is the solid accent rather than a pale wash of it.

   These are tokens, not colours written into rules, so all three themes state their own
   values and nothing else in the product moves. Measured on the ground each sits on:
   light  ink 12.8:1 · muted 7.4:1 · eyebrow 5.1:1 · selected row 5.9:1
   night  ink 15.4:1 · muted 8.8:1 · eyebrow 6.4:1 · selected row 9.0:1
   --work-dim is the same job for the reading columns: the 9-10px mono times, day headings
   and counts used --faint, which is a hairline grey meant for decoration, not for a date a
   person is trying to read. */
:root {
  --work-rail-bg: #E3E3DE;
  --work-rail-line: #C9C9C2;
  --work-rail-ink: #171A1F;
  --work-rail-mute: #4E545B;
  --work-rail-label: #545A61;
  --work-rail-hover: #D3D3CC;
  --work-rail-active-bg: #B23A34;
  --work-rail-active-ink: #FFFFFF;
  --work-dim: #5C6167;
}
[data-theme="night"] {
  --work-rail-bg: #0A0C10;
  --work-rail-line: #2C313A;
  --work-rail-ink: #F2F2F0;
  --work-rail-mute: #A9AEB6;
  --work-rail-label: #8E949C;
  --work-rail-hover: #191D24;
  --work-rail-active-bg: #2A3566;
  --work-rail-active-ink: #DCE1FF;
  --work-dim: #A2A9B2;
}
[data-theme="enclave"] {
  --work-rail-bg: #07090A;
  --work-rail-line: #232A2F;
  --work-rail-ink: #EDEFF0;
  --work-rail-mute: #A6AEB4;
  --work-rail-label: #8B949B;
  --work-rail-hover: #14181B;
  --work-rail-active-bg: #C6F32F;
  --work-rail-active-ink: #0B0D0F;
  --work-dim: #9AA3AA;
}

/* ---------------------------------------------------------------- Work + Yarnball (WS-08)
   Work is a working surface with somebody to work with: the mail/calendar/Teams/files frame
   on the left, and Yarnball's own conversation held open on the right for as long as the
   screen is up — with nothing picked, with a message open, with a file open. It is the SAME
   chat component the Chat screen renders, so the column below is a place to put it, not a
   second chat: everything inside it is the design system's existing chat vocabulary
   (.yarnball-main, .card, .compwrap, .box), re-proportioned here for a column instead of a
   page and nowhere else. */
.work-frame {
  display: flex;
  flex: 1;
  min-height: 0;
  min-width: 0;
  /* The handle hangs off this, and rides it as the pane moves. */
  position: relative;
}
.work-chat {
  /* WS-D23 (item 11 of Ben's mail markup, 2026-08-22): "Bigger and RESIZABLE." The width
     is a custom property set by D.ChatResizer (_ds_bundle.js), the same pattern .mail-shell
     uses for its own three columns — this file only ever reads it, never writes a style.
     348 is the width the surface has always opened at, kept as the fallback. */
  position: relative;
  display: flex;
  /* Sized by the grip; 348px is only where it starts. */
  flex: 0 0 var(--work-chat-w, 348px);
  flex-direction: column;
  /* CR-5 (2026-08-26): "min 280px, max 60vw" enforced here too, not only in the grip's own
     JS clamp — a width persisted before this rule existed, or any other future writer of
     the custom property, still cannot push the pane narrower or wider than this. */
  min-width: 280px;
  max-width: 60vw;
  min-height: 0;
  border-left: 1px solid var(--line-soft);
  background: var(--bg);
}
/* The grip sits on the LEFT edge of this column and drags the opposite way from every grip
   .mail-shell draws: those grow their column moving right, this one grows moving left,
   because it is the last column on the screen. Same hit-area and reveal-on-interaction
   treatment as .work-rail-grip and friends. */
.work-chat-grip {
  position: absolute;
  top: 0;
  bottom: 0;
  left: -4px;
  z-index: 4;
  width: 9px;
  cursor: col-resize;
  touch-action: none;
}
.work-chat-grip::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: 4px;
  width: 1px;
  background: transparent;
}
.work-chat-grip:hover::before,
.work-chat-grip:focus-visible::before,
.work-chat-grip.is-dragging::before { background: var(--accent); }
.work-chat-grip:focus-visible { outline: none; }
/* Redline p11 item 1 ("Put this drawer on top of the email!"): on the Mail screen only
   (.work-frame[data-source="mail"], set in work.js), the Yarnball pane overlays the reading
   pane instead of sitting in a fixed column that pushes it narrower. It keeps its own width
   and the same resize grip; it just no longer takes a flex slot, so the mail list/reading
   pane behind it stay full width and the pane floats above them with a shadow and a scrim
   to close by clicking outside, the same way every other overlay panel in this app works. */
.work-frame[data-source="mail"] .work-chat {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  /* flex-basis stops driving width once this is out of flow — the resizable width (the
     same custom property D.ChatResizer already writes) has to be restated explicitly. */
  width: var(--work-chat-w, 348px);
  z-index: 20;
  box-shadow: -8px 0 24px rgba(0, 0, 0, 0.18);
}
.work-chat-scrim,
.work-frame[data-source="mail"] .work-chat-scrim {
  position: absolute;
  inset: 0;
  z-index: 19;
  background: color-mix(in srgb, var(--ink) 15%, transparent);
}
/* Shut state for the Mail overlay above. That rule takes the pane out of flow, so the flex
   rule which otherwise collapses it when the drawer is closed (.work-frame.is-chat-shut
   .work-chat, further down this file) can no longer reach it — Mail would keep showing the
   drawer at full width after it was closed. Mail's own shut rule says so explicitly. It
   hides the pane rather than removing it, so D.ChatResizer's ResizeObserver keeps measuring
   a real width: the remembered width is never overwritten with the zero a display:none box
   reports, and re-opening paints at the right size with no hitch. The handle stays above the
   scrim so the way out is still the handle the reader came in by. */
.work-frame[data-source="mail"].is-chat-shut .work-chat { visibility: hidden; }
.work-frame[data-source="mail"] .work-chat-tab { z-index: 21; }
/* Items 10, 12, 13 (Ben's mail markup): the briefing at the top of Yarnball's column —
   what matters right now, real actions that send straight into the same chat pipeline
   (item 12: "real action starts here"), and a line to ask for more context. Sits above the
   transcript, inside the same resizable column, and never claims a number it did not
   actually read (WorkBriefing renders nothing where it has nothing real to say). */
.work-briefing {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--line-soft);
  background: var(--surface);
}
.work-briefing-label { font-family: var(--mono); font-size: 11px; letter-spacing: 0.07em; text-transform: uppercase; color: var(--work-dim); }
.work-briefing-text { margin: 0; font-family: var(--ui); font-size: 12.5px; line-height: 1.45; color: var(--ink-2); }
.work-briefing-actions { display: flex; flex-wrap: wrap; gap: 6px; }
.work-briefing-ask { display: flex; align-items: center; gap: 6px; }
.work-briefing-input {
  flex: 1;
  min-width: 0;
  padding: 6px 8px;
  border: 1px solid var(--line);
  border-radius: var(--r-xs);
  background: var(--surface-2);
  color: var(--ink);
  font-family: var(--ui);
  font-size: 12px;
}
/* Item 14 (Ben's mail markup): right-click anywhere on the Work screen for an AI action.
   Positioned by D.ContextMenu (_ds_bundle.js), which clamps the point on screen and
   publishes it as --ctx-x/--ctx-y — this file only ever reads those two properties. */
.yb-context-menu {
  position: fixed;
  top: var(--ctx-y, 0);
  left: var(--ctx-x, 0);
  z-index: 96;
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 200px;
  padding: 6px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
  box-shadow: var(--shadow);
}
.yb-context-item {
  padding: 7px 8px;
  border: 0;
  border-radius: var(--r-xs);
  background: transparent;
  color: var(--ink);
  font-family: var(--ui);
  font-size: 12.5px;
  text-align: left;
  cursor: pointer;
}
.yb-context-item:hover,
.yb-context-item:focus-visible { background: var(--work-rail-hover); outline: none; }
/* A column is not a page: the transcript's page margins and its 1040px measure are for a
   full-width screen, and inside 348px they leave the conversation a gutter to live in. */
.work-chat .yarnball-main { padding: 10px 0 4px; justify-content: flex-start; }
.work-chat .card { max-width: none; padding: 0 12px; }
.work-chat .compwrap { max-width: none; padding: 8px 12px 12px; }
.work-chat .greet { font-size: 22px; margin: 2px 0 8px; }
.work-chat .sub { font-size: 12.5px; margin-bottom: 12px; }
.work-chat .box textarea { font-size: 13.5px; }
.work-chat .hint { display: none; }
/* ------------------------------------------------------------------ WS-D5: the composer fits its column
   The composer is a flex row: four round controls and one text field. A flex item's
   automatic minimum size is its CONTENT's minimum, and a <textarea>'s content minimum is
   the intrinsic width of its `cols` attribute — twenty characters, about 190px at 15px.
   Add the four 28-34px controls, four 5px gaps and the row's own 20px of padding and the
   composer's minimum width is roughly 375px. Nothing gave, so in any container narrower
   than that the row overflowed to the right instead of shrinking: in the 348px Yarnball
   column on Work the placeholder was cut mid-word and the send control sat half past the
   right edge of the screen.

   This is not the Work chat pane's defect. The composer has always had this minimum; the
   pane is simply the first surface to give it a column rather than a page, which is also
   why "right rail clipped at viewport edge" was recorded against the right-hand dock on
   2026-07-27. The fix belongs to the composer and applies everywhere: the field is the
   part that yields, so it is the part allowed to. */
.box { min-width: 0; }
.box textarea { min-width: 0; }
.compwrap { min-width: 0; }
/* A column narrower than a page runs the composer's controls at the same density the
   phone already uses, so the field keeps a readable share of the width once it can
   shrink. Same treatment as .box-compact, applied by where the composer is rather than
   by which device is holding it. */
.work-chat .box { gap: 4px; padding: 5px 5px 5px 10px; }
.work-chat .box .clip { width: 30px; height: 30px; }
.work-chat .box .send { width: 30px; height: 30px; }
.work-chat-note {
  padding: 16px 12px;
  font-family: var(--ui);
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--mute);
}
/* SB 2026-09-04: a rule here used to force the pane to 300px on any window narrower than
   1500px, whatever width the reader had dragged it to - and the handle, reading the dragged
   width, stood 48px inside the pane. The pane is the width the reader gave it, clamped by
   the floor and ceiling above; a narrower window is what the ceiling is for. */
/* There is a width below which five columns is four columns and a sliver: the reading pane
   is the one that runs out of room first, and a message opened into 160px is not opened.
   So below it the frame stops being columns and becomes two stacked surfaces — the
   conversation keeps a readable share of the HEIGHT instead of being dropped or squeezing
   what it is meant to be talking about. */
@media (max-width: 1180px) {
  .work-frame { flex-direction: column; }
  .work-chat {
    flex: 0 0 auto;
    max-height: 42vh;
    border-left: 0;
    border-top: 1px solid var(--line-soft);
    /* The 60vw ceiling is for a column beside the mail; a band takes the whole width. */
    max-width: none;
  }
  /* SB 2026-09-04: stacked, the pane is a band across the whole width, so its width is
     not a distance from the right edge any more. The handle stays on the edge; the .is-open
     rule below is the one that carried it 348px in, hence the same specificity here. */
  .work-chat-tab.is-open { right: 0; }
}

.mail-shell {
  /* WS-07: the two left columns are as wide as the reader dragged them. The value arrives
     as a custom property set by the D.WorkShell component on this element (see the
     2026-08-21 addendum in _ds_bundle.js) — never from app code, and never as a written
     grid-template, so the phone rule below can still collapse the whole frame to one
     column. The fallbacks are the widths this surface has always opened at. */
  position: relative;
  display: grid;
  /* The fourth track is the Yarnball panel, shut. It is a column rather than an overlay so
     that opening it narrows the reading pane instead of covering it. position:relative is
     what the .yb-tab handle hangs off. The first two widths come from the custom properties
     the column grips write, so a dragged edge and a docked panel are not two layouts
     arguing -- one grid, four tracks, two of them movable. */
  grid-template-columns: var(--work-rail-w, fit-content(280px)) var(--work-list-w, 340px) minmax(0, 1fr) 0;
  transition: grid-template-columns .34s cubic-bezier(.32, .72, 0, 1);
  position: relative;
  align-items: stretch;
  flex: 1;
  min-height: 0;
  min-width: 0;
  width: 100%;
  background: var(--surface);
  border-top: 1px solid var(--line-soft);
}
.mail-shell.work-assist-open { grid-template-columns: var(--work-rail-w, fit-content(280px)) var(--work-list-w, 340px) minmax(0, 1fr) 296px; }
/* The easing above is for the Yarnball panel sliding open. While a column edge is held
   (D.WorkShell sets is-sizing), every pointer move is a new width, and easing each one made
   the columns trail the edge by a third of a second. The same rule .yb-f.yb-is-sizing
   follows in layouts.css. */
.mail-shell.is-sizing { transition: none; cursor: col-resize; user-select: none; }
/* The panel's chrome, thread and handle are the side rail's own (.yb-edge-*, .yb-b*,
   .yb-tab* in layouts.css) — this is that same panel docked into a different screen, not a
   second one built to look like it. Only the docking is new. */
.work-assist { display: flex; align-items: stretch; min-width: 0; min-height: 0; overflow: hidden; }
/* The way in and the way out in one place: a handle halfway down the right edge that opens
   the panel and closes it, and rides the edge as the panel slides so it is never anywhere
   else (SB, 2026-08-20). Deliberately louder than the Tiles rail's bare chevron -- that one
   works because the whole workspace is built around it, and on Work a 20px sliver reads as
   a rendering artefact rather than a control. This one carries the icon and the word. */
.work-assist-tab {
  position: absolute;
  top: 50%;
  right: 0;
  transform: translateY(-50%);
  z-index: 6;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 36px;
  padding: 16px 0;
  border: 1px solid var(--line);
  border-right: 0;
  border-radius: var(--r) 0 0 var(--r);
  background: var(--surface);
  color: var(--mute);
  box-shadow: var(--shadow-sm);
  font: inherit;
  cursor: pointer;
  transition: right .34s cubic-bezier(.32, .72, 0, 1), color .15s ease, border-color .15s ease;
}
.work-assist-tab:hover { color: var(--accent); border-color: var(--accent); }
.work-assist-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.work-assist-tab.is-open { right: 296px; color: var(--accent); border-color: var(--accent); }
.work-assist-tab-label {
  writing-mode: vertical-rl;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
}
/* A thumb is not a mouse pointer: the handle widens to a real touch target on a phone, and
   the panel takes the whole column there, so open it sits against the wall it came from. */
@media (max-width: 900px) {
  .work-assist-tab { width: 44px; }
  .work-assist-tab.is-open { right: 0; }
}
/* The edge between two columns, as something you can put a cursor on. It is a hit area
   wider than the line it draws, sitting astride the boundary, and it only shows its line
   while it is under the pointer, focused, or being dragged — an inbox is not a place for a
   permanent set of handles. */
.work-rail-grip,
.work-directory-grip,
.work-list-grip {
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: 4;
  width: 9px;
  margin-left: -4px;
  cursor: col-resize;
  touch-action: none;
}
.work-rail-grip { left: var(--work-rail-w, 190px); }
.work-directory-grip { left: calc(var(--work-rail-w, 190px) + var(--work-directory-w, 250px)); }
/* The second column on every source that has no directory: the list. Teams' second column
   is the directory above; both are the reader's to size, so both carry an edge. */
.work-list-grip { left: calc(var(--work-rail-w, 190px) + var(--work-list-w, 340px)); }
.work-rail-grip::before,
.work-directory-grip::before,
.work-list-grip::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: 4px;
  width: 1px;
  background: transparent;
}
.work-rail-grip:hover::before,
.work-rail-grip:focus-visible::before,
.work-rail-grip.is-dragging::before,
.work-directory-grip:hover::before,
.work-directory-grip:focus-visible::before,
.work-directory-grip.is-dragging::before,
.work-list-grip:hover::before,
.work-list-grip:focus-visible::before,
.work-list-grip.is-dragging::before { background: var(--accent); }
.work-rail-grip:focus-visible,
.work-directory-grip:focus-visible,
.work-list-grip:focus-visible { outline: none; }
.mail-rail {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-height: 0;
  overflow-y: auto;
  padding: 6px 6px;
  border-right: 1px solid var(--work-rail-line);
  background: var(--work-rail-bg);
}
.mail-rail-label {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--work-rail-label);
  padding: 10px 8px 4px;
}
.mail-folder {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 4px 8px;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--work-rail-ink);
  font-family: var(--ui);
  font-size: 13px;
  text-align: left;
  cursor: pointer;
}
.mail-folder:hover { background: var(--work-rail-hover); }
.mail-folder.active { background: var(--work-rail-active-bg); color: var(--work-rail-active-ink); box-shadow: inset 3px 0 0 var(--work-rail-active-bar, transparent); font-weight: 600; }
.mail-folder-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mail-folder-count { font-family: var(--mono); font-size: 11px; color: var(--work-rail-mute); }
.mail-folder.active .mail-folder-count { color: var(--work-rail-active-ink); }
/* Redline p10 item 5: a Mailbox row is a checkable toggle (included in the combined list)
   rather than an exclusive switch — the check box shows that state; .active reuses the same
   highlight a selected folder gets. is-primary (the account driving the folder tree/new
   mail identity) gets a touch more weight, not a second colour — one signal is enough. */
.mail-account-check {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  flex: 0 0 auto;
  color: var(--accent);
}
.mail-folder.is-account.is-primary .mail-folder-name { font-weight: 600; }
/* Item 4 (Ben's mail markup): the automatic/manual settings pass, as a row of switches in
   the rail rather than a screen of its own — read every time this pane opens, so it earns
   the space here. */
.mail-automation { display: flex; flex-direction: column; gap: 2px; margin-bottom: 6px; }
.mail-automation-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  width: 100%;
  padding: 5px 8px;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--work-rail-ink);
  font-family: var(--ui);
  font-size: 11.5px;
  text-align: left;
  cursor: pointer;
}
.mail-automation-toggle:hover { background: var(--work-rail-hover); }
.mail-automation-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mail-automation-state {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--work-rail-mute);
}
.mail-automation-toggle.is-manual .mail-automation-state { color: var(--accent); }

/* ---------------------------------------------------------------- the language mark (WS-17)
   The language a person reads in is set once and then never touched, so it does not get a
   rail row with a label and a dropdown in it — it gets one mark at the top of the rail, and
   the list of languages behind it. The list is drawn against the FRAME rather than the rail,
   because the rail is a scrolling column and anything inside a scrolling column is cut off
   at its edge; the mark stays put at the top of the rail so the two stay together however
   far the folder tree below it is scrolled. */
.work-lang {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  padding-bottom: 8px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--work-rail-line);
  background: var(--work-rail-bg);
}
.work-lang-button {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--work-rail-mute);
  cursor: pointer;
}
.work-lang-button:hover { color: var(--work-rail-ink); background: var(--work-rail-hover); }
.work-lang-button.active { color: var(--work-rail-active-ink); border-color: var(--work-rail-active-bg); background: var(--work-rail-active-bg); }
.work-lang-menu {
  position: absolute;
  top: 42px;
  left: 8px;
  z-index: 30;
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 184px;
  max-height: 58vh;
  overflow-y: auto;
  padding: 6px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
  box-shadow: var(--shadow);
}
.work-lang-title {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--work-dim);
  padding: 2px 6px 5px;
}
.work-lang-option {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 4px 6px;
  border: 0;
  border-radius: var(--r-xs);
  background: transparent;
  color: var(--ink-2);
  font-family: var(--ui);
  font-size: 12.5px;
  text-align: left;
  cursor: pointer;
}
.work-lang-option:hover { background: var(--surface-2); }
.work-lang-option.active { background: var(--accent-soft); color: var(--accent); }
.work-lang-name { flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.work-lang-code { font-family: var(--mono); font-size: 11px; color: var(--mute); }
.work-lang-option.active .work-lang-code { color: var(--accent); }

.mail-list-pane {
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
  border-right: 1px solid var(--line-soft);
}
.mail-list-head {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 8px;
  border-bottom: 1px solid var(--line-soft);
  background: var(--surface);
}
.mail-search {
  flex: 1;
  min-width: 0;
  padding: 5px 8px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface-2);
  color: var(--ink);
  font-family: var(--ui);
  font-size: 12.5px;
}
.mail-list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}
/* YUM-910 (row density, coordinator 2026-08-22, on Ben's own "dense by default, no
   whitespace bloat" rule): a row used to spend four lines — from+date, subject, a preview
   line, a full-width flags line — and five messages filled the whole pane. .mail-item is
   now a flex ROW: the clickable open-button (from+time, then subject+preview merged into
   one truncated line — two lines total, not three) on the left, and the flags — now a
   column, and only ever holding what actually differs from the default — in a fixed-width
   strip on the right, so a plain unremarkable message costs exactly the two lines its
   content needs and nothing declares itself for having nothing to declare. */
/* Redline p15 items 5-7 / p18 item 1 ("no frames" / "reduce, size, compress" / "compress"):
   the border between every row read as a literal grid of frames around each message; it is
   gone. The left border stays — it is transparent except on the active row, so it is a
   selection indicator, not a frame. Padding is tightened further on top of the density pass
   already in this row (see the comment above .mail-item below in source history). */
.mail-item {
  display: flex;
  align-items: flex-start;
  gap: 4px;
  width: 100%;
  padding: 3px 8px;
  border: 0;
  border-left: 2px solid transparent;
  background: transparent;
  text-align: left;
  cursor: pointer;
  font-family: var(--ui);
}
.mail-item:hover { background: var(--surface-2); }
.mail-item.active { background: var(--accent-soft); border-left-color: var(--accent); }
/* T (SB 2026-09-08, "the Yesterday and Last week thing, we need that too"): Outlook's date
   groups in the list - Pinned, Today, Yesterday, the weekday, Last week, ... Older - each
   a strip with a chevron that folds the group. Date order only; the other sorts have no
   dates to group by. */
.mail-list-group {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  padding: 5px 10px;
  border: 0;
  border-bottom: 1px solid var(--line-soft);
  background: var(--surface);
  color: var(--ink);
  font-family: var(--ui);
  font-size: 12px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
.mail-list-group:hover { background: var(--surface-2); }
.mail-list-group-chevron { display: inline-flex; color: var(--mute); transition: transform .15s ease; }
.mail-list-group-chevron svg { transform: rotate(90deg); }
.mail-list-group.is-folded .mail-list-group-chevron svg { transform: rotate(0deg); }
.mail-list-group-count { margin-left: auto; font-family: var(--mono); font-size: 11px; font-weight: 400; color: var(--mute); }
.mail-item-open { flex: 1; min-width: 0; }
.mail-item-top { display: flex; align-items: baseline; gap: 8px; }
.mail-item-from {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 13px;
  color: var(--ink);
}
.mail-item.unread .mail-item-from { font-weight: 600; }
.mail-item-time { font-family: var(--mono); font-size: 11px; color: var(--work-dim); white-space: nowrap; }
.mail-item-subject-line { display: flex; align-items: baseline; min-width: 0; margin-top: 1px; overflow: hidden; }
.mail-item-subject {
  flex: 0 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 12.5px;
  color: var(--ink-2);
}
.mail-item.unread .mail-item-subject { color: var(--ink); font-weight: 600; }
.mail-item-preview {
  flex: 1 1 0%;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 12px;
  color: var(--mute);
}
.mail-item-flags {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 3px;
  max-width: 92px;
}
.mail-flag {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 1px 5px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  color: var(--mute);
  background: var(--surface-2);
}
.mail-flag.is-translated { border-color: color-mix(in srgb, var(--accent) 35%, transparent); color: var(--accent); background: var(--accent-soft); }
/* D (SB 2026-09-08): a pinned row says so, in the same chip family as the thread count. */
.mail-flag.is-pinned { border-color: var(--ink-2); color: var(--ink); background: var(--surface); }
/* F (SB 2026-09-08): the shortcut sheet - key on the left, what it does on the right. */
.mail-keys { display: grid; grid-template-columns: 130px 1fr; gap: 8px 14px; align-items: baseline; margin: 4px 0 0; }
.mail-keys-key { margin: 0; }
.mail-keys-key kbd { font-family: var(--mono); font-size: 11px; padding: 2px 7px; border: 1px solid var(--line); border-radius: var(--r-xs); background: var(--surface-2); color: var(--ink); }
.mail-keys-what { margin: 0; font-size: 13px; line-height: 1.4; color: var(--ink-2); }
/* C2 (2026-08-26): a grouped thread row (thread sort only) expands in place to list the
   messages byThread() absorbed into it, instead of jumping straight to the reader — same
   rotate-on-expand chevron convention as the folder tree (.ds-tree-toggle). */
.mail-item.is-thread-group { cursor: pointer; }
.mail-thread-chevron { flex: 0 0 auto; color: var(--mute); transition: transform 120ms ease; }
.mail-item[aria-expanded='true'] .mail-thread-chevron { transform: rotate(90deg); }
.mail-thread-messages { display: flex; flex-direction: column; background: var(--surface-2); border-bottom: 1px solid var(--line-soft); }
.mail-thread-message { display: flex; align-items: center; gap: 8px; width: 100%; padding: 4px 10px 4px 28px; border: 0; border-top: 1px solid var(--line-soft); background: transparent; text-align: left; cursor: pointer; font-family: var(--ui); }
.mail-thread-message:hover { background: var(--surface); }
.mail-thread-message.active { background: var(--accent-soft); }
.mail-thread-message-from { flex: 0 0 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; color: var(--ink-2); }
.mail-thread-message.unread .mail-thread-message-from, .mail-thread-message.unread .mail-thread-message-subject { color: var(--ink); font-weight: 600; }
.mail-thread-message-subject { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; color: var(--mute); }
.mail-thread-message-time { flex: 0 0 auto; font-family: var(--mono); font-size: 11px; color: var(--work-dim); white-space: nowrap; }
/* Item 2 (Ben's mail markup): a translate button on the row itself, sized to sit beside
   the flags rather than compete with them. */
.mail-item-translate {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface-2);
  color: var(--mute);
  cursor: pointer;
}
.mail-item-translate:hover { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 35%, transparent); }
.mail-item-translate:disabled { opacity: 0.5; cursor: default; }
/* B (SB 2026-09-08, YUM-934): the globe stays lit while the row shows its translation; a
   press then brings the original back. */
.mail-item-translate.is-on { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 35%, transparent); background: var(--accent-soft); }
.mail-list-status { padding: 10px 12px; font-size: 12.5px; color: var(--mute); }
.mail-list-status.is-error { color: var(--accent); }

.mail-reader {
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
  background: var(--paper);
}
.mail-reader-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1;
  padding: 20px;
  font-size: 13px;
  color: var(--work-dim);
}
.mail-reader-head {
  /* G02 (SB 2026-09-04): the Work screen tightened - less air around every block.
     P28.1/P29.1/P29.4/P32.1 (redline 2026-09-26): "more compact viewer" / "Reduce
     Navigable" / "much much smaller" — one more pass, specifically on the file-preview
     header that sits above an ONLYOFFICE embed, so more of the document itself shows. */
  padding: 5px 12px 4px;
  border-bottom: 1px solid var(--line-soft);
  background: var(--surface);
}
/* Redline p12: one-line subject with ellipsis; full text in the title attribute. */
.mail-reader-subject { margin: 0; font-family: var(--display); font-size: 13.5px; line-height: 1.25; color: var(--ink); max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mail-reader-meta { display: flex; flex-wrap: wrap; gap: 2px 10px; margin-top: 1px; font-size: 11px; color: var(--mute); }
.mail-reader-meta-addr { color: var(--ink-2); }
.mail-reader-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 9px; }
/* Items 6 + 8 (Ben's mail markup, 2026-08-22): reply, reply-all, forward and the rest of
   the normal verbs, at the top of the reading pane — a real toolbar, not one Reply button
   found by scrolling past everything else. */
.mail-verb-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  padding: 8px 16px;
  border-bottom: 1px solid var(--line-soft);
  background: var(--surface);
}
/* The list pane never leaves the screen at desk width, so a "back to list" control
   would be a button that goes nowhere. It exists for the single-pane phone layout
   only, and the media query below is what turns it on. */
.mail-back-bar { display: none; }
.mail-notice {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding: 7px 16px;
  border-bottom: 1px solid var(--line-soft);
  background: var(--accent-soft);
  font-size: 12.5px;
  color: var(--accent);
}
.mail-notice.is-plain { background: var(--surface-2); color: var(--mute); }
.mail-notice-text { flex: 1; min-width: 0; }
/* The sender's own markup renders inside a sandboxed frame, so the reading pane cannot
   reach in to give it breathing room — the frame itself is what gets indented, on the
   same 16px gutter the header and the notice above it use. */
.mail-body-frame {
  flex: 1;
  min-height: 0;
  display: flex;
  padding: 6px 12px 10px;
  background: var(--paper);
}
.mail-body {
  flex: 1;
  min-height: 0;
  width: 100%;
  border: 0;
  background: var(--paper);
}
.mail-body-text {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  margin: 0;
  padding: 14px 16px;
  font-family: var(--ui);
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--ink);
  white-space: pre-wrap;
  word-break: break-word;
}

.mail-compose {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 8px 12px 10px;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line-soft);
  background: var(--surface);
}
/* B6 (SB 2026-09-08, "look how clean Outlook is"): the message stays on screen and the reply
   card sits UNDER it, as in Outlook's reading pane. The body keeps the room above the card;
   the card takes only its own height; a window too small for both scrolls the column.
   Redline p15 item 3 ("move up"): the body used to FORCE-GROW to fill every pixel of
   leftover column height (flex: 1 1 auto, min-height 160px) even for a six-line message,
   which is what pushed the compose card — and the empty space below it — toward the
   bottom of the pane. It now takes only the room its own content needs, capped so a very
   long message still scrolls instead of shoving the reply off-screen; the card follows
   right after it, near the top, the way a short email's reply does in every other client. */
.mail-reader:has(.mail-compose) { overflow-y: auto; }
.mail-reader:has(.mail-compose) .mail-body-frame { flex: 0 1 auto; max-height: 240px; overflow-y: auto; }
/* R: a plain-text body (.mail-body-text) gets the same content-hugging cap. */
.mail-reader:has(.mail-compose) .mail-body-text { flex: 0 1 auto; max-height: 240px; overflow-y: auto; }
.mail-reader:has(.mail-compose) .mail-compose { flex: 0 0 auto; }
.mail-compose-head { display: flex; align-items: center; gap: 8px; }
/* Item 8 (Ben's mail markup): the title now sits INSIDE .mail-compose-actions, first
   child, beside the buttons that do something — not on a row of its own above them. */
.mail-compose-title { font-family: var(--mono); font-size: 11px; letter-spacing: 0.07em; text-transform: uppercase; color: var(--work-dim); }
/* New meeting: a caption over each control (the modal used to borrow the input styles for its labels). */
.meeting-field { display: flex; flex-direction: column; gap: 4px; flex: 1 1 0; min-width: 0; margin-bottom: 10px; }
.meeting-field-label { color: var(--ink-2); font-family: var(--ui); font-size: 12px; font-weight: 600; }
.meeting-field > input, .meeting-field > select, .meeting-field > textarea { width: 100%; }
.mail-compose-field {
  width: 100%;
  padding: 6px 8px;
  border: 1px solid var(--line);
  border-radius: var(--r-xs);
  background: var(--surface-2);
  color: var(--ink);
  font-family: var(--ui);
  font-size: 12.5px;
}
.mail-compose-text {
  width: 100%;
  min-height: 86px;
  resize: vertical;
  padding: 8px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-2);
  color: var(--ink);
  font-family: var(--ui);
  font-size: 13px;
  line-height: 1.5;
}
/* Item 7 (Ben's mail markup): "Make the composer BIGGER." A real writing surface, not a
   four-line box under a subject line — min-height alone triples the old textarea, and it
   still resizes taller by hand. Reading text stays serif everywhere else on this surface;
   the composer is where the reader TYPES, so it keeps the UI face, same as the old
   textarea did. */
.mail-compose-rte {
  width: 100%;
  /* B5 (SB 2026-09-08): the original left the editor, so the body is a real writing surface
     from the start — Outlook gives the reply the room, not the quote. */
  /* R (SB 2026-09-08): a share of the pane, not a fixed strip; it grows with the text and
     the reader column scrolls - no cap on its height, so it never scrolls inside itself. */
  min-height: max(180px, 40vh);
  overflow-y: auto;
  resize: vertical;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-2);
  color: var(--ink);
  font-family: var(--ui);
  font-size: 13.5px;
  line-height: 1.55;
}
.mail-compose-rte:focus-visible { outline: none; border-color: var(--accent); }
.mail-compose-rte:empty::before { content: attr(data-placeholder); color: var(--mute); }
.mail-compose-rte p { margin: 0 0 8px; }
/* SB 2026-09-25 ("it goes all the way down on the email being replied to"): the reading pane
   is ONE scrolling column, as in Outlook - the whole message at its own height (BodyFrame
   grow sets --mail-body-h from the srcdoc), then the reply directly under it. The reply is
   no card: the same paper, one hairline between the message and the reply. The reply body
   is as tall as --reply-h, which the grip on the reply's top edge drags or keys. */
.mail-reader:has(.mail-body-frame.is-grow),
.mail-reader:has(.mail-body-text.is-grow) { overflow-y: auto; }
.mail-reader .mail-body-frame.is-grow { flex: none; min-height: 0; }
.mail-body-frame.is-grow .mail-body { flex: none; height: var(--mail-body-h, 320px); overflow: hidden; }
.mail-reader .mail-body-text.is-grow { flex: none; min-height: 0; overflow: visible; }
.mail-reader .mail-compose.is-sized { position: relative; flex: none; padding-top: 12px; border-top: 1px solid var(--line); border-bottom: 0; background: var(--paper); box-shadow: none; }
.mail-compose.is-sized .mail-compose-rte { flex: none; height: var(--reply-h, 320px); min-height: 0; resize: none; border: 0; border-radius: 0; background: transparent; padding: 8px 2px; }
/* Outlook's body is a bare writing area; the caret says where focus is, not a red frame. */
html .mail-compose.is-sized .mail-compose-rte:focus,
html .mail-compose.is-sized .mail-compose-rte:focus-visible { outline: none; box-shadow: none; border: 0; }
.mail-reply-grip {
  position: absolute;
  top: -5px;
  left: 0;
  right: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 10px;
  cursor: row-resize;
  touch-action: none;
}
/* The drawn grip: a short pill of two hairlines, centred on the rule. */
.mail-reply-grip::before {
  content: "";
  width: 32px;
  height: 4px;
  border-top: 1px solid var(--mute);
  border-bottom: 1px solid var(--mute);
  border-radius: var(--r-xs);
  background: var(--paper);
  opacity: 0.7;
}
.mail-reply-grip:hover::before,
.mail-reply-grip.is-dragging::before { border-color: var(--accent); opacity: 1; }
.mail-reply-grip.is-dragging { cursor: row-resize; }
.mail-reply-grip:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.mail-reply-grip:focus-visible::before { border-color: var(--accent); opacity: 1; }
.mail-compose-rte .yb-quote { margin-top: 14px; padding-top: 8px; border-top: 1px solid var(--line-soft); color: var(--mute); font-size: 12.5px; }
/* S (SB 2026-09-08, "make it more like Outlook"): the reading-pane reply. Compact: the
   "..." under the body that opens the full form; one bar at the bottom. Full form: the
   Send bar at the top, the subject line with the draft note beside it, the formatting
   strip, the body, then the message history under a rule, read-only, exactly what is
   appended at send. The .mail-reader:has() forms outrank B6's flex: 0 0 auto on the card. */
.mail-compose-more {
  align-self: flex-start;
  padding: 0 8px;
  border: 1px solid var(--line);
  border-radius: var(--r-xs);
  background: var(--surface-2);
  color: var(--ink-2);
  font-family: var(--ui);
  font-size: 11px;
  letter-spacing: 0.1em;
  line-height: 18px;
  cursor: pointer;
}
.mail-compose-more:hover { border-color: var(--accent); color: var(--accent); }
.mail-compose-subject-row { display: flex; align-items: baseline; gap: 10px; min-width: 0; }
.mail-compose-subject-row .mail-compose-subject { flex: 1 1 auto; min-width: 0; }
.mail-compose-actions.is-top { padding-bottom: 6px; border-bottom: 1px solid var(--line-soft); }
.mail-history-row { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--line); }
.mail-quote-block { width: 100%; color: var(--ink); font-family: var(--ui); font-size: 13px; line-height: 1.5; overflow-wrap: anywhere; }
.mail-quote-block p { margin: 0 0 6px; }
.mail-quote-block hr { border: 0; border-top: 1px solid var(--line); margin: 6px 0 10px; }
.mail-quote-block b { font-weight: 600; }
.mail-quote-block img { max-width: 100%; height: auto; }
/* K04: Outlook's split Send - the button and its arrow read as one control; the arrow's
   menu opens upward from the bottom bar so it stays on the screen. Schedule send is a
   strip in the card, not a popover. */
.mail-send-split { display: inline-flex; align-items: stretch; }
.mail-send-more {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  margin-left: 1px;
  border: 1px solid var(--ink);
  border-left: 0;
  border-radius: 0 var(--r-pill) var(--r-pill) 0;
  background: var(--ink);
  color: var(--on-ink);
  cursor: pointer;
}
.mail-send-more svg { transform: rotate(90deg); }
.mail-send-more:hover { background: var(--ink-2); border-color: var(--ink-2); }
.mail-send-more:disabled { opacity: 0.5; cursor: default; }
.mail-send-menu { min-width: 180px; }
.mail-send-menu.is-up { transform: translateY(-100%); }
.mail-send-menu .link-context-menu-item:disabled { opacity: 0.5; cursor: default; }
.mail-send-when { order: 10; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; width: 100%; padding: 6px 8px; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface-2); }
.mail-send-when .mail-compose-field { width: auto; flex: 0 1 220px; }
.mail-send-when-label { font-family: var(--ui); font-size: 12.5px; color: var(--ink-2); }
/* K03: the options as a row that can be seen - importance, the two receipts. */
.mail-options-row { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 10px; font-family: var(--ui); font-size: 12px; color: var(--ink-2); }
.mail-options-row .work-compose-select { padding: 2px 4px; font-size: 12px; }
.mail-compose.is-full { flex: 1 1 auto; min-height: 0; }
.mail-reader:has(.mail-compose) .mail-compose.is-full { flex: 1 1 auto; min-height: 0; }
.mail-compose.is-full .mail-compose-rte { flex: 1 1 auto; min-height: 240px; resize: none; }
.mail-reader-folded { display: flex; align-items: center; gap: 8px; padding: 6px 12px; border-top: 1px solid var(--line-soft); background: var(--paper); font-family: var(--ui); font-size: 12.5px; color: var(--ink-2); }
.mail-reader-folded-text { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Real formatting (item 5): bold/italic/underline/lists/link, as a dense strip of mark
   buttons above the field it acts on — the same one-letter-mark convention this surface
   has nowhere else, chosen because six spelled-out labels would not fit a composer this
   width without wrapping. */
.mail-compose-toolbar { display: flex; align-items: center; gap: 3px; }
.mail-compose-fmt {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 26px;
  height: 24px;
  padding: 0 6px;
  border: 1px solid var(--line);
  border-radius: var(--r-xs);
  background: var(--surface-2);
  color: var(--ink-2);
  font-family: var(--ui);
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
}
.mail-compose-fmt:hover { border-color: var(--accent); color: var(--accent); }
.mail-compose-fmt:disabled { opacity: 0.5; cursor: default; }
/* Real attachments (item 5): a file picker and the files staged for this message, each
   removable before it sends. */
/* Item 1.11: the standalone attach block is gone — the picker input is visually hidden
   (the paperclip in the compose action row clicks it) and only the chip list remains. */
.mail-attach-input { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.mail-attach-list { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.mail-attach-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 6px 3px 8px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface-2);
  color: var(--ink-2);
  font-family: var(--mono);
  font-size: 11px;
}
.mail-attach-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--mute);
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
}
.mail-attach-remove:hover { color: var(--accent); }
/* B5 (SB 2026-09-08, Outlook's reply): an attachment is a card, not a mono pill — a file
   icon, the name, its size, and a remove x, sat right under the recipients where it is
   easy to see. */
.mail-attach-card {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  max-width: 260px;
  padding: 5px 8px 5px 7px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-2);
  color: var(--ink);
  font-family: var(--ui);
  font-size: 12px;
}
.mail-attach-card-icon { flex: 0 0 auto; color: var(--accent); }
.mail-attach-card-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.mail-attach-card-size { flex: 0 0 auto; color: var(--mute); font-family: var(--mono); font-size: 11px; }
/* B5: the original message, folded away under the reply and read-only. The toggle is a
   quiet word; the body, when opened, is walled off from the editor so it cannot be typed
   into or deleted. */
/* B5: the "Aa" that reveals the formatting mark buttons, hidden until asked, like Outlook. */
.mail-fmt-toggle {
  display: inline-flex;
  align-items: baseline;
  height: 26px;
  padding: 0 8px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-2);
  color: var(--ink-2);
  font-family: var(--ui);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.02em;
  line-height: 1;
  cursor: pointer;
}
.mail-fmt-toggle:hover { border-color: var(--accent); color: var(--accent); }
.mail-fmt-toggle.is-on { border-color: var(--accent); color: var(--accent); background: var(--surface); }
.mail-compose-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; }
/* The compose window's buttons are pills (Ben, 2026-10-21). Three rules compete for their
   corners and this must win all of them: the letterpress theme squares .btn, the app-wide
   YUM-1491 rule gives secondary buttons 6px (:root .btn:not(.solid):not(.is-primary)), and
   the row's own pill would round the split Send on both sides. The doubled classes are the
   weight; the split seam comes last so it keeps its flat edge against its caret. */
:root .mail-compose-actions .btn.btn.btn,
:root .work-compose-ask .btn.btn.btn {
  border-radius: var(--r-pill);
  padding: 0 18px;
}
:root .mail-compose-actions .mail-send-split .btn.btn.btn { border-radius: var(--r-pill) 0 0 var(--r-pill); }
/* G04 (SB 2026-09-04, Outlook's reply): Discard sits at the far end of the Send row; the
   subject is a line of text between the recipients and the editor; the To / Cc / Bcc
   labels are small boxed words, not mono captions. */
.mail-compose-actions-end { margin-left: auto; display: flex; align-items: center; gap: 6px; }
.mail-compose-subject { padding: 4px 2px; border-bottom: 1px solid var(--line-soft); font-family: var(--ui); font-size: 13px; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mail-compose .work-compose-label {
  flex: 0 0 auto;
  min-width: 40px;
  padding: 3px 8px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
  font-family: var(--ui);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  text-align: center;
  color: var(--ink-2);
}
.mail-compose .mail-compose-toolbar-wrap { flex-wrap: wrap; justify-content: flex-start; gap: 6px; }
.mail-compose-rte .yb-quote { margin-top: 10px; padding-top: 0; border-top: 0; color: var(--ink); font-size: 13px; }
.mail-compose-rte .yb-quote hr { border: 0; border-top: 1px solid var(--line); margin: 6px 0 10px; }
.mail-compose-rte .yb-quote b { font-weight: 600; }
.mail-compose-status { font-size: 12px; color: var(--mute); }
.mail-compose-status.is-error { color: var(--accent); }
.mail-outgoing {
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  border-radius: var(--r-sm);
  background: var(--accent-soft);
  padding: 8px 10px;
}
.mail-outgoing-label { font-family: var(--mono); font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--accent); }
.mail-outgoing-text {
  margin: 4px 0 0;
  font-family: var(--ui);
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink);
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 150px;
  overflow-y: auto;
}

.mail-connect {
  display: flex;
  /* the connect prompt IS the surface column while nothing is connected: without a grow
     factor it sized to its own text inside .work-frame's flex row, leaving a dead band
     between it and the chat column (seen on the XP lane 2026-08-22, present in every
     theme — the rule never had one). */
  flex: 1;
  min-width: 0;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  padding: 22px;
}
.mail-connect-text { font-size: 13.5px; line-height: 1.55; color: var(--ink-2); max-width: 52ch; }

@media (max-width: 900px) {
  .mail-shell { grid-template-columns: minmax(0, 1fr); }
  /* One column has no edge between two columns to drag. */
  .work-rail-grip, .work-directory-grip, .work-list-grip { display: none; }
  .mail-rail { flex-direction: row; overflow-x: auto; border-right: 0; border-bottom: 1px solid var(--line-soft); padding: 6px; }
  .mail-rail-label { display: none; }
  .work-lang { position: static; border-bottom: 0; margin-bottom: 0; padding: 0 4px 0 0; background: transparent; }
  .work-lang-menu { top: 46px; }
  .mail-folder { width: auto; white-space: nowrap; }
  .mail-list-pane { border-right: 0; }
  .mail-pane-hidden { display: none; }
  .mail-back-bar { display: flex; align-items: center; gap: 6px; padding: 7px 12px; border-bottom: 1px solid var(--line-soft); background: var(--surface); }
  /* One column, so the panel cannot take its room from anything — it takes the whole
     column instead, the way every other pane does at this width. */
  .mail-shell, .mail-shell.work-assist-open { grid-template-columns: minmax(0, 1fr); }
  .mail-shell:not(.work-assist-open) .work-assist { display: none; }
  .mail-shell.work-assist-open > :not(.work-assist):not(.work-assist-tab) { display: none; }
  .work-assist .yb-edge-panel { width: auto; flex: 1; border-left: 0; }

}

@media (prefers-reduced-motion: reduce) {
  .mail-shell { transition: none; }
}

/* ---------------------------------------------------------------- Work (YUM-606)
   The nav stop that was Office. Five sources — mail, calendar, Teams channels, Teams
   messages, files — share the .mail-shell three-column frame and its list/reader
   components; what is added here is what the other four need and mail did not: a source
   rail, a day heading, a paging row, a breadcrumb trail, the verdict pills the assessment
   writes, and a thread of posts. Same tokens, same density as the mail family above. */
.work-sources { display: flex; flex-direction: column; gap: 2px; }
.work-source {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 4px 8px;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--work-rail-ink);
  font-family: var(--ui);
  font-size: 13px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
.work-source:hover { background: var(--work-rail-hover); }
.work-source.active { background: var(--work-rail-active-bg); color: var(--work-rail-active-ink); box-shadow: inset 3px 0 0 var(--work-rail-active-bar, transparent); }
/* A section that has its own selected place, folder or view under it steps back to a
   quiet mark, so only one row on the rail carries the tint at a time. */
.work-source.active:has(~ .mail-nav-folders-tree),
.mail-rail:has(.mail-folder.active, .ds-tree-node.active, .todo-rail-item.is-active) .work-source.active { background: transparent; color: var(--work-rail-active-ink); box-shadow: inset 3px 0 0 var(--work-rail-line); }
/* The rail reflows when a section opens or closes its sub-list, which can slide another
   row under a pointer that has not moved. Hover only tints once the pointer moves again. */
.work-sources[data-hover-hold] .work-source:not(.active):hover { background: transparent; }
/* YUM-1446 (T6b/T6c): one rail row size for sections, places, folders and trees, so the
   five sections read as primary navigation and the groups under them as secondary. */
.work-sources { padding-bottom: 6px; margin-bottom: 2px; border-bottom: 1px solid var(--work-rail-line); }
.work-source, .mail-folder { min-height: 30px; padding: 5px 10px; }
.work-source { font-size: 13.5px; }
.work-rail-group { display: flex; flex-direction: column; gap: 1px; }
.work-rail-group-head {
  display: flex; align-items: center; gap: 6px; width: 100%;
  border: 0; border-radius: var(--r-sm); background: transparent; text-align: left; cursor: pointer;
}
.work-rail-group-head:hover { color: var(--work-rail-ink); background: var(--work-rail-hover); }
.work-rail-group-chevron { flex: none; transition: transform 120ms ease; }
.work-rail-group-head[aria-expanded='true'] .work-rail-group-chevron { transform: rotate(90deg); }
.work-source:disabled { opacity: 0.55; cursor: not-allowed; }
.work-source-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.work-source .work-source-name { display: none; }

/* WS-03 — a permission Microsoft has not granted, said on the surface that needs it.
   It sits at the top of the list it is blocking rather than taking over the screen, because
   the rest of the source still works: the teams and their channels are there, only the
   conversation inside one is not. `.is-admin` is the case the person reading cannot fix at
   all — the mark moves to the accent so it reads as "somebody else has to act", and the
   block never offers them a reconnect that could not grant it. */
.work-permission {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 8px 10px;
  padding: 9px 11px;
  border: 1px solid var(--line);
  border-left: 2px solid var(--mute);
  border-radius: var(--r-sm);
  background: var(--surface-2);
}
.work-permission.is-admin {
  border-left-color: var(--accent);
  background: var(--accent-soft);
}
.work-permission-text {
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-2);
  max-width: 62ch;
}
/* The permission names themselves, exactly as Microsoft spells them, so an administrator
   can search for them in Entra without translating anything back. */
.work-permission-scopes {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--work-dim);
  word-break: break-word;
}
.work-permission-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}
.work-permission-link {
  font-family: var(--ui);
  font-size: 12px;
  font-weight: 600;
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.work-permission-result {
  font-size: 11.5px;
  color: var(--mute);
}

/* One heading per day in the calendar list, sticky so the day a meeting belongs to is
   still on screen when its row is. */
.work-day {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: 6px 10px 4px;
  border-bottom: 1px solid var(--line-soft);
  background: var(--surface-2);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--work-dim);
}

/* "Load 50 more" and the count beside it — a list says where it has got to rather than
   ending silently at the first page (YUM-610). */
/* A long chat opens at its latest messages; this reaches the earlier ones (2026-10-13). */
.chat-earlier { align-self: center; margin: 4px auto 12px; }
.work-more {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border-bottom: 1px solid var(--line-soft);
}
.work-more-count { font-family: var(--mono); font-size: 11px; color: var(--work-dim); }

.work-crumbs {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px;
  padding: 6px 10px;
  border-bottom: 1px solid var(--line-soft);
  background: var(--surface-2);
}

/* The strip above a body: the verdict, the one-line summary, and the things that can be
   done about it (reply proposals, accept/decline, open in the editor). */
.work-advice {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  padding: 8px 12px;
  border-bottom: 1px solid var(--line-soft);
  background: var(--surface-2);
}
.work-advice-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; }
.work-advice-summary { flex: 1 1 260px; min-width: 0; font-family: var(--ui); font-size: 12.5px; color: var(--ink-2); }
.work-advice-status { font-family: var(--ui); font-size: 12px; color: var(--mute); }
.work-advice-status.is-error { color: var(--danger, var(--accent)); }
.work-advice-link { font-family: var(--ui); font-size: 12.5px; color: var(--accent); }
/* C4 (2026-08-26): the Files pane translate control — an icon button beside a target-
   language dropdown, replacing the old text button that named a fixed language. */
.work-translate-control { display: flex; align-items: center; gap: 6px; }
.work-translate-control .mail-lang-select { max-width: 150px; }
/* C5: the job progress panel sits where the old "state — N of M" text line did. */
.work-progress-panel { padding: 8px 12px; border-bottom: 1px solid var(--line-soft); background: var(--surface-2); }

/* A Teams thread: the opening post and its replies, each one its own block so a reply
   cannot be mistaken for part of the post above it. */
.work-thread { display: flex; flex-direction: column; gap: 10px; padding: 12px; min-height: 0; overflow-y: auto; }
.work-post { border: 1px solid var(--line-soft); border-radius: var(--r-sm); background: var(--surface); }
.work-post-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
  padding: 6px 10px;
  border-bottom: 1px solid var(--line-soft);
}
.work-post-from { flex: 1; min-width: 0; font-family: var(--ui); font-size: 12.5px; color: var(--ink); }
.work-post-time { font-family: var(--mono); font-size: 11px; color: var(--work-dim); }
.work-post-flag {
  padding: 3px 10px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent);
}

/* The assessment, on the row itself: what this message is, in one line, and whose move
   it is (YUM-607, YUM-608). "Waiting on" is what a message the reader SENT is — it is
   never their own action. */
.mail-item-summary {
  margin-top: 2px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 11.5px;
  color: var(--ink-2);
}
.mail-flag.is-action { border-color: color-mix(in srgb, var(--accent) 45%, transparent); color: var(--accent); background: var(--accent-soft); }
.mail-flag.is-fyi { color: var(--mute); }
.mail-flag.is-waiting { border-style: dashed; color: var(--ink-2); }
.mail-flag.is-unknown { opacity: 0.6; }
.mail-item.is-cancelled .mail-item-subject { text-decoration: line-through; color: var(--mute); }

/* --------------------------------------------------- Work: New message, and the rail's ends
   The rail's first slot is where you start something, so it holds the button that starts
   one. The language you read in moved to the foot of the same column: it is a setting you
   set once, and it had the most valuable row on the screen for no reason (SB, 2026-08-21). */
.work-new {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  width: 100%;
  margin-bottom: 6px;
  padding: 6px;
  border: 1px solid var(--accent);
  border-radius: var(--r-pill);
  background: var(--accent);
  color: var(--on-accent);
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  transition: filter .12s ease;
}
.work-new:hover { filter: brightness(1.06); }
.work-new:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* The rail turns into a horizontal strip on a phone; the button should not stretch across
   it, and its label is the first thing to go when there is no room. */
.work-new-label { white-space: nowrap; }
/* Pushed to the foot of the rail's column, under whatever each source put there. */
.mail-rail > .mail-lang-row { margin-top: auto; }

.work-compose { display: flex; flex-direction: column; gap: 9px; }
/* The reply box sits under an open mail and is a few lines by design. A new message is the
   whole of what you are writing, so it gets room to be written in. */
.work-compose .mail-compose-text { min-height: 200px; }
.work-compose-row { display: flex; align-items: center; gap: 10px; }
.work-compose-label {
  flex: 0 0 58px;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--mute);
}
/* min-width:0 so a long recipient list shrinks the field instead of widening the card. */
.work-compose-row .mail-compose-field, .work-compose-row .mail-lang-select { flex: 1 1 auto; min-width: 0; }
/* G03 (SB 2026-09-04): the small Cc / Bcc words at the end of a recipient row (Outlook). */
.work-compose-links { display: flex; align-items: center; gap: 2px; flex: 0 0 auto; }
.work-compose-link {
  padding: 2px 6px;
  border: 0;
  background: transparent;
  color: var(--mute);
  font-family: var(--ui);
  font-size: 12.5px;
  cursor: pointer;
}
.work-compose-link:hover, .work-compose-link:focus-visible { color: var(--accent); outline: none; }
.work-compose-files { display: flex; flex-wrap: wrap; gap: 6px; }
.work-compose-file {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 6px 4px 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface-2);
  color: var(--ink-2);
  font-size: 11.5px;
}
/* Attaching the wrong file is the most likely mistake in this window, so taking one back
   is one press and not a re-open. */
.work-compose-file-drop {
  width: 20px;
  height: 20px;
  border: 0;
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--mute);
  font: inherit;
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
}
.work-compose-file-drop:hover { background: var(--surface); color: var(--ink); }
.work-compose-ask { display: flex; align-items: center; gap: 8px; }
/* Pushed to the far end of the row, away from the buttons that send. This one changes the
   message; those post it. */
.work-compose-translate { display: flex; align-items: center; gap: 6px; margin-left: auto; }
.work-compose-translate .mail-lang-select { max-width: 150px; }
.work-compose-ask .mail-compose-field { flex: 1 1 auto; min-width: 0; }
/* The real <input type=file> is never the control anyone sees -- browsers each draw their
   own, none of them match the design system, and none can be made to. */
.work-compose-fileinput { display: none; }

/* The recipient suggestions. The row is the positioning context, so the list hangs off the
   field itself rather than off the card and cannot drift when the card scrolls. */
.work-people-wrap { position: relative; flex: 1 1 auto; min-width: 0; }
.work-people {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  z-index: 20;
  margin: 0;
  padding: 4px;
  list-style: none;
  max-height: 232px;
  overflow-y: auto;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
  box-shadow: var(--shadow);
}

/* ---------------------------------------------------------------- Work: calendar, files,
   conversation filters (Ben, 2026-08-19 — the annotated pass over the first Work build).
   The third column of the Work shell can now hold two things at once: a surface (a drawn
   calendar, a file preview) with the thing that was clicked underneath it. */
.work-split { display: flex; flex-direction: column; min-height: 0; min-width: 0; }
.work-split-detail {
  flex: 1 1 45%;
  min-height: 0;
  overflow-y: auto;
  border-top: 1px solid var(--line-soft);
}

/* The calendar drawn as a calendar: one column per day, meetings in time order inside it.
   Deliberately not pixel-per-minute — a half-hour rendered as a sliver is not readable, and
   this surface is for reading a week and clicking the meeting. */
.work-cal { display: flex; flex: 1 1 55%; min-height: 0; overflow: auto; background: var(--surface); }
.work-cal-week { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); }
.work-cal-single { display: block; }
.work-cal-month { flex-direction: column; }
.work-cal-week-row { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); border-bottom: 1px solid var(--line-soft); }
.work-cal-col { display: flex; flex-direction: column; min-width: 0; border-right: 1px solid var(--line-soft); }
.work-cal-col.is-today { background: var(--accent-soft); }
.work-cal-col-head {
  display: flex;
  align-items: baseline;
  gap: 4px;
  padding: 4px 6px;
  border-bottom: 1px solid var(--line-soft);
  position: sticky;
  top: 0;
  background: var(--surface-2);
}
.work-cal-col-day { font-family: var(--mono); font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--work-dim); }
.work-cal-col-date { font-family: var(--ui); font-size: 12px; color: var(--ink-2); }
.work-cal-col-body { display: flex; flex-direction: column; gap: 2px; padding: 4px; min-height: 56px; }
.work-cal-col-empty { font-family: var(--mono); font-size: 11px; color: var(--work-dim); padding: 2px 4px; }
.work-cal-chip {
  display: flex;
  flex-direction: column;
  gap: 1px;
  width: 100%;
  padding: 3px 5px;
  border: 1px solid var(--line-soft);
  border-left: 2px solid var(--line);
  border-radius: var(--r-xs);
  background: var(--surface);
  text-align: left;
  cursor: pointer;
}
.work-cal-chip:hover { background: var(--surface-2); }
.work-cal-chip.active { border-color: var(--accent); background: var(--accent-soft); }
.work-cal-chip.is-mine { border-left-color: var(--ink-2); }
.work-cal-chip.needs-answer { border-left-color: var(--accent); }
.work-cal-chip.is-cancelled { opacity: 0.5; border-left-color: var(--line); background: transparent; }
.work-cal-chip.is-cancelled .work-cal-chip-title { text-decoration: line-through; color: var(--mute); }
.work-cal-chip-time { font-family: var(--mono); font-size: 11px; color: var(--work-dim); }
.work-cal-chip-title { font-family: var(--ui); font-size: 11.5px; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Filters read off the conversations that actually loaded, not a fixed taxonomy. */
.work-filters { display: flex; flex-wrap: wrap; gap: 4px; padding: 2px 8px 6px; }
.work-pill {
  padding: 2px 8px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--ink-2);
  font-family: var(--ui);
  font-size: 11.5px;
  cursor: pointer;
}
.work-pill:hover { background: var(--surface-2); }
.work-pill.active { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }

/* What has been shared in a conversation, above the conversation itself. */
.work-files-strip { display: flex; align-items: center; flex-wrap: wrap; gap: 5px; padding: 6px 12px; border-bottom: 1px solid var(--line-soft); background: var(--surface-2); }
.work-files-strip-label { font-family: var(--mono); font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--work-dim); }
.work-file-chip {
  padding: 2px 8px;
  border: 1px solid var(--line-soft);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--ink-2);
  font-family: var(--ui);
  font-size: 11.5px;
  text-decoration: none;
}
.work-file-chip:hover { border-color: var(--accent); color: var(--accent); }

/* The file itself, beside its folder: previewed where it can be, edited where it can be. */
/* P28.2: "expand the actual file and waste less space" — less gutter around the document
   itself so it fills more of the pane. */
.work-preview { display: flex; flex: 1; min-height: 0; padding: 4px 6px 6px; }
.work-preview-frame { flex: 1; width: 100%; min-height: 320px; border: 1px solid var(--line-soft); border-radius: var(--r-sm); background: var(--surface); }
/* C6 (2026-08-26): the Files-pane pdf/text preview, rendered through the same pdf.js pages
   (.docpv-stack/.docpv-page, canvas.css) the attachment viewer popup uses instead of a bare
   iframe — same fit-to-width zoom, no native viewer chrome, embedded inline (no modal card)
   so it sits in this pane's own layout rather than opening over it. */
.work-preview-doc { flex: 1; width: 100%; min-width: 0; min-height: 320px; overflow: auto; border: 1px solid var(--line-soft); border-radius: var(--r-sm); background: var(--bg); padding: 14px 10px 22px; }
/* YUM-1398 side-by-side viewer: carried from 1.0.3, which test no longer serves. */
.work-preview-split { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; flex: 1; min-height: 0; width: 100%; }
.work-preview-split > .work-preview-doc { max-height: 100%; }
.work-preview-toggle { display: flex; justify-content: flex-end; padding: 6px 12px 0; }
@media (max-width: 720px) { .work-preview-split { grid-template-columns: 1fr; } }
.work-preview-image { max-width: 100%; max-height: 100%; object-fit: contain; border: 1px solid var(--line-soft); border-radius: var(--r-sm); }
/* YUM-1398 O6 (ported from 1.0.3 for the served 1.0.4): original left, translation right,
   each its own scroll box (scrolled together in file-manager.js SideBySide). On a phone the
   two stack. */
.work-preview-split { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; flex: 1; min-height: 0; width: 100%; }
.work-preview-split > .work-preview-doc { max-height: 100%; }
@media (max-width: 720px) { .work-preview-split { grid-template-columns: 1fr; } }

/* A Teams post that carries only a file, a reaction or a system notice: one line, not an
   empty frame the height of a paragraph (Ben, 2026-08-19). */
.work-post-empty { padding: 8px 10px; font-family: var(--ui); font-size: 12px; color: var(--mute); }
/* The file preview hands its whole area to whichever engine renders that kind. */
.work-preview .canvas-onlyoffice-host { flex: 1; min-height: 360px; width: 100%; }
/* The file picker behind "Attach a file" — the button is the control, the input never shows. */
.work-file-input { display: none; }
/* A meeting called "Group IT Roadmap Presentation — Apps" is not named by "Group IT Roa…":
   a chip title wraps to two lines before it truncates. */
.work-cal-chip-title { white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
/* A message frame sized to what is in it, so a one-line reply is one line. The frame is a
   flex child of the post, so the height goes on the frame AND the growth is switched off. */
.mail-body-frame.is-short { flex: none; height: 62px; padding: 6px 10px; }
.mail-body-frame.is-medium { flex: none; height: 150px; padding: 8px 10px; }

/* ---------------------------------------------------------------- directory tree (WS-01, WS-02)
   One component for every hierarchy the Work surface shows: the mailbox drawn as its own
   folders, and Teams drawn as its teams and their channels. One line per node — a name, an
   optional count, and a disclosure control only where there is something to disclose. Depth
   is drawn by the nesting itself and a guide line down the left of each level, so a tree of
   any depth needs no per-level class and no value handed across from app code. */
.ds-tree { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.ds-tree-branch { display: flex; flex-direction: column; min-width: 0; }
.ds-tree-row { display: flex; align-items: center; gap: 1px; min-width: 0; }
.ds-tree-toggle {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 22px;
  padding: 0;
  border: 0;
  border-radius: var(--r-xs);
  background: transparent;
  color: var(--work-rail-mute);
  cursor: pointer;
}
.ds-tree-toggle:hover { color: var(--work-rail-ink); }
/* A node with nothing under it keeps the column so every name on the level starts at the
   same x — it is a spacer, not a control, so it takes no hover and no click. */
.ds-tree-toggle.is-leaf { cursor: default; pointer-events: none; }
.ds-tree-chevron { transition: transform 120ms ease; }
.ds-tree-toggle[aria-expanded='true'] .ds-tree-chevron { transform: rotate(90deg); }
.ds-tree-node {
  display: flex;
  flex: 1;
  align-items: center;
  gap: 7px;
  min-width: 0;
  padding: 4px 7px;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--work-rail-ink);
  font-family: var(--ui);
  font-size: 12.5px;
  line-height: 1.25;
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
}
.ds-tree-node svg { flex: none; }
.ds-tree-node:hover { background: var(--work-rail-hover); }
.ds-tree-node.active { background: var(--work-rail-active-bg); color: var(--work-rail-active-ink); box-shadow: inset 3px 0 0 var(--work-rail-active-bar, transparent); font-weight: 600; }
/* A level that groups other levels and holds nothing of its own: a heading inside the
   tree, not a destination. */
.ds-tree-node:disabled { color: var(--work-rail-label); cursor: default; }
.ds-tree-node:disabled:hover { background: transparent; }
.ds-tree-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.ds-tree-count { font-family: var(--mono); font-size: 11px; color: var(--work-rail-mute); }
.ds-tree-node.active .ds-tree-count { color: var(--work-rail-active-ink); }
.ds-tree-children { margin-left: 8px; padding-left: 7px; border-left: 1px solid var(--work-rail-line); }
.ds-tree-empty { padding: 4px 8px; font-family: var(--ui); font-size: 12px; color: var(--work-rail-label); }
.ds-tree-empty.is-error { color: var(--accent); }
/* At phone width the rail is a horizontal band; a tree cannot be read sideways, so a rail
   that carries one stays a column and takes a share of the height instead. */
@media (max-width: 900px) {
  .mail-rail.has-tree { flex-direction: column; max-height: 34vh; overflow-y: auto; }
  .mail-rail.has-tree .mail-rail-label { display: block; }
}

/* ---------------------------------------------------------------- the file tree (CR-9, WS-05)
   The file browser's main column, redrawn as a real tree: a folder is a row with a
   disclosure control, and everything under it — sub-folders and files together — is drawn
   nested beneath it rather than replacing it. Same row height and density as the flat
   .work-browser it replaces; a file row answers the same three questions (kind, size,
   modified) a table column used to, on the row itself. Depth is DOM nesting, never a number:
   each folder's open children sit inside one more .file-tree-indent, exactly the way
   .ds-tree-children has always drawn the mailbox and Teams trees. */
.file-tree {
  flex: 1;
  min-height: 0;
  min-width: 0;
  overflow-x: hidden;
  overflow-y: auto;
  padding: 2px 0;
}
.file-tree-branch { display: flex; flex-direction: column; min-width: 0; }
/* A folder: the disclosure chevron, the folder's own icon, its name, and how much is in it —
   one row, the whole row clickable, same convention as .work-browser-row. */
.file-tree-folder {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  height: 26px;
  padding: 0 10px 0 6px;
  border: 0;
  border-bottom: 1px solid var(--line-soft);
  background: transparent;
  color: var(--ink-2);
  font-family: var(--ui);
  font-size: 12.5px;
  line-height: 1;
  text-align: left;
  cursor: pointer;
}
.file-tree-folder:hover { background: var(--surface-2); }
.file-tree-chevron { flex: none; color: var(--work-rail-mute); transition: transform 120ms ease; }
.file-tree-folder[aria-expanded='true'] .file-tree-chevron { transform: rotate(90deg); }
.file-tree-folder .file-tree-icon { flex: none; color: var(--work-dim); }
.file-tree-folder.is-open .file-tree-icon { color: var(--accent); }
/* One level of nesting: the folder's own rows (sub-folders and files) indented in from it
   and guided by one rule down the left — the .ds-tree-children precedent, applied here. */
.file-tree-indent {
  margin-left: 10px;
  padding-left: 8px;
  border-left: 1px solid var(--work-rail-line);
}
/* A file row: same height as a folder row, one level further in by DOM nesting alone, its
   kind icon, name, and fixed-width size/modified — each a single run of text that ellipses
   rather than wraps, so a long folder stays a column of equal-height rows. */
.file-tree-row {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  height: 26px;
  padding: 0 10px;
  border: 0;
  border-bottom: 1px solid var(--line-soft);
  border-left: 2px solid transparent;
  background: transparent;
  color: var(--ink-2);
  font-family: var(--ui);
  font-size: 12.5px;
  line-height: 1;
  text-align: left;
  cursor: pointer;
}
.file-tree-row:hover { background: var(--surface-2); }
.file-tree-row.active { background: var(--accent-soft); border-left-color: var(--accent); color: var(--accent); }
.file-tree-icon { flex: none; }
.file-tree-name { flex: 1 1 auto; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.file-tree-count { flex: none; font-family: var(--mono); font-size: 11px; color: var(--work-rail-mute); }
.file-tree-meta { flex: none; font-family: var(--mono); font-size: 11px; color: var(--work-dim); white-space: nowrap; text-align: right; }
.file-tree-row.active .file-tree-meta { color: var(--accent); }
.file-tree-size { width: 58px; }
.file-tree-date { width: 66px; }
.file-tree-status { padding: 6px 10px; font-family: var(--ui); font-size: 12px; color: var(--work-rail-label); }
.file-tree-status.is-error { color: var(--accent); }
.file-tree-more { padding: 4px 10px 6px; }

/* The second column, for a source whose directory is too big to live in the rail: Teams
   keeps its teams in the rail and the selected team's channels here, wider, same density.
   The shell only widens to four columns when a source actually has one. */
.work-directory {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-height: 0;
  min-width: 0;
  overflow-y: auto;
  padding: 10px 8px;
  border-right: 1px solid var(--work-rail-line);
  background: var(--work-rail-bg);
}
.mail-shell.has-directory { grid-template-columns: var(--work-rail-w, 190px) var(--work-directory-w, 250px) 320px minmax(0, 1fr); }
/* The directory view has its own track list, so the docked Yarnball panel needs its fifth
   track stated here too -- without this the panel opens onto a grid with nowhere to put it
   and the reading pane keeps the room. Same widths, one more column. */
.mail-shell.has-directory.work-assist-open { grid-template-columns: var(--work-rail-w, 190px) var(--work-directory-w, 250px) 320px minmax(0, 1fr) 296px; }
/* The two left columns keep whatever width their reader gave them at every desk size; it is
   the thread list that gives ground first when the window is narrow. */
@media (max-width: 1200px) {
  .mail-shell.has-directory { grid-template-columns: var(--work-rail-w, 190px) var(--work-directory-w, 250px) 280px minmax(0, 1fr); }
}
@media (max-width: 900px) {
  .mail-shell.has-directory { grid-template-columns: minmax(0, 1fr); }
  .work-directory { max-height: 30vh; border-right: 0; border-bottom: 1px solid var(--line-soft); padding: 6px; }
}

/* ---------------------------------------------------------------- the file browser's own bar (WS-04)
   One thin line of working controls above a list of files: what is being kept out of the
   way, and how much of it there is. Nothing here is decoration — every control on the line
   changes what the list below shows. */
.work-browser-bar {
  display: flex;
  align-items: center;
  gap: 5px;
  min-height: 0;
  padding: 4px 10px;
  border-bottom: 1px solid var(--line-soft);
  background: var(--surface);
}
/* Discreet by design: off, it is a quiet outline that says how many entries it is holding
   back; on, it wears the accent so the extra rows in the list are explained. */
.work-browser-toggle {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 2px 7px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--work-dim);
  font-family: var(--ui);
  font-size: 11px;
  line-height: 1.4;
  white-space: nowrap;
  cursor: pointer;
}
.work-browser-toggle:hover { color: var(--ink-2); background: var(--surface-2); }
.work-browser-toggle.active { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.work-browser-toggle-name { overflow: hidden; text-overflow: ellipsis; }
.work-browser-toggle-count { font-family: var(--mono); font-size: 11px; color: var(--mute); }
.work-browser-toggle.active .work-browser-toggle-count { color: var(--accent); }

/* ---------------------------------------------------------------- the file browser (WS-05)
   A list of files is a list of lines. One item, one line, always — a row is a fixed height
   and every cell in it is a single run of text that ends in an ellipsis rather than wrapping
   onto a second line, so a hundred files read as a hundred rows and not as a hundred
   paragraphs. The columns are named by what they hold; their widths live here and the app
   only ever names them. */
.work-browser {
  flex: 1;
  min-height: 0;
  min-width: 0;
  overflow-x: hidden;
  overflow-y: auto;
}
.work-browser-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 10px;
  height: 22px;
  border-bottom: 1px solid var(--line-soft);
  background: var(--surface);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--work-dim);
}
.work-browser-row {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  height: 26px;
  padding: 0 10px;
  border: 0;
  border-bottom: 1px solid var(--line-soft);
  border-left: 2px solid transparent;
  background: transparent;
  color: var(--ink-2);
  font-family: var(--ui);
  font-size: 12.5px;
  line-height: 1;
  text-align: left;
  cursor: pointer;
}
.work-browser-row:hover { background: var(--surface-2); }
.work-browser-row.active { background: var(--accent-soft); border-left-color: var(--accent); color: var(--accent); }
/* The cell: never wraps, never grows the row. */
.work-browser-cell {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: none;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.work-browser-cell svg { flex: none; }
/* Each column is as wide as what it holds needs, and the name takes whatever is left. */
.work-browser-cell.is-name { flex: 1 1 auto; min-width: 0; }
.work-browser-cell.is-modified { width: 66px; font-family: var(--mono); font-size: 11px; color: var(--work-dim); }
.work-browser-cell.is-size { width: 58px; justify-content: flex-end; font-family: var(--mono); font-size: 11px; color: var(--work-dim); }
.work-browser-cell.is-kind { width: 46px; font-family: var(--mono); font-size: 11px; color: var(--work-dim); }
.work-browser-cell.is-modifiedBy { width: 92px; font-size: 11.5px; color: var(--mute); }
.work-browser-head .work-browser-cell { color: inherit; font: inherit; letter-spacing: inherit; }
.work-browser-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---------------------------------------------------------------- sorting, filtering, columns (WS-06)
   The column labels double as the sort control, a filter line sits directly under them so
   what is typed lines up with the cells it narrows, and both stay at the top of the list
   while it scrolls. Which columns exist, in what order, is the reader's own choice, made in
   a small menu on the browser's bar. */
.work-browser-sticky {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--surface);
}
.work-browser-sort {
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  text-align: left;
  cursor: pointer;
}
.work-browser-sort:hover { color: var(--ink-2); }
.work-browser-sort.active { color: var(--accent); }
.work-browser-sort-name { overflow: hidden; text-overflow: ellipsis; }
/* The mark points the way the list is going: up for ascending, down for descending. */
.work-browser-sort-mark { flex: none; transform: rotate(-90deg); }
.work-browser-sort-mark.is-desc { transform: rotate(90deg); }

.work-browser-filters {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 3px 10px;
  border-bottom: 1px solid var(--line-soft);
  background: var(--surface-2);
}
/* Scoped through the row so the input beats the column's own type and colour: a field a
   person types into is never drawn in the muted tone its column reads in. */
.work-browser-filters .work-browser-filter {
  display: block;
  min-width: 0;
  padding: 1px 5px;
  border: 1px solid var(--line);
  border-radius: var(--r-xs);
  background: var(--surface);
  color: var(--ink);
  font-family: var(--ui);
  font-size: 11px;
  line-height: 1.5;
}
.work-browser-filters .work-browser-filter:focus { outline: none; border-color: var(--accent); }

/* The columns menu: the chosen columns in their order, each able to move or go, and the
   ones not chosen offered underneath. */
.work-browser-menu { position: relative; display: flex; }
.work-browser-columns {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  z-index: 20;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 168px;
  padding: 6px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
  box-shadow: var(--shadow);
}
.work-person {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 6px 9px;
  border-radius: var(--r-xs);
  cursor: pointer;
  font-size: 12.5px;
}
/* One highlight, driven by both the keyboard and the pointer, so arrowing down and then
   moving the mouse does not leave two rows looking chosen. */
.work-person.is-on { background: var(--accent-soft); }
.work-person-name { color: var(--ink); font-weight: 600; white-space: nowrap; }
.work-person-addr { color: var(--mute); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Which of the two lists a name is in, said in the same voice as the rail's own labels. */
.work-people-head {
  padding: 7px 9px 3px;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--faint);
}
.work-people-head + .work-person { margin-top: 0; }

/* ------------------------------------------------------- Work: formatting the message
   The left half of a mail ribbon: what a message is written with, rather than what a
   document is laid out with. One row, wrapping, above the box it acts on. */
.work-rte { display: flex; flex-direction: column; min-width: 0; }
.work-rte-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 3px;
  padding: 5px 6px;
  border: 1px solid var(--line);
  border-bottom: 0;
  border-radius: var(--r-sm) var(--r-sm) 0 0;
  background: var(--surface-2);
}
.work-rte-btn {
  min-width: 26px;
  height: 26px;
  padding: 0 7px;
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--ink-2);
  font: inherit;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
}
.work-rte-btn:hover:enabled { background: var(--surface); border-color: var(--line); }
.work-rte-btn:disabled { opacity: .4; cursor: default; }
/* On, and unmistakably so: the same fill the app already uses for an active tab. A control
   that looks identical whether it is on or off makes you read the text to find out. */
.work-rte-btn.is-on {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}
.work-rte-btn.is-on:hover:enabled { background: var(--accent); border-color: var(--accent); }
/* Each button shows what it does, which is cheaper to read than any icon of it. */
.work-rte-btn.is-bold { font-weight: 800; }
.work-rte-btn.is-italic { font-style: italic; font-family: var(--font-body-serif, serif); }
.work-rte-btn.is-underline { text-decoration: underline; }
.work-rte-btn.is-struck { text-decoration: line-through; }
.work-rte-font, .work-rte-size {
  height: 26px;
  max-width: 120px;
  border: 1px solid var(--line);
  border-radius: var(--r-xs);
  background: var(--surface);
  color: var(--ink-2);
  font: inherit;
  font-size: 11.5px;
}
.work-rte-size { max-width: 84px; }
/* The swatch is the control. Sized like a button so the row keeps one rhythm. */
.work-rte-colour {
  width: 26px;
  height: 26px;
  padding: 2px;
  border: 1px solid var(--line);
  border-radius: var(--r-xs);
  background: var(--surface);
  cursor: pointer;
}
.work-rte-body {
  border-radius: 0 0 var(--r-sm) var(--r-sm);
  overflow-y: auto;
  text-align: left;
}
.work-rte-body:focus { outline: 2px solid var(--accent-soft); outline-offset: -2px; }
/* An empty contenteditable has no placeholder of its own; this is the whole reason the
   attribute is on the element. */
.work-rte-body:empty::before {
  content: attr(data-placeholder);
  color: var(--faint);
  pointer-events: none;
}
.work-rte-body p { margin: 0 0 10px; }
.work-rte-body ul, .work-rte-body ol { margin: 0 0 10px; padding-left: 22px; }
.work-rte-body blockquote {
  margin: 0 0 10px;
  padding-left: 12px;
  border-left: 2px solid var(--line);
  color: var(--ink-2);
}
.work-rte-body a { color: var(--accent); }
.work-browser-column {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 2px 2px 2px 6px;
  border-radius: var(--r-xs);
}
.work-browser-column:hover { background: var(--surface-2); }
.work-browser-column-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-family: var(--ui);
  font-size: 12px;
  color: var(--ink-2);
}
.work-browser-column-move,
.work-browser-column-drop {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  padding: 0;
  border: 0;
  border-radius: var(--r-xs);
  background: transparent;
  color: var(--faint);
  cursor: pointer;
}
.work-browser-column-move:hover,
.work-browser-column-drop:hover { color: var(--ink-2); background: var(--surface); }
.work-browser-column-move:disabled,
.work-browser-column-drop:disabled { opacity: 0.3; cursor: default; }
.work-browser-column-move:disabled:hover,
.work-browser-column-drop:disabled:hover { color: var(--faint); background: transparent; }
.work-browser-move-mark.is-up { transform: rotate(-90deg); }
.work-browser-move-mark.is-down { transform: rotate(90deg); }
.work-browser-column-add {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 4px;
  padding-top: 5px;
  border-top: 1px solid var(--line-soft);
}

/* ---------------------------------------------------------------- Work: the three lenses (WS-16)
   The Work shell is columns, and a lens changes what the SECOND one lists. Messages is
   today's list, the one that has always been there. Topics lists the work streams the
   reader's mail and Teams have been sorted into; People lists the people on the other end
   of them. The rail, the reader and the five sources do not move.

   Nothing here computes a length in JS. A group row's counts are TEXT, not a bar — the
   number of things in a stream is a number a reader reads, and eleven width bands to
   express one number is the shape of a defect this codebase already carries twice. The
   switcher is a static column and every row is a flex row with fixed and flexible tracks. */
.work-lenses { display: flex; flex-direction: column; gap: 2px; margin-bottom: 6px; }
.work-lens {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 5px 8px;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--work-rail-label);
  font-family: var(--ui);
  font-size: 12px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
.work-lens:hover { background: var(--work-rail-hover); color: var(--work-rail-ink); }
.work-lens.active { background: var(--work-rail-hover); color: var(--work-rail-active-bg); }
.work-lens-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Column two under a lens: one line per stream, or one line per person. */
.work-groups { flex: 1; min-height: 0; overflow-y: auto; }
.work-group {
  display: flex;
  align-items: baseline;
  gap: 8px;
  width: 100%;
  padding: 7px 10px;
  border: 0;
  border-left: 2px solid transparent;
  border-bottom: 1px solid var(--line-soft);
  background: transparent;
  text-align: left;
  cursor: pointer;
  white-space: nowrap;
}
.work-group:hover { background: var(--surface-2); }
.work-group.active { background: var(--accent-soft); border-left-color: var(--accent); }
.work-group-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  font-family: var(--ui);
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
}
/* The counts give ground first: how many of these are mine, and when, are what the reader
   is scanning for, and neither of those may ellipsis away. */
.work-group-meta {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--faint);
}
.work-group-flag {
  flex-shrink: 0;
  padding: 1px 6px;
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  border-radius: var(--r-pill);
  background: var(--accent-soft);
  color: var(--accent);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.work-group-time { flex-shrink: 0; font-family: var(--mono); font-size: 11px; color: var(--faint); }

/* Column three under a lens: the selected stream, with whatever was clicked in it below. */
.work-stream { display: flex; flex: 1 1 55%; flex-direction: column; min-height: 0; overflow-y: auto; }
.work-stream-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 8px 12px;
  border-bottom: 1px solid var(--line-soft);
  background: var(--surface);
}
.work-stream-title { font-family: var(--ui); font-size: 13.5px; font-weight: 700; color: var(--ink); }
.work-stream-addr { font-family: var(--mono); font-size: 11px; color: var(--mute); }
.work-stream-row {
  display: flex;
  align-items: baseline;
  gap: 8px;
  width: 100%;
  padding: 6px 12px;
  border: 0;
  border-bottom: 1px solid var(--line-soft);
  background: transparent;
  text-align: left;
  cursor: pointer;
  white-space: nowrap;
}
.work-stream-row:hover { background: var(--surface-2); }
.work-stream-row.active { background: var(--accent-soft); }
.work-stream-source { flex: 0 0 16px; color: var(--faint); }
.work-stream-who {
  flex: 0 0 140px;
  overflow: hidden;
  text-overflow: ellipsis;
  font-family: var(--ui);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink-2);
}
.work-stream-line {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  font-family: var(--ui);
  font-size: 12.5px;
  color: var(--ink-2);
}
.work-stream-time { flex-shrink: 0; font-family: var(--mono); font-size: 11px; color: var(--faint); }

/* What the lens does not know, said out loud rather than rendered as an empty list. */
.work-lens-empty { padding: 12px 14px; font-family: var(--ui); font-size: 12.5px; line-height: 1.5; color: var(--mute); }
.work-lens-empty.is-error { color: var(--accent); }
.work-lens-note { padding: 6px 12px; font-family: var(--mono); font-size: 11px; line-height: 1.5; color: var(--faint); }

@media (max-width: 900px) {
  .work-lenses { flex-direction: row; margin-bottom: 0; }
  .work-lens { width: auto; white-space: nowrap; }
}

/* ---------------------------------------------------------------- Work: popup windows (WS-12, WS-13)
   An identified action is something a reader acts on, so it is a control: hovering it says
   what is being proposed, and pressing it opens the thread in a window inside the app
   rather than taking the screen away from them. The windows move, resize, minimise, snap to
   each other and to the edges, survive opening another message, and — when kept — survive
   leaving Work altogether.

   THE ONE EXCEPTION THIS BLOCK TAKES, STATED RATHER THAN HIDDEN. The strict-CDS header of
   this file says dynamic geometry uses a native element or CSS-only structure and never a
   JS-computed value. A window manager's rect cannot be either of those: it is an arbitrary
   {x, y, w, h} chosen by the reader's own drag, on a viewport of unknown size, and there is
   no native element for it and no finite set of classes that could express it without
   quantising the reader's own placement. This file already carries that carve-out in
   writing for .cobrowse-window ("Position and size are the one thing that cannot be a
   static rule, so they arrive as custom properties the surface sets at runtime"), and
   .work-popup is its third consumer after the co-browse window and the canvas panes.

   The exception is bounded: ONE function in work-popups.js (applyPopupGeometry) may touch a
   node's style, it writes only --wpop-x/-y/-w/-h, no render function anywhere passes a
   style prop, and every appearance decision — surface, border, radius, shadow, type,
   spacing, the minimised collapse, the phone bottom sheet — is a class here. A test asserts
   all three. */
.work-popup-layer { position: fixed; inset: 0; z-index: 40; pointer-events: none; }
.work-popup {
  /* WS-D21: the bar's contents depend on how wide THIS window is, not on the viewport - two
     windows of different widths sit on the same screen. Declaring the window a container is
     what lets the chain count hide on a narrow one and show on a wide one. */
  container-type: inline-size;
  position: fixed;
  left: var(--wpop-x, 40px);
  top: var(--wpop-y, 60px);
  width: var(--wpop-w, 420px);
  height: var(--wpop-h, 480px);
  z-index: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
  box-shadow: var(--shadow);
  pointer-events: auto;
}
.work-popup.is-front { border-color: color-mix(in srgb, var(--ink-2) 30%, var(--line)); }
.work-popup.is-kept { border-left: 2px solid var(--accent); }
.work-popup.is-min { height: auto; width: min(var(--wpop-w, 360px), 280px); }
.work-popup.is-min .work-popup-body { display: none; }
.work-popup-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 34px;
  padding: 0 6px 0 10px;
  border-bottom: 1px solid var(--line-soft);
  background: var(--surface-2);
  cursor: move;
  touch-action: none;
}
/* WS-D21: the bar's word-buttons are flex:none, so they took the whole row and the title
   collapsed to the two letters that happened to fit - a window about "We detected
   synchronization errors in your directory" was titled "We". Giving the title an 11ch floor
   fixed the name and broke something worse: the tools ran off the right edge and the close
   control became unreachable. So the floor is modest, the chain count gives way first, and
   the tools are never allowed to shrink or clip - a window must always be closable. */
.work-popup-title {
  flex: 1 1 auto;
  min-width: 7ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--ui);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink);
}
/* WS-D21 second remainder: a count truncated to "1 i..." tells the reader nothing and costs
   the title the room to be read. Below the width where both fit, the count goes away and
   the title gets it. */
@container (max-width: 470px) {
  .work-popup-sub { display: none; }
}
.work-popup-sub {
  flex: 0 0 auto;
  min-width: 0;
  max-width: 40%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--mute);
}
.work-popup-tools { display: flex; flex: none; align-items: center; gap: 4px; margin-left: auto; }
/* The two controls in the bar that act on the WINDOW rather than on what is inside it. */
.work-popup-act {
  padding: 2px 7px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--ink-2);
  font-family: var(--ui);
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
}
.work-popup-act:hover { border-color: var(--accent); color: var(--accent); }
/* "Kept" says what the control does where "pinned" is jargon, and it is a word rather than
   a glyph because the design system's icon map has no pin in it and cannot be rebuilt from
   its source today. */
.work-popup-keep {
  padding: 2px 6px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface-2);
  color: var(--mute);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
}
.work-popup-keep.is-on { border-color: color-mix(in srgb, var(--accent) 45%, transparent); background: var(--accent-soft); color: var(--accent); }
.work-popup-tool {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border: 0;
  border-radius: var(--r-xs);
  background: transparent;
  color: var(--mute);
  cursor: pointer;
}
.work-popup-tool:hover { background: var(--surface); color: var(--ink); }
.work-popup-tool.is-on { background: var(--accent-soft); color: var(--accent); }
.work-popup-mark { display: block; }
.work-popup-mark.is-down { transform: rotate(90deg); }
.work-popup-mark.is-up { transform: rotate(-90deg); }
.work-popup-body { flex: 1; min-height: 0; overflow: auto; padding: 0; }
.work-popup-grip { position: absolute; right: 0; bottom: 0; width: 14px; height: 14px; cursor: nwse-resize; touch-action: none; }
.work-popup-grip-x { position: absolute; top: 0; right: 0; bottom: 14px; width: 5px; cursor: ew-resize; touch-action: none; }
.work-popup-grip-y { position: absolute; right: 14px; bottom: 0; left: 0; height: 5px; cursor: ns-resize; touch-action: none; }
.work-popup-status { padding: 8px 12px; font-family: var(--ui); font-size: 12px; line-height: 1.5; color: var(--mute); }
.work-popup-status.is-error { color: var(--accent); }
.work-popup-empty { padding: 12px; font-family: var(--ui); font-size: 12.5px; color: var(--mute); }
.work-popup-thread-head { padding: 10px 12px 6px; border-bottom: 1px solid var(--line-soft); }
.work-popup-thread-meta { font-family: var(--mono); font-size: 11px; line-height: 1.5; color: var(--mute); }

/* The identified action, in the list, as a control. */
.mail-item-open {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.mail-action-tag {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 1px 5px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  color: var(--mute);
  background: var(--surface-2);
  cursor: pointer;
}
.mail-action-tag.is-action { border-color: color-mix(in srgb, var(--accent) 45%, transparent); background: var(--accent-soft); color: var(--accent); }
.mail-action-tag:hover { border-color: var(--accent); }
.mail-action-tag:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Deliberately the geometry the chat surface's own hover panel already uses, so the two
   hover panels in this product read as one thing. */
.mail-action-hint {
  position: absolute;
  left: 34px;
  right: 10px;
  top: calc(100% - 2px);
  z-index: 8;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}
.mail-action-hint-ask { font-family: var(--ui); font-size: 12.5px; line-height: 1.45; color: var(--ink); }
.mail-action-hint-status { font-family: var(--ui); font-size: 11.5px; color: var(--mute); }

@media (prefers-reduced-motion: reduce) {
  .work-popup { transition: none; }
}
/* On a phone the layer stops being a window manager: the front window is a sheet along the
   bottom, everything else is a one-line bar above it, and nothing is dragged or resized. */
@media (max-width: 900px) {
  .work-popup {
    top: auto;
    right: 0;
    bottom: 0;
    left: 0;
    width: auto;
    height: min(72vh, 560px);
    border-radius: var(--r-sm) var(--r-sm) 0 0;
  }
  .work-popup.is-min { position: static; width: auto; height: auto; }
  .work-popup-bar { cursor: default; }
  .work-popup-grip, .work-popup-grip-x, .work-popup-grip-y { display: none; }
}

/* ---------------------------------------------------------------- Work: the action body (WS-14, WS-D7)
   What is inside the window an action tag opens. Dense, one fact per line, no headings, no
   cards, no stat tiles: the verdict, whether it has already been handled and the evidence
   for that, the chain in words with the threads it is related to, the plan running on it,
   and the proposals as buttons that start one.

   The progress bar is a native <progress> whose value and max are ATTRIBUTES, not styles —
   which is what the design system's own rule asks for, and what its usage meter above
   already does. A <progress> with no value is natively indeterminate, which is the honest
   render while the planner is still thinking: no fake zero, no invented percentage, and no
   JS-computed width. */
.work-action-body {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-height: 0;
  padding: 10px 12px 14px;
}
.work-action-line { display: flex; align-items: baseline; gap: 8px; }
.work-action-label {
  flex: none;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--faint);
}
.work-action-value {
  flex: 1;
  min-width: 0;
  font-family: var(--ui);
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-2);
}
.work-action-value.is-error { color: var(--danger, var(--accent)); }

/* Whether this is already dealt with, in one word plus what that word rests on. */
.work-handled {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 4px 8px;
  border-left: 2px solid var(--line);
  background: var(--surface-2);
  font-family: var(--ui);
  font-size: 12.5px;
  color: var(--ink-2);
}
.work-handled.is-handled { border-left-color: var(--accent); color: var(--ink); }
.work-handled.is-progress { border-left-color: var(--mute); }
.work-handled.is-changed { border-left-color: var(--accent); }
.work-handled.is-open { border-left-color: var(--line); }
.work-handled.is-unchecked { border-left-color: var(--line); color: var(--mute); }
.work-evidence { display: flex; flex-direction: column; gap: 2px; }
.work-evidence-row {
  display: flex;
  align-items: baseline;
  gap: 6px;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--faint);
  white-space: nowrap;
}
.work-evidence-quote {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  font-family: var(--ui);
  font-size: 11.5px;
  color: var(--mute);
}

/* The chain this message is part of, and the threads that share its topic. */
.work-chain-line { font-family: var(--ui); font-size: 12.5px; line-height: 1.5; color: var(--ink-2); }
.work-chain-link {
  display: block;
  width: 100%;
  padding: 3px 0;
  border: 0;
  background: transparent;
  color: var(--accent);
  font-family: var(--ui);
  font-size: 12px;
  text-align: left;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: pointer;
}
.work-chain-link:hover { text-decoration: underline; }

/* The plan, and how far through it Yarnball is. */
.work-plan-strip { display: flex; align-items: center; gap: 8px; }
.work-plan-meter {
  flex: 1;
  width: 100%;
  height: 4px;
  border: 0;
  border-radius: 2px;
  background: var(--line-soft);
  appearance: none;
  -webkit-appearance: none;
}
.work-plan-meter::-webkit-progress-bar { background: var(--line-soft); border-radius: 2px; }
.work-plan-meter::-webkit-progress-value { background: var(--accent); border-radius: 2px; }
.work-plan-meter::-moz-progress-bar { background: var(--accent); border-radius: 2px; }
.work-plan-steps { display: flex; flex-direction: column; gap: 2px; }
.work-plan-step {
  display: flex;
  align-items: baseline;
  gap: 6px;
  font-family: var(--ui);
  font-size: 12px;
  color: var(--ink-2);
}
/* The state marker is drawn here, never as a glyph in app code. */
.work-plan-step::before {
  content: '·';
  flex: none;
  width: 10px;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--faint);
  text-align: center;
}
.work-plan-step.is-pending::before { content: '·'; }
.work-plan-step.is-in_progress::before { content: '>'; color: var(--accent); }
.work-plan-step.is-done::before { content: '+'; color: var(--mute); }
.work-plan-step.is-failed::before { content: '!'; color: var(--danger, var(--accent)); }
.work-plan-step.is-waiting_approval::before { content: '?'; color: var(--accent); }
.work-plan-step.is-done { color: var(--mute); }
.work-plan-step-label { flex: none; }
.work-plan-step-result {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--ui);
  font-size: 11.5px;
  color: var(--faint);
}
.work-plan-step-act {
  flex: none;
  padding: 0 5px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface-2);
  color: var(--ink-2);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
}
.work-plan-step-act:hover { border-color: var(--accent); color: var(--accent); }

/* ---- Apps: the app store list and the workspace host (branch app-store, YUM-888).
   The store is a dense row list — name, publisher, what the app adds and reaches,
   Install/Remove/Open. The workspace host fills the content area with the app's
   sandboxed iframe under a one-line bar. Same vocabulary rules as .work-*: semantic
   classes only, tokens only, no per-app styling leaks (the iframe INTERIOR is the
   app's own content, sandboxed on an opaque origin). */
.apps-view { display: flex; flex-direction: column; gap: 2px; padding: 12px 16px; overflow-y: auto; min-height: 0; }
.apps-row { display: flex; align-items: center; gap: 12px; padding: 8px 10px; border-bottom: 1px solid var(--line-soft); }
.apps-row-main { flex: 1; min-width: 0; }
.apps-row-name { font-weight: 600; }
.apps-row-hint { font-weight: 400; color: var(--ink-soft); font-size: 12px; }
.apps-row-sub { color: var(--ink-soft); font-size: 12px; line-height: 1.4; }
.apps-row-actions { display: flex; gap: 6px; flex: none; }
.apps-workspace { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.apps-workspace-bar { display: flex; align-items: center; gap: 10px; padding: 6px 10px; border-bottom: 1px solid var(--line-soft); }
.apps-workspace-title { font-weight: 600; }
.apps-workspace-frame { flex: 1; width: 100%; border: 0; min-height: 0; background: var(--paper, #fff); }
/* ── Phone pass, Ben's annotated review 2026-08-18 ────────────────────────────────────
   Three notes on the phone captures: "much smaller on phone" against the headings and
   cards, "add space" between the sign-in card and the section under it, and "only show
   mobile for mobile version" on the downloads. Type scale is stepped down here rather
   than by editing the shared tokens, which the signed-in app reads too. */
@media (max-width: 640px) {
  .public-hero-title { font-size: var(--t-title); line-height: 1.16; }
  .public-hero-subtitle { font-size: var(--t-body); margin-bottom: 20px; }
  .public-section-title { font-size: var(--t-title-s); }
  .public-section-sub { font-size: var(--t-small); margin-bottom: 20px; }
  .public-feature-title, .public-download-title { font-size: var(--t-body); }
  .public-feature-desc, .public-download-meta, .public-download-note, .public-download-caveat { font-size: var(--t-small); }
  .public-feature-card, .public-download-card { padding: 16px; }
  .public-security-title { font-size: var(--t-body); }
  .public-security-desc { font-size: var(--t-small); }

  /* "add space" — the sign-in card sat flush against the heading of the next section. */
  .public-hero-split { margin-bottom: 44px; }
  .public-hero-auth { margin-bottom: 8px; }

  /* "only show mobile for mobile version" — a .dmg or an .exe cannot be installed from the
     phone that is reading the page. The cards stay in the document for anyone who opens the
     same URL on a laptop; they are simply not offered here. */
  .public-download-card.is-desktop-only { display: none; }

  /* "shrink" on the pricing cards. */
  .pricing-plan-name { font-size: var(--t-body); }
  .pricing-plan-price { font-size: var(--t-title-s); }
  .pricing-plan-card { padding: 16px; }
  .pricing-plan-list, .pricing-plan-desc { font-size: var(--t-small); }
}

/* Pricing page alignment, Ben's annotated review 2026-08-18 ("not aligned").
   .legal-head is shared with /terms and /privacy, where a centred document measure is right,
   so the correction is scoped to the pricing page: heading, card grid and the prose under it
   all start at the same left edge. The prose keeps its reading measure — it is the centring
   that moved, not the width. */
.pricing-page .legal-head { max-width: none; margin-inline: 0; }
.pricing-page .legal-head h1 { text-align: left; }
.pricing-page .legal-lede { margin-inline: 0; }
.pricing-page .pricing-notes { margin-inline: 0; max-width: var(--doc-measure); }
.pricing-page .pricing-footnote { max-width: var(--doc-measure); margin-inline: 0; }
/* Three cards need a narrower minimum than two did, or the third wraps to its own row on a
   laptop and the row Ben bracketed is empty again. */
.pricing-page .pricing-grid { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
@media (max-width: 720px) { .pricing-page .pricing-grid { grid-template-columns: 1fr; } }

/* Contact page alignment, Ben's annotated review 2026-08-18 ("align", bracketed down the
   left of the heading and the form). Same shape as the pricing fix and scoped the same way,
   because /terms and /privacy want to keep the centred document measure. */
.contact-page .legal-head { max-width: none; margin-inline: 0; }
.contact-page .legal-head h1 { text-align: left; }
.contact-page .legal-lede { margin-inline: 0; }
.contact-page .contact-form { margin-inline: 0; }

/* Step numbers on the security diagram, keyed to the numbered sections below it
   (Ben, 2026-08-18: "make it more understandable"). */
.sec-step-no { fill: var(--accent); font-family: var(--mono); font-size: 12px; font-weight: 700; letter-spacing: var(--ls-label); }

/* ── Second pass, after looking at the rendered result (2026-08-18) ───────────────────
   Three things only the screenshots showed. */

/* 1. The feature list is five cards now that shared sessions leads it. auto-fit against a
      230px minimum resolves to four across on a 1440 window, which strands the fifth alone
      on its own row and reads as an unfinished grid. Five fit at this measure. */
@media (min-width: 1100px) {
  .public-features-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}
/* Between the two, three across with a pair under it — even, not stranded. */
@media (min-width: 720px) and (max-width: 1099px) {
  .public-features-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* 2. On a phone the Sign in pill was landing between the brand and the menu button, because
      only the toggle and the links carried an order. Give the pill one too so the bar reads
      brand — sign in — menu, left to right, with nothing floating in the middle. */
@media (max-width: 640px) {
  .public-nav-brand { order: 1; }
  .public-nav-signin, .public-nav-cta { order: 2; margin-left: auto; }
  .has-nav-menu .public-nav-menu-toggle { order: 3; margin-left: 10px; }
  .public-nav-links, .has-nav-menu .public-nav-links.is-open { order: 4; }
}
/* ---- Maximized modal ----------------------------------------------------------------
   Last in this file ON PURPOSE, and deliberately more specific than `.mcard`.
   Two separate rules cap the document card — `.mcard{max-height:86vh}` in the DS bundle and
   `.mcard:has(.simple-canvas-viewer-body){max-width:96vw;max-height:94vh}` above — and a
   maximized card has to beat both under every viewport and media-query combination.
   Measured on the ben lane at 1280x720: with the rule sitting earlier in the file the
   "maximized" card came out 1226x672, i.e. exactly 96vw x 94vh, inside a visible margin.
   Specificity alone should have been enough; being last as well costs nothing and removes
   the question entirely. */
.modal.is-maximized { padding: 0; }
.modal .mcard.is-maximized {
  width: 100vw;
  height: 100vh;
  max-width: none;
  max-height: none;
  border-radius: 0;
  /* A drag handle would fight the maximized size on the next reflow. */
  resize: none;
}

/* 3. The security diagram is three boxes side by side. Scaled to a 390px phone its labels
      came out around 5px and were simply not readable — checked on the lane, 2026-08-18.
      It keeps its real size and scrolls inside its own figure instead, which is the same
      answer we give any wide element; the page body must never scroll sideways with it. */
@media (max-width: 720px) {
  .sec-figure { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .sec-diagram { min-width: 660px; }
}

/* Landing security band: the copy and the recording of the live check, side by side
   (YUM-616). Copy leads on a phone; the clip follows it. */
.public-security-band { display: grid; grid-template-columns: minmax(0, 1fr) minmax(320px, 520px); gap: 26px; align-items: start; }
.public-security-copy .public-security-desc + .public-security-desc { margin-top: 10px; }
@media (max-width: 900px) { .public-security-band { grid-template-columns: 1fr; } }

/* The proof block that opens the security page: the live check and the recording of the
   same check inside the app, side by side above the explanation (YUM-616). */
/* Aligned to the document grid below it (.legal-layout), so the page has one left edge
   from the title down: the check sits over the prose column, the clip over the index rail. */
.sec-proof { width: min(100%, calc(62ch + 60px + 236px)); margin: 26px auto 38px; }
.sec-proof-head { max-width: 62ch; }
.sec-proof-head h2 { font-family: var(--display); font-size: var(--t-title); letter-spacing: var(--ls-title); margin: 0 0 8px; color: var(--ink); }
.sec-proof-head p { color: var(--ink-2); font-size: var(--t-body); line-height: var(--lh-body); margin: 0 0 16px; }
.sec-proof .livecheck-film { max-width: 556px; margin-top: 26px; }

/* Reference pairs — term left, mechanism right, hairline between. Used where the copy is
   already written as "In transit / At rest / In use" leads, which read as a table, not prose. */
.legal-defs { margin: 0 0 4px; border-top: 1px solid var(--line); }
.legal-def { display: grid; grid-template-columns: 150px minmax(0, 1fr); gap: 8px 24px; padding: 11px 0; border-bottom: 1px solid var(--line-soft); }
.legal-def dt { color: var(--ink); font-size: var(--t-body-s); }
.legal-def dd { color: var(--ink-2); font-size: var(--t-body-s); line-height: var(--lh-body); margin: 0; }
@media (max-width: 620px) { .legal-def { grid-template-columns: 1fr; gap: 3px; } }

/* ==========================================================================
   YUM-614 — the live security check on the public /security page.
   The signed-in modal's lock (.sec-lock) is reused as-is; everything else here
   is the public surface's own semantics: the walked path, the padlock panel
   drawn from the measured handshake, the model roster, and where this runs.
   Dense by intent — this is evidence, not a dashboard.
   ========================================================================== */
/* The path lock at row scale. The 44x52 lock above is a hero mark; a dense evidence row
   needs the same drawing at half size, so the variant restates its geometry rather than
   scaling a transform (a transform would keep the big footprint and push the label off). */
.sec-lock.is-sm { width: 22px; height: 26px; }
.sec-lock.is-sm .sec-lock-body { left: 2px; right: 2px; height: 15px; border-width: 2px; border-radius: 4px; }
.sec-lock.is-sm .sec-lock-keyhole { top: 4px; width: 3px; height: 3px; margin-left: -1.5px; }
.sec-lock.is-sm .sec-lock-keyhole::after { left: 1px; top: 2px; width: 1px; height: 4px; }
.sec-lock.is-sm .sec-lock-shackle { left: 5px; right: 5px; top: 1px; height: 11px; border-width: 2px; border-radius: 7px 7px 0 0; }
.sec-lock.is-sm.is-pending .sec-lock-shackle, .sec-lock.is-sm.is-fail .sec-lock-shackle { transform: rotate(-42deg) translate(-1px, -1.5px); }
.livecheck { max-width: none; margin: 0; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface); }
.livecheck-head { display: grid; grid-template-columns: 1fr auto; align-items: baseline; column-gap: 12px; row-gap: 2px; padding: 12px 14px; border-bottom: 1px solid var(--line-soft); }
.livecheck-when { grid-column: 1; }
.livecheck-state { font-size: var(--t-body-s); color: var(--ink); font-weight: 600; }
.livecheck-state.is-fail { color: var(--accent); }
.livecheck-when { font-family: var(--mono); font-size: var(--t-label); letter-spacing: var(--ls-label); color: var(--mute); }
.livecheck-again { grid-column: 2; grid-row: 1 / span 2; align-self: center; font: inherit; font-size: var(--t-small); color: var(--ink); background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-pill); padding: 4px 12px; cursor: pointer; }
.livecheck-again:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.livecheck-again:disabled { color: var(--faint); cursor: default; }

.livecheck-path { padding: 4px 0; }
.livecheck-step-holder + .livecheck-step-holder { border-top: 1px solid var(--line-soft); }
.livecheck-step { display: grid; grid-template-columns: 26px 1fr auto; align-items: start; gap: 12px; padding: 10px 14px; }
.livecheck-step-label { font-size: var(--t-body-s); color: var(--ink); }
.livecheck-step-sub { font-size: var(--t-small); color: var(--mute); line-height: var(--lh-dense); }
.livecheck-step.is-fail .livecheck-step-sub { color: var(--accent); }
.livecheck-proof-toggle { font: inherit; font-family: var(--mono); font-size: 11px; white-space: nowrap; padding-top: 2px; letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--mute); background: none; border: 0; padding: 0; cursor: pointer; }
.livecheck-proof-toggle:hover { color: var(--accent); }
.livecheck-proof-slot { padding: 0 14px 10px 52px; }
.livecheck-proof-line { display: grid; grid-template-columns: minmax(120px, 210px) 1fr; gap: 10px; padding: 2px 0; }
.livecheck-proof-k { font-family: var(--mono); font-size: var(--t-label); color: var(--mute); }
.livecheck-proof-v { font-family: var(--mono); font-size: var(--t-label); color: var(--ink-2); overflow-wrap: anywhere; }

.livecheck-padlock-slot { padding: 10px 14px 14px; border-top: 1px solid var(--line-soft); }
.livecheck-padlock-btn { display: inline-flex; align-items: center; gap: 8px; font: inherit; font-size: var(--t-small); color: var(--ink); background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-pill); padding: 5px 14px 5px 10px; cursor: pointer; }
.livecheck-padlock-btn:hover, .livecheck-padlock-btn.is-open { border-color: var(--accent); }
.livecheck-padlock-panel { margin-top: 8px; max-width: 460px; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface-2); padding: 12px 14px; animation: livecheck-drop .22s ease-out both; }
.livecheck-padlock-panel[hidden] { display: none; }
@keyframes livecheck-drop { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .livecheck-padlock-panel { animation: none; } }
.livecheck-padlock-title { font-size: var(--t-body-s); font-weight: 600; color: var(--ink); }
.livecheck-padlock-sub { font-family: var(--mono); font-size: var(--t-label); color: var(--mute); margin: 2px 0 9px; overflow-wrap: anywhere; }
.livecheck-padlock-row { display: grid; grid-template-columns: minmax(110px, 170px) 1fr; gap: 10px; padding: 2px 0; }
.livecheck-padlock-k { font-size: var(--t-small); color: var(--mute); }
.livecheck-padlock-v { font-family: var(--mono); font-size: var(--t-label); color: var(--ink-2); overflow-wrap: anywhere; }
.livecheck-padlock-note { margin-top: 9px; font-size: var(--t-small); color: var(--faint); line-height: var(--lh-dense); }

.livecheck-models { border-top: 1px solid var(--line-soft); }
.livecheck-models-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; padding: 10px 14px 6px; }
.livecheck-models-title { font-size: var(--t-body-s); color: var(--ink); font-weight: 600; }
.livecheck-models-note { font-size: var(--t-small); color: var(--mute); }
.livecheck-model + .livecheck-model { border-top: 1px solid var(--line-soft); }
.livecheck-model.is-pending .livecheck-model-sub { color: var(--faint); }
.livecheck-model-main { display: grid; grid-template-columns: 26px 1fr auto; align-items: start; gap: 12px; width: 100%; text-align: left; font: inherit; background: none; border: 0; padding: 8px 14px; cursor: pointer; }
.livecheck-model-main:hover { background: var(--surface-2); }
.livecheck-model-name { font-size: var(--t-body-s); color: var(--ink); }
.livecheck-model-sub { font-size: var(--t-small); color: var(--mute); line-height: var(--lh-dense); }
.livecheck-model.is-fail .livecheck-model-sub { color: var(--accent); }
.livecheck-model-role { font-family: var(--mono); font-size: var(--t-label); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--faint); }
.livecheck-model-link { display: inline-block; margin-top: 6px; font-size: var(--t-small); color: var(--accent); }

.livecheck-hosting { border-top: 1px solid var(--line-soft); padding: 10px 14px 13px; }
.livecheck-hosting-title { font-size: var(--t-body-s); font-weight: 600; color: var(--ink); margin-bottom: 5px; }
.livecheck-hosting-row { display: grid; grid-template-columns: minmax(150px, 220px) 1fr; gap: 10px; padding: 2px 0; }
.livecheck-hosting-k { font-size: var(--t-small); color: var(--mute); }
.livecheck-hosting-v { font-size: var(--t-small); color: var(--ink-2); line-height: var(--lh-dense); }

/* The recording of the same check running inside the signed-in app. No frame: the clip is
   cropped to the card itself, so a band around it would only add a second border to a
   picture that already has one (Ben, 2026-08-19: "crop it a bit remove the grey frame"). */
.livecheck-film { margin: 0; }
.livecheck-film-label { display: block; font-family: var(--mono); font-size: var(--t-label); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--mute); margin-bottom: 8px; }
.livecheck-film video { display: block; width: 100%; height: auto; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface); }
.livecheck-film figcaption { margin-top: 9px; font-size: var(--t-small); color: var(--mute); line-height: var(--lh-dense); }

@media (max-width: 620px) {
  .livecheck-proof-slot { padding-left: 14px; }
  .livecheck-proof-line, .livecheck-padlock-row, .livecheck-hosting-row { grid-template-columns: 1fr; gap: 0; }
  .livecheck-model-role { display: none; }
  /* At phone width the proof toggle in its own column squeezed every label onto two
     lines. It drops under the text instead, where it costs one line and takes none. */
  .livecheck-step, .livecheck-model-main { grid-template-columns: 26px 1fr; }
  .livecheck-proof-toggle { grid-column: 2; justify-self: start; margin-top: 5px; }
}

/* Email Reader Attachments & Signatures & Lang Select */
/* G02: attachments are a row of chips under the header - the paperclip on each is the
   heading, the size sits beside the name, the two actions are small. No box, no title. */
.mail-reader-attachments { margin: 6px 12px 0; padding: 0; border: 0; background: transparent; }
.mail-reader-attachments-head { font-size: var(--t-body-s); font-weight: 600; color: var(--ink); margin-bottom: 8px; }
.mail-reader-attachments-list { display: flex; flex-direction: row; flex-wrap: wrap; gap: 6px; }
/* C1 (2026-08-26): the whole card is now the open control (paperclip + name + size), Save
   and Save-to-OneDrive are the only two actions on it, and the old text "View attachment"
   button / .mail-attachment-btn download link are gone — a click anywhere on the card opens
   the viewer, so a separate button saying the same thing had nothing left to do. */
.mail-attachment-card { display: flex; flex-direction: column; max-width: 100%; border: 1px solid var(--line-soft); border-radius: var(--r-sm); background: var(--surface); padding: 3px 8px 3px 6px; cursor: pointer; }
.mail-attachment-card:hover { border-color: var(--accent); background: var(--surface-2); }
.mail-attachment-card:hover .mail-attachment-name { color: var(--accent); }
.mail-attachment-main { display: flex; align-items: center; gap: 6px; flex-wrap: nowrap; min-width: 0; }
.mail-attachment-info { display: flex; flex-direction: row; align-items: baseline; gap: 6px; flex: 0 1 auto; min-width: 0; }
.mail-attachment-name { font-size: var(--t-body-s); color: var(--ink); font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mail-attachment-size { font-size: var(--t-small); color: var(--mute); }
.mail-attachment-actions { display: flex; align-items: center; gap: 4px; margin-left: 4px; }
.mail-attachment-actions .icbtn { width: 24px; height: 24px; box-shadow: none; }
.mail-attachment-error { font-size: var(--t-small); color: var(--accent); margin-top: 6px; }
.mail-attachment-translation { margin-top: 8px; padding: 8px 10px; border-top: 1px solid var(--line-soft); background: var(--surface-2); border-radius: var(--r-sm); }
.mail-attachment-tx-head { font-size: var(--t-small); font-weight: 600; color: var(--ink); margin-bottom: 4px; }
.mail-attachment-tx-body { font-size: var(--t-small); color: var(--ink-2); white-space: pre-wrap; font-family: inherit; margin: 0; }
.mail-lang-select-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 6px 10px; }
/* Ben, 2026-10-21: the compose window's buttons and choosers are rounded, not square —
   the same pill language the top bar's controls use. */
.mail-lang-select { font: inherit; font-size: var(--t-small); color: var(--ink); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-pill); padding: 2px 10px; }
.mail-sig-select { font: inherit; font-size: var(--t-small); color: var(--ink); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-pill); padding: 2px 10px; }
.mail-compose-toolbar-wrap { display: flex; align-items: center; justify-content: space-between; gap: 8px; }


/* The Files window (SB 2026-09-08): the one file manager, a window the person moves and
   resizes. Its geometry crosses from side-canvas-panel.js as the same four custom
   properties the browser window uses (--cbw-x/y/w/h, applyGeometry in browser-modal.js);
   it opens beside the chat on the right and remembers where it was put. */
.side-canvas-panel {
  position: fixed;
  left: var(--cbw-x, auto);
  top: var(--cbw-y, 64px);
  width: var(--cbw-w, 440px);
  height: var(--cbw-h, calc(100vh - 80px));
  max-width: calc(100vw - 16px);
  max-height: calc(100vh - 16px);
  background: var(--surface, #ffffff);
  border: 1px solid var(--line, #e0e0e0);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow);
  display: flex;
  flex-direction: column;
  z-index: 99;
}
.side-canvas-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 14px;
  cursor: move;
  touch-action: none;
  user-select: none;
  border-bottom: 1px solid var(--line, #e0e0e0);
  background: var(--surface-2, #f8f9fa);
}
.side-canvas-title-wrap {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
  font-size: 14px;
  color: var(--ink, #111111);
}
.side-canvas-actions {
  display: flex;
  align-items: center;
  gap: 6px;
}
.side-canvas-close-btn {
  background: transparent;
  border: none;
  font-size: 16px;
  cursor: pointer;
  color: var(--ink-2, #555555);
  padding: 2px 6px;
}
.side-canvas-close-btn:hover {
  color: var(--accent, #cc0000);
}
.side-canvas-btn {
  font-family: inherit;
  font-size: 12px;
  font-weight: 500;
  padding: 4px 10px;
  border-radius: var(--r-sm, 4px);
  cursor: pointer;
  border: 1px solid var(--line, #ccc);
  background: var(--surface, #fff);
  color: var(--ink, #111);
}
.side-canvas-btn-primary {
  background: var(--accent, #cc0000);
  color: #ffffff;
  border-color: var(--accent, #cc0000);
}
.side-canvas-btn-secondary {
  background: var(--surface-2, #f5f5f5);
  color: var(--ink, #111);
}
.side-canvas-tabs {
  display: flex;
  border-bottom: 1px solid var(--line, #e0e0e0);
  background: var(--surface-2, #fafafa);
}
.side-canvas-tab-btn {
  flex: 1;
  padding: 8px 12px;
  font-size: 13px;
  font-weight: 500;
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--ink-2, #555555);
  cursor: pointer;
}
.side-canvas-tab-btn.is-active {
  border-bottom-color: var(--accent, #cc0000);
  color: var(--ink, #111111);
  font-weight: 600;
}
.side-canvas-body {
  flex: 1;
  overflow-y: auto;
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.side-canvas-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  /* F-C01: the Files panel's toolbars hold a row of places and a row of actions; they
     wrap inside the panel's 420px rather than running off its right edge. */
  flex-wrap: wrap;
  padding: 8px 12px 0;
}
/* G01: the Files panel's one control row - the place picker takes the room a name needs
   and no more; the search keeps the rest. The head's own gap and padding do the spacing. */
.side-canvas-panel .mail-list-head > select.mail-search {
  flex: 0 1 44%;
  min-width: 120px;
}
.side-canvas-panel .side-canvas-actions {
  display: flex;
  align-items: center;
  gap: 6px;
}
.side-canvas-search-input {
  flex: 1;
  font: inherit;
  font-size: var(--t-small, 13px);
  padding: 6px 10px;
  border: 1px solid var(--line, #ccc);
  border-radius: var(--r-sm, 4px);
  background: var(--surface, #fff);
  color: var(--ink, #111);
}
.side-canvas-file-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 12px;
  border: 1px solid var(--line-soft, #e0e0e0);
  border-radius: var(--r-sm, 6px);
  background: var(--surface, #fff);
  cursor: pointer;
  transition: background 0.15s ease;
}
.side-canvas-file-card:hover {
  background: var(--surface-2, #f5f5f5);
  border-color: var(--line, #ccc);
}
.side-canvas-file-card.is-selected {
  border-color: var(--accent, #cc0000);
  background: var(--surface-2, #f9f9f9);
}
.side-canvas-file-info {
  display: flex;
  align-items: center;
  gap: 8px;
  overflow: hidden;
}
.side-canvas-file-name {
  font-size: 13px;
  font-weight: 500;
  color: var(--ink, #111);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.side-canvas-file-meta {
  font-size: 11px;
  color: var(--mute, #888);
  text-transform: uppercase;
}
.side-canvas-empty {
  text-align: center;
  padding: 32px 16px;
  color: var(--mute, #888);
  font-size: 13px;
}
.side-canvas-viewer-container {
  display: flex;
  flex-direction: column;
  height: 100%;
  gap: 8px;
}
.side-canvas-viewer-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--line-soft, #e0e0e0);
}
.side-canvas-viewer-title {
  font-size: 13px;
  color: var(--ink, #111);
  font-weight: 600;
}
.side-canvas-viewer-content {
  flex: 1;
  overflow-y: auto;
  border: 1px solid var(--line-soft, #e0e0e0);
  border-radius: var(--r-sm, 4px);
  background: var(--surface, #fff);
  padding: 8px;
}
.side-canvas-text-preview {
  font-family: var(--mono, monospace);
  font-size: 12px;
  color: var(--ink, #111);
  white-space: pre-wrap;
  word-break: break-word;
  margin: 0;
}
.side-canvas-viewer-frame {
  width: 100%;
  height: 100%;
  min-height: 450px;
  border: none;
}
/* Nested Mail Folders in Navigation */
.mail-nav-folders-tree { display: flex; flex-direction: column; gap: 2px; padding: 4px 0 6px 14px; border-left: 2px solid var(--line-soft); margin: 2px 0 4px 10px; }
.mail-item-top-right { display: inline-flex; align-items: center; gap: 6px; flex: none; }
.mail-attach-indicator { display: inline-flex; align-items: center; justify-content: center; color: var(--accent); font-size: var(--t-small); }
.mail-subject-attach-icon { display: inline-flex; align-items: center; color: var(--accent); margin-right: 4px; flex: none; vertical-align: middle; }
.mail-flag.is-attachment { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); font-weight: 600; }




/* ---- Apps: the app store list and the workspace host (branch app-store, YUM-888).
   The store is a dense row list — name, publisher, what the app adds and reaches,
   Install/Remove/Open. The workspace host fills the content area with the app's
   sandboxed iframe under a one-line bar. Same vocabulary rules as .work-*: semantic
   classes only, tokens only, no per-app styling leaks (the iframe INTERIOR is the
   app's own content, sandboxed on an opaque origin). */
.apps-view { display: flex; flex-direction: column; gap: 2px; padding: 12px 16px; overflow-y: auto; min-height: 0; }
.apps-row { display: flex; align-items: center; gap: 12px; padding: 8px 10px; border-bottom: 1px solid var(--line-soft); }
.apps-row-main { flex: 1; min-width: 0; }
.apps-row-name { font-weight: 600; }
.apps-row-hint { font-weight: 400; color: var(--ink-soft); font-size: 12px; }
.apps-row-sub { color: var(--ink-soft); font-size: 12px; line-height: 1.4; }
.apps-row-actions { display: flex; gap: 6px; flex: none; }
.apps-workspace { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.apps-workspace-bar { display: flex; align-items: center; gap: 10px; padding: 6px 10px; border-bottom: 1px solid var(--line-soft); }
.apps-workspace-title { font-weight: 600; }
/* The store with nothing in it. Centred card rather than a line of text stranded at the
   top-left of a tall empty page — same shape as .memory-space-empty-card. */
.apps-empty { display: flex; justify-content: center; padding: 48px 16px; }
.apps-empty-card { max-width: 34em; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r); box-shadow: var(--shadow); padding: 20px 22px; }
.apps-empty-card p { margin: 0 0 14px; font-size: 13.5px; line-height: 1.5; color: var(--mute); }
.apps-workspace-frame { flex: 1; width: 100%; border: 0; min-height: 0; background: var(--paper, #fff); }


/* --------------------------------------------------- Work: New message, and the rail's ends
   The rail's first slot is where you start something, so it holds the button that starts
   one. The language you read in moved to the foot of the same column: it is a setting you
   set once, and it had the most valuable row on the screen for no reason (SB, 2026-08-21). */
.work-new {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  width: 100%;
  margin-bottom: 6px;
  padding: 6px;
  border: 1px solid var(--accent);
  border-radius: var(--r-pill);
  background: var(--accent);
  color: var(--on-accent);
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  transition: filter .12s ease;
}
.work-new:hover { filter: brightness(1.06); }
.work-new:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* The rail turns into a horizontal strip on a phone; the button should not stretch across
   it, and its label is the first thing to go when there is no room. */
.work-new-label { white-space: nowrap; }
/* Pushed to the foot of the rail's column, under whatever each source put there. */
.mail-rail > .mail-lang-row { margin-top: auto; }

.work-compose { display: flex; flex-direction: column; gap: 9px; }
.work-compose-row { display: flex; align-items: center; gap: 10px; }
.work-compose-label {
  flex: 0 0 58px;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--mute);
}
/* min-width:0 so a long recipient list shrinks the field instead of widening the card. */
.work-compose-row .mail-compose-field, .work-compose-row .mail-lang-select { flex: 1 1 auto; min-width: 0; }
/* G03 (SB 2026-09-04): the small Cc / Bcc words at the end of a recipient row (Outlook). */
.work-compose-links { display: flex; align-items: center; gap: 2px; flex: 0 0 auto; }
.work-compose-link {
  padding: 2px 6px;
  border: 0;
  background: transparent;
  color: var(--mute);
  font-family: var(--ui);
  font-size: 12.5px;
  cursor: pointer;
}
.work-compose-link:hover, .work-compose-link:focus-visible { color: var(--accent); outline: none; }
.work-compose-files { display: flex; flex-wrap: wrap; gap: 6px; }
.work-compose-file {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 6px 4px 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface-2);
  color: var(--ink-2);
  font-size: 11.5px;
}
/* Attaching the wrong file is the most likely mistake in this window, so taking one back
   is one press and not a re-open. */
.work-compose-file-drop {
  width: 20px;
  height: 20px;
  border: 0;
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--mute);
  font: inherit;
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
}
.work-compose-file-drop:hover { background: var(--surface); color: var(--ink); }
.work-compose-ask { display: flex; align-items: center; gap: 8px; }
.work-compose-ask .mail-compose-field { flex: 1 1 auto; min-width: 0; }
/* The real <input type=file> is never the control anyone sees -- browsers each draw their
   own, none of them match the design system, and none can be made to. */
.work-compose-fileinput { display: none; }

/* ---- the New message window, brought over whole ---- */

/* The compose window's own menus -- which mailbox it goes from, and which language Translate
   to is pointed at. They borrowed the rail's language select until WS-17 deleted that row. */
.work-compose-select {
  height: 32px;
  min-width: 0;
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: 13px;
}

.work-compose-select:focus { outline: 2px solid var(--accent-soft); outline-offset: -1px; }

/* min-width:0 so a long recipient list shrinks the field instead of widening the card. */
.work-compose-row .mail-compose-field, .work-compose-row .work-compose-select { flex: 1 1 auto; min-width: 0; }

/* Pushed to the far end of the row, away from the buttons that send. This one changes the
   message; those post it. */
.work-compose-translate { display: flex; align-items: center; gap: 6px; margin-left: auto; }

.work-compose-translate .work-compose-select { max-width: 150px; }


/* The recipient suggestions. The row is the positioning context, so the list hangs off the
   field itself rather than off the card and cannot drift when the card scrolls. */
.work-people-wrap { position: relative; flex: 1 1 auto; min-width: 0; }

.work-people {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  z-index: 20;
  margin: 0;
  padding: 4px;
  list-style: none;
  max-height: 232px;
  overflow-y: auto;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
  box-shadow: var(--shadow);
}

.work-person {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 6px 9px;
  border-radius: var(--r-xs);
  cursor: pointer;
  font-size: 12.5px;
}

/* One highlight, driven by both the keyboard and the pointer, so arrowing down and then
   moving the mouse does not leave two rows looking chosen. */
.work-person.is-on { background: var(--accent-soft); }

.work-person-name { color: var(--ink); font-weight: 600; white-space: nowrap; }

.work-person-addr { color: var(--mute); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }


/* Which of the two lists a name is in, said in the same voice as the rail's own labels. */
.work-people-head {
  padding: 7px 9px 3px;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--faint);
}

.work-people-head + .work-person { margin-top: 0; }


/* ------------------------------------------------------- Work: formatting the message
   The left half of a mail ribbon: what a message is written with, rather than what a
   document is laid out with. One row, wrapping, above the box it acts on. */
.work-rte { display: flex; flex-direction: column; min-width: 0; }

.work-rte-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 3px;
  padding: 5px 6px;
  border: 1px solid var(--line);
  border-bottom: 0;
  border-radius: var(--r-sm) var(--r-sm) 0 0;
  background: var(--surface-2);
}

.work-rte-btn {
  min-width: 26px;
  height: 26px;
  padding: 0 7px;
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--ink-2);
  font: inherit;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
}

.work-rte-btn:hover:enabled { background: var(--surface); border-color: var(--line); }

.work-rte-btn:disabled { opacity: .4; cursor: default; }

/* On, and unmistakably so: the same fill the app already uses for an active tab. A control
   that looks identical whether it is on or off makes you read the text to find out. */
.work-rte-btn.is-on {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}

.work-rte-btn.is-on:hover:enabled { background: var(--accent); border-color: var(--accent); }

/* Each button shows what it does, which is cheaper to read than any icon of it. */
.work-rte-btn.is-bold { font-weight: 800; }

.work-rte-btn.is-italic { font-style: italic; font-family: var(--font-body-serif, serif); }

.work-rte-btn.is-underline { text-decoration: underline; }

.work-rte-btn.is-struck { text-decoration: line-through; }

.work-rte-font, .work-rte-size {
  height: 26px;
  max-width: 120px;
  border: 1px solid var(--line);
  border-radius: var(--r-xs);
  background: var(--surface);
  color: var(--ink-2);
  font: inherit;
  font-size: 11.5px;
}

.work-rte-size { max-width: 84px; }

/* The swatch is the control. Sized like a button so the row keeps one rhythm. */
.work-rte-colour {
  width: 26px;
  height: 26px;
  padding: 2px;
  border: 1px solid var(--line);
  border-radius: var(--r-xs);
  background: var(--surface);
  cursor: pointer;
}

.work-rte-body {
  border-radius: 0 0 var(--r-sm) var(--r-sm);
  overflow-y: auto;
  text-align: left;
}

.work-rte-body:focus { outline: 2px solid var(--accent-soft); outline-offset: -2px; }

/* An empty contenteditable has no placeholder of its own; this is the whole reason the
   attribute is on the element. */
.work-rte-body:empty::before {
  content: attr(data-placeholder);
  color: var(--faint);
  pointer-events: none;
}

.work-rte-body p { margin: 0 0 10px; }

.work-rte-body ul, .work-rte-body ol { margin: 0 0 10px; padding-left: 22px; }

.work-rte-body blockquote {
  margin: 0 0 10px;
  padding-left: 12px;
  border-left: 2px solid var(--line);
  color: var(--ink-2);
}

.work-rte-body a { color: var(--accent); }

/* ---- the Yarnball pane folds away, and takes a width ---------------------------------
   Retracted it keeps no floor at all: the surface is full width, which is the point, since
   a column that permanently owns 348px is 348px the work never gets back. */
.work-frame.is-chat-shut .work-chat {
  flex-basis: 0;
  /* CR-5's own min-width: 280px on .work-chat above is a floor for the OPEN pane only —
     shut, it must still collapse to nothing, so this overrides it back to none. */
  min-width: 0;
  overflow: hidden;
  border-left: 0;
}
/* One handle, halfway down the edge, doing both jobs. Open, it stands on the pane's own
   border; shut, against the wall -- and it moves between them on the same easing as the
   pane, so the two read as one thing. */
.work-chat-tab {
  position: absolute;
  top: 50%;
  /* Clamped exactly as .work-chat clamps its own width, so a remembered width outside
     280px..60vw cannot leave the handle floating in the content off the pane's edge. */
  right: clamp(280px, var(--work-chat-w, 348px), 60vw);
  transform: translateY(-50%);
  z-index: 6;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 64px;
  padding: 0;
  border: 1px solid var(--line);
  border-right: 0;
  border-radius: var(--r-md) 0 0 var(--r-md);
  background: var(--surface);
  color: var(--mute);
  box-shadow: var(--shadow-sm);
  cursor: pointer;
  transition: right .2s ease, color .15s ease, border-color .15s ease;
}
.work-chat-tab:not(.is-open) { right: 0; }
.work-chat-tab:hover { color: var(--accent); border-color: var(--accent); }
.work-chat-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* The grip is the pane's left edge itself: a hair of width, a real cursor, and a lit line
   while it is being used or focused -- the same feel as the column grips on the left. */
.work-chat-grip {
  flex: 0 0 5px;
  align-self: stretch;
  cursor: col-resize;
  background: transparent;
  border-left: 1px solid transparent;
  touch-action: none;
}
.work-chat-grip:hover, .work-chat-grip:focus-visible { border-left-color: var(--accent); outline: none; }

/* A pane that owns half a phone is not a pane, it is the screen. */
@media (max-width: 900px) {
  /* The 60vw ceiling above is for a resizable desktop column sharing the screen with the
     rest of Work; full-screen here is the point, not a width to cap. */
  .work-chat { flex-basis: 100%; max-width: none; }
  .work-chat-tab { right: 0; }
  .work-chat-grip { display: none; }
  .work-frame:not(.is-chat-shut) > :not(.work-chat):not(.work-chat-tab) { display: none; }
}

/* H01 (SB 2026-09-04, "the buttons look so basic"): the Work page spends the bundle's own
   button kinds the way Outlook's toolbar does. The verbs on a mail, the formatting tools,
   Refresh and the paperclip are quiet until the pointer reaches them; the pressed verb is
   ink; the one control that sends is solid. Same .btn / .icbtn / .mail-compose-fmt the
   bundle ships, scoped to these rows. Tokens only, no new colour.
   The `html` prefix is specificity, nothing else: a theme file such as letterpress.css
   restyles .btn under [data-theme=...], which weighs the same as one class and loads
   after this file, so without the prefix the theme's border wins on the same button. */
html .mail-actionbar .btn,
html .mail-compose-toolbar-wrap .btn,
html .mail-list-head .btn,
html .mail-back-bar .btn {
  display: inline-flex; align-items: center; gap: 6px;
  border-color: transparent; background: transparent; box-shadow: none;
}
html .mail-actionbar .btn:hover,
html .mail-compose-toolbar-wrap .btn:hover,
html .mail-list-head .btn:hover,
html .mail-back-bar .btn:hover { background: var(--bg-2); border-color: transparent; color: var(--ink); transform: none; }
html .mail-actionbar .btn.active,
html .mail-actionbar .btn.active:hover { background: var(--ink); border-color: var(--ink); color: var(--on-ink); }
html .mail-actionbar .btn svg,
html .mail-compose-toolbar-wrap .btn svg,
html .mail-compose-actions .btn svg { flex: 0 0 auto; }
html .mail-compose-toolbar-wrap .btn svg { color: var(--accent); }
html .mail-actionbar .btn.active svg { color: inherit; }
.mail-more-chevron { transform: rotate(90deg); }
html .mail-actionbar .icbtn,
html .mail-compose-toolbar-wrap .icbtn,
html .mail-compose-actions .icbtn { border-color: transparent; background: transparent; box-shadow: none; }
html .mail-actionbar .icbtn:hover,
html .mail-compose-toolbar-wrap .icbtn:hover,
html .mail-compose-actions .icbtn:hover { background: var(--bg-2); border-color: transparent; color: var(--ink); }
/* The format bar is one strip, not six boxes. */
.mail-compose-toolbar { gap: 0; border: 1px solid var(--line); border-radius: var(--r-xs); background: var(--surface); overflow: hidden; }
.mail-compose-fmt { border: 0; border-right: 1px solid var(--line-soft); border-radius: 0; background: transparent; min-width: 28px; height: 26px; }
.mail-compose-fmt:last-child { border-right: 0; }
.mail-compose-fmt:hover { background: var(--bg-2); color: var(--ink); }
/* Send: the one solid control on its row, with its glyph. */
html .mail-compose-actions .btn.solid { display: inline-flex; align-items: center; gap: 6px; padding: 6px 14px; }

/* J (SB 2026-09-05, "like Outlook, similar features"): the mail list. A tick on every row
   (shown on hover, or always while something is ticked), hover actions on the row's right,
   the flag / importance / draft marks, the bulk bar in the list head, the Move-to menu,
   the folder that lights up under a dragged row, and the previous / next pair on the
   reading pane. Tokens only, the bundle's own controls. */
.mail-item { position: relative; }
.mail-item-check { flex: 0 0 auto; display: none; align-items: center; padding: 2px 2px 0 0; }
.mail-item-check input,
.mail-select-all input { margin: 0; width: 14px; height: 14px; accent-color: var(--accent); cursor: pointer; }
.mail-item:hover .mail-item-check,
.mail-item:focus-within .mail-item-check,
.mail-item.is-picked .mail-item-check,
.mail-list.has-picks .mail-item-check { display: flex; }
.mail-item.is-picked { background: var(--accent-soft); }
.mail-item.is-busy { opacity: 0.55; pointer-events: none; }
.mail-item-marks { display: inline-flex; align-items: center; gap: 4px; }
.mail-glyph { font-size: 13px; line-height: 1; }
.mail-glyph.is-unread { font-size: 11px; }
.mail-glyph.is-flag.is-on, .mail-flag-mark { color: var(--accent); }
.mail-importance { color: var(--accent); font-family: var(--mono); font-weight: 700; font-size: 12px; line-height: 1; }
.mail-item-draft { color: var(--accent); font-weight: 600; }
.mail-item-hover {
  position: absolute; right: 6px; top: 3px; display: none; gap: 1px; padding: 1px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-xs);
}
.mail-item:hover .mail-item-hover,
.mail-item:focus-within .mail-item-hover { display: inline-flex; }
.mail-item-hover .icbtn { width: 22px; height: 22px; border: 0; border-radius: var(--r-xs); background: transparent; color: var(--mute); box-shadow: none; }
.mail-item-hover .icbtn:hover { background: var(--bg-2); color: var(--accent); }
.mail-select-all { flex: 0 0 auto; display: flex; align-items: center; padding-right: 2px; }
.mail-bulk-bar { display: flex; align-items: center; gap: 3px; flex: 1; min-width: 0; }
.mail-bulk-count { font-family: var(--mono); font-size: 11px; color: var(--mute); margin-right: 4px; white-space: nowrap; }
.mail-bulk-bar .icbtn { width: 26px; height: 26px; }
.mail-bulk-clear { margin-left: auto; }
.mail-glyph-btn { display: inline-flex; align-items: center; justify-content: center; cursor: pointer; }
.mail-sort-chip { gap: 3px; }
.mail-sort-chip .mail-more-chevron { color: var(--mute); }
.link-context-menu-title { padding: 4px 12px 2px; font-family: var(--mono); font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--faint); }
.link-context-menu-item.is-current { color: var(--accent); }
.link-context-menu-item.is-empty { color: var(--mute); cursor: default; }
.link-context-menu-item[data-depth="1"] { padding-left: 26px; }
.link-context-menu-item[data-depth="2"] { padding-left: 38px; }
.link-context-menu-item[data-depth="3"] { padding-left: 50px; }
.link-context-menu-divider { height: 1px; margin: 4px 0; background: var(--line-soft); }
.mail-move-menu { max-height: 60vh; overflow-y: auto; }
/* The Move-to chooser's search box (Ben, 2026-10-21: one "Browse folders" entry, then choose). */
.mail-move-search {
  width: calc(100% - 16px);
  margin: 4px 8px 6px;
  padding: 6px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--ink);
  font: 400 13px/1.3 var(--ui);
}
.mail-move-search:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
.ds-tree-node.is-drop-target { outline: 2px solid var(--accent); outline-offset: -2px; background: var(--accent-soft); }
.mail-reader { position: relative; }
.mail-reader-nav { position: absolute; top: 6px; right: 12px; z-index: 2; display: inline-flex; gap: 2px; }
.mail-reader-nav .icbtn { width: 24px; height: 24px; }
.mail-reader-nav .is-up svg { transform: rotate(-90deg); }
.mail-reader-nav .is-down svg { transform: rotate(90deg); }
.mail-reader-head { padding-right: 70px; }

/* K (SB 2026-09-05, Outlook's compose): the draft-saved note, the send-later chip, and the
   message options menu (importance, receipts, send later). */
.mail-draft-status { font-family: var(--mono); font-size: 11px; color: var(--mute); white-space: nowrap; }
.mail-sendat { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 35%, transparent); background: var(--accent-soft); text-transform: none; letter-spacing: 0; font-size: 11px; }
.mail-option-check { display: inline-flex; align-items: center; gap: 4px; cursor: pointer; white-space: nowrap; }
.mail-option-check input { margin: 0; accent-color: var(--accent); }
.work-compose-row .mail-importance { margin-right: 4px; }

/* L (SB 2026-09-05, Outlook's reader and rail): initials avatars, the reading pane that
   can sit below the list or stay off, a New folder control under the tree, the
   Favourites block, and the signature editor in Settings > Work. */
.mail-avatar { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; border-radius: 50%; font-family: var(--ui); font-weight: 600; letter-spacing: 0.02em; }
.mail-avatar.is-row { width: 26px; height: 26px; margin-top: 1px; font-size: 11px; }
.mail-avatar.is-reader { width: 36px; height: 36px; font-size: 13px; }
.mail-avatar[data-tone="0"] { background: var(--bg-2); color: var(--ink); }
.mail-avatar[data-tone="1"] { background: var(--accent-soft); color: var(--accent); }
.mail-avatar[data-tone="2"] { background: var(--surface-2); color: var(--ink-2); border: 1px solid var(--line); }
/* YUM-1286: the compose chip's small disc. It has to sit inside the pill without stretching
   it, and the picture has to fill the disc exactly — a photo at its own aspect ratio inside
   a fixed circle reads as a broken avatar. */
.mail-avatar.is-chip { width: 18px; height: 18px; font-size: 9px; }
.mail-avatar img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; display: block; }
.mail-reader-head { display: flex; align-items: flex-start; gap: 10px; }
.mail-reader-head-text { flex: 1; min-width: 0; }
.mail-rail-favourites { margin-bottom: 4px; }
.mail-rail-newfolder {
  display: inline-flex; align-items: center; gap: 4px; margin: 4px 0 0 6px; padding: 3px 6px;
  border: 0; border-radius: var(--r-sm); background: transparent; color: var(--work-rail-label, var(--mute));
  font-family: var(--ui); font-size: 11px; cursor: pointer;
}
.mail-rail-newfolder:hover { background: var(--work-rail-hover); color: var(--work-rail-ink, var(--ink)); }
/* L04: the reading pane below the list - the list on top, the open mail under it. */
.work-frame[data-reading-pane="bottom"] .mail-shell { grid-template-columns: var(--work-rail-w, 190px) minmax(0, 1fr) 0; grid-template-rows: minmax(0, 42%) minmax(0, 1fr); }
.work-frame[data-reading-pane="bottom"] .mail-shell.work-assist-open { grid-template-columns: var(--work-rail-w, 190px) minmax(0, 1fr) 296px; }
.work-frame[data-reading-pane="bottom"] .mail-rail { grid-column: 1; grid-row: 1 / span 2; }
.work-frame[data-reading-pane="bottom"] .mail-list-pane { grid-column: 2; grid-row: 1; border-right: 0; border-bottom: 1px solid var(--line-soft); }
.work-frame[data-reading-pane="bottom"] .mail-reader { grid-column: 2; grid-row: 2; }
.work-frame[data-reading-pane="bottom"] .work-assist,
.work-frame[data-reading-pane="bottom"] .work-assist-tab { grid-column: 3; grid-row: 1 / span 2; }
.work-frame[data-reading-pane="bottom"] .work-list-grip { display: none; }
/* L04: the reading pane off - the list takes the room, and an opened mail takes the list's
   place with "Back to list" at its top, as on a phone. */
.work-frame[data-reading-pane="off"] .mail-shell { grid-template-columns: var(--work-rail-w, 190px) minmax(0, 1fr) 0 0; }
.work-frame[data-reading-pane="off"] .mail-shell.work-assist-open { grid-template-columns: var(--work-rail-w, 190px) minmax(0, 1fr) 0 296px; }
.work-frame[data-reading-pane="off"] .work-list-grip { display: none; }
.work-frame[data-reading-pane="off"] .mail-list-pane { border-right: 0; }
/* "A mail is open" is read from the reader itself (it has a head), not from the phone-only
   class the list carries - that class stays inside its narrow media query alone. */
.work-frame[data-reading-pane="off"] .mail-shell:has(.mail-reader-head) .mail-list-pane { display: none; }
.work-frame[data-reading-pane="off"] .mail-shell:not(:has(.mail-reader-head)) .mail-reader { display: none; }
.work-frame[data-reading-pane="off"] .mail-shell:has(.mail-reader-head) .mail-reader { grid-column: 2; }
.work-frame[data-reading-pane="off"] .mail-back-bar { display: flex; align-items: center; gap: 6px; padding: 7px 12px; border-bottom: 1px solid var(--line-soft); background: var(--surface); }
/* L05: Settings > Work > Signatures. */
.mail-sig-editor { display: flex; flex-direction: column; gap: 10px; }
.mail-sig-row { display: flex; flex-direction: column; gap: 6px; padding: 8px 10px; border: 1px solid var(--line-soft); border-radius: var(--r-sm); background: var(--surface); }
.mail-sig-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.mail-sig-name { flex: 1; min-width: 140px; }
.mail-sig-default { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; color: var(--mute); white-space: nowrap; }
.mail-sig-default input { margin: 0; accent-color: var(--accent); }
.mail-sig-text { width: 100%; resize: vertical; font-family: var(--ui); font-size: 13px; line-height: 1.4; }
.mail-sig-actions { display: flex; gap: 8px; align-items: center; }

/* J (follow-up, SB's screenshot 2026-09-05): five chips and a sort menu left the search
   box two letters wide at the default list width. The search takes the first line by
   itself; the tick, refresh, filters and sort share the line under it. */
/* Only the mail list's head (.is-mail): the Files panel and the Work > Files pane reuse
   .mail-list-head inside a .mail-list-pane of their own, and keep their one row. */
.mail-list-head.is-mail { flex-wrap: wrap; row-gap: 5px; }
.mail-list-head.is-mail .mail-search { flex: 1 1 100%; order: -1; }
.mail-list-head.is-mail .mail-bulk-bar { flex: 1 1 auto; }

/* M02 (SB 2026-09-07) made the chats panel a full-height drawer on the left edge. SB
   2026-09-24 reversed that: it is a dropdown under the Chat button (the anchored rule
   above), with New chat, Projects, search and recent chats. Only the search box style
   stays from M02. */
.chat-nav-search {
  width: 100%;
  margin: 2px 0 4px;
  padding: 6px 8px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface-2);
  color: var(--ink);
  font-family: var(--ui);
  font-size: 12.5px;
}
.chat-nav-search:focus { outline: none; border-color: var(--accent); }
.chat-nav-panel-item .chat-nav-panel-item-archive + .chat-nav-panel-item-archive { margin-left: -2px; }

/* N01 (SB 2026-09-07, "like the tree in an operating system"): the file tree draws its
   branches - a guide line down each level, a short tick into every row, no row-wide
   rules - and a row that can be dragged says so with the hand. N02/N03: a composer
   that will take the dragged file lights its edge while the file is over it. */
.file-tree-folder,
.file-tree-row { height: 24px; border-bottom: 0; }
.file-tree-indent {
  position: relative;
  margin-left: 14px;
  padding-left: 10px;
  border-left: 1px solid var(--line);
}
.file-tree-indent > .file-tree-row,
.file-tree-indent > .file-tree-branch > .file-tree-folder { position: relative; }
.file-tree-indent > .file-tree-row::before,
.file-tree-indent > .file-tree-branch > .file-tree-folder::before {
  content: '';
  position: absolute;
  left: -10px;
  top: 50%;
  width: 8px;
  border-top: 1px solid var(--line);
}
.file-tree-folder .file-tree-chevron { width: 11px; }
.file-tree-row[draggable="true"] { cursor: grab; user-select: none; -webkit-user-drag: element; }
.file-tree-row[draggable="true"]:active { cursor: grabbing; }
.mail-compose.is-drag-over,
.work-compose.is-drag-over { outline: 2px dashed var(--accent); outline-offset: -2px; background: var(--accent-soft); }

/* M03 (SB 2026-09-07): the empty chat's greeting. The three plain starter lines that sat under
   it were removed on 2026-10-13 (Ben), and their .chat-starters / .chat-starter rules with them. */
/* Critique 2026-10-07: on a wide desktop the empty chat sat top-left under a centred header,
   leaving most of the page blank. Above the phone breakpoint the greeting now sits in the middle
   of the available height and on the header's centre line. Phones keep their own bottom-anchored
   layout in mobile-ergonomics.css. Scoped with :has(.chat-hello), the greeting line, because
   Settings also renders .yarnball-main-compact. */
@media (min-width: 601px) {
  .yarnball-main-compact:has(.chat-hello) { justify-content: safe center; }
  .yarnball-main-compact > .card:has(.chat-hello) { margin-inline: auto; text-align: center; }
  .yarnball-main-compact .chat-hello { margin-top: 0; }
  /* Ben 2026-10-12: the composer stays at the bottom of the screen on an empty chat, where it
     sits once the conversation starts. Lifting it into the middle with the greeting (the
     2026-10-11 grouping) put the text box too high. Only the greeting centres. */
}

/* A02/A03 (SB 2026-09-07): a meeting transcript reads as turns - who spoke, when, what -
   and the one line above it says who is in the meeting and how the names were found. A
   speaker's name is a button: press it to give the voice a name by hand. */
.meeting-note { margin: 6px 0 8px; font-size: 12px; color: var(--mute); line-height: 1.5; }
.meeting-speaker-name,
.meeting-turn-name { padding: 0; border: 0; background: transparent; color: var(--accent); font-family: var(--ui); font-size: inherit; font-weight: 600; cursor: pointer; }
.meeting-speaker-name:hover,
.meeting-turn-name:hover { text-decoration: underline; text-underline-offset: 3px; }
.meeting-turn { padding: 6px 0; border-bottom: 1px solid var(--line-soft); }
.meeting-turn:last-child { border-bottom: 0; }
.meeting-turn-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 2px; }
.meeting-turn-name { font-size: 13px; }
.meeting-turn-time { font-family: var(--mono); font-size: 11px; color: var(--faint); }
.meeting-turn-text { margin: 0; font-size: 13.5px; line-height: 1.5; color: var(--ink-2); }
/* C (SB 2026-09-08, YUM-1067): the minutes block above the transcript - summary, decisions,
   actions, and the two things to do with them. */
.meeting-minutes { margin: 8px 0; padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface-2); }
.meeting-minutes-title { margin: 0 0 4px; font-family: var(--mono); font-size: 11px; letter-spacing: 0.07em; text-transform: uppercase; color: var(--mute); }
.meeting-minutes-summary { margin: 0 0 10px; font-size: 13.5px; line-height: 1.5; color: var(--ink); }
.meeting-minutes-list { margin: 0 0 10px; padding-left: 18px; font-size: 13px; line-height: 1.5; color: var(--ink-2); }
.meeting-minutes-actions { display: flex; align-items: center; gap: 6px; margin-top: 2px; }

/* P01 (SB 2026-09-07, "like a folder in Windows"): the places are the top of the tree -
   a place row reads a little heavier than a folder inside it. */
.file-tree-folder.is-place { font-weight: 600; color: var(--ink); }
.file-tree-folder.is-place .file-tree-icon { color: var(--accent); }

/* Q01 (SB 2026-09-07): the Tiles chat pane takes files - dropped from the Files tree or the
   device, or picked with the clip. The chips are the main composer's. */
.yb-is-drop { outline: 2px dashed var(--accent); outline-offset: -4px; }
.yb-b-files { display: flex; flex-wrap: wrap; gap: 6px; padding: 8px 12px 0; }

/* Work rail (SB 2026-09-08): Word, Excel and PowerPoint are rows of the WORK source list,
   one each, drawn as .work-source like Mail and Calendar. Only the icon needs a rule of its
   own: it is an inline svg from chat.js's file-type set, not a YarnballIcon. */
.work-app-icon { display: inline-flex; flex: 0 0 auto; line-height: 0; }

/* The Files window's edges (SB 2026-09-08, "resize it, both height and width"): eight
   invisible handles, one per side and corner, over the window's border. The bottom-right
   one draws the two short diagonal lines every desktop uses to say "resize here". */
.files-win-edge { position: absolute; z-index: 2; touch-action: none; }
.files-win-edge-n { top: -3px; left: 10px; right: 10px; height: 7px; cursor: ns-resize; }
.files-win-edge-s { bottom: -3px; left: 10px; right: 10px; height: 7px; cursor: ns-resize; }
.files-win-edge-e { right: -3px; top: 10px; bottom: 10px; width: 7px; cursor: ew-resize; }
.files-win-edge-w { left: -3px; top: 10px; bottom: 10px; width: 7px; cursor: ew-resize; }
.files-win-edge-ne { top: -3px; right: -3px; width: 14px; height: 14px; cursor: nesw-resize; }
.files-win-edge-nw { top: -3px; left: -3px; width: 14px; height: 14px; cursor: nwse-resize; }
.files-win-edge-sw { bottom: -3px; left: -3px; width: 14px; height: 14px; cursor: nesw-resize; }
.files-win-edge-se {
  bottom: 0; right: 0; width: 18px; height: 18px; cursor: nwse-resize;
  background: linear-gradient(135deg, transparent 0 52%, var(--mute) 52% 58%, transparent 58% 70%, var(--mute) 70% 76%, transparent 76%);
  border-bottom-right-radius: var(--r-sm);
}

/* The editor in the Work reader (Files pane, mail attachment popup, a just-made Office file).
   DocsAPI REPLACES the .canvas-onlyoffice-mount div with its iframe, so the iframe is a bare
   child of .canvas-onlyoffice-host and canvas.css's `.canvas-onlyoffice-mount > iframe` never
   matches it; without a rule of its own it is a default 150px-tall iframe under a 650px host
   (seen 2026-09-08 on the "+ New" popup: ribbon, then a cut-off page). Same fix the canvas
   viewer carries for .canvas-viewer-office-editor above. */
.work-preview .canvas-onlyoffice-host iframe,
.work-preview .canvas-onlyoffice-wrap iframe { flex: 1 1 auto; width: 100% !important; height: 100% !important; min-height: 0; border: 0; }

/* ---------------------------------------------------------------------------
   Round-8 wishlist surfaces, carried into the stylesheet this lineage loads.

   The feature branches wrote these rules into _cds/1.0.4/, which the dev line
   serves. This line's index.html loads _cds/1.0.3/, so the port carried the
   markup and left the styling behind: every class below was in the document
   and defined nowhere. Same rules, same tokens, moved to the sheet that is
   actually on the page.

   YUM-1323/1324 calendar, YUM-1328 files, YUM-1331 New menu, YUM-1336 hover
   actions, plus the four shell classes (topnav-cluster, yb-edge-clip,
   yb-tab-badge, yb-is-dot) the Settings port left in the same state.
   --------------------------------------------------------------------------- */

/* ------------------------------------------------------------------------ YUM-1323
   The Outlook calendar views. A day's column is a canvas 24 hours tall; a meeting is drawn
   where it falls and as tall as it lasts, and two meetings that overlap share the column
   side by side. The four numbers a block needs — top, height, left, width — are handed in as
   custom properties (top/height/left/width are percentages); every colour, border, radius
   and type below comes from the theme's tokens, so the grid restyles with the theme and no
   other stylesheet exists. This is the design system's own extension file, the same one the
   rest of Work wears. */
/* The third column is its own query container (YUM-1123's rule, stated here too so the
   narrow calendar works in the file index.html actually loads — see the PR note). The
   column is resizable, so the calendar's own width is the question, not the window's. */
.work-split { container-type: inline-size; }

/* p19 item 1: "smaller and make it exactly like in MS outlook web" — the toolbar drops to
   Outlook web's own height (~32px), the nav buttons lose their pill padding in favour of a
   tight icon-sized square, and the date label reads as a plain small heading, not a button
   dressed as a headline. */
.work-cal-bar { display: flex; align-items: center; gap: 5px; flex: 0 0 auto; padding: 3px 6px; min-height: 32px; border-bottom: 1px solid var(--line-soft); background: var(--surface-2); }
.work-cal-nav { display: flex; align-items: center; gap: 2px; }
.work-cal-btn { padding: 2px 8px; height: 24px; font-family: var(--ui); font-size: 11.5px; color: var(--ink-2); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-xs); cursor: pointer; }
.work-cal-btn:hover { background: var(--surface-2); border-color: var(--accent); }
.work-cal-arrow { width: 24px; padding: 0; display: inline-flex; align-items: center; justify-content: center; }
.work-cal-period { display: inline-flex; align-items: center; gap: 3px; padding: 2px 5px; font-family: var(--ui); font-size: 12px; font-weight: 600; color: var(--ink); background: transparent; border: 0; border-radius: var(--r-xs); cursor: pointer; }
.work-cal-period:hover { background: var(--surface); }
.work-cal-period-caret { color: var(--work-dim); }
.work-cal-refresh.icbtn,
.work-cal-bar .work-cal-refresh.icbtn { width: 24px; height: 24px; box-shadow: none; margin-left: 2px; }
.work-cal-status { padding: 6px 8px; font-family: var(--ui); font-size: 12px; color: var(--mute); }
.work-cal-status.is-error { color: var(--accent); }
.work-cal-popover { padding: 10px; }
.work-cal-picker { min-width: 208px; }
.work-cal-picker-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 6px; }
.work-cal-picker-title { font-family: var(--ui); font-size: 12.5px; font-weight: 600; color: var(--ink); }
.work-cal-picker-grid { display: grid; grid-template-columns: repeat(7, 26px); gap: 2px; }
.work-cal-picker-dow { font-family: var(--mono); font-size: 11px; text-align: center; color: var(--work-dim); }
.work-cal-picker-day { width: 26px; height: 24px; font-family: var(--ui); font-size: 11.5px; color: var(--ink-2); background: transparent; border: 1px solid transparent; border-radius: var(--r-xs); cursor: pointer; }
.work-cal-picker-day:hover { background: var(--surface-2); }
.work-cal-picker-day.is-out { color: var(--faint); }
.work-cal-picker-day.is-today { border-color: var(--accent); }
.work-cal-picker-day.is-sel { background: var(--accent); color: var(--on-accent); }

.work-tg { display: flex; flex-direction: column; flex: 1 1 55%; min-height: 0; background: var(--surface); }
.work-tg-alldayrow { display: flex; align-items: stretch; min-height: 24px; border-bottom: 1px solid var(--line); background: var(--surface-2); }
.work-tg-alldaylabel { font-family: var(--mono); font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: var(--work-dim); }
.work-tg-alldaycell { display: flex; flex: 1 1 0; flex-direction: column; gap: 2px; min-width: 0; padding: 3px 4px; border-left: 1px solid var(--line-soft); }
.work-tg-alldaycell.is-today { background: var(--accent-soft); }
.work-tg-scroll { flex: 1 1 auto; min-height: 0; overflow-x: hidden; overflow-y: auto; }
.work-tg-cols { display: flex; align-items: stretch; }
.work-tg-gutter { display: flex; flex: 0 0 54px; flex-direction: column; width: 54px; }
.work-tg-gutterhead { height: 28px; border-bottom: 1px solid var(--line-soft); }
.work-tg-hour { position: relative; height: 44px; }
.work-tg-hourlabel { position: absolute; top: -6px; right: 6px; font-family: var(--mono); font-size: 11px; color: var(--work-dim); }
.work-tg-col { display: flex; flex: 1 1 0; flex-direction: column; min-width: 0; border-left: 1px solid var(--line-soft); }
.work-tg-colhead { position: sticky; top: 0; z-index: 3; display: flex; align-items: baseline; gap: 4px; height: 28px; padding: 0 6px; background: var(--surface-2); border-bottom: 1px solid var(--line-soft); }
.work-tg-col.is-today .work-tg-colhead { background: var(--accent-soft); }
.work-tg-dayname { font-family: var(--mono); font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: var(--work-dim); }
.work-tg-daynum { font-family: var(--ui); font-size: 12px; color: var(--ink-2); }
.work-tg-canvas { position: relative; height: 1056px; }
.work-tg-hourline { height: 44px; border-top: 1px solid var(--line-soft); }
.work-tg-hourline:first-child { border-top: 0; }
.work-tg-now { position: absolute; left: 0; right: 0; top: var(--tg-top); z-index: 2; height: 0; border-top: 2px solid var(--accent); pointer-events: none; }
.work-tg-now::before { content: ''; position: absolute; left: -3px; top: -4px; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); }
.work-tg-ev { position: absolute; top: var(--tg-top); left: var(--tg-l); display: flex; flex-direction: column; gap: 1px; width: var(--tg-w); height: var(--tg-h); overflow: hidden; padding: 2px 5px; text-align: left; background: var(--surface); border: 1px solid var(--line-soft); border-left: 2px solid var(--line); border-radius: var(--r-xs); cursor: pointer; }
.work-tg-ev:hover { background: var(--surface-2); }
.work-tg-ev.active { border-color: var(--accent); background: var(--accent-soft); }
.work-tg-ev.is-mine { border-left-color: var(--ink-2); }
.work-tg-ev.needs-answer { border-left-color: var(--accent); }
.work-tg-ev.is-cancelled { opacity: 0.5; border-left-color: var(--line); background: transparent; }
.work-tg-ev.is-cancelled .work-tg-ev-title { text-decoration: line-through; color: var(--mute); }
.work-tg-ev-time { font-family: var(--mono); font-size: 11px; color: var(--work-dim); }
.work-tg-ev-title { font-family: var(--ui); font-size: 11px; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.work-tg-ev-where { font-family: var(--ui); font-size: 11px; color: var(--work-mute); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.work-cal-col.is-out { background: var(--surface-2); }
.work-cal-col.is-out .work-cal-col-date { color: var(--faint); }

.work-tg-col-allday { display: none; flex-direction: column; gap: 4px; padding: 4px 6px 0; }

/* Narrow: seven columns of hours do not fit in ~600px, so the days become full-width agenda
   rows — the presentation YUM-1123 gave the week — and the hour rules step aside. The query
   container is .work-split (set above), so it is the calendar's own width that decides. */
@container (max-width: 620px) {
  .work-tg-gutter { display: none; }
  .work-tg-cols { flex-direction: column; }
  .work-tg-col { flex: 0 0 auto; border-left: 0; border-bottom: 1px solid var(--line-soft); }
  .work-tg-canvas { height: auto; }
  .work-tg-hourline, .work-tg-now { display: none; }
  .work-tg-ev { position: static; left: auto; width: auto; height: auto; margin: 3px 6px; }
  .work-cal-week { grid-template-columns: minmax(0, 1fr); }
  .work-cal-week .work-cal-col { border-right: 0; border-bottom: 1px solid var(--line-soft); }
  /* YUM-1472: the shared all-day row is seven slivers when the days stack, so each day shows
     its own all-day items instead. */
  .work-tg-alldayrow { display: none; }
  .work-tg-col-allday { display: flex; }
}


/* ------------------------------------------------------------------------ YUM-1324
   Creating and changing a meeting. The grid's empty canvas takes a press or a drag, a
   meeting takes a drag to move and an edge drag to resize, and the editor is a plain form of
   labelled rows — same tokens as everything else. */
.work-tg-canvas.is-editable { cursor: crosshair; }
.work-cal-new { display: inline-flex; align-items: center; gap: 5px; margin-left: auto; }

/* p19 item 3: "[…] should be in icons" — Day/Work week/Week/Month as a compact row of icon
   buttons in the rail, instead of the text list Ben boxed. */
.work-cal-view-icons { display: flex; align-items: center; gap: 2px; padding: 4px 2px 8px; }
.work-cal-view-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 26px; padding: 0;
  border: 1px solid transparent; border-radius: var(--r-xs);
  background: transparent; color: var(--work-rail-ink); cursor: pointer;
}
.work-cal-view-icon:hover { background: var(--work-rail-hover); }
.work-cal-view-icon.active { background: var(--work-rail-active-bg); color: var(--work-rail-active-ink); border-color: var(--line); }
.work-tg-ghost {
  position: absolute; left: 2px; right: 2px; top: var(--tg-top); height: var(--tg-h);
  border: 1px dashed var(--accent); border-radius: var(--r-xs); background: var(--accent-soft);
  opacity: .8; pointer-events: none; z-index: 4;
}

.work-ev-editor { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; }
.work-ev-editor-title { font-family: var(--ui); font-size: 13px; font-weight: 600; color: var(--ink); }
.work-ev-field { display: flex; flex-direction: column; gap: 3px; font-family: var(--mono); font-size: 11px; letter-spacing: .05em; text-transform: uppercase; color: var(--work-dim); }
.work-ev-field > span { padding-left: 1px; }
.work-ev-input { padding: 5px 7px; font-family: var(--ui); font-size: 12.5px; color: var(--ink); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-xs); }
.work-ev-input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.work-ev-notes { min-height: 64px; resize: vertical; }
.work-ev-check { display: flex; align-items: center; gap: 6px; font-family: var(--ui); font-size: 12.5px; color: var(--ink-2); }
.work-ev-more { align-self: flex-start; padding: 0; font-family: var(--ui); font-size: 12px; color: var(--accent); background: transparent; border: 0; cursor: pointer; }
.work-ev-more:hover { text-decoration: underline; }
.work-ev-actions { display: flex; gap: 6px; margin-top: 2px; }
.work-ev-status { font-family: var(--ui); font-size: 12px; color: var(--ink-2); }
.work-ev-status.is-error { color: var(--accent); }

.work-cal-toast { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; padding: 6px 10px; border-top: 1px solid var(--line); background: var(--surface-2); font-family: var(--ui); font-size: 12px; color: var(--ink-2); }
.work-cal-toast-text { flex: 1 1 auto; min-width: 0; }
.work-cal-undo { padding: 2px 8px; font-family: var(--ui); font-size: 12px; color: var(--accent); background: transparent; border: 1px solid var(--line); border-radius: var(--r-xs); cursor: pointer; }
.work-cal-undo:hover { border-color: var(--accent); background: var(--accent-soft); }
.work-cal-toast-x { padding: 0 4px; font-size: 14px; line-height: 1; color: var(--work-dim); background: transparent; border: 0; cursor: pointer; }


/* ------------------------------------------------------------------------ YUM-1328
   The approved Files layout (docs/design/files-ui-20260921/README.md, Variant A): grouped
   places, a breadcrumb, a row menu beside right-click, and a details pane that says what a
   place allows in words. Design-system tokens only; no new colours. */
.file-tree-group {
  padding: 8px 8px 2px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--work-dim);
}
.file-tree-kebab {
  flex: 0 0 auto;
  width: 22px;
  margin-left: auto;
  padding: 0;
  font-size: 15px;
  line-height: 1;
  color: var(--work-dim);
  background: transparent;
  border: 0;
  border-radius: var(--r-xs);
  cursor: pointer;
  visibility: hidden;
}
.file-tree-row:hover .file-tree-kebab,
.file-tree-folder:hover .file-tree-kebab,
.file-tree-kebab:focus-visible { visibility: visible; }
.file-tree-kebab:hover { background: var(--surface-2); color: var(--ink); }
.yb-context-item.is-off { color: var(--faint); cursor: not-allowed; }
.yb-context-item.is-off:hover { background: transparent; }

.work-details { flex: 0 0 auto; padding: 8px 12px 12px; border-top: 1px solid var(--line-soft); background: var(--surface-2); }
.work-details-title { font-family: var(--mono); font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: var(--work-dim); margin-bottom: 6px; }
.work-details-grid { display: grid; grid-template-columns: 76px minmax(0, 1fr); gap: 3px 8px; font-family: var(--ui); font-size: 12.5px; }
.work-details-k { color: var(--work-dim); }
.work-details-v { color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; }
.work-details-rule { margin: 8px 0 0; font-family: var(--ui); font-size: 12px; color: var(--mute); }


/* --------------------------------------------------- YUM-1331: the one New menu
   In Outlook, New is the one place you go to start anything. The Work rail and the Files
   window mount the same component: the main part of the split button does the page's
   obvious thing, the caret opens the same grouped list on both, and a page with no built
   primary shows the caret alone. */
.new-menu-split {
  display: flex;
  align-items: stretch;
  gap: 4px;
  width: 100%;
  margin-bottom: 8px;
}

/* The Files window's head is a row of small controls, not the rail's full-width column. */
.new-menu-split.is-compact { width: auto; margin-bottom: 0; }

.new-menu-split .new-menu-main { flex: 1 1 auto; margin-bottom: 0; }

.new-menu-split .new-menu-caret {
  flex: 0 0 auto;
  width: auto;
  margin-bottom: 0;
  padding: 8px 7px;
}

.new-menu-split.is-compact .new-menu-main,
.new-menu-split.is-compact .new-menu-caret { padding: 6px; }

/* Never overflow. The primary's label is `nowrap`, so on its own it would push the caret —
   and the toolbar row the split sits in — past the edge of the viewport. Give the label the
   shrink floor instead: it gives up its letters before anything leaves the screen. */
.new-menu-split { min-width: 0; }
.new-menu-split .new-menu-main { min-width: 0; }
.new-menu-split .new-menu-main-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; }

/* Redline 2026-09-26 (p13: "this compress[es], only show[s] icons -", verify p25): the split
   trigger keeps its label while the window has room for it, and collapses to the icon alone
   below the app's one narrow breakpoint. Both mount points — the Work rail and the Files
   window's head — pass compact, so the label's visibility is decided here rather than per
   surface: above 900px a compact trigger reads "New mail" / "Upload" again, and at 900px and
   below the label goes. That is also what stops the trigger, and the row it sits in, running
   past the right edge of a phone screen — the overflow the verification found at 750px. The
   words are never the only carrier of the meaning: the button keeps its title and aria-label
   (NEW_MENU_PRIMARY_TITLE), so the icon-only state still says what it does on hover. */
@media (min-width: 901px) {
  .new-menu-split.is-compact .new-menu-main-label { display: inline; }
}
@media (max-width: 900px) {
  .new-menu-split,
  .new-menu-split.is-compact { width: auto; margin-bottom: 0; }
  .new-menu-split .new-menu-main-label,
  .new-menu-split.is-compact .new-menu-main-label { display: none; }
}

/* The caret points down at what it opens; the design system's chevron points right. */
.new-menu-caret svg { transform: rotate(90deg); }

.new-menu-item { gap: 8px; }

.new-menu-item-icon { flex: 0 0 auto; color: var(--mute); }

.new-menu-item-label { white-space: nowrap; }
.new-menu-list .new-menu-item-label { display: none; }

/* Nothing to start on this page: the caret is the whole control. */
.new-menu-split.is-caret-only { width: auto; }

/* The one New list: wider than a link menu so a grouped label and an icon fit on one line. */
.new-menu-list { min-width: 232px; }

/* The main part of the split is the page's primary action; the design-system gate requires
   a class app code names to be defined unscoped, so this is not nested under the split. */
.new-menu-main { flex: 1 1 auto; margin-bottom: 0; }
.new-menu-main-label { white-space: nowrap; }

/* YUM-1336: Outlook's row actions. Right-aligned over the date, one size and spacing, shown
   on hover, on the focused row (the row is a button, so :focus-within brings these into the
   tab order) and on the selected row — which is what touch has instead of hover. The strip is
   absolutely placed, so the row's height never changes when it appears. */
  position: absolute; right: 4px; top: 2px; display: none; gap: 2px; padding: 1px;
.mail-item:focus-within .mail-item-hover,
.mail-item.active .mail-item-hover { display: inline-flex; }
/* The actions replace the date while they are showing, as Outlook's do. */
.mail-item:hover .mail-item-time,
.mail-item:focus-within .mail-item-time,
.mail-item.active .mail-item-time { visibility: hidden; }
.mail-item-hover .icbtn { width: 24px; height: 24px; border: 0; border-radius: var(--r-xs); background: transparent; color: var(--mute); box-shadow: none; }
.mail-item-hover .icbtn:hover { background: var(--bg-2); color: var(--ink); }
/* Intent colour: delete is destructive, and a flag that is set stays red. */
.mail-item-hover .icbtn.is-danger:hover { background: var(--danger-soft); color: var(--danger); }
/* YUM-1336: the same destructive action appears on the calendar's rows, so the class is
   declared unscoped as well. Scoped-only left it undefined everywhere but mail. */
.icbtn.is-danger:hover { background: var(--danger-soft); color: var(--danger); }
.mail-item-hover .icbtn.is-on { color: var(--danger); }


/* YUM-1330: the Work mail command bar — one height per role, one icon size.
   Audited from source and the 2026-09-21 before-shots (the dev render is throttled right now;
   see the ticket comment). Before this the same line carried 26px DS icon buttons, bare
   mail-glyph buttons and filter chips of their own height beside a 28px search box, so no two
   controls agreed. Heights come from the token scale, not a new number invented here. */
.mail-list-head.is-mail > .icbtn,
.mail-list-head.is-mail > .mail-glyph-btn,
.mail-list-head.is-mail > .mail-filter-chip,
.mail-list-head.is-mail > .mail-search,
.mail-list-head.is-mail > .mail-select-all { height: 30px; }
.mail-list-head.is-mail > .icbtn,
.mail-list-head.is-mail > .mail-glyph-btn { width: 30px; min-width: 30px; }
.mail-list-head.is-mail > .mail-filter-chip { min-width: 30px; padding: 0 8px; }
.mail-list-head.is-mail .mail-glyph { font-size: 14px; line-height: 1; }
.mail-list-head.is-mail .mail-bulk-bar .icbtn,
.mail-list-head.is-mail .mail-bulk-bar .mail-glyph-btn { height: 30px; width: 30px; }

/* YUM-1330: the open-mail command bar, one height per role. Measured before this on
   2026-09-22: the bar's buttons were 20px and 22px, and its icons 11px and 12px, on one row. */
.mail-actionbar .btn,
.mail-actionbar .mail-actionbar-chip { height: 30px; }
/* Redline p16 item 2 ("compress"): tightens layouts.css's base .mail-actionbar row (and the
   summary strip right under it) further — the loop in the annotation circles the toolbar,
   the AI suggestion chips AND the one-line summary as one block to shrink. */
.mail-actionbar { gap: 3px; padding: 3px 10px; }
.work-advice { padding: 4px 12px; gap: 6px; }
/* letterpress-app.css:477 pads the head's bottom back out to 8px under
   [data-theme="letterpress"] (specificity 0,2,0 beats this file's plain .mail-reader-head),
   which quietly undoes half the compression above on the theme the app actually ships
   with. :root wins it back without editing the theme file. */
:root [data-theme="letterpress"] .mail-reader-head { padding-bottom: 4px; }


/* Shell classes the Settings port (YUM-1305) left defined only in 1.0.4. */
.topnav-cluster { display: inline-flex; align-items: center; gap: 8px; }
.topnav-cluster + .topnav-cluster { padding-left: 16px; border-left: 1px solid var(--line); }
.yb-edge-clip{
  flex:1 1 auto;min-width:0;min-height:0;overflow:hidden;display:flex;align-items:stretch
}
.yb-tab-badge{
  position:absolute;top:4px;inset-inline-end:1px;
  min-width:15px;height:15px;padding:0 3px;border-radius:var(--r-pill);
  background:var(--accent);color:var(--on-accent);
  font-family:var(--mono);font-size: 11px;font-weight:700;line-height:15px;text-align:center;
  box-shadow:0 0 0 2px var(--surface);pointer-events:none
}
.yb-tab-badge.yb-is-dot{
  top:6px;inset-inline-end:5px;
  min-width:0;width:8px;height:8px;padding:0;border-radius:50%
}

/* ============ YUM-1397: tokens + invite card + mail header/toolbar ============ */
:root{--danger:#B3260E}

/* ============ YUM-1397: mail reader header (H1/H2) ============ */
/* Outlook's shape: round avatar, subject with the action flag chip beside it, the
   sender's name bold with the address lighter after it, To:/Cc: on their own lines,
   the short date and the header's reply icons on the right. No monospace, no seconds. */
.mail-reader-head { align-items: flex-start; }
/* Fix 4: name and address sit on ONE line that truncates with an ellipsis, and the
   head's own icons and date never give up their room — the text block shrinks instead. */
.mail-reader-head-text { min-width: 0; }
.mail-reader-head-actions, .mail-reader-date { flex: none; }
.mail-reader-from { display: flex; align-items: baseline; gap: 6px; min-width: 0; max-width: 100%; overflow: hidden; }
.mail-reader-from-name { color: var(--ink); font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 0 1 auto; }
.mail-reader-from-addr { color: var(--mute); font-weight: 400; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1 1 auto; min-width: 0; }
.mail-reader-meta { display: flex; flex-direction: column; align-items: flex-start; gap: 1px; font-size: 12px; color: var(--mute); font-family: var(--ui); }
.mail-reader-to { color: var(--mute); }
.mail-reader-head-actions { display: flex; align-items: center; gap: 2px; margin-left: auto; }
.mail-reader-date { color: var(--mute); font-size: 12px; white-space: nowrap; margin-left: 12px; }
.mail-reader-flag { flex: none; margin-left: 8px; }

/* ============ YUM-1397: the invite card (M2/M3/M5, C1) ============ */
/* Outlook's order: title, when + answer, day preview, location, organiser, chat.
   Tokens only, so the card follows the person's theme and colour option. */
.invite-card { border: 1px solid var(--line); border-left: 3px solid var(--accent); background: var(--surface); border-radius: var(--r-xs); padding: 12px 14px; margin: 10px 0; display: flex; flex-direction: column; gap: 9px; }
.invite-card.is-cancelled { border-left-color: var(--danger); }
.invite-card-title { font-size: 15px; font-weight: 700; color: var(--ink); display: flex; align-items: center; gap: 8px; }
.invite-cancelled-chip { flex: none; font-size: 11px; font-weight: 700; text-transform: none; color: var(--danger); border: 1px solid var(--danger); border-radius: var(--r-xs); padding: 1px 6px; }
.invite-card-row { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--ink-2); }
.invite-card-row.is-when { justify-content: flex-start; gap: 12px; flex-wrap: wrap; }
.invite-ico { color: var(--mute); flex: none; }
.invite-when { display: inline-flex; align-items: center; gap: 8px; color: var(--ink); }
.invite-actions { display: inline-flex; align-items: center; gap: 6px; margin-left: auto; }
.invite-answered { display: inline-flex; align-items: center; gap: 6px; color: var(--ink); font-weight: 600; }
.invite-card-status { font-size: 12px; color: var(--mute); }
.invite-card-status.is-error { color: var(--danger); }
.invite-location { color: var(--ink-2); }
.invite-attendee-link { appearance: none; background: none; border: 0; padding: 0; color: var(--accent); cursor: pointer; font: inherit; }
.invite-attendee-link:hover { text-decoration: underline; }
.invite-attendee { display: flex; align-items: center; gap: 8px; padding: 4px 6px; font-size: 12.5px; }
.invite-attendee-name { color: var(--ink); }
.invite-attendee-response { margin-left: auto; color: var(--mute); }
.invite-menu.is-attendees { max-height: 300px; overflow: auto; display: flex; flex-direction: column; min-width: 260px; }
/* Row 2: the day strip — the reader's own calendar, 2h before to 2h after. */
.invite-daystrip { position: relative; height: 30px; width: 100%; border: 1px solid var(--line); border-radius: var(--r-xs); background: var(--bg-2); overflow: hidden; }
.invite-daystrip-block { position: absolute; top: 4px; bottom: 4px; left: var(--ev-left, 0%); width: var(--ev-width, 10%); background: var(--surface-2); border: 1px solid var(--line); border-radius: 3px; color: var(--mute); font-size: 11px; line-height: 20px; padding: 0 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.invite-daystrip-block.is-invite { background: var(--accent-soft); border-color: var(--accent); color: var(--ink); font-weight: 600; z-index: 1; }
.invite-daystrip-tick { position: absolute; left: 4px; top: 50%; transform: translateY(-50%); font-size: 11px; color: var(--faint); background: var(--bg-2); padding: 0 2px; z-index: 2; }
.invite-daystrip-tick.is-end { left: auto; right: 4px; }
.invite-conflict { font-size: 11.5px; color: var(--mute); }
.invite-conflict.is-conflict { color: var(--danger); }
/* Somebody's answer to a meeting: the small line, no card, no buttons. */
.invite-answer-line { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--mute); border: 1px solid var(--line); border-radius: var(--r-xs); background: var(--surface); padding: 8px 12px; margin: 8px 0; }
.invite-answer-line svg { color: var(--accent); }

/* ============ YUM-1397: message list (H4, M4) ============ */
/* Focused / Other: real tabs with an underline, like Outlook. */
.mail-focus-tabs { display: inline-flex; align-items: stretch; align-self: stretch; margin: 0 2px; }
.mail-focus-tab { appearance: none; background: none; border: 0; border-bottom: 2px solid transparent; padding: 0 10px; font: inherit; font-size: 13px; color: var(--mute); cursor: pointer; }
.mail-focus-tab:hover { color: var(--ink); }
.mail-focus-tab.is-on { color: var(--ink); font-weight: 600; border-bottom-color: var(--accent); }
/* The invite chip under a row's preview. */
.mail-item-invite { display: inline-flex; align-items: center; gap: 8px; margin-top: 3px; padding: 3px 6px 3px 4px; border: 1px solid var(--line); border-radius: var(--r-xs); background: var(--surface); max-width: 100%; }
.mail-item-invite-when { font-size: 11.5px; color: var(--ink-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mail-item-invite .btn { height: 28px; flex: none; }
/* H4: the row's translate control stops being a boxed icon on every row — quiet until
   used. It keeps its title and aria-label, so nothing is unreachable. */
.mail-item-translate { background: transparent; border-color: transparent; box-shadow: none; opacity: 0.55; }
.mail-item-translate.is-on, .mail-item-translate:hover { opacity: 1; background: transparent; border-color: transparent; }
/* One size per line of the row, and the same line heights everywhere (H4). */
.mail-item-from { font-size: 13px; }
.mail-item-subject { font-size: 13px; }
.mail-item-preview { font-size: 12.5px; color: var(--mute); }
/* === YUM-1396 Part 1: the To do page, like Outlook To Do (public/app/todo-page.js). ===
   One top-level screen: left rail of lists, header with title + Grid/List + Sort/Group,
   an add bar with due/reminder/repeat, grouped rows with drag order, and a rail footer
   with the Outlook To Do sync state. Tokens only — light/dark and the colour option come
   from the theme variables, never from this file. No Needs you on test (ticket L4). */
/* YUM-1441 (T1b/T1g): To do is a Work source. Its lists nest under "To do" in Work's rail
   the way Mail's folders nest under Mail (same tree line, same row metrics, same rail
   tokens), and the pane's one column spans the list and reading tracks of .mail-shell. */
.mail-shell > .todo-main { grid-column: 2 / 4; }
.mail-shell:has(> .todo-main) .work-list-grip { display: none; }
.todo-lists{display:flex;flex-direction:column;gap:1px;padding:4px 0 6px 14px;border-left:2px solid var(--line-soft);margin:2px 0 4px 10px}
.todo-rail-item{display:flex;align-items:center;gap:8px;width:100%;padding:4px 8px;border:0;border-radius:var(--r-sm);background:transparent;color:var(--work-rail-ink);font-family:var(--ui);font-size:13px;cursor:pointer;text-align:left}
.todo-rail-item:hover{background:var(--work-rail-hover)}
.todo-rail-item.is-active{background:var(--work-rail-active-bg);color:var(--work-rail-active-ink);box-shadow:inset 3px 0 0 var(--work-rail-active-bar, transparent);font-weight:600}
.todo-rail-ic{display:inline-flex;flex:0 0 auto;color:var(--work-rail-mute)}
.todo-rail-item.is-active .todo-rail-ic{color:var(--accent)}
.todo-rail-label{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.todo-rail-count{flex:0 0 auto;font-family:var(--mono);font-size: 11px;color:var(--work-rail-mute);font-variant-numeric:tabular-nums}
.todo-rail-item.is-active .todo-rail-count{color:var(--ink)}
.todo-rail-newlist{display:flex;align-items:center;gap:8px;width:100%;padding:4px 8px;border:0;border-radius:var(--r-sm);background:transparent;color:var(--accent);font-family:var(--ui);font-size:13px;font-weight:600;cursor:pointer;text-align:left}
.todo-rail-newlist:hover{background:var(--work-rail-hover)}

/* sync footer (N8), pinned to the foot of Work's rail */
.todo-sync{display:flex;align-items:center;flex-wrap:wrap;gap:6px 8px;margin-top:auto;padding:10px 6px 4px;border-top:1px solid var(--line-soft);font-size:12px;color:var(--work-rail-mute)}
.todo-sync-ic{display:inline-flex;flex:0 0 auto}
.todo-sync-label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.todo-sync-label{white-space:normal;flex:1 1 120px}
.todo-sync.is-error .todo-sync-label,.todo-sync.is-error .todo-sync-ic{color:var(--danger)}
.todo-sync .btn{flex:0 0 auto;padding:4px 10px;font-size:12px}

/* main column */
.todo-main{position:relative;min-width:0;min-height:0;display:flex;flex-direction:column;background:var(--surface)}
/* p24 item 1 "make those icons close": the header toolbar (title, Grid/List, search, Sort,
   Group) reads as one tight bar, not a row of loosely spaced controls. */
.todo-head{display:flex;align-items:center;gap:8px;padding:9px 16px 8px;border-bottom:1px solid var(--line-soft);flex-wrap:wrap}
.todo-head-title{display:flex;align-items:center;gap:6px;min-width:0}
.todo-head-ic{display:inline-flex;color:var(--accent)}
.todo-head-text{margin:0;font-size:17px;font-weight:700;letter-spacing:-.01em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.todo-head-btn{display:inline-flex;align-items:center;gap:5px;padding:4px 7px;border:0;border-radius:var(--r-xs);background:transparent;color:var(--ink-2);font:inherit;font-size:12.5px;cursor:pointer}
.todo-head-btn:hover{background:var(--surface-2)}
.todo-head-modes{display:inline-flex;border:1px solid var(--line);border-radius:var(--r-xs);overflow:hidden}
.todo-mode{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;border:0;background:transparent;color:var(--mute);font:inherit;font-size:12px;cursor:pointer}
.todo-mode+.todo-mode{border-left:1px solid var(--line)}
.todo-mode.is-active{background:var(--accent-soft);color:var(--accent);font-weight:600}
.todo-head-search{display:flex;align-items:center;gap:5px;flex:1 1 140px;max-width:280px;min-width:120px;padding:4px 9px;border:1px solid var(--line);border-radius:var(--r-pill);background:var(--surface)}
.todo-head-search:focus-within{border-color:var(--accent)}
.todo-head-search-ic{display:inline-flex;color:var(--mute)}
.todo-search{flex:1 1 auto;min-width:0;border:0;background:transparent;color:var(--ink);font:inherit;font-size:12.5px;outline:none}
.todo-search::placeholder{color:var(--faint)}
.todo-search-clear{display:inline-flex;border:0;background:transparent;color:var(--mute);cursor:pointer;padding:2px}
.todo-head-right{display:flex;align-items:center;gap:2px;margin-left:auto}

/* add bar */
.todo-add{position:relative;display:flex;align-items:center;gap:8px;margin:14px 18px 4px;padding:8px 10px;border:1px solid var(--line);border-radius:var(--r-sm);background:var(--surface)}
.todo-add:focus-within{border-color:var(--accent)}
.todo-add-input{flex:1 1 auto;min-width:80px;border:0;background:transparent;color:var(--ink);font:inherit;font-size:14px;outline:none}
.todo-add-input::placeholder{color:var(--faint)}
.todo-add-tools{display:flex;align-items:center;gap:2px}
.todo-tool{display:inline-flex;padding:6px;border:0;border-radius:var(--r-xs);background:transparent;color:var(--mute);cursor:pointer}
.todo-tool:hover{background:var(--surface-2);color:var(--ink)}
.todo-tool.is-on{color:var(--accent)}
.todo-add-go{flex:0 0 auto;padding:6px 16px}

/* tick circle */
.todo-tick{display:inline-flex;flex:0 0 auto;padding:2px;border:0;border-radius:50%;background:transparent;color:var(--mute);cursor:pointer}
.todo-tick:hover{color:var(--accent)}
.todo-tick.is-done{color:var(--accent)}
.todo-tick.is-static{cursor:pointer}

/* rows (list mode) */
.todo-body{flex:1 1 auto;min-height:0;overflow-y:auto;padding:6px 18px 24px}
.todo-group{margin-top:10px}
.todo-group-title{margin:8px 0 4px;font-size:12px;font-weight:700;color:var(--mute);text-transform:none;letter-spacing:.01em}
.todo-rows{display:flex;flex-direction:column;gap:2px}
.todo-rows.is-grid{container-type:inline-size;display:grid;grid-template-columns:repeat(auto-fill,minmax(min(100%,300px),1fr));grid-auto-flow:dense;gap:14px}
.todo-row{display:flex;align-items:center;gap:9px;padding:7px 8px;border-radius:var(--r-xs);background:transparent;border-bottom:1px solid var(--line-soft)}
.todo-row:hover{background:var(--surface)}
.todo-row.is-done .todo-row-title{text-decoration:line-through;color:var(--mute)}
.todo-row.is-readonly{opacity:.85}
.todo-row-main{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:1px}
/* p23 item 1 / p24 item 2 "allow editing a task from here directly": the title is a button
   that turns into a text field in place, so it must read exactly like the old plain text
   until touched — no border, no button chrome, left-aligned, same size/colour. */
.todo-row-title{display:block;width:100%;padding:0;border:0;background:transparent;font:inherit;font-size:13.5px;color:var(--ink);text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:text}
.todo-row-title:hover{text-decoration:underline;text-decoration-color:var(--line)}
.todo-row-title-input{width:100%;padding:1px 0;border:0;border-bottom:1px solid var(--accent);background:transparent;font:inherit;font-size:13.5px;color:var(--ink);outline:none}
.todo-row-notes{font-size:12px;color:var(--mute);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Now a button (was a plain span) so a due date and status can be set straight from the
   row (p23 item 2); .is-empty is the icon-only state before a due date exists. */
.todo-due{display:inline-flex;align-items:center;gap:4px;flex:0 0 auto;padding:2px 5px;border:0;border-radius:var(--r-xs);background:transparent;font:inherit;font-size:12px;color:var(--mute);font-variant-numeric:tabular-nums;cursor:pointer}
.todo-due:hover{background:var(--surface-2);color:var(--ink-2)}
.todo-due.is-empty{color:var(--faint)}
.todo-due.is-over{color:#B3261E;font-weight:600}
.todo-flag-ic{display:inline-flex;flex:0 0 auto;color:var(--mute)}
.todo-star{display:inline-flex;flex:0 0 auto;padding:3px;border:0;border-radius:var(--r-xs);background:transparent;color:var(--faint);cursor:pointer}
.todo-star:hover{color:var(--accent)}
.todo-star.is-on{color:var(--accent)}
.todo-row-menu{display:inline-flex;flex:0 0 auto;padding:3px;border:0;border-radius:var(--r-xs);background:transparent;color:var(--faint);cursor:pointer;opacity:0}
.todo-row:hover .todo-row-menu,.todo-row-menu:focus-visible{opacity:1}
.todo-row-menu:hover{color:var(--ink)}
/* p24 item 3 "allow attaching additional notes and attachments": the row's own expand
   toggle, and the panel it opens directly underneath the row — never a separate page. */
.todo-row-expand{display:inline-flex;flex:0 0 auto;padding:3px;border:0;border-radius:var(--r-xs);background:transparent;color:var(--faint);cursor:pointer;transition:transform .12s ease}
.todo-row-expand:hover{color:var(--ink)}
.todo-row-expand.is-open{color:var(--accent);transform:rotate(180deg)}
.todo-detail{display:flex;flex-direction:column;gap:8px;margin:0 0 4px 33px;padding:8px 10px;border:1px solid var(--line-soft);border-left:2px solid var(--accent-soft);border-radius:var(--r-xs);background:var(--surface-2)}
.todo-detail-origin{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--mute)}
.todo-detail-notes{width:100%;min-height:52px;padding:6px 8px;border:1px solid var(--line);border-radius:var(--r-xs);background:var(--surface);color:var(--ink);font:inherit;font-size:12.5px;resize:vertical}
.todo-detail-attach{display:flex;flex-direction:column;gap:4px}
.todo-detail-attach-head{display:flex;align-items:center;justify-content:space-between;gap:8px}
.todo-detail-attach-title{font-size:11px;font-weight:700;letter-spacing:.02em;color:var(--mute)}
.todo-detail-attach-add{padding:3px 8px;border:1px solid var(--line);border-radius:var(--r-xs);background:var(--surface);color:var(--ink-2);font:inherit;font-size:12px;cursor:pointer}
.todo-detail-attach-add:hover{border-color:var(--accent);color:var(--accent)}
.todo-detail-attach-add:disabled{color:var(--faint);cursor:default}
.todo-detail-attach-row{display:flex;align-items:center;gap:6px;padding:3px 0}
.todo-detail-attach-name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12.5px;color:var(--accent)}
.todo-detail-attach-remove{display:inline-flex;flex:0 0 auto;padding:2px;border:0;border-radius:var(--r-xs);background:transparent;color:var(--faint);cursor:pointer}
.todo-detail-attach-remove:hover{color:var(--accent)}
.todo-detail-attach-empty{font-size:12px;color:var(--faint)}
.todo-hidden-file-input{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
/* The due-date + status popover both the add bar and a row's own control open. */
.todo-props-pop{display:flex;flex-direction:column;gap:10px;min-width:220px;padding:2px}
.todo-props-row{display:flex;flex-direction:column;gap:4px}
.todo-props-label{font-size:11px;font-weight:700;letter-spacing:.02em;color:var(--mute)}
.todo-props-status{display:flex;flex-wrap:wrap;gap:4px}
.todo-props-stat{padding:4px 9px;border:1px solid var(--line);border-radius:var(--r-pill);background:var(--surface);color:var(--ink-2);font:inherit;font-size:12px;cursor:pointer}
.todo-props-stat:hover{border-color:var(--accent)}
.todo-props-stat.is-active{background:var(--accent-soft);border-color:var(--accent);color:var(--accent);font-weight:600}
.todo-chip{display:inline-flex;align-items:center;flex:0 0 auto;padding:2px 8px;border-radius:var(--r-pill);background:var(--surface-2);color:var(--mute);font-size:11px}
.todo-chip.is-accent{background:var(--accent-soft);color:var(--accent)}
.todo-status{margin:8px 18px 0;padding:8px 12px;border-radius:var(--r-xs);background:var(--surface-2);color:var(--mute);font-size:12.5px}

/* grid cards */
:root{--todo-working:oklch(70% .16 75);--todo-hue-0:oklch(65% .13 20);--todo-hue-1:oklch(65% .13 65);--todo-hue-2:oklch(65% .13 110);--todo-hue-3:oklch(65% .13 155);--todo-hue-4:oklch(65% .13 200);--todo-hue-5:oklch(65% .13 245);--todo-hue-6:oklch(65% .13 290);--todo-hue-7:oklch(65% .13 335)}
.todo-card{position:relative;display:flex;flex-direction:column;gap:8px;padding:12px;border:1px solid var(--line);border-radius:var(--r-sm);background:var(--surface)}
.todo-card-status{position:absolute;top:10px;right:10px;width:8px;height:8px;border-radius:50%;background:var(--faint)}
.todo-card[data-state="working"] .todo-card-status{background:var(--todo-working)}
.todo-card[data-state="needs"] .todo-card-status{background:var(--danger)}
.todo-card[data-hue="0"]{background:color-mix(in srgb,var(--todo-hue-0) 12%,var(--surface))}
.todo-card[data-hue="1"]{background:color-mix(in srgb,var(--todo-hue-1) 12%,var(--surface))}
.todo-card[data-hue="2"]{background:color-mix(in srgb,var(--todo-hue-2) 12%,var(--surface))}
.todo-card[data-hue="3"]{background:color-mix(in srgb,var(--todo-hue-3) 12%,var(--surface))}
.todo-card[data-hue="4"]{background:color-mix(in srgb,var(--todo-hue-4) 12%,var(--surface))}
.todo-card[data-hue="5"]{background:color-mix(in srgb,var(--todo-hue-5) 12%,var(--surface))}
.todo-card[data-hue="6"]{background:color-mix(in srgb,var(--todo-hue-6) 12%,var(--surface))}
.todo-card[data-hue="7"]{background:color-mix(in srgb,var(--todo-hue-7) 12%,var(--surface))}
.todo-card:hover{box-shadow:var(--shadow-sm)}
.todo-card.is-done .todo-card-title{text-decoration:line-through;color:var(--mute)}
.todo-card-top{display:flex;align-items:center;justify-content:space-between;padding-right:14px}
.todo-action-card{min-height:236px;padding:16px;gap:12px;transition:border-color .15s ease}
.todo-action-card .todo-card-title{margin:0;font-family:var(--display);font-size:18px;line-height:1.3}
.todo-action-card.is-open{grid-column:span 2;min-height:360px}
.todo-action-source{display:flex;align-items:center;justify-content:space-between;gap:10px;padding-right:16px;font-size:12px;color:var(--mute)}
.todo-action-preview{margin:0;font-size:13px;color:var(--ink-2);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.todo-action-state{padding:10px 12px;border:1px solid var(--line-soft);border-radius:var(--r-sm);background:color-mix(in srgb,var(--surface-2) 70%,transparent)}
.todo-action-state strong{font-size:12px}.todo-action-state p{margin:6px 0;font-size:13px;line-height:1.5}
.todo-action-buttons,.todo-action-footer{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.todo-action-footer{margin-top:auto;padding-top:10px;border-top:1px solid var(--line-soft)}
.todo-action-tabs{display:flex;gap:16px;border-bottom:1px solid var(--line)}
.todo-action-tab{appearance:none;border:0;border-bottom:2px solid transparent;background:transparent;color:var(--mute);padding:8px 0;font:inherit;cursor:pointer}
.todo-action-tab.is-on{border-bottom-color:var(--accent);color:var(--ink)}
.todo-action-details{display:flex;flex-direction:column;gap:16px;min-width:0}
.todo-action-draft,.todo-action-original{padding:12px;border:1px solid var(--line);border-radius:var(--r-sm);background:var(--surface)}
.todo-action-details h4{margin:0 0 10px;font-size:13px}.todo-action-original p{white-space:pre-wrap;overflow-wrap:anywhere}
.todo-action-field{display:flex;align-items:center;gap:10px;margin:8px 0}.todo-action-field span{width:54px;font-size:12px;color:var(--mute)}
.todo-action-field input,.todo-action-answer{min-width:0;flex:1;padding:8px;border:1px solid var(--line);border-radius:var(--r-xs);background:var(--surface);color:var(--ink);font:inherit}
.todo-action-draft-body{width:100%;box-sizing:border-box;min-height:160px;resize:vertical;padding:10px;border:1px solid var(--line);border-radius:var(--r-xs);background:var(--surface);color:var(--ink);font:inherit;line-height:1.5}
.todo-action-muted{font-size:12px;color:var(--mute)}
.todo-action-plan{margin:6px 0;padding-left:20px;font-size:13px;line-height:1.5}.todo-action-plan li{padding:2px 0}
.todo-action-how{display:flex;flex-direction:column;gap:6px;margin:8px 0}.todo-action-how-label{font-size:12px;font-weight:600;color:var(--ink)}
.todo-action-how-text{width:100%;box-sizing:border-box;min-height:56px;resize:vertical;padding:8px;border:1px solid var(--line);border-radius:var(--r-xs);background:var(--surface);color:var(--ink);font:inherit;line-height:1.5}
.todo-action-how-saved{margin:6px 0;overflow-wrap:anywhere}
.todo-action-history ol{padding-left:20px}.todo-action-history li{padding:5px 0;font-size:13px}.todo-action-history time{margin-right:10px;font-family:var(--mono);color:var(--mute);font-size:11px}
.todo-action-questions{display:flex;flex-direction:column;gap:12px}.todo-action-question{margin:0;padding:12px;border:1px solid var(--line);border-radius:var(--r-sm)}
.todo-action-choice{display:flex;align-items:center;gap:8px;padding:6px 0;font-size:13px}
@container(max-width:620px){.todo-action-card.is-open{grid-column:span 1}}
@media(max-width:700px){.todo-action-card.is-open{grid-column:span 1}.todo-action-card{min-width:0}.todo-action-field{align-items:flex-start;flex-direction:column}.todo-action-field input{width:100%;box-sizing:border-box}.todo-action-buttons .btn,.todo-action-footer .btn{min-height:44px}}
.todo-card-title{font-size:14px;font-weight:600;color:var(--ink)}
.todo-card-meta{display:flex;align-items:center;gap:8px;flex-wrap:wrap}

/* empty states (N5) */
.todo-empty{flex:1 1 auto;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;padding:40px 20px;text-align:center}
.todo-empty-plate{display:flex;align-items:center;justify-content:center;width:72px;height:72px;margin-bottom:8px;border-radius:50%;background:var(--surface-2);color:var(--mute)}
.todo-empty-title{font-size:16px;font-weight:700;color:var(--ink)}
.todo-empty-line{font-size:13px;color:var(--mute);max-width:380px}

/* menus — PositionedPopover (DS) carries the fixed anchor; this only paints it */
.todo-menu-backdrop{position:fixed;inset:0;z-index:60;background:transparent}
.todo-menu{z-index:61;min-width:190px;max-width:280px;max-height:60vh;overflow-y:auto;padding:6px;border:1px solid var(--line);border-radius:var(--r-sm);background:var(--surface);box-shadow:var(--shadow)}
.todo-menu-item{display:flex;align-items:center;gap:8px;width:100%;padding:7px 9px;border:0;border-radius:var(--r-xs);background:transparent;color:var(--ink);font:inherit;font-size:13px;cursor:pointer;text-align:left}
.todo-menu-item:hover{background:var(--surface-2)}
.todo-menu-item:disabled{color:var(--faint);cursor:default}
.todo-menu-check{display:inline-flex;flex:0 0 18px;color:var(--accent)}
.todo-menu-label{flex:1 1 auto}
.todo-menu-divider{height:1px;margin:5px 4px;background:var(--line-soft)}
.todo-menu-header{padding:6px 9px 2px;font-size:11px;font-weight:700;color:var(--mute)}

/* add-bar popovers (due / reminder / repeat) */
.todo-draft-pop{position:absolute;left:46px;top:calc(100% + 6px);z-index:40;min-width:220px;padding:8px;border:1px solid var(--line);border-radius:var(--r-sm);background:var(--surface);box-shadow:var(--shadow)}
.todo-pop-field{width:100%;padding:6px 8px;border:1px solid var(--line);border-radius:var(--r-xs);background:var(--surface-2);color:var(--ink);font:inherit;font-size:13px}
.todo-pop-quick{display:flex;justify-content:flex-end;gap:6px;margin-top:8px}

/* the inline icon plate: inlined Fluent SVGs inherit currentColor */
.todo-ic{display:inline-flex;width:20px;height:20px}
.todo-ic svg{width:20px;height:20px;fill:currentColor}
.todo-ic.is-16{width:16px;height:16px}
.todo-ic.is-16 svg{width:16px;height:16px}

/* narrow screens: the rail folds into a top strip, one column everywhere (X3) */
@media (max-width: 760px){
  .mail-shell > .todo-main { grid-column: auto; }
  .todo-rows.is-grid{grid-template-columns:1fr}
  .todo-head{gap:8px}
}

/* ============ YUM-1397 fix round 2: one height everywhere (B1) ============ */
/* The toolbar and the invite card are 32px; the compact rows (the list chip, the row's
   hover actions) are 28px. The rail's round icon buttons are a different control, not
   part of this count. */
html .mail-actionbar > .btn { height: 32px; }
html .mail-actionbar > .icbtn { width: 32px; height: 32px; }
html .mail-actionbar > .icbtn svg,
html .mail-actionbar > .btn svg { width: 14px; height: 14px; }
.invite-actions .btn, .invite-card-row > .btn, .invite-card-row .invite-actions ~ .btn { height: 32px; }
.invite-actions .btn.compact, .invite-card-row .btn.compact { height: 28px; }
.mail-item-hover .icbtn { width: 28px; height: 28px; }
/* Fix 6 (YUM-1397 B1): theme files restyle .btn one class deep, which weighs the same as
   .btn.solid/.btn.is-primary and loads later — so on letterpress the filled primary went
   transparent and every primary read as quiet. The html prefix is one element more
   specific, so the filled style holds on every theme, for both the legacy alias and the
   new variant name. */
html .btn.solid,
html .btn.is-primary {
  background: var(--accent); border-color: var(--accent); color: var(--on-accent);
}
html .btn.solid:hover,
html .btn.is-primary:hover {
  background: color-mix(in srgb, var(--accent) 86%, #000); border-color: var(--accent); color: var(--on-accent);
}
/* YUM-1396 Part 1 additions for the 1.0.4 combined test build (wM:p7 moved the main To do
   styles here at merge; this file carries what fix r2 adds on top). */
.todo-ic.is-24{width:24px;height:24px}
.todo-ic.is-24 svg{width:24px;height:24px}

/* YUM-1446 (T6c): one toolbar button across the Work sections. Before this, Mail used
   30px outlined chips, Calendar 23px boxes, To do 26px borderless buttons and Files 32px
   bold ink-bordered ones, so the same kind of control looked different in every section.
   Now each is a 30px quiet outline in 13px type: it tints on hover and wears the accent when
   pressed or on. The html prefix only adds weight, so a theme's own .btn/.icbtn rule, which
   loads later, cannot set a second style on these rows. Mail's Focused/Other tabs and To do's
   Grid/List switch stay tabs and a segmented switch; they only take the same height.
   YUM redline (Calendar/To do pass, 2026-09-26): Calendar's .work-cal-btn and To do's
   .todo-head-btn are carved back OUT of this 30px unification — p19 item 1 asks the
   calendar header to match Outlook web's own (smaller) size, and p24 item 1 asks the To do
   header tighter. Both keep their own compact sizing below instead. Mail and Files are
   untouched by that ask and stay at the shared 30px. */
html .mail-list-head .icbtn,
html .mail-list-head .btn,
html .mail-list-head .mail-filter-chip,
html .work-browser-toggle,
html .work-browser-bar .btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: 30px; min-width: 30px; padding: 0 10px;
  border: 1px solid var(--line); border-radius: var(--r-xs);
  background: transparent; box-shadow: none; transform: none;
  color: var(--ink-2); font-family: var(--ui); font-size: 13px; font-weight: 450; line-height: 1;
  white-space: nowrap; cursor: pointer;
}
html .mail-list-head .icbtn { width: 30px; padding: 0; }
html .mail-list-head .icbtn:hover,
html .mail-list-head .btn:hover,
html .mail-list-head .mail-filter-chip:hover,
html .work-browser-toggle:hover,
html .work-browser-bar .btn:hover { background: var(--bg-2); border-color: var(--line); color: var(--ink); }
html .mail-list-head .icbtn.active,
html .mail-list-head .mail-filter-chip.active,
html .work-browser-toggle.active {
  background: var(--accent-soft); border-color: color-mix(in srgb, var(--accent) 40%, transparent); color: var(--accent);
}
html .work-browser-toggle:focus-visible,
html .work-browser-bar .btn:focus-visible,
html .mail-list-head .mail-filter-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.work-browser-bar { padding: 6px 10px; gap: 6px; }
/* Calendar/To do's own compact toolbar buttons keep their hover styling from .work-cal-bar
   above and from .todo-head-btn:hover below; only what the removed html-prefixed block also
   carried for them — focus ring and the pressed/open state — needs restating here. */
.work-cal-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.todo-head-btn[aria-expanded='true'] { background: var(--accent-soft); color: var(--accent); }
.todo-head-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* YUM-1445 (T5): Yarnball file-type icon pack (public/app/file-icons.js). Every colour is a
   theme token, so Default and Classic, light and dark, all draw from their own palette. */
.ft-icon { flex: none; display: inline-block; vertical-align: middle; overflow: visible; }
.ft-icon path, .ft-icon rect, .ft-icon circle { vector-effect: non-scaling-stroke; stroke-linejoin: round; stroke-linecap: round; }
.ft-icon { --ft-tint: var(--mute); }
.ft-fam-red { --ft-tint: var(--accent); }
.ft-fam-green { --ft-tint: var(--success, #4A6B45); } /* light schemes define no --success; the letterpress green is the fallback */
.ft-fam-amber { --ft-tint: var(--warning, #9A6A1E); }
/* The page stays light in dark mode, so its neutral glyphs use fixed page inks, not --ink-2. */
.ft-fam-ink { --ft-tint: var(--file-ink, #4A5058); }
.ft-fam-mute { --ft-tint: var(--file-mute, #7A828A); }
.ft-sheet { fill: var(--file-page, #F4F5F6); stroke: var(--file-edge, #B9BEC3); stroke-width: 1; }
.ft-fold { fill: var(--file-fold, #D5D9DD); stroke: var(--file-edge, #B9BEC3); stroke-width: 1; }
.ft-glyph { fill: none; stroke: var(--ft-tint); stroke-width: 1.5; }
.ft-glyph-fill { fill: var(--ft-tint); }
.ft-sm .ft-glyph { stroke-width: 1.25; }
.ft-badge { fill: var(--ft-tint); stroke: none; }
.ft-badge-text { fill: var(--file-page, #F4F5F6); font: 700 5.6px/1 var(--ui, system-ui, sans-serif); letter-spacing: .03em; }
/* YUM-1444 round 2 (SB: "like default windows or linux file explorers"): flat, solid, filled
   folders in one saturated blue (the Breeze family), darker back, lighter body, a light top
   edge, and a white glyph ON the body for special folders. Tokens per scheme and mode live
   in colour-options/*.css; these fallbacks are the light values. */
.ft-folder-back { fill: var(--folder-back, #2B8FC6); stroke: none; }
.ft-folder-front { fill: var(--folder, #3DAEE9); stroke: none; }
.ft-folder-tab { fill: var(--folder-tab, #7CCBF2); stroke: none; }
.ft-folder-glyph { fill: none; stroke: var(--folder-glyph, #FFFFFF); stroke-width: 1.6; }
.ft-sm .ft-folder-glyph { stroke-width: 1.1; }
.ft-folder-glyph-fill { fill: var(--folder-glyph, #FFFFFF); }
.ft-place { flex: none; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.ft-peek-sheet { fill: var(--file-page, #F4F5F6); stroke: var(--folder-back, #2B8FC6); stroke-width: 1; }
.ft-peek-line { fill: none; stroke: var(--ft-tint); stroke-width: 1.25; }

/* YUM-1444 (T4): the folder view in the Files reading column (FolderView, file-manager.js).
   Toolbar, contents in one of three views, status bar. Selection is a light accent wash,
   never a dark bar. All colours are theme tokens. */
.fm-view { display: flex; flex-direction: column; flex: 1; min-height: 0; min-width: 0; }
.fm-bar { display: flex; align-items: center; gap: 8px; padding: 8px 12px; border-bottom: 1px solid var(--line); background: var(--surface); flex-wrap: wrap; }
/* P27.2/P30.2: "I dont understand any of these" / "browser" — back/forward/up/refresh sat
   as four loose floating icons, reading as literal browser chrome. Grouped into one bordered
   app-native control (same treatment as .fm-path/.fm-views beside it) so it reads as this
   file manager's own toolbar, not a copy of the browser's own nav row; each button already
   carries a real tooltip via RoundIconButton's title prop. */
.fm-nav { display: flex; align-items: center; gap: 1px; padding: 2px; border: 1px solid var(--line); border-radius: 6px; background: var(--paper); }
.fm-up svg { transform: rotate(-90deg); }
.fm-path { display: flex; align-items: center; gap: 2px; flex: 1; min-width: 140px; overflow: hidden; padding: 0 6px; height: 30px; border: 1px solid var(--line); border-radius: 6px; background: var(--paper); }
.fm-crumb { border: 0; background: none; color: var(--ink-2); font: inherit; font-size: 13px; padding: 3px 6px; border-radius: 4px; cursor: pointer; white-space: nowrap; }
.fm-crumb:hover { background: var(--surface-2); }
.fm-crumb.is-here { color: var(--ink); font-weight: 600; }
.fm-path-sep { color: var(--mute); flex: none; }
.fm-filter { width: 170px; height: 30px; padding: 0 10px; border: 1px solid var(--line); border-radius: 6px; background: var(--paper); color: var(--ink); font: inherit; font-size: 13px; }
.fm-filter:focus { outline: 2px solid var(--focus-ring, var(--accent)); outline-offset: -1px; }
.fm-views { display: flex; border: 1px solid var(--line); border-radius: 6px; overflow: hidden; }
.fm-view-btn { display: flex; align-items: center; gap: 5px; border: 0; background: var(--surface); color: var(--ink-2); font: inherit; font-size: 12px; padding: 5px 10px; cursor: pointer; }
.fm-view-btn + .fm-view-btn { border-left: 1px solid var(--line); }
.fm-view-btn:hover { background: var(--surface-2); }
.fm-view-btn.is-on { background: var(--accent-soft); color: var(--accent); }
.fm-body { flex: 1; min-height: 0; overflow: auto; padding: 12px; background: var(--paper); }
.fm-status { padding: 5px 14px; border-top: 1px solid var(--line); background: var(--surface); color: var(--mute); font-size: 12px; }
.fm-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; min-height: 200px; color: var(--mute); font-size: 13px; }
.fm-empty.is-error { color: var(--danger); }
.fm-clip { display: flex; align-items: center; gap: 8px; padding: 6px 12px; background: var(--accent-soft); color: var(--ink); font-size: 13px; }
.fm-clip span { flex: 1; }

.fm-item { cursor: default; user-select: none; border: 1px solid transparent; border-radius: 6px; color: var(--ink); }
.fm-item:hover { background: var(--surface-2); }
.fm-item:focus-visible { outline: 2px solid var(--focus-ring, var(--accent)); outline-offset: 1px; }
.fm-item.is-selected { background: var(--accent-soft); border-color: color-mix(in srgb, var(--accent) 35%, transparent); }
.fm-item.is-drop { border-color: var(--accent); border-style: dashed; background: var(--accent-soft); }
.fm-name { overflow: hidden; text-overflow: ellipsis; }

/* P26.4: "more dense" — smaller tiles, tighter gaps. tileSize()/tileWhen() already feed a
   size and a modified date into every tile's two .fm-sub lines (P26.3); this just gives
   Icons view a denser grid to hold them in. */
.fm-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: 10px 8px; align-content: start; padding: 6px; }
.fm-item-icons { display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 10px 6px 10px; text-align: center; }
.fm-item-icons .ft-icon { margin-bottom: 6px; }
.fm-item-icons .fm-name { font-size: 13px; line-height: 1.3; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; word-break: break-word; }
.fm-sub { font-size: 11.5px; line-height: 1.35; color: var(--mute); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }

/* YUM-1444 round 2: Dolphin's Places sidebar (Files window and Work rail). Line icons in the
   text colour; the place you are in is a light tint, not a bar. */
.fm-explorer { display: flex; flex: 1; min-height: 0; min-width: 0; }
.fm-explorer-main { display: flex; flex-direction: column; flex: 1; min-width: 0; min-height: 0; }
.fm-places { flex: none; width: 190px; overflow: auto; padding: 8px 6px; border-right: 1px solid var(--line); background: var(--surface); }
.fm-places-group + .fm-places-group { margin-top: 10px; }
.fm-places-head { padding: 4px 10px; font-size: 11.5px; font-weight: 600; color: var(--mute); }
.fm-place { display: flex; align-items: center; gap: 9px; width: 100%; padding: 6px 10px; border: 0; border-radius: 6px; background: none; color: var(--ink); font: inherit; font-size: 13px; text-align: left; cursor: pointer; }
.fm-place:hover { background: var(--surface-2); }
.fm-place.is-on, .mail-folder.fm-place.active { background: var(--accent-soft); color: var(--ink); box-shadow: none; font-weight: 500; }
.fm-place-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fm-place .ft-place { color: var(--ink-2); }

.fm-compact { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 2px 8px; align-content: start; }
.fm-item-compact { display: flex; align-items: center; gap: 8px; padding: 4px 8px; font-size: 13px; white-space: nowrap; }

.fm-details { display: flex; flex-direction: column; font-size: 13px; }
.fm-details-head, .fm-item-details { display: grid; grid-template-columns: minmax(200px, 1fr) 90px 80px 130px; align-items: center; gap: 8px; }
.fm-details-head { position: sticky; top: -12px; background: var(--paper); border-bottom: 1px solid var(--line); padding: 0 8px; z-index: 1; }
.fm-col { display: flex; align-items: center; gap: 4px; border: 0; background: none; color: var(--mute); font: inherit; font-size: 12px; padding: 6px 0; text-align: left; cursor: pointer; }
.fm-col:hover, .fm-col.is-sorted { color: var(--ink); }
.fm-sort-mark { transform: rotate(-90deg); }
.fm-sort-mark.is-desc { transform: rotate(90deg); }
.fm-item-details { padding: 3px 8px; }
.fm-cell { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.fm-cell-name { display: flex; align-items: center; gap: 8px; }
.fm-cell-size, .fm-cell-kind, .fm-cell-modified { color: var(--mute); font-size: 12px; }
.fm-cell-size { text-align: right; }
.file-tree-icon.ft-icon { width: 16px; height: 16px; }

/* YUM-1442 T2: the translate toolbar in the Files viewer. Translate group first (source
   detected, target picker, one labelled Translate button), this-file tools next, file
   actions behind "More" at the far right. */
/* P28.1/P29/P32.1: the file-preview's own toolbar row, scoped so this does not touch
   .work-advice's other use (the mail AI-advice strip) — only .tx-toolbar callers compact. */
.work-advice.tx-toolbar { gap: 10px; padding: 5px 12px; }
.tx-toolbar { gap: 12px; }
.tx-group { display: flex; align-items: center; gap: 6px; }
.tx-group + .tx-group { padding-left: 12px; border-left: 1px solid var(--line-soft); }
.tx-group-end { margin-left: auto; }
.tx-from { font-family: var(--ui); font-size: 12px; color: var(--mute); white-space: nowrap; }
.tx-arrow { color: var(--faint); }
.tx-go { display: inline-flex; align-items: center; gap: 6px; }
.tx-lang { position: relative; }
.tx-lang-button {
  display: inline-flex; align-items: center; gap: 6px; max-width: 240px;
  padding: 6px 10px; border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--surface); color: var(--ink); font-family: var(--ui); font-size: 12.5px; cursor: pointer;
}
.tx-lang-button span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tx-lang-button:hover { border-color: var(--ink-2); }
.tx-lang-button:focus-visible, .tx-lang-option:focus-visible, .tx-text-body:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.tx-lang-button:disabled { opacity: .55; cursor: not-allowed; }
.tx-lang-pop {
  position: absolute; top: calc(100% + 4px); left: 0; z-index: 96; width: 280px;
  display: flex; flex-direction: column; border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--surface); box-shadow: var(--shadow-sm);
}
.tx-lang-search {
  margin: 8px; padding: 6px 8px; border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--surface-2); color: var(--ink); font-family: var(--ui); font-size: 12.5px;
}
.tx-lang-list { max-height: 320px; overflow-y: auto; padding: 0 6px 6px; }
.tx-lang-group { padding: 8px 8px 4px; font-family: var(--ui); font-size: 11px; font-weight: 600; letter-spacing: .02em; text-transform: uppercase; color: var(--mute); }
.tx-lang-option {
  display: flex; width: 100%; align-items: baseline; justify-content: space-between; gap: 8px;
  padding: 6px 8px; border: 0; border-radius: var(--r-xs); background: transparent;
  color: var(--ink); font-family: var(--ui); font-size: 13px; text-align: left; cursor: pointer;
}
.tx-lang-option:hover, .tx-lang-option.is-cursor { background: var(--work-rail-hover, var(--surface-2)); }
.tx-lang-option.is-on { font-weight: 600; }
.tx-lang-native { font-size: 12px; color: var(--mute); }
.tx-lang-empty { padding: 8px; font-family: var(--ui); font-size: 12.5px; color: var(--mute); }
.tx-pages { display: flex; flex-wrap: wrap; gap: 3px; margin: 8px 0 0; padding: 0; list-style: none; }
.tx-page { width: 14px; height: 6px; border-radius: 2px; background: var(--line); }
.tx-page.is-done { background: var(--accent); }
.tx-page.is-now { background: var(--ink-2); }
.tx-result {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px; padding: 8px 12px;
  border-bottom: 1px solid var(--line-soft); background: var(--surface);
  font-family: var(--ui); font-size: 12.5px; color: var(--ink-2);
}
.tx-result-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tx-views { display: flex; gap: 4px; }
.tx-result-actions { display: flex; gap: 6px; margin-left: auto; }
.tx-unclear-list { margin: 4px 0 0; padding-left: 18px; }
.tx-text { gap: 12px; padding: 12px; }
.tx-text-pane { display: flex; flex-direction: column; min-height: 0; border: 1px solid var(--line-soft); border-radius: var(--r-sm); background: var(--surface); }
.tx-text-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 8px 10px; border-bottom: 1px solid var(--line-soft); font-family: var(--ui); font-size: 12px; font-weight: 600; color: var(--ink-2); }
.tx-text-head .btn { display: inline-flex; align-items: center; gap: 4px; }
.tx-text-body { flex: 1 1 auto; margin: 0; padding: 10px; overflow: auto; white-space: pre-wrap; font-family: var(--ui); font-size: 13px; line-height: 1.5; color: var(--ink); }
.yb-context-item.is-danger { color: var(--danger, var(--accent)); }

/* ---------------------------------------------------------------- YUM-1444 round 3: Work > Files, two columns
   SB 2026-08-28: "we now have 3 times the same folder listed". Files is the Places rail and
   the folder view at the full remaining width, as in Dolphin. Mail, Calendar and Teams keep
   the three-column frame. */
.mail-shell.is-two-column { grid-template-columns: var(--work-rail-w, 190px) minmax(0, 1fr) 0; }
.mail-shell.is-two-column.work-assist-open { grid-template-columns: var(--work-rail-w, 190px) minmax(0, 1fr) 296px; }
.work-frame[data-reading-pane] .mail-shell.is-two-column { grid-template-rows: none; }
.work-frame[data-reading-pane] .mail-shell.is-two-column .mail-reader { grid-column: 2; grid-row: auto; display: flex; }
.work-frame[data-reading-pane] .mail-shell.is-two-column .work-assist,
.work-frame[data-reading-pane] .mail-shell.is-two-column .work-assist-tab { grid-column: 3; grid-row: auto; }
.mail-reader.fm-two { border-left: 0; }
.fm-search { flex: 0 1 240px; min-width: 120px; }
.fm-new { display: inline-flex; align-items: center; gap: 4px; }
@media (max-width: 900px) {
  .mail-shell.is-two-column, .mail-shell.is-two-column.work-assist-open { grid-template-columns: minmax(0, 1fr); }
}

/* YUM-1444 round 4: the Work > Files toolbar stays on one row. The path, filter and search
   boxes give up width first; the buttons keep theirs. */
.fm-one-row { flex-wrap: nowrap; }
.fm-one-row > * { flex-shrink: 0; }
.fm-one-row > .fm-path { flex: 1 1 160px; min-width: 70px; }
.fm-one-row > .fm-filter { flex: 0 1 170px; width: auto; min-width: 60px; }
.fm-one-row > .fm-search { flex: 0 1 240px; min-width: 80px; }

/* Mail folders collapsed by default (SB 2026-09-25): the "More folders" row under Inbox. */
.mail-rail-more {
  display: flex; align-items: center; gap: 4px; width: 100%; margin: 2px 0 0; padding: 4px 8px;
  border: 0; border-radius: var(--r-sm); background: transparent; color: var(--work-rail-label, var(--mute));
  font-family: var(--ui); font-size: 11.5px; text-align: left; cursor: pointer;
}
.mail-rail-more:hover:not(:disabled) { background: var(--work-rail-hover); color: var(--work-rail-ink, var(--ink)); }
.mail-rail-more:disabled { cursor: default; opacity: .7; }
.mail-rail-more:focus-visible { outline: 2px solid var(--focus-ring, var(--accent)); outline-offset: -2px; }
.mail-rail-more svg { transition: transform .15s; }
.mail-rail-more.is-open svg { transform: rotate(90deg); }

/* Redline 2026-09-26: the Settings window header spacer (same geometry as the DS .mhead spacer). */
.mh-sp { width: 36px; flex: none; }

/* SB 2026-09-25: the mail list head and rows, like Outlook's (TASK-mail-list-outlook).
   Head: the search box on its own line, then ONE row - Focused / Other and four 20px Fluent
   icon buttons (Select, Attachments only, Filter, Sort). A filter that is on tints its button
   and puts a dot on Filter. Rows: three lines (sender; subject with the time on the right;
   preview), a pastel initials avatar, a 3px accent bar on unread rows, a light tint for the
   selected row. Colours are tokens; the avatar pastels have light and dark values. */
:root {
  --avatar-1-bg: #CFE4FA; --avatar-1-ink: #0F4A85;
  --avatar-2-bg: #FCE3C4; --avatar-2-ink: #8A4B08;
  --avatar-3-bg: #E3DAF7; --avatar-3-ink: #4B3A8C;
  --avatar-4-bg: #D3F0E0; --avatar-4-ink: #1E6B45;
  --avatar-5-bg: #F8D9E4; --avatar-5-ink: #8C2C51;
  --avatar-6-bg: #DCEFF2; --avatar-6-ink: #1F5F6B;
}
[data-mode="dark"] {
  --avatar-1-bg: #1F3A57; --avatar-1-ink: #B7D6F7;
  --avatar-2-bg: #4A3319; --avatar-2-ink: #F6D0A2;
  --avatar-3-bg: #362D55; --avatar-3-ink: #D5C9F4;
  --avatar-4-bg: #1D3F2D; --avatar-4-ink: #B4E6CA;
  --avatar-5-bg: #4A2433; --avatar-5-ink: #F3C2D4;
  --avatar-6-bg: #1C3D43; --avatar-6-ink: #BCE3E9;
}

/* The head. */
.icbtn.mail-tool { flex: none; }
.mail-list-head.is-mail { flex-wrap: wrap; row-gap: 4px; padding: 8px 8px 0; border-bottom: 1px solid var(--line); }
.mail-list-head.is-mail > .mail-search { flex: 1 1 100%; order: 0; height: 32px; }
.mail-list-tools, .mail-list-head.is-mail .mail-bulk-bar { flex: 1 1 100%; display: flex; align-items: center; gap: 2px; min-width: 0; height: 40px; }
.mail-list-tools-gap { flex: 1 1 auto; }
.mail-list-tools .mail-focus-tabs { align-self: stretch; }
.mail-list-tools .mail-focus-tab { font-size: 14px; padding: 0 8px; }
html .mail-list-head .icbtn.mail-tool {
  position: relative; width: 32px; height: 32px; min-width: 32px; padding: 0;
  border: 0; border-radius: var(--r-xs); background: transparent; color: var(--ink-2);
}
html .mail-list-head .icbtn.mail-tool:hover { background: var(--surface-2); color: var(--ink); border: 0; }
html .mail-list-head .icbtn.mail-tool.is-on,
html .mail-list-head .icbtn.mail-tool[aria-expanded='true'] { background: var(--accent-soft); color: var(--accent); }
html .mail-list-head .icbtn.mail-tool:disabled { opacity: 0.4; cursor: default; background: transparent; }
html .mail-list-head .icbtn.mail-tool:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.mail-tool-dot { position: absolute; top: 5px; right: 5px; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 2px var(--surface); }
.mail-list-head.is-mail .mail-select-all { height: 32px; padding: 0 6px 0 4px; }
.mail-list-head.is-mail .mail-select-all input { width: 16px; height: 16px; }
.mail-list-head.is-mail .mail-bulk-count { font-family: var(--ui); font-size: 13px; color: var(--ink-2); margin-right: auto; }
.mail-list-head.is-mail .mail-bulk-clear { margin-left: 4px; }
/* The Filter and Sort menus: a tick column, so the choices line up whether ticked or not. */
.mail-menu-check { gap: 8px; }
.mail-menu-tick { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; flex: none; color: var(--accent); }

/* The rows. */
.mail-item {
  align-items: flex-start; gap: 12px; min-height: 76px; padding: 8px 12px 8px 9px;
  border-bottom: 1px solid var(--line-soft);
}
.mail-item.unread .mail-item-from::before { content: ""; display: inline-block; width: 6px; height: 6px; margin-right: 6px; border-radius: 50%; background: var(--accent); vertical-align: middle; }
.mail-item:hover { --mail-row-bg: color-mix(in srgb, var(--accent) 4%, var(--surface)); background: var(--mail-row-bg); }
.mail-item.active,
.mail-item.is-picked { --mail-row-bg: color-mix(in srgb, var(--accent) 10%, var(--surface)); background: var(--mail-row-bg); }
.mail-item-open { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.mail-item-top { display: flex; align-items: center; gap: 6px; min-width: 0; height: 20px; }
.mail-item-from { flex: 1 1 auto; font-size: 14px; line-height: 20px; color: var(--ink); }
.mail-item.unread .mail-item-from { font-weight: 600; }
.mail-item-top .mail-flag,
.mail-item-top .mail-action-tag { flex: none; font-size: 8.5px; padding: 0 5px; line-height: 14px; }
.mail-item-marks { flex: none; gap: 2px; color: var(--mute); }
.mail-item-marks svg { display: block; }
.mail-importance, .mail-flag-mark { display: inline-flex; color: var(--danger); }
.mail-attach-mark { display: inline-flex; color: var(--mute); }
.mail-item-top .mail-item-translate { width: 20px; height: 20px; border: 0; opacity: 0.8; color: var(--mute); }
.mail-item-top .mail-item-translate.is-on { color: var(--accent); opacity: 1; }
.mail-item-subject-line { display: flex; align-items: baseline; gap: 6px; min-width: 0; margin: 0; height: 20px; }
.mail-item-subject { flex: 0 1 auto; min-width: 0; font-size: 14px; line-height: 20px; color: var(--ink); font-weight: 400; }
.mail-item.unread .mail-item-subject { color: var(--ink); font-weight: 600; }
.mail-item-count { flex: none; font-size: 13px; color: var(--accent); font-weight: 600; }
.mail-item-subject-line .mail-item-time { flex: none; margin-left: auto; font-family: var(--ui); font-size: 12px; color: var(--mute); }
.mail-item.unread .mail-item-time { color: var(--ink); font-weight: 600; }
.mail-item-preview { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; line-height: 18px; color: var(--mute); }
.mail-item-invite { margin-top: 4px; }
.mail-thread-chevron { align-self: center; color: var(--ink-2); }
/* A pastel initials circle, like Outlook's. Six tones from a hash of the address. */
.mail-avatar.is-row { width: 36px; height: 36px; margin-top: 2px; font-size: 14px; font-weight: 600; border: 0; }
.mail-avatar[data-tone="0"] { background: var(--avatar-1-bg); color: var(--avatar-1-ink); border: 0; }
.mail-avatar[data-tone="1"] { background: var(--avatar-2-bg); color: var(--avatar-2-ink); border: 0; }
.mail-avatar[data-tone="2"] { background: var(--avatar-3-bg); color: var(--avatar-3-ink); border: 0; }
.mail-avatar[data-tone="3"] { background: var(--avatar-4-bg); color: var(--avatar-4-ink); border: 0; }
.mail-avatar[data-tone="4"] { background: var(--avatar-5-bg); color: var(--avatar-5-ink); border: 0; }
.mail-avatar[data-tone="5"] { background: var(--avatar-6-bg); color: var(--avatar-6-ink); border: 0; }
/* Row ticks only in selection mode (or on a ticked row); a hover never shifts the row. */
.mail-item:hover .mail-item-check,
.mail-item:focus-within .mail-item-check { display: none; }
.mail-item.is-picked .mail-item-check,
.mail-list.has-picks .mail-item-check { display: flex; align-self: center; padding: 0; }
.mail-item-check input { width: 16px; height: 16px; }
/* The date strips: quieter count. */
.mail-list-group { font-size: 13px; padding: 7px 12px; border-bottom: 0; }
.mail-list-group-count { font-family: var(--ui); font-size: 11px; color: var(--faint, var(--mute)); }

/* SB 2026-09-25, row hover actions: compact, borderless, at the right edge on the time's
   line, Delete last. The time hides while the strip shows. */
html .mail-item-hover {
  position: absolute; top: 30px; right: 8px; gap: 0; padding: 0;
  border: 0; border-radius: var(--r-xs); background: var(--mail-row-bg, var(--surface)); box-shadow: none;
}
/* A short fade on the strip's left edge, so clipped text never meets the icons head on. */
html .mail-item-hover::before {
  content: ""; position: absolute; top: 0; bottom: 0; right: 100%; width: 16px; pointer-events: none;
  background: linear-gradient(to right, transparent, var(--mail-row-bg, var(--surface)));
}
html .mail-item-hover .icbtn {
  width: 28px; height: 28px; min-width: 28px; padding: 0; border: 0; border-radius: var(--r-xs);
  background: transparent; box-shadow: none; color: var(--ink-2);
}
html .mail-item-hover .icbtn:hover { background: color-mix(in srgb, var(--ink) 8%, transparent); color: var(--ink); }
html .mail-item-hover .icbtn.is-on { color: var(--danger); }
html .mail-item-hover .icbtn.is-danger:hover { background: var(--danger-soft); color: var(--danger); }
/* While the strip shows, the subject and preview end in an ellipsis before it instead of
   running under it. */
.mail-item:hover .mail-item-subject-line,
.mail-item:focus-within .mail-item-subject-line,
.mail-item.active .mail-item-subject-line,
.mail-item:hover .mail-item-preview,
.mail-item:focus-within .mail-item-preview,
.mail-item.active .mail-item-preview { padding-right: 116px; }
/* The document viewer (SB 2026-09-25: "open the view over everything else, and darken the
   surrounding"). One overlay for every document open, drawn by doc-viewer.js as a portal on
   the body. The scrim is neutral black in every theme and colour option, never tinted, and
   the app stays visible behind it. The pages sit on a neutral canvas like paper. */
.doc-overlay {
  position: fixed; inset: 0; z-index: 200;
  display: flex; align-items: center; justify-content: center;
  padding: 4vh 5vw;
  background: rgba(0, 0, 0, .6);
  backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px);
}
[data-theme] .doc-overlay { background: rgba(0, 0, 0, .6); }
.doc-viewer {
  display: flex; flex-direction: column;
  width: 90vw; max-width: 1200px; height: 92vh;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--r);
  box-shadow: 0 24px 64px rgba(0, 0, 0, .45);
  overflow: hidden;
}
.doc-viewer:focus { outline: none; }
.doc-viewer.is-maximized { width: 100vw; max-width: none; height: 100vh; border-radius: 0; border: 0; }
.doc-overlay:has(.doc-viewer.is-maximized) { padding: 0; }
.dv-head {
  display: flex; align-items: center; gap: 12px; flex: 0 0 auto;
  min-height: 52px; padding: 8px 12px 8px 18px;
  border-bottom: 1px solid var(--line-soft);
}
.dv-title {
  margin: 0; min-width: 60px; flex: 0 1 auto;
  font-family: var(--ui); font-size: 15px; font-weight: 700; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dv-meta { flex: 0 0 auto; font-family: var(--ui); font-size: 12px; color: var(--mute); white-space: nowrap; }
.dv-meta:empty { display: none; }
.dv-tools { display: flex; align-items: center; gap: 8px; margin-left: auto; min-width: 0; }
/* FilePreview's toolbar, moved onto the viewer's one header line. */
.dv-head > .tx-toolbar {
  flex: 1 1 auto; min-width: 0; margin: 0; padding: 0; border: 0; background: none;
  flex-wrap: nowrap; justify-content: flex-end;
}
.dv-head > .tx-toolbar .tx-group-end { margin-left: 0; }
.dv-zoom, .dv-window { position: relative; display: inline-flex; align-items: center; gap: 2px; flex: 0 0 auto; }
.dv-zoom { padding-left: 10px; border-left: 1px solid var(--line-soft); }
.dv-window { padding-left: 10px; border-left: 1px solid var(--line-soft); }
.dv-zoom-value {
  min-width: 56px; padding: 5px 6px; border: 1px solid transparent; border-radius: var(--r-sm);
  background: none; color: var(--ink); font-family: var(--ui); font-size: 12.5px;
  font-variant-numeric: tabular-nums; cursor: pointer;
}
.dv-zoom-value:hover { border-color: var(--line); }
.dv-zoom-value:focus-visible, .dv-zoom-item:focus-visible, .dv-canvas:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.dv-zoom-menu {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 5;
  display: flex; flex-direction: column; min-width: 140px; padding: 6px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-sm);
  box-shadow: var(--shadow);
}
.dv-zoom-item {
  padding: 6px 10px; border: 0; border-radius: var(--r-sm); background: none; color: var(--ink);
  font-family: var(--ui); font-size: 13px; text-align: left; cursor: pointer;
}
.dv-zoom-item:hover { background: var(--surface-2); }
.dv-zoom-item.is-on { font-weight: 700; color: var(--accent); }
.dv-zoom-sep { height: 1px; margin: 4px 2px; background: var(--line-soft); }
/* The rest of FilePreview (translation result, progress, notices) sits between the header
   and the canvas, and the canvas takes the room left. */
.doc-viewer > .mail-notice, .doc-viewer > .tx-result, .doc-viewer > .work-progress-panel,
.doc-viewer > .docpv-onedrive-error { flex: 0 0 auto; margin: 8px 16px 0; }
.doc-viewer > .simple-canvas-viewer-body { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
.dv-canvas {
  flex: 1 1 auto; min-height: 0; overflow: auto;
  padding: 24px; background: var(--bg-2);
  touch-action: pan-x pan-y;
}
.dv-canvas.is-dragging { cursor: grabbing; user-select: none; }
.dv-canvas.is-plain { padding: 0; background: none; }
/* A page larger than the canvas scrolls both ways, and a smaller one sits in the middle. */
.dv-canvas .docpv-stack { display: flex; flex-direction: column; align-items: center; gap: 16px; width: max-content; min-width: 100%; margin: 0 auto; }
.dv-canvas .docpv-page { max-width: none; background: #FFFFFF; box-shadow: 0 1px 3px rgba(0, 0, 0, .18), 0 6px 18px rgba(0, 0, 0, .10); }
.dv-canvas .work-preview-doc { width: max-content; min-width: 100%; }
.dv-canvas .work-preview-image.is-zoomable { display: block; max-width: none; max-height: none; margin: 0 auto; box-shadow: 0 1px 3px rgba(0, 0, 0, .18); }
.dv-canvas > .work-preview-frame, .dv-canvas > iframe { width: 100%; height: 100%; min-height: 60vh; border: 0; background: var(--surface); }
/* OnlyOffice has its own zoom and fills the canvas edge to edge. */
.dv-canvas:has(> .oo-editor), .dv-canvas:has(> .onlyoffice-host) { padding: 0; }
.dv-zoom-box { width: max-content; min-width: min(100%, 860px); margin: 0 auto; background: var(--surface); }
body.dv-open { overflow: hidden; }

/* Phone: the viewer is the whole screen, and close is a large target at the top right. */
@media (max-width: 720px) {
  .doc-overlay { padding: 0; }
  .doc-viewer { width: 100vw; max-width: none; height: 100dvh; border-radius: 0; border: 0; }
  .dv-head { flex-wrap: wrap; gap: 6px; padding: 6px 8px 6px 12px; }
  .dv-title { flex: 1 1 0; }
  .dv-head > .dv-window { order: 2; }
  .dv-head > .tx-toolbar { order: 3; flex: 1 1 100%; overflow-x: auto; justify-content: flex-start; }
  .dv-tools > .dv-window { margin-left: auto; }
  .dv-close { min-width: 44px; min-height: 44px; }
  .dv-meta { display: none; }
  .dv-canvas { padding: 12px; touch-action: none; }
}
/* Shared mailboxes (SB 2026-09-25): an added inbox in the Work > Mail rail, its "…" menu
   (shown on hover and focus), a warning mark when Graph refuses it, and the Add mailbox dialog. */
.mail-shared-more {
  padding: 0 4px; border-radius: var(--r-sm); color: var(--work-rail-mute);
  font-size: 13px; line-height: 1; opacity: 0;
}
.mail-shared-box:hover .mail-shared-more,
.mail-shared-box:focus-within .mail-shared-more,
.mail-shared-more:focus-visible { opacity: 1; }
.mail-shared-more:hover { background: var(--work-rail-hover); color: var(--work-rail-ink); }
.mail-shared-warn { font-family: var(--mono); font-size: 11px; font-weight: 700; color: var(--danger, var(--accent)); }
/* Room between the dialog's caption and the address label. */
.mail-add-form { margin-top: 14px; }
.mail-add-error { margin: 0; font-size: 13px; color: var(--danger, var(--accent)); }
.mail-add-other {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  padding-top: 10px; border-top: 1px solid var(--line); font-size: 12px; color: var(--mute);
}
.mail-add-other > span { flex-basis: 100%; }
/* Round 2 (SB 2026-09-26): the rail as Outlook draws it. One collapsible root per mailbox
   (caret, icon, name, unread count when shut, "…" on hover), its folders indented under it.
   The heading carries a "+" for Add a mailbox or account. */
.mail-boxes-label { display: flex; align-items: center; justify-content: space-between; }
.mail-boxes-add {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border: 0; background: none; border-radius: var(--r-sm); color: var(--work-rail-mute);
}
.mail-boxes-add:hover, .mail-boxes-add:focus-visible { background: var(--work-rail-hover); color: var(--work-rail-ink, var(--ink)); }
.mail-box-root + .mail-box-root { margin-top: 6px; }
.mail-box-head {
  display: flex; align-items: center; gap: 6px; min-height: 30px; padding: 0 6px;
  border-radius: var(--r-sm); cursor: pointer; font-size: 13px; font-weight: 600;
  color: var(--work-rail-ink, var(--ink));
}
.mail-box-head:hover { background: var(--work-rail-hover); }
.mail-box-head:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.mail-box-caret { display: inline-flex; color: var(--work-rail-mute); transition: transform 120ms ease; }
.mail-box-caret.is-open { transform: rotate(90deg); }
.mail-box-mark { display: inline-flex; flex: none; color: var(--work-rail-ink, var(--ink)); }
/* YUM-1547: a long connector address (25+ chars) was ellipsised to a fragment inside the
   190px folder column — 'Ben.Hippler@ba…' with the real address unrecoverable. There is no
   room to shorten an address to nothing, so it wraps instead, at any character, keeping the
   whole address on screen. Same rule as the .mcard h3 phone case above (white-space normal +
   overflow-wrap anywhere + overflow visible). Short names still sit on one line. */
.mail-box-name { flex: 0 1 auto; min-width: 0; white-space: normal; overflow-wrap: anywhere; overflow: visible; }
/* Count, warning and "…" sit at the right edge; the people mark stays next to the name. */
.mail-box-head > .mail-folder-count, .mail-box-head > .mail-shared-warn, .mail-box-more { margin-left: auto; }
.mail-box-head > .mail-folder-count + .mail-box-more, .mail-box-head > .mail-shared-warn + .mail-box-more { margin-left: 0; }
/* YUM-1547: opacity: 0 kept the hidden "…" button's width, which squeezed the name further.
   Hidden it now takes no space at all (width 0, no padding, clipped); hovering or focusing the
   row expands it back to its full footprint beside the reveal. */
.mail-box-more {
  width: 0; padding: 0; overflow: hidden;
  border-radius: var(--r-sm); color: var(--work-rail-mute);
  font-size: 13px; line-height: 1; opacity: 0;
}
.mail-box-head:hover .mail-box-more,
.mail-box-head:focus-within .mail-box-more,
.mail-box-more:focus-visible { opacity: 1; width: auto; padding: 0 4px; }
.mail-box-more:hover { background: var(--work-rail-hover); color: var(--work-rail-ink, var(--ink)); }
.mail-box-root > .ds-tree, .mail-box-root > div:not(.mail-box-head) { padding-left: 12px; }
/* The two ways into Add a mailbox or account, chosen first. */
.mail-add-choices { display: grid; gap: 8px; }
.mail-add-choice {
  display: flex; gap: 10px; align-items: flex-start; padding: 10px 12px;
  border: 1px solid var(--line); border-radius: var(--r-sm); cursor: pointer;
}
.mail-add-choice.is-on { border-color: var(--accent); }
.mail-add-choice input { margin-top: 3px; accent-color: var(--accent); }
.mail-add-choice-text { display: grid; gap: 2px; font-size: 13px; color: var(--mute); }
.mail-add-choice-text strong { color: var(--ink); font-weight: 600; }
.work-compose-from { font-size: 13px; color: var(--ink); }

/* ---- Video player (YUM-1467). The stage is always dark, like every video player: the
   picture needs a neutral surround in all four themes. The chrome around it uses tokens. */
.vp { outline: none; display: flex; gap: 12px; width: 100%; min-height: 0; color: var(--ink); font-family: var(--ui); }
.vp-stage { position: relative; flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; border-radius: var(--r-sm, 8px); overflow: hidden; background: #0b0b0b; outline: none; }
.vp-frame { position: relative; display: flex; align-items: center; justify-content: center; min-height: 200px; background: #0b0b0b; }
.vp-video { display: block; width: 100%; max-height: 66vh; background: #0b0b0b; cursor: pointer; }
.vp.is-full .vp-stage { border-radius: 0; }
.vp.is-full .vp-video { max-height: calc(100vh - 88px); height: calc(100vh - 88px); }
.vp.is-full { background: #0b0b0b; }
.vp-bigplay { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 72px; height: 72px; border-radius: 50%; border: 0; display: grid; place-items: center; background: rgba(0, 0, 0, 0.55); color: #fff; cursor: pointer; }
.vp-bigplay:hover, .vp-bigplay:focus-visible { background: var(--accent); color: var(--on-accent, #fff); }
.vp-wait { position: absolute; left: 12px; top: 12px; padding: 4px 10px; border-radius: var(--r-pill, 999px); background: rgba(0, 0, 0, 0.6); color: #fff; font-size: 12px; }
.vp-error { position: absolute; inset: auto 16px 16px 16px; padding: 10px 12px; border-radius: var(--r-sm, 8px); background: rgba(0, 0, 0, 0.75); color: #fff; font-size: 13px; }
.vp-controls { display: flex; flex-direction: column; gap: 4px; padding: 6px 10px 8px; background: #161616; color: #f2f2f2; }
.vp-row { display: flex; align-items: center; gap: 2px; min-height: 36px; }
.vp-spacer { flex: 1 1 auto; }
.vp-time { font-family: var(--mono); font-size: 12px; color: #d8d8d8; margin-left: 8px; white-space: nowrap; font-variant-numeric: tabular-nums; }
.vp-btn { display: inline-grid; place-items: center; min-width: 34px; height: 34px; padding: 0 6px; border: 0; border-radius: var(--r-xs, 6px); background: transparent; color: #f2f2f2; cursor: pointer; font: 600 12px var(--ui); }
.vp-btn:hover { background: rgba(255, 255, 255, 0.12); }
.vp-btn:focus-visible, .vp-seek:focus-visible, .vp-vol-range:focus-visible, .vp-bigplay:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.vp-btn[aria-pressed="true"] { color: var(--accent); }
.vp-speed { min-width: 44px; font-variant-numeric: tabular-nums; }
.vp-icon { display: block; }
.vp-vol { display: inline-flex; align-items: center; }
.vp-vol-range { width: 0; opacity: 0; accent-color: var(--accent); transition: width .15s ease, opacity .15s ease; }
.vp-vol:hover .vp-vol-range, .vp-vol:focus-within .vp-vol-range { width: 80px; opacity: 1; margin: 0 6px; }
.vp-seek { position: relative; height: 16px; display: flex; align-items: center; cursor: pointer; touch-action: none; }
.vp-seek progress { position: absolute; left: 0; right: 0; width: 100%; height: 4px; border: 0; border-radius: 2px; appearance: none; background: transparent; pointer-events: none; }
.vp-seek progress::-webkit-progress-bar { background: transparent; border-radius: 2px; }
.vp-seek-buf { background: rgba(255, 255, 255, 0.18); }
.vp-seek-buf::-webkit-progress-bar { background: rgba(255, 255, 255, 0.18) !important; }
.vp-seek-buf::-webkit-progress-value { background: rgba(255, 255, 255, 0.35); border-radius: 2px; }
.vp-seek-buf::-moz-progress-bar { background: rgba(255, 255, 255, 0.35); }
.vp-seek-fill::-webkit-progress-value { background: var(--accent); border-radius: 2px; }
.vp-seek-fill::-moz-progress-bar { background: var(--accent); }
.vp-seek:hover progress { height: 6px; }
.vp-seek-tip { position: absolute; bottom: 18px; transform: translateX(-50%); padding: 2px 6px; border-radius: var(--r-xs, 6px); background: rgba(0, 0, 0, 0.85); color: #fff; font: 12px var(--mono); pointer-events: none; white-space: nowrap; }
.vp-seek-tip[data-x="0"] { left: 0%; }
.vp-seek-tip[data-x="2"] { left: 2%; }
.vp-seek-tip[data-x="4"] { left: 4%; }
.vp-seek-tip[data-x="6"] { left: 6%; }
.vp-seek-tip[data-x="8"] { left: 8%; }
.vp-seek-tip[data-x="10"] { left: 10%; }
.vp-seek-tip[data-x="12"] { left: 12%; }
.vp-seek-tip[data-x="14"] { left: 14%; }
.vp-seek-tip[data-x="16"] { left: 16%; }
.vp-seek-tip[data-x="18"] { left: 18%; }
.vp-seek-tip[data-x="20"] { left: 20%; }
.vp-seek-tip[data-x="22"] { left: 22%; }
.vp-seek-tip[data-x="24"] { left: 24%; }
.vp-seek-tip[data-x="26"] { left: 26%; }
.vp-seek-tip[data-x="28"] { left: 28%; }
.vp-seek-tip[data-x="30"] { left: 30%; }
.vp-seek-tip[data-x="32"] { left: 32%; }
.vp-seek-tip[data-x="34"] { left: 34%; }
.vp-seek-tip[data-x="36"] { left: 36%; }
.vp-seek-tip[data-x="38"] { left: 38%; }
.vp-seek-tip[data-x="40"] { left: 40%; }
.vp-seek-tip[data-x="42"] { left: 42%; }
.vp-seek-tip[data-x="44"] { left: 44%; }
.vp-seek-tip[data-x="46"] { left: 46%; }
.vp-seek-tip[data-x="48"] { left: 48%; }
.vp-seek-tip[data-x="50"] { left: 50%; }
.vp-seek-tip[data-x="52"] { left: 52%; }
.vp-seek-tip[data-x="54"] { left: 54%; }
.vp-seek-tip[data-x="56"] { left: 56%; }
.vp-seek-tip[data-x="58"] { left: 58%; }
.vp-seek-tip[data-x="60"] { left: 60%; }
.vp-seek-tip[data-x="62"] { left: 62%; }
.vp-seek-tip[data-x="64"] { left: 64%; }
.vp-seek-tip[data-x="66"] { left: 66%; }
.vp-seek-tip[data-x="68"] { left: 68%; }
.vp-seek-tip[data-x="70"] { left: 70%; }
.vp-seek-tip[data-x="72"] { left: 72%; }
.vp-seek-tip[data-x="74"] { left: 74%; }
.vp-seek-tip[data-x="76"] { left: 76%; }
.vp-seek-tip[data-x="78"] { left: 78%; }
.vp-seek-tip[data-x="80"] { left: 80%; }
.vp-seek-tip[data-x="82"] { left: 82%; }
.vp-seek-tip[data-x="84"] { left: 84%; }
.vp-seek-tip[data-x="86"] { left: 86%; }
.vp-seek-tip[data-x="88"] { left: 88%; }
.vp-seek-tip[data-x="90"] { left: 90%; }
.vp-seek-tip[data-x="92"] { left: 92%; }
.vp-seek-tip[data-x="94"] { left: 94%; }
.vp-seek-tip[data-x="96"] { left: 96%; }
.vp-seek-tip[data-x="98"] { left: 98%; }
.vp-seek-tip[data-x="100"] { left: 100%; }
/* The frame preview above the cursor, like a YouTube seek bar. */
.vp-seek-tip.has-frame { padding: 3px; text-align: center; }
.vp-seek-frame { display: block; width: 160px; height: 90px; margin-bottom: 3px; border-radius: 4px; background: #0b0b0b; }
.vp-seek-time { display: block; }
.vp-preview-src { display: none; }
.vp-pop { position: relative; }
.vp-menu { position: absolute; right: 0; bottom: 40px; z-index: 5; min-width: 150px; max-height: 50vh; overflow: auto; padding: 4px; border-radius: var(--r-sm, 8px); background: var(--surface); color: var(--ink); border: 1px solid var(--line); box-shadow: var(--shadow); display: flex; flex-direction: column; }
.vp-menu-item { text-align: left; padding: 7px 10px 7px 28px; border: 0; border-radius: var(--r-xs, 6px); background: transparent; color: var(--ink); font: 13px var(--ui); cursor: pointer; position: relative; }
.vp-menu-item:hover, .vp-menu-item:focus-visible { background: var(--accent-soft); outline: none; }
.vp-menu-item.is-checked::before { content: ""; position: absolute; left: 11px; top: 50%; width: 7px; height: 7px; margin-top: -3.5px; border-radius: 50%; background: var(--accent); }
/* Captions: drawn by the player so their look follows the theme. */
.vp-cue { position: absolute; left: 5%; right: 5%; bottom: 6%; text-align: center; pointer-events: none; white-space: pre-line; }
.vp-cue-text { display: inline; padding: 2px 8px; line-height: 1.45; font: 600 20px/1.45 var(--ui); color: #fff; background: rgba(0, 0, 0, 0.72); box-decoration-break: clone; -webkit-box-decoration-break: clone; }
.vp.is-full .vp-cue-text { font-size: 28px; }
/* Video: the bar under the player, the transcript panel and minutes (YUM-1467 T7d-T7j). */
.vp-wrap { display: flex; flex-direction: column; gap: 8px; width: 100%; min-height: 0; }
.vp.has-side { align-items: flex-start; }
.vp-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; font-size: 13px; color: var(--mute); }
.vp-bar-btn { display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 12px; border: 1px solid var(--line); border-radius: var(--r-xs, 6px); background: var(--surface); color: var(--ink); font: 500 13px var(--ui); cursor: pointer; }
.vp-bar-btn:hover { background: var(--accent-soft); }
.vp-bar-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.vp-bar-btn:disabled { opacity: 0.55; cursor: default; }
.vp-bar-btn.is-primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent, #fff); }
.vp-bar-note { color: var(--mute); }
.vp-bar-status { color: var(--ink); }
.vp-bar-status.is-error { color: var(--danger, var(--accent)); }
.vp-hidden-input { display: none; }
.vp-btn:disabled { opacity: 0.4; cursor: default; }
.vp-side { flex: 0 0 340px; display: flex; flex-direction: column; min-height: 0; max-height: calc(66vh + 60px); border: 1px solid var(--line); border-radius: var(--r-sm, 8px); background: var(--surface); color: var(--ink); overflow: hidden; }
.vp.is-full .vp-side { max-height: 100vh; }
.vp-side-head { display: flex; align-items: center; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--line); }
.vp-side-title { font-weight: 600; font-size: 14px; }
.vp-side-sub { font-size: 12px; color: var(--mute); }
.vp-side-btn { display: inline-grid; place-items: center; width: 28px; height: 28px; border: 0; border-radius: var(--r-xs, 6px); background: transparent; color: var(--mute); cursor: pointer; }
.vp-side-btn:hover { background: var(--accent-soft); color: var(--ink); }
.vp-side-search { margin: 8px 12px; height: 30px; padding: 0 10px; border: 1px solid var(--line); border-radius: var(--r-xs, 6px); background: var(--bg); color: var(--ink); font: 13px var(--ui); }
.vp-side-search:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.vp-side-list { flex: 1 1 auto; overflow: auto; padding: 4px 6px; display: flex; flex-direction: column; }
.vp-line { display: flex; gap: 10px; text-align: left; padding: 6px 8px; border: 0; border-radius: var(--r-xs, 6px); background: transparent; color: var(--ink); font: 13px/1.45 var(--ui); cursor: pointer; }
.vp-line:hover { background: var(--accent-soft); }
.vp-line:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.vp-line.is-now { background: var(--accent-soft); box-shadow: inset 3px 0 0 var(--accent); }
.vp-line-time { flex: 0 0 auto; min-width: 44px; font: 12px/1.6 var(--mono); color: var(--accent); font-variant-numeric: tabular-nums; }
.vp-line-text b { font-weight: 600; }
.vp-side-empty { margin: 12px; color: var(--mute); font-size: 13px; }
.vp-side-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 8px 12px; border-top: 1px solid var(--line); }
.vp-side-act { display: inline-flex; align-items: center; height: 28px; padding: 0 10px; border: 1px solid var(--line); border-radius: var(--r-xs, 6px); background: transparent; color: var(--ink); font: 500 12px var(--ui); text-decoration: none; cursor: pointer; }
.vp-side-act:hover { background: var(--accent-soft); }
.vp-side-act:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.vp-side-act.is-primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent, #fff); }
.vp-side-msg { font-size: 12px; color: var(--mute); }
.vp-edit-row { display: flex; flex-direction: column; gap: 4px; padding: 6px; border-bottom: 1px solid var(--line); }
.vp-edit-times { display: flex; gap: 6px; }
.vp-edit-time { width: 96px; height: 26px; padding: 0 6px; border: 1px solid var(--line); border-radius: var(--r-xs, 6px); background: var(--bg); color: var(--ink); font: 12px var(--mono); }
.vp-edit-text { resize: vertical; padding: 4px 6px; border: 1px solid var(--line); border-radius: var(--r-xs, 6px); background: var(--bg); color: var(--ink); font: 13px/1.4 var(--ui); }
.vp-edit-time:focus-visible, .vp-edit-text:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.vp-minutes { border: 1px solid var(--line); border-radius: var(--r-sm, 8px); background: var(--surface); }
.vp-minutes-text { margin: 0; padding: 10px 12px; max-height: 40vh; overflow: auto; white-space: pre-wrap; font: 13px/1.5 var(--ui); color: var(--ink); }

/* Redline p10.1 (2026-09-27): the New button is its icon — a square beside the caret, not a
   full-width bar with one glyph in it. */
html .new-menu-split,
html .new-menu-split.is-compact { width: auto; align-self: flex-start; }
html .new-menu-split .new-menu-main {
  flex: none; width: 32px; min-width: 32px; height: 32px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
}

/* Redline p21/p22 follow-up (2026-09-27): with the agenda/detail pane gone, the calendar is the
   only thing right of the rail, so it takes the list and reading columns together instead of
   sitting in the 340px list column with an empty pane beside it. */
.work-split.is-calendar { min-width: 0; }
.mail-shell > .work-split.is-calendar { grid-column: 2 / 4; }
/* YUM-1478 P6a: Calendar, People and the other areas without a folder column carry only the 52px
   app rail in the first track. The track keeps the rail width instead of the folder width, so no
   empty column sits between the rail and the page. Desktop only; the phone rules restack the frame. */
@media (min-width: 901px) {
  /* --work-rail-w is written inline by the column grip, so the track itself is set here. */
  .mail-shell:has(> .mail-rail > .work-rail-zone:only-child):not(:has(.work-folder-col)) { grid-template-columns: 52px var(--work-list-w, 340px) minmax(0, 1fr) 0; }
  .mail-shell.work-assist-open:has(> .mail-rail > .work-rail-zone:only-child):not(:has(.work-folder-col)) { grid-template-columns: 52px var(--work-list-w, 340px) minmax(0, 1fr) 296px; }
  .mail-shell:has(> .mail-rail > .work-rail-zone:only-child):not(:has(.work-folder-col)) > .work-rail-grip { display: none; }
  /* People (and any area whose only extra is a short filter group) keep that group, at a narrow
     width, instead of a folder-width track: 52px rail plus a 168px filter column. */
  .mail-shell:not(.is-two-column):has(> .mail-rail > .work-rail-group):not(:has(.work-folder-col)) { grid-template-columns: 220px var(--work-list-w, 340px) minmax(0, 1fr) 0; }
  .mail-shell.work-assist-open:not(.is-two-column):has(> .mail-rail > .work-rail-group):not(:has(.work-folder-col)) { grid-template-columns: 220px var(--work-list-w, 340px) minmax(0, 1fr) 296px; }
  /* That first track is a fixed width, so the rail grip has nothing to size. Left in place it sat
     at --work-rail-w (190px by default), inside the 220px filter column, and its accent line ran
     through the filter chips. The list grip moves to the real column edge as well. */
  .mail-shell:not(.is-two-column):has(> .mail-rail > .work-rail-group):not(:has(.work-folder-col)) > .work-rail-grip { display: none; }
  .mail-shell:not(.is-two-column):has(> .mail-rail > .work-rail-group):not(:has(.work-folder-col)) > .work-list-grip { left: calc(220px + var(--work-list-w, 340px)); }
  .mail-shell:not(.is-two-column):has(> .mail-rail > .work-rail-group):not(:has(.work-folder-col)) { grid-template-columns: 260px var(--work-list-w, 340px) minmax(0, 1fr) 0; }
  .mail-shell.work-assist-open:not(.is-two-column):has(> .mail-rail > .work-rail-group):not(:has(.work-folder-col)) { grid-template-columns: 260px var(--work-list-w, 340px) minmax(0, 1fr) 296px; }
}

/* Redline p1.2 "remove the frames around all the icons" and p5.1/p33.1/p39.1 "remove all
   horizontal lines" (2026-09-27): the base letterpress theme re-drew both at a higher
   specificity than the first pass, so these carry :root to win under every theme. */
:root .topnav-cluster .icbtn.nav-icon-red,
:root .top > .icbtn.nav-icon-red,
:root .model-toggle-wrap .model-lock-standalone { border-color: transparent; box-shadow: none; }
:root .topnav-cluster .icbtn.nav-icon-red:hover,
:root .model-toggle-wrap .model-lock-standalone:hover { background: var(--surface-2); }
:root .yb-win .setlink,
:root .yb-win .setlink:last-child { border-bottom: 0; }
:root .yb-win .setgroup { border: 0; box-shadow: none; }

/* Ben 2026-09-27, landing page: "remove these edges outer lines", and white only. No outline
   around the sign-in card, the feature cards, the security panel and its live check, no rule
   under the header or over the footer; the security panel is white like the page. */
:root .public-nav-band,
:root .public-footer { border: 0; }
:root .authc,
:root .public-feature-card,
:root .public-security-banner,
:root .livecheck { border: 0; box-shadow: none; }
:root .public-security-banner { background: var(--surface); }
:root .livecheck-head,
:root .livecheck-nostore,
:root .livecheck-padlock-slot,
:root .livecheck-models,
:root .livecheck-hosting { border-top: 0; border-bottom: 0; }
/* ---- Redline round 3 (2026-09-27), pages 26/28/29/32: denser icon grid, tile metadata,
   a slimmer document-viewer head, and the +New button that folds to an icon on narrow
   screens. No backgrounds are set here: every surface stays whatever the theme's plain
   white gives it. */

/* p26.4 "more dense": smaller tiles, tighter grid — roughly half the air. */
.fm-grid { grid-template-columns: repeat(auto-fill, minmax(90px, 1fr)); gap: 6px 6px; padding: 4px; }
.fm-item-icons { gap: 1px; padding: 6px 4px; }
.fm-item-icons .ft-icon { margin-bottom: 3px; }
.fm-item-icons .fm-name { font-size: 12.5px; }

/* p28.1/p29.1/p29.2/p29.4/p32.1 "Compress": one thin line above the document, not a band. */
.dv-head { min-height: 38px; padding: 4px 10px 4px 14px; gap: 8px; }
.dv-title { font-size: 13.5px; }
.dv-meta { font-size: 11.5px; }
.dv-zoom, .dv-window { padding-left: 8px; }
.dv-zoom-value { min-width: 48px; padding: 2px 4px; font-size: 11.5px; }
.dv-head > .tx-toolbar .tx-lang-btn, .dv-head > .tx-toolbar button { font-size: 12px; }

/* The Files bar's +New keeps its label until the bar gets tight, then folds to the icon
   alone (title still says "New"). */
.fm-new-label { display: inline; }
@media (max-width: 900px) {
  .fm-new { gap: 0; padding-inline: 8px; }
  .fm-new .fm-new-label { display: none; }
  .fm-filter { width: 110px; }
}

/* Redline p34.2 / p35.1 / p35.2 / p36.1 / p37.1 (round 3, 2026-09-27): "Compress" and
   "Compress & redesign" on Account, Connections, Mail and Custom skills. No boxes, about
   half the padding, smaller type; the granted-permissions list "much much smaller" in two
   narrow columns. :root so the letterpress base theme cannot pad them back out. */
:root .yb-win .settings-row-pad { padding: 3px 8px; }
:root .yb-win .usage-hero { font-size: 13px; }
:root .yb-win .usage-meter-block { gap: 2px; }
:root .yb-win .plan-row { padding: 4px 6px; border: 0; box-shadow: none; }
:root .yb-win .plan-row.is-current { background: var(--accent-soft); }
:root .yb-win .plan-row-desc { font-size: 11px; line-height: 1.3; }
:root .yb-win .conn-account-card { padding: 2px 0; border: 0; box-shadow: none; }
:root .yb-win .conn-account-card .srv { padding: 2px 0; }
:root .yb-win .conn-cap-list { columns: 2; column-gap: 16px; margin: 0; padding-left: 12px; }
:root .yb-win .conn-cap-item { font-size: 11px; line-height: 1.35; break-inside: avoid; }
:root .yb-win .conn-cap-group-title { font-size: 11px; margin: 2px 0 0; }
:root .yb-win .section-gap-18 { margin-top: 6px; }
:root .yb-win .ds-notice { padding: 3px 0; font-size: 12px; line-height: 1.4; border: 0; background: var(--surface); }
:root .yb-win .mcp-directory-grid { grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: 2px; }
:root .yb-win .mcp-symbol-card { padding: 4px 2px; border: 0; box-shadow: none; }
:root .yb-win .mail-automation.automation-form-card { padding: 0; border: 0; box-shadow: none; }
:root .yb-win .mail-lang-select-row { padding: 2px 0; }
:root .yb-win .mail-automation-toggle { padding: 2px 0; }
:root .yb-win .custom-skill-card { padding: 4px 2px; }
:root .yb-win .action-row { gap: 6px; }
:root .yb-win .action-row .btn { padding: 4px 10px; font-size: 12.5px; }

/* Redline p1.4 "make this more prominent" and p1.6 "remove frames" (round 3): the + is the
   brick accent at a larger glyph; the send arrow loses its ring. */
:root form.box .composer-plus-prominent { color: var(--accent); }
:root form.box .composer-plus-prominent svg { width: 24px; height: 24px; stroke-width: 2.2; }
:root form.box .send { border: 0; box-shadow: none; }

/* Redline p2.1 "make all objects with 6px corner radius" and p2.3 "less space" (round 3):
   the security popup itself, its close and action buttons and its checkbox panel. */
:root .modal .mcard:has(.sec-panel) { border-radius: 6px; overflow: hidden; }
:root .mcard:has(.sec-panel) .modal-close-btn,
:root .sec-actions .btn,
:root .sec-e2e-only-row,
:root .sec-e2e-only-check { border-radius: 6px; }
:root .sec-e2e-only-row { margin-top: 6px; }
:root .sec-actions { margin-top: 4px; }
/* Round 3 follow-up (checked live): the rules above lost to layout rules, not to colours. */
:root .yb-win .plan-row { min-height: 0; margin-top: 2px; }
:root .yb-win .usage-hero-plan { font-size: 15px; }
:root .yb-win .conn-cap-list { display: block; }
:root .yb-win .conn-cap-item { display: list-item; list-style: disc inside; }
:root .yb-win .mail-automation-name { font-size: 13px; }
:root .yb-win .mail-lang-select { padding: 2px 6px; }

/* Round 3: the app-wide right-click menu (p31.2) spaces its icon from the label. */
:root .yb-context-item { display: flex; align-items: center; gap: 8px; }

/* Redline p38.2 / p38.3 "fix the design", "proper formatting, design, buttons, UX" (round 3):
   automations and their run history are plain rows, not stacked framed boxes; one line
   per run, 6px radii on the controls, the modal card without its heavy top rule. */
:root .automations-modal-body .srv.action-row { padding: 4px 2px; margin-bottom: 0; border: 0; border-bottom: 1px solid var(--line-soft); box-shadow: none; border-radius: 0; }
:root .automations-modal-body .run-row-clickable .s { display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden; }
:root .automations-modal-body .run-row-clickable:hover { background: var(--accent-soft); }
:root .automations-modal-body .trust.inkline { padding: 1px 6px; }
:root .automations-modal-body .automation-select,
:root .automations-modal-body .btn { border-radius: 6px; }
:root .automations-modal-body .section-divided { border-top: 0; padding-top: 6px; }
:root .modal .mcard:has(.automations-modal-body) { border-top-width: 1px; border-radius: 6px; }

/* Redline p15.6 / p15.7 / p18.1 "reduce", "compress" (round 3, checked on a real inbox): mail
   rows were 79px. Smaller avatar, tighter lines and padding bring a three-line row to ~58px. */
:root .mail-item { min-height: 0; padding: 4px 10px 4px 7px; gap: 8px; }
:root .mail-avatar.is-row { width: 28px; height: 28px; font-size: 11.5px; margin-top: 2px; }
:root .mail-item-open { gap: 0; }
:root .mail-item-top, :root .mail-item-subject-line { height: 17px; }
:root .mail-item-from, :root .mail-item-subject { font-size: 13px; line-height: 17px; }
:root .mail-item-preview { font-size: 12px; line-height: 16px; }
:root .mail-item:hover .mail-item-subject-line,
:root .mail-item:focus-within .mail-item-subject-line { padding-right: 104px; }
:root .mail-item-hover { top: 20px; }

/* Redline p19.1 "exactly like MS Outlook web" (round 3, seen on a real calendar): a 15-minute
   meeting squeezed its title to 1px and showed only its times. The title now never shrinks and
   comes first; the time line is what gets clipped. */
:root .work-tg-ev-title { flex: none; font-size: 11.5px; line-height: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
:root .work-tg-ev-time { flex: none; }

/* Redline p25.2: a Teams channel's folders, nested under it in the rail. */
.channel-folder-tree { padding-left: 14px; }

/* Redline p6.2 "no frame, just slightly default shaded" and p6.3 "tickmark is mis-aligned"
   (read from the page itself, round 3): the four row actions are one frameless, lightly
   shaded button; each step's tick sits on its title line instead of floating between lines. */
.gs-action { display: inline-flex; align-items: center; }
:root .yb-win .tlink.gs-action { padding: 3px 10px; border: 0; border-radius: 6px; background: var(--line-soft); color: var(--ink); text-decoration: none; font-size: 12.5px; }
:root .yb-win .tlink.gs-action:hover { background: var(--line); }
:root .yb-win .wizard-step-row { align-items: flex-start; }
:root .yb-win .wizard-step-glyph { margin-top: 1px; }
:root .yb-win .wizard-step-row > .tlink.gs-action { align-self: center; }

/* Redline p10.6 read from the page: "remove frames from all buttons" (not "names"), with
   p6.2's "no frame, just slightly default shaded" and p1.2 "remove the frames around all the
   icons". One rule for the whole app: secondary text buttons are frameless and lightly
   shaded; icon buttons are frameless and only shade on hover. The one filled primary button
   per surface (.solid, the red New) keeps its fill. */
/* YUM-1491: the filled primary is .solid OR .is-primary (the design system styles both
   identically at _ds_bundle.css), so a variant "primary" button must be excluded here too.
   Without it this rule shaded every .is-primary button with --line-soft, which is how the
   Automations Save button and the Create custom skill dialog ended up reading as disabled
   while they were enabled. Specificity is why the earlier "html .btn.is-primary" fix did not
   save them: :root .btn:not(.solid) weighs three classes against its two. */
:root .btn:not(.solid):not(.is-primary),
:root .work-cal-btn,
:root .mail-box-more { border-color: transparent; box-shadow: none; border-radius: 6px; background: var(--line-soft); color: var(--ink); }
:root .btn:not(.solid):not(.is-primary):hover,
:root .work-cal-btn:hover,
:root .mail-box-more:hover { background: var(--line); }
:root .btn.is-danger:not(.solid) { color: var(--danger); }
html:root .icbtn,
html:root .work-cal-view-icon { border-color: transparent; box-shadow: none; border-radius: 6px; }
html:root .icbtn:not(.is-on):not(.active):not(.is-danger) { background: transparent; }
html:root .icbtn:hover,
html:root .work-cal-view-icon:hover { background: var(--line-soft); }
:root .work-cal-view-icon.active { background: var(--accent-soft); }

/* Redline p13.1 read from the page ("compress, remove text, only show icons"): the New menu is
   a compact grid of icons, each named by its tooltip and aria-label, no group titles. */
:root .new-menu-list { display: grid; grid-template-columns: repeat(5, 36px); gap: 2px; padding: 6px; width: auto; }
:root .new-menu-list .link-context-menu-title { display: none; }
:root .new-menu-list .new-menu-item { width: 36px; height: 36px; padding: 0; justify-content: center; border-radius: 6px; }
:root .new-menu-list .new-menu-item-label { display: none; }
:root .new-menu-list .new-menu-item-icon { width: 18px; height: 18px; }

/* Redline p18 read from the page ("compress", arrow to the mail row's right-click menu): every
   context menu in the app runs at half the row height, no group gaps. */
:root .link-context-menu:not(.new-menu-list) .link-context-menu-item,
:root .yb-context-item { padding: 3px 10px; min-height: 0; font-size: 13px; line-height: 18px; }
:root .link-context-menu:not(.new-menu-list) .link-context-menu-title { padding: 4px 10px 1px; }
:root .link-context-menu-sep { margin: 2px 0; }

/* YUM-1470 (SB 2026-09-25, brief T8): ONE highlight for the whole top bar.
   T8a/T8b  hovering or keyboard-focusing ANY top-bar control (the lock, the plan toggle,
            the Chat stop, Work, Chief of staff, Settings) shows the current-page highlight
            (accent-soft, same shape) drawn lighter, so the current page still stands out.
            The old opacity fade is removed above; the lock keeps its red/green icon colour.
   T8c      a 120 ms fade in and out, and none at all under prefers-reduced-motion.
   T8d/T8e  background only — no size, border or radius change, so nothing moves or jumps —
            and the colour is the theme's own --accent-soft, so Default and Classic, light
            and dark (and every other theme) each get their own soft red.
   T8h/T8i  the labelled Chat stop takes its height, corner radius and border from the SAME
            declaration as the RoundIconButton icons (.icbtn), so the two cannot drift apart
            again; only its width differs, because Chat carries a word.
   The `html` prefix is deliberate: it outranks letterpress.css's `[data-theme="letterpress"]
   .icbtn`/`.topnav-chat` (0,2,0) without an !important, so one shared box holds in every
   theme and the per-theme copies in layouts.css / letterpress.css cannot win the cascade. */
.top { --topnav-control-h: 36px; --topnav-control-r: 18px; }
html .top .icbtn,
html .top .topnav-chat {
  height: var(--topnav-control-h);
  border-radius: var(--topnav-control-r);
  border: 1px solid transparent;
  background: transparent;
  box-shadow: none;
  transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
}
/* Chat carries a word: only its width and inner spacing differ from the icon buttons. */
html .top .topnav-chat { width: auto; padding: 0 12px; gap: 6px; }
html .top .model-lock-standalone { transition: background-color 120ms ease; }
html .top .icbtn:is(:hover, :focus-visible),
html .top .topnav-chat:is(:hover, :focus-visible),
html .top .model-lock-standalone:is(:hover, :focus-visible) {
  background: color-mix(in srgb, var(--accent-soft) 55%, transparent);
  opacity: 1;
}
html .top .icbtn.active:is(:hover, :focus-visible),
html .top .topnav-chat.active:is(:hover, :focus-visible) { background: var(--accent-soft); }
@media (prefers-reduced-motion: reduce) {
  html .top .icbtn,
  html .top .topnav-chat,
  html .top .model-lock-standalone { transition: none; }
}

/* The quiet top bar (Ben, 2026-10-18: "less text, more icons and buttons"; DESIGN.md,
   Navigation). Desktop only, meaning wider than 600px: at 600px and below the controls float
   as 44px round icons (mobile-ergonomics.css, and isMobile in main.js), which is the phone
   row. Between 601 and 760px this bar applies, as the worded bar did before it. Places, a hairline, tools. Every control is a 40px round icon in
   --mute; the open one is --accent on --accent-soft and is the only one that shows its word.
   `:root` outranks the letterpress `.model-lock-standalone` and `.nav-icon-red` colours. */
.topnav-places,
.topnav-tools { gap: 4px; }
/* A phone keeps its row as it was: no hairline, and the lock first. The two groups dissolve
   into the row so the lock can lead it again. */
@media (max-width: 600px) {
  .top .topnav-places,
  .top .topnav-tools { display: contents; }
  .top .topnav-tools .model-toggle-wrap { order: -1; }
}
@media (min-width: 601px) {
  html .top { --topnav-control-h: 40px; --topnav-control-r: 20px; }
  .top .topnav-places + .topnav-tools { padding-left: 12px; margin-left: 4px; border-left: 1px solid var(--line); }
  :root .top .topnav-chat,
  :root .top .model-lock-standalone {
    width: var(--topnav-control-h); height: var(--topnav-control-h); padding: 0; gap: 7px;
    justify-content: center; border-radius: var(--topnav-control-r);
    color: var(--mute); font-size: 13px;
  }
  :root .top .topnav-chat .topnav-chat-label { display: none; }
  :root .top .topnav-chat:is(:hover, :focus-visible),
  :root .top .model-lock-standalone:is(:hover, :focus-visible) { color: var(--ink); transform: none; }
  :root .top .topnav-chat:is(.active, [aria-pressed="true"]) {
    width: auto; padding: 0 14px 0 12px; color: var(--accent); background: var(--accent-soft);
  }
  :root .top .topnav-chat:is(.active, [aria-pressed="true"]) .topnav-chat-label { display: inline; }
  /* The lock keeps its one colour cue: it turns --accent once the security path is verified. */
  :root .top .model-lock-standalone.is-ok { color: var(--accent); }
}
/* Redline 2026-10-01 p6c: message list prominence. Unread reads as unread (accent dot,
   bold, stronger accent), read is quieter (lower ink weight, dimmer), and every row carries
   the everyday object icons: draft (✎), attachment (📎), the follow-up flag (⚑), plus the
   newest message's snippet under the row (dark mode passes right along with the theme). */
.email-row-head { font-weight: 400; color: var(--mute); }
.email-row.unread .email-row-head { font-weight: 700; color: var(--ink); }
.email-row.unread .email-symbol-flag { color: var(--accent); }
.email-row.unread .email-snippet { color: var(--ink-2); }
.email-row .email-snippet { margin: 0 12px 8px; padding: 0 12px; font-family: var(--ui); font-size: 12px; color: var(--mute); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.email-symbol-followup { color: var(--accent); }
.email-row.is-action .email-symbol-flag { color: var(--accent); }

/* Redline 2026-10-01, items G1 and G2:
   G1 - one standard 6px corner radius throughout the app's containers. The design tokens
   carry it (--r, --r-sm, --r-xs) so every existing rule reads the same number; --r-pill
   keeps its own work. The token override lands on :root and on the theme roots so no
   theme can disagree.
   G2 - a visible scrollbar wherever content scrolls: thin, themed, with a thumb that
   darkens on hover. Firefox gets the same through scrollbar-width/scrollbar-color. The
   few rules that sprite a scrollbar off on purpose keep their own. */
:root, body, [data-theme] { --r: 6px; --r-sm: 6px; --r-xs: 6px; }
.yarnball-app ::-webkit-scrollbar { width: 10px; height: 10px; }
.yarnball-app ::-webkit-scrollbar-track { background: transparent; }
.yarnball-app ::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--mute) 45%, transparent);
  border-radius: 6px;
  border: 2px solid transparent;
  background-clip: padding-box;
}
.yarnball-app ::-webkit-scrollbar-thumb:hover { background: color-mix(in srgb, var(--mute) 70%, transparent); }
body, .yarnball-app, .yarnball-app * { scrollbar-width: thin; scrollbar-color: color-mix(in srgb, var(--mute) 50%, transparent) transparent; }

/* Redline 2026-10-01, item G4: the account usage section reads as one tidy block. The
   stat strip becomes a grid of three aligned panels, a plan row keeps its rounded 6px
   corner and a hair wider gap, and the meter sits within the same padding so the whole
   block lines up edge to edge. Nothing is removed; only the layout cleans up. */
.usage-stat-strip { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; align-items: center; }
.usage-stat-strip .usage-stat-sep { display: none; }
.usage-stat-strip .usage-stat { justify-self: start; display: flex; gap: 5px; padding: 6px 10px; border: 1px solid var(--line-soft); border-radius: 6px; background: var(--surface-2); }
.usage-hero { border-radius: 6px; }
.plan-row { border-radius: 6px; }
.usage-meter-block { border-radius: 6px; padding: 2px 0; }

/* P6c: the row snippet is its own element, not only a child of an unread row. */
.email-snippet { display: block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* G1 (redline round 2): the design token is 6px in every theme, but a number of older
   component rules carry their own radius. The four pages Ben reviews — Chat, Mail,
   Calendar and chief of staff — read the token on the visible containers, rows, buttons,
   tags and inputs; genuinely round shapes keep their pill. One block, late in the sheet,
   so no component has to be re-authored. */
.email-status { border-radius: var(--r-xs); }
.mail-item { border-radius: var(--r-xs); }
.mail-item-top, .mail-item-from, .mail-subject { border-radius: 0; }
.mail-search { border-radius: var(--r-xs); }
.email-row-shell { border-radius: var(--r-xs); }
.new-menu-main, .new-menu-caret, .new-menu-split .new-menu-main, .new-menu-split .new-menu-caret { border-radius: var(--r-xs); }
.work-app-rail-btn, .work-folder-col .new-menu-split .new-menu-main, .work-folder-col .new-menu-split .new-menu-caret { border-radius: var(--r-xs); }
.mail-folder, .ds-tree-node { border-radius: var(--r-xs); }
.yarnball-main .btn, .yarnball-main .btn.solid { border-radius: var(--r-xs); }
.mail-tag, .mail-action-tag, .upload-state-chip, .quick-reply-chip, .attachment-file-project { border-radius: var(--r-xs); }
.top .btn, .top .btn.solid, .chat-input-row .btn { border-radius: var(--r-sm); }
[data-theme="xp"] .work-app-rail-btn, [data-theme="xp"] .work-slim-col-btn, [data-theme="xp"] .mail-folder, [data-theme="xp"] .mail-item { border-radius: var(--r-xs); }
[data-theme="xp"] .new-menu-main, [data-theme="xp"] .new-menu-caret { border-radius: var(--r-xs); }
.yarnball-app .mail-row-chip, .yarnball-app .work-cal-chip { border-radius: var(--r-xs); }
/* Mail rows and their chips at 6px from this sheet; the pill stays only on round assets:
   avatars, connection toggles, progress meters, the composer send button. */
.mail-avatar { border-radius: 999px; }
.conn-switch-track, .wizard-progress-meter, .sync-progress-track { border-radius: 999px; }
/* G1c: nothing left here for the chief-of-staff screen; its section is COS-CARDS at the end. */

/* Public entry accessibility and deferred background media, GH-1042. */
.auth-page .fld label {
  color: var(--ink-2);
  font-size: 12px;
  line-height: 1.5;
}
.auth-page .fld input {
  min-height: 44px;
  font-size: 16px;
}
.auth-page .atabs > button.atab {
  min-height: 44px;
  border: 0;
  font-family: inherit;
  font-size: 14px;
}
.auth-page .atabs > button.atab:not(.on) {
  color: var(--ink-2);
  background: transparent;
}
.auth-page .authc button,
.auth-page .public-nav-cta,
.auth-page .public-nav-link {
  min-height: 44px;
}
.auth-page .public-nav-link {
  display: inline-flex;
  align-items: center;
}
.auth-page .authc .or,
.auth-page .authc .hint {
  color: var(--ink-2);
  font-size: 12px;
  line-height: 1.5;
}
.auth-page .aerr {
  font-size: 14px;
  line-height: 1.5;
}
.auth-page button:focus-visible,
.auth-page a:focus-visible,
.auth-page input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}
.auth-page .public-footer-copy {
  color: var(--ink-2);
  font-size: 13px;
}
.public-hero-poster {
  display: block;
}
.public-hero-motion {
  position: absolute;
  right: 0;
  bottom: 8px;
  min-height: 44px;
  padding: 8px 12px;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}
@media (max-width: 760px) {
  .auth-page .fld input,
  .auth-page .authc button,
  .auth-page .atabs > button.atab,
  .auth-page .public-nav-cta,
  .auth-page .public-nav-link,
  .auth-page .public-hero-motion {
    min-height: 48px;
  }
}

.public-footer .public-footer-copy { color: var(--ink-2); font-size: 13px; line-height: 1.5; }

.sp-page .sp-diag-kind,
.sp-page .sp-diag-wire span,
.sp-page .sp-diag-cap { color: var(--ink-2); font-size: 12px; line-height: 1.5; }
.sp-page .sp-diag-sub { color: var(--ink-2); font-size: 14px; line-height: 1.5; }
.sp-page .sp-diag-wire .sp-diag-arrow { font-size: 18px; }
@media (max-width: 720px) {
  .sp-page .sp-diagram { flex-direction: column; }
  .sp-page .sp-diag-wire { width: auto; padding: 8px 0; }
  .sp-page .sp-diag-arrow { transform: rotate(90deg); }
}

.pricing-page .pricing-notes,
.pricing-page .pricing-footnote,
.pricing-page .legal-lede { max-width: 70ch; }
.public-shell.pricing-page .hint { color: var(--ink-2); font-family: var(--ui); font-size: 14px; line-height: 1.5; text-transform: none; letter-spacing: normal; text-align: left; max-width: 70ch; }

.yb-b-empty {
  margin-block: auto;
  padding-block: 16px;
  max-width: 46ch;
  font-size: 16px;
  line-height: 1.5;
  color: var(--mute);
}

/* Task controls, request feedback and rows share one work column. */
.todo-main {
  --todo-measure: 1000px;
  --todo-gutter: 24px;
  padding-top: 24px;
}
.work-frame[data-source="todo"] .mail-rail {
  flex-direction: column;
}
.work-frame[data-source="todo"] .mail-rail > .work-rail-zone {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
}
.work-frame[data-source="todo"] .mail-rail > .todo-sync {
  flex: 0 0 auto;
  flex-direction: row;
  margin-top: 0;
  padding: 12px;
}
.work-frame[data-source="todo"] .mail-rail > .work-sources-viewport-phone { display: none; }
.work-folder-col[data-source="todo"] .todo-lists {
  gap: 4px;
  padding: 8px 0;
  margin: 0;
  border: 0;
}
.work-folder-col[data-source="todo"] :is(.todo-rail-item, .todo-rail-newlist) {
  min-height: 40px;
  padding: 8px 10px;
  font-size: 14px;
}
.work-folder-col[data-source="todo"] .todo-rail-newlist {
  margin-top: 10px;
  border-top: 1px solid var(--line-soft);
  border-radius: 0;
}
.work-folder-col[data-source="todo"].is-slim .todo-lists { align-items: center; }
.work-folder-col[data-source="todo"].is-slim :is(.todo-rail-item, .todo-rail-newlist) {
  width: 40px;
  justify-content: center;
  padding-inline: 8px;
}
.work-folder-col[data-source="todo"].is-slim :is(.todo-rail-label, .todo-rail-count) {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.todo-main > :is(.todo-head, .todo-add, .todo-status, .todo-body, .todo-empty, .todo-sections) {
  box-sizing: border-box;
  width: min(calc(100% - var(--todo-gutter) * 2), var(--todo-measure));
  margin-inline: auto;
}
.todo-main > .todo-head { padding: 0 0 16px; gap: 10px; }
.todo-main .todo-head-title { margin-right: 8px; }
.todo-main .todo-head-text { font-size: 24px; font-weight: 600; }
.todo-main .todo-head-search { min-height: 36px; padding: 0 12px; max-width: 320px; }
.todo-main .todo-search { font-size: 14px; }
.todo-main :is(.todo-head-btn, .todo-mode) { min-height: 34px; min-width: 34px; justify-content: center; }
.todo-main .todo-head-right { gap: 4px; }
.todo-main > .todo-add { margin-top: 20px; margin-bottom: 0; padding: 10px 12px; }
.todo-main .todo-add-input { min-height: 32px; font-size: 16px; }
.todo-main .todo-add-go { min-height: 36px; }
.todo-main > .todo-body { padding: 12px 0 24px; }
.todo-main .todo-row { min-height: 48px; padding: 8px; }
.todo-main .todo-row-title { font-size: 15px; }
.todo-main .todo-row-notes { font-size: 13px; }
.todo-main .todo-group-title { margin-block: 12px 6px; font-size: 13px; }
.todo-main > .todo-status {
  margin-top: 16px;
  padding: 16px 18px;
  border: 1px solid var(--line);
  background: var(--surface-2);
  color: var(--ink-2);
  font-size: 15px;
  line-height: 1.5;
}
.todo-main .todo-status h2 { margin: 0 0 6px; color: var(--ink); font-size: 20px; font-weight: 600; }
.todo-main .todo-status p { max-width: 62ch; margin: 0 0 12px; }
.todo-main .todo-status .btn { min-height: 36px; padding: 7px 14px; font-size: 14px; }
.todo-main > .todo-empty {
  flex: 0 0 auto;
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr);
  align-items: start;
  justify-content: start;
  column-gap: 14px;
  row-gap: 6px;
  padding: 32px 0;
  text-align: left;
}
.todo-main .todo-empty-plate {
  grid-row: span 2;
  width: 40px;
  height: 40px;
  margin: 0;
  border-radius: 0;
  background: transparent;
}
.todo-main .todo-empty-title { font-size: 20px; font-weight: 600; }
.todo-main .todo-empty-line { max-width: 52ch; font-size: 15px; line-height: 1.5; }
.todo-main .todo-empty[role="status"] { display: block; color: var(--mute); font-size: 15px; }

/* Higher scope than the later Letterpress skin: captions are sentences, not machine labels. */
html .settings-wrap.settings-home {
  max-width: 720px;
  padding: 20px 24px 32px;
}
html .settings-home .setgh {
  margin: 22px 0 8px;
  font-size: 11px;
  letter-spacing: .08em;
  color: var(--mute);
}
html .settings-home .setgroup {
  margin: 0;
  border: 0;
  border-top: 1px solid var(--line-soft);
  border-radius: 0;
  background: transparent;
  box-shadow: none;
}
html .settings-home > div:first-child .setgroup { border-top: 0; }
html .settings-home .setlink {
  min-height: 58px;
  padding: 11px 4px;
  gap: 14px;
  border: 0;
  align-items: center;
}
html .settings-home .setlink:hover { background: var(--surface-2); }
html .settings-home .setlink .sl-ic { width: 24px; height: 24px; flex: 0 0 24px; color: var(--mute); }
html .settings-home .setlink .sl-b { min-width: 0; }
html .settings-home .setlink .sl-l { font-size: 17px; line-height: 1.3; }
html .settings-home .setlink .sl-v {
  max-width: 58ch;
  margin-top: 4px;
  color: var(--mute);
  font-family: var(--ui);
  font-size: 14px;
  line-height: 1.45;
  text-transform: none;
  letter-spacing: normal;
  overflow-wrap: anywhere;
}
.settings-display {
  padding-bottom: 8px;
}
html .settings-home .settings-display .setrow {
  justify-content: flex-start;
  flex-wrap: wrap;
  gap: 12px 24px;
  padding: 14px 4px;
}
html .settings-home .settings-display .setrow > label { flex: 0 0 auto; font-size: 16px; }
html .settings-home .fontscale-row { flex-wrap: wrap; gap: 6px; }
html .settings-home .fontscale-btn { min-width: 54px; min-height: 38px; font-size: 14px; }
.settings-home :is(.setlink, button):focus-visible,
.todo-main :is(button, input, textarea):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}
@media (max-width: 760px) {
  .yarnball-shell:has(.yb-winlayer) > .top {
    box-sizing: border-box;
    height: 64px;
    flex: 0 0 64px;
    padding-block: 8px;
  }
  .yarnball-shell .yb-winlayer { top: 64px; }
  .todo-main { --todo-gutter: 16px; padding-top: 16px; }
  html .mail-rail .todo-lists > :is(.todo-rail-item, .todo-rail-newlist) { min-height: 48px; font-size: 14px; }
  .todo-main > .todo-head { gap: 8px; }
  .todo-main .todo-head-search { flex-basis: 100%; order: 3; max-width: none; min-height: 48px; }
  .todo-main .todo-head-right { margin-left: auto; }
  .todo-main .todo-add { flex-wrap: wrap; gap: 8px; }
  .todo-main .todo-add-input { flex-basis: calc(100% - 56px); min-width: 0; }
  .todo-main .todo-add-tools { margin-left: auto; }
  .todo-main :is(button, .todo-add-input, .todo-search) { min-height: 48px; }
  .todo-main :is(.todo-head-btn, .todo-mode, .todo-tool, .todo-tick, .todo-due, .todo-star, .todo-row-menu, .todo-row-expand, .todo-search-clear, .todo-detail-attach-remove) { min-width: 48px; align-items: center; justify-content: center; }
  .todo-main :is(input, textarea) { font-size: 16px; }
  .todo-main .todo-row { flex-wrap: wrap; gap: 4px; }
  .todo-main .todo-row-main { flex-basis: calc(100% - 60px); }
  .todo-main .todo-row-title { white-space: normal; line-height: 1.4; }
  .todo-main .todo-row-menu { opacity: 1; }
  .todo-main > .todo-empty { padding-block: 24px; }
  .todo-main .todo-detail { margin-left: 0; }
  html .settings-wrap.settings-home { padding: 16px 16px 32px; }
  html .settings-home .setlink { min-height: 64px; padding-block: 12px; }
  html .settings-home .settings-display .setrow { align-items: flex-start; flex-direction: column; gap: 10px; }
  html .settings-home .fontscale-btn { min-width: 56px; min-height: 48px; font-size: 16px; }
  .yb-winlayer .yb-win:has(.settings-home) { min-width: min(420px, 96vw); }
}
@media (max-width: 600px) {
  .work-frame[data-source="todo"] .mail-rail { flex-direction: row; padding: 8px 10px; }
  .work-frame[data-source="todo"] .mail-rail > .work-rail-zone { display: none; }
  .work-frame[data-source="todo"] .mail-rail > .work-sources { display: grid; flex: 0 0 100%; }
  .work-frame[data-source="todo"] .mail-rail > .todo-sync { flex: 1 1 100%; padding: 2px 2px 0; }
}

/* Settings: the connect button is an enabled neutral secondary that opens the Add connector flow, and
   the delete-account group is separated from Sign out and drawn in the destructive colour. */
.section-gap-18 .conn-add-secondary {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-pill);
  color: var(--ink-2); font: inherit; font-size: 13px; font-weight: 600; padding: 7px 14px;
  box-shadow: none; opacity: 1; cursor: pointer;
}
.section-gap-18 .conn-add-secondary:hover:not(:disabled) { border-color: var(--ink); color: var(--ink); background: var(--surface); }
.section-gap-18 .conn-add-secondary:disabled { opacity: 1; color: var(--mute); cursor: not-allowed; }
/* Connected account row: provider title once, the account in normal case, then a small status. */
.conn-row-head .conn-row-account { font-size: 13px; color: var(--ink-2); text-transform: none; letter-spacing: normal; font-family: inherit; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.conn-row-head .conn-row-status { font-size: 11.5px; color: var(--trust-green); text-transform: none; letter-spacing: normal; flex: none; }
.conn-icon-btn.conn-text-btn { width: auto; gap: 5px; padding: 0 10px; font: inherit; font-size: 12px; font-weight: 600; }
.conn-account-card .conn-icon-btn.conn-disconnect-icon { border-color: var(--danger, #d32f2f); color: var(--danger, #d32f2f); }
.conn-account-card .conn-icon-btn.conn-disconnect-icon:hover:not(:disabled) { background: var(--danger, #d32f2f); color: var(--on-accent, #fff); }
.setlink-danger { margin-top: 28px; }
.setlink-danger .setlink .sl-l,
.setlink-danger .setlink .sl-ic { color: var(--danger, #d32f2f); }
/* Mid widths (925px): rail rows must not shrink inside the scrolling flex column (Files printed
   its section labels on top of each other), and the reader header wraps instead of clipping
   subject and sender to a letter. The rail and list give ground before the reading pane. */
.mail-rail .work-rail-group { flex-shrink: 0; }
/* Files puts its Places, Connected, Teams and Recent groups straight beside the 52px app rail.
   The rail zone turns .mail-rail into a flex row, which laid the groups side by side and squeezed
   each name to a letter per line. A grid keeps the app rail in the first column and stacks the
   groups in the second; the last explicit row is 1fr so the tall app rail never spreads the groups.
   Names stay on one line with an ellipsis. */
@media (min-width: 901px) {
  .mail-rail:has(> .work-rail-zone):has(> .work-rail-group) {
    display: grid; grid-template-columns: 52px minmax(0, 1fr);
    grid-template-rows: repeat(8, max-content) 1fr; align-items: start; gap: 0; overflow-y: auto;
  }
  .mail-rail:has(> .work-rail-zone):has(> .work-rail-group) > .work-rail-zone { grid-column: 1; grid-row: 1 / -1; align-self: stretch; }
  .mail-rail:has(> .work-rail-zone):has(> .work-rail-group) > :not(.work-rail-zone) { grid-column: 2; padding: 4px 6px 0; }
}
.mail-reader-head { min-width: 0; flex-wrap: wrap; }
.mail-reader-subject { overflow-wrap: anywhere; white-space: normal; }
.mail-reader-meta, .mail-reader-meta > span { min-width: 0; overflow-wrap: anywhere; white-space: normal; }
@media (max-width: 1100px) and (min-width: 901px) {
  .mail-shell:not(.is-two-column):has(> .mail-rail > .work-rail-group):not(:has(.work-folder-col)),
  .mail-shell.work-assist-open:not(.is-two-column):has(> .mail-rail > .work-rail-group):not(:has(.work-folder-col)) { grid-template-columns: 252px minmax(0, 264px) minmax(0, 1fr) 0; }
  .mail-shell:has(.work-folder-col) { grid-template-columns: 52px minmax(0, 264px) minmax(0, 1fr) 0; }
  .mail-shell:has(.work-folder-col) .work-folder-col { display: none; }
  .mail-shell:not(.is-two-column):has(> .mail-rail > .work-rail-group):not(:has(.work-folder-col)) > .work-list-grip { display: none; }
}

/* Settings sub-view Back: sits at the left of the Settings header, inside the modal. */
.settings-head { display: flex; align-items: center; gap: 10px; }
.settings-head > .ptitle { flex: 1; min-width: 0; }
.settings-back { flex: none; gap: 4px; }
/* New project modal (product owner, 2026-10-07): match Settings and the other dialogs.
   The card loses the heavy top rule (same as the automations modal), the form gets top
   padding so the first label clears the title divider, the name input is a normal
   bordered field in every theme, and the helper lines read as plain sentence-case text. */
:root .modal .mcard:has(.new-project-form) { border-top-width: 1px; border-radius: 6px; }
:root .modal .mcard > .new-project-form { padding-top: 14px; gap: 14px; }
:root .new-project-form .fld { margin-bottom: 0; }
:root .new-project-form .fld input {
  border: 1px solid var(--line); border-radius: 6px; background: var(--surface);
  padding: 9px 11px; font-family: var(--ui); font-size: 14px; color: var(--ink); box-shadow: none;
}
:root .new-project-form .fld input:focus {
  border-color: var(--accent); outline: none;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 12%, transparent);
}
.new-project-drive { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; }
:root .new-project-hint { margin: 0; font-family: var(--ui); font-size: 12px; color: var(--mute); text-transform: none; letter-spacing: normal; }
.new-project-form .action-row.is-end { align-items: center; }
.new-project-form .action-row.is-end > .new-project-hint { margin-right: auto; }
/* Design review (2026-10-07): at wide desktop the header kept the chat column's centred
   1040px max-width while full-width workspaces (Work tabs, the mail/To do frame) ran edge
   to edge, so the logo floated over nothing. When such a workspace is on screen the header
   spans the full width too and its left padding matches the content's left edge. The chat
   home is untouched: .top, .card and .compwrap already share the same 1040px column. */
@media (min-width: 761px) {
  .yarnball-shell:has(.mail-shell, .yb-f-main) > .top { max-width: none; }
  .yarnball-shell:has(.yb-f-main) > .top { padding-inline: 40px; }
}

/* Base definitions for classes the app names directly; the scoped rules above refine them. */
.conn-add-secondary { font-weight: 600; }

/* YUM-1284: a long unbroken string from an external source (a Teams join URL, a calendar
   description, a file path) must wrap inside a notification, not widen it. The modal draws each
   item as a column flex (.srv-stack) whose children keep their max-content width, and the title
   and time share a row (.row-between). Without min-width: 0 one long title sets the width of the
   whole row and pushes the time off the surface. The title wraps and may shrink; the body wraps. */
.srv-stack .row-between > b { min-width: 0; overflow-wrap: anywhere; word-break: break-word; }
.srv-stack > .s { min-width: 0; overflow-wrap: anywhere; word-break: break-word; }
.conn-row-account { min-width: 0; }
.conn-row-status { flex: none; }
.new-project-hint { margin: 0; font-size: 12px; color: var(--mute); }
.yb-chats-newchat { flex: none; }

/* ---- WhatsApp messages window (YUM-1543, 2026-10-09). The wa-* block from design/SPEC.md
   (design/WhatsApp Messages Page.html) — token-only additions. States are is-* modifiers;
   nothing here hard-codes a colour, radius or font. ---- */
.wa-barstatus {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 46%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--mono);
  font-size: 11px;
  padding: 2px 7px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  color: var(--mute);
}
.wa-barstatus.is-linked { color: var(--ink-2); }
.wa-barstatus.is-waiting { color: var(--accent); border-color: var(--accent-soft); }
.wa-barstatus.is-off { color: var(--faint); }

.wa-pair { display: flex; flex-wrap: wrap; gap: 18px; align-items: flex-start; width: 100%; }
.wa-paircol { flex: 1 1 240px; min-width: 0; display: flex; flex-direction: column; gap: 12px; }

.wa-qr {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  padding: 16px; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r); box-shadow: var(--shadow-sm);
}
.wa-qrimg {
  display: block; width: 180px; height: 180px;
  border: 1px solid var(--line-soft); border-radius: var(--r-xs); background: var(--surface);
}
.wa-qrcap { margin: 0; font-family: var(--mono); font-size: 11px; color: var(--faint); text-align: center; }

.wa-time { display: block; margin-top: 4px; font-family: var(--mono); font-size: 10px; color: var(--faint); text-align: right; }
.row.me .wa-time { color: color-mix(in srgb, var(--on-ink) 62%, transparent); }

.wa-media {
  display: inline-block; margin-right: 6px; padding: 2px 6px;
  font-family: var(--mono); font-size: 11px;
  color: var(--accent); background: var(--accent-soft); border-radius: var(--r-xs);
}
.row.me .wa-media { color: var(--on-ink); background: color-mix(in srgb, var(--on-ink) 16%, transparent); }

/* ---- WhatsApp messages window — review fixes (Claude Design sign-off round 1,
   2026-10-09). The shipped tile is ~600px wide, not the mock's 920px: the QR column
   sizes to its card so the copy column keeps the rest, the bar reserves room for the
   board's window controls so its action stays clear, one-to-one chats drop the empty
   sender line, and error detail reads as a secondary mono line. Tokens only. ---- */
.wa-paircol.wa-is-qr { flex: 0 0 232px; max-width: 100%; }
.wa-win .yb-m-bar { padding-right: 72px; }
.wa-win .row .who:empty { display: none; }
.wa-errdetail {
  margin: 4px 0 0; font-family: var(--mono); font-size: 10px; color: var(--faint);
  overflow-wrap: anywhere;
}

/* Settings, every view (design critique 2026-10-11). One left edge, one measure, rules instead
   of boxes, and sentence-case captions. Sub-views get a quiet text Back and the same title
   scale as the root list, so moving between views no longer changes the design language. */
html .settings-wrap { max-width: 720px; margin: 0 auto; padding: 16px 24px 32px; box-sizing: border-box; }
html .settings-wrap.settings-home { padding-top: 8px; }
html .settings-wrap .settings-head { gap: 12px; margin: 0 0 4px; align-items: baseline; }
html .settings-wrap .settings-head > .ptitle { margin: 0; padding: 0 0 10px; border-bottom: 1px solid var(--line); background: transparent; }
html .settings-wrap .settings-head .ptitle h2 { font-family: var(--display); font-size: 24px; font-weight: 600; letter-spacing: -.015em; }
html .settings-wrap .settings-head .btn.settings-back, :root[data-theme] .settings-wrap .settings-head .btn.settings-back { min-height: 32px; padding: 0 6px 0 0; border: 0; border-radius: 4px; background: transparent; box-shadow: none; color: var(--mute); font-family: var(--ui); font-size: 14px; }
html .settings-wrap .settings-head .btn.settings-back:hover, :root[data-theme] .settings-wrap .settings-head .btn.settings-back:hover { background: transparent; color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }
html .settings-wrap .setgh { margin: 24px 0 6px; padding: 0; font-family: var(--mono); font-size: 11px; letter-spacing: .08em; color: var(--mute); }
html .settings-wrap .setgroup { margin: 0; border: 0; border-top: 1px solid var(--line-soft); border-radius: 0; background: transparent; box-shadow: none; }
html .settings-wrap .settings-row-pad,
:root .yb-win .settings-wrap .settings-row-pad { padding: 10px 0; }
html .settings-wrap .settings-row-pad.is-divided,
:root .yb-win .settings-wrap .settings-row-pad.is-divided { border-top: 1px solid var(--line-soft); }
html .settings-wrap .setlink,
html .settings-wrap .setrow { padding: 12px 0; gap: 14px; border: 0; border-bottom: 1px solid var(--line-soft); }
html .settings-wrap .setgroup > :last-child { border-bottom: 0; }
html .settings-wrap .setlink .sl-l { font-family: var(--display); font-size: 16px; font-weight: 600; }
html .settings-wrap .setlink .sl-v,
html .settings-wrap .setrow .sl-v { margin-top: 3px; font-family: var(--ui); font-size: 13.5px; line-height: 1.45; letter-spacing: normal; text-transform: none; color: var(--mute); }
html .settings-wrap .setrow > label { font-family: var(--display); font-size: 15px; font-weight: 500; color: var(--ink); }
html .settings-wrap .account-email-row { margin: 0; padding: 0; border: 0; }
/* Usage: figures in a plain row, no chips. */
html .settings-wrap .usage-hero-plan { font-size: 18px; font-weight: 600; }
html .settings-wrap .usage-stat-strip { display: flex; flex-wrap: wrap; gap: 6px 28px; }
html .settings-wrap .usage-stat-strip .usage-stat { padding: 0; border: 0; border-radius: 0; background: transparent; font-size: 13.5px; }
html .settings-wrap .usage-stat-strip .usage-stat b { font-family: var(--mono); font-weight: 600; }
/* Plans: ruled rows on the same left edge; the current plan is marked by its badge, not a wash. */
html .settings-wrap .plan-row,
:root .yb-win .settings-wrap .plan-row { margin: 0; padding: 12px 0; gap: 16px; border: 0; border-bottom: 1px solid var(--line-soft); border-radius: 0; background: transparent; box-shadow: none; }
html .settings-wrap .plan-row.is-current,
:root .yb-win .settings-wrap .plan-row.is-current { background: transparent; }
html .settings-wrap .plan-row-name { font-family: var(--display); font-size: 16px; font-weight: 600; }
html .settings-wrap .plan-row-desc,
:root .yb-win .settings-wrap .plan-row-desc { font-size: 13.5px; line-height: 1.45; max-width: 52ch; }
html .settings-wrap .plan-row-price { font-family: var(--mono); font-size: 13px; }
html .settings-wrap .hint { font-family: var(--ui); font-size: 13px; letter-spacing: normal; text-transform: none; color: var(--mute); text-align: left; }
/* On/Off switches read as one bordered control; the chosen side is inked. */
html .settings-wrap .setrow .atabs { flex: none; display: inline-flex; padding: 2px; gap: 2px; border: 1px solid var(--line); border-radius: 4px; background: transparent; }
html .settings-wrap .setrow .atab { min-width: 44px; min-height: 28px; padding: 4px 12px; border: 0; border-radius: 3px; background: transparent; color: var(--mute); font-family: var(--ui); font-size: 13px; text-align: center; }
html .settings-wrap .setrow .atab.on { background: var(--ink); color: var(--bg); }
html .settings-wrap .setrow .atab.disabled { opacity: .45; cursor: not-allowed; }
/* Redesign pass: captions are sentence-case headings, not tracked mono labels; status
   badges read as a quiet dot and a word; icons sit in one soft square so the column of
   rows has a steady left rhythm. The accent red is kept for the current state only. */
html .settings-wrap .setgh,
html .settings-home .setgh { margin: 28px 0 4px; font-family: var(--display); font-size: 15px; font-weight: 600; letter-spacing: 0; text-transform: none; color: var(--ink); }
html .settings-wrap > div:first-child .setgh,
html .settings-wrap .settings-head + div .setgh { margin-top: 12px; }
html .settings-wrap .setlink .sl-ic,
html .settings-home .setlink .sl-ic { display: inline-grid; place-items: center; width: 32px; height: 32px; flex: 0 0 32px; border-radius: 6px; background: var(--surface-2); color: var(--ink-2); }
html .settings-wrap .setlink .sl-ch { color: var(--mute); }
html .settings-wrap .setlink:hover,
html .settings-home .setlink:hover { background: transparent; }
html .settings-wrap .setlink:hover .sl-l { text-decoration: underline; text-decoration-color: var(--line); text-underline-offset: 4px; }
html .settings-wrap .setlink:hover .sl-ch { color: var(--ink); }
html .settings-wrap .trust,
:root[data-theme] .settings-wrap .trust { display: inline-flex; align-items: center; gap: 6px; padding: 0; border: 0; border-radius: 0; background: transparent; box-shadow: none; font-family: var(--ui); font-size: 13px; font-weight: 600; letter-spacing: 0; text-transform: none; color: var(--accent); }
html .settings-wrap .settings-head .ptitle .sp { display: none; }

@media (max-width: 640px) {
  html .settings-wrap { padding: 8px 16px 24px; }
  html .settings-wrap .setlink, html .settings-wrap .setrow, html .settings-wrap .plan-row { min-height: 44px; }
  html .settings-wrap .plan-row { flex-wrap: wrap; }
}
/* To do board, design 2a and 2b (Task Cards.html turn 2): state groups, count strip, source edge, people, progress. */
.todo-card[data-source]{border-top:4px solid var(--src-edge,var(--line))}
.todo-card[data-source="mail"]{--src-edge:var(--todo-hue-5)}
.todo-card[data-source="meeting"]{--src-edge:var(--todo-hue-3)}
.todo-card[data-source="code"]{--src-edge:var(--mute)}
.todo-board-strip{display:flex;flex-wrap:wrap;align-items:center;gap:8px 22px;padding:12px 16px;margin:0 0 18px;border:1px solid var(--line);border-radius:var(--r-sm);background:var(--surface);font-size:13px;color:var(--ink-2)}
.todo-board-strip strong{font-family:var(--display);font-size:15px;color:var(--ink)}
.todo-board-count{display:inline-flex;align-items:center;gap:7px}
.todo-board-count::before,.todo-group[data-board] .todo-group-title::before{content:'';width:8px;height:8px;border-radius:50%;background:var(--faint);flex:none}
[data-board="needs"].todo-board-count::before,.todo-group[data-board="needs"] .todo-group-title::before{background:var(--danger)}
[data-board="working"].todo-board-count::before,.todo-group[data-board="working"] .todo-group-title::before{background:var(--todo-working)}
[data-board="proposed"].todo-board-count::before,.todo-group[data-board="proposed"] .todo-group-title::before{background:transparent;box-shadow:inset 0 0 0 1.6px var(--accent)}
.todo-group[data-board="done"] .todo-group-title::before{background:var(--todo-hue-3)}
.todo-group[data-board="failed"] .todo-group-title::before{border-radius:1px;transform:rotate(45deg);background:var(--mute)}
.todo-board-kinds{display:inline-flex;flex-wrap:wrap;align-items:center;gap:6px}
.todo-board-kinds .todo-chip{border:0;font:inherit;font-size:11px;cursor:pointer}
.todo-board-switch{margin-left:auto;display:inline-flex;align-items:center;gap:8px;cursor:pointer}
.todo-board-switch input{width:16px;height:16px;accent-color:var(--accent)}
.todo-group[data-board] .todo-group-title{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--ink)}
.todo-group-count{color:var(--mute);font-weight:500}
.todo-action-people{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--mute)}
.todo-action-avatar{width:22px;height:22px;border-radius:50%;display:grid;place-items:center;background:color-mix(in srgb,var(--src-edge,var(--accent)) 18%,var(--surface));color:var(--ink);font-size:9.5px;font-weight:700}
.todo-action-progress{display:flex;align-items:center;gap:8px;margin-top:6px}
.todo-action-bar{flex:1;height:4px;accent-color:var(--todo-working)}
@media(max-width:700px){.todo-board-switch{margin-left:0;min-height:44px}}

/* Decision cards (D2): a prepared decision with Approve, Hold and Reject. */
.todo-decision{min-height:0;border-left:3px solid var(--line)}.todo-decision[data-decision="undecided"]{border-left-color:var(--danger)}
.todo-decision-head{display:flex;align-items:center;flex-wrap:wrap;gap:8px;font-size:12px}
.todo-decision-chip{padding:2px 6px;border:1px solid var(--line);border-radius:var(--r-xs);font-size:11px;letter-spacing:.06em;color:var(--mute)}
.todo-decision-who{font-size:13px;color:var(--ink)}
.todo-decision-state{margin-left:auto;font-size:12px;color:var(--mute)}
.todo-decision-headline{font-size:15px;line-height:1.35}
.todo-decision-context{margin:0;font-size:14px;line-height:1.55;color:var(--ink-2)}
.todo-decision-proposal{margin:0;padding-left:10px;border-left:2px solid var(--line);font-size:13px;line-height:1.5}
.todo-decision-proposal.is-line{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.todo-decision-label{font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--mute)}
.todo-decision-body{display:flex;flex-direction:column;gap:10px}
.todo-decision-action{margin:0;font-size:13px}
.todo-decision-field{display:flex;flex-direction:column;gap:6px}
.todo-decision-text,.todo-decision-note{width:100%;box-sizing:border-box;padding:8px;border:1px solid var(--line);border-radius:var(--r-xs);background:var(--surface);color:var(--ink);font:inherit;line-height:1.5}
.todo-decision-text{min-height:120px;resize:vertical}
.todo-decision-choices{display:flex;gap:8px;flex-wrap:wrap}
.todo-decision-result{display:flex;flex-direction:column;gap:6px;margin:0;font-size:13px}.todo-decision-result p{margin:0}
@media(max-width:700px){.todo-decision-choices .btn,.todo-decision-result .btn{min-height:44px;flex:1}}

/* To do board sections (D3): Today and next, Done since last run, Being handled, access notes. */
.todo-sections{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px;margin:14px 18px 0}
.todo-main > .todo-sections{margin-top:16px}
.todo-section{border:1px solid var(--line);border-radius:var(--r-sm);background:var(--surface);padding:10px 14px}
.todo-section-title{margin:0;font-size:13px}
.todo-section-toggle{all:unset;cursor:pointer;display:flex;align-items:center;gap:8px;font-family:var(--display);font-size:14px;color:var(--ink)}
.todo-section-toggle:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.todo-section-list{list-style:none;margin:8px 0 0;padding:0;display:grid;gap:6px}
.todo-section-row{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 10px;font-size:13px;color:var(--ink-2)}
.todo-section-time{flex:0 0 auto;min-width:96px;color:var(--mute);font-variant-numeric:tabular-nums}
.todo-section-what{flex:1 1 160px;color:var(--ink);min-width:0}
.todo-section-status{flex:1 1 100%;color:var(--ink-2);font-size:12px}
.todo-section-warn{flex:1 1 100%;color:var(--danger,var(--accent));font-size:12px;font-weight:600}
.todo-section-link{all:unset;cursor:pointer;color:var(--accent);text-decoration:underline;font-size:12px}
@media (max-width: 640px){
  .todo-sections{grid-template-columns:1fr;gap:8px}
  .todo-section{padding:8px 10px}
  .todo-section-time{min-width:0}
}

/* ── The app maker (make-ui.js, Open Intelligent UI) ─────────────────────────────────────
   .made-app is one made app in its sandboxed frame: inline in a chat bubble, or filling a
   board window (.is-bare, the window has its own title bar). The frame's interior is the app's own document on an opaque origin,
   themed by src/make-ui.js; only the bar around it is Yarnball chrome. */
.made-app { margin: 10px 0 4px; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface); overflow: hidden; display: flex; flex-direction: column; max-width: 100%; }
.made-app.is-fill { margin: 0; flex: 1; min-height: 0; border-radius: 0; border-width: 0 0 0 0; }
.made-app-bar { display: flex; align-items: center; gap: 8px; padding: 6px 10px; border-bottom: 1px solid var(--line-soft); font-size: 13px; color: var(--mute); }
.made-app-title { flex: 1; min-width: 0; font-weight: 600; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.made-app-open { color: var(--mute); font-size: 12px; text-decoration: none; }
.made-app-open:hover, .made-app-open:focus-visible { color: var(--accent); text-decoration: underline; }
.made-app-frame { display: block; width: 100%; border: 0; background: var(--paper); min-height: 120px; }
.made-app.is-bare { border: 0; border-radius: 0; margin: 0; height: 100%; }
.made-app-consent { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 8px 10px; border-bottom: 1px solid var(--line-soft); background: var(--surface-2, var(--surface)); font-size: 13px; }
.made-app-consent-text { flex: 1 1 16em; min-width: 0; color: var(--ink); }
.made-app-stopped { margin: 0; padding: 14px 12px; color: var(--accent); font-size: 13px; }
.made-app.is-fill .made-app-frame { flex: 1; min-height: 420px; height: 100%; }
.made-app.is-bare .made-app-frame { min-height: 0; }

/* YUM-1491: the Automations modal at phone width. Save is the form's one confirming action, so
   it is a full 48px target, and its two empty-state lines and the dialog subtitle read as
   sentences (13px, sentence case) instead of the 9px uppercase mono .hint stack, which is
   8.5px on the letterpress themes this app ships. Scoped to this modal so no other surface
   changes. */
/* The wrapper the two empty-state lines are rendered in, so the phone block below can restyle
   them and nothing else in the modal. Unscoped so the class is a design-system class wherever it
   is named; it adds no styling of its own. */
.automations-empty { display: block; }
@media (max-width: 600px) {
  /* Only the form's confirming action, which the design system renders as .btn.is-primary: any
     other button in this card keeps the height the design system gives it. */
  .automation-form-card .btn.is-primary { min-height: 48px; }
  /* Only the two empty-state lines, wrapped in .automations-empty where they are rendered, and
     the dialog subtitle. The theme sheets load after this file and style .hint and the subtitle
     at their own specificity ([data-theme="letterpress"] .hint is 8.5px uppercase mono), so these
     carry the html element to outrank them on the same screen. */
  html .automations-modal-body .automations-empty .hint,
  html .modal .mcard:has(.automations-modal-body) > p.s {
    font-family: var(--ui);
    font-size: 13px;
    letter-spacing: normal;
    text-transform: none;
    line-height: 1.4;
    color: var(--mute);
  }
}

/* ==========================================================================
   Workspace picker + custom MCP tiles (2026-10-13 UI polish).
   Base, theme-agnostic definitions live here so the design-system gate can
   see these classes unscoped. Letterpress adds its own polish on top in
   letterpress-app.css.
   ========================================================================== */
.yb-ws-picker {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 10px;
  padding: 6px 0 4px;
}
.yb-ws-kind {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 18px 10px 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm, 6px);
  background: transparent;
  cursor: pointer;
  text-align: center;
  font-family: var(--ui);
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}
.yb-ws-kind:hover {
  background: var(--accent-soft);
  border-color: var(--accent);
}
/* The lift is motion; it waits for a user who has not asked for reduced motion. */
@media (prefers-reduced-motion: no-preference) {
  .yb-ws-kind:hover { transform: translateY(-2px); }
}
/* Keyboard users get the same affordance hover gives the mouse. */
.yb-ws-kind:focus-visible {
  background: var(--accent-soft);
  border-color: var(--accent);
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.mcp-symbol-card:focus-visible {
  border-color: var(--accent);
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.yb-ws-kind-ic {
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  border-radius: var(--r-sm, 6px);
}
.mcp-symbol-card.mcp-add-tile {
  border-style: dashed;
}
.mcp-symbol-card.mcp-add-tile:hover {
  border-color: var(--accent);
}

.yb-ws-kind-about {
  display: block;
  font-family: var(--ui);
  font-size: 11px;
  font-weight: 400;
  color: var(--mute);
  line-height: 1.35;
}

/* ---- Keyboard, motion and target size (DESIGN.md › Keyboard and screen readers) ---- */
/* Skip link: the first Tab stop on a signed-in screen. Hidden until it has focus, then a
   plain ink chip at the top left, above everything, so a keyboard user can jump past the
   top bar and tab row straight to the screen's content. */
.skip-to-main {
  position: fixed; top: 8px; left: 8px; z-index: 10000;
  padding: 8px 14px; border-radius: var(--r-xs);
  background: var(--ink); color: var(--on-ink);
  font: 600 14px var(--ui); text-decoration: none;
  transform: translateY(-200%);
}
.skip-to-main:focus, .skip-to-main:focus-visible { transform: none; outline: 2px solid var(--accent); outline-offset: 2px; }
/* The target of the skip link and of a dialog's own focus takes focus without a ring around
   the whole screen or card: the next Tab shows the ring on the first control. */
/* :root raises it above the theme's own [data-theme] :focus-visible ring, which otherwise drew a
   red frame round the whole Settings window and the page's main area. Only these three
   containers lose the ring; every control inside keeps it. */
:root main:is(:focus, :focus-visible),
:root .mcard[role="dialog"]:is(:focus, :focus-visible),
:root .yb-win:is(:focus, :focus-visible) { outline: none; }

/* Settings › Display › Motion › Reduce, for someone who wants less motion here and not
   everywhere. Every animation and transition finishes at once, with no delay, and a
   looping one runs a single time; anything with a fill mode ends on its final frame. The
   @media (prefers-reduced-motion) escapes elsewhere still follow the operating system only. */
html[data-motion="reduce"],
html[data-motion="reduce"] *,
html[data-motion="reduce"] *::before,
html[data-motion="reduce"] *::after {
  animation-duration: 0.01ms !important;
  animation-delay: 0s !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.01ms !important;
  transition-delay: 0s !important;
  scroll-behavior: auto !important;
}

/* The mailbox "More" button collapses to 4px until the row is hovered. A keyboard user
   reaches it by Tab, and :focus-visible already opens it; a coarse pointer (touch) has no
   hover, so there it stays open at a usable size. */
@media (hover: none) {
  .mail-box-more { opacity: 1; width: auto; min-width: 32px; min-height: 32px; padding: 0 6px; }
}

/* ============ Notification center: the bell in the top bar (2026-10-16) ============
   docs/specs/2026-10-16-notification-center.md. The bell takes the top bar's own button box
   (.topnav-chat) and the panel the standard popover card the Chats menu uses, so it reads as
   part of the bar rather than a new kind of thing. The panel is wider than the Chats menu
   because a notification carries a sentence, and a mail card carries a reply box. */
.yb-bell-anchor { position: relative; display: inline-flex; }
/* Above its own backdrop (z 45), so a second press on the bell closes the panel it opened. */
.yb-bell { position: relative; z-index: 47; padding-inline: 10px; }
.yb-bell.yb-is-quiet svg { opacity: .5; }
.yb-bell.yb-is-quiet::after {
  content: ''; position: absolute; left: 50%; top: 50%; width: 20px; height: 1.5px;
  background: currentColor; transform: translate(-50%, -50%) rotate(-45deg); pointer-events: none;
}
.yb-bell.yb-is-ringing { color: var(--accent); background: var(--accent-soft); }
@media (prefers-reduced-motion: no-preference) {
  .yb-bell.yb-is-ringing svg { animation: yb-bell-ring 1.2s ease-in-out infinite; transform-origin: 50% 10%; }
  @keyframes yb-bell-ring { 0%, 60%, 100% { transform: rotate(0); } 10%, 30% { transform: rotate(14deg); } 20%, 40% { transform: rotate(-14deg); } }
}
.yb-bell-badge {
  position: absolute; top: 2px; inset-inline-end: 0;
  min-width: 16px; height: 16px; padding: 0 4px; border-radius: var(--r-pill);
  background: var(--accent); color: var(--on-accent);
  font-family: var(--mono); font-size: 11px; font-weight: 700; line-height: 16px; text-align: center;
  box-shadow: 0 0 0 2px var(--surface); pointer-events: none;
}
.yb-bell-badge.yb-is-dot { top: 6px; inset-inline-end: 6px; min-width: 0; width: 8px; height: 8px; padding: 0; border-radius: 50%; }
.yb-bell-backdrop { position: fixed; inset: 0; z-index: 45; background: transparent; }
.yb-bell-panel.yb-is-shut { display: none; }
.yb-bell-panel {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 46;
  display: flex; flex-direction: column;
  width: min(400px, calc(100vw - 24px));
  max-height: min(78vh, 680px);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-sm);
  box-shadow: var(--shadow);
  text-align: left;
}
.yb-bell-head {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 10px 10px 8px 14px; border-bottom: 1px solid var(--line-soft); flex: none;
}
.yb-bell-title {
  font-family: var(--mono); font-size: 11px; text-transform: uppercase; letter-spacing: .08em; color: var(--mute);
}
.yb-bell-body { overflow-y: auto; overscroll-behavior: contain; padding: 10px 12px 12px; min-height: 0; }
@media (max-width: 600px) {
  /* The phone's top row puts its controls on the right, so the panel anchors under them and
     takes the screen's width less a margin. */
  .yb-bell-panel { position: fixed; top: 60px; right: 12px; left: 12px; width: auto; max-height: calc(100vh - 96px); }
}

/* ============ COS-DECISIONS: the Chief of staff decision cards (2026-10-19, quiet card
   2026-10-20) ============
   Ben: "just cards that pop up and you answer them, basically copy the way his version
   worked", then "make it look/feel more minimalistic/simplified". One card at a time: one
   quiet source line, the subject, what happened, the proposal, the draft under a small
   label, one note line, three small buttons, and the quiet "don't ask about this sender"
   link. Nothing is hidden — the person answers what they read. The quiet lists below are
   its done and watching lines. */
.cosq { max-width: 640px; margin: 0 auto; padding: 18px 20px 40px; }
.cosq-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.cosq-title { margin: 0; font-family: var(--display); font-size: 22px; font-weight: 600; letter-spacing: -.02em; line-height: 1.2; }
.cosq-quiet { margin: 3px 0 0; font: 400 13px/1.5 var(--ui); color: var(--mute); }
.cosq-instr { margin: 14px 0; font: 400 15px/1.55 var(--ui); color: var(--ink-2); }
.cosq-error { margin: 8px 0; font: 400 14px/1.5 var(--ui); color: var(--cos-fail, var(--accent)); }
.cosq-stage { margin: 12px 0 0; }
.cosq-card {
  margin-top: 10px; padding: 18px 20px;
  border: 1px solid var(--line); border-left: 3px solid var(--accent); border-radius: var(--r);
  background: var(--surface);
}
@keyframes cosq-pop { from { opacity: 0; transform: translateY(8px) scale(.985); } }
.cosq-card { animation: cosq-pop .18s ease-out; }
.cosq-src { margin: 0 0 2px; font: 400 13px/1.5 var(--ui); color: var(--mute); }
.cosq-subject { margin: 4px 0 6px; font-family: var(--display); font-size: 19px; font-weight: 600; line-height: 1.3; }
.cosq-context, .cosq-proposal, .cosq-alt, .cosq-action { margin: 6px 0; font: 400 15px/1.55 var(--ui); color: var(--ink-2); }
.cosq-proposal b, .cosq-action b { margin-right: 6px; color: var(--ink); }
.cosq-draft-label { margin: 12px 0 4px; font: 500 11.5px/1.4 var(--ui); letter-spacing: .05em; text-transform: uppercase; color: var(--mute); }
.cosq-draft {
  margin: 0 0 12px; padding: 0 0 0 12px; border-left: 2px solid var(--line-soft);
  font: 400 14.5px/1.6 var(--ui); color: var(--ink-2); white-space: pre-wrap;
}
.cosq-link { color: var(--accent); text-decoration: none; }
.cosq-link:hover { text-decoration: underline; }
.cosq-note {
  width: 100%; margin-top: 6px; padding: 8px 2px;
  border: 0; border-bottom: 1px solid var(--line); border-radius: 0; background: transparent;
  font: 400 14px/1.4 var(--ui); color: var(--ink);
}
.cosq-note::placeholder { color: var(--mute); }
.cosq-note:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
.cosq-answers { display: flex; gap: 10px; margin-top: 14px; }
.cosq-answers .btn { flex: 0 0 auto; min-height: 40px; padding: 8px 20px; font-size: 14px; }
.cosq-skip {
  display: block; margin: 10px 0 0; padding: 4px 0 0;
  border: 0; background: none; cursor: pointer;
  font: 400 12.5px/1.5 var(--ui); color: var(--mute); text-decoration: underline;
}
.cosq-skip:hover { color: var(--ink-2); }
.cosq-undo {
  margin-left: 10px; padding: 2px 0;
  border: 0; background: none; cursor: pointer;
  font: 400 12.5px/1.4 var(--ui); color: var(--mute); text-decoration: underline;
}
.cosq-list-head { margin: 24px 0 4px; font: 600 12px/1.4 var(--ui); letter-spacing: .06em; text-transform: uppercase; color: var(--mute); }
.cosq-list { margin: 0; padding: 0; list-style: none; }
.cosq-list li { padding: 9px 0; border-top: 1px solid var(--line-soft); font: 400 14.5px/1.5 var(--ui); color: var(--ink-2); }
.cosq-foot { margin: 26px 0 0; font: 400 13px/1.55 var(--ui); color: var(--mute); }
@media (max-width: 600px) {
  .cosq { padding: 12px 16px calc(28px + env(safe-area-inset-bottom)); }
  .cosq-card { padding: 14px 16px; }
  .cosq-subject { font-size: 18px; }
  /* Touch targets stay 44px and up on a phone even though the desktop buttons shrank. */
  .cosq-answers .btn { min-height: 48px; padding: 10px 18px; }
  .cosq-note { min-height: 44px; }
  .cosq-skip, .cosq-undo { min-height: 44px; }
}

/* ------------------------------------------------------------------ One conversation, shown twice
   The Chat screen and the Yarnball side panel mount the same ChatConversation (chat.js), so a turn that
   is running looks the same in both: a "Working" header with a live timer, one checklist row per tool,
   a soft collapsible Thinking line, and the answer growing under them. The side panel draws it in a
   column (.chat-pane); the Chat screen draws it inside its card. */
.chat-progress {
  display: flex; flex-direction: column; gap: 6px;
  margin: 4px 0 12px; padding: 10px 12px;
  border: 1px solid var(--line-soft); border-radius: var(--r-sm);
  background: var(--surface-2);
}
.chat-progress-head { display: flex; align-items: center; gap: 8px; }
.chat-progress-dot {
  flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--accent);
  animation: pulse 1.4s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) { .chat-progress-dot { animation: none; } }
.chat-progress-title { font-size: 13px; font-weight: 600; color: var(--ink-2); font-variant-numeric: tabular-nums; }
.chat-progress-note { margin: 0; font-size: 12px; line-height: 1.45; color: var(--mute); }
.chat-progress .yls { margin: 0; }
.chat-progress-stall { margin: 0; font-size: 12px; line-height: 1.45; color: var(--mute); }
.chat-reason { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.chat-reason-head {
  display: flex; align-items: baseline; gap: 8px; min-width: 0; width: 100%;
  padding: 2px 0; border: 0; background: none; font: inherit; font-size: 12px;
  color: var(--mute); text-align: left; cursor: pointer;
}
.chat-reason-head:hover { color: var(--ink-2); }
.chat-reason-head:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-xs); }
.chat-reason-label { flex: none; font-weight: 600; }
.chat-reason-line { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-style: italic; }
.chat-reason-caret { flex: none; margin-left: auto; font-size: 10px; }
.chat-reason-full {
  margin: 0; padding: 0 0 0 16px; max-height: 180px; overflow: auto;
  font-size: 12px; line-height: 1.5; color: var(--mute);
  display: flex; flex-direction: column; gap: 3px;
}

/* The Chat screen's column. Same shape as the side panel's: the thread scrolls and grows,
   the composer and the queue sit under it. It is the one child of .yb-chats-split, which is
   a row to hold the conversations list beside the conversation. */
.chat-col { display: flex; flex-direction: column; flex: 1 1 auto; min-width: 0; min-height: 0; }
.chat-col > .yarnball-main { min-width: 0; min-height: 0; }

/* The side panel's column. The thread scrolls; the composer and the queue stay below it. */
.chat-pane { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; height: 100%; }
.chat-pane-thread {
  flex: 1 1 auto; min-height: 0; overflow: auto;
  display: flex; flex-direction: column; gap: 10px; padding: 12px;
}
.chat-pane-empty { margin-block: auto; padding-block: 16px; font-size: 14px; line-height: 1.5; color: var(--mute); }
.chat-pane-empty p { margin: 0; }
.chat-pane .queued-msgs { padding: 0 12px; margin-bottom: 6px; }
.chat-pane .compwrap { max-width: none; padding: 8px 12px 12px; }
.chat-pane .hint { display: none; }
.chat-pane .box { gap: 4px; padding: 5px 5px 5px 10px; }
.chat-pane .box textarea { font-size: 13.5px; }
.chat-pane .composer-pending-files { padding: 0 2px 6px; }
.chat-pane .msg-prose { font-size: 13.5px; line-height: 1.55; }
.yb-edge-body:has(> .chat-pane), .yb-edge-body:has(> .yb-rail-chats + .chat-pane) { padding: 0; }
.yb-edge-body > .chat-pane { margin: 0; }

/* ==========================================================================
   The app's tabs (docs/specs/2026-10-20-tabs-desk-projects-tasks.md, 2026-10-21).
   One strip in the top bar between the logo and the controls, as a browser's tabs sit:
   a tab is its kind's icon and its name, the open one is the accent tint the top-bar
   stops already use, and its close mark shows on the open tab and on hover. The strip
   scrolls sideways when full and never wraps. New is the plus at its end, and opens a
   menu built from the chats menu's own classes (.chat-nav-panel), placed under the plus.
   ========================================================================== */
/* `html` outranks the themes' own .top width (letterpress-app.css loads later at 0,2,0). */
html .top:has(> .app-tabs),
html .yarnball-shell:has(.yb-f-main) > .top:has(> .app-tabs) { max-width: none; padding-inline: 20px; gap: 12px; }
.top > .app-tabs + .sp { flex: 0 0 8px; }
.app-tabs {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 4px;
}
.app-tabs-scroll {
  display: flex;
  align-items: center;
  gap: 2px;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
  padding: 2px;
}
.app-tabs-scroll::-webkit-scrollbar { display: none; }
/* More tabs past an edge: that edge fades (app-tab-strip.js sets the classes as it scrolls). */
.app-tabs-scroll.has-more-right { mask-image: linear-gradient(to right, #000 calc(100% - 64px), transparent); }
.app-tabs-scroll.has-more-left { mask-image: linear-gradient(to left, #000 calc(100% - 64px), transparent); }
.app-tabs-scroll.has-more-left.has-more-right { mask-image: linear-gradient(to right, transparent, #000 64px, #000 calc(100% - 64px), transparent); }
/* A tab shrinks to show a few words of its name, then the strip scrolls; it never shrinks to
   one letter and an ellipsis. */
.app-tab {
  position: relative;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  min-width: 0;
  max-width: 200px;
  height: var(--topnav-control-h, 36px);
  border-radius: var(--topnav-control-r, 18px);
  color: var(--ink-2);
  transition: background 0.15s ease, color 0.15s ease;
}
.app-tab:hover { background: var(--surface-2); color: var(--ink); }
.app-tab.is-on { background: var(--accent-soft); color: var(--accent); }
.app-tab.is-dragging { opacity: 0.5; }
.app-tab-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
  height: 100%;
  padding: 0 4px 0 12px;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  border-radius: inherit;
}
.app-tab.is-on .app-tab-btn { font-weight: 600; }
.app-tab-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.app-tab-ic { display: inline-flex; flex: none; }
.app-tab-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 24px: the smallest pointer target WCAG 2.5.8 allows. Shown on the open tab, on hover,
   and when it has keyboard focus; on a touch screen always, because touch has no hover. */
.app-tab-x {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  margin-right: 6px;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  opacity: 0;
}
.app-tab.is-on .app-tab-x,
.app-tab:hover .app-tab-x,
.app-tab-x:focus-visible { opacity: 0.7; }
.app-tab-x:hover { opacity: 1; background: var(--line-soft); }
.app-tab-x:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
@media (hover: none) { .app-tab-x { opacity: 0.7; } }
.app-tab-edit {
  flex: none;
  width: 11rem;
  height: var(--topnav-control-h, 36px);
  padding: 0 12px;
  border: 1px solid var(--accent);
  border-radius: var(--topnav-control-r, 18px);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: 13px;
}
.app-tab-new-anchor { position: relative; display: inline-flex; flex: none; }
.app-tab-new {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--mute);
  cursor: pointer;
}
.app-tab-new:hover, .app-tab-new[aria-expanded="true"] { background: var(--surface-2); color: var(--ink); }
.app-tab-new:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.app-tab-new:disabled { opacity: 0.4; cursor: default; }
.app-tab-menu.chat-nav-panel { left: 0; right: auto; width: min(280px, calc(100vw - 32px)); }
@media (prefers-reduced-motion: reduce) { .app-tab { transition: none; } }
/* A context-menu item's icon sits in a span that hides it from assistive tech (context-menu.js);
   the span lines the glyph up the way the bare svg did. */
.yb-context-ic { display: inline-flex; flex: none; }
