/* FSTA Auswertung — design foundation.
   Plain, high-contrast, professional. No framework, no build step.
   Every later page reuses the building blocks below (nav, card, form, button,
   table, errors) instead of adding task-specific CSS. */

/* ---------------------------------------------------------------- tokens */
:root {
    /* Accent: dark red, matching the title colour of the ranking PDF. */
    --color-accent: #8b0000;
    --color-accent-dark: #6d0000;
    --color-accent-soft: #f7ecec;

    --color-text: #1a1a1a;
    --color-text-muted: #5c5c5c;
    --color-bg: #ffffff;
    --color-bg-subtle: #f5f4f3;
    --color-border: #cfcbc8;
    --color-border-strong: #9a9490;

    --color-error: #8b0000;
    --color-error-bg: #fbeaea;
    --color-success: #1e6b32;
    --color-success-bg: #e9f4ec;

    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-5: 1.5rem;
    --space-6: 2rem;
    --space-7: 3rem;

    --font-sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-size-sm: 0.875rem;
    --font-size-base: 1rem;
    --font-size-lg: 1.125rem;
    --font-size-xl: 1.5rem;
    --font-size-2xl: 2rem;

    --radius: 3px;
    --border: 1px solid var(--color-border);
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.08);

    /* Comfortable hit area for touch use during data entry. */
    --touch-target: 2.75rem;
    --content-width: 74rem;
}

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

/* `overflow-anchor: none` IS THE FIX OF TASK-078, AND IT IS DELIBERATELY GLOBAL. Chrome prefers the
   focused element as the scroll anchor. Every page of this application saves by swapping a whole
   section, which removes the field or the button that has the focus — and the browser then corrects
   the scroll position against an anchor that no longer exists, which ends at the bottom stop: the
   page jumped to the end of the list. Measured 370 → 2389 with the focus in a field, 370 → 370 with
   this line, and the same 2389 with the focus on the submit button.

   Suppressed rather than overridden. A scroll of our own that runs against the anchoring wins
   sometimes and loses sometimes — which is exactly what made the defect sporadic on a computer — so
   the correction is turned off rather than raced. Here on `html`, the scroll container of the
   document, so that no page has to remember anything; measured on `html` alone.

   What it costs: anchoring exists to keep the reading position when content above the viewport
   changes height late. This application renders on the server and loads nothing above the fold
   afterwards — the one image in it is the organiser logo, bounded to 200×120 by `.logo-vorschau`,
   on the one page that carries no list at all. The trade is therefore a property nothing here uses
   against a defect that hits every save. */
html {
    font-family: var(--font-sans);
    font-size: 100%;
    line-height: 1.5;
    color: var(--color-text);
    background: var(--color-bg-subtle);
    -webkit-text-size-adjust: 100%;
    overflow-anchor: none;
}

/* The column is what keeps the footer at the foot — see the footer section for why.

   dvh RATHER THAN vh, AND BOTH LINES ARE MEANT (TASK-074). `100vh` is measured against the viewport
   WITHOUT the browser's own chrome, while the visible area shrinks when the address bar is shown —
   so on a phone a page that fits became scrollable by about the height of that bar, and the footer
   was cut off. Confirmed on a device on /login, which is the shortest page there is: content that
   fits, and a page that still scrolls. `100dvh` resolves against what is actually visible.

   The `100vh` above it is the fallback and costs one line: a browser that does not know the unit
   drops the second declaration, and without the first there would be no minimum height at all — the
   footer would sit under the content in the middle of an empty window, which is what TASK-073 set
   out to fix. On everything that knows dvh the second wins, and on a desktop the two are the same
   number anyway, there being no chrome that comes and goes. */
body {
    margin: 0;
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    min-height: 100dvh;
}

h1,
h2,
h3 {
    line-height: 1.25;
    margin: 0 0 var(--space-4);
    font-weight: 600;
}

h1 {
    font-size: var(--font-size-2xl);
}

