/* ============================================================
   Portal components — warm grey + oxblood on paper, rules only
   ------------------------------------------------------------
   Every value here resolves to a token. Don't hardcode colors
   or sizes in this file or in page CSS — add a token instead.
   Load portal-tokens.css BEFORE this file.

   The 2026-08-16 redesign draws structure with RULES (borders),
   not boxes: a 2px ink rule under the brand, hairlines between
   rows, dotted leaders in key-value lists, underline inputs,
   underline links. Serif (Newsreader) for display + editorial
   copy, sans (Archivo) for UI, mono (IBM Plex Mono) for labels,
   data and status tokens.
   ============================================================ */

/* --- Base ------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

body.portal {
  margin: 0;
  background: var(--portal-bg);
  color: var(--portal-text);
  font-family: var(--portal-font);
  font-size: var(--portal-text-base);
  line-height: var(--portal-leading-body);
  -webkit-font-smoothing: antialiased;
}

/* One focus treatment for everything. Keyboard users must
   always be able to see where they are. */
.portal :focus-visible {
  outline: var(--portal-focus-width) solid var(--portal-focus-color);
  outline-offset: var(--portal-focus-offset);
}

@media (prefers-reduced-motion: reduce) {
  .portal *, .portal *::before, .portal *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
  }
}

/* Skip link — first focusable element on every page. */
.portal-skip-link {
  position: absolute;
  left: -9999px;
  top: var(--portal-space-2);
  background: var(--portal-bg);
  color: var(--portal-accent);
  padding: var(--portal-space-2) var(--portal-space-4);
  border: var(--portal-border-width) solid var(--portal-text);
  font-size: var(--portal-text-sm);
  z-index: 100;
}
.portal-skip-link:focus { left: var(--portal-space-4); }

/* --- Typography ------------------------------------------- */

/* Page headings are serif display, tight-tracked. */
.portal-page-title {
  font-family: var(--portal-font-serif);
  font-size: var(--portal-display-lg);
  font-weight: 500;
  letter-spacing: -.015em;
  line-height: 1.25;
  margin: 0 0 var(--portal-space-2);
}

/* Editorial intro: serif, a shade darker than muted. */
.portal-page-intro {
  font-family: var(--portal-font-serif);
  font-size: 16px;
  line-height: 1.55;
  color: var(--portal-text-secondary);
  margin: 0 0 var(--portal-space-5);
  max-width: 46ch;
}

.portal-section-title {
  font-family: var(--portal-font-serif);
  font-size: var(--portal-text-lg);
  font-weight: 500;
  letter-spacing: -.01em;
  margin: 0 0 var(--portal-space-4);
}

/* Mono kicker/label — ACCOUNT CREATED, DATA FEEDS, EVIDENCE… */
.portal-kicker {
  font-family: var(--portal-font-mono);
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--portal-text-muted);
}

/* The heavy section rule under a brand or page header. */
.portal-rule {
  height: 2px;
  background: var(--portal-text);
  border: 0;
  margin: 0;
}

/* --- Layout ----------------------------------------------- */

.portal-shell { min-height: 100vh; }

.portal-main {
  max-width: var(--portal-content-width);
  margin: 0 auto;
  padding: var(--portal-space-8) var(--portal-space-5);
}
.portal-main--wide { max-width: var(--portal-wide-width); }

.portal-stack > * + * { margin-top: var(--portal-space-4); }

.portal-row {
  display: flex;
  gap: var(--portal-space-2);
  align-items: center;
  flex-wrap: wrap;
}
.portal-row--end { justify-content: flex-end; }

.portal-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--portal-space-4);
}

/* --- Top navigation --------------------------------------- */

.portal-nav {
  background: transparent;
  border-bottom: var(--portal-border-width) solid var(--portal-border);
  height: var(--portal-nav-height);
  display: flex;
  align-items: center;
  gap: var(--portal-space-5);
  padding: 0 var(--portal-space-5);
}

