/*
 * sis.css - the few things the SIS draws that the administration does not:
 * the start screen's tiles, the curriculum's year blocks and their totals,
 * the record's sections. Everything else is admin.css and app.css, copied
 * from the administration unchanged (Mišo, 14 Oct 2026: "kopirao u cjelosti
 * look & feel"). Only the shared custom properties are used, so the accent
 * and the light/dark scheme the reader picks reach these too.
 */

.sis-tiles
{
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 12px;
    margin: 0 0 18px;
}

.sis-tile
{
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 14px 16px;
    border: 1px solid var(--line);
    border-radius: var(--radius-m);
    background: var(--surface);
    color: var(--ink);
    text-decoration: none;
}

.sis-tile:hover
{
    border-color: var(--brand);
}

.sis-tile-n
{
    font-size: 26px;
    font-weight: 600;
    color: var(--brand-ink);
    font-variant-numeric: tabular-nums;
}

.sis-tile-label
{
    color: var(--ink-soft);
    font-size: 13px;
}

.sis-takeover h2,
.sis-home .formbox h2,
.sis-section h2
{
    margin: 0 0 8px;
    font-size: 15px;
}

.sis-log td
{
    vertical-align: top;
    font-size: 12.5px;
}

.sis-log-detail
{
    color: var(--ink-soft);
    word-break: break-word;
}

/* the curriculum: one block per year, its rows and its totals */

.sis-year
{
    margin: 0 0 20px;
}

.sis-year h2
{
    display: flex;
    align-items: baseline;
    gap: 12px;
    margin: 0 0 6px;
    font-size: 15px;
}

.sis-total
{
    font-weight: 400;
    color: var(--ink-soft);
    font-size: 13px;
}

.sis-total b
{
    color: var(--ink);
    font-variant-numeric: tabular-nums;
}

.sis-row-form
{
    display: flex;
    gap: 6px;
    justify-content: flex-end;
    white-space: nowrap;
}

.sis-add
{
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: end;
    padding: 8px 0 0;
}

.sis-add label
{
    display: flex;
    flex-direction: column;
    gap: 2px;
    font-size: 12px;
    color: var(--ink-soft);
}

.sis-add select.sis-subject
{
    max-width: 360px;
}

/*  A SENTENCE GETS A SENTENCE'S WIDTH - 26 Oct 2026.

    A row of the shape "Reason [____] [Cancel the thesis]" drew the field at
    the browser's default ~20 characters, however wide the card. Mišo: "reason
    ima kratko polje ... nema potrebe da se skrati ovoliko - slicno treba
    provjerit i na ostalim mjestima".

    So in every such row a field that holds a SENTENCE - a reason, a note, a
    comment, a description - takes the room that is left, and the button
    stays at the end of the line. Recognised by its name, not by a class on
    each page: the rule then reaches every screen that has one today and
    every screen that gets one tomorrow. Short fields (a decision number, a
    date, a code) keep their own width - stretching those reads as a place
    to write a paragraph. flex-basis 18em: on a phone the field takes the
    whole line and the button wraps under it. */
.sis-add > label:has(> input[name="reason"]),
.sis-add > label:has(> input[name="note"]),
.sis-add > label:has(> input[name="comment"]),
.sis-add > label:has(> input[name="description"]),
.sis-add > label:has(> input[name="text"])
{
    flex: 1 1 18em;
    min-width: 0;
}

.sis-add > label > input[name="reason"],
.sis-add > label > input[name="note"],
.sis-add > label > input[name="comment"],
.sis-add > label > input[name="description"],
.sis-add > label > input[name="text"]
{
    width: 100%;
}

/*  "EXPORT: EXCEL / CSV" UNDER EVERY LIST - 26 Oct 2026.

    These rules were in sis-io.css, whose header says it is "loaded on every
    SIS page through the module registry". It is not: ModuleRegistry::css()
    is called by the render checks only, and each page loads the list in its
    own CSS constant - so sis-io.css reaches the import and ERP screens and
    nothing else, and on every list the label and the two buttons stood a
    pixel apart with the tick box wrapped under them. Mišo: "ergonomski je
    ljepse ako ih malo razdvojimo barem za 3-4 pixela".

    Here because sis.css is in every office page's list (beside admin.css),
    and the control is drawn under every list. sis-io.css keeps its copy for
    the two screens that load it; the two say the same. */
.io-export
{
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    margin-left: auto;
}

.io-export-label
{
    margin-right: 2px;
    color: var(--ink-soft);
    font-size: 13px;
}

.io-export-repeat
{
    margin-left: 6px;
    font-size: 13px;
}

.io-export-bar
{
    display: flex;
    justify-content: flex-end;
    align-items: center;   /* wave 9 (fixcrm9, UX9-25): "Export:" on the line of its buttons, also inside a form */
    gap: 6px;
    margin: 8px 0 16px;
}

