/* ---------------------------------------------------------------------------
   SAAR — the sign-in screen. One look, seven products.

   COPY THIS FILE INTO EACH PRODUCT'S OWN STATIC DIRECTORY. Do not link it
   across domains: a login page that fetches its stylesheet from another host
   is a login page that looks broken the day that host is slow. Each product
   serves its own copy; this directory is the master.

   Products differ in template engine (Django 1.11, Django 5.2, Blade,
   CodeIgniter) so they cannot share a template. They can share the class
   names below, which is what makes the screens identical.

   MARKUP CONTRACT — produce exactly this shape, whatever the engine:

     <div class="sl">
       <div class="sl__panel">                 left: brand + one sentence
         <div class="sl__bg" data-sl-bg></div> photo layer, filled by JS
         <img class="sl__logo" src="...">
         <h1 class="sl__title">…</h1>
         <p class="sl__sub">…</p>
         <ul class="sl__features">             what THIS product does
           <li>…</li>
         </ul>
       </div>
       <div class="sl__form">                  right: the form
         <div class="sl__card">
           <h2 class="sl__h">Sign in</h2>
           <p class="sl__hint">…</p>
           <form>
             <div class="sl__field">
               <label class="sl__label">Username</label>
               <input class="sl__input">
               <p class="sl__err">…</p>        only when there is an error
             </div>
             <button class="sl__btn">Sign in</button>
           </form>
           <a class="sl__link">Forgotten your password?</a>
         </div>
         <p class="sl__foot">© 2026 SAAR Solutions</p>
       </div>
     </div>

   The brand name, logo and tagline are VALUES, not markup. They come from each
   product's own branding source. Never type a product name into this file or
   into the template.
--------------------------------------------------------------------------- */
:root {
    --sl-brand:      #3b82f6;
    --sl-brand-deep: #1d4ed8;
    --sl-violet:     #8b5cf6;
    --sl-ink:        #0f172a;
    --sl-ink-600:    #475569;
    --sl-ink-400:    #94a3b8;
    --sl-line:       #e2e8f0;
    --sl-wash:       #f8fafc;
    --sl-err:        #b91c1c;
    --sl-err-bg:     #fef2f2;

    /* The photo layer. Opacity is a token because the right value differs by
       theme: the same picture that sits quietly behind dark text has to be
       dimmer still behind light text on a dark card. */
    --sl-photo-op:   .30;
    --sl-photo-blur: 0px;
    --sl-card:       #ffffff;
}

