/*
 * STUTE — designrichting uit sectie 7 van de technische specificatie.
 *
 * Bijna zuiver zwart-wit: #1A1919 op wit, geen accentkleur. Productfoto's en
 * winkellogo's zijn de enige kleurbron op de site en moeten dat blijven.
 * Rechte hoeken, dunne lijnen en randen in plaats van kaarten met schaduw,
 * geen gradients. Karakter komt uit typografie: brede letter-spacing op koppen en
 * een groot schaalverschil tussen koppen en labels.
 *
 * Bootstrap staat hieronder als basis; dit bestand trekt zijn variabelen naar
 * die richting toe in plaats van zijn standaardlook te ondergaan.
 */

/*
 * Jost Medium, zelf gehost onder de Open Font License (zie fonts/OFL.txt).
 * Dient als terugval voor Avenir op systemen die dat niet meeleveren.
 *
 * Let op het gewicht: dit ís het Medium-bestand (500), maar het staat hier als
 * font-weight 600 omdat de koppen dat gewicht vragen. Zonder die aanpassing
 * ziet de browser een verschil tussen gevraagd en beschikbaar gewicht en maakt
 * hij er zelf een namaakvette letter van. Optisch komt Jost Medium overeen met
 * Avenir op 600, dus dit zet de twee families op dezelfde zwaarte.
 *
 * De twee bestanden zijn de latin- en latin-ext-subsets van Google Fonts. Op
 * macOS wordt geen van beide gedownload: Avenir staat vooraan in de stack en
 * een browser haalt een webfont pas op als hij hem echt nodig heeft.
 */
@font-face {
    font-family: 'Jost';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url('/assets/fonts/jost-500-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
        U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191,
        U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Jost';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url('/assets/fonts/jost-500-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
        U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
        U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
    --stute-ink: #1A1919;
    --stute-paper: #FFFFFF;
    --stute-rule: rgba(26, 25, 25, 0.16);
    --stute-rule-strong: rgba(26, 25, 25, 0.42);
    --stute-muted: rgba(26, 25, 25, 0.56);

    /* Rood draagt twee dingen, en verder niets: hier ontbreken nog gegevens
       (het bolletje in het menu, de lijn naast het blok dat het betreft) en
       hier ging iets mis (.notice-error). Allebei zijn het signalen die in
       zwart-wit tussen de labels verdwijnen, en allebei te belangrijk om te
       missen. Eén regel om terug te draaien: zet hem op var(--stute-ink).

       Groen komt maar op één plek voor: de bevestiging dat er iets bewaard is
       (.notice-ok). Nergens anders — een groen vinkje naast elk afgewerkt
       onderdeel maakt van de kleur weer decoratie. */
    --stute-alert: #C2261B;
    --stute-alert-tint: #FBF0EF;
    --stute-ok: #1B6B3F;
    --stute-ok-tint: #EFF5F1;

    --stute-sans: "Helvetica Neue", Helvetica, Arial, system-ui, -apple-system, sans-serif;

    /* Koppen: Avenir waar het toestel hem heeft (macOS, iOS), anders het zelf
       gehoste Jost. Avenir staat vooraan, Avenir Next erachter voor toestellen
       met alleen die familie. */
    --stute-display: Avenir, "Avenir Next", Jost, "Segoe UI", system-ui, -apple-system, sans-serif;

    /* Bootstrap omzetten naar de STUTE-richting. */
    --bs-body-bg: var(--stute-paper);
    --bs-body-color: var(--stute-ink);
    --bs-body-font-family: var(--stute-sans);
    --bs-border-color: var(--stute-rule);
    --bs-link-color: var(--stute-ink);
    --bs-link-hover-color: var(--stute-ink);
    --bs-emphasis-color: var(--stute-ink);

    /* Rechte hoeken, overal. */
    --bs-border-radius: 0;
    --bs-border-radius-sm: 0;
    --bs-border-radius-lg: 0;
    --bs-border-radius-xl: 0;
    --bs-border-radius-xxl: 0;
    --bs-border-radius-2xl: 0;
    --bs-border-radius-pill: 0;
}

body {
    font-family: var(--stute-sans);
    color: var(--stute-ink);
    background: var(--stute-paper);
    -webkit-font-smoothing: antialiased;
}

/* Wat `hidden` draagt, hoort weg te zijn — ook wanneer een klasse er een eigen
   `display` op zet. Dat verbergen komt namelijk alleen uit de standaardstijl van
   de browser, en elke klasse hier wint daarvan: een knop met .btn-ink (display:
   block) of een balk met display:flex blijft dus staan, ook al zegt de html van
   niet. Dat raakt alles wat pas met een script bruikbaar wordt — de kopieerknop
   bij het deelmateriaal, de opmaakbalk, het wachtteken bij het ophalen — en dat
   zijn precies de dingen die zonder script niets doen.

   Met !important, want anders is elke klasse met een `display` sterker; dit is
   de enige plaats in dit bestand waar dat nodig is. */
[hidden] {
    display: none !important;
}

/* Geen schaduwen: Bootstrap zet ze via deze variabelen, dus die zijn de plek
   om ze uit te zetten — niet een * { box-shadow: none }, want dat haalt ook de
   focus-indicatie weg bij alles wat hieronder niet expliciet genoemd staat. */
:root {
    --bs-box-shadow: none;
    --bs-box-shadow-sm: none;
    --bs-box-shadow-lg: none;
    --bs-box-shadow-inset: none;
}

.form-control:focus,
.btn:focus,
.btn:active {
    box-shadow: none;
}

/* Eén zichtbare focus-indicatie voor toetsenbordgebruik, in inkt in plaats van
   de blauwe gloed van Bootstrap. */
:focus-visible {
    outline: 2px solid var(--stute-ink);
    outline-offset: 2px;
}

/* --- typografie --------------------------------------------------------- */

/* De viewBox van logo.svg sluit strak om de letters, dus deze breedte is de
   werkelijke breedte van het wordmark — er zit geen onzichtbare marge meer in
   die de positie beïnvloedt. */
.wordmark {
    display: block;
    width: 113px;
    height: auto;
}

.eyebrow {
    font-size: 0.6875rem;
    line-height: 1.4;
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color: var(--stute-muted);
    margin: 0;
}

.display-title {
    font-family: var(--stute-display);
    font-size: clamp(2rem, 7vw, 3rem);
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    line-height: 1.05;
    margin: 0;
}

/* Een productnaam is data en geen schermnaam: hij houdt zijn eigen schrijfwijze.
   In kapitalen met brede letterafstand wordt "Jollein - Baby Slaapzak Met
   Afritsbare Mouw 60cm" vier regels die niemand leest. */
.display-title.is-name {
    font-size: clamp(1.5rem, 4vw, 2.125rem);
    letter-spacing: 0.02em;
    text-transform: none;
    line-height: 1.15;
}

.section-title {
    font-family: var(--stute-display);
    font-size: 0.8125rem;
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    margin: 0;
}

.lede {
    font-size: 0.9375rem;
    line-height: 1.65;
    color: var(--stute-muted);
    margin: 0;
}

.rule {
    border: 0;
    border-top: 1px solid var(--stute-rule);
    margin: 0;
    opacity: 1;
}

/* --- formulieren -------------------------------------------------------- */

.form-label {
    font-size: 0.6875rem;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--stute-muted);
    margin-bottom: 0.5rem;
}

/* Het label zelf staat gedempt; het sterretje staat in inkt, zodat de
   verplichte velden er in één blik uit springen zonder een tweede kleur. */
.required-mark {
    margin-left: 0.35em;
    color: var(--stute-ink);
}

.form-control {
    border: 1px solid var(--stute-rule-strong);
    border-radius: 0;
    padding: 0.8rem 0.9rem;
    font-size: 1rem;
    color: var(--stute-ink);
    background-color: transparent;
}

/* Een veld op slot krijgt van Bootstrap een blauwgrijze vulling, en dat is een
   kleur die hier nergens voorkomt. Gedempt in plaats van gevuld: dezelfde
   achtergrond als de rest, een lichtere rand en lichtere tekst. De waarde blijft
   leesbaar — ze staat er niet voor niets, ze telt alleen even niet mee. */
.form-control:disabled {
    background-color: transparent;
    border-color: var(--stute-rule);
    color: var(--stute-muted);
}

.form-control:focus {
    border-color: var(--stute-ink);
    background-color: transparent;
    color: var(--stute-ink);
}

.btn-ink {
    display: block;
    width: 100%;
    border: 1px solid var(--stute-ink);
    border-radius: 0;
    background: var(--stute-ink);
    color: var(--stute-paper);
    font-size: 0.75rem;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    padding: 0.95rem 1.5rem;
    cursor: pointer;
    transition: background-color 0.12s linear, color 0.12s linear;
}

.btn-ink:hover {
    background: var(--stute-paper);
    color: var(--stute-ink);
}

/* Dezelfde knop als <a>, bv. wanneer er niets te versturen valt maar wel iets
   te doen. Zonder dit staat de onderlijning van een link in de knop. */
a.btn-ink {
    text-align: center;
    text-decoration: none;
}

/* Omkering: dezelfde knop, andere richting. Geen tweede kleur nodig. */
.btn-outline-ink {
    background: transparent;
    color: var(--stute-ink);
}

.btn-outline-ink:hover {
    background: var(--stute-ink);
    color: var(--stute-paper);
}

/* Knop die zich naar zijn inhoud voegt in plaats van de volle breedte te nemen. */
.btn-compact {
    display: inline-block;
    width: auto;
    padding: 0.7rem 1.25rem;
}

.link-plain {
    color: var(--stute-ink);
    text-decoration: none;
    border-bottom: 1px solid var(--stute-rule-strong);
    padding-bottom: 1px;
}

/* Leest als een link, is een knop: acties die naast de hoofdknop staan hebben
   geen nadruk nodig, maar er hangt wel een POST met een token aan. Ze mogen
   dus geen link worden. */
.btn-plain {
    border: 0;
    border-bottom: 1px solid var(--stute-rule-strong);
    background: none;
    padding: 0 0 1px;
    font: inherit;
    color: var(--stute-ink);
    cursor: pointer;
}

.btn-plain:hover {
    border-bottom-color: var(--stute-ink);
}

.link-plain:hover {
    border-bottom-color: var(--stute-ink);
}

/* --- meldingen ---------------------------------------------------------- */

.notice {
    border: 1px solid var(--stute-ink);
    padding: 0.8rem 1rem;
    font-size: 0.875rem;
    line-height: 1.5;
}

.notice-label {
    display: block;
    font-size: 0.625rem;
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color: var(--stute-muted);
    margin-bottom: 0.25rem;
}

/* Meer dan één vaststelling in dezelfde melding: elk op zijn eigen regel, want
   twee zinnen achter elkaar lezen als één. */
.notice-line {
    display: block;
}

/* Twee meldingen wijken af van het zwart-wit, omdat ze allebei het antwoord
   zijn op iets wat de gebruiker net gedaan heeft: het is misgegaan, of het is
   bewaard. Een kader alleen leest als de rest van de pagina — wie net op
   "bewaren" klikte en weer bovenaan uitkomt, moet vanaf de rand van het scherm
   zien welk van de twee het is.

   De kleur doet dat werk niet alleen: het teken links (driehoek of vinkje) zegt
   hetzelfde, ook voor wie rood en groen niet uit elkaar houdt. Het teken blijft
   daarom zwart — het is de vórm die het verschil maakt.

   Alle andere meldingen (uitleg, een stand van zaken, wat nog moet gebeuren)
   blijven zwart op wit: die vragen niets en zijn geen antwoord. */
.notice-error,
.notice-ok {
    position: relative;
    padding-left: 2.875rem;
}

.notice-error::before,
.notice-ok::before {
    content: "";
    position: absolute;
    left: 1rem;
    /* Uitgelijnd op de eerste regel, niet gecentreerd in het kader: bij een
       melding van vier regels hangt een gecentreerd teken nergens naast. */
    top: 0.8rem;
    width: 1.125rem;
    height: 1.125rem;
    background-repeat: no-repeat;
    background-position: center;
    background-size: contain;
}

.notice-error {
    border-color: var(--stute-alert);
    background: var(--stute-alert-tint);
}

/* Het woord "fout" staat er al; het krijgt de kleur zodat kop en kader
   hetzelfde zeggen. De tekst eronder blijft zwart en dus leesbaar. */
.notice-error .notice-label {
    color: var(--stute-alert);
}

.notice-error::before {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M10 2.2 19 17.8H1z' fill='none' stroke='%231A1919' stroke-width='1.6' stroke-linejoin='round'/%3E%3Cpath d='M10 7.6v4.3' stroke='%231A1919' stroke-width='1.6' stroke-linecap='round'/%3E%3Cpath d='M10 15.1h0' stroke='%231A1919' stroke-width='1.9' stroke-linecap='round'/%3E%3C/svg%3E");
}

.notice-ok {
    border-color: var(--stute-ok);
    background: var(--stute-ok-tint);
}

.notice-ok::before {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M2.8 10.6 7.8 15.4 17.2 4.8' fill='none' stroke='%231A1919' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/* --- aanmeldscherm ------------------------------------------------------ */

.auth-shell {
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 2.5rem 1.25rem;
}

.auth-panel {
    width: 100%;
    max-width: 23rem;
}

.auth-header {
    margin-bottom: 2.75rem;
}

.auth-foot {
    padding-top: 1.5rem;
    border-top: 1px solid var(--stute-rule);
}

/* De taalkiezer, in de voet van de publieke zone en onder het aanmeldscherm.
   Dicht staat alleen de huidige taal er, in dezelfde kleine kapitalen als de
   rest van de labels; open staan de talen in een raster dat met hun aantal
   meegroeit. Geen pijltje van de browser: het streepje eronder zegt al dat het
   iets doet, net als bij een link. */
.lang-picker {
    font-size: 0.6875rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
}

.lang-picker summary {
    display: inline-block;
    list-style: none;
    cursor: pointer;
    color: var(--stute-ink);
    border-bottom: 1px solid var(--stute-rule-strong);
    padding-bottom: 1px;
}

.lang-picker summary::-webkit-details-marker {
    display: none;
}

.lang-picker summary::after {
    content: "+";
    margin-left: 0.6em;
}

.lang-picker[open] summary::after {
    content: "\2212";
}

.lang-picker ul {
    list-style: none;
    padding: 0;
    margin: 1.25rem 0 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
    gap: 0.75rem 1.5rem;
}

.lang-picker a {
    color: var(--stute-muted);
    text-decoration: none;
}

.lang-picker a:hover,
.lang-picker a[aria-current="true"] {
    color: var(--stute-ink);
}

/* --- beheerzone --------------------------------------------------------- */

.admin-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1.5rem;
    padding: 1.5rem 0;
    border-bottom: 1px solid var(--stute-rule);
    flex-wrap: wrap;
}

