/*! Home-UI v0.1.50 - reusable Web Components
 * Copyright (c) 2026 James Harrison
 * SPDX-License-Identifier: MIT
 * Source and licence: https://github.com/haribo256/home-ui-components
 */
/*
 * Home-UI class sheet (TASK-011, UI-CONTRACT-002, UI-CONTRACT-003).
 *
 * The shadcn-derived components that carry no behaviour ship as classes, not
 * custom elements. The server renders this markup and the page can swap it or
 * bind to it with no shadow boundary in the way.
 *
 * Every value comes from a token, so these classes re-theme automatically
 * under class="dark" and never need a dark-specific rule.
 *
 * Load it with a single <link>; no build step is required on the host:
 *
 *   <link rel="stylesheet" href="https://home-ui.harriverse.com/dist/latest/home-ui.css">
 */

@import "./tokens.css";
@import "./icon-masks.css";

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

/*
 * The page surface (ISS-001, UI-CONTRACT-002).
 *
 * Home-UI does not style the host's <body>: the host owns the page, and a
 * library that claimed <body> would fight a host page with its own
 * background. But tokens alone leave the page itself untouched, so a host
 * that sets class="dark" gets dark components on a white page - and the
 * ghost and link button variants become white text on white.
 *
 * This class is the opt-in. The host applies it to <body>, or to any wrapper
 * it wants themed, and keeps the choice:
 *
 *   <html class="{{.Theme}}">
 *     <body class="hui-surface">
 */
.hui-surface {
  background-color: var(--hui-background, canvas);
  color: var(--hui-foreground, canvastext);
  font-family: var(--hui-font-sans, system-ui, sans-serif);
}

/*
 * Text-bearing classes carry the design-system font themselves. Home-UI does
 * not style the host's <body>, so without this rule the classes inherit the
 * browser default serif. Mono classes (.hui-kbd, .hui-code) set their own.
 */
.hui-prose,
.hui-h1,
.hui-h2,
.hui-h3,
.hui-h4,
.hui-p,
.hui-lead,
.hui-muted,
.hui-blockquote,
.hui-list,
.hui-link,
.hui-card,
.hui-badge,
.hui-alert,
.hui-table,
.hui-empty,
.hui-item,
.hui-breadcrumb,
.hui-label,
.hui-native-select,
.hui-input-group,
.hui-scroll-area,
.hui-mark {
  font-family: var(--hui-font-sans, system-ui, sans-serif);
}

/* -------------------------------------------------------------------------
 * Before the elements are defined
 *
 * Until the module runs, a hui-* element is an unknown inline element: the
 * browser shows its light-DOM children as they stand - every option in a
 * select, every tab panel at once - and the page is the wrong height, which is
 * the flash a reader sees on a slow connection. Measured on the standalone
 * pair with the script delayed: 147px of raw text against the 125px the
 * upgraded page settles at.
 *
 * `:defined` is the platform's hook for it, and this sheet is loaded before
 * the script, so the rule is in place first. Five elements are left out -
 * hui-field, hui-table, hui-listbox, hui-tree and hui-resizable - because they
 * render the host's own markup in place: their children are already styled by this sheet
 * and there is nothing to hide.
 *
 * Nothing here can leave content hidden for good. If the script never arrives
 * - blocked, failed, JavaScript off - the animation reveals everything after
 * three seconds, unstyled but readable, which is the state the page would have
 * been in anyway.
 * ---------------------------------------------------------------------- */

hui-accordion:not(:defined),
hui-app-shell:not(:defined),
hui-area-chart:not(:defined),
hui-bar-chart:not(:defined),
hui-button:not(:defined),
hui-button-group:not(:defined),
hui-calendar:not(:defined),
hui-carousel:not(:defined),
hui-checkbox:not(:defined),
hui-combobox:not(:defined),
hui-command:not(:defined),
hui-confirm-action:not(:defined),
hui-date-picker:not(:defined),
hui-dialog:not(:defined),
hui-dropdown-menu:not(:defined),
hui-input:not(:defined),
hui-input-otp:not(:defined),
hui-line-chart:not(:defined),
hui-menubar:not(:defined),
hui-navigation-menu:not(:defined),
hui-pagination:not(:defined),
hui-panel:not(:defined),
hui-path:not(:defined),
hui-pie-chart:not(:defined),
hui-popover:not(:defined),
hui-radial-chart:not(:defined),
hui-radio-group:not(:defined),
hui-search-field:not(:defined),
hui-select:not(:defined),
hui-sidebar:not(:defined),
hui-slider:not(:defined),
hui-switch:not(:defined),
hui-tabs:not(:defined),
hui-textarea:not(:defined),
hui-theme-toggle:not(:defined),
hui-toast:not(:defined),
hui-toggle:not(:defined),
hui-toggle-group:not(:defined),
hui-tooltip:not(:defined) {
  animation: hui-reveal-undefined 1ms linear 3s forwards;
  visibility: hidden;
}

@keyframes hui-reveal-undefined {
  to {
    visibility: visible;
  }
}

/*
 * Keep the space the control will take, so the page does not jump as it
 * upgrades: these are the 2rem-tall controls, clamped to that height with
 * their unupgraded contents clipped.
 */
hui-button:not(:defined),
hui-combobox:not(:defined),
hui-date-picker:not(:defined),
hui-input:not(:defined),
hui-search-field:not(:defined),
hui-select:not(:defined),
hui-switch:not(:defined),
hui-toggle:not(:defined) {
  block-size: 2rem;
  display: inline-block;
  overflow: hidden;
}

/*
 * A chart's height comes from the host, inline or in its own stylesheet, and
 * applies before the upgrade too - it only needs to be a block to take it.
 */
hui-area-chart:not(:defined),
hui-bar-chart:not(:defined),
hui-line-chart:not(:defined),
hui-pie-chart:not(:defined),
hui-radial-chart:not(:defined) {
  display: block;
}

/* -------------------------------------------------------------------------
 * Typography
 * ---------------------------------------------------------------------- */

.hui-prose {
  color: var(--hui-foreground, CanvasText);
  font-family: var(--hui-font-sans, system-ui, sans-serif);
  font-size: var(--hui-text-base, 1rem);
  line-height: var(--hui-leading-normal, 1.5);
}

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

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

.hui-h1,
.hui-h2,
.hui-h3,
.hui-h4 {
  color: var(--hui-foreground, CanvasText);
  font-weight: var(--hui-weight-semibold, 600);
  line-height: var(--hui-leading-tight, 1.25);
  margin: 0 0 var(--hui-space-md, 0.75rem);
}

.hui-h1 {
  font-size: var(--hui-text-2xl, 1.5rem);
}

.hui-h2 {
  font-size: var(--hui-text-xl, 1.25rem);
}

.hui-h3 {
  font-size: var(--hui-text-lg, 1.125rem);
}

.hui-h4 {
  font-size: var(--hui-text-base, 1rem);
}

.hui-p {
  margin: 0 0 var(--hui-space-md, 0.75rem);
}

.hui-lead {
  color: var(--hui-muted-foreground, GrayText);
  font-size: var(--hui-text-lg, 1.125rem);
  line-height: var(--hui-leading-normal, 1.5);
}

.hui-muted {
  color: var(--hui-muted-foreground, GrayText);
  font-size: var(--hui-text-sm, 0.875rem);
}

.hui-code {
  background: var(--hui-muted, Canvas);
  border-radius: var(--hui-radius-sm, 0.25rem);
  color: var(--hui-foreground, CanvasText);
  font-family: var(--hui-font-mono, monospace);
  font-size: 0.875em;
  padding: 0.15em 0.35em;
}

/*
 * A search hit, or a flagged word (TASK-058). A background behind the text it
 * marks, so it keeps the host's own inline flow. `.hui-mark--warning` is a
 * match that is a problem rather than a hit, on the destructive tint.
 */
.hui-mark {
  background: var(--hui-mark, Highlight);
  border-radius: var(--hui-radius-sm, 0.25rem);
  color: var(--hui-foreground, CanvasText);
  padding: 0.1em 0.15em;
}

.hui-mark--warning {
  background: var(--hui-destructive-subtle, Highlight);
  color: var(--hui-destructive-subtle-foreground, CanvasText);
}

/*
 * A background-only highlight disappears under a forced palette. The mark
 * becomes an underline so a match is still visibly a match.
 */
@media (forced-colors: active) {
  .hui-mark,
  .hui-mark--warning {
    background: transparent;
    color: inherit;
    text-decoration: underline;
  }
}

.hui-blockquote {
  border-left: 3px solid var(--hui-border, CanvasText);
  color: var(--hui-muted-foreground, GrayText);
  margin: 0 0 var(--hui-space-md, 0.75rem);
  padding-left: var(--hui-space-lg, 1rem);
}

.hui-list {
  margin: 0 0 var(--hui-space-md, 0.75rem);
  padding-left: var(--hui-space-xl, 1.5rem);
}

.hui-link {
  color: var(--hui-primary-text, LinkText);
  text-decoration: underline;
  text-underline-offset: 4px;
}

/* -------------------------------------------------------------------------
 * Card
 * ---------------------------------------------------------------------- */

.hui-card {
  background: var(--hui-card, Canvas);
  border-radius: var(--hui-radius-xl, 0.63rem);
  /*
   * A ring rather than a border: it sits outside the layout box, so adding or
   * removing it never reflows the card's contents.
   */
  box-shadow: 0 0 0 1px color-mix(in oklab, var(--hui-foreground, CanvasText) 10%, transparent);
  color: var(--hui-card-foreground, CanvasText);
  /*
   * nova's density (ISS-013): the card owns the vertical rhythm - 16px above
   * the first part and between parts - and each part pads only its sides.
   * With a footer the footer's own padding closes the card instead.
   */
  display: flex;
  flex-direction: column;
  font-size: var(--hui-text-sm, 0.875rem);
  gap: var(--hui-space-lg, 1rem);
  line-height: 1.25rem;
  padding-block: var(--hui-space-lg, 1rem);
}

/*
 * A grid, so an optional .hui-card__action sits in its own column beside the
 * title and description rather than pushing them down. Every other part is
 * pinned to the first column, so without an action the second is empty and
 * takes no width - the layout needs no :has() to know whether there is one.
 */
.hui-card__header {
  column-gap: 0;
  display: grid;
  grid-auto-rows: min-content;
  grid-template-columns: minmax(0, 1fr) auto;
  padding: 0 var(--hui-space-lg, 1rem);
  row-gap: var(--hui-space-xs, 0.25rem);
}

.hui-card__header > * {
  grid-column: 1;
}

.hui-card__header > .hui-card__action {
  align-self: start;
  grid-column: 2;
  grid-row: 1 / span 2;
  justify-self: end;
  margin-inline-start: var(--hui-space-sm, 0.5rem);
}

.hui-card__title {
  font-family: var(--hui-font-heading, Georgia, serif);
  font-size: var(--hui-text-base, 1rem);
  font-weight: var(--hui-weight-medium, 500);
  line-height: 1.375;
  margin: 0;
}

.hui-card__description {
  color: var(--hui-muted-foreground, GrayText);
  font-size: var(--hui-text-sm, 0.875rem);
  margin: 0;
}

.hui-card__content {
  padding: 0 var(--hui-space-lg, 1rem);
}

/*
 * A tinted band with a rule above it, rounded to the card's lower corners.
 * The negative margin takes back the card's closing padding, so the footer
 * is the card's last edge.
 */
