/* DraftsReady app styles. Mobile-first. Tokens copied from docs/design-system.md. */

:root {
  /* Light is the base; the dark block at the foot of this sheet swaps the tokens when
     <html data-theme="dark">. Declaring "light dark" here instead would let the browser paint
     scrollbars and form controls dark while our own tokens stayed light. */
  color-scheme: light;
  /* Palette C, "Бумага", picked by Max on 2026-09-20 from
     docs/initiatives/meta-app-review/turquoise-and-logo.html: the warm paper ground stays, the
     green is replaced by the turquoise off his DR logo. The page is warm off-white and a post
     card is pure white - a white card on a white page is not a card, which is what made one
     comment run into the next (Max, 2026-09-18). */
  --bg: #f7f5ef;
  --bg-sunken: #f0ece2;
  --bg-raised: #ffffff;
  --border: #e6e0d4;
  --border-strong: #cfc8b8;
  --divider: #e6e0d4; /* between two comments in one card: has to be seen, not guessed */
  --text: #1b2a33;
  --text-muted: #6a6a5e;
  --text-faint: #8d8d7f;
  --brand: #159aa8; /* turquoise - primary action, logo, focus */
  --brand-ink: #0c757f; /* brand text on light, hover */
  --brand-wash: #dff0f2;
  /* White, not a tint. The draft is the one box a person types in and Max asked for it to stop
     reading as a greyed-out area (2026-09-20: "попробуй сделать поле с черновиком полностью
     белым, а не серым"). On a white card it is its border, not its fill, that marks it out. */
  --field: #ffffff;
  --field-border: #c7ddda;
  --accent: #4a7c3f; /* success, "sent" - a green that is clearly not the brand teal */
  --accent-wash: #eaf1e5;
  --warn: #94501d;
  --warn-wash: #fbf0e2;
  --danger: #9d321e;
  --danger-wash: #fbeae5;

  --radius-control: 10px;
  --radius-card: 14px;
  --radius-pill: 999px;
  --shadow-card: 0 1px 2px rgba(27, 42, 51, 0.06), 0 8px 24px rgba(27, 42, 51, 0.06);
  --shadow-soft: 0 1px 2px rgba(27, 42, 51, 0.05);
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto, sans-serif;
  /* One gutter, AI Drafts' own value (Max, 2026-09-21: "убери лишние [margins], чтобы больше
     влазило"). `.post-group` used to add its own 14px of horizontal padding on top of this one,
     so the list paid the gutter twice - 28px a side on a 390px phone for 334px of usable width,
     against AI Drafts' single 12px gutter and 366px. Fixed by dropping `.post-group`'s
     horizontal padding entirely; see its rule below. */
  --gutter: 12px;
  --topbar-h: 50px;
  /* Tap targets stay 44px where a mistake costs something (Send), 38px elsewhere, which is
     what keeps a screenful of comments a screenful rather than a scroll. */
  --tap: 40px;
  --tap-sm: 34px;
}

* {
  box-sizing: border-box;
}

/* Every control, not only .btn. The account menu's backdrop is a full-screen button, so the
   default iOS tap highlight painted a grey flash across the entire screen on open and on
   dismiss - which read as the screen blinking (Max, 2026-09-18). */
button,
a,
summary,
[role="button"] {
  -webkit-tap-highlight-color: transparent;
}
/* Hover belongs to a mouse. On a touch screen it sticks after the tap and looks like a second
   flash when the element is still there underneath a closing menu. */
@media (hover: none) {
  .account-trigger:hover,
  .menu button:hover {
    background: transparent;
  }
}

html,
body {
  margin: 0;
  padding: 0;
}

html {
  -webkit-text-size-adjust: 100%;
  /* Inherited by everything, including a div with a click handler: iOS paints its tap
     highlight on those too, and the account menu's full-screen backdrop is exactly that. */
  -webkit-tap-highlight-color: transparent;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.5;
  overflow-x: hidden;
}

#root {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

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

button {
  font-family: inherit;
  color: inherit;
}

a {
  color: var(--brand-ink);
}

/* ---------- type scale ---------- */

.display {
  font-size: 40px;
  line-height: 1.05;
  font-weight: 700;
}
.h1 {
  font-size: 24px;
  line-height: 1.15;
  font-weight: 700;
}
.h2 {
  font-size: 19px;
  line-height: 1.25;
  font-weight: 650;
}
.small {
  font-size: 14px;
  line-height: 1.45;
}
.micro {
  font-size: 12px;
  line-height: 1.4;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.muted {
  color: var(--text-muted);
}
.faint {
  color: var(--text-faint);
}
.prose {
  max-width: 68ch;
}

/* ---------- shell ---------- */

.shell {
  width: 100%;
  max-width: 720px;
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.main {
  flex: 1 1 auto;
  padding-bottom: 88px;
}

/* ---------- pull to refresh ---------- */

/* Anchors the indicator to the top of the list it refreshes, not to the viewport - it has to
   stay above the first row wherever that row scrolls to, not float over the topbar. */
.pull-container {
  position: relative;
}
/* The list itself moves down with the finger, which is what opens the empty strip the spinner
   turns in. Without it the spinner had nowhere to be but on top of the first card's text. */
.pull-content {
  transition: transform 200ms ease-out;
}
.pull-content.is-dragging {
  transition: none;
}
@media (prefers-reduced-motion: reduce) {
  .pull-content {
    transition: none;
  }
}
.pull-indicator {
  position: absolute;
  left: 50%;
  top: 0;
  /* translate() itself is set inline per pull distance; this only fixes the -50% centering
     transform-origin math the inline style below builds on. */
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--brand-ink);
  pointer-events: none;
  z-index: 1;
  transition: transform 200ms ease-out, opacity 200ms ease-out;
}
/* No transition while the finger is down - the indicator has to sit exactly where the touch is,
   not ease toward it a beat later. */
.pull-indicator.is-dragging {
  transition: none;
}
@media (prefers-reduced-motion: reduce) {
  .pull-indicator {
    transition: none;
  }
}
.pull-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
}

.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  /* The manifest asks for a standalone window and the viewport is viewport-fit=cover, so on a
     phone the page starts BEHIND the status bar. Without this the whole 50px of chrome - the
     wordmark and the account button - is painted under the clock and reads as missing. */
  padding-top: env(safe-area-inset-top);
  background: var(--bg-raised);
  border-bottom: 1px solid var(--border);
}

