/*
 * Awdur brand foundation — the shared design system for every surface
 * (the authenticated app AND the public author landing pages). Slice-specific
 * layout lives with each slice; this file owns the tokens, typography, base
 * elements, and the curated palette presets (D13) so nothing drifts.
 *
 * Fonts (Newsreader serif + Hanken Grotesk sans) are loaded in the layout head.
 * Propshaft serves this as `application`.
 */

:root {
  --paper:      #F2EDE3;   /* warm ground */
  --paper-hi:   #FBF8F2;   /* raised card */
  --paper-in:   #EFE8DB;   /* inset well */
  --line:       #d8d2c6;   /* hairline */
  --line-soft:  #e4ddd0;
  --ink:        #3b352c;   /* primary text */
  --ink-soft:   #736B5C;   /* secondary text */
  --ink-faint:  #a99f8d;   /* placeholders */

  /* App default accent is PLUM. Components reference --accent/-hi/-deep, never a
     hard-coded hue, so a `.palette-*` preset class (author pages) recolors
     everything beneath it; unclassed app chrome (dashboard, editor, auth) uses
     this default. */
  --accent:      #6B4A6B;
  --accent-hi:   #8a638a;
  --accent-deep: #573957;

  --ok:    #3A5A43;
  --warn:  #9a6a2f;
  --error: #8f3a2f;

  --serif: 'Newsreader', Georgia, 'Times New Roman', serif;
  --sans:  'Hanken Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;

  --radius: 11px;
  --measure: 64ch;
}

