/* gateway.css — the pre-auth gateway page set (PBI-2331).
 *
 * This is a SEPARATE bundle/origin from the per-tenant instance SPA, so it
 * re-declares the design tokens verbatim (the sanctioned token copy, mirroring
 * web/kanban/src/index.css) and replicates the glass-panel / btn-primary /
 * focus-ring / background-glow recipes from internal/web/static/style.css and
 * docs/reference/architecture/design-system.md. It invents no palette: every
 * value composes an existing token. New .gw-* classes are the gateway surface.
 */

:root {
  --primary-bg: #030712;
  --glass-bg: rgba(255, 255, 255, 0.05);
  --glass-border: rgba(255, 255, 255, 0.1);
  --accent-purple: #7c3aed;
  --accent-blue: #3b82f6;
  --accent-pink: #db2777;
  --status-green: #10b981;
  --status-yellow: #f59e0b;
  --status-red: #ef4444;
  --text-primary: #f9fafb;
  --text-secondary: #9ca3af;
  --font-main: 'Inter', sans-serif;
  --font-display: 'Outfit', sans-serif;
}

*, *::before, *::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: var(--font-main);
  background-color: var(--primary-bg);
  color: var(--text-primary);
  min-height: 100dvh;
  overflow-x: hidden;
  background-image:
    radial-gradient(at 0% 0%, rgba(124, 58, 237, 0.15) 0px, transparent 50%),
    radial-gradient(at 100% 0%, rgba(59, 130, 246, 0.15) 0px, transparent 50%),
    radial-gradient(at 50% 100%, rgba(219, 39, 119, 0.1) 0px, transparent 50%);
}

h1, h2, h3 { font-family: var(--font-display); }

/* --- Centered auth layout --- */
.gw-page {
  min-height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Clear the notch / home indicator like the app nav does. */
  padding: calc(1.5rem + env(safe-area-inset-top)) 1.25rem
           calc(1.5rem + env(safe-area-inset-bottom));
}

/* Glass-panel recipe (design-system.md:108-118) at a 440px auth width. */
.gw-card {
  width: 100%;
  max-width: 440px;
  background: var(--glass-bg);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid var(--glass-border);
  border-radius: 1.5rem;
  padding: 2rem 1.5rem;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

/* --- Hub-style hero (style.css .hub__hero/.hub__logo/.hub__title) --- */
.gw-hero {
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
}
.gw-hero__logo {
  width: 3rem;
  height: 3rem;
  color: var(--accent-purple);
  margin-bottom: 0.5rem;
}
.gw-hero__title {
  font-size: 2.5rem;
  letter-spacing: 0.12em;
  background: linear-gradient(135deg, #fff, var(--accent-purple));
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}
.gw-hero__subtitle {
  color: var(--text-secondary);
  font-size: 1rem;
  font-weight: 400;
  letter-spacing: 0.05em;
}

/* --- Fields (mirrors ui.js .field*) --- */
.field {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.field__label {
  font-size: 0.85rem;
  font-weight: 600;
}
.field__input {
  font-size: 1rem; /* >=16px avoids iOS focus auto-zoom */
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--glass-border);
  border-radius: 0.5rem;
  background: rgba(255, 255, 255, 0.04);
  color: var(--text-primary);
  -webkit-appearance: none;
  appearance: none;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.field__input:focus {
  outline: none;
  border-color: rgba(124, 58, 237, 0.75);
  box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.18);
}
.field__hint {
  font-size: 0.8rem;
  color: var(--text-secondary);
}

/* --- Checkbox field (PBI-4301 "Remember me"). Composes the existing field
   vocabulary and the --accent-purple focus ring; invents no palette. --- */
.field--check {
  flex-direction: row;
  align-items: flex-start;
  gap: 0.6rem;
}
.field__check {
  flex: none;
  width: 1.15rem;
  height: 1.15rem;
  margin-top: 0.1rem;
  border: 1px solid var(--glass-border);
  border-radius: 0.3rem;
  background: rgba(255, 255, 255, 0.04);
  -webkit-appearance: none;
  appearance: none;
  cursor: pointer;
  display: grid;
  place-content: center;
  transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
}
.field__check::before {
  content: "";
  width: 0.62rem;
  height: 0.62rem;
  transform: scale(0);
  transition: transform 0.12s ease-out;
  background: var(--text-primary);
  /* A tick, drawn rather than typed, so it needs no icon font. */
  clip-path: polygon(14% 47%, 0 61%, 39% 100%, 100% 24%, 85% 10%, 39% 71%);
}
.field__check:checked {
  background: linear-gradient(135deg, var(--accent-purple), var(--accent-blue));
  border-color: transparent;
}
.field__check:checked::before {
  transform: scale(1);
}
.field__check:focus-visible {
  outline: none;
  border-color: rgba(124, 58, 237, 0.75);
  box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.18);
}
.field__check-label {
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}
.field__check-label .field__hint {
  font-weight: 400;
}

/* --- Primary button (design-system.md:131-140) --- */
.btn-primary {
  background: linear-gradient(135deg, var(--accent-purple), var(--accent-blue));
  border: none;
  border-radius: 0.75rem;
  color: white;
  padding: 0.75rem 1.5rem;
  font-family: var(--font-main);
  font-weight: 600;
  font-size: 1rem;
  min-height: 44px; /* touch target */
  cursor: pointer;
  transition: transform 0.2s, box-shadow 0.2s;
}
.btn-primary:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 20px rgba(124, 58, 237, 0.3);
}
.btn-primary:disabled {
  opacity: 0.4;
  cursor: default;
  transform: none;
  box-shadow: none;
}

