/* ==========================================================================
   nimbuz.app — portal
   Loads AFTER tokens.css and main.css and reuses their primitives
   (.btn, .card, .wrap, .eyebrow, colour tokens). Only portal-specific
   chrome lives here.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Auth pages (login / signup)
   -------------------------------------------------------------------------- */
/* Both halves are flex columns with identical vertical padding, an identical
   top row, and a content block hung from that row by the same offset. Using
   one mechanism on both sides is the whole point: when the left centred itself
   with `auto` margins and the right with `place-items: center`, the two never
   landed on the same line, and the taller signup form made the gap obvious. */
.auth-page {
  /* One shared measure, so the pitch text and the form share a column width. */
  --auth-measure: 420px;
  min-height: 100vh;
  display: grid;
  grid-template-columns: 1fr 1fr;
  background: var(--bg-dark);
}

.auth-aside,
.auth-main {
  display: flex;
  flex-direction: column;
  padding: 48px 52px;
  min-width: 0;
}

.auth-aside {
  background:
    radial-gradient(700px 460px at 20% 0%, var(--accent-glow), transparent 62%),
    linear-gradient(180deg, var(--bg-dark-2), var(--bg-deep));
  border-right: 1px solid var(--line);
  align-items: flex-end;   /* pulls the measure toward the centre gutter */
}
.auth-main { align-items: flex-start; }

/* The top row is a fixed height on both sides so the brand and the theme
   toggle sit on one line across the fold. 38px is the toggle's height. */
.auth-top {
  width: 100%;
  max-width: var(--auth-measure);
  min-height: 38px;
  display: flex;
  align-items: center;
  gap: 16px;
  flex: none;
}
.auth-main .auth-top { justify-content: flex-end; }
/* Standby for narrow screens, where the aside (and its brand) is dropped. */
.auth-top-brand { display: none; }

/* Top-aligned, not centred. Centring put the *midpoints* of the two blocks on
   one line, which is not what the eye reads — the card is far taller than the
   pitch, so its heading floated well above "Managed apps…". Giving both the
   same offset below the top row puts the two first lines on the same line,
   which is the alignment that actually reads as aligned. */
.auth-pitch,
.auth-card {
  width: 100%;
  max-width: var(--auth-measure);
  margin: clamp(28px, 9vh, 92px) 0 auto;
}

/* Both headings share a size and line-height so their first baselines match. */
.auth-pitch h2,
.auth-card h1 { font-size: 30px; line-height: 1.28; }
.auth-pitch h2 { margin: 0 0 10px; }
.auth-pitch > p { color: var(--ink-soft); margin: 0; }
.auth-points {
  list-style: none;
  padding: 0;
  margin: 26px 0 0;
  display: grid;
  gap: 14px;
}
.auth-points li {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  color: var(--ink-soft);
  font-size: 15px;
}
.auth-points i { color: var(--accent); margin-top: 4px; }

.auth-card h1 { margin: 0 0 8px; }
.auth-card .sub { color: var(--ink-soft); margin-bottom: 30px; }
.auth-alt { margin-top: 26px; font-size: 15px; color: var(--ink-soft); text-align: center; }
.auth-alt a { color: var(--accent-2); font-weight: 600; }

/* Post-signup "check your email" panel. Replaces the form in place — see
   showVerifyNotice() in auth.js. */
.verify-note {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--fill-card);
  padding: 24px;
}
.verify-ico {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  margin-bottom: 14px;
  font-size: 20px;
  color: var(--accent-2);
  background: var(--fill-raised);
  border: 1px solid var(--line);
}
.verify-note p { margin: 0 0 10px; color: var(--ink-soft); }
.verify-note b { color: var(--ink); word-break: break-all; }
.verify-note .hint { font-size: 14px; }
.verify-note .btn { margin-top: 18px; }

/* --------------------------------------------------------------------------
   2. Forms
   -------------------------------------------------------------------------- */
