/*
 * Layout, structure and every component rule of the site. Colors, fonts, font sizes, radii, shadows, gradients and
 * durations are the theme's tokens (theme.css); this file adds only the layout tokens below, which are the same in
 * every theme. Text and icon glyphs use the four --font-size-* tokens or inherit; nothing here names a color.
 *
 * Sections, in this order: tokens, fonts and icons, base, utilities, shell (app bar, main, footer), components,
 * pages (requests list, overview, request detail, create request, rich editor, admin, logs, content pages, home, admin
 * consent), motion.
 *
 * Breakpoints: 900px (the app bar collapses into its menu button, two-column pages stack) and 640px (phones: tables
 * stack into cards, cards pad 16px, form buttons go full width).
 */

/* ===================================================================================================================
   Tokens
   =================================================================================================================== */

/* Spacing follows an 8px rhythm, with 4px and 12px for tight spots. The gutter grows with the window between its
   limits. Form and document pages use the narrow width (.page-narrow). */
:root {
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 24px;
    --space-6: 32px;
    --space-7: 48px;
    --space-8: 64px;
    --page-max-width: 1400px;
    --page-narrow-width: 960px;
    --page-gutter: clamp(16px, 4vw, 48px);
    /* The horizontal padding of the full-width shell rows (the app bar's row, main, the footer's row), which places their
       content: at most --page-max-width wide including the gutters, centered on the WHOLE window. 100vw counts the page
       scrollbar and 100% (the row's own width, resolved where the padding is used) does not, so the start depends on the
       window alone and the content does not jump sideways when the scrollbar comes or goes, while no room is reserved
       for a scrollbar that is not there. The end takes what is left; in a window narrower than the maximum the start is
       the gutter and only the end gives up the scrollbar's width. */
    --page-start: max(var(--page-gutter), (100vw - var(--page-max-width)) / 2 + var(--page-gutter));
    --page-end: max(var(--page-gutter), 100% - var(--page-start) - var(--page-max-width) + 2 * var(--page-gutter));
    --app-bar-height: 64px;
    --control-height: 40px;
    --control-height-sm: 32px;
    --switch-width: 36px;
    --sidebar-width: 320px;
    --admin-nav-width: 264px;
    --admin-nav-rail-width: 68px;
    --admin-nav-tile-inset: 3px;
    --z-app-bar: 50;
    --z-menu: 60;
    --z-nav-progress: 70;
    --z-dialog: 100;
}

@media (max-width: 899px) {
    :root {
        --app-bar-height: 56px;
    }
}

/* ===================================================================================================================
   Fonts and icons
   =================================================================================================================== */

/* Lucide 1.49.0, served from /lib/lucide (see its README). block: while the font loads, an icon stays blank instead of
   flashing as a raw private-use character. */
@font-face {
    font-family: "lucide";
    src: url("../lib/lucide/lucide.woff2") format("woff2");
    font-display: block;
}

/* Icons are glyphs of the icon font drawn by ::before from the code point in --icon, so they add no text nodes: an
   element's textContent, which tests and the request filter's toggle text read, stays its own text. .icon is an
   empty, decorative element of its own; .with-icon draws the glyph before an element's own text, where it survives
   i18n.js and scripts setting textContent. The empty alternative text after the slash keeps the glyph out of
   accessible names. Every other glyph this file draws from the font shares the same base. A glyph takes its
   element's font size; components that want a bigger one set one of the four sizes. */
.icon,
.with-icon::before,
.banner::before,
.field-error::before,
.breadcrumbs li + li::before,
.sort-indicator::after,
.details-toggle::after {
    display: inline-block;
    flex: none;
    width: 1em;
    height: 1em;
    font-family: "lucide";
    font-style: normal;
    font-weight: normal;
    font-variant: normal;
    font-feature-settings: normal;
    letter-spacing: normal;
    line-height: 1;
    text-align: center;
    text-transform: none;
    vertical-align: -0.125em;
    speak: never;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

.icon::before {
    content: var(--icon) / "";
}

.with-icon::before {
    content: var(--icon) / "";
    margin-inline-end: var(--space-2);
}

/* Flex components put their gap between the glyph and the text instead. */
:is(.btn, .btn-icon, .dropdown-toggle, .badge, .role-badge, .badge-internal, .log-entry-type, .notification-status, .comment-edit, .view-saved, .request-age, .attention-age, .attention-more-toggle, .overview-full-list).with-icon::before {
    margin-inline-end: 0;
}

/* The icons the app uses, sorted by name, with their Lucide 1.49.0 code points. To add one, see lib/lucide/README.md. */
.icon-activity { --icon: "\e038"; }
.icon-arrow-down { --icon: "\e042"; }
.icon-arrow-left-right { --icon: "\e24a"; }
.icon-arrow-right { --icon: "\e049"; }
.icon-arrow-up { --icon: "\e04a"; }
.icon-arrow-up-down { --icon: "\e37d"; }
.icon-bell { --icon: "\e059"; }
.icon-bold { --icon: "\e05d"; }
.icon-bookmark { --icon: "\e060"; }
.icon-bug { --icon: "\e20c"; }
.icon-building-2 { --icon: "\e290"; }
.icon-calendar { --icon: "\e063"; }
.icon-check { --icon: "\e06c"; }
.icon-chevron-down { --icon: "\e06d"; }
.icon-chevron-left { --icon: "\e06e"; }
.icon-chevron-right { --icon: "\e06f"; }
.icon-chevron-up { --icon: "\e070"; }
.icon-chevrons-down { --icon: "\e071"; }
.icon-chevrons-up { --icon: "\e074"; }
.icon-circle-alert { --icon: "\e077"; }
.icon-circle-check { --icon: "\e226"; }
.icon-circle-dashed { --icon: "\e4b0"; }
.icon-circle-question-mark { --icon: "\e082"; }
.icon-clock { --icon: "\e087"; }
.icon-cloud-upload { --icon: "\e091"; }
.icon-code { --icon: "\e093"; }
.icon-columns-3 { --icon: "\e099"; }
.icon-cookie { --icon: "\e26b"; }
.icon-download { --icon: "\e0b2"; }
.icon-ellipsis { --icon: "\e0b6"; }
.icon-equal { --icon: "\e1bd"; }
.icon-eye { --icon: "\e0ba"; }
.icon-file { --icon: "\e0c0"; }
.icon-file-archive { --icon: "\e30d"; }
.icon-file-image { --icon: "\e31c"; }
.icon-file-spreadsheet { --icon: "\e326"; }
.icon-file-text { --icon: "\e0cc"; }
.icon-filter { --icon: "\e0dc"; }
.icon-flag { --icon: "\e0d1"; }
.icon-folder { --icon: "\e0d7"; }
.icon-globe { --icon: "\e0e8"; }
.icon-hourglass { --icon: "\e296"; }
.icon-inbox { --icon: "\e0f7"; }
.icon-info { --icon: "\e0f9"; }
.icon-italic { --icon: "\e0fb"; }
.icon-layers { --icon: "\e529"; }
.icon-layout-dashboard { --icon: "\e1c1"; }
.icon-life-buoy { --icon: "\e101"; }
.icon-lightbulb { --icon: "\e1c2"; }
.icon-link { --icon: "\e102"; }
.icon-list { --icon: "\e106"; }
.icon-list-checks { --icon: "\e1d0"; }
.icon-list-filter { --icon: "\e460"; }
.icon-list-ordered { --icon: "\e1d1"; }
.icon-lock { --icon: "\e10b"; }
.icon-log-in { --icon: "\e10d"; }
.icon-log-out { --icon: "\e10e"; }
.icon-mail { --icon: "\e10f"; }
.icon-menu { --icon: "\e115"; }
.icon-message-circle-question-mark { --icon: "\e568"; }
.icon-message-square { --icon: "\e117"; }
.icon-messages-square { --icon: "\e40d"; }
.icon-minus { --icon: "\e11c"; }
.icon-monitor { --icon: "\e11d"; }
.icon-moon { --icon: "\e11e"; }
.icon-panel-left-close { --icon: "\e21c"; }
.icon-panel-left-open { --icon: "\e21d"; }
.icon-paperclip { --icon: "\e12d"; }
.icon-pencil { --icon: "\e1f9"; }
.icon-plus { --icon: "\e13d"; }
.icon-redo-2 { --icon: "\e2a0"; }
.icon-reply { --icon: "\e22a"; }
.icon-rows-3 { --icon: "\e58a"; }
.icon-scroll-text { --icon: "\e45f"; }
.icon-search { --icon: "\e151"; }
.icon-send { --icon: "\e152"; }
.icon-separator-horizontal { --icon: "\e1c8"; }
.icon-settings { --icon: "\e154"; }
.icon-shield-alert { --icon: "\e1fe"; }
.icon-shield-check { --icon: "\e1ff"; }
.icon-shield-x { --icon: "\e200"; }
.icon-siren { --icon: "\e2ef"; }
.icon-sliders-horizontal { --icon: "\e29a"; }
.icon-sparkles { --icon: "\e412"; }
.icon-square-check-big { --icon: "\e16a"; }
.icon-square-code { --icon: "\e16b"; }
.icon-strikethrough { --icon: "\e177"; }
.icon-sun { --icon: "\e178"; }
.icon-table { --icon: "\e17d"; }
.icon-text-quote { --icon: "\e49e"; }
.icon-ticket { --icon: "\e20f"; }
.icon-trash-2 { --icon: "\e18e"; }
.icon-trending-up { --icon: "\e191"; }
.icon-triangle-alert { --icon: "\e193"; }
.icon-underline { --icon: "\e19a"; }
.icon-undo-2 { --icon: "\e2a1"; }
.icon-user { --icon: "\e19f"; }
.icon-user-minus { --icon: "\e1a1"; }
.icon-user-plus { --icon: "\e1a2"; }
.icon-users { --icon: "\e1a4"; }
.icon-wrench { --icon: "\e1b1"; }
.icon-x { --icon: "\e1b2"; }
.icon-zap { --icon: "\e1b4"; }

/* ===================================================================================================================
   Base
   =================================================================================================================== */

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

/* Keeps a fragment's target (#comment-…, #request-status, #request-priority) clear of the sticky app bar. The scrollbar
   colors inherit from the root to every scrolling box; the width does not, so every box gets the thin one. */
html {
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
    scroll-padding-top: calc(var(--app-bar-height) + var(--space-5));
    scrollbar-color: var(--color-scrollbar-thumb) var(--color-scrollbar-track);
}

* {
    scrollbar-width: thin;
}


/* Column layout keeps the footer at the bottom of the window on short pages. The page glow lies over the page color
   near the top only. Inter's cv11 (single-storey a) and ss01 (open digits) read calmer in dense tables. */
body {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    margin: 0;
    background-color: var(--color-page);
    background-image: var(--gradient-page);
    background-repeat: no-repeat;
    color: var(--color-text);
    font-family: var(--font-sans);
    font-size: var(--font-size-md);
    font-feature-settings: "cv11", "ss01";
    line-height: var(--line-height-base);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
    transition: opacity var(--duration-base) var(--ease-standard);
}

/* The boot gate of a full load: the layout's head script marks the page is-booting, and nav.js removes the mark once the
   page is built (translations, enhanced controls) and its fonts are loaded, so the page fades in once, finished. The
   canvas keeps the page's color meanwhile, as body's background belongs to it, not to the transparent body. */
html.is-booting body {
    opacity: 0;
}

h1, h2, h3, h4, h5, h6 {
    margin: var(--space-6) 0 var(--space-3);
    color: var(--color-text);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight);
}

/* Inter runs wide when set large, so the two big sizes close up. */
h1 {
    margin-top: 0;
    margin-bottom: var(--space-4);
    font-size: var(--font-size-xl);
    letter-spacing: var(--letter-spacing-tight);
}

h2 {
    font-size: var(--font-size-lg);
    letter-spacing: var(--letter-spacing-tight);
}

h3, h4, h5, h6 {
    margin-top: var(--space-5);
}

:is(h1, h2, h3, h4, h5, h6):first-child {
    margin-top: 0;
}

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

strong, b {
    font-weight: var(--font-weight-semibold);
}

/* The browser's own smaller sizes would add sizes beyond the four. */
small, sub, sup {
    font-size: var(--font-size-sm);
}

legend {
    padding: 0;
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight);
    letter-spacing: var(--letter-spacing-tight);
}

/* A faint underline that strengthens on hover: links stay recognizable in running text without shouting. */
a {
    color: var(--color-link);
    text-decoration-line: underline;
    text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
    text-decoration-thickness: 1px;
    text-underline-offset: 0.2em;
    transition: color var(--duration-fast) var(--ease-standard), text-decoration-color var(--duration-fast) var(--ease-standard);
}

a:hover {
    text-decoration-color: currentColor;
}

::selection {
    background-color: var(--color-selection);
    color: var(--color-text);
}

/* One focus ring for everything focusable. Inputs show focus with their border and halo instead (form controls),
   and controls whose real input is transparent (pills, option rows, the drop zone) draw it on their label. */
:focus-visible {
    outline: 2px solid var(--color-focus);
    outline-offset: 2px;
}

/* Form controls default to a 13.33px system font; they take the page's font instead. */
button, input, select, textarea {
    font: inherit;
    color: inherit;
}

code, pre, kbd, samp {
    font-family: var(--font-mono);
    font-size: var(--font-size-sm);
}

:not(pre) > code {
    padding: 0 var(--space-1);
    border-radius: var(--radius-xs);
    background-color: var(--color-surface-sunken);
    box-shadow: inset 0 0 0 1px var(--color-outline-variant);
}

pre {
    overflow-x: auto;
}

/* Images from comments and descriptions never widen the page. */
img {
    max-width: 100%;
    height: auto;
}

hr {
    margin: var(--space-5) 0;
    border: 0;
    border-top: 1px solid var(--color-outline-variant);
}

/* ===================================================================================================================
   Utilities
   =================================================================================================================== */

/* The drop zone's file input is hidden from the first paint when scripts run (the layout sets "js" on <html>);
   create-request.js lists the files instead, or sets is-fallback on the drop zone when it cannot. */
.visually-hidden,
.js .drop-zone:not(.is-fallback) input[type="file"] {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* The scripts hide and show things with the hidden attribute (menus, editor parts, comment forms, the send progress),
   and any display rule of this file would otherwise win over it. A transition still plays out first where one is
   declared (the menus), as transitions override important declarations. until-found keeps its own behavior. */
[hidden]:not([hidden="until-found"]) {
    display: none !important;
}

/* The root wrapper of a form or document page: a narrower, centered column. */
.page-narrow {
    max-width: var(--page-narrow-width);
    margin-inline: auto;
}

/* ===================================================================================================================
   Shell: app bar, navigation, user menu, mobile menu, main, footer
   =================================================================================================================== */

/* A fragment's target (#comment-…, #request-status, #request-priority) stays clear of the sticky app bar. */
html {
    scroll-padding-top: calc(var(--app-bar-height) + var(--space-4));
}

/* The app bar: sticky at every width, white with a hairline below. Where the browser
   blurs what is behind it, it turns translucent, so the page shows faintly as it scrolls under; without the blur it
   stays solid, as the page's text would show through it. app-shell.js sets is-scrolled once the page has scrolled,
   which trades the hairline for a soft shadow, and is-open while the narrow window's menu sheet is open. */
.site-header {
    position: sticky;
    top: 0;
    z-index: var(--z-app-bar);
    border-bottom: 1px solid var(--color-outline-variant);
    background-color: var(--color-surface);
    transition: box-shadow var(--duration-base) var(--ease-standard), border-color var(--duration-base) var(--ease-standard);
}

@supports (backdrop-filter: blur(12px)) or (-webkit-backdrop-filter: blur(12px)) {
    .site-header {
        background-color: color-mix(in srgb, var(--color-surface) 85%, transparent);
        -webkit-backdrop-filter: saturate(180%) blur(12px);
        backdrop-filter: saturate(180%) blur(12px);
    }
}

.site-header.is-scrolled {
    border-bottom-color: transparent;
    box-shadow: var(--shadow-2);
}

.site-header.is-open {
    background-color: var(--color-surface);
}

/* One row, as wide as main's content and lined up with it: brand, the navigation tabs, then the Admin tab and the tools at the end. */
.app-bar {
    display: flex;
    align-items: center;
    gap: var(--space-6);
    height: var(--app-bar-height);
    padding: 0 var(--page-end) 0 var(--page-start);
}

/* The brand name, a title: on a crowded row it gives way to an ellipsis first (the high shrink factor), before the
   user's name does, but keeps its first letters; the tabs never shrink. */
.brand {
    display: inline-flex;
    flex: 0 100 auto;
    align-items: center;
    gap: var(--space-3);
    min-width: 96px;
    color: var(--color-text);
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight);
    letter-spacing: var(--letter-spacing-tight);
    text-decoration: none;
}

.brand:hover {
    color: var(--color-text);
}

.brand-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The logo, in the app bar and the footer (Views/Shared/_BrandMark.cshtml): the compass in the theme's two logo colors,
   on no tile of its own. Hovering the brand lifts it slightly. */
.brand-mark {
    display: block;
    flex: none;
    width: 32px;
    height: 32px;
    transition: translate var(--duration-base) var(--ease-standard);
}

.brand-mark-ink {
    fill: var(--color-logo);
}

.brand-mark-arc {
    fill: var(--color-logo-accent);
}

.brand:hover .brand-mark {
    translate: 0 -1px;
}

/* The menu button shows only in a narrow window where scripts run (below); without them the menu is always shown. */
.site-header .app-menu-toggle {
    display: none;
}

/* What the menu button opens in a narrow window: the navigation and the tools. In a wide one it is just the rest of
   the row. A signed-out visitor's has only the preferences, which stay in the row at every width. */
.app-menu {
    display: flex;
    flex: 1 1 auto;
    align-items: center;
    gap: var(--space-5);
    min-width: 0;
}

/* The signed-out row keeps its preferences whole; the brand's name gives way instead. */
.app-menu:not(.app-menu-sheet) {
    flex-shrink: 0;
}

/* The tabs: the current page's is primary, with the accent line along the bottom edge of the app bar under it. They
   keep their width, so they never run into the tools; the row takes the space left over, so the tab that ends it
   (.app-nav-link-end, Admin) sits against the tools. */
.app-nav {
    display: flex;
    flex: 1 0 auto;
    align-items: center;
    gap: var(--space-1);
}

.app-nav-link {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: var(--space-3);
    height: var(--control-height);
    padding: 0 var(--space-3);
    border-radius: var(--radius-md);
    color: var(--color-text-muted);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
    text-decoration: none;
    white-space: nowrap;
    transition: background-color var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard);
}

.app-nav-link:hover {
    background-color: var(--color-state-hover);
    color: var(--color-text);
}

.app-nav-link[aria-current="page"] {
    color: var(--color-primary);
}

/* The tab pushed to the end of the row, next to the user menu. */
.app-nav-link-end {
    margin-left: auto;
}

/* Icons only in the menu sheet's rows. */
.app-nav-link .icon {
    display: none;
}

@media (min-width: 900px) {
    /* The tab is centered in the bar, so the bar's bottom edge is half the difference of their heights below it. */
    .app-nav-link[aria-current="page"]::after {
        content: "";
        position: absolute;
        right: var(--space-3);
        bottom: calc((var(--control-height) - var(--app-bar-height)) / 2);
        left: var(--space-3);
        height: 2px;
        border-radius: var(--radius-xs) var(--radius-xs) 0 0;
        background-image: var(--gradient-accent);
        pointer-events: none;
    }
}

/* The user menu, or a signed-out visitor's preferences menu, at the end of the row. */
.header-tools {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-width: 0;
    margin-left: auto;
}

/* "Quit impersonation" while a staff member uses the portal as a member: a button before the user menu, which keeps its
   size while the bar is tight. */
.quit-impersonation {
    flex: none;
}

/* A signed-out visitor's preferences: an icon button that opens the theme and language choices in a menu surface like
   the user menu's, also in a narrow window (the sheet is for signed-in users only). */
.preferences-menu {
    position: relative;
    flex: none;
}

.preferences-menu-summary {
    list-style: none;
    user-select: none;
}

.preferences-menu-summary::-webkit-details-marker {
    display: none;
}

.preferences-menu[open] > .preferences-menu-summary {
    background-color: var(--color-state-hover);
    color: var(--color-text);
}

/* The user menu: the avatar, the name (an ellipsis beyond 160px; the full text stays in the DOM), the staff badge and
   a chevron open a panel with the name, the address, the role, the theme and language choices and the sign-out item.
   app-shell.js closes it on Escape and outside clicks; without scripts the details element opens and closes by
   itself. */
.user-menu {
    position: relative;
    min-width: 0;
}

.user-menu-summary {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-width: 0;
    height: var(--control-height);
    padding: 0 var(--space-2) 0 var(--space-1);
    border-radius: var(--radius-pill);
    color: var(--color-text);
    list-style: none;
    cursor: pointer;
    user-select: none;
    transition: background-color var(--duration-fast) var(--ease-standard);
}

.user-menu-summary::-webkit-details-marker {
    display: none;
}

.user-menu-summary:hover,
.user-menu[open] > .user-menu-summary {
    background-color: var(--color-state-hover);
}

.user-menu-summary > :not(.user-name) {
    flex: none;
}

.site-header .user-name {
    display: block;
    min-width: 0;
    max-width: 160px;
    overflow: hidden;
    color: var(--color-text);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight);
    text-overflow: ellipsis;
    white-space: nowrap;
}

.user-menu-chevron {
    color: var(--color-text-muted);
    transition: rotate var(--duration-base) var(--ease-standard);
}

.user-menu[open] .user-menu-chevron {
    rotate: 180deg;
}

/* A menu surface below the summary, at its end, never wider than the window. */
.user-menu-panel,
.preferences-menu-panel {
    position: absolute;
    top: calc(100% + var(--space-2));
    right: 0;
    z-index: var(--z-menu);
    width: 280px;
    max-width: calc(100vw - 2 * var(--page-gutter));
    padding: 6px;
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-lg);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-3);
    animation: fade-in-down var(--duration-fast) var(--ease-emphasized);
}

/* The identity, the preferences and the sign-out item start 16px from the panel's edge (its 6px padding plus their
   10px). */
.user-menu-identity {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: var(--space-2) 10px;
    line-height: var(--line-height-tight);
}

.user-menu-name {
    color: var(--color-text);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-semibold);
    overflow-wrap: anywhere;
}

.user-menu-email {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    overflow-wrap: anywhere;
}

.user-menu-role {
    margin-top: var(--space-1);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

.user-menu-divider {
    margin: 6px 0;
    border: 0;
    border-top: 1px solid var(--color-divider);
}

/* The theme and language choices (_Preferences.cshtml), aligned with the identity above them. */
.preferences {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-2) 10px;
}

/* A full-width row of the panel: the hover layer spans the panel's content, the icon and text line up with the rest. */
.user-menu-item {
    display: flex;
    align-items: center;
    min-height: 36px;
    padding: 0 10px;
    border-radius: var(--radius-md);
    color: var(--color-text);
    line-height: var(--line-height-tight);
    text-decoration: none;
    transition: background-color var(--duration-fast) var(--ease-standard);
}

.user-menu-item::before {
    color: var(--color-text-muted);
}

.user-menu-item:hover {
    background-color: var(--color-state-hover);
    color: var(--color-text);
}

/* Behind the open menu sheet, below the app bar; app-shell.js closes the sheet on a click on it. Out of the header,
   whose blur would make it the containing block of anything fixed inside it. */
.app-menu-scrim {
    display: none;
}

/* Narrow windows: brand and the menu button in the bar; the navigation and the tools in a sheet under it. */
@media (max-width: 899px) {
    .app-bar {
        gap: var(--space-3);
    }

    .js .site-header .app-menu-toggle {
        display: inline-flex;
        margin-left: auto;
    }

    .app-menu-sheet {
        position: absolute;
        top: 100%;
        right: 0;
        left: 0;
        display: none;
        flex-direction: column;
        align-items: stretch;
        gap: var(--space-4);
        max-height: calc(100dvh - var(--app-bar-height));
        overflow-y: auto;
        overscroll-behavior: contain;
        padding: var(--space-4);
        border-radius: 0 0 var(--radius-xl) var(--radius-xl);
        background-color: var(--color-surface);
        box-shadow: var(--shadow-4);
    }

    .app-menu-sheet.is-open {
        display: flex;
        animation: slide-down var(--duration-base) var(--ease-emphasized);
    }

    .app-menu-scrim.is-open {
        position: fixed;
        inset: var(--app-bar-height) 0 0;
        z-index: calc(var(--z-app-bar) - 1);
        display: block;
        background-color: var(--color-scrim);
        animation: fade-in var(--duration-base) var(--ease-standard);
    }

    /* The tabs become full-width rows with their icons, the end tab too; the current one is tinted. */
    .app-menu-sheet .app-nav {
        flex: none;
        flex-direction: column;
        align-items: stretch;
    }

    .app-menu-sheet .app-nav-link {
        height: 48px;
        padding: 0 var(--space-4);
    }

    .app-menu-sheet .app-nav-link-end {
        margin-left: 0;
    }

    .app-menu-sheet .app-nav-link .icon {
        display: inline-block;
    }

    .app-menu-sheet .app-nav-link[aria-current="page"] {
        background-color: var(--color-primary-container);
        color: var(--color-on-primary-container);
    }

    /* The user menu below the tabs, full width. */
    .app-menu-sheet .header-tools {
        flex-direction: column;
        align-items: stretch;
        gap: var(--space-4);
        margin-left: 0;
    }

    /* The user menu is no dropdown here: app-shell.js keeps it open, and the summary shows who is signed in above the
       panel's content, which flows inline (the panel's name would repeat the summary's). Where the browser can,
       ::details-content shows the content even before the script or without it. */
    .app-menu-sheet .user-menu-summary {
        height: auto;
        min-height: var(--control-height);
        padding: 0 var(--space-4);
        border-radius: var(--radius-md);
    }

    .app-menu-sheet .user-menu-summary:hover,
    .app-menu-sheet .user-menu[open] > .user-menu-summary {
        background-color: transparent;
    }

    .js .app-menu-sheet .user-menu-summary {
        cursor: default;
    }

    .app-menu-sheet .user-name {
        max-width: none;
    }

    .app-menu-sheet .user-menu-chevron,
    .app-menu-sheet .user-menu-name {
        display: none;
    }

    .app-menu-sheet .user-menu-panel {
        position: static;
        width: auto;
        min-width: 0;
        max-width: none;
        padding: 0;
        border: 0;
        background-color: transparent;
        box-shadow: none;
        animation: none;
    }

    .app-menu-sheet .user-menu-item {
        min-height: 48px;
        padding: 0 var(--space-4);
    }

    /* The rows' content 16px in, like the tabs' above. */
    .app-menu-sheet .user-menu-identity,
    .app-menu-sheet .preferences {
        padding: var(--space-2) var(--space-4);
    }

    /* Without scripts there is no menu button: the sheet follows the bar in the flow, stacked, and the bar scrolls away
       with the page, as a sticky one would cover half the screen. */
    html:not(.js) .site-header {
        position: relative;
    }

    html:not(.js) .app-bar {
        flex-wrap: wrap;
        height: auto;
    }

    html:not(.js) .brand {
        height: var(--app-bar-height);
    }

    html:not(.js) .app-menu-sheet {
        position: static;
        display: flex;
        flex-basis: 100%;
        max-height: none;
        overflow: visible;
        padding: 0 0 var(--space-4);
        border-radius: 0;
        background-color: transparent;
        box-shadow: none;
    }
}

/* A rule of its own: a browser without this pseudo-element would drop a list it is part of. */
@media (max-width: 899px) {
    .app-menu-sheet .user-menu::details-content {
        content-visibility: visible;
    }
}

/* The page container: centered, as wide as the app bar's row. */
main {
    flex: 1 0 auto;
    width: 100%;
    padding: var(--space-6) var(--page-end) var(--space-8) var(--page-start);
}

/* After a client-side page change nav.js focuses the new page's heading (or main) for assistive technology; the ring
   would only point at a heading nobody can act on. */
main h1[tabindex="-1"]:focus,
main[tabindex="-1"]:focus {
    outline: none;
}

/* The progress bar of a client-side page change (nav.js), above the app bar: hidden and empty at rest. is-loading
   (set once the page takes a moment) grows it slowly towards 80 %, is-done fills it and then fades it out; nav.js
   removes is-done afterwards, which resets it at once, as only the two states have transitions. */
.nav-progress {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 3px;
    z-index: var(--z-nav-progress);
    background-image: var(--gradient-accent);
    opacity: 0;
    transform: scaleX(0);
    transform-origin: left;
    pointer-events: none;
    view-transition-name: nav-progress;
}

.nav-progress.is-loading {
    opacity: 1;
    transform: scaleX(0.8);
    transition: transform calc(var(--duration-slow) * 25) var(--ease-emphasized), opacity var(--duration-fast) var(--ease-standard);
}

.nav-progress.is-done {
    opacity: 0;
    transform: scaleX(1);
    transition: transform var(--duration-base) var(--ease-standard), opacity var(--duration-base) var(--ease-exit) var(--duration-base);
}

/* The page change of a client-side navigation (nav.js runs it inside document.startViewTransition): main is the one part
   that cross-fades, the old page out as the new one in. Everything else stays as it is: the root's pair (the
   background and the footer) shows the new state at once, as its old picture would only show the old surroundings, and
   the browser's default blend of two opaque pictures would brighten the page. The group of the page does not morph, so
   the new page's box is where the new page is, whatever the old page's height was, and the old picture is not
   stretched to it; after a scrolled page it fades out at the top of that box. */
main {
    view-transition-name: page;
}

/* A named part is drawn above the root's content, in the order of the elements' own painting. The sticky app bar and
   the progress bar are named so that they stay above the page's pictures (Back and Forward restore the scroll position,
   which puts the page under the app bar). Each shows its new state only: the app bar's old picture would show the
   translucent bar twice, and the progress bar goes on filling and fading while the pages cross-fade. */
.site-header {
    view-transition-name: app-bar;
}

::view-transition-group(root),
::view-transition-group(page),
::view-transition-group(app-bar),
::view-transition-group(nav-progress) {
    animation-name: none;
}

::view-transition-old(root),
::view-transition-old(app-bar),
::view-transition-old(nav-progress) {
    display: none;
}

::view-transition-new(root),
::view-transition-new(app-bar),
::view-transition-new(nav-progress) {
    animation: none;
    mix-blend-mode: normal;
}

::view-transition-old(page),
::view-transition-new(page) {
    animation-duration: var(--duration-base);
    animation-timing-function: var(--ease-standard);
}

/* The old page keeps the size it was drawn at. The browser's default makes a picture as wide as its group, which is the
   new page's box: when the page scrollbar comes or goes with the change, that box is a scrollbar's width narrower or wider than the old
   page was, and the old picture would be squeezed or stretched sideways while it fades out, which looks like the page
   wobbling. It sits at the top left of the box instead, so what the two pages share stays where it is. */
::view-transition-old(page) {
    inline-size: auto;
    block-size: auto;
}

/* The footer: slim, on the page's background below a hairline; the inner row lines up with main's content. */
.site-footer {
    border-top: 1px solid var(--color-outline-variant);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

.site-footer-inner {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    align-items: center;
    gap: var(--space-3) var(--space-5);
    padding: var(--space-5) var(--page-end) var(--space-5) var(--page-start);
}

.site-footer-brand {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--color-text);
    font-weight: var(--font-weight-medium);
    justify-self: start;
}

/* A smaller logo than the app bar's. */
.site-footer-brand .brand-mark {
    width: 24px;
    height: 24px;
}

