/* ============================================================================
 * studio-v2.css — Studio v2 theme layer (dark mode + small UI affordances).
 * ----------------------------------------------------------------------------
 * Loaded in index.html AFTER styles.css, so every rule here wins on specificity
 * ties and the [data-theme="dark"] custom-property overrides cascade into the
 * existing component styles. styles.css remains the source of truth for the
 * class vocabulary and is NEVER edited for theme work.
 *
 * Contract (shared with app.js):
 *   - app.js sets  document.documentElement.dataset.theme = "dark" | "light".
 *   - Default comes from localStorage "studioTheme", else the OS preference;
 *     index.html's inline <head> bootstrap applies it before first paint.
 *   - The topbar #btn-theme-toggle flips it and persists the choice.
 *
 * Light is the current default look — defined entirely in styles.css :root. We
 * only RE-DECLARE the custom properties for the dark theme here, plus patch the
 * few spots in styles.css that hard-code light colours (#fff / #f0f3f6) instead
 * of going through a var.
 * ========================================================================== */

/* The theme toggle button reads as an icon button, sized to match .btn--sm-ish
 * height while staying square. Purely additive class; styles.css has no rule for
 * it, so this is the sole definition. */
#btn-theme-toggle.btn--icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  min-width: 38px;
  padding: 0;
  font-size: 16px;
  line-height: 1;
}
#btn-theme-toggle .theme-ico {
  display: inline-block;
  transition: transform var(--t-fast, .15s ease);
}
#btn-theme-toggle:hover .theme-ico {
  transform: rotate(-12deg) scale(1.08);
}

/* ----------------------------------------------------------------------------
 * DARK THEME — re-declare the styles.css :root tokens with dark values.
 * Because every component in styles.css consumes these vars, overriding them
 * here re-skins the whole app with no per-component rules needed.
 * -------------------------------------------------------------------------- */
/* FILLED brand surfaces, as opposed to brand TEXT.
 *
 * --blue is lifted to #3b8ee0 in dark mode so a LINK reads on a dark background.
 * That same colour as a BUTTON FILL puts white text at 3.42:1 — measured across
 * primary buttons, the calendar toolbar and the skip link (2026-09-19). A filled
 * surface needs a deeper blue; a link needs a lighter one. Two jobs, two tokens.
 *
 *   light  #0a66c2 + white = 5.69 : 1
 *   dark   #2a6cb0 + white = 5.43 : 1
 *
 * This is a BASE token, so it does not belong in the alias block below — those
 * are var() indirections and css-tokens.test.js enforces that.
 */
:root { --blue-fill: #0a66c2; }

/* --badge-amber — the 'drafted' platform badge (a TikTok inbox draft: at the
 * platform, not yet visible to anyone). --amber itself is #8a6d00, which is
 * 4.92:1 on a card but only 4.35:1 on the light app background — and a badge
 * lands on both. Same shape of problem as --blue above, same fix.
 *
 *   light  #7a6000 on --card 6.00:1, on --bg 5.30:1
 *   dark   #e8c35a on --card 9.84:1, on --bg 10.93:1
 *
 * BASE token, so it stays out of the alias block below.
 */
:root { --badge-amber: #7a6000; }

/* ----------------------------------------------------------------------------
 * TOKEN ALIASES — names the app uses that were never declared.
 *
 * A var() naming a token that does not exist silently falls back to its literal,
 * and those literals were all written for LIGHT mode. In dark mode that is how
 * the calendar hover cards ended up painting rgb(15,23,42) ink on a rgb(19,31,43)
 * card — black on black, contrast 1.05:1 (owner, 2026-09-18). styles.css also
 * has 15 uses of var(--text) with NO fallback at all, which makes the whole
 * declaration invalid and silently inherits whatever the parent had.
 *
 * These alias to the CANONICAL tokens rather than to literals, so one block
 * serves both themes and neither can drift from the other.
 * -------------------------------------------------------------------------- */
:root {
  --text: var(--ink);
  --muted: var(--ink-soft);
  --color-text-secondary: var(--ink-soft);
  --danger: var(--red);
  --warn: var(--amber);
  --ok: var(--green);
  --brand: var(--blue);
  --surface: var(--card);
  --surface-2: var(--surface-soft);
  --surface-3: var(--line);
  --border: var(--line);
}

:root[data-theme="dark"] {
  /* Hint the UA so form controls, scrollbars, etc. render dark. */
  color-scheme: dark;

  /* Deeper than --blue on purpose — see the note on --blue-fill above. */
  --blue-fill: #2a6cb0;

  /* Brand — keep the LinkedIn blue identity, deepen the navy chrome. */
  --navy: #0e2a40;
  --navy-deep: #081826;
  --navy-line: #21496b;
  --blue: #3b8ee0;        /* lifted so it reads on dark surfaces */
  --blue-dark: #2f78c4;
  --blue-tint: #14304a;   /* dark wash instead of the light tint */

  /* Surfaces */
  --bg: #0c141d;          /* app background */
  --card: #131f2b;        /* card / panel surface */
  --surface-soft: #0f1925;/* nested panels, table rows */

  /* Ink (text) */
  --ink: #e8eef5;         /* primary text */
  --ink-soft: #aebccb;    /* secondary text */
  --ink-faint: #7c8c9d;   /* tertiary / hints */
  --ink-invert: #eaf2fb;  /* text on (already dark) navy surfaces */
  --ink-invert-soft: #9fb6cb;

  /* Lines */
  --line: #23323f;
  --line-soft: #1b2733;

  /* Status palette — keep hues, swap to dark-friendly tints/lines + brighter ink. */
  --green: #57d08a;  --green-tint: #103024;  --green-line: #1f5a3e;
  --amber: #e8c35a;  --amber-tint: #322a10;  --amber-line: #5c4d1c;
  /* see .plat-badge.is-drafted in ux-app.css — its own token so the badge
     can clear 4.5:1 on BOTH the card and the app background. */
  --badge-amber: #e8c35a;
  --red:   #f08a80;  --red-tint:   #361613;  --red-line:   #6b2a24;
  --slate: #9fb0bf;  --slate-tint: #1a242e;  --slate-line: #2c3a47;

  /* Elevation — heavier shadows read better on dark. */
  --shadow: 0 1px 2px rgba(0,0,0,.45), 0 6px 20px rgba(0,0,0,.45);
  --shadow-lg: 0 18px 60px rgba(0,0,0,.6);
}

/* ----------------------------------------------------------------------------
 * Patch the hard-coded light colours in styles.css that bypass the vars.
 * Scoped to the dark theme so light mode is untouched.
 * -------------------------------------------------------------------------- */

/* Inputs: focus/disabled backgrounds were literal #fff / #f0f3f6. */
:root[data-theme="dark"] input,
:root[data-theme="dark"] select,
:root[data-theme="dark"] textarea {
  background: var(--surface-soft);
  color: var(--ink);
  border-color: var(--line);
}
:root[data-theme="dark"] input:focus,
:root[data-theme="dark"] select:focus,
:root[data-theme="dark"] textarea:focus {
  background: var(--card);
}
:root[data-theme="dark"] input:disabled,
:root[data-theme="dark"] select:disabled,
:root[data-theme="dark"] textarea:disabled {
  background: #0a121a;
  color: var(--ink-faint);
}
/* The <select> caret SVG is a fixed light-grey arrow — re-tint for contrast. */
:root[data-theme="dark"] select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%23aebccb' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
}

/* Banner ink: .banner.info forces color:var(--navy) which is now near-black on a
 * dark wash — restore readable text. Others already use bright status inks. */
:root[data-theme="dark"] .banner.info {
  color: var(--ink);
  border-color: var(--navy-line);
}

/* Calendar event chips / legend that hard-code #fff text on var(--navy) stay
 * fine (navy is dark), but the calendar body inherits FullCalendar light chrome.
 * Nudge its surfaces to the dark tokens without fighting FullCalendar internals. */
:root[data-theme="dark"] .calendar-wrap .fc {
  --fc-page-bg-color: var(--card);
  --fc-neutral-bg-color: var(--surface-soft);
  --fc-list-event-hover-bg-color: var(--surface-soft);
  --fc-today-bg-color: rgba(59,142,224,.10);
  --fc-border-color: var(--line);
  --fc-neutral-text-color: var(--ink-soft);
  color: var(--ink);
}

/* Modal/dialog surfaces and toast already use var(--card)/var(--ink) via
 * styles.css, so they follow automatically. Nothing extra needed here. */

/* ----------------------------------------------------------------------------
 * #pref-tz-picker — searchable multi-select container (app.js fills it).
 * These are layout primitives app.js can rely on; app.js may add its own inner
 * classes too. Kept theme-agnostic (uses the vars, so it follows dark/light).
 * -------------------------------------------------------------------------- */
.pref-tz-picker {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 2px;
}
.pref-tz-picker .tz-pick-search {
  width: 100%;
}
.pref-tz-picker .tz-pick-selected {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.pref-tz-picker .tz-pick-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 6px 4px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill, 999px);
  background: var(--surface-soft);
  color: var(--ink);
  font-size: 12.5px;
  font-weight: 600;
}
.pref-tz-picker .tz-pick-chip button {
  border: 0;
  background: transparent;
  color: var(--ink-faint);
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  padding: 0 2px;
}
.pref-tz-picker .tz-pick-chip button:hover {
  color: var(--red, #b42318);
}
.pref-tz-picker .tz-pick-options {
  display: flex;
  flex-direction: column;
  max-height: 220px;
  overflow-y: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm, 10px);
  background: var(--card);
}
.pref-tz-picker .tz-pick-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 7px 11px;
  cursor: pointer;
  font-size: 13px;
  color: var(--ink);
  border-bottom: 1px solid var(--line-soft);
}
.pref-tz-picker .tz-pick-option:last-child {
  border-bottom: 0;
}
.pref-tz-picker .tz-pick-option:hover {
  background: var(--surface-soft);
}
.pref-tz-picker .tz-pick-option[aria-disabled="true"] {
  opacity: .5;
  cursor: not-allowed;
}
.pref-tz-picker .tz-pick-option.is-selected {
  color: var(--blue);
  font-weight: 700;
}
.pref-tz-picker .tz-pick-hint {
  font-size: 12px;
  color: var(--ink-faint);
}

