/* Editor right-hand panel: the Slice -> Prepare -> Done workflow rail.
   Deliberately the same shape as the send modal's #klaviyo-progress-strip - a labels
   row over a segmented fill track - so it reads as the app's existing progress
   language rather than a new invention. Flush, with no card of its own: it is the
   header of the panel card, not a widget sitting inside it. */

.workflow-rail {
  display: block;
  padding: 2px 2px 0;
}

.workflow-rail__labels,
.workflow-rail__track {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 6px;
}

.workflow-rail__labels { margin-bottom: 8px; }

.workflow-rail__step {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0;
  border: none;
  background: none;
  font-family: inherit;
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  color: var(--text-tertiary);
  white-space: nowrap;
  cursor: default;
}
/* Green, and deliberately so. The app's rule is INDIGO = what you can act on, GREEN =
   what is finished - so the rail's two states separate by hue, which is a far stronger
   signal than the saturation step that replaced it. All-indigo made three identical
   dots over three identical bars and the rail stopped saying where you were; muting the
   finished ones fixed that with the weakest tool available when a second colour was
   already sitting there doing nothing. Nothing here contradicts the original fix: that
   was about BUTTONS promising success before anything had happened. A tick behind you
   is not a promise - it is a fact. */
.workflow-rail__step.done { cursor: default; color: var(--success-dark); }
/* Only steps you can actually go to advertise themselves as clickable. A ticked
   Prepare after a finished download has no form to return to. */
.workflow-rail__step.nav { cursor: pointer; }
.workflow-rail__step.now { color: var(--text-primary); }
.workflow-rail__step.skip { opacity: 0.4; }
/* Reachable but not finished - a paused Prepare. Clickable like a completed step,
   but it keeps its number instead of a tick, because nothing was completed. */
.workflow-rail__step.avail { color: var(--text-secondary); }
.workflow-rail__step.avail .workflow-rail__dot {
  background: rgba(var(--primary-rgb), 0.14);
  color: var(--primary-text);
  border: 1px dashed rgba(var(--primary-rgb), 0.45);
}
.workflow-rail__seg.avail { background: rgba(var(--primary-rgb), 0.25); }
.workflow-rail__labels .workflow-rail__step:nth-child(2) { justify-content: center; }
.workflow-rail__labels .workflow-rail__step:nth-child(3) { justify-content: flex-end; }

.workflow-rail__dot {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(15, 23, 42, 0.06);
  color: var(--text-tertiary);
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.workflow-rail__dot svg { width: 10px; height: 10px; }
/* line-height: 1 collapses the line box so flex centring acts on the glyph rather
   than on reserved descender space. Measured against the real font metrics: the ink
   centre then lands exactly on the dot centre, so NO nudge is wanted - an earlier
   0.5px correction was calibrated for a different dot size and pushed 1 and 3 low. */
.workflow-rail__num { display: block; line-height: 1; }
/* Filled, like .now - the two are told apart by hue and by tick-vs-numeral, so neither
   has to give up weight to the other. */
.workflow-rail__step.done .workflow-rail__dot { background: var(--icon-success); color: var(--surface-white); }
.workflow-rail__step.now .workflow-rail__dot { background: var(--primary); color: var(--surface-white); }
.workflow-rail__step.skip .workflow-rail__dot {
  background: transparent;
  border: 1px dashed var(--border-strong);
}

.workflow-rail__seg {
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--border);
}
.workflow-rail__seg.done { background: var(--icon-success); }
.workflow-rail__seg.now { background: var(--primary); }
/* Only ever applied while the Creating checklist is the screen - never behind a form
   the user is still filling in. See renderWorkflowPanel(): motion beside inputs reads
   as a deadline, and there is no deadline here. */
.workflow-rail__seg.work {
  background: linear-gradient(90deg, var(--primary) 0%, var(--primary-light) 50%, var(--primary) 100%);
  background-size: 200% 100%;
  animation: workflowRailShimmer 1.15s linear infinite;
}
@keyframes workflowRailShimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .workflow-rail__seg.work { animation: none; }
}

.workflow-stage.hidden { display: none; }

/* === Prepare stage ===
   The action sits ABOVE the form: the Link and Alt text folds expand downward, so
   anchoring it to the bottom would push it past the fold. */

/* Leaving the stage sits at the end of it, matching .done-secondary so "back" is
   in the same place and the same shape on both stages. */
.prepare-footer {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 4px;
  padding-top: 12px;
  border-top: 1px solid var(--border-hairline);
}
.prepare-footer.hidden { display: none; }
.prepare-footer button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 34px;
  padding: 0 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius-base);
  background: var(--surface-white);
  cursor: pointer;
  font-family: inherit;
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  color: var(--text-secondary);
}
.prepare-footer button:hover { color: var(--text-primary); border-color: var(--border-strong); }