.site-footer-links {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-5);
    justify-self: end;
}

.site-footer a {
    color: inherit;
    text-decoration: none;
}

.site-footer a:hover {
    color: var(--color-primary);
}

/* The language swap at the end of the links, drawn like them: EN and DE as plain text, the current one in the text color
   and medium weight, a hairline between them. */
.site-footer-links {
    align-items: center;
}

.site-footer-lang {
    display: inline-flex;
    align-items: center;
}

.site-footer-lang-option {
    padding: 0 var(--space-2);
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    cursor: pointer;
}

.site-footer-lang-option + .site-footer-lang-option {
    border-inline-start: 1px solid var(--color-divider);
}

.site-footer-lang-option:hover {
    color: var(--color-primary);
}

.site-footer-lang-option[aria-pressed="true"] {
    color: var(--color-text);
    font-weight: var(--font-weight-medium);
    cursor: default;
}

/* The copyright line sits in the middle of the footer row, between the brand and the links; it is centered. */
.site-footer-copyright {
    margin: 0;
    text-align: center;
}

/* Below 860px the three do not fit one row: they stack, centered. */
@media (max-width: 859px) {
    .site-footer-inner {
        grid-template-columns: minmax(0, 1fr);
        justify-items: center;
        text-align: center;
    }

    .site-footer-brand,
    .site-footer-links {
        justify-self: center;
        justify-content: center;
    }
}

/* ===================================================================================================================
   Components
   =================================================================================================================== */

/* Buttons: 40px with the md radius and a medium label, semibold on the filled ones (primary, danger, the hero's). The
   plain .btn is the outlined one (.btn-outline names it); the variants change the colors and that weight only. A hover or press on a surface is a state layer painted as a background image
   over the button's own color, so it works on white and on the page alike. Hover rules skip disabled buttons through
   :where(), which adds no specificity, so each variant's hover still wins over the base one by order. */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-height: var(--control-height);
    padding: 0 var(--space-4);
    border: 1px solid var(--color-outline-control);
    border-radius: var(--radius-md);
    background-color: var(--color-surface);
    color: var(--color-text);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
    text-align: center;
    text-decoration: none;
    cursor: pointer;
    user-select: none;
    transition:
        background-color var(--duration-fast) var(--ease-standard),
        border-color var(--duration-fast) var(--ease-standard),
        box-shadow var(--duration-fast) var(--ease-standard),
        color var(--duration-fast) var(--ease-standard),
        transform var(--duration-fast) var(--ease-standard);
}

.btn:where(:not(:disabled, [aria-disabled="true"])):hover {
    border-color: var(--color-outline-control-hover);
    background-image: linear-gradient(var(--color-state-hover) 0 0);
}

.btn:where(:not(:disabled, [aria-disabled="true"])):active {
    background-image: linear-gradient(var(--color-state-pressed) 0 0);
}

.btn:disabled,
.btn[aria-disabled="true"] {
    box-shadow: none;
    opacity: 0.45;
    cursor: not-allowed;
}

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

/* Lit from above; hover settles on the darker solid and lifts a step, pressing sets it down 1px. The transparent
   border keeps the outlined button's size and shows in forced-colors mode. */
.btn-primary {
    border-color: transparent;
    background-color: var(--color-primary);
    background-image: var(--gradient-primary);
    box-shadow: var(--shadow-1);
    color: var(--color-text-on-primary);
    font-weight: var(--font-weight-semibold);
}

.btn-primary:where(:not(:disabled, [aria-disabled="true"])):hover {
    border-color: transparent;
    background-color: var(--color-primary-hover);
    background-image: none;
    box-shadow: var(--shadow-2);
}

.btn-primary:where(:not(:disabled, [aria-disabled="true"])):active {
    background-color: var(--color-primary-pressed);
    background-image: none;
    box-shadow: var(--shadow-1);
    transform: translateY(1px);
}

.btn-tonal {
    border-color: transparent;
    background-color: var(--color-primary-container);
    color: var(--color-on-primary-container);
}

.btn-tonal:where(:not(:disabled, [aria-disabled="true"])):hover {
    border-color: transparent;
    background-color: var(--color-primary-container-hover);
    background-image: none;
}

.btn-danger {
    border-color: transparent;
    background-color: var(--color-danger);
    box-shadow: var(--shadow-1);
    color: var(--color-text-on-primary);
    font-weight: var(--font-weight-semibold);
}

.btn-danger:where(:not(:disabled, [aria-disabled="true"])):hover {
    border-color: transparent;
    background-color: var(--color-danger-hover);
    background-image: none;
    box-shadow: var(--shadow-2);
}

.btn-danger:where(:not(:disabled, [aria-disabled="true"])):active {
    transform: translateY(1px);
}

/* A text button: no edge until hovered. */
.btn-ghost {
    border-color: transparent;
    background-color: transparent;
    color: var(--color-text-muted);
}

.btn-ghost:where(:not(:disabled, [aria-disabled="true"])):hover {
    border-color: transparent;
    background-color: var(--color-state-hover);
    background-image: none;
    color: var(--color-text);
}

.btn-ghost:where(:not(:disabled, [aria-disabled="true"])):active {
    background-color: var(--color-state-pressed);
    background-image: none;
}

/* The call to action on the signed-out hero only, in the hero's own action colors: in the light theme white with primary
   text, the inverse of the primary button. Hover tints it and lifts it 1px, pressing sets it back down. */
.btn-inverse {
    border-color: transparent;
    background-color: var(--color-hero-action);
    box-shadow: var(--shadow-2);
    color: var(--color-text-on-hero-action);
    font-weight: var(--font-weight-semibold);
}

.btn-inverse:where(:not(:disabled, [aria-disabled="true"])):hover {
    border-color: transparent;
    background-color: var(--color-hero-action-hover);
    background-image: none;
    transform: translateY(-1px);
}

.btn-inverse:where(:not(:disabled, [aria-disabled="true"])):active {
    transform: translateY(0);
}

.btn-sm {
    min-height: var(--control-height-sm);
    padding: 0 var(--space-3);
}

.btn-block {
    display: flex;
    width: 100%;
}

/* Square and icon only, with the ghost look; the name is in a .visually-hidden span and the title. The glyph is a size
   up from the text, as icons alone read small. */
.btn-icon {
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: var(--control-height);
    height: var(--control-height);
    min-height: 0;
    padding: 0;
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    background-color: transparent;
    color: var(--color-text-muted);
    font-size: var(--font-size-lg);
    line-height: 1;
    text-decoration: none;
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard);
}

.btn-icon:where(:not(:disabled)):hover {
    background-color: var(--color-state-hover);
    color: var(--color-text);
}

.btn-icon:where(:not(:disabled)):active {
    background-color: var(--color-state-pressed);
}

.btn-icon:disabled {
    opacity: 0.45;
    cursor: not-allowed;
}

.btn-icon.btn-sm {
    width: var(--control-height-sm);
    height: var(--control-height-sm);
    padding: 0;
    font-size: var(--font-size-md);
}

/* Form controls: inputs, selects and textareas share one look, 40px high with the md radius. Focus shows as the
   primary border with a halo instead of the outline. */
.text-input {
    display: block;
    width: 100%;
    min-height: var(--control-height);
    padding: 0 var(--space-3);
    border: 1px solid var(--color-outline-control);
    border-radius: var(--radius-md);
    background-color: var(--color-surface);
    color: var(--color-text);
    font-size: var(--font-size-md);
    line-height: var(--line-height-tight);
    transition: border-color var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard);
}

.text-input::placeholder {
    color: var(--color-text-subtle);
    opacity: 1;
}

.text-input:hover {
    border-color: var(--color-outline-control-hover);
}

.text-input:focus {
    border-color: var(--color-primary);
    box-shadow: var(--focus-ring);
    outline: none;
}

.text-input[aria-invalid="true"] {
    border-color: var(--color-danger);
}

.text-input[aria-invalid="true"]:focus {
    border-color: var(--color-danger);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-danger) 20%, transparent);
}

textarea.text-input {
    padding: 10px var(--space-3);
    line-height: var(--line-height-base);
    resize: vertical;
}

/* No shadow rule here: at .text-input:focus's specificity, after it, it would take a focused field's halo away. */
.text-input[readonly] {
    background-color: var(--color-surface-sunken);
    color: var(--color-text-muted);
}

.text-input:disabled {
    background-color: var(--color-surface-sunken);
    color: var(--color-text-subtle);
    cursor: not-allowed;
}

/* The native file input: its button looks like a tonal .btn and its "no file chosen" text is muted; the labels come
   from the browser, so they follow its language. Focus shows as the halo on the button. */
.file-input {
    display: block;
    max-width: 100%;
    color: var(--color-text-muted);
    font-size: var(--font-size-md);
    line-height: var(--line-height-tight);
}

.file-input::file-selector-button {
    min-height: var(--control-height);
    margin-inline-end: var(--space-3);
    padding: 0 var(--space-4);
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    background-color: var(--color-primary-container);
    color: var(--color-on-primary-container);
    font: inherit;
    font-weight: var(--font-weight-medium);
    cursor: pointer;
    transition:
        background-color var(--duration-fast) var(--ease-standard),
        box-shadow var(--duration-fast) var(--ease-standard);
}

.file-input:where(:not(:disabled))::file-selector-button:hover {
    background-color: var(--color-primary-container-hover);
}

.file-input:focus-visible {
    outline: none;
}

.file-input:focus-visible::file-selector-button {
    box-shadow: var(--focus-ring);
}

.file-input:disabled {
    color: var(--color-text-subtle);
}

.file-input:disabled::file-selector-button {
    opacity: 0.45;
    cursor: not-allowed;
}

/* Without scripts every select draws its chevron from two gradient lines in its text color, so it needs no image and
   no markup. The editor's class moves to the button where dropdown.js draws one, so only a select gets it. */
select.text-input,
#page-size,
select.editor-select {
    appearance: none;
    padding-right: calc(var(--space-3) + 10px + var(--space-1));
    background-image:
        linear-gradient(45deg, transparent calc(50% - 0.8px), color-mix(in srgb, currentColor 70%, transparent) calc(50% - 0.8px) calc(50% + 0.8px), transparent calc(50% + 0.8px)),
        linear-gradient(135deg, transparent calc(50% - 0.8px), color-mix(in srgb, currentColor 70%, transparent) calc(50% - 0.8px) calc(50% + 0.8px), transparent calc(50% + 0.8px));
    background-position: right calc(var(--space-3) + 5px) center, right var(--space-3) center;
    background-repeat: no-repeat;
    background-size: 5px 5px;
    cursor: pointer;
}

/* Dropdowns: dropdown.js draws a button (.dropdown-toggle) and a listbox (.dropdown-menu) for every select with
   data-dropdown, together with the select in a positioned wrapper (.dropdown). The real select stays, as big as the
   button and invisible, so it still counts as visible and takes values from scripts. A button that carries
   .text-input (moved there from the select) is a form field as wide as its container; any other is a compact
   outlined button, which the app bar and the editor toolbar restyle in their own sections. */
.dropdown {
    position: relative;
    display: inline-flex;
    max-width: 100%;
}

.dropdown:has(> .text-input) {
    display: flex;
}

.dropdown-native {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    min-height: 0;
    opacity: 0;
    pointer-events: none;
}

/* The chevron is the last flex item, pushed to the end, so any padding a section gives the button keeps it clear. */
.dropdown-toggle {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    max-width: 100%;
    min-height: var(--control-height-sm);
    padding: 0 var(--space-2) 0 var(--space-3);
    border: 1px solid var(--color-outline-control);
    border-radius: var(--radius-md);
    background-color: var(--color-surface);
    color: var(--color-text);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
    text-align: left;
    white-space: nowrap;
    cursor: pointer;
    transition: border-color var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard);
}

.dropdown-toggle:hover {
    border-color: var(--color-outline-control-hover);
    background-image: linear-gradient(var(--color-state-hover) 0 0);
}

/* As a form field: the input's height, weight and padding, and the input's focus look while its menu is open (the
   focus is in the menu then). */
.dropdown-toggle.text-input {
    display: flex;
    min-height: var(--control-height);
    padding: 0 var(--space-3);
    font-weight: var(--font-weight-regular);
}

.dropdown-toggle.text-input:hover {
    background-image: none;
}

.dropdown-toggle.text-input[aria-expanded="true"] {
    border-color: var(--color-primary);
    box-shadow: var(--focus-ring);
}

.dropdown-toggle[aria-invalid="true"],
.dropdown-toggle[aria-invalid="true"]:hover {
    border-color: var(--color-danger);
}

/* The shown text and the sizers share one grid cell, which is as wide as the widest of them: the button is as wide as
   its widest option and keeps that width, whatever is chosen or the language is. A sizer takes the width of its text
   (drawn from data-text, so that it is no text of the page) and no height. A long choice ends in an ellipsis instead of
   widening the page. */
.dropdown-value {
    display: grid;
    flex: 1 1 auto;
    min-width: 0;
}

.dropdown-value > * {
    grid-area: 1 / 1;
}

.dropdown-value-text {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

.dropdown-sizer {
    height: 0;
    overflow: hidden;
    visibility: hidden;
    white-space: nowrap;
}

.dropdown-sizer::after {
    content: attr(data-text);
}

/* The "choose ..." prompt, shown while nothing is chosen. */
.dropdown-value.is-placeholder {
    color: var(--color-text-subtle);
}

/* Turns over while the menu is open. */
.dropdown-chevron {
    margin-inline-start: auto;
    color: var(--color-text-muted);
    pointer-events: none;
    transition: rotate var(--duration-base) var(--ease-standard);
}

.dropdown-toggle[aria-expanded="true"] .dropdown-chevron {
    rotate: 180deg;
}

/* Menus (this listbox, the combobox's, the filter menus and the user menu share the look): a white surface on the
   menu shadow with 36px items. It fades in and slides 4px from @starting-style, and back out when hidden before the
   hidden attribute's display: none takes effect (allow-discrete). */
.dropdown-menu {
    position: absolute;
    top: calc(100% + var(--space-1));
    left: 0;
    z-index: var(--z-menu);
    display: block;
    width: max-content;
    min-width: 100%;
    max-width: calc(100vw - 2 * var(--page-gutter));
    max-height: 360px;
    overflow-y: auto;
    padding: 6px;
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-lg);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-3);
    transition-property: opacity, translate, display;
    transition-duration: var(--duration-fast);
    transition-timing-function: var(--ease-emphasized);
    transition-behavior: allow-discrete;
}

/* The active option shows where the focus is, so the menu has no ring of its own; while the keys drive the menu
   (is-keyboard, set by dropdown.js) the active option has one, as the hover's tint alone is too faint. */
.dropdown-menu:focus-visible {
    outline: none;
}

/* No clicks while it fades out. */
.dropdown-menu[hidden] {
    opacity: 0;
    translate: 0 calc(-1 * var(--space-1));
    pointer-events: none;
    transition-timing-function: var(--ease-exit);
}

@starting-style {
    .dropdown-menu {
        opacity: 0;
        translate: 0 calc(-1 * var(--space-1));
    }

    .dropdown-menu.is-above {
        translate: 0 var(--space-1);
    }
}

/* dropdown.js sets these when the menu would leave the window below the button or at its right edge. */
.dropdown-menu.is-above {
    top: auto;
    bottom: calc(100% + var(--space-1));
}

.dropdown-menu.is-above[hidden] {
    translate: 0 var(--space-1);
}

.dropdown-menu.is-end {
    left: auto;
    right: 0;
}

.dropdown-option {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    min-height: 36px;
    padding: 0 var(--space-3);
    border-radius: var(--radius-md);
    color: var(--color-text);
    font-size: var(--font-size-md);
    line-height: var(--line-height-tight);
    white-space: nowrap;
    cursor: pointer;
}

.dropdown-option.is-active {
    background-color: var(--color-state-hover);
}

.dropdown-option[aria-selected="true"] {
    background-color: var(--color-primary-container);
    color: var(--color-on-primary-container);
    font-weight: var(--font-weight-medium);
}

.dropdown-option[aria-selected="true"].is-active {
    background-color: var(--color-primary-container-hover);
}

.dropdown-menu.is-keyboard .dropdown-option.is-active {
    outline: 2px solid var(--color-focus);
    outline-offset: -2px;
}

.dropdown-option[aria-disabled="true"] {
    color: var(--color-text-subtle);
    cursor: not-allowed;
}

.dropdown-check {
    color: var(--color-primary);
}

/* Comboboxes: combobox.js wraps an input[data-combobox] in .combobox together with a listbox that is a dropdown menu
   (.dropdown-menu, with its options and its is-above, is-keyboard and is-active states) as wide as the input. The
   input is a plain form field. An option shows the address and, muted at the end, the organization; both end in an
   ellipsis instead of widening the menu. */
.combobox {
    position: relative;
    display: block;
}

.combobox-menu {
    width: 100%;
    max-height: 320px;
}

.combobox-address,
.combobox-org {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

.combobox-org {
    flex-shrink: 2;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    text-align: right;
}

.combobox-match {
    background-color: transparent;
    color: var(--color-primary);
    font-weight: var(--font-weight-semibold);
}

/* A message in the list, no option: the keys and the mouse pass it by. */
.combobox-status {
    padding: var(--space-2) var(--space-3);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-tight);
}

/* A field: label, control, then its note or error, 6px apart. Items stretch to the field's width, except a compact
   dropdown, whose menu aligns with its own button. */
.form-field {
    display: grid;
    gap: 6px;
    min-width: 0;
    margin-bottom: var(--space-5);
}

.form-field > .dropdown {
    justify-self: start;
}

.form-field > .dropdown:has(> .text-input) {
    justify-self: stretch;
}

.form-label {
    display: block;
    color: var(--color-text);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
}

.required-mark {
    margin-left: var(--space-1);
    color: var(--color-danger);
}

.required-note {
    margin: 0 0 var(--space-4);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

.field-error, .field-note {
    margin: 6px 0 0;
    font-size: var(--font-size-sm);
    line-height: var(--line-height-base);
}

.form-field > :is(.field-error, .field-note) {
    margin-top: 0;
}

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

/* The alert glyph is drawn before the text, so it survives scripts setting the message. */
.field-error {
    color: var(--color-danger);
}

.field-error::before {
    content: "\e077" / "";
    margin-inline-end: var(--space-1);
}

.form-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
}

.form-errors ul {
    margin: var(--space-1) 0 0;
    padding-left: var(--space-5);
}

/* On a phone the buttons take the full width, one per row, the primary one first. */
@media (max-width: 640px) {
    .form-actions {
        flex-direction: column;
        align-items: stretch;
    }

    .form-actions > .btn {
        width: 100%;
    }

    .form-actions > :is(.btn-primary, .btn-danger) {
        order: -1;
    }
}

/* Switch: the checkbox itself is drawn as the track, its ::before as the thumb, so the real input stays the click
   target and keeps its focus ring. The transparent borders show the shapes in forced-colors mode. The off track is
   the secondary blue (3.9:1 on white), apart from the primary blue of the on track, so the control meets the 3:1 asked
   of a component's boundary. */
.switch-field {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-height: var(--control-height-sm);
    font-size: var(--font-size-md);
    cursor: pointer;
}

input.switch {
    appearance: none;
    position: relative;
    flex: none;
    width: var(--switch-width);
    height: 20px;
    margin: 0;
    border: 1px solid transparent;
    border-radius: var(--radius-pill);
    background-color: var(--color-switch-off);
    cursor: pointer;
    transition: background-color var(--duration-base) var(--ease-standard);
}

input.switch::before {
    content: "";
    position: absolute;
    top: 1px;
    left: 1px;
    width: 16px;
    height: 16px;
    border: 1px solid transparent;
    border-radius: var(--radius-pill);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-1);
    transition: translate var(--duration-base) var(--ease-standard);
}

input.switch:hover {
    background-color: var(--color-switch-off-hover);
}

input.switch:checked {
    background-color: var(--color-primary);
}

input.switch:checked:hover {
    background-color: var(--color-primary-pressed);
}

input.switch:checked::before {
    translate: 16px 0;
}

/* One look for every switch that cannot be used: disabled, or in a form that is on its way (the members pages' busy
   forms, which only change the cursor, below). */
input.switch:disabled,
.member-form[aria-busy="true"] input.switch,
.organization-access-all-form[aria-busy="true"] input.switch {
    opacity: 0.65;
    cursor: not-allowed;
}

.switch-field:has(> input.switch:disabled) {
    cursor: not-allowed;
}

/* A switch in a field, under its note: the switch row is taller than the switch, so it is pulled up by that slack to sit
   as close under the note as the note sits under the input. */
.form-field > .switch-field {
    margin-top: -6px;
}

/* A note under a switch starts at the switch's left edge, like the all-members note on an organization's page, and sits
   a small step under the switch row, with a little room below it. */
.switch-note {
    margin-top: var(--space-2);
    margin-bottom: var(--space-1);
}

/* Pill options (multi-select chips): the checkbox is transparent and covers the whole pill, so a click anywhere on it
   hits the real input, which stays focusable and enabled. Checked pills take the tonal accent and grow a check mark,
   which is collapsed to no width otherwise. */
.pill-options {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.pill-option {
    position: relative;
    display: inline-flex;
    align-items: center;
    min-height: var(--control-height-sm);
    padding: 0 var(--space-3);
    border: 1px solid var(--color-outline-control);
    border-radius: var(--radius-pill);
    background-color: var(--color-surface);
    color: var(--color-text);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
    cursor: pointer;
    user-select: none;
    transition: background-color var(--duration-fast) var(--ease-standard), border-color var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard);
}

.pill-option > input {
    position: absolute;
    inset: 0;
    z-index: 1;
    width: 100%;
    height: 100%;
    margin: 0;
    border-radius: inherit;
    opacity: 0;
    cursor: pointer;
}

.pill-option-check {
    width: 0;
    overflow: hidden;
    opacity: 0;
    transition: width var(--duration-base) var(--ease-standard), margin var(--duration-base) var(--ease-standard), opacity var(--duration-base) var(--ease-standard);
}

.pill-option:hover {
    border-color: var(--color-outline-control-hover);
    background-image: linear-gradient(var(--color-state-hover) 0 0);
}

.pill-option:has(> input:checked) {
    border-color: var(--color-primary);
    background-color: var(--color-primary-container);
    color: var(--color-on-primary-container);
}

.pill-option:has(> input:checked):hover {
    background-color: var(--color-primary-container-hover);
    background-image: none;
}

.pill-option:has(> input:checked) .pill-option-check {
    width: 1em;
    margin-inline-end: var(--space-1);
    opacity: 1;
}

.pill-option:has(> input:focus-visible) {
    outline: 2px solid var(--color-focus);
    outline-offset: 2px;
}

.pill-option:has(> input:disabled) {
    opacity: 0.45;
    cursor: not-allowed;
}

/* A pill that is a link (the organizer's switch between organizations): the current page's takes the checked look. */
a.pill-option,
a.pill-option:hover {
    text-decoration: none;
}

a.pill-option[aria-current="page"] {
    border-color: var(--color-primary);
    background-color: var(--color-primary-container);
    color: var(--color-on-primary-container);
}

a.pill-option[aria-current="page"]:hover {
    background-color: var(--color-primary-container-hover);
    background-image: none;
}

a.pill-option:focus-visible {
    outline: 2px solid var(--color-focus);
    outline-offset: 2px;
}

/* Segmented controls (one of a few choices that applies at once: the theme, the language): the radios of a fieldset
   joined in one frame, equal widths, so a group fills the row (the icon-only variant is below). Each radio covers its
   segment like the pill's checkbox, so a click on the text hits it and the arrow keys move within the group; the
   checked segment takes the tonal accent and lifts a little. */
.segmented-field {
    min-width: 0;
    margin: 0;
    padding: 0;
    border: 0;
}

.segmented-field > legend {
    margin-bottom: var(--space-2);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    letter-spacing: normal;
}

.segmented {
    display: flex;
    gap: 2px;
    padding: 2px;
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-md);
    background-color: var(--color-surface);
}

.segment {
    position: relative;
    display: inline-flex;
    flex: 1 1 0;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-width: 0;
    height: var(--control-height-sm);
    padding: 0 var(--space-3);
    border-radius: var(--radius-sm);
    color: var(--color-text-muted);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
    white-space: nowrap;
    cursor: pointer;
    user-select: none;
    transition: background-color var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard);
}

.segment > input {
    position: absolute;
    inset: 0;
    z-index: 1;
    width: 100%;
    height: 100%;
    margin: 0;
    border-radius: inherit;
    opacity: 0;
    cursor: pointer;
}

.segment > span:not(.icon) {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

.segment:hover {
    background-color: var(--color-state-hover);
    color: var(--color-text);
}

.segment:has(> input:checked) {
    background-color: var(--color-primary-container);
    box-shadow: var(--shadow-1);
    color: var(--color-on-primary-container);
}

.segment:has(> input:focus-visible) {
    outline: 2px solid var(--color-focus);
    outline-offset: 1px;
}

/* Icons only (the theme): equal segments across the row like the text ones, the icon centered, the same checked look.
   The names are .visually-hidden spans and the tooltips. */
.segmented-icons > .segment {
    padding: 0;
}

/* Cards: white surfaces with a hairline and the resting elevation. A card with a header or body parts has no padding of
   its own; the parts bring theirs. Cards do not clip their content, so menus inside them can open past their edge; the
   footer rounds its own corners instead, and only cards around a table clip, for the table's corners. */
.card {
    min-width: 0;
    padding: var(--space-5);
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-lg);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-1);
}

.card + .card {
    margin-top: var(--space-5);
}

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

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

.card-flush,
.card:has(> .card-header, > .card-body) {
    padding: 0;
}

.card-flush,
.card:has(> .table-wrap) {
    overflow: clip;
}

.card-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2) var(--space-4);
    padding: var(--space-4) var(--space-5);
    border-bottom: 1px solid var(--color-divider);
}

.card-title {
    margin: 0;
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight);
    letter-spacing: var(--letter-spacing-tight);
}

/* A sentence in a card header (the admin email list's note) takes the free width and wraps; the action stays at the end. */
.card-note {
    flex: 1 1 24rem;
    margin: 0;
    color: var(--color-text-muted);
}

.card-body {
    padding: var(--space-5);
}

/* Two bodies in a row share one gap, and a table after a body starts on a hairline. */
.card-body + .card-body {
    padding-top: 0;
}

.card-body + .table-wrap {
    border-top: 1px solid var(--color-divider);
}

.card-body > :first-child {
    margin-top: 0;
}

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

/* 1px inside the card's radius, as the card's border lies around it. */
.card-footer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-3);
    padding: var(--space-4) var(--space-5);
    border-top: 1px solid var(--color-divider);
    border-radius: 0 0 calc(var(--radius-lg) - 1px) calc(var(--radius-lg) - 1px);
    background-color: var(--color-surface-sunken);
}

/* A card whose form holds both the body and the footer with the form's actions: the card stretches the form over its
   height, the body takes what is left and the footer stays at the bottom, so the actions of cards side by side sit on
   one line. The actions start at the left (.form-actions on the footer wraps and stacks them on a phone). A card whose
   content is swapped as a whole (.card-content, the panels of the Configuration page) wraps its form, or its body, in
   the same way. */
.card:has(> .card-form, > .card-content) {
    display: flex;
    flex-direction: column;
}

.card-form,
.card-content {
    display: flex;
    flex: 1;
    flex-direction: column;
    min-width: 0;
}

/* The content's height is what admin-panel-edit.js animates, so its basis is its height, not zero. */
.card-content {
    flex-basis: auto;
}

.card-form > .card-body,
.card-content > .card-body {
    flex: 1;
}

/* On the card's own color, not the sunken one of a table header, which the footer would otherwise be taken for. */
.card-form > .card-footer {
    justify-content: flex-start;
    background-color: var(--color-surface);
}

/* A body after read-only content keeps the gap of two bodies in a row; after a form with a footer it has its own padding. */
.card-content:not(:has(> .card-form)) + .card-body {
    padding-top: 0;
}

@media (max-width: 640px) {
    /* Only cards that pad themselves; flush cards and cards with parts keep none. :where() leaves the specificity of
       .card, so page rules for particular cards still win. */
    .card:where(:not(.card-flush):not(:has(> .card-header, > .card-body))) {
        padding: var(--space-4);
    }

    .card-header, .card-body, .card-footer {
        padding-inline: var(--space-4);
    }

    .card-body {
        padding-block: var(--space-4);
    }

    .card-body + .card-body,
    .card-content:not(:has(> .card-form)) + .card-body {
        padding-top: 0;
    }
}

/* A page title with its actions at the end, which wrap below it on a narrow window. Breadcrumbs sit above the title in
   .page-heading-text. */
.page-heading {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3) var(--space-4);
    margin-bottom: var(--space-5);
}

.page-heading h1 {
    margin: 0;
}

.page-heading-text {
    min-width: 0;
}

.page-heading .breadcrumbs ol {
    margin-bottom: var(--space-2);
}

.page-subtitle {
    margin: var(--space-1) 0 0;
    color: var(--color-text-muted);
}

.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-7) var(--space-5);
    color: var(--color-text-muted);
    text-align: center;
}

.empty-state > .icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    border-radius: var(--radius-pill);
    background-color: var(--color-primary-container);
    color: var(--color-on-primary-container);
    font-size: var(--font-size-lg);
}

/* The title is a card title, also on a paragraph. */
.empty-state :is(h2, h3, .empty-state-title) {
    margin: 0;
    color: var(--color-text);
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight);
    letter-spacing: var(--letter-spacing-tight);
}

.empty-state > p {
    margin: 0;
}

/* Banners: the icon comes from ::before, so server-rendered and script-built banners get it without markup. It sits
   in the left padding, centered on the first line, and leaves the content's own flow (paragraphs, lists) alone. A
   banner without a variant is an info banner. */
.banner {
    position: relative;
    margin-bottom: var(--space-4);
    padding: var(--space-3) var(--space-4) var(--space-3) calc(var(--space-4) + 1em + var(--space-3));
    border: 1px solid var(--color-info-outline);
    border-radius: var(--radius-md);
    background-color: var(--color-info-container);
    color: var(--color-text);
    overflow-wrap: break-word;
}

/* As tall as the first line of text, with the glyph centered in it. */
.banner::before {
    content: "\e0f9" / "";
    position: absolute;
    top: var(--space-3);
    left: var(--space-4);
    height: auto;
    color: var(--color-primary);
    line-height: var(--line-height-base);
}

.banner > p {
    margin: 0;
}

.banner > p + p,
.banner > p + ul {
    margin-top: var(--space-1);
}

.banner-error {
    border-color: var(--color-danger-outline);
    background-color: var(--color-danger-container);
}

.banner-error::before {
    content: "\e077" / "";
    color: var(--color-danger);
}

.banner-success {
    border-color: var(--color-success-outline);
    background-color: var(--color-success-container);
}

.banner-success::before {
    content: "\e226" / "";
    color: var(--color-success);
}

.banner-warning {
    border-color: var(--color-warning-outline);
    background-color: var(--color-warning-container);
}

.banner-warning::before {
    content: "\e193" / "";
    color: var(--color-warning);
}

.banner-info {
    border-color: var(--color-info-outline);
    background-color: var(--color-info-container);
}

/* Badges: small tonal pills. The role, internal, log type and mail status badges keep their own names and need no badge class. */
.badge,
.role-badge,
.badge-internal,
.log-entry-type,
.notification-status {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    min-height: 20px;
    padding: 0 var(--space-2);
    border: 1px solid transparent;
    border-radius: var(--radius-pill);
    background-color: var(--color-primary-container);
    color: var(--color-on-primary-container);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
    white-space: nowrap;
    vertical-align: middle;
}

