/* Portal styles. One palette from tokens.css (imported so any page that loads app.css has it). */
@import url("/assets/tokens.css");
:root {
  /* One palette: tokens.css (Old Glory navy and red; the portal no longer keeps its own variants). */
  --navy: var(--s-navy);
  --red: var(--s-red);
  --ink: var(--s-ink);
  --muted: var(--s-muted);
  --line: var(--s-line);
  --bg: var(--s-bg);
  --card: var(--s-surface);
  --ok: var(--s-lit-text);
  --warn: var(--s-hold-text);
  --radius: var(--s-radius); /* controls; cards use --s-radius-lg */
  /* the landing body's stack, family for family (src/scss/root.scss; tests/app-look.test.js) */
  font-family: var(--s-font); /* tokens.css, the one stack (tally 217) */
  /* Light only (tally 160, docs/APP-UI-DESIGN.md §2): Claude's taupe page, a lighter taupe bar, and the card from
     Ben's CourseSwap SavingsCard kept on the taupe (tally 228: white, no border, 28px radius, a floating shadow). */
  --app-bg: #FAF9F5;
  --app-bar: #F0EEE6;
  --app-edge: #E8E6DC;
  --app-card: #FFFFFF;
  --app-card-edge: transparent;
  --app-card-radius: 28px;
  --app-card-shadow: 0 2px 6px rgba(0, 0, 0, 0.04), 0 10px 28px rgba(0, 0, 0, 0.1);
  /* tally 191, 228: the glass is now the same white floating card (no blur, no edge); the tints are for accents
     only (a dot, an icon, an eyebrow colour), for the server pages; the islands have the same values as Tailwind keys (tailwind.config.js: glass, tint) */
  --glass-fill: #FFFFFF;
  --glass-edge: transparent;
  --glass-blur: 0px;
  --glass-shadow: 0 2px 6px rgba(0, 0, 0, 0.04), 0 10px 28px rgba(0, 0, 0, 0.1);
  --tint-working: rgba(23, 120, 200, 0.1);
  --tint-working-edge: rgba(23, 120, 200, 0.55);
  --tint-lit: rgba(47, 179, 106, 0.2);
  --tint-lit-edge: rgba(47, 179, 106, 0.75);
  --tint-hold: rgba(224, 161, 0, 0.12);
  --tint-hold-edge: rgba(201, 141, 0, 0.7);
  --tint-track: rgba(23, 120, 200, 0.06);
  --tint-track-edge: rgba(23, 120, 200, 0.14);
}
/* a glass panel on a server page: since tally 228 the one card, white, no border, 28px, the floating shadow */
.s-glass { background: var(--glass-fill); border: 0; border-radius: var(--app-card-radius); box-shadow: var(--glass-shadow); }
/* components.css's .s-card and .s-doc inside the app; app.css loads only in the portal shell (src/portal/html.js),
   so the public site's 16px quiz and report cards stay as they are */
.app-root .s-card, .app-root .s-doc, #main .s-card, #main .s-doc { background: var(--app-card); border: 0; border-radius: var(--app-card-radius); box-shadow: var(--app-card-shadow); padding: 20px; }
/* Inter's @font-face is in tokens.css, imported above. */
* { box-sizing: border-box; }
html { color: var(--ink); background: var(--app-bg); }
body { margin: 0; min-height: 100vh; }
.skip { position: absolute; left: -999px; top: 0; background: var(--navy); color: var(--s-on-fill); padding: 8px 12px; z-index: 10; }
.skip:focus { left: 8px; top: 8px; }
.top { display: flex; align-items: center; justify-content: space-between; gap: 24px; padding: 0 16px; height: 56px; background: var(--app-bar); border-bottom: 1px solid var(--app-edge); color: var(--s-text); position: relative; z-index: 6; }
.top .brand { color: var(--s-navy-text); font-weight: 800; letter-spacing: 0.12em; text-decoration: none; }
.badge { display: inline-block; min-width: 20px; padding: 0 6px; border-radius: 10px; background: var(--s-hold); color: var(--s-ink); font-weight: 700; font-size: 12px; text-align: center; line-height: 20px; margin-left: 4px; }
.signout { display: flex; align-items: center; gap: 12px; margin: 0; color: var(--s-muted); font-size: 14px; }
.signout button { background: transparent; color: var(--s-navy-text); border: 1px solid var(--app-card-edge); }
/* Five sections, five destinations (tally 160, §9 item 4): one nav, a left sidebar from under the header on a
   desktop, a bottom tab bar on a phone. `body.has-side-nav` offsets `main` so the fixed nav never covers it;
   a bare shell (welcome) has neither the class nor the nav. */
/* tally 407 (Ben: the far-left strip lost its colour once the page scrolled): the bar runs from the top of the screen,
   under the header, so its colour reaches the top once the header has scrolled away */
.side-nav { display: flex; flex-direction: column; gap: 2px; position: fixed; top: 0; left: 0; bottom: 0; width: 15rem; padding: 68px 12px 12px; background: var(--app-bar); border-right: 1px solid var(--app-edge); overflow-y: auto; overflow-x: hidden; box-sizing: border-box; }
.side-nav a { display: flex; align-items: center; color: var(--s-text); text-decoration: none; padding: 10px 14px; border-radius: var(--radius); white-space: nowrap; }
.side-nav a[aria-current="page"], .side-nav a:hover { background: var(--app-card); color: var(--s-navy-text); }
.side-nav a[aria-current="page"] { font-weight: 600; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06); }
/* each section's lucide icon before its name (src/portal/html.js NAV) */
.side-nav a { gap: 10px; }
.side-nav .nav-icon { flex: none; }
/* Ben, Oct 15 ("the spacing of the cards doesn't scale well on full screen"): the column fills the width beside the
   sidebar as it does at 1440, up to 2200px; its side padding stays 16px to 1440 and grows past it (28px at 1920, 44px
   at 2560), so a wide screen keeps 1440's proportions instead of a 1200px strip and a white band */
main { max-width: 2200px; margin: 0 auto; padding: 24px clamp(16px, calc(2.5vw - 20px), 48px) 64px; }
body.has-side-nav main { margin-left: 15rem; }
/* tally 407: the fold. Folded by its button (body.nav-collapsed, kept by app.js) or on its own while a Demo run plays
   (body[data-demo-playing], the Demo island), unless the button has opened it again (body.nav-open); icons only, each
   name kept for a screen reader; the page takes the room */
