/*
 * SUNO - Global Design System
 * ============================================================================
 * Archivo + IBM Plex Mono on plain Bootstrap 5. Load order per page:
 *   vendor/bootstrap -> vendor/bootstrap-icons -> fonts.css -> tokens.css -> this
 *
 * The design language is "instrument": the look of tools people trust while
 * something is going wrong. Four things carry it, and none of them is decoration:
 *
 *   TYPE     Archivo for UI, IBM Plex Mono for anything numeric or
 *            identifier-shaped. This app is saturated with job ids, timestamps,
 *            counts, phone numbers and API keys, and those are far easier to scan
 *            and compare in a monospace.
 *   SHAPE    One tight radius scale (4px / 6px). Precise corners read as
 *            instrumentation; 12-16px reads consumer.
 *   SURFACE  Hairlines and space instead of card-with-shadow. Elevation is
 *            reserved for things that genuinely float (modal, dropdown, toast)
 *            and, since editorial.css §5, for things that are pressable --
 *            a static panel stays flat, a click target rests and rises.
 *   COLOUR   ConnectHear teal as chrome. Severity owns its own hues, because in
 *            alert software colour carries meaning.
 *
 * Sections 1 and 1b below are the whole mechanism. --ewai-* is kept as the app's
 * vocabulary (sections 3-20 already reference it throughout, so aliasing is far
 * fewer edits than renaming) and now resolves to --suno-*. Section 1b sets
 * Bootstrap's OWN --bs-* variables, which is what lets the framework restyle at
 * the same specificity instead of being fought with !important.
 */

/* ==========================================================================
   1. TOKENS - aliases onto the design system in tokens.css
   ========================================================================== */
:root {
  /* Brand. Note --ewai-primary is teal-700, NOT the published brand teal:
     #33cccc is 1.97:1 on white and cannot carry a label. bg-primary text-white
     appears in seven places in the templates. */
  --ewai-primary:       var(--suno-action);
  --ewai-primary-dark:  var(--suno-action-hover);
  --ewai-accent:        var(--suno-orange-500);

  /* Severity, not brand. Deliberately distinct hues. */
  --ewai-danger:        var(--suno-sev-emergency);
  --ewai-success:       var(--suno-sev-ok);
  --ewai-info:          var(--suno-sev-info);
  --ewai-warning:       var(--suno-sev-warning);

  /* Neutrals */
  --ewai-bg:            var(--suno-bg);
  --ewai-surface:       var(--suno-surface);
  --ewai-border:        var(--suno-border);
  --ewai-border-control: var(--suno-border-control);
  --ewai-text:          var(--suno-text);
  --ewai-text-secondary: var(--suno-text-secondary);
  --ewai-text-muted:    var(--suno-text-muted);

  /* Auth-page field. Deep teal rather than the old navy, so the one large brand
     surface in the product actually reads as ConnectHear. */
  --ewai-hero-gradient: linear-gradient(150deg,
                          var(--suno-teal-900) 0%,
                          #0c3a3a 55%,
                          #10262e 100%);

  /* Code surface */
  --ewai-code-bg:       #12171b;
  --ewai-code-fg:       #dde3e8;

  /* Layout */
  --ewai-navbar-height: 64px;
  --ewai-sidebar-width: 240px;
  --ewai-card-radius:   var(--suno-radius-md);
  --ewai-btn-radius:    var(--suno-radius-sm);
  --ewai-input-radius:  var(--suno-radius-sm);

  /* Elevation: flat by default. Grouping is done with hairlines and space, so
     these resolve to nothing and only real overlays get a shadow. */
  --ewai-shadow-sm:     none;
  --ewai-shadow-md:     none;
  --ewai-shadow-overlay: var(--suno-shadow-overlay);

  /* Typography */
  --ewai-font:          var(--suno-font-sans);
  --ewai-mono-font:     var(--suno-font-mono);
}

/* ==========================================================================
   1b. BOOTSTRAP BRIDGE
   Bootstrap 5.3 builds its components from --bs-* custom properties, so setting
   them here restyles buttons, inputs, tables, cards, badges, dropdowns and the
   navbar at the framework's own specificity. That is the reason this file needs
   almost no !important: we are configuring Bootstrap, not overriding it.
   ========================================================================== */
:root {
  --bs-body-font-family: var(--suno-font-sans);
  --bs-body-font-size:   var(--suno-text-base);
  --bs-body-line-height: var(--suno-leading-body);
  --bs-body-color:       var(--suno-text);
  --bs-body-bg:          var(--suno-bg);
  --bs-emphasis-color:   var(--suno-text);
  --bs-secondary-color:  var(--suno-text-secondary);
  --bs-tertiary-color:   var(--suno-text-muted);
  --bs-font-monospace:   var(--suno-font-mono);

  --bs-border-color:            var(--suno-border);
  --bs-border-color-translucent: var(--suno-border);
  --bs-border-radius:      var(--suno-radius-md);
  --bs-border-radius-sm:   var(--suno-radius-sm);
  --bs-border-radius-lg:   var(--suno-radius-md);
  --bs-border-radius-xl:   var(--suno-radius-md);
  --bs-border-radius-2xl:  var(--suno-radius-md);
  --bs-border-radius-pill: var(--suno-radius-full);

  --bs-primary:     var(--suno-action);
  --bs-primary-rgb: 31, 122, 122;   /* matches --suno-teal-700 #1f7a7a */
  --bs-link-color:            var(--suno-action);
  --bs-link-color-rgb:        31, 122, 122;
  --bs-link-hover-color:      var(--suno-action-hover);
  --bs-link-hover-color-rgb:  24, 98, 98;

  /* Severity. Never aliased to brand. */
  --bs-danger:      var(--suno-sev-emergency);
  --bs-danger-rgb:  179, 38, 30;
  --bs-success:     var(--suno-sev-ok);
  --bs-success-rgb: 27, 107, 58;
  --bs-warning:     var(--suno-sev-warning);
  --bs-warning-rgb: 138, 83, 0;
  --bs-info:        var(--suno-sev-info);
  --bs-info-rgb:    28, 95, 168;

  /* Bootstrap's default focus ring is its own blue. */
  --bs-focus-ring-color: rgba(31, 122, 122, 0.35);
  --bs-focus-ring-width: 3px;

  /* Bootstrap ships inline <code> in its own pink (#d63384). */
  --bs-code-color: var(--suno-accent-text);

  /* Bootstrap's .shadow-sm / .shadow / .shadow-lg utilities paint from these and
     do it with !important, so they cannot be neutralised by a plain rule -- the
     `box-shadow: none` on .card below only ever applied to cards WITHOUT the
     utility class, and most templates use .shadow-sm. Pointing the variables at
     the tinted scale in tokens.css §11 brings those utilities into the system
     instead of leaving them painting untinted black. */
  --bs-box-shadow-sm: var(--suno-shadow-xs);
  --bs-box-shadow:    var(--suno-shadow-sm);
  --bs-box-shadow-lg: var(--suno-shadow-md);

  /* Bootstrap 5.3 builds .alert-* (and .text-*-emphasis, .bg-*-subtle) from these
     three variables per colour, NOT from --bs-info / --bs-danger. Bootstrap ships dark
     values for them under its own [data-bs-theme="dark"] selector -- which this app
     does not use, because theming here is driven by [data-theme]. The result was that
     every alert in the app kept a pale panel in dark mode: a light blue info box on a
     #0e1216 page. Mapping them onto the callout tokens fixes all of them at once. */
  --bs-info-bg-subtle:       var(--suno-info-surface);
  --bs-info-border-subtle:   var(--suno-info-edge);
  --bs-info-text-emphasis:   var(--suno-sev-info);
  --bs-success-bg-subtle:     var(--suno-ok-surface);
  --bs-success-border-subtle: var(--suno-ok-edge);
  --bs-success-text-emphasis: var(--suno-sev-ok);
  --bs-warning-bg-subtle:     var(--suno-warning-surface);
  --bs-warning-border-subtle: var(--suno-warning-edge);
  --bs-warning-text-emphasis: var(--suno-sev-warning);
  --bs-danger-bg-subtle:      var(--suno-danger-surface);
  --bs-danger-border-subtle:  var(--suno-danger-edge);
  --bs-danger-text-emphasis:  var(--suno-sev-emergency);
}

/* Dark counterparts of the bridge values that cannot be expressed as a token
   reference. Bootstrap builds several utilities from *-rgb triples --
   .text-primary is rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) -- and a
   raw triple cannot follow a var(), so these have to be restated per theme. This
   is exactly what the dark audit caught: .text-primary stayed at teal-700 (the
   light-mode action colour) on a dark surface, at 3.37:1. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bs-primary-rgb:          100, 216, 216;   /* --suno-teal-400 #64d8d8 */
    --bs-link-color-rgb:       100, 216, 216;
    --bs-link-hover-color-rgb: 141, 226, 226;   /* --suno-teal-300 */
    --bs-danger-rgb:  255, 157, 149;
    --bs-success-rgb: 111, 211, 152;
    --bs-warning-rgb: 232, 181,  61;
    --bs-info-rgb:    143, 189, 240;
    --bs-focus-ring-color: rgba(100, 216, 216, 0.4);
    --bs-light-rgb: 23, 28, 33;
    --bs-dark-rgb: 233, 237, 240;
  }
}
[data-theme="dark"] {
  --bs-primary-rgb:          100, 216, 216;
  --bs-link-color-rgb:       100, 216, 216;
  --bs-link-hover-color-rgb: 141, 226, 226;
  --bs-danger-rgb:  255, 157, 149;
  --bs-success-rgb: 111, 211, 152;
  --bs-warning-rgb: 232, 181,  61;
  --bs-info-rgb:    143, 189, 240;
  --bs-focus-ring-color: rgba(100, 216, 216, 0.4);
  /* .bg-light is used on six elements across broadcast, admin and onboard, and
     Bootstrap paints it from --bs-light-rgb with !important -- so without this it
     stays near-white in dark mode and swallows the text on top of it. */
  --bs-light-rgb: 23, 28, 33;    /* --suno-surface */
  --bs-dark-rgb: 233, 237, 240;  /* --suno-text */
}

