/* ============================================================================
 * takt-shell.css — SMM's own chrome layer over DsAppShell (Takt slice 5, Task 2).
 *
 * Every selector here is one of SMM's OWN class names (.takt-*). The `ds-c-*`
 * namespace belongs to Ds.DesignSystem.Blazor and is never written to from this
 * repo — DesignSystemVocabularyTests fails the build on a new one.
 *
 * Every value is a --ds-* token. No hex, no px for a design value, no font
 * literal — and every --ds-* NAME mentioned below is one the packed RCL actually
 * emits, with no raw fallback standing in for it. Both halves are enforced by
 * TaktShellCssTests, which greps this file against ds-takt-light.css inside the
 * nupkg. A name that does not exist is silently inert in CSS; a fallback is what
 * hides it.
 *
 * THE ONE EXCEPTION, AND WHY. Two custom properties the design system owns are
 * redefined here as LAYOUT knobs:
 *
 *   --ds-appShell-bottomNavHeight   the height the bottom-nav row reserves
 *   --ds-sideNav-width              the nav's own width inside the sidebar column
 *
 * sideNav-width is a property DsSideNav reads through a fallback
 * (`width: var(--ds-sideNav-width, 280px)`) — the seam it has. bottomNavHeight has
 * NONE, and is the honest overreach; gaps-T2.md records it as such against
 * design-system#522 rather than claiming the exemption quietly. It is set from CSS
 * rather than from a parameter for exactly one reason: a parameter writes an inline
 * style on the root, and an inline style cannot carry a media query. Above the md
 * rung there is no bar, so the row must reserve nothing; below it, a bar's height.
 *
 * The list used to be four. Adopting DsAppShell.Static retired two of them:
 * sidebarWidth no longer varies with the viewport (the Static drawer takes the panel
 * out of the flow itself, so nothing has to collapse the column) and is set through
 * the documented `SidebarWidth` parameter now; borderWidth no longer has to be zeroed
 * below the rung for the same reason, and its default already is the hairline.
 *
 * A THIRD AND FOURTH, AND WHY THEY ARE DIFFERENT. `--ds-appShell-topbar`, its ink
 * counterpart `--ds-appShell-onTopbar` and `--ds-appShell-border` are also redefined
 * on `.takt-shell`, SMM's own class on the shell root (owner, round 1:
 * "so much white on white" — the topbar and the page ground behind it were both
 * a near-white the theme generated, so the chrome never separated from the
 * content; owner, round 2, TAKT-256: the round-1 fix repainted the bar
 * `primarySubtle`, a pink brand tint — rejected outright, "fix it so we don't
 * have a pink tint in the topbar. I want the same colour as in the wireframe" —
 * the wireframe's own `.pbar` bar, `#171224` light / `#0D0A18` dark). Unlike the
 * two above these ARE theme values, not layout — and DsAppShell's own doc
 * comment says so is fine: "overriding other --ds-appShell-* names works but is
 * theme, not layout." Both sides still resolve through --ds-color-* tokens
 * (inverseSurface — the design system's own night-panel colour, `#241E3F`
 * light / `#120E22` dark, a near-exact match for the wireframe bar with no hex
 * of our own; outline for the hairline, not outlineVariant — see the topbar
 * rule below for the measured ratios and why outline is a redundant nicety in
 * light and the one thing holding the seam together in dark), so the theme
 * itself is still exactly what the design system generated; only which slot of
 * it this one shell instance's topbar paints with is redirected. Going dark
 * also means every control the topbar draws now sits on light-on-dark ground
 * instead of dark-on-light — see the topbar section below for the full
 * ink/border re-pairing that took.
 *
 * Breakpoints — TWO rungs, two jobs (design-system spec/responsive-contract.md §1; app
 * issue #422, 2026-09-06). One spelling of each edge, the DS's own `.98` partner rung:
 *
 *   sm  `max-width: 599.98px`  the CHROME collapses. The topbar and the public header fold
 *       their utility controls (help, feedback, DA|EN, theme, the club page's section
 *       anchors) into ONE ⋯ overflow panel and keep a single row of 44px controls — mark,
 *       search, ⋯, avatar. This is the phone. It used to happen at 900, which handed an
 *       820px tablet the phone bar with 430px unused (and the bar still wrapped at 390).
 *   md  `max-width: 899.98px`  the SHELL's own drawer rung — DsAppShell's `CollapseAt="Md"`
 *       (TaktShell.razor; app issue #448). Below it the sidebar becomes the drawer, the bottom
 *       tab bar appears, the search pill sheds its label and the platform wordmark gives way to
 *       its badge; every control stays ON the bar. This is the tablet. It used to be the DS
 *       default `Sm` (599.98px), which handed a 768/820 portrait tablet a 280px in-flow rail
 *       and 64% of the screen for content — the header collapsed at md (#422) but the rail did
 *       not move with it, and the peek row's gutter bleed then pushed `main` 31px sideways at
 *       768 (#441). One rung for the whole chrome, the DS's own.
 *   lg  `max-width: 1199.98px`  the wordmark alone. design-system#2126: DsAppBar's search
 *       pill paints ~64px left of its own layout box in the 1024–1199 band, over the tail of
 *       "Simple Member Management" (the logo slot is 320px, the label 288px — no layout
 *       collision, a paint one). Until the pill honours its box the label is hidden one rung
 *       earlier than the md block below would; harvest: delete the lg block when #2126 ships.
 * ============================================================================ */

/* Wide content scrolls in its own container; the document never does. */
body {
    overflow-x: hidden;
}

/* ── The shell root ─────────────────────────────────────────────────────── */

.takt-shell {
    /* No bottom bar above the md rung, so its row reserves nothing. Without this the
       grid keeps a bar-height strip of dead space at the foot of every desktop
       page — and the floating slot's own bottom offset, which sums this token,
       lifts the FAB by a bar height that is not there. */
    --ds-appShell-bottomNavHeight: 0px;
}

/* The chat FAB, rehomed into DsAppShell's floating slot. The slot already sits
   above the bottom-nav row (the grid reserves it), so the FAB must stop
   positioning itself — two fixed positions would double-count the bar. */
.takt-shell__fab .chat-fab {
    position: static;
}

/* ── Content column (TAKT-220) ─────────────────────────────────────────────
 * Every signed-in page used to render full-bleed to the pane edge — this is the
 * one shell-level fix. .takt-content wraps ChildContent INSIDE DsAppShell's own
 * <main> (TaktShell.razor); it is SMM's own div, not a `ds-c-*` one, so this
 * stays a composition choice around the package rather than a rewrite of it.
 *
 * The cap is --ds-layout-contentMaxWidthStandard (1120px). It used to be a raw
 * 1120px literal: the theme's content-measure ladder only had Narrow 768 /
 * 1024 / Wide 1280 / Widest 1440, none near the wireframe's measured ~1120px,
 * so a fifth rung was requested upstream rather than guessed or forked here.
 * DS 1.6.0 shipped it as "Standard"; the literal became the token the day the
 * submodule moved past that release.
 *
 * Horizontal-only: DsAppShell's own <main> padding (--ds-appShell-mainPadding,
 * defaulting to --ds-space-lg) already gives every side breathing room against
 * the shell chrome; this rule's padding-inline is the COLUMN's own gutter, the
 * one that stays even once the cap has room to act (viewports well past
 * 1120px + 2 * space-lg). Below that width it still reads as "some space to the
 * right and left of the fields" — the customer's own words re-confirmed
 * 2026-08-27 with prod-vs-wireframe screenshots. */
.takt-content {
    display: block;
    inline-size: 100%;
    max-inline-size: var(--ds-layout-contentMaxWidthStandard); /* 1120px — the rung landed in DS 1.6.0; see note above. */
    margin-inline: auto;
    padding-inline: var(--ds-space-lg);
}

/* The opt-out (TAKT-220). A page that genuinely needs the full column width —
 * today only the Kanban board (ProjectBoard.razor): several fixed-width
 * DsKanban columns need room to breathe, and the board already scrolls
 * horizontally INSIDE itself the same way DsTable/the timetable grid do, so
 * capping the column just adds a second, redundant scroll — puts
 * `takt-page--wide` on its own root element. `:has()` reaches from the wrapper
 * this file owns to a child the PAGE owns, so no page has to know `.takt-content`
 * exists to opt out of it. */
.takt-content:has(> .takt-page--wide) {
    max-inline-size: none;
}

/* The data rung (TAKT-322, #342 → TAKT-337, owner 2026-09-03). A screen whose
 * subject IS a wide table — the members register, the payments book, the
 * workshops lists, the month calendar — first moved from Standard to the
 * theme's Wide measure (1280px), and the owner's verdict on a big monitor was
 * the same complaint one rung later: "make the tables wider — potentially all
 * the way to the side of the screen". So the data rung is UNCAPPED now, the
 * same as `--wide` above: the register grows with the viewport, and what keeps
 * it breathing at the edges is `.takt-content`'s own padding-inline gutter —
 * the padding his annotated screenshot drew, which survives at every width.
 * The class stays distinct from `--wide` because the two still mean different
 * things (a wide TABLE that wraps its own scroll vs. a board that scrolls
 * itself); if a capped data measure ever comes back, only this rule moves. */