.topbar-inner {
  height: var(--topbar-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.wordmark {
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.01em;
  background: none;
  border: 0;
  padding: 0;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  cursor: pointer;
}
.wordmark .mark {
  color: var(--brand);
}
.wordmark .brand-mark {
  flex: none;
  display: block;
  margin-right: 7px;
  border-radius: 5px; /* the tile's own corner radius, at this size */
}

/* ---------- buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: var(--tap-sm);
  min-width: var(--tap-sm);
  padding: 0 12px;
  border-radius: var(--radius-control);
  border: 1px solid var(--border-strong);
  background: var(--bg-raised);
  color: var(--text);
  font-size: 14px;
  font-weight: 780;
  line-height: 1;
  cursor: pointer;
  transition: background 120ms ease-out, border-color 120ms ease-out, color 120ms ease-out;
  -webkit-tap-highlight-color: transparent;
  text-decoration: none;
}
.btn svg {
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
}
/* The sparkles say "a model wrote this". They carry the brand colour on a plain button and
   inherit the ink on a filled one, so they never disappear into their own background. */
.btn:not(.btn-primary):not(:disabled) svg {
  color: var(--brand);
}
.btn:hover:not(:disabled) {
  background: var(--bg-sunken);
}
.btn:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}
.btn:focus-visible,
.wordmark:focus-visible,
a:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

.btn-primary {
  background: var(--brand);
  border-color: var(--brand);
  color: #fff;
  /* Send is the one press that cannot be taken back, so it keeps the full target. */
  min-height: var(--tap);
  padding: 0 18px;
}
.btn-primary:hover:not(:disabled) {
  background: var(--brand-ink);
  border-color: var(--brand-ink);
}

.btn-ghost {
  border-color: transparent;
  background: transparent;
  color: var(--text-muted);
  padding: 0 10px;
}
.btn-ghost:hover:not(:disabled) {
  background: var(--bg-sunken);
  color: var(--text);
}

.btn-danger {
  color: var(--danger);
  border-color: transparent;
  background: transparent;
}
.btn-danger:hover:not(:disabled) {
  background: var(--danger-wash);
}

.btn-sm {
  min-height: var(--tap-sm);
  padding: 0 11px;
  font-size: 13px;
  font-weight: 780;
}
/* Skip, Rewrite and Send are one row of three - they get one height and one type size. Send
   stands out by being the only filled one, not by being bigger (Max, 2026-09-18). */
.row-actions .btn {
  min-height: var(--tap-sm);
  padding: 0 11px;
  font-size: 13px;
  font-weight: 780;
}
.row-actions .btn-primary {
  padding: 0 16px;
}

.btn-block {
  width: 100%;
}

/* ---------- spinner ---------- */

.spinner {
  display: inline-block;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: rr-spin 0.7s linear infinite;
  vertical-align: -2px;
}
@keyframes rr-spin {
  to {
    transform: rotate(360deg);
  }
}
@media (prefers-reduced-motion: reduce) {
  .spinner {
    animation-duration: 2s;
  }
}

/* ---------- top bar account ---------- */

.account {
  position: relative;
}
.account-trigger {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  padding: 4px 8px;
  border: 0;
  background: transparent;
  border-radius: var(--radius-pill);
  cursor: pointer;
  font-size: 14px;
  font-weight: 600;
  max-width: 60vw;
}
.account-trigger:hover {
  background: var(--bg-sunken);
}
.account-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--brand-wash);
  color: var(--brand-ink);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  font-weight: 700;
  flex: 0 0 auto;
  overflow: hidden;
}
.avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  min-width: 200px;
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: 6px;
  z-index: 30;
}
.menu button {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: 44px;
  padding: 0 12px;
  border: 0;
  border-radius: var(--radius-control);
  background: transparent;
  text-align: left;
  font-size: 16px;
  cursor: pointer;
}
.menu button:hover {
  background: var(--bg-sunken);
}
.menu .menu-danger {
  color: var(--danger);
}
.menu-sep {
  height: 1px;
  background: var(--border);
  margin: 6px 4px;
}
.menu-backdrop {
  position: fixed;
  inset: 0;
  z-index: 25;
  background: transparent;
  border: 0;
  padding: 0;
  cursor: default;
}
.menu-policies {
  display: flex;
  gap: 8px;
  padding: 8px 12px 4px;
  font-size: 11px;
}
.menu-policies a {
  color: var(--text-faint);
}

/* ---------- quota strip ---------- */

.quota-strip {
  background: var(--warn-wash);
  color: var(--warn);
  border-bottom: 1px solid var(--border);
}
.quota-strip-calm {
  /* "Today's drafts are written, tomorrow carries on" is news, not a warning: in the warn
     colours it reads as a fault, and the person goes looking for what broke. */
  background: var(--bg-sunken);
  color: var(--text-muted);
}
.quota-strip .shell {
  padding-top: 8px;
  padding-bottom: 8px;
  font-size: 14px;
  font-weight: 600;
}

.sample-banner {
  position: sticky;
  top: calc(var(--topbar-h) + env(safe-area-inset-top));
  z-index: 19;
  background: var(--warn-wash);
  color: var(--warn);
  border-bottom: 1px solid var(--warn);
}
.sample-banner .shell {
  padding-top: 8px;
  padding-bottom: 8px;
  font-size: 13px;
  font-weight: 700;
}
.sample-banner-inner {
  display: flex;
  align-items: baseline;
  gap: 12px;
}
.sample-banner-inner > span {
  flex: 1 1 auto;
  min-width: 0;
}
.sample-banner-back {
  flex: none;
  color: inherit;
  text-decoration: underline;
  white-space: nowrap;
}

/* ---------- a newer build is up ---------- */

/* Sits where the sample banner sits, and only one of them can ever be on screen: the sample
   banner belongs to /demo and to test mode, neither of which is a deployment anybody reloads. */
.update-strip {
  position: sticky;
  top: calc(var(--topbar-h) + env(safe-area-inset-top));
  z-index: 19;
  background: var(--brand-wash);
  color: var(--text);
  border-bottom: 1px solid var(--brand);
}
.update-strip .shell {
  padding-top: 8px;
  padding-bottom: 8px;
  font-size: 13px;
}
.update-strip-inner {
  display: flex;
  align-items: center;
  gap: 12px;
}
.update-strip-inner > span {
  flex: 1 1 auto;
  min-width: 0;
}
.update-strip-button {
  flex: none;
  border: 1px solid var(--brand);
  background: transparent;
  color: var(--brand-ink);
  border-radius: 999px;
  padding: 4px 14px;
  font: inherit;
  font-weight: 700;
  cursor: pointer;
}

/* ---------- demo context ---------- */

.demo-intro {
  padding-top: 20px;
  padding-bottom: 8px;
  color: var(--text-muted);
}
.demo-intro .h2 {
  color: var(--text);
  margin-bottom: 8px;
}

/* ---------- voice panel (pills) ---------- */

.voice-panel {
  margin: 12px 0 20px;
  padding: 14px;
  /* A card like every other card. The draft box is the only filled surface in the product. */
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
}
.voice-panel-title {
  color: var(--text-muted);
  margin-bottom: 10px;
}
.pills {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.pill {
  display: inline-flex;
  align-items: flex-start;
  min-height: 30px;
  padding: 4px 12px;
  border-radius: var(--radius-pill);
  background: var(--bg-raised);
  border: 1px solid var(--border);
  font-size: 14px;
  max-width: 100%;
  overflow-wrap: anywhere;
}
.pill-topic {
  flex: none; /* the topic label is one word and must never break mid-word */
  white-space: nowrap;
  padding-top: 2px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-faint);
  margin-right: 6px;
}

/* ---------- toolbar above the list ---------- */

.toolbar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 0 2px;
  flex-wrap: wrap;
}
.toolbar .grow {
  flex: 1 1 auto;
}
.toolbar-link {
  border: 0;
  background: none;
  padding: 4px 0;
  color: var(--brand-ink);
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  min-height: 28px;
}
.toolbar-link:hover {
  text-decoration: underline;
}
.count {
  color: var(--text-muted);
  font-size: 14px;
}