/* Secondary / text button. */
.btn-text {
  background: none;
  border: none;
  color: var(--text-secondary);
  font-family: var(--font-main);
  font-size: 0.85rem;
  cursor: pointer;
  text-decoration: underline;
  padding: 0.25rem;
}
.btn-text:hover { color: var(--text-primary); }

/* --- Error / notice boxes --- */
.error {
  color: var(--status-red);
  font-size: 0.9rem;
  min-height: 1.2em;
}
.gw-notice {
  border-radius: 0.75rem;
  padding: 1rem 1.25rem;
  font-size: 0.92rem;
  line-height: 1.5;
}
.gw-notice--info {
  background: rgba(59, 130, 246, 0.1);
  border: 1px solid rgba(59, 130, 246, 0.25);
  color: var(--text-primary);
}
.gw-notice--error {
  background: rgba(239, 68, 68, 0.1);
  border: 1px solid rgba(239, 68, 68, 0.3);
  color: var(--text-primary);
}
/* Warning notice — the "we could not verify this" state, deliberately distinct
 * from --error (a proven problem) and --info (nothing wrong). Composes the
 * existing --status-yellow token, same rgba shape as the siblings above. */
.gw-notice--warn {
  background: rgba(245, 158, 11, 0.1);
  border: 1px solid rgba(245, 158, 11, 0.3);
  color: var(--text-primary);
}
/* Stack of role/modality verdicts on the model configurator (PBI-3115). */
.gw-notice-stack {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}
.gw-reasons {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  color: var(--text-secondary);
  font-size: 0.9rem;
}
.gw-reasons li::before { content: "• "; color: var(--accent-purple); }
.gw-muted { color: var(--text-secondary); font-size: 0.85rem; line-height: 1.5; }
.gw-status-line { text-align: center; color: var(--text-secondary); min-height: 1.4em; }

/* --- Provisioning spinner (composes settings-spin, style.css:645) --- */
.gw-spinner {
  width: 2.5rem;
  height: 2.5rem;
  margin: 0 auto;
  border: 3px solid rgba(124, 58, 237, 0.2);
  border-top-color: var(--accent-purple);
  border-radius: 50%;
  animation: gw-spin 0.8s linear infinite;
}
@keyframes gw-spin { to { transform: rotate(360deg); } }

