/* THE SIGN-IN PAGE — login.html, "Focus Portal".
   =================================================================================
   The door between index.html and the suite: three compositions, two themes,
   six breakpoints — too much CSS for a <style> block re-downloaded with the
   markup on every visit, so it is a cacheable file with a `?v=` stamp.

   Deliberately does not load CSS/tokens.css, components.css or switch.css —
   sys_design-system.md §14a. This page must render instantly with no
   application stylesheet on its critical path. It MIRRORS CSS/landing.css's
   token NAMES with landing.css's own MEASURED VALUES, the way landing.css
   mirrors tokens.css, so the door and the room are demonstrably one palette.

   What is not here: login.html's own <head> <style> keeps only the ground
   both themes paint on, `[hidden]{display:none !important}`, and the bare
   `:focus-visible` floor. This file never writes `outline:none` without a
   replacement in the same rule, and never redeclares the bare
   `:focus-visible` — a component here may only tighten its own ring.

   Blue is the primary, no gold on this page at all: `--blue-fill` carries
   the primary button, `--blue` is focus/selection, `--blue-text` is accent
   text. Cyan, violet and green each carry one idea, used consistently.
   `--sc-*` is the brand's own blue -> cyan -> green relationship,
   sys_branding.md's "small number of deliberate brand moments", scoped to
   `.lg-scenic` and read by nothing else.

   Both theme selectors, every time: `:root.theme-light` AND
   `body.theme-light` — the boot script can only stamp <html>, JS/core/theme.js
   stamps both, so a rule written with one selector flashes on first paint
   or fails after the toggle.

   Every added colour carries its measured ratio, for both themes, beside
   it. Four grounds on this page, not two: the page ground `--bg`, the card
   `--surface`, `--surface-2`/`--surface-3` under tiles and disabled
   controls, and the COMPOSITED washes (`--blue-soft` etc.) — a wash is not
   its token, since rgba over a ground is a new opaque colour, and that is
   the colour a ratio must be taken against.

   Type scale: 11, 12, 13, 14, 16, 18, 21, 24, 28 and the display clamp
   above it. No half-pixel sizes, nothing under 11px. Durations and easings
   are literals or this file's own tokens — never `var(--dur-quick, 160ms)`,
   since tokens.css is not loaded and the fallback would be the real value used.
   ================================================================================= */

/* ---------------------------------------------------------------- tokens: DARK */
/* :root is dark and .theme-light is light, the application's convention —
   login.html's boot script asks the DEVICE when nothing is stored, since
   login sits between a page that defaults light and an app that defaults
   dark and belongs to neither. */
:root{
  --bg:#0a1628;
  --surface:#101f36;
  --surface-2:#16283f;
  --surface-3:#1b2f4a;

  --border:#294263;
  --border-mid:#395379;
  --border-strong:#4e71a5;

  --text:#f4f6fb;
  --text-2:#a9b6cc;
  --text-3:#8390a5;

  /* Three blue roles, not one — white on #3b82f6 is 3.68:1 and every primary button
     in the application failed on it. --blue-fill is the one that carries white text
     (4.52:1); --blue-text is the one that IS text (6.57:1 on the card). */
  --blue:#3b82f6;
  --blue-fill:#1e6ff5;
  --blue-text:#70a4f8;
  --blue-soft:rgba(59,130,246,.18);
  --blue-line:rgba(59,130,246,.42);

  --cyan:#7dd3fc;
  --cyan-soft:rgba(125,211,252,.20);
  --green:#17b35e;

  --ring:#3b82f6;

  /* ---- THE SCENIC PANEL, DARK. sys_branding.md's own brand values, used
     here and nowhere else. The panel is a GROUND, so every number was
     chosen against what the text inside it has to reach: `--sc-haze`/
     `--sc-cyan` composite to the panel's lightest ground; `--sc-ridge-*`
     are lighter than the base in this theme, since darker left a
     silhouette with nowhere to go against so dark a base. The light block
     reads backwards for the same reason — there the base is pale, so a
     ridge must go darker, and the panel pays for it by darkening --text-3. */
  --sc-haze:rgba(15,111,255,.28);
  --sc-cyan:rgba(56,189,248,.16);
  --sc-base-top:#0e2036;
  --sc-base-mid:#0b1b30;
  --sc-base-bot:#0a172a;
  --sc-ridge-far:#1a3152;
  --sc-ridge-near:#0f2036;

  --sh-sm:0 4px 14px rgba(0,0,0,.22);
  --sh:0 10px 30px rgba(0,0,0,.34);
  --sh-lg:0 24px 60px rgba(0,0,0,.46);
  /* The primary button's shadow is tinted with its own colour, not black — a black
     shadow under a saturated fill reads as dirt. */
  --sh-blue:0 4px 12px rgba(30,111,245,.18);

  /* ---- Added for this page: landing.css has no violet, red or green wash,
     since it has no form, refusal or live checklist. Each measured on the
     two grounds this page actually uses. ---- */

  /* VIOLET — the Team colour, shared by the principle icon, the flow node
     and the Team Board tile so the three are visibly one thing.
     #a78bfa on the card = 6.07:1 · on the page ground = 6.66:1. */
  --violet:#a78bfa;
  /* The wash composites to #283055; --violet on it = 4.69:1. */
  --violet-soft:rgba(167,139,250,.16);

  /* RED — a refusal, and nothing else. Never a border, never a wash on an
     unfinished field: an unmet password rule is not an error yet.
     #ff9b9b on the card = 8.19:1; the wash composites to #313044, red on
     it = 6.36:1. */
  --red:#ff9b9b;
  --red-soft:rgba(255,155,155,.14);
  --red-line:rgba(255,155,155,.38);

  /* GREEN — a met requirement and a confirmation. --green is landing's,
     6.01:1 on the card; --green-text is the brighter tick at 12px.
     #34d17c on the card = 8.30:1; the wash composites to #153840, green
     on it = 6.32:1. */
  --green-text:#34d17c;
  --green-soft:rgba(52,209,124,.14);
  --green-line:rgba(52,209,124,.36);

  /* --border-strong is not a control tier: do not "tidy" --field-line back
     to it. --border-strong is mirrored from CSS/landing.css unchanged,
     measured there for a DECORATIVE edge, and fails WCAG 1.4.11's 3:1
     control floor in light mode (1.94:1). Changing the shared token here
     would make this file disagree with landing.css under the same name —
     so this page adds its own control tier instead.
     #5c81b3 on the dark card = 4.13:1. */
  --field-line:#5c81b3;

  /* The flow connector is a meaningful graphical object (WCAG 1.4.11, 3:1)
     — the only thing turning four list items into one sequence. The
     mirrored --blue-line cannot reach the floor at any usable alpha, so
     this is a solid token instead, and solid rather than dashed since
     dashes remove half the ink the ratio is measured on.
     #5578ac on the page ground = 4.03:1 (on the card = 3.67:1). */
  --flow-line:#5578ac;
  /* The signal that travels the connector — decoration, not a meaning-
     bearing graphic (the LINE carries the meaning), so tuned to read as
     light rather than to a contrast floor. */
  --flow-pulse:#8fc4ff;
  --flow-halo:rgba(143,196,255,.30);

  /* --blue-hover fails under white in dark mode: do not "tidy" --blue-press
     back to it. --blue-hover is mirrored from landing.css, where it is only
     ever a hover for text/borders; here the primary button is filled with
     it and carries white, which drops to 3.59:1 (below 4.5:1) against the
     resting state's 4.52:1. The fix deepens rather than brightens, with a
     stronger tinted shadow saying "hover" instead.
     white on #1a63e0 = 5.38:1. */
  --blue-press:#1a63e0;
}