.nav-toggle { display: flex; align-items: center; gap: 10px; margin-top: auto; padding: 10px 14px; border: 0; border-radius: var(--radius); background: transparent; color: var(--s-muted); font: inherit; cursor: pointer; white-space: nowrap; text-align: left; }
.nav-toggle:hover { background: var(--app-card); color: var(--s-navy-text); }
@media (min-width: 641px) {
  /* it eases only while the fold changes (app.js sets nav-easing for the moment): an ease always on set main sliding
     under the bar whenever the page was resized, a full-page capture included (A1's smoke shot) */
  body.nav-easing .side-nav, body.nav-easing.has-side-nav main { transition: width 0.25s ease, margin-left 0.25s ease; }
  /* the bar's colour on the page behind it as well, the whole height of the document: a full-page capture draws a fixed
     bar only as tall as the screen (A1's smoke shot stopped it 890px down) */
  body.has-side-nav { --nav-w: 15rem; background: linear-gradient(to right, var(--app-bar) calc(var(--nav-w) - 1px), var(--app-edge) calc(var(--nav-w) - 1px), var(--app-edge) var(--nav-w), transparent var(--nav-w)); }
  body.has-side-nav.nav-collapsed, body.has-side-nav[data-demo-playing]:not(.nav-open) { --nav-w: 4.5rem; }
  body.nav-collapsed .side-nav, body[data-demo-playing]:not(.nav-open) .side-nav { width: 4.5rem; }
  body.nav-collapsed main, body[data-demo-playing]:not(.nav-open) main { margin-left: 4.5rem; }
  body.nav-collapsed .side-nav .nav-label, body[data-demo-playing]:not(.nav-open) .side-nav .nav-label { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
  body.nav-collapsed .side-nav .badge, body[data-demo-playing]:not(.nav-open) .side-nav .badge { position: absolute; margin: -14px 0 0 14px; }
  body.nav-collapsed .side-nav .nav-toggle .nav-icon, body[data-demo-playing]:not(.nav-open) .side-nav .nav-toggle .nav-icon { transform: scaleX(-1); }
}
/* Ben's design review, group 2: between a phone and 1024px the open bar (15rem) took a third of the screen once a Demo
   run was left (PC 2, Chrome at 720). There the bar is folded unless its button opened it (body.nav-open, app.js) */
@media (min-width: 641px) and (max-width: 1023px) {
  body.has-side-nav:not(.nav-open) { --nav-w: 4.5rem; }
  body.has-side-nav:not(.nav-open) .side-nav { width: 4.5rem; }
  body.has-side-nav:not(.nav-open) main { margin-left: 4.5rem; }
  body.has-side-nav:not(.nav-open) .side-nav .nav-label { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
  body.has-side-nav:not(.nav-open) .side-nav .badge { position: absolute; margin: -14px 0 0 14px; }
  body.has-side-nav:not(.nav-open) .side-nav .nav-toggle .nav-icon { transform: scaleX(-1); }
}
@media (min-width: 641px) and (prefers-reduced-motion: reduce) {
  body.nav-easing .side-nav, body.nav-easing.has-side-nav main { transition: none; }
}
/* headings on the shared token scale (tokens.css: the app's page title and section heading) at the landing title's
   weight and leading (root.scss .cs-title) */
h1 { font-size: var(--s-fs-xl); font-weight: 700; line-height: 1.2em; text-wrap: balance; margin: 0 0 8px; color: var(--s-navy-text); }
/* a page title with its action beside it (the Demo's Start, Ben Oct 15) */
.title-row { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin: 0 0 8px; }
.title-row h1 { margin: 0; }
/* tally 294: a document's title never runs past a phone's edge; a word too long to fit is cut with an ellipsis */
.doc-title { max-width: 100%; overflow-wrap: normal; }
.doc-title-word { display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }
h2 { font-size: var(--s-fs-lg); font-weight: 700; line-height: 1.2em; margin: 32px 0 12px; color: var(--s-navy-text); }
.lede { color: var(--muted); margin: 0 0 24px; }
/* a page inside a nav section says which, with the way back (tally 212: Compliance inside Insights) */
.crumbs { color: var(--muted); font-size: 0.875rem; margin: 0 0 4px; }
section { background: var(--app-card); border: 0; border-radius: var(--app-card-radius); box-shadow: var(--app-card-shadow); padding: 8px 20px 20px; margin-bottom: 16px; }
section h2 { margin-top: 12px; }
/* a table wider than the page scrolls inside its box (src/portal/html.js table(), beta fix pass 4.7); on a phone
   each row is already a card (the max-width 640px block below) */
@media (min-width: 641px) { .table-box { overflow-x: auto; } }
table { width: 100%; border-collapse: collapse; font-size: 14px; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
th, td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--line); vertical-align: top; }
th { background: var(--s-th); font-weight: 600; color: var(--s-navy-text); }
tr:last-child td { border-bottom: 0; }
td.empty { color: var(--muted); text-align: center; padding: 24px; }
td small { color: var(--muted); margin-left: 4px; }
code { font-family: ui-monospace, "Cascadia Mono", Consolas, monospace; font-size: 12px; word-break: break-all; }
/* links and buttons as the landing draws them (root.scss): no underline until hovered; the landing button's 16px
   semibold on the one radius, its colour easing over 0.3 s */
a { color: var(--s-navy-text); text-decoration: none; }
a:hover { text-decoration: underline; }
button, .button { font: inherit; font-size: var(--s-fs-base); font-weight: 600; padding: 6px 12px; border-radius: var(--radius); border: 1px solid var(--navy); background: var(--navy); color: var(--s-on-fill); cursor: pointer; text-decoration: none; display: inline-block; transition: background-color 0.3s, color 0.3s; }
/* the landing's focus ring (root.scss): keyboard focus visible everywhere in Old Glory red, clicks show none */
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible, [tabindex]:focus-visible { outline: 3px solid var(--s-navy); outline-offset: 3px; border-radius: 4px; }
button.correct, button.dismiss { background: var(--s-surface); color: var(--s-navy-text); }
button.confirm { background: var(--s-lit-fill); border-color: var(--s-lit-fill); }
button[disabled], button[aria-disabled="true"] { opacity: 0.6; cursor: wait; }
/* visually hidden, still read by a screen reader */
.sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; }
.toast:empty { display: none; }
.actions { display: flex; gap: 6px; flex-wrap: wrap; }
.filters a { margin-right: 10px; text-transform: capitalize; }
.filters a[aria-current="page"] { font-weight: 700; color: var(--s-navy-text); text-decoration: underline; }
.facts { display: grid; grid-template-columns: max-content 1fr; gap: 6px 16px; margin: 0 0 16px; font-size: 14px; }
.facts dt { color: var(--muted); }
.facts dd { margin: 0; }
.facts .s-message { white-space: pre-wrap; overflow-wrap: anywhere; }
form.inline { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
form.inline input { font: inherit; padding: 6px 8px; border: 1px solid var(--line); border-radius: var(--radius); width: 7em; }
.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; }
.tile { background: var(--app-card); border: 0; border-radius: var(--app-card-radius); box-shadow: var(--app-card-shadow); padding: 20px; display: flex; flex-direction: column; gap: 4px; }
.tile-value { font-size: 26px; font-weight: 700; color: var(--s-navy-text); }
.tile-label { font-size: 13px; color: var(--muted); }
.tile-note { font-size: 12px; color: var(--muted); }
.toast { position: fixed; z-index: 9500; left: 50%; bottom: 24px; transform: translateX(-50%); background: var(--navy); color: var(--s-on-fill); padding: 10px 16px; border-radius: var(--radius); box-shadow: 0 6px 24px rgba(0, 0, 0, 0.2); }
.toast.error { background: var(--red); }
/* Phones: the header stays brand and sign-out only; the side nav becomes a fixed bottom tab bar (tally
   160, §9 item 4) so every page below sits in one column (nothing scrolls sideways, nothing is cut); a
   table becomes one card per row, each cell labelled with its column (data-label, from html.js table()). */