.admin-shell {
    max-width: 72rem;
    margin: 0 auto;
    padding: 0 1.5rem 4rem;
}

/* Menu van de beheerzone. Zelfde kleine kapitalen als de rest van de labels,
   op één dunne lijn. Het actieve item krijgt die lijn in het zwart onder zich
   in plaats van een accentkleur of een vulling. */
.admin-nav {
    display: flex;
    flex-wrap: wrap;
    /* Smalle rijafstand: op een klein scherm valt het menu over twee regels en
       moet dat één blok blijven, geen twee losse balken. */
    gap: 0.9rem 1.75rem;
    padding: 0.9rem 0;
    border-bottom: 1px solid var(--stute-rule);
}

.admin-nav a {
    font-size: 0.6875rem;
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color: var(--stute-muted);
    text-decoration: none;
    padding-bottom: 0.15rem;
    border-bottom: 1px solid transparent;
}

.admin-nav a:hover {
    color: var(--stute-ink);
}

.admin-nav a[aria-current="page"] {
    color: var(--stute-ink);
    border-bottom-color: var(--stute-ink);
}

/* Het bolletje staat naast het label, niet erboven of erin: op deze kleine
   kapitalen valt het zo op zonder de regelhoogte te veranderen. Geen cijfer —
   hoevéél er ontbreekt, staat op de pagina zelf.

   Staat naast een menu-item én naast de kop van de melding op de pagina waar
   het naartoe wijst, zodat wie het bolletje volgt hetzelfde teken terugziet. */
.alert-dot {
    display: inline-block;
    width: 0.375rem;
    height: 0.375rem;
    margin-left: 0.4rem;
    vertical-align: 0.35em;
    border-radius: 50%;
    background: var(--stute-alert);
}

/* Tijdens impersonatie: omgekeerde balk, onmogelijk over het hoofd te zien
   zonder een waarschuwingskleur te gebruiken. */
.impersonation-bar {
    background: var(--stute-ink);
    color: var(--stute-paper);
    padding: 0.75rem 1.5rem;
    font-size: 0.75rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
}

.impersonation-bar a,
.impersonation-bar button {
    color: var(--stute-paper);
    background: none;
    border: 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.55);
    padding: 0;
    font: inherit;
    cursor: pointer;
}

.inline-form {
    display: inline;
}

/* --- publieke zone en formulieren ---------------------------------------- */

.public-shell {
    max-width: 52rem;
    margin: 0 auto;
    padding: 0 1.5rem 5rem;
}

.public-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1.5rem;
    padding: 1.5rem 0 2.5rem;
    flex-wrap: wrap;
}

.public-content {
    max-width: 34rem;
}

/* Het wordmerk en de taalkeuze onderaan de pagina van een winkel of initiatief:
   STUTE is daar de gastheer en niet de hoofdrol, en een taalkiezer die bovenaan
   om aandacht vraagt, staat vóór het beeld waarvoor de bezoeker kwam. */
.public-foot {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 1.5rem;
    margin-top: 5rem;
    padding-top: 2rem;
    border-top: 1px solid var(--stute-rule);
}

.public-foot .wordmark {
    opacity: 0.75;
}

/* De kiezer blijft staan waar hij staat, ook open: de lijst klapt eroverheen in
   plaats van de voet te herschikken. Anders verspringt de knop naar links op het
   moment dat je hem aanklikt, en zoek je hem daarna opnieuw.

   Naar boven, want dit is de voet: eronder is er niets om in open te klappen.

   En met margin-left: auto blijft hij rechts staan, ook wanneer de voet in twee
   regels breekt. Zonder dat schuift hij op een smal scherm naar de linkerkant —
   justify-content: space-between zet een regel met één element aan het begin —
   en dan klapt de lijst, die met haar rechterrand aan de knop hangt, naar links
   het scherm uit. Op een breed scherm verandert de auto-marge niets: ze slorpt
   dezelfde ruimte op die space-between anders verdeelt. */
.public-foot .lang-picker {
    position: relative;
    margin-left: auto;
}

.public-foot .lang-picker ul {
    position: absolute;
    right: 0;
    bottom: 100%;
    margin: 0 0 0.75rem;
    width: max-content;
    max-width: min(24rem, calc(100vw - 3rem));
    background: var(--stute-paper);
    border: 1px solid var(--stute-rule-strong);
    padding: 1.25rem;
    z-index: 2;
}

.public-foot-links {
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem;
}

.public-foot-link {
    font-size: 0.6875rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--stute-muted);
    text-decoration: none;
}

.public-foot-link:hover {
    color: var(--stute-ink);
}

/* Aanmelden als link, registreren als knop: allebei in de balk, maar wie nieuw
   is, moet de weg niet zoeken. */
.public-nav {
    display: flex;
    align-items: center;
    gap: 1.5rem;
    font-size: 0.75rem;
    letter-spacing: 0.2em;
    text-transform: uppercase;
}

/* --- voorpagina ---------------------------------------------------------- */

/* De kop is een zin en geen schermnaam: in kapitalen met brede letterafstand
   wordt hij drie regels die niemand leest. Groot, in de koppenletter, en de
   schaal draagt de rest. */
.home-hero {
    padding: 2.5rem 0 1rem;
}

.home-title {
    font-family: var(--stute-display);
    font-size: clamp(2.5rem, 8vw, 4.5rem);
    font-weight: 600;
    letter-spacing: 0.01em;
    line-height: 1.05;
    max-width: 12em;
    margin: 0;
}

.home-claim {
    font-size: 0.75rem;
    letter-spacing: 0.24em;
    text-transform: uppercase;
    margin: 2rem 0 0;
    max-width: 34rem;
    line-height: 1.8;
}

.home-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-top: 2.75rem;
}

.home-login {
    margin-top: 1.25rem;
}

.home-section {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 1rem;
    margin-top: 5rem;
}

.home-section .rule {
    align-self: stretch;
    margin-bottom: 0.5rem;
}

.home-section .lede {
    max-width: 34rem;
}

/* Drie stappen naast elkaar, elk met een groot cijfer: het schaalverschil
   tussen cijfer en zin is hier het beeld. Op een smal scherm onder elkaar. */
.home-steps {
    list-style: none;
    padding: 0;
    margin: 1rem 0 0;
    display: grid;
    gap: 2rem;
    align-self: stretch;
}

.home-steps li {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.home-steps p {
    margin: 0;
    font-size: 0.9375rem;
    line-height: 1.6;
    max-width: 18rem;
}

.home-step-number {
    font-family: var(--stute-display);
    font-size: 2.75rem;
    font-weight: 600;
    line-height: 1;
}

.home-audiences {
    display: grid;
    gap: 0 3rem;
}

/* Naast elkaar staan de twee knoppen op één hoogte, hoe lang de tekst erboven
   ook is. */
.home-audiences .btn-ink {
    margin-top: auto;
}

/* De link naar de noden hoort bij de knop en niet bij de voorbeelden erboven:
   samen staan ze onderaan het blok, en dan staan ze in allebei de kolommen op
   dezelfde hoogte. Zonder die link (een taal zonder content/needs.<taal>.php)
   houdt de knop zijn eigen marge en verandert er niets. */
.home-audiences .home-needs {
    margin-top: auto;
}

.home-audiences .home-needs + .btn-ink {
    margin-top: 0;
}

/* De voorbeelden als rij labels met een dunne rand: ze lezen als een opsomming
   en niet als knoppen, want er valt niets op te klikken. */
.home-tags {
    list-style: none;
    padding: 0;
    margin: 0.5rem 0 1rem;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.home-tags li {
    font-size: 0.6875rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    border: 1px solid var(--stute-rule);
    padding: 0.4rem 0.65rem;
}

/* De logo's zelf, zonder naam en zonder kader: het is een strook merken en
   geen opsomming. Ze staan even hoog, zodat een breed en een smal logo niet
   met elkaar gaan concurreren. */
.home-logos {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1.5rem 2.5rem;
}

.home-logo img {
    display: block;
    height: 2.25rem;
    width: auto;
    max-width: 9rem;
    object-fit: contain;
}

/* De verwijzing naar /shops onderaan de voorpagina. Lichter dan een blok met
   een kop: een label, een rij logo's en een link. */
.home-partners {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 1.25rem;
    margin-top: 5rem;
}

.home-partners .rule {
    align-self: stretch;
}

.home-partners p {
    margin: 0;
}

/* --- de winkels die meedoen --------------------------------------------- */

/* Lopende tekst op een publieke pagina: groter en in inkt, anders dan .lede —
   dit is leesstof en geen bijschrift. De breedte is die van een leesregel, ook
   wanneer de pagina zelf breder staat voor haar raster. */
.page-text {
    font-size: 1rem;
    line-height: 1.75;
    max-width: 34rem;
    margin: 0;
}

.page-text > p + p {
    margin-top: 1.25rem;
}

/* Elke winkel een vak met zijn logo erin, even groot voor iedereen: winkels
   leveren logo's van alle formaten aan, en het raster moet rustig blijven. De
   naam eronder, want een logo alleen is niet te lezen door een zoekmachine of
   een schermlezer. */
.shop-tiles {
    align-self: stretch;
    margin-top: 1rem;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
    gap: 1.5rem;
}

.shop-tile {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    border: 1px solid var(--stute-rule);
    padding: 1.25rem;
    color: inherit;
    text-decoration: none;
}

.shop-tile:hover,
.shop-tile:focus-visible {
    border-color: var(--stute-ink);
}

.shop-tile-logo {
    display: block;
    width: 100%;
    height: 3.5rem;
    object-fit: contain;
    object-position: left center;
    margin-bottom: 0.75rem;
}

.shop-tile-logo.is-empty {
    /* Geen leeg kader: de ruimte volstaat om de namen te laten uitlijnen. */
    background: none;
}

.shop-tile-name {
    font-family: var(--stute-display);
    font-size: 1rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    line-height: 1.25;
}

.shop-tile-city {
    font-size: 0.8125rem;
    color: var(--stute-muted);
}

/* --- een lijst vinden --------------------------------------------------- */

/* Het zoekveld naar een lijst: breder dan de zoekbalk in de beheerzone, want
   hier is het de hoofdzaak van het blok en niet een hulpmiddel boven een tabel. */
.finder-form {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    align-self: stretch;
    max-width: 36rem;
}

.finder-field {
    flex: 1 1 16rem;
}

.finder-field[type="search"] {
    -webkit-appearance: none;
    appearance: none;
}

/* Een naam die zelf de link is: geen onderlijning, want de kop draagt hem al —
   het kaartje eromheen zegt genoeg. */
.link-bare {
    color: inherit;
    text-decoration: none;
}

.link-bare:hover {
    text-decoration: underline;
}

.home-give-lists {
    align-self: stretch;
    margin: 1rem 0 1.5rem;
    /* De kaartjes lenen hun opbouw van de pagina van een initiatief, waar de
       lijnen en de knop de merkkleur dragen. Hier is STUTE de gastheer: inkt. */
    --brand-line: var(--stute-ink);
    --brand-fill: var(--stute-ink);
    --brand-on-fill: var(--stute-paper);
}

.finder-grid {
    align-self: stretch;
    margin-top: 1rem;
}

/* De kaartjes lenen hun opbouw van de pagina van een initiatief, en daar komen
   lijnen en knop in de merkkleur. Hier is STUTE de gastheer, dus inkt. */
.list-finder {
    --brand-line: var(--stute-ink);
    --brand-fill: var(--stute-ink);
    --brand-on-fill: var(--stute-paper);
}

@media (min-width: 48rem) {
    .home-steps {
        grid-template-columns: repeat(3, 1fr);
    }

    .home-audiences {
        grid-template-columns: 1fr 1fr;
    }
}

/* Eén kolom met een vaste ritmische tussenruimte, in plaats van marges per veld. */
.form-column {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
}

.field-row {
    display: flex;
    gap: 1.5rem;
    flex-wrap: wrap;
}

.field-grow {
    flex: 1 1 14rem;
}

.field-narrow {
    flex: 0 1 8rem;
}

/* Iets ruimer dan .field-narrow, niet voor het bedrag zelf maar voor het label
   erboven: "Gratis vanaf (€) *" past bij 8rem net niet op één regel. Het veld
   houdt hoogstens een bedrag van een paar cijfers vast, dus breder mag het niet
   worden dan wat het label vraagt. */
.field-amount {
    flex: 0 1 10rem;
}

/* Twee velden naast elkaar horen met hun bovenrand gelijk te staan, ook wanneer
   het ene label over twee regels loopt en het andere niet — "Gratuit à partir de
   (€)" naast "Montant (€)". Zonder dit zakt het ene invoervak een regel lager
   dan het andere.

   Er is bewust geen slimmere oplossing: de invoervakken onderaan uitlijnen zou
   het scheeftrekken zodra één van de twee een foutmelding onder zich krijgt, en
   een subgrid vraagt om kolombreedtes die hier uit .field-grow en .field-narrow
   komen. Twee regels ruimte reserveren kost hoogstens wat witruimte boven een
   kort label, en witruimte is hier geen probleem. */
.field-row .form-label {
    /* Ruimte voor twee regels, zodat twee velden naast elkaar met hun bovenrand
       gelijk blijven staan wanneer het ene label wel over twee regels loopt en
       het andere niet — "Gratuit à partir de (€)" naast "Montant (€)".

       Block en geen flex, hoe verleidelijk dat ook is om de tekst onderaan die
       ruimte te zetten: in een flexbox wordt het sterretje van een verplicht
       veld een eigen item, en dat springt dan naar een regel op zichzelf in
       plaats van achter het laatste woord te blijven. De overgebleven ruimte
       staat dus onder het label; dat is bij een kort label wat extra witruimte
       boven het invoervak, en die is hier geen probleem.

       Bootstrap zet .form-label op inline-block, en dat moet weg: een
       inline-block wordt op de tekstbasislijn uitgelijnd, en die ligt bij twee
       regels elders dan bij één — goed voor een paar pixels verschil die de
       reservering weer ongedaan maken.

       De invoervakken onderaan uitlijnen was het alternatief, maar dat trekt
       scheef zodra één van de twee een foutmelding onder zich krijgt. */
    display: block;
    line-height: 1.4;
    min-height: 2.8em;   /* twee regels; em volgt de labelgrootte */
}


.field-error {
    margin: 0.5rem 0 0;
    font-size: 0.8125rem;
    /* Geen rood: de fout staat onder het veld waar hij bij hoort, en dat is
       genoeg aanwijzing. Kleur blijft voorbehouden aan productfoto's. */
    color: var(--stute-ink);
    border-left: 2px solid var(--stute-ink);
    padding-left: 0.6rem;
}

/* Staat er meer dan één zin — een melding met de bedragen die wél kunnen — dan
   loopt de lijn langs het hele blok en niet alleen langs de eerste regel. */
.field-error > p {
    margin: 0;
}

/* Bijzin onder een waarde of een veld: waarom iets niet aan te passen is, of
   wat er van een invoer verwacht wordt. Gedempt, want het is nooit het
   belangrijkste op de regel. */
/* De naam zoals de winkel hem levert, onder het veld waar een eigen naam mag
   komen. Inkt en geen grijs: het is de waarde die straks getoond wordt en geen
   toelichting erbij. */
.field-note.name-original {
    color: var(--stute-ink);
}

/* Het adres van de eigen pagina, onder de kop van het huisstijlscherm. Klein en
   dicht onder de kop: het zegt waar het scherm over gaat en hoort die kop niet
   te beconcurreren. */
.page-address {
    margin: 0.75rem 0 0;
    font-size: 0.875rem;
    overflow-wrap: anywhere;
}

/* Hetzelfde adres met de knoppen ernaast: bekijken en delen. Op de lijsten van
   een initiatief staat het onder elke naam, want daar is het adres het werk —
   een lijst die niemand kent, brengt niets op. Op een smal scherm zakken de
   knoppen eronder in plaats van het adres af te knippen. */
.address-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.75rem 1.5rem;
}

.address-row .page-address {
    margin: 0;
}

/* Het adres dat de ingetikte naam oplevert, onder het naamveld bij de
   inschrijving. Inkt en geen grijs, om dezelfde reden als hierboven: het is
   geen toelichting maar het adres zelf. Een lange naam mag afbreken in plaats
   van het formulier open te duwen. */
.field-note.slug-preview {
    color: var(--stute-ink);
    overflow-wrap: anywhere;
}

/* Een placeholder is een voorbeeld en geen ingevulde waarde. De standaardkleur
   van de browser staat te dicht bij gewone tekst, en dan lijkt een leeg veld
   ingevuld. */
.form-control::placeholder {
    color: var(--stute-muted);
    opacity: 0.6;
}

.field-note {
    margin: 0.35rem 0 0;
    font-size: 0.8125rem;
    line-height: 1.5;
    color: var(--stute-muted);
}

.checkbox-line {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    font-size: 0.9375rem;
    line-height: 1.5;
    cursor: pointer;
}

.checkbox-line input {
    margin-top: 0.25rem;
    accent-color: var(--stute-ink);
    width: 1rem;
    height: 1rem;
}

/* Bootstrap zet hier een blauwgrijs pijltje neer. Vervangen door hetzelfde
   pictogram in inkt: zonder pijltje is een keuzelijst niet van een tekstveld te
   onderscheiden, dus weglaten kan niet. */
select.form-control {
    appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%231A1919' stroke-width='1.4'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 0.9rem center;
    background-size: 10px 6px;
    padding-right: 2.4rem;
}

/* --- waar zit de nood ---------------------------------------------------- */

/* Eén tabel met twee ingangen. Ze ís er ook echt een: een soort initiatief,
   wat het vraagt, waar dat ligt en wanneer horen per rij bij elkaar, en het
   nut van de pagina zit in het vergelijken van die rijen. Scheiding met lijnen
   zoals overal — geen streping, geen kader om de tabel. */
/* Het standpunt: dezelfde markering als het actieve item in het menu — kleine
   kapitalen, de huidige in inkt met een lijn eronder. Geen knoppen: van
   standpunt wisselen is navigeren en geen handeling. Niet .order-filter
   hergebruikt, al ziet het er hetzelfde uit: dat is de filterbalk van de
   bestellingen van een winkel, en die twee horen niet aan elkaar vast te
   zitten omdat ze vandaag toevallig samenvallen. */
.needs-views {
    display: flex;
    flex-wrap: wrap;
    gap: 0.9rem 1.75rem;
}

.needs-views a {
    font-size: 0.6875rem;
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color: var(--stute-muted);
    text-decoration: none;
    padding-bottom: 0.15rem;
    border-bottom: 1px solid transparent;
}

.needs-views a:hover {
    color: var(--stute-ink);
}

.needs-views a[aria-current="page"] {
    color: var(--stute-ink);
    border-bottom-color: var(--stute-ink);
}

.needs-filters {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 1.75rem;
}

/* De filterknoppen lezen als de voorbeeldlabels op de voorpagina: kleine
   kapitalen met een dunne rand. Hier valt er wél op te klikken, dus de
   gekozene keert om naar inkt — het verschil is vorm en geen kleur, en wie
   niets gekozen heeft, ziet "Alles" omgekeerd staan. */
.needs-filter {
    font-size: 0.6875rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--stute-ink);
    text-decoration: none;
    border: 1px solid var(--stute-rule);
    padding: 0.4rem 0.65rem;
}