h2 {
    font-size: var(--font-size-xl);
}

h3 {
    font-size: var(--font-size-lg);
}

p {
    margin: 0 0 var(--space-4);
}

a {
    color: var(--color-accent);
}

a:hover {
    color: var(--color-accent-dark);
}

.lead {
    font-size: var(--font-size-lg);
    color: var(--color-text-muted);
}

/* A section heading that directly follows a form or a table needs air above it, so it does not
   sit cramped against the block before it — the list under an entry form (/schuetzen,
   /teilnahmen), or the per-society titles heading under the first titles table (/rangliste/titel).
   One structural rule rather than a class repeated on every such page. */
form + h2,
.table-wrap + h2 {
    margin-top: var(--space-6);
}

.muted {
    color: var(--color-text-muted);
}

:focus-visible {
    outline: 3px solid var(--color-accent);
    outline-offset: 2px;
}

/* ---------------------------------------------------------------- header */
.app-header {
    background: var(--color-bg);
    border-bottom: 3px solid var(--color-accent);
    box-shadow: var(--shadow);
}

.app-header__bar {
    max-width: var(--content-width);
    margin: 0 auto;
    padding: var(--space-3) var(--space-4);
    display: flex;
    align-items: center;
    gap: var(--space-5);
    flex-wrap: wrap;
}

.app-header__brand {
    display: flex;
    flex-direction: column;
    text-decoration: none;
    color: var(--color-text);
}

.app-header__title {
    font-size: var(--font-size-lg);
    font-weight: 700;
    color: var(--color-accent);
    letter-spacing: 0.02em;
}

.app-header__subtitle {
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
}

/* ------------------------------------------------------------------- nav */
.nav {
    display: flex;
    gap: var(--space-1);
    flex-wrap: wrap;
    margin-right: auto;
}

.nav__link {
    display: inline-flex;
    align-items: center;
    min-height: var(--touch-target);
    padding: 0 var(--space-3);
    border-radius: var(--radius);
    color: var(--color-text);
    text-decoration: none;
    font-weight: 500;
}

.nav__link:hover {
    background: var(--color-bg-subtle);
    color: var(--color-accent);
}

.nav__link--active {
    color: var(--color-accent);
    box-shadow: inset 0 -3px 0 var(--color-accent);
}

/* --------------------------------------------------------- lang switcher */
.lang-switch {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    border: var(--border);
    border-radius: var(--radius);
    overflow: hidden;
}

.lang-switch__item {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: var(--touch-target);
    min-height: var(--touch-target);
    padding: 0 var(--space-2);
    font-size: var(--font-size-sm);
    font-weight: 600;
    text-decoration: none;
    color: var(--color-text-muted);
    background: var(--color-bg);
}

.lang-switch__item:hover {
    background: var(--color-bg-subtle);
    color: var(--color-accent);
}

.lang-switch__item--active {
    background: var(--color-accent);
    color: #fff;
}

/* ------------------------------------------------------------------ main */
.app-main {
    max-width: var(--content-width);
    margin: 0 auto;
    padding: var(--space-6) var(--space-4) var(--space-7);
    /* Takes the free height, which is what pushes the footer down on a short page (TASK-073). */
    flex: 1 0 auto;
    /* width: 100% IS LOAD-BEARING, and the reason is easy to miss. Since the footer made body a
       column flex container, this block is a flex ITEM — and an item with auto margins on the cross
       axis is not stretched: it falls back to shrink-to-fit. Measured after TASK-073: at a 1574px
       viewport .app-main came out 818px wide instead of 1184, because the widest thing on the help
       page is its 46rem prose column, and every page quietly followed its own content. width: 100%
       gives the item a definite cross size again, which max-width caps and the auto margins centre
       — the block layout this had before the footer, restored deliberately. */
    width: 100%;
}

