/*
 * SUNO - Editorial layer
 * ============================================================================
 * Loaded after index.css, before motion.css.
 *
 * index.css is the design system: it makes every control correct, accessible and
 * consistent. It is deliberately quiet, and at display sizes quiet reads as
 * unfinished -- a 1.6rem page title above a 17px body has almost no hierarchy,
 * so every page opens at the same volume as its own form labels.
 *
 * This file adds the volume. It is a thin layer with a narrow remit:
 *
 *   - a real display voice for the few places that carry a page
 *   - asymmetry, so a layout has a reading order instead of a centre line
 *   - surface texture, so large empty areas are not flat vector fields
 *   - typographic detail: balanced wraps, tabular numerals, tracking that
 *     tightens as size grows
 *
 * WHAT IT DOES NOT TOUCH
 * Colour and contrast are settled in tokens.css and were measured there. This
 * file introduces no new colour values and no new foreground/background pairs;
 * every colour below is an existing semantic token, so both themes stay AA
 * without re-deriving anything. Nothing here changes a control's hit area,
 * label association, or focus ring.
 *
 * WHERE THE VOLUME GOES
 * Loudest on the surfaces an operator meets once or reads slowly: the auth
 * pages, page openings, empty states, the rendered-alert payoff, analytics.
 * Quietest on the surfaces they work IN under time pressure -- the recipient
 * step, admin tables, the field grid. Bold there means firmer hierarchy and
 * better numerals, not more decoration between them and the send button.
 */


/* ===========================================================================
   1. TEXTURE AND AMBIENCE

   `.suno-textured` lays two non-interactive washes behind a section: a fine
   fractal grain (tokens.css §14) and an off-centre radial tint.

   Both live on pseudo-elements with pointer-events: none and a negative
   z-index, so they cannot intercept a click and cannot alter the stacking of
   anything real. The host needs a positioning context and its own stacking
   context, hence isolation: isolate -- without it a negative z-index child can
   escape behind an ancestor's background and vanish.
   =========================================================================== */

.suno-textured {
  position: relative;
  isolation: isolate;
}

.suno-textured::before,
.suno-textured::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: var(--suno-z-under);
}

/* Grain. Fixed 140px tile, so it does not scale with the element and stays the
   same physical grain on every surface. */
.suno-textured::before {
  background-image: var(--suno-noise);
  background-size: 140px 140px;
  opacity: var(--suno-noise-opacity);
}

/* Tint. Sits above the grain so the wash colours the grain rather than the
   grain muddying the wash. */
.suno-textured::after {
  background-image: var(--suno-ambient);
}

/* Grain alone, for surfaces that should not carry brand tint (tables, panels). */
.suno-grain { position: relative; isolation: isolate; }
.suno-grain::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: var(--suno-z-under);
  background-image: var(--suno-noise);
  background-size: 140px 140px;
  opacity: var(--suno-noise-opacity);
}


/* ===========================================================================
   2. THE PAGE OPENING

   Replaces the centred-title-over-hairline pattern with a three-part opening:

     kicker  small, mono, tracked wide. Says WHERE you are -- which is exactly
             the job the navbar's active marker does badly on the two pages it
             does not branch on.
     title   display scale, tight tracking and leading.
     lede    one sentence at a real measure (65ch), not a 0.9rem grey subtitle.

   Asymmetric on purpose: the text column is bounded and left-aligned, with a
   meta slot on the right that most pages leave empty. A title that stops at
   ~22ch against open space has a reading order; a title centred in the
   viewport has a centre line and nothing else.
   =========================================================================== */

.suno-hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: var(--suno-space-6);
  padding: var(--suno-space-10) var(--suno-space-6) var(--suno-space-8);
  margin-bottom: var(--suno-space-8);
  border-bottom: 1px solid var(--suno-border);
  /* Pull the wash out to the container edges without a full-bleed wrapper. */
  border-radius: var(--suno-radius-md);
}

