/* 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: 10px;
  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: 10px;
  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: 10px;
}
.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: 10px;
  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: 10px; 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;
}
.model-lock-standalone:hover { opacity: .8; }
/* 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: 10.5px;
  letter-spacing: 0.04em;
}
.memory-vault-btn {
  font-family: var(--mono);
  font-size: 10.5px;
  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: 9.5px;
  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: 10px;
  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: 10px;
  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: 10px;
  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; }

/* "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 h3 { 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; }
/* 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; 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: 1.15fr 0.85fr; 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; }
.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; }

.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; margin: 0 0 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: var(--ls-label); }
.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(--faint); 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: 10.5px; 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: 9.5px; 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: 10px; letter-spacing: var(--ls-label); text-anchor: middle; }
.sec-wire-sub { fill: var(--faint); font-size: 9.5px; 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(--faint); 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(--faint); 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: 10.5px; 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; }
/* 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); }
.nav-icon-red:hover { opacity: .8; }

/* 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. */
.attach-menu-anchor { position: relative; display: inline-flex; }
.attach-menu-backdrop { position: fixed; inset: 0; z-index: 45; background: transparent; }
.attach-menu {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 0;
  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: 10px;
  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: 10.5px; 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: 10px;
  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: 10.5px; 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: 10.5px; text-transform: uppercase; letter-spacing: .04em; color: var(--faint); margin: 14px 0 4px; }
.files-popup-attach-btn {
  font-family: var(--mono); font-size: 10.5px; 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: 10.5px; 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: 10.5px; 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: 10px; 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: 10px; 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; }
.sec-actions { margin-top: 16px; display: flex; justify-content: space-between; align-items: center; gap: 12px; }
.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: 10px; white-space: pre-wrap; }
.admin-pre-block-lg { max-height: 240px; overflow: auto; font-size: 10px; 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: 10px; 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: 9.5px; 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: 9.5px; 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: 10px; 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: 10px;
  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: 10.5px; 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; 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%; }
.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; }

.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: 10px; 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: 10px;
  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: 10px; 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: 10.5px; 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: 10px;
  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;
}

.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;
}
.cobrowse-bar {
  margin: 0;
  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;
}
.cobrowse-nav {
  margin: 0;
  padding: 10px 14px;
  align-items: center;
}
/* 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;
  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;
}
.cobrowse-note {
  margin: 8px 14px 12px;
}
.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): start-page greeting line — replaces the starter suggestion chips.
   One subtle text line ("Good morning, Ben."), packed at the top of the empty-state card
   with the tagline treatment's restraint: ink weight, no hero sizing, no extra chrome. */
.chat-hello {
  margin: 22px 0 4px;
  font-size: 21px;
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.3;
  color: var(--ink);
}

/* 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: 9px;
  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: 10px; background: transparent;
  text-align: left; font-family: var(--ui); color: var(--ink); cursor: pointer;
  transition: background .15s ease;
}
.sec-diagram-row:hover { background: var(--surface-2); }
.sec-diagram-row:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.sec-diagram-node {
  width: 40px; height: 40px; border-radius: 50%; 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; }
.sec-diagram-label { font-size: 14px; font-weight: 700; line-height: 1.3; }
.sec-diagram-sub { font-size: 12px; color: var(--mute); line-height: 1.35; }
.sec-diagram-row.is-fail .sec-diagram-sub { color: var(--accent); }
.sec-diagram-state { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border-radius: 50%; 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 {
  width: 2px; height: 14px; margin-left: 27px; flex: none; 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;
  padding: 10px 12px; border: 1px solid var(--line-soft); border-radius: 10px;
  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); opacity: 0; transition: opacity .12s ease, color .12s ease, background .12s ease;
}
.chat-nav-panel-item:hover .chat-nav-panel-item-archive,
.chat-nav-panel-item-archive:focus-visible { opacity: 1; }
.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: 10.5px; 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: 10.5px; 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: 10px; 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: 2; line-clamp: 2; }
.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: 10.5px; 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: 10px;
  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; }
/* 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; }

.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%;
}

/* 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: 9.5px;
  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: 9.5px; 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; }
/* 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: 9.5px; 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, 190px) 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, 190px) var(--work-list-w, 340px) minmax(0, 1fr) 296px; }
/* 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: 10px;
  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: 9.5px;
  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); }
.mail-folder-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mail-folder-count { font-family: var(--mono); font-size: 10.5px; color: var(--work-rail-mute); }
.mail-folder.active .mail-folder-count { color: var(--work-rail-active-ink); }
/* 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: 9px;
  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: 9.5px;
  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: 9.5px; 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. */
.mail-item {
  display: flex;
  align-items: flex-start;
  gap: 4px;
  width: 100%;
  padding: 4px 8px;
  border: 0;
  border-bottom: 1px solid var(--line-soft);
  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); }
.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: 10px; 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: 9px;
  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); }
/* 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: 10px; 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; }
.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. */
  padding: 8px 12px 6px;
  border-bottom: 1px solid var(--line-soft);
  background: var(--surface);
}
.mail-reader-subject { margin: 0; font-family: var(--display); font-size: 15px; line-height: 1.3; color: var(--ink); }
.mail-reader-meta { display: flex; flex-wrap: wrap; gap: 2px 10px; margin-top: 2px; font-size: 11.5px; 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);
}
.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: 9.5px; letter-spacing: 0.07em; text-transform: uppercase; color: var(--work-dim); }
.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%;
  /* G03 (SB 2026-09-04): four lines to start with; it grows as the reply does. */
  min-height: 96px;
  max-height: 60vh;
  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; }
