/* =============================================================================
 * gyro-layouts.css — the four panel presets. Sprint 45.
 *
 * WHY THIS FILE EXISTS. The owner tested the presets and reported: "Only 1
 * work as intended. Which is Top Menu." He was right, and the reason is
 * structural rather than cosmetic. A preset used to be three booleans and a
 * width handed to Filament, and Filament's layout emits exactly five body
 * classes (vendor filament/filament/resources/views/components/layout/
 * index.blade.php):
 *
 *     fi-body-has-navigation
 *     fi-body-has-sidebar-collapsible-on-desktop
 *     fi-body-has-sidebar-fully-collapsible-on-desktop
 *     fi-body-has-topbar
 *     fi-body-has-top-navigation
 *
 * Top navigation gets a class of its own, so Top looked like a real choice.
 * Classic and Rail emit an IDENTICAL set - both collapsible, neither fully
 * collapsible - so nothing in any stylesheet could tell them apart, and both
 * rendered as the stock sidebar. Minimal differed only by a max-width and one
 * extra boolean. Three of the four were the same screen.
 *
 * The missing piece was a per-preset hook. LayoutPreset::bodyClass() now
 * supplies one and AdminPanelProvider stamps it on <html> from the head, which
 * is what every rule below hangs off.
 *
 * WHERE THE SELECTORS COME FROM. Rule 1 of this project: never guess a Filament
 * class name. Every `fi-` class used here was read out of the installed v5.7
 * source on the server, not inferred from what the markup looks like -
 *
 *     vendor/filament/filament/resources/views/components/layout/index.blade.php
 *     vendor/filament/filament/resources/views/components/layout/base.blade.php
 *     vendor/filament/filament/resources/views/livewire/sidebar.blade.php
 *     vendor/filament/filament/resources/views/livewire/topbar.blade.php
 *     vendor/filament/filament/resources/views/components/sidebar/group.blade.php
 *     vendor/filament/filament/resources/views/components/sidebar/item.blade.php
 *     vendor/filament/filament/dist/theme.css
 *
 * and each block below says which file it came from. Two names that everybody
 * gets wrong, and that are wrong in gyro-theme.css today, are worth stating up
 * front: the nav link is `.fi-sidebar-item-btn`, NOT `.fi-sidebar-item-button`;
 * and a collapsed sidebar is marked by the ABSENCE of `.fi-sidebar-open`, not
 * by `[data-collapsed]` or `.fi-sidebar-collapsed`, neither of which exists.
 *
 * WHY NO !important ANYWHERE. Filament's whole compiled theme.css sits inside
 * `@layer properties / theme / base / components / utilities`. An unlayered
 * rule beats every layered rule regardless of specificity, so this file wins
 * against Filament by virtue of being unlayered. It is linked AFTER
 * gyro-theme.css and gyro-chrome.css, which are also unlayered, so among the
 * three source order decides and this one is last on purpose.
 *
 * The one thing an unlayered rule does NOT beat is an inline style, and Alpine
 * writes `style="display: none"` for every `x-show` that is false. That is why
 * the rail is built out of the state Filament itself renders rather than by
 * fighting `x-show` - see section 2.
 *
 * REM IS 13.5px HERE. gyro-theme.css sets `html { font-size: var(--type-size-
 * base) }` and that token is 13.5px, so Filament's stock `20rem` sidebar is
 * 270px and `4rem` is 54px. Inside a MEDIA QUERY, though, rem is the initial
 * 16px per spec - so `min-width: 64rem` below really is Tailwind's 1024px `lg`
 * breakpoint, the same one Filament's own layout switches on.
 *
 * EVERY VALUE IS A TOKEN, per gyro-chrome.css's rule. Where a preset needs a
 * length the token scale does not name, it is composed from tokens with calc()
 * rather than typed as a literal.
 * ========================================================================== */


/* -----------------------------------------------------------------------------
 * 0. SHARED — density, and the lengths the rail is built from.
 *
 * `data-gyro-density` is stamped on <html> beside the preset class, from
 * LayoutPreset::density(). Density is a separate axis from shape on purpose:
 * Rail and Top are both compact for the same reason - they buy screen height
 * for tables - while Minimal is the only preset that spends space rather than
 * saving it.
 *
 * The fallbacks in every var() below are not decoration. If the head script is
 * ever blocked, no attribute and no class is stamped, and every rule that reads
 * one of these must still resolve to the Classic value rather than to nothing.
 * -------------------------------------------------------------------------- */
:root {
  --gyro-nav-item-pad: var(--space-2);
  --gyro-nav-gap: var(--space-2);
  --gyro-page-pad: var(--space-5);
  --gyro-page-gap: var(--space-5);

  /* The rail's resting width comes from PHP: Filament emits
   * `--collapsed-sidebar-width` on :root from Panel::collapsedSidebarWidth()
   * (layout/base.blade.php), and LayoutPreset::collapsedSidebarWidth() returns
   * 64px for Rail. Filament's own theme.css never reads that variable - as of
   * v5.7 a collapsed sidebar is sized by its content - so reading it here is
   * what gives the number one home instead of two. */
  --gyro-rail-width: var(--collapsed-sidebar-width, 64px);

  /* 240px: --space-7 is 48px, and five of those is a comfortable label column
   * without becoming a second sidebar. */
  --gyro-rail-expanded-width: calc(var(--space-7) * 5);

  /* THE ONE HEIGHT EVERY TOP-BAR CONTROL IS. Spec 28.5's control height, named
   * once here so section 5's switchers and section 6's search, timer, bell and
   * avatar cannot drift apart the way they had by sprint 47 - six controls,
   * five different heights, measured. See section 6. */
  --gyro-topbar-control-height: var(--size-control-button);
  --gyro-topbar-control-gap: var(--space-2);

  /* THERE IS NO --gyro-topbar-search-width ANY MORE. Sprint 49, owner item 3:
   * the 240px field became a 32px icon that opens the command palette, so the
   * only width the search control has is --gyro-topbar-control-height, and the
   * two narrow-viewport overrides that used to shrink it (section 6.6) went
   * with it. Recorded here rather than silently deleted because the token was
   * referenced from three places and the next person to read section 6.1 will
   * wonder where it went. */
}

html[data-gyro-density='compact'] {
  --gyro-nav-item-pad: var(--space-1);
  --gyro-nav-gap: var(--space-1);
  --gyro-page-pad: var(--space-4);
  --gyro-page-gap: var(--space-4);
}

html[data-gyro-density='comfortable'] {
  --gyro-nav-item-pad: var(--space-2);
  --gyro-nav-gap: var(--space-2);
  --gyro-page-pad: var(--space-5);
  --gyro-page-gap: var(--space-5);
}

html[data-gyro-density='spacious'] {
  --gyro-nav-item-pad: var(--space-3);
  --gyro-nav-gap: var(--space-3);
  --gyro-page-pad: var(--space-7);
  --gyro-page-gap: var(--space-6);
}

/* `.fi-page-header-main-ctn` is the element that carries the page's vertical
 * padding (theme.css: padding-block 2rem) and `.fi-page-content` the gap
 * between sections (row-gap 2rem). Those two are the whole vertical rhythm of
 * a page, which is why density is applied here and nowhere else - a density
 * that touched twenty selectors would be four themes, not four layouts. */
html[data-gyro-density] .fi-page-header-main-ctn {
  padding-block: var(--gyro-page-pad);
}

html[data-gyro-density] .fi-page-content {
  row-gap: var(--gyro-page-gap);
}

/* One nav link. `.fi-sidebar-item-btn` is the <a> inside the item's <li>
 * (sidebar/item.blade.php). */
html[data-gyro-density] .fi-sidebar-item-btn {
  padding-block: var(--gyro-nav-item-pad);
}

html[data-gyro-density] .fi-sidebar-group-items,
html[data-gyro-density] .fi-sidebar-sub-group-items {
  row-gap: var(--gyro-nav-gap);
}


/* -----------------------------------------------------------------------------
 * 1. CLASSIC — the grouped sidebar, but visibly a choice.
 *
 * Classic is the default and the shape every existing screenshot assumes, so
 * nothing here moves furniture. What it adds is the one thing the brief asks
 * for and the stock sidebar does not do: group labels that stay visible.
 *
 * `.fi-sidebar-nav` is the scroll container (theme.css: `overflow: hidden
 * auto`), and each group is an <li class="fi-sidebar-group"> whose first child
 * is `.fi-sidebar-group-btn` (sidebar/group.blade.php). Sticking that button to
 * the top of its own <li> means a long group's label stays on screen while its
 * items scroll under it - so you always know which module you are looking at.
 *
 * The background is not optional. At >=1024px Filament sets the sidebar's own
 * background to transparent (theme.css `.fi-sidebar { background-color: #0000 }`
 * inside the lg media query) and gyro-theme.css paints `--color-bg-page`
 * behind it; a sticky element with no background of its own would show the
 * scrolling items straight through it.
 * -------------------------------------------------------------------------- */
html.gyro-layout-classic .fi-sidebar-group-btn {
  position: sticky;
  top: 0;
  z-index: 1; /* a stacking index, not a length: no token applies */
  background-color: var(--color-bg-page);
}

/* The chevron is an icon-button Filament renders only when the group is
 * collapsible; dimming it until the group is hovered or focused keeps eight
 * group headers from reading as eight controls. */
html.gyro-layout-classic .fi-sidebar-group-collapse-btn {
  opacity: 0.55;
  transition: opacity var(--motion-duration-fast) var(--motion-easing-out);
}