.needs-filter:hover {
    border-color: var(--stute-ink);
    color: var(--stute-ink);
}

.needs-filter.is-on {
    background: var(--stute-ink);
    border-color: var(--stute-ink);
    color: var(--stute-paper);
}

.needs-search {
    margin-top: 1.25rem;
}

/* Het aantal staat er alleen wanneer er iets weggefilterd is. Gedempt en klein:
   het is een vaststelling over de tabel, geen kop erboven. */
.needs-count {
    margin: 1.25rem 0 0;
}

.needs-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.9375rem;
    margin-top: 1.25rem;
}

.needs-table th,
.needs-table td {
    text-align: left;
    vertical-align: top;
    padding: 0.9rem 1.5rem 0.9rem 0;
    border-bottom: 1px solid var(--stute-rule);
}

.needs-table thead th {
    font-size: 0.6875rem;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--stute-muted);
    font-weight: 400;
    white-space: nowrap;
}

/* De soort initiatief is de rijkop: het is waar je met je oog langsgaat. */
.needs-cause {
    font-weight: 600;
    min-width: 11rem;
}

/* De groep waarin die soort valt, eronder: ze verklaart de filterknop bovenaan
   en is niet wat je leest. */
.needs-category {
    display: block;
    font-weight: 400;
    font-size: 0.8125rem;
    color: var(--stute-muted);
    margin-top: 0.15rem;
}

.needs-when {
    color: var(--stute-muted);
    min-width: 9rem;
}

/* De producten en de soorten winkel als labels: het zijn opsommingen binnen
   één cel, en zonder omkadering lopen ze in elkaar over. */
.needs-tag {
    display: inline-block;
    font-size: 0.8125rem;
    border: 1px solid var(--stute-rule);
    padding: 0.2rem 0.5rem;
    margin: 0 0.25rem 0.25rem 0;
}

/* Vanuit een winkel is één soort degene die aan staat: die keert om, zoals de
   filterknop waar hij bij hoort. Zo is in één blik te zien waarom deze rij er
   nog staat. */
.needs-tag.is-on {
    background: var(--stute-ink);
    border-color: var(--stute-ink);
    color: var(--stute-paper);
}

/* Onder de 700 px wordt elke rij een blokje met de kolomnaam boven elke cel:
   vier kolommen naast elkaar op een telefoon geeft vier onleesbare stroken, en
   horizontaal schuiven door een tabel die je juist wil overlopen, werkt niet. */
@media (max-width: 43.75rem) {
    .needs-table thead {
        /* Niet display:none — dan verdwijnen de kolomnamen ook voor een
           schermlezer, terwijl elke cel ze hieronder juist zichtbaar krijgt. */
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip: rect(0 0 0 0);
        white-space: nowrap;
    }

    .needs-table,
    .needs-table tbody,
    .needs-table tr,
    .needs-table th,
    .needs-table td {
        display: block;
        width: 100%;
    }

    .needs-table tr {
        border-bottom: 1px solid var(--stute-rule);
        padding: 0.75rem 0;
    }

    .needs-table th,
    .needs-table td {
        border: 0;
        padding: 0.35rem 0;
    }

    .needs-table td::before {
        content: attr(data-label);
        display: block;
        font-size: 0.6875rem;
        letter-spacing: 0.2em;
        text-transform: uppercase;
        color: var(--stute-muted);
        margin-bottom: 0.3rem;
    }
}

/* --- over ons ------------------------------------------------------------ */

/* De claim onder de titel staat in kleine kapitalen, zoals op de voorpagina:
   het is geen ondertitel die de kop uitlegt, maar de drie woorden waar de rest
   van de pagina op staat. */
.about-intro {
    margin-top: 2.5rem;
}

/* De projecten die aan STUTE voorafgingen. Geen bolletjes: zes regels met een
   naam voorop lezen als een opsomming zodra de namen zwaarder staan dan de
   zinnen, en een lijn ertussen houdt ze uit elkaar. */
.about-list {
    list-style: none;
    padding: 0;
    margin: 0.5rem 0 0;
    align-self: stretch;
    max-width: 44rem;
    font-size: 0.9375rem;
    line-height: 1.65;
}

.about-list li {
    padding: 0.85rem 0;
    border-bottom: 1px solid var(--stute-rule);
}

.about-list li:first-child {
    border-top: 1px solid var(--stute-rule);
}

.about-term {
    font-weight: 600;
    color: var(--stute-ink);
}

/* Een project met een eigen adres is een link, een project zonder is dat niet.
   Het onderscheid mag niet uit kleur komen, dus de onderlijning doet het werk —
   en die staat er pas bij het aanwijzen, zodat de zes namen even zwaar wegen.
   De punten onder "Waarom STUTE?" dragen dezelfde vorm en zijn nooit een link. */
a.about-term {
    text-decoration: none;
}

a.about-term:hover {
    text-decoration: underline;
}

/* Twee kaartjes naast elkaar, op een smal scherm onder elkaar. Geen kaders en
   geen schaduw: een lijn bovenaan scheidt ze, zoals overal. */
.about-team {
    display: grid;
    gap: 2.5rem;
    align-self: stretch;
    margin-top: 0.5rem;
}

@media (min-width: 40rem) {
    .about-team {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

.about-member {
    border-top: 1px solid var(--stute-ink);
    padding-top: 1.25rem;
}

/* Vierkant, voor alle teamleden hetzelfde: de foto's komen van verschillende
   kanten, dus de gelijkheid moet uit de layout komen en niet uit het beeld. */
.about-photo {
    display: block;
    width: 100%;
    max-width: 11rem;
    aspect-ratio: 1 / 1;
    object-fit: cover;
    margin-bottom: 1.25rem;
    /* In grijswaarden, om twee redenen. De portretten komen van waar ze komen:
       de ene is zwart-wit, de andere een kleurenfoto tegen een blauwe lucht, en
       naast elkaar valt dat verschil harder op dan de gezichten. En kleur op de
       site hoort van productfoto's en winkellogo's te komen (7.); een portret
       is geen van beide. Weg te halen met deze ene regel. */
    filter: grayscale(1);
}

/* Zolang er geen foto is: de initialen in de titelletter, in een vak met
   dezelfde maat. Een leeg kader leest als een plaatje dat niet geladen is, en
   een grijze silhouet-illustratie is decoratie die hier niet hoort. */
.about-photo.is-empty {
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--stute-rule);
    font-family: var(--stute-display);
    font-weight: 600;
    font-size: 2rem;
    letter-spacing: 0.12em;
    /* De letters staan gedempt: het vak is een plaatshouder en geen kop. */
    color: var(--stute-muted);
}

/* De oproep aan vrijwilligers, onder de kaartjes. Een dunne lijn erboven en
   niet de zwarte van een teamlid: het is geen derde lid maar een vraag aan wie
   leest, en dat verschil hoort te zien te zijn zonder het op te schrijven. */
.about-join {
    align-self: stretch;
    max-width: 44rem;
    border-top: 1px solid var(--stute-rule);
    padding-top: 1.25rem;
    font-size: 0.9375rem;
    line-height: 1.65;
}

.about-join p {
    margin: 0;
}

.about-join p + p {
    margin-top: 0.75rem;
}

/* De knop staat in een eigen regel onder de alinea en niet erachter: hij is de
   volgende stap en geen woord in de zin. */
.about-join p + p.about-join-action {
    margin-top: 1.25rem;
}

.about-member-name {
    font-family: var(--stute-display);
    font-size: 1.125rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    margin: 0;
}

/* Rol en land, klein en gedempt onder de naam: het zegt wie iemand is, niet
   wat je over hem leest. */
.about-member-role {
    font-size: 0.6875rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--stute-muted);
    margin: 0.4rem 0 0;
}

.about-member-text {
    font-size: 0.9375rem;
    line-height: 1.65;
    margin: 0.9rem 0 0;
}

.about-member-link {
    margin: 0.9rem 0 0;
    font-size: 0.875rem;
}

/* --- contact ------------------------------------------------------------- */

/* Het adres is de hele pagina, dus het staat in de titelletter en niet als een
   regel tekst. Kleiner dan de kop erboven — die zegt waar je bent, dit zegt wat
   je zoekt — en de onderlijning laat staan: het is een link, en dat hoort te
   zien te zijn zonder kleur. */
.contact-email {
    font-family: var(--stute-display);
    font-size: clamp(1.5rem, 5vw, 2.25rem);
    font-weight: 600;
    letter-spacing: 0.02em;
    line-height: 1.2;
    margin: 2.5rem 0 0;
    /* Een adres breekt niet netjes af; op een smal scherm mag het dan wel over
       de regel heen in plaats van eruit te lopen. */
    overflow-wrap: anywhere;
}

.contact-email a {
    color: var(--stute-ink);
    text-decoration: underline;
    text-underline-offset: 0.18em;
}

/* --- kiezen tussen initiatief en winkel ---------------------------------- */

/* Twee blokken onder elkaar, elk met een lijn erboven. Geen kaders en geen
   kolommen: één kolom leest op een telefoon hetzelfde als op een scherm, en de
   lijn doet wat een kaart met schaduw ook zou doen (7.). */
.register-options {
    display: flex;
    flex-direction: column;
    gap: 3rem;
}

.register-option {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 1rem;
}

/* De lijn hoort bij de kop en niet bij het blok ervoor, dus staat ze er strak
   boven in plaats van in het midden van de tussenruimte. */
.register-option .rule {
    align-self: stretch;
    margin-bottom: 0.5rem;
}

.register-option .lede {
    max-width: 30rem;
}