/* Ink on amber (8.2:1): staff-only content stands out at a glance. */
.badge-internal {
    background-color: var(--color-internal-badge-bg);
    color: var(--color-internal-badge-fg);
}

/* Tonal variants of the plain badge: green for a good answer, red for a bad one. */
.badge-success {
    border-color: var(--color-success-outline);
    background-color: var(--color-success-container);
    color: var(--color-success);
}

.badge-danger {
    border-color: var(--color-danger-outline);
    background-color: var(--color-danger-container);
    color: var(--color-danger);
}

.log-entry-type {
    border-color: var(--color-outline-variant);
    background-color: var(--color-surface-sunken);
    color: var(--color-text-muted);
}

/* After .log-entry-type, which it overrides at equal specificity. */
.log-entry-type-error {
    border-color: var(--color-danger-outline);
    background-color: var(--color-danger-container);
    color: var(--color-danger);
}

/* What a member or the team did in the portal, in the primary tones; after .log-entry-type too. */
.log-entry-type-activity {
    border-color: var(--color-info-outline);
    background-color: var(--color-primary-container);
    color: var(--color-on-primary-container);
}

/* The status of a mail in the queue: sent in green, waiting in amber, out of attempts in red. After the badge rules
   above, which they override at equal specificity. */
.notification-status-sent {
    border-color: var(--color-success-outline);
    background-color: var(--color-success-container);
    color: var(--color-success);
}

.notification-status-pending {
    border-color: var(--color-warning-outline);
    background-color: var(--color-warning-container);
    color: var(--color-warning);
}

.notification-status-failed {
    border-color: var(--color-danger-outline);
    background-color: var(--color-danger-container);
    color: var(--color-danger);
}

/* Status chips: the status name as written (never upper case) after a dot in the category's hue. --lozenge-height is
   local to the chip; the stacked table's two-line chips derive their padding from it. */
.lozenge {
    --lozenge-height: 22px;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: var(--lozenge-height);
    padding: 0 10px 0 var(--space-2);
    border-radius: var(--radius-pill);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
    text-transform: none;
    white-space: nowrap;
    vertical-align: middle;
}

.lozenge::before {
    content: "";
    flex: none;
    width: 6px;
    height: 6px;
    border-radius: var(--radius-pill);
    background-color: currentColor;
}

.lozenge-new, .lozenge-default {
    background-color: var(--color-status-new-bg);
    color: var(--color-status-new-fg);
}

.lozenge-new::before, .lozenge-default::before {
    background-color: var(--color-status-new-dot);
}

.lozenge-indeterminate {
    background-color: var(--color-status-indeterminate-bg);
    color: var(--color-status-indeterminate-fg);
}

.lozenge-indeterminate::before {
    background-color: var(--color-status-indeterminate-dot);
}

.lozenge-done {
    background-color: var(--color-status-done-bg);
    color: var(--color-status-done-fg);
}

.lozenge-done::before {
    background-color: var(--color-status-done-dot);
}

/* A flex cell would stop behaving as a table cell, so the icon keeps its gap to the name with a margin. */
.priority-icon {
    margin-right: var(--space-2);
    color: var(--color-priority-unknown);
}

.priority-highest { color: var(--color-priority-highest); }
.priority-high { color: var(--color-priority-high); }
.priority-medium { color: var(--color-priority-medium); }
.priority-low { color: var(--color-priority-low); }
.priority-lowest { color: var(--color-priority-lowest); }
.priority-unknown { color: var(--color-priority-unknown); }

/* One tile for every issue type, so the type reads from its icon and name rather than a color. */
.type-icon {
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: var(--radius-sm);
    background-image: var(--gradient-tile);
    color: var(--color-on-primary-container);
    font-size: var(--font-size-md);
    vertical-align: middle;
}

/* Photos, initials (avatar-placeholder) and icons (avatar-system) share the one round shape. A tone class
   (AvatarTones.ClassFor) gives a person's initials their own colors; it comes after the placeholder to win over it. */
.avatar {
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    overflow: hidden;
    border-radius: var(--radius-pill);
    background-color: var(--color-surface-sunken);
    object-fit: cover;
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    letter-spacing: normal;
    line-height: 1;
    vertical-align: middle;
    user-select: none;
}

.avatar-sm {
    width: 24px;
    height: 24px;
}

.avatar-lg {
    width: 40px;
    height: 40px;
    font-size: var(--font-size-md);
}

.avatar-placeholder {
    background-color: var(--color-primary-container);
    color: var(--color-on-primary-container);
}

.avatar-system {
    background-color: var(--color-surface-sunken);
    box-shadow: inset 0 0 0 1px var(--color-outline-variant);
    color: var(--color-primary);
}

.avatar-tone-1 { background-color: var(--color-avatar-1-bg); color: var(--color-avatar-1-fg); }
.avatar-tone-2 { background-color: var(--color-avatar-2-bg); color: var(--color-avatar-2-fg); }
.avatar-tone-3 { background-color: var(--color-avatar-3-bg); color: var(--color-avatar-3-fg); }
.avatar-tone-4 { background-color: var(--color-avatar-4-bg); color: var(--color-avatar-4-fg); }
.avatar-tone-5 { background-color: var(--color-avatar-5-bg); color: var(--color-avatar-5-fg); }
.avatar-tone-6 { background-color: var(--color-avatar-6-bg); color: var(--color-avatar-6-fg); }

/* Tables: a sunken header row in small medium-weight text, hairline rows and a hover tint. A wide table scrolls in .table-wrap
   instead of widening the page. */
.table-wrap {
    overflow-x: auto;
}

.data-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    font-size: var(--font-size-md);
}

.data-table th,
.data-table td {
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--color-divider);
    text-align: left;
    vertical-align: middle;
}

.data-table th {
    height: 40px;
    padding-block: 0;
    border-bottom-color: var(--color-outline-variant);
    background-color: var(--color-surface-sunken);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
    white-space: nowrap;
}

.data-table > tbody > tr {
    transition: background-color var(--duration-fast) var(--ease-standard);
}

.data-table > tbody > tr:hover {
    background-color: var(--color-state-hover);
}

/* Inside a flush card the card's border closes the table. */
.card-flush .data-table > tbody > tr:last-child > td {
    border-bottom: 0;
}

/* A table's title stands above its card as a section heading, with the number of rows in a badge, so the card's only
   header band is the table's own header row. */
.table-heading {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin: var(--space-6) 0 var(--space-3);
}

.table-heading:first-child {
    margin-top: 0;
}

.table-heading-title {
    margin: 0;
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight);
    letter-spacing: var(--letter-spacing-tight);
}

/* The footer of a table's card: the rows shown at the start, the page size and the pager at the end; on a phone they
   wrap. The card does not clip (the page size's menu opens past its edge), so the table's wrap rounds the card's top
   corners for the header row and the footer its bottom ones. */
.card:has(> .table-footer) {
    overflow: visible;
}

.card:has(> .table-footer) .table-wrap {
    border-radius: calc(var(--radius-lg) - 1px) calc(var(--radius-lg) - 1px) 0 0;
}

.table-footer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2) var(--space-4);
    min-height: 48px;
    padding: var(--space-2) var(--space-4);
    border-top: 1px solid var(--color-outline-variant);
    border-radius: 0 0 calc(var(--radius-lg) - 1px) calc(var(--radius-lg) - 1px);
    background-color: var(--color-surface-sunken);
}

.table-range,
.table-page-info {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-variant-numeric: tabular-nums;
}

.table-footer-end {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-4);
    margin-left: auto;
}

.table-footer form {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.table-pager {
    display: flex;
    align-items: center;
    gap: var(--space-1);
}

.table-pager .table-page-info {
    padding-inline: var(--space-1);
}

/* A row of its own below the rows (the request list's "Show more"), centered between hairlines. */
.table-more {
    display: flex;
    justify-content: center;
    padding: var(--space-2) var(--space-4);
    border-top: 1px solid var(--color-divider);
}

.table-more:empty {
    display: none;
}

.table-more > .banner {
    width: 100%;
    margin: 0;
}

/* The actions of a row sit at its end, icon buttons side by side; the column's header names them only for assistive
   technology. */
.data-table th.member-actions,
.data-table td.member-actions {
    text-align: right;
}

/* A stacked row starts its actions where its other values start, and its label stays aligned with the others. */
@media (max-width: 640px) {
    .data-table-stack td.member-actions {
        text-align: left;
    }
}

.row-actions {
    display: inline-flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-1);
}

.row-actions > form {
    display: contents;
}

/* A row's menu: the browser's popover in the top layer (the table wrap cannot clip it), under the ⋯ button with its end
   edge on the button's; without anchor positioning it shows in the middle of the window, as a popover does by default.
   Each item is a full-width button with its icon, the hover tint across the menu. */
.row-menu-toggle {
    anchor-name: --row-menu;
}

.row-menu-scope {
    anchor-scope: --row-menu;
}

.row-menu {
    min-width: 12rem;
    padding: 6px;
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-lg);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-3);
    color: var(--color-text);
    font-size: var(--font-size-md);
    text-align: left;
    white-space: nowrap;
}

@supports (position-area: bottom) {
    .row-menu {
        position-anchor: --row-menu;
        position-area: bottom span-left;
        position-try-fallbacks: flip-block;
        inset: auto;
        margin: var(--space-1) 0;
    }
}

.row-menu > form {
    display: block;
}

.row-menu-item {
    display: flex;
    align-items: center;
    width: 100%;
    min-height: 36px;
    padding: 0 10px;
    border: 0;
    border-radius: var(--radius-md);
    background: none;
    color: var(--color-text);
    font: inherit;
    line-height: var(--line-height-tight);
    text-align: left;
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease-standard);
}

.row-menu-item::before {
    color: var(--color-text-muted);
}

.row-menu-item:hover,
.row-menu-item:focus-visible {
    background-color: var(--color-state-hover);
}

.row-menu-item-danger,
.row-menu-item-danger::before {
    color: var(--color-danger);
}

/* On a phone each row becomes a card-like block of its own between hairlines, inside the table's card, and each cell
   shows its column's name (data-label) at its left. The label is positioned in the cell's padding, so the content
   keeps its inline flow. A sortable header turns into one line of sort pills above the rows, which scrolls sideways on
   its own (its scrollbar hidden) rather than wrapping into rows; one without sort buttons only stays for screen
   readers. */
@media (max-width: 640px) {
    .data-table-stack,
    .data-table-stack > tbody,
    .data-table-stack > tbody > tr,
    .data-table-stack > tbody > tr > td {
        display: block;
    }

    .data-table-stack > thead {
        display: block;
        border-bottom: 1px solid var(--color-outline-variant);
        background-color: var(--color-surface-sunken);
    }

    /* The right edge fades out, so that a pill cut off there reads as "more to scroll to" rather than a clipped
       button. Only the mask's alpha counts, so the opaque text color serves. The end padding is as wide as the fade,
       so the last pill is fully visible once scrolled to the end; the row's background and hairline are the header's,
       which the fade leaves alone. */
    .data-table-stack > thead > tr {
        display: flex;
        gap: var(--space-2);
        overflow-x: auto;
        padding: var(--space-3) var(--space-5) var(--space-3) var(--space-4);
        scroll-padding-inline: var(--space-4) var(--space-5);
        scroll-snap-type: x proximity;
        scrollbar-width: none;
        -webkit-mask-image: linear-gradient(to right, currentColor calc(100% - var(--space-5)), transparent);
        mask-image: linear-gradient(to right, currentColor calc(100% - var(--space-5)), transparent);
    }

    .data-table-stack > thead > tr::-webkit-scrollbar {
        display: none;
    }

    .data-table-stack > thead > tr > th {
        display: block;
        flex: none;
        height: auto;
        padding: 0;
        border: 0;
        background-color: transparent;
        scroll-snap-align: start;
    }

    .data-table-stack > thead:not(:has(.sort-button)) {
        position: absolute;
        width: 1px;
        height: 1px;
        margin: -1px;
        overflow: hidden;
        clip: rect(0, 0, 0, 0);
        white-space: nowrap;
    }

    .data-table-stack .sort-button {
        min-height: var(--control-height-sm);
        padding: 0 var(--space-3);
        border: 1px solid var(--color-outline-control);
        border-radius: var(--radius-pill);
        background-color: var(--color-surface);
        color: var(--color-text);
        font-weight: var(--font-weight-medium);
    }

    .data-table-stack .sort-button:hover {
        border-color: var(--color-outline-control-hover);
        background-image: linear-gradient(var(--color-state-hover) 0 0);
    }

    .data-table-stack th[aria-sort] .sort-button {
        border-color: var(--color-primary);
        background-color: var(--color-primary-container);
        color: var(--color-on-primary-container);
    }

    .data-table-stack > tbody > tr {
        padding: var(--space-3) var(--space-4);
    }

    .data-table-stack > tbody > tr + tr {
        border-top: 1px solid var(--color-divider);
    }

    /* At least one line high, so a label never runs into the next cell's; an empty cell has nothing to label. */
    .data-table-stack > tbody > tr > td {
        position: relative;
        min-width: 0;
        min-height: calc(var(--line-height-base) * var(--font-size-md) + 2 * var(--space-1));
        padding: var(--space-1) 0 var(--space-1) calc(7.5rem + var(--space-3));
        border: 0;
        overflow-wrap: anywhere;
        white-space: normal;
    }

    .data-table-stack > tbody > tr > td:empty {
        display: none;
    }

    /* A long status wraps inside its chip instead of sticking out of the narrow value column. The block padding keeps a
       one-line chip at its height and gives the lines of a two-line chip the same room from its edges. */
    .data-table-stack .lozenge {
        max-width: 100%;
        padding-block: calc((var(--lozenge-height) - var(--font-size-sm) * var(--line-height-tight)) / 2);
        white-space: normal;
    }

    .data-table-stack > tbody > tr > td::before {
        content: attr(data-label);
        position: absolute;
        top: var(--space-1);
        left: 0;
        width: 7.5rem;
        color: var(--color-text-muted);
        font-size: var(--font-size-sm);
        font-weight: var(--font-weight-medium);
        line-height: calc(var(--line-height-base) * var(--font-size-md));
    }
}

/* The dialog fades and scales in and out; allow-discrete keeps it displayed and in the top layer until the exit has
   played. The scrim dims the page and blurs it slightly. */
dialog {
    width: min(480px, calc(100vw - 2 * var(--space-4)));
    max-height: calc(100% - 2 * var(--space-4));
    padding: 0;
    border: 0;
    border-radius: var(--radius-xl);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-4);
    color: var(--color-text);
    opacity: 0;
    scale: 0.96;
    transition-property: opacity, scale, display, overlay;
    transition-duration: var(--duration-base);
    transition-timing-function: var(--ease-exit);
    transition-behavior: allow-discrete;
}

/* The sign-in dialog is a narrow card with the page glow at its top, over the surface color (its parts are in the
   Sign-in dialog section below). */
#signin-dialog {
    width: min(440px, calc(100vw - 2 * var(--space-4)));
    background-image: var(--gradient-page);
    background-repeat: no-repeat;
}

dialog[open] {
    opacity: 1;
    scale: 1;
    transition-timing-function: var(--ease-emphasized);
}

@starting-style {
    dialog[open] {
        opacity: 0;
        scale: 0.96;
    }
}

dialog::backdrop {
    background-color: var(--color-scrim);
    -webkit-backdrop-filter: blur(2px);
    backdrop-filter: blur(2px);
    opacity: 0;
    transition-property: opacity, display, overlay;
    transition-duration: var(--duration-base);
    transition-timing-function: var(--ease-standard);
    transition-behavior: allow-discrete;
}

dialog[open]::backdrop {
    opacity: 1;
}

@starting-style {
    dialog[open]::backdrop {
        opacity: 0;
    }
}

/* The parts pad themselves, not the dialog: the sign-in script closes the dialog on a click whose target is the
   dialog itself, meant for the backdrop, so no click inside the card may land on it. */
.dialog-body {
    padding: var(--space-5);
}

.dialog-body > :first-child {
    margin-top: 0;
}

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

.dialog-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: var(--space-3);
    margin-top: var(--space-5);
}

/* Actions after the body, rather than in it: the body's padding is the gap above them. */
.dialog-body + .dialog-actions {
    margin-top: 0;
    padding: 0 var(--space-5) var(--space-5);
}

/* Breadcrumbs: the separators are chevrons from the icon font, so the markup keeps plain list items. */
.breadcrumbs ol {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    margin: 0 0 var(--space-4);
    padding: 0;
    list-style: none;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

.breadcrumbs li {
    min-width: 0;
    overflow-wrap: anywhere;
}

.breadcrumbs li + li::before {
    content: "\e06f" / "";
    margin: 0 var(--space-1);
    color: var(--color-text-subtle);
}

.breadcrumbs a {
    color: inherit;
    text-decoration: none;
}

.breadcrumbs a:hover {
    color: var(--color-primary);
    text-decoration: underline;
}

.breadcrumbs [aria-current="page"] {
    color: var(--color-text);
}

/* Document pages (About, Imprint, Privacy): a centered reading column. */
.document {
    max-width: var(--page-narrow-width);
    margin-inline: auto;
}

@media (min-width: 641px) {
    .document.card {
        padding: var(--space-6);
    }
}

/* The request filters: pill toggles that open a menu below them. */
.filter-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-3);
    margin-bottom: var(--space-4);
}

.filter {
    position: relative;
    max-width: 100%;
}

.filter-toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    max-width: 100%;
    min-height: var(--control-height);
    padding: 0 var(--space-4);
    border: 1px solid var(--color-outline-control);
    border-radius: var(--radius-pill);
    background-color: var(--color-surface);
    color: var(--color-text);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
    white-space: nowrap;
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease-standard), border-color var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard);
}

.filter-toggle:hover {
    border-color: var(--color-outline-control-hover);
    background-image: linear-gradient(var(--color-state-hover) 0 0);
}

.filter-toggle.is-active {
    border-color: var(--color-primary);
    background-color: var(--color-primary-container);
    color: var(--color-on-primary-container);
}

.filter-toggle.is-active:hover {
    background-color: var(--color-primary-container-hover);
    background-image: none;
}

.filter-toggle-text {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The leading icon and the chevron are muted until the filter is active; the chevron turns over while the menu is
   open. */
.filter-toggle > .icon {
    color: var(--color-text-muted);
}

.filter-toggle.is-active > .icon {
    color: inherit;
}

.chevron {
    transition: rotate var(--duration-base) var(--ease-standard);
}

.filter-toggle[aria-expanded="true"] .chevron {
    rotate: 180deg;
}

/* A menu like the dropdown's (see Dropdowns), holding a switch and pills rather than options. */
.filter-menu {
    position: absolute;
    top: calc(100% + var(--space-1));
    left: 0;
    z-index: var(--z-menu);
    display: block;
    width: max-content;
    min-width: 240px;
    max-width: min(400px, calc(100vw - 2 * var(--page-gutter)));
    max-height: 360px;
    overflow-y: auto;
    padding: 6px;
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-lg);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-3);
    transition-property: opacity, translate, display;
    transition-duration: var(--duration-fast);
    transition-timing-function: var(--ease-emphasized);
    transition-behavior: allow-discrete;
}

/* No clicks while it fades out. */
.filter-menu[hidden] {
    opacity: 0;
    translate: 0 calc(-1 * var(--space-1));
    pointer-events: none;
    transition-timing-function: var(--ease-exit);
}

@starting-style {
    .filter-menu {
        opacity: 0;
        translate: 0 calc(-1 * var(--space-1));
    }
}

.filter-menu > .switch-field,
.filter-menu > .pill-options {
    padding: var(--space-2);
}

.filter-separator {
    margin: var(--space-1) 0;
    border: 0;
    border-top: 1px solid var(--color-divider);
}

/* The type filter's pills carry the type's tile, smaller than in a table so the pill keeps its height; on a checked
   pill the tile turns white against the tonal pill. */
.pill-option .type-icon {
    width: 20px;
    height: 20px;
    margin-inline: calc(-1 * var(--space-1)) var(--space-2);
    border-radius: var(--radius-pill);
    font-size: var(--font-size-sm);
}

.pill-option:has(> input:checked) .type-icon {
    margin-inline-start: 0;
    background-color: var(--color-surface);
    background-image: none;
}

/* The search box: an input with the search icon inside at the left, which lets clicks through. */
.search-box {
    position: relative;
    width: 320px;
    max-width: 100%;
}

.search-box .icon {
    position: absolute;
    top: 0;
    bottom: 0;
    left: var(--space-3);
    margin-block: auto;
    color: var(--color-text-muted);
    pointer-events: none;
}

.search-box input {
    width: 100%;
    min-height: var(--control-height);
    padding: 0 var(--space-3) 0 calc(var(--space-3) + 1em + var(--space-2));
    border: 1px solid var(--color-outline-control);
    border-radius: var(--radius-md);
    background-color: var(--color-surface);
    color: var(--color-text);
    font-size: var(--font-size-md);
    line-height: var(--line-height-tight);
    transition: border-color var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard);
}

.search-box input::placeholder {
    color: var(--color-text-subtle);
    opacity: 1;
}

.search-box input:hover {
    border-color: var(--color-outline-control-hover);
}

.search-box input:focus {
    border-color: var(--color-primary);
    box-shadow: var(--focus-ring);
    outline: none;
}

@media (max-width: 640px) {
    .search-box {
        flex: 1 1 100%;
        width: auto;
    }
}

/* From 1024px the last filter's menu opens leftwards from its toggle's right end: from its left end, longer toggle
   texts before it (a German status with several ticked) could carry it out of the window. */
@media (min-width: 1024px) {
    .filter:last-of-type .filter-menu {
        right: 0;
        left: auto;
    }
}

/* Below 1024px a menu is as wide as the filter bar, below it: up to 400px wide at its toggle, a toggle further right
   would carry it out of the window. */
@media (max-width: 1023px) {
    .filter-bar {
        position: relative;
    }

    .filter {
        position: static;
    }

    .filter-menu {
        right: 0;
        width: auto;
        min-width: 0;
        max-width: none;
    }
}

/* Header buttons look like plain header text. */
.sort-button {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    width: 100%;
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font-weight: inherit;
    line-height: inherit;
    text-align: left;
    white-space: nowrap;
    cursor: pointer;
    transition: color var(--duration-fast) var(--ease-standard);
}

.sort-button:hover,
th[aria-sort] .sort-button {
    color: var(--color-text);
}

/* Reserves the arrow's space so the header text does not move when it appears. The arrows come from the icon font
   in ::after, so the header's text stays the column name. */
.sort-indicator {
    display: inline-block;
    flex: none;
    width: 1em;
    line-height: 1;
}

th:not([aria-sort]) .sort-button:hover .sort-indicator::after {
    content: "\e37d" / "";
    color: var(--color-text-subtle);
}

th[aria-sort="ascending"] .sort-indicator::after {
    content: "\e04a" / "";
}

th[aria-sort="descending"] .sort-indicator::after {
    content: "\e042" / "";
}

/* ===================================================================================================================
   Pages
   =================================================================================================================== */

/* ---- Requests list ---- */

/* The results dim while a reload is on its way (requests.js sets aria-busy). */
#request-results {
    transition: opacity var(--duration-base) var(--ease-standard);
}

#request-results[aria-busy="true"] {
    opacity: 0.6;
}

/* The page size (below the request list, the logs page's footer): a muted label before a compact 32px dropdown
   button, which is the plain .dropdown-toggle of Controls with compact rows. Without scripts the select itself is that
   button; its chevron comes from Controls. Its border, shadow and focus stay those of .text-input: set here, at this
   specificity, they would win over its focus look. */
.page-size {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    white-space: nowrap;
}

.page-size .dropdown-option {
    min-height: var(--control-height-sm);
}

.page-size select:not(.dropdown-native) {
    display: inline-block;
    width: auto;
    min-height: var(--control-height-sm);
    font-weight: var(--font-weight-medium);
}

/* The card's top: the view tabs, with "Saved", the grouping, the sort, the columns and "Reset view" at their end, over a
   hairline; the toolbar (search, filters); the chips of the filters that hold values; then the table's header row. */
.request-toolbar {
    padding-bottom: var(--space-4);
}

.view-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2) var(--space-4);
    margin-bottom: var(--space-4);
    padding: 0 var(--space-5);
    border-bottom: 1px solid var(--color-divider);
}

/* The tabs: links to the list's views, the current one in the text color over the accent line, like the app bar's tab,
   each with its number in a small pill. They scroll sideways rather than wrap. */
.view-tabs {
    display: flex;
    gap: var(--space-1);
    min-width: 0;
    overflow-x: auto;
    scrollbar-width: none;
}

.view-tab {
    position: relative;
    display: inline-flex;
    flex: none;
    align-items: center;
    gap: var(--space-2);
    min-height: 48px;
    padding: 0 var(--space-3);
    color: var(--color-text-muted);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
    text-decoration: none;
    white-space: nowrap;
    transition: color var(--duration-fast) var(--ease-standard);
}

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

.view-tab:focus-visible {
    outline-offset: -2px;
}

.view-tab[aria-current="page"] {
    color: var(--color-text);
}

.view-tab[aria-current="page"]::after {
    content: "";
    position: absolute;
    right: var(--space-3);
    bottom: 0;
    left: var(--space-3);
    height: 2px;
    border-radius: var(--radius-xs) var(--radius-xs) 0 0;
    background-image: var(--gradient-accent);
    pointer-events: none;
}

.view-tab-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    min-height: 20px;
    padding: 0 6px;
    border-radius: var(--radius-pill);
    background-color: var(--color-surface-sunken);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    font-variant-numeric: tabular-nums;
}

.view-tab[aria-current="page"] .view-tab-count {
    background-color: var(--color-primary-container);
    color: var(--color-on-primary-container);
}

.view-tools {
    display: flex;
    flex: none;
    align-items: center;
    gap: var(--space-2);
}

/* "Saved" for a moment after a change of the view: a small green pill with a check. */
.view-saved {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    min-height: 22px;
    padding: 0 var(--space-2);
    border: 1px solid var(--color-success-outline);
    border-radius: var(--radius-pill);
    background-color: var(--color-success-container);
    color: var(--color-success);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
}

/* The toolbar: the search and the filter toggles, in one row that wraps among the filters when they are many. The filter
   panel is a box of its own only on a phone (below); here its filters stand in the toolbar's row. */
.list-toolbar.filter-bar {
    margin: 0 var(--space-5);
}

.request-filter-panel {
    display: contents;
}

.request-filter-panel-head,
#filter-panel-toggle {
    display: none;
}

.filter-panel-count {
    margin-inline-start: var(--space-1);
    font-variant-numeric: tabular-nums;
}

/* How the list is shown, at the tab bar's end: the grouping and the sort as text buttons like Reset view (32px, muted, no
   edge until hovered, an icon before the text), the columns button, a hairline, then Reset view. The Overview's panels
   share .view-bar and .view-tools but have none of these, so the tighter gap is the request list's own. The buttons are
   as wide as their text (no sizers), so a choice moves only the buttons before it, as the group sits at the bar's end. */
.request-toolbar .view-tools {
    gap: var(--space-1);
}

.view-controls {
    display: flex;
    align-items: center;
    gap: var(--space-1);
}

.view-divider {
    width: 1px;
    height: 20px;
    margin-inline: var(--space-2);
    background-color: var(--color-divider);
}

.toolbar-select {
    display: block;
}

.dropdown-toggle.view-ghost {
    border-color: transparent;
    background-color: transparent;
    color: var(--color-text-muted);
}

.dropdown-toggle.view-ghost:hover {
    background-color: var(--color-state-hover);
    background-image: none;
    color: var(--color-text);
}

.dropdown-toggle.view-ghost[aria-expanded="true"] {
    background-color: var(--color-state-pressed);
    color: var(--color-text);
}

.dropdown-toggle.view-ghost .dropdown-chevron {
    color: inherit;
}

.view-controls .dropdown-sizer {
    display: none;
}

/* The menus open under their button with their end edge on the button's: the controls sit at the card's end. */
.view-controls .dropdown-menu {
    right: 0;
    left: auto;
}

/* When the bar runs short the tabs scroll, so the controls give way first: below 1400px Reset view keeps only its icon
   (its name stays in the title and for screen readers), below 1240px the grouping and the sort keep theirs too (a
   customer's five tabs with German texts, the widest, fit down to there). */
@media (max-width: 1400px) {
    .view-reset {
        padding-inline: var(--space-2);
    }

    .view-reset-text {
        position: absolute;
        width: 1px;
        height: 1px;
        margin: -1px;
        padding: 0;
        overflow: hidden;
        clip: rect(0, 0, 0, 0);
        white-space: nowrap;
        border: 0;
    }
}

@media (max-width: 1240px) {
    .view-controls .dropdown-value {
        position: absolute;
        width: 1px;
        height: 1px;
        margin: -1px;
        padding: 0;
        overflow: hidden;
        clip: rect(0, 0, 0, 0);
        white-space: nowrap;
        border: 0;
    }
}

/* Staff's organization and projects, first among the filters: one pill in two halves (the organization, then its
   projects, a hairline between), each a button that opens the panel at its own column. A half is tonal while it holds a
   choice, the pill's edge primary while either does; open, the pill has the input's focus look and the half whose column
   has the focus is pressed. */
.scope-picker {
    position: relative;
    max-width: 100%;
}

.scope-split {
    display: inline-flex;
    align-items: stretch;
    max-width: 100%;
    min-height: var(--control-height);
    border: 1px solid var(--color-outline-control);
    border-radius: var(--radius-pill);
    background-color: var(--color-surface);
    transition: border-color var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard);
}

.scope-split:hover {
    border-color: var(--color-outline-control-hover);
}

.scope-split.is-active {
    border-color: var(--color-primary);
}

.scope-split.is-open {
    border-color: var(--color-primary);
    box-shadow: var(--focus-ring);
}

.scope-split-part {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-width: 0;
    padding: 0 var(--space-3);
    border: 0;
    background: none;
    color: var(--color-text);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
    white-space: nowrap;
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease-standard);
}

.scope-split-part:first-child {
    padding-inline-start: var(--space-2);
    border-radius: var(--radius-pill) 0 0 var(--radius-pill);
}

.scope-split-part:last-child {
    border-inline-start: 1px solid var(--color-outline-variant);
    border-radius: 0 var(--radius-pill) var(--radius-pill) 0;
}

.scope-split.is-active .scope-split-part:last-child {
    border-inline-start-color: var(--color-outline);
}

.scope-split-part:hover {
    background-color: var(--color-state-hover);
}

.scope-split-part:focus-visible {
    outline: 2px solid var(--color-focus);
    outline-offset: -2px;
}

.scope-split-part > .icon {
    color: var(--color-text-muted);
}

.scope-split-part.is-set {
    background-color: var(--color-primary-container);
    color: var(--color-on-primary-container);
}

.scope-split-part.is-set:hover,
.scope-split.is-open .scope-split-part.is-current {
    background-color: var(--color-primary-container-hover);
}

.scope-split-part.is-set > .icon {
    color: inherit;
}

.scope-split.is-open .chevron {
    rotate: 180deg;
}