.portal-nav__brand {
  font-family: var(--portal-font-serif);
  font-size: 19px;
  font-weight: 500;
  letter-spacing: -.015em;
  color: var(--portal-text);
  text-decoration: none;
}

.portal-nav__link {
  font-size: var(--portal-text-sm);
  color: var(--portal-text-muted);
  text-decoration: none;
  height: var(--portal-nav-height);
  display: flex;
  align-items: center;
  border-bottom: 2px solid transparent;
  transition: color var(--portal-transition);
}
.portal-nav__link:hover { color: var(--portal-text); }
.portal-nav__link[aria-current="page"] {
  color: var(--portal-text);
  border-bottom-color: var(--portal-accent);
}

.portal-nav__spacer { margin-left: auto; }

/* Where the notification bell lands — the right end of the bar, which is
   where the account menu sits on the Portal page. Empty in the markup:
   wwwroot/_portal/notify.js fills it for a signed-in visitor and leaves it at
   zero width for everyone else, so the nav is unchanged when there is nothing
   to show. */
.portal-nav__notify { display: inline-flex; align-items: center; }

/* --- Card ------------------------------------------------- */
/* Flat: a hairline frame on the paper, no fill, no shadow. */

.portal-card {
  background: transparent;
  border: var(--portal-border-width) solid var(--portal-border);
  padding: var(--portal-space-5);
}

/* --- Buttons ---------------------------------------------- */

.portal-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--portal-space-2);
  height: var(--portal-control-height);
  padding: 0 var(--portal-space-5);
  border: var(--portal-border-width) solid transparent;
  font-family: var(--portal-font);
  font-size: var(--portal-text-sm);
  font-weight: var(--portal-weight-medium);
  line-height: 1;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  transition: background var(--portal-transition),
              border-color var(--portal-transition),
              color var(--portal-transition);
}

/* At most ONE of these per page. Ink fill, oxblood on hover. */
.portal-btn--primary {
  background: var(--portal-btn-fill);
  color: var(--portal-btn-label);
}
.portal-btn--primary:hover { background: var(--portal-btn-fill-hover); }

.portal-btn--secondary {
  background: transparent;
  color: var(--portal-text);
  border-color: var(--portal-border-strong);
}
.portal-btn--secondary:hover { border-color: var(--portal-text); }

.portal-btn--ghost {
  background: transparent;
  color: var(--portal-text-muted);
}
.portal-btn--ghost:hover {
  background: var(--portal-surface-sunken);
  color: var(--portal-text);
}

.portal-btn--danger {
  background: var(--portal-danger);
  color: var(--portal-text-inverse);
}
.portal-btn--danger:hover { background: var(--portal-danger-hover); }

.portal-btn--sm {
  height: var(--portal-control-height-sm);
  padding: 0 var(--portal-space-3);
  font-size: var(--portal-text-xs);
}

.portal-btn[disabled],
.portal-btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Loading state: keep the label, don't collapse the width. */
.portal-btn[data-loading="true"] { pointer-events: none; opacity: 0.7; }

/* --- Links ------------------------------------------------ */
/* Underlines are drawn as bottom borders with a little air,
   not text-decoration — the redesign's link signature. */

.portal-link {
  color: var(--portal-accent);
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--portal-accent) 40%, transparent);
  padding-bottom: 2px;
  transition: border-color var(--portal-transition), color var(--portal-transition);
}
.portal-link:hover {
  color: var(--portal-accent-hover);
  border-bottom-color: var(--portal-accent);
}

/* A quiet sibling for secondary actions. */
.portal-link--quiet {
  color: var(--portal-text-muted);
  border-bottom-color: var(--portal-border-strong);
}
.portal-link--quiet:hover {
  color: var(--portal-text);
  border-bottom-color: var(--portal-text);
}

/* --- Forms ------------------------------------------------ */

.portal-field { margin-bottom: var(--portal-space-6); }

.portal-label {
  display: block;
  font-family: var(--portal-font-mono);
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--portal-text-muted);
  margin-bottom: var(--portal-space-2);
}