/* ============================================================================
 * NETWORK CRM — slide-over contact drawer (#11) + per-card refresh (#10).
 * Owned by web/network.js. All classes are additive (styles.css has no rules
 * for them) and consume the shared design tokens so dark/light follows for free.
 * ========================================================================== */

/* The drawer root spans the whole viewport and hosts the backdrop + panel. It is
 * inert until network.js adds .np-drawer-open (children carry [hidden] too, so
 * the panel can't be tabbed into while closed). */
.np-drawer-root {
  position: fixed;
  inset: 0;
  z-index: 1200;             /* above the app chrome; below toasts (app uses ~2000) */
  pointer-events: none;      /* closed state lets clicks through to the view */
}
.np-drawer-root.np-drawer-open {
  pointer-events: auto;
}

/* Backdrop — fades in under the panel; clicking it closes (wired in network.js). */
.np-drawer-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(8, 22, 38, .42);
  opacity: 0;
  transition: opacity var(--t-med, .25s ease);
}
.np-drawer-root.np-drawer-open .np-drawer-backdrop {
  opacity: 1;
}
:root[data-theme="dark"] .np-drawer-backdrop {
  background: rgba(0, 0, 0, .58);
}

/* The panel: fixed to the right edge, slides in from off-screen. Width caps at
 * a comfortable reading column but shrinks on narrow viewports. */
.np-drawer {
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  width: min(880px, 94vw);
  display: flex;
  flex-direction: column;
  background: var(--bg);
  border-left: 1px solid var(--line);
  box-shadow: var(--shadow-lg, 0 18px 60px rgba(8, 22, 38, .28));
  transform: translateX(100%);
  transition: transform var(--t-med, .25s ease);
  will-change: transform;
}
.np-drawer-root.np-drawer-open .np-drawer {
  transform: translateX(0);
}

/* Respect reduced-motion: cut the slide/fade, just show/hide. */
@media (prefers-reduced-motion: reduce) {
  .np-drawer,
  .np-drawer-backdrop {
    transition: none;
  }
}

/* Sticky drawer header with the close affordance. */
.np-drawer-head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--line);
  background: var(--card);
  position: sticky;
  top: 0;
  z-index: 1;
}
.np-drawer-heading {
  font-size: 13px;
  font-weight: 800;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.np-drawer-head .spacer {
  flex: 1 1 auto;
}
.np-drawer-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm, 10px);
  background: var(--surface-soft);
  color: var(--ink-soft);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  transition: background var(--t-fast, .15s ease), color var(--t-fast, .15s ease), border-color var(--t-fast, .15s ease);
}
.np-drawer-close:hover {
  background: var(--card);
  color: var(--ink);
  border-color: var(--ink-faint);
}
.np-drawer-close:focus-visible {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 3px rgba(10, 102, 194, .25);
}

/* Previous / Next through the list it was opened from, and the profile over the whole window (2026-10-02). */
.np-drawer-head [hidden] { display: none !important; }
.np-drawer-pos { font-size: 12px; color: var(--ink-soft); white-space: nowrap; }
.np-drawer-step[disabled] { opacity: .45; cursor: default; }
.np-drawer.is-full { width: 100vw; border-left: 0; }
.np-drawer.is-full .np-drawer-body .np-workspace { max-width: 1440px; margin-left: auto; margin-right: auto; }
@media (max-width: 700px) {
  .np-drawer-head { gap: 8px; padding: 10px 12px; flex-wrap: wrap; }
  .np-drawer-heading, .np-drawer-pos, .np-drawer-full { display: none; }
}

/* Scrollable drawer body that holds the existing .np-workspace. The detail card
 * inside loses its own outer chrome since the drawer already supplies it. */
.np-drawer-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 18px;
}
.np-drawer-body .np-workspace {
  margin-top: 0;
}
/* Inside the drawer the detail panel is the full surface — drop its card border/
 * shadow so it doesn't read as a card-in-a-card. */
.np-drawer-body .np-detail {
  border: 0;
  box-shadow: none;
  padding: 0;
  background: transparent;
}
/* Keep the guide rail sticky relative to the scrolling drawer body. */
.np-drawer-body .np-guide {
  top: 0;
}

/* Embedded: the DraftADay extension's side panel (#/network?contact=<id>&embed=1,
 * docs/PLAN_NETWORK_COMPARE.md slice 2) shows only the contact's card, edge to
 * edge. It never closes, and nothing of the app sits beside or behind it. */