/* Errors only - see the note in index.html. Routine progress is the rail's job. */
.prepare-ticker {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
  border-radius: var(--radius-md);
  border-left: 3px solid var(--danger);
  background: var(--surface-canvas);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  color: var(--danger-text);
}
.prepare-ticker.hidden { display: none; }
.prepare-ticker #klaviyo-send-template-status { min-width: 0; line-height: 1.35; }

@keyframes prepareTickerSpin { to { transform: rotate(360deg); } }

/* A single full-width primary. The old white "Back" button sat beside it competing
   for weight; Back is a quiet text control in the row above now. */
.prepare-actions { display: flex; }
.prepare-actions.hidden { display: none; }
.prepare-actions .btn {
  flex: 1;
  height: 46px;
  justify-content: center;
  border-radius: var(--radius-base);
  font-weight: var(--weight-semibold);
}
/* Same treatment as the Slice stage's export button, so it reads as one button
   that moved rather than a different, flatter one. Same gradient and the same shadow
   numbers as #download-all in app-shell.css: the journey has one primary action and it
   should look like the SAME control travelling from Slice to Prepare, not a second one
   in a different colour. */
.prepare-actions .btn-primary {
  background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
  border: none;
  color: white;
  letter-spacing: 0.02em;
  box-shadow: 0 4px 12px -2px rgba(var(--primary-rgb), 0.4);
  transition: filter 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
}
.prepare-actions .btn-primary:hover:not(:disabled) {
  filter: brightness(105%);
  transform: translateY(-1px);
  box-shadow: 0 6px 18px -2px rgba(var(--primary-rgb), 0.5);
}

/* Spacing carries the grouping, since nothing here is boxed. A single uniform gap
   made unrelated things - the CTA link's own disclosure and the separate Alt text
   fold - sit as close together as a label and its input. Base gap is tight; the
   boundaries between groups buy extra margin below. */
/* :not(.hidden) is load-bearing, not decoration. An id selector outranks
   .workflow-stage.hidden, so a plain `#prepare-view { display: flex }` would beat the
   hide rule and leak the whole pane onto the Slice stage. Scoping the layout to the
   visible state means it CANNOT apply while hidden, whatever the specificity - which
   a second `.hidden` override at matching specificity would not guarantee for the
   next rule someone adds. */
#prepare-view:not(.hidden) { display: flex; flex-direction: column; gap: 8px; }
/* Back sits directly above the primary action, so it needs air on both sides:
   cramped against the button it read as part of it. */
/* End of the action group - the form below is a new thought. */
#prepare-view .prepare-actions { margin-bottom: 10px; }
/* The Alt text fold is a separate concern from the CTA link and its per-snippet
   disclosure, so it gets a clear break rather than the base gap. */
#prepare-view .alt-fold-toggle { margin-top: 8px; }
#prepare-view .es-form-input { font-size: var(--text-sm); padding: 8px 10px; }
#prepare-view .klaviyo-send-field-label { font-size: var(--text-xs); margin-bottom: 3px; }
#prepare-view .klaviyo-send-field { margin-bottom: 12px; }
#prepare-view .klaviyo-send-field:last-child { margin-bottom: 0; }

/* Klaviyo blocks card on Prepare. One row per brand block: order number, name, and
   an include switch (the existing auto-alt track). Hidden entirely when the active
   brand has no blocks, so brands without the feature never see it.

   The card borrows the alt-text fold's frame so the two read as siblings, and it has
   two shapes. With ONE block - the common case - it is a single row: the label, the
   block's name, the switch. With two or more, the heading and the bulk action come
   back, because only then do they describe something that exists. A lone block used to
   carry a heading, an order number, a "Turn all off" and a sentence about ordering:
   six pieces of furniture for one on/off decision, half of them implying a set. */
