/* ==========================================================================
   COBALTSITES APP — surfaces the marketing site does not have.

   Load order: tokens.css → components.css → app.css.

   Everything here is composed from the existing tokens. Where a new value was
   unavoidable it is derived from one (the purple below is the Discord token's
   gradient geometry, not a new hue picked by eye). Two house rules from the
   elixir-ui README apply throughout:

     1. letter-spacing stays at -0.3px. It is set by .elx and never overridden.
     2. One --primary button per view. On the dashboard that is "Request
        changes"; the purple "Add new site" is a distinct family so the two
        never compete.
   ========================================================================== */

/* --------------------------------------------------------------------------
   00 · Additional tokens
   -------------------------------------------------------------------------- */
:root {
  /* Read off the live page's own Sign In button (.header__discord-join), not
     picked by eye — same radial geometry as the cyan CTA, same 10px radius,
     same shadow construction with the hue swapped. */
  --cs-grad-purple: var(--elx-grad-discord);
  --cs-purple: #5865f2;
  --cs-shadow-purple:
    0 62px 17px rgba(67, 56, 202, 0),
    0 40px 16px rgba(67, 56, 202, .02),
    0 22px 13px rgba(67, 56, 202, .05),
    0 10px 10px rgba(67, 56, 202, .10),
    0 2px 5px rgba(67, 56, 202, .12),
    14px 14px 20px rgba(109, 40, 217, .20),
    inset -3px -3px 4px rgba(196, 181, 253, .27),
    inset 4px 4px 4px rgba(59, 45, 176, .10);

  --cs-topbar-h: 64px;
  --cs-side-w: 232px;
  --cs-side-w-mini: 68px;
}

/* --------------------------------------------------------------------------
   01 · Page frame
   -------------------------------------------------------------------------- */
/* The HTML `hidden` attribute only sets display:none in the UA sheet, so ANY
   author `display` rule beats it. `.elx-alert { display:flex }` therefore made
   every hidden alert render as an empty bordered box above the first field.
   One rule, because this will happen again with any future flex/grid utility. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }
body.elx { margin: 0; min-height: 100vh; }

/* Skip link — the first thing in the tab order on every page. */
.cs-skip {
  position: absolute; left: 12px; top: -60px; z-index: 200;
  padding: 10px 16px; border-radius: var(--elx-r-sm);
  background: var(--elx-surface); border: 1px solid var(--elx-accent);
  color: var(--elx-text); font-size: var(--elx-body-sm);
  transition: top var(--elx-dur);
}
.cs-skip:focus { top: 12px; }

.cs-only-sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* --------------------------------------------------------------------------
   02 · AUTH LAYOUT
   The split card: form on the right, supporting content on the left so a
   1440px viewport is not two thirds empty. Below 900px the columns stack and
   the supporting panel moves BELOW the form — on a phone the job is to sign
   in, and marketing copy above the fields just pushes them off screen.
   -------------------------------------------------------------------------- */
.cs-auth {
  min-height: 100vh;
  display: grid;
  grid-template-rows: auto 1fr auto;
}

/* Logo and back link sit together on the left. The back link used to be
   pushed to the far right by space-between, which put the only way out of the
   flow as far from the brand as the viewport allowed. */
.cs-auth__bar {
  display: flex; align-items: center; justify-content: flex-start; gap: var(--elx-s-5);
  padding: var(--elx-s-5) var(--elx-gutter);
  max-width: 1180px; margin-inline: auto; width: 100%;
}
.cs-auth__logo { display: flex; align-items: center; }

/* The logo PNG carries transparent safe-area padding, so the live site crops
   it in CSS rather than shipping a second file. Same numbers, same source
   pixels — copied from the #cobaltsites-branding block on the landing page.
   The wordmark is INSIDE this image, which is why there is no text beside it;
   adding one renders the brand name twice. */
.cs-logo-crop {
  position: relative;
  display: block;
  overflow: hidden;
  aspect-ratio: 1562 / 343;
  height: 30px;
  flex: 0 0 auto;
}
.cs-logo-crop > img {
  position: absolute;
  top: -68.22%;
  left: -13.96%;
  width: 126.95%;
  height: auto;
  max-width: none;
  margin: 0;
  object-fit: unset;
}
/* Separates the back link from the brand without a hard divider. */
.cs-auth__bar .cs-auth__back { padding-left: var(--elx-s-5); border-left: 1px solid var(--elx-hairline); }
@media (max-width: 560px) {
  .cs-logo-crop { height: 24px; }
  .cs-auth__bar { gap: var(--elx-s-3); }
  .cs-auth__bar .cs-auth__back { padding-left: var(--elx-s-3); }
}
.cs-auth__back {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: var(--elx-body-sm); color: var(--elx-text-50);
  padding: 8px 4px; border-radius: var(--elx-r-sm);
  transition: color var(--elx-dur);
}
.cs-auth__back:hover { color: var(--elx-text); }
.cs-auth__back svg { width: 15px; height: 15px; }

.cs-auth__body {
  display: grid; place-items: center;
  padding: var(--elx-s-4) var(--elx-gutter) var(--elx-s-10);
  width: 100%;
}

/* The card itself. One rectangle, split down the middle on desktop. */
.cs-card {
  width: min(980px, 100%);
  display: grid;
  grid-template-columns: 1fr 1fr;
  background-image: var(--elx-card-fill);
  border: 0;
  border-radius: var(--elx-r-card);
  box-shadow: var(--elx-card-shadow);
  overflow: hidden;
}
.cs-card--narrow { grid-template-columns: 1fr; width: min(520px, 100%); }

.cs-card__aside {
  padding: var(--elx-s-10) var(--elx-s-8);
  background-image: var(--elx-glass);
  border-right: 1px solid var(--elx-hairline);
  display: flex; flex-direction: column; gap: var(--elx-s-6);
  position: relative; overflow: hidden;
}
.cs-card__aside::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(circle at 0% 0%, rgba(110, 228, 255, .12) 0%, transparent 60%);
}
.cs-card__aside > * { position: relative; }

.cs-card__form { padding: var(--elx-s-10) var(--elx-s-8); }

@media (max-width: 900px) {
  .cs-card { grid-template-columns: 1fr; }
  /* Form first, supporting panel after — order matters more than source order. */
  .cs-card__form  { order: 1; padding: var(--elx-s-8) var(--elx-s-6); }
  .cs-card__aside { order: 2; border-right: 0; border-top: 1px solid var(--elx-hairline); padding: var(--elx-s-8) var(--elx-s-6); }
}

/* Every heading on the live page is a clipped gradient, never a flat white.
   Dropping this is the most visible way an auth page stops matching. */