.scope-split-text {
    min-width: 0;
    max-width: 200px;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* "+1": the projects checked beyond the first one, as a small filled pill. */
.scope-more {
    display: inline-flex;
    align-items: center;
    min-height: 18px;
    padding: 0 6px;
    border-radius: var(--radius-pill);
    background-color: var(--color-primary);
    color: var(--color-text-on-primary);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    font-variant-numeric: tabular-nums;
}

/* An organization's monogram in the avatar tones (AvatarTones), on a rounded square so organizations never read as
   people; "All organizations" is the globe on the sunken well. */
.org-tile {
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: var(--radius-sm);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    line-height: 1;
}

.org-tile-sm {
    width: 20px;
    height: 20px;
}

.org-tile-all {
    background-color: var(--color-surface-sunken);
    box-shadow: inset 0 0 0 1px var(--color-outline-variant);
    color: var(--color-primary);
}

/* A project's key as a small tile ("WEB"), like a type tile. */
.project-key {
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    min-width: 40px;
    min-height: 20px;
    padding: 0 var(--space-1);
    border-radius: var(--radius-sm);
    background-image: var(--gradient-tile);
    color: var(--color-on-primary-container);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    line-height: 1;
}

/* A check box drawn square: the real input, its check the icon font's glyph on ::before (like the switch's thumb). */
input.check {
    appearance: none;
    position: relative;
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    margin: 0;
    border: 1px solid var(--color-outline-control);
    border-radius: var(--radius-sm);
    background-color: var(--color-surface);
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease-standard), border-color var(--duration-fast) var(--ease-standard);
}

input.check::before {
    content: "\e06c" / "";
    font-family: "lucide";
    font-size: var(--font-size-sm);
    line-height: 1;
    color: var(--color-text-on-primary);
    opacity: 0;
    scale: 0.6;
    transition: opacity var(--duration-fast) var(--ease-standard), scale var(--duration-base) var(--ease-emphasized);
}

input.check:hover {
    border-color: var(--color-outline-control-hover);
}

input.check:checked {
    border-color: var(--color-primary);
    background-color: var(--color-primary);
}

input.check:checked::before {
    opacity: 1;
    scale: 1;
}

input.check:focus-visible {
    outline: 2px solid var(--color-focus);
    outline-offset: 2px;
}

/* The panel: the menus' surface and fade, one search across both columns, the organizations on the sunken well at the
   start, the chosen organization's projects at the end, and a footer with what it holds, "All organizations" and Done. */
.scope-panel {
    position: absolute;
    top: calc(100% + var(--space-1));
    left: 0;
    z-index: var(--z-menu);
    display: flex;
    flex-direction: column;
    width: 720px;
    max-width: calc(100vw - 2 * var(--page-gutter));
    overflow: hidden;
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-xl);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-3);
    transition-property: opacity, translate, display;
    transition-duration: var(--duration-fast);
    transition-timing-function: var(--ease-emphasized);
    transition-behavior: allow-discrete;
}

/* No clicks while it fades out. */
.scope-panel[hidden] {
    opacity: 0;
    translate: 0 calc(-1 * var(--space-1));
    pointer-events: none;
    transition-timing-function: var(--ease-exit);
}

@starting-style {
    .scope-panel {
        opacity: 0;
        translate: 0 calc(-1 * var(--space-1));
    }
}

.scope-panel-head {
    padding: var(--space-3);
    border-bottom: 1px solid var(--color-divider);
}

.search-box.scope-search {
    width: 100%;
}

.scope-panes {
    display: grid;
    grid-template-columns: 290px minmax(0, 1fr);
    min-height: 0;
}

.scope-pane {
    max-height: 380px;
    overflow-y: auto;
    padding: 6px;
}

.scope-pane-organizations {
    border-inline-end: 1px solid var(--color-divider);
    background-color: var(--color-surface-sunken);
}

.scope-section-title {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    margin: 0;
    padding: var(--space-2) var(--space-2) var(--space-1);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
}

.scope-pane-title {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    min-width: 0;
}

.scope-pane-name {
    overflow: hidden;
    color: var(--color-text);
    font-weight: var(--font-weight-semibold);
    text-overflow: ellipsis;
    white-space: nowrap;
}

.scope-links {
    display: inline-flex;
    flex: none;
    gap: var(--space-1);
}

.scope-link {
    padding: 2px var(--space-1);
    border: 0;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--color-link);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    cursor: pointer;
}

.scope-link:hover {
    background-color: var(--color-state-hover);
}

/* A row: a tile or a check box, the name over a muted line, then the open requests at the end. An organization's radio
   covers its row (like a pill's check box), so a click anywhere chooses it and the arrow keys move among them; the
   chosen one takes the tonal fill and a primary bar at its start, like the list's keyboard row. */
.scope-row {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-height: 40px;
    padding: 0 var(--space-3) 0 var(--space-2);
    border-radius: var(--radius-md);
    color: var(--color-text);
    font-size: var(--font-size-md);
    line-height: var(--line-height-tight);
    white-space: nowrap;
    cursor: pointer;
}

.scope-row:hover {
    background-color: var(--color-state-hover);
}

.scope-organization > input[type="radio"] {
    position: absolute;
    inset: 0;
    z-index: 1;
    width: 100%;
    height: 100%;
    margin: 0;
    border-radius: inherit;
    opacity: 0;
    cursor: pointer;
}

.scope-organization:has(> input:checked) {
    background-color: var(--color-primary-container);
    box-shadow: inset 3px 0 0 var(--color-primary);
    color: var(--color-on-primary-container);
    font-weight: var(--font-weight-medium);
}

.scope-organization:has(> input:focus-visible) {
    outline: 2px solid var(--color-focus);
    outline-offset: -2px;
}

.scope-project {
    padding-inline-start: var(--space-3);
}

.scope-project:has(> input:checked) {
    background-color: var(--color-state-selected);
}

.scope-row-text {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
    padding-block: var(--space-2);
}

.scope-row-name {
    overflow: hidden;
    text-overflow: ellipsis;
}

.scope-row-sub {
    overflow: hidden;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-regular);
    text-overflow: ellipsis;
}

.scope-organization:has(> input:checked) .scope-row-sub {
    color: inherit;
}

.scope-row-meta {
    flex: none;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-variant-numeric: tabular-nums;
}

.scope-row-open::before {
    content: "·" / "";
    margin-inline: var(--space-1);
}

/* Without the number of projects before it (they could not be loaded) the open requests stand alone. */
.scope-row-open:first-child::before {
    content: none;
}

/* An organization's open requests against the busiest one's (requests.js sets the fill's width). */
.scope-bar {
    flex: none;
}

.scope-bar-track {
    fill: var(--color-divider);
}

.scope-bar-fill {
    fill: var(--color-secondary);
}

.scope-organization:has(> input:checked) .scope-bar-track {
    fill: var(--color-surface);
}

.scope-organization:has(> input:checked) .scope-bar-fill {
    fill: var(--color-primary);
}

.scope-hint {
    margin: var(--space-1) var(--space-2) var(--space-2);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    white-space: normal;
}

.scope-panel-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-2) var(--space-2) var(--space-4);
    border-top: 1px solid var(--color-divider);
    background-color: var(--color-surface-sunken);
}

.scope-summary {
    min-width: 0;
    overflow: hidden;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    text-overflow: ellipsis;
    white-space: nowrap;
}

.scope-summary strong {
    color: var(--color-text);
    font-weight: var(--font-weight-medium);
}

.scope-foot-end {
    display: flex;
    flex: none;
    align-items: center;
    gap: var(--space-2);
}

.scope-keys {
    margin-inline-end: var(--space-2);
}

/* The chip of staff's organization and projects: the chevron between them. */
.filter-chip-separator {
    margin-inline: 2px;
    font-size: var(--font-size-sm);
}

/* Below 1240px the halves keep the tile and the "+1" (their names stay for screen readers and in the panel). */
@media (min-width: 641px) and (max-width: 1240px) {
    .scope-split-text {
        position: absolute;
        width: 1px;
        height: 1px;
        margin: -1px;
        padding: 0;
        overflow: hidden;
        clip: rect(0, 0, 0, 0);
        white-space: nowrap;
        border: 0;
    }

    .scope-split-part {
        padding-inline: var(--space-2);
    }

    .scope-split-part:last-child {
        padding-inline-end: var(--space-3);
    }
}

/* Below 1024px the panel is as wide as the filter bar, below it, like the filter menus. */
@media (max-width: 1023px) {
    .scope-picker {
        position: static;
    }

    .scope-panel {
        right: 0;
        width: auto;
        max-width: none;
    }
}

/* The columns menu opens under its button with its end edge on the button's: a switch per column, the two that always
   show disabled with a note, and "Default columns" below a hairline. */
.columns-menu-scope {
    position: relative;
}

.columns-menu-toggle[aria-expanded="true"] {
    background-color: var(--color-primary-container);
    color: var(--color-on-primary-container);
}

.columns-menu-scope > .columns-menu {
    right: 0;
    left: auto;
    width: max-content;
    min-width: 240px;
    max-width: min(320px, calc(100vw - 2 * var(--page-gutter)));
}

.columns-menu-title {
    margin: 0;
    padding: var(--space-2) var(--space-2) var(--space-1);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
}

.column-option {
    border-radius: var(--radius-md);
}

.column-option:hover {
    background-color: var(--color-state-hover);
}

.column-option-note {
    margin-inline-start: auto;
    padding-inline-start: var(--space-3);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

.columns-menu-foot {
    display: flex;
    justify-content: flex-end;
    margin-top: var(--space-1);
    padding: var(--space-2) var(--space-1) var(--space-1);
    border-top: 1px solid var(--color-divider);
}

/* The chips: one tonal pill per filter with values ("Project: Alpha, Beta") and its remove button, then "Clear filters". */
.filter-chips {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin: var(--space-3) var(--space-5) 0;
}

.filter-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    max-width: 100%;
    min-height: 28px;
    padding: 0 var(--space-1) 0 var(--space-3);
    border: 1px solid var(--color-outline);
    border-radius: var(--radius-pill);
    background-color: var(--color-primary-container);
    color: var(--color-on-primary-container);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
}

.filter-chip-text {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.filter-chip-remove {
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    padding: 0;
    border: 0;
    border-radius: var(--radius-pill);
    background: none;
    color: inherit;
    font-size: var(--font-size-sm);
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease-standard);
}

.filter-chip-remove:hover {
    background-color: var(--color-state-pressed);
}

.filter-chip-remove:focus-visible {
    outline-offset: 1px;
}

/* On a phone: the tabs fade at their end as they scroll, the bar's end keeps the sort and Reset view as icons; the
   toolbar is the search across the card, then the filter button with the number of filters on; the grouping, the columns
   menu and the keys' hint stay for wider windows. The button opens the filters as a panel along the bottom of the window,
   the menus inside it opening in place. */
@media (max-width: 640px) {
    .view-bar {
        padding-inline: var(--space-4);
    }

    .view-tabs {
        padding-right: var(--space-5);
        -webkit-mask-image: linear-gradient(to right, currentColor calc(100% - var(--space-5)), transparent);
        mask-image: linear-gradient(to right, currentColor calc(100% - var(--space-5)), transparent);
    }

    .list-toolbar.filter-bar {
        margin-inline: var(--space-4);
    }

    #filter-panel-toggle {
        display: inline-flex;
        flex: none;
    }

    .toolbar-select:has(#request-group),
    .columns-menu-scope,
    .view-divider {
        display: none;
    }

    .filter-chips {
        margin-inline: var(--space-4);
    }

    .request-filter-panel {
        display: none;
    }

    .request-filter-panel.is-open {
        position: fixed;
        inset: auto 0 0;
        z-index: var(--z-dialog);
        display: flex;
        flex-direction: column;
        gap: var(--space-3);
        max-height: 85vh;
        overflow-y: auto;
        padding: var(--space-4);
        border-top: 1px solid var(--color-outline-variant);
        border-radius: var(--radius-xl) var(--radius-xl) 0 0;
        background-color: var(--color-surface);
        box-shadow: var(--shadow-4);
        transition: translate var(--duration-base) var(--ease-emphasized);
    }

    @starting-style {
        .request-filter-panel.is-open {
            translate: 0 var(--space-4);
        }
    }

    .request-filter-panel-head {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: var(--space-3);
    }

    .request-filter-panel-title {
        font-size: var(--font-size-lg);
        font-weight: var(--font-weight-semibold);
        line-height: var(--line-height-tight);
    }

    .request-filter-panel .filter,
    .request-filter-panel .scope-picker {
        width: 100%;
    }

    /* In the phone's panel the pill spans it, the projects half taking the room, and the panel opens in place below it
       with the organizations above the projects. */
    .request-filter-panel .scope-split {
        display: flex;
    }

    .request-filter-panel .scope-split-part:last-child {
        flex: 1 1 auto;
    }

    .request-filter-panel .scope-split .chevron {
        margin-inline-start: auto;
    }

    .request-filter-panel .scope-panel {
        position: static;
        width: auto;
        max-width: none;
        margin-top: var(--space-2);
        box-shadow: none;
    }

    .request-filter-panel .scope-panes {
        grid-template-columns: minmax(0, 1fr);
    }

    .request-filter-panel .scope-pane {
        max-height: 240px;
    }

    .request-filter-panel .scope-pane-organizations {
        border-inline-end: 0;
        border-bottom: 1px solid var(--color-divider);
    }

    .request-filter-panel .scope-keys {
        display: none;
    }

    .request-filter-panel .filter-toggle {
        width: 100%;
    }

    .request-filter-panel .filter-toggle > .chevron {
        margin-inline-start: auto;
    }

    .request-filter-panel .filter-menu {
        position: static;
        width: auto;
        min-width: 0;
        max-width: none;
        max-height: none;
        margin-top: var(--space-2);
        box-shadow: none;
    }
}

/* The banner of a failed load stands in the card in place of the table. */
#request-results > .banner {
    margin: var(--space-4);
}

/* Cells stay on one line beside the request, which wraps. The title, which a row is read by, is medium; the meta line
   under it, the assignee and the last update are secondary, so muted; the type column is as narrow as its tile. The wrap
   holds the visually hidden texts (the watchers' addresses), which would otherwise widen the page. */
.requests-card .table-wrap {
    position: relative;
}

.requests-table td {
    white-space: nowrap;
}

.requests-table td.col-type {
    width: 1%;
    padding-right: 0;
}

/* The whole row opens the request (requests.js), so hovering it underlines the link that says so. */
#request-rows > tr[data-key] {
    cursor: pointer;
}

/* The request takes the width the one-line cells leave: the title line (the title, "N days open", the status chip
   unless grouped by status), then the key, the project and the requester in small muted text. */
.requests-table td.col-summary {
    width: 100%;
    min-width: 240px;
    white-space: normal;
}

.request-title-line {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1) var(--space-2);
}

.requests-table .request-title {
    margin: 0;
    color: var(--color-text);
    font-weight: var(--font-weight-medium);
    text-decoration: none;
    overflow-wrap: anywhere;
}

#request-rows > tr:hover .request-title,
.requests-table .request-title:focus-visible {
    color: var(--color-link);
    text-decoration: underline;
}

.request-age {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    min-height: 20px;
    padding: 0 var(--space-2);
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-pill);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
    white-space: nowrap;
}

.requests-table .request-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0 var(--space-1);
    margin-top: 2px;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-tight);
}

.requests-table .request-key {
    color: inherit;
}

/* Staff only: the portal organizations the request names, after the project, in the meta line's muted text. */
.requests-table .request-organization {
    min-width: 0;
    overflow-wrap: anywhere;
}

.requests-table td:is(.col-assignee, .col-updated) {
    color: var(--color-text-muted);
}

.requests-table td.col-updated {
    font-size: var(--font-size-sm);
}

/* The watchers: up to three initials avatars overlapping, each ringed in the row's surface, then "+N". The addresses are
   the tooltip and a visually hidden text. */
.request-watchers {
    display: inline-flex;
    align-items: center;
    vertical-align: middle;
}

.request-watchers > .avatar {
    box-shadow: 0 0 0 2px var(--color-surface);
}

.request-watchers > .avatar[data-initials]::before {
    content: attr(data-initials);
}

.request-watchers > .avatar + :is(.avatar, .request-watchers-more) {
    margin-inline-start: calc(-1 * var(--space-1));
}

.request-watchers-more {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 24px;
    height: 24px;
    padding: 0 var(--space-1);
    border-radius: var(--radius-pill);
    background-color: var(--color-surface-sunken);
    box-shadow: 0 0 0 2px var(--color-surface);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
}

/* Reply and Copy link show while the pointer is on the row or the focus is in it (and always where nothing hovers);
   they are only see-through otherwise, so they stay reachable by Tab and clickable. */
.requests-table td.col-actions {
    width: 1%;
    text-align: right;
}

.requests-table .row-actions {
    position: relative;
    gap: var(--space-2);
    opacity: 0;
    transition: opacity var(--duration-fast) var(--ease-standard);
}

#request-rows > tr:is(:hover, :focus-within) .row-actions,
.requests-table .row-actions:has(.copy-status) {
    opacity: 1;
}

@media (hover: none) {
    .requests-table .row-actions {
        opacity: 1;
    }
}

/* "Link copied" (or the failure) above the button for a moment, for the eye; the list's live region reads it out. */
.copy-status {
    position: absolute;
    right: 0;
    bottom: calc(100% + var(--space-1));
    z-index: var(--z-menu);
    padding: var(--space-1) var(--space-2);
    border: 1px solid var(--color-success-outline);
    border-radius: var(--radius-md);
    background-color: var(--color-success-container);
    box-shadow: var(--shadow-2);
    color: var(--color-success);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
    white-space: nowrap;
}

.copy-status.is-error {
    border-color: var(--color-danger-outline);
    background-color: var(--color-danger-container);
    color: var(--color-danger);
}

/* The row whose title has the keyboard's focus (J, K, Tab): the selected tint and the accent along its start, beside
   the link's own ring. */
#request-rows > tr:has(a.request-title:focus-visible) {
    background-color: var(--color-state-selected);
}

#request-rows > tr:has(a.request-title:focus-visible) > td:first-child {
    box-shadow: inset 3px 0 0 var(--color-primary);
}

/* A group: its header row on the sunken tint (the fold button, the status chip, priority or project, the number of its
   rows on the page and a note), its rows, and "Show N more" for the rows past the first few. */
#request-rows > tr.request-group > td {
    padding-block: var(--space-2);
    background-color: var(--color-surface-sunken);
}

#request-rows > tr:is(.request-group, .request-group-more) {
    cursor: default;
}

.request-group-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-3);
}

.request-group-toggle .icon {
    transition: rotate var(--duration-base) var(--ease-standard);
}

.request-group-toggle[aria-expanded="false"] .icon {
    rotate: -90deg;
}

.request-group-label {
    display: inline-flex;
    align-items: center;
    font-weight: var(--font-weight-semibold);
}

.request-group-note {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

#request-rows > tr.request-group-more > td {
    padding-block: var(--space-1);
}

/* Hidden columns (the columns menu): their header and cells go. */
.requests-card.hide-col-priority .col-priority,
.requests-card.hide-col-assignee .col-assignee,
.requests-card.hide-col-watchers .col-watchers,
.requests-card.hide-col-updated .col-updated {
    display: none;
}

/* The keys' hint in the footer, each key a small key cap in the text's font. */
.keyboard-hint {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

.keyboard-hint kbd {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    min-height: 20px;
    padding: 0 var(--space-1);
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-sm);
    background-color: var(--color-surface);
    box-shadow: inset 0 -1px 0 var(--color-outline-variant);
    color: var(--color-text);
    font-family: inherit;
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    line-height: 1;
}

/* The requester: an initials avatar in the person's tone, then the name. The initials are drawn from data-initials,
   so the cell's text stays the name. */
.requests-person {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    max-width: 100%;
    vertical-align: middle;
}

.requests-person > .avatar[data-initials]::before {
    content: attr(data-initials);
}

.requests-person-name {
    min-width: 0;
}

/* A long type name in a card or pill wraps rather than widening it. */
.issue-type-name {
    min-width: 0;
    overflow-wrap: anywhere;
}

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

/* A request on a phone, as a compact card without labels: the type tile, the key and the project with the last update
   at the end on the first line, the title line below, then the priority with Reply and Copy link at the end. The
   request's cell lets its two lines into the card's grid (display: contents). The assignee and the watchers stay for the
   wide table; the sort is the toolbar's, so the header row is only there for screen readers. Group rows span the card. */
@media (max-width: 640px) {
    .requests-table.data-table-stack > thead {
        position: absolute;
        width: 1px;
        height: 1px;
        margin: -1px;
        overflow: hidden;
        clip: rect(0, 0, 0, 0);
        white-space: nowrap;
    }

    .requests-table.data-table-stack > tbody > tr[data-key] {
        display: grid;
        grid-template-columns: auto minmax(0, 1fr) auto;
        grid-template-areas:
            "type meta updated"
            "title title title"
            "priority priority actions";
        align-items: center;
        gap: var(--space-2) var(--space-3);
        padding: var(--space-3) var(--space-4);
    }

    .requests-table.data-table-stack > tbody > tr > td {
        min-height: 0;
        padding: 0;
    }

    .requests-table.data-table-stack > tbody > tr > td::before {
        content: none;
    }

    .requests-table.data-table-stack td.col-type {
        grid-area: type;
        width: auto;
    }

    .requests-table.data-table-stack td.col-summary {
        display: contents;
    }

    .requests-table.data-table-stack .request-title-line {
        grid-area: title;
    }

    /* One line: a long organization or requester ends in an ellipsis. */
    .requests-table.data-table-stack .request-meta {
        grid-area: meta;
        flex-wrap: nowrap;
        min-width: 0;
        margin-top: 0;
        overflow: hidden;
        white-space: nowrap;
    }

    .requests-table.data-table-stack .request-organization,
    .requests-table.data-table-stack .request-requester {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    .requests-table.data-table-stack td.col-priority {
        grid-area: priority;
    }

    .requests-table.data-table-stack td.col-updated {
        grid-area: updated;
        justify-self: end;
    }

    .requests-table.data-table-stack td.col-actions {
        grid-area: actions;
        justify-self: end;
        width: auto;
    }

    .requests-table.data-table-stack td:is(.col-assignee, .col-watchers) {
        display: none;
    }

    .requests-table .row-actions {
        opacity: 1;
    }

    .keyboard-hint {
        display: none;
    }

    .requests-table.data-table-stack > tbody > tr:is(.request-group, .request-group-more) {
        padding: 0;
    }

    .requests-table.data-table-stack > tbody > tr:is(.request-group, .request-group-more) > td {
        padding: var(--space-2) var(--space-4);
    }
}

/* ---- Overview ---- */

/* A customer's start page: the heading, then "Needs your attention", "Recent activity" and the request panel, stacked
   with the page's card spacing. */
.overview-activity,
.overview-panel {
    margin-top: var(--space-5);
}

/* A card's message in place of its list (nothing to show, or the list could not be loaded). */
.overview-state {
    margin: 0;
    padding: var(--space-4) var(--space-5);
    color: var(--color-text-muted);
}

/* The two list cards' titles show their icon in the primary color; the attention card's note stands under its title. */
.overview-attention .card-title::before,
.overview-activity .card-title::before {
    color: var(--color-primary);
}

.overview-attention .card-header {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-1);
}

.overview-attention .card-note {
    flex: none;
}

/* "Needs your attention": a line per request (the type tile, the title, the key, the status chip and, where it applies,
   "From your organization", then the age and Reply at the end), with hairlines between them as between the request
   rows. The items past the first few fold behind "Show more", a summary row like the table's "Show more" row. On a
   phone the title takes the first line beside the tile and the rest wraps below it. */
.attention-list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.attention-item {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-3);
    padding: var(--space-3) var(--space-5);
}

.attention-item + .attention-item,
.attention-more,
.attention-more > .attention-list {
    border-top: 1px solid var(--color-divider);
}

.attention-title {
    min-width: 0;
    color: var(--color-text);
    font-weight: var(--font-weight-semibold);
    text-decoration: none;
    overflow-wrap: anywhere;
}

.attention-title:hover,
.attention-title:focus-visible {
    color: var(--color-link);
    text-decoration: underline;
}

.attention-key {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.attention-age {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    margin-inline-start: auto;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    white-space: nowrap;
}

.attention-reply {
    flex: none;
}

/* As high as the table's "Show more" row (its 32px button and padding); the browser's disclosure marker is left out. */
.attention-more-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-height: 48px;
    padding: 0 var(--space-4);
    color: var(--color-text-muted);
    font-weight: var(--font-weight-medium);
    list-style: none;
    cursor: pointer;
    user-select: none;
    transition: background-color var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard);
}

.attention-more-toggle::-webkit-details-marker {
    display: none;
}

.attention-more-toggle:hover {
    background-color: var(--color-state-hover);
    color: var(--color-text);
}

.attention-more-toggle:focus-visible {
    outline-offset: -2px;
}

/* Closed, the row ends the card, so its hover tint keeps to the card's rounded corners. */
.attention-more:last-child:not([open]) > .attention-more-toggle {
    border-radius: 0 0 calc(var(--radius-lg) - 1px) calc(var(--radius-lg) - 1px);
}

/* "Recent activity": entries in two columns, newest first along the rows (one column below 900px), each a round tonal
   icon tile, the sentence with the request's title muted under it, and the time at the end; hairlines between the rows
   but under the last one. "Show more" is a row of its own like the table's. */
.activity-feed {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--space-7);
    margin: 0;
    padding: var(--space-1) var(--space-5);
    list-style: none;
}

.activity-feed-item {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    min-width: 0;
    padding-block: var(--space-3);
    border-bottom: 1px solid var(--color-divider);
}

.activity-feed-item:last-child,
.activity-feed-item:nth-child(odd):nth-last-child(2) {
    border-bottom: 0;
}

.activity-feed-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: var(--radius-pill);
    background-color: var(--color-primary-container);
    color: var(--color-on-primary-container);
    font-size: var(--font-size-md);
}

/* The sentence's first line sits level with the middle of the 28px tile. */
.activity-feed-body {
    flex: 1 1 auto;
    min-width: 0;
    padding-top: var(--space-1);
}

.activity-feed-text {
    margin: 0;
    overflow-wrap: anywhere;
}

.activity-feed-actor {
    color: var(--color-text);
    font-weight: var(--font-weight-semibold);
}

.activity-feed-key {
    font-weight: var(--font-weight-medium);
    text-decoration: none;
}

.activity-feed-key:hover,
.activity-feed-key:focus-visible {
    text-decoration: underline;
}

.activity-feed-value {
    font-weight: var(--font-weight-medium);
}

.activity-feed-title {
    margin: var(--space-1) 0 0;
    color: var(--color-text-muted);
    overflow-wrap: anywhere;
}

/* As high as a line of the sentence, so the small text sits level with its first line. */
.activity-feed-time {
    flex: none;
    padding-top: var(--space-1);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    line-height: calc(var(--font-size-md) * var(--line-height-base));
    white-space: nowrap;
}

.activity-feed-more {
    display: flex;
    justify-content: center;
    padding: var(--space-2) var(--space-4);
    border-top: 1px solid var(--color-divider);
}

.activity-feed-more > .overview-state {
    padding: var(--space-1) 0;
}

@media (max-width: 899px) {
    .activity-feed {
        grid-template-columns: minmax(0, 1fr);
    }

    .activity-feed-item:nth-child(odd):nth-last-child(2) {
        border-bottom: 1px solid var(--color-divider);
    }
}

/* The request panel: the request list's card and rows (.requests-card), padded by its parts. Its bar holds the tabs and
   the form (the search, the type and project choices) on one line; below 1200px the form takes a line of its own under
   the tabs. Apply sends the form without scripts; overview.js hides it. The rows come in one order (the last update,
   newest first) and open from their links: the table's sort buttons, which only the request list handles, read as plain
   header text here, and the rows keep the normal pointer. */
.overview-panel {
    padding: 0;
}

.overview-panel-bar {
    align-items: stretch;
    margin-bottom: 0;
}

.overview-panel-tools {
    flex: 0 1 auto;
    min-width: 0;
    padding-block: var(--space-2);
}

.overview-panel-tools .search-box {
    flex: 0 1 320px;
    min-width: 12rem;
}

.overview-panel-tools .dropdown-toggle {
    min-height: var(--control-height);
}

.overview-panel .sort-button {
    cursor: default;
    pointer-events: none;
}

.overview-panel .sort-indicator {
    display: none;
}

.overview-panel #request-rows > tr[data-key] {
    cursor: auto;
}

/* The rows dim while the panel loads (overview.js sets aria-busy); the banner of a failed load stands in the card. */
#overview-results {
    transition: opacity var(--duration-base) var(--ease-standard);
}

.overview-panel[aria-busy="true"] #overview-results {
    opacity: 0.6;
}

#overview-results > .banner {
    margin: var(--space-4);
}

/* The footer: the rows shown at the start, "Open the full list" at the end with its arrow after the text. */
.overview-full-list {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    margin-inline-start: auto;
    font-weight: var(--font-weight-medium);
    text-decoration: none;
}

.overview-full-list::before {
    order: 1;
}

.overview-full-list:hover,
.overview-full-list:focus-visible {
    text-decoration: underline;
}

@media (max-width: 1199px) {
    .overview-panel-bar {
        flex-direction: column;
        gap: 0;
    }

    .overview-panel-tools {
        flex-wrap: wrap;
        margin-inline: calc(-1 * var(--space-5));
        padding: var(--space-3) var(--space-5);
        border-top: 1px solid var(--color-divider);
    }

    .overview-panel-tools .search-box {
        flex: 1 1 240px;
    }
}

/* On a phone the cards' parts pad 16px, as the card headers do; the search takes a line of its own. */
@media (max-width: 640px) {
    .overview-state,
    .attention-item {
        padding-inline: var(--space-4);
    }

    .attention-title {
        flex: 1 1 calc(100% - 28px - var(--space-3));
    }

    .activity-feed {
        padding-inline: var(--space-4);
    }

    .overview-panel-tools {
        margin-inline: calc(-1 * var(--space-4));
        padding-inline: var(--space-4);
    }

    .overview-panel-tools .search-box {
        flex-basis: 100%;
    }
}

/* ---- Staff overview ---- */

/* Staff's start page (Views/Overview/Staff.cshtml): the heading with the portal's counts and the two actions, the
   Projects and customer activity cards side by side (stacked below 1200px, where the project rows would get too
   narrow), then the Organizations card. The cards load their rows in place (staff-overview.js), which dim while a load
   is under way (aria-busy on the card); Apply sends the forms without scripts only. */
.staff-overview-subtitle {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1) var(--space-4);
}

.staff-overview-fact {
    white-space: nowrap;
}

.staff-overview-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-3);
}

/* "Impersonate": the member picker (the combobox's wrapper, or the plain input without scripts) and its button. */
.staff-overview-view-as {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-width: 0;
}

.staff-overview-view-as > :is(.combobox, .text-input) {
    flex: 0 1 16rem;
    min-width: 10rem;
}

.staff-overview-grid {
    display: grid;
    grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
    gap: var(--space-5);
}

.staff-overview-grid > .card + .card {
    margin-top: 0;
}

.staff-overview-organizations {
    margin-top: var(--space-5);
}

/* The two cards of the grid are as high as the taller one: their bodies take the free height, so "Show more" stays at
   the bottom of each. The note stands under the title, whose icon is in the primary color as on a customer's Overview. */
.staff-overview-projects,
.staff-overview-activity {
    display: flex;
    flex-direction: column;
}

.staff-overview-projects > .card-header,
.staff-overview-activity > .card-header {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-1);
}

.staff-overview-projects .card-note,
.staff-overview-activity .card-note {
    flex: none;
}

.staff-overview-projects .card-title::before,
.staff-overview-activity .card-title::before {
    color: var(--color-primary);
}

#staff-overview-projects-body,
#staff-overview-activity-body {
    display: flex;
    flex: 1 0 auto;
    flex-direction: column;
}

#staff-overview-projects-body,
#staff-overview-organizations-body {
    transition: opacity var(--duration-base) var(--ease-standard);
}