.klaviyo-send-blocks {
  padding: 9px var(--spacing-sm);
  border: 1px solid rgba(15, 23, 42, 0.07);
  border-radius: var(--radius-md);
  background: var(--surface-card);
}
.klaviyo-send-blocks.hidden { display: none; }
.klaviyo-send-blocks.is-single .klaviyo-send-blocks-head { display: none; }
.klaviyo-send-blocks.is-single .klaviyo-send-block-row { padding: 0; border-bottom: none; }
.klaviyo-send-blocks-order { font-weight: var(--weight-regular); color: var(--text-tertiary); }
.klaviyo-send-block-solo-label {
  flex-shrink: 0;
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  color: var(--text-secondary);
}
.klaviyo-send-blocks-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}
.klaviyo-send-blocks-all {
  padding: 0;
  border: none;
  background: transparent;
  font-family: inherit;
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  color: var(--primary);
  cursor: pointer;
}
.klaviyo-send-blocks-all:hover { text-decoration: underline; }
.klaviyo-send-blocks-list {
  display: flex;
  flex-direction: column;
}
.klaviyo-send-block-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 2px;
  border-bottom: 1px solid var(--border-hairline);
  cursor: pointer;
}
.klaviyo-send-block-row:last-child { border-bottom: none; }
.klaviyo-send-block-order {
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  color: var(--text-tertiary);
  min-width: 12px;
  text-align: center;
  flex-shrink: 0;
}
.klaviyo-send-block-name {
  flex: 1;
  min-width: 0;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.klaviyo-send-block-row.is-off .klaviyo-send-block-name { color: var(--text-tertiary); }
.klaviyo-send-block-switch { flex-shrink: 0; }

/* Alt text fold. Closed by default: an open fold is what pushes the campaign
   stage past a single screen. */
.alt-fold-toggle {
  display: flex;
  align-items: center;
  gap: 7px;
  width: 100%;
  padding: 9px var(--spacing-sm);
  border: 1px solid rgba(15, 23, 42, 0.07);
  border-radius: var(--radius-md);
  background: var(--surface-card);
  cursor: pointer;
  font-family: inherit;
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  color: var(--text-secondary);
  text-align: left;
}
.alt-fold-toggle.hidden { display: none; }
.alt-fold-toggle:hover { border-color: rgba(99, 102, 241, 0.25); }
.alt-fold-toggle__chev { flex-shrink: 0; color: var(--text-tertiary); transition: transform 0.2s ease; }
.alt-fold-toggle.is-open .alt-fold-toggle__chev { transform: rotate(90deg); }
.alt-fold-toggle__count {
  margin-left: auto;
  font-weight: var(--weight-medium);
  color: var(--text-tertiary);
}
.alt-fold-toggle__count.is-ready { color: var(--success-darker); }
.alt-fold-toggle__count.is-busy { color: var(--warning-amber); }

.alt-fold-body { padding: 4px 2px 0; }
.alt-fold-body.hidden { display: none; }

/* Alt text rows. One hairline-separated row per slice - numbered thumbnail, the text,
   and a copy icon that appears on hover. Replaces a white card per slice with a "Copy"
   text button, which stacked cards inside the fold inside the panel card. */
.alt-controls {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 2px 0 9px;
  border-bottom: 1px solid var(--border-hairline);
}
.alt-controls .auto-alt-text-tab-label { font-size: var(--text-xs); }
.alt-extract-btn {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  height: 26px;
  padding: 0 12px;
  border: none;
  border-radius: var(--radius-sm);
  background: var(--primary);
  cursor: pointer;
  font-family: inherit;
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  color: var(--surface-white);
  white-space: nowrap;
}
.alt-extract-btn:hover:not(:disabled) { filter: brightness(108%); }
.alt-extract-btn:disabled { opacity: 0.6; cursor: default; }

/* The list scrolls itself. The panel is sticky and (in the v2 shell) has no scroller
   of its own, so an uncapped list made the pinned panel taller than the screen and the
   wheel could only move the page - the rows below the fold were unreachable until the
   page hit bottom. overscroll-behavior stops the list handing the wheel to the page
   when it runs out. */
.alt-rows {
  display: block;
  max-height: min(44vh, 440px);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.alt-rows__empty {
  margin: 10px 2px;
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  line-height: 1.45;
}
.alt-row { position: relative; display: flex; align-items: flex-start; gap: 7px; padding: 7px 0; }
.alt-row + .alt-row { border-top: 1px solid var(--border-hairline); }
/* Matches the link rows: split halves indented, no connector line - the indent and
   the L/R badges carry the grouping on their own. */
.alt-row--col { padding-left: 14px; }
.alt-row__num {
  flex-shrink: 0;
  margin-top: 7px;
  min-width: 14px;
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  color: var(--text-tertiary);
  font-variant-numeric: tabular-nums;
}
.alt-row__text {
  flex: 1;
  min-width: 0;
  box-sizing: border-box;
  padding: 6px 26px 6px 9px;
  border: 1px solid var(--border-input);
  border-radius: var(--radius-base);
  background: var(--bg-elevated);
  font-family: inherit;
  font-size: var(--text-sm);
  color: var(--text-primary);
  line-height: 1.45;
  resize: none;
  overflow: hidden;   /* height is set from scrollHeight on render and on input */
  outline: none;
}
.alt-row__text:focus { border-color: var(--primary); }
/* Clamped rows get a marker so it is obvious there is more text than is shown. It
   sits clear of the copy button, which lives at the top right. */
.alt-row.is-clipped::after {
  content: '...';
  position: absolute;
  right: 32px;
  bottom: 9px;
  padding: 0 4px;
  background: var(--bg-elevated);
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  color: var(--text-tertiary);
  pointer-events: none;
}
.alt-row__text.is-empty {
  border-style: dashed;
  background: transparent;
  color: var(--text-quaternary);
  font-style: italic;
}
/* A slice that could not be read is also empty, so .is-empty already applies. This is
   the only thing separating it from a slice that genuinely has no copy in it — without
   it, a failure reads as a legitimate result. */
.alt-row__text.is-failed {
  border-color: var(--danger-border);
  background: var(--danger-wash);
}
.alt-row__text.is-failed::placeholder { color: var(--danger-text); }

/* Summary above the rows. The toast that reports failures disappears after 8s; this
   stays until the failures are resolved, and gives them somewhere to be acted on. */
.alt-rows__retry {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin: 8px 0;
  padding: 8px 10px;
  border: 1px solid var(--danger-border);
  border-radius: var(--radius-base);
  background: var(--danger-wash);
  font-size: var(--text-xs);
  color: var(--danger-text);
}
.alt-rows__retry-btn {
  flex-shrink: 0;
  padding: 4px 10px;
  border: 1px solid var(--danger-border);
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  font-family: inherit;
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  color: var(--danger-text);
  cursor: pointer;
}
.alt-rows__retry-btn:hover { background: var(--danger-subtle); }
.alt-rows__retry-btn:focus-visible { outline: 2px solid var(--danger); outline-offset: 1px; }
/* Icon, not a button with a word in it: at six rows the labels were the loudest
   thing in the fold. Always present for keyboard users, revealed on hover. */
.alt-row__copy {
  position: absolute;
  top: 11px;
  right: 4px;
  width: 22px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-white);
  color: var(--text-tertiary);
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.15s ease, color 0.15s ease;
}
.alt-row:hover .alt-row__copy,
.alt-row__copy:focus-visible { opacity: 1; }
.alt-row__copy:hover { color: var(--text-primary); border-color: var(--border-strong); }
.alt-row__copy svg { width: 11px; height: 11px; }
.alt-row__copy.is-copied { opacity: 1; color: var(--success-dark); border-color: var(--success-border); }

/* Replaces syncSnippetsWrapperMinHeight()'s hard-coded pixel estimates: the panel
   card just reserves enough room that the absolutely-positioned export dropdown is
   never clipped, without JS having to guess at its height. */
/* The card must keep its content height; .editor-right is a max-height flex column,
   so without this the card shrinks and its background stops short while the expanded
   link and alt lists spill out past it. The COLUMN scrolls, never the card. */
/* 24px of air above the title read as a gap rather than as the card's inset once the
   row went from 15px with a prefix to 14px without one. 20px rather than the 24 the
   sides keep: text sits optically lower than a box edge, so matching the number would
   not match the look. */
body.editor-mode .snippets-section { min-height: min(420px, 46vh); flex-shrink: 0; padding-top: 20px; }


/* Creating checklist, shown in place of the form once details are locked. */
.prepare-busy { display: flex; flex-direction: column; gap: var(--spacing-sm); }
.prepare-busy.hidden { display: none; }
.prepare-busy__list {
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding: var(--spacing-sm);
  border: 1px solid rgba(15, 23, 42, 0.07);
  border-radius: var(--radius-md);
  background: var(--surface-card);
}
.prepare-busy__item {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--text-tertiary);
}
.prepare-busy__item.done { color: var(--text-secondary); }
.prepare-busy__item.now { color: var(--text-primary); font-weight: var(--weight-semibold); }
.prepare-busy__dot {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  border-radius: 50%;
  background: rgba(15, 23, 42, 0.06);
  display: flex;
  align-items: center;
  justify-content: center;
}
.prepare-busy__dot svg { width: 9px; height: 9px; color: var(--surface-white); }
/* Matches the rail's ticked dot directly above it - the checklist is the same
   progression at a finer grain, so it cannot use a different colour for "done".
   The spinner on .now below stays indigo: work in progress is not a finished thing. */