/* ---------------------------------------------------------------- footer */
/* The developer credit (TASK-073). Two things are asked of it, and they pull apart:
   - it must not move the content of any page, which is why nothing above it changes and the rule
     lives at the end rather than in a wrapper around everything;
   - on a short page it must sit at the foot of the WINDOW, not float in the middle of an empty
     one. That is what the body column plus flex-grow on .app-main does; sticking it to the
     viewport with position: fixed would have taken it out of the flow and over the content.
   Subordinate by construction: the small size and the muted colour app.css already defines for
   secondary text, so the line cannot compete with a page it shares with entry forms. */
.app-footer {
    padding: var(--space-4);
    text-align: center;
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
}

.app-footer p {
    margin: 0;
}

/* 0.8em rather than 1em, and that is optical rather than arithmetic: a heart fills its box almost
   completely while a lowercase letter uses about half of its own, so a heart set to the nominal
   size of the text reads as visibly larger than the words around it. The small negative shift puts
   it ON the baseline instead of leaving it perched above one. */
.app-footer__heart {
    width: 0.8em;
    height: 0.8em;
    fill: currentColor;
    vertical-align: -0.08em;
}

/* The footer's copy of the language switch is not painted on a wide screen — the header has it
   there. See the narrow-screen section at the end of this file, which is where the two swap. */
.app-footer .lang-switch {
    display: none;
}

/* ------------------------------------------------------------------ card */
.card {
    background: var(--color-bg);
    border: var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: var(--space-5);
    margin-bottom: var(--space-5);
}

.card__header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-4);
    margin-bottom: var(--space-4);
}

.card > :last-child {
    margin-bottom: 0;
}

/* ----------------------------------------------------------------- forms */
.form__row {
    margin-bottom: var(--space-4);
}

.form__grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: var(--space-4);
}

/* A grid cell holding only a button. Without this the button starts at the top of the cell and
   lines up with the labels of its neighbours instead of with their inputs, because those cells
   spend their first line on a label the button does not have. */
.form__row--aktion {
    display: flex;
    align-items: flex-end;
}

label {
    display: block;
    margin-bottom: var(--space-1);
    font-weight: 600;
    font-size: var(--font-size-sm);
}

input[type="text"],
input[type="number"],
input[type="password"],
input[type="date"],
input[type="search"],
select,
textarea {
    width: 100%;
    min-height: var(--touch-target);
    padding: var(--space-2) var(--space-3);
    font: inherit;
    color: var(--color-text);
    background: var(--color-bg);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius);
}

input:focus,
select:focus,
textarea:focus {
    border-color: var(--color-accent);
}

input[aria-invalid="true"],
.is-invalid {
    border-color: var(--color-error);
    background: var(--color-error-bg);
}

.form__hint {
    display: block;
    margin-top: var(--space-1);
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
}

/* Field-level validation message. */
.field-error {
    display: block;
    margin-top: var(--space-1);
    font-size: var(--font-size-sm);
    font-weight: 600;
    color: var(--color-error);
}

/* Form- or page-level messages. */
.alert {
    padding: var(--space-3) var(--space-4);
    border-left: 4px solid var(--color-border-strong);
    border-radius: var(--radius);
    background: var(--color-bg-subtle);
    margin-bottom: var(--space-4);
}

.alert--error {
    border-left-color: var(--color-error);
    background: var(--color-error-bg);
    color: var(--color-error);
}

.alert--success {
    border-left-color: var(--color-success);
    background: var(--color-success-bg);
    color: var(--color-success);
}

/* --------------------------------------------------------------- buttons */
.button,
button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-height: var(--touch-target);
    padding: 0 var(--space-4);
    font: inherit;
    font-weight: 600;
    color: #fff;
    background: var(--color-accent);
    border: 1px solid var(--color-accent);
    border-radius: var(--radius);
    text-decoration: none;
    cursor: pointer;
}

.button:hover,
button:hover {
    background: var(--color-accent-dark);
    border-color: var(--color-accent-dark);
    color: #fff;
}

