/* ═══════════════════════════════════════════════════════════════════════════
   04-portal.css — client-portal only.

   Components that exist only on the buyer side: the licence cards and their
   seat meters, licensed variants and modules, the offline trust-bundle import,
   the activation-code entry.

   Also holds the handful of shared-looking rules the two portals have not
   reconciled yet (.mono, .kv dd, the base input rules, the seat-outcome
   badges). Those are Phase 7 / Phase 6 work; keeping them here is what lets
   03-base.css stay byte-identical with licence-server-portal today.

   Loads LAST, so nothing here may set a property that an @media block in
   03-base.css sets on the same selector — it would win and kill the
   responsive override. Verified with css-verify.js --hazards.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Pending reconciliation with licence-server-portal ───────────────────── */
.kv-spaced{margin-top:var(--space-4)}
.is-frozen{color:var(--red)}

/* ── Sortable table headers ──────────────────────────────────────────────────
   Ported verbatim from licence-server-portal's 04-portal.css, where these have
   lived since that portal got sortable tables. Without the cursor and hover a
   sortable header is clickable with nothing to say so, which is the same
   affordance gap a plain-styled button has.

   The hover mixes against --bg-surface2 rather than a literal, so it picks up
   this portal's own accent (blue) instead of the server's teal and stays
   legible in both themes. */
.table thead th.sortable{cursor:pointer;user-select:none}
.table thead th.sortable:hover{background:color-mix(in srgb, var(--bg-surface2) 88%, var(--accent))}

/* ── Forms ───────────────────────────────────────────────────────────────── */
label.btn{display:inline-block}
.btn-row{display:flex;gap:var(--space-3);flex-wrap:wrap;margin-top:var(--space-4);align-items:center}

/* ── Badges: seat grant/reclaim outcomes ─────────────────────────────────── */
.badge-granted,.badge-yes{background:var(--green-light);color:var(--green)}
.badge-reattached{background:var(--orange-light);color:var(--orange)}
.badge-refused,.badge-no{background:var(--red-light);color:var(--red)}
.badge-released,.badge-reclaimed{background:var(--bg-surface2);color:var(--text-muted)}
.stale-flag{color:var(--orange);font-size:var(--text-xs);font-weight:700}

/* ── Which call was refused ──────────────────────────────────────────────────
   Sits ahead of the refusal code in the audit table's Reason column. Deliberately
   quieter than a .badge: the code is the headline, the phase is the qualifier
   that stops a heartbeat refusal from reading as a failed install. */
.phase-chip{display:inline-block;background:var(--bg-surface2);color:var(--text-muted);
  border:1px solid var(--border);border-radius:var(--radius-pill);
  padding:0 var(--space-2);font-size:var(--text-2xs);font-weight:700;
  text-transform:uppercase;letter-spacing:.5px;vertical-align:middle;white-space:nowrap}

/* ── Expandable seat rows ────────────────────────────────────────────────────
   The seats table used to carry nine columns, three of them a 36-character
   machine GUID, a licence ID and a build fingerprint. Their combined natural
   width always exceeded the table's min-width, so it scrolled sideways at
   every viewport. Those three — plus hostname, app version, heartbeat count,
   org and release detail, none of which had anywhere to live at all — moved
   into a detail row that opens on click. Six columns fit without scrolling.

   The detail row is toggled with the `hidden` attribute, so nothing here sets
   `display` on it: that would beat the UA's [hidden]{display:none} and the row
   would never close. */
.col-toggle{width:1%;white-space:nowrap}
.row-toggle{background:none;border:none;padding:0;cursor:pointer;font-family:inherit;
  font-size:var(--text-xs);line-height:1;color:var(--text-faint);
  transition:transform var(--dur-fast) var(--ease)}
.row-toggle[aria-expanded="true"]{transform:rotate(180deg)}
/* The same control sits in the header cell as an expand/collapse-all, where it
   goes disabled on an empty table — there is nothing to expand. */
.row-toggle:disabled{opacity:.35;cursor:not-allowed}
.table thead th .row-toggle{color:var(--text-muted)}
.seat-row{cursor:pointer}
.seat-detail>td{background:var(--bg-surface2);padding:var(--space-5) var(--space-6)}
/* Without a cap the auto/1fr grid stretches the value column to the full table
   width, leaving each label marooned from its value. */