.prepare-busy__item.done .prepare-busy__dot { background: var(--icon-success); }
.prepare-busy__item.now .prepare-busy__dot {
  background: none;
  border: 2px solid rgba(var(--primary-rgb), 0.2);
  border-top-color: var(--primary);
  animation: prepareTickerSpin 0.8s linear infinite;
}
@media (prefers-reduced-motion: reduce) { .prepare-busy__item.now .prepare-busy__dot { animation: none; } }
/* First paint only - renderPrepareBusy adds --reveal once per job and the signature
   guard stops later renders replaying it. `backwards` holds each row hidden through
   its own delay, so they arrive in order instead of all appearing then sliding. */
.prepare-busy__list--reveal .prepare-busy__item {
  animation: prepareBusyReveal 0.28s ease-out backwards;
  animation-delay: calc(var(--reveal-i, 0) * 110ms);
}
@keyframes prepareBusyReveal {
  from { opacity: 0; transform: translateY(-3px); }
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .prepare-busy__list--reveal .prepare-busy__item { animation: none; }
}
.prepare-busy__cancel { height: 46px; border-radius: var(--radius-base); }

/* Done stage. The whole card is the link to Klaviyo: that is the action in almost
   every case, so it should not compete with the secondary ones. */
/* Stack the result, any secondary actions and the way back with real separation:
   they were touching, which made "Back to slices" both hard to hit and easy to
   read as part of the result card. */