html.gyro-layout-classic .fi-sidebar-group-btn:hover .fi-sidebar-group-collapse-btn,
html.gyro-layout-classic .fi-sidebar-group-btn:focus-within .fi-sidebar-group-collapse-btn {
  opacity: 1;
}

/* Active item. `.fi-active` lands on the <li class="fi-sidebar-item">, and the
 * link inside it is a direct child - verified in sidebar/item.blade.php, and
 * matching how Filament's own theme.css writes the same selector. */
html.gyro-layout-classic .fi-sidebar-item.fi-active > .fi-sidebar-item-btn {
  background-color: var(--color-accent-subtle);
  font-weight: var(--type-weight-medium);
}

/* The label and the icon have to be named, not inherited. Filament colours them
 * directly - `.fi-sidebar-item.fi-active>.fi-sidebar-item-btn>.fi-sidebar-item-label
 * { color: var(--primary-700) }` - so a `color` on the anchor alone would be
 * overridden on the only two elements anybody sees. --primary-700 is generated
 * from the accent through Filament's oklch ramp and is close but not equal to
 * it; naming the token keeps the active item the SAME blue as the primary
 * button and the focus ring, which is the point of having one accent. */
html.gyro-layout-classic .fi-sidebar-item.fi-active > .fi-sidebar-item-btn > .fi-sidebar-item-label,
html.gyro-layout-classic .fi-sidebar-item.fi-active > .fi-sidebar-item-btn > .fi-icon {
  color: var(--color-accent-base);
}


/* -----------------------------------------------------------------------------
 * 2. RAIL — Bitrix24. A narrow icon rail that opens over the content.
 *
 * HOW THE RAIL IS MADE, and why it is not a pile of overrides. Filament's
 * COLLAPSED sidebar already is an icon rail: when `$store.sidebar.isOpen` is
 * false, sidebar/group.blade.php hides the group's item list and shows
 * `.fi-sidebar-group-dropdown-trigger-btn` instead - one button per group
 * carrying that group's icon, with the group's items in a dropdown behind it.
 * That is the Bitrix24 shape, built by the framework, keyboard-operable and
 * already translated.
 *
 * What was missing is that nobody ever SAW it. `isOpen` is an Alpine $persist
 * store defaulting to true, so a user who picked Rail got the open sidebar -
 * Classic - and reasonably concluded the preset did nothing. The head script in
 * AdminPanelProvider now writes that store once, on the request where the
 * preset changes, so the rail is what you get the moment you choose it. Once
 * only: after that the collapse control belongs to the user again.
 *
 * So every rule here is scoped to `.fi-sidebar:not(.fi-sidebar-open)`. That is
 * the real collapsed marker - `fi-sidebar-open` is bound with
 * `x-bind:class="{ 'fi-sidebar-open': $store.sidebar.isOpen }"` in
 * livewire/sidebar.blade.php. A rail the user has deliberately expanded is
 * simply the Classic sidebar again, which is the honest behaviour rather than
 * a third state nobody asked for.
 *
 * Everything is inside the lg media query. Below 1024px Filament's sidebar is
 * an off-canvas drawer that slides in over the page, and a rail is not a thing
 * a phone should have.
 * -------------------------------------------------------------------------- */
@media (min-width: 64rem) {
  /* THE RAIL ITSELF.
   *
   * `position: fixed` is the load-bearing declaration. Filament makes a
   * collapsed sidebar `position: sticky` at lg (theme.css), which puts it in
   * the `.fi-layout` flex row so it PUSHES `.fi-main-ctn` sideways. Taking it
   * out of flow is what lets it widen over the content on hover instead of
   * shoving every table 176px to the right; `.fi-main-ctn` gets the width back
   * as a margin below.
   *
   * The background is not optional either: at lg the sidebar is transparent, so
   * a fixed rail with no background of its own would have page content sliding
   * underneath it.
   *
   * z-index 25 sits between Filament's collapsed sidebar (20) and the sticky
   * topbar container (30), so an expanded rail covers the page but never the
   * topbar it hangs below. */
  html.gyro-layout-rail .fi-sidebar:not(.fi-sidebar-open) {
    position: fixed;
    z-index: 25;
    width: var(--gyro-rail-width);
    background-color: var(--color-bg-surface);
    border-inline-end: 1px solid var(--color-border-hairline);
    transition: width var(--motion-duration-base) var(--motion-easing-out);
  }

  /* The space the rail no longer reserves for itself, given back.
   *
   * `.fi-main-ctn-sidebar-open` is Filament's own marker, bound in
   * layout/index.blade.php with
   * `x-bind:class="{ 'fi-main-ctn-sidebar-open': $store.sidebar.isOpen }"`.
   * Keying off it rather than off :has() means the margin and the rail flip
   * together off one piece of state: when the user expands the rail, the
   * sidebar returns to the flex row and this margin must NOT also apply, or
   * the content would be offset twice. */
  html.gyro-layout-rail .fi-main-ctn:not(.fi-main-ctn-sidebar-open) {
    margin-inline-start: var(--gyro-rail-width);
    transition: margin-inline-start var(--motion-duration-base) var(--motion-easing-out);
  }

  /* `overflow: visible` on the scroll container, deliberately.
   *
   * `.fi-sidebar-nav` ships as `overflow: hidden auto` (theme.css). The group
   * dropdown is rendered INSIDE that element and positioned absolutely -
   * sidebar/group.blade.php does not teleport it, unlike the topbar's, which
   * does - so with the stock overflow every flyout in the rail is clipped at
   * the rail's own 64px edge. The trade is that a viewport too short for nine
   * icons cannot scroll the rail; nine icons at ~34px is ~310px, so that means
   * a window under about 370px tall. Judged the lesser problem, and it is
   * written down here rather than discovered later. */
  html.gyro-layout-rail .fi-sidebar:not(.fi-sidebar-open) .fi-sidebar-nav {
    overflow: visible;
    padding-inline: var(--space-2);
    padding-block: var(--space-3);
    row-gap: var(--space-3);
  }

  /* theme.css pulls the group list out by -0.5rem to bleed hover states into
   * the sidebar padding. In a 64px rail that just knocks the icons off-centre. */
  html.gyro-layout-rail .fi-sidebar:not(.fi-sidebar-open) .fi-sidebar-nav-groups {
    margin-inline: 0;
    row-gap: var(--space-2);
  }

  /* One icon, centred, at rest. Both selectors are real buttons in the
   * collapsed markup: the group trigger from sidebar/group.blade.php, and the
   * plain item link for anything ungrouped (Dashboard), from
   * sidebar/item.blade.php. */
  html.gyro-layout-rail .fi-sidebar:not(.fi-sidebar-open) .fi-sidebar-group-dropdown-trigger-btn,
  html.gyro-layout-rail .fi-sidebar:not(.fi-sidebar-open) .fi-sidebar-item-btn {
    justify-content: center;
    gap: var(--space-3);
    padding-inline: var(--space-2);
    padding-block: var(--space-2);
    border-radius: var(--radius-control);
    overflow: hidden;
  }

  html.gyro-layout-rail .fi-sidebar:not(.fi-sidebar-open) .fi-icon {
    flex-shrink: 0;
    color: var(--color-text-secondary);
  }

  /* THE ACTIVE ITEM, UNMISTAKABLE. Stock Filament tints only the icon, which on
   * a 64px rail is a 20px colour difference and nothing else. Three signals
   * instead of one: an accent wash, an accent bar down the leading edge, and
   * the icon in the accent. The bar is drawn with an inset box-shadow rather
   * than a border so it costs no width - a border would move the icon 3px and
   * make the active row the only one not centred.
   *
   * `.fi-active` on `.fi-sidebar-group` is set from `$group->isActive()`;
   * `.fi-active` on `.fi-sidebar-item` from `$item->isActive()`. Active nav is
   * one of the accent's four sanctioned uses (spec 28.5), so this is the
   * accent's job and not a status colour's. */
  html.gyro-layout-rail .fi-sidebar:not(.fi-sidebar-open) .fi-sidebar-group.fi-active .fi-sidebar-group-dropdown-trigger-btn,
  html.gyro-layout-rail .fi-sidebar:not(.fi-sidebar-open) .fi-sidebar-item.fi-active > .fi-sidebar-item-btn {
    background-color: var(--color-accent-subtle);
    box-shadow: inset 3px 0 0 0 var(--color-accent-base);
    color: var(--color-accent-base);
  }

  html.gyro-layout-rail .fi-sidebar:not(.fi-sidebar-open) .fi-sidebar-group.fi-active .fi-sidebar-group-dropdown-trigger-btn .fi-icon,
  html.gyro-layout-rail .fi-sidebar:not(.fi-sidebar-open) .fi-sidebar-item.fi-active > .fi-sidebar-item-btn .fi-icon {
    color: var(--color-accent-base);
  }

  /* OPENING OVER THE CONTENT.
   *
   * :focus-within is not a nicety - it is the only reason this is usable from a
   * keyboard. Tabbing into the rail expands it exactly as hovering does, so a
   * keyboard user reads the same labels a mouse user does. The two are listed
   * as separate selectors rather than through :is() so that a browser without
   * :focus-within still applies the hover half.
   *
   * The shadow is elevation.overlay, the ONE elevation this design system
   * permits (spec 28.5); the rail is an overlay while it is open, which is
   * precisely the case that token exists for. */
  html.gyro-layout-rail .fi-sidebar:not(.fi-sidebar-open):hover,
  html.gyro-layout-rail .fi-sidebar:not(.fi-sidebar-open):focus-within {
    width: var(--gyro-rail-expanded-width);
    box-shadow: var(--elevation-overlay);
  }

  html.gyro-layout-rail .fi-sidebar:not(.fi-sidebar-open):hover .fi-sidebar-group-dropdown-trigger-btn,
  html.gyro-layout-rail .fi-sidebar:not(.fi-sidebar-open):focus-within .fi-sidebar-group-dropdown-trigger-btn,
  html.gyro-layout-rail .fi-sidebar:not(.fi-sidebar-open):hover .fi-sidebar-item-btn,
  html.gyro-layout-rail .fi-sidebar:not(.fi-sidebar-open):focus-within .fi-sidebar-item-btn {
    justify-content: flex-start;
    padding-inline: var(--space-3);
  }

  /* THE LABELS, FROM aria-label RATHER THAN FROM THE HIDDEN SPAN.
   *
   * The obvious move is to un-hide `.fi-sidebar-item-label`. It does not work:
   * that span is behind `x-show="$store.sidebar.isOpen"`, so when the sidebar is
   * collapsed Alpine has written `style="display: none"` on it, and an inline
   * style loses only to !important. Worse, the group's <ul> is behind x-collapse
   * as well, which sets height AND overflow, so forcing it open means fighting
   * three declarations for a result that flickers on every navigation.
   *
   * Both buttons already carry the text as an attribute, put there by Filament
   * for exactly this situation - the group trigger has a literal
   * `aria-label="{{ $label }}"`, and the item link has
   * `x-bind:aria-label="$store.sidebar.isOpen ? null : <label>"`, i.e. the
   * label precisely when it is collapsed. attr() reads what is really there,
   * fights nothing, and degrades to an empty string the moment the sidebar is
   * expanded and Filament clears the attribute.
   *
   * These are decorative duplicates of an accessible name that is already
   * announced, so aria-hidden is not needed and would not apply - generated
   * content is not in the accessibility tree. */
  html.gyro-layout-rail .fi-sidebar:not(.fi-sidebar-open):hover .fi-sidebar-group-dropdown-trigger-btn::after,
  html.gyro-layout-rail .fi-sidebar:not(.fi-sidebar-open):focus-within .fi-sidebar-group-dropdown-trigger-btn::after,
  html.gyro-layout-rail .fi-sidebar:not(.fi-sidebar-open):hover .fi-sidebar-item-btn::after,
  html.gyro-layout-rail .fi-sidebar:not(.fi-sidebar-open):focus-within .fi-sidebar-item-btn::after {
    content: attr(aria-label);
    flex: 1;
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    text-align: start;
    font-size: var(--type-size-base);
    line-height: var(--type-line-base);
    font-weight: var(--type-weight-medium);
    color: inherit;
  }

  /* The rail is the collapsed state, so the topbar control that would collapse
   * it further has nothing to do. The control that EXPANDS it stays - that is
   * how a user leaves the rail. Both classes are from livewire/topbar.blade.php. */
  html.gyro-layout-rail .fi-topbar-close-collapse-sidebar-btn {
    display: none;
  }
}