/* DARK, in the three-state shape the rest of the suite uses:
   an explicit data-theme on <html> wins; prefers-color-scheme is only the
   fallback for somebody who has expressed no choice. A bare media query here
   would make the sign-in screen flip while the product behind it did not.
   The LEFT panel is dark in both themes by design - it is the brand half -
   so only the form side and the photo opacity change. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --sl-ink:      #e6edf3;
        --sl-ink-600:  #a9b6c4;
        --sl-ink-400:  #71808f;
        --sl-line:     #26313c;
        --sl-wash:     #0d1318;
        --sl-err:      #f0a090;
        --sl-err-bg:   #2d1713;
        --sl-photo-op: .22;
        --sl-card:     #151e25;
    }
}
:root[data-theme="dark"] {
    --sl-ink:      #e6edf3;
    --sl-ink-600:  #a9b6c4;
    --sl-ink-400:  #71808f;
    --sl-line:     #26313c;
    --sl-wash:     #0d1318;
    --sl-err:      #f0a090;
    --sl-err-bg:   #2d1713;
    --sl-photo-op: .22;
    --sl-card:     #151e25;
}

.sl, .sl * { box-sizing: border-box; }
.sl {
    min-height: 100vh;
    min-height: 100dvh;
    display: grid;
    grid-template-columns: 1.05fr 1fr;
    font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
    color: var(--sl-ink);
    background: #fff;
    -webkit-font-smoothing: antialiased;
}

/* left — the brand half */
.sl__panel {
    position: relative;
    overflow: hidden;
    padding: 56px 56px 48px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 18px;
    color: #fff;
    background: linear-gradient(140deg, #0b2a6b 0%, #143b8f 48%, #2b1f78 100%);
}
.sl__panel::after {
    content: "";
    position: absolute;
    right: -140px;
    bottom: -220px;
    width: 520px;
    height: 520px;
    border-radius: 50%;
    background: radial-gradient(circle, rgba(139,92,246,.45), transparent 66%);
    pointer-events: none;
}
.sl__logo {
    position: relative;
    z-index: 1;
    height: 44px;
    width: auto;
    background: #fff;
    border-radius: 10px;
    padding: 8px 12px;
    align-self: flex-start;
}
.sl__title {
    position: relative;
    z-index: 1;
    margin: 6px 0 0;
    font-size: clamp(28px, 3.2vw, 40px);
    line-height: 1.12;
    letter-spacing: -1.4px;
    font-weight: 800;
}
.sl__sub {
    position: relative;
    z-index: 1;
    margin: 0;
    max-width: 420px;
    font-size: 15px;
    line-height: 1.7;
    color: #c7d5ee;
}

/* right — the form half */
.sl__form {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 18px;
    padding: 48px 32px;
    background: var(--sl-wash);
}
.sl__card {
    width: 100%;
    max-width: 380px;
    background: var(--sl-card, #fff);
    border: 1px solid var(--sl-line);
    border-radius: 16px;
    padding: 32px 28px;
    box-shadow: 0 18px 44px rgba(15,23,42,.07);
}
.sl__h { margin: 0; font-size: 22px; font-weight: 700; letter-spacing: -.4px; }
.sl__hint { margin: 6px 0 22px; font-size: 13.5px; color: var(--sl-ink-600); }

.sl__field { margin-bottom: 16px; }
.sl__label {
    display: block;
    margin-bottom: 6px;
    font-size: 13px;
    font-weight: 600;
    color: var(--sl-ink);
}
.sl__input {
    width: 100%;
    height: 44px;
    padding: 0 13px;
    font-size: 15px;
    font-family: inherit;
    color: var(--sl-ink);
    background: #fff;
    border: 1px solid var(--sl-line);
    border-radius: 9px;
    outline: none;
    transition: border-color .15s, box-shadow .15s;
}
.sl__input:focus {
    border-color: var(--sl-brand);
    box-shadow: 0 0 0 3px rgba(59,130,246,.16);
}
.sl__input[aria-invalid="true"] { border-color: var(--sl-err); }

/* Errors are announced, not just coloured: colour alone is not a message. */
.sl__err { margin: 6px 0 0; font-size: 13px; color: var(--sl-err); }
.sl__alert {
    margin: 0 0 18px;
    padding: 10px 12px;
    font-size: 13.5px;
    color: var(--sl-err);
    background: var(--sl-err-bg);
    border: 1px solid #fecaca;
    border-radius: 9px;
}

.sl__btn {
    width: 100%;
    height: 46px;
    margin-top: 6px;
    font-size: 15px;
    font-weight: 600;
    font-family: inherit;
    color: #fff;
    background: linear-gradient(100deg, var(--sl-brand), var(--sl-violet));
    border: 0;
    border-radius: 10px;
    cursor: pointer;
    transition: transform .15s, box-shadow .15s;
    box-shadow: 0 6px 18px rgba(59,130,246,.26);
}
.sl__btn:hover { transform: translateY(-1px); box-shadow: 0 10px 24px rgba(59,130,246,.32); }
.sl__btn:active { transform: none; }
.sl__btn:disabled { opacity: .6; cursor: default; transform: none; }

.sl__link {
    display: block;
    margin-top: 16px;
    text-align: center;
    font-size: 13.5px;
    color: var(--sl-ink-600);
    text-decoration: none;
}
.sl__link:hover { color: var(--sl-brand-deep); text-decoration: underline; }

.sl__foot { margin: 0; font-size: 12.5px; color: var(--sl-ink-400); }

/* The phone layout lives in ONE place, further down under EVERY SCREEN.

   This is where it used to live, and the rule was `.sl__panel { display:
   none }` - the brand half dropped entirely on a phone because it cost a
   screenful before the thing the visitor came to do. That reasoning was
   right about the cost and wrong about the only way to pay it: the panel is
   now a 200px header rather than a screenful, so it keeps the logo, the name
   and the photograph without pushing the form off the bottom.

   Left here as a note rather than as rules, because two blocks setting
   `display` on the same element at two breakpoints is how the panel ended up
   invisible at every width under 900px while a second block carefully styled
   a header nobody could see.
*/

@media (prefers-reduced-motion: reduce) {
    .sl__btn, .sl__input { transition: none; }
    .sl__btn:hover { transform: none; }
}

/* ===========================================================================
   THE PHOTO BACKGROUND
   ---------------------------------------------------------------------------
   Two stacked layers so one can fade into the next; JS owns which picture is
   in which. Both sit UNDER the panel's gradient, not over it, so the brand
   colour still reads as the brand colour and the photograph is atmosphere.

   WHY THE PICTURES ARE SHIPPED WITH THE PRODUCT and not fetched from a photo
   service: a sign-in screen that pulls its background from another host is a
   sign-in screen that looks broken behind a customer firewall, and this suite
   has a standing rule against external hosts. They are deliberately small -
   the whole set is under 500 KB, and only the first one loads eagerly.

   They are also lightly blurred on the way in, which is not a compromise: a
   sharp photograph behind sign-in text fights the words, and the blur is what
   lets the files be this small.

   NO JAVASCRIPT, NO PHOTOGRAPH. The panel keeps its gradient and the screen
   is complete without this. Nothing here is load-bearing.
   ======================================================================== */
.sl__bg {
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    overflow: hidden;
}
.sl__bg-layer {
    position: absolute;
    inset: 0;
    background-position: center;
    background-size: cover;
    background-repeat: no-repeat;
    opacity: 0;
    transition: opacity 1400ms ease-in-out;
    will-change: opacity;
    /* A very slow drift. It is the difference between a photograph and a
       still, and at this speed it never asks to be looked at. */
    animation: sl-drift 48s ease-in-out infinite alternate;
}
.sl__bg-layer.is-on { opacity: var(--sl-photo-op); }

@keyframes sl-drift {
    from { transform: scale(1.06) translate3d(0, 0, 0); }
    to   { transform: scale(1.14) translate3d(-1.5%, -1.5%, 0); }
}

/* Somebody who gets motion sick from a moving background has said so. The
   picture stays, the movement and the cross-fade do not. */
@media (prefers-reduced-motion: reduce) {
    .sl__bg-layer { animation: none; transition: none; }
}

/* Everything the panel already had must sit above the photograph. */
.sl__panel > *:not(.sl__bg) { position: relative; z-index: 1; }

/* ===========================================================================
   WHAT THIS PRODUCT DOES
   ---------------------------------------------------------------------------
   Three to five lines, on the brand half, under the tagline. Somebody landing
   on a sign-in screen they were sent a link to should be able to tell what
   they are signing in TO without signing in first.

   Write them as things the product DOES for the reader, not as a feature
   list: "Run payroll and produce payslips" beats "Payroll module".
   ======================================================================== */
.sl__features {
    position: relative;
    z-index: 1;
    margin: 6px 0 0;
    padding: 0;
    list-style: none;
    display: grid;
    gap: 9px;
    max-width: 420px;
}
.sl__features li {
    position: relative;
    padding-left: 26px;
    font-size: 14px;
    line-height: 1.55;
    color: #d7e2f4;
}
/* A drawn tick rather than a glyph: no icon font to load, no emoji to render
   differently on every platform, and it inherits the colour. */
.sl__features li::before {
    content: "";
    position: absolute;
    left: 2px;
    top: 6px;
    width: 11px;
    height: 6px;
    border-left: 2px solid rgba(255,255,255,.72);
    border-bottom: 2px solid rgba(255,255,255,.72);
    transform: rotate(-45deg);
}

/* ===========================================================================
   EVERY SCREEN
   ---------------------------------------------------------------------------
   Four sizes, and each one is a decision about what the screen is FOR at that
   width rather than the same layout squeezed:

     wide      two halves, the brand half slightly the larger
     laptop    still two halves, but the padding and the title come down
               before the two columns start fighting for the same 380px card
     phone     one column. The brand half stops being a half and becomes a
               header: logo, title, photograph. The feature list goes - on a
               phone the job is to sign in, and four lines of prose between
               somebody and the form is four lines in the way.
     small     the same, with the gutters at their floor

   There is a fifth case that is not a width: a phone held sideways, where
   there is no vertical room for a header at all. That one drops the brand
   half entirely rather than pushing the form off the bottom.

   The side gutter never goes below 16px, and nothing in here is ever wider
   than the screen it is on.
   ======================================================================== */

/* laptop - squeeze the padding before squeezing the content */
@media (max-width: 1100px) {
    .sl { grid-template-columns: 1fr 1fr; }
    .sl__panel { padding: 40px 36px 36px; }
    .sl__title { font-size: clamp(24px, 3vw, 32px); letter-spacing: -1px; }
    .sl__sub { font-size: 14px; }
    .sl__form { padding: 40px 24px; }
}

/* phone - one column, the brand half becomes a header */
@media (max-width: 860px) {
    .sl {
        grid-template-columns: 1fr;
        /* A header plus a form is taller than the screen, and that is fine -
           it should scroll, not be crushed into it. */
        min-height: 0;
    }
    .sl__panel {
        display: flex;
        padding: 32px 20px 28px;
        gap: 10px;
        /* No longer a full-height half, so it must not stretch. */
        justify-content: flex-start;
        min-height: 210px;
    }
    /* The logo is in the header, so the card must not carry a second one. */
    .sl__card .sl__logo { display: none; }
    .sl__panel::after { right: -180px; bottom: -280px; }
    .sl__title { font-size: 26px; }
    .sl__sub { font-size: 14px; max-width: none; }
    .sl__features { display: none; }
    .sl__logo { height: 38px; }
    .sl__form {
        padding: 28px 20px 40px;
        /* The card was centred in a full-height column; now it sits under the
           header and centring it would leave a gap above it on a tall phone. */
        justify-content: flex-start;
    }
    .sl__card { max-width: 440px; }
}

/* small - gutters at their floor, and the card gives up its own padding
   rather than the page giving up its margin */
@media (max-width: 480px) {
    .sl__panel { padding: 26px 16px 22px; min-height: 178px; }
    .sl__title { font-size: 23px; letter-spacing: -.6px; }
    .sl__form { padding: 22px 16px 34px; }
    .sl__card { padding: 22px 18px; border-radius: 14px; }
    .sl__foot { font-size: 11px; text-align: center; }
}

/* a phone held sideways: no vertical room for a header, so there is no
   header. The photograph goes with it - it was atmosphere, and atmosphere is
   not worth the only 380px of height somebody has. */
@media (max-height: 460px) and (orientation: landscape) {
    .sl { grid-template-columns: 1fr; }
    .sl__panel { display: none; }
    .sl__form { padding: 18px 16px; justify-content: center; }
    .sl__card { padding: 18px; }
}

/* Nothing may push the page sideways. A long error message, an email address
   that does not break, a translated label - each of those has done it before
   in one product or another. */
.sl, .sl__panel, .sl__form, .sl__card { max-width: 100%; min-width: 0; }
.sl__title, .sl__sub, .sl__err, .sl__hint, .sl__label, .sl__foot,
.sl__features li { overflow-wrap: break-word; }
.sl__input, .sl__btn { max-width: 100%; }