.field { margin-bottom: 18px; }
.field > label {
  display: block;
  font-size: 13.5px;
  font-weight: 600;
  margin-bottom: 7px;
  color: var(--ink);
}
.field .hint { font-size: 12.5px; color: var(--ink-faint); margin: 6px 0 0; }

/* A label with something on the far right of the same line — "Forgot
   password?" today. The label keeps its own bottom margin; the row zeroes it
   so the two can't fight over the gap. */
.label-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 7px;
}
.label-row > label { margin-bottom: 0; }
.label-link { font-size: 13px; color: var(--accent-2); font-weight: 600; }
.label-link:hover { text-decoration: underline; }

.input, .select {
  width: 100%;
  padding: 12px 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  background: var(--fill-input);
  color: var(--ink);
  font: inherit;
  font-size: 15px;
  transition: border-color .2s var(--ease), background .2s var(--ease);
}
.input::placeholder { color: var(--ink-faint); }
.input:focus, .select:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--fill-raised);
}
.input[aria-invalid="true"] { border-color: var(--red); }
.select { appearance: none; cursor: pointer; }
.select option { background: var(--bg-dark-2); color: var(--ink); }

.field-err { color: var(--red); font-size: 12.5px; margin: 6px 0 0; min-height: 0; }

.pw-wrap { position: relative; }
.pw-wrap .input { padding-right: 46px; }
.pw-toggle {
  position: absolute;
  right: 6px; top: 50%;
  transform: translateY(-50%);
  background: none;
  border: 0;
  color: var(--ink-faint);
  padding: 8px 10px;
  border-radius: 8px;
}
.pw-toggle:hover { color: var(--accent-2); }

.seg-radio { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.seg-radio label {
  border: 1px solid var(--line);
  background: var(--fill-card);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  cursor: pointer;
  font-size: 14.5px;
  display: flex;
  align-items: center;
  gap: 10px;
  transition: border-color .2s var(--ease), background .2s var(--ease);
}
.seg-radio input { accent-color: var(--accent); margin: 0; }
.seg-radio label:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-tint);
}

/* Password strength meter */
.pw-bars { display: flex; gap: 5px; margin-top: 9px; }
.pw-bars i {
  height: 4px;
  flex: 1;
  border-radius: var(--pill);
  background: var(--fill-strong);
  transition: background .25s var(--ease);
}
.pw-bars i.on-weak   { background: var(--red); }
.pw-bars i.on-medium { background: var(--amber); }
.pw-bars i.on-strong { background: var(--green); }

/* Alerts */
.alert {
  display: flex;
  gap: 11px;
  align-items: flex-start;
  padding: 13px 15px;
  border-radius: var(--radius-sm);
  font-size: 14.5px;
  margin-bottom: 20px;
  border: 1px solid;
  line-height: 1.5;
}
.alert i { margin-top: 3px; flex: none; }
.alert.error   { background: var(--red-tint);   border-color: var(--red-edge);   color: var(--red-ink); }
.alert.success { background: var(--green-tint); border-color: var(--green-edge); color: var(--green-ink); }
.alert.info    { background: var(--amber-tint); border-color: var(--amber-edge); color: var(--amber-ink); }
/* Same palette as info by design — the distinction is the icon and the
   wording, not the colour. Amber twice would be noise. */
.alert.warn    { background: var(--amber-tint); border-color: var(--amber-edge); color: var(--amber-ink); }
.alert[hidden] { display: none; }

/* Busy state for submit buttons */
.btn[aria-busy="true"] { pointer-events: none; opacity: .72; }
.btn[aria-busy="true"] .fa-spinner { animation: spin .8s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* --------------------------------------------------------------------------
   3. Portal shell
   -------------------------------------------------------------------------- */
.shell {
  display: grid;
  grid-template-columns: 244px 1fr;
  min-height: 100vh;
}

.side {
  border-right: 1px solid var(--line);
  background: var(--bg-dark-2);
  padding: 20px 16px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
}
.side .brand { padding: 6px 8px 20px; }
.side-label {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-faint);
  padding: 16px 10px 6px;
}
.side a.nav-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  color: var(--ink-soft);
  font-size: 15px;
  transition: background .2s var(--ease), color .2s var(--ease);
}
.side a.nav-item i { width: 18px; text-align: center; font-size: 15px; }
.side a.nav-item:hover { background: var(--fill-hover); color: var(--ink); }
.side a.nav-item.on {
  background: var(--accent-tint);
  color: var(--accent-2);
  font-weight: 600;
}
.side-foot { margin-top: auto; padding-top: 16px; border-top: 1px solid var(--line-faint); }