.hui-card__footer {
  align-items: center;
  background: color-mix(in oklab, var(--hui-muted, Canvas) 50%, transparent);
  border-block-start: 1px solid var(--hui-border, CanvasText);
  border-radius: 0 0 var(--hui-radius-xl, 0.63rem) var(--hui-radius-xl, 0.63rem);
  display: flex;
  gap: var(--hui-space-sm, 0.5rem);
  margin-block-end: calc(-1 * var(--hui-space-lg, 1rem));
  padding: var(--hui-space-lg, 1rem);
}

/* -------------------------------------------------------------------------
 * Badge
 * ---------------------------------------------------------------------- */

.hui-badge {
  align-items: center;
  background: var(--hui-primary, CanvasText);
  border: 1px solid transparent;
  border-radius: var(--hui-radius-pill, 9999px);
  color: var(--hui-primary-foreground, Canvas);
  display: inline-flex;
  font-size: var(--hui-text-xs, 0.75rem);
  font-weight: var(--hui-weight-medium, 500);
  gap: var(--hui-space-xs, 0.25rem);
  height: 1.25rem;
  line-height: 1;
  padding: 0 var(--hui-space-sm, 0.5rem);
  white-space: nowrap;
}

.hui-badge--secondary {
  background: var(--hui-secondary, Canvas);
  color: var(--hui-secondary-foreground, CanvasText);
}

/* A tint with coloured text, at values that clear AA - see UI-CONTRACT-006. */
.hui-badge--destructive {
  background: var(--hui-destructive-subtle, Canvas);
  color: var(--hui-destructive-subtle-foreground, CanvasText);
}

.hui-badge--outline {
  background: transparent;
  border-color: var(--hui-border, CanvasText);
  color: var(--hui-foreground, CanvasText);
}

/* -------------------------------------------------------------------------
 * Alert
 * ---------------------------------------------------------------------- */

.hui-alert {
  background: var(--hui-card, Canvas);
  border: 1px solid var(--hui-border, CanvasText);
  border-radius: var(--hui-radius-lg, 0.625rem);
  color: var(--hui-card-foreground, CanvasText);
  display: grid;
  gap: var(--hui-space-xs, 0.25rem);
  padding: var(--hui-space-lg, 1rem);
}

.hui-alert__title {
  font-weight: var(--hui-weight-medium, 500);
  line-height: var(--hui-leading-tight, 1.25);
}

.hui-alert__description {
  color: var(--hui-muted-foreground, GrayText);
  font-size: var(--hui-text-sm, 0.875rem);
}

.hui-alert--destructive {
  border-color: var(--hui-destructive, CanvasText);
}

.hui-alert--destructive .hui-alert__title {
  color: var(--hui-destructive, CanvasText);
}

/* -------------------------------------------------------------------------
 * Separator
 * ---------------------------------------------------------------------- */

.hui-separator {
  background: var(--hui-border, CanvasText);
  border: 0;
  flex-shrink: 0;
  height: 1px;
  width: 100%;
}

.hui-separator--vertical {
  height: 100%;
  width: 1px;
}

/* -------------------------------------------------------------------------
 * Skeleton
 * ---------------------------------------------------------------------- */

.hui-skeleton {
  animation: hui-skeleton-pulse 2s var(--hui-ease, cubic-bezier(0.4, 0, 0.2, 1)) infinite;
  background: var(--hui-muted, Canvas);
  border-radius: var(--hui-radius-md, calc(var(--hui-radius, 0.625rem) - 2px));
}

@keyframes hui-skeleton-pulse {
  0%,
  100% {
    opacity: 1;
  }

  50% {
    opacity: 0.5;
  }
}

/* -------------------------------------------------------------------------
 * Table
 * ---------------------------------------------------------------------- */

table.hui-table {
  border-collapse: collapse;
  color: var(--hui-foreground, CanvasText);
  font-size: var(--hui-text-sm, 0.875rem);
  width: 100%;
}

.hui-table caption {
  caption-side: bottom;
  color: var(--hui-muted-foreground, GrayText);
  font-size: var(--hui-text-sm, 0.875rem);
  padding-top: var(--hui-space-md, 0.75rem);
}

.hui-table th,
.hui-table td {
  border-bottom: 1px solid var(--hui-border, CanvasText);
  padding: var(--hui-space-sm, 0.5rem);
  text-align: left;
  vertical-align: middle;
}

.hui-table th {
  color: var(--hui-muted-foreground, GrayText);
  font-weight: var(--hui-weight-medium, 500);
}

.hui-table tbody tr:last-child td {
  border-bottom: 0;
}

/* -------------------------------------------------------------------------
 * Kbd
 * ---------------------------------------------------------------------- */

.hui-kbd {
  background: var(--hui-muted, Canvas);
  border: 1px solid var(--hui-border, CanvasText);
  border-radius: var(--hui-radius-sm, 0.25rem);
  color: var(--hui-muted-foreground, GrayText);
  display: inline-flex;
  font-family: var(--hui-font-mono, monospace);
  font-size: var(--hui-text-xs, 0.75rem);
  align-items: center;
  gap: 0.25rem;
  line-height: 1;
  padding: 0.2rem 0.4rem;
}

/* -------------------------------------------------------------------------
 * Avatar
 * ---------------------------------------------------------------------- */

.hui-avatar {
  background: var(--hui-muted, Canvas);
  border-radius: 9999px;
  display: inline-flex;
  flex-shrink: 0;
  height: 2.5rem;
  overflow: hidden;
  position: relative;
  width: 2.5rem;
}

.hui-avatar__image {
  height: 100%;
  object-fit: cover;
  width: 100%;
}

.hui-avatar__fallback {
  align-items: center;
  color: var(--hui-muted-foreground, GrayText);
  display: flex;
  font-size: var(--hui-text-sm, 0.875rem);
  height: 100%;
  justify-content: center;
  width: 100%;
}

.hui-avatar--sm {
  height: 2rem;
  width: 2rem;
}

.hui-avatar--lg {
  height: 3rem;
  width: 3rem;
}

/*
 * A row of overlapping avatars (ENH-013). nova's AvatarGroup is
 * `flex -space-x-2` with every avatar in it ringed `ring-2 ring-background`,
 * so the overlap reads as a stack rather than a smear.
 *
 * The ring colour is a variable because --hui-background is only right when
 * the group sits on the page. On a card, --hui-background is darker than the
 * card in the dark theme and the ring draws a seam, so a host on another
 * surface sets --hui-avatar-ring to that surface's token.
 */
.hui-avatar-group {
  display: flex;
}

.hui-avatar-group > .hui-avatar {
  box-shadow: 0 0 0 2px var(--hui-avatar-ring, var(--hui-background, Canvas));
}

.hui-avatar-group > .hui-avatar + .hui-avatar {
  margin-left: -0.5rem;
}

/* Faces drained of colour, for an empty or invitation state. */
.hui-avatar-group--grayscale > .hui-avatar {
  filter: grayscale(1);
}

/* -------------------------------------------------------------------------
 * Aspect ratio
 * ---------------------------------------------------------------------- */

.hui-aspect-ratio {
  aspect-ratio: var(--hui-aspect-ratio, 16 / 9);
  overflow: hidden;
  width: 100%;
}

.hui-aspect-ratio > * {
  height: 100%;
  object-fit: cover;
  width: 100%;
}

/* -------------------------------------------------------------------------
 * Empty state
 * ---------------------------------------------------------------------- */

.hui-empty {
  align-items: center;
  border-radius: var(--hui-radius-xl, 0.63rem);
  color: var(--hui-foreground, CanvasText);
  display: flex;
  flex-direction: column;
  gap: var(--hui-space-lg, 1rem);
  justify-content: center;
  min-width: 0;
  padding: var(--hui-space-lg, 1rem);
  text-align: center;
  text-wrap: balance;
}

/*
 * nova draws an empty state borderless, inside whatever holds it. The dashed
 * outline Home-UI used to draw by default is now opt-in, for an empty state
 * that stands on the page by itself.
 */
.hui-empty--outline {
  border: 1px dashed var(--hui-border, CanvasText);
}

.hui-empty__header {
  align-items: center;
  display: flex;
  flex-direction: column;
  gap: var(--hui-space-sm, 0.5rem);
  max-width: 24rem;
}

.hui-empty__icon {
  align-items: center;
  background: var(--hui-muted, Canvas);
  border-radius: var(--hui-radius-lg, 0.45rem);
  color: var(--hui-foreground, CanvasText);
  display: flex;
  flex: none;
  height: 2rem;
  justify-content: center;
  width: 2rem;
}

.hui-empty__header > .hui-empty__icon {
  margin-block-end: var(--hui-space-sm, 0.5rem);
}

.hui-empty__icon > svg {
  height: 1rem;
  width: 1rem;
}

.hui-empty__title {
  color: var(--hui-foreground, CanvasText);
  font-family: var(--hui-font-heading, Georgia, serif);
  font-size: var(--hui-text-sm, 0.875rem);
  font-weight: var(--hui-weight-medium, 500);
  letter-spacing: -0.025em;
  line-height: 1.25rem;
}

.hui-empty__description {
  color: var(--hui-muted-foreground, GrayText);
  font-size: var(--hui-text-sm, 0.875rem);
  line-height: 1.625;
}

.hui-empty__content {
  align-items: center;
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
  max-width: 24rem;
  min-width: 0;
  width: 100%;
}

/* -------------------------------------------------------------------------
 * Item
 * ---------------------------------------------------------------------- */

.hui-item {
  align-items: center;
  border: 1px solid transparent;
  border-radius: var(--hui-radius-lg, 0.45rem);
  color: inherit;
  display: flex;
  flex-wrap: wrap;
  font-size: var(--hui-text-sm, 0.875rem);
  gap: 0.625rem;
  padding: 0.625rem var(--hui-space-md, 0.75rem);
  text-decoration: none;
  transition: background-color 100ms var(--hui-ease, ease);
}

/* nova's two variants: a quiet fill, and an outline for a group of options. */
.hui-item--muted {
  background: color-mix(in oklab, var(--hui-muted, Canvas) 50%, transparent);
}

.hui-item--outline {
  border-color: var(--hui-border, CanvasText);
}

/*
 * Only an item that does something responds to the pointer. A display item
 * that lit up under the cursor read as clickable when it was not.
 */
a.hui-item:hover,
button.hui-item:hover {
  background: var(--hui-muted, Canvas);
}

.hui-item__media {
  align-items: center;
  display: flex;
  flex: none;
  gap: var(--hui-space-sm, 0.5rem);
  justify-content: center;
}

.hui-item__media > svg {
  height: 1rem;
  width: 1rem;
}

.hui-item__content {
  display: flex;
  flex: 1 1 0;
  flex-direction: column;
  gap: var(--hui-space-xs, 0.25rem);
  min-width: 0;
}

.hui-item__title {
  align-items: center;
  display: flex;
  font-weight: var(--hui-weight-medium, 500);
  gap: var(--hui-space-sm, 0.5rem);
  line-height: 1.375;
}

.hui-item__description {
  color: var(--hui-muted-foreground, GrayText);
  font-size: var(--hui-text-sm, 0.875rem);
  line-height: var(--hui-leading-normal, 1.5);
  margin: 0;
}

.hui-item__actions {
  align-items: center;
  display: flex;
  gap: var(--hui-space-sm, 0.5rem);
}

.hui-item__footer {
  align-items: center;
  display: flex;
  flex-basis: 100%;
  gap: var(--hui-space-sm, 0.5rem);
  justify-content: space-between;
}

/* -------------------------------------------------------------------------
 * Breadcrumb
 * ---------------------------------------------------------------------- */

.hui-breadcrumb {
  color: var(--hui-muted-foreground, GrayText);
  font-size: var(--hui-text-sm, 0.875rem);
}

.hui-breadcrumb__list {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: var(--hui-space-xs, 0.25rem);
  list-style: none;
  margin: 0;
  padding: 0;
}

