/* Idana AI Assist — login surface.
 *
 * Sections 1-6 are the delivered specification's values, carried over 1:1 from
 * `handoff/login-standalone/login.css`. It calls itself a "verbindliche visuelle
 * Spezifikation" and asks not to be reinterpreted, so where a number here looks
 * arbitrary it is because it is theirs: the 18px card radius, the 840px card, the two
 * overlay gradients, the single 700px breakpoint.
 *
 * Section 7 is ours — the states the handoff explicitly leaves open (its §7, "Verhalten
 * (noch offen, bitte anbinden)"): field errors, loading, alerts, and the screens beyond
 * sign-in that the handoff does not cover. They follow the same idiom rather than
 * inventing a second one.
 *
 * THREE DEPARTURES FROM THE HANDOFF, EACH REQUIRED
 *
 *  1. NO fonts.googleapis.com. The handoff links it; carrying that over would transmit
 *     every visitor's IP to a third party on the first screen they see, before any
 *     consent interaction exists (FR-103). The faces are self-hosted — see
 *     scripts/fetch_fonts.mjs.
 *  2. WebP heroes with a JPEG fallback. The delivered PNGs are 6.6 MB and 6.2 MB; served
 *     as-is they would consume the entire sign-in latency budget on their own.
 *  3. AN OPAQUE COLOUR UNDER THE HERO. The handoff relies on the photograph for
 *     contrast. Its overlay is semi-transparent, so if the image is slow or never
 *     arrives, white text lands on the page background and becomes unreadable. The
 *     gradient therefore sits on an opaque base.
 *
 * TWO THINGS THAT MUST NOT BE TOUCHED
 *
 *  - `.gsi-material-button*` is Google's own code. Not renamed, not restyled, no Idana
 *    colours, no 8px radius, no Inter, height fixed at 40px. It is a branding-guideline
 *    requirement, and it is the one thing the vendor login could not deliver at all.
 *  - The hero overlay opacities. They were tuned so white text keeps >= 4.5:1 against
 *    the photograph, and both heroes have text-safe zones the headline sits in. Changing
 *    an overlay silently breaks a contrast requirement — see assets/README.md.
 */
@import url('./fonts/fonts.css');

/* ── 1. Tokens ───────────────────────────────────────────────────────────────────
 * The real Idana design-system values, not the handoff's standalone excerpt. Two
 * differences from that excerpt, both deliberate:
 *   - `--primary-200` is declared. The excerpt uses it in `.btn-link-account:hover` but
 *     never defines it, so that hover had no border colour at all.
 *   - the headline family is Source Sans 3 first. The Source Sans Pro .ttf files the
 *     product ships cover no Cyrillic and no Greek — read out of their cmap, not
 *     assumed — so Bulgarian and Greek headlines rendered in a system face. */
:root {
  --font-headline: 'Source Sans 3', 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif;
  --font-body: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;

  --primary: #277691;
  --primary-50: #d4e4e9;
  --primary-100: #b7d1da;
  --primary-200: #93bbc8;
  --primary-600: #216279;
  --primary-700: #1a4f61;
  --primary-800: #143b49;
  --primary-900: #0d2730;

  --neutral-50: #f8f9fa;
  --neutral-200: #dee2e6;
  --neutral-300: #ced4da;
  --neutral-400: #adb5bd;

  --fg-1: #343a40;
  --fg-2: #6c757d;
  --fg-3: #adb5bd;

  --danger: #c62828;
  --danger-bg: #fdecea;
  --danger-fg: #8a1f1f;
  --danger-border: #f3c4c0;
}

* { box-sizing: border-box; }

/* Present in the document, absent from the page. Used for the long-form SSO labels, which
 * Google's branding spec wants present while the visible label stays short enough to fit two
 * buttons side by side in a 420px column.
 *
 * NOT `display: none`: this page's CSP forbids inline style attributes, which is how the
 * handoff hid them, and clip-rect is the technique that survives a screen reader as well. */
.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;
}