.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; }
/* 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: 10.5px;
}
.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); }
.mail-compose-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; }
/* 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: 9.5px; 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); }
.work-source:disabled { opacity: 0.55; cursor: not-allowed; }
.work-source-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.work-rail-note { font-family: var(--mono); font-size: 10px; color: var(--work-rail-label); padding: 2px 8px 6px; }

/* 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: 10px;
  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: 9.5px;
  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). */
.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: 10px; 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: 10px; color: var(--work-dim); }
.work-post-flag {
  padding: 3px 10px;
  font-family: var(--mono);
  font-size: 9px;
  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: 10px;
  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: 9px; 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: 10px; 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 .work-cal-chip-title { text-decoration: line-through; color: var(--mute); }
.work-cal-chip-time { font-family: var(--mono); font-size: 9px; 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: 9px; 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. */
.work-preview { display: flex; flex: 1; min-height: 0; padding: 8px 12px 12px; }
.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; }
.work-preview-image { max-width: 100%; max-height: 100%; object-fit: contain; border: 1px solid var(--line-soft); border-radius: var(--r-sm); }

/* 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); }
/* 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: 10px; 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: 10px; color: var(--work-rail-mute); }
.file-tree-meta { flex: none; font-family: var(--mono); font-size: 10px; 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: 9.5px; 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: 9.5px;
  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: 10px; color: var(--work-dim); }
.work-browser-cell.is-size { width: 58px; justify-content: flex-end; font-family: var(--mono); font-size: 10px; color: var(--work-dim); }
.work-browser-cell.is-kind { width: 46px; font-family: var(--mono); font-size: 10px; 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: 9.5px;
  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 6px;
  border: 1px solid transparent;
  border-radius: var(--r-xs);
  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: 10px;
  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: 9px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.work-group-time { flex-shrink: 0; font-family: var(--mono); font-size: 10px; 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: 10px; 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: 10px; 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: 10px; 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: 9px;
  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: 9px;
  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: 10px;
  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: 10px;
  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: 9px;
  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: 10px; 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; }
.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-sm); padding: 2px 6px; }
.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-sm); padding: 2px 6px; }
.mail-compose-toolbar-wrap { display: flex; align-items: center; justify-content: space-between; gap: 8px; }

/* Side Canvas Panel (Right Sliding Drawer for Files & Viewer) */
.side-canvas-panel {
  position: fixed;
  top: 56px;
  right: 0;
  bottom: 0;
  width: 420px;
  max-width: 90vw;
  background: var(--surface, #ffffff);
  border-left: 1px solid var(--line, #e0e0e0);
  box-shadow: -4px 0 16px rgba(0, 0, 0, 0.12);
  display: flex;
  flex-direction: column;
  z-index: 99;
  transform: translateX(100%);
  transition: transform 0.25s cubic-bezier(0.16, 1, 0.3, 1);
}
.side-canvas-panel.is-open {
  transform: translateX(0);
}
.side-canvas-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 14px;
  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: 10px;
  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 8px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  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: 9.5px;
  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 6px;
  border: 1px solid transparent;
  border-radius: var(--r-xs);
  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%;
  right: var(--work-chat-w, 348px);
  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: 9px; }
.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: 10.5px; 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: 9.5px; 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; }
.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: 10.5px; 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: 10.5px; }
.mail-options-menu { min-width: 240px; }
.mail-option-check { display: flex; align-items: center; gap: 6px; cursor: pointer; }
.mail-option-check input { margin: 0; accent-color: var(--accent); }
.mail-option-when { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; cursor: default; }
.mail-option-when:hover { background: transparent; }
.mail-option-when .mail-compose-field { width: 100%; }
.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: 10.5px; }
.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); }
.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): the chats panel is a drawer on the left edge, full height, with a
   search box - not a small box hanging off the Chat button. Same panel, same rows; the
   rename and delete controls share the archive control's look. On a phone it starts at the
   left edge, so nothing hangs off the screen any more. */
.chat-nav-panel {
  position: fixed;
  top: 56px;
  left: 0;
  right: auto;
  bottom: 0;
  width: min(320px, calc(100vw - 32px));
  max-height: none;
  border-radius: 0;
  border-top: 0;
  border-left: 0;
  border-bottom: 0;
}
.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): three plain starter lines under the empty chat's greeting. Text in
   the greeting's own colour, a shade lighter, ink on hover; no box, no border. */
.chat-starters { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; margin-top: 6px; }
.chat-starter {
  padding: 3px 0;
  border: 0;
  background: transparent;
  color: var(--mute);
  font-family: var(--ui);
  font-size: 14px;
  line-height: 1.4;
  text-align: left;
  cursor: pointer;
}
.chat-starter:hover, .chat-starter:focus-visible { color: var(--ink); outline: none; text-decoration: underline; text-underline-offset: 3px; }

/* 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: 10.5px; color: var(--faint); }
.meeting-turn-text { margin: 0; font-size: 13.5px; line-height: 1.5; color: var(--ink-2); }

/* 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; }