#done-view:not(.hidden) { display: flex; flex-direction: column; gap: 10px; }

.done-card {
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  box-sizing: border-box;
  padding: 13px var(--spacing-sm);
/* A success SURFACE, not a white card with green writing on it. Its two siblings below
   already work this way - --error takes a danger background and border, --cancelled a
   muted one - so the default variant reading as plain white was the odd one out, and
   left the green text with nothing to sit on. Tinting it also buys the contrast that
   makes the whole card legible in one hue: --success-darker measures 5.21:1 on this
   mint, where the old white-card treatment ran the campaign name at --success-darker
   0.75 opacity - 3.4:1, under the 4.5:1 that 12px text needs. */
  border: 1px solid var(--success-border);
  border-radius: var(--radius-md);
  background: var(--success-subtle);
  text-decoration: none;
  cursor: pointer;
  transition: border-color 0.16s ease, box-shadow 0.16s ease, transform 0.16s ease;
}
.done-card:hover { border-color: var(--success); box-shadow: 0 4px 14px rgba(16, 185, 129, 0.18); transform: translateY(-1px); }
.done-card:hover .done-card__arrow { transform: translate(2px, -2px); }
.done-card--static { cursor: default; }
.done-card--static:hover { border-color: var(--success-border); box-shadow: none; transform: none; }
.done-card--static .done-card__arrow { display: none; }
.done-card--cancelled { background: var(--surface-card); border-color: var(--border); }
.done-card--cancelled .done-card__badge { background: var(--text-tertiary); }
.done-card--cancelled .done-card__title,
.done-card--cancelled .done-card__name,
.done-card--cancelled .done-card__go { color: var(--text-secondary); }
.done-card--cancelled .done-card__arrow { color: var(--text-tertiary); }
/* A Template or Images upload that failed. It has no form to go back to, so this is
   the only place the reason can be read. */
.done-card--error { background: var(--surface-card); border-color: var(--danger); }
.done-card--error .done-card__badge { background: var(--danger); }
/* The name and the reason have to leave the success hue with the title. Only the title
   was overridden before, which was survivable while the card was white - now that the
   default variant is a green surface, unoverridden children would put green text on a
   failure card. */
.done-card--error .done-card__title { color: var(--danger-text); }
.done-card--error .done-card__name,
.done-card--error .done-card__go { color: var(--danger-dark); }
.done-card--error .done-card__arrow { color: var(--danger); }
.done-card--error:hover { border-color: var(--danger); box-shadow: none; transform: none; }
/* Green, per the app's rule: INDIGO is what you can act on, GREEN is what is finished.
   The badge is the strongest statement of "finished" in the flow, so it takes the solid
   fill. Kept in step with .es-toast--success's chip, which is the same mark in the
   corner and is on screen beside it when an export lands.
   The original bug this all started from is still fixed: no BUTTON is green anywhere,
   because a button is a thing you can act on and green would promise success before
   anything had happened. --danger and --text-tertiary override this for the error and
   cancelled variants above - the badge tracks the outcome, and green is one of three. */
.done-card__badge {
  width: 22px;
  height: 22px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--icon-success);
  display: flex;
  align-items: center;
  justify-content: center;
}
.done-card__badge svg { width: 11px; height: 11px; color: var(--surface-white); }
.done-card__body { flex: 1; min-width: 0; }
.done-card__title {
/* Green like the rest of the card. Black ink here read as a stray from another
   component - the sibling variants put their outcome colour on the title too
   (--error uses --danger-text), so the success title using neutral ink was the
   inconsistency, not the colour. Hierarchy inside the card comes from SIZE and
   WEIGHT - 14/600 title over a 12/400 name over a 12/600 action - which is what
   stops one hue from flattening into a wall. */
  display: block;
  font-size: var(--text-md);
  font-weight: var(--weight-semibold);
  color: var(--success-darker);
  line-height: 1.25;
}
/* Same ink as the title, one size and two weights down. NOT the old 0.75 opacity: that
   washed --success-darker out to 3.4:1 on white, under the 4.5:1 that 12px text needs. */
