html, body {
    margin: 0;
    padding: 0;
    min-height: 100vh;
}

/*  On a phone, a sideways drag is not a request to leave.

    A browser reads a horizontal drag that runs past the edge of a page as "go
    back" one way and "go forward" the other. On a phone almost nothing scrolls
    sideways, so almost every sideways drag ran past the edge — and a child who
    brushed the screen the wrong way while reading was thrown out of whatever
    they were doing and back to the page before it, which from a screen reached
    straight after signing in is the login page.

    `none` since 2026-08-28 (Kamal: sideways swiping is to be off everywhere on
    a phone). It was `contain`, which stops a drag from chaining OUT of an inner
    strip into the page, but still allows the page's own overscroll — and the
    page's own overscroll is the navigation gesture. `none` refuses that too.

    A table or a strip of cards that really does scroll sideways still scrolls,
    and still stops at its own end; only the page turning into a back button is
    refused.

    Two things this does NOT do, and cannot:

      * Android's own edge-swipe back, when the phone is set to gesture
        navigation. That is the operating system, not the page, and no web page
        can refuse it. MyChessClub met the same wall and answered it in its
        Android app — its pages hand the app a `__appBackGuard` hook and the app
        asks before it navigates. We have no app to ask.
      * Safari on an iPhone, which navigates on its own gesture and does not
        read this property.

    On both html and body on purpose. Which of the two a browser reads for the
    page itself is a matter of propagation rules that have differed between
    them; setting both is one line more and leaves nothing to argue about.

    Scoped to `.elbe-mobile` on the <html> tag, which the two phone layouts set
    outright and the shared shell sets when MobileHelper says the reader is on a
    phone — a coach or an administrator gets the shared shell even on a phone,
    and the drag that throws a child out of a lesson throws them out too.

    Decided on the server, not from the width of a window, so a desktop dragged
    narrow keeps the trackpad swipe its reader expects. */
html.elbe-mobile,
html.elbe-mobile > body {
    overscroll-behavior-x: none;
}

body {
    background-color: var(--bs-body-bg, #1a1a2e);
    color: var(--bs-body-color, #e2e8f0);
}

/* RTL adjustments */
[dir="rtl"] .me-1 { margin-right: 0 !important; margin-left: 0.25rem !important; }
[dir="rtl"] .me-2 { margin-right: 0 !important; margin-left: 0.5rem !important; }
[dir="rtl"] .me-3 { margin-right: 0 !important; margin-left: 1rem !important; }
[dir="rtl"] .ms-1 { margin-left: 0 !important; margin-right: 0.25rem !important; }
[dir="rtl"] .ms-2 { margin-left: 0 !important; margin-right: 0.5rem !important; }
[dir="rtl"] .ms-3 { margin-left: 0 !important; margin-right: 1rem !important; }

[dir="rtl"] body {
    font-family: "Tahoma", "Segoe UI", "Geeza Pro", "Noto Sans Arabic", Arial, sans-serif;
}

/* Loading overlay */
.loading-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.5);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 9999;
}

/* Card hover */
.card-hover {
    transition: transform 0.2s, box-shadow 0.2s;
}
.card-hover:hover {
    transform: translateY(-2px);
    box-shadow: 0 8px 25px rgba(0,0,0,0.3);
}

/* Form validation */
.field-validation-error {
    color: #dc3545;
    font-size: 0.85rem;
}

.input-validation-error {
    border-color: #dc3545 !important;
}

/* Captcha */
/*  The sum on the contact form.

    Bootstrap blue on a pale blue wash of itself, both written out: 3.39:1, and
    the same on all eight themes because neither value knows a theme exists.
    The theme's own accent, on the theme's own raised surface. */
.captcha-question {
    background: var(--bs-tertiary-bg);
    border: 1px solid var(--elbe-line);
    border-radius: 0.5rem;
    padding: 0.65rem 1.25rem;
    font-size: 1.1rem;
    font-weight: 700;
    color: var(--elbe-brass-text);
    white-space: nowrap;
}

/*  Slate #94a3b8, chosen when the sign-in screens were dark cards. They are
    the theme's cards now, and on a white one that is 2.56:1. */
.captcha-label {
    color: var(--theme-text-muted);
    font-size: 0.9rem;
    font-weight: 500;
}

.captcha-input {
    max-width: 120px;
}

/* ============================================================
   Placeholders read as placeholders
   ============================================================

   A placeholder was set in the same face, the same size and the same weight
   as what the field holds once it is filled in — on the light themes #5E584C
   against #1C1814 for real text, which is a difference in shade and nothing
   else. Looking at a form there was no telling a hint from an answer.

   Italic says it at a glance and says it in every language and every theme,
   which a colour cannot: this file is loaded by all six layouts and the
   themes underneath it range from cream to near-black.

   Inter is loaded here in upright cuts only (lib/fonts/fonts.css: 400, 500,
   600, 700, all `font-style: normal`), so the browser slants it rather than
   using a drawn italic. At placeholder size that is the right trade — a
   drawn italic would be another font file on every page load.

   Lighter is done with tone rather than weight for the same reason: 400 is
   the lightest cut loaded, so asking for 300 would change nothing. Fading to
   85% is a real step and keeps it legible — the light themes' #5E584C is
   7.1:1 on white and comes out about 4.6:1 faded, still past the 4.5:1 line.
   Stated rather than left to the browser, because Firefox fades placeholders
   to 54% of its own accord and Chrome does not.

   Bare `::placeholder` first, with no class in front of it, so it reaches
   every field on every screen: the search boxes, the dialog forms, the chat,
   and whatever a new screen adds. `.form-control` and `.form-select` are
   named after it only because Bootstrap writes `opacity: 1` on those two and
   a bare pseudo-element cannot outweigh a class. This file is loaded after
   Bootstrap's, so naming them is enough — no `!important`.

   The colour is left to the themes, which is why nothing here names one. */
::placeholder,
.form-control::placeholder,
.form-select::placeholder,
.form-control-sm::placeholder,
.swal2-input::placeholder,
.swal2-textarea::placeholder {
    font-style: italic;
    opacity: 0.85;
}


/* =========================================================================
   A switch never touches its wording
   =========================================================================

   Bootstrap reserves 2.5em at the start of a switch row and pulls the box
   back into it. This school draws its switches wider than that — 2.6rem in
   elbe-parts.css, 3rem in adminlayout.css — so the box overran the space
   reserved for it and the first letter of the label ended up against the
   switch, or under it.

   Reserved from the switch's real width instead, plus the two spaces the
   rule asks for. Each stylesheet that draws a switch at its own size states
   that size in `--elbe-switch-w` beside the width, so this arithmetic
   follows it rather than guessing.

   Logical properties throughout: in Arabic, Kurdish and Persian the gap has
   to sit on the other side, and `padding-inline-start` puts it there without
   a second rule.

   Desktop only, as the rule says. Below 768px a phone's own layouts are left
   exactly as they are.

   `.usr-switch` is left out: it lays its row out with flex and a gap of its
   own, so it already keeps the wording clear and has a border whose padding
   this would overwrite. */

@media (min-width: 768px) {
    .form-check.form-switch:not(.usr-switch) {
        padding-inline-start: calc(var(--elbe-switch-w, 2em) + 8px);
    }

    .form-check.form-switch:not(.usr-switch) > .form-check-input {
        margin-inline-start: calc((var(--elbe-switch-w, 2em) + 8px) * -1);
    }
}
