/* PRISMA UI KIT (issue 29, ADR 0001)
   The shared component classes every screen composes; rendered via the
   kit partials/helpers (shared/_modal, shared/_tabs, shared/_card,
   shared/_flash, shared/_empty_state, UiHelper, UiFormBuilder). Values
   come from tokens.css only — if a component needs a new raw size or
   color, it becomes a token first. The living style guide at /styleguide
   (dev) shows everything below in both themes. */

/* ---- foundation ---- */
* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--text-base);
  background: var(--bg);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
  line-height: var(--leading-body);
}

a { color: inherit; text-decoration: none; }
button { font: inherit; cursor: pointer; }

h1,
h2,
h3,
p { margin: 0; }

/* Vertical rhythm primitive (ADR 0011): a container's direct children are
   separated by one owned gap, set per container via --flow-space, instead
   of each child carrying its own margin-bottom. */
.flow > * + * { margin-block-start: var(--flow-space, var(--space-5)); }

/* ---- text ---- */

/* Page headings are Fraunces display heroes (ADR 0004): the serif carries
   the brand's character at the top of every screen, sans everywhere else. */
.page-title {
  font-family: var(--font-serif);
  font-size: var(--text-2xl);
  font-weight: var(--weight-bold);
  letter-spacing: -0.01em;
  margin-bottom: var(--space-5);
}

.sec-title {
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  color: var(--ink-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;

  /* Default top gap so a section heading always separates from the block
     above it (ADR 0011); zeroed when it opens a container. */
  margin: var(--space-5) 0 var(--space-3);
}

.sec-title:first-child { margin-top: 0; }

.kicker {
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  color: var(--ink-faint);
  text-transform: uppercase;
  letter-spacing: 0.14em;
}

/* Inline links inside prose read as links: the global reset strips the
   affordance, so prose contexts restore it (WCAG 1.4.1). --accent-text
   clears 4.5:1 on every surface (issue 07). */
.link,
.field-hint a,
.list-row-body a,
.sec-note a {
  color: var(--accent-text);
  text-decoration: underline;
  font-weight: var(--weight-medium);
}

:is(.link, .field-hint a, .list-row-body a, .sec-note a):focus-visible {
  outline: var(--focus-outline);
  outline-offset: var(--focus-outline-offset);
  border-radius: var(--radius-sm);
}

.prism-text {
  /* Solid fallback first so the wordmark stays readable if background-clip
     fails (the external review footer is an uncontrolled surface). */
  color: var(--accent-text);
}

/* unprefixed background-clip: text needs Chrome 120+, which is exactly
   the allow_browser :modern baseline — no -webkit- prefix required */
@supports (background-clip: text) {
  .prism-text {
    background: var(--prism);
    background-clip: text;
    color: transparent;
  }
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Skip link: the first tab stop in every layout, parked above the
   viewport until keyboard focus brings it in (WCAG 2.4.1). Sits above
   the toast stack (z 200). */
.skip-link {
  position: fixed;
  inset-block-start: var(--space-2);
  inset-inline-start: var(--space-2);
  z-index: 300;
  background: var(--surface);
  color: var(--ink);
  border: var(--hairline-strong);
  border-radius: var(--radius-full);
  padding: var(--space-2) var(--space-4);
  font-weight: var(--weight-medium);
  box-shadow: var(--shadow-pop);
  transform: translateY(calc(-100% - var(--space-4)));
}

.skip-link:focus-visible {
  transform: none;
  outline: var(--focus-outline);
  outline-offset: var(--focus-outline-offset);
}

/* ---- buttons ---- */
.btn-prism {
  background: var(--prism);
  color: var(--accent-ink);
  border: 0;
  border-radius: var(--radius-full);
  padding: var(--btn-pad);
  font-weight: var(--weight-bold);
  font-size: var(--text-base);
  box-shadow: var(--shadow-md);
  transition: transform 0.12s, box-shadow 0.12s;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  white-space: nowrap;
}

.btn-prism:hover { transform: translateY(-1px); box-shadow: var(--shadow-pop); }

/* A disabled primary reads inert, not pressable (e.g. the composer's
   adaptive publish button while a custom time is missing or in the past). */

/* opacity: 1 overrides the shared [disabled] opacity: 0.5 below, so the
   neutral fill owns the look and --ink-faint keeps its tuned AA contrast. */
.btn-prism:disabled { background: var(--surface-2); color: var(--ink-faint); box-shadow: none; cursor: not-allowed; opacity: 1; }
.btn-prism:disabled:hover { transform: none; box-shadow: none; }

.btn-quiet {
  background: var(--surface);
  color: var(--ink);
  border: var(--hairline);
  border-radius: var(--radius-full);
  padding: var(--space-2) var(--space-4);
  font-weight: var(--weight-medium);
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.btn-quiet:hover { border-color: var(--border-strong); }
.btn-danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 35%, var(--border)); }
.btn-danger:hover { border-color: var(--danger); }
.btn-ink { background: var(--ink); color: var(--bg); border: 0; }
.btn-ink:hover { opacity: 0.85; }

:is(.btn-prism, .btn-quiet, .btn-ink, .iconbtn, .toggle, .tabs a, .seg a):focus-visible {
  outline: var(--focus-outline);
  outline-offset: var(--focus-outline-offset);
}

:is(.btn-prism, .btn-quiet, .btn-ink)[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
}

/* Button width and size modifiers. */
.btn--full  { width: 100%; justify-content: center; }
.btn--small { padding: var(--chip-pad); font-size: var(--text-sm); }

/* Square icon-only button: top-bar actions, modal close, toast dismiss. */
.iconbtn {
  background: none;
  border: 0;
  color: var(--ink-faint);
  padding: var(--space-2);
  border-radius: var(--radius-sm);
  display: inline-grid;
  place-items: center;
}

.iconbtn:hover { color: var(--ink); background: var(--surface-2); }

/* Theme toggle shows the mode you'd switch to: moon in light, sun in dark. */
.theme-toggle .sun { display: none; }
.theme-toggle .moon { display: inline; }
.btn-quiet.theme-toggle { display: inline-grid; place-items: center; padding: var(--space-2) var(--space-3); }

[data-theme="dark"] .theme-toggle .sun { display: inline; }
[data-theme="dark"] .theme-toggle .moon { display: none; }

/* ---- badges & dots ---- */
.state-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--chip-gap);
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  padding: var(--chip-pad);
  border-radius: var(--radius-full);
  letter-spacing: 0.01em;

  /* a pill never breaks mid-label ("Needs reconnecting", "Partially
     failed"); tight rows wrap around it instead */
  white-space: nowrap;
}