/* -----------------------------------------------------------------------------
 * 3. MINIMAL — NextCloud. Low chrome, quiet header, a readable column.
 *
 * The shape work is already done in PHP: Panel::maxContentWidth() is 7xl for
 * this preset alone, so the content column stops instead of filling a 1920px
 * screen. What was missing is everything else NextCloud is - the absence of
 * boxes, rings and shadows. Filament draws every card with a 1px ring plus a
 * drop shadow, both as box-shadow; this preset trades all of them for hairlines.
 *
 * Not gated on the lg breakpoint, unlike the rail: quiet chrome is quiet at
 * every width, and none of these rules move anything.
 * -------------------------------------------------------------------------- */

/* The topbar stops being a raised bar and becomes the top edge of the page.
 * theme.css gives `.fi-topbar` a white fill, a ring and a shadow (all one
 * box-shadow declaration, which is why `box-shadow: none` clears the lot). */
html.gyro-layout-minimal .fi-topbar {
  background-color: var(--color-bg-page);
  box-shadow: none;
  border-block-end: 1px solid var(--color-border-hairline);
}

/* The sidebar loses its divider too. gyro-theme.css puts a hairline on
 * `.fi-sidebar`; here the whitespace does that job. */
html.gyro-layout-minimal .fi-sidebar {
  background-color: transparent;
  border-inline-end: 0;
}

html.gyro-layout-minimal .fi-sidebar-nav {
  row-gap: var(--space-6);
  padding-block: var(--space-6);
}

