/* ============================================================================
 * login.css — the logged-out Login page (Phase 2 Task 11).
 *
 * Pixel source: project/login/login-screens.jsx (the bundle's VarGraph login —
 * neuron hero + side sign-in panel). The page lives OUTSIDE the portal shell
 * (no rail / no topbar): a standalone surface that still consumes the Phase-1
 * --tp-* tokens and self-hosted fonts. Token-pure: no hardcoded palette hex
 * except the on-ink text whites the design system already allowlists for
 * solid fills and the Microsoft brand squares (a vendor glyph, not a token).
 *
 * D-085: no inline JS, no inline style="" — the show/hide toggle, tagline
 * rotation, sign-in button state, and the (CSP-clean canvas) hero animation
 * all live in static/js/login.js bound through data-* hooks. element.style
 * written by JS is allowed; this stylesheet supplies every static rule.
 *
 * OWNER DECISION (2026-06-17): the identifier field renders as a full Email
 * (multi-org / multi-domain), not the bundle's "Username / firstname.lastname".
 * Visual styling matches the bundle field exactly. (docs/design-deltas.md.)
 *
 * Hero background: the bundle uses a Three.js WebGL neuron field. The strict
 * CSP (no CDN, default-src 'self') + proportionality favored a vendor-free
 * 2D-canvas neuron field driven from login.js over self-hosting Three.js; it
 * renders on a static brand gradient that also stands alone with JS off or
 * under prefers-reduced-motion. (design-delta logged.)
 * ========================================================================= */

.lg-page {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  background: var(--tp-surface);
  font-family: var(--tp-font-ui);
  color: var(--tp-ink);
}

/* ---- left hero pane (desktop) ----------------------------------------------
 * The dark neuron pane: a radial brand gradient with the canvas painted over
 * it, the brand mark top-left, the headline block centered, the version tag
 * bottom-left. Stacks above the form on phone. */
/* DEVICE CHROME (safe-area sweep 2026-08-06, finding 3 — the Phase 4.6
 * "letterboxed /login-as-first-screen inset audit"). This page is the installed
 * PWA's first screen: start_url is `/`, and an unauthenticated launch lands
 * here. It sits OUTSIDE the shell, so nothing above it absorbs an inset the way
 * the topbar does for .tp-content — and until this pass login.css contained
 * ZERO --tp-safe-* references on any axis.
 *
 * Which edges each column really touches drives which terms it takes. In the
 * default ROW layout the hero is the LEFT column and the panel the RIGHT one,
 * so each takes top + bottom plus its OWN outer side; the seam between them is
 * interior and takes nothing — reserving there would pad against no device edge
 * (the .adm-drill-header reasoning). The <768 stacked layout below re-states
 * both shorthands with the edges THAT layout has.
 *
 * Operators per the sweep's two-axis rule: max() on the sides (the notch inset
 * already IS the clearance — and the shipped 56px landscape gutter was 3px
 * SHORT of a 59px notch, which is what put the brand lockup under it), calc()
 * on the vertical (the designed 44/40px has to survive on top of the reserved
 * band; the portrait lockup rendered at y 24-58, wholly inside the 59px Island
 * band, and the panel's last row ended 22px above a 34px home indicator).
 * The hero's dark gradient still runs to the glass — this pads CONTENT only.
 * Every inset is 0 in a browser tab, so the designed 44/56 and 40/56 render
 * unchanged there. */