/* Underline fields: no box, just a rule that answers in
   oxblood when focused. */
.portal-input,
.portal-select {
  width: 100%;
  height: auto;
  padding: 2px 0 9px;
  background: transparent;
  border: none;
  border-bottom: var(--portal-border-width) solid var(--portal-border-strong);
  border-radius: 0;
  color: var(--portal-text);
  font-family: var(--portal-font);
  font-size: var(--portal-text-base);
  transition: border-color var(--portal-transition);
}
.portal-select {
  cursor: pointer;
  appearance: none;
  padding-right: var(--portal-space-6);
}
.portal-textarea {
  width: 100%;
  min-height: 88px;
  padding: var(--portal-space-2) var(--portal-space-3);
  background: transparent;
  border: var(--portal-border-width) solid var(--portal-border-strong);
  border-radius: 0;
  color: var(--portal-text);
  font-family: var(--portal-font);
  font-size: var(--portal-text-sm);
  line-height: var(--portal-leading-body);
  resize: vertical;
  transition: border-color var(--portal-transition);
}
.portal-input:hover,
.portal-select:hover,
.portal-textarea:hover { border-color: var(--portal-text-muted); }
.portal-input:focus,
.portal-select:focus,
.portal-textarea:focus {
  outline: none;
  border-color: var(--portal-accent);
}

.portal-input::placeholder,
.portal-textarea::placeholder { color: var(--portal-text-subtle); }

/* 16px is iOS Safari's zoom threshold: focus a field below it and the page
   zooms in and does not zoom back out. The tokens are deliberate sizes on a
   pointer device, so they are raised only where the zoom fires — /Guest is
   the surface that matters, a code arrives by text or email and is redeemed
   on a phone. Same rule as the sign-in skins carry. */
@media (max-width: 640px) {
  .portal-input,
  .portal-select,
  .portal-textarea { font-size: 16px; }
}

.portal-input[disabled],
.portal-select[disabled],
.portal-textarea[disabled] {
  background: var(--portal-surface-sunken);
  color: var(--portal-text-muted);
  cursor: not-allowed;
}

/* Helper text sits under the field and explains the rule
   BEFORE the person gets it wrong. */
.portal-help {
  display: block;
  font-size: var(--portal-text-xs);
  color: var(--portal-text-muted);
  margin-top: var(--portal-space-1);
}

/* Error state. Pair with aria-invalid="true" and
   aria-describedby pointing at the .portal-error element. */
.portal-input[aria-invalid="true"],
.portal-select[aria-invalid="true"],
.portal-textarea[aria-invalid="true"] {
  border-color: var(--portal-danger);
  border-bottom-width: 2px;
}

.portal-error {
  display: flex;
  align-items: flex-start;
  gap: var(--portal-space-1);
  font-size: var(--portal-text-xs);
  color: var(--portal-danger-text);
  margin-top: var(--portal-space-1);
}

.portal-fieldset {
  border: 0;
  padding: 0;
  margin: 0 0 var(--portal-space-4);
}
.portal-legend {
  padding: 0;
  font-family: var(--portal-font-mono);
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--portal-text-muted);
  margin-bottom: var(--portal-space-2);
}

.portal-check {
  display: flex;
  align-items: flex-start;
  gap: var(--portal-space-2);
  font-size: var(--portal-text-sm);
  padding: var(--portal-space-1) 0;
  cursor: pointer;
}
.portal-check input { margin-top: 3px; accent-color: var(--portal-accent); }

/* Toggle rows in settings */
.portal-toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--portal-space-4);
  padding: var(--portal-space-3) 0;
  border-bottom: var(--portal-border-width) solid var(--portal-border);
}
.portal-toggle-row:last-child { border-bottom: 0; padding-bottom: 0; }
.portal-toggle-row__label { font-size: var(--portal-text-sm); margin: 0; }
.portal-toggle-row__desc {
  font-size: var(--portal-text-xs);
  color: var(--portal-text-muted);
  margin: 0;
}