.staff-overview-projects[aria-busy="true"] #staff-overview-projects-body,
.staff-overview-organizations[aria-busy="true"] #staff-overview-organizations-body {
    opacity: 0.6;
}

/* A "Show more" row of its own, centered below a hairline, like the table's; inert while its page loads. */
.staff-overview-more {
    display: flex;
    justify-content: center;
    padding: var(--space-2) var(--space-4);
    border-top: 1px solid var(--color-divider);
}

.staff-overview-more > .btn[aria-disabled="true"] {
    pointer-events: none;
}

/* The two tiles: sunken boxes with a small title and the number, then a note (and the assignees' initials, overlapping
   and ringed in the tile's color). */
.staff-overview-tiles {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-3);
    padding: var(--space-4) var(--space-5) 0;
}

.staff-overview-tile {
    min-width: 0;
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-md);
    background-color: var(--color-surface-sunken);
}

.staff-overview-tile-title {
    margin: 0;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
}

.staff-overview-tile-value {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-1) var(--space-2);
    margin: var(--space-1) 0 0;
}

.staff-overview-tile-number {
    font-size: var(--font-size-xl);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight);
    font-variant-numeric: tabular-nums;
}

.staff-overview-tile-prefix,
.staff-overview-tile-note {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

.staff-overview-avatars {
    display: inline-flex;
    align-self: center;
}

.staff-overview-avatars > .avatar {
    box-shadow: 0 0 0 2px var(--color-surface-sunken);
}

.staff-overview-avatars > .avatar + .avatar {
    margin-inline-start: -6px;
}

/* The forms: the projects' search, sort and organization under the tiles; the organizations' search beside the tabs. */
.staff-overview-tools {
    flex-wrap: wrap;
    min-width: 0;
}

.staff-overview-tools .dropdown-toggle {
    min-height: var(--control-height);
}

.staff-overview-projects .staff-overview-tools {
    padding: var(--space-4) var(--space-5) var(--space-2);
}

.staff-overview-projects .staff-overview-tools .search-box {
    flex: 1 1 14rem;
    width: auto;
}

/* A row per project between hairlines: the name with its organizations (cut off with an ellipsis), the bar and the
   counts. The bar's parts are SVG rectangles whose position and width the server writes as attributes; their colors
   are the statuses' (new, in progress, done), and a stroke in the card's color leaves a gap between them. */
.project-rows {
    margin: 0;
    padding: 0 var(--space-5);
    list-style: none;
}

.project-row {
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(4rem, 4fr) auto;
    align-items: center;
    gap: var(--space-2) var(--space-4);
    min-height: 44px;
    padding-block: var(--space-2);
    border-bottom: 1px solid var(--color-divider);
}

.project-row-name {
    min-width: 0;
    margin: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.project-row-title {
    color: var(--color-text);
    font-weight: var(--font-weight-medium);
}

.project-row-organizations {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

.project-row-bar {
    display: block;
    width: 100%;
    height: 8px;
}

.project-row-bar rect {
    rx: var(--radius-sm);
    stroke: var(--color-surface);
    stroke-width: 2px;
}

.project-row-bar-new {
    background-color: var(--color-status-new-dot);
    fill: var(--color-status-new-dot);
}

.project-row-bar-progress {
    background-color: var(--color-status-indeterminate-dot);
    fill: var(--color-status-indeterminate-dot);
}

.project-row-bar-resolved {
    background-color: var(--color-status-done-dot);
    fill: var(--color-status-done-dot);
}

.project-row-counts {
    margin: 0;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-variant-numeric: tabular-nums;
    text-align: right;
    white-space: nowrap;
}

/* The footer: the legend and the rows shown on its first line, "Show more" across the card at the bottom (the lines
   spread over the free height of the body). */
.staff-overview-projects-footer {
    display: flex;
    flex: 1 0 auto;
    flex-wrap: wrap;
    align-content: space-between;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3) var(--space-4);
    padding: var(--space-3) var(--space-5);
}

.staff-overview-projects-footer > .staff-overview-more {
    flex: 1 0 calc(100% + 2 * var(--space-5));
    margin: 0 calc(-1 * var(--space-5)) calc(-1 * var(--space-3));
}

.staff-overview-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1) var(--space-4);
    margin: 0;
    padding: 0;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    list-style: none;
}

.staff-overview-legend > li {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.staff-overview-swatch {
    flex: none;
    width: 10px;
    height: 10px;
    border-radius: var(--radius-xs);
}

/* The customer activity: the customer feed in one column; each entry's avatar shows the member's initials with the
   kind's icon on a small round badge at its corner, and the organizations and title below the sentence are small. */
.staff-activity-feed {
    grid-template-columns: minmax(0, 1fr);
}

.staff-activity-feed > .activity-feed-item:nth-child(odd):nth-last-child(2) {
    border-bottom: 1px solid var(--color-divider);
}

.staff-activity-avatar {
    position: relative;
    width: 28px;
    height: 28px;
    overflow: visible;
}

.staff-activity-kind {
    position: absolute;
    right: -4px;
    bottom: -4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    border-radius: var(--radius-pill);
    background-color: var(--color-surface);
    box-shadow: 0 0 0 1px var(--color-outline-variant);
    color: var(--color-primary);
    font-size: var(--font-size-sm);
}

.staff-activity-meta {
    font-size: var(--font-size-sm);
}

.staff-activity-more {
    margin-top: auto;
}

/* The Organizations card: the tabs and the search in its bar, the table, and the footer with the rows shown, "Show
   more" and the links to the organizations page. */
.staff-overview-organizations-bar {
    flex-wrap: wrap;
    margin-bottom: 0;
}

.staff-overview-organizations-bar .staff-overview-tools {
    padding-block: var(--space-2);
}

.org-row-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    margin-inline-end: var(--space-3);
    border-radius: var(--radius-md);
    background-color: var(--color-primary-container);
    color: var(--color-on-primary-container);
    font-size: var(--font-size-md);
    vertical-align: middle;
}

.org-row-text {
    display: inline-block;
    min-width: 0;
    vertical-align: middle;
}

.org-row-link {
    color: var(--color-text);
    font-weight: var(--font-weight-semibold);
    text-decoration: none;
}

.org-row-link:hover,
.org-row-link:focus-visible {
    color: var(--color-link);
    text-decoration: underline;
}

.org-row-meta {
    margin: 0;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

.org-row-members,
.org-row-sign-in,
.org-row-reply {
    color: var(--color-text-muted);
    white-space: nowrap;
}

.org-row-open,
.org-row-waiting {
    font-variant-numeric: tabular-nums;
}

/* What the organization's settings allow: outlined pills, tonal in the primary colors while on. */
.org-row-pills {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
    margin: 0;
    padding: 0;
    list-style: none;
}

.org-row-pill {
    display: inline-flex;
    align-items: center;
    min-height: 22px;
    padding: 0 var(--space-2);
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-pill);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
    white-space: nowrap;
}

.org-row-pill.is-on {
    border-color: transparent;
    background-color: var(--color-primary-container);
    color: var(--color-on-primary-container);
}

/* "View as": the eye opens the browser's disclosure with a button per member with portal access. The list is anchored
   below the eye, its end edge on the eye's, and fixed, so the table's scrolling wrap and the card do not clip it;
   without anchor positioning it hangs below the eye inside the table. Dimmed and inert without such a member. */
.org-row-view-as {
    width: 1%;
    text-align: right;
}

.org-row-view-as-menu {
    position: relative;
    display: inline-block;
    anchor-scope: --org-row-view-as;
}

.org-row-view-as-toggle {
    list-style: none;
    anchor-name: --org-row-view-as;
}

.org-row-view-as-toggle::-webkit-details-marker {
    display: none;
}

.org-row-view-as-menu[open] > .org-row-view-as-toggle {
    background-color: var(--color-state-selected);
    color: var(--color-text);
}

.org-row-view-as-toggle.is-disabled {
    color: var(--color-text-subtle);
    cursor: default;
    opacity: 0.5;
}

.org-row-view-as-toggle.is-disabled:hover {
    background-color: transparent;
    color: var(--color-text-subtle);
}

.org-row-view-as-list {
    position: absolute;
    top: calc(100% + var(--space-1));
    right: 0;
    z-index: var(--z-menu);
    min-width: 14rem;
    max-width: min(24rem, calc(100vw - 2 * var(--page-gutter)));
    margin: 0;
    padding: 6px;
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-lg);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-3);
    color: var(--color-text);
    text-align: left;
    white-space: normal;
    list-style: none;
    animation: fade-in-down var(--duration-fast) var(--ease-emphasized);
}

@supports (position-area: bottom) {
    .org-row-view-as-list {
        position: fixed;
        position-anchor: --org-row-view-as;
        position-area: bottom span-left;
        position-try-fallbacks: flip-block;
        inset: auto;
        margin: var(--space-1) 0;
    }
}

.org-row-view-as-list form {
    display: block;
}

.org-row-view-as-member {
    display: flex;
    align-items: center;
    width: 100%;
    min-height: 36px;
    padding: 0 10px;
    border: 0;
    border-radius: var(--radius-md);
    background: none;
    color: var(--color-text);
    font: inherit;
    line-height: var(--line-height-tight);
    text-align: left;
    overflow-wrap: anywhere;
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease-standard);
}

.org-row-view-as-member:hover,
.org-row-view-as-member:focus-visible {
    background-color: var(--color-state-hover);
}

/* In the table's footer "Show more" is one of its items, not a row of its own. "All organizations" is a medium link
   with its arrow after the text. */
.table-footer > .staff-overview-more {
    padding: 0;
    border-top: 0;
}

#staff-overview-all-organizations {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    font-weight: var(--font-weight-medium);
    text-decoration: none;
}

#staff-overview-all-organizations::before {
    order: 1;
    margin-inline-end: 0;
}

#staff-overview-all-organizations:hover,
#staff-overview-all-organizations:focus-visible {
    text-decoration: underline;
}

@media (max-width: 1199px) {
    .staff-overview-grid {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* On a phone: the picker takes the width; one tile per line; a project's counts stand beside its name with the bar
   below them; the organizations' search takes a line of its own and the stacked rows leave the icon out. */
@media (max-width: 640px) {
    .staff-overview-actions,
    .staff-overview-view-as {
        flex: 1 1 100%;
    }

    .staff-overview-view-as > :is(.combobox, .text-input) {
        flex: 1 1 auto;
    }

    .staff-overview-tiles {
        grid-template-columns: minmax(0, 1fr);
        padding-inline: var(--space-4);
    }

    .staff-overview-projects .staff-overview-tools {
        padding-inline: var(--space-4);
    }

    .staff-overview-projects .staff-overview-tools .search-box {
        flex-basis: 100%;
    }

    .project-rows {
        padding-inline: var(--space-4);
    }

    .project-row {
        grid-template-areas:
            "name counts"
            "bar bar";
        grid-template-columns: minmax(0, 1fr) auto;
    }

    .project-row-name {
        grid-area: name;
    }

    .project-row-bar {
        grid-area: bar;
    }

    .project-row-counts {
        grid-area: counts;
    }

    .staff-overview-projects-footer {
        padding-inline: var(--space-4);
    }

    .staff-overview-projects-footer > .staff-overview-more {
        flex-basis: calc(100% + 2 * var(--space-4));
        margin-inline: calc(-1 * var(--space-4));
    }

    .staff-overview-organizations-bar .staff-overview-tools {
        flex: 1 1 100%;
    }

    .staff-overview-organizations-bar .staff-overview-tools .search-box {
        flex: 1 1 auto;
        width: auto;
    }

    .org-row-icon {
        display: none;
    }

    .org-row-view-as {
        width: auto;
        text-align: left;
    }
}

/* ===================================================================================================================
   Request detail
   =================================================================================================================== */

/* The title and a line of facts (type, reference, type and project, status) above the grid, the actions (the status
   form and Copy link) at its end; who raised it and when is in the raised box. The actions wrap below the title when the
   row is too narrow for both, and take the whole width on a phone. */
.request-header {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4) var(--space-5);
    margin-bottom: var(--space-4);
}

.request-title {
    margin: 0 0 var(--space-3);
    overflow-wrap: anywhere;
}

.request-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-4);
    color: var(--color-text-muted);
    font-size: var(--font-size-md);
    line-height: var(--line-height-tight);
}

.request-meta-item {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-width: 0;
    overflow-wrap: anywhere;
}

.request-key {
    color: var(--color-text);
    font-weight: var(--font-weight-medium);
}

.request-meta .icon {
    color: var(--color-text-subtle);
}

/* The rules below are scoped to the header: the request list's rows use .request-title and .request-meta too. The facts
   line comes first, the title below it. */
.request-header-main {
    flex: 1 1 24rem;
    min-width: 0;
}

.request-header .request-meta {
    gap: var(--space-2) var(--space-3);
}

.request-header .request-meta .type-icon {
    color: var(--color-on-primary-container);
}

.request-header .request-title {
    margin: var(--space-2) 0 0;
}

.request-header-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: flex-end;
    gap: var(--space-2);
    max-width: 100%;
}

/* The status form: the dropdown (whose first option is the current status) and, without scripts, its button beside it;
   the label stays for screen readers only, as the dropdown shows the status. Refusals show below the form. */
.request-status-block {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    min-width: 0;
}

.request-header #request-status .banner {
    order: 1;
    margin: 0;
}

.request-header #status-form {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin-top: 0;
}

.request-header #status-form .form-field {
    min-width: 0;
    margin-bottom: 0;
}

.request-header #status-form .field-label {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* The status as a tonal button: the current status is the dropdown's chosen value, never a muted placeholder. */
.request-header #status-form .dropdown-toggle:not([aria-invalid="true"]) {
    border-color: var(--color-primary);
}

.request-header #status-form .dropdown-toggle {
    background-color: var(--color-primary-container);
    color: var(--color-on-primary-container);
    font-weight: var(--font-weight-medium);
}

.request-header #status-form .dropdown-toggle:hover {
    background-color: var(--color-primary-container-hover);
}

.request-header #status-form .dropdown-value.is-placeholder,
.request-header #status-form .dropdown-chevron {
    color: inherit;
}

@media (max-width: 640px) {
    .request-header-actions {
        flex: 1 1 100%;
        flex-wrap: nowrap;
    }

    .request-status-block,
    .request-header #status-form .form-field {
        flex: 1 1 auto;
    }

    #request-copy-link {
        flex: none;
    }
}

/* Staff only, above the header: what a member did last on the request in the portal, then View as the requester and the
   request as each of its organizations sees it, at the end; the actions wrap below the sentence when the row is too
   narrow for both. */
.staff-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2) var(--space-4);
    margin-bottom: var(--space-4);
    padding: var(--space-3) var(--space-4);
}

.staff-bar-last {
    flex: 1 1 24rem;
    min-width: 0;
    margin: 0;
    color: var(--color-text);
    overflow-wrap: anywhere;
}

.staff-bar-label,
.staff-bar-time {
    color: var(--color-text-muted);
}

.staff-bar-actor {
    font-weight: var(--font-weight-semibold);
}

.staff-bar-key,
.staff-bar-value {
    font-weight: var(--font-weight-medium);
}

.staff-bar-time {
    font-size: var(--font-size-sm);
    white-space: nowrap;
}

.staff-bar-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    max-width: 100%;
}

.staff-bar-view-as {
    max-width: 100%;
    margin: 0;
}

.staff-bar .btn {
    max-width: 100%;
}

.staff-bar-view-as-address,
.staff-bar-organization {
    min-width: 0;
    overflow-wrap: anywhere;
}

/* Staff's read-only look as a member sees the request: the sentence with the organization, and Back to the staff view at
   the end, on one row while they fit, the icon centered on it. */
.customer-view-banner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2) var(--space-4);
}

.customer-view-banner::before {
    top: 50%;
    translate: 0 -50%;
}

.customer-view-banner > p {
    flex: 1 1 20rem;
    min-width: 0;
}

.customer-view-organization {
    font-weight: var(--font-weight-semibold);
}

/* On a phone the actions take the whole width below the text, which the icon then stays level with. */
@media (max-width: 640px) {
    .staff-bar-actions,
    .staff-bar-actions > .btn,
    .staff-bar-view-as {
        flex: 1 1 100%;
    }

    .staff-bar-view-as > .btn {
        width: 100%;
    }

    .customer-view-banner::before {
        top: var(--space-3);
        translate: none;
    }
}

/* Progress: four equal steps (Open, In progress, Waiting for feedback, Done), each a bar over its name and date. The bars
   up to the current step take the primary color and the current step's name is set in the text color; a step after it
   shows no date. On a phone the date goes below the name. */
.request-progress {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--space-1);
    margin: 0 0 var(--space-5);
    padding: 0;
    list-style: none;
}

.progress-step {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0 var(--space-1);
    min-width: 0;
    padding-top: calc(4px + var(--space-2));
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-tight);
}

.progress-step::before {
    content: "";
    position: absolute;
    top: 0;
    right: 0;
    left: 0;
    height: 4px;
    border-radius: var(--radius-pill);
    background-color: var(--color-outline-variant);
}

.progress-step.is-reached::before {
    background-color: var(--color-primary);
}

.progress-step-name {
    min-width: 0;
    overflow-wrap: anywhere;
}

.progress-step.is-current .progress-step-name {
    color: var(--color-text);
    font-weight: var(--font-weight-semibold);
}

.progress-step-date {
    white-space: nowrap;
}

.progress-step-date::before {
    content: "\00b7";
    margin-inline-end: var(--space-1);
}

@media (max-width: 640px) {
    .progress-step {
        flex-direction: column;
        align-items: flex-start;
    }

    .progress-step-date::before {
        content: none;
    }
}

/* The raised box, the activity and the sidebar are items of one grid, in markup order (the main column's box gives
   way): one column below 900px, the sidebar last, from 900px with the sidebar in a column of its own from the raised
   box's top down. Its row takes any height the sidebar needs beyond the raised box and the activity, so the gap below
   the raised box stays. */
.request-detail {
    display: grid;
    grid-template-areas: "raised" "activity" "side";
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-5);
    align-items: start;
}

.request-main {
    display: contents;
}

.request-main > .raised-box {
    grid-area: raised;
}

.request-main > .request-sidebar,
.request-detail > .request-sidebar {
    grid-area: side;
}

.request-main > .activity {
    grid-area: activity;
}

/* The linked requests card sits between the raised box and the rest; its row exists only when the page has one
   (has-linked), so a page without it keeps the gap it had. */
.request-main > .linked-requests {
    grid-area: linked;

    /* The grid's gap spaces it from the raised box, which .card + .card would add to. */
    margin-top: 0;
}

.request-detail.has-linked {
    grid-template-areas: "raised" "linked" "activity" "side";
}

/* On wide windows the sidebar stays in view below the app bar while the activity scrolls, as long as it fits the
   window: it never scrolls on its own. request-detail.js measures it and sets is-sticky while it fits below its top
   offset (which it reads from top here); otherwise, and without scripts, it stays in the page's flow, where the page
   scrolls to its end. */
@media (min-width: 900px) {
    .request-detail {
        grid-template-areas: "raised side" "activity side";
        grid-template-columns: minmax(0, 1fr) var(--sidebar-width);
        grid-template-rows: auto 1fr;
    }

    .request-detail.has-linked {
        grid-template-areas: "raised side" "linked side" "activity side";
        grid-template-rows: auto auto 1fr;
    }

    .request-sidebar {
        top: calc(var(--app-bar-height) + var(--space-5));
    }

    .request-sidebar.is-sticky {
        position: sticky;
    }
}

/* Who raised the request (a flush card), folded open into the description. */
/* "raised this on" and the date are meta text; the name next to them is a name. */
.raised-box > summary {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-3);
    padding: var(--space-4) var(--space-5);
    list-style: none;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-tight);
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease-standard);
}

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

.raised-box > summary:hover {
    background-color: var(--color-state-hover);
}

/* The ring would be cut off by the card's clipping, so it is drawn inside. */
.raised-box > summary:focus-visible {
    outline-offset: -2px;
    border-radius: var(--radius-lg);
}

.raised-box[open] > summary:focus-visible {
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}

.raised-by {
    color: var(--color-text);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-semibold);
}

/* Pushed to the end of the row; a text button whose chevron turns over while the box is open. */
.details-toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    min-height: var(--control-height-sm);
    margin-left: auto;
    padding: 0 var(--space-2) 0 var(--space-3);
    border-radius: var(--radius-md);
    color: var(--color-primary);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-medium);
    white-space: nowrap;
    transition: background-color var(--duration-fast) var(--ease-standard);
}

.raised-box > summary:hover .details-toggle {
    background-color: var(--color-state-hover);
}

.details-toggle::after {
    content: "\e06d" / "";
    transition: rotate var(--duration-base) var(--ease-standard);
}

.raised-box[open] .details-toggle::after {
    rotate: 180deg;
}

.raised-box .hide-details,
.raised-box[open] .show-details {
    display: none;
}

.raised-box[open] .hide-details {
    display: inline;
}

.raised-details {
    padding: var(--space-5);
    border-top: 1px solid var(--color-divider);
}

.raised-details > .field-label:first-child {
    margin-top: 0;
}

/* The box is open in the page's HTML, which gets no entrance animation. Opening it again drops the details in from
   just above their place (fade-in-down), only once request-detail.js has marked the box with is-toggled on the
   user's click; a browser with ::details-content also grows the box's height from 0 and runs the fade and height
   backwards on closing, which keyframes alone cannot do (content-visibility stays visible until the transition
   ends). Only where motion is welcome: the no-preference wrapper also keeps the closed state's zero height and
   opacity from applying under reduced motion. */
.raised-box.is-toggled[open] .raised-details {
    animation: fade-in-down var(--duration-slow) var(--ease-standard) both;
}

.raised-box:not(.is-toggled) .raised-details,
.raised-box:not([open]) .raised-details {
    animation: none;
}

@media (prefers-reduced-motion: no-preference) {
    @supports selector(::details-content) {
        .raised-box {
            interpolate-size: allow-keywords;
        }

        .raised-box::details-content {
            block-size: 0;
            overflow: clip;
            opacity: 0;
            transition:
                block-size var(--duration-slow) var(--ease-standard),
                opacity var(--duration-slow) var(--ease-standard),
                content-visibility var(--duration-slow) allow-discrete;
        }

        .raised-box[open]::details-content {
            block-size: auto;
            opacity: 1;
        }
    }
}

@media (max-width: 640px) {
    .raised-box > summary {
        padding-inline: var(--space-4);
    }

    .raised-details {
        padding: var(--space-4);
    }
}

/* Labels above values: small and muted, also on h2 and dt elements, where they override the element's own look. */
.field-label {
    display: block;
    margin: 0 0 var(--space-1);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    letter-spacing: normal;
    line-height: var(--line-height-tight);
}

.field-value {
    margin: 0;
    font-size: var(--font-size-md);
}

/* Also the empty lists of the admin and logs pages. */
.field-empty {
    color: var(--color-text-muted);
}

/* The sidebar: one card per section, stacked. Its cards pad a step less than the page's, as the column is narrow. */
.request-sidebar {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    min-width: 0;
}

.request-sidebar > .card + .card {
    margin-top: 0;
}

.sidebar-card {
    padding: var(--space-4) var(--space-5) var(--space-5);
}

/* An h2, which gives it the card title's look. */
.sidebar-card-title {
    margin: 0 0 var(--space-3);
}

@media (max-width: 640px) {
    .sidebar-card {
        padding: var(--space-4);
    }
}

/* The number of items in a pill beside a card's title (the activity, the watchers, the attachments). */
.card-count {
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    min-height: 20px;
    padding: 0 6px;
    border-radius: var(--radius-pill);
    background-color: var(--color-primary-container);
    color: var(--color-on-primary-container);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
    font-variant-numeric: tabular-nums;
}

/* A sidebar card with a count: the title, the count and the watchers' Edit button (pushed to the end) share the first
   line; everything after them takes a line of its own, the first one a step below. */
.sidebar-card:has(> .card-count) {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    column-gap: var(--space-2);
}

.sidebar-card:has(> .card-count) > * {
    flex: 1 0 100%;
    min-width: 0;
}

.sidebar-card:has(> .card-count) > :is(.sidebar-card-title, .card-count, #watchers-edit) {
    flex: none;
}

.sidebar-card:has(> .card-count) > .sidebar-card-title {
    margin: 0;
}

.sidebar-card:has(> .card-count) > :is(.card-count, #watchers-edit) + :not(#watchers-edit) {
    margin-top: var(--space-3);
}

#watchers-edit {
    margin-left: auto;
}

/* Status: the chip, then the form with the list above a full-width button. The priority form under the details and the watchers form are the same. */
.request-status {
    margin: 0;
}

#request-status .banner,
#request-priority .banner,
#request-watchers .banner {
    margin: var(--space-3) 0 0;
}

#status-form,
#priority-form,
#watchers-form {
    margin-top: var(--space-4);
}

#status-form .form-field,
#priority-form .form-field,
#watchers-form .form-field {
    gap: var(--space-2);
    margin-bottom: var(--space-3);
}

/* Details: definition rows, label above value. */
.field-list {
    display: grid;
    gap: var(--space-4);
    margin: 0;
}

.field-row {
    min-width: 0;
}

.field-list dd {
    margin: 0;
}

.request-issue-type,
.request-priority {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    overflow-wrap: anywhere;
}

.request-priority .priority-icon {
    margin-right: 0;
}

/* The Details card's rows: the label at the start (small, muted), the value beside it. A row's wrapper, if any, gives way
   to the grid, so the labels share one column. The priority's form sits in its row: the dropdown alone with scripts (it
   saves on choice), with its button below it without; its label stays for screen readers only, as the row names it. */
.detail-rows {
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr);
    align-items: center;
    gap: var(--space-3) var(--space-4);
    margin: 0;
}

.detail-rows > div {
    display: contents;
}

.detail-rows dt {
    margin: 0;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
}

.detail-rows dd {
    min-width: 0;
    margin: 0;
    font-size: var(--font-size-md);
    overflow-wrap: anywhere;
}

/* The Type row: issue type, "·" and request type on one line. */
.detail-rows .request-issue-type {
    display: inline-flex;
    vertical-align: middle;
}

.detail-rows #request-priority .banner {
    margin: 0 0 var(--space-2);
}

.detail-rows #priority-form {
    display: grid;
    gap: var(--space-2);
    margin-top: 0;
}

.detail-rows #priority-form .form-field {
    margin-bottom: 0;
}

.detail-rows #priority-form .field-label {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* Which control saves how, below the rows; nothing when the card has neither. */
.detail-note {
    margin-top: var(--space-4);
    font-size: var(--font-size-sm);
}

.detail-note:not(:has(> span)) {
    display: none;
}

.person {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-width: 0;
    overflow-wrap: anywhere;
}

.request-assignee {
    gap: var(--space-2);
}

.person-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
    line-height: var(--line-height-tight);
}

.person-name {
    color: var(--color-text);
    font-weight: var(--font-weight-semibold);
}

.person-role {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

.shared-with {
    display: grid;
    gap: var(--space-3);
    margin: 0;
    padding: 0;
    list-style: none;
}

/* Attachments: rows that are links as a whole (a file-type tile, the name over its details, the download icon). The
   list reaches 8px beyond the card's content, so the rows' hover tint surrounds the content, which stays in line with
   the title. A long name wraps to two lines and then ends in an ellipsis. */
.attachment-list {
    margin: 0 calc(-1 * var(--space-2));
    padding: 0;
    list-style: none;
}

.attachment-list li + li {
    margin-top: 2px;
}

.attachment-item {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-height: 48px;
    padding: var(--space-2);
    border-radius: var(--radius-md);
    color: var(--color-text);
    text-decoration: none;
    transition: background-color var(--duration-fast) var(--ease-standard);
}

.attachment-item:hover {
    background-color: var(--color-state-hover);
}

.attachment-item:focus-visible {
    outline-offset: -2px;
}

.attachment-icon {
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: var(--radius-md);
    background-image: var(--gradient-tile);
    color: var(--color-on-primary-container);
    font-size: var(--font-size-lg);
}

.attachment-text {
    display: flex;
    flex: 1;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.attachment-name {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
    overflow-wrap: anywhere;
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
}

/* "size · uploader" and the date wrap as units, with a dot after the first unit, so a dot never starts a line; only the
   uploader's name, which may be long, breaks inside. */
.attachment-meta {
    display: flex;
    flex-wrap: wrap;
    column-gap: var(--space-1);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-tight);
}

.attachment-meta > * {
    min-width: 0;
    white-space: nowrap;
}

.attachment-meta > .attachment-who {
    white-space: normal;
}

.attachment-size {
    white-space: nowrap;
}

.attachment-uploader {
    overflow-wrap: anywhere;
}

/* The blank the markup leaves before each dot is the gap on its left. */
.attachment-uploader::before {
    content: "\00b7";
    margin-inline-end: var(--space-1);
}

.attachment-who:not(:last-child)::after {
    content: "\00b7";
}

.attachment-download {
    color: var(--color-text-subtle);
    transition: color var(--duration-fast) var(--ease-standard);
}

.attachment-item:hover .attachment-download,
.attachment-item:focus-visible .attachment-download {
    color: var(--color-primary);
}

/* "Download all · size" above the files: a medium link with its icon. Not a flex box, which would drop the blanks
   around the dot between its two spans. */
.request-download-all {
    display: inline-block;
    margin-bottom: var(--space-3);
    color: var(--color-link);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
    text-decoration: none;
}

.request-download-all:hover,
.request-download-all:focus-visible {
    text-decoration: underline;
}

/* The images among the files as thumbnails above the list, three to a row in the sidebar; each one a link that
   downloads it. */
.attachment-thumbs {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(72px, 1fr));
    gap: var(--space-2);
    margin: 0 0 var(--space-3);
    padding: 0;
    list-style: none;
}

.attachment-thumb {
    display: block;
    aspect-ratio: 4 / 3;
    overflow: hidden;
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-md);
    background-color: var(--color-surface-sunken);
    transition: border-color var(--duration-fast) var(--ease-standard);
}

.attachment-thumb:hover {
    border-color: var(--color-primary);
}

.attachment-thumb img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Linked requests: a group per relation ("is blocked by", "blocks"), each request a bordered row that is a link as a
   whole: the type tile, the key, the title (which gives way and ends in an ellipsis), the status, the assignee's avatar
   and the priority. The last two keep a slot of their own, so the columns line up when a request lacks one. */
.linked-group + .linked-group {
    margin-top: var(--space-4);
}

.linked-relation {
    margin-bottom: var(--space-2);
}

.linked-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

.linked-item {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-height: 48px;
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-md);
    color: var(--color-text);
    text-decoration: none;
    transition: background-color var(--duration-fast) var(--ease-standard);
}

.linked-item:hover,
.linked-item:focus-visible {
    border-color: var(--color-primary);
    background-color: var(--color-state-hover);
}

.linked-key {
    flex: none;
    color: var(--color-text-muted);
    white-space: nowrap;
}

.linked-summary {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.linked-item .lozenge {
    flex: none;
}

.linked-assignee,
.linked-priority {
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 24px;
}

.linked-priority .priority-icon {
    margin-right: 0;
}

/* The activity card: a timeline of comments and status changes, then the composer. Each item's avatar sits on a 2px
   line that runs from below it to the next item's avatar (::before, on the avatar's center); a ring in the card's
   white sets the avatar off from the line. */
.activity-list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.activity-item {
    position: relative;
    display: flex;
    gap: var(--space-3);
    padding-bottom: var(--space-5);
}

.activity-item:last-child {
    padding-bottom: 0;
}

.activity-item:not(:last-child)::before {
    content: "";
    position: absolute;
    top: calc(32px + var(--space-1));
    bottom: var(--space-1);
    left: 15px;
    width: 2px;
    border-radius: var(--radius-pill);
    background-color: var(--color-outline-variant);
}

.activity-item > .avatar {
    position: relative;
    box-shadow: 0 0 0 var(--space-1) var(--color-surface);
}

.activity-item > .avatar-system {
    box-shadow: inset 0 0 0 1px var(--color-outline-variant), 0 0 0 var(--space-1) var(--color-surface);
}

.activity-body {
    flex: 1;
    min-width: 0;
}

/* The author, the badge, the time and the edit state; the Edit button at the end. 32px high, the avatar's height, so
   the name sits on the avatar's center. A long name, such as an email address, wraps instead of widening the
   comment. */
.activity-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1) var(--space-2);
    min-height: 32px;
}