html.gyro-layout-minimal .fi-sidebar-group-label {
  font-size: var(--type-size-micro);
  line-height: var(--type-line-micro);
  font-weight: var(--type-weight-semibold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* Active nav as a mark in the margin rather than a filled pill - the same
 * signal at a fraction of the ink. Inset box-shadow, not border, so the row
 * does not shift 2px when it becomes active. */
html.gyro-layout-minimal .fi-sidebar-item.fi-active > .fi-sidebar-item-btn {
  background-color: transparent;
  box-shadow: inset 2px 0 0 0 var(--color-accent-base);
  font-weight: var(--type-weight-medium);
}

/* Named for the same reason as in Classic: Filament colours these two elements
 * directly, so inheriting from the anchor would not reach them. */
html.gyro-layout-minimal .fi-sidebar-item.fi-active > .fi-sidebar-item-btn > .fi-sidebar-item-label,
html.gyro-layout-minimal .fi-sidebar-item.fi-active > .fi-sidebar-item-btn > .fi-icon {
  color: var(--color-accent-base);
}

/* Cards: hairline instead of ring-plus-shadow. `.fi-section` is Filament's card
 * primitive; `:not(.fi-section-not-contained)` mirrors how theme.css scopes its
 * own rule, so a section that was deliberately drawn without a container is
 * left alone here too. */
html.gyro-layout-minimal .fi-section:not(.fi-section-not-contained) {
  box-shadow: none;
  border: 1px solid var(--color-border-hairline);
  border-radius: var(--radius-control);
}

html.gyro-layout-minimal .fi-wi .fi-section:not(.fi-section-not-contained) {
  box-shadow: none;
  border: 1px solid var(--color-border-hairline);
}

/* Badges toned down, NOT decoloured. theme.css draws a badge as a fill plus an
 * inset ring in the status colour; dropping the ring and the semibold weight
 * takes the shout out of it while the fill and the text keep carrying the
 * status. Removing the colour would break spec 28.10 - status may never be
 * signalled by shape alone - so the colour is exactly what stays. */
html.gyro-layout-minimal .fi-badge {
  box-shadow: none;
  font-weight: var(--type-weight-normal);
}

/* Page heading: NextCloud's is a line of text, not a banner. */
html.gyro-layout-minimal .fi-header-heading {
  font-weight: var(--type-weight-medium);
  letter-spacing: 0;
}

/* The readable column is set in PHP - Panel::maxContentWidth() is 7xl for this
 * preset alone. This is the breathing room around it: Filament stops at 2rem of
 * inline padding on a wide screen, which is right for a full-bleed ERP table
 * and mean for a column that has already given up the width. */
@media (min-width: 64rem) {
  html.gyro-layout-minimal .fi-main {
    padding-inline: var(--space-7);
  }
}


/* -----------------------------------------------------------------------------
 * 4. TOP — Odoo. The one preset that already worked; finish it, do not fix it.
 *
 * Nothing here changes behaviour. Two things made it look unfinished:
 *
 * ACTIVE STATE. theme.css marks the active group with `background: var(--gray-50)`
 * and primary-coloured text. Against a white topbar, gray-50 is a difference of
 * about two percent lightness - on the sandbox it is invisible. Replaced with
 * the accent wash plus an underline, which is the convention every top-nav
 * product uses and the one an evaluator will be looking for.
 *
 * OVERFLOW. `.fi-topbar-nav-groups` ships as `flex-wrap: wrap` at lg, so the
 * ninth group drops onto a second line and the topbar grows taller - which
 * moves the whole page down and looks like a bug. One row that scrolls is the
 * ordinary answer.
 *
 * `teleport` DOES NOT MOVE THE PANEL. The sentence that used to stand here said
 * the topbar's group dropdowns "render at the end of <body>, so a scroll
 * container on the list cannot clip them". That is false, and believing it is
 * what put a mask on this element in sprint 46 and took every top-bar menu off
 * the screen for a whole sprint. Read off the shipped bundle rather than the
 * component's prop name -
 *
 *     vendor/filament/support/dist/index.js
 *         teleport") && (t.strategy = "fixed")
 *
 * - so the `.teleport` modifier on `x-float` sets Floating UI's strategy to
 * `fixed` and NOTHING else. The panel stays exactly where Blade emitted it:
 * inside `.fi-topbar-nav-groups`. Counted in the hydrated DOM on 21 August
 * 2026: 14 `.fi-dropdown-panel` elements, all of them descendants of the nav
 * list. Anything applied to this element that affects how its subtree is
 * PAINTED therefore applies to the menus too. See the block below for what
 * that cost. The sidebar's dropdowns are not teleported either, which is why
 * section 2 has to do the same thing for a different reason.
 * -------------------------------------------------------------------------- */
html.gyro-layout-top .fi-topbar-item.fi-active > .fi-topbar-item-btn {
  background-color: var(--color-accent-subtle);
  box-shadow: inset 0 -2px 0 0 var(--color-accent-base);
  border-radius: var(--radius-control);
}

html.gyro-layout-top .fi-topbar-item.fi-active .fi-topbar-item-label,
html.gyro-layout-top .fi-topbar-item.fi-active .fi-topbar-item-btn > .fi-icon {
  color: var(--color-accent-base);
}

@media (min-width: 64rem) {
  html.gyro-layout-top .fi-topbar-nav-groups {
    flex-wrap: nowrap;
    /* `flex: 1 1 auto` with `min-width: 0` is what keeps `.fi-topbar-end`
     * flush right AT THIS WIDTH: this list eats the free space, so there is
     * none left for the cluster to be pushed away from the right edge by.
     *
     * It is NOT theme.css that pins the cluster, and that is worth correcting
     * here rather than leaving as it stood, because the sentence that used to
     * be on this line said the opposite and is the reason nobody looked.
     * gyro-theme.css section 3 ZEROES Filament's `margin-inline-start: auto` on
     * `.fi-topbar-end`; section 6 below puts it back, and the measurement that
     * settles it is written out there. Below 64rem this list is
     * `display: none`, so this rule stops applying and section 6's margin is
     * the only thing left holding the cluster on the right. */
    flex: 1 1 auto;
    min-width: 0;
    overflow-x: auto;
    overflow-y: hidden;
    /* `overflow-y: hidden` would cut a focus ring drawn at the stock +2px
     * offset on a 34px item inside a 34px box, and spec 28.10 will not have a
     * focus indicator that is only half visible. Four pixels of block padding
     * is the whole fix; the topbar's own 54px min-height absorbs it. */
    padding-block: var(--space-1);
  }

  html.gyro-layout-top .fi-topbar-item-btn {
    white-space: nowrap;
  }

  html.gyro-layout-top .fi-topbar-item-label {
    white-space: nowrap;
  }

  /* A MASK ON AN ANCESTOR OF A MENU IS A MENU THAT DOES NOT OPEN, AND HIDING A
   * SCROLLBAR IS NEVER WORTH THAT.
   *
   * Sprint 46 put a `mask-image` fade on this element to hide the scrollbar
   * under the menu bar, and from that moment none of the fourteen top-bar
   * menus appeared. The owner opened the panel and reported "menus do not
   * open". He was right, and no test in the suite could have told him: the
   * markup, the aria state and the geometry were all perfect.
   *
   * MEASURED IN THE HYDRATED DOM, 21 August 2026, on the CRM group at 1280px,
   * by opening the panel and asking the browser what is actually painted at
   * the panel's own centre point (`document.elementFromPoint`):
   *
   *   mask + overflow, as shipped   rect 189x134 at (252,51)  hit: .fi-header
   *   overflow: visible, mask kept  rect 189x134 at (252,51)  hit: .fi-header
   *   mask removed, overflow kept   rect 189x134 at (252,51)  hit: .fi-dropdown-list-item-label
   *
   * Three things follow, and only the third was guessed correctly beforehand:
   *
   * 1. THE MASK ALONE HIDES IT. Removing `overflow` changed nothing. A mask
   *    rasterises the element AND ITS ENTIRE SUBTREE through the mask's alpha
   *    channel; the gradient covers this element's own 34px-tall box, and the
   *    menu hangs 51px down the page, where the mask has no coverage at all.
   *    Alpha zero. Full size, correctly positioned, perfectly invisible, and
   *    transparent to hit-testing - which is why it reads as "nothing happens
   *    when I click".
   * 2. THE OVERFLOW IS NOT THE CLIPPER HERE. The panel's rect never moved
   *    across all three states, so this element is NOT a containing block for
   *    it in Chrome - the CSS Masking spec says a mask creates one, Chrome
   *    does not implement that - and an ancestor's `overflow` cannot clip a
   *    `position: fixed` descendant it is not the containing block for. So
   *    `overflow-x: auto` stays: it is what stops 974px of navigation
   *    (scrollWidth 1706 against clientWidth 732, measured) from running
   *    straight over the search box, and it costs the menus nothing.
   * 3. The scrollbar still has to go, and `scrollbar-width: none` does that on
   *    its own. It changes no paint, establishes no containing block and
   *    clips nothing. That is the whole difference between the two ways of
   *    hiding a scrollbar, and it is the only one that is safe.
   *
   * The fade is gone with the mask, so a list that continues past the right
   * edge now says so with nothing at all. That is a real loss and it is the
   * smaller one; the honest fix is fewer than fourteen top-level groups, which
   * is a navigation decision and not a stylesheet's to take.
   *
   * SECTION 2 SAYS THE SAME THING ABOUT THE SIDEBAR RAIL, 300 lines up, and it
   * said it first. If you are about to put `overflow`, `mask`, `filter`,
   * `transform`, `contain` or `backdrop-filter` on anything a Filament dropdown
   * lives inside, read that comment and this one, and then check the hydrated
   * DOM rather than the markup. */
  html.gyro-layout-top .fi-topbar-nav-groups {
    scrollbar-width: none;
    -ms-overflow-style: none;
  }

  html.gyro-layout-top .fi-topbar-nav-groups::-webkit-scrollbar {
    display: none;
  }

  /* Tabbing to an item that is currently scrolled out of view must bring it
   * into view rather than leaving the focus ring somewhere nobody can see. */
  html.gyro-layout-top .fi-topbar-item {
    scroll-margin-inline: 1.5rem;
  }

  /* A little tighter, so more of the fourteen groups are reachable without
   * scrolling at all. Not so tight that the labels run together - the density
   * target in spec 28.5 is dense, not cramped. */
  html.gyro-layout-top .fi-topbar-item-btn {
    padding-inline: 0.625rem;
  }
}

/* -----------------------------------------------------------------------------
 * 4.1 THE MEGA MENU — HubSpot's panel shape over ERPNext's workspace content.
 *
 * Read app/Filament/Components/MegaMenu.php first. It carries the measurement
 * this exists to answer (Finance opens 1041px tall into 714px of screen, and
 * the overflow is a CUT, not a scroll), the competitor comparison, and every
 * threshold the script uses. Nothing here invents a number.
 *
 * WHAT THE SCRIPT PUTS ON THE PANEL, and it is only ever this:
 *
 *   data-gyro-mega-cols="1|2|3"   how many columns the group's own item count
 *                                 and the live viewport height came out at
 *   fi-width-xl / fi-width-3xl    Filament's OWN width class, which is the only
 *                                 thing that can beat its layered
 *                                 `max-width: 14rem !important` (see MegaMenu)
 *   style.maxHeight               on the ONE-column case only
 *
 * NOTHING IS ADDED ABOVE THE PANEL, AND THAT IS NOT A STYLE PREFERENCE.
 * The panel is `position: fixed` only because `x-float`'s `teleport` modifier
 * sets Floating UI's strategy - the node itself never leaves
 * `.fi-topbar-nav-groups`, which carries `overflow: auto/hidden`, inside
 * `.fi-topbar-ctn`, which carries `overflow-x: clip`. It survives both of them
 * on ONE property: no ancestor is a containing block for it. The moment any of
 * them gains `transform`, `filter`, `backdrop-filter`, `perspective`,
 * `contain` or a `will-change` naming one of those, both existing `overflow`
 * declarations start clipping every menu again with no change to either of
 * them. That is the sprint-46 fault verbatim (a `mask-image` added to hide a
 * scrollbar took all fourteen menus off the screen for two sprints), section 4
 * above tells the whole story, and
 * TopBarTest::test_no_ancestor_of_a_topbar_dropdown_carries_a_paint_effect_that_would_hide_it
 * enforces it. A mega panel makes it MORE dangerous, not less, because a 648px
 * panel hangs much further outside its ancestors' boxes than a 189px one.
 *
 * So every rule below has `.fi-dropdown-panel` or `.fi-dropdown-list` as its
 * SUBJECT. `.fi-topbar-nav-groups` appears in these selectors as an ancestor
 * and is never styled by them.
 * -------------------------------------------------------------------------- */

/* (a) THE 28 EMPTY LISTS. No script needed, no column count needed, and it
 * helps every preset and the no-JavaScript case too.
 *
 * The vendor blade starts a new `$lists` bucket after every item that has
 * children and then pops only the LAST empty one, so every place two
 * children-bearing parents sit next to each other leaves an empty <div> behind.
 * COUNTED in the rendered HTML for a tenant administrator: 28 of the 86
 * `.fi-dropdown-list` blocks contain no item. Each is 7.75px tall AND draws a
 * 1px divider, because Filament's divide-y rule gives every non-last child of
 * the panel a bottom border - so what the eye sees is a double rule with 7.75px
 * of nothing between it, 28 times across the eight menus. Finance carries five
 * of them: 43.75px of blank and five stray hairlines.
 *
 * `:empty` DOES NOT MATCH THEM - measured, 0 of 28 - because the divs contain
 * Livewire's `<!--[if BLOCK]-->` comments and whitespace. `:not(:has(...))`
 * matched all 28. In a browser without `:has()` the whole rule is dropped and
 * the menus are exactly what they are today, which is the correct fallback. */
html.gyro-layout-top .fi-topbar-nav-groups .fi-dropdown-list:not(:has(.fi-dropdown-list-item)) {
  display: none;
}

/* (b) ONE COLUMN — clamp it instead of cutting it.
 *
 * This is the fallback shape (a group under MegaMenu::MIN_ROWS, or a viewport
 * too narrow for a second column), and it is also the honest floor for the
 * whole feature: even if every measurement above is wrong, a menu that scrolls
 * is reachable and a menu that is cut is not. The height comes from the script,
 * which is the only thing that knows where the trigger's bottom edge is. */
html.gyro-layout-top .fi-topbar-nav-groups .fi-dropdown-panel[data-gyro-mega-cols="1"] {
  overflow-y: auto;
  /* A menu that has run out of scroll must not then scroll the page behind it. */
  overscroll-behavior: contain;
}

/* (c) TWO AND THREE COLUMNS.
 *
 * CSS multi-column and NOT grid: measured, grid's row-major auto-flow scatters
 * the sections across rows and leaves Finance 185px taller than the same
 * content balanced into columns. `column-fill: balance` is the default for an
 * auto-height element and is written out because it is load-bearing - it is
 * what makes three columns three EQUAL columns instead of two full ones and a
 * stub. No `max-height` here, deliberately: a multi-column box with a
 * restricted height fragments into MORE columns sideways rather than
 * scrolling, which would be a worse bug than the one being fixed. The script
 * chooses a column count that fits instead. */
html.gyro-layout-top .fi-topbar-nav-groups .fi-dropdown-panel[data-gyro-mega-cols="2"],
html.gyro-layout-top .fi-topbar-nav-groups .fi-dropdown-panel[data-gyro-mega-cols="3"] {
  column-fill: balance;
  column-gap: var(--space-4);
  column-rule: 1px solid var(--color-border-hairline);
  padding-inline: var(--space-1);
  padding-block: var(--space-1);
}

html.gyro-layout-top .fi-topbar-nav-groups .fi-dropdown-panel[data-gyro-mega-cols="2"] {
  column-count: 2;
}

html.gyro-layout-top .fi-topbar-nav-groups .fi-dropdown-panel[data-gyro-mega-cols="3"] {
  column-count: 3;
}

/* A section is the group's own parent/child structure - `Chart of accounts` and
 * its six children are one `.fi-dropdown-list` - so a section split down the
 * middle of a column break would be the menu lying about the tree. `break-inside`
 * is the whole reason this reads as a mega menu rather than as a wrapped list.
 *
 * MEASURED IN ONE ENGINE ONLY. `break-inside: avoid` in CSS multi-column was
 * verified in the target browser and NOT in Firefox or Safari. If it is ignored
 * there the sections split across columns: untidy, still complete, still
 * reachable. That is the failure this was chosen for.
 *
 * The vendor divide-y border comes off in this shape. A horizontal hairline at
 * the foot of a column separates nothing from nothing; the column rule above is
 * what does the separating, and the margin is what groups the sections. */
html.gyro-layout-top .fi-topbar-nav-groups .fi-dropdown-panel[data-gyro-mega-cols="2"] > .fi-dropdown-list,
html.gyro-layout-top .fi-topbar-nav-groups .fi-dropdown-panel[data-gyro-mega-cols="3"] > .fi-dropdown-list {
  break-inside: avoid;
  border-bottom-width: 0;
  margin-block-end: var(--space-2);
}

/* =============================================================================
 * 5. CONTEXT SWITCHER — the company (1.1) and branch (1.33) controls that
 *    spec 28.2 puts in the top bar. Sprint 47 wave 1.
 *
 * WHY THESE RULES LIVE IN THIS FILE. They are panel chrome rather than a
 * layout preset, and on ownership grounds this is the stylesheet the switcher
 * work owns. It is also the right one on the merits: it is linked LAST of the
 * four project sheets (see preset-head.blade.php), all four are unlayered, so
 * source order decides between them and these rules cannot be quietly
 * overridden by gyro-theme.css or gyro-chrome.css. Every rule below is scoped
 * to `.gyro-switcher*`, so it applies in all four presets and to nothing else.
 *
 * NO !important, for the same reason as the rest of this file: Filament's
 * compiled theme.css sits inside @layer, and an unlayered rule beats every
 * layered rule whatever the specificity.
 *
 * WHERE THE SURROUNDING SELECTORS COME FROM. Read off the installed v5.7
 * source on the server, not inferred:
 *
 *     vendor/filament/filament/resources/views/livewire/topbar.blade.php:256
 *         renders panels::global-search.before INSIDE `.fi-topbar-end`
 *     vendor/filament/filament/dist/theme.css
 *         `.fi-topbar-end { display:flex; align-items:center; column-gap:16px }`
 *         `.fi-topbar-ctn { position:sticky; z-index:30; overflow-x:clip }`
 *
 * The `overflow-x: clip` is the one that decides how the menu is positioned.
 * `clip` on one axis does NOT force the other axis to become a scroll
 * container - that is exactly what distinguishes it from `hidden` - so a menu
 * hanging BELOW the bar is visible, while anything hanging past the left or
 * right edge would be cut. Hence the menu is anchored to the trigger's inline
 * END and grows inwards, never outwards. No teleport is needed, and the
 * z-index of 30 on the container means these menus already paint above the
 * page without a z-index war.
 *
 * COLOUR. Everything here is a token. The accent appears in exactly two
 * places, both of them permitted by spec 28.5: the focus ring, and the ONE
 * item that is currently active. Hover is neutral, because hover is not one of
 * the four accent uses.
 * ============================================================================= */

.gyro-switcher {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

/* A tenant user on a single-site company has NEITHER control, and Livewire
 * still needs a single root element to hydrate. Left as an empty flex child it
 * would contribute another 16px column-gap and push the search box left on
 * every page, for every customer - so the component stamps this class and the
 * element stops taking part in the layout. `:empty` would not do: the root
 * contains whitespace, and `:has()` is not something to depend on here. */
.gyro-switcher--empty {
  display: none;
}

.gyro-switcher__control {
  position: relative;
}

.gyro-switcher__trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  /* Long enough for "Northwind Trading Ltd", short enough that two of these
   * plus the search box still fit a laptop. The label ellipses past it and the
   * full name is on the button's aria-label either way. */
  max-width: 11rem;
  /* Was --size-control-button-compact (24px), which made these two the
   * shortest of the six controls in a row where every one was a different
   * height. Section 6 owns that number now. */
  height: var(--gyro-topbar-control-height);
  padding-block: var(--space-0);
  padding-inline: var(--space-2);
  border: 1px solid var(--color-border-hairline);
  border-radius: var(--radius-control);
  background: var(--color-bg-surface);
  color: var(--color-text-secondary);
  font-family: var(--type-family-ui);
  font-size: var(--type-size-small);
  line-height: var(--type-line-small);
  cursor: pointer;
  transition: background-color var(--motion-duration-fast) var(--motion-easing-out),
    color var(--motion-duration-fast) var(--motion-easing-out);
}

.gyro-switcher__trigger:hover {
  background: var(--color-bg-hover);
  color: var(--color-text-primary);
}

.gyro-switcher__value {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.gyro-switcher__chevron {
  width: var(--space-3);
  height: var(--space-3);
  flex-shrink: 0;
  color: var(--color-text-muted);
}

/* The one shadow on the control, and the only elevation token there is. A
 * closed switcher is flat by design: spec 28.5's density target is a bar that
 * reads as one surface, not a row of floating chips. */
.gyro-switcher__menu {
  position: absolute;
  inset-inline-end: 0;
  top: calc(100% + var(--space-1));
  z-index: 1;
  min-width: 13rem;
  max-height: 60vh;
  overflow-y: auto;
  padding-block: var(--space-1);
  border: 1px solid var(--color-border-hairline);
  border-radius: var(--radius-control);
  background: var(--color-bg-surface);
  box-shadow: var(--elevation-overlay);
}

/* Alpine removes x-cloak once it has initialised, after which x-show's inline
 * style is what hides the menu. Without this rule the menu is briefly open on
 * every page load, for both controls. Filament defines a global [x-cloak] too;
 * this one is scoped and needs no !important because nothing else sets display
 * on this element before Alpine does. */
.gyro-switcher__menu[x-cloak] {
  display: none;
}

.gyro-switcher__heading {
  margin: 0;
  padding-block: var(--space-1);
  padding-inline: var(--space-2);
  color: var(--color-text-muted);
  font-size: var(--type-size-micro);
  line-height: var(--type-line-micro);
}

.gyro-switcher__item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding-block: var(--space-1);
  padding-inline: var(--space-2);
  border: 0;
  background: transparent;
  color: var(--color-text-primary);
  font-family: var(--type-family-ui);
  font-size: var(--type-size-small);
  line-height: var(--type-line-small);
  text-align: start;
  cursor: pointer;
  transition: background-color var(--motion-duration-fast) var(--motion-easing-out);
}

.gyro-switcher__item:hover {
  background: var(--color-bg-hover);
}

/* Spec 28.5 lists the four things the single accent is for, and "active item"
 * is one of them. Not a background wash: in a menu this dense a filled row
 * reads as a hover state that will not go away. */
.gyro-switcher__item[aria-checked="true"] {
  color: var(--color-accent-base);
  font-weight: var(--type-weight-medium);
}

/* Reserved whether or not there is a tick in it, so the labels line up and the
 * list does not jump by a glyph width when the choice changes. */
.gyro-switcher__tick {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--space-3);
  height: var(--space-3);
  flex-shrink: 0;
}

.gyro-switcher__tick svg {
  width: 100%;
  height: 100%;
}

.gyro-switcher__item-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Spec 28.10: an always-visible 2px accent ring, and `outline: none` is
 * forbidden outright. tokens.css already gives every button one through a
 * zero-specificity :where() rule; these two restate it so that a later rule
 * which happens to set `outline` on a topbar button cannot silently win. The
 * menu item's ring is drawn INSIDE its own box - at the stock +2px offset it
 * would be clipped by the menu's own border on the first and last row. */
.gyro-switcher__trigger:focus-visible {
  outline: var(--focus-ring-width) var(--focus-ring-style) var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
}

.gyro-switcher__item:focus-visible {
  outline: var(--focus-ring-width) var(--focus-ring-style) var(--focus-ring-color);
  outline-offset: calc(var(--focus-ring-offset) * -1);
}

/* A phone topbar is the brand, the search and the user menu. The switchers
 * stay - which company you are in is not optional information - but the label
 * gives up its width first, since the full name is still on the aria-label and
 * in the menu underneath. */
@media (max-width: 48rem) {
  .gyro-switcher__trigger {
    max-width: 6rem;
  }
}


/* =============================================================================
 * 6. THE TOP-BAR CONTROL ROW — spec 28.2's controls, one height, one radius,
 *    one hairline. Sprint 47 wave 3.
 *
 * WHY THIS EXISTS. The owner opened the panel on 21 August 2026 and reported
 * that the profile and the bell were in the wrong place, the search looked
 * broken and the timer button was broken. Measured in his browser at 1280px,
 * in the Top preset, before a line of this section was written:
 *
 *     .fi-topbar-end           flex-shrink: 1, six children, overflowing
 *     gyro-switcher            102 x 24
 *     fi-global-search-ctn      52 x 34   <- a magnifier in a box, no field
 *     gyro-palette-hint         44 x 32   <- a SECOND bordered box saying Ctrl K
 *     gyro-timer                66 x 24   <- "Start" / "timer", on two lines
 *     fi-no-database            20 x 20   with a 24 x 18 count badge beside it
 *     fi-user-menu              27 x 27   right edge 1278.4 in a 1264.9 bar
 *
 * Six controls, five heights, three radii, and the avatar 13.5px past the end
 * of the bar it lives in - underneath the page scrollbar. None of that is a
 * Filament defect: `.fi-topbar-end` ships `flex-shrink: 1`, so a navigation
 * list too wide for the screen does not overflow, it CRUSHES the controls to
 * its right. That is the single root cause of four of the six symptoms, and
 * `flex-shrink: 0` is the whole fix for them.
 *
 * SCOPED TO `.fi-topbar-end` AND NOT TO A PRESET, deliberately. Spec 28.2
 * describes one top bar, and these same controls sit in it in all four
 * presets - the crush is worst in Top because that is where the navigation
 * shares the bar, but the five mismatched heights were in every preset.
 *
 * WHERE THE SELECTORS COME FROM. Rule 1: read, never guess. All of these were
 * read off the installed v5.7.6 source on the server and then confirmed
 * present in the hydrated DOM -
 *
 *     vendor/filament/filament/resources/views/livewire/topbar.blade.php
 *         `.fi-topbar-end`, and the three render-hook positions inside it
 *     vendor/filament/filament/resources/views/livewire/global-search.blade.php
 *         `.fi-global-search-ctn` > `.fi-global-search` > `.fi-global-search-field`
 *     vendor/filament/support/resources/views/components/input/wrapper.blade.php
 *         `.fi-input-wrp`, `.fi-input-wrp-prefix`, `.fi-input-wrp-suffix`,
 *         and `.fi-input-wrp-label`, which is the span the suffix text lands in
 *     vendor/filament/filament/resources/views/components/topbar/database-notifications-trigger.blade.php
 *         `.fi-topbar-database-notifications-btn`, which is an `.fi-icon-btn`
 *     vendor/filament/support/resources/views/components/icon-button.blade.php
 *         `.fi-icon-btn-badge-ctn` > `.fi-badge`
 *     vendor/filament/filament/resources/views/components/user-menu.blade.php
 *         `.fi-user-menu-trigger` > `.fi-user-avatar`, which is an `.fi-avatar`,
 *         and an `<img>` - which is why the initials cannot come from inside it
 *
 * NO !important, for the reason at the top of this file: everything here is
 * unlayered and Filament's whole theme.css is inside @layer, so a single class
 * already wins. Where a project sheet is being overridden instead of Filament
 * (`.gyro-timer__*` carries its own style block inside the Livewire view, and
 * an element's own style block appears later in the document than this
 * stylesheet's link), the `.fi-topbar-end` prefix supplies the specificity and
 * source order never has to be argued about.
 * ============================================================================= */

/* THE ONE RULE THAT MATTERS. Everything to the right of the navigation is
 * fixed furniture: it is the same controls on every screen in the product, and
 * no amount of navigation may take a pixel off it. */
.fi-topbar-end {
  flex-shrink: 0;
  column-gap: var(--gyro-topbar-control-gap);

  /* AND THE CLUSTER GOES ON THE RIGHT. THIS IS "the profile and the bell are in
   * the wrong place", and `flex-shrink: 0` above does not fix it - it stops the
   * controls being CRUSHED, it does not decide where they sit.
   *
   * Filament ships `.fi-topbar-end { margin-inline-start: auto }`, which is the
   * whole mechanism that pins this cluster flush right. gyro-theme.css section 3
   * zeroes it:
   *
   *     .fi-topbar-end { order: 100; margin-inline-start: 0; }
   *
   * That rule is tagged `[guess]` in its own file and its comment says the auto
   * margin "must be zeroed or the topbar cluster centers instead of the user-menu
   * sitting flush right". MEASURED IN CHROME AT 1280px ON THE CLASSIC PRESET,
   * which is the default and therefore what most people see: with it zeroed the
   * cluster's left edge is at x=180, immediately after the logo, with 558px of
   * empty top bar to its right. Restoring the auto margin puts it at x=725, and
   * its right edge at 1251 against a bar ending at 1265 - the bar's own 14px of
   * padding. The guess was backwards.
   *
   * `order: 100` cannot stand in for it. Order only decides the SEQUENCE of flex
   * items; it distributes no free space. It looks like it works in the Top preset
   * at >=1024px, and only because the navigation list next door carries
   * `flex: 1 1 auto` and eats the gap itself - which is why this survived: the
   * one configuration anybody checked was the one where a different rule was
   * doing the job. Measured with the nav present, this declaration changes
   * nothing (endL 739 both ways); measured in Classic, and in Top below 1024px
   * where the nav is `display: none`, it is the only thing holding the cluster.
   *
   * Here rather than in gyro-theme.css because both rules are unlayered and this
   * stylesheet is registered second on `panels::head.end`, so source order
   * decides and this wins without an !important. */
  margin-inline-start: auto;
}

/* One height, one radius, and the vertical centring that stops a 24px control
 * from sitting a few pixels high next to a 32px one. `min-height` as well as
 * `height`, because two of these are flex containers whose own content would
 * otherwise push them taller than the row.
 *
 * TWO DELIBERATE EXCEPTIONS TO "ONE RADIUS", SO NEITHER READS AS DRIFT LATER.
 * The avatar (6.4) is a circle, because that is what an avatar is and what the
 * brief asked for, and the unread count (6.3) is a pill, because
 * `--radius-chip` is what every count in this product already uses. Both are
 * round by identity rather than by decoration; every rectangular control in the
 * row is `--radius-control` and stays that way.
 *
 * "ONE HAIRLINE" MEANS THE CONTROLS THAT HAVE AN EDGE SHARE ONE, and sprint 49
 * moved two controls across that line. The switcher is a read-from surface with
 * a label in it and still carries `--color-border-hairline`. The search and the
 * timer are now ICON-ONLY TARGETS, like the bell and the avatar, so they carry
 * no border at all - which is the rule this paragraph already stated and is what
 * every product this is measured against does. A bordered box round a bell reads
 * as a button that is disabled, and the same is true of a bordered magnifier. */
.fi-topbar-end .gyro-switcher__trigger,
.fi-topbar-end .gyro-timer__bar,
.fi-topbar-end .gyro-timer__button,
.fi-topbar-end .gyro-search-trigger,
.fi-topbar-end .fi-icon-btn,
.fi-topbar-end .fi-user-menu-trigger {
  height: var(--gyro-topbar-control-height);
  min-height: var(--gyro-topbar-control-height);
  border-radius: var(--radius-control);
  box-sizing: border-box;
}

/* Filament's `.fi-icon-btn` ships `margin: -0.5rem` so an icon button can sit
 * flush inside a table cell. In a control row that negative margin is what
 * makes the bell look like it is floating between the timer and the avatar
 * rather than standing in the row with them. */
.fi-topbar-end .fi-icon-btn {
  width: var(--gyro-topbar-control-height);
  margin: var(--space-0);
  flex-shrink: 0;
}

/* -----------------------------------------------------------------------------
 * 6.1 SEARCH — an icon, not a bar. Sprint 49, owner item 3.
 *
 * HIS WORDS: "The search bar only open once click on the icon, default state
 * should be an icon, and upon click or ctrl+k should open the same popup, there
 * is no need to have a bar."
 *
 * WHAT THIS REPLACES, AND WHAT IT BUYS. Measured on the live sandbox at 1280px
 * in the Top preset, `.fi-global-search-ctn` was 240px - a prefix magnifier at
 * 33.8, an input at 144.5, a `Ctrl K` key cap at 59.8 - inside a bar whose
 * navigation strip had 560px to show 954.5px of sections. Four of the nine
 * sections were on the bar and five were behind "More". A 32px icon gives 208px
 * of that back; item 4's timer gives another 60.5px stopped and 234.4px running.
 *
 * THE FIELD IS NOT HIDDEN, IT IS NOT RENDERED. `AdminPanelProvider` passes
 * `->globalSearch(false)`, which makes the vendor topbar skip the `@if` around
 * `@livewire(Filament\Livewire\GlobalSearch::class)` outright, so there is no
 * `.fi-global-search-ctn` in the document to write a rule against and nothing
 * invisible left in the tab order. Every rule that used to be in this section -
 * the 240px width, the field's `width: 100%`, and the `.fi-input-wrp-suffix
 * .fi-input-wrp-label` key cap - selected markup that no longer exists, so they
 * are gone rather than kept "just in case". A stylesheet that describes markup
 * the product does not emit is how three shipped rules in this file came to do
 * nothing at all.
 *
 * NO BORDER AND NO FILL. It is an icon-only target, so it follows the bell and
 * the avatar rather than the switcher; see the "one hairline" paragraph above.
 * The 32px height comes from the shared rule, so the icon cannot drift off the
 * row the way six controls at five heights once did.
 * -------------------------------------------------------------------------- */
.fi-topbar-end .gyro-search-trigger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--gyro-topbar-control-height);
  padding: var(--space-0);
  border: 0;
  background: transparent;
  color: var(--color-text-secondary);
  cursor: pointer;
  flex-shrink: 0;
}