.topbar {
  height: 66px;
  border-bottom: 1px solid var(--line);
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 0 28px;
  position: sticky;
  top: 0;
  z-index: 30;
  background: var(--header-bg);
  backdrop-filter: blur(12px);
}
/* .topbar h1 removed with the duplicated page title. */
.topbar .spacer { margin-left: auto; }

.who {
  display: flex;
  align-items: center;
  gap: 11px;
  font-size: 14px;
}
.who .avatar {
  width: 34px; height: 34px;
  border-radius: 50%;
  display: grid; place-items: center;
  background: linear-gradient(135deg, var(--accent), var(--accent-deep));
  color: var(--on-accent);
  font-weight: 700;
  font-size: 14px;
  flex: none;
}
.who .meta { line-height: 1.25; }
.who .meta b { display: block; font-weight: 600; }
.who .meta span { color: var(--ink-faint); font-size: 12.5px; font-family: var(--mono); }

.view { padding: 32px 28px 64px; max-width: 1220px; }
.view-head {
  display: flex;
  align-items: flex-end;
  gap: 18px;
  flex-wrap: wrap;
  margin-bottom: 26px;
}
/* The view's heading is the page's <h1> — the topbar no longer carries one, so
   this is the only top-level heading on the page and the document outline
   depends on it. h2 is kept for any panel that still uses the same block. */
.view-head h1, .view-head h2 { margin: 0 0 4px; font-size: 26px; line-height: 1.25; }
.view-head p { margin: 0; color: var(--ink-soft); font-size: 15px; }
.view-head .spacer { margin-left: auto; }

.burger { display: none; background: none; border: 0; color: var(--ink); font-size: 19px; }

/* --------------------------------------------------------------------------
   4. Data display
   -------------------------------------------------------------------------- */