.cs-card__title {
  font-size: var(--elx-h3);
  font-weight: var(--elx-w-semi);
  background-image: var(--elx-grad-head);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.cs-card__sub { color: var(--elx-text-60); font-size: var(--elx-body-sm); line-height: var(--elx-lh-loose); margin-top: 10px; }

/* Benefit list in the aside */
.cs-points { display: grid; gap: var(--elx-s-5); }
.cs-point { display: flex; gap: 13px; }
.cs-point__icon {
  width: 32px; height: 32px; flex: none; border-radius: var(--elx-r-sm);
  display: grid; place-items: center;
  background: rgba(110, 228, 255, .1); border: 1px solid rgba(110, 228, 255, .22);
  color: var(--elx-accent);
}
.cs-point__icon svg { width: 16px; height: 16px; }
.cs-point__title { font-size: var(--elx-body-sm); font-weight: var(--elx-w-medium); }
.cs-point__body { font-size: var(--elx-caption); color: var(--elx-text-50); line-height: var(--elx-lh-loose); margin-top: 3px; }

/* --------------------------------------------------------------------------
   03 · FORMS
   -------------------------------------------------------------------------- */
.cs-form { display: grid; gap: var(--elx-s-5); }
.cs-form__foot { font-size: var(--elx-body-sm); color: var(--elx-text-50); text-align: center; }
.cs-form__foot a { color: var(--elx-accent); text-decoration: underline; text-underline-offset: 3px; }
.cs-form__foot a:hover { color: var(--elx-accent-hover); }

.cs-label-row { display: flex; align-items: baseline; justify-content: space-between; gap: var(--elx-s-3); }
.cs-label-row a { font-size: var(--elx-caption); color: var(--elx-accent); text-decoration: underline; text-underline-offset: 3px; }

/* Error text sits under the field. The is-invalid class on .elx-field already
   turns the border red; this adds the icon and reserves no space when empty
   so the layout does not jump on first error. */
.elx-field__error { align-items: center; gap: 6px; }
.elx-field.is-invalid .elx-field__error { display: flex; }
.elx-field__error svg { width: 13px; height: 13px; flex: none; }
/* The red ring, not just a red line — matches the accent focus ring's weight. */
.elx-field.is-invalid .elx-input:focus,
.elx-field.is-invalid .elx-input {
  border-color: var(--elx-danger);
  box-shadow: 0 0 0 3px rgba(248, 113, 113, .14);
}

/* Reveal / hide password */
.cs-reveal {
  position: absolute; right: 6px; top: 50%; translate: 0 -50%;
  display: grid; place-items: center; width: 34px; height: 34px;
  background: none; border: 0; border-radius: var(--elx-r-sm);
  color: var(--elx-text-40); cursor: pointer;
  transition: color var(--elx-dur), background-color var(--elx-dur);
}
.cs-reveal:hover { color: var(--elx-text-80); background: rgba(255, 255, 255, .05); }
.cs-reveal svg { width: 17px; height: 17px; }
.cs-inputwrap { position: relative; display: block; }
.cs-inputwrap .elx-input { padding-right: 46px; }

/* Password strength meter */
.cs-meter { display: grid; gap: 6px; margin-top: 2px; }
.cs-meter__track { display: flex; gap: 4px; }
.cs-meter__seg { height: 3px; flex: 1; border-radius: 3px; background: rgba(255, 255, 255, .09); transition: background-color var(--elx-dur); }
.cs-meter__seg.is-on { background: var(--elx-warning); }
.cs-meter[data-score="3"] .cs-meter__seg.is-on,
.cs-meter[data-score="4"] .cs-meter__seg.is-on { background: var(--elx-success); }
.cs-meter[data-score="1"] .cs-meter__seg.is-on { background: var(--elx-danger); }
.cs-meter__text { font-size: var(--elx-micro); color: var(--elx-text-40); }

/* Divider with a word in it */
.cs-or { display: flex; align-items: center; gap: var(--elx-s-4); color: var(--elx-text-40); font-size: var(--elx-caption); }
.cs-or::before, .cs-or::after { content: ''; flex: 1; height: 1px; background: var(--elx-hairline); }

/* Google button. Google's brand guidelines require the unmodified mark and
   readable label; this is their neutral dark treatment. */
.cs-google {
  display: flex; align-items: center; justify-content: center; gap: 11px;
  width: 100%; padding: 13px 18px;
  background: rgba(255, 255, 255, .04);
  border: 1px solid var(--elx-hairline);
  border-radius: var(--elx-r-btn);
  font-family: var(--elx-font); font-size: var(--elx-body-sm); font-weight: var(--elx-w-medium);
  letter-spacing: var(--elx-ls); color: var(--elx-text); cursor: pointer;
  transition: background-color var(--elx-dur), border-color var(--elx-dur);
}
.cs-google:hover { background: rgba(255, 255, 255, .09); border-color: rgba(255, 255, 255, .24); }
.cs-google:disabled { opacity: .5; pointer-events: none; }
.cs-google svg { width: 18px; height: 18px; }

/* Button busy state — swaps the label for a spinner without changing width,
   so the button does not resize mid-submit. */
.cs-btn-busy { position: relative; pointer-events: none; }
.cs-btn-busy > span:not(.cs-spin) { opacity: 0; }
.cs-spin {
  position: absolute; inset: 0; display: grid; place-items: center;
}
.cs-spin::after {
  content: ''; width: 16px; height: 16px; border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, .3); border-top-color: #fff;
  animation: cs-rotate .7s linear infinite;
}
@keyframes cs-rotate { to { transform: rotate(360deg); } }

/* Purple action — the "add another site" family. */
.elx-btn--purple {
  background-image: var(--cs-grad-purple);
  color: #fff;
  box-shadow: var(--cs-shadow-purple);
  position: relative; isolation: isolate; overflow: hidden;
}
.elx-btn--purple::before {
  /* The 2px glossy rim, same mask-composite trick as the primary button. */
  content: ''; position: absolute; inset: 0; border-radius: inherit; padding: 2px;
  background: var(--elx-grad-rim);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  pointer-events: none; z-index: 2;
}
.elx-btn--purple > * { position: relative; z-index: 3; }
.elx-btn--purple:hover { filter: brightness(1.07); transform: translateY(-1px); }
.elx-btn--purple:active { transform: translateY(0); }

/* --------------------------------------------------------------------------
   04 · CLAIM — token entry and the verifying stage
   -------------------------------------------------------------------------- */
.cs-token {
  font-family: ui-monospace, 'SF Mono', 'Cascadia Mono', Menlo, monospace;
  font-size: 20px;
  letter-spacing: 2.5px;
  text-align: center;
  text-transform: uppercase;
  padding: 18px 15px;
}
.cs-token::placeholder { letter-spacing: 2.5px; opacity: .35; }

/* Stage machine: exactly one [data-stage] is visible at a time. */
[data-stage] { display: none; }
[data-stage].is-active { display: block; }

.cs-verify { display: grid; justify-items: center; gap: var(--elx-s-6); padding: var(--elx-s-10) 0; text-align: center; }

/* The spinner: two counter-rotating conic arcs in the brand cyan around a
   pulsing core. Conic gradients keep it to two elements and no SVG. */
.cs-orbit { position: relative; width: 96px; height: 96px; }
.cs-orbit__ring {
  position: absolute; inset: 0; border-radius: 50%;
  background: conic-gradient(from 0deg, transparent 0deg, transparent 250deg, var(--elx-accent) 340deg, transparent 360deg);
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 calc(100% - 3px));
  mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 calc(100% - 3px));
  animation: cs-rotate 1.4s linear infinite;
}
.cs-orbit__ring--inner {
  inset: 16px;
  background: conic-gradient(from 180deg, transparent 0deg, transparent 280deg, var(--elx-accent-sky) 350deg, transparent 360deg);
  animation-duration: 2.1s;
  animation-direction: reverse;
  opacity: .65;
}
.cs-orbit__core {
  position: absolute; inset: 34px; border-radius: 50%;
  background: var(--elx-grad-buy);
  box-shadow: 0 0 24px -4px rgba(110, 228, 255, .7);
  animation: cs-pulse 1.9s var(--elx-ease-soft) infinite;
}
@keyframes cs-pulse {
  0%, 100% { transform: scale(1); opacity: .9; }
  50% { transform: scale(.82); opacity: 1; }
}

/* Rotating status line. Each phrase fades up as the previous fades out. */
.cs-verify__status { min-height: 26px; position: relative; width: 100%; }
.cs-verify__word {
  position: absolute; inset: 0;
  font-size: var(--elx-body-lg); font-weight: var(--elx-w-medium); color: var(--elx-text);
  opacity: 0; transform: translateY(8px);
  transition: opacity .45s var(--elx-ease-soft), transform .45s var(--elx-ease-soft);
}
.cs-verify__word.is-in { opacity: 1; transform: none; }
.cs-verify__word.is-out { opacity: 0; transform: translateY(-8px); }

.cs-verify .elx-progress { width: min(280px, 100%); }

/* A checklist that ticks off as the stages complete — makes the wait feel like
   work happening rather than a spinner with nothing behind it. */
.cs-checklist { display: grid; gap: 10px; text-align: left; width: min(300px, 100%); }
.cs-checkitem {
  display: flex; align-items: center; gap: 10px;
  font-size: var(--elx-body-sm); color: var(--elx-text-40);
  transition: color var(--elx-dur-med);
}
.cs-checkitem__mark {
  width: 17px; height: 17px; flex: none; border-radius: 50%;
  border: 1px solid var(--elx-hairline-2);
  display: grid; place-items: center;
  transition: background-color var(--elx-dur-med), border-color var(--elx-dur-med);
}
.cs-checkitem__mark svg { width: 10px; height: 10px; opacity: 0; transition: opacity var(--elx-dur-med); }
.cs-checkitem.is-done { color: var(--elx-text-80); }
.cs-checkitem.is-done .cs-checkitem__mark { background: var(--elx-accent); border-color: var(--elx-accent); color: var(--elx-accent-ink); }
.cs-checkitem.is-done .cs-checkitem__mark svg { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .cs-orbit__ring, .cs-orbit__core, .cs-spin::after { animation: none; }
  .cs-orbit__ring { background: conic-gradient(from 0deg, var(--elx-accent) 0deg, transparent 300deg); }
  .cs-verify__word { transition: none; }
}