.suno-hero__kicker {
  display: block;
  font-family: var(--suno-font-mono);
  font-size: var(--suno-text-xs);
  font-weight: 500;
  letter-spacing: var(--suno-tracking-wide);
  text-transform: uppercase;
  color: var(--suno-accent-text);
  margin-bottom: var(--suno-space-3);
}

.suno-hero__title {
  font-size: var(--suno-text-4xl);
  font-weight: var(--suno-weight-display);
  line-height: var(--suno-leading-display);
  letter-spacing: var(--suno-tracking-display);
  color: var(--suno-text);
  margin: 0;
  /* balance evens the line lengths of a short heading instead of leaving one
     orphaned word on the last line. Ignored where unsupported. */
  text-wrap: balance;
  max-width: 24ch;
}

.suno-hero__lede {
  margin: var(--suno-space-4) 0 0;
  max-width: var(--suno-measure);
  font-size: var(--suno-text-lg);
  line-height: var(--suno-leading-snug);
  color: var(--suno-text-secondary);
  /* pretty avoids orphans in running text without balance's line-length
     equalising, which is wrong for anything over about three lines. */
  text-wrap: pretty;
}

/* Right-hand slot: counts, timestamps, a control. Mono, so digits align. */
.suno-hero__meta {
  font-family: var(--suno-font-mono);
  font-size: var(--suno-text-sm);
  font-variant-numeric: tabular-nums;
  color: var(--suno-text-muted);
  text-align: right;
  white-space: nowrap;
}

@media (max-width: 767.98px) {
  .suno-hero {
    grid-template-columns: minmax(0, 1fr);
    padding: var(--suno-space-8) var(--suno-space-4) var(--suno-space-6);
    margin-bottom: var(--suno-space-6);
  }
  .suno-hero__meta { text-align: left; }
  .suno-hero__title { max-width: none; }
}

/* Lift the legacy .page-header (admin, developer console, onboard) onto the same
   display voice without needing every template edited in this pass. */
.page-header { padding-top: var(--suno-space-8); }

.page-header h1 {
  font-size: var(--suno-text-3xl);
  font-weight: var(--suno-weight-display);
  line-height: var(--suno-leading-display);
  letter-spacing: var(--suno-tracking-display);
  text-wrap: balance;
}

.page-header .page-subtitle {
  font-size: var(--suno-text-base);
  max-width: var(--suno-measure);
  line-height: var(--suno-leading-snug);
  text-wrap: pretty;
}


/* ===========================================================================
   3. HEADINGS AND PROSE DETAIL

   Section headings currently sit at Bootstrap's defaults with normal tracking,
   which at 24-32px is visibly loose against Archivo's own sidebearings.
   =========================================================================== */

h1, h2, .h1, .h2 {
  letter-spacing: var(--suno-tracking-display);
  text-wrap: balance;
}

h3, h4, .h3, .h4 {
  letter-spacing: var(--suno-tracking-tight);
  text-wrap: balance;
}

p { text-wrap: pretty; }

/* .suno-prose was here, bounding running text to --suno-measure. Deleted unused:
   the only genuinely prose-shaped pages in the app are /welcome and the legal pages,
   and both arrived with their own self-contained vocabularies (welcome.css,
   .legal__*). Nothing on an operator surface is long-form enough to need it, and
   keeping a plausible-but-unused class is what this pass exists to stop. */

/* Every number in this app benefits from tabular figures: counts that change on
   poll, phone numbers, durations, percentages. Proportional digits make a
   changing value jitter its own width. */