.fi-topbar-end .gyro-search-trigger:hover {
  background: var(--color-bg-hover);
  color: var(--color-text-primary);
}

/* Sized from the toolbar icon token, the same one the bell is drawn at, so the
 * two icon-only controls in this row are optically the same weight. `stroke`
 * rather than `fill` on this one: an outlined magnifier next to a filled bell
 * is how every product this is measured against draws the pair. */
.fi-topbar-end .gyro-search-trigger__icon {
  width: var(--icon-toolbar);
  height: var(--icon-toolbar);
  stroke-width: var(--icon-stroke);
  flex: none;
}

/* -----------------------------------------------------------------------------
 * 6.2 THE TIMER — an icon too, and never wider than one. Sprint 49, owner
 *     item 4: "The timer bar should only have an timer icon, pause and play
 *     icon makes sense too. No need for the text."
 *
 * WHAT THIS SECTION USED TO SAY, AND WHY IT IS KEPT IN THE PAST TENSE:
 * `.gyro-timer__button` had no `white-space`, so "Start timer" broke across two
 * lines the moment the row was squeezed, and a two-line button in a one-line bar
 * is the single most visible way a top bar can look unfinished. There is no
 * label left to wrap - the accessible name and the tooltip carry the words now -
 * but `white-space: nowrap` stays, because the panel's own "Start" button is the
 * same class and is still text, and because relying on nobody putting a label
 * back is how it happened the first time.
 *
 * THE WIDTH IS THE CONTRACT NOW. An icon-only button with no width is a button
 * whose size is decided by its padding, which is exactly how the timer came to
 * be 92.5px stopped and 306.4px running - measured on the live sandbox at
 * 1280px, where the running bar took 213.9px straight out of the navigation
 * strip beside it. Square, from the same token as the height.
 * -------------------------------------------------------------------------- */