.button--secondary {
    color: var(--color-text);
    background: var(--color-bg);
    border-color: var(--color-border-strong);
}

.button--secondary:hover {
    background: var(--color-bg-subtle);
    color: var(--color-accent);
    border-color: var(--color-accent);
}

.button:disabled,
button:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

/* Icon-only action button (edit/delete in a table row): square, the glyph centred. The label
   travels in title + aria-label, so the row stays compact without losing its meaning. */
.button--icon {
    width: var(--touch-target);
    min-width: var(--touch-target);
    padding: 0;
}

/* The glyph inside any button, whether it stands alone (.button--icon) or beside a label. Stroked
   in currentColor on purpose: the icon then follows the label through :hover and :focus-visible
   instead of staying black while the text turns accent.

   flex-shrink: 0 IS NOT DECORATION. A button is a flex container, and an inline <svg> is a flex
   item with no intrinsic size of its own — a viewBox is a coordinate system, not a width. In a
   cramped row a flex item like that is the first thing a browser squeezes, and a squeezed glyph is
   an invisible glyph, while the label beside it stays put. The header bar is exactly such a row:
   with the German admin menu it needs about 1327px against the 1152px the content width offers.
   The svg elements also carry width/height attributes for the same reason, so the intrinsic size
   exists even before this stylesheet is applied — a browser holding a cached older app.css then
   draws a small icon rather than a 300x150 one. */
.button svg,
button svg {
    width: 1.15rem;
    height: 1.15rem;
    flex-shrink: 0;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.8;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* The logout glyph stands alone, next to the language switcher's 600-weight abbreviations. At the
   table-row weight it would read as the faintest thing in the corner, so it is a notch larger and
   a notch heavier: 1.35rem at stroke-width 2 puts its stems at about 1.8px, which is what the
   letters beside it measure. */
.app-header__logout svg {
    width: 1.35rem;
    height: 1.35rem;
    stroke-width: 2;
}

/* A row's action cell: its icon buttons sit side by side with a small, even gap. */
.row-actions {
    display: flex;
    gap: var(--space-2);
}

.button-row {
    display: flex;
    gap: var(--space-3);
    flex-wrap: wrap;
    margin-top: var(--space-5);
}

/* The stored organiser logo, shown so the page reflects what is saved rather than what was last
   picked in the file dialog. Bounded, since the upload is kept at its original resolution. */
.logo-vorschau {
    max-width: 200px;
    max-height: 120px;
    height: auto;
}

/* The edition selector governs all six ranking tabs, so it sits above the tab bar rather than
   inside it — one inline row, clearly separated from the tabs it applies to. */
.rangliste-auswahl {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
    margin-bottom: var(--space-4);
}

/* ------------------------------------------------------------------ tabs */
/* Server-rendered ranking tabs: a row of links, the active one underlined in the accent. */
.tabs {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
    /* Air below the tab bar so the content — or the titles heading — does not sit cramped
       against it. Applies to every /rangliste tab. */
    margin-bottom: var(--space-6);
    border-bottom: 1px solid var(--color-border);
}

.tab {
    padding: var(--space-2) var(--space-4);
    color: var(--color-text-muted);
    text-decoration: none;
    font-weight: 600;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
}

.tab:hover {
    color: var(--color-accent);
}

.tab--active {
    color: var(--color-text);
    border-bottom-color: var(--color-accent);
}

/* ---------------------------------------------------------------- tables */
/* Rankings are the heart of the app: dense but readable, numeric columns aligned. */
.table-wrap {
    overflow-x: auto;
}

table {
    width: 100%;
    border-collapse: collapse;
    font-variant-numeric: tabular-nums;
}

caption {
    text-align: left;
    font-weight: 600;
    padding-bottom: var(--space-2);
}

th,
td {
    padding: var(--space-2) var(--space-3);
    text-align: left;
    border-bottom: 1px solid var(--color-border);
    vertical-align: top;
}

thead th {
    background: var(--color-bg-subtle);
    border-bottom: 2px solid var(--color-border-strong);
    font-size: var(--font-size-sm);
    text-transform: uppercase;
    letter-spacing: 0.03em;
    white-space: nowrap;
    position: sticky;
    top: 0;
}

tbody tr:hover {
    background: var(--color-accent-soft);
}

/* Numeric columns. The config pages use .num (right); the ranking and entry tables use the two
   shared classes below so the same kind of column aligns the same way on every page (NFR-008). */
.num {
    text-align: right;
    white-space: nowrap;
}

/* Counts and small whole numbers — results, Tiefschuss, birth year, participant/Pflicht counts —
   read best centred under their header. */
.num-center {
    text-align: center;
    white-space: nowrap;
}

/* Amounts (Betrag/Auszahlung) and precise decimals (Durchschnitt) align right, so their digits
   and decimal points line up down the column. */
.num-right {
    text-align: right;
    white-space: nowrap;
}

/* A column whose single glyph reads best centred, e.g. the Auszeichnung ✓/–. */
.col-center {
    text-align: center;
}

/* The Auszeichnung marker: ✓ earned, – not earned; an empty cell means "cannot be judged yet"
   and carries no mark, so a blank is never read as a decided "no". */
.mark--yes {
    color: var(--color-success);
    font-weight: 700;
}

.mark--no {
    color: var(--color-text-muted);
}

.rank {
    text-align: right;
    font-weight: 700;
    white-space: nowrap;
}

/* Marks a row that still needs attention, e.g. a pending Teilnahme. */
.row--pending td {
    color: var(--color-text-muted);
    font-style: italic;
}

.badge {
    display: inline-block;
    padding: 0 var(--space-2);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius);
    font-size: var(--font-size-sm);
    font-weight: 600;
    white-space: nowrap;
}