/* ==========================================================================
   2. BASE RESETS
   ========================================================================== */
*, *::before, *::after {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
}

/* Flex column so the footer sits at the bottom of short pages instead of floating
   mid-viewport with dead space beneath it. onboarding.css used to do this, scoped
   to admin only; removing that fork was right, but it left every short page (admin
   with one domain card, an empty history) with a footer hanging in the middle.
   base.html gives every page the same <main>, so this belongs here. */
body {
  font-family: var(--ewai-font);
  background-color: var(--ewai-bg);
  color: var(--ewai-text);
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* main takes the slack; the footer keeps its natural height. The 2rem that used to
   be body's padding-bottom lives here now, so it does not add space below the
   footer. */
body > main {
  flex: 1 0 auto;
  padding-bottom: var(--suno-space-8);
}

body > .suno-footer {
  flex: 0 0 auto;
}

/* Display type: tightened tracking and pulled-in leading. Archivo is fairly wide,
   so headings need negative tracking to stop reading as billboard copy. */
/* Deliberately no `color` here. Headings must inherit, because some of them sit on
   the dark teal auth panel: setting an explicit near-black turned
   "ConnectHear SUNO System" into black-on-dark-teal. */
h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
  font-weight: 600;
  letter-spacing: var(--suno-tracking-tight);
  line-height: var(--suno-leading-tight);
  text-wrap: balance;
}

h1, .h1 { font-size: var(--suno-text-2xl); font-weight: 700; }
h2, .h2 { font-size: var(--suno-text-xl); }
h3, .h3 { font-size: var(--suno-text-lg); }
h4, .h4, h5, .h5, h6, .h6 { font-size: var(--suno-text-base); }

/* Prose gets a measure. Unbounded body text across a 1440px viewport is
   unreadable no matter how good the type is. */
p { max-width: var(--suno-measure); }

/* Data is monospaced. Job ids, timestamps, counts, keys, phone numbers and
   tabular figures are all easier to scan and compare in a fixed pitch, and this
   is the single clearest signal that the app is an instrument. */
code, kbd, samp, pre, .font-mono,
.ewai-num, td .ewai-num, .table td.ewai-num {
  font-family: var(--ewai-mono-font);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1, "zero" 1;
}

/* ----------------------------------------------------------------- TEXTURE
   A very faint grain over the whole page. Completely flat fills read sterile and
   slightly synthetic; a little noise gives the surfaces something to sit on.
   Deliberately built as a FIXED, pointer-events-none pseudo-element on <body>
   rather than a background on scrolling containers -- a filter or background on a
   scrolling element forces continuous GPU repaints and wrecks scroll performance
   on mid-range hardware, which is what an agency laptop actually is.
   Inline SVG feTurbulence, so there is no image request. */
body::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 60;
  pointer-events: none;
  opacity: 0.028;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ------------------------------------------------------------------ FOCUS
   One visible ring for the whole app. Before this, keyboard users got whatever
   the browser drew by default, and only .form-control / .form-select had any
   focus styling at all -- so tabbing through the navbar, the tabs, or a link was
   effectively invisible. :focus-visible rather than :focus so it appears for
   keyboard and programmatic focus but not on mouse clicks. */
:focus-visible {
  outline: var(--suno-focus-outline-width) solid var(--suno-action);
  outline-offset: 2px;
  border-radius: var(--suno-radius-sm);
}

/* The ring has to contrast with whatever it sits ON, and the action colour is a
   mid teal -- against the navbar's teal-800 it measured 1.39:1, so keyboard focus
   was effectively invisible in the chrome. White is 7.09:1 there. Same reasoning on
   the auth panel and the error page, both dark brand fields. */
.ewai-nav :focus-visible,
.auth-branding :focus-visible,
.error-page :focus-visible {
  outline-color: #fff;
}
/* Elements that already draw their own ring should not get both. */
.form-control:focus-visible,
.form-select:focus-visible,
.form-check-input:focus-visible,
.btn:focus-visible {
  outline: none;
}

/* Skip link. Required for keyboard users and previously absent; base.html now
   fills the skip_link block. Off-screen until focused. */
.skip-link {
  position: absolute;
  left: var(--suno-space-4);
  top: -3rem;
  z-index: 100;
  padding: var(--suno-space-2) var(--suno-space-4);
  background: var(--suno-surface);
  color: var(--suno-action);
  border: 1px solid var(--suno-action);
  border-radius: var(--suno-radius-sm);
  font-size: var(--suno-text-sm);
  font-weight: 500;
  text-decoration: none;
  transition: top var(--suno-duration-fast) var(--suno-ease);
}
.skip-link:focus {
  top: var(--suno-space-4);
}

/* --------------------------------------------------------------- REDUCED MOTION
   The app ships three keyframe animations, one of them an infinite pulse on the
   "Proceed to broadcast" CTA, plus smooth scrolling. None of it was gated. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Small caps-y labels. Positive tracking, unlike display type. */
.ewai-label {
  font-size: var(--suno-text-xs);
  font-weight: 500;
  letter-spacing: var(--suno-tracking-wide);
  text-transform: uppercase;
  color: var(--suno-text-muted);
}

a {
  color: var(--ewai-primary);
  text-decoration: none;
  transition: color 0.15s ease;
}
a:hover {
  color: var(--ewai-primary-dark);
  text-decoration: underline;
}

/* ==========================================================================
   3. NAVIGATION
   ========================================================================== */
/* Bootstrap 5.3 builds the navbar from --bs-navbar-*, so setting those here wins
   at the framework's own specificity. This block used to need five !important
   declarations, all of them fighting the bg-primary utility and .navbar-dark that
   the markup itself asked for; both have been removed from partials/navbar.html.
   Deep teal rather than the brand hue, because white text sits on it. */
.navbar.ewai-nav {
  --bs-navbar-color:             rgba(255, 255, 255, 0.82);
  --bs-navbar-hover-color:       #fff;
  --bs-navbar-active-color:      #fff;
  --bs-navbar-brand-color:       #fff;
  --bs-navbar-brand-hover-color: #fff;
  --bs-navbar-padding-y:         0;
  --bs-navbar-toggler-border-color: rgba(255, 255, 255, 0.28);
  --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255,255,255,0.85%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");

  background-color: var(--suno-teal-800);
  min-height: var(--ewai-navbar-height);
  /* A hairline, not a drop shadow. The chrome should sit flat against the page. */
  border-bottom: 1px solid var(--suno-teal-900);
}

.navbar.ewai-nav .navbar-brand {
  display: flex;
  align-items: center;
  gap: var(--suno-space-2);
  padding-block: var(--suno-space-3);
}
/* Replaces the inline style="height: 36px" that was on the mark in navbar.html.
   width: auto keeps the intrinsic ratio while the declared width/height attributes
   reserve the box, so the nav no longer shifts on first paint. */
.navbar.ewai-nav .navbar-brand img {
  height: 36px;
  width: auto;
}

.navbar.ewai-nav .nav-link {
  font-weight: 500;
  font-size: var(--suno-text-sm);
  letter-spacing: 0.01em;
  padding: 0 var(--suno-space-4);
  height: var(--ewai-navbar-height);
  display: flex;
  align-items: center;
  /* The active marker is a 2px rule in brand orange: the one place the warm half
     of the palette earns its keep, and the only accent on the chrome. */
  border-bottom: 2px solid transparent;
  transition: color var(--suno-duration-fast) var(--suno-ease),
              border-color var(--suno-duration-fast) var(--suno-ease);
}

.navbar.ewai-nav .nav-link:hover {
  border-bottom-color: rgba(255, 255, 255, 0.35);
}

.navbar.ewai-nav .nav-link.active {
  border-bottom-color: var(--suno-orange-500);
}

.navbar.ewai-nav .navbar-text {
  color: rgba(255, 255, 255, 0.75);
  font-size: var(--suno-text-sm);
}

/* The user menu is chrome, so it follows the navbar's own colour, and its email
   is an identifier, so it is monospaced. */
.navbar.ewai-nav .dropdown-toggle {
  font-size: var(--suno-text-sm);
}
.navbar.ewai-nav #userEmail {
  font-family: var(--ewai-mono-font);
  font-size: var(--suno-text-xs);
  opacity: 0.85;
}

/* The same address repeated as the menu header. It was set in the UI sans while the
   navbar showed it in mono, so the identical string appeared in two typefaces a few
   pixels apart. */
#userEmailMenu {
  font-family: var(--ewai-mono-font);
  font-size: var(--suno-text-xs);
  font-weight: 400;
  color: var(--suno-text-muted);
  text-transform: none;
  letter-spacing: 0;
}

/* Theme control inside the user menu. Bootstrap's .active for an outline button is
   grey, so the chosen option read as DISABLED rather than selected -- the opposite
   of what it means. Selection is the action colour. */