.done-card__name {
  display: block;
  margin-top: 2px;
  font-size: var(--text-xs);
  color: var(--success-darker);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.done-card__go {
  display: block;
  margin-top: 6px;
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
/* The action. It sits in the card's own hue rather than the app's link indigo, because
   the WHOLE card is the link - an indigo phrase inside a green card read as a second,
   different control sitting in it. Semibold is what marks it as the thing to press. */
  color: var(--success-darker);
}
.done-card__go:empty { display: none; }
.done-card__arrow {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  color: var(--success-dark);
  transition: transform 0.16s cubic-bezier(0.4, 0, 0.2, 1);
}

.done-links { display: flex; flex-direction: column; gap: 6px; }
.done-links:empty { display: none; }
.done-links button {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 9px var(--spacing-sm);
  border: 1px solid rgba(15, 23, 42, 0.07);
  border-radius: var(--radius-md);
  background: var(--surface-card);
  cursor: pointer;
  font-family: inherit;
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
  text-align: left;
}
.done-links button:hover { border-color: rgba(99, 102, 241, 0.28); }
.done-links .sub {
  display: block;
  margin-top: 1px;
  font-size: var(--text-2xs);
  font-weight: var(--weight-medium);
  color: var(--text-tertiary);
}

.done-secondary {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 6px;
  padding-top: 12px;
  border-top: 1px solid var(--border-hairline);
}
.done-secondary button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 34px;
  padding: 0 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius-base);
  background: var(--surface-white);
  cursor: pointer;
  font-family: inherit;
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  color: var(--text-secondary);
}
.done-secondary button:hover { color: var(--text-primary); border-color: var(--border-strong); }

/* Free-export meter for signed-out users. Moved out of the export dropdown, where
   it was only visible if you happened to open the menu, onto the Slice stage so
   someone approaching the limit sees it while they work rather than discovering it
   at the moment they are blocked. */
.guest-usage {
  padding: var(--spacing-sm) 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-card);
}
.guest-usage.hidden { display: none; }
.guest-usage__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--spacing-xs);
  margin-bottom: 9px;
}
.guest-usage__label {
  font-size: var(--text-md);
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
}
.guest-usage__count {
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}
.guest-usage.is-low .guest-usage__count { color: var(--warning-amber); }
.guest-usage__track {
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--bg-tertiary);
  overflow: hidden;
}
.guest-usage__bar {
  display: block;
  width: 0;
  height: 100%;
  border-radius: var(--radius-pill);
  background: var(--primary);
  transition: width 0.25s ease, background 0.2s ease;
}
.guest-usage__bar.near-limit { background: var(--warning-icon); }
.guest-usage__bar.at-limit { background: var(--danger); }
.guest-usage__cta {
  display: inline-block;
  margin-top: 11px;
  font-size: var(--text-md);
  font-weight: var(--weight-semibold);
  color: var(--primary-text);
  text-decoration: none;
}
.guest-usage__cta:hover { text-decoration: underline; }

/* Project title: the head of the panel card.

   The name of the project, and the name every Klaviyo campaign or template exported
   from it carries - so it sits at the top of the card that the whole export runs
   through, above the rail that says where in that export you are. The Prepare form
   used to ask for this same value on every single send, prefilled from it.

   All name, no label. A "Project /" prefix in front of it cost about nine characters
   of a 322px row to say what the card's position already says - and those are nine
   characters of a client's campaign name, which is the one thing this row exists to
   show. 14px rather than 15 for the same reason: the row is width-bound, not
   attention-bound, and it already reads as the heading of the card by being at the
   top of it.

   It reads as a heading and behaves as a field: no border and no fill at rest, a
   surface on hover, and the focus ring every other input in the app draws.

   The hairline under it is the card's own header rule - the same job .snippets-label
   does further down, one level up. */
.project-title-wrap {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  padding: 0 0 7px;
  /* The card lays its children out with a 16px gap, which is the right distance
     between blocks but too much between a header rule and the rail it heads. This
     pulls that one gap back to 10px without touching the rest of the column. */
  margin-bottom: -6px;
  border-bottom: 1px solid var(--border-hairline);
}
.project-title-wrap.hidden { display: none; }
/* The glyphs, not the input's box, are what has to line up with the column beneath.
   The padding and the border are the hover/focus pill talking, so they are pulled back
   out with a matching negative margin: the name then starts on the same x as the rail's
   first dot, and the pill bleeds into the card's own inset where there is room for it.
   Worth about a character and a half of a name at each end. */
.project-title-input {
  flex: 1;
  min-width: 0;
  padding: 2px 5px;
  margin-left: -4px;
  margin-right: -5px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  font-family: inherit;
  font-size: var(--text-base);
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
  text-overflow: ellipsis;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}