.lg-hero {
  position: relative;
  flex: 1 1 56%;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  padding: calc(44px + var(--tp-safe-top) + var(--tp-env-bar-h)) 56px
    calc(44px + var(--tp-safe-bottom)) max(56px, var(--tp-safe-left));
  color: #FFFFFF; /* on-ink text (design-system allowlisted on solid fills) */
  /* The bundle's radial cast: #16243F -> #070B14. Kept as literals: this is the
   * sanctioned dark hero surface (like RAPID), not part of the light palette. */
  background:
    radial-gradient(120% 130% at 50% 6%, #16243F 0%, #070B14 72%);
}

/* The vignette the bundle lays over the neuron field (focuses the center). */
.lg-hero-vignette {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    radial-gradient(78% 66% at 50% 42%, rgba(7, 11, 20, 0) 18%, rgba(7, 11, 20, 0.74) 100%);
}

/* The CSP-clean 2D-canvas neuron field; sits between the gradient and the
 * vignette. login.js sizes + paints it (element.style writes are allowed). */
.lg-hero-canvas {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  display: block;
}

.lg-hero-inner {
  position: relative;
  z-index: 3;
  display: flex;
  flex-direction: column;
  height: 100%;
}

/* ---- brand mark (BrandMark tone="light") ----------------------------------- */
.lg-brand {
  display: flex;
  align-items: center;
  gap: 11px;
}

/* The hero lockup renders via the .st-* brand layer (brand.css, dark-GROUND
   set — D-153); only the text stack + byline colour live here. */
.lg-brand-text {
  display: flex;
  flex-direction: column;
  gap: 3px;
  line-height: 1.25;
}

.lg-brand-org {
  font-size: 11px;
  color: rgba(255, 255, 255, 0.6);
}

/* ---- hero copy block (Hero tone="dark") ------------------------------------ */
.lg-hero-copy {
  flex: 1;
  display: flex;
  align-items: center;
}

.lg-hero-stack {
  display: flex;
  flex-direction: column;
  gap: 20px;
  max-width: 540px;
}

.lg-h1 {
  margin: 0;
  font-size: 54px;
  /* display-scale type — 700; was 800, off §9.6's five-value scale. */
  font-weight: var(--tp-type-display-weight);
  letter-spacing: -0.025em;
  line-height: 1.04;
  color: #FFFFFF;
  text-wrap: balance;
}

.lg-sub {
  margin: 0;
  font-size: 18.5px;
  line-height: 1.5;
  /* a SENTENCE — body copy's 400; was 450, off §9.6's five-value scale. */
  font-weight: var(--tp-type-body-weight);
  max-width: 460px;
  color: rgba(255, 255, 255, 0.74);
}

/* Rotating tagline row: bolt glyph + italic line, cross-fading in login.js. */
.lg-tagline {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  margin-top: 6px;
  height: 48px;
  overflow: hidden;
}

.lg-tagline-icon {
  color: var(--tp-brand);
  display: grid;
  place-items: center;
  flex-shrink: 0;
  margin-top: 2px;
  transition: opacity 0.45s;
}

.lg-tagline-text {
  font-size: 13.5px;
  font-style: italic;
  font-weight: 500;
  line-height: 1.4;
  color: rgba(255, 255, 255, 0.6);
  transition: opacity 0.45s, transform 0.45s;
}

/* The cross-fade state class login.js toggles between tagline swaps. */
.lg-tagline.is-fading .lg-tagline-icon,
.lg-tagline.is-fading .lg-tagline-text {
  opacity: 0;
}

.lg-tagline.is-fading .lg-tagline-text {
  transform: translateY(4px);
}

/* ---- version tag (VersionTag tone="dark") ---------------------------------- */
.lg-version {
  display: flex;
  align-items: center;
  gap: 7px;
  font-family: var(--tp-font-mono);
  font-size: 10.5px;
  color: rgba(255, 255, 255, 0.42);
  letter-spacing: 0.2px;
}

.lg-version-dot {
  width: 6px;
  height: 6px;
  border-radius: var(--tp-radius-pill);
  background: var(--tp-ok);
}

.lg-version-sep {
  opacity: 0.5;
}

/* ---- right sign-in panel ---------------------------------------------------- */
.lg-panel {
  flex: 0 0 44%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  /* Device chrome — the RIGHT column's own edges (see the .lg-hero block).
     The bottom term is also what reserves the scroll end: at 844x390 the form
     is taller than the viewport, so the page scrolls and this padding is the
     last thing above the home indicator. */
  padding: calc(40px + var(--tp-safe-top) + var(--tp-env-bar-h)) max(56px, var(--tp-safe-right))
    calc(40px + var(--tp-safe-bottom)) 56px;
  background: var(--tp-surface);
}

.lg-panel-inner {
  width: 100%;
  max-width: 360px;
  display: flex;
  flex-direction: column;
  gap: 26px;
}

.lg-panel-head {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.lg-panel-title {
  font-size: 23px;
  /* display-scale type — 700; was 750, off §9.6's five-value scale. */
  font-weight: var(--tp-type-display-weight);
  color: var(--tp-ink);
  letter-spacing: -0.015em;
}

.lg-panel-welcome {
  font-size: 14px;
  color: var(--tp-muted);
}

/* ---- error banner ----------------------------------------------------------- */
.lg-error {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  margin: 0;
  padding: 11px 13px;
  border-radius: var(--tp-radius-control);
  background: var(--tp-danger-soft);
  color: var(--tp-danger);
  border: 1px solid var(--tp-danger);
  font-size: 13px;
  font-weight: 500;
  line-height: 1.4;
}

.lg-error i {
  margin-top: 1px;
  flex-shrink: 0;
}

/* ---- the notice ------------------------------------------------------------- */
/* M22 #r4-34's one-line notice on the in-scope sign-in, `/ucheck/login`, after a
 * personal session ended (login.html says when). The board draws it on UCheck's own
 * sign-in face, which is 3C's; on THIS page it is `.lg-error`'s box in the neutral
 * tones — R4·33a's anatomy (a surface card, a hairline, 11/13 padding, the muted
 * ink) through the portal's tokens. A D-088 small-state reading (design-delta
 * #182): a minor state of this panel's own message line, not a new surface. */
.lg-notice {
  margin: 0;
  padding: 11px 13px;
  border-radius: var(--tp-radius-control);
  background: var(--tp-surface);
  border: 1px solid var(--tp-hairline);
  color: var(--tp-muted);
  font-size: 13px;
  font-weight: 500;
  line-height: 1.4;
}

/* ---- the form --------------------------------------------------------------- */
/* The form's own rhythm, PUBLISHED rather than typed twice: the F-IOS-6 targets
 * below expand into exactly this gap and have to shrink with it when the phone
 * block tightens it to 12 (a hit area sized to the desktop 14 overlapped the
 * email field and the Sign-in CTA by ~1px at 390 — measured). One declaration
 * moves the rhythm and both targets follow it. */
.lg-form {
  width: 100%;
  display: flex;
  flex-direction: column;
  --lg-form-gap: 14px;
  gap: var(--lg-form-gap);
}

.lg-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.lg-field-label {
  display: flex;
  align-items: center;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--tp-ink-2);
}

/* The Show/Hide password toggle sits at the end of the password label row.
 *
 * ---- F-IOS-6: AS CLOSE TO THE TAP FLOOR AS THIS CARD CAN GET ---------------
 * The 2026-08-30 iOS-simulator audit measured this control at 31x15 on real
 * hardware — a third of --tp-touch-min on each axis, on the first screen every
 * user of the portal touches. It does NOT reach 44 here, and the reason is
 * arithmetic rather than effort, so both halves are written down.
 *
 * WHAT THIS RULE BUYS: 31x17 -> 47x37, a 3.4x target, and NOT ONE PIXEL MOVES.
 * The VERTICAL growth is padding handed straight back as a negative margin, so
 * the margin box — which is what `.lg-field-label`'s flex line measures — is
 * byte-identical. The HORIZONTAL growth needs no margin at all and deliberately
 * does not use one: leading padding inside a flex item whose `margin-left` is
 * `auto` is absorbed by that `auto`, so the box grows leftward into the row's
 * own free space while its trailing edge stays exactly where it was and the
 * word "Show" renders at the coordinates it always rendered at. (No negative
 * horizontal margin therefore exists here to answer test_device_chrome.py's
 * pull guard for — the growth cannot reach a device edge because it never
 * crosses its own row's trailing edge.)
 *
 * WHAT IT CANNOT BUY, MEASURED. Every neighbour on this form is interactive, so
 * the expansion may only take space that belongs to NOTHING. Measured against
 * this sheet in WebKit and Chromium: 14.8px of `.lg-form` gap above (the email
 * field's box ends there) and 6.8px of `.lg-field` gap below (the password
 * input's box starts there). 17.2 + 14.8 + 6.8 = 38.8px is therefore the
 * pixel-neutral CEILING on this control's height, and 44 is 5.2px past it — and
 * the ceiling is LOWER at 390, where the phone block tightens the form gap to
 * 12 (hence `--lg-form-gap` rather than a typed 14: sized to the desktop gap,
 * this hit area overlapped the email field by 1.2px on a phone).
 *
 * Reaching the floor means growing the label row by ~27px or moving the toggle
 * INSIDE the field the way `auth.css .au-show-toggle` already does elsewhere in
 * this app — both of which redraw the login card, which is D-088's owner/design
 * call and not a padding tweak. The prompt is written and waiting at
 * docs/design-prompts/2026-08-30-login-touch-targets.md. */
.lg-show-toggle {
  margin-left: auto;
  border: none;
  background: transparent;
  color: var(--tp-muted);
  font-size: 11.5px;
  font-weight: 600;
  cursor: pointer;
  font-family: var(--tp-font-ui);
  /* Block padding grows the TARGET and the block margin hands every pixel of it
   * back; the 16px of inline padding is absorbed by `margin-left: auto` and
   * needs no counterweight. The gap above is the form's (published, so it
   * follows the phone override); the 6 below is `.lg-field`'s own, which does
   * not change by band. */
  padding: var(--lg-form-gap) 0 6px 16px;
  margin-block: calc(-1 * var(--lg-form-gap)) -6px;
}

.lg-input {
  height: 46px;
  border-radius: 11px;
  border: 1px solid var(--tp-hairline);
  box-shadow: none;
  background: var(--tp-surface);
  padding: 0 13px;
  font-size: 14.5px;
  color: var(--tp-ink);
  font-family: var(--tp-font-ui);
  outline: none;
  width: 100%;
  transition: border 0.15s, box-shadow 0.15s;
}

.lg-input::placeholder {
  color: var(--tp-muted);
}

.lg-input:focus {
  border-color: var(--tp-brand);
  box-shadow: 0 0 0 3px var(--tp-brand-soft);
}

/* Keep autofilled fields on the surface background. Browsers paint their own
   blue/yellow wash on an autofilled input, which made the (autofilled) email box
   read with a different background than the password box on mobile — owner redline
   2026-06-23. The inset box-shadow trick repaints the fill with the surface token
   and forces the text colour back to ink, so every field matches. Token-pure. */
.lg-input:-webkit-autofill,
.lg-input:-webkit-autofill:hover,
.lg-input:-webkit-autofill:focus,
.lg-input:-webkit-autofill:active {
  -webkit-text-fill-color: var(--tp-ink);
  -webkit-box-shadow: 0 0 0 100px var(--tp-surface) inset;
  box-shadow: 0 0 0 100px var(--tp-surface) inset;
  caret-color: var(--tp-ink);
}

.lg-input:-webkit-autofill:focus {
  -webkit-box-shadow: 0 0 0 100px var(--tp-surface) inset, 0 0 0 3px var(--tp-brand-soft);
  box-shadow: 0 0 0 100px var(--tp-surface) inset, 0 0 0 3px var(--tp-brand-soft);
}

/* ---- forgot link ------------------------------------------------------------ */
.lg-forgot-row {
  display: flex;
  justify-content: flex-end;
  margin-top: -4px;
}

/* F-IOS-6's second control — 108x17 measured on real hardware. The height is
 * the whole deficiency (108 already clears the floor on the inline axis, which
 * is why this one grows on ONE axis and carries no horizontal margin at all),
 * and the same grow-and-hand-it-back shape applies: 41.8px tall at 1440,
 * pixel-neutral, with the text at the coordinates it always rendered at.
 *
 * THE CEILING HERE IS 42.7 AND IT IS MEASURED, not assumed: `.lg-forgot-row`
 * carries `margin-top: -4px`, so the clear space above the link is the form gap
 * LESS that 4 (10.0px at 1440, 8.0px at 390) before the password input's box,
 * and the form gap itself below (13.9px / 11.9px) before `.lg-submit`'s.
 * 18.8 + 10.0 + 13.9 = 42.7 at the widest, and 44 is past it either way. Both
 * terms are written against `--lg-form-gap` for that reason — typed as the
 * desktop 14 they put this link's hit area 1px over the primary CTA at 390, and
 * a mis-tap that SUBMITS the form is a worse outcome than a pixel of target.
 * The trailing 1px is the same insurance against sub-pixel rounding.
 * See the note at `.lg-show-toggle` for the design gate that owns the rest. */
.lg-forgot {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--tp-brand);
  text-decoration: none;
  white-space: nowrap;
  padding-block: calc(var(--lg-form-gap) - 4px) calc(var(--lg-form-gap) - 1px);
  margin-block: calc(4px - var(--lg-form-gap)) calc(1px - var(--lg-form-gap));
}