.suno-theme-group .btn {
  --bs-btn-color: var(--suno-text-secondary);
  --bs-btn-border-color: var(--suno-border-control);
  --bs-btn-hover-bg: var(--suno-bg);
  --bs-btn-hover-border-color: var(--suno-border-control);
  --bs-btn-hover-color: var(--suno-text);
  --bs-btn-active-bg: var(--suno-action);
  --bs-btn-active-border-color: var(--suno-action);
  --bs-btn-active-color: var(--suno-on-action);
  font-size: var(--suno-text-xs);
  font-weight: 500;
}

.navbar.ewai-nav .btn-outline-light {
  border-color: rgba(255,255,255,.4);
  color: rgba(255,255,255,.9);
  font-size: 0.8rem;
  padding: 0.3rem 0.75rem;
  border-radius: var(--ewai-btn-radius);
  transition: background 0.15s, color 0.15s;
}
.navbar.ewai-nav .btn-outline-light:hover {
  background: rgba(255,255,255,.15);
  color: #fff;
}

/* ==========================================================================
   4. CARDS
   ========================================================================== */
/* Cards are flat panels, not floating objects. A shadow on every container means
   nothing is actually elevated, and it is the clearest tell of an untouched
   Bootstrap build. Grouping here comes from a hairline and generous padding; the
   one real elevation token is reserved for modals, dropdowns and toasts.
   Nothing lifts or animates on hover: a panel is not a button. */
.card {
  --bs-card-border-color: var(--suno-border);
  --bs-card-border-radius: var(--ewai-card-radius);
  --bs-card-inner-border-radius: var(--ewai-card-radius);
  --bs-card-bg: var(--suno-surface);
  --bs-card-spacer-y: var(--suno-space-6);
  --bs-card-spacer-x: var(--suno-space-6);
  --bs-card-cap-bg: transparent;
  box-shadow: none;
}

/* The section label above a panel's content. Not a heading competing with the
   page title -- a quiet label, tracked wide. */
.card-title {
  font-size: var(--suno-text-xs);
  font-weight: 500;
  letter-spacing: var(--suno-tracking-wide);
  text-transform: uppercase;
  color: var(--suno-text-muted);
  margin-bottom: var(--suno-space-4);
}

.card-header {
  background: transparent;
  border-bottom: 1px solid var(--suno-border);
  padding: var(--suno-space-4) var(--suno-space-6);
  font-weight: 600;
  letter-spacing: var(--suno-tracking-tight);
}

/* .card-lift is retained because admin markup still uses it, but the lift is
   gone. Translating panels on hover is motion without meaning. */
.card-lift:hover {
  border-color: var(--suno-border-control);
}

/* Stat readouts. The number is the point, so it gets the mono face and tabular
   figures -- columns of stats line up digit for digit, which is the whole reason
   to use a monospace here rather than a stylistic one. */
.stat-card .stat-value {
  font-family: var(--ewai-mono-font);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1, "zero" 1;
  font-size: var(--suno-text-2xl);
  font-weight: 500;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--suno-text);
}
.stat-card .stat-label {
  font-size: var(--suno-text-xs);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: var(--suno-tracking-wide);
  color: var(--suno-text-muted);
  margin-top: var(--suno-space-2);
}
.stat-card .stat-icon {
  font-size: var(--suno-text-xl);
  color: var(--suno-ui);
  opacity: 1;
}

/* ---------------------------------------------------------------------- TOASTS
   Toasts are the app's primary feedback channel -- sixteen window.alert() calls
   were moved into them -- so they cannot sit outside the design system.
   They were using Bootstrap's text-bg-* helpers, which in 5.3 bake the RGB literal
   into the rule (.text-bg-warning is `RGBA(255,193,7, ...)`) rather than reading
   --bs-warning-rgb. That is a real limit of configuring Bootstrap through its
   variables: it works for components built from component-scoped properties, and
   not at all for the colour utilities. So these are driven from tokens directly.

   Each severity keeps its own hue, and the label colour comes from --suno-on-sev so
   it inverts with the theme rather than assuming white. */
.suno-toast {
  border: 1px solid transparent;
  border-radius: var(--ewai-card-radius);
  box-shadow: var(--suno-shadow-overlay);
  color: var(--suno-on-sev);
}
.suno-toast .toast-body {
  font-size: var(--suno-text-sm);
  line-height: var(--suno-leading-snug);
}
.suno-toast .toast-body i {
  flex: 0 0 auto;
  font-size: 1rem;
  opacity: 0.9;
}
/* The close glyph is a black SVG by default. Match it to the label rather than
   forcing white, which vanished on the lighter severities. */
.suno-toast .btn-close {
  opacity: 0.7;
}
.suno-toast .btn-close:hover { opacity: 1; }

.suno-toast--emergency { background-color: var(--suno-sev-emergency); }
.suno-toast--warning   { background-color: var(--suno-sev-warning); }
.suno-toast--ok        { background-color: var(--suno-sev-ok); }
.suno-toast--info      { background-color: var(--suno-sev-info); }

/* In light mode the fills are dark and the label is white, so the close glyph has
   to be inverted to be seen. Dark mode flips both. */
.suno-toast .btn-close { filter: invert(1) grayscale(1) brightness(2); }
[data-theme="dark"] .suno-toast .btn-close { filter: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .suno-toast .btn-close { filter: none; }
}

/* --------------------------------------------------------------------- FOOTER
   One row on a hairline, not a four-column link farm. This is an eight-page
   operator tool and the nav already reaches every page, so the footer carries only
   what the nav cannot: attribution and support links. */
.suno-footer {
  margin-top: var(--suno-space-16);
  border-top: 1px solid var(--suno-border);
  background: var(--suno-surface);
}
.suno-footer__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--suno-space-5) var(--suno-space-8);
  padding-block: var(--suno-space-6);
}
.suno-footer__built {
  margin: 0;
  flex: 1 1 22ch;
  max-width: none;   /* overrides the global prose measure on <p> */
  font-size: var(--suno-text-sm);
  color: var(--suno-text-secondary);
}
/* wrap, because this row is now four marks: at 375px the un-wrapped flex line
   measured 393px and pushed the whole page into a sideways scroll. */
.suno-footer__marks {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--suno-space-4) var(--suno-space-6);
}
/* Optically sized, matching the sign-in screen. A single shared height makes GSMA
   tower over ConnectHear and shrinks Ufone to a smudge.

   The source artwork is WHITE on transparent -- these files were made for the dark
   video overlays, not for a page. On the light footer they have to be flipped to a
   dark silhouette with brightness(0), or they are literally invisible, which is how
   the first version of this shipped to my screenshot. Dark mode leaves them as-is. */
.suno-footer__marks img {
  width: auto;
  opacity: 0.6;
  filter: brightness(0);
  transition: opacity var(--suno-duration-base) var(--suno-ease);
}
.suno-footer__marks:hover img {
  opacity: 0.9;
}
.suno-footer__marks img[alt="ConnectHear"] { height: 17px; }
.suno-footer__marks img[alt="GSMA"]        { height: 14px; }
.suno-footer__marks img[alt="Ufone 4G"]    { height: 19px; }
/* Prefix match: this mark's alt carries its funder line, which is part of the
   artwork. Without a rule here it fell back to the file's natural 88px and
   towered over the other three. Taller than them on purpose -- it is a
   three-line lockup, not a wordmark, so equal height would make it unreadable. */
.suno-footer__marks img[alt^="Global Research"] { height: 26px; }

.suno-footer__links {
  display: flex;
  gap: var(--suno-space-5);
  margin-inline-start: auto;
  font-size: var(--suno-text-sm);
}
.suno-footer__links a {
  color: var(--suno-text-secondary);
  text-decoration: none;
}
.suno-footer__links a:hover {
  color: var(--suno-action);
  text-decoration: underline;
}

@media (max-width: 767.98px) {
  .suno-footer__links { margin-inline-start: 0; }
}

/* Dark mode: the artwork is already white, so the light-mode brightness(0) flip has
   to come back off. */
[data-theme="dark"] .suno-footer__marks img {
  filter: none;
  opacity: 0.55;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .suno-footer__marks img {
    filter: none;
    opacity: 0.55;
  }
}

/* ----------------------------------------------------------------- LEGAL PAGES
   The only long-form reading surface in the product, so it gets a genuine prose
   measure and looser leading rather than the app's UI density. */
.legal {
  max-width: 68ch;
  margin-inline: auto;
  padding: var(--suno-space-12) var(--suno-space-6) var(--suno-space-16);
}
.legal__eyebrow {
  margin: 0 0 var(--suno-space-2);
  font-size: var(--suno-text-xs);
  font-weight: 500;
  letter-spacing: var(--suno-tracking-wide);
  text-transform: uppercase;
  color: var(--suno-text-muted);
}
.legal h1 {
  font-size: var(--suno-text-3xl);
  margin-bottom: var(--suno-space-4);
}
.legal__summary {
  font-size: var(--suno-text-lg);
  line-height: var(--suno-leading-snug);
  color: var(--suno-text-secondary);
  max-width: none;
}
/* The review date is a date, so it is data: mono, like every other figure. */
.legal__meta {
  margin: var(--suno-space-5) 0 0;
  padding-top: var(--suno-space-5);
  border-top: 1px solid var(--suno-border);
  font-family: var(--ewai-mono-font);
  font-size: var(--suno-text-xs);
  color: var(--suno-text-muted);
}
.legal__section {
  margin-top: var(--suno-space-10);
}
.legal__section h2 {
  font-size: var(--suno-text-lg);
  margin-bottom: var(--suno-space-4);
}
.legal__section p {
  margin-bottom: var(--suno-space-4);
  max-width: none;   /* .legal already carries the measure */
  line-height: var(--suno-leading-body);
}
.legal__list {
  margin: 0 0 var(--suno-space-4);
  padding-inline-start: 0;
  list-style: none;
}
/* Hairline-led items rather than bullets, matching how the rest of the app groups
   things. */