.activity-meta > * {
    min-width: 0;
}

.activity-author {
    color: var(--color-text);
    font-weight: var(--font-weight-semibold);
    overflow-wrap: anywhere;
}

.activity-meta time,
.activity-edited {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

/* Staff only: the customer's newest comment or reply. Outlined in the primary color on the surface, so it reads apart
   from the tonal organization badge and the amber of internal content. */
.badge-customer-replied {
    border-color: var(--color-primary);
    background-color: var(--color-surface);
    color: var(--color-primary);
}

.comment-reply,
.comment-edit {
    margin-left: auto;
}

/* Reply and Edit sit together at the end of the line. */
.comment-reply:not([hidden]) + .comment-edit {
    margin-left: 0;
}

/* The replies of a comment: a list of their own in the comment's body, below its text and forms, with the timeline's line
   between their avatars. */
.comment-replies {
    margin-top: var(--space-4);
}

/* A comment's text is a bubble below its meta line, its corner next to the avatar nearly square. */
.activity-text {
    margin: var(--space-1) 0 0;
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--color-divider);
    border-radius: var(--radius-xs) var(--radius-lg) var(--radius-lg) var(--radius-lg);
    background-color: var(--color-surface-sunken);
}

/* An internal comment's bubble takes the amber, the one place it is used besides the high priority; customers never
   see one. */
.activity-internal .activity-text {
    border-color: color-mix(in srgb, var(--color-internal-border) 35%, transparent);
    border-left: 3px solid var(--color-internal-border);
    background-color: var(--color-internal-bg);
}

/* Code blocks would vanish into the sunken bubble, so they are white there. */
.activity-text pre {
    background-color: var(--color-surface);
}

/* A status change is one compact, muted line: a smaller icon on the timeline and its text without a bubble. */
.activity-status {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

/* Still centered on the line: 4px of margin on each side of its 24px. */
.activity-status > .avatar {
    width: 24px;
    height: 24px;
    margin-inline: var(--space-1);
    font-size: var(--font-size-sm);
}

.activity-status:not(:last-child)::before {
    top: calc(24px + var(--space-1));
}

/* As tall as the icon where it fits, wrapping below it on a phone. */
.activity-status .activity-body {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0 var(--space-2);
    min-height: 24px;
}

.activity-status .activity-text {
    margin: 0;
    padding: 0;
    border: 0;
    background: none;
}

.activity-status time {
    color: var(--color-text-subtle);
}

.activity-empty {
    margin: 0;
    color: var(--color-text-muted);
}

/* The card's parts bring their own padding: the header, the lists (the earlier items' fold between them), the message
   for an empty filter, then the composer. The lists' margins collapse into one gap between them. */
.card.activity {
    padding: 0;
}

.activity > .activity-list,
.activity > .activity-fold,
.activity > .activity-empty {
    margin: var(--space-5);
}

/* The title with its count, then the filters and the order at the end; on a phone those take a line of their own. */
.activity-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-4) var(--space-5);
    border-bottom: 1px solid var(--color-divider);
}

.activity-header > .card-title {
    margin: 0;
}

.activity-tools {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-3);
    margin-left: auto;
}

/* The filters: buttons joined in one sunken frame, the pressed one raised on the card's color. */
.activity-filters {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 2px;
    max-width: 100%;
    padding: 2px;
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-md);
    background-color: var(--color-surface-sunken);
}

.activity-filter {
    display: inline-flex;
    align-items: center;
    min-height: calc(var(--control-height-sm) - 6px);
    padding: 0 var(--space-3);
    border: 0;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--color-text-muted);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
    white-space: nowrap;
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard);
}

.activity-filter:hover {
    background-color: var(--color-state-hover);
    color: var(--color-text);
}

.activity-filter[aria-pressed="true"] {
    background-color: var(--color-surface);
    box-shadow: var(--shadow-1);
    color: var(--color-text);
}

/* "<who> created the request": a compact, muted line like a status change, with a plus on the timeline. */
.activity-icon {
    position: relative;
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    margin-inline: var(--space-1);
    border-radius: var(--radius-pill);
    background-color: var(--color-surface-sunken);
    box-shadow: inset 0 0 0 1px var(--color-outline-variant), 0 0 0 var(--space-1) var(--color-surface);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

.activity-created {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

.activity-created:not(:last-child)::before {
    top: calc(24px + var(--space-1));
}

.activity-created .activity-body {
    display: flex;
    align-items: center;
    min-height: 24px;
}

.activity-line {
    margin: 0;
}

.activity-line .activity-author {
    font-weight: var(--font-weight-medium);
}

.activity-line time {
    color: var(--color-text-subtle);
}

/* The date after a dot, on the created and status lines. */
.activity-line time::before,
.activity-status .activity-body > time::before {
    content: "\00b7";
    margin-inline-end: var(--space-1);
}

/* A message from the team: a light tonal bubble (never the amber of internal comments, which keep theirs) and the
   Innovapps badge after the name. */
.badge-staff {
    border-color: var(--color-info-outline);
}

.activity-staff:not(.activity-internal) > .activity-body > .activity-text {
    border-color: var(--color-outline-variant);
    background-color: color-mix(in srgb, var(--color-primary-container) 55%, var(--color-surface));
}

/* The newest message shown is outlined in the primary color: a ring outside its border, and the border too unless it
   is an internal comment's amber one. */
.activity-item.is-latest > .activity-body > .activity-text {
    box-shadow: 0 0 0 1px var(--color-primary);
}

.activity-item.is-latest:not(.activity-internal) > .activity-body > .activity-text {
    border-color: var(--color-primary);
}

/* "Show N earlier items": a row with a hairline to its end, its text in line with the messages; open, the chevrons turn
   over. An empty word (German has none before the number) takes no gap. */
.activity-fold-toggle {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    min-height: var(--control-height-sm);
    padding-left: calc(32px + var(--space-3));
    color: var(--color-text-muted);
    font-weight: var(--font-weight-medium);
    list-style: none;
    cursor: pointer;
    user-select: none;
    transition: color var(--duration-fast) var(--ease-standard);
}

.activity-fold-toggle::-webkit-details-marker {
    display: none;
}

.activity-fold-toggle::before {
    margin-inline-end: var(--space-1);
    transition: rotate var(--duration-base) var(--ease-standard);
}

.activity-fold[open] > .activity-fold-toggle::before {
    rotate: 180deg;
}

.activity-fold-toggle::after {
    content: "";
    flex: 1;
    height: 1px;
    margin-inline-start: var(--space-2);
    background-color: var(--color-divider);
}

.activity-fold-toggle:hover {
    color: var(--color-text);
}

.activity-fold-toggle > span:empty {
    display: none;
}

.activity-fold > .activity-list {
    margin-top: var(--space-5);
}

@media (max-width: 640px) {
    .activity > .activity-list,
    .activity > .activity-fold,
    .activity > .activity-empty {
        margin: var(--space-4);
    }

    .activity-header {
        padding: var(--space-4);
    }

    .activity-tools {
        flex: 1 1 100%;
        justify-content: space-between;
        margin-left: 0;
    }
}

/* Editing in place of a comment's text, and replying below it. */
.comment-edit-form,
.comment-reply-form {
    margin-top: var(--space-2);
    animation: fade-in var(--duration-base) var(--ease-standard) backwards;
}

.comment-edit-form .banner,
.comment-reply-form .banner {
    margin-bottom: var(--space-3);
}

.comment-edit-form .field-error,
.comment-reply-form .field-error {
    margin-top: var(--space-2);
}

.comment-edit-form .form-actions,
.comment-reply-form .form-actions {
    margin-top: var(--space-3);
}

/* The composer: a body of its own below the timeline, on a hairline. */
.activity > .comment-form {
    padding-top: var(--space-5);
    border-top: 1px solid var(--color-divider);
}

.comment-form h3 {
    margin: 0 0 var(--space-3);
}

/* Who will see the comment, under the heading; the hint about Reply at the end of the line, or on a line of its own. */
.comment-form h3:has(+ .comment-visibility) {
    margin-bottom: var(--space-1);
}

.comment-visibility {
    display: flex;
    flex-wrap: wrap;
    gap: 0 var(--space-1);
    margin: 0 0 var(--space-3);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-tight);
}

.comment-visibility > * {
    min-width: 0;
    overflow-wrap: anywhere;
}

.comment-visibility-hint {
    margin-left: auto;
    padding-left: var(--space-3);
}

.comment-form .form-field {
    margin-bottom: var(--space-4);
}

@media (max-width: 640px) {
    .activity > .comment-form {
        padding-top: var(--space-4);
    }

    /* The hint takes a line of its own below who will see the comment. */
    .comment-visibility-hint {
        flex-basis: 100%;
        margin-left: 0;
        padding-left: 0;
    }
}

/* Content rendered from ADF (Atlassian Document Format), also the rich editor's content. */
.adf {
    overflow-wrap: anywhere;
}

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

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

.adf p, .adf ul, .adf ol, .adf pre, .adf blockquote, .adf-table, .adf-panel, .adf-media {
    margin: 0 0 var(--space-3);
}

.adf ul, .adf ol {
    padding-left: var(--space-5);
}

.adf li > p {
    margin: 0;
}

.adf h1, .adf h2, .adf h3, .adf h4, .adf h5, .adf h6 {
    margin: var(--space-4) 0 var(--space-2);
}

.adf pre {
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-md);
    background-color: var(--color-surface-sunken);
}

.adf blockquote {
    margin-left: 0;
    padding-left: var(--space-3);
    border-left: 3px solid var(--color-outline);
    color: var(--color-text-muted);
}

.adf hr {
    margin: var(--space-3) 0;
}

/* As a block, a wide table scrolls on its own instead of widening the page. */
.adf-table {
    display: block;
    max-width: 100%;
    overflow-x: auto;
    border-collapse: collapse;
}

/* The block shrinks the table to its content; the body as a table of its own spans the block again, and a table wider
   than the block still scrolls it. */
.adf-table > tbody {
    display: table;
    width: 100%;
}

/* .adf wraps anywhere, which also lowers a cell's minimum width to one letter, so the table layout squeezed a short
   column (a header such as "Gesamtaufwand") to a letter per line. break-word wraps only a word that is wider than the
   cell, and the cell's minimum stays its longest word. */
.adf-table th, .adf-table td {
    overflow-wrap: break-word;
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--color-outline-variant);
    background-color: var(--color-surface);
    text-align: left;
    vertical-align: top;
}

.adf-table th {
    background-color: var(--color-surface-sunken);
    font-weight: var(--font-weight-semibold);
}

/* Panels: a tint with an accent edge, by type (the editor writes data-panel-type; rendered panels are info panels). */
.adf-panel {
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--color-info-outline);
    border-left: 3px solid var(--color-primary);
    border-radius: var(--radius-sm);
    background-color: var(--color-info-container);
}

.adf-panel[data-panel-type="success"] {
    border-color: var(--color-success-outline);
    border-left-color: var(--color-success);
    background-color: var(--color-success-container);
}

.adf-panel[data-panel-type="warning"] {
    border-color: var(--color-warning-outline);
    border-left-color: var(--color-warning);
    background-color: var(--color-warning-container);
}

.adf-panel[data-panel-type="error"] {
    border-color: var(--color-danger-outline);
    border-left-color: var(--color-danger);
    background-color: var(--color-danger-container);
}

.adf-panel[data-panel-type="note"] {
    border-color: var(--color-outline-variant);
    border-left-color: var(--color-secondary);
    background-color: var(--color-surface-sunken);
}

/* An expand node with a title: a hairline box whose summary row has a chevron tile and the title; closed, the chevron
   points right, open, down. The browser's own details element does the opening, so no script is needed. */
.adf-expand {
    margin: 0 0 var(--space-3);
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-md);
    background-color: var(--color-surface);
}

.adf-expand > summary {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3);
    list-style: none;
    border-radius: var(--radius-md);
    color: var(--color-text-muted);
    cursor: pointer;
}

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

.adf-expand > summary:focus-visible {
    outline-offset: -2px;
}

.adf-expand > summary .icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--control-height-sm);
    height: var(--control-height-sm);
    border-radius: var(--radius-sm);
    background-color: var(--color-state-hover);
    color: var(--color-text);
    transition: rotate var(--duration-base) var(--ease-standard);
}

.adf-expand:not([open]) > summary .icon {
    rotate: -90deg;
}

.adf-expand-content {
    padding: 0 var(--space-3) var(--space-3) calc(var(--control-height-sm) + var(--space-3) * 2);
}

.adf-expand-content > :last-child {
    margin-bottom: 0;
}

/* A mention is a tonal chip, which stands out against white and the sunken bubble alike. */
.adf-mention {
    padding: 0 var(--space-2);
    border-radius: var(--radius-pill);
    background-color: var(--color-primary-container);
    color: var(--color-on-primary-container);
    font-weight: var(--font-weight-medium);
}

/* A status inside rendered content, as written (never upper case), like the neutral status chip; its hairline keeps
   it apart from the sunken bubble. */
.adf-status {
    display: inline-block;
    padding: 0 var(--space-2);
    border-radius: var(--radius-pill);
    background-color: var(--color-status-new-bg);
    box-shadow: inset 0 0 0 1px var(--color-outline-variant);
    color: var(--color-status-new-fg);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-base);
    text-transform: none;
}

/* A link to an issue: an inline card (type icon, key, title, status chip) with a card that opens under it on hover and
   on focus. The popover is fixed, so the clipping of the cards and of the description box around it cannot cut it off;
   it sits under the inline card by anchor positioning (above it when there is no room below, at the start of the
   other side when it would leave the window), and a browser without that leaves it at its place in the text. Its
   ::before bridges the gap, so the pointer can cross it to the popover, and Escape closes it (request-detail.js sets
   is-dismissed until the pointer or focus leaves). */
.issue-card-wrap {
    anchor-scope: --issue-card;
    display: inline-block;
    max-width: 100%;
    vertical-align: bottom;
}

.issue-card {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    max-width: 100%;
    padding: 0 var(--space-2);
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-md);
    background-color: var(--color-surface);
    color: var(--color-link);
    text-decoration: none;
    anchor-name: --issue-card;
}

.issue-card:hover,
.issue-card:focus-visible {
    border-color: var(--color-primary);
}

.issue-card .icon {
    flex: none;
}

.issue-card-key {
    flex: none;
    font-weight: var(--font-weight-semibold);
    white-space: nowrap;
}

.issue-card-summary {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.issue-card .lozenge {
    flex: none;
}

.issue-popover {
    position: fixed;
    position-anchor: --issue-card;
    position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
    top: anchor(bottom);
    left: anchor(left);
    z-index: var(--z-menu);
    display: none;
    width: 22rem;
    max-width: min(22rem, 80vw);
    padding: var(--space-4);
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-lg);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-3);
    color: var(--color-text);
    font-size: var(--font-size-md);
    text-align: left;
    white-space: normal;
}

.issue-popover::before {
    content: "";
    position: absolute;
    z-index: -1;
    inset: calc(-1 * var(--space-2)) 0;
}

.issue-card-wrap:hover:not(.is-dismissed) > .issue-popover,
.issue-card-wrap:focus-within:not(.is-dismissed) > .issue-popover {
    display: block;
}

:where(.issue-popover) > span {
    display: block;
}

:where(.issue-popover) > * + * {
    margin-top: var(--space-3);
}

/* The title is a link to the request, in the link color, as the card is. */
.issue-popover-title {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    color: var(--color-link);
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    text-decoration: none;
}

.issue-popover-title:hover {
    text-decoration: underline;
}

.issue-popover-title .icon {
    margin-top: var(--space-1);
}

.issue-popover-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
}

.issue-popover-priority {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    color: var(--color-text-muted);
}

.issue-popover-assignee {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

/* The description's lines as written, four at most; a longer one ends in an ellipsis. */
.issue-popover-excerpt {
    display: -webkit-box;
    overflow: hidden;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 4;
    line-clamp: 4;
    white-space: pre-line;
    color: var(--color-text-muted);
    overflow-wrap: anywhere;
}

/* Rows the width of the popover, each with its icon, that tint on hover and focus. */
.issue-popover-actions {
    display: flex;
    flex-direction: column;
    margin-inline: calc(-1 * var(--space-2));
}

.issue-popover-action {
    display: flex;
    align-items: center;
    width: auto;
    padding: var(--space-2);
    border: 0;
    border-radius: var(--radius-md);
    background: none;
    color: var(--color-text);
    font: inherit;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}

.issue-popover-action::before {
    margin-inline-end: var(--space-3);
    color: var(--color-text-muted);
}

.issue-popover-action:hover,
.issue-popover-action:focus-visible {
    background-color: var(--color-state-hover);
}

.issue-popover-footer {
    padding-top: var(--space-3);
    border-top: 1px solid var(--color-divider);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

/* The files of a document, side by side: an image at its own size, never wider than the text, which opens in full in a
   new tab; any other file a link. */
.adf-media {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: var(--space-2);
}

.adf-image {
    display: block;
    min-width: 0;
    max-width: 100%;
}

.adf-image img {
    display: block;
    max-width: 100%;
    height: auto;
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-sm);
}

/* A file of a comment in the editor (its file node): a block of its own between the paragraphs, named by its icon and its
   name; it is selected and moved as a whole and the selection shows. The insert buttons of the picker's rows wait for the
   editor to be there. */
.file-chip {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    width: fit-content;
    max-width: 100%;
    margin: var(--space-2) 0;
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-md);
    background-color: var(--color-surface);
    cursor: grab;
}

.file-chip > .icon {
    color: var(--color-text-muted);
}

.file-chip-name {
    overflow-wrap: anywhere;
}

.file-chip.ProseMirror-selectednode {
    outline: 2px solid var(--color-primary);
    outline-offset: 1px;
}

form[data-insertable-files]:not([data-editor-ready]) .file-insert {
    display: none;
}

/* The files a comment was posted with, under its text: an image (.adf-image) at its own size, any other file a link with
   its type icon, the size beside the name. */
.comment-files {
    margin-top: var(--space-3);
}

.comment-file {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    max-width: 100%;
    overflow-wrap: anywhere;
}

.comment-file-size {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    white-space: nowrap;
}

/* The picker of a comment or reply form follows the editor under a title of its own. */
.comment-attachments-title {
    margin-bottom: var(--space-2);
}

/* The detail page's note after a request was created or a comment with files was posted, inside its banner. */
.failed-attachments {
    margin: var(--space-1) 0 0;
    padding-left: var(--space-5);
    overflow-wrap: anywhere;
}

/* ---- Create request ---- */

/* The form is one card across the page: three sections (type, request, attachments) between full-width hairlines, then
   the footer with Send and the progress. */
.create-section.card-body {
    padding: var(--space-6);
}

.create-section + .create-section {
    border-top: 1px solid var(--color-divider);
}

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

/* A section's title: an h2, or the type section's legend, which both look like a card title, so all three read alike. */
.create-section-title {
    margin: 0 0 var(--space-5);
}

.issue-type-picker {
    min-width: 0;
    margin: 0;
    padding: 0;
    border: 0;
}

/* The types side by side across the section, as many to a row as fit at 220px or more each, sharing the whole width
   (auto-fit drops the empty columns, so a few types still span the card), so they wrap on a narrow window and stack on
   a phone. Each is a row: the icon tile, the name and a check that shows only on the checked row.
   The radio covers its row like the pill's checkbox, so a click on the text hits it and the arrow keys move within the
   group; the checked row takes the tonal accent and a 1px ring (an inset shadow, so nothing moves). The list reaches
   into the section's padding by the rows' own, so the icons line up with the title and the note. */
.issue-type-list {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
    gap: var(--space-2);
    margin-inline: calc(-1 * var(--space-3));
}

.issue-type-option {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-height: 48px;
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-md);
    color: var(--color-text);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
    cursor: pointer;
    user-select: none;
    transition: background-color var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard);
}

.issue-type-option > input {
    position: absolute;
    inset: 0;
    z-index: 1;
    width: 100%;
    height: 100%;
    margin: 0;
    border-radius: inherit;
    opacity: 0;
    cursor: pointer;
}

.issue-type-option > .type-icon {
    width: 32px;
    height: 32px;
}

.issue-type-option > .issue-type-name {
    flex: 1 1 auto;
}

.issue-type-check {
    color: var(--color-primary);
    opacity: 0;
    scale: 0.6;
    transition: opacity var(--duration-base) var(--ease-standard), scale var(--duration-base) var(--ease-emphasized);
}

.issue-type-option:hover {
    background-color: var(--color-state-hover);
}

.issue-type-option:has(> input:checked) {
    background-color: var(--color-primary-container);
    box-shadow: inset 0 0 0 1px var(--color-primary);
    color: var(--color-on-primary-container);
}

.issue-type-option:has(> input:checked):hover {
    background-color: var(--color-primary-container-hover);
}

.issue-type-option:has(> input:checked) > .issue-type-check {
    opacity: 1;
    scale: 1;
}

.issue-type-option:has(> input:focus-visible) {
    outline: 2px solid var(--color-focus);
    outline-offset: 2px;
}

/* Below the rows, in line with their icons. */
.issue-type-list + .field-error {
    margin-top: var(--space-3);
}

/* Below the types, the last line of their section. */
.issue-type-picker + .required-note {
    margin-top: var(--space-4);
}

/* The drop zone: a dashed well with the upload icon in a white circle over the text and the Browse button; while files
   are dragged over it, it takes the accent and grows slightly. Positioned, so the file input hidden in it (see
   Utilities) stays inside. */
.drop-zone {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-3);
    padding: var(--space-6) var(--space-4);
    border: 1.5px dashed var(--color-outline);
    border-radius: var(--radius-lg);
    background-color: var(--color-surface-sunken);
    color: var(--color-text-muted);
    font-size: var(--font-size-md);
    text-align: center;
    transition:
        background-color var(--duration-fast) var(--ease-standard),
        border-color var(--duration-fast) var(--ease-standard),
        scale var(--duration-base) var(--ease-standard);
}

.drop-zone-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    border-radius: var(--radius-pill);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-1);
    color: var(--color-primary);
    font-size: var(--font-size-xl);
}

.drop-zone.is-dragover {
    border-color: var(--color-primary);
    background-color: var(--color-primary-container);
    scale: 1.01;
}

/* The real input is hidden in the zone, so the zone shows its focus. */
.drop-zone:has(input:focus-visible) {
    outline: 2px solid var(--color-focus);
    outline-offset: 2px;
}

.drop-zone-text {
    max-width: 100%;
}

/* Without the script the plain file input shows in the zone; it never widens the page. */
.drop-zone.is-fallback input[type="file"] {
    max-width: 100%;
}

.attachment-messages:empty {
    display: none;
}

/* The chosen files: a framed list, one file per row with its icon, name, size and remove button. */
.file-list {
    margin: var(--space-2) 0 0;
    padding: 0;
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-md);
    background-color: var(--color-surface);
    list-style: none;
}

.file-list:empty {
    display: none;
}

.file-item {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-height: 48px;
    padding: var(--space-2) var(--space-2) var(--space-2) var(--space-4);
    animation: fade-in var(--duration-base) var(--ease-standard) backwards;
}

.file-item + .file-item {
    border-top: 1px solid var(--color-divider);
}

.file-item > .icon {
    color: var(--color-text-muted);
}

.file-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow-wrap: anywhere;
}

.file-size {
    flex: none;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* The footer: Send and Cancel at the start, the upload progress at the end while create-request.js sends the form. On
   a phone the buttons stack full width (.form-actions) and the progress goes last. */
.create-actions {
    justify-content: flex-start;
}

/* In line with the sections' content. */
.create-actions.card-footer {
    padding-inline: var(--space-6);
}

.send-progress {
    display: inline-flex;
    align-items: center;
    gap: var(--space-3);
    margin-inline-start: auto;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

.send-progress progress {
    appearance: none;
    flex: none;
    width: 160px;
    max-width: 100%;
    height: 4px;
    overflow: hidden;
    border: 0;
    border-radius: var(--radius-pill);
    background-color: var(--color-surface-tonal);
}

.send-progress progress::-webkit-progress-bar {
    border-radius: var(--radius-pill);
    background-color: var(--color-surface-tonal);
}

.send-progress progress::-webkit-progress-value {
    border-radius: var(--radius-pill);
    background-color: var(--color-primary);
    transition: width var(--duration-base) var(--ease-standard);
}

.send-progress progress::-moz-progress-bar {
    border-radius: var(--radius-pill);
    background-color: var(--color-primary);
}

@media (max-width: 640px) {
    .create-section.card-body {
        padding: var(--space-5) var(--space-4);
    }

    .drop-zone {
        padding: var(--space-5) var(--space-4);
    }

    .create-actions.card-footer {
        padding-inline: var(--space-4);
    }

    /* Under the buttons: the bar across the footer, the text below it. */
    .send-progress {
        order: 1;
        align-self: stretch;
        flex-direction: column;
        align-items: stretch;
        gap: var(--space-2);
        margin-inline-start: 0;
        text-align: center;
    }

    .send-progress progress {
        width: 100%;
    }
}

/* ===================================================================================================================
   Rich editor
   =================================================================================================================== */

/* One framed control: the toolbar above the editable area, both inside the frame, which takes an input's hover and
   focus look (the editable area's own outline is off). It does not clip, so the toolbar's dropdown menus can open past
   its edge. */
.rich-editor {
    border: 1px solid var(--color-outline-control);
    border-radius: var(--radius-md);
    background-color: var(--color-surface);
    transition: border-color var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard);
}

.rich-editor:hover {
    border-color: var(--color-outline-control-hover);
}

.rich-editor:focus-within {
    border-color: var(--color-primary);
    box-shadow: var(--focus-ring);
}

[data-rich-editor]:has(textarea[aria-invalid="true"]) .rich-editor {
    border-color: var(--color-danger);
}

[data-rich-editor]:has(textarea[aria-invalid="true"]) .rich-editor:focus-within {
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-danger) 20%, transparent);
}

/* The toolbar is the frame's quiet well, 1px inside its radius. It wraps onto more rows where it must (phones). */
.editor-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 2px;
    padding: var(--space-1) var(--space-2);
    border-bottom: 1px solid var(--color-outline-variant);
    border-radius: calc(var(--radius-md) - 1px) calc(var(--radius-md) - 1px) 0 0;
    background-color: var(--color-surface-sunken);
}

/* The text style and color lists: compact text buttons like the toolbar's icons. The select keeps its chevron from the
   select rule in Controls; the button that replaces it (.dropdown-toggle, which takes this class) draws its own. */
.editor-select {
    min-height: var(--control-height-sm);
    padding-block: 0;
    padding-left: var(--space-2);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    background-color: transparent;
    color: var(--color-text);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-medium);
}

.editor-select:hover {
    border-color: transparent;
    background-color: var(--color-state-hover);
}

/* The dropdown's own hover paints a state layer image, which the background color above replaces. */
.dropdown-toggle.editor-select:hover {
    background-image: none;
}

.editor-select[aria-expanded="true"] {
    background-color: var(--color-state-pressed);
}

/* 32px icon buttons; pressed ones take the tonal accent, like checked pills. */
.editor-button {
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: var(--control-height-sm);
    height: var(--control-height-sm);
    padding: 0;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    background-color: transparent;
    color: var(--color-text-muted);
    font-size: var(--font-size-md);
    line-height: 1;
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard);
}

.editor-button:where(:not(:disabled)):hover {
    background-color: var(--color-state-hover);
    color: var(--color-text);
}

.editor-button:where(:not(:disabled)):active {
    background-color: var(--color-state-pressed);
}

.editor-button[aria-pressed="true"] {
    background-color: var(--color-primary-container);
    color: var(--color-on-primary-container);
}

.editor-button[aria-pressed="true"]:where(:not(:disabled)):hover {
    background-color: var(--color-primary-container-hover);
    color: var(--color-on-primary-container);
}

.editor-button:disabled {
    opacity: 0.45;
    cursor: not-allowed;
}

.editor-separator {
    flex: none;
    width: 1px;
    height: 20px;
    margin: 0 var(--space-1);
    background-color: var(--color-outline-variant);
}

/* The link popover, between the toolbar and the editable area. */
.link-popover {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--color-divider);
    animation: fade-in var(--duration-fast) var(--ease-standard) backwards;
}

.link-popover .text-input {
    flex: 1 1 200px;
    width: auto;
    min-height: var(--control-height-sm);
}

.link-label {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
}

.link-popover .field-error {
    flex-basis: 100%;
    margin: 0;
}

/* The editable area; its content is styled like rendered content (.adf). */
.editor-content .ProseMirror {
    min-height: 140px;
    padding: var(--space-3) var(--space-4);
    border-radius: 0 0 calc(var(--radius-md) - 1px) calc(var(--radius-md) - 1px);
    line-height: var(--line-height-base);
    outline: none;
}

.editor-content .ProseMirror p.is-editor-empty:first-child::before {
    content: attr(data-placeholder);
    float: left;
    height: 0;
    color: var(--color-text-subtle);
    pointer-events: none;
}

/* While editing, a table spans the editor; .adf-table's block display would shrink it to its content. */
.editor-content .adf-table {
    display: table;
    width: 100%;
    table-layout: fixed;
}

.editor-content .adf-table > tbody {
    display: table-row-group;
    width: auto;
}

.editor-content .adf-table p {
    margin: 0;
}

/* The cell selection prosemirror-tables marks while cells are selected. */
.editor-content .selectedCell {
    background-color: var(--color-state-selected);
    outline: 2px solid var(--color-focus);
    outline-offset: -2px;
}

.editor-content .ProseMirror-gapcursor::after {
    border-top-color: var(--color-text);
}

/* ---- Admin ---- */

/* The admin dashboard: the impersonation, test email and scheduled tasks cards side by side in columns of at least 360px,
   one column on a narrow window; the settings card below them takes the full width. The cards of a row are as tall as
   the tallest one. The grid's gap spaces the cards, not the gap between cards. */
.admin-dashboard {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 360px), 1fr));
    gap: var(--space-5);
    align-items: stretch;
}

.admin-dashboard > .card {
    min-width: 0;
    /* A card's anchor in the side navigation lands clear of the app bar. */
    scroll-margin-top: calc(var(--app-bar-height) + var(--space-4));
}

.admin-dashboard > .card + .card {
    margin-top: 0;
}

/* The capabilities cards take the full width of the dashboard, whatever the number of columns. */
.admin-dashboard > .admin-capabilities-card {
    grid-column: 1 / -1;
}

/* Tasks, Hooks & Caches: the tasks and hooks cards stacked on the left, the caches card beside them on the right as tall
   as both; one column below 1100px, in the order of the page. */