/* ---- curated palette presets (D13: constrained choices, not raw hex) ---- */
.palette-terracotta { --accent:#9C5B3B; --accent-hi:#c08560; --accent-deep:#8a4f33; }
.palette-slate      { --accent:#4A5A6B; --accent-hi:#6E7E8F; --accent-deep:#3a4857; }
.palette-plum       { --accent:#6B4A6B; --accent-hi:#8a638a; --accent-deep:#573957; }
.palette-forest     { --accent:#3A5A43; --accent-hi:#4f7a5c; --accent-deep:#2d4834; }
.palette-ink        { --accent:#4a4438; --accent-hi:#6a6152; --accent-deep:#2a251e; }

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4 {
  font-family: var(--serif);
  font-weight: 400;
  line-height: 1.1;
  letter-spacing: -0.01em;
  text-wrap: balance;
  margin: 0 0 0.4em;
}
h1 { font-size: clamp(30px, 5vw, 46px); }
h2 { font-size: clamp(24px, 3.4vw, 32px); }
h3 { font-size: 22px; }

p { margin: 0 0 1rem; max-width: var(--measure); }

a { color: var(--accent-deep); text-underline-offset: 3px; text-decoration-thickness: 1px; }
a:hover { color: var(--accent); }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }

/* eyebrow / uppercase label */
.eyebrow {
  font-size: 12px; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--ink-soft);
}

/* ---- forms ---- */
label { display: block; font-size: 14px; color: var(--ink-soft); margin: 0 0 6px; }

input[type=text], input[type=email], input[type=password],
input[type=url], input[type=number], textarea, select {
  width: 100%;
  font-family: var(--sans); font-size: 16px; color: var(--ink);
  background: var(--paper-hi);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 13px 14px;
  transition: border-color .18s, box-shadow .18s, background .18s;
}
input::placeholder, textarea::placeholder { color: var(--ink-faint); }
input:focus, textarea:focus, select:focus {
  outline: none; background: var(--paper-hi);
  border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
}

button, .button, input[type=submit] {
  font-family: var(--sans); font-size: 16px; font-weight: 600;
  color: var(--paper-hi); background: var(--accent);
  border: 1px solid var(--accent); border-radius: var(--radius);
  padding: 13px 22px; cursor: pointer; text-decoration: none; display: inline-block;
  transition: background .18s, transform .06s;
}
button:hover, .button:hover, input[type=submit]:hover { background: var(--accent-deep); }
button:active, .button:active { transform: translateY(1px); }

/* THE SPECIFICITY TRAP, and the over-correction that follows it. Read both.
 *
 * The base rule above matches a submit button as `input[type=submit]` — an
 * attribute selector, so (0,1,1). A bare `.button-quiet` is (0,1,0) and LOSES
 * every declaration to it, which meant every `f.submit` asking to be quiet
 * (admin Reject/Suspend, the panel's Grant and Update expiry, the book editor's
 * Add link / Save link) rendered accent-FILLED — the "wall of identical
 * recommended actions" the 2026-07-25 audit kept finding. The compound selector
 * (0,2,1) is what makes the class do anything at all on those elements, and it
 * must restate colour and background, not just the border.
 *
 * But the two rules CANNOT be merged, and the border must not be hoisted into
 * the bare one. ~22 `link_to` sites wear this class — the app header's
 * Settings/Help/Admin on every authenticated page, the public-plane preview
 * bar's back link — and an <a> matches NO base button rule, so it has no
 * radius and no button chrome. A `border` shorthand there paints a square 1px
 * box around each of them. `border-color` alone is inert on an anchor (no
 * border-style to go with it) and is exactly enough on a <button>, where the
 * class already outranks the bare element selector. So: longhand in the bare
 * rule, shorthand ONLY in the submit compound. */
.button-quiet {
  color: var(--accent-deep); background: transparent; border-color: var(--line);
}
input[type=submit].button-quiet {
  color: var(--accent-deep); background: transparent; border: 1px solid var(--line);
}
.button-quiet:hover,
input[type=submit].button-quiet:hover { background: var(--paper-hi); color: var(--accent-deep); }

/* Destructive variant (polish backlog P2-8, first needed by P1-10.4). Built from
   --error exactly as .badge-blocked mixes it — no new token. An irreversible
   action must not wear the accent fill, which is the app's ONE signal for
   "recommended", nor the same quiet button as "Add another link". Reversible
   actions ("Take offline") stay .button-quiet: this class means "can't be
   undone", and it keeps its confirm/reason requirement either way.
   Same two-rule shape as .button-quiet above — no anchor wears this class today,
   but seeding the merged shape is how the anchor bug comes back. */
.button-danger {
  color: var(--error); background: transparent;
  border-color: color-mix(in srgb, var(--error) 40%, var(--line));
}
input[type=submit].button-danger {
  color: var(--error); background: transparent;
  border: 1px solid color-mix(in srgb, var(--error) 40%, var(--line));
}
.button-danger:hover,
input[type=submit].button-danger:hover {
  background: color-mix(in srgb, var(--error) 8%, var(--paper-hi));
  color: var(--error);
}

/* ---- flash ---- */
.flash {
  max-width: 640px; margin: 16px auto; padding: 12px 16px;
  border-radius: var(--radius); border: 1px solid var(--line);
  background: var(--paper-hi); font-size: 15px;
}
.flash-notice { border-color: color-mix(in srgb, var(--ok) 40%, var(--line)); color: var(--ok); }
.flash-alert  { border-color: color-mix(in srgb, var(--error) 40%, var(--line)); color: var(--error); }

.form-errors { color: var(--error); font-size: 14px; margin: 0 0 16px; }
.form-errors ul { margin: 0; padding-left: 18px; }

/* ---- layout helpers ---- */
.wrap { width: 100%; max-width: 720px; margin: 0 auto; padding: clamp(24px, 5vw, 56px) 22px; }
.stack > * + * { margin-top: 18px; }

.brand { font-family: var(--serif); font-size: 20px; color: var(--ink); text-decoration: none; }
.brand .dot { color: var(--accent); }
.ink-soft { color: var(--ink-soft); }
.ink-faint { color: var(--ink-faint); }
.hint { color: var(--ink-soft); font-size: 14px; }

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

/* =====================================================================
 * Author app shell — the signed-in author's own workspace (dashboard,
 * page editor, holding page) plus the staff admin console. Utilitarian
 * tool chrome, not a marketing surface; reuses the tokens/elements above.
 * ===================================================================== */

.app-shell { min-height: 100vh; }

.app-header {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; padding: 16px clamp(20px, 4vw, 40px);
  border-bottom: 1px solid var(--line);
  background: var(--paper-hi);
}
.app-nav { display: flex; align-items: center; gap: 16px; font-size: 14px; color: var(--ink-soft); }
.app-nav .button-quiet { padding: 8px 14px; font-size: 14px; }

.app-main { max-width: 760px; margin: 0 auto; padding: clamp(24px, 5vw, 48px) 22px; }
.app-main-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; flex-wrap: wrap; margin-bottom: 24px;
}
.app-main-head h1 { margin: 0; }

.card {
  background: var(--paper-hi); border: 1px solid var(--line); border-radius: var(--radius);
  padding: clamp(18px, 3vw, 28px); margin-bottom: 20px;
}
.card h2 { font-size: 18px; margin-bottom: 12px; }
.card > div + div { margin-top: 14px; }

.badge {
  display: inline-block; font-size: 12px; font-weight: 600; letter-spacing: 0.04em;
  text-transform: uppercase; padding: 3px 10px; border-radius: 999px;
}
.badge-live { background: color-mix(in srgb, var(--ok) 16%, transparent); color: var(--ok); }
.badge-draft { background: var(--paper-in); color: var(--ink-soft); }
.badge-admin { background: color-mix(in srgb, var(--accent) 16%, transparent); color: var(--accent-deep); }
/* D14 secondary badges — ADDITIONS beside .badge-live, never replacements.
   Staged keys off --warn (the .badge-live formula); scheduled off --accent
   (the .badge-admin formula). No new token. */
.badge-staged { background: color-mix(in srgb, var(--warn) 16%, transparent); color: var(--warn); }
.badge-scheduled { background: color-mix(in srgb, var(--accent) 16%, transparent); color: var(--accent-deep); }

/* ---- admin console: wider main + plain roster tables ---- */
.app-main-wide { max-width: 1040px; }
.table-scroll { overflow-x: auto; }
.data-table { width: 100%; border-collapse: collapse; font-size: 15px; }
.data-table th {
  text-align: left; font-weight: 600; font-size: 12px; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--ink-soft);
  padding: 0 14px 10px; border-bottom: 1px solid var(--line);
}
.data-table td { padding: 12px 14px; border-bottom: 1px solid var(--line-soft); vertical-align: middle; }
.data-table tr:last-child td { border-bottom: none; }
.data-table td:first-child { font-weight: 500; color: var(--ink); }
.data-table form { display: inline-block; }
.data-table td button { padding: 7px 14px; font-size: 14px; }
.data-table details summary { cursor: pointer; color: var(--accent-deep); font-size: 14px; }
/* P1-10.2 — dates and timestamps never wrap. An ISO date that broke into
   "2026-" / "07-25" read as a damaged value and made every row two lines tall;
   tabular figures then let a column of dates align on their digits. The nowrap is
   scoped to the TOKEN, not the cell: an Expires cell also carries relative-time
   prose ("· 3 days ago"), and holding that on one line would push this 7-column
   table into horizontal scroll even at 1040px. */
.data-table td.date-cell { font-variant-numeric: tabular-nums; }
.data-table .date-token { white-space: nowrap; }
/* a plain-language note inside an uppercase column header (e.g. what an em dash
   means in that column) — same ink, sentence case, no tooltip */
.data-table th .th-note { text-transform: none; letter-spacing: 0; font-weight: 400; }
/* P1-4 — the audit log's DETAILS cell. The payload wraps (it used to clip
   mid-token, so a row read as corrupted) and the JSON lives in a --paper-in well
   inside the existing details/summary disclosure. Monospace is a SYSTEM stack —
   the only non-brand family in the app, and no webfont. */
.data-table td.audit-details { overflow-wrap: anywhere; }
.data-table details pre {
  background: var(--paper-in); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 10px 12px; margin: 8px 0 0;
  max-height: 220px; overflow: auto; white-space: pre-wrap;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px; color: var(--ink-soft);
}

/* P1-5 — the roster's first cell is the row's link to the dossier. It inherits
   the cell's ink/500 emphasis so the table still reads as a table; the accent
   only appears on hover/focus. (A clickable <tr> was rejected: it swallows the
   Suspend button's hit target and breaks keyboard order.) */
.data-table td:first-child a.row-link { color: inherit; text-decoration: none; }
.data-table td:first-child a.row-link:hover,
.data-table td:first-child a.row-link:focus-visible {
  text-decoration: underline; text-decoration-color: var(--accent);
}

/* admin entitlements panel (admin-entitlements-panel-spec §4.6). Layout + tone
   ONLY: no colour beyond existing tokens, no new token, no .palette-*.
   Precedent: .condition-row, .schedule-controls, .link-row-btn. */
.ent-row-form { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 8px 10px; margin-top: 10px; }
/* the global input rule is width:100% — a reason input needs a floor, not a fill,
   or it blows out its table cell */
.ent-reason { width: auto; min-width: 16rem; flex: 1 1 16rem; }
/* a not-granted flag reads quieter than a granted one (overrides the
   .data-table td:first-child ink/500 emphasis) */
.data-table td.flag-off { color: var(--ink-soft); font-weight: 400; }

/* moderation queue status filters (admin/authors) */
.admin-filters {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  font-size: 14px; color: var(--ink-soft); margin-bottom: 24px;
}
.admin-filters a { text-decoration: none; color: var(--ink-soft); }
.admin-filters a:hover { color: var(--accent-deep); }
.admin-filters a.current {
  color: var(--accent-deep); font-weight: 600;
  border-bottom: 2px solid var(--accent);
}

/* admin author dossier: stat tiles (counts only, never reader lists) */
.stat-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 12px; }
.stat {
  background: var(--paper-in); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 12px 14px;
}
.stat-figure { display: block; font-family: var(--serif); font-size: 28px; line-height: 1.15; color: var(--ink); }
.stat-label {
  display: block; font-size: 12px; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--ink-soft); margin-top: 2px;
}
/* suspended/rejected status badge — mirrors the .badge-live formula on --error */
.badge-blocked { background: color-mix(in srgb, var(--error) 16%, transparent); color: var(--error); }