.hui-breadcrumb__link {
  color: inherit;
  text-decoration: none;
}

.hui-breadcrumb__link:hover {
  color: var(--hui-foreground, CanvasText);
}

.hui-breadcrumb__page {
  color: var(--hui-foreground, CanvasText);
  font-weight: var(--hui-weight-normal, 400);
}

.hui-breadcrumb__separator {
  color: var(--hui-muted-foreground, GrayText);
  user-select: none;
}

/* -------------------------------------------------------------------------
 * Progress
 * ---------------------------------------------------------------------- */

.hui-progress {
  background: var(--hui-muted, Canvas);
  border-radius: 9999px;
  height: 0.25rem;
  overflow: hidden;
  width: 100%;
}

.hui-progress__indicator {
  background: var(--hui-primary, CanvasText);
  height: 100%;
  transition: width var(--hui-duration-normal, 200ms) var(--hui-ease, cubic-bezier(0.4, 0, 0.2, 1));
  width: 0;
}

/* -------------------------------------------------------------------------
 * Spinner
 * ---------------------------------------------------------------------- */

/*
 * The spinner draws Remix Icon's loader glyph as a mask over currentColor
 * (TASK-032). A class sheet has no shadow root to inline an <svg> into, and
 * the previous two-pixel border ring was almost invisible at 1rem.
 */
.hui-spinner {
  animation: hui-spin 0.8s steps(8) infinite;
  background-color: currentColor;
  display: inline-block;
  height: var(--hui-spinner-size, 1rem);
  mask: var(--hui-icon-loader-line) center / contain no-repeat;
  vertical-align: text-bottom;
  width: var(--hui-spinner-size, 1rem);
}

@keyframes hui-spin {
  to {
    transform: rotate(360deg);
  }
}

/* -------------------------------------------------------------------------
 * Scroll area
 * ---------------------------------------------------------------------- */

.hui-scroll-area {
  max-height: var(--hui-scroll-area-height, 16rem);
  overflow: auto;
  scrollbar-width: thin;
}

.hui-scroll-area::-webkit-scrollbar {
  height: 0.5rem;
  width: 0.5rem;
}

.hui-scroll-area::-webkit-scrollbar-thumb {
  background: var(--hui-border, CanvasText);
  border-radius: 9999px;
}

/* -------------------------------------------------------------------------
 * Label
 * ---------------------------------------------------------------------- */

.hui-label {
  align-items: center;
  color: var(--hui-foreground, CanvasText);
  display: inline-flex;
  font-size: var(--hui-text-sm, 0.875rem);
  font-weight: var(--hui-weight-medium, 500);
  gap: var(--hui-space-xs, 0.25rem);
  line-height: 1;
}

.hui-label[data-disabled="true"],
.hui-label:has(+ :disabled) {
  opacity: 0.5;
}

/* -------------------------------------------------------------------------
 * Overlay items (light DOM, slotted into hui-dropdown-menu, hui-select,
 * hui-combobox and hui-command)
 * ---------------------------------------------------------------------- */

/*
 * ISS-005. These components take their items from the host: a server template
 * writes <button role="menuitem"> or <div role="option"> and the component
 * slots them into its panel. Until now nothing styled them, so a menu rendered
 * as a stack of user-agent buttons - Arial, ButtonFace grey, square corners -
 * inside a correctly themed panel. In dark mode that reads as a white block on
 * a dark page.
 *
 * The rules live here rather than in each component's shadow CSS because
 * ::slotted() matches only direct children: an item inside <div role="group">
 * is a grandchild of the slot and gets nothing. That is why two of the four
 * command entries were styled and two were not. A document rule reaches any
 * depth, and for slotted content it also wins over ::slotted on a tie, so a
 * component may still carry its own rules for what is specific to it.
 *
 * Scoped by host element name so a host using these ARIA roles elsewhere on
 * the page is untouched.
 */

/*
 * :where(:not([slot="trigger"])) because a hui-menubar gives each menu's trigger
 * role="menuitem" as the menubar pattern has it; the trigger is a button in
 * the bar, not a row in the menu, and must not be drawn as one. :where()
 * adds no specificity, so the checkbox and radio gutter rules below still win.
 */
hui-dropdown-menu [role="menuitem"]:where(:not([slot="trigger"])),
hui-dropdown-menu [role="menuitemcheckbox"]:where(:not([slot="trigger"])),
hui-dropdown-menu [role="menuitemradio"]:where(:not([slot="trigger"])),
hui-select [role="option"],
hui-combobox [role="option"],
hui-command [role="option"] {
  align-items: center;
  /* The host writes a <button>, so the user agent's own chrome has to go. */
  background: none;
  border: 0;
  border-radius: var(--hui-radius-md, 0.36rem);
  color: inherit;
  cursor: default;
  display: flex;
  font: inherit;
  font-size: var(--hui-text-sm, 0.875rem);
  gap: var(--hui-space-sm, 0.5rem);
  line-height: var(--hui-leading-normal, 1.5);
  margin: 0;
  min-height: 1.75rem;
  padding: 0.25rem var(--hui-space-sm, 0.5rem);
  text-align: left;
  /* An <a role="menuitem"> is a row, not an underlined link (ISS-031). */
  text-decoration: none;
  transition: background-color var(--hui-duration-fast, 120ms) var(--hui-ease, ease),
    color var(--hui-duration-fast, 120ms) var(--hui-ease, ease);
  user-select: none;
  width: 100%;
}

/*
 * TASK-054. The three families that differ from the base above. nova's
 * SelectItem is `gap-1.5 py-1 pl-1.5` and its ComboboxItem `gap-2 py-1
 * pl-1.5`, so both start 6px in; CommandItem is `gap-2 px-2 py-1.5 rounded-sm`,
 * a 32px row with the small radius. All four overlay rows sit on nova's 20px
 * `text-sm` line. Home-UI's select and combobox draw no check glyph at the
 * end, so they keep the 8px end padding rather than the 32px nova reserves
 * for one.
 */
hui-select [role="option"] {
  gap: 0.375rem;
  line-height: 1.25rem;
  padding: 0.25rem var(--hui-space-sm, 0.5rem) 0.25rem 0.375rem;
}

hui-combobox [role="option"] {
  line-height: 1.25rem;
  padding: 0.25rem var(--hui-space-sm, 0.5rem) 0.25rem 0.375rem;
}

hui-command [role="option"] {
  border-radius: var(--hui-radius-sm, 0.27rem);
  line-height: 1.25rem;
  padding: 0.375rem var(--hui-space-sm, 0.5rem);
}

/*
 * An author `display` beats the user agent's `[hidden] { display: none }`, and
 * hiding is exactly how these components filter - hui-combobox and hui-command
 * set the hidden property on the options a query excludes. Without this the
 * rule above would show every option regardless of the query.
 */
hui-dropdown-menu [role="menuitem"][hidden],
hui-dropdown-menu [role="menuitemcheckbox"][hidden],
hui-dropdown-menu [role="menuitemradio"][hidden],
hui-select [role="option"][hidden],
hui-combobox [role="option"][hidden],
hui-command [role="option"][hidden],
hui-command [role="group"][hidden] {
  display: none;
}

/*
 * Hover and keyboard highlight are one appearance, so a pointer user and a
 * keyboard user see the same thing in the same place. data-active is what the
 * components set as they move the highlight with the arrow keys.
 */
hui-dropdown-menu [role="menuitem"]:where(:not([slot="trigger"])):hover,
hui-dropdown-menu [role="menuitemcheckbox"]:where(:not([slot="trigger"])):hover,
hui-dropdown-menu [role="menuitemradio"]:where(:not([slot="trigger"])):hover,
hui-select [role="option"]:hover,
hui-combobox [role="option"]:hover,
hui-command [role="option"]:hover,
hui-dropdown-menu [data-active="true"],
hui-select [role="option"][data-active="true"],
hui-combobox [role="option"][data-active="true"],
hui-command [role="option"][data-active="true"] {
  background: var(--hui-muted, Canvas);
  color: var(--hui-foreground, CanvasText);
}

hui-select [role="option"][aria-selected="true"],
hui-combobox [role="option"][aria-selected="true"] {
  color: var(--hui-primary-text, CanvasText);
  font-weight: var(--hui-weight-medium, 500);
}

/*
 * TASK-061. A multiple select marks each chosen option with a check and
 * reserves the gutter for it. Scoped to `selection="multiple"`, so the single
 * select, the combobox, the command palette and the menu - which share the
 * item rules above - are untouched.
 */
hui-select[selection="multiple"] [role="option"] {
  padding-inline-start: 1.75rem;
  position: relative;
}

hui-select[selection="multiple"] [role="option"]::before {
  background: currentColor;
  block-size: 0.875rem;
  content: '';
  inline-size: 0.875rem;
  inset-inline-start: var(--hui-space-sm, 0.5rem);
  mask: var(--hui-icon-check-line) center / contain no-repeat;
  opacity: 0;
  position: absolute;
  top: 50%;
  translate: 0 -50%;
}

/*
 * The check is the selection cue in multiple mode, so the row keeps the body
 * text colour. The primary-text used by a single selected row measures below
 * AA on the dark active fill; the tick and the medium weight already say
 * selected.
 */
hui-select[selection="multiple"] [role="option"][aria-selected="true"] {
  color: var(--hui-foreground, CanvasText);
}

hui-select[selection="multiple"] [role="option"][aria-selected="true"]::before {
  opacity: 1;
}

/*
 * A checkable item shows its state, as shadcn's checkbox and radio items do: a
 * gutter on the inline start, a tick in it when checked and, for a radio, a
 * dot. Without a mark the two states are the same row with a different
 * `aria-checked`, which only a screen reader could tell apart.
 */
hui-dropdown-menu [role="menuitemcheckbox"],
hui-dropdown-menu [role="menuitemradio"] {
  padding-inline-start: 1.75rem;
  position: relative;
}

hui-dropdown-menu [role="menuitemcheckbox"]::before,
hui-dropdown-menu [role="menuitemradio"]::before {
  content: '';
  inset-inline-start: var(--hui-space-sm, 0.5rem);
  opacity: 0;
  position: absolute;
  top: 50%;
  translate: 0 -50%;
}

hui-dropdown-menu [role="menuitemcheckbox"]::before {
  background: currentColor;
  block-size: 0.875rem;
  inline-size: 0.875rem;
  mask: var(--hui-icon-check-line) center / contain no-repeat;
}

hui-dropdown-menu [role="menuitemradio"]::before {
  background: currentColor;
  block-size: 0.375rem;
  border-radius: var(--hui-radius-pill, 9999px);
  inline-size: 0.375rem;
  margin-inline-start: 0.25rem;
}

hui-dropdown-menu [role="menuitemcheckbox"][aria-checked="true"]::before,
hui-dropdown-menu [role="menuitemradio"][aria-checked="true"]::before {
  opacity: 1;
}

/*
 * An unavailable item keeps its place in the list and its accessible name, so
 * it is still announced - it simply cannot be chosen. aria-disabled rather
 * than the disabled attribute, which would remove it from the accessibility
 * tree entirely.
 */
hui-dropdown-menu [aria-disabled="true"],
hui-select [role="option"][aria-disabled="true"],
hui-combobox [role="option"][aria-disabled="true"],
hui-command [role="option"][aria-disabled="true"] {
  opacity: 0.5;
  pointer-events: none;
}

hui-dropdown-menu [role="separator"],
hui-select [role="separator"],
hui-command [role="separator"] {
  background: var(--hui-border, CanvasText);
  block-size: 1px;
  border: 0;
  margin: var(--hui-space-xs, 0.25rem) 0;
}