.state-chip::before { content: ""; width: var(--dot-sm); height: var(--dot-sm); border-radius: 50%; background: currentcolor; }
.state-draft { color: var(--state-draft); background: var(--state-draft-bg); }
.state-pending { color: var(--state-pending); background: var(--state-pending-bg); }
.state-approved { color: var(--state-approved); background: var(--state-approved-bg); }
.state-scheduled { color: var(--state-scheduled); background: var(--state-scheduled-bg); }
.state-publishing { color: var(--state-publishing); background: var(--state-publishing-bg); }
.state-published { color: var(--state-published); background: var(--state-published-bg); }
.state-failed { color: var(--state-failed); background: var(--state-failed-bg); }
.state-active { color: var(--state-active); background: var(--state-active-bg); }
.state-paused { color: var(--state-paused); background: var(--state-paused-bg); }

.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  border: var(--hairline);
  background: var(--surface);
  border-radius: var(--radius-full);
  padding: var(--pill-pad);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  min-width: 0;
}

/* Status dots: channel health, the brand pill. */
.dot { width: var(--dot); height: var(--dot); border-radius: 50%; flex: none; }
.dot-accent { background: var(--accent); }
.dot-ok { background: var(--ok); }
.dot-warn { background: var(--warn); }
.dot-danger { background: var(--danger); }

/* ---- cards ---- */
.card {
  background: var(--surface);
  border: var(--hairline);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}

.card-pad { padding: var(--space-5); }