.actions { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 16px; }

.editor-form .actions { margin-top: 24px; }
.editor-form .actions .hint { align-self: center; }

/* THE RULE (polish backlog P1-7): A CARD OWNS ITS OWN SAVE. Every action row we
   ship now lives inside a card, so this rule has to WIN over `.editor-form
   .actions` above — the two selectors have identical specificity, which means
   source order decides and this one must come last. Do not move it back up. */
.card .actions { margin-top: 12px; }

/* ---- page status block (dashboard card + editor) ---- */
/* card heading + badge on one scannable line (same pattern as .app-main-head) */
.card-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 8px; }
.card-head h2 { margin-bottom: 0; }

.page-status { margin-bottom: 20px; }
.card .page-status { margin-bottom: 0; }
.page-status .actions { margin-top: 10px; }

/* the live address: plain selectable text + copy + view link, on an inset well */
.url-row {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  background: var(--paper-in); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 10px 14px;
}
.url-row-address { font-weight: 500; }
.url-row-copy { padding: 6px 12px; font-size: 14px; }

/* info panel inside/below a card (e.g. the editor's pending-review note) */
.callout {
  background: var(--paper-in); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 12px 16px; margin-top: 16px;
  font-size: 14px; color: var(--ink-soft);
}
.callout strong { color: var(--ink); }