/* A group's own label, where the host gives one. */
hui-dropdown-menu [role="group"] > [data-label],
hui-select [role="group"] > [data-label],
hui-command [role="group"] > [data-label] {
  color: var(--hui-muted-foreground, GrayText);
  font-size: var(--hui-text-xs, 0.75rem);
  font-weight: var(--hui-weight-medium, 500);
  padding: var(--hui-space-xs, 0.25rem) var(--hui-space-sm, 0.5rem);
}

/*
 * TASK-054. The dropdown menu's own look at preset b5ZcnBEttq, where the menu
 * is `menuColor: default-translucent` and `menuAccent: subtle`. The rules
 * above are shared with hui-select, hui-combobox and hui-command, whose item
 * geometry differs, so the menu's is scoped here rather than changed for
 * everyone.
 *
 * DropdownMenuItem is `gap-1.5 px-1.5 py-1 text-sm`: a 6px gap, 4px by 6px
 * padding and a 20px line, so a row is 28px. The focused row takes the
 * foreground at 10 percent - the "subtle" accent - and no outline, replacing
 * the browser's blue focus ring. The component sets `data-active` as the keys
 * move, so a pointer hover and a keyboard move draw one appearance.
 */
hui-dropdown-menu [role="menuitem"]:not([slot="trigger"]) {
  gap: 0.375rem;
  line-height: 1.25rem;
  outline: none;
  padding: 0.25rem 0.375rem;
}

/*
 * A checkable item keeps the 1.75rem start gutter the rule above draws its
 * tick in, so only the block and inline-end padding move.
 */
hui-dropdown-menu [role="menuitemcheckbox"]:not([slot="trigger"]),
hui-dropdown-menu [role="menuitemradio"]:not([slot="trigger"]) {
  gap: 0.375rem;
  line-height: 1.25rem;
  outline: none;
  padding-block: 0.25rem;
  padding-inline-end: 0.375rem;
}

hui-dropdown-menu [role="menuitem"]:not([slot="trigger"]):hover,
hui-dropdown-menu [role="menuitemcheckbox"]:not([slot="trigger"]):hover,
hui-dropdown-menu [role="menuitemradio"]:not([slot="trigger"]):hover,
hui-dropdown-menu [data-active="true"] {
  background: color-mix(in oklab, var(--hui-foreground, CanvasText) 10%, transparent);
  color: var(--hui-foreground, CanvasText);
}

/* DropdownMenuSeparator is `-mx-1 my-1 h-px bg-border`: it bleeds to the panel's edges. */
hui-dropdown-menu [role="separator"] {
  background: color-mix(in oklab, var(--hui-foreground, CanvasText) 5%, transparent);
  margin: var(--hui-space-xs, 0.25rem) calc(-1 * var(--hui-space-xs, 0.25rem));
}

/* DropdownMenuLabel is `px-1.5 py-1`, 4px by 6px rather than the shared 8px. */
hui-dropdown-menu [role="group"] > [data-label] {
  padding: var(--hui-space-xs, 0.25rem) 0.375rem;
}

/* DropdownMenuShortcut is `ml-auto text-xs tracking-widest text-muted-foreground`. */
hui-dropdown-menu [data-shortcut] {
  color: var(--hui-muted-foreground, GrayText);
  font-size: var(--hui-text-xs, 0.75rem);
  letter-spacing: 0.1em;
  margin-inline-start: auto;
}

/*
 * A submenu trigger is a menu item that opens another menu; nova draws it as
 * an item with the chevron pushed to the end. hui-dropdown-menu has no nested
 * menu behaviour yet (TASK-054 records that), so only the geometry hook is
 * here, on the `aria-haspopup="menu"` the host already writes.
 */
hui-dropdown-menu [role="menuitem"][aria-haspopup="menu"] [data-submenu-icon] {
  margin-inline-start: auto;
}

@media (prefers-reduced-motion: reduce) {
  hui-dropdown-menu [role="menuitem"],
  hui-dropdown-menu [role="menuitemcheckbox"],
  hui-dropdown-menu [role="menuitemradio"],
  hui-select [role="option"],
  hui-combobox [role="option"],
  hui-command [role="option"] {
    transition: none;
  }
}

/* -------------------------------------------------------------------------
 * Field (light DOM, wired by hui-field)
 * ---------------------------------------------------------------------- */

/*
 * ISS-002. hui-field wires a label, description and error to a control with
 * aria-describedby, and deliberately has no shadow root so a server template can
 * render the markup and the page can swap it. That left the appearance to this
 * sheet, which had no rule for it: description and error ran together on one
 * line, both in body colour, reading as one sentence.
 *
 * The error is marked by colour, by position and by an icon, so it does not
 * depend on colour alone.
 */
hui-field {
  display: flex;
  flex-direction: column;
  gap: var(--hui-space-sm, 0.5rem);
}

hui-field [data-description],
hui-field [data-error] {
  display: block;
  font-size: var(--hui-text-sm, 0.875rem);
  line-height: var(--hui-leading-normal, 1.5);
}

hui-field [data-description] {
  color: var(--hui-muted-foreground, GrayText);
}

hui-field [data-error] {
  align-items: center;
  color: var(--hui-destructive, CanvasText);
  display: flex;
  font-weight: var(--hui-weight-medium, 500);
  gap: var(--hui-space-xs, 0.25rem);
}

/*
 * The rules above set display, which beats the user agent's [hidden]. A host
 * hides a message it has not needed yet, or leaves an error empty until the
 * server fills it, and neither should draw - the error's glyph included
 * (ISS-037).
 */
hui-field [data-description][hidden],
hui-field [data-error][hidden],
hui-field [data-error]:empty {
  display: none;
}

hui-field [data-error]::before {
  background-color: currentColor;
  content: "";
  flex: none;
  height: 0.875rem;
  mask: var(--hui-icon-error-warning-line) center / contain no-repeat;
  width: 0.875rem;
}

/* -------------------------------------------------------------------------
 * Radio item (light DOM, managed by hui-radio-group)
 * ---------------------------------------------------------------------- */

/*
 * The item is a <button role="radio"> in the host's markup, so the user
 * agent's own button chrome has to go: without this reset the indicator sits
 * inside a second, native-looking box.
 */
.hui-radio {
  align-items: center;
  background: none;
  border: 0;
  color: inherit;
  cursor: pointer;
  display: inline-flex;
  margin: 0;
  padding: 0;
  text-align: left;
  font-family: var(--hui-font-sans, system-ui, sans-serif);
  font-size: var(--hui-text-sm, 0.875rem);
  gap: var(--hui-space-sm, 0.5rem);
}

.hui-radio::before {
  background: var(--hui-background, Canvas);
  border: 1px solid var(--hui-input, CanvasText);
  border-radius: 9999px;
  content: '';
  display: inline-block;
  flex-shrink: 0;
  height: 1rem;
  width: 1rem;
}

.hui-radio[aria-checked='true']::before {
  background: radial-gradient(
    circle,
    var(--hui-primary, CanvasText) 0 35%,
    var(--hui-background, Canvas) 40% 100%
  );
  border-color: var(--hui-primary, CanvasText);
}

.hui-radio[aria-disabled='true'] {
  cursor: not-allowed;
  opacity: 0.5;
}

/* -------------------------------------------------------------------------
 * Native select
 * ---------------------------------------------------------------------- */

.hui-native-select {
  appearance: none;
  background-color: transparent;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 1.05rem) 50%, calc(100% - 0.75rem) 50%;
  background-repeat: no-repeat;
  background-size: 0.3rem 0.3rem, 0.3rem 0.3rem;
  border: 1px solid var(--hui-input, CanvasText);
  border-radius: var(--hui-radius-lg, 0.45rem);
  color: var(--hui-foreground, CanvasText);
  font-size: var(--hui-text-sm, 0.875rem);
  height: 2rem;
  padding: 0 2rem 0 0.625rem;
  width: 100%;
}

.hui-native-select:focus-visible {
  outline: var(--hui-focus-ring-width, 2px) solid var(--hui-ring, Highlight);
  outline-offset: var(--hui-focus-ring-offset, 2px);
}

/* -------------------------------------------------------------------------
 * Input group
 * ---------------------------------------------------------------------- */

.hui-input-group {
  align-items: stretch;
  display: flex;
  width: 100%;
}

/*
 * Square the corners where two controls meet, so they read as one control.
 *
 * A radius on the host does nothing when the control is drawn inside a shadow
 * root, which is why a hui-button beside a hui-input kept its own left corners
 * and sat on the input's right ones rather than joining it (ISS-025). The
 * rules therefore name both: the host, for a light-DOM control like
 * .hui-native-select, and the part each element draws.
 */
.hui-input-group > *:not(:first-child) {
  margin-left: -1px;
}

.hui-input-group > *:not(:first-child),
.hui-input-group > *:not(:first-child)::part(button),
.hui-input-group > *:not(:first-child)::part(control),
.hui-input-group > *:not(:first-child)::part(input),
.hui-input-group > *:not(:first-child)::part(trigger) {
  border-bottom-left-radius: 0;
  border-top-left-radius: 0;
}

.hui-input-group > *:not(:last-child),
.hui-input-group > *:not(:last-child)::part(button),
.hui-input-group > *:not(:last-child)::part(control),
.hui-input-group > *:not(:last-child)::part(input),
.hui-input-group > *:not(:last-child)::part(trigger) {
  border-bottom-right-radius: 0;
  border-top-right-radius: 0;
}

.hui-input-group > *:focus-within,
.hui-input-group > *:focus-visible {
  z-index: 1;
}

@media (prefers-reduced-motion: reduce) {
  .hui-skeleton,
  .hui-spinner {
    animation-duration: 0.01ms;
    animation-iteration-count: 1;
  }

  .hui-progress__indicator {
    transition: none;
  }
}

/* -------------------------------------------------------------------------
 * Accordion
 *
 * The trigger and panel are light-DOM descendants of the slotted item, so the
 * component's shadow styles cannot reach them. These classes are how a host
 * gives them the nova shape; the component only wires the behaviour.
 * ---------------------------------------------------------------------- */

.hui-accordion__trigger {
  align-items: center;
  background: transparent;
  border: 0;
  color: var(--hui-foreground, CanvasText);
  cursor: pointer;
  display: flex;
  font: inherit;
  font-size: var(--hui-text-sm, 0.875rem);
  font-weight: var(--hui-weight-medium, 500);
  gap: var(--hui-space-sm, 0.5rem);
  justify-content: space-between;
  line-height: 1.25rem;
  padding: 0.625rem 0;
  text-align: start;
  width: 100%;
}

.hui-accordion__trigger::after {
  color: var(--hui-muted-foreground, GrayText);
  content: '';
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  height: 0.45em;
  transform: rotate(45deg);
  transition: transform var(--hui-duration-fast, 120ms) var(--hui-ease, ease);
  width: 0.45em;
}

.hui-accordion__trigger[aria-expanded='true']::after {
  transform: rotate(-135deg);
}

/* The answer is body text, as nova draws it, not a muted aside. */
.hui-accordion__panel {
  color: var(--hui-foreground, CanvasText);
  font-size: var(--hui-text-sm, 0.875rem);
  line-height: 1.25rem;
  padding-block-end: 0.625rem;
}

/* -------------------------------------------------------------------------
 * Table furniture (TASK-035)
 *
 * The host renders the rows; hui-table injects the selection column and wires
 * the sort headers. These classes style the two controls it creates, so a host
 * that uses the element alone still gets the nova shape.
 * ---------------------------------------------------------------------- */

.hui-table__select {
  text-align: center;
  width: 2.5rem;
}

/*
 * The selection box is drawn at hui-checkbox's 16px, inside a 24px label so a
 * thumb has the WCAG 2.5.8 minimum to hit: a click on the label is a click on
 * its box.
 */