.suno-num,
.stat-value,
.suno-hero__meta,
td[data-num],
.table td.text-end {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* Identifier-shaped strings: job ids, filenames, API keys, phone numbers. */
.suno-mono {
  font-family: var(--suno-font-mono);
  font-size: 0.9375em;   /* Plex Mono runs large next to Archivo at equal px */
  letter-spacing: 0;
}


/* ===========================================================================
   4. EMPTY STATES

   PR #79 composed these properly (icon / title / body, and true-empty split
   from filtered-empty). What they lack is presence: an empty history is a whole
   viewport holding a 17px title, which reads as a failed load rather than as a
   state someone designed.

   The icon becomes a large tinted plate on a textured ground, and the title
   moves to the display scale. This is the one place in the app where decoration
   is the point -- there is nothing else on screen to respect.
   =========================================================================== */

.ewai-empty {
  padding-block: var(--suno-space-16);
  border-radius: var(--suno-radius-md);
}

.ewai-empty__icon {
  width: 4.5rem;
  height: 4.5rem;
  font-size: 2rem;
  /* Tinted plate rather than a grey circle: brand teal at low alpha over the
     page ground. --suno-ui carries the glyph and clears 3:1 in both themes. */
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--suno-ui) 12%, transparent) 0%,
      color-mix(in srgb, var(--suno-ui) 4%, transparent) 100%),
    var(--suno-surface);
  border-color: color-mix(in srgb, var(--suno-ui) 24%, var(--suno-border));
  margin-bottom: var(--suno-space-4);
}

.ewai-empty__title {
  font-size: var(--suno-text-xl);
  font-weight: var(--suno-weight-display);
  line-height: var(--suno-leading-snug);
  letter-spacing: var(--suno-tracking-tight);
  color: var(--suno-text);
  text-wrap: balance;
}

.ewai-empty__body {
  max-width: 46ch;
  font-size: var(--suno-text-base);
  line-height: var(--suno-leading-body);
  text-wrap: pretty;
}

/* color-mix is Baseline 2023 but not universal. Without it the two declarations
   above are dropped and the component keeps index.css's flat grey plate, which
   is the previous design and perfectly legible. */
@supports not (background: color-mix(in srgb, red 10%, transparent)) {
  .ewai-empty__icon {
    background: var(--suno-bg);
    border-color: var(--suno-border);
  }
}


/* ===========================================================================
   5. SURFACES

   index.css deliberately shipped one shadow and flat hairline cards, on the
   grounds that elevation should mean something. That holds. What it produced in
   practice is that a static panel and a clickable card look identical, so
   nothing on the page signals what can be pressed.

   The rule applied here: elevation encodes INTERACTIVITY, not decoration. A
   card that does nothing stays flat. A card that is a click target rests at
   --shadow-xs and rises on hover (motion.css §4). Radius varies by nesting --
   containers softer than the controls inside them, which reads as depth without
   a shadow doing the work.
   =========================================================================== */

.card {
  border-radius: var(--suno-radius-md);
}

/* The .shadow-sm utility that most cards in the templates carry is handled at
   the source, in index.css §1b, by pointing Bootstrap's own --bs-box-shadow-sm
   at the tinted scale. Nothing to override here. */

/* Inner controls sit one radius step tighter than their container. */
.card .form-control,
.card .form-select,
.card .btn:not(.btn-lg):not(.btn-link) {
  border-radius: var(--suno-radius-sm);
}

/* The payoff surface. The rendered alert is the only thing in the app that
   carries brand-hued elevation, which is what makes it read as the result
   rather than as another panel. */
.workspace-stage {
  border-radius: var(--suno-radius-md);
}

.workspace-stage:has(.workspace-stage__reveal),
.workspace-stage:has(video[src]) {
  box-shadow: var(--suno-shadow-accent);
}

/* Media surfaces. Replaces the hardcoded #000 / #0b0e11 letterbox fills, which
   were the same value in both themes and one of them was pure black -- against
   this system's own rule that #000 is never used. */
