/* ─────────────────────────────────────────────────────────────────────
   Tech Ops — account portal stylesheet

   DEPLOY: upload to public_html/techops/portal/portal.css on Bluehost.

   Used by:
     techops/portal/index.html   (login + signup)
     techops/portal/app.html     (the signed-in shell — Task 5+)

   ── HOW THIS FILE RELATES TO /techops/styles.css ──────────────────────
   The marketing stylesheet is the design system. It owns the palette
   (--bg / --card / --border / --text / --text-muted / --text-dim /
   --accent-cyan / --accent-orange / --success / --danger / the navy +
   amber + teal ticket inks), the radii, the shadows, and the two
   typefaces. It also owns .wrap, .site-nav, .brand, .btn*, .hero,
   .section-head, .card, .kv and .site-footer.

   NOTHING in this file redefines any of those tokens. It @imports the
   marketing sheet and then adds ONLY what the portal genuinely needs and
   the marketing site never had: form controls, inline form errors, an
   auth panel, a signed-in shell chrome, a company card, a data table,
   pills, an empty state, and a modal.

   Because of the @import, a portal page links THIS FILE ONLY:
       <link rel="stylesheet" href="portal.css">
   Linking styles.css as well is harmless (identical rules, applied
   twice) but unnecessary.

   Visual direction: Tech Ops identity — Share Tech Mono labels in
   uppercase, flat 3px corners, hairline rules instead of soft shadows,
   navy chrome over the light workbench — on generous Material-ish
   spacing so a long billing form doesn't read like a spreadsheet.
   ───────────────────────────────────────────────────────────────────── */

@import url('../styles.css');

/* ── Page frame ───────────────────────────────────────────────────── */

/* Push the footer to the bottom on short pages (the auth page is short). */
.portal-body {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
.portal-main { flex: 1 0 auto; padding: 40px 0 64px; }
.portal-main.is-centered {
  display: flex;
  align-items: flex-start;
  justify-content: center;
}

/* A mono, uppercase micro-label. The site uses this treatment inline in
   several places (.hero-eyebrow, .section-head .eyebrow, .kv-row .k);
   the portal needs it as a standalone utility for form labels and
   card meta, so it lives here rather than being copy-pasted per page. */
.mono-label {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text-muted);
}

.portal-title {
  font-family: var(--sans);
  font-size: 26px;
  font-weight: 700;
  line-height: 1.2;
  margin: 0 0 6px;
  color: var(--text);
}
.portal-sub {
  margin: 0;
  font-size: 15px;
  color: var(--text-muted);
}

/* ── Panel — the portal's workhorse surface ───────────────────────── */
/* .card in styles.css is a marketing tile (hover lift, icon slot, CTA
   row). A portal panel holds a form or a table, doesn't lift, and has a
   ruled header — different component, not a variant. */

.panel {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  /* styles.css:475 sets a BARE `section { padding: 60px 0 }` — the
     vertical rhythm between marketing bands. A panel is a component, not
     a band, and the natural markup for one is <section class="panel">, so
     without this every panel gets 60px of dead space above and below its
     contents. (Measured in Brave: 63px top, 61px bottom, before this
     line.) Same reason on .company-card below. Anything the portal adds
     later that uses <section> and does not set its own padding needs the
     same zeroing. */
  padding: 0;
}
.panel + .panel { margin-top: 20px; }

.panel-head {
  padding: 16px 20px 14px;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
}
.panel-head h2 {
  margin: 0;
  font-family: var(--sans);
  font-size: 18px;
  font-weight: 700;
  color: var(--text);
}
.panel-body { padding: 20px; }
.panel-foot {
  padding: 14px 20px;
  border-top: 1px solid var(--border);
  background: var(--bg-alt);
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
}

/* The auth panel sits on its own, centered, with the amber tear edge
   from the ticket motif along the top. */
.auth-panel {
  width: 100%;
  max-width: 460px;
  border-top: 3px solid var(--amber);
}