.takt-content:has(> .takt-page--data) {
    max-inline-size: none;
}

/* ── Nav glyphs ─────────────────────────────────────────────────────────── */

/* `em`, not px and not --ds-size-icon-*: the icon-size ramp is SCSS-only and
   never reaches the custom properties, so `em` is the one binding available that
   still tracks the brand's type scale — the glyph grows with its label. */
.takt-nav__glyph {
    inline-size: 1.15em;
    block-size: 1.15em;
    flex: 0 0 auto;
    fill: none;
}

/* ── Sidebar ────────────────────────────────────────────────────────────── */

/* The rail is ALWAYS the brand's night panel (wireframe; DS #556's inverse tone, wired in
   TaktShell/TaktSideNav). Since DS #585 the club chip and the who-am-I foot ride INSIDE
   DsSideNav (Header/Footer slots), so the rail's own inverse cascade re-points the generic
   tokens this chrome reads (--ds-color-onSurface[Variant], --ds-sideNav-itemHover/itemActive,
   --ds-color-outlineVariant, --ds-color-focusRing) — nothing below names an inverse token,
   and the wrapper paints no background: the aside is its only child and paints itself. */
.takt-sidenav {
    display: flex;
    flex-direction: column;
    min-block-size: 100%;
    /* DsSideNav writes `width: var(--ds-sideNav-width, 280px)` as an inline style, so the
       only way to make it follow its container — which the drawer below deliberately makes
       narrower than 280px on a small phone — is to define the property it reads. */
    --ds-sideNav-width: 100%;
    /* TAKT-222: a sideNav item used to resolve to ~41px here, under the 44px touch baseline,
       and this wrapper redefined --ds-sideNav-itemPaddingY to push it over. DS 1.6.0 gave the
       item its own --ds-sideNav-itemMinBlockSize floor (44px by default), so the component
       clears the baseline unaided and the override is gone. */
    /* TAKT-222 — kill the raw, always-visible OS scrollbar gutter beside the rail. Standard
       CSS Scrollbars Level 1 properties are INHERITED, so setting them on our own ancestor
       class reaches DsSideNav's own internal scrolling box with no `.ds-c-*` rule of our own
       (DesignSystemVocabularyTests forbids one). "thin", not "none": a dozen admin
       destinations can genuinely outgrow a short viewport, and the rail should say so rather
       than hide the fact it scrolls. */
    scrollbar-width: thin;
    scrollbar-color: var(--ds-color-outlineVariant) transparent;
}

.takt-sidenav__club {
    display: flex;
    align-items: center;
    gap: var(--ds-space-sm);
    /* D10 — 44px touch floor. */
    min-block-size: 2.75rem;
    padding: var(--ds-space-xs) var(--ds-space-sm);
    border-radius: var(--ds-radius-md);
    /* Generic ink, not an inverse token: the rail's tone re-points it (DS #585). */
    color: var(--ds-color-onSurface);
    font-family: var(--ds-typography-label-font-family);
    font-size: var(--ds-typography-label-font-size);
    font-weight: var(--ds-typography-label-font-weight);
    cursor: pointer;
    list-style: none;
}

.takt-sidenav__club::-webkit-details-marker {
    display: none;
}

.takt-sidenav__club--static {
    cursor: default;
}

.takt-sidenav__club:hover {
    background-color: var(--ds-sideNav-itemHover);
}

.takt-sidenav__club:focus-visible {
    outline: var(--ds-border-focusRing-width) solid var(--ds-color-focusRing);
    outline-offset: var(--ds-border-focusRing-offset);
}

.takt-sidenav__mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    inline-size: 1.75rem;
    block-size: 1.75rem;
    flex: 0 0 auto;
    border-radius: var(--ds-radius-full);
    background-color: var(--ds-color-primary);
    color: var(--ds-color-onPrimary);
    font-size: var(--ds-typography-labelSmall-font-size);
    font-weight: var(--ds-typography-label-font-weight);
}