/* A segmented control, used for the archive filter. */
.seg {
  display: inline-flex;
  padding: 2px;
  gap: 2px;
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
}
.seg button {
  border: 0;
  background: transparent;
  border-radius: var(--radius-pill);
  min-height: 30px;
  padding: 0 12px;
  font-size: 14px;
  font-weight: 600;
  color: var(--text-muted);
  cursor: pointer;
}
/* Inside a settings row the control starts at the left edge of the row, under its label. */
.seg-theme {
  align-self: flex-start;
  margin-top: 6px;
}
.seg button[aria-pressed="true"] {
  background: var(--bg-raised);
  color: var(--text);
  box-shadow: var(--shadow-soft);
}

/* ---------- the palette playground (Settings -> Appearance) ---------- */
/* Sits under AppearanceControl's own segmented control, marked as an experiment rather than a
   fourth peer next to System/Бумага/Dark/AI Drafts - it is a device preference layered on top
   of whichever of those is selected, not another one of them (Max, 2026-09-21). */
.palette-lab {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--divider);
}
.palette-lab-label {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin-bottom: 8px;
}
.palette-lab-inputs {
  display: flex;
  gap: 16px;
  margin-bottom: 10px;
}
.palette-lab-field {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 700;
  color: var(--text);
}
/* The native picker's own swatch, just resized and framed to sit level with its label - every
   other visual comes from the OS, which is the point of using `type="color"` at all. */
.palette-lab-field input[type="color"] {
  -webkit-appearance: none;
  appearance: none;
  width: 34px;
  height: 34px;
  padding: 0;
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  background: none;
  cursor: pointer;
}
.palette-lab-field input[type="color"]::-webkit-color-swatch-wrapper {
  padding: 0;
  border-radius: 50%;
}
.palette-lab-field input[type="color"]::-webkit-color-swatch {
  border: 0;
  border-radius: 50%;
}
.palette-lab-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 10px;
}
/* A two-colour dot ahead of the name - accent over background, the same two things tapping the
   chip sets - so comparing chips means reading dots, not names, which is what "compare fast"
   asked for (Max, 2026-09-21). */
.palette-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--bg-raised);
  padding: 4px 10px 4px 4px;
  font-size: 13px;
  font-weight: 650;
  color: var(--text);
  cursor: pointer;
  min-height: 30px;
}
.palette-chip:hover {
  background: var(--bg-sunken);
}
.palette-chip-dot {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--chip-accent) 50%, var(--chip-bg) 50%);
  border: 1px solid var(--border);
}
.palette-lab-reset {
  min-height: var(--tap-sm);
}

/* ---------- inbox list ---------- */

/* No card any more: the page background now runs edge to edge under every row, the way AI
   Drafts does it, and the draft box becomes the only white surface on the screen
   (Max, 2026-09-20/21: "фон заливает полностью весь экран, только кроме самого окошка с
   черновиком"). Rows separate by spacing and a hairline instead of a box - the mockup's own
   values. `.flat-group` (the archive's list) never had a CSS block of its own, so it goes flat
   along with everything else here rather than needing an exception.
   No horizontal padding here (2026-09-21): `.shell` already pays the one gutter, and this row
   used to add a second 14px on top of it - the double gutter Max noticed against AI Drafts.
   Vertical padding stays; it is spacing between rows, not a side margin. */
.post-group {
  padding: 12px 0 2px;
  border-bottom: 1px solid var(--divider);
}
.post-head {
  display: flex;
  gap: 10px;
  align-items: center;
  width: 100%;
  border: 0;
  background: none;
  padding: 0;
  text-align: left;
  cursor: pointer;
  color: inherit;
  font: inherit;
  min-height: var(--tap-sm);
}
.thumb {
  width: 38px;
  height: 38px;
  border-radius: 8px;
  object-fit: cover;
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  flex: 0 0 auto;
}
.thumb-fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-faint);
  font-size: 11px;
}
.caption {
  flex: 1 1 auto;
  /* AI Drafts scale (Max, 2026-09-20/21): the post caption moves from 13px to 14px along with
     the handle and comment text it sits above. */
  font-size: 14px;
  font-weight: 800;
  color: var(--text);
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow-wrap: anywhere;
}
.post-meta {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--text-muted);
  font-size: 12px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}
.chev {
  display: inline-block;
  transition: transform 140ms ease-out;
}
.post-group.collapsed .chev {
  transform: rotate(-90deg);
}

/* The bottom padding is what stands between the last button in a row and the divider under
   it. At 4px the button looked stuck to the line (Max, 2026-09-19); 10px is still dense and
   the divider reads as a separator rather than an underline. */
.row {
  padding: 11px 0 10px;
}
/* Full-bleed, not inset: a hairline that stops at the text column reads as decoration, one
   that runs the whole width of the card reads as "a new comment starts here". This used to
   escape `.post-group`'s 14px padding with a matching negative margin and add it back as its
   own padding; now that `.post-group` carries no horizontal padding at all (2026-09-21,
   double-gutter fix), the row already spans the full width and needs neither. */
.row + .row {
  padding: 12px 0 10px;
  border-top: 1px solid var(--divider);
}

/* ---------- the comment, then the reply under it ---------- */

/* The commenter's own words sit on the card itself, at full size and full contrast: they are
   what the person is reading, and a grey box around them only made them look secondary. */
.comment-card {
  padding: 0;
}
/* An avatar column, the way AI Drafts has one: a left anchor gives every comment a visible
   start, which a run of plain paragraphs never has. Instagram gives us no avatar for a
   commenter, so it is their initial - the point is the column, not the picture. */
.comment-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
}
.comment-avatar {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--brand-wash);
  color: var(--brand-ink);
  font-size: 12px;
  font-weight: 850;
  text-transform: uppercase;
}
/* "@username wrote · 2h ago" as one line: the handle, the link to the comment, and the time all
   flow together rather than the time being pinned to the row's far edge, which is how a single
   sentence about who said what reads (Max, 2026-09-20). */