.legal__list li {
  position: relative;
  padding-inline-start: var(--suno-space-5);
  margin-bottom: var(--suno-space-2);
  line-height: var(--suno-leading-snug);
}
.legal__list li::before {
  content: '';
  position: absolute;
  inset-inline-start: 0;
  top: 0.7em;
  width: 10px;
  height: 1px;
  background: var(--suno-ui);
}
.legal__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--suno-space-4);
  margin-top: var(--suno-space-12);
  padding-top: var(--suno-space-6);
  border-top: 1px solid var(--suno-border);
}
.legal__foot p {
  margin: 0;
  max-width: none;
  font-size: var(--suno-text-sm);
  color: var(--suno-text-secondary);
}

/* ---------------------------------------------------------------- ERROR PAGES
   Reached while signed out, so this carries its own chrome. Same deep teal field
   as the auth screens, which keeps a dead end recognisably part of the product
   rather than a bare server response. */
.error-page {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: var(--suno-space-4);
  padding: var(--suno-space-12) clamp(var(--suno-space-6), 8vw, 8rem);
  background: var(--suno-teal-900);
  color: #fff;
}
.error-page__mark {
  position: absolute;
  top: var(--suno-space-8);
  left: clamp(var(--suno-space-6), 8vw, 8rem);
}
/* The status number is data, so it takes the mono face -- and it is the one place
   a very large figure is the point rather than decoration. */
.error-page__code {
  margin: 0;
  font-family: var(--ewai-mono-font);
  font-size: clamp(2.5rem, 7vw, 4rem);
  font-weight: 500;
  line-height: 1;
  letter-spacing: -0.04em;
  color: var(--suno-teal-400);
}
.error-page__title {
  margin: 0;
  font-size: clamp(1.6rem, 3.4vw, 2.2rem);
  font-weight: 700;
  letter-spacing: var(--suno-tracking-tight);
  color: #fff;
}
.error-page__body {
  margin: 0;
  max-width: 52ch;
  font-size: var(--suno-text-base);
  line-height: var(--suno-leading-snug);
  color: rgba(255, 255, 255, 0.84);
}
.error-page__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--suno-space-3);
  margin-top: var(--suno-space-3);
}
/* Both buttons sit on a dark teal field, so neither can inherit the light-surface
   treatment. The primary uses the BRIGHT brand teal with dark text -- the one
   context where #33cccc is correct, because nothing light sits on top of it and it
   is the only thing that reads as an action against teal-900. teal-700 on teal-900
   was legible but barely separated from the background. */
.error-page .btn-primary {
  --bs-btn-bg: var(--suno-teal-500);
  --bs-btn-border-color: var(--suno-teal-500);
  --bs-btn-color: #0b2222;              /* 10.65:1 on #33cccc */
  --bs-btn-hover-bg: var(--suno-teal-400);
  --bs-btn-hover-border-color: var(--suno-teal-400);
  --bs-btn-hover-color: #0b2222;
}

.error-page .btn-outline-secondary {
  --bs-btn-color: rgba(255, 255, 255, 0.92);
  --bs-btn-border-color: rgba(255, 255, 255, 0.4);
  --bs-btn-hover-bg: rgba(255, 255, 255, 0.12);
  --bs-btn-hover-border-color: rgba(255, 255, 255, 0.7);
  --bs-btn-hover-color: #fff;
}
.error-page__note {
  margin: var(--suno-space-6) 0 0;
  max-width: 52ch;
  font-size: var(--suno-text-sm);
  color: rgba(255, 255, 255, 0.66);
}
.error-page__note a {
  color: var(--suno-teal-300);
}

/* --------------------------------------------------------------- EMPTY STATES
   A composed empty state rather than a line of grey text. Each one says what is
   missing, why, and what to do about it -- an empty screen is the moment a new
   operator most needs a next step. */
.ewai-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--suno-space-3);
  padding: var(--suno-space-12) var(--suno-space-6);
  color: var(--suno-text-secondary);
}
.ewai-empty__icon {
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: var(--suno-radius-full);
  background: var(--suno-bg);
  border: 1px solid var(--suno-border);
  color: var(--suno-ui);
  font-size: 1.25rem;
  margin-bottom: var(--suno-space-1);
}
.ewai-empty__title {
  font-size: var(--suno-text-base);
  font-weight: 600;
  letter-spacing: var(--suno-tracking-tight);
  color: var(--suno-text);
  margin: 0;
}
.ewai-empty__body {
  margin: 0;
  max-width: 42ch;
  font-size: var(--suno-text-sm);
}
.ewai-empty .btn {
  margin-top: var(--suno-space-2);
}

/* A readout strip: several figures divided by hairlines rather than boxed into
   separate coloured cards. Cheaper to scan, and it stops each metric shouting for
   equal attention. */
.ewai-readout {
  background: var(--suno-surface);
  border: 1px solid var(--suno-border);
  border-radius: var(--ewai-card-radius);
  overflow: hidden;
}
.ewai-readout .stat-card {
  padding: var(--suno-space-5) var(--suno-space-6);
  border-inline-start: 1px solid var(--suno-border);
}
/* No leading rule on the first item in each row. */
.ewai-readout .stat-card:first-child,
.ewai-readout .stat-card:nth-child(3) {
  border-inline-start: 0;
}
@media (min-width: 768px) {
  .ewai-readout .stat-card:nth-child(3) {
    border-inline-start: 1px solid var(--suno-border);
  }
}
@media (max-width: 767.98px) {
  .ewai-readout .stat-card:nth-child(n + 3) {
    border-top: 1px solid var(--suno-border);
  }
}

/* The one figure whose value is itself a signal. */
.stat-value--alert {
  color: var(--suno-sev-emergency);
}

/* Real elevation, for things that genuinely float. */
.modal-content,
.dropdown-menu,
.toast {
  border: 1px solid var(--suno-border);
  border-radius: var(--ewai-card-radius);
  box-shadow: var(--ewai-shadow-overlay);
}

/* ==========================================================================
   5. FORMS
   ========================================================================== */
/* Control boundaries use --suno-border-control, not the decorative hairline.
   WCAG 1.4.11 wants 3:1 for the edge of an input, because the user has to be able
   to see where the field is; a 1.3:1 divider does not qualify. */
.form-control,
.form-select {
  border-radius: var(--ewai-input-radius);
  border-color: var(--suno-border-control);
  background-color: var(--suno-surface);
  color: var(--suno-text);
  font-family: var(--ewai-font);
  font-size: var(--suno-text-sm);
  padding: var(--suno-space-2) var(--suno-space-3);
  transition: border-color var(--suno-duration-fast) var(--suno-ease),
              box-shadow var(--suno-duration-fast) var(--suno-ease);
}

.form-control:focus,
.form-select:focus {
  border-color: var(--suno-action);
  box-shadow: var(--suno-focus-ring);
}

.form-control::placeholder {
  color: var(--suno-text-muted);
  opacity: 1;   /* Firefox dims placeholders by default, which fails contrast. */
}

/* Anything the operator types that is an identifier or a number reads as data. */
input[type="tel"],
input[type="number"],
input[type="datetime-local"],
input[type="date"],
input[type="time"],
.form-control.ewai-num {
  font-family: var(--ewai-mono-font);
  font-variant-numeric: tabular-nums;
}

/* Sentence case, not uppercase. An earlier pass set every field label to tracked
   all-caps, which put eleven shouting labels on the generator screen at once and
   turned a hierarchy device into the default. Uppercase is now rationed to two
   levels -- the section label above a panel (.card-title) and table headers -- so
   it actually signals something. Field labels are the quiet layer. */
.form-label {
  font-weight: 500;
  font-size: var(--suno-text-sm);
  letter-spacing: 0;
  text-transform: none;
  color: var(--suno-text-secondary);
  margin-bottom: var(--suno-space-2);
}

.form-text {
  font-size: var(--suno-text-xs);
  color: var(--suno-text-muted);
}

/* Inline validation. The form used to hand failures to the browser's native
   reportValidity() bubble, which is styled by the OS, vanishes on the next click
   and is announced inconsistently -- and required checkbox groups only produced a
   toast, so the message lived nowhere near the field it was about. Errors now sit
   directly under the control, in the emergency hue, and are announced. */
.form-control.is-invalid,
.form-select.is-invalid {
  border-color: var(--suno-sev-emergency);
}
.form-control.is-invalid:focus,
.form-select.is-invalid:focus {
  border-color: var(--suno-sev-emergency);
  box-shadow: 0 0 0 3px rgba(179, 38, 30, 0.28);
}

.invalid-feedback,
.ewai-field-error {
  display: block;
  margin-top: var(--suno-space-2);
  font-size: var(--suno-text-xs);
  color: var(--suno-sev-emergency);
}

/* A required checkbox group marks its whole block, since no single input is at
   fault -- it is the absence of any selection. */
[data-required-group="true"].is-invalid-group .form-check-group {
  padding-inline-start: var(--suno-space-3);
  border-inline-start: 2px solid var(--suno-sev-emergency);
}

/* Checkbox & Radio */
.form-check-input {
  border-color: var(--suno-border-control);
}
.form-check-input:checked {
  background-color: var(--suno-action);
  border-color: var(--suno-action);
}
.form-check-input:focus {
  border-color: var(--suno-action);
  box-shadow: var(--suno-focus-ring);
}