.badge--accent {
    color: #fff;
    background: var(--color-accent);
    border-color: var(--color-accent);
}

/* Shown instead of an empty table or list. */
.empty-state {
    padding: var(--space-6);
    text-align: center;
    color: var(--color-text-muted);
}

/* One society's block in the entry overview. Generous space between the groups so the eye can
   tell them apart at a glance during the event. */
.gruppe + .gruppe {
    margin-top: var(--space-7);
}

/* The society name is the heading; its counts follow in smaller, quieter type. */
.gruppe__meta {
    margin-left: var(--space-3);
    font-size: var(--font-size-sm);
    font-weight: 400;
    color: var(--color-text-muted);
}

/* --------------------------------------------------------- session block */
/* The form around the logout button, last item in the header bar. It used to carry the user name
   beside the button as well; that is the button's title now, and the block is a wrapper that keeps
   the form from adding a margin of its own. */
.app-header__session {
    display: flex;
    align-items: center;
    margin: 0;
}

/* ------------------------------------------------------------------ help */
/* The manual (ADR-013). Its markup comes from Markdown, so it is plain h1/h2/p/ul and cannot carry
   classes of its own — these rules address the elements directly, inside .hilfe only. */

/* The measure the prose is set at. Prose is read line after line, unlike the tables everywhere
   else, and the full content width would make it hard to find the start of the next line. It is
   carried by the CARD (see .card--text), not by a wrapper inside it: with the sheet exactly as wide
   as the text, heading, index and prose share its edges by having nowhere else to go, and the white
   ends where the text ends. */

/* Air between sections, and a rule that makes the boundary visible without a heading level for it. */
.hilfe section + section {
    margin-top: var(--space-7);
    padding-top: var(--space-5);
    border-top: var(--border);
}

.hilfe ul,
.hilfe ol {
    margin: 0 0 var(--space-4);
    padding-left: var(--space-5);
}

.hilfe li + li {
    margin-top: var(--space-2);
}