/* --------------------------------------------------------------- tokens: LIGHT */
:root.theme-light, body.theme-light{
  --bg:#e7ebf2;
  --surface:#ffffff;
  --surface-2:#f2f5fa;
  --surface-3:#e9eef7;

  --border:#dbe3ef;
  --border-mid:#c8d3e4;
  --border-strong:#aebbd0;

  --text:#0d1b2e;
  --text-2:#45536a;
  --text-3:#5d6a7e;

  --blue:#1d63f0;
  --blue-fill:#1d63f0;
  --blue-text:#1550cf;
  --blue-soft:rgba(29,99,240,.10);
  --blue-line:rgba(29,99,240,.30);

  --cyan:#0e8fb8;
  --cyan-soft:rgba(14,143,184,.12);
  --green:#157a3d;

  --ring:#1550cf;

  /* ---- THE SCENIC PANEL, LIGHT, deliberately quieter than the dark one.
     Measured, not aesthetic: --text-3 on the light page ground is 4.59:1,
     nine hundredths above AA, so this theme has almost no room to darken
     anything before the text on it fails — the dark theme has room to
     spare. So the light panel starts ABOVE the page ground rather than
     below it, the washes run at roughly a third of the dark alpha, and the
     panel darkens its own --text-3 to #4e5c72 to pay for its ridges:
     worst case 5.49:1, both clear of AA with the margin the page ground
     could not give. */
  --sc-haze:rgba(15,111,255,.10);
  --sc-cyan:rgba(56,189,248,.10);
  --sc-base-top:#ffffff;
  --sc-base-mid:#f4f7fc;
  --sc-base-bot:#eef3fa;
  --sc-ridge-far:#e6edf8;
  --sc-ridge-near:#dfe8f6;

  --sh-sm:0 1px 2px rgba(16,32,56,.05), 0 4px 14px rgba(16,32,56,.06);
  --sh:0 10px 30px rgba(16,32,56,.10);
  --sh-lg:0 24px 60px rgba(16,32,56,.16);
  --sh-blue:0 4px 12px rgba(29,99,240,.16);

  /* ---- the same additions, re-measured on white ---- */
  /* #6d28d9 on the card #ffffff = 7.10:1 · on the page ground #e7ebf2 = 5.94:1. */
  --violet:#6d28d9;
  /* Wash composites to #f0eafb; --violet on it = 6.04:1. */
  --violet-soft:rgba(109,40,217,.10);

  /* #b3261e on the card = 6.54:1. Wash composites to #f8ebeb, red on it = 5.63:1. */
  --red:#b3261e;
  --red-soft:rgba(179,38,30,.09);
  --red-line:rgba(179,38,30,.30);

  /* #0f6a33 on the card = 6.71:1. Wash composites to #e7f0eb, green on it = 5.77:1. */
  --green-text:#0f6a33;
  --green-soft:rgba(15,106,51,.10);
  --green-line:rgba(15,106,51,.30);

  /* The light half of --field-line — the mirrored --border-strong fails
     the control floor here too (1.94:1 against WCAG 1.4.11's 3:1); see the
     dark block. #7d8da8 on the card = 3.36:1. */
  --field-line:#7d8da8;
  /* The connector, re-measured on the light ground, where alpha could not
     have worked at any value either; see the dark block.
     #66768f on the page ground = 3.86:1 (on the card = 4.61:1). */
  --flow-line:#66768f;
  /* Deeper on a light ground — a pale pulse on near-white is invisible. */
  --flow-pulse:#1d63f0;
  --flow-halo:rgba(29,99,240,.22);
  /* Light mode's --blue-hover was already safe (6.11:1); restated under
     this page's own name so the primary's hover reads ONE token in both
     themes rather than --blue-press in one and --blue-hover in the other. */
  --blue-press:#1857dc;
}

/* --------------------------------------------------------------------- shared */
:root{
  /* Mirrored from landing.css so the door's corners are the site's. */
  --r-sm:8px;
  --r:10px;
  --r-lg:14px;
  --r-xl:18px;
  --r-pill:999px;

  --t-fast:180ms;
  --t:280ms;
  /* The application's signature curve (CSS/tokens.css), reused verbatim
     so motion here feels like motion there. */
  --ease:cubic-bezier(.22,.7,.28,1);

  --display:'Space Grotesk', system-ui, -apple-system, sans-serif;
  --body:'Inter', system-ui, -apple-system, sans-serif;
  --mono:'JetBrains Mono', ui-monospace, monospace;

  --gutter:32px;
  /* A different name because it is a different number: CSS/landing.css's
     --measure is a 1280px reading column, this is a three-column
     composition wanting 1560px. The same name with a different value in
     the two files is exactly the drift the mirrored token block exists
     to prevent, so where this page agrees with landing.css it mirrors the
     name AND value; where it needs a different number it takes a new name. */
  --page-max:1560px;

  /* The auth card's width, one place, since three breakpoints refer to it. */
  --card-w:468px;
  /* The flow's node size and the gap between two steps — the connector's
     arithmetic is derived from these, so changing one does not leave a
     line ending in mid-air. */
  --node:44px;
  --node-gap:22px;
  /* One cycle, declared once, rather than six coupled numbers across two media blocks. */
  --flow-cycle:6400ms;

  /* The layer scale, mirrored from CSS/tokens.css — this page does not
     load that file, so a raw z-index here is the drift the scale exists
     to prevent. Only the rungs this page uses are restated. */
  --z-sticky:20;
}

/* ----------------------------------------------------------------------- reset */
/* html/body margin, the ground, the body font and [hidden] are all in
   login.html's critical block. What is left would only be unstyled,
   rather than visibly wrong, if this file were slow. */
*, *::before, *::after{ box-sizing:border-box; }

body{
  font-size:16px;
  line-height:1.5;
  /* A horizontal scrollbar on a sign-in page is a layout failure. The
     real defence is minmax(0,1fr) and min-width:0 throughout; this is
     the belt, not the braces. */
  overflow-x:hidden;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
}

a{ color:inherit; text-decoration:none; }
button{ font-family:inherit; font-size:inherit; color:inherit; }
img, svg{ max-width:100%; }
h1, h2, h3{ font-family:var(--display); margin:0; letter-spacing:-.015em; }
p, ul, ol{ margin:0; }
ul, ol{ padding:0; list-style:none; }

/* The icon convention, in one rule — the same conventions altIcon() sets
   for every icon in the product. Setting stroke and size here rather than
   on forty elements is the only reason that stays true. */
.i{
  width:18px; height:18px; flex:0 0 auto;
  fill:none; stroke:currentColor; stroke-width:1.9;
  stroke-linecap:round; stroke-linejoin:round;
}

/* Visually hidden, still read aloud — the password checklist's state word
   is the whole reason the list is accessible, since a tick and a circle
   are the same shape to a screen reader. */
.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;
}

/* ================================================================== skip link */
/* It jumps to #authCard, which is the reason anybody is on this page — not to a
   generic "main content" that would land the reader back at the story column. */
.lg-skip{
  /* From the scale, not a number: the skip link outranks the page header,
     so it takes the sticky rung plus one, the same expression landing.css
     uses for its own skip link. A literal here is the first step of the
     99999 spiral the scale exists to stop. */
  position:absolute; left:16px; top:-100px; z-index:calc(var(--z-sticky) + 1);
  padding:12px 18px; border-radius:var(--r-sm);
  background:var(--blue-fill); color:#fff;
  font-family:var(--body); font-size:14px; font-weight:600;
  box-shadow:var(--sh);
  transition:top var(--t-fast) var(--ease);
}
.lg-skip:focus{ top:16px; }

/* ================================================================== the page */
/* A column: header, composition, footer. `min-height` uses 100dvh where
   supported so the footer is not pushed under a phone's collapsing URL bar.

   Nothing on this page is vertically centred in the viewport, and that is
   the point: a centred flex child on a viewport SHORTER than the card
   overflows equally in both directions, and the top overflow is
   unreachable, since no scroll container scrolls above its own start — an
   email field above the window with no way to reach it. So `min-height`,
   never `height`; the composition is top-aligned and the page grows
   downward. If a centred overlay ever returns here, the auto-margin
   technique that collapses to zero is the one to use — `align-items:center`
   is the trap it avoids.

   The safe area is honoured on all three sides that can be intruded on —
   `max()` rather than an addition, so the ordinary case is still --gutter. */
.lg-page{
  max-width:var(--page-max);
  margin:0 auto;
  min-height:100vh;
  min-height:100dvh;
  display:flex;
  flex-direction:column;
  padding-left:max(var(--gutter), env(safe-area-inset-left));
  padding-right:max(var(--gutter), env(safe-area-inset-right));
  padding-bottom:max(20px, env(safe-area-inset-bottom));
}

/* ================================================================== the header */
.lg-head{
  display:flex; align-items:center; justify-content:space-between;
  gap:16px; min-height:72px; padding:14px 0;
  flex:0 0 auto;
}

.lg-brand{
  display:flex; align-items:center; gap:11px;
  /* The wordmark is a link home, under the touch floor without this.
     Unconditional rather than per-breakpoint: the header row is 72px, so
     this costs nothing at any width and cannot be forgotten at one. */
  min-height:44px;
  /* Never shrink — a shrinkable flex item with nowrap text goes narrower
     than its own wordmark and paints outside its box. */
  flex:0 0 auto; min-width:0;
  border-radius:var(--r-sm);
}
/* The mark is an <img> now, and the sizing is restated here rather than
   left to width/height attributes, since a flex row can still squeeze one. */
.lg-brand-mark{ width:34px; height:34px; flex:0 0 auto; }
.lg-brand-txt{ display:flex; flex-direction:column; min-width:0; }
/* The wordmark is live text — sys_branding.md's lockup rule: themeable,
   selectable, scalable, reaches a screen reader, none of which a baked-in
   image would. Positive letter-spacing since STRIDE is set in caps, which
   close up at 16px without it. */
.lg-brand-name{
  font-family:var(--display); font-weight:700; font-size:16px; line-height:1.15;
  letter-spacing:.06em; color:var(--text); white-space:nowrap;
}
/* `.lg-brand-tag` is gone, not merely unused — it carried the COMPANY's
   line under the COMPANY's name, and sys_branding.md allows this page only
   two brand statements, the scenic panel's and the footer's lockup line. */

.lg-head-right{ display:flex; align-items:center; gap:16px; flex:0 1 auto; min-width:0; }

/* The worded way back to the public site — a logo is not a link to
   everybody, and this page is the one place a visitor may have arrived by accident. */