/* D14 staged-changes summary inside the status block (the V1 diff affordance) */
.staged-summary { margin-top: 12px; }
.staged-summary p { margin: 0; }
.staged-summary .hint { margin-top: 2px; }

/* D14 pending-schedule panel — the "goes live {time}" promise + its controls.
   Mirrors .callout's inset well (--paper-in). */
.schedule-panel {
  background: var(--paper-in); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 12px 16px; margin-top: 12px;
  font-size: 14px; color: var(--ink-soft);
}
.schedule-panel p { margin: 0; }
.schedule-panel .badge { margin-right: 4px; }

/* ---- authenticated preview chrome over the public template ---- */
/* carries .palette-plum in markup so the bar reads as APP chrome against the
   terracotta public page below it */
.preview-bar {
  position: sticky; top: 0; z-index: 10;
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; min-height: 44px; padding: 6px 16px;
  background: var(--paper-hi); border-bottom: 1px solid var(--line);
}
.preview-bar-label {
  display: flex; align-items: center; gap: 10px;
  font-size: 14px; color: var(--ink-soft);
}
.preview-bar .button-quiet { padding: 6px 12px; font-size: 14px; }

/* inert signup form on preview renders: no visual chrome of its own */
.preview-inert { border: 0; padding: 0; margin: 0; min-width: 0; }