.takt-sidenav__clubname {
    flex: 1 1 auto;
    min-inline-size: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.takt-sidenav__chevron {
    flex: 0 0 auto;
    color: var(--ds-color-onSurfaceVariant);
}

/* TAKT-250 finding C6 — the drawer's own close button (TaktSideNav.razor, the Header
   slot's first child). Present in the markup at every width, shown only below the md rung
   (the media block further down) — above that the rail is a permanent sidebar with nothing
   to close, the same gate the shell's own hamburger is under. */
.takt-sidenav__close {
    display: none;
}

/* ── The ⋯ overflow (app issue #422; design-system#1326) ──────────────────────────────
 * ONE panel, in both chromes, holding the utility controls a phone bar cannot fit. The
 * anatomy is the one DsAppShell.Static's own hamburger uses: a checkbox that fills a 44px
 * <label>, no script needed to open or close it, and the state readable by CSS.
 *
 * WHY THE CONTROLS ARE NOT RENDERED TWICE. Above the sm rung the panel is `display:
 * contents`, so its children ARE the bar's own flex items, in the order they always had;
 * below it the same elements become the rows of a dropdown. One DOM for both shapes: a
 * theme toggle, a help form and a DA|EN switcher that existed twice would have been two
 * `[data-theme-toggle]`s for theme.js and every test locator to disambiguate, and two
 * forms posting the same preference.
 *
 * design-system#1326 — DsAppBar has no narrow-width overflow for its Actions slot, and
 * DsMoreMenu's static disclosure takes item MODELS, so it cannot host a segmented control
 * or a form. This is that issue's cited stand-in: when the DS ships the slot-level overflow,
 * the four `.takt-more*` rules below and the toggle markup in TaktTopbar / TaktPublicNav
 * go, and only the controls stay. */
/* `label.` on purpose: the toggle also wears its host bar's control class (.takt-topbar__action
   / .takt-publicnav__link, defined later in this file) for its size, ink and hover, and that
   class sets `display: inline-flex` at the same specificity — the element name is what lets
   "hidden above sm" win the cascade without an !important. */
label.takt-more__toggle {
    /* Hidden above sm: the panel's children sit inline there and there is nothing to open. */
    display: none;
    position: relative;
}

/* The checkbox IS the control — it fills the label so the pointer target is the whole 44px
   box (the DS's own nav-checkbox measures 40x40 the same way) and keyboard focus lands on a
   real form control with a real checked state, which is what a screen reader announces. */
.takt-more__state {
    position: absolute;
    inset: 0;
    inline-size: 100%;
    block-size: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}

.takt-more {
    display: contents;
}

.takt-sidenav__clubs[open] .takt-sidenav__chevron {
    transform: rotate(180deg);
}

/* Inside the rail the surfaceContainer family is NOT re-pointed by the tone cascade (it
   would pair a light panel with the re-pointed near-white ink), so the panel and its hover
   take the sideNav item steps — the same raised-surface ramp the DS's own company strip
   uses, correct under either tone. */
.takt-sidenav__clublist {
    display: flex;
    flex-direction: column;
    margin-block-start: var(--ds-space-xxs);
    padding: var(--ds-space-xxs);
    border: var(--ds-border-width-thin) solid var(--ds-color-outlineVariant);
    border-radius: var(--ds-radius-md);
    background-color: var(--ds-sideNav-itemHover);
}

.takt-sidenav__clubitem {
    display: flex;
    align-items: center;
    min-block-size: 2.75rem;
    padding: var(--ds-space-xs) var(--ds-space-sm);
    border-radius: var(--ds-radius-sm);
    color: var(--ds-color-onSurface);
    font-size: var(--ds-typography-bodySmall-font-size);
    text-decoration: none;
}

.takt-sidenav__clubitem:hover {
    background-color: var(--ds-sideNav-itemActive);
    text-decoration: underline;
}

.takt-sidenav__clubsep {
    margin: var(--ds-space-xxs) 0;
    border: 0;
    border-block-start: var(--ds-border-width-thin) solid var(--ds-color-outlineVariant);
}

.takt-sidenav__nav {
    flex: 1 1 auto;
    min-block-size: 0;
}

/* Layout only — the DS #585 Footer slot already carries the divider and padding. */
.takt-sidenav__foot {
    display: flex;
    flex-direction: column;
    gap: var(--ds-space-xs);
}

/* Who you are, and which hat you are wearing — the React chrome's own sidebar foot
   (TaktShell.tsx, .takt-sidebar__foot). No count badges beside the nav entries: SMM has no
   seam that produces them (see gaps-T2.md). */
.takt-sidenav__me {
    display: flex;
    align-items: center;
    gap: var(--ds-space-xs);
    min-inline-size: 0;
}

.takt-sidenav__meta {
    display: flex;
    flex-direction: column;
    min-inline-size: 0;
}

/* The name keeps `label` (14px/700) — it was already the one emphasised line here. What was
   missing was anything BELOW it stepping down: see the footlink rule at the foot of this block. */
.takt-sidenav__mename {
    overflow: hidden;
    color: var(--ds-color-onSurface);
    font-family: var(--ds-typography-label-font-family);
    font-size: var(--ds-typography-label-font-size);
    font-weight: var(--ds-typography-label-font-weight);
    line-height: var(--ds-typography-label-line-height);
    text-overflow: ellipsis;
    white-space: nowrap;
}

.takt-sidenav__merole {
    color: var(--ds-color-onSurfaceVariant);
    font-family: var(--ds-typography-caption-font-family);
    font-size: var(--ds-typography-caption-font-size);
    line-height: var(--ds-typography-caption-line-height);
}

/* TAKT-309 — the rule between "who you are" and the site furniture below it. Same shape as
   .takt-sidenav__clubsep one panel up, for the same reason: the rows under it are not more
   values of the thing above. It inherits the rail's Tone="Inverse" re-point of
   --ds-color-outlineVariant, so it is correct on the night ramp without a colour of its own. */
.takt-sidenav__footsep {
    inline-size: 100%;
    margin: 0;
    border: 0;
    border-block-start: var(--ds-border-width-thin) solid var(--ds-color-outlineVariant);
}

/* The privacy link and the build stamp are one group — a link and its own small print — not two
   unrelated rows a full --ds-space-xs apart. */
.takt-sidenav__footlinks {
    display: flex;
    flex-direction: column;
    gap: var(--ds-space-xxs);
    min-inline-size: 0;
}

.takt-sidenav__footlink {
    /* Mobile audit C6 — the footer link measured 303x21, well under the 44px baseline.
       inline-flex + min-block-size gives the whole row a tall tap target while the text
       itself stays the small caption size the foot already uses. */
    display: inline-flex;
    align-items: center;
    min-block-size: 2.75rem;
    color: var(--ds-color-onSurfaceVariant);
    /* TAKT-309: caption, not bodySmall. At 14px this link was the same size as the member's own
       NAME two rows up, which is most of why the foot read as four undifferentiated lines — the
       one thing that identifies you and a standing legal link had identical weight in the eye.
       12px is the step the role line and the build stamp beside it already sit on, so the block
       now has exactly one emphasised line and three quiet ones. The 44px tap target above is
       untouched: it is the ROW that is tall, never the type. */
    font-size: var(--ds-typography-caption-font-size);
    line-height: var(--ds-typography-caption-line-height);
    text-decoration: none;
}

.takt-sidenav__footlink:hover {
    text-decoration: underline;
}

/* ── Topbar ─────────────────────────────────────────────────────────────── */

/* TAKT-256 — the wireframe's own night-navy bar (owner: "I want the same colour as in the
   wireframe" — see the shell-root comment above for the round-1/round-2 history).
   --ds-color-inverseSurface is the design system's own expression of that colour (the
   "Takt night panel — the dark surface used on the light theme", already load-bearing for
   the sidebar's Tone="Inverse" rail and DsCard's variant="inverse" — never a hex of our
   own): `#241E3F` light, `#120E22` dark, against the wireframe's measured `#171224` /
   `#0D0A18`. DsAppShell documents --ds-appShell-* as a themable slot a consumer may
   override on its own class ("overriding other --ds-appShell-* names works but is theme,
   not layout" — DsAppShell.razor.cs) — that is the sanctioned seam used here, not an
   override of a foundational --ds-color-* value, and it re-tints only this shell
   instance's topbar/border, leaving every other --ds-color-inverseSurface consumer alone.

   The border: --ds-color-outline, unchanged from round 1, but re-measured against the new
   ground. It is the one token in reach that cannot clear 3:1 against BOTH the dark bar and
   the light page ground at once (no flat colour can — the two are ~14.6:1 apart in light
   mode, so nothing sits in the narrow band that reads 3:1 against a near-black AND a
   near-white surface simultaneously):
     - light: 5.69:1 against the page ground it meets · 2.57:1 against the bar itself.
       The shortfall is cosmetic, not a masking failure — the bar and the ground it sits on
       already separate by 14.65:1 on their own fills, so the hairline is a finishing touch
       here, not the thing carrying the seam.
     - dark: 8.59:1 against the bar · 7.56:1 against the page ground. This is the case that
       matters: --ds-appShell-topbar and --ds-appShell-container render within 1.14:1 of
       each other in dark mode (both are near-black night-ramp steps), so the border is the
       ENTIRE separation between chrome and content there, and outline clears the 3:1
       non-text floor on both sides with room to spare.
   Every ink/border pairing INSIDE the bar (crumb, mark, search pill, actions, avatar) sits
   entirely on the inverseSurface ground rather than spanning it, so those use
   onInverseSurfaceVariant instead — it clears 7.26:1 (light) / 8.59:1 (dark) against
   inverseSurface where outline only reaches 2.57:1 in light; see each rule below for its
   own measured ratio. */
.takt-shell {
    --ds-appShell-topbar: var(--ds-color-inverseSurface);
    /* The ink counterpart to the ground above, and the reason this shell can use
       DsAppShell.Static's built-in hamburger at all. The topbar ROW's `color` is what the
       toggle inherits, and its default is the core on-surface role — which in the takt LIGHT
       theme is the very same hex as inverseSurface, so a bar that re-points its ground without
       re-pointing this draws an invisible, 1.00:1 hamburger. onInverseSurfaceVariant is the ink
       every other control in this bar already uses (7.26:1 light / 8.59:1 dark against
       inverseSurface — see the crumb/action rules below), so the toggle now matches its
       neighbours rather than merely being visible. */
    --ds-appShell-onTopbar: var(--ds-color-onInverseSurfaceVariant);
    --ds-appShell-border: var(--ds-color-outline);
    /* DS 1.11.0's two Static-toggle seams, both needed because this bar re-points its ground
       to an inverse surface: the default hover is a BLACK overlay tint (wrong direction on a
       dark bar — a hover should raise it), so it points at the raised inverse tone; and the
       default focus ring is tuned against the theme's own topbar, not this inverse one, so
       the ring borrows the bar's own ink family (7.26:1 light / 8.59:1 dark against
       inverseSurface, same pairing as every control in the bar). */
    --ds-appShell-toggleHover: var(--ds-color-inverseSurfaceRaised);
    --ds-appShell-toggleFocusRing: var(--ds-color-onInverseSurfaceVariant);
}

/* TAKT-269 — the bar is a DsAppBar now, so the flex row that used to live here is the
   component's. What remains is the set of component-token redirects needed to make DsAppBar's
   own chrome disappear inside DsAppShell's topbar row, which already paints the bar: DsAppBar
   is designed to BE the page's top bar (its own ground, hairline, shadow, padding and sticky
   position), and here it is a passenger in one. Each redirect below is the sanctioned
   per-instance component-token seam (the same one --ds-segmentedControl-* and --ds-avatar-*
   use further down), never a --ds-color-* override. */
.takt-topbar {
    /* Without this the bar paints --ds-appBar-container (#FFFFFF in light) OVER the shell's
       navy --ds-appShell-topbar. */
    --ds-appBar-container: var(--ds-color-inverseSurface);
    --ds-appBar-onContainer: var(--ds-color-onInverseSurface);
    /* The shell's header already draws the hairline; DsAppBar's would be a second one. */
    --ds-appBar-borderWidth: 0;
    /* DsAppBar carries a drop shadow the shell's flat topbar row does not. */
    --ds-appBar-elevation: none;
    /* 14px on top of the shell row's own 8px padding-inline would inset every cell to 22px. */
    --ds-appBar-paddingX: 0px;
    --ds-appBar-gap: var(--ds-space-sm);
}

/* Ours, inside DsAppBar's Nav cell (`flex: 1`): the pill is right-aligned within it, which is
   what the removed __spacer div used to do in the old flat row. */
.takt-topbar__searchcell {
    display: flex;
    justify-content: flex-end;
    min-inline-size: 0;
}

/* TAKT-261 — the platform lockup, now the whole of the topbar's left half at every width (the
   breadcrumb this used to sit beside is gone; the page's own DsPageHeader H1 says where you are
   now, one line lower, top-left of the white canvas). Visible at every width, unlike the club
   mark it replaces: nothing else in the chrome duplicates the platform's own name the way the
   sidebar's switcher duplicated the club's, so there is no "reads as a second header" reason to
   hide it above 900px. TAKT-256 — onInverseSurface: 8.95:1 light / 16.41:1 dark against
   inverseSurface, both well past the 4.5:1 body-text floor. */
.takt-topbar__mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--ds-space-xs);
    /* D10's 44x44 floor, same as every other topbar control — load-bearing below 900px, where
       the label is hidden and this is a bare icon-shaped badge. */
    min-inline-size: 2.75rem;
    min-block-size: 2.75rem;
    color: var(--ds-color-onInverseSurface);
    font-family: var(--ds-typography-heading5-font-family);
    font-size: var(--ds-typography-heading5-font-size);
    font-weight: var(--ds-typography-heading5-font-weight);
    text-decoration: none;
}

/* Untouched by TAKT-256: primary/onPrimary is its own solid fill, independent of whatever the
   bar behind it paints. It still needs to read as a distinct shape against the new ground,
   which it does — primary-on-inverseSurface measures 3.48:1 (light) / 5.26:1 (dark), clearing
   the 3:1 floor for a graphical object's boundary. */
.takt-topbar__markbadge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    inline-size: 1.75rem;
    block-size: 1.75rem;
    flex: 0 0 auto;
    border-radius: var(--ds-radius-full);
    background-color: var(--ds-color-primary);
    color: var(--ds-color-onPrimary);
    font-size: var(--ds-typography-labelSmall-font-size);
}