@media (max-width: 640px) {
  .signout span { display: none; }
  body.has-side-nav main { margin-left: 0; padding-bottom: 76px; }
  .nav-toggle { display: none; }
  .side-nav { flex-direction: row; top: auto; left: 0; right: 0; bottom: 0; width: auto; height: 64px; padding: 4px; gap: 2px; border-right: 0; border-top: 1px solid var(--app-edge); box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.06); z-index: 5; }
  /* tally 205: six tabs share the bar evenly, icon over label, and never push it wider than the screen (at 383px the
     bar was 441 wide, Demo cut off); a label too long for its share ends in an ellipsis; the bar clears the home
     indicator (safe area), and the page's foot and every toast clear the bar */
  .side-nav { height: auto; min-height: 64px; padding-bottom: calc(4px + env(safe-area-inset-bottom)); }
  .side-nav a { position: relative; flex: 1 1 0; min-width: 0; flex-direction: column; justify-content: center; gap: 2px; padding: 4px 1px; font-size: 10.5px; letter-spacing: -0.01em; line-height: 1.2; text-align: center; white-space: nowrap; }
  .side-nav .nav-label { display: block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
  .side-nav .badge { position: absolute; top: 2px; left: calc(50% + 6px); margin: 0; min-width: 16px; padding: 0 4px; font-size: 10px; line-height: 16px; }
  body.has-side-nav main { padding-bottom: calc(80px + env(safe-area-inset-bottom)); }
  .toast { bottom: calc(76px + env(safe-area-inset-bottom)); max-width: calc(100vw - 32px); box-sizing: border-box; }
  /* the header: the brand and a one-line Sign out, nothing past the edge (the pilot's form.signout at 370 > 354) */
  .signout button { white-space: nowrap; }
  .top { gap: 12px; }
  .top .brand, .top .signout { flex: none; }
  .top .heartbeat { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
  table, thead, tbody, tr, th, td { display: block; }
  table { border: 0; background: transparent; overflow: visible; }
  thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  tbody tr { background: var(--card); border: 1px solid var(--line); border-radius: var(--s-radius); padding: 8px 12px; margin-bottom: 8px; }
  td { display: grid; grid-template-columns: minmax(6.5em, 38%) minmax(0, 1fr); gap: 8px; padding: 6px 0; border: 0; border-bottom: 1px solid var(--line); overflow-wrap: anywhere; }
  td:last-child { border-bottom: 0; }
  td:not([data-label]) { display: block; }
  td[data-label]::before { content: attr(data-label); font-size: var(--s-fs-xs); font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); padding-top: 2px; }
  td.empty { text-align: center; color: var(--muted); }
}

/* More (tally 392, review 1d): nine tabs are too many for a phone's bar at 44px each. The bar holds Home, Demo,
   Invoicing, Documents and More, each named (a fifth of 328px holds "Documents"); More opens the other five as
   full-width rows over the bar (the nav grows upward from the bottom edge: the rows wrap above it by their order),
   and Escape, a tap outside or More again folds them (public/assets/app.js). The sidebar never shows More. */
.side-nav .nav-more { display: none; }
@media (max-width: 640px) {
  .side-nav { flex-wrap: wrap; }
  /* the five behind More: every tab but html.js PHONE_BAR's four */
  .side-nav a:not([href="/app"]):not([href="/app/demo"]):not([href="/app/invoicing"]):not([href="/app/documents"]) { display: none; }
  .side-nav .nav-more { position: relative; display: flex; flex: 1 1 0; min-width: 0; min-height: 44px; flex-direction: column; align-items: center; justify-content: center; gap: 2px; padding: 4px 1px; border: 0; border-radius: var(--radius); background: transparent; color: var(--s-text); font-size: 10.5px; font-weight: 400; letter-spacing: -0.01em; line-height: 1.2; white-space: nowrap; }
  .side-nav .nav-more[data-current], .side-nav .nav-more[aria-expanded="true"] { background: var(--app-card); color: var(--s-navy-text); font-weight: 600; }
  body.nav-more-open .side-nav { box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.12); }
  body.nav-more-open .side-nav a:not([href="/app"]):not([href="/app/demo"]):not([href="/app/invoicing"]):not([href="/app/documents"]) { display: flex; order: -1; flex: 0 0 100%; flex-direction: row; justify-content: flex-start; align-items: center; gap: 12px; min-height: 48px; padding: 10px 16px; font-size: 15px; text-align: left; }
}

/* /start: the Google sign-in form and the consent checkboxes (same look as the report's claim block) */
.google-continue { display: inline-flex; align-items: center; gap: 10px; font-size: 15px; font-weight: 700; padding: 10px 20px; background: var(--card); color: var(--s-navy-text); border: 1px solid var(--navy); }
.google-continue:hover { background: var(--s-tint); }
.google-continue .g-mark { flex: none; }
label.check { display: block; position: relative; padding-left: 26px; font-size: 13px; line-height: 1.5; color: var(--muted); }
label.check input { position: absolute; left: 0; top: 2px; margin: 0; }

/* Forms that were plain browser defaults (/start, /app/setup) */
form.stack { display: grid; gap: 14px; max-width: 520px; }
/* A2's walk, Oct 15: a live note that has nothing to say yet (Partners' code preview) takes no room in a form; it
   shows the moment it says something, and a screen reader still hears it (the region stays in the page) */
form.stack > .field-note:empty { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); border: 0; }
form.stack > * { min-width: 0; }
form.stack label:not(.check) { display: grid; gap: 4px; font-size: 14px; font-weight: 600; color: var(--ink); }
form.stack input:not([type="checkbox"]):not([type="radio"]):not([type="file"]), form.stack select { width: 100%; box-sizing: border-box; font: inherit; font-weight: 400; padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); color: var(--ink); }
/* a file input draws its own button and file name at their natural width: inside the form, never wider (tally 205,
   the Learned page at 328px) */
