:root {
    --brand-primary-text-colour: #333333;
    --bg-secondary-colour: white;

    /* Brand-derived text colours. --brand-ink is the brand colour toned for
       body-adjacent text (page titles, card headings); --brand-accent-ink is
       the stronger accent for active tabs/links. Both are remixed toward
       white in dark mode (see dark-mode.css), so views should use these vars
       for brand-coloured TEXT rather than mixing inline. */
    --brand-ink: color-mix(in srgb, var(--brand-primary-colour) 70%, #374151);
    --brand-accent-ink: var(--brand-primary-colour);
}

html {
    background-color: var(--page-bg-colour);
    background-image: var(--page-bg-image);
    color: var(--page-text-colour, #333333);
    font-family: var(--font-family);
    font-size: var(--font-size);
    font-weight: var(--font-weight);
}

body {
    margin: 0;
    padding: 0;
    overflow-x: hidden;
    flex-direction: row;
}

textarea {
    min-height: 110px;
}

/* The base heading treatment, driven by the Headings → General brand settings.
   Size, weight and tracking ship blank and are omitted from the branding
   stylesheet, so the fallbacks here are what applies — `inherit` is exactly
   what Tailwind's preflight resets headings to, which is what an unstyled
   heading did before these settings existed. Every other heading group
   (page / widget / content) falls back through these, so setting one here
   moves them all. */
h1, h2, h3, h4, h5, h6 {
    color: var(--header-colour);
    font-family: var(--header-font-family, var(--font-family));
    font-size: var(--header-font-size, inherit);
    font-weight: var(--header-font-weight, inherit);
    text-transform: var(--header-text-transform, none);
    letter-spacing: var(--header-letter-spacing, normal);
}

/* ── Heading variants ───────────────────────────────────────────────────────
   One rule per Headings brand-setting group. Markup gets these through
   <x-ui::heading variant="…"> rather than by naming the class — the one
   exception is the stat tile's label, which is a <span> inside a value tile
   rather than a heading element, so it takes the widget treatment by selector.

   The doubled class (.ui-heading.ui-heading--x) is deliberate: legacy rules
   like `.widget h2` are a class plus an element, which would otherwise
   out-specify a single class and silently override the whole variant.

   Each variant falls through to the General group before landing on a
   literal, so setting a general font, weight, case or tracking moves every
   heading. The sizes do NOT fall through: a page title, a widget title and a
   card title are different sizes on purpose, which is why each group carries
   its own size. The literals are what these headings rendered at before the
   settings existed. */

.ui-heading.ui-heading--general {
    font-family: var(--header-font-family, var(--font-family));
    font-size: var(--header-font-size, inherit);
    font-weight: var(--header-font-weight, inherit);
    text-transform: var(--header-text-transform, none);
    letter-spacing: var(--header-letter-spacing, normal);
    color: var(--header-colour);
}

.ui-heading.ui-heading--page {
    font-family: var(--page-heading-font-family, var(--header-font-family, var(--font-family)));
    font-size: var(--page-heading-size, 1.5rem);
    font-weight: var(--page-heading-weight, var(--header-font-weight, 700));
    text-transform: var(--page-heading-text-transform, var(--header-text-transform, none));
    letter-spacing: var(--page-heading-letter-spacing, var(--header-letter-spacing, normal));
    /* Falls through to the General heading colour like every other variant —
       a literal here left a plain page title (the dashboards greeting) in
       near-black on the dark theme while the crumbed titles, which colour
       their name themselves, read fine. */
    color: var(--page-heading-colour, var(--header-colour, #1a1a1a));
}

.ui-heading.ui-heading--section {
    font-family: var(--section-heading-font-family, var(--header-font-family, var(--font-family)));
    font-size: var(--section-heading-size, 1.125rem);
    font-weight: var(--section-heading-weight, var(--header-font-weight, 500));
    text-transform: var(--section-heading-text-transform, var(--header-text-transform, none));
    letter-spacing: var(--section-heading-letter-spacing, var(--header-letter-spacing, normal));
    color: var(--section-heading-colour, var(--header-colour));
}

.ui-heading.ui-heading--widget,
.dashboard-stat--card .ds-label {
    font-family: var(--widget-heading-font-family, var(--header-font-family, var(--font-family)));
    font-size: var(--widget-heading-size, 0.95rem);
    font-weight: var(--widget-heading-weight, var(--header-font-weight, 600));
    text-transform: var(--widget-heading-text-transform, var(--header-text-transform, none));
    letter-spacing: var(--widget-heading-letter-spacing, var(--header-letter-spacing, normal));
    color: var(--widget-heading-colour, #6b7280);
}

.ui-heading.ui-heading--content {
    font-family: var(--content-heading-font-family, var(--header-font-family, var(--font-family)));
    font-size: var(--content-heading-size, 1.125rem);
    font-weight: var(--content-heading-weight, var(--header-font-weight, 500));
    text-transform: var(--content-heading-text-transform, var(--header-text-transform, none));
    letter-spacing: var(--content-heading-letter-spacing, var(--header-letter-spacing, normal));
    color: var(--content-heading-colour, var(--header-colour));
}

.hide {
    display: none;
}

.font-sans {
    font-family: var(--font-family);
}

.p-4 {
    padding: 1rem;
}

.p-8 {
    padding: 2rem;
}

.px-8 {
    padding-right: 2rem;
    padding-left: 2rem;
}

.pb-8 {
    padding-bottom: 2rem;
}

.pt-8 {
    padding-top: 2rem;
}

.gap-2 {
    gap: 0.5rem;
}

.flex-none {
    flex: 0 0 auto
}

#toggle-sidebar a {
    height: 32px;
}

.toggle-btn {
    background: none;
    border: none;
    cursor: pointer;
    transition: transform 0.3s ease;
}

.sidebar.collapsed .toggle-btn .sidebar-item-icon {
    transform: rotate(180deg);
}

/* Row padding comes from the ui::sidebar-item-spacing (vertical) and
   ui::sidebar-item-inset (horizontal) brand settings rather than Tailwind
   utilities on the component, so a site can tighten the menu without a
   template change. The fallbacks are the old `py-2 px-5`. */
.sidebar-item a {
    border-left: 3px solid transparent;
    color: var(--sidebar-text-colour, #374151);
    border-radius: 8px;
    margin-left: var(--sidebar-item-gutter, 10px);
    margin-right: var(--sidebar-item-gutter, 10px);
    font-size: 14.5px;
    padding: var(--sidebar-item-spacing, 0.5rem) var(--sidebar-item-inset, 1.25rem);
}

/* Icon size follows the ui::sidebar-icon-size brand setting; the fallback
   is the old `h-4 w-4` on the icon wrapper. */
.sidebar-item-icon {
    height: var(--sidebar-icon-size, 1rem);
    width: var(--sidebar-icon-size, 1rem);
}

.sidebar-item-icon svg {
    height: 100%;
    width: 100%;
}

/* Uppercase group heading above a block of nav items ("Manage"). Sized by
   ui::sidebar-section-label-size and inset to match the rows beneath it;
   fallbacks are the old `text-xs`, `px-5` and `pt-4`. The gutter is added
   on to the inset so the heading lines up with the row icons rather than
   sitting to their left — it reads the same setting the rows do, so widening
   one does not break the alignment.

   Only the TOP spacing is shared. The two elements carrying this class keep
   their own bottom spacing (`pb-1` on the system label, `pb-2` on a group
   label) in the Blade, because this sheet loads after Tailwind and one
   padding-bottom here would override both. */
.sidebar-section-label {
    font-size: var(--sidebar-section-label-size, 0.75rem);
    padding-left: calc(var(--sidebar-item-inset, 1.25rem) + var(--sidebar-item-gutter, 10px));
    padding-right: var(--sidebar-item-inset, 1.25rem);
    padding-top: var(--sidebar-section-label-spacing, 1rem);
}

/* A labelled group of nav items (see ui::navigation.sidebar-group). The
   heading reuses .sidebar-section-label above, so only the collapse
   affordance and the indent of the rows inside need styling here. */
.sidebar-group-label button {
    /* The global `button` rule rounds and pads buttons; the group heading
       must sit flush with .sidebar-section-label's own inset instead. */
    border-radius: 0 !important;
    padding: 0;
    background: none;
}

.sidebar-group-icon {
    height: var(--sidebar-icon-size, 1rem);
    width: var(--sidebar-icon-size, 1rem);
}

.sidebar-group-icon svg {
    height: 100%;
    width: 100%;
}

/* Rows inside a group are indented past the heading so the bucket reads
   as containing them. Indent is relative to the shared item inset so it
   tracks the ui::sidebar-item-inset brand setting. */
.sidebar-group-items .sidebar-item a,
.sidebar-group-items .sidebar-item button {
    padding-left: calc(var(--sidebar-item-inset, 1.25rem) + 1.25rem);
}

.sidebar-group--collapsed .sidebar-group-chevron {
    transform: rotate(-90deg);
}

/* Feedback/Support are <button>s; the global `button` rule rounds them via
   var(--buttons-border-radius) !important, but in the sidebar they must stay
   square to align with the <a> items, so override it back to 0. */
.sidebar-item button {
    border-radius: 0 !important;
    padding: var(--sidebar-item-spacing, 0.5rem) var(--sidebar-item-inset, 1.25rem);
}

.sidebar-item a:hover,
/* Feedback/Support are <button>s (they open modals) rather than links, so
   they need the same hover lift as the <a> items alongside them. */
.sidebar-item button:hover {
    /* Keep the resting text colour; just a translucent lift, a touch
       stronger than the workspace pill tint, and lighter than the solid
       brand wash of the selected state. */
    background-color: color-mix(in srgb, var(--sidebar-text-colour, #374151) 14%, transparent);
}

.sidebar-item.selected a {
    background-color: var(--sidebar-selected-bg, white);
    color: var(--sidebar-selected-text, var(--brand-primary-colour));
    position: relative;
}

/* The accent rule down the left edge, drawn as a square bar rather than as a
   border on the row.

   A border follows the row's 8px radius, so its ends bow inwards and it reads
   as part of the rounded pill instead of a straight edge against it. An
   absolutely positioned child is not clipped by the parent's radius, so it
   keeps square ends. It sits at left: -3px — over the transparent border every
   row already carries — so it lands exactly on the row's edge without taking
   width from the content or moving the label.

   Inset top and bottom by the row's own 8px radius, so the bar covers only the
   straight part of that edge and stops short of where the corners start to
   curve. That is what lets the row stay rounded while the bar stays square:
   the two never meet.

   Unset means transparent, not the brand colour, and the row is the same size
   either way: nothing shifts as the selection moves. */
.sidebar-item.selected a::before {
    content: "";
    position: absolute;
    left: -3px;
    top: 8px;
    bottom: 8px;
    width: 3px;
    background: var(--sidebar-selected-border, transparent);
}

/* The icon inherits the row's text colour unless ui::sidebar-selected-icon
   gives it one of its own — the sprite draws with currentColor, so setting
   `color` on the wrapper is enough. */
.sidebar-item.selected .sidebar-item-icon {
    color: var(--sidebar-selected-icon, var(--brand-primary-colour));
}

.sidebar {
    color: var(--sidebar-text-colour, #374151);
    min-width: var(--sidebar-width, max-content);
    max-width: var(--sidebar-width, max-content);
    transition: width 0.3s ease;
    overflow-y: auto;
    overflow-x: hidden;
    position: relative;
}

/* Optional decorative pattern behind the sidebar, chosen via the
   ui::sidebar-pattern brand setting (default off). Each pattern is a
   .sidebar-pattern--{value}::after overlay; the shared rules below set
   up the overlay layer and keep nav content above it. Add a new
   pattern by adding an option to the SidebarPattern setting + a
   matching ::after rule here. */
.sidebar.sidebar-pattern::after {
    content: '';
    position: absolute;
    inset: 0;
    min-height: 100%;
    pointer-events: none;
    z-index: 0;
}
.sidebar.sidebar-pattern > * {
    position: relative;
    z-index: 1;
}

/* Pattern: diagonal lines. Tint adapts to the sidebar text colour, so
   it reads on both light and dark sidebars. */
.sidebar.sidebar-pattern--diagonal-lines::after {
    background-image: repeating-linear-gradient(
        135deg,
        color-mix(in srgb, var(--sidebar-text-colour, #374151) 8%, transparent) 0 2px,
        transparent 2px 16px
    );
}

/* Label type comes from the ui::sidebar-font-size / sidebar-font-weight
   brand settings; the fallbacks are the old `text-base font-normal`. */
.sidebar-item-label {
    font-size: var(--sidebar-font-size, 1rem);
    font-weight: var(--sidebar-font-weight, 400);
    min-width: 120px;
    visibility: visible;
    opacity: 1;
    transition: opacity 0.3s ease, visibility 0s 0s;
}

.sidebar.collapsed .sidebar-item-label {
    display: none;
    transition: opacity 0.3s ease, visibility 0s 0.3s;
}

.sidebar.collapsed:hover .sidebar-item-label {
    display: unset;
}

.sidebar nav ul {
    list-style: none;
    padding: 0;
    margin: 0;
}

/* The top-level workspace navigation list. Was `pt-2` on the element: the gap
   between the logo row and the first navigation item. Scoped to this one list
   rather than `.sidebar nav ul`, because the domain lists and the nested
   .sidebar-group-items carry no top padding and must not gain one. */
.sidebar-nav-list {
    padding-top: var(--sidebar-nav-padding-y, 0.5rem);
}

/* The sidebar's logo row. Was `px-5` on the element; the inset lines the logo
   up with the navigation rows beneath it. The vertical padding is the other
   half of that pair: 1rem round the 2rem logo is 64px, under the 65px minimum
   the row always had, so the default draws as before and a larger value
   grows the row. */
.sidebar-header {
    padding: var(--sidebar-header-padding-y, 1rem) var(--sidebar-header-inset, 1.25rem);
    min-height: 65px;
}

.sidebar nav .sidebar-item:hover {
    background-color: transparent;
}

/* ─────────────────────────────────────────────────────────────────
   Page chrome + scroll model.

   Goal: exactly ONE scrollable container in the page chrome chain,
   below the static header. The scroll lives on .main-sections so
   the page header (hero or in-page) and any domain sub-nav stay
   visible while content scrolls underneath. Sidebars are full-
   height columns that don't scroll with the content.

   DOM produced by layouts/page.blade.php + pages/sections.blade.php:

     .page-container (full-height flex column, no scroll)
       └── .main-panel (flex column, fills, no scroll)
             ├── .page-hero-banner  OR  .page-header   (static)
             ├── .page-nav         (domain sub-nav, optional, static)
             └── .page-content     (flex row when sidebars present,
                                    single column otherwise)
                   ├── aside.main-sidebar--left   (full height, optional)
                   ├── .main-sections             (THE scroll container)
                   │     ├── @stack('page-chrome')  (banners, alerts,
                   │     │                           notifications)
                   │     └── [sections]
                   └── aside.main-sidebar--right  (full height, optional)

   Why one scroll: a single source of truth for "where does the
   user's wheel event go" eliminates the common bug where two
   nested overflow regions trap scroll in the inner one. .main-
   sections is the only `overflow-y: auto` in this chain; everything
   above it caps its own height with `overflow: hidden` + flex sizing
   so children can't blow past their parent.
   ───────────────────────────────────────────────────────────────── */

/* Outer chrome — no scroll. The full-page flex column that holds
   the topbar, tenant banners, and .main-panel.

   overflow: hidden bounds the children's height. Tooltips / dropdowns
   from descendants that need to escape their parent's box use
   z-index + position: relative against later siblings (e.g. the
   .page-nav vs .page-content stacking); they don't extend outside
   .page-container's actual rectangle so this overflow doesn't clip
   them. Scroll lives on .main-sections only. */
.page-container {
    display: flex;
    flex-direction: column;
    flex-grow: 1;
    /* The page-container owns the vertical scroll by default, so the
       scrollbar sits at the screen edge while content stays centered
       (max-width on .main-panel). Pages that render an aside (sticky
       right rail, e.g. the code-generator chat) opt back into the
       inner-scroll chain via the :has() overrides below. */
    overflow-y: auto;
    min-height: 0;
}

/* Aside-bearing pages keep the original bounded inner-scroll chain so
   sticky rails / composers stay pinned on-screen. */
.page-container:has(.main-sidebar) {
    overflow: hidden;
}

/* Main panel — holds the page header (hero or in-page), optional
   domain sub-nav, and the scrollable .page-content. Flex column;
   itself does not scroll.

   overflow: hidden is REQUIRED here to bound the children's height —
   without it, a tall .page-content (with sticky asides like the
   iterate chat rail) grows past the viewport instead of constraining
   itself to .main-panel's flex height. That would push the rail's
   composer offscreen.

   The domain tab-bar dropdown stacking is handled by .page-nav
   (z-index: 50) + .page-content (z-index: 0) so dropdowns paint
   above content even though .main-panel clips at its own bounds —
   the dropdown's actual rectangle stays within .main-panel's box. */
.main-panel {
    flex: 1 0 auto;
    display: flex;
    flex-direction: column;
    width: 100%;
    /* Content typography brand settings. Both ship blank and are omitted
       from the branding stylesheet, so the fallbacks are what a site gets
       until it opts in: the site-wide font size, and the 1.5 leading the
       page inherits from the preflight today. */
    font-size: var(--content-font-size, var(--font-size));
    line-height: var(--content-line-height, 1.5);
    max-width: var(--content-max-width, 1280px);
    /* Set by the content-alignment brand setting via the generated
       branding stylesheet — `0 auto` centres, `0 auto 0 0` left-aligns. */
    margin: var(--content-margin, 0 auto);
    transition: margin-left 0.3s ease, max-width 0.3s ease;
}

/* When an aside is present, the panel is height-bounded again so its
   inner .page-content/.main-sections own the scroll (sticky rails). */
.page-container:has(.main-sidebar) .main-panel {
    flex: 1;
    min-height: 0;
    overflow: hidden;
}

/* The page header row — title, description and actions. Was `px-8 pt-6` on
   the element; the utilities were dropped so the inset can follow the Page
   Spacing brand settings, with the old literals kept as the var() fallbacks. */
.page-header {
    padding: var(--page-header-padding-top, 1.5rem)
             var(--page-header-padding-x, 2rem)
             0;
}

/* The sub-heading under the title — the page description, plain or enriched.
   It used to carry its own px-8, which drifted from the title as soon as the
   header inset was changed; now it reads the same Page Header Padding (Sides)
   setting, plus the Page → Spacing gap above it (0 by default, as it was). */
.page-description {
    padding: var(--page-header-description-gap, 0)
             var(--page-header-padding-x, 2rem)
             0;
}

/* Domain sub-navigation (e.g. tabs across a domain landing page).
   Static — does not scroll with content.

   Class name is .page-domain-nav (NOT .page-nav, which is a
   pre-existing class in this stylesheet with overflow: hidden
   for a different horizontally-scrollable nav strip). The unique
   name is what lets the inner topbar's overflow "..." dropdown
   paint correctly — overflow defaults to visible. */
.page-domain-nav {
    flex-shrink: 0;
    margin-top: 1.5rem;
    /* Same inset as the content column below it (Page -> Page Spacing ->
       Content Padding (Sides)), so the first tab lines up with the first
       section card. It used to be a hard-coded 30px margin on the bar
       itself, which ignored the setting entirely. */
    padding: 0 var(--page-content-padding-x, 2rem);
}

/* The flex container that holds the sidebar columns + main sections.
   Fills the remaining vertical space below the header chrome.
   - With sidebars present: becomes a flex row (sidebars + main).
   - Without sidebars: single column, .main-sections fills.
   Itself does not scroll — overflow lives on .main-sections. */
.page-content {
    flex: 1 0 auto;
    display: flex;
    flex-direction: row;
}

/* Aside-bearing pages: bound the row + delegate scroll to .main-sections. */
.page-container:has(.main-sidebar) .page-content {
    flex: 1;
    min-height: 0;
    overflow: hidden;
}

/* The page footer band — copyright line, policy links, small print.
   Sits inside #main-panel BELOW .page-content, so it stays put while
   .main-sections scrolls, and repeats that column's horizontal padding
   so its rule lines up with the content above it. Rendered only when
   the page-footer brand setting is enabled (see the page-footer
   component); flex-shrink: 0 keeps it off the content's height budget.
   Colours come from the content brand variables, which dark-mode.css
   remaps under html.dark — so no dark-specific rules are needed. */
.page-footer {
    flex-shrink: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem 1.5rem;
    padding: var(--page-footer-padding-y, 1rem) var(--page-footer-padding-x, 2rem);
    /* The rule follows the Page Footer brand setting; blank is not emitted,
       so the fallback is the content border the footer always read. */
    border-top: 1px solid var(--page-footer-border-colour, var(--content-border-colour, #e5e7eb));
    font-size: 0.75rem;
    line-height: 1.25rem;
    color: color-mix(in srgb, var(--content-text-colour, #333333) 65%, transparent);
}

.page-footer__left,
.page-footer__right {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem 1.5rem;
}

.page-footer__link {
    text-decoration: underline;
}

.page-footer__link:hover {
    color: var(--content-text-colour, #333333);
}

/* The auth-page footer — same content as .page-footer (copyright,
   policy links, note) but rendered on the pre-auth pages, which are
   always light and don't carry the content brand variables, so colours
   are fixed greys except on the branded split panel where the text
   colour follows the panel like the privacy-agreement line above it.
   Variants: --full-width spans the page bottom below both split panels;
   --left-panel flows inside the branded panel; --card sits centred
   under the form card (single-column mode and the mobile fallback). */
.auth-footer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem 1.5rem;
    font-size: 0.75rem;
    line-height: 1.25rem;
}

.auth-footer__left,
.auth-footer__right {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem 1.5rem;
}

.auth-footer__link {
    text-decoration: underline;
}

.auth-footer__link:hover {
    text-decoration: none;
}

.auth-footer--full-width {
    flex-shrink: 0;
    justify-content: space-between;
    padding: 1rem 2rem;
    border-top: 1px solid var(--page-footer-border-colour, #e5e7eb);
    color: #6b7280;
}

.auth-footer--full-width .auth-footer__link:hover {
    color: #374151;
}

.auth-footer--card {
    justify-content: center;
    margin-top: 2rem;
    color: #9ca3af;
}

.auth-footer--card .auth-footer__left,
.auth-footer--card .auth-footer__right {
    justify-content: center;
}

.auth-footer--card .auth-footer__link:hover {
    color: #374151;
}

.auth-footer--left-panel {
    position: relative;
    z-index: 10;
    justify-content: flex-start;
    margin-top: 1rem;
    color: var(--text-primary-colour);
    opacity: 0.6;
}

/* In the panel variants the note gets its own line under the copyright
   and links (the container already wraps, so a full basis is enough);
   the full-width band keeps them on one line, copyright left / note
   right. */
.auth-footer--left-panel .auth-footer__right,
.auth-footer--right-panel .auth-footer__right {
    flex-basis: 100%;
}

.auth-footer--right-panel {
    justify-content: center;
    margin-top: 2rem;
    color: #9ca3af;
}

.auth-footer--right-panel .auth-footer__left,
.auth-footer--right-panel .auth-footer__right {
    justify-content: center;
}

.auth-footer--right-panel .auth-footer__link:hover {
    color: #374151;
}

/* The scrollable main content column. Padding gives content
   breathing room from the column edges; reduced horizontally when
   an aside is adjacent (each aside provides its own inner padding
   so we don't need to double up).

   Top padding (1rem) lifts the first widget off the page header /
   tab-bar border so content doesn't touch the chrome. Less than
   bottom (1.5rem) because the bottom benefits from extra breathing
   room when scrolled to the end. */
.main-sections {
    flex: 1;
    min-width: 0;
    /* Spacing brand settings (Page -> Page Spacing). Each ships the literal
       below as its default, and it is repeated as the var() fallback so the
       column is unchanged on a deploy where the settings import has not run
       yet. */
    padding: var(--page-content-padding-top, 1rem)
             var(--page-content-padding-x, 2rem)
             var(--page-content-padding-bottom, 1.5rem);
    display: flex;
    flex-direction: column;
    gap: var(--page-content-gap, 1rem);
}

/* Aside-bearing pages: .main-sections owns the vertical scroll. */
.page-container:has(.main-sidebar) .main-sections {
    min-height: 0;
    overflow-y: auto;
}
/* When a left aside sits to the LEFT of .main-sections, drop the
   main-sections' left padding (aside has padding-right of its own). */
.page-content:has(> .main-sidebar--left) > .main-sections {
    padding-left: 1rem;
}
.page-content:has(> .main-sidebar--right) > .main-sections {
    padding-right: 1rem;
}

/* Sidebars — full-height static columns. Each aside is a flex
   column with its own (optional) internal scroll. The framework
   default is 30vw wide; --right modifier narrows to 420px.

   Padding mirrors .main-sections so sidebar content has the same
   breathing room as the main column and doesn't kiss the screen
   edge or the scroll gutter. Asymmetric horizontal padding (more
   on the OUTSIDE) keeps the visual balance — left aside has more
   padding-left, right aside has more padding-right.

   Top padding (1rem) matches .main-sections so all three columns
   start at the same baseline below the page header / tab-bar
   border. */
.main-sidebar {
    flex: 0 0 30vw;
    min-height: 0;
    align-self: stretch;
    display: flex;
    flex-direction: column;
    overflow-y: auto;
    padding: 1rem 1rem 1.5rem;
    /* Matches .main-sections. Without it two stacked panels in a rail butt
       together and read as one merged slab rather than two cards. The rail
       follows the page's section gap so the two columns keep one rhythm; its
       own padding stays literal, being the gutter BETWEEN columns rather than
       the page's outer inset. */
    gap: var(--page-content-gap, 1rem);
}
.main-sidebar--left  { padding-left: 2rem; }
.main-sidebar--right { padding-right: 2rem; }

@media (min-width: 640px) {
    /* Order the flex children: left aside → main-sections → right aside. */
    .main-sections       { order: 1; }

    /* Both rails settle at the same width. A rail carries reference panels
       beside the content, and 30vw gave it well over half a wide screen —
       enough that the content it supports had less room than the support.
       The section-nav rail keeps its own narrower rule below. */
    .main-sidebar--left {
        order: 0;
        flex-basis: 420px;
    }
    .main-sidebar--right {
        order: 2;
        flex-basis: 420px;
    }

    /* Legacy: a .main-sidebar with no left/right modifier (used by
       any consumer that bypasses the sections template and writes
       the aside directly into a custom view). Kept floating left to
       match the historical behaviour these consumers depend on. */
    .main-sidebar:not(.main-sidebar--left):not(.main-sidebar--right) {
        float: left;
        margin-right: 2rem;
    }
}

/* Mobile: collapse to a single vertical stack. Asides become full-
   width blocks above/below the sections. The single-scroll model
   still holds — only .main-sections scrolls. */
@media (max-width: 639px) {
    .page-content {
        flex-direction: column;
        overflow-y: auto;
    }
    .main-sidebar {
        flex: 0 0 auto;
        margin-bottom: 1rem;
        overflow-y: visible;
    }
    .main-sections {
        overflow-y: visible;
    }
}

/* Section navigation rail (sections/section-nav.blade.php) — a search box
   plus jump list indexing the page's main sections.

   The framework's left aside is 30vw, sized for content panels; a jump
   list only needs a column. When the aside holds NOTHING BUT the rail we
   narrow it so the section cards keep the width they had before the rail
   was added. Any other left-aside page is untouched. */
.main-sidebar--left:has(> .section-nav-section:only-child) {
    flex: 0 0 260px;
}

/* Keep the search box in reach while a long tree scrolls under it. */
.section-nav-search {
    position: sticky;
    top: 0;
    z-index: 1;
}

.section-nav-list {
    padding: 0.25rem 0 0.5rem;
}

/* Group toggle — full-width row: chevron, label, and (via the tree line
   on the items) the group it opens. */
.section-nav-heading {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    width: 100%;
    padding: 0.625rem var(--section-nav-item-inset, 0.75rem) 0.375rem;
    background: none;
    border: 0;
    cursor: pointer;
    text-align: left;
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: #9ca3af;
}

.section-nav-heading:hover {
    color: #4b5563;
}

.section-nav-chevron {
    width: 0.875rem;
    height: 0.875rem;
    flex-shrink: 0;
    transition: transform 0.15s ease;
}

.section-nav-heading.is-collapsed .section-nav-chevron {
    transform: rotate(-90deg);
}

/* The group holding the section you're currently viewing — readable even
   when the group is collapsed. */
.section-nav-heading.is-active-group {
    color: var(--brand-primary-colour);
}

/* Items sit indented under their heading, joined by a hairline that reads
   as the tree's spine. */
.section-nav-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    margin-left: 1.125rem;
    padding: var(--section-nav-item-spacing, 0.375rem) var(--section-nav-item-inset, 0.75rem);
    font-size: 0.8125rem;
    line-height: 1.3;
    color: #374151;
    border-left: 2px solid #e5e7eb;
}

.section-nav-item:hover {
    background-color: rgba(0, 0, 0, 0.03);
    color: #111827;
}

/* Collapsed group — its items are removed from the flow entirely. */
.section-nav-item--collapsed {
    display: none;
}

/* Current section — the rail highlights whichever section the content
   column is scrolled to (and whichever item was clicked). */
.section-nav-item.is-active {
    border-left-color: var(--brand-primary-colour);
    color: var(--brand-primary-colour);
    font-weight: 500;
    background-color: color-mix(in srgb, var(--brand-primary-colour) 6%, transparent);
}

.section-nav-item-label {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* How many settings the section holds. */
.section-nav-count {
    flex-shrink: 0;
    padding: 0 0.375rem;
    border-radius: 9999px;
    background-color: #f3f4f6;
    font-size: 0.6875rem;
    font-variant-numeric: tabular-nums;
    color: #6b7280;
}

.section-nav-item.is-active .section-nav-count {
    background-color: color-mix(in srgb, var(--brand-primary-colour) 12%, transparent);
    color: var(--brand-primary-colour);
}

.content-panel {
    overflow-x: visible;
}

.topbar {
    padding-left: 10px;
    padding-right: 10px;
    display: flex;
    position: sticky;
    top: 0px;
    background-color: white;
    min-height: 65px;
    justify-content: space-between;
    border-bottom-width: 1px;
    align-items: center;
    z-index: 1000;
}

/* Tab bars — routing tabs, the dashboards switcher and the in-page panel
   switcher — all render through the shared navigation/tab-bar blade partial,
   and these are its rules (they replaced the old .page-tab* set). Colours,
   thicknesses and timing come from the tab-bar brand settings; every fallback
   is the classic hairline-and-underline look, so an unset site is unchanged.
   One rule set for every tab bar on the site, so they cannot drift apart
   again. Dark-mode lifts live in dark-mode.css. */
.ui-tab-bar {
    display: flex;
    border-bottom: var(--tab-row-border-width, 2px) solid var(--tab-row-border-colour, #e5e7eb);
    /* Close to its panel: the tab names what is directly beneath it, and a
       wide gap reads as two unrelated blocks. */
    margin-bottom: 0.5rem;
}

.ui-tab-bar-row {
    display: flex;
    gap: 0.25rem;
    min-width: 0;
    flex: 1;
}

.ui-tab-bar--wrap .ui-tab-bar-row {
    flex-wrap: wrap;
}

.ui-tab {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: var(--tab-spacing, 0.5rem) var(--tab-inset, 0.875rem);
    font-size: 0.875rem;
    font-weight: var(--tab-font-weight, 500);
    color: var(--tab-text-colour, #6b7280);
    background: transparent;
    border: none;
    text-decoration: none;
    white-space: nowrap;
    /* Transparent on every tab, so a tab does not change height or shift its
       label when it becomes the active one. The underline stacks ABOVE the
       row's hairline rather than covering it, so the bar's own line stays
       visible the whole way across. */
    border-bottom: var(--tab-indicator-width, 2px) solid transparent;
    /* Square, and !important because this sheet carries a global
       `button { border-radius: var(--buttons-border-radius) !important }`
       that specificity alone cannot beat. The underline IS the bottom edge of
       the bar, and rounding it leaves the active tab's rule reading as a
       separate pill rather than a continuation of the hairline it sits on. */
    border-radius: 0 !important;
    cursor: pointer;
    transition: color var(--tab-transition, 0.15s), border-color var(--tab-transition, 0.15s);
}

.ui-tab:hover {
    color: var(--tab-hover-text-colour, #374151);
}

/* Transparent by default — the colour change alone carries the hover. A site
   that sets the hover background gets it on every bar. */
.ui-tab:not(.is-active):not(.is-restricted):hover {
    background: var(--tab-hover-bg, transparent);
}

.ui-tab.is-active {
    color: var(--tab-active-text-colour, var(--brand-accent-ink, var(--brand-primary-colour)));
    border-bottom-color: var(--tab-indicator-colour, var(--brand-primary-colour));
    font-weight: var(--tab-active-font-weight, 600);
    /* Only when the tab-selected-bg brand setting is set — unset, the active
       tab is carried by its underline alone. */
    background: var(--tab-selected-bg, transparent);
}

/* A tab you may not open. Muted past the inactive colour and left out of the
   hover darkening — there is nothing to click through to. */
.ui-tab.is-restricted,
.ui-tab.is-restricted:hover {
    color: #9ca3af;
}

/* A tab whose panel is empty is muted, so you can see at a glance which ones
   hold something without opening them. An active tab keeps its brand colour —
   you are looking at it, so its emptiness is self-evident. */
.ui-tab.is-empty {
    color: #9ca3af;
    font-weight: 400;
}

.ui-tab.is-empty:hover {
    color: #6b7280;
}

.ui-tab-dot {
    width: 0.375rem;
    height: 0.375rem;
    border-radius: 9999px;
    background: currentColor;
    opacity: 0.55;
    flex: none;
}

.ui-tab.is-active .ui-tab-dot {
    opacity: 1;
}

.ui-tab-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.25rem;
    padding: 0 0.375rem;
    font-size: 0.65rem;
    font-weight: 600;
    line-height: 1.25rem;
    color: #1e40af;
    background: #dbeafe;
    border-radius: 9999px;
}

.ui-tab.is-active .ui-tab-badge {
    color: var(--brand-primary-colour);
    background: #e0e7ff;
}

.nav-link {
    border-bottom: 3px solid transparent;
}

.nav-link:hover,
.nav-link:focus {
    border-bottom: 3px solid var(--brand-primary-colour);
    background-color: color-mix(in srgb, var(--brand-primary-colour) 30%, transparent);
    color: var(--brand-primary-colour);
    outline: none;
}

.nav-link.active {
    border-bottom: 3px solid var(--brand-secondary-colour);
}

.topbar-nav {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    height: 100%;
}

.topbar-left,
.topbar-right  {
    height: 100%;
}

.topbar-nav a {
    color: #333333BB;
    text-decoration: none;
    font-size: 1em;
    transition: color 0.3s;
    border-bottom: 2px solid white;
}

.topbar-nav a.active {
    border-bottom: 2px solid var(--brand-secondary-colour);
    font-weight: bold;
}

.topbar-nav a:hover {
    color: var(--brand-secondary-colour);
}

.topbar-title {
    font-weight: bold;
}

.subnav {
    padding-left: 10px;
    padding-right: 10px;
    display: flex;
    position: sticky;
    top: 0px;
    background-color: var(--bg-primary-colour);
    justify-content: space-between;
    border-bottom-width: 1px;
    align-items: center;
}

.subnav a {
    border-bottom: 0;
    margin-top: 5px;
    margin-bottom: 5px;
    padding: 0px;
    padding-right: 1rem;
    padding-left: 1rem;
}

.subnav a:not(:last-child) {
    border-right: 1px solid #a0a0a0;
}

.subnav a.active {
    border-bottom: 0;
    color: var(--brand-primary-colour);
    font-weight: 600;
}

.page-nav {
    margin-left: 0px;
    white-space: nowrap;
    overflow-x: hidden;
    overflow-y: hidden;
}

.site-nav {
    background-color: white;
}

/* ---- Main layout: top navigation (ui::main-layout = top-nav) ----
   layouts/page marks the body; the sidebar stays in the DOM as the mobile
   drawer and is only hidden from the sm breakpoint up, where the bar carries
   the logo and the navigation strip instead. */
@media (min-width: 640px) {
    body.layout-top-nav .sidebar {
        display: none;
    }
}

.top-nav-domains {
    list-style: none;
    margin: 0;
    padding: 0;
    gap: 0.25rem;
    min-width: 0;
}

.top-nav-domain {
    position: relative;
}

.top-nav-domain__link {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.5rem 0.75rem;
    border-radius: 0.375rem;
    font-size: 0.875rem;
    font-weight: 500;
    line-height: 1.25rem;
    white-space: nowrap;
    /* The content text colour, not --text-primary-colour: sites that paint
       the sidebar dark seed that one white, and the bar is white. */
    color: var(--content-text-colour, #374151);
    background: transparent;
    border: 0;
    cursor: pointer;
    text-decoration: none;
    /* The global button rule pins a radius with !important; match it. */
    border-radius: 0.375rem !important;
}

/* Hover and the active domain take --top-nav-icon-colour — the one colour the
   bar's icons already stroke with: brand primary by day, a light tone by
   night — rather than brand primary itself, which a site may seed dark in
   both halves and would vanish on the slate bar. */
.top-nav-domain__link:hover,
.top-nav-domain__trigger[aria-expanded="true"] {
    color: var(--top-nav-icon-colour, var(--brand-primary-colour));
    background: color-mix(in srgb, var(--top-nav-icon-colour, var(--brand-primary-colour)) 8%, transparent);
}

/* The active domain: the colour plus a rule beneath, so it reads as the
   current section even where that colour is close to the text colour. */
.top-nav-domain.selected > .top-nav-domain__link,
.top-nav-domain.selected > .relative > div > .top-nav-domain__link {
    color: var(--top-nav-icon-colour, var(--brand-primary-colour));
    box-shadow: inset 0 -2px 0 0 var(--top-nav-icon-colour, var(--brand-primary-colour));
}

.top-nav-domain.restricted > .top-nav-domain__link {
    opacity: 0.7;
}

.top-nav-domain__chevron {
    flex: none;
    opacity: 0.6;
}

.top-nav-domain__menu {
    min-width: 12rem;
}

.top-nav-domain__item.selected {
    color: var(--top-nav-icon-colour, var(--brand-primary-colour));
    font-weight: 600;
}

.top-nav-domain__item.restricted {
    opacity: 0.7;
}

/* The collapsed-items menu: a domain that was a drop-down becomes a labelled
   group of its entries. */
.top-nav-domain--more {
    flex: none;
}

.site-nav--compact .top-nav-domain__link {
    padding: 0.3rem 0.6rem;
    font-size: 0.8125rem;
}

/* ---- Top navigation: size (top-nav-size brand setting) ----
   "Compact" tightens the bar and everything sitting in it. The height comes
   off the inner h-16 row rather than the <nav>, which is what actually sets
   the bar's height; the trigger and icons shrink with it so the row keeps its
   proportions instead of a short bar full of full-size furniture. */

.site-nav--compact .h-16 {
    height: 3rem;
}

/* Horizontal inset on the top navigation row at desktop width. Was `lg:px-8`
   on the element; the utility was dropped rather than out-specified, because a
   Tailwind breakpoint utility and a rule here land at the same specificity and
   the winner would come down to stylesheet order. Below 1024px the element's
   own `px-4 sm:px-6` still applies. */
@media (min-width: 1024px) {
    .site-nav__inner {
        padding-left: var(--top-nav-inset, 2rem);
        padding-right: var(--top-nav-inset, 2rem);
    }
}

/* Pill controls in the top bar — the command-palette trigger, and any
   package-contributed chip that sits beside it — take their outline from here
   rather than from inline styles, so the row is one set of controls of one
   size at either nav height. */
.top-nav-pill {
    min-width: 280px;
    height: 2.375rem;
}

.site-nav--compact .top-nav-pill {
    padding-top: 0.25rem;
    padding-bottom: 0.25rem;
    min-width: 220px;
    height: 1.875rem;
    font-size: 0.8125rem;
}

/* An avatar riding inside a pill shrinks with it, so the pill keeps its
   height instead of being propped open by its contents. !important because the
   avatar component sizes itself inline from its `size` prop. */
.site-nav--compact .top-nav-pill__avatar {
    width: 1.375rem !important;
    height: 1.375rem !important;
}

/* Every registered icon gets the same 34px box in compact mode. Some of them
   (the messaging bell, announcements and compose buttons) carry their own
   42px chrome rather than the shared icon component's padding, so the size is
   set outright instead of trimmed off a padding — and inline-flex is needed
   because the shared component's icons are anchors, which ignore width. */
/* `:not(.top-nav-popover *)`: an item's popover (the notification panel, the
   compose card) carries links and buttons of its own with their own padding
   and chrome; the icon-button rules below must stop at the bar and never
   reach inside it. A popover opts out by carrying the .top-nav-popover class
   on its root. */
.site-nav--compact .top-nav-item a:not(.top-nav-popover *),
.site-nav--compact .top-nav-item button:not(.top-nav-popover *) {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.125rem;
    height: 2.125rem;
    padding: 0;
}

.site-nav--compact .top-nav-item svg {
    width: 1.25rem;
    height: 1.25rem;
}

.site-nav--compact .user-dropdown img {
    height: 1.75rem;
    width: 1.75rem;
}

/* The one colour every top-nav icon strokes with — the dark-mode toggle here,
   the messaging bell / compose / announcement icons in their own blades. The
   Top Navigation → Icon Colour brand setting emits the property in both theme
   blocks (brand primary by day, slate by night, unless an admin says
   otherwise); the fallback only matters before the stylesheet loads. */
.top-nav-icon {
    color: var(--top-nav-icon-colour, var(--brand-primary-colour, #111827));
}

/* ---- Top navigation: icon style (top-nav-icon-style brand setting) ----
   "Inline" drops the button chrome from the registered top-nav icons —
   notifications, announcements, dark mode — so they read as a row of bare
   icons. There is no hover state in either style: the icons are one quiet
   row, and neither a pill behind them nor a colour shift on the glyph is
   wanted (DEV-48). Badges keep their absolute positioning, so the padding
   is traded for a margin rather than removed outright.

   The border/background/fixed-size reset has to be blanket rather than aimed
   at the shared icon component: the messaging bell, announcements megaphone
   and compose button each ship their own 42px bordered box (.ncb-btn /
   .ann-btn / .mc-btn, styled inline in laravel-messaging's views), and those
   are exactly the boxes "inline" is meant to remove. */

.site-nav--inline-icons .top-nav-item {
    margin-inline-start: 0.125rem;
    margin-inline-end: 0.125rem;
}

.site-nav--inline-icons .top-nav-item a:not(.top-nav-popover *),
.site-nav--inline-icons .top-nav-item button:not(.top-nav-popover *) {
    padding: 0.375rem;
    width: auto;
    height: auto;
    border: none;
    border-radius: 0;
    background-color: transparent;
    background-image: none;
    box-shadow: none;
}

.site-nav--inline-icons .top-nav-item svg:not(.top-nav-popover *) {
    width: 1.25rem;
    height: 1.25rem;
}

.site-header {
    color: var(--brand-primary-colour);
    font-size: 1.1rem;
}

@media screen and (max-width: 600px) {
    .pt-8 {
        padding-top: 1rem;
    }
    
    .px-8 {
        padding-right: 1rem;
        padding-left: 1rem;
    }

    .gap-6 {
        gap: 1rem;
    }

    .page-container {
        padding-bottom: 70px;
    }

    .site-header {
        gap: 1rem;
    }

    .site-header h2 {
        color: var(--text-primary-colour);
    }

    .sidebar {
        min-width: 80vw;
        max-width: 80vw;
        position: fixed;
        z-index: 10100;
        top: 0;
        bottom: 0;
        left: 0;
        height: 100vh;
        box-shadow: 4px 0 20px rgba(0, 0, 0, 0.15);
    }

    /* When the sidebar drawer is open on mobile, hide the bottom
       tab bar so it doesn't peek through to the right of the drawer. */
    body:has(#sidebar:not(.hidden)) .mobile-tab-bar {
        display: none;
    }

    .sidebar.collapsed {
        display: none;
    }

    .sidebar.collapsed .sidebar-item-label {
        display: unset;
    }

    .page-container {
        max-width: 100vw;
    }

    .page-nav {
        margin-left: 0px;
        white-space: nowrap;
        overflow-x: scroll;
    }

    .page-nav {
        overflow: auto;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none; 
        -ms-overflow-style: none;
    }

    .page-nav ::-webkit-scrollbar {
        display: none;
    }

    /* Page-header mobile overrides moved to the end of this file — they
       must come AFTER the base .page-header__* rules so CSS source order
       lets them win. See "Page-header mobile (post-base overrides)". */

    .page-fab.link-button {
        aspect-ratio: 1/1;
        border-radius: 50% !important;
        align-content: center;
        outline: 4px solid color-mix(in oklab, #fff 85%, transparent);
    }

    .page-fab-overlay {
        position: fixed;
        background-color: #eee;
        top: 0px;
        left: 0px;
        width: 100%;
        height: 100%;
        opacity: 0.5;
        z-index: -1;
        display: none;
    }

    #action-toggle:checked ~ .page-fab-overlay {
        display: block;
    }

    .page-actions {
        position: fixed;
        bottom: 110px;
        right: 16px;
        z-index: 100;
        flex-direction: column-reverse;
        align-items: end;
    }

    #action-toggle ~ a,
    #action-toggle ~ .dropdown {
        display: none;
    }

    #action-toggle:checked ~ a,
    #action-toggle:checked ~ .dropdown {
        display: flex;
    }

    main,
    .page-container {
        padding-bottom: 60px;
    }

    .content {
        padding-bottom: 50px;
    }

    .main-nav {
        overflow: hidden;
        margin-right: 1rem;
    }

    .main-nav-links {
        position: fixed;
        right: 0px;
        left: 0px;
        bottom: 0px;
        margin: 0px;
        background-color: white;
        border-top: 1px solid #ddd;
        gap: 0px;
        height: 60px;
    }

    .main-nav-links a {
        display: flex;
        flex-direction: column;
        flex: 1;
        font-size: small;
        gap: 0px;
        padding-top: 10px;
        padding-right: 0px;
        padding-left: 0px
    }
}

/* Tables */
.table-header {
    background-color: #f2eefb; /* fallback */
    background-color: var(--brand-primary-soft);
}

tfoot tr {
    background-color: #F0EDEE;
}

th {
    text-align: left;
    padding: 0.5rem;
    text-transform: uppercase;
}

.log-table td,
.log-table th {
    max-width: 200px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.table-frame {
    overflow-x: auto;
}

.table-action {
    display: inline-block;
}

/* --- Contained table section chrome (desktop) -----------------------------
   A table section renders as one bordered card (sections/base.blade.php adds
   .table-section-card): a grey-gradient toolbar on top, a transparent table in
   the middle, and pagination rows above/below. A subtle bottom shadow lifts the
   card off the page — table sections only. The mobile @media block further
   down owns the small-screen (card-stack) variants. */

/* Subtle bottom shadow — table sections only (not every section). */
.table-section-card {
    box-shadow: 0 8px 20px -8px color-mix(in srgb, var(--brand-primary-colour) 12%, transparent);
}

/* Toolbar band (search + quick filters): grey gradient, rounded to the card's
   top, divided from the table by a hairline. Scoped to .table-section so
   widget/dashboard toolbars are untouched. */
.table-section .table-search-container,
.table-section .table-quick-filters {
    background: linear-gradient(180deg, #fdfdfe 0%, #f6f6f9 100%);
    border-bottom: 1px solid var(--content-border-colour, #e5e7eb);
    border-top-left-radius: 0.5rem;
    border-top-right-radius: 0.5rem;
    padding: 1rem;
}

/* The table itself is transparent so the card background shows through — the
   card owns the border + shadow, not the table (drop its own shadow-sm). */
.table-section .table-frame table.data-table {
    background: transparent;
    box-shadow: none;
}
.table-section .data-table tbody {
    background-color: transparent;
}

/* Quick-filter pill group: a tint of the brand primary instead of white. The
   active pill keeps its solid brand fill (set inline in the blade). */
/* ---- Table filter pills (ui::filter-pill-* brand settings) ----

   The selected pill's colours are resolved into this pair because the
   default variant paints them with an INLINE style, which cannot switch on
   `html.dark`. dark-mode.css redeclares the same two names, so the inline
   attribute reads whichever theme is in force without knowing about either.

   The fallbacks are what the pill did before these settings existed, and the
   settings ship blank and are omitted from :root when blank (see
   resources/css.blade.php), so an untouched instance lands here unchanged. */
:root {
    --pill-active-bg: var(--filter-pill-active-bg, var(--button-primary-bg));
    --pill-active-text: var(--filter-pill-active-text-colour, var(--button-primary-fg, #fff));
}

/* Unselected pills in the default variant sit on the capsule's tint, so
   their resting background falls back to `transparent` rather than white —
   painting them white by default would fill in the capsule this variant
   deliberately shows through. The floating variant, which has no capsule,
   falls back to white in its own stylesheet. */
.quick-filter-pills > a:not([style]) {
    background-color: var(--filter-pill-bg, transparent);
    border: 1px solid var(--filter-pill-border-colour, transparent);
    color: var(--filter-pill-text-colour, #4b5563);
}

.quick-filter-pills > a:not([style]):hover {
    background-color: color-mix(in srgb, var(--pill-active-bg) 10%, var(--filter-pill-bg, #ffffff));
    color: var(--filter-pill-text-colour, #111827);
}

.quick-filter-pills {
    background-color: #efeafb; /* fallback */
    background-color: var(--brand-primary-soft);
    border: 1px solid var(--brand-primary-soft); /* fallback */
    border: 1px solid var(--brand-primary-soft);
}

/* ---- Segmented toggle (x-ui::actions.segmented-toggle) ---- */

/* The selected pair, resolved once so the option rules below read it without
   knowing about themes — dark-mode.css redefines these two under html.dark the
   same way --pill-active-* is handled for the table filters.

   Unlike the filter pills, the default here is the brand SECONDARY colour and
   its paired text colour, not the primary button. That is the point of the
   control: the label follows a setting instead of being pinned to white, so a
   pale brand no longer produces an unreadable selected option. */
:root {
    --segmented-selected-bg: var(--segmented-toggle-selected-bg, var(--brand-secondary-colour, #73e7e5));
    --segmented-selected-text: var(--segmented-toggle-selected-text-colour, var(--text-secondary-colour, #111827));
}

.segmented-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.125rem;
    /* Stadium, to match the options inside it. Concentric rounding wants the
       inner radius to be the outer one minus the padding; a fixed 1rem shell
       around a fully-rounded option does not satisfy that at the heights this
       control actually renders at (a ~24px option rounds to 12px, inside a
       16px shell), and the selected fill reads as tighter than the capsule.
       Rounding both ends fully makes the two curves parallel at any height,
       so the sm variant and a taller option both stay true. */
    border-radius: 9999px;
    background-color: #efeafb; /* fallback */
    background-color: var(--segmented-toggle-track-bg, var(--brand-primary-soft));
    border: 1px solid var(--brand-primary-soft); /* fallback */
    border: 1px solid var(--brand-primary-soft);
}

.segmented-toggle__option {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.25rem 0.75rem;
    /* !important because the global `button { border-radius:
       var(--buttons-border-radius) !important }` rule below is, and a bare
       element selector with !important beats this class. Without it a selected
       option wears the brand BUTTON radius (20px by default, and whatever a
       site sets — 4px would render it near-square) instead of following the
       capsule around it. Same reason, and same treatment, as the
       .page-action-group .btn-seg-* rules. Link mode is unaffected, which is
       why only the button-mode instances looked wrong. */
    border-radius: 9999px !important;
    font-size: 0.75rem;
    font-weight: 500;
    line-height: 1.25rem;
    white-space: nowrap;
    cursor: pointer;
    text-decoration: none;
    transition: background-color 0.15s ease, color 0.15s ease;
    /* Keyed on aria-checked rather than on the presence of an inline style
       attribute, which is how the older pill markup had to do it. The state is
       in the DOM as the accessibility tree already needs it, so there is
       nothing to keep in sync. */
    background-color: var(--segmented-toggle-bg, transparent);
    border: 1px solid var(--segmented-toggle-border-colour, transparent);
    color: var(--segmented-toggle-text-colour, #4b5563);
}

.segmented-toggle__option:hover {
    background-color: color-mix(in srgb, var(--segmented-selected-bg) 10%, var(--segmented-toggle-bg, #ffffff));
    color: var(--segmented-toggle-text-colour, #111827);
}

.segmented-toggle__option[aria-checked="true"],
.segmented-toggle__option[aria-checked="true"]:hover {
    background-color: var(--segmented-selected-bg);
    color: var(--segmented-selected-text);
    box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
}

.segmented-toggle__option:focus-visible {
    outline: 2px solid color-mix(in srgb, var(--segmented-selected-bg) 70%, #000000);
    outline-offset: 1px;
}

.segmented-toggle--sm .segmented-toggle__option {
    padding: 0.125rem 0.5rem;
    font-size: 0.6875rem;
}

.segmented-toggle__count {
    font-size: 0.6875rem;
    font-weight: 600;
    opacity: 0.75;
}

/* Desktop pagination: bordered number + chevron buttons; the active page is
   filled with the brand primary. (Mobile has its own compact rules below.) */
@media screen and (min-width: 601px) {
    /* Pagination bar text — rows-per-page, summary and page buttons — at 13px. */
    .paginator .text-sm {
        font-size: 0.8125rem;
    }
    .paginator > nav > a {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-width: 2rem;
        height: 2rem;
        padding: 0 0.5rem;
        border: 1px solid var(--content-border-colour, #e5e7eb);
        border-radius: 0.5rem;
        color: #374151;
        font-size: 0.8125rem;
        text-decoration: none;
        transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
    }
    .paginator > nav > a:hover {
        background-color: #f9fafb;
    }
    .paginator > nav > a.active-page {
        background-color: var(--button-primary-bg);
        border-color: var(--button-primary-bg);
        color: var(--button-primary-fg, #fff);
    }
    /* Prev/next chevrons are the first/last links — no box, just the icon
       (a transparent border preserves the button's size so the row stays
       vertically aligned with the numbered buttons). */
    .paginator > nav > a:first-child,
    .paginator > nav > a:last-child {
        border-color: transparent;
        color: #9ca3af;
    }
    .paginator > nav > a:first-child:hover,
    .paginator > nav > a:last-child:hover {
        background-color: transparent;
        color: #6b7280;
    }
}

/* Fixed column layout — opt-in via `data-table--fixed` when a table
   declares explicit column widths (BaseColumn::width). Desktop only:
   the mobile @media block below renders the table as a card stack
   where a fixed layout / colgroup widths make no sense, so the rule
   is scoped above the 600px breakpoint. */
@media screen and (min-width: 601px) {
    table.data-table--fixed {
        table-layout: fixed;
    }
}

/* Chromeless empty table. A table with no rows drops its column headers so
   the empty state reads as a standalone panel rather than a stub of a data
   grid. The toolbar above (search + filters) is untouched — a viewer who
   filtered down to zero rows still needs it to get back.
   The <thead> is HIDDEN, not removed: it carries the Alpine `tableSort`
   root, and toolbar.js re-shows it by dropping this class the moment a
   client-side search puts rows back. The pagination bars are hidden for
   the same reason — updatePaginationFromJson() redraws bars in place and
   never creates them, so they must survive in the DOM. */
table.data-table--chromeless > thead {
    display: none;
}

.table-pagination[hidden] {
    display: none;
}

/* The empty-state cell is a panel, not a row: drop the row separators the
   global `th, td` rule gives every cell (top AND bottom — that bottom edge
   is what draws a stray hairline across the card under the empty state).
   Not scoped to --chromeless: the cell should read as a panel whether or
   not the headers went with it. */
.data-table > tbody > tr.table-empty-row > td {
    border-top: 0;
    border-bottom: 0;
}

/* Nor should it take the row hover tint — there's no row to act on. */
.data-table > tbody > tr.table-empty-row:hover {
    background-color: transparent;
}

/* Content links: cross-reference links in table cells (LinkColumn, .table-link)
   and prose links that opt in with .brand-link. A row of default blue
   hyperlinks reads as browser chrome, not as the brand, so the link keeps the
   body ink and earns its affordance from a brand-tinted rule under the text
   that solidifies on hover.

   Every part of that is a brand setting (elements.links → --link-*), and each
   ships blank so the var() fallbacks below reproduce the historical treatment
   untouched: body ink, the brand primary at 32% under it, the brand primary on
   hover. Blank settings are not emitted at all (see resources/css.blade.php),
   which is what keeps these fallbacks — and the dark-theme ones in
   dark-mode.css — reachable.

   The underline is a border rather than text-decoration so it can carry a
   colour of its own (a translucent accent, say) and sharpen to solid on hover,
   and so 'no underline' is just a zero width. */
.table-link,
.brand-link {
    color: var(--link-colour, var(--text-accent-colour, #111827));
    font-weight: var(--link-font-weight, inherit);
    text-decoration: none;
    border-bottom: var(--link-underline-width, 1px) solid
        var(--link-underline-colour, color-mix(in srgb, var(--brand-primary-colour) 32%, transparent));
    border-radius: 2px;
    background-color: transparent;
    transition: color 0.15s ease, border-color 0.15s ease, background-color 0.15s ease;
}

.table-link:hover,
.brand-link:hover {
    color: var(--link-hover-colour, var(--brand-primary-colour));
    font-weight: var(--link-hover-font-weight, var(--link-font-weight, inherit));
    /* currentColor keeps the rule legible on any ink, including inside a cell
       that sets its own colour. */
    border-bottom-color: var(--link-hover-colour, currentColor);
    background-color: var(--link-hover-bg, transparent);
}

/* Keyboard focus needs a visible ring — the hover underline alone is too
   quiet for a link reached by tabbing. */
.table-link:focus-visible,
.brand-link:focus-visible,
.page-content a:not([class]):focus-visible {
    outline: 2px solid color-mix(in srgb, var(--brand-primary-colour) 60%, transparent);
    outline-offset: 2px;
    border-radius: 2px;
}

/* Links in page copy pick the treatment up on their own, without a class.
   `a:not([class])` is the whole rule: an anchor with no class of its own is
   prose — a link someone wrote in content, a CMS page, a description, a
   markdown block. Anything componentised (buttons, tabs, chips, cards, rows,
   pagination, the hand-rolled .dashboard-tab strip, a package's own .ds-*
   controls) carries a class, and so is left entirely alone.

   This replaced a `.page-content a:not(<long list>)` rule. The list could
   never be complete — every package has its own control-anchor vocabulary,
   and each one missed showed up as an underline under a tab or a chip. The
   class attribute is already the signal we were trying to reconstruct.

   A component that WANTS the treatment asks for it with .brand-link (see the
   rule above, shared with .table-link). A class-less anchor that does not
   want it takes any class at all — `class="no-brand-link"` reads clearest. */
.page-content a:not([class]) {
    color: var(--link-colour, inherit);
    font-weight: var(--link-font-weight, inherit);
    text-decoration: none;
    border-bottom: var(--link-underline-width, 0) solid var(--link-underline-colour, transparent);
    border-radius: 2px;
    background-color: transparent;
    transition: color 0.15s ease, border-color 0.15s ease, background-color 0.15s ease;
}

.page-content a:not([class]):hover {
    color: var(--link-hover-colour, inherit);
    font-weight: var(--link-hover-font-weight, var(--link-font-weight, inherit));
    border-bottom-color: var(--link-hover-colour, transparent);
    background-color: var(--link-hover-bg, transparent);
}

/* Responsive styles for small screens — render data tables as a
   stack of cards, one card per row. Each cell becomes a label/value
   line; the action column floats to the top-right of the card. */
@media screen and (max-width: 600px) {
    /* Side gutters for the card stack — the desktop table is full-bleed
       inside the section card, but the mobile cards look better inset
       from the section edge. Padding on the container (not the cards)
       so the search/header toolbar above lines up with the cards. */
    .table-container {
        padding-left: 0.75rem;
        padding-right: 0.75rem;
    }

    /* `display: block` on the table is essential — a `display: table`
       element shrink-wraps to its content, and any `<colgroup>` width
       (emitted by `data-table--fixed`) would otherwise collapse the
       card stack to a fraction of the screen. As a block the table and
       its `<colgroup>` are inert, so the cards span the full width. */
    .responsive-table {
        display: block;
        width: 100%;
        background: transparent !important;
        box-shadow: none !important;
        border-spacing: 0 0.75rem;
        border-collapse: separate;
    }

    .responsive-table colgroup {
        display: none;
    }

    .responsive-table thead {
        display: none;
    }

    .responsive-table tbody {
        display: block;
        width: 100%;
        background: transparent !important;
    }

    .responsive-table tr {
        display: block;
        position: relative;
        background: #fff;
        border: 1px solid #e5e7eb;
        border-radius: 0.75rem;
        padding: 0.875rem 1rem;
        margin-bottom: 0.75rem;
        box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
    }

    .responsive-table td {
        display: flex;
        align-items: flex-start;
        gap: 0.5rem;
        border: none;
        padding: 0.25rem 0;
        position: relative;
        /* `min-width: 0` lets the cell shrink below its content so the
           value wraps at word boundaries; `overflow-wrap: break-word`
           only kicks in for genuinely unbreakable strings (long URLs,
           UUIDs) — it must NOT be `anywhere`, which breaks ordinary
           words mid-character. */
        min-width: 0;
        width: 100%;
        max-width: 100%;
        box-sizing: border-box;
        overflow-wrap: break-word;
        word-break: break-word;
    }

    /* Anything the cell renders — text, links, badges, a nested
       <span> from an HtmlColumn — is clamped to the cell width and
       breaks unbreakable strings (emails, UUIDs) rather than forcing
       the card wide. `min-width: 0` is needed on flex/inline children
       so they can shrink below their intrinsic content width. */
    .responsive-table td > * {
        min-width: 0;
        max-width: 100%;
        overflow-wrap: break-word;
        word-break: break-word;
    }

    /* Label column: a fixed-basis flex item that does NOT grow — it
       takes exactly ~6.5rem and yields the rest of the row to the
       value, so the value text uses the full remaining width instead
       of shrink-wrapping and wrapping awkwardly after a word or two.
       Top-aligned so a multi-line value doesn't drag the label down. */
    .responsive-table td::before {
        content: attr(data-label);
        font-weight: 600;
        font-size: 0.8125rem;
        color: #6b7280;
        flex: 0 0 6.5rem;
        min-width: 0;
        align-self: flex-start;
        overflow-wrap: break-word;
        word-break: break-word;
    }

    /* UserColumn cells render an avatar that is taller than a single
       line of text. Centre the label (and the cell's flex items) so
       the label lines up with the name rather than the avatar top. */
    .responsive-table td.user-column-cell {
        align-items: center;
    }

    .responsive-table td.user-column-cell::before {
        align-self: center;
    }

    /* Action column docks to the bottom of the card as a full-width
       footer row, separated by a hairline divider. Negative margin
       breaks out of the card's `0.875rem 1rem` padding so the divider
       and footer span edge-to-edge. The cell carries `.fit-content`
       (width:1px) and `min-width:100px` from desktop — override both
       so the footer can stretch full-width on mobile. */
    .responsive-table td.table-actions-column,
    .responsive-table td[data-label="Actions"] {
        /* Do NOT set an explicit width. As a block-level flex element
           inside the card, `width: auto` fills the card's content box
           exactly; the `-1rem` side margins then extend it precisely
           to the card border on each side. An explicit `calc(100% +
           2rem)` would over-extend (100% is the content box, already
           inside the padding) and push the divider past the edge.
           `box-sizing: border-box` keeps the 1rem padding inside that
           width so the pills wrap instead of bleeding off-screen. */
        box-sizing: border-box;
        width: auto !important;
        /* The base `.responsive-table td` rule clamps every cell to
           `max-width: 100%` (= the card content box). The action cell
           must break OUT past that to the card border, so the clamp
           has to be lifted here or the divider stops short on the
           right. */
        max-width: none !important;
        min-width: 0 !important;
        margin: 0.625rem -1rem -0.875rem;
        padding: 0.625rem 1rem;
        border-top: 1px solid #f3f4f6;
        /* When the pills wrap, `flex-start` keeps the wrapped row left
           aligned and the rows packed — a lone wrapped pill hanging
           off the right edge (from `flex-end`) looks unbalanced. */
        justify-content: flex-start;
        flex-wrap: wrap;
        gap: 0.375rem;
        white-space: normal;
    }

    .responsive-table td.table-actions-column::before,
    .responsive-table td[data-label="Actions"]::before {
        display: none;
    }

    /* Promote each `<div class="table-action">` (16x16 icon-only on
       desktop) into a proper labelled pill button on mobile. The
       action's `title` attribute on the inner `<a>` carries the label
       — surface it via a `::after` pseudo-element. */
    .responsive-table td.table-actions-column .table-action,
    .responsive-table td[data-label="Actions"] .table-action {
        width: auto;
        height: auto;
        display: inline-flex;
        align-items: center;
    }

    .responsive-table td.table-actions-column .table-action > a,
    .responsive-table td[data-label="Actions"] .table-action > a {
        display: inline-flex;
        align-items: center;
        gap: 0.3125rem;
        padding: 0.375rem 0.625rem;
        background: #f3f4f6;
        color: #374151;
        border-radius: 9999px;
        font-size: 0.75rem;
        font-weight: 500;
        line-height: 1;
        /* Pills can shrink below their content and clip the label so
           a row of 3+ actions wraps rather than overflowing the card. */
        max-width: 100%;
        white-space: nowrap;
    }

    .responsive-table td.table-actions-column .table-action > a > svg,
    .responsive-table td[data-label="Actions"] .table-action > a > svg {
        width: 0.875rem;
        height: 0.875rem;
        flex-shrink: 0;
    }

    .responsive-table td.table-actions-column .table-action > a::after,
    .responsive-table td[data-label="Actions"] .table-action > a::after {
        content: attr(title);
    }

    /* Hide cells with no rendered value so empty fields don't push
       the card height. Targets cells whose only content is whitespace
       (CSS can't see "empty after render", so this is best-effort:
       visually-empty cells keep showing the label — packages that
       want a tighter look can null out empty cells server-side). */
    .responsive-table td:empty {
        display: none;
    }

    /* Hide cells with no column title — UUID copy-buttons and other
       label-less utility columns. They're noise in the card layout. */
    .responsive-table td[data-label=""] {
        display: none;
    }

    /* Hide the "N results" count from the search header on mobile —
       saves vertical space; the count is implicit from the card list. */
    .table-search-container [data-table-result-count] {
        display: none !important;
    }

    /* Compact the search input vertically so it doesn't dwarf the
       inline action button next to it. */
    .table-search-container input[type="text"] {
        padding-top: 0.5rem !important;
        padding-bottom: 0.5rem !important;
        font-size: 0.875rem !important;
    }

    /* Inline action button ("+ Create X") next to the search input
       is `<a class="link-button uppercase tracking-widest …">`.
       Parent flex row uses `items-stretch` which makes the button
       grow to match the search input height — pin it to its own
       content height and shrink padding/font for proportional sizing.
       Anchor to the top of its flex row so when the filters wrap to
       a new line below the search input, the button stays aligned
       with the search bar instead of floating between the two. */
    .table-search-container a.link-button.uppercase,
    .table-search-container a.link-button.uppercase:hover,
    .table-search-container a.link-button.uppercase:focus,
    .table-search-container a.link-button.uppercase:active {
        padding: 0.5rem 0.875rem !important;
        font-size: 0.75rem !important;
        border-radius: 9999px !important;
        align-self: flex-start !important;
        height: auto !important;
        margin-top: 0.25rem !important;
        background-color: var(--button-primary-bg) !important;
        color: var(--button-primary-fg) !important;
    }

    /* Mobile quick-filter row — the search blade renders a dedicated
       `.quick-filter-mobile` block at full row width below the search
       + action button. Drop the rounded-capsule chrome so pills stand
       alone, and pin active/inactive colours across hover/focus/active
       so a tap doesn't flash to slate-grey. */
    .quick-filter-mobile .quick-filter-pills {
        background: transparent !important;
        border: 0 !important;
        padding: 0 !important;
        gap: 0.375rem !important;
    }
    .quick-filter-mobile .quick-filter-pills > a:not([style*="background-color"]),
    .quick-filter-mobile .quick-filter-pills > a:not([style*="background-color"]):hover,
    .quick-filter-mobile .quick-filter-pills > a:not([style*="background-color"]):focus,
    .quick-filter-mobile .quick-filter-pills > a:not([style*="background-color"]):active {
        background: #f3f4f6 !important;
        color: #374151 !important;
        border: 1px solid #e5e7eb !important;
    }
    /* Active pill border to match the inactive ones — keeps the
       circular outline consistent so pills don't shift size when
       toggling state. */
    .quick-filter-mobile .quick-filter-pills > a[style*="background-color"] {
        border: 1px solid transparent !important;
    }
    .quick-filter-mobile .quick-filter-pills > a[style*="background-color"],
    .quick-filter-mobile .quick-filter-pills > a[style*="background-color"]:hover,
    .quick-filter-mobile .quick-filter-pills > a[style*="background-color"]:focus,
    .quick-filter-mobile .quick-filter-pills > a[style*="background-color"]:active {
        background-color: var(--button-primary-bg) !important;
        color: var(--button-primary-fg, #fff) !important;
    }

    /* Mobile icon buttons (`+` action, funnel toggle) — circular
       36px tap targets with state-pinned colours so a tap doesn't
       flash to a different background.
         --primary: brand-coloured fill, white icon
         --neutral: grey background, grey icon */
    .mobile-icon-btn,
    button.mobile-icon-btn {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 2.25rem;
        height: 2.25rem;
        border-radius: 9999px !important;
        flex-shrink: 0;
        transition: background-color 0.15s, color 0.15s;
    }
    .mobile-icon-btn--primary,
    .mobile-icon-btn--primary:hover,
    .mobile-icon-btn--primary:focus,
    .mobile-icon-btn--primary:active {
        background-color: var(--button-primary-bg) !important;
        color: var(--button-primary-fg, #fff) !important;
        border: 0 !important;
    }
    .mobile-icon-btn--neutral,
    .mobile-icon-btn--neutral:hover,
    .mobile-icon-btn--neutral:focus,
    .mobile-icon-btn--neutral:active {
        background-color: #f3f4f6 !important;
        color: #6b7280 !important;
        border: 1px solid #e5e7eb !important;
    }

    /* Pagination — restyled as a compact pill row. The "Showing X
       of Y" sentence drops below the page-number row, page numbers
       become circular tap targets, the active page is filled with
       the brand colour, and the prev/next chevrons sit at the row
       edges so they don't wrap to a new line on their own. */
    .paginator {
        flex-direction: column-reverse;
        gap: 0.75rem;
        align-items: center;
    }
    .paginator > div {
        font-size: 0.75rem;
    }
    .paginator > nav {
        flex-wrap: nowrap;
        justify-content: center;
        gap: 0.25rem !important;
        max-width: 100%;
    }
    .paginator > nav > a,
    .paginator > nav > svg {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-width: 2rem;
        height: 2rem;
        padding: 0 0.5rem;
        border-radius: 9999px;
        font-size: 0.875rem;
        color: #374151;
    }
    .paginator > nav > a:hover {
        background: #f3f4f6;
    }
    .paginator > nav > a.active-page {
        background: var(--button-primary-bg);
        color: var(--button-primary-fg, #fff) !important;
    }
    .paginator > nav > svg {
        color: #9ca3af;
    }

    /* Global guard against horizontal overflow on mobile — any child
       that exceeds the viewport width clamps to it. Tables wrapped in
       `.table-frame` already scroll their own overflow on desktop;
       on mobile we render as cards so the desktop scroll is moot. */
    html, body {
        max-width: 100vw;
        overflow-x: hidden;
    }
    .table-frame {
        overflow-x: visible;
    }

    /* Page-tab nav (domainTabs) — cap visible tabs at 3 on mobile and
       push the rest into the More dropdown. The Alpine `domainTabs`
       component normally measures tab widths and hides those that
       don't fit; on mobile we want a deterministic 3-up layout. The
       `tab-overflow.js` hijack reads which tabs are actually hidden
       (post-CSS) and writes the dropdown contents accordingly so the
       strip and dropdown can never overlap. */
    nav[x-data*="domainTabs"] [x-ref="tabContainer"] {
        gap: 0 !important;
        flex: 1 1 auto !important;
        width: 100% !important;
    }
    nav[x-data*="domainTabs"] [x-ref="tabContainer"] a[data-tab-item]:nth-of-type(n+4) {
        display: none !important;
    }
    nav[x-data*="domainTabs"] [x-ref="tabContainer"] a[data-tab-item]:nth-of-type(-n+3) {
        display: flex !important;
        flex: 1 1 0 !important;
        justify-content: center !important;
    }
    nav[x-data*="domainTabs"] [x-ref="moreButton"] {
        flex: 0 0 auto !important;
        margin-left: auto !important;
    }
    nav[x-data*="domainTabs"] [x-ref="tabContainer"]:has(a[data-tab-item]:nth-of-type(4)) [x-ref="moreButton"] {
        display: flex !important;
    }

    /* Empty page-header (showHeader true but title in topbar) still
       renders an empty flex div with whitespace/blade comments —
       :empty won't match, so collapse it when it has no element
       children. */
    .page-header:not(:has(> *)) {
        display: none !important;
    }
    /* When the page-header only holds an actions block (no title,
       no description) it still adds 16px `pb-4` above the tabs.
       Strip that bottom padding on mobile — the FAB/actions float
       instead of pushing layout space. */
    .page-header > .pb-4,
    .page-header > div[class*="pb-4"] {
        padding-bottom: 0 !important;
    }
}

.fit-content {
    white-space: nowrap;
    width: 1px;
}

.table-actions-column {
    white-space: nowrap;
    width: 1px;
    min-width: 100px;
}

/* A table whose rows carry ONE click-through action keeps the Actions column
   in the DOM but not on screen: clicking the row IS the action (table.js
   synthesises a click on the menu item, which fires on a display:none element
   just the same), so the "⋯" menu would be a second control for the one thing
   the row does. See TableDTO::hidesActionColumn() for when this is applied —
   a second action always brings the column back.

   !important because the mobile card-stack rules below re-lay-out this same
   cell as a full-width card footer; without it the footer wins at <=600px and
   the menu reappears there alone. */
table.data-table--actions-hidden > thead th.table-actions-column,
table.data-table--actions-hidden .table-actions-cell {
    display: none !important;
}

.quote {
    border-left: 3px solid var(--brand-primary-colour);
    padding-left: 1rem;
}

.grid-cols-4 {
    grid-template-columns: repeat(4,minmax(0,1fr));
}

.grid-container {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
}

@media (max-width: 600px) {
    .grid {
        grid-template-columns: 1fr; 
    }
  
    .grid > * {
        min-width: 100%;
    }
}

.card {
    background-color: var(--content-bg-colour);
    background-image: var(--content-bg-image);
    color: var(--content-text-colour, #333333);
    border-radius: 5px;
    padding: 2rem;
}

.card-icon {
    width: 2rem;
    height: 2rem;
    color: var(--brand-secondary-colour);
}

.oneline {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    display: inline-block;
}

.mx-16 {
    margin-right: 4rem;
    margin-left: 4rem;
}

.pickr {
    border: 1px solid grey;
    box-shadow: 0 0 0 1px white;
    display: inline-block;
    border-radius: 3px;
}

.pickr .pcr-button{
    position: unset;
}

.pcr-color-preview button {
    border-radius: 0px !important;
}

.button-group {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

button.bg-red-600 {
    background-color: var(--button-danger-bg);
    color: var(--button-danger-fg);
}

button.bg-gray-800 {
    background-color: var(--button-primary-bg);
    color: var(--button-primary-fg);
}

a.bg-gray-800 {
    background-color: var(--button-primary-bg);
    color: var(--button-primary-fg);
}

button.bg-white {
    background-color: var(--button-secondary-bg);
    color: var(--button-secondary-fg);
}

.notice {
    background-color: var(--alert-warning-bg, #fffdd0);
    color: var(--alert-warning-fg, inherit);
    align-items: center;
    gap: 0.5rem;
    border-bottom: 1px solid var(--alert-warning-border, #e5e7eb);
}

.tile {
    display: flex;
    background-color: var(--content-bg-colour);
    background-image: var(--content-bg-image);
    color: var(--content-text-colour, #333333);
    border-radius: 12px;
    overflow: hidden;
    border: 1px solid #ddd;
}

.left-section {
    background-color: var(--brand-primary-colour);
    width: 120px;
    display: flex;
    justify-content: center;
    align-items: center;
    background: linear-gradient(color-mix(in srgb, var(--brand-primary-colour) 30%, transparent), color-mix(in srgb, var(--brand-primary-colour) 30%, transparent)), url(http://portal.docker.localhost/img/Sop.png);
    background-repeat: no-repeat;
    background-size: cover;
}

.progress-circle {
    width: 70px;
    height: 70px;
    border-radius: 50%;
    background: radial-gradient(circle closest-side, var(--brand-primary-colour) 80%, transparent 81%),
                conic-gradient(#26c6da 0% 30%, #e0e0e0 30% 100%);
    display: flex;
    justify-content: center;
    align-items: center;
    color: white;
    font-size: 14px;
    font-weight: bold;
    position: relative;
}

.progress-circle::after {
    width: 50px;
    height: 50px;
    border-radius: 50%;
    background-color: white;
    display: flex;
    justify-content: center;
    align-items: center;
    color: white;
    font-size: 14px;
    font-weight: bold;
    position: relative;
}

.progress-circle span {
    position: absolute;
}

.right-section {
    padding: 20px;
    display: flex;
    flex-direction: column;
    flex: 1;
}

.right-section h3 {
    margin: 0;
}

.right-section p {
    margin: 10px 0 0;
    color: #666666; /* Light gray text */
    font-size: 14px;
}

.arrow-button {
    align-self: flex-end;
    background-color: #26c6da; /* Teal button */
    color: white;
    width: 35px;
    height: 35px;
    border-radius: 50%;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 16px;
    margin-top: 20px;
    cursor: pointer;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}

.arrow-button:hover {
    background-color: #1b9db4; /* Darker teal on hover */
}

.choices {
    margin-bottom: 0px;
    border-radius: .375rem;
    --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);
    --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);
    box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}

.choices__inner {
    /* margin-top: 0.25rem; */
    background-color: white;
    padding: 6px 6px 3.75px;
    border: 1px solid #ddd;
    border-radius: 5px;
    height: 42px;
    min-height: 42px;
}

.choices__input {
    margin-bottom: 0px;
    background-color: white;
}

.choices[data-type*=select-multiple] .choices__button, .choices[data-type*=text] .choices__button {
    border-left: none;
}

.choices__button {
    height: 24px !important; 
    width: 24px !important;
}

.is-disabled .choices__button {
    display: none;
}

.choice-element {
    max-height: 37px;
}

select.choice-element {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    z-index: 0; /* Put it behind */
    pointer-events: none;
}

.choices__list--multiple .choices__item {
    color: var(--text-secondary-colour);
    background-color: var(--brand-secondary-colour);
    border: unset;
}

.choices__input:focus {
    box-shadow: unset;
}

.choices__list--dropdown, .choices__list[aria-expanded] {
    z-index: 100;
}

.visibility-hidden {
    visibility: hidden;
}

.information-container.wrapper {
    display: none;
}

/* Toogle Button */
.toggle-container {
    display: flex;
    align-items: center;
    gap: 10px;
}

.toggle-label {
    display: inline-block;
    width: 35px;
    height: 18px;
    background: grey;
    border-radius: 25px;
    position: relative;
    cursor: pointer;
    transition: background 0.3s ease;
}

.toggle-label::before {
    content: "";
    width: 14px;
    height: 14px;
    background: white;
    position: absolute;
    top: 2px;
    left: 2px;
    border-radius: 50%;
    transition: transform 0.3s ease;
}

.toggle-checkbox {
    display: none;
}

.toggle-checkbox:checked + .toggle-label {
    background: var(--brand-secondary-colour);
}

.toggle-checkbox:checked + .toggle-label::before {
    transform: translateX(15px);
}

/* Editing CSS */
.editing-toolbar {
    display: none;
}

.ql-editor {
    font-family: var(--font-family);
    font-size: var(--font-size);
    padding: 0px;
}

.html-editor .ql-editor {
    padding: 1rem;
}

.editing .editing-toolbar {
    position: absolute;
    top: 8px;
    right: 8px;
    left: auto;
    transform: none;
    background: #f3f4f6;
    border: 1px solid #e5e7eb;
    border-radius: 10px;
    padding: 2px;
    display: flex;
    align-items: center;
    gap: 2px;
    z-index: 15;
}

/* Compact, icon-only action buttons. The visible label is dropped in
   favour of title/aria-label tooltips so the bar takes minimal space. */
.editing .editing-toolbar .tool-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border: none;
    background: transparent;
    border-radius: 7px;
    color: #4b5563;
    cursor: pointer;
    transition: background-color 0.12s ease, color 0.12s ease;
}

.editing .editing-toolbar .tool-btn:hover {
    background: #e5e7eb;
    color: #111827;
}

.editing .editing-toolbar .tool-btn .tool-icon {
    display: inline-flex;
    pointer-events: none;
}

.editing .editing-toolbar .move-handle {
    cursor: move;
}

/* Thin separator between the move handle and the action icons. */
.editing .editing-toolbar .tool-divider {
    width: 1px;
    align-self: stretch;
    margin: 4px 2px;
    background: #d1d5db;
}

.editing .ql-editor::before {
  content: "";
  position: absolute;
  top: -5px;
  left: -5px;
  right: -5px;
  bottom: -5px;
  border: 2px dotted var(--brand-secondary-colour);
  pointer-events: none; /* Ensures the pseudo-element doesn't block interactions */
}

.ql-tooltip {
    z-index: 100;
}

/* Tables */
th, td {
    border: 1px solid #ddd; /* Light grey border */
    padding: 8px;
    text-align: left;
    border-right: none;
    border-left: none;
}

th:first-child,
td:first-child {
    border-left: none;
}

/* Rows inside widgets (item-rows, the CRM recent-companies list, the MAPS
   substrate rows). Only a row you can open highlights — the same rule the
   tables follow — and the wash is the Widgets → Widget Cards row hover
   setting, which always ships both theme halves. */
.widget-row {
    /* Inline padding so the hover wash surrounds the row's dot, label and
       trailing link; the matching negative margin keeps the text on the
       heading's edge while the wash bleeds into the card padding. */
    padding-inline: .5rem;
    margin-inline: -.5rem;
    border-radius: .375rem;
    transition: background-color .12s;
}
.widget-row--link:hover {
    background-color: var(--widget-row-hover-bg, #f9fafb);
}

/* The first DATA cell of a data-table row (the row partial marks it; the
   reorder, expander and action cells are outside the mark). The weight is
   the Tables → Bold First Column brand setting, emitted only when enabled,
   so the fallback is what the cell inherits today. */
.data-table td.table-lead-cell {
    font-weight: var(--table-first-column-weight, inherit);
}

/* A cell that TRUNCATES rather than widens. Auto table layout sizes a
   column to its content's minimum width, and a nowrap title's minimum is
   the whole title — so one long title pushes every other column off the
   page. max-width: 0 removes that cell from the minimum-width sum: the
   column takes the width its <col> hint and the remaining space allow, and
   the content inside ellipsises. Desktop only: the stacked responsive
   layout lays cells out as blocks, where a zero max-width would collapse
   them. */
@media screen and (min-width: 601px) {
    /* width: 100% is the hint that makes it the FLEXIBLE column: the
       others shrink to their content and this one takes the rest. */
    .data-table td.table-truncate-cell { max-width: 0; width: 100%; }
    .data-table td.table-truncate-cell > * { max-width: 100%; }
}

th:last-child,
td:last-child {
    border-right: none;
}

tbody tr:hover {
    background-color: #F7F3FD;
}

/* Widgets */
.widget {
    /* Same brand group as .layout-cell (Widgets -> Widget Cards) so a widget
       looks the same whether a dashboard cell draws its card or it draws its
       own. Blank settings are not emitted, so the fallbacks below are the
       hairline, shadowless card as it always was. */
    border: var(--widget-border-width, 1px) solid
        var(--widget-border-colour, var(--content-border-colour));
    box-shadow: var(--widget-shadow, none);
    box-sizing: content-box;
    border-radius: 12px;
    position: relative;
    background-color: var(--content-bg-colour);
    background-image: var(--content-bg-image);
    color: var(--content-text-colour, #333333);
}

/* The tile a chromeless widget builds inside itself — the same Widgets ->
   Widget Cards treatment as .widget above, at tile scale. It lives here
   rather than in x-ui::widgets.card's own @once block because the class is
   meant to be worn by any package that needs a widget-looking surface, and
   a rule that ships only when its component renders is invisible to
   everyone who reaches for the class alone (DEV-58: the message centre's
   simple list panel bound the class and got no border at all). The
   component still owns the markup; this owns the paint. */
.widget-card-tile {
    background: var(--content-bg-colour);
    border: var(--widget-border-width, 1px) solid
        var(--widget-border-colour, var(--content-border-colour));
    border-radius: 12px;
    box-shadow: var(--widget-shadow, none);
    padding: var(--widget-padding, var(--widget-padding-y, 1rem) var(--widget-padding-x, 1.25rem));
    min-width: 0;
}

.widget-card-tile--muted {
    opacity: 0.55;
}

@media screen and (max-width: 600px) {
    .widget-search {
        border: 0px;
    }

    .widget-search .form-actions {
        display: none;
    }

    .widget-search {
        background-color: transparent;
    }

    .widget-search form.advanced {
        background-color: white;
        border: 1px;
    }

    .widget-search form:not(.advanced) .field {
        padding-right: 0px;
        padding-left: 0px;
        padding-bottom: 0px;
        padding-top: 0.5rem;
    }
}

.widget.disabled {
    opacity: 0.7;
}

.widget-header {
    margin-bottom: var(--widget-header-spacing, 20px);
}

.add-widget {
    display: none;
    flex-grow: 1;
    height: 1.5rem; 
    position: relative;
    background-color: #C7F4F3;
    margin-bottom: 1rem;
}

.add-widget::before {
    content: '';
    position: absolute;
    top: 50%; 
    left: 0;
    width: 100%;
    border-top: 2px dashed #1ACBB6;
    z-index: 1; 
}

.editing .add-widget {
    display: block;
}

.plus {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 1.5rem;
    height: 1.5rem;
    transform: translate(-50%, -50%); 
    z-index: 20; 
    color: #1ACBB6;
    background-color: white;
    border-radius: 50%;
    border: 1px solid #1ACBB6;
}

/* Forms */
fieldset {
    position: relative;
}

.form-actions {
    border-bottom-right-radius: 11px;
    border-bottom-left-radius: 11px;
}

.form-status {
    background-color: white;
    border: 1px solid #D0CCD5;
    padding: 5px 10px;
    border-radius: var(--buttons-border-radius)
}

input:disabled,
select:disabled {
    background-color: #f5f5f5;
    cursor: not-allowed;
}

.field.required label:after {
    content: '*';
    color: #F0435A;
}

label.font-medium {
    font-weight: 900;
}

.hidden-field {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    z-index: 0;
    pointer-events: none;
}

form:not(.advanced) .field.advanced {
    display: none;
}

.toggle-link {
    display: inline-block;
    margin-top: 10px;
    cursor: pointer;
    color: #0077cc;
    text-decoration: underline;
}

#toggle-advanced:checked ~ .field.advanced,
#toggle-advanced:checked ~ .form-actions {
    display: block;
}

.array-field td {
    padding: 0 0.75rem;
    border: none;
    border-bottom: 1px solid #ddd;
}

.array-field td:first-child {
    padding-left: 0;
}

.array-field td:last-child {
    padding-right: 0;
}

/* Inputs inside array rows render with their default chrome at all
   times — previously they were stripped of background/shadow/border
   when not :hover or :focus, which made them look like static text
   and required users to hover the cell before the editable field
   was visible. The standard input styling (set on the element by
   inputs/input.blade.php) does the right thing on its own. */

table .field,
table .static-field {
    padding: 0px;
}

table tr:nth-child(n+2) .field label, 
table tr:nth-child(n+2) .static-field label {
    display: none;
}

/* Buttons */
button {
    border-radius: var(--buttons-border-radius) !important;
}

.link-button {
    background-color: var(--button-primary-bg);
    color: var(--button-primary-fg);
    border-radius: var(--buttons-border-radius);
}

/* Segments of a page-action button group (actions/partials/button-group)
   join edge to edge: only the group's outer corners keep the brand radius.
   !important because the global button rule above is. */
.page-action-group .btn-seg-first {
    border-top-right-radius: 0 !important;
    border-bottom-right-radius: 0 !important;
}

.page-action-group .btn-seg-mid {
    border-radius: 0 !important;
}

.page-action-group .btn-seg-last {
    border-top-left-radius: 0 !important;
    border-bottom-left-radius: 0 !important;
}

/* Button label casing, from the ui::buttons-text-transform brand setting.
   Carried by ui::actions.button and ui::actions.link-button in place of the
   `uppercase` utility they used to hardcode; the fallback keeps that
   behaviour on an instance whose branding CSS predates the setting.

   !important because Tailwind's .uppercase / .normal-case utilities live in
   the app stylesheet and would otherwise win by load order wherever a caller
   has also passed one through $attributes. */
.btn-label {
    text-transform: var(--buttons-text-transform, uppercase) !important;
}

.uppy-StatusBar-actionBtn {
    background-color: var(--brand-primary-colour) !important;
}

/* File Uploader */
.error-status {
    color: red;
    font-weight: 900;
}

.dragover .file-area {
    background-color: #c0c0c0;
}

.toolbar-item {
    border-right: 1px solid rgb(209 213 219);
    padding: 0.5rem;
}

.uppy-Dashboard-Item-preview img.uppy-Dashboard-Item-previewImg,
.file-area .file-preview {
    border-radius: 3px;
    height: 100%;
    object-fit: contain;
    transform: translateZ(0);
    width: 100%;
    border: 1px solid #e5e7eb;
}

.uppy-Dashboard-Item-preview img.uppy-Dashboard-Item-previewImg {
    min-height: 100%;
}

.uppy-StatusBar-actions {
    justify-content: end;
}

.fileItem[data-state="active"] .file-delete {
    display: inline-block;
}

.fileItem[data-state="deleted"] .file-restore {
    display: inline-block;
}

.fileItem[data-state="deleted"] .file-thumb {
    opacity: 0.1;
}

.file-delete {
    right: -8px;
    top: -8px;
    position: absolute; 
}

.file-restore {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 2;
}

.file-status {
    font-weight: 900;
}

.restriction-error {
    position: fixed;
    top: -4rem;
    background-color: #F0435A;
    width: 100%;
    padding: 1rem;
    border-radius: 6px;
    font-weight: 900;
    font-size: smaller;
}

/* Icons */
.icon {
    width: 24px;
    height: 24px;
    margin-right: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    color: var(--brand-primary-colour);
}

/* Tasks */
.task-list {
    display: flex;
    flex-direction: column;
}

.task-item {
    display: flex;
    align-items: center;
    padding: 10px 15px;
    border-top: 1px solid #ddd;
    background-color: white;
}

.task-item.overdue {
    color: #721c24;
    background-color: #f8d7da;
}

.task-item.locked {
    background-color: #D0CCD555;
}

.task-item.complete .task-icon {
    color: #1ACBB6;
}

.task-item.incomplete .task-icon {
    color: #D0CCD5;
}

.task-item.locked .task-icon {
    color: #D0CCD5;
}

.icon.task-icon {
    margin-right: 0px;
}

.overdue .icon {
    color: #721c24;
}

.task-content .task-title {
    font-size: 16px;
    font-weight: 400;
    margin: 0;
    line-height: 1.2;
}

.task-content .task-title strong {
    font-weight: bold;
}

.task-content .task-due,
.task-content .task-received {
    font-size: 14px;
    color: #555;
    margin: 5px 0 0;
}

.task-content .task-title {
    font-size: 16px;
    font-weight: 400;
    margin: 0;
    line-height: 1.2;
}
  
.task-content .task-title strong {
    font-weight: bold;
}
  
.task-content .task-due,
.task-content .task-received {
    font-size: 14px;
    color: #555;
    margin: 5px 0 0;
}

/* Table toolbar group: the datasets view control and the section-filter
   dropdown sit as ONE control — the two buttons share a border and only
   the group's outer ends are rounded (DEV-39). Each child is a wrapper
   (the datasets root span / the dropdown's relative div) holding the
   button, so the radius rules address the button one level down. A group
   with a single child keeps its full pill. */
.table-toolbar-group > :not(style):not(script):not(template) {
    display: inline-flex;
    align-items: stretch;
}

/* A member may carry its own <style>/<script> (the datasets control does);
   those must never be laid out as group members. */
.table-toolbar-group > style,
.table-toolbar-group > script,
.table-toolbar-group > template {
    display: none;
}

/* !important for the same reason as .segmented-toggle__option: the global
   `button { border-radius: var(--buttons-border-radius) !important }` rule
   would otherwise put the site's button radius back on the joined sides. */
.table-toolbar-group > :not(:first-child) > button {
    border-top-left-radius: 0 !important;
    border-bottom-left-radius: 0 !important;
    margin-left: -1px;
}

.table-toolbar-group > :not(:last-child) > button {
    border-top-right-radius: 0 !important;
    border-bottom-right-radius: 0 !important;
}

/* The open / active member sits above its neighbour so its border wins
   where the two overlap. */
.table-toolbar-group > * > button:hover,
.table-toolbar-group > * > button[aria-expanded="true"],
.table-toolbar-group > * > .ds-adv-btn--on {
    position: relative;
    z-index: 1;
}

/* Notices.

   Every alert surface — <x-ui::alert>, <x-ui::page-banner>, the isolation
   and console banners in layouts/page.blade.php and the session flash
   notices below — paints from the alert brand settings (DEV-39). Each
   --alert-<type>-* property is a light/dark colour pair, so the dark
   theme arrives through html.dark:root and nothing here needs a dark rule.
   The Tailwind palettes these replaced (bg-amber-50 and friends) were never
   remapped by dark-mode.css, which is how a pale yellow banner survived
   onto the slate page. */
.ui-alert {
    background-color: var(--alert-info-bg);
    border-color: var(--alert-info-border);
    color: var(--alert-info-fg);
}

.ui-alert--info {
    background-color: var(--alert-info-bg);
    border-color: var(--alert-info-border);
    color: var(--alert-info-fg);
}

.ui-alert--success {
    background-color: var(--alert-success-bg);
    border-color: var(--alert-success-border);
    color: var(--alert-success-fg);
}

.ui-alert--warning {
    background-color: var(--alert-warning-bg);
    border-color: var(--alert-warning-border);
    color: var(--alert-warning-fg);
}

.ui-alert--danger {
    background-color: var(--alert-danger-bg);
    border-color: var(--alert-danger-border);
    color: var(--alert-danger-fg);
}

/* Supporting text inside an alert: the same ink, stepped back. */
.ui-alert__muted {
    opacity: 0.8;
}

/* A link or button inside an alert keeps the alert's ink. */
.ui-alert__link {
    color: inherit;
    text-decoration: underline;
}

.ui-alert__link:hover {
    opacity: 0.8;
}

/* A copyable value inside a banner sits on the content surface, not the
   alert tint, so it reads as a field. */
.ui-alert__code {
    background-color: var(--content-bg-colour, #fff);
    border-color: var(--content-border-colour, #e5e7eb);
    color: var(--content-text-colour, #1f2937);
}

/* Session flash notices (layouts/page.blade.php loops success / error /
   warning / info). Same box as before; the colours now follow the alert
   settings above. */
.alert-success,
.alert-error,
.alert-warning,
.alert-info {
    padding: 12px 16px;
    border-radius: 12px;
    border: 1px solid;
    font-size: 1rem;
    margin: 2rem;
    margin-top: 0;
}

.alert-success {
    color: var(--alert-success-fg);
    background-color: var(--alert-success-bg);
    border-color: var(--alert-success-border);
}

.alert-error {
    color: var(--alert-danger-fg);
    background-color: var(--alert-danger-bg);
    border-color: var(--alert-danger-border);
}

.alert-warning {
    color: var(--alert-warning-fg);
    background-color: var(--alert-warning-bg);
    border-color: var(--alert-warning-border);
}

.alert-info {
    color: var(--alert-info-fg);
    background-color: var(--alert-info-bg);
    border-color: var(--alert-info-border);
}

/* Passwords */
.password-wrapper {
    display: flex;
    width: 100%;
}

.password-field {
    flex: 1;
    padding: 10px 12px;
    border: 1px solid #ccc;
    border-radius: 6px 0 0 6px;
    font-size: 1rem;
}

.toggle-password {
    padding: 0 12px;
    background-color: #f1f1f1;
    border: 1px solid #ccc;
    border-left: none;
    border-radius: 0 6px 6px 0 !important;
    cursor: pointer;
    font-size: 1rem;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #555;
    transition: background 0.2s;
}

.toggle-password:hover {
    background-color: #e2e2e2;
}

/* API */
#progress-bar {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 4px;
    background-color: var(--brand-primary-colour);
    transition: width 0.5s ease;
    opacity: 0;
    z-index: 1500;
}

/* Dropdowns */
.dropdown {
    position: relative;
    display: inline-flex;
}

.dropdown a {
    position: relative;
    display: inline-flex;
}

.dropdown-toggle {
    border: none;
    cursor: pointer;
}

.dropdown-split .dropdown-toggle {
    border-top-left-radius: 0px !important;
    border-bottom-left-radius: 0px !important;
    padding-left: 0.5rem;
}

.dropdown-split a {
    border-top-right-radius: 0px !important;
    border-bottom-right-radius: 0px !important;
    border-right: 1px solid white;
    padding-right: 0.5rem;
}
.dropdown-menu {
    display: none;
    position: absolute;
    background-color: #f1f1f1;
    min-width: 160px;
    box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
    z-index: 1;
    top: 40px;
}

.dropdown-menu a {
    color: black;
    padding: 12px 16px;
    text-decoration: none;
    display: block;
}

.dropdown-menu a:hover {
    background-color: #ddd;
}

.dropdown.show .dropdown-menu {
    display: block;
}

/* Dialog */
dialog {
    border-radius: 16px;
    max-height: 75vh;
    min-height: 75vh;
    width: 80vw;
}

dialog::backdrop {
    background: rgba(0, 0, 0, 0.2);
    backdrop-filter: blur(3px);
}

dialog .header {
    z-index: 100;
    position: sticky;
    top: 0px;
    background-color: white;
    border-bottom: 1px solid #ddd;
    padding-top: 1rem;
}

dialog .header h2 {
    margin-bottom: 1rem;
}

dialog nav {
    border-top: 1px solid #ddd;
}

#popup-inner {
    overflow-y: hidden;
    display: flex;
}

dialog .content {
    overflow-y: auto;
}

dialog .widget {
    border: none;
    border-left: 1px solid #ddd;
    /*border-radius: 0; */
}

@media screen and (max-width: 600px) {
    dialog {
        border-bottom-left-radius: 0px;
        border-bottom-right-radius: 0px;
        bottom: 0px;
        top: unset;
        margin: 0px;
        max-height: 80vh;
        min-height: unset;
        min-width: 100vw;
        animation: fadeInUp 0.3s ease-out;
    }   

    dialog nav.bg-white {
        background-color: #f8f8f8;
    }

    dialog .content {
        padding-bottom: 0px;
        padding-top: 1rem;
    }

    dialog .widget {
        border: none;
    }

    dialog #popup-inner {
        padding: 0px;
    }

    dialog::before {
        content: '';
        position: absolute;
        top: 0;
        left: 50%;
        transform: translateX(-50%);
        width: 50px;
        height: 5px;
        background-color: #ccc;
        border-radius: 9999px;
        margin-top: 8px;
        z-index: 150;
    }

    .section-info {
        display: none;
    }
}

@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(30%);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}


/* The page header (hero or in-page variant) sits as a static
   first child of .main-panel now that .main-sections owns the
   scroll. No `position: sticky` needed — header naturally stays
   above the scrollable region because it's structurally above it.

   .page-header (in-page variant, when hero is disabled) inherits
   its background from the page element behind it. */

/* Page hero banner — domain-coloured header strip with sticker icon.
   `position: relative` is essential: the hero's children
   (.page-hero-banner__bg svg, .page-hero-banner__label,
   .page-hero-banner__sticker) all use `position: absolute` and
   anchor against this element. Without it they escape to the next
   positioned ancestor (likely <body>), making the title float
   halfway down the page. */
.page-hero-banner {
    --banner-primary: var(--brand-primary-colour);
    --banner-tint: 8;
    --banner-tint-deep: calc(var(--banner-tint) * 2);
    --banner-bg: color-mix(in srgb, var(--banner-primary) calc(var(--banner-tint) * 1%), white);
    --banner-bg-deep: color-mix(in srgb, var(--banner-primary) calc(var(--banner-tint-deep) * 1%), white);
    --banner-soft: color-mix(in srgb, var(--banner-primary) 45%, white);
    --banner-ink: color-mix(in srgb, var(--banner-primary) 75%, white);
    --banner-ink-deep: var(--banner-primary);

    position: relative;
    min-height: 104px;
    flex-shrink: 0;
    background: var(--banner-bg);
    border-bottom: 1px solid #ececec;
    /* No `overflow: hidden` here — it clips the action-dropdown menu
       that opens DOWNWARD past the banner's bottom edge. The
       background ink/SVG that NEEDS clipping is in `__bg` below;
       clipping is scoped there so decorative shapes still stay
       inside the banner while interactive popovers (dropdowns,
       tooltips) can escape. */
}

.page-hero-banner__bg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 104px;
    pointer-events: none;
    /* Clip the decorative shapes here, not on the parent banner.
       See `.page-hero-banner` above. */
    overflow: hidden;
}

.page-hero-banner__label {
    position: absolute;
    left: 2rem;
    top: 0;
    bottom: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    gap: 16px;
    min-width: 0;
    /* Decorative container — text + image only. The previous
       `right: 240px` was clipping the label to make room for the
       actions/sticker zone, but absolute positioning + an empty
       box was still catching pointer events over the action
       buttons, blocking clicks. Drop the right constraint and
       make the box itself click-through; any interactive child
       inside the label can opt back in with pointer-events: auto. */
    pointer-events: none;
}

/* When an image is present, bottom-align the image and text together
   so their bottom edges share a baseline. The padding keeps the text
   off the banner's bottom edge. */
.page-hero-banner__label--with-image {
    align-items: flex-end;
    padding-bottom: 12px;
}

.page-hero-banner__crumb {
    font-size: 10px;
    letter-spacing: 1.6px;
    font-weight: 700;
    text-transform: uppercase;
    color: var(--banner-ink-deep);
    opacity: 0.75;
}

.page-hero-banner__sticker {
    position: absolute;
    right: 40px;
    top: 50%;
    transform: translateY(-50%) rotate(-4deg);
    z-index: 2;
    width: 62px;
    height: 62px;
    border-radius: 12px;
    background: #fff;
    border: 1.5px solid rgba(0, 0, 0, 0.08);
    box-shadow:
        0 2px 0 rgba(0, 0, 0, 0.06),
        0 4px 10px rgba(0, 0, 0, 0.05);
    display: flex;
    align-items: center;
    justify-content: center;
}

/* When actions sit alongside the domain icon, the sticker grows
   wider into a horizontal pill. Drop the tilt so action buttons
   stay readable, and let width expand to fit the buttons. */
.page-hero-banner__sticker--with-actions {
    width: auto;
    height: auto;
    min-height: 62px;
    transform: translateY(-50%);
    padding: 8px 12px;
    gap: 12px;
}

.page-hero-banner__sticker-icon {
    width: 32px !important;
    height: 32px !important;
    color: var(--banner-primary, var(--brand-primary-colour)) !important;
    stroke: var(--banner-primary, var(--brand-primary-colour)) !important;
    flex-shrink: 0;
}

.page-hero-banner__sticker-actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

/* Page actions carry no paint of their own: each renders through the
   btn-* variant its PageActionType maps to in the Page Actions brand
   settings, so rest, hover and focus all come from the brand vars.
   The header used to repaint every action white here, which the
   variant's own hover rule then snapped back to the brand colour. */

/* The mobile FAB overlay is purely a small-screen affordance — at
   desktop sizes it serves no purpose but still occupies a flex slot
   inside .page-actions, adding a phantom gap. Hide it by default;
   the mobile media query re-enables it when the toggle is checked. */
.page-fab-overlay {
    display: none;
}

/* When a header image is present, bottom-align the image and text
   together so their baselines match. Desktop-only — on mobile the
   page-header switches to flex-direction: column-reverse, and using
   align-items:flex-end there would right-align children instead of
   bottom-aligning them. */
@media screen and (min-width: 601px) {
    .page-header--with-image {
        align-items: flex-end;
    }
}

/* Shared header content styling — used by both the in-page header
   row and the hero banner's __label, so the two render identical
   content at identical sizes. The hero only adds background tint,
   sticker, and the small __crumb line above the title. */
.page-header__image {
    flex-shrink: 0;
    width: 72px;
    height: 72px;
    border-radius: 12px;
    object-fit: cover;
    background: #fff;
    border: 1.5px solid rgba(0, 0, 0, 0.08);
    box-shadow:
        0 2px 0 rgba(0, 0, 0, 0.06),
        0 4px 10px rgba(0, 0, 0, 0.05);
}

.page-header__text {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
}

/* Context-page breadcrumb bar — a compact strip above the page title.
   Smaller and more detailed than the title's own short crumb: a back
   button to the immediate parent plus the full ancestor trail. Aligns
   with the in-page header's 32px horizontal padding. */
.page-breadcrumb {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    padding: 12px 32px 0;
    font-size: 0.8125rem;
    line-height: 1.4;
    min-width: 0;
}

.page-breadcrumb__back {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 10px 2px 7px;
    border-radius: 9999px;
    color: #4b5563;
    background: color-mix(in srgb, var(--brand-primary-colour) 6%, #f3f4f6);
    border: 1px solid #e5e7eb;
    font-weight: 500;
    text-decoration: none;
    white-space: nowrap;
    transition: background-color 0.15s ease, color 0.15s ease;
}

.page-breadcrumb__back:hover {
    color: #1f2937;
    background: color-mix(in srgb, var(--brand-primary-colour) 12%, #eceef1);
}

.page-breadcrumb__back-icon {
    flex-shrink: 0;
}

.page-breadcrumb__trail {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
    margin: 0;
    padding: 0;
    list-style: none;
    min-width: 0;
}

.page-breadcrumb__item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
}

/* Slash separator before every crumb except the first. */
.page-breadcrumb__item + .page-breadcrumb__item::before {
    content: "/";
    color: #9ca3af;
    font-weight: 300;
}

.page-breadcrumb__link,
.page-breadcrumb__text,
.page-breadcrumb__current {
    display: inline-block;
    max-width: 220px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: bottom;
}

.page-breadcrumb__link {
    color: #6b7280;
    text-decoration: none;
}

.page-breadcrumb__link:hover {
    color: #374151;
    text-decoration: underline;
}

.page-breadcrumb__text {
    color: #6b7280;
}

.page-breadcrumb__current {
    max-width: 260px;
    color: #374151;
    font-weight: 500;
}

.page-header__title-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    min-width: 0;
}

.page-header__title {
    margin: 0;
    letter-spacing: -0.2px;
    line-height: 1.2;
    min-width: 0;
}

.page-header__pill {
    display: inline-flex;
    align-items: center;
    /* The dot needs room. Without this it is jammed against the label, which
       the meta variant below has always known. */
    gap: 6px;
    padding: 2px 10px;
    border-radius: 9999px;
    font-size: 12px;
    /* The Foundations → Pills font weight, like every other pill. */
    font-weight: var(--pill-font-weight, 700);
    line-height: 1.4;
    white-space: nowrap;
}

.page-header__permission-badge {
    position: relative;
    display: inline-flex;
    align-items: center;
}

.page-header__permission-badge [x-cloak] {
    display: none !important;
}

.page-header__permission-badge-trigger {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 3px;
    border-radius: 9999px;
    color: #6b7280;
    background: transparent;
    border: none;
    cursor: pointer;
    line-height: 0;
}

.page-header__permission-badge-trigger:hover,
.page-header__permission-badge-trigger[aria-expanded="true"] {
    color: #374151;
    background: #f3f4f6;
}

.page-header__permission-popover {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    z-index: 50;
    min-width: 280px;
    max-width: min(560px, 90vw);
    width: max-content;
    padding: 10px 12px;
    background: #ffffff;
    border: 1px solid #e5e7eb;
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
}

.page-header__permission-popover-head {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.4px;
    color: #6b7280;
    margin-bottom: 6px;
}

.page-header__permission-popover-note {
    margin: 0;
    font-size: 13px;
    color: #4b5563;
}

.page-header__permission-list {
    margin: 0;
    padding: 0;
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.page-header__permission-fqn {
    display: inline-block;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 12px;
    color: #1f2937;
    background: #f3f4f6;
    padding: 2px 6px;
    border-radius: 4px;
    white-space: nowrap;
}

.page-header__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 14px;
    margin-top: 4px;
    font-size: 13px;
    color: #4b5563;
}

.page-header__meta-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
}

.page-header__meta-icon {
    width: 14px;
    height: 14px;
    flex-shrink: 0;
    color: #6b7280;
}

.page-header__meta-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 2px 10px;
    border-radius: 9999px;
    font-size: 12px;
    /* The Foundations → Pills font weight: a status on the meta line (a
       project's RAG, say) is a pill like the one beside the title. */
    font-weight: var(--pill-font-weight, 700);
    line-height: 1.4;
    white-space: nowrap;
}

.page-header__meta-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    flex-shrink: 0;
}

/* A pill that DOES something — the org-unit pill opens the sharing drawer.
   It has to read as a control, not a label, before it is clicked: a pointer
   cursor, a trailing glyph, and a tint that deepens on hover. */
.page-header__pill--action,
.page-header__meta-pill--action {
    cursor: pointer;
    text-decoration: none;
    transition: filter .12s ease-in-out, box-shadow .12s ease-in-out;
}

.page-header__pill--action:hover,
.page-header__meta-pill--action:hover {
    filter: brightness(0.96);
    box-shadow: 0 1px 3px rgba(0, 0, 0, .12);
}

.page-header__pill-glyph {
    width: 11px;
    height: 11px;
    flex-shrink: 0;
    opacity: .75;
}

/* Page-header mobile (post-base overrides) — these rules must be
   defined AFTER the desktop .page-header__* rules above so the cascade
   applies them last at small viewports. Otherwise the base rules win
   even when the media query matches. */
@media screen and (max-width: 600px) {
    /* Stack vertically (image, text, actions) in natural DOM order.
       Image keeps its 72px intrinsic width via align-self instead of
       being stretched. Bumped left/right padding gives the content
       more breathing room from the viewport edge on small screens. */
    .page-header {
        flex-direction: column;
        align-items: stretch;
        padding-left: 2.5rem;
        padding-right: 2.5rem;
    }

    .page-header__image {
        align-self: flex-start;
    }

    /* On mobile, force the title to claim the full width of its
       title-row so any inline pills wrap to a new line below it.
       Prevents long titles + pills from pushing off-screen. */
    .page-header__title {
        flex-basis: 100%;
    }

    /* Stack meta items vertically and left-align them so each line
       starts at the same x as the title above. */
    .page-header__meta {
        flex-direction: column;
        align-items: flex-start;
        gap: 6px;
    }

    /* Hero banner mobile layout — desktop uses an absolute-positioned
       __label overlay on top of a fixed-height (104px) decorative
       banner. That layout breaks on phone widths because the wrapped
       title + pills + meta extend taller than 104px and visually
       leak up onto the page-tab nav above (which is itself lifted
       above the header via order: -1).

       On mobile, drop the absolute positioning so the label sits in
       normal flow and the banner grows to contain whatever content
       it holds. Image stacks above the text rather than next to it. */
    .page-hero-banner {
        min-height: 0;
        padding: 1rem 1.5rem;
    }

    /* Keep image + text side-by-side on mobile (don't stack) — just
       drop the absolute positioning so the banner can size to fit.
       The text block (__text) handles its own internal stacking. */
    .page-hero-banner__label,
    .page-hero-banner__label--with-image {
        position: static;
        flex-direction: row;
        align-items: flex-start;
        gap: 0.75rem;
        padding-bottom: 0;
        min-width: 0;
    }

    /* Lock the image to its intrinsic width so the text gets the
       remaining row. min-width: 0 lets the text block shrink and wrap
       instead of pushing the row past the banner edge. */
    .page-hero-banner__label .page-header__image {
        flex-shrink: 0;
    }

    .page-hero-banner__label .page-header__text {
        min-width: 0;
        flex: 1;
    }

    /* Hide the decorative SVG + sticker on mobile — the banner is now
       a content carrier rather than a wide decorative strip, and the
       sticker (positioned with right: 40px, transform: translateY(-50%))
       overlaps the stacked label content. */
    .page-hero-banner__bg,
    .page-hero-banner__sticker {
        display: none;
    }
}
/* Brand-themed button variants. Used by ui::actions.button and
   ui::actions.link-button via the variant => class string map.
   Backgrounds + foregrounds resolve to brand-settings CSS custom
   properties so a brand colour change in the DB propagates without
   a rebuild. Ghost variants have their own text/border settings,
   which SHIP as references to the solid colours they always borrowed
   (--button-ghost-primary-fg -> var(--button-primary-bg)); the var()
   fallbacks here repeat that chain for a stylesheet served before the
   settings were imported.

   Hover colours are brand settings too: every variant has hover
   bg/fg/border settings (blank = the rest value, so the fallback is the
   rest var). The shared fade and shadow live with the .btn box rules
   below. :not(:disabled) keeps a disabled button on its own opacity. */
.btn-primary {
    background-color: var(--button-primary-bg);
    color: var(--button-primary-fg);
    border: 1px solid var(--button-primary-bg);
}
.btn-primary:hover:not(:disabled) {
    background-color: var(--button-primary-hover-bg, var(--button-primary-bg));
    color: var(--button-primary-hover-fg, var(--button-primary-fg));
    border-color: var(--button-primary-hover-border, var(--button-primary-bg));
}
.btn-secondary {
    background-color: var(--button-secondary-bg);
    color: var(--button-secondary-fg);
    border: 1px solid #d1d5db;
}
.btn-secondary:hover:not(:disabled) {
    background-color: var(--button-secondary-hover-bg, var(--button-secondary-bg));
    color: var(--button-secondary-hover-fg, var(--button-secondary-fg));
    border-color: var(--button-secondary-hover-border, #d1d5db);
}
.btn-danger {
    background-color: var(--button-danger-bg);
    color: var(--button-danger-fg);
    border: 1px solid var(--button-danger-bg);
}
.btn-danger:hover:not(:disabled) {
    background-color: var(--button-danger-hover-bg, var(--button-danger-bg));
    color: var(--button-danger-hover-fg, var(--button-danger-fg));
    border-color: var(--button-danger-hover-border, var(--button-danger-bg));
}
.btn-ghost-primary {
    background-color: transparent;
    color: var(--button-ghost-primary-fg, var(--button-primary-bg));
    border: 1px solid var(--button-ghost-primary-border, var(--button-primary-bg));
}
.btn-ghost-primary:hover:not(:disabled) {
    background-color: var(--button-ghost-primary-hover-bg, transparent);
    color: var(--button-ghost-primary-hover-fg, var(--button-ghost-primary-fg, var(--button-primary-bg)));
    border-color: var(--button-ghost-primary-hover-border, var(--button-ghost-primary-border, var(--button-primary-bg)));
}
.btn-ghost-danger {
    background-color: transparent;
    color: var(--button-ghost-danger-fg, var(--button-danger-bg));
    border: 1px solid var(--button-ghost-danger-border, var(--button-danger-bg));
}
.btn-ghost-danger:hover:not(:disabled) {
    background-color: var(--button-ghost-danger-hover-bg, transparent);
    color: var(--button-ghost-danger-hover-fg, var(--button-ghost-danger-fg, var(--button-danger-bg)));
    border-color: var(--button-ghost-danger-hover-border, var(--button-ghost-danger-border, var(--button-danger-bg)));
}
/* Ghost secondary's border default is a fixed neutral, not
   var(--button-secondary-bg): secondary backgrounds are often white-on-white
   against the content surface and would disappear. It matches the solid
   secondary variant's border colour. */
.btn-ghost-secondary {
    background-color: transparent;
    color: var(--button-ghost-secondary-fg, var(--button-secondary-fg));
    border: 1px solid var(--button-ghost-secondary-border, #d1d5db);
}
.btn-ghost-secondary:hover:not(:disabled) {
    background-color: var(--button-ghost-secondary-hover-bg, transparent);
    color: var(--button-ghost-secondary-hover-fg, var(--button-ghost-secondary-fg, var(--button-secondary-fg)));
    border-color: var(--button-ghost-secondary-hover-border, var(--button-ghost-secondary-border, #d1d5db));
}

/* The button box itself — what every btn-* variant sits on. Named here
   rather than spelt out as a dozen utilities on each element, so a button
   is `btn btn-label btn-secondary btn-md` and custom CSS can target it.
   The radius reads the brand setting directly (buttons-border-radius), so
   no !important is needed to carry it; the colours come from the variant
   rules above. */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--buttons-border-radius, 0.375rem);
    font-weight: 600;
    letter-spacing: 0.1em;
    transition: all 0.15s ease-in-out;
}
.btn:focus {
    outline: none;
}
/* The fade and the shadow are brand settings shared by every variant
   (ui::buttons-hover-opacity, ui::buttons-hover-shadow). Blank is omitted
   from the branding stylesheet, so the fallbacks here ARE the shipped look:
   .9 on a solid button, .8 on a ghost, no shadow. The per-variant
   :hover rules above change only the colours. The shadow is hover-only
   and steps aside for the keyboard focus ring below. */
.btn:hover:not(:disabled),
.btn:focus:not(:disabled) {
    opacity: var(--buttons-hover-opacity, 0.9);
}
.btn-ghost-primary:hover:not(:disabled),
.btn-ghost-primary:focus:not(:disabled),
.btn-ghost-secondary:hover:not(:disabled),
.btn-ghost-secondary:focus:not(:disabled),
.btn-ghost-danger:hover:not(:disabled),
.btn-ghost-danger:focus:not(:disabled) {
    opacity: var(--buttons-hover-opacity, 0.8);
}
.btn:hover:not(:disabled):not(:focus-visible) {
    box-shadow: var(--buttons-hover-shadow, none);
}
.btn:focus-visible {
    box-shadow: 0 0 0 2px #fff, 0 0 0 4px #6366f1;
}
.btn-danger:focus-visible,
.btn-ghost-danger:focus-visible {
    box-shadow: 0 0 0 2px #fff, 0 0 0 4px #ef4444;
}
.btn:disabled,
.btn[aria-disabled="true"] {
    opacity: 0.5;
    cursor: not-allowed;
}
.btn-sm {
    padding: 0.25rem 0.75rem;
    font-size: 0.75rem;
    line-height: 1rem;
}
.btn-md {
    padding: 0.5rem 1rem;
    font-size: 0.75rem;
    line-height: 1rem;
}
.btn-lg {
    padding: 0.625rem 1.25rem;
    font-size: 0.875rem;
    line-height: 1.25rem;
}

/* -------------------------------------------------------------------------
   Pill stack (PillStackColumn cell) — a row of coloured pills, one per item.

   Mobile-first: full labelled pills, wrapped, no hover required.
   >= 640px: collapses to overlapping initial-only circles that fan out to
   full labelled pills when the cell is hovered.

   The expanded pills are an absolutely-positioned overlay. The .pill-stack
   box keeps a fixed collapsed footprint (--stack-w, set inline per cell), so
   hovering never changes the column width. If it did, the table would
   re-lay-out and shift the cell out from under the cursor, flip-flopping the
   hover state (an expand/collapse loop). Per-pill colours arrive as the
   --pill-bg / --pill-fg / --pill-bd custom properties set inline.
   ------------------------------------------------------------------------- */
.pill-stack__items {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px;
}

.pill-stack__pill {
    display: inline-flex;
    align-items: center;
    box-sizing: border-box;
    height: 24px;
    padding: 0 10px;
    border-radius: 9999px;
    font-size: 11px;
    /* The Foundations → Pills font weight, like every other pill. */
    font-weight: var(--pill-font-weight, 700);
    line-height: 1;
    white-space: nowrap;
    background: var(--pill-bg);
    color: var(--pill-fg);
    border: 1px solid var(--pill-bd);
}

/* Mobile carries meaning in the label, so the initial is hidden there. */
.pill-stack__initial {
    display: none;
}

@media (min-width: 640px) {
    /* Fixed collapsed footprint — the stable hover target. */
    .pill-stack {
        position: relative;
        width: var(--stack-w);
        height: 24px;
    }

    /* Pills overlay the footprint; expanding them never resizes .pill-stack. */
    .pill-stack__items {
        position: absolute;
        top: 0;
        left: 0;
        height: 24px;
        flex-wrap: nowrap;
        gap: 0;
    }

    /* Collapsed state: an overlapping circle showing just the initial. */
    .pill-stack__pill {
        width: 24px;
        padding: 0;
        justify-content: center;
        overflow: hidden;
        transition: width 0.18s ease, padding 0.18s ease, margin-left 0.18s ease;
    }

    .pill-stack__pill + .pill-stack__pill {
        margin-left: -8px;
    }

    .pill-stack__initial {
        display: inline;
    }

    .pill-stack__label {
        display: none;
    }

    /* Hovering the footprint fans the overlay out into full labelled pills,
       floating above the neighbouring cell. The strip itself stays
       transparent — only the (opaque) pills sit above the row. */
    .pill-stack:hover .pill-stack__items {
        z-index: 20;
        gap: 4px;
        background: transparent;
    }

    .pill-stack:hover .pill-stack__pill {
        width: auto;
        padding: 0 10px;
    }

    .pill-stack:hover .pill-stack__pill + .pill-stack__pill {
        margin-left: 0;
    }

    .pill-stack:hover .pill-stack__initial {
        display: none;
    }

    .pill-stack:hover .pill-stack__label {
        display: inline;
    }

    /* A single-item stack has nothing to collapse or disambiguate, so it
       renders as a plain full pill — no circle, no reserved footprint, no
       hover expansion. */
    .pill-stack--single {
        position: static;
        width: auto;
        height: auto;
    }

    .pill-stack--single .pill-stack__items {
        position: static;
    }

    .pill-stack--single .pill-stack__pill {
        width: auto;
        padding: 0 10px;
    }

    .pill-stack--single .pill-stack__initial {
        display: none;
    }

    .pill-stack--single .pill-stack__label {
        display: inline;
    }
}

/* ── Filter chips ───────────────────────────────────────────────────────
   Lifted out of the floating table's own view, where they were page-local:
   any other surface using them (the message centre's filter pills) got bare
   text unless a floating table happened to be on the same page. */
/* Filter chips: white pills, solid brand fill when active. Sized to
   match the default variant's quick-filter pills (px-3 py-1 text-xs
   font-medium).

   Every colour is an ui::filter-pill-* brand setting layered over
   what the chip did before it existed. Those settings ship blank and
   blank is never emitted (see resources/css.blade.php), so an
   unset instance falls all the way through to the values below. */
.floating-chip {
    display: inline-flex;
    align-items: center;
    padding: 0.25rem 0.75rem;
    border-radius: 9999px;
    font-size: 0.75rem;
    font-weight: 500;
    background: var(--filter-pill-bg, #ffffff);
    border: 1px solid var(--filter-pill-border-colour, var(--content-border-colour, #e5e7eb));
    color: var(--filter-pill-text-colour, #374151);
    text-decoration: none;
    transition: background-color .15s ease, color .15s ease;
}
/* Hover tints the resting colours rather than replacing them, so a
   branded pill darkens toward its own selected state instead of
   snapping back to the stock grey. */
.floating-chip:hover {
    background: #f3f4f6;
    background: color-mix(in srgb, var(--filter-pill-active-bg, var(--button-primary-bg, #111827)) 8%, var(--filter-pill-bg, #ffffff));
    color: var(--filter-pill-active-bg, #111827);
}
/* Active chip defaults to the primary button: the brand's CTA
   background AND its paired foreground, so the label keeps guaranteed
   contrast (a light primary colour with a hardcoded white label
   washed out). Matches the options-dropdown trigger in the same row. */
.floating-chip--active,
.floating-chip--active:hover {
    background: var(--filter-pill-active-bg, var(--button-primary-bg, #111827));
    border-color: transparent;
    color: var(--filter-pill-active-text-colour, var(--button-primary-fg, #ffffff));
}

/* ---------------------------------------------------------------------------
   Avatars — the class contract.

   The Blade component (indicators/avatar) is how a server-rendered avatar is
   built. Some avatars cannot use it: the notification centre and bell render
   their rows from an Alpine array, and the CRM and project board cards bind
   :src on a template, so there is no server-side loop to put a component in.
   Those wear these classes instead and land on exactly the same shape and
   colours, because both routes read the same three custom properties.

   Anything with its own avatar geometry outside this file is a bug the
   `bespoke-avatar-markup` compliance rule will fail on.
   --------------------------------------------------------------------------- */
.avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    overflow: hidden;
    vertical-align: middle;
    box-sizing: border-box;
    /* Set by the modifier below, or inline by a caller that needs an odd size.
       The fallback is the md step, so a bare `.avatar` is still a real box. */
    width: var(--avatar-size, 2rem);
    height: var(--avatar-size, 2rem);
    min-width: var(--avatar-size, 2rem);
    border-radius: var(--avatar-radius, 22%);
    background: var(--avatar-default-bg-colour, #E5E7EB);
    color: var(--avatar-default-text-colour, #374151);
    font-weight: 600;
    letter-spacing: .025em;
    line-height: 1;
    /* Proportional to the box, matching the component's 0.42 ratio, so a
       monogram reads the same whichever route drew it. */
    font-size: calc(var(--avatar-size, 2rem) * 0.42);
}
/* An <img class="avatar"> is the box itself rather than a wrapper around one. */
img.avatar { object-fit: cover; display: inline-block; }

.avatar--xs { --avatar-size: 1.5rem; }
.avatar--sm { --avatar-size: 1.75rem; }
.avatar--md { --avatar-size: 2rem; }
.avatar--lg { --avatar-size: 2.5rem; }
.avatar--xl { --avatar-size: 3.5rem; }

/* A logo drawn whole (the component's fit="contain") sits on plain white, or
   black under html.dark, which sets --avatar-logo-bg-colour (dark-mode.css) —
   not the brand default pair, which is tuned for a monogram and can clash
   with a logo's own colours. The component paints it inline, as it does the
   default pair. */

/* A shadow, not a border: the ring must not change the box size, or a face in
   an overlapping stack would jump when it gains one. */
.avatar--ring { box-shadow: 0 0 0 1px var(--avatar-ring-colour, rgba(0, 0, 0, .12)); }

/* Overlapping group. The negative margin is on the stack rather than on each
   face so a single-item stack has no stray offset. */
.avatar-stack { display: inline-flex; align-items: center; }
.avatar-stack > * + * { margin-left: -0.5rem; }
.avatar-stack__overflow {
    /* Quieter than a face: it is a count, not a person. */
    background: var(--avatar-overflow-bg, #e5e7eb);
    color: var(--avatar-overflow-text, #4b5563);
    font-size: calc(var(--avatar-size, 1.5rem) * 0.36);
}
.avatar-stack__empty { color: #9ca3af; font-size: .75rem; }

/* Activity heat-map (x-ui::data.activity-heatmap). Columns are weeks, rows
   are Monday..Sunday; the grid flows down each column so a week is a
   vertical strip, as on GitLab. Cells are tints of the brand colour mixed
   over the content background, so the five steps track the brand and the
   theme without a colour of their own. Widths derive from --heatmap-weeks so
   the strip fits its band at any window length. */
.activity-heatmap {
    --heatmap-cell: .7rem;
    --heatmap-gap: .2rem;
    --heatmap-empty: #ebedf0;
    --heatmap-label: #6b7280;
    font-size: .7rem;
    line-height: 1;
    overflow-x: auto;
}
.activity-heatmap__months {
    display: grid;
    grid-template-columns: repeat(var(--heatmap-weeks, 52), var(--heatmap-cell));
    gap: var(--heatmap-gap);
    margin-left: calc(1.4rem + var(--heatmap-gap));
    height: 1rem;
    color: var(--heatmap-label);
}
.activity-heatmap__month { white-space: nowrap; }
.activity-heatmap__body { display: flex; gap: var(--heatmap-gap); }
.activity-heatmap__weekdays {
    display: grid;
    grid-template-rows: repeat(7, var(--heatmap-cell));
    gap: var(--heatmap-gap);
    width: 1.4rem;
    color: var(--heatmap-label);
}
.activity-heatmap__weekday { display: flex; align-items: center; }
.activity-heatmap__grid {
    display: grid;
    grid-auto-flow: column;
    grid-template-rows: repeat(7, var(--heatmap-cell));
    grid-auto-columns: var(--heatmap-cell);
    gap: var(--heatmap-gap);
}
.activity-heatmap__cell {
    display: block;
    width: var(--heatmap-cell);
    height: var(--heatmap-cell);
    border-radius: 2px;
    background: var(--heatmap-empty);
}
.activity-heatmap__cell--l1 { background: color-mix(in srgb, var(--brand-primary-colour) 30%, var(--content-bg-colour, #fff)); }
.activity-heatmap__cell--l2 { background: color-mix(in srgb, var(--brand-primary-colour) 55%, var(--content-bg-colour, #fff)); }
.activity-heatmap__cell--l3 { background: color-mix(in srgb, var(--brand-primary-colour) 80%, var(--content-bg-colour, #fff)); }
.activity-heatmap__cell--l4 { background: var(--brand-primary-colour); }
.activity-heatmap__cell--future { visibility: hidden; }
.activity-heatmap__legend {
    display: flex;
    align-items: center;
    gap: var(--heatmap-gap);
    margin-top: .5rem;
    color: var(--heatmap-label);
}
.activity-heatmap__legend-text { margin: 0 .2rem; }
.activity-heatmap__legend-label { margin-left: auto; }

:root {
    /* The surface a chip's tint is mixed toward. A fixed design constant, not
       a brand setting, so it lives here rather than in the generated branding
       stylesheet; dark-mode.css redefines it for the dark ground. */
    --chip-tint-base: #ffffff;
}

/* Colour palette chips.
   ---------------------------------------------------------------------------
   A badge whose colour comes from a palette member rather than a Tailwind
   name. The member supplies ONE colour on --chip-colour (set inline by
   ChipColour::style()); the fill and the edge are tints of it, mixed toward
   --chip-tint-base, which is white on light and slate on dark (set just above
   and in dark-mode.css). So one admin-chosen colour is correct on both grounds
   and nothing here needs a dark-mode counterpart.

   Two literal classes rather than a composed one: Tailwind's purge and the
   brand-colour rule can both see every class ChipColour is able to return.

   The three mixes are the Foundations → Pills fill-style setting's: soft (the
   default) leaves them at 12% fill / 28% edge / 0% ink, so the colour is the
   label on a wash of itself; solid drives all three to 100%, so the colour
   fills the badge and the label becomes the ink that contrasts with it.
   PaletteColour::chip() carries the same three expressions for the badges
   that are painted inline rather than by these classes.

   The rules paint with --chip-fill and --chip-fill-ink rather than the
   element's own --chip-colour / --chip-ink, so that dark-mode.css can point
   them at the derived dark half (--chip-colour-dark / --chip-ink-dark, which
   ChipColour::style() emits beside a literal colour) without the element
   knowing which theme is showing. A chip with no dark half — a brand or
   palette reference, whose property already switches — falls back to its
   light declarations, so nothing that renders today changes. */
.chip-palette,
.chip-palette-solid {
    --chip-fill: var(--chip-colour);
    --chip-fill-ink: var(--chip-ink, var(--chip-tint-base));
}

.chip-palette {
    background-color: color-mix(in srgb, var(--chip-fill) var(--chip-fill-mix, 12%), var(--chip-tint-base));
    color: color-mix(in srgb, var(--chip-fill-ink) var(--chip-ink-mix, 0%), var(--chip-fill));
    border: 1px solid color-mix(in srgb, var(--chip-fill) var(--chip-border-mix, 28%), var(--chip-tint-base));
    /* The Foundations → Pills font weight. The pill view carries no
       Tailwind weight class of its own, so this is the only place a pill's
       weight is decided and one setting reaches every pill on the site. */
    font-weight: var(--pill-font-weight, 700);
}

/* The solid variant, for a counter or a badge that has to carry weight
   whatever the fill style is set to: the colour becomes the fill and the ink
   is the contrast ink computed for that colour (Ink::on(), emitted beside the
   colour by BrandCssResolver). The tint base is only the fallback for a chip
   that reached here without one — before, it was the ink itself, which put a
   white label on a pale colour an administrator had chosen. */
.chip-palette-solid {
    background-color: var(--chip-fill);
    color: var(--chip-fill-ink);
    border: 1px solid var(--chip-fill);
    font-weight: var(--pill-font-weight, 700);
}

/* The inverse variant, for a pill that sits on a dark banner or a
   brand-coloured header, where no chip colour reads: a translucent white wash
   with a white label. It carries no colour of its own, so it needs no
   dark-mode counterpart, and it reads the same font weight as every other
   pill. */
.chip-palette-inverse {
    background-color: rgb(255 255 255 / 0.2);
    color: #fff;
    border: 1px solid rgb(255 255 255 / 0.28);
    font-weight: var(--pill-font-weight, 700);
}

/* A surface with a dark-mode logo renders both images; only the one for the
   current theme shows. The dark one is hidden here, the light one under
   html.dark in dark-mode.css. Hide-only rules, so a caller's own display
   class (block, h-8 …) still governs the image that is showing. */
html:not(.dark) .brand-logo--dark {
    display: none;
}
