/* Grüße 2.0 — self-hosted only, no CDNs (hard rule).
   Montserrat comes from css/fonts.css; the system stack stays as the
   fallback while the font swaps in. */

* {
    box-sizing: border-box;
}

:root {
    /* This is a light page and says so. Without the declaration an
       OS-dark visitor gets dark-variant inputs, caret and scrollbars on
       a hardcoded cream background. Whether it also reaches the native
       <audio> widget is browser-dependent — nothing here relies on it. */
    color-scheme: light;

    /* Same scale as the main site (config.css) — one project, one rhythm */
    --text-xs: 0.8125rem;
    --text-sm: 0.875rem;
    --text-base: 1rem;
    --text-md: 1.125rem;
    --text-lg: 1.25rem;
    --text-h1: clamp(1.5rem, 1.045rem + 2.27vw, 2.5rem);

    --space-xs: 0.5rem;
    --space-sm: 0.75rem;
    --space-md: 1.5rem;
    --space-lg: 2.5rem;

    /* The colour, radius and interaction tokens below are gruss-local BY
       INTENT — do not mirror them into config.css. The type and space
       scales are mirrored because rhythm would visibly diverge if it
       drifted; colour is not in that position. The main site has no
       buttons, no form controls and no interactive states, so a copy
       there would be declarations nothing references — and unused
       declarations drift and then lie. Promote them the day the main
       site grows its first button. */

    /* The green of the logo (home/staunwaerts-logo.svg) and of the printed
       QR codes (export_qr_labels.QR_COLOR). Until now it was ink on paper
       and absent from every stylesheet — the screen someone scanned onto
       did not match the card in their hand. */
    --brand: #0a5d4b;         /* 7.3:1 on --page, 7.8:1 behind white text */
    --brand-hover: #084a3c;   /* white label 10.2:1 */
    --brand-active: #063a2f;  /* white label 12.7:1 */

    --page: #faf7f2;
    --surface: #fff;
    --surface-sunken: #efe9e0;
    --text: #2b2b2b;          /* 13.25:1 on --page, 14.2:1 on --surface */
    /* 4.99:1 against the page background — passes AA with no headroom.
       Do not lighten, and do not drop below 400 weight. */
    --text-muted: #6b6b6b;
    --line: #d8d0c4;          /* decorative hairline only — never a state boundary */
    --line-strong: #8f877b;   /* boundary of a real control — 3.32:1, meets 1.4.11 */
    --error: #a33;            /* 6.08:1 */
    --focus: #0a5d4b;

    --radius-sm: 0.25rem;
    --radius: 0.5rem;

    /* 44px, Apple HIG floor. The station is an iPad handled in a hurry
       for four hours, sometimes with acrylic paint on the fingers (O3). */
    --tap: 2.75rem;

    --transition: 120ms ease-out;
}

body.gruss {
    margin: 0;
    font-family: "Montserrat", system-ui, -apple-system, "Segoe UI", sans-serif;
    color: var(--text);
    background: var(--page);
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

.branding {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-md);
    padding: 1rem 1.25rem;
}

/* E9 trap: the wordmark SVG carries two hardcoded fill:#fff counter paths,
   knockouts that assume a light backdrop. NEVER put --brand, or any dark
   fill, behind .logo-staunwaerts or .logo-museum — the counters would turn
   into white blobs inside the letterforms. The green lives inside <main>. */
.logo {
    font-weight: 700;
    letter-spacing: 0.02em;
}

.logo-staunwaerts {
    max-width: 8rem;
}

/* E9: player page only. Set below the wordmark so the pair reads as
   "Staunwärts, at the museum" instead of two logos competing. */
.logo-museum {
    font-size: var(--text-sm);
    font-weight: 600;
    letter-spacing: 0.01em;
    text-align: right;
}

main {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.5rem;
}

.card-message,
.recording-shell {
    max-width: 28rem;
    width: 100%;
    text-align: center;
}

.card-message h1 {
    font-size: var(--text-h1);
    line-height: 1.2;
    letter-spacing: -0.02em;
    /* The card pages are read on a phone held one-handed; balanced line
       lengths matter more here than anywhere else on the site. */
    text-wrap: balance;
}

audio {
    width: 100%;
    margin: 1rem 0;
}

.hoerbar-bis,
.hint {
    font-size: var(--text-sm);
    color: var(--text-muted);
    letter-spacing: 0.01em;
}

/* Something arrived here, and that is carried by a frame rather than by
   decoration: the message side of a field postcard — white field, one
   green rule along the top like a stamp band, room to breathe.
   DOMAIN.md:16-17 — Dix wrote words, not pictures of himself.
   leer.html and expired.html deliberately do NOT get this. Their
   plainness is the point: a "success look" here would implicitly invent
   the failure look E1 forbids. */