.stat-cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 30px; }
.stat-card {
  background: var(--fill-card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 18px 20px;
}
.stat-card .lbl {
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  display: block;
  margin-bottom: 8px;
}
.stat-card .big { font-family: var(--disp); font-size: 27px; font-weight: 700; line-height: 1; }
.stat-card .sub { font-size: 13px; color: var(--ink-faint); margin: 6px 0 0; }

.panel {
  background: var(--fill-card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.panel-head {
  padding: 16px 20px;
  border-bottom: 1px solid var(--line);
  display: flex;
  align-items: center;
  gap: 14px;
}
.panel-head h3 { margin: 0; font-size: 16px; }
.panel-head .spacer { margin-left: auto; }
.panel-head .panel-note {
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: .06em;
  color: var(--ink-soft);
}
.panel-body-pad { padding: 20px; }

/* Sits below a table, so no top border of its own — the last row supplies it. */
.panel-foot {
  padding: 14px 20px;
  display: flex;
  justify-content: center;
}

.tbl { width: 100%; border-collapse: collapse; font-size: 14.5px; }
.tbl th, .tbl td { padding: 14px 20px; text-align: left; border-bottom: 1px solid var(--line-faint); }
.tbl thead th {
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: 400;
}
.tbl tbody tr:last-child td { border-bottom: 0; }
.tbl tbody tr:hover { background: var(--fill-hover); }
.tbl .num { font-family: var(--mono); font-size: 13.5px; }

.inst-name { display: flex; align-items: center; gap: 12px; }
.inst-ico {
  width: 34px; height: 34px;
  border-radius: 10px;
  display: grid; place-items: center;
  background: var(--accent-tint);
  color: var(--accent-2);
  flex: none;
}
.inst-name b { display: block; font-weight: 600; }
.inst-name span { font-family: var(--mono); font-size: 12px; color: var(--ink-faint); }

.pill-status {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 11px;
  border-radius: var(--pill);
  font-size: 12.5px;
  font-weight: 600;
  border: 1px solid;
}
/* Four tones, not one class per platform status. The stream can report a
   status we've never seen, and a rule per known value would leave those
   unstyled; NimbuzData maps whatever arrives onto one of these. */
.pill-status.ok   { color: var(--green);     border-color: var(--green-edge); background: var(--green-tint); }
.pill-status.warn { color: var(--amber);     border-color: var(--amber-edge); background: var(--amber-tint); }
.pill-status.bad  { color: var(--red);       border-color: var(--red-edge);   background: var(--red-tint); }
.pill-status.off  { color: var(--ink-faint); border-color: var(--line);       background: var(--fill-card); }

/* A second line under a table cell — the vCPU/RAM beneath a size name. */
.sub-cell {
  display: block;
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--ink-faint);
  margin-top: 3px;
}

.row-actions { display: flex; gap: 8px; justify-content: flex-end; }
.icon-btn {
  width: 32px; height: 32px;
  border-radius: 9px;
  border: 1px solid var(--line);
  background: var(--fill-card);
  color: var(--ink-soft);
  display: grid; place-items: center;
  transition: border-color .2s var(--ease), color .2s var(--ease);
}
.icon-btn:hover { border-color: var(--accent); color: var(--accent-2); }

/* An action that exists in the design but has no endpoint behind it yet.
   Visibly present, visibly unavailable, and it explains itself on hover
   rather than looking like something that failed. */
.icon-btn.is-off {
  opacity: .4;
  cursor: not-allowed;
  border-style: dashed;
}
.icon-btn.is-off:hover { border-color: var(--line); color: var(--ink-soft); }

/* The view/download pair in an invoice row. Right-aligned on the wide table
   and left-aligned once the table collapses to stacked cards, where the cell
   has a label sitting beside it. */
.row-tools { display: flex; gap: 6px; justify-content: flex-end; }
.row-tools .fa-spinner { animation: spin .8s linear infinite; }
.row-tools .icon-btn:disabled { opacity: .55; cursor: default; }
.row-tools .icon-btn:disabled:hover { border-color: var(--line); color: var(--ink-soft); }

.empty { text-align: center; padding: 60px 24px; }
.empty i.big-ico { font-size: 34px; color: var(--accent); opacity: .6; margin-bottom: 16px; display: block; }
.empty h3 { margin-bottom: 8px; }
.empty p { color: var(--ink-soft); max-width: 44ch; margin: 0 auto 22px; }

/* The opposite of the mock flag: this data is arriving live, right now. */
.live-flag {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--green);
  border: 1px solid var(--green-edge);
  background: var(--green-tint);
  border-radius: var(--pill);
  padding: 4px 10px;
}

/* Mock-data marker — remove these when the real endpoints land */
.mock-flag {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--amber);
  border: 1px dashed var(--amber-edge);
  border-radius: var(--pill);
  padding: 4px 10px;
}

/* --------------------------------------------------------------------------
   5. Deploy wizard
   -------------------------------------------------------------------------- */
.steps-bar { display: flex; gap: 10px; margin-bottom: 30px; flex-wrap: wrap; }
.step-chip {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 9px 15px;
  border-radius: var(--pill);
  border: 1px solid var(--line);
  font-size: 13.5px;
  color: var(--ink-faint);
  background: var(--fill-card);
}
.step-chip b {
  width: 20px; height: 20px;
  border-radius: 50%;
  display: grid; place-items: center;
  font-family: var(--mono);
  font-size: 11px;
  background: var(--fill-strong);
  color: var(--ink-soft);
}
.step-chip.on { border-color: var(--accent); color: var(--ink); }
.step-chip.on b { background: var(--accent); color: var(--on-accent); }
.step-chip.done { color: var(--green); border-color: var(--green-edge); }
.step-chip.done b { background: var(--green); color: var(--on-green); }