form.stack input[type="file"] { width: 100%; max-width: 100%; min-width: 0; box-sizing: border-box; }
form.stack label:not(.check) { grid-template-columns: minmax(0, 1fr); }
form.stack input:focus, form.stack select:focus { outline: 2px solid var(--s-navy-2); outline-offset: 1px; }

/* Touch targets: 44px where a thumb is the pointer (tally 24) */
@media (max-width: 640px), (pointer: coarse) {
  button, .button { min-height: 44px; padding: 8px 16px; }
  .button { display: inline-flex; align-items: center; }
  .side-nav a, .top .brand { display: inline-flex; align-items: center; min-height: 44px; }
  textarea { min-height: 44px; }
  td > a { display: inline-flex; align-items: center; min-height: 44px; }
  /* a link inside a sentence stays in the sentence: the hit area grows with padding, the line does not break */
  p > a:only-child { padding-block: 12px; }
  input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]), select { min-height: 44px; }
}

/* The review list's upload (src/portal/pages.js UPLOAD, /assets/upload.js). The input sits inside the
   zone's label, visually hidden but focusable, so the keyboard and a screen reader reach the picker. */
.s-upload, .s-reading { margin: 0 0 20px; }
.s-reading-list { margin: 0; padding-left: 1.2em; line-height: 1.7; overflow-wrap: anywhere; }
.s-upload-title { font-size: 1rem; margin: 0 0 8px; }
.s-drop { position: relative; display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 22px 16px; border: 2px dashed var(--s-line); border-radius: var(--s-radius); background: var(--s-surface); text-align: center; cursor: pointer; }
.s-drop:hover, .s-drop[data-over] { border-color: var(--s-working); background: var(--s-tint); }
.s-drop:focus-within { outline: 3px solid var(--s-working); outline-offset: 2px; }
.s-drop-input { position: absolute; width: 1px; height: 1px; opacity: 0; overflow: hidden; }
.s-drop-text { color: var(--s-ink); }
.s-drop-pick { color: var(--s-working); text-decoration: underline; }
/* principle 4: with nothing waiting on a person, the drop is Documents' one primary action, its picker the filled button;
   with something waiting, "Answer the next one" leads above it (src/portal/pages.js documentsPage) */
.s-upload[data-upload-lead] .s-drop-pick { display: inline-block; margin-left: 4px; padding: 4px 14px; border-radius: 999px; background: var(--navy); color: var(--s-on-fill); text-decoration: none; font-weight: 600; }
.s-doc-lead { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin: 0 0 16px; color: var(--muted); }
.s-doc-lead .button { border-radius: 999px; padding: 8px 18px; }
.s-drop-hint { color: var(--s-muted); font-size: 0.875rem; }
.s-upload-list { list-style: none; margin: 10px 0 0; padding: 0; display: grid; gap: 8px; }
/* The intake address (tally 158): each part stays whole, the line may break only before the @ */
.intake-addr { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px; max-width: 100%; }
.intake-addr code { word-break: normal; overflow-wrap: normal; }
.intake-part { white-space: nowrap; }
/* Settings: Connect Gmail / Connect Outlook, a row of actions, not a bulleted list (tally 156) */
.mailbox-list { list-style: none; margin: 10px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: center; }
.s-upload-file { min-width: 0; padding: 10px 12px; border: 1px solid var(--s-line); border-radius: var(--s-radius); background: var(--s-surface); }
.s-upload-head { display: flex; align-items: center; gap: 8px; min-width: 0; }
.s-upload-icon { flex: none; width: 22px; height: 22px; line-height: 0; }
.s-upload-icon .s-lottie-icon { margin: 0; }
.s-upload-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; color: var(--s-ink); }
.s-upload-meta { flex: none; color: var(--s-muted); font-size: 0.875rem; }
.s-upload-bar { display: block; width: 100%; height: 6px; margin: 8px 0 0; accent-color: var(--s-working); }
.s-upload-bar[hidden] { display: none; }
.s-upload-status { margin: 6px 0 0; font-size: 0.9375rem; color: var(--s-text); overflow-wrap: anywhere; }
.s-upload-file[data-upload-state="held"] .s-upload-status { color: var(--s-hold-text); }
.s-upload-file[data-upload-state="entered"] .s-upload-status { color: var(--s-lit-text); }
.s-upload-file[data-upload-state="refused"] .s-upload-status, .s-upload-file[data-upload-state="failed"] .s-upload-status { color: var(--s-red); }

/* The owner/admin dashboard at /app (A5, Phase D): four cards on the tokens, two columns on a desktop,
   one on a phone; light and dark come from tokens.css. */
/* Settings as tabs (src/portal/pages.js settingsTabs): links, the current one underlined in navy; on a phone the row
   scrolls sideways instead of wrapping into a second line */
