/* =============================================================================
   Cyflow Social — design system
   A small, self-contained SaaS design system: tokens, layout shell, sidebar +
   mobile drawer, cards, forms, buttons, badges, toasts, modals, skeletons,
   empty/error states, and a month calendar. No external fonts or CDNs.
   ========================================================================== */

/* --- tokens --------------------------------------------------------------- */
/*
 * The application green is SAMPLED FROM THE LOGO, not chosen from memory:
 * #1f9e5b is the median of 119,696 solid pixels in the supplied mark, recorded
 * in public/assets/brand/brand-green.json by tools/build-logo.mjs.
 *
 * The ramp is one hue (148) at different lightness, because the mark's own green
 * cannot do every job:
 *
 *   #1f9e5b on white is 3.44:1. That passes WCAG 1.4.11 for non-text UI (3:1),
 *   so it is correct for the mark, active indicators, focus rings and borders.
 *   It FAILS 1.4.3 for normal text (4.5:1), so a primary button filled with it
 *   and labelled in white would be inaccessible.
 *
 * So `--brand-600` is darkened to L=31% (#1a844c, 4.72:1) for filled controls and
 * green text, while `--brand-500` stays the true mark green for marks and edges.
 * Hue is never moved. This mirrors the rule the creative engine already follows
 * for customer palettes: adjust lightness only, keep the hue, record that it
 * happened.
 *
 * Replaces the previous indigo ramp (#6366f1/#4f46e5/#4338ca), which made the
 * product read as a generic purple AI startup and matched nothing about Cyflow.
 */
/*
 * THE APPLICATION IS DARK.
 *
 * This is a deliberate identity change, not a preference toggle: the product is
 * a design studio, and it should feel like the tool it is — a near-black
 * workspace where the only bright things are the user's own colours and the
 * posters they make. A white SaaS chrome competes with every poster shown
 * inside it; a dark one gets out of the way.
 *
 * Only the VALUES below change. Every token NAME is the same, and the ramp keeps
 * its meaning — `--ink-900` is still the strongest reading colour and `--ink-50`
 * still the quietest surface — so every component that addressed meaning rather
 * than a literal colour came across without being touched. That is what the
 * "retuned from one place" note under `--bg` was written for.
 *
 * The customer's own brand colours are untouched by any of this: they live on
 * generated posters, never in the chrome.
 */
:root {
  /*
   * The interactive accent. On a near-black field the logo green (#1f9e5b) reads
   * as a status colour rather than an action, and reserving green for "published"
   * is a rule this design system already keeps. The accent is therefore a soft
   * blue, and green goes back to meaning success.
   *
   * The mark itself keeps its own green — it is a logo, not a control.
   */
  --brand-50: #10151f;   /* barely-there accent wash on dark */
  --brand-100: #161f30;  /* soft accent surface  */
  --brand-200: #24344d;  /* accent border        */
  --brand-500: #6ea8fe;  /* the accent: marks, indicators, focus */
  --brand-600: #6ea8fe;  /* filled controls (dark ink on top, 8.6:1) */
  --brand-700: #8bbcff;  /* hover: lighter on dark, not darker */
  --brand-800: #a5ccff;  /* active               */

  /* Semantic aliases. Components should prefer these over the ramp. */
  --cyflow-brand: var(--brand-500);
  --cyflow-brand-hover: var(--brand-700);
  --cyflow-brand-active: var(--brand-800);
  --cyflow-brand-soft: var(--brand-100);
  --cyflow-brand-subtle: var(--brand-50);
  --cyflow-brand-border: var(--brand-200);
  --cyflow-brand-on: #0b0d10;   /* ink ON the accent fill: near-black, 8.6:1 */
  --focus-ring: var(--brand-500);

  /*
   * The ink ramp, inverted. 900 remains "strongest ink", which on dark means
   * lightest. Components reading --ink-800 for body text or --ink-200 for a
   * border keep working, and keep meaning the same thing.
   */
  --ink-900: #f2f5f9;  /* headings                */
  --ink-800: #e6e9ef;  /* body text               */
  --ink-700: #c8cfdb;  /* secondary text          */
  --ink-600: #a7b0bf;
  --ink-500: #8b94a3;  /* muted / supporting copy */
  --ink-400: #6e7787;  /* placeholder             */
  --ink-300: #3a4150;  /* input edges, emphasis   */
  --ink-200: #2a2f38;  /* ordinary separation     */
  --ink-100: #1b1f26;  /* nested quiet block      */
  --ink-50: #14171c;   /* resting surface         */

  /*
   * Status, re-tuned for a dark field: a deep tinted bed, a light legible
   * foreground, a visible edge. The light pastels these replaced turned into
   * glowing slabs on near-black and shouted louder than the content.
   */
  --ok-bg: #10251a; --ok-fg: #6ee7a8; --ok-bd: #1d4634;
  --warn-bg: #2a2411; --warn-fg: #fcd34d; --warn-bd: #4d4318;
  --err-bg: #2b1416; --err-fg: #fca5a5; --err-bd: #542329;
  --info-bg: #13202f; --info-fg: #93c5fd; --info-bd: #24425f;

  /*
   * F2 semantic surface + text layer. Components address MEANING, not the ink
   * ramp, so the whole application can be retuned from one place. The ramp stays
   * as the source of truth; these are the names components should reach for.
   */
  --bg: #0b0d10;                  /* the page field behind everything */
  --surface: var(--ink-50);       /* ordinary resting surface: cards, sidebar   */
  --surface-2: var(--ink-100);    /* a nested/quiet block inside a surface      */
  --surface-raised: var(--ink-100); /* something that floats: modal, drawer     */
  --border: var(--ink-200);       /* ordinary separation                        */
  --border-strong: var(--ink-300);/* input edges, hover emphasis                */
  --text: var(--ink-900);         /* primary reading colour                     */
  --text-secondary: var(--ink-700);
  --text-muted: var(--ink-500);   /* supporting copy, never body text           */

  /*
   * Domain status. One token set for the post/automation lifecycle so the Queue,
   * Calendar, Automations, Create Post and Dashboard cannot drift apart. Status
   * is never colour alone: every chip carries a dot AND a label.
   *
   * Deliberately restrained: only "published" earns green, so a healthy screen
   * is calm and a real problem is the only thing shouting.
   */
  --st-draft-bg: var(--ink-100);   --st-draft-fg: var(--ink-600);  --st-draft-bd: var(--ink-200);
  --st-sched-bg: #161b30;          --st-sched-fg: #a5b4fc;         --st-sched-bd: #2d3557;
  --st-active-bg: var(--info-bg);  --st-active-fg: var(--info-fg);  --st-active-bd: var(--info-bd);
  --st-done-bg: var(--ok-bg);      --st-done-fg: var(--ok-fg);      --st-done-bd: var(--ok-bd);
  --st-attn-bg: var(--warn-bg);    --st-attn-fg: var(--warn-fg);    --st-attn-bd: var(--warn-bd);
  --st-fail-bg: var(--err-bg);     --st-fail-fg: var(--err-fg);     --st-fail-bd: var(--err-bd);
  --st-off-bg: var(--ink-100);     --st-off-fg: var(--ink-500);     --st-off-bd: var(--ink-200);

  --radius: 12px;
  --radius-sm: 8px;
  --radius-lg: 16px;
  /* F2 shape scale: small controls 8-10, buttons/inputs 10-12, cards 14-18,
     modals 18-24. One rung per role so nothing is arbitrary. */
  --radius-control: 10px;
  --radius-card: 14px;
  --radius-modal: 20px;
  /* Depth on a near-black field comes from real shadow, not a faint grey wash. */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .45), 0 1px 3px rgba(0, 0, 0, .35);
  --shadow-md: 0 4px 14px rgba(0, 0, 0, .55);
  --shadow-lg: 0 14px 36px rgba(0, 0, 0, .65);

  --sidebar-w: 248px;
  --topbar-h: 56px;
  --font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --speed: 160ms;
}

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

/* --- base ----------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0;
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.5;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4 { margin: 0; color: var(--ink-900); line-height: 1.25; }
h1 { font-size: 1.5rem; font-weight: 700; letter-spacing: -.01em; }
h2 { font-size: 1.125rem; font-weight: 650; }
h3 { font-size: .95rem; font-weight: 650; }
p { margin: 0; }
a { color: var(--brand-600); }
img { max-width: 100%; }

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

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--surface); padding: .6rem 1rem; border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md);
}
.skip-link:focus { left: .5rem; top: .5rem; }

.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;
}

/* --- shell ---------------------------------------------------------------- */
/* The UA rule for [hidden] is display:none at the lowest specificity, so any
   component that sets its own `display` would otherwise ignore it. */
[hidden] { display: none !important; }

.shell { display: flex; min-height: 100%; }

.topbar {
  display: none;
  align-items: center; gap: .75rem;
  height: var(--topbar-h); padding: 0 .75rem;
  background: var(--surface); border-bottom: 1px solid var(--ink-200);
  position: sticky; top: 0; z-index: 30;
}

.brand {
  display: inline-flex; align-items: center; gap: .55rem;
  font-weight: 700; color: var(--ink-900); text-decoration: none;
  letter-spacing: -.01em;
}
.brand img { border-radius: 6px; }

/* --- sidebar -------------------------------------------------------------- */
.sidebar {
  width: var(--sidebar-w); flex: 0 0 var(--sidebar-w);
  background: var(--surface); border-right: 1px solid var(--ink-200);
  display: flex; flex-direction: column;
  position: sticky; top: 0; height: 100vh; padding: 1rem .75rem;
}
.sidebar-head { display: flex; align-items: center; justify-content: space-between; padding: .25rem .5rem 1rem; }
/*
 * The drawer close button is mobile-only.
 *
 * `!important` is deliberate and load-bearing here. The button carries both
 * .icon-btn and .drawer-close, and .icon-btn's `display:inline-flex` is
 * declared LATER in this file at equal specificity, so it won. The result was a
 * stray close control sitting in the desktop sidebar on every authenticated
 * page. The mobile media query re-shows it explicitly, also with !important, so
 * the pair stays symmetrical rather than one rule quietly out-ranking the other.
 */
.drawer-close { display: none !important; }