.pick-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
.pick {
  text-align: left;
  border: 1px solid var(--line);
  background: var(--fill-card);
  border-radius: var(--radius);
  padding: 18px;
  color: var(--ink);
  transition: border-color .2s var(--ease), background .2s var(--ease), transform .2s var(--ease);
}
.pick:hover:not(:disabled) { border-color: var(--accent); transform: translateY(-2px); }
.pick.on { border-color: var(--accent); background: var(--accent-tint); }
.pick:disabled { opacity: .45; cursor: not-allowed; }
.pick .t { font-weight: 600; display: flex; align-items: center; gap: 10px; margin-bottom: 6px; }
.pick .t i { color: var(--accent-2); }
.pick .d { font-size: 13.5px; color: var(--ink-soft); }
.pick .p { font-family: var(--mono); font-size: 13px; color: var(--accent-2); margin-top: 10px; }

.summary-row {
  display: flex;
  justify-content: space-between;
  gap: 14px;
  padding: 12px 0;
  border-bottom: 1px solid var(--line-faint);
  font-size: 14.5px;
}
.summary-row:last-of-type { border-bottom: 0; }
.summary-row .k { color: var(--ink-soft); white-space: nowrap; }
.summary-row .v { font-family: var(--mono); font-size: 13.5px; }

/* Mono is right for identifiers — an id, a GSTIN, an amount, things people
   compare character by character. It is wrong for prose: a company name or a
   postal address set in mono reads like a config file. `.text` opts a value
   back into the body face, and lets a long address wrap and align right
   instead of running off the row. */
.summary-row .v.text {
  font-family: inherit;
  font-size: 14.5px;
  text-align: right;
}

/* A value with a copy button beside it. Baseline-aligned so the button rides
   with the text rather than the row's full height. */
.summary-row .v.with-copy {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.copy-btn {
  width: 26px;
  height: 26px;
  font-size: 11px;
  flex: none;
}
.copy-btn.done { border-color: var(--green); color: var(--green); }

.summary-total { font-size: 17px; font-weight: 700; padding-top: 16px; }

/* The "nothing saved yet" state of a panel that will later hold a record.
   Lighter than .empty, which owns a whole panel body on its own. */
.blank-state { text-align: center; padding: 8px 0 4px; }
.blank-state .big-ico { font-size: 26px; color: var(--ink-faint); margin-bottom: 12px; }
.blank-state p { color: var(--ink-soft); margin: 0 auto 18px; max-width: 42ch; font-size: 14px; }

/* Row of buttons closing an edit form: primary first, then the way out. */
.form-actions { display: flex; gap: 12px; align-items: center; margin-top: 4px; }
.form-actions .spacer { margin-left: auto; }
.saved-at { color: var(--ink-faint); font-size: 12.5px; margin: 14px 0 0; }

.wizard-nav { display: flex; gap: 12px; margin-top: 28px; }
.wizard-nav .spacer { margin-left: auto; }

/* --------------------------------------------------------------------------
   6. Responsive
   -------------------------------------------------------------------------- */
@media (max-width: 1024px) {
  .stat-cards { grid-template-columns: repeat(2, 1fr); }
  .pick-grid { grid-template-columns: repeat(2, 1fr); }
  /* One column: the aside goes, so the card's own top row carries the brand
     and the card centres itself instead of hugging the (now absent) gutter. */
  .auth-page { grid-template-columns: 1fr; }
  .auth-aside { display: none; }
  .auth-main { align-items: center; padding: 32px 24px 48px; }
  .auth-main .auth-top { justify-content: space-between; }
  .auth-top-brand { display: inline-flex; }
}

@media (max-width: 820px) {
  .shell { grid-template-columns: 1fr; }
  .side {
    position: fixed;
    inset: 0 auto 0 0;
    width: 244px;
    z-index: 80;
    transform: translateX(-100%);
    transition: transform .28s var(--ease);
  }
  .side.open { transform: none; }
  .burger { display: block; }
  .view { padding: 24px 18px 56px; }
  .topbar { padding: 0 18px; }
  .who .meta { display: none; }
}

@media (max-width: 620px) {
  .stat-cards, .pick-grid { grid-template-columns: 1fr; }
  .tbl thead { display: none; }
  .tbl tbody tr {
    display: block;
    border-bottom: 1px solid var(--line);
    padding: 8px 0;
  }
  .tbl td {
    display: flex;
    justify-content: space-between;
    gap: 16px;
    border: 0;
    padding: 8px 18px;
  }
  .tbl td::before {
    content: attr(data-th);
    font-family: var(--mono);
    font-size: 11px;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--ink-faint);
    flex: none;
  }
  .row-actions { justify-content: flex-start; }
  .row-tools { justify-content: flex-start; }
}