.greeting {
    background: var(--surface);
    border: 1px solid var(--line);
    border-top: 3px solid var(--brand);
    border-radius: var(--radius-sm);
    /* Fluid, not stepped. At a 320px viewport this still leaves the native
       widget ~246px — above the width where Chrome starts dropping its
       timeline to save room. */
    padding: var(--space-lg) clamp(var(--space-sm), 4vw, var(--space-md)) var(--space-md);
}

.greeting h1 {
    /* .card-message h1 already sets size, leading, tracking and balance;
       only the rhythm inside the frame belongs here. */
    margin: 0 0 var(--space-md);
    font-weight: 600;
}

.greeting audio {
    display: block;
    /* No height, ever. Chrome renders the widget at 54px, Safari and
       Firefox at something else, and none of it is derivable — a fixed
       height either clips the controls or leaves a gap. The padding on
       .greeting does the framing instead. */
    margin: 0 0 var(--space-lg);
}

.greeting .hoerbar-bis {
    /* A postmark, not a warning: quiet, small, at the bottom edge.
       #6b6b6b on white is 5.33:1 — slightly more headroom than the 4.99:1
       it has out on the cream page. */
    margin: 0;
}

/* Base reset only. A button someone forgets to class must not fall back
   to a 13px OS control — but the look now lives in .btn, so this rule
   can never fight it. */
button {
    font: inherit;
    color: inherit;
    cursor: pointer;
}

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: var(--tap);
    padding: 0 1.25rem;
    margin: 0.25rem;
    border: 1px solid transparent;
    border-radius: var(--radius);
    font-weight: 500;
    text-align: center;
    /* Colour only. No transform, no padding shift, no weight animation:
       a button that changes geometry under a finger is worse than no
       effect at all (typography skill, references/montserrat.md). */
    transition:
        background-color var(--transition),
        border-color var(--transition),
        color var(--transition);
    -webkit-tap-highlight-color: transparent;
}

/* Recording station only: above the 44px floor. Four hours, ~100
   participants, one operator working fast against a queue. */
.btn--lg {
    min-height: 3.5rem;
    padding: 0 var(--space-md);
    font-size: var(--text-md);
}

.btn--primary {
    background: var(--brand);
    border-color: var(--brand);
    color: var(--surface);
}

.btn--secondary {
    background: var(--surface);
    border-color: var(--text);
    color: var(--text);
}

.btn--ghost {
    background: transparent;
    border-color: var(--line-strong);
    color: var(--text);
}

/* Hover is a desktop nicety — the iPad never fires it. :active is the
   state that actually matters at the station. */
@media (hover: hover) {
    .btn--primary:hover:not(:disabled) {
        background: var(--brand-hover);
        border-color: var(--brand-hover);
    }

    .btn--secondary:hover:not(:disabled),
    .btn--ghost:hover:not(:disabled) {
        background: var(--surface-sunken);
        border-color: var(--text);
    }
}

.btn--primary:active:not(:disabled) {
    background: var(--brand-active);
    border-color: var(--brand-active);
}

.btn--secondary:active:not(:disabled),
.btn--ghost:active:not(:disabled) {
    background: var(--surface-sunken);
}

/* "Noch nicht", not "kaputt". #record-stop and #record-save are disabled
   at first paint, so this is the default look of a third of the recording
   UI — the old opacity: 0.4 put the label at ~2.7:1 and read as breakage.
   Same box, same position, same weight: enabling is a fill arriving, not
   a shape appearing, so nothing under the finger moves. */
.btn:disabled {
    background: transparent;
    border-color: var(--line-strong);
    color: var(--text-muted);
    cursor: default;
}

/* One ring for the whole host. Green is 7.3:1 on the cream page and
   7.8:1 on white; the offset leaves a gap of page colour so the ring
   stays visible on top of a filled green button too. */
:focus-visible {
    outline: 3px solid var(--focus);
    outline-offset: 3px;
}

/* Recording shell — vertical rhythm, no template changes needed. */

.card-token {
    font-size: var(--text-sm);
    color: var(--text-muted);
    letter-spacing: 0.01em;
    margin-bottom: var(--space-md);
}

.card-token code {
    font-size: 0.95em;
    background: var(--surface-sunken);
    padding: 0.1em 0.35em;
    border-radius: var(--radius-sm);
    color: var(--text);
}

.hint {
    margin-bottom: var(--space-md);
}

#record-preview {
    margin: var(--space-md) 0 0;
}

#upload-form {
    margin-top: var(--space-md);
}