/* The ADMIN record-panel idiom: uppercase eyebrow over a read-only fact, for
   dossier panels where nothing is editable and the density earns it. Author-facing
   surfaces use .field-list below instead (P2-13). The spacing is a GROUPING
   signal — the gap above a term must clearly exceed the gap below it, or a stack
   of term/definition pairs reads as one undifferentiated column (P1-11). */
.detail-list { margin: 0; }
.detail-list dt {
  font-size: 12px; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--ink-soft); margin-top: 18px;
}
.detail-list dt:first-child { margin-top: 0; }
.detail-list dd { margin: 4px 0 0; }
/* P1-11 — `p` has no top margin, so a paragraph after a definition list ran
   straight into the last definition and the two read as one block. */
.detail-list + p { margin-top: 16px; }

/* The AUTHOR-facing read-only fact (P1-9 / P2-13): the form-label idiom — 14px
   sentence case, --ink-soft — over a body-scale value, so an editable field and
   a read-only one speak the same language inside one card. Any explanation is a
   BLOCK under the value, never a trailing span sharing its wrapping line (which
   made a stated email address look like the start of a sentence). */
.field-list { margin: 0; }
.field-list dt { font-size: 14px; color: var(--ink-soft); margin: 0 0 6px; }
.field-list dd + dt { margin-top: 18px; }
.field-list dd { margin: 0; color: var(--ink); }
.field-list dd p.hint { margin: 4px 0 0; }

/* ---- auth screens (registration / sign-in) ----
 * Split layout: a brand hero beside the form, so the logged-out entry lands
 * you in the brand, not a bare box. Stacks on narrow screens. */
.auth-shell {
  min-height: 100vh;
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  /* faint paper grain, tinted by the active palette's accent (so plum stays
     plum, terracotta stays terracotta) */
  background-image:
    radial-gradient(1200px 600px at 78% -8%, color-mix(in srgb, var(--accent-hi) 12%, transparent), transparent 60%),
    radial-gradient(900px 500px at 8% 108%, color-mix(in srgb, var(--accent-deep) 9%, transparent), transparent 60%);
}

.auth-hero { display: flex; align-items: center; padding: clamp(32px, 6vw, 76px); border-right: 1px solid var(--line); }
.auth-hero-inner { max-width: 30rem; }
.eyebrow-row { display: flex; align-items: center; gap: 12px; margin-bottom: 30px; }
.eyebrow-row .mark { font-family: var(--serif); font-size: 22px; font-weight: 500; color: var(--ink); }
.eyebrow-row .mark .dot { color: var(--accent); }
.eyebrow-row .pill {
  font-size: 11px; letter-spacing: .18em; text-transform: uppercase; color: var(--ink-soft);
  border: 1px solid var(--line); border-radius: 999px; padding: 5px 11px;
}
.auth-hero-h {
  font-family: var(--serif); font-weight: 400;
  font-size: clamp(34px, 4.4vw, 52px); line-height: 1.04; letter-spacing: -0.02em; margin: 0;
}
.auth-hero-h em { font-style: italic; color: var(--accent); }
.auth-hero-lede { font-size: clamp(16px, 2vw, 18px); line-height: 1.55; color: var(--ink-soft); margin: 22px 0 0; max-width: 44ch; }
.auth-hero-lede .pron { font-style: italic; }
.auth-hero-points { list-style: none; margin: 30px 0 0; padding: 0; display: flex; flex-direction: column; gap: 13px; }
.auth-hero-points li { position: relative; padding-left: 24px; color: var(--ink); font-size: 15px; }
.auth-hero-points li::before { content: "\2726"; position: absolute; left: 0; color: var(--accent); }