/* Stacked cards never glue together: any two adjacent cards in the column sit
   one rhythm step apart, so a page that renders several shared/card sections
   (billing, member edit, …) gets correct spacing without each card carrying
   its own margin (ADR 0011). The app is single-column (ADR 0003), so adjacent
   cards are always vertical; this collapses with the per-component card
   margins, so it never doubles. */
.card + .card { margin-block-start: var(--space-5); }

.card-title {
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  margin-bottom: var(--space-3);
}

/* ---- list rows (hairline-separated rows in a bare .card:
       channels, posts, variants) ---- */
.list-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-bottom: var(--hairline);
  font-size: var(--text-sm);
}

.list-row:last-child { border-bottom: 0; }

/* Stacked shape: content on top, a metadata line under it. */
.list-row-stack {
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-1);
}

.list-row-body { flex: 1; min-width: 0; }
.list-row-body b { display: block; overflow-wrap: anywhere; }

.list-row-body span {
  display: block;
  color: var(--ink-muted);
  font-size: var(--text-xs);
  overflow-wrap: anywhere;
}

/* A row action posted by button_to arrives wrapped in a form; the form
   is the flex item, and it never shrinks below its button. */
.list-row form { flex: none; }

/* A pickable row (the OAuth account selection): the row is the label,
   its checkbox styled like the kit's .check-row control. */
label.list-row { cursor: pointer; }

.list-row > input[type="checkbox"] {
  width: var(--space-4);
  height: var(--space-4);
  accent-color: var(--accent);
  margin: 0;
  flex: none;
}

a.list-row { color: inherit; }
a.list-row:hover { background: var(--surface-2); }

/* A picked account row lifts to the hover surface so the selection reads at
   a glance, not only via the small native tick (issue 09). */
label.list-row:has(input[type="checkbox"]:checked) { background: var(--surface-2); }

a.list-row:first-child { border-radius: var(--radius-lg) var(--radius-lg) 0 0; }
a.list-row:last-child { border-radius: 0 0 var(--radius-lg) var(--radius-lg); }

a.list-row:focus-visible {
  outline: var(--focus-outline);

  /* Inset: the ring must stay inside the card's clipped corners. */
  outline-offset: -2px;
}

/* ---- forms ---- */
.input,
.textarea,
select.input {
  width: 100%;
  background: var(--surface);
  color: var(--ink);
  border: var(--hairline-strong);
  border-radius: var(--radius-md);
  padding: var(--input-pad);
  font: inherit;
  outline: none;
}

/* The native select arrow hugs the right edge; draw the kit's chevron
   instead, inset to sit with the input's text padding. Physical "right"
   position is fine while the app is LTR-only (en/es); revisit here if
   an RTL locale ever lands. */
select.input {
  appearance: none;
  background: var(--surface) var(--chevron) no-repeat right var(--space-3) center;
  padding-inline-end: calc(var(--space-6) + var(--space-2));
}

.input::placeholder,
.textarea::placeholder { color: var(--ink-faint); }

.input:focus,
.textarea:focus {
  border-color: var(--accent);
  box-shadow: var(--focus-ring);
}

/* Element-level floor (ADR 0012): a form control with no kit class still
   renders themed, so a forgotten .input/.textarea never ships as a raw
   native widget. Zero-specificity :where() so .field-invalid and other
   overrides still win; the .input/.textarea/select.input rules above hold
   the same values at class specificity. The denylist excludes controls
   with their own styling (checkbox, radio, toggle, file, range, buttons). */
:where(
  textarea,
  select,
  input:not([type="checkbox"], [type="radio"], [type="file"], [type="range"], [type="color"], [type="submit"], [type="button"], [type="reset"], [type="image"], [type="hidden"])
) {
  width: 100%;
  background: var(--surface);
  color: var(--ink);
  border: var(--hairline-strong);
  border-radius: var(--radius-md);
  padding: var(--input-pad);
  font: inherit;
  outline: none;
}

:where(select) {
  appearance: none;
  background: var(--surface) var(--chevron) no-repeat right var(--space-3) center;
  padding-inline-end: calc(var(--space-6) + var(--space-2));
}