#record-timer {
    display: inline-block;
    /* Wide enough for the recording dot below plus "0:00", so the row
       does not jump when a take starts. */
    min-width: 4.5rem;
    font-variant-numeric: tabular-nums;
}

/* recorder.js only ever toggles `disabled` on these siblings, so the
   recording state is expressible in CSS and the JS stays untouched.
   start disabled + stop enabled == a take is running. */

/* During a take, Stopp is the only thing to press — so the green moves
   to it rather than sitting on a button that cannot be used. */
#record-start:disabled ~ #record-stop:enabled {
    background: var(--brand);
    border-color: var(--brand);
    color: var(--surface);
}

/* Confirms to the operator that it really is capturing — the one piece
   of feedback the station had no way to show. */
#record-start:disabled ~ #record-timer::before {
    content: "";
    display: inline-block;
    width: 0.6em;
    height: 0.6em;
    margin-right: 0.4em;
    border-radius: 50%;
    background: var(--error);
    animation: rec-pulse 1.4s ease-in-out infinite;
}

@keyframes rec-pulse {
    50% { opacity: 0.25; }
}

/* recorder.js swaps this label to "Speichert…" mid-upload (and back on
   failure). "Speichert…" measures 100.4px at --text-md weight 500 against
   the shipped font (scripts/measure_text.py); + 2x --space-md padding =
   148.4px. Reserved here so the button cannot resize under the finger. */
#record-save {
    min-width: 9.5rem;
}

.error {
    color: var(--error);
}

/* Login — staff-only, so a wrong password genuinely is an error and red
   belongs here. E1's "no error look" governs the public token pages. */

.auth-form {
    /* A form reads as a column, not as a poster: left-aligned labels
       inside the centred .card-message. */
    max-width: 20rem;
    margin-inline: auto;
    text-align: left;
}

/* as_p wraps each field in a <p> — that is the row. */
.auth-form p {
    margin: 0 0 var(--space-md);
}

.auth-form label {
    display: block;
    margin-bottom: var(--space-xs);
    font-size: var(--text-sm);
    font-weight: 500;
    letter-spacing: 0.01em;
}

.auth-form input[type="text"],
.auth-form input[type="password"] {
    /* font: inherit keeps these at 1rem = 16px. Below 16px iOS Safari
       zooms the page on focus, which leaves the Kundin looking at a
       magnified half-field on the login screen. */
    font: inherit;
    width: 100%;
    min-height: var(--tap);
    padding: 0 var(--space-sm);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--text);
    transition: border-color var(--transition);
}

.auth-form input:focus-visible {
    /* :focus-visible above draws the ring; this darkens the field edge
       underneath it. */
    border-color: var(--brand);
}

.auth-form .errorlist {
    margin: 0 0 var(--space-xs);
    padding: 0;
    list-style: none;
    font-size: var(--text-sm);
    letter-spacing: 0.01em;
    color: var(--error);
}

.auth-form .helptext {
    display: block;
    margin-top: var(--space-xs);
    font-size: var(--text-xs);
    letter-spacing: 0.01em;
    color: var(--text-muted);
}

/* Impressum / Datenschutz — reachable from every page, including the
   token pages the QR codes lead to. */

.site-footer {
    display: flex;
    gap: 1.25rem;
    justify-content: center;
    flex-wrap: wrap;
    padding: 1.25rem;
    font-size: var(--text-xs);
    letter-spacing: 0.01em;
}

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

/* Long prose must start at the top; main centers its content vertically
   for the short card pages. */
body.legal-page main {
    align-items: flex-start;
}

.legal {
    max-width: 42rem;
    width: 100%;
    line-height: 1.6;
    hyphens: auto;
}

.legal p {
    text-wrap: pretty;
}

.legal h1 {
    font-size: var(--text-h1);
    font-weight: 700;
    line-height: 1.15;
    letter-spacing: -0.02em;
    text-wrap: balance;
    margin-bottom: var(--space-md);
}

/* Proximity: more space above a heading than below it */
.legal h2 {
    font-size: var(--text-md);
    line-height: 1.25;
    letter-spacing: -0.01em;
    text-wrap: balance;
    margin-top: var(--space-lg);
    margin-bottom: var(--space-xs);
}

.legal h3 {
    font-size: var(--text-base);
    line-height: 1.3;
    text-wrap: balance;
    margin-top: var(--space-md);
    margin-bottom: var(--space-xs);
}

.legal code {
    font-size: 0.9em;
    background: var(--surface-sunken);
    padding: 0.1em 0.35em;
    border-radius: var(--radius-sm);
}

/* The only @media beyond the hover query above. Kills the button colour
   fades and the recording pulse for anyone who asked for less motion. */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        transition-duration: 0.01ms !important;
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
    }
}