.takt-topbar__marklabel {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* TAKT-256 — a deliberate call, not an accident: the pill keeps its own opaque
   surfaceContainer fill and onSurfaceVariant ink UNCHANGED (both are self-contained pairs —
   ink against the pill's own fill, DS-vetted, correct in either theme regardless of what the
   bar behind it paints), so in light mode this stays a light pill floating on the dark bar
   (surfaceContainer clears 13.67:1 against inverseSurface — obviously a distinct shape) — the
   same "light chip on the night bar" look the wireframe's own search field carries. Only the
   BORDER changes, from outlineVariant to onInverseSurfaceVariant: outlineVariant measures
   under 1.3:1 against inverseSurface in dark mode (where the pill's own fill also nearly
   matches the bar, 1.14:1 — surfaceContainer and inverseSurface are both near-black night-ramp
   steps there), so the pill would have NO visible boundary at all in dark mode without this.
   onInverseSurfaceVariant clears 7.26:1 (light) / 8.59:1 (dark) against inverseSurface, so the
   outline alone carries the pill's boundary in dark mode even though its fill barely lifts off
   the bar — an intentional "outlined chip" look there, not a stray inconsistency. */
.takt-topbar__search {
    display: inline-flex;
    align-items: center;
    gap: var(--ds-space-sm);
    inline-size: min(24rem, 40vw);
    min-block-size: 2.75rem;
    padding: 0 var(--ds-space-md);
    border: var(--ds-border-width-thin) solid var(--ds-color-onInverseSurfaceVariant);
    border-radius: var(--ds-radius-full);
    background-color: var(--ds-color-surfaceContainer);
    color: var(--ds-color-onSurfaceVariant);
    font-family: var(--ds-typography-bodySmall-font-family);
    font-size: var(--ds-typography-bodySmall-font-size);
    cursor: pointer;
}

.takt-topbar__search:hover {
    background-color: var(--ds-color-surfaceContainerHigh);
}

.takt-topbar__searchlabel {
    flex: 1 1 auto;
    overflow: hidden;
    text-align: start;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.takt-topbar__glyph {
    display: inline-flex;
    align-items: center;
    color: currentColor;
}

.takt-topbar__actions {
    display: flex;
    align-items: center;
    gap: var(--ds-space-xs);
    flex: 0 0 auto;
    /* The anchor the ⋯ panel drops from below the sm rung (see the overflow block above). */
    position: relative;
}

/* TAKT-256 — LanguageSwitcher (.lang-switch, Components/Layout/LanguageSwitcher.razor) renders
   inside .takt-topbar__actions. Its own fill/ink (--ds-segmentedControl-container/-itemText/
   -itemContainerSelected/-itemTextSelected) stay untouched for the same reason the search
   pill's do — self-contained pairs, correct in either theme regardless of the bar behind them,
   giving the same "light chip on the night bar" look in light mode. Only its container border
   is redirected here, scoped to `.takt-topbar .lang-switch` so the SAME component used on the
   still-light TaktPublicNav header (.takt-publicnav__links .lang-switch) is untouched: outline
   -Variant-based --ds-segmentedControl-containerBorder measures under 1.3:1 against
   inverseSurface in dark mode, same shortfall as the search pill's border and for the same
   reason (dark segmentedControl-container is a near-black night-ramp step, ~1.14:1 from the
   bar itself) — onInverseSurfaceVariant clears 7.26:1 / 8.59:1 instead. This is a component
   TOKEN redirect (--ds-segmentedControl-containerBorder), the sanctioned per-instance seam,
   not an override of the --ds-color-* value it happens to equal upstream. */
.takt-topbar .lang-switch {
    --ds-segmentedControl-containerBorder: var(--ds-color-onInverseSurfaceVariant);
}

/* TAKT-256 — onInverseSurfaceVariant, same reasoning as the crumb: 7.26:1 light / 8.59:1 dark
   against the bar. The hover fill moves from surfaceContainer (a near-white patch that would
   read as a bright blob on the night bar) to inverseSurfaceRaised — the same "one step up the
   night ramp" hover the sidebar rail and DsCard's inverse variant already use for exactly this
   ground; onInverseSurfaceVariant still clears 6.33:1 (light) / 7.56:1 (dark) against it. */
.takt-topbar__action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--ds-space-xxs);
    min-inline-size: 2.75rem;
    min-block-size: 2.75rem;
    padding: 0 var(--ds-space-sm);
    border: 0;
    /* The BUTTON role's radius, not the generic md step (app issue #422; design-system
       spec/responsive-contract.md §4 "one radius per role per page"): every DsButton on a
       screen renders --ds-button-radius (10px on Takt) and these rendered --ds-radius-md
       (8px), which was the "corners don't match" the consumer gate counted 57 times. */
    border-radius: var(--ds-button-radius);
    background: none;
    color: var(--ds-color-onInverseSurfaceVariant);
    font-family: var(--ds-typography-labelSmall-font-family);
    font-size: var(--ds-typography-labelSmall-font-size);
    cursor: pointer;
    /* FEAT-004: this class now dresses an <a> (the "Fejl & idéer" link) as well as the theme
       <button>. Both a text-decoration reset and a colour that survives :visited are needed for
       the anchor — without them the site-wide link rules would underline it and re-ink it on a
       ground those colours were never measured against. */
    text-decoration: none;
}

/* App issue #427 — the theme control's glyph. It is a text character (☾ ☀ ◐, swapped by
   theme.js) because DsIcon's curated set has no sun / moon / follow-system glyph
   (design-system#1328); until it does, the character takes the size every other glyph in
   this bar renders at instead of the 12px caption the label sits on — "a tiny crescent"
   was the owner's reading. Both chromes' toggles share the rule. */
.takt-topbar__action [data-theme-icon],
.takt-publicnav__link [data-theme-icon] {
    font-size: var(--ds-typography-heading5-font-size);
    line-height: 1;
}

.takt-topbar__action:visited {
    color: var(--ds-color-onInverseSurfaceVariant);
}

.takt-topbar__action:hover {
    background-color: var(--ds-color-inverseSurfaceRaised);
}

/* TAKT-256 — every focus-visible ring in the bar that lands directly on inverseSurface
   (not inside a self-contained pill/menu whose own fill already handles it): the site-wide
   default (member-management.css's bare `:focus-visible` rule) points outline-color at
   --ds-color-focusRing, which the design system's own inverse-context audit found does not
   clear the 3:1 non-text floor against the Takt night ramp for any of the six brands (checked
   there against inverseSurfaceHigh specifically). Measured directly against THIS bar's own
   inverseSurface ground, --ds-color-focusRing is actually a marginal pass (3.48:1 light /
   4.58:1 dark) — but onInverseSurface is the token that audit established as the safe
   inverse-context ring for exactly this reason ("clears AA against every night-ramp step …
   doubles as the inverse focus ring"), and it clears 8.95:1 / 16.41:1 here with real headroom
   instead of a margin
   that a background change could erode. Only outline-color is touched — width/offset stay
   the site default. The language switcher's own arms (DsSegmentedControl) are deliberately
   excluded: their ring is INSET (-2px), landing on the pill's own selected fill rather than
   the bar, and already uses the component's own --ds-segmentedControl-focusRingColor seam. */
.takt-topbar__mark:focus-visible,
.takt-topbar__action:focus-visible,
.takt-topbar__search:focus-visible {
    outline-color: var(--ds-color-onInverseSurface);
}

/* The ⋯ toggle is a <label>; the focus lands on the checkbox filling it (see the overflow
   block), so the ring is drawn on the label from the checkbox's state. The site-wide
   :focus-visible rule never reaches a label, hence the full declaration here. */
.takt-topbar__action:has(.takt-more__state:focus-visible) {
    outline: var(--ds-border-focusRing-width) solid var(--ds-color-onInverseSurface);
    outline-offset: var(--ds-border-focusRing-offset);
}

/* ── The avatar disclosure ──────────────────────────────────────────────── */

/* TAKT-256 — DsAvatar (rendered inside .takt-topbar__avatarsummary) has no inverse tone/
   variant of its own (unlike DsCard's variant="inverse" or DsSideNav's Tone="Inverse"): its
   fallback-initials fill/ink/ring are DEDICATED --ds-avatar-* tokens the generated theme
   bakes to flat hex, not live var(--ds-color-*) reach-ins — so redirecting the generic ink
   roles (the re-point MemberHome.razor.css does for its hero) would not reach it here,
   and left alone its default fill measures ~1.03:1 against inverseSurface in dark mode
   (--ds-avatar-container is a near-black brand-darkest swatch there too — both are dark
   night-ramp steps), i.e. no visible circle at all, just floating initials. --ds-avatar-*
   is itself a documented per-instance seam (the same vocabulary the component already
   exposes containerTertiary/containerIndigo/containerNeutral through), so this redirects it
   to the same inverse triad used throughout the bar: inverseSurfaceRaised for the fill
   (matching the "one step up the ramp" convention every other inverse surface here uses),
   onInverseSurface for the initials ink (7.81:1 light / 14.45:1 dark against that fill), and
   onInverseSurfaceVariant for the ring (2.24:1 light / 7.56:1 dark against the fill — the
   light figure is the same "the two adjacent fills already separate on their own" case as
   the shell's own outline hairline: inverseSurfaceRaised sits 12.1:1 off the page ground
   behind it, so the ring is a finishing touch there, not the only signal). */