:where(textarea, select, input):not([type="checkbox"], [type="radio"], [type="file"], [type="range"], [type="color"], [type="submit"], [type="button"], [type="reset"], [type="image"], [type="hidden"]):focus {
  border-color: var(--accent);
  box-shadow: var(--focus-ring);
}

/* Themed file input: the native picker button gets kit chrome (ADR 0012),
   so the media uploader is no longer an unthemeable native control. */
::file-selector-button {
  font: inherit;
  margin-inline-end: var(--space-3);
  padding: var(--space-2) var(--space-4);
  background: var(--surface-2);
  color: var(--ink);
  border: var(--hairline-strong);
  border-radius: var(--radius-full);
  cursor: pointer;
}

::file-selector-button:hover { border-color: var(--border-strong); }

.textarea { resize: vertical; min-height: var(--textarea-min-h); line-height: var(--leading-relaxed); }
.field { display: block; margin-bottom: var(--space-4); }

.field > label {
  display: block;
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  color: var(--ink-muted);
  margin-bottom: var(--space-2);
}

/* Non-label heading for a group of repeated inputs (variation rows, keyword list). */
.field-group-label {
  display: block;
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  color: var(--ink-muted);
  margin-bottom: var(--space-2);
}

.field-invalid :is(.input, .textarea) { border-color: var(--danger); }

.field-error {
  display: block;
  font-style: normal;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--danger);
  margin-top: var(--space-2);
}

.field-hint {
  display: block;
  font-size: var(--text-sm);
  color: var(--ink-faint);
  margin-top: var(--space-2);
}

/* Form-level problems (errors on no single field, e.g. a publish refusal). */
.form-errors {
  border: var(--hairline);
  border-color: color-mix(in srgb, var(--danger) 35%, var(--border));
  background: color-mix(in srgb, var(--danger) 8%, var(--surface));
  color: var(--danger);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  margin-bottom: var(--space-4);
}

.form-errors ul {
  margin: 0;
  padding-left: var(--space-5);
}

.form-errors li + li { margin-top: var(--space-1); }

/* The advisory cousin: same shape on --warn, for problems that don't
   block (a draft over a network's rules fixes itself before the gate). */
.form-errors.form-warnings {
  border-color: color-mix(in srgb, var(--warn) 35%, var(--border));
  background: color-mix(in srgb, var(--warn) 8%, var(--surface));
  color: var(--warn);
}

/* The page-level alarm on the same danger surface: a banner above the
   content (broken channels today; anything brand-wide tomorrow). */
.brand-banner {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  border: var(--hairline);
  border-color: color-mix(in srgb, var(--danger) 35%, var(--border));
  background: color-mix(in srgb, var(--danger) 8%, var(--surface));
  color: var(--danger);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  margin-bottom: var(--space-4);
}

.brand-banner span { flex: 1; min-width: 0; }

.brand-banner a {
  color: var(--danger);
  font-weight: var(--weight-bold);
  text-decoration: underline;
  flex: none;
}

/* Advisory variant: same shape but amber --warn tokens in place of --danger.
   Use for near-limit warnings and degraded-but-not-broken states. */
.brand-banner--warn {
  background: color-mix(in srgb, var(--warn) 8%, var(--surface));
  color: var(--warn);
  border-color: color-mix(in srgb, var(--warn) 35%, var(--border));
}

.brand-banner--warn a { color: var(--warn); }

.toggle {
  width: var(--toggle-w);
  height: var(--toggle-h);
  border-radius: var(--radius-full);
  background: var(--border-strong);
  border: 0;
  position: relative;
  flex: none;
}

.toggle::after {
  content: "";
  position: absolute;
  top: var(--toggle-inset);
  left: var(--toggle-inset);
  width: calc(var(--toggle-h) - 2 * var(--toggle-inset));
  height: calc(var(--toggle-h) - 2 * var(--toggle-inset));
  border-radius: 50%;
  background: var(--accent-ink);

  /* slide on transform, never left, so the knob never triggers layout
     (the kit's animate-transform/opacity-only rule) */
  transition: transform 0.15s;
}