.lg-forgot:hover {
  text-decoration: underline;
}

/* ---- primary sign-in button (INK fill, never brand-blue) -------------------- */
.lg-submit {
  height: 50px;
  border-radius: 12px;
  border: 1px solid var(--tp-ink);
  background: var(--tp-ink);
  color: #FFFFFF;
  font-size: 15px;
  /* a BUTTON LABEL, 600; was 650, off §9.6's five-value scale. */
font-weight: var(--tp-type-button-label-weight);
  font-family: var(--tp-font-ui);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  white-space: nowrap;
  transition: opacity 0.2s;
}

.lg-submit:disabled {
  cursor: default;
}

/* The three faces, swapped by the data-lg-state attribute login.js writes. */
.lg-submit-face {
  display: none;
  align-items: center;
  gap: 10px;
}

.lg-submit[data-lg-state="idle"] .lg-submit-face--idle,
.lg-submit[data-lg-state="signing"] .lg-submit-face--signing,
.lg-submit[data-lg-state="done"] .lg-submit-face--done {
  display: inline-flex;
}

.lg-submit[data-lg-state="done"] {
  opacity: 0.9;
}

/* The signing spinner (reuses the spinner duration token). */
.lg-spin {
  width: 15px;
  height: 15px;
  border-radius: var(--tp-radius-pill);
  border: 2px solid var(--tp-spinner-track-on-fill);
  border-top-color: #FFFFFF;
  animation: lg-spin var(--tp-spin-dur) linear infinite;
}