.seat-detail .kv{max-width:620px}

/* ── Seat meter ──────────────────────────────────────────────────────────── */
.meter{height:10px;background:var(--bg-surface2);border-radius:var(--radius-pill);overflow:hidden;
  border:1px solid var(--border)}
.meter-fill{height:100%;width:0;border-radius:var(--radius-pill);
  background:linear-gradient(90deg,var(--mid-blue),var(--teal));transition:width var(--dur-slow) var(--ease)}
.meter-fill.is-full{background:linear-gradient(90deg,var(--orange),var(--red))}
.meter-label{font-size:var(--text-base);color:var(--text-muted);margin-top:var(--space-3);
  font-variant-numeric:tabular-nums}
.meter-label strong{color:var(--heading);font-size:var(--text-lg)}

/* ── Licensed variants ───────────────────────────────────────────────────── */
.variants{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:var(--space-4)}
.variant{border:1px solid var(--border);border-left:3px solid var(--mid-blue);
  border-radius:var(--radius-sm);padding:var(--space-4);background:var(--bg-surface2)}
.variant-name{font-size:var(--text-base);font-weight:700;color:var(--heading)}
.variant-id{font-size:var(--text-xs);color:var(--text-muted);margin-top:var(--space-1)}

/* ── Modules ─────────────────────────────────────────────────────────────── */
.modules{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:var(--space-4)}
.module{border:1px solid var(--border);border-left:3px solid var(--teal);
  border-radius:var(--radius-sm);padding:var(--space-4);background:var(--bg-surface2)}
.module.is-enterprise{border-left-color:var(--purple)}
.module.is-degraded{border-left-color:var(--orange)}
.module-name{font-size:var(--text-base);font-weight:700;color:var(--heading)}
.module-ref{font-size:var(--text-xs);font-weight:700;background:var(--purple);color:var(--on-fill);border-radius:var(--radius-pill);
  padding:var(--space-1) var(--space-2);margin-left:var(--space-2);vertical-align:middle}
.module-id{font-size:var(--text-xs);color:var(--text-muted);margin-top:var(--space-1)}
.module-note{font-size:var(--text-sm);color:var(--orange);margin-top:var(--space-2);font-style:italic}

/* ── Token drop zone ─────────────────────────────────────────────────────── */
.drop{border:2px dashed var(--border);border-radius:var(--radius-md);padding:var(--space-5);
  background:var(--bg-surface2);transition:border-color var(--dur),background var(--dur)}
.drop.is-over{border-color:var(--mid-blue);background:var(--light-blue)}

/* ── Licence cards ───────────────────────────────────────────────────────────
   One card per installed licence — pools are separate, so they are shown apart.
   Collapsed to its summary by default; the head toggles the rest. */
.lic-card{background:var(--bg-surface);border:1px solid var(--border);border-left:4px solid var(--mid-blue);
  border-radius:var(--radius-md);padding:var(--space-6);margin-bottom:var(--space-5);box-shadow:var(--shadow-sm)}
.lic-card.st-renewal_due,.lic-card.st-renewal_urgent{border-left-color:var(--orange)}
.lic-card.st-locked{border-left-color:var(--red)}
.lic-card.st-suspended{border-left-color:var(--purple)}
.lic-card.st-active{border-left-color:var(--green)}
/* Same reachable-but-unstyled gap as .badge-pending: st-${lifecycle.state}
   could be st-pending and silently fell through to the default border. Stated
   explicitly so the state set is complete and a future change to .lic-card's
   default cannot quietly redefine "not started yet". */
.lic-card.st-pending{border-left-color:var(--mid-blue)}
.lic-head{display:flex;align-items:center;gap:var(--space-4);flex-wrap:wrap;margin-bottom:var(--space-4);cursor:pointer}
.lic-opt{font-size:var(--text-md);font-weight:700;color:var(--heading)}
.lic-id{font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-faint)}
.lic-chevron{margin-left:auto;font-size:var(--text-xs);color:var(--text-faint);transition:transform var(--dur-fast) var(--ease)}
.lic-head[aria-expanded="true"] .lic-chevron{transform:rotate(180deg)}
.lic-summary{margin-bottom:var(--space-2)}
.lic-body{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-5);margin-top:var(--space-5)}
.lic-meter{margin-bottom:var(--space-3)}
.lic-covers{display:flex;gap:var(--space-2);flex-wrap:wrap;margin-top:var(--space-3)}
.lic-chip{background:var(--light-blue);color:var(--dark-blue);border-radius:var(--radius-pill);padding:var(--space-1) var(--space-3);
  font-size:var(--text-sm);font-weight:700;white-space:nowrap}