.auth-form-col { display: flex; align-items: center; justify-content: center; padding: clamp(28px, 5vw, 56px); }
.auth-card {
  width: 100%; max-width: 400px; background: var(--paper-hi);
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: clamp(24px, 5vw, 36px);
}
.auth-card h1 { font-size: clamp(26px, 3vw, 32px); }
.auth-sub { color: var(--ink-soft); font-size: 15px; margin: 0 0 22px; }
.auth-alt { font-size: 14px; color: var(--ink-soft); margin: 18px 0 0; }

@media (max-width: 820px) {
  .auth-shell { grid-template-columns: 1fr; }
  .auth-hero { border-right: none; border-bottom: 1px solid var(--line); padding: clamp(28px, 7vw, 44px); }
  .auth-hero-h { font-size: clamp(30px, 8vw, 40px); }
  .auth-hero-lede { margin-top: 16px; }
  .auth-hero-points { display: none; }
}

/* ---- P2 S4: broadcast composer (Trix-constrained, token-based) ---- */
/* trix.css (vendored) ships its own greys; re-skin the chrome with the app
   tokens so the editor reads as plum app chrome, and hide the file-attachment
   group entirely (attachments are refused in JS; no upload endpoint exists). */
trix-toolbar .trix-button-group--file-tools { display: none; }
trix-toolbar .trix-button-group {
  border-color: var(--line); border-radius: var(--radius);
  margin-bottom: 8px;
}
trix-toolbar .trix-button { border-bottom: none; }
trix-toolbar .trix-button.trix-active {
  background: color-mix(in srgb, var(--accent) 14%, transparent);
}
.composer-editor {
  min-height: 260px; width: 100%;
  font-family: var(--sans); font-size: 16px; color: var(--ink);
  background: var(--paper-hi);
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 13px 14px;
  transition: border-color .18s, box-shadow .18s;
}
.composer-editor:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
}
.composer-editor:empty:not(:focus)::before { color: var(--ink-faint); }

/* the saved-draft preview iframe: inert document in a paper well */
.composer-preview {
  width: 100%; height: 380px; border: 1px solid var(--line);
  border-radius: var(--radius); background: #fff;
}

/* "or schedule for later" row under the primary Send button */
.schedule-row { margin-top: 18px; }
.schedule-controls { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }
.schedule-controls input[type=datetime-local] {
  width: auto; flex: 0 1 260px;
  font-family: var(--sans); font-size: 15px; color: var(--ink);
  background: var(--paper-hi); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 10px 12px;
}
.schedule-controls .button-quiet { padding: 10px 18px; font-size: 15px; }

/* P1 links editor — stack the rows inside the links card */
.link-row + .link-row { margin-top: 12px; }
/* row reorder/remove buttons: compact, dedicated class (NOT .url-row-copy, which
   is the live-URL clipboard affordance the draft editor must not show) */
.link-row-btn { padding: 6px 12px; font-size: 14px; }

/* ---- P1 featured-books editor (spec §4.7) — a checklist of the author's
   published books, checked = leads the shelf, up/down to order the featured.
   Layout only; every value is an existing token (no raw hex, no .palette-*).
   Reuses .check-inline for the pick control and .link-row-btn for reorder. ---- */
