/* The /login surface, restyled from the design-system tokens (FR-LDS-004, DESIGN.md D1/D3).
 *
 * Hand-authored and token-consuming: every value here is a var(--iep-*) declared in
 * iep-tokens.css, which is projected from specs/DESIGN_SYSTEM.md. A literal fails the build
 * (NFR-MNT-606) — see the README beside this file.
 *
 * SCOPE. `web_include_css` is a website-layer hook: Frappe puts these links on every www/ page,
 * not only /login. Containment is therefore this file's job, and it is spelt in the selector —
 * `body[data-path="login"]`, the attribute base.html writes from the resolved route. Nothing here
 * may be written unscoped.
 *
 * The card's radius and shadow are pinned by FR-LDS-005 AC-2 to --iep-radius-xl and
 * --iep-shadow-float. A first cut of this file set both to different values and shipped a card
 * failing its own stated oracle, which is why those two are now asserted by a test rather than
 * left to review.
 *
 * --iep-canvas and --iep-surface are BOTH #ffffff, so the card has no colour separation from the
 * page in light. The float shadow plus a hairline mixed from the text colour is what draws its
 * edge. The contract's "a surface separates against the canvas" note is scoped to the DARK canvas
 * (#27272a on #18181b) and does not transfer to light.
 *
 * The espresso rules underneath are left in place rather than reset — the class names belong to
 * the vendored template, which this feature does not own (DESIGN.md §4, R-LDS-08). These
 * selectors outrank them on specificity, so they win regardless of link order.
 *
 * ALPHA WITHOUT rgba(). The signature treatments are specified with alpha — aurora at 0.16/0.13,
 * the dot grid at 0.1 — but NFR-MNT-606's lint rejects any rgba()/hex outside the token layer, and
 * rightly so: an rgba() literal here would freeze a colour the theme is supposed to re-point.
 * color-mix() carries the alpha while the HUE stays a var(--iep-*), so the dark block re-points
 * these layers for free. That is the whole point of citing a token rather than a value.
 *
 * ASSET BUILD. A change here does not reach the served page until the asset is rebuilt —
 * `make frappe-reload ASSETS=1 APP=erp_ai_sso`, or an image rebuild. A 404 on this stylesheet is
 * the documented symptom of skipping that step (FR-LDS-012), not a broken rule.
 */

body[data-path="login"] {
  background-color: var(--iep-canvas);
  color: var(--iep-text-strong);
}

/* ── Ambient layers (FR-LDS-005 AC-1) ──────────────────────────────────────────────────────────
 * Pseudo-elements, not markup. BOUNDARIES NEVER 3 forbids editing frappe/www/login.html, so the
 * chatos SPA's `.aurora-bg` / `.dot-grid` DIVs cannot be ported as elements — only as the body's
 * own two pseudo-elements. Both are fixed, inert to the pointer, and sit at z-index 0 under the
 * content lifted to 1 below.
 */

body[data-path="login"]::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image:
    radial-gradient(
      40rem 30rem at 18% 22%,
      color-mix(in srgb, var(--iep-primary) 16%, transparent),
      transparent 70%
    ),
    radial-gradient(
      36rem 28rem at 82% 78%,
      color-mix(in srgb, var(--iep-brand-cyan) 13%, transparent),
      transparent 70%
    );
  /* blur(80px) is the declared treatment value, not a choice made here — see
   * specs/DESIGN_SYSTEM.md § Signature treatments. */
  filter: blur(80px);
  animation: iep-login-aurora-drift var(--iep-duration-ambient) var(--iep-ease-spring) infinite
    alternate;
}

body[data-path="login"]::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image: radial-gradient(
    color-mix(in srgb, var(--iep-text-strong) 10%, transparent) 1px,
    transparent 1px
  );
  background-size: 22px 22px;
  /* An opaque colour is a full-alpha stencil; the mask reads alpha, not hue. Citing the token
   * rather than `black` keeps the literal lint honest about what is and is not a design value. */
  -webkit-mask-image: radial-gradient(
    ellipse 60% 50% at 50% 45%,
    var(--iep-text-strong) 0%,
    transparent 100%
  );
  mask-image: radial-gradient(
    ellipse 60% 50% at 50% 45%,
    var(--iep-text-strong) 0%,
    transparent 100%
  );
}

/* Composited only — transform, never a layout property (NFR-PERF-604). */
@keyframes iep-login-aurora-drift {
  from {
    transform: translate3d(0, 0, 0) scale(1);
  }
  to {
    transform: translate3d(2%, -3%, 0) scale(1.08);
  }
}