html.np-embed body { overflow: hidden; }
html.np-embed .appbar,
html.np-embed .appnav,
html.np-embed .version-banner { display: none !important; }
html.np-embed .np-drawer {
  width: 100vw;
  border-left: 0;
  box-shadow: none;
  transition: none;
}
html.np-embed .np-drawer-backdrop,
html.np-embed .np-drawer-close,
html.np-embed .np-detail-close { display: none; }
html.np-embed .np-drawer-body { padding: 12px; }

/* On narrow viewports the workspace already collapses to one column (styles.css
 * @media 900px); the drawer just goes near-full-width via its width clamp. */

/* ----------------------------------------------------------------------------
 * #10 — per-card "Refresh profile" button + the "top N" overflow note.
 * -------------------------------------------------------------------------- */
.np-card-meta .spacer {
  flex: 1 1 auto;
}
/* Lay the meta row out so the age sits left and the refresh button sits right. */
.np-card-meta {
  display: flex;
  align-items: center;
  gap: 8px;
}
.np-refresh-btn {
  flex: 0 0 auto;
  white-space: nowrap;
}
.np-refresh-btn.is-queued {
  color: var(--green);
  border-color: var(--green-line);
}
.np-refresh-btn:disabled {
  cursor: default;
  opacity: .8;
}
.np-results-more {
  margin: 4px 2px 0;
  font-size: 12px;
  color: var(--ink-faint);
}

/* ============================================================================
 * ADDITIVE v2 COMPONENTS (appended) — contracted class names from the brief.
 * Everything above is preserved untouched. The blocks below add the remaining
 * assigned affordances and provide the brief's exact class names alongside the
 * existing ones (so the HTML/JS fleet can use either vocabulary safely).
 * All rules consume the same design tokens, so they follow dark/light.
 * ========================================================================== */

/* ----------------------------------------------------------------------------
 * #4 (alias) — .theme-toggle topbar button. Mirrors #btn-theme-toggle above so
 * markup using the generic class also gets a styled icon button.
 * -------------------------------------------------------------------------- */
.theme-toggle {
  flex: 0 0 auto;
  width: 38px; height: 38px;
  display: inline-grid; place-items: center;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: var(--card);
  color: var(--ink-soft);
  font-size: 16px; line-height: 1;
  cursor: pointer; font-family: inherit;
  transition: background var(--t-fast), border-color var(--t-fast),
              color var(--t-fast), transform var(--t-fast);
}
.theme-toggle:hover { border-color: var(--blue); color: var(--blue); background: var(--blue-tint); }
.theme-toggle:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.theme-toggle:active { transform: scale(.94); }
.theme-toggle .theme-toggle-ico,
.theme-toggle .theme-ico { width: 18px; height: 18px; pointer-events: none; }

/* ----------------------------------------------------------------------------
 * #2 — World-clock date line. Small muted date under each tz-chip time.
 * -------------------------------------------------------------------------- */
.tz-chip-date {
  font-size: 10.5px; font-weight: 600;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums; letter-spacing: .01em;
  white-space: nowrap;
}
.tz-chip--local .tz-chip-date { color: var(--blue); opacity: .85; }
/* When a chip carries a date, stack label+time over the date neatly. */
.tz-chip.has-date { align-items: center; padding-top: 4px; padding-bottom: 4px; }
.tz-chip.has-date .tz-chip-main {
  display: inline-flex; flex-direction: column; gap: 1px; line-height: 1.2;
}

/* ----------------------------------------------------------------------------
 * #3 (alias) — .tz-picker searchable multi-select with selected chips, a
 * checkbox results list, and an up-to-N limit affordance. Mirrors the
 * .pref-tz-picker primitives above using the brief's class names.
 * -------------------------------------------------------------------------- */
.tz-picker { display: flex; flex-direction: column; gap: 12px; max-width: 560px; }

/* Selected chips row. */
.tz-picker-selected { display: flex; flex-wrap: wrap; gap: 8px; min-height: 34px; }
.tz-picker-selected:empty::before {
  content: "No zones selected — your local time always shows.";
  font-size: 12.5px; color: var(--ink-faint); font-style: italic; align-self: center;
}
.tz-sel-chip {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 8px 5px 12px; border-radius: var(--radius-pill);
  background: var(--blue-tint); border: 1px solid var(--blue); color: var(--blue);
  font-size: 12.5px; font-weight: 600;
}
.tz-sel-chip .tz-sel-name { line-height: 1.2; }
.tz-sel-chip .tz-sel-off {
  font-size: 11px; font-weight: 700; color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}
:root[data-theme="dark"] .tz-sel-chip .tz-sel-off { color: var(--ink-soft); }
.tz-sel-chip .tz-sel-x {
  width: 18px; height: 18px; border: none; cursor: pointer;
  display: grid; place-items: center; border-radius: 50%;
  background: transparent; color: var(--blue);
  font-size: 14px; line-height: 1; font-family: inherit;
  transition: background var(--t-fast), color var(--t-fast);
}
.tz-sel-chip .tz-sel-x:hover { background: var(--red-tint); color: var(--red); }

/* Search box (reuses input styling from styles.css; adds an icon affordance). */
.tz-picker-search { position: relative; }
.tz-picker-search input[type="search"],
.tz-picker-search input[type="text"] { padding-left: 34px; }
.tz-picker-search .tz-search-ico {
  position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
  width: 15px; height: 15px; pointer-events: none; opacity: .6; color: var(--ink-faint);
}

/* Limit affordance — "X / N selected". app.js adds .is-full at the cap. */
.tz-picker-limit {
  display: flex; align-items: center; gap: 8px;
  font-size: 11.5px; font-weight: 700; color: var(--ink-faint);
  text-transform: uppercase; letter-spacing: .03em;
}
.tz-picker-limit .tz-limit-count { color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.tz-picker-limit.is-full,
.tz-picker-limit.is-full .tz-limit-count { color: var(--amber); }

/* Results list — scrollable checkbox rows. */
.tz-picker-list {
  display: flex; flex-direction: column;
  max-height: 280px; overflow-y: auto;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface-soft);
}
.tz-picker-opt {
  display: flex; align-items: center; gap: 10px; padding: 9px 12px;
  font-size: 13px; color: var(--ink); cursor: pointer; user-select: none;
  border-bottom: 1px solid var(--line-soft); transition: background var(--t-fast);
}
.tz-picker-opt:last-child { border-bottom: none; }
.tz-picker-opt:hover { background: var(--blue-tint); }
.tz-picker-opt input { accent-color: var(--blue); margin: 0; cursor: pointer; flex: 0 0 auto; }
.tz-picker-opt .tz-opt-name { font-weight: 600; }
.tz-picker-opt .tz-opt-region { color: var(--ink-faint); font-size: 12px; }
.tz-picker-opt .tz-opt-off {
  margin-left: auto; font-size: 11.5px; font-weight: 700;
  color: var(--ink-soft); font-variant-numeric: tabular-nums;
}
.tz-picker-opt.is-disabled, .tz-picker-opt input:disabled { cursor: not-allowed; }
.tz-picker-opt.is-disabled { opacity: .5; }
.tz-picker-opt.is-selected { background: var(--blue-tint); }
.tz-picker-list .tz-picker-empty {
  padding: 18px 12px; text-align: center; font-size: 12.5px; color: var(--ink-faint);
}