.hui-table__hit {
  align-items: center;
  block-size: 1.5rem;
  cursor: pointer;
  display: inline-flex;
  inline-size: 1.5rem;
  justify-content: center;
  vertical-align: middle;
}

.hui-table__hit input {
  accent-color: var(--hui-primary, AccentColor);
  block-size: 1rem;
  cursor: pointer;
  inline-size: 1rem;
  margin: 0;
}

.hui-table__sort {
  align-items: center;
  background: none;
  border: 0;
  color: inherit;
  cursor: pointer;
  display: inline-flex;
  font: inherit;
  font-weight: var(--hui-weight-medium, 500);
  gap: var(--hui-space-xs, 0.25rem);
  padding: 0;
  text-align: start;
}

.hui-table__sort::after {
  border-bottom: 2px solid currentColor;
  border-right: 2px solid currentColor;
  content: '';
  height: 0.4em;
  opacity: 0;
  transform: rotate(45deg) translateY(-0.1em);
  transition: transform var(--hui-duration-fast, 120ms) var(--hui-ease, ease);
  width: 0.4em;
}

th[aria-sort] .hui-table__sort::after {
  opacity: 1;
}

th[aria-sort='descending'] .hui-table__sort::after {
  transform: rotate(-135deg) translateY(0.1em);
}

/* -------------------------------------------------------------------------
 * Sidebar menu (TASK-036)
 *
 * The menu itself is the host's markup, slotted into hui-sidebar. It is styled
 * from the sheet for the same reason the overlay items are (ISS-005): a rule
 * at the document level reaches any depth, where ::slotted() matches only
 * direct children.
 * ---------------------------------------------------------------------- */

.hui-sidebar-menu {
  display: flex;
  flex-direction: column;
  font-family: var(--hui-font-sans, system-ui, sans-serif);
  gap: 0;
  list-style: none;
  margin: 0;
  padding: 0;
}

.hui-sidebar-group {
  display: flex;
  flex-direction: column;
  gap: 0;
  padding-block: var(--hui-space-xs, 0.25rem);
}

/* A 32px row, the height of an item, at 70 percent of the body colour. */
.hui-sidebar-group-label {
  align-items: center;
  color: color-mix(in oklab, var(--hui-foreground, CanvasText) 70%, transparent);
  display: flex;
  font-size: var(--hui-text-xs, 0.75rem);
  font-weight: var(--hui-weight-medium, 500);
  height: 2rem;
  padding: 0 var(--hui-space-sm, 0.5rem);
}

.hui-sidebar-item {
  align-items: center;
  border-radius: var(--hui-radius-md, 0.36rem);
  color: inherit;
  display: flex;
  font-size: var(--hui-text-sm, 0.875rem);
  gap: var(--hui-space-sm, 0.5rem);
  min-height: 2rem;
  padding: 0 var(--hui-space-sm, 0.5rem);
  text-decoration: none;
}

.hui-sidebar-item:hover {
  background: var(--hui-accent, Canvas);
  color: var(--hui-accent-foreground, CanvasText);
}

.hui-sidebar-item[aria-current='page'] {
  background: var(--hui-accent, Canvas);
  color: var(--hui-accent-foreground, CanvasText);
  font-weight: var(--hui-weight-medium, 500);
}

.hui-sidebar-item:focus-visible {
  outline: var(--hui-focus-ring-width, 3px) solid
    color-mix(in oklab, var(--hui-ring, Highlight) 50%, transparent);
  outline-offset: var(--hui-focus-ring-offset, 0px);
}

/*
 * An avatar standing in for an item's icon - the account row in a footer -
 * takes the icon's size, so it lines up with the icons above and below it
 * and sits at the same inset when the rail collapses. Whatever size modifier
 * it carries, the item decides. Initials shrink with it.
 */
.hui-sidebar-item > .hui-avatar {
  height: 1rem;
  width: 1rem;
}

.hui-sidebar-item > .hui-avatar .hui-avatar__fallback {
  font-size: 0.5rem;
  font-weight: var(--hui-weight-medium, 500);
}

.hui-sidebar-badge {
  background: var(--hui-primary, CanvasText);
  border-radius: var(--hui-radius-pill, 9999px);
  color: var(--hui-primary-foreground, Canvas);
  font-size: var(--hui-text-xs, 0.75rem);
  font-weight: var(--hui-weight-medium, 500);
  line-height: 1;
  margin-inline-start: auto;
  padding: 0.15rem var(--hui-space-sm, 0.5rem);
}

/*
 * A brand in the header slot: a mark, and optionally a wordmark beside it.
 * The mark is in both states, so it stays drawn through a collapse the way
 * the item icons do; the wordmark is what the rail takes away. The rail is
 * 3rem by default, less the panel's 0.5rem either side, so a mark has 2rem
 * to sit in - which is what the sizing here leaves it.
 *
 * The inline-start padding is the item's own, so the mark lines up with the
 * icons of the menu below it rather than sitting 0.5rem to their left. The
 * rail keeps the same inset, so the mark does not move when it collapses.
 */
.hui-sidebar-brand {
  align-items: center;
  color: inherit;
  display: flex;
  flex: 1 1 auto;
  font-family: var(--hui-font-sans, system-ui, sans-serif);
  gap: var(--hui-space-sm, 0.5rem);
  min-width: 0;
  padding-inline-start: var(--hui-space-sm, 0.5rem);
  text-decoration: none;
}

.hui-sidebar-brand > svg,
.hui-sidebar-brand > img {
  flex: none;
  height: 1.25rem;
  width: 1.25rem;
}