.lg-home{
  display:inline-flex; align-items:center; gap:7px;
  min-height:44px; padding:0 12px; border-radius:var(--r-sm);
  font-size:14px; font-weight:500; color:var(--text-2); white-space:nowrap;
  transition:color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.lg-home:hover{ color:var(--text); background:var(--surface-2); }

/* The theme control. Two segments, matching the application — Light and
   Dark, no System option, which would be a second answer JS/core/theme.js
   already gives. */
.lg-theme{ display:flex; align-items:center; gap:9px; }
.lg-theme > .lbl{ font-size:13px; font-weight:500; color:var(--text-3); white-space:nowrap; }
.lg-seg{
  display:flex; align-items:center; gap:2px; padding:3px;
  background:var(--surface-2); border:1px solid var(--border);
  border-radius:var(--r-pill);
}
.lg-seg button{
  display:inline-flex; align-items:center; justify-content:center;
  width:38px; height:32px; padding:0;
  border:1px solid transparent; border-radius:var(--r-pill);
  background:transparent; color:var(--text-3); cursor:pointer;
  transition:background var(--t-fast) var(--ease), color var(--t-fast) var(--ease),
             border-color var(--t-fast) var(--ease);
}
.lg-seg button .i{ width:15px; height:15px; }
.lg-seg button:hover{ color:var(--text); }
/* The selected segment is a RAISED CHIP as well as aria-pressed — never colour alone,
   and the state is in the accessibility tree, not only on the screen. */
.lg-seg button[aria-pressed="true"]{
  background:var(--surface); color:var(--blue-text); border-color:var(--border-mid);
  box-shadow:var(--sh-sm);
}

/* =============================================================== the composition
   Three columns: the story, the auth card, the flow. Placed by NAMED
   AREAS rather than `order`, since the two-column shape needs the auth
   card to SPAN two rows beside a stacked story and flow, which `order`
   cannot express. DOM order (story, auth, flow) is untouched, so a screen
   reader and the tab sequence read the same at every width.

   `minmax(0,…)` on every track, without exception — a bare `minmax(340px,…)`
   cannot shrink below its own floor on a narrow phone. */
.lg-main{
  flex:1 1 auto;
  display:grid;
  /* Symmetric side tracks, so the card is at optical centre: a narrower
     flow track moved the middle track off centre, and the card is the
     one thing a reader's eye goes to first. */
  grid-template-columns:minmax(0,1fr) minmax(0,var(--card-w)) minmax(0,1fr);
  grid-template-areas:'story auth flow';
  gap:44px;
  align-items:start;
  padding:24px 0 40px;
}
/* .lg-story takes its grid-area in its own block below, with the panel it now is. */
.lg-auth{ grid-area:auth; min-width:0; }
.lg-flow{ grid-area:flow; min-width:0; }

/* ================================================ LEFT: the story, on the scenic panel
   A scenic brand surface on one side, a focused form on the other. The
   panel is the STORY COLUMN and not the page, for contrast: the light
   page ground leaves --text-3 nine hundredths of a point above AA, so a
   bounded panel owns its own ground and gives its text that margin back.

   The panel re-declares one token and nothing else — --text-3, once per
   theme. --text and --text-2 cleared the panel's grounds too with room to
   spare, so re-declaring them would be churn pretending to be rigour. */
.lg-story{
  grid-area:story; min-width:0;
  position:relative;
  /* The panel takes the row's height, which is the card's — `.lg-main` is
     `align-items:start`, so without this the scene had a stub of panel
     below the last principle, the skyline a smudge behind one line of
     text. Cannot strand anything: this is a grid item growing inside a
     row the card already defines, not a box centred in the viewport. */
  align-self:stretch;
  padding:30px 30px 34px;
  border:1px solid var(--border);
  border-radius:var(--r-xl);
  overflow:hidden;
}
/* Measured on the panel's own grounds, not the page's, worst case first:
     dark  #9aa8c0 on the lightest composited wash   4.93:1, on the far ridge 5.44:1
     light #4e5c72 on the darkest composited wash    5.70:1, on the near ridge 5.49:1
   Confirmed in a headless browser afterwards, not left as arithmetic. */
:root .lg-story{ --text-3:#9aa8c0; }
:root.theme-light .lg-story, body.theme-light .lg-story{ --text-3:#4e5c72; }

/* The story's content sits above the scene without a z-index: `.lg-scenic`
   is absolutely positioned and first in the DOM, so making its siblings
   positioned is enough for document order to decide the paint. */
.lg-story > :not(.lg-scenic){ position:relative; }

/* ------------------------------------------------------------------ the scene
   Three layers, one element: the sky is the element's own background,
   the two ridges are its pseudos. No image, no external request, nothing
   for the CSP to refuse.

   The ridges are `clip-path` polygons rather than a gradient, since a
   ridge needs a hard edge against the sky. A browser that ignores
   clip-path paints two plain bands instead — less pretty, still correct,
   since the bands are the colours the contrast numbers were taken against.

   Nothing here moves: a drifting scene on a sign-in page is motion under
   a password field, so the reduced-motion block has nothing to turn off. */
.lg-scenic{
  position:absolute; inset:0;
  pointer-events:none;
  background:
    radial-gradient(120% 85% at 12% -10%, var(--sc-haze) 0%, transparent 60%),
    radial-gradient(90% 70% at 100% 6%,   var(--sc-cyan) 0%, transparent 62%),
    linear-gradient(180deg, var(--sc-base-top) 0%, var(--sc-base-mid) 55%, var(--sc-base-bot) 100%);
}
.lg-scenic::before,
.lg-scenic::after{
  content:''; position:absolute; left:0; right:0; bottom:0;
}
/* The far ridge: two dominant summits with long shoulders between them.
   The shape is the point — evenly spaced peaks of one amplitude read as a
   sawtooth, not a skyline. Few peaks, unequal heights, long slopes. */
.lg-scenic::before{
  height:42%;
  background:linear-gradient(180deg, var(--sc-ridge-far) 0%, var(--sc-ridge-near) 100%);
  clip-path:polygon(0% 82%, 7% 66%, 14% 74%, 26% 30%, 34% 56%, 41% 48%, 52% 70%,
                    63% 24%, 72% 54%, 82% 44%, 91% 62%, 100% 50%, 100% 100%, 0% 100%);
}
/* The near ridge: shorter, sharper, one flat tone. */
.lg-scenic::after{
  height:26%;
  background:var(--sc-ridge-near);
  clip-path:polygon(0% 70%, 11% 52%, 20% 66%, 33% 42%, 45% 68%, 57% 50%, 69% 72%,
                    82% 46%, 92% 64%, 100% 56%, 100% 100%, 0% 100%);
}

.lg-headline{
  font-weight:700;
  font-size:clamp(26px, 2.7vw, 34px);
  line-height:1.08;
  color:var(--text);
  margin:0 0 16px;
}
/* The promise carries the blue — a gradient text-fill was rejected for
   the landing page since it fails at 200% zoom in Safari; same reason here. */
.lg-headline .hl{ color:var(--blue-text); }
.lg-lede{
  font-size:16px; line-height:1.6; color:var(--text-2);
  max-width:44ch; margin:0 0 26px;
}

.lg-principles{ display:flex; flex-direction:column; gap:16px; }
.lg-principles li{ display:flex; align-items:flex-start; gap:13px; min-width:0; }

/* The icon chip. One treatment, four tints — and the tint is the same one the flow
   step and the destination tile for the same idea wear. */
.lg-pico{
  display:inline-flex; align-items:center; justify-content:center;
  width:34px; height:34px; flex:0 0 auto;
  border-radius:var(--r-sm);
  background:var(--surface-2); border:1px solid var(--border);
  color:var(--text-2);
}
.lg-pico.blue  { background:var(--blue-soft);   border-color:var(--blue-line);   color:var(--blue-text); }
.lg-pico.cyan  { background:var(--cyan-soft);   border-color:var(--cyan);        color:var(--cyan); }
.lg-pico.violet{ background:var(--violet-soft); border-color:var(--violet);      color:var(--violet); }
.lg-pico.green { background:var(--green-soft);  border-color:var(--green-line);  color:var(--green-text); }

.lg-ptxt{ display:flex; flex-direction:column; gap:2px; min-width:0; }
.lg-ptxt b{
  font-family:var(--display); font-weight:700; font-size:14px; line-height:1.25;
  letter-spacing:-.005em; color:var(--text);
}
/* Not italic — <i> is the caption element the markup happened to use;
   slanting it would be the tag deciding the design. */
.lg-ptxt i{ font-style:normal; font-size:13px; line-height:1.45; color:var(--text-3); }

/* ======================================================= CENTRE: the auth card
   The visual focus of the page: strongest surface, strongest action, the
   only thing with a signature wash on it. */
.lg-auth{ display:flex; justify-content:center; }
.lg-card{
  width:100%; max-width:var(--card-w);
  /* The skip link's target — breathing room, not header clearance, since
     `.lg-head` is a normal flex item, not sticky. */
  scroll-margin-top:24px;
  padding:32px 30px 28px;
  border-radius:var(--r-xl);
  border:1px solid var(--border);
  /* The landing page's own hero wash — one gesture saying this is the
     same product without repeating its graphic. */
  background:
    radial-gradient(ellipse 620px 420px at 88% 18%, var(--blue-soft), transparent 68%),
    var(--surface);
  min-width:0;
}
/* Shadows are for things that float (landing.css). In dark mode a card on
   navy is separated by its border and lighter ground already; in light
   mode a near-white card on a tinted ground needs the smallest one. */
:root.theme-light .lg-card, body.theme-light .lg-card{ box-shadow:var(--sh-sm); }

.lg-card-head{ margin:0 0 20px; }
.lg-card-title{
  font-weight:700; font-size:24px; line-height:1.15; letter-spacing:-.02em;
  color:var(--text); margin:0 0 6px;
}
.lg-card-sub{ font-size:14px; line-height:1.45; color:var(--text-2); margin:0; }

/* ------------------------------------------------------------------ the tabs */
/* Underline, not a segmented pill: the tabs sit directly above the form
   they change. `display:flex`, nothing `!important`, so `hidden` still
   hides the whole row when the forced-password-change panel takes over. */
.lg-tabs{
  display:flex; align-items:stretch; gap:4px;
  border-bottom:1px solid var(--border);
  margin:0 0 22px;
}
.lg-tab{
  position:relative;
  flex:0 1 auto; min-width:0;
  padding:0 4px; margin:0 10px 0 0;
  min-height:44px;
  background:none; border:0; cursor:pointer;
  font-family:var(--body); font-size:14px; font-weight:500; line-height:1.2;
  color:var(--text-3);
  transition:color var(--t-fast) var(--ease);
}
.lg-tab:hover{ color:var(--text-2); }
/* The bar lives on the tab, not travelling between them, so it cannot be
   left stranded when the row wraps. */
.lg-tab::after{
  content:''; position:absolute; left:0; right:0; bottom:-1px; height:2px;
  background:var(--blue);
  border-radius:2px 2px 0 0;
  transform:scaleX(0);
  transition:transform var(--t) var(--ease);
}
/* Three signals, only one of them colour: the bar, the heavier weight,
   and aria-selected. */
.lg-tab.active{ color:var(--blue-text); font-weight:700; }
.lg-tab.active::after{ transform:scaleX(1); }
/* Tighter than the page floor because the tab's own hit box is flush to its label. */
.lg-tab:focus-visible{ outline:2px solid var(--ring); outline-offset:1px; border-radius:var(--r-sm); }

/* ------------------------------------------------------------------ the form */
.lg-form{ display:flex; flex-direction:column; gap:16px; min-width:0; }

.lg-field{ display:flex; flex-direction:column; gap:6px; min-width:0; }
/* The label: mono, uppercase, letter-spaced, the same kind-label language
   the application uses. --text-2, not the faint tier — 11px is the floor
   of the scale, and muted colour at the floor is where contrast maths
   stops predicting what a person can read.

   `:not(.lg-hint):not(.lg-pw-wrap)` is a real measured defect, not caution:
   `.lg-field > span` tied `.lg-hint` on specificity, and `.lg-pw-wrap` is
   also a direct child span — it WRAPS THE PASSWORD INPUT, so uppercase and
   letter-spacing inherited onto it, and pressing "Show" displayed the
   typed password in uppercase. The value itself was never altered, but
   the one control for proof-reading what was typed showed a different string.

   Excluded by name rather than `:first-child`, which would break silently
   the first time a field gained a wrapper. `> label` alongside it, since
   the two password fields use a real <label> rather than one that wraps
   the reveal button and the hint into the field's accessible name. */
.lg-field > span:not(.lg-hint):not(.lg-pw-wrap),
.lg-field > label{
  font-family:var(--mono); font-size:11px; font-weight:500;
  letter-spacing:.09em; text-transform:uppercase;
  color:var(--text-2);
}

.lg-field input[type="email"],
.lg-field input[type="password"],
.lg-field input[type="text"]{
  width:100%; min-width:0; max-width:100%;
  height:46px; padding:0 14px;
  border-radius:var(--r-sm);
  /* The control tier, not --border: an input's boundary is a non-text
     contrast requirement (WCAG 1.4.11, 3:1); --border is decorative. */
  border:1px solid var(--field-line);
  background:var(--surface);
  color:var(--text);
  font-family:var(--body);
  /* 16px, always, at every width — below it iOS Safari zooms the page on
     focus and does not zoom back, leaving the reader typing a password
     into a page scrolled sideways. The font size is the fix, not a
     viewport restriction, which would take zoom away from everybody. */
  font-size:16px;
  line-height:1.4;
  transition:border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.lg-field input::placeholder{ color:var(--text-3); opacity:1; }
.lg-field input:hover{ border-color:var(--border-strong); }
/* Focus is the border and a soft ring, with the page's own :focus-visible
   outline still on top for keyboard users — no `outline:none` anywhere. */
.lg-field input:focus{
  border-color:var(--blue);
  box-shadow:0 0 0 2px var(--blue-soft);
}
.lg-field input:focus-visible{ outline:2px solid var(--ring); outline-offset:1px; }

/* A one-time code — read off a phone or mail client and typed here, so it
   gets a mono-and-spaced treatment, larger and centred: a short value in
   a full-width box otherwise reads as waiting for something longer.

   One rule for both boxes deliberately — the same control asked twice, so
   a second class would be two places for "what a code looks like" to
   drift. No new colour, no new token: it inherits from
   `.lg-field input[type="text"]` and changes only type, size, spacing and
   alignment, so every ratio already measured holds unchanged. */
.lg-field input.lg-pin{
  font-family:var(--mono);
  font-size:24px;
  font-weight:500;
  letter-spacing:.3em;
  /* THE LAST CHARACTER'S TRACKING IS STILL DRAWN, so a centred string sits half a space
     left of centre. Half the tracking back is the standard correction. */
  text-indent:.3em;
  text-align:center;
}
.lg-pin::placeholder{ letter-spacing:.12em; font-size:18px; }

/* Show what I typed — the button shares the field's box, so the input
   needs room for it, which depends on whether the word is showing. */
.lg-pw-wrap{ position:relative; display:block; min-width:0; }
.lg-field .lg-pw-wrap input{ padding-right:92px; }
.lg-pw-eye{
  position:absolute; right:5px; top:50%; transform:translateY(-50%);
  display:inline-flex; align-items:center; gap:6px;
  height:36px; padding:0 9px;
  border:1px solid transparent; border-radius:var(--r-sm);
  background:transparent; color:var(--text-3); cursor:pointer;
  font-family:var(--body); font-size:13px; font-weight:500; line-height:1;
  transition:color var(--t-fast) var(--ease), background var(--t-fast) var(--ease),
             border-color var(--t-fast) var(--ease);
}
.lg-pw-eye:hover{ color:var(--text); background:var(--surface-2); border-color:var(--border); }
.lg-pw-eye:focus-visible{ outline:2px solid var(--ring); outline-offset:1px; }
.lg-pw-eye .i{ width:17px; height:17px; }
/* The word and icon are both in the button; CSS decides which shows. A
   real visible label above 480px, since a reveal control labelled only by
   an eye is one many people do not press; below that the icon carries it
   alone, since 92px of a 320px field is a third of the box. */
.lg-pw-word{ white-space:nowrap; }

/* The policy hint — a one-line summary above the live checklist, which
   states every rule in full, so it must not compete with the label above
   or the list below. --text-3 on the card: 5.11:1 dark, 5.49:1 light. */
.lg-hint{
  display:block;
  font-size:12px; line-height:1.45; color:var(--text-3);
}

/* First name / last name, side by side. minmax(0,1fr), because a text input's
   intrinsic minimum is wider than half a phone. */
.lg-name-row{
  display:grid; grid-template-columns:repeat(2, minmax(0,1fr)); gap:12px; min-width:0;
}

/* ------------------------------------------------- the live password checklist */
/* Neutral until met, never red — a rule not yet reached is not a mistake.
   Grey circle to green tick, and the .sr-only word beside each is what a
   screen reader actually reads. `display:flex` with no `!important`, so
   `hidden` still hides it on the sign-in tab. */
.lg-pw-reqs{
  display:flex; flex-wrap:wrap; gap:6px 18px;
  padding:12px 14px; margin:0;
  border-radius:var(--r-sm);
  background:var(--surface-2); border:1px solid var(--border);
}
.pw-req{
  display:flex; align-items:center; gap:7px; min-width:0;
  font-size:12px; line-height:1.4; color:var(--text-3);
  transition:color var(--t-fast) var(--ease);
}
.pw-req-mark{
  display:inline-flex; align-items:center; justify-content:center;
  width:14px; flex:0 0 auto;
  font-size:12px; line-height:1; color:var(--text-3);
  transition:color var(--t-fast) var(--ease);
}
.pw-req-label{ min-width:0; }
.pw-req.is-met{ color:var(--text-2); }
.pw-req.is-met .pw-req-mark{ color:var(--green-text); }

/* ------------------------------------------------ remember me, forgot password */
.lg-row-between{
  display:flex; align-items:center; justify-content:space-between;
  flex-wrap:wrap; gap:8px 14px; min-width:0;
}
/* A native checkbox, on purpose — sys_design-system.md §14a's one stated
   exception to the switch standard, since the switch needs a second
   stylesheet on the critical path of the one screen that must render
   instantly. `accent-color` paints it in the page's blue. */
.lg-remember{
  display:inline-flex; align-items:center; gap:9px;
  min-height:44px; min-width:0; cursor:pointer;
  font-size:13px; line-height:1.4; color:var(--text-2);
}
.lg-remember input[type="checkbox"]{
  width:17px; height:17px; flex:0 0 auto; margin:0;
  accent-color:var(--blue);
  cursor:pointer;
}
.lg-remember input:focus-visible{ outline:2px solid var(--ring); outline-offset:2px; }

/* The quiet text action — a <button> in the markup, an <a> nowhere, so it
   has to shed the button's chrome first. */
.lg-link{
  display:inline-flex; align-items:center; gap:6px;
  min-height:44px; padding:0; min-width:0;
  background:none; border:0; cursor:pointer;
  font-family:var(--body); font-size:13px; font-weight:600; line-height:1.4;
  color:var(--blue-text); text-align:left;
  transition:color var(--t-fast) var(--ease);
}
.lg-link:hover{ color:var(--blue); text-decoration:underline; text-underline-offset:3px; }
.lg-link:focus-visible{ outline:2px solid var(--ring); outline-offset:2px; border-radius:var(--r-sm); }
.lg-link.strong{ font-weight:700; }
/* The registration-help link — quieter than an accent action, since it is
   a detour for a reader already stuck. */
.lg-link.quiet{
  font-weight:500; color:var(--text-3);
  text-decoration:underline; text-decoration-color:var(--border-strong);
  text-underline-offset:3px;
}
.lg-link.quiet:hover{ color:var(--text-2); text-decoration-color:currentColor; }

/* ------------------------------------------------------ the anti-abuse check */
/* Cloudflare Turnstile, and the whole job of this block is that nothing
   moves. The widget draws nothing in the ordinary case and then, only for
   a reader Cloudflare wants something from, inserts a frame directly
   above the submit button — the reserve below is derived from `--ts-h`
   rather than typed twice, for the same reason `.lg-status` reserves its
   own line boxes.

   Reserved from the moment the flow is ARMED, not from the moment
   something appears: the wrapper carries `hidden` until login.html arms
   it, so a switched-off Turnstile costs this page no space at all.

   No new colour, no new token, nothing animates here. */
.lg-turnstile{
  /* 71px is the MEASURED height, not Cloudflare's published 65 — the
     frame actually lays out at 71 in this host, and reserving the
     published number left the card jumping 6px at the exact moment a
     challenge appears. turnstile-login.test.js asserts the reserve
     against the same measurement rather than a constant copied twice. */
  --ts-h:71px;
  display:flex; flex-direction:column; gap:6px; min-width:0;
}
.lg-turnstile-lbl{
  font-size:11px; font-weight:600; letter-spacing:.06em; text-transform:uppercase;
  color:var(--text-3);
}
/* The state word, nested inside the label so the host group's accessible
   name picks it up with no extra ARIA. The label is uppercased and
   letter-spaced; the state is not, since it is a sentence fragment. */
.lg-turnstile-state{
  text-transform:none; letter-spacing:0; font-weight:500;
  color:var(--text-3);
}
.lg-turnstile-state.done { color:var(--green-text); font-weight:600; }
.lg-turnstile-state.act  { color:var(--text);       font-weight:600; }
.lg-turnstile-state.stuck{ color:var(--red); }
.lg-turnstile-host{
  /* `min-height`, not `height`: a widget taller than the reserve grows
     the box downward rather than being clipped, the safer failure for a
     control somebody has to be able to press. */
  min-height:var(--ts-h);
  /* Keeps a frame that is somehow still too wide from pushing the page sideways. */
  max-width:100%;
}
.lg-turnstile-host iframe{ max-width:100%; border:0; }

/* ---------------------------------------------------------- the primary action */
/* One class, two buttons: the sign-in submit and the forced-password-
   change submit. `.auth-submit-btn` is the legacy name and is kept —
   support-surfaces.test.js asserts the literal attribute string. */
.auth-submit-btn{
  position:relative;
  display:inline-flex; align-items:center; justify-content:center; gap:10px;
  width:100%; min-height:50px; padding:14px 22px;
  border:1px solid transparent; border-radius:var(--r-sm);
  background:var(--blue-fill); color:#fff;
  box-shadow:var(--sh-blue);
  font-family:var(--body); font-size:16px; font-weight:600; line-height:1.2;
  cursor:pointer;
  transition:background var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease),
             transform var(--t-fast) var(--ease);
}
/* Hover deepens rather than brightens — brightening to --blue-hover would
   put white text at 3.59:1 in dark mode; see --blue-press. */
.auth-submit-btn:hover:not(:disabled){
  background:var(--blue-press);
  box-shadow:0 6px 18px rgba(30,111,245,.28);
  transform:translateY(-1px);
}
.auth-submit-btn:active:not(:disabled){ transform:translateY(0); box-shadow:var(--sh-blue); }
.auth-submit-btn:focus-visible{ outline:2px solid var(--ring); outline-offset:3px; }
/* An explicit disabled pair, not a blanket opacity — a composited opacity
   label was unreadable, even though a disabled control is exempt from
   1.4.3. A named fill and full-opacity label say "not available" legibly.
   --text-2 on --surface-3: 6.60:1 dark, 6.68:1 light.
   Not while busy: login.html sets `disabled` and `.is-busy` together, and
   "Signing in…" is precisely when somebody is staring at the button, so
   busy keeps the full blue fill and says it is working, not unavailable. */
.auth-submit-btn:disabled:not(.is-busy){
  background:var(--surface-3);
  border-color:var(--border-mid);
  color:var(--text-2);
  cursor:not-allowed; transform:none; box-shadow:none;
}
.auth-submit-btn:disabled{ transform:none; }

/* Busy is a state, not a delay — on screen for exactly as long as the
   network call takes. The label stays put; a ring appears beside it. */
.auth-submit-btn.is-busy{ cursor:progress; }
.auth-submit-btn.is-busy::after{
  content:'';
  width:17px; height:17px; flex:0 0 auto;
  border:2px solid rgba(255,255,255,.42);
  border-top-color:#fff;
  border-radius:50%;
  animation:lgSpin 720ms linear infinite;
}
@keyframes lgSpin{ to{ transform:rotate(360deg); } }

/* ------------------------------------------------------------------ the status */
/* One line, announced, and it does not move the page: the height of one
   line is reserved whether or not there is a message, since a refusal
   that pushes the button out from under the cursor is one the reader
   presses through. A class, not an inline colour, so both themes are
   measured here rather than hardcoded from script. */
.lg-status{
  /* One source of truth for the geometry — the reserve below is derived
     from these two, so changing the leading or padding cannot leave it behind. */
  --status-lh:1.5;
  --status-pad-y:10px;

  /* Two line boxes, reserved, empty or not: reserving only one let an
     ordinary refusal — which wraps to two lines at the card's width —
     grow the card and shift everything below the status line. Two, not
     three: the common refusals are two lines; three would be permanent
     dead space for a rare sentence. The reserve includes the padding,
     since `box-sizing:border-box` means min-height is measured over the
     padding box, not just the line boxes alone. */
  --status-border:1px;
  min-height:calc(1em * var(--status-lh) * 2 + var(--status-pad-y) * 2
                  + var(--status-border) * 2);
  /* 8px rather than 14px top and bottom — the reserve itself is untouched;
     only the margins, which are not load-bearing, give back space. */
  margin:8px 0 0;
  font-size:13px; line-height:var(--status-lh); color:var(--text-2);

  /* The full geometry lives on the base and only the colour changes below,
     so empty -> .err -> .ok is a repaint, never a reflow. */
  padding:var(--status-pad-y) 12px;
  border-radius:var(--r-sm);
  /* A hairline all round plus the heavier accent edge, both transparent
     at rest — the same shape as .lg-note, counted into the reserve above. */
  border:var(--status-border) solid transparent;
  border-left-width:3px;
  /* The empty state is reserved space and nothing else — the element
     stays in normal flow, since it is role="status" aria-live="polite". */
  background:transparent;
}
/* Calm, not alarming — a wrong password is the most ordinary thing that
   happens here. Colour only; see the base rule. */
.lg-status.err{ background:var(--red-soft);   border-color:var(--red-line);   border-left-color:var(--red);        color:var(--red); }
.lg-status.ok { background:var(--green-soft); border-color:var(--green-line); border-left-color:var(--green-text); color:var(--green-text); }

/* The "New to HCA?" line. It moves the TAB rather than going anywhere, so there is
   one create-account surface and no dead link. */
.lg-swap{
  margin:8px 0 0;
  font-size:13px; line-height:1.5; color:var(--text-2);
}
.lg-swap .lg-link{ min-height:0; vertical-align:baseline; }

/* The forced-password-change note. A blue wash, though amber was
   available and canonical for the WARNING role elsewhere — declined
   because nothing is wrong at this moment: this panel is only ever
   reached holding a valid session, so an amber wash would say something
   had failed when the reader has simply arrived at a required next step.
   Blue is what the rest of this page says "here is what to do next" in. */
.lg-note{
  padding:14px 16px;
  border-radius:var(--r-sm);
  background:var(--blue-soft);
  border:1px solid var(--blue-line);
  border-left:3px solid var(--blue);
  font-size:13px; line-height:1.55; color:var(--text);
}

/* ================================================ where are you going: the tiles */
.lg-dest{
  margin:24px 0 0; padding:22px 0 0;
  border-top:1px solid var(--border);
}
.lg-dest-h{
  font-family:var(--mono); font-size:11px; font-weight:500;
  letter-spacing:.12em; text-transform:uppercase;
  color:var(--text-3); margin:0 0 12px;
}
.lg-dest-grid{
  display:grid; grid-template-columns:repeat(3, minmax(0,1fr)); gap:10px;
}
.lg-dest-tile{
  position:relative;
  display:flex; flex-direction:column; align-items:flex-start; gap:8px;
  min-width:0; min-height:44px; padding:13px 12px;
  border-radius:var(--r); border:1px solid var(--border);
  background:var(--surface-2); color:var(--text);
  cursor:pointer; text-align:left;
  transition:background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease),
             box-shadow var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.lg-dest-tile:hover{ border-color:var(--border-strong); transform:translateY(-1px); }
.lg-dest-tile:focus-visible{ outline:2px solid var(--ring); outline-offset:2px; }
.lg-dest-ico{
  display:inline-flex; align-items:center; justify-content:center;
  width:32px; height:32px; flex:0 0 auto;
  border-radius:var(--r-sm);
  background:var(--surface); border:1px solid var(--border);
  color:var(--text-2);
}
.lg-dest-ico.blue  { background:var(--blue-soft);   border-color:var(--blue-line);  color:var(--blue-text); }
.lg-dest-ico.violet{ background:var(--violet-soft); border-color:var(--violet);     color:var(--violet); }
.lg-dest-ico.green { background:var(--green-soft);  border-color:var(--green-line); color:var(--green-text); }
.lg-dest-tile b{
  font-family:var(--display); font-weight:700; font-size:13px; line-height:1.2;
  letter-spacing:-.005em; color:var(--text); overflow-wrap:anywhere;
}
.lg-dest-tile i{ font-style:normal; font-size:11px; line-height:1.3; color:var(--text-3); }

/* The selected tile says so three ways: a wash, a heavier blue edge drawn
   with an inset shadow so nothing reflows, and a tick in the corner.
   aria-pressed is the fourth, the one that reaches a screen reader. Both
   selectors, since the class and attribute are set in the same breath. */
.lg-dest-tile.active,
.lg-dest-tile[aria-pressed="true"]{
  background:var(--blue-soft);
  border-color:var(--blue);
  box-shadow:inset 0 0 0 1px var(--blue);
}
/* Both lines lift, not just the title — the caption's --text-3 is measured
   against the card, but a selected tile is the --blue-soft wash instead,
   where it falls to 4.00:1 in dark mode (and Tasks is pre-selected, so
   every reader saw it first). --text-2 on the dark wash = 6.32:1; light = 6.75:1. */
.lg-dest-tile.active b,
.lg-dest-tile[aria-pressed="true"] b{ color:var(--blue-text); }
.lg-dest-tile.active i,
.lg-dest-tile[aria-pressed="true"] i{ color:var(--text-2); }
.lg-dest-tile.active::after,
.lg-dest-tile[aria-pressed="true"]::after{
  content:'\2713';
  position:absolute; top:8px; right:9px;
  font-size:11px; line-height:1; color:var(--blue-text);
}

/* Admin is not a peer of the three — a console most accounts cannot open,
   so it is a quiet full-width row, visibly a different kind of thing.
   Selecting it is a NAVIGATION PREFERENCE and nothing more:
   admin.html asks is_app_admin() after authentication and every function
   behind it enforces membership again. */
.lg-dest-admin{
  display:flex; align-items:center; gap:10px;
  width:100%; min-height:44px; margin:10px 0 0; padding:0 12px;
  border-radius:var(--r-sm);
  border:1px dashed var(--border-mid);
  background:transparent; color:var(--text-3);
  cursor:pointer; text-align:left;
  font-family:var(--body); font-size:13px; font-weight:500;
  transition:color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease),
             background var(--t-fast) var(--ease);
}
.lg-dest-admin span{ flex:1 1 auto; min-width:0; }
.lg-dest-admin .arw{
  color:inherit;
  transition:transform var(--t-fast) var(--ease);
}
.lg-dest-admin:hover{ color:var(--text-2); border-color:var(--border-strong); background:var(--surface-2); }
.lg-dest-admin:hover .arw{ transform:translateX(3px); }
.lg-dest-admin:focus-visible{ outline:2px solid var(--ring); outline-offset:2px; }
.lg-dest-admin.active,
.lg-dest-admin[aria-pressed="true"]{
  border-style:solid; border-color:var(--blue);
  background:var(--blue-soft); color:var(--blue-text);
}

/* Pin it — a real <a> with a real href so the browser's own bookmark
   gestures work on it. Quiet, since it is a power move, not the main task. */
.auth-pin-link{
  display:inline-flex; align-items:center; gap:7px;
  min-height:44px; margin:6px 0 0; min-width:0;
  font-size:12px; line-height:1.4; color:var(--text-3);
  transition:color var(--t-fast) var(--ease);
}
.auth-pin-link .i{ width:15px; height:15px; }
.auth-pin-link:hover{ color:var(--blue-text); }
.auth-pin-link:focus-visible{ outline:2px solid var(--ring); outline-offset:2px; border-radius:var(--r-sm); }
.auth-pin-dest{ font-weight:600; color:var(--text-2); }
.auth-pin-link:hover .auth-pin-dest{ color:inherit; }

/* ============================================================== RIGHT: the flow
   The wireframe's connected sequence, as an ordered list with the
   connector drawn in CSS — a pseudo-element line, not an image, which
   would need two of itself for the two themes and one more request on
   the critical path of a sign-in page. */
.lg-flow-h{
  font-family:var(--mono); font-size:12px; font-weight:500;
  letter-spacing:.12em; text-transform:uppercase;
  color:var(--text-3); margin:0 0 18px;
}
.lg-flow-list{
  display:flex; flex-direction:column; gap:var(--node-gap);
  min-width:0;
}
.lg-flow-step{
  position:relative;
  display:grid; grid-template-columns:var(--node) minmax(0,1fr);
  align-items:start; gap:13px;
  min-height:var(--node); min-width:0;
}
/* The connector runs from this node's centre down into the next one,
   anchored to BOTH ends of the step rather than given a fixed height, so
   a wrapped caption lengthens the line instead of leaving it in mid-air. */
.lg-flow-step::before{
  content:'';
  position:absolute;
  left:calc(var(--node) / 2 - 1px);
  /* Starts at the node's bottom edge, not its centre — the coloured
     variants override the node's opaque base with a translucent wash, so
     starting at the centre drew the line straight through the icon. */
  top:var(--node);
  bottom:calc(var(--node-gap) * -1);
  width:0;
  /* Solid, in its own measured token — see --flow-line. */
  border-left:2px solid var(--flow-line);
  z-index:0;
}
.lg-flow-step:last-child::before{ display:none; }

/* ================= ONE SIGNAL, TASK -> KEY RESULT =================

   The four steps are a SEQUENCE; a single light that runs the whole
   column says "these are one path" in the time it takes to read the
   first caption. One pulse on the LIST, not three on the steps — a pulse
   anchored to each short connector travelled only a few pixels and three
   staggered twitches read as flickering rather than flow; anchored to the
   list, the same pulse travels the full column as one journey.

   It passes behind the nodes: opaque and a stacking level above, so the
   light disappears at each one and re-emerges below it — a signal
   entering a station, the station answering, the signal continuing.

   It runs once per cycle, then rests — a loop with no rest stops reading
   as meaning and starts reading as noise, beside a form people came here
   to fill in. */
.lg-flow-list{ position:relative; }

.lg-flow-list::after{
  content:'';
  position:absolute;
  /* 4px against the connector's 2px, centred on it — see --flow-pulse. */
  left:calc(var(--node) / 2 - 2px);
  /* The pulse's centre traces node centre to node centre — half its
     height subtracted here as well as at the far end, or the flight
     biases low and falls short of the distance between them. */
  top:calc(var(--node) / 2 - 9px);
  width:4px;
  height:18px;
  border-radius:2px;
  background:linear-gradient(to bottom, transparent, var(--flow-pulse) 35%, var(--flow-pulse) 65%, transparent);
  box-shadow:0 0 8px 1px var(--flow-halo);
  opacity:0;
  z-index:0;
  pointer-events:none;
  /* Linear, and the ring delays are why: each ring fires where the pulse
     is only if it covers equal distance in equal time — an eased curve
     put a ring's light well after the pulse had already passed it. */
  animation:lgFlowSignal var(--flow-cycle) linear infinite;
}

/* Translate, not `top` — a layout property that cost measurable main-
   thread time on the one screen with no fallback; `transform` is
   compositor-only. The distance is a constant because the geometry is:
   `translateY(%)` resolves against the element's own box, not the list,
   so the honest form is three gaps of node-plus-gap, which
   login-redesign.test.js asserts the list height stays equal to. */
@keyframes lgFlowSignal{
  0%   { transform:translateY(0); opacity:0; }
  3%   { transform:translateY(0); opacity:1; }
  30%  { transform:translateY(calc(3 * (var(--node) + var(--node-gap)))); opacity:1; }
  34%  { transform:translateY(calc(3 * (var(--node) + var(--node-gap)))); opacity:0; }
  100% { transform:translateY(calc(3 * (var(--node) + var(--node-gap)))); opacity:0; }
}

/* The node answers as the signal reaches it — a ring OUTSIDE the node's
   own edge, so nothing inside the box moves. Its own pseudo-element
   rather than an animated `box-shadow`, since light mode already puts
   `--sh-sm` there. The delays are the pulse's arrival times, not a
   decorative stagger — retiming the flight means retiming these. */
.lg-flow-node::after{
  content:'';
  position:absolute;
  inset:-3px;
  border-radius:calc(var(--r-sm) + 3px);
  border:2px solid currentColor;
  opacity:0;
  pointer-events:none;
  animation:lgFlowArrive var(--flow-cycle) var(--ease) infinite;
  animation-delay:192ms;
}
.lg-flow-step:nth-child(2) .lg-flow-node::after{ animation-delay:768ms; }
.lg-flow-step:nth-child(3) .lg-flow-node::after{ animation-delay:1344ms; }
/* Key Result is the terminus, the one exception to the rule above: the
   other three light when the signal's centre reaches theirs, since the
   signal carries on past them — but nothing carries on from Key Result,
   so the same delay left a dead beat right when the sequence should land.
   A slightly earlier delay keeps the ring's peak inside the flight window. */
.lg-flow-step:nth-child(4) .lg-flow-node::after{ animation-delay:1728ms; }

@keyframes lgFlowArrive{
  0%   { opacity:0; transform:scale(.94); }
  2%   { opacity:.9;  transform:scale(1); }
  11%  { opacity:0; transform:scale(1.07); }
  100% { opacity:0; transform:scale(1.07); }
}

.lg-flow-node{
  position:relative; z-index:1;
  display:inline-flex; align-items:center; justify-content:center;
  width:var(--node); height:var(--node); flex:0 0 auto;
  border-radius:var(--r-sm);
  background:var(--surface); border:1px solid var(--border-mid);
  color:var(--text-2);
}
.lg-flow-node .i{ width:20px; height:20px; }
:root.theme-light .lg-flow-node, body.theme-light .lg-flow-node{ box-shadow:var(--sh-sm); }
/* The wash is composited over an opaque ground, not painted as the whole
   background — every `--*-soft` is an rgba, so the bare value alone would
   leave the node translucent and the connector showing through the icon.
   Each step wears the colour of the destination tile that opens it. */
.lg-flow-step.blue   .lg-flow-node{ background:linear-gradient(var(--blue-soft),var(--blue-soft)), var(--surface);   border-color:var(--blue-line);  color:var(--blue-text); }
.lg-flow-step.cyan   .lg-flow-node{ background:linear-gradient(var(--cyan-soft),var(--cyan-soft)), var(--surface);   border-color:var(--cyan);       color:var(--cyan); }
.lg-flow-step.violet .lg-flow-node{ background:linear-gradient(var(--violet-soft),var(--violet-soft)), var(--surface); border-color:var(--violet);     color:var(--violet); }
.lg-flow-step.green  .lg-flow-node{ background:linear-gradient(var(--green-soft),var(--green-soft)),   var(--surface);  border-color:var(--green-line); color:var(--green-text); }

.lg-flow-txt{ display:flex; flex-direction:column; gap:2px; min-width:0; padding-top:3px; }
.lg-flow-txt b{
  font-family:var(--display); font-weight:700; font-size:14px; line-height:1.25;
  letter-spacing:-.005em; color:var(--text);
}
.lg-flow-txt i{ font-style:normal; font-size:12px; line-height:1.45; color:var(--text-3); }

/* A caption, dressed as one — it previously used the exact grammar of a
   call-to-action button, offering readers a button that does nothing. A
   four-word summary of the list above is now just text: the mono
   uppercase caption voice stays, the fill and border go.
   --blue-text on the page ground: 7.21:1 dark, 5.70:1 light. */
.lg-flow-note{
  margin:16px 0 0; padding:0;
  background:none; border:0;
  font-family:var(--mono); font-size:11px; font-weight:500;
  letter-spacing:.1em; text-transform:uppercase;
  color:var(--blue-text); text-align:left;
}

/* A one-shot reveal, four steps, then over — a sign-in page that keeps
   moving while somebody types their password competes with them. */
.lg-flow-step{ animation:lgFlowIn 460ms var(--ease) both; }
.lg-flow-step:nth-child(1){ animation-delay:60ms; }
.lg-flow-step:nth-child(2){ animation-delay:160ms; }
.lg-flow-step:nth-child(3){ animation-delay:260ms; }
.lg-flow-step:nth-child(4){ animation-delay:360ms; }
@keyframes lgFlowIn{ from{ opacity:0; transform:translateY(6px); } }

/* ================================================================== the footer */
.lg-foot{
  flex:0 0 auto;
  display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between;
  gap:6px 20px;
  padding:20px 0 8px;
  border-top:1px solid var(--border);
}
.lg-foot p{ margin:0; font-size:12px; line-height:1.5; color:var(--text-3); }
/* The lockup line, restrained — sys_branding.md's footer statement. Mono
   and letter-spaced so it reads as a lockup, left at --text-3 so it never
   competes with the card. Written `.lg-foot .lg-foot-*` so it outranks
   `.lg-foot p` above rather than losing the colour to it. */
.lg-foot .lg-foot-tag{
  font-family:var(--mono); letter-spacing:.14em; text-transform:uppercase;
  font-weight:500;
}
/* Who makes this — a step brighter than the rest of the footer, since
   it's the answer somebody is actually looking for here. */
.lg-foot .lg-foot-by{ color:var(--text-2); font-weight:600; }

/* ================================================================== RESPONSIVE
   Five shapes, on the ladder the landing page and the suite share.
   Nothing is hidden at any width — the flow MOVES rather than
   disappearing, since it is the only place explaining what the product is.

   ≥1360   three columns: story · card · flow
   1024-1359  two columns: story over flow on the left, the card spanning both on the right
   640-1023   one column, card FIRST — authentication before explanation
   ≤639    phone: card near-full-bleed, tiles become stacked rows
   ≤379    tightest gutters, must not overflow at 320 */

/* ---- 1024-1359: two columns. The flow moves into the left column,
   under the principles — not hidden, not duplicated, one markup placed
   by a named area, the whole reason this grid uses areas instead of `order`. */
@media (max-width:1359px){
  :root{ --gutter:24px; --card-w:440px; }
  .lg-main{
    grid-template-columns:minmax(0,1fr) minmax(0,var(--card-w));
    grid-template-areas:
      'story auth'
      'flow  auth';
    grid-template-rows:auto 1fr;
    gap:28px 36px;
    align-content:start;
  }
  .lg-flow{ padding-top:4px; }

  /* The left column uses its width instead of leaving it empty: the lede
     is capped at 44ch on purpose, so the fix is not to stretch the text —
     it is to let the two lists go two-up. The connector withdraws with
     them, since in two columns it would cross a row it has nothing to do
     with; the colours and reading order still carry the sequence, and it
     returns at ≤639 where the list is one column again. */
  .lg-principles{ display:grid; grid-template-columns:repeat(2, minmax(0,1fr)); gap:16px 28px; }
  .lg-flow-list{ display:grid; grid-template-columns:repeat(2, minmax(0,1fr)); gap:20px 28px; }
  .lg-flow-step::before{ display:none; }
  .lg-flow-list::after{ display:none; }
  .lg-flow-node::after{ display:none; }
}

/* ---- 640-1023: one column, and the order changes — the card is first
   under the header, the story at the bottom. DOM order is untouched, so
   the tab sequence still runs story -> card -> flow, which is why the
   skip link points at #authCard. */
@media (max-width:1023px){
  .lg-main{
    grid-template-columns:minmax(0,1fr);
    grid-template-areas:
      'auth'
      'flow'
      'story';
    grid-template-rows:none;
    gap:36px;
    padding:20px 0 32px;
  }
  .lg-auth{ justify-content:center; }
  .lg-card{ max-width:520px; }
  /* The two-up lists and the withdrawn connector are inherited from the =<1359 block,
     which covers this width too — they are not restated here. */
  .lg-flow-note{ margin-top:20px; }
  .lg-story{ max-width:720px; margin:0 auto; }
  .lg-lede{ max-width:none; }
  /* The panel is shorter here, so the ridges take proportionally less of
     it — sized in % of the panel, and at one column the story is wide and
     short, where the taller-panel percentage would put a skyline through
     the principles instead of behind them. */
  .lg-scenic::before{ height:34%; }
  .lg-scenic::after{ height:20%; }

}

/* ======================================================== TOUCH SIZING
   By width AND by pointer, since neither alone is right: a tablet in
   portrait is a touch surface at a width a phone query never catches, and
   the same tablet sideways misses a width-only breakpoint by one pixel.
   `(pointer:coarse)` asks the real question — is the input a finger? —
   true at any width; the width half stays because a headless probe
   reports a fine pointer whatever size it emulates. */
@media (max-width:1023px), (pointer:coarse){
  .lg-seg button{ width:48px; height:44px; }
  /* Fills the 46px field without changing it; the word still shows. */
  .lg-pw-eye{ height:44px; }
  /* "Create an account" is the second most likely thing tapped on the page. */
  .lg-swap .lg-link{ min-height:44px; vertical-align:middle; }
}

/* ---- ≤639: the phone. */
@media (max-width:639px){
  :root{ --gutter:16px; }
  .lg-head{ flex-wrap:wrap; gap:10px 12px; min-height:0; padding:12px 0 10px; }
  /* No tagline under the mark — the corner is `[S] STRIDE` at every
     width, the constrained lockup sys_branding.md asks for anyway. */
  .lg-head-right{ gap:8px; margin-left:auto; }
  .lg-theme > .lbl{ display:none; }
  .lg-home{ padding:0 8px; }

  .lg-main{ gap:28px; padding:14px 0 28px; }

  /* Near-full-bleed — the radius steps down to --r-lg, since --r-xl on a
     box this close to the edge reads as a bubble. */
  .lg-card{
    max-width:none;
    padding:24px 18px 22px;
    border-radius:var(--r-lg);
  }
  .lg-card-title{ font-size:21px; }

  /* Touch size, everywhere the finger goes. */
  .lg-field input[type="email"],
  .lg-field input[type="password"],
  .lg-field input[type="text"]{ height:48px; }
  /* The word goes; the icon carries the reveal alone, since the field is
     where the typing happens. Still in the button for the markup. */
  .lg-pw-word{ display:none; }
  /* The reveal fills the field's height to clear the 44px touch floor,
     without changing the field's own height; the input's right padding
     grows with it so the text keeps clearance and never runs underneath. */
  .lg-pw-eye{ right:2px; width:44px; padding:0; justify-content:center; }
  .lg-field .lg-pw-wrap input{ padding-right:52px; }

  .lg-name-row{ grid-template-columns:minmax(0,1fr); }
  .lg-pw-reqs{ flex-direction:column; gap:7px; }
  .lg-row-between{ gap:0 12px; }
  /* The label is the target, not the box — scaling the native tick
     itself to 44px looks wrong and renders coarse; the label already
     carries the 44px hit area via min-height and align-items. */
  .lg-remember input[type="checkbox"]{ width:18px; height:18px; }
  /* The swap action is a word inside a sentence, so it cannot become a
     44px block without breaking the line — inline-flex with a min-height
     grows the LINE BOX instead, not padding plus a negative margin, which
     would push this button's hit area into its neighbour's. */
  .auth-submit-btn{ min-height:52px; }

  /* The tiles become rows, not smaller squares — three tiny squares
     across a phone card leave no room for icon or text; full-width rows
     read as a list of places, which is what they are. */
  .lg-dest-grid{ grid-template-columns:minmax(0,1fr); gap:8px; }
  .lg-dest-tile{
    flex-direction:row; align-items:center; gap:12px;
    min-height:56px; padding:10px 14px;
  }
  .lg-dest-tile b{ font-size:14px; }
  /* The caption goes to the end of the row rather than under the title. */
  .lg-dest-tile i{ margin-left:auto; padding-left:10px; text-align:right; }
  .lg-dest-tile.active::after,
  .lg-dest-tile[aria-pressed="true"]::after{ display:none; }
  /* With no room for a corner tick, the row states it in the flow of the text — still
     a non-colour signal, and still not the only one, because aria-pressed is set. */
  .lg-dest-tile.active b::after,
  .lg-dest-tile[aria-pressed="true"] b::after{ content:' \2713'; color:var(--blue-text); }

  /* The panel gives back its padding before the content gives back
     anything — the radius steps down with the card's for the same reason. */
  .lg-story{ padding:22px 16px 24px; border-radius:var(--r-lg); }

  .lg-flow-list{ display:flex; flex-direction:column; gap:var(--node-gap); }
  /* One column again, so the connector comes back. */
  .lg-flow-step::before{ display:block; }
  .lg-flow-list::after{ display:block; }
  .lg-flow-node::after{ display:block; }
  .lg-flow-step:last-child::before{ display:none; }
  .lg-principles{ grid-template-columns:minmax(0,1fr); }

  .lg-foot{ flex-direction:column; align-items:flex-start; gap:4px; }
}

/* ---- ≤379: the smallest phone still in use. Nothing is hidden and no
   type shrinks — the gutters give back the room instead, checked at 320px. */
@media (max-width:379px){
  :root{ --gutter:12px; }
  .lg-brand-name{ font-size:14px; }
  .lg-brand-mark{ width:30px; height:30px; }
  .lg-card{ padding:20px 14px 18px; }
  .lg-story{ padding:18px 14px 20px; }
  .lg-dest-tile{ padding:10px 12px; }
  /* At 320px the title and the caption cannot share a row without one of them
     truncating, so the caption goes back under the title. */
  .lg-dest-tile{ flex-wrap:wrap; }
  .lg-dest-tile i{ margin-left:0; padding-left:44px; text-align:left; }
  /* The compact widget's height — login.html reads the same 379px
     breakpoint to pick the compact drawing here. Measured, not published
     — see --ts-h; the reserve has to move with the tier or the widget
     lands short of its box. */
  .lg-turnstile{ --ts-h:146px; }   /* measured, not published — see --ts-h above */
}

/* =========================================================== SHORT VIEWPORTS
   Height is a dimension too. Everything above already fails safe — top-
   aligned, min-height not height, document scrolls — so these rules are
   the vertical half of the ladder: when there are only ~360-390 CSS
   pixels of height, the chrome framing the card gives, not the card
   itself. In a landscape phone the grid is already one column with the
   auth card first, so the story and flow give way in reading order.
   Nothing is display:none. */
@media (max-height:640px){
  .lg-head{ min-height:0; padding:10px 0; }
  .lg-main{ gap:22px; padding:12px 0 24px; }
  .lg-card{ padding-top:24px; padding-bottom:22px; }
  .lg-dest{ margin-top:20px; padding-top:18px; }
}

@media (max-height:480px) and (orientation:landscape){
  .lg-head{ padding:6px 0; }
  /* The brand drops to 28px here, deliberately: WCAG 2.5.8 has an
     explicit EQUIVALENT exception when a same-destination 44px control
     sits right beside a smaller one — `.lg-home` is that control. If
     `.lg-home` is ever removed from the header, this override must go with it. */
  .lg-brand{ min-height:0; }
  .lg-brand-mark{ width:28px; height:28px; }
  .lg-main{ gap:16px; padding:4px 0 20px; }
  .lg-flow-note{ margin-top:12px; }
  .lg-foot{ padding:14px 0 6px; }
  /* The scene keeps its colour and loses its padding, like everything
     else in a 390px-tall window — it is last in reading order here, so
     it never costs the card a pixel. */
  .lg-story{ padding:18px 18px 20px; }

  /* Get the submit button above the fold: the subtitle is pure welcome,
     restating the tab beneath it, and is the only thing removed. Type
     does not shrink below the scale, controls do not shrink at all —
     what gives is the spacing between them. */
  .lg-card{ padding-top:14px; }
  .lg-card-head{ margin-bottom:10px; }
  .lg-card-title{ font-size:18px; }
  .lg-card-sub{ display:none; }
  .lg-tabs{ margin-bottom:10px; }
  .lg-form{ gap:10px; }
  .lg-field{ gap:3px; }
}

/* ============================================================== REDUCED MOTION
   One block naming everything that moves on this page, not a sample.
   Settles rather than stopping: content must stay VISIBLE and useful with
   motion off — the flow's four steps use `both` fill, so removing the
   animation is what makes them appear at full opacity; pausing it would
   leave them invisible instead.

   1ms, not 0s: a zero-duration transition does not reliably fire
   `transitionend`, and a rule that silently breaks a future listener is
   worse than one that costs a millisecond. */
@media (prefers-reduced-motion: reduce){
  /* The one-shot reveal: removed, and therefore SHOWN. */
  .lg-flow-step{ animation:none; opacity:1; transform:none; }
  /* The signal goes entirely, not frozen mid-flight — a pulse parked
     halfway down a connector reads as a fault rather than stillness. */
  .lg-flow-list::after{ animation:none; opacity:0; }
  .lg-flow-node::after{ animation:none; opacity:0; }

  /* The busy ring stays on screen as a ring; it stops rotating. */
  .auth-submit-btn.is-busy::after{ animation:none; }

  /* Nothing travels. */
  .auth-submit-btn:hover:not(:disabled){ transform:none; }
  .auth-submit-btn:active:not(:disabled){ transform:none; }
  .lg-dest-tile:hover{ transform:none; }
  .lg-dest-admin:hover .arw{ transform:none; }
  .lg-tab::after{ transition:none; transform:scaleX(0); }
  .lg-tab.active::after{ transform:scaleX(1); }

  /* Everything that transitions on hover, focus, open or state change. */
  .lg-skip,
  .lg-home,
  .lg-seg button,
  .lg-tab,
  .lg-field input,
  .lg-pw-eye,
  .pw-req,
  .pw-req-mark,
  .lg-link,
  .auth-submit-btn,
  .lg-dest-tile,
  .lg-dest-admin,
  .lg-dest-admin .arw,
  .auth-pin-link{
    transition-duration:1ms !important;
  }
}

/* ======================================================================= PRINT
   Nobody prints a sign-in page on purpose. A saved PDF should not be
   four pages of navy. */
@media print{
  .lg-skip, .lg-theme{ display:none !important; }
  body{ background:#fff; color:#000; }
  .lg-card{ background:#fff; box-shadow:none; }
  /* The scene is ink nobody asked for; the panel keeps its border. */
  .lg-scenic{ display:none !important; }
  .lg-story{ background:#fff; }
  .auth-submit-btn.is-busy::after, .lg-flow-step,
  .lg-flow-list::after, .lg-flow-node::after{ animation:none !important; }
  .lg-flow-list::after, .lg-flow-node::after{ opacity:0 !important; }
}