.comment-head-meta {
  min-width: 0;
  flex: 1 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The comment lines up with the handle rather than with the avatar. The draft does NOT: it is
   the one box people type in, and 36px of indent is 36px taken off it on a phone. */
.comment-card .comment-text {
  margin-left: 36px;
}
/* AI Drafts scale (Max, 2026-09-20/21): small and heavy, one size up from before - 14px, not
   13px, and .meta-link matches it so "wrote" does not look smaller than the name beside it. */
.handle {
  font-size: 14px;
  font-weight: 800;
  overflow-wrap: anywhere;
}
/* The small link on "wrote" to the comment itself. Quiet on purpose - a tap target here is a
   detour, not the point of the row - so it reads as text with a link in it, not a control. */
.meta-link {
  color: var(--brand-ink);
  font-size: 14px;
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.time {
  color: var(--text-muted);
  font-size: 11px;
  font-weight: 700;
}
.comment-text {
  margin: 0;
  font-size: 14px;
  font-weight: 600;
  line-height: 19px;
  color: var(--text);
  overflow-wrap: anywhere;
  white-space: pre-wrap;
}
.tag-pill {
  display: inline-flex;
  align-items: center;
  padding: 1px 7px;
  border-radius: var(--radius-pill);
  background: var(--brand-wash);
  color: var(--brand-ink);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

/* One banner at the top of the row says what the whole row is, before any of it is read
   (Max, 2026-09-18). Everything the app says about itself is teal; everything a person
   wrote is ordinary ink. That one rule is what separates the two without a single box. */
.row-banner {
  display: block;
  margin: 0 0 7px;
  color: var(--brand-ink);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* The thread this comment sits in. Every line says out loud whose it is - who started the
   thread, what we already answered there - because "a quote with a left border" left people
   guessing which of the three voices they were reading. */
.thread-root {
  border-left: 2px solid var(--border-strong);
  padding: 2px 0 2px 10px;
  margin: 0 0 9px 36px;
}
@media (max-width: 359px) {
  .thread-root {
    margin-left: 0;
  }
}
.thread-line + .thread-line {
  margin-top: 6px;
}
/* Our own reply inside the quote steps in once more and carries the brand rule, so "what we
   already said here" is never mistaken for another stranger's comment. */
.thread-line.is-mine {
  margin-left: 10px;
  padding-left: 10px;
  border-left: 2px solid var(--brand);
}
.thread-role {
  display: block;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--brand-ink);
}
.thread-role .handle {
  text-transform: none;
  letter-spacing: 0;
  font-size: 11px;
  font-weight: 800;
  color: inherit;
}
.thread-root .handle {
  font-size: 12px;
  color: var(--text);
}
.thread-text {
  margin: 2px 0 0;
  /* Deliberately one step quieter than .comment-text (14/19, full ink): the quote is context,
     the comment under it is the thing being answered - keeping them apart is what makes a
     thread with three voices in it readable (Max, 2026-09-21: "не понятно, как ветки будут
     выглядеть"). */
  font-size: 13px;
  font-weight: 600;
  line-height: 18px;
  color: var(--text-muted);
  overflow-wrap: anywhere;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* What kind of comment this row is, when it is not simply a comment under a post. It replaced
   the "tagged you" pill: one loud line that points down at the comment says the same thing
   without a second marker competing with it. */
.comment-role {
  display: block;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--brand-ink);
  margin: 0 0 3px;
}

/* The draft sits under the comment in its own indented row - see .draft-row below - so this
   wrapper only needs to give that row (and the actions under it) room from the comment above. */
.reply-body {
  min-width: 0;
  margin-top: 10px;
}
@media (max-width: 359px) {
  .comment-card .comment-text {
    margin-left: 0;
  }
}

/* ---------- draft box ---------- */

/* White, not a tint (Max, 2026-09-20). What marks the draft out now is its border - the one
   turquoise-tinted hairline on the screen - and the arrow leading into it from the comment
   above, not a grey fill. */
/* The gutter and the arrow: a flex row carrying the same 36px indent `.comment-card
   .comment-text` already uses, so the arrow and the box together start where the comment above
   them starts (Max, 2026-09-21, on the mockup's narrower box: "чтобы поле с черновиком было
   немного уже, как и в ai drafts"). `.draft-block` is the flex:1 second child, so it fills
   whatever the 18px arrow column and the 6px gap leave of the row's own width. */
.draft-row {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  margin-left: 36px;
}
/* Same breakpoint as .comment-card .comment-text above: below 360px the 36px indent that lines
   the row up with the comment drops to 0, but the arrow gutter itself stays (its width is set
   on .reply-mark, not here). This override has to come after the base rule above - equal
   specificity means source order decides, and a media block placed earlier in the file would
   lose to a later plain rule. */
@media (max-width: 359px) {
  .draft-row {
    margin-left: 0;
  }
}
.draft-block {
  position: relative;
  flex: 1;
  min-width: 0;
  background: var(--field);
  border: 1px solid var(--field-border);
  /* AI Drafts' own corner radius, not --radius-control (Max, 2026-09-21, AI Drafts parity). */
  border-radius: 7px;
  padding: 7px 9px;
}
/* The caption rides ON the top border rather than taking a line inside the box: a whole line
   for one nine-pixel word was too much of the box to give away (Max, 2026-09-20: "сделай слово
   draft поменьше на этом поле и повыше, чтобы оно не занимало пространство поля"). Its
   background is whatever the box sits on, not the field's, because it straddles the two. That
   used to be --bg-raised unconditionally because a draft box only ever appeared inside a
   .post-group card; the card is gone everywhere now (2026-09-21), including the archive's own
   list, so the label always sits on --bg and a white sliver here would be exactly the mismatch
   this rule exists to avoid. */
.draft-label {
  position: absolute;
  top: -6px;
  left: 8px;
  display: flex;
  align-items: center;
  gap: 3px;
  padding: 0 4px;
  background: var(--bg);
  font-size: 9px;
  line-height: 12px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--brand-ink);
}
/* The reply arrow is back at the size it had before it was switched off, in its own gutter
   column beside the box rather than folded onto the caption line (Max, 2026-09-20 asked for the
   arrow back; 2026-09-21: "стрелку надо сделать побольше, как ранее у нас была" - the caption-
   line version was too small). 18/15/1.6 is verbatim from commit 375c15f's .reply-mark. */
.reply-mark {
  flex: 0 0 auto;
  width: 18px;
  font-size: 15px;
  line-height: 1.6;
  text-align: center;
  color: var(--text-faint);
  user-select: none;
}
/* 14px/20px at weight 560 - the same type as the AI Drafts draft line, measured off
   `.draft-line textarea` in its theme.css (Max, 2026-09-18: "сделай также"). It is under
   16px, which iOS Safari would normally answer by zooming; the app viewport carries
   `user-scalable=no` for exactly this, the way AI Drafts does. */
.draft-box {
  display: block;
  width: 100%;
  min-height: 40px;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--text);
  font-family: inherit;
  font-size: 14px;
  font-weight: 560;
  line-height: 20px;
  resize: none;
  overflow: hidden;
}
.draft-box:focus {
  outline: none;
}
.draft-block:focus-within {
  border-color: var(--brand);
}
/* Placeholder text is a sample, not content. Italic says so before the words are read. */
.draft-box::placeholder,
.input::placeholder,
.free-text-box::placeholder {
  color: var(--text-faint);
  font-style: italic;
  font-weight: 500;
}
.draft-box:disabled {
  color: var(--text-muted);
}
.field-label {
  display: block;
  font-size: 14px;
  font-weight: 600;
  margin-bottom: 5px;
}

/* ---------- row actions ---------- */

/* Right-hand side, always: on a phone that is where the thumb is. Left to right the order
   is Skip, Rewrite, Send, so the destructive one is furthest from the one that publishes. */
.row-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: center;
  gap: 6px;
  margin-top: 6px;
}
.row-actions .grow {
  flex: 1 1 auto;
}
.row-error {
  margin-top: 8px;
}

.rewrite-panel {
  margin-top: 8px;
  padding: 10px;
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
}
/* Four presets, two by two. A wrapping flex row put three on one line and dropped the fourth
   onto its own, which read as a mistake at every phone width. */
.preset-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
  margin-bottom: 8px;
}
.preset-grid .btn {
  justify-content: flex-start;
  gap: 6px;
  padding: 0 8px;
  /* Four labels of different lengths in a 2x2 grid: the longest one wrapping made its cell
     taller than its neighbour and the block stopped reading as a set. */
  font-size: 12px;
  white-space: nowrap;
}
/* A one-line input could not show back what had just been dictated into it - a long
   instruction scrolled past its own field (Max, 2026-09-18). It is a textarea now: it starts
   at one line, grows with the text, and stops growing at four lines, after which it scrolls
   rather than pushing Send off the screen. */