.fi-topbar-end .gyro-timer,
.fi-topbar-end .gyro-timer__bar,
.fi-topbar-end .gyro-timer__button {
  white-space: nowrap;
  flex-shrink: 0;
}

/* The two states are one square each: an outline play when stopped, a solid
 * pause plus a quiet disclosure when running. `padding-inline: 0` because the
 * width is doing the work; leaving the text button's padding on would make the
 * square a rectangle again. */
.fi-topbar-end .gyro-timer__button--icon {
  width: var(--gyro-topbar-control-height);
  padding-inline: var(--space-0);
}

/* -----------------------------------------------------------------------------
 * 6.3 THE BELL — the count belongs ON the icon.
 *
 * Filament already anchors `.fi-icon-btn-badge-ctn` absolutely at the top-right
 * corner of the button, which is the reason a three-character count can never
 * resize the control: an absolutely positioned box is out of flow, so "99+" is
 * exactly as wide as the button as "1" is. What was wrong was the size of the
 * thing being anchored - gyro-theme.css styles every `.fi-badge` in the product
 * as an 18px status chip, and an 18px pill 24px wide hung off a 20px bell reads
 * as two controls rather than one. Sixteen square, centred, tabular.
 * -------------------------------------------------------------------------- */
/* SIZED BY ITS OWN LINE BOX, like every other chip in the product since sprint
 * 48 - see gyro-theme.css section 5 for the arithmetic and the measurements.
 * The bell is the one chip that must stay SIXTEEN and not eighteen, because it
 * hangs off a 20px icon rather than sitting in a 34px row, so it gives up the
 * hairline instead of the two pixels: 0 border + 16px line-height = 16px, and
 * the fill plus the count still carry the status. `min-height` rather than
 * `height` for the same reason as everywhere else - a box shorter than its own
 * line box only looks right because `overflow: hidden` is hiding the rest. */