/* ── Tabs (sign in / create account, and the shell's view nav) ────── */

.tabs {
  display: flex;
  border-bottom: 1px solid var(--border);
  background: var(--bg-alt);
}
.tab-btn {
  flex: 1;
  appearance: none;
  background: transparent;
  border: 0;
  border-bottom: 2px solid transparent;
  padding: 14px 12px 12px;
  cursor: pointer;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--text-muted);
  transition: color 120ms ease, background 120ms ease, border-color 120ms ease;
}
.tab-btn:hover { color: var(--text); background: rgba(14, 124, 134, 0.05); }
.tab-btn[aria-selected="true"] {
  color: var(--accent-cyan);
  background: var(--card);
  border-bottom-color: var(--accent-cyan);
}
.tab-btn:focus-visible { outline: 2px solid var(--accent-cyan); outline-offset: -2px; }

/* ── Forms ────────────────────────────────────────────────────────── */
/* styles.css has no input styling at all (only .manual-search-input, a
   one-off). Everything here is new. */

.form-stack { display: flex; flex-direction: column; gap: 16px; }

.field { display: flex; flex-direction: column; gap: 6px; }
.field > label {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text-muted);
}
.field-hint { font-size: 12.5px; line-height: 1.45; color: var(--text-dim); }

.input,
.select,
.textarea {
  width: 100%;
  padding: 11px 12px;
  background: var(--card);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-family: var(--sans);
  font-size: 16px;   /* 16px keeps iOS Safari from zooming on focus */
  font-weight: 500;
  line-height: 1.35;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.textarea { min-height: 92px; resize: vertical; }
.input::placeholder, .textarea::placeholder { color: var(--text-dim); }

.input:focus,
.select:focus,
.textarea:focus {
  outline: none;
  border-color: var(--accent-cyan);
  box-shadow: 0 0 0 3px var(--accent-cyan-soft);
}
.input:disabled,
.select:disabled,
.textarea:disabled {
  background: var(--bg-alt);
  color: var(--text-muted);
  cursor: not-allowed;
}
/* Set by JS when a specific field is what the server rejected. */
.input.is-invalid { border-color: var(--danger); }
.input.is-invalid:focus { box-shadow: 0 0 0 3px rgba(196, 60, 60, 0.15); }

/* Codes and serials read as data, not prose. */
.input-mono { font-family: var(--mono); letter-spacing: 1px; text-transform: uppercase; }

.form-row { display: flex; gap: 14px; flex-wrap: wrap; }
.form-row > .field { flex: 1 1 200px; min-width: 0; }

.btn-block { width: 100%; }

/* ── Large — the portal's highest-intent buttons ──────────────────────
   "Start subscription" (billing), "Launch app" / "Open my account" on
   the post-checkout success page, and the companies empty state's two
   actions. styles.css defines .btn-lg for the marketing site's hero
   band, where several sit side by side and a modest step up is enough to
   read as a group of CTAs. Here .btn-lg is usually the ONLY button on a
   panel, and it is the one asking for money or standing in for an empty
   account — a bigger step earns its place. Still the same voice as every
   other .btn (mono, uppercase, the shared 3px radius, the caller's own
   color via .btn-cyan/.btn-outline/etc.) — only size changes. */
.btn-lg {
  padding: 16px 30px;
  font-size: 15px;
}

/* A button mid-request. Keep the label — swapping it to "…" loses the
   affordance — just dim it and lock it. */
.btn.is-busy { opacity: 0.6; pointer-events: none; }

/* A button that is genuinely disabled must LOOK it. Without this, the
   modal's Cancel goes inert while a POST is in flight and gives no sign
   of it, which reads as a broken button rather than a busy one. */
.btn[disabled] { opacity: 0.55; cursor: not-allowed; pointer-events: none; }

/* A destructive secondary action — "Cancel subscription" is the only one
   in the portal today. The marketing sheet has no such variant because
   nothing on the marketing site destroys anything. It is deliberately an
   OUTLINE button, not a filled red one: cancelling should be findable and
   unmistakable, never the loudest thing on the page. */
.btn-outline.btn-danger {
  border-color: rgba(196, 60, 60, 0.45);
  color: var(--danger);
}
.btn-outline.btn-danger:hover {
  border-color: var(--danger);
  background: rgba(196, 60, 60, 0.07);
  color: var(--danger);
}

/* The FILLED variant, for a modal's confirm button when confirming
   destroys something. The shared modal's confirm is otherwise the primary
   cyan, which would make "Cancel subscription" the most inviting thing on
   a dialog whose whole purpose is to slow that decision down. */
.btn-danger-solid {
  background: var(--danger);
  border-color: var(--danger);
  color: #fff;
}
.btn-danger-solid:hover { filter: brightness(0.92); color: #fff; }

/* ── Alerts — the server's own words, inline, next to the form ────── */

.alert {
  padding: 11px 13px;
  border: 1px solid var(--border);
  border-left-width: 3px;
  border-radius: var(--radius-sm);
  font-size: 14.5px;
  line-height: 1.45;
}
.alert[hidden] { display: none; }
.alert-error {
  border-left-color: var(--danger);
  background: rgba(196, 60, 60, 0.07);
  color: #8E2C2C;
}
.alert-success {
  border-left-color: var(--success);
  background: var(--success-soft);
  color: #206A2B;
}
.alert-info {
  border-left-color: var(--accent-cyan);
  background: var(--accent-cyan-soft);
  color: #0A5F67;
}
.alert-warn {
  border-left-color: var(--accent-orange);
  background: var(--accent-orange-soft);
  color: #74490B;
}
/* A secondary SENTENCE inside an alert — a consequence, a caveat, the
   thing the person will wish they had read. Prose, not a machine code:
   it stays in the reading typeface at a readable size. (.alert-code
   below is mono and 10.5px, which is right for INTERNAL_ERROR and wrong
   for "your own seat is a billable seat".) */
.alert .alert-note {
  display: block;
  margin-top: 8px;
  font-size: 13.5px;
  line-height: 1.5;
  opacity: 0.92;
}

/* The machine-readable code, for support tickets. Small and dim on
   purpose — it is a footnote to the human sentence above it. */
.alert .alert-code {
  display: block;
  margin-top: 5px;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 1px;
  opacity: 0.72;
}

/* ── Signed-in shell chrome (app.html) ────────────────────────────── */

.portal-bar {
  background: var(--navy-1);
  border-bottom: 1px solid rgba(234, 246, 251, 0.08);
}
.portal-bar .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding-top: 12px;
  padding-bottom: 12px;
  flex-wrap: wrap;
}
.portal-bar-id {
  display: flex;
  align-items: center;
  gap: 12px;
  color: var(--muted-dark);
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.6px;
}
.portal-bar-email { color: var(--ink-dark); }

.portal-views {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
  background: var(--bg-alt);
}
.portal-views a {
  padding: 7px 12px;
  border-radius: var(--radius-sm);
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  color: var(--text-muted);
}
.portal-views a:hover { background: var(--card); color: var(--text); text-decoration: none; }
.portal-views a.is-active { background: var(--card); color: var(--accent-cyan); box-shadow: inset 0 -2px 0 var(--accent-cyan); }

/* ── Company cards ────────────────────────────────────────────────── */

.company-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 18px;
}