body {
  margin: 0;
  font-family: var(--font-body);
  color: var(--fg-1);
  background: linear-gradient(168deg, #0d2730 0%, #143b49 55%, #1a4f61 100%);
  min-height: 100vh;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 20px;
}
::selection { background: var(--primary-100); }
a { color: var(--primary); text-decoration: underline; }
a:hover { color: var(--primary-600); }

/* Visible focus everywhere, on top of the design's own focus styling. The handoff sets
 * `outline:none` on the Google button and on the inputs; without a replacement, keyboard
 * navigation becomes invisible. */
:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.hero :focus-visible { outline-color: #fff; }

/* ── 2. The card ───────────────────────────────────────────────────────────────── */
.card {
  margin: auto;
  width: 100%;
  max-width: 840px;
  background: #fff;
  border-radius: 18px;
  box-shadow: 0 30px 70px rgba(8, 24, 29, .34);
  overflow: hidden;
  display: grid;
  /* `minmax(0, 1fr)` rather than the handoff's bare `1fr`, and this is enforcing its intent
   * rather than departing from it: §1 says both halves must be EXACTLY equal in width. A bare
   * `1fr` is `minmax(auto, 1fr)`, so a wide child can push its column past the split — which
   * is what happened live, where the form column grew to 610px against the hero's 230px and
   * the headline wrapped after two words. `minmax(0, …)` makes the halves equal whatever they
   * contain. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  animation: lgUp .5s cubic-bezier(.22, .61, .36, 1) both;
}
@keyframes lgUp {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) { .card { animation: none; } }

/* ── 3. Hero ───────────────────────────────────────────────────────────────────── */
.hero {
  position: relative;
  /* Departure 3: the opaque base. Everything above it may fail to load. */
  background-color: var(--primary-900);
  background-image:
    linear-gradient(168deg, rgba(7, 26, 33, .84) 0%, rgba(9, 34, 42, .7) 44%, rgba(6, 22, 28, .9) 100%),
    image-set(url('./assets/signin-hero.webp') type('image/webp'), url('./assets/signin-hero.jpg') type('image/jpeg'));
  background-size: cover, cover;
  background-position: center, 50% 40%;
  padding: 40px 36px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 26px;
  color: #fff;
}
/* An <a> since 2026-07-29 (owner request): the logo leads back to the product landing
 * page. Same box as before; the link must not restyle the wordmark. */
.lock { display: inline-flex; align-items: center; gap: 9px; position: relative; text-decoration: none; color: inherit; }
.lock:hover { opacity: .9; }
.lock img { height: 22px; width: auto; filter: brightness(0) invert(1) drop-shadow(0 1px 6px rgba(6, 20, 25, .55)); }
.lock > span {
  font-family: var(--font-headline);
  font-size: 15px;
  font-weight: 600;
  padding-left: 9px;
  border-left: 1px solid rgba(255, 255, 255, .34);
  text-shadow: 0 1px 8px rgba(6, 20, 25, .55);
}
.claim { position: relative; }
.claim h1 {
  font-family: var(--font-headline);
  font-size: 26px;
  font-weight: 600;
  line-height: 1.15;
  margin: 0 0 12px;
  color: #fff;
  text-shadow: 0 1px 14px rgba(6, 20, 25, .6);
}
.claim p {
  font-size: 14.5px;
  color: #dceaef;
  line-height: 1.6;
  margin: 0;
  text-shadow: 0 1px 12px rgba(6, 20, 25, .6);
}
.claim b { color: #fff; font-weight: 600; }
.claim .hint { display: block; margin-top: 1.2em; }

.trustwrap { position: relative; display: flex; flex-direction: column; gap: 14px; }
.trust { display: flex; align-items: center; gap: 13px; }
.tbadge {
  flex: none;
  position: relative;
  width: 46px;
  height: 46px;
  border-radius: 999px;
  background: rgba(255, 255, 255, .12);
  border: 1px solid rgba(255, 255, 255, .24);
  display: grid;
  place-items: center;
}
.tbadge .stars { position: absolute; inset: 0; width: 100%; height: 100%; fill: #ffd75e; opacity: .95; }
.tbadge .glyph { width: 19px; height: 19px; fill: none; stroke: #fff; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.ttext { display: flex; flex-direction: column; min-width: 0; line-height: 1.35; }
.ttext b { font-size: 14px; font-weight: 600; color: #fff; text-shadow: 0 1px 10px rgba(6, 20, 25, .6); }
.ttext > span { font-size: 12.5px; color: #bcd4dc; margin-top: 2px; }

/* ── 4. Form ───────────────────────────────────────────────────────────────────── */
.form { padding: 40px 38px; display: flex; flex-direction: column; justify-content: center; }

label.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--fg-2);
  margin-bottom: 14px;
}
label.field input {
  border: 1px solid var(--neutral-300);
  border-radius: 8px;
  padding: 11px 13px;
  font: inherit;
  font-size: 14px;
  font-weight: 400;
  color: var(--fg-1);
  outline: none;
  transition: border-color .16s, box-shadow .16s;
}
label.field input:focus { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(39, 118, 145, .2); }

.remember {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 8px 14px;
  margin: -6px 0 22px;
}
.remember label { display: inline-flex; align-items: center; gap: 7px; font-size: 12.5px; color: var(--fg-2); cursor: pointer; white-space: nowrap; }
.remember input { accent-color: var(--primary); width: 14px; height: 14px; }
.remember a { font-size: 12.5px; font-weight: 600; white-space: nowrap; }

.btn-primary {
  width: 100%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  background: var(--primary);
  color: #fff;
  border: none;
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  padding: 13px;
  border-radius: 8px;
  cursor: pointer;
  box-shadow: 0 8px 20px rgba(39, 118, 145, .28);
  transition: background .16s;
}
.btn-primary:hover { background: var(--primary-600); }
.btn-primary svg { width: 13px; height: 13px; fill: none; stroke: #fff; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }

.sep { display: flex; align-items: center; gap: 12px; margin: 18px 0; }
.sep i { flex: 1; height: 1px; background: var(--neutral-200); }
.sep > span { font-size: 12px; color: var(--fg-3); }

.signup { text-align: center; font-size: 13px; color: var(--fg-2); margin: 18px 0 0; }

/* ── 5. Third-party sign-in — GOOGLE'S OWN CODE, DO NOT RESTYLE ─────────────────── */
.sso { display: flex; gap: 10px; }
.sso > .gsi-material-button { flex: 1 1 0; min-width: 0; }
.gsi-material-button {
  -webkit-user-select: none;
  user-select: none;
  -webkit-appearance: none;
  background-color: #fff;
  background-image: none;
  border: 1px solid #747775;
  border-radius: 4px;
  box-sizing: border-box;
  color: #1f1f1f;
  cursor: pointer;
  font-family: 'Roboto', arial, sans-serif;
  font-size: 14px;
  height: 40px;
  letter-spacing: .25px;
  outline: none;
  overflow: hidden;
  padding: 0 12px;
  position: relative;
  text-align: center;
  transition: background-color .218s, border-color .218s, box-shadow .218s;
  vertical-align: middle;
  white-space: nowrap;
  width: 100%;
}
.gsi-material-button .gsi-material-button-icon { height: 20px; margin-right: 10px; min-width: 20px; width: 20px; }
.gsi-material-button .gsi-material-button-icon svg { display: block; }
.gsi-material-button .gsi-material-button-content-wrapper {
  align-items: center;
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  height: 100%;
  justify-content: space-between;
  position: relative;
  width: 100%;
}
.gsi-material-button .gsi-material-button-contents {
  flex-grow: 1;
  font-family: 'Roboto', arial, sans-serif;
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: top;
}
.gsi-material-button .gsi-material-button-state { transition: opacity .218s; bottom: 0; left: 0; opacity: 0; position: absolute; right: 0; top: 0; }
.gsi-material-button:not(:disabled):active .gsi-material-button-state,
.gsi-material-button:not(:disabled):focus .gsi-material-button-state { background-color: #303030; opacity: 12%; }
.gsi-material-button:not(:disabled):hover { box-shadow: 0 1px 2px 0 rgba(60, 64, 67, .30), 0 1px 3px 1px rgba(60, 64, 67, .15); }
.gsi-material-button:not(:disabled):hover .gsi-material-button-state { background-color: #303030; opacity: 8%; }

/* ── 6. The Idana account sign-in button ────────────────────────────────────────
 * Visually separated from the generic third-party buttons on purpose: it opens
 * the familiar Idana account sign-in. The realm may offer account linking on
 * first use; subsequent uses follow the permanent link as ordinary sign-in. */
.btn-link-account {
  width: 100%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  background: var(--primary-50);
  border: 1px solid var(--primary-100);
  color: var(--primary-800);
  font: inherit;
  font-size: 14.5px;
  font-weight: 600;
  padding: 12px;
  border-radius: 8px;
  cursor: pointer;
  margin-top: 10px;
  transition: background .16s, border-color .16s;
}
.btn-link-account:hover { background: var(--primary-100); border-color: var(--primary-200); }
.btn-link-account img { flex: none; }

/* ── 7. Ours: the open states, and the screens beyond sign-in ────────────────────
 *
 * ONE alert style per screen and no variants. On sign-in it carries the single outcome
 * shared by five different account conditions, so there is deliberately no "warning" or
 * "info" flavour to reach for: a second style invites a second message, and two
 * distinguishable messages are an account-existence oracle (FR-110). */
.alert {
  margin: 0 0 16px;
  padding: 10px 12px;
  border-radius: 8px;
  background: var(--danger-bg);
  border: 1px solid var(--danger-border);
  color: var(--danger-fg);
  font-size: 13px;
  line-height: 1.5;
}
.notice {
  margin: 0 0 16px;
  padding: 10px 12px;
  border-radius: 8px;
  background: var(--primary-50);
  border: 1px solid var(--primary-100);
  color: var(--primary-800);
  font-size: 13px;
  line-height: 1.5;
}
.alert[hidden], .notice[hidden] { display: none; }

label.field.is-invalid input { border-color: var(--danger); }
label.field.is-invalid input:focus { box-shadow: 0 0 0 3px rgba(198, 40, 40, .18); }

/* NEUTRAL by default, and red only when the field is actually invalid.
 *
 * It was unconditionally red, which made the password-rule hint on the registration screen read
 * as a rejection before anything had been typed. Same element, two meanings — "here is what is
 * required" and "what you typed is wrong" — and only the second one is an error. */
.fieldhelp { font-size: 12px; font-weight: 400; color: var(--fg-2); }
label.field.is-invalid .fieldhelp { color: var(--danger); }

/* Loading state on the primary button, driven by login.js. Without JS the button stays
 * as it is and the browser's own navigation indicator does the work — which is why this
 * is an enhancement and not the only feedback. */
.btn-primary[aria-busy='true'] { background: var(--primary-600); cursor: progress; }
.btn-primary[aria-busy='true'] svg { animation: spin 1s linear infinite; transform-origin: 50% 50%; }
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .btn-primary[aria-busy='true'] svg { animation: none; } }

/* A secondary action that is not a provider: "use a different method", "back", "skip".
 * Quieter than .btn-link-account on purpose, so the Suite path stays the only emphasised
 * alternative on the sign-in screen. */
.btn-secondary {
  width: 100%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  background: #fff;
  border: 1px solid var(--neutral-300);
  color: var(--fg-1);
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  padding: 11px;
  border-radius: 8px;
  cursor: pointer;
  margin-top: 8px;
  transition: background .16s, border-color .16s;
}
.btn-secondary:hover { background: var(--neutral-50); border-color: var(--neutral-400); }

/* Second-factor enrolment. The TOTP secret is rendered for manual entry BESIDE the QR
 * code, not instead of it: the QR is generated here, offline, because handing the shared
 * secret to a QR service would put it in a third party's request log — the single worst
 * place for it. Monospace and letter-spaced because it is transcribed by hand from a
 * screen into a phone, where 0/O and 1/l decide between working and "invalid code". */
.totp { display: flex; flex-direction: column; align-items: center; gap: 14px; margin-bottom: 18px; }
.totp svg { width: 188px; height: 188px; background: #fff; border: 1px solid var(--neutral-200); border-radius: 8px; padding: 8px; }
.totp code {
  font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
  font-size: 14px;
  letter-spacing: .12em;
  word-break: break-all;
  text-align: center;
  background: var(--neutral-50);
  border: 1px solid var(--neutral-200);
  border-radius: 6px;
  padding: 8px 10px;
  color: var(--fg-1);
}
.totp p { margin: 0; font-size: 12.5px; color: var(--fg-2); text-align: center; line-height: 1.5; }

/* The enrolment options, rendered from what the instance actually permits — so this is a
 * container with no assumption about how many children it has. */
.methods { display: flex; flex-direction: column; gap: 10px; }

/* The recommended (passkey) enrolment option. Grouped so the badge and the one-line reason
 * travel with the button, and revealed together by login.js only when WebAuthn is possible. */
.method-recommended { display: flex; flex-direction: column; gap: 4px; }
.method-recommended .btn-primary { display: flex; align-items: center; justify-content: center; gap: 10px; }
.badge-recommended {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .02em;
  text-transform: uppercase;
  padding: 2px 8px;
  border-radius: 999px;
  /* On the primary button, so it reads as a light chip against the filled background. */
  background: rgba(255, 255, 255, .22);
  color: #fff;
}
.method-hint { margin: 0 2px; font-size: 12.5px; line-height: 1.45; color: var(--fg-2); }

/* Read-only summary of what a provider told us, on the completion screen. Shown so the
 * person can see WHY they are being asked for the rest, rather than facing an
 * unexplained form after a successful third-party sign-in. */
.fromprovider {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 18px;
  padding: 10px 12px;
  border-radius: 8px;
  background: var(--neutral-50);
  border: 1px solid var(--neutral-200);
  font-size: 13px;
  color: var(--fg-2);
}
.fromprovider strong { color: var(--fg-1); font-weight: 600; }

/* A button that reads as a link. Used where the action changes server state — resending a
 * code, stepping back a screen — and therefore has to be a submit button rather than an
 * anchor, but should not look like a third call to action. */
.linklike {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-size: 13px;
  color: var(--primary);
  text-decoration: underline;
  cursor: pointer;
}
.linklike:hover { color: var(--primary-600); }

/* Language picker. ISO codes only — the owner's decision: 26 endonyms in a dropdown is
 * unreadable, and a flag is a country, not a language.
 *
 * A native <details> rather than a <select>, because this sits inside the credentials form:
 * a named select would be posted along with the password, and a nested form to avoid that
 * is invalid HTML. <details> also opens with no JavaScript at all. */
.langbar { margin: 18px 0 0; text-align: center; font-size: 12.5px; }
.langbar summary {
  display: inline-block;
  cursor: pointer;
  color: var(--fg-2);
  border: 1px solid var(--neutral-300);
  border-radius: 6px;
  padding: 4px 10px;
  list-style: none;
}
.langbar summary::-webkit-details-marker { display: none; }
.langbar summary::after { content: ' \25BE'; }
.langbar[open] summary { border-color: var(--primary); color: var(--primary); }
.langlist {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px 10px;
  margin-top: 10px;
}
.langlist a { color: var(--fg-2); text-decoration: none; padding: 2px 4px; border-radius: 4px; }
.langlist a:hover { color: var(--primary); background: var(--neutral-50); }
.langlist a[aria-current='true'] { color: var(--primary); font-weight: 600; }

/* ── The foot of the form column: legal links, with the dev language picker beside them ──
 * The sign-in belongs to the product, so Datenschutz/AGB/Impressum are one click away
 * (owner request 2026-07-29). Deliberately quiet: 12.5px, muted, underline only on hover. */
.legalrow { margin: 18px 0 0; text-align: center; }
.legallinks { font-size: 12.5px; color: var(--fg-2); }
.legallinks a { color: var(--fg-2); text-decoration: none; padding: 2px 2px; border-radius: 4px; }
.legallinks a:hover { color: var(--primary); text-decoration: underline; }
.legalrow .langbar { margin-top: 8px; }

/* The sign-in/registration switch, moved to the very top of the form column — above even
 * the alert region — so someone on the wrong screen sees the way over first. */
.topswitch { margin: 0 0 10px; text-align: right; }

/* ── 8. Breakpoint — the ONLY one, per the specification ────────────────────────── */
@media (max-width: 700px) {
  .card { grid-template-columns: 1fr; }
  .hero {
    min-height: 200px;
    padding: 22px;
    background-image:
      linear-gradient(100deg, rgba(6, 22, 28, .93) 0%, rgba(7, 26, 33, .86) 52%, rgba(6, 22, 28, .62) 100%),
      image-set(url('./assets/signin-hero-mobile.webp') type('image/webp'), url('./assets/signin-hero-mobile.jpg') type('image/jpeg'));
    background-position: center, center;
  }
  .claim h1 { font-size: 22px; }
  .claim .hint { display: none; }
  .trust { display: none; }
  .form { padding: 26px 22px; }
}