.admin-dashboard-split {
    grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 1100px) {
    .admin-dashboard-split {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .admin-dashboard-split > .admin-dashboard-aside {
        grid-column: 2;
        grid-row: 1 / span 2;
    }
}

/* The impersonation, test email and import cards: their notes differ in length, so the field sits at the bottom of the
   body and the footer with the button at the bottom of the card (.card-form), and the inputs and buttons line up across
   the row. */
.admin-form-card {
    /* The impersonation card's address list opens past the card's bottom edge; the header has no background that the
       clip would round off. */
    overflow: visible;
}

.admin-form-card > .card-form > .card-body {
    display: flex;
    flex-direction: column;
}

.admin-form-card > .card-form > .card-body > .form-field {
    margin-top: auto;
}

.admin-test-email-note,
.admin-impersonate-note,
.admin-import-note,
.admin-code-signin-note,
.admin-service-account-note,
.admin-caches-note,
.admin-maintenance-note,
.admin-emoji-note {
    color: var(--color-text-muted);
}

/* Download all as a zip: the note under the title, then the switch (saved on change) and the fixed rules, one sentence
   per row between hairlines. */
#admin-attachment-zip > .card-header > .card-note {
    flex-basis: 100%;
}

.admin-rules {
    margin: var(--space-4) 0 0;
    padding: 0;
    list-style: none;
}

.admin-rules > li {
    padding-block: var(--space-3);
    border-top: 1px solid var(--color-divider);
}

.admin-rules > li:last-child {
    padding-bottom: 0;
}

/* The live cards of the calls to and from the ticket system (admin-ticket-calls.js), each across the whole dashboard. The
   title has its note below it. Without scripts only a line saying the counts need them shows. */
.admin-dashboard > .calls-card {
    grid-column: 1 / -1;
}

.calls-card-heading {
    min-width: 0;
}

.calls-card-heading > .card-note {
    margin-top: var(--space-1);
}

html:not(.js) .calls-live,
.js .calls-noscript {
    display: none;
}

.calls-noscript {
    margin: 0;
    color: var(--color-text-muted);
}

/* The tile: its title with the live pill, the big number of the last complete minute with the estimate per hour and the
   sub line, the legend, the chart (or the table of the minutes) and the foot note with the button that swaps them. */
.calls-tile {
    padding: var(--space-4) var(--space-5) var(--space-3);
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-lg);
    background-color: var(--color-surface);
}

.calls-tile-title {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin: 0 0 var(--space-3);
    font-weight: var(--font-weight-semibold);
}

/* LIVE while the pushes arrive; neutral while connecting and once they stopped (Offline). */
.live-pill {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: 0 var(--space-2);
    border-radius: var(--radius-pill);
    background-color: var(--color-surface-tonal);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    letter-spacing: var(--letter-spacing-caps);
    text-transform: uppercase;
}

.live-pill::before {
    content: "";
    width: 6px;
    height: 6px;
    border-radius: var(--radius-pill);
    background-color: currentColor;
}

.live-pill[data-state="live"] {
    background-color: var(--color-danger);
    color: var(--color-text-on-primary);
}

.calls-tile-top {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--space-2) var(--space-5);
    margin-bottom: var(--space-3);
}

.calls-now {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-1) var(--space-3);
    margin: 0;
}

.calls-now-value {
    color: var(--color-primary);
    font-size: var(--font-size-xl);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight);
    letter-spacing: var(--letter-spacing-tight);
    font-variant-numeric: tabular-nums;
}

.calls-now-unit,
.calls-sub {
    color: var(--color-text-muted);
}

.calls-sub {
    margin: var(--space-2) 0 0;
}

.calls-number {
    font-variant-numeric: tabular-nums;
}

.calls-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1) var(--space-4);
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: var(--font-size-sm);
}

.calls-legend li {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.calls-swatch {
    flex: none;
    width: 10px;
    height: 10px;
    border-radius: var(--radius-xs);
}

/* The outcomes in the legend's order, bottom-up in a bar: a swatch's background, a bar's fill. */
.calls-series-0 {
    background-color: var(--color-primary);
    fill: var(--color-primary);
}

.calls-series-1 {
    background-color: var(--color-text-subtle);
    fill: var(--color-text-subtle);
}

.calls-series-2 {
    background-color: var(--color-warning-outline);
    fill: var(--color-warning-outline);
}

.calls-series-3 {
    background-color: var(--color-danger);
    fill: var(--color-danger);
}

.calls-series-4 {
    background-color: var(--color-danger-outline);
    fill: var(--color-danger-outline);
}

/* The chart's box gives the height; the script sizes the SVG to the box in pixels and draws it again when the box's
   width changes (until then the SVG shrinks with it). */
.calls-chart {
    min-width: 0;
    height: 210px;
}

.calls-chart > svg {
    display: block;
    max-width: 100%;
    overflow: visible;
}

.calls-grid {
    stroke: var(--color-divider);
    stroke-width: 1;
    shape-rendering: crispEdges;
}

.calls-axis-label {
    fill: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-variant-numeric: tabular-nums;
}

/* A minute's whole column takes the pointer, so its tooltip shows over an empty minute too. */
.calls-bar-hit {
    fill: transparent;
}

.calls-bar:hover > .calls-bar-hit {
    fill: var(--color-state-hover);
}

.calls-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2) var(--space-4);
    margin-top: var(--space-2);
}

.calls-foot-note {
    flex: 1 1 20rem;
    margin: 0;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

/* "Show as table" / "Show as chart": a button that looks like the page's links. */
.calls-toggle {
    padding: 0;
    border: 0;
    background: none;
    color: var(--color-link);
    font: inherit;
    text-decoration-line: underline;
    text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
    text-decoration-thickness: 1px;
    text-underline-offset: 0.2em;
    cursor: pointer;
    transition: text-decoration-color var(--duration-fast) var(--ease-standard);
}

.calls-toggle:hover {
    text-decoration-color: currentColor;
}

/* The table of the minutes, newest first, scrolls inside the tile under its sticky header row. */
.calls-table-wrap {
    max-height: 420px;
    margin-top: var(--space-3);
    overflow-y: auto;
    border: 1px solid var(--color-divider);
    border-radius: var(--radius-md);
}

.calls-table th {
    position: sticky;
    top: 0;
}

.calls-table th + th,
.calls-table td + td {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* The last hour by kind or event: the name with its muted note, a bar against the largest row and the number. */
.calls-kinds {
    margin-top: var(--space-5);
    padding-top: var(--space-4);
    border-top: 1px solid var(--color-divider);
}

.calls-kind-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

.calls-kind {
    display: grid;
    grid-template-columns: minmax(0, 340px) minmax(0, 1fr) 64px;
    align-items: center;
    gap: var(--space-4);
}

.calls-kind-name {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0 var(--space-2);
    min-width: 0;
}

.calls-kind-note {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

.calls-kind-count {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.calls-kind-bar {
    appearance: none;
    display: block;
    width: 100%;
    height: 8px;
    overflow: hidden;
    border: 0;
    border-radius: var(--radius-pill);
    background-color: var(--color-surface-sunken);
}

.calls-kind-bar::-webkit-progress-bar {
    border-radius: var(--radius-pill);
    background-color: var(--color-surface-sunken);
}

.calls-kind-bar::-webkit-progress-value {
    border-radius: var(--radius-pill);
    background-color: var(--color-primary);
}

.calls-kind-bar::-moz-progress-bar {
    border-radius: var(--radius-pill);
    background-color: var(--color-primary);
}

/* A phone: the name and the number on one line, the bar across below them. */
@media (max-width: 640px) {
    .calls-tile {
        padding: var(--space-3) var(--space-4);
    }

    .calls-kind {
        grid-template-columns: minmax(0, 1fr) auto;
        gap: var(--space-1) var(--space-3);
    }

    .calls-kind-bar {
        grid-column: 1 / -1;
        grid-row: 2;
    }
}

/* The Service Agent page: a column of cards, then the result table. Each check is an icon (a colour is never the only sign:
   the word is visually hidden beside it); the access level is a badge in the semantic colours. */
.service-agent {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
}

.service-agent > .card {
    margin-top: 0;
}

.service-agent-subtitle {
    margin-top: var(--space-4);
}

.service-agent-checks {
    margin: var(--space-2) 0 0;
    padding-left: var(--space-5);
}

.service-agent-checks li + li {
    margin-top: var(--space-1);
}

.service-agent-account-list {
    margin: 0;
}

.probe-state {
    display: inline-flex;
    align-items: center;
    vertical-align: middle;
}

.probe-state-granted {
    color: var(--color-success);
}

.probe-state-denied {
    color: var(--color-danger);
}

.probe-state-unknown {
    color: var(--color-text-subtle);
}

.probe-level-full {
    border-color: var(--color-success-outline);
    background-color: var(--color-success-container);
    color: var(--color-success);
}

.probe-level-limited {
    border-color: var(--color-warning-outline);
    background-color: var(--color-warning-container);
    color: var(--color-warning);
}

.probe-level-none {
    border-color: var(--color-danger-outline);
    background-color: var(--color-danger-container);
    color: var(--color-danger);
}
.probe-level-unknown {
    border-color: var(--color-outline-variant);
    background-color: var(--color-surface-sunken);
    color: var(--color-text-muted);
}

.service-agent-table th {
    white-space: normal;
}

.service-agent-table td {
    vertical-align: top;
}

/* Ten columns in a content column: the cells are narrower than the table's usual, and a long name or key breaks rather
   than widen the table past its card. */
.service-agent-table th,
.service-agent-table td {
    padding-inline: var(--space-2);
}

.service-agent-project,
.service-agent-organizations {
    width: 22%;
    overflow-wrap: anywhere;
}
/* A project is two rows in a body of its own: the permissions, then one line about it, which belongs to the first row, so
   the hairline is under both. */
.service-agent-table .service-agent-main > td {
    border-bottom: 0;
}

.service-agent-detail-row > td {
    padding-top: 0;
    color: var(--color-text-muted);
}

.service-agent-project-name {
    display: block;
    color: var(--color-text-muted);
}

.service-agent-sample-items {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1) var(--space-5);
}

.service-agent-sample-item {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
}

.service-agent-sample-item strong {
    color: var(--color-text);
    font-weight: var(--font-weight-medium);
}
/* The permission cells hold one icon: centred under their heading. */
.service-agent-permission {
    text-align: center;
}

.service-agent-table th:nth-child(n + 4) {
    text-align: center;
}

@media (max-width: 640px) {
    .service-agent-permission {
        text-align: left;
    }

    /* The rows are stacked blocks here, so the share of the table's width means nothing. */
    .service-agent-project,
    .service-agent-organizations {
        width: auto;
    }
}

/* The Cookies page: the banner and the two cards a card's gap apart. */
.admin-cookies {
    display: grid;
    gap: var(--space-5);
}

.admin-cookies > .card {
    min-width: 0;
    margin-top: 0;
}

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

/* A cookie's names, one under the other; the cells align at the top as the purpose text can run to several lines. */
.cookies-name code {
    display: block;
    width: fit-content;
    overflow-wrap: anywhere;
}

.cookies-name code + code {
    margin-top: var(--space-1);
}

.cookies-table td {
    vertical-align: top;
}

/* The state of maintenance mode, below its note. */
.admin-maintenance-status {
    font-weight: var(--font-weight-semibold);
}

/* The scheduled tasks: each task's name small and muted above its last run, the tasks between hairlines. */
.admin-tasks {
    margin: 0;
}

.admin-tasks dt {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
}

.admin-tasks dt:not(:first-of-type) {
    margin-top: var(--space-3);
    padding-top: var(--space-3);
    border-top: 1px solid var(--color-divider);
}

.admin-tasks dd {
    margin: var(--space-1) 0 0;
    font-variant-numeric: tabular-nums;
    overflow-wrap: anywhere;
}

.admin-tasks dd::before {
    color: var(--color-text-muted);
}

/* ---- The admin pages' side navigation ---- */

/* The side navigation and the page, side by side from 900px (the app's breakpoint); below it the navigation folds into
   a button above the page (admin-nav.js). The navigation stays in view while the page scrolls. */
.admin-shell {
    display: grid;
    grid-template-columns: var(--admin-nav-width) minmax(0, 1fr);
    align-items: start;
    gap: var(--space-5);
}

.admin-content {
    min-width: 0;
}

.admin-nav {
    position: sticky;
    top: calc(var(--app-bar-height) + var(--space-4));
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - var(--app-bar-height) - var(--space-5));
    padding: var(--space-3);
    overflow-y: auto;
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-xl);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-1);
}

/* The title block: a tile with the page's mark, the section's name and a line below it. The button of the narrow
   window is the same block with the current page's name instead of the line. */
.admin-nav-head,
.admin-nav-toggle {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--color-divider);
}

/* The tile's center is the center of the row icons (and of the rail), so it stays put when the navigation folds. */
.admin-nav-head {
    padding-inline-start: var(--admin-nav-tile-inset);
}

.admin-nav-tile {
    display: grid;
    flex: none;
    place-items: center;
    width: 36px;
    height: 36px;
    border-radius: var(--radius-lg);
    background-color: var(--color-primary-container);
    color: var(--color-on-primary-container);
    font-size: var(--font-size-lg);
}

.admin-nav-head-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
    line-height: var(--line-height-tight);
    text-align: start;
}

.admin-nav-title {
    font-weight: var(--font-weight-semibold);
    letter-spacing: var(--letter-spacing-tight);
}

.admin-nav-subtitle {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

/* The collapse button of the wide window, in the title block; its icon shows what pressing it does next (admin-nav.js). */
.admin-nav-collapse {
    display: none;
    margin-inline-start: auto;
}

.admin-nav-collapse .icon-panel-left-open {
    display: none;
}

/* The button only exists for the narrow window with scripts. */
.admin-nav-toggle {
    display: none;
    width: 100%;
    padding: var(--space-1);
    border-top: 0;
    border-right: 0;
    border-left: 0;
    background: none;
    color: inherit;
    font: inherit;
    cursor: pointer;
}

.admin-nav-chevron {
    margin-inline-start: auto;
    color: var(--color-text-muted);
    font-size: var(--font-size-lg);
    transition: transform var(--duration-base) var(--ease-standard);
}

.admin-nav.is-open .admin-nav-chevron {
    transform: rotate(180deg);
}

.admin-nav-body {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.admin-nav-label {
    margin: var(--space-3) 0 0;
    padding: var(--space-1) var(--space-3);
    color: var(--color-text-subtle);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
}

.admin-nav-link {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-height: var(--control-height);
    padding: 0 var(--space-3);
    border: 1px solid transparent;
    border-radius: var(--radius-lg);
    color: var(--color-text-muted);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
    text-decoration: none;
    transition: background-color var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard);
}

.admin-nav-link .icon {
    color: var(--color-secondary);
    font-size: var(--font-size-lg);
}

.admin-nav-link:hover {
    background-color: var(--color-state-hover);
    color: var(--color-text);
}

/* The page being shown: a raised, tinted row with a primary icon. */
.admin-nav-link[aria-current="page"] {
    border-color: var(--color-outline);
    background-color: var(--color-primary-container);
    color: var(--color-on-primary-container);
    box-shadow: var(--shadow-1);
}

.admin-nav-link[aria-current="page"] .icon {
    color: var(--color-primary);
}

.admin-nav-link-text {
    flex: 1;
    min-width: 0;
}

/* The unsent mails beside Mail queue. */
.admin-nav-count {
    display: inline-grid;
    flex: none;
    place-items: center;
    min-width: 22px;
    height: 22px;
    padding: 0 var(--space-2);
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-pill);
    background-color: var(--color-surface-tonal);
    color: var(--color-on-primary-container);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    font-variant-numeric: tabular-nums;
}

/* The groups of the Configuration page under its row, on a rule, each with a small square mark. */
.admin-nav-sub {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin: 0 0 var(--space-1) calc(var(--space-3) + 8px);
    padding-inline-start: var(--space-2);
    border-inline-start: 1px solid var(--color-outline-variant);
}

.admin-nav-sub .admin-nav-link {
    min-height: 36px;
}

.admin-nav-sub .admin-nav-link::before {
    content: "";
    flex: none;
    width: 8px;
    height: 8px;
    border: 1.5px solid var(--color-outline-control);
    border-radius: var(--radius-xs);
}

/* From 900px with scripts the navigation can fold into a rail of icons (the class is on <html>, set by admin-nav.js, so
   it holds across page changes). The rail is laid out on the same grid as the column: the tile and the row icons sit at
   the same place in both, the collapse button leaves the title block's row for the slot of the first group name, and
   the group names keep their height and show only a hairline, so nothing but the width changes when it folds. The
   groups of the page under its row are left out, the count of unsent mails sits on the icon, and a row's name shows
   beside the rail as a label on hover or focus. The columns only slide after the person's own press. */
@media (min-width: 900px) {
    .js .admin-nav-collapse {
        display: inline-flex;
    }

    .admin-shell.is-animated {
        transition: grid-template-columns var(--duration-base) var(--ease-standard);
    }

    /* While the columns slide the navigation is narrower than its content: the names stay on one line and the overflow
       is clipped, so nothing wraps, grows or sticks out on the way. */
    .admin-shell.is-animated .admin-nav {
        overflow: clip;
    }

    .admin-shell.is-animated .admin-nav-head-text,
    .admin-shell.is-animated .admin-nav-link-text {
        white-space: nowrap;
    }

    .admin-nav-collapsed .admin-shell {
        grid-template-columns: var(--admin-nav-rail-width) minmax(0, 1fr);
    }

    /* The labels sit outside the rail, so it cannot clip them; a window too short for its rows is handled below. */
    .admin-nav-collapsed .admin-nav {
        z-index: var(--z-menu);
        overflow: visible;
    }

    .admin-nav-collapsed .admin-nav-head {
        position: relative;
    }

    .admin-nav-collapsed .admin-nav-head-text {
        display: none;
    }

    /* Under the tile, in the room of the first group name. */
    .admin-nav-collapsed .admin-nav-collapse {
        position: absolute;
        top: calc(100% + var(--space-1));
        inset-inline-start: 50%;
        margin: 0;
        translate: -50% 0;
    }

    .admin-nav-collapsed .admin-nav-collapse .icon-panel-left-close {
        display: none;
    }

    .admin-nav-collapsed .admin-nav-collapse .icon-panel-left-open {
        display: inline-block;
    }

    /* A group name keeps its line and its margins and shows a hairline instead of its text; the first one has none, the
       title block's rule is above it. */
    .admin-nav-collapsed .admin-nav-label {
        overflow: hidden;
        background: linear-gradient(var(--color-divider), var(--color-divider)) center / calc(100% - 2 * var(--space-2)) 1px no-repeat;
        color: transparent;
        white-space: nowrap;
    }

    .admin-nav-collapsed .admin-nav-body > .admin-nav-label:first-child {
        background: none;
    }

    .admin-nav-collapsed .admin-nav-sub {
        display: none;
    }

    .admin-nav-collapsed .admin-nav-link-text {
        position: absolute;
        top: 50%;
        inset-inline-start: calc(100% + var(--space-3));
        z-index: var(--z-menu);
        flex: none;
        padding: var(--space-2) var(--space-3);
        border: 1px solid var(--color-outline-variant);
        border-radius: var(--radius-md);
        background-color: var(--color-surface);
        box-shadow: var(--shadow-3);
        color: var(--color-text);
        font-size: var(--font-size-sm);
        white-space: nowrap;
        translate: 0 -50%;
        opacity: 0;
        pointer-events: none;
        transition: opacity var(--duration-fast) var(--ease-standard);
    }

    .admin-nav-collapsed .admin-nav-link:is(:hover, :focus-visible) .admin-nav-link-text {
        opacity: 1;
    }

    .admin-nav-collapsed .admin-nav-count {
        position: absolute;
        top: 2px;
        inset-inline-end: 2px;
        min-width: 18px;
        height: 18px;
        padding: 0 var(--space-1);
    }
}

/* In a window too short for the rail's rows it stops sticking and scrolls with the page, so its labels are not cut off. */
@media (min-width: 900px) and (max-height: 700px) {
    .admin-nav-collapsed .admin-nav {
        position: static;
        max-height: none;
    }
}

/* The current settings: one card per group, side by side in columns of at least 420px, as many as the page is wide;
   the cards of a row are as tall as the tallest one, as on the dashboard. */
.admin-config {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 420px), 1fr));
    align-items: stretch;
    gap: var(--space-5);
}

.admin-config > .card {
    min-width: 0;
    margin-top: 0;
    /* A group's anchor lands clear of the app bar. */
    scroll-margin-top: calc(var(--app-bar-height) + var(--space-4));
}

/* A card that takes a row of its own, whatever the number of columns. */
.admin-config > .admin-config-wide {
    grid-column: 1 / -1;
}

@media (max-width: 899px) {
    .admin-shell {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--space-4);
    }

    .admin-nav {
        position: static;
        max-height: none;
        overflow: visible;
    }

    .js .admin-nav-head {
        display: none;
    }

    .js .admin-nav-toggle {
        display: flex;
    }

    .js .admin-nav:not(.is-open) .admin-nav-toggle {
        border-bottom-color: transparent;
    }

    .js .admin-nav:not(.is-open) .admin-nav-body {
        display: none;
    }

    .js .admin-nav.is-open .admin-nav-body {
        margin-top: var(--space-2);
    }

    .admin-nav-link {
        min-height: 44px;
    }

    .admin-nav-sub .admin-nav-link {
        min-height: 40px;
    }

    .admin-config > .card {
        scroll-margin-top: calc(var(--app-bar-height) + var(--space-3));
    }
}

.admin-settings-block {
    min-width: 0;
}

.admin-settings-block-title {
    margin: 0 0 var(--space-3);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight);
}

/* A group's rows: name and value on one row between hairlines, the value at the end; a value too long for the row
   wraps. Both cells of a row stretch to its height, so their hairlines meet; the name's line is as tall as the value's,
   so the two read on one line. The first and last row of each group have no padding on the outer side. */
.admin-settings-list {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, max-content);
    margin: 0;
}

/* A note above the list keeps its distance from the first row. */
.field-note + .admin-settings-list {
    margin-top: var(--space-4);
}

/* A note below the list (the Log archive card's) keeps the same distance from the last row and ends the card's body. */
.admin-settings-list + .field-note {
    margin: var(--space-4) 0 0;
}

.admin-settings-list dt,
.admin-settings-list dd {
    padding: var(--space-2) 0;
}

.admin-settings-list dt:not(:first-of-type),
.admin-settings-list dt:not(:first-of-type) + dd {
    border-top: 1px solid var(--color-divider);
}

.admin-settings-list dt:first-of-type,
.admin-settings-list dt:first-of-type + dd {
    padding-top: 0;
}

.admin-settings-list dt:last-of-type,
.admin-settings-list dd:last-of-type {
    padding-bottom: 0;
}

.admin-settings-list dt {
    padding-right: var(--space-4);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    line-height: calc(var(--font-size-md) * var(--line-height-base));
}

.admin-settings-list dd {
    margin: 0;
    text-align: right;
    overflow-wrap: anywhere;
}

/* The scopes of the service account: a list between hairlines (the first and last without padding on the outer side),
   each the scope's name above one sentence on what it is used for. */
.admin-scope-list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.admin-scope-list li {
    padding: var(--space-2) 0;
    overflow-wrap: anywhere;
}

.admin-scope-list li + li {
    border-top: 1px solid var(--color-divider);
}

.admin-scope-list li:first-child {
    padding-top: 0;
}

.admin-scope-list li:last-child {
    padding-bottom: 0;
}

.admin-scope-name,
.admin-scope-description {
    display: block;
}

.admin-scope-name {
    font-weight: var(--font-weight-medium);
}

.admin-scope-description {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

/* What an admin can do: the groups side by side like the settings, each a list of sentences between hairlines (the
   first and last without padding on the outer side). */
.admin-capabilities {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr));
    align-items: start;
    gap: var(--space-5) var(--space-6);
}

.admin-capabilities-list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.admin-capabilities-list li {
    padding: var(--space-2) 0;
    overflow-wrap: anywhere;
}

.admin-capabilities-list li + li {
    border-top: 1px solid var(--color-divider);
}

.admin-capabilities-list li:first-child {
    padding-top: 0;
}

.admin-capabilities-list li:last-child {
    padding-bottom: 0;
}

/* The edit controls of a panel of the Configuration page (admin-panel-edit.js swaps them for the values): a form whose
   fields sit one below the other, as wide as the card, with Save and Cancel under them. A field's id and its type share
   one row. */
.admin-panel-form {
    display: grid;
    gap: var(--space-4);
    min-width: 0;
}

.admin-panel-form > * {
    margin: 0;
}

.admin-settings-field-row {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    align-items: start;
    gap: var(--space-4) var(--space-5);
}

.admin-settings-field-row > * {
    margin: 0;
}

@media (max-width: 640px) {
    .admin-settings-field-row {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* A panel form that spaces its rows with margins instead of the grid's gap (a gap would stay around a row of zero height). */
.admin-panel-form-stacked {
    gap: 0;
}

.admin-panel-form-stacked > * + * {
    margin-top: var(--space-4);
}

/* A note under a switch keeps its small step under the switch (it would otherwise get the row spacing). */
.admin-panel-form-stacked > .switch-note {
    margin-top: var(--space-2);
}

/* The mail server form holds the fields of both sign-ins (user name and password, OAuth); the OAuth switch at its top picks
   which set is open, so the form needs no script and a form swapped in by admin-panel-edit.js is right at once (the
   transition only runs when the switch is flipped). A closed set is a row of zero height without the margin above it, and
   is hidden (after the slide) so its fields leave the tab order. The inner box clips the fields while the row grows, but
   not the focus ring around them (the clip margin). */
.admin-panel-form-stacked > .smtp-collapse {
    display: grid;
    grid-template-rows: 0fr;
    margin-top: 0;
    visibility: hidden;
    transition:
        grid-template-rows var(--duration-base) var(--ease-standard),
        margin-top var(--duration-base) var(--ease-standard),
        visibility 0s linear var(--duration-base);
}

.admin-panel-form-stacked:has(#admin-setting-smtp-use-oauth:checked) > .smtp-collapse-oauth,
.admin-panel-form-stacked:not(:has(#admin-setting-smtp-use-oauth:checked)) > .smtp-collapse-password {
    grid-template-rows: 1fr;
    margin-top: var(--space-4);
    visibility: visible;
    transition-delay: 0s;
}

.smtp-collapse-inner {
    display: grid;
    gap: var(--space-4);
    min-height: 0;
    overflow: clip;
    overflow-clip-margin: var(--space-1);
}

.smtp-collapse-inner > * {
    margin: 0;
}

/* The end of a card's header: the pencil of a panel. The space stays when the pencil is gone (the panel is being edited),
   so the header keeps its height, and so does the card beside it that has no pencil. */
.card-actions {
    display: flex;
    align-items: center;
    min-height: var(--control-height-sm);
}

/* ---- Organizations ---- */

/* The parts of an organization page that nav.js swaps on their own after a change (data-nav-target). Such a swap runs
   no view transition: the page stays as it is, with its scroll position, and only the region's content changes. */

/* The staff page's top grid: the rename, add-member and projects cards in the first row, the member and organizer
   settings cards in the second; the cards of a row are as tall as the tallest one. From 1100px the grid has six columns,
   so the first row's cards take two each and the second row's take three; below it two columns, with the projects card
   across both; one column below 900px (the app's breakpoint). The grid's gap spaces the cards, not .card + .card. */
.organization-top {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    align-items: stretch;
    gap: var(--space-5);
}

.organization-top > .card + .card {
    margin-top: 0;
}

.organization-top > .organization-projects {
    grid-column: 1 / -1;
}

@media (min-width: 1100px) {
    .organization-top {
        grid-template-columns: repeat(6, minmax(0, 1fr));
    }

    .organization-top > .card {
        grid-column: span 3;
    }

    .organization-top > .organization-top-card,
    .organization-top > .organization-projects {
        grid-column: span 2;
    }
}

/* The projects of an organization, one per row between hairlines like the members without an organization. */
.project-list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.project-list > li {
    padding: var(--space-2) 0;
    overflow-wrap: anywhere;
}

.project-list > li + li {
    border-top: 1px solid var(--color-divider);
}

@media (max-width: 899px) {
    .organization-top {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* The members region below the top row keeps a card's gap from it. */
.organization-top + .organization-region {
    margin-top: var(--space-5);
}

/* The status pills of the Organization and Request List cards sit a little below the note above them. */
.admin-status-options {
    margin-top: var(--space-3);
}

/* The title of the customer statuses, set apart from the switches above it in the Organization global settings card. */
.admin-organization-statuses-title {
    margin-top: var(--space-4);
}

/* The Apply and Save buttons of the page size and the portal access forms are for pages without scripts, where
   organization-members.js sends the choice at once. While such a form is on its way (nav.js sets aria-busy) its switch
   looks disabled, and a click on it is undone. */
.js .organization-page-size-apply,
.js .admin-inline-save-button,
.js .member-access-toggle,
.js .organization-access-all-button {
    display: none;
}

.member-form[aria-busy="true"] input.switch,
.organization-access-all-form[aria-busy="true"] input.switch {
    cursor: progress;
}

/* The all-members switch below the rename form: its note under it, muted, and the Save button for pages without scripts. */
.organization-access-all-form {
    display: grid;
    gap: var(--space-2);
}

.organization-access-all-form .field-note {
    margin: 0;
}

.organization-access-all-form .btn {
    justify-self: start;
}

/* The switches of the organizations list have no visible text: their label is only as wide as the switch, so a click
   elsewhere in the cell does nothing. */
.organization-flag-form .switch-field {
    width: fit-content;
    min-height: 0;
}

/* A row's permissions: the summary chips and the Edit button on one line, wrapping on a narrow column. A chip is a pill
   with the capability's icon, outlined and tinted in the primary color while any of its settings is on, followed by the
   letters of the roles that have it (a setting that is off shows none). */
.permission-cell {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-4);
}

.permission-summary {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
    margin: 0;
    padding: 0;
    list-style: none;
}

.permission-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    min-height: 26px;
    padding: 0 var(--space-2);
    border-radius: var(--radius-pill);
    box-shadow: inset 0 0 0 1px var(--color-outline-variant);
    color: var(--color-text-subtle);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    line-height: 1;
    cursor: default;
    transition: background-color var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard);
}

.permission-chip > .icon {
    font-size: var(--font-size-md);
}

.permission-chip.is-on {
    background-color: var(--color-state-selected);
    box-shadow: inset 0 0 0 1px var(--color-primary);
    color: var(--color-link);
}

.permission-mark:not(.is-on) > .permission-role {
    display: none;
}

/* At the column's end, so the buttons of all rows line up whatever their chips (at the start in a stacked row). */
.permission-edit {
    flex: none;
    margin-inline-start: auto;
    anchor-name: --permission-edit;
}

/* Each row's Edit button names the same anchor; the scope keeps the popover of a row on its own button. */
.organization-permissions {
    anchor-scope: --permission-edit;
}

/* The browser's popover in the top layer, so the table wrap cannot clip it. With anchor positioning it opens under the
   Edit button, its end edge on the button's (below); a browser without that shows it in the middle of the window, as
   the popover does by default. It does not inherit the cell's look (a stacked row's
   wrapping, the hover tint). It fades in like a dialog. */
.permission-popover {
    width: min(26rem, calc(100vw - 2 * var(--space-4)));
    max-height: calc(100vh - 2 * var(--space-4));
    padding: 0;
    overflow-y: auto;
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-lg);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-3);
    color: var(--color-text);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-regular);
    line-height: var(--line-height-base);
    text-align: left;
    white-space: normal;
    overflow-wrap: normal;
    opacity: 0;
    translate: 0 calc(-1 * var(--space-1));
    transition-property: opacity, translate, display, overlay;
    transition-duration: var(--duration-fast);
    transition-timing-function: var(--ease-exit);
    transition-behavior: allow-discrete;
}

.permission-popover:popover-open {
    opacity: 1;
    translate: 0 0;
    transition-timing-function: var(--ease-emphasized);
}