/* --- Status tokens (pills) --------------------------------- */
/* Mono, uppercase, underlined in a faded cast of their color —
   the redesign's status voice. Only for states, never tags. */

.portal-pill {
  display: inline-block;
  padding: 0 0 2px;
  border-radius: 0;
  background: transparent;
  font-family: var(--portal-font-mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: .12em;
  text-transform: uppercase;
  line-height: 1.5;
  white-space: nowrap;
  border-bottom: 1px solid var(--portal-border-strong);
}
.portal-pill--success { color: var(--portal-success-text); border-bottom-color: color-mix(in srgb, var(--portal-success) 55%, transparent); }
.portal-pill--warning { color: var(--portal-warning-text); border-bottom-color: color-mix(in srgb, var(--portal-warning) 55%, transparent); }
.portal-pill--danger  { color: var(--portal-danger-text);  border-bottom-color: color-mix(in srgb, var(--portal-danger) 55%, transparent); }
.portal-pill--neutral { color: var(--portal-neutral-text); border-bottom-color: var(--portal-border-strong); }
.portal-pill--info    { color: var(--portal-accent-text);  border-bottom-color: color-mix(in srgb, var(--portal-accent) 55%, transparent); }

/* --- Alerts (left-rule callouts) --------------------------- */

.portal-alert {
  display: block;
  padding: 2px 0 2px var(--portal-space-4);
  border-left: 2px solid var(--portal-text);
  border-radius: 0;
  background: transparent;
  margin-bottom: var(--portal-space-5);
}
.portal-alert__title {
  font-family: var(--portal-font-mono);
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  margin: 0 0 var(--portal-space-2);
}
.portal-alert__body {
  font-family: var(--portal-font-serif);
  font-size: 15.5px;
  line-height: 1.55;
  color: var(--portal-text-secondary);
  margin: 0;
  /* 68ch, matching .triage-finding__evidence — the same body measure this
     codebase already uses inside a card. 46ch is a fine editorial column for
     a page lede that stands alone, but an alert is a full-width bordered box,
     and a third of it filled with text reads as a broken layout rather than a
     deliberate one. */
  max-width: 68ch;
}
.portal-alert--info    { border-left-color: var(--portal-text); }
.portal-alert--info    .portal-alert__title { color: var(--portal-text-muted); }
.portal-alert--success { border-left-color: var(--portal-success); }
.portal-alert--success .portal-alert__title { color: var(--portal-success-text); }
.portal-alert--warning { border-left-color: var(--portal-warning); }
.portal-alert--warning .portal-alert__title { color: var(--portal-warning-text); }
.portal-alert--danger  { border-left-color: var(--portal-danger); }
.portal-alert--danger  .portal-alert__title { color: var(--portal-danger-text); }

/* --- Key-value leader rows --------------------------------- */
/* key ……………………………… value — receipts, evidence, summaries. */

.portal-kv {
  display: flex;
  align-items: baseline;
  gap: var(--portal-space-3);
  padding: 7px 0;
}
.portal-kv__key {
  font-size: 12.5px;
  color: var(--portal-text-muted);
  flex: none;
}
.portal-kv__leader {
  flex: 1;
  border-bottom: 1px dotted var(--portal-border-strong);
  transform: translateY(-3px);
}
.portal-kv__value {
  font-family: var(--portal-font-mono);
  font-size: var(--portal-text-sm);
  color: var(--portal-text);
  flex: none;
  text-align: right;
}
.portal-kv__value--warning { color: var(--portal-warning); }
.portal-kv__value--success { color: var(--portal-success); }
.portal-kv__value--danger  { color: var(--portal-danger); }

/* --- Table ------------------------------------------------ */
/* No box around it: a 1px ink rule opens the header, hairlines
   divide the rows. */

.portal-table-wrap {
  background: transparent;
  border: none;
  border-radius: 0;
  overflow: visible;
}

.portal-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--portal-text-sm);
}
.portal-table th {
  text-align: left;
  font-family: var(--portal-font-mono);
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--portal-text-muted);
  background: transparent;
  padding: var(--portal-space-2) var(--portal-space-3) var(--portal-space-2) 0;
  border-top: var(--portal-border-width) solid var(--portal-text);
  border-bottom: var(--portal-border-width) solid var(--portal-border);
  white-space: nowrap;
}
.portal-table td {
  padding: var(--portal-space-3) var(--portal-space-3) var(--portal-space-3) 0;
  border-bottom: var(--portal-border-width) solid var(--portal-border);
  vertical-align: middle;
}
.portal-table tbody tr:last-child td { border-bottom: var(--portal-border-width) solid var(--portal-border); }
.portal-table tbody tr:hover { background: var(--portal-surface-sunken); }