.nav-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; flex: 1; }
.nav-sublist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.nav-group-label {
  font-size: 11px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-400); padding: .85rem .75rem .3rem;
}
.nav-list > li:first-child.nav-group-label { padding-top: .15rem; }
.nav-link {
  display: flex; align-items: center; gap: .65rem;
  padding: .55rem .65rem; border-radius: var(--radius-sm);
  color: var(--ink-600); text-decoration: none; font-weight: 500; font-size: .9rem;
  transition: background var(--speed), color var(--speed);
}
.nav-link:hover { background: var(--ink-100); color: var(--ink-900); }
.nav-link[aria-current="page"] { background: var(--brand-50); color: var(--brand-700); font-weight: 600; }
.nav-link .ico { width: 18px; height: 18px; flex: 0 0 18px; opacity: .9; }

.sidebar-foot { border-top: 1px solid var(--ink-200); padding-top: .75rem; margin-top: .5rem; }
.nav-user { display: flex; align-items: center; justify-content: space-between; gap: .5rem; padding: 0 .35rem; }
.nav-user-name { font-size: .8rem; color: var(--ink-500); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.scrim { display: none; }

/* --- main ----------------------------------------------------------------- */
.main { flex: 1; min-width: 0; padding: 1.75rem; }
.page { max-width: 1080px; margin: 0 auto; display: flex; flex-direction: column; gap: 1.25rem; }
.page-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.page-head .sub { color: var(--ink-500); font-size: .9rem; margin-top: .25rem; }
.page-actions { display: flex; gap: .5rem; flex-wrap: wrap; }

.grid { display: grid; gap: 1rem; }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.stack { display: flex; flex-direction: column; gap: 1rem; }
.row { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; }
.spacer { flex: 1; }

/* --- cards ---------------------------------------------------------------- */
/*
 * A card rests; it does not float. Elevation is reserved for things that
 * genuinely sit above the page (modal, drawer, popover, toast), so a screen full
 * of cards reads as one calm surface instead of a tray of floating panels.
 */
.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-card); padding: 1.25rem;
}
.card-head { display: flex; align-items: center; justify-content: space-between; gap: .75rem; margin-bottom: .85rem; }
.card-title { font-size: .95rem; font-weight: 650; color: var(--ink-900); }
.card-sub { font-size: .82rem; color: var(--ink-500); }
.card-hover { transition: border-color var(--speed), background var(--speed); }
.card-hover:hover { border-color: var(--border-strong); background: var(--surface-2); }

.stat { display: flex; flex-direction: column; gap: .3rem; }
.stat-value { font-size: 1.75rem; font-weight: 700; color: var(--ink-900); letter-spacing: -.02em; }
.stat-label { font-size: .8rem; color: var(--ink-500); }