.toggle.on { background: var(--prism); }
.toggle.on::after { transform: translateX(calc(var(--toggle-w) - var(--toggle-h))); }

/* The same switch as a real form control: <input type="checkbox"
   class="toggle">, usually inside a .toggle-row. */
input.toggle { appearance: none; cursor: pointer; margin: 0; }
input.toggle:checked { background: var(--prism); }
input.toggle:checked::after { transform: translateX(calc(var(--toggle-w) - var(--toggle-h))); }

/* A labelled switch row: title + muted description left, switch right.
   The <label> targets the checkbox by for/id; the description is wired
   to it with aria-describedby, not wrapped in the label. */
.toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

/* The last toggle-row in its container needs no trailing margin — the card's
   padding (or the next element's own spacing) carries the gap. */
.toggle-row:last-child { margin-bottom: 0; }

.toggle-row-text label {
  display: block;
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  cursor: pointer;
}
.toggle-row-text p { font-size: var(--text-xs); color: var(--ink-muted); }

/* A checkbox checklist: .check-group wraps .check-row labels. */
.check-group { border: 0; padding: 0; margin: 0 0 var(--space-4); }

.check-group legend {
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  color: var(--ink-muted);
  margin-bottom: var(--space-2);
}

.check-row {
  display: flex;
  align-items: center;

  /* long labels plus trailing chips/limits wrap to a second line on
     phones instead of clipping */
  flex-wrap: wrap;
  gap: var(--space-3);
  padding: var(--space-2) 0;
  font-size: var(--text-sm);
  cursor: pointer;
}

.check-row input {
  width: var(--space-4);
  height: var(--space-4);
  accent-color: var(--accent);
  margin: 0;
}

/* A quota/usage bar is calm, not celebratory: a solid accent fill (never
   the prism gradient, whose pink end made a full bar read like an alarm).
   .meter--full switches to --warn so an at/near-capacity bar signals it. */
.meter { height: var(--meter-h); border-radius: var(--radius-full); background: var(--surface-2); overflow: hidden; }
.meter i { display: block; height: 100%; background: var(--accent); border-radius: var(--radius-full); }
.meter--full i { background: var(--warn); }

/* ---- tables ---- */

/* Wrap a .ptable in .table-scroll so wide tables scroll inside the
   column instead of overflowing it (ADR 0003). */
.table-scroll { overflow-x: auto; }
.ptable { width: 100%; border-collapse: collapse; font-size: var(--text-sm); }

.ptable th {
  text-align: left;
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
  font-weight: var(--weight-bold);
  padding: var(--space-3) var(--space-4);
  border-bottom: var(--hairline);
}

.ptable td { padding: var(--space-3) var(--space-4); border-bottom: var(--hairline); vertical-align: middle; }
.ptable tr:last-child td { border-bottom: 0; }

/* ---- tabs (the underline row; shared/_tabs) ---- */
.tabs {
  display: flex;
  gap: var(--space-4);
  overflow-x: auto;
  scrollbar-width: none;
  font-size: var(--text-sm);
  border-bottom: var(--hairline);

  /* Scrolling shadows (background-attachment: local): the covers scroll
     with the content and hide the edge shadows at either end, so a
     shadow only shows while more tabs sit off-screen — the overflow
     affordance the hidden scrollbar takes away. The covers must match
     the surface behind the tabs: --bg everywhere today; set --tabs-bg
     if a tab row ever sits on another surface. */
  background:
    linear-gradient(to right, var(--tabs-bg, var(--bg)) 40%, transparent) left / var(--space-6) 100% no-repeat local,
    linear-gradient(to left, var(--tabs-bg, var(--bg)) 40%, transparent) right / var(--space-6) 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%, color-mix(in srgb, var(--ink) 20%, transparent), transparent) left / var(--space-3) 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, color-mix(in srgb, var(--ink) 20%, transparent), transparent) right / var(--space-3) 100% no-repeat scroll;
}

.tabs::-webkit-scrollbar { display: none; }

.tabs a {
  color: var(--ink-faint);
  font-weight: var(--weight-medium);
  flex: none;
  padding-block: var(--space-2);
  border-bottom: var(--underline-w) solid transparent;
}