.lic-mod{background:var(--teal-light);color:var(--teal)}
.lic-note{font-size:var(--text-sm);color:var(--orange);margin-top:var(--space-3);font-style:italic}

/* ── Offline trust-bundle import — the path for a network with no route out ── */
.import-panel{margin-top:var(--space-5);border:2px solid var(--mid-blue);border-radius:var(--radius-md);
  background:var(--light-blue);padding:var(--space-5)}
.import-head strong{display:block;font-size:var(--text-base);color:var(--heading);margin-bottom:var(--space-1)}
.import-head span{font-size:var(--text-sm);color:var(--text-muted);display:block;margin-bottom:var(--space-4)}

/* ── Activation code — the second, out-of-band secret ────────────────────── */
.code-row{display:flex;align-items:center;gap:var(--space-3);margin-top:var(--space-4);flex-wrap:wrap}
.code-row label{font-size:var(--text-base);font-weight:600;
  color:var(--text-muted);white-space:nowrap}
/* `input.` prefix is load-bearing: as a bare .code-input (0,1,0) this lost
   font-family, font-size and font-weight to input[type=text] (0,1,1), so the
   activation code has never actually rendered monospace — only its
   letter-spacing and uppercasing survived, because the base rule sets neither.
   At (0,1,1) it ties and wins on load order. */
input.code-input{font-family:var(--font-mono);font-size:var(--text-lg);font-weight:700;
  letter-spacing:2px;text-transform:uppercase;width:200px;padding:var(--space-3) var(--space-4);
  border:1px solid var(--border);border-radius:var(--radius-sm);
  background:var(--bg-surface);color:var(--text)}
input.code-input:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-ring)}
.code-input.is-ok{border-color:var(--green)}

/* ── Empty state ─────────────────────────────────────────────────────────── */
.empty{background:var(--bg-surface);border:2px dashed var(--border);border-radius:var(--radius-md);
  padding:var(--space-9) var(--space-7);text-align:center}
.empty h3{font-size:var(--text-lg);font-weight:700;color:var(--heading);margin-bottom:var(--space-3)}
.empty p{font-size:var(--text-base);color:var(--text-muted);max-width:520px;margin:0 auto}

/* ── Seats status filter ──────────────────────────────────────────────────
   A multi-select dropdown replacing two independent checkboxes (show
   released / show suspended). Modelled on .avatar-menu in 03-base.css --
   same position:absolute-under-a-relative-button shape, same
   click-outside/Escape-to-close behaviour -- but kept here rather than
   promoted to the shared file: this is a Seats-tab feature specific to this
   portal, not something licence-server-portal has a use for. */
.status-filter{position:relative}
.status-filter-chevron{font-size:var(--text-xs);color:var(--text-faint);margin-left:var(--space-1)}
.status-filter-menu{position:absolute;top:calc(100% + 6px);left:0;min-width:190px;
  background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius-md);
  box-shadow:var(--shadow-md);padding:var(--space-2);z-index:var(--z-dropdown)}
/* flex-direction:row is load-bearing, not decoration: the shared `label` rule
   in 03-base.css sets flex-direction:column (every other label in this app is
   a field label with its control below), and this class never declared that
   property -- so the column value leaked straight through, stacking each
   checkbox above its own text and forcing the whole menu into one tall,
   centered column. Same trap .chk exists to avoid; this just missed it. */
.status-filter-option{display:flex;flex-direction:row;align-items:center;gap:var(--space-2);
  font-size:var(--text-sm);font-weight:600;color:var(--text);
  padding:var(--space-2) var(--space-3);border-radius:var(--radius-sm);cursor:pointer;
  white-space:nowrap}
.status-filter-option:hover{background:var(--bg-surface2)}
.status-filter-all{font-weight:700}
.status-filter-divider{height:1px;background:var(--border);margin:var(--space-1) var(--space-2)}

/* ── Conditional at-rules for this file's components only ────────────────── */

@media(max-width:820px){
  .lic-body{grid-template-columns:1fr}
}