.takt-topbar__avatar {
    position: relative;
    --ds-avatar-container: var(--ds-color-inverseSurfaceRaised);
    --ds-avatar-onContainer: var(--ds-color-onInverseSurface);
    --ds-avatar-border: var(--ds-color-onInverseSurfaceVariant);
}

.takt-topbar__avatarsummary {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-inline-size: 2.75rem;
    min-block-size: 2.75rem;
    border-radius: var(--ds-radius-full);
    cursor: pointer;
    list-style: none;
}

.takt-topbar__avatarsummary::-webkit-details-marker {
    display: none;
}

/* TAKT-256 — onInverseSurface, not the generic focusRing: see the combined focus-visible
   comment above the actions block for why (this ring lands on the bar itself, same ground). */
.takt-topbar__avatarsummary:focus-visible {
    outline: var(--ds-border-focusRing-width) solid var(--ds-color-onInverseSurface);
    outline-offset: var(--ds-border-focusRing-offset);
}

/* Untouched by TAKT-256: this popover floats above ordinary page content on its own opaque
   --ds-color-surface, independent of the (inverse) bar it is anchored to — the same light
   context every other overlay in the app opens on, not a second bar. Its ink (onSurface/
   onSurfaceVariant/outlineVariant below) already pairs correctly with that light fill; had
   this block been reached by a blanket ink-role redirect scoped to .takt-topbar, its light-
   on-dark ink would have landed on this light surface instead and gone the same way the
   dark-on-light defaults did on the bar itself, just inverted — which is exactly why this
   file redirects each bar element's OWN colour individually rather than the generic roles. */
.takt-topbar__avatarmenu {
    position: absolute;
    inset-inline-end: 0;
    inset-block-start: calc(100% + var(--ds-space-xxs));
    z-index: var(--ds-zIndex-dropdown);
    display: flex;
    flex-direction: column;
    min-inline-size: 12rem;
    padding: var(--ds-space-xxs);
    border: var(--ds-border-width-thin) solid var(--ds-color-outlineVariant);
    border-radius: var(--ds-radius-md);
    background-color: var(--ds-color-surface);
    box-shadow: var(--ds-elevation-level-002);
}

.takt-topbar__avatarname {
    padding: var(--ds-space-xs) var(--ds-space-sm);
    color: var(--ds-color-onSurfaceVariant);
    font-size: var(--ds-typography-bodySmall-font-size);
}

.takt-topbar__avataritem {
    display: flex;
    align-items: center;
    min-block-size: 2.75rem;
    padding: var(--ds-space-xs) var(--ds-space-sm);
    border-radius: var(--ds-radius-sm);
    color: var(--ds-color-onSurface);
    font-size: var(--ds-typography-bodySmall-font-size);
    text-decoration: none;
}

.takt-topbar__avataritem:hover {
    background-color: var(--ds-color-surfaceContainer);
    text-decoration: underline;
}

.takt-topbar__avatarsep {
    margin: var(--ds-space-xxs) 0;
    border: 0;
    border-block-start: var(--ds-border-width-thin) solid var(--ds-color-outlineVariant);
}

/* ── Bottom tab bar ─────────────────────────────────────────────────────── */

/* Nothing here any more. The bar is in the markup at every width (§6b) and DsAppShell.Static
   hides the ROW it sits in at/above the md rung, in the shell's own container query — which is
   what this file used to do by hand, on a wrapper div that existed only to outrank
   DsBottomNavigation's own scoped `display: flex`. Both are gone. */

/* ── The help toggle, in BOTH chromes (TAKT-301 finding 5) ───────────────────
   The control is a form because /prefs/help is a form-post endpoint (no circuit on either
   header — see TaktHelpToggle.razor). `display: contents` keeps the <form> out of the layout
   entirely, so its button sits in the topbar's / public nav's own flex row as a sibling of the
   theme and language controls rather than inside a box of its own that would break the row's
   gap and alignment. The button itself takes whichever host's class the caller passes, so it
   inherits that chrome's ink, size and 44px floor with no second set of rules here. */
.takt-helptoggle {
    display: contents;
}

/* ── The public header ──────────────────────────────────────────────────── */

/* TAKT-301 finding 4 — the landing shell is a page-height column, so its footer is pinned to
   the bottom instead of floating directly under short content. The app shell's own foot is
   already pinned this way (the sidebar rail's Footer slot), and the two chromes reading
   differently is what made the "Privatlivspolitik · v1.x" line look like stray text on the
   directory rather than site furniture. 100dvh, not 100vh: the dynamic unit is the one that
   accounts for a mobile browser's collapsing address bar, so the footer does not sit under it. */
.takt-public {
    display: flex;
    flex-direction: column;
    min-block-size: 100dvh;
}

/* The content takes the slack; the footer keeps its own height. `flex: 1 0 auto`, never
   `1 1 auto`: the column must be allowed to grow PAST the viewport on a long page, and a
   shrinkable main would instead squeeze the directory grid into one screen. */
.takt-public > .takt-public__content {
    flex: 1 0 auto;
}

/* Skip-to-content, in front of the public header (the shell brings its own). Visually hidden
   until focused, then pinned top-start. Moved here from MainLayout.razor.css: it is rendered
   by TaktPublicNav now, and CSS isolation would never have reached it there. */
.skip-link {
    position: absolute;
    inset-inline-start: var(--ds-space-sm);
    inset-block-start: -10rem;
    z-index: var(--ds-zIndex-max);
    padding: var(--ds-space-xs) var(--ds-space-md);
    border-radius: var(--ds-radius-md);
    background-color: var(--ds-color-primary);
    color: var(--ds-color-onPrimary);
    text-decoration: none;
}

.skip-link:focus {
    inset-block-start: var(--ds-space-sm);
}


.takt-publicnav {
    position: sticky;
    inset-block-start: 0;
    z-index: var(--ds-zIndex-appBar);
    background-color: var(--ds-color-surface);
    border-block-end: var(--ds-border-width-thin) solid var(--ds-color-outlineVariant);
}

.takt-publicnav__bar {
    display: flex;
    align-items: center;
    gap: var(--ds-space-sm);
    /* A MINIMUM, not a fixed height: at 430 the controls wrap onto a second line rather than
       running off the edge, and the bar grows to hold them. */
    min-block-size: 4rem;
    padding: var(--ds-space-xs) var(--ds-space-lg);
    flex-wrap: wrap;
}

.takt-publicnav__mark {
    display: inline-flex;
    align-items: center;
    /* D10 — a 44px target. The lockup is a link (it goes to the club's front door) and measured
       30px tall, which the mobile audit caught on every public page. */
    min-block-size: 2.75rem;
    gap: var(--ds-space-xs);
    color: var(--ds-color-onSurface);
    font-family: var(--ds-typography-heading5-font-family);
    font-size: var(--ds-typography-heading5-font-size);
    font-weight: var(--ds-typography-heading5-font-weight);
    text-decoration: none;
}

.takt-publicnav__markbadge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    inline-size: 1.75rem;
    block-size: 1.75rem;
    border-radius: var(--ds-radius-full);
    background-color: var(--ds-color-primary);
    color: var(--ds-color-onPrimary);
    font-size: var(--ds-typography-labelSmall-font-size);
}

/* TAKT-303 — the page's own section anchors, split out of __links so the two groups wrap
   INDEPENDENTLY. They used to be the leading items of the one links row, so a club with three
   sections pushed the language, theme and account controls onto a second line and the login CTA
   onto a third: the four-row header the owner measured on a phone. Above the md rung this sits
   inline exactly where it always did; the 900px block below is what gives it a row of its own. */
.takt-publicnav__sections {
    display: flex;
    align-items: center;
    gap: var(--ds-space-xs);
    min-inline-size: 0;
    /* Splitting one nav into two must not MOVE anything at the widths that were never the
       complaint: the anchors used to be the leading items of a single flex-end group, so the auto
       margin travels with them and __links gives its own up whenever they are present. Both groups
       then pack against the trailing edge exactly as the one group did. */
    margin-inline-start: auto;
}

.takt-publicnav__bar:has(.takt-publicnav__sections) .takt-publicnav__links {
    margin-inline-start: 0;
}

.takt-publicnav__links {
    display: flex;
    align-items: center;
    gap: var(--ds-space-xs);
    /* Wrapping is the honest answer at a width the row genuinely cannot hold; squeezing the
       controls is how text ends up edge-tight. The 900px block below is what makes 390px a width
       that DOES hold, so this is the degradation for narrower still, not the phone layout. */
    flex-wrap: wrap;
    justify-content: flex-end;
    margin-inline-start: auto;
    min-inline-size: 0;
}

/* The account cell — who you are, and the way out (or in). A sibling of the two navs rather
   than the tail of __links since #422, so the ⋯ overflow can take the utility controls off the
   bar on a phone while the disc and the door stay on it at every width. */
.takt-publicnav__account {
    display: flex;
    align-items: center;
    gap: var(--ds-space-xs);
    flex: 0 0 auto;
}