.settings-tabs { display: flex; gap: var(--s-space-4); margin: var(--s-space-4) 0 var(--s-space-5); border-bottom: 1px solid #E8E6DC; overflow-x: auto; scrollbar-width: none; }
/* flex: none (the demo walk, tally 381): a phone's 44px tap-target floor made each tab shrinkable to 44px, so the
   words ran over one another; a tab keeps its words' width and the row scrolls */
.settings-tabs a { flex: none; padding: 0.5rem 0.125rem; margin-bottom: -1px; border-bottom: 2px solid transparent; color: var(--s-ink-2, #4b5563); font-weight: 500; text-decoration: none; white-space: nowrap; }
.settings-tabs a:hover { color: var(--s-navy); }
/* A3's 390 walk: the strip ran off the edge ("Reten…") with no sign it scrolls; app.js says which way there is more
   (data-more), and the strip fades on that side */
.settings-tabs[data-more="right"] { -webkit-mask-image: linear-gradient(to right, #000 80%, transparent); mask-image: linear-gradient(to right, #000 80%, transparent); }
.settings-tabs[data-more="left"] { -webkit-mask-image: linear-gradient(to left, #000 80%, transparent); mask-image: linear-gradient(to left, #000 80%, transparent); }
.settings-tabs[data-more="both"] { -webkit-mask-image: linear-gradient(to right, transparent, #000 15%, #000 85%, transparent); mask-image: linear-gradient(to right, transparent, #000 15%, #000 85%, transparent); }
/* Partners (A3's 390 walk): Search takes the row's room, and the link to what STRYKE learned sits under Learn more */
.partners-filter label:first-child { flex: 1 1 14rem; }
.partners-filter label:first-child input { flex: 1; width: auto; min-width: 0; }
.lede + details.more + .partners-learned { margin-top: -18px; }
.settings-tabs a[aria-current="page"] { color: var(--s-navy); border-bottom-color: var(--s-navy); }
.settings-panel[hidden] { display: none; }
/* tally 425: on a wide screen a Settings tab's cards sit two across instead of each stretching the whole width round a
   520px form, as two column stacks (A5's wide referee: a grid's rows left ~110px under a short card beside a tall one);
   a card never breaks across the columns, and a saved note above them keeps the full width */
@media (min-width: 1600px) {
  .settings-panel:not([hidden]) { column-count: 2; column-gap: 16px; }
  .settings-panel > section { break-inside: avoid; display: inline-block; width: 100%; box-sizing: border-box; margin: 0 0 16px; }
  .settings-panel > :not(section) { column-span: all; }
}
.dash-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 26rem), 1fr)); gap: var(--s-space-4); margin-top: var(--s-space-5); }
.dash-card { margin: 0; display: flex; flex-direction: column; gap: var(--s-space-2); }
.dash-card .dash-title { margin: 0; font-size: var(--s-fs-sm); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); }
.dash-card .dash-value { margin: 0; font-size: var(--s-fs-xl); line-height: var(--s-lh-tight); font-weight: 800; color: var(--s-navy-text); font-variant-numeric: tabular-nums; }
.dash-unit { font-size: var(--s-fs-md); font-weight: 600; color: var(--muted); }
.dash-list { margin: 0; padding-left: var(--s-space-5); color: var(--s-text); line-height: var(--s-lh-body); }
.dash-note { margin: 0; color: var(--muted); font-size: var(--s-fs-sm); line-height: var(--s-lh-body); }
.dash-action { margin: var(--s-space-1) 0 0; }
.dash-alert { margin: 0; padding: var(--s-space-2) var(--s-space-3); border-radius: var(--s-radius); background: var(--s-hold-wash); color: var(--s-red-text); font-weight: 600; }
.dash-card .s-lottie-empty { align-self: flex-start; }
/* /app/billing (Phase D): the money tiles, the month's meter against the plan, and the alerts. Tokens only, so dark mode follows. */
.money-unit { font-size: 15px; font-weight: 600; color: var(--muted); }
.money-number { font-family: var(--s-mono); font-size: 13px; overflow-wrap: anywhere; }
.money-note { font-size: 13px; color: var(--muted); margin: 6px 0 0; }
.money-meter { width: 100%; height: 8px; margin-top: 6px; }
.money-alert { border: 1px solid var(--s-hold); background: var(--s-hold-wash); color: var(--s-hold-text); border-radius: var(--s-radius-lg); padding: 12px 14px; margin: 0 0 14px; }
.money-alert strong { color: inherit; }

/* The document history at /app/documents (A5): the filters as a wrapping row, one column on a phone. */
.doc-filters { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr)); gap: var(--s-space-3); align-items: end; margin: var(--s-space-4) 0; }
.doc-filters label { display: grid; gap: var(--s-space-1); font-size: var(--s-fs-sm); font-weight: 600; color: var(--ink); }
.doc-filters input, .doc-filters select { font: inherit; font-weight: 400; padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); color: var(--ink); min-width: 0; }
.doc-filter-actions { display: flex; gap: var(--s-space-3); align-items: center; }
.doc-pager { display: flex; justify-content: space-between; margin: var(--s-space-4) 0; }
.doc-pager a[rel="next"] { margin-left: auto; }
.doc-export { color: var(--muted); font-size: var(--s-fs-sm); }
.muted { color: var(--muted); }
.money-tiles { margin-bottom: 18px; }
.money-links { display: inline-flex; flex-wrap: wrap; gap: 4px 14px; }
/* /app/settings/connections (Phase D part 2): one card per connection; tokens only, so dark mode follows. */
.conn-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); gap: 14px; margin: 12px 0 8px; }
.conn-card { background: var(--app-card); border: 0; border-radius: var(--app-card-radius); box-shadow: var(--app-card-shadow); padding: 20px; display: flex; flex-direction: column; gap: 8px; min-width: 0; }
/* tally 409: Settings › Connections' systems as big logo tiles, the Demo first and largest; a picked tile opens its form
   in place across the row (src/portal/connections-catalog.js) */
.sys-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 140px), 1fr)); grid-auto-flow: row dense; gap: 14px; margin: 12px 0 8px; }
.sys-tile { padding: 0; }
.sys-tile.sys-demo { grid-column: span 2; grid-row: span 2; }
.sys-tile:has(details[open]) { grid-column: 1 / -1; grid-row: auto; }
.sys-tile > details:not([open]) { height: 100%; }
.sys-face { list-style: none; cursor: pointer; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; min-height: 168px; height: 100%; padding: 20px 16px; text-align: center; border-radius: var(--app-card-radius); }
.sys-face::-webkit-details-marker { display: none; }
.sys-face:hover, .sys-face:focus-visible { background: var(--s-navy-wash, rgba(30, 58, 95, 0.05)); }
.sys-logo { width: 64px; height: 64px; object-fit: contain; }
.sys-demo .sys-logo { width: 120px; height: 120px; }
.sys-initials { display: flex; align-items: center; justify-content: center; border-radius: 16px; background: var(--s-navy-wash, rgba(30, 58, 95, 0.08)); color: var(--s-navy-text); font-weight: 700; font-size: 22px; }
.sys-name { font-size: 16px; font-weight: 600; line-height: 1.25; overflow-wrap: anywhere; }
.sys-demo .sys-name { font-size: 28px; }
.sys-tile details[open] .sys-face { height: auto; min-height: 0; flex-direction: row; flex-wrap: wrap; justify-content: flex-start; text-align: left; }
.sys-tile details[open] .sys-logo { width: 40px; height: 40px; }
.sys-tile details[open] .sys-name { font-size: 18px; overflow-wrap: normal; }
@media (max-width: 340px) { .sys-tile.sys-demo { grid-column: auto; } }
.sys-body { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; padding: 0 20px 20px; }
.conn-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.conn-head h3 { margin: 0; font-size: 18px; display: flex; align-items: center; gap: 8px; }
/* tally 187: lucide icons from src/portal/icons.js, inline SVG in currentColor: a system's mark on its card, its state in its pill */
.s-icon { flex: none; }
.conn-icon { color: var(--s-navy-text); }
.s-pill .s-icon { width: 14px; height: 14px; vertical-align: -2px; margin-right: 4px; }
/* a system's brand mark (src/portal/marks.js): in colour once connected or needing attention, grey until then */
.s-mark { flex: none; border-radius: 4px; vertical-align: middle; margin-right: 6px; object-fit: contain; }
.s-mark[data-state="off"] { filter: grayscale(1); opacity: 0.55; }
/* tally 187: a save shows a check that rises in beside its button, holds, and fades, in 560 ms (public/assets/app.js
   savedMark); the no-script "Saved." note's check rises in and stays. Keyframes here, never a style element (CSP).
   Reduced motion: the check is simply there, then gone. */
