/* ============================================================
   Design tokens
   ============================================================ */
:root {
    /* Brand green: bright fill vs. AA-safe text.
       #71b002 as a *fill* (with dark ink text) passes AA; as *text* on a light
       background it fails (2.65:1), so links/labels use the deepened --color-primary-ink. */
    --color-primary:        #71b002;   /* fills, accent rules, marks (with dark text) */
    --color-primary-hover:  #5e9600;   /* primary fill hover */
    --color-primary-ink:    #3f6a0f;   /* green as TEXT / links — AA on BOTH light grounds
                                          (5.6:1 on --color-bg, 5.1:1 on --color-bg-warm) */
    --color-secondary:      #865628;   /* warm brown */
    --color-text:           #4a4845;   /* body copy */
    /* Deepened from #6f6a60 on 2026-08-11. The old value was recorded in design.md as a bare
       "5.0:1" with no ground named — the only entry on that line missing its grounds, which is
       precisely why nobody could check it. Measured properly it was 5.07:1 on --color-bg but
       4.52:1 on --color-bg-warm: passing, by two hundredths, which the margin rule calls a
       finding rather than a pass. This value clears 5.20 / 5.83 / 6.18 across warm, bg and
       white while staying visibly lighter than --color-text (1.47:1 apart) so the type
       hierarchy still reads. Deepening is one-directional here — all 53 consumers sit on
       white, --color-bg or --color-bg-warm; the dark footer and admin sidebar deliberately
       use their own greys, so nothing gets worse. */
    --color-muted:          #646159;   /* hints, captions — AA on all three light grounds */
    --color-heading:        #1e1c1a;   /* headings, primary-button text */
    --color-bg:             #faf8f4;   /* page background (warm off-white) */
    --color-bg-warm:        #f0ebe1;   /* section tint */
    --color-footer-bg:      #1a1714;   /* dark footer */
    --color-line:           rgba(30, 28, 26, 0.12); /* hairline borders */

    /* Semantic (alerts, validation, status) */
    --color-ok:    #3f6a0f;  --color-ok-bg:   rgba(113, 176, 2, 0.12);
    /* Warn text is darkened to #7a4c00 (was #8a5a00) because --color-warn-bg is translucent, so a
       warn element's real contrast depends on the surface under it. On white the old pair gave
       5.2:1, but on the warm section tint (--color-bg-warm) it fell to 4.49:1 — failing WCAG AA by
       a hundredth, which is exactly where the Väntar badge sits on /bokningar and /kalender. The
       darker value clears both surfaces (6.5:1 and 5.6:1). It is not a new colour: .alert--warn
       already hardcoded it, and now there is one definition instead of two. */
    --color-warn:  #7a4c00;  --color-warn-bg: rgba(214, 158, 46, 0.16);
    --color-warn-line: rgba(214, 158, 46, 0.4);
    /* Error text is #8f2915 (was #b3341a) for the same reason as --color-warn above: --color-err-bg
       is translucent, so a red element's real contrast depends on the surface beneath it. The old
       value cleared WCAG AA on the warm section tint by 0.03 — a coin on its edge, and exactly
       where the Avbokad/Nekad badge sits on /bokningar. The darker value clears both surfaces
       comfortably. It is not a new colour: .alert--error already hardcoded it.
       There was briefly a separate --color-err-text holding this value while --color-err stayed
       bright; that split was the near-duplicate the duplication rule warns about, so there is now
       one error red for text. -bg and -line stay derived from the brighter red on purpose — they
       are tints and hairlines, not text, and darkening them would muddy the alert surfaces. */
    --color-err:   #8f2915;  --color-err-bg:  rgba(179, 52, 26, 0.09);
    --color-err-line: rgba(179, 52, 26, 0.32);
    /* Each semantic colour owns a -line token at its own alpha. The alphas differ between families
       (err .32, info .3, warn .4) and are deliberately left as they were: they are hairlines of
       three *different* hues, not the near-duplicates of one hue that the duplication rule is
       about. Tokenising them without normalising keeps this a pure refactor — the loose rgba()
       literals are gone, and nothing changes on screen. */
    --color-info:  #6f4a1f;  --color-info-bg: rgba(134, 86, 40, 0.10);
    --color-info-line: rgba(134, 86, 40, 0.3);
    /* The neutral status family (Avslutad / Besvarad). It is a token, not a literal on the
       badge, because it is a *semantic status colour* like its four siblings above and its
       value comes from a contrast measurement — so it has to be findable when the greys are
       next touched. Until 2026-08-11 the badge borrowed --color-muted, which was never picked
       for this job and failed on it: see the badge note under Colour in design.md. */
    --color-neutral: #5d5b56;  --color-neutral-bg: rgba(30, 28, 26, 0.07);
    /* The "På plats" family. Deliberately darker than --color-ok rather than reusing it: this
       tint is 0.22 where the ok tint is 0.12, and --color-ok #3f6a0f over it measures only 4.54
       on a hovered row — a pass by four hundredths. This is NOT the near-duplicate the
       duplication rule forbids; it is a different colour for a measurably different ground. */
    --color-active: #2c4708;   --color-active-bg: rgba(113, 176, 2, 0.22);

    /* Admin shell. Declared here rather than in a second `:root` beside the admin section,
       which is where they used to live — a palette split across two blocks is one nobody can
       audit, because editing the colours means finding a declaration you have no reason to
       know exists. Every token in this file is now in this one block. */
    --admin-nav-bg: #26221d;
    --admin-nav-fg: rgba(255, 255, 255, 0.72);

    --font-body:    'Roboto', sans-serif;
    --font-heading: 'Playfair Display', Georgia, serif;

    /* Shape & motion */
    --radius-sm:    2px;   /* buttons, inputs — editorial = sharp */
    --radius-card:  3px;
    --radius-modal: 6px;
    --transition:   160ms ease;

    --max-width:  1200px;

    /* Sticky-bar heights. These are *fallbacks*, not the truth: `js/sticky-offsets.js`
       overwrites each one with the bar's real measured height. They are in `rem` (not the
       `px` they used to be) so that a page is still close to correct before that script
       runs, and on any public page that does not load it. Never read a bar's height from
       here in new code — offset by the custom property and let the script keep it honest.

       Two properties, not one: the public `.site-header` and the admin `.admin-topbar` are
       different elements with different heights on different hosts. They were a single
       `--header-h: 72px` until 2026-08-19, which meant `.detail-rail` positioned itself
       below the *admin* topbar using a number measured off the *public* header. That worked
       only because 72px and the topbar's ~70.6px happen to be close. */
    --header-h:       4.5rem;   /* .site-header: 72px at a default root */
    --admin-topbar-h: 4.4rem;   /* .admin-topbar: 1.95rem padding + a 1.5rem/1.6 line box + 1px border */

    /* The header's own minimum height — a design floor, and deliberately a *separate*
       property from the measured `--header-h` above. Feeding a measured height back into the
       min-height of the element it was measured from is a ratchet: min-height rises to the
       bar's tallest-ever state and can never come back down (widen the window at a large text
       size, then narrow it, and the bar stays tall). Never written by script. */
    --header-min-h:   4.5rem;
}

/* Honour reduced-motion for all transitions/animations below. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
    /* !important so this wins over the later base `html { scroll-behavior: smooth }` (equal
       specificity, defined afterwards) — otherwise reduced-motion users still get smooth scroll. */
    html { scroll-behavior: auto !important; }
}

/* ============================================================
   Self-hosted fonts — files must be present in /fonts/
   Download Roboto (400, 700) and Playfair Display (400, 700)
   from fonts.google.com and place .woff2 files here.
   ============================================================ */