.free-text-row {
  display: flex;
  gap: 6px;
  align-items: flex-end;
}
.free-text-box {
  min-width: 0;
  flex: 1 1 auto;
  display: block;
  min-height: var(--tap-sm);
  max-height: 88px;
  padding: 7px 9px;
  border: 1px solid var(--field-border);
  border-radius: var(--radius-control);
  background: var(--field);
  color: var(--text);
  font-family: inherit;
  font-size: 14px;
  font-weight: 560;
  line-height: 20px;
  resize: none;
  overflow-y: auto;
}
.free-text-box:focus {
  outline: none;
  border-color: var(--brand);
}
.free-text-icon {
  flex: 0 0 auto;
  display: inline-flex;
  align-self: center;
  color: var(--text-faint);
}
.free-text-icon svg {
  width: 16px;
  height: 16px;
}
/* The same "say it in your own words" field takes the voice, so dictation lives where the
   instruction is typed instead of on a screen of its own. */
.free-text-row .mic-btn svg {
  width: 15px;
  height: 15px;
}
/* The microphone and Apply sit on their own line under the field, both pinned to the right
   edge in that order, so the pair reads as one control under the thumb instead of two things
   at opposite ends of the panel (Max, 2026-09-19). */
.free-text-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
  margin-top: 6px;
}
.free-text-actions .mic-btn {
  width: var(--tap-sm);
  height: var(--tap-sm);
}
.free-text-apply {
  flex: 0 0 auto;
  gap: 5px;
}

/* ---------- a field the person is meant to fill ---------- */

/* Tinted and outlined in the brand colour, so "type here" is visible without a word of
   instruction - a plain white box among white boxes was not (Max, 2026-09-18). */
/* Doubled class: .input is declared further down the sheet, so a single .input-primed lost
   to it and the field came out the same colour as every other box. */
.input-primed,
.input.input-primed,
textarea.input-primed {
  background: var(--field);
  border-color: var(--field-border);
}
.input-primed:focus,
.input.input-primed:focus,
textarea.input-primed:focus {
  border-color: var(--brand);
}

/* ---------- a skip you can take back ---------- */

/* Skip archives the row at once, so a reload never resurrects it. The strip that replaces the
   row is the undo window, not the pending action. */
.skip-undo {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 40px;
  margin: 6px 0 2px;
  padding: 0 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  background: var(--bg-sunken);
}
.skip-undo .what {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 13px;
  font-weight: 700;
  color: var(--text-muted);
}
.skip-undo .grow {
  flex: 1 1 auto;
}
.skip-undo .left {
  flex: 0 0 auto;
  min-width: 18px;
  text-align: right;
  font-size: 12px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  color: var(--text-faint);
}

/* ---------- a sent row, collapsed ---------- */

/* Same layout idea as .skip-undo - a row that finished its job shrinks to one line - but its own
   class: a sent row has nothing to undo, and giving it the skip strip's class would mean editing
   one strip's rules for two unrelated states (Max, 2026-09-20). */
.sent-collapsed {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 40px;
  margin: 6px 0 2px;
  padding: 0 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  background: var(--bg-sunken);
}
.sent-collapsed .what {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 13px;
  font-weight: 700;
  color: var(--text-muted);
}
.sent-collapsed .grow {
  flex: 1 1 auto;
}
/* A quiet status line, not a button - Max: "сделай это send, view on instagram шрифт меньшего
   размера, типа как и остальной шрифт" (2026-09-20). Sized like the row's own meta text
   (.time / .handle), not the 14px pill it used to be. */
.sent-collapsed-link {
  flex: 0 0 auto;
  color: var(--brand-ink);
  font-size: 13px;
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.input {
  width: 100%;
  min-width: 0; /* let a flex row shrink the field instead of overflowing at 320px */
  min-height: var(--tap-sm);
  padding: 7px 10px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-control);
  background: var(--bg-raised);
  color: var(--text);
  font-family: inherit;
  font-size: 16px;
}
.input::placeholder {
  color: var(--text-faint);
}

/* ---------- states ---------- */

.note {
  padding: 10px 12px;
  border-radius: var(--radius-control);
  border: 1px solid var(--border);
  background: var(--bg-sunken);
  font-size: 14px;
}
.error-note {
  padding: 10px 12px;
  border-radius: var(--radius-control);
  border: 1px solid var(--danger);
  background: var(--danger-wash);
  color: var(--danger);
  font-size: 14px;
  overflow-wrap: anywhere;
}
.uncertain {
  padding: 10px 12px;
  border-radius: var(--radius-control);
  border: 1px solid var(--warn);
  background: var(--warn-wash);
  color: var(--warn);
  font-size: 14px;
}
.sent {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
}
/* Sized like ordinary row text (.time / .handle), not a pill - it is a status word, never a
   control (Max, 2026-09-20: "сделай это send, view on instagram шрифт меньшего размера"). */
.sent-badge {
  display: inline-flex;
  align-items: center;
  color: var(--accent);
  font-size: 13px;
  font-weight: 700;
}
.skip-pill {
  display: inline-flex;
  align-items: center;
  min-height: 30px;
  padding: 4px 12px;
  border-radius: var(--radius-pill);
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  color: var(--text-muted);
  font-size: 14px;
  margin-bottom: 10px;
}
/* Only the demo's inline "View on Instagram" still uses this (account mode's sent row collapses
   instead - see .sent-collapsed-link). Same quiet, ordinary-text size as the rest of the row,
   not the 44px tap-target button this used to be (Max, 2026-09-20). */
.link {
  border: 0;
  background: none;
  padding: 0;
  color: var(--brand-ink);
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
}
.link:hover {
  text-decoration: underline;
}

/* ---------- empty state ---------- */

.empty {
  padding: 40px 0;
  text-align: center;
}
.empty .h2 {
  margin-bottom: 8px;
}
.empty p {
  color: var(--text-muted);
  margin-bottom: 16px;
}

/* ---------- sticky demo CTA ---------- */

.sticky-cta {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 20;
  background: var(--bg-raised);
  border-top: 1px solid var(--border);
  box-shadow: var(--shadow-card);
  padding-top: 12px;
  padding-bottom: max(16px, env(safe-area-inset-bottom));
}
.sticky-cta-inner {
  display: flex;
  gap: 10px;
}
.sticky-cta .btn-primary {
  flex: 1 1 auto;
}

/* Was .rewrites-left, back when this line was a countdown. It holds the feedback link now. */
.demo-foot {
  text-align: center;
  color: var(--text-muted);
  padding: 8px 0 4px;
}

/* ---------- voice screen ---------- */

.screen {
  padding-top: 14px;
}
.welcome-screen {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;
  padding-top: 48px;
  min-height: calc(100vh - var(--topbar-h));
}
.welcome-screen .mark {
  color: var(--brand);
}
.welcome-demo-link {
  font-weight: 600;
}
.welcome-policies {
  margin-top: auto;
  padding-bottom: 16px;
  color: var(--text-faint);
  font-size: 12px;
}
.screen-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 4px;
}
/* A settings screen is where a person decides whether this app is worth the trouble, so it
   has to be skimmable, not complete. Cards are spaced and titled; everything that does not
   need the owner's attention is folded away under Advanced (Max, 2026-09-18). */
.section {
  margin-top: 14px;
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-soft);
  padding: 12px 12px 13px;
}
.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 6px;
}
.section-title {
  font-size: 15px;
  font-weight: 800;
  letter-spacing: -0.01em;
}