.company-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  display: flex;
  flex-direction: column;
  padding: 0;   /* see the note on .panel — bare `section` padding */
}
.company-card-head {
  padding: 16px 18px 13px;
  border-bottom: 1px dashed var(--border);
}
.company-card-head h3 {
  margin: 0 0 4px;
  font-family: var(--sans);
  font-size: 19px;
  font-weight: 700;
  color: var(--text);
  overflow-wrap: anywhere;
}
.company-card-body { padding: 14px 18px; flex: 1; }
.company-card-foot {
  padding: 13px 18px;
  border-top: 1px solid var(--border);
  background: var(--bg-alt);
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.company-card-foot .btn { padding: 9px 14px; font-size: 12px; }

/* Label/value stack inside a company card — plan, seats, dates. */
.meta-list { display: flex; flex-direction: column; gap: 9px; margin: 0; }
.meta-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.meta-row .k {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--text-muted);
  flex: none;
}
.meta-row .v { font-size: 14.5px; color: var(--text); text-align: right; overflow-wrap: anywhere; }

/* ── Pills — plan / status / role ─────────────────────────────────── */

.pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 8px;
  border-radius: var(--radius-sm);
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  white-space: nowrap;
}
.pill-trial   { background: var(--accent-orange-soft); color: var(--accent-orange); }
.pill-active  { background: var(--success-soft);       color: var(--success); }
.pill-pro     { background: var(--accent-cyan-soft);   color: var(--accent-cyan); }
.pill-danger  { background: rgba(196, 60, 60, 0.12);   color: var(--danger); }
.pill-neutral { background: var(--bg-alt);             color: var(--text-muted); }