.hui-sidebar-brand-label {
  font-size: var(--hui-text-sm, 0.875rem);
  font-weight: var(--hui-weight-semibold, 600);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/*
 * The item's label. Wrapping it lets the collapsed rail hide it and show the
 * icon alone, the way shadcn's icon mode does; a bare text node cannot be
 * targeted. A host that omits the wrapper gets its label clipped in the rail
 * instead of hidden.
 */
.hui-sidebar-item-label {
  flex: 1 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/*
 * The parts a collapse takes away, and the only parts that fade. The icons
 * stay - they are in both states - so they are held still while the panel
 * moves rather than fading and reappearing somewhere else.
 *
 * The fade runs with the resize, not after it, so the parts are in flow at
 * zero opacity through the motion and the rail's display rule only lands once
 * the panel has stopped. Each is a single clipped line, so nothing reflows
 * while the panel moves.
 */
.hui-sidebar-item-label,
.hui-sidebar-badge,
.hui-sidebar-group-label,
.hui-sidebar-brand,
.hui-sidebar-brand-label {
  transition: opacity var(--hui-duration-fast, 120ms) var(--hui-ease, ease);
}

/*
 * When the header's own collapse control is drawn, the rail has room for the
 * control alone, so the brand fades away and the control takes its place. It
 * fades with the resize like the labels, then is dropped once the panel has
 * settled. With the control at the foot, or none, the brand is the only thing
 * in the header and stays as the mark.
 */
hui-sidebar[data-header-control]:not([data-sheet])[data-animating='resize'] .hui-sidebar-brand {
  opacity: 0;
}

hui-sidebar[data-header-control]:not([data-sheet])[data-animating='fade-in'] .hui-sidebar-brand {
  opacity: 1;
}

/*
 * While the collapse runs the brand leaves the flow, so it cannot collide
 * with the control as the panel narrows and cannot jolt it when the motion
 * ends: it keeps its size and position and simply fades where it is, while
 * the control follows the narrowing panel to the rail's centre.
 */
hui-sidebar[data-header-control]:not([data-sheet])[data-visual='collapsed'][data-animating='resize'] .hui-sidebar-brand {
  inset-block-start: var(--hui-space-sm, 0.5rem);
  inset-inline-start: var(--hui-space-sm, 0.5rem);
  position: absolute;
}

hui-sidebar[data-header-control]:not([collapsible='offcanvas']):not([collapsible='none'])[data-visual='collapsed']:not([data-sheet]):not([data-animating]) .hui-sidebar-brand {
  display: none;
}

.hui-sidebar-group-label {
  white-space: nowrap;
}

hui-sidebar:not([data-sheet])[data-animating='resize'] .hui-sidebar-item-label,
hui-sidebar:not([data-sheet])[data-animating='resize'] .hui-sidebar-badge,
hui-sidebar:not([data-sheet])[data-animating='resize'] .hui-sidebar-group-label,
hui-sidebar:not([data-sheet])[data-animating='resize'] .hui-sidebar-brand-label {
  opacity: 0;
}

hui-sidebar:not([data-sheet])[data-animating='fade-in'] .hui-sidebar-item-label,
hui-sidebar:not([data-sheet])[data-animating='fade-in'] .hui-sidebar-badge,
hui-sidebar:not([data-sheet])[data-animating='fade-in'] .hui-sidebar-group-label,
hui-sidebar:not([data-sheet])[data-animating='fade-in'] .hui-sidebar-brand-label {
  opacity: 1;
}

/*
 * Collapsed to a rail: hide the label, the badge and the group heading. The
 * icon is not re-centred and the item is not shrunk to it - both of those the
 * rail used to do, and both moved things that had not otherwise moved. Keeping
 * the item's own padding-inline means the icon sits at the same inset in either
 * state, whatever size it is, and --hui-sidebar-rail-width is the width at
 * which that inset is also the middle. Keeping the item full width means the
 * current-page highlight narrows with the panel rather than snapping to the
 * icon when the motion ends, and stays a readable target in the rail.
 *
 * Scoped to the host so the same classes still work inside a sidebar that is
 * expanded or presenting as a sheet. The host test is what collapsing is not,
 * rather than that it is a rail: both rail and overlay leave a rail behind,
 * and the element's own width rule already asks the question that way. The selector is data-visual, not state,
 * and it waits for data-animating to clear: state changes on the click, and
 * the drawn state changes when the panel starts moving, but hiding a label
 * while the panel is still 16rem wide would empty it a beat too early. So the
 * rail layout is what the panel settles into, not something it animates
 * through.
 */
hui-sidebar:not([collapsible='offcanvas']):not([collapsible='none'])[data-visual='collapsed']:not([data-sheet]):not([data-animating]) .hui-sidebar-item-label,
hui-sidebar:not([collapsible='offcanvas']):not([collapsible='none'])[data-visual='collapsed']:not([data-sheet]):not([data-animating]) .hui-sidebar-badge,
hui-sidebar:not([collapsible='offcanvas']):not([collapsible='none'])[data-visual='collapsed']:not([data-sheet]):not([data-animating]) .hui-sidebar-group-label,
hui-sidebar:not([collapsible='offcanvas']):not([collapsible='none'])[data-visual='collapsed']:not([data-sheet]):not([data-animating]) .hui-sidebar-brand-label {
  display: none;
}

hui-sidebar:not([collapsible='offcanvas']):not([collapsible='none'])[data-visual='collapsed']:not([data-sheet]) .hui-sidebar-item > svg {
  flex: none;
}

/*
 * The rail stacks a brand above the collapse control. The brand keeps the
 * expanded row's 2rem height, so its mark does not step upward when the
 * header becomes a column; .hui-sidebar-brand already centres it in that row.
 */
hui-sidebar:not([collapsible='offcanvas']):not([collapsible='none'])[data-visual='collapsed']:not([data-sheet]):not([data-animating]) .hui-sidebar-brand {
  min-height: 2rem;
}

/* -------------------------------------------------------------------------
 * Panel (ENH-022)
 *
 * A list, tree, table or grid placed straight in a hui-panel fills the body
 * and scrolls inside itself, so its own toolbar, header row or pager stays in
 * view, and drops its border and radius: the panel is already the frame.
 * ---------------------------------------------------------------------- */

hui-panel > hui-tree,
hui-panel > hui-listbox,
hui-panel > hui-table,
hui-panel > hui-data-grid {
  block-size: 100%;
  border: 0;
  border-radius: 0;
  box-sizing: border-box;
}

hui-panel > hui-data-grid > .hui-grid {
  block-size: 100%;
  gap: 0;
}

hui-panel > hui-data-grid .hui-grid__scroll {
  border: 0;
  border-radius: 0;
  flex: 1 1 auto;
  max-block-size: none;
  min-block-size: 0;
}

hui-panel > hui-data-grid .hui-grid__toolbar,
hui-panel > hui-data-grid .hui-grid__footer {
  padding: var(--hui-space-sm, 0.5rem) var(--hui-space-md, 0.75rem);
}

hui-panel > hui-data-grid .hui-grid__footer {
  border-block-start: 1px solid var(--hui-border, CanvasText);
}

/* -------------------------------------------------------------------------
 * Data grid (ENH-017)
 *
 * hui-data-grid renders its view into its own light DOM, so the class sheet
 * reaches it and the cells a host's templates draw. The table takes .hui-table
 * for nova's table geometry - 14px text, 8px cells, border-bottom rows - and
 * these add what a grid needs on top: a toolbar and footer at the button
 * group's 28px, a sticky header with an opaque fill, the focused cell's ring,
 * the selected row's primary tint, and sort, resize and check furniture.
 * Cells keep to one line and clip with an ellipsis, as a data grid's do.
 * ---------------------------------------------------------------------- */

hui-data-grid {
  --hui-grid-select-width: 2.5rem;
  color: var(--hui-foreground, CanvasText);
  display: block;
  font-family: var(--hui-font-sans, system-ui, sans-serif);
  font-size: var(--hui-text-sm, 0.875rem);
  max-inline-size: 100%;
}

.hui-grid {
  display: flex;
  flex-direction: column;
  gap: var(--hui-space-sm, 0.5rem);
}

.hui-grid__toolbar,
.hui-grid__footer {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: var(--hui-space-sm, 0.5rem);
}

.hui-grid__footer {
  color: var(--hui-muted-foreground, GrayText);
}

.hui-grid__spacer {
  flex: 1 1 auto;
}

.hui-grid__search,
.hui-grid__filter {
  background: var(--hui-background, Canvas);
  border: 1px solid var(--hui-input, CanvasText);
  border-radius: var(--hui-radius-lg, 0.45rem);
  box-sizing: border-box;
  color: var(--hui-foreground, CanvasText);
  font: inherit;
  font-size: var(--hui-text-sm, 0.875rem);
  min-block-size: 1.75rem;
  padding: 0 0.5rem;
}

.hui-grid__search {
  inline-size: min(100%, 16rem);
  min-block-size: 2rem;
}

.hui-grid__filter {
  font-weight: var(--hui-weight-normal, 400);
  inline-size: 100%;
  min-inline-size: 5rem;
}

hui-select.hui-grid__filter {
  border: 0;
  display: block;
  padding: 0;
}

.hui-grid__range {
  display: flex;
  gap: var(--hui-space-xs, 0.25rem);
}

.hui-grid__search:focus-visible,
.hui-grid__filter:focus-visible,
.hui-grid__tool:focus-visible {
  border-color: var(--hui-ring, Highlight);
  box-shadow: 0 0 0 var(--hui-focus-ring-width, 3px) color-mix(in oklab, var(--hui-ring, Highlight) 50%, transparent);
  outline: none;
}

.hui-grid__tool {
  align-items: center;
  background: var(--hui-background, Canvas);
  border: 1px solid var(--hui-border, CanvasText);
  border-radius: var(--hui-radius-lg, 0.45rem);
  color: var(--hui-foreground, CanvasText);
  cursor: pointer;
  display: inline-flex;
  font: inherit;
  font-size: var(--hui-text-sm, 0.875rem);
  font-weight: var(--hui-weight-medium, 500);
  gap: 0.375rem;
  justify-content: center;
  min-block-size: 1.75rem;
  min-inline-size: 1.75rem;
  padding: 0 0.625rem;
}

.hui-grid__tool:hover:not(:disabled) {
  background: var(--hui-muted, Canvas);
}

.hui-grid__tool:disabled {
  cursor: default;
  opacity: 0.5;
}

.hui-grid__pager {
  align-items: center;
  display: inline-flex;
  gap: var(--hui-space-xs, 0.25rem);
}

.hui-grid__page {
  padding-inline: var(--hui-space-xs, 0.25rem);
  white-space: nowrap;
}

/* The grid scrolls in its own box, both ways; give the element a height to scroll rows under the header. */
.hui-grid__scroll {
  border: 1px solid var(--hui-border, CanvasText);
  border-radius: var(--hui-radius-lg, 0.45rem);
  max-block-size: var(--hui-grid-height, none);
  overflow: auto;
  scroll-padding-block-start: 5rem;
}

table.hui-grid__table {
  border-collapse: separate;
  border-spacing: 0;
}

.hui-grid__table th,
.hui-grid__table td {
  max-inline-size: 24rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.hui-grid__table thead {
  position: sticky;
  top: 0;
  z-index: 2;
}

.hui-grid__table thead th,
.hui-grid__table thead td {
  background: var(--hui-card, Canvas);
}

.hui-grid__head {
  cursor: default;
  position: relative;
  user-select: none;
}

.hui-grid__head[data-sortable] {
  cursor: pointer;
}

.hui-grid__head[data-sortable]:hover {
  color: var(--hui-foreground, CanvasText);
}

.hui-grid__head[aria-sort="ascending"],
.hui-grid__head[aria-sort="descending"] {
  color: var(--hui-foreground, CanvasText);
}

.hui-grid__label {
  vertical-align: middle;
}

/* The sort mark: the chevron the class sheet masks, up or down, and the key's order when several sort. */
.hui-grid__sort {
  align-items: center;
  display: inline-flex;
  gap: 1px;
  margin-inline-start: 0.25rem;
  vertical-align: middle;
}

.hui-grid__sort::before {
  background: currentColor;
  block-size: 0.875rem;
  content: '';
  inline-size: 0.875rem;
  mask: var(--hui-icon-arrow-right-s-line) center / contain no-repeat;
  opacity: 0;
  rotate: -90deg;
}

.hui-grid__head[data-sortable]:hover .hui-grid__sort::before {
  opacity: 0.4;
}

.hui-grid__sort[data-dir]::before {
  opacity: 1;
}

.hui-grid__head[data-sortable]:hover .hui-grid__sort[data-dir]::before {
  opacity: 1;
}

.hui-grid__sort[data-dir="desc"]::before {
  rotate: 90deg;
}

.hui-grid__sort-order {
  font-size: 0.6875rem;
  font-variant-numeric: tabular-nums;
}

.hui-grid__resize {
  cursor: col-resize;
  inset-block: 0;
  inset-inline-end: 0;
  inline-size: 0.5rem;
  position: absolute;
  touch-action: none;
}

.hui-grid__resize:hover {
  background: color-mix(in oklab, var(--hui-ring, Highlight) 40%, transparent);
}

.hui-grid__filters td {
  padding-block: 0 var(--hui-space-sm, 0.5rem);
  overflow: visible;
}

.hui-grid__table .hui-grid__align-end {
  text-align: end;
}

.hui-grid__table .hui-grid__align-center {
  text-align: center;
}

.hui-grid__table .hui-grid__select {
  inline-size: var(--hui-grid-select-width);
  max-inline-size: var(--hui-grid-select-width);
  min-inline-size: var(--hui-grid-select-width);
  text-align: center;
}

/* The row check, at hui-checkbox's 16px; decoration, because the row carries aria-selected. */
.hui-grid__check {
  align-items: center;
  block-size: 1rem;
  border: 1px solid var(--hui-input, CanvasText);
  border-radius: 0.25rem;
  box-sizing: border-box;
  cursor: pointer;
  display: inline-flex;
  inline-size: 1rem;
  justify-content: center;
  vertical-align: middle;
}

.hui-grid__check::after {
  background: var(--hui-primary-foreground, Canvas);
  block-size: 0.75rem;
  content: '';
  inline-size: 0.75rem;
  mask: var(--hui-icon-check-line) center / contain no-repeat;
  opacity: 0;
}

.hui-grid__check[data-checked="true"],
.hui-grid__check[aria-checked="true"],
.hui-grid__check[aria-checked="mixed"] {
  background: var(--hui-primary, Highlight);
  border-color: var(--hui-primary, Highlight);
}

.hui-grid__check[data-checked="true"]::after,
.hui-grid__check[aria-checked="true"]::after {
  opacity: 1;
}

.hui-grid__check[aria-checked="mixed"]::after {
  mask-image: var(--hui-icon-subtract-line);
  opacity: 1;
}

.hui-grid__table tbody tr[aria-selected="true"] td {
  background: color-mix(in oklab, var(--hui-primary, Highlight) 8%, transparent);
}

.hui-grid__table tbody tr:hover td {
  background: color-mix(in oklab, var(--hui-foreground, CanvasText) 4%, transparent);
}

.hui-grid__table tbody tr[aria-selected="true"]:hover td {
  background: color-mix(in oklab, var(--hui-primary, Highlight) 12%, transparent);
}

/* The active cell: one tab stop, drawn as the focus ring when it has focus. */
.hui-grid__table [data-r]:focus-visible,
.hui-grid__table [role="checkbox"]:focus-visible {
  box-shadow: inset 0 0 0 2px var(--hui-ring, Highlight);
  outline: none;
}

.hui-grid__table [data-r]:focus {
  outline: none;
}

.hui-grid__table a {
  color: inherit;
  text-underline-offset: 2px;
}

/*
 * A link or button a cell template draws stands alone in its cell, so it
 * gets the 24px target WCAG 2.5.8 asks for (the 320px audit measured 18px);
 * the row is 36px, so the row does not grow.
 */
.hui-grid__table td a,
.hui-grid__table td button {
  align-items: center;
  display: inline-flex;
  min-block-size: 1.5rem;
  vertical-align: middle;
}

.hui-grid__empty td,
.hui-grid__error {
  color: var(--hui-muted-foreground, GrayText);
  padding-block: var(--hui-space-xl, 1.5rem);
  text-align: center;
}

.hui-grid__error {
  color: var(--hui-destructive, CanvasText);
  display: block;
  padding: 0;
}

/* While a server query is out, the rows it will replace fade and a bar runs under the header. */
hui-data-grid[data-loading] tbody {
  opacity: 0.55;
  transition: opacity var(--hui-duration-normal, 200ms) var(--hui-ease, ease);
}

hui-data-grid[data-loading] .hui-grid__scroll {
  background-image: linear-gradient(90deg, transparent, var(--hui-primary, Highlight), transparent);
  background-repeat: no-repeat;
  background-size: 40% 2px;
  animation: hui-grid-loading 1.2s linear infinite;
}

@keyframes hui-grid-loading {
  from {
    background-position: -40% 0;
  }
  to {
    background-position: 140% 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  hui-data-grid[data-loading] .hui-grid__scroll {
    animation: none;
    background-position: 50% 0;
  }
}

/* The first data column stays put while the grid scrolls sideways. */
.hui-grid[data-sticky-column] .hui-grid__select,
.hui-grid[data-sticky-column] tr > :first-child,
.hui-grid[data-sticky-column] tr > .hui-grid__select + * {
  background: var(--hui-card, Canvas);
  inset-inline-start: 0;
  position: sticky;
  z-index: 1;
}

.hui-grid[data-sticky-column] tr > .hui-grid__select + * {
  inset-inline-start: var(--hui-grid-select-width);
}

.hui-grid[data-sticky-column] thead tr > :first-child,
.hui-grid[data-sticky-column] thead tr > .hui-grid__select + * {
  z-index: 3;
}

.hui-grid__status {
  block-size: 1px;
  clip-path: inset(50%);
  inline-size: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
}

@media (forced-colors: active) {
  .hui-grid__table tbody tr[aria-selected="true"] td {
    background: Highlight;
    color: HighlightText;
  }

  /*
   * What a cell template draws - a badge, a link - keeps its own box, which
   * forced colours paints Canvas: a white island in a Highlight row. Inside a
   * selected row it takes the row's pair instead, so the whole row reads as
   * selected; transparent keeps its alpha under forced colours, so the row's
   * Highlight shows through.
   */
  .hui-grid__table tbody tr[aria-selected="true"] td * {
    background-color: transparent;
    border-color: HighlightText;
    color: HighlightText;
  }

  .hui-grid__table [data-r]:focus-visible,
  .hui-grid__table [role="checkbox"]:focus-visible {
    outline: 2px solid Highlight;
    outline-offset: -2px;
  }

  .hui-grid__sort::before,
  .hui-grid__check::after {
    background: CanvasText;
  }

  .hui-grid__check {
    border-color: CanvasText;
  }
}

/* -------------------------------------------------------------------------
 * Path (ENH-020)
 *
 * hui-path-node renders the host's own content in its default slot, so what
 * the host writes there - a link, or the trigger of the node's menu or popover
 * - is styled here, in the document, where it lives. Each is a crumb: nova's
 * breadcrumb colours (muted, the foreground on hover and for the current
 * node) on a 28px, md-radius button shape, the Explorer address bar's hit
 * area, with an icon and its label 6px apart.
 * ---------------------------------------------------------------------- */

hui-path-node > a,
hui-path-node > hui-dropdown-menu > [slot="trigger"],
hui-path-node > hui-popover > [slot="trigger"],
hui-path-node > [data-crumb] {
  align-items: center;
  background: none;
  border: 1px solid transparent;
  border-radius: var(--hui-radius-md, 0.36rem);
  box-sizing: border-box;
  color: var(--hui-muted-foreground, GrayText);
  cursor: pointer;
  display: inline-flex;
  font: inherit;
  font-family: var(--hui-font-sans, system-ui, sans-serif);
  font-size: var(--hui-text-sm, 0.875rem);
  gap: 0.375rem;
  line-height: 1.25rem;
  max-inline-size: 16rem;
  min-block-size: 1.75rem;
  overflow: hidden;
  padding: 0 0.375rem;
  text-decoration: none;
  text-overflow: ellipsis;
  transition: background-color var(--hui-duration-fast, 120ms) var(--hui-ease, ease),
    color var(--hui-duration-fast, 120ms) var(--hui-ease, ease);
  white-space: nowrap;
}

hui-path-node > a:hover,
hui-path-node > hui-dropdown-menu > [slot="trigger"]:hover,
hui-path-node > hui-popover > [slot="trigger"]:hover,
hui-path-node > hui-dropdown-menu[open] > [slot="trigger"],
hui-path-node > hui-popover[open] > [slot="trigger"] {
  background: var(--hui-muted, Canvas);
  color: var(--hui-foreground, CanvasText);
}

hui-path-node > a:focus-visible,
hui-path-node > hui-dropdown-menu > [slot="trigger"]:focus-visible,
hui-path-node > hui-popover > [slot="trigger"]:focus-visible {
  border-color: var(--hui-ring, Highlight);
  box-shadow: 0 0 0 var(--hui-focus-ring-width, 3px)
    color-mix(in oklab, var(--hui-ring, Highlight) 50%, transparent);
  outline: none;
}

/*
 * A children menu is opened by the node's chevron and anchored to its own
 * trigger slot, which the host leaves empty. The node places that slot just
 * before the chevron; giving it the chevron's height makes the menu open under
 * the chevron rather than over it.
 */
hui-path-node > [slot="children"] {
  display: block;
}

hui-path-node > [slot="children"]::part(anchor) {
  block-size: 1.75rem;
  display: block;
}

/* A label node is text, not a control: the crumb's box without the hover. */
hui-path-node {
  color: var(--hui-muted-foreground, GrayText);
  font-family: var(--hui-font-sans, system-ui, sans-serif);
  font-size: var(--hui-text-sm, 0.875rem);
}

hui-path-node[current],
hui-path-node[current] > a,
hui-path-node[current] > hui-dropdown-menu > [slot="trigger"],
hui-path-node[current] > hui-popover > [slot="trigger"] {
  color: var(--hui-foreground, CanvasText);
}

hui-path-node > svg,
hui-path-node > a > svg,
hui-path-node > hui-dropdown-menu > [slot="trigger"] > svg,
hui-path-node > hui-popover > [slot="trigger"] > svg {
  flex: none;
  height: var(--hui-icon-size, 1rem);
  width: var(--hui-icon-size, 1rem);
}

@media (forced-colors: active) {
  hui-path-node > a,
  hui-path-node > hui-dropdown-menu > [slot="trigger"],
  hui-path-node > hui-popover > [slot="trigger"] {
    color: LinkText;
  }

  hui-path-node[current] > a,
  hui-path-node[current] {
    color: CanvasText;
  }
}

/* -------------------------------------------------------------------------
 * Navigation menu (TASK-037)
 * ---------------------------------------------------------------------- */

.hui-nav-link {
  align-items: center;
  border-radius: var(--hui-radius-md, 0.36rem);
  color: inherit;
  display: inline-flex;
  font-family: var(--hui-font-sans, system-ui, sans-serif);
  font-size: var(--hui-text-sm, 0.875rem);
  font-weight: var(--hui-weight-medium, 500);
  gap: var(--hui-space-xs, 0.25rem);
  min-height: 2rem;
  padding: 0 var(--hui-space-sm, 0.5rem);
  text-decoration: none;
}

.hui-nav-link:hover {
  background: var(--hui-accent, Canvas);
  color: var(--hui-accent-foreground, CanvasText);
}

.hui-nav-link[aria-current='page'] {
  color: var(--hui-primary-text, LinkText);
}

/*
 * hui-table scrolls sideways in its own box (ISS-020), as shadcn's Table wraps
 * its table in overflow-x: auto. A five-column table is wider than a phone, and
 * without this the whole page scrolled instead. While it overflows the element
 * makes itself a focusable region, so it needs a visible focus ring.
 */
hui-table {
  display: block;
  max-inline-size: 100%;
  overflow-x: auto;
}

/*
 * Cells keep to one line inside the element, as shadcn's TableHead and
 * TableCell do (whitespace-nowrap): a narrow table scrolls rather than
 * squeezing "12 October 2026" onto three lines. A bare .hui-table outside
 * the element wraps as before.
 */
hui-table th,
hui-table td {
  white-space: nowrap;
}

hui-table[data-overflowing]:focus-visible {
  outline: var(--hui-focus-ring-width, 2px) solid var(--hui-ring, Highlight);
  outline-offset: var(--hui-focus-ring-offset, 2px);
}

/*
 * Sticky header (TASK-035). In CSS rather than a scroll listener, so a swap
 * cannot leave one behind. The header needs an opaque fill or the rows show
 * through it. Because the element scrolls sideways it is the header's scroll
 * container, so the header sticks inside the element: give it a height, as
 * the sticky example does, for the rows to scroll under it.
 */
hui-table[sticky] thead th {
  background: var(--hui-card, Canvas);
  position: sticky;
  top: 0;
  z-index: 1;
}

/* -------------------------------------------------------------------------
 * Listbox (TASK-055)
 *
 * hui-listbox is light DOM: the options are the host's rows, styled here at
 * nova's select-item geometry, measured from the base-nova registry's
 * SelectItem - 28px rows, 4px by 6px padding with 32px kept at the end for the
 * check, a 6px gap, 14px text on 20px, the md radius - and its group label:
 * 12px muted text, 4px by 6px.
 *
 * The cursor is data-cursor, which the element mirrors from
 * aria-activedescendant because CSS cannot select that target. While the list
 * has focus the cursor row takes the accent fill; when focus is elsewhere it
 * fades to an outline, which is how a page with several lists shows which one
 * is active. Selected rows take the check and a primary tint.
 * ---------------------------------------------------------------------- */

hui-listbox {
  background: var(--hui-background, Canvas);
  border: 1px solid var(--hui-border, CanvasText);
  border-radius: var(--hui-radius-lg, 0.45rem);
  box-sizing: border-box;
  color: var(--hui-foreground, CanvasText);
  display: block;
  font-family: var(--hui-font-sans, system-ui, sans-serif);
  font-size: var(--hui-text-sm, 0.875rem);
  overflow: auto;
  overscroll-behavior: contain;
  padding: var(--hui-space-xs, 0.25rem);
}

/*
 * With column heads the element still scrolls, so the head sticks inside it,
 * but the padding moves onto the rows: a sticky head sits below the scroll
 * box's padding, and the rows would show through the gap.
 */
hui-listbox:has(> [data-rows]) {
  padding: 0;
}

hui-listbox > [data-rows] {
  padding: var(--hui-space-xs, 0.25rem);
}

hui-listbox:focus-visible,
hui-listbox > [data-rows]:focus-visible {
  box-shadow: inset 0 0 0 1px var(--hui-ring, Highlight);
  outline: none;
}

/*
 * content-visibility lets a list of thousands skip the rows out of view when
 * a range or Ctrl+A restyles them all: on 5,000 rows it took a Shift+End from
 * 35ms to 8ms in Chromium. The intrinsic size keeps the scroll height right
 * for rows never drawn.
 */
hui-listbox [role="option"] {
  align-items: center;
  border-radius: var(--hui-radius-md, 0.36rem);
  box-sizing: border-box;
  contain-intrinsic-block-size: auto 1.75rem;
  content-visibility: auto;
  cursor: default;
  display: flex;
  gap: 0.375rem;
  line-height: 1.25rem;
  min-block-size: 1.75rem;
  padding: 0.25rem 2rem 0.25rem 0.375rem;
  position: relative;
  user-select: none;
}

hui-listbox [role="option"][hidden] {
  display: none;
}

hui-listbox [role="option"][aria-selected="true"] {
  background: color-mix(in oklab, var(--hui-primary, Highlight) 8%, transparent);
  color: var(--hui-primary-text, CanvasText);
  font-weight: var(--hui-weight-medium, 500);
}

/*
 * Every row carries the check, hidden until it is selected. Creating the
 * pseudo-element as a row is selected cost Firefox 240ms for a Shift+End over
 * 5,000 rows; revealing one that exists costs 60ms.
 */
hui-listbox [role="option"]::after {
  background: currentColor;
  block-size: 1rem;
  content: '';
  inline-size: 1rem;
  inset-inline-end: 0.5rem;
  mask: var(--hui-icon-check-line) center / contain no-repeat;
  opacity: 0;
  position: absolute;
  top: 50%;
  translate: 0 -50%;
}

hui-listbox [role="option"][aria-selected="true"]::after {
  opacity: 1;
}

hui-listbox [role="option"][data-cursor] {
  outline: 1px solid var(--hui-ring, GrayText);
  outline-offset: -1px;
}

hui-listbox:focus [role="option"][data-cursor],
hui-listbox > [data-rows]:focus [role="option"][data-cursor] {
  background: var(--hui-accent, Canvas);
  color: var(--hui-accent-foreground, CanvasText);
  outline: none;
}

hui-listbox [role="option"][aria-disabled="true"],
hui-listbox [role="option"][disabled] {
  opacity: 0.5;
}

hui-listbox[disabled] {
  opacity: 0.5;
}

hui-listbox [role="group"] > [data-group-label] {
  color: var(--hui-muted-foreground, GrayText);
  font-size: var(--hui-text-xs, 0.75rem);
  line-height: 1rem;
  padding: 0.25rem 0.375rem;
}

/*
 * Columns: the head's row and every option lay their cells on the one
 * template the element sets from `columns`, with the same inline padding, so
 * a cell lines up with its head at any width.
 */
hui-listbox[columns] [role="option"],
hui-listbox [data-head-row] {
  column-gap: var(--hui-space-sm, 0.5rem);
  display: grid;
  grid-template-columns: var(--hui-listbox-columns, 1fr);
}

hui-listbox[columns] [role="option"] > *,
hui-listbox [data-head-row] > * {
  min-inline-size: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

hui-listbox > [data-head] {
  background: var(--hui-background, Canvas);
  border-block-end: 1px solid var(--hui-border, CanvasText);
  padding-inline: var(--hui-space-xs, 0.25rem);
  position: sticky;
  top: 0;
  z-index: 1;
}

hui-listbox [data-head-row] {
  align-items: center;
  color: var(--hui-muted-foreground, GrayText);
  font-size: var(--hui-text-xs, 0.75rem);
  font-weight: var(--hui-weight-medium, 500);
  line-height: 1rem;
  min-block-size: 2rem;
  padding: 0 2rem 0 0.375rem;
}

.hui-listbox__sort {
  align-items: center;
  background: none;
  border: 0;
  border-radius: var(--hui-radius-sm, 0.27rem);
  color: inherit;
  cursor: pointer;
  display: inline-flex;
  font: inherit;
  gap: var(--hui-space-xs, 0.25rem);
  max-inline-size: 100%;
  min-block-size: 1.5rem;
  padding: 0;
  text-align: start;
  text-transform: inherit;
}

.hui-listbox__sort:hover {
  color: var(--hui-foreground, CanvasText);
}

.hui-listbox__sort:focus-visible {
  outline: 2px solid var(--hui-ring, Highlight);
  outline-offset: 2px;
}

.hui-listbox__sort::after {
  border-bottom: 1.5px solid currentColor;
  border-right: 1.5px solid currentColor;
  content: '';
  flex: none;
  height: 0.35em;
  opacity: 0;
  transform: rotate(45deg) translateY(-0.1em);
  width: 0.35em;
}

[aria-sort] > .hui-listbox__sort::after {
  opacity: 1;
}

[aria-sort='descending'] > .hui-listbox__sort::after {
  transform: rotate(-135deg) translateY(0.1em);
}

/*
 * Forced colours drop fills and shadows, so selection is drawn in the
 * system's Highlight pair and the cursor as a system outline, thicker while
 * the list has focus.
 */
@media (forced-colors: active) {
  hui-listbox [role="option"][aria-selected="true"] {
    background: Highlight;
    color: HighlightText;
    forced-color-adjust: none;
  }

  hui-listbox [role="option"][data-cursor] {
    outline: 1px dashed CanvasText;
    outline-offset: -2px;
  }

  hui-listbox:focus [role="option"][data-cursor],
  hui-listbox > [data-rows]:focus [role="option"][data-cursor] {
    outline: 2px solid CanvasText;
    outline-offset: -2px;
  }

  hui-listbox [role="option"][aria-selected="true"][data-cursor] {
    outline-color: HighlightText;
  }
}

/* -------------------------------------------------------------------------
 * Tree (TASK-056)
 *
 * hui-tree is light DOM, like hui-listbox, and its rows are drawn at the same
 * nova list-row geometry: each item's [data-label] is the row - 28px, 4px by
 * 6px padding with 32px kept at the end for the check, a 6px gap, 14px text
 * on 20px, the md radius - indented by the level the element writes as
 * --hui-tree-level. The indent is the marker and the gap, 22px, so a child's
 * marker sits under its parent's label.
 *
 * A branch's marker is Remix's arrow-right-s-line, turned a quarter when the
 * branch is open; a leaf's marker is the same width and empty, so labels align
 * at every level. The cursor and selection are drawn as in hui-listbox.
 * ---------------------------------------------------------------------- */

hui-tree {
  --hui-tree-indent: 1.375rem;
  background: var(--hui-background, Canvas);
  border: 1px solid var(--hui-border, CanvasText);
  border-radius: var(--hui-radius-lg, 0.45rem);
  box-sizing: border-box;
  color: var(--hui-foreground, CanvasText);
  display: block;
  font-family: var(--hui-font-sans, system-ui, sans-serif);
  font-size: var(--hui-text-sm, 0.875rem);
  overflow: auto;
  overscroll-behavior: contain;
  padding: var(--hui-space-xs, 0.25rem);
}

hui-tree:focus-visible {
  box-shadow: inset 0 0 0 1px var(--hui-ring, Highlight);
  outline: none;
}

hui-tree [role="treeitem"] {
  display: block;
}

hui-tree [role="treeitem"] > [data-label] {
  align-items: center;
  border-radius: var(--hui-radius-md, 0.36rem);
  box-sizing: border-box;
  cursor: default;
  display: flex;
  gap: 0.375rem;
  line-height: 1.25rem;
  min-block-size: 1.75rem;
  padding-block: 0.25rem;
  padding-inline: calc(0.375rem + (var(--hui-tree-level, 1) - 1) * var(--hui-tree-indent)) 2rem;
  position: relative;
  user-select: none;
}

/* Children of a closed branch are hidden, and skipped by every key. */
hui-tree [role="treeitem"][aria-expanded="false"] > [role="group"] {
  display: none;
}

hui-tree [role="group"] {
  margin: 0;
  padding: 0;
}

.hui-tree__marker {
  block-size: 1rem;
  color: var(--hui-muted-foreground, GrayText);
  flex: none;
  inline-size: 1rem;
  position: relative;
}

[aria-expanded] > [data-label] > .hui-tree__marker {
  background: currentColor;
  cursor: pointer;
  mask: var(--hui-icon-arrow-right-s-line) center / contain no-repeat;
  transition: rotate var(--hui-duration-fast, 120ms) var(--hui-ease, ease);
}

/* The chevron is 16px; a thumb gets 24px (WCAG 2.5.8), as hui-checkbox's box does. */
[aria-expanded] > [data-label] > .hui-tree__marker::before {
  content: '';
  inset: -4px;
  position: absolute;
}

[aria-expanded="true"] > [data-label] > .hui-tree__marker {
  rotate: 90deg;
}

hui-tree:dir(rtl) [aria-expanded="false"] > [data-label] > .hui-tree__marker {
  scale: -1 1;
}

/* Loading children: the chevron becomes the spinner until they arrive. */
[aria-busy="true"] > [data-label] > .hui-tree__marker {
  animation: hui-spin 1s linear infinite;
  mask-image: var(--hui-icon-loader-line);
  rotate: none;
}

/*
 * Checkbox mode draws hui-checkbox's nova box in the row: 16px, a 1px input
 * border, a 4px radius, the primary fill with the check or, for a mixed
 * branch, the indeterminate dash. It is decoration: the tree keeps focus.
 */
.hui-tree__check {
  background: var(--hui-background, Canvas);
  block-size: 1rem;
  border: 1px solid var(--hui-input, CanvasText);
  border-radius: 4px;
  box-sizing: border-box;
  color: var(--hui-primary-foreground, Canvas);
  cursor: pointer;
  flex: none;
  inline-size: 1rem;
  position: relative;
}

.hui-tree__check::before {
  content: '';
  inset: -5px;
  position: absolute;
}

.hui-tree__check::after {
  background: currentColor;
  content: '';
  inset: 0;
  mask: var(--hui-icon-check-line) center / 0.875rem no-repeat;
  opacity: 0;
  position: absolute;
}

[aria-checked="true"] > [data-label] > .hui-tree__check,
[aria-checked="mixed"] > [data-label] > .hui-tree__check {
  background: var(--hui-primary, CanvasText);
  border-color: var(--hui-primary, CanvasText);
}

[aria-checked="true"] > [data-label] > .hui-tree__check::after,
[aria-checked="mixed"] > [data-label] > .hui-tree__check::after {
  opacity: 1;
}

[aria-checked="mixed"] > [data-label] > .hui-tree__check::after {
  mask-image: var(--hui-icon-subtract-line);
}

hui-tree [role="treeitem"][aria-selected="true"] > [data-label] {
  background: color-mix(in oklab, var(--hui-primary, Highlight) 8%, transparent);
  color: var(--hui-primary-text, CanvasText);
  font-weight: var(--hui-weight-medium, 500);
}

hui-tree [role="treeitem"] > [data-label]::after {
  background: currentColor;
  block-size: 1rem;
  content: '';
  inline-size: 1rem;
  inset-inline-end: 0.5rem;
  mask: var(--hui-icon-check-line) center / contain no-repeat;
  opacity: 0;
  position: absolute;
  top: 50%;
  translate: 0 -50%;
}

hui-tree [role="treeitem"][aria-selected="true"] > [data-label]::after {
  opacity: 1;
}

hui-tree [role="treeitem"][data-cursor] > [data-label] {
  outline: 1px solid var(--hui-ring, GrayText);
  outline-offset: -1px;
}

hui-tree:focus [role="treeitem"][data-cursor] > [data-label] {
  background: var(--hui-accent, Canvas);
  color: var(--hui-accent-foreground, CanvasText);
  outline: none;
}

hui-tree [role="treeitem"][aria-disabled="true"] > [data-label],
hui-tree [role="treeitem"][disabled] > [data-label] {
  opacity: 0.5;
}

hui-tree[disabled] {
  opacity: 0.5;
}

/*
 * Indent guides: a line down each open group, under the centre of its
 * parent's marker. The group inherits its parent's --hui-tree-level.
 */
hui-tree[guides] [role="group"] {
  position: relative;
}

hui-tree[guides] [role="group"]::before {
  border-inline-start: 1px solid var(--hui-border, GrayText);
  content: '';
  inset-block: 0;
  inset-inline-start: calc(0.375rem + 0.5rem - 0.5px + (var(--hui-tree-level, 1) - 1) * var(--hui-tree-indent));
  pointer-events: none;
  position: absolute;
}

@media (prefers-reduced-motion: reduce) {
  [aria-expanded] > [data-label] > .hui-tree__marker {
    transition: none;
  }

  [aria-busy="true"] > [data-label] > .hui-tree__marker {
    animation: none;
  }
}

/*
 * Forced colours drop fills, which would take the masked glyphs with them,
 * so they are drawn in system colours; selection is the Highlight pair and
 * the cursor a system outline, as in hui-listbox.
 */
@media (forced-colors: active) {
  hui-tree [role="treeitem"][aria-selected="true"] > [data-label] {
    background: Highlight;
    color: HighlightText;
    forced-color-adjust: none;
  }

  [aria-expanded] > [data-label] > .hui-tree__marker,
  hui-tree [role="treeitem"] > [data-label]::after,
  .hui-tree__check::after {
    background: CanvasText;
    forced-color-adjust: none;
  }

  hui-tree [role="treeitem"][aria-selected="true"] > [data-label]::after {
    background: HighlightText;
  }

  .hui-tree__check {
    border-color: CanvasText;
  }

  hui-tree [role="treeitem"][data-cursor] > [data-label] {
    outline: 1px dashed CanvasText;
    outline-offset: -2px;
  }

  hui-tree:focus [role="treeitem"][data-cursor] > [data-label] {
    outline: 2px solid CanvasText;
    outline-offset: -2px;
  }

  hui-tree [role="treeitem"][aria-selected="true"][data-cursor] > [data-label] {
    outline-color: HighlightText;
  }
}