.s-saved { display: inline-block; vertical-align: middle; margin-left: 8px; color: var(--s-lit-text); animation: s-saved-flash 560ms ease-out both; }
.s-saved-note .s-saved { margin: 0 6px 0 0; animation: s-saved-rise 300ms ease-out both; }
@keyframes s-saved-flash { 0% { opacity: 0; transform: translateY(6px); } 35% { opacity: 1; transform: none; } 70% { opacity: 1; } 100% { opacity: 0; } }
@keyframes s-saved-rise { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .s-saved, .s-saved-note .s-saved { animation: none; } }
/* the Test connection verdict leads with its icon: a lit check on a pass, the amber triangle where it stopped */
.conn-result-head { display: flex; align-items: flex-start; gap: 8px; }
.conn-result-head .s-icon { margin-top: 1px; }
.conn-result[data-result="ok"] .conn-result-head .s-icon { color: var(--s-lit-text); }
.conn-result[data-result="failed"] .conn-result-head .s-icon { color: var(--s-hold-text); }
.s-btn-icon { vertical-align: -3px; margin-right: 6px; }
.conn-line { margin: 0; overflow-wrap: anywhere; }
.conn-note { margin: 4px 0 0; font-size: 13px; color: var(--muted); }
.conn-actions { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; margin-top: 4px; }
.conn-error { margin: 0; padding: 10px 12px; border-radius: var(--radius); background: var(--s-hold-wash); color: var(--s-hold-text); }
.conn-raw summary, .s-node-raw summary { cursor: pointer; font-size: 13px; color: var(--muted); }
.conn-raw code, .s-node-raw code { display: block; margin-top: 6px; font-family: var(--s-mono); font-size: 12px; white-space: pre-wrap; overflow-wrap: anywhere; color: var(--ink); }
.conn-alert { border: 1px solid var(--s-hold); background: var(--s-hold-wash); color: var(--s-hold-text); border-radius: var(--s-radius-lg); padding: 12px 14px; }


/* UI pass (A5): every form control on the tokens, wherever it sits (the partners filter, the team role
   picker, inline forms), so nothing is a bare browser control in either scheme; form.stack and the
   filters keep their own sizing on top of this. */
/* :where() keeps it a base (specificity 0): at (0,5,1) it outranked the filters' own sizing and every Tailwind
   utility in the app's islands (tally 160: the Documents search box's icon padding). */
:where(input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]):not([type="hidden"]), select, textarea) {
  font: inherit; font-size: var(--s-fs-sm); padding: 6px 10px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--card); color: var(--ink);
}
/* inputs take the landing ring above */
input[type="file"] { font: inherit; font-size: var(--s-fs-sm); color: var(--muted); }
input[type="file"]::file-selector-button { font: inherit; font-weight: 600; margin-right: var(--s-space-3); padding: 6px 12px; border-radius: var(--radius); border: 1px solid var(--line); background: var(--s-tint); color: var(--s-navy-text); cursor: pointer; }
form.inline { display: flex; flex-wrap: wrap; gap: var(--s-space-2) var(--s-space-3); align-items: center; }
form.inline label { display: inline-flex; gap: var(--s-space-2); align-items: center; font-size: var(--s-fs-sm); font-weight: 600; }

/* The sandbox test report (src/portal/connections-sandbox.js): each step's own words on their own line under its
   label, so "Connect to the sandbox" and "Connected to THORN-SBX" never run together (A5's UI pass). */