/* ==========================================================================
   6. BUTTONS
   ========================================================================== */
/* Buttons are configured through --bs-btn-*, so no !important and no fighting
   Bootstrap's own .btn-primary. transition: all is replaced with a named list --
   "all" animates layout properties too, which is how hover jank starts. */
.btn {
  font-family: var(--ewai-font);
  font-weight: 500;
  font-size: var(--suno-text-sm);
  border-radius: var(--ewai-btn-radius);
  letter-spacing: 0.01em;
  padding: var(--suno-space-2) var(--suno-space-4);
  transition: background-color var(--suno-duration-fast) var(--suno-ease),
              border-color var(--suno-duration-fast) var(--suno-ease),
              color var(--suno-duration-fast) var(--suno-ease);
}

.btn:focus-visible {
  box-shadow: var(--suno-focus-ring);
}

/* Pressed feedback. There was none: the button changed colour on hover and then
   nothing acknowledged the click. A 1px push is enough to feel physical without
   moving the target, which matters more here than elsewhere -- the operator is
   clicking "dispatch this alert". Guarded so it collapses under reduced motion. */
@media (prefers-reduced-motion: no-preference) {
  .btn:active:not(:disabled),
  .btn.active:not(:disabled) {
    transform: translateY(1px);
  }
}

/* The old hover lifted the button 1px and dropped a coloured glow under it. A
   colour change is enough, and it does not move the target the operator is
   reaching for. */
.btn-primary {
  --bs-btn-bg: var(--suno-action);
  --bs-btn-border-color: var(--suno-action);
  --bs-btn-color: var(--suno-on-action);
  --bs-btn-hover-bg: var(--suno-action-hover);
  --bs-btn-hover-border-color: var(--suno-action-hover);
  --bs-btn-hover-color: var(--suno-on-action);
  --bs-btn-active-bg: var(--suno-teal-900);
  --bs-btn-active-border-color: var(--suno-teal-900);
  /* Neutral when disabled, not a paler teal. A washed-out brand colour reads as
     "this is the button, just faint", which invites clicking; grey reads as
     unavailable. It also fixes a real measurement -- white on teal-600 is 3.36:1,
     and history.js renders disabled primary buttons for rows with no public URL. */
  --bs-btn-disabled-bg: #e6e9ec;
  --bs-btn-disabled-border-color: #e6e9ec;
  /* Solved for AA against the disabled fill (4.59:1) rather than reusing
     --suno-text-muted, which landed at 4.14. Disabled controls are exempt from
     1.4.3, but "exempt" is not a reason to ship something harder to read. */
  --bs-btn-disabled-color: #5f6972;
}

.btn-outline-primary {
  --bs-btn-color: var(--suno-action);
  --bs-btn-border-color: var(--suno-border-control);
  --bs-btn-hover-bg: var(--suno-action);
  --bs-btn-hover-border-color: var(--suno-action);
  --bs-btn-hover-color: var(--suno-on-action);
  --bs-btn-active-bg: var(--suno-action-hover);
  --bs-btn-active-border-color: var(--suno-action-hover);
  /* The template-category toggle is a .btn-check + .btn-outline-primary, so its
     selected state is the ACTIVE state. Bootstrap defaults that label to #fff,
     which is unreadable once the fill inverts to light teal in dark mode. */
  --bs-btn-active-color: var(--suno-on-action);
}

/* Destructive actions carry the emergency hue, so dispatch and delete never look
   like an ordinary confirm. */
.btn-danger {
  --bs-btn-bg: var(--suno-sev-emergency);
  --bs-btn-border-color: var(--suno-sev-emergency);
  --bs-btn-color: var(--suno-on-sev);
  --bs-btn-hover-bg: #8f1e17;
  --bs-btn-hover-border-color: #8f1e17;
  --bs-btn-hover-color: var(--suno-on-sev);
}

.btn-success {
  --bs-btn-bg: var(--suno-sev-ok);
  --bs-btn-border-color: var(--suno-sev-ok);
  --bs-btn-color: var(--suno-on-sev);
  --bs-btn-hover-bg: #155530;
  --bs-btn-hover-border-color: #155530;
  --bs-btn-hover-color: var(--suno-on-sev);
}

/* Secondary actions are text with a hairline, not a second filled button
   competing with the primary one. */
.btn-outline-secondary {
  --bs-btn-color: var(--suno-text-secondary);
  --bs-btn-border-color: var(--suno-border-control);
  --bs-btn-hover-bg: var(--suno-bg);
  --bs-btn-hover-border-color: var(--suno-text-secondary);
  --bs-btn-hover-color: var(--suno-text);
}

.btn-link {
  --bs-btn-color: var(--suno-action);
  --bs-btn-hover-color: var(--suno-action-hover);
  text-decoration: none;
  font-weight: 500;
}
.btn-link:hover { text-decoration: underline; }

/* ==========================================================================
   7. BADGES
   ========================================================================== */
/* Badges are status, not ornament: squared off, quiet, and never pill-shaped.
   A pill reads as a tag you can dismiss; a rectangle reads as a state. */
.badge {
  font-family: var(--ewai-font);
  font-weight: 500;
  font-size: var(--suno-text-xs);
  letter-spacing: var(--suno-tracking-wide);
  text-transform: uppercase;
  border-radius: var(--suno-radius-sm);
  padding: 0.25em 0.55em;
  border: 1px solid transparent;
}

/* Severity badges keep Bootstrap's solid .bg-* fill and get white text.
 *
 * An earlier attempt made these outline-style by setting background-color:
 * transparent, which silently failed: Bootstrap's .bg-* utilities are declared
 * !important, so the fill survived while the text went near-black, leaving
 * unreadable coloured bars on the sign-in page. Fighting a utility that is
 * !important by design is the wrong move, so the fill stays and only the
 * foreground is set.
 *
 * White clears AA on every severity fill in the palette: emergency 6.54,
 * warning 6.33, ok 6.54, info 6.46, action 5.09.
 */
/* --suno-on-sev, not #fff. The severity fills invert to LIGHT hues in dark mode
   (emergency #ff9d95, warning #e8b53d), so white text on them measures under 2:1.
   The token is white in light mode and near-black in dark, which is the entire
   reason it exists. */
.badge.bg-danger,
.badge.bg-success,
.badge.bg-warning,
.badge.bg-info,
.badge.bg-secondary {
  color: var(--suno-on-sev);
}

.badge.bg-primary {
  color: var(--suno-on-action);
}

/* Quiet variant for dense surfaces, where a row of solid fills would be louder
   than the data. Opt in with .ewai-badge-quiet. */
.badge.ewai-badge-quiet {
  background-color: transparent !important;  /* beats the !important .bg-* utility */
}
.badge.ewai-badge-quiet.bg-danger  { border-color: var(--suno-sev-emergency); color: var(--suno-sev-emergency); }
.badge.ewai-badge-quiet.bg-warning { border-color: var(--suno-sev-warning);   color: var(--suno-sev-warning); }
.badge.ewai-badge-quiet.bg-success { border-color: var(--suno-sev-ok);        color: var(--suno-sev-ok); }
.badge.ewai-badge-quiet.bg-info    { border-color: var(--suno-sev-info);      color: var(--suno-sev-info); }
.badge.ewai-badge-quiet.bg-primary { border-color: var(--suno-action);        color: var(--suno-action); }

/* ==========================================================================
   8. TABLES
   ========================================================================== */
/* Tables are the densest surface in the app and the one that most rewards the
   instrument treatment: quiet hairlines, no zebra striping, and mono figures so
   columns of numbers, ids and timestamps align digit for digit. */
.table {
  --bs-table-border-color: var(--suno-border);
  --bs-table-color: var(--suno-text);
  --bs-table-bg: transparent;
  --bs-table-hover-bg: rgba(31, 122, 122, 0.05);
  --bs-table-hover-color: var(--suno-text);
  font-size: var(--suno-text-sm);
}

.table thead th {
  font-weight: 500;
  text-transform: uppercase;
  font-size: var(--suno-text-xs);
  letter-spacing: var(--suno-tracking-wide);
  color: var(--suno-text-muted);
  border-bottom: 1px solid var(--suno-border-control);
  white-space: nowrap;
  padding-block: var(--suno-space-3);
}

.table tbody td {
  padding-block: var(--suno-space-3);
  vertical-align: middle;
  border-bottom-color: var(--suno-border);
}

/* Tabular figures across the whole table body: even mixed columns benefit,
   because digits stop shifting width between rows. */
.table tbody td {
  font-variant-numeric: tabular-nums;
}

/* Columns that are purely data get the mono face outright. */
.table td.ewai-num,
.table td.ewai-id,
.table td code {
  font-family: var(--ewai-mono-font);
  font-size: var(--suno-text-xs);
  color: var(--suno-text-secondary);
}

/* Accepted values wrap as inline chips. They used to be <br>-joined into a tall
   column, which stretched each table row to the height of its longest value list
   and left the other three cells sitting beside 200px of blank space. */
.param-values {
  display: flex;
  flex-wrap: wrap;
  gap: var(--suno-space-1) var(--suno-space-2);
}
/* Each value gets a chip. Without a boundary, adjacent values ran together into
   one unreadable line: "Air Pollution Coldwave Cyclone Duststorm Earthquake..." */
.param-value {
  display: inline-flex;
  align-items: baseline;
  gap: 0.3rem;
  white-space: nowrap;
  padding: 0.15rem 0.4rem;
  background: var(--suno-bg);
  border: 1px solid var(--suno-border);
  border-radius: var(--suno-radius-sm);
}
.param-value code { color: var(--suno-text); }
.param-value .text-muted { font-size: var(--suno-text-xs); }

