/* Ellington Clerk Workspace - server-rendered rebuild (Task R1, KAN-86).
   Values below are pixel-truth extracted directly from the committed,
   content-hashed reference bundle (hub/appui/index.html's inline styles +
   hub/appui/assets/dc-logic-precompiled.50a09bf7.js's TAG_COLORS/navItems
   objects), not hand-guessed from the general prose description in
   .superpowers/sdd/2026-08-13-lore-hub-workspace-rebuild/ui-fidelity-spec.md
   - that doc's "13px/700/0.39px" micro-label rule turns out to be the exact
   table-column-header style (13px, letter-spacing .03em == 0.39px at 13px);
   the Home screen's two card titles (TODAY'S SCHEDULE / RECENT ACTIVITY)
   are a related but distinct 15px/700/.03em rule in the real bundle - both
   are ported below as separate rules rather than force-unified to 13px.

   Zero inline JS/CSS: every color/spacing value here lives in this
   stylesheet, referenced from markup only via class names - never a
   style="..." attribute (tag/schedule pill colors are per-label CSS
   classes for exactly this reason; see hub.townflows.home.TAG_CSS_CLASS).

   Task 1 (docs/superpowers/plans/2026-08-21-workspace-reskin.md): the
   :root token block below and the shared shell/component-recipe rules
   that follow it are the Ellington Clerk Workspace MOCK's design instead
   (cited as TOKENS - that plan's Design Token Sheet section), under a new
   wsk-* class prefix so the two skins can coexist mid-migration without
   colliding. The R1/R2/R3 ws-* rules further below are UNCHANGED content
   styling, kept live until the task that owns each screen migrates it
   (plan Global Constraints); Task 7 deletes whatever ws-* remains once
   nothing references it. Task 2 migrated login/register/forgot-password/
   join onto their own wsk-auth-* section (below the shell/component-recipe
   rules) - .ws-form-error survives that migration on purpose, narrowed to
   its one remaining consumer (workspace/fragments/admin_users.html, out of
   Task 2's scope); see that rule's own comment. (Corrected, Task 4: this
   comment used to also carry .ws-wordmark as a same-reason survivor via
   workspace/admin.html - stale the moment Task 3 migrated admin.html onto
   the shared shell fragment and deleted .ws-wordmark alongside .ws-topbar/
   .ws-topbar-row, its last consumer having moved off all three at once;
   see the "page layout" section below for that deletion's own comment.) */

/* --- fonts (Task R1: copied from hub/appui/assets/ - see README's "Font
   provenance" section for source + sha256 of each file) ------------------ */
@font-face {
  font-family: "Atkinson Hyperlegible";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/atkinson-hyperlegible-regular.woff2") format("woff2");
}
@font-face {
  font-family: "Atkinson Hyperlegible";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/static/fonts/atkinson-hyperlegible-bold.woff2") format("woff2");
}
@font-face {
  font-family: "Atkinson Hyperlegible";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/atkinson-hyperlegible-italic.woff2") format("woff2");
}
@font-face {
  font-family: "Atkinson Hyperlegible";
  font-style: italic;
  font-weight: 700;
  font-display: swap;
  src: url("/static/fonts/atkinson-hyperlegible-bolditalic.woff2") format("woff2");
}

:root {
  /* Task 1 token layer (docs/superpowers/plans/2026-08-21-workspace-reskin.md
     Design Token Sheet, cited below as TOKENS), CORRECTED post-close (fix
     round 2, owner-spotted live + confirmed against the mock's own source):
     TOKENS was extracted from the mock's PRE-PATCH consts, which default to
     theme "red" ("Ellington Red"). The mock ALSO carries a replaceOnce patch
     layer ("Ellington Clerk Workspace.html", ~line 2334) applied on top of
     those consts, verified directly:
       "const ACCENT_THEMES = {\n  red: { label: 'Ellington Red', ... },"
       -> "const ACCENT_THEMES = {\n  indigo: { label: 'Indigo', ... },
           \n  red: { label: 'Brick Red', ... },"   (red's hex is UNCHANGED,
                                                       only its label moves)
       "const ACCENT_THEME_ORDER = ['red', 'green', 'navy', 'slate'];"
       -> "const ACCENT_THEME_ORDER = ['indigo', 'red', 'green', 'navy', 'slate'];"
       "accentTheme: 'red',"  ->  "accentTheme: 'indigo',"
     The mock the hub is actually gated against therefore ships with Indigo
     as its real default, not Ellington Red - --accent/-hover/-light below
     are Indigo accordingly.

     Full theme map (mock's ACCENT_THEME_ORDER, patched) - Task 6's theme
     switcher should read ITS options from this table, not re-derive them:
       indigo (DEFAULT) "Indigo"       accent #5b4fd6 / hover #473dbd / light #eeecfc
       red               "Brick Red"    accent #7a2428 / hover #5c1a1d / light #f0e2e2
       green             "Forest Green" accent #2f4a3a / hover #22352a / light #e7ede7
       navy              "Harbor Navy"  accent #2f3f5c / hover #243149 / light #e4e7ee
       slate             "Slate"        accent #4a4740 / hover #37342f / light #e9e6de
     Only the default (indigo) is wired up as :root custom properties below.
     Task 6 built the theme switcher (live html.wsk-theme-* class rules
     ~160-182 below) - this map is the switcher's data source, consumed by
     hub/townflows/prefs.py THEME_OPTIONS and the Settings page's swatch
     controls. The non-default four themes are referenced by CSS rules that
     apply their override custom properties conditionally, not dead weight. */
  --accent: #5b4fd6;
  --accent-hover: #473dbd;
  --accent-light: #eeecfc;

  /* Neutrals (TOKENS) - the wsk-* namespace every later reskin task
     consumes. Don't add a raw hex value to a new wsk-* rule below when one
     of these already names it. */
  --wsk-bg: #f8f8f8;
  --wsk-cream: #f3f0e6;
  --wsk-card: #ffffff;
  --wsk-border: #cfcfcf;
  --wsk-border-warm: #d8d3c8;
  --wsk-border-light: #e5e5e5;
  --wsk-border-row: #d9d6cc;
  --wsk-text: #242424;
  --wsk-heading: #1d1c17;
  --wsk-text-secondary: #495156;
  --wsk-meta: #636363;
  --wsk-icon: #8a8a8a;
  --wsk-disabled: #a39d8c;

  /* Vertical-rhythm scale (Home spacing pass, 2026-10-06). One short scale
     for the gaps between headings, controls and cards, so a gap is a choice
     from this list and not a one-off number: 4 hugs a label to what it
     labels, 8 sits a control beside its caption, 12 is the gap between
     sibling cards, 16 separates blocks, 24 separates groups. */
  --wsk-space-1: 4px;
  --wsk-space-2: 8px;
  --wsk-space-3: 12px;
  --wsk-space-4: 16px;
  --wsk-space-5: 24px;

  /* Phase-A aliases - kept ONLY so the R1/R2/R3 ws-* content rules further
     below (not yet migrated to wsk-* - see this file's own header comment)
     keep resolving to the exact same colors. Delete these eight lines in
     Task 7 alongside their last ws-* consumer, never before: "no old
     elements survive" (plan Global Constraints) is this task's mandate for
     the SHELL it replaces, not for content this task explicitly leaves
     alone ("Screen-content rules from R1/R2/R3 stay for now"). */
  --brand: var(--accent);
  --ink: var(--wsk-text);
  --muted: var(--wsk-text-secondary);
  --line: var(--wsk-border);
  --row-line: var(--wsk-border-row);
  --page-bg: var(--wsk-bg);
  --card-bg: var(--wsk-card);
  --header-band: var(--wsk-cream);
}

/* Task 6 (docs/superpowers/plans/2026-08-21-workspace-reskin.md): the four
   non-default Town Theme overrides - values read verbatim from this
   file's own :root comment above (the authoritative five-theme map; this
   is that map's first consumer, per the Task 6 dispatch instruction).
   Applied as an <html> class (hub.redirects.html_class_for) so a chosen
   theme recolors the ENTIRE app, not just the Settings page itself -
   live-verified against the mock: switching Town Theme there also
   recolors Home's active-tab underline, task action buttons, etc.
   (task-6-report.md). Higher specificity than the plain :root selector
   above (html.foo vs :root, both matching the same <html> element) is
   what lets these three custom properties actually override the indigo
   defaults - kept as three overridden custom properties per theme, not
   five duplicated component rulesets, so every existing
   var(--accent)/var(--accent-hover)/var(--accent-light) consumer already
   in this file (dozens of rules) picks up the swap for free.

   html.wsk-theme-indigo has no properties to override (indigo already IS
   the :root default) but still needs a real rule here, not just this
   comment mentioning it: hub.townflows.prefs.html_class renders the
   literal class "wsk-theme-indigo" on every page whose user hasn't chosen
   a different theme (the common case), and tests/test_css_coverage.py's
   dead-class scan requires every class token actually rendered in HTML to
   have a matching CSS selector somewhere in this file - a comment
   mentioning the name doesn't count (that scanner strips comments before
   tokenizing, on purpose, for exactly this reason). */
html.wsk-theme-indigo {
  /* intentionally empty - matches :root's own indigo defaults */
}
html.wsk-theme-red {
  --accent: #7a2428;
  --accent-hover: #5c1a1d;
  --accent-light: #f0e2e2;
}
html.wsk-theme-green {
  --accent: #2f4a3a;
  --accent-hover: #22352a;
  --accent-light: #e7ede7;
}
html.wsk-theme-navy {
  --accent: #2f3f5c;
  --accent-hover: #243149;
  --accent-light: #e4e7ee;
}
html.wsk-theme-slate {
  --accent: #4a4740;
  --accent-hover: #37342f;
  --accent-light: #e9e6de;
}

/* Task 6: Accessibility text-size steps - hub.townflows.prefs.
   TEXTSIZE_OPTIONS' own css_key values, applied as an <html> class the
   same way the theme classes above are. `zoom` (not a font-size/rem
   scale) - a CORRECTION over the brief's own speculative "font-size
   scaling classes" suggestion: this stylesheet uses raw px throughout
   (zero rem/em usage anywhere in this file, confirmed by grep), so a
   font-size change on <html> would visibly affect nothing; the mock
   itself achieves this same effect via an inline `zoom:` style on its own
   root app div (live-measured, task-6-report.md - min="1" max="1.5"
   step="0.125" slider driving a zoom ratio directly). `zoom` is an
   ordinary CSS property, legal here under this repo's strict CSP (which
   forbids INLINE style="..."/<style> tags, not which properties a
   LINKED stylesheet may declare) and reproduces the mock's actual
   whole-layout scaling exactly, rather than only scaling text - verified
   supported in the Chromium build this project's own Puppeteer fidelity
   harness runs. wsk-textsize-100 (zoom:1, the default) is listed
   explicitly rather than omitted so every html_class string
   hub.townflows.prefs.html_class can ever produce has a real, matching
   CSS rule (tests/test_css_coverage.py's dead-class scan requires this
   for every rendered class). */
html.wsk-textsize-100 { zoom: 1; }
html.wsk-textsize-112 { zoom: 1.125; }
html.wsk-textsize-125 { zoom: 1.25; }
html.wsk-textsize-137 { zoom: 1.375; }
html.wsk-textsize-150 { zoom: 1.5; }

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--page-bg);
  color: var(--ink);
  font-family: "Atkinson Hyperlegible", system-ui, sans-serif;
  font-size: 16px;
  line-height: 1.4;
}

a { color: inherit; }

/* --- shared shell (Task 1, 2026-08-21-workspace-reskin.md) ------------------
   workspace/fragments/shell_top.html, included by every workspace page.
   Replaces the old .ws-tabnav/.ws-topbar-actions/.ws-icon-btn/
   .ws-topbar-admin-link/.ws-tab* rules that used to live here (deleted, not
   kept alongside - "no old elements survive", plan Global Constraints) and
   the old bottom-floating .ws-searchbar-wrap (also deleted below; the
   search form now lives in row 1 here). At Task 1 close, .ws-topbar/
   .ws-topbar-row themselves were NOT yet deleted - workspace/admin.html
   (out of Task 1's scope, chartered into Task 3) still rendered its own
   header with them. (Corrected, Task 4: stale the moment Task 3 landed -
   admin.html now includes this same shared shell fragment too, and
   .ws-topbar/.ws-topbar-row/.ws-wordmark were deleted alongside that
   migration, their last consumer gone; see the "page layout" section
   below for that deletion's own comment, and tests/test_shell.py's
   admin-shell tests for the absence pin.) Layout/shape values here are
   TOKENS' (Design Token Sheet) Layout section, verbatim. */

.wsk-shell { background: var(--wsk-card); border-bottom: 1px solid var(--wsk-border); }

.wsk-shell-row1 {
  padding: 14px 40px 0;
  display: flex; align-items: center; gap: 24px;
}

/* Task 5 correction (shell polish, 2026-08-21-workspace-reskin.md): measured
   #1a1a1a, a fixed dark neutral - NOT var(--accent) (the mock's own inline
   style sets this literally, independent of whichever accent theme is
   active). No existing wsk-* neutral token names this exact value (closest
   is --wsk-heading #1d1c17, a distinct value) - recorded directly rather
   than adding a new token for a single one-off use. */
.wsk-logo {
  font-size: 19px; font-weight: 700; color: #1a1a1a; letter-spacing: .2px;
  white-space: nowrap;
  /* KAN-443: the wordmark is a link home; it keeps the plain look it had as
     text. The global :focus-visible ring still marks it for keyboard users. */
  text-decoration: none;
}

/* DEMO badge (owner directive 2026-08-30, towns.is_demo - hub/control.py):
   renders beside the wordmark on EVERY workspace screen of a demo town
   (workspace/fragments/shell_top.html, server-side conditional - absent
   entirely for a production town). Same loud pill recipe as
   .ws-badge-breakglass above ("the loudest thing in the table" - a state
   nobody should be able to miss), restated under the wsk-* namespace with
   the accent TOKEN instead of that legacy rule's --brand so it recolors
   with the town's own theme like every other wsk-* accent surface.
   flex-shrink:0 because it's a row1 flex item sitting against
   .wsk-search-col's flex:1 - the label must never be crushed into an
   ellipsis, that's the whole point of it. */
.wsk-demo-badge {
  display: inline-block; flex-shrink: 0; padding: 2px 9px;
  border-radius: 999px; background: var(--accent); color: #fff;
  font-size: 12px; font-weight: 700; letter-spacing: .8px;
  white-space: nowrap;
}

/* Fix round 1 (coordinator-gated, 2026-08-21-workspace-reskin.md Task 5):
   the search bar was left-anchored right after the logo (flex:1 + max-width
   on .wsk-search ITSELF, with .wsk-shell-icons' own margin-left:auto
   absorbing all the leftover space AFTER the bar instead of splitting it
   around it) - live re-measurement via getBoundingClientRect (not inferred
   from inline-style text this time) found the mock wraps its search pill in
   a SEPARATE centering column: `<div style="flex:1 1 0%; display:flex;
   justify-content:center">` containing the actual `width:100%;
   max-width:560px` pill - the column absorbs ALL of row1's leftover flex
   space and centers the pill WITHIN it (pill center 616.7px at 1280
   viewport width = the center of the space between the logo and the icon
   cluster, not the page's own center - confirmed by the numbers: row1
   padding 40px each side, logo 83.4px wide, icon cluster 130px wide, one
   938.6px-wide centering column absorbs everything else, gap 24px x2
   already included). .wsk-search-col (shell_top.html) is that column;
   .wsk-shell-icons' own margin-left:auto is REMOVED below - redundant and
   wrong once the column does the real work, it was only ever compensating
   for the missing column's absence. */
.wsk-search-col { flex: 1 1 0%; display: flex; justify-content: center; min-width: 0; }
.wsk-search { position: relative; width: 100%; max-width: 560px; display: flex; align-items: center; }
/* Mobile task (owner queue Q13, 2026-08-22): hit area enlarged 16x16 ->
   24x24 (the a11y floor - "Touch targets >=24px" - this control was under it
   at every viewport, not just narrow ones, so the fix is unconditional, not
   media-gated). Provably zero pixel diff for the fidelity board: the box
   grows by +8px on each axis but stays CENTERED on the exact same point
   (left 16->12 is -4px, half of +8, so the box's own center is unmoved;
   top:50%+translateY(-50%) already self-centers on any height, so it holds
   automatically as height grows too) and the child <svg> stays a literal
   16x16, now centered inside the larger box via flex - only the invisible
   box grew, not a single rendered icon pixel. */
.wsk-search-icon {
  position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
  width: 24px; height: 24px; padding: 0; margin: 0; border: none; background: none;
  appearance: none; color: var(--wsk-icon); cursor: pointer; line-height: 0;
  display: flex; align-items: center; justify-content: center;
}
/* padding 10px 14px 10px 42px (Task 5 correction: measured exactly off the
   mock's own input; was 0/16/0/44, one of Task 1's own disclosed
   inferences, reconciled here per that task's note to revisit at Task 5).
   height 40px, NOT 42 (fix round 1 correction, getBoundingClientRect this
   time, not inferred from padding+line-height arithmetic): the search pill
   is row1's OWN tallest child, so row1's entire auto-height (padding-top
   14 + tallest-child 40 = 54px measured, matching exactly) and therefore
   EVERY other row1 child's own align-items:center vertical position
   (logo, icon buttons) cascades from this one number - a 2px error here
   was silently inflating row1 by 2px and, since row2 (.wsk-tabs) starts
   flush against row1's own bottom with no gap between them, pushing the
   ENTIRE tab row down by the same 2px on top of its own separate error
   (see .wsk-tab below). */
/* color: #000, not var(--wsk-text) (fix round 1 correction): the mock's own
   input carries NO color property at all in its inline style (verified
   directly against the captured style attribute) - it renders on the
   browser's own default form-control text color, which pixel-sampling
   confirms is pure black (0,0,0), not this app's #242424 body-text token.
   A real, provable color difference (not a focus-state artifact): sampled
   at a fully-covered glyph pixel, hub read 36,36,36 where mock read
   0,0,0 - a summed distance well inside "real difference" territory. */
.wsk-search-input {
  width: 100%; height: 40px; font-size: 15px; font-family: inherit;
  color: #000; background: var(--wsk-card);
  border: 1.5px solid var(--wsk-border); border-radius: 999px;
  padding: 10px 14px 10px 42px;
}
.wsk-search-input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-light);
}
/* KAN-443: "Searching..." while a search is on its way. workspace_shell.js
   writes the words into the span and sets data-searching on the form; with the
   script off the span stays empty and takes no room. In the header pill it sits
   at the right end, inside the box, with room kept for it; on the Search page it
   sits between the box and the button. */
.wsk-search-status { font-size: 14px; color: var(--wsk-meta); white-space: nowrap; }
.wsk-search-status:empty { display: none; }
.wsk-search .wsk-search-status {
  position: absolute; right: 16px; top: 50%; transform: translateY(-50%); pointer-events: none;
}
.wsk-search[data-searching="true"] .wsk-search-input { padding-right: 96px; }
.wsk-sr-form .wsk-search-status { flex: none; font-size: 16px; }
form[data-searching="true"], form[data-searching="true"] input, form[data-searching="true"] button { cursor: progress; }
.wsk-sr-submit[aria-disabled="true"] { opacity: .6; }

.wsk-shell-icons { display: flex; align-items: center; gap: 12px; }
/* Shared layout only (shape, border, centering) - size/background/color are
   NOT uniform across the three buttons (Task 5 correction: live
   measurement found three distinct recipes, not one shared 38px/white
   treatment), so those live on the three modifier classes below instead of
   here. */
.wsk-icon-btn {
  border-radius: 50%; border: 1px solid var(--wsk-border);
  display: inline-flex; align-items: center; justify-content: center;
}
/* Task 6.5 (Q8, owner queue 2026-08-22): renamed from .wsk-icon-help - same
   button (size/bg/color recipe unchanged), the icon inside it (shell_top.html)
   is now a chat/speech-bubble glyph instead of a help-circle "?". */
.wsk-icon-chat { width: 38px; height: 38px; background: var(--wsk-card); color: var(--accent); }
.wsk-icon-chat[aria-current="page"] { background: var(--accent-light); border-color: var(--accent); }
.wsk-icon-bell { width: 34px; height: 34px; background: none; color: var(--wsk-text-secondary); }
/* The third row1 icon slot (account) - accent-light tint distinguishes it
   from the two plain neutral icon buttons beside it, using only tokens
   already declared above (never a new hex value). Task 5 correction: 34px
   (was 38px, uniform with the other two before this task's own live
   measurement told them apart) - the icon itself also swapped to the
   mock's solid/filled person glyph (shell_top.html), not the outline one. */
.wsk-avatar { width: 34px; height: 34px; background: var(--accent-light); border-color: var(--accent-light); color: var(--accent); cursor: pointer; }

/* Account menu (fix round 1, post-Task-7): the mock's own avatar popover -
   <details>/<summary>, zero JS, strict CSP - see shell_top.html's own
   comment for the full rationale. .wsk-account-menu is the <details>'
   positioning anchor; .wsk-avatar (above) is now also the <summary>, so
   list-style/marker suppression lives here rather than touching that
   shared rule. Panel recipe (border/radius/shadow) measured directly off
   the live mock's own popover (2026-08-22, not TOKENS' auth-card shadow,
   which is a different, larger card): border 1px var(--wsk-border), r10,
   shadow 0 4px 16px rgba(38,36,29,.12), width 160px (narrower than the
   mock's own 200px - this panel holds one control, not a name/role header
   too; "Profile settings" has no hub route and is never stubbed). Sign
   out's own text color (#a33e3e) is measured off the mock's own popover
   (rgb(163,62,62) - byte-identical) - already this file's one established
   danger/destructive red (.wsk-btn-danger, .priority-high below), reused
   here rather than a new token. */
.wsk-account-menu { position: relative; }
.wsk-account-menu > summary { list-style: none; }
.wsk-account-menu > summary::-webkit-details-marker { display: none; }
/* Explicit, not relied-on-implicitly: a plain position:absolute rule on
   .wsk-account-menu-panel (below) has higher selector specificity than the
   UA stylesheet's own details:not([open]) > *:not(summary) hiding rule in
   at least some engines, which can leave the panel occupying layout space
   (and worse, visible) even while collapsed. This rule's own specificity
   (class + attribute) beats the plain class rule below, so collapsed is
   unambiguously hidden regardless of engine-specific UA-stylesheet
   behavior - verified needed, not decorative (live-checked against the
   running dev server: without this rule getComputedStyle reported
   display:block on the panel while the <details> had no [open] attribute). */
.wsk-account-menu:not([open]) > .wsk-account-menu-panel { display: none; }
.wsk-account-menu-panel {
  position: absolute; right: 0; top: 42px; width: 160px;
  background: var(--wsk-card); border: 1px solid var(--wsk-border);
  border-radius: 10px; box-shadow: 0 4px 16px rgba(38, 36, 29, .12);
  overflow: hidden; z-index: 5;
}
.wsk-account-menu-signout {
  display: block; width: 100%; text-align: left; background: none;
  border: none; padding: 12px 16px; font-size: 15px; font-weight: 700;
  font-family: inherit; color: #a33e3e; cursor: pointer;
}
.wsk-account-menu-signout:hover { background: var(--wsk-bg); }

/* Task 5 correction (shell polish): live-measured tab row is CONTENT-SIZED
   (gap:4px, no flex:1 stretch) with NO dividers between tabs at all - a
   correction over TOKENS' own documented "tabs flex:1 ... 2px dividers
   except last" layout note, which this investigation found stale against
   the CURRENT mock capture (same pattern as the Indigo default/auth-kicker
   letter-spacing corrections earlier tasks already made - TOKENS was
   extracted once, 2026-08-21, and the mock has visibly moved since for at
   least these elements). Padding 8px 32px 0 (was 12px 40px 0); each tab
   padding 10px 16px 12px (was 0 0 12px) with a leading icon (gap:8px,
   shell_top.html) and rounded top corners (radius 8px 8px 0 0) - all
   measured directly.

   Task 6.5 (Q12, owner queue 2026-08-22) FIRST PASS, RE-VERIFIED LIVE: the
   queue item asks to "verify tabs spread evenly across the full width...
   flex:1 should already do it; fix if uneven." Live re-measurement against
   the STILL-CURRENT mock (2026-08-22, same day) via getBoundingClientRect +
   getComputedStyle at the real 1280x900 gate viewport found the mock's own
   nav row's authored inline style is still literally `display: flex; gap:
   4px; padding: 8px 32px 0px` with NO flex-grow on any tab (each tab's own
   inline style carries no `flex` property at all, i.e. the CSS default
   `flex: 0 1 auto`) - content-sized, NOT evenly spread, confirmed byte-
   identical to the hub's own rendering: both sides measured tab widths
   [101.2, 100.4, 121.2, 106.8]px (Home/Email/Settings/Admin) at IDENTICAL
   x-offsets, leaving the same ~800px of unused nav-row width on the right
   in BOTH. This technical finding was correct and independently confirmed
   (auditor + tester); see below for why the DISPOSITION built on it was
   overturned.

   APPROVED DEVIATION #7 (controller adjudication, fix round 1, same day):
   Q12 is an OWNER DESIGN DIRECTIVE, not a fidelity question the mock gets
   to settle - double provenance: an annotated screenshot ("expand the 4
   buttons across") plus the dictated queue item's own "spread that evenly
   across the plane." Same posture as R-1 (El-bot -> indigo) and Q8 (chat
   icon): the mock loses. `.wsk-tab` now carries `flex: 1 1 0%` +
   `justify-content: center` (below) so each tab shares the nav row's full
   width evenly, at 3 tabs (non-admin) and 4 (admin) alike - the SAME
   unconditional rule governs both counts (no :nth-child/[href=...] override
   exists anywhere in this file - tests/test_shell.py::test_admin_tab_
   absent_for_non_admin_user already exercises the 3-tab markup shape
   structurally, so a second live capture at 3 tabs isn't needed to trust
   this generalizes). This is an INTENDED, NAMED difference from the mock on
   every shell-bearing screen from here on (home/email/email_detail/search/
   ask/settings/admin all include this same shared shell) - it will widen
   the measured chrome diff on all of them vs the mock's own left-packed
   tabs; that is the point of the override, never a defect to chase back
   down. See tools/e2e/fidelity.mjs's own settings/home driver-pair comments
   for the fidelity-report attribution this now carries, and
   test_shell.py::test_wsk_tab_flexes_evenly_and_carries_no_fixed_width_
   override for the pin. */
.wsk-tabs { display: flex; gap: 4px; padding: 8px 32px 0; }
/* height: 44px, EXPLICIT (fix round 1 correction, 2026-08-21-workspace-
   reskin.md Task 5): the earlier "line-height:normal" attempt (still kept,
   harmless) assumed the mismatch was body's own line-height:1.4 leaking in
   - it barely moved the number (8291->8274 diff px in that round's own
   measurement) because Atkinson Hyperlegible's OWN 'normal' line-height for
   16px/700 apparently already lands close to 1.4x anyway, so the override
   was correcting for a cause that wasn't the dominant one. getBoundingClientRect
   against the live mock (not inferred from inline-style padding text this
   time) measured the ACTUAL tab height directly: 44.4px (y=62 to
   bottom=106.4, tabsRow itself starting at y=54 with padding-top 8 ->
   content starts at 62). Setting height explicitly removes any dependency
   on this font's own line-height metrics entirely, rather than continuing
   to guess at them - align-items:center still centers the icon+label
   within it. Combined with the row1 fix above (row1 bottom now lands at
   the same y=54 on both sides, since .wsk-tabs starts flush against it),
   this is what actually closes the "whole tab row 3px lower" finding - the
   line-height change alone never could. */
.wsk-tab {
  display: inline-flex; align-items: center; justify-content: center;
  /* gap/font-size/padding read from --wsk-tab-* custom properties, each with
     a fallback equal to the ORIGINAL literal value (8px/16px/16px) - Mobile
     task (owner queue Q13): the mobile media block far below this file
     redefines these three on :root under its two breakpoints so this ONE
     rule (never duplicated - see test_shell.py::
     test_wsk_tab_flexes_evenly_and_carries_no_fixed_width_override, which
     asserts exactly one `.wsk-tab {` rule exists in the whole file) can
     shrink at narrow widths without a second selector. Outside those
     breakpoints the var()s resolve to their fallback, i.e. byte-identical
     to the values this rule always had - zero desktop/fidelity drift. */
  gap: var(--wsk-tab-gap, 8px);
  /* flex: 1 1 0% - approved deviation #7 (owner directive, this section's
     own header comment above): grow/shrink evenly, zero basis, so N tabs
     always split the row's available width equally regardless of each
     tab's own content width. justify-content:center (added alongside -
     text-align:center on its own does nothing for a flex container's own
     flex-item children, which is what the icon+label pair here are) keeps
     the icon+label centered WITHIN each tab's now-wider box; the 3px
     active border-bottom needs no separate change - a border-bottom
     inherently spans its own element's full width, so it automatically
     stretches to match. */
  flex: 1 1 0%;
  /* overflow: hidden (Mobile task, Q13) - NOT `min-width: 0` (a fixed-width
     regression risk this rule is explicitly pinned against by name - the
     literal substring "width:" is banned from this rule's body by the same
     test cited above, `assert "width:" not in body`): per the flexbox spec,
     a flex item's automatic min-width floor (its own content's min-content
     size, which is what stops `flex: 1 1 0%` from ever shrinking a tab
     below "Settings"'s own unbroken text width) resolves to 0 whenever the
     item's own overflow is anything other than `visible` - this achieves
     the identical "let this tab shrink below its label's width instead of
     forcing the row wider than the viewport" effect without ever writing
     that banned substring. Invisible at every width this file's own
     fidelity board measures (1280px): tabs never come close to their own
     min-content floor there, so nothing is ever actually clipped. */
  overflow: hidden;
  background: none; border: none; border-bottom: 3px solid transparent;
  border-radius: 8px 8px 0 0;
  font-size: var(--wsk-tab-font, 16px); font-weight: 700; color: var(--wsk-text-secondary);
  line-height: normal;
  height: 44px;
  padding: 0 var(--wsk-tab-pad-x, 16px); text-align: center; text-decoration: none;
  transition: transform .15s ease;
}
.wsk-tab-active { color: var(--accent); border-bottom-color: var(--accent); }
.wsk-tab:active { transform: scale(.96); }

/* --- shared component recipes (Task 1) ---------------------------------------
   wsk-* building blocks for later reskin tasks (buttons/chips/inputs/table
   header/card - TOKENS' Component recipes + Shape sections). NOT consumed
   by any screen-content markup yet: the R1/R2/R3 ws-* rules further below
   stay as the live content styling this task (plan Global Constraints:
   "Screen-content rules from R1/R2/R3 stay for now"); Tasks 2-6 migrate
   their own screen onto these one at a time, Task 7 deletes whatever ws-*
   is left. .wsk-btn-outline's padding/font-size are inferred from its
   sibling .wsk-btn-neutral (TOKENS states r8px explicitly for both, but
   gives neither an explicit padding, and only states 14/700 for
   .wsk-btn-neutral) rather than invented outright from nothing - recorded
   here since TOKENS itself doesn't spell it out. */

.wsk-shell-panel { width: min(560px, calc(100vw - 32px)); max-height: 85vh; padding: 22px; border: 1px solid #ddd; border-radius: 14px; background: var(--surface, #fff); color: var(--text-primary, #222); box-shadow: 0 12px 48px #0003; }
.wsk-shell-panel::backdrop { background: #0004; }
.wsk-source-document { white-space: pre-wrap; overflow-wrap: anywhere; padding: 20px; background: white; border: 1px solid #ddd; border-radius: 12px; }
.wsk-shell-panel-heading, .wsk-shell-panel-actions { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.wsk-shell-panel-heading h2 { margin: 0; font-size: 20px; }
.wsk-shell-panel form { display: grid; gap: 8px; }
.wsk-chat-page { min-height: 100vh; min-height: 100dvh; height: 100vh; height: 100dvh; display: flex; flex-direction: column; }
.wsk-chat-page .wsk-shell { flex-shrink: 0; }
.wsk-chat-main { width: calc(100% - 32px); max-width: 1120px; margin: 20px auto; flex: 1; min-height: 0; display: flex; flex-direction: column; }
.wsk-chat-main h1 { margin: 0; color: var(--wsk-heading); font-size: 30px; }
.wsk-chat-intro { margin: 8px 0 20px; color: var(--wsk-text-secondary); }
.wsk-chat-session { padding: 20px; border: 1px solid var(--wsk-border-light); border-radius: 14px; background: var(--wsk-card); display: flex; flex-direction: column; flex: 1; min-height: 0; }
.wsk-chat-messages { flex: 1; min-height: 0; overflow-x: hidden; overflow-y: auto; padding-bottom: 16px; }
.wsk-chat-composer { display: grid; gap: 8px; border-top: 1px solid var(--wsk-border-light); padding-top: 20px; flex-shrink: 0; }
.wsk-chat-composer label { font-weight: 700; }
.wsk-chat-composer textarea { width: 100%; resize: vertical; }
.wsk-chat-hint { margin: 0; color: var(--wsk-meta); font-size: 13px; }
.wsk-chat-composer [role="status"] { margin: 0; }
.wsk-chat-actions { display: flex; flex-wrap: wrap; gap: 12px; }
.wsk-shell-message { padding: 12px; margin: 8px 0; background: var(--accent-light, #f0ecff); border-radius: 8px; overflow-wrap: anywhere; }
.wsk-shell-message p { white-space: pre-wrap; margin: 6px 0; }
.wsk-shell-message ul, .wsk-shell-message ol { margin: 6px 0; padding-left: 24px; }
.wsk-shell-message > :is(p, ul, ol) + :is(p, ul, ol) { margin-top: 12px; }
.wsk-shell-message li { white-space: pre-wrap; }

.wsk-icon-bell { position: relative; }
.wsk-icon-bell [data-notification-badge] { position: absolute; top: -4px; right: -5px; border-radius: 12px; padding: 2px 5px; background: var(--accent); color: white; font-size: 10px; }
.wsk-icon-bell [hidden] { display: none; }
.wsk-btn-primary {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  background: var(--accent); color: #fff; border: 1px solid var(--accent);
  border-radius: 9px; padding: 10px 18px; font-size: 15px; font-weight: 700;
  font-family: inherit; text-decoration: none; cursor: pointer;
  transition: background-color .15s ease, border-color .15s ease, transform .15s ease;
}
.wsk-btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
.wsk-btn-primary:active { transform: scale(.96); }

.wsk-btn-outline {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  background: #fff; color: var(--accent); border: 1.5px solid var(--accent);
  border-radius: 8px; padding: 10px 18px; font-size: 14px; font-weight: 700;
  font-family: inherit; text-decoration: none; cursor: pointer;
  transition: background-color .15s ease, transform .15s ease;
}
.wsk-btn-outline:hover { background: var(--accent-light); }
.wsk-btn-outline:active { transform: scale(.96); }

.wsk-btn-neutral {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  background: #fff; color: var(--wsk-text-secondary); border: 1px solid var(--wsk-border);
  border-radius: 8px; padding: 10px 18px; font-size: 14px; font-weight: 700;
  font-family: inherit; text-decoration: none; cursor: pointer;
  transition: border-color .15s ease, transform .15s ease;
}
.wsk-btn-neutral:hover { border-color: var(--accent); }
.wsk-btn-neutral:active { transform: scale(.96); }

.wsk-chip {
  display: inline-flex; align-items: center;
  background: var(--wsk-cream); border: 1px solid var(--wsk-border);
  border-radius: 999px; padding: 6px 14px; font-size: 14px;
  font-family: inherit; text-decoration: none; cursor: pointer;
  transition: background-color .15s ease, border-color .15s ease, color .15s ease;
}
.wsk-chip-active { background: var(--accent); color: #fff; border-color: var(--accent); }

.wsk-input {
  width: 100%; font-family: inherit; font-size: 15px; color: var(--wsk-text);
  background: var(--wsk-card); border: 1.5px solid var(--wsk-border);
  border-radius: 8px; padding: 10px 12px;
}
.wsk-input:focus { border-color: var(--accent); }

.wsk-thead {
  background: var(--wsk-cream); border-bottom: 1px solid var(--wsk-border);
  padding: 12px 20px; font-size: 13px; font-weight: 700;
  color: var(--wsk-text-secondary); text-transform: uppercase; letter-spacing: .03em;
}

.wsk-card {
  background: var(--wsk-card); border: 1px solid var(--wsk-border);
  border-radius: 14px; padding: 20px;
  box-shadow: 0 2px 10px rgba(38, 36, 29, .05);
  transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}
.wsk-card:hover {
  border-color: var(--accent); box-shadow: 0 2px 8px rgba(38, 36, 29, .08);
  transform: translateY(-1px);
}

/* Global a11y focus ring (TOKENS: "fixed, a11y" - not themeable, layered
   on top of any component's own focus styling, e.g. .wsk-search-input's
   accent-light glow above). :focus-visible so it shows for keyboard
   navigation, not every mouse click. */
:focus-visible { outline: 3px solid #0b57d0; outline-offset: 3px; }

@media (prefers-reduced-motion: reduce) {
  /* .wsk-auth-sso-btn added (Task 5 rider, from Task 2's own deferred
     minor): it declares a transition (border-color/transform, see the auth
     screens section below) just like the five listed beside it, and was
     missed from this kill-switch when Task 2 introduced it.
     .wsk-theme-swatch-btn added (Task 6): declares its own transform
     transition (settings section above) - added in the SAME commit that
     introduces it, not deferred to a later rider like .wsk-auth-sso-btn
     above had to be. */
  .wsk-tab, .wsk-btn-primary, .wsk-btn-outline, .wsk-btn-neutral, .wsk-chip, .wsk-card,
  .wsk-auth-sso-btn, .wsk-theme-swatch-btn {
    transition: none;
  }
}

/* --- page layout ------------------------------------------------------------
   Task 3 (2026-08-21-workspace-reskin.md) migrated workspace/admin.html
   onto the shared shell fragment (workspace/fragments/shell_top.html,
   Task 1) - its own hand-rolled <header class="ws-topbar"><div
   class="ws-topbar-row"> is gone, and with it the last consumer of BOTH
   that pair AND .ws-wordmark (admin.html's <span class="ws-wordmark"> lived
   inside that same header; every other template that once rendered
   .ws-wordmark directly - login/register/forgot_password/join - already
   moved onto .wsk-auth-kicker/.wsk-auth-title in Task 2). All three rules
   are deleted here, not left "for Task 7" as their retention comments used
   to say - "dead CSS is removed in the same task that retires its last
   consumer" (plan Global Constraints), and Task 3 is that task now that
   admin.html's own shell migration was chartered into it. */

.ws-main {
  max-width: 1280px; margin: 0 auto; padding: 36px 40px 40px;
  display: flex; gap: 32px; align-items: flex-start;
}
.ws-content { flex: 1; display: flex; flex-direction: column; gap: 24px; min-width: 0; }
.ws-side { width: 300px; flex-shrink: 0; display: flex; flex-direction: column; gap: 20px; }

.ws-greeting { margin: 0 0 4px; font-size: 26px; font-weight: 700; }
.ws-date { margin: 0; font-size: 16px; color: var(--muted); }

.ws-card {
  background: var(--card-bg); border: 1px solid var(--line);
  border-radius: 14px; padding: 20px;
}
.ws-card-title {
  margin: 0 0 12px; font-size: 15px; font-weight: 700;
  letter-spacing: .03em; text-transform: uppercase; color: var(--muted);
}
.ws-muted { margin: 0; font-size: 15px; color: #636363; }

/* --- Home: page layout, greeting, generic card title/empty (Task 3,
   2026-08-21-workspace-reskin.md) --------------------------------------------
   wsk-* rewrite of R1's page shell for the one page that owns this content
   now. ws-main/ws-content/ws-side/ws-greeting/ws-date (further below,
   unchanged) stay defined too - admin.html (out of Task 3's charter beyond
   its own shell migration) is still their only remaining consumer.
   max-width 1400px is Home's own measured value (direct DOM measurement
   against the mock, 2026-08-21), not TOKENS' generic 1280px Layout figure -
   the four Task-1-migrated screens (email/email_detail/search/ask) keep
   1280 via the ws-main rule below, which they share with admin.html; only
   Home differs. */
.wsk-main {
  max-width: 1400px; margin: 0 auto; padding: 36px 40px 40px;
  display: flex; gap: 32px; align-items: flex-start;
}
.wsk-content { flex: 1; display: flex; flex-direction: column; gap: var(--wsk-space-4); min-width: 0; }
.wsk-side { width: 300px; flex-shrink: 0; display: flex; flex-direction: column; gap: 20px; }

/* The greeting and its date are one compact line (owner order 2026-10-11): the date
   sits on the greeting's baseline 16px to its right and drops under it (4px) only
   when the line runs out of width, as on a phone. The board follows one block-gap
   (16px) below. */
.wsk-greeting-block { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--wsk-space-1) var(--wsk-space-4); }
.wsk-greeting { margin: 0 0 4px; font-size: 26px; font-weight: 700; }
.wsk-greeting-block > .wsk-greeting { margin: 0; font-size: 24px; line-height: 1.25; }
.wsk-date { margin: 0; font-size: 16px; color: var(--wsk-text-secondary); }

.wsk-card-title {
  margin: 0 0 12px; font-size: 15px; font-weight: 700;
  letter-spacing: .03em; text-transform: uppercase; color: var(--wsk-text-secondary);
}
.wsk-empty { margin: 0; font-size: 15px; color: var(--wsk-meta); }

/* --- My Tasks (Task 3) -------------------------------------------------------
   R1 rendered "My Tasks" as table rows (KAN-111 comment below, written
   against an OLDER reference than this task's own mock). The mock actually
   gated against here (verified live 2026-08-21) renders one CARD per task
   instead - this replaces the whole #tasks-card / ws-tasks-* / ws-select /
   ws-viewer-banner / ws-thead / ws-th-* / ws-row / ws-td-* / ws-task-text /
   ws-task-more/ws-due-input/ws-hide-inert/ws-suggestion-panel family for
   Home; ws-btn-primary/ws-btn-suggestion/ws-empty (further below) stay
   defined for admin_users.html/admin_invite.html. */
.wsk-tasks-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-bottom: 12px;
}
.wsk-tasks-heading { margin: 0; font-size: 19px; font-weight: 700; color: var(--wsk-heading); }
.wsk-tasks-controls { display: flex; align-items: center; gap: 8px; }
/* Layout only. The look of every <select> (height, border, radius, chevron,
   states) is the one shared rule in the "upload page + modern selects" block
   at the end of this file. */
.wsk-sort-select { min-width: 190px; flex-shrink: 0; }
/* Full-strength, not dimmed: matches the Task 1 shell's own inert-control
   convention (wsk-icon-btn's help/notification/account controls carry no
   opacity reduction either) and the mock's own "+ Add task" rendering
   (measured live, 2026-08-21 - no opacity property at all) - R1's OLDER
   ws-btn-ghost[aria-disabled] pattern (further below, opacity .5) predates
   both and is NOT the convention this task's new wsk-* inert controls
   follow. pointer-events:none is kept (harmless on the <span> this
   modifies - neither natively interactive nor actually clickable either
   way) purely as a second, redundant signal of non-interactivity. */
.wsk-btn-outline[aria-disabled="true"], .wsk-btn-neutral[aria-disabled="true"] {
  pointer-events: none;
}

.wsk-delegated-notice {
  margin: 0 0 10px; font-size: 14px; color: #8c5d26; background: #faf1de;
  border: 1px solid #8c5d26; border-radius: 8px; padding: 4px 6px 4px 12px;
  display: flex; align-items: center; gap: 8px;
}
.wsk-delegated-notice-text { flex: 1; min-width: 0; }
.wsk-notice-dismiss {
  width: 28px; height: 28px; flex: 0 0 28px; display: inline-flex;
  align-items: center; justify-content: center; border-radius: 6px;
  color: #6f5217; font-size: 20px; font-weight: 700; line-height: 1; cursor: not-allowed;
}

.wsk-tasks-list { display: flex; flex-direction: column; gap: 16px; min-width: 0; }

.wsk-task-card { position: relative; padding: 20px 24px; }
.wsk-card-dismiss {
  position: absolute; top: 14px; right: 14px; width: 28px; height: 28px;
  border-radius: 50%; display: flex; align-items: center; justify-content: center;
  color: var(--wsk-icon); font-size: 18px; line-height: 1; cursor: not-allowed;
}
.wsk-task-tags { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 14px; padding-right: 32px; }
.wsk-task-body { padding-right: 32px; }
.wsk-task-title {
  margin: 0 0 8px; font-size: 19px; font-weight: 700; line-height: 1.35; color: var(--wsk-text);
  /* KAN-377: shown in full. A title is at most 300 characters, so the old
     3-line clamp and its "Show more" protected nothing; a long unbroken
     token still wraps instead of widening the card. */
  overflow-wrap: anywhere;
}

.wsk-task-meta { display: flex; align-items: center; gap: 6px; margin-top: 12px; }
.wsk-task-received, .wsk-task-meta-sep { font-size: 14px; color: var(--wsk-icon); white-space: nowrap; }
.wsk-task-due-label { font-size: 14px; font-weight: 700; color: var(--wsk-text); white-space: nowrap; }
.wsk-task-due-input {
  /* Fixed styling for every row rather than the mock's conditional overdue
     red: hub.townflows.bootstrap.derive_tasks() already computes a
     per-task dueSort offset that would make real overdue detection
     possible, but hub.townflows.home.task_row() doesn't surface it on the
     row shape this template receives, and adding that is a townflow
     change outside this task's "townflows untouched" scope - see
     task-3-report.md. Bold + dark (not muted) still gives the field more
     visual weight than the plain received-date beside it, without
     asserting an urgency status this row shape can't back up. */
  width: 90px; text-align: left; font-size: 14px; font-weight: 700; color: var(--wsk-text);
  padding: 2px 26px 2px 6px; border: none; background: transparent no-repeat right 4px center;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%238a8a8a' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'/%3E%3Cline x1='16' y1='2' x2='16' y2='6'/%3E%3Cline x1='8' y1='2' x2='8' y2='6'/%3E%3Cline x1='3' y1='10' x2='21' y2='10'/%3E%3C/svg%3E");
  font-family: inherit;
}

.wsk-task-divider { height: 1px; background: var(--wsk-border-light); margin: 16px 0; }
.wsk-task-actions { display: flex; align-items: center; gap: 10px; }
.wsk-task-action {
  background: var(--accent); color: #fff; border: none; border-radius: 8px;
  padding: 10px 20px; font-size: 14px; font-weight: 700; text-decoration: none;
  display: inline-flex; align-items: center; cursor: pointer;
}
.wsk-task-action:hover { background: var(--accent-hover); }
.wsk-task-action-secondary {
  background: #fff; color: var(--accent); border: 1.5px solid var(--accent); border-radius: 8px;
  padding: 8.5px 16px; font-size: 14px; font-weight: 700; font-family: inherit;
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
}
.wsk-task-action-secondary:hover { background: var(--accent-light); }

.wsk-suggestion-panel:empty { display: none; }
.wsk-suggestion-panel {
  margin-top: 14px; padding: 10px 14px; font-size: 13.5px; color: var(--wsk-text-secondary);
  background: var(--wsk-bg); border: 1px solid var(--wsk-border-row); border-radius: 8px;
}

/* --- Today's Schedule (Task 3) -----------------------------------------------
   The mock's hour grid uses continuous style="top:Npx" placement (its own
   inline-style idiom - strictly forbidden here, see this file's header
   comment and workspace/fragments/macros.html's schedule_slot_key). Each
   event's vertical position is instead one of 21 fixed half-hour classes
   below; the 11 on-the-hour ones double as the gutter labels/gridlines
   (static, hardcoded in home.html) and any event landing exactly on the
   hour. 22px = half of the 44px/hour scale measured directly off the mock
   (8 AM-6 PM spans 440px there). */
.wsk-sched-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }
.wsk-sched-title-wrap { display: flex; align-items: center; gap: 8px; }
.wsk-sched-icon { flex-shrink: 0; color: var(--accent); }
.wsk-sched-title { margin: 0; font-size: 17px; font-weight: 700; color: var(--wsk-heading); }
.wsk-sched-nav { display: flex; align-items: center; gap: 4px; }
.wsk-sched-nav-btn {
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--wsk-border); border-radius: 8px; color: var(--wsk-text-secondary);
  font-size: 14px; font-weight: 700; padding: 4px 8px; line-height: 1; cursor: not-allowed;
}
a.wsk-sched-nav-btn, button.wsk-sched-nav-btn, summary.wsk-sched-nav-btn { cursor: pointer; text-decoration: none; background: var(--wsk-bg); }
#schedule-card .wsk-sched-nav { flex-wrap: wrap; }
/* KAN-235's calendar control, ported to the card head (KAN-246). The
   <details> is the positioning anchor; its form floats below the toggle
   (panel recipe = the account menu's). The :not([open]) rule is explicit
   for the same UA-stylesheet reason .wsk-account-menu-panel gives above. */
.wsk-sched-calendar { position: relative; }
.wsk-sched-calendar > summary { list-style: none; }
.wsk-sched-calendar > summary::-webkit-details-marker { display: none; }
.wsk-sched-calendar:not([open]) > .wsk-sched-calendar-form { display: none; }
.wsk-sched-calendar-form {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 5; min-width: 220px;
  display: grid; gap: 6px; margin: 0; padding: 12px;
  background: var(--wsk-card); border: 1px solid var(--wsk-border);
  border-radius: 10px; box-shadow: 0 4px 16px rgba(38, 36, 29, .12);
}
.wsk-sched-calendar-form label { display: grid; gap: 4px; font-size: 13px; color: var(--wsk-text-secondary); }
.wsk-sched-calendar-form input {
  font: inherit; max-width: 100%; padding: 6px 8px; box-sizing: border-box;
  border: 1px solid var(--wsk-border); border-radius: 6px; color: var(--wsk-text);
}
.wsk-sched-calendar-form .wsk-task-action { justify-self: start; }
.wsk-schedule-tasks { padding-left: 20px; }
.wsk-schedule-tasks li { margin-bottom: 16px; overflow-wrap: anywhere; }
.wsk-schedule-tasks p { margin: 4px 0; }

/* Flow rows keep simultaneous meetings visible instead of covering one another. */
/* One date row stays usable in a narrow sidebar. */
#schedule-card .wsk-sched-head { gap: 8px; margin-bottom: 8px; }
#schedule-card .wsk-sched-title-wrap { min-width: 0; }
/* Owner 2026-10-06, "buttons are fat too big doesnt look clean", then on the next
   screenshot "make the buttons cleaner no border on buttons so it looks seamless":
   every control in the card header is a quiet 32px target with NO border and NO fill
   at rest (8px radius, 16px icon). Hover and keyboard focus lay a soft tint over it;
   keyboard focus also draws an outline ring - an outline, never a border, so the row
   cannot shift and forced-colors modes keep it. The date is a compact pill centred
   between the arrows, not a block that fills the row. Refresh and collapse share one
   small group at the end of the title row. An open date picker keeps its pill tinted,
   the one cue a borderless pill would otherwise lose. */
#schedule-card .wsk-sched-nav-btn { min-height: 32px; padding: 0 10px; box-sizing: border-box; border: 0; border-radius: 8px; background: transparent; font-size: 13px; line-height: 1; }
#schedule-card .wsk-sched-icon-btn { width: 32px; height: 32px; padding: 0; flex-shrink: 0; }
#schedule-card .wsk-sched-nav-btn:hover, #schedule-card .wsk-sched-nav-btn:focus-visible, #schedule-card .wsk-sched-calendar[open] > summary { background: var(--accent-light); color: var(--wsk-text); }
#schedule-card .wsk-sched-nav-btn:focus-visible { outline: 3px solid #0b57d0; outline-offset: 1px; }
#schedule-card .wsk-sched-actions { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
#schedule-card .wsk-sched-day { flex-wrap: nowrap; justify-content: center; gap: 6px; margin-bottom: 12px; }
#schedule-card .wsk-sched-day .wsk-sched-calendar { flex: 0 1 auto; min-width: 0; }
#schedule-card .wsk-sched-calendar > summary { width: auto; }
#schedule-card .wsk-sched-head form { margin: 0; }
#schedule-card .wsk-sched-calendar-form { left: 50%; right: auto; transform: translateX(-50%); }
.wsk-meeting-hour { display: grid; grid-template-columns: 42px minmax(0, 1fr); gap: 10px; }
.wsk-meeting-hour-label { font-size: 11px; color: var(--wsk-icon); white-space: nowrap; transform: translateY(-6px); }
.wsk-meeting-hour-body { min-height: 44px; border-top: 1px solid var(--wsk-border-light); border-left: 1px solid var(--wsk-border-light); }
.wsk-meeting { display: block; margin: 0 2px 4px 6px; padding: 3px 8px; min-height: 40px; box-sizing: border-box; border-left: 3px solid currentColor; border-radius: 3px; text-decoration: none; }
.wsk-meeting:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.wsk-meeting .wsk-sched-event-time { display: block; }
/* An event read live from the clerk's Microsoft calendar: same shape as an
   extracted meeting, visibly not a link, because there is no email behind
   it to open. */
.wsk-meeting-calendar { display: block; color: #2f6f4f; background: #f0f7f3; cursor: default; }
.wsk-calendar-grid, .wsk-calendar-enhanced-list { display: none; }
.wsk-calendar-enhanced .wsk-calendar-fallback { display: none; }
.wsk-calendar-enhanced .wsk-calendar-grid { display: flex; gap: 8px; margin: 12px 0; }
.wsk-calendar-enhanced .wsk-calendar-enhanced-list { display: block; }
.wsk-calendar-gutter { position: relative; width: 40px; flex-shrink: 0; height: 528px; }
.wsk-calendar-gutter > span { position: absolute; font-size: 11px; color: var(--wsk-text-secondary); }
.wsk-calendar-track { position: relative; display: grid; grid-template-columns: repeat(12,minmax(0,1fr)); grid-template-rows: repeat(660,0.8px); flex: 1; min-width: 0; height: 528px; }
.wsk-calendar-line { position: absolute; left: 0; right: 0; border-top: 1px solid var(--wsk-border-light); pointer-events: none; }
.wsk-calendar-block { z-index: 1; margin: 0 2px 1px; padding: 3px 5px; border-left: 3px solid currentColor; border-radius: 4px; overflow: hidden; font-size: 12px; line-height: 1.2; min-width: 0; }
.wsk-calendar-block a, .wsk-calendar-block strong { display: block; overflow: hidden; text-overflow: ellipsis; }
.wsk-calendar-range { display: block; font-size: 10px; font-weight: 600; white-space: nowrap; }
.wsk-calendar-block small { display: block; font-size: 10px; }
/* KAN-506: the "No end time" pill of a start-only block sits on the time line, smaller than in the lists. */
.wsk-calendar-range-open { white-space: normal; }
.wsk-calendar-range-open ~ a, .wsk-calendar-range-open ~ strong { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; }
.wsk-calendar-range .wsk-event-tag { padding: 0 6px; font-size: 9px; font-weight: 700; line-height: 1.4; vertical-align: 1px; }
.wsk-calendar-block.wsk-calendar-marker { padding: 0; }
.wsk-meeting-hour:last-child .wsk-meeting-hour-body { min-height: 0; }
/* Minute offsets use classes because the application forbids inline styles. */
.wsk-meeting-minute-01 > .wsk-meeting:first-child { margin-top: 0.733px; }
.wsk-meeting-minute-02 > .wsk-meeting:first-child { margin-top: 1.467px; }
.wsk-meeting-minute-03 > .wsk-meeting:first-child { margin-top: 2.2px; }
.wsk-meeting-minute-04 > .wsk-meeting:first-child { margin-top: 2.933px; }
.wsk-meeting-minute-05 > .wsk-meeting:first-child { margin-top: 3.667px; }
.wsk-meeting-minute-06 > .wsk-meeting:first-child { margin-top: 4.4px; }
.wsk-meeting-minute-07 > .wsk-meeting:first-child { margin-top: 5.133px; }
.wsk-meeting-minute-08 > .wsk-meeting:first-child { margin-top: 5.867px; }
.wsk-meeting-minute-09 > .wsk-meeting:first-child { margin-top: 6.6px; }
.wsk-meeting-minute-10 > .wsk-meeting:first-child { margin-top: 7.333px; }
.wsk-meeting-minute-11 > .wsk-meeting:first-child { margin-top: 8.067px; }
.wsk-meeting-minute-12 > .wsk-meeting:first-child { margin-top: 8.8px; }
.wsk-meeting-minute-13 > .wsk-meeting:first-child { margin-top: 9.533px; }
.wsk-meeting-minute-14 > .wsk-meeting:first-child { margin-top: 10.267px; }
.wsk-meeting-minute-15 > .wsk-meeting:first-child { margin-top: 11px; }
.wsk-meeting-minute-16 > .wsk-meeting:first-child { margin-top: 11.733px; }
.wsk-meeting-minute-17 > .wsk-meeting:first-child { margin-top: 12.467px; }
.wsk-meeting-minute-18 > .wsk-meeting:first-child { margin-top: 13.2px; }
.wsk-meeting-minute-19 > .wsk-meeting:first-child { margin-top: 13.933px; }
.wsk-meeting-minute-20 > .wsk-meeting:first-child { margin-top: 14.667px; }
.wsk-meeting-minute-21 > .wsk-meeting:first-child { margin-top: 15.4px; }
.wsk-meeting-minute-22 > .wsk-meeting:first-child { margin-top: 16.133px; }
.wsk-meeting-minute-23 > .wsk-meeting:first-child { margin-top: 16.867px; }
.wsk-meeting-minute-24 > .wsk-meeting:first-child { margin-top: 17.6px; }
.wsk-meeting-minute-25 > .wsk-meeting:first-child { margin-top: 18.333px; }
.wsk-meeting-minute-26 > .wsk-meeting:first-child { margin-top: 19.067px; }
.wsk-meeting-minute-27 > .wsk-meeting:first-child { margin-top: 19.8px; }
.wsk-meeting-minute-28 > .wsk-meeting:first-child { margin-top: 20.533px; }
.wsk-meeting-minute-29 > .wsk-meeting:first-child { margin-top: 21.267px; }
.wsk-meeting-minute-30 > .wsk-meeting:first-child { margin-top: 22px; }
.wsk-meeting-minute-31 > .wsk-meeting:first-child { margin-top: 22.733px; }
.wsk-meeting-minute-32 > .wsk-meeting:first-child { margin-top: 23.467px; }
.wsk-meeting-minute-33 > .wsk-meeting:first-child { margin-top: 24.2px; }
.wsk-meeting-minute-34 > .wsk-meeting:first-child { margin-top: 24.933px; }
.wsk-meeting-minute-35 > .wsk-meeting:first-child { margin-top: 25.667px; }
.wsk-meeting-minute-36 > .wsk-meeting:first-child { margin-top: 26.4px; }
.wsk-meeting-minute-37 > .wsk-meeting:first-child { margin-top: 27.133px; }
.wsk-meeting-minute-38 > .wsk-meeting:first-child { margin-top: 27.867px; }
.wsk-meeting-minute-39 > .wsk-meeting:first-child { margin-top: 28.6px; }
.wsk-meeting-minute-40 > .wsk-meeting:first-child { margin-top: 29.333px; }
.wsk-meeting-minute-41 > .wsk-meeting:first-child { margin-top: 30.067px; }
.wsk-meeting-minute-42 > .wsk-meeting:first-child { margin-top: 30.8px; }
.wsk-meeting-minute-43 > .wsk-meeting:first-child { margin-top: 31.533px; }
.wsk-meeting-minute-44 > .wsk-meeting:first-child { margin-top: 32.267px; }
.wsk-meeting-minute-45 > .wsk-meeting:first-child { margin-top: 33px; }
.wsk-meeting-minute-46 > .wsk-meeting:first-child { margin-top: 33.733px; }
.wsk-meeting-minute-47 > .wsk-meeting:first-child { margin-top: 34.467px; }
.wsk-meeting-minute-48 > .wsk-meeting:first-child { margin-top: 35.2px; }
.wsk-meeting-minute-49 > .wsk-meeting:first-child { margin-top: 35.933px; }
.wsk-meeting-minute-50 > .wsk-meeting:first-child { margin-top: 36.667px; }
.wsk-meeting-minute-51 > .wsk-meeting:first-child { margin-top: 37.4px; }
.wsk-meeting-minute-52 > .wsk-meeting:first-child { margin-top: 38.133px; }
.wsk-meeting-minute-53 > .wsk-meeting:first-child { margin-top: 38.867px; }
.wsk-meeting-minute-54 > .wsk-meeting:first-child { margin-top: 39.6px; }
.wsk-meeting-minute-55 > .wsk-meeting:first-child { margin-top: 40.333px; }
.wsk-meeting-minute-56 > .wsk-meeting:first-child { margin-top: 41.067px; }
.wsk-meeting-minute-57 > .wsk-meeting:first-child { margin-top: 41.8px; }
.wsk-meeting-minute-58 > .wsk-meeting:first-child { margin-top: 42.533px; }
.wsk-meeting-minute-59 > .wsk-meeting:first-child { margin-top: 43.267px; }
.wsk-schedule-deadlines { border-top: 1px solid var(--wsk-border-light); padding-top: 12px; }
.wsk-schedule-day-heading { margin: 0 0 8px; font-size: 13px; }
#schedule-card .wsk-schedule-tasks { padding-left: 18px; font-size: 12px; }
#schedule-card .wsk-task-help { font-size: 12px; color: var(--wsk-text-secondary); line-height: 1.45; }

.wsk-sched-grid { position: relative; display: flex; gap: 10px; }
.wsk-sched-gutter { position: relative; width: 52px; flex-shrink: 0; height: 440px; }
.wsk-sched-hour-label {
  position: absolute; left: 0; transform: translateY(-50%);
  font-size: 11px; color: var(--wsk-icon); white-space: nowrap;
}
.wsk-sched-body { position: relative; flex: 1; height: 440px; border-left: 1px solid var(--wsk-border-light); }
.wsk-sched-gridline { position: absolute; left: 0; right: 0; border-top: 1px solid #ececec; }
.wsk-sched-event {
  position: absolute; left: 6px; right: 2px; height: 40px; border-left: 3px solid currentColor;
  border-radius: 4px; padding: 3px 8px; box-sizing: border-box; overflow: hidden;
}
.wsk-sched-event-time { margin: 0; font-size: 11px; font-weight: 700; color: inherit; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wsk-sched-event-title {
  margin: 0; font-size: 13px; font-weight: 700; color: var(--wsk-heading); line-height: 1.25;
  overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical;
}

.wsk-sched-t-0800 { top: 0px; }
.wsk-sched-t-0830 { top: 22px; }
.wsk-sched-t-0900 { top: 44px; }
.wsk-sched-t-0930 { top: 66px; }
.wsk-sched-t-1000 { top: 88px; }
.wsk-sched-t-1030 { top: 110px; }
.wsk-sched-t-1100 { top: 132px; }
.wsk-sched-t-1130 { top: 154px; }
.wsk-sched-t-1200 { top: 176px; }
.wsk-sched-t-1230 { top: 198px; }
.wsk-sched-t-1300 { top: 220px; }
.wsk-sched-t-1330 { top: 242px; }
.wsk-sched-t-1400 { top: 264px; }
.wsk-sched-t-1430 { top: 286px; }
.wsk-sched-t-1500 { top: 308px; }
.wsk-sched-t-1530 { top: 330px; }
.wsk-sched-t-1600 { top: 352px; }
.wsk-sched-t-1630 { top: 374px; }
.wsk-sched-t-1700 { top: 396px; }
.wsk-sched-t-1730 { top: 418px; }
.wsk-sched-t-1800 { top: 440px; }

/* --- Quick Check (KAN-111), wsk-* rename (Task 3) -----------------------------
   Not part of the 2026-08-21 mock's own Home (verified live - no third
   sidebar card there today); kept as real, working, already-shipped
   functionality (hub.townflows.confirmations) rather than removed - see
   task-3-report.md's residual-attribution notes. Colors unchanged from the
   rule this replaces (amber #faf1de/#8c5d26/#e4cfa0 - the OLDER reference
   this card was originally fidelity-checked against, not this task's own
   mock capture, which has no equivalent card at all).

   Task 6.5 (Q10, owner queue 2026-08-22): the section is now absent
   entirely (workspace/home.html) when there's nothing to check, rather
   than always rendering with an empty-state line - .wsk-quickcheck-empty's
   last consumer went with it, so that rule is deleted here ("dead CSS is
   removed in the same task that retires its last consumer", plan Global
   Constraints). */
.wsk-card-quickcheck { background: #faf1de; border-color: #8c5d26; display: flex; flex-direction: column; gap: 14px; }
.wsk-quickcheck-title { margin: 0; color: #8c5d26; border-bottom: 1px solid #e4cfa0; padding-bottom: 10px; }
.wsk-quickcheck-q { margin: 0; font-size: 15px; color: #26241d; line-height: 1.45; }

/* Shared buttons and empty state still used by the Admin fragments. */

.ws-btn-primary {
  flex-shrink: 0; min-width: 92px; text-align: center;
  background: var(--brand); color: #fff; border: none; border-radius: 7px;
  padding: 6px 10px; font-size: 13px; font-weight: 700; text-decoration: none;
  display: inline-flex; align-items: center; justify-content: center; gap: 4px;
}
.ws-btn-suggestion {
  flex-shrink: 0; min-width: 112px; text-align: center;
  background: var(--card-bg); color: var(--brand); border: 1px solid var(--brand);
  border-radius: 7px; padding: 6px 10px; font-size: 13px; font-weight: 700;
  font-family: inherit; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 4px;
}
.ws-empty { margin: 0; padding: 16px 20px; font-size: 15px; color: #636363; }

/* --- tag pills (hub.townflows.home.TAG_CSS_CLASS) --------------------------- */

/* wsk-tag (Task 3): the one tag-chip pill shape, wsk-* namespace - Home's
   markup was already 100% wsk-* at Task 3 close; ws-tag (the R1/R2-era
   twin this replaced) stayed alive only because email.html/email_detail.html/
   fragments/email_list.html still consumed it, until Task 4
   (2026-08-21-workspace-reskin.md) migrated those three onto this same
   rule (and the wsk_tag_chip macro, fragments/macros.html) - their last
   use retired, ws-tag is deleted here rather than kept alongside a screen
   that no longer references it. Shares the SAME bare tag-* color modifiers
   below (those never carried a ws-/wsk- prefix of their own -
   hub.townflows.home.TAG_CSS_CLASS names them generically), so there is
   exactly one color decision per label regardless of which screen renders
   it. */
.wsk-tag {
  display: inline-block; font-size: 13px; font-weight: 700; padding: 3px 9px;
  border-radius: 999px; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; max-width: 100%;
}
.tag-project        { background: #e3ece4; color: var(--brand); }
.tag-meeting-minutes{ background: #e3ece4; color: var(--brand); }
.tag-personal        { background: #f1e6d2; color: #7f612b; }
.tag-budget-finance  { background: #f1e6d2; color: #7f612b; }
.tag-vital-records   { background: #e2e8ee; color: #4c5f73; }
.tag-land-records    { background: #e8e2d3; color: #766334; }
.tag-permits-zoning  { background: #eadfe9; color: #6d4f7a; }
.tag-foia-general    { background: #f5e6d3; color: #8c5d26; }
.tag-reference        { background: #eeecdd; color: #6e6b45; }
.tag-spam             { background: #ecebe4; color: #6c685e; }
.tag-default          { background: #eeeeee; color: #666666; }

/* --- auth screens: login/register/forgot-password/join (Task 2,
   2026-08-21-workspace-reskin.md) ------------------------------------------
   Card chrome measured directly off the mock's own sign-in screen 2026-08-21
   (r20, border #cfcfcf, shadow `0 2px 16px rgba(38,36,29,.07)`, 420px card,
   padding 48px vertical / 40px horizontal - TOKENS' shorthand "padding 40"
   turned out to mean that once measured against the live computed style,
   not a uniform 40 on every side). These four pages don't use the workspace
   shell (Task 1's wsk-shell/wsk-tabs) - "consumes the token layer only" per
   this task's own plan entry - so this section owns a small card-specific
   set (wsk-auth-*) and reuses .wsk-input/.wsk-btn-primary (Task 1) for the
   actual form controls rather than re-inventing them. Replaces the old
   task-login-report.md (2026-08-13) .login-* family below this comment
   used to be - deleted, not kept alongside ("no old elements survive",
   plan Global Constraints); .ws-form-error is the one rule that family
   shared with a page OUTSIDE this task's scope
   (workspace/fragments/admin_users.html) and stays separate, unrenamed.

   Deliberately NOT literally what the mock renders, twice over: (1) the
   mock's card has no password form at all - a scripted demo with two fake
   SSO buttons and nothing else - while this hub always needs a real one
   for towns without SSO (KAN-11's show_password_form gate,
   hub/routes/pages.py, untouched by this task); (2) the mock hardcodes its
   title to its one demo town ("Ellington") - hub/templates/login.html's
   /login is shared by every municipality on the box, so its title stays
   the honest, town-agnostic "Sign in" (tests/test_pages.py::
   test_login_wordmark_is_company_brand_not_a_town_name pins "Ellington"
   never appearing there). The kicker ("Slayer Digital", company brand) and
   the card chrome itself ARE ported verbatim - both are honestly
   town-agnostic in the mock too. join.html is the one screen that DOES
   know its one specific town (the invite token already fixed it) and
   names it in the title, same wsk-auth-title class. */

.wsk-auth-page {
  background: var(--wsk-bg);
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px 20px;
  box-sizing: border-box;
}

.wsk-auth-shell { width: 100%; max-width: 420px; }

.wsk-auth-card {
  background: var(--wsk-card);
  border: 1px solid var(--wsk-border);
  border-radius: 20px;
  box-shadow: 0 2px 16px rgba(38, 36, 29, .07);
  padding: 48px 40px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  box-sizing: border-box;
}

/* letter-spacing .1em RECONFIRMED (Task 5 rider, 2026-08-21-workspace-
   reskin.md): TOKENS' Design Token Sheet documents kickers generically as
   .05em, and Task 2's own report deferred this exact value as an
   undisclosed discrepancy to reconcile here. Live re-measurement against
   the mock's own sign-in card kicker ("Slayer Digital", a fresh/cleared-
   localStorage session, 2026-08-21) reads letter-spacing: 0.1em directly
   off the rendered element's own inline style - Task 2's shipped value was
   already correct; TOKENS' generic .05em figure was the stale one for THIS
   specific kicker (same pattern as the Search zero-state El-bot kicker
   nearby, independently measured at .04em, not .05em either - TOKENS' one
   "kickers" figure doesn't hold uniformly across every kicker-shaped
   element the mock actually renders). No code change; this comment is the
   documented reconciliation the task's own instructions asked for. */
.wsk-auth-kicker {
  margin: 0;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--wsk-meta);
}

.wsk-auth-title {
  margin: 0;
  font-size: 24px;
  font-weight: 700;
  color: var(--accent);
  letter-spacing: .3px;
}
/* Keep the existing 8px gap only when a kicker is shown directly above. */
.wsk-auth-kicker + .wsk-auth-title { margin-top: 8px; }

/* Body copy under the header: login's subtitle, forgot-password's longer
   notice, the SSO "sign in with your work account" line. First occurrence
   in the card carries the mock's own measured subtitle rhythm (16px above,
   28px below); a second copy block later in the same card (there's at most
   one today) doesn't need that large a leading gap. */
.wsk-auth-copy {
  margin: 16px 0 28px;
  font-size: 16px;
  line-height: 1.5;
  color: var(--wsk-text-secondary);
  text-align: center;
}
.wsk-auth-copy ~ .wsk-auth-copy { margin: 0 0 20px; }

/* Long body blocks on the join and Microsoft setup cards. */
.wsk-auth-copy-long { text-align: left; }

/* KAN-218. Both classes are worn ALONGSIDE .wsk-auth-copy, and both are a
   second copy block in their card, so the `~` rule above owns their margins -
   these two rules deliberately set none, since a margin declared here would
   lose to it on specificity and only look like it did something.

   .wsk-auth-intro: the municipality step's introduction line, which carries the
   Microsoft mark beside its text - flex so the 18px mark sits on the text's
   centre line instead of its baseline.
   .wsk-auth-brand: the line naming which municipality a town view is for. Card
   ink rather than the secondary grey, because that line IS the branding rather
   than supporting copy (it wins over .wsk-auth-copy's own color on order, at
   equal specificity). */
.wsk-auth-intro {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}
.wsk-auth-brand { color: var(--wsk-text); }
/* KAN-236 (owner ruling 2026-09-12): on the town's own tenant host the brand
   line leads with the municipality's name and its login domain drops to this
   second, muted line, so the clerk still sees which portal this is. */
.wsk-auth-brand-domain {
  display: block; margin-top: 4px;
  font-size: 14px; font-weight: 400; color: var(--wsk-text-secondary);
}

.wsk-auth-error {
  width: 100%;
  box-sizing: border-box;
  margin: 0 0 20px;
  padding: 10px 14px;
  border-radius: 8px;
  font-size: 14px;
  color: #a33e3e;
  background: #f7e9e9;
  border: 1px solid #e3b9ba;
}
/* .ws-form-error: still used directly by
   workspace/fragments/admin_users.html (out of this task's scope) - same
   visual treatment, kept as its own selector rather than folded in. */
.ws-form-error {
  margin: 0;
  padding: 10px 14px;
  border-radius: 8px;
  font-size: 14px;
  color: var(--accent);
  background: #f7e9e9;
  border: 1px solid #e3b9ba;
}

/* The SSO entry point: an <a> when the town is already known (a GET to
   /auth/sso/start), and since 2026-09-02 also the submit <button> of the
   town-less page's "work email or municipality domain" form (a POST to the
   same path) - one recipe for both, which is why it pins font-family and
   cursor a plain link would not need. Styled to match the mock's own
   "Continue with X" buttons exactly (measured 2026-08-21): neutral
   white/bordered, not accent-filled - on this card it's often the ONLY
   affordance, so it doesn't need a fill to read as primary. */
.wsk-auth-sso-btn {
  width: 100%;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  background: var(--wsk-card);
  border: 1.5px solid var(--wsk-border);
  border-radius: 10px;
  padding: 14px 20px;
  font-size: 18px;
  font-weight: 700;
  font-family: inherit;
  color: var(--wsk-text);
  text-decoration: none;
  cursor: pointer;
  transition: border-color .15s ease, transform .15s ease;
}
.wsk-auth-sso-btn:hover { border-color: var(--accent); }
.wsk-auth-sso-btn:active { transform: scale(.96); }
.wsk-auth-sso-btn svg { flex-shrink: 0; }

/* Town Microsoft sign-in leads the secondary password form. The shared
   front door keeps the unmodified neutral SSO button above. */
.wsk-auth-sso-btn.wsk-btn-primary {
  background: var(--accent); color: #fff; border-color: var(--accent);
}
.wsk-auth-sso-btn.wsk-btn-primary:hover {
  background: var(--accent-hover); border-color: var(--accent-hover);
}

/* Only ever rendered when the SSO link AND the password form are BOTH on
   screen at once - since the owner ruling of 2026-09-27 that is EVERY view
   of an SSO-configured town with a provider button (under
   HUB_MFA_POLICY=require only its unlinked break-glass view - see
   login.html's own comment); no counterpart in the mock (it
   never shows a password form to divide anything from), so this is
   original, token-only styling. */
.wsk-auth-divider {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  margin: 20px 0;
  color: var(--wsk-meta);
  font-size: 13px;
}
.wsk-auth-divider::before, .wsk-auth-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--wsk-border);
}

.wsk-auth-form { width: 100%; display: flex; flex-direction: column; gap: 20px; }
.wsk-auth-field { display: flex; flex-direction: column; gap: 6px; text-align: left; }
/* Two fields that belong on one line of a mailing address (register.html's
   city + ZIP, owner directive 2026-09-27) - the shape every public address
   form uses. minmax(0, ...) on both tracks so a long city name shrinks
   instead of pushing the ZIP out of the card, and one column below the
   card's own narrow width. */
.wsk-auth-row {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: 16px;
  min-width: 0;
}
@media (max-width: 420px) {
  .wsk-auth-row { grid-template-columns: minmax(0, 1fr); }
}
.wsk-auth-group {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}
.wsk-auth-group-title {
  width: 100%;
  margin: 0 0 2px;
  padding: 0 0 8px;
  border-bottom: 1px solid var(--wsk-border);
  color: var(--wsk-text);
  font-size: 16px;
  font-weight: 700;
}
.wsk-auth-required {
  color: var(--wsk-meta);
  font-size: 11px;
  font-weight: 600;
  text-transform: none;
}
.wsk-auth-form .wsk-input { min-height: 44px; }
.wsk-auth-prefixed-input {
  display: flex;
  align-items: stretch;
  width: 100%;
}
.wsk-auth-input-prefix {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  padding: 10px 12px;
  border: 1.5px solid var(--wsk-border);
  border-right: 0;
  border-radius: 8px 0 0 8px;
  background: var(--wsk-cream);
  color: var(--wsk-text-secondary);
  font-size: 15px;
  font-weight: 700;
  min-height: 44px;
}
.wsk-auth-prefixed-input .wsk-input { border-radius: 0 8px 8px 0; }
.wsk-domain-preview {
  min-width: 0;
  padding: 14px;
  border: 1px solid var(--wsk-border);
  border-radius: 8px;
  background: var(--wsk-cream);
}
.wsk-domain-preview-title {
  margin: 0 0 6px;
  color: var(--wsk-text);
  font-size: 14px;
  font-weight: 700;
}
.wsk-domain-preview-list { margin: 12px 0 0; }
.wsk-domain-preview-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 2px;
  padding: 8px 0;
  border-top: 1px solid var(--wsk-border);
}
.wsk-domain-preview-row dt { color: var(--wsk-text); font-size: 13px; font-weight: 700; }
.wsk-domain-preview-row dd {
  min-width: 0;
  margin: 0;
  color: var(--wsk-text-secondary);
  font-size: 13px;
  overflow-wrap: anywhere;
}

.wsk-auth-label {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--wsk-text-secondary);
}

/* Pairs with .wsk-btn-primary (Task 1) - the width/size modifier this card
   needs beyond that shared recipe's own (inline-flex, unsized) default. */
.wsk-auth-submit { width: 100%; font-size: 16px; padding: 13px 16px; }

/* register.html's "already registered? Sign in" footnote (Task B2) and
   friends (Forgot password?, Sign in to a different municipality, New
   municipality?) - every secondary link below the card's primary
   action(s). */
.wsk-auth-footnote { margin: 16px 0 0; text-align: center; font-size: 14px; color: var(--wsk-text-secondary); }
.wsk-auth-footnote-link { color: var(--accent); font-weight: 700; text-decoration: none; }
.wsk-auth-footnote-link:hover { text-decoration: underline; }
.wsk-auth-register { display: block; text-align: center; text-decoration: none; margin-top: 16px; }

/* register.html's one per-field help line (2026-09-03 onboarding, F2): the
   sentence under the clerk-mailbox field saying what the address is for.
   Secondary colour like the label, but sentence case - it is prose, not a
   caption - and no top margin beyond the field's own 6px gap. */
.wsk-auth-help { margin: 0; font-size: 13px; line-height: 1.4; color: var(--wsk-text-secondary); }

.mfa-digit-grid {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 8px;
  width: 100%;
  min-width: 0;
}
.mfa-digit-grid[hidden] { display: none; }
.mfa-digit-input {
  box-sizing: border-box;
  min-width: 0;
  min-height: 52px;
  padding: 4px;
  text-align: center;
  font-size: 24px;
  font-variant-numeric: tabular-nums;
}
.mfa-recovery-toggle {
  align-self: flex-start;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--accent);
  font: inherit;
  font-size: 14px;
  font-weight: 700;
  text-decoration: underline;
  cursor: pointer;
}
.mfa-recovery-toggle[hidden] { display: none; }

/* --- Billing landing page: billing.html (2026-09-03 onboarding, F2) ---------
   Where registration lands. Same wsk-auth-* card as register/login, plus
   a small family for what the card holds: a titled section of label/value
   rows (the town summary registration just collected) and the honest
   no-provider line (spec §5: absence renders "not configured", never a
   silent fallback). Left-aligned inside a centred card on purpose - rows
   of facts read as a table, not as a headline. F4 adds the price rows and
   the checkout button; F3/F5 the survey and progress sections. */
.wsk-billing-section { width: 100%; text-align: left; margin: 20px 0 0; }
.wsk-billing-heading {
  margin: 0 0 8px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--wsk-text-secondary);
}
.wsk-billing-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  padding: 10px 0;
  border-top: 1px solid var(--wsk-border);
}
.wsk-billing-row:last-child { border-bottom: 1px solid var(--wsk-border); }
.wsk-billing-label { font-size: 14px; font-weight: 700; color: var(--wsk-text); flex-shrink: 0; }
.wsk-billing-value { font-size: 14px; color: var(--wsk-text-secondary); text-align: right; overflow-wrap: anywhere; }
.wsk-billing-domain-row { flex-direction: column; align-items: stretch; gap: 4px; }
.wsk-billing-domain-row .wsk-billing-value {
  width: 100%;
  text-align: left;
  overflow-wrap: anywhere;
  word-break: normal;
}
.wsk-billing-unconfigured { margin: 0; font-size: 14px; line-height: 1.5; color: var(--wsk-text-secondary); }

/* F4 (payment step). The total row reads as the sum line of a receipt:
   both cells in the primary text colour and bold, a heavier top rule than
   the item rows above it. The note is the one-line help under the price
   block ("Card entry happens on Stripe's page..." / "Demo checkout: no
   card will be requested."), sitting between the rows and the button. */
.wsk-billing-total { border-top: 2px solid var(--wsk-text); }
.wsk-billing-total .wsk-billing-label,
.wsk-billing-total .wsk-billing-value { color: var(--wsk-text); font-weight: 700; font-size: 15px; }
.wsk-billing-note { margin: 12px 0 0; font-size: 13px; line-height: 1.5; color: var(--wsk-text-secondary); }
.wsk-billing-note + .wsk-auth-submit { margin-top: 12px; }
.wsk-billing-invoice { margin: 20px 0 0; padding: 16px 0 0; border-top: 1px solid var(--wsk-border-light); }
.wsk-billing-subheading { margin: 0; font-size: 15px; font-weight: 700; color: var(--wsk-heading); }
.wsk-billing-invoice-form { display: flex; flex-direction: column; gap: 6px; margin: 12px 0 0; }
.wsk-billing-field-label { font-size: 13px; font-weight: 700; color: var(--wsk-text-secondary); margin: 6px 0 0; }
.wsk-billing-testnote { margin: 10px 0 0; font-size: 12px; line-height: 1.4; color: var(--wsk-text-secondary); font-style: italic; }

/* The DEMO provider (hub.payments.DemoProvider, HUB_PAYMENTS=demo). The
   banner sits at the very top of the card on EVERY page that provider is
   behind - the checkout page it renders, the billing page offering it and
   the success view after it - loud enough that no screenshot of this
   origin can pass for a real charge. The amount is the demo checkout's
   headline number, set large the way a hosted checkout would. */
.wsk-demo-banner {
  width: 100%;
  margin: 0 0 16px;
  padding: 10px 12px;
  border: 2px dashed var(--wsk-text);
  border-radius: 6px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .02em;
  text-transform: uppercase;
  text-align: center;
  color: var(--wsk-text);
  background: var(--wsk-cream);
}
.wsk-demo-amount { margin: 8px 0 4px; font-size: 34px; font-weight: 800; letter-spacing: -.02em; color: var(--wsk-text); }
.wsk-demo-amount-period { font-size: 14px; font-weight: 500; letter-spacing: 0; color: var(--wsk-text-secondary); }
/* Website-survey line (2026-09-03 onboarding, F3): one sentence saying what
   the survey service is doing, or that there is none configured. Same
   secondary-prose look as the no-provider line above; the two modifiers only
   colour it, so the copy stays the message and the colour is reinforcement
   (never the sole signal). The text can wrap a URL the clerk typed, hence
   overwrap. */
.wsk-survey-status { margin: 0; font-size: 14px; line-height: 1.5; color: var(--wsk-text-secondary); overflow-wrap: anywhere; }
.wsk-survey-running { color: var(--wsk-text); }
.wsk-survey-failed { color: var(--accent); }

/* --- Town admin page (Task ADMIN, 2026-08-13) -------------------------------
   No demo reference (control-plane only, new-stack-native screen - see
   hub/routes/admin.py's module docstring) - the micro-label look below
   (13px/700/.03em letter-spacing/var(--muted) == the brief's "13px/700/
   0.39px #495156") independently redefines the same values .wsk-thead
   (Task 1's shared component recipe, further above) already carries,
   rather than composing it - a pre-existing duplication this rider
   doesn't resolve, just documents accurately. (Corrected, Task 4: this
   comment used to cite ".ws-thead/.ws-row" as My Tasks' still-live
   5-column table rule - stale since Task 3 replaced Home's My Tasks
   table with per-task wsk-* cards and deleted the old table row/header classes.
   .ws-td-* entirely; neither name exists in this file anymore. Email's
   own table header cells carry the same look too as of this task, see
   .wsk-eth below in the Email screen section.) */

/* Admin page (KAN-310, redesigned 2026-10-11): every rule for it lives in the
   "ADMIN PAGE UX" block at the end of this file. */

.ws-input-sm {
  font-size: 14px; font-family: inherit; padding: 6px 10px; height: 34px;
  border-radius: 8px; border: 1.5px solid var(--line); background: var(--card-bg);
  color: var(--ink);
}


/* --- Email screen (Task R2, KAN-87; reskinned onto the wsk-* namespace by
   Task 4, 2026-08-21-workspace-reskin.md) - values pixel-truth extracted
   from live DOM measurement against the mock (2026-08-21), same sourcing
   discipline as the Home rules above. Corrections this task's live walk
   made over the R2-era values it replaces: the page container measures
   1400px max-width (Email joins Home in that one Home-specific override
   over TOKENS' generic 1280px Layout figure - see that section's own
   comment); filter-pills-and-sort share ONE row via justify-content:
   space-between (measured) rather than R2's own KAN-111 finding of two
   stacked left-aligned rows - accurate for an older capture, not the
   current, owner-patched mock; the inbox grid's column template gained
   minmax(360px, 1fr) + a 1000px min-width (measured, was a bare 1fr /
   760px); Prev/Next measured NEUTRAL-toned (border/text match the page's
   ordinary ink/border tokens, no accent anywhere), while Refresh
   (email.html, reuses .wsk-btn-outline) measured genuinely accent-toned -
   two different mock button treatments R2's one shared .ws-btn-ghost never
   distinguished; the detail view's priority control gained a "Priority:"
   text label (measured) with no R2 equivalent. -------------------------- */

/* line-height: normal on the CONTAINER, not each child rule - live
   measurement (2026-08-21) found the mock's buttons/pills/headings render
   with the browser's normal line-height, while body's own line-height:1.4
   (Task 1's global reset) inherits into every wsk-* element here that
   doesn't override it, making pills ~2.6px taller than the mock's (34.6px
   vs 32px, computed style diff confirmed: "700 15px/21px ..." here vs
   "700 15px ..." there) - a per-element height difference that compounds
   down the whole page (toolbar 81.2px vs 76px, table start 8.8px lower)
   and manifests as a large false-positive fidelity diff band (measured:
   ~23% for just the header+row1 strip, both sides showing the SAME text,
   merely offset a few px vertically - a known pixel-diff pitfall, not a
   real visual defect). R2 already found and fixed this same root cause
   for .wsk-erow-subject/.wsk-erow-snippet specifically (their own
   "line-height: normal, not the body's inherited 1.4" comment, further
   below); this container-level rule extends the same fix to every OTHER
   wsk-* element Task 4 introduced (pills, toolbar labels, headings,
   buttons) without touching body's own global rule - out of this task's
   scope, and would affect every other screen too. */
.wsk-email-main, .wsk-detail-main {
  max-width: 1400px; margin: 0 auto; padding: 36px 40px 40px; line-height: normal;
}
.wsk-detail-main { max-width: 1000px; display: flex; flex-direction: column; gap: 20px; }

.wsk-email-head {
  display: flex; justify-content: space-between; align-items: center;
  margin: 0 0 18px;
}
.wsk-inbox-heading { margin: 0; font-size: 26px; font-weight: 700; color: var(--wsk-heading); }
.wsk-email-head-actions { display: flex; align-items: center; gap: 10px; }
.wsk-inbox-search { flex: 1 1 260px; min-width: 0; max-width: 460px; margin-inline-start: 16px; }
.wsk-inbox-search input {
  box-sizing: border-box; width: 100%; min-height: 44px; padding: 0 16px; font: inherit; color: var(--wsk-text);
  background: var(--wsk-card); border: 1px solid var(--wsk-border); border-radius: 999px;
}
.wsk-refreshed-label { font-size: 13px; color: #777777; white-space: nowrap; }

/* KAN-111 (carried forward): the mock does NOT wrap the email toolbar+table
   in a card - filters sit directly on the page background, and the table
   carries its own 12px-radius border (further below). #email-list-card
   (the <section> itself, unclassed) is the HTMX swap target. */
.wsk-email-toolbar {
  display: flex; justify-content: space-between; align-items: center;
  flex-wrap: wrap; gap: 12px; margin: 0 0 18px;
}
.wsk-pill-row { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.wsk-toolbar-label { font-size: 15px; color: var(--wsk-text-secondary); }

.wsk-pill {
  background: var(--wsk-card); color: var(--wsk-text); border: 1px solid var(--wsk-border);
  border-radius: 999px; padding: 6px 14px; font-size: 15px; font-weight: 700;
  font-family: inherit; text-decoration: none; cursor: pointer;
}
.wsk-pill-active { background: var(--accent); color: #fff; border-color: var(--accent); }
/* KAN-501: the number of messages a Filter by pill shows - the same text colour as its
   label, a lighter weight so the name still reads first. */
.wsk-pill-count { font-weight: 400; margin-inline-start: 2px; }
/* A phone or any touch screen: every Filter by pill is a 44px target (the row wraps, so more pills
   only means more lines). A mouse keeps the 32px pill the page has always had. */
@media (max-width: 560px), (pointer: coarse) {
  .wsk-email-toolbar .wsk-pill { display: inline-flex; align-items: center; gap: 4px; min-height: 44px; box-sizing: border-box; }
}
/* KAN-571: the inbox row's More menu. Long section names wrap, and each group has a quiet heading. */
.wsk-filter-more .wsk-viewer-menu a { white-space: normal; overflow-wrap: anywhere; }
.wsk-filter-more-head {
  padding: 8px var(--wsk-space-3) 4px; color: var(--wsk-text-secondary); font-size: 13px; font-weight: 700;
}
/* KAN-576: More opens a list, it is not one more filter. No pill outline or fill, the town's accent
   colour, and an arrow that turns when the list is open. */
.wsk-filter-more > summary.wsk-pill {
  display: inline-flex; align-items: center; gap: 6px;
  background: transparent; border-color: transparent; color: var(--accent);
}
.wsk-filter-more > summary.wsk-pill::after {
  content: ""; width: 7px; height: 7px; margin-block-start: -3px;
  border-inline-end: 2px solid currentColor; border-block-end: 2px solid currentColor;
  transform: rotate(45deg);
}
.wsk-filter-more[open] > summary.wsk-pill::after { margin-block-start: 3px; transform: rotate(-135deg); }
.wsk-filter-more > summary.wsk-pill:hover,
.wsk-filter-more[open] > summary.wsk-pill { background: var(--accent-light); color: var(--accent-hover); }
.wsk-filter-more > summary.wsk-pill:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.wsk-table-wrap { border: 1px solid var(--wsk-border); border-radius: 12px; overflow: hidden; }
.wsk-table-scroll { overflow-x: auto; padding-bottom: 6px; }
/* KAN-112 (carried forward, corrected): the inbox table is a GRID matching
   the mock's own template - minmax(360px, 1fr) + a 1000px min-width
   (live-measured 2026-08-21; R2's own capture had a bare 1fr / 760px). */
.wsk-ethead, .wsk-erow {
  display: grid; grid-template-columns: 160px minmax(360px, 1fr) 230px 100px 165px;
  min-width: 1015px;
}
.wsk-ethead { background: var(--wsk-cream); border-bottom: 1px solid var(--wsk-border); }
/* 13px vertical, not the mock's own 10px: the mock wraps each header label
   in a <button> that carries its own padding and renders 24px tall, where
   the hub's bare <a> is 18px. Matching the padding number would leave the
   band 5px short; matching the RENDERED 45px band is what actually lines
   the two tables up (R2 finding, still true). */
.wsk-eth {
  background: none; border: none; text-decoration: none; cursor: pointer;
  font: inherit; font-size: 13px; font-weight: 700; color: var(--wsk-text-secondary);
  text-transform: uppercase; letter-spacing: .03em; padding: 13px 16px;
}
.wsk-eth-priority { text-align: center; }
/* KAN-487: the cell is the grid item (it carries aria-sort); the link fills it so the
   whole heading is one 44px-tall target. Same padding, type and dividers as before;
   only the arrows are new. */
.wsk-eth-cell { display: flex; align-items: stretch; min-width: 0; position: relative; }
.wsk-eth { display: flex; align-items: center; gap: 6px; flex: 1; min-height: 44px; box-sizing: border-box; white-space: nowrap; }
.wsk-eth-priority { justify-content: center; }
.wsk-eth:hover .wsk-eth-label { text-decoration: underline; }
.wsk-eth:focus-visible { outline: 3px solid var(--accent); outline-offset: -3px; }
.wsk-sort-off { display: inline-flex; flex-direction: column; font-size: 8px; line-height: 8px; opacity: .35; }
.wsk-sort-on { font-size: 14px; line-height: 1; font-weight: 900; color: var(--accent); }
/* Vertical cell dividers (measured 2026-08-21, #e2dfd6) - :not(:last-child)
   on the grid's own direct children rather than a fifth per-cell class, so
   the date column (always last) never gets a trailing divider on either
   row type. */
.wsk-ethead > *:not(:last-child), .wsk-erow > *:not(:last-child) {
  border-right: 1px solid #e2dfd6;
}

.wsk-erow { border-bottom: 1px solid var(--wsk-border-row); text-decoration: none; color: inherit; }
/* Zebra striping - the mock alternates #fff / #fafafa (R2 finding, still
   true). nth-of-type, not nth-child: the rows share a parent with the
   header, so nth-child would invert the stripe. Hover must out-specify
   both stripes, hence it follows them. */
.wsk-erow:nth-of-type(odd)  { background: #fff; }
.wsk-erow:nth-of-type(even) { background: #fafafa; }
.wsk-erow:hover { background: #f2ede1; }
.wsk-erow-dot { display: inline-block; width: 12px; color: var(--accent); font-size: 11px; }
.wsk-erow-from {
  padding: 13px 16px; font-size: 15px; color: var(--wsk-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.wsk-erow-subject-wrap { padding: 11px 16px; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
/* line-height: normal, not the body's inherited 1.4 (R2 finding, still
   true - see git history for the exact px math this fixes). */
.wsk-erow-subject {
  font-size: 15px; line-height: normal; color: var(--wsk-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.wsk-erow-snippet {
  font-size: 15px; line-height: normal; color: var(--wsk-meta);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.wsk-erow-bold { font-weight: 700; }
.wsk-erow-tag { padding: 11px 16px; display: flex; align-items: center; }
/* Same chip, one notch quieter than the single one a Home task card shows:
   down a column of fourteen rows the 700 weight stacked into a wall of
   colour (demo-fidelity gate, 2026-09-14). The label and its category colour
   both stay - they are what the engine's tagging is FOR, and the filter
   pills above the table are read against them. */
.wsk-erow-tag .wsk-tag { font-weight: 600; }
.wsk-erow-priority { padding: 11px 12px; text-align: center; }
.wsk-erow-date { padding: 13px 16px; font-size: 15px; color: var(--wsk-meta); }

/* Pairs with the bare priority-high/-medium/-low/-none modifiers directly
   below (hub.townflows.email_list.PRIORITY_META names them; unchanged
   values from the R2 .ws-priority-* rules this replaces, just renamed -
   these never carried a ws-/wsk- prefix of their own, same convention as
   the tag-* modifiers above). */
/* DEMO-FIDELITY GATE, 2026-09-14: with the engine now assigning an urgency
   to every ingested email, a filled pill on every row of a full inbox reads
   as alarm, and the reference build's calm - one accent, used where it means
   something - was gone. A fill is now reserved for High: the row the clerk
   should look at first. Normal, Low and none keep their WORDS (nothing is
   hidden, and the column still reads correctly to a screen reader or a
   colour-blind clerk - tests/test_email_list_calm.py pins that) in the same
   muted voice the "-" already used, so colour on this screen means "this one
   is different" instead of "every one is coloured". */
.wsk-priority { display: inline-block; font-size: 13px; font-weight: 700; padding: 3px 9px; border-radius: 999px; }
.priority-high   { background: #f4dede; color: #a33e3e; }
.priority-medium { background: transparent; color: #8a8371; font-weight: 600; }
.priority-low    { background: transparent; color: #a39d8c; font-weight: 600; }
.priority-none   { background: transparent; color: #a39d8c; }

.wsk-pagination {
  display: flex; justify-content: space-between; align-items: center;
  flex-wrap: wrap; gap: 12px; padding: 14px 20px 20px;
}
.wsk-pagination-nav { display: flex; align-items: center; gap: 14px; }
/* Neutral-toned (measured 2026-08-21): border/text match the ordinary page
   ink/border tokens, no accent - unlike Refresh (email.html's own
   .wsk-btn-outline), which measured genuinely accent-toned. R2's one
   shared .ws-btn-ghost used accent for both; this corrects Prev/Next only. */
.wsk-btn-ghost {
  background: var(--wsk-card); color: var(--wsk-text); border: 1px solid var(--wsk-border);
  border-radius: 7px; padding: 6px 14px; font-size: 15px; font-weight: 700;
  font-family: inherit; text-decoration: none; cursor: pointer; display: inline-block;
}
.wsk-btn-disabled { color: var(--wsk-disabled); pointer-events: none; cursor: default; }

/* "Show N messages filtered as spam" (Task 4, new) - a plain text-button
   below pagination, live-measured 2026-08-21: no background/border of its
   own, just a top divider on its wrapping block. */
.wsk-spam-banner { margin-top: 18px; border-top: 1px solid var(--wsk-border); padding-top: 14px; }
.wsk-spam-banner-link {
  background: none; border: none; color: var(--wsk-text-secondary); font-size: 15px;
  font-family: inherit; padding: 4px 0; cursor: pointer; text-decoration: none; display: inline-block;
}
.wsk-spam-banner-link:hover { text-decoration: underline; }

/* --- Email detail / review screen (Task 4 rename; values unchanged from
   R2 except the added "Priority:" label and the two button treatments
   below - see this section's own header comment for what live measurement
   corrected). -------------------------------------------------------- */

/* padding: 4px 0 (measured 2026-08-21) - without it this renders 8px
   shorter than the mock's own back-link, an offset that cascades down
   through the whole detail card below (confirmed via getBoundingClientRect:
   card top 181.6px here vs 189.2px there before this fix). */
.wsk-back-link {
  align-self: flex-start; font-size: 16px; color: var(--wsk-text-secondary);
  text-decoration: none; padding: 4px 0;
}
/* padding overrides .wsk-card's uniform 20px - measured 2026-08-21: this
   card is 20px vertical / 24px horizontal, not uniform; left at 20px
   uniform it shifts every text line 4px, visible as a "ghosting" diff
   against the mock at the pixel level even though nothing else about the
   card's position was wrong. border-radius likewise measured 12px, not
   .wsk-card's 14px. */
.wsk-detail-card { display: flex; flex-direction: column; gap: 0; padding: 20px 24px; border-radius: 12px; }
.wsk-detail-head {
  display: flex; justify-content: space-between; align-items: baseline;
  flex-wrap: wrap; gap: 6px; margin-bottom: 8px;
}
.wsk-detail-sender { font-size: 15px; font-weight: 700; color: var(--wsk-heading); }
.wsk-detail-date { font-size: 13px; color: var(--wsk-meta); }
.wsk-detail-subject { margin: 0 0 10px; font-size: 17px; font-weight: 700; }
.wsk-detail-meta { display: flex; justify-content: flex-end; align-items: center; gap: var(--wsk-space-2); margin: 0 0 var(--wsk-space-3); }
/* "Priority:" reads as a pair with its value: the value's own 9px of padding is the gap between them. */
.wsk-detail-priority-label { margin-right: calc(var(--wsk-space-2) * -1); font-size: 14px; color: var(--wsk-text-secondary); }
.wsk-detail-body { margin: 0; font-size: 16px; white-space: pre-line; line-height: 1.5; }
/* ===== Email actions row (owner order 2026-10-11: "make this look better") ==========
   Under the message there is ONE row. It used to be two rows in three button styles: two
   white bordered buttons, a small "More actions" word, and on a line of its own a big solid
   purple "Delegate task". Now the one primary action comes first as a compact solid button,
   Archive and Mark as unread follow as quiet borderless buttons (a small icon and their
   words, a soft tint on hover, an outline ring on focus), and "More actions" is a three-dot
   icon button at the far end of the same row, opening the menu it always opened.

   One height for every control (--wsk-email-act-h) and every gap is the 8px step of the
   --wsk-space-* scale. Narrow first: a phone gets 44px targets, Delegate task takes a line
   of its own and the other actions share the line under it; from 561px the row is one line
   and 36px tall (a touch screen keeps 44px at any width, the last rule below).

   Delegate task is a <details> inside #email-task (fragments/email_task.html, the htmx swap
   target), so #email-task is display: contents here: its button joins this row, and its form
   opens full width under the button with the other actions dropping below it while it is open.
   Namespaced wsk-email-*; no :nth-child. */
.wsk-email-actions {
  --wsk-email-act-h: 44px;
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--wsk-space-2);
  margin-top: var(--wsk-space-4); padding-top: var(--wsk-space-3);
  border-top: 1px solid var(--wsk-border-row);
}
.wsk-email-actions .wsk-email-task { display: contents; }
.wsk-email-actions .wsk-email-task > p { flex: 1 1 100%; margin: 0; }
.wsk-email-act {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--wsk-space-2);
  box-sizing: border-box; min-height: var(--wsk-email-act-h); padding: 0 var(--wsk-space-3);
  border: 0; border-radius: 8px; background: transparent; color: var(--wsk-text-secondary);
  font-family: inherit; font-size: 14px; font-weight: 700; line-height: 1; white-space: nowrap;
  cursor: pointer; touch-action: manipulation;
  transition: background-color .15s ease, color .15s ease;
}
.wsk-email-act:hover { background: var(--accent-light); color: var(--accent-hover); }
.wsk-email-act:focus-visible { background: var(--accent-light); color: var(--accent-hover); outline: 3px solid #0b57d0; outline-offset: 1px; }
.wsk-email-act svg { flex: 0 0 auto; }
@media (prefers-reduced-motion: reduce) { .wsk-email-act { transition: none; } }
/* Delegate task: the compact solid primary, first in the row. The selectors carry details.
   so they outrank the shared ".wsk-engine-add[open] > summary" rule (Home's disclosure). */
.wsk-email-actions details.wsk-engine-add { flex: 0 0 auto; }
.wsk-email-actions details.wsk-engine-add[open] { flex: 1 1 100%; margin-bottom: var(--wsk-space-2); }
.wsk-email-actions details.wsk-engine-add > summary { width: fit-content; margin-left: 0; }
.wsk-email-actions details.wsk-engine-add > summary.wsk-btn-primary {
  box-sizing: border-box; min-height: var(--wsk-email-act-h); padding: 0 var(--wsk-space-4);
  border-radius: 8px; font-size: 14px; line-height: 1;
}
/* The same control when it only says who holds the task ("Assigned to ..."): a quiet disclosure, not a
   button. Its chevron comes from the shared disclosure list near the end of this file; here it gets the
   row's own height, so it is a 44px target on a phone and on any touch screen like its neighbours. */
.wsk-email-actions details.wsk-engine-add > summary.wsk-task-help {
  box-sizing: border-box; min-height: var(--wsk-email-act-h); padding: 0 var(--wsk-space-2);
  font-size: 14px; font-weight: 700; line-height: 1.2; justify-content: flex-start;
}
/* More actions: a three-dot icon button, square, at the far end; the menu hangs under it. */
.wsk-mail-more { position: relative; margin-left: auto; }
.wsk-email-actions .wsk-mail-more > summary::before { display: none; }
.wsk-email-more { width: var(--wsk-email-act-h); padding: 0; }
.wsk-email-actions .wsk-mail-more[open] > summary { background: var(--accent-light); color: var(--accent-hover); }
.wsk-mail-more:not([open]) > .wsk-mail-more-items { display: none; }
.wsk-mail-more-items {
  position: absolute; right: 0; top: calc(100% + var(--wsk-space-1)); z-index: 5;
  width: 180px; max-width: calc(100vw - 48px);
  display: flex; flex-direction: column; gap: 6px; padding: 8px;
  background: var(--wsk-card); border: 1px solid var(--wsk-border);
  border-radius: 10px; box-shadow: 0 4px 16px rgba(38, 36, 29, .12);
}
.wsk-mail-more-items > button {
  display: block; width: 100%; min-height: 44px; padding: 10px 12px;
  text-align: left; font-size: 14px; border: 0; background: transparent;
}
.wsk-mail-more-items > button:hover { background: var(--wsk-bg); }
@media (max-width: 560px) {
  .wsk-email-actions details.wsk-engine-add { flex: 1 1 100%; }
  .wsk-email-actions details.wsk-engine-add > summary { width: 100%; justify-content: center; }
  .wsk-email-actions > .wsk-email-act { padding: 0 6px; }
  /* The first quiet button starts the line under Delegate task: its icon sits on the text edge. */
  .wsk-email-act-lead { margin-left: -6px; }
}
/* The narrowest phones (320px): the words stay, the small icons give way, so Archive, Mark as
   unread and the menu button still share one line instead of the menu button wrapping alone. */
@media (max-width: 339px) {
  .wsk-email-actions > .wsk-email-act > svg { display: none; }
}
@media (min-width: 561px) { .wsk-email-actions { --wsk-email-act-h: 36px; } }
@media (pointer: coarse) { .wsk-email-actions { --wsk-email-act-h: 44px; } }
.wsk-task-viewer { display: flex; align-items: center; gap: var(--wsk-space-2); margin: 0; min-width: 0; max-width: 100%; font-size: 14px; font-weight: 700; }
.wsk-task-viewing { margin: 12px 0; padding: 10px 14px; border-left: 3px solid var(--accent); background: var(--accent-light); color: var(--wsk-heading); font-size: 14px; }
.wsk-btn-danger {
  background: var(--wsk-card); color: #a33e3e; border: 1px solid var(--wsk-border);
  border-radius: 7px; padding: 6px 10px; font-size: 13px; font-weight: 700;
  font-family: inherit; cursor: pointer;
}

.wsk-attachment-card { display: flex; flex-direction: column; gap: 8px; background: #faf3f3; border-color: #d9baba; }
.wsk-attachment-link { color: var(--accent); font-size: 14px; font-weight: 700; text-decoration: underline; }

.wsk-draft-panel { display: flex; flex-direction: column; gap: 8px; }
.wsk-draft-body { margin: 0; font-size: 16px; white-space: pre-line; line-height: 1.5; }



/* Search screen (Task 5, 2026-08-21-workspace-reskin.md) - live-measured
   against the mock (2026-08-21), replacing this section's earlier "no
   fidelity capture exists yet" design-judgment placeholder. Content column
   is 1400px (measured directly: the "N results for..." paragraph's own
   parent carries max-width:1400px/padding:36px 40px 40px - the SAME figure
   Home/Email use, not TOKENS' generic 1280px Layout figure and not this
   section's own earlier 900px guess, which matched nothing once actually
   checked against a live DOM measurement).

   Result cards carry NO fourth badge element on the mock's own real-corpus-
   query results (verified against two different real queries, "dog license
   renewal" and "budget" - each just title/date/snippet, three elements,
   full stop). The uppercase kicker chip on each row here is real,
   unfabricated hub metadata (r.scope, already part of the engine's result
   shape) rendered in the mock's OWN kicker visual language rather than
   omitted - a directed addition (task brief), not a mock-observed one; see
   tests/test_workspace_search_ask_style.py's module docstring for the full
   writeup, including two other corrections this same investigation made
   over the plan's prose (the zero-state card is accent-themed, not oxblood;
   the follow-up chips on the Ask page below are accent pills, not oxblood
   callouts). r.heading_path (a short breadcrumb-like string the engine
   already returns) stands in for the position the mock's own date sits in
   - an honest substitution of one real, already-available field for
   another (the engine's result shape carries no date at all), not new
   data. */

.wsk-srch-main {
  max-width: 1400px; margin: 0 auto; padding: 36px 40px 40px;
}
.wsk-srch-title { margin: 0 0 18px; font-size: 26px; font-weight: 700; color: var(--wsk-heading); }
.wsk-srch-count { margin: 0 0 16px; font-size: 15px; color: var(--wsk-text-secondary); }
/* Forced survivor of the ws-srch-* prefix this task otherwise retires
   entirely: tests/test_workspace_search.py::
   test_search_page_malformed_engine_payload_renders_friendly_error (a
   pinned R3 contract, unmodified by this task) asserts this literal class
   string - only its token values are re-pointed at wsk-* tokens here. */
.ws-srch-error {
  margin: 0 0 16px; padding: 10px 14px; border-radius: 8px; font-size: 14px;
  color: var(--accent); background: #f7e9e9; border: 1px solid #e3b9ba;
}

.wsk-srch-rows { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.wsk-srch-row {
  background: var(--wsk-card); border: 1px solid var(--wsk-border); border-radius: 12px;
  padding: 14px 18px; display: flex; flex-direction: column; gap: 6px;
}
.wsk-srch-row-title { font-size: 16px; font-weight: 700; color: var(--wsk-heading); }
.wsk-srch-row-text { margin: 0; font-size: 14px; color: var(--wsk-text-secondary); line-height: 1.4; }
/* Uppercase kicker chip (TOKENS' kicker recipe: letter-spacing .05em,
   distinct from the .03em table-header family - see this file's :root
   comment) - neutral cream/meta-toned, not accent: r.scope is
   informational metadata (public/clerk visibility), not a call-to-action,
   so it doesn't visually compete with the page's one real CTA (the El-bot
   card's "Ask El-bot" button below). */
.wsk-srch-kicker {
  align-self: flex-start; display: inline-block; font-size: 12px; font-weight: 700;
  padding: 3px 9px; border-radius: 999px; background: var(--wsk-cream); color: var(--wsk-text-secondary);
  text-transform: uppercase; letter-spacing: .05em;
}

/* Zero-state "Ask El-bot instead?" card - live-measured ACCENT-themed
   throughout (kicker: accent-light bg / accent text; button: accent fill),
   NOT oxblood - oxblood is reserved for the Ask PAGE's own El-bot identity
   elements only (the user-question bubble, the follow-up callout - see the
   Ask section below), never this card. Composes .wsk-card (Task 1's shared
   recipe: white bg, 14px radius, `0 2px 10px rgba(38,36,29,.05)` shadow -
   all measured-identical here) with two small overrides this specific card
   needs (a warmer border tone, a max-width cap) rather than re-declaring
   the whole recipe. */
.wsk-srch-elbot-card {
  border-color: var(--wsk-border-warm); max-width: 680px;
  display: flex; flex-direction: column; gap: 12px; align-items: flex-start;
}
.wsk-srch-elbot-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* letter-spacing .04em (measured exactly off this element) - close to but
   not identical to the .05em general kicker figure above; recorded as
   measured, not forced to match a sibling kicker the mock itself doesn't
   unify either. */
.wsk-srch-elbot-kicker {
  display: inline-flex; align-items: center; background: var(--accent-light); color: var(--accent);
  border-radius: 999px; padding: 4px 10px; font-size: 12px; font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase;
}
.wsk-srch-elbot-heading { margin: 0; font-size: 18px; font-weight: 700; color: var(--wsk-text); }
.wsk-srch-elbot-body { margin: 0; font-size: 15px; color: var(--wsk-text-secondary); line-height: 1.5; }
.wsk-srch-elbot-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.wsk-srch-elbot-hint { font-size: 13px; color: var(--wsk-meta); }

.wsk-srch-recent-heading {
  margin: 0 0 8px; font-size: 15px; font-weight: 700; letter-spacing: .03em;
  text-transform: uppercase; color: var(--wsk-text-secondary);
}
.wsk-srch-recent { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.wsk-srch-recent a {
  display: inline-block; padding: 8px 0; font-size: 15px; color: var(--wsk-text);
  text-decoration: none; border-bottom: 1px solid var(--wsk-border-row);
}
.wsk-srch-recent a:hover { color: var(--accent); }
.wsk-srch-empty { margin: 0; font-size: 15px; color: var(--wsk-meta); }

/* ---- Search results page, PR1 of the redesign (wsk-sr-*) -------------------
   A Google-like results page: one large rounded search form, All / Emails /
   Documents tabs, and flat result rows (type icon, source line, title,
   snippet with the query's words marked, then the action links). Everything is
   class-based: no inline style anywhere (strict CSP). Colours come from the
   site's own tokens; the one literal pair is the highlight, dark ink on
   #ffe98a, 14:1 (WCAG AA needs 4.5:1). The pinned wsk-srch-row / -title /
   -text / -kicker classes stay on the markup (tests pin them), so the flat
   look below overrides the older card rules by scoping to .wsk-sr-list. The
   column is 760px wide so lines stay readable; the no-match card keeps its
   own 680px cap. */
.wsk-sr-col { max-width: 760px; }
/* The tabs and the list they filter: one block, so the script has a single root. */
.wsk-sr-results { min-width: 0; }
.wsk-sr-form {
  display: flex; align-items: center; gap: 14px; box-sizing: border-box;
  min-height: 56px; padding: 0 8px 0 22px; margin: 0 0 4px;
  border: 1px solid var(--wsk-border); border-radius: 28px; background: var(--wsk-card);
  box-shadow: 0 1px 6px rgba(32, 33, 36, .18);
}
/* The input itself has no outline (the pill is the control), so the pill
   carries the site's 3px focus ring whenever anything inside it has focus. */
.wsk-sr-form:focus-within { border-color: var(--accent); outline: 3px solid #0b57d0; outline-offset: 2px; }
.wsk-sr-form + .wsk-srch-count { margin-top: 22px; }
.wsk-sr-form-icon { flex: none; color: var(--wsk-meta); }
.wsk-sr-input {
  flex: 1; min-width: 0; min-height: 44px; border: 0; outline: 0; background: transparent;
  font-family: inherit; font-size: 19px; color: var(--wsk-heading);
}
.wsk-sr-input::placeholder { color: var(--wsk-meta); opacity: 1; }
.wsk-sr-submit {
  flex: none; min-height: 44px; padding: 0 22px; border: 0; border-radius: 22px; cursor: pointer;
  background: var(--accent); color: #fff; font-family: inherit; font-size: 16px; font-weight: 700;
}
.wsk-sr-submit:hover { background: var(--accent-hover); }
/* KAN-443: the sentence on the Search page when the search limit was hit. */
.wsk-sr-limited { margin: 22px 0 0; font-size: 18px; line-height: 1.5; color: var(--wsk-text); }
.wsk-sr-tabs {
  display: flex; gap: 4px; margin: 18px 0 16px; border-bottom: 1px solid var(--wsk-border-light);
}
.wsk-sr-tab {
  display: inline-flex; align-items: center; gap: 6px; min-height: 44px; box-sizing: border-box;
  padding: 0 18px; font-size: 17px; color: var(--wsk-text-secondary); text-decoration: none;
  border-bottom: 3px solid transparent; margin-bottom: -1px;
}
.wsk-sr-tab:hover { color: var(--wsk-heading); }
.wsk-sr-tab[aria-current="page"] { color: var(--accent); font-weight: 700; border-bottom-color: var(--accent); }
.wsk-sr-tab-count { font-weight: 400; color: var(--wsk-meta); }
.wsk-sr-allhint { margin: 0 0 16px; font-size: 16px; }
.wsk-sr-allhint a { color: var(--accent); font-weight: 700; }

.wsk-sr-list { gap: 0; }
.wsk-sr-list .wsk-srch-row {
  background: transparent; border: 0; border-bottom: 1px solid var(--wsk-border-light); border-radius: 0;
  padding: 20px 0; gap: 6px;
}
/* A hidden row (outside the selected tab) must stay hidden: the flex display
   the row rule sets would otherwise override the hidden attribute. */
.wsk-sr-list .wsk-srch-row[hidden], .wsk-sr-status[hidden] { display: none; }
.wsk-sr-meta { display: flex; align-items: center; gap: 10px; font-size: 15px; color: var(--wsk-text-secondary); }
.wsk-sr-meta strong { color: var(--wsk-heading); }
/* The text beside the icon wraps in its own column (min-width 0 lets a long
   sender shrink instead of pushing the line under the icon); unicode-bidi
   isolate keeps a name's direction from reordering the date after it. */
.wsk-sr-source { flex: 1 1 0; min-width: 0; overflow-wrap: anywhere; unicode-bidi: isolate; }
.wsk-sr-meta .wsk-srch-kicker { margin-left: auto; flex: none; align-self: center; }
.wsk-sr-icon {
  flex: none; width: 28px; height: 28px; border-radius: 14px;
  display: flex; align-items: center; justify-content: center;
}
.wsk-sr-icon-email { background: var(--accent-light); color: var(--accent); }
.wsk-sr-icon-doc { background: #fdf0d2; color: #5c3d00; }
.wsk-sr-list .wsk-srch-row-title {
  font-size: 22px; line-height: 1.25; font-weight: 700; color: var(--accent); text-decoration: none;
  overflow-wrap: anywhere;
}
.wsk-sr-list a.wsk-srch-row-title:hover { text-decoration: underline; }
.wsk-sr-list .wsk-srch-row-text { font-size: 17px; line-height: 1.5; color: #34383b; overflow-wrap: anywhere; }
.wsk-sr-list mark {
  background: #ffe98a; color: #1d1c17; font-weight: 700; padding: 0 2px; border-radius: 3px;
}
.wsk-sr-actions { margin: 2px 0 0; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; font-size: 16px; color: var(--wsk-meta); }
.wsk-sr-actions a { color: var(--accent); text-decoration: none; padding: 4px 0; }
.wsk-sr-actions a:hover { text-decoration: underline; }
.wsk-sr-tips { margin: 0 0 20px; font-size: 17px; line-height: 1.55; color: #34383b; }
.wsk-sr-tips-lead { margin: 0 0 6px; font-weight: 700; color: var(--wsk-heading); }
.wsk-sr-tips ul { margin: 0; padding-left: 24px; display: flex; flex-direction: column; gap: 4px; }
@media (max-width: 700px) {
  .wsk-sr-form { min-height: 52px; padding-left: 16px; gap: 10px; }
  .wsk-sr-input { font-size: 17px; }
  .wsk-sr-submit { padding: 0 16px; }
  .wsk-sr-tabs { flex-wrap: wrap; gap: 0 2px; }
  .wsk-sr-tab { padding: 0 12px; font-size: 16px; }
  .wsk-sr-list .wsk-srch-row-title { font-size: 20px; }
  .wsk-sr-list .wsk-srch-row-text { font-size: 16px; }
  .wsk-sr-actions { gap: 0 10px; }
  .wsk-sr-actions a { display: inline-flex; align-items: center; min-height: 44px; }
}

/* Ask (El-bot) screen (Task 5) - live-measured against the mock
   (2026-08-21). Content column is 900px (measured directly - unchanged
   from this section's own earlier guess, the one figure that turned out
   correct without adjustment); the mock's own thread renders each turn as
   TWO bubbles (a right-aligned user-question bubble, a left-aligned white
   bot-answer bubble, .wsk-ask-answer below) rather than a plain heading +
   unstyled response area - see workspace/ask.html and fragments/
   ask_answer.html's own header comments for the full correction writeup
   (individual follow-up chips are accent pills, not left-border callouts;
   no avatar exists anywhere in the mock). Confidence banner colors were
   unchanged by this task (the exact demo-era honesty-banner values,
   hub/static/app.css's .banner.green/.banner.amber) - no mock equivalent
   exists for this real, hub-only feature to measure against. KAN-305 later
   turned the banner into a quiet line (see its rule below). Red never
   reaches a banner (tf_search.confidence_band's own contract), so no
   wsk-ask-banner-red rule exists.

   TASK 6.5 OWNER RULING R-1 (2026-08-22, docs/superpowers/specs/2026-08-22-
   owner-queue.md): the mock's own thread bubble/callout/label were oxblood
   #8b0000 at Task 5 close - the mock's own scripted color, live-measured
   accurate at the time and deliberately kept OUTSIDE the accent system
   (TOKENS' own Semantic section). The owner overrode this directly ("El-bot
   says it's red, change that to blue... keep everything at the indigo"):
   El-bot renders in the town accent per owner ruling R-1; the mock's
   oxblood is overridden, not followed - see the owner-queue doc. All three
   oxblood rules below (.wsk-ask-q, .wsk-ask-elbot-callout,
   .wsk-ask-elbot-label) now read var(--accent)/var(--accent-light) instead,
   so El-bot recolors with the rest of the app under any of the five Town
   Themes (Task 6) - see tests/test_workspace_search_ask_style.py's own
   test_elbot_accent_rules_carry_accent_not_oxblood for the flipped CSS-value
   pin (through Task 6 this pinned the opposite invariant). */

.wsk-ask-main {
  max-width: 900px; margin: 0 auto; padding: 24px 32px 32px;
  display: flex; flex-direction: column; gap: 18px;
}
.wsk-ask-back { align-self: flex-start; font-size: 15px; color: var(--wsk-text-secondary); text-decoration: none; }
.wsk-ask-back:hover { color: var(--accent); }
/* KAN-281: the one in-product line that says what Ask IS - Q&A over the town
   record, not something that acts. Left-aligned with the back link and sized
   below body copy, so it reads as page furniture rather than as the first
   message in the thread (.wsk-ask-q, the clerk's own question, is the first
   bubble and must stay visually dominant). max-width keeps it off the
   right-aligned question bubble on a wide viewport. */
.wsk-ask-help {
  align-self: flex-start; max-width: 62ch; margin: 2px 0 6px;
  font-size: 13px; line-height: 1.5; color: var(--wsk-meta);
}
/* The query, as the user-message bubble (measured, Task 5: radius 14px 14px
   4px 14px [bottom-right squared - the "tail" corner for a right-aligned
   bubble], padding 12px 18px, font-size 16px, NORMAL weight - the mock's
   own inline style carries no font-weight at all, so this <h1> needs an
   explicit 400 to cancel the browser's default h1 bold; kept as <h1>, not
   the mock's own plain <div>, for accessible heading structure - "restyle
   only" governs the visual language, not the mock's own semantic HTML
   choices). Background var(--accent)/white text, NOT the mock's own oxblood
   #8b0000 (Task 6.5 owner ruling R-1, 2026-08-22 - see this section's own
   header comment above). */
.wsk-ask-q {
  align-self: flex-end; max-width: 75%; margin: 0;
  background: var(--accent); color: #fff; border-radius: 14px 14px 4px 14px;
  padding: 12px 18px; font-size: 16px; font-weight: 400;
}

/* The bot's own reply, in every state (loading/error/red/green/amber) -
   measured: white bg, 1px #cfcfcf border, radius 14px 14px 14px 4px
   [bottom-left squared - the tail corner for a left-aligned bubble],
   padding 16px 20px, max-width 92%. */
.wsk-ask-answer {
  align-self: flex-start; max-width: 92%; box-sizing: border-box;
  background: var(--wsk-card); border: 1px solid var(--wsk-border);
  border-radius: 14px 14px 14px 4px; padding: 16px 20px;
}
.wsk-ask-loading { margin: 0; font-size: 15px; color: var(--wsk-text-secondary); }

/* Ask opened with no question (2026-09-15). The page used to render
   .wsk-ask-q empty - an accent pill with nothing in it - and then abstain
   from the question nobody asked. The empty state is a heading and an
   invitation instead, left-aligned like the rest of the page rather than
   dressed as a chat message, because nobody has said anything yet. */
.wsk-ask-empty-title {
  align-self: flex-start; margin: 0; font-size: 20px; font-weight: 600;
  color: var(--wsk-text);
}
.wsk-ask-empty-lead {
  align-self: flex-start; margin: 0; font-size: 15px;
  color: var(--wsk-text-secondary);
}
.wsk-ask-empty-suggestions {
  align-self: flex-start; display: flex; flex-wrap: wrap; gap: 8px;
  margin: 0; padding: 0; list-style: none;
}
.wsk-ask-empty-suggestions a { text-decoration: none; }

.wsk-ask-error {
  margin: 0; padding: 10px 14px; border-radius: 8px; font-size: 14px;
  color: var(--accent); background: #f7e9e9; border: 1px solid #e3b9ba;
}

.wsk-ask-noanswer { margin: 0; font-size: 16px; line-height: 1.6; color: var(--wsk-text); white-space: pre-line; }

/* El-bot follow-up callout - TOKENS' own callout recipe (shape confirmed
   byte-accurate by live measurement, Task 5: radius 0 8px 8px 0, 3px
   left-border). Wraps the "El-bot" name label AND the lead line as ONE unit
   (not per-chip - see the chips further below, which use the plain accent-
   pill recipe, not this callout's). Colors are var(--accent-light) bg /
   var(--accent) border, NOT the mock's own bg #f7f2e9 / border #8b0000
   (Task 6.5 owner ruling R-1, 2026-08-22 - see this section's own header
   comment above). */
.wsk-ask-elbot-callout {
  margin-top: 14px; padding: 10px 12px; background: var(--accent-light);
  border-left: 3px solid var(--accent); border-radius: 0 8px 8px 0;
}
/* Mixed-case as-is ("El-bot", not uppercase) - measured: unlike the Search
   zero-state kicker above, this label carries no text-transform. Color
   var(--accent), not the mock's own #8b0000 (R-1, as above). */
.wsk-ask-elbot-label { display: block; margin-bottom: 3px; font-size: 12px; font-weight: 700; color: var(--accent); }
.wsk-ask-followup-lead { margin: 0; font-size: 15px; line-height: 1.5; color: var(--wsk-text); }

.wsk-ask-followups { list-style: none; margin: 14px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
/* Plain accent-bordered pills - NOT oxblood, NOT left-border callouts
   (measured directly off the mock's own chip buttons; a correction over
   the plan's "follow-up chips as left-border callouts" prose - the ONE
   oxblood callout is the lead-in block above, not each chip). */
.wsk-ask-chip {
  display: inline-block; font-size: 13px; font-weight: 600; color: var(--accent);
  background: var(--wsk-card); border: 1px solid var(--accent); border-radius: 20px;
  padding: 8px 14px; text-decoration: none;
}
.wsk-ask-chip:hover { background: var(--accent-light); }

/* KAN-305: confidence is a quiet line below the answer. */
.wsk-ask-banner { margin: 0; font-size: 13px; line-height: 1.45; }
.wsk-ask-body + .wsk-ask-banner { margin-top: 8px; }
.wsk-ask-banner-green { color: #2e6b34; }
.wsk-ask-banner-amber { color: #7a5b12; }
.wsk-ask-conflict { font-weight: 600; }

.wsk-ask-body { margin: 0; font-size: 16px; color: var(--wsk-text); line-height: 1.5; white-space: pre-wrap; overflow-wrap: anywhere; }
.wsk-ask-body + .wsk-ask-body { margin-top: 12px; }
ul.wsk-ask-body, ol.wsk-ask-body { padding-left: 24px; white-space: normal; }
ul.wsk-ask-body li, ol.wsk-ask-body li { white-space: pre-wrap; }
ul.wsk-ask-body li + li, ol.wsk-ask-body li + li { margin-top: 8px; }
.wsk-ask-body strong { font-weight: 700; color: var(--wsk-heading); }
.wsk-ask-body.wsk-ask-nextstep { border-left: 3px solid var(--accent); background: var(--accent-light); padding: 8px 12px; border-radius: 0 6px 6px 0; margin-top: 14px; }
/* Only the first three sources show until the native disclosure is opened. */
.wsk-ask-citations { list-style: none; margin: 12px 0 0; padding: 0; }
.wsk-ask-citation {
  font-size: 13px; line-height: 1.4; color: var(--wsk-text-secondary);
  padding: 6px 0; border-top: 1px solid var(--wsk-border-light); overflow-wrap: anywhere;
}
.wsk-ask-sources-more > .wsk-ask-citations { margin-top: 0; }
.wsk-ask-sources-more > summary {
  margin-top: 6px; cursor: pointer; padding: 6px 0; min-height: 24px;
  font-size: 13px; font-weight: 600; color: var(--accent);
}
.wsk-ask-sources-more > summary:hover { text-decoration: underline; }
.wsk-ask-sources-more > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.wsk-ask-sources-expand { display: inline; }
.wsk-ask-sources-collapse { display: none; }
.wsk-ask-sources-more[open] > summary .wsk-ask-sources-expand { display: none; }
.wsk-ask-sources-more[open] > summary .wsk-ask-sources-collapse { display: inline; }

/* --- Settings screen (Task 6, 2026-08-21-workspace-reskin.md) ---------------------
   workspace/settings.html. Live-measured against the mock's own Settings
   screen (2026-08-21, full JS structural dump - see task-6-report.md):
   content column is 1400px, NOT the 1280px every other workspace screen
   uses (Task 1's own .wsk-main/.wsk-content) - Settings is genuinely a
   single, wider column, not a two-column layout, so it gets its own
   wrapper class rather than forcing 1280px onto a screen that measures
   otherwise. Section wrappers themselves carry NO card border/background
   (measured: only individual elements inside some of them - the
   Connections rows - are bordered) - see settings.html's own comment.

   line-height: normal on the CONTAINER (Task 6 rider, fidelity re-measure,
   2026-08-22 - the SAME fix .wsk-email-main/.wsk-detail-main's own comment
   above already documents for this exact bug class, missed here at first
   pass): body's global line-height:1.4 was inheriting into every element
   below that doesn't set its own, most visibly the Accessibility slider
   row's 24px "A" glyph (computed line-height 33.6px here vs the mock's own
   "normal" 30px) - a 3-4px error that compounds down the page (Town Theme
   heading measured 12px lower than the mock by the time it's reached,
   Connections further still), the identical "small per-element offset
   compounds into a large false-positive fidelity band" pitfall that
   comment already explains. One container-level override fixes every
   descendant at once rather than patching each element individually. */
.wsk-settings-main {
  max-width: 1400px; margin: 0 auto; padding: 36px 40px 40px;
  display: flex; flex-direction: column; gap: 28px; box-sizing: border-box;
  line-height: normal;
}
.wsk-settings-title { margin: 0; font-size: 26px; font-weight: 700; color: var(--wsk-heading); }
.wsk-settings-h2 { margin: 0 0 4px; font-size: 18px; font-weight: 700; }
/* Connections' own H2 has no following .wsk-settings-sub paragraph to
   supply the extra spacing the way Accessibility/Town Theme's do - measured
   margin-bottom is 12px there, not 4px. */
.wsk-settings-h2-solo { margin-bottom: 12px; }
.wsk-settings-sub { margin: 0 0 12px; font-size: 14px; color: var(--wsk-text-secondary); }

/* Accessibility: A/A slider + live percentage readout. Native <input
   type=range> styled with accent-color (the SAME simple technique the
   mock itself uses - measured directly off its own inline style - rather
   than a custom ::-webkit-slider-thumb ruleset neither side needs).

   Acceptance review 2026-09-09 ("the accessibility text size slider doesn't
   work"): the author `height: 6px` that used to live on this rule is gone.
   It shrank the control's whole box - track, thumb and hit area alike - to
   six pixels, which is a mouse target roughly a quarter the height of the
   thumb it is trying to grab; the slider was not broken so much as barely
   catchable. This was already a known defect, just fixed in the wrong
   scope: tools/e2e/mobile-smoke.mjs's tap-target audit measured it at
   229.2x6px and the @media block near the end of this file bumped it to
   24px MOBILE-ONLY, leaving desktop (where a fine pointer makes a 6px
   target worse, not better) on the failing value. 24px is now the base,
   unconditional, so that @media override is deleted rather than duplicated.
   The visible line stays slim regardless: Chrome/Edge/Firefox draw the
   native track as its own thin, vertically centred line independent of the
   element box's height (confirmed at 375px, mobile-report.md), so this
   grows the HIT AREA only. Kept as a plain native control - still no
   ::-webkit-slider-thumb rebuild. */
.wsk-settings-slider-row { display: flex; align-items: center; gap: 14px; width: 100%; }
.wsk-settings-a-sm { font-size: 14px; color: var(--wsk-text-secondary); flex-shrink: 0; }
.wsk-settings-a-lg { font-size: 24px; color: var(--wsk-text-secondary); flex-shrink: 0; }
.wsk-settings-slider { flex: 1 1 0%; accent-color: var(--accent); height: 24px; cursor: pointer; }
.wsk-settings-pct {
  font-size: 14px; font-weight: 700; color: var(--wsk-text);
  width: 44px; text-align: right; flex-shrink: 0;
}

/* Town Theme: 5 swatch buttons, measured 32px circles, gap 14px, wrapping.
   Each swatch's own color is a fixed hex (not var(--accent)) on purpose -
   all five must show simultaneously regardless of which ONE is currently
   active, the same reasoning hub.townflows.home.TAG_CSS_CLASS's own fixed
   per-label colors already establish for this file. Values are the exact
   five from this file's own :root comment block (the authoritative map -
   this is that map's second consumer, after the html.wsk-theme-* rules
   near the top of this file). Selected ring measured directly off the
   mock: box-shadow 0 0 0 2px #fff (the gap ring), 0 0 0 4px <that swatch's
   own color> (the outer ring) - not a single shared accent ring, since an
   UNSELECTED swatch showing another theme's own color must never imply
   that OTHER color is "selected". */
.wsk-theme-swatches { display: flex; gap: 14px; flex-wrap: wrap; }
.wsk-theme-swatch-btn {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  background: none; border: none; cursor: pointer; padding: 4px;
  font-family: inherit; transition: transform .15s ease;
}
.wsk-theme-swatch-btn:active { transform: scale(.96); }
.wsk-theme-swatch { width: 32px; height: 32px; border-radius: 50%; }
.wsk-theme-swatch-indigo { background: #5b4fd6; }
.wsk-theme-swatch-red { background: #7a2428; }
.wsk-theme-swatch-green { background: #2f4a3a; }
.wsk-theme-swatch-navy { background: #2f3f5c; }
.wsk-theme-swatch-slate { background: #4a4740; }
.wsk-theme-swatch-indigo.wsk-theme-swatch-selected { box-shadow: 0 0 0 2px #fff, 0 0 0 4px #5b4fd6; }
.wsk-theme-swatch-red.wsk-theme-swatch-selected { box-shadow: 0 0 0 2px #fff, 0 0 0 4px #7a2428; }
.wsk-theme-swatch-green.wsk-theme-swatch-selected { box-shadow: 0 0 0 2px #fff, 0 0 0 4px #2f4a3a; }
.wsk-theme-swatch-navy.wsk-theme-swatch-selected { box-shadow: 0 0 0 2px #fff, 0 0 0 4px #2f3f5c; }
.wsk-theme-swatch-slate.wsk-theme-swatch-selected { box-shadow: 0 0 0 2px #fff, 0 0 0 4px #4a4740; }
.wsk-theme-swatch-label { margin: 0; font-size: 13px; font-weight: 400; color: var(--wsk-text); }

/* Connections: read-only rows, measured byte-accurate off the mock's own
   Email row (the ONE row this hub renders for real - see settings.html's
   own comment for why the "town website" row is deliberately omitted). */
.wsk-conn-rows { display: flex; flex-direction: column; gap: 10px; }
.wsk-conn-row {
  display: flex; justify-content: space-between; align-items: center;
  background: var(--wsk-card); border: 1px solid var(--wsk-border);
  border-radius: 12px; padding: 16px 20px; box-sizing: border-box;
}
.wsk-conn-label { margin: 0; font-size: 16px; font-weight: 700; }
.wsk-conn-value { margin: 2px 0 0; font-size: 14px; color: var(--wsk-text-secondary); }
.wsk-conn-status { font-size: 15px; font-weight: 700; color: var(--accent); }

.wsk-demo-stream-tag { margin-left: 8px; font-size: 12px; font-weight: 400; color: var(--wsk-meta); }
.wsk-demo-stream .wsk-conn-row { gap: 12px; }
.wsk-demo-stream-detail { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.wsk-demo-stream form { margin: 0; flex-shrink: 0; }
.wsk-demo-stream .wsk-task-notice { margin-top: 8px; }
.wsk-demo-stream [hidden] { display: none; }

@media (max-width: 767px) {
  .wsk-demo-stream .wsk-conn-row { flex-direction: column; align-items: stretch; }
  .wsk-demo-stream form { width: 100%; }
  .wsk-demo-stream form button { width: 100%; }
}

/* ==== Settings page redesign (owner order 2026-10-11) ====================
   workspace/settings.html. The audience is town clerks, many older and not
   technical, so: a few labelled groups, one short line under each title, one
   obvious action per row, 16px body text, 44px targets, and a state said in a
   word and a mark, never in color alone.

   LEAK-PROOF: every selector below starts with .wsk-sp (the page class, on
   <main>), and the shared .wsk-settings-* / .wsk-conn-* / .wsk-theme-* rules
   above stay as they were - Upload and Email still use them. tests/
   test_settings_page_ux.py reads this block between its two markers and fails
   the build on any selector that does not start with .wsk-sp, on :nth-child,
   !important or url(, and on a removed focus ring (the global
   :focus-visible ring above stays in force; nothing here switches it off).
   This block sits beside the older Settings rules, not at the end of the file,
   so peer lanes that append at the end do not collide with it.

   Layout: each group is a two-column grid on a wide screen (title and one line
   on the left, ONE white card of rows on the right), a single column below
   900px. A row is one setting: what it is, one plain sentence, and the one
   action. Rows are divided by hairlines inside the card (the structure is the
   information: a row is a setting), and groups by one more hairline. Spacing
   comes from the Home scale (--wsk-space-*). tests/test_demo_stream_controls.py
   reads the LAST phone-width media block of 767px in this file, and that must
   stay the demo one above, so this block uses 899px and 600px instead. */
.wsk-sp {
  --sp-ok-ink: #1b5e36;   --sp-ok-bg: #e4f2e8;
  --sp-warn-ink: #7a4b00; --sp-warn-bg: #fdf1d3;
  --sp-bad-ink: #8c2323;  --sp-bad-bg: #fbe8e8;
  --sp-quiet-ink: var(--wsk-text-secondary); --sp-quiet-bg: #eceff0;
  --sp-card-pad: 20px;
}
.wsk-sp.wsk-settings-main { gap: var(--wsk-space-5); }
.wsk-sp-head { display: flex; flex-direction: column; gap: var(--wsk-space-1); }
.wsk-sp-lead { margin: 0; max-width: 62ch; font-size: 17px; line-height: 1.5; color: var(--wsk-text-secondary); }

.wsk-sp-group {
  display: grid; grid-template-columns: minmax(200px, 280px) minmax(0, 880px);
  column-gap: calc(var(--wsk-space-5) * 2); row-gap: var(--wsk-space-3); align-items: start;
  max-width: 1208px;   /* 280 + 48 + 880: the hairline between groups ends where the card does */
}
.wsk-sp-group + .wsk-sp-group { padding-top: var(--wsk-space-5); border-top: 1px solid var(--wsk-border-light); }
.wsk-sp-group-head { display: flex; flex-direction: column; gap: var(--wsk-space-1); }
.wsk-sp-group-title { margin: 0; font-size: 20px; line-height: 1.25; font-weight: 700; color: var(--wsk-heading); }
.wsk-sp-group-help { margin: 0; font-size: 16px; line-height: 1.5; color: var(--wsk-text-secondary); }
.wsk-sp-card { min-width: 0; background: var(--wsk-card); border: 1px solid var(--wsk-border); border-radius: 12px; }

.wsk-sp-row {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--wsk-space-3) var(--wsk-space-5); min-width: 0;
  padding: var(--wsk-space-4) var(--sp-card-pad);
}
.wsk-sp-row + .wsk-sp-row { border-top: 1px solid var(--wsk-border-light); }
.wsk-sp-row-stack { flex-direction: column; flex-wrap: nowrap; align-items: stretch; gap: var(--wsk-space-3); }
.wsk-sp-text {
  flex: 1 1 260px; min-width: 0; display: flex; flex-direction: column; align-items: flex-start;
  gap: var(--wsk-space-1); overflow-wrap: anywhere;
}
.wsk-sp-row-stack > .wsk-sp-text { flex: none; }
.wsk-sp-label { margin: 0; font-size: 17px; line-height: 1.3; font-weight: 700; color: var(--wsk-heading); }
.wsk-sp-value { margin: 0; font-size: 18px; line-height: 1.3; font-weight: 700; color: var(--wsk-text); overflow-wrap: anywhere; }
.wsk-sp-help { margin: 0; max-width: 62ch; font-size: 16px; line-height: 1.5; color: var(--wsk-text-secondary); }
.wsk-sp-control { flex: 0 0 auto; display: flex; flex-wrap: wrap; align-items: center; gap: var(--wsk-space-2); margin: 0; min-width: 0; }
.wsk-sp-row-stack > .wsk-sp-control { flex: none; }
.wsk-sp-control-status { flex-direction: column; align-items: flex-end; }

/* Buttons keep the house look (.wsk-btn-*), at a 44px touch size and 15px type. */
.wsk-sp .wsk-btn-primary, .wsk-sp .wsk-btn-outline, .wsk-sp .wsk-btn-neutral {
  min-height: 44px; padding: 10px 20px; box-sizing: border-box; border-radius: 8px;
  font-size: 15px; line-height: 1.25; text-align: center;
}

/* A state in a word and a mark. */
.wsk-sp-chip {
  display: inline-flex; align-items: center; gap: 6px; width: fit-content;
  padding: 3px 12px; border-radius: 999px; font-size: 15px; font-weight: 700; line-height: 1.4;
  color: var(--sp-quiet-ink); background: var(--sp-quiet-bg);
}
.wsk-sp-chip::before { content: "\2022"; }
.wsk-sp-chip-neutral { color: var(--sp-quiet-ink); background: var(--sp-quiet-bg); }
.wsk-sp-chip-ok { color: var(--sp-ok-ink); background: var(--sp-ok-bg); }
.wsk-sp-chip-ok::before { content: "\2713"; }
.wsk-sp-chip-attention { color: var(--sp-warn-ink); background: var(--sp-warn-bg); }
.wsk-sp-chip-attention::before { content: "!"; }
.wsk-sp-chip-problem { color: var(--sp-bad-ink); background: var(--sp-bad-bg); }
.wsk-sp-chip-problem::before { content: "!"; }

/* "Saved." beside the control that was changed. */
.wsk-sp-saved {
  margin: 0; padding: 10px 14px; border-radius: 8px; background: var(--sp-ok-bg); color: var(--sp-ok-ink);
  font-size: 16px; font-weight: 700; line-height: 1.4;
}
.wsk-sp-saved::before { content: "\2713\00a0"; }
.wsk-sp-saved[hidden] { display: none; }
.wsk-sp-note { flex: 1 0 100%; }
.wsk-sp .wsk-task-notice { font-size: 16px; line-height: 1.4; }

/* Text size: a slider with a thumb big enough to grab, A to A, and the percentage. */
.wsk-sp .wsk-settings-slider-row { flex-wrap: wrap; gap: var(--wsk-space-2) var(--wsk-space-3); width: 100%; }
.wsk-sp .wsk-settings-a-sm { font-size: 16px; }
.wsk-sp .wsk-settings-a-lg { font-size: 30px; line-height: 1; }
.wsk-sp .wsk-settings-slider {
  -webkit-appearance: none; appearance: none; flex: 1 1 140px; min-width: 0;
  height: 36px; margin: 0; background: transparent; accent-color: var(--accent);
}
.wsk-sp .wsk-settings-slider::-webkit-slider-runnable-track {
  height: 8px; border-radius: 999px; background: var(--wsk-border-light); box-shadow: inset 0 0 0 1px var(--wsk-border);
}
.wsk-sp .wsk-settings-slider::-webkit-slider-thumb {
  -webkit-appearance: none; box-sizing: border-box; width: 28px; height: 28px; margin-top: -10px;
  border: 3px solid #fff; border-radius: 50%; background: var(--accent);
  box-shadow: 0 0 0 1px var(--accent), 0 2px 5px rgba(38, 36, 29, .3);
}
.wsk-sp .wsk-settings-slider::-moz-range-track {
  height: 8px; border-radius: 999px; background: var(--wsk-border-light); box-shadow: inset 0 0 0 1px var(--wsk-border);
}
.wsk-sp .wsk-settings-slider::-moz-range-thumb {
  box-sizing: border-box; width: 28px; height: 28px; border: 3px solid #fff; border-radius: 50%; background: var(--accent);
  box-shadow: 0 0 0 1px var(--accent), 0 2px 5px rgba(38, 36, 29, .3);
}
.wsk-sp .wsk-settings-pct {
  width: auto; min-width: 72px; padding: 4px 12px; border-radius: 999px; text-align: center;
  background: var(--accent-light); color: var(--accent-hover); font-size: 16px;
}

/* Live preview under the slider: a two-line sample drawn at the size the slider is on.
   Its font is 16px (the page's body size) times chosen/page, because the page itself only
   rescales when the slider is let go (workspace_settings.js sets data-chosen and data-page
   to the step keys of hub.townflows.prefs.TEXTSIZE_OPTIONS). It has no transition on
   purpose: it moves with the slider and nothing in it animates, so reduced motion needs
   no special case. */
.wsk-sp-preview {
  --sp-chosen: 1; --sp-page: 1; min-width: 0;
  padding: var(--wsk-space-3) var(--wsk-space-4); border: 1px solid var(--wsk-border-light);
  border-radius: 10px; background: var(--wsk-bg);
}
.wsk-sp-preview[data-chosen="100"] { --sp-chosen: 1; }
.wsk-sp-preview[data-chosen="112"] { --sp-chosen: 1.125; }
.wsk-sp-preview[data-chosen="125"] { --sp-chosen: 1.25; }
.wsk-sp-preview[data-chosen="137"] { --sp-chosen: 1.375; }
.wsk-sp-preview[data-chosen="150"] { --sp-chosen: 1.5; }
.wsk-sp-preview[data-page="100"] { --sp-page: 1; }
.wsk-sp-preview[data-page="112"] { --sp-page: 1.125; }
.wsk-sp-preview[data-page="125"] { --sp-page: 1.25; }
.wsk-sp-preview[data-page="137"] { --sp-page: 1.375; }
.wsk-sp-preview[data-page="150"] { --sp-page: 1.5; }
.wsk-sp-preview-label { margin: 0 0 var(--wsk-space-1); font-size: 15px; line-height: 1.3; font-weight: 700; color: var(--wsk-text-secondary); }
.wsk-sp-preview-text { margin: 0; font-size: calc(16px * var(--sp-chosen) / var(--sp-page)); line-height: 1.5; color: var(--wsk-text); overflow-wrap: anywhere; }
.wsk-sp-preview-line { display: block; }

/* Color: the five choices are pills with a dot, a name and, once chosen, a check. */
.wsk-sp .wsk-theme-swatches { flex: 1 1 100%; gap: var(--wsk-space-2) var(--wsk-space-3); }
.wsk-sp .wsk-theme-swatch-btn {
  flex: 1 1 auto; justify-content: center;
  flex-direction: row; gap: 10px; min-height: 44px; padding: 6px 18px 6px 8px;
  border: 1.5px solid var(--wsk-border); border-radius: 999px; background: var(--wsk-card);
  color: var(--wsk-text); font-size: 16px; line-height: 1.25;
  transition: border-color .12s ease, background-color .12s ease, transform .15s ease;
}
.wsk-sp .wsk-theme-swatch-btn:hover { border-color: var(--wsk-icon); }
.wsk-sp .wsk-theme-swatch-btn[aria-pressed="true"] { border-color: var(--accent); background: var(--accent-light); font-weight: 700; }
.wsk-sp .wsk-theme-swatch { display: grid; place-items: center; flex: none; width: 28px; height: 28px; }
.wsk-sp .wsk-theme-swatch-selected { box-shadow: none; }
.wsk-sp .wsk-theme-swatch-selected::after {
  content: ""; width: 7px; height: 12px; margin-top: -2px;
  border: solid #fff; border-width: 0 2.5px 2.5px 0; rotate: 45deg;
}
.wsk-sp .wsk-theme-swatch-label { font-size: 16px; font-weight: inherit; color: inherit; }

/* Remembered answers: the Show link reads as a button, and the opened list as rows. */
.wsk-sp #remembered-answers-open {
  display: inline-flex; align-items: center; justify-content: center; min-height: 44px; padding: 10px 20px;
  box-sizing: border-box; border: 1.5px solid var(--accent); border-radius: 8px; background: var(--wsk-card);
  color: var(--accent); font-size: 15px; font-weight: 700; line-height: 1.25; text-decoration: none;
}
.wsk-sp #remembered-answers-open:hover { background: var(--accent-light); }
/* KAN-499: while the list is open its own Close is the one control; the opener comes back on close. */
.wsk-sp #remembered-answers-open[hidden] { display: none; }
.wsk-sp .wsk-remembered-slot { flex: 1 0 100%; min-width: 0; }
.wsk-sp .wsk-remembered { display: flex; flex-direction: column; align-items: flex-start; gap: var(--wsk-space-3); padding-top: var(--wsk-space-3); border-top: 1px solid var(--wsk-border-light); }
.wsk-sp .wsk-remembered > [data-close-remembered] { margin-bottom: 0; }
.wsk-sp .wsk-remembered-list { align-self: stretch; list-style: none; margin: 0; padding: 0; }
.wsk-sp .wsk-remembered-item {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--wsk-space-2) var(--wsk-space-4);
  padding: var(--wsk-space-3) 0;
}
/* KAN-499: one compact row - question and saved answer on the left, small Edit / Undo on the
   right (under the text on a phone). The shared sentences are one muted line above the list. */
.wsk-sp .wsk-remembered-note { margin: 0; font-size: 15px; line-height: 1.45; color: var(--wsk-text-secondary); }
.wsk-sp .wsk-remembered-main { flex: 1 1 260px; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.wsk-sp .wsk-remembered-main .wsk-remembered-label { flex: none; }
.wsk-sp .wsk-remembered-answer { margin: 0; font-size: 15px; line-height: 1.45; color: var(--wsk-text); overflow-wrap: anywhere; }
.wsk-sp .wsk-remembered-answer-long { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; }
.wsk-sp .wsk-remembered-meta { margin: 0; font-size: 14px; line-height: 1.4; color: var(--wsk-text-secondary); }
.wsk-sp .wsk-remembered-saved { margin: 2px 0 0; width: fit-content; padding: 4px 12px; border: 1px solid #a9d9bb; border-radius: 8px; background: #eefaf2; color: #14532d; font-size: 15px; font-weight: 700; line-height: 1.4; }
.wsk-sp .wsk-remembered-actions { flex: 0 0 auto; display: flex; flex-wrap: wrap; gap: var(--wsk-space-2); }
.wsk-sp .wsk-remembered-actions .wsk-btn-outline { min-width: 64px; padding: 8px 16px; }
.wsk-sp .wsk-remembered-edit { flex: 1 0 100%; display: flex; flex-direction: column; gap: var(--wsk-space-2); }
.wsk-sp .wsk-remembered-edit textarea { width: 100%; box-sizing: border-box; resize: vertical; font: inherit; font-size: 15px; line-height: 1.5; }
.wsk-sp .wsk-remembered-edit-actions { display: flex; flex-wrap: wrap; gap: var(--wsk-space-2); }
.wsk-sp .wsk-remembered-item + .wsk-remembered-item { border-top: 1px solid var(--wsk-border-light); }
.wsk-sp .wsk-remembered-item form { margin: 0; }
.wsk-sp .wsk-remembered-label { margin: 0; flex: 1 1 260px; min-width: 0; font-size: 16px; line-height: 1.4; font-weight: 700; color: var(--wsk-heading); overflow-wrap: anywhere; }
.wsk-sp .wsk-remembered .wsk-empty { font-size: 15px; line-height: 1.4; }
.wsk-sp .wsk-remembered-item > [role="alert"], .wsk-sp .wsk-remembered-item > [role="status"] { flex: 1 0 100%; font-weight: 700; }
.wsk-sp .wsk-remembered-main [role="alert"] { font-weight: 700; }
.wsk-sp .wsk-remembered-item [role="alert"] { color: var(--sp-bad-ink); }

/* Town website: a short line first, the detail one click away. */
.wsk-sp-more { margin: 0; }
/* The chevron before the summary is the shared one (.wsk-sp-more is in the
   disclosure-chevron lists further down, which tests/test_disclosure_chevrons.py
   requires of every <details>); only the look of the summary text is set here. */
.wsk-sp-more > summary { padding: 6px 0; font-size: 16px; font-weight: 700; color: var(--accent); cursor: pointer; }
.wsk-sp .wsk-survey-status { font-size: 16px; line-height: 1.5; }
.wsk-sp .wsk-survey-failed { color: var(--sp-bad-ink); }

/* The question that comes before an action that is hard to take back. */
.wsk-sp-confirm {
  width: min(480px, calc(100vw - 32px)); max-height: calc(100vh - 32px); overflow: auto; margin: auto;
  padding: var(--wsk-space-5); box-sizing: border-box; background: var(--wsk-card); color: var(--wsk-text);
  border: 1px solid var(--wsk-border); border-radius: 14px; box-shadow: 0 12px 40px rgba(38, 36, 29, .22);
}
.wsk-sp-confirm::backdrop { background: rgba(38, 36, 29, .45); }
.wsk-sp-confirm-title { margin: 0 0 var(--wsk-space-2); font-size: 20px; line-height: 1.3; font-weight: 700; color: var(--wsk-heading); }
.wsk-sp-confirm-detail { margin: 0 0 var(--wsk-space-4); font-size: 16px; line-height: 1.5; color: var(--wsk-text-secondary); overflow-wrap: anywhere; }
.wsk-sp-confirm-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--wsk-space-3); margin: 0; }

/* Demo tools (development server only): two sub-rows under one heading. */
.wsk-sp-demo { min-width: 0; }
.wsk-sp-demo-head { display: flex; flex-direction: column; gap: var(--wsk-space-1); padding: var(--wsk-space-4) var(--sp-card-pad) var(--wsk-space-3); }
.wsk-sp .wsk-demo-stream-tag { margin-left: 8px; font-size: 14px; font-weight: 400; color: var(--wsk-meta); }
.wsk-sp .wsk-conn-rows { gap: 0; }
.wsk-sp .wsk-conn-row {
  border: 0; border-top: 1px solid var(--wsk-border-light); border-radius: 0; background: none;
  padding: var(--wsk-space-4) var(--sp-card-pad); gap: var(--wsk-space-3) var(--wsk-space-5);
}
.wsk-sp .wsk-conn-label { font-size: 17px; }
.wsk-sp .wsk-conn-value { margin: var(--wsk-space-1) 0 0; font-size: 16px; line-height: 1.5; }

/* KAN-505: "Need help?" near the top of the page, and one width for every row's action button. */
.wsk-sp-helpbar {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--wsk-space-3) var(--wsk-space-5); max-width: 1208px; box-sizing: border-box;
  padding: var(--wsk-space-4) var(--sp-card-pad); background: var(--accent-light);
  border: 1px solid var(--wsk-border); border-radius: 12px;
}
.wsk-sp-helpbar-text { flex: 1 1 260px; min-width: 0; display: flex; flex-direction: column; gap: var(--wsk-space-1); }
.wsk-sp-helpbar-title { margin: 0; font-size: 18px; line-height: 1.3; font-weight: 700; color: var(--wsk-heading); }
.wsk-sp-helpbar-line { margin: 0; font-size: 16px; line-height: 1.5; color: var(--wsk-text-secondary); }
.wsk-sp-helpbar > .wsk-btn-primary { flex: 0 0 auto; min-width: 200px; }
.wsk-sp-control-status > .wsk-btn-outline, .wsk-sp-control-status > .wsk-btn-primary,
.wsk-sp-control-act > .wsk-btn-outline { width: 332px; max-width: 100%; }

@media (max-width: 899px) {
  .wsk-sp-group { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 600px) {
  .wsk-sp { --sp-card-pad: 16px; }
  .wsk-sp-group-title { font-size: 19px; }
  .wsk-sp-row:not(.wsk-sp-row-stack) > .wsk-sp-control { flex: 1 1 100%; }
  .wsk-sp-control > a, .wsk-sp-control > button { flex: 1 1 100%; }
  .wsk-sp-helpbar > .wsk-btn-primary { flex: 1 1 100%; }
  .wsk-sp-control-status > .wsk-btn-outline, .wsk-sp-control-status > .wsk-btn-primary,
  .wsk-sp-control-act > .wsk-btn-outline { width: 100%; }
  .wsk-sp-control-status { align-items: stretch; }
  .wsk-sp-control-status > .wsk-sp-chip { align-self: flex-start; }
  .wsk-sp-confirm { padding: var(--wsk-space-4); }
  .wsk-sp-confirm-actions { flex-direction: column-reverse; }
  .wsk-sp-confirm-actions > button { width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .wsk-sp .wsk-theme-swatch-btn { transition: none; }
}
/* ==== end Settings page redesign ==== */

/* --- Upload (U3, docs/superpowers/plans/2026-09-03-upload-import.md) --------
   Same page container recipe as Settings (the two screens share the
   wsk-settings-title/-h2/-sub type scale on purpose - one text system, no
   third heading style). Tokens only: the accent family for the positive
   "indexed" pill and focus rings, neutrals for everything else. The table is
   a real <table> (five labelled columns of metadata) inside the existing
   wsk-table-wrap/wsk-table-scroll pair so it scrolls on a phone instead of
   widening the page. */
.wsk-upload-main {
  max-width: 1400px; margin: 0 auto; padding: 28px 40px 40px;
  display: flex; flex-direction: column; gap: 20px; box-sizing: border-box;
  line-height: normal;
}
/* Label text for the input in the Upload drop zone - present for screen
   readers, off-screen for everyone else. */
.wsk-visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}
/* The Upload form, drop zone, file list and action row live in the
   "upload page + modern selects" block at the end of this file. */
.wsk-upload-tags { display: flex; flex-wrap: wrap; gap: 5px; }
.wsk-upload-tag { border-radius: 12px; padding: 3px 8px; background: #f0ecff; color: #493b91; font-size: 12px; }
.wsk-upload-tag-editor { margin-top: 8px; }
.wsk-upload-tag-editor summary { cursor: pointer; }
.wsk-upload-tag-editor form { display: grid; gap: 8px; margin-top: 8px; min-width: 180px; }
.wsk-upload-tag-editor label { display: grid; gap: 4px; }
.wsk-upload-tag-editor [hidden] { display: none; }
.wsk-upload-note { margin: 0; font-size: 14px; color: var(--wsk-text-secondary); }
.wsk-upload-unconfigured { margin: 0; font-size: 15px; color: var(--wsk-text); }
.wsk-upload-table { width: 100%; min-width: 760px; border-collapse: collapse; font-size: 14px; }
.wsk-upload-table th {
  text-align: left; padding: 13px 16px; font-size: 13px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .03em; color: var(--wsk-text-secondary);
  background: var(--wsk-cream); border-bottom: 1px solid var(--wsk-border);
}
.wsk-upload-row td {
  padding: 12px 16px; vertical-align: top; color: var(--wsk-text);
  border-bottom: 1px solid var(--wsk-border-row);
}
.wsk-upload-row:nth-of-type(even) td { background: var(--wsk-bg); }
.wsk-upload-name { overflow-wrap: anywhere; }
.wsk-upload-when { white-space: nowrap; color: var(--wsk-text-secondary); }
.wsk-upload-status {
  display: inline-block; padding: 3px 10px; border-radius: 999px;
  font-size: 13px; font-weight: 700; white-space: nowrap;
}
.wsk-upload-status-sent { background: var(--wsk-cream); color: var(--wsk-text-secondary); border: 1px solid var(--wsk-border); }
.wsk-upload-status-indexed { background: var(--accent-light); color: var(--accent); border: 1px solid var(--accent-light); }
.wsk-upload-status-failed { background: var(--wsk-heading); color: #fff; border: 1px solid var(--wsk-heading); }
.wsk-upload-detail { display: block; margin-top: 4px; font-size: 13px; color: var(--wsk-text-secondary); }

/* ============================================================================
   Mobile responsive (owner queue Q13, "Mobile-friendly: responsive layout for
   phone ratios + functionality tested on mobile viewport" - the reskin
   lane's last item, docs/superpowers/specs/2026-08-22-owner-queue.md).
   Design target 375x812, verified to also hold at 360x812 and 414x896 (the
   no-horizontal-scroll invariant) by tools/e2e/mobile-smoke.mjs. Two
   breakpoints, both narrower than every desktop layout figure in this file
   (the wider one is TOKENS' own single 700px media width; 480px is a second,
   tighter tier for the shell's own 4-tab admin nav row and a couple of other
   surfaces that stay cramped at 700px's own spacing alone) - matches this
   task's own instruction to prefer a small breakpoint set over one per
   component.

   Everything mobile-specific lives in ONE OF THESE TWO BLOCKS, with exactly
   two exceptions living beside the rules they modify instead (both proven
   zero-visual-diff at >=700px, see each one's own comment): .wsk-search-icon's
   enlarged hit area (shell section, above) and .wsk-tab's var()-wrapped
   gap/font-size/padding + `overflow: hidden` (shared shell section, above).
   Nothing else in this file changed - every desktop pixel value the
   fidelity board measures against the mock is governed by the SAME rules
   Tasks 1-7 already shipped and gated; see mobile-report.md for the
   GATE_SCREENS re-run proving that (any drift is attributed there, not
   hand-waved).

   Design choices worth naming up front (full rationale + screenshots in
   mobile-report.md):
     - Shell row1 (logo/search/icons) WRAPS to two lines below 700px: logo +
       icon buttons share line 1 (CSS `order`, NOT a markup/DOM reorder - the
       shared shell fragment is byte-identical at every width; only its
       in-viewport visual arrangement below 700px changes), the search form
       drops to its own full-width line 2 - the exact pattern this task's own
       dispatch names as acceptable ("logo + icons row, search full-width
       beneath"). Disclosed trade-off: `order` reorders visually, not in the
       DOM, so a sighted keyboard-only user's tab order (search input, then
       the account-menu avatar - the chat/bell icons are aria-disabled, never
       real tab stops) runs one step "out of visual order" relative to the
       two-line arrangement; a common, accepted trade-off for responsive top
       bars, and touch users (the primary mobile input mode) never encounter
       it since they tap directly rather than tabbing.
     - Two-column pages (Home's My Tasks + side rail; the town-admin page's
       .ws-main/.ws-content) STACK to one column; the side rail's fixed
       300px width becomes 100%.
     - The email inbox table RESTRUCTURES into stacked cards (from/date on
       top, subject+snippet full width, tag+priority chips on the bottom
       line) rather than gaining a horizontal-scroll container - the admin
       users table instead keeps its existing column-scroll wrapper AND also
       stacks its own flex columns, a simpler treatment for a simpler table.
       Column headers (which double as per-column sort links on Email) are
       hidden below 700px: a 5-column grid header has no honest visual
       counterpart once the rows themselves stop being 5 columns wide, and
       the toolbar's own always-visible "Sort: Most urgent" pill remains the
       mobile sort affordance either way - no sort capability is actually
       lost, only the finer per-column control.
   ============================================================================ */

@media (max-width: 700px) {
  /* --- shell: two-line top bar, condensed tab row -------------------------- */
  .wsk-shell-row1 {
    flex-wrap: wrap;
    justify-content: space-between;
    padding: 12px 16px;
    row-gap: 10px;
  }
  /* Visual reorder only (see this section's own header comment on the
     tab-order trade-off) - .wsk-logo/.wsk-shell-icons share line 1 (no other
     rule of theirs changes), .wsk-search-col claims the whole of line 2.
     .wsk-demo-badge shares .wsk-logo's own order value: without it the
     badge's default order 0 would jump it AHEAD of the wordmark here (equal
     order values fall back to document order, which keeps it glued right
     after the logo, same as at desktop width). */
  .wsk-logo { order: 1; }
  .wsk-demo-badge { order: 1; }
  .wsk-shell-icons { order: 2; }
  .wsk-search-col { order: 3; flex-basis: 100%; }

  :root {
    --wsk-tab-pad-x: 8px;
    --wsk-tab-gap: 6px;
  }
  .wsk-tabs { padding: 6px 8px 0; gap: 2px; }
  /* The label <span> is ALSO a flex item (of .wsk-tab's own inline-flex) and
     needs the identical "let me shrink below my own content size" fix
     .wsk-tab itself carries above - a DIFFERENT selector (not the one that
     rule's own "exactly one .wsk-tab rule" pin governs), so a plain
     min-width:0 is fine here. Ellipsis is the graceful floor: if a tab still
     can't fit its full label ("Settings", the longest, at the narrowest
     tested width - 360px, 4-tab admin view) it truncates instead of forcing
     the row - and the page - wider than the viewport. */
  .wsk-tab span {
    min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }

  /* --- page containers: less horizontal padding, single column ------------ */
  .wsk-main, .ws-main, .wsk-email-main, .wsk-detail-main, .wsk-srch-main,
  .wsk-ask-main, .wsk-settings-main, .wsk-upload-main {
    padding: 20px 16px 28px;
  }
  /* .wsk-main (Home)/.ws-main (town-admin) are the only two-column layouts
     (content + a fixed-width side rail) - align-items:stretch OVERRIDES
     their own align-items:flex-start (a row-axis concern there; once
     flex-direction is column, flex-start would instead left-align each
     child at its own content width rather than stretching it to fill the
     line - the opposite of what stacking needs here). */
  .wsk-main, .ws-main {
    flex-direction: column;
    align-items: stretch;
    gap: 20px;
  }
  .wsk-side { width: 100%; }

  /* --- auth cards (login/register/forgot-password/join) ------------------- */
  .wsk-auth-page { padding: 24px 16px; }
  .wsk-auth-card { padding: 32px 24px; }

  /* --- Home: tasks-head control row, task-card action/meta rows ----------- */
  .wsk-tasks-head { flex-wrap: wrap; }
  .wsk-tasks-controls { flex-wrap: wrap; width: 100%; }
  .wsk-sort-select { min-width: 0; flex: 1 1 auto; }
  .wsk-task-actions { flex-wrap: wrap; }
  .wsk-task-meta { flex-wrap: wrap; }
  /* Real, live-measured mobile-smoke finding (not hand-computed): 90x21.6px
     at 375px, under the 24px floor. Mobile-gated for the same in-normal-flow
     reason as the other in-flow padding in this block: this field sits inside .wsk-task-meta,
     followed by .wsk-task-divider/.wsk-task-actions in the same card, so a
     taller box here shifts everything after it - safe only below 700px.
     Right padding (26px, reserved for the calendar-icon background image)
     and every other property are untouched; only the vertical padding
     grows, and the icon's own `background-position: right 4px center`
     recenters automatically as the box gets taller. */
  .wsk-task-due-input { padding: 6px 26px 6px 6px; }
  .wsk-sched-head { flex-wrap: wrap; row-gap: 8px; }

  /* --- Email: header row, table -> stacked cards --------------------------- */
  .wsk-email-head { flex-wrap: wrap; row-gap: 8px; }
  .wsk-email-head-actions { flex-wrap: wrap; min-width: 0; }
  .wsk-refreshed-label { white-space: normal; }
  .wsk-detail-meta { flex-wrap: wrap; }
  .wsk-ethead { display: none; }
  .wsk-erow {
    grid-template-columns: 1fr auto;
    grid-template-areas:
      "from date"
      "subject subject"
      "tag priority";
    min-width: 0;
    column-gap: 10px;
    row-gap: 4px;
    padding: 12px 14px;
  }
  /* Same selector/specificity as the desktop divider rule (further above in
     this file) - equal specificity, later source order wins, so this
     cleanly turns the dividers off instead of needing !important. The
     desktop 5-column dividers have no honest place in a 3-row stacked
     card. */
  .wsk-ethead > *:not(:last-child), .wsk-erow > *:not(:last-child) {
    border-right: none;
  }
  .wsk-erow-from { grid-area: from; padding: 0; min-width: 0; }
  .wsk-erow-date { grid-area: date; padding: 0; text-align: right; white-space: nowrap; }
  .wsk-erow-subject-wrap { grid-area: subject; padding: 0; }
  .wsk-erow-tag { grid-area: tag; padding: 0; justify-self: start; }
  .wsk-erow-priority { grid-area: priority; padding: 0; justify-self: end; text-align: right; }
  .wsk-pagination-nav { flex-wrap: wrap; }

  /* --- Ask: back-to-home link ------------------------------------------------
     Real, live-measured mobile-smoke finding: 102x21px, under the 24px
     floor. Not a "link in a sentence" (WCAG's own exemption for those) - a
     standalone back-navigation link, fixed the same way as the in-flow
     padding above. .wsk-ask-back is a flex
     item of .wsk-ask-main's own column layout (gap: 18px between children),
     so - same reasoning as .wsk-task-due-input above - the padding is
     mobile-gated rather than universal: it grows this element's own box,
     which would shift every answer bubble below it by a few px on the
     1280px-viewport fidelity board. */
  .wsk-ask-back { padding: 4px 0; }

  /* --- Settings: connections row (a long connected-account value must wrap,
     not force the row wider - text-flow analogue of .wsk-tab's own
     unbroken-run floor explained above) -------------------------------------- */
  .wsk-conn-row { flex-wrap: wrap; row-gap: 6px; }
  .wsk-conn-value { overflow-wrap: anywhere; }
  /* (The mobile-only `.wsk-settings-slider { height: 24px }` that used to
     sit here is gone - not dropped, PROMOTED. It was this file's fix for
     mobile-smoke.mjs's 229.2x6px tap-target finding; the 2026-09-09
     acceptance review found the same 6px box made the slider near-
     ungrabbable with a mouse too, so 24px is now the unconditional base
     value up in the Settings section and this breakpoint has nothing left
     to override. See that rule's own comment for the full reasoning.) */

}

@media (max-width: 480px) {
  /* Phone tier for every .wsk-tab row, operator included. Icons stay
     hidden: five labels plus icons do not fit a phone row. --wsk-tab-font
     stays 13px, the floor the clerk's text-size zoom scales. This block
     never sets a smaller font. Wider than 480px, none of the rules below
     apply, so desktop keeps the base tab rule. */
  :root {
    --wsk-tab-pad-x: 6px;
    --wsk-tab-font: 13px;
  }
  .wsk-tab svg { display: none; }
  /* Workspace nav only. Each tab sizes to its own label and never below
     44px, and leftover row width is shared as extra tab width (padding
     around the centered label). 4px of padding on each side plus the 8px
     row gap is 16px between adjacent labels at that minimum; growth only
     increases it. When the labels plus that 16px cannot fit, the same
     minimum holds and the row scrolls, with no scrollbar. Edge fades are
     scrolling shadows: local cover gradients sit over scroll-attached
     shadows, so a fade paints only on an edge that has tabs out of view.
     The active tab is the row's initial scroll target, so no script has
     to scroll it into view. */
  #workspace-tabs {
    --wsk-tab-pad-x: 4px;
    --wsk-tab-gap: 0px;
    gap: 8px;
    padding: 6px 0 0;
    overflow-x: auto;
    scrollbar-width: none;
    background-image:
      linear-gradient(to right, var(--wsk-card), rgb(from var(--wsk-card) r g b / 0)),
      linear-gradient(to left, var(--wsk-card), rgb(from var(--wsk-card) r g b / 0)),
      linear-gradient(to right, rgb(0 0 0 / 0.55), rgb(0 0 0 / 0)),
      linear-gradient(to left, rgb(0 0 0 / 0.55), rgb(0 0 0 / 0));
    background-repeat: no-repeat;
    background-position: left center, right center, left center, right center;
    background-size: 20px 100%, 20px 100%, 16px 100%, 16px 100%;
    background-attachment: local, local, scroll, scroll;
  }
  #workspace-tabs::-webkit-scrollbar { display: none; width: 0; height: 0; }
  #workspace-tabs > a {
    flex: 1 0 auto;
    width: max-content;
    min-width: 44px;
  }
  #workspace-tabs .wsk-tab span {
    min-width: max-content;
    overflow: visible;
    text-overflow: clip;
  }
  #workspace-tabs > a.wsk-tab-active { scroll-initial-target: nearest; }
}
/* Engine task forms share the existing clerk card palette. */
.wsk-task-editor { display: grid; gap: 12px; margin-top: 16px; }
.wsk-task-editor label { display: grid; gap: 4px; min-width: 0; }
.wsk-task-editor input, .wsk-task-editor select, .wsk-task-editor textarea, .wsk-reply-editor {
  font: inherit; padding: 8px; border: 1px solid #cbd5e1; border-radius: 6px;
  max-width: 100%; box-sizing: border-box;
}
.wsk-reply-editor { width: 100%; min-height: 220px; resize: vertical; }
.wsk-task-editor textarea { width: 100%; resize: vertical; }
.wsk-task-description {
  overflow-wrap: anywhere; white-space: normal;
  margin: 6px 0; color: var(--wsk-text-secondary); font-size: 14px; line-height: 1.5;
}

/* Approved composer mocks: a reply must exist before there is an editor. */
.wsk-composer { line-height: 1.5; }
.wsk-composer-head {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 10px; margin-bottom: 16px;
}
.wsk-composer-head h2 { margin: 0; font-size: 20px; color: var(--wsk-heading); }
.wsk-composer-meta { margin: 0; color: var(--wsk-meta); font-size: 13px; }
.wsk-composer-empty {
  padding: 28px; border: 1px solid var(--wsk-border-light);
  border-radius: 8px; background: var(--wsk-bg);
}
.wsk-composer-empty h3 { margin: 0 0 8px; font-size: 18px; color: var(--wsk-heading); }
.wsk-composer-empty p {
  margin: 0 0 16px; max-width: 65ch; color: var(--wsk-text-secondary); font-size: 14px;
}
.wsk-composer-empty p:last-child { margin-bottom: 0; }
.wsk-composer-warming { padding-top: 16px; border-top: 1px solid var(--wsk-border-light); }
.wsk-composer-label {
  display: block; margin-bottom: 8px; font-size: 13px; color: var(--wsk-text-secondary);
}
.wsk-composer-actions { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 10px; margin-top: 18px; }
.wsk-composer-actions form { margin: 0; max-width: 100%; }
.wsk-composer-actions .wsk-composer-send { margin-left: auto; }
.wsk-composer-actions button:disabled {
  background: var(--wsk-bg); color: var(--wsk-meta); border-color: var(--wsk-border);
  cursor: not-allowed; transform: none;
}
.wsk-composer-note { margin-top: 10px; }
/* What already happened (workspace._completed): a sent reply, a stored draft,
   an attachment recorded. It reads as a confirmation, sits above any error
   beside it, and uses the same surface tokens as the task notice. */
.wsk-composer-done {
  margin: 0 0 12px; padding: 10px 14px; font-size: 14px; color: var(--wsk-heading);
  background: var(--wsk-bg); border: 1px solid var(--wsk-border-row); border-radius: 8px;
}
.wsk-composer-input-empty { margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--wsk-border-light); }
.wsk-composer-input-empty h3 { margin: 0 0 6px; font-size: 15px; }
.wsk-composer .wsk-qa { margin-top: 16px; padding: 16px; }
.wsk-composer .wsk-qa-title { font-size: 15px; }
.wsk-composer .wsk-qa-prompt { font-size: 14px; font-weight: 700; margin-bottom: 8px; }
.wsk-composer .wsk-qa-choice { align-items: flex-start; padding: 7px 0; }
@media (max-width: 600px) {
  .wsk-composer-empty { padding: 20px; }
  .wsk-composer-actions .wsk-composer-send { margin-left: 0; }
  .wsk-composer .wsk-rt-bar { flex-wrap: wrap; }
}

/* Engine task cards retain the demo hierarchy without inventing appointment times. */
.wsk-engine-task-meta { display:flex; align-items:center; flex-wrap:wrap; gap:10px; margin-bottom:12px; }
.wsk-engine-task-dates { margin-left:auto; font-size:12px; color:var(--wsk-text-secondary); }
.wsk-engine-task-snippet { margin:8px 0; font-size:15px; line-height:1.5; color:var(--wsk-text-secondary); overflow-wrap:anywhere; }
.wsk-task-help { font-size:13px; color:var(--wsk-text-secondary); line-height:1.4; }
.wsk-task-refresh { margin:0 0 14px; font-size:13px; color:var(--wsk-text-secondary); line-height:1.4; }
.wsk-engine-task-edit { margin-top:14px; }
.wsk-engine-task-evidence { margin-top:10px; font-size:13px; }
.wsk-engine-task-evidence > summary { cursor:pointer; color:var(--wsk-text-secondary); }
.wsk-engine-task-evidence blockquote { margin:8px 0; padding-left:12px; border-left:2px solid var(--wsk-border); white-space:pre-wrap; overflow-wrap:anywhere; }
.wsk-engine-task-edit > summary { cursor:pointer; font-size:14px; color:var(--accent); font-weight:700; }
#schedule-card .wsk-sched-title { font-size:15px; }
#schedule-card .wsk-schedule-date { font-size:12px; margin-top:10px; }
#schedule-card .wsk-schedule-date .wsk-task-action { padding:6px 10px; }
.wsk-schedule-day-heading { font-size:13px; margin:14px 0 6px; }
.wsk-schedule-all-day { font-size:12px; color:var(--wsk-text-secondary); margin:6px 0; }
#schedule-card .wsk-schedule-tasks { list-style:none; padding:0; margin:8px 0 24px; font-size:13px; }
#schedule-card .wsk-schedule-tasks li { border-left:3px solid var(--accent); background:var(--accent-light); padding:7px 9px; margin-bottom:8px; border-radius:4px; }
#schedule-card .wsk-schedule-tasks a { color:var(--wsk-text); font-weight:700; text-decoration:none; }
#schedule-card .wsk-schedule-tasks p { font-size:11px; }
#schedule-card .wsk-schedule-tasks .wsk-task-description { font-size: 12px; }
.wsk-sched-no-times { position:relative; margin:12px 8px; font-size:12px; color:var(--wsk-text-secondary); background:var(--wsk-card); }

/* Home spacing pass (2026-10-06): "+ Add task" is a bare row, no card, no empty
   panel, and gaps come from the --wsk-space-* scale in :root. The header row it
   sat under was replaced by the toolbar below (home top declutter, 2026-10-11). */
#tasks-card > .wsk-task-help { margin:0 0 var(--wsk-space-3); }
.wsk-add-task-row { margin:0; }
#tasks-card > .wsk-tasks-head { flex-wrap:wrap; }
#tasks-card > .wsk-tasks-head > .wsk-sort-select { margin-left:auto; min-width:140px; }
.wsk-engine-add { flex-shrink:0; }
.wsk-engine-add > summary { list-style:none; cursor:pointer; }
.wsk-engine-add > summary::-webkit-details-marker { display:none; }
.wsk-engine-add[open] { flex-basis:100%; }
.wsk-engine-add[open] > summary { width:fit-content; margin-left:auto; }
.wsk-engine-add .wsk-task-editor { padding:20px; border:1px solid var(--wsk-border); border-radius:8px; background:var(--wsk-card); }

/* ===== Home top declutter (owner order 2026-10-11) ================================
   "claude design to make this clutter more clear". The stack above the first task
   card was six lines: banner, greeting, date, a heading row (heading, tiny "Check
   now", member filter), a big "+ Add task" row, then "Your tasks" and its chips.
   Now: one toolbar holds everything the clerk can press, every control one height.
   Namespaced wsk-home-*; gaps from the --wsk-space-* scale (8 and 16 here).

   Narrow first (a phone, or the narrow column beside the side rail on a tablet):
   the member filter has a line to itself; "Check now" and "+ Add task" share the
   next line, half the width each, 44px tall. Wide (the toolbar itself 600px or
   more - a container query, so the narrow two-column tablet layout is handled by
   the width the toolbar really has, not the screen's): one row, the filter and
   "Check now" at the left, "+ Add task" at the right, 40px tall. A browser
   without container queries simply keeps the narrow layout.

   "+ Add task" stays a <details> with its form inside (taskboard.js's poll guard
   reads details.wsk-engine-add[open]). Only its <summary> is lifted into the row:
   it is positioned over the toolbar's corner while the form still opens in normal
   flow under the row, so the button never moves when the form opens. The summary
   rules are written .wsk-engine-add > .wsk-add-task-btn so they outrank the shared
   ".wsk-engine-add[open] > summary" rule (the email page's own disclosure). */
:root { --wsk-home-control-h: 44px; --wsk-home-add-w: 128px; }
.wsk-home-toolbar { position: relative; margin: 0 0 var(--wsk-space-4); container: home-toolbar / inline-size; }
/* One row, the pills and then the icon. Narrow: the pill tray takes whatever width the icon leaves and wraps
   its own pills inside it, so a long list never pushes the icon onto a line of its own. Wide: the tray is
   only as wide as its pills (see the container query). */
.wsk-home-tools {
  display: flex; flex-wrap: nowrap; align-items: center; gap: var(--wsk-space-2);
  min-height: var(--wsk-home-control-h);
}
.wsk-home-tools .wsk-task-filter { flex: 1 1 0; min-width: 0; }
.wsk-home-tools .wsk-task-refresh { display: inline-flex; flex: 0 0 auto; margin: 0; }
/* KAN-443 (1): "Whose tasks?" is a row of pills in a soft tray (a segmented control), the active one
   white with the accent colour and bold. The tray is the toolbar's one control height; every pill is
   4px less on each side, so the row keeps the 40px (44px on a touch screen) every control shares. */
.wsk-task-filter {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--wsk-space-1);
  box-sizing: border-box; max-width: 100%; min-height: var(--wsk-home-control-h);
  padding: var(--wsk-space-1); border-radius: 22px; background: var(--accent-light);
}
.wsk-task-pill {
  display: inline-flex; align-items: center; gap: 6px; box-sizing: border-box;
  min-height: calc(var(--wsk-home-control-h) - 2 * var(--wsk-space-1)); max-width: 100%;
  padding: 0 12px; border: 0; border-radius: 999px; background: transparent;
  color: var(--wsk-text-secondary); font: inherit; font-size: 14px; line-height: 1;
  text-decoration: none; white-space: nowrap; cursor: pointer; touch-action: manipulation;
}
.wsk-task-pill:hover { background: rgba(255, 255, 255, .65); color: var(--wsk-text); }
.wsk-task-pill:focus-visible { outline: 3px solid #0b57d0; outline-offset: 1px; }
.wsk-task-pill.wsk-task-pill-on {
  background: #fff; color: var(--accent); font-weight: 700; box-shadow: 0 1px 2px rgba(0, 0, 0, .08);
}
.wsk-task-pill-count { font-size: 12px; font-weight: 400; color: var(--wsk-meta); }
.wsk-task-pill-on .wsk-task-pill-count { color: var(--accent); }
.wsk-task-pill-name { min-width: 0; max-width: 11em; overflow: hidden; text-overflow: ellipsis; }
/* The Teammate menu is a native <details>: it opens and closes without JavaScript, and its list floats
   above the cards (no layout change). Closed, it takes no room. taskboard.js treats an open one as
   "busy" so the quiet 90-second refresh cannot swap it away. */
.wsk-viewer-more { position: relative; }
.wsk-viewer-more > summary { list-style: none; }
.wsk-viewer-more > summary::-webkit-details-marker { display: none; }
.wsk-viewer-menu {
  position: absolute; z-index: 20; top: calc(100% + var(--wsk-space-2)); left: 0;
  box-sizing: border-box; min-width: 200px; max-width: min(320px, calc(100vw - 32px)); max-height: 50vh; overflow-y: auto;
  margin: 0; padding: var(--wsk-space-1); list-style: none;
  background: var(--wsk-card); border: 1px solid var(--wsk-border); border-radius: 12px;
  box-shadow: 0 8px 24px rgba(38, 36, 29, .16);
}
.wsk-viewer-menu a {
  display: block; padding: 10px var(--wsk-space-3); border-radius: 8px; overflow: hidden;
  color: var(--wsk-text); font-size: 14px; text-decoration: none; text-overflow: ellipsis; white-space: nowrap;
}
.wsk-viewer-menu a:hover, .wsk-viewer-menu a:focus-visible { background: var(--accent-light); }
.wsk-viewer-menu a:focus-visible { outline: 3px solid #0b57d0; outline-offset: -3px; }
.wsk-viewer-menu a[aria-current="true"] { color: var(--accent); font-weight: 700; }
/* "Check now" is an icon button (KAN-443 (1)): the name and tooltip are on the link, the words are not
   drawn. Quiet at rest - the accent colour, no border or fill - a soft tint on hover, and a focus ring
   that is an outline, never a border. */
.wsk-home-tool {
  display: inline-flex; align-items: center; justify-content: center;
  box-sizing: border-box; width: var(--wsk-home-control-h); min-height: var(--wsk-home-control-h); padding: 0;
  border: 0; border-radius: 50%; background: transparent; color: var(--accent);
  text-decoration: none; cursor: pointer; touch-action: manipulation;
}
.wsk-home-tool:hover { background: var(--accent-light); color: var(--accent-hover); }
.wsk-home-tool:focus-visible { background: var(--accent-light); outline: 3px solid #0b57d0; outline-offset: 1px; }
/* "+ Add task" is a <details> (taskboard.js's poll guard reads details.wsk-engine-add[open]). Narrow - a
   phone, or the narrow column beside the side rail: the button is a full-width bar on the line under
   the filter row, and its form opens right under it. Wide (the toolbar itself 600px or more, a container
   query): the summary is lifted onto the filter's row at the right, so the button never moves when the
   form opens. The rules are written .wsk-engine-add > .wsk-add-task-btn so they outrank the shared
   ".wsk-engine-add[open] > summary" rule (the email page's own disclosure). */
.wsk-home-toolbar .wsk-engine-add > .wsk-add-task-btn {
  box-sizing: border-box; margin: var(--wsk-space-2) 0 0; width: 100%;
  min-height: var(--wsk-home-control-h);
  padding: 0 var(--wsk-space-4); justify-content: center; white-space: nowrap; line-height: 1;
}
.wsk-home-toolbar .wsk-engine-add .wsk-task-editor { margin-top: var(--wsk-space-4); }
/* KAN-443 (1), narrow (the toolbar under 600px - an 800px window has a 388px task column): the pills get the
   whole first row and Check now moves down beside the Add task bar, instead of sharing the pills' row. With the
   owner's real counts ("Everyone 114", "Unassigned 98") the four pills are about 400px at full size, so they
   are also tighter here (10px padding, 13px type, 2px apart) and fit one row in 388px; a phone (358px) still
   wraps them inside the tray. .wsk-home-tools becomes display: contents so its two children take part in the
   grid of .wsk-home-bar. Every control has a cell of its own and the cells never intersect (tests/test_home_polish.py
   checks the areas, layout_check.py asks the browser which element a mouse reaches at each control's centre): the
   tray is row 1, the Add task row is row 2 column 1, Check now is row 2 column 2. The Add task form still opens
   full width: it is below the icon (its top is under the bar, the icon is as tall as the bar), so a negative
   right margin lets it run on under the icon's column. Without container queries the flex layout above is used. */
@container home-toolbar (width < 600px) {
  .wsk-home-bar { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--wsk-space-2); align-items: start; }
  .wsk-home-tools { display: contents; }
  .wsk-home-tools .wsk-task-filter { grid-area: 1 / 1 / 2 / 3; }
  .wsk-home-tools .wsk-task-refresh { grid-area: 2 / 2; justify-self: end; }
  .wsk-home-bar .wsk-add-task-row { grid-area: 2 / 1; min-width: 0; }
  .wsk-home-toolbar .wsk-engine-add > .wsk-add-task-btn { margin: 0; }
  .wsk-home-bar .wsk-engine-add .wsk-task-editor { margin-right: calc(-1 * (var(--wsk-home-control-h) + var(--wsk-space-2))); }
  .wsk-task-filter { gap: 2px; }
  .wsk-task-pill { padding: 0 10px; font-size: 13px; }
  .wsk-task-pill-count { font-size: 11px; }
}
@container home-toolbar (min-width: 600px) {
  .wsk-home-tools {
    --wsk-home-control-h: 40px;
    padding-right: calc(var(--wsk-home-add-w) + var(--wsk-space-2));
  }
  .wsk-home-tools .wsk-task-filter { flex: 0 1 auto; }
  .wsk-home-toolbar .wsk-engine-add > .wsk-add-task-btn {
    --wsk-home-control-h: 40px; position: absolute; top: 0; right: 0; margin: 0; width: var(--wsk-home-add-w);
  }
}
/* A touch screen (a tablet in landscape has a wide toolbar) keeps 44px targets. After the
   container block so it wins at equal specificity. */
@media (pointer: coarse) {
  .wsk-home-tools, .wsk-home-toolbar .wsk-engine-add > .wsk-add-task-btn { --wsk-home-control-h: 44px; }
}
/* KAN-443 (2): "Nothing due today." is a sentence in a soft grey box, not a bare line (and not a deadline row). */
#schedule-card .wsk-schedule-tasks .wsk-schedule-empty { border: 0; border-radius: 10px; background: #f6f6f4; padding: 10px 12px; color: var(--wsk-meta); font-size: 14px; }
/* KAN-443 (2): ONE style for every event listed under the hour grid - the timed agenda, "Start time only",
   "Outside working hours", the overflow and all-day events: a coloured block in the same .tag-* category colour as
   the grid (an Outlook event takes the calendar green), the time in bold, then the title, then the source line.
   Text is dark on the block; only the time, the source line and the tag use the category colour (each pair is
   at least 4.5:1). The older agenda and all-day list styles were removed so the card has no second style. */
.wsk-event-list { list-style: none; margin: 0 0 12px; padding: 0; display: grid; gap: 8px; }
.wsk-event-block {
  display: flex; gap: 12px; align-items: flex-start; box-sizing: border-box; min-width: 0; margin: 0;
  padding: 10px 12px; border-left: 4px solid currentColor; border-radius: 10px;
}
.wsk-event-time { flex: none; max-width: 45%; font-size: 14px; font-weight: 700; line-height: 1.35; overflow-wrap: anywhere; }
/* Only a clock time is unbreakable. A long start - "Continues from previous day" - wraps inside the 45% column
   instead of running into the title. */
.wsk-event-time > span { display: block; }
.wsk-event-clock { white-space: nowrap; }
.wsk-event-time > .wsk-event-time-end { font-size: 12px; font-weight: 400; }
.wsk-event-body { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; min-width: 0; }
.wsk-event-title { color: var(--wsk-heading); font-size: 14px; line-height: 1.35; overflow-wrap: anywhere; text-decoration: none; }
a.wsk-event-title:hover { text-decoration: underline; }
a.wsk-event-title:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.wsk-event-line { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; }
.wsk-event-meta { font-size: 12px; line-height: 1.35; overflow-wrap: anywhere; }
.wsk-event-tag {
  display: inline-block; padding: 1px 8px; border-radius: 999px; background: rgba(255, 255, 255, .7);
  font-size: 11px; font-weight: 700; letter-spacing: .02em; line-height: 1.5; white-space: nowrap;
}

/* --- disclosure chevrons (acceptance review 2026-09-09) ----------------------
   "the native black disclosure triangle looks tacky." Every <details> whose
   summary still rendered the UA's own solid ▶/▼ marker now draws a thin
   chevron instead, in the summary's own currentColor so each one inherits
   the weight/tone its section already sets (secondary grey on Engine
   suggestion, accent bold on Edit task, and so on) rather than stamping one
   hard black glyph across surfaces that agree on nothing else.

   Drawn, not iconified: two borders on an empty 7px box rotated 45deg, the
   same "shape from borders" technique .wsk-sched-no-times and friends
   already use in this file. No SVG asset, no icon font, no dependency, and
   nothing for tests/test_css_coverage.py's dead-class scan to chase since
   the hook is a pseudo-element on existing markup - which is also why this
   is a CSS-only change: not one of the six templates below was touched, so
   their <details>/<summary> semantics (native keyboard toggling, Enter/
   Space, the [open] attribute, find-in-page expansion) are untouched by
   construction, and so is every bit of content inside them - Engine Review
   metadata and the task edit form included.

   The selector list is explicit, one entry per real disclosure, rather than
   a blanket `details > summary`. Summaries in this app that are NOT
   disclosures in the visual sense must stay chevron-free: the account
   avatar (.wsk-account-menu, an icon-only menu button) and "+ Add task"
   (.wsk-engine-add), plus the "Show more" link the task card carried until
   KAN-377. A blanket rule would have grown an unwanted chevron on each;
   list-style:none is already set individually on each of them for the same
   reason. The two upload disclosures are reached through their shared row
   (.wsk-upload-row details) because the "All N tags" one carries no class
   of its own in upload_list.html and this file cannot add one. */
.wsk-engine-task-evidence > summary,
.wsk-engine-task-edit > summary,
details.wsk-card > summary,
.wsk-upload-row details > summary,
.wsk-admin-group > summary,
.wsk-admin-more > summary,
.wsk-ask-sources-more > summary,
.wsk-srch-closest > summary,
.wsk-task-collapsed > summary,
.wsk-task-rest > summary,
.wsk-consent-details > summary,
.wsk-auth-disclosure > summary,
.wsk-email-task > .wsk-task-held > summary.wsk-task-help,
.wsk-mail-more > summary,
.wsk-notification-documents > summary,
.wsk-site-assets > summary,
.wsk-sp-more > summary,
.ws-invite-approval > summary,
.op-coverage > summary,
.wsk-sum-sources > summary,
.wsk-help-item > summary {
  display: flex; align-items: center; gap: 8px;
  list-style: none;                 /* Firefox's own marker */
}
.wsk-engine-task-evidence > summary::-webkit-details-marker,
.wsk-engine-task-edit > summary::-webkit-details-marker,
details.wsk-card > summary::-webkit-details-marker,
.wsk-upload-row details > summary::-webkit-details-marker,
.wsk-admin-group > summary::-webkit-details-marker,
.wsk-admin-more > summary::-webkit-details-marker,
.wsk-ask-sources-more > summary::-webkit-details-marker,
.wsk-srch-closest > summary::-webkit-details-marker,
.wsk-task-collapsed > summary::-webkit-details-marker,
.wsk-task-rest > summary::-webkit-details-marker,
.wsk-consent-details > summary::-webkit-details-marker,
.wsk-auth-disclosure > summary::-webkit-details-marker,
.wsk-email-task > .wsk-task-held > summary.wsk-task-help::-webkit-details-marker,
.wsk-mail-more > summary::-webkit-details-marker,
.wsk-notification-documents > summary::-webkit-details-marker,
.wsk-site-assets > summary::-webkit-details-marker,
.wsk-sp-more > summary::-webkit-details-marker,
.ws-invite-approval > summary::-webkit-details-marker,
.op-coverage > summary::-webkit-details-marker,
.wsk-sum-sources > summary::-webkit-details-marker,
.wsk-help-item > summary::-webkit-details-marker { display: none; }

/* Collapsed: the chevron points right (borders on the right/bottom edges of
   an empty box, rotated -45deg). translateY nudges the rotated square's
   optical centre back onto the text baseline - a rotated box's bounding box
   grows around its own centre, so flex centring alone sits it a shade low. */
.wsk-engine-task-evidence > summary::before,
.wsk-engine-task-edit > summary::before,
details.wsk-card > summary::before,
.wsk-upload-row details > summary::before,
.wsk-admin-group > summary::before,
.wsk-admin-more > summary::before,
.wsk-ask-sources-more > summary::before,
.wsk-srch-closest > summary::before,
.wsk-task-collapsed > summary::before,
.wsk-task-rest > summary::before,
.wsk-consent-details > summary::before,
.wsk-auth-disclosure > summary::before,
.wsk-email-task > .wsk-task-held > summary.wsk-task-help::before,
.wsk-mail-more > summary::before,
.wsk-notification-documents > summary::before,
.wsk-site-assets > summary::before,
.wsk-sp-more > summary::before,
.ws-invite-approval > summary::before,
.op-coverage > summary::before,
.wsk-sum-sources > summary::before,
.wsk-help-item > summary::before {
  content: ""; flex: 0 0 auto; box-sizing: border-box;
  width: 7px; height: 7px; margin-left: 1px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  opacity: .65;
  transform: rotate(-45deg) translateY(-1px);
  transition: transform .15s ease;
}
/* Expanded: same chevron, quarter-turn down. */
.wsk-engine-task-evidence[open] > summary::before,
.wsk-engine-task-edit[open] > summary::before,
details.wsk-card[open] > summary::before,
.wsk-upload-row details[open] > summary::before,
.wsk-admin-group[open] > summary::before,
.wsk-admin-more[open] > summary::before,
.wsk-ask-sources-more[open] > summary::before,
.wsk-srch-closest[open] > summary::before,
.wsk-task-collapsed[open] > summary::before,
.wsk-task-rest[open] > summary::before,
.wsk-consent-details[open] > summary::before,
.wsk-auth-disclosure[open] > summary::before,
.wsk-email-task > .wsk-task-held[open] > summary.wsk-task-help::before,
.wsk-mail-more[open] > summary::before,
.wsk-notification-documents[open] > summary::before,
.wsk-site-assets[open] > summary::before,
.wsk-sp-more[open] > summary::before,
.ws-invite-approval[open] > summary::before,
.op-coverage[open] > summary::before,
.wsk-sum-sources[open] > summary::before,
.wsk-help-item[open] > summary::before {
  transform: rotate(45deg) translate(-1px, -1px);
}

@media (prefers-reduced-motion: reduce) {
  /* Kept beside the rule it silences rather than in this file's other
     prefers-reduced-motion block near the top: that one lists whole
     COMPONENT classes, and these are pseudo-elements of a selector list
     that only means anything alongside the rules above. Same kill-switch,
     same effect - the chevron still flips, it just doesn't animate. */
  .wsk-engine-task-evidence > summary::before,
  .wsk-engine-task-edit > summary::before,
  details.wsk-card > summary::before,
  .wsk-upload-row details > summary::before,
  .wsk-admin-group > summary::before,
  .wsk-admin-more > summary::before,
  .wsk-ask-sources-more > summary::before,
  .wsk-srch-closest > summary::before,
  .wsk-task-collapsed > summary::before,
  .wsk-task-rest > summary::before,
  .wsk-consent-details > summary::before,
  .wsk-auth-disclosure > summary::before,
  .wsk-email-task > .wsk-task-held > summary.wsk-task-help::before,
  .wsk-sp-more > summary::before,
.wsk-mail-more > summary::before,
.wsk-notification-documents > summary::before,
    .ws-invite-approval > summary::before,
    .op-coverage > summary::before,
    .wsk-sum-sources > summary::before,
    .wsk-help-item > summary::before { transition: none; }
}

/* KAN-232: one-click Complete/Hide beside Review/Suggestion (a standalone
   form inside the actions row), the one-response status line with its Undo
   link, and the collapsed hidden-task list with per-row Restore. */
.wsk-task-quick { display: flex; flex: 1; align-items: center; gap: 10px; margin: 0; }
/* KAN-254: the engine card follows the demo reference. Top row = chip and
   the Received/Due meta with the calendar icon (the popover's target);
   the title is the review link; Hide sits at the far right of the quick
   form. Due and the icon use the file's one established danger red. */
.wsk-task-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.wsk-task-top .wsk-task-meta { margin: 0 0 0 auto; }
/* KAN-443 (17): the new date line (a pill, then "Due Sep 28 · Received Sep 29", then the calendar button) is
   wider than the old one, and a card in the narrow column is narrow at ANY screen width. So the row wraps
   whenever it must - the category chip keeps its own width and the date line drops under it - instead of
   squeezing the chip to nothing. */
.wsk-task-top { flex-wrap: wrap; row-gap: 8px; }
.wsk-task-top .wsk-tag { flex: 0 0 auto; }
.wsk-task-top .wsk-task-meta { flex: 0 1 auto; flex-wrap: wrap; row-gap: 4px; }
@media (max-width: 700px) {
  .wsk-task-top { flex-wrap: wrap; row-gap: 8px; }
  .wsk-task-top .wsk-tag { flex: 0 0 auto; }
  .wsk-task-top .wsk-task-meta { flex: 0 0 auto; }
}
/* KAN-443 (17): the card's date line is a red "Overdue 25 days" pill (only when overdue), then
   "Due Sep 28 · Received Sep 29" in muted text, then the calendar button. The pill's own rule is
   further down, after the shared .wsk-task-overdue base it refines. */
.wsk-task-dates { font-size: 14px; color: var(--wsk-text-secondary); white-space: nowrap; }
/* The dates and the calendar button are one unit (they wrap together, never the button alone). */
.wsk-task-when { display: inline-flex; align-items: center; gap: 4px; }
.wsk-task-when .wsk-task-edit { margin-left: 0; }
.wsk-task-edit {
  display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px;
  margin-left: 4px; padding: 0; border: none; border-radius: 6px; background: transparent;
  color: var(--wsk-text-secondary); cursor: pointer;
}
.wsk-task-edit:hover, .wsk-task-edit:focus-visible { background: var(--accent-light); }
.wsk-task-title-link { color: inherit; text-decoration: none; }
.wsk-task-title-link:hover, .wsk-task-title-link:focus-visible { text-decoration: underline; }
.wsk-task-line { margin: 0; font-size: 14px; color: var(--wsk-text-secondary); }
/* KAN-500: the engine's SUGGESTED due date / priority is marked in the same muted voice as the
   date line, so it never reads as something the email said. The note says why, in one line. */
.wsk-task-suggested-flag, .wsk-task-suggested-tag { font-weight: 400; font-size: 12px; }
.wsk-task-suggested-note { font-size: 13px; line-height: 1.4; margin-bottom: 6px; }
/* "Suggested date passed (Sep 22) . Received Sep 18" is longer than a phone card is wide, so a suggested
   date line may wrap (the calendar button stays beside it) instead of pushing the button off the card. */
.wsk-task-dates.wsk-task-dates-suggested { white-space: normal; text-align: right; }
.wsk-task-when { min-width: 0; }
/* KAN-377: who the mail is from, and what they asked (the quote). */
.wsk-task-from, .wsk-task-why {
  margin: 0 0 4px; font-size: 14px; line-height: 1.5; color: var(--wsk-text-secondary);
  overflow-wrap: anywhere;
}
.wsk-task-why { font-style: italic; }
/* KAN-443 (18): "Draft a reply" is the outline button (.wsk-task-action-secondary) with a pen icon, the same
   height as Mark done. It sets no font-size of its own, so it keeps the shared 14px. */
.wsk-task-suggest { white-space: nowrap; }
.wsk-task-suggest svg { flex: none; }
.wsk-task-hide { margin-left: auto; }
.wsk-task-notice {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0;
  padding: 10px 14px; font-size: 14px; color: var(--wsk-text-secondary);
  background: var(--wsk-bg); border: 1px solid var(--wsk-border-row); border-radius: 8px;
}
.wsk-task-undo { margin: 0; }
.wsk-task-link {
  background: none; border: none; padding: 0; font: inherit; font-weight: 700;
  color: var(--accent); text-decoration: underline; cursor: pointer;
}
.wsk-task-history { margin-top: 16px; }
.wsk-task-history-list { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 10px; }
/* Each completed or hidden task is a card of its own, like the open ones above (it was bare text on the page's card). */
.wsk-task-history-list li { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; padding: 12px 16px; background: var(--wsk-card); border: 1px solid var(--wsk-border); border-radius: 12px; }
.wsk-task-restore { margin: 0; }
.wsk-task-history-title { flex: 1 1 200px; min-width: 0; overflow-wrap: anywhere; }
.wsk-task-history-state { font-size: 13px; color: var(--wsk-meta); white-space: nowrap; }

/* KAN-234: the task editor lives in a native popover. [popover] elements
   render in the top layer (no z-index games) and the platform supplies Esc
   and light-dismiss; these rules make it read as a modal above the card:
   centred, card surface, a dimmed backdrop, and visible focus both on the
   popover itself and on the fields inside it. */
.wsk-task-popover {
  width: min(560px, calc(100vw - 32px)); max-height: calc(100vh - 32px); overflow: auto;
  margin: auto; padding: 0; box-sizing: border-box;
  background: var(--wsk-card); color: var(--wsk-text);
  border: 1px solid var(--wsk-border); border-radius: 14px;
  box-shadow: 0 12px 40px rgba(38, 36, 29, .22);
}
.wsk-task-popover::backdrop { background: rgba(38, 36, 29, .45); }
.wsk-task-popover:focus-visible { outline: 3px solid #0b57d0; outline-offset: -3px; }
.wsk-task-popover input:focus-visible, .wsk-task-popover select:focus-visible {
  border-color: var(--accent); outline-offset: 1px;
}
.wsk-task-popover-title { margin: 0; font-size: 17px; font-weight: 700; color: var(--wsk-heading); }
.wsk-task-popover .wsk-task-editor { margin-top: 0; }

/* KAN-494: the larger, plainer Edit task dialog. Older audience: 16px text, bold labels, 48px fields and 44px or
   larger targets. Scoped to .wsk-te (the Edit task form) so the Add task form keeps its own look. The script-only
   pieces (.wsk-te-js: counter, date shortcuts, date in words) stay hidden until taskeditor.js puts .wsk-js on
   <html>, so the form without script is the plain labels, date input, radios and select. */
.wsk-task-editor[data-te] { gap: 0; }
.wsk-te-head { display: flex; align-items: flex-start; gap: 12px; padding: 20px 20px 0 24px; }
.wsk-te-head-text { flex: 1; min-width: 0; padding-top: 6px; }
.wsk-te-head .wsk-task-popover-title { font-size: 22px; line-height: 1.25; }
.wsk-te-source { margin: 4px 0 0; font-size: 16px; line-height: 1.4; color: var(--wsk-text-secondary); overflow-wrap: anywhere; }
.wsk-te-close {
  flex: none; width: 44px; height: 44px; display: inline-flex; align-items: center; justify-content: center;
  padding: 0; border: 1px solid transparent; border-radius: 10px; background: transparent;
  color: var(--wsk-text-secondary); cursor: pointer;
}
.wsk-te-close:hover { background: var(--accent-light); color: var(--accent); }
.wsk-te-close:focus-visible { outline: 3px solid #0b57d0; outline-offset: 2px; }
.wsk-te-body { display: grid; gap: 20px; min-width: 0; }
.wsk-task-popover .wsk-te-body { padding: 20px 24px 0; }
.wsk-te-field { display: grid; gap: 6px; min-width: 0; border: 0; margin: 0; padding: 0; }
.wsk-task-editor[data-te] .wsk-te-label { display: block; padding: 0; font-size: 16px; font-weight: 700; color: var(--wsk-heading); }
.wsk-te-due, .wsk-te-prio { min-width: 0; }
.wsk-te-p { display: block; }
.wsk-te-label-row { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.wsk-te-hint { font-size: 14px; font-weight: 400; color: var(--wsk-meta); }
.wsk-te-count { font-variant-numeric: tabular-nums; }
.wsk-te-count.is-near { color: #7a5b12; font-weight: 700; }
.wsk-te-js { display: none; }
.wsk-js .wsk-te-chips { display: flex; }
.wsk-js .wsk-te-date-words { display: block; }
.wsk-task-editor[data-te] input:not([type=radio]), .wsk-task-editor[data-te] textarea {
  font-size: 16px; line-height: 1.4; min-height: 48px; padding: 10px 12px; border-radius: 10px; background-color: #fff;
}
.wsk-task-editor[data-te] textarea { min-height: 96px; }
[data-te] .wsk-te-assign select:not([multiple]):not([size]) {
  height: 48px; min-height: 48px; font-size: 16px; padding-left: 12px; border-radius: 10px; width: 100%;
}
[data-te] input:hover:not([type=radio]), [data-te] textarea:hover { border-color: var(--wsk-icon); }
[data-te] input:focus-visible, [data-te] textarea:focus-visible, [data-te] select:focus-visible {
  outline: 3px solid #0b57d0; outline-offset: 1px; border-color: var(--accent); box-shadow: none;
}
.wsk-te-chips { flex-wrap: wrap; gap: 8px; }
.wsk-te-chip {
  min-height: 44px; padding: 0 16px; border: 1px solid var(--wsk-border); border-radius: 999px; background: #fff;
  color: var(--wsk-text); font: inherit; font-size: 16px; font-weight: 700; cursor: pointer;
}
.wsk-te-chip:hover { border-color: var(--accent); }
.wsk-te-chip[aria-pressed=true] { background: var(--accent-light); border-color: var(--accent); color: var(--accent); }
.wsk-te-chip:focus-visible { outline: 3px solid #0b57d0; outline-offset: 2px; }
.wsk-te-date-row { display: grid; grid-template-columns: minmax(150px, 190px) 1fr; gap: 12px; align-items: center; }
.wsk-te-date-words { margin: 0; font-size: 16px; font-weight: 700; color: var(--wsk-heading); line-height: 1.3; }
.wsk-te-date-words small { display: block; font-size: 14px; font-weight: 400; color: var(--wsk-meta); }
.wsk-te-seg { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }
.wsk-task-editor[data-te] .wsk-te-seg label { position: relative; display: block; margin: 0; }
.wsk-task-editor[data-te] .wsk-te-seg input {
  position: absolute; inset: 0; opacity: 0; margin: 0; padding: 0; width: 100%; height: 100%; min-height: 0; border: 0; cursor: pointer;
}
.wsk-te-seg span {
  display: flex; align-items: center; justify-content: center; gap: 6px; min-height: 48px; padding: 0 8px; box-sizing: border-box;
  border: 1px solid var(--wsk-border); border-radius: 10px; background: #fff; color: var(--wsk-text); font-size: 16px; font-weight: 700;
}
.wsk-te-seg span svg { display: none; flex: none; }
.wsk-te-seg input:hover + span { border-color: var(--wsk-icon); }
.wsk-te-seg input:checked + span svg { display: block; }
.wsk-te-seg input:checked + span { border-width: 2px; padding: 0 7px; }
.wsk-te-p-urgent input:checked + span { background: #f4dede; color: #a33e3e; border-color: #a33e3e; }
.wsk-te-p-high input:checked + span { background: var(--wsk-cream); color: #7a5b12; border-color: #7a5b12; }
.wsk-te-p-normal input:checked + span { background: #e8eef4; color: #2f4a63; border-color: #2f4a63; }
.wsk-te-p-low input:checked + span { background: #fff; color: var(--wsk-text-secondary); border-color: var(--wsk-text-secondary); border-style: dotted; }
.wsk-te-seg input:focus-visible + span { outline: 3px solid #0b57d0; outline-offset: 2px; }
.wsk-te-assign { display: grid; grid-template-columns: 44px 1fr; gap: 12px; align-items: center; }
.wsk-te-avatar {
  width: 44px; height: 44px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent-light); color: var(--accent); font-weight: 700; font-size: 16px; border: 1px solid var(--accent); box-sizing: border-box;
}
.wsk-te-avatar.is-none { background: #fff; border-style: dashed; color: var(--wsk-meta); border-color: var(--wsk-meta); }
.wsk-te-foot { margin-top: 20px; gap: 12px; }
.wsk-task-popover .wsk-te-foot { position: sticky; bottom: 0; padding: 16px 24px; margin-top: 24px; background: var(--wsk-card); border-top: 1px solid var(--wsk-border-light); border-radius: 0 0 14px 14px; }
.wsk-te-foot .wsk-task-action, .wsk-te-foot .wsk-task-action-secondary { min-height: 48px; font-size: 16px; padding: 0 22px; border-radius: 10px; }
.wsk-te-foot button:focus-visible { outline: 3px solid #0b57d0; outline-offset: 2px; }
@media (max-width: 480px) {
  .wsk-te-head { padding: 12px 8px 0 16px; }
  .wsk-task-popover .wsk-te-body { padding: 16px 16px 0; }
  .wsk-te-body { gap: 18px; }
  .wsk-te-seg { grid-template-columns: repeat(2, 1fr); }
  .wsk-te-date-row { grid-template-columns: 1fr; }
  .wsk-task-popover .wsk-te-foot { margin-top: 20px; padding: 12px 16px; }
  .wsk-te-foot button { flex: 1; padding: 0 12px; }
}

/* --- reply composer toolbar (owner feedback 2026-09-14) -------------------
   The bare textarea became a real editor. These style the surface that
   hub/static/replyeditor.js puts in its place; the textarea itself is hidden
   behind .wsk-rt-source but still present and still what the form posts, so
   with no JS the clerk sees exactly the editor they had before. */
.wsk-rt { border: 1px solid var(--wsk-border); border-radius: 8px; overflow: hidden; background: #fff; }
.wsk-rt-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 0; padding: 4px; border-bottom: 1px solid var(--wsk-border-light); background: var(--wsk-cream); }
.wsk-rt-group { display: inline-flex; flex: 0 0 auto; flex-wrap: nowrap; max-width: 100%; align-items: center; }
.wsk-rt-group + .wsk-rt-group { border-left: 1px solid var(--wsk-border); margin-left: 4px; padding-left: 4px; }
.wsk-rt-btn {
  min-width: 24px; height: 26px; padding: 0 3px; border: 1px solid transparent;
  border-radius: 5px; background: transparent; color: var(--wsk-text-secondary);
  font-size: 13px; line-height: 1; cursor: pointer;
}
.wsk-rt-btn:hover { background-color: #fff; border-color: var(--wsk-border); color: var(--wsk-heading); }
.wsk-rt-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.wsk-rt-btn[aria-pressed="true"] { background-color: #e2defa; border-color: var(--accent); color: var(--wsk-heading); }
.wsk-rt-choice { display: inline-flex; align-items: center; margin: 0; font-size: 12px; }
.wsk-rt-choice select { width: 70px; height: 26px; font: inherit; color: var(--wsk-heading); background: #fff; border: 1px solid var(--wsk-border); border-radius: 4px; padding: 2px; }
.wsk-rt-choice select[aria-label="Font"] { width: 145px; }
.wsk-rt-choice select[aria-label="Block style"] { width: 100px; }
.wsk-rt-choice select[aria-label="Text size"] { width: 72px; }
.wsk-rt-choice select[aria-label="Line spacing"] { width: 125px; }
.wsk-rt-choice select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Fixed, CSP-safe editor vocabulary; reply_markup.to_email emits equivalent
   allowlisted CSS for mail clients. No inline style allowance is needed. */
.wsk-rt-surface a[href] { text-decoration: underline; }
.wsk-rt-surface a[href]::after { content: "(" attr(data-destination) ")"; display: inline-block; margin-left: .3em; max-width: 100%; color: var(--wsk-text-secondary); font-size: 12px; text-decoration: none; overflow-wrap: anywhere; }
.wsk-rt-surface font[face] { font-family: inherit; }
.wsk-rt-surface font[face="Arial"] { font-family: Arial; }
.wsk-rt-surface font[face="Georgia"] { font-family: Georgia; }
.wsk-rt-surface font[face="Times New Roman"] { font-family: "Times New Roman"; }
.wsk-rt-surface font[face="Verdana"] { font-family: Verdana; }
.wsk-rt-surface font[face="Courier New"] { font-family: "Courier New"; }
.wsk-rt-surface [data-highlight="yellow"] { background-color: #fef08a; }
.wsk-rt-surface h2 { font-size: 24px; margin: 0 0 10px; }
.wsk-rt-surface h3 { font-size: 18px; margin: 0 0 10px; }
.wsk-rt-surface p[data-line-height="1"] { line-height: 1; }
.wsk-rt-surface p[data-line-height="1.15"] { line-height: 1.15; }
.wsk-rt-surface p[data-line-height="1.5"] { line-height: 1.5; }
.wsk-rt-surface p[data-line-height="2"] { line-height: 2; }
.wsk-rt-surface font[size="2"] { font-size: 12px; }
.wsk-rt-surface font[size="3"] { font-size: 14px; }
.wsk-rt-surface font[size="5"] { font-size: 18px; }
.wsk-rt-surface font[face="wsk-hl-yellow"] { background-color: #fef08a; }
.wsk-rt-surface [align="left"] { text-align: left; }
.wsk-rt-surface [align="center"] { text-align: center; }
.wsk-rt-surface [align="right"] { text-align: right; }
.wsk-rt-surface hr { border: 0; border-top: 1px solid #767676; margin: 12px 0; }
.wsk-rt-bold { font-weight: 800; }
.wsk-rt-italic { font-style: italic; }
.wsk-rt-underline { text-decoration: underline; }
.wsk-rt-strike { text-decoration: line-through; }
/* A hairline between toolbar groups, the way a compose window separates
   character formatting from lists from history. Decorative only - it carries
   aria-hidden, so it is not announced as a control. */
.wsk-rt-highlight { position: relative; }
.wsk-rt-highlight::after { content: ""; position: absolute; left: 4px; right: 4px; bottom: 2px; height: 4px; background: #fef08a; }
.wsk-rt-align { background-image: linear-gradient(currentColor, currentColor), linear-gradient(currentColor, currentColor), linear-gradient(currentColor, currentColor); background-size: 16px 2px, 10px 2px, 16px 2px; background-repeat: no-repeat; }
.wsk-rt-left { background-position: left 3px top 7px, left 3px top 12px, left 3px top 17px; }
.wsk-rt-center { background-position: center top 7px, center top 12px, center top 17px; }
.wsk-rt-right { background-position: right 3px top 7px, right 3px top 12px, right 3px top 17px; }
@media (max-width: 900px) {
  .wsk-rt-choice { font-size: 11px; }
  .wsk-rt-btn { min-width: 20px; padding: 0 2px; }
}
@media (max-width: 360px) {
  .wsk-rt-btn { min-width: 18px; padding: 0 1px; }
  .wsk-rt-group + .wsk-rt-group { margin-left: 0; padding-left: 2px; }
}
/* Quote and indent both land as <blockquote> once the style attribute is
   stripped, so it has to READ as a quote rather than as a stray paragraph. */
.wsk-rt-surface blockquote {
  margin: 0 0 10px; padding: 2px 0 2px 12px;
  border-left: 3px solid var(--wsk-border); color: var(--wsk-text-secondary);
}
.wsk-rt-surface { min-height: 190px; padding: 10px 12px; font-size: 14px; line-height: 1.5; color: var(--wsk-heading); overflow-y: auto; }
.wsk-rt-surface:focus { outline: none; }
.wsk-rt-surface p { margin: 0 0 10px; line-height: normal; }
.wsk-rt-surface ul, .wsk-rt-surface ol { margin: 0 0 10px; padding-left: 22px; }
/* The real input, kept in the DOM and kept submitting - just not shown once
   the surface above is standing in for it. */
.wsk-rt-source { display: none; }

/* --- EL-BOT's questions (owner feedback 2026-09-14) -----------------------
   The drafter asks rather than inventing; this is where the clerk answers.
   Quiet by design - it sits under the reply, and it is a prompt, not an
   alarm. */
.wsk-qa { margin: 12px 0 0; padding: 12px 14px; border: 1px solid var(--wsk-border-light); border-radius: 8px; background: var(--wsk-cream); }
.wsk-qa-title { margin: 0 0 8px; font-size: 13px; font-weight: 700; color: var(--wsk-heading); }
.wsk-qa-lead { margin: 0 0 8px; font-size: 13px; color: var(--wsk-text-secondary); }
.wsk-qa-item { display: block; margin: 0 0 8px; }
.wsk-qa-prompt { display: block; margin: 0 0 3px; font-size: 13px; color: var(--wsk-text-secondary); }
.wsk-qa-input { width: 100%; box-sizing: border-box; }
.wsk-qa-note { margin: 6px 0 0; font-size: 12px; color: var(--wsk-icon); }

/* Multiple choice on an open question (owner ask 2026-09-14). Options are
   phrases found in the record, so each shows its provenance under the value -
   the clerk checks rather than trusts. The free-text box is always present
   and is the default when the record offers nothing. */
.wsk-qa-item { border: 0; margin: 0 0 10px; padding: 0; }
.wsk-qa-choice { display: flex; align-items: baseline; gap: 8px; padding: 3px 0; cursor: pointer; }
.wsk-qa-choice-value { font-size: 14px; color: var(--wsk-heading); }
.wsk-qa-choice-source { font-size: 12px; color: var(--wsk-icon); }
.wsk-qa-other { display: block; margin-top: 6px; }
.wsk-qa-other-label { display: block; margin-bottom: 3px; font-size: 12px; color: var(--wsk-text-secondary); }
.wsk-qa-lead { margin: 0 0 8px; font-size: 13px; color: var(--wsk-text-secondary); }

/* --- interactive values in the reply (owner ask 2026-09-14) ---------------
   An uncertain spot is a filled-in token, not a hole: it carries the best
   guess from the record so the letter reads as finished, and it is marked so
   nobody mistakes a guess for a confirmed fact. Clicking one opens its
   question in "Needs your input". */
.wsk-var {
  border-radius: 4px; padding: 0 3px; cursor: pointer;
  background: #eceafb; box-shadow: inset 0 -1px 0 var(--accent);
}
.wsk-var:hover { background: #e2defa; }
/* Still carrying the question rather than an answer from the record - the one
   the clerk most needs to look at. */
.wsk-var[data-unanswered="1"] { background: #fdf1d8; box-shadow: inset 0 -1px 0 #c08a2e; font-style: italic; }
.wsk-qa-input-active { outline: 2px solid var(--accent); outline-offset: 1px; }

/* --- reply attachments (owner ask 2026-09-14) -----------------------------
   A RULE AND A HEADING, not a nested card: the walkdown screenshots put one
   card round the whole reply panel and nothing inside it, so a bordered box
   here would read as a second card the demo never has. The heading borrows
   the panel's own small-caps idiom instead. Suggestions carry their
   provenance the same way the answer choices do. */
.wsk-att { margin: 14px 0 0; padding: 12px 0 0; border-top: 1px solid var(--wsk-border-light); }
.wsk-att-title { margin: 0 0 8px; font-size: 12px; font-weight: 700; letter-spacing: .08em;
                 text-transform: uppercase; color: var(--wsk-text-secondary); }
.wsk-att-lead { margin: 8px 0 4px; font-size: 12px; color: var(--wsk-text-secondary); }
.wsk-att-suggest, .wsk-att-list { margin: 0; padding: 0; list-style: none; }
.wsk-att-chip, .wsk-att-row { display: flex; align-items: center; gap: 8px; padding: 4px 0; flex-wrap: wrap; }
.wsk-att-name { font-size: 14px; color: var(--wsk-heading); }
.wsk-att-source { font-size: 12px; color: var(--wsk-icon); }
.wsk-att-done { font-size: 12px; font-weight: 700; color: var(--wsk-text-secondary); }
.wsk-att-btn { padding: 2px 10px; font-size: 12px; }
.wsk-att-note { margin: 8px 0 0; font-size: 12px; color: var(--wsk-icon); }
.wsk-att-slot { margin-top: 8px; }
.wsk-att-slot-note { font-size: 12px; color: var(--wsk-icon); }

/* Regenerate, and the box that says what to change (owner ask 2026-09-14). They live
   in the reply card's "More reply options" popover now (see the reply footer block at
   the end of this file); these are the box's own looks. */
.wsk-regen { display: flex; flex-direction: column; align-items: stretch; gap: var(--wsk-space-2); }
.wsk-steer { min-width: 0; }
.wsk-steer label { display: block; margin: 0 0 4px; font-size: 12px; color: var(--wsk-meta); }
.wsk-steer-box {
  width: 100%; box-sizing: border-box; padding: 8px; font: inherit; font-size: 13px;
  border: 1px solid var(--wsk-border-light); border-radius: 6px; resize: vertical;
}
.wsk-steer-note { margin: 4px 0 0; font-size: 12px; color: var(--wsk-meta); }

/* ---------------------------------------------------------------------------
   KAN-287 individuality / KAN-288 search honesty (approved mocks v2,
   .superpowers/sdd/2026-09-14-bowtie/gaps-individuality/). Tokens only -
   no new hex values. */

/* The persistent clerk chip (shell_top.html): a second shell row under the
   wordmark/search line - initials in the same accent-light tint as the
   account avatar, name, then role · town in secondary text. */
.wsk-clerk-chip-row { padding: 8px 40px 0; }
.wsk-clerk-chip {
  display: inline-flex; align-items: center; gap: 10px; color: inherit; text-decoration: none;
  padding: 4px 12px 4px 4px; border: 1px solid var(--wsk-border-light);
  border-radius: 999px; background: var(--wsk-card); font-size: 13px;
}
.wsk-clerk-chip-avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--accent-light); color: var(--accent);
  font-size: 11px; font-weight: 700; letter-spacing: .02em;
}
.wsk-clerk-chip-who { font-weight: 600; color: var(--wsk-text); }
.wsk-clerk-chip-role { color: var(--wsk-text-secondary); }

/* Home's Recent activity list and Ask's recent-Ask list. */
.wsk-recent-list { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 8px; }
.wsk-recent-item { display: flex; flex-direction: column; gap: 2px; font-size: 14px; }
.wsk-recent-item a { color: var(--wsk-text); text-decoration: none; }
.wsk-recent-item a:hover { text-decoration: underline; }
.wsk-recent-when { font-size: 12px; color: var(--wsk-meta); }
.wsk-recent-item a { overflow-wrap: anywhere; }
.wsk-recent-item a[data-activity-open] { color: var(--accent); margin: 0 -8px; padding: 6px 8px; border-radius: 6px; }
.wsk-recent-item a[data-activity-open]:hover,
.wsk-recent-item a[data-activity-open]:focus-visible { background: var(--accent-light); }
.wsk-recent-item a:focus-visible,
.wsk-shell-panel [data-shell-close]:focus-visible { outline: 2px solid var(--wsk-text); outline-offset: 3px; box-shadow: none; }
/* KAN-443 (12): the "what to do today" card under the greeting - one sentence and a few chips that link to
   the right view. The rule-based sentence carries no AI marker; the AI sentence that can replace it
   (KAN-445, HUB_DAY_BRIEF_AI) carries its own "AI summary" label, below. The wrapper is always in the page
   (an out-of-band swap target); empty, it takes no room in the column's flex gap.
   KAN-445 (the owner, 2026-10-24: "use different color"): the card is a soft teal-green, no longer the
   accent's lavender, so it reads apart from the board and the other cards. The text keeps its colours:
   body text 14.2:1, secondary text 7.4:1 and the red chip's text 7.6:1 on this green (WCAG AA is 4.5:1).
   The page has no dark theme, so there is no second set of values. */
#day-brief:empty { display: none; }
.wsk-day-brief { box-sizing: border-box; padding: 14px var(--wsk-space-4); border: 1px solid #cfe7df; border-radius: 14px; background: #eef7f4; }
.wsk-day-brief-text { margin: 0 0 10px; font-size: 15px; line-height: 1.5; color: var(--wsk-text); overflow-wrap: anywhere; }
.wsk-day-brief-text:last-child { margin-bottom: 0; }
.wsk-day-brief-lead { color: inherit; text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; }
.wsk-day-brief-lead:hover { color: var(--accent); }
.wsk-day-brief-lead:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.wsk-day-brief-chips { display: flex; flex-wrap: wrap; gap: var(--wsk-space-2); margin: 0; padding: 0; list-style: none; }
.wsk-day-brief-chip {
  display: inline-flex; align-items: center; min-height: 30px; box-sizing: border-box; padding: 0 12px;
  border: 1px solid var(--wsk-border-light); border-radius: 999px; background: #fff;
  color: var(--wsk-heading); font-size: 13px; font-weight: 700; line-height: 1; text-decoration: none;
}
.wsk-day-brief-chip:hover { border-color: var(--accent); color: var(--accent); }
.wsk-day-brief-chip:focus-visible { outline: 3px solid #0b57d0; outline-offset: 1px; }
.wsk-day-brief-chip.wsk-day-brief-chip-red { border-color: #f0caca; background: #fff7f7; color: #8f2a2a; }
.wsk-day-brief-chip.wsk-day-brief-chip-red:hover { border-color: #8f2a2a; }
/* KAN-445: the AI-written summary that replaces the card's sentence (fragments/day_brief_ai.html). The label
   is the Search summary's own: a round icon, then small bold capitals (.wsk-sum-icon / .wsk-sum-title),
   here in the card's teal (white on #1f7a63 is 5.2:1; #17604d on the card is 6.8:1). The rule-based lead
   sentence stays under the summary, quieter. */
.wsk-day-brief-ai { margin: 0 0 10px; }
.wsk-day-brief-ai:last-child { margin-bottom: 0; }
.wsk-day-brief-ai-head { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; margin: 0 0 6px; }
.wsk-day-brief-ai-icon { flex: none; width: 22px; height: 22px; border-radius: 11px; background: #1f7a63; color: #fff; display: flex; align-items: center; justify-content: center; }
.wsk-day-brief-ai-label { font-size: 13px; letter-spacing: 0.08em; text-transform: uppercase; font-weight: 700; color: #17604d; white-space: nowrap; }
.wsk-day-brief-ai-note { margin-left: auto; font-size: 13px; line-height: 1.4; color: var(--wsk-text-secondary); }
.wsk-day-brief-ai .wsk-day-brief-text { margin: 0 0 6px; }
.wsk-day-brief-ai .wsk-day-brief-text:last-child { margin-bottom: 0; }
.wsk-day-brief-ai .wsk-day-brief-start { font-size: 14px; color: var(--wsk-text-secondary); }
/* KAN-443 (3): Recent activity - sentence-case heading, a small day heading above each day's rows, a tinted
   icon per kind, the name on one line (cut with an ellipsis; the full name is in its tooltip) and a muted
   "what, what came of it, when" line. The row is still the one link that opens the detail dialog, so the
   rules above (colour, focus ring, hover tint) keep applying; the rules here are written
   .wsk-recent-item a.wsk-recent-row so they come after and win. */
#activity-heading { text-transform: none; letter-spacing: 0; font-size: 15px; color: var(--wsk-heading); }
.wsk-recent-day { margin: var(--wsk-space-1) 0 6px; font-size: 12px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--wsk-meta); }
.wsk-recent-list + .wsk-recent-day { margin-top: var(--wsk-space-3); }
.wsk-recent-day + .wsk-recent-list { gap: 2px; }
.wsk-recent-item a.wsk-recent-row { display: flex; align-items: center; gap: var(--wsk-space-3); margin: 0 -8px; padding: 8px; border-radius: 10px; color: inherit; text-decoration: none; }
.wsk-recent-item a.wsk-recent-row:hover { text-decoration: none; }
.wsk-recent-icon { flex: none; display: grid; place-items: center; width: 34px; height: 34px; border-radius: 10px; }
.wsk-recent-icon-search { background: var(--accent-light); color: var(--accent); }
.wsk-recent-icon-upload { background: #e7f2ec; color: #2f6f4f; }
.wsk-recent-icon-ask { background: var(--wsk-cream); color: var(--wsk-text-secondary); }
.wsk-recent-row:hover .wsk-recent-icon, .wsk-recent-row:focus-visible .wsk-recent-icon { background: #fff; }
.wsk-recent-text { display: flex; flex-direction: column; gap: 2px; flex: 1 1 auto; min-width: 0; }
.wsk-recent-what { min-width: 0; overflow: hidden; font-size: 14px; font-weight: 700; color: var(--wsk-heading); text-overflow: ellipsis; white-space: nowrap; }
.wsk-recent-meta { font-size: 13px; color: var(--wsk-meta); }
.wsk-activity-detail { box-sizing: border-box; overflow-wrap: anywhere; }
.wsk-activity-detail .wsk-shell-panel-heading button { flex-shrink: 0; }
.wsk-activity-subject { font-weight: 600; white-space: pre-wrap; }

/* Ask's empty state: an "honest empty" block and the recent-Ask card. */
.wsk-ask-empty-honest {
  align-self: stretch; display: flex; flex-direction: column; gap: 10px;
  padding: 18px 20px; border: 1px dashed var(--wsk-border); border-radius: 12px;
}
.wsk-ask-empty-h2 { margin: 0; font-size: 17px; font-weight: 600; color: var(--wsk-text); }
.wsk-ask-recent { align-self: stretch; }

/* Search no-match: the status line reads as a statement, not a count; the
   closest-records disclosure is visibly secondary and closed by default. */
.wsk-srch-nomatch { font-weight: 600; color: var(--wsk-text); }
.wsk-srch-closest { margin-top: 20px; }
.wsk-srch-closest > summary { cursor: pointer; font-size: 14px; color: var(--wsk-text-secondary); }
/* Its chevron is drawn by the shared disclosure rule further down. */
.wsk-srch-closest-note { margin: 8px 0 12px; font-size: 13px; color: var(--wsk-meta); }

@media (max-width: 720px) {
  .wsk-clerk-chip-row { padding: 8px 12px 0; }
}

/* --- KAN-341: the administrator's "Get started" checklist on Home ----------
   hub/templates/workspace/fragments/getting_started.html. A wsk-card with an
   ordered list of steps; a done step is muted with a check mark, an open one
   carries its link. Tokens only, no new hex. */
.wsk-start-card { margin: 0; }
.wsk-start-card:hover { transform: none; }
.wsk-start-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin: 0 0 12px; }
.wsk-start-count { font-size: 13px; color: var(--wsk-meta); }
.wsk-start-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.wsk-start-step { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; padding: 10px 12px; border: 1px solid var(--wsk-border-light); border-radius: 10px; }
.wsk-start-step-done { background: var(--wsk-bg); }
.wsk-start-step-done .wsk-start-label { color: var(--wsk-meta); }
.wsk-start-mark {
  flex: 0 0 28px; width: 28px; height: 28px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 14px; font-weight: 700; color: var(--accent); background: var(--accent-light);
}
/* KAN-341 (tester): the text column keeps at least 160px; the action link
   wraps to its own line in the narrow side column instead of squeezing the
   label to one word per line. */
.wsk-start-body { flex: 1 1 160px; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.wsk-start-label { font-size: 15px; font-weight: 700; color: var(--wsk-heading); }
.wsk-start-status { font-size: 13px; color: var(--wsk-text-secondary); }
.wsk-start-link { flex: 0 0 auto; margin-left: auto; font-size: 14px; font-weight: 700; color: var(--accent); text-decoration: none; }
.wsk-start-link:hover { text-decoration: underline; }
@media (max-width: 640px) {
  .wsk-start-link { flex-basis: 100%; padding-left: 40px; margin-left: 0; }
}

/* --- KAN-341: Home as a triage desk (clerk walker C7) -----------------------
   engine_tasks.html groups the open tasks; the rest sit behind one details.
   The overdue count is also surfaced in the schedule card. */
/* Group labels (Overdue, Due today, New and unassigned) ride on the heading's
   row: "Your tasks 8  Overdue 8" on one shared baseline, wrapping onto a second
   line when the row runs out of width (a phone, or several groups) instead of
   overflowing. The cards they head follow as .wsk-task-section-cards, one per
   label; each takes its parent's gap between cards, and a thin divider
   separates one group's cards from the next. */
.wsk-task-head-row { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--wsk-space-2) var(--wsk-space-4); min-width: 0; }
.wsk-task-section-heading { margin: 0; font-size: 15px; font-weight: 700; color: var(--wsk-heading); display: flex; align-items: center; gap: 8px; }
.wsk-task-section-cards { display: flex; flex-direction: column; gap: inherit; min-width: 0; }
.wsk-task-section-cards + .wsk-task-section-cards { border-top: 1px solid var(--wsk-border-light); padding-top: var(--wsk-space-3); }
/* KAN-530: "Hide this section" - a small text button beside a named section heading. */
.wsk-task-section-hide { margin: 0; display: inline; }
.wsk-task-section-hide-button { margin: 0; padding: 0; border: 0; background: none; font: inherit; font-size: 13px; color: var(--wsk-text-secondary); text-decoration: underline; cursor: pointer; }
.wsk-task-section-hide-button:hover, .wsk-task-section-hide-button:focus-visible { color: var(--wsk-heading); }
.wsk-task-section-heading[data-task-topic] {
  display: block; min-inline-size: 0; max-inline-size: 100%; overflow-wrap: anywhere;
}
.wsk-task-section-count { font-size: 12px; font-weight: 700; color: var(--wsk-text-secondary); background: var(--wsk-cream); border-radius: 999px; padding: 1px 8px; }
.wsk-task-section-overdue { color: #8c2323; }
/* Home top declutter (2026-10-11): no loose chips on the heading row. The count after "Your tasks"
   and after each label is a plain number; each label (Due today, New and unassigned) carries one
   small status dot, the same dot-and-words vocabulary as the mail notice. KAN-443 (1): the Overdue
   label is a small red pill reading "4 overdue" (the same red as the card's "Overdue 25 days" pill),
   so it reads as a status instead of a second heading. */
#tasks-card .wsk-task-head-row .wsk-task-section-count { padding: 0; background: none; font-size: inherit; font-weight: 700; color: inherit; }
#tasks-card .wsk-task-owner-heading .wsk-task-section-count { font-weight: 600; color: var(--wsk-meta); }
#tasks-card .wsk-task-section-overdue { padding: 2px 10px; border-radius: 999px; background: #fbecec; color: #8f2a2a; font-size: 13px; }
#tasks-card .wsk-task-section-today::before { content: ""; flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--accent); }
#tasks-card .wsk-task-section-new::before { content: ""; flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--wsk-icon); }
.wsk-task-overdue { font-size: 13px; font-weight: 700; color: #8c2323; }
/* KAN-443 (17): on the card's date line the same label is a small red pill. */
.wsk-task-meta .wsk-task-overdue {
  padding: 2px 10px; border-radius: 999px; background: #fbecec; color: #8f2a2a; white-space: nowrap;
}
/* KAN-509: the expanded "Show all N upcoming or undated tasks" list. A flex gap on <details> never reached the
   cards: browsers wrap everything after the <summary> in one anonymous content box, so the cards sat
   edge to edge. Each card carries its own 12px top margin instead (the main list's gap). */
.wsk-task-rest { margin: 8px 0 0; }
.wsk-task-rest > :not(summary) { margin-top: 12px; }
.wsk-task-rest-summary { cursor: pointer; font-size: 14px; font-weight: 700; color: var(--accent); padding: 8px 0; }
.wsk-task-rest-open { display: none; }
.wsk-task-rest[open] > summary .wsk-task-rest-closed { display: none; }
.wsk-task-rest[open] > summary .wsk-task-rest-open { display: inline; }
.wsk-schedule-overdue { margin: 8px 0 0; font-size: 13px; color: #8c2323; }
/* Mail notice (home top declutter, 2026-10-11): one slim line instead of a boxed yellow
   banner - a small amber status dot, one short sentence in body type, and the one action
   as a bold underlined link. Same words and link as before; it still sits above the greeting. */
.wsk-sync-banner { margin: 0; padding: var(--wsk-space-1) 0; color: var(--wsk-text); font-size: 14px; font-weight: 400; line-height: 1.5; }
.wsk-sync-banner::before { content: ""; display: inline-block; width: 8px; height: 8px; margin-right: var(--wsk-space-2); border-radius: 50%; background: #b7791f; vertical-align: 1px; }
.wsk-sync-banner a { margin-left: var(--wsk-space-1); color: var(--accent); font-weight: 700; text-decoration: underline; text-underline-offset: 2px; }

/* --- KAN-341 (tester, visual LOWs) -------------------------------------------
   Disabled buttons look disabled: muted colours, a not-allowed cursor, no
   hover lift. One shared rule for every button family a disabled control can
   carry (Invite before team setup, the sole administrator's Save role). */
.ws-btn-primary:disabled, .ws-btn-suggestion:disabled,
.wsk-btn-primary:disabled, .wsk-btn-outline:disabled {
  background: var(--wsk-border-light); color: var(--wsk-disabled);
  border-color: var(--wsk-border); cursor: not-allowed; box-shadow: none; transform: none;
}
/* The invite field's focus ring (3px + 3px offset) no longer runs into the
   status line under the form. */
.ws-admin-invite-form + .ws-muted, .ws-admin-invite-form + p { margin-top: 12px; }
.wsk-auth-account-note { margin: 8px 0 0; font-size: 13px; text-align: center; color: var(--wsk-text-secondary); }
.wsk-billing-locked { color: var(--wsk-text-secondary); }

/* KAN-344: the clerk-workflow fixes. The open item's form inside the reply's
   own "Needs your input" panel (one panel, no second card); the record box on
   a reply question; the access notice and the admin's per-member access line. */
.wsk-qa-embedded { margin: 10px 0 0; padding: 10px 0 0; border-top: 1px solid var(--wsk-border-light); }
/* KAN-443 item 6 / KAN-499: the green "saved" confirmation (fragments/answer_thanks.html) is ONE
   line, with the Undo beside it (under it on a phone). Dark green on pale green is about 9:1. */
.wsk-thanks { display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: center; margin: 10px 0 0; padding: 8px 12px; border: 1px solid #a9d9bb; border-radius: 10px; background: #eefaf2; color: #14532d; }
.wsk-thanks-check { flex: 0 0 auto; display: grid; place-items: center; width: 24px; height: 24px; border-radius: 50%; background: #1a7f45; color: #fff; }
.wsk-thanks-line { flex: 1 1 220px; min-width: 0; margin: 0; font-size: 15px; line-height: 1.4; font-weight: 700; overflow-wrap: anywhere; }
.wsk-thanks-answer { color: var(--wsk-heading); }
.wsk-thanks form { margin: 0; }
.wsk-qa-remember { font-size: 13px; color: var(--wsk-text-secondary); }
.wsk-access-notice { margin: 12px 0; }
.wsk-detail-own { margin: 0 0 6px; font-size: 12px; font-weight: 700; color: var(--wsk-text-secondary); }
.wsk-erow-replied { font-weight: 700; color: var(--wsk-text-secondary); }
.wsk-email-task { margin: 12px 0 0; }
.wsk-sync-stopped { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 0 0 12px; padding: 12px 14px; border: 1px solid #c08a2e; border-radius: 8px; background: #fdf1d8; color: var(--wsk-heading); }

.wsk-auth-disclosure > summary { cursor: pointer; margin: 12px 0; color: var(--accent); }
.wsk-auth-qr { width: 260px; margin: 0 auto 20px; background: #fff; }
.wsk-auth-qr svg { display: block; width: 260px; height: 260px; }
.wsk-auth-setup-key { overflow-wrap: anywhere; user-select: all; }

/* Guided two-step setup (app_mfa.html). Three always-visible numbered steps for
   a non-technical clerk: get Microsoft Authenticator, add this workspace by
   scanning from INSIDE the app, type the 6-digit code. Written for older,
   non-technical readers: 18px body text, 21px step headings, 56px tap targets,
   one white card per step on a tinted page, a yellow warning beside the QR and a
   red alert inside step 3. Every rule is scoped under .wsk-mfa-* so no other
   auth screen restyles. The returning "Verify your sign-in" screen shares the
   template and flow, so it deliberately takes the shared (.wsk-mfa-card) part:
   56px digit boxes, 58px submit, the red alert box and 16px hints. The large,
   wide-spaced, centred style for the single code field is .wsk-mfa-wide only
   (enrolment): the returning field also takes a 32-character recovery code, so it
   keeps the stock input look.
   Phone: one column. >= 900px: two columns by CSS columns only - steps 1 and 2
   on the left, the QR card then step 3 on the right (a forced column break sits
   in front of .wsk-mfa-scan). Colours reuse the accent tokens; the few fixed
   ones (warning amber, alert red, the red "not") were measured >= 4.5:1 on
   their background. */
.wsk-mfa-page { background: var(--accent-light); padding: 28px 20px 40px; align-items: flex-start; }
.wsk-mfa-wide { max-width: 560px; }
.wsk-mfa-wide .wsk-mfa-card {
  padding: 0; border: 0; background: transparent; box-shadow: none;
  align-items: stretch; text-align: left;
}
.wsk-mfa-wide .wsk-auth-kicker { padding: 4px 4px 0; font-size: 13px; letter-spacing: .14em; color: var(--wsk-meta); }
.wsk-mfa-wide .wsk-auth-title { padding: 0 4px; font-size: 32px; line-height: 1.15; letter-spacing: 0; }
.wsk-mfa-wide .wsk-auth-copy { margin: 10px 4px 14px; font-size: 18px; line-height: 1.45; text-align: left; color: var(--wsk-text); }
.wsk-mfa-progress { display: flex; gap: 8px; margin: 0 4px 20px; }
.wsk-mfa-progress > span { flex: 1; height: 6px; border-radius: 3px; background: var(--accent); opacity: .35; }
.wsk-mfa-progress > .wsk-mfa-progress-on { opacity: 1; }
.wsk-mfa-steps {
  width: 100%; margin: 0; padding: 0; list-style: none;
  text-align: left; color: var(--wsk-text);
}
.wsk-mfa-step {
  margin: 0 0 16px; padding: 22px 20px; border-radius: 20px; background: var(--wsk-card);
  box-shadow: 0 1px 0 rgba(28, 26, 46, .06), 0 8px 24px rgba(60, 50, 160, .08);
  break-inside: avoid;
}
.wsk-mfa-step:last-child { margin-bottom: 0; }
.wsk-mfa-step-title {
  display: flex; align-items: center; gap: 12px;
  margin: 0 0 14px; font-size: 21px; font-weight: 700; line-height: 1.25;
  color: var(--wsk-heading);
}
.wsk-mfa-badge {
  flex: 0 0 auto; box-sizing: border-box; width: 36px; height: 36px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%; background: var(--accent); color: #fff;
  font-size: 18px; font-weight: 700;
}
.wsk-mfa-icon { flex: 0 0 auto; }
.wsk-mfa-store-icon { width: 24px; height: 24px; }
.wsk-mfa-store { display: flex; flex-direction: column; gap: 12px; margin: 0 0 14px; }
.wsk-mfa-card .wsk-mfa-store-link {
  display: flex; align-items: center; justify-content: flex-start; gap: 12px;
  box-sizing: border-box; min-height: 56px; padding: 0 18px;
  border: 2px solid var(--accent); border-radius: 14px;
  background: var(--accent-light); color: var(--accent-hover);
  font-size: 18px; font-weight: 700; text-align: left;
}
.wsk-mfa-card .wsk-mfa-store-link:hover { background: #fff; }
.wsk-mfa-store-name { flex: 1 1 auto; }
.wsk-mfa-substeps {
  margin: 0 0 4px; padding: 0; list-style: none; counter-reset: mfa-sub;
  display: flex; flex-direction: column; gap: 12px;
  font-size: 18px; line-height: 1.4; color: var(--wsk-text);
}
.wsk-mfa-substeps li { position: relative; margin: 0; padding-left: 40px; counter-increment: mfa-sub; }
.wsk-mfa-substeps li::before {
  content: counter(mfa-sub, lower-alpha);
  content: counter(mfa-sub, lower-alpha) / "";
  position: absolute; left: 0; top: 0; box-sizing: border-box; width: 28px; height: 28px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%; background: var(--accent-light); color: var(--accent-hover);
  font-size: 15px; font-weight: 700;
}
.wsk-mfa-substeps-numbered { margin-bottom: 18px; text-align: left; }
.wsk-mfa-substeps-numbered li::before {
  content: counter(mfa-sub);
  content: counter(mfa-sub) / "";
}
.wsk-mfa-not { color: #9a2a1f; }
.wsk-mfa-text { margin: 0 0 12px; font-size: 18px; line-height: 1.45; color: var(--wsk-text); }
.wsk-mfa-hint { margin: 0 0 14px; font-size: 16px; line-height: 1.45; color: var(--wsk-text-secondary); }
.wsk-mfa-hint:last-child { margin-bottom: 0; }
.wsk-mfa-hint a, .wsk-mfa-text a { color: var(--accent); font-weight: 700; }
.wsk-mfa-scan { margin-top: 18px; }
.wsk-mfa-caption { margin: 0 0 12px; font-size: 14px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--wsk-text-secondary); }
.wsk-mfa-caption-qr { display: none; }
.wsk-mfa-card .wsk-auth-qr {
  width: min(264px, 100%); box-sizing: border-box; margin: 0 auto 16px; padding: 16px;
  border: 2px solid var(--wsk-border); border-radius: 18px;
}
.wsk-mfa-card .wsk-auth-qr svg { width: 100%; height: auto; }
.wsk-mfa-note {
  display: flex; gap: 12px; margin: 0 0 14px; padding: 14px 16px; border: 2px solid transparent; border-radius: 14px;
  background: #fff4d6; color: #4a3500; font-size: 17px; line-height: 1.4; text-align: left;
}
.wsk-mfa-note .wsk-mfa-icon { margin-top: 2px; }
.wsk-mfa-card .wsk-auth-disclosure { border-top: 1px solid var(--wsk-border-light); padding-top: 4px; }
.wsk-mfa-card .wsk-auth-disclosure > summary {
  display: flex; align-items: center; gap: 8px; box-sizing: border-box;
  min-height: 48px; margin: 0; font-size: 17px; font-weight: 700; color: var(--accent-hover);
  list-style: none;
}
.wsk-mfa-card .wsk-auth-disclosure > summary::-webkit-details-marker { display: none; }
.wsk-mfa-card .wsk-auth-disclosure > summary::before { width: 10px; height: 10px; border-width: 2.5px; opacity: 1; }
.wsk-mfa-card .wsk-auth-setup-key {
  display: block; padding: 12px 14px; border: 2px dashed var(--wsk-border); border-radius: 12px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 18px; letter-spacing: .06em; line-height: 1.4; word-break: break-all;
}
.wsk-mfa-alert {
  display: flex; gap: 12px; align-items: flex-start; margin: 0 0 14px; padding: 14px 16px;
  border: 2px solid transparent; border-radius: 14px; background: #fdebe8; color: #7a1f14;
  font-size: 18px; line-height: 1.4; text-align: left;
}
.wsk-mfa-alert .wsk-mfa-icon { margin-top: 2px; }
@media (forced-colors: active) {
  .wsk-mfa-alert, .wsk-mfa-note { border-color: CanvasText; }
}
.wsk-mfa-alert p { margin: 0; font-weight: 700; }
.wsk-mfa-card .wsk-auth-form { margin-top: 4px; gap: 16px; }
.wsk-mfa-card .wsk-auth-field { gap: 10px; }
.wsk-mfa-card .wsk-auth-label { font-size: 14px; letter-spacing: .1em; color: var(--wsk-text-secondary); }
.wsk-mfa-card .wsk-auth-help { font-size: 16px; line-height: 1.45; }
.wsk-mfa-wide .wsk-auth-form .wsk-input { min-height: 64px; border-width: 2px; border-color: #8a85b8; border-radius: 14px; font-size: 28px; font-weight: 700; letter-spacing: .3em; text-align: center; }
.wsk-mfa-card .wsk-auth-form .mfa-digit-input { min-height: 56px; height: 56px; padding: 0; border: 2px solid #8a85b8; border-radius: 12px; font-size: 28px; font-weight: 700; letter-spacing: 0; text-align: center; }
.wsk-mfa-card .mfa-digit-grid { gap: 8px; }
.wsk-mfa-card .wsk-auth-form .wsk-input:focus { border-color: var(--accent); }
.wsk-mfa-card .wsk-auth-form .wsk-input[aria-invalid="true"] { border-color: #b3281b; }
.wsk-mfa-card .mfa-recovery-toggle { min-height: 44px; font-size: 16px; }
.wsk-mfa-card .wsk-auth-submit { min-height: 58px; border-radius: 14px; font-size: 19px; }
.wsk-mfa-card .wsk-auth-form + .wsk-mfa-hint { margin: 14px 0 0; }
.wsk-mfa-card .wsk-auth-form + .wsk-mfa-hint:not(:last-child) { margin-bottom: 14px; }
.wsk-mfa-card .wsk-auth-footnote { margin-top: 20px; font-size: 16px; }
.wsk-mfa-card .wsk-auth-footnote a { display: inline-flex; align-items: center; min-height: 44px; color: var(--accent); }
.wsk-mfa-wide .wsk-auth-footnote { margin-top: 12px; text-align: center; }
@media (max-width: 480px) {
  .wsk-mfa-card { padding: 32px 20px; }
  .wsk-mfa-wide .wsk-mfa-card { padding: 0; }
}
@media (max-width: 380px) {
  .wsk-mfa-page { padding: 20px 12px 32px; }
  .wsk-mfa-step { padding: 18px 14px; }
  .wsk-mfa-wide .wsk-auth-title { font-size: 28px; }
  .wsk-mfa-card .mfa-digit-grid { gap: 5px; }
}
@media (min-width: 900px) {
  .wsk-mfa-page { padding: 56px 64px 64px; }
  .wsk-mfa-wide { max-width: 640px; }
  .wsk-mfa-wide .wsk-auth-kicker { font-size: 14px; }
  .wsk-mfa-wide .wsk-auth-title { font-size: 42px; line-height: 1.12; }
  .wsk-mfa-wide .wsk-auth-copy { font-size: 20px; }
  .wsk-mfa-progress { max-width: 420px; }
  .wsk-mfa-steps { column-count: 1; }
  .wsk-mfa-step { margin-bottom: 24px; padding: 28px; border-radius: 22px; }
  .wsk-mfa-step:last-child { margin-bottom: 0; }
  .wsk-mfa-step-title { gap: 14px; font-size: 24px; }
  .wsk-mfa-badge { width: 40px; height: 40px; font-size: 20px; }
  .wsk-mfa-substeps { gap: 14px; font-size: 19px; }
  .wsk-mfa-substeps li::before { top: 1px; }
  .wsk-mfa-caption-qr { display: block; }
  .wsk-mfa-card .wsk-auth-qr { width: 280px; }
  .wsk-mfa-wide .wsk-auth-form .wsk-input { min-height: 68px; font-size: 32px; }
  .wsk-mfa-card .wsk-auth-form .mfa-digit-input { min-height: 60px; height: 60px; }
  .wsk-mfa-card .wsk-auth-submit { min-height: 60px; font-size: 20px; }
}

/* KAN-420: concise consent overview, with the audited text one disclosure away. */
.wsk-auth-copy.wsk-join-steps { text-align: left; padding-left: 24px; }
.wsk-join-steps li { padding-left: 4px; margin-bottom: 12px; }
.wsk-consent { margin: 24px 0; text-align: left; color: var(--wsk-text-secondary); font-size: 14px; line-height: 1.5; }
.wsk-consent-list { display: grid; gap: 14px; list-style: none; margin: 0 0 16px; padding: 0; }
.wsk-consent-list li { display: flex; align-items: flex-start; gap: 10px; }
.wsk-consent-list svg { flex: 0 0 18px; margin-top: 2px; color: var(--wsk-text-secondary); }
.wsk-consent-list span { min-width: 0; overflow-wrap: anywhere; }
.wsk-consent-details { padding-top: 12px; border-top: 1px solid var(--wsk-border); }
.wsk-consent-details summary { margin: 0; cursor: pointer; color: var(--accent); font-weight: 700; }
.wsk-consent-details summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.wsk-consent-details p { margin: 12px 0 0; }

/* KAN-418: icons and outlines carry meaning alongside the priority words.
   Red reuses priority-high; amber reuses the existing ask warning ink. */
.wsk-task-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.wsk-task-chips-edit { border: 0; padding: 0; background: transparent; font: inherit; cursor: pointer; text-align: start; max-width: 100%; }
.wsk-task-chips-edit:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 999px; }
.wsk-task-chip { display: inline-flex; align-items: center; gap: 6px; padding: 4px 9px; border: 1px solid var(--wsk-border); border-radius: 999px; font-size: 13px; line-height: 1.4; max-width: 100%; box-sizing: border-box; white-space: nowrap; }
.wsk-task-chip svg { flex: 0 0 14px; }
.wsk-assignee-chip span { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.wsk-task-priority-urgent { border-color: currentColor; font-weight: 700; }
.wsk-task-priority-high { background: var(--wsk-cream); color: #7a5b12; border-color: currentColor; font-weight: 700; }
.wsk-task-priority-normal { background: #e8eef4; color: #2f4a63; border-color: #7d93a8; }
.wsk-task-priority-low { background: transparent; color: var(--wsk-meta); border-style: dotted; }
.wsk-assignee-chip { background: var(--wsk-card); color: var(--wsk-text-secondary); }
.wsk-assignee-unassigned { border-style: dashed; border-color: var(--wsk-meta); background: transparent; }

.wsk-task-chip-pair { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; max-width: 100%; }
.wsk-task-chip-pair > :first-child { flex-shrink: 0; }
.wsk-assignee-chip { min-width: 0; }

/* Information only (a teammate's card, Today's Schedule): no pill. A bordered
   or filled pill reads as a control, and these do nothing when pressed, so they
   keep their icon, words and colour as plain text. Only the chips inside
   .wsk-task-chips-edit (the button that opens the task editor) stay pills. */
.wsk-task-chips-plain .wsk-task-chip { border: 0; background: transparent; padding: 0; border-radius: 0; }
.wsk-task-chips-plain { gap: 12px; }
/* Category and priority labels that sit beside a title and are not links: the
   Home task cards and the email page. (The Inbox list keeps its pills: the
   whole row is the link there.) Same rule, scoped to where they are inert. */
.wsk-task-top .wsk-tag, .wsk-task-tags .wsk-tag, .wsk-detail-meta .wsk-tag,
.wsk-detail-meta .wsk-priority { background: transparent; padding: 0; border-radius: 0; }

/* KAN-419: question, choices, custom answer and submit have distinct space. */
.wsk-qa, .wsk-qa-embedded { line-height: 1.5; }
.wsk-qa > p, .wsk-qa-embedded > p { margin: 12px 0; }
.wsk-qa form, .wsk-qa-embedded form { display: flex; flex-direction: column; align-items: stretch; gap: 12px; }
.wsk-qa form > button, .wsk-qa-embedded form > button { align-self: flex-start; margin-top: 4px; }
.wsk-qa .wsk-qa-item, .wsk-qa-embedded .wsk-qa-item { margin: 0; min-width: 0; }
.wsk-qa .wsk-qa-other, .wsk-qa-embedded .wsk-qa-other { margin-top: 12px; }
.wsk-qa-other-label { margin-bottom: 6px; }
.wsk-qa form > .wsk-qa-other-label, .wsk-qa-embedded form > .wsk-qa-other-label { margin: 0 0 -6px; }
.wsk-qa-prompt { letter-spacing: .02em; }
.wsk-qa .wsk-qa-choice, .wsk-qa-embedded .wsk-qa-choice { padding: 6px 0; flex-wrap: wrap; }
.wsk-qa-choice-value { overflow-wrap: anywhere; }
.wsk-qa-choice-source { flex-basis: 100%; padding-left: 24px; color: var(--wsk-text-secondary); }
.wsk-qa-help, .wsk-qa form > .wsk-qa-help { margin: 0; font-size: 13px; color: var(--wsk-text-secondary); }
.wsk-qa > .wsk-qa-lead, .wsk-qa-embedded > .wsk-qa-lead { margin: 6px 0 10px; font-size: 12px; font-style: italic; }
.wsk-qa-skip { padding: 4px 0; min-height: 32px; border: 0; background: transparent; color: var(--wsk-text-secondary); font: inherit; font-size: 12px; font-style: italic; text-decoration: underline; cursor: pointer; }
.wsk-qa-note { color: var(--wsk-text-secondary); }
.wsk-qa .wsk-qa-remember { margin-top: 12px; flex-wrap: nowrap; gap: 8px; }
.wsk-qa-remember input { flex-shrink: 0; }
.wsk-qa input:focus-visible, .wsk-qa-embedded input:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
/* --- The question card (owner 2026-10-06: "build a better design") ---------
   One white card, a thin accent on its left edge, the question as one clear
   line, and the choices as tappable option cards with radio semantics: the
   chosen one is outlined and tinted, never colour alone (the radio itself
   fills too). The blank "Something else" option is always the last card and
   opens its box when chosen or focused. Skip is a quiet text button; Submit
   is the one primary, and waits for a choice (replyquestions.js). Spacing is
   the 8px scale, radius 8px like .wsk-input, and every colour is a token
   already in this file. Used by the reply's question panel, the standalone
   open-question card and (for the card chrome) the knowledge questions. */
.wsk-qa, .wsk-composer .wsk-qa {
  background: var(--wsk-card); border: 1px solid var(--wsk-border-light);
  border-left: 3px solid var(--accent); border-radius: 8px;
  padding: 16px 16px 16px 20px; margin: 16px 0 0; box-shadow: none;
}
.wsk-qa.wsk-card:hover { transform: none; box-shadow: none; border-color: var(--wsk-border-light); }
.wsk-qa-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 8px; margin: 0 0 8px; }
.wsk-qa .wsk-qa-head .wsk-qa-title, .wsk-composer .wsk-qa .wsk-qa-head .wsk-qa-title {
  margin: 0; font-size: 14px; font-weight: 700; letter-spacing: 0; text-transform: none;
  color: var(--wsk-heading);
}
.wsk-qa .wsk-qa-head .wsk-qa-lead { margin: 0; font-size: 13px; font-style: normal; color: var(--wsk-text-secondary); }
.wsk-qa .wsk-qa-question, .wsk-qa fieldset.wsk-qa-item > .wsk-qa-prompt, .wsk-composer .wsk-qa fieldset.wsk-qa-item > .wsk-qa-prompt {
  margin: 8px 0 16px; padding: 0; font-size: 16px; font-weight: 700; line-height: 1.4;
  letter-spacing: 0; color: var(--wsk-heading); overflow-wrap: anywhere;
}
.wsk-qa fieldset.wsk-qa-item { border: 0; margin: 0; padding: 0; min-width: 0; }
fieldset.wsk-qa-item > .wsk-qa-prompt { float: left; width: 100%; }
fieldset.wsk-qa-item > .wsk-qa-prompt + * { clear: both; }

/* the option cards */
.wsk-qa fieldset.wsk-qa-item .wsk-qopt {
  display: grid; grid-template-columns: 20px minmax(0, 1fr); column-gap: 12px; row-gap: 2px;
  align-items: start; box-sizing: border-box; min-height: 48px; margin: 0 0 8px; padding: 12px 16px;
  background: var(--wsk-card); border: 1px solid var(--wsk-border); border-radius: 8px;
  cursor: pointer; transition: border-color .15s ease, background-color .15s ease;
}
.wsk-qa fieldset.wsk-qa-item .wsk-qopt:hover { border-color: var(--accent); }
.wsk-qa fieldset.wsk-qa-item .wsk-qopt:has(input[type="radio"]:checked) {
  border-color: var(--accent); background: var(--accent-light); box-shadow: inset 0 0 0 1px var(--accent);
}
.wsk-qa fieldset.wsk-qa-item .wsk-qopt:has(input[type="radio"]:focus-visible) { outline: 3px solid #0b57d0; outline-offset: 2px; }
.wsk-qa .wsk-qopt input[type="radio"] {
  grid-row: 1 / span 2; grid-column: 1; width: 20px; height: 20px; min-width: 20px; min-height: 20px;
  margin: 1px 0 0; accent-color: var(--accent); cursor: pointer;
}
.wsk-qa .wsk-qopt input[type="radio"]:focus-visible { outline: none; }
.wsk-qa .wsk-qopt .wsk-qa-choice-value, .wsk-qa .wsk-qopt .wsk-qa-other-name {
  grid-column: 2; font-size: 15px; font-weight: 700; line-height: 1.4; color: var(--wsk-heading);
  overflow-wrap: anywhere; cursor: pointer;
}
.wsk-qa .wsk-qopt .wsk-qa-choice-source {
  grid-column: 2; padding: 0; font-size: 13px; line-height: 1.4; color: var(--wsk-meta);
}

/* the blank option: its box opens when the row is chosen or the box is focused */
.wsk-qa .wsk-qother-field { grid-column: 2; display: grid; grid-template-rows: 0fr; transition: grid-template-rows .18s ease; }
.wsk-qa .wsk-qother:has(input[type="radio"]:checked) .wsk-qother-field,
.wsk-qa .wsk-qother:focus-within .wsk-qother-field { grid-template-rows: 1fr; }
.wsk-qa .wsk-qother-inner { min-height: 0; overflow: hidden; padding: 0 3px 3px; }
.wsk-qa .wsk-qother .wsk-qa-other-input { margin: 8px 0 0; min-height: 44px; width: 100%; box-sizing: border-box; }
.wsk-qa .wsk-qother .wsk-qa-other-input:focus-visible { outline-offset: 0; }
.wsk-qa .wsk-qother .wsk-qa-other-input::placeholder { color: var(--wsk-meta); }

/* a question with nothing to choose from is just its labelled box */
.wsk-qa fieldset.wsk-qa-item > .wsk-qa-other-label { display: block; margin: 0 0 8px; font-size: 14px; font-weight: 700; color: var(--wsk-heading); }
.wsk-qa fieldset.wsk-qa-item > .wsk-qa-input { min-height: 44px; margin: 0 0 8px; box-sizing: border-box; }

/* Skip is quiet; Submit is the one primary */
.wsk-qa .wsk-qa-skip {
  display: inline-flex; align-items: center; min-height: 44px; padding: 0 12px; border: 0; border-radius: 8px;
  background: transparent; color: var(--wsk-text-secondary); font: inherit; font-size: 14px; font-style: normal;
  text-decoration: none; cursor: pointer;
}
.wsk-qa .wsk-qa-skip:hover { color: var(--wsk-heading); text-decoration: underline; }
.wsk-qa fieldset.wsk-qa-item > .wsk-qa-skip { margin: 0 0 4px -8px; padding: 0 8px; }
.wsk-qa .wsk-qa-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 8px; margin-top: 4px; }
.wsk-qa .wsk-qa-submit { align-self: flex-start; min-height: 44px; margin: 0; padding: 10px 24px; }
.wsk-qa .wsk-qa-submit:disabled {
  background: var(--wsk-border-light); border-color: var(--wsk-border-light); color: var(--wsk-meta);
  cursor: not-allowed; transform: none;
}
.wsk-qa .wsk-qa-help { margin: 0; font-size: 13px; line-height: 1.4; color: var(--wsk-text-secondary); }
/* One "Goes into this reply only." is enough, however many questions there are. */
.wsk-qa .wsk-qa-item ~ .wsk-qa-item .wsk-qa-help-reply { display: none; }
@media (max-width: 600px) {
  .wsk-qa, .wsk-composer .wsk-qa { padding: 16px 12px 16px 16px; }
  .wsk-qa fieldset.wsk-qa-item .wsk-qopt { padding: 12px; column-gap: 8px; }
  .wsk-qa .wsk-qa-actions { justify-content: space-between; }
  .wsk-qa .wsk-qa-submit { flex: 1 1 auto; }
}
@media (prefers-reduced-motion: reduce) {
  .wsk-qa fieldset.wsk-qa-item .wsk-qopt, .wsk-qa .wsk-qother-field { transition: none; }
}

/* KAN-427: the Settings answer list takes space only after it is opened. */
.wsk-remembered-slot:empty { display: none; }
/* Keep review context compact without hiding evidence or source links. */
.wsk-qa-help { margin-top: 6px; }
.wsk-draft-review { display: grid; grid-template-columns: 18px minmax(0, 1fr); column-gap: 10px; align-items: start; width: 100%; max-width: 640px; box-sizing: border-box; margin-top: var(--wsk-space-3); padding: 12px 14px; border: 1px solid var(--wsk-border-light); border-radius: 10px; background: var(--wsk-bg); font-size: 14px; line-height: 1.5; color: var(--wsk-text); overflow-wrap: anywhere; }
/* One small status icon, calm in both tones: grey for a plain note, a muted amber for
   "not found". Never a coloured card, never a red. */
.wsk-review-icon { grid-column: 1; margin-top: 2px; }
.wsk-review-icon-info { color: var(--wsk-meta); }
.wsk-review-icon-warn { color: #946200; }
.wsk-review-body { grid-column: 2; min-width: 0; }
.wsk-review-title { margin: 0; font-size: 16px; font-weight: 700; line-height: 22px; color: var(--wsk-heading); }
.wsk-draft-review .wsk-review-status { margin: 4px 0 0; font-weight: 700; color: var(--wsk-text); }
.wsk-draft-review h4 { margin: 12px 0 4px; font-size: 13px; color: var(--wsk-text-secondary); }
.wsk-draft-review p, .wsk-draft-review ul { margin: 4px 0 0; }
.wsk-review-notes { padding-left: 18px; }
.wsk-review-notes:has(> li:only-child) { list-style: none; padding-left: 0; }
.wsk-review-notes li + li { margin-top: 4px; }
.wsk-review-todo { font-size: 13px; color: var(--wsk-text-secondary); }
.wsk-review-sources { display: block; margin-top: 12px; }
.wsk-review-label { display: block; font-size: 13px; font-weight: 700; color: var(--wsk-text-secondary); }
/* KAN-443 item 5: one card per source - icon, title, kind/sender/date, and where it opens. */
.wsk-draft-review .wsk-src-list { display: grid; gap: 8px; margin: 8px 0 0; padding: 0; list-style: none; }
.wsk-src-card { display: flex; align-items: flex-start; gap: 12px; box-sizing: border-box; padding: 12px 14px; border: 1px solid var(--wsk-border-light); border-radius: 12px; background: var(--wsk-card); color: inherit; text-decoration: none; }
.wsk-src-card:hover { border-color: var(--accent); }
.wsk-src-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.wsk-src-icon { flex: none; display: grid; place-items: center; width: 30px; height: 30px; border-radius: 8px; background: var(--accent-light); color: var(--accent); }
.wsk-src-body { min-width: 0; }
.wsk-src-title, .wsk-src-meta, .wsk-src-use { display: block; overflow-wrap: anywhere; }
.wsk-src-title { font-size: 14px; font-weight: 700; color: var(--wsk-heading); }
.wsk-src-meta { font-size: 13px; color: var(--wsk-meta); }
.wsk-src-use { margin-top: 2px; font-size: 13px; color: var(--wsk-text-secondary); }
.wsk-draft-review .wsk-review-saved { margin: 8px 0 0; font-size: 11.5px; line-height: 1.3; color: var(--wsk-meta); }
@media (pointer: coarse) { .wsk-src-card { min-height: 44px; } }

.wsk-calendar-read-state { margin: 8px 0; font-size: 12px; line-height: 1.4; }

.wsk-notification-help { font-size: 14px; color: var(--wsk-text-secondary); }

#tasks-card { min-width: 0; max-width: 100%; }
.wsk-task-owner-group { display: flex; flex-direction: column; gap: var(--wsk-space-3); margin-top: var(--wsk-space-2); min-width: 0; max-width: 100%; }
.wsk-task-owner-heading { display: block; margin: 0; min-width: 0; max-width: 100%; font-size: 20px; font-weight: 700; line-height: 1.35; color: var(--wsk-heading); overflow-wrap: anywhere; }
.wsk-task-owner-name { overflow-wrap: anywhere; }
.wsk-task-owner-heading > .wsk-task-section-count { margin-left: 8px; vertical-align: middle; }

/* Keep source choices inside the task form on narrow screens. */
.wsk-task-editor select { min-width: 0; max-width: 100%; }
.wsk-task-alert {
  padding: 12px 16px;
  border: 1px solid #b45309;
  border-radius: 8px;
  background: #fffbeb;
  color: #78350f;
  font-weight: 600;
}
/* KAN-428/429: retained reply panels and a phone-width schedule return. */
.wsk-suggestion-panel[hidden] { display: none; }
.wsk-sched-head:has(.wsk-sched-back) { flex-wrap: wrap; }
.wsk-sched-back { flex-basis: 100%; }
.wsk-remembered > [data-close-remembered] { margin-bottom: 12px; }
@media (max-width: 700px) {
  .wsk-task-quick:has([data-unsaved-reply]) { flex-wrap: wrap; }
  .wsk-task-quick > [data-unsaved-reply] { flex-basis: 100%; order: 1; }
}

@media (max-width: 600px) {
  .wsk-task-top .wsk-task-meta { flex: 1 1 auto; min-width: 0; }
}

/* KAN-432: account navigation is distinct from the destructive sign-out action. */
.wsk-account-menu-link {
  display: block; padding: 12px 16px; color: var(--wsk-text);
  font-size: 15px; font-weight: 700; text-decoration: none;
}
.wsk-account-menu-link:hover { background: var(--wsk-bg); }

/* KAN-238: the chat scrolls independently; the composer stays within reach. */
.wsk-chat-page { overflow: hidden; }
.wsk-chat-main { max-width: 940px; margin: 16px auto 20px; }
.wsk-chat-heading { display:flex; align-items:center; justify-content:space-between; gap:16px; margin-bottom:12px; }
.wsk-chat-main h1 { font-size:22px; }
.wsk-chat-heading .wsk-btn-outline { padding:7px 12px; font-size:13px; }
.wsk-chat-session { border:0; padding:0; background:transparent; border-radius:0; }
/* 2026-10-06: the messages area scrolls only when it overflows, and then with a slim, quiet bar (no
   track, no arrow buttons). Chromium on Windows keeps its small arrow buttons whenever
   scrollbar-width is set (it then ignores ::-webkit-scrollbar), so the 8px ::-webkit-scrollbar rules
   carry Chromium and Safari, and scrollbar-width / scrollbar-color are applied only where the
   pseudo-element does not exist (Firefox). The gutter is reserved on both edges so the centred
   empty state never sits off-centre and nothing jumps sideways when the bar first appears. */
.wsk-chat-messages { padding:12px 12px 24px; overscroll-behavior:contain; scrollbar-gutter:stable both-edges; }
@supports not selector(::-webkit-scrollbar) {
  .wsk-chat-messages { scrollbar-width:thin; scrollbar-color:var(--wsk-border) transparent; }
}
.wsk-chat-messages::-webkit-scrollbar { width:8px; height:8px; }
.wsk-chat-messages::-webkit-scrollbar-track { background:transparent; }
.wsk-chat-messages::-webkit-scrollbar-thumb { background-color:var(--wsk-border); border-radius:8px; }
.wsk-chat-messages::-webkit-scrollbar-thumb:hover { background-color:var(--wsk-meta); }
.wsk-chat-messages::-webkit-scrollbar-button { display:none; width:0; height:0; }
.wsk-chat-messages::-webkit-scrollbar-corner { background:transparent; }
/* The welcome block is centred in whatever height the messages area has (min-height:100% of a
   flexed, definite-height area), so it never needs a scrollbar of its own on an empty chat. */
.wsk-chat-empty { max-width:660px; margin:0 auto; min-height:100%; box-sizing:border-box; display:flex; flex-direction:column; align-items:center; justify-content:center; text-align:center; }
.wsk-chat-empty[hidden] { display:none; }
.wsk-chat-welcome-mark { display:inline-grid; place-items:center; width:44px; height:44px; border-radius:50%; background:var(--accent); color:white; font-weight:700; }
.wsk-chat-empty h2 { font-size:28px; line-height:1.25; margin:18px 0 10px; color:var(--wsk-heading); }
.wsk-chat-empty > p { color:var(--wsk-text-secondary); margin:0 0 24px; }
.wsk-chat-suggestions { display:flex; flex-wrap:wrap; gap:10px; }
.wsk-chat-empty .wsk-chat-suggestions { display:grid; grid-template-columns:1fr 1fr; align-self:stretch; }
.wsk-chat-suggestion { border:1px solid var(--wsk-border); border-radius:14px; background:var(--wsk-card); color:var(--wsk-text); padding:14px 16px; text-align:left; font:inherit; font-size:14px; line-height:1.45; cursor:pointer; }
.wsk-chat-suggestion:hover { border-color:var(--accent); background:var(--wsk-cream); }
.wsk-chat-message { display:flex; align-items:flex-start; gap:12px; max-width:100%; margin:0 0 24px; }
.wsk-chat-message-body { min-width:0; max-width:calc(100% - 44px); font-size:16px; line-height:1.65; color:var(--wsk-text); overflow-wrap:anywhere; }
.wsk-chat-message-body p { white-space:pre-wrap; margin:0 0 12px; }
.wsk-chat-message-body p:last-child { margin-bottom:0; }
.wsk-chat-message-body ul,.wsk-chat-message-body ol { padding-left:24px; margin:8px 0 16px; }
.wsk-chat-message-body li { margin:9px 0; }
/* KAN-510: bold key words stand out a little more than the body; the Next step line is a small
   callout (thin accent rule, very light tint, same text size). */
.wsk-chat-message-body strong { font-weight:700; color:var(--wsk-heading); }
.wsk-chat-message-body .wsk-chat-nextstep { border-left:3px solid var(--accent); background:var(--accent-light); padding:8px 12px; border-radius:0 6px 6px 0; margin-top:4px; }
.wsk-chat-avatar { flex:0 0 30px; height:30px; display:grid; place-items:center; background:var(--accent); color:white; border-radius:50%; font-size:13px; font-weight:700; margin-top:3px; }
.wsk-chat-user { justify-content:flex-end; }
.wsk-chat-user .wsk-chat-message-body { max-width:80%; padding:12px 18px; border-radius:22px 22px 6px 22px; background:var(--wsk-border-light); }
.wsk-chat-thinking { display:flex; align-items:center; gap:10px; padding:12px 16px; background:var(--wsk-card); border-radius:18px; }
/* Owner 2026-10-28: the words show beside the dots, and alone when motion is reduced. */
.wsk-chat-thinking-dots { display:inline-flex; gap:5px; }
.wsk-chat-thinking-label { font-size:15px; color:var(--wsk-text-secondary); }
.wsk-chat-thinking i { width:6px; height:6px; border-radius:50%; background:var(--wsk-meta); animation:wsk-chat-pulse 1.2s infinite ease-in-out; }
.wsk-chat-thinking i:nth-of-type(2) { animation-delay:.15s; }
.wsk-chat-thinking i:nth-of-type(3) { animation-delay:.3s; }
@keyframes wsk-chat-pulse { 0%,70%,100% { opacity:.35; transform:translateY(0); } 35% { opacity:1; transform:translateY(-3px); } }
@media (prefers-reduced-motion:reduce) { .wsk-chat-thinking i { animation:none; } }
.wsk-chat-composer { gap:6px; padding:12px 14px 10px; border:1px solid var(--wsk-border); border-radius:22px; background:var(--wsk-card); box-shadow:0 3px 12px #00000008; }
.wsk-chat-composer label { position:absolute; width:1px; height:1px; overflow:hidden; clip-path:inset(50%); }
.wsk-chat-composer textarea { min-height:40px; max-height:156px; resize:none; border:0; border-radius:8px; padding:6px 4px; line-height:24px; background:transparent; box-sizing:border-box; }
.wsk-chat-composer textarea:focus { box-shadow:none; outline:2px solid transparent; }
.wsk-chat-composer:focus-within { border-color:var(--accent); }
.wsk-chat-actions { display:flex; align-items:center; gap:8px; }
.wsk-chat-tool { width:36px; height:36px; display:inline-grid; place-items:center; border:0; border-radius:50%; color:var(--wsk-text-secondary); background:transparent; cursor:pointer; flex-shrink:0; }
.wsk-chat-tool:hover,.wsk-chat-tool.is-listening { background:var(--wsk-border-light); color:var(--accent); }
.wsk-chat-tool.is-listening { outline:2px solid var(--accent); }
.wsk-chat-tool:disabled { opacity:.4; cursor:not-allowed; }
.wsk-chat-composer-note { flex:1; font-size:12px; color:var(--wsk-meta); }
.wsk-chat-actions .wsk-btn-primary { margin-left:auto; padding:8px 18px; border-radius:999px; }
.wsk-chat-hint { text-align:center; font-size:11px; padding:8px 4px 0; }
#shell-chat-status:empty { display:none; }
#shell-chat-status { font-size:13px; color:var(--wsk-text-secondary); padding:3px 4px; }
.wsk-chat-copy { margin-top:9px; padding:3px 7px; font-size:12px; color:var(--wsk-meta); background:transparent; border:1px solid transparent; border-radius:6px; cursor:pointer; }
.wsk-chat-copy:hover { border-color:var(--wsk-border); }
/* KAN-443: the numbered source chips are gone; the Sources (N) list below is the only control,
   closed until clicked, and keeps the 8px the chip row used to leave above it. */
.wsk-chat-source-details { margin:8px 0; font-size:13px; color:var(--wsk-text-secondary); }
.wsk-chat-source-details summary { cursor:pointer; }
/* KAN-443 lane D: reply drafts as cards, "fill in" tags for blanks the model left, and the caret of
   the typed reveal. The cards fade in one by one (the script un-hides them in turn); the colours
   are the page's own tokens, and the yellow tag is the one place a blank must stand out. */
.wsk-chat-drafts { display:grid; grid-template-columns:repeat(auto-fill,minmax(min(100%,300px),1fr)); gap:12px; margin:14px 0 12px; }
.wsk-chat-drafts[hidden], .wsk-chat-draft[hidden] { display:none; }
.wsk-chat-draft { min-width:0; border:1px solid var(--wsk-border-light); border-radius:14px; padding:14px; background:var(--wsk-card); animation:wsk-chat-draft-in .35s ease both; }
@keyframes wsk-chat-draft-in { from { opacity:0; transform:translateY(6px); } to { opacity:1; transform:none; } }
.wsk-chat-draft-top { display:flex; align-items:center; gap:10px; margin-bottom:8px; }
.wsk-chat-draft-avatar { flex:none; width:30px; height:30px; border-radius:50%; display:grid; place-items:center; background:var(--accent-light); color:var(--accent); font-size:13px; font-weight:700; }
.wsk-chat-draft-who { min-width:0; display:flex; flex-direction:column; }
.wsk-chat-draft-name { font-size:14px; line-height:1.3; color:var(--wsk-heading); }
.wsk-chat-draft-subject { font-size:12px; line-height:1.3; color:var(--wsk-meta); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* KAN-513: a drafted email is a card inside the chat: who it is for, To and Subject, and the body in a
   box the clerk can edit (a text box, so the model's words can never become markup). */
.wsk-chat-draft-name { font-weight:700; }
.wsk-chat-draft-row { display:flex; gap:8px; align-items:baseline; margin:0 0 4px; font-size:14px; line-height:1.4; min-width:0; }
.wsk-chat-draft-label { flex:none; width:58px; font-size:12px; font-weight:700; text-transform:uppercase; letter-spacing:.03em; color:var(--wsk-meta); }
.wsk-chat-draft-row .wsk-chat-draft-to, .wsk-chat-draft-row .wsk-chat-draft-subject { min-width:0; font-size:14px; color:var(--wsk-text); white-space:normal; overflow:visible; overflow-wrap:anywhere; }
.wsk-chat-draft-text { display:block; box-sizing:border-box; width:100%; margin:8px 0 0; font:inherit; font-size:15px; line-height:1.55; color:var(--wsk-text); background:var(--wsk-bg); border:1px solid var(--wsk-border); border-radius:10px; padding:10px 12px; white-space:pre-wrap; overflow-wrap:anywhere; resize:vertical; min-height:7em; }
.wsk-chat-draft-text:focus { outline:2px solid var(--accent); outline-offset:1px; border-color:var(--accent); }
.wsk-chat-draft-fills { margin:8px 0 0; font-size:13px; line-height:1.6; color:var(--wsk-text-secondary); }
.wsk-chat-fill { background:#fff4c7; color:#6b5300; border-radius:6px; padding:0 6px; font-size:13px; font-weight:700; white-space:nowrap; }
.wsk-chat-draft .wsk-chat-draft-note { margin:8px 0 0; font-size:12px; line-height:1.4; color:var(--wsk-text-secondary); }
.wsk-chat-draft-actions { display:flex; flex-wrap:wrap; gap:8px; margin-top:10px; }
.wsk-chat-draft-actions:empty { min-height:34px; }   /* the row is held while the answer types, so the buttons arriving do not move the card */
.wsk-chat-draft-actions .wsk-btn-primary, .wsk-chat-draft-actions .wsk-btn-outline { padding:7px 12px; font-size:13px; border-radius:8px; }
.wsk-chat-message-body .is-typing::after { content:''; display:inline-block; width:2px; height:1.1em; margin-left:1px; vertical-align:-2px; background:var(--accent); animation:wsk-chat-caret 1s steps(1) infinite; }
@keyframes wsk-chat-caret { 50% { opacity:0; } }
@media (prefers-reduced-motion:reduce) { .wsk-chat-draft { animation:none; } .wsk-chat-message-body .is-typing::after { animation:none; } }
.wsk-chat-muted { color:var(--wsk-meta); font-size:13px; }
.wsk-chat-error .wsk-chat-message-body { border-left:3px solid var(--wsk-border); padding-left:12px; }
.wsk-chat-retry { font-size:13px; padding:6px 12px; }
.wsk-chat-attachments { display:flex; flex-wrap:wrap; gap:8px; }
.wsk-chat-attachments:empty { display:none; }
.wsk-chat-attachment { display:flex; align-items:center; gap:7px; padding:5px 8px; border:1px solid var(--wsk-border); border-radius:9px; font-size:12px; max-width:100%; box-sizing:border-box; }
.wsk-chat-attachment > span { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:240px; }
.wsk-chat-attachment small { white-space:nowrap; color:var(--wsk-meta); }
.wsk-chat-attachment button { border:0; background:transparent; color:var(--wsk-text); cursor:pointer; font-size:20px; width:24px; height:24px; }
.wsk-chat-message-body .wsk-chat-file-tiles { display:flex; flex-wrap:wrap; gap:8px; list-style:none; padding:0; margin:10px 0 0; }
.wsk-chat-file-tile { display:flex; align-items:center; flex-wrap:wrap; gap:7px; border:1px solid var(--wsk-border); border-radius:9px; padding:8px 10px; font-size:13px; background:var(--wsk-card); }
.wsk-chat-file-tile small { flex-basis:100%; color:var(--wsk-meta); }
.wsk-chat-file-icon { color:var(--accent); }
.wsk-chat-drop { outline:2px dashed var(--accent); outline-offset:-3px; border-radius:12px; }
@media (max-width:600px) {
  .wsk-chat-main { width:calc(100% - 20px); margin:10px auto 10px; }
  .wsk-chat-heading { margin-bottom:6px; }
  .wsk-chat-messages { padding:10px 2px 16px; }
  .wsk-chat-empty h2 { font-size:23px; }
  .wsk-chat-empty > p { font-size:14px; margin-bottom:16px; }
  .wsk-chat-suggestion { padding:11px 12px; font-size:12px; border-radius:12px; }
  .wsk-chat-message { gap:8px; margin-bottom:20px; }
  .wsk-chat-message-body { font-size:15px; max-width:calc(100% - 38px); }
  .wsk-chat-user .wsk-chat-message-body { max-width:88%; padding:10px 14px; }
  .wsk-chat-composer { padding:9px 10px; border-radius:18px; }
  .wsk-chat-composer-note { font-size:10px; }
  .wsk-chat-hint { font-size:10px; }
  .wsk-chat-attachment > span { max-width:180px; }
}
/* 2026-10-06: short screens tighten the welcome block (mark, headline gaps, chip height) instead of
   overflowing. Written after the max-width:600px block, so each value here is no larger than that
   block's own where both apply (a short phone keeps its 23px headline). */
@media (max-height:800px) {
  .wsk-chat-messages { padding-top:8px; padding-bottom:12px; }
  .wsk-chat-welcome-mark { width:36px; height:36px; }
  .wsk-chat-empty h2 { font-size:23px; margin:12px 0 6px; }
  .wsk-chat-empty > p { margin-bottom:16px; }
  .wsk-chat-empty .wsk-chat-suggestions { gap:8px; }
  .wsk-chat-suggestion { padding:10px 12px; }
}
@media (max-height:680px) {
  .wsk-chat-messages { padding-top:4px; padding-bottom:8px; }
  .wsk-chat-welcome-mark { width:28px; height:28px; font-size:14px; }
  .wsk-chat-empty h2 { font-size:20px; margin:8px 0 4px; }
  .wsk-chat-empty > p { font-size:13px; margin-bottom:8px; }
  .wsk-chat-empty .wsk-chat-suggestions { gap:6px; }
  .wsk-chat-suggestion { padding:6px 12px; line-height:1.3; }
}
.wsk-chat-message-body ul,.wsk-chat-message-body ol { margin:12px 0; }

/* ---- Notifications drawer (the bell) ---------------------------------------
   A right-hand drawer, full height, full width on a phone. The header (title,
   unread count, Mark all read, Refresh, Close, status) is pinned; only
   .wsk-notice-body scrolls. Inside it the list is grouped by day with sticky
   headers; each notice is ONE large link (title, who has it, when) beside a
   separate Hide button. Unread = bold title + accent bar + dot + a hidden
   "Unread" for screen readers; read = quiet. The rows are drawn by
   workspace_shell.js (they are not in any server template), so the classes
   below are the contract with that file and tests/test_notifications_drawer.py.
   Colours and the font come from the tokens above; there is no dark theme to
   follow here. */
#shell-notifications { width: min(440px, 100vw); max-width: 100vw; height: 100dvh; max-height: 100dvh; margin: 0 0 0 auto; padding: 0; border: 0; border-left: 1px solid var(--wsk-border); border-radius: 0; background: var(--wsk-card); color: var(--wsk-text); box-shadow: -12px 0 40px #0003; overflow: hidden; }
#shell-notifications[open] { display: flex; flex-direction: column; animation: wsk-notice-in .18s ease-out; }
@keyframes wsk-notice-in { from { transform: translateX(28px); opacity: 0; } to { transform: none; opacity: 1; } }
#shell-notifications::backdrop { background: #0005; }

#shell-notifications .wsk-notice-head { flex: 0 0 auto; padding: 18px 16px 12px 20px; border-bottom: 1px solid var(--wsk-border-light); background: var(--wsk-card); }
.wsk-notice-titlebar { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.wsk-notice-titles { flex: 1 1 auto; min-width: 0; }
.wsk-notice-groups { margin: 0; }
#shell-notifications h2 { margin: 0; font-size: 24px; line-height: 1.2; color: var(--wsk-heading); }
.wsk-notice-summary { min-height: 22px; margin: 4px 0 0; font-size: 16px; line-height: 22px; color: var(--wsk-text-secondary); }
.wsk-notice-toolbar { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
#shell-notifications .wsk-notice-button { display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: 44px; padding: 0 16px; border: 1px solid var(--wsk-border); border-radius: 10px; background: var(--wsk-card); color: var(--wsk-text); font: inherit; font-size: 16px; font-weight: 700; cursor: pointer; }
#shell-notifications .wsk-notice-button:hover:not(:disabled) { border-color: var(--accent); background: var(--accent-light); }
#shell-notifications .wsk-notice-button:disabled { opacity: .55; cursor: default; }
#shell-notifications .wsk-notice-button:focus-visible, #shell-notifications .wsk-notice-close:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; box-shadow: none; }
#shell-notifications .wsk-notice-close { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px; margin: -6px -4px 0 0; padding: 0; border: 0; border-radius: 10px; background: transparent; color: var(--wsk-text); cursor: pointer; }
#shell-notifications .wsk-notice-close:hover { background: var(--wsk-border-light); }
#shell-notification-status { margin: 10px 0 0; font-size: 15px; color: var(--wsk-text-secondary); }
#shell-notification-status:empty { display: none; }
#shell-notifications[data-state="loading"] #shell-notification-status::before { content: ""; display: inline-block; width: 12px; height: 12px; margin-right: 8px; vertical-align: -1px; border: 2px solid var(--wsk-border); border-top-color: var(--accent); border-radius: 50%; animation: wsk-notice-spin .8s linear infinite; }
@keyframes wsk-notice-spin { to { transform: rotate(360deg); } }

#shell-notifications .wsk-notice-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 0 0 24px; }
.wsk-notice-group-head { position: sticky; top: 0; z-index: 1; display: flex; align-items: center; gap: 8px; margin: 0; padding: 10px 20px; background: var(--wsk-bg); border-bottom: 1px solid var(--wsk-border-light); font-size: 15px; font-weight: 700; line-height: 22px; color: var(--wsk-text-secondary); }
.wsk-notice-group-count { min-width: 24px; padding: 0 8px; border-radius: 999px; background: var(--wsk-border-light); color: var(--wsk-text); font-size: 13px; line-height: 22px; text-align: center; }
#shell-notifications .wsk-notice-list { list-style: none; margin: 0; padding: 0; }
.wsk-notice { border-bottom: 1px solid var(--wsk-border-light); border-left: 4px solid transparent; }
.wsk-notice-unread { border-left: 4px solid var(--accent); background: var(--wsk-card); }
.wsk-notice-read { background: transparent; }
.wsk-notice-main { display: flex; align-items: stretch; }
#shell-notifications .wsk-notice-link { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; justify-content: center; gap: 6px; min-height: 60px; padding: 12px 8px 12px 14px; color: inherit; text-decoration: none; }
#shell-notifications .wsk-notice-link:hover { background: var(--accent-light); }
#shell-notifications .wsk-notice-link:focus-visible { outline: 3px solid var(--accent); outline-offset: -3px; }
#shell-notifications .wsk-notice-title { display: block; font-size: 17px; line-height: 1.35; overflow-wrap: anywhere; }
.wsk-notice-unread .wsk-notice-title { font-weight: 700; color: var(--wsk-heading); }
.wsk-notice-read .wsk-notice-title { font-weight: 400; color: var(--wsk-text-secondary); }
.wsk-notification-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; font-size: 14px; line-height: 22px; color: var(--wsk-text-secondary); }
.wsk-notification-unread { width: 10px; height: 10px; flex: 0 0 10px; border-radius: 50%; background: var(--accent); }
.wsk-notice-pill { display: inline-block; max-width: 100%; padding: 0 10px; border: 1px solid var(--wsk-border); border-radius: 999px; background: var(--wsk-card); color: var(--wsk-text); font-size: 14px; line-height: 22px; overflow-wrap: anywhere; }
.wsk-notice-pill-mine { border-color: transparent; background: var(--accent-light); color: var(--accent-hover); font-weight: 700; }
.wsk-notice-pill-review { border-color: #f0c36d; background: #fff8e6; color: #3b2f0b; font-weight: 700; }
.wsk-notice-count { min-width: 24px; padding: 0 8px; border-radius: 999px; background: var(--accent); color: #fff; font-size: 13px; font-weight: 700; line-height: 22px; text-align: center; }
#shell-notifications .wsk-notification-hide { flex: 0 0 auto; align-self: center; min-width: 56px; min-height: 44px; margin-right: 6px; padding: 0 10px; border: 0; border-radius: 8px; background: transparent; color: var(--wsk-text-secondary); font: inherit; font-size: 15px; cursor: pointer; }
#shell-notifications .wsk-notification-hide:hover { background: var(--wsk-border-light); color: var(--wsk-text); }
#shell-notifications .wsk-notification-hide:focus-visible { outline: 3px solid var(--accent); outline-offset: 0; }
#shell-notifications .wsk-notification-hide:disabled { opacity: .5; cursor: default; }
#shell-notifications .wsk-notice .wsk-notification-detail { margin: -2px 16px 10px 14px; font-size: 15px; line-height: 1.4; color: var(--wsk-text-secondary); overflow-wrap: anywhere; }
#shell-notifications .wsk-notice .wsk-notification-allow { display: block; width: calc(100% - 30px); margin: 0 16px 14px 14px; min-height: 44px; padding: 8px 14px; font-size: 16px; }
.wsk-notice-empty { display: grid; justify-items: center; gap: 4px; padding: 56px 24px 32px; text-align: center; color: var(--wsk-text-secondary); font-size: 16px; }
.wsk-notice-empty svg { color: var(--wsk-icon); margin-bottom: 8px; }
.wsk-notice-empty p { margin: 0; }
.wsk-notice-empty .wsk-notice-empty-title { font-size: 20px; font-weight: 700; color: var(--wsk-heading); }
#shell-notifications .wsk-notification-help { margin: 16px 20px 0; font-size: 14px; line-height: 1.5; }
#shell-notifications #shell-notification-documents-group { margin: 20px 0 0; }
#shell-notifications #shell-notification-documents-group summary { cursor: pointer; margin: 0; padding: 12px 20px; min-height: 44px; font-size: 16px; font-weight: 700; color: var(--wsk-text); }
#shell-notifications #shell-notification-documents-group summary:focus-visible { outline: 3px solid var(--accent); outline-offset: -3px; }
@media (max-width: 480px) {
  #shell-notifications { width: 100vw; }
  #shell-notifications .wsk-notice-head { padding: 14px 12px 10px 16px; }
  #shell-notifications h2 { font-size: 22px; }
  .wsk-notice-group-head { padding-left: 16px; padding-right: 16px; }
}
@media (prefers-reduced-motion: reduce) {
  #shell-notifications[open] { animation: none; }
  #shell-notifications[data-state="loading"] #shell-notification-status::before { animation: none; }
}

/* KAN-438: quiet Home/calendar metadata. */
#schedule-card .wsk-calendar-zone { margin: 2px 0; font-size: 12px; }
/* ---- upload page + modern selects ---------------------------------------------
   Owner order 2026-10-06: "make all dropdowns not look ancient, take majority of
   screen to upload files". Everything new for that lives in this ONE block (peer
   lanes also append to the end of this file; keep their blocks and this one
   apart). Two parts: (1) every native <select> in the hub, (2) the Upload page.

   CSP: style-src is 'self' with no style-src-attr (hub/security.py), so nothing
   here may need an inline style, and nothing here fetches an image or a data:
   URI. The select's chevron is therefore DRAWN, as two thin diagonal gradient
   strokes meeting in a V, not an SVG background. */

/* (1) Modern selects. Tokens first: a variant changes the tokens, never the
   rule. Compact is 36px. */
:root {
  --wsk-select-h: 40px;
  --wsk-select-r: 10px;
  --wsk-select-fs: 14px;
  --wsk-select-pl: 12px;
  --wsk-select-ink: 12px;   /* chevron's distance from the right edge */
  --wsk-select-cs: 7px;     /* each of the chevron's two strokes is this wide and tall */
  --wsk-select-gap: 12px;   /* clear space between the text and the chevron */
  --wsk-select-chevron: var(--wsk-text-secondary);
}

/* The one rule. Bare `select` (not a class) so a select added tomorrow is
   covered without anyone remembering a class; "multiple" and "size" lists are
   not dropdowns and are left to the platform (tests/test_modern_selects.py
   fails the build if a template ever emits one). Its specificity beats the
   older per-class chrome (.ws-input-sm, .wsk-input, .wsk-task-editor select)
   on purpose, so the look cannot drift per page. */
select:not([multiple]):not([size]) {
  -webkit-appearance: none;
  appearance: none;
  box-sizing: border-box;
  height: var(--wsk-select-h);
  min-height: var(--wsk-select-h);
  max-width: 100%;
  margin: 0;
  padding: 0 calc(var(--wsk-select-ink) + 2 * var(--wsk-select-cs) + var(--wsk-select-gap)) 0 var(--wsk-select-pl);
  border: 1px solid var(--wsk-border);
  border-radius: var(--wsk-select-r);
  background-color: var(--wsk-card);
  background-image:
    linear-gradient(to top right, transparent calc(50% - 1.2px), var(--wsk-select-chevron) calc(50% - .5px), var(--wsk-select-chevron) calc(50% + .5px), transparent calc(50% + 1.2px)),
    linear-gradient(to top left, transparent calc(50% - 1.2px), var(--wsk-select-chevron) calc(50% - .5px), var(--wsk-select-chevron) calc(50% + .5px), transparent calc(50% + 1.2px));
  background-repeat: no-repeat;
  background-size: var(--wsk-select-cs) var(--wsk-select-cs);
  background-position: right calc(var(--wsk-select-ink) + var(--wsk-select-cs)) center, right var(--wsk-select-ink) center;
  color: var(--wsk-text);
  font-family: inherit;
  font-size: var(--wsk-select-fs);
  font-weight: 400;
  line-height: 1.25;
  text-overflow: ellipsis;
  cursor: pointer;
  transition: border-color .12s ease, box-shadow .12s ease, background-color .12s ease;
}
select:not([multiple]):not([size]):hover:not(:disabled) { border-color: var(--wsk-icon); }
select:not([multiple]):not([size]):focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-light); }
select:not([multiple]):not([size]):focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
select:not([multiple]):not([size]):disabled {
  cursor: not-allowed; color: var(--wsk-meta); background-color: var(--wsk-bg);
  border-color: var(--wsk-border-light); --wsk-select-chevron: var(--wsk-icon);
}
select option { color: var(--wsk-text); background: var(--wsk-card); }
/* Windows high-contrast: the drawn chevron is a background image, which that
   mode removes, so hand those users the platform's own select back. */
@media (forced-colors: active) {
  select:not([multiple]):not([size]) { appearance: auto; }
}

/* Variants: tokens only. */
select.ws-input-sm { --wsk-select-h: 36px; }
.wsk-admin-panel select { --wsk-select-h: 42px; --wsk-select-fs: 15px; }
.wsk-auth-form select { --wsk-select-h: 44px; --wsk-select-fs: 15px; }
/* The sign-up form's "Town of" prefix is one half of a joined control with the
   text field beside it: keep the join (flat right edge, shared radius). */
.wsk-auth-prefixed-input > select.wsk-auth-input-prefix {
  --wsk-select-h: 44px; --wsk-select-r: 8px 0 0 8px;
  border-right-width: 0; background-color: var(--wsk-cream); font-weight: 700;
}

/* Where the browser supports customizable selects (Chromium 135+), the OPEN
   list is styled too: a rounded panel with a soft shadow, roomy options and a
   check on the chosen one. Everywhere else this whole block is skipped and the
   closed control above is the whole story (the platform draws the open list).
   One keyboard difference comes with the upgrade: Chromium opens the list on
   ArrowDown instead of changing the value in place, which is that control's own
   behaviour. Only <select> elements are touched: the composer's icon menu
   buttons and the question card's choices are not selects and are not reached. */
@supports (appearance: base-select) {
  select:not([multiple]):not([size]),
  select:not([multiple]):not([size])::picker(select) {
    appearance: base-select;
  }
  select:not([multiple]):not([size]) {
    background-image: none;
    padding: 0 var(--wsk-select-pl);
    align-items: center;
    justify-content: space-between;
    gap: 10px;
  }
  /* The same V the closed fallback draws, here as two borders of a small
     box, so it does not change shape when the browser upgrades the control. */
  select:not([multiple]):not([size])::picker-icon {
    content: "";
    flex: none;
    width: 7px;
    height: 7px;
    margin: -4px 4px 0 0;
    border: solid var(--wsk-select-chevron);
    border-width: 0 1.6px 1.6px 0;
    rotate: 45deg;
    transition: rotate .15s ease, margin .15s ease;
  }
  select:not([multiple]):not([size]):open::picker-icon { rotate: 225deg; margin-top: 4px; }
  select:not([multiple]):not([size])::picker(select) {
    margin-block: 6px;
    padding: 6px;
    border: 1px solid var(--wsk-border);
    border-radius: 12px;
    background: var(--wsk-card);
    box-shadow: 0 12px 32px rgba(38, 36, 29, .16), 0 2px 6px rgba(38, 36, 29, .08);
  }
  select:not([multiple]):not([size]) option {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 38px;
    padding: 8px 12px;
    border-radius: 8px;
    font-size: var(--wsk-select-fs);
    cursor: pointer;
  }
  select:not([multiple]):not([size]) option:hover,
  select:not([multiple]):not([size]) option:focus-visible { background: var(--accent-light); outline: none; }
  select:not([multiple]):not([size]) option:checked { font-weight: 700; }
  select:not([multiple]):not([size]) option::checkmark { order: 1; margin-left: auto; color: var(--accent); }
  select:not([multiple]):not([size]) option:disabled { color: var(--wsk-meta); cursor: not-allowed; }
}

/* (2) The Upload page. One card: a big drop zone, ONE compact action row, ONE
   short muted line. The zone is most of the screen on purpose. */
.wsk-upload-form { display: flex; flex-direction: column; gap: 14px; box-sizing: border-box; }
/* The file input COVERS the zone at zero opacity and is the drop target
   itself: dropping files onto a file input assigns them natively, so drag and
   drop needs no script, and the keyboard path, the focus ring and the picker
   stay the native ones. The face is the visible box; its pointer-events are
   off so it never intercepts the drop, and only the remove buttons are raised
   above the input. */
.wsk-drop { position: relative; display: flex; }
.wsk-drop-large { min-height: max(320px, 62vh); }
.wsk-drop-input {
  position: absolute; inset: 0; z-index: 1;
  width: 100%; height: 100%; margin: 0; padding: 0;
  opacity: 0; cursor: pointer;
}
.wsk-drop-face {
  flex: 1 1 auto; min-width: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 22px;
  padding: 32px 24px; box-sizing: border-box; pointer-events: none;
  border: 2px dashed var(--wsk-border); border-radius: 16px;
  border-color: color-mix(in srgb, var(--accent) 38%, #fff);
  background: var(--wsk-bg);
  background: color-mix(in srgb, var(--accent-light) 45%, #fff);
  color: var(--wsk-text); text-align: center;
  transition: border-color .12s ease, background-color .12s ease, box-shadow .12s ease;
}
.wsk-drop-prompt { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.wsk-drop-badge {
  display: grid; place-items: center; width: 88px; height: 88px; margin-bottom: 8px;
  border-radius: 50%; background: var(--accent-light); color: var(--accent);
  transition: transform .15s ease, background-color .12s ease;
}
.wsk-drop-icon { width: 44px; height: 44px; }
.wsk-drop-lead { font-size: 22px; font-weight: 700; line-height: 1.25; color: var(--wsk-heading); }
.wsk-drop-sub { font-size: 15px; color: var(--wsk-text-secondary); }
.wsk-drop-input:hover + .wsk-drop-face { border-color: var(--accent); background: var(--accent-light); }
.wsk-drop-input:focus-visible + .wsk-drop-face {
  border-color: var(--accent); box-shadow: 0 0 0 4px var(--accent-light);
}
/* A file is being dragged over the zone (static/upload.js adds the class). */
.wsk-drop-over .wsk-drop-face {
  border-color: var(--accent); border-width: 3px; background: var(--accent-light);
  box-shadow: 0 0 0 4px var(--accent-light);
}
.wsk-drop-over .wsk-drop-badge { transform: scale(1.1); background: #fff; }
/* Files chosen. The input is `required`, so :valid is exactly "the clerk has
   picked something", which gives the zone its ready look with no script at
   all; static/upload.js adds .wsk-drop-ready for the same state and draws the
   list. */
.wsk-drop-input:valid + .wsk-drop-face,
.wsk-drop-ready .wsk-drop-face {
  border-style: solid; border-color: var(--accent);
  background: var(--accent-light);
}
.wsk-drop-ready .wsk-drop-face { gap: 18px; }
.wsk-drop-ready .wsk-drop-badge { width: 64px; height: 64px; background: #fff; }
.wsk-drop-ready .wsk-drop-icon { width: 32px; height: 32px; }
.wsk-drop-ready .wsk-drop-sub { color: var(--wsk-text); }
/* The chosen files, inside the zone, above the covering input so their remove
   buttons can be pressed. Only the buttons take the pointer; everything else
   falls through to the input, so a click on a file row still opens the picker. */
.wsk-drop-files {
  position: relative; z-index: 2; pointer-events: none;
  display: flex; flex-direction: column; gap: 8px;
  width: min(560px, 100%); margin: 0; padding: 0; list-style: none; text-align: left;
}
.wsk-drop-files[hidden] { display: none; }
.wsk-drop-file {
  display: flex; align-items: center; gap: 12px; min-width: 0;
  padding: 6px 6px 6px 14px; background: var(--wsk-card);
  border: 1px solid var(--wsk-border-warm); border-radius: 10px; font-size: 14px;
}
.wsk-drop-file-name {
  flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-weight: 700; color: var(--wsk-heading);
}
.wsk-drop-file-size { flex: none; font-size: 13px; color: var(--wsk-text-secondary); }
/* KAN-490: per-file send progress, drawn by static/upload.js while a batch uploads. */
.wsk-drop-file-progress { flex: none; width: 96px; height: 10px; }
.wsk-drop-remove {
  position: relative; z-index: 2; pointer-events: auto; flex: none;
  width: 36px; height: 36px; padding: 0; border: 0; border-radius: 50%;
  background: transparent; color: var(--wsk-text-secondary);
  font: inherit; font-size: 22px; line-height: 1; cursor: pointer;
}
.wsk-drop-remove:hover { background: var(--accent-light); color: var(--accent); }
.wsk-drop-remove:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* The action row: label + modern select on the left, Upload on the right. A
   docked bar that stays at the foot of the visible screen while the zone is
   taller than the window, so the Upload button never ends up below the fold;
   once the page is scrolled to the end it simply rests under the zone. */
.wsk-upload-controls {
  display: flex; align-items: center; flex-wrap: wrap; gap: 12px;
  position: sticky; bottom: 12px; z-index: 3;
  padding: 10px 12px 10px 18px; box-sizing: border-box;
  background: var(--wsk-card); border: 1px solid var(--wsk-border); border-radius: 14px;
  box-shadow: 0 6px 20px rgba(38, 36, 29, .10);
}
.wsk-upload-field {
  display: flex; align-items: center; gap: 10px;
  font-size: 14px; font-weight: 700; color: var(--wsk-text-secondary);
}
.wsk-upload-controls select { min-width: 200px; }
.wsk-upload-controls .wsk-btn-primary {
  margin-left: auto; height: 40px; padding: 0 26px; border-radius: 10px;
}
@media (prefers-reduced-motion: reduce) {
  .wsk-drop-face, .wsk-drop-badge, select:not([multiple]):not([size]) { transition: none; }
}
@media (max-width: 700px) {
  .wsk-drop-large { min-height: max(320px, 55vh); }
  .wsk-drop-face { padding: 24px 16px; }
  .wsk-drop-lead { font-size: 19px; }
  /* One compact row on a phone too: the label sits over the select, and the
     Upload button stays beside them instead of becoming a second full row. */
  .wsk-upload-controls { flex-wrap: nowrap; align-items: flex-end; padding: 10px 12px; bottom: 8px; }
  .wsk-upload-field { flex: 1 1 0; flex-direction: column; align-items: stretch; gap: 4px; min-width: 0; }
  .wsk-upload-controls select { min-width: 0; width: 100%; }
  .wsk-upload-controls .wsk-btn-primary { flex: none; margin-left: 0; padding: 0 20px; }
}

/* KAN-439: a readable mail window with a single primary action bar. */
.wsk-composer { min-width: 0; color: #263238; }
.wsk-composer-head h2 { font-size: 24px; }
.wsk-mail-fields { margin: 12px 0 22px; font-size: 16px; }
.wsk-mail-fields > div { display: flex; gap: 16px; padding: 12px 0; border-bottom: 1px solid #e1e5eb; }
.wsk-mail-fields dt { flex: 0 0 64px; color: #56616e; }
.wsk-mail-fields dd { margin: 0; overflow-wrap: anywhere; min-width: 0; }
.wsk-mail-fields dd span { color: #56616e; }
/* Icons stay at the bottom of the writing area; menus open above them. */
.wsk-rt { overflow: visible; }
.wsk-composer .wsk-rt-bar { position: relative; padding: 6px; border-top: 1px solid #dce3eb; border-bottom: 0; border-radius: 0 0 8px 8px; background: #f6f8fb; flex-wrap: nowrap; }
.wsk-rt-tool { display: inline-flex; flex: 0 0 auto; }
.wsk-rt-divider { border-left: 1px solid #c4ccd7; padding-left: 1px; margin-left: 1px; }
.wsk-composer .wsk-rt-btn { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 40px; min-width: 40px; height: 40px; min-height: 40px; padding: 0; color: #344054; }
.wsk-rt-btn > svg { width: 20px; height: 20px; flex-shrink: 0; }
.wsk-rt-btn > .wsk-rt-caret { position: absolute; right: 0; width: 9px; height: 12px; }
.wsk-composer .wsk-rt-btn[aria-pressed="true"], .wsk-composer .wsk-rt-btn[aria-expanded="true"] { background: #d3e3fd; color: #0842a0; border-color: #94b8ed; }
.wsk-rt-menu, .wsk-rt-overflow { position: absolute; z-index: 20; bottom: calc(100% + 4px); left: 4px; max-width: calc(100% - 8px); padding: 6px; border: 1px solid #c4ccd7; border-radius: 8px; background: white; box-shadow: 0 6px 20px #18223026; }
.wsk-rt-menu { width: 218px; }
.wsk-rt-menu button { display: flex; align-items: center; gap: 10px; width: 100%; min-height: 40px; padding: 8px 12px; border: 0; border-radius: 4px; background: #fff; color: #263238; font: inherit; font-size: 16px; text-align: left; cursor: pointer; }
.wsk-rt-menu button:hover, .wsk-rt-menu [aria-checked="true"] { background: #e8f0fe; }
.wsk-rt-menu[hidden], .wsk-rt-overflow[hidden], .wsk-composer .wsk-rt-more-button { display: none; }
.wsk-rt-compact { flex-wrap: wrap !important; }
.wsk-composer .wsk-rt-compact .wsk-rt-more-button { display: inline-flex; }
.wsk-rt-overflow { left: auto; right: 4px; display: grid; grid-template-columns: repeat(5, 44px); gap: 4px; }
.wsk-rt-overflow .wsk-rt-divider { border: 0; padding: 0; margin: 0; }
.wsk-rt-overflow .wsk-rt-menu { bottom: calc(100% + 4px); left: auto; right: 0; }
.wsk-composer .wsk-rt-surface, .wsk-composer .wsk-reply-editor { min-height: 400px; font-size: 18px; line-height: 1.6; padding: 18px; overflow-wrap: anywhere; }
.wsk-composer .wsk-rt-surface p { line-height: 1.6; }
.wsk-composer :is(button, input, textarea, summary, a, [contenteditable]):focus-visible { outline: 3px solid #0b57d0; outline-offset: 3px; }
.wsk-composer .wsk-rt-surface:focus-visible { outline-offset: -3px; }
.wsk-composer .wsk-reply-attachments, .wsk-composer .wsk-qa { padding: 20px; margin: 18px 0; border: 1px solid #dce3eb; border-radius: 12px; background: #f7f9fc; }
.wsk-composer .wsk-att-title, .wsk-composer .wsk-qa-title { font-size: 18px; letter-spacing: 0; text-transform: none; color: #263238; }
.wsk-composer .wsk-reply-attachments p { font-size: 16px; line-height: 1.5; margin: 8px 0; }
.wsk-composer .wsk-reply-attachments .wsk-file-help { font-size: 14px; color: #56616e; }
.wsk-file-suggestions, .wsk-chosen-files { list-style: none; padding: 0; margin: 12px 0; }
.wsk-file-suggestions li { background: #fff; border: 1px solid #dce3eb; border-radius: 8px; padding: 14px; margin: 8px 0; overflow-wrap: anywhere; }
.wsk-composer .wsk-att-name { font-size: 16px; font-weight: 600; }
.wsk-chosen-files { display: flex; flex-wrap: wrap; gap: 8px; }
.wsk-file-chip { display: flex; align-items: center; gap: 12px; background: #e8f0fe; padding: 4px 10px; border-radius: 8px; min-width: 0; max-width: 100%; }
.wsk-file-chip span { overflow-wrap: anywhere; min-width: 0; }
.wsk-file-chip button { flex-shrink: 0; min-height: 44px; }
.wsk-reply-attachments input[type=file] { display: block; max-width: 100%; margin: 10px 0; font-size: 16px; }
.wsk-reply-attachments input[hidden] { display: none; }
.wsk-reply-attachments input::file-selector-button { min-height: 44px; border: 1px solid #a5b0c0; background: #fff; border-radius: 6px; padding: 8px 12px; margin-right: 10px; cursor: pointer; }
.wsk-composer .wsk-composer-actions { display: flex; align-items: center; gap: 8px; padding-top: 0; margin-top: 12px; }
.wsk-composer .wsk-composer-send { margin: 0; }
.wsk-composer .wsk-composer-save { margin-left: auto; }
.wsk-composer-actions button { min-height: 48px; font-size: 16px; padding: 12px 20px; }
.wsk-composer-actions .wsk-send-button { color: #fff; background: #0b57d0; border-color: #0b57d0; min-width: 120px; }
.wsk-composer-actions .wsk-send-button:hover { background: #0845a7; }
/* Reply sends; Save draft is the quiet secondary beside it. */
.wsk-composer-actions .wsk-save-draft { border-color: transparent; color: #344054; background: transparent; }
.wsk-composer-actions .wsk-save-draft:hover { background: #f2f4f7; }
/* What the clerk reads when Reply did not send (workspace._send_notice). */
.wsk-composer .wsk-send-notice { margin: 12px 0 0; padding: 12px 14px; border: 1px solid #f0c36d; border-left-width: 4px; border-radius: 8px; background: #fff8e6; color: #3b2f0b; font-size: 15px; }
.wsk-composer .wsk-send-notice p { margin: 0 0 6px; }
.wsk-composer .wsk-send-notice p:last-of-type { margin-bottom: 0; }
.wsk-composer .wsk-send-notice .wsk-composer-meta { color: #5c4a14; }
.wsk-composer .wsk-send-allow { margin: 10px 0 0; }
.wsk-composer .wsk-send-allow button { min-height: 44px; font-size: 16px; padding: 10px 18px; color: #fff; background: #0b57d0; border-color: #0b57d0; }
.wsk-composer-actions .wsk-attach-button { display: inline-flex; align-items: center; justify-content: center; width: 44px; min-width: 44px; padding: 0; border-color: transparent; color: #344054; }
@media (pointer: coarse), (max-width: 600px) {
  .wsk-composer .wsk-rt-btn { width: 44px; min-width: 44px; height: 44px; min-height: 44px; }
  .wsk-rt-menu button { min-height: 44px; }
}
@media (max-width: 600px) {
  .wsk-composer .wsk-rt-bar { display: grid; grid-template-columns: repeat(6, minmax(44px, 1fr)); }
  .wsk-rt-divider { padding: 0; margin: 0; border: 0; }
  .wsk-composer-actions .wsk-send-button { min-width: 88px; }
  .wsk-composer-actions button { padding: 10px 12px; }
  .wsk-composer .wsk-reply-attachments, .wsk-composer .wsk-qa { padding: 14px; }
  .wsk-mail-fields > div { gap: 8px; }
  .wsk-mail-fields dt { flex-basis: 56px; }
}

/* ==== KAN-439 round 4: the reply card, designed (owner order 2026-10-06) ====
   One hierarchy, top to bottom: the card title; the To / Subject rows; the
   writing frame with its formatting bar attached to the TOP of the text; one
   compact attachments row; then the action row where Reply is the single
   dominant control and Save draft stays quiet beside it.
   Rules of the system: an 8px spacing scale (4, 8, 12, 16, 24); one radius for
   frames (12px) and one for controls (8px); neutral surfaces and one blue,
   used only for the primary action and focus; 36px toolbar targets on a
   pointer, 44px on touch or a phone; every control keeps a visible focus ring.
   Scoped to .wsk-composer and placed last so it wins by order, not by weight. */
.wsk-composer {
  --rc-line: #d5dbe3; --rc-line-soft: #e6e9ef; --rc-surface: #f6f7f9; --rc-ink: #1f2933;
  --rc-muted: #52606d; --rc-blue: #0b57d0; --rc-blue-strong: #0842a0; --rc-chip: #eef3fb;
  color: var(--rc-ink);
}
.wsk-draft-panel { padding: 24px; }
.wsk-composer-head { margin: 0 0 8px; }
.wsk-composer-head h2 { font-size: 20px; line-height: 28px; font-weight: 600; letter-spacing: 0; color: var(--rc-ink); }
.wsk-composer .wsk-mail-fields { margin: 0 0 16px; border-top: 1px solid var(--rc-line-soft); }
.wsk-composer .wsk-mail-fields > div { display: flex; gap: 16px; padding: 8px 0; border: 0; border-bottom: 1px solid var(--rc-line-soft); }
.wsk-composer .wsk-mail-fields dt { flex: 0 0 64px; font-size: 14px; color: var(--rc-muted); }
.wsk-composer .wsk-mail-fields dd { font-size: 15px; color: var(--rc-ink); }
.wsk-composer .wsk-mail-fields dd span { color: var(--rc-muted); }

/* the writing frame: one border, the formatting bar attached on top */
.wsk-composer .wsk-rt { display: flex; flex-direction: column; border: 1px solid var(--rc-line); border-radius: 12px; background: #fff; overflow: visible; transition: border-color .15s ease, box-shadow .15s ease; }
.wsk-composer .wsk-rt:focus-within { border-color: var(--rc-blue); box-shadow: 0 0 0 3px rgba(11, 87, 208, .16); }
.wsk-composer .wsk-rt-bar { position: relative; gap: 2px; padding: 4px 8px; border: 0; border-bottom: 1px solid var(--rc-line-soft); border-radius: 12px 12px 0 0; background: var(--rc-surface); }
.wsk-composer .wsk-rt-btn { width: 36px; min-width: 36px; height: 36px; min-height: 36px; border: 0; border-radius: 6px; background: transparent; color: #3e4c59; }
.wsk-composer .wsk-rt-btn:hover { background: #e6eaf0; }
.wsk-composer .wsk-rt-btn[aria-pressed="true"], .wsk-composer .wsk-rt-btn[aria-expanded="true"] { background: #d7e5fb; color: var(--rc-blue-strong); }
.wsk-composer .wsk-rt-btn:focus-visible { outline-offset: 0; }
.wsk-composer .wsk-rt-divider { position: relative; margin-left: 12px; padding-left: 0; border-left: 0; }
.wsk-composer .wsk-rt-divider::before { content: ""; position: absolute; left: -7px; top: 8px; bottom: 8px; width: 1px; background: var(--rc-line); }
.wsk-composer .wsk-rt-surface, .wsk-composer .wsk-reply-editor { min-height: 360px; padding: 16px 20px; border: 0; border-radius: 0 0 12px 12px; font-size: 18px; line-height: 1.6; color: var(--rc-ink); }
.wsk-composer .wsk-rt-surface:focus-visible { outline: none; }
/* the bar is on top now, so its menus open downward */
.wsk-composer .wsk-rt-menu, .wsk-composer .wsk-rt-overflow { top: calc(100% + 4px); bottom: auto; border-color: var(--rc-line); border-radius: 8px; }
.wsk-composer .wsk-rt-overflow .wsk-rt-menu { top: calc(100% + 4px); bottom: auto; }
.wsk-composer .wsk-composer-meta { margin: 8px 0 0; font-size: 13px; color: var(--rc-muted); }

/* attachments: one compact row, not a card */
.wsk-composer .wsk-reply-attachments { margin: 12px 0 0; padding: 0; border: 0; border-radius: 0; background: transparent; }
.wsk-composer .wsk-att-bar { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; min-height: 36px; }
.wsk-composer .wsk-reply-attachments .wsk-att-title { margin: 0 4px 0 0; font-size: 14px; font-weight: 600; letter-spacing: 0; text-transform: none; color: var(--rc-muted); }
.wsk-composer .wsk-attach-button { display: inline-flex; align-items: center; justify-content: center; width: 36px; min-width: 36px; height: 36px; min-height: 36px; padding: 0; border: 1px solid var(--rc-line); border-radius: 8px; background: #fff; color: #3e4c59; }
.wsk-composer .wsk-attach-button:hover { background: #eef1f5; }
.wsk-composer [data-file-selection] { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; min-width: 0; }
.wsk-composer .wsk-reply-attachments [data-no-files], .wsk-composer .wsk-reply-attachments .wsk-att-none { margin: 0; font-size: 14px; color: var(--rc-muted); }
.wsk-composer .wsk-chosen-files { margin: 0; gap: 8px; }
.wsk-composer .wsk-file-chip { gap: 4px; min-height: 36px; padding: 0 4px 0 12px; border: 1px solid #cddcf4; border-radius: 18px; background: var(--rc-chip); font-size: 14px; }
.wsk-composer .wsk-file-chip button { min-height: 28px; padding: 0 10px; border: 0; background: transparent; color: var(--rc-blue-strong); font-size: 13px; font-weight: 600; }
.wsk-composer .wsk-reply-attachments .wsk-file-help { margin: 4px 0 0; font-size: 13px; color: var(--rc-muted); }
.wsk-composer .wsk-reply-attachments .wsk-att-lead { margin: 16px 0 0; font-size: 14px; font-weight: 600; color: var(--rc-ink); }
.wsk-composer .wsk-reply-attachments .wsk-att-note { margin: 2px 0 8px; font-size: 13px; color: var(--rc-muted); }
.wsk-composer .wsk-file-suggestions { display: grid; gap: 8px; margin: 0; padding: 0; }
.wsk-composer .wsk-file-suggestions li { margin: 0; padding: 8px 12px; border: 1px dashed var(--rc-line); border-radius: 8px; background: #fff; }
.wsk-composer .wsk-reply-attachments .wsk-file-suggestions li p { margin: 2px 0 0; font-size: 13px; color: var(--rc-muted); }
.wsk-composer .wsk-reply-attachments .wsk-att-name { font-size: 14px; font-weight: 600; color: var(--rc-ink); }

/* notice when Reply did not send */
.wsk-composer .wsk-send-notice { margin: 16px 0 0; padding: 12px 16px; border: 1px solid #ecc977; border-left-width: 4px; border-radius: 12px; background: #fffaf0; color: #3b2f0b; font-size: 15px; }
.wsk-composer .wsk-send-allow button { min-height: 44px; border-radius: 8px; font-weight: 600; }

/* actions: Reply dominates, Save draft is quiet beside it */
.wsk-composer .wsk-composer-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 16px 0 0; padding: 0; }
.wsk-composer .wsk-composer-send, .wsk-composer .wsk-composer-save { margin: 0; }
.wsk-composer .wsk-composer-actions .wsk-send-button { min-width: 128px; min-height: 44px; padding: 0 28px; border: 1px solid var(--rc-blue); border-radius: 8px; background: var(--rc-blue); color: #fff; font-size: 16px; font-weight: 600; box-shadow: 0 1px 2px rgba(8, 66, 160, .35); }
.wsk-composer .wsk-composer-actions .wsk-send-button:hover { background: var(--rc-blue-strong); }
.wsk-composer .wsk-composer-actions .wsk-send-button:disabled { border-color: #d5dbe3; background: #e9ecf1; color: #6b7785; box-shadow: none; }
.wsk-composer .wsk-composer-actions .wsk-save-draft { min-height: 44px; padding: 0 16px; border: 1px solid transparent; border-radius: 8px; background: transparent; color: #334155; font-size: 15px; font-weight: 600; }
.wsk-composer .wsk-composer-actions .wsk-save-draft:hover { background: #eef1f5; }
.wsk-composer .wsk-composer-note { margin: 8px 0 0; font-size: 13px; color: var(--rc-muted); }

/* IN FLIGHT (2026-10-11). hx-disabled-elt disables every button of the editor for
   the request; htmx marks the requesting form .htmx-request (the built-in
   indicator styles are off, so this is ours). The label becomes the button's own
   data-busy-label - "Sending..." / "Saving..." - drawn over the same box, so the
   button keeps its size and nothing around it moves. Reply stays blue (working,
   not unavailable); Save draft keeps the quiet disabled look. */
.wsk-composer .wsk-composer-actions .htmx-request .wsk-send-button[data-busy-label]:disabled {
  position: relative; color: transparent; border-color: var(--rc-blue); background: var(--rc-blue);
  box-shadow: none; opacity: .88; cursor: progress;
}
.wsk-composer .wsk-composer-actions .htmx-request .wsk-send-button[data-busy-label]::after {
  content: attr(data-busy-label); position: absolute; inset: 0; display: flex; align-items: center;
  justify-content: center; color: #fff; white-space: nowrap;
}
.wsk-composer .wsk-composer-actions .wsk-save-draft:disabled { opacity: .55; cursor: progress; }
.wsk-composer .wsk-composer-actions .htmx-request .wsk-save-draft[data-busy-label]:disabled {
  position: relative; color: transparent; opacity: 1; cursor: progress;
}
.wsk-composer .wsk-composer-actions .htmx-request .wsk-save-draft[data-busy-label]::after {
  content: attr(data-busy-label); position: absolute; inset: 0; display: flex; align-items: center;
  justify-content: center; color: var(--rc-muted); white-space: nowrap;
}

/* SENT (workspace._mark_reply_sent): the editor is replaced by this - one large,
   plain confirmation with a check and the recipient's name, then a quiet "Sent"
   where Reply and Save draft were. Dark green on pale green is about 9:1. */
.wsk-composer .wsk-sent-banner {
  display: flex; align-items: center; gap: 18px; margin: 8px 0 0; padding: 26px 28px;
  border: 1px solid #a9d9bb; border-radius: 12px; background: #eefaf2; color: #14532d; outline: 0;
}
.wsk-composer .wsk-sent-check { flex: 0 0 auto; color: #1a7f45; }
.wsk-composer .wsk-sent-text { margin: 0; min-width: 0; overflow-wrap: anywhere; font-size: 22px; line-height: 32px; font-weight: 600; color: #14532d; }
.wsk-composer .wsk-sent-body { min-width: 0; }
.wsk-composer .wsk-sent-sub { margin: 4px 0 0; overflow-wrap: anywhere; font-size: 15px; line-height: 22px; color: #14532d; }

/* KAN-443 item 4: the AI draft look. A model-written reply carries a badge, a slowly moving
   gradient border (still under reduced motion), a "can make mistakes" line, and - while a new
   version is written - a shimmer over the old text. Regenerate and Undo sit at the top right. */
.wsk-composer-head .wsk-reply-tools { margin-left: auto; display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 6px; }
.wsk-reply-version { font-size: 13px; color: var(--wsk-meta); margin-right: 4px; }
.wsk-reply-version[hidden], .wsk-tool[hidden] { display: none; }
.wsk-tool { display: inline-flex; align-items: center; gap: 6px; box-sizing: border-box; min-height: 36px; padding: 0 14px; border: 1px solid var(--wsk-border); border-radius: 999px; background: #fff; color: var(--accent); font: inherit; font-size: 14px; font-weight: 700; cursor: pointer; }
.wsk-tool:hover:not(:disabled) { border-color: var(--accent); background: var(--accent-light); }
.wsk-tool:disabled { border-color: var(--wsk-border-light); background: #f1f1f1; color: #5c6168; cursor: not-allowed; }
.wsk-ai-badge { display: inline-flex; align-items: center; gap: 6px; padding: 3px 10px; border-radius: 999px; background: linear-gradient(90deg, #5b4fd6, #9333ea); color: #fff; font-size: 12px; font-weight: 700; line-height: 18px; }
.wsk-reply-frame { position: relative; border-radius: 12px; }
.wsk-ai-frame { padding: 2px; background: linear-gradient(120deg, #5b4fd6, #8b5cf6, #d946ef, #3b82f6, #5b4fd6); background-size: 300% 300%; animation: wsk-ai-flow 6s ease infinite; box-shadow: 0 0 0 4px rgba(139, 92, 246, .08), 0 6px 24px rgba(91, 79, 214, .15); }
.wsk-ai-frame .wsk-rt { border-color: transparent; border-radius: 10px; }
@keyframes wsk-ai-flow { 0%, 100% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } }
.wsk-ai-note { display: flex; align-items: center; gap: 8px; margin: 10px 2px 0; font-size: 13px; color: var(--wsk-meta); }
.wsk-ai-note svg { flex: none; color: #7c3aed; }
.wsk-writing { display: none; position: absolute; inset: 2px; z-index: 2; box-sizing: border-box; overflow: hidden; padding: 14px 18px; border-radius: 10px; background: #fff; }
.wsk-composer[data-writing] .wsk-writing { display: block; }
.wsk-writing-label { display: flex; align-items: center; gap: 6px; margin: 0; font-size: 13px; font-weight: 700; color: #5b4fd6; }
.wsk-shimmer { padding-top: 6px; }
.wsk-shimmer i { display: block; height: 12px; margin: 10px 0; border-radius: 6px; background: linear-gradient(90deg, #efedfb 25%, #e2dcfb 37%, #efedfb 63%); background-size: 400% 100%; animation: wsk-shimmer 1.4s ease infinite; }
.wsk-shimmer .wsk-sh-1 { width: 92%; }
.wsk-shimmer .wsk-sh-2 { width: 80%; }
.wsk-shimmer .wsk-sh-3 { width: 86%; }
.wsk-shimmer .wsk-sh-4 { width: 40%; }
@keyframes wsk-shimmer { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }
/* While a new version is written the "Writing a suggestion..." status line stays for screen
   readers but gives way on screen to the shimmer. */
.wsk-composer[data-writing] [data-suggestion-status] { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
/* Undo brought an earlier version back: the notes and questions below describe the newer one. */
.wsk-restored-note { display: none; }
.wsk-composer[data-restored] .wsk-restored-note { display: block; }
.wsk-composer[data-restored] .wsk-qa, .wsk-composer[data-restored] .wsk-draft-review { display: none; }
@media (prefers-reduced-motion: reduce) { .wsk-ai-frame, .wsk-shimmer i { animation: none; } }
/* THE THREAD under the confirmation (hub/templates/workspace/fragments/thread_messages.html).
   A message keeps the email page's own head/sender/date/body rules; this only
   frames it. Older messages scroll inside a short box so the reply just sent,
   the newest and last, stays on screen. */
.wsk-composer .wsk-thread { margin: 20px 0 0; }
.wsk-composer .wsk-thread-title { margin: 0 0 8px; font-size: 14px; font-weight: 600; color: var(--rc-muted); }
.wsk-composer .wsk-thread-message { margin: 0 0 10px; padding: 14px 16px; border: 1px solid var(--rc-line-soft); border-radius: 10px; background: #fff; }
.wsk-composer .wsk-thread-own { background: var(--rc-surface); }
.wsk-composer .wsk-thread-new { border-color: #a9d9bb; background: #f6fcf8; }
.wsk-composer .wsk-thread-message:not(.wsk-thread-new) .wsk-detail-body { max-height: 9em; overflow: auto; }
.wsk-composer .wsk-thread-message .wsk-detail-body { overflow-wrap: anywhere; }
.wsk-composer .wsk-thread-files { display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0 0; padding: 0; list-style: none; }
.wsk-composer .wsk-thread-files li { display: inline-flex; align-items: center; gap: 6px; min-height: 32px; padding: 0 12px; border: 1px solid #cddcf4; border-radius: 16px; background: var(--rc-chip); color: var(--rc-ink); font-size: 14px; overflow-wrap: anywhere; }
.wsk-composer .wsk-sent-banner { scroll-margin-top: 96px; }
.wsk-composer .wsk-sent-copy { margin: 16px 0 0; }
.wsk-composer .wsk-sent-copy-title { margin: 0 0 6px; font-size: 14px; font-weight: 600; color: var(--rc-muted); }
.wsk-composer .wsk-sent-copy-body {
  max-height: 14em; overflow: auto; padding: 12px 16px; border: 1px solid var(--rc-line-soft);
  border-radius: 8px; background: var(--rc-surface); color: var(--rc-ink); font-size: 15px;
  line-height: 24px; white-space: pre-wrap; overflow-wrap: anywhere;
}
.wsk-composer .wsk-sent-state {
  display: inline-flex; align-items: center; gap: 8px; margin: 0; min-height: 44px; padding: 0 20px;
  border: 1px solid var(--rc-line); border-radius: 8px; background: var(--rc-surface);
  color: var(--rc-muted); font-size: 16px; font-weight: 600;
}
@media (max-width: 600px) {
  .wsk-composer .wsk-sent-banner { padding: 20px 18px; gap: 14px; }
  .wsk-composer .wsk-sent-text { font-size: 18px; line-height: 26px; }
}

/* the Optional questions card sits under it as a sibling of the same family */
.wsk-composer .wsk-qa { margin: 24px 0 0; padding: 16px; border: 1px solid var(--rc-line); border-radius: 12px; background: var(--rc-surface); }
.wsk-composer .wsk-qa-title { margin: 0 0 8px; font-size: 16px; font-weight: 600; color: var(--rc-ink); }

@media (pointer: coarse), (max-width: 600px) {
  .wsk-composer .wsk-rt-btn { width: 44px; min-width: 44px; height: 44px; min-height: 44px; }
  .wsk-composer .wsk-attach-button { width: 44px; min-width: 44px; height: 44px; min-height: 44px; }
  .wsk-composer .wsk-file-chip { min-height: 44px; }
  .wsk-composer .wsk-file-chip button { min-height: 44px; }
}
@media (max-width: 600px) {
  .wsk-draft-panel { padding: 16px; }
  .wsk-composer .wsk-rt-bar { padding: 4px; }
  .wsk-composer .wsk-rt-surface, .wsk-composer .wsk-reply-editor { padding: 14px 16px; }
  .wsk-composer .wsk-composer-send { flex: 1 1 160px; }
  .wsk-composer .wsk-composer-actions .wsk-send-button { width: 100%; }
  .wsk-composer .wsk-mail-fields dt { flex-basis: 56px; }
  .wsk-composer .wsk-mail-fields > div { gap: 8px; }
}
@media (prefers-reduced-motion: reduce) { .wsk-composer .wsk-rt { transition: none; } }

/* ===== reply question options: the engine's choices arrive after the page (feat/reply-question-options) =====
   The card paints with its standard options and one calm line, "Finding answer
   options..."; the engine's question-specific options replace the choices list
   only (hub/reply_engine_options.py). The blank "Something else" row and Skip are
   outside the list. No spinner: the line breathes (opacity only), and not at
   all under reduced motion. Existing tokens only. */
.wsk-qa .wsk-qa-choices { display: block; min-width: 0; margin: 0; padding: 0; }
.wsk-qa .wsk-qa-lazy-note { margin: 0 0 8px; font-size: 13px; line-height: 1.4; color: var(--wsk-text-secondary); }
.wsk-qa .wsk-qa-lazy-note.wsk-qa-lazy-muted { color: var(--wsk-meta); }
@media (prefers-reduced-motion: no-preference) {
  .wsk-qa .wsk-qa-choices[data-qa-lazy] .wsk-qa-lazy-note:not(.wsk-qa-lazy-muted) {
    animation: wsk-qa-lazy-breathe 1.8s ease-in-out infinite;
  }
  @keyframes wsk-qa-lazy-breathe { 0%, 100% { opacity: 1; } 50% { opacity: .7; } }
}
/* ===== end reply question options ===== */

/* ==== Reply footer (owner order 2026-10-06): "find a better place to keep this" ====
   "More reply options" was a collapsed disclosure alone at the bottom of the card
   under an empty strip. It is an icon-only button now, beside Save draft, opening a
   small popover (static/replyoptions.js). The same quiet look as the paperclip. */
.wsk-draft-panel { padding-bottom: var(--wsk-space-4); }
/* The popover hangs from the ACTION ROW, not from its own 36px button: the row is where
   the card's content edge is, so the panel can never run off the card whichever side the
   button sits on. */
.wsk-composer .wsk-composer-actions { position: relative; }
.wsk-composer .wsk-reply-more { display: inline-flex; flex: 0 0 auto; }
.wsk-composer .wsk-reply-more-alone { margin-left: auto; }
.wsk-composer .wsk-reply-more-button { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 36px; min-width: 36px; height: 36px; min-height: 36px; padding: 0; border: 1px solid var(--rc-line); border-radius: 8px; background: #fff; color: #3e4c59; cursor: pointer; }
.wsk-composer .wsk-reply-more-button:hover { background: #eef1f5; }
.wsk-composer .wsk-reply-more-button[aria-expanded="true"] { background: #e6eaf0; }
/* A small dot when the box inside already holds words the clerk typed. */
.wsk-composer .wsk-reply-more-button[data-has-instruction]::after { content: ""; position: absolute; top: 4px; right: 4px; width: 7px; height: 7px; border-radius: 50%; background: var(--rc-blue); }
.wsk-composer .wsk-reply-secondary { position: absolute; z-index: 30; top: calc(100% + 8px); left: 0; display: flex; flex-direction: column; gap: 12px; box-sizing: border-box; width: min(360px, 100%); padding: 12px; border: 1px solid var(--rc-line); border-radius: 12px; background: #fff; box-shadow: 0 8px 24px rgba(16, 24, 40, .14); }
/* An author display:flex beats the UA's [hidden]{display:none}; say it again. */
.wsk-reply-secondary[hidden], .wsk-composer .wsk-reply-secondary[hidden] { display: none; }
.wsk-composer .wsk-reply-secondary form,
.wsk-composer .wsk-suggest-now form { margin: 0; }
.wsk-composer .wsk-reply-secondary .wsk-btn-outline,
.wsk-composer .wsk-suggest-now .wsk-btn-outline { min-height: 40px; padding: 0 16px; border: 1px solid var(--rc-line); border-radius: 8px; background: #fff; color: var(--rc-ink); font-size: 15px; font-weight: 600; cursor: pointer; }
.wsk-composer .wsk-reply-secondary .wsk-btn-outline:hover,
.wsk-composer .wsk-suggest-now .wsk-btn-outline:hover { background: #eef1f5; }
.wsk-composer .wsk-steer label { color: var(--rc-muted); }
.wsk-composer .wsk-steer-box { min-height: 64px; padding: 8px 10px; font-size: 14px; border-color: var(--rc-line); border-radius: 8px; }
.wsk-composer .wsk-steer-note { color: var(--rc-muted); }
@media (pointer: coarse), (max-width: 600px) {
  .wsk-composer .wsk-reply-more-button { width: 44px; min-width: 44px; height: 44px; min-height: 44px; }
}
/* On a phone the popover would hang off the edge of a narrow row, so it opens
   in place instead: its own full row under the buttons. */
@media (max-width: 600px) {
  .wsk-composer .wsk-reply-more { display: contents; }
  .wsk-composer .wsk-reply-secondary { position: static; flex: 1 1 100%; width: auto; box-shadow: none; }
  /* Reply, Save draft and the button share one row: Reply gives up some of its room. */
  .wsk-composer .wsk-composer-send { flex: 1 1 120px; }
  .wsk-composer .wsk-composer-actions .wsk-save-draft { padding: 0 10px; }
}

/* An empty reply with nothing on its way: Suggest a reply sits in plain sight under the
   reason, in the same quiet outline look the popover's buttons have (that look is declared
   once, with the popover's, above). */
.wsk-composer .wsk-suggest-now { margin: 8px 0 12px; }
.wsk-composer .wsk-suggest-now .wsk-regen { align-items: flex-start; }
.wsk-composer .wsk-suggest-now .wsk-steer { align-self: stretch; }
@media (pointer: coarse), (max-width: 600px) {
  .wsk-composer .wsk-suggest-now .wsk-btn-outline { min-height: 44px; }
}

/* A suggested file that resolves to one readable source gets a small Open link. */
.wsk-composer .wsk-file-suggestions .wsk-att-open { display: inline-flex; align-items: center; min-height: 28px; margin-top: 4px; padding: 0 10px; border: 1px solid var(--rc-line); border-radius: 14px; background: #fff; color: var(--rc-blue-strong); font-size: 13px; font-weight: 600; text-decoration: none; }
.wsk-composer .wsk-file-suggestions .wsk-att-open:hover { background: #eef1f5; }
@media (pointer: coarse) { .wsk-composer .wsk-file-suggestions .wsk-att-open { min-height: 36px; } }

/* ==== TASK DELEGATED BANNER (begin) ====
   A calm, one-time confirmation in the normal task-board flow. */
.wsk-delegated { margin: 16px 0; padding: 12px 16px; border: 1px solid var(--wsk-border); border-radius: 8px; background: var(--wsk-card); }
.wsk-delegated-title { margin: 0; font-weight: 700; color: var(--wsk-text); }
.wsk-delegated-detail { margin: 4px 0 0; color: var(--wsk-text-secondary); overflow-wrap: anywhere; }
/* ==== TASK DELEGATED BANNER (end) ==== */

/* END invite-simple */

/* ==== ADMIN PAGE UX (begin) =================================================
   Owner order 2026-10-11: the Admin page (invite a colleague, your team, roles)
   clearer and easier for town administrators, many of them older and not
   technical. One reading order - invite, team, subscription - with the
   Home spacing scale (--wsk-space-*), the modern select and the 8px control /
   12px frame radii the other pages use. Every rule here starts with a class that
   belongs to this page (tests/test_admin_page_ux.py fails the build if one does
   not), so nothing can leak onto another screen; no :nth-child, no !important.
   This block sits at the end of the file so it wins by order, not by weight;
   keep other lanes' blocks and this one apart.

   Colour is never the only signal: every chip carries its state in words, every
   notice carries an icon, and each pair below meets 4.5:1 (checked: ok 7.2,
   warn 7.8, idle 8.0, bad 7.6). Keyboard focus is the global 3px ring from the
   top of this file; this block only nudges its offset. */
.wsk-admin-main {
  --adm-ok-bg: #e4f3e8; --adm-ok-ink: #1b5a2f; --adm-ok-line: #a9d3b6;
  --adm-warn-bg: #fff2d3; --adm-warn-ink: #6a4300; --adm-warn-line: #e3be62;
  --adm-idle-bg: #eceeef; --adm-idle-ink: #40484d;
  --adm-bad-bg: #fbeaea; --adm-bad-ink: #8c2323; --adm-bad-line: #e3b9ba;
  --adm-info-bg: var(--accent-light); --adm-info-ink: var(--accent-hover);
  --adm-line: var(--wsk-border-light);
  --adm-radius: 12px;
  max-width: 1400px; margin: 0 auto; padding: 36px 40px 40px; box-sizing: border-box;
  display: flex; flex-direction: column; gap: var(--wsk-space-5);
  line-height: 1.4;
}
.wsk-admin-main :focus-visible { outline-offset: 2px; }

/* -- heading ------------------------------------------------------------- */
.wsk-admin-head { display: flex; align-items: flex-start; justify-content: space-between; flex-wrap: wrap; gap: var(--wsk-space-3) var(--wsk-space-4); }
.wsk-admin-head-text { flex: 1 1 360px; min-width: 0; }
.wsk-admin-title { margin: 0 0 var(--wsk-space-1); font-size: 26px; font-weight: 700; color: var(--wsk-heading); }
.wsk-admin-subtitle { margin: 0; max-width: 64ch; font-size: 15px; color: var(--wsk-text-secondary); }
.wsk-admin-back {
  display: inline-flex; align-items: center; min-height: 40px; padding: 8px 16px;
  border: 1px solid var(--wsk-border); border-radius: 8px; background: var(--wsk-card);
  color: var(--wsk-text-secondary); font-size: 14px; font-weight: 700; text-decoration: none;
}
.wsk-admin-back:hover { border-color: var(--accent); color: var(--accent); }

/* -- sections: heading on the page, content on one white surface --------------- */
.wsk-admin-panel { display: flex; flex-direction: column; gap: var(--wsk-space-2); min-width: 0; margin: 0; }
.wsk-admin-main #admin-users { display: flex; flex-direction: column; gap: var(--wsk-space-2); min-width: 0; }
.wsk-admin-panel-title {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--wsk-space-2);
  margin: 0; font-size: 18px; font-weight: 700; color: var(--wsk-heading);
}
/* KAN-579: the mock grid of roles and email tags. */
.wsk-admin-main .wsk-role-grid-scroll { overflow-x: auto; }
.wsk-admin-main .wsk-role-grid { width: 100%; border-collapse: collapse; font-size: 15px; }
.wsk-admin-main .wsk-role-grid th, .wsk-admin-main .wsk-role-grid td { padding: 8px 12px; border-bottom: 1px solid var(--wsk-border); text-align: center; }
.wsk-admin-main .wsk-role-grid th:first-child { text-align: start; font-weight: 400; }
.wsk-admin-main .wsk-role-grid thead th, .wsk-admin-main .wsk-role-grid thead th:first-child { font-weight: 700; }
.wsk-admin-main .wsk-role-grid input[type="checkbox"] { width: 22px; height: 22px; accent-color: var(--accent); }
.wsk-admin-main .wsk-role-grid-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--wsk-space-2); margin-top: var(--wsk-space-3); }
.wsk-admin-subheading { margin: 0; font-size: 16px; font-weight: 700; color: var(--wsk-heading); }
.wsk-admin-panel > .ws-muted, .wsk-admin-main #admin-users > .ws-muted {
  max-width: 72ch; margin: 0; font-size: 15px; color: var(--wsk-text-secondary);
}
.wsk-admin-surface {
  display: flex; flex-direction: column; gap: var(--wsk-space-4); min-width: 0; box-sizing: border-box;
  padding: var(--wsk-space-5); border: 1px solid var(--wsk-border); border-radius: var(--adm-radius);
  background: var(--wsk-card);
}
.wsk-admin-surface p { margin: 0; }
.wsk-admin-surface > .ws-btn-primary, .wsk-admin-surface > .ws-btn-suggestion { align-self: flex-start; }
.wsk-admin-surface > .ws-muted { max-width: 72ch; font-size: 15px; color: var(--wsk-text-secondary); }
.wsk-admin-subscription { flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--wsk-space-3) var(--wsk-space-4); }
.wsk-admin-subscription-text { flex: 1 1 280px; min-width: 0; font-size: 15px; color: var(--wsk-text); }

/* -- fields and buttons --------------------------------------------------------- */
.wsk-admin-field { display: flex; flex-direction: column; gap: var(--wsk-space-1); min-width: 0; }
.wsk-admin-field-label { font-size: 14px; font-weight: 700; color: var(--wsk-text); }
.wsk-admin-hint { font-size: 13px; color: var(--wsk-text-secondary); }
.wsk-admin-field-label .wsk-admin-hint { font-weight: 400; }
.wsk-admin-main .ws-input-sm {
  box-sizing: border-box; width: 100%; min-width: 0; height: 44px; padding: 10px 12px;
  border: 1px solid var(--wsk-border); border-radius: 8px; background: var(--wsk-card);
  color: var(--wsk-text); font-family: inherit; font-size: 15px;
}
.wsk-admin-main .ws-input-sm:hover:not(:disabled) { border-color: var(--wsk-icon); }
.wsk-admin-main select { --wsk-select-h: 44px; --wsk-select-fs: 15px; }
.wsk-admin-main .ws-btn-primary, .wsk-admin-main .ws-btn-suggestion {
  min-width: 0; font-family: inherit; font-weight: 700; border-radius: 8px; cursor: pointer;
  transition: background-color .15s ease, border-color .15s ease;
}
.wsk-admin-main .ws-btn-primary {
  min-height: 44px; padding: 10px 22px; border: 1px solid var(--accent); background: var(--accent);
  color: #fff; font-size: 15px;
}
.wsk-admin-main .ws-btn-primary:hover:not(:disabled) { background: var(--accent-hover); border-color: var(--accent-hover); }
.wsk-admin-main .ws-btn-suggestion {
  min-height: 40px; padding: 8px 16px; border: 1.5px solid var(--accent); background: var(--wsk-card);
  color: var(--accent); font-size: 14px;
}
.wsk-admin-main .ws-btn-suggestion:hover:not(:disabled) { background: var(--accent-light); }
.wsk-admin-main .ws-btn-primary:disabled, .wsk-admin-main .ws-btn-suggestion:disabled {
  border-color: var(--wsk-border); background: var(--wsk-border-light); color: var(--wsk-disabled); cursor: not-allowed;
}
.wsk-admin-main .ws-btn-suggestion.wsk-admin-danger { border-color: var(--adm-bad-ink); background: var(--adm-bad-ink); color: #fff; }
.wsk-admin-main .ws-btn-suggestion.wsk-admin-danger:hover:not(:disabled) { background: #6f1a1a; border-color: #6f1a1a; }
.wsk-admin-main .ws-invite-setup-link {
  display: inline-flex; align-items: center; align-self: flex-start; min-height: 40px; padding: 8px 16px;
  border: 1.5px solid var(--accent); border-radius: 8px; background: var(--wsk-card);
  color: var(--accent); font-size: 14px; font-weight: 700; text-decoration: none;
}
.wsk-admin-main .ws-invite-setup-link:hover { background: var(--accent-light); }

/* -- status chips: the state in words, with a dot as a second cue ---------------- */
.wsk-status {
  display: inline-flex; align-items: center; gap: 6px; padding: 3px 10px; border-radius: 999px;
  background: var(--adm-idle-bg, #eceeef); color: var(--adm-idle-ink, #40484d);
  font-size: 13px; font-weight: 700; line-height: 1.3; white-space: nowrap;
}
.wsk-status::before { content: ""; flex: none; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.wsk-status-active { background: var(--adm-ok-bg); color: var(--adm-ok-ink); }
.wsk-status-idle { background: var(--adm-idle-bg); color: var(--adm-idle-ink); }
.wsk-status-invited { background: var(--adm-info-bg); color: var(--adm-info-ink); }
.wsk-status-warn { background: var(--adm-warn-bg); color: var(--adm-warn-ink); }
.wsk-status-you { background: transparent; color: var(--wsk-text-secondary); box-shadow: inset 0 0 0 1px var(--wsk-border); }
.wsk-status-you::before { content: none; }
/* KAN-11: the break-glass badge is deliberately the loudest chip - it marks the
   ONE account that can still bypass the town's identity provider, and an
   administrator should spot it without reading. */
.ws-badge-breakglass {
  display: inline-flex; align-items: center; padding: 3px 10px; border-radius: 999px;
  background: var(--brand); color: #ffffff; font-size: 13px; font-weight: 700; line-height: 1.3; white-space: nowrap;
}

/* -- notices: one message, with an icon, that answers what the person just did ----- */
.wsk-admin-notice {
  display: flex; align-items: flex-start; gap: var(--wsk-space-3); box-sizing: border-box;
  padding: 12px 16px; border: 1px solid var(--wsk-border); border-radius: 10px;
  font-size: 15px; line-height: 1.45;
}
.wsk-admin-notice-icon { flex: none; margin-top: 1px; }
.wsk-admin-notice-body { min-width: 0; overflow-wrap: anywhere; }
.wsk-admin-notice .ws-form-error, .wsk-admin-notice .wsk-task-notice, .wsk-admin-notice .ws-invite-note, .wsk-admin-notice .ws-muted {
  display: block; margin: 0; padding: 0; border: 0; background: none; color: inherit; font-size: inherit;
}
.wsk-admin-notice-ok { border-color: var(--adm-ok-line); background: var(--adm-ok-bg); color: var(--adm-ok-ink); }
.wsk-admin-notice-info { background: var(--adm-info-bg); color: var(--adm-info-ink); }
.wsk-admin-notice-warn { border-color: var(--adm-warn-line); background: var(--adm-warn-bg); color: var(--adm-warn-ink); }
.wsk-admin-notice-error { border-color: var(--adm-bad-line); background: var(--adm-bad-bg); color: var(--adm-bad-ink); }
.wsk-admin-status { font-size: 14px; font-weight: 700; }
.wsk-admin-status-ok { color: var(--adm-ok-ink); }
.wsk-admin-status-error { color: var(--adm-bad-ink); }
.ws-pw-status:empty { display: none; }

/* -- invite a colleague ----------------------------------------------------------- */
.ws-admin-invite-form { display: flex; align-items: flex-end; gap: var(--wsk-space-3); max-width: 640px; }
.ws-admin-invite-form .wsk-admin-field { flex: 1 1 auto; }
.ws-admin-invite-form .ws-btn-primary { flex: 0 0 auto; }
.ws-invite-note { margin: 0; font-size: 15px; }
.ws-invite-link {
  margin: 0; padding: 12px 14px; border: 1px solid var(--wsk-border); border-radius: 8px;
  background: var(--wsk-bg); font-size: 14px; word-break: break-all;
}
.ws-invite-approval { padding-top: var(--wsk-space-2); border-top: 1px solid var(--adm-line); }
.ws-invite-approval > summary { min-height: 44px; cursor: pointer; font-size: 15px; font-weight: 700; color: var(--accent); }
.ws-invite-approval-body { display: flex; flex-direction: column; align-items: stretch; gap: var(--wsk-space-3); max-width: 640px; padding: var(--wsk-space-1) 0 var(--wsk-space-2); }
.ws-invite-approval-body p { margin: 0; }
.wsk-admin-option { display: flex; flex-direction: column; align-items: flex-start; gap: var(--wsk-space-2); }
.wsk-admin-option ~ .wsk-admin-option { padding-top: var(--wsk-space-3); border-top: 1px solid var(--adm-line); }
.ws-admin-consent-share { display: flex; flex-direction: column; align-items: flex-start; gap: var(--wsk-space-2); }
.ws-admin-consent-share .wsk-input { align-self: stretch; }
.ws-admin-consent-share-label { font-size: 14px; font-weight: 700; color: var(--wsk-heading); }
.ws-admin-invite-list { margin: 0; padding: 0; list-style: none; overflow-wrap: anywhere; border-top: 1px solid var(--adm-line); }
.ws-admin-invite-row {
  display: grid; grid-template-columns: minmax(0, 1.6fr) auto auto minmax(0, 1fr) minmax(0, 1.4fr);
  align-items: center; gap: var(--wsk-space-1) var(--wsk-space-4);
  padding: var(--wsk-space-3) 0; border-bottom: 1px solid var(--adm-line);
  font-size: 14px; color: var(--wsk-text-secondary);
}
.ws-admin-invite-row > span:first-child { font-size: 15px; font-weight: 700; color: var(--wsk-text); }
.wsk-admin-subheading + .ws-admin-invite-list { margin-top: calc(var(--wsk-space-2) * -1); }

/* -- your team: people grouped by role in expandable groups (KAN-310) ----------------- */
.wsk-admin-count-line { margin: 0; }
.ws-admin-add {
  display: flex; flex-direction: column; gap: var(--wsk-space-3); box-sizing: border-box;
  padding: var(--wsk-space-5); border: 1px solid var(--wsk-border); border-radius: var(--adm-radius); background: var(--wsk-card);
}
.ws-admin-add p { margin: 0; }
.ws-admin-add-form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--wsk-space-3) var(--wsk-space-4); }
.ws-admin-add-form .wsk-admin-field { flex: 1 1 220px; }
.ws-admin-checkbox { display: flex; align-items: center; gap: var(--wsk-space-2); min-height: 44px; font-size: 15px; color: var(--wsk-text); }
.ws-admin-checkbox input { width: 20px; height: 20px; margin: 0; accent-color: var(--accent); }
.wsk-admin-groups { overflow: hidden; border: 1px solid var(--wsk-border); border-radius: var(--adm-radius); background: var(--wsk-card); }
.wsk-admin-group + .wsk-admin-group { border-top: 1px solid var(--wsk-border); }
.wsk-admin-group > .wsk-admin-group-summary {
  position: relative; display: grid; grid-template-columns: minmax(0, 1fr) auto auto; align-items: center;
  gap: var(--wsk-space-4); padding: 16px 20px; cursor: pointer;
}
.wsk-admin-group-summary:hover { background: var(--wsk-bg); }
.wsk-admin-group-summary:hover .wsk-admin-disclosure { background: var(--accent-light); }
/* The group header is edge to edge inside a clipped frame, so its focus ring sits inside it. */
.wsk-admin-main .wsk-admin-group-summary:focus-visible { outline-offset: -3px; }
.wsk-admin-group[open] > .wsk-admin-group-summary { border-bottom: 1px solid var(--adm-line); }
.wsk-admin-group-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.wsk-admin-group-name { font-size: 17px; font-weight: 700; color: var(--wsk-heading); overflow-wrap: anywhere; }
.wsk-admin-group-help { font-size: 14px; color: var(--wsk-text-secondary); }
.wsk-admin-member-count { font-size: 14px; color: var(--wsk-text-secondary); white-space: nowrap; }
.wsk-admin-disclosure {
  padding: 8px 34px 8px 14px; border: 1.5px solid var(--accent); border-radius: 8px;
  background: var(--wsk-card); color: var(--accent); font-size: 14px; font-weight: 700;
  line-height: 20px; white-space: nowrap;
}
.wsk-admin-hide, .wsk-admin-group[open] .wsk-admin-show { display: none; }
.wsk-admin-group[open] .wsk-admin-hide { display: inline; }
/* The chevron is the shared disclosure one (a summary ::before); here it sits
   inside the Show/Hide button, level with its middle. */
.wsk-admin-group > .wsk-admin-group-summary::before { position: absolute; top: calc(50% - 4px); right: 34px; color: var(--accent); opacity: 1; }
.wsk-admin-group-body { padding: 0; }
.wsk-admin-empty { margin: 0; padding: var(--wsk-space-5) 20px; font-size: 15px; color: var(--wsk-text-secondary); }
.wsk-admin-members { margin: 0; padding: 0; list-style: none; }
.wsk-admin-member {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: start;
  gap: var(--wsk-space-2) var(--wsk-space-5); padding: var(--wsk-space-4) 20px var(--wsk-space-2);
}
.wsk-admin-member + .wsk-admin-member { border-top: 1px solid var(--adm-line); }
.wsk-admin-member > .wsk-admin-more, .wsk-admin-member > .wsk-admin-notice { grid-column: 1 / -1; }
.wsk-admin-person { display: flex; flex-wrap: wrap; align-items: center; gap: 6px var(--wsk-space-3); min-width: 0; }
.wsk-admin-username { margin: 0; font-size: 16px; font-weight: 700; color: var(--wsk-heading); overflow-wrap: anywhere; }
.wsk-admin-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 0; }
.wsk-admin-signin { display: flex; flex: 1 1 100%; flex-wrap: wrap; gap: 2px var(--wsk-space-5); font-size: 14px; color: var(--wsk-text-secondary); overflow-wrap: anywhere; }
.wsk-admin-signin p { margin: 0; }
.wsk-admin-access { display: flex; flex-wrap: wrap; align-items: center; gap: var(--wsk-space-2); }
.wsk-admin-access strong { color: var(--wsk-text); }
.wsk-admin-role { display: flex; flex-direction: column; align-items: flex-start; gap: var(--wsk-space-2); min-width: 0; }
.wsk-admin-role .ws-muted { max-width: 36ch; font-size: 14px; color: var(--wsk-text-secondary); }
.wsk-admin-role-form {
  display: grid; grid-template-columns: auto minmax(160px, 1fr) auto; grid-template-areas: "label pick save";
  align-items: center; gap: var(--wsk-space-1) var(--wsk-space-2); margin: 0; width: 100%;
}
.wsk-admin-role-form > label { grid-area: label; }
.wsk-admin-role-form > select { grid-area: pick; }
.wsk-admin-role-form > button { grid-area: save; }
.wsk-admin-more > summary { width: fit-content; min-height: 36px; cursor: pointer; font-size: 14px; font-weight: 700; color: var(--accent); }
.wsk-admin-more-body {
  display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--wsk-space-4) var(--wsk-space-5);
  margin-top: var(--wsk-space-1); padding: var(--wsk-space-3) var(--wsk-space-4) var(--wsk-space-4);
  border-radius: 10px; background: var(--wsk-bg);
}
.ws-pw-form { display: flex; flex: 1 1 360px; flex-wrap: wrap; align-items: flex-end; gap: var(--wsk-space-2); margin: 0; }
.ws-pw-form .wsk-admin-field { flex: 1 1 220px; }
.wsk-admin-more-body > .ws-pw-status { flex-basis: 100%; }
.wsk-admin-two-step { display: flex; flex: 1 1 260px; flex-direction: column; align-items: flex-start; gap: var(--wsk-space-2); }
.wsk-admin-two-step .ws-muted { margin: 0; font-size: 14px; color: var(--wsk-text-secondary); }
.wsk-admin-two-step form { margin: 0; }
.wsk-admin-confirm { box-sizing: border-box; max-width: 520px; padding: var(--wsk-space-3) var(--wsk-space-4); border: 1px solid var(--adm-bad-line); border-radius: 10px; background: var(--adm-bad-bg); color: var(--adm-bad-ink); }
.wsk-admin-confirm-text { margin: 0 0 var(--wsk-space-3); font-size: 14px; }
.wsk-admin-confirm-actions { display: flex; flex-wrap: wrap; gap: var(--wsk-space-2); }
.wsk-admin-confirm-actions form { margin: 0; }

/* -- roles: who holds each role (People and roles) ---------------------------------------- */
.wsk-admin-roles-note { margin: 0; }
.wsk-admin-roles-more-note { flex: 1 1 100%; margin: 0; font-size: 14px; color: var(--wsk-text-secondary); }
.wsk-admin-member > .wsk-admin-roles-form { grid-column: 1 / -1; display: flex; flex-direction: column; align-items: flex-start; gap: var(--wsk-space-3); margin: 0; }
.wsk-admin-roles-people { display: flex; flex-wrap: wrap; gap: var(--wsk-space-1) var(--wsk-space-5); min-width: 0; margin: 0; padding: 0; border: 0; }
.wsk-admin-roles-people > legend { margin-bottom: var(--wsk-space-1); padding: 0; }

/* A change answers on its own row: a short, calm highlight, then it is still. */
@media (prefers-reduced-motion: no-preference) {
  .wsk-admin-notice { animation: wsk-admin-notice-in .28s ease-out both; }
  .wsk-admin-member[data-just-changed] { animation: wsk-admin-flash 1.6s ease-out; }
}
@keyframes wsk-admin-notice-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
@keyframes wsk-admin-flash { from { background: var(--adm-info-bg); } to { background: transparent; } }

/* -- smaller screens ----------------------------------------------------------------- */
@media (max-width: 900px) {
  .wsk-admin-member { grid-template-columns: minmax(0, 1fr); }
  .wsk-admin-role-form { max-width: 420px; }
  .ws-admin-invite-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--wsk-space-1) var(--wsk-space-3); }
  .ws-admin-invite-row > span:first-child, .ws-admin-invite-row > span:last-child { flex: 1 1 100%; }
}
@media (max-width: 600px) {
  .wsk-admin-main { padding: 20px 16px 28px; }
  .wsk-admin-surface, .ws-admin-add { padding: var(--wsk-space-4); }
  .ws-admin-invite-form { flex-direction: column; align-items: stretch; }
  .ws-admin-invite-form .ws-btn-primary { width: 100%; }
  .ws-admin-add-form .ws-btn-suggestion { width: 100%; }
  .wsk-admin-group > .wsk-admin-group-summary { grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "text pick" "count count"; align-items: start; gap: var(--wsk-space-2) var(--wsk-space-3); padding: 16px; }
  .wsk-admin-group-text { grid-area: text; }
  .wsk-admin-disclosure { grid-area: pick; }
  .wsk-admin-member-count { grid-area: count; }
  .wsk-admin-group > .wsk-admin-group-summary::before { top: 32px; right: 30px; }
  .wsk-admin-member { padding: var(--wsk-space-4); }
  .wsk-admin-role-form { grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "label label" "pick save"; max-width: none; }
  .wsk-admin-more-body { padding: var(--wsk-space-3); }
  .ws-pw-form .ws-btn-suggestion { width: 100%; }
  .wsk-admin-main .ws-btn-suggestion, .wsk-admin-more > summary, .wsk-admin-back, .wsk-admin-main .ws-invite-setup-link { min-height: 44px; }
  .wsk-admin-subscription .ws-invite-setup-link { width: 100%; justify-content: center; }
}
@media (pointer: coarse) {
  .wsk-admin-main .ws-btn-suggestion, .wsk-admin-more > summary, .wsk-admin-back, .wsk-admin-main .ws-invite-setup-link { min-height: 44px; }
}
/* ==== ADMIN PAGE UX (end) ==== */

/* Recovery-codes page (MFA enrolment). Scoped to .wsk-recovery so the shared auth card is untouched:
   a key badge, a plain "shown only now" note, the codes as a two-column grid behind one toggle,
   and the two buttons with a gap between them. */
.wsk-recovery { align-items: stretch; padding: 40px 32px 32px; }
/* KAN-542: compact centered sign-in summary. Line 1 account . workspace, line 2 [env pill] host. The dt labels stay in
   the DOM, visually hidden, so screen readers still get Account, Workspace, Environment and Host. */
.wsk-recovery-context, .wsk-mfa-context { display: flex; flex-wrap: wrap; justify-content: center; align-items: baseline; gap: 4px 8px; margin: 0 0 18px; padding: 0; border: 0; background: transparent; text-align: center; }
.wsk-recovery-context::after { content: ""; order: 3; flex-basis: 100%; height: 0; }
.wsk-recovery-context > div { min-width: 0; padding: 0; }
.wsk-recovery-context dt { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.wsk-recovery-context dd { margin: 0; overflow-wrap: anywhere; }
.wsk-ctx-account { order: 1; font-size: 16px; }
.wsk-ctx-workspace { order: 2; font-size: 16px; }
.wsk-ctx-account dd, .wsk-ctx-workspace dd { display: inline; }
.wsk-ctx-sep { margin-left: 4px; color: var(--wsk-text-secondary); }
.wsk-wrap-anywhere { overflow-wrap: anywhere; }
.wsk-ctx-env { order: 4; }
.wsk-ctx-env dd { display: inline-block; padding: 1px 8px; border-radius: 999px; background: var(--accent-light); color: var(--accent); font-size: 12px; font-weight: 700; letter-spacing: .04em; }
.wsk-ctx-host { order: 5; font-size: 13px; color: var(--wsk-text-secondary); }
.wsk-appicon { display: block; width: 56px; height: 56px; margin: 0 auto 12px; border-radius: 22%; }
.wsk-appicon-inline { display: inline-block; width: 40px; height: 40px; margin: 0; flex: none; }
.wsk-mfa-context { font-size: 14px; }
.wsk-recovery-badge {
  align-self: center; display: inline-flex; align-items: center; justify-content: center;
  width: 56px; height: 56px; margin-bottom: 16px; border-radius: 50%;
  background: var(--accent-light); color: var(--accent);
}
.wsk-recovery .wsk-auth-title { text-align: center; }
.wsk-recovery-copy { margin: 10px 0 16px; }
.wsk-recovery-note {
  margin: 0 0 20px; padding: 12px 14px; border-radius: 12px; text-align: left;
  font-size: 15px; line-height: 1.45; color: var(--wsk-text);
  background: var(--accent-light); border-left: 4px solid var(--accent);
}
.wsk-recovery-codes {
  margin: 0 0 20px; border: 1px solid var(--wsk-border-light); border-radius: 12px;
  background: var(--wsk-bg); text-align: left; overflow: hidden;
}
.wsk-recovery-codes > summary {
  margin: 0;
  padding: 14px 16px; cursor: pointer; font-size: 16px; font-weight: 700; color: var(--accent);
}
.wsk-recovery-count { margin-left: auto; font-size: 13px; font-weight: 400; color: var(--wsk-text-secondary); }
.wsk-recovery-codes ul {
  list-style: none; margin: 0; padding: 4px 16px 16px;
  display: grid; grid-template-columns: minmax(0, 1fr); gap: 8px;
}
.wsk-recovery-codes li {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 10px 8px; text-align: center; border-radius: 8px;
  background: var(--wsk-card); border: 1px solid var(--wsk-border-light);
}
.wsk-recovery-codes code { font-size: 14px; line-height: 1.6; letter-spacing: .04em; color: var(--wsk-text); overflow-wrap: anywhere; }
.wsk-recovery-copy-button { min-height: 36px; padding: 4px 12px; border: 1px solid var(--wsk-border-light); border-radius: 8px; color: var(--accent); background: var(--wsk-card); font-weight: 700; cursor: pointer; }
.wsk-recovery-copy-status { min-height: 1.4em; margin: 8px 0; color: var(--wsk-text-secondary); font-size: 14px; }
@media (max-width: 480px) {
  .wsk-recovery-codes code { font-size: 14px; }
  .wsk-recovery-codes ul { padding: 4px 12px 14px; }
  .wsk-recovery-codes li { padding: 10px 6px; }
}
/* The box clips its rounded corners; keep the keyboard focus ring of its toggle inside it. */
.wsk-recovery-codes > summary:focus-visible { outline-offset: -3px; }
.wsk-recovery-actions { display: flex; flex-direction: column; gap: 12px; }
.wsk-recovery-actions .wsk-auth-form { gap: 0; }
.wsk-recovery-actions .wsk-btn-outline,
.wsk-recovery-actions .wsk-btn-primary {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 48px; box-sizing: border-box; text-decoration: none;
}
@media (max-width: 480px) { .wsk-recovery { padding: 32px 20px 24px; } }

/* ---- wsk-sum-*: the Search page's AI summary card (HUB_SEARCH_SUMMARY) -------------
   hub/templates/workspace/fragments/search_summary.html. Scoped to this card only.
   The working block is revealed by htmx's own .htmx-request class on the form (the
   meta tag turns htmx's built-in indicator styles off, so these rules are the only
   ones); .is-working shows the same block for the working state rendered on its own. */
.wsk-sum-card {
  box-sizing: border-box; max-width: 760px; margin: 20px 0 0; padding: 22px 24px;
  border-radius: 20px; background: var(--accent-light);
  border: 1px solid color-mix(in srgb, var(--accent) 22%, #fff);
  display: flex; flex-direction: column; gap: 12px; color: var(--wsk-heading);
}
.wsk-sum-quiet {
  background: #f7f7f7; border-color: #dcdcdc;
}
.wsk-sum-head { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; }
.wsk-sum-icon {
  flex: none; width: 28px; height: 28px; border-radius: 14px; background: var(--accent);
  color: #fff; display: flex; align-items: center; justify-content: center;
}
.wsk-sum-quiet .wsk-sum-icon { background: var(--wsk-meta); }
.wsk-sum-title {
  margin: 0; font-size: 14px; letter-spacing: 0.08em; text-transform: uppercase;
  font-weight: 700; color: var(--accent-hover); white-space: nowrap;
}
.wsk-sum-quiet .wsk-sum-title { color: #34383b; }
.wsk-sum-meta { margin-left: auto; font-size: 14px; color: var(--wsk-text-secondary); }
.wsk-sum-lead { margin: 0; font-size: 18px; color: var(--wsk-heading); }
.wsk-sum-text { margin: 0; font-size: 18px; line-height: 1.6; color: var(--wsk-heading); overflow-wrap: anywhere; }
.wsk-sum-note {
  margin: 0; padding: 12px 14px; border-radius: 12px; background: #fff4d6; color: #4a3500;
  font-size: 16px; line-height: 1.45;
}
.wsk-sum-kind { font-size: 13px; color: var(--wsk-meta); }
/* KAN-443: the numbered chips are gone. The records the summary rests on are one folded list,
   "Sources (N)", closed until it is clicked - the same control the chat uses. */
.wsk-sum-sources summary { cursor: pointer; font-size: 14px; line-height: 1.45; color: var(--wsk-text-secondary); }
.wsk-sum-sources ul { margin: 8px 0 0; padding-left: 22px; font-size: 16px; line-height: 1.5; color: var(--wsk-text-secondary); }
.wsk-sum-sources a { color: var(--accent-hover); font-weight: 700; overflow-wrap: anywhere; }
.wsk-sum-foot { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 16px; }
.wsk-sum-limited { margin-top: -2px; }
.wsk-sum-actions { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.wsk-sum-actions .wsk-btn-primary, .wsk-sum-actions .wsk-btn-outline, .wsk-sum-stop {
  min-height: 44px; box-sizing: border-box; border-radius: 22px; font-size: 16px; padding: 0 20px;
}
.wsk-sum-go:disabled { opacity: .6; cursor: progress; }
.wsk-sum-fine { margin: 0; font-size: 14px; line-height: 1.45; color: var(--wsk-text-secondary); }
.wsk-sum-form { display: flex; flex-direction: column; gap: 12px; }
.wsk-sum-working { display: none; flex-direction: column; gap: 14px; }
.wsk-sum-form.htmx-request .wsk-sum-working, .wsk-sum-form.is-working .wsk-sum-working { display: flex; }
.wsk-sum-form.htmx-request .wsk-sum-idle, .wsk-sum-form.is-working .wsk-sum-idle { display: none; }
.wsk-sum-idle { display: flex; flex-direction: column; gap: 12px; }
.wsk-sum-skeleton { display: flex; flex-direction: column; gap: 10px; }
.wsk-sum-skeleton span {
  display: block; height: 14px; border-radius: 7px;
  background: color-mix(in srgb, var(--accent) 16%, #fff);
}
.wsk-sum-skel-1 { width: 96%; }
.wsk-sum-skel-2 { width: 88%; }
.wsk-sum-skel-3 { width: 62%; }
.wsk-sum-wait { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; font-size: 16px; color: var(--wsk-text-secondary); }
.wsk-sum-stop { margin-left: auto; border-color: var(--wsk-border); color: #34383b; border-width: 2px; }
@media (max-width: 480px) {
  .wsk-sum-card { padding: 18px 16px; border-radius: 16px; }
  .wsk-sum-lead, .wsk-sum-text { font-size: 17px; }
  .wsk-sum-meta { margin-left: 0; flex-basis: 100%; }
  .wsk-sum-stop { margin-left: 0; }
}

/* ---- KAN-443 lane E: the Search page answers at once, and the summary writes itself ------
   hub/templates/workspace/fragments/search_pending.html (the skeleton the page shell shows
   while htmx fetches the results) and the typing effect of workspace_search.js. */

/* The card in the page's summary slot holds its height from the moment the results appear, so
   the rows below it do not move when the summary arrives (nor when it is replaced by a shorter
   "could not find an answer"). The height is that of the usual four-sentence answer; the foot
   stays at the bottom, so a shorter answer leaves air inside the card, not a gap under it. A
   longer answer grows the card. */
.wsk-sum-slot .wsk-sum-card { min-height: 300px; }
.wsk-sum-slot .wsk-sum-foot, .wsk-sum-slot .wsk-sum-wait { margin-top: auto; }
.wsk-sum-slot .wsk-sum-form { flex: 1; }
.wsk-sum-text.is-typing { position: relative; color: transparent; }
.wsk-sum-typed { position: absolute; top: 0; right: 0; bottom: 0; left: 0; color: var(--wsk-heading); }
/* net width 0 (the margin gives back the 2px), so the caret never changes where a line breaks */
.wsk-sum-caret {
  display: inline-block; width: 2px; height: 1.1em; margin-right: -2px; vertical-align: text-bottom;
  background: var(--accent); animation: wsk-sum-blink 1s steps(2, start) infinite;
}
@keyframes wsk-sum-blink { to { visibility: hidden; } }
@keyframes wsk-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
.wsk-sum-skeleton span { animation: wsk-pulse 1.4s ease-in-out infinite; }

/* The results while they load: the tab names, one line, and four result-shaped bars. */
.wsk-sr-loading { min-width: 0; }
.wsk-sr-loading .wsk-sr-tab { cursor: default; }
.wsk-sr-skeleton { display: flex; flex-direction: column; }
.wsk-sr-skel-row { display: flex; flex-direction: column; gap: 10px; padding: 20px 0; border-bottom: 1px solid var(--wsk-border-light); }
.wsk-sr-skeleton span { display: block; height: 14px; border-radius: 7px; background: var(--wsk-border-light); animation: wsk-pulse 1.4s ease-in-out infinite; }
.wsk-sr-skeleton .wsk-sr-skel-meta { width: 30%; height: 12px; }
.wsk-sr-skeleton .wsk-sr-skel-title { width: 58%; height: 20px; }
.wsk-sr-skeleton .wsk-sr-skel-line { width: 96%; }
.wsk-sr-skeleton .wsk-sr-skel-short { width: 68%; }
@media (max-width: 480px) {
  .wsk-sum-slot .wsk-sum-card { min-height: 460px; }
}
@media (prefers-reduced-motion: reduce) {
  .wsk-sum-caret, .wsk-sum-skeleton span, .wsk-sr-skeleton span { animation: none; }
}

/* ===== Email page: pills, "Tasks from this email" card, task-feedback survey (KAN-453) =========
   From the approved design (Main.dc.html / FlagSurvey.dc.html). Colours are the site tokens where one exists
   (accent, neutrals, spacing scale); the red and amber chips reuse the two warm tones the task board already
   uses. 44px targets on every control a thumb meets. No inline styles anywhere (the CSP allows none). */
.wsk-email-pills { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: var(--wsk-space-4); }
.wsk-email-pill {
  display: inline-flex; align-items: center; gap: var(--wsk-space-2); box-sizing: border-box; min-height: 44px;
  padding: 0 16px; border-radius: 999px; border: 1px solid var(--wsk-border); background: var(--wsk-card);
  color: var(--wsk-text); font-size: 15px; text-decoration: none; touch-action: manipulation;
}
.wsk-email-pill:hover { background: var(--accent-light); color: var(--accent-hover); }
.wsk-email-pill:focus-visible { outline: 3px solid #0b57d0; outline-offset: 2px; }
.wsk-email-pill-soft { border-color: transparent; background: var(--accent-light); color: var(--accent); font-weight: 700; }
.wsk-email-pill svg { flex: 0 0 auto; }

.wsk-etasks { margin-top: var(--wsk-space-5); border: 1px solid var(--wsk-border-light); border-radius: 14px; overflow: hidden; background: var(--wsk-card); }
.wsk-etasks-head {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--wsk-space-3);
  padding: var(--wsk-space-4) 20px; background: var(--wsk-bg); border-bottom: 1px solid var(--wsk-border-light);
}
.wsk-etasks-headline { display: flex; align-items: center; gap: var(--wsk-space-3); }
.wsk-etasks-title { margin: 0; font-size: 18px; color: var(--wsk-heading); }
.wsk-etasks-count { padding: 3px 10px; border-radius: 999px; background: var(--wsk-border-light); font-size: 13px; font-weight: 700; color: var(--wsk-text); }
.wsk-etasks-lore { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--wsk-text-secondary); }
.wsk-etasks-list { list-style: none; margin: 0; padding: 0; }
.wsk-etasks-item { border-bottom: 1px solid var(--wsk-border-light); }
.wsk-etasks-item:last-child { border-bottom: 0; }
.wsk-etasks-row { display: flex; align-items: center; gap: var(--wsk-space-4); padding: var(--wsk-space-4) 20px; }
.wsk-etasks-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.wsk-etasks-name { margin: 0; font-size: 16px; font-weight: 700; color: var(--wsk-heading); overflow-wrap: anywhere; }
.wsk-etasks-meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--wsk-space-2); font-size: 13px; }
.wsk-etasks-chip { padding: 3px 10px; border-radius: 999px; font-weight: 700; background: var(--accent-light); color: var(--accent-hover); }
.wsk-etasks-due-overdue { background: #fdecec; color: #8c2323; }
.wsk-etasks-due-soon { background: #fff4e0; color: #7a4300; }
.wsk-etasks-due-later { background: var(--accent-light); color: var(--accent-hover); }
.wsk-etasks-prio-urgent { background: #fdecec; color: #8c2323; }
.wsk-etasks-prio-high { background: #fff4e0; color: #7a4300; }
.wsk-etasks-prio-normal, .wsk-etasks-prio-low, .wsk-etasks-chip-done { background: var(--wsk-border-light); color: var(--wsk-text); }
.wsk-etasks-who { display: inline-flex; align-items: center; gap: 6px; color: var(--wsk-text); }
.wsk-etasks-avatar {
  display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border-radius: 50%;
  background: var(--accent-light); color: var(--accent); font-size: 11px; font-weight: 700;
}
.wsk-etasks-by { color: var(--wsk-meta); }
.wsk-etasks-flag {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px;
  border-radius: 12px; border: 1px solid var(--wsk-border-light); background: var(--wsk-card); color: var(--wsk-meta);
  cursor: pointer; touch-action: manipulation;
}
.wsk-etasks-flag:hover { border-color: var(--accent); background: var(--accent-light); color: var(--accent); }
.wsk-etasks-flag:focus-visible { outline: 3px solid #0b57d0; outline-offset: 2px; }
.wsk-etasks-act {
  flex: 0 0 auto; display: inline-flex; align-items: center; box-sizing: border-box; min-height: 44px; padding: 0 16px;
  border-radius: 10px; border: 1px solid var(--wsk-border); color: var(--wsk-text); font-size: 14px; font-weight: 700;
  text-decoration: none; white-space: nowrap;
}
.wsk-etasks-act:hover { background: var(--accent-light); color: var(--accent-hover); }
.wsk-etasks-act:focus-visible { outline: 3px solid #0b57d0; outline-offset: 2px; }
.wsk-etasks-act-primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.wsk-etasks-act-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: #fff; }
.wsk-etasks-foot { padding: var(--wsk-space-3) 20px var(--wsk-space-4); border-top: 1px solid var(--wsk-border-light); }
.wsk-etasks-add { display: inline-flex; align-items: center; min-height: 44px; font-size: 15px; font-weight: 700; color: var(--accent); text-decoration: none; }
.wsk-etasks-add:hover { color: var(--accent-hover); text-decoration: underline; }
.wsk-etasks-empty { margin: var(--wsk-space-4) 0 0; padding: var(--wsk-space-4) 20px; border: 1px dashed var(--wsk-border); border-radius: 14px; font-size: 15px; color: var(--wsk-text-secondary); }
.wsk-etasks-empty a { display: inline-flex; align-items: center; min-height: 44px; font-weight: 700; color: var(--accent); }
@media (max-width: 640px) {
  .wsk-etasks-row { flex-wrap: wrap; }
  .wsk-etasks-main { flex-basis: calc(100% - 60px); }
  .wsk-etasks-act { margin-left: auto; }
}

/* The survey: opens under the task it is about; Cancel empties the slot. */
.wsk-flag-slot:empty { display: none; }
.wsk-flag-slot { padding: 0 20px var(--wsk-space-4); }
.wsk-flag-panel {
  display: flex; flex-direction: column; gap: var(--wsk-space-4); box-sizing: border-box; max-width: 440px;
  padding: var(--wsk-space-5); background: var(--wsk-card); border: 1px solid var(--wsk-border-light); border-radius: 16px;
  box-shadow: 0 12px 32px rgba(20, 20, 40, .12);
}
.wsk-flag-title { margin: 0; font-size: 20px; color: var(--wsk-heading); }
.wsk-flag-lead { margin: 0; font-size: 14px; color: var(--wsk-text-secondary); }
.wsk-flag-error { margin: 0; padding: 10px 14px; border-left: 3px solid #8c2323; background: #fdecec; color: #8c2323; font-size: 14px; }
.wsk-flag-reasons { display: flex; flex-wrap: wrap; gap: var(--wsk-space-2); margin: 0; padding: 0; border: 0; min-width: 0; }
.wsk-flag-pill { position: relative; display: inline-flex; }
.wsk-flag-input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }
.wsk-flag-pill > span {
  display: inline-flex; align-items: center; box-sizing: border-box; min-height: 44px; padding: 0 16px; border-radius: 999px;
  border: 1px solid var(--wsk-border); background: var(--wsk-card); color: var(--wsk-text); font-size: 14px; font-weight: 700; cursor: pointer;
}
.wsk-flag-input:checked + span { border: 2px solid var(--accent); padding: 0 15px; background: var(--accent-light); color: var(--accent-hover); }
.wsk-flag-input:focus-visible + span { outline: 3px solid #0b57d0; outline-offset: 2px; }
/* The ONE write-in box: shown only while "Something else" is ticked. */
.wsk-flag-note { display: none; flex-direction: column; gap: 6px; }
.wsk-flag-form:has(input[value="other"]:checked) .wsk-flag-note { display: flex; }
@supports not selector(:has(*)) { .wsk-flag-note { display: flex; } }
.wsk-flag-note label { font-size: 14px; font-weight: 700; }
.wsk-flag-note textarea { box-sizing: border-box; width: 100%; padding: 10px 12px; border: 1px solid var(--wsk-border); border-radius: 10px; font: inherit; font-size: 15px; resize: vertical; }
.wsk-flag-hint { margin: 0; font-size: 13px; color: var(--wsk-meta); }
.wsk-flag-remove { display: flex; align-items: center; gap: 10px; min-height: 44px; font-size: 14px; }
.wsk-flag-remove input { width: 18px; height: 18px; accent-color: var(--accent); }
.wsk-flag-actions { display: flex; justify-content: flex-end; gap: 10px; }
.wsk-flag-cancel, .wsk-flag-send { min-height: 44px; padding: 0 18px; border-radius: 10px; font: inherit; font-size: 15px; font-weight: 700; cursor: pointer; }
.wsk-flag-cancel { border: 1px solid var(--wsk-border); background: var(--wsk-card); color: var(--wsk-text); }
.wsk-flag-send { border: 0; background: var(--accent); color: #fff; }
.wsk-flag-send:hover { background: var(--accent-hover); }
.wsk-flag-cancel:focus-visible, .wsk-flag-send:focus-visible { outline: 3px solid #0b57d0; outline-offset: 2px; }

/* KAN-453: done checkbox on the email card; the flag button on a Home task card. */
.wsk-etasks-check { flex: 0 0 auto; width: 22px; height: 22px; margin: 0; accent-color: var(--accent); cursor: pointer; }
.wsk-etasks-done { display: flex; align-items: center; justify-content: center; min-width: 44px; min-height: 44px; margin: 0; }
.wsk-etasks-error { margin: var(--wsk-space-3) 20px 0; }

/* KAN-462: Home "Optional question" - progress dots, the buttons, and the thank-you state. */
.wsk-qa-progress { display: flex; align-items: center; gap: 8px; margin: 0 0 8px; font-size: 13px; color: var(--wsk-text-secondary); }
.wsk-qa-dots { display: inline-flex; gap: 4px; }
.wsk-qa-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--wsk-border); }
.wsk-qa-dot-on { background: var(--accent); }
.wsk-qa .wsk-qa-btn { min-height: 44px; }
.wsk-qa-thanks { border-color: #cfe7df; }
.wsk-qa-saved { margin: 8px 0; font-size: 15px; font-weight: 700; color: var(--wsk-heading); overflow-wrap: anywhere; }
.wsk-qa-thanks .wsk-qa-btn { margin-top: 12px; }
.wsk-qa .wsk-btn-outline.wsk-qa-btn:disabled { color: var(--wsk-meta); border-color: var(--wsk-border); background: var(--wsk-card); cursor: not-allowed; transform: none; }

/* KAN-461: the "What's wrong with this answer?" flag under every AI output. The survey itself reuses the
   task survey's .wsk-flag-* classes above. */
.wsk-aiflag { margin-top: var(--wsk-space-2); }
.wsk-aiflag-btn { min-height: 44px; padding: 0 4px; border: 0; background: none; font: inherit; font-size: 14px; color: var(--wsk-text-secondary); text-decoration: underline; cursor: pointer; }
.wsk-aiflag-btn:hover { color: var(--accent-hover); }
.wsk-aiflag-btn:focus-visible { outline: 3px solid #0b57d0; outline-offset: 2px; }
.wsk-aiflag-slot { padding: 0; }
/* KAN-511: after Send, one muted line takes the place of the "What's wrong..." link: no card, no shadow, no overlay.
   Same 44px height as the link so nothing around it moves. Without :has support the link simply stays above it. */
.wsk-flag-thanks { display: flex; align-items: center; min-height: 44px; margin: 0; padding: 0 4px; font-size: 14px; color: var(--wsk-text-secondary); }
.wsk-aiflag:has(.wsk-flag-thanks) > .wsk-aiflag-btn { display: none; }
@supports not selector(:has(*)) { .wsk-flag-thanks { min-height: 0; } }
.wsk-flag-slot:has(.wsk-flag-thanks) { padding-bottom: 0; }
/* The task row's flag icon after Send: invisible but still in flow, so the row does not move. */
.wsk-etasks-flag-spent { visibility: hidden; pointer-events: none; }

/* KAN-468: a colleague's task is one line until it is opened. The native details toggles it (keyboard and
   screen reader for free); the chevron comes from the shared disclosure rule above. The summary is the
   row; opened, the unchanged full card follows under it. Title shrinks and ellipsizes, the rest keeps its size. */
.wsk-task-collapsed { margin: 0; }
.wsk-task-collapsed-summary {
  box-sizing: border-box; min-height: 44px; padding: 8px var(--wsk-space-3); cursor: pointer;
  background: var(--wsk-card); border: 1px solid var(--wsk-border); border-radius: 12px;
  color: var(--wsk-text); font-size: 14px; line-height: 1.3; touch-action: manipulation;
}
.wsk-task-collapsed-summary:hover { border-color: var(--accent); }
.wsk-task-collapsed-summary:focus-visible { outline: 3px solid #0b57d0; outline-offset: 2px; }
.wsk-task-collapsed-title { flex: 1 1 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 700; }
.wsk-task-collapsed-owner, .wsk-task-collapsed-due, .wsk-task-collapsed-priority, .wsk-task-collapsed-category, .wsk-task-collapsed-from { flex: 0 1 auto; white-space: nowrap; }
.wsk-task-collapsed-category { width: 9em; flex: 0 0 9em; overflow: hidden; text-overflow: ellipsis; font-size: 12px; font-weight: 600; color: var(--wsk-text-secondary); }
.wsk-task-collapsed-from { max-width: 12em; overflow: hidden; text-overflow: ellipsis; font-size: 13px; color: var(--wsk-text-secondary); }
.wsk-task-collapsed-owner { max-width: 10em; overflow: hidden; text-overflow: ellipsis; color: var(--wsk-text-secondary); }
.wsk-task-collapsed-due { font-size: 13px; color: var(--wsk-text-secondary); }
.wsk-task-collapsed-due.wsk-task-overdue { color: #8c2323; }
.wsk-task-collapsed-priority { padding: 2px 8px; font-size: 12px; }
.wsk-task-collapsed[open] > .wsk-task-collapsed-summary { border-bottom-left-radius: 0; border-bottom-right-radius: 0; border-bottom-color: transparent; }
.wsk-task-collapsed[open] > .wsk-task-card { border-top-left-radius: 0; border-top-right-radius: 0; }
@media (max-width: 520px) { .wsk-task-collapsed-due, .wsk-task-collapsed-from, .wsk-task-collapsed-category { display: none; } }
/* The Collapsed view / Expanded view switch: the same soft tray and white active pill as "Whose tasks?". */
.wsk-others-view {
  display: inline-flex; gap: var(--wsk-space-1); margin: var(--wsk-space-2) 0 0; padding: var(--wsk-space-1);
  border-radius: 999px; background: var(--accent-light);
}
.wsk-others-view-btn {
  min-height: 32px; padding: 0 12px; border: 0; border-radius: 999px; background: transparent;
  color: var(--wsk-text-secondary); font: inherit; font-size: 13px; cursor: pointer; touch-action: manipulation;
}
.wsk-others-view-btn:hover { background: rgba(255, 255, 255, .65); color: var(--wsk-text); }
.wsk-others-view-btn:focus-visible { outline: 3px solid #0b57d0; outline-offset: 1px; }
.wsk-others-view-btn[aria-pressed="true"] { background: #fff; color: var(--accent); font-weight: 700; box-shadow: 0 1px 2px rgba(0, 0, 0, .08); }

/* KAN-473: Add task's source email is picked in the Inbox. */
.wsk-pick-banner { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; margin: 0 0 12px; padding: 10px 14px; border: 1px solid var(--accent); border-radius: 8px; background: var(--wsk-cream); }
.wsk-pick-box { display: inline-block; width: 16px; height: 16px; margin-right: 8px; vertical-align: -3px; border: 2px solid var(--accent); border-radius: 4px; background: #fff; }
.wsk-erow-pick { cursor: pointer; }
.wsk-erow-pick:hover .wsk-pick-box, .wsk-erow-pick:focus-visible .wsk-pick-box { background: var(--accent); box-shadow: inset 0 0 0 2px #fff; }
.wsk-task-source { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; }
.wsk-task-source-label { font-weight: 600; }
.wsk-task-source-who { color: var(--wsk-muted, #555); }
.wsk-task-source-pick { text-decoration: none; }

/* ==== KAN-477 UI polish (begin) ====
   Numbers that sit in columns line up (tabular figures); headings wrap evenly instead of leaving
   one orphan word; the small controls get a 44px target on touch screens. */
.wsk-erow-date, .wsk-detail-date, .wsk-srch-count, .wsk-task-pill-count, .wsk-task-section-count,
.wsk-sched-hour-label, .wsk-meeting-hour-label, .wsk-sched-event-time, .wsk-event-time,
.wsk-task-dates, .wsk-engine-task-dates, .wsk-task-received, .wsk-task-due-label,
.wsk-task-collapsed-due, .wsk-task-overdue, .wsk-etasks-count { font-variant-numeric: tabular-nums; }
.wsk-tasks-heading, .wsk-card-title, .ws-card-title, .wsk-sched-title, .wsk-inbox-heading,
.wsk-srch-title, .wsk-settings-title, .wsk-settings-h2, .wsk-sp-group-title,
.wsk-task-section-heading, .wsk-task-popover-title, .wsk-auth-title, .wsk-ask-empty-h2 { text-wrap: balance; }
@media (pointer: coarse) {
  .wsk-composer .wsk-file-suggestions .wsk-att-open { min-height: 44px; }
  .wsk-task-pill { min-height: 44px; }
  .wsk-viewer-more > summary { min-height: 44px; }
}
/* ==== KAN-477 UI polish (end) ==== */

/* ==== KAN-479 Undo toast (begin) ====
   New elements only (hub/static/undo_toast.js builds them): nothing that already exists is restyled. A dark notice at
   the bottom left after Archive, Report spam, Delete or Mark done, and a white one when the server refuses.
   Buttons are 44px tall; the countdown bar is the only moving part and is dropped for reduced motion. */
.wsk-toast-region { position: fixed; left: 16px; bottom: 16px; z-index: 3000; display: flex; flex-direction: column; gap: 8px;
  max-width: min(520px, calc(100vw - 32px)); pointer-events: none; }
.wsk-toast-slot { display: flex; flex-direction: column; gap: 8px; }
.wsk-toast { position: relative; overflow: hidden; display: flex; align-items: center; gap: 12px; box-sizing: border-box;
  padding: 10px 8px 12px 16px; border-radius: 10px; font-size: 16px; line-height: 1.35; pointer-events: auto;
  box-shadow: 0 6px 20px rgba(0, 0, 0, .28); }
.wsk-toast-undo { background: #26242f; color: #fff; }
.wsk-toast-error { background: #fff; color: #1a1a1a; border: 2px solid #b3261e; }
.wsk-toast-icon { flex: none; }
.wsk-toast-error .wsk-toast-icon { color: #b3261e; }
.wsk-toast-text { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.wsk-toast-btn, .wsk-toast-close { flex: none; box-sizing: border-box; min-height: 44px; font: inherit; cursor: pointer;
  border-radius: 8px; touch-action: manipulation; }
.wsk-toast-btn { min-width: 64px; padding: 0 16px; font-weight: 700; }
.wsk-toast-undo .wsk-toast-btn { background: #fff; color: #26242f; border: 2px solid #fff; }
.wsk-toast-error .wsk-toast-btn { background: var(--accent); color: #fff; border: 2px solid var(--accent); }
.wsk-toast-close { width: 44px; padding: 0; display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: 2px solid transparent; color: inherit; }
.wsk-toast-close:hover { border-color: currentColor; }
.wsk-toast-btn:focus-visible, .wsk-toast-close:focus-visible { outline: 3px solid #8ab4f8; outline-offset: 2px; }
.wsk-toast-error .wsk-toast-btn:focus-visible, .wsk-toast-error .wsk-toast-close:focus-visible { outline-color: #0b57d0; }
.wsk-toast-bar { position: absolute; left: 0; right: 0; bottom: 0; height: 4px; background: rgba(255, 255, 255, .75);
  transform-origin: left center; animation: wsk-toast-countdown 6s linear forwards; }
.wsk-toast:hover .wsk-toast-bar, .wsk-toast:focus-within .wsk-toast-bar { animation-play-state: paused; }
@keyframes wsk-toast-countdown { from { transform: scaleX(1); } to { transform: scaleX(0); } }
@media (prefers-reduced-motion: reduce) { .wsk-toast-bar { display: none; animation: none; } }
/* A card that was just marked done leaves the list at once; the server's answer replaces the card either way. */
.wsk-undo-leaving { display: none; }
/* ==== KAN-479 Undo toast (end) ==== */

/* ==== KAN-480 loading placeholders (begin) ====
   While a new Inbox list or Home board is on its way, the rows already on screen keep their size and show as grey bars
   (hub/static/list_loading.js adds .wsk-skel-row and aria-busy). Same shimmer/pulse the rest of the site uses; for
   reduced motion the bars are still and grey. */
.wsk-skel-row { position: relative; pointer-events: none; }
.wsk-skel-row > * { visibility: hidden; }
.wsk-erow.wsk-skel-row::before { content: ""; position: absolute; left: 16px; right: 16px; top: 50%; height: 14px; margin-top: -7px;
  border-radius: 7px; background: linear-gradient(90deg, #efedfb 25%, #e2dcfb 37%, #efedfb 63%); background-size: 400% 100%;
  animation: wsk-shimmer 1.4s ease infinite; }
.wsk-skel-made { min-height: 57px; }
.wsk-task-card.wsk-skel-row::before, .wsk-task-card.wsk-skel-row::after { content: ""; position: absolute; left: 24px; height: 16px;
  border-radius: 8px; background: linear-gradient(90deg, #efedfb 25%, #e2dcfb 37%, #efedfb 63%); background-size: 400% 100%;
  animation: wsk-shimmer 1.4s ease infinite; }
.wsk-task-card.wsk-skel-row::before { top: 24px; width: 55%; height: 20px; }
.wsk-task-card.wsk-skel-row::after { top: 60px; width: 35%; }
@media (prefers-reduced-motion: reduce) {
  .wsk-erow.wsk-skel-row::before, .wsk-task-card.wsk-skel-row::before, .wsk-task-card.wsk-skel-row::after {
    animation: none; background: var(--wsk-border-light); }
}
/* ==== KAN-480 loading placeholders (end) ==== */

/* ==== KAN-481 show/hide password button (begin) ====
   Layout for the eye button beside a password box on the sign-in pages (and the admin add/reset forms). Every
   selector is new (.wsk-pw-*): the password box keeps all of its own styling. The button sits beside the box,
   44px square. Nothing animates, so reduced motion needs no override. */
.wsk-pw-wrap { display: flex; align-items: center; gap: 8px; }
.wsk-pw-wrap > input { flex: 1 1 auto; min-width: 0; }
.wsk-pw-toggle {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  min-width: 44px; min-height: 44px; padding: 0 10px; cursor: pointer;
  color: var(--wsk-text, #242424); background: var(--wsk-card, #fff);
  border: 1.5px solid var(--wsk-border, #cfcfcf); border-radius: 8px;
}
.wsk-pw-toggle:hover { border-color: var(--wsk-text-secondary, #495156); }
.wsk-pw-toggle:focus-visible { outline: 3px solid #0b57d0; outline-offset: 2px; }
.wsk-pw-slash { display: none; }
.wsk-pw-toggle[aria-pressed="true"] .wsk-pw-slash { display: inline; }
/* ==== KAN-481 show/hide password button (end) ==== */

/* ==== KAN-482 Copy button (begin) ====
   Layout for the Copy button and its status line under the search AI summary (KAN-503: the reply box no longer has one). Every
   selector is new (.wsk-copy-*); nothing existing is restyled. There is no animation, so reduced motion needs
   no override. Touch target: 44px. */
.wsk-copy-row { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; margin: 12px 0 0; }
.wsk-copy-btn { min-width: 44px; min-height: 44px; }
.wsk-copy-status { font-size: 15px; color: var(--wsk-text-secondary); }
/* ==== KAN-482 Copy button (end) ==== */

/* ==== KAN-485 (approved mockup additions: wsk-conv-* only; no existing selector is overridden) ==== */

/* Conversation block: sits in the existing flow under the message body, plain bordered list like the tasks card */
.wsk-conv { margin-top: var(--wsk-space-5); border: 1px solid var(--wsk-border-light); border-radius: 14px; overflow: hidden; background: var(--wsk-card); }
.wsk-conv-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px var(--wsk-space-3); padding: 14px 20px; border-bottom: 1px solid var(--wsk-border-light); }
.wsk-conv-title { margin: 0; font-size: 18px; color: var(--wsk-heading); }
.wsk-conv-hint { font-size: 14px; color: var(--wsk-text-secondary); }
.wsk-conv-msg { border-bottom: 1px solid var(--wsk-border-light); }
.wsk-conv-msg:last-child { border-bottom: 0; }
.wsk-conv-msg summary {
  list-style: none; cursor: pointer; display: grid; grid-template-columns: 150px 230px minmax(0, 1fr) auto;
  align-items: center; gap: var(--wsk-space-3); min-height: 52px; padding: 10px 20px; font-size: 15px;
}
.wsk-conv-msg summary::-webkit-details-marker { display: none; }
.wsk-conv-msg summary:hover { background: var(--accent-light); }
.wsk-conv-msg summary:focus-visible { outline: 3px solid #0b57d0; outline-offset: -3px; }
.wsk-conv-who { font-weight: 700; color: var(--wsk-heading); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wsk-conv-when { color: var(--wsk-meta); font-size: 14px; }
.wsk-conv-first { color: var(--wsk-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wsk-conv-toggle { color: var(--accent); font-weight: 700; font-size: 14px; }
.wsk-conv-msg[open] .wsk-conv-toggle::before { content: "Hide"; }
.wsk-conv-msg[open] .wsk-conv-toggle { font-size: 0; }
.wsk-conv-msg[open] .wsk-conv-toggle::before { font-size: 14px; }
.wsk-conv-msg[open] .wsk-conv-first { white-space: normal; }
.wsk-conv-full { margin: 0; padding: 4px 20px 8px 20px; overflow-wrap: anywhere; font-size: 16px; line-height: 1.5; white-space: pre-line; color: var(--wsk-text); }

.wsk-conv-open { margin: 0; padding: 0 20px 14px; font-size: 14px; }
.wsk-conv-open a { color: var(--accent); font-weight: 700; }

/* Task card additions (inside the existing row) */
.wsk-conv-source { display: inline-flex; align-items: center; gap: 6px; margin: 0; font-size: 13px; color: var(--wsk-text-secondary); }
.wsk-conv-followup { margin: 0; padding: 8px 12px; border-left: 4px solid var(--accent); background: var(--accent-light); border-radius: 0 8px 8px 0; font-size: 14px; line-height: 1.45; color: var(--wsk-text); }
.wsk-conv-followup-when { color: var(--wsk-meta); white-space: nowrap; }


/* Tasks list: small message count inside the existing meta strip */
.wsk-conv-count-chip { display: inline-flex; align-items: center; gap: 5px; padding: 3px 10px; border-radius: 999px; background: var(--wsk-border-light); color: var(--wsk-text); font-size: 13px; font-weight: 700; margin-right: 6px; }

@media (max-width: 640px) {
  .wsk-conv-msg summary { grid-template-columns: minmax(0, 1fr) auto; row-gap: 2px; }
  .wsk-conv-who { grid-column: 1; }
  .wsk-conv-toggle { grid-column: 2; grid-row: 1; }
  .wsk-conv-when { grid-column: 1 / -1; }
  .wsk-conv-first { grid-column: 1 / -1; white-space: normal; }
  .wsk-conv-answered-actions { width: 100%; }
}

/* Home's task card: the same added lines, spaced to sit in the card's own rhythm. */
.wsk-task-card .wsk-conv-followup { margin: 8px 0 10px; }
.wsk-task-card .wsk-conv-answered { margin: 12px 0 0; }
/* Home's task card: the flag sits on the action row next to Hide (it used to be a row of its own). On a phone the
   row wraps; the flag stays on the last line, beside Hide, so no empty band is left under the buttons. */
@media (max-width: 640px) { .wsk-task-card .wsk-task-actions { align-items: flex-end; } }
/* Home's task card: a removed task stays in place as a compact greyed row with Undo (owner, 2026-10-28). */
.wsk-task-card .wsk-task-remove { margin: 0; flex: 0 0 auto; }
.wsk-task-removed { display: flex; align-items: center; justify-content: space-between; gap: var(--wsk-space-3); padding: 12px 24px; background: var(--wsk-bg); border-style: dashed; }
.wsk-task-removed-text { margin: 0; font-size: 16px; color: var(--wsk-text-secondary); }
.wsk-task-removed-undo { margin: 0; }
.wsk-task-removed-undo .wsk-task-action { min-height: 44px; padding: 0 22px; }
/* ==== KAN-485 (end) ==== */

/* KAN-494: the Add task "Email" picker. Same look as the Edit task dialog: 16px text, 44px or larger targets,
   bold label, visible focus. The radios are the form's source_id field, hidden behind their card. The search box
   and the chosen-card swap are script-only (.wsk-te-js stays hidden until taskeditor.js adds .wsk-js). */
.wsk-te-picker { display: grid; gap: 8px; min-width: 0; }
.wsk-te-picker .wsk-te-label { display: block; }
.wsk-te-find { display: grid; gap: 8px; min-width: 0; }
.wsk-js .wsk-te-picker input[type=search] { display: block; }
.wsk-task-editor .wsk-te-picker input[type=search] {
  font-size: 16px; line-height: 1.4; min-height: 48px; padding: 10px 12px; border-radius: 10px; background-color: #fff;
}
.wsk-te-picker input[type=search]:focus-visible { outline: 3px solid #0b57d0; outline-offset: 1px; border-color: var(--accent); }
.wsk-te-mails { display: grid; gap: 8px; max-height: 320px; overflow: auto; padding: 2px; }
.wsk-task-editor .wsk-te-mails label { position: relative; display: block; margin: 0; }
.wsk-task-editor .wsk-te-mails input {
  position: absolute; inset: 0; opacity: 0; margin: 0; padding: 0; width: 100%; height: 100%; min-height: 0; border: 0; cursor: pointer;
}
.wsk-te-mail-card {
  position: relative; display: grid; gap: 2px; min-height: 48px; padding: 10px 40px 10px 12px; box-sizing: border-box;
  border: 1px solid var(--wsk-border); border-radius: 10px; background: #fff; color: var(--wsk-text); font-size: 16px; line-height: 1.35;
}
.wsk-te-mails input:hover + .wsk-te-mail-card { border-color: var(--wsk-icon); }
.wsk-te-mails input:checked + .wsk-te-mail-card { border: 2px solid var(--accent); padding: 9px 39px 9px 11px; background: var(--accent-light); }
.wsk-te-mails input:focus-visible + .wsk-te-mail-card { outline: 3px solid #0b57d0; outline-offset: 2px; }
.wsk-te-mail-check { display: none; position: absolute; top: 12px; right: 12px; color: var(--accent); }
.wsk-te-mails input:checked + .wsk-te-mail-card .wsk-te-mail-check { display: block; }
.wsk-te-mail-top { display: flex; justify-content: space-between; gap: 12px; align-items: baseline; }
.wsk-te-mail-who { font-weight: 700; color: var(--wsk-heading); overflow-wrap: anywhere; }
.wsk-te-mail-date { flex: none; font-size: 14px; color: var(--wsk-meta); }
.wsk-te-mail-subject { overflow-wrap: anywhere; }
.wsk-te-mail-convo { margin: 0; font-size: 14px; color: var(--wsk-text-secondary); }
.wsk-te-chosen {
  display: grid; gap: 4px; padding: 12px; border: 1px solid var(--wsk-border); border-radius: 10px; background: var(--wsk-cream); min-width: 0;
}
.wsk-te-chosen[hidden] { display: none; }
.wsk-te-chosen-line { margin: 0; font-size: 16px; line-height: 1.4; font-weight: 700; color: var(--wsk-heading); overflow-wrap: anywhere; }
.wsk-te-change {
  justify-self: start; display: inline-flex; align-items: center; min-height: 44px; padding: 0; border: 0; background: none;
  font: inherit; font-size: 16px; font-weight: 700; color: var(--accent); text-decoration: underline; cursor: pointer;
}
.wsk-te-change:focus-visible { outline: 3px solid #0b57d0; outline-offset: 2px; }
.wsk-te-picker[data-chosen] .wsk-te-find { display: none; }
.wsk-te-mail { display: block; }
.wsk-te-picker .wsk-te-label, .wsk-task-editor[data-task-add-form] > label { font-size: 16px; font-weight: 700; color: var(--wsk-heading); }
.wsk-task-editor[data-task-add-form] > label input:not([type=radio]), .wsk-task-editor[data-task-add-form] > label textarea,
.wsk-task-editor[data-task-add-form] > label select { font-size: 16px; min-height: 48px; border-radius: 10px; font-weight: 400; }
.wsk-te-more { margin: 0; font-size: 14px; }

/* ==== KAN-502: change priority, assignee or due date right from the task card ====
   The chips on the clerk's own card are buttons (templates/workspace/fragments/inline_edit.html) that open a
   small menu (static/inline_edit.js). The cue that they can be pressed is deliberately quiet, for an older
   audience that should not have to guess: a small caret after the words, a soft ring on hover, and the
   ordinary focus ring. The card's layout, sizes and colours are unchanged; a teammate's card has none of this. */
.wsk-inline-edit { position: relative; display: inline-flex; min-width: 0; max-width: 100%; }
.wsk-task-when[data-inline-edit] { position: relative; }
.wsk-task-card.wsk-inline-open { z-index: 40; }      /* inline_edit.js: the card with an open menu sits above the next cards */
.wsk-inline-chip { font-family: inherit; cursor: pointer; }
button.wsk-task-chip.wsk-inline-chip { line-height: 1.4; }
.wsk-inline-chip .wsk-inline-caret { flex: 0 0 10px; margin-left: 1px; opacity: .65; }
.wsk-inline-chip:hover { box-shadow: 0 0 0 2px var(--accent-light); }
.wsk-inline-chip:hover .wsk-inline-caret, .wsk-inline-chip[aria-expanded="true"] .wsk-inline-caret { opacity: 1; }
.wsk-inline-chip[aria-expanded="true"] { box-shadow: 0 0 0 2px var(--accent-light); }
.wsk-inline-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* KAN-524: the due date is a quiet chip, the same 13px size and round shape as the red Overdue pill beside it and
   the priority and assignee chips under it, but neutral: no grey fill, a hairline border, the muted date colour.
   Every property is stated outright (appearance, fill, border, font) so the chip never shows the browser's own grey
   button, whatever else styles buttons. The caret and soft hover ring are the click cue; the hit area is
   stretched to 44px by an invisible ::after, so the card's height does not change. */
button.wsk-inline-chip.wsk-inline-due { position: relative; display: inline-flex; align-items: center; gap: 4px; margin: 0; padding: 2px 10px; min-height: 24px; box-sizing: border-box; appearance: none; -webkit-appearance: none; border: 1px solid var(--wsk-border); border-radius: 999px; background: transparent; color: var(--wsk-text-secondary); font-family: inherit; font-size: 13px; font-weight: 600; line-height: 1.4; font-variant-numeric: tabular-nums; white-space: nowrap; cursor: pointer; }
button.wsk-inline-chip.wsk-inline-due::after { content: ""; position: absolute; top: -10px; bottom: -10px; left: -2px; right: -2px; }
button.wsk-inline-chip.wsk-inline-due:hover, button.wsk-inline-chip.wsk-inline-due[aria-expanded="true"] { background: transparent; border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-light); }
.wsk-inline-due .wsk-task-suggested-flag { font-size: 12px; font-weight: 400; }
/* A suggested or passed date can be a longer sentence: the chip wraps inside the card instead of pushing it wide. */
.wsk-task-dates-suggested .wsk-inline-due { white-space: normal; text-align: left; border-radius: 16px; }
.wsk-inline-menu { position: absolute; z-index: 30; top: calc(100% + 6px); left: 0; display: flex; flex-direction: column; gap: 2px; box-sizing: border-box; min-width: 220px; max-width: min(340px, calc(100vw - 16px)); padding: 6px; border: 1px solid var(--wsk-border); border-radius: 12px; background: var(--wsk-card); color: var(--wsk-text); box-shadow: 0 8px 24px rgba(16, 24, 40, .14); white-space: normal; text-align: left; font-size: 15px; font-weight: 400; }
/* An author display:flex beats the UA's [hidden]{display:none}; say it again. */
.wsk-inline-menu[hidden] { display: none; }
.wsk-inline-menu-due { left: auto; right: 0; min-width: 290px; }
.wsk-inline-menu.wsk-inline-right { left: auto; right: 0; }
.wsk-inline-menu.wsk-inline-left { left: 0; right: auto; }
.wsk-inline-form { display: flex; flex-direction: column; gap: 2px; margin: 0; }
.wsk-inline-option { display: flex; align-items: center; gap: 10px; box-sizing: border-box; width: 100%; min-height: 44px; padding: 6px 10px; border: 0; border-radius: 8px; background: transparent; color: var(--wsk-text); font: inherit; text-align: left; cursor: pointer; }
.wsk-inline-option:hover, .wsk-inline-option:focus-visible { background: var(--accent-light); }
.wsk-inline-option:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.wsk-inline-option[aria-checked="true"] { font-weight: 600; }
.wsk-inline-option-name { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.wsk-inline-option-hint { flex: 0 0 auto; color: var(--wsk-text-secondary); font-size: 14px; font-variant-numeric: tabular-nums; }
.wsk-inline-check { flex: 0 0 16px; color: var(--accent); }
.wsk-inline-clear { margin-top: 2px; border-top: 1px solid var(--wsk-border); border-radius: 0 0 8px 8px; color: var(--wsk-text-secondary); }
.wsk-inline-date { display: grid; gap: 6px; margin-top: 4px; padding: 8px 10px 4px; border-top: 1px solid var(--wsk-border); color: var(--wsk-text-secondary); font-size: 14px; }
.wsk-inline-date-row { display: flex; gap: 8px; align-items: stretch; }
.wsk-inline-date input { flex: 1 1 auto; min-width: 0; min-height: 44px; box-sizing: border-box; padding: 0 10px; border: 1px solid var(--wsk-border); border-radius: 8px; font: inherit; color: var(--wsk-text); background: var(--wsk-card); }
.wsk-inline-date input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.wsk-inline-set { flex: 0 0 auto; min-height: 44px; }
.wsk-inline-saved { margin: 8px 0 0; font-size: 14px; color: var(--wsk-text-secondary); }
@media (max-width: 600px) {
  .wsk-inline-menu { min-width: min(260px, calc(100vw - 32px)); }
  .wsk-inline-menu.wsk-inline-menu-due { min-width: min(290px, calc(100vw - 32px)); }
}
/* ==== end KAN-502 ==== */

/* ==== KAN-525: the chat email draft looks like the email page's Reply box ====
   The card carries .wsk-composer, so the Reply box's tokens, head, To/Subject rows, AI badge and
   gradient frame come from the rules above; this block only adjusts what the chat card does differently. */
.wsk-chat-drafts { grid-template-columns:minmax(0,1fr); }
.wsk-chat-draft.wsk-composer { padding:20px; }
.wsk-chat-draft .wsk-composer-head { justify-content:flex-start; margin:0 0 8px; }
.wsk-chat-draft .wsk-composer-head .wsk-chat-draft-name { font-size:20px; line-height:28px; font-weight:600; color:var(--rc-ink); }
.wsk-chat-draft .wsk-mail-fields { margin:0 0 16px; }
.wsk-chat-draft .wsk-mail-fields .wsk-chat-draft-subject { display:block; font-size:15px; color:var(--rc-ink); white-space:normal; overflow:visible; text-overflow:clip; }
.wsk-chat-draft .wsk-chat-draft-frame { margin:0; }
.wsk-chat-draft .wsk-chat-draft-text-slot { border-radius:10px; background:#fff; font-size:18px; }
.wsk-chat-draft .wsk-chat-draft-text { margin:0; padding:18px; border:0; border-radius:10px; background:#fff; color:var(--rc-ink); font-size:18px; line-height:1.6; min-height:7em; max-height:70vh; }
.wsk-chat-draft .wsk-chat-draft-text:focus { outline:3px solid #0b57d0; outline-offset:2px; }
.wsk-chat-draft .wsk-chat-draft-foot { display:flex; flex-wrap:wrap; align-items:center; gap:10px 16px; margin-top:14px; }
.wsk-chat-draft .wsk-chat-draft-actions { margin:0; }
.wsk-chat-draft .wsk-chat-draft-actions:empty { min-height:44px; }
.wsk-chat-draft .wsk-chat-draft-actions .wsk-btn-primary, .wsk-chat-draft .wsk-chat-draft-actions .wsk-btn-outline { display:inline-flex; align-items:center; gap:8px; min-height:44px; padding:10px 18px; font-size:16px; border-radius:8px; }
.wsk-chat-draft .wsk-chat-draft-sendnote { margin:0 0 0 auto; font-size:14px; line-height:1.4; color:var(--rc-muted); }
.wsk-copy-icon { position:relative; flex:none; width:16px; height:16px; background:inherit; }
.wsk-copy-icon::before, .wsk-copy-icon::after { content:""; position:absolute; box-sizing:border-box; width:10px; height:11px; border:2px solid currentColor; border-radius:2px; }
.wsk-copy-icon::before { left:0; top:0; }
.wsk-copy-icon::after { left:5px; top:4px; background:inherit; }
/* Under the answer: Copy whole answer, Sources and the flag link share one row. */
.wsk-chat-tools { display:flex; flex-wrap:wrap; align-items:center; gap:0 20px; margin:8px 0 0; }
.wsk-chat-tools .wsk-chat-copy { margin:0; min-height:44px; padding:0 4px; font-size:14px; color:var(--wsk-text-secondary); text-decoration:underline; }
.wsk-chat-tools .wsk-chat-copy:hover { border-color:transparent; color:var(--accent-hover); }
.wsk-chat-tools .wsk-chat-source-details { margin:0; font-size:14px; }
.wsk-chat-tools .wsk-chat-source-details summary { display:list-item; list-style-position:inside; padding:12px 4px; }
.wsk-chat-tools .wsk-chat-source-details[open] { order:5; flex-basis:100%; }
.wsk-chat-tools .wsk-aiflag { display:contents; }
.wsk-chat-tools .wsk-aiflag-btn { margin-left:auto; }
.wsk-chat-tools .wsk-aiflag-slot { order:6; flex-basis:100%; }
.wsk-chat-suggestions .wsk-chat-next-label { flex-basis:100%; margin:6px 0 -2px; font-size:13px; font-weight:600; color:var(--wsk-text-secondary); }
.wsk-chat-suggestions:has(.wsk-chat-next-label) .wsk-chat-suggestion { min-height:44px; padding:10px 18px; border-radius:999px; }
/* ==== end KAN-525 ==== */

/* KAN-529: the Automatic reply card in Settings. Slots into the existing .wsk-sp-row; nothing else restyled. */
.wsk-ar-form { display: flex; flex-direction: column; gap: var(--wsk-space-2); max-width: 62ch; }
.wsk-ar-check { display: flex; align-items: center; gap: var(--wsk-space-2); font-size: 17px; font-weight: 700; color: var(--wsk-heading); }
.wsk-ar-check input { width: 22px; height: 22px; }
.wsk-ar-label { font-size: 16px; font-weight: 700; color: var(--wsk-heading); margin-top: var(--wsk-space-2); }
.wsk-ar-text { resize: vertical; min-height: 7em; line-height: 1.5; }
.wsk-ar-warn { margin: 0; max-width: 62ch; padding: 10px 14px; font-size: 16px; line-height: 1.4; color: var(--sp-warn-ink); background: var(--sp-warn-bg); border-radius: 8px; }
.wsk-ar-subhead { margin: 0; font-size: 16px; font-weight: 700; color: var(--wsk-heading); }
.wsk-ar-list { margin: var(--wsk-space-2) 0 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: var(--wsk-space-1); font-size: 16px; line-height: 1.4; }
.wsk-ar-when { color: var(--wsk-text-secondary); }
.wsk-ar-what { font-weight: 700; color: var(--wsk-text); }
/* KAN-542: on the enrolment card the title is left-aligned, so the summary lines up with it. */
.wsk-mfa-wide .wsk-recovery-context { justify-content: flex-start; padding: 0 4px; text-align: left; }

/* ===== KAN-543: Filtered mail + Spam filter panel (own block; additive, no existing rule restyled) ===== */
.wsk-fm-linkrow { margin: 0 0 14px; font-size: 15px; display: flex; flex-wrap: wrap; gap: 4px 12px; align-items: baseline; }
.wsk-fm-linkrow .wsk-settings-sub { margin: 0; }
.wsk-fm-list { display: flex; flex-direction: column; border: 1px solid var(--wsk-border); border-radius: 12px; overflow: hidden; background: var(--wsk-card, transparent); }
.wsk-fm-row { display: flex; align-items: center; gap: 16px; padding: 14px 16px; border-top: 1px solid var(--wsk-border); }
.wsk-fm-row:first-child { border-top: 0; }
.wsk-fm-main { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1 1 auto; }
.wsk-fm-sender { font-weight: 700; color: var(--wsk-heading); overflow-wrap: anywhere; }
.wsk-fm-addr { font-weight: 400; color: var(--wsk-text-secondary); }
.wsk-fm-subject { color: var(--wsk-text); overflow-wrap: anywhere; }
.wsk-fm-why { font-size: 14px; color: var(--wsk-text-secondary); }
.wsk-fm-date { font-size: 14px; color: var(--wsk-text-secondary); white-space: nowrap; }
.wsk-fm-btn { flex: 0 0 auto; }
.wsk-fm-done { padding: 14px 16px; font-size: 15px; color: var(--wsk-text); background: var(--wsk-soft, rgba(0,0,0,.04)); border-top: 1px solid var(--wsk-border); }
.wsk-fm-note { margin: 8px 0 0; font-size: 14px; color: var(--wsk-text); }
.wsk-fm-note-err { color: #a12222; }
.wsk-fm-stats { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 10px 20px; margin: 6px 0 14px; }
.wsk-fm-stats > div { display: flex; justify-content: space-between; gap: 10px; border-bottom: 1px solid var(--wsk-border); padding: 4px 0; font-size: 14px; }
.wsk-fm-stats dt { margin: 0; color: var(--wsk-text-secondary); }
.wsk-fm-stats dd { margin: 0; font-weight: 700; color: var(--wsk-text); }
@media (max-width: 640px) {
  .wsk-fm-row { flex-wrap: wrap; align-items: flex-start; gap: 8px 12px; }
  .wsk-fm-main { flex-basis: 100%; }
}