/* ---------- a folded list ---------- */

/* <details>, so it costs no state and keeps working with a keyboard. The whole summary row is
   the target, the count sits on the right, the chevron turns. */
.fold {
  margin-top: 14px;
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-soft);
}
.fold > summary {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: var(--tap);
  padding: 4px 12px;
  list-style: none;
  cursor: pointer;
  user-select: none;
}
.fold > summary::-webkit-details-marker {
  display: none;
}
.fold-title {
  flex: 1 1 auto;
  font-size: 15px;
  font-weight: 800;
  letter-spacing: -0.01em;
}
.fold-count {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: var(--radius-pill);
  background: var(--brand-wash);
  color: var(--brand-ink);
  font-size: 11px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}
.fold-chev {
  flex: 0 0 auto;
  display: inline-flex;
  color: var(--text-faint);
  transition: transform 140ms ease-out;
}
.fold[open] .fold-chev {
  transform: rotate(180deg);
}
.fold-chev svg {
  width: 18px;
  height: 18px;
}
.fold-body {
  padding: 0 12px 12px;
}
.fold-body > .help:first-child {
  margin-top: 0;
}
/* A heading inside Advanced. Smaller than a card title, and separated by a rule, because two
   unrelated blocks inside one fold otherwise run together. */
.sub-title {
  margin: 0;
  font-size: 14px;
  font-weight: 800;
}
.sub-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid var(--divider);
}
.capture-box {
  padding: 8px 10px;
  border: 1px solid var(--field-border);
  border-radius: var(--radius-control);
  min-height: 72px;
}
.help {
  color: var(--text-muted);
  font-size: 13px;
  margin-bottom: 8px;
}

.rule-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 0;
  border-top: 1px solid var(--divider);
}
.rule-row:first-of-type {
  border-top: 0;
}
/* A list of fourteen rules is read by skimming it, so it is set at list density, not at body
   size (Max, 2026-09-18). */
.rule-text {
  flex: 1 1 auto;
  overflow-wrap: anywhere;
  font-size: 13px;
  font-weight: 600;
  line-height: 18px;
}
.rule-off .rule-text {
  color: var(--text-faint);
}

.switch {
  position: relative;
  flex: 0 0 auto;
  width: 44px;
  height: 27px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong);
  background: var(--bg-sunken);
  cursor: pointer;
  padding: 0;
  transition: background 120ms ease-out, border-color 120ms ease-out;
}
.switch::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 21px;
  height: 21px;
  border-radius: 50%;
  background: var(--bg-raised);
  border: 1px solid var(--border-strong);
  transition: transform 120ms ease-out;
}
.switch[aria-checked="true"] {
  background: var(--brand);
  border-color: var(--brand);
}
.switch[aria-checked="true"]::after {
  transform: translateX(17px);
  border-color: transparent;
}
.switch:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

.add-row {
  display: flex;
  gap: 8px;
  margin-top: 12px;
}
.fact-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
  border-top: 1px solid var(--divider);
}
.fact-row:first-of-type {
  border-top: 0;
}
.fact-text {
  flex: 1 1 auto;
  overflow-wrap: anywhere;
  font-size: 13px;
  font-weight: 600;
  line-height: 18px;
}
.icon-btn {
  border: 0;
  background: transparent;
  color: var(--text-faint);
  cursor: pointer;
  min-width: var(--tap-sm);
  min-height: var(--tap-sm);
  border-radius: var(--radius-control);
  font-size: 17px;
  line-height: 1;
  flex: 0 0 auto;
}
.icon-btn:hover {
  background: var(--bg-sunken);
  color: var(--danger);
}

.settings-card {
  padding: 0;
}
.setting {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 11px 0;
  border-top: 1px solid var(--divider);
}
.setting:first-of-type {
  border-top: 0;
}
.setting-label {
  font-size: 14px;
  font-weight: 700;
}
.setting-help {
  color: var(--text-muted);
  font-size: 12px;
  line-height: 17px;
}
.slider {
  width: 100%;
  accent-color: var(--brand);
  min-height: 34px;
}
.slider-value {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}

/* ---------- usage screen ---------- */

.plan-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: 16px;
  background: var(--bg-raised);
}
.stat-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 0;
  border-top: 1px solid var(--border);
}
.stat-row:first-of-type {
  border-top: 0;
}
.stat-value {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.meter {
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--bg-sunken);
  overflow: hidden;
  margin-top: 8px;
}
.meter-fill {
  height: 100%;
  background: var(--brand);
}
.meter-fill.warn {
  background: var(--warn);
}
.plan-options {
  display: grid;
  gap: 12px;
  margin-top: 16px;
}
.plan-option {
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: 14px;
}
.plan-option-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}
.price {
  font-weight: 700;
}

/* ---------- early access ---------- */

.card {
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: 16px;
  background: var(--bg-raised);
  margin: 16px 0;
}
.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 40;
  background: rgba(22, 21, 15, 0.4);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: var(--gutter);
}
.modal {
  width: 100%;
  max-width: 560px;
  max-height: 90vh;
  overflow-y: auto;
  background: var(--bg-raised);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: 16px;
  padding-bottom: max(16px, env(safe-area-inset-bottom));
  margin: 0;
}
.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 8px;
}
/* The card already has a frame; inside a modal the modal is the frame. */
.modal .card {
  margin: 0;
  border: 0;
  padding: 0;
}

.form-field {
  margin-bottom: 16px;
}
.radios {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.radio {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  padding: 0 12px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-control);
  cursor: pointer;
  font-size: 14px;
}
.radio input {
  accent-color: var(--brand);
}
.radio.checked {
  border-color: var(--brand);
  background: var(--brand-wash);
}

/* ---------- skeleton ---------- */

.skeleton {
  border-radius: var(--radius-control);
  background: var(--bg-sunken);
  height: 14px;
  margin-bottom: 8px;
}
.skeleton-row {
  padding: 16px 0;
  border-top: 1px solid var(--border);
}
.skeleton-row:first-of-type {
  border-top: 0;
}
.skeleton.w40 {
  width: 40%;
}
.skeleton.w70 {
  width: 70%;
}
.skeleton.w90 {
  width: 90%;
}
.skeleton.box {
  height: 88px;
  border-radius: var(--radius-card);
}

/* ---------- errors / center ---------- */

.center {
  display: flex;
  justify-content: center;
  padding: 40px 0;
}
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- wider screens ---------- */

@media (min-width: 768px) {
  :root {
    --gutter: 24px;
  }
  .modal-backdrop {
    align-items: center;
  }
}

@media (max-width: 359px) {
  .display {
    font-size: 32px;
  }
  .h1 {
    font-size: 26px;
  }
  .account-trigger {
    max-width: 46vw;
  }
}

/* ---------- settings: save indicator and the language select ---------- */

.section-note {
  color: var(--text-muted);
  font-size: 13px;
  white-space: nowrap;
}

.select {
  width: 100%;
  min-width: 0;
  min-height: 44px;
  margin-top: 8px;
  padding: 8px 12px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-control);
  background: var(--bg-raised);
  color: var(--text);
  font-family: inherit;
  font-size: 16px; /* 16px or iOS zooms the page on focus */
}

/* The archive filter sits in a toolbar row, not in a settings card. */
.select-sm {
  width: auto;
  min-width: 190px;
  min-height: var(--tap-sm);
  margin-top: 0;
  padding: 6px 10px;
  font-size: 14px;
}