/* Found-site confirmation panel */
.cs-found { display: grid; gap: var(--elx-s-5); }
.cs-found__shot {
  aspect-ratio: 16 / 9; border-radius: var(--elx-r-md); overflow: hidden;
  border: 1px solid var(--elx-border); background: #0f0f0f;
}

/* --------------------------------------------------------------------------
   05 · DASHBOARD SHELL
   Top bar always. Sidebar collapses to icons for a single-site account and
   expands at two or more, because a full nav rail around one card is noise.
   -------------------------------------------------------------------------- */
.cs-shell { min-height: 100vh; display: grid; grid-template-rows: auto var(--cs-topbar-h) 1fr; }
/* Each child pinned to its row. The alertbar is display:none whenever there is
   no banner, and without explicit placement its absence shifted everything up:
   the menu bar fell into the auto row and collapsed to content height. */
.cs-alertbar { grid-row: 1; }
.cs-top { grid-row: 2; }
.cs-body { grid-row: 3; }

/* --------------------------------------------------------------------------
   Lifecycle alert bar.

   The one "your site is going away" message, rendered as part of the fixed
   chrome rather than page content: a strip ABOVE the top bar, pinned with it,
   visible on every panel, and impossible to scroll away. The top bar and the
   sidebar offset themselves by its measured height (--cs-alertbar-h, set by
   renderAlertbar) so the two read as one piece of chrome.
   -------------------------------------------------------------------------- */
:root { --cs-alertbar-h: 0px; }