/* Wat een registratie inhoudt, boven het formulier van een winkel of een
   initiatief: per punt een kleine kop in kapitalen en één zin in inkt. Geen
   opsommingstekens en geen kaders — het schaalverschil tussen kop en zin draagt de lijst. */
.register-intro {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
    margin-bottom: 0;
}

.register-intro dt {
    font-family: var(--stute-display);
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    margin-bottom: 0.375rem;
}

.register-intro dd {
    font-size: 0.9375rem;
    line-height: 1.6;
    margin: 0;
}

.plain-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

/* --- verzending en afhalen ----------------------------------------------- */

/* Een <fieldset> hoort hier omdat de twee groepen radio's en velden elk één
   vraag beantwoorden en een schermlezer dat verband anders niet hoort. De
   browserrand eromheen moet wel weg: die is een kader, en kaders zijn hier de
   dunne lijn (7.). */
.settings-fieldset {
    border: 0;
    padding: 0;
    margin: 0;
    min-width: 0;
}

.settings-fieldset legend {
    float: none;
    width: auto;
    padding: 0;
    margin: 0;
}

/* --- opmaakbalk boven een tekstveld --------------------------------------- */

/* De balk hangt op het veld en niet erboven: samen zijn het één ding. Vandaar
   dat alleen de onderrand tussen de twee staat en de buitenrand doorloopt. */
.richtext-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    gap: 0;
    border: 1px solid var(--stute-rule-strong);
    border-bottom: 0;
    background: var(--stute-paper);
}

.richtext-toolbar button,
.richtext-toolbar .richtext-style {
    border: 0;
    border-right: 1px solid var(--stute-rule);
    background: transparent;
    color: var(--stute-ink);
    font-family: inherit;
    font-size: 0.85rem;
    line-height: 1;
    padding: 0.6rem 0.85rem;
    cursor: pointer;
}

.richtext-toolbar button:hover,
.richtext-toolbar .richtext-style:hover {
    background: var(--stute-ink);
    color: var(--stute-paper);
}

.richtext-toolbar .richtext-style {
    /* De keuzelijst is even hoog als de knoppen ernaast; zonder deze regel
       zet de browser er zijn eigen hoogte op en springt de balk. */
    appearance: none;
    padding-right: 1.5rem;
}

/* Het bewerkbare vlak leest als het veld dat het vervangt: dezelfde rand,
   dezelfde binnenruimte, alleen hoger omdat er meer in komt. */
.richtext-area {
    min-height: 9rem;
    height: auto;
    overflow-y: auto;
    text-align: left;
}

/* Wat erin staat, staat er zoals het straks op het portaal staat: koppen
   kleiner naarmate ze dieper zitten, en geen marge boven de eerste regel. */
.richtext-area h2,
.richtext-area h3,
.richtext-area h4 {
    font-family: var(--stute-display);
    font-weight: 600;
    letter-spacing: 0.02em;
    margin: 1.2rem 0 0.4rem;
}

.richtext-area h2 { font-size: 1.35rem; }
.richtext-area h3 { font-size: 1.15rem; }
.richtext-area h4 { font-size: 1rem; }

.richtext-area > :first-child {
    margin-top: 0;
}

.richtext-area p,
.richtext-area ul,
.richtext-area ol {
    margin: 0 0 0.75rem;
}

.richtext-area ul,
.richtext-area ol {
    padding-left: 1.4rem;
}

/* Een link in eigen tekst blijft inkt met een streep eronder. Het blauw van de
   browser is de enige kleur die hier ongevraagd binnenkomt, en kleur is
   voorbehouden aan foto's en logo's (7.). */
.richtext-area a {
    color: var(--stute-ink);
    text-decoration: underline;
    text-underline-offset: 0.15em;
}

/* --- huisstijl: logo, kleuren, foto's ------------------------------------- */

/* Zonder balk bovenaan begint de pagina met het beeld. Een strook lucht erboven
   houdt het een pagina en geen banner die tegen de rand van het scherm plakt. */
.portal {
    display: block;
    padding-top: 2.5rem;
}

/* Het logo van een winkel is een van de twee plaatsen waar kleur binnenkomt
   (7.), dus staat er niets omheen wat om aandacht vraagt: een dunne lijn en
   wit eronder, zodat een doorzichtige png zichtbaar blijft. */
.brand-logo {
    display: inline-block;
    border: 1px solid var(--stute-rule);
    background: var(--stute-paper);
    padding: 1rem 1.25rem;
}

.brand-logo img {
    display: block;
    max-width: 15rem;
    max-height: 6rem;
    width: auto;
    height: auto;
}

/* Het tekstveld draagt de waarde, het vierkantje ernaast is de kiezer. Ze staan
   naast elkaar en even hoog, zodat het één veld lijkt in plaats van twee. */
.colour-field {
    display: flex;
    align-items: stretch;
    gap: 0.75rem;
}

.colour-field .form-control {
    max-width: 11rem;
    font-variant-numeric: tabular-nums;
}

.colour-swatch {
    flex: 0 0 auto;
    width: 3rem;
    padding: 0;
    border: 1px solid var(--stute-rule-strong);
    background: var(--stute-paper);
    cursor: pointer;
}

/* Het voorbeeld bij de kleurvelden: een lijn en een kader, de voortgang van een
   lijst en de hoofdknop — de plaatsen waar de huisstijl van een winkel of
   initiatief op zijn pagina's terechtkomt. De kleuren komen binnen als
   --brand-fill, --brand-on-fill en --brand-line; welke kleur welke rol krijgt,
   wordt in src/brand.php beslist en niet hier.

   Lijnen en kaders krijgen de kleur zoals ze gegeven is, ook een bijna-witte —
   ze dragen geen betekenis, dus een lichte lijn is een keuze en geen fout. Bij
   een gevuld vlak ligt het anders: daar zegt de vulling hoeveel er al is, en
   die grens moet zichtbaar blijven. Vandaar de haarlijn in inkt rond de knop en
   langs het gevulde deel van de balk. */
.brand-preview {
    max-width: 22rem;
    border: 1px solid var(--stute-rule);
    background: var(--stute-paper);
    padding: 1.5rem;
}

.brand-preview-rule {
    border: 0;
    border-top: 1px solid var(--brand-line);
    margin: 0;
    opacity: 1;
}

/* De tweede kleur draagt de lijnen én de kaders, en één haarlijn bovenaan is te
   weinig om dat te tonen. Het kader staat in 3:2, dezelfde verhouding als de
   foto's op het portaal. */
.brand-preview-frame {
    width: 9rem;
    aspect-ratio: 3 / 2;
    border: 1px solid var(--brand-line);
    margin-top: 1.5rem;
}

/* De baan is de hele lijst, het blok erin is wat er al geschonken is. Dat blok
   sluit rechts af met een lijn in inkt, zodat de grens ook zichtbaar blijft bij
   een lichte kleur. */
.brand-preview-bar {
    height: 0.55rem;
    border: 1px solid var(--stute-ink);
    margin-top: 1.5rem;
}

.brand-preview-bar span {
    display: block;
    width: 62%;
    height: 100%;
    background: var(--brand-fill);
    border-right: 1px solid var(--stute-ink);
}

/* Dezelfde maten als .btn-ink .btn-compact, maar het is geen knop: er valt
   niets te klikken, het toont alleen hoe de tekst op de kleur staat. */
.brand-preview-button {
    display: inline-block;
    margin-top: 1.5rem;
    border: 1px solid var(--stute-ink);
    background: var(--brand-fill);
    color: var(--brand-on-fill);
    font-size: 0.75rem;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    padding: 0.7rem 1.25rem;
}

/* Eén verhouding voor alle foto's (7.): wat een winkel ook aanlevert, hier
   staat het in 3:2, want de consistentie moet uit de layout komen. */
.photo-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
    gap: 1.5rem;
    list-style: none;
    padding: 0;
    margin: 0;
}

.photo-item img {
    display: block;
    width: 100%;
    aspect-ratio: 3 / 2;
    object-fit: cover;
    border: 1px solid var(--stute-rule);
}

/* Bedankfoto's (migratie 046): op de schenking en op de publieke pagina van
   een initiatief. Vierkant en niet 3:2 zoals de portaalfoto's: ze worden met een
   gsm genomen, even vaak staand als liggend, en een vierkant snijdt van allebei
   evenveel weg. Het bestand zelf is niet bijgesneden; de volledige foto staat
   één klik verder. */
.thank-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
    gap: 2rem 1.5rem;
    list-style: none;
    padding: 0;
    margin: 0;
}

.thank-photo img {
    display: block;
    width: 100%;
    aspect-ratio: 1 / 1;
    object-fit: cover;
    border: 1px solid var(--stute-rule);
}

.thank-message {
    font-size: 0.9375rem;
    line-height: 1.55;
    margin: 0.875rem 0 0;
    overflow-wrap: anywhere;
}

.thank-meta {
    font-size: 0.8125rem;
    color: var(--stute-muted);
    margin: 0.375rem 0 0;
}

/* Op de publieke pagina draagt het kader de lijnkleur van het initiatief, net
   als de foto's van zijn lijsten. */
.portal-at-work .thank-photo img {
    border-color: var(--brand-line);
}

/* Op de bedankpagina de hele foto, in de breedte van het dankwoord. */
.thanks-photo {
    max-width: 34rem;
    margin-bottom: 0;
}

.thanks-photo img {
    display: block;
    width: 100%;
    height: auto;
    border: 1px solid var(--brand-line, var(--stute-rule));
}

/* --- publieke pagina van een winkel of initiatief ------------------------ */

/* Een portaal gaat breder dan leestekst: het fotoraster zou in de kolom van de
   publieke zone (34rem) in twee kolommen blijven steken. De tekst erbinnen houdt
   zijn eigen breedte, want een alinea over de volle 52rem leest niet. */
.public-content-wide {
    max-width: none;
}

/* Het logo ís de kop. Er staat geen naam onder: een wordmark met de naam
   eronder zet er twee keer hetzelfde (7.). Zonder logo staat de naam er als
   tekst, en dan draagt de h1 die. */
.portal-name {
    margin: 0;
}

/* De plaat met het logo overlapt de foto linksonder. Wit met een haarlijn, zodat
   ze ook op een lichte foto een vlak blijft en niet in het beeld oplost. De
   inspringing links volgt de foto en niet de tekstkolom: zo hangt de plaat aan
   het beeld vast in plaats van ernaast te zweven. */
.portal-name.is-over {
    display: inline-block;
    position: relative;
    margin-top: -3.5rem;
    margin-left: 2rem;
    /* Even ver van de rechterrand van de foto als van de linker: een plaat die
       aan één kant buiten het beeld steekt, leest als een fout. */
    max-width: calc(100% - 4rem);
    padding: 1.25rem 1.75rem;
    background: var(--stute-paper);
    border: 1px solid var(--stute-rule);
}

/* Binnen de plaat wint de plaat: een breed wordmerk krimpt mee in plaats van
   erbuiten te steken. */
.portal-name.is-over .portal-logo {
    max-width: 100%;
}

@media (max-width: 40rem) {
    .portal-name.is-over {
        margin-top: -2rem;
        margin-left: 1rem;
        max-width: calc(100% - 2rem);
        padding: 1rem 1.25rem;
    }
}

.portal-logo {
    display: block;
    max-width: 18rem;
    max-height: 7rem;
    width: auto;
    height: auto;
}

/* De fotoreeks opent de pagina. Eén foto per keer en over de volle breedte: de
   foto's zijn samen met het logo het enige dat hier kleur mag dragen (7.), en
   een rij duimnagels maakt van dat voorrecht een contactblad. De verhouding is
   3:2, dezelfde waarin src/media.php ze bewaart, zodat er niet twee keer
   bijgesneden wordt.

   Zonder javascript staan alle foto's onder elkaar, even groot. De klasse
   is-slideshow wordt door portal.js gezet en pas dan verdwijnen ze op één na. */
.portal-slide {
    margin: 0;
}

.portal-slide + .portal-slide {
    margin-top: 1.5rem;
}

.portal-hero.is-slideshow .portal-slide {
    display: none;
}

.portal-hero.is-slideshow .portal-slide.is-current {
    display: block;
    margin-top: 0;
}

.portal-slide img {
    display: block;
    width: 100%;
    aspect-ratio: 3 / 2;
    object-fit: cover;
    border: 1px solid var(--brand-line);
}

/* De pijlen staan in het beeld, tegen de linker- en rechterrand. Geen teller
   erbij: hoeveel foto's er zijn, is geen mededeling, en de vorige/volgende zegt
   de pijl zelf al. Ze liggen als één laag over de foto en vangen alleen daar
   muisklikken waar ze zelf staan. */
.portal-hero {
    position: relative;
}

.portal-slide-nav {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 1rem;
    pointer-events: none;
}

/* Een wit vlakje met de pijl in inkt: leesbaar op elke foto, ook op een lichte,
   en zonder de schaduw waar 7. vanaf wil. Gedempt tot je erover gaat. */
.portal-step {
    pointer-events: auto;
    width: 2.75rem;
    height: 2.75rem;
    border: 0;
    border-radius: 0;
    background: var(--stute-paper);
    color: var(--stute-ink);
    font-size: 1.5rem;
    line-height: 1;
    cursor: pointer;
    opacity: 0.8;
    transition: opacity 0.12s linear;
}

.portal-step:hover,
.portal-step:focus-visible {
    opacity: 1;
}

/* De lijn die de pagina indeelt, in de kleur van de winkel of het initiatief.
   Zonder huisstijl is dat inkt en ziet het eruit als elke andere STUTE-pagina. */
.portal-rule {
    border: 0;
    border-top: 1px solid var(--brand-line);
    margin: 0;
    opacity: 1;
}

.portal-text {
    max-width: 34rem;
}

/* Het schaalverschil tussen de eerste alinea en de rest doet hier het werk dat
   elders een accentkleur zou doen (7.). */
.portal-text > p:first-of-type {
    font-size: 1.125rem;
    line-height: 1.6;
}

/* De enige knopvorm op deze pagina, en de plek waar de huisstijl een vlak vult:
   naar een lijst, naar de registratie. Het is telkens dezelfde handeling — ga
   hierheen — dus krijgt ze dezelfde vorm. De tekstkleur volgt uit de merkkleur
   (src/brand.php), de haarlijn in inkt houdt de vorm staan bij een bijna-witte
   kleur, en omkeren bij hover vraagt geen tweede kleur — net als .btn-ink. */