.featured-books-list { display: flex; flex-direction: column; }
.featured-book-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px 0; border-bottom: 1px solid var(--line-soft);
}
.featured-book-row:last-child { border-bottom: none; }
/* .check-inline override: own the row's left, no top margin inside the row */
.featured-book-pick { margin: 0; gap: 12px; min-width: 0; }
.featured-thumb {
  flex: 0 0 auto; width: 34px; height: 51px; object-fit: cover;
  border-radius: 4px; border: 1px solid var(--line);
}
.featured-book-title {
  font-size: 15px; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.featured-book-controls { flex: 0 0 auto; display: flex; gap: 6px; }

/* ---- P4 S3: author-shell cover/headshot upload bits (spec §3.9) ---- */
.book-thumb {
  flex: 0 0 auto; width: 44px; height: 66px; object-fit: cover;
  border-radius: 6px; border: 1px solid var(--line);
}
.cover-preview { margin: 0 0 10px; }
.cover-preview img {
  max-height: 120px; width: auto; border-radius: 8px; border: 1px solid var(--line);
}
.check-inline { display: flex; align-items: center; gap: 8px; font-size: 14px; color: var(--ink-soft); margin-top: 10px; }
.check-inline input { width: auto; }
.qr-row { margin-top: 12px; }

/* ---- P3 S1: reader segments — preset grid + flat-AND builder rows ----
   Composition/layout only; every value is an existing token (no raw hex, no
   .palette-*). The condition row keeps native controls styled by the global
   form CSS — the only new markup in the feature. */
.preset-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px;
  margin-bottom: 8px;
}
.preset-grid .card { margin-bottom: 0; }

.condition-row {
  display: flex; flex-wrap: wrap; align-items: flex-start; gap: 10px 14px;
  padding: 14px 0; border-bottom: 1px solid var(--line-soft);
}
.condition-row:first-child { padding-top: 4px; }
.condition-row:last-child { border-bottom: none; }
.condition-kind label { margin-bottom: 4px; }
.condition-operand { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; }
.condition-operand select,
.condition-operand input[type=number] { margin: 0; }
.condition-days { width: 6rem; }
.condition-fixed-op { font-size: 14px; }
.condition-remove { margin-left: auto; padding: 6px 12px; font-size: 14px; }

/* Scrollable tag well (Open Q2 interim): existing tokens only, no new component. */
.tag-picker {
  width: 100%; max-height: 168px; overflow-y: auto;
  background: var(--paper-in); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 8px 12px;
}
.tag-picker .check-inline { margin-top: 6px; }
.tag-picker .check-inline:first-child { margin-top: 0; }

/* ---- P3 S2: broadcast composer audience picker ----
   Layout only; every value is an existing token (no raw hex, no .palette-*).
   The picker reuses .card/.check-inline/.stat/.hint; this block just resets the
   fieldset chrome and spaces the segment field + count frame. */
.audience-picker { margin-bottom: 18px; }
.audience-picker fieldset { border: none; margin: 0; padding: 0; }
.audience-picker .label {
  font-family: var(--serif); font-size: 15px; color: var(--ink); padding: 0; margin-bottom: 4px;
}
.audience-picker .hint { margin: 4px 0 0 26px; }
.audience-picker [data-broadcast-audience-target="segmentField"] { margin: 10px 0 0 26px; }
.audience-picker select { margin-bottom: 10px; }
.audience-count { display: block; }

/* P7 S2 analytics — layout + the two new primitives. Plum chrome only; every
   value is a token or a color-mix() of tokens (no raw hex). */
.chart { margin-top: 18px; }
.chart figcaption { margin-top: 8px; }

/* chart canvas Observable Plot draws into; sized so the layout is stable */
.chart svg { max-width: 100%; height: auto; }

/* placeholder in the chart footprint (mirrors .stat's inset well) */
.chart-empty {
  background: var(--paper-in); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 22px 18px; margin-top: 18px;
  min-height: 120px; display: flex; align-items: center;
}
.chart-empty .hint { margin: 0; }

/* distribution bars — tiers + retailers. Single-hue fill, label+length carry data. */
.tier-bars { display: flex; flex-direction: column; gap: 10px; margin-top: 6px; }
.tier-bar { display: grid; grid-template-columns: 9rem 1fr 4rem; align-items: center; gap: 12px; }
.tier-bar-label { font-size: 14px; color: var(--ink-soft); }
.tier-bar-track {
  height: 14px; border-radius: 999px; background: var(--paper-in);
  border: 1px solid var(--line); overflow: hidden;
}
.tier-bar-fill {
  display: block; height: 100%;
  /* token-only ramp — palette-stable under plum chrome */
  background: color-mix(in srgb, var(--accent) 65%, var(--paper-hi));
}
.tier-bar-value { font-size: 14px; color: var(--ink); text-align: right; }