/* ---------- voice panel: collapsible on a phone ---------- */

.voice-panel-summary {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  min-height: 32px;
  cursor: pointer;
  list-style: none;
}
.voice-panel-summary::-webkit-details-marker {
  display: none;
}
.voice-panel[open] .voice-panel-summary {
  margin-bottom: 10px;
}
.voice-panel .voice-panel-title {
  margin-bottom: 0;
}

/* ---------- archive ---------- */

.archive-row .comment-card {
  background: var(--bg-raised);
}
.archive-why {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 6px;
}
.by-pill {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  padding: 2px 9px;
  border-radius: var(--radius-pill);
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  color: var(--text-muted);
  font-size: 12px;
  font-weight: 600;
}
/* The kept draft, in the editable box's skin but static. Same type as the live draft, so the
   archive shows the same thing the inbox would. */
.archive-draft {
  margin-top: 6px;
}
.draft-static {
  margin: 0;
  font-size: 14px;
  font-weight: 560;
  line-height: 20px;
  color: var(--text);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
/* The archive is one card of rows in one order, with no post headers to break it up. */
.flat-group .row:first-child {
  padding-top: 2px;
}

/* Bring back takes .row-actions' own size and spacing, exactly like Skip / Rewrite / Send:
   measured at 360px, both are 34px tall at 13px (Max, 2026-09-19). */
.archive-row .row-actions {
  margin-top: 6px;
}

/* ---------- dictation ---------- */

.dictate {
  display: flex;
  align-items: center;
  gap: 8px;
}
.mic-btn {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap);
  height: var(--tap);
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: var(--bg-raised);
  cursor: pointer;
  font-size: 17px;
  line-height: 1;
}
.mic-btn svg {
  width: 17px;
  height: 17px;
}
.free-text-row .mic-btn svg {
  width: 15px;
  height: 15px;
}
.mic-btn[aria-pressed="true"] {
  background: var(--brand);
  border-color: var(--brand);
  color: #fff;
}
.mic-btn:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}
/* Twenty-four bars driven by the real microphone level, so a silent room looks silent. */
.bars {
  display: flex;
  align-items: center;
  gap: 2px;
  height: 22px;
  flex: 1 1 auto;
  min-width: 0;
}
.bars i {
  display: block;
  flex: 1 1 auto;
  min-width: 2px;
  max-width: 4px;
  height: 3px;
  border-radius: 2px;
  background: var(--brand);
  opacity: 0.85;
  transform-origin: center;
  transition: height 90ms linear;
}
.dictate-note {
  color: var(--text-muted);
  font-size: 13px;
  white-space: nowrap;
}

/* ---------- one field that becomes rules and facts ---------- */

.capture textarea {
  min-height: 74px;
}
.capture-actions {
  display: flex;
  justify-content: flex-end;
  gap: 6px;
  margin-top: 6px;
  flex-wrap: wrap;
}
.proposals {
  margin-top: 10px;
  border-top: 1px solid var(--border);
  padding-top: 8px;
}
.proposal {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 7px 0;
  border-top: 1px solid var(--border);
}
.proposal:first-of-type {
  border-top: 0;
}
.proposal-text {
  flex: 1 1 auto;
  overflow-wrap: anywhere;
  font-size: 15px;
}
.kind-pill {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  min-height: 22px;
  margin-top: 1px;
  padding: 1px 8px;
  border-radius: var(--radius-pill);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  color: var(--text-muted);
}
.kind-pill.is-fact {
  background: var(--accent-wash);
  border-color: transparent;
  color: var(--accent);
}
.proposal.dropped .proposal-text {
  color: var(--text-faint);
  text-decoration: line-through;
}

/* ---------- the two tabs: what needs answering, and what was set aside ---------- */

.tabs {
  display: flex;
  align-items: stretch;
  gap: 18px;
  border-bottom: 1px solid var(--border);
}
.tab {
  border: 0;
  background: none;
  padding: 9px 0 7px;
  margin-bottom: -1px;
  border-bottom: 2px solid transparent;
  color: var(--text-muted);
  font: inherit;
  font-size: 15px;
  font-weight: 650;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: var(--tap-sm);
}
.tab[aria-current="page"] {
  color: var(--text);
  border-bottom-color: var(--brand);
}
.tab-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  height: 20px;
  padding: 0 6px;
  border-radius: var(--radius-pill);
  background: var(--brand-wash);
  color: var(--brand-ink);
  font-size: 12px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.tab[aria-current="page"] .tab-count {
  background: var(--brand);
  color: #ffffff;
}

/* ---------- undoing an edit, and a send nobody could confirm ---------- */

.undo-row {
  display: flex;
  justify-content: flex-end;
  margin-top: 4px;
}
.uncertain-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 6px;
  margin-top: 6px;
}

/* ---------- dark ---------- */

/* AI Drafts follows the system setting and this one did not, which on an evening phone made
   DraftsReady the one app that stayed white (Max, 2026-09-18). It is a token swap and nothing
   else: every surface, rule and ink on this screen is already a variable. The brand teal
   does not change - it is the brand - but the washes behind it lose their brightness, and
   the field tint stays warm so "type here" still reads as an invitation.

   The switch is `data-theme` on <html>, never a bare media query: the phone's setting is only
   the default, and Settings can override it for this device (Max, 2026-09-19). One selector
   means one copy of these tokens - resolving the media query is done once, in JS, before the
   first paint (src/client/theme.ts and the inline script in index.html). */
:root[data-theme="dark"] {
  color-scheme: dark;
  /* The same swap as the light block: the ground moves off green onto the dark navy of the
     mark, and the teal becomes the logo's turquoise. The draft field stays dark here - white is
     what it should be on paper, not at night. */
  --bg: #10171d;
  --bg-sunken: #182430;
  --bg-raised: #16202a;
  --border: #2a3843;
  --border-strong: #3e5261;
  --divider: #2f3e4a;
  --text: #eef3f5;
  --text-muted: #a5b2ba;
  --text-faint: #7f8d96;
  --brand: #45c2d0;
  --brand-ink: #6bd6e2;
  --brand-wash: #0b2e34;
  --field: #1a2530;
  --field-border: #2c5b63;
  --accent: #7fc98e;
  --accent-wash: #142b1c;
  --warn: #e0b06b;
  --warn-wash: #32270f;
  --danger: #ef9c8e;
  --danger-wash: #38211c;
  --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.5), 0 8px 24px rgba(0, 0, 0, 0.42);
  --shadow-soft: 0 1px 2px rgba(0, 0, 0, 0.4);
}
/* On a dark card a shadow separates nothing; the border is what does the work. */
:root[data-theme="dark"] .section,
:root[data-theme="dark"] .fold,
:root[data-theme="dark"] .post-group {
  box-shadow: none;
}
/* The filled Send button: near-black on this turquoise is the pair with the most contrast. */
:root[data-theme="dark"] .btn-primary,
:root[data-theme="dark"] .mic-btn[aria-pressed="true"],
:root[data-theme="dark"] .tab[aria-current="page"] .tab-count {
  color: #06222a;
}
:root[data-theme="dark"] .modal-backdrop {
  background: rgba(0, 0, 0, 0.62);
}