.portal-action {
    display: inline-block;
    border: 1px solid var(--stute-ink);
    background: var(--brand-fill);
    color: var(--brand-on-fill);
    font-size: 0.75rem;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    text-decoration: none;
    padding: 0.95rem 1.75rem;
    transition: background-color 0.12s linear, color 0.12s linear;
}

.portal-action:hover {
    background: var(--stute-paper);
    color: var(--stute-ink);
}

/* Het blok waar de knop naartoe springt. De ruimte erboven houdt de kop van de
   rand van het scherm wanneer de browser er zelf naartoe springt. */
.portal-section {
    scroll-margin-top: 2rem;
}

/* Een kop met een kort streepje in de merkkleur erboven. Dat streepje is de
   enige plek waar de huisstijl een kop raakt: de letter zelf blijft van STUTE. */
.portal-section-title::before {
    content: "";
    display: block;
    width: 2.5rem;
    border-top: 2px solid var(--brand-line);
    margin-bottom: 1.25rem;
}

/* De lijsten. Dezelfde rasteropbouw als de fotoreeks: de foto van een lijst is
   het grootste element, de naam eronder. */
.portal-lists {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
    gap: 2.5rem;
}

.portal-list-photo {
    display: block;
    width: 100%;
    aspect-ratio: 3 / 2;
    object-fit: cover;
    border: 1px solid var(--brand-line);
    margin-bottom: 1rem;
}

/* Een lijst zonder foto naast een lijst met foto: een leeg kader in plaats van
   niets, zodat de koppen op dezelfde hoogte blijven. Het kader is lichter dan
   dat van een foto — er valt niets te zien, en dat mag het zeggen. */
.portal-list-photo.is-empty {
    border-color: var(--stute-rule);
}

/* Valt het beeld terug op het logo van het initiatief, dan is het geen foto:
   een logo hoort heel te blijven en niet bijgesneden te worden. Contain in
   plaats van cover, met lucht eromheen, zoals in de logostrook op de
   voorpagina. */
.portal-list-photo.is-logo {
    object-fit: contain;
    padding: 1.5rem;
    border-color: var(--stute-rule);
}

/* De merktekens onder de foto: wie de lijst draagt en waar de producten liggen.
   Klein, want ze zijn de herkenning en niet de boodschap — en in kleur, want
   logo's en productfoto's zijn de enige kleurbron op de site. */
.portal-list-marks {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem;
    margin: -0.25rem 0 0.75rem;
    min-height: 1.5rem;
}

.portal-list-mark {
    display: block;
    height: 1.5rem;
    width: auto;
    max-width: 4.5rem;
    object-fit: contain;
}

/* Een winkel zonder logo: een vierkantje met de eerste letter van zijn naam,
   even hoog als een logo. Dezelfde plaatshoudervorm als het vak van een teamlid
   zonder foto (.about-photo.is-empty) — dunne lijn, gedempte letter — zodat het
   naast een echt logo niet om aandacht vraagt. Twee winkels met dezelfde letter
   is geen probleem: het is herkenning en geen sleutel. */
.portal-list-mark.is-initial {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 1.5rem;
    border: 1px solid var(--stute-rule);
    font-family: var(--stute-display);
    font-weight: 600;
    font-size: 0.6875rem;
    color: var(--stute-muted);
}

/* Het logo van het initiatief staat vooraan en wordt van de winkels gescheiden
   door een dunne lijn: het is de ontvanger en niet een van de leveranciers. */
.portal-list-mark.is-charity {
    padding-right: 0.6rem;
    border-right: 1px solid var(--stute-rule);
}

/* Het aantal winkels dat niet meer op de rij past. Dezelfde kleine kapitalen
   als de regel eronder, zodat het meeweegt als tekst en niet als logo. */
.portal-list-more {
    font-size: 0.6875rem;
    letter-spacing: 0.14em;
    color: var(--stute-muted);
}

/* De naam van een lijst is data en houdt zijn eigen schrijfwijze, net als de
   naam bovenaan de pagina. */
.portal-list-name {
    font-family: var(--stute-display);
    font-size: 1.125rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    line-height: 1.2;
    margin: 0;
}

.portal-list-meta {
    font-size: 0.8125rem;
    color: var(--stute-muted);
    margin: 0.5rem 0 0;
}

/* De twee wervende blokken: naast elkaar wanneer het scherm het toelaat, anders
   onder elkaar. Binnenin blijft het de opbouw van het registratiescherm. */
.portal-join {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
    gap: 2.5rem;
}

/* Wie het is, waar het gevestigd is en onder welk nummer — verplichte gegevens
   en geen leesstof, dus gedempt en klein, onderaan de pagina. */
.portal-legal {
    font-size: 0.8125rem;
    line-height: 1.7;
    color: var(--stute-muted);
}

/* --- leverregio's: kaart en keuze ---------------------------------------- */

.region-form {
    max-width: 40rem;
}

/* De kaart is zwart-wit zoals de rest: gekozen regio's staan in inkt, de rest
   staat open. Geen kleur nodig om aan/uit te tonen, want er zijn maar twee
   toestanden en het contrast is maximaal. */
.region-map {
    display: block;
    width: 100%;
    height: auto;
    margin: 2rem 0 2.5rem;
    /* Een kaart is breder dan hij hoog hoeft te zijn; zonder deze grens neemt
       een noord-zuid gerekt gebied het halve scherm. */
    max-height: 26rem;
}

.region-shape {
    fill: var(--stute-paper);
    stroke: var(--stute-rule-strong);
    stroke-width: 1.5;
    cursor: pointer;
    transition: fill 0.1s linear;
}

.region-shape.is-selected {
    fill: var(--stute-ink);
    stroke: var(--stute-paper);
}

.region-shape.is-hover:not(.is-context) {
    fill: var(--stute-rule);
}

.region-shape.is-selected.is-hover {
    fill: var(--stute-muted);
}

/* De eigen regio staat vast aan: hij mag er niet uitzien alsof je erop kan
   klikken. */
.region-shape.is-own {
    cursor: default;
}

/* Wat buiten bereik ligt, blijft zichtbaar maar doet niet mee: het toont waar
   de uitsnede ophoudt zonder te doen alsof het een keuze is. Grijs gevuld, want
   met alleen een lichtere lijn valt op deze schaal niet te zien welk gebied nu
   wel of niet aan te klikken is. Drie toestanden, drie tinten: wit is te
   kiezen, zwart is gekozen, grijs is buiten bereik. */
.region-shape.is-context {
    fill: rgba(26, 25, 25, 0.06);
    stroke: var(--stute-rule);
    stroke-width: 1;
    cursor: default;
    pointer-events: none;
}

/* Het punt op de eigen regio. Wit, want het ligt op een zwarte vorm. */
.region-pin {
    fill: var(--stute-paper);
    pointer-events: none;
}

.region-group {
    margin-top: 2rem;
    margin-bottom: 0.75rem;
}

.region-option {
    padding: 0.15rem 0;
}

.region-option.is-hover span {
    text-decoration: underline;
    text-underline-offset: 0.25em;
}

/* De vermelding dat een regio de eigen streek is. Geen tweede zin eronder,
   alleen dit label achter de naam. */
.region-note {
    margin-left: 0.75rem;
    font-size: 0.6875rem;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--stute-muted);
}

.region-counter {
    margin: 0;
}

/* Een invoerveld dat meeloopt in een regel tekst, zoals het maximum aantal
   regio's in het winkeloverzicht. Geen .form-control: dat is een veld in een
   formulier met een label erboven, dit is een getal in een zin. */
.input-inline {
    width: 4rem;
    border: 0;
    border-bottom: 1px solid var(--stute-rule-strong);
    border-radius: 0;
    background: none;
    padding: 0 0 1px;
    font: inherit;
    color: var(--stute-ink);
}

.input-inline:focus {
    outline: none;
    border-bottom-color: var(--stute-ink);
}

/* Hetzelfde veld, maar voor een slug: die is geen getal van drie cijfers. Groeit
   niet mee met de pagina — voorbij een naam of twintig tekens zegt de breedte
   niets meer. */
.input-slug {
    width: 16rem;
    max-width: 100%;
}

/* --- zoeken in een overzicht ---------------------------------------------- */

/* Eén veld met de knop ernaast, op één regel zolang er ruimte is. Het veld
   groeit niet mee met de pagina: een zoekterm is kort, en een balk over de
   volle breedte zou zwaarder wegen dan de lijst eronder. */
.search-form {
    display: flex;
    align-items: center;
    gap: 1rem;
    flex-wrap: wrap;
}

.search-field {
    flex: 1 1 16rem;
    max-width: 24rem;
}

/* Safari zet een eigen kruisje en afronding op type="search". */
.search-field[type="search"] {
    -webkit-appearance: none;
    appearance: none;
}

/* --- lijst met records (winkels, initiatieven, later lijsten en orders) --- */

/* Scheiding tussen twee records is een lijn, geen kaart. De ruimte eromheen is
   groter dan die binnen een record, zodat de blokken zonder rand al uit elkaar
   vallen. */
.record + .record {
    border-top: 1px solid var(--stute-rule);
    margin-top: 3rem;
    padding-top: 3rem;
}

.record:first-of-type {
    margin-top: 2.5rem;
}

.record-title {
    font-family: var(--stute-display);
    font-size: 1.375rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    margin: 0;
}

/* Label links, waarde rechts. Op een smal scherm onder elkaar: de labels zijn
   in sommige talen te lang om een kolom van te maken. */
.data-list {
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.data-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem 1.5rem;
}

.data-row dt {
    flex: 0 0 11rem;
    font-size: 0.6875rem;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--stute-muted);
    font-weight: 400;
    padding-top: 0.2rem;
}

.data-row dd {
    margin: 0;
    font-size: 0.9375rem;
    line-height: 1.5;
    /* Blijft naast het label staan zolang er ruimte is, ook wanneer er een
       bijzin onder de waarde hangt. */
    flex: 1 1 16rem;
    min-width: 0;
}

/* --- instellingen ------------------------------------------------------- */

/* Zelfde ritme als de recordlijst: blokken uit elkaar door ruimte en een lijn.
   In een .card-grid worden ze kaarten (zie onder de stappen). */
.settings-block {
    margin-top: 2.5rem;
}

.settings-block + .settings-block {
    border-top: 1px solid var(--stute-rule);
    margin-top: 2.5rem;
    padding-top: 2.5rem;
}

/* Wat nog moet gebeuren, springt eruit met een lijn links, in dezelfde kleur
   als het bolletje in het menu: wie dat bolletje volgt, komt hier uit en ziet
   meteen wélke blokken het waren.

   Let op het verschil met .field-error, dat dezelfde vorm heeft maar zwart
   blijft: een foutmelding staat onder het veld waar hij bij hoort en wordt
   gelezen door wie er net op geklikt heeft. Dit moet opvallen vanaf de rand van
   het scherm. */
.settings-block.is-todo {
    border-left: 2px solid var(--stute-alert);
    padding-left: 1.25rem;
}

/* Het adres van een feed staat voluit onder het blok. Een lang adres mag de
   kolom niet breder maken dan de rest van de pagina. */
.import-feed-url {
    word-break: break-all;
}

.settings-summary {
    margin: 0.75rem 0 1.25rem;
    font-size: 0.9375rem;
    line-height: 1.5;
}

/* --- handelingen in een ander programma ----------------------------------- */

/* Wat een winkel in zijn eigen webshop moet doen voor hij hier iets kan
   invullen. De volgorde telt, dus nummers — klein en grijs, zoals de labels
   elders: het is een geheugensteun naast de tekst, niet een kop. */
.instruction-list {
    counter-reset: instruction;
    list-style: none;
    padding: 0;
    margin: 0;
    max-width: 38rem;
}

.instruction-list li {
    counter-increment: instruction;
    position: relative;
    padding-left: 2rem;
    font-size: 0.9375rem;
    line-height: 1.65;
}

.instruction-list li + li {
    margin-top: 0.75rem;
}

.instruction-list li::before {
    content: counter(instruction);
    position: absolute;
    left: 0;
    top: 0.2rem;
    font-size: 0.6875rem;
    letter-spacing: 0.2em;
    color: var(--stute-muted);
}

/* De rechten onder een stap: per tabel de naam zoals de webshop ze schrijft
   (vaste letterbreedte, want ze wordt opgezocht en niet gelezen), en wat er
   aangevinkt moet worden. Twee kolommen waar er plaats is: het zijn er tot
   dertig, en een winkel loopt ze af met zijn eigen rooster ernaast. */
.privilege-list {
    list-style: none;
    padding: 0;
    margin: 0.75rem 0 0;
    columns: 2 16rem;
    column-gap: 2rem;
    border-top: 1px solid var(--stute-rule);
    padding-top: 0.75rem;
}

.instruction-list .privilege-list li {
    counter-increment: none;
    padding-left: 0;
    margin: 0;
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    break-inside: avoid;
    font-size: 0.8125rem;
    line-height: 1.8;
}

.instruction-list .privilege-list li::before {
    content: none;
}

.privilege-list code {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.8125rem;
    color: var(--stute-ink);
}

.privilege-list span {
    color: var(--stute-muted);
    text-align: right;
}

/* De rechten om te plakken, onder dezelfde lijn als de rechten per tabel:
   het veld zo breed als de stap, de knop ernaast. */
.scope-copy {
    display: flex;
    gap: 0.75rem;
    align-items: center;
    margin-top: 0.75rem;
    padding-top: 0.75rem;
    border-top: 1px solid var(--stute-rule);
}

.scope-field {
    flex: 1;
    min-width: 0;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.8125rem;
}

/* Een tweede weg die niet de eerste is: dicht tot iemand ze zoekt. Dezelfde
   vorm als de taalkeuze — een klein label met een plus — zodat ze niet als een
   kop leest die even zwaar weegt als wat erboven staat. */
.disclosure > summary {
    display: inline-block;
    list-style: none;
    cursor: pointer;
    font-size: 0.6875rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--stute-ink);
    border-bottom: 1px solid var(--stute-rule-strong);
    padding-bottom: 1px;
}

.disclosure > summary::-webkit-details-marker {
    display: none;
}

.disclosure > summary::after {
    content: "+";
    margin-left: 0.6em;
}

.disclosure[open] > summary::after {
    content: "\2212";
}

/* Wachten op iets wat elders gebeurt: een stip die zacht pulseert naast de
   tekst. Geen kleur, geen draaiend wiel — alleen een teken dat er nog leven in
   zit. */
.app-waiting::before {
    content: "";
    display: inline-block;
    width: 0.4rem;
    height: 0.4rem;
    margin-right: 0.6rem;
    border-radius: 50%;
    background: var(--stute-ink);
    vertical-align: middle;
    animation: app-waiting 1.6s ease-in-out infinite;
}