.portal-table__num { text-align: right; font-variant-numeric: tabular-nums; }

/* Below this width a data table stops being readable.
   Cards are more usable than horizontal scrolling. */
@media (max-width: 640px) {
  .portal-table-wrap--stack .portal-table thead { display: none; }
  .portal-table-wrap--stack .portal-table tr {
    display: block;
    border-bottom: var(--portal-border-width) solid var(--portal-border);
    padding: var(--portal-space-2) 0;
  }
  .portal-table-wrap--stack .portal-table td {
    display: flex;
    justify-content: space-between;
    gap: var(--portal-space-4);
    border: 0;
    padding: var(--portal-space-1) var(--portal-space-3) var(--portal-space-1) 0;
  }
  .portal-table-wrap--stack .portal-table td::before {
    content: attr(data-label);
    color: var(--portal-text-muted);
    font-size: var(--portal-text-xs);
  }
}

/* --- Empty state ------------------------------------------ */
/* An invitation to act, not an apology. */

.portal-empty {
  text-align: center;
  padding: var(--portal-space-10) var(--portal-space-5);
}
.portal-empty__title {
  font-family: var(--portal-font-serif);
  font-size: var(--portal-text-lg);
  font-weight: 500;
  margin: 0 0 var(--portal-space-1);
}
.portal-empty__body {
  font-size: var(--portal-text-sm);
  color: var(--portal-text-muted);
  margin: 0 auto var(--portal-space-4);
  max-width: 42ch;
}

/* --- Auth sheet ------------------------------------------- */
/* Not a card any more: a 520px sheet of the paper itself,
   opened by the serif brand + mono kicker + 2px rule. */

.portal-auth {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--portal-space-8) var(--portal-space-5);
}
.portal-auth__card {
  width: 100%;
  max-width: 520px;
  background: transparent;
  border: none;
  border-radius: 0;
  padding: 0;
}
.portal-auth__brand { margin-bottom: var(--portal-space-6); }
.portal-auth__title {
  margin: 0;
  font-family: var(--portal-font-serif);
  font-size: var(--portal-display-xl);
  font-weight: 500;
  letter-spacing: -.015em;
  line-height: 1;
  color: var(--portal-text);
}
.portal-auth__kicker {
  font-family: var(--portal-font-mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: .26em;
  text-transform: uppercase;
  color: var(--portal-text-muted);
  margin-top: var(--portal-space-2);
}
.portal-auth__brand::after {
  content: "";
  display: block;
  height: 2px;
  background: var(--portal-text);
  margin-top: 14px;
}
.portal-auth__footer {
  display: flex;
  flex-wrap: wrap;
  gap: var(--portal-space-5);
  font-size: 12.5px;
  margin-top: var(--portal-space-8);
  padding-top: var(--portal-space-4);
  border-top: var(--portal-border-width) solid var(--portal-border);
}

/* --- Theme toggle ----------------------------------------- */
/* A quiet mono utility in the nav. */

.portal-theme-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--portal-space-2);
  height: var(--portal-control-height-sm);
  padding: 0;
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--portal-border-strong);
  color: var(--portal-text-muted);
  font-family: var(--portal-font-mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  cursor: pointer;
  transition: border-color var(--portal-transition),
              color var(--portal-transition);
}
.portal-theme-toggle:hover {
  border-bottom-color: var(--portal-text);
  color: var(--portal-text);
}