/* --- the AI Drafts palette, an option to try on, not a replacement --------- */
/* Max: "сделай мне еще как вариантом полную версию цветов как в ai drafts... хочу пощёлкать"
   (2026-09-21). One more `data-theme` value beside light/dark/system, switched from Settings,
   reverting is deleting this block - nothing above it changes. Values read off AI Drafts' own
   `theme.css` (Emailer repo, enzo/ai-drafts/app/client/src/theme.css), not invented.

   AI Drafts has no coloured brand: its accent is lime, used only as a fill, and every piece of
   coloured text DraftsReady has (role labels, the DRAFT caption, "wrote" links) is quiet ink
   over there. So --brand does not become lime - lime as text on paper is unreadable - it
   becomes AI Drafts' own near-black ink, --brand-ink becomes its quiet grey-green dim, and the
   lime shows up exactly once: as the wash/border on the Rewrite button below, the same treatment
   AI Drafts gives its one AI action (`.ai-button` in its theme.css). */
:root[data-theme="aidrafts"] {
  color-scheme: light;
  --bg: #f6f3ec;
  --bg-sunken: #e7e9e3;
  /* AI Drafts' own --surface (#fffdf8), not pure white - pure white is kept for --field below,
     the one box a person types in, same split AI Drafts itself makes with --surface-strong. */
  --bg-raised: #fffdf8;
  --border: #d7d9d2;
  --border-strong: #c3c5be;
  --divider: #d7d9d2;
  --text: #18221d;
  --text-muted: #59625b;
  --text-faint: #7c857e;
  /* Primary fill + focus ring: AI Drafts' near-black, not a hue - Send becomes its black pill. */
  --brand: #18221d;
  /* Coloured text everywhere else (.thread-role, .row-banner, .comment-role, .draft-label,
     .meta-link): AI Drafts' own --dim, a quiet ink, not lime. ~5.7:1 on --bg - comfortable. */
  --brand-ink: #59625b;
  /* The one lime moment that is not the Rewrite button below: AI Drafts' lime wash, used for
     the small badges (avatar initials, tag pill, counters) that already pair --brand-wash with
     --brand-ink text. */
  --brand-wash: color-mix(in srgb, #c9f24a 14%, #fffdf8);
  --field: #ffffff;
  --field-border: #d7d9d2;
  /* AI Drafts' "sent" green, literal from its theme.css (.sent-label, .thread-sent). */
  --accent: #15704c;
  --accent-wash: color-mix(in srgb, var(--accent) 12%, var(--bg-raised));
  --warn: #94501d;
  --warn-wash: color-mix(in srgb, var(--warn) 10%, var(--bg-raised));
  --danger: #9d321e;
  --danger-wash: color-mix(in srgb, var(--danger) 10%, var(--bg-raised));
  --shadow-card: 0 1px 2px rgba(24, 34, 29, 0.06), 0 8px 24px rgba(24, 34, 29, 0.06);
  --shadow-soft: 0 1px 2px rgba(24, 34, 29, 0.05);
}
/* Rewrite is the AI action, so it is the one control that gets AI Drafts' `.ai-button`
   treatment verbatim: a lime-tinted fill and border, dark-olive text. `[aria-expanded]` is
   unique to the Rewrite button inside `.row-actions` in both InboxRow.tsx render paths (the
   connected-account row and the demo row) - Skip and Send do not carry that attribute - so this
   reaches it without adding a class in the component. */
:root[data-theme="aidrafts"] .row-actions .btn[aria-expanded] {
  border-color: color-mix(in srgb, #c9f24a 42%, var(--border));
  background: color-mix(in srgb, #c9f24a 14%, var(--bg-raised));
  color: #425b0b;
}
:root[data-theme="aidrafts"] .row-actions .btn[aria-expanded] svg {
  color: #425b0b;
}

/* --- the palette playground: a custom accent + background, tried on top -------------- */
/* Settings -> Appearance, "an experiment behind the segmented control" (Max, 2026-09-21:
   "хочу сам поставить нужный акцентный цвет... и поиграть с ними"). `--brand` and `--bg`
   themselves come in as inline properties on <html> from src/client/palette.ts (and, on boot,
   its duplicate in public/theme-boot.js) - never written here. Everything else in this block
   is one formula, in one place, run for whichever theme happens to be active underneath:
   this selector's specificity ties the three `[data-theme=...]` blocks above, so placing it
   last in the file is what lets it win regardless of which of those is also present.

   `--text` / `--text-muted` / `--text-faint` are NOT here: `color-mix` always mixes toward one
   fixed target, so it cannot flip from dark ink to light ink as the chosen background does -
   that needs to know the background's lightness first, which only palette.ts can compute. They
   come in as inline properties too, alongside `--brand-on` below. */
:root[data-palette="custom"] {
  --brand-ink: color-mix(in srgb, var(--brand) 55%, var(--text) 45%);
  --brand-wash: color-mix(in srgb, var(--brand) 14%, var(--bg-raised));
  --bg-sunken: color-mix(in srgb, var(--bg) 94%, black 6%);
  --bg-raised: color-mix(in srgb, var(--bg) 90%, white 10%);
  --border: color-mix(in srgb, var(--bg) 82%, var(--text) 18%);
  --border-strong: color-mix(in srgb, var(--bg) 65%, var(--text) 35%);
  --divider: var(--border);
  --field: var(--bg-raised);
  --field-border: color-mix(in srgb, var(--brand) 40%, var(--border));
}
/* The one thing a fixed color-mix formula cannot get right either: text ON the accent fill
   (Send, the pressed mic, the active tab count) needs to know whether the accent itself is
   light or dark, the same reason --text above cannot be a plain color-mix. `--brand-on` is
   computed once, in JS, from the accent's relative luminance - see palette.ts - and used only
   here, the same three selectors the dark theme already overrides for the same reason. */
:root[data-palette="custom"] .btn-primary,
:root[data-palette="custom"] .mic-btn[aria-pressed="true"],
:root[data-palette="custom"] .tab[aria-current="page"] .tab-count {
  color: var(--brand-on);
}

/* --- light or dark, inside the account menu ------------------------------- */
/* Three icons on one row rather than a menu item leading to a screen: it is the one setting
   somebody changes because of the room they are sitting in (Max, 2026-09-19). */
.menu-theme {
  display: flex;
  gap: 4px;
  padding: 4px;
}
.menu button.menu-theme-btn {
  flex: 1 1 0;
  min-height: 36px;
  justify-content: center;
  padding: 0;
  border: 1px solid transparent;
  color: var(--text-muted);
}
.menu-theme-btn svg {
  width: 18px;
  height: 18px;
}
.menu button.menu-theme-btn[aria-pressed="true"] {
  background: var(--bg-sunken);
  border-color: var(--border);
  color: var(--text);
}

/* --- the failure log ------------------------------------------------------ */
.log-row {
  padding: 10px 0 9px;
}
.log-row + .log-row {
  border-top: 1px solid var(--divider);
}
.log-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
}
.log-subject {
  font-size: 13px;
  font-weight: 600;
}
.log-when {
  margin-left: auto;
  font-size: 12px;
  color: var(--text-muted);
}
.log-message {
  margin: 4px 0 0;
  font-size: 13px;
  line-height: 18px;
  color: var(--text-muted);
  overflow-wrap: anywhere;
}
/* Not confirmed either way is its own thing: it may well have gone out. */
.by-pill.is-uncertain {
  border-color: var(--warn);
  color: var(--text);
}