.fi-topbar-end .fi-icon-btn-badge-ctn .fi-badge {
  min-height: var(--space-4);
  padding-block: var(--space-0);
  border-width: 0;
  min-width: var(--space-4);
  padding-inline: var(--space-1);
  border-radius: var(--radius-chip);
  font-size: var(--type-size-micro);
  line-height: var(--type-line-micro);
  font-weight: var(--type-weight-medium);
  /* Tabular figures, for the same reason as the timer's clock: a number that
   * changes must not change the width of what is around it. */
  font-variant-numeric: tabular-nums;
  justify-content: center;
}

/* -----------------------------------------------------------------------------
 * 6.4 THE AVATAR — initials in a circle, drawn here, fetched from nobody.
 *
 * WHAT IT WAS. Filament's stock avatar provider builds an img whose src is a
 * ui-avatars.com URL. Confirmed in the live DOM: the currentSrc host was
 * ui-avatars.com. So a self-hosted business suite made a third-party request
 * on every page load of every screen, told that third party the name of the
 * signed-in person, and rendered the result at 27px with a radius and a
 * palette belonging to somebody else's design system. Offline - which is what
 * "self-hosted, on the customer's own server" often means - it is a broken
 * image, and a broken image with alt text is a bare letter, which is what the
 * owner saw and reported.
 *
 * WHAT IT IS. `App\Filament\Components\InitialsAvatarProvider` returns a
 * transparent SVG data URI, so no request leaves the machine and the img still
 * exists to carry the alt text a screen reader reads. The disc and the ink are
 * drawn here, from tokens, so dark mode is right for free. The initials
 * themselves arrive as a custom property set per request by
 * `resources/views/filament/components/avatar-initials.blade.php` - CSS cannot
 * take the first letters of a name, and an img cannot read a custom property,
 * so the two halves have to meet somewhere and a custom property is the only
 * place they can.
 *
 * `--color-bg-selected` and `--color-text-secondary` are the pair, not an
 * accent: spec 28.5 lists the four things the accent is for and an avatar is
 * not one of them. From the token file they clear 4.5:1 in both modes
 * (#565a61 on #e2e4e6 light, #b7bac0 on #454749 dark).
 * -------------------------------------------------------------------------- */
.fi-topbar-end .fi-user-menu-trigger {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--gyro-topbar-control-height);
  flex-shrink: 0;
  border-radius: var(--radius-chip);
  background-color: var(--color-bg-selected);
  color: var(--color-text-secondary);
  font-family: var(--type-family-ui);
  font-size: var(--type-size-small);
  line-height: var(--type-line-small);
  font-weight: var(--type-weight-medium);
}

/* The fallback is an empty string and not a letter: an empty disc is a person
 * with no name on file, a wrong letter is a lie about who is signed in. */
.fi-topbar-end .fi-user-menu-trigger::before {
  content: var(--gyro-avatar-initials, "");
}

.fi-topbar-end .fi-user-menu-trigger .fi-avatar {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border-radius: var(--radius-chip);
}

/* -----------------------------------------------------------------------------
 * 6.5 KEYBOARD AND FOCUS — spec 28.10.
 *
 * Every control in this row is a real button or a real input in DOM order, so
 * Tab reaches them left to right, Enter and Space activate them and nothing
 * traps focus - none of which is this stylesheet's doing and all of which it
 * could undo, which is why the ring is restated here. tokens.css already draws
 * one through a zero-specificity :where() rule; this repeats it at a
 * specificity a later top-bar rule cannot quietly outrank, exactly as section 5
 * does for the switcher.
 *
 * There is no `outline: none` anywhere in this file and spec 28.10 forbids one
 * being added. The token linter enforces it as OUTLINE_NONE.
 * -------------------------------------------------------------------------- */
.fi-topbar-end :is(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: var(--focus-ring-width) var(--focus-ring-style) var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
}

/* Hover and focus move colour only, at the 140ms the motion tokens name.
 * Nothing here animates on load: spec 28.9 forbids an entrance animation and a
 * top bar that assembles itself is the most obvious place to break that. */
.fi-topbar-end .gyro-switcher__trigger,
.fi-topbar-end .gyro-search-trigger,
.fi-topbar-end .gyro-timer__button,
.fi-topbar-end .fi-icon-btn,
.fi-topbar-end .fi-user-menu-trigger {
  transition-property: var(--motion-allowed-hover-and-focus-state);
  transition-duration: var(--motion-duration-fast);
  transition-timing-function: var(--motion-easing-out);
}

@media (prefers-reduced-motion: reduce) {
  .fi-topbar-end .gyro-switcher__trigger,
  .fi-topbar-end .gyro-search-trigger,
  .fi-topbar-end .gyro-timer__button,
  .fi-topbar-end .fi-icon-btn,
  .fi-topbar-end .fi-user-menu-trigger {
    transition-duration: var(--motion-reduced-duration);
    transition-timing-function: var(--motion-reduced-easing);
  }
}