/* Logos and diagrams drawn for a light background will glare in
   dark mode. Tag any such image and supply a dark variant, or
   apply this as a last resort for flat monochrome marks. */
/* Unconditional since 2026-08-27: the system is dark-only, so gating this on
   [data-portal-theme="dark"] left a light-preference visitor with an
   un-inverted mark on a dark page. */
.portal-invert-on-dark {
  filter: invert(1) hue-rotate(180deg);
}

/* --- Utilities -------------------------------------------- */

.portal-muted { color: var(--portal-text-muted); }
.portal-mono  { font-family: var(--portal-font-mono); }
.portal-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;
}

/* ============================================================
   Portal additions
   ------------------------------------------------------------
   Components the shipped pages need that the base kit doesn't
   cover, added here per the rule at the top of this file: if a
   page needs a pattern, it gets added here — not hardcoded in
   page CSS. Tokens only.
   ============================================================ */

/* Browsers render <b>/<strong> at 700; the scale has two weights. */
.portal b, .portal strong { font-weight: var(--portal-weight-medium); }

/* Card as a link (admin index). */
a.portal-card {
  display: block;
  color: inherit;
  text-decoration: none;
  transition: border-color var(--portal-transition),
              background var(--portal-transition);
}
a.portal-card:hover { border-color: var(--portal-accent); }

/* --- Sub-navigation tabs (admin sections) ------------------ */

.portal-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--portal-space-1);
  border-bottom: var(--portal-border-width) solid var(--portal-border);
  margin-bottom: var(--portal-space-5);
}
.portal-tab {
  padding: var(--portal-space-2) var(--portal-space-3);
  font-size: var(--portal-text-sm);
  color: var(--portal-text-muted);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  margin-bottom: calc(-1 * var(--portal-border-width));
  transition: color var(--portal-transition);
}
.portal-tab:hover { color: var(--portal-accent); }
.portal-tab[aria-current="page"] {
  color: var(--portal-text);
  font-weight: var(--portal-weight-medium);
  border-bottom-color: var(--portal-accent);
}

/* --- Stat tiles (admin dashboards) ------------------------- */
/* A 2px ink rule opens each figure — no boxes. */

.portal-stats {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: var(--portal-space-5);
}
.portal-stat {
  background: transparent;
  border: none;
  border-top: 2px solid var(--portal-text);
  padding: var(--portal-space-3) 0 0;
}
.portal-stat__value {
  font-family: var(--portal-font-mono);
  font-size: var(--portal-text-2xl);
  font-weight: 500;
  letter-spacing: -.02em;
  line-height: var(--portal-leading-tight);
  font-variant-numeric: tabular-nums;
}
.portal-stat__label {
  font-family: var(--portal-font-mono);
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--portal-text-muted);
  margin-top: var(--portal-space-1);
}

/* --- Inline form row --------------------------------------- */
/* Labelled fields and a button sharing one baseline (admin
   filters, account quick actions). */

.portal-form-row {
  display: flex;
  gap: var(--portal-space-4);
  align-items: flex-end;
  flex-wrap: wrap;
}
.portal-form-row .portal-field { margin-bottom: 0; }
.portal-form-row .portal-field--grow { flex: 1 1 200px; }

/* Small input that sits beside --sm buttons in table rows. */
.portal-input--sm {
  padding-bottom: 5px;
  font-size: var(--portal-text-sm);
}

/* An input that shares a table row with its button — the password a
   last-passkey removal asks for. A cap, not a width: a fixed 160px pushed
   the passkeys table past its card on a phone, where the cell is narrower
   than that and the input has to shrink with it. */
.portal-input--narrow { max-width: 160px; }