.video-container,
.video-container video,
.workspace-stage__empty {
  background: var(--suno-media-bg, #0b0e11);
  border-radius: var(--suno-radius-sm);
}


/* ===========================================================================
   5b. ACTIVITY RAIL

   Sits below the preview stage on the workspace, in the space the sticky panel
   used to leave empty for the full height of the form.

   Built as a hairline-separated list rather than a stack of cards: it is a log,
   and five bordered cards beside a form would compete with the form. Same
   reasoning as the .ewai-readout strip on the history page.
   =========================================================================== */

.activity-rail {
  margin-top: var(--suno-space-6);
  padding-top: var(--suno-space-5);
  border-top: 1px solid var(--suno-border);
}

.activity-rail__heading {
  font-family: var(--suno-font-mono);
  font-size: var(--suno-text-xs);
  font-weight: 500;
  letter-spacing: var(--suno-tracking-wide);
  text-transform: uppercase;
  color: var(--suno-text-muted);
  margin: 0 0 var(--suno-space-3);
}

.activity-rail__none {
  margin: 0;
  font-size: var(--suno-text-sm);
  line-height: var(--suno-leading-snug);
  color: var(--suno-text-muted);
  max-width: 40ch;
}

.activity-rail__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.activity-rail__item {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--suno-space-3);
  padding: var(--suno-space-3) 0;
  border-bottom: 1px solid var(--suno-border);
}

.activity-rail__item:last-child { border-bottom: 0; }

/* Status dot. Severity tokens, not brand: a failed render is a failure, and this
   is the same colour vocabulary the rest of the app uses for one. */
.activity-rail__dot {
  width: 8px;
  height: 8px;
  border-radius: var(--suno-radius-full);
  background: var(--suno-text-muted);
}

.activity-rail__dot[data-status="complete"] { background: var(--suno-sev-ok); }
.activity-rail__dot[data-status="failed"]   { background: var(--suno-sev-emergency); }
.activity-rail__dot[data-status="running"],
.activity-rail__dot[data-status="queued"]   { background: var(--suno-ui); }

/* A live job's dot pulses, so "still going" is legible without reading the text.
   The only infinite animation in the app, and the one place it is warranted:
   something genuinely is still happening. Gated, like everything else. */
@media (prefers-reduced-motion: no-preference) {
  .activity-rail__item.is-live .activity-rail__dot {
    animation: activity-pulse 1.8s var(--suno-ease) infinite;
  }
}

@keyframes activity-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: 0.45; transform: scale(0.82); }
}

.activity-rail__body { display: flex; flex-direction: column; min-width: 0; }

.activity-rail__title {
  font-size: var(--suno-text-sm);
  color: var(--suno-text);
  /* Ellipsis rather than wrap: the rail is narrow and a two-line title would break
     the alignment of every row against its dot. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Mono and tabular: these are percentages, durations and timestamps, and they
   change on poll -- proportional digits would make each row jitter its own width. */
.activity-rail__meta {
  font-family: var(--suno-font-mono);
  font-size: var(--suno-text-xs);
  font-variant-numeric: tabular-nums;
  color: var(--suno-text-muted);
  /* Wraps, unlike the title. A failure reason is the one thing in this row worth
     reading in full, and the row can afford to be two lines tall. */
  line-height: var(--suno-leading-snug);
}

/* A failed row's reason carries the severity colour, since it is the reason. */
.activity-rail__item:has(.activity-rail__dot[data-status="failed"]) .activity-rail__meta {
  color: var(--suno-sev-emergency);
}

.activity-rail__action {
  font-size: var(--suno-text-sm);
  font-weight: 500;
  color: var(--suno-action);
  text-decoration: none;
  white-space: nowrap;
}

.activity-rail__action:hover { color: var(--suno-action-hover); text-decoration: underline; }

/* There was an .activity-rail__failed rule here, for the failure reason as a
   right-hand element. It truncated at 14ch, which cut the useful half off every
   real error message, so the reason moved onto the meta line instead. Removed
   rather than left behind: dead CSS outlives the markup that explains it. */


/* ===========================================================================
   5d. DRAFT BANNER

   The offer to restore unsent work. Quiet on purpose: it is a convenience, and it
   sits directly above the fields an operator came to fill in. It carries the
   accent hue rather than a severity one, because nothing is wrong.
   =========================================================================== */

.draft-banner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--suno-space-3);
  margin-bottom: var(--suno-space-4);
  padding: var(--suno-space-3) var(--suno-space-4);
  border: 1px solid var(--suno-border);
  border-left: 3px solid var(--suno-ui);
  border-radius: var(--suno-radius-sm);
  background: var(--suno-bg);
}