.cs-alertbar {
  position: sticky; top: 0; z-index: 61;
  display: flex; align-items: center; gap: var(--elx-s-4);
  padding: 9px var(--elx-s-5);
  font-size: var(--elx-body-sm);
  line-height: var(--elx-lh-loose);
  color: #fff;
}
.cs-alertbar[hidden] { display: none; }
.cs-alertbar svg { width: 16px; height: 16px; flex: none; color: #fff; }
.cs-alertbar__msg { flex: 1; min-width: 0; }

/* Critical: the site is offline or about to be. Solid red, same family as
   .elx-alert--critical, flush against the top bar below it. */
.cs-alertbar--critical {
  background: #b3261e;
  border-bottom: 1px solid rgba(0, 0, 0, .25);
}
/* Warning: the trial is ending soon. Amber, dark text for contrast. */
.cs-alertbar--warning {
  background: #b45309;
  border-bottom: 1px solid rgba(0, 0, 0, .25);
}

/* The action: solid white on the tinted bar. Deliberately NOT the cyan
   primary — a blue glow floating on a red strip reads as two different
   messages. No gradient, no rim, no lift shadow. */
.cs-alertbar .cs-alertbar__btn {
  margin-left: auto; flex: none;
  background: #fff;
  border: 1px solid #fff;
  border-radius: var(--elx-r-md);
  color: #7f1d1d;
  font-weight: var(--elx-w-semi);
  box-shadow: 0 1px 2px rgba(0, 0, 0, .25);
  text-shadow: none;
}
.cs-alertbar--warning .cs-alertbar__btn { color: #78350f; }
.cs-alertbar .cs-alertbar__btn:hover { background: #f3f4f6; border-color: #f3f4f6; transform: none; box-shadow: 0 1px 3px rgba(0, 0, 0, .3); }
.cs-alertbar .cs-alertbar__btn:active { transform: translateY(1px); }
.cs-alertbar .cs-alertbar__btn:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

@media (max-width: 640px) {
  /* Stack rather than squeeze — the button is the point. */
  .cs-alertbar { flex-wrap: wrap; }
  .cs-alertbar .cs-alertbar__btn { margin-left: 0; width: 100%; justify-content: center; }
}

/* On a phone the top bar cannot hold two labelled action buttons next to the
   burger, logo and avatar — they pushed the whole page into horizontal
   scroll. Both actions live inside the panels too, so the header copy of
   "Request changes" goes and "Add new site" collapses to its plus icon. */
@media (max-width: 760px) {
  .cs-top > [data-request-change] { display: none; }
  .cs-top [data-add-site] span:last-child { display: none; }
}

.cs-top {
  position: sticky; top: var(--cs-alertbar-h); z-index: 60;
  display: flex; align-items: center; gap: var(--elx-s-4);
  padding: 0 var(--elx-s-5);
  background: rgba(25, 25, 25, .85);
  backdrop-filter: var(--elx-blur);
  border-bottom: 1px solid rgba(255, 255, 255, .1);
}
.cs-top__logo { display: flex; align-items: center; }
.cs-top__logo .cs-logo-crop { height: 26px; }
.cs-top__spacer { flex: 1; }

.cs-burger {
  display: none; width: 38px; height: 38px; place-items: center;
  background: none; border: 1px solid var(--elx-hairline); border-radius: var(--elx-r-sm);
  color: var(--elx-text-60); cursor: pointer;
}
.cs-burger svg { width: 18px; height: 18px; }

/* Site switcher in the bar — only rendered when there is more than one site. */
.cs-switch {
  display: flex; align-items: center; gap: 9px;
  padding: 7px 13px; border-radius: var(--elx-r-pill);
  background: rgba(255, 255, 255, .04); border: 1px solid var(--elx-hairline);
  font-size: var(--elx-body-sm); color: var(--elx-text-80); cursor: pointer;
}
.cs-switch:hover { border-color: var(--elx-hairline-2); }
.cs-switch svg { width: 13px; height: 13px; color: var(--elx-text-40); }

.cs-body { display: grid; grid-template-columns: var(--cs-side-w) 1fr; min-height: 0; }
.cs-body > * { min-width: 0; }
.cs-body[data-nav="mini"] { grid-template-columns: var(--cs-side-w-mini) 1fr; }

.cs-side {
  border-right: 1px solid var(--elx-hairline);
  background: var(--elx-bg-alt);
  padding: var(--elx-s-5) var(--elx-s-3) var(--elx-s-4);
  display: flex; flex-direction: column; gap: var(--elx-s-2);
  position: sticky; top: calc(var(--cs-topbar-h) + var(--cs-alertbar-h)); align-self: start;
  height: calc(100vh - var(--cs-topbar-h) - var(--cs-alertbar-h)); overflow-y: auto;
}

/* Expand / collapse, pinned to the bottom of the rail. */
.cs-side__toggle {
  margin-top: auto;
  display: flex; align-items: center; gap: 11px;
  padding: 10px 12px; border-radius: var(--elx-r-sm);
  background: none; border: 0; width: 100%; text-align: left; cursor: pointer;
  font-family: var(--elx-font); font-size: var(--elx-body-sm);
  letter-spacing: var(--elx-ls); color: var(--elx-text-40);
  transition: background-color var(--elx-dur), color var(--elx-dur);
}
.cs-side__toggle:hover { background: rgba(255, 255, 255, .04); color: var(--elx-text-80); }
.cs-side__toggle svg { width: 18px; height: 18px; flex: none; transition: transform var(--elx-dur-med) var(--elx-ease-soft); }
.cs-body[data-nav="mini"] .cs-side__toggle { justify-content: center; padding: 11px 0; }
.cs-body[data-nav="mini"] .cs-side__toggle svg { transform: rotate(180deg); }
.cs-body[data-nav="mini"] .cs-side__toggle span { display: none; }
.cs-side__title { font-size: var(--elx-micro); text-transform: uppercase; letter-spacing: .8px; color: var(--elx-text-40); padding: var(--elx-s-4) 12px var(--elx-s-2); }
.cs-body[data-nav="mini"] .cs-side__title,
.cs-body[data-nav="mini"] .cs-navitem span { display: none; }
.cs-body[data-nav="mini"] .cs-navitem { justify-content: center; padding: 11px 0; }

.cs-navitem {
  display: flex; align-items: center; gap: 11px;
  padding: 10px 12px; border-radius: var(--elx-r-sm);
  font-size: var(--elx-body-sm); color: var(--elx-text-60);
  background: none; border: 0; width: 100%; text-align: left;
  font-family: var(--elx-font); letter-spacing: var(--elx-ls); cursor: pointer;
  transition: background-color var(--elx-dur), color var(--elx-dur);
}
.cs-navitem svg { width: 16px; height: 16px; flex: none; }
.cs-navitem:hover { background: rgba(255, 255, 255, .04); color: var(--elx-text); }
.cs-navitem[aria-current="page"] { background: rgba(110, 228, 255, .1); color: var(--elx-accent); font-weight: var(--elx-w-medium); }
.cs-navitem__count { margin-left: auto; font-size: var(--elx-micro); color: var(--elx-text-40); font-variant-numeric: tabular-nums; }
.cs-body[data-nav="mini"] .cs-navitem__count { display: none; }

/* Two column behaviours, because the panels are two different kinds of thing.
   Everything except Sites is a document — one column of settings you read top
   to bottom, so it is centred and as wide as the column allows. Sites is a
   board of cards, and a board fills from the top-left corner; centring one
   card in a wide column is what left it stranded in the middle of the screen.
   [data-current] is set by goToPanel in dashboard.js. */
.cs-main { padding: var(--elx-s-8) var(--elx-s-8) var(--elx-s-15); max-width: 1320px; width: 100%; margin-inline: auto; }
.cs-main[data-current="sites"] { max-width: 1600px; margin-inline: 0; }
.cs-main__head { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--elx-s-5); flex-wrap: wrap; margin-bottom: var(--elx-s-8); }
.cs-main__head h1 { font-size: var(--elx-h3); font-weight: var(--elx-w-semi); }
.cs-main__head p { color: var(--elx-text-50); font-size: var(--elx-body-sm); margin-top: 6px; }

@media (max-width: 1000px) {
  .cs-body, .cs-body[data-nav="mini"] { grid-template-columns: 1fr; }
  .cs-burger { display: grid; }
  .cs-side {
    position: fixed; inset: calc(var(--cs-topbar-h) + var(--cs-alertbar-h)) auto 0 0; z-index: 70;
    width: var(--cs-side-w); height: auto;
    transform: translateX(-100%); transition: transform var(--elx-dur-med) var(--elx-ease-soft);
    border-right: 1px solid var(--elx-border);
  }
  .cs-side.is-open { transform: none; }
  .cs-body[data-nav="mini"] .cs-side__title,
  .cs-body[data-nav="mini"] .cs-navitem span { display: revert; }
  .cs-body[data-nav="mini"] .cs-navitem { justify-content: flex-start; padding: 10px 12px; }
  .cs-main { padding: var(--elx-s-6) var(--elx-gutter) var(--elx-s-10); }
}

/* --------------------------------------------------------------------------
   06 · SITE CARD
   Upper half is a live frame of the real site. Lower half is the facts and
   the actions.
   -------------------------------------------------------------------------- */
/* Capped rather than 1fr: stretched across the full column a single card was
   mostly empty preview. `start` lines the first card up with the panel heading
   above it — the column itself is what centres the whole block on screen. */
.cs-sites {
  display: grid; gap: var(--elx-s-5);
  grid-template-columns: repeat(auto-fill, minmax(340px, 460px));
  justify-content: start;
}

.cs-site {
  display: flex; flex-direction: column;
  background-image: var(--elx-card-fill);
  border: 0;
  border-radius: var(--elx-r-card);
  box-shadow: var(--elx-card-shadow);
  overflow: hidden;
  transition: transform var(--elx-dur), filter var(--elx-dur);
}
.cs-site:hover { filter: brightness(1.06); transform: translateY(-2px); }

/* The seam between the screenshot and the card body.
   --cs-site-seam is the one colour both halves fade to. It sits close to the
   card's own #343434→#272727 ramp at this point, and because BOTH sides use
   the same constant the join is invisible even where it drifts a shade off
   the pure ramp. A hard 1px border here made the card read as two stacked
   boxes; this makes the picture end and the card begin. */
:root { --cs-site-seam: 45, 45, 45; }

.cs-site__shot {
  position: relative;
  display: block;
  aspect-ratio: 16 / 10;
  background: #0d0d0d;
  overflow: hidden;
  /* Own stacking context. Without it the badge, the scrim and the seam fade
     compete at card level, and the fade (z-index 2) painted straight across
     the kebab menu — which opens upward, out of the body and over the shot. */
  isolation: isolate;
}
.cs-site__shot::before {
  content: ''; position: absolute; inset: auto 0 -1px 0; height: 38%; z-index: 2;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    rgba(var(--cs-site-seam), 0) 0%,
    rgba(var(--cs-site-seam), .28) 40%,
    rgba(var(--cs-site-seam), .76) 73%,
    rgb(var(--cs-site-seam)) 100%);
}
/* The whole preview opens the site. The frame inside is pointer-inert, so the
   click has to be caught by a transparent hit layer above it.
   No scrim and no button: dimming the screenshot on hover hides the one thing
   the card exists to show, and a floating CTA over a picture that is already
   entirely clickable is a second affordance for the same action. All that is
   left is a small corner glyph saying where the click goes. */
a.cs-site__shot { cursor: pointer; }
.cs-site__open { position: absolute; inset: 0; z-index: 3; }
.cs-site__open span {
  position: absolute; top: 10px; right: 10px;
  display: grid; place-items: center; width: 28px; height: 28px;
  border-radius: 8px;
  background: rgba(15, 15, 15, .55);
  border: 1px solid rgba(255, 255, 255, .14);
  backdrop-filter: var(--elx-blur);
  -webkit-backdrop-filter: var(--elx-blur);
  color: rgba(255, 255, 255, .78);
  opacity: 0; transform: translateY(-2px);
  transition: opacity var(--elx-dur), transform var(--elx-dur), color var(--elx-dur);
}
a.cs-site__shot:hover .cs-site__open span,
a.cs-site__shot:focus-visible .cs-site__open span { opacity: 1; transform: none; }
a.cs-site__shot:hover .cs-site__open span { color: #fff; }
.cs-site__open svg { width: 14px; height: 14px; }
/* The real site, rendered at desktop width and scaled down. Sandboxed and
   pointer-inert: it is a picture, not an interactive surface. */
.cs-site__frame {
  position: absolute; top: 0; left: 0;
  width: 1280px; height: 800px;
  transform-origin: 0 0;
  border: 0;
  pointer-events: none;
}
.cs-site__img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: top center; }
.cs-site__shot--empty { display: grid; place-items: center; color: var(--elx-text-40); font-size: var(--elx-caption); }

/* While the screenshot or the framed site is still arriving, the shot area
   shimmers instead of sitting black — a serverless render can take seconds
   from cold, and an empty dark box for that long reads as broken rather than
   loading. Cleared by the load event; the content then fades in. */
.cs-site__shot.is-loading {
  background: linear-gradient(100deg,
    rgba(255, 255, 255, .04) 30%, rgba(255, 255, 255, .09) 46%,
    rgba(255, 255, 255, .09) 54%, rgba(255, 255, 255, .04) 70%);
  background-size: 220% 100%;
  animation: cs-shot-shimmer 1.4s ease-in-out infinite;
}
@keyframes cs-shot-shimmer {
  from { background-position: 130% 0; }
  to { background-position: -30% 0; }
}
.cs-site__shot.is-loading .cs-site__img,
.cs-site__shot.is-loading .cs-site__frame { opacity: 0; }
.cs-site__shot .cs-site__img,
.cs-site__shot .cs-site__frame { transition: opacity var(--elx-dur-med, .3s) ease; }
@media (prefers-reduced-motion: reduce) {
  .cs-site__shot.is-loading { animation: none; }
}

.cs-site__badge { position: absolute; top: 12px; left: 12px; z-index: 2; }
/* Keeps the badge legible over a light hero image. */
.cs-site__shot::after {
  content: ''; position: absolute; inset: 0 0 60% 0; z-index: 1; pointer-events: none;
  background: linear-gradient(180deg, rgba(0, 0, 0, .55), transparent);
}

/* The other half of the seam: the body starts at the same colour the picture
   faded to and dissolves back into the card's own fill. */
/* Above the shot, so anything that opens upward out of the body — the kebab
   menu — clears the picture and its fade. */
.cs-site__body { position: relative; z-index: 2; padding: var(--elx-s-5); display: grid; gap: var(--elx-s-4); flex: 1; }
.cs-site__body::before {
  content: ''; position: absolute; inset: 0 0 auto 0; height: 90px; z-index: 0;
  pointer-events: none;
  background: linear-gradient(180deg, rgb(var(--cs-site-seam)), rgba(var(--cs-site-seam), 0));
}
.cs-site__body > * { position: relative; z-index: 1; }
.cs-site__title { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--elx-s-3); }
.cs-site__name { font-size: var(--elx-body-lg); font-weight: var(--elx-w-semi); }
.cs-site__url { font-size: var(--elx-caption); color: var(--elx-text-40); margin-top: 4px; word-break: break-all; }
.cs-site__url a { color: var(--elx-text-50); text-decoration: underline; text-underline-offset: 2px; }
.cs-site__url a:hover { color: var(--elx-accent); }