@keyframes app-waiting {
    0%, 100% { opacity: 0.2; }
    50%      { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    .app-waiting::before {
        animation: none;
        opacity: 0.6;
    }
}

/* --- stappen op het overzicht -------------------------------------------- */

/* Zelfde ritme als de instellingenblokken; het nummer staat in de eyebrow, dus
   de opsomming heeft er zelf geen nodig. */
.step-list {
    list-style: none;
    padding: 0;
    margin: 0;
}

.step {
    margin-top: 2.5rem;
}

.step + .step {
    border-top: 1px solid var(--stute-rule);
    padding-top: 2.5rem;
}

/* De stap van dit moment, op hetzelfde spoor als het bolletje in het menu en de
   lijn bij de instellingen. Precies één stap draagt hem: stappen staan na
   elkaar, en drie keer hetzelfde teken is geen volgorde meer. */
.step.is-current {
    border-left: 2px solid var(--stute-alert);
    padding-left: 1.25rem;
}

/* Waarom deze stap er is. De enige uitleggende zin op het scherm, en hij zegt
   iets wat de kop niet zegt — anders hoorde hij hier niet te staan. */
.step-why {
    margin: 0.75rem 0 0;
    max-width: 38rem;
    font-size: 0.9375rem;
    line-height: 1.65;
    color: var(--stute-muted);
}

.step-part {
    margin-top: 1.75rem;
}

/* De naam van een onderdeel binnen een stap. Kleiner dan een kop en groter dan
   een label: het is een tussenniveau, niet een tweede kop. */
.step-part-title {
    margin: 0;
    font-size: 0.6875rem;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--stute-muted);
}

.step-part-title + .settings-summary {
    margin-top: 0.4rem;
}

/* --- kaarten in de beheerzone -------------------------------------------- */

/* Een reeks gelijkwaardige blokken — instellingen, stappen, winkels,
   bestellingen, lijsten — staat naast elkaar zolang er plaats is, elk in een
   dunne rand. Onder elkaar werd het op een groot scherm een lange kolom van
   halve regels; zo past een overzicht meestal op één scherm.

   Het blijft dezelfde richting: de rand is de lijn die elders twee blokken
   scheidt, alleen nu rondom. Geen schaduw, geen afronding, geen vulling.

   Een kolom zakt nooit onder 20rem (of de schermbreedte op een gsm): een kop,
   een regel en een knop moeten erin passen zonder dat de kop breekt. auto-fill
   en geen vast aantal kolommen: het aantal blokken verschilt per scherm en per
   gebruiker, en een rij die niet vol raakt, stoort hier niet — dit is geen
   raster van foto's. */
.card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 20rem), 1fr));
    gap: 1.5rem;
    margin: 2.5rem 0 0;
    padding: 0;
    list-style: none;
}

/* De lijn onder de kop van het scherm valt weg vlak boven een rij kaarten:
   hun bovenrand doet dat werk al, en twee lijnen op 2,5rem van elkaar lezen
   als een slordigheid. Waar de rij leeg is (een zin in plaats van kaarten),
   blijft de lijn staan. */
.rule:has(+ .card-grid),
.rule:has(+ form > .card-grid) {
    display: none;
}

/* Na de regels van .settings-block, .record en .step, met dezelfde
   specificiteit: zo wint de kaart van hun lijn-tussen-twee-blokken. */
.card-grid > .settings-block,
.card-grid > .record,
.card-grid > .step {
    display: flex;
    flex-direction: column;
    min-width: 0;
    margin: 0;
    padding: 1.5rem;
    border: 1px solid var(--stute-rule);
}

/* Wat nog moet gebeuren of aan de beurt is, houdt zijn lijn links — nu als
   linkerrand van de kaart, in dezelfde dikte als voorheen. */
.card-grid > .is-todo,
.card-grid > .is-current {
    border-left: 2px solid var(--stute-alert);
}

/* Een knop rechtstreeks in de kaart wordt niet zo breed als de kaart. */
.card-grid > * > .btn-ink {
    align-self: flex-start;
}

/* De knoppen van kaarten naast elkaar staan op dezelfde hoogte: het blok vóór
   de knoppen vult de ruimte op die een kortere kaart overhoudt. Niet bij de
   stappen — daar heeft een stap soms twee onderdelen met elk een knop. */
.card-grid > .settings-block > :nth-last-child(2),
.card-grid > .record > :nth-last-child(2) {
    flex-grow: 1;
}

/* Aantal achter een verwijzing. Gedempt: het is een bijzin bij de link. */
.count {
    margin-left: 0.75rem;
    font-size: 0.6875rem;
    letter-spacing: 0.2em;
    color: var(--stute-muted);
}

/* Bevestigende knop en de tegenhanger ernaast, op één regel. */
.action-row {
    display: flex;
    align-items: center;
    gap: 2rem;
    flex-wrap: wrap;
}

/* --- lopende tekst: voorwaarden en privacybeleid ------------------------- */

/* De enige plek waar tekst van buiten de templates binnenkomt (content/), dus
   de opmaak wordt hier op de tag gezet en niet op klassen: wie het contract
   schrijft, mag geen klassennamen hoeven kennen. Tussenruimte komt van de
   afstand tussen broers en zussen, zodat er nergens een marge onderaan een blok
   blijft hangen. */
.prose {
    font-size: 0.9375rem;
    line-height: 1.7;
}

.prose h2,
.prose h3,
.prose p,
.prose ul,
.prose ol {
    margin: 0;
}

.prose > * + * {
    margin-top: 1.25rem;
}

.prose > * + h2 {
    margin-top: 3rem;
}

.prose > * + h3 {
    margin-top: 2rem;
}

/* Een kop hoort dichter bij zijn eigen alinea te staan dan bij de vorige. */
.prose > h2 + *,
.prose > h3 + * {
    margin-top: 0.75rem;
}

.prose h2 {
    font-family: var(--stute-display);
    font-size: 1rem;
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: uppercase;
}

.prose h3 {
    font-family: var(--stute-display);
    font-size: 0.9375rem;
    font-weight: 600;
    letter-spacing: 0.04em;
}

.prose ul,
.prose ol {
    padding-left: 1.2rem;
}

.prose li + li {
    margin-top: 0.5rem;
}

.prose a {
    color: var(--stute-ink);
    text-decoration: underline;
    text-underline-offset: 2px;
}

/* --- productimport ------------------------------------------------------- */

/* De schermen van de import zijn de enige plek met een tabel. Ze is er ook
   echt een: kolomkop, voorbeeldwaarde en wat die kolom wordt, horen per rij bij
   elkaar. Scheiding met lijnen, zoals overal — geen streping, geen kader. */
.table-scroll {
    overflow-x: auto;
}

.import-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.9375rem;
}

.import-table th,
.import-table td {
    text-align: left;
    vertical-align: middle;
    padding: 0.75rem 1.5rem 0.75rem 0;
    border-bottom: 1px solid var(--stute-rule);
}

.import-table thead th {
    font-size: 0.6875rem;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--stute-muted);
    font-weight: 400;
}

.import-column {
    font-weight: 600;
}

/* De waarde uit het bestand staat gedempt: ze is er om de kop te duiden, niet
   om gelezen te worden. */
.import-example {
    color: var(--stute-muted);
    max-width: 22rem;
    overflow-wrap: anywhere;
}

.import-price {
    white-space: nowrap;
}

.import-role {
    width: 15rem;
    padding-right: 0;
}

.import-role .form-control {
    padding: 0.5rem 0.7rem;
    font-size: 0.875rem;
}

.import-issue {
    font-size: 0.9375rem;
    line-height: 1.5;
}

.import-issue-examples {
    display: block;
    margin-top: 0.15rem;
    font-size: 0.8125rem;
    color: var(--stute-muted);
}

.import-history-row {
    display: flex;
    align-items: baseline;
    gap: 0.5rem 1.5rem;
    flex-wrap: wrap;
    font-size: 0.9375rem;
}

.import-history-date {
    flex: 0 0 6rem;
    font-size: 0.6875rem;
    letter-spacing: 0.2em;
    color: var(--stute-muted);
}

.import-history-name {
    flex: 1 1 14rem;
    overflow-wrap: anywhere;
}

/* De rij voor "geen waarde" in een aanvinklijst. Gedempt omdat het geen waarde
   uit het bestand is maar een woord van ons — het aantal ernaast staat wel in
   inkt, want dat komt wél uit het bestand. */
.import-empty {
    color: var(--stute-muted);
}

/* Aanvinklijst van categorieën of filterwaarden. Kolommen die zich naar de
   breedte voegen: het zijn er soms drie en soms zestig. */
.filter-choices {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    gap: 0.6rem 2rem;
    max-width: 48rem;
}

/* Het ophalen bij een webshop-API duurt tot dertig seconden per stuk. Zolang
   het loopt, staat er in de plaats van de knop een draaiend ringetje: geen
   balk, want de webshop zegt niet hoeveel producten er in totaal zijn, en een
   percentage van een geraden geheel is een leugen met cijfers erbij. */
.fetch-waiting {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    font-size: 0.9375rem;
}


.spinner {
    display: block;
    width: 1rem;
    height: 1rem;
    border: 1px solid var(--stute-rule-strong);
    /* De ring is dezelfde lijn als overal, met één zijde in inkt: het draaien
       maakt het teken, niet de kleur. */
    border-top-color: var(--stute-ink);
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
}

@keyframes spin {
    to { transform: rotate(360deg); }
}

/* Wie beweging liever niet ziet, krijgt een teken dat stilstaat maar er wel is:
   de ring blijft staan als een halve cirkel in inkt. */
@media (prefers-reduced-motion: reduce) {
    .spinner {
        animation: none;
    }
}

/* Een knop die geen knop hoort te lijken: hij staat naast een link en doet
   hetzelfde soort werk, maar het is een POST en dus een formulier. */
.btn-bare {
    border: 0;
    background: none;
    padding: 0;
    font: inherit;
    cursor: pointer;
}

/* Samen met .link-plain hoort de onderlijn van die link te blijven staan; de
   border:0 hierboven neemt hem anders weg, want deze regel staat erna. */
.link-plain.btn-bare {
    border-bottom: 1px solid var(--stute-rule-strong);
    padding-bottom: 1px;
}

/* --- producten in beeld -------------------------------------------------- */

/* Winkels leveren zelf hun foto's aan, dus de consistentie moet uit de layout
   komen (7.): elk kader is vierkant en de foto vult het, hoe hij ook aangeleverd
   is. De foto is het grootste element per product — het is de enige kleur op de
   pagina en hoort dat te blijven. */
/* --- winkels van een initiatief ------------------------------------------ */

/* De strook boven de zoekbalk en op de lijstenpagina: uit welke winkels er
   gekozen kan worden, met de knop om er een aan te brengen ernaast. Logo's
   mogen hier kleur dragen — dat is precies de kleurbron die sectie 7 toelaat. */
.shop-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
}

.shop-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    border: 1px solid var(--stute-rule);
    padding: 0.4rem 0.75rem;
    font-size: 0.8125rem;
    line-height: 1.3;
    color: inherit;
    text-decoration: none;
}

a.shop-chip:hover,
a.shop-chip:focus-visible {
    border-color: var(--stute-ink);
}

/* De winkel waarop nu gefilterd staat. Een rand in inkt met een streep eronder,
   dezelfde markering als het actieve item in het menu — geen vulling. */
.shop-chip.is-current {
    border-color: var(--stute-ink);
    box-shadow: inset 0 -2px 0 var(--stute-ink);
}

/* De knop om er een aan te brengen hoort bij de rij maar is geen winkel; het
   plusteken zegt dat zonder er een tweede stijl bij te halen. */
.shop-chip.is-add::before {
    content: "+";
    font-size: 1rem;
    line-height: 1;
}

/* Een winkel die meedoet maar nog niets te koop heeft. Gedempt en zonder link,
   zoals een veld op slot: de waarde staat er wel, ze telt alleen even niet mee. */
.shop-chip.is-quiet {
    color: var(--stute-muted);
}

.shop-chip.is-quiet .shop-chip-logo {
    opacity: 0.45;
}

.shop-chip-logo {
    height: 1.5rem;
    width: auto;
    max-width: 5rem;
    object-fit: contain;
    display: block;
}

/* De winkel kiezen bij een nieuwe lijst: één blok per winkel, met het logo
   boven de naam. De radio is onzichtbaar maar blijft focusseerbaar; gekozen
   staat er met dezelfde markering als .shop-chip.is-current — een rand in
   inkt met een streep eronder, geen vulling. */
.shop-picks {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
    gap: 0.75rem;
}

.shop-pick {
    display: block;
    cursor: pointer;
}

.shop-pick-input {
    position: absolute;
    opacity: 0;
    width: 1px;
    height: 1px;
}

.shop-pick-body {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    gap: 0.9rem;
    height: 100%;
    min-height: 6.5rem;
    border: 1px solid var(--stute-rule);
    padding: 0.9rem 1rem;
}

.shop-pick:hover .shop-pick-body {
    border-color: var(--stute-ink);
}

.shop-pick-input:checked + .shop-pick-body {
    border-color: var(--stute-ink);
    box-shadow: inset 0 -2px 0 var(--stute-ink);
}

.shop-pick-input:focus-visible + .shop-pick-body {
    outline: 2px solid var(--stute-ink);
    outline-offset: 2px;
}

/* Het blok om een winkel aan te brengen: dezelfde maat als de winkels, maar een
   link en geen keuze. Het plusteken staat waar bij de anderen het logo staat. */
.shop-pick-add {
    color: inherit;
    text-decoration: none;
}

.shop-pick-add .shop-pick-logo {
    font-size: 1.5rem;
    line-height: 1;
}

.shop-pick-add:focus-visible .shop-pick-body {
    outline: 2px solid var(--stute-ink);
    outline-offset: 2px;
}

.shop-pick-logo {
    display: flex;
    align-items: flex-end;
    height: 2.5rem;
}

.shop-pick-logo img {
    max-height: 100%;
    max-width: 100%;
    width: auto;
    object-fit: contain;
    display: block;
}

.shop-pick-name {
    font-size: 0.8125rem;
    line-height: 1.3;
}

/* Het overzicht op /admin/lists/shops: ruimer dan de strook, want het zijn er
   een handvol en daar mag het logo groot. */
.shop-cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
    gap: 2rem 1.5rem;
}

.shop-card-logo {
    display: flex;
    align-items: flex-end;
    height: 3rem;
    margin-bottom: 0.9rem;
}