.project-title-input::placeholder {
  color: var(--text-tertiary);
  font-weight: var(--weight-medium);
}
.project-title-input:hover { background: var(--surface-wash); }
.project-title-input:focus {
  outline: none;
  background: var(--surface-white);
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-subtle);
  text-overflow: clip;
}

/* Restore note ("restored - start fresh"). Rides the title row instead of floating
   over the canvas, and fades once seen. Sits where Revert sits; the two cannot
   coincide - a just-restored name IS its origin, so Revert has nothing to offer. */
.project-title-note {
  flex-shrink: 0;
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  white-space: nowrap;
  transition: opacity 0.9s ease;
}
.project-title-note.hidden { display: none; }
.project-title-note.is-fading { opacity: 0; }
.project-title-note__action {
  padding: 0;
  border: none;
  background: none;
  font-family: inherit;
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  color: var(--primary-text);
  cursor: pointer;
}
.project-title-note__action:hover { text-decoration: underline; }

/* Revert. Present only while the name differs from the one the project arrived with,
   so it is an answer to something the user just did rather than a permanent control.
   Quiet by default and coloured on hover: it undoes a rename, which is cheap and
   reversible, so it should read as an offer rather than a warning. */
.project-title-revert {
  flex-shrink: 0;
  padding: 2px 6px;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  font-family: inherit;
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  color: var(--text-tertiary);
  cursor: pointer;
  transition: color 0.15s ease, background-color 0.15s ease;
}
.project-title-revert.hidden { display: none; }
.project-title-revert:hover { color: var(--primary-text); background: var(--primary-subtle); }
.project-title-revert:focus-visible {
  outline: none;
  color: var(--primary-text);
  box-shadow: 0 0 0 2px var(--primary-subtle);
}

/* In the editor the header rail must span the same box as .container
   (--app-shell-max, 24px gutter) so the logo lines up with the canvas. The public
   route keeps its narrower 1200px so the nav stays aligned with the hero column.
   padding-right is deliberately NOT set here: editor.css already sets 28px to absorb
   the 4px .editor-right reserves for the card's shadow, which is what makes the
   header controls share a right edge with the card rather than overshooting it. */
body.editor-mode .app-header-inner {
  max-width: var(--app-shell-max);
  padding-left: 24px;
}

/* Paused campaign.
   It sits directly above the slice list, so it has to read as "your work is safe
   here" without shouting. The amber frame it used to wear is a warning colour this
   panel uses nowhere else - and nothing has gone wrong, the details are saved. So it
   borrows the language of the cards around it instead: white surface, hairline
   border, the same radius and shadow as the Done card and the Creating checklist.
   No status chip: the title says "paused" already, and a mark beside it was
   decorating the word rather than adding to it. */
.export-paused {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: var(--spacing-md);
  padding: 13px var(--spacing-sm);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-white);
  box-shadow: var(--shadow-sm);
}
.export-paused.hidden { display: none; }

.export-paused__body { display: flex; flex-direction: column; gap: 2px; }
.export-paused__title {
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
}
.export-paused__msg {
  font-size: var(--text-xs);
  line-height: 1.45;
  color: var(--text-secondary);
}

/* Resume is the whole point of the card, so it takes the width. Discard is the one
   irreversible thing here and gets no border of its own - a box that size sat beside
   the primary read as an equal choice, which is the last thing it should be. */
.export-paused__actions { display: flex; align-items: center; gap: 4px; }
.export-paused__actions .btn-primary {
  flex: 1;
  height: 34px;
  justify-content: center;
  font-size: var(--text-xs);
}
.export-paused__actions button:not(.btn-primary) {
  flex-shrink: 0;
  height: 34px;
  padding: 0 12px;
  border: 0;
  border-radius: var(--radius-base);
  background: none;
  cursor: pointer;
  font-family: inherit;
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  color: var(--text-tertiary);
  transition: color 0.15s ease, background-color 0.15s ease;
}
.export-paused__actions button:not(.btn-primary):hover {
  color: var(--danger-text);
  background: var(--danger-subtle, rgba(239, 68, 68, 0.07));
}


/* === Focus shift ===
   While slicing, the canvas is the work and the panel is a sidebar. On Prepare that
   reverses: the canvas is only a reference and the form is the work. So the panel
   takes space the canvas was not using - the email renders ~403px inside an ~808px
   column, so roughly 405px sits empty - and gives it back on the way out.

   The email neither moves nor rescales; the column simply stops being twice the
   width it needs. Guarded to >=1201px because below that the layout is already a
   single stacked column and there is no space to trade. */