@media (max-width: 560px) { .tier-bar { grid-template-columns: 7rem 1fr 3.5rem; } }

/* ---- P0a progress meter — the shared primitive, used by the public book page,
   the public shelf, and (structurally) nothing else. Lives here, not in
   public.css, because this file is the cross-surface design system and is
   loaded by BOTH layouts; the fill keys off --accent, so it is terracotta on a
   public author page and plum in app chrome with no per-surface code. Geometry
   adapted from .tier-bar-track/.tier-bar-fill; every value is an existing token
   (no raw hex, no new token, no .palette-* dependency). The AUTHOR card renders
   no bar — the public partial consumes this block, so do not move it, rename its
   classes, or fold it into public.css. ---- */
.meter { margin: 0; }

.meter-track {
  height: 10px;                       /* .book-meter/.shelf-meter re-scale this */
  border-radius: 999px;
  background: var(--paper-in);
  border: 1px solid var(--line);
  overflow: hidden;
}
.meter-fill {
  display: block; height: 100%;
  background: var(--accent);
  /* NO transition/animation: the bar states a fact, it does not perform. */
}

.meter-caption {
  margin: 7px 0 0;
  max-width: none;                    /* overrides the global p { max-width: 64ch } */
  font-family: var(--sans); font-size: 13px; line-height: 1.4;
}
.meter-value { color: var(--ink); font-weight: 500; }
.meter-sep   { color: var(--ink-faint); margin: 0 2px; }
/* the D19 stamp: quiet and INVARIANT — no age-based variant exists by design. */
.meter-stamp { color: var(--ink-soft); font-weight: 400; }

/* 100% state ("Draft complete"): replaces the bar. Accent-tinted pill (the
   .author-monogram tint formula at .badge scale) — NOT .badge-live, whose --ok
   green would be the first non-paper/ink/accent hue on a public author page. */
.meter-complete {
  display: inline-block;
  font-size: 12px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase;
  padding: 3px 10px; border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 14%, var(--paper-hi));
  color: var(--accent-deep);
}

/* ---- P0a author slider. The global input rule enumerates types and omits
   `range`, so a range renders fully native today. accent-color is the whole
   treatment: it tints thumb + filled track from the palette in every modern
   engine, with a graceful fall back to native chrome where unsupported — and it
   avoids the ::-webkit-slider-thumb / ::-moz-range-track fork entirely. ---- */
input[type=range].progress-slider {
  width: 100%; max-width: 420px;
  height: 28px;                 /* comfortable pointer/touch target */
  accent-color: var(--accent);  /* plum in app chrome, palette-reactive */
  margin: 4px 0 0;
}

/* the honest-value readout beside the slider: bigger than a .hint (it's the
   number the author is setting), quieter than a heading. */
.meter-readout {
  margin: 8px 0 0; max-width: none;
  font-size: 15px; color: var(--ink-soft);
}
.meter-readout output { font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }

/* hide/show button pushed to the far end of .card-head (already a flex row with
   gap + wrap). button_to renders a <form>, so the auto-margin goes on the form
   element, not the button. Layout only — no colour. */
.card-head .card-head-action { padding: 6px 12px; font-size: 14px; }
.card-head form:has(.card-head-action) { margin-left: auto; }

/* Turbo's own busy flag is the only loading affordance this card needs. */
turbo-frame#book-progress[aria-busy="true"] { opacity: .65; }

/* ---- app-plane error pages (P1-1) ----
   The .not-found centring idiom from public.css, expressed in the app shell
   (public.css is not loaded on this plane). Layout only — no colour beyond the
   tokens the shell already uses, and no new token. */
.app-error { text-align: center; padding-top: clamp(24px, 8vw, 72px); }
.app-error p { margin: 0 auto; color: var(--ink-soft); }
.app-error .actions { justify-content: center; margin-top: 24px; }