/* The contextual help control (TASK-067): a round "?" beside a page heading, opening the manual at
   that page's section.

   `flex: none` and `margin-right: auto` are what keep it from disturbing the header it joins.
   .card__header is a flex row with `justify-content: space-between`, so a third child would spread
   the row and push the year badge to the middle. Absorbing the free space to the right of the
   control instead leaves the heading at the left and the badge at the right, exactly where both
   were before. */
.hilfe-link {
    flex: none;
    align-self: center;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.5rem;
    height: 1.5rem;
    margin-right: auto;
    border: 1px solid var(--color-border-strong);
    border-radius: 50%;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-weight: 600;
    line-height: 1;
    text-decoration: none;
}

.hilfe-link:hover,
.hilfe-link:focus-visible {
    border-color: var(--color-accent);
    color: var(--color-accent);
}

/* The result mask's heading is an ordinary h2, not a flex row, so there the control follows the
   text inline and the auto margin has nothing to absorb. */
h2 > .hilfe-link {
    margin-left: var(--space-3);
    margin-right: 0;
    vertical-align: middle;
}

/* The table of contents: the sections in index order, above the prose. */
.hilfe__inhalt ul {
    margin: 0 0 var(--space-6);
    padding-left: var(--space-5);
}

.hilfe__inhalt li + li {
    margin-top: var(--space-1);
}

/* Narrow card for focused forms such as the login page. */
.card--narrow {
    max-width: 26rem;
    margin-left: auto;
    margin-right: auto;
}

/* The same idea as .card--narrow, at the width of a column of prose: the card is exactly the
   measure plus its own padding, so the white sheet ends where the text ends and the page background
   stands beside it, as it does above and below. Used by the help page alone.

   The calc rather than 49rem so that a change to the card's padding cannot silently leave the sheet
   wider or narrower than the text it is cut for. */
.card--text {
    max-width: calc(46rem + 2 * var(--space-5));
    margin-left: auto;
    margin-right: auto;
}

/* ------------------------------------------------------- narrow screens */
/* THE FIRST MEDIA QUERY IN THIS PROJECT (TASK-076), and therefore the reference for every later
   one. It is deliberately NOT derived from the tables.

   Measured on /teilnahmen with 40 rows: the entry table needs 989px and the ranking tables are of
   the same order, so below roughly 1000px they scroll inside .table-wrap — which is what
   `overflow-x: auto` is there for and needs no breakpoint at all. Tying this value to that number
   would have conflated two questions and hidden the application title on a laptop.

   What this query is about is VERTICAL space, so it asks about vertical space. Measured header
   heights on /teilnahmen: 145px at 1094px wide, 213px at 994, 261px from 894 down to 594, and 309px
   at 494 — a third of a phone screen before the first line of content. The width arm catches a
   phone held upright; the height arm catches the same phone turned sideways, where the viewport is
   some 390px tall and the header would eat most of it, and a desktop window somebody has dragged
   short. 48rem/30rem rather than pixels, so the whole thing scales with a reader's font size.

   Two changes, and both are one line:
   - The brand block goes. Hiding only its two text spans would leave an <a href="/"> with no
     accessible name at all — worse than the space it saves — so the LINK goes and the application
     name keeps being announced by the <title> of every page, which carries it already.
   - The language switch swaps ends: out of the header, into the footer. It is not removed, and that
     is the point: on /login it is the only way to change the language, and a French-speaking
     Erfasser facing a German page cannot be sent through Profil to find it. */
@media (max-width: 48rem), (max-height: 30rem) {
    .app-header__brand {
        display: none;
    }

    .app-header .lang-switch {
        display: none;
    }

    /* Stacked rather than side by side: on a narrow screen the credit is already a full line, and
       the two would either collide or force the credit to wrap around the switch. Centred under it,
       with a gap of its own, the footer reads as two short lines instead of one crowded one. */
    .app-footer {
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: var(--space-3);
    }

    .app-footer .lang-switch {
        display: inline-flex;
    }
}