/* Below md the reference table restacks into labelled blocks. Four columns have
   nowhere to go at 375px: .table-responsive does scroll, but the content just read
   as chopped mid-word, and the tallest cell dictated the row height. Each row
   becomes one parameter, each cell captioned by its header. */
@media (max-width: 767.98px) {
  .table-responsive .table,
  .table-responsive .table tbody,
  .table-responsive .table tr,
  .table-responsive .table td {
    display: block;
    width: auto;
  }
  .table-responsive .table thead { display: none; }

  .table-responsive .table tr {
    padding: var(--suno-space-4) 0;
    border-bottom: 1px solid var(--suno-border);
  }
  .table-responsive .table td {
    border: 0;
    padding: 0 0 var(--suno-space-2);
  }
  /* The caption comes from data-label, set by the row builder. */
  .table-responsive .table td[data-label]::before {
    content: attr(data-label);
    display: block;
    margin-bottom: var(--suno-space-1);
    font-size: var(--suno-text-xs);
    font-weight: 500;
    letter-spacing: var(--suno-tracking-wide);
    text-transform: uppercase;
    color: var(--suno-text-muted);
  }
  /* Parameter name leads the block, so it does not need its own caption. */
  .table-responsive .table td[data-label="Parameter"]::before { display: none; }
  .table-responsive .table td[data-label="Parameter"] {
    font-size: var(--suno-text-base);
  }
  /* Required and Type are short: sit them side by side rather than stacked. */
  .table-responsive .table td[data-label="Required"],
  .table-responsive .table td[data-label="Type"] {
    display: inline-block;
    margin-right: var(--suno-space-6);
  }
}

/* ==========================================================================
   9. CODE BLOCKS (Developer Console)
   ========================================================================== */
.code-block {
  background-color: var(--ewai-code-bg);
  color: var(--ewai-code-fg);
  padding: 1.25rem 1rem;
  border-radius: var(--ewai-card-radius);
  font-family: var(--ewai-mono-font);
  font-size: 0.82rem;
  line-height: 1.6;
  overflow-x: auto;
  position: relative;
  border: 1px solid rgba(255,255,255,.06);
}

/* Reserve the copy button's lane. The button is absolutely positioned top-right,
   and on a narrow viewport the first line of the snippet ran straight underneath
   it -- the curl URL was sitting behind "Copy". */
.code-block pre {
  margin: 0;
  white-space: pre-wrap;
  word-break: break-word;   /* break-all split identifiers mid-token */
  padding-right: 4.5rem;
}

.copy-btn {
  position: absolute;
  top: 0.6rem;
  right: 0.6rem;
  font-size: 0.72rem;
  padding: 0.2rem 0.55rem;
  border-radius: 0.35rem;
  background: rgba(255,255,255,.12);
  border: 1px solid rgba(255,255,255,.18);
  color: #ccc;
  cursor: pointer;
  transition: background 0.15s;
  font-family: var(--ewai-font);
  font-weight: 600;
}
.copy-btn:hover {
  background: rgba(255,255,255,.22);
  color: #fff;
}
.copy-btn.copied {
  background: var(--ewai-success);
  color: #fff;
  border-color: var(--ewai-success);
}

/* ==========================================================================
   10. PAGE HEADERS
   ========================================================================== */
.page-header {
  padding: 1.25rem 0 1rem;
  border-bottom: 1px solid var(--ewai-border);
  margin-bottom: 1.5rem;
}

.page-header h1 {
  font-size: 1.6rem;
  margin-bottom: 0.2rem;
}

.page-header .page-subtitle {
  color: var(--ewai-text-muted);
  font-size: 0.9rem;
  margin: 0;
}

/* ==========================================================================
   11. ALERTS
   ========================================================================== */
.alert {
  border-radius: var(--ewai-card-radius);
  border-left-width: 4px;
  font-size: 0.9rem;
}

/* ==========================================================================
   12. AUTH PAGES (Login / Signup)
   ========================================================================== */
/* Recomposed. The old version centred a stack of text in a flat gradient panel,
 * which left a large empty band above and below the copy and ended short of the
 * viewport. Now the left panel is full-bleed product output: the SUNO avatar
 * presenter signing a real flood warning, assembled from the same clip library
 * the generator uses. Avatar Mode is the default presenter, so the avatar is the
 * face of the product; the copy is anchored to the bottom over a scrim. For a
 * sign-language product the presenter IS the subject, so this is the one place a
 * video belongs on an auth screen.
 *
 * 100dvh, not 100vh: on iOS Safari the address bar makes vh taller than the
 * visible area, which is what produced the pale strip under the layout. */
/* The 2rem of bottom padding that scrolling product pages get on <main> would push
   a 100dvh layout past the viewport and show a pale strip beneath it, which reads
   as a rendering bug. The auth screens and the error page are full-height
   compositions, so they opt out. */
body.auth-page > main {
  padding-bottom: 0;
}

.auth-layout {
  min-height: 100dvh;
  display: grid;
  grid-template-columns: 7fr 5fr;
}

.auth-branding {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--suno-teal-900);
  color: #fff;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: var(--suno-space-10) var(--suno-space-12);
}

.auth-branding__video {
  position: absolute;
  inset: 0;
  z-index: -2;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Barely graded. The avatar is the brand asset and its palette is warm (maroon,
     tan) on a light backdrop, so desaturating it or pushing a teal cast over it
     just makes it muddy. Left close to source; the scrim below does the contrast
     work, and only in the band where text actually sits. */
  filter: contrast(1.03) saturate(1.02);
}

/* Scrim, weighted hard to the bottom. The avatar's backdrop is light, so white
   copy needs a genuinely dark band under it -- but the top two-thirds stay clear
   so the signing reads. Only a trace of teal, enough to tie the panel to the
   brand without tinting the character. */
.auth-branding::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(180deg,
      rgba(6, 22, 24, 0.00) 0%,
      rgba(6, 22, 24, 0.06) 34%,
      rgba(6, 22, 24, 0.58) 58%,
      rgba(6, 22, 24, 0.93) 78%,
      rgba(5, 18, 20, 0.98) 100%),
    linear-gradient(180deg, rgba(18, 73, 73, 0.20) 0%, rgba(18, 73, 73, 0.10) 100%);
}

.auth-branding__mark img {
  height: 34px;
  width: auto;
}

/* Wide enough that the headline sets in two lines. At 34ch it broke to three,
   which the type scale is not built for. */
.auth-branding__copy {
  max-width: 54ch;
}

.auth-branding h1 {
  font-size: clamp(1.9rem, 3.1vw, 2.6rem);
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.08;
  margin-bottom: var(--suno-space-4);
  text-wrap: balance;
}

.auth-branding .lead {
  font-size: var(--suno-text-base);
  line-height: var(--suno-leading-snug);
  color: rgba(255, 255, 255, 0.86);
  margin-bottom: 0;
  max-width: 42ch;
}

/* Partner attribution. Real marks, no category labels under them. */
.auth-branding__partners {
  display: flex;
  align-items: center;
  gap: var(--suno-space-6);
  margin-top: var(--suno-space-8);
  padding-top: var(--suno-space-5);
  border-top: 1px solid rgba(255, 255, 255, 0.16);
}
/* Optically sized, not uniformly sized. These three marks have very different
   aspect ratios and cap heights, so setting one height made GSMA tower over
   ConnectHear and shrank Ufone to an illegible smudge. Each is set to the height
   at which it reads as the same visual weight. */
.auth-branding__partners img {
  width: auto;
  opacity: 0.85;
  /* Dark-on-transparent PNGs; flatten to white so they sit on the field. */
  filter: brightness(0) invert(1);
}
.auth-branding__partners img[alt="ConnectHear"] { height: 18px; }
.auth-branding__partners img[alt="GSMA"]        { height: 15px; }
.auth-branding__partners img[alt="Ufone 4G"]    { height: 20px; }

.auth-form-panel {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--suno-space-8);
  background: var(--suno-surface);
}

.auth-form-inner {
  width: 100%;
  max-width: 380px;
}

.auth-form-inner h2 {
  font-size: var(--suno-text-xl);
  margin-bottom: var(--suno-space-1);
}

/* Single column below md. The video becomes a short banner rather than a
   half-screen: on a phone the operator wants the sign-in button, not a film. */
@media (max-width: 767.98px) {
  .auth-layout {
    grid-template-columns: 1fr;
  }
  .auth-branding {
    min-height: 42vh;
    padding: var(--suno-space-6);
  }
  .auth-branding h1 { font-size: 1.6rem; }
  .auth-branding .lead { font-size: var(--suno-text-sm); }
  .auth-branding__partners { margin-top: var(--suno-space-5); }
}

/* No autoplay under reduced motion: the poster frame carries the same meaning. */
@media (prefers-reduced-motion: reduce) {
  .auth-branding__video { display: none; }
  .auth-branding {
    background:
      var(--suno-teal-900) url('/static/samples/psl-alert-poster.jpg') center / cover no-repeat;
  }
}

.auth-divider {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  color: var(--ewai-text-muted);
  font-size: 0.82rem;
  margin: 1rem 0;
}
.auth-divider::before,
.auth-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--ewai-border);
}

.auth-divider {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  color: var(--ewai-text-muted);
  font-size: 0.82rem;
  margin: 1rem 0;
}
.auth-divider::before,
.auth-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--ewai-border);
}

/* ==========================================================================
   13. VIDEO WORKSPACE (Generator)
   ========================================================================== */