.cs-site__meta { display: flex; flex-wrap: wrap; gap: var(--elx-s-2) var(--elx-s-4); font-size: var(--elx-caption); color: var(--elx-text-50); }
.cs-site__meta b { color: var(--elx-text-80); font-weight: var(--elx-w-medium); }
.cs-site__meta--warn b { color: var(--elx-warning); }

.cs-site__actions { display: flex; align-items: center; gap: var(--elx-s-2); margin-top: auto; }
.cs-site__actions .elx-btn--primary { flex: 1; }

/* --------------------------------------------------------------------------
   06b · SETUP CHECKLIST
   Tick / cross / ellipsis against a colour, so "what is left to do" is one
   glance rather than a paragraph. See checklistItems() in dashboard.js for
   what each tone means — the short version is that red is the only one that
   is waiting on the customer, and every red row is a button.
   -------------------------------------------------------------------------- */
.cs-check {
  display: grid; gap: 1px;
  padding: 6px; margin: 0 -6px;
  border-radius: var(--elx-r-sm);
  background: rgba(255, 255, 255, .03);
}
.cs-check__row {
  display: flex; align-items: center; gap: 9px; width: 100%;
  padding: 7px 8px; border-radius: 7px;
  background: none; border: 0; text-align: left;
  font-family: var(--elx-font); font-size: var(--elx-caption);
  letter-spacing: var(--elx-ls); color: var(--elx-text-60);
}
button.cs-check__row { cursor: pointer; transition: background-color var(--elx-dur); }
button.cs-check__row:hover { background: rgba(255, 255, 255, .06); }

.cs-check__mark {
  width: 17px; height: 17px; flex: none; border-radius: 50%;
  display: grid; place-items: center;
}
.cs-check__mark svg { width: 11px; height: 11px; stroke-width: 2.6; }
.cs-check__row.is-ok   .cs-check__mark { background: rgba(74, 222, 128, .16);  color: var(--elx-success); }
.cs-check__row.is-todo .cs-check__mark { background: rgba(248, 113, 113, .16); color: var(--elx-danger); }

.cs-check__label { flex: none; color: var(--elx-text-80); font-weight: var(--elx-w-medium); }
/* The value is the answer — the domain, the address, the reason — so it gets
   the row's colour and the label stays neutral. */
.cs-check__value {
  flex: 1; min-width: 0; text-align: right;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cs-check__row.is-ok   .cs-check__value { color: var(--elx-text-60); }
.cs-check__row.is-todo .cs-check__value { color: var(--elx-danger); }

/* Present on every row so the value column ends at the same x, whether or not
   the row is actionable — visible only where there is somewhere to go. */
.cs-check__go {
  width: 13px; height: 13px; flex: none; color: var(--elx-text-40);
  opacity: 0; transition: opacity var(--elx-dur), transform var(--elx-dur);
}
button.cs-check__row:hover .cs-check__go,
button.cs-check__row:focus-visible .cs-check__go { opacity: 1; transform: translateX(2px); }

/* Kebab menu */
.cs-kebab { position: relative; }
.cs-kebab__btn {
  width: 40px; height: 40px; display: grid; place-items: center;
  background: rgba(255, 255, 255, .04); border: 1px solid var(--elx-hairline);
  border-radius: var(--elx-r-btn); color: var(--elx-text-60); cursor: pointer;
  transition: background-color var(--elx-dur), color var(--elx-dur);
}
.cs-kebab__btn:hover { background: rgba(255, 255, 255, .09); color: var(--elx-text); }
.cs-kebab__btn svg { width: 17px; height: 17px; }

.cs-menu {
  position: absolute; right: 0; bottom: calc(100% + 8px); z-index: 50;
  min-width: 236px; padding: 6px;
  background: var(--elx-raise); border: 1px solid var(--elx-border);
  border-radius: var(--elx-r-md); box-shadow: var(--elx-shadow-pop);
}
.cs-menu[hidden] { display: none; }
.cs-menu--down { top: calc(100% + 8px); bottom: auto; }
.cs-menu__item {
  display: flex; align-items: center; gap: 11px; width: 100%;
  padding: 10px 12px; border-radius: var(--elx-r-sm);
  background: none; border: 0; text-align: left;
  font-family: var(--elx-font); font-size: var(--elx-body-sm); letter-spacing: var(--elx-ls);
  color: var(--elx-text-80); cursor: pointer;
  transition: background-color var(--elx-dur), color var(--elx-dur);
}
.cs-menu__item:hover { background: rgba(255, 255, 255, .06); color: var(--elx-text); }
.cs-menu__item svg { width: 15px; height: 15px; flex: none; color: var(--elx-text-40); }
.cs-menu__item:hover svg { color: var(--elx-accent); }
.cs-menu__item--danger { color: var(--elx-danger); }
.cs-menu__item--danger svg { color: var(--elx-danger); }
.cs-menu__sep { height: 1px; background: var(--elx-hairline); margin: 5px 0; }
.cs-menu__head { padding: 10px 12px 8px; }
.cs-menu__head b { display: block; font-size: var(--elx-body-sm); font-weight: var(--elx-w-medium); }
.cs-menu__head span { display: block; font-size: var(--elx-caption); color: var(--elx-text-40); margin-top: 2px; word-break: break-all; }

/* Avatar button in the top bar */
.cs-avatar-btn {
  display: flex; align-items: center; gap: 9px; padding: 4px 4px 4px 10px;
  background: none; border: 1px solid var(--elx-hairline); border-radius: var(--elx-r-pill);
  color: var(--elx-text-80); cursor: pointer; font-family: var(--elx-font);
  font-size: var(--elx-body-sm); letter-spacing: var(--elx-ls);
  transition: border-color var(--elx-dur);
}
.cs-avatar-btn:hover { border-color: var(--elx-hairline-2); }
.cs-avatar-btn .elx-avatar { width: 30px; height: 30px; }
@media (max-width: 700px) { .cs-avatar-btn > span { display: none; } }

/* Empty state */
.cs-empty {
  display: grid; justify-items: center; gap: var(--elx-s-5); text-align: center;
  padding: var(--elx-s-15) var(--elx-s-6);
  border: 1px dashed var(--elx-border); border-radius: var(--elx-r-card);
}
.cs-empty h2 { font-size: var(--elx-h4); font-weight: var(--elx-w-semi); }
.cs-empty p { color: var(--elx-text-50); font-size: var(--elx-body-sm); max-width: 420px; line-height: var(--elx-lh-loose); }

/* --------------------------------------------------------------------------
   07 · PANELS, MODALS, TOASTS
   -------------------------------------------------------------------------- */
.cs-panel[hidden] { display: none; }

.cs-modal-card {
  width: min(560px, calc(100vw - 32px));
  max-height: calc(100vh - 64px);
  overflow-y: auto;
  /* The default light scrollbar is jarring on a dark modal. */
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, .18) transparent;
}
.cs-modal-card::-webkit-scrollbar { width: 10px; }
.cs-modal-card::-webkit-scrollbar-track { background: transparent; }
.cs-modal-card::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, .16);
  border-radius: 10px;
  border: 3px solid transparent;
  background-clip: content-box;
}
.cs-modal-card::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, .28); background-clip: content-box; }
.cs-modal-card--wide { width: min(720px, calc(100vw - 32px)); }
.cs-modal-card .cs-modal__title,
.cs-modal__title {
  /* Scoped: `.elx h2` is (0,1,1) and beats a single class, so every modal
     heading in the dashboard was rendering at hero size and wrapping. */
  font-size: var(--elx-h4);
  font-weight: var(--elx-w-semi);
  line-height: 1.25;
}
.cs-modal__sub { color: var(--elx-text-50); font-size: var(--elx-body-sm); margin-top: 5px; line-height: var(--elx-lh-loose); }
.cs-modal__close {
  width: 34px; height: 34px; flex: none; display: grid; place-items: center;
  background: none; border: 0; border-radius: var(--elx-r-sm);
  color: var(--elx-text-40); cursor: pointer;
}
.cs-modal__close:hover { background: rgba(255, 255, 255, .06); color: var(--elx-text); }
.cs-modal__close svg { width: 17px; height: 17px; }