/* --- buttons -------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .45rem;
  padding: .55rem .95rem; border-radius: var(--radius-control);
  font-size: .875rem; font-weight: 600; font-family: inherit;
  border: 1px solid transparent; cursor: pointer;
  /* A real touch target on every surface, without changing visual height. */
  min-height: 38px;
  /* A button is a button even when it is an <a>. Without this the global anchor
     underline leaks onto every link-styled action in the app. */
  text-decoration: none;
  transition: background var(--speed), border-color var(--speed), color var(--speed);
}
.btn:disabled { opacity: .55; cursor: not-allowed; }
.btn-primary { background: var(--brand-600); color: #fff; }
.btn-primary:hover:not(:disabled) { background: var(--brand-700); }
.btn-secondary { background: var(--surface); color: var(--ink-700); border-color: var(--ink-300); }
.btn-secondary:hover:not(:disabled) { background: var(--ink-100); }
.btn-ghost { background: transparent; color: var(--ink-600); }
.btn-ghost:hover:not(:disabled) { background: var(--ink-100); color: var(--ink-900); }
.btn-danger { background: var(--surface); color: var(--err-fg); border-color: var(--err-bd); }
.btn-danger:hover:not(:disabled) { background: var(--err-bg); }
.btn-success { background: #059669; color: #fff; }
.btn-success:hover:not(:disabled) { background: #047857; }
.btn-sm { padding: .35rem .6rem; font-size: .8rem; }
.btn-block { width: 100%; }

.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; border-radius: var(--radius-sm);
  border: 1px solid transparent; background: transparent; cursor: pointer;
  color: var(--ink-700); font-size: 1rem;
}
.icon-btn:hover { background: var(--ink-100); }
.hamburger, .hamburger::before, .hamburger::after {
  display: block; width: 18px; height: 2px; background: currentColor; border-radius: 2px;
}
.hamburger { position: relative; }
.hamburger::before, .hamburger::after { content: ''; position: absolute; left: 0; }
.hamburger::before { top: -6px; }
.hamburger::after { top: 6px; }

/* --- forms ---------------------------------------------------------------- */
.field { display: flex; flex-direction: column; gap: .35rem; }
/*
 * Vertical rhythm BETWEEN form blocks. A .field only spaces its own label/input/
 * hint; stacked directly in a card, one field's hint ended up almost touching the
 * next field's label. This gives every form the same breathing room without
 * changing how cards lay out generally.
 */
.field + .field,
.field + .grid, .grid + .field,
.field + fieldset, fieldset + .field { margin-top: .95rem; }
/* ...but a grid already spaces its own cells. Without this reset the stacked
   rhythm also applies to columns, so every field after the first in a row sat
   15px lower than its neighbour and the row lost its shared baseline. */
.grid > .field + .field,
.grid > .field + .grid,
.grid > .field + fieldset,
.grid > fieldset + .field { margin-top: 0; }
.label { font-size: .82rem; font-weight: 600; color: var(--ink-700); }
.hint { font-size: .75rem; color: var(--ink-500); }
.input, .select, .textarea {
  width: 100%; padding: .6rem .75rem; font-size: .875rem; font-family: inherit;
  color: var(--text); background: var(--surface);
  border: 1px solid var(--border-strong); border-radius: var(--radius-control);
  transition: border-color var(--speed), box-shadow var(--speed);
  /* A native select computes its own line-height and came out 2px taller than
     the input beside it. Same declared box for every control, so a select and
     a text input sitting in one row are the same height. */
  box-sizing: border-box; line-height: 1.45;
}
/* Explicit height rather than appearance:none, which would strip the native
   dropdown arrow and leave no clue the control opens a list. */
.input, .select { height: 40px; }
.input:focus, .select:focus, .textarea:focus {
  outline: none; border-color: var(--brand-500);
  box-shadow: 0 0 0 3px var(--brand-100);
}
.input:disabled, .select:disabled, .textarea:disabled { background: var(--ink-100); color: var(--ink-400); }
.textarea { resize: vertical; min-height: 90px; }
.input[aria-invalid="true"], .select[aria-invalid="true"], .textarea[aria-invalid="true"] {
  border-color: var(--err-bd); box-shadow: 0 0 0 3px var(--err-bg);
}
.field-error { font-size: .75rem; color: var(--err-fg); }
.check { display: flex; align-items: flex-start; gap: .5rem; font-size: .85rem; color: var(--ink-700); }
.check input { margin-top: .2rem; }
.color-row { display: flex; align-items: center; gap: .5rem; }
.color-row input[type="color"] {
  width: 42px; height: 34px; padding: 2px; border: 1px solid var(--ink-300);
  border-radius: var(--radius-sm); background: var(--surface); cursor: pointer;
}
fieldset { border: 0; margin: 0; padding: 0; }
legend { padding: 0; }

/* --- badges / chips ------------------------------------------------------- */
.badge {
  display: inline-flex; align-items: center; gap: .3rem;
  padding: .15rem .5rem; border-radius: 999px;
  font-size: .72rem; font-weight: 600; border: 1px solid transparent;
}
.badge::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.badge-ok { background: var(--ok-bg); color: var(--ok-fg); border-color: var(--ok-bd); }
.badge-warn { background: var(--warn-bg); color: var(--warn-fg); border-color: var(--warn-bd); }
.badge-err { background: var(--err-bg); color: var(--err-fg); border-color: var(--err-bd); }
.badge-info { background: var(--info-bg); color: var(--info-fg); border-color: var(--info-bd); }
.badge-neutral { background: var(--ink-100); color: var(--ink-600); border-color: var(--ink-200); }
.badge-plain::before { display: none; }

/*
 * F2 status chip — ONE component for the whole post/automation lifecycle, driven
 * by a data-status attribute. Queue, Calendar, Automations, Create Post and the
 * Dashboard all render status through this, so the same state can never look
 * different in two places. Colour is never the only signal: every chip has a dot
 * and a written label.
 *
 * Only "published" is green. Partial success is deliberately an ATTENTION state,
 * never a success one, because one account failing is not a success.
 */
.status {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .2rem .6rem; border-radius: 999px; white-space: nowrap;
  font-size: .75rem; font-weight: 600; border: 1px solid;
  background: var(--st-draft-bg); color: var(--st-draft-fg); border-color: var(--st-draft-bd);
}
.status::before {
  content: ''; width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; flex: none;
}
.status[data-status="scheduled"], .status[data-status="queued"] {
  background: var(--st-sched-bg); color: var(--st-sched-fg); border-color: var(--st-sched-bd);
}
.status[data-status="publishing"], .status[data-status="submitted"],
.status[data-status="reconciling"], .status[data-status="processing"] {
  background: var(--st-active-bg); color: var(--st-active-fg); border-color: var(--st-active-bd);
}
.status[data-status="published"], .status[data-status="approved"] {
  background: var(--st-done-bg); color: var(--st-done-fg); border-color: var(--st-done-bd);
}
.status[data-status="partial"], .status[data-status="attention_needed"],
.status[data-status="retry_scheduled"], .status[data-status="retrying"],
.status[data-status="waiting_approval"], .status[data-status="needs_review"],
.status[data-status="review"], .status[data-status="partially_queued"] {
  background: var(--st-attn-bg); color: var(--st-attn-fg); border-color: var(--st-attn-bd);
}
/* A hard failure and an explicit rejection both mean "this will not go out". */
.status[data-status="failed"], .status[data-status="generation_failed"],
.status[data-status="rejected"] {
  background: var(--st-fail-bg); color: var(--st-fail-fg); border-color: var(--st-fail-bd);
}
.status[data-status="generating"] {
  background: var(--st-active-bg); color: var(--st-active-fg); border-color: var(--st-active-bd);
}
.status[data-status="cancelled"], .status[data-status="paused"],
.status[data-status="skipped"], .status[data-status="disabled"],
.status[data-status="archived"] {
  background: var(--st-off-bg); color: var(--st-off-fg); border-color: var(--st-off-bd);
}

.chip {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .25rem .6rem; border-radius: 999px;
  background: var(--ink-100); border: 1px solid var(--ink-200);
  font-size: .8rem; color: var(--ink-700);
}
.chip button {
  border: 0; background: transparent; cursor: pointer; color: var(--ink-500);
  font-size: .9rem; line-height: 1; padding: 0;
}
.chip button:hover { color: var(--err-fg); }
.chips { display: flex; flex-wrap: wrap; gap: .4rem; }

/* --- notices -------------------------------------------------------------- */
.notice {
  display: flex; gap: .6rem; align-items: flex-start;
  padding: .7rem .85rem; border-radius: var(--radius-sm);
  border: 1px solid; font-size: .85rem;
}
.notice-ok { background: var(--ok-bg); color: var(--ok-fg); border-color: var(--ok-bd); }
.notice-warn { background: var(--warn-bg); color: var(--warn-fg); border-color: var(--warn-bd); }
.notice-err { background: var(--err-bg); color: var(--err-fg); border-color: var(--err-bd); }
.notice-info { background: var(--info-bg); color: var(--info-fg); border-color: var(--info-bd); }

/* --- empty / error states ------------------------------------------------- */
.empty {
  display: flex; flex-direction: column; align-items: center; gap: .5rem;
  padding: 2.25rem 1.25rem; text-align: center;
  border: 1px dashed var(--ink-300); border-radius: var(--radius); background: var(--surface);
}
.empty-title { font-weight: 650; color: var(--ink-800); }
.empty-sub { font-size: .85rem; color: var(--ink-500); max-width: 46ch; }

/* --- skeletons ------------------------------------------------------------ */
.skeleton {
  background: linear-gradient(90deg, var(--ink-100) 25%, var(--ink-200) 37%, var(--ink-100) 63%);
  background-size: 400% 100%;
  animation: shimmer 1.2s ease-in-out infinite;
  border-radius: var(--radius-sm);
}
.skeleton-line { height: 12px; margin-bottom: .5rem; }
.skeleton-card { height: 96px; }
@keyframes shimmer { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }

/* --- toasts --------------------------------------------------------------- */
/*
 * Above the modal host (70) and the drawer (60), not level with the drawer.
 * A toast is the app answering the user; at z-index 60 the answer to an action
 * taken inside a dialog was painted behind that dialog, so the result of the
 * thing they just did was invisible until they dismissed it.
 */
.toasts {
  position: fixed; right: 1rem; bottom: 1rem; z-index: 90;
  display: flex; flex-direction: column; gap: .5rem; max-width: min(92vw, 380px);
}
.toast {
  display: flex; align-items: flex-start; gap: .6rem;
  padding: .7rem .85rem; border-radius: var(--radius-sm);
  background: var(--surface); border: 1px solid var(--ink-200); box-shadow: var(--shadow-lg);
  font-size: .85rem; animation: toast-in var(--speed) ease-out;
}
.toast-ok { border-left: 3px solid #16a34a; }
.toast-err { border-left: 3px solid #dc2626; }
.toast-info { border-left: 3px solid var(--brand-600); }
.toast .toast-close { margin-left: auto; border: 0; background: transparent; cursor: pointer; color: var(--ink-400); }
@keyframes toast-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

/* --- modal ---------------------------------------------------------------- */
.modal-host {
  position: fixed; inset: 0; z-index: 70;
  display: flex; align-items: center; justify-content: center; padding: 1rem;
  background: rgba(15, 23, 42, .45);
  /* A modal taller than the viewport scrolls rather than clipping at top/bottom. */
  overflow-y: auto;
}
.modal {
  background: var(--surface-raised); border-radius: var(--radius-modal); box-shadow: var(--shadow-lg);
  width: min(94vw, 440px); padding: 1.5rem; display: flex; flex-direction: column; gap: .9rem;
}
.modal-actions { display: flex; justify-content: flex-end; gap: .5rem; }

/* --- progress steps ------------------------------------------------------- */
.steps { display: flex; align-items: center; gap: .5rem; }
.step-dot {
  display: flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 50%;
  font-size: .75rem; font-weight: 700;
  background: var(--ink-100); color: var(--ink-500); border: 1px solid var(--ink-200);
}
.step-dot[data-state="active"] { background: var(--brand-600); color: #fff; border-color: var(--brand-600); }
.step-dot[data-state="done"] { background: var(--ok-bg); color: var(--ok-fg); border-color: var(--ok-bd); }
.step-bar { flex: 1; height: 2px; background: var(--ink-200); border-radius: 2px; }
.step-label { font-size: .8rem; color: var(--ink-500); font-weight: 600; }

.progress { height: 6px; background: var(--ink-200); border-radius: 999px; overflow: hidden; }
.progress > span { display: block; height: 100%; background: var(--brand-600); transition: width var(--speed); }

/* --- auth / onboarding centred layout ------------------------------------- */
.centered {
  min-height: 100vh; display: flex; align-items: center; justify-content: center;
  padding: 1.5rem; background: var(--ink-50);
}
/*
 * Percentage of the CONTAINER, not of the viewport.
 *
 * This was `min(94vw, 420px)`, which overflowed on a phone: .centered adds
 * 1.5rem of padding each side, and a vw unit knows nothing about it, so at
 * 390px the card asked for 366px inside a 342px box and hung off the right
 * edge. width:100% respects the padding at every size.
 */
.auth-card { width: 100%; max-width: 420px; }

/* --- auth split ------------------------------------------------------------ */
/*
 * Desktop: a restrained brand field beside the form. Mobile: the panel is gone
 * entirely (display:none, not merely stacked) so a phone shows the form without
 * scrolling past decoration to reach it.
 */
.auth-split { display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr); gap: 3rem; align-items: center; width: 100%; max-width: 1040px; }
.auth-split .auth-card { width: 100%; max-width: 420px; justify-self: center; }

.auth-brand {
  position: relative; overflow: hidden; border-radius: var(--radius-lg);
  background: linear-gradient(155deg, #10231a 0%, #0b1a13 100%);
  padding: 2.5rem 2.25rem; min-height: 27rem; display: flex; align-items: center;
}
/* The same faint-grid idea the creatives use: it stops a large field reading as
   an unfinished slab, without adding a decorative shape. */
.auth-brand-grid {
  position: absolute; inset: 0; pointer-events: none; color: #fff; opacity: .05;
  background-image:
    repeating-linear-gradient(to right, currentColor 0 1px, transparent 1px 64px),
    repeating-linear-gradient(to bottom, currentColor 0 1px, transparent 1px 64px);
}
.auth-brand-inner { position: relative; z-index: 1; display: flex; flex-direction: column; gap: 1.5rem; }
.auth-brand-lockup { display: flex; align-items: center; gap: .7rem; color: #fff; font-size: 1.05rem; font-weight: 700; letter-spacing: -.01em; }
/* The measure is set so this lede breaks to three balanced lines. At a narrower
   measure it stranded "them." alone on a fourth, which is the one-word-final-
   line defect the creative rules reject on a card and which looks no better
   here. text-wrap:balance evens the lines where the browser supports it. */
.auth-brand-lede {
  margin: 0; color: #fff; font-size: 1.55rem; line-height: 1.3; font-weight: 700;
  letter-spacing: -.02em; max-width: 24ch; text-wrap: balance;
}
.auth-points { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 1rem; }
.auth-points li { display: flex; gap: .75rem; align-items: flex-start; }
.auth-points li > div { display: flex; flex-direction: column; gap: .15rem; }
.auth-point-tick {
  flex: 0 0 18px; width: 18px; height: 18px; margin-top: .18rem; border-radius: 5px;
  background: var(--brand-500); position: relative;
}
.auth-point-tick::after {
  content: ""; position: absolute; left: 5px; top: 4px; width: 5px; height: 9px;
  border-right: 2px solid #0b1a13; border-bottom: 2px solid #0b1a13; transform: rotate(45deg);
}
.auth-point-title { color: #fff; font-size: .9rem; font-weight: 650; }
.auth-point-body { color: rgba(255, 255, 255, .62); font-size: .82rem; line-height: 1.45; }
.auth-reassure { margin: 0; font-size: .78rem; line-height: 1.5; color: var(--ink-500); }

@media (max-width: 900px) {
  .auth-split { grid-template-columns: minmax(0, 1fr); max-width: 420px; gap: 0; }
  /* Not stacked — removed. A phone should see the form, not a hero. */
  .auth-brand { display: none; }
}
.wizard { width: min(96vw, 880px); margin: 0 auto; display: flex; flex-direction: column; gap: 1.25rem; }

/* --- choice cards --------------------------------------------------------- */
.choice {
  display: flex; flex-direction: column; gap: .6rem; align-items: flex-start;
  padding: 1.25rem; border: 1px solid var(--ink-200); border-radius: var(--radius);
  background: var(--surface); box-shadow: var(--shadow-sm);
  transition: border-color var(--speed), box-shadow var(--speed);
}
.choice:hover { border-color: var(--brand-500); box-shadow: var(--shadow-md); }
.choice h3 { font-size: 1rem; }
.choice p { font-size: .85rem; color: var(--ink-500); }

/* --- provider cards ------------------------------------------------------- */
.provider-head { display: flex; align-items: center; gap: .7rem; }
.provider-icon { width: 34px; height: 34px; border-radius: 8px; flex: 0 0 34px; }
.account-row {
  display: flex; align-items: center; gap: .75rem; justify-content: space-between;
  padding: .65rem 0; border-top: 1px solid var(--ink-200); flex-wrap: wrap;
}
.account-name { font-weight: 600; font-size: .875rem; color: var(--ink-900); }
.account-meta { font-size: .75rem; color: var(--ink-500); }

/* --- brand preview -------------------------------------------------------- */
.brand-preview { border-radius: var(--radius); overflow: hidden; border: 1px solid var(--ink-200); }
.brand-preview-head { padding: 1.25rem; color: #fff; display: flex; align-items: center; gap: .75rem; }
/* A customer's logo keeps a white bed: most marks are drawn for white paper, and
   a dark tile behind a dark logo would hide it. This is their asset, not chrome. */
.brand-preview-logo { width: 40px; height: 40px; object-fit: contain; background: #fff; border-radius: 8px; padding: 4px; }
.brand-preview-body { padding: 1rem 1.25rem; background: var(--surface); }
.swatch { width: 100%; height: 34px; border-radius: var(--radius-sm); border: 1px solid var(--ink-200); }
.logo-preview {
  width: 100%; height: 120px; object-fit: contain; background: var(--ink-50);
  border: 1px solid var(--ink-200); border-radius: var(--radius-sm); padding: .5rem;
}
/* Shown when a business has no logo. The slot stays the same size so adding a
   logo does not shift the layout, and it never borrows the Cyflow app mark to
   fill itself. */
.logo-preview-empty {
  width: 100%; height: 120px; display: flex; align-items: center; justify-content: center;
  background: var(--ink-50); border: 1px dashed var(--ink-300);
  border-radius: var(--radius-sm); color: var(--ink-500); font-size: 13px;
}

/* --- weekly rhythm preview ------------------------------------------------ */
/*
 * Seven days, side by side, so the shape of the week is legible at a glance.
 * It wraps rather than scrolls on a phone: a horizontal scroller here would
 * hide days behind a gesture nobody knows to make.
 */
.rhythm-week {
  display: grid; grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: .4rem; margin-top: .85rem;
}
.rhythm-day {
  display: flex; flex-direction: column; gap: .3rem; min-width: 0;
  padding: .6rem .5rem; border-radius: var(--radius-sm);
  background: var(--brand-50); border: 1px solid var(--brand-200);
}
.rhythm-day.is-off { background: var(--ink-50); border-color: var(--ink-200); opacity: .7; }
.rhythm-day-name {
  font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--brand-700);
}
.rhythm-day.is-off .rhythm-day-name { color: var(--ink-500); }
.rhythm-day-pillar { font-size: 12px; line-height: 1.35; color: var(--ink-700); font-weight: 500; }

@media (max-width: 900px) {
  .rhythm-week { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 420px) {
  .rhythm-week { grid-template-columns: minmax(0, 1fr); }
}

/* --- 404 ------------------------------------------------------------------ */
.notfound { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 1.5rem; }
.notfound-inner { text-align: center; max-width: 30rem; display: flex; flex-direction: column; align-items: center; gap: .35rem; }
.notfound-inner img { margin-bottom: .75rem; }
.notfound-code {
  font-size: 13px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  color: var(--brand-600); margin: 0;
}
.notfound-inner h1 { margin: .15rem 0 0; font-size: 1.6rem; }
.notfound-inner .sub { margin: 0 0 1.15rem; }

/* --- tabs ----------------------------------------------------------------- */
/*
 * A filter strip scrolls WITHIN ITSELF. Left as a plain flex row it grew wider
 * than a phone and pushed the whole page sideways (112px of horizontal overflow
 * on the Queue at 390px), which is a page-level bug, not a tab-level one.
 */
.tabs {
  display: flex; gap: .25rem; border-bottom: 1px solid var(--border);
  max-width: 100%; overflow-x: auto; scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
}
.tab {
  padding: .5rem .8rem; border: 0; background: transparent; cursor: pointer;
  font-size: .85rem; font-weight: 600; color: var(--text-muted);
  border-bottom: 2px solid transparent; font-family: inherit;
  flex: 0 0 auto; white-space: nowrap; min-height: 40px;
}
.tab[aria-selected="true"] { color: var(--brand-700); border-bottom-color: var(--brand-600); }

/* --- table / list --------------------------------------------------------- */
/* A row of content INSIDE a card. Pure layout: the card is already the
   surface, so a border/background here would draw a second identical box
   around every list entry. */
.list-item {
  display: flex; gap: .9rem; align-items: flex-start;
}
@media (max-width: 640px) {
  /* Actions drop below the content instead of crushing the title into a
     three-line wrap beside them. */
  .list-item { flex-wrap: wrap; }
  .list-item > .row:last-child { width: 100%; }
}
.thumb { width: 64px; height: 64px; border-radius: var(--radius-sm); object-fit: cover; background: var(--ink-100); flex: 0 0 64px; }
.thumb-empty { border: 1px dashed var(--ink-200); }

/* --- inline choice (checkbox rows) --------------------------------------- */
.choice-inline {
  display: flex; align-items: center; gap: .55rem; cursor: pointer;
  padding: .45rem .6rem; border: 1px solid var(--ink-200); border-radius: var(--radius-sm);
  background: var(--surface); font-size: .88rem;
}
.choice-inline:hover { border-color: var(--brand-500); }
.choice-inline input { accent-color: var(--brand-600); width: 16px; height: 16px; flex: 0 0 16px; }
.choice-inline:focus-within { box-shadow: 0 0 0 3px var(--brand-100); border-color: var(--brand-600); }

/* --- generated image preview --------------------------------------------- */
.image-preview {
  display: block; max-width: 100%; height: auto; margin-top: .8rem;
  border: 1px solid var(--ink-200); border-radius: var(--radius); background: var(--ink-50);
}

/* The frame reserves the render's aspect ratio up front, so the page never
   reflows when the image arrives. */
.preview { margin-top: .9rem; display: flex; flex-direction: column; gap: .6rem; max-width: 520px; }
.preview-frame {
  position: relative; width: 100%; overflow: hidden;
  border: 1px solid var(--ink-200); border-radius: var(--radius);
  background: var(--ink-100);
  display: flex; align-items: center; justify-content: center;
}
.preview-square { aspect-ratio: 1 / 1; }
.preview-portrait { aspect-ratio: 4 / 5; }
.preview-landscape { aspect-ratio: 40 / 21; }
.preview-img { display: block; width: 100%; height: 100%; object-fit: contain; }
.preview-frame.is-loading::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, var(--ink-100) 25%, var(--ink-50) 37%, var(--ink-100) 63%);
  background-size: 400% 100%; animation: shimmer 1.4s ease-in-out infinite;
}
.preview-failed { padding: 1rem; text-align: center; color: var(--err-fg); font-size: .85rem; }
.preview-meta { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }

/* --- calendar ------------------------------------------------------------- */
.cal-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 4px; }
.cal-head {
  text-align: center; font-size: .72rem; font-weight: 700; color: var(--ink-500);
  text-transform: uppercase; letter-spacing: .04em; padding: .35rem 0;
}
.cal-day {
  min-height: 84px; border: 1px solid var(--ink-200); border-radius: var(--radius-sm);
  background: var(--surface); padding: .35rem; text-align: left; cursor: pointer; font-family: inherit;
  display: flex; flex-direction: column; gap: .2rem;
}
.cal-day:hover { border-color: var(--brand-500); }
.cal-day[aria-pressed="true"] { border-color: var(--brand-600); box-shadow: 0 0 0 2px var(--brand-100); }
.cal-day[data-outside="true"] { background: var(--ink-50); color: var(--ink-400); }
.cal-daynum { font-size: .75rem; font-weight: 600; color: var(--ink-600); }
.cal-day[data-today="true"] .cal-daynum {
  background: var(--brand-600); color: #fff; border-radius: 999px;
  width: 20px; height: 20px; display: inline-flex; align-items: center; justify-content: center;
}
.cal-pill {
  font-size: .65rem; padding: .1rem .3rem; border-radius: 4px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  border: 1px solid;
}
.cal-pill-queued { background: var(--ok-bg); color: var(--ok-fg); border-color: var(--ok-bd); }
.cal-pill-draft { background: var(--ink-100); color: var(--ink-600); border-color: var(--ink-200); }
.cal-pill-published { background: var(--info-bg); color: var(--info-fg); border-color: var(--info-bd); }
.cal-pill-failed { background: var(--err-bg); color: var(--err-fg); border-color: var(--err-bd); }

/* --- planner board -------------------------------------------------------- */
.planner-bulk {
  position: sticky; top: 0; z-index: 20;
  padding: .6rem .75rem; margin-bottom: .75rem;
  background: rgba(255, 255, 255, .92); backdrop-filter: blur(6px);
  border: 1px solid var(--ink-200); border-radius: var(--radius);
}
.planner-day { margin-bottom: 1.25rem; }
.planner-day-head {
  display: flex; align-items: baseline; gap: .6rem;
  padding-bottom: .45rem; margin-bottom: .7rem;
  border-bottom: 1px solid var(--ink-200);
}
.planner-day-title { font-size: .95rem; font-weight: 700; color: var(--ink-900); letter-spacing: -.01em; }
.planner-day-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: .75rem; }

.planner-card {
  display: flex; flex-direction: column; gap: .55rem;
  padding: .8rem; background: var(--surface);
  border: 1px solid var(--ink-200); border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  transition: border-color var(--speed), box-shadow var(--speed);
}
.planner-card:hover { border-color: var(--brand-500); box-shadow: var(--shadow-md); }
.planner-card[data-status="approved"] { border-left: 3px solid var(--ok-fg); }
.planner-card[data-status="queued"] { border-left: 3px solid var(--brand-600); }
.planner-card[data-status="needs_review"] { border-left: 3px solid var(--warn-fg); }
/* Rejected cards stay readable but visibly out of the running. */
.planner-card.is-rejected { opacity: .55; }
.planner-card.is-rejected .planner-thumb { filter: grayscale(1); }

.planner-card-head { display: flex; align-items: center; gap: .5rem; }
.card-select { width: 16px; height: 16px; flex: 0 0 16px; accent-color: var(--brand-600); }
.planner-time { font-size: .78rem; font-weight: 700; color: var(--ink-600); }
.planner-card-body { display: flex; gap: .7rem; align-items: flex-start; }
.planner-thumb {
  width: 84px; height: 84px; flex: 0 0 84px;
  border-radius: var(--radius-sm); object-fit: cover; background: var(--ink-100);
}
.planner-thumb-empty {
  display: flex; align-items: center; justify-content: center;
  border: 1px dashed var(--ink-200);
}
.thumb-note { font-size: .65rem; color: var(--ink-400); }
/* A failed render is visually distinct from a not-yet-rendered one, and names
   the reason ("HCTI · Credits exhausted") rather than a bare "No image". */
.planner-thumb-failed {
  flex-direction: column; gap: 2px; padding: 4px;
  border-color: var(--danger-400, #e11d48);
  background: var(--danger-50, #fff1f2);
}
.thumb-note-err { color: var(--danger-600, #be123c); font-weight: 700; }
.thumb-sub { font-size: .58rem; color: var(--danger-600, #be123c); text-align: center; line-height: 1.15; }
.planner-image-failure .planner-failure-summary { color: var(--danger-700, #9f1239); }
.drawer-preview-failed { flex-direction: column; gap: .35rem; padding: .75rem; text-align: center; border-color: var(--danger-400, #e11d48); }
.planner-card-text { min-width: 0; display: flex; flex-direction: column; gap: .3rem; }
.planner-template { font-size: .7rem; color: var(--ink-500); }
.planner-headline { font-size: .88rem; font-weight: 700; color: var(--ink-900); line-height: 1.25; }
.planner-caption { font-size: .78rem; color: var(--ink-500); line-height: 1.4; }
.planner-meta { font-size: .7rem; color: var(--ink-400); }
.planner-warning {
  font-size: .72rem; padding: .4rem .55rem; border-radius: var(--radius-sm);
  background: var(--warn-bg); color: var(--warn-fg); border: 1px solid var(--warn-bd);
}
.planner-card-actions { display: flex; gap: .35rem; flex-wrap: wrap; }

/*
 * Why a post failed.
 *
 * The sentence is always visible; the measurements are one click away. A failed
 * post used to say nothing at all here, so the only way to find out why was to
 * read quality_failures_json in the database.
 */
.planner-failure {
  display: flex; flex-direction: column; gap: .3rem;
  padding: .45rem .55rem; border-radius: var(--radius-sm);
  background: var(--err-bg); color: var(--err-fg); border: 1px solid var(--err-bd);
}
.planner-failure-summary { font-size: .74rem; font-weight: 600; margin: 0; }
.planner-failure-detail > summary {
  font-size: .7rem; cursor: pointer; list-style: revert;
  /* The row is small; give the pointer something honest to land on. */
  padding: .1rem 0;
}
.planner-failure-detail > summary:focus-visible {
  outline: 2px solid currentColor; outline-offset: 2px; border-radius: 2px;
}
.planner-failure-list {
  margin: .3rem 0 0; padding-left: 1.1rem;
  font-size: .7rem; line-height: 1.45;
  /* Validator output, verbatim: never re-wrapped into a paragraph, because each
     line is one specific measurement the user may need to act on. */
  display: flex; flex-direction: column; gap: .15rem;
}

/* --- timezone combobox ---------------------------------------------------- */
.tz-field { position: relative; }
.tz-list {
  position: absolute; top: 100%; left: 0; right: 0; z-index: 30;
  margin-top: 4px; max-height: 280px; overflow-y: auto;
  list-style: none; padding: .25rem;
  background: var(--surface); border: 1px solid var(--ink-200); border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
}
.tz-option {
  display: flex; align-items: center; gap: .5rem;
  padding: .45rem .55rem; border-radius: var(--radius-sm); cursor: pointer;
  font-size: .82rem;
}
.tz-option:hover, .tz-option:focus { background: var(--brand-50); outline: none; }
.tz-option.is-selected { background: var(--brand-50); font-weight: 600; }
.tz-id { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink-900); }
.tz-offset { flex: 0 0 auto; font-variant-numeric: tabular-nums; color: var(--ink-500); font-size: .78rem; }
.tz-empty { padding: .6rem; font-size: .82rem; color: var(--ink-500); }

/* --- plan summary --------------------------------------------------------- */
.plan-summary { display: flex; flex-direction: column; gap: .5rem; }
.plan-summary-main strong { display: block; font-size: 1rem; color: var(--ink-900); letter-spacing: -.01em; }
.plan-summary-main p { margin-top: .25rem; font-size: .85rem; color: var(--ink-500); }

/* --- edit drawer ---------------------------------------------------------- */
.drawer {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 60;
  width: min(560px, 100vw);
  display: flex; flex-direction: column;
  background: var(--surface); border-left: 1px solid var(--ink-200);
  box-shadow: var(--shadow-lg);
}
.drawer-head {
  display: flex; align-items: center; gap: .5rem;
  padding: .9rem 1rem; border-bottom: 1px solid var(--ink-200);
}
.drawer-body { flex: 1; overflow-y: auto; padding: 1rem; display: flex; flex-direction: column; gap: .7rem; }
.drawer-section { font-size: .82rem; font-weight: 600; margin: .4rem 0 0; color: var(--text-secondary, #475569); }

/* --- media library (C3) --------------------------------------------------- */
.media-dropzone {
  border: 2px dashed var(--ink-200); border-radius: var(--radius);
  padding: 1.2rem; text-align: center; transition: border-color .15s, background .15s;
}
.media-dropzone.is-drag { border-color: var(--brand-primary, #1a844c); background: var(--brand-soft, rgba(26,132,76,.06)); }
.media-dropzone-title { margin: 0 0 .2rem; font-weight: 600; }
.media-upload-preview { margin-top: .8rem; display: flex; flex-direction: column; align-items: center; gap: .3rem; }
.media-upload-thumb { max-width: 220px; max-height: 220px; border-radius: var(--radius-sm); border: 1px solid var(--ink-200); }
.media-grid {
  display: grid; gap: 1rem; margin-top: 1rem;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
}
.media-tile {
  border: 1px solid var(--ink-200); border-radius: var(--radius);
  padding: .7rem; display: flex; flex-direction: column; gap: .5rem; background: var(--surface-solid, #fff);
}
.media-tile-frame {
  aspect-ratio: 1 / 1; border-radius: var(--radius-sm); overflow: hidden;
  background: var(--ink-50); display: flex; align-items: center; justify-content: center;
}
.media-tile-img { width: 100%; height: 100%; object-fit: cover; }
.media-tile-meta { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; justify-content: space-between; }
.media-picker { max-width: 720px; width: 92vw; }
.media-picker-grid { grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); max-height: 55vh; overflow-y: auto; }
.media-picker-tile {
  padding: 0; border: 2px solid transparent; border-radius: var(--radius-sm); overflow: hidden;
  cursor: pointer; background: var(--ink-50); aspect-ratio: 1 / 1;
}
.media-picker-tile:hover { border-color: var(--brand-primary, #1a844c); }
/* A real focus ring, not a colour-only border swap, so the keyboard position is
   unmistakable and not reliant on colour perception. */
.media-picker-tile:focus-visible {
  border-color: var(--brand-primary, #1a844c);
  outline: 2px solid var(--brand-primary, #1a844c); outline-offset: 2px;
}

/* --- per-platform editor (C2) -------------------------------------------- */
.pe { display: flex; flex-direction: column; gap: .5rem; }
.pe-tabs {
  display: flex; gap: .3rem; flex-wrap: nowrap; overflow-x: auto;
  border-bottom: 1px solid var(--ink-200); padding-bottom: .1rem;
}
.pe-tab {
  display: inline-flex; align-items: center; gap: .4rem; white-space: nowrap;
  padding: .45rem .7rem; border: 0; background: transparent; cursor: pointer;
  border-bottom: 2px solid transparent; color: var(--text-secondary, #475569);
  font-size: .85rem; border-radius: var(--radius-sm) var(--radius-sm) 0 0;
}
.pe-tab:hover { background: var(--ink-50); }
.pe-tab.is-active { color: var(--text-primary, #0f172a); border-bottom-color: var(--brand-primary, #1a844c); font-weight: 600; }
.pe-tab:focus-visible { outline: 2px solid var(--brand-primary, #1a844c); outline-offset: 2px; }
.pe-tab .platform-mark { border-radius: 5px; flex: none; }
.pe-tab-edited { color: var(--brand-primary, #1a844c); font-weight: 700; }
.pe-tab-dirty { color: var(--warn-fg, #92400e); font-weight: 700; }
.pe-panel { display: flex; flex-direction: column; gap: .4rem; }
.pe-copy { min-height: 8rem; resize: vertical; font: inherit; line-height: 1.5; }
.pe-measure { margin-top: .2rem; }
.pe-failures { display: flex; flex-direction: column; gap: .2rem; }
.pe-failure {
  margin: 0; font-size: .74rem; padding: .3rem .5rem; border-radius: var(--radius-sm);
  background: var(--err-bg, #fee2e2); color: var(--err-fg, #991b1b); border: 1px solid var(--err-bd, #fca5a5);
}

/* --- revision timeline (C2) ---------------------------------------------- */
.rev-timeline { border: 1px solid var(--ink-200); border-radius: var(--radius-sm); padding: .3rem .6rem; }
.rev-timeline > summary { cursor: pointer; font-size: .82rem; font-weight: 600; padding: .2rem 0; }
.rev-entries { list-style: none; margin: .4rem 0 0; padding: 0; display: flex; flex-direction: column; gap: .3rem; }
.rev-entry > summary {
  display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; cursor: pointer;
  font-size: .78rem; padding: .3rem 0;
}
.rev-entry .platform-mark { border-radius: 4px; flex: none; }
.rev-type { font-weight: 600; }
.rev-when { color: var(--text-muted, #94a3b8); font-size: .72rem; margin-left: auto; }
.rev-copy { margin: .3rem 0 0; font-size: .78rem; white-space: pre-wrap; color: var(--text-secondary, #475569); }
.rev-tags { margin: .2rem 0 0; font-size: .74rem; color: var(--brand-primary, #1a844c); }
.drawer-foot {
  display: flex; align-items: center; gap: .4rem; flex-wrap: wrap;
  padding: .8rem 1rem; border-top: 1px solid var(--ink-200); background: var(--ink-50);
}
.drawer-preview {
  width: 100%; max-width: 320px; aspect-ratio: 1 / 1; align-self: center;
  border-radius: var(--radius); border: 1px solid var(--ink-200);
  object-fit: contain; background: var(--ink-100);
}
.drawer-preview-empty { display: flex; align-items: center; justify-content: center; border-style: dashed; }

/* --- responsive ----------------------------------------------------------- */
@media (max-width: 900px) {
  .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 780px) {
  .topbar { display: flex; }
  .shell { display: block; }
  .main { padding: 1rem; }
  .grid-2, .grid-3, .grid-4 { grid-template-columns: minmax(0, 1fr); }

  /* The mobile topbar (sticky top:0, z-index:30) would otherwise paint over the
     sticky bulk bar. Park the bulk bar just below it so its actions stay visible
     when the board is scrolled. */
  .planner-bulk { top: var(--topbar-h); }

  .sidebar {
    position: fixed; inset: 0 auto 0 0; z-index: 50; height: 100dvh;
    transform: translateX(-100%); transition: transform var(--speed) ease-out;
    box-shadow: var(--shadow-lg);
  }
  .sidebar[data-open="true"] { transform: none; }
  .drawer-close { display: inline-flex !important; }
  .scrim { display: block; position: fixed; inset: 0; z-index: 40; background: rgba(15, 23, 42, .45); }
  .scrim[hidden] { display: none; }
  .cal-day { min-height: 60px; }

  /* The drawer becomes a full-height sheet rather than a side panel. */
  .drawer { width: 100vw; }
  .planner-day-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ==========================================================================
   F: Public marketing site — header, hero, sections, footer.
   Reuses the app's ink/brand tokens so the public and authenticated surfaces
   feel like one product. Full-width (not the centered app column).
   ========================================================================== */
.pub { display: flex; flex-direction: column; min-height: 100vh; }
.pub-main { flex: 1; }
.pub-container { max-width: 1080px; margin: 0 auto; padding: 0 1.5rem; }

/* header */
/* A translucent bed over the dark field — the same frosted effect, inverted, so
   the header stops reading as a leftover white bar on a black page. */
.pub-header { position: sticky; top: 0; z-index: 20; background: rgba(11, 13, 16, .82); backdrop-filter: saturate(1.2) blur(10px); border-bottom: 1px solid var(--ink-200); }
.pub-header-inner { max-width: 1080px; margin: 0 auto; padding: .7rem 1.5rem; display: flex; align-items: center; gap: 1.25rem; }
.pub-brand { display: inline-flex; align-items: center; gap: .5rem; font-weight: 700; color: var(--ink-900); text-decoration: none; }
.pub-nav { display: flex; gap: .35rem; margin-left: .5rem; }
.pub-nav-link { padding: .4rem .6rem; border-radius: var(--radius-sm); color: var(--ink-600); text-decoration: none; font-weight: 500; font-size: .9rem; }
.pub-nav-link:hover { background: var(--ink-100); color: var(--ink-900); }
.pub-nav-link.is-active { color: var(--brand-700); background: var(--brand-50); font-weight: 600; }
.pub-header-cta { margin-left: auto; display: flex; gap: .5rem; }
.pub-nav-mobile { margin-left: auto; display: none; position: relative; }
.pub-nav-toggle { list-style: none; cursor: pointer; padding: .4rem; border-radius: var(--radius-sm); }
.pub-nav-toggle::-webkit-details-marker { display: none; }
.pub-nav-drawer { position: absolute; right: 0; top: 2.4rem; background: var(--surface); border: 1px solid var(--ink-200); border-radius: var(--radius); box-shadow: var(--shadow-lg); padding: .5rem; display: flex; flex-direction: column; gap: .25rem; min-width: 200px; }
.pub-nav-drawer .btn { justify-content: center; }

/* hero */
.pub-hero { padding: 4.5rem 0 3rem; background: linear-gradient(180deg, var(--brand-50), var(--ink-50)); }
.pub-eyebrow { display: inline-block; font-size: .78rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--brand-700); margin-bottom: .75rem; }
.pub-h1 { font-size: clamp(1.9rem, 4vw, 3rem); letter-spacing: -.02em; max-width: 20ch; }
.pub-lead { margin-top: 1rem; font-size: 1.1rem; color: var(--ink-600); max-width: 60ch; line-height: 1.6; }
.pub-hero-cta { margin-top: 1.75rem; display: flex; gap: .75rem; flex-wrap: wrap; }

/* sections */
.pub-section { padding: 3.25rem 0; }
.pub-section-alt { background: var(--surface); border-top: 1px solid var(--ink-200); border-bottom: 1px solid var(--ink-200); }
.pub-h2 { font-size: clamp(1.4rem, 2.5vw, 1.9rem); letter-spacing: -.01em; }
.pub-section-lead { margin-top: .6rem; color: var(--ink-600); max-width: 60ch; }
.pub-grid { margin-top: 1.75rem; display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
.pub-card { background: var(--ink-50); border: 1px solid var(--ink-200); border-radius: var(--radius-lg); padding: 1.25rem; }
.pub-section-alt .pub-card { background: var(--surface); }
.pub-card-title { font-size: 1rem; font-weight: 650; color: var(--ink-900); }
.pub-card-body { margin-top: .5rem; color: var(--ink-600); font-size: .92rem; line-height: 1.55; }
.pub-list { margin-top: 1rem; padding-left: 1.1rem; color: var(--ink-700); line-height: 1.8; }
.pub-cta-row { margin-top: 1.75rem; }
.pub-prose { max-width: 62ch; color: var(--ink-700); line-height: 1.7; margin-top: 1rem; }
.pub-fineprint { margin-top: 1.5rem; color: var(--ink-500); font-size: .85rem; max-width: 62ch; }
.pub-mail { font-weight: 600; }
.pub-callout { background: var(--warn-bg); border: 1px solid var(--warn-bd); color: var(--warn-fg); border-radius: var(--radius); padding: .9rem 1rem; max-width: 70ch; }
.pub-legal-block { margin-top: 1.25rem; max-width: 66ch; }

/* platform chips */
.pub-platforms { display: flex; gap: .6rem; flex-wrap: wrap; }
.pub-platform-chip { padding: .5rem .9rem; border-radius: 999px; background: var(--surface); border: 1px solid var(--ink-200); font-weight: 600; font-size: .9rem; color: var(--ink-800); }

/* steps */
.pub-steps { margin-top: 1.5rem; list-style: none; padding: 0; display: grid; gap: 1rem; }
.pub-step { display: flex; gap: 1rem; align-items: flex-start; background: var(--ink-50); border: 1px solid var(--ink-200); border-radius: var(--radius-lg); padding: 1.1rem 1.25rem; }
.pub-step-num { flex: none; width: 2rem; height: 2rem; border-radius: 999px; background: var(--brand-600); color: #fff; display: grid; place-items: center; font-weight: 700; }

/* footer */
/* The footer used to invert against a white page. On a dark one it separates by
   being a shade DEEPER than the field, with a hairline, not by flipping. */
.pub-footer { background: #080a0d; color: var(--ink-600); border-top: 1px solid var(--ink-200); margin-top: 2rem; }
.pub-footer-inner { max-width: 1080px; margin: 0 auto; padding: 2.5rem 1.5rem; display: grid; grid-template-columns: 1.6fr repeat(4, 1fr); gap: 1.5rem; }
.pub-footer .pub-brand { color: #fff; }
.pub-foot-sub { margin-top: .6rem; color: var(--ink-400); font-size: .88rem; max-width: 34ch; line-height: 1.5; }
.pub-foot-col { display: flex; flex-direction: column; gap: .4rem; }
.pub-foot-title { font-size: .78rem; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-400); margin-bottom: .2rem; }
.pub-foot-link { color: var(--ink-200); text-decoration: none; font-size: .9rem; }
.pub-foot-link:hover { color: #fff; text-decoration: underline; }
.pub-footer-base { border-top: 1px solid var(--ink-700); }
.pub-footer-base > * { display: block; }
.pub-footer-base { max-width: 1080px; margin: 0 auto; padding: 1rem 1.5rem 2rem; color: var(--ink-400); font-size: .82rem; display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }

/* responsive */
@media (max-width: 860px) {
  .pub-grid { grid-template-columns: 1fr; }
  .pub-nav, .pub-header-cta { display: none; }
  .pub-nav-mobile { display: block; }
  .pub-footer-inner { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 520px) {
  .pub-footer-inner { grid-template-columns: 1fr; }
}

/* (The link-underline reset for buttons now lives on .btn itself, so the public
   site and the app share one rule instead of two.) */

/* G: settings danger zone. */
.card-danger { border-color: var(--err-bd); }
.card-title.danger { color: var(--err-fg); }

/* ==========================================================================
   F2: authenticated app surfaces — attention list, figures, and lanes.
   These are the shared shapes the redesigned routes compose from, so Dashboard,
   Queue, Automations and Calendar present the same information the same way.
   ========================================================================== */

/* A figure that leads somewhere. The whole card is the target, so the hit area
   is the card, not a small link inside it. */
.dash-figure { display: block; text-decoration: none; color: inherit; }
.dash-figure:hover { text-decoration: none; }
.dash-figure .stat-value { line-height: 1.1; }

/* "Needs your attention" — a real list of real problems, each with a way out. */
.attn-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .5rem; }
.attn-item {
  display: flex; align-items: center; gap: .7rem;
  padding: .6rem .75rem; border-radius: var(--radius-control);
  background: var(--warn-bg); border: 1px solid var(--warn-bd); color: var(--warn-fg);
}
.attn-dot { width: 8px; height: 8px; border-radius: 50%; background: currentColor; flex: none; }
.attn-text { flex: 1; min-width: 0; font-size: .875rem; font-weight: 500; }
.dash-allclear { color: var(--text-muted); font-size: .9rem; }

/* A "lane" is a scannable list of real rows: status, what it is, when. Used for
   upcoming posts, setup readiness and recent activity. */
.lane { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.lane-row {
  display: flex; align-items: center; gap: .7rem;
  padding: .65rem .25rem; border-bottom: 1px solid var(--border);
}
.lane-row:last-child { border-bottom: 0; }
.lane-title { font-weight: 600; color: var(--text); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lane-meta { font-size: .8rem; color: var(--text-muted); white-space: nowrap; }
.lane-when { font-size: .8rem; color: var(--text-muted); white-space: nowrap; }

@media (max-width: 640px) {
  /* Rows wrap instead of squeezing; the timestamp drops to its own line rather
     than being clipped. */
  .lane-row { flex-wrap: wrap; gap: .4rem .6rem; }
  .lane-row .spacer { display: none; }
  .lane-title { white-space: normal; flex: 1 1 100%; order: 2; }
  .lane-when, .lane-meta { order: 3; }
  /* The dot belongs beside the sentence it marks. Only the action drops to its
     own line; sending the text down too left the dot stranded on a line alone. */
  .attn-item { align-items: flex-start; flex-wrap: wrap; }
  .attn-dot { margin-top: .4rem; }
  .attn-item > .btn { flex: 1 0 100%; }
}

/* ==========================================================================
   AI Studio — a dark "design tool" surface, modeled on the studio aesthetic.
   Scoped entirely under .ai-studio so it never affects the rest of the app.
   ========================================================================== */
.ai-studio {
  --ais-bg: #0b0d10;
  --ais-panel: #14171c;
  --ais-panel2: #1b1f26;
  --ais-border: #2a2f38;
  --ais-fg: #e6e9ef;
  --ais-muted: #8b94a3;
  --ais-accent: #6ea8fe;
  background: var(--ais-bg);
  color: var(--ais-fg);
  border: 1px solid var(--ais-border);
  border-radius: 16px;
  padding: clamp(1rem, 2.5vw, 2rem);
}
.ai-studio .mono {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.ais-head { margin-bottom: 1.25rem; }
.ais-brand { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.ais-dot { width: 22px; height: 22px; border-radius: 7px; background: var(--ais-accent); display: inline-block; }
.ais-brand h1 { font-size: 1.2rem; font-weight: 700; margin: 0; color: var(--ais-fg); }
.ais-tag { font-size: 11px; color: var(--ais-muted); }
/*
 * The app shell styles `.sub` and small print in the marketing/link colour, and
 * this panel sits on a dark surface where that reads as a link. Every muted
 * string in the studio states its own colour and weight so nothing inherits an
 * accent from outside this component.
 */
.ai-studio .ais-sub,
.ai-studio .ais-hint { color: var(--ais-muted); font-weight: 400; }
.ais-sub { font-size: .9rem; margin: .6rem 0 0; max-width: 60ch; }

/* The analyze bar: a website in, a brand out. Full width above the two panels. */
.ais-urlbar { margin-bottom: 1.25rem; }
.ais-urlrow { display: flex; gap: .6rem; }
@media (max-width: 560px) { .ais-urlrow { flex-direction: column; } }
.ais-url { flex: 1 1 auto; min-width: 0; padding: .75rem .9rem; }
.ais-analyze { flex: 0 0 auto; padding: .75rem 1.6rem; }
.ais-analyze-note { margin: .5rem 0 0; min-height: 1em; }

/* --- what the website gave us ------------------------------------------------
   Everything the reader found, shown in full. It appears after an analyze, so it
   fades in rather than snapping the layout. */
.ais-brandcard {
  background: var(--ais-panel); border: 1px solid var(--ais-border); border-radius: 12px;
  padding: 1.1rem 1.2rem; margin-bottom: 1.25rem;
  animation: ais-rise .28s ease-out both;
}
@keyframes ais-rise { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .ais-brandcard { animation: none; } }

.ais-brandhead { display: flex; align-items: flex-start; gap: .9rem; margin-bottom: 1rem; }
.ais-logowrap { flex: 0 0 64px; }
.ais-logofield { flex: 1 1 auto; display: flex; flex-direction: column; gap: .3rem; min-width: 0; max-width: 460px; }

/* Two columns where the fields are short, one where the screen is narrow. */
.ais-fieldgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: .75rem .9rem; }

/* An editable chip: the value, and a quiet × that removes it. */
.ais-chip-edit { display: inline-flex; align-items: center; gap: .35rem; padding-right: .3rem; }
.ais-chip-x {
  background: none; border: none; color: var(--ais-muted); cursor: pointer;
  font-size: 1rem; line-height: 1; padding: 0 .15rem; border-radius: 4px;
}
.ais-chip-x:hover { color: var(--ais-fg); background: rgba(255, 255, 255, .08); }
.ais-addrow { display: flex; gap: .5rem; align-items: center; }
.ais-add { max-width: 320px; }
.ais-addbtn { padding: .55rem 1rem; font-size: .85rem; }

/* A quiet second action: present, but never competing with the primary one. */
.ais-secondary {
  background: transparent; color: var(--ais-fg);
  border: 1px solid var(--ais-border); font-weight: 600;
}
.ais-secondary:hover { border-color: var(--ais-accent); filter: none; }
.ais-file { display: none; }
.ais-logoactions { display: flex; gap: .5rem; margin-top: .1rem; }

/* The site's own photographs. Lazy — a dozen full-size images would otherwise
   hold up the panel they sit in. */
.ais-photos { display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: .5rem; }
.ais-photo {
  position: relative; aspect-ratio: 1 / 1; padding: 0; overflow: hidden; cursor: pointer;
  border: 1px solid var(--ais-border); border-radius: 10px; background: var(--ais-panel2);
  transition: border-color .15s ease, transform .15s ease, opacity .15s ease;
  opacity: .45;
}
.ais-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ais-photo:hover { transform: translateY(-2px); }
.ais-photo.is-on { opacity: 1; border-color: var(--ais-accent); }
.ais-photo-tick {
  position: absolute; top: 5px; right: 5px; width: 20px; height: 20px; border-radius: 50%;
  display: grid; place-items: center; font-size: .7rem; font-weight: 700;
  background: var(--ais-accent); color: #0b0d10; opacity: 0; transition: opacity .15s ease;
}
.ais-photo.is-on .ais-photo-tick { opacity: 1; }
/* A logo or icon says so, which explains why it did not start selected. */
.ais-photo-kind {
  position: absolute; left: 0; right: 0; bottom: 0; padding: .12rem 0;
  font-size: .58rem; letter-spacing: .06em; text-transform: uppercase; font-weight: 700;
  background: rgba(0, 0, 0, .62); color: var(--ais-muted);
}
/* The collapsed library: one row that says what it holds and opens on click. */
.ais-libbtn {
  display: flex; align-items: center; gap: .8rem; width: 100%; text-align: left;
  background: var(--ais-panel2); border: 1px solid var(--ais-border); border-radius: 10px;
  padding: .7rem .85rem; cursor: pointer; color: var(--ais-fg);
  transition: border-color .15s ease, transform .15s ease;
}
.ais-libbtn:hover { border-color: var(--ais-accent); transform: translateY(-1px); }
.ais-libicon { position: relative; width: 34px; height: 30px; flex: 0 0 34px; }
.ais-libicon span {
  position: absolute; border: 1.5px solid var(--ais-accent); border-radius: 4px;
  width: 22px; height: 22px; background: var(--ais-panel);
}
.ais-libicon span:nth-child(1) { left: 0; top: 0; opacity: .45; }
.ais-libicon span:nth-child(2) { left: 5px; top: 4px; opacity: .7; }
.ais-libicon span:nth-child(3) { left: 10px; top: 8px; }
.ais-libtext { display: flex; flex-direction: column; gap: .1rem; margin-right: auto; }
.ais-liblabel { font-weight: 600; font-size: .9rem; }
.ais-libcount { font-size: .78rem; color: var(--ais-muted); }
.ais-libpeek { display: flex; gap: .25rem; }
.ais-libpeek img { width: 30px; height: 30px; object-fit: cover; border-radius: 6px; border: 1px solid var(--ais-border); }
.ais-libopen { color: var(--ais-accent); font-size: .82rem; font-weight: 700; }

/* The library itself, over the page: the panel stays about the business. */
.ais-sheet { position: fixed; inset: 0; z-index: 60; display: grid; place-items: center; padding: 1.5rem; }
.ais-sheet-scrim { position: absolute; inset: 0; background: rgba(0, 0, 0, .68); backdrop-filter: blur(2px); }
.ais-sheet-body {
  position: relative; width: min(920px, 100%); max-height: 84vh; overflow: auto;
  background: var(--ais-panel); border: 1px solid var(--ais-border); border-radius: 14px;
  padding: 1.1rem 1.2rem; display: flex; flex-direction: column; gap: .85rem;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .7);
  animation: ais-rise .2s ease-out both;
}

.ais-imghead { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.ais-imgactions { display: flex; gap: .75rem; }
.ais-linkbtn {
  background: none; border: none; padding: 0; cursor: pointer;
  color: var(--ais-accent); font-size: .78rem; font-weight: 600;
}
.ais-linkbtn:hover { text-decoration: underline; }

/* --- the week ---------------------------------------------------------------
   Seven days, each with the idea that makes it different, and the poster and
   copy as they arrive. A day that is still building shows its idea rather than a
   spinner: the week can be read while it is made. */
.ais-week {
  background: var(--ais-panel); border: 1px solid var(--ais-border); border-radius: 12px;
  padding: 1.1rem 1.2rem; margin-top: 1.25rem;
  animation: ais-rise .28s ease-out both;
}
.ais-weekhead { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.ai-studio .ais-weekhead p { margin: .2rem 0 0; max-width: 60ch; }
.ais-weekcount { display: flex; flex-direction: column; align-items: flex-end; }
.ais-weeknum { font-size: 1.3rem; font-weight: 700; color: var(--ais-accent); }

/* Offered beside a week that finished with gaps, so the way out is where the
   failure is being read rather than back up the page. Full width on its own
   line: it is a decision, not a header ornament. */
.ais-again { flex-basis: 100%; margin-top: .35rem; }

.ais-bar { height: 4px; border-radius: 999px; background: var(--ais-panel2); overflow: hidden; margin: .9rem 0 1.2rem; }
.ais-bar-fill { display: block; height: 100%; background: var(--ais-accent); transition: width .4s ease; }

.ais-days { display: flex; flex-direction: column; gap: 1rem; }
.ais-day {
  background: var(--ais-panel2); border: 1px solid var(--ais-border); border-radius: 11px;
  padding: .9rem 1rem; animation: ais-rise .25s ease-out both;
}
.ais-day.is-waiting { opacity: .72; }
/* A day that gave up looks different from one that is still trying. */
.ais-day.is-failed { border-color: var(--err-bd); }
.ais-dayfail {
  background: var(--err-bg); border: 1px solid var(--err-bd); border-radius: 9px;
  padding: .65rem .8rem; display: flex; flex-direction: column; gap: .2rem;
}
.ais-dayfail .ais-cap-label { color: var(--err-fg); }
.ai-studio .ais-dayfail p { margin: 0; }
.ais-dayhead { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; margin-bottom: .35rem; }
.ais-daynum { font-weight: 700; font-size: .9rem; color: var(--ais-fg); }
.ais-dayangle { margin: 0 0 .8rem; color: var(--ais-muted); font-size: .86rem; line-height: 1.5; max-width: 80ch; }
.ais-daybody { display: grid; grid-template-columns: minmax(0, 260px) 1fr; gap: 1rem; align-items: start; }
@media (max-width: 760px) { .ais-daybody { grid-template-columns: 1fr; } }
.ais-dayposter { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; border-radius: 10px; border: 1px solid var(--ais-border); display: block; }
.ais-dayposter-none { display: grid; place-items: center; text-align: center; padding: 1rem; background: var(--ais-panel); }
.ais-daycaps { display: flex; flex-direction: column; gap: .6rem; min-width: 0; }
/* The poster and its own "again" button travel together, so the control sits
   under the thing it changes rather than in a row of buttons about the card. */
.ais-daypost { display: flex; flex-direction: column; gap: .55rem; min-width: 0; }
.ais-redo {
  width: 100%; font-size: .82rem; padding: .5rem .7rem;
  background: var(--ais-panel); color: var(--ais-ink); border: 1px solid var(--ais-border);
}
.ais-redo:hover:not(:disabled) { border-color: var(--ais-accent); color: var(--ais-accent); }
.ais-redo.is-busy, .ais-redo:disabled { opacity: .65; cursor: default; }

/* The last step: accounts, a timezone, a time, one button. Nothing else is
   allowed on this panel — it is a choice, not a settings page. */
.ais-activate {
  margin-top: 1.6rem; padding-top: 1.3rem; border-top: 1px solid var(--ais-border);
  display: flex; flex-direction: column; gap: .75rem;
}
.ais-accounts { display: flex; flex-direction: column; gap: .5rem; }
.ais-account {
  display: flex; align-items: center; gap: .6rem; cursor: pointer;
  background: var(--ais-panel2); border: 1px solid var(--ais-border);
  border-radius: 10px; padding: .6rem .8rem;
}
.ais-account:hover { border-color: var(--ais-accent); }
.ais-account.is-off { opacity: .55; cursor: default; }
.ais-account.is-off:hover { border-color: var(--ais-border); }
.ais-acct-name { font-weight: 600; }
.ais-check { width: 18px; height: 18px; accent-color: var(--ais-accent); flex: none; }
.ais-when { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: 1rem; }
@media (max-width: 620px) { .ais-when { grid-template-columns: 1fr; } }
.ais-select, .ais-time { width: 100%; }
.ais-gorow { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
.ais-go { font-weight: 700; }
.ais-gonote { margin: 0; }
.ais-daywait { display: flex; align-items: center; gap: .6rem; }
.ais-spinner-sm { width: 18px; height: 18px; border-width: 2px; margin: 0; }

.ais-nextbar {
  margin-top: 1.35rem; padding-top: 1.1rem; border-top: 1px solid var(--ais-border);
  display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
}
.ais-nextbar .ais-hint { margin: 0; max-width: 52ch; }
.ais-next { padding: .75rem 1.4rem; }
.ais-logo {
  width: 56px; height: 56px; flex: 0 0 56px; object-fit: contain; border-radius: 10px;
  background: #fff; padding: 6px; border: 1px solid var(--ais-border);
}
.ais-logo-none {
  display: grid; place-items: center; background: var(--ais-panel2); color: var(--ais-muted);
  font-weight: 700; font-size: 1.4rem; padding: 0;
}
.ais-brandname { margin: 0; font-size: 1.05rem; font-weight: 700; color: var(--ais-fg); }
.ai-studio .ais-brandhead p { margin: .15rem 0 0; }

.ais-brandbody { display: flex; flex-direction: column; gap: .95rem; }
.ais-brandabout { margin: 0; color: var(--ais-fg); font-size: .9rem; line-height: 1.55; max-width: 80ch; }
.ais-bgroup { display: flex; flex-direction: column; gap: .35rem; }

.ais-swatches { display: flex; flex-wrap: wrap; gap: .4rem; }
.ais-sw {
  width: 30px; height: 30px; border-radius: 8px; border: 1px solid var(--ais-border);
  cursor: pointer; padding: 0; transition: transform .12s ease, box-shadow .12s ease;
}
.ais-sw:hover { transform: translateY(-2px); box-shadow: 0 4px 10px rgba(0,0,0,.5); }

.ais-details { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: .6rem .9rem; }
.ais-detail { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
.ais-detail-v { font-size: .88rem; color: var(--ais-fg); overflow-wrap: anywhere; }

.ais-warn {
  margin-top: .9rem; background: var(--ais-panel2); border: 1px solid var(--ais-border);
  border-radius: 9px; padding: .7rem .85rem;
}
.ais-warnlist { margin: .3rem 0 0; padding-left: 1.1rem; color: var(--ais-muted); font-size: .82rem; }
.ais-warnlist li { margin: .15rem 0; }

.ais-grid { display: grid; grid-template-columns: minmax(0, 380px) 1fr; gap: 1.5rem; align-items: start; }
@media (max-width: 900px) { .ais-grid { grid-template-columns: 1fr; } }

.ais-panel {
  background: var(--ais-panel); border: 1px solid var(--ais-border); border-radius: 12px;
  padding: 1.1rem; display: flex; flex-direction: column; gap: .85rem;
}
.ais-row { display: flex; flex-direction: column; gap: .35rem; }
.ais-label { font-size: .78rem; font-weight: 600; color: var(--ais-muted); letter-spacing: .02em; }
.ais-hint { font-size: .72rem; color: var(--ais-muted); }
.ais-input {
  width: 100%; background: var(--ais-panel2); color: var(--ais-fg);
  border: 1px solid var(--ais-border); border-radius: 9px; padding: .6rem .7rem;
  font-size: .9rem; outline: none; transition: border-color .15s;
}
.ais-input:focus { border-color: var(--ais-accent); }
.ais-area { resize: vertical; min-height: 66px; font-family: inherit; }
select.ais-input { appearance: none; cursor: pointer; }

/*
 * Three colours across a 380px panel: the hex input must be allowed to shrink
 * (min-width:0) or the swatch pushes it wide enough to clip "#0ea5e9" to
 * "#0ea5e". Its font-size is reduced so the full seven characters always fit.
 */
.ais-colors { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .5rem; }
.ais-color { display: flex; flex-direction: column; gap: .3rem; min-width: 0; }
.ais-color-in { display: flex; align-items: center; gap: .3rem; min-width: 0; }
.ais-swatch {
  -webkit-appearance: none; appearance: none; width: 34px; height: 34px; flex: 0 0 34px;
  padding: 0; border: 1px solid var(--ais-border); border-radius: 8px; background: none; cursor: pointer;
}
.ais-swatch::-webkit-color-swatch-wrapper { padding: 2px; }
.ais-swatch::-webkit-color-swatch { border: none; border-radius: 6px; }
.ais-hex { text-transform: lowercase; min-width: 0; flex: 1 1 auto; width: 100%; padding: .5rem .4rem; font-size: .78rem; letter-spacing: -.01em; }

.ais-btn {
  background: var(--ais-accent); color: #0b0d10; border: none; border-radius: 9px;
  padding: .7rem 1.1rem; font-size: .92rem; font-weight: 700; cursor: pointer;
  transition: filter .15s, opacity .15s; text-align: center; text-decoration: none; display: inline-block;
}
.ais-btn:hover { filter: brightness(1.08); }
.ais-btn:disabled { opacity: .55; cursor: not-allowed; }

.ais-output {
  background: var(--ais-panel); border: 1px solid var(--ais-border); border-radius: 12px;
  padding: 1.1rem; min-height: 420px;
}
.ais-empty {
  min-height: 380px; display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; gap: .5rem; border: 1px dashed var(--ais-border); border-radius: 10px; padding: 1.5rem;
}
.ais-empty p { margin: 0; color: var(--ais-fg); }
.ais-empty .ais-hint { max-width: 46ch; }
.ais-spinner {
  width: 34px; height: 34px; border-radius: 50%; border: 3px solid var(--ais-border);
  border-top-color: var(--ais-accent); animation: ais-spin .8s linear infinite; margin-bottom: .3rem;
}
@keyframes ais-spin { to { transform: rotate(360deg); } }

.ais-poster-wrap { display: flex; flex-direction: column; gap: .7rem; align-items: flex-start; }
.ais-poster {
  width: 100%; max-width: 560px; height: auto; aspect-ratio: 1 / 1; border-radius: 12px;
  border: 1px solid var(--ais-border); display: block;
}
.ais-dl { align-self: flex-start; }
.ais-note {
  background: rgba(110,168,254,.1); border: 1px solid rgba(110,168,254,.35); color: var(--ais-fg);
  border-radius: 9px; padding: .6rem .8rem; font-size: .85rem; margin-bottom: .9rem;
}

.ais-caps { display: flex; flex-direction: column; gap: .7rem; margin-top: 1rem; }
.ais-cap { background: var(--ais-panel2); border: 1px solid var(--ais-border); border-radius: 10px; padding: .8rem; }
.ais-cap-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: .45rem; }
.ais-cap-label { font-size: .75rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--ais-muted); }
.ais-copy {
  background: transparent; color: var(--ais-accent); border: 1px solid var(--ais-border);
  border-radius: 7px; padding: .25rem .6rem; font-size: .75rem; font-weight: 600; cursor: pointer;
}
.ais-copy:hover { border-color: var(--ais-accent); }
.ais-cap-text { margin: 0; white-space: pre-wrap; font-size: .88rem; line-height: 1.55; color: var(--ais-fg); }
.ais-tags { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .9rem; }
.ais-chip {
  background: var(--ais-panel2); border: 1px solid var(--ais-border); color: var(--ais-accent);
  border-radius: 999px; padding: .2rem .6rem; font-size: .78rem;
}

/* Whether a poster carries the business's own photograph. Quiet by default and
   only loud when something needs doing: a silent skip is the thing this fixes. */
.ais-photonote { margin: 0; font-size: .78rem; color: var(--ais-warn, #e0a458); line-height: 1.4; }
.ais-photonote.is-on { color: var(--ais-muted); }

/* What is left, beside the button that spends it. Quiet until it is gone. */
.ais-allowance { margin: .5rem 0 0; }
.ais-allowance.is-spent { color: var(--ais-warn, #e0a458); }

/* What to know before pressing Connect. Set apart from the ordinary hints
   because it is the difference between a failed attempt and a successful one. */
.provider-note {
  margin-top: .6rem; padding: .6rem .7rem;
  border-left: 3px solid var(--ais-accent, #e0a458);
  background: color-mix(in srgb, var(--surface, #1a1a1a) 92%, transparent);
  border-radius: 0 8px 8px 0; line-height: 1.5;
}