/* Lift the page above the two fixed layers.
 *
 * Explicit rather than a negative z-index on the layers: body's background-color propagates to
 * the canvas, so `z-index: -1` would put the wash BEHIND the paint it is supposed to tint.
 *
 * `> *` rather than the two containers by name (`#page-login`, `.web-footer`). Both are vendored,
 * and this rule is the only thing keeping a fixed, full-viewport blurred layer underneath the
 * credential form: a positioned element at z-index 0 paints ABOVE static content, so if either
 * name changed the aurora and the dot grid would render on top of the card. Clicks would still
 * work — the layers are pointer-events: none — which is what makes that failure a quiet one.
 * Selecting every child costs nothing (a <script> takes `position: relative` as a no-op) and owes
 * nothing to a name this feature does not control (R-LDS-08).
 */
body[data-path="login"] > * {
  position: relative;
  z-index: 1;
}

/* ── Credential card (FR-LDS-005 AC-2) ─────────────────────────────────────────────────────── */

body[data-path="login"] .login-content.page-card {
  border-radius: var(--iep-radius-xl);
  box-shadow: var(--iep-shadow-float);
  background-color: var(--iep-surface);
  padding: var(--iep-space-8);
  /* --iep-canvas and --iep-surface are both #ffffff in light, so the float shadow alone leaves the
   * card edgeless on a pale wash. A hairline mixed from the text colour draws it in both renders
   * without introducing a border token the contract does not declare. */
  border: 1px solid color-mix(in srgb, var(--iep-text-strong) 8%, transparent);
  animation: iep-login-fade-up var(--iep-duration-entrance) var(--iep-ease-spring) both;
}

/* ── Product name (FR-LDS-005 AC-3) ────────────────────────────────────────────────────────────
 * The 100deg brand gradient, clipped to the glyphs. Both tokens re-point under [data-theme="dark"],
 * so the treatment themes without a second declaration. The string itself is NOT this task's —
 * A-LDS-03 is open and DEC-68275da8 owns it. Type size is not set here either: the espresso
 * heading size is inherited deliberately, since no AC specifies one and A-LDS-02 leaves the
 * page's composition open.
 *
 * The transparent fill is gated behind @supports, and that gate is load-bearing rather than
 * decorative. `color: transparent` unconditionally would leave the ONLY headline on the page
 * invisible on any engine without background-clip: text — the precise failure NFR-COMP-609
 * forbids ("0 invisible text"). Ungated, the degradation is not a missing gradient, it is a
 * missing heading.
 */

body[data-path="login"] .page-card-head h4 {
  background-image: linear-gradient(100deg, var(--iep-primary) 15%, var(--iep-brand-cyan) 85%);
  -webkit-background-clip: text;
  background-clip: text;
  /* The legible fallback: without clipping support the heading paints solid brand blue. */
  color: var(--iep-primary);
}

@supports (background-clip: text) or (-webkit-background-clip: text) {
  body[data-path="login"] .page-card-head h4 {
    color: transparent;
    -webkit-text-fill-color: transparent;
  }
}

/* ── Form controls (FR-LDS-006) ────────────────────────────────────────────────────────────────
 * Espresso gives the fields `border: none` on a grey `--control-bg`. The token treatment is a
 * surface-coloured field with a hairline and a resting elevation, so the two states read as
 * resting and focused rather than as flat and flat.
 *
 * SPECIFICITY IS LOAD-BEARING HERE, and this is R-LDS-08's accepted exposure made concrete. The
 * rules being overridden are espresso's `.for-login .page-card .page-card-body input[type="…"]`,
 * which scores (0,4,1). A shorter override — `body[data-path="login"] .page-card-body
 * .form-control`, (0,3,1) — LOSES, and loses silently: the page still renders, just with none of
 * the tokens applied. The first cut of this block did exactly that, and only a computed-style
 * read caught it. So each selector below carries `.page-card` plus the element's own class or
 * type, putting it strictly above espresso rather than level with it (a tie would leave the
 * result to stylesheet order, which `web_include_css` does not guarantee).
 *
 * What is deliberately NOT here: `outline: none`. A custom focus shadow invites it, and it would
 * remove the only affordance a keyboard user has — the regression FR-LDS-011 exists to prevent.
 * `test_the_stylesheet_never_suppresses_the_focus_outline` fails the build if it ever appears.
 */