.edittable td.num,
.edittable th.num
{
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.sis-filters
{
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
    margin: 0 0 10px;
}

/* the record: sections side by side where the screen is wide enough */

.sis-sections
{
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    gap: 14px;
}

.sis-section
{
    padding: 12px 14px;
    border: 1px solid var(--line);
    border-radius: var(--radius-m);
    background: var(--surface);
}

.sis-meta
{
    color: var(--ink-soft);
    font-size: 12px;
    margin: 6px 0 0;
}

/*  A CHIP THAT IS "OFF" - a fact about the thing ("external", "suggested,
    not saved yet", "public list switched off", a stage with 0 in it), never
    an alarm. UI QA, 14 Oct 2026: this used to be `opacity: .65` on top of
    the administration's .chip-off, which is RED ("the exception is a No").
    The two together gave a faded red that read as an error at 2.3:1 on a
    phone, and in tables (where .edittable .chip is white) a grey word at
    2.8:1 - both under the 4.5:1 a word needs. Every module had drawn its own
    way round it (sis-requests made it grey on purpose).

    So in Wise Ed it is one quiet chip, at full strength: the second
    surface, a hairline, the soft ink - 5.3:1 light, 6.1:1 dark. Quiet by
    colour, not by transparency, so it stays readable and stays quiet. */
body.sis .chip.chip-off
{
    opacity: 1;
    background: var(--surface-2);
    border-color: var(--line-strong);
    color: var(--ink-soft);
    font-weight: 500;
}

@media (max-width: 640px)
{
    .sis-row-form
    {
        flex-wrap: wrap;
    }
}

/* ================================================================== */
/* THE SHELL - 14 Oct 2026: status chips, the work queue, the student */
/* page's header and timeline, the lists' check banner.               */
/* ================================================================== */

/*  STATUS CHIPS - ONE VOCABULARY, FIVE TONES (src/View/Status.php).

    A tone is two custom properties on the element that carries its class:
    --st, the colour of the dot, the border and the tint, and --st-ink, the
    colour of the word. Everything else is mixed from the page's own
    surface, so a chip sits on light and dark alike without a second set of
    rules per chip.

    THE HUES ARE FIXED, NOT THE ACCENT. A status must read the same to every
    referent: if "submitted" took the reader's chosen accent, the teal reader
    and the red reader would see two different statuses - and red means
    something here. The accent stays what it is in the administration, the
    colour of "you are here" and of the primary button.

    DARK: lighter hues and lighter words, for the hand-set dark scheme
    (body.mode-dark) and for the machine's (prefers-color-scheme, unless the
    reader chose light) - the same pair of selectors admin.css uses. */

.st-neutral { --st: #6b7280; --st-ink: #4b5563; }
.st-info    { --st: #2563eb; --st-ink: #1e4fc2; }
.st-warn    { --st: #d97706; --st-ink: #8a4b06; }
.st-good    { --st: #16a34a; --st-ink: #166534; }
.st-bad     { --st: #dc2626; --st-ink: #b4231f; }

body.mode-dark .st-neutral { --st: #9aa3b2; --st-ink: #c6ccd6; }
body.mode-dark .st-info    { --st: #60a5fa; --st-ink: #a5c8fd; }
body.mode-dark .st-warn    { --st: #f59e0b; --st-ink: #fcd38a; }
body.mode-dark .st-good    { --st: #34d399; --st-ink: #94e9c4; }
body.mode-dark .st-bad     { --st: #f87171; --st-ink: #fcaaaa; }

@media (prefers-color-scheme: dark)
{
    body.admin:not(.mode-light) .st-neutral { --st: #9aa3b2; --st-ink: #c6ccd6; }
    body.admin:not(.mode-light) .st-info    { --st: #60a5fa; --st-ink: #a5c8fd; }
    body.admin:not(.mode-light) .st-warn    { --st: #f59e0b; --st-ink: #fcd38a; }
    body.admin:not(.mode-light) .st-good    { --st: #34d399; --st-ink: #94e9c4; }
    body.admin:not(.mode-light) .st-bad     { --st: #f87171; --st-ink: #fcaaaa; }
}

/*  The administration's .chip, narrowed for a status: smaller, the tone's
    tint and word, a dot in front. A chip with a tooltip says so with the
    help cursor - "every chip has a tooltip" is only useful if people know
    to point at it. */
.chip.sis-st
{
    gap: 6px;
    padding: 2px 10px 2px 8px;
    font-size: 12px;
    font-weight: 600;
    line-height: 1.5;
    white-space: nowrap;
    vertical-align: middle;
    background: color-mix(in srgb, var(--st) 13%, var(--surface));
    border-color: color-mix(in srgb, var(--st) 40%, var(--line));
    color: var(--st-ink);
}

.chip.sis-st[title]
{
    cursor: help;
}

.sis-st-dot
{
    flex: none;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--st);
}

/*  An abbreviation or a column heading with a tooltip shows that it has one. */
body.sis abbr[title],
body.sis th[title],
body.sis .formgrid label > span[title]
{
    text-decoration: underline dotted;
    text-underline-offset: 3px;
    cursor: help;
}

/*  A note that something could not be read - amber, never alarming: the
    rest of the screen is right. */
.sis-note
{
    margin: 0 0 12px;
    padding: 8px 12px;
    border: 1px solid color-mix(in srgb, #d97706 35%, var(--line));
    border-radius: var(--radius-s);
    background: color-mix(in srgb, #d97706 9%, var(--surface));
    font-size: 13px;
}

/*  The filter chips over the queue and the timeline - links, the one you
    are on marked like the rail's "here". */
.sis-chips
{
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin: 8px 0 12px;
}

.chip.sis-filter
{
    gap: 6px;
    padding: 4px 12px;
    font-size: 13px;
    font-weight: 500;
    background: var(--surface);
    border-color: var(--line-strong);
    color: var(--ink);
    text-decoration: none;
}

.chip.sis-filter b
{
    color: var(--ink-soft);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

a.chip.sis-filter:hover
{
    border-color: var(--brand);
}

.chip.sis-filter.is-on
{
    background: color-mix(in srgb, var(--brand) 12%, var(--surface));
    border-color: color-mix(in srgb, var(--brand) 45%, var(--line));
    font-weight: 600;
}

/* ---- the work queue ------------------------------------------------ */

.sis-queue
{
    margin: 0 0 22px;
}

.sis-q-head h1
{
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 0 0 2px;
    font-size: 20px;
}

.sis-q-head .fieldhint
{
    margin: 0 0 6px;
}

.sis-q-count
{
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 26px;
    height: 26px;
    padding: 0 8px;
    border-radius: 999px;
    background: var(--brand);
    color: var(--on-brand, #fff);
    font-size: 13px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    cursor: help;
}

.sis-q-count.is-zero
{
    background: var(--surface-2);
    color: var(--ink-soft);
}

.sis-q-list
{
    list-style: none;
    margin: 0;
    padding: 0;
    border: 1px solid var(--line);
    border-radius: var(--radius-m);
    background: var(--surface);
    overflow: hidden;
}

/*  ONE ROW: status | what | how long | the one action. A grid rather than a
    table because the row re-flows on a phone into two lines, which a table
    cannot do without hiding a column. */
.sis-q-row
{
    display: grid;
    grid-template-columns: 9.5rem minmax(0, 1fr) auto auto;
    grid-template-areas: "status main age act";
    align-items: center;
    gap: 4px 14px;
    padding: 10px 14px;
    border-top: 1px solid var(--line);
}

.sis-q-row:first-child
{
    border-top: 0;
}

.sis-q-row:hover
{
    background: color-mix(in srgb, var(--brand) 4%, var(--surface));
}

/*  LATE: a bar down the left edge in the danger colour, and the age in it.
    The bar survives a narrow screen and a glance; the word "late" beside the
    age is for the reader who cannot tell red from grey. */
.sis-q-row.is-late
{
    box-shadow: inset 3px 0 0 var(--danger);
}

.sis-q-status { grid-area: status; }
.sis-q-main   { grid-area: main; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.sis-q-age    { grid-area: age; }
.sis-q-act    { grid-area: act; }

.sis-q-what
{
    color: var(--ink);
    overflow-wrap: anywhere;
}

.sis-q-thing
{
    font-weight: 600;
}

.sis-q-detail
{
    color: var(--ink-soft);
    font-size: 12.5px;
}

.sis-q-module
{
    margin-right: 6px;
    font-weight: 600;
}

.sis-q-age
{
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--ink-soft);
    font-size: 12.5px;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
    cursor: help;
}

.is-late .sis-q-age
{
    color: var(--danger);
    font-weight: 600;
}

/*  The tint is 9%, not 14% (UI QA, 14 Oct 2026): at 10.5px the word needs
    4.5:1, and on the deeper pink the red measured 4.4:1. */
.sis-q-late
{
    padding: 1px 6px;
    border-radius: 4px;
    background: color-mix(in srgb, var(--danger) 9%, var(--surface));
    color: var(--danger);
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
}

/*  An action whose screen is not installed yet: drawn, visibly dead, and its
    tooltip says why - the rule of the administration's disabled tabs. */
.btn.sis-off
{
    opacity: .5;
    cursor: default;
}

/*  EMPTY MEANS DONE, and looks it: a tick, a sentence, room around them. */
.sis-done
{
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    padding: 28px 16px;
    border: 1px dashed var(--line-strong);
    border-radius: var(--radius-m);
    text-align: center;
}

.sis-done-mark
{
    color: var(--ok);
}

.sis-done.is-fresh .sis-done-mark
{
    color: var(--ink-soft);
}

.sis-done-what
{
    margin: 0;
    font-size: 15px;
    font-weight: 600;
}

.sis-done .fieldhint
{
    margin: 0;
}

.sis-done.is-quiet
{
    padding: 18px 16px;
}

/* ---- the student page: header ------------------------------------- */

.sis-who
{
    margin: 0 0 14px;
}

.sis-who-top
{
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 6px 12px;
}

.sis-who-name
{
    margin: 0;
    font-size: 22px;
}

.sis-who-number
{
    padding: 1px 8px;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-s);
    color: var(--ink-soft);
    font-size: 14px;
    font-variant-numeric: tabular-nums;
    cursor: help;
}

.sis-who-line
{
    margin: 4px 0 8px;
}

.sis-who-soft
{
    color: var(--ink-soft);
}

.sis-who-chips
{
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
}

.sis-badge
{
    text-decoration: none;
}

.sis-badge:hover .chip.sis-st
{
    border-color: var(--st);
}

/* ---- the student page: the timeline -------------------------------- */

/*  A VERTICAL LINE WITH DOTS. The line is the list's ::before; each entry's
    dot sits on it in its status tone (neutral for an entry without one),
    ringed in the page's background so the line seems to pass behind it. */
.sis-tl-year h2
{
    margin: 18px 0 8px;
    color: var(--ink-soft);
    font-size: 13px;
    font-weight: 600;
    letter-spacing: .02em;
}

.sis-tl-list
{
    position: relative;
    list-style: none;
    margin: 0;
    padding: 0 0 0 4px;
}

.sis-tl-list::before
{
    content: '';
    position: absolute;
    left: 9px;
    top: 8px;
    bottom: 8px;
    width: 2px;
    border-radius: 1px;
    background: var(--line-strong);
}

.sis-tl-item
{
    position: relative;
    display: flex;
    gap: 12px;
    padding: 0 0 12px;
}

.sis-tl-dot
{
    position: relative;
    z-index: 1;
    flex: none;
    width: 12px;
    height: 12px;
    margin-top: 12px;
    border: 3px solid var(--st);
    border-radius: 50%;
    background: var(--surface);
    box-shadow: 0 0 0 3px var(--bg);
}

.sis-tl-body
{
    flex: 1;
    min-width: 0;
    padding: 8px 12px;
    border: 1px solid var(--line);
    border-radius: var(--radius-m);
    background: var(--surface);
}

.sis-tl-head
{
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 4px 10px;
}

.sis-tl-what
{
    overflow-wrap: anywhere;
}

.sis-tl-what b
{
    font-weight: 600;
}

.sis-tl-detail
{
    margin: 4px 0 0;
    color: var(--ink-soft);
    font-size: 13px;
}

.sis-tl-changes
{
    margin: 6px 0 0;
    padding: 0;
    list-style: none;
    font-size: 13px;
}

.sis-tl-changes li
{
    padding: 1px 0;
    overflow-wrap: anywhere;
}

.sis-tl-field,
.sis-tl-from,
.sis-tl-quiet
{
    color: var(--ink-soft);
}

.sis-tl-quiet
{
    font-style: italic;
}

.sis-tl-meta
{
    margin: 6px 0 0;
    color: var(--ink-soft);
    font-size: 12px;
}

/* ---- the lists' check banner, and small things ---------------------- */

.sis-checkbar
{
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 10px;
    margin: 0 0 10px;
    padding: 8px 12px;
    border: 1px solid var(--line);
    border-radius: var(--radius-s);
    background: var(--surface-2);
}

.sis-checkbar-what
{
    font-weight: 600;
}

.sis-checkbar .fieldhint
{
    flex-basis: 100%;
    margin: 0;
}

.sis-totals
{
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    font-weight: 400;
}

/*  A choice of one, shown and not offered: the value in a dashed box the
    height of the control it replaces, so the line does not jump. */
.sis-only
{
    display: inline-flex;
    align-items: center;
    min-height: var(--control-h);
    padding: 0 10px;
    border: 1px dashed var(--line-strong);
    border-radius: var(--radius-s);
    color: var(--ink);
}

.sis-add-head
{
    margin: 14px 0 0;
    color: var(--ink-soft);
    font-size: 13px;
    font-weight: 600;
}

.edittable tr.is-editing td
{
    background: color-mix(in srgb, var(--brand) 8%, var(--surface));
}

.formgrid label small.fieldhint
{
    margin: 0;
    font-size: .78rem;
    line-height: 1.35;
}

/*  A flag that is not a status ("in the timetable") at a status chip's size,
    so the header's chips read as one row and not two sizes. */
.chip.sis-flag
{
    padding: 2px 10px;
    font-size: 12px;
    line-height: 1.5;
    vertical-align: middle;
}

/* ---- phone width ----------------------------------------------------- */

@media (max-width: 640px)
{
    /*  The queue row in three lines: status and age, what, the action across
        the whole width - a thumb's target, not a corner button. */
    .sis-q-row
    {
        grid-template-columns: minmax(0, 1fr) auto;
        grid-template-areas:
            "status age"
            "main   main"
            "act    act";
        padding: 10px 12px;
    }

    .sis-q-act .btn
    {
        display: flex;
        justify-content: center;
        width: 100%;
    }

    .sis-q-head h1
    {
        font-size: 18px;
    }

    .sis-who-name
    {
        font-size: 19px;
    }

    .sis-tl-head
    {
        flex-direction: column;
        align-items: flex-start;
    }

    .sis-tiles
    {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .sis-only
    {
        width: 100%;
    }

    /*  A wide table scrolls inside itself, and the PAGE stays the phone's
        width - otherwise the whole screen, the queue and the rail included,
        slides sideways under the reader's thumb. */
    body.sis .editwrap .edittable
    {
        display: block;
        max-width: 100%;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }

    body.sis .sis-add,
    body.sis .sis-filters
    {
        max-width: 100%;
    }

    body.sis .sis-add select,
    body.sis .sis-add input
    {
        max-width: 100%;
    }
}


/* ------------------------------------------------------------------ */
/* integration 14 Oct 2026: a tick box sits BESIDE its sentence. The  */
/* admin's .formgrid label stacks its children; a checkline must not. */
/* (Found by the finance module, which had patched it locally.)       */
/* ------------------------------------------------------------------ */

.formgrid label.checkline
{
    flex-direction: row;
    align-items: center;
    gap: 8px;
}

.sis-who-links
{
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin: 10px 0 0;
}


/* ------------------------------------------------------------------ */
/* TABLE HEADINGS: ONE BASELINE, ONE LINE - 14 Oct 2026.              */
/* ------------------------------------------------------------------ */

/*  Mišo, on the programmes list: "na nasim tabelama unutar SIS trebamo malo
    poravnat stvari ... ili sve smjestit u jednu liniju". The heading row was
    on three different lines at once. The admin's sheet gives a NUMBER column
    (th.num) a fixed 88px, lets it wrap, and sits it at the BOTTOM, so
    "ECTS in total" and "Students this year" broke onto two lines and "Years"
    dropped to the second one - while every text column stayed in the
    vertical MIDDLE. Three heights in one stripe reads as a layout bug.

    So in Wise Ed, every heading of every list:
      - sits on the bottom edge, so whatever wraps, the last line of every
        heading is one line, right above the first row it names;
      - and on a screen wide enough for it (1200px and up, every office
        monitor) does not wrap at all - one line, as he asked. Below that
        width a long heading may still take two lines rather than push the
        table off a laptop, and the bottom alignment keeps it tidy.

    th.num needs its own selector: `body.admin .edittable th.num` is three
    classes deep, and a plain `thead th` here would lose to it. The 88px is
    dropped with the wrapping - a fixed width plus nowrap would let a long
    heading spill over its neighbour. */
body.sis .edittable thead th,
body.sis .listtable thead th,
body.sis .edittable thead th.num,
body.sis .listtable thead th.num
{
    vertical-align: bottom;
}

@media (min-width: 1200px)
{
    body.sis .edittable thead th,
    body.sis .listtable thead th,
    body.sis .edittable thead th.num,
    body.sis .listtable thead th.num
    {
        white-space: nowrap;
    }

    body.sis .edittable thead th.num,
    body.sis .listtable thead th.num
    {
        width: auto;
    }
}


/* ------------------------------------------------------------------ */
/* DATE FIELDS DRAWN LIKE EVERY OTHER FIELD - 14 Oct 2026.            */
/* ------------------------------------------------------------------ */

/*  Mišo, on finance's "Due by": "ovi izbori datuma mi isto nisu savršeni
    niti u stilu sa ostalim". They were not ours at all. app.css's one rule
    for text controls ("text controls, everywhere") names text, password,
    number, search, email, select and textarea - and not date, so every one of
    Wise Ed's 33 date fields was left to the browser: its own font (the wide,
    typewriter-like digits), its own thin light border, its own square corners
    and a height four pixels short of the field beside it.

    So the date-like types get exactly that rule's declarations - our surface,
    our line, our radius, the shared --control-h - and the inherited font,
    which is what turns the digits back into the page's own. Kept here and not
    added to app.css's list, because app.css is the copy shared with the
    timetable's administration and this is Wise Ed's call; the declarations
    are the same, so the two read alike. */
body.sis input[type="date"],
body.sis input[type="time"],
body.sis input[type="datetime-local"],
body.sis input[type="month"],
body.sis input[type="week"],
body.sis input[type="tel"],
body.sis input[type="url"]
{
    font: inherit;
    font-variant-numeric: tabular-nums;
    color: var(--ink);
    background: var(--surface);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-s);
    padding: 7px 10px;
    min-height: var(--control-h);
    box-sizing: border-box;
}

body.sis input[type="date"]:hover,
body.sis input[type="time"]:hover,
body.sis input[type="datetime-local"]:hover,
body.sis input[type="month"]:hover,
body.sis input[type="tel"]:hover,
body.sis input[type="url"]:hover
{
    border-color: color-mix(in srgb, var(--brand) 40%, var(--line-strong));
}

body.sis input[type="date"]:focus-visible,
body.sis input[type="time"]:focus-visible,
body.sis input[type="datetime-local"]:focus-visible,
body.sis input[type="month"]:focus-visible,
body.sis input[type="tel"]:focus-visible,
body.sis input[type="url"]:focus-visible
{
    outline: 2px solid var(--brand);
    outline-offset: 1px;
    border-color: var(--brand);
}

/*  The calendar mark inside the field: the browser draws it in the page's
    colour scheme (light on dark already, from color-scheme), but at full
    strength it outshouts the date. Softer, and a pointer, because it opens
    the picker on click. */
body.sis input::-webkit-calendar-picker-indicator
{
    opacity: .6;
    cursor: pointer;
}

body.sis input::-webkit-calendar-picker-indicator:hover
{
    opacity: 1;
}

/*  AND THE BUTTON BESIDE IT. A filter row - a field or two and "Show" -
    lines its controls up on the bottom edge (.sis-add: align-items: end),
    but a pill is 36px and a field is --control-h (38px), so the two never
    quite met. In these rows the button takes the field's height. */
body.sis .sis-add > .btn,
body.sis .sis-filters .btn
{
    min-height: var(--control-h);
}


/* ------------------------------------------------------------------ */
/* LINKS ON THE DARK THEME: A LIGHTER SHADE, NOT A DARKER ONE.        */
/* 14 Oct 2026.                                                       */
/* ------------------------------------------------------------------ */

/*  Mišo, on the request types list in dark mode: "ova plava boja teksta na
    tamnoj podlozi nije baš prijatna za čitanje - bolje da je svjetlija plava".

    Every link is `color: var(--brand-ink)` (app.css), and --brand-ink is by
    definition a DARKER shade of the accent - the right ink for an accent
    coloured word on white paper. On the dark surface the same #1d4ed8 sits
    at about 2.6:1 against #181c23, under the 4.5:1 body text needs, which is
    exactly the "not pleasant to read" he saw.

    So on the dark theme an anchor gets its own --brand-ink: the accent mixed
    halfway towards white. Derived from --brand rather than typed as a hex,
    so it follows whichever theme colour the reader picked (blue gives a soft
    #97b7f6, teal a light teal, amber a light amber) and stays one rule.

    ON THE ANCHOR, NOT ON THE BODY. Overriding --brand-ink page-wide would
    also lighten .btn-primary's gradient, which ends in --brand-ink and
    carries white text. Buttons that are anchors are left out for the same
    reason. A rule that gives a link some OTHER colour (a tab in --ink, a
    chip) is untouched: it never reads --brand-ink in the first place. */
body.sis.mode-dark a:not(.btn)
{
    --brand-ink: color-mix(in srgb, var(--brand) 50%, #ffffff);
}

@media (prefers-color-scheme: dark)
{
    body.sis:not(.mode-light) a:not(.btn)
    {
        --brand-ink: color-mix(in srgb, var(--brand) 50%, #ffffff);
    }
}


/* ================================================================== */
/* UI QA PASS - 14 Oct 2026, evening.                                 */
/* ================================================================== */

/*  Every screen of Wise Ed was rendered from the render tools' made-up data
    and photographed at 1440 and 390 px, light and dark, then measured
    (contrast of every word, controls in one row, what pokes past the
    screen's edge). The rules below are what that found that one rule here
    can fix everywhere; each says what was wrong and why this is the fix.
    Mišo's brief for it: "dovršena ergonomija ... lijep i jednostavan user
    interface, jasni statusi, tooltipovi". */


/* ---- 1. A LINE THROUGH THE MIDDLE OF EVERY SCREEN ON A PHONE -------- */

/*  Under 900px the left rail becomes a strip across the top, and
    admin.css says so - "AND NOT AT ALL WHEN THE RAIL IS A ROW" - by
    resetting the shell's background-image. But the stripe itself is drawn
    by `body.admin .shell:has(> .lrail)`, and :has()'s argument counts in
    the specificity: that rule is one class stronger than its own reset, so
    the reset never won. Result: a 1px vertical line at 210px running from
    the rail strip to the footer through the tiles, the forms and the
    tables of every Wise Ed screen on a phone or a narrow tablet. Same
    selector here, so it is strong enough. */
@media (max-width: 900px)
{
    body.sis .shell:has(> .lrail)
    {
        background-image: none;
    }
}


/* ---- 2. ACCENT-COLOURED WORDS ON THE DARK THEME --------------------- */

/*  The 20:51 rule below ("LINKS ON THE DARK THEME") lightened --brand-ink
    on anchors only, and said why: --brand-ink also ends .btn-primary's
    gradient. Measured afterwards, the words that are NOT anchors were
    still dark blue on dark grey:
      - the rail's current entry ("Pregled", "Študenti" ...) - 2.0:1, on
        every screen, the one word that says where you are;
      - the screen's name in the top bar ("Wise Ed - Študentski ...",
        "Sprejem", "Predmetnik") - 2.6:1;
      - the numbers on the finance tiles, the admission ranking's line of
        places, the diploma's step links - 2.6:1.

    THE FIX, WITHOUT TOUCHING THE BUTTON: the theme's own dark ink is kept
    under a second name on the BODY (--brand-deep: a custom property is
    resolved where it is declared, so every element below inherits the
    theme's value, not this file's), and only then is --brand-ink
    lightened - on the body's CHILDREN, so the body's own value that
    --brand-deep captured stays the theme's. .btn-primary then ends its
    gradient in --brand-deep: exactly the colour it had, light and dark,
    every theme. The accent mixed halfway to white is the 20:51 rule's
    colour, so links and these words are one blue (8.5:1 on the surface,
    6.7:1 on the rail's tinted "here"). */
body.sis
{
    --brand-deep: var(--brand-ink);
}

body.sis.mode-dark > *
{
    --brand-ink: color-mix(in srgb, var(--brand) 50%, #ffffff);
}

@media (prefers-color-scheme: dark)
{
    body.sis:not(.mode-light) > *
    {
        --brand-ink: color-mix(in srgb, var(--brand) 50%, #ffffff);
    }
}

body.sis .btn-primary
{
    background: linear-gradient(180deg, #3f7cf0, var(--brand-deep));
}


/* ---- 3. RED, GREEN AND AMBER THAT CAN BE READ ----------------------- */

/*  --danger (#d64545) is the colour of every word that matters most: "Ni
    izpolnjeno", "40 od 30 dni", "Zamuja", the red outline buttons that
    remove or cancel. On white it measured 4.38:1, on the dark surface
    3.9:1 - both under the 4.5:1 a word needs, and on the pale red tints it
    sits on, 3.8:1. --ok (#2e9e5b) as a word on white was 3.4:1 ("novo",
    "3 opravilo", a passed step).

    --warn was not defined in Wise Ed at all: sis-admission.css colours a
    score under the threshold, a tie and its warning sentence with
    var(--warn), and sis-work.css falls back to it - so all of those were
    drawn in plain ink and the amber they were meant to have never showed.

    One place, the three tokens, both themes:
      light  --danger #c23636 (5.4:1 on white, 4.7:1 on its own tint)
             --ok     #1f7f45 (5.0:1) - and white on it, the diploma's
                      step ticks, rises from 3.4 to 5.0
             --warn   #a34a07 (a deep amber, 5.9:1 on white and 5.3:1
                      on the accepted row's green tint, where a tie sits)
      dark   --danger #f07171 (5.9:1 on the surface)
             --ok     unchanged, 5.0:1 already
             --warn   #fbbf24 (the administration's own dark amber)
    On dark, the outline red button fills with --danger when pointed at;
    white on the lighter red would fall to 2.9:1, so there its word turns
    dark instead (6.7:1). */
body.sis
{
    --danger: #c23636;
    --ok:     #1f7f45;
    --warn:   #a34a07;
}

body.sis.mode-dark
{
    --danger: #f07171;
    --ok:     #2e9e5b;
    --warn:   #fbbf24;
}

body.sis.mode-dark .btn-danger:hover,
body.sis.mode-dark .btn-danger:focus-visible
{
    color: #1a0b0b;
}

@media (prefers-color-scheme: dark)
{
    body.sis:not(.mode-light)
    {
        --danger: #f07171;
        --ok:     #2e9e5b;
        --warn:   #fbbf24;
    }

    body.sis:not(.mode-light) .btn-danger:hover,
    body.sis:not(.mode-light) .btn-danger:focus-visible
    {
        color: #1a0b0b;
    }
}


/* ---- 4. TICK BOXES AND RADIOS: OUR COLOUR, THEIR OWN SIZE ----------- */

/*  Every checkbox and radio was the browser's default blue, whatever
    accent the school picked - the one control on a teal page that is not
    teal. accent-color hands the browser our accent; it still draws the box
    (and keeps it accessible), in our colour.

    AND THEIR SIZE. roomsedit.css gives every input inside a .formgrid the
    field height (`.formgrid input { height: var(--control-h) }`), which is
    right for a text box and wrong for a tick box: 73 checkboxes on the
    admission and sync settings, the programme, subject, diploma, eVŠ and
    request forms stood in a 38px box, so the box floated above or below its
    words, and on the sync settings the radios ("Le iz skupin ...") were
    drawn as 38px discs. A label that BEGINS with a tick box is a row: the
    box, then its words, the box level with the first line. (sis.css
    already did this for label.checkline; :has() does it for every such
    label without each template having to remember the class.) */
body.sis input[type="checkbox"],
body.sis input[type="radio"]
{
    accent-color: var(--brand);
}

body.sis .formgrid input[type="checkbox"],
body.sis .formgrid input[type="radio"]
{
    width: auto;
    height: auto;
    min-height: 0;
    flex: none;
    margin: 3px 0 0;
}

body.sis .formgrid label:has(> input[type="checkbox"]:first-child),
body.sis .formgrid label:has(> input[type="radio"]:first-child)
{
    flex-direction: row;
    align-items: flex-start;
    gap: 8px;
}


/* ---- 5. THE FILE FIELD, DRAWN LIKE OURS ----------------------------- */

/*  The same story as the date fields at 20:47, one control further: no
    rule anywhere names input[type=file], so the browser drew it - a grey
    square "Choose File" button in the system font, 21px tall, next to our
    38px fields and round buttons (attachments on a request, the diploma's
    files, the bank statement import). It is also where somebody DROPS a
    file, so the field gets a dashed edge, the usual sign for "put it
    here", and its button is one of our pills. The words inside ("Izberi
    datoteko", "Nobena datoteka ni izbrana") are the browser's, in the
    browser's language, and are left as they are. */
body.sis input[type="file"]
{
    box-sizing: border-box;
    max-width: 100%;
    min-height: var(--control-h);
    padding: 2px 10px 2px 2px;
    border: 1px dashed var(--line-strong);
    border-radius: var(--radius-s);
    background: var(--surface);
    color: var(--ink-soft);
    font: inherit;
    font-size: 13px;
    cursor: pointer;
}

body.sis input[type="file"]:hover
{
    border-color: color-mix(in srgb, var(--brand) 45%, var(--line-strong));
}

body.sis input[type="file"]:focus-visible
{
    outline: 2px solid var(--brand);
    outline-offset: 1px;
}

body.sis input[type="file"]::file-selector-button
{
    margin: 0 10px 0 0;
    padding: 4px 14px;
    border: 1px solid var(--line-strong);
    border-radius: 999px;
    background: var(--surface-2);
    color: var(--ink);
    font: inherit;
    font-weight: 600;
    cursor: pointer;
}

body.sis input[type="file"]::file-selector-button:hover
{
    border-color: var(--brand);
}


/* ---- 6. A SELECT THAT LOOKS LIKE A SELECT --------------------------- */

/*  app.css draws every select's chevron as a background image. admin.css
    then gives `.input` a `background:` SHORTHAND - which also resets the
    image - and 23 of Wise Ed's selects carry class="input" (the page-size
    box under every paged list, the eVŠ filters, the sync hour, the room
    pick). Those had no arrow and read as a text box with "25" typed in it.
    The chevron is put back for exactly those; the colour from the shorthand
    stays. */
body.sis select.input
{
    padding-right: 32px;
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%239aa3b2' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round' d='M4 6.5 8 10.5l4-4'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 9px center;
    background-size: 16px 16px;
}

/*  AND IN THE PAGER, the size box sits between round 36px buttons ("Nazaj",
    "Naprej", the arrows) as a square 38px field. There it takes their shape
    and height, so the row under every list is one row. The same box in the
    list's top bar, beside the search field, keeps the field's shape. The
    pager also wraps on a phone instead of pushing "»" past the edge. */
body.sis .pager
{
    flex-wrap: wrap;
    row-gap: 8px;
}

body.sis .pager .perpick-box
{
    min-height: 36px;
    height: 36px;
    padding: 0 30px 0 12px;
    border-radius: 999px;
    font-size: 13px;
    font-weight: 600;
    background-position: right 8px center;
}


/* ---- 7. THE HISTORY'S TIME HUGS THE LEFT EDGE ----------------------- */

/*  Every "Zgodovina" / "Zadnje spremembe" table (.sis-log: the start
    screen, admission, enrolment forms, finance, diploma, exams, portal,
    requests, sync - eleven screens) is a full-width table with no heading
    row, and its first cell, the time, is a right-aligned number. With the
    width shared out by the browser, that column got a quarter of the card
    and the time floated in the middle of it, with an empty gap on its
    left that looked like a missing column. The time column now takes only
    what the time needs, on one line; the rest of the row gets the room. */
body.sis .sis-log td:first-child
{
    width: 1%;
    white-space: nowrap;
}


/* ---- 8. THE ACTION ROW ON A PHONE ----------------------------------- */

/*  `.formactions` (roomsedit.css) is a flex row that never wraps, and a
    .btn may shrink. On a phone the admission decision row ("Vzemi v
    pregled", "Zahtevaj dokazila", "Sprejmi", "Ne sprejmi", "Umaknjena")
    squeezed each button into a three-line pill and pushed the last ones off
    the screen; the programme form and the new exam did the same with two.
    The row wraps instead, and a button keeps its words on one line - a
    button whose label breaks reads as two buttons. */
body.sis .formactions
{
    flex-wrap: wrap;
    row-gap: 10px;
}

body.sis .formactions > .btn,
body.sis .sis-filters .btn,
body.sis .sis-add > .btn
{
    flex: none;
    white-space: nowrap;
}


/* ---- 9. A LONG CHOICE DOES NOT WIDEN THE PAGE ----------------------- */

/*  A select is as wide as its longest option unless told otherwise, and a
    grid or flex item is at least as wide as its content. Together: the
    diploma screen's sections were 598px wide on a 390px phone (a mentor
    list, "Napovedovanje porabe - Novak Ana (prostih: 1)"), and the exams
    record's rule form 405px, so half of each form was off the screen.

    max-width: 100% on a select also takes its width out of the minimum the
    layout must give its box (CSS Sizing: a form control with a percentage
    width counts as zero there), so the section can be the phone's width
    and the select fits in it, its text clipped at the arrow - the full
    words are in the open list. Not inside table cells: a table shares its
    width by its cells' minimums, and a select that counts as zero there
    is squeezed to nothing. Text fields not inside a <details> either: the
    small "why?" box of a pop-over (remove a file, revoke a certificate)
    sits in a box only as wide as its content, and a percentage there
    shrank it from 240px to 72px. The cards themselves are allowed to be
    narrower than their content for the same reason. */
body.sis select:not(td *),
body.sis input:not(td *, details *),
body.sis textarea:not(td *, details *)
{
    max-width: 100%;
}

body.sis .sis-section,
body.sis .formbox
{
    min-width: 0;
}

/*  The same for a field whose width comes from its size attribute: the
    exams record's "Zakaj se pravilo odmisli" (size="40") is 374px of text
    box in a label of its own in a wrapping row, and the label, as wide as
    its content, carried it 15px past a phone's edge. A label in such a row
    is never wider than the row. */
body.sis .sis-add > label,
body.sis .sis-filters > label
{
    max-width: 100%;
}


/* ---- 10. A QUIET BUTTON IS STILL A BUTTON --------------------------- */

/*  admin.css's .btn-quiet is the "All / None" pair beside a settings group:
    10px, capitals, letter-spaced, no box - a link that happens to be a
    button. Wise Ed's modules use the same class for a secondary BUTTON:
    "Nazaj na vrste", "Prekliči paket", "Odstrani" beside "Odpri" and
    "Prenesi", "Opravi" beside an input. Next to 36px pills those came out
    as 19px capitals - a label, not something to press - and the row's
    heights disagreed. Here a quiet button is a full-size pill without fill
    or shadow, in the soft ink, that shows its edge when pointed at. The
    administration's own All / None (inside .setgroup-tools) and the undo
    button keep their look. */
body.sis .btn.btn-quiet:not(.setgroup-tools .btn-quiet):not(.undo-btn)
{
    min-height: 36px;
    padding: 7px 14px;
    border: 1px solid transparent;
    background: transparent;
    box-shadow: none;
    color: var(--ink-soft);
    font-size: inherit;
    font-weight: 600;
    letter-spacing: normal;
    text-transform: none;
}

body.sis .btn.btn-small.btn-quiet:not(.setgroup-tools .btn-quiet):not(.undo-btn)
{
    padding: 4px 10px;
    font-size: 12px;
}

body.sis .btn.btn-quiet:not(.setgroup-tools .btn-quiet):not(.undo-btn):hover,
body.sis .btn.btn-quiet:not(.setgroup-tools .btn-quiet):not(.undo-btn):focus-visible
{
    border-color: var(--line-strong);
    color: var(--ink);
}


/* ---- 11. NO DARK BLUE WORDS ON THE DARK THEME - THE WHOLE LIST ------ */

/*  Mišo at 22:15, after the link fix of 20:51: "ona plava boja se jos
    pojavljuje sto je ruzno citljiva u dark modu". The accent reaches a
    WORD on the dark page three ways, and each is closed here:

      1. var(--brand-ink) - links, the rail's current entry, the screen's
         name in the top bar, tile numbers, step numbers ... Closed by
         rule 2 above: on the dark theme every element under the body gets
         the light --brand-ink (the accent halfway to white, 8.5:1 on
         --surface, 7.7:1 on --surface-2), and .btn-primary keeps its fill.
      2. var(--st-ink) - the status chips. Their dark inks are already set
         (sis.css "STATUS CHIPS", body.mode-dark .st-*): #a5c8fd for info
         measures 8:1 on the chip's own dark tint; nothing to do.
      3. var(--brand) itself as a text colour - the accent at full strength,
         #2f6fed, 3.7:1 on --surface. These are the list below.

    THE LIST IS GENERATED, NOT HAND-PICKED. C:\CVI\_xfer\uiqa\darkblue\
    cssscan.py reads app.css, roomsedit.css, admin.css, sis.css and every
    sis-*.css, and lists each rule whose `color:` is var(--brand) (or
    --brand-ink, --st-ink, or a hard-coded dark blue - there are none as a
    text colour today); gen.py writes the selectors below from it. A rule
    already inside a dark block, and var(--brand-ink, var(--brand)) where
    --brand is only the fallback, are left out. Run both again when a
    module adds `color: var(--brand)`:
        python3 cssscan.py <the sheets> > sis-hits.json
        python3 gen.py sis sis-hits.json

    Each selector is the original one under the dark condition, so it is
    exactly as specific as the original plus body.sis.mode-dark: it wins
    over the rule it corrects and only in the dark theme. A link that is a
    button (`a.btn`) is left out of `.listtable a`, so a primary button in a
    list keeps its white word. Text only: fills, borders and button
    backgrounds are not touched. Proved on every Wise Ed screen in both
    dark modes: no word in the accent's hue under 4.5:1, and no element
    changed colour that was not accent-blue before (the before/after
    pictures are in the UI QA report). */
body.sis.mode-dark .evlink:hover,   /* app.css */
body.sis.mode-dark .badge-doc,   /* app.css */
body.sis.mode-dark .sethelp-mark:hover,   /* app.css */
body.sis.mode-dark .signage-tryex:hover,   /* app.css */
body.sis.mode-dark .listtable a:not(.btn),   /* admin.css */
body.sis.mode-dark .weeknav a.weeklabel,   /* admin.css */
body.sis.mode-dark .iconbtn:hover,   /* admin.css */
body.sis.mode-dark .lrail-here .lrail-icon,   /* admin.css */
body.sis.mode-dark .lrail-fold:hover,   /* admin.css */
body.admin.sis.mode-dark .grid-hour.grid-hour-half.is-cursor::after,   /* admin.css */
body.admin.sis.mode-dark .donemark.is-full,   /* admin.css */
body.admin.sis.mode-dark .placenote-move .placenote-clear,   /* admin.css */
body.admin.sis.mode-dark .slotadd-btn,   /* admin.css */
body.admin.sis.mode-dark .planpart-plus:hover,   /* admin.css */
body.admin.sis.mode-dark .planpart-plus:focus-visible,   /* admin.css */
body.sis.mode-dark .planlines .linkish,   /* admin.css */
body.admin.sis.mode-dark .lineicon-goto:hover,   /* admin.css */
body.admin.sis.mode-dark .lineicon-goto:focus-visible,   /* admin.css */
body.admin.sis.mode-dark .tickset-clear,   /* admin.css */
body.sis.mode-dark .sis-sync .sync-star   /* sis-sync.css */
{
    color: color-mix(in srgb, var(--brand) 50%, #ffffff);
}

@media (prefers-color-scheme: dark)
{
    body.sis:not(.mode-light) .evlink:hover,   /* app.css */
    body.sis:not(.mode-light) .badge-doc,   /* app.css */
    body.sis:not(.mode-light) .sethelp-mark:hover,   /* app.css */
    body.sis:not(.mode-light) .signage-tryex:hover,   /* app.css */
    body.sis:not(.mode-light) .listtable a:not(.btn),   /* admin.css */
    body.sis:not(.mode-light) .weeknav a.weeklabel,   /* admin.css */
    body.sis:not(.mode-light) .iconbtn:hover,   /* admin.css */
    body.sis:not(.mode-light) .lrail-here .lrail-icon,   /* admin.css */
    body.sis:not(.mode-light) .lrail-fold:hover,   /* admin.css */
    body.admin.sis:not(.mode-light) .grid-hour.grid-hour-half.is-cursor::after,   /* admin.css */
    body.admin.sis:not(.mode-light) .donemark.is-full,   /* admin.css */
    body.admin.sis:not(.mode-light) .placenote-move .placenote-clear,   /* admin.css */
    body.admin.sis:not(.mode-light) .slotadd-btn,   /* admin.css */
    body.admin.sis:not(.mode-light) .planpart-plus:hover,   /* admin.css */
    body.admin.sis:not(.mode-light) .planpart-plus:focus-visible,   /* admin.css */
    body.sis:not(.mode-light) .planlines .linkish,   /* admin.css */
    body.admin.sis:not(.mode-light) .lineicon-goto:hover,   /* admin.css */
    body.admin.sis:not(.mode-light) .lineicon-goto:focus-visible,   /* admin.css */
    body.admin.sis:not(.mode-light) .tickset-clear,   /* admin.css */
    body.sis:not(.mode-light) .sis-sync .sync-star   /* sis-sync.css */
    {
        color: color-mix(in srgb, var(--brand) 50%, #ffffff);
    }
}


/* ---- 12. A SECONDARY BUTTON THAT LOOKS PRESSABLE ON THE DARK THEME --- */

/*  On the light theme a plain .btn is a white pill with a hairline and a
    soft shadow on a grey page - it reads as a button. On the dark theme the
    same pill is --surface (#181c23) on a --surface card with a --line
    (#262c36) edge, and its shadow is black on near-black: "Vseeno shrani",
    "Nazaj na seznam", "Pošlji študentu", "Pripravi zavrnitev", the pager's
    "Naprej" were words floating in the card, nothing to press. On the dark
    theme a plain button is filled with the second surface and edged with
    the strong line, so it stands off the card the way the white pill
    stands off the grey page.

    :where() keeps this rule as weak as app.css's own .btn (one class), so
    the primary, the danger, the quiet button and every module rule that
    gives a button its own fill still win; it only replaces what .btn
    itself set. */
:where(body.sis.mode-dark) .btn:where(:not(.btn-primary, .btn-quiet, .btn-danger, .btn-soft-danger))
{
    background: var(--surface-2);
    border-color: var(--line-strong);
}

@media (prefers-color-scheme: dark)
{
    :where(body.sis:not(.mode-light)) .btn:where(:not(.btn-primary, .btn-quiet, .btn-danger, .btn-soft-danger))
    {
        background: var(--surface-2);
        border-color: var(--line-strong);
    }
}


/* ---- 13. ONE TEXT SIZE IN EVERY FIELD ------------------------------- */

/*  `.sis-add label` sets 12px for its small caption ("Predmet", "Vrsta",
    "Semester" above the field), and the field inside inherits it (app.css
    gives every control `font: inherit`). So the add rows - the
    curriculum's "Dodaj predmet", the record's new enrolment, the exam's
    subject picker, the diploma's and the exam record's small forms, 34
    fields in all - showed their values at 12px, while every other field of
    Wise Ed (the 440 in the lists' bars and the 302 in the forms) is 14px,
    the body's size. The caption stays small; the field is the same size as
    every other field. */
body.sis .sis-add label :is(input, select, textarea)
{
    font-size: 14px;
}


/* ---- 14. THE TOP BAR ON A PHONE: THREE ROWS, NOT FOUR --------------- */

/*  On a 390px phone the top bar's tools (public timetable, administration,
    refresh, language, theme) and the signed-in name ("Referat Test") need
    379px with their 8px gaps, and the row has 358px - so the name dropped
    to a fourth row of its own, and the bar took 174px (a fifth of the
    screen) before the first word of the page. With 3px gaps the tools and
    the name are one row (354px). The name is the one thing that may give
    way: its full text stays in the tooltip and in the menu it opens
    ("Prijavljeni kot ..."), and it is a block here, not the button's flex
    row, so a long name ends in "..." rather than being cut mid-letter. */
@media (max-width: 480px)
{
    body.sis .topbar-actions
    {
        flex-wrap: nowrap;
        gap: 3px;
    }

    body.sis .topbar-actions > *
    {
        flex: none;
    }

    /*  Only the name gives way, on a phone narrower still (360px): it
        shrinks to "Refer..." and the tools stay whole. */
    body.sis .topbar-actions > .whopick
    {
        flex: 0 1 auto;
        min-width: 48px;
    }

    body.sis .topbar-actions .whopick-btn
    {
        display: block;
        box-sizing: border-box;
        max-width: 100%;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        line-height: 20px;
    }
}


/* ---- 15. A PHONE PAGE THAT SCROLLED SIDEWAYS INTO NOTHING ------------ */

/*  On a phone a wide list scrolls inside itself (the phone rules above make
    `.editwrap .edittable` a block with overflow-x: auto) - but three
    screens still let the whole page slide 470-720px to the right into
    empty space: the exam record (1108px), the work report (1019px) and the
    month of work reports (912px). Not visible content: the screen-reader
    words in a heading cell (`.visually-hidden` / `.sr-only` - "Dejanja",
    the column of actions), which are absolutely positioned 1px boxes. An
    absolutely positioned box is clipped by a scrolling ancestor only if
    that ancestor is also its containing block, and the table was not
    positioned - so the 1px box sat at the table's far end, outside the
    scroller, and widened the document. The scrolling table is now its
    containing block, and what it scrolls stays inside it.

    AND ON A TABLET. Between 641 and 900px the rail is already a strip
    across the top, but the tables did not scroll inside themselves yet:
    measured at 768px (an iPad held upright), 17 screens - the lists of
    applications, calls, certificates, students' programmes, exam records,
    the curriculum, the work month - pushed the whole page sideways, top
    bar and rail strip with it, up to 1328px. The phone's arrangement now
    starts where the rail becomes a strip. The exam record's own scroller
    (.ex-scroll) gets its containing block in sis-exams.css. */
@media (max-width: 900px)
{
    body.sis .editwrap .edittable
    {
        display: block;
        position: relative;
        max-width: 100%;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }
}


/* ---- 16. A LIST WIDER THAN THE WINDOW SCROLLS IN ITS OWN BOX --------- */

/*  On a desktop the rail takes 210px, and a list with many columns can be
    wider than what is left: at 1280px the calls for applications measured
    1579px, the diploma register 1477px and the month of work reports
    1296px; at 1024px eighteen screens did - and then the WHOLE page,
    top bar and rail included, slid sideways under the trackpad.

    Every such list is in a `.listwrap` (the house's list wrapper, which no
    sheet styled until now), so that box scrolls sideways instead and the
    page stays the window's width. The table keeps width: 100%, so a list
    that fits looks exactly as before. None of the 34 lists in a .listwrap
    carries a pop-over that would be clipped (checked template by
    template); position: relative keeps the headings' screen-reader words
    inside the box (see 15). The 4px of padding, given back by a -4px
    margin, is room for a focus ring on the first and last cells, which a
    scrolling box would otherwise cut. */
body.sis .listwrap
{
    position: relative;
    max-width: calc(100% + 8px);
    margin: -4px;
    padding: 4px;
    overflow-x: auto;
}

/*  AND A TABLE IN A CARD. The same at 1024px for the lists that sit in a
    card rather than a .listwrap - the curriculum's year, the record's
    enrolments, the exam subject's terms, the eVŠ programme map, the
    certificates: 1031-1108px, the page sliding sideways. The card scrolls
    sideways instead (its own 12-14px padding is the room for focus
    rings). Only a card whose table holds no <details>: a pop-over inside
    would be clipped by the scrolling card, and an overflowing card is the
    lesser fault. */
body.sis :is(.formbox, .sis-section, .sis-year):has(> table.edittable, > form > table.edittable):not(:has(details))
{
    position: relative;
    overflow-x: auto;
}


/* ---- 17. THE "WISE TIMETABLE" PILL IN THE TOP BAR (15 Oct 2026) ----- */

/*  It replaced two bare icons (an eye and three sliders) after Mišo read the
    sliders as Wise Ed's settings and landed in the timetable administration
    (templates/_rail.php says more). Icon and name side by side; the
    pop-over is the menupick family's, so it opens, closes and themes like
    its neighbours. On a phone the name is hidden VISUALLY only - the
    summary keeps it for screen readers and the title for the pointer. */
body.sis .ttpick-btn
{
    display: inline-flex;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
}

body.sis .ttpick-btn svg
{
    display: block;
    flex: none;
}

body.sis .ttpick-pop
{
    width: min(240px, 92vw);
}

@media (max-width: 640px)
{
    body.sis .ttpick-text
    {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip: rect(0 0 0 0);
        clip-path: inset(50%);
        white-space: nowrap;
    }
}


/* ================================================================== */
/* 18. FORM SECTIONS - auditA, the ergonomics audit, 15 Oct 2026.      */
/* ================================================================== */

/*  Mišo, 00:44: "neki ekrani su samo hrpa unosnih polja - moglo bi se
    malo uredit i stavit u sekcije, dekoracije". A programme was fifteen
    fields in one grid, a subject seventeen: the clerk read every caption to
    find the one she came for, and nothing said which fields belong
    together or what a group is for.

    ONE PATTERN, USED THE SAME WAY ON EVERY SETTINGS AND FORM SCREEN
    (docs/ergonomics-audit-2026-10-15.md lists where):

      <div class="sis-fsecs">                  the stack of sections
        <section class="sis-fsec" aria-labelledby="fs-x">
          <header class="sis-fsec-head">
            <h2 id="fs-x">Heading</h2>
            <p class="sis-fsec-what">One line: what this group is for.</p>
          </header>
          <div class="formgrid sis-fgrid">
            <label>...</label>                      half of a wide row
            <label class="sis-f-short">...</label>  a quarter: a number, a code, a postcode
            <label class="wide">...</label>         the whole row
            <p class="sis-f-sub">Sub-heading</p>    a group inside one section
          </div>
          <details class="sis-fsec-more">          what is seldom changed, folded
            <summary>More options</summary>
            <div class="formgrid sis-fgrid">...</div>
          </details>
        </section>
      </div>

    .sis-fsecs-2 puts two sections side by side where the screen is wide.
    The action row (.formactions: the primary first, the quiet ones after,
    anything destructive in its own .deleteform below) stays OUTSIDE the
    sections, under the last one, so there is one Save for the whole form.

    THE GRID SIZES ITSELF BY THE SECTION, NOT THE WINDOW (a container
    query): the same section is four columns in a wide card, two in a half
    card or on a tablet, one on a phone - so a postcode is a short field
    beside its town instead of a half-screen box. Browsers without
    container queries keep roomsedit.css's two columns, as before.

    THE DECORATION is deliberately small: a short accent bar before the
    heading and a hairline under the head - enough rhythm for the eye to
    find the next group, nothing that competes with the fields. Colours are
    the shared tokens only, so both themes read (sis.css 2 and 11 make
    --brand-ink light on the dark theme). */

body.sis .sis-fsecs
{
    display: grid;
    gap: 14px;
    margin: 0 0 6px;
}

@media (min-width: 1180px)
{
    body.sis .sis-fsecs-2
    {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        align-items: start;
    }
}

body.sis .sis-fsec
{
    container: fsec / inline-size;
    min-width: 0;
    padding: 14px 18px 4px;
    border: 1px solid var(--line);
    border-radius: var(--radius-m);
    background: var(--surface);
}

body.sis .sis-fsec-head
{
    position: relative;
    margin: 0 0 14px;
    padding: 0 0 10px 12px;
    border-bottom: 1px solid var(--line);
}

/*  The accent bar: the one decoration, the same on every section. */
body.sis .sis-fsec-head::before
{
    content: "";
    position: absolute;
    left: 0;
    top: 3px;
    width: 4px;
    height: 16px;
    border-radius: 2px;
    background: var(--brand);
}

body.sis .sis-fsec-head h2,
body.sis .sis-fsec-head h3
{
    margin: 0;
    font-size: 15px;
    font-weight: 600;
    line-height: 1.35;
    color: var(--ink);
}

body.sis .sis-fsec-what
{
    margin: 3px 0 0;
    font-size: 13px;
    line-height: 1.45;
    color: var(--ink-soft);
    max-width: 78ch;
}

body.sis .sis-fgrid
{
    margin-bottom: 12px;
}

/*  A field's own hint, under it - quieter than the caption, never wider
    than its field. */
body.sis .sis-fgrid label > small,
body.sis .sis-fgrid .sis-f-hint
{
    margin: 0;
    font-size: 12px;
    line-height: 1.4;
    color: var(--ink-soft);
}

/*  A read-only fact among the fields (a name that comes from the
    timetable): caption above, value as text - not a disabled box that looks
    editable and refuses. */
body.sis .sis-fgrid .sis-f-fact
{
    display: flex;
    flex-direction: column;
    gap: 4px;
}

body.sis .sis-fgrid .sis-f-fact > span:first-child
{
    font-size: .82rem;
    color: var(--ink-soft);
}

body.sis .sis-fgrid .sis-f-fact > b
{
    min-height: var(--control-h);
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
    font-weight: 600;
    color: var(--ink);
}

/*  A group inside one section ("Address" and "Bank" inside "The school as
    issuer"): a small caption across the whole row. */
body.sis .sis-fgrid > .sis-f-sub
{
    grid-column: 1 / -1;
    margin: 6px 0 -4px;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .03em;
    text-transform: uppercase;
    color: var(--ink-soft);
}

body.sis .sis-fgrid > .sis-f-sub:first-child
{
    margin-top: 0;
}

/*  A tick box across the row, level with its words. */
body.sis .sis-fgrid > .checkline
{
    grid-column: 1 / -1;
    flex-direction: row;
}

/*  A select or a text field in a grid fills its cell (roomsedit.css does it
    for inputs only). */
body.sis .sis-fgrid select,
body.sis .sis-fgrid textarea
{
    width: 100%;
    min-width: 0;
}

@container fsec (min-width: 760px)
{
    body.sis .sis-fgrid
    {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }

    body.sis .sis-fgrid > *
    {
        grid-column: span 2;
    }

    body.sis .sis-fgrid > .sis-f-short
    {
        grid-column: span 1;
    }

    body.sis .sis-fgrid > .wide,
    body.sis .sis-fgrid > .checkline,
    body.sis .sis-fgrid > .sis-f-sub
    {
        grid-column: 1 / -1;
    }
}

@container fsec (max-width: 519px)
{
    body.sis .sis-fgrid
    {
        grid-template-columns: minmax(0, 1fr);
    }

    /*  Two short fields still share a row on a phone (postcode | town,
        lectures | practice): a quarter of a desktop row is half of this. */
    body.sis .sis-fgrid:has(> .sis-f-short)
    {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    body.sis .sis-fgrid:has(> .sis-f-short) > :not(.sis-f-short)
    {
        grid-column: 1 / -1;
    }
}

/*  What is seldom changed, folded - a disclosure that reads as part of the
    section, with a chevron that turns. */
body.sis .sis-fsec-more
{
    margin: 0 0 12px;
    padding: 10px 0 0;
    border-top: 1px dashed var(--line);
}

body.sis .sis-fsec-more > summary
{
    display: inline-flex;
    align-items: center;
    gap: 6px;
    cursor: pointer;
    list-style: none;
    font-size: 13px;
    font-weight: 600;
    color: var(--brand-ink);
}

body.sis .sis-fsec-more > summary::-webkit-details-marker
{
    display: none;
}

body.sis .sis-fsec-more > summary::before
{
    content: "";
    width: 7px;
    height: 7px;
    border-right: 1.5px solid currentColor;
    border-bottom: 1.5px solid currentColor;
    transform: rotate(-45deg);
    transition: transform .15s ease;
}

body.sis .sis-fsec-more[open] > summary
{
    margin-bottom: 12px;
}

body.sis .sis-fsec-more[open] > summary::before
{
    transform: rotate(45deg);
}

body.sis .sis-fsec-more > summary:focus-visible
{
    outline: 2px solid var(--brand);
    outline-offset: 3px;
    border-radius: 4px;
}

/*  A navigation link that belongs to the record, not to its Save ("the
    curriculum of this programme"): next to the heading of the page, so the
    action row holds only what acts on the form. */
body.sis .sis-f-links
{
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin: 0 0 12px;
}

/*  The name of a code under its field ("SI" -> Slovenija), so the clerk
    reads what she chose, not only the code it is stored as. */
body.sis .sis-f-name
{
    font-size: 12px;
    color: var(--ink-soft);
}

/*  A group's caption straight in a section (not in its grid): a table of
    facts under its own small heading (the school's "What the country
    brings"). */
body.sis .sis-fsec > .sis-f-sub
{
    margin: 14px 0 8px;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .03em;
    text-transform: uppercase;
    color: var(--ink-soft);
}

/*  A table straight in a section (the school's "What the country brings")
    scrolls in the section on a phone instead of running out of the card -
    the rule of 16 for .formbox and .sis-section, for this pattern too. */
body.sis .sis-fsec:has(> table.edittable, > form > table.edittable):not(:has(details))
{
    overflow-x: auto;
}

/*  A .formbox made a section (finance settings) keeps the stack's rhythm:
    its own 18px bottom margin on top of the stack's 14px gap read as a
    hole between two parts of one form. */
body.sis .sis-fsecs > .formbox.sis-fsec
{
    margin: 0;
}

/* ---- 18.1 THE WISE ED MARK ON THE SIGN-IN AND THE SCHOOL DOOR ------- */

/*  Both doors showed the Wise Timetable logo (a picture of the words "wise
    timetable") above the heading "Wise Ed - Student information system":
    two product names, and the bigger one the wrong one. The corner of every
    screen already says wise ed with the family's four squares
    (templates/_brand.php); the door says the same, drawn, so it is sharp
    and follows the theme. */
.sis-edmark
{
    display: inline-flex;
    align-items: center;
    gap: 12px;
    margin: 0 auto;
    color: var(--ink);
}

.sis-edmark svg
{
    width: 44px;
    height: 44px;
    flex: none;
}

.sis-edmark-name
{
    font-size: 40px;
    font-weight: 500;
    letter-spacing: -.02em;
    line-height: 1;
}

.sis-edmark-name b
{
    font-weight: 700;
    color: var(--brand-ink);
}

/*  The school door is drawn without the SIS's body class (Pages\RootPage),
    so section 2's light accent for the dark theme does not reach it: the
    mark carries its own, the same mix. */
body.mode-dark .sis-edmark-name b
{
    color: color-mix(in srgb, var(--brand) 50%, #ffffff);
}

@media (prefers-color-scheme: dark)
{
    body:not(.mode-light) .sis-edmark-name b
    {
        color: color-mix(in srgb, var(--brand) 50%, #ffffff);
    }
}

@media (max-width: 420px)
{
    .sis-edmark svg
    {
        width: 34px;
        height: 34px;
    }

    .sis-edmark-name
    {
        font-size: 31px;
    }
}


/*  The door's hint under the school code ("the word after
    wise-tt.com/web/") read at 4.3:1: the administration's .sub is the soft
    ink at 85% opacity. At full strength it reads 5.9:1 and stays quiet. */
.signin-door .field .sub
{
    color: var(--ink-soft);
    opacity: 1;
}


/* ================================================================== */
/* 19. A FORM OF SECTIONS UNDER ITS OWN HEADING - auditB, 15 Oct 2026. */
/* ================================================================== */

/*  Append-only, auditB's section (18 is auditA's pattern and is used as it
    is: .sis-fsecs / .sis-fsec / .sis-fsec-head / .sis-fgrid).

    WHERE A SETTINGS BOX BECAME SECTIONS - the requests' rules, the school in
    eVS, a new CRM person - the box's own heading and lead line used to sit
    inside the one card that held every field. Now the sections are the
    cards, so the form's heading stands above them as a plain heading of the
    page: the same size as a card's h2, a little air above it to part it
    from what came before (a list, another box), its lead line quiet under
    it. One class, .sis-fsecs-titled, on the wrapper; the shared tokens only,
    so both themes read. */
body.sis .sis-fsecs-titled
{
    margin: 22px 0 0;
}

body.sis .sis-fsecs-titled > h2
{
    margin: 0 0 4px;
    font-size: 17px;
    font-weight: 600;
    line-height: 1.35;
    color: var(--ink);
}

body.sis .sis-fsecs-titled > .fieldhint
{
    margin: 0 0 12px;
    max-width: 90ch;
}

body.sis .sis-fsecs-titled > h2 + .sis-fsecs,
body.sis .sis-fsecs-titled > h2 + form
{
    margin-top: 10px;
}

/*  A tick that confirms texts spread over two sections ("I have read
    these texts") stands under the stack, level with its words, before the
    Save - not inside one section, which would say it is about that one. */
body.sis .sis-fsecs-titled form > .checkline
{
    display: flex;
    margin: 10px 0 2px;
}

/* ================================================================== */
/* 20. THE LONG FORM'S ACTION ROW AND THE UNSAVED-CHANGES GUARD -      */
/*     tag `polish`, 15 Oct 2026 (sis-forms.js).                       */
/* ================================================================== */

/*  Append-only. The ergonomics audit's open questions 2 and 4: a long
    settings screen (finance settings is ~3000 px) had its one Save at the
    very end, and a screen with several Saves lost the changes of the other
    parts on every save. public/assets/sis-forms.js decides which form is
    long and which has unsaved changes; this section only draws it.

    THE STICKY ROW. sis-forms.js gives a form's action row (.formactions, or
    .editbar where a settings screen kept the administration's name for it)
    .sis-stickrow
    while the form is taller than the window, and .is-stuck while the row
    is actually standing in for its place below the window. At rest (the
    end of the form reached) the row looks as it always did; stuck, it is a
    bar on the page's surface with a hairline and a soft shadow above it, so
    the fields scrolling under it read as going UNDER it. Its padding is the
    same stuck or not, so nothing jumps when it sticks. The shared tokens
    only - both themes read. */
body.sis :is(.formactions, .editbar).sis-stickrow
{
    position: sticky;
    bottom: 0;
    z-index: 6;
    margin-inline: -10px;
    padding: 10px;
    border-top: 1px solid transparent;
    border-radius: var(--radius-s) var(--radius-s) 0 0;
    transition: background-color .15s ease, box-shadow .15s ease, border-color .15s ease;
}

body.sis :is(.formactions, .editbar).sis-stickrow.is-stuck
{
    background: var(--surface);
    border-top-color: var(--line);
    box-shadow: 0 -8px 16px -12px rgb(16 24 40 / .35);
}

body.mode-dark :is(.formactions, .editbar).sis-stickrow.is-stuck
{
    box-shadow: 0 -8px 18px -10px rgb(0 0 0 / .7);
}

@media (prefers-color-scheme: dark)
{
    body.admin:not(.mode-light) :is(.formactions, .editbar).sis-stickrow.is-stuck
    {
        box-shadow: 0 -8px 18px -10px rgb(0 0 0 / .7);
    }
}

/*  A CARD WHOSE TABLE FITS need not scroll sideways (section 16 makes every
    card with a table do so, for the phone): sis-forms.js marks it, while
    its table fits, so the long form's row inside it can stick to the window
    instead of to the card. !important because section 16's selector (a
    :has() with two arguments) outweighs any plain class, and this is a
    state the script sets and takes away on every resize. */
body.sis .sis-stick-host
{
    overflow-x: visible !important;
}

/*  The marker sis-forms.js puts after a sticky row, to know when the row is
    stuck. It takes no room. */
body.sis .sis-stick-end
{
    display: block;
    height: 0;
}

/*  A field the browser scrolls to (Tab, a validation bubble) is not left
    under the stuck row. */
html.sis-has-stick
{
    scroll-padding-bottom: 88px;
}

@media (prefers-reduced-motion: reduce)
{
    body.sis :is(.formactions, .editbar).sis-stickrow
    {
        transition: none;
    }
}

/*  "UNSAVED CHANGES" - a status, not a button: the warn tone of the status
    vocabulary (sis.css THE SHELL: .st-warn), a dot and the words, at the
    end of the row. It appears the moment a field differs from what was
    drawn and goes when it is put back. */
body.sis .sis-unsaved
{
    --st: #d97706;
    --st-ink: #8a4b06;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-inline-start: auto;
    font-size: 13px;
    font-weight: 600;
    color: var(--st-ink);
    white-space: nowrap;
}

body.sis .sis-unsaved::before
{
    content: "";
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--st);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--st) 22%, transparent);
}

body.sis .sis-unsaved[hidden]
{
    display: none;
}

body.mode-dark .sis-unsaved
{
    --st: #f59e0b;
    --st-ink: #fcd38a;
}

@media (prefers-color-scheme: dark)
{
    body.admin:not(.mode-light) .sis-unsaved
    {
        --st: #f59e0b;
        --st-ink: #fcd38a;
    }
}

/*  On a phone the row wraps (section 8); the note takes its own line under
    the buttons rather than squeezing them. */
@media (max-width: 560px)
{
    body.sis .sis-unsaved
    {
        flex-basis: 100%;
        margin-inline-start: 0;
    }
}

/*  THE QUESTION (a <dialog> sis-forms.js makes once): what would be lost,
    by name, and two answers - stay (primary, focused: the safe one) and go
    ahead. The administration's panel look: the surface, a large radius, the
    deepest shadow, the backdrop of the event dialog. */
.sis-guard-dlg
{
    width: min(520px, calc(100vw - 32px));
    max-height: 86vh;
    overflow: auto;
    padding: 20px 22px 6px;
    border: 1px solid var(--line);
    border-radius: var(--radius-l);
    background: var(--surface);
    color: var(--ink);
    box-shadow: var(--shadow-3);
}

.sis-guard-dlg::backdrop
{
    background: rgb(0 0 0 / .42);
}

.sis-guard-dlg h2
{
    margin: 0 0 8px;
    font-size: 17px;
    font-weight: 600;
    line-height: 1.35;
}

.sis-guard-dlg .sis-guard-text
{
    margin: 0 0 8px;
    color: var(--ink-soft);
    line-height: 1.45;
}

.sis-guard-dlg .sis-guard-list
{
    margin: 0;
    padding-inline-start: 20px;
    line-height: 1.5;
}

.sis-guard-dlg .sis-guard-list li
{
    overflow-wrap: anywhere;
}

.sis-guard-dlg .formactions
{
    flex-wrap: wrap;
    row-gap: 10px;
    column-gap: 18px;
    margin-top: 18px;
}

/*  The two answers are apart by the row's own gap; the house rule that
    steps the button after the primary back (roomsedit.css, "Save and Cancel
    are not a pair") would indent the second answer when the row wraps on a
    phone. */
.sis-guard-dlg .formactions > .btn-primary + .btn
{
    margin-inline-start: 0;
}

/*  On the dark theme a black veil at .42 hardly shows over a page that is
    already dark; the question must still read as standing in front. */
body.mode-dark .sis-guard-dlg::backdrop
{
    background: rgb(0 0 0 / .62);
}

@media (prefers-color-scheme: dark)
{
    body.admin:not(.mode-light) .sis-guard-dlg::backdrop
    {
        background: rgb(0 0 0 / .62);
    }
}

/* ================================================================== */
/* 21. THE RAIL'S LONG NAMES - the lead, 15 Oct 2026.                 */
/* ================================================================== */

/*  Mišo, 15 Oct 16:44, with a Portuguese screenshot: "kod nekih jezika se
    pojavi dole na meniju horizontalni scroll". The rail is 210px wide and its
    items are white-space: nowrap (admin.css, written for the timetable's
    short English and Slovenian names). Measured on the live office home in
    all 13 languages: 11 of them had a name wider than the rail - "Exportação
    para a contabilidade", "Cronologia della sincronizzazione", "Regeln für
    Abschlussarbeiten", even Croatian "Diplome i dopunske isprave" - and
    .lrail-scroll, being overflow-y: auto, then scrolls sideways too (a box
    that scrolls one way computes auto in the other).

    THE CURE IS TO WRAP, NOT TO CUT. An ellipsis would hide the very word
    that tells two items apart ("Impostazioni della contabilità" /
    "...della sincronizzazione"), so a long name takes a second line; the
    icon stays centred beside it (.lrail-item is align-items: center) and
    keeps its 16px (flex: 0 0 16px). A word longer than the whole rail still
    breaks (overflow-wrap), hyphenated where the browser knows the language.
    overflow-x: hidden is the belt to those braces - the sideways bar can
    never come back, whatever a later translation does.

    ONLY THE UPRIGHT RAIL (min-width: 901px, not folded). Below 900px the
    rail is a strip across the top that SHOULD scroll sideways (admin.css,
    max-width: 900px), and folded it shows icons only. Scoped to body.sis:
    the timetable's administration keeps its own rail as it is. */
@media (min-width: 901px)
{
    body.sis .lrail-scroll
    {
        overflow-x: hidden;
    }

    body.sis .lrail:not(.lrail-folded) .lrail-item
    {
        white-space: normal;
        padding-top: 5px;
        padding-bottom: 5px;
    }

    body.sis .lrail:not(.lrail-folded) .lrail-label
    {
        min-width: 0;
        overflow-wrap: break-word;
        hyphens: auto;
    }
}


/* ================================================================== */
/* 22. THE RAIL'S SECTIONS IN TWO TONES - 19 Oct 2026.                */
/* ================================================================== */

/*  Mišo, 19 Oct 01:12: "sad je to prilicno duga lista i bilo bi fino da
    imamo svaku sekciju malo samo drugacije obojenu - pa ne mislim da bi bilo
    sad sareno sve - mogli bi imati interleave - parne jedna nijansa, neparne
    druga nijansa, sa samo malo vise istaknutim naslovima sekcija ... (isto
    vazi sto se tice boja i za ikone kad skupis meni)".

    WHY. With every module installed the rail is some thirty places under a
    dozen headings, and the only thing telling one module from the next was
    a 10.5px grey uppercase word. Read top to bottom it was one long list;
    the eye had nothing to land on when it came back to it.

    WHAT. _leftrail.php wraps each drawn section in a box: .lrail-sec-a and
    .lrail-sec-b alternate down the rail (counted over the sections that are
    actually drawn, so a section hidden by roles or by the school's country
    never leaves two boxes of one tone touching). The overview at the top
    has no heading and stays on the bare rail (.lrail-sec-top) - it is the
    way home, not a module.

    TWO TONES, NOT TWO COLOURS. Tone A is a whisper of the ink (grey on a
    light page, a lighter grey on a dark one), tone B a whisper of the
    theme's own accent. Both are mixed against `transparent`, not against a
    surface, so they tint whatever the rail sits on in all eight palettes,
    light and dark, and no theme needs its own pair. "Ne šareno": nothing
    here is a new hue - the accent was already on the rail in the current
    entry.

    THE CURRENT ENTRY HAD TO MOVE UP. admin.css tints it with 10% of the
    accent - and tone B is 10% of the accent, so inside a B box "you are
    here" would have vanished. It is now 20% plus a one-pixel ring of the
    accent, distinct on either tone and on the bare rail. Hover is an
    overlay of the ink rather than --surface-2, which on a tinted box would
    LIGHTEN the row instead of marking it.

    THE HEADINGS, a little stronger: full ink instead of --ink-soft, 700
    instead of 650, 11px instead of 10.5px. Still small capitals - a heading
    that shouts competes with the screen beside it all day.

    FOLDED, THE BOXES CARRY THE ICONS. The headings used to fold into a
    hairline; the boxes now say "these belong together" by themselves, in
    the same two tones, so the hairline goes (display: none - the heading
    still names the group for a screen reader through aria-labelledby, which
    reads hidden elements).

    THE GEOMETRY DOES NOT MOVE. The rail's side padding gives the box its
    4px (open) or 3px (folded), so items keep their 190px / 40px and the
    long-name wrapping of section 21 is unchanged; the current entry's
    spine is moved onto the box's edge, inside .lrail-scroll, which clips
    everything outside it (overflow-y: auto). Below 900px the rail is a row
    across the top: the boxes are `display: contents` there and the row is
    exactly what it was.

    THE TEST THAT KEEPS THIS HONEST: tools/render-roles.php counts
    `class="lrail-head"` and live-crawl.py reads `<a class="lrail-item"
    href=` - neither class attribute was touched, only boxes added around
    them. */

body.sis .lrail
{
    --lrail-tone-a:     color-mix(in srgb, var(--ink) 4.5%, transparent);
    --lrail-tone-b:     color-mix(in srgb, var(--brand) 10%, transparent);
    --lrail-tone-hover: color-mix(in srgb, var(--ink) 8%, transparent);
    --lrail-tone-here:  color-mix(in srgb, var(--brand) 20%, transparent);
    --lrail-ring-here:  color-mix(in srgb, var(--brand) 38%, transparent);
}

/*  DARK NEEDS MORE, as admin.css says of every tint: a 4% mix on a dark
    page is invisible. Proportions up, the same two hues. */
body.sis.mode-dark .lrail
{
    --lrail-tone-a:     color-mix(in srgb, var(--ink) 6%, transparent);
    --lrail-tone-b:     color-mix(in srgb, var(--brand) 17%, transparent);
    --lrail-tone-hover: color-mix(in srgb, var(--ink) 10%, transparent);
    --lrail-tone-here:  color-mix(in srgb, var(--brand) 32%, transparent);
    --lrail-ring-here:  color-mix(in srgb, var(--brand) 55%, transparent);
}

@media (prefers-color-scheme: dark)
{
    body.sis:not(.mode-light) .lrail
    {
        --lrail-tone-a:     color-mix(in srgb, var(--ink) 6%, transparent);
        --lrail-tone-b:     color-mix(in srgb, var(--brand) 17%, transparent);
        --lrail-tone-hover: color-mix(in srgb, var(--ink) 10%, transparent);
        --lrail-tone-here:  color-mix(in srgb, var(--brand) 32%, transparent);
        --lrail-ring-here:  color-mix(in srgb, var(--brand) 55%, transparent);
    }
}

@media (min-width: 901px)
{
    /*  The box's 4px comes out of the rail's 10px, so the items do not
        lose any width. */
    body.sis .lrail
    {
        padding-left: 6px;
        padding-right: 6px;
    }

    body.sis .lrail.lrail-folded
    {
        padding-left: 3px;
        padding-right: 3px;
    }

    body.sis .lrail-sec
    {
        display: flex;
        flex-direction: column;
        gap: 1px;
        padding: 3px 4px;
        border-radius: 8px;
    }

    body.sis .lrail-sec + .lrail-sec
    {
        margin-top: 4px;
    }

    body.sis .lrail-sec-a
    {
        background: var(--lrail-tone-a);
    }

    body.sis .lrail-sec-b
    {
        background: var(--lrail-tone-b);
    }

    /*  Both selectors: admin.css gives :first-of-type 18px (to clear the
        fold chevron when the heading was the rail's first), and inside a
        box every heading is the first of its type. The chevron now sits
        beside the overview, which has no heading. */
    body.sis .lrail-sec .lrail-head,
    body.sis .lrail-sec .lrail-head:first-of-type
    {
        margin: 5px 0 3px 10px;
        font-size: 11px;
        font-weight: 700;
        letter-spacing: .06em;
        color: var(--ink);
    }

    body.sis .lrail .lrail-sec .lrail-item:not(.lrail-here):hover
    {
        background: var(--lrail-tone-hover);
    }

    body.sis .lrail .lrail-sec .lrail-here
    {
        background: var(--lrail-tone-here);
        box-shadow: inset 0 0 0 1px var(--lrail-ring-here);
    }

    /*  The spine on the box's own edge (4px of box padding to the left of
        the item) - at -10px it was outside the scroller and clipped. */
    body.sis .lrail .lrail-sec .lrail-here::before
    {
        left: -4px;
    }

    /*  FOLDED: a box of icons, shrunk to them and centred on the rail. */
    body.sis .lrail-folded .lrail-sec
    {
        padding: 3px;
        align-items: center;
    }

    body.sis .lrail-folded .lrail-sec .lrail-head
    {
        display: none;
    }

    body.sis .lrail.lrail-folded .lrail-sec .lrail-here::before
    {
        left: -3px;
    }
}

/*  THE ROW ACROSS THE TOP (under 900px) stays as it was: the boxes give
    their items back to the row. */
@media (max-width: 900px)
{
    body.sis .lrail-sec
    {
        display: contents;
    }
}

/*  A TEXT BOX SHOWS ITS LINES (Mišo, 26 Oct 2026). admin.css gives every .input the one
    control height, --control-h - right for a field somebody types a name into, wrong for a
    <textarea class="input">: a note, the API key's address list, a document's description,
    the letter bodies (rows=14) all opened one or two lines tall and read as if something had
    been cut off. In the office the markup's rows decide again, and every box still grows by
    hand. :where() keeps this at the weight of one class, so a module that sizes its own boxes
    (electives, placements, the guardians' relations list) still wins. */
body.sis :where(textarea.input)
{
    height: auto;
    line-height: 1.45;
    resize: vertical;
}


/* ================================================================== */
/* 23. THE RAIL REACHES THE BOTTOM OF THE WINDOW - 26 Oct 2026.       */
/* ================================================================== */

/*  Mišo, with the rail ending at "Degree audit" over 150px of nothing: "i
    sidebar moze bit jos malo visi - spustis ga jos jedno 3 opcije".

    The nothing was admin.css's --foot-h: 7rem taken off the rail's height so
    a sticky rail could not hang into the footer on a short page (12 Sep, the
    rail landing on the wise timetable mark). That is a RESERVATION for a
    footer that, on a screen as tall as a laptop's, is not even on screen.

    THE OTHER CURE FOR THE SAME COLLISION: the shell is never shorter than the
    window under the top bar, so the footer always starts below the fold and
    the rail - which cannot leave its shell - has the whole height to itself.
    The footer is then one scroll away on a short page, which is where a
    footer belongs. 12px stays reserved as breathing room at the bottom.

    Upright rail only (min-width: 901px); below that the rail is a strip
    across the top and neither number applies. */
@media (min-width: 901px)
{
    /*  REVERSED, 8 Nov 2026. Misho: "smanji container lijevog traila toliko da se na 1080
        vertikalno jos vidi nas logo dole lijevo - onda skrolamo malo duze do dna funkcija".
        The 26 Oct cure above pushed the footer (and the wise mark in it) below the fold. Now the
        rail leaves the footer's height free (--foot-h, the 7rem admin.css reserves: the footer
        at its usual height plus its margin) and the shell is never shorter than the rail, so the
        footer starts exactly at the bottom of the window: the logo is on the first screen, and a
        long menu scrolls inside the rail (.lrail-scroll) down to its last function. */
    body.sis .shell:has(> .lrail)
    {
        min-height: calc(100vh - var(--topbar-h) - var(--foot-h-keep, 3rem));
    }

    body.sis .lrail
    {
        --foot-h: var(--foot-h-keep, 3rem);
    }
}


/* ---- 22. A SOFT LABEL IN A ROW SITS ON THE ROW'S MIDDLE (26 Oct 2026) - */

/*  .sis-meta was made for a line of small print UNDER something (a hint
    below a field, "changed 3 days ago" under a title), so it carries a 6px
    top margin. Inline in running text that margin does nothing - but as a
    flex item (a filter row, a list bar, a heading row) it pushes the label
    6px down inside a centred row: on the materials screen "Študijsko leto"
    sat visibly below "2025/2026" (Mišo, 26 Oct). An inline element with
    this class is never a line of its own, so it never wants that margin;
    the block forms (p, div) keep it. */
body.sis span.sis-meta,
body.sis small.sis-meta,
body.sis a.sis-meta,
body.sis em.sis-meta,
body.sis strong.sis-meta,
body.sis label.sis-meta
{
    margin: 0;
}

/*  And a block one that is itself an item of a centred row - a filter
    row, the materials' year picker - drops it too, for the same reason. */
body.sis .sis-filters > .sis-meta,
body.sis .sis-filters > form > .sis-meta,
body.sis .sis-filters > label > .sis-meta
{
    margin: 0;
}

/* ---- 24. EVERY ITEM OF A ROW ON THE ROW'S MIDDLE (align8, 26 Oct 2026) - */

/*  Section 22 fixed the first case Mišo saw ("usput popravi ovakve linije
    gdje nije centrirano sve"). A sweep then measured every row of every
    office, web and administration screen of temp - light and dark, 1440 and
    390 wide - and flagged any row whose words' middles differ by more than
    2px (C:\CVI\_xfer\wave8\align8\detect.js). What it found is the same
    mistake in four shapes: a class made for a block of its own, with the
    margin or the stacking a block wants, used as ONE ITEM OF A ROW. */

/*  (a) THE HINT BESIDE A BUTTON. .fieldhint is small print under a field
    (margin 0 0 8px). In an action row - "Show who" and the sentence that
    explains it - that bottom margin lifted the sentence 4px above the
    button's middle: ranking, overdue invoices, sync, exams by subject, the
    work month, teaching availability. An item of a centred row never wants
    a block margin; the hint under a field keeps its 8px. */
body.sis :is(.editbar, .formactions, .sis-work-actions, .teach-winline) > .fieldhint
{
    margin-block: 0;
}

/*  (b) A FILTER ROW INSIDE A ROW. .sis-filters keeps 10px under it for when
    it is a line of the page; on the work month it is the right half of the
    bar beside the status chips, and the 10px lifted its fields 5px above
    the chips. */
body.sis .sis-work-bar > .sis-filters
{
    margin: 0;
}

/*  (c) A ROW THAT STRETCHES. A flex row that does not say align-items
    stretches its items to the tallest one, and each item's words then sit
    at its TOP: a padded chip or a button is tall, a soft label is not, so
    "In the timetable as: 64101 ..." rode 10px above the "in the timetable"
    chip beside it on a subject record. The links beside a heading centre. */
body.sis .sis-f-links
{
    align-items: center;
}

/*  (d) A TICK BOX OR A HINT IN A BOTTOM-ALIGNED FORM ROW. .sis-add lines
    its fields up on their bottom edge (a caption over each field) and
    `.sis-add label` stacks its children - so "Required" on the exams'
    coursework form was a tick box with its word UNDER it, 6px below the
    "Add" beside it, and the hint beside "Prepare" (work month) stood on the
    bottom edge, not on the button's middle. Both now take a field's height
    and centre their words in it, so they stand on the line of the fields
    and the buttons whatever the captions above them do. */
body.sis .sis-add > label.checkline,
body.sis .sis-add > .fieldhint
{
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 8px;
    min-height: var(--control-h);
    margin-block: 0;
}


/* ================================================================== */
/* 24. FIGURES AND UNITS - 26 Oct 2026.                               */
/* ================================================================== */

/*  Mišo, on the office home's "Key numbers of this academic year": the
    overdue tile read "6,713.00" on one line and "EUR" on the next, and the
    whole bottom row grew taller around it. "EUR u istoj vrstici kao broj."

    Three causes, three answers, for the whole application:

    (a) A plain space stood between the number and its unit, a place the
        browser may break. InsightsPage::value() now joins them with a
        no-break space, and View\Figure::html() wraps a figure as
        .fig > .fig-num + .fig-unit; .fig never wraps, in a tile or a table
        cell alike.

    (b) The tile's number was a fixed 26px whatever the tile's width - two
        tiles a row on a phone left ~130px for "6,713.00 EUR". A tile is
        now a size container and its number scales with it: 26px on a
        desktop, down to 18px on the narrowest phone, never more.

    (c) The unit was as loud as the number. On a tile the eye should land
        on "6,713.00", with "EUR", "%" or "years" a quiet suffix on the same
        baseline - the way every good dashboard draws it. In table cells
        the unit stays the text's own size: there it is read, not glanced. */

body.sis .fig
{
    white-space: nowrap;
}

body.sis .sis-tile
{
    container-type: inline-size;
    min-width: 0;
}

body.sis .sis-tile-n
{
    font-size: clamp(18px, 13cqi, 26px);
    line-height: 1.15;
    white-space: nowrap;
}

body.sis .sis-tile-n .fig-unit,
body.sis .ins-tile-n .fig-unit,
body.sis .r7-fig-value .fig-unit
{
    font-size: .6em;
    font-weight: 500;
    letter-spacing: .02em;
    opacity: .72;
}

body.sis .ins-tile-n,
body.sis .r7-fig-value
{
    white-space: nowrap;
}

/*  The warning tone of the home's key numbers (students at risk, overdue
    fees) lived in sis-insights.css, which the home page does not load - so
    1,291 students at risk and 6,713 EUR overdue were drawn in the calm
    brand colour, like a count of subjects. It belongs here, with the tile.
    --warn is the SIS's own amber (set on body.sis above, with --danger and
    --ok: 5.9:1 on white, the
    administration's #fbbf24 on dark). */
body.sis .sis-tile-n.is-warn
{
    color: var(--warn);
}

/*  p1030cards, 30 Oct 2026 - the unsaved-changes question in the web's
    panel language (p1030ui): the answers as a footer, a line above, the
    safe one (Stay, primary) on the right. row-reverse because the markup
    puts it first, which is also the order the keyboard reaches them in. */
.sis-guard-dlg .formactions
{
    flex-direction: row-reverse;
    justify-content: flex-start;
    column-gap: 8px;
    padding-top: 12px;
    padding-bottom: 12px;
    border-top: 1px solid var(--line);
}


/* ================================================================== */
/* THE PAGER AND THE EXPORT BAR, THE SAME SHAPE AT ANY WIDTH           */
/* 8 Nov 2026.                                                         */
/* ================================================================== */

/*  Misho, with the "add members" column of a conversation: "malo su tu stvari neuredne".
    The pager was a row of six things that wrapped wherever the column ended - "<<  Back  Page 1
    of 10  1-25" on one line, "Next >>" alone on the next, then the export on a third, and its
    last checkbox ("one value per row, repeated") floating with no heading.

    Now it is a grid of three: Back on the left, the sentence in the middle (it may take two
    lines), Next on the right - in a column of 330px as on a wide table, which is why the pager
    is also kept to 36rem and centred rather than spread across the whole table. The page-size
    select and the export each take a line of their own under it, centred. */
body.sis .pager
{
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    justify-items: center;
    gap: 10px;
    max-width: 36rem;
    margin-left: auto;
    margin-right: auto;
}

body.sis .pager-prev,
body.sis .pager-next
{
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

body.sis .pager-prev { justify-self: start; }
body.sis .pager-next { justify-self: end; }

body.sis .pager-where
{
    text-align: center;
    line-height: 1.35;
}

body.sis .pager-rows
{
    display: inline-block;
    margin-left: .4em;
    white-space: nowrap;
}

body.sis .pager > .pager-per,
body.sis .pager > .io-export
{
    grid-column: 1 / -1;
}

/*  EXPORT: "Export: [Excel] [CSV]" on one line, its option under it, centred - and no longer
    pushed to the right edge (margin-left: auto) of a bar that is not there in a pager. */
body.sis .pager .io-export
{
    margin-left: 0;
    justify-content: center;
    row-gap: 6px;
}

body.sis .io-export-repeat
{
    flex: 1 0 100%;
    justify-content: center;
    margin-left: 0;
    text-align: center;
}


/* ---- THE MARK UNDER THE RAIL'S MENU (8 Nov 2026) --------------------- */

/*  Misa: "wise ed tools logo stavit tik ispod raila - na kraju ce nestat i desni scroll bar".
    The product's mark was the page footer's, a gap below the rail. It is now the rail's own last row
    (templates/_leftrail.php): directly under the last place of the menu, and when the menu is longer
    than the window it is the menu that scrolls, above the mark (.lrail-scroll has min-height: 0 and
    the mark does not shrink). A short menu has no scroll bar at all. Only where the rail is a column:
    under 900px the rail is a row across the top and the footer keeps the mark. */
.lrail-brand
{
    display: none;
}

@media (min-width: 901px)
{
    body.sis .lrail-brand
    {
        display: inline-flex;
        align-items: center;
        gap: 8px;
        flex: 0 0 auto;
        margin-top: 8px;
        padding: 8px 10px 2px;
        text-decoration: none;
        color: var(--ink-soft);
    }

    body.sis .lrail-brand:hover
    {
        color: var(--ink);
    }

    /*  Folded: only the mark (the rail centres its children). */
    body.sis .lrail-folded .lrail-brand
    {
        padding-left: 0;
        padding-right: 0;
    }

    body.sis .lrail-folded .lrail-brand .brand-name
    {
        display: none;
    }

    /*  The footer's own copy goes where the rail has one; a screen without a rail (sign-in, landing) keeps it. */
    body.sis .shell:has(> .lrail) + .foot .foot-brand
    {
        display: none;
    }
}