@starting-style {
    .permission-popover:popover-open {
        opacity: 0;
        translate: 0 calc(-1 * var(--space-1));
    }
}

/* The area under the button and to its start, or above it when there is more room there. The area is the popover's
   containing block, so the percentage holds its height to the area less the margins: in a short window it scrolls
   inside rather than leave the window. */
@supports (position-area: bottom) {
    .permission-popover {
        position-anchor: --permission-edit;
        position-area: bottom span-left;
        position-try-fallbacks: flip-block;
        position-try-order: most-height;
        inset: auto;
        max-height: calc(100% - 2 * var(--space-2));
        margin: var(--space-2) 0;
    }
}

.permission-popover-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-4) var(--space-4) var(--space-3) var(--space-5);
    border-bottom: 1px solid var(--color-divider);
}

.permission-popover-eyebrow {
    margin: 0;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
}

.permission-popover-title {
    margin: 0;
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    overflow-wrap: anywhere;
}

/* Capabilities down the side, the two roles across the top, a switch per cell. A setting of both roles has one switch
   on a sunken band across both columns, so it reads as one setting rather than two that happen to agree. */
.permission-matrix {
    display: grid;
    grid-template-columns: 1fr repeat(2, minmax(5.5rem, auto));
    align-items: center;
    row-gap: var(--space-1);
    padding: var(--space-3) var(--space-4) var(--space-3) var(--space-5);
}

.permission-matrix-role {
    padding-bottom: var(--space-1);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    text-align: center;
}

.permission-matrix-name {
    padding-block: var(--space-2);
}

.permission-matrix-name::before {
    color: var(--color-text-muted);
}

.permission-matrix-cell {
    display: flex;
    justify-content: center;
}

.permission-matrix-both {
    grid-column: span 2;
    padding-block: var(--space-1);
    border-radius: var(--radius-pill);
    background-color: var(--color-surface-sunken);
}

.permission-matrix-none {
    color: var(--color-text-subtle);
}

.permission-popover-link {
    display: block;
    padding: var(--space-3) var(--space-4) var(--space-4) var(--space-5);
    border-top: 1px solid var(--color-divider);
    font-size: var(--font-size-sm);
}

/* On a phone the popover is a sheet along the bottom of the window. In the stacked rows, the portal access cell is two
   lines high, as its label takes two lines beside the switch and would otherwise run into the next cell's. */
@media (max-width: 640px) {
    .organizations-table.data-table-stack > tbody > tr > td.organization-access-all {
        min-height: calc(2 * var(--line-height-base) * var(--font-size-md) + 2 * var(--space-1));
    }

    .permission-edit {
        margin-inline-start: 0;
    }

    .permission-popover {
        position-area: none;
        position-try-fallbacks: none;
        inset: auto 0 0;
        width: 100%;
        max-width: none;
        max-height: 85vh;
        margin: 0;
        border-width: 1px 0 0;
        border-radius: var(--radius-xl) var(--radius-xl) 0 0;
        translate: 0 var(--space-4);
    }

    .permission-popover:popover-open {
        translate: 0 0;
    }

    @starting-style {
        .permission-popover:popover-open {
            translate: 0 var(--space-4);
        }
    }
}

/* The visually hidden texts of the rows (each button's address, the switch's label) are absolutely positioned: the table
   wrap must contain them, or those in a wide table's far columns widen the page, which then scrolls sideways. */
.organization-members .table-wrap,
.organization-region .table-wrap {
    position: relative;
}

/* A long heading (the portal access of all members) wraps rather than widen the table. */
.organizations-table th {
    padding-block: var(--space-2);
    white-space: normal;
}

/* When the control that had the focus is gone after a swap (a removed member's button), nav.js focuses the region for
   assistive technology; like the page heading, it gets no ring. */
.organization-region[tabindex="-1"]:focus {
    outline: none;
}

/* The search and the create form side by side, the search wider; one column on a narrow window. The cards are spaced by
   the grid's gap, and the wrapper keeps the same gap from what follows it. */
.organization-list-top {
    display: grid;
    grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
    gap: var(--space-5);
    margin-bottom: var(--space-5);
}

.organization-list-top > .card + .card {
    margin-top: 0;
}

@media (max-width: 899px) {
    .organization-list-top {
        grid-template-columns: minmax(0, 1fr);
    }
}

.organization-member-count {
    font-variant-numeric: tabular-nums;
}

/* The number of members after the people icon, in the muted color of secondary values. */
.member-count {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--color-text-muted);
}

.member-count > .icon {
    color: var(--color-secondary);
}

/* The organization's name leads its row in the text color; it shows itself a link under the pointer. */
.organization-link {
    color: var(--color-text);
    font-weight: var(--font-weight-medium);
    text-decoration: none;
}

.organization-link:hover,
.organization-link:focus-visible {
    color: var(--color-link);
    text-decoration: underline;
}

/* A member's address after an initials avatar in the address's tone. The initials are drawn from data-initials, so the
   cell's text stays the address. The time of the last sign-in is secondary, so muted. */
.member-identity {
    display: inline-flex;
    align-items: center;
    gap: var(--space-3);
    max-width: 100%;
    vertical-align: middle;
}

.member-identity > .avatar[data-initials]::before {
    content: attr(data-initials);
}

.member-email {
    min-width: 0;
    overflow-wrap: anywhere;
}

.member-last-signed-in {
    color: var(--color-text-muted);
}

/* The create form and the members without an organization side by side in columns of at least 360px, one column on a
   narrow window. The members card, a list of long addresses, takes two columns where two fit (744px of content, which the
   gutters and a classic scrollbar leave from a window 840px wide); in one column it must not span, as that would add a
   second one. */
.organization-cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 360px), 1fr));
    gap: var(--space-5);
    align-items: start;
    margin-top: var(--space-5);
}

.organization-cards > .card {
    min-width: 0;
}

.organization-cards > .card + .card {
    margin-top: 0;
}

@media (min-width: 840px) {
    .organization-cards > #unassigned-members {
        grid-column: span 2;
    }
}

/* The members without an organization: one form around the card, with the choice of organization once at the top, then an
   address per row between hairlines with its button at the end. */
.unassigned-list {
    margin: 0;
    padding: 0;
    border-top: 1px solid var(--color-divider);
    list-style: none;
}

.unassigned-member {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2) var(--space-4);
    padding: var(--space-3) var(--space-5);
}

.unassigned-member + .unassigned-member {
    border-top: 1px solid var(--color-divider);
}

.unassigned-email {
    min-width: 0;
    overflow-wrap: anywhere;
}

.unassigned-choice {
    max-width: 28rem;
    margin-bottom: 0;
}

.unassigned-form .card-body > * + * {
    margin-top: var(--space-3);
}

@media (max-width: 640px) {
    .unassigned-member {
        padding-inline: var(--space-4);
    }
}

/* The members table: a row's switch with its Save button in a small form of its own; the action buttons' forms sit in
   .row-actions and in the row's menu (see the table rules). */
.member-form {
    display: inline-flex;
    align-items: center;
    gap: var(--space-3);
    margin: 0;
    vertical-align: middle;
}

.member-actions {
    white-space: nowrap;
}

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

.my-organization-switch {
    margin-bottom: var(--space-5);
}

/* ---- Logs ---- */

/* The search card: the note on a row of its own, then From, To, Message and Type in one row from 1024px, two columns
   below and one on a phone, then the archive switch, with the button at the row's end from 1024px and on a row of its
   own below. Items line up at their bottom edge; the grid's gap spaces the rows, so the fields have no bottom margin. */
.logs-search-card {
    margin-bottom: var(--space-5);
}

.logs-search {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    align-items: end;
    gap: var(--space-4);
}

.logs-search .form-field {
    margin-bottom: 0;
}

.logs-dates-note {
    grid-column: 1 / -1;
    margin: 0;
}

/* The switch row is as tall as the button, so the two sit level when they share a row. */
.logs-archive-switch {
    grid-column: 1 / -1;
    min-height: var(--control-height);
}

/* Below 1024px the button takes the last row, at its end. */
.logs-search-actions {
    grid-column: 1 / -1;
    justify-content: flex-end;
}

@media (min-width: 1024px) {
    .logs-search {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 2fr) minmax(0, 1fr) auto;
    }

    /* Under the fields, leaving the last column to the button. */
    .logs-archive-switch {
        grid-column: 1 / -2;
    }

    .logs-search-actions {
        grid-column: auto;
    }
}

@media (max-width: 640px) {
    .logs-search {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* The native date fields take the input's look from .text-input; their calendar button gets the pointer. */
.logs-search input[type="date"] {
    min-width: 0;
}

.logs-search input[type="date"]::-webkit-calendar-picker-indicator {
    cursor: pointer;
}

/* The entries: rows in one flush card between hairlines. A long message wraps at its spaces and, failing that,
   anywhere, so a phone never scrolls sideways; line breaks in a message (a stack trace) are kept. */
.log-list {
    margin: 0;
    padding: 0;
    list-style: none;
}

/* One row per entry: type, message, time. The type column has a fixed width so the messages of all rows start at the
   same edge; type, message and time are centered vertically in the row. */
.log-entry {
    display: grid;
    grid-template-columns: 7rem minmax(0, 1fr) max-content;
    grid-template-areas: "type message time";
    align-items: center;
    gap: var(--space-1) var(--space-4);
    min-width: 0;
    padding: var(--space-3) var(--space-5);
}

.log-entry + .log-entry {
    border-top: 1px solid var(--color-divider);
}

.log-entry-type {
    grid-area: type;
    justify-self: start;
}

.log-entry-time {
    display: inline-flex;
    grid-area: time;
    align-items: center;
    gap: var(--space-1);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* The message, under the name of the method that wrote it. */
.log-entry-body {
    display: flex;
    flex-direction: column;
    grid-area: message;
    gap: var(--space-1);
    min-width: 0;
}

.log-entry-caller {
    display: inline-flex;
    align-items: center;
    align-self: flex-start;
    gap: var(--space-1);
    max-width: 100%;
    color: var(--color-text-muted);
    font-family: var(--font-mono);
    font-size: var(--font-size-sm);
}

.log-entry-caller-name {
    min-width: 0;
    overflow-wrap: anywhere;
}

.log-entry-message {
    margin: 0;
    font-family: var(--font-mono);
    font-size: var(--font-size-md);
    line-height: var(--line-height-base);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* On a phone the message takes its own line below the type and the time. */
@media (max-width: 640px) {
    .log-entry {
        grid-template-columns: max-content minmax(0, 1fr);
        grid-template-areas:
            "type time"
            "message message";
        row-gap: var(--space-2);
        padding-inline: var(--space-4);
    }

    .log-entry-time {
        justify-self: end;
    }
}

/* Below the entries: the page size at the start, the pager at the end; on a phone they wrap onto two lines. (The tables of
   members and of organizations page in their card's footer instead, .table-footer.) */
.logs-footer,
.notifications-footer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3) var(--space-4);
    margin-top: var(--space-4);
}

.logs-pager,
.notifications-pager {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-3);
    margin-left: auto;
}

#logs-page-info,
#notifications-page-info {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-variant-numeric: tabular-nums;
}

/* The next page's chevron follows its text. */
#logs-next::before,
#notifications-next::before {
    order: 1;
}

/* After a search or another page of results, nav.js may focus the region for assistive technology; like the page
   heading, it gets no ring. */
.logs-results[tabindex="-1"]:focus,
.notifications-results[tabindex="-1"]:focus {
    outline: none;
}

/* ---- Notifications ---- */

/* The search card: the text field and the button in one row, one column on a phone; the button lines up with the
   field's bottom edge. */
.notifications-search-card {
    margin-bottom: var(--space-5);
}

.notifications-search {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: end;
    gap: var(--space-4);
}

.notifications-search .form-field {
    margin-bottom: 0;
}

@media (max-width: 640px) {
    .notifications-search {
        grid-template-columns: minmax(0, 1fr);
    }

    .notifications-search-actions {
        justify-content: flex-end;
    }
}

/* The mails: rows in one flush card between hairlines. Long subjects and addresses wrap anywhere, so a phone never
   scrolls sideways. */
.notification-list {
    margin: 0;
    padding: 0;
    list-style: none;
}

/* One row per mail: status, what and to whom, then the buttons at the end. The status column has a fixed width so the
   subjects of all rows start at the same edge. */
.notification-entry {
    display: grid;
    grid-template-columns: 5.5rem minmax(0, 1fr) max-content;
    grid-template-areas: "status main actions";
    align-items: center;
    gap: var(--space-2) var(--space-4);
    min-width: 0;
    padding: var(--space-3) var(--space-5);
}

.notification-entry + .notification-entry {
    border-top: 1px solid var(--color-divider);
}

.notification-entry .notification-status {
    grid-area: status;
    justify-self: start;
}

.notification-entry-main {
    grid-area: main;
    min-width: 0;
}

.notification-entry-main p {
    margin: 0;
    overflow-wrap: anywhere;
}

.notification-entry-subject {
    font-weight: var(--font-weight-medium);
}

.notification-entry-addresses,
.notification-entry-meta {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

.notification-entry-addresses > span + span,
.notification-entry-meta > span + span {
    margin-inline-start: var(--space-4);
}

.notification-entry-meta {
    font-variant-numeric: tabular-nums;
}

.notification-entry-created {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
}

.notification-label {
    color: var(--color-text-muted);
    font-weight: var(--font-weight-medium);
}

.notification-entry-actions {
    display: flex;
    grid-area: actions;
    gap: var(--space-2);
}

/* On a phone the buttons take their own line below the mail. */
@media (max-width: 640px) {
    .notification-entry {
        grid-template-columns: minmax(0, 1fr);
        grid-template-areas:
            "status"
            "main"
            "actions";
        padding-inline: var(--space-4);
    }
}

/* The page of one mail: its buttons sit with the title, which is the subject and so wraps anywhere; a field's addresses
   are one to a line. */
.notification-title {
    overflow-wrap: anywhere;
}

.notification-detail-actions {
    display: flex;
    gap: var(--space-2);
}

.notification-fields {
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr);
    gap: var(--space-3) var(--space-5);
    margin: 0;
}

.notification-fields dt {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    line-height: calc(var(--font-size-md) * var(--line-height-base));
}

.notification-fields dd {
    margin: 0;
    overflow-wrap: anywhere;
}

.notification-address {
    display: block;
}

.notification-body-card {
    margin-top: var(--space-5);
}

/* The switch that turns the notifications on and off, above the rules, set off from them by a hairline. */
.notification-enabled {
    border-bottom: 1px solid var(--color-divider);
}

/* The rules: a list between hairlines (the first and last without padding on the outer side), each its name above one
   sentence, like the service account's scopes; the settings follow a hairline below. */
.notification-rule-list {
    margin: var(--space-4) 0 0;
    padding: 0;
    list-style: none;
}

.notification-rule-list li {
    padding: var(--space-2) 0;
    overflow-wrap: anywhere;
}

.notification-rule-list li + li {
    border-top: 1px solid var(--color-divider);
}

.notification-rule-list li:first-child {
    padding-top: 0;
}

.notification-rule-name,
.notification-rule-description {
    display: block;
}

.notification-rule-name {
    font-weight: var(--font-weight-medium);
}

.notification-rule-description {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

.notification-rules-settings {
    margin-top: var(--space-4);
    padding-top: var(--space-4);
    border-top: 1px solid var(--color-divider);
}

/* A notification's card: its German and English text side by side, read-only or as fields, one below the other when
   the card is narrow. */
.notification-template-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 360px), 1fr));
    gap: var(--space-5);
}

.notification-template {
    min-width: 0;
}

/* A notification's form: one language below the other, each with the card's full width, set off by a hairline. */
.notification-template-stack > .notification-template + .notification-template {
    margin-top: var(--space-5);
    padding-top: var(--space-5);
    border-top: 1px solid var(--color-divider);
}

.notification-template-subject {
    margin: 0 0 var(--space-2);
    font-weight: var(--font-weight-medium);
    overflow-wrap: anywhere;
}

.notification-placeholder-list {
    margin: var(--space-3) 0 0;
    padding-left: var(--space-5);
    font-size: var(--font-size-sm);
}

/* The body is plain text: spacing and line breaks are kept, and a long line wraps instead of scrolling sideways. */
.notification-body {
    margin: 0;
    font-family: var(--font-mono);
    font-size: var(--font-size-md);
    line-height: var(--line-height-base);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* ---- Content pages (About, Imprint, Privacy) ---- */

/* A document is one card (.document, Components) with the title on a hairline above the text. */
.document > h1 {
    margin-bottom: var(--space-5);
    padding-bottom: var(--space-4);
    border-bottom: 1px solid var(--color-divider);
}

/* Running text keeps a readable line length; tables and the storage list take the card's full width. */
.document > :is(p, ul, ol) {
    max-width: 72ch;
}

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

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

.document li::marker {
    color: var(--color-secondary);
}

/* Long German compounds, addresses and cookie names wrap anywhere rather than widen a phone's page. Not the headings:
   they break only between words and at their soft hyphens. */
.document :is(p, li, dt, dd, td, code) {
    overflow-wrap: anywhere;
}

/* The closing line (the imprint's dispute notice, the privacy policy's date) sits apart on a hairline. */
.document > .document-footnote {
    max-width: none;
    margin-top: var(--space-6);
    padding-top: var(--space-4);
    border-top: 1px solid var(--color-divider);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

.imprint-company {
    font-weight: var(--font-weight-semibold);
}

/* A table in a document sits in a hairline frame of its own, which rounds the header row's corners with it; the frame
   closes the last row. */
.document .table-wrap {
    margin-bottom: var(--space-5);
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-md);
}

.document .table-wrap > .data-table > tbody > tr:last-child > td {
    border-bottom: 0;
}

/* The About page's technology tables. A fixed layout with fixed widths lines up the columns of the three tables; the
   description takes the rest. The widths hold the longest name ("Microsoft.IdentityModel.Validators") and version
   ("OIDC 1.0") on one line with the cells' padding. */
.about-table {
    table-layout: fixed;
}

.about-table th:nth-child(1) {
    width: 18rem;
}

.about-table th:nth-child(2) {
    width: 8rem;
}

.about-table td {
    vertical-align: top;
}

.about-table td:nth-child(-n+2) {
    white-space: nowrap;
}

.about-table td:nth-child(1) {
    font-weight: var(--font-weight-medium);
}

.about-table td:nth-child(2) {
    color: var(--color-text-muted);
}

/* A version cell that lists several versions (ProseMirror's packages) wraps, so it does not widen its column. */
.about-table td.about-version-list {
    white-space: normal;
}

/* Below 900px each technology becomes a block: name and version on one line (the version drops below a name that
   leaves it no room), the description below. The header row stays for screen readers only. */
@media (max-width: 899px) {
    .about-table,
    .about-table > tbody {
        display: block;
    }

    .about-table > thead {
        position: absolute;
        width: 1px;
        height: 1px;
        margin: -1px;
        overflow: hidden;
        clip: rect(0, 0, 0, 0);
        white-space: nowrap;
    }

    .about-table > tbody > tr {
        display: flex;
        flex-wrap: wrap;
        align-items: baseline;
        gap: var(--space-1) var(--space-3);
        padding: var(--space-3) var(--space-4);
        border-bottom: 1px solid var(--color-divider);
    }

    .about-table > tbody > tr:last-child {
        border-bottom: 0;
    }

    .about-table > tbody > tr > td {
        display: block;
        min-width: 0;
        padding: 0;
        border: 0;
    }

    .about-table td:nth-child(1) {
        white-space: normal;
    }

    .about-table td:nth-child(2) {
        margin-left: auto;
        text-align: right;
    }

    .about-table td:nth-child(3) {
        flex: 1 0 100%;
        color: var(--color-text-muted);
    }
}

/* The privacy policy's inventory of cookies and storage keys: a framed list, one entry per name, the name (and where
   it is kept) beside its purpose on a wide window and above it on a phone. */
.storage-list {
    margin: 0 0 var(--space-5);
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-md);
}

.storage-entry {
    display: grid;
    grid-template-columns: minmax(0, 18rem) minmax(0, 1fr);
    gap: var(--space-1) var(--space-5);
    padding: var(--space-3) var(--space-4);
}

.storage-entry + .storage-entry {
    border-top: 1px solid var(--color-divider);
}

.storage-entry > dt {
    font-weight: var(--font-weight-medium);
}

.storage-entry > dd {
    margin: 0;
}

.storage-where {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-regular);
}

@media (max-width: 640px) {
    .storage-entry {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* ---- Home ---- */

/* The approval link for the IT department, right under the error banner it belongs to. */
.home-banner-hint {
    margin: 0 0 var(--space-5);
}

/* The signed-out hero: a deep blue card with white text and the white call to action, the text at the start and a
   decorative request card at the end. It clips the art's tilted sheets at its rounded edge. */
.home-hero {
    position: relative;
    isolation: isolate;
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 360px);
    align-items: center;
    gap: var(--space-7);
    overflow: hidden;
    margin-bottom: var(--space-6);
    padding: var(--space-7);
    border-radius: var(--radius-xl);
    background-color: var(--color-primary);
    background-image: var(--gradient-hero);
    box-shadow: var(--shadow-3);
    color: var(--color-text-on-hero);
}

.home-hero-text {
    min-width: 0;
}

/* The eyebrow: a short rule from white into the light blue above the title. Not --gradient-accent: it starts at the
   primary blue, which is the hero's own color behind it, so half the rule would vanish. */
.home-hero-text::before {
    content: "";
    display: block;
    width: var(--space-6);
    height: 3px;
    margin-bottom: var(--space-5);
    border-radius: var(--radius-pill);
    background-image: linear-gradient(90deg, var(--color-text-on-hero), var(--color-outline));
}

.home-hero h1 {
    margin: 0 0 var(--space-3);
    color: inherit;
}

.home-hero p {
    max-width: 34rem;
    margin: 0 0 var(--space-6);
    color: color-mix(in srgb, var(--color-text-on-hero) 85%, transparent);
}

/* White on the dark card: the page's blue ring would vanish into it. */
.home-hero :focus-visible {
    outline-color: var(--color-text-on-hero);
}

/* The art: three translucent white sheets stacked and slightly turned, the front one drawn as a request card (an
   avatar, title and text lines, a status pill). */
.home-hero-art {
    position: relative;
    height: 232px;
}

.hero-sheet {
    position: absolute;
    border: 1px solid color-mix(in srgb, var(--color-text-on-hero) 25%, transparent);
    border-radius: var(--radius-lg);
    background-color: color-mix(in srgb, var(--color-text-on-hero) 10%, transparent);
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
}

.hero-sheet-back {
    top: 0;
    right: 0;
    width: 70%;
    height: 56%;
    rotate: 7deg;
}

.hero-sheet-middle {
    top: 12%;
    right: 8%;
    width: 78%;
    height: 60%;
    rotate: -4deg;
    background-color: color-mix(in srgb, var(--color-text-on-hero) 12%, transparent);
}

.hero-sheet-front {
    bottom: var(--space-2);
    left: 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
    width: 86%;
    padding: var(--space-5);
    rotate: -1.5deg;
    background-color: color-mix(in srgb, var(--color-text-on-hero) 16%, transparent);
    box-shadow: var(--shadow-4);
}

.hero-sheet-head {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-bottom: var(--space-1);
}

.hero-dot {
    flex: none;
    width: 32px;
    height: 32px;
    border-radius: var(--radius-pill);
    background-color: color-mix(in srgb, var(--color-text-on-hero) 30%, transparent);
}

.hero-lines {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    gap: 6px;
}

.hero-bar {
    display: block;
    width: 100%;
    height: 8px;
    border-radius: var(--radius-pill);
    background-color: color-mix(in srgb, var(--color-text-on-hero) 30%, transparent);
}

.hero-bar-strong {
    width: 72%;
    height: 10px;
    background-color: color-mix(in srgb, var(--color-text-on-hero) 70%, transparent);
}

.hero-bar-short {
    width: 40%;
}

.hero-bar-long {
    width: 92%;
}

.hero-bar-medium {
    width: 62%;
}

.hero-bar-tiny {
    width: var(--space-7);
}

.hero-sheet-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: var(--space-1);
}

/* The status pill: a dot at its start, like a status chip. */
.hero-pill {
    position: relative;
    width: 76px;
    height: 20px;
    border-radius: var(--radius-pill);
    background-color: color-mix(in srgb, var(--color-text-on-hero) 22%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-text-on-hero) 30%, transparent);
}

.hero-pill::before {
    content: "";
    position: absolute;
    top: 7px;
    left: var(--space-2);
    width: 6px;
    height: 6px;
    border-radius: var(--radius-pill);
    background-color: color-mix(in srgb, var(--color-text-on-hero) 85%, transparent);
}

@media (max-width: 899px) {
    .home-hero {
        grid-template-columns: minmax(0, 1fr) minmax(0, 240px);
        gap: var(--space-6);
        padding: var(--space-6);
    }

    .home-hero-art {
        height: 200px;
    }

    .hero-sheet-front {
        width: 100%;
        padding: var(--space-4);
    }
}

/* A phone shows the text alone. */
@media (max-width: 640px) {
    .home-hero {
        grid-template-columns: minmax(0, 1fr);
        padding: var(--space-5);
    }

    .home-hero-art {
        display: none;
    }

    .home-hero p {
        margin-bottom: var(--space-5);
    }
}

/* Three feature cards in a row, one column on a phone; the grid's gap separates them, not the margin between cards. */
.home-features {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-5);
}

.home-features > .card + .card {
    margin-top: 0;
}

.home-feature-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    margin-bottom: var(--space-4);
    border-radius: var(--radius-md);
    background-image: var(--gradient-tile);
    box-shadow: inset 0 0 0 1px var(--color-outline-variant);
    color: var(--color-on-primary-container);
    font-size: var(--font-size-lg);
}

/* An h2, which gives it the card title's look. */
.home-feature-title {
    margin: 0 0 var(--space-2);
}

.home-feature > p {
    color: var(--color-text-muted);
}

@media (max-width: 640px) {
    .home-features {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--space-4);
    }
}

/* ---- Sign-in dialog ---- */

/* The head is shared by the steps: the logo on a tonal tile, centered, with the close button in the corner. */
.signin-head {
    position: relative;
    display: flex;
    justify-content: center;
    padding: var(--space-6) var(--space-5) 0;
}

.signin-close {
    position: absolute;
    top: var(--space-3);
    right: var(--space-3);
}

.signin-logo {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    border: 1px solid var(--color-outline-variant);
    border-radius: var(--radius-xl);
    background-image: var(--gradient-tile);
    box-shadow: var(--shadow-2);
}

/* Each step is a centered column: title, text, then full-width buttons one under the other. */
.signin-body {
    padding-top: var(--space-4);
    text-align: center;
}

.signin-title {
    margin: 0 0 var(--space-2);
    font-size: var(--font-size-xl);
}

.signin-text {
    margin: 0 0 var(--space-5);
    color: var(--color-text-muted);
}

/* The address the code went to, closer to the text above it than to the field below. */
.signin-address {
    margin: calc(-1 * var(--space-3)) 0 var(--space-5);
    overflow-wrap: anywhere;
}

.signin-field {
    margin-bottom: var(--space-5);
    text-align: left;
}

.signin-actions {
    display: grid;
    gap: var(--space-3);
}

/* "or" between the two ways in, on a hairline either side. */
.signin-divider {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin: 0;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

.signin-divider::before,
.signin-divider::after {
    content: "";
    flex: 1;
    height: 1px;
    background-color: var(--color-outline-variant);
}

.signin-actions + .signin-note {
    margin-top: var(--space-5);
}

.signin-note + .signin-note {
    margin-top: var(--space-2);
}

.signin-links {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--space-1) var(--space-2);
}

/* The code: large, centered and spaced out, so each digit reads on its own. The extra padding on the left balances the
   space the last digit leaves on the right. */
.signin-code-input {
    padding-inline: calc(var(--space-3) + var(--letter-spacing-code)) var(--space-3);
    font-size: var(--font-size-lg);
    font-variant-numeric: tabular-nums;
    font-weight: var(--font-weight-semibold);
    letter-spacing: var(--letter-spacing-code);
    text-align: center;
}

/* A field with a glyph at its start (the sign-in's address): the glyph sits over the input's left padding and takes the
   accent while the field has the focus. */
.input-icon-wrap {
    position: relative;
}

.input-icon {
    position: absolute;
    top: 50%;
    left: var(--space-3);
    translate: 0 -50%;
    color: var(--color-text-subtle);
    font-size: var(--font-size-lg);
    pointer-events: none;
    transition: color var(--duration-fast) var(--ease-standard);
}

.input-icon-wrap:focus-within .input-icon {
    color: var(--color-primary);
}

.input-icon-wrap .text-input {
    padding-left: calc(var(--space-3) + var(--font-size-lg) + var(--space-2));
}

/* ---- Admin consent ---- */

/* The result of an administrator's approval: a centered card with a shield in a tonal circle, green or red, over the
   title and the message. */
.consent-result {
    max-width: 520px;
    margin-inline: auto;
    padding: var(--space-7) var(--space-6);
    text-align: center;
}

.consent-result-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    margin-bottom: var(--space-4);
    border-radius: var(--radius-pill);
    font-size: var(--font-size-xl);
}

.consent-result-success .consent-result-icon {
    background-color: var(--color-success-container);
    box-shadow: inset 0 0 0 1px var(--color-success-outline);
    color: var(--color-success);
}

.consent-result-failure .consent-result-icon {
    background-color: var(--color-danger-container);
    box-shadow: inset 0 0 0 1px var(--color-danger-outline);
    color: var(--color-danger);
}

.consent-result h1 {
    margin: 0 0 var(--space-2);
}

.consent-result-text {
    margin: 0 0 var(--space-5);
    color: var(--color-text-muted);
}

.consent-result .banner {
    margin: var(--space-4) 0 var(--space-5);
    text-align: left;
}

/* Microsoft's error code: white on the danger container, and it wraps rather than widen a phone's card. */
.consent-result .banner code {
    background-color: var(--color-surface);
    box-shadow: inset 0 0 0 1px var(--color-danger-outline);
    overflow-wrap: anywhere;
}

.consent-result-actions {
    margin: 0;
}

@media (max-width: 640px) {
    .consent-result {
        padding: var(--space-6) var(--space-4);
    }
}

/* ===================================================================================================================
   Motion
   =================================================================================================================== */

/* Content unfolding below its trigger (the request detail's "Show details"). */
@keyframes fade-in-down {
    from {
        opacity: 0;
        translate: 0 -8px;
    }

    to {
        opacity: 1;
        translate: 0 0;
    }
}

@keyframes fade-in {
    from {
        opacity: 0;
    }

    to {
        opacity: 1;
    }
}

/* A sheet coming down from under the app bar. */
@keyframes slide-down {
    from {
        opacity: 0;
        translate: 0 calc(-1 * var(--space-4));
    }

    to {
        opacity: 1;
        translate: 0 0;
    }
}

/* Motion off when the visitor asks for it: every state then shows at once, as no style here waits for an animation's
   end. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after, ::backdrop {
        animation: none !important;
        transition: none !important;
    }

    /* A rule of its own: a browser without this pseudo-element would drop the whole list above. */
    .send-progress progress::-webkit-progress-value {
        transition: none !important;
    }

    /* Likewise: * does not match this pseudo-element (the "Show details" box's height transition). */
    ::details-content {
        transition: none !important;
    }

    /* And these: nav.js starts no view transition for a visitor who asks for reduced motion, and this keeps one that
       another script starts from fading. */
    ::view-transition-group(*),
    ::view-transition-image-pair(*),
    ::view-transition-old(*),
    ::view-transition-new(*) {
        animation: none !important;
    }
}