/* App issue #427 — the signed-in name used to inherit the site's link ink (the brand red at
   link weight), so "Owner" read as the page's primary action while "Find a club" and "Log out"
   beside it were muted. It is an identity, not a verb: the DISC the sidebar's who-am-I foot and
   the app shell's topbar already draw for the same person (data-initials through ::before —
   generated content on purpose, see TaktPublicNav.razor), then the name in ink at body weight.
   The same rule at every width; below md the name simply hides and the disc is the control. */
.takt-publicnav__account .auth-user {
    display: inline-flex;
    align-items: center;
    gap: var(--ds-space-xs);
    /* D10 — the same 44px floor every other control on this bar takes; it was a bare inline link
       measuring only the height of its own text. */
    min-block-size: 2.75rem;
    min-inline-size: 2.75rem;
    max-inline-size: 14rem;
    padding-inline: var(--ds-space-xs);
    border-radius: var(--ds-radius-full);
    color: var(--ds-color-onSurface);
    font-family: var(--ds-typography-body-font-family);
    font-size: var(--ds-typography-bodySmall-font-size);
    font-weight: var(--ds-typography-body-font-weight);
    text-decoration: none;
    white-space: nowrap;
}

.takt-publicnav__account .auth-user:hover {
    background-color: var(--ds-color-surfaceContainer);
    color: var(--ds-color-onSurface);
    text-decoration: underline;
}

/* The disc: the person's initials, sized like DsAvatar's small size beside the name. */
.takt-publicnav__account .auth-user::before {
    content: attr(data-initials);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    inline-size: var(--ds-avatar-sizeSm);
    block-size: var(--ds-avatar-sizeSm);
    border-radius: var(--ds-radius-full);
    background-color: var(--ds-avatar-container);
    color: var(--ds-avatar-onContainer);
    font-family: var(--ds-typography-labelSmall-font-family);
    font-size: var(--ds-typography-labelSmall-font-size);
    font-weight: var(--ds-typography-label-font-weight);
}

.takt-publicnav__account .auth-user__name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The glyph a control wears in place of its word below the md rung. Hidden above it: there the
   label IS the control, and printing a picture of the same destination beside the word is noise
   on a bar this dense. Sized in `em` so it tracks the type it replaces — the binding TaktGlyph's
   own doc comment explains. */
.takt-publicnav__glyph {
    display: none;
    inline-size: 1.25em;
    block-size: 1.25em;
}

.takt-publicnav__link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--ds-space-xxs);
    /* D10 — a 44x44 target in BOTH axes. Below 900px the theme control loses its word and
       would otherwise be a 44-tall, ~20-wide sliver. */
    min-inline-size: 2.75rem;
    min-block-size: 2.75rem;
    padding: 0 var(--ds-space-sm);
    border: 0;
    /* The button role's radius — see .takt-topbar__action for why it is not --ds-radius-md. */
    border-radius: var(--ds-button-radius);
    background: none;
    color: var(--ds-color-onSurfaceVariant);
    font-family: var(--ds-typography-labelSmall-font-family);
    font-size: var(--ds-typography-labelSmall-font-size);
    text-decoration: none;
    cursor: pointer;
}

.takt-publicnav__link:hover {
    background-color: var(--ds-color-surfaceContainer);
    text-decoration: underline;
}

/* The public header's ⋯ toggle — same label-with-checkbox anatomy as the topbar's; the ring
   comes from the checkbox's focus, on the site's own ring colour (a light ground here). */
.takt-publicnav__link:has(.takt-more__state:focus-visible) {
    outline: var(--ds-border-focusRing-width) solid var(--ds-color-focusRing);
    outline-offset: var(--ds-border-focusRing-offset);
}

.takt-publicnav__cta {
    display: inline-flex;
    align-items: center;
    min-block-size: 2.75rem;
    padding: 0 var(--ds-space-md);
    border-radius: var(--ds-radius-full);
    background-color: var(--ds-color-primary);
    color: var(--ds-color-onPrimary);
    font-family: var(--ds-typography-label-font-family);
    font-size: var(--ds-typography-label-font-size);
    font-weight: var(--ds-typography-label-font-weight);
    text-decoration: none;
}

.takt-publicnav__cta:hover {
    background-color: var(--ds-color-primaryHover);
    text-decoration: underline;
}

.takt-public__content {
    display: block;
    inline-size: 100%;
    max-inline-size: 90rem;
    margin-inline: auto;
    padding: var(--ds-space-lg);
}

.takt-public__content:focus {
    outline: none;
}

.takt-public__footer {
    inline-size: 100%;
    max-inline-size: 90rem;
    margin-inline: auto;
    padding: 0 var(--ds-space-lg) var(--ds-space-lg);
    font-size: var(--ds-typography-bodySmall-font-size);
    display: flex;
    align-items: center;
    gap: var(--ds-space-sm);
}

/* The footer link is de-emphasised chrome, so it takes the subtle-on-surface pair
   (#514A6E on #FAF6F1, 8.0:1) rather than inheriting the default link colour. A semantic
   choice about emphasis, not a contrast workaround — the default link ink clears AA too.
   Parity audit (WCAG 2.5.8 AA): the caption-sized link measured 97x21 — under the 24px
   floor that applies on every device, mouse included, not only touch. inline-flex +
   min-block-size grows the tap/click area without enlarging the caption text itself — the
   same technique .takt-sidenav__footlink already uses below for its 44px AAA/touch floor. */
.takt-public__footer a {
    display: inline-flex;
    align-items: center;
    min-block-size: var(--ds-size-targetMin);
    color: var(--ds-color-onSurfaceVariant);
    text-decoration: underline;
}

/* ============================================================================
 * Below the md rung — the tab bar appears and the topbar sheds its labels.
 * Grids that hold tables use minmax(0, 1fr) so a wide table scrolls in its own
 * container instead of stretching the row.
 *
 * THE DRAWER IS NOT HERE. DsAppShell.Static owns the off-canvas switch — the
 * panel, the scrim, the slide, the reduced-motion opt-out — in a container query
 * against the shell root. This file used to reimplement all of it against
 * .takt-sidenav and a scrim of its own; what is left below is the app's own
 * chrome, none of which the package can know about.
 *
 * 899.98px, not 900px: the shell's container query is `max-width: 899.98px`
 * (the design system's md partner rung), and at exactly 900 the two used to
 * disagree — the tab bar appeared while the sidebar was still a column.
 * ============================================================================ */

/* The 900–1249.98px narrow-sidebar band that used to sit here (owner 2026-09-03, cited against
   the DS's fixed drawer rung) is gone with the #422 harvest: the DS shipped the collapse-rung
   seam, the sizing contract (design-system spec/sizing-contract.md, owner 2026-09-06) now owns
   what a sidebar column costs at each rung, and the register pages are uncapped data measures
   that scroll inside their own card rather than fight the rail for width. */

/* design-system#2126 — see the rung table at the top of this file: the wordmark steps aside one
   rung above the md block's own hide, because the DS search pill paints over it between 1024 and
   1199.98px (measured at 1024: label x 40–328 inside a 320px logo slot, the pill's painted box
   starting ≈ x 272 against a layout box at x 336). The badge stands for the platform at this
   width exactly as it does below md. Harvest: delete this block when #2126 ships. */
@media (max-width: 1199.98px) {
    .takt-topbar__marklabel {
        display: none;
    }
}