body[data-path="login"] .page-card .page-card-body input.form-control[type] {
  border-radius: var(--iep-radius);
  box-shadow: var(--iep-shadow-input);
  background-color: var(--iep-surface);
  color: var(--iep-text-strong);
  border: 1px solid color-mix(in srgb, var(--iep-text-strong) 12%, transparent);
  transition:
    box-shadow var(--iep-duration-control) var(--iep-ease-spring),
    border-color var(--iep-duration-control) var(--iep-ease-spring);
}

body[data-path="login"] .page-card .page-card-body input.form-control[type]::placeholder {
  color: var(--iep-text-muted);
  opacity: 1;
}

body[data-path="login"] .page-card .page-card-body input.form-control[type]:focus {
  box-shadow: var(--iep-shadow-input-focus);
  border-color: var(--iep-primary);
  background-color: var(--iep-surface);
}

/* The sprite icons and the show/hide toggle. Colour only — position, z-index and the `toggle`
 * attribute the handler reads are espresso's and stay untouched (BOUNDARIES NEVER 9). */

body[data-path="login"] .page-card .page-card-body svg.field-icon {
  fill: var(--iep-text-muted);
}

/* THE ONLY SANCTIONED USE OF !important IN THIS FILE, and both instances share one cause.
 *
 * Frappe's `website.bundle.css` ships Bootstrap's utility as `.text-muted { color: #7c7c7c
 * !important }`. No selector outranks an `!important` from another sheet, so any element carrying
 * that class is unreachable by ordinary means — and #7c7c7c does not clear AA anywhere on this
 * page: 4.02:1 on the light card at the 12px the toggle renders, 4.17:1 for the 14px divider, and
 * 3.57:1 for that divider on the dark card. --iep-text-muted is 7.73:1 light and re-points to
 * #a1a1aa in dark.
 *
 * So the keyword buys an accessibility fix, not a specificity shortcut, and it is confined to
 * `color` on the two controls that carry `.text-muted`. Anything else on this page is beatable by
 * raising the selector; test_important_is_confined_to_the_text_muted_overrides fails the build if
 * a third one appears or if one lands on a property other than colour. */
body[data-path="login"] .page-card .page-card-body .password-field span.toggle-password {
  color: var(--iep-text-muted) !important;
}

/* FR-LDS-009, NFR-USE-601. Found by T-2609's rendered audit, not by reading the token table — which is the
 * whole argument for running one: before this rule the divider cited no token at all, so both the
 * design-system gate and the generator's forward lint had nothing to look at. Measured #7c7c7c at
 * 4.17:1 on the light card and 3.57:1 on the dark one, against a 4.5 floor. */
body[data-path="login"] .page-card .page-card-body p.login-divider {
  color: var(--iep-text-muted) !important;
}

body[data-path="login"] .page-card .page-card-body .password-field span.toggle-password:hover {
  color: var(--iep-primary) !important;
}

body[data-path="login"] .page-card .page-card-body .forgot-password-message > * {
  color: var(--iep-text-muted);
}

/* Error state. `login.set_status()` adds `.invalid` here and espresso paints --red-500 (#e03636),
 * which is neither token. --iep-danger-text is 4.83:1 on the surface and passes AA;
 * --iep-danger-fill would be 3.76:1 and fails it (BR-LDS-003, BOUNDARIES NEVER 4). */

body[data-path="login"] .page-card .page-card-body.invalid input.form-control[type] {
  border-color: var(--iep-danger-text);
}

/* ── Action hierarchy (FR-LDS-007) ─────────────────────────────────────────────────────────────
 * One primary fill on the page, and it belongs to the credential submit. Espresso paints
 * `.btn.btn-primary` with its own near-black `--btn-primary`, which is a perfectly good button and
 * the wrong one: it is not in this product's palette, and it gives the page a default action that
 * does not look like the product's default action.
 *
 * The SSO button and the email-link button are SECONDARY — surface fill, token hairline, token
 * text. `login_sso_buttons.js` injects `.btn-sso` as the card's first child when a provider is
 * configured and emits nothing at all when none is, so the empty case needs no collapse rule
 * (AC-2); the wrapper below therefore sets spacing only, never a height.
 *
 * NOT USED HERE, deliberately: --iep-primary-hover. It has no dark counterpart in the SSOT (no
 * `Re-points onto` row), so under [data-theme="dark"] it would keep its LIGHT value and darken
 * toward the dark canvas. The backlog routed that gap to /change and BOUNDARIES NEVER 11 forbids
 * closing it from a Phase-4 task, so the primary button carries no hover fill for now — one
 * missing hover beats a wrong one that T-2609 would find in the dark render.
 */