/* -----------------------------------------------------------------------------
 * 6.6 NARROW SCREENS.
 *
 * THIS SECTION USED TO DO TWO JOBS AND SPRINT 49 REMOVED THE NEED FOR BOTH, so
 * what is left is one line. It shrank `--gyro-topbar-search-width` twice, and
 * there is no search width any more - the control is a 32px square at every
 * viewport. And it CLIPPED the timer button's label rather than hiding it,
 * because that label was the button's whole accessible name and `display: none`
 * would have left a screen reader announcing an unnamed button; there is no
 * label now, the name is an `aria-label`, and the clipping rule selected a
 * `> span` the view no longer emits.
 *
 * What remains is the gap: on a phone the five controls sit 4px apart instead of
 * 8px. Nothing gives up width, because after items 3 and 4 nothing in this row
 * is wider than one square except the company switcher, which has its own
 * `max-width` in section 5.
 * -------------------------------------------------------------------------- */
@media (max-width: 48rem) {
  :root {
    --gyro-topbar-control-gap: var(--space-1);
  }
}


/* =============================================================================
 * 7. THE TOP BAR'S OVERFLOW MENU — the "More" control at the end of the group
 *    strip. Sprint 47, wave 3 follow-up.
 *
 * WHAT IT ANSWERS. Measured in the owner's browser on the sandbox at 5970de3,
 * Top preset, 1280px, signed in:
 *
 *     .fi-topbar-nav-groups   children 9   scrollWidth 954   clientWidth 545
 *
 * 409px of navigation — three of the nine top-level entries — reachable only by
 * dragging a strip whose scrollbar section 4 deliberately hides. Section 4 says
 * in its own words that the honest fix "is fewer than fourteen top-level
 * groups, which is a navigation decision and not a stylesheet's to take". That
 * decision was taken (NavigationMap cut fourteen to eight) and it was not
 * enough: eight groups plus the Dashboard link plus a search field, a company
 * picker, a timer, a bell and an avatar still do not fit a 1280px laptop. An
 * overflow menu is what Odoo and Bitrix24 do at exactly this point, and it is
 * the difference between navigation that adapts and navigation that hides.
 *
 * NOTHING IN THIS SECTION CLIPS OR PAINTS OVER A MENU, and that sentence is
 * load-bearing. Read section 4's long block before editing anything here: a
 * `mask-image` on `.fi-topbar-nav-groups` took every top-bar dropdown off the
 * screen for two sprints while leaving its rect, its aria state and its markup
 * perfect. So this section adds NO mask, NO filter, NO transform, NO contain
 * and NO non-visible overflow to any element a Filament dropdown panel is
 * inside. The only `overflow` below is on `.gyro-navmore__panel`, which is a
 * leaf: it contains nothing but this control's own links, and Filament's
 * dropdowns are neither inside it nor beneath it.
 *
 * THE PANEL IS `position: fixed`, WHICH IS THE WHOLE ANTI-CLIPPING ARGUMENT.
 * A fixed box's containing block is the viewport unless an ancestor creates
 * one, so an ancestor's overflow — `overflow-x: auto` on the <ul>,
 * `overflow-x: clip` on `.fi-topbar-ctn` — cannot clip it. That is not
 * reasoning from a spec: section 4 records the measurement on the hydrated DOM
 * (panel rect unchanged with the overflow removed), and Filament's own top-bar
 * panels already depend on the same fact. Coordinates come from the script, so
 * this element needs no positioned ancestor and gets none.
 *
 * `flex: 0 0 auto` ON THE CHILDREN IS WHAT MAKES THE MEASUREMENT HONEST. The
 * script decides what fits by hiding one entry and asking the <ul> whether it
 * still overflows. If a group trigger could shrink under flex pressure, the
 * strip would stop "overflowing" by squeezing labels instead, the script would
 * conclude everything fits, and the bar would go back to being unreadable
 * rather than unreachable. The nowrap in section 4 already makes shrinking
 * unlikely; this makes it impossible.
 * ============================================================================= */

/* Subject is `*`, not a top-bar class: this rule is about the children, and it
 * sets no property that could establish a containing block or paint a subtree. */
html.gyro-layout-top .fi-topbar-nav-groups > * {
  flex: 0 0 auto;
}

/* The script hides an entry with the `hidden` attribute, and the attribute's
 * `display: none` comes from the user-agent stylesheet — which any author rule
 * beats, including every layered rule in Filament's compiled theme.css. This
 * one is unlayered, so it wins without the important flag, which this file does
 * not use. Without it, an entry the script believes is hidden is still on the
 * bar and the fit is never reached. */
html.gyro-layout-top .fi-topbar-nav-groups > [hidden] {
  display: none;
}

.gyro-navmore {
  position: relative;
  display: flex;
  align-items: center;
}

/* Deliberately shaped like `.fi-topbar-item-btn` next to it — same height, same
 * inline padding as section 4's tightened override, same radius. It is a
 * navigation entry, not a toolbar oddity, and a control that announces itself
 * as different invites the reader to skip it. */
.gyro-navmore__trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  height: var(--size-control-button);
  padding-block: var(--space-0);
  padding-inline: 0.625rem;
  border: 0;
  border-radius: var(--radius-control);
  background: transparent;
  color: var(--color-text-secondary);
  font-family: var(--type-family-ui);
  font-size: var(--type-size-small);
  line-height: var(--type-line-small);
  font-weight: var(--type-weight-medium);
  white-space: nowrap;
  cursor: pointer;
  transition-property: var(--motion-allowed-hover-and-focus-state);
  transition-duration: var(--motion-duration-fast);
  transition-timing-function: var(--motion-easing-out);
}

.gyro-navmore__trigger:hover {
  background: var(--color-bg-hover);
  color: var(--color-text-primary);
}

.gyro-navmore__trigger:focus-visible {
  outline: var(--focus-ring-width) var(--focus-ring-style) var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
}

/* THE COUNT IS PART OF THE LABEL, and it is not decoration. Nobody should have
 * to discover that a third of the navigation is behind this control, so the
 * number is on the button in the bar rather than only in the panel. A neutral
 * chip and not the accent: spec 28.5 gives the accent four uses — primary
 * button, active nav item, focus ring, link — and "how many things are hidden"
 * is none of them. */
.gyro-navmore__count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--space-4);
  padding-inline: var(--space-1);
  border-radius: var(--radius-chip);
  background: var(--color-bg-sunken);
  color: var(--color-text-muted);
  font-size: var(--type-size-micro);
  line-height: var(--type-line-micro);
  font-variant-numeric: var(--type-numeric-variant);
}

.gyro-navmore__chevron {
  width: var(--space-3);
  height: var(--space-3);
  flex-shrink: 0;
  color: var(--color-text-muted);
}

/* One shadow, one hairline, one radius — the same overlay treatment as the
 * company switcher's menu in section 5, because they are the same kind of
 * object and two different-looking menus in one bar reads as two products. */
.gyro-navmore__panel {
  position: fixed;
  z-index: 40;
  min-width: 14rem;
  max-width: 22rem;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding-block: var(--space-1);
  border: 1px solid var(--color-border-hairline);
  border-radius: var(--radius-control);
  background: var(--color-bg-surface);
  box-shadow: var(--elevation-overlay);
}

.gyro-navmore__section + .gyro-navmore__section {
  margin-block-start: var(--space-1);
  border-block-start: 1px solid var(--color-border-hairline);
  padding-block-start: var(--space-1);
}

/* THERE IS NO `.gyro-navmore__heading` AND NO `.gyro-navmore__item--child`.
 * Both were deleted in sprint 49 and named here so the deletion is a record
 * rather than a gap. Commit 3513e9c removed the `<p class="gyro-navmore__
 * heading">` and the indented per-item anchor from topbar-overflow.blade.php
 * when the panel became one lead link per section; the two rules stayed, so
 * roughly twenty lines of stylesheet shipped on every Top-preset page selecting
 * markup that had not existed for a sprint. If the panel ever lists items under
 * a heading again, write the rules again with the markup, in the same commit. */

.gyro-navmore__item {
  display: block;
  padding-block: var(--space-1);
  padding-inline: var(--space-2);
  color: var(--color-text-primary);
  font-family: var(--type-family-ui);
  font-size: var(--type-size-small);
  line-height: var(--type-line-small);
  text-decoration: none;
  transition-property: var(--motion-allowed-hover-and-focus-state);
  transition-duration: var(--motion-duration-fast);
  transition-timing-function: var(--motion-easing-out);
}

/* The lead link is a group that has no menu of its own — Dashboard, today. It
 * is a destination rather than a heading, so it carries the heading's weight
 * and the item's colour. */
.gyro-navmore__item--lead {
  font-weight: var(--type-weight-medium);
}

.gyro-navmore__item:hover {
  background: var(--color-bg-hover);
}

.gyro-navmore__item:focus-visible {
  outline: var(--focus-ring-width) var(--focus-ring-style) var(--focus-ring-color);
  outline-offset: calc(var(--focus-ring-offset) * -1);
}

/* One of spec 28.5's four accent uses: the item you are looking at. Colour and
 * weight rather than a filled row — in a list this dense a wash reads as a
 * hover state that will not go away. The active entry is never offered up by
 * the script, so it should not be in here at all; this covers the one case
 * where it can be, which is a strip narrow enough that the active entry is the
 * only thing left on it. */
.gyro-navmore__item[aria-current="page"] {
  color: var(--color-accent-base);
  font-weight: var(--type-weight-medium);
}

@media (prefers-reduced-motion: reduce) {
  .gyro-navmore__trigger,
  .gyro-navmore__item {
    transition-duration: var(--motion-reduced-duration);
    transition-timing-function: var(--motion-reduced-easing);
  }
}