/* ----------------------------------------------------------------------------
 * #5 — Inline scheduled editor. Edit a scheduled post's time in place.
 * -------------------------------------------------------------------------- */
.sched-edit {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  padding: 10px 12px; background: var(--surface-soft);
  border: 1px solid var(--line); border-radius: var(--radius-sm); margin: 6px 0;
}
.sched-edit .sched-edit-label {
  font-size: 11px; font-weight: 700; color: var(--ink-soft);
  text-transform: uppercase; letter-spacing: .04em;
}
.sched-edit input[type="datetime-local"] {
  width: auto; min-width: 220px; padding: 8px 11px; font-size: 13px;
}
.sched-edit .sched-edit-tz { font-size: 11.5px; color: var(--ink-faint); font-weight: 600; }
.sched-edit .sched-edit-actions { display: inline-flex; align-items: center; gap: 8px; margin-left: auto; }
.sched-edit .sched-save,
.sched-edit .sched-cancel {
  cursor: pointer; font-family: inherit; font-weight: 700;
  border-radius: var(--radius-pill); border: 1px solid transparent;
  padding: 7px 14px; font-size: 12.5px; line-height: 1;
  transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast);
}
.sched-edit .sched-save { background: var(--blue-fill); color: #fff; border-color: var(--blue-fill); }
.sched-edit .sched-save:hover { background: var(--blue-dark); border-color: var(--blue-dark); }
.sched-edit .sched-save:disabled { opacity: .6; cursor: default; }
.sched-edit .sched-cancel {
  background: transparent; color: var(--ink-soft); border-color: var(--line); font-weight: 600;
}
.sched-edit .sched-cancel:hover { border-color: var(--blue); color: var(--blue); }
.sched-edit .sched-edit-err { flex: 1 1 100%; font-size: 12px; color: var(--red); }


/* ----------------------------------------------------------------------------
 * #8/#9/#10 — "Refresh from LinkedIn" button + seen-count badge.
 * HONEST: this button only flags the row for the extension to re-harvest later
 * (server sets refresh_requested_at); it is NOT a live pull. The pending state
 * reflects that a request is queued.
 * -------------------------------------------------------------------------- */
.btn-refresh-li {
  display: inline-flex; align-items: center; gap: 6px;
  cursor: pointer; font-family: inherit;
  padding: 6px 12px; font-size: 12px; font-weight: 600;
  border-radius: var(--radius-pill); border: 1px solid var(--line);
  background: var(--card); color: var(--ink-soft); white-space: nowrap;
  transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast);
}
.btn-refresh-li:hover { border-color: var(--blue); color: var(--blue); background: var(--blue-tint); }
.btn-refresh-li:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.btn-refresh-li .btn-refresh-ico { width: 14px; height: 14px; flex: 0 0 auto; }
.btn-refresh-li:disabled { opacity: .6; cursor: default; }
/* Pending state once refresh_requested_at is set (app.js adds .is-pending). */
.btn-refresh-li.is-pending {
  background: var(--amber-tint); color: var(--amber); border-color: var(--amber-line); cursor: default;
}
.btn-refresh-li.is-pending .btn-refresh-ico { animation: studio-spin .9s linear infinite; }
/* Bulk variant (acts on selected rows). */
.btn-refresh-li.btn-refresh-bulk {
  padding: 8px 16px; font-size: 12.5px; font-weight: 700; color: var(--blue); border-color: #c9d6e3;
}
:root[data-theme="dark"] .btn-refresh-li.btn-refresh-bulk { border-color: var(--line); }

/* Seen-count badge — dedup-on-ingest counter (how many times re-seen). */
.feed-seen {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 9px; border-radius: var(--radius-pill);
  background: var(--surface-soft); border: 1px solid var(--line);
  font-size: 11px; font-weight: 700; color: var(--ink-soft);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.feed-seen::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--blue); flex: 0 0 auto;
}
.feed-seen .feed-seen-n { color: var(--ink); }
.feed-seen .feed-seen-label {
  font-weight: 600; color: var(--ink-faint); text-transform: uppercase; letter-spacing: .03em;
}
/* Highlight when a fresh re-harvest bumped the count (app.js adds .is-updated). */
.feed-seen.is-updated { background: var(--green-tint); border-color: var(--green-line); color: var(--green); }
.feed-seen.is-updated::before { background: var(--green); }

/* ----------------------------------------------------------------------------
 * Responsive — drawer full-width on narrow screens; pickers stack.
 * -------------------------------------------------------------------------- */
@media (max-width: 640px) {
  .np-drawer { width: 100vw; max-width: 100vw; }
  .sched-edit .sched-edit-actions { margin-left: 0; width: 100%; }
  .tz-picker { max-width: 100%; }
}

/* ============================================================================
 * app.js-emitted components (appended) — exact class names rendered by
 * studio/web/app.js for items #2/#3/#5/#6/#8/#9. Additive only; distinct from
 * the class vocabulary above so there are no collisions. All consume the shared
 * design tokens, so dark/light follows automatically.
 * ========================================================================== */