body[data-path="login"] .page-card .page-card-actions button.btn-primary.btn-login {
  background-color: var(--iep-primary);
  color: var(--iep-on-primary);
  border-radius: var(--iep-radius);
  border: 1px solid var(--iep-primary);
  box-shadow: var(--iep-shadow-input);
}

/* ── Keyboard focus indicator (FR-LDS-011) ─────────────────────────────────────────────────────
 * ONE rule scoped to the card, covering element TYPES rather than a list of controls — the shape
 * `app-rail-theme.css` set. A per-control list is what rots: the control added next month is the
 * one nobody adds to the list, and the miss is invisible to anyone using a mouse.
 *
 * `:focus-visible`, not `:focus` — a pointer click draws nothing (AC-2). The input elevation on
 * plain `:focus` above is a different thing: clicking into a text field should show it took focus.
 *
 * No dark override. `--iep-focus-ring` re-points to #60a5fa under [data-theme="dark"] in the token
 * layer, so one citation themes both renders. Naming --iep-focus-ring-dark here would hard-code
 * the dark render against a name the light rule never uses — the error FR-LDS-008 AC-4 spells out
 * for --iep-canvas.
 *
 * `[role="button"]` is the fourth selector, and it is why this list is by TYPE rather than by tag.
 * The show/hide toggle is a <span>: Frappe ships it with no tabindex, so until 2026-08-01 it
 * received no focus and this rule had nothing to reach. `login_a11y.js` now promotes it with
 * `tabindex="0"` + `role="button"`, and matching the ROLE means any control promoted that way is
 * covered the moment it is promoted — rather than needing this rule edited to name it.
 */

body[data-path="login"] .page-card a:focus-visible,
body[data-path="login"] .page-card button:focus-visible,
body[data-path="login"] .page-card input:focus-visible,
body[data-path="login"] .page-card [role="button"]:focus-visible {
  outline: 2px solid var(--iep-focus-ring);
  outline-offset: 2px;
}

/* The two secondary actions: the injected provider button (`.btn-sso`, this app's own class from
 * login_sso_buttons.js) and Frappe's email-link button. They share ONE rule rather than two
 * matching ones — "secondary is one look" should be structural, not a comment asking the next
 * editor to keep two blocks in step. */
body[data-path="login"] .page-card a.btn-sso,
body[data-path="login"] .page-card .page-card-body a.btn-login-option {
  border-radius: var(--iep-radius);
  background-color: var(--iep-surface);
  color: var(--iep-text-strong);
  border: 1px solid color-mix(in srgb, var(--iep-text-strong) 12%, transparent);
  box-shadow: var(--iep-shadow-input);
}

body[data-path="login"] .page-card a.btn-sso:hover,
body[data-path="login"] .page-card .page-card-body a.btn-login-option:hover {
  border-color: var(--iep-primary);
  color: var(--iep-primary);
}

/* Spacing only — a height here would reserve the slot the script leaves empty (AC-2). */
body[data-path="login"] .page-card .chatos-sso-buttons {
  margin-bottom: var(--iep-space-4);
}

/* The house `fade-up` entrance (FR-LDS-010 AC-3) is declared on the card rule above rather than in
 * a second rule for the same selector — one element, one block. `both` holds the start frame so
 * the card does not flash at full opacity before the animation begins. */

@keyframes iep-login-fade-up {
  from {
    opacity: 0;
    transform: translate3d(0, 12px, 0);
  }
  to {
    opacity: 1;
    transform: translate3d(0, 0, 0);
  }
}

/* ── Reduced motion (FR-LDS-010, BOUNDARIES NEVER 7) ───────────────────────────────────────────
 * Motion goes; DECORATION stays. The aurora and the dot grid keep rendering — someone who asked
 * for less motion did not ask for a plainer page — so nothing here hides a layer, zeroes an
 * opacity or drops a background.
 *
 * `.invalid-login` is espresso's, not ours: a 0.5s `wiggle` it plays on every failed sign-in
 * (login.bundle.scss). A reduced-motion preference is a statement about the page, not about who
 * authored which rule, and answering it costs one selector.
 *
 * scripts/tests/test_login_motion.py DERIVES the set of animated selectors from this file and
 * requires each to appear below, so the next animation added here cannot quietly skip this block.
 */

@media (prefers-reduced-motion: reduce) {
  body[data-path="login"]::before,
  body[data-path="login"] .login-content.page-card,
  body[data-path="login"] .login-content.page-card.invalid-login {
    animation: none;
  }
}