.tabs a.current { color: var(--ink); border-bottom-color: var(--accent); }
.tabs a:hover { color: var(--ink); }

/* ---- segmented control (a small either/or view switch: links in a
   pill, .current marks the active one; aria-current comes from the
   caller) ---- */
.seg {
  display: inline-flex;
  gap: var(--space-1);
  padding: var(--space-1);
  border: var(--hairline);
  border-radius: var(--radius-full);
  background: var(--surface);
}

.seg a {
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-full);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--ink-muted);
}

.seg a:hover { color: var(--ink); }
.seg a.current { background: var(--surface-2); color: var(--ink); }

/* ---- modal (shared/_modal: a native <dialog>) ---- */
.modal {
  background: var(--surface);
  color: var(--ink);
  border: var(--hairline);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-pop);
  padding: 0;
  width: min(var(--card-narrow), calc(100% - 2 * var(--space-4)));
}

.modal::backdrop { background: var(--backdrop); }

.modal-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-4) var(--space-3) var(--space-5);
  border-bottom: var(--hairline);
}

.modal-title { font-size: var(--text-lg); font-weight: var(--weight-bold); }
.modal-body { padding: var(--space-5); }

/* ---- emoji picker (shared/_emoji_picker + the emoji-picker Stimulus
   controller, issue 47, ADR 0007). .emoji-attach scopes the controller
   around a caption; .control-affix (form.field's affix wrapper) holds
   the textarea plus the trigger riding its bottom-right corner, and
   anchors the popover right under the caption. At phone widths the
   popover becomes a fixed bottom sheet so it never clips inside the
   column. ---- */
.control-affix { position: relative; }

/* Block, so the wrapper hugs the textarea exactly (the default
   inline-block leaves a baseline gap that would push the trigger's
   anchor below the border). The padding reserves the trigger's strip
   so the caption's last line never types under it (icon box + the
   button's own padding + the corner inset). */
.control-affix .textarea {
  display: block;
  padding-bottom: calc(var(--icon-box) + 2 * var(--space-2) + var(--space-2));
}

.control-affix .emoji-trigger {
  position: absolute;
  bottom: var(--space-2);
  inset-inline-end: var(--space-2);
}

.emoji-popover {
  position: absolute;

  /* hangs off the trigger's corner: right-aligned under the caption */
  inset-inline-end: 0;
  top: calc(100% + var(--space-1));
  z-index: 150; /* over the FAB (100), under toasts (200) */
  width: min(var(--emoji-popover-w), 100%);
  background: var(--surface);
  color: var(--ink);
  border: var(--hairline);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-pop);
  overflow: hidden;
}

.emoji-popover[hidden] { display: none; }

/* the popover's own loading / failed-to-load line; the web component
   replaces it once mounted */
.emoji-popover-status {
  margin: 0;
  padding: var(--space-5);
  font-size: var(--text-sm);
  color: var(--ink-muted);
  text-align: center;
}

/* The web component themes through its published custom properties —
   every one mapped to tokens, so light and dark both just work; color
   and font-family inherit through the shadow root. */
.emoji-popover emoji-picker {
  display: block;
  width: 100%;
  height: var(--emoji-popover-h);
  font-family: var(--font-sans);

  --background: var(--surface);
  --border-color: var(--border);
  --border-size: 0;
  --button-active-background: var(--surface-2);
  --button-hover-background: var(--surface-2);
  --category-font-color: var(--ink-muted);
  --category-font-size: var(--text-xs);
  --indicator-color: var(--accent);
  --input-border-color: var(--border-strong);
  --input-border-radius: var(--radius-sm);
  --input-font-color: var(--ink);
  --input-font-size: var(--text-sm);
  --input-placeholder-color: var(--ink-faint);
  --outline-color: var(--accent);
  --skintone-border-radius: var(--radius-sm);
}

@media (width <= 600px) {
  .emoji-popover {
    position: fixed;
    inset-inline: 0;
    top: auto;
    bottom: 0;
    width: auto;
    border-bottom: 0;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  }

  .emoji-popover emoji-picker { height: var(--emoji-sheet-h); }
}