.preview-sticky {
  position: sticky;
  top: calc(var(--ewai-navbar-height) + var(--suno-space-6));
}

/* --------------------------------------------------------------------------
   Compose column: two-column field grid
   The renderer builds one `.mb-3` wrapper per field, so eight of them stacked
   vertically produced a monotonous column of identical selects roughly two
   viewports tall. Pairing them halves the height and gives the eye a rhythm,
   without touching the renderer.
   -------------------------------------------------------------------------- */
#dynamicFormContainer {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--suno-space-5) var(--suno-space-5);
  align-items: start;
}

/* A field carrying a checkbox group (Recommended Actions) holds seven options and
   needs the full width; :has() keeps this in CSS rather than in the renderer. */
#dynamicFormContainer > div:has(.form-check-group),
#dynamicFormContainer > div[data-required-group] {
  grid-column: 1 / -1;
}

/* The skeleton is the container's only child while fields fetch. */
#dynamicFormContainer > .ewai-skeleton-fields,
#dynamicFormContainer > .text-center {
  grid-column: 1 / -1;
}

/* Skeleton matching the field grid it stands in for, so the panel keeps its height
   and the page does not jump when the real fields land. */
.ewai-skeleton-fields {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--suno-space-5);
}
.ewai-skel-field::before,
.ewai-skel-field::after {
  content: '';
  display: block;
  border-radius: var(--suno-radius-sm);
  background: linear-gradient(90deg,
    var(--suno-skeleton-base) 0%,
    var(--suno-skeleton-sheen) 50%,
    var(--suno-skeleton-base) 100%);
  background-size: 200% 100%;
  animation: ewai-shimmer 1.4s ease-in-out infinite;
}
.ewai-skel-field::before {   /* the label */
  width: 42%;
  height: 0.75rem;
  margin-bottom: var(--suno-space-2);
}
.ewai-skel-field::after {    /* the control */
  height: 2.45rem;
}

@keyframes ewai-shimmer {
  from { background-position: 200% 0; }
  to   { background-position: -200% 0; }
}

@media (max-width: 575.98px) {
  .ewai-skeleton-fields { grid-template-columns: minmax(0, 1fr); }
}

#dynamicFormContainer > div { margin-bottom: 0 !important; }

/* Checkbox groups (Recommended Actions, seven options). Moved here from a
   page-level <style> in index.html, where it was a single flex column -- so the
   group spanned the full width but used only the left half of it. Columns fill the
   space and cut the group's height by two thirds. `columns` rather than a grid so
   items flow naturally and a shorter list simply uses fewer columns. */
.form-check-group {
  columns: 2;
  column-gap: var(--suno-space-6);
}
.form-check-group .form-check {
  break-inside: avoid;
  margin-bottom: var(--suno-space-2);
}

@media (max-width: 575.98px) {
  .form-check-group { columns: 1; }
}

@media (max-width: 575.98px) {
  #dynamicFormContainer { grid-template-columns: minmax(0, 1fr); }
}

/* --------------------------------------------------------------------------
   Stage column: preview + the action that fills it + what you do next
   -------------------------------------------------------------------------- */
/* Padding matches .card's --bs-card-spacer so the stage's section label sits on
   the same baseline as the label in the panel beside it. They were 6px out, which
   is exactly the kind of misalignment that reads as sloppy without being
   obviously wrong. */
.workspace-stage {
  background: var(--suno-surface);
  border: 1px solid var(--suno-border);
  border-radius: var(--ewai-card-radius);
  padding: var(--suno-space-6);
  display: flex;
  flex-direction: column;
  gap: var(--suno-space-5);
}
.workspace-stage > .card-title {
  margin-bottom: 0;   /* the flex gap owns the spacing here */
}

/* Empty state. A dark bed, so the panel reads as a screen waiting for output
   rather than as a missing image. */
.workspace-stage__empty {
  aspect-ratio: 16 / 9;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--suno-space-2);
  background: var(--suno-media-bg);
  border: 1px solid var(--suno-border);
  border-radius: var(--ewai-card-radius);
  color: rgba(255, 255, 255, 0.62);
  font-size: var(--suno-text-sm);
}
.workspace-stage__empty i {
  font-size: 1.6rem;
  color: rgba(255, 255, 255, 0.35);
}

.workspace-stage__go {
  margin-bottom: 0;
}

.workspace-stage__done {
  padding-top: var(--suno-space-5);
  border-top: 1px solid var(--suno-border);
}

/* The stage stacks under the form on narrow viewports, where sticky positioning
   would pin it over the fields being filled in. */
@media (max-width: 991.98px) {
  .preview-sticky { position: static; }
}

/* The rendered alert is the product. It gets a hairline and a near-black bed, not
   a shadow -- the video's own frame is the edge. */
.video-container video {
  width: 100%;
  display: block;
  border-radius: var(--ewai-card-radius);
  border: 1px solid var(--suno-border);
  background: var(--suno-media-bg);
}

.video-placeholder {
  aspect-ratio: 16/9;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--suno-bg);
  border-radius: var(--ewai-card-radius);
  border: 2px dashed var(--ewai-border);
  color: var(--ewai-text-muted);
  font-size: 0.9rem;
  flex-direction: column;
  gap: 0.5rem;
}

/* Template toggle buttons */
/* The .template-toggle-group rules that were here are gone. The markup only ever
   carried `btn-group w-100` with an id -- the class appeared in no template -- so
   three !important border-radius declarations were styling nothing. Bootstrap's own
   btn-group radii already resolve from --bs-border-radius, which the bridge sets to
   the 4px scale, so the segmented control looks the same without them.
   Deleting dead CSS took real !important declarations from 6 down to 3. */

/* Generating spinner overlay */
.generate-spinner {
  display: none;
  align-items: center;
  gap: 0.5rem;
  color: var(--ewai-primary);
  font-weight: 600;
}
.generate-spinner.active {
  display: flex;
}

/* ==========================================================================
   14. HISTORY PAGE
   ========================================================================== */
.history-thumbnail {
  width: 80px;
  height: 52px;
  object-fit: cover;
  border-radius: 0.4rem;
  background: var(--suno-media-bg);
}

.history-row-actions {
  display: flex;
  gap: 0.4rem;
  flex-wrap: nowrap;
}

/* ==========================================================================
   15. BROADCAST PAGE
   ========================================================================== */
/* Step markers. The three stages used to be "1. Select Video Asset" and so on --
   the number baked into the heading string. A marker makes the sequence a real
   component instead of punctuation, and lets the label be a plain verb phrase. */
.ewai-step {
  display: flex;
  align-items: center;
  gap: var(--suno-space-3);
  margin-bottom: var(--suno-space-5);
}
.ewai-step__n {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: var(--suno-radius-full);
  background: var(--suno-action);
  color: var(--suno-on-action);
  font-family: var(--ewai-mono-font);
  font-size: var(--suno-text-xs);
  font-weight: 500;
  /* Optical nudge: the digit's own bearing sits it slightly high in the circle. */
  padding-top: 1px;
}
.ewai-step__label {
  font-size: var(--suno-text-base);
  font-weight: 600;
  letter-spacing: var(--suno-tracking-tight);
  color: var(--suno-text);
}

/* The dispatch action. This one sends WhatsApp messages to real recipients and
   cannot be recalled, so it is bounded, tinted with the emergency hue, and states
   its consequence next to itself instead of looking like every other button. */
.dispatch-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--suno-space-5);
  flex-wrap: wrap;
  padding: var(--suno-space-5);
  border: 1px solid var(--suno-sev-emergency);
  border-radius: var(--ewai-card-radius);
  background: var(--suno-danger-surface);
}
.dispatch-bar__note {
  margin: 0;
  max-width: 46ch;
  font-size: var(--suno-text-sm);
  line-height: var(--suno-leading-snug);
  color: var(--suno-sev-emergency);
}
.dispatch-bar .btn {
  flex: 0 0 auto;
}
@media (max-width: 575.98px) {
  .dispatch-bar { flex-direction: column; align-items: stretch; }
  .dispatch-bar .btn { width: 100%; }
}

/* Recipients CSV drop zone. Moved out of broadcast.html's page-level <style>,
   where it hardcoded the retired navy #0f2b46 and a grey fill. */
.drag-drop-area {
  border: 1px dashed var(--suno-border-control);
  border-radius: var(--ewai-card-radius);
  padding: var(--suno-space-8);
  text-align: center;
  background-color: var(--suno-bg);
  color: var(--suno-text-secondary);
  cursor: pointer;
  transition: border-color var(--suno-duration-fast) var(--suno-ease),
              background-color var(--suno-duration-fast) var(--suno-ease);
}
.drag-drop-area:hover {
  border-color: var(--suno-action);
  background-color: var(--suno-surface);
}

/* Drag-active. Distinct from hover, and firmer: the operator is holding a file over
   the target and needs to see it is armed. Applied by the drop handlers in
   broadcast.html -- previously this class existed in CSS and nothing ever set it. */
.drag-drop-area.dragover {
  border-style: solid;
  border-color: var(--suno-action);
  background-color: var(--suno-surface);
  box-shadow: var(--suno-focus-ring);
}
.drag-drop-area.dragover i {
  transform: translateY(-2px);
}

.drag-drop-area i {
  color: var(--suno-ui);
  transition: transform var(--suno-duration-base) var(--suno-ease);
}

.ewai-dropzone-hint {
  margin-top: var(--suno-space-1);
  font-size: var(--suno-text-xs);
  color: var(--suno-text-muted);
}