/* --- Modal ---------------------------------------------------------------
   Appended to <body>, not to #view: the applications table repaints on every
   stream tick and would take a dialog nested inside it down with it. */
.modal-scrim {
  position: fixed;
  inset: 0;
  z-index: 90;
  background: var(--scrim);
  backdrop-filter: blur(6px);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 40px 20px;
  overflow-y: auto;
  animation: modal-in .16s var(--ease);
}
body.modal-open { overflow: hidden; }

.modal {
  width: min(860px, 100%);
  background: var(--bg-dark);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}
.modal-head {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 20px 22px;
  border-bottom: 1px solid var(--line);
  background: var(--fill-card);
}
.modal-head h3 { margin: 0; font-size: 18px; }
.modal-head p  { margin: 3px 0 0; font-size: 13px; color: var(--ink-faint); }
.modal-head .spacer { margin-left: auto; }
.modal-body { padding: 22px; }

@keyframes modal-in { from { opacity: 0; } to { opacity: 1; } }

/* --- The invoice PDF dialog ---------------------------------------------- */
/* Holds the spinner and any error at the same height the document will take,
   so the dialog doesn't jump when the frame arrives. */
.pdf-stage { display: grid; place-items: center; min-height: 340px; }

.pdf-frame {
  display: block;
  width: 100%;
  height: min(68vh, 860px);
  min-height: 340px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--fill-card);
}

.pdf-foot {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 14px;
  font-size: 13px;
  color: var(--ink-faint);
}
.pdf-foot .spacer { margin-left: auto; }

@media (max-width: 720px) {
  .pdf-frame { height: 60vh; }
  .pdf-foot { flex-wrap: wrap; }
}

/* --- Range chips --------------------------------------------------------- */
.range-chips { display: flex; gap: 8px; margin-bottom: 20px; flex-wrap: wrap; }
.range-chips .chip {
  font-family: var(--mono);
  font-size: 12px;
  padding: 7px 14px;
  border-radius: var(--pill);
  border: 1px solid var(--line);
  background: var(--fill-card);
  color: var(--ink-soft);
  cursor: pointer;
  transition: border-color .15s var(--ease), color .15s var(--ease);
}
.range-chips .chip:hover { border-color: var(--line-strong); color: var(--ink); }
.range-chips .chip.on {
  border-color: var(--accent);
  color: var(--accent-2);
  background: var(--accent-tint);
}

/* --- Metric tiles -------------------------------------------------------- */
.metric-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
  margin-bottom: 24px;
}
.metric-tile {
  background: var(--fill-card);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 15px 16px;
}
.metric-tile .lbl {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  display: block;
  margin-bottom: 7px;
}
.metric-tile .big {
  font-family: var(--disp);
  font-size: 23px;
  font-weight: 700;
  line-height: 1;
}
.metric-tile .big .unit {
  font-family: var(--body);
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-faint);
  margin-left: 5px;
}
.metric-tile .sub { font-size: 12px; color: var(--ink-faint); margin: 6px 0 0; }

/* --- Charts --------------------------------------------------------------
   Hand-rolled SVG, no charting library — see the note in portal.js. The
   viewBox is stretched to the container, so every stroke needs
   vector-effect to stay the width it says it is. */