@media (prefers-reduced-motion: no-preference) {
  .emoji-popover { animation: emoji-pop-in 0.15s ease-out; }

  @keyframes emoji-pop-in {
    from { opacity: 0; transform: translateY(calc(-1 * var(--space-1))); }
  }
}

/* ---- toasts (shared/_flash) ---- */
.toast-stack {
  position: fixed;
  inset-inline: 0;

  /* clears the compose FAB's zone so a toast never covers it */
  bottom: calc(var(--space-6) + var(--fab-size) + var(--space-3));
  z-index: 200;
  display: grid;
  justify-items: center;
  gap: var(--space-2);
  padding-inline: var(--space-4);
  pointer-events: none;
}

.toast {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  max-width: 100%;
  background: var(--surface);
  border: var(--hairline);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-pop);
  padding: var(--space-3) var(--space-3) var(--space-3) var(--space-4);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
}

.toast-notice > i { color: var(--ok); }
.toast-alert > i { color: var(--danger); }

@media (prefers-reduced-motion: no-preference) {
  .toast { animation: toast-in 0.2s ease-out; }

  @keyframes toast-in {
    from { opacity: 0; transform: translateY(var(--space-2)); }
  }
}

/* ---- empty states ---- */
.empty-state {
  text-align: center;
  padding: var(--space-8) var(--space-5);
  color: var(--ink-muted);
}

.empty-state-icon {
  font-size: var(--text-2xl);
  color: var(--ink-faint);
  background: var(--surface-2);
  width: var(--space-8);
  height: var(--space-8);
  display: inline-grid;
  place-items: center;
  border-radius: 50%;
  margin-bottom: var(--space-4);
}

.empty-state h2 {
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  color: var(--ink);
  margin-bottom: var(--space-2);
}

.empty-state p { max-width: var(--measure-narrow); margin: 0 auto; }

/* ---- small atoms ---- */
.net {
  display: inline-grid;
  place-items: center;
  width: var(--icon-box);
  height: var(--icon-box);
  flex: none;
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  background: var(--surface-2);
  color: var(--ink-muted);
  border: var(--hairline);
}

.avatar {
  width: var(--icon-box-lg);
  height: var(--icon-box-lg);
  border-radius: var(--radius-sm);
  flex: none;
  display: inline-grid;
  place-items: center;
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  color: var(--accent-ink);
  background: var(--accent);
}

.checkdot {
  width: var(--icon-box-sm);
  height: var(--icon-box-sm);
  border-radius: 50%;
  flex: none;
  display: inline-grid;
  place-items: center;
  font-size: var(--text-xs);
  border: var(--hairline-strong);
  color: transparent;
}

.checkdot.done { background: var(--prism); border: 0; color: var(--accent-ink); }
.thumb { border-radius: var(--radius-md); border: var(--hairline); position: relative; overflow: hidden; }
.thumb i { position: absolute; inset: 0; opacity: 0.85; }

/* ---- touch input ---- */

/* Coarse pointers get full-size targets (--tap-min) without changing
   the pointer look: same layout at every width (ADR 0003), only the
   hit boxes grow. */
@media (pointer: coarse) {
  :is(.btn-prism, .btn-quiet, .btn-ink, .iconbtn) { min-height: var(--tap-min); }
  .iconbtn { min-width: var(--tap-min); }

  /* the emoji trigger's reserved strip grows with its tap box */
  .control-affix .textarea { padding-bottom: calc(var(--tap-min) + var(--space-2)); }
  .tabs a { padding-block: var(--space-3); }
  .check-row { min-height: var(--tap-min); }

  /* the segmented view switch sits below the kit's tap floor; grow the hit
     box, the pill's look unchanged (the whole segment is the target) */
  .seg a { min-height: var(--tap-min); display: inline-flex; align-items: center; }

  /* input.toggle stays 38x22 by design: its row label (.toggle-row)
     carries the touch target. */
}

/* ---- reduced motion ---- */
@media (prefers-reduced-motion: reduce) {
  *,
  ::before,
  ::after {
    transition: none !important;
    animation: none !important;
  }
}