.shop-card-logo img {
    max-height: 3rem;
    max-width: 100%;
    width: auto;
    object-fit: contain;
    display: block;
}

/* Het aantal kolommen deelt altijd 24 — het aantal resultaten per pagina — zodat
   de laatste rij vol staat en er onderaan geen gat valt. Vandaar vaste stappen
   (2, 3, 4, 6) in plaats van auto-fill: dat laatste komt bij een tussenbreedte
   op 5 of 7 kolommen uit, en dan eindigt elke pagina met een halve rij.
   Acht staat er nog niet bij: binnen de 72rem van .admin-shell zou een tegel
   dan 117 pixels breed zijn, en dat is geen productfoto meer. Die stap hoort bij
   een bredere zone, en dan is het één regel erbij. */
.product-grid {
    display: grid;
    /* minmax(0, 1fr) en niet 1fr: anders duwt één lange productnaam zonder
       spaties zijn kolom breder dan de rest. */
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 2.5rem 1.5rem;
}

@media (min-width: 34rem) {
    .product-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

@media (min-width: 48rem) {
    .product-grid {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }
}

@media (min-width: 72rem) {
    .product-grid {
        grid-template-columns: repeat(6, minmax(0, 1fr));
    }
}

/* De tegel is zelf een kolom en vult de hoogte van zijn rij. Zonder dat staat
   de knop onderaan een tegel met een naam van één regel hoger dan die ernaast
   met een naam van twee, en zigzagt een raster van vierentwintig knoppen. */
.product {
    display: flex;
    flex-direction: column;
    height: 100%;
}

/* Het kader is vierkant en overal even groot: dat is de consistentie die uit de
   layout moet komen, want winkels leveren zelf hun foto's aan (7.). */
.product-frame {
    aspect-ratio: 1 / 1;
    border: 1px solid var(--stute-rule);
    overflow: hidden;
    background: var(--stute-paper);
}

/* 'contain' en niet 'cover': de foto past volledig in het kader in plaats van
   het te vullen. Met 'cover' verdween er bij een liggende productfoto zo'n derde
   van de breedte en bij een staande bijna de helft van de hoogte — precies de
   randen waar bij een product de steel, het deksel of het tweede exemplaar
   staat. Wat overblijft is wit, en dat valt niet op: de meeste productfoto's
   staan zelf al op wit. */
.product-photo {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
}

.product-name {
    margin: 0.75rem 0 0;
    font-family: var(--stute-sans);
    font-size: 0.875rem;
    font-weight: 400;
    line-height: 1.4;
}

/* Flex, zodat het teken erachter (.product-mark) met zijn midden op dat van de
   prijs staat. Met vertical-align en een vast getal klopt dat maar bij één
   lettergrootte en één hoogte van het vakje. */
.product-price {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin: 0.25rem 0 0;
    font-size: 0.8125rem;
    color: var(--stute-muted);
}

/* Wat de winkel bovenop de verkoop geeft (3.4). Het vakje staat achter de prijs,
   want het gaat over dat bedrag: bij % ligt het lager dan in de winkel, bij *
   spaart het initiatief er punten mee.

   Geen kleur. Op deze pagina's zijn de productfoto's de kleur (7.), en een
   gekleurd vlekje bij elk product zou dat meteen ondermijnen; bovendien mag een
   teken nooit alleen aan zijn kleur te herkennen zijn. Het onderscheid zit dus in
   de vulling en in het teken zelf — dat scheelt ook in een raster van tachtig
   tegels, waar twee dunne kadertjes met een ander lettertekentje op elkaar
   zouden lijken. */
.product-mark {
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 1.125rem;
    height: 1.125rem;
    border: 1px solid var(--stute-ink);
    font-size: 0.625rem;
    line-height: 1;
    color: var(--stute-ink);
}

/* Gevuld, want dit is de sterkste van de twee: de prijs ernaast is echt lager
   dan in de winkel. */
.product-mark.is-discount {
    background: var(--stute-ink);
    color: var(--stute-paper);
}

/* Een sterretje hangt bovenaan in zijn letterblok en tekent kleiner dan zijn
   korpsgrootte belooft. Groter gezet en met padding teruggeduwd staat het even
   zwaar als het procentteken hiernaast; zonder allebei is het een stipje. */
.product-mark.is-points {
    font-size: 0.875rem;
    padding-top: 0.5em;
}

/* Een ballon bij het aanwijzen, voor wat zichzelf niet uitlegt: het teken achter
   een prijs, en het logo van een winkel — een wordmark op deze hoogte is niet te
   lezen. Een eigen ballon en geen `title`: die van de browser laat een seconde
   op zich wachten, tekent in elk besturingssysteem anders, en is bij een vakje
   van achttien bij achttien precies het soort uitleg dat niemand ooit ziet.

   Bovenaan uitgelijnd op de linkerkant: dat is de kant waar plaats is, want wat
   deze ballon draagt staat links in zijn tegel en niet aan de rechterrand. */
.has-note {
    position: relative;
    cursor: help;
}

.has-note::after {
    content: attr(data-note);
    position: absolute;
    bottom: calc(100% + 0.35rem);
    left: -0.35rem;
    z-index: 3;
    width: max-content;
    max-width: 11rem;
    padding: 0.3rem 0.5rem;
    border: 1px solid var(--stute-ink);
    background: var(--stute-paper);
    color: var(--stute-ink);
    font-size: 0.6875rem;
    line-height: 1.35;
    text-align: left;
    /* De ballon hangt aan wat er staat en erft dus de opmaak daarvan. In het
       mandje staat de winkelnaam in kleine kapitalen met veel letterafstand, en
       dan zou de ballon dat ook doen — twee regels geroep in plaats van een
       leesbare zin. Ze zet haar eigen tekst dus terug. */
    font-weight: 400;
    text-transform: none;
    letter-spacing: normal;
    opacity: 0;
    pointer-events: none;
}

/* Ook bij toetsenbordfocus, voor het geval er ooit iets aanklikbaars in zit:
   een ballon die alleen op een muis reageert, bestaat voor de helft van de
   toestellen niet. Wat de ballon zegt, staat daarom ook altijd in de alt-tekst
   of het aria-label — de ballon is de snelle weg, niet de enige. */
.has-note:hover::after,
.has-note:focus-within::after {
    opacity: 1;
}

/* Op een telefoon staan er twee tegels naast elkaar en zit het teken van de
   rechtse al dicht bij de rand; een ballon van elf rem duwt daar de pagina
   breder. Smaller mag: de zin loopt dan over drie regels in plaats van twee. */
@media (max-width: 34rem) {
    .has-note::after {
        max-width: 8rem;
    }
}

/* Het logo van een winkel draagt zijn ballon op een omhulling en niet op het
   beeld zelf: een img is een vervangen element en heeft geen ::after. */
.shop-note {
    display: inline-flex;
    align-items: center;
    max-width: 100%;
}

/* De winkel boven de naam. Kleiner en stiller dan .eyebrow: in een raster van
   vierentwintig tegels staat die letterafstand te veel te roepen. */
.product-shop {
    margin: 0.75rem 0 0;
    font-size: 0.6875rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--stute-muted);
}

.product-shop + .product-name {
    margin-top: 0.15rem;
}

/* Wat het initiatief over dit product beslist heeft: aantal, categorie, en of
   het nog te koop staat. */
.product-note {
    margin: 0.25rem 0 0;
    font-size: 0.8125rem;
    color: var(--stute-muted);
}

/* De hele tegel is de link naar het scherm van dat product; alleen de naam
   krijgt de streep, zodat het raster rustig blijft. */
.product-link {
    color: inherit;
    text-decoration: none;
}

.product-link:hover .product-name,
.product-link:focus-visible .product-name {
    text-decoration: underline;
}

/* Wat de rij afsluit — de knop, of de regel dat het er al op staat — wordt naar
   de onderkant van de tegel geduwd. De witruimte komt zo tussen de prijs en de
   knop terecht, en niet tussen de tegels onderling. */
.product-add,
.product-added {
    margin-top: auto;
    padding-top: 0.5rem;
}

/* De knop volgt de breedte van de tegel, want hij hoort bij de foto erboven en
   niet bij zijn eigen tekst. .btn-compact zet de breedte terug op auto, dus dat
   moet hier weer aan. */
.product-add .btn-ink {
    width: 100%;
}


/* De keuzelijsten naast het zoekveld. Smaller dan het veld zelf: de zoekterm is
   de hoofdvraag, de filters staan ernaast. */
.search-filter {
    flex: 0 1 12rem;
}

/* Het product zoals de winkel het levert, boven het formulier. */
.product-head {
    display: flex;
    gap: 2rem;
    align-items: flex-start;
    flex-wrap: wrap;
}

.product-head-photo {
    flex: 0 0 12rem;
    max-width: 12rem;
}

.product-head-data {
    flex: 1 1 18rem;
    margin: 0;
}

/* --- de lijst voor schenkers (5.2, 5.3) ---------------------------------- */

/* De publieke zone is op leestekst gesneden (52rem). Eén pagina gaat daar
   overheen: de lijst met haar mandje ernaast. Blijft ze 52rem, dan houdt het
   raster twee smalle kolommen over zodra het mandje verschijnt. */
.public-shell.is-wide {
    max-width: 72rem;
}

/* Het logo van het initiatief boven de naam van de lijst, als weg terug naar
   zijn eigen pagina. Klein: de kop van deze pagina is de lijst, niet het
   initiatief. Zonder logo staat de naam er als tekst, net als in de strook. */
.list-crumb {
    margin: 2.5rem 0 0;
}

.list-crumb-link {
    display: inline-block;
    color: inherit;
    font-size: 0.8125rem;
    text-decoration: none;
}

.list-crumb-logo {
    display: block;
    height: 2.25rem;
    width: auto;
    max-width: 12rem;
    object-fit: contain;
}

/* De foto van de lijst, in dezelfde verhouding als de portaalfoto's (3:2) —
   media.php bewaart ze zo, dus er wordt niet twee keer bijgesneden. */
.list-photo {
    display: block;
    width: 100%;
    aspect-ratio: 3 / 2;
    object-fit: cover;
    border: 1px solid var(--brand-line);
}

/* De voortgang: het tweede vlak dat de huisstijl mag vullen (2.). Een spoor met
   een haarlijn eromheen, zodat ook een bijna-witte merkkleur nog een balk
   blijft in plaats van te verdwijnen. */
.list-progress {
    max-width: 34rem;
}

.brand-bar {
    height: 0.5rem;
    border: 1px solid var(--brand-line);
    background: var(--stute-paper);
}

.brand-bar-fill {
    height: 100%;
    background: var(--brand-fill);
}

.list-progress-text {
    font-size: 0.8125rem;
    color: var(--stute-muted);
    margin: 0;
}

/* Het mandje staat naast de producten (5.3). Onder de 60rem past dat niet
   naast elkaar en zakt het eronder; de tegel zelf zegt dan al dat het product
   erin ligt, dus er gaat niets verloren. */
.donate-layout {
    display: grid;
    gap: 3rem;
    align-items: start;
}

@media (min-width: 60rem) {
    .donate-layout.has-cart {
        grid-template-columns: minmax(0, 1fr) 19rem;
    }
}

/* Anders dan .product-grid in de beheerzone: daar is het aantal kolommen op 24
   resultaten per pagina gesneden, hier is het aantal producten wat het is en
   telt alleen dat een tegel breed genoeg blijft voor een productfoto. Negen rem
   is de ondergrens waarbij een telefoon er nog twee naast elkaar zet: bij één
   kolom wordt elke foto zo groot dat er niets meer te vergelijken valt, en
   kiezen is precies wat een schenker hier doet. */
.donate-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
    gap: 2.5rem 1.5rem;
}

/* Het logo van de winkel bij elk product (2.): één vaste hoogte, op wit, zonder
   kader — naast de productfoto wordt een kader een tweede lijstje. De naam
   staat er niet naast, want de meeste logo's zijn wordmarks; hij zit in de alt.
   De vaste hoogte van het vak houdt de namen op één lijn, ook bij een winkel
   zonder logo. */
.donate-shop {
    display: flex;
    align-items: center;
    min-height: 1.5rem;
    margin: 0.75rem 0 0;
}

.donate-shop-logo {
    display: block;
    height: 1.5rem;
    width: auto;
    max-width: 6rem;
    object-fit: contain;
}

.donate-shop-name {
    font-size: 0.6875rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--stute-muted);
}

.donate-shop + .product-name {
    margin-top: 0.35rem;
}

/* Wat het initiatief zelf over dit product schrijft. Kleiner dan de naam: het
   is toelichting bij de keuze, geen productbeschrijving. */
.donate-text {
    font-size: 0.8125rem;
    color: var(--stute-muted);
}

/* De knop bij een product staat in een tegel van amper elf rem breed. Op de
   maat van .btn-ink zou "In het mandje" er in twee regels in staan; iets kleiner
   en met minder letterafstand past hij, en blijft hij dezelfde knop. */
.donate-item .btn-ink {
    font-size: 0.6875rem;
    letter-spacing: 0.12em;
    padding: 0.8rem 0.75rem;
}

/* De bedragen die wél kunnen, onder een geweigerde ingave. Het bedrag is de
   knop en staat vooraan, want dat is waar iemand naar zoekt; het woord erachter
   zegt welke grens het is. Geen opsommingstekens: het zijn drie antwoorden op
   dezelfde vraag en geen lijst om te lezen. */
.donate-choices {
    list-style: none;
    margin: 0.5rem 0 0;
    padding: 0;
    font-size: 0.75rem;
    line-height: 1.5;
}

.donate-choices li {
    display: flex;
    align-items: baseline;
    gap: 0.4rem;
}

.donate-choices li + li {
    margin-top: 0.25rem;
}

.donate-choices span {
    color: var(--stute-muted);
}

/* Wat er in het mandje ligt, komt op de plaats van de knop die er niet meer
   staat: zo blijven de tegels van een rij onderaan uitlijnen. */