.cs-toasts { position: fixed; right: 16px; bottom: 16px; z-index: 200; display: grid; gap: 10px; width: min(360px, calc(100vw - 32px)); }
.cs-toast {
  display: flex; align-items: flex-start; gap: 11px;
  padding: 14px 16px; border-radius: var(--elx-r-md);
  background: var(--elx-raise); border: 1px solid var(--elx-border);
  border-left: 2px solid var(--elx-accent);
  box-shadow: var(--elx-shadow-pop);
  font-size: var(--elx-body-sm); color: var(--elx-text-80); line-height: var(--elx-lh-normal);
  animation: cs-toast-in .28s var(--elx-ease-soft);
}
.cs-toast--success { border-left-color: var(--elx-success); }
.cs-toast--danger { border-left-color: var(--elx-danger); }
.cs-toast svg { width: 16px; height: 16px; flex: none; margin-top: 2px; }
.cs-toast--success svg { color: var(--elx-success); }
.cs-toast--danger svg { color: var(--elx-danger); }
@keyframes cs-toast-in { from { opacity: 0; transform: translateY(10px); } }
@media (prefers-reduced-motion: reduce) { .cs-toast { animation: none; } }

/* Request timeline */
.cs-timeline { display: grid; gap: 0; }
.cs-tl { display: grid; grid-template-columns: 22px 1fr; gap: var(--elx-s-4); }
.cs-tl__rail { display: grid; grid-template-rows: 20px 1fr; justify-items: center; }
.cs-tl__dot { width: 11px; height: 11px; border-radius: 50%; border: 2px solid var(--elx-hairline-2); background: var(--elx-bg); margin-top: 5px; }
.cs-tl__line { width: 1px; background: var(--elx-hairline); }
.cs-tl.is-done .cs-tl__dot { background: var(--elx-accent); border-color: var(--elx-accent); }
.cs-tl.is-current .cs-tl__dot { background: var(--elx-accent); border-color: var(--elx-accent); box-shadow: 0 0 0 4px rgba(110, 228, 255, .18); }
.cs-tl__body { padding-bottom: var(--elx-s-5); }
.cs-tl__label { font-size: var(--elx-body-sm); color: var(--elx-text-40); }
.cs-tl.is-done .cs-tl__label, .cs-tl.is-current .cs-tl__label { color: var(--elx-text-80); }
.cs-tl:last-child .cs-tl__line { display: none; }

/* DNS record table */
.cs-dns { width: 100%; border-collapse: collapse; font-size: var(--elx-body-sm); }
.cs-dns th { text-align: left; font-weight: var(--elx-w-medium); color: var(--elx-text-40); font-size: var(--elx-micro); text-transform: uppercase; letter-spacing: .8px; padding-bottom: 8px; }
.cs-dns td { padding: 9px 12px 9px 0; border-top: 1px solid var(--elx-hairline); font-family: ui-monospace, Menlo, monospace; color: var(--elx-text-80); word-break: break-all; }

/* Loading skeleton for the dashboard's first paint */
.cs-skel-card { border: 1px solid var(--elx-border); border-radius: var(--elx-r-card); overflow: hidden; }
.cs-skel-card__shot { aspect-ratio: 16 / 10; }
.cs-skel-card__body { padding: var(--elx-s-5); display: grid; gap: 12px; }

/* --------------------------------------------------------------------------
   08 · FILE DROP
   Uploads go straight to Cloud Storage under orgs/{orgId}/uploads/{requestId}/,
   which storage.rules already restricts by membership, content type and size.
   The same limits are enforced here so a wrong file fails instantly instead of
   after the upload.
   -------------------------------------------------------------------------- */
/* A small square tile, not a full-width banner. The old version stretched the
   width of the modal and — because its inline SVG carried no class and so no
   width — rendered the icon at the intrinsic 300x150 replaced-element size.
   It read as the biggest thing in the form when it is an optional extra. */
.cs-drop {
  width: 128px; height: 128px; flex: none;
  display: grid; place-items: center; gap: 6px; align-content: center;
  padding: var(--elx-s-3);
  border: 1px dashed var(--elx-border);
  border-radius: var(--elx-r-md);
  background: rgba(255, 255, 255, .02);
  text-align: center; cursor: pointer;
  transition: border-color var(--elx-dur), background-color var(--elx-dur);
}
.cs-drop:hover, .cs-drop:focus-visible { border-color: var(--elx-hairline-2); background: rgba(255, 255, 255, .04); }
.cs-drop.is-over { border-color: var(--elx-accent); background: rgba(110, 228, 255, .07); }
.cs-drop__title { font-size: var(--elx-caption); color: var(--elx-text-50); }

/* The tile artwork: three image cards fanned out behind an upload arrow. Grey
   throughout — it is a placeholder for content that does not exist yet, so it
   must not compete with anything real on the form. */
.cs-drop__art { position: relative; width: 62px; height: 48px; }
/* Opaque, not translucent: three overlapping translucent rectangles read as
   one muddy shape rather than a stack of cards. */
.cs-drop__card {
  position: absolute; left: 50%; top: 50%;
  width: 30px; height: 34px; margin: -17px 0 0 -15px;
  border-radius: 5px;
  border: 1.4px solid rgba(255, 255, 255, .30);
  background: #262626;
  transition: transform var(--elx-dur-med) var(--elx-ease-soft), border-color var(--elx-dur);
}
.cs-drop__card--back { transform: rotate(-15deg) translate(-10px, 1px); }
.cs-drop__card--mid  { transform: rotate(13deg) translate(10px, 1px); }
.cs-drop__card--front {
  display: grid; place-items: center;
  border-color: rgba(255, 255, 255, .44);
  background: #2e2e2e;
}
.cs-drop__pic { width: 20px; height: 20px; color: rgba(255, 255, 255, .42); }
.cs-drop__up {
  position: absolute; right: -3px; bottom: -5px;
  width: 22px; height: 22px; border-radius: 50%;
  display: grid; place-items: center;
  background: #2e2e2e; border: 1.4px solid rgba(255, 255, 255, .44);
  color: var(--elx-text-50);
  transition: color var(--elx-dur), border-color var(--elx-dur), transform var(--elx-dur-med) var(--elx-ease-soft);
}
.cs-drop__up svg { width: 12px; height: 12px; }
.cs-drop:hover .cs-drop__card--back { transform: rotate(-22deg) translate(-14px, 0); }
.cs-drop:hover .cs-drop__card--mid  { transform: rotate(20deg) translate(14px, 0); }
.cs-drop:hover .cs-drop__up { transform: translateY(-2px); color: var(--elx-accent); border-color: var(--elx-accent); }
.cs-drop:hover .cs-drop__pic { color: rgba(255, 255, 255, .6); }
.cs-drop.is-over .cs-drop__card,
.cs-drop.is-over .cs-drop__up { border-color: var(--elx-accent); }

/* Tile on the left, the rules beside it — the constraints are the part people
   actually need to read, and stacking them under a full-width box buried them. */
.cs-droprow { display: flex; align-items: flex-start; gap: var(--elx-s-4); }
.cs-droprow__side { flex: 1; min-width: 0; display: grid; gap: 6px; align-content: start; }
.cs-droprow__side .cs-files { margin-top: 0; }
@media (max-width: 560px) { .cs-droprow { flex-direction: column; } }

.cs-files { display: grid; gap: 8px; margin-top: var(--elx-s-3); }
.cs-file {
  display: flex; align-items: center; gap: 11px;
  padding: 9px 11px; border-radius: var(--elx-r-sm);
  background: rgba(255, 255, 255, .04); border: 1px solid var(--elx-hairline);
  font-size: var(--elx-body-sm);
}
.cs-file__thumb { width: 34px; height: 34px; flex: none; border-radius: 6px; object-fit: cover; background: #0f0f0f; }
.cs-file__name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--elx-text-80); }
/* The label the customer can refer to in their description. */
.cs-file__ref { font-family: ui-monospace, Menlo, monospace; font-size: var(--elx-micro); color: var(--elx-accent); flex: none; }
.cs-file__size { font-size: var(--elx-micro); color: var(--elx-text-40); flex: none; }
.cs-file__x { background: none; border: 0; color: var(--elx-text-40); cursor: pointer; padding: 4px; line-height: 0; }
.cs-file__x:hover { color: var(--elx-danger); }
.cs-file__x svg { width: 14px; height: 14px; }
.cs-file--bad { border-color: var(--elx-danger); }
.cs-file--bad .cs-file__name { color: var(--elx-danger); }
.cs-file__bar { height: 2px; background: var(--elx-hairline); border-radius: 2px; overflow: hidden; margin-top: 6px; }
.cs-file__bar > i { display: block; height: 100%; width: 0; background: var(--elx-accent); transition: width .2s; }