.draft-banner__text {
  font-size: var(--suno-text-sm);
  line-height: var(--suno-leading-snug);
  color: var(--suno-text-secondary);
  max-width: 52ch;
}

.draft-banner__actions {
  display: flex;
  gap: var(--suno-space-2);
  flex-shrink: 0;
}


/* ===========================================================================
   5c. RECIPIENT REVIEW

   The step between "send" and sending. Deliberately not a modal: the operator
   often needs to edit the textarea it is describing, and a modal would cover the
   thing it is asking them to check. It is an inline panel below the dispatch bar,
   so the list and the summary of it are on screen together.

   Carries the emergency hue on its left edge because it guards the one
   irreversible action in the product.
   =========================================================================== */

.recipient-review {
  margin-top: var(--suno-space-5);
  padding: var(--suno-space-5);
  background: var(--suno-surface);
  border: 1px solid var(--suno-border);
  border-left: 3px solid var(--suno-sev-emergency);
  border-radius: var(--suno-radius-md);
  box-shadow: var(--suno-shadow-sm);
}

/* No focus ring on the panel itself: it takes focus programmatically so a keyboard
   user lands on the decision, which is not the same as it being an interactive
   control. The buttons inside keep their own rings. */
.recipient-review:focus { outline: none; }

.recipient-review__heading {
  font-size: var(--suno-text-lg);
  font-weight: var(--suno-weight-display);
  letter-spacing: var(--suno-tracking-tight);
  margin: 0 0 var(--suno-space-3);
}

/* The count is the number the operator is actually agreeing to. Tabular, because
   it changes as they edit the list behind the panel. */
.recipient-review__count {
  margin: 0 0 var(--suno-space-2);
  font-size: var(--suno-text-base);
  font-variant-numeric: tabular-nums;
}

.recipient-review__count strong {
  font-family: var(--suno-font-mono);
  font-size: var(--suno-text-lg);
}

.recipient-review__line {
  margin: 0 0 var(--suno-space-1);
  font-size: var(--suno-text-sm);
  color: var(--suno-text-secondary);
}

.recipient-review__invalid {
  margin-top: var(--suno-space-3);
  padding: var(--suno-space-3);
  border-radius: var(--suno-radius-sm);
  background: var(--suno-danger-surface);
  border: 1px solid var(--suno-sev-emergency);
  font-size: var(--suno-text-sm);
}

/* Mono, and scrollable rather than unbounded: a pasted CSV can produce a long list
   of rejects, and the panel must not push its own confirm button off screen. */
.recipient-review__list {
  margin: 0;
  padding-left: var(--suno-space-5);
  max-height: 9rem;
  overflow-y: auto;
  font-family: var(--suno-font-mono);
  font-size: var(--suno-text-xs);
}

.recipient-review__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--suno-space-3);
  margin-top: var(--suno-space-4);
}


/* ===========================================================================
   6. REMOVED: asymmetric grid and section rhythm

   .suno-split, .suno-split--reverse, .suno-offset-up, .suno-section,
   .suno-section-tight and .suno-fill were defined here and referenced by nothing.

   They were written for marketing-shaped pages -- a bounded text column beside a
   wider one, full-viewport sections, deliberate overlap. This app's operator
   surfaces are dense two-column working views inside a Bootstrap grid, and every
   attempt to place them read as decoration between an operator and a form.

   Deleted rather than left available "in case", because that is precisely how
   .pulse-animation and .fade-in survived in index.css long enough to be found by
   an audit: defined, plausible, and used nowhere. The public /welcome page does
   want this vocabulary and has its own welcome.css; if that page is ever folded
   into the design system, take these from git history rather than guessing again.
   =========================================================================== */