@media (max-width: 899.98px) {
    /* Parity 2026-09-02: the 24px AA floor .takt-public__footer a carries above still sits under
       the 44px AAA/touch tier on the phone captures (WCAG 2.5.5), so below the shell's
       breakpoint the footer link takes the design system's own touch-target size — the same
       rung .takt-sidenav__footlink already holds. One 899.98px block for the whole md rung
       (#422) rather than two — the file used to carry this rule in a block of its own. */
    .takt-public__footer a {
        min-block-size: var(--ds-size-touchTarget);
    }

    /* Owner 2026-09-03 round 2: the tab bar is a TOUCH idiom — "no matter the screensize, it's
       really odd on a PC". Below the md rung the DS shows the bottom-nav row either way (its own
       container query), so the bar's height reservation AND the bar itself are gated on the
       pointer here: a phone gets the tab bar, a narrowed desktop window keeps the hamburger
       drawer alone. The triple selector is the cascade idiom (gotcha 13) — our unscoped class
       ties the RCL's scoped display rule at double, and the RCL loads after us. */
    @media (pointer: coarse) {
        .takt-shell {
            /* The bar's row reserves its height here and nowhere else. sidebarWidth is
               deliberately NOT collapsed: the shell's Static drawer is position: absolute, so it
               reserves no column to collapse, and the same length is what gives the drawer its
               width. borderWidth is deliberately not zeroed either — with no column in the flow
               there is no 0-width column left to paint a hairline down, and the topbar keeps
               drawing its own from the shell rather than needing a DsAppBar override. */
            --ds-appShell-bottomNavHeight: 3.5rem;
        }
    }

    @media (pointer: fine), (pointer: none) {
        .takt-bottomnav.takt-bottomnav.takt-bottomnav {
            display: none;
        }
    }

    .takt-topbar__kbd {
        display: none;
    }

    /* The search field gives way to its glyph — the palette it opens carries the
       rest, and DsKbd hides itself under a coarse pointer anyway. */
    .takt-topbar__search {
        inline-size: auto;
        min-inline-size: 2.75rem;
        padding: 0 var(--ds-space-sm);
        justify-content: center;
    }

    .takt-topbar__searchlabel {
        display: none;
    }

    /* The action WORDS stay at this rung (#422): with the search pill collapsed to its glyph
       and the wordmark to its badge, hamburger + badge + search + "? hjælp" + "Fejl & idéer" +
       DA|EN + "☾ Mørk" + avatar measure ~560px, which every tablet from the sm rung up holds
       on one row. Below sm the whole row moves into the ⋯ panel, where the word IS the row. */

    /* Requirement 1 — "Simple Member Management" does not fit beside a hamburger, four
       controls and an avatar at 820px; hidden rather than truncated, same mechanism the club
       mark's label already used pre-TAKT-261. The badge (always visible, above) plus the
       hamburger and search are what the bar shows at this width. */
    .takt-topbar__marklabel {
        display: none;
    }

    /* TAKT-250 finding C6 — only the off-canvas drawer gets a close button; the permanent
       desktop sidebar (base rule above) has nothing to close. Pinned to the header row's
       trailing edge — D10's 44px floor in both axes, the same floor the shell's own toggle
       hits from --ds-size-touchTarget under a coarse pointer. */
    .takt-sidenav__close {
        /* flex + margin-inline-start:auto, NOT align-self: flex-end. The rule used to say the
           latter and this comment used to claim it pinned the button to the trailing edge; it
           never did — DsSideNav's Header slot is a plain block, not a flex container, so
           align-self was inert and the ✕ has been sitting at the leading edge the whole time.
           An auto inline-start margin is what actually moves a box to the end of a block — and
           it needs a block-level box with a shrink-to-fit width to have anything to distribute,
           which is why display and inline-size move together with it. */
        display: flex;
        align-items: center;
        justify-content: center;
        inline-size: fit-content;
        margin-inline-start: auto;
        min-inline-size: 2.75rem;
        min-block-size: 2.75rem;
        margin-block-end: var(--ds-space-xs);
        border: 0;
        /* The button role's radius — see .takt-topbar__action. */
        border-radius: var(--ds-button-radius);
        background: none;
        color: var(--ds-color-onSurfaceVariant);
        cursor: pointer;
    }

    .takt-sidenav__close:hover {
        background-color: var(--ds-sideNav-itemHover);
    }

    .takt-sidenav__close:focus-visible {
        outline: var(--ds-border-focusRing-width) solid var(--ds-color-focusRing);
        outline-offset: var(--ds-border-focusRing-offset);
    }

    /* TAKT-250 finding C2 (mobile audit 2026-08-27) — the FAB paints over page content and
       stays tappable above the off-canvas nav scrim. Sibling of design-system#599 (the same
       overlap, filed against DsAppShell's floating slot reserving no clearance at all, but
       scoped to DESKTOP and closed there) — the consumer-side clearance for the 430px case is
       SMM's to add:

       1. CLEARANCE — SUPERSEDED BY TAKT-303, see the note directly below. This used to reserve
          the FAB's whole footprint off .takt-content's trailing edge; the clearance is a BOTTOM
          measure now, and the package's own is what provides it.
       2. Z-ORDER. The FAB is position:static (above) inside the package's own floating-slot
          wrapper, which is the design system's own --ds-zIndex-fab (1050) layer — already
          below the shell's own scrim and drawer layers by construction, so paint order should
          never put it above the scrim. The rule below is the defense-in-depth half regardless:
          while the drawer is open the FAB is not just behind the scrim, it is INERT —
          pointer-events:none and visibility:hidden — so "stays tappable through the scrim"
          cannot recur even if some future change (ours or the package's) ever perturbs the
          stacking order.

          Keyed off the drawer checkbox's own :checked state, by the id this app pins
          (TaktNav.NavCheckboxId) rather than by any class of the package's — an <html> class
          this file used to set from script is exactly what the checkbox replaced. */
    /* TAKT-303 — CLEARANCE IS A BOTTOM MEASURE HERE, NOT AN INLINE ONE. Point 1 above used to
       reserve the FAB's whole footprint off the content column's trailing edge. On a 390px phone
       that is 88px — 23% of the viewport — gone from every signed-in page in a club with chat, on
       top of the 38px leading gutter the column and the shell's own main padding already take: the
       cards on Min klub stopped 100px short of the right edge while starting 38px from the left,
       and the signup basket's "Videre til hold" wrapped to two lines inside what was left. It also
       double-paid for something the package now does: DsAppShell puts
       `--ds-appShell-floatingClearance` (96px) of padding under any main that carries a floating
       slot, so content already ends well above the FAB at rest. What the inline reservation bought
       beyond that was the mid-scroll case — a FAB floating over content while you scroll past it,
       which is what a FAB does on every platform that has one. The z-order/inert half of C2 (the
       real complaint: tappable THROUGH the drawer scrim) is untouched below. */

    body:has(#takt-nav-state:checked) .chat-fab {
        visibility: hidden;
        pointer-events: none;
    }

    /* TAKT-303 — the phone type ramp. The theme has ONE typography scale (no responsive rungs of
       its own — see the generated takt CSS), so a heading sized for a 1440px canvas renders at the
       same 36px on a 390px one: the directory's "Find din klub." and every page's DsPageHeader H1
       ate a fifth of the viewport before a word of content. These re-point the DS's own COMPONENT
       token to a lower rung of the DS's own type scale, on OUR wrapper classes — not an override
       of any --ds-* value, and never a literal px. Both content columns take it, so the public
       front door and the signed-in shell step down together rather than disagreeing by one rung. */
    .takt-public__content,
    .takt-content {
        --ds-pageHeader-titleTypography-font-size: var(--ds-typography-heading4-font-size);
        --ds-pageHeader-titleTypography-line-height: var(--ds-typography-heading4-line-height);
    }

    .takt-public__content {
        padding: var(--ds-space-md);
    }

    /* The footer sits in the same column and has to keep the same gutter: with the content stepped
       down to md and the footer left at lg, the privacy link started 8px further in than every card
       above it, which reads as a stray indent rather than as a footer. */
    .takt-public__footer {
        padding-inline: var(--ds-space-md);
    }

    /* The column's own gutter, one step down the space scale: 24px each side of a 390px viewport
       is 12% of the width spent on emptiness, and the shell's own --ds-appShell-mainPadding is
       already outside it. */
    .takt-content {
        padding-inline: var(--ds-space-md);
    }

    /* ── The landing header, in ONE row (TAKT-303; #422) ──────────────────────────────────
       The owner's report was the shape of this bar on a phone: the lockup on its own line, then
       find-a-club + DA/EN + theme + the person's name on a second, then "Log ud" (or a full-width
       "Log ind") on a third — 160px of chrome before the page began. Nothing here shrinks a touch
       target: every control below keeps its 44px floor, and the row fits because the two WORDS
       that cost the most give way to the glyph and the disc that already stand for them elsewhere
       in this product.

       #422: the bar no longer wraps at this rung at all. It used to keep `flex-wrap: wrap` as
       the degradation, and a club page — whose section anchors took a row of their own here —
       measured 111px on an 820px tablet and 163px on a phone. The anchors now share the row and
       SCROLL sideways in whatever width the fixed controls leave them (see __sections below);
       below sm they leave the bar for the ⋯ panel with the utility links. */

    .takt-publicnav__bar {
        padding-inline: var(--ds-space-sm);
        /* One row of 44px controls plus the bar's own block padding — it was sized to hold two. */
        min-block-size: 3.5rem;
        flex-wrap: nowrap;
        /* The anchor the ⋯ panel drops from below the sm rung. */
        position: relative;
    }

    /* Same rule, same reason as the topbar's own __marklabel: the wordmark does not fit beside
       five controls and an account at 390px, and hiding it beats truncating a club's name to two
       letters and an ellipsis. The badge is the mark at this width — the club's own initial on a
       club page, "T" on Takt's — and the lockup keeps its aria-label, so the link is still named. */
    .takt-publicnav__marklabel {
        display: none;
    }

    /* D10 — and with the word gone the lockup is a 28px badge in a 44px-tall link, which the
       parity audit caught as the one NEW touch-target finding this pass introduced. The badge keeps
       its own size; the link gets the same 44px floor in the inline axis that every other control
       on this bar already has in both. */
    .takt-publicnav__mark {
        justify-content: center;
        min-inline-size: 2.75rem;
    }

    /* The section anchors stay on the ONE row and take only the slack the fixed controls leave
       (`flex: 1 1 0%` — a zero basis, so they never push the utility links into wrapping), and
       scroll sideways inside it rather than wrapping: a club that publishes three sections
       would otherwise cost a second row again, and a half-height stub of a fourth link is a
       worse signal than an edge that visibly continues. */
    .takt-publicnav__sections {
        flex: 1 1 0%;
        flex-wrap: nowrap;
        overflow-x: auto;
        justify-content: flex-start;
        /* The auto margin that packs it right at desktop has nothing to distribute once it owns
           the slack itself. */
        margin-inline-start: 0;
    }

    .takt-publicnav__sections .takt-publicnav__link {
        white-space: nowrap;
    }

    /* The controls are 44px squares already (min-inline-size above); the horizontal padding only
       widened the two that still carry a word. */
    .takt-publicnav__link {
        padding-inline: var(--ds-space-none);
    }

    /* The utility links never shrink: the sections scroller gives way first (see above). */
    .takt-publicnav__links {
        gap: var(--ds-space-xxs);
        flex: 0 0 auto;
    }

    /* Same rule as the topbar's: the theme control keeps its icon and drops its word. */
    .takt-publicnav__link [data-theme-label] {
        display: none;
    }

    /* "Find en klub" gives way to the search glyph it shares with the topbar's own collapsed
       search control. Its accessible name is the aria-label the markup carries for exactly this. */
    .takt-publicnav__glyph {
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

    .takt-publicnav__linklabel {
        display: none;
    }

    /* The account link is the disc alone — the same person the app shell's topbar shows the same
       way. The monogram is the link's own data-initials through generated content (the base rule
       above), so the name stays the link's text (and its aria-label) for anything that reads
       either; only the visible word goes. */
    .takt-publicnav__account .auth-user {
        justify-content: center;
        inline-size: 2.75rem;
        min-inline-size: 2.75rem;
        max-inline-size: 2.75rem;
        padding-inline: 0;
    }

    .takt-publicnav__account .auth-user .auth-user__name {
        display: none;
    }

    /* With its word gone the control is one small glyph in a 44px box on `background: none` —
       nothing marks it as pressable, so at the row's right edge it read as a stray character
       (the mobile audit logged it as "clipped"; the audit's own overflow check shows it never
       was). The wireframe's theme button is a filled, centred pill; give the label-less form
       the same resting surface it already shows on hover. */
    .takt-publicnav__link[data-theme-toggle] {
        background-color: var(--ds-color-surfaceContainer);
        border: var(--ds-border-width-thin) solid var(--ds-color-outlineVariant);
    }

    /* mobile-audit L1 made this CTA a full-width row of its own, because the header wrapped and
       "Log ind" was stranded alone on a short trailing line — a deliberate wrap beats one that
       reads as a rendering fault. TAKT-303 removes the wrap that made it stranded in the first
       place, so the CTA rejoins the row as a compact pill: a third row for one button was most of
       what "so freaking big" was measuring. It keeps its 44px block floor from the base rule and
       stays the only filled control on the bar, which is what marks it as the thing to press. */
    .takt-publicnav__cta {
        padding-inline: var(--ds-space-sm);
        justify-content: center;
    }
}

/* ============================================================================
 * Below the sm rung — the phone. The chrome COLLAPSES (#422): each bar keeps one row of
 * 44px controls and the rest go into the ⋯ overflow panel described at .takt-more above.
 *   topbar:        hamburger · badge · search · ⋯ · avatar
 *   public header: badge · ⋯ · disc + "Log ud" (or the "Log ind" pill)
 * 599.98px, the DS `sm` partner rung — the boundary spelled the same way DsHidden and
 * DsTable's own container query spell it.
 * ============================================================================ */
@media (max-width: 599.98px) {
    label.takt-more__toggle {
        display: inline-flex;
    }

    /* The panel: the avatar menu's own surface, one step further down the elevation ramp
       because it holds more. Hidden until the checkbox is checked — read through :has() on the
       cell the panel drops from, so the toggle can sit anywhere in that cell. Capped to the
       viewport minus the bar's gutter: a language switcher plus four rows must never push the
       document wider than the screen. */
    .takt-more {
        display: none;
        position: absolute;
        inset-inline-end: 0;
        inset-block-start: calc(100% + var(--ds-space-xxs));
        z-index: var(--ds-zIndex-dropdown);
        flex-direction: column;
        align-items: stretch;
        gap: var(--ds-space-xxs);
        min-inline-size: 14rem;
        max-inline-size: calc(100vw - 2 * var(--ds-space-sm));
        padding: var(--ds-space-xxs);
        border: var(--ds-border-width-thin) solid var(--ds-color-outlineVariant);
        border-radius: var(--ds-radius-md);
        background-color: var(--ds-color-surface);
        box-shadow: var(--ds-elevation-level-003);
    }

    .takt-topbar__actions:has(.takt-more__state:checked) .takt-more,
    .takt-publicnav__bar:has(.takt-more__state:checked) .takt-more {
        display: flex;
    }

    /* The rows. The controls keep their bar classes — the panel is where they LIVE at this
       width, not a second rendering — so the bar's inverse ink and glyph-only compaction are
       undone here, on the panel's light surface: full-width rows, the word visible again as the
       row's label, the avatar menu's own row rhythm. */
    .takt-more .takt-topbar__action,
    .takt-more .takt-publicnav__link {
        inline-size: 100%;
        justify-content: flex-start;
        gap: var(--ds-space-sm);
        padding: var(--ds-space-xs) var(--ds-space-sm);
        border: 0;
        border-radius: var(--ds-radius-sm);
        background: none;
        color: var(--ds-color-onSurface);
        font-family: var(--ds-typography-bodySmall-font-family);
        font-size: var(--ds-typography-bodySmall-font-size);
        white-space: nowrap;
    }

    .takt-more .takt-topbar__action:visited,
    .takt-more .takt-publicnav__link:visited {
        color: var(--ds-color-onSurface);
    }

    .takt-more .takt-topbar__action:hover,
    .takt-more .takt-publicnav__link:hover {
        background-color: var(--ds-color-surfaceContainer);
        text-decoration: underline;
    }

    /* Inside the panel the ring lands on a light surface, not the night bar. */
    .takt-more .takt-topbar__action:focus-visible {
        outline-color: var(--ds-color-focusRing);
    }

    /* The words are the rows' labels here — every md compaction of a word is undone. */
    .takt-more .takt-publicnav__linklabel,
    .takt-more .takt-publicnav__link [data-theme-label] {
        display: inline;
    }

    /* The find-a-club glyph beside its word reads as a menu row with an icon; the theme
       control's filled md pill does not belong in a list. */
    .takt-more .takt-publicnav__link[data-theme-toggle] {
        background-color: transparent;
        border: 0;
    }

    /* DA|EN in the panel: on the light surface the segmented control's own default border is
       right again — the inverse-context redirect on `.takt-topbar .lang-switch` is for the bar.
       The value is the theme's own default (outlineVariant), restated because a redefined custom
       property cannot be un-set from a descendant. */
    .takt-more .lang-switch {
        --ds-segmentedControl-containerBorder: var(--ds-color-outlineVariant);
        margin: var(--ds-space-xs) var(--ds-space-sm);
    }

    /* The two public navs stack as row groups inside the panel; the md-rung scroller and the
       desktop packing are both undone. */
    .takt-more .takt-publicnav__sections,
    .takt-more .takt-publicnav__links {
        flex: none;
        flex-direction: column;
        align-items: stretch;
        gap: var(--ds-space-xxs);
        margin: 0;
        overflow: visible;
    }

    /* The section anchors and the utility rows are two groups: a rule between them. */
    .takt-more .takt-publicnav__sections + .takt-publicnav__links {
        padding-block-start: var(--ds-space-xxs);
        border-block-start: var(--ds-border-width-thin) solid var(--ds-color-outlineVariant);
    }

    /* The search pill is already its glyph at md; the topbar row is hamburger · badge · search ·
       ⋯ · avatar, five 44px controls in 390px. */
    .takt-topbar {
        --ds-appBar-gap: var(--ds-space-xs);
    }
}

/* A drawer that slides for someone who asked the OS not to animate is a fault — and
   DsAppShell.Static carries its own prefers-reduced-motion opt-out for the panel it now owns,
   so the pair of rules that used to live here have nothing left to switch off. */

/* ── design-system#2105 (GAP-TOAST-REGION-COLLAPSE) ──────────────────────────────────────
 * Since DS 1.34.0 every toast renders 52px wide, one word per line (measured at 1280×720:
 * region 52×470, the alert's grid tracks 20px 0 0, the text 0×420 — so Playwright reads it as
 * hidden and every "click, then wait for the success toast" E2E went red). DsToastHost sizes
 * the region `width: max-content`, and 1.34.0 rebuilt DsAlert as a grid with
 * `container-type: inline-size` — inline-axis size containment, so the alert contributes
 * NOTHING to an intrinsically sized parent (the mechanism DsTable.razor.css documents in its
 * own squeeze-fix note, DS issue 1571). Giving the alert its intrinsic width back is the fix; the
 * toast then shrink-wraps its message as DsToast's own contract says. A `ds-c-*` rule in our
 * CSS, deliberately: no token, parameter or wrapper of ours can reach the alert a toast wraps,
 * and DesignSystemVocabularyTests freezes exactly these two names against this number. The
 * class is doubled because the DS's scoped `.ds-c-alert[b-…]` is 0,2,0 and its bundle loads
 * after ours. Traded away, knowingly: `normal` also removes the alert's own `ds-alert` query
 * container, so DsAlert's narrow rung (its action slot moving to a full-width row under
 * 599.98px) can never fire inside a toast — a toast's only action is the 24px dismiss, which
 * sits on the message's row (measured at 1280: 250×42, one row). Harvest: delete this block
 * and the two frozen names when #2105 ships, and the rung comes back with it. */
.ds-c-toast-region .ds-c-alert.ds-c-alert {
    container-type: normal;
}