.conn-steps { padding-left: var(--s-space-5); margin: var(--s-space-2) 0; }
.conn-step { margin-bottom: var(--s-space-2); line-height: var(--s-lh-body); }
/* the result first (tally 160, the review doc item 6): green with the company reached, red with the step that stopped it */
.conn-result { margin: var(--s-space-2) 0; padding: 14px 16px; border-radius: 20px; border: 1px solid var(--app-card-edge); }
.conn-result[data-result="ok"] { background: var(--s-lit-wash); border-color: rgba(47, 179, 106, 0.35); }
.conn-result[data-result="failed"] { background: #fef2f2; border-color: #fecaca; }
.conn-result-head { margin: 0 0 4px; font-size: var(--s-fs-base); color: var(--s-ink); }
.conn-result[data-result="ok"] .conn-result-head { color: var(--s-lit-text); }
.conn-result[data-result="failed"] .conn-result-head { color: var(--s-red-text); }
.conn-step-words { display: block; color: var(--muted); font-size: var(--s-fs-sm); overflow-wrap: anywhere; }

/* The JobBOSS² connect form (src/portal/connections-form.js): its summary is a heading with room under it when open. */
.conn-form > summary, .conn-connect > summary, .conn-group > summary { cursor: pointer; font-weight: 600; color: var(--s-navy-text); }
.conn-group > summary h3 { display: inline; }
.conn-form[open] > summary, .conn-connect[open] > summary { margin-bottom: var(--s-space-3); }
/* Inline validation (tally 160; app.js, forms marked data-validate): the example under each field, what is wrong
   with it under that, and the field's own edge when it is wrong. */
.field-hint { display: block; margin-top: 4px; color: var(--muted); font-size: var(--s-fs-xs); font-weight: 400; }
.field-error { display: block; margin-top: 2px; color: var(--s-red-text); font-size: var(--s-fs-sm); font-weight: 500; }
.field-error[hidden] { display: none; }
[aria-invalid="true"] { border-color: var(--s-red) !important; box-shadow: 0 0 0 1px var(--s-red); }

/* Review item 4 (src/portal/html.js learnMore): a card's one line, then the paragraph behind "Learn more". */
.lede + details.more { margin: -16px 0 24px; }
details.more > summary { cursor: pointer; font-size: 13px; color: var(--s-navy-text); width: max-content; }
details.more > p { color: var(--muted); margin: 8px 0 0; max-width: 68ch; }

/* Review item 6 (src/portal/heartbeat.js): "last document received" in the header, quiet gray, a lit dot when fresh. */
.top .heartbeat { margin: 0 0 0 auto; font-size: 13px; color: var(--muted); white-space: nowrap; }
.top .heartbeat[data-heartbeat="fresh"]::before { content: ""; display: inline-block; width: 7px; height: 7px; margin-right: 6px; border-radius: 50%; background: var(--s-lit, #2fb36a); vertical-align: 1px; }
.top .heartbeat + .signout { margin-left: 0; }
/* Tap targets (tally 178, A4's walk at 390px): on a phone every link, button and disclosure in the page is at least
   44 by 44 pixels, server pages and islands alike. :where() keeps it a floor under any layout a page sets (a block
   link, a full-width button); a link inside a sentence still wraps. The nav has its own sizes. */
@media (max-width: 640px) {
  main :where(a[href], button, summary, [role="button"]) { min-height: 44px; min-width: 44px; box-sizing: border-box; }
  main :where(a[href], button, [role="button"]) { display: inline-flex; align-items: center; justify-content: center; vertical-align: middle; }
  main :where(summary) { display: flex; align-items: center; }
}

/* tally 188: a connection's state plays once beside its pill (RESOLVED connected, ATTENTION needs attention) */
.conn-state { display: inline-flex; align-items: center; gap: 0.375rem; }
.conn-lottie { display: inline-block; width: 1.5rem; height: 1.5rem; margin: 0; }

/* the pencil row (src/portal/card-actions.js; Ben's Airtable reference): round white buttons, a shadow, 1.1 on hover,
   0.95 on press; 44px so a thumb finds them; Trash2 red. The islands' CardActions draws the same. */
.s-card-actions { display: flex; gap: 0.5rem; justify-content: flex-end; margin: 0.25rem 0 0.5rem; }
.s-round-btn { display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px; padding: 0; border: 0; border-radius: 9999px; background: #fff; color: #1f2937; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1); cursor: pointer; transition: transform 150ms ease-out, background-color 150ms; }
.s-round-btn:hover { background: #f3f4f6; transform: scale(1.1); }
.s-round-btn:active { transform: scale(0.95); }
.s-round-btn:focus-visible { outline: 2px solid var(--s-working, #1778c8); outline-offset: 2px; }
.s-round-danger { color: #dc2626; }
.s-round-lit { color: var(--s-lit, #2fb36a); }
@media (prefers-reduced-motion: reduce) { .s-round-btn, .s-round-btn:hover, .s-round-btn:active { transition: none; transform: none; } }

/* A document waiting for a person (src/portal/hold-card.js; Ben: "GSAP translucent"): the one white card (tally 228), amber
   only as the dot before its eyebrow, risen in by src/app/rise.ts; shown anyway after 1.5 s if that never runs. */
#main .s-doc.s-hold-card, .s-doc.s-hold-card, .s-hold-card.s-hold-group { background: var(--glass-fill); border: 0; border-radius: var(--app-card-radius); box-shadow: var(--glass-shadow); padding: 20px; }
.s-hold-eyebrow::before { content: ""; display: inline-block; width: 10px; height: 10px; border-radius: 50%; background: var(--s-hold, #e0a100); margin-right: 8px; vertical-align: 0.05em; }
.s-hold-card[data-rise="pending"] { opacity: 0; animation: s-rise-fallback 0s linear 1.5s forwards; }
@keyframes s-rise-fallback { to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .s-hold-card[data-rise="pending"] { opacity: 1; animation: none; } }
.s-hold-list { list-style: none; margin: 0.75rem 0 0; padding: 0; display: grid; gap: 1rem; }
.s-hold-question { display: block; padding: 0.875rem 1rem; border-radius: 1rem; background: var(--tint-hold); }
.s-hold-question::before { display: none; }
.s-hold-field { font-size: 0.75rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--s-hold-text, #7a5600); }
.s-hold-q { margin: 0.25rem 0 0.75rem; font-size: 1.0625rem; font-weight: 600; line-height: 1.4; color: var(--s-text, #0a1a2f); }
.s-hold-answers { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.s-round-pill { display: inline-flex; align-items: center; min-height: 44px; padding: 0 1.125rem; border: 0; border-radius: 9999px; background: #fff; color: var(--s-navy, #0a3161); font: inherit; font-weight: 600; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1); cursor: pointer; transition: transform 150ms ease-out, background-color 150ms; }
.s-round-pill:hover { background: #f3f4f6; transform: scale(1.05); }
.s-round-pill:active { transform: scale(0.95); }
.s-round-pill:focus-visible { outline: 2px solid var(--s-navy, #0a3161); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .s-round-pill, .s-round-pill:hover, .s-round-pill:active { transition: none; transform: none; } }
/* over the base button rules (button.confirm, button.correct, button.dismiss above): on the hold card they are round and white */
.s-hold-answers button.s-round-pill, .s-hold-answers button.s-round-btn { background: #fff; border: 0; color: var(--s-navy, #0a3161); }
.s-hold-answers button.s-round-btn { padding: 0; }
.s-hold-answers button.s-round-danger { color: #dc2626; }
.s-hold-answers button.s-round-pill:hover, .s-hold-answers button.s-round-btn:hover { background: #f3f4f6; }
/* Correct, inline (tally 208): the field holding what STRYKE read, Save and Cancel beside it */
.s-hold-correct { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0.5rem; margin-top: 0.75rem; }
.s-hold-correct[hidden] { display: none; }
.s-hold-correct label { display: flex; flex-direction: column; gap: 0.25rem; flex: 1 1 12rem; font-size: 0.875rem; font-weight: 600; }
.s-hold-correct input { min-height: 44px; }
.s-hold-correct button.s-round-pill { background: #fff; border: 0; color: var(--s-navy, #0a3161); }
/* Ask first, then Undo (tally 208, public/assets/app.js data-confirm / data-undo) */
[data-undo-hidden] { display: none !important; }
.s-confirm { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; margin: 0.5rem 0; padding: 0.75rem 1rem; border-radius: 1rem; background: var(--glass-fill); border: 1px solid var(--tint-hold-edge); box-shadow: var(--glass-shadow); -webkit-backdrop-filter: blur(var(--glass-blur)); backdrop-filter: blur(var(--glass-blur)); }
.s-confirm-text { flex: 1 1 14rem; font-weight: 600; }
.s-confirm-reason { display: flex; flex-direction: column; gap: 0.25rem; flex: 1 1 100%; font-size: 0.875rem; }
.s-confirm-reason input { min-height: 44px; }
.s-confirm button.s-round-pill { background: #fff; border: 0; color: var(--s-navy, #0a3161); }
.s-confirm button.s-confirm-yes { color: #b91c1c; }
.s-undo { position: fixed; left: 50%; bottom: calc(1rem + env(safe-area-inset-bottom)); transform: translateX(-50%); z-index: 9000; display: flex; align-items: center; gap: 0.75rem; padding: 0.5rem 0.5rem 0.5rem 1rem; border-radius: 9999px; background: var(--s-navy, #0a3161); color: #fff; box-shadow: var(--glass-shadow); }
.s-undo button { min-height: 44px; padding: 0 1rem; border: 0; border-radius: 9999px; background: #fff; color: var(--s-navy, #0a3161); font-weight: 700; }
.s-quiet-danger { background: transparent; border: 1px solid transparent; color: #b91c1c; }
.s-quiet-danger:hover { background: rgba(185, 28, 28, 0.08); }
/* one cause, one card (tally 208): its documents behind a disclosure, Dismiss all beside the cause */
.s-hold-group { margin: 0 0 1rem; }
.s-hold-group .s-hold-q { font-size: 1.125rem; }
.s-hold-group-docs { margin-top: 0.75rem; }
.s-hold-group-docs > summary { cursor: pointer; min-height: 44px; display: flex; align-items: center; font-weight: 600; color: var(--s-navy, #0a3161); }
.s-hold-group-docs .s-hold-card { margin-top: 0.75rem; }
.s-hold-answers button.s-hold-bulk { color: #b91c1c; }
/* pending (tally 218, 5.1): a button or link whose request is in flight carries a small spinner after its words */
[aria-busy="true"]::after { content: ""; display: inline-block; width: 0.8em; height: 0.8em; margin-left: 0.5em; vertical-align: -0.1em; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: s-spin 0.8s linear infinite; }
@keyframes s-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { [aria-busy="true"]::after { animation: none; } }
button[aria-busy="true"] { cursor: progress; }
/* a hold answered (tally 218, 5.3; 228): the amber dot before the card's eyebrow fades to green, then it collapses (public/assets/app.js resolveOut); the badge bounces as it counts down */
.s-hold-card[data-resolved] .s-hold-eyebrow::before { background: var(--s-lit, #2fb36a); transition: background-color 250ms ease; }
.s-hold-question[data-resolved] { background: var(--tint-lit); transition: background-color 250ms ease; }
.badge.s-bump { animation: s-bump 300ms ease-out; }
@keyframes s-bump { 0% { transform: scale(1); } 40% { transform: scale(1.3); } 100% { transform: scale(1); } }
@media (prefers-reduced-motion: reduce) { .s-hold-card[data-resolved] .s-hold-eyebrow::before, .s-hold-question[data-resolved] { transition: none; } .badge.s-bump { animation: none; } }
/* a hold's what-to-do link after its headline (A5's 158466a) */
.s-hold-link { display: inline-block; margin: -0.25rem 0 0.75rem; font-weight: 600; }
/* the empty states' family of drawings on a server page (tally 218, 5.7; src/portal/empty-art.js) */
.s-empty-art { display: flex; flex-direction: column; align-items: center; gap: 0.25rem; padding: 1.5rem 1rem; border: 1px dashed var(--s-line, #d9dee6); border-radius: 1rem; color: var(--muted, #5b6472); text-align: center; }
.s-empty-art p { margin: 0; }
/* Connections (tally 209): Connect is the page's primary act, in navy, never the red of a destructive one; each credential form waits behind its own Connect */
.conn-card .s-btn, .conn-form > summary { background: var(--s-navy); border-color: var(--s-navy); color: var(--s-on-fill, #fff); }
.conn-form > summary { display: inline-flex; align-items: center; min-height: 44px; padding: 0 1.25rem; border: 1px solid var(--s-navy); border-radius: var(--s-radius); font-weight: 700; cursor: pointer; list-style: none; }
.conn-form > summary::-webkit-details-marker { display: none; }
.conn-form[open] > summary { margin-bottom: 0.75rem; }
.conn-sandboxes > summary { cursor: pointer; min-height: 44px; display: flex; align-items: center; font-weight: 700; color: var(--s-navy-text); }
/* Saved in words beside the button for a few seconds (tally 209) */
.s-saved-text { display: inline-block; margin-left: 0.35rem; font-weight: 600; color: var(--s-lit-text, #1d7a46); vertical-align: middle; }

/* tally 355 (A5's walk): Partners' "Named in your documents" as rows like the rest of the page, never plain bullets: */
/* who on the left, the buttons at the row's end; at a phone's width the buttons wrap under the name */
.s-named { list-style: none; margin: 12px 0 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.s-named > li { background: var(--app-card); border-radius: var(--app-card-radius); box-shadow: var(--app-card-shadow); padding: 14px 16px; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 16px; min-width: 0; }
.s-named-who { flex: 1 1 16rem; min-width: 0; overflow-wrap: anywhere; }
.s-named-who > p { margin: 4px 0 0; }
.s-named-act { display: flex; flex-wrap: wrap; gap: 8px; flex: 0 0 auto; }
.s-named-act form.inline { margin: 0; }

/* Ben, Oct 15: the Training tab's two forms side by side from 1024px */
.pair-forms { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0 24px; align-items: start; }
@media (min-width: 1024px) { .pair-forms { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }
.pair-forms > section { min-width: 0; }
/* Ben, Oct 15: CourseSwap's schedule-build loader (DashboardPreloader.css, "writing on paper") over a frosted scrim while
   a Training form is sent; larger on a wide screen (--size 1.5px, 2.5px from 768px), in the brand blue */
.s-train-loader { position: fixed; inset: 0; z-index: 200; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 24px; background: rgba(255, 255, 255, 0.55); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); animation: sTrainIn 0.25s ease-out; }
.s-train-loader.is-leaving { opacity: 0; transition: opacity 0.3s ease-in-out; }
@keyframes sTrainIn { from { opacity: 0; } to { opacity: 1; } }
.s-train-line { margin: 0; min-height: 2.5rem; font-size: 1.125rem; font-weight: 600; letter-spacing: 0.01em; color: #3a3a3a; text-align: center; padding: 0 24px; }
.s-train-word { color: #1778c8; }
.s-write-loader { --c1: #ffffff; --c2: #0000; --c3: #1778c8; --c4: rgba(0, 0, 0, 0.25); --c5: #7db3e6; --size: 1.5px; position: relative; display: block; width: calc(100 * var(--size)); height: calc(130 * var(--size)); background: var(--c1); border-radius: calc(4 * var(--size)); box-shadow: 0 calc(6 * var(--size)) calc(20 * var(--size)) rgba(23, 120, 200, 0.18); }
@media (min-width: 768px) { .s-write-loader { --size: 2.5px; } .s-train-line { font-size: 1.5rem; } }
.s-write-loader::before { content: ""; position: absolute; width: calc(54 * var(--size)); height: calc(25 * var(--size)); left: 50%; top: 0; background-image: radial-gradient(ellipse at center, var(--c2) 24%, var(--c3) 25%, var(--c3) 64%, var(--c2) 65%), linear-gradient(to bottom, var(--c2) 34%, var(--c3) 35%); background-size: calc(12 * var(--size)) calc(12 * var(--size)), 100% auto; background-repeat: no-repeat; background-position: center top; transform: translate(-50%, -65%); box-shadow: 0 calc(-3 * var(--size)) var(--c4) inset; }
.s-write-loader::after { content: ""; position: absolute; left: 50%; top: 20%; transform: translateX(-50%); width: 66%; height: 60%; background: linear-gradient(to bottom, var(--c5) 30%, var(--c2) 31%); background-size: 100% calc(16 * var(--size)); animation: sWriteDown 2s ease-out infinite; }
@keyframes sWriteDown { 0% { height: 0%; opacity: 0; } 20% { height: 0%; opacity: 1; } 80% { height: 65%; opacity: 1; } 100% { height: 65%; opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .s-write-loader::after { animation: none; height: 65%; } .s-train-loader { animation: none; } }
