/* Unauthenticated shell — IM2-183.
 *
 * ONE shell for every logged-out page: brand panel + form column. Each page swaps only the form
 * column's contents, which is what makes login, forgot-password, reset and the 2FA screens read as
 * one product rather than four pages that happen to share a footer.
 *
 * EVERY colour derives from the tenant's single primary, --green, declared per theme in
 * assets/css/themes/<name>.css. The panel tint is that primary at ~6%, the hover is it darkened —
 * both computed with color-mix rather than authored, so "custom theme" stays one config value per
 * tenant and never becomes a second stylesheet. There is deliberately no brand hex in this file —
 * BrandableShellTests.TheShellStylesheet_ContainsNoBrandColour enforces it. (That sentence used to
 * assert a guard that had never been written; the guard exists now.)
 *
 * The old login drew a white half against a grey half with a visible seam, an uncontained
 * illustration and a hard border around the viewport. All three are gone: the tint carries the
 * brand side, the form side is pure white, and the artwork is contained in a fixed box. */

:root {
    --auth-ink:      #1f2933;
    --auth-ink-soft: #5f6b76;
    --auth-line:     #d9dee3;
}

/* Derived from the one primary. If a theme sets no --green the fallbacks keep the page neutral
 * rather than unstyled — the "no theme" configuration is a supported state, not a failure. */