/* --- Utilities -------------------------------------------- */
/* These exist for a specific reason: the credential pages run under
   CSP style-src 'self' with no 'unsafe-inline' (see Program.cs), so a
   style= attribute in their markup is dropped by the browser. Any
   one-off spacing on those pages has to be expressible as a class.
   Added 2026-08-26 when the redesign's inline spacing met main's
   hardened CSP in the merge. */

.portal-hidden { display: none; }

.portal-mt-3 { margin-top: var(--portal-space-3); }
.portal-mt-4 { margin-top: var(--portal-space-4); }
.portal-mt-5 { margin-top: var(--portal-space-5); }
.portal-mt-6 { margin-top: var(--portal-space-6); }
.portal-mt-8 { margin-top: var(--portal-space-8); }
.portal-m-0  { margin: 0; }
.portal-mb-0 { margin-bottom: 0; }

.portal-measure-narrow { max-width: 20ch; }

/* A rule that opens a section, rather than a box that encloses one. */
.portal-divider-top {
  margin-top: var(--portal-space-8);
  padding-top: var(--portal-space-4);
  border-top: var(--portal-border-width) solid var(--portal-border);
}

/* Codes read as codes: monospace, tracked out so digits are countable
   at a glance. --code is the 6-digit TOTP, --key the longer guest pass. */
.portal-input--code { font-family: var(--portal-font-mono); letter-spacing: 0.2em; }
.portal-input--key  { font-family: var(--portal-font-mono); letter-spacing: 0.08em; }

/* --- Two-factor enrollment (/Account) ---------------------- */
/* The QR beside the key it encodes. These lived in a <style> block on
   the page until 2026-10-02, and /Account is a credential surface, so
   its CSP dropped them: the QR came out at its 570px natural size and
   pushed a phone's page sideways. The image keeps a hardcoded white
   ground on purpose — authenticator apps need a light QR even in dark
   mode. */

.portal-qr { display: flex; gap: var(--portal-space-4); align-items: center; flex-wrap: wrap; margin-bottom: var(--portal-space-4); }
.portal-qr img { width: 168px; height: 168px; border-radius: var(--portal-radius); background: #fff; padding: var(--portal-space-2); }
.portal-qr code { font-family: var(--portal-font-mono); font-size: var(--portal-text-sm); letter-spacing: .06em; }
.portal-qr .portal-help { margin-bottom: var(--portal-space-1); }

/* --- Passkey management (/Account/Passkeys) ---------------- */
/* pk-hidden is toggled by passkey-manage.js (classList.add/remove) to
   reveal the alert and the last-passkey password confirm. It came from
   site.css, which this file replaced — without the rule those elements
   are visible from first paint. */

.pk-hidden { display: none; }

/* Rules, not boxes — the original was a stack of dark slate cards from
   site.css, which read as a different product next to everything else.
   The pk-* names are fixed by passkey-manage.js; only the look changed. */

.pk-list {
  border-top: var(--portal-border-width) solid var(--portal-border);
  margin: var(--portal-space-5) 0 var(--portal-space-6);
}

.pk-empty {
  padding: var(--portal-space-6) 0;
  text-align: center;
  font-size: var(--portal-text-sm);
  color: var(--portal-text-muted);
}

.pk-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--portal-space-4);
  padding: var(--portal-space-4) 0;
  border-bottom: var(--portal-border-width) solid var(--portal-border);
}

.pk-row-main {
  display: flex;
  flex-direction: column;
  gap: var(--portal-space-1);
  min-width: 0;
}

.pk-name {
  font-weight: var(--portal-weight-medium);
  color: var(--portal-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pk-meta {
  font-size: var(--portal-text-xs);
  color: var(--portal-text-muted);
}

/* A credential the engine no longer recognises. Flagged rather than
   hidden, so it can still be removed. */
.pk-row.pk-stale { border-bottom-color: var(--portal-danger); }
.pk-stale .pk-name,
.pk-stale .pk-meta { color: var(--portal-danger-text); }