.chart-card {
  background: var(--fill-card);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 15px 16px 10px;
  margin-bottom: 14px;
}
.chart-head { display: flex; align-items: baseline; gap: 12px; margin-bottom: 10px; }
.chart-head h4 { margin: 0; font-size: 14px; }
.chart-head .spacer { margin-left: auto; }
.chart-note { font-size: 12px; color: var(--ink-faint); }

.chart { position: relative; }
.chart svg {
  display: block;
  width: 100%;
  height: 140px;
  overflow: visible;
}
.chart .line {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
.chart .fill { fill: var(--accent-tint); stroke: none; }
.chart .grid {
  stroke: var(--line);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
/* Generously sized and invisible: they exist to give the browser something to
   hover for the <title> tooltip, not to be seen. */
.chart .dot { fill: transparent; }
.chart .dot:hover { fill: var(--accent); }
.chart-max {
  position: absolute;
  top: -4px;
  left: 0;
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--ink-faint);
  pointer-events: none;
}
.chart-empty {
  font-size: 13px;
  color: var(--ink-faint);
  padding: 40px 0;
  text-align: center;
}
.metrics-loading { padding: 30px 0; }
.metrics-foot {
  font-size: 12px;
  color: var(--ink-faint);
  margin: 14px 0 0;
  text-align: center;
}

@media (max-width: 720px) {
  .metric-grid { grid-template-columns: repeat(2, 1fr); }
  .modal-scrim { padding: 0; }
  .modal { border-radius: 0; min-height: 100%; }
}
@media (max-width: 460px) {
  .metric-grid { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   Destructive actions
   --------------------------------------------------------------------------
   Red only where something is irreversible. The trash icon in a row is red on
   hover rather than at rest, so a table of eight applications isn't eight red
   dots demanding attention; the confirm button is red outright, because by
   then red is the whole point.
   -------------------------------------------------------------------------- */
.icon-btn.danger:hover {
  border-color: var(--red-edge);
  background: var(--red-tint);
  color: var(--red);
}

.btn-danger {
  background: var(--red);
  border: 1px solid var(--red);
  color: var(--on-red);
}
.btn-danger:hover:not(:disabled) { filter: brightness(1.06); }
.btn-danger:disabled {
  opacity: .45;
  cursor: not-allowed;
  filter: none;
}

.confirm-ask {
  font-size: 14px;
  color: var(--ink-soft);
  margin: 0 0 8px;
}
.confirm-ask b { color: var(--ink); font-family: var(--mono); font-size: 13px; }

.confirm-err {
  margin-top: 12px;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--red-ink);
  background: var(--red-tint);
  border: 1px solid var(--red-edge);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
}
.confirm-err[hidden] { display: none; }

.confirm-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 20px;
}
.confirm-actions .fa-spinner { animation: spin .8s linear infinite; }

@media (max-width: 460px) {
  .confirm-actions { flex-direction: column-reverse; }
  .confirm-actions .btn { width: 100%; justify-content: center; }
}

/* --------------------------------------------------------------------------
   Help

   The accordion itself is main.css's .faq, which the dashboard already loads.
   Only the scale changes: that one is sized for a marketing section with air
   around it, and inside a panel it reads as shouting.
   -------------------------------------------------------------------------- */
.faq-compact { max-width: none; }
.faq-compact summary { font-size: 15px; padding: 15px 34px 15px 0; }
.faq-compact summary::after { font-size: 20px; }
.faq-compact details p { font-size: 14.5px; }
.faq-compact details:last-child { border-bottom: 0; }

.help-lead { color: var(--ink-soft); font-size: 14.5px; margin: 0 0 18px; max-width: 62ch; }

/* --------------------------------------------------------------------------
   Session panel
   -------------------------------------------------------------------------- */
/* The countdown. Amber inside the last hour, red once it has run out — the
   colour is the whole point, since "expires in 43 minutes" and "expired 43
   minutes ago" are one word apart and read almost identically in a hurry. */
.ttl-soon { color: var(--amber-ink); }
.ttl-gone { color: var(--red-ink); }

.pw-form { margin-top: 4px; }
.pw-form .field:last-of-type { margin-bottom: 18px; }