.auth-shell {
    --auth-brand:      var(--green, #4a5568);
    --auth-brand-dark: color-mix(in srgb, var(--green, #4a5568) 82%, black);
    --auth-tint-a:     color-mix(in srgb, var(--green, #4a5568)  6%, white);
    --auth-tint-b:     color-mix(in srgb, var(--green, #4a5568) 14%, white);

    display: flex;
    min-height: 100vh;
}

/* ── Brand panel — 55% ─────────────────────────────────────────────────────
 * Drops out entirely when the tenant has no artwork; the form then centres on a faint tint rather
 * than leaving an empty half-page, which is the "unfinished" look this ticket exists to remove. */
.auth-shell__brand {
    flex: 0 0 55%;
    background: linear-gradient(160deg, var(--auth-tint-a) 0%, var(--auth-tint-b) 100%);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 48px;
    position: relative;
    overflow: hidden;
}

/* Fixed render box, 10:9, capped. The artwork is never styled per tenant — that is what keeps every
 * tenant's illustration reading at the same size, and why the upload enforces the same ratio. An
 * off-spec asset is contained and letterboxed here rather than distorted or overflowing. */
.auth-shell__art {
    width: min(60%, 520px);
    aspect-ratio: 1 / 0.9;
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
}

.auth-shell__art img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;   /* contain, never cover — cropping someone's brand artwork is not ours to do */
    display: block;
}

/* ── Form panel — 45%, pure white ──────────────────────────────────────── */
.auth-shell__form {
    flex: 1;
    background: #fff;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 48px 40px 32px;
}

.auth-shell__box {
    width: 100%;
    max-width: 400px;
}

/* Wide variant — for the one page that asks the user to READ (the consent gate). It widens the
 * measure and gives the brand panel back some of its share, rather than dropping the shell: a page
 * that opts out is exactly how this surface became four different-looking screens in the first
 * place. The form panel still scrolls, because terms are long and the shell is 100vh. */
.auth-shell--wide .auth-shell__box  { max-width: 620px; }
.auth-shell--wide .auth-shell__brand { flex: 0 0 38%; }
.auth-shell--wide .auth-shell__form {
    justify-content: flex-start;
    overflow-y: auto;
    max-height: 100vh;
}

/* No artwork: the brand panel is gone, so the form carries the tint instead of floating on white. */
.auth-shell--no-art .auth-shell__brand { display: none; }
.auth-shell--no-art .auth-shell__form {
    background: linear-gradient(160deg, var(--auth-tint-a), #fff 55%);
}

/* ── Form vocabulary — identical on every page ─────────────────────────── */
.auth-shell h1 { font-size: 28px; font-weight: 600; margin-bottom: 8px; }

.auth-shell__sub {
    color: var(--auth-ink-soft);
    font-size: 15px;
    margin-bottom: 32px;
}

.auth-shell__field { margin-bottom: 24px; }

.auth-shell label {
    display: block;
    font-size: 14px;
    font-weight: 500;
    margin-bottom: 8px;
}

.auth-shell label .req { color: var(--auth-brand); }

.auth-shell input[type=text],
.auth-shell input[type=email],
.auth-shell input[type=password] {
    width: 100%;
    height: 48px;
    padding: 0 14px;
    border: 1px solid var(--auth-line);
    border-radius: 8px;
    font-size: 15px;
    color: var(--auth-ink);
    background: #fff;
}

.auth-shell input:focus {
    outline: none;
    border-color: var(--auth-brand);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--auth-brand) 18%, transparent);
}

/* Every Bootstrap .is-invalid rule is COMPOUND — .form-control.is-invalid, .form-select.is-invalid,
 * .form-check-input.is-invalid. Dropping .form-control from these inputs therefore made the class
 * inert: a rejected field rendered identically to a good one, leaving only the message text. The
 * class is still the right signal, so it gets a rule of its own here. */
.auth-shell input.is-invalid {
    border-color: #dc3545;
}

.auth-shell input.is-invalid:focus {
    border-color: #dc3545;
    box-shadow: 0 0 0 3px rgba(220, 53, 69, .18);
}

/* The field hint sits BELOW the input and reserves space. That gap is also what keeps a browser's
 * native autofill dropdown from landing on the CTA — the collision reported as an "account picker"
 * bug is the browser's own list, which no build flag can suppress, so the fix is clearance. */
.auth-shell__hint {
    display: block;
    margin-top: 6px;
    font-size: 13px;
    color: var(--auth-ink-soft);
}

/* Flex rather than a line-height, so the same class works on a <button> AND on an <a> — the
 * "continue" action is a link on some pages and a form submit on others, and they must not be
 * two different-looking buttons. */
.auth-shell__cta {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 48px;
    border: 0;
    border-radius: 8px;
    background: var(--auth-brand);
    color: #fff;
    font-size: 15px;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
}

.auth-shell__cta:hover { background: var(--auth-brand-dark); color: #fff; text-decoration: none; }
.auth-shell__cta:disabled { opacity: .55; cursor: default; }

/* The one legitimate second button: an action that is genuinely optional and genuinely a button
 * ("send me a code"). Outlined, so it never competes with the CTA for the eye. */
.auth-shell__cta--ghost {
    background: transparent;
    border: 1px solid var(--auth-brand);
    color: var(--auth-brand);
}

.auth-shell__cta--ghost:hover {
    background: color-mix(in srgb, var(--auth-brand) 8%, white);
    color: var(--auth-brand);
}

/* Secondary action is a text link BELOW the CTA on every page — never beside it, never above. */
.auth-shell__secondary {
    display: block;
    margin-top: 20px;
    text-align: center;
    font-size: 14px;
    color: var(--auth-ink-soft);
}

.auth-shell__secondary a { color: var(--auth-brand); text-decoration: none; }
.auth-shell__secondary a:hover { text-decoration: underline; }

/* A secondary action that must POST (defer, resend) is a <button> in a form. It still has to read
 * as the same text link — otherwise "Set up later" arrives as a third button on the page. */
.auth-shell__secondary button {
    border: 0;
    padding: 0;
    background: none;
    font: inherit;
    color: var(--auth-brand);
    cursor: pointer;
}

.auth-shell__secondary button:hover { text-decoration: underline; }

.auth-shell__secondary button:disabled {
    color: var(--auth-ink-soft);
    opacity: .65;
    cursor: default;
    text-decoration: none;
}

/* ── Multi-step flows (2FA setup) ──────────────────────────────────────────
 * An eyebrow above the heading, not a heading of its own. "Step 1 of 2" is orientation; making it
 * an <h5> — as the old pages did — gave every step two competing titles. */
.auth-shell__step {
    display: block;
    margin-bottom: 10px;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--auth-brand);
}

/* One-time codes are read back digit by digit, so they get tracking and a monospace-ish rhythm. */
.auth-shell input.auth-shell__code {
    text-align: center;
    font-size: 22px;
    font-weight: 600;
    letter-spacing: .5em;
    text-indent: .5em;   /* centres the group despite the trailing letter-space */
}

/* ── Confirmation / error states ───────────────────────────────────────────
 * These REPLACE the form in place — same shell, same panel, same footer — so "check your email" and
 * "this link expired" are pages of the product rather than dead ends. */
.auth-shell__state { text-align: center; }

.auth-shell__state-icon {
    width: 64px;
    height: 64px;
    margin: 0 auto 20px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 30px;
    background: color-mix(in srgb, var(--auth-brand) 12%, white);
    color: var(--auth-brand);
}

/* ── Enrolment furniture (QR, secret key, summary, choices) ──────────────── */
.auth-shell__qr {
    text-align: center;
    margin-bottom: 24px;
}

.auth-shell__qr img {
    padding: 8px;
    border: 1px solid var(--auth-line);
    border-radius: 8px;
    background: #fff;
    image-rendering: pixelated;   /* a resampled QR is a QR that will not scan */
}

.auth-shell__key {
    display: block;
    padding: 10px 12px;
    border: 1px solid var(--auth-line);
    border-radius: 8px;
    background: color-mix(in srgb, var(--auth-brand) 4%, white);
    font-size: 13px;
    line-height: 1.5;
    word-break: break-all;        /* the base32 secret has no spaces to wrap on */
    color: var(--auth-ink);
}

.auth-shell__summary {
    list-style: none;
    margin: 0 0 28px;
    padding: 0;
    font-size: 15px;
}

.auth-shell__summary li {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 8px 0;
}

.auth-shell__summary li.is-muted { color: var(--auth-ink-soft); }
.auth-shell__summary a { color: var(--auth-brand); text-decoration: none; font-size: 13px; }
.auth-shell__summary a:hover { text-decoration: underline; }

/* `.auth-shell label` is (0,1,1) and would otherwise beat a lone `.auth-shell__choice` (0,1,0),
 * killing display:flex, gap and font-weight on every checkbox row — worst on the consent gate,
 * where a wrapping document title fell back under the checkbox instead of aligning with its text.
 * Qualifying with the element makes this (0,2,1) and lets it win. */
.auth-shell label.auth-shell__choice {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    font-weight: 400;
    margin-bottom: 6px;
    cursor: pointer;
}

.auth-shell__choice input {
    accent-color: var(--auth-brand);
    flex: 0 0 auto;
    margin-top: .2em;   /* optical alignment with the first line of a wrapping label */
}

/* ── Single column below 900px ─────────────────────────────────────────────
 * The brand panel is hidden rather than stacked: on a phone a half-screen illustration pushes the
 * form below the fold, and signing in is the only thing anyone came here to do. */
@media (max-width: 900px) {
    .auth-shell__brand { display: none; }
    .auth-shell__form  { padding-top: 64px; }

    /* The wide variant's inner scroller is a desktop device for keeping the brand panel visible
     * beside long terms. On a phone there is no panel to keep, and a 100vh cap strands the consent
     * CTA below the visual viewport on iOS Safari, where 100vh > the visible area and the body
     * itself cannot scroll. Let the page scroll normally instead. */
    .auth-shell--wide .auth-shell__form {
        max-height: none;
        overflow-y: visible;
    }
}