/* Reduced motion: a static indicator, never a spin (design-system.md:270). */
@media (prefers-reduced-motion: reduce) {
  .gw-spinner {
    animation: none;
    border-top-color: rgba(124, 58, 237, 0.2);
    border-right-color: var(--accent-purple);
  }
  .btn-primary { transition: none; }
  .btn-primary:hover { transform: none; }
}

/* --- Operator invite admin (settings provider-row variant) --- */
.gw-admin {
  width: 100%;
  max-width: 720px;
}
.gw-invite-list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-top: 1rem;
}
.gw-invite-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  padding: 0.75rem 1rem;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--glass-border);
  border-radius: 0.75rem;
}
.gw-invite-row__email { font-weight: 500; flex: 1 1 12rem; }
.gw-invite-row__date { color: var(--text-secondary); font-size: 0.8rem; }
.gw-actions { display: flex; gap: 0.5rem; align-items: center; }

/* Status pills (99px radius, semantic tokens only — design-system.md:102). */
.pill {
  border-radius: 99px;
  padding: 2px 10px;
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.pill--invited  { background: rgba(156, 163, 175, 0.2); color: var(--text-secondary); }
.pill--redeemed { background: rgba(16, 185, 129, 0.2);  color: var(--status-green); }
.pill--revoked  { background: rgba(255, 255, 255, 0.06); color: var(--text-secondary); }
/* Tenant lifecycle pills (operator fleet inventory — PBI-2466). */
.pill--provisioning   { background: rgba(245, 158, 11, 0.2); color: var(--status-yellow); }
.pill--running        { background: rgba(16, 185, 129, 0.2); color: var(--status-green); }
.pill--suspended      { background: rgba(239, 68, 68, 0.2);  color: var(--status-red); }
.pill--decommissioned { background: rgba(255, 255, 255, 0.06); color: var(--text-secondary); }
/* Live /healthz probe pills (operator fleet inventory — PBI-2482). Reuse the
   semantic status tokens; down/unknown are neutral-grey (no live signal). */
.health--green   { background: rgba(16, 185, 129, 0.2); color: var(--status-green); }
.health--amber   { background: rgba(245, 158, 11, 0.2); color: var(--status-yellow); }
.health--red     { background: rgba(239, 68, 68, 0.2);  color: var(--status-red); }
.health--down    { background: rgba(239, 68, 68, 0.2);  color: var(--status-red); }
.health--unknown { background: rgba(255, 255, 255, 0.06); color: var(--text-secondary); }
/* Engine-version drift (operator fleet inventory — PBI-3134 P3-f): the INTENDED
   version stamped on the tenant vs the one its /healthz reports. Same semantic
   tokens as the health states above, no new palette.
   - drift: --status-red, and the ONLY state that also gets a pill. A failed upgrade
     reports its old version and looks green, so this must be the loudest thing on
     the row.
   - unconfirmed: --status-yellow ("we could not check"), matching gw-notice--warn's
     meaning — distinct from drift, which is a proven disagreement.
   - ok / unrecorded / na (retired, terminal): no rule at all; they inherit
     gw-invite-row__date's muted secondary text, which is the point — the normal case
     and the dead rows both stay quiet.
   The drift rules are guarded by TestGatewayCSS_DriftIsLoud: delete them and every
   behavioural test still passes while drift silently renders as a grey pill. */
.pill.version--drift               { background: rgba(239, 68, 68, 0.2); color: var(--status-red); }
.gw-invite-row__date.version--drift       { color: var(--status-red); }
.gw-invite-row__date.version--unconfirmed { color: var(--status-yellow); }

/* Rows stuck in 'provisioning' (operator fleet inventory — PBI-4075): the control
   plane still claims a provision is in flight more than an hour after it started.
   --status-yellow, deliberately NOT red: drift is a proven failure and must stay the
   loudest state on the row, while a stuck row is an anomaly awaiting an operator
   decision (retire, or retry) that this surface explicitly does not make.
   A stuck row is ALWAYS status=provisioning, so .pill--provisioning is rendered
   three spans earlier in the same amber — identical background and colour. The
   separation is therefore WEIGHT, not hue: a ring and a heavier face, which reads as
   the louder of the two amber pills without competing with drift's red. Guarded by
   TestGatewayCSS_StuckProvisioningIsVisible, which pins both the token and the ring:
   delete either and the flag becomes a second copy of the badge beside it. */
.pill.provisioning--stale                  { background: rgba(245, 158, 11, 0.2); color: var(--status-yellow); box-shadow: inset 0 0 0 1px var(--status-yellow); font-weight: 600; }
.gw-invite-row__date.provisioning--stale   { color: var(--status-yellow); }

/* Operator-surface nav (links between the invites + instances + config admin pages). */
.gw-nav { display: flex; gap: 1rem; margin-top: 0.5rem; }

/* Section heading inside the operator model configurator (PBI-2467 S2a). */
.gw-section-title {
  font-size: 1rem;
  font-weight: 600;
  margin: 1.5rem 0 0.25rem;
  color: var(--text-primary);
}

.btn-danger {
  background: none;
  border: none;
  color: var(--text-secondary);
  font-family: var(--font-main);
  font-size: 0.85rem;
  cursor: pointer;
  padding: 0.25rem 0.5rem;
  border-radius: 0.5rem;
}
.btn-danger:hover { color: var(--status-red); }

.gw-copied { color: var(--status-green); font-size: 0.8rem; }

@media (max-width: 640px) {
  .gw-card { padding: 1.5rem 1.25rem; }
  .gw-hero__title { font-size: 2rem; }
}

/* CSP-safe utilities (no inline style= / el.style.*; strict style-src) */
.gw-center { text-align: center; }
.gw-link-plain { text-decoration: none; }
.gw-hidden { display: none !important; }
.gw-inline { display: inline; }
.gw-offscreen { position: fixed; top: 0; left: -9999px; }

/* Links inside prose (PBI-4647). gateway.css had NO anchor rule at all: every
   link on every other page is .btn-text or .gw-link-plain, so a bare <a> in a
   sentence fell through to the browser default — dark blue on a dark blue
   notice, which is unreadable and was invisible to every test we had. Composes
   the existing tokens rather than introducing a link colour. */
.gw-notice a,
.gw-muted a {
  color: var(--text-primary);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-color: rgba(167, 139, 250, 0.8);
}
.gw-notice a:hover,
.gw-muted a:hover { text-decoration-color: var(--text-primary); }

/* The funnel row carries eight stage pills, a set of actions and an invite
   link. Left to the parent flex row they interleave, and "ACTIVE" ends up
   sitting in the middle of the action links — the journey stops being readable
   left to right, which is the only thing the row is for. Grouping the stages
   makes them wrap as one block, and keeps the actions together after it. */
.gw-stages {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  align-items: center;
  flex: 1 1 100%;
  margin: 0.15rem 0;
}

/* Alpha-funnel stage pills (PBI-4647). Three states, not eight colours: a row is
   read left to right as a journey, so the eye needs to find the point it stops,
   not decode a palette. They reuse .pill and the same status tokens the health
   and drift states use — no new colour system.

   UNKNOWN is deliberately NOT the same as NOT-REACHED. "The instance told us
   nothing" and "they have not done it" send an operator to two different places,
   and the whole reason healthResult.onboarded is a pointer is to keep them
   apart; rendering both as the same grey would throw that away at the last step.
   It carries an inset ring so it reads as a question rather than an absence. */
.pill.stage--reached     { background: rgba(16, 185, 129, 0.2); color: var(--status-green); }
.pill.stage--not-reached { background: rgba(255, 255, 255, 0.06); color: var(--text-secondary); }
.pill.stage--unknown     { background: rgba(245, 158, 11, 0.12); color: var(--status-yellow); box-shadow: inset 0 0 0 1px var(--status-yellow); }

/* The state marker inside a stage pill (PBI-4647, review finding 10). Slightly
   dimmed so it reads as a status glyph rather than part of the stage name, but
   it is TEXT: the state must survive a monochrome display, a screen reader and a
   touch device that never shows the title attribute. */
.stage__mark { opacity: 0.75; font-weight: 700; }