@keyframes lg-spin {
  to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
  .lg-spin { animation: none; }
}

/* ---- OR divider ------------------------------------------------------------- */
.lg-divider {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 2px 0;
}

.lg-divider-line {
  flex: 1;
  height: 1px;
  background: var(--tp-hairline);
}

.lg-divider-text {
  font-size: 11.5px;
  color: var(--tp-muted);
  font-weight: 500;
}

/* ---- Continue with Microsoft (surface + hairline) --------------------------- */
.lg-ms {
  height: 48px;
  border-radius: 12px;
  border: 1px solid var(--tp-hairline);
  background: var(--tp-surface);
  color: var(--tp-ink-2);
  font-size: 14.5px;
  font-weight: 600;
  font-family: var(--tp-font-ui);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  text-decoration: none;
}

.lg-ms:hover {
  background: var(--tp-bg);
}

/* Microsoft 4-square glyph — a vendor brand mark (its 4 colors are not tokens). */
.lg-ms-glyph {
  display: grid;
  grid-template-columns: 7px 7px;
  gap: 2px;
}

.lg-ms-sq {
  width: 7px;
  height: 7px;
  border-radius: 1px;
}

.lg-ms-sq--r { background: #F25022; }
.lg-ms-sq--g { background: #7FBA00; }
.lg-ms-sq--b { background: #00A4EF; }
.lg-ms-sq--y { background: #FFB900; }

/* ---- footer ----------------------------------------------------------------- */
.lg-footer {
  margin: 6px 0 0;
  font-size: 12px;
  color: var(--tp-muted);
  line-height: 1.5;
  text-align: center;
}

/* ---- responsive: stack the hero above the form on phone --------------------- */
@media (max-width: 767.98px) {
  /* Phone: hero on top FILLS the space above the form so the version tag pins to
     the hero's bottom edge and the page fits the viewport without scrolling on the
     common phone range; the form panel is content-sized at the bottom. (Owner
     redline 2026-06-23: brand→headline margin, version to the bottom, nudge the
     copy down, no scroll on various smartphones.) */
  .lg-page {
    flex-direction: column;
    min-height: 100dvh;
  }

  /* Stacked: the hero owns top + both sides, the panel owns bottom + both
     sides, and the seam between them is interior. Re-stated in full (not
     patched per-side) because each shorthand replaces the base one outright. */
  .lg-hero {
    flex: 1 1 auto;
    min-height: 0;
    padding: calc(24px + var(--tp-safe-top) + var(--tp-env-bar-h)) max(22px, var(--tp-safe-right)) 18px
      max(22px, var(--tp-safe-left));
  }

  /* Fill the flex-grown hero so the version tag pins to its bottom edge (height:100%
     does not resolve against a flex-sized parent in every engine — use flex-grow). */
  .lg-hero-inner {
    flex: 1 1 auto;
    min-height: 0;
  }

  /* Breathing room between the brand mark and the headline; the copy stays
     centered in the (growing) hero space below that gap. */
  .lg-hero-copy {
    margin-top: 16px;
  }

  .lg-hero-stack {
    gap: 11px;
  }

  /* Compacted so the headline + sub + quip + the brand gap all fit above the form
     without scrolling, down to ~360x740 phones. */
  .lg-h1 {
    font-size: 27px;
  }

  .lg-sub {
    font-size: 14px;
    line-height: 1.45;
  }

  .lg-tagline {
    height: 44px;
    margin-top: 2px;
  }

  .lg-tagline-text {
    font-size: 12.5px;
  }

  /* Sits at the hero's bottom edge (the copy above is flex:1). */
  .lg-version {
    margin-top: 16px;
  }

  .lg-panel {
    flex: 0 0 auto;
    align-items: stretch;
    justify-content: flex-start;
    padding: 18px max(22px, var(--tp-safe-right)) calc(22px + var(--tp-safe-bottom))
      max(22px, var(--tp-safe-left));
  }

  /* Slightly tighter form rhythm so the whole page clears the shortest target
     viewport (down to ~360x740) without scrolling. Declared as the published
     variable, not as `gap` — the base rule's `gap: var(--lg-form-gap)` still
     resolves to 12 here, AND the two F-IOS-6 hit areas that expand into this
     gap shrink with it instead of overlapping their neighbours at 390. */
  .lg-form {
    --lg-form-gap: 12px;
  }

  .lg-panel-inner {
    max-width: 100%;
  }

  /* On phone the bundle drops the side panel's "Sign in" heading (the hero
   * already framed the page); keep the form-first layout. */
  .lg-panel-head {
    display: none;
  }
}