.donate-in-cart {
    margin: auto 0 0;
    padding-top: 0.5rem;
    font-size: 0.6875rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

/* Al geschonken, of door de winkel niet meer geleverd. Het product blijft op de
   lijst staan (5.2) maar valt stil: de foto gaat naar grijs en de tekst wordt
   gedempt. Op een pagina waar productfoto's de enige kleur zijn (7.), is kleur
   weghalen het duidelijkste signaal dat er is — en het werkt ook voor wie de
   regel eronder niet leest. */
.donate-item.is-gone .product-photo,
.donate-item.is-gone .donate-shop-logo,
.product-link.is-gone .product-photo {
    filter: grayscale(1);
    opacity: 0.45;
}

.donate-item.is-gone .product-frame {
    border-color: var(--stute-rule);
}

.donate-item.is-gone .product-name,
.donate-item.is-gone .product-price {
    color: var(--stute-muted);
}

/* De regel die zegt waarom er niets te doen valt, komt op de plaats van de knop
   die er niet staat: zo blijven de tegels van een rij even hoog uitlijnen. */
.donate-item.is-gone .product-note:last-child {
    margin-top: auto;
    padding-top: 0.5rem;
}

/* --- het mandje ---------------------------------------------------------- */

/* Een kader in de kleur van het initiatief, zoals de andere lijnen op deze
   pagina. Het loopt mee terwijl je door de producten scrollt: het mandje is
   waar je naartoe wil zodra je klaar bent, en dat hoort niet bovenaan achter te
   blijven. */
.donate-cart-inner {
    border: 1px solid var(--brand-line);
    padding: 1.5rem;
}

@media (min-width: 60rem) {
    .donate-cart-inner {
        position: sticky;
        top: 1.5rem;
    }
}

.cart-shop-name {
    display: flex;
    align-items: center;
    min-height: 1.5rem;
    margin: 0 0 0.75rem;
    font-size: 0.6875rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--stute-muted);
}

.cart-item + .cart-item {
    margin-top: 1rem;
}

/* De naam met het kruisje ervoor. Uitgelijnd op de basislijn en niet op de
   bovenkant: dan staat het kruisje op de eerste regel van de naam, ook wanneer
   die over drie regels loopt, en zonder een getal dat bij een andere
   lettergrootte weer scheef staat. */
.cart-line-name {
    display: flex;
    align-items: baseline;
    gap: 0.4rem;
    margin: 0 0 0.35rem;
    font-size: 0.8125rem;
    line-height: 1.35;
}

.cart-line-name form {
    margin: 0;
}

/* Het bedrag staat altijd rechts, met of zonder de min- en plusknop ervoor: een
   deelbedrag heeft geen aantal, en zonder deze regel zou het bedrag daar naar
   links springen. */
.cart-line-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.cart-line-row form {
    margin: 0;
}

.cart-steps {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

/* Vierkante knopjes met een haarlijn, in inkt: min, plus en weg. Ze horen bij
   het mandje en niet bij de huisstijl — een gevuld vlak per knopje zou van het
   mandje een bedieningspaneel maken. */
.cart-step {
    width: 1.5rem;
    height: 1.5rem;
    padding: 0;
    border: 1px solid var(--stute-rule);
    background: none;
    color: inherit;
    font: inherit;
    line-height: 1;
    cursor: pointer;
}

.cart-step:hover:not(:disabled) {
    border-color: var(--stute-ink);
}

.cart-step:disabled {
    opacity: 0.35;
    cursor: default;
}

/* Wat een deelbedrag is, op de plaats waar bij een gewoon product het aantal
   staat. Zonder dat leest "€ 10,00" naast een voetenzak van € 79,99 als de prijs
   van die voetenzak. Gedempt: het is een bijschrift bij het bedrag ernaast en
   geen tweede getal. */
.cart-part {
    font-size: 0.6875rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--stute-muted);
}

.cart-qty {
    min-width: 1.25rem;
    text-align: center;
    font-size: 0.8125rem;
}

/* Weghalen staat vóór de naam en niet bij de aantallen of achter het bedrag: het
   is een andere handeling dan optellen en aftrekken, en in de rechterkolom staan
   alleen bedragen — die horen onder elkaar te eindigen, tot en met het totaal.
   Zonder rand, want het is de stilste van de drie knoppen. */
.cart-drop {
    border-color: transparent;
    font-size: 1rem;
}

.cart-line-total {
    margin-left: auto;
    font-size: 0.8125rem;
    white-space: nowrap;
}

.cart-line {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
    font-size: 0.8125rem;
}

.cart-shipping {
    margin-top: 0.75rem;
    color: var(--stute-muted);
}

.cart-note {
    margin: 0.35rem 0 0;
    font-size: 0.75rem;
    color: var(--stute-muted);
}

/* Een winkel die haar minimumbedrag nog niet haalt, krijgt de lijn links — in
   dezelfde kleur en dezelfde vorm als de openstaande blokken bij de
   instellingen en de stap die aan de beurt is. Het is hetzelfde signaal: hier
   ontbreekt nog iets, en dit is waar het over gaat. In een mandje met twee
   winkels wijst de lijn meteen de juiste aan. */
.cart-shop.is-short {
    border-left: 2px solid var(--stute-alert);
    padding-left: 1rem;
}

/* Wat er nog aan ontbreekt, in inkt en niet gedempt: het is geen bijschrift bij
   een bedrag maar de reden dat de knop eronder ontbreekt. */
.cart-short {
    margin-top: 0.75rem;
    color: var(--stute-ink);
}

.cart-total {
    font-size: 0.9375rem;
}

/* De enige gevulde knop op deze pagina, in de kleur van het initiatief: de
   hoofdknop (2.). De knoppen bij de producten staan in inkt en met een omtrek —
   twintig gevulde vlakken in een raster zouden van de merkkleur behang maken. */
.donate-cart .portal-action {
    display: block;
    text-align: center;
}

/* De wettelijke gegevens van de verkoper, onder zijn logo in het mandje. Klein
   en gedempt: het is geen mededeling maar iets wat er hoort te staan en
   opzoekbaar moet zijn (14.1). De regels lopen dicht op elkaar, zodat vier
   regels per winkel het mandje niet uit elkaar trekken. */
.cart-seller {
    margin: -0.35rem 0 1rem;
    font-size: 0.6875rem;
    line-height: 1.6;
    color: var(--stute-muted);
}

/* De link naar de voorwaarden staat in dezelfde gedempte inkt als de gegevens
   erboven — hij hoort bij dat blok en niet bij de knoppen — maar houdt zijn
   lijntje, want het blijft een link. */
.cart-seller .link-plain {
    color: inherit;
}

.cart-seller .link-plain:hover {
    color: var(--stute-ink);
}

/* --- afrekenen ------------------------------------------------------------ */

/* Op een smal scherm staat het mandje bóven de velden en niet eronder. Op de
   lijst is de volgorde omgekeerd — daar zijn de producten de hoofdzaak — maar
   hier staat er een knop met een bedrag erop, en wat dat bedrag is, hoort de
   schenker gezien te hebben voor hij eraan toe is. */
@media (max-width: 59.99rem) {
    .donate-layout.is-checkout .donate-cart {
        order: -1;
    }
}

/* Het factuuradres staat dicht en klapt open met het vakje ernaast. Zonder
   javascript: wie iets wil geven, hoort daar geen script voor nodig te hebben.
   Vandaar dat het vakje, het opschrift en het blok broers zijn in hetzelfde
   raster — een selector kan alleen een broer openzetten, geen kind. */
.checkout-choice {
    display: grid;
    grid-template-columns: 1rem 1fr;
    column-gap: 0.75rem;
    align-items: start;
}

.checkout-choice > .checkout-toggle {
    width: 1rem;
    height: 1rem;
    margin-top: 0.2rem;
    accent-color: var(--stute-ink);
}

.checkout-choice > label {
    font-size: 0.9375rem;
    line-height: 1.5;
    cursor: pointer;
}

.checkout-invoice {
    display: none;
    grid-column: 1 / -1;
}

.checkout-toggle:checked ~ .checkout-invoice {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
    margin-top: 1.5rem;
}

/* --- bedanken ------------------------------------------------------------- */

/* Het enige tastbare dat een schenker overhoudt (5.4). De kop mag hier groter
   staan dan elders: er staat verder bijna niets op deze pagina, en dat is de
   bedoeling. */
.thanks-title {
    font-size: clamp(2.25rem, 5vw, 3.25rem);
}

.thanks-text {
    max-width: 34rem;
    font-size: 1.0625rem;
    line-height: 1.7;
}

/* Wat er gegeven is, in dezelfde regels als het mandje ernaast stond: klein,
   met de bedragen op één rechterlijn. */
.thanks-lines {
    max-width: 26rem;
}

/* De tweede en volgende verkoper krijgen lucht boven zich: zonder die ruimte
   loopt de laatste regel van de ene winkel tegen het logo van de volgende. */
.thanks-lines .cart-shop-name:not(:first-child) {
    margin-top: 1.75rem;
}

/* Een productnaam mag over twee regels lopen, een bedrag niet: "€" op de ene
   regel en "49,99" op de volgende is geen bedrag meer. */
.thanks-lines .cart-line > span:last-child {
    padding-left: 1.5rem;
    white-space: nowrap;
}

/* --- delen ---------------------------------------------------------------- */

/* Wat er van het blok op een vreemde website terechtkomt. Het kader eromheen is
   de dunne lijn van STUTE en niet die van het initiatief: zonder kader is niet
   te zien waar het voorbeeld ophoudt en deze pagina weer begint. */
.share-preview {
    border: 1px solid var(--stute-rule);
    padding: 1.5rem;
}

/* Het stuk om over te nemen. Vaste letterbreedte, want het is code en geen
   tekst: zo blijft de inspringing staan. Lange regels schuiven opzij in plaats
   van af te breken — een afgebroken regel ziet eruit als twee regels, en dan is
   niet meer te zien wat er werkelijk staat. */
.share-source {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.8125rem;
    line-height: 1.6;
    white-space: pre;
    overflow-x: auto;
}

/* De twee tekstblokken zijn geen code maar lopende tekst: ze breken gewoon af
   en staan in de letter van de rest van het scherm. De regeleinden die er wél in
   staan, blijven staan — het zijn de witregels tussen de alinea's. */
.share-text {
    font-size: 0.875rem;
    line-height: 1.6;
    white-space: pre-wrap;
}

/* De kopieerknop wisselt even van opschrift. Hij staat op dat moment onder de
   muis, dus hij mag niet van maat veranderen: de breedte ligt vast op de
   langste van de twee woorden. */
[data-copy] {
    min-width: 11rem;
}

/* De taal waarin het deelmateriaal geschreven staat. Dezelfde vorm als de
   taalkiezer op het aanmeldscherm: het is dezelfde vraag, op een andere plaats. */
.lang-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem;
    font-size: 0.6875rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
}

.lang-tabs a {
    color: var(--stute-muted);
    text-decoration: none;
}

.lang-tabs a:hover,
.lang-tabs a[aria-current="true"] {
    color: var(--stute-ink);
}

/*
 * Noot bij de koplettertypes (--stute-display)
 *
 * Avenir is een licentieletter van Monotype die met macOS en iOS meekomt. Er
 * wordt hier niets van verspreid: de stack noemt hem alleen, en gebruikt hem
 * als de bezoeker hem zelf al heeft. Dat is toegestaan; het bestand meesturen
 * zou dat niet zijn en vraagt een aparte webfont-licentie.
 *
 * Overal elders neemt Jost het over, dat onder de Open Font License wel zelf
 * gehost mag worden. Twee families dus, met een klein verschil in tekening —
 * gelijk in gewicht, uitlijning en spatiëring, maar niet identiek van vorm.
 * Wie dat verschil helemaal weg wil, zet Jost vooraan in --stute-display en
 * haalt Avenir eruit; dan ziet iedereen hetzelfde.
 */

/* --- bestellingen -------------------------------------------------------- */

/* De drie standen waarop een winkel haar bestellingen filtert. Dezelfde
   markering als het actieve item in het menu — kleine kapitalen, de huidige in
   inkt met een lijn eronder — en niet drie knoppen: filteren is navigeren en
   geen handeling. */
.order-filter {
    display: flex;
    flex-wrap: wrap;
    gap: 0.9rem 1.75rem;
}

.order-filter a {
    font-size: 0.6875rem;
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color: var(--stute-muted);
    text-decoration: none;
    padding-bottom: 0.15rem;
    border-bottom: 1px solid transparent;
}

.order-filter a:hover {
    color: var(--stute-ink);
}

.order-filter a[aria-current="page"] {
    color: var(--stute-ink);
    border-bottom-color: var(--stute-ink);
}

/* Eén regel van een bestelling: wat het is links, hoeveel er van moet rechts.
   Niet in een data-list, want daar is de linkerkolom een label in kleine
   kapitalen — hier staat er een productnaam, en die hoort gewoon leesbaar. */
.order-line {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
}

.order-line + .order-line {
    margin-top: 0.6rem;
}

/* Boven een deel, bij de winkel: te leveren of op de bon. Kleine kapitalen
   zoals een label, maar in de tekstkleur en niet gedempt — het is wat ze moet
   weten voor ze iets inpakt. */
.order-line-label {
    display: block;
    font-size: 0.6875rem;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    margin-bottom: 0.15rem;
}

/* De artikelreferentie naast de naam: de winkel zoekt er haar product mee terug,
   dus ze hoort erbij te staan, maar ze is niet wat je leest. */
.order-line-ref {
    color: var(--stute-muted);
    font-size: 0.8125rem;
}

/* Wat de winkel erbij gaf of wat er per stuk betaald werd, onder de naam van
   het product. Klein en gedempt: het verklaart het bedrag rechts, het is niet
   wat je eerst leest. */
.order-line-note {
    display: block;
    color: var(--stute-muted);
    font-size: 0.8125rem;
    margin-top: 0.15rem;
}

.order-line-amount {
    white-space: nowrap;
}

/* Een som: een lijn erboven, geen vet en geen kader. De laatste som van een
   opbouw — wat er echt overblijft — krijgt het gewicht van een kop. */
.order-line-sum {
    border-top: 1px solid var(--stute-rule);
    padding-top: 0.6rem;
}

.order-line-total {
    font-weight: 600;
    border-top-color: var(--stute-ink);
}

/* --- punten -------------------------------------------------------------- */

/* Het saldo van een initiatief: het getal waar dit scherm om draait, dus groot
   en in de titelletter — geen kader, geen kleur. */
.points-balance {
    font-family: var(--stute-display);
    font-weight: 600;
    font-size: 1.75rem;
    letter-spacing: 0.04em;
    margin: 0.5rem 0 0;
}

/* De knop "Connect with Mollie": het beeldmerk in een zwarte bol links van de
   tekst, in dezelfde omtrek als elke andere knop. */
.connect-button {
    display: inline-flex;
    align-items: center;
    gap: 0.75rem;
    padding-left: 0.4rem;
}

.connect-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 2rem;
    height: 2rem;
    border-radius: 50%;
    background: var(--stute-ink);
}

.connect-badge img {
    display: block;
    width: 1.25rem;
    height: auto;
}