/* Swatch picker for the new-site intake. */
.cs-swatches { display: flex; flex-wrap: wrap; gap: 10px; }
.cs-swatch { position: relative; }
.cs-swatch input { position: absolute; opacity: 0; width: 0; height: 0; }
.cs-swatch span {
  display: grid; place-items: center;
  width: 38px; height: 38px; border-radius: 50%;
  border: 2px solid transparent; cursor: pointer;
  transition: border-color var(--elx-dur), transform var(--elx-dur-fast);
}
.cs-swatch span::after { content: ''; width: 26px; height: 26px; border-radius: 50%; background: var(--sw); }
.cs-swatch input:checked + span { border-color: var(--elx-text); transform: scale(1.06); }
.cs-swatch input:focus-visible + span { outline: 2px solid var(--elx-accent); outline-offset: 2px; }

/* Priced add-on row. */
.cs-addon {
  display: flex; align-items: flex-start; gap: 11px;
  padding: 14px; border-radius: var(--elx-r-md);
  border: 1px solid var(--elx-hairline); background: rgba(255, 255, 255, .03);
  cursor: pointer;
}
.cs-addon:hover { border-color: var(--elx-hairline-2); }
/* Same treatment as .elx-check's radio — left native it rendered as a bare
   white dot, the only unstyled control on the form. */
.cs-addon input {
  appearance: none; margin: 2px 0 0; flex: none;
  width: 18px; height: 18px; cursor: pointer;
  background: rgba(255, 255, 255, .03);
  border: 1px solid var(--elx-hairline-2); border-radius: 50%;
  transition: background-color var(--elx-dur), border-color var(--elx-dur);
}
.cs-addon input:checked { background: var(--elx-accent); border-color: var(--elx-accent); box-shadow: inset 0 0 0 4px var(--elx-bg); }
.cs-addon input:focus-visible { outline: 2px solid var(--elx-accent); outline-offset: 2px; }
.cs-addon__title { font-size: var(--elx-body-sm); font-weight: var(--elx-w-medium); display: flex; gap: 8px; align-items: center; }
.cs-addon__price { color: var(--elx-accent); font-variant-numeric: tabular-nums; }
.cs-addon__body { font-size: var(--elx-caption); color: var(--elx-text-50); line-height: var(--elx-lh-loose); margin-top: 3px; }

/* Billing: included-items disclosure. */
.cs-included { display: grid; gap: 0; }
.cs-included__row { display: flex; align-items: flex-start; gap: 12px; padding: 13px 0; border-top: 1px solid var(--elx-hairline); }
.cs-included__row:first-child { border-top: 0; }
.cs-included__tick { width: 18px; height: 18px; flex: none; margin-top: 1px; color: var(--elx-accent); }
.cs-included__name { font-size: var(--elx-body-sm); color: var(--elx-text-80); }
.cs-included__note { font-size: var(--elx-caption); color: var(--elx-text-40); margin-top: 2px; }
.cs-included__row--off .cs-included__tick { color: var(--elx-text-40); }
.cs-included__row--off .cs-included__name { color: var(--elx-text-50); }

/* "Coming soon" marker on options that are visible but not yet built. */
.cs-soon {
  display: inline-block; margin-left: 8px; padding: 2px 8px;
  border-radius: var(--elx-r-pill); border: 1px solid var(--elx-hairline-2);
  font-size: var(--elx-micro); text-transform: uppercase; letter-spacing: .7px;
  color: var(--elx-text-40);
}
.elx-check.is-disabled { opacity: .55; cursor: not-allowed; }
.elx-check.is-disabled input { pointer-events: none; }

/* --------------------------------------------------------------------------
   09 · CUSTOM SELECT
   A native <select> renders its option list with the OS, not the page. On a
   dark form that means white-on-white on Windows and an unstyleable popup
   everywhere — there is no CSS that fixes it, which is why this exists.
   Behaviour lives in dashboard.js (`selectControl`); it keeps a hidden input
   so FormData still sees a plain field.
   -------------------------------------------------------------------------- */