/* Parse succeeded. Uses the "ok" severity rather than Bootstrap's .text-success,
   whose utility bakes an RGB literal and so ignores the token in dark mode. */
.ewai-csv-ok {
  color: var(--suno-sev-ok);
  font-weight: 500;
}

.broadcast-video-card {
  cursor: pointer;
  border: 2px solid transparent;
  overflow: hidden;
  /* Was a hardcoded 0.15s that also transitioned transform without ever setting
     one. Tokens here, and no transform: see the selection rule below. */
  transition: border-color var(--suno-duration-fast) var(--suno-ease-out);
}
.broadcast-video-card:hover {
  border-color: var(--suno-border-control);
}
/* Selection is a ring in the action colour, not a lift. The operator is choosing
   which alert goes out; the state has to be unmistakable and stay still. This is
   why .suno-tactile is deliberately not applied to these cards. */
.broadcast-video-card.selected {
  border-color: var(--suno-action);
  box-shadow: var(--suno-focus-ring);
}
.broadcast-video-card:focus-visible {
  outline: none;
  border-color: var(--suno-action);
  box-shadow: var(--suno-focus-ring);
}
.broadcast-video-card video {
  width: 100%;
  aspect-ratio: 16/9;
  object-fit: cover;
  border-radius: calc(var(--ewai-card-radius) - 2px);
  background: var(--suno-media-bg);
}

@media (prefers-reduced-motion: reduce) {
  .broadcast-video-card { transition: none; }
}

/* The poster frame. Before this the grid was text-only, so scanning it meant
   reading a column of near-identical uuid filenames. */
.broadcast-video-card__poster {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: var(--suno-media-bg);
  overflow: hidden;
}
.broadcast-video-card__poster img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* Legacy objects predate poster extraction and simply have none, so the plate is
   a normal state rather than an error. */
.broadcast-video-card__poster--empty {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--suno-text-muted);
  font-size: var(--suno-text-2xl);
}

.broadcast-video-card__title {
  font-size: var(--suno-text-base);
  font-weight: 600;
  line-height: var(--suno-leading-snug);
  color: var(--suno-text);
  margin: 0 0 var(--suno-space-1);
  /* Two lines, then ellipsis: titles are "Disaster - Area" and wrap at narrow
     widths, but a card that grows to fit one long title breaks the grid row. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Date and size. Mono with tabular figures so the column aligns down the grid. */
.broadcast-video-card__meta {
  font-family: var(--suno-font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--suno-text-xs);
  color: var(--suno-text-muted);
  margin: 0;
}

.broadcast-video-card__note {
  font-size: var(--suno-text-xs);
  color: var(--suno-text-muted);
  margin: 0;
}

/* Signing failed for this object, so there is nothing to preview. Kept visible
   rather than filtered out: the asset exists, and a silently shorter list is
   more confusing than one that says why. */
.broadcast-video-card--unavailable {
  cursor: default;
  opacity: 0.62;
}
.broadcast-video-card--unavailable:hover {
  border-color: transparent;
}

.broadcast-facet__label {
  display: block;
  font-size: var(--suno-text-xs);
  color: var(--suno-text-muted);
  margin-bottom: var(--suno-space-1);
}

.broadcast-preview {
  aspect-ratio: 16 / 9;
  background: var(--suno-media-bg);
}

.broadcast-detail__title {
  font-size: var(--suno-text-base);
  font-weight: 600;
  color: var(--suno-text);
  margin: 0 0 var(--suno-space-1);
}
.broadcast-detail__table {
  font-size: var(--suno-text-xs);
  margin-top: var(--suno-space-3);
}
.broadcast-detail__key {
  font-weight: 500;
  color: var(--suno-text-muted);
  width: 38%;
  white-space: nowrap;
}
.broadcast-detail__filename {
  font-size: var(--suno-text-xs);
  color: var(--suno-text-muted);
  word-break: break-all;
  margin: var(--suno-space-2) 0 0;
}

/* ==========================================================================
   16. DEVELOPER CONSOLE
   ========================================================================== */
.key-display {
  background: var(--suno-warning-surface);
  border: 1px solid var(--suno-warning-edge);
  border-radius: var(--ewai-card-radius);
  padding: 1rem;
  font-family: var(--ewai-mono-font);
  font-size: 0.85rem;
  word-break: break-all;
  line-height: 1.5;
}

.snippet-tabs .nav-link {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ewai-text-muted);
  padding: 0.4rem 0.9rem;
  border-radius: var(--ewai-btn-radius) var(--ewai-btn-radius) 0 0;
}
.snippet-tabs .nav-link.active {
  color: var(--ewai-primary);
  background: var(--ewai-surface);
}

/* ==========================================================================
   17. ONBOARDING WIZARD
   ========================================================================== */
/* Sign vocabulary chips. These are identifiers (action_HigherGround), so mono.
   The fill was a fixed #e9ecef, which in dark mode put near-white text on a light
   grey chip at 1.01:1 -- the single largest cluster in the dark audit. */
.vocab-badge {
  font-family: var(--ewai-mono-font);
  font-size: var(--suno-text-xs);
  margin: 0.2rem;
  background-color: var(--suno-bg);
  border: 1px solid var(--suno-border);
  color: var(--suno-text-secondary);
  border-radius: var(--suno-radius-sm);
  padding: 0.35rem 0.6rem;
  display: inline-block;
  font-weight: 400;
}

.vocab-badge.missing {
  background-color: var(--suno-warning-surface);
  color: var(--suno-sev-warning);
  border: 1px solid var(--suno-warning-edge);
}

/* Scrolling chip list. A hard overflow cut sliced the last visible row through the
   middle of its glyphs, which looks like a rendering fault rather than an
   indication that there is more below. The mask fades the final rows out, so the
   boundary reads as intentional, and the extra bottom padding keeps a full row
   clear of the fade. */
.vocab-scroll {
  max-height: 30rem;
  overflow-y: auto;
  padding: var(--suno-space-4);
  padding-bottom: var(--suno-space-8);
  background: var(--suno-bg);
  border: 1px solid var(--suno-border);
  border-radius: var(--ewai-card-radius);
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 2.5rem), transparent 100%);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 2.5rem), transparent 100%);
}
/* Once scrolled to the end there is nothing more to hint at, so drop the fade. */
.vocab-scroll:has(> :last-child:only-child) {
  -webkit-mask-image: none;
  mask-image: none;
}

.vocab-badge.covered {
  background-color: transparent;
  border-color: var(--suno-sev-ok);
  color: var(--suno-sev-ok);
}

/* ==========================================================================
   18. ADMIN DASHBOARD
   ========================================================================== */
.tenant-card {
  cursor: pointer;
  transition: border-color var(--suno-duration-fast) var(--suno-ease);
}
.tenant-card:hover {
  border-color: var(--suno-action);
}

/* Bootstrap's nav-tabs default their active tab to a white background and a
   white bottom border, both fixed. In dark mode that put near-white label text on
   a near-white tab (1.18:1). Driven from --bs-nav-tabs-* so it follows the theme. */
.admin-tabs {
  --bs-nav-tabs-border-color: var(--suno-border);
  --bs-nav-tabs-link-active-bg: var(--suno-surface);
  --bs-nav-tabs-link-active-color: var(--suno-action);
  --bs-nav-tabs-link-active-border-color: var(--suno-border) var(--suno-border) var(--suno-surface);
  --bs-nav-tabs-link-hover-border-color: var(--suno-border);
}
.admin-tabs .nav-link {
  font-weight: 500;
  color: var(--ewai-text-muted);
  border-radius: var(--ewai-btn-radius) var(--ewai-btn-radius) 0 0;
}
.admin-tabs .nav-link.active {
  color: var(--suno-action);
}

.sequence-item {
  cursor: move;
  background: var(--suno-bg);
  border: 1px solid var(--ewai-border);
  margin-bottom: 0.4rem;
  padding: 0.6rem 0.85rem;
  border-radius: 0.4rem;
  font-size: 0.875rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  transition: background 0.1s;
}
.sequence-item:hover {
  background: #e9ecef;
}
.sequence-item .drag-handle {
  color: var(--ewai-text-muted);
  cursor: grab;
}

/* ==========================================================================
   19. UTILITY CLASSES
   ========================================================================== */
.text-mono {
  font-family: var(--ewai-mono-font);
}

.fw-semibold {
  font-weight: 600 !important;
}

.border-radius-lg {
  border-radius: var(--ewai-card-radius) !important;
}

/* Animated pulse for generating state */
@keyframes ewai-pulse {
  0%   { opacity: 1; }
  50%  { opacity: 0.55; }
  100% { opacity: 1; }
}
.pulse-animation {
  animation: ewai-pulse 1.8s ease-in-out infinite;
}

/* Fade-in */
@keyframes ewai-fadein {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
.fade-in {
  animation: ewai-fadein 0.3s ease both;
}

/* ==========================================================================
   20. RESPONSIVE OVERRIDES
   ========================================================================== */
@media (max-width: 767.98px) {
  /* The old rule here was `.auth-branding { display: none }`. That made sense when
     the panel was only a block of text, but it now carries the product's identity,
     the interpreter, and the partner marks -- and because .auth-form-panel centres
     its contents inside a 100dvh grid, hiding the panel left the sign-in button
     floating alone in the middle of an empty white screen. The panel stays as a
     compact banner; sizing is in §12's own mobile block. */
  .auth-form-panel {
    padding: var(--suno-space-8) var(--suno-space-5);
  }
  .preview-sticky {
    position: static;
  }
  .page-header h1 {
    font-size: 1.3rem;
  }
}