@font-face {
    font-family: 'Roboto';
    src: url('../fonts/roboto-regular.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}
@font-face {
    font-family: 'Roboto';
    src: url('../fonts/roboto-bold.woff2') format('woff2');
    font-weight: 700;
    font-style: normal;
    font-display: swap;
}
@font-face {
    font-family: 'Playfair Display';
    src: url('../fonts/playfair-display-regular.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}
@font-face {
    font-family: 'Playfair Display';
    src: url('../fonts/playfair-display-bold.woff2') format('woff2');
    font-weight: 700;
    font-style: normal;
    font-display: swap;
}

/* ============================================================
   Reset & base
   ============================================================ */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* `100%`, never an absolute size: an absolute root font size overrides the reader's own
   browser text-size setting, and there is nothing they can do about it (WCAG 2.1 SC 1.4.4
   "Resize text"). 100% resolves to whatever they chose — 16px for almost everyone, so this
   changes nothing for a default visitor. Anything sized by or sized to fit type must then be
   in `rem` so it follows; see `design.md`, "the reader's text size is an input". */
html { font-size: 100%; scroll-behavior: smooth; }

body {
    font-family: var(--font-body);
    color: var(--color-text);
    background-color: var(--color-bg);
    line-height: 1.6;
}

/* Headings are set large, and Swedish headings are routinely a single compound — the
   /integritetspolicy <h1> is the one word "Integritetspolicy", which is 467px at a 32px root and
   simply cannot fit a 360px screen. `break-word` only ever acts on a word that has nowhere to go,
   so ordinary headings are untouched. */
h1, h2, h3, h4 { overflow-wrap: break-word; }

/* Buttons must never be wider than what holds them. A .btn is `inline-flex`, so it is sized by
   its label; at a large text size "Skicka förfrågan" made a 358px button inside a 296px column. */
.btn, .btn-outline, .btn-ghost { max-width: 100%; }

h1, h2, h3, h4, h5, h6 {
    font-family: var(--font-heading);
    color: var(--color-heading);
    line-height: 1.2;
}

/* Link reset via :where() (zero specificity) so component classes (.btn, .nav, footer,
   calendar cells) always win their own colour without a specificity fight. Green as *text*
   uses the AA-safe --color-primary-ink, not the bright fill green. */
:where(a) { color: var(--color-primary-ink); text-decoration: none; }
:where(a:hover) { color: var(--color-primary-hover); }

img { max-width: 100%; height: auto; display: block; }

/* ============================================================
   Layout utilities
   ============================================================ */
.container {
    width: 100%;
    max-width: var(--max-width);
    margin: 0 auto;
    padding: 0 1rem;
}

@media (min-width: 480px) {
    .container { padding: 0 1.5rem; }
}

@media (min-width: 1024px) {
    .container { padding: 0 2rem; }
}

.section {
    padding: 3rem 0;
}

@media (min-width: 1024px) {
    .section { padding: 4.5rem 0; }
}

/* ============================================================
   Buttons
   ============================================================ */
.btn, .btn-outline, .btn-ghost {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.8rem 1.6rem;
    border-radius: var(--radius-sm);
    font-family: var(--font-body);
    font-weight: 700;
    font-size: 0.95rem;
    cursor: pointer;
    transition: background-color var(--transition), color var(--transition), border-color var(--transition);
    border: 1.5px solid transparent;
    text-align: center;
}

/* Primary: bright brand-green FILL with dark ink text (6.5:1 — AA). White-on-green fails
   contrast and must never be used. */
.btn {
    background-color: var(--color-primary);
    color: var(--color-heading);
    border-color: var(--color-primary);
}
.btn:hover {
    background-color: var(--color-primary-hover);
    border-color: var(--color-primary-hover);
    color: var(--color-heading);
}
.btn:disabled, .btn[aria-disabled="true"] {
    opacity: 0.4;
    cursor: not-allowed;
}

.btn-outline {
    background-color: transparent;
    color: var(--color-heading);
    border-color: var(--color-heading);
}
.btn-outline:hover {
    background-color: var(--color-heading);
    color: #fff;
}

/* Ghost: plain text button in AA-safe green. (The hero/CTA underline treatment is scoped
   to those sections so admin/calendar ghost buttons stay plain.) */
.btn-ghost {
    background-color: transparent;
    color: var(--color-primary-ink);
    border-color: transparent;
}
.btn-ghost:hover {
    color: var(--color-primary-hover);
}

/* Visible keyboard focus on all interactive elements (NFR-38). */
a:focus-visible, button:focus-visible,
.btn:focus-visible, .btn-outline:focus-visible, .btn-ghost:focus-visible,
input:focus-visible, select:focus-visible, textarea:focus-visible {
    outline: 2px solid var(--color-primary-ink);
    outline-offset: 3px;
}

/* FocusOnNavigate (Routes.razor) moves focus to the page <h1> after each navigation — good for
   screen-reader/keyboard users. But it makes the heading programmatically focusable (tabindex=-1),
   which otherwise paints a focus ring (a "box" around the title on load). A keyboard user cannot
   tab to a tabindex=-1 element, so suppressing its ring is safe and removes the visual artefact. */
[tabindex="-1"]:focus { outline: none; }

/* ============================================================
   Header — solid sticky bar (direction A "Editorial lugn")
   Always solid on the off-white ground; the dark-stroke logo is
   always shown, so the old transparent/scroll/logo-swap logic is
   retired. Mobile nav is a CSS-only checkbox toggle (no JS — works
   under Static SSR).
   ============================================================ */
.site-header {
    position: sticky;
    top: 0;
    z-index: 100;
    background-color: var(--color-bg);
    border-bottom: 1px solid var(--color-line);
}

.site-header .container {
    display: flex;
    align-items: center;
    justify-content: space-between;
    /* `min-height`, never `height`: the bar's contents are type, and type grows when the
       reader raises their text size or when someone lengthens the CTA label or adds a nav
       item. Pinned to a fixed height the CTA button simply spilled out of the bar (measured
       at a 24px root, 2026-08-19). The constant is now only a floor. */
    min-height: var(--header-min-h);
}

.site-header__logo { display: flex; align-items: center; }
.site-header__logo .logo { height: 55px; width: auto; }

/* The toggle checkbox is kept off-screen but operable (keyboard-focusable). */
.site-header__toggle {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

.site-header__burger {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 5px;
    width: 44px;
    height: 44px;
    padding: 10px;
    cursor: pointer;
}

.site-header__burger span {
    display: block;
    height: 2px;
    background-color: var(--color-heading);
    border-radius: 2px;
}

.site-header__toggle:focus-visible ~ .site-header__burger {
    outline: 2px solid var(--color-primary-ink);
    outline-offset: 2px;
}

/* Collapsed by default; opens as a dropdown panel below the sticky header. */
.site-header__nav {
    display: none;
    position: absolute;
    /* Resolved against `.site-header` (the nearest positioned ancestor — it is `sticky`;
       `.container` in between is static), so `100%` *is* the header's rendered height. No
       constant to keep in sync, and it stays exact when the bar grows. */
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    gap: 0;
    padding: 0.5rem 1.5rem 1.25rem;
    background-color: var(--color-bg);
    border-bottom: 1px solid var(--color-line);
    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.06);
}

.site-header__toggle:checked ~ .site-header__nav {
    display: flex;
}

/* Text nav links (the CTA is a .btn and is excluded so it keeps its button styles). */
.site-header__nav a:not(.site-header__cta) {
    color: var(--color-heading);
    /* 400/700 only — Roboto is self-hosted at those two weights, so 500 was being
       synthesized by the browser (faux bold). See design.md "Fonts". */
    font-weight: 400;
    padding: 0.75rem 0;
    border-bottom: 1px solid var(--color-line);
}
.site-header__nav a:not(.site-header__cta):hover { color: var(--color-primary-ink); }

/* Current page in the mobile panel: green inset bar + tint, mirroring the admin sidebar.
   Colour alone is not a sufficient state indicator (WCAG 1.4.1) — desktop gets an underline
   via ::after below, but that rule is inside the 1024px query and never applied here. */
.site-header__nav a.active {
    color: var(--color-primary-ink);
    font-weight: 700;
    box-shadow: inset 3px 0 0 var(--color-primary);
    background-color: rgba(113, 176, 2, 0.07);
    padding-left: 0.75rem;
    margin-left: -0.75rem;
    margin-right: -0.75rem;
    padding-right: 0.75rem;
}

.site-header__cta { margin-top: 0.85rem; }

/* --- Desktop: inline horizontal nav, no hamburger ---
   The breakpoint is `64em`, not `1024px`, and this is the one place in the file where that
   distinction matters. `em` in a media query resolves against the browser's *initial* font
   size — the reader's own text-size setting — so this asks "is the viewport wide enough
   relative to how large this reader's text is", which is the actual question: the row holds
   five nav labels plus a CTA, and those grow with the setting while a px breakpoint does not.
   At a default 16px root, 64em is 1024px and nothing changes. At a 32px root it is 2048px, so
   the hamburger is kept instead of overflowing the bar by ~101px, which is what happened while
   this was pinned to px. Note `em` here is *not* affected by `html { font-size }` — it tracks
   the browser setting only, which is exactly what makes it stable. --- */
@media (min-width: 64em) {
    .site-header__burger { display: none; }

    .site-header__nav {
        display: flex;
        position: static;
        flex-direction: row;
        gap: 1.75rem;
        align-items: center;
        padding: 0;
        background-color: transparent;
        border-bottom: none;
        box-shadow: none;
    }

    .site-header__nav a:not(.site-header__cta) {
        position: relative;
        padding: 0.25rem 0;
        border-bottom: none;
        font-weight: 400;
        /* A nav label must never break mid-phrase ("Om" / "oss"). Nothing wraps today —
           measured at 1024px, the width where this inline nav replaces the hamburger, the
           row has ~66px to spare — but the 2026-08 lockup spent half the slack that used to
           be there, so this turns a future silent squeeze into a visible overflow. */
        white-space: nowrap;
    }

    /* Drop the mobile panel's inset-bar treatment — desktop uses the underline below. */
    .site-header__nav a.active {
        font-weight: 400;
        box-shadow: none;
        background-color: transparent;
        padding-left: 0;
        padding-right: 0;
        margin-left: 0;
        margin-right: 0;
    }

    /* Current-page indicator: a thin green underline under the active link. */
    .site-header__nav a.active::after {
        content: "";
        position: absolute;
        left: 0;
        right: 0;
        bottom: -0.4rem;
        height: 2px;
        background: var(--color-primary);
    }

    .site-header__cta { margin-top: 0; margin-left: 1.25rem; }
}

/* ============================================================
   Home page (direction A "Editorial lugn")
   ============================================================ */

/* Eyebrow / kicker label — reusable */
.eyebrow {
    display: block;
    font-family: var(--font-body);
    font-weight: 700;
    font-size: 0.75rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--color-primary-ink);
    margin-bottom: 1rem;
}

/* Photo placeholder caption (real photos swapped at go-live) */
.photo-tag {
    position: absolute;
    bottom: 0.6rem;
    right: 0.75rem;
    z-index: 2;
    font-size: 0.68rem;
    letter-spacing: 0.03em;
    padding: 0.2rem 0.6rem;
    border-radius: 1rem;
    /* Near-opaque so white text passes AA even where axe composites the chip over the
       light page background (it can't read the gradient beneath). */
    background: rgba(26, 23, 20, 0.9);
    color: #fff;
}

/* --- Hero: split text + photo --- */
.hero { background-color: var(--color-bg); }

/* `minmax(0, 1fr)`, not `1fr`: a bare `1fr` has an `auto` minimum, so the single mobile track
   could not shrink below the hero heading's longest word and the whole hero sat 105px wider than
   a 360px screen at a 32px root. Paired with the heading `overflow-wrap` above, which is what
   lets that word break once the track is allowed to be narrow. */
.hero__grid { display: grid; grid-template-columns: minmax(0, 1fr); }

.hero__photo {
    position: relative;
    /* 11rem, not 15rem: measured at 375px the primary "Skicka förfrågan" CTA landed at
       640px — below the fold once real browser chrome (~100px) is accounted for, on every
       phone smaller than a modern iPhone. The booking funnel is the primary KPI, so the
       hero image yields the height rather than the call to action. Grows back at 480px+. */
    min-height: 11rem;
    order: -1; /* photo above text on mobile */
    background: linear-gradient(180deg, #cdd7d0 0%, #bcc7bd 32%, #9fae95 58%, #7e8f6a 82%, #6d7f5a 100%);
}
.hero__photo::after {
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(120% 70% at 72% 18%, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0) 55%);
}

.hero__text { padding: 3rem 1rem 2.5rem; }

.hero h1 {
    font-weight: 400;
    font-size: clamp(2.25rem, 5vw, 3.5rem);
    line-height: 1.05;
    letter-spacing: -0.015em;
    color: var(--color-heading);
    margin: 0;
}

.hero__rule { width: 3.5rem; height: 2px; background: var(--color-primary); margin: 1.5rem 0; }

.hero__sub { font-size: 1.125rem; color: var(--color-text); margin: 0 0 2rem; max-width: 44ch; }

.hero__cta { display: flex; gap: 1.25rem; flex-wrap: wrap; align-items: center; }

@media (min-width: 480px) {
    .hero__text { padding-left: 1.5rem; padding-right: 1.5rem; }
    .hero__photo { min-height: 15rem; }
}
@media (min-width: 64em) {
    .hero__grid { grid-template-columns: 1fr 1fr; align-items: stretch; }
    .hero__photo { order: 0; min-height: 34rem; }
    .hero__text { align-self: center; padding: 5rem 0; }
    /* Snap hero copy to the site container's left edge: a half-container box flush to the
       centre, with the container gutter (2rem) as inner padding — so the headline lines up
       with the header, nav and trust strip instead of poking out full-bleed. */
    .hero__text-inner {
        max-width: calc(var(--max-width) / 2);
        margin-left: auto;
        padding-left: 2rem;
        padding-right: 3rem;
    }
}

/* Secondary CTA underline treatment — scoped to hero + closing band only */
.hero__cta .btn-ghost,
.cta-band__actions .btn-ghost {
    color: var(--color-heading);
    border-bottom: 1.5px solid var(--color-heading);
    border-radius: 0;
    padding-left: 0.25rem;
    padding-right: 0.25rem;
}
.hero__cta .btn-ghost:hover,
.cta-band__actions .btn-ghost:hover {
    color: var(--color-primary-ink);
    border-color: var(--color-primary-ink);
}

/* Layout breakpoints below are in `em`, not `px`, wherever they *add a column*: `em` in a media
   query resolves against the browser's initial font size — the reader's text-size setting — so
   the question asked is "is there room for another column of this reader's text", which is the
   real one. At a default 16px root every value resolves to the pixel width it replaced
   (40em=640, 45em=720, 48em=768, 64em=1024), so nothing moves for a default visitor. Breakpoints
   that only change padding stay in `px`; they are not about whether text fits. See design.md,
   "Text scaling". --- */

/* --- Trust strip --- */
.trust-strip {
    background: var(--color-bg-warm);
    border-top: 1px solid var(--color-line);
    border-bottom: 1px solid var(--color-line);
}
/* Separators are drawn by a 1px grid gap over the strip's own background rather than by
   per-item borders picked out with :nth-child. The old border rules hard-coded a 2-column
   mobile / 4-column desktop layout, so they could not survive the strip collapsing to a
   single column (which it must: `1fr` cannot go below its longest word, and at a 24px root
   two columns needed 370px inside a 312px phone screen). The gap draws the correct hairlines
   at one, two or four columns without knowing which it is. */
.trust-strip__grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 7.5rem), 1fr));
    gap: 1px;
    background: var(--color-line);
}
.trust-strip__item { padding: 1.25rem 1rem; background: var(--color-bg-warm); }
/* Pin two columns once there is room; `em` for the same reason as .site-footer__grid. */
@media (min-width: 30em) {
    .trust-strip__grid { grid-template-columns: repeat(2, 1fr); }
}
.trust-strip__num { font-family: var(--font-heading); font-size: 1.4rem; color: var(--color-heading); line-height: 1; }
.trust-strip__label { font-size: 0.8rem; color: var(--color-text); margin-top: 0.35rem; }
@media (min-width: 64em) {
    .trust-strip__grid { grid-template-columns: repeat(4, 1fr); }
    .trust-strip__item { padding: 1.5rem 2rem; }
}

/* --- Section heading block --- */
.section--warm { background: var(--color-bg-warm); }
.section__head { max-width: 60ch; margin-bottom: 2.5rem; }
.section__head h2 {
    font-weight: 400;
    font-size: clamp(1.75rem, 3.6vw, 2.5rem);
    line-height: 1.1;
    letter-spacing: -0.01em;
    margin: 0;
}
.section__head p { font-size: 1.0625rem; color: var(--color-text); margin: 1rem 0 0; }

/* --- "Vad ni kan göra här" cards — image card (design.md's other documented card pattern:
   photo + title + body), not the flat green-rule card these used to be. Three identical
   text-only blocks gave no visual distinction between very different use cases; a photo per
   card is a stronger differentiator than a rule and a heading weight. Same shape as
   Facilities' .fac-card, kept as its own class per this codebase's per-page naming. --- */