.cs-sel { position: relative; }
.cs-sel__btn {
  width: 100%;
  display: flex; align-items: center; gap: var(--elx-s-3);
  padding: 13px 15px;
  background: rgba(255, 255, 255, .03);
  border: 1px solid var(--elx-hairline);
  border-radius: var(--elx-r-md);
  font-family: var(--elx-font); font-size: var(--elx-body-sm); font-weight: var(--elx-w-light);
  letter-spacing: var(--elx-ls); color: var(--elx-text);
  text-align: left; cursor: pointer;
  transition: border-color var(--elx-dur), background-color var(--elx-dur), box-shadow var(--elx-dur);
}
.cs-sel__btn:hover { border-color: var(--elx-hairline-2); }
.cs-sel__btn[aria-expanded="true"],
.cs-sel__btn:focus-visible {
  outline: 0; border-color: var(--elx-accent);
  background: rgba(255, 255, 255, .05);
  box-shadow: 0 0 0 3px rgba(110, 228, 255, .14);
}
.cs-sel__btn:disabled { opacity: .55; cursor: not-allowed; }
.cs-sel__value { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cs-sel__value--placeholder { color: var(--elx-text-40); }
.cs-sel__caret { width: 15px; height: 15px; flex: none; color: var(--elx-text-40); transition: transform var(--elx-dur-med) var(--elx-ease-soft); }
.cs-sel__btn[aria-expanded="true"] .cs-sel__caret { transform: rotate(180deg); }
.elx-field.is-invalid .cs-sel__btn { border-color: var(--elx-danger); }

.cs-sel__list {
  position: absolute; left: 0; right: 0; top: calc(100% + 6px); z-index: 80;
  max-height: 264px; overflow-y: auto; padding: 6px;
  background: var(--elx-raise);
  border: 1px solid var(--elx-border);
  border-radius: var(--elx-r-md);
  box-shadow: var(--elx-shadow-pop);
  scrollbar-width: thin; scrollbar-color: rgba(255,255,255,.18) transparent;
}
.cs-sel__list[hidden] { display: none; }
.cs-sel__list::-webkit-scrollbar { width: 9px; }
.cs-sel__list::-webkit-scrollbar-thumb { background: rgba(255,255,255,.16); border-radius: 9px; border: 3px solid transparent; background-clip: content-box; }
.cs-sel__opt {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 10px 11px; border-radius: var(--elx-r-sm);
  background: none; border: 0; text-align: left; cursor: pointer;
  font-family: var(--elx-font); font-size: var(--elx-body-sm); font-weight: var(--elx-w-light);
  letter-spacing: var(--elx-ls); color: var(--elx-text-80);
  transition: background-color var(--elx-dur), color var(--elx-dur);
}
.cs-sel__opt:hover, .cs-sel__opt.is-active { background: rgba(255, 255, 255, .07); color: var(--elx-text); }
.cs-sel__opt[aria-selected="true"] { color: var(--elx-accent); }
.cs-sel__opt-main { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cs-sel__opt-note { font-size: var(--elx-micro); color: var(--elx-text-40); flex: none; }
.cs-sel__opt[aria-selected="true"] .cs-sel__tick { opacity: 1; }
.cs-sel__tick { width: 14px; height: 14px; flex: none; opacity: 0; color: var(--elx-accent); }

/* --------------------------------------------------------------------------
   10 · LEAD INBOX CARD
   The state is the headline here, not a badge tucked in a corner: "unconfirmed"
   means enquiries are being dropped, which is the single most expensive thing
   that can quietly go wrong on one of these sites.
   -------------------------------------------------------------------------- */
.cs-lead { display: grid; gap: var(--elx-s-5); }
.cs-lead__top { display: flex; align-items: flex-start; gap: var(--elx-s-4); flex-wrap: wrap; }
.cs-lead__name { font-size: var(--elx-body-lg); font-weight: var(--elx-w-semi); }
.cs-lead__site { font-size: var(--elx-caption); color: var(--elx-text-40); margin-top: 3px; }
.cs-lead__state { margin-left: auto; display: flex; align-items: center; gap: 8px; }

.cs-lead__box {
  display: flex; align-items: center; gap: var(--elx-s-3);
  padding: 13px 15px; border-radius: var(--elx-r-md);
  background: rgba(255, 255, 255, .04); border: 1px solid var(--elx-hairline);
}
.cs-lead__box svg { width: 17px; height: 17px; flex: none; color: var(--elx-text-40); }
.cs-lead__addr { flex: 1; min-width: 0; font-size: var(--elx-body-sm); color: var(--elx-text); word-break: break-all; }
.cs-lead__addr--none { color: var(--elx-text-40); }
/* The action sits inside the field row. Below it, it left a band of empty
   card between the address and the button that carried no information. */
.cs-lead__go { flex: none; margin: -4px -6px -4px 0; }

.cs-lead__why {
  display: flex; align-items: flex-start; gap: var(--elx-s-3);
  padding: 13px 15px; border-radius: var(--elx-r-md);
  font-size: var(--elx-body-sm); line-height: var(--elx-lh-loose);
  background: rgba(251, 191, 36, .07); border: 1px solid rgba(251, 191, 36, .25);
  color: var(--elx-text-80);
}
.cs-lead__why svg { width: 16px; height: 16px; flex: none; margin-top: 3px; color: var(--elx-warning); }
.cs-lead__why--ok { background: rgba(74, 222, 128, .06); border-color: rgba(74, 222, 128, .22); }
.cs-lead__why--ok svg { color: var(--elx-success); }
.cs-lead__actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* --------------------------------------------------------------------------
   10b · INQUIRY LIST
   A table of one-line rows that open. A list of inquiries is scanned, not
   read — who, when, did it reach me — and everything else is one click away.
   Built on <details> so the disclosure works without our JavaScript and is
   keyboard-operable for free.
   -------------------------------------------------------------------------- */
.cs-inq { display: grid; }
.cs-inq__item { border-bottom: 1px solid var(--elx-hairline); }
.cs-inq__item:last-child { border-bottom: 0; }

.cs-inq__row {
  display: flex; align-items: center; gap: var(--elx-s-4);
  padding: 14px var(--elx-s-6);
  cursor: pointer; list-style: none;
  font-size: var(--elx-body-sm);
  transition: background-color var(--elx-dur);
}
.cs-inq__row::-webkit-details-marker { display: none; }
.cs-inq__row:hover { background: rgba(255, 255, 255, .035); }
.cs-inq__item[open] .cs-inq__row { background: rgba(255, 255, 255, .05); }

.cs-inq__caret {
  width: 14px; height: 14px; flex: none; color: var(--elx-text-40);
  transition: transform var(--elx-dur-med) var(--elx-ease-soft);
}
.cs-inq__item[open] .cs-inq__caret { transform: rotate(180deg); }

.cs-inq__who { font-weight: var(--elx-w-medium); color: var(--elx-text); flex: none; }
.cs-inq__from {
  flex: 1; min-width: 0; color: var(--elx-text-50);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cs-inq__when { flex: none; font-size: var(--elx-caption); color: var(--elx-text-40); }
@media (max-width: 640px) {
  .cs-inq__row { flex-wrap: wrap; gap: var(--elx-s-2) var(--elx-s-3); padding: 12px var(--elx-s-5); }
  .cs-inq__from { flex-basis: 100%; order: 3; }
}

.cs-inq__body {
  padding: 2px var(--elx-s-6) var(--elx-s-6);
  display: grid; gap: 2px;
  background: rgba(255, 255, 255, .02);
}
/* Label / value pairs, because an unlabelled paragraph could be anything. */
.cs-inq__pair {
  display: grid; grid-template-columns: 132px 1fr; gap: var(--elx-s-4);
  padding: 9px 0; border-top: 1px solid var(--elx-hairline);
  font-size: var(--elx-body-sm);
}
.cs-inq__pair:first-child { border-top: 0; }
.cs-inq__key { color: var(--elx-text-40); font-size: var(--elx-caption); }
.cs-inq__val { color: var(--elx-text-80); min-width: 0; word-break: break-word; }
.cs-inq__val--block { white-space: pre-wrap; line-height: var(--elx-lh-loose); }
.cs-inq__link { color: var(--elx-accent); text-decoration: underline; text-underline-offset: 3px; }
.cs-inq__link:hover { color: var(--elx-accent-hover); }
@media (max-width: 560px) { .cs-inq__pair { grid-template-columns: 1fr; gap: 2px; } }

.cs-inq__held {
  display: flex; align-items: flex-start; gap: var(--elx-s-3);
  margin-top: var(--elx-s-4); padding: 12px 14px; border-radius: var(--elx-r-md);
  background: rgba(251, 191, 36, .07); border: 1px solid rgba(251, 191, 36, .22);
  font-size: var(--elx-caption); line-height: var(--elx-lh-loose); color: var(--elx-text-80);
}
.cs-inq__held svg { width: 15px; height: 15px; flex: none; margin-top: 2px; color: var(--elx-warning); }
.cs-inq__actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: var(--elx-s-5); }

/* --------------------------------------------------------------------------
   11 · CODE ENTRY
   Six boxes, one digit each, paste-aware. A single text input works, but it
   gives no sense of how long the code is and no feedback per keystroke.
   -------------------------------------------------------------------------- */
.cs-code { display: flex; gap: 9px; justify-content: center; }
.cs-code input {
  width: 46px; height: 56px; flex: none;
  text-align: center;
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: 22px; font-weight: var(--elx-w-semi);
  color: var(--elx-text);
  background: rgba(255, 255, 255, .04);
  border: 1px solid var(--elx-hairline-2);
  border-radius: var(--elx-r-md);
  transition: border-color var(--elx-dur), box-shadow var(--elx-dur), background-color var(--elx-dur);
}
.cs-code input:focus {
  outline: 0; border-color: var(--elx-accent);
  background: rgba(255, 255, 255, .06);
  box-shadow: 0 0 0 3px rgba(110, 228, 255, .14);
}
.cs-code input:not(:placeholder-shown) { border-color: var(--elx-accent); }
.cs-code.is-bad input { border-color: var(--elx-danger); }
@media (max-width: 420px) { .cs-code input { width: 40px; height: 50px; font-size: 19px; } }

/* --------------------------------------------------------------------------
   12 · BILLING
   One card that answers "what am I paying, and when" before anything else.
   -------------------------------------------------------------------------- */
.cs-bill { display: grid; gap: var(--elx-s-5); }

.cs-bill__hero { display: flex; align-items: flex-start; gap: var(--elx-s-6); flex-wrap: wrap; }
.cs-bill__amount { font-size: 44px; font-weight: var(--elx-w-semi); letter-spacing: -2px; line-height: 1; }
.cs-bill__amount small { font-size: 15px; font-weight: var(--elx-w-medium); letter-spacing: var(--elx-ls); color: var(--elx-text-50); }
.cs-bill__caption { font-size: var(--elx-body-sm); color: var(--elx-text-50); margin-top: 8px; line-height: var(--elx-lh-loose); }
.cs-bill__when { margin-left: auto; text-align: right; }
.cs-bill__when b { display: block; font-size: var(--elx-body-lg); font-weight: var(--elx-w-semi); }
.cs-bill__when span { font-size: var(--elx-caption); color: var(--elx-text-40); }
@media (max-width: 620px) { .cs-bill__when { margin-left: 0; text-align: left; } }

.cs-bill__row {
  display: flex; align-items: center; gap: var(--elx-s-4);
  padding: 14px 0; border-top: 1px solid var(--elx-hairline);
}
.cs-bill__row-name { flex: 1; min-width: 0; font-size: var(--elx-body-sm); }
.cs-bill__row-name span { display: block; font-size: var(--elx-caption); color: var(--elx-text-40); margin-top: 2px; }
.cs-bill__row-price { font-size: var(--elx-body-sm); color: var(--elx-text-80); font-variant-numeric: tabular-nums; flex: none; }

/* A button that reads as inline body text — used where prose needs to trigger
   an in-app action rather than navigate away. */
.cs-linkbtn {
  background: none; border: 0; padding: 0;
  font: inherit; letter-spacing: inherit;
  color: var(--elx-accent); text-decoration: underline; text-underline-offset: 3px;
  cursor: pointer;
}
.cs-linkbtn:hover { color: var(--elx-accent-hover); }