/* ── Data table (users, invoices) ─────────────────────────────────── */

.table-wrap { overflow-x: auto; }   /* long rows scroll, the page never does */

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14.5px;
}
.data-table th,
.data-table td {
  padding: 11px 14px;
  text-align: left;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
.data-table th {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--text-muted);
  background: var(--bg-alt);
  white-space: nowrap;
}
.data-table tbody tr:last-child td { border-bottom: 0; }
.data-table tbody tr:hover { background: rgba(14, 124, 134, 0.035); }
.data-table td.num { text-align: right; font-family: var(--mono); }
.data-table th.num { text-align: right; }

/* A totals row. <tfoot> inherits the header treatment, which is right for
   the word "Total" and wrong for the money beside it — the figure a
   customer is about to be charged must not render as an 11px uppercase
   column label. */
.data-table tfoot th {
  border-top: 2px solid var(--border);
  border-bottom: 0;
}
.data-table tfoot th.num {
  font-family: var(--mono);
  font-size: 14.5px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--text);
}

/* Money and counts must never wrap mid-figure — "$1,234" broken across two
   lines reads as two numbers. */
.data-table td.num, .data-table tfoot th.num { white-space: nowrap; }

/* A two-line first cell: the seat name, then what it includes. */
.seat-name { display: block; }
.seat-hint {
  display: block;
  margin-top: 2px;
  font-size: 12.5px;
  color: var(--text-dim);
}

/* ── Empty state — for a brand-new account with no companies yet ──── */

.empty-state {
  padding: 44px 24px;
  text-align: center;
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  background: var(--card);
}
.empty-state .glyph {
  font-family: var(--mono);
  font-size: 26px;
  color: var(--accent-cyan);
  letter-spacing: 2px;
  margin-bottom: 10px;
}
.empty-state h3 {
  margin: 0 0 8px;
  font-family: var(--sans);
  font-size: 20px;
  font-weight: 700;
  color: var(--text);
}
.empty-state p {
  margin: 0 auto 18px;
  max-width: 460px;
  font-size: 15px;
  line-height: 1.55;
  color: var(--text-muted);
}
.empty-actions { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }

/* ── Loading ──────────────────────────────────────────────────────── */

.loading {
  padding: 34px 20px;
  text-align: center;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  color: var(--text-dim);
}
.spinner {
  display: inline-block;
  width: 15px;
  height: 15px;
  margin-right: 9px;
  vertical-align: -2px;
  border: 2px solid var(--border);
  border-top-color: var(--accent-cyan);
  border-radius: 50%;
  animation: portal-spin 720ms linear infinite;
}
@keyframes portal-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .spinner { animation-duration: 2.4s; }
}