@media (min-width: 1201px) {
  body.editor-mode .editor-screen,
  body.editor-mode #editor-screen {
    transition: grid-template-columns 0.32s cubic-bezier(0.4, 0, 0.2, 1);
  }
  body.editor-mode.panel-wide .editor-screen,
  body.editor-mode.panel-wide #editor-screen {
    /* Width is negotiated in _resolvePanelWideWidth() from what the canvas can
       spare at the current zoom; 560px is the cap, not a constant. */
    grid-template-columns: 1fr var(--panel-wide-w, 560px);
  }
  /* Hold the email where it was. The column narrows from its right-hand edge, which
     is empty space, so re-centring would move the design for no reason.
     --canvas-hold-x is the resting offset, computed in _resolvePanelWideWidth(). */
  body.editor-mode.panel-wide .canvas-wrapper {
    justify-content: flex-start;
    padding-left: var(--canvas-hold-x, 0px);
  }
  body.editor-mode .canvas-wrapper {
    transition: padding-left 0.32s cubic-bezier(0.4, 0, 0.2, 1);
  }
  /* .editor-right is pinned to width/min-width 400px in style.css, so widening the
     grid track alone would leave the panel its old size inside a bigger cell. Let the
     panel simply fill its track instead: one animated property (the grid) rather than
     two racing each other, so the canvas and the panel move as one. */
  body.editor-mode .editor-right { width: 100%; min-width: 0; }
  /* The whole point of the width: paired fields stop being two rows. */
  body.editor-mode.panel-wide .klaviyo-send-pair {
    grid-template-columns: 1fr 1fr;
  }
}
@media (prefers-reduced-motion: reduce) {
  body.editor-mode .editor-screen,
  body.editor-mode #editor-screen { transition: none; }
}

/* One column by default, so the narrow panel and small screens are unchanged. */
.klaviyo-send-pair { display: grid; grid-template-columns: 1fr; gap: 0 12px; }
/* Grid items default to min-width:auto, which refuses to shrink below their content.
   A long audience name therefore blew its own column out to 298px, squeezed the other
   to 66px and pushed past the card edge. min-width:0 lets the columns stay equal and
   the chip truncate, which is what it is already built to do. */
.klaviyo-send-pair > * { min-width: 0; }

/* === Export limit, in-panel (limit redesign) ================================ */

/* The wall (state C). Renders where the export menu would have opened; borrows the
   alt-fold's card frame so it reads as panel furniture, not an alarm. Body colours
   throughout: a plan boundary is a normal event. */
.export-limit-wall {
  display: flex;
  flex-direction: column;
  gap: 9px;
  /* Same 12px its neighbour .apply-previous-offer uses: this column spaces its
     children with margins, not a gap, so a card without one sits flush against the
     export button below it. */
  margin-bottom: 12px;
  padding: 12px var(--spacing-sm);
  border: 1px solid rgba(15, 23, 42, 0.07);
  border-radius: var(--radius-md);
  background: var(--surface-card);
}
.export-limit-wall.hidden { display: none; }
.export-limit-wall__title {
  margin: 0;
  font-size: var(--text-md);
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
}
.export-limit-wall__track,
.done-usage__track {
  height: 7px;
  border-radius: var(--radius-pill);
  background: var(--border);
  overflow: hidden;
}
.export-limit-wall__track i,
.done-usage__track i {
  display: block;
  height: 100%;
  border-radius: var(--radius-pill);
  background: linear-gradient(90deg, var(--primary) 0%, var(--primary-dark) 100%);
}
.export-limit-wall__meta,
.done-usage__meta {
  margin: 0;
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}
.export-limit-wall__cta { width: 100%; justify-content: center; }
.export-limit-wall__cta.hidden { display: none; }
.export-limit-wall__hint {
  margin: 0;
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}

/* Done usage card (state A). Same family as the wall, but under a success: the
   headline is information, and the upgrade is a full-width action of its own. */
.done-usage {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 11px var(--spacing-sm);
  border: 1px solid rgba(15, 23, 42, 0.07);
  border-radius: var(--radius-md);
  background: var(--surface-card);
}
.done-usage.hidden { display: none; }
.done-usage__title {
  margin: 0;
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
}
/* Its own line, and sized like a real control - the tiny inline link version was
   the thing being upsold hiding from the person it was for. White rather than the
   primary gradient: this card sits under a success, and the wall's full-primary
   button is reserved for the moment exporting is actually blocked mid-task. */
.done-usage__cta {
  width: 100%;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-base);
  background: var(--surface-white);
  cursor: pointer;
  font-family: inherit;
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--primary-text);
  box-shadow: var(--shadow-sm);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.done-usage__cta:hover { border-color: rgba(var(--primary-rgb), 0.4); box-shadow: var(--shadow-md); }
.done-usage__cta.hidden { display: none; }

/* The low-exports note as one control: the whole strip answers a click, so the
   target is the message rather than a six-character link beside it. */
.export-usage-note--clickable { cursor: pointer; }
.export-usage-note--clickable:hover { filter: brightness(0.985); }