/* ---- #2 world-clock chips: full date+day+time on one line ----------------- */
.tz-chip { align-items: center; }
.tz-chip .tz-chip-time { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ---- #3 searchable IANA tz picker (app.js DOM) ---------------------------- */
.tz-pick-mirror { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.tz-pick-chips { display: flex; flex-wrap: wrap; gap: 6px; min-height: 2px; }
.tz-pick-empty { font-size: 12.5px; color: var(--ink-faint); font-style: italic; }
.tz-pick-chip { display: inline-flex; align-items: center; gap: 7px; padding: 4px 6px 4px 11px;
  border: 1px solid var(--blue); background: var(--blue-tint); color: var(--blue);
  border-radius: var(--radius-pill, 999px); font-size: 12.5px; font-weight: 600; }
.tz-pick-chip-label { line-height: 1.2; }
.tz-pick-chip-id { font-size: 10.5px; font-weight: 600; color: var(--ink-faint); font-variant-numeric: tabular-nums; }
:root[data-theme="dark"] .tz-pick-chip-id { color: var(--ink-soft); }
.tz-pick-chip-x { border: 0; background: transparent; color: var(--blue); cursor: pointer;
  font-size: 13px; line-height: 1; padding: 0 2px; font-family: inherit; }
.tz-pick-chip-x:hover { color: var(--red); }
.tz-pick-searchrow { display: flex; align-items: center; gap: 10px; }
.tz-pick-searchrow .tz-pick-search { flex: 1 1 auto; }
.tz-pick-count { font-size: 11.5px; font-weight: 700; color: var(--ink-faint);
  text-transform: uppercase; letter-spacing: .03em; font-variant-numeric: tabular-nums; white-space: nowrap; }
.tz-pick-results { display: flex; flex-direction: column; max-height: 260px; overflow-y: auto;
  border: 1px solid var(--line); border-radius: var(--radius-sm, 10px); background: var(--card); }
.tz-pick-results.hidden { display: none; }
.tz-pick-opt { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  padding: 8px 12px; border: 0; border-bottom: 1px solid var(--line-soft); background: transparent;
  color: var(--ink); font-size: 13px; font-family: inherit; cursor: pointer; }
.tz-pick-opt:last-child { border-bottom: 0; }
.tz-pick-opt:hover { background: var(--blue-tint); }
.tz-pick-opt-label { font-weight: 600; }
.tz-pick-opt-id { color: var(--ink-faint); font-size: 12px; }
.tz-pick-opt-now { margin-left: auto; font-size: 11.5px; font-weight: 700; color: var(--ink-soft);
  font-variant-numeric: tabular-nums; white-space: nowrap; }
.tz-pick-noresult { padding: 16px 12px; text-align: center; font-size: 12.5px; color: var(--ink-faint); }

/* ---- #5 inline scheduled-time editor (app.js DOM) ------------------------- */
.sched-edit-trigger { display: inline-block; border: 0; background: transparent; padding: 0;
  margin: 2px 0 0; font: inherit; color: var(--ink-soft); cursor: pointer; text-align: left; }
.sched-edit-trigger:hover { color: var(--blue); text-decoration: underline; }
.sched-inline-edit { display: flex; flex-direction: column; gap: 6px; margin: 4px 0 2px; }
.sched-inline-row { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.sched-inline-input { width: auto; min-width: 220px; }
.sched-inline-err { margin: 0; font-size: 12px; color: var(--red); }

/* ---- #6 feed timestamps + seen-count meta row (app.js DOM) ---------------- */
.feed-meta-row { display: flex; flex-wrap: wrap; gap: 8px 14px; margin: 6px 0 2px;
  font-size: 11.5px; color: var(--ink-soft); }
.feed-meta-row .feed-ts { display: inline-flex; align-items: baseline; gap: 4px; white-space: nowrap; }
.feed-ts-label { font-weight: 700; color: var(--ink-faint); text-transform: uppercase; letter-spacing: .02em; font-size: 10.5px; }
.feed-ts-val { font-variant-numeric: tabular-nums; }
.feed-refresh-flag { color: var(--amber); }
.feed-refresh-flag .feed-ts-label { color: var(--amber); }

/* ---- #8/#9 per-card select + refresh button + bulk bar (app.js DOM) ------- */
.feed-select-wrap { display: inline-flex; align-items: center; margin-right: 4px; }
.feed-select { width: 16px; height: 16px; accent-color: var(--blue); cursor: pointer; }
.feed-card-actions { margin-top: 12px; }
.feed-refresh-btn { white-space: nowrap; }
.feed-bulk-bar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 14px; margin: 0 0 12px; background: var(--surface-soft);
  border: 1px solid var(--line); border-radius: var(--radius-sm, 10px); }
.feed-bulk-bar.hidden { display: none; }
.feed-bulk-count { font-size: 12.5px; font-weight: 700; color: var(--ink-soft); }

/* ============================================================================
 * Dark-mode visual fixes (#4 re-test): topbar, ghost/secondary buttons, and the
 * <select> caret. Appended LAST so equal-specificity rules win the cascade.
 * ========================================================================== */

/* 1) Topbar was hard-coded rgba(255,255,255,.82) with --navy text -> a bright bar
 *    in a dark app. Darken it and light the title/subtitle. */
:root[data-theme="dark"] .topbar { background: rgba(12,20,29,.86); }
:root[data-theme="dark"] .topbar h1,
:root[data-theme="dark"] .topbar .page-title { color: var(--ink); }
:root[data-theme="dark"] .topbar .page-sub { color: var(--ink-faint); }

/* 2) Ghost + secondary buttons were background:#fff + soft ink -> unreadable
 *    light-on-light. Give them a card surface with primary ink. */
:root[data-theme="dark"] .btn--ghost,
:root[data-theme="dark"] .ghost,
:root[data-theme="dark"] .btn--secondary,
:root[data-theme="dark"] .secondary {
  background: var(--card); color: var(--ink); border-color: var(--line);
}
:root[data-theme="dark"] .btn--ghost:hover,
:root[data-theme="dark"] .ghost:hover,
:root[data-theme="dark"] .btn--secondary:hover,
:root[data-theme="dark"] .secondary:hover {
  background: var(--blue-tint); border-color: var(--blue); color: var(--blue);
}

/* 3) <select> caret TILED in dark mode: the earlier dark rule re-set the chevron
 *    image but not its repeat/position (the `background:` shorthand had reset
 *    them). Re-assert the full caret so it shows ONCE, on the right. */
:root[data-theme="dark"] select,
:root[data-theme="dark"] select:focus {
  background-color: var(--surface-soft);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%23aebccb' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 13px center;
}
:root[data-theme="dark"] select:focus { background-color: var(--card); }

/* ============================================================================
 * Dark-mode sweep #2 — every styles.css rule that hard-codes a LIGHT surface
 * (#fff / #f0f3f6 / #fffbeb / #fdecea) and so stayed bright in dark mode. Full
 * site audit (all 8 views + modal). :not() guards keep selected/solid states.
 * ========================================================================== */

/* Danger buttons (Forget token / Clear / Disconnect) — were #fff. */
:root[data-theme="dark"] .btn--danger:not(.solid) { background: var(--card); }
:root[data-theme="dark"] .btn--danger:not(.solid):hover { background: var(--red-tint); }

/* FullCalendar toolbar buttons (Today / ‹ › / Month / Week) + faint title. */
:root[data-theme="dark"] .calendar-wrap .fc .fc-button-primary {
  background: var(--card); border-color: var(--line); color: var(--ink-soft);
}
:root[data-theme="dark"] .calendar-wrap .fc .fc-button-primary:hover { background: var(--surface-soft); color: var(--ink); }
:root[data-theme="dark"] .calendar-wrap .fc .fc-button-primary:disabled { background: var(--surface-soft); color: var(--ink-faint); }
/* The ACTIVE view button is a filled brand surface, so it takes --blue-fill.
   --blue is lifted in this theme for link legibility and gives only 3.42:1
   behind white (measured on Month/Week/List, 2026-09-19). */
:root[data-theme="dark"] .calendar-wrap .fc .fc-button-active,
:root[data-theme="dark"] .calendar-wrap .fc .fc-button-primary:not(:disabled).fc-button-active {
  background: var(--blue-fill); border-color: var(--blue-fill); color: #fff;
}
:root[data-theme="dark"] .calendar-wrap .fc .fc-toolbar-title { color: var(--ink); }

/* Modal close (✕) + mobile nav toggle — were #fff. */
:root[data-theme="dark"] .modal-close,
:root[data-theme="dark"] .nav-toggle { background: var(--card); color: var(--ink); border-color: var(--line); }

/* Chips / filter pills — were #fff. */
:root[data-theme="dark"] .chip,
:root[data-theme="dark"] .chip-x,
:root[data-theme="dark"] .feed-theme-chip { background: var(--card); color: var(--ink-soft); border-color: var(--line); }

/* Network: score pill, timeline icon, opener card — were #fff. */
:root[data-theme="dark"] .np-pill--score,
:root[data-theme="dark"] .np-tl-ico,
:root[data-theme="dark"] .np-opener { background: var(--card); color: var(--ink-soft); border-color: var(--line); }

/* Compose: image-style chip + collapse/slide/copy buttons — were #fff. */
:root[data-theme="dark"] .cmp-style-chip:not(.is-selected),
:root[data-theme="dark"] .cmp-collapse-btn,
:root[data-theme="dark"] .cmp-slide-btn,
:root[data-theme="dark"] .cmp-copy-btn { background: var(--card); color: var(--ink-soft); border-color: var(--line); }
:root[data-theme="dark"] .cmp-slide-btn:disabled { background: var(--surface-soft); color: var(--ink-faint); }

/* Light tinted banners. */
:root[data-theme="dark"] .dup-warning { background: var(--amber-tint); border-color: var(--amber-line); color: var(--amber); }
:root[data-theme="dark"] .pts-menu-item.danger:hover { background: var(--red-tint); }

/* ============================================================================
 * #10 — NETWORK RESULTS PAGER. Page-number pills + prev/next, a page-size
 * control, and a "Showing X–Y of Z" summary. Additive only; styles.css has no
 * rules for these classes. Mirrors the existing pill-button language (radius,
 * var(--t-fast) transitions, blue active/focus) and consumes only design
 * tokens, so it reads correctly in BOTH light and dark with no extra rules.
 * ========================================================================== */

/* Container — a centered, wrapping row of controls beneath the results list. */
.np-pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin: 16px 0 4px;
}

/* Each page control is a pill button matching .btn-refresh-li / .np-drawer-close. */
.np-page-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 36px;
  padding: 7px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill, 999px);
  background: var(--card);
  color: var(--ink-soft);
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  transition: background var(--t-fast, .15s ease),
              border-color var(--t-fast, .15s ease),
              color var(--t-fast, .15s ease);
}
.np-page-btn:hover {
  border-color: var(--blue);
  color: var(--blue);
  background: var(--blue-tint);
}
.np-page-btn:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
}
/* Current page — filled blue with readable ink on both themes. */
.np-page-btn.is-active,
.np-page-btn[aria-current="page"] {
  background: var(--blue);
  border-color: var(--blue);
  color: var(--ink-invert, #fff);
  cursor: default;
}
.np-page-btn.is-active:hover,
.np-page-btn[aria-current="page"]:hover {
  background: var(--blue);
  border-color: var(--blue);
  color: var(--ink-invert, #fff);
}
/* Disabled (e.g. the active page, or a clamped prev/next) — quiet, inert. */
.np-page-btn:disabled,
.np-page-btn[disabled],
.np-page-btn[aria-disabled="true"] {
  cursor: default;
  opacity: .55;
}
.np-page-btn:disabled:hover,
.np-page-btn[disabled]:hover,
.np-page-btn[aria-disabled="true"]:hover {
  background: var(--card);
  border-color: var(--line);
  color: var(--ink-soft);
}

/* Prev / Next — slightly wider pills; same look, just a roomier hit target. */
.np-page-btn.np-page-prev,
.np-page-btn.np-page-next {
  padding-left: 14px;
  padding-right: 14px;
  color: var(--ink);
}
.np-page-btn.np-page-prev:disabled,
.np-page-btn.np-page-next:disabled,
.np-page-btn.np-page-prev[disabled],
.np-page-btn.np-page-next[disabled] {
  color: var(--ink-faint);
}

/* Ellipsis gap between non-contiguous page ranges (e.g. 1 … 7 8 9 … 42). */
.np-page-gap {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  padding: 0 2px;
  color: var(--ink-faint);
  font-size: 12.5px;
  font-weight: 700;
  user-select: none;
}

/* Page-size control — a labelled <select> that sits with the pager row. */
.np-page-size {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 11.5px;
  font-weight: 700;
  color: var(--ink-faint);
  text-transform: uppercase;
  letter-spacing: .03em;
}
.np-page-size .np-page-size-label {
  white-space: nowrap;
}
.np-page-size select {
  width: auto;
  min-width: 64px;
  padding: 7px 30px 7px 11px;
  font-size: 12.5px;
  font-weight: 700;
  font-family: inherit;
  text-transform: none;
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
  border-radius: var(--radius-pill, 999px);
}

/* "Showing X–Y of Z" summary text is written to #network-results-summary
 * (.np-results-summary) by setSummary() — styled there, not here. */

/* Narrow screens — let the row breathe and keep the summary on its own line. */
@media (max-width: 640px) {
  .np-pager { gap: 6px; }
  .np-page-btn { min-width: 34px; padding: 7px 10px; }
  .np-pager-summary { flex: 1 1 100%; text-align: center; }
}

/* =============================================================================
 * UX AUDIT round (100 frontend-only improvements) — additive polish layer.
 * All rules reuse the :root tokens; nothing here touches the backend. Grouped to
 * mirror docs/UX_AUDIT_100.md. Loaded last so it can refine earlier rules.
 * ========================================================================== */

/* B14 — global keyboard focus ring (only a few elements had one before). */
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible,
textarea:focus-visible, [tabindex]:focus-visible, .nav-item:focus-visible,
.btn:focus-visible, .cmp-topic-chip:focus-visible, .cmp-hook-use:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
  border-radius: 6px;
}

/* B17 — respect reduced-motion. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; scroll-behavior: auto !important; }
}

/* A3/A4 — clearer active + hover nav. */
.nav-item { transition: background var(--t-fast), color var(--t-fast); position: relative; }
.nav-item:hover { background: rgba(255,255,255,.06); }
.nav-item.active::before { content: ""; position: absolute; left: 0; top: 8px; bottom: 8px; width: 3px; border-radius: 0 3px 3px 0; background: var(--blue); }

/* A5/A6 — sticky topbar with a divider so long pages keep the title + actions. */
.topbar { position: sticky; top: 0; z-index: 30; border-bottom: 1px solid var(--line); background: var(--card); }

/* B15 — nudge tertiary text up to AA on white (light theme only). */
/* #76838f gave .hint 3.43:1 on a tinted panel — hints are body text people
   are expected to read, not decoration. #5c6873 is 5.04 there, 5.70 on white. */
:root:not([data-theme="dark"]) { --ink-faint: #5c6873; }

/* F55/F57/F61 — button consistency: hover lift, active, and a real disabled look. */
.btn { transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast), box-shadow var(--t-fast); }
.btn:not(:disabled):hover { box-shadow: var(--shadow); }
.btn:disabled, .btn[disabled] { opacity: .55; cursor: not-allowed; box-shadow: none; }

/* C/D/H/I — utility: clamp long previews to N lines for scannable lists. */
.u-clamp-2, .u-clamp-3 { display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden; }
.u-clamp-2 { -webkit-line-clamp: 2; }
.u-clamp-3 { -webkit-line-clamp: 3; }

/* H73/I79 — sticky filter/search bars sit just under the sticky topbar. */
.feed-toolbar, .u-sticky-bar {
  position: sticky; top: var(--topbar-h, 64px); z-index: 20;
  background: var(--bg); padding-top: 8px; padding-bottom: 8px;
}

/* M100 — centre content + cap line length on wide monitors (compose keeps its own 2-col). */
[data-view-panel]:not([data-view-panel="compose"]) { max-width: 1360px; margin-inline: auto; }

/* D35 — loading skeleton shimmer. */
.u-skeleton { position: relative; overflow: hidden; background: var(--surface-soft); border-radius: var(--radius-sm); min-height: 16px; }
.u-skeleton::after { content: ""; position: absolute; inset: 0; transform: translateX(-100%); background: linear-gradient(90deg, transparent, rgba(120,140,160,.14), transparent); animation: u-shimmer 1.2s infinite; }
@keyframes u-shimmer { 100% { transform: translateX(100%); } }
.u-skeleton-row { height: 56px; margin-bottom: 10px; }

/* I81 — floating "back to top" button (shown by JS after scrolling). */
.u-totop { position: fixed; right: 22px; bottom: 22px; z-index: 40; width: 40px; height: 40px; border-radius: var(--radius-pill); border: 1px solid var(--line); background: var(--card); color: var(--ink-soft); box-shadow: var(--shadow); cursor: pointer; font-size: 18px; display: none; }
.u-totop.is-on { display: inline-flex; align-items: center; justify-content: center; }
.u-totop:hover { color: var(--blue); border-color: var(--blue); }

/* G25 — calendar legend + G63 upcoming-posts agenda that fills the dead space. */
.cal-legend { display: flex; flex-wrap: wrap; gap: 14px; align-items: center; padding: 8px 4px 2px; font-size: 12px; color: var(--ink-soft); }
.cal-legend .dot { display: inline-flex; align-items: center; gap: 6px; }
.cal-legend .dot::before { content: ""; width: 9px; height: 9px; border-radius: 50%; }
.cal-legend .dot.scheduled::before { background: var(--blue); }
.cal-legend .dot.published::before { background: var(--green); }
.cal-legend .dot.failed::before { background: var(--red); }
.cal-legend .dot.draft::before { background: var(--slate); }
.cal-legend .dot.publishing::before { background: var(--amber); }
.cal-legend .dot.manual::before { background: var(--navy); }
/* Dark --navy is #0e2a40, which vanished on the dark chip (navy on navy, audit 2026-10-03,
 * V6). A light ring keeps it the same dark navy as the Manual posts on the grid, and visible. */
:root[data-theme="dark"] .cal-legend .dot.manual::before,
:root[data-theme="dark"] .cal-legend .leg.manual .sw { box-shadow: inset 0 0 0 1.5px var(--ink-soft); }
.cal-agenda { margin-top: 18px; }
.cal-agenda h3 { font-size: 13px; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-faint); margin: 0 0 8px; }
.cal-agenda-item { display: flex; align-items: center; gap: 10px; padding: 9px 12px; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--card); margin-bottom: 8px; cursor: pointer; transition: border-color var(--t-fast); }
.cal-agenda-item:hover { border-color: var(--blue); }
.cal-agenda-when { font-variant-numeric: tabular-nums; font-weight: 700; color: var(--ink-soft); min-width: 118px; font-size: 12.5px; }
.cal-agenda-title { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* K90 — sticky Compose action bar so Save/Schedule/Publish are always reachable. */
.cmp-actions { position: sticky; bottom: 0; z-index: 15; background: var(--card); border-top: 1px solid var(--line); padding: 10px 0; margin-top: 8px; }

/* K92/K96 — small inline meta (char count, daily spend). */
.cmp-meta-note { font-size: 11.5px; color: var(--ink-faint); }
.cmp-post-count { font-variant-numeric: tabular-nums; }
.cmp-post-count.is-over { color: var(--red); font-weight: 700; }

/* J85 — network score badge gets a label affordance via cursor + title (set in JS). */
.np-score, .net-score, [class*="score-badge"] { cursor: help; }

/* C27/D39 — generic empty-state used by JS (reuses .empty look). */
.u-empty { text-align: center; padding: 34px 16px; color: var(--ink-soft); }
.u-empty .u-empty-ico { font-size: 30px; opacity: .5; }
.u-empty .u-empty-title { font-weight: 700; margin: 8px 0 4px; color: var(--ink-soft); }
/* An empty state's own button (the Calendar's "Try again") sits under its words, not against them. */
.u-empty > .btn { margin-top: 14px; }

/* M99 — comfortable tap targets on touch + keep world-clock from overflowing. */
@media (max-width: 640px) {
  .btn, .nav-item, .pill, input, select { min-height: 40px; }
  .topbar .page-title { font-size: 18px; }
}

/* B10 — skip link: off-screen until focused, then pinned top-left. */
.skip-link { position: absolute; left: -9999px; top: 0; z-index: 100; background: var(--blue); color: #fff; padding: 8px 14px; border-radius: 0 0 8px 0; font-weight: 700; }
.skip-link:focus { left: 0; }

/* =====================================================================
   UX AUDIT — round 2: wire the utilities into Scheduled / Feed / Network /
   Newsletter and finish the partials. Frontend-only; loaded last.
   ===================================================================== */

/* A9 — one title size/weight for every view (Compose vs Calendar drifted). */
#view-title, .topbar .page-title { font-size: 20px; font-weight: 700; letter-spacing: -0.01em; line-height: 1.2; }

/* F55 — consistent button geometry so pills/sharper buttons stop mixing. */
.btn { min-height: 36px; border-radius: var(--radius-pill, 999px); }
.btn--sm { min-height: 30px; }
/* M99's 40px phone targets, restated after F55. Written above with the same weight, M99 lost to these
 * two lines on order, so on a phone every button stayed 36px and every small one 30px (audit 2026-10-03). */
@media (max-width: 640px) { .btn, .btn--sm { min-height: 40px; } }

/* F60 — a rotating chevron affordance on every <details> summary. */
details > summary { list-style: none; cursor: pointer; }
details > summary::-webkit-details-marker { display: none; }
details > summary::before { content: "▸"; display: inline-block; margin-right: 7px; color: var(--ink-faint); transition: transform var(--t-fast); }
details[open] > summary::before { transform: rotate(90deg); }

/* H71 — clamp Scheduled-card bodies to 2 lines so cards scan fast. */
.card-body.u-clamp-2 { -webkit-line-clamp: 2; }

/* H73 — Scheduled filter/header sticks under the topbar; chips carry counts. */
[data-view-panel="schedule"] .section-head { position: sticky; top: var(--topbar-h, 64px); z-index: 12; background: var(--card); padding-top: 6px; padding-bottom: 8px; }
.sched-count { font-variant-numeric: tabular-nums; color: var(--ink-faint); font-weight: 600; margin-left: 6px; }

/* I80 — Feed body clamps to 3 lines with an inline "show more"/"show less". */
.feed-card .card-body.u-clamp-3 { -webkit-line-clamp: 3; }
.feed-more { background: none; border: 0; color: var(--blue); cursor: pointer; font: inherit; font-weight: 600; padding: 2px 0; }
.feed-more:hover { text-decoration: underline; }

/* J89 — Network search + segment filters stay reachable while scrolling. */
.np-searchbar { position: sticky; top: var(--topbar-h, 64px); z-index: 11; background: var(--card); padding: 8px 0; }
/* J86 — compact icon-only "refresh profile" button. */
.np-refresh-btn.is-icon { width: 34px; min-width: 34px; padding: 0; font-size: 15px; line-height: 1; }

/* C20 / G — calendar event pills get a status glyph (colour alone isn't enough). */
.fc .fc-daygrid-event.ev--published .fc-event-title::before,
.fc .fc-list-event.ev--published .fc-event-title::before { content: "✓ "; }
.fc .fc-daygrid-event.ev--scheduled .fc-event-title::before,
.fc .fc-list-event.ev--scheduled .fc-event-title::before { content: "⏱ "; }
.fc .fc-daygrid-event.ev--failed .fc-event-title::before,
.fc .fc-list-event.ev--failed .fc-event-title::before { content: "⚠ "; }

/* Trashed (owner 2026-09-09): frozen on the calendar — greyed, struck through,
   not draggable. Clicking opens the Restore / Delete dialog. */
.fc .ev--trashed { opacity: .5; filter: grayscale(1); }
.fc .ev--trashed .fc-event-title { text-decoration: line-through; }
.fc .ev--trashed .fc-event-main { cursor: pointer; }

/* L97 — Newsletter density: clamp issue summaries; pin the builder action head. */
.nl-issue-item-sub { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }
.nl-builder-head { position: sticky; top: var(--topbar-h, 64px); z-index: 10; background: var(--card); padding: 6px 0; }

/* E49 — small live char counters for the Compose Theme / Seed fields. */
.cmp-mini-count { display: block; font-size: 11px; color: var(--ink-faint); text-align: right; margin-top: 2px; font-variant-numeric: tabular-nums; }

/* D35 — skeleton list shown while a view's data loads (JS inserts the rows). */
.u-skeleton-list { padding: 6px 0; }

/* M99 — extra small-screen polish: stack dense rows, keep filters usable. */
@media (max-width: 640px) {
  .np-searchbar, .feed-toolbar { flex-wrap: wrap; }
  .np-searchbar > * , .feed-toolbar > * { flex: 1 1 auto; }
  [data-view-panel="schedule"] .section-head { flex-wrap: wrap; }
  .card-head { flex-wrap: wrap; }
}

/* E50 — inline validation: red-border an input marked directly (not via .field) + the message. */
input.has-error, select.has-error, textarea.has-error { border-color: var(--red, #c0392b) !important; }
input.has-error:focus { box-shadow: 0 0 0 3px rgba(180,35,24,.12); }
.field-inline-err { display: block; font-size: 12px; color: var(--red, #c0392b); margin-top: 4px; }

/* ----------------------------------------------------------------------------
 * Round 8 — dark-mode contrast fix. Several headings / stat numbers / chips use
 * `color: var(--navy)`. In dark mode --navy is a near-black chrome colour, so
 * navy-on-dark-surface text (e.g. "SCHEDULED POSTS · N posts", the calendar
 * month title, modal titles, stat numbers, network sub-heads) renders almost
 * invisible. Lift those to --ink on dark surfaces; lift navy-on-tint chips to
 * the bright --blue. Dark-only overrides — light mode is untouched.
 * ------------------------------------------------------------------------- */
:root[data-theme="dark"] .section-head h2,
:root[data-theme="dark"] .modal-title,
:root[data-theme="dark"] .stat-value,
:root[data-theme="dark"] .calendar-wrap .fc .fc-toolbar-title,
:root[data-theme="dark"] .np-subhead,
:root[data-theme="dark"] .np-guide-title { color: var(--ink); }
:root[data-theme="dark"] .pill--manual,
:root[data-theme="dark"] .np-pill--future,
:root[data-theme="dark"] .countdown-banner { color: var(--blue); }
:root[data-theme="dark"] .countdown-banner { border-color: var(--blue-tint); }
:root[data-theme="dark"] .nav-toggle { background: var(--card); color: var(--ink); }

/* ============================================================================
 * Round 9 — dark-mode sweep (#1/#2). Remaining hard-coded LIGHT colours that
 * the earlier rounds missed: scrollbars (forced a light-grey thumb that was
 * invisible on dark), dashed dropzone border, card/chip hover borders, the
 * light-blue disabled primary button, and the neutral diagnostic dot. All are
 * dark-only overrides; light mode is untouched.
 * ========================================================================== */

/* Scrollbars — the global `* { scrollbar-color:#c5d2df }` + webkit thumb #cdd9e4
 * overrode color-scheme and stayed light. Re-tint to the dark line palette. */
:root[data-theme="dark"] * { scrollbar-color: #34465a transparent; }
:root[data-theme="dark"] *::-webkit-scrollbar-thumb { background: #34465a; }
:root[data-theme="dark"] *::-webkit-scrollbar-thumb:hover { background: #43586e; }

/* Dropzone (New Post / media attach) — light dashed border on a dark surface. */
:root[data-theme="dark"] .dropzone { border-color: var(--line); }
:root[data-theme="dark"] .dropzone:hover,
:root[data-theme="dark"] .dropzone.drag { border-color: var(--blue); }

/* Hover borders that hard-coded #c4d2df (light grey) — use the line/blue tokens. */
:root[data-theme="dark"] .chip:hover { border-color: var(--blue); }
:root[data-theme="dark"] .np-card:hover { border-color: var(--blue); }
:root[data-theme="dark"] .cmp-style-chip:not(.is-selected):hover { border-color: var(--blue); }

/* Disabled primary button was #aac3dd (light blue) — quiet it on dark surfaces. */
:root[data-theme="dark"] .btn--primary:disabled { background: var(--surface-soft); color: var(--ink-faint); border-color: var(--line); }

/* Neutral diagnostic dot default (#cbd5e1) — only the unknown/idle state. Scoped to
 * a check that neither passed nor failed: written for every dot, it out-ranked
 * .diag-ok / .diag-bad in styles.css, so in dark the passing and the failing check
 * were the same grey (audit 2026-10-03, V6). The failing check's words were #dc2626,
 * 3.46:1 on the dark card; the theme's red reads. */
:root[data-theme="dark"] .diag-item:not(.diag-ok):not(.diag-bad) .diag-dot { background: var(--slate-line); }
:root[data-theme="dark"] .diag-bad .diag-detail { color: var(--red); }

/* New Post modal: the .field <span class="hint"> ("Leave empty to save as a
 * draft.") + read-only media note read fine via tokens; the modal foot already
 * uses --surface-soft. Lift the modal sub + any --navy field labels just in case
 * a view passes them through. */
:root[data-theme="dark"] .modal-sub { color: var(--ink-faint); }

/* ============================================================================
 * Round 10 — Content Calendar hub (#3/#4): dark-mode contrast + filter chips.
 * ==========================================================================*/
/* Event chips: white-on-light-green/amber failed WCAG (~1.95:1). The published
 * green and publishing amber are light enough in dark mode to need ink-dark text. */
:root[data-theme="dark"] .calendar-wrap .fc .ev--published,
:root[data-theme="dark"] .calendar-wrap .fc .ev--publishing,
:root[data-theme="dark"] .calendar-wrap .fc .ev--published .fc-event-time,
:root[data-theme="dark"] .calendar-wrap .fc .ev--publishing .fc-event-time,
:root[data-theme="dark"] .calendar-wrap .fc .ev--published .fc-event-title,
:root[data-theme="dark"] .calendar-wrap .fc .ev--publishing .fc-event-title { color: #0b1622; }
/* Today's date number was blue-on-faint-blue (~1.0:1); lift to a light tint. */
:root[data-theme="dark"] .calendar-wrap .fc .fc-day-today .fc-daygrid-day-number { color: #cfe4fb; }
/* Interactive legend/filter chips read correctly on the dark card surface. */
:root[data-theme="dark"] .cal-legend-chip { background: var(--card); border-color: var(--line); color: var(--ink-soft); }
:root[data-theme="dark"] .cal-legend-chip:hover { border-color: var(--blue); color: #cfe4fb; }
:root[data-theme="dark"] .cal-legend-chip.is-active { background: rgba(59,142,224,.16); border-color: var(--blue); color: #cfe4fb; }
/* A grab cursor only where editing is allowed (FullCalendar tags those events
 * .fc-event-draggable); terminal events keep the default pointer. */
.calendar-wrap .fc .fc-event-draggable { cursor: grab; }
.calendar-wrap .fc .fc-event-draggable:active { cursor: grabbing; }