.usecards { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.75rem; }
.usecard { border: 1px solid var(--color-line); border-radius: var(--radius-card); overflow: hidden; background: #fff; }
.usecard__img {
    aspect-ratio: 4 / 3;
    position: relative;
    background: linear-gradient(160deg, #d3d9cf, #8a9a72);
}
.usecard__img img { width: 100%; height: 100%; object-fit: cover; }
.usecard__body { padding: 1.1rem 1.25rem; }
.usecard h3 { font-weight: 700; font-size: 1.2rem; margin: 0 0 0.4rem; }
.usecard p { margin: 0; font-size: 0.95rem; color: var(--color-text); }
@media (min-width: 48em) { .usecards { grid-template-columns: repeat(3, 1fr); } }

/* --- Facilities teaser (asymmetric) --- */
/* `minmax(0, 1fr)` everywhere a single-column track holds text, not a bare `1fr`.
   A bare `1fr` is min-content-floored, and **`overflow-wrap: break-word` does not reduce
   min-content — only `anywhere` does** — so the heading rule on h1–h4 could not save a track
   that had already grown to fit the whole word. `.teaser h2` is `clamp(1.75rem, 3.4vw, …)`, and
   at a 32px root the clamp *minimum* is 56px while `3.4vw` is only 12px on a phone, so the
   minimum always wins: "Konferensrum" rendered 365px wide inside a 296px column and took the
   photo, eyebrow and heading out with it. Letting the track shrink is what lets the word break. */
.teaser { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.75rem; align-items: center; }
.teaser__photo {
    position: relative;
    min-height: 15rem;
    border-radius: var(--radius-card);
    overflow: hidden;
    background: linear-gradient(160deg, #d3d9cf 0%, #b3bfa4 45%, #8a9a72 100%);
}
.teaser__photo::after {
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(80% 60% at 30% 20%, rgba(255, 255, 255, 0.4), rgba(0, 0, 0, 0) 60%);
}
.teaser__photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.teaser h2 { font-weight: 400; font-size: clamp(1.75rem, 3.4vw, 2.375rem); line-height: 1.1; margin: 0 0 1rem; }
.teaser p { font-size: 1.0625rem; color: var(--color-text); margin: 0 0 1.5rem; max-width: 46ch; }
.link-arrow { color: var(--color-primary-ink); font-weight: 700; border-bottom: 1.5px solid transparent; }
.link-arrow:hover { border-color: var(--color-primary-ink); }
@media (min-width: 64em) {
    .teaser { grid-template-columns: 0.9fr 1.1fr; gap: 3.5rem; }
    .teaser__photo { min-height: 24rem; }
    /* Flips photo to the right, text to the left — for a second .teaser lower on the same
       page (e.g. Om oss) so it doesn't just repeat the layout of the one above it. */
    .teaser--reverse .teaser__photo { order: 2; }
}

/* --- "Så går det till" — genuine 3-step sequence (numbers carry order) --- */
.steps { display: grid; grid-template-columns: minmax(0, 1fr); gap: 2rem; }
.step__n { font-family: var(--font-heading); font-size: 0.95rem; font-weight: 700; color: var(--color-primary-ink); letter-spacing: 0.1em; }
.step h3 { font-weight: 700; font-size: 1.25rem; margin: 0.75rem 0 0.5rem; }
.step p { margin: 0; color: var(--color-text); }
@media (min-width: 48em) { .steps { grid-template-columns: repeat(3, 1fr); gap: 2.5rem; } }

/* --- Closing CTA band --- */
.cta-band {
    position: relative;
    overflow: hidden;
    text-align: center;
    color: #fff;
    background: linear-gradient(180deg, #5a6b48, #3f4d31);
}
.cta-band::before { content: ""; position: absolute; inset: 0; background: rgba(20, 24, 14, 0.42); }
.cta-band .container { position: relative; }
.cta-band h2 { color: #fff; font-weight: 400; font-size: clamp(1.875rem, 3.8vw, 2.625rem); margin: 0 0 1rem; }
.cta-band p { font-size: 1.125rem; margin: 0 auto 2rem; max-width: 48ch; color: rgba(255, 255, 255, 0.92); }
.cta-band__actions { display: flex; gap: 1.25rem; flex-wrap: wrap; align-items: center; justify-content: center; }
.cta-band__actions .btn-ghost { color: #fff; border-color: #fff; }
.cta-band__actions .btn-ghost:hover { color: #fff; border-color: #fff; opacity: 0.85; }

/* ============================================================
   Phone-only docked CTA
   Desktop keeps the header CTA in view, so this is mobile-only. Measured at 375px the hero
   CTA lands ~60px below the fold once browser chrome is counted, and the header CTA is inside
   the collapsed hamburger — leaving a first screen with no route to the funnel.
   ============================================================ */
.mobile-cta { display: none; }

@media (max-width: 640px) {
    .mobile-cta {
        display: block;
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 90;
        padding: 0.7rem 1rem calc(0.7rem + env(safe-area-inset-bottom, 0px));
        background-color: var(--color-bg);
        border-top: 1px solid var(--color-line);
        box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.08);
    }
    .mobile-cta .btn { width: 100%; }

    /* Reserve the bar's height so it never covers the end of the footer. */
    .has-mobile-cta main { padding-bottom: 1rem; }
    .has-mobile-cta .site-footer { padding-bottom: 6.5rem; }
}

/* ============================================================
   Footer
   ============================================================ */
.site-footer {
    background-color: var(--color-footer-bg);
    color: #a8a49c;
    padding: 4rem 0 2.5rem;
}

.site-footer__grid {
    display: grid;
    /* Two columns that collapse to one when they genuinely stop fitting, rather than two that
       overflow the screen. `1fr` carries an `auto` minimum, so a column can never be narrower
       than its longest unbreakable word — and Swedish compounds are long: "Integritetspolicy"
       measures 100px at a default root but 150px at a 24px one, so two columns plus the gap
       came to 397px inside a 312px phone screen and every public page grew a horizontal
       scrollbar (measured at 360px, 2026-08-19). An explicit `9rem` minimum replaces that
       `auto`, so the track count follows the reader's text size: two columns at a default
       root (identical to before), one as soon as two no longer fit. `min(100%, …)` keeps the
       track from exceeding the container on the narrowest screens. */
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 9rem), 1fr));
    gap: 2rem;
}
/* Pin two columns once there is room, so auto-fit cannot start packing in a third as the
   viewport grows. In `em`, not `px`: this *is* a "does the text fit" breakpoint, and pinning it
   at 480px re-introduced the overflow it was meant to prevent — every page ran ~97px past a
   480px screen at a 32px root, because the pin forced two columns back on at a width where
   auto-fit had correctly collapsed to one. */
@media (min-width: 30em) {
    .site-footer__grid { grid-template-columns: 1fr 1fr; }
}
@media (min-width: 64em) {
    .site-footer__grid { grid-template-columns: 1.4fr 1fr 1fr 1fr; gap: 2.5rem; }
}

.site-footer__brand { font-family: var(--font-heading); font-size: 1.25rem; color: #fff; margin: 0 0 0.85rem; letter-spacing: 0.03em; }
.site-footer p { margin: 0 0 0.4rem; font-size: 0.875rem; color: #a8a49c; }

.site-footer h2 { /* column headings */
    font-family: var(--font-body);
    font-size: 0.75rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: #8f8a80;
    font-weight: 700;
    margin: 0 0 0.85rem;
}
.site-footer ul { list-style: none; margin: 0; padding: 0; }
.site-footer ul li { margin: 0.5rem 0; }
.site-footer a { color: #cbc7bf; font-size: 0.875rem; }
.site-footer a:hover { color: #fff; }

.site-footer__bar {
    margin-top: 3rem;
    padding-top: 1.35rem;
    border-top: 1px solid rgba(255, 255, 255, 0.1);
    display: flex;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.6rem;
    font-size: 0.8125rem;
    color: #8f8a80;
}

/* ============================================================
   Forms
   ============================================================ */
.form-row { display: grid; gap: 1rem; }
/* `30em`, not `480px` — see the header's `64em` note. Two form columns only fit if the
   viewport is wide enough *for this reader's text*: at a 24px root, 480px left each field
   238px inside a 432px row and /boka overflowed by 57px. 30em is 480px at a default root
   (unchanged) and 720px at a 24px one. */
@media (min-width: 30em) { .form-row { grid-template-columns: 1fr 1fr; } }

.form-group { display: flex; flex-direction: column; gap: 0.4rem; margin-bottom: 1.05rem; }
.form-group label { font-weight: 700; font-size: 0.9rem; color: var(--color-heading); }

.form-group input:not([type="checkbox"]):not([type="radio"]),
.form-group select,
.form-group textarea {
    width: 100%;
    padding: 0.7rem 0.8rem;
    border: 1.5px solid #cfc9bd;
    border-radius: var(--radius-sm);
    font-family: var(--font-body);
    font-size: 1rem;
    color: var(--color-heading);
    background-color: #fff;
    transition: border-color var(--transition), box-shadow var(--transition);
}
.form-group input:not([type="checkbox"]):not([type="radio"]):focus,
.form-group select:focus,
.form-group textarea:focus {
    outline: none;
    border-color: var(--color-primary-ink);
    box-shadow: 0 0 0 3px rgba(63, 106, 15, 0.18);
}
.form-group textarea { min-height: 5.5rem; resize: vertical; }

/* Branded native checkboxes / radios */
.form-group input[type="checkbox"], .form-group input[type="radio"], .checkbox-row input {
    accent-color: var(--color-primary);
    width: 1.1rem;
    height: 1.1rem;
}

/* Checkbox groups (facilities / cleaning) */
fieldset.form-group { border: 0; padding: 0; }
fieldset.form-group legend { font-weight: 700; font-size: 0.9rem; color: var(--color-heading); padding: 0; margin-bottom: 0.5rem; }

/* Single-checkbox rows (acknowledgement, organisation, "kom ihåg mig").
   Inherits .form-group's column direction — it must NOT be a row. The row is *inside* the label
   (checkbox + text, rule below); this container stacks the label and its validation message. It was
   `flex-direction: row` until 2026-08-17, which put the message *beside* the label and split the
   available width three ways: at 360px the FR-109 label rendered "Jag godkänner" as a two-line
   column with the error as a third, clipped column. It only ever looked acceptable because the
   label was short and the message almost never appeared. */
.form-group--checkbox { align-items: flex-start; }
.form-group--checkbox label { font-weight: 400; color: var(--color-text); font-size: 0.95rem; display: flex; gap: 0.6rem; align-items: flex-start; }

/* Validation */
.validation-message, .form-error { color: var(--color-err); font-size: 0.85rem; font-weight: 600; }
.form-error { margin-bottom: 1rem; }

/* Grouped form sections (inquiry) */
.form-section { padding: 1.6rem 0; border-top: 1px solid var(--color-line); }
.form-section:first-of-type { padding-top: 0; border-top: 0; }
.form-section > h2 { font-family: var(--font-heading); font-weight: 700; font-size: 1.15rem; color: var(--color-heading); margin: 0 0 0.9rem; }
.form-section .sec-note { font-size: 0.85rem; color: var(--color-muted); margin: -0.4rem 0 0.9rem; }
.field-opt { color: var(--color-muted); font-weight: 400; }
.form-hint { font-size: 0.8rem; color: var(--color-muted); margin: 0.35rem 0 0; }

/* "Från kalender" prefill chip */
.prefill { font-size: 0.66rem; font-weight: 700; letter-spacing: 0.03em; text-transform: uppercase; color: var(--color-primary-ink); background: rgba(113, 176, 2, 0.14); padding: 0.12rem 0.5rem; border-radius: 1rem; margin-left: 0.4rem; vertical-align: middle; }

/* Segmented two-track toggle (CSS-only, SSR-safe) */
.track-radio { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
/* wrap + max-width: the two segment labels are unshrinkable flex items, so at a large text size
   the control ran past its column on /boka. */
.segmented { display: inline-flex; flex-wrap: wrap; max-width: 100%; border: 1.5px solid var(--color-line); border-radius: 4px; overflow: hidden; background: #fff; margin-bottom: 1.75rem; }
.segmented__btn { font-weight: 700; font-size: 0.9rem; padding: 0.7rem 1.4rem; color: var(--color-text); cursor: pointer; user-select: none; }
.segmented__btn + .segmented__btn { border-left: 1.5px solid var(--color-line); }
#track-stay:checked ~ .segmented .segmented__btn[for="track-stay"],
#track-general:checked ~ .segmented .segmented__btn[for="track-general"] { background: var(--color-primary); color: var(--color-heading); }
.track-radio:focus-visible ~ .segmented { outline: 2px solid var(--color-primary-ink); outline-offset: 2px; }
.track { display: none; }
#track-stay:checked ~ .track--stay { display: block; }
#track-general:checked ~ .track--general { display: block; }

/* Progressive-disclosure panel (organisation fields) */
.reveal-panel { background: var(--color-bg-warm); border: 1px solid var(--color-line); border-radius: var(--radius-card); padding: 1.1rem 1.25rem; margin-top: 0.75rem; }
.reveal-panel .form-group:last-child { margin-bottom: 0; }

/* Reassurance banner + submit row */
.reassure { margin-bottom: 1.6rem; }
.submit-row { margin-top: 1.75rem; display: flex; gap: 1.1rem; align-items: center; flex-wrap: wrap; }

/* Acknowledgement (Tack) */
.tack { text-align: center; padding: 3.5rem 0; max-width: 40rem; margin: 0 auto; }
.tack__badge { width: 4rem; height: 4rem; border-radius: 50%; background: rgba(113, 176, 2, 0.16); color: var(--color-primary-ink); display: inline-flex; align-items: center; justify-content: center; font-size: 1.9rem; margin-bottom: 0.5rem; }
.tack h1 { font-weight: 400; font-size: clamp(1.8rem, 4vw, 2.2rem); margin: 0.75rem 0; }
.tack p { color: var(--color-text); font-size: 1.0625rem; margin: 0 0 1.6rem; }

/* ============================================================
   Guest completion form (/slutfor) — Phase 6 slice 5
   ============================================================ */
/* Reusable compact input (completion meal selects live outside .form-group) */
.inp {
    width: 100%;
    padding: 0.6rem 0.7rem;
    border: 1.5px solid #cfc9bd;
    border-radius: var(--radius-sm);
    font-family: var(--font-body);
    font-size: 0.95rem;
    color: var(--color-heading);
    background: #fff;
    transition: border-color var(--transition), box-shadow var(--transition);
}
.inp:focus { outline: none; border-color: var(--color-primary-ink); box-shadow: 0 0 0 3px rgba(63, 106, 15, 0.18); }
select.inp {
    appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%234a4845' stroke-width='1.6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 0.6rem center;
    padding-right: 1.9rem;
}

/* Booking summary strip */
.booking-summary {
    display: flex;
    flex-wrap: wrap;
    gap: 0;
    background: #fff;
    border: 1px solid var(--color-line);
    border-radius: var(--radius-card);
    overflow: hidden;
    margin: 1.4rem 0 0.5rem;
}
.booking-summary > div { padding: 0.9rem 1.25rem; flex: 1; min-width: 9rem; }
.booking-summary > div + div { border-left: 1px solid var(--color-line); }
.booking-summary dt { font-size: 0.7rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--color-muted); font-weight: 700; margin: 0; }
.booking-summary dd { margin: 0.25rem 0 0; font-family: var(--font-heading); font-size: 1.15rem; color: var(--color-heading); }

/* Live progress (hidden until JS reveals it) */
.completion-progress { display: flex; align-items: center; gap: 0.65rem; font-size: 0.85rem; color: var(--color-muted); margin: 1.5rem 0 0.5rem; }
.completion-progress[hidden] { display: none; }
.completion-progress .bar { flex: 1; height: 6px; border-radius: 3px; background: var(--color-bg-warm); overflow: hidden; }
.completion-progress .bar i { display: block; height: 100%; width: 0; background: var(--color-primary); transition: width var(--transition); }

/* Participant / day-guest card */
.pcard { border: 1px solid var(--color-line); border-radius: var(--radius-card); margin: 0 0 1.15rem; overflow: hidden; background: #fff; }
.pcard__head { display: flex; align-items: center; gap: 0.75rem; padding: 0.85rem 1.25rem; background: var(--color-bg-warm); border-bottom: 1px solid var(--color-line); }
.pcard__badge { width: 1.9rem; height: 1.9rem; border-radius: 50%; background: var(--color-primary); color: var(--color-heading); font-family: var(--font-body); font-weight: 700; font-size: 0.85rem; display: inline-flex; align-items: center; justify-content: center; flex: none; }
.pcard__badge--day { background: var(--color-secondary); color: #fff; font-size: 0.72rem; }
.pcard__title { font-family: var(--font-heading); font-weight: 700; font-size: 1.05rem; color: var(--color-heading); }
.pcard__body { padding: 1.25rem; }
.pcard__actions { display: flex; justify-content: flex-end; padding: 0 1.25rem 1rem; }

/* Full-board / organisation toggle row */
.fbrow { display: flex; align-items: center; gap: 0.65rem; padding: 0.75rem 0.9rem; background: var(--color-bg); border: 1px solid var(--color-line); border-radius: var(--radius-card); margin: 0.25rem 0 1rem; }
.fbrow input { accent-color: var(--color-primary); width: 1.15rem; height: 1.15rem; flex: none; }
.fbrow label { font-weight: 700; font-size: 0.92rem; color: var(--color-heading); margin: 0; }

/* Meals block */
.field-label { display: block; font-weight: 700; font-size: 0.85rem; color: var(--color-heading); margin-bottom: 0.4rem; }
/* `.meals__title`, `.meals__same`, `.meals__same-grid` and `.same-hint` were removed in the
   2026-08-11 audit: the 2026-08 group-meals rework re-parented meal selection from the
   participant to the booking and rebuilt this markup, leaving those rules with no element to
   match. `.day__meals` shared two of those selectors and is still live, so it keeps them. */
.meals { border: 1px solid var(--color-line); border-radius: var(--radius-card); overflow: hidden; margin: 0.4rem 0 1rem; }
.day__meals { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0.75rem; }
/* `30em` for the same reason as .form-row above. Not directly measured — /slutfor needs a
   live completion token — but it is the identical two-column-of-labelled-controls shape. */
@media (min-width: 30em) { .day__meals { grid-template-columns: 1fr 1fr; } }
.day__meals label { display: block; font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--color-muted); margin-bottom: 0.3rem; }
.day { padding: 0.85rem 1rem; border-bottom: 1px solid var(--color-line); }
.day:last-child { border-bottom: 0; }
.day__date { font-size: 0.72rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--color-primary-ink); margin: 0 0 0.65rem; }

/* Dietary chips (:has drives the checked look — no JS) */
.diet { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.diet label { display: inline-flex; align-items: center; gap: 0.45rem; border: 1.5px solid var(--color-line); border-radius: 20px; padding: 0.35rem 0.8rem; font-size: 0.85rem; color: var(--color-text); cursor: pointer; }
.diet label:has(input:checked) { border-color: var(--color-primary); background: rgba(113, 176, 2, 0.08); color: var(--color-heading); }
.diet input { accent-color: var(--color-primary); }

/* Submit bar (kept in flow at the end — a sticky bar would float over a long form's fields) */
.completion-submit { border-top: 1px solid var(--color-line); padding: 1.25rem 0 0; margin-top: 1.5rem; display: flex; gap: 1rem; align-items: center; flex-wrap: wrap; }
.completion-submit .hint { font-size: 0.8rem; color: var(--color-muted); }

/* Access-state badge (reuses .utility from the trust pages) */
.utility__badge { width: 3.75rem; height: 3.75rem; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-size: 1.75rem; margin-bottom: 0.4rem; }
.utility__badge--ok { background: rgba(113, 176, 2, 0.16); color: var(--color-primary-ink); }
.utility__badge--warn { background: rgba(214, 158, 46, 0.18); color: #6f4900; }

/* ---- Completion at scale: group defaults + collapsible participant rows ---- */

/* "Standard för hela gruppen" panel */
.group-default { background: rgba(113, 176, 2, 0.09); border: 1.5px solid rgba(113, 176, 2, 0.4); border-radius: var(--radius-card); padding: 1.1rem 1.25rem; margin: 0.5rem 0 1.5rem; }
.group-default h3 { font-family: var(--font-heading); font-weight: 700; font-size: 1.05rem; margin: 0 0 0.25rem; color: var(--color-heading); }
.group-default > p { font-size: 0.8rem; color: var(--color-muted); margin: 0 0 0.9rem; }
.group-default .fbrow { margin: 0 0 0.9rem; }

.completion-rowcount { font-size: 0.85rem; color: var(--color-muted); margin: 0 0 0.6rem; }

/* Collapsible participant row */
.prow { border: 1px solid var(--color-line); border-radius: var(--radius-card); background: #fff; margin-bottom: 0.5rem; overflow: hidden; }
.prow__toggle-cb { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.prow__head { display: flex; align-items: center; gap: 0.6rem; padding: 0.6rem 0.85rem; flex-wrap: wrap; }
.prow__badge { width: 1.7rem; height: 1.7rem; border-radius: 50%; background: var(--color-primary); color: var(--color-heading); font-weight: 700; font-size: 0.8rem; display: inline-flex; align-items: center; justify-content: center; flex: none; }
.prow__name { flex: 1 1 11rem; }
.prow__age { flex: 0 0 8.5rem; }
.prow__toggle { display: inline-flex; align-items: center; gap: 0.4rem; margin-left: auto; background: transparent; border: 1px solid var(--color-line); border-radius: 20px; padding: 0.4rem 0.8rem; font-family: var(--font-body); font-size: 0.78rem; font-weight: 700; color: var(--color-muted); cursor: pointer; white-space: nowrap; }
.prow__toggle .chev { transition: transform var(--transition); }
.prow__body { display: none; border-top: 1px solid var(--color-line); padding: 1rem 0.85rem; background: var(--color-bg); }
.prow__toggle-cb:checked ~ .prow__body { display: block; }
.prow__toggle-cb:checked ~ .prow__head .prow__toggle { color: var(--color-primary-ink); border-color: var(--color-primary-ink); }
.prow__toggle-cb:checked ~ .prow__head .prow__toggle .chev { transform: rotate(90deg); }
.prow__toggle-cb:focus-visible ~ .prow__head .prow__toggle { outline: 2px solid var(--color-primary-ink); outline-offset: 2px; }
@media (max-width: 35em) { .prow__age { flex: 1 1 8rem; } .prow__toggle { margin-left: 0; } }


/* ============================================================
   Admin — productivity register (Phase 6 slice 6)
   Reuses the direction-A tokens/fonts with a denser, status-forward
   discipline: grouped sidebar, sticky topbar, data tables, status
   badges, a booking-detail action rail. NOT the public persuasion look.
   ============================================================ */

/* The sidebar is sized to hold its own labels, so its width is text-derived and belongs in
   `rem` — 14.75rem is exactly the 236px it replaces at a default root, and it grows with the
   reader instead of clipping the nav at a large text size. `minmax(0, 1fr)` on the content track
   so a wide child (a table) cannot push the grid wider than the viewport. */
.admin-shell { display: grid; grid-template-columns: 14.75rem minmax(0, 1fr); }

/* ── Sidebar ─────────────────────────────────────────────── */
.admin-nav {
    background: var(--admin-nav-bg);
    color: #fff;
    display: flex;
    flex-direction: column;
    padding: 1.1rem 0.75rem 1rem;
    /* Stay in view while a long page (e.g. the expanded Byggnader tree) scrolls. Pinned to the top
       and capped at the viewport height, with its own scroll if the nav itself is taller. */
    position: sticky;
    top: 0;
    align-self: start;
    height: 100vh;
    overflow-y: auto;
}
/* Top bar of the sidebar: brand + (mobile-only) hamburger. Carries the divider under the logo. */
.admin-nav__bar {
    display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
    padding: 0.25rem 0.5rem 1.1rem;
    border-bottom: 1px solid rgba(255, 255, 255, 0.09);
    margin-bottom: 0.6rem;
}
.admin-nav__brand { display: flex; align-items: center; gap: 0.55rem; padding: 0; }
.admin-nav__brand .logo { height: 55px; width: auto; }

/* CSS-only hamburger (the layout is static SSR — no circuit for an @onclick). The checkbox is kept
   off-screen but keyboard-operable; the label is the visible control. */
.admin-nav__checkbox { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
/* Hamburger toggle (a <label>) — hidden on desktop (the links are the always-on sidebar); shown ≤860px. */
.admin-nav__toggle {
    display: none; align-items: center; gap: 0.5rem;
    background: none; border: 1px solid rgba(255, 255, 255, 0.2); color: #fff;
    border-radius: var(--radius-sm); padding: 0.45rem 0.7rem;
    font-size: 0.85rem; cursor: pointer;
}
.admin-nav__toggle:hover { background: rgba(255, 255, 255, 0.08); }
.admin-nav__checkbox:focus-visible ~ .admin-nav__bar .admin-nav__toggle { outline: 2px solid #fff; outline-offset: 2px; }
.admin-nav__toggle-icon { position: relative; display: inline-block; width: 18px; height: 2px; background: currentColor; }
.admin-nav__toggle-icon::before,
.admin-nav__toggle-icon::after {
    content: ""; position: absolute; left: 0; width: 18px; height: 2px; background: currentColor;
}
.admin-nav__toggle-icon::before { top: -6px; }
.admin-nav__toggle-icon::after { top: 6px; }

/* Link groups + foot. Desktop: fills the sidebar so the logout foot sits at the bottom. */
.admin-nav__links { display: flex; flex-direction: column; flex: 1; }
.admin-nav__group {
    font-size: 0.64rem; letter-spacing: 0.13em; text-transform: uppercase;
    color: rgba(255, 255, 255, 0.55); /* AA on the dark sidebar (~5.8:1) */
    padding: 0.9rem 0.6rem 0.35rem;
}
.admin-nav ul { list-style: none; display: flex; flex-direction: column; gap: 1px; margin: 0; padding: 0; }
.admin-nav a {
    display: flex; align-items: center; gap: 0.55rem;
    color: var(--admin-nav-fg);
    padding: 0.44rem 0.6rem;
    border-radius: var(--radius-sm);
    font-size: 0.9rem;
    text-decoration: none;
    transition: background-color var(--transition), color var(--transition);
}
.admin-nav a:hover { background: rgba(255, 255, 255, 0.08); color: #fff; }
.admin-nav a.active {
    background: rgba(113, 176, 2, 0.16);
    color: #fff;
    box-shadow: inset 2px 0 0 var(--color-primary);
}
.admin-nav a .count {
    margin-left: auto;
    background: var(--color-primary); color: var(--color-heading);
    font-size: 0.72rem; font-weight: 700;
    border-radius: 9px; padding: 0 0.42rem; line-height: 1.35;
}
.admin-nav__foot { margin-top: auto; padding-top: 0.8rem; border-top: 1px solid rgba(255, 255, 255, 0.09); }
.admin-nav__user { font-size: 0.78rem; color: rgba(255, 255, 255, 0.62); padding: 0.3rem 0.6rem 0.5rem; word-break: break-all; }
.admin-nav__foot form { margin: 0; }
.admin-nav__foot button {
    background: none; border: 0; color: rgba(255, 255, 255, 0.7);
    font: inherit; font-size: 0.85rem;
    padding: 0.35rem 0.6rem; width: 100%; text-align: left;
    border-radius: var(--radius-sm); cursor: pointer;
}
.admin-nav__foot button:hover { background: rgba(255, 255, 255, 0.08); color: #fff; }

/* ── Main column: sticky topbar + body ───────────────────── */
.admin-main { min-width: 0; min-height: 100vh; display: flex; flex-direction: column; }
.admin-topbar {
    position: sticky; top: 0; z-index: 8;
    display: flex; align-items: baseline; gap: 0.75rem; flex-wrap: wrap;
    padding: 1.05rem 2rem 0.9rem;
    border-bottom: 1px solid var(--color-line);
    background: var(--color-bg);
}
/* `min-width: 0` because a flex item will not shrink below its min-content width by default,
   and `overflow-wrap` because Swedish page titles are single compounds — "Administratörskonton"
   is one 360px word at a 24px root, which pushed /konton 172px past a 360px screen. */
.admin-topbar h1 { font-size: 1.5rem; font-weight: 400; min-width: 0; overflow-wrap: break-word; }
.admin-topbar .spacer { flex: 1; }
.admin-topbar .crumb { font-size: 0.82rem; color: var(--color-muted); letter-spacing: 0.02em; }
/* Underline the breadcrumb link so it is distinguishable from the surrounding crumb text by more
   than colour alone (WCAG 1.4.1 / axe link-in-text-block). */
.admin-topbar .crumb a { text-decoration: underline; }
.admin-body { padding: 1.5rem 2rem 3rem; max-width: 1160px; }

/* ── Status badges (all 8 booking states) ────────────────── */
.badge {
    display: inline-flex; align-items: center; gap: 0.35rem;
    font-size: 0.75rem; font-weight: 700;
    padding: 0.15rem 0.55rem; border-radius: 999px; line-height: 1.5;
    white-space: nowrap;
}
.badge::before { content: ""; width: 0.5rem; height: 0.5rem; border-radius: 50%; background: currentColor; }
.badge--pending   { color: var(--color-warn); background: var(--color-warn-bg); }
.badge--approved  { color: var(--color-ok);   background: var(--color-ok-bg); }
.badge--complete  { color: var(--color-info); background: var(--color-info-bg); }
.badge--active    { color: var(--color-active); background: var(--color-active-bg); }
/* The one badge that had borrowed a general-purpose token instead of a purpose-picked colour,
   and the only one that failed. Its tint is translucent, so on /bokningar the real ground is the
   tint composited over the ROW — and .admin-table tbody tr:hover swaps that row to
   --color-bg-warm. Against that #e1ddd3 the old var(--color-muted) measured 3.96:1: a genuine
   AA failure, live, on hover, for the Avslutad and Besvarad badges. Every other badge already
   carries its own deepened value for exactly this reason (warn and err were deepened in
   2026-08-07/10; --active hardcodes #2c4708). This one now does too: 5.00:1 hovered, 5.90:1 at
   rest. The deepened --color-muted alone would only have reached 4.56 — passing by 0.06, which
   is the coin-on-its-edge the margin rule exists to reject. */
.badge--done,
.badge--neutral   { color: var(--color-neutral); background: var(--color-neutral-bg); }
.badge--denied,
.badge--cancelled { color: var(--color-err); background: var(--color-err-bg); }
.chip {
    display: inline-block; font-size: 0.72rem; color: var(--color-muted);
    background: var(--color-bg-warm); border: 1px solid var(--color-line);
    border-radius: 999px; padding: 0.05rem 0.55rem;
}

/* Admin button modifiers */
.btn-sm { padding: 0.32rem 0.7rem; font-size: 0.8rem; }
.btn-outline.btn-danger { color: var(--color-err); border-color: var(--color-err-line); }
.btn-outline.btn-danger:hover { border-color: var(--color-err); background: var(--color-err-bg); }

/* ── Modal / confirmation dialog ─────────────────────────── */
.modal-overlay {
    /* Above .form-status (1000) and .admin-panel (1001): a confirmation dialog is a true blocking
       modal and must always render on top of whatever triggered it, panel or plain page alike. */
    position: fixed; inset: 0; z-index: 1002;
    display: flex; align-items: center; justify-content: center; padding: 1rem;
    background: rgba(26, 23, 20, 0.45);
}
.modal {
    background: #fff; border: 1px solid var(--color-line); border-radius: var(--radius-modal);
    width: 100%; max-width: 26rem; padding: 1.4rem 1.5rem;
    box-shadow: 0 10px 40px rgba(26, 23, 20, 0.22);
}
.modal__title {
    font-family: var(--font-heading); font-size: 1.25rem;
    color: var(--color-heading); margin: 0 0 0.5rem;
}
.modal__body { margin: 0 0 1.2rem; color: var(--color-text); font-size: 0.925rem; line-height: 1.5; }
.modal__actions { display: flex; justify-content: flex-end; gap: 0.6rem; }

/* ── Cards / panels ──────────────────────────────────────── */
.card { background: #fff; border: 1px solid var(--color-line); border-radius: var(--radius-card); padding: 1.1rem 1.2rem; }
/* wrap: the head is a title plus a right-aligned "Alla bokningar →" link held there by
   `margin-left: auto`. Unwrapped, the link ran 17px past the dashboard at a 32px root. */
.card__head { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; margin-bottom: 0.7rem; }
.card__head h2 {
    font-family: var(--font-body); font-weight: 700; font-size: 0.95rem;
    letter-spacing: 0.01em; color: var(--color-heading); margin: 0;
}
.card__head .n { background: var(--color-warn-bg); color: var(--color-warn); font-size: 0.75rem; font-weight: 700; border-radius: 999px; padding: 0 0.5rem; }
.card__head .link { margin-left: auto; font-size: 0.8rem; font-weight: 600; }

/* Dashboard (Översikt) top row: one column on mobile, two side-by-side from 720px up. */
/* minmax(0, …) so a card cannot force the track wider than the column — the dashboard cards
   overflowed by 13px at a 32px root while the track carried `1fr`'s auto minimum. */
.dash-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1rem; margin-bottom: 1rem; }
@media (min-width: 45em) { .dash-grid { grid-template-columns: 1fr 1fr; } }

/* ── Admin data table (dense, scannable, status-forward) ─── */
.admin-table-wrap { overflow-x: auto; background: #fff; border: 1px solid var(--color-line); border-radius: var(--radius-card); }
.card .admin-table-wrap { border: 0; background: none; }
table.admin-table { width: 100%; border-collapse: collapse; font-size: 0.875rem; }
table.admin-table th {
    text-align: left; font-weight: 700; font-size: 0.72rem;
    letter-spacing: 0.05em; text-transform: uppercase; color: var(--color-muted);
    padding: 0.6rem 0.8rem; border-bottom: 1px solid var(--color-line);
    white-space: nowrap; background: var(--color-bg);
}
table.admin-table td { padding: 0.62rem 0.8rem; border-bottom: 1px solid var(--color-line); vertical-align: middle; color: var(--color-text); }
table.admin-table tr:last-child td { border-bottom: 0; }
table.admin-table tbody tr:hover { background: var(--color-bg-warm); }
table.admin-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.admin-table .mono { font-variant-numeric: tabular-nums; }
.admin-table .rowlink { font-weight: 600; color: var(--color-heading); text-decoration: none; }
/* Only a real <a> (Bookings/Dashboard/AdminCalendar, genuine navigation to booking detail) gets
   the link-hover-underline. The <span class="rowlink"> used on reference-data pages (Buildings,
   MealOptions, Facilities, PriceRules, CleaningZones) is plain label text — those rows already
   open edit on a click anywhere in the row plus an explicit Redigera button, so underlining the
   title on hover falsely implied only that text was clickable. */
.admin-table a.rowlink:hover { color: var(--color-primary-ink); text-decoration: underline; }
.admin-table .sub { display: block; font-size: 0.78rem; color: var(--color-muted); }
.admin-table .rowactions { text-align: right; white-space: nowrap; }
.admin-table .rowactions .btn-ghost { padding: 0.2rem 0.5rem; }
.admin-empty { color: var(--color-muted); font-size: 0.9rem; padding: 0.4rem 0; }

/* ── Mobile card counterpart to .admin-table (below 640px) ───
   Every list page renders both the table and this card list; the media query at the bottom of
   this file shows exactly one of the two. Cards mirror the table's columns as a title/badge head,
   an optional secondary line, and label/value field rows — no sideways scrolling. */
.data-cards { display: none; flex-direction: column; gap: 0.6rem; }
.data-card { background: #fff; border: 1px solid var(--color-line); border-radius: var(--radius-card); padding: 0.75rem 0.85rem; }
.data-card--clickable { cursor: pointer; }
.data-card--clickable:hover { background: var(--color-bg-warm); }
/* `flex-wrap: wrap` so the status badge drops to its own line rather than being pushed out of
   the card: a .badge is `white-space: nowrap` by design (a status pill must not break mid-
   phrase), so at a raised text size "Väntar på aktivering" is a 209px item that cannot shrink,
   and with a nowrap row it overflowed /konton by 172px at a 24px root on a 360px screen. */
.data-card__head { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 0.6rem; }
/* A card title is often an email address — one long token with no break opportunity in it, so
   `anywhere` rather than `break-word`. At a 32px root one ran 50px past a 360px screen. */
.data-card__title { font-weight: 600; font-size: 0.9rem; color: var(--color-heading); line-height: 1.3; overflow-wrap: anywhere; }
/* Holds the guest's email — one long token with no break opportunity, same as __title above. */
.data-card__sub { font-size: 0.78rem; color: var(--color-muted); margin-top: 0.15rem; overflow-wrap: anywhere; }
.data-card__fields { display: flex; flex-direction: column; gap: 0.3rem; font-size: 0.8rem; margin: 0.55rem 0 0; }
.data-card__fields > div { display: flex; justify-content: space-between; gap: 0.6rem; }
/* min-width:0 + anywhere: a dt/dd pair is a flex row, so neither side shrinks below its own
   min-content by default, and admin values are routinely single Swedish compounds
   ("Konferensbyggnad"). Without this the dd ran past the card at a 32px root. */
.data-card__fields dt, .data-card__fields dd { min-width: 0; overflow-wrap: anywhere; }
.data-card__fields dt { color: var(--color-muted); }
.data-card__fields dd { margin: 0; color: var(--color-text); font-weight: 500; text-align: right; }
.data-card__actions { display: flex; justify-content: flex-end; gap: 0.4rem; margin-top: 0.55rem; padding-top: 0.55rem; border-top: 1px solid var(--color-line); }

/* Reference-CRUD add/edit panel (below the list) */
.admin-editcard { margin-top: 1.5rem; max-width: 640px; }
.admin-editcard h2, .admin-editcard h3 { font-family: var(--font-body); font-weight: 700; font-size: 1.05rem; margin: 0 0 1rem; }
.admin-editcard .form-group:last-of-type { margin-bottom: 1.2rem; }
.admin-subsection { font-family: var(--font-body); font-weight: 700; font-size: 1.15rem; margin: 2rem 0 1rem; }

/* ── Byggnader hierarchy tree (PhysicalBuilding → Building → Room, Phase 11) ─
   Replaces the old two-page flat-table pattern with one indented tree + a slide-in edit
   panel. "bldg-" prefixed since .tree/.row/.panel are too generic for this shared stylesheet. */
.bldg-tree { border: 1px solid var(--color-line); border-radius: var(--radius-card); overflow: hidden; background: #fff; }
.bldg-tree__toolbar { display: flex; align-items: center; justify-content: space-between; padding: 0.7rem 0.9rem; border-bottom: 1px solid var(--color-line); background: var(--color-bg-warm); }
.bldg-tree__toolbar .cols { display: flex; gap: 2.1rem; font-size: 0.68rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--color-muted); margin-left: auto; padding-right: 6.5rem; }
.bldg-tree__expandall { font-size: 0.78rem; color: var(--color-primary-ink); background: none; border: 0; cursor: pointer; font-weight: 700; padding: 0.2rem 0.3rem; }
.bldg-tree__expandall:hover { text-decoration: underline; }

.bldg-row {
    display: grid; grid-template-columns: 1fr auto; grid-template-areas: "main stats"; align-items: center;
    padding: 0.6rem 0.9rem; border-bottom: 1px solid var(--color-line); gap: 0.75rem; cursor: pointer;
}
.bldg-row:last-child { border-bottom: 0; }
.bldg-row:hover { background: var(--color-bg-warm); }
.bldg-row.is-selected { box-shadow: inset 3px 0 0 var(--color-primary); background: var(--color-ok-bg); }
.bldg-row--l0 { background: #fff; font-weight: 700; }
.bldg-row--l1 { background: #fbf9f6; }
.bldg-row--l2 { background: #f6f3ee; font-size: 0.88rem; }

.bldg-row__main { grid-area: main; display: flex; align-items: center; gap: 0.55rem; min-width: 0; }
.bldg-row--l1 .bldg-row__main { padding-left: 1.6rem; }
.bldg-row--l2 .bldg-row__main { padding-left: 3.2rem; }

.bldg-twist {
    width: 1.1rem; height: 1.1rem; flex: none; display: inline-flex; align-items: center; justify-content: center;
    background: none; border: 0; color: var(--color-muted); cursor: pointer; padding: 0; transition: transform var(--transition);
}
.bldg-twist svg { width: 0.6rem; height: 0.6rem; }
.bldg-row.is-open > .bldg-row__main > .bldg-twist { transform: rotate(90deg); }
.bldg-twist--spacer { visibility: hidden; }

.bldg-row__name {
    color: var(--color-heading); background: none; border: 0; padding: 0; text-align: left; font: inherit;
    cursor: pointer; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bldg-row--l0 .bldg-row__name { font-family: var(--font-heading); font-size: 1.02rem; font-weight: 400; }
.bldg-row__meta { color: var(--color-muted); font-size: 0.78rem; font-weight: 400; white-space: nowrap; }
.bldg-row__summary { display: none; color: var(--color-muted); font-size: 0.78rem; font-variant-numeric: tabular-nums; }

.bldg-row__stats { grid-area: stats; display: flex; align-items: center; gap: 1.6rem; font-size: 0.82rem; color: var(--color-muted); font-variant-numeric: tabular-nums; }
.bldg-row__stats .num { min-width: 3.2rem; text-align: right; }
.bldg-row__actions { display: flex; gap: 0.15rem; }

.bldg-children { display: none; }
.bldg-children.is-open { display: block; }

.bldg-addrow {
    display: flex; align-items: center; gap: 0.4rem;
    padding: 0.5rem 0.9rem 0.5rem 3.2rem; font-size: 0.82rem; color: var(--color-primary-ink);
    background: #f6f3ee; border-bottom: 1px solid var(--color-line); cursor: pointer; font-weight: 700;
}
.bldg-addrow:hover { text-decoration: underline; }
.bldg-addrow--l1 { padding-left: 1.6rem; background: #fbf9f6; }
.bldg-addrow--l2 { padding-left: 3.2rem; background: #f6f3ee; }
.bldg-addrow--l0 { padding-left: 0.9rem; background: #fff; border-bottom: 0; border-top: 1px solid var(--color-line); }

.bldg-emptychild { padding: 0.55rem 0.9rem 0.7rem 3.2rem; font-size: 0.82rem; color: var(--color-muted); font-style: italic; background: #fbf9f6; border-bottom: 1px solid var(--color-line); }

/* Shared slide-in edit panel (AdminPanel.razor) — used by every reference-data CRUD page
   (Buildings, MealOptions, Facilities, PriceRules, CleaningZones, Blocks) for its create/edit
   form, replacing the old below-the-table admin-editcard everywhere. One shared surface means
   one place to fix spacing/behaviour instead of N near-identical copies. */
.admin-scrim {
    position: fixed; inset: 0; background: rgba(20, 18, 16, 0.28); opacity: 0; pointer-events: none;
    transition: opacity var(--transition); z-index: 40;
}
.admin-scrim.is-open { opacity: 1; pointer-events: auto; }

.admin-panel {
    position: fixed; top: 0; right: 0; bottom: 0; width: min(380px, 92vw);
    background: var(--color-bg); border-left: 1px solid var(--color-line);
    box-shadow: 0 12px 40px rgba(26, 23, 20, 0.16);
    transform: translateX(100%); transition: transform 220ms ease;
    /* Stacking order (highest wins): .modal-overlay 1002 > .admin-panel 1001 > .form-status 1000.
       This panel sits above the toast (also fixed bottom-right) so its own Spara/Ta bort buttons
       don't end up under a lingering toast and unclickable — the panel is an active surface the
       admin is mid-task in, so it should win over a passive notification. But ConfirmDialog's
       .modal-overlay must win over THIS panel, since "Ta bort" inside the panel opens a
       ConfirmDialog on top of it — if you touch either z-index, keep this order intact. */
    z-index: 1001;
    display: flex; flex-direction: column;
}
.admin-panel.is-open { transform: translateX(0); }
.admin-panel > form { display: flex; flex-direction: column; flex: 1; min-height: 0; } /* the EditForm's rendered <form> must flex its body/foot children */
.admin-panel__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.75rem; padding: 1.1rem 1.3rem 0.9rem; border-bottom: 1px solid var(--color-line); }
.admin-panel__kicker { font-size: 0.68rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--color-muted); font-weight: 700; margin-bottom: 0.25rem; }
.admin-panel__title { font-size: 1.2rem; font-weight: 400; }
.admin-panel__close { background: none; border: 0; font-size: 1.3rem; line-height: 1; color: var(--color-muted); cursor: pointer; padding: 0.15rem 0.3rem; }
.admin-panel__close:hover { color: var(--color-heading); }
.admin-panel__body { padding: 1.1rem 1.3rem; overflow-y: auto; flex: 1; }
.admin-panel__body .form-group:last-of-type { margin-bottom: 1.2rem; }
/* flex-wrap as a defensive fallback at any width (not just below the mobile breakpoint) — three
   buttons (Spara/Aktivera-Inaktivera/Ta bort) in the fixed 380px desktop drawer is already tight;
   wrapping to a second line beats overflowing or squeezing each label onto two lines. */
.admin-panel__foot { padding: 1rem 1.3rem 1.3rem; border-top: 1px solid var(--color-line); display: flex; flex-wrap: wrap; gap: 0.6rem; }

.admin-crumbtrail { display: flex; align-items: center; gap: 0.35rem; font-size: 0.78rem; color: var(--color-muted); flex-wrap: wrap; }
.admin-crumbtrail b { color: var(--color-heading); font-weight: 700; }

/* Mobile (≤640px): rows stack, the two numeric columns fold into one summary line, the whole
   row becomes a forgiving tap target, and the edit panel goes full-screen (NFR-17 admin-mobile,
   extended here beyond the "tables may stay horizontally scrollable" floor since this admin
   works mostly from a phone). */
@media (max-width: 40em) {
    .bldg-tree__toolbar .cols { display: none; }

    .bldg-row {
        grid-template-columns: minmax(0, 1fr); grid-template-areas: "main" "stats";
        row-gap: 0.3rem; padding: 0.7rem 0.85rem;
    }
    .bldg-row--l1 .bldg-row__main { padding-left: 0.85rem; }
    .bldg-row--l2 .bldg-row__main { padding-left: 1.7rem; }
    .bldg-row__main { flex-wrap: wrap; row-gap: 0.15rem; }
    .bldg-row__meta { display: none; } /* folded into .bldg-row__summary to avoid repeating the same info twice */
    .bldg-row__summary { display: block; flex-basis: 100%; padding-left: 1.65rem; }
    .bldg-row--l1 .bldg-row__summary { padding-left: 2.5rem; }
    .bldg-row--l2 .bldg-row__summary { padding-left: 3.35rem; }

    .bldg-row__stats { justify-content: flex-start; padding-left: 1.65rem; }
    .bldg-row--l1 .bldg-row__stats { padding-left: 2.5rem; }
    .bldg-row--l2 .bldg-row__stats { padding-left: 3.35rem; }
    .bldg-row__stats .num { display: none; }
    .bldg-row__actions { display: none; } /* the whole row already opens edit — no need for a second button */

    /* Touch target: keep the visual chevron small but grow its hit area to ~44px (WCAG 2.5.5). */
    .bldg-twist { width: 2.6rem; height: 2.6rem; margin: -0.75rem 0 -0.75rem -0.85rem; }

    .bldg-addrow--l0 { padding-left: 0.85rem; }
    .bldg-addrow--l1 { padding-left: 2.5rem; }
    .bldg-addrow--l2 { padding-left: 3.35rem; }
    .bldg-emptychild { padding-left: 1.7rem; }

    /* Shared with every other reference-data page — not Buildings-specific, unlike the rules above. */
    .admin-panel { width: 100%; }
    .admin-panel__foot { flex-direction: column; }
    .admin-panel__foot .btn, .admin-panel__foot .btn-outline { width: 100%; }
}

/* ── Filters + toolbar row ───────────────────────────────── */
.admin-toolbar { display: flex; flex-wrap: wrap; gap: 0.6rem; align-items: center; margin-bottom: 1.1rem; }
.admin-filters { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: end; margin-bottom: 1.1rem; }
.admin-filters .form-group { margin-bottom: 0; gap: 0.25rem; }
.admin-filters .form-group label { font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--color-muted); }

/* ── Booking detail: main column + sticky action rail ────── */
.admin-detail-head { display: flex; align-items: center; gap: 0.9rem; flex-wrap: wrap; margin-bottom: 1.3rem; }
.admin-detail-head h1 { font-size: 1.6rem; font-weight: 400; margin: 0; }
.detail-grid { display: grid; grid-template-columns: 1fr 320px; gap: 1.4rem; align-items: start; }
.detail-rail { position: sticky; top: calc(var(--admin-topbar-h) + 0.5rem); max-height: calc(100vh - var(--admin-topbar-h) - 1rem); overflow-y: auto; display: flex; flex-direction: column; gap: 1rem; }
/* Booking detail sections (Kontaktuppgifter, Vistelse, Deltagare, ...) are each a native
   <details class="detail-acc">, not a plain <div> — collapsing what's already been reviewed is
   what keeps a many-participant booking from becoming one long scroll. `open` is left as a
   static/unbound attribute in the Razor so the browser owns collapsed/expanded state across
   Blazor re-renders (e.g. after saving one participant) instead of resetting on every save. */
.detail-acc { margin-bottom: 0.8rem; background: #fff; border: 1px solid var(--color-line); border-radius: var(--radius-card); }
.detail-acc__head {
    display: flex; align-items: center; justify-content: space-between; gap: 0.8rem;
    padding: 0.75rem 1rem; cursor: pointer; list-style: none;
}
.detail-acc__head::-webkit-details-marker { display: none; }
.detail-acc__head::marker { content: ""; }
/* A real <h2> (not a <span>) so the section title still shows up in screen-reader heading
   navigation despite living inside a <summary> — <details>'s content model allows one heading
   element followed by phrasing content, so this doesn't change the disclosure semantics. */
.detail-acc__title {
    font-family: var(--font-body); font-weight: 700; font-size: 0.8rem;
    text-transform: uppercase; letter-spacing: 0.05em; color: var(--color-muted);
    margin: 0;
}
.detail-acc__meta { font-size: 0.82rem; color: var(--color-text); text-align: right; }
.detail-acc__chev { color: var(--color-muted); font-size: 0.7rem; flex: none; transition: transform var(--transition); }
.detail-acc[open] > .detail-acc__head { border-bottom: 1px solid var(--color-line); }
.detail-acc[open] > .detail-acc__head .detail-acc__chev { transform: rotate(180deg); }
.detail-acc__body { padding: 1rem; }
.detail-acc__body > h3:first-child { margin-top: 0; }
.detail-acc__body > h3 { font-family: var(--font-body); font-weight: 700; font-size: 0.9rem; color: var(--color-heading); margin: 1.1rem 0 0.5rem; }
.kv { display: grid; grid-template-columns: auto 1fr; gap: 0.35rem 1rem; font-size: 0.9rem; max-width: 480px; }
.kv dt { color: var(--color-muted); }
.kv dd { color: var(--color-heading); margin: 0; }
/* Inline qualifier trailing a value in a dd — "2026-08-15 kl. 15:00", "1200 kr exkl. moms".
   `.sub` was already the markup on BookingDetail's four of these, but the only rule carrying
   that name is `.admin-table .sub`, so inside a .kv it matched nothing and the qualifier
   rendered at full dd size and ink — indistinguishable from the value it qualifies.
   Scoped per component, exactly like the admin-table rule; deliberately NOT display:block,
   because unlike a table's second line these sit on the same line as the value. */
.kv .sub { font-size: 0.8125rem; color: var(--color-muted); }
.rail-actions { display: flex; flex-direction: column; gap: 0.5rem; }
.rail-actions .btn, .rail-actions .btn-outline { justify-content: center; }
.action-note { font-size: 0.85rem; color: var(--color-muted); margin: 0.7rem 0 0; }
/* Building assignment (FR-103) — admin booking detail. Replaced .paycheck, whose last consumers
   were the payment checkboxes (FR-42, removed) and the room-choice toggle (FR-56, tombstoned). */
.assign-list { display: flex; flex-direction: column; gap: 0.15rem; border: 0; margin: 0; padding: 0; }
.assign-item { display: flex; gap: 0.5rem; align-items: center; font-size: 0.875rem; color: var(--color-text); padding: 0.4rem 0.5rem; border-radius: var(--radius-sm); }
.assign-item:hover { background: var(--color-bg-warm); }
.assign-item__name { flex: 1; }
.assign-item__meta { color: var(--color-muted); font-size: 0.8125rem; }
/* Blocked buildings stay visible but unselectable, so the admin sees why rather than wondering
   where the building went (FR-102). */
.assign-item--blocked { color: var(--color-muted); }
.assign-item--blocked:hover { background: none; }
.assign-item--blocked .assign-item__meta { color: var(--color-warn); }

/* Read-only assigned lodging on the completion form (FR-103/D4). */
/* Scoped to the .lodging-note block on purpose. These were `.lodging-list` / `.lodging-rooms`
   until 2026-08-11, which collided head-on with the *public* Boende list further down this
   file (search `.lodging-group`) — same class name, same specificity, and the public one is
   defined later, so it won. The guest's assigned buildings were silently rendering as that
   grid: two columns from 640px and four from 1024px, instead of the stacked rows intended
   here. Nothing errored and no test caught it, because a wrong-but-valid layout looks
   deliberate. The duplication rule in design.md is about repeated *values*; this is the
   same failure one level up — a repeated *name*. Keep both families prefixed by their own
   block (`lodging-note__*` here, `lodging-group`/`lodging-item` there). */
.lodging-note { margin-top: 1.5rem; }
.lodging-note__list { list-style: none; margin: 0.5rem 0 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }
.lodging-note__list li { display: flex; flex-direction: column; gap: 0.1rem; padding: 0.6rem 0.75rem; background: var(--color-bg-warm); border-radius: var(--radius-sm); }
/* The physical-building line was marked up as a bare `.sub`, which is only ever defined as
   `.admin-table .sub` — so on this public page it matched nothing and rendered at full body
   size and colour, between a bold name and a muted rooms line. The flex column hid it as a
   layout bug (children stack either way) and left it looking merely inconsistent. */
.lodging-note__sub { font-size: 0.8125rem; color: var(--color-muted); }
.lodging-note__rooms { font-size: 0.875rem; color: var(--color-muted); }
.detail-rail .card textarea {
    width: 100%; font: inherit; font-size: 0.875rem; padding: 0.45rem 0.55rem;
    border: 1px solid var(--color-line); border-radius: var(--radius-sm); background: #fff; resize: vertical;
}
.detail-rail .card textarea:focus { outline: 2px solid var(--color-primary-ink); outline-offset: 1px; border-color: transparent; }

/* Participant editor cards (booking detail) */
fieldset.participant {
    border: 1px solid var(--color-line); border-radius: var(--radius-card);
    padding: 1rem 1.1rem; margin-bottom: 0.9rem; background: #fff;
}
fieldset.participant legend {
    font-weight: 700; font-size: 0.85rem; color: var(--color-heading);
    padding: 0 0.4rem; margin-left: -0.4rem;
}

/* Participant read view — the default. A completed booking's admin/edit form only opens per
   person on request (the "Redigera" pencil), so visiting a many-guest booking to look something
   up doesn't load every field for every guest as live inputs. */
.participant-view__row { display: flex; align-items: center; justify-content: space-between; gap: 0.6rem; }
.participant-view__name { font-weight: 600; color: var(--color-heading); }
.participant-view__meta { font-size: 0.85rem; color: var(--color-muted); margin-top: 0.2rem; }
.participant-view__diet { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-top: 0.5rem; }
.edit-pencil {
    font-size: 0.78rem; font-weight: 700; color: var(--color-primary-ink);
    background: none; border: 0; cursor: pointer; padding: 0.2rem 0.3rem; flex: none;
}
.edit-pencil:hover { text-decoration: underline; }

/* ── Login / password pages ──────────────────────────────── */
.login-page { min-height: 100vh; display: flex; align-items: center; justify-content: center; background: var(--color-bg-warm); padding: 3rem 1rem; }
.login-card { width: 100%; max-width: 400px; background: #fff; border: 1px solid var(--color-line); border-radius: var(--radius-card); padding: 2.25rem 2rem; }
.login-card__logo { display: flex; justify-content: center; margin-bottom: 1.25rem; }
/* 104px, not the 55px every other surface uses: this is the only place that renders the
   *stacked* lockup, whose box is 250x156 rather than the horizontal 192x52. At 55px the
   stacked mark would be 88px wide — a thumbnail on a 400px card. Height is the wrong
   dimension to share between two lockups of different aspect. */
.login-card__logo .logo { height: 104px; width: auto; }
.login-card h1 { text-align: center; font-weight: 400; font-size: 1.5rem; margin-bottom: 0.35rem; }
.login-card .login-sub { text-align: center; color: var(--color-muted); font-size: 0.85rem; margin-bottom: 1.5rem; }
.login-card .btn { width: 100%; justify-content: center; }
.login-card .login-alt { text-align: center; margin-top: 1rem; font-size: 0.9rem; }

/* ── Responsive: collapse the sidebar to a top bar under 860px ─ */
@media (max-width: 53.75em) {
    .admin-shell { grid-template-columns: minmax(0, 1fr); }
    /* Collapses to a top bar here — drop the desktop full-height sizing but keep it pinned to the
       top so the hamburger stays reachable while the page scrolls. z-index sits above page content
       but below the slide-in panel / dialog (1000+). */
    .admin-nav { padding: 0.55rem 0.9rem; height: auto; overflow-y: visible; top: 0; z-index: 20; }
    .admin-nav__bar { border: 0; margin: 0; padding: 0; }
    .admin-nav__toggle { display: inline-flex; }
    /* Collapsible menu: hidden until the hamburger checkbox is checked; then drops down as full-width rows. */
    .admin-nav__links { display: none; flex: initial; padding-top: 0.6rem; }
    .admin-nav__checkbox:checked ~ .admin-nav__links { display: flex; }
    .admin-nav a { padding: 0.7rem 0.6rem; }
    .admin-nav__foot { margin-top: 0.6rem; }
    .admin-topbar, .admin-body { padding-left: 1rem; padding-right: 1rem; }
    .detail-grid { grid-template-columns: minmax(0, 1fr); }
    .detail-rail { position: static; max-height: none; overflow-y: visible; }
}

/* ── Responsive: swap .admin-table for .data-cards under 640px ─
   Narrower than the 860px sidebar breakpoint above — a collapsed sidebar still leaves enough
   width for a table down to about 640px; below that, columns get too cramped to read without
   sideways scrolling, so the card list takes over instead. */
@media (max-width: 40em) {
    .admin-table-wrap { display: none; }
    .data-cards { display: flex; }
}

/* ============================================================
   Public content pages — generic text defaults
   (Used by the still-functional funnel/completion pages until their
   slices; the trust pages below use scoped direction-A components.)
   ============================================================ */
.section h1 {
    margin-bottom: 1.5rem;
}

.section h2 {
    margin-top: 2.5rem;
    margin-bottom: 1rem;
}

.section p {
    margin-bottom: 1rem;
}

.section dl {
    margin: 1rem 0;
}

.section dt {
    font-weight: 700;
    color: var(--color-heading);
    margin-top: 1rem;
}

/* ============================================================
   Interior trust pages (direction A, Phase 6 slice 3)
   "chapter opener" header · om-oss · faciliteter · mat & priser
   · kontakt · utility (not-found / error)
   ============================================================ */

/* Chapter-opener page header (interior pages have no hero) */
.page-head {
    background: var(--color-bg-warm);
    border-bottom: 1px solid var(--color-line);
}
.page-head .container { padding-top: 3.5rem; padding-bottom: 3rem; }
.page-head h1 {
    font-weight: 400;
    font-size: clamp(1.9rem, 4.2vw, 2.9rem);
    line-height: 1.05;
    letter-spacing: -0.015em;
    margin: 0;
}
.page-head__rule { width: 3.5rem; height: 2px; background: var(--color-primary); margin: 1.25rem 0 0; }
.page-head__intro { font-size: 1.125rem; color: var(--color-text); margin: 1.25rem 0 0; max-width: 56ch; }

/* Readable prose column */
/* Swedish compounds get long, and at a raised text size a single word can exceed a phone
   column — one <p> on /integritetspolicy overflowed the viewport by 6px at a 24px root.
   `break-word` only ever acts on a word that cannot fit, so normal text is untouched. */
.prose { max-width: 65ch; overflow-wrap: break-word; }
.prose p { font-size: 1.0625rem; color: var(--color-text); margin: 0 0 1.2rem; }
.prose p:last-child { margin-bottom: 0; }
/* Sub-heading and bullet list inside a prose column. Added for the privacy policy (the first
   public page with real document structure — numbered sections, sub-headings, a rights list);
   the reset strips list markers and .prose previously styled paragraphs only.

   The size is the *prose body* size, not the type scale's H3 (~21px): .subhead — the numbered
   section head this sits beneath — is 1.25rem, so a scale H3 here would outrank its own parent.
   It separates from body copy by family and weight instead, and is recorded as its own step in
   design.md's type scale rather than left as a loose number. */
.prose h3 {
    font-family: var(--font-heading);
    color: var(--color-heading);
    font-weight: 700;
    font-size: 1.0625rem;
    margin: 1.5rem 0 0.75rem;
}
/* Bottom margin matches .prose p above, not the 4-based step: a list and a paragraph in the same
   column must separate from what follows them identically, and matching the immediate sibling is
   the stronger consistency here. */
.prose ul { margin: 0 0 1.2rem; padding-left: 1.5rem; list-style: disc; }
.prose li { font-size: 1.0625rem; color: var(--color-text); margin-bottom: 0.5rem; }
.prose li:last-child { margin-bottom: 0; }

/* Inline links inside running text are underlined so they're distinguishable without relying on
   colour (WCAG 1.4.1 — axe "link-in-text-block"). Nav/footer/cards/buttons are excluded: they're
   set apart by layout, not sitting inside a sentence. */
/* Two selectors joined this list rather than getting rules of their own, for the same reason
   each time — there must be exactly one definition of "inline links are underlined":
   `.form-group--checkbox label a` (2026-08-17) — the allergy-consent label (FR-110) is a full
   sentence with the policy link inside it, which axe correctly reported as link-in-text-block;
   the acknowledgement label on /boka passed only because it is almost entirely link text with
   little prose around it, i.e. the same latent failure one longer sentence away.
   `.findmap-figure__caption a` (2026-08-18) — the OpenStreetMap attribution sits inside a
   sentence under the map. */
.prose a, .section p a, .menu-note a, .contact-dl dd a,
.form-group--checkbox label a, .findmap-figure__caption a { text-decoration: underline; text-underline-offset: 0.15em; }

/* Section eyebrow + subhead on content pages */
.subhead {
    font-family: var(--font-heading);
    color: var(--color-heading);
    font-weight: 700;
    font-size: 1.25rem;
    margin: 0 0 1rem;
}

/* Om oss */
.about-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 2.5rem; align-items: start; }
.about-photo {
    min-height: 15rem;
    border-radius: var(--radius-card);
    position: relative;
    overflow: hidden;
    background: linear-gradient(160deg, #d3d9cf, #8a9a72);
}
.facts {
    display: grid;
    /* Same `1fr` auto-minimum trap as .site-footer__grid: the track cannot go below its
       longest word, so at a 24px root this box measured 399px inside a 312px screen — and
       because it is a grid item of .about-grid it dragged the prose and photo out with it.
       An explicit minimum lets it drop to one column instead. */
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 7.5rem), 1fr));
    gap: 1px;
    background: var(--color-line);
    border: 1px solid var(--color-line);
    border-radius: var(--radius-card);
    overflow: hidden;
    margin-top: 2rem;
}
/* Pin two columns once there is room; `em` for the same reason as .site-footer__grid. */
@media (min-width: 30em) {
    .facts { grid-template-columns: repeat(2, 1fr); }
}
.facts > div { background: var(--color-bg); padding: 1.1rem 1.25rem; }
.facts__num { font-family: var(--font-heading); font-size: 1.4rem; color: var(--color-heading); line-height: 1; }
.facts__label { font-size: 0.8125rem; color: var(--color-muted); margin-top: 0.25rem; }
@media (min-width: 64em) {
    .about-grid { grid-template-columns: 1.1fr 0.9fr; gap: 3rem; }
    .about-photo { min-height: 21rem; }
}

/* Facilities grid — sits below the lead .teaser feature, so card titles are h3 (h2 is the
   section head above them). Deliberately capped at 2 columns (not 3): with the lead facility
   pulled into its own feature spread, the remaining count is usually small, and the first
   remaining card takes a --wide, two-up treatment so the grid isn't a uniform wall of boxes. */
.fac-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.5rem; }
.fac-card { border: 1px solid var(--color-line); border-radius: var(--radius-card); overflow: hidden; background: #fff; }
.fac-card__img {
    aspect-ratio: 4 / 3;
    position: relative;
    background: linear-gradient(160deg, #d3d9cf, #8a9a72);
}
.fac-card__img img { width: 100%; height: 100%; object-fit: cover; }
.fac-card__body { padding: 1.1rem 1.25rem; }
.fac-card h3 { font-size: 1.2rem; font-weight: 700; margin: 0 0 0.4rem; }
.fac-card p { margin: 0; font-size: 0.95rem; color: var(--color-text); }
@media (min-width: 40em) {
    .fac-grid { grid-template-columns: repeat(2, 1fr); }
    .fac-card--wide { grid-column: span 2; display: grid; grid-template-columns: 1.2fr 1fr; }
    .fac-card--wide .fac-card__img { aspect-ratio: auto; height: 100%; }
    .fac-card--wide .fac-card__body { display: flex; flex-direction: column; justify-content: center; padding: 1.5rem 1.75rem; }
    .fac-card--wide .fac-card__body h3 { font-size: 1.4rem; }
}

/* Lodging (Boende) — active buildings grouped by physical building (BuildingService.
   GroupByPhysicalBuilding). A compact list, not a full photo card each: eight similar
   sleeping sections would just reintroduce the "wall of boxes" problem one section down. */
.lodging-group { margin-bottom: 2.25rem; }
.lodging-group:last-child { margin-bottom: 0; }
.lodging-group__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    border-bottom: 1px solid var(--color-line);
    padding-bottom: 0.6rem;
    margin-bottom: 1rem;
}
.lodging-group__head h3 { font-weight: 700; font-size: 1.05rem; color: var(--color-heading); }
.lodging-group__head span { font-size: 0.8rem; color: var(--color-muted); }
/* `minmax(0, 1fr)`: a bare `1fr` cannot shrink below its item's min-content, and a lodging item
   is a flex row (mark + body) whose body already carries `min-width: 0` — the flex half of this
   was fixed before, the grid half was not, so the track still refused to go narrow and the list
   ran past a 360px screen from a 24px root upward. Only visible with realistic building names;
   the page renders empty without seeded data, which is why it was missed until the dev database
   was reseeded (2026-08-20). */
.lodging-list { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.1rem; }
.lodging-item { display: flex; gap: 0.85rem; align-items: flex-start; }
.lodging-item__body { min-width: 0; } /* flex children default to min-width:auto — without this,
    a long unbroken word in an admin-entered building description could overflow the row */
.lodging-item__mark {
    width: 2.5rem;
    height: 2.5rem;
    flex: none;
    border-radius: var(--radius-card);
    background: linear-gradient(150deg, #cddca0, #7c9a4a);
}
.lodging-item h4 { font-family: var(--font-heading); font-weight: 700; font-size: 1.02rem; color: var(--color-heading); margin: 0; }
.lodging-item p { font-size: 0.85rem; color: var(--color-text); margin: 0.25rem 0 0; }
.lodging-item__tag {
    display: inline-block;
    margin-top: 0.4rem;
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--color-primary-ink);
    background: var(--color-ok-bg);
    padding: 0.18rem 0.5rem;
    border-radius: 1rem;
    /* "Tillgänglighetsanpassad" is one 23-character compound with no break opportunity in it, so
       `anywhere` rather than `break-word`. It is the widest single token on the public site and
       overflowed a 360px screen from a 24px root up. `max-width` keeps the pill inside its column
       even when the word is broken. */
    max-width: 100%;
    overflow-wrap: anywhere;
}
@media (min-width: 40em) { .lodging-list { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 64em) { .lodging-list { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

/* Quiet non-CTA sign-off — closes a page that already has enough routes into the funnel
   (header CTA + docked mobile CTA) without stacking on a third, heavier one. Bookends the
   page-head's warm tint from the top. See /faciliteter. */
.closing-line { text-align: center; padding: 1rem 0; }
.closing-line p {
    font-family: var(--font-heading);
    font-weight: 400;
    font-size: clamp(1.25rem, 2.6vw, 1.625rem);
    line-height: 1.35;
    color: var(--color-heading);
    max-width: 34ch;
    margin: 0 auto;
}

/* Menu (part of Mat & priser) — sits below a lead .teaser (shared component, reused from
   Home/Facilities), which is the page's only visual anchor; everything from .menu-body down
   is otherwise unchanged. */
.menu-body { margin-top: 2.5rem; }
.menu-cols { display: grid; grid-template-columns: minmax(0, 1fr); gap: 2.5rem; }
@media (min-width: 48em) { .menu-cols { grid-template-columns: 1fr 1fr; gap: 3rem; } }
.menu-col h2 {
    font-weight: 700;
    font-size: 1.25rem;
    margin: 0 0 1rem;
    padding-bottom: 0.5rem;
    border-bottom: 2px solid var(--color-primary);
}
.dish { padding: 0.9rem 0; border-bottom: 1px solid var(--color-line); }
.dish:last-child { border-bottom: 0; }
.dish__top { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.dish h3 { font-family: var(--font-heading); font-weight: 700; font-size: 1rem; margin: 0; }
.dish p { margin: 0.3rem 0 0; font-size: 0.9rem; color: var(--color-muted); }
.meal-veg {
    font-size: 0.66rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-ok);
    background: var(--color-ok-bg);
    padding: 0.15rem 0.5rem;
    border-radius: 1rem;
}
.menu-note { font-size: 0.875rem; color: var(--color-muted); font-style: italic; margin: 1.2rem 0 0; }

/* Data table (prices, cleaning zones) */
.data-table-wrap {
    overflow-x: auto;
    background: var(--color-bg);
    border: 1px solid var(--color-line);
    border-radius: var(--radius-card);
}
.data-table { width: 100%; border-collapse: collapse; font-size: 0.95rem; }
.data-table th {
    text-align: left;
    font-family: var(--font-body);
    font-weight: 700;
    color: var(--color-heading);
    font-size: 0.75rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    padding: 0.7rem 0.9rem;
    border-bottom: 2px solid var(--color-line);
}
.data-table th.num, .data-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
.data-table td { padding: 0.8rem 0.9rem; border-bottom: 1px solid var(--color-line); color: var(--color-text); vertical-align: top; }
.data-table tr:last-child td { border-bottom: 0; }
.data-table__label { color: var(--color-heading); font-weight: 600; }
.data-table__sub { display: block; font-size: 0.8125rem; color: var(--color-muted); margin-top: 0.2rem; }
.data-table__season { color: var(--color-secondary); font-size: 0.85rem; white-space: nowrap; }
.price-note { font-size: 0.875rem; color: var(--color-muted); margin: 0 0 1.25rem; }
.price-block + .price-block { margin-top: 2.5rem; }

/* Legal document pages (integritetspolicy, and the booking terms when they land) — numbered
   sections of prose interleaved with .data-table blocks. The prose column is capped at 65ch for
   readability while the tables run full width, so each section is a block wrapper rather than one
   .prose element around everything. */
/* Block separation reuses .price-block's 2.5rem rather than introducing a second value for the
   same job — two content-page block rhythms 4px apart is exactly the near-duplicate drift the
   duplication rule in design.md exists to prevent. */
.legal-block + .legal-block { margin-top: 2.5rem; }
.legal-block .prose + .data-table-wrap,
.legal-block .data-table-wrap + .prose { margin-top: 1.2rem; }
.legal-draft { margin-bottom: 2.5rem; }
/* An aside that qualifies the table above it (e.g. "the audit log holds no guest data") — set
   apart from the running text so it doesn't read as another body paragraph. */
.prose p.legal-note {
    border-left: 3px solid var(--color-line);
    padding-left: 1rem;
    color: var(--color-muted);
    /* 0.85rem is the house small/caption size (design.md type scale, ~13.5px) — the same value
       .sec-note and the admin table's .sub already use. It was 0.95rem, a fourth muted-note size
       four hundredths off three existing ones. */
    font-size: 0.85rem;
}

/* Contact — a location photo anchor beside two labelled info groups (Kontakta oss / Hitta
   hit), rather than one flat six-row definition list with no visual hierarchy. */
.contact-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 2rem; align-items: start; }
.contact-photo {
    min-height: 16rem;
    border-radius: var(--radius-card);
    position: relative;
    overflow: hidden;
    background: linear-gradient(160deg, #d3d9cf, #8a9a72);
    /* The frame is what survives forced-colors (Windows High Contrast): the gradient is
       suppressed there, so without a border the placeholder became an invisible hole with its
       caption floating loose in it. A real photo at launch fills the box either way. */
    border: 1px solid var(--color-line);
}
@media (min-width: 64em) {
    .contact-grid { grid-template-columns: 0.9fr 1.1fr; gap: 3.5rem; }
    /* A stated shape, not a borrowed one. This was `min-height: 100%`, which made the photo's
       height a by-product of whatever stood in the other column — and when the find-here map
       landed there on 2026-08-19 the column grew by ~400px, so the placeholder became a ~1000px
       slab of empty green and the launch photo silently acquired a ~1:2.2 portrait crop nobody
       had chosen. Almost every photograph of a rural property is landscape. 4:5 is an ordinary
       portrait crop the client can actually satisfy, and .contact-grid's `align-items: start`
       already keeps the column top-aligned, so the photo no longer answers to its neighbour. */
    .contact-photo { aspect-ratio: 4 / 5; min-height: 0; }
}

.contact-group { margin-bottom: 2rem; }
.contact-group:last-child { margin-bottom: 0; }
.contact-group__head { border-bottom: 1px solid var(--color-line); padding-bottom: 0.5rem; margin-bottom: 1rem; }
.contact-group__head h2 {
    /* Matches the documented eyebrow tokens exactly (design.md's typography table), just
       without .eyebrow's own margin-bottom — .contact-group__head's border/padding already
       carries that spacing. */
    font-family: var(--font-body);
    font-weight: 700;
    font-size: 0.75rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--color-primary-ink);
}
.contact-dl { margin: 0; }
.contact-dl dt { font-size: 0.75rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--color-muted); font-weight: 700; margin-top: 1.25rem; }
.contact-dl dt:first-child { margin-top: 0; }
.contact-dl dd { margin: 0.25rem 0 0; font-size: 1.0625rem; color: var(--color-heading); }

/* Flat card — green top-rule, no image (design.md's documented card pattern; matches
   .usecard's treatment). Used both below the Kontakt grid (own margin-top there, scoped so
   it doesn't leak into other contexts) and standalone in its own section (Om oss). */
.cta-card { border-top: 2px solid var(--color-primary); padding-top: 1.35rem; }
.cta-card h2 { font-weight: 400; font-size: 1.5rem; margin: 0 0 0.5rem; }
.cta-card p { font-size: 0.95rem; color: var(--color-text); margin: 0 0 1.25rem; max-width: 52ch; }
.cta-card .cta-links { font-size: 0.9rem; color: var(--color-muted); margin: 1.5rem 0 0; }
.contact-grid + .cta-card { margin-top: 2.5rem; }

/* Reassurance note next to a CTA */
.cta-note { font-size: 0.9rem; color: var(--color-muted); }

/* Find-here map (/kontakt) — inline SVG drawn from OpenStreetMap data. Inline rather than
   <img> precisely so it inherits the tokens and fonts from here; see the comment in
   FindMap.razor for why that is the opposite of the logo rule. Every colour below is an
   existing token — the map introduces no palette of its own. Land is --color-bg-warm and
   water --color-bg, the site's two existing grounds, which is why a map of an archipelago
   needs no blue. */
.findmap-figure { margin: 0 0 1.5rem; }
.findmap-figure__link {
    display: block;
    border: 1px solid var(--color-line);
    border-radius: var(--radius-card);
    overflow: hidden;
    /* Removes the line-height gap under a block-level SVG. */
    line-height: 0;
    transition: border-color var(--transition);
}
.findmap-figure__link:hover { border-color: var(--color-primary-ink); }
.findmap-figure__caption { font-size: 0.85rem; color: var(--color-muted); margin-top: 0.5rem; }

/* Opt the drawing out of forced colours (Windows High Contrast). The map already survives
   there — fill and stroke are not forced-color properties — but that is the spec doing us a
   favour rather than a decision we took. This states the decision: on a map the colours carry
   meaning (land is not water, a road is not a shoreline), so forcing them to a two-colour
   palette would destroy the information. Verified under CDP forced-colors emulation. */
.findmap { display: block; width: 100%; height: auto; forced-color-adjust: none; }
.findmap__land { fill: var(--color-bg-warm); }
/* evenodd lets the islands (closed rings inside the sea polygons) punch back out of the water. */
.findmap__water { fill: var(--color-bg); fill-rule: evenodd; stroke: var(--color-line); stroke-width: 1; }
.findmap__road {
    fill: none;
    stroke: var(--color-secondary);
    stroke-width: 2.5;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.findmap__road--major { stroke-width: 5; }
.findmap__road--minor { stroke-width: 1; opacity: 0.35; }
.findmap__node { fill: var(--color-bg-warm); stroke: var(--color-secondary); stroke-width: 2.5; }
/* A text outline in the land colour, so a label stays legible where it crosses a road or the
   water — and so --color-muted is always measured against --color-bg-warm (5.20:1), never
   against a road. The label's ground is a known one because this rule makes it one. */
.findmap__halo { paint-order: stroke; stroke: var(--color-bg-warm); stroke-width: 3px; stroke-linejoin: round; }
/* These are viewBox units, not type-scale members — they cannot be, because what a label
   actually renders at is the unit times the map's own scale factor. They are picked so that
   product lands on house values: on /kontakt the map is 495–592px wide inside .contact-grid's
   right column, so 0.8125rem renders at 11.5–13.7px (the 12px label / 13.5px caption sizes)
   and 1.4375rem at 20–24px (the ~21px H3).

   Two separate bugs are fixed here, and both were found by measuring rather than reading.
   First the sizes were 11u and 20u, chosen against the widest case alone — at the 1024px
   breakpoint, where the grid turns two-column and the map halves to 495px, that rendered the
   labels at 9.7px, the smallest text on the site, one pixel of viewport away from 19px. So
   size against the *narrowest* rendering, not the widest.

   Then they were 13px and 23px, and absolute px is the second bug: a reader who raises their
   browser's text size scales the whole page except this map. Worse, it scales the map *down* —
   .container's padding is in rem, so the column narrows, the scale factor drops, and a fixed
   px label renders smaller. Measured at a 24px root: the caption grew 13.6 -> 20.4px while the
   labels shrank 13.7 -> 13.0px. In rem the unit tracks the reader's own text, so the ratio
   holds. Do not "simplify" these back to px. Guarded by
   Contact_map_labels_are_sized_in_rem_so_they_follow_the_reader, which asserts the unit rather
   than the behaviour: raising the root font size from a Playwright test does not take effect against
   this app and the cause was never found, so the behavioural version could not be written. The
   collision half is Contact_map_labels_do_not_collide_at_their_largest. */
.findmap__label { font-family: var(--font-body); font-size: 0.8125rem; fill: var(--color-muted); }
.findmap__label--road { font-weight: 700; fill: var(--color-text); }
.findmap__place { font-family: var(--font-heading); font-size: 1.4375rem; fill: var(--color-heading); }
.findmap__region { font-family: var(--font-body); font-size: 0.8125rem; letter-spacing: 0.18em; fill: var(--color-muted); }
.findmap__pin-halo { fill: var(--color-ok-bg); }
.findmap__pin { fill: var(--color-primary-ink); }
.findmap__pin-dot { fill: var(--color-bg-warm); }
.findmap__scale path { fill: none; stroke: var(--color-muted); stroke-width: 1.5; }

/* On a narrow screen the map is only ~310–470px wide and the labels scale down with the viewBox
   to around 10px. The sizes are therefore raised in user units. Raising them alone makes the
   labels collide, so the supporting ones are hidden instead (.findmap__label--aside: the
   region name, the eastward direction, the road's name beneath its number, the scale bar).
   What remains is what a guest actually navigates by: E 18, the direction to Norrtälje, the
   exit, väg 1031 and the property. The address is in the <dl> right below the map either way.

   530px is deliberately NOT the house 480px breakpoint, and this is the one component allowed
   to differ. The house breakpoints mark where the *page* changes column count; this one marks
   where the *map* runs out of room for ten labels, which is a fact about the drawing. Measured:
   the desktop sizes only reach 11px at about a 528px viewport, so between 481 and 528 the map
   showed all ten labels at 10.0–10.9px while the body text beside it was 16px — the only
   fine print on the page, and the extra five labels it bought are exactly the supporting ones
   this block exists to drop. The two numbers coinciding at 480 was a guess, not a decision. */
@media (max-width: 530px) {
    .findmap__label { font-size: 1.1875rem; }
    .findmap__place { font-size: 2.125rem; }
    .findmap__label--aside,
    .findmap__region,
    .findmap__scale { display: none; }
}

/* Utility pages (not found / error) */
.utility { text-align: center; padding: 5rem 0; }
.utility__code { font-family: var(--font-heading); font-size: clamp(3rem, 8vw, 4rem); color: var(--color-primary); line-height: 1; margin: 0; }
.utility h1 { font-weight: 400; font-size: clamp(1.6rem, 4vw, 2rem); margin: 0.75rem 0 0.6rem; }
.utility p { color: var(--color-text); margin: 0 0 1.6rem; }
.utility__actions { display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap; align-items: center; }

/* ── Phase 3: inquiry form, admin booking mgmt, availability calendar ───────── */

/* The label text is an anonymous flex item beside the box, so it will not shrink below its own
   min-content — and these labels are reference data, i.e. names an admin types. A facility named
   "Tillgänglighetsanpassad samlingslokal" pushed /boka 42px past a 360px screen at a 32px root.
   `min-width: 0` lets the text shrink, `anywhere` breaks the compound once it has to. */
.checkbox-row {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.3rem 0;
    font-size: 0.95rem;
    color: var(--color-text);
    cursor: pointer;
    min-width: 0;
    overflow-wrap: anywhere;
}
.checkbox-row input { flex: none; }

/* Alert / message box (reassurance banner, form feedback) */
.alert {
    display: flex;
    gap: 0.75rem;
    padding: 0.9rem 1rem;
    border-radius: var(--radius-card);
    font-size: 0.95rem;
    border: 1px solid transparent;
}
/* Round badge so the glyph reads as an info/error indicator, not stray punctuation (a bare bold
   "i" was mistaken for an upside-down exclamation mark). Italic "i" = the conventional info mark. */
.alert__ic {
    flex: none;
    align-self: start;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.3rem;
    height: 1.3rem;
    border-radius: 50%;
    font-size: 0.82rem;
    font-weight: 700;
    font-style: italic;
    line-height: 1;
}
.alert b { color: var(--color-heading); }
/* The glyph is #fff on all three variants, as .alert--warn already had it. Info and error
   used to set `color: var(--color-*-bg)` — but those tokens are the translucent *tints*
   (9–10% alpha), so the glyph was drawn at one-tenth opacity of a colour barely distinct
   from the solid circle behind it: composited, roughly 1.3:1. The "i" on the public
   inquiry form's reassurance banner was invisible, which is the exact failure the round
   badge was introduced to prevent — design.md records that a bare "i" had been mistaken
   for stray punctuation, so it was given a filled badge to read as an indicator. It then
   stopped reading as anything at all. Found by measuring every rule that sets both a
   colour and its own background (2026-08-11 design-system audit). */
.alert--info { background: var(--color-info-bg); border-color: var(--color-info-line); color: var(--color-info); }
.alert--info .alert__ic { background: var(--color-info); color: #fff; }
.alert--error { background: var(--color-err-bg); border-color: var(--color-err-line); color: var(--color-err); }
.alert--error .alert__ic { background: var(--color-err); color: #fff; font-style: normal; }
.alert--warn { background: var(--color-warn-bg); border-color: var(--color-warn-line); color: var(--color-warn); }
.alert--warn .alert__ic { background: var(--color-warn); color: #fff; font-style: normal; }
/* Bulleted items inside an alert (e.g. multiple approval warnings) */
.alert__list { margin: 0.35rem 0 0; padding-left: 1.1rem; }
.alert__list li { margin-bottom: 0.25rem; }
.alert__list li:last-child { margin-bottom: 0; }

/* Admin save/action feedback. Rendered as a fixed toast (bottom-right) so it is always in the
   viewport regardless of scroll position — previously it sat at the top of the page and a save
   made far down (e.g. payment status) gave feedback the admin had to scroll up to see. The base
   style is neutral (not green) and reports a completed action; a failure uses .form-status--error
   below. Until 2026-08-10 there was no error variant at all — both outcomes shared this one style,
   so "kan inte tas bort eftersom den används av en bokning" looked exactly like "har sparats". */
.form-status {
    position: fixed;
    right: 1.25rem;
    bottom: 1.25rem;
    z-index: 1000;
    max-width: min(30rem, calc(100vw - 2rem));
    margin: 0;
    padding: 0.85rem 1.1rem;
    color: var(--color-heading);
    font-weight: 600;
    background: var(--color-bg-warm);
    border: 1px solid var(--color-line);
    border-left: 4px solid var(--color-primary);
    border-radius: var(--radius-card);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.16);
    animation: form-status-in 0.28s ease-out;
}
/* A failed or blocked action. Same position and size — only the colour changes, so the two are
   told apart at a glance without the toast moving around. Reuses the .alert--error palette via the
   shared tokens rather than restating it. This variant does not auto-dismiss (see Toast.razor). */
.form-status--error {
    /* Only this variant carries a second child (the dismiss button), so only it needs a layout
       mode; the success toast stays a plain block with a single text node. */
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    color: var(--color-err);
    /* Opaque, unlike .alert--error's translucent --color-err-bg. This toast is position:fixed and
       floats over arbitrary page content, so a 9%-alpha ground lets rows and text show through it.
       #ebdbcf is exactly that tint flattened onto the toast's own --color-bg-warm base, so it looks
       as intended and stops depending on what scrolls underneath (text contrast 6.24:1). */
    background: #ebdbcf;
    border-color: var(--color-err-line);
    border-left-color: var(--color-err);
}
/* Dismiss control — only the error variant has one, because only it stays put. Follows
   .admin-panel__close (the existing dismiss idiom): bare button, hover shifts the colour rather
   than adding a fill. Keyboard focus comes from the global button:focus-visible rule (NFR-38). */
.form-status__close {
    flex: 0 0 auto;
    margin: -0.15rem -0.35rem 0 0;
    padding: 0.15rem 0.3rem;
    color: inherit;
    font-size: 1.3rem;
    line-height: 1;
    background: none;
    border: 0;
    cursor: pointer;
}
.form-status__close:hover { color: var(--color-err); }

@keyframes form-status-in {
    from { opacity: 0; transform: translateY(0.6rem); }
    to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) { .form-status { animation: none; } }

.action-panel {
    border: 1px solid var(--color-line);
    border-radius: var(--radius-card);
    padding: 1rem;
    margin: 1rem 0;
    background: var(--color-bg-warm);
}
.action-panel h3 { font-family: var(--font-body); font-weight: 700; font-size: 0.95rem; margin: 0 0 0.6rem; }
.action-panel label { display: block; font-weight: 700; font-size: 0.82rem; color: var(--color-heading); margin-bottom: 0.3rem; }
.action-panel textarea, .action-panel input, .action-panel select {
    width: 100%; font: inherit; font-size: 0.875rem;
    padding: 0.45rem 0.55rem; margin-bottom: 0.7rem;
    border: 1px solid var(--color-line); border-radius: var(--radius-sm); background: #fff;
}
.action-panel textarea { min-height: 4rem; resize: vertical; }
.action-panel textarea:focus, .action-panel input:focus, .action-panel select:focus {
    outline: 2px solid var(--color-primary-ink); outline-offset: 1px; border-color: transparent;
}

/* Availability calendar (direction A) */
.cal-legend { display: flex; gap: 1.4rem; flex-wrap: wrap; margin: 0 0 1.4rem; font-size: 0.875rem; color: var(--color-text); }
.cal-legend > span { display: inline-flex; align-items: center; gap: 0.5rem; }
.cal-swatch { display: inline-block; width: 1rem; height: 1rem; border-radius: 3px; border: 1px solid rgba(0, 0, 0, 0.08); }
.cal-free { background: #dcecc4; }
.cal-partial { background: #f6e2a8; }
.cal-full { background: #eccdc7; }

.cal-selection {
    position: sticky;
    /* Sticks directly below the public header. `Calendar.razor` loads `js/sticky-offsets.js`
       so this is the header's real height; without it the `rem` fallback applies, which is
       exact at a default text size and close otherwise. */
    top: var(--header-h);
    z-index: 5;
    display: flex;
    align-items: center;
    gap: 1rem;
    flex-wrap: wrap;
    background: var(--color-bg-warm);
    border: 1px solid var(--color-line);
    border-radius: var(--radius-card);
    padding: 0.9rem 1.1rem;
    margin-bottom: 1.5rem;
}
.cal-selection[hidden] { display: none; }

/* On a phone the top-sticky bar cost 124px directly under the 73px header — 197px, ~30% of a
   640px viewport, consumed while the user is trying to read the grid. Docked to the bottom
   instead: off the reading area and inside thumb reach. */
@media (max-width: 40em) {
    /* Qualified with the element on purpose: `.section p` (0,1,1) outranks a bare
       `.cal-selection` (0,1,0) and its 1rem bottom margin survives. `bottom: 0` positions the
       *margin* box, so that margin lifted the docked bar 16px off the screen edge. */
    p.cal-selection {
        position: fixed;
        top: auto;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 90;
        margin: 0;
        border-radius: 0;
        border-left: none;
        border-right: none;
        border-bottom: none;
        gap: 0.6rem 1rem;
        padding: 0.75rem 1rem calc(0.75rem + env(safe-area-inset-bottom, 0px));
        box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.08);
    }
    /* Range text takes its own line; the actions sit together beneath it. */
    .cal-selection__label { flex: 1 0 100%; }
    .cal-selection > .btn { flex: 1 1 auto; }

    /* Reserve room so the docked bar never covers the last week of the final month. */
    .cal-months { padding-bottom: 7rem; }
}

.cal-months { display: grid; grid-template-columns: minmax(0, 1fr); gap: 2rem 2.5rem; }
@media (min-width: 45em) { .cal-months { grid-template-columns: 1fr 1fr; } }
.cal-month { margin: 0; }
.cal-month h2 { font-weight: 700; font-size: 1.1rem; margin: 0 0 0.75rem; }

.cal-grid { width: 100%; border-collapse: collapse; table-layout: fixed; }
.cal-grid th {
    text-align: center;
    font-size: 0.7rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-muted);
    font-weight: 700;
    padding: 0.35rem 0;
}

.cal-day, .cal-empty {
    height: 2.75rem;
    text-align: center;
    vertical-align: middle;
    border: 2px solid var(--color-bg);
    border-radius: 4px;
    font-size: 0.9rem;
}
.cal-day { color: var(--color-heading); }
.cal-day.cal-partial { color: #6b5310; }
.cal-day.cal-full { color: #6b3229; } /* darker than the pink bg for AA contrast */
.cal-past { background: var(--color-bg-warm); color: #6b665c; } /* darkened from #bdb7ac (1.7:1) to ~4.8:1 for AA */

.cal-day a { display: block; line-height: 2.75rem; margin: -2px; border-radius: 4px; color: inherit; text-decoration: none; }
.cal-day a:hover { box-shadow: inset 0 0 0 2px var(--color-primary-ink); }

/* Selected range (JS adds .cal-selected to the day <td>). */
.cal-day.cal-selected { background: var(--color-primary); }
.cal-day.cal-selected a { color: var(--color-heading); font-weight: 700; }