/* ── Modal — confirmations and the Launch App password prompt ─────── */
/* Set width, content-driven height to a max; flat, not rounded-soft. */

.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: rgba(8, 18, 30, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.modal-backdrop[hidden] { display: none; }

.modal {
  width: 100%;
  max-width: 440px;
  max-height: calc(100vh - 40px);
  overflow-y: auto;
  background: var(--card);
  border: 1px solid var(--border);
  border-top: 3px solid var(--accent-cyan);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
}
.modal-head {
  padding: 16px 20px 13px;
  border-bottom: 1px solid var(--border);
}
.modal-head h2 {
  margin: 0;
  font-family: var(--sans);
  font-size: 18px;
  font-weight: 700;
  color: var(--text);
}
.modal-body { padding: 20px; }
.modal-foot {
  padding: 14px 20px;
  border-top: 1px solid var(--border);
  background: var(--bg-alt);
  display: flex;
  gap: 10px;
  justify-content: flex-end;
  flex-wrap: wrap;
}

/* ── Small utilities the portal reuses everywhere ─────────────────── */

.stack-sm { display: flex; flex-direction: column; gap: 8px; }
.stack-md { display: flex; flex-direction: column; gap: 16px; }
.row-gap  { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.push-right { margin-left: auto; }
.muted { color: var(--text-muted); }
.dim   { color: var(--text-dim); }
.text-sm { font-size: 13.5px; }
.nowrap { white-space: nowrap; }
/* app.html's footer and the profile view's sign-in address both ask for
   this; it had never been defined, so both silently rendered in the
   reading typeface. An email address is a string you have to read
   character by character — it belongs in the mono face. */
.mono { font-family: var(--mono); }
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ── Phones ───────────────────────────────────────────────────────── */

@media (max-width: 640px) {
  .portal-main { padding: 24px 0 44px; }
  .portal-title { font-size: 22px; }
  .panel-body, .modal-body { padding: 16px; }
  .panel-head, .panel-foot { padding-left: 16px; padding-right: 16px; }
  .company-grid { grid-template-columns: 1fr; }
  .company-card-foot .btn { flex: 1 1 auto; }
  .meta-row { flex-direction: column; gap: 2px; }
  .meta-row .v { text-align: left; }

  /* The billing seat table has to fit a 390px phone WITHOUT the cost
     column falling off the right edge behind a horizontal scroll — the
     cost is the whole reason the table exists. Tighter cells plus the
     three-column shape (count and rate share a cell) get it there. */
  .data-table th, .data-table td { padding: 9px 8px; }
  .data-table { font-size: 13.5px; }
  .seat-hint { font-size: 11.5px; }
}

/* ─────────────────────────────────────────────────────────────────────
   Signed-in shell + companies view (app.html, js/views/companies.js)

   Everything below is new surface the auth page never needed. Nothing
   here redefines a token or an existing component — same rule as the
   top of the file.
   ───────────────────────────────────────────────────────────────────── */

/* The view nav is a FULL-BLEED band with a centered .wrap inside, so its
   tint and bottom rule reach the viewport edge while the links line up
   with the page content. .portal-views above declares `display: flex`
   for the links themselves; with a .wrap in between, that flex row has
   to move inward — a single `margin: 0 auto` child of a flex container
   collapses to its content width and drags the links to the middle of
   the screen. */
.portal-views { display: block; }
.portal-views .wrap {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  align-items: center;
}

/* Outlined buttons on the navy account bar re-ink for dark chrome —
   .btn-outline is text-muted on a hairline border, which is invisible
   against --navy-1. Same treatment styles.css gives `.hero .btn-outline`. */
.portal-bar .btn { padding: 8px 14px; font-size: 12px; }
.portal-bar .btn-outline {
  color: #C3D5E0;
  border-color: rgba(234, 246, 251, 0.32);
}
.portal-bar .btn-outline:hover {
  color: var(--ink-dark);
  background: rgba(255, 255, 255, 0.07);
}

/* A view's own header: title on the left, that view's actions on the
   right. Not .panel-head — this one sits on the page, not on a card. */
.view-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 20px;
}
.view-head-actions { display: flex; gap: 10px; flex-wrap: wrap; }

/* A company that needs attention wears it on the card's top edge, in the
   same inks the pills use, so a grid of cards is scannable without
   reading a word of it. */
.company-card.is-warn   { border-top: 3px solid var(--accent-orange); }
.company-card.is-danger { border-top: 3px solid var(--danger); }

/* A failed action (Launch App, most likely) reports on its own card
   rather than replacing the grid or hijacking a page-level banner.
   Full-width so it sits ABOVE the button row it belongs to. */
.company-card-foot .alert { flex: 1 1 100%; }

/* The shared dialog's inline error sits above the dialog body. */
.modal-body > .alert { margin-bottom: 16px; }

@media (max-width: 640px) {
  .view-head { align-items: stretch; }
  .view-head-actions { width: 100%; }
  .view-head-actions .btn { flex: 1 1 auto; }
}

/* ─────────────────────────────────────────────────────────────────────
   Users view (js/views/users.js)

   The roster is a four-column table — person, role, seat, actions — and
   each of the last two carries a line of consequence text underneath it,
   because the whole point of the screen is that the price of an action is
   next to the action. That is more than a row of cells can be, so the
   cells are top-aligned and the phone layout below abandons the table
   shape entirely rather than hiding the cost behind a sideways scroll
   (the mistake Task 6's seat table had to be rebuilt to avoid).
   ───────────────────────────────────────────────────────────────────── */

/* A button living in a panel header sits beside an 18px heading, so it
   takes the compact treatment the company cards use. */
.panel-head .btn { padding: 8px 14px; font-size: 12px; }

.user-table td { vertical-align: top; }
/* Name and the "You" badge on one line — a block name pushed the badge
   onto its own row and left the address orphaned below it. */
.user-ident { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.user-table .user-name { font-weight: 600; color: var(--text); }
.user-table .user-sub {
  display: block;
  margin-top: 3px;
  font-size: 12.5px;
  color: var(--text-dim);
  overflow-wrap: anywhere;
}
/* The per-seat figure under the pill reads as money, so it is set in the
   mono face like every other number in the portal. */
.user-table .seat-cost { font-family: var(--mono); color: var(--text-muted); }
.user-table .field-hint { display: block; margin-top: 6px; }

.user-table .actions-col { text-align: right; }
.user-actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
.user-table .actions-col .field-hint { text-align: right; }
.user-table .actions-col .btn { padding: 8px 12px; font-size: 12px; }

@media (max-width: 640px) {
  /* Stacked blocks, one per person. Every cell keeps its column name from
     the `data-label` attribute, so nothing is lost with the header row —
     and the seat cost and the consequence line stay on screen, which they
     would not inside a horizontally scrolling table. */
  .user-table, .user-table tbody, .user-table tr, .user-table td { display: block; width: 100%; }
  .user-table thead { display: none; }
  .user-table tr {
    padding: 14px 0;
    border-bottom: 1px solid var(--border);
  }
  .user-table tbody tr:last-child { border-bottom: 0; }
  .user-table tbody tr:hover { background: transparent; }
  .user-table td { border: 0; padding: 4px 0; }
  .user-table td[data-label]::before {
    content: attr(data-label);
    display: block;
    margin-bottom: 3px;
    font-family: var(--mono);
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--text-muted);
  }
  /* The person's own name is the row's heading — it does not need to be
     told it is a person. */
  .user-table td[data-label="Person"]::before { content: none; }
  .user-table td[data-label="Person"] .user-name { font-size: 15.5px; }
  .user-table .actions-col,
  .user-table .actions-col .field-hint { text-align: left; }
  .user-actions { justify-content: flex-start; }
  .user-actions .btn { flex: 1 1 auto; }
}
