/* arqu_ui — engine-owned layout + chrome styles.
 *
 * Hand-authored (UNLIKE styles.css, which is generated from the KTUI dist and
 * must not be edited). The admin/app layout chrome lives here so it survives a
 * styles.css regeneration: the vendored dist scopes the sidebar shell under a
 * non-semantic template class nothing in a real app carries — this file
 * re-authors that layout under the SEMANTIC .kt-admin-layout the shipped layouts
 * put on <body>, adds the fixed positioning the dist assumed but never declared,
 * wires the responsive off-canvas drawer, and defines the shared chrome regions
 * both engine layouts reference.
 *
 * Engine-owned responsiveness: the responsive drawer-enable (.kt-drawer-responsive)
 * and the drawer-toggle hide (.kt-drawer-toggle) are declared HERE with real
 * @media queries rather than relying on the dist's compiled Tailwind utilities
 * (lg:hidden, lg:[--kt-drawer-enable:false]) — those utilities are tree-shaken
 * from the generated dist and absent in a non-Tailwind host, so engine-critical
 * behavior must not depend on them.
 *
 * Single desktop breakpoint: 64rem. CSS @media cannot read a custom property in
 * its condition, so the literal is repeated across the queries in THIS file
 * only — change the breakpoint here.
 *
 * Cascade: loaded after styles.css, and the dist declares
 * `@layer theme, base, components, utilities`, so these @layer components rules
 * order after the dist's component rules (locked by a test asserting the dist
 * declares that layer). Light-only; theme tokens only.
 */

/* Accessibility token override — UNLAYERED on purpose.
 *
 * The generated styles.css sets the light-theme --primary to var(--color-blue-500)
 * (oklch 62.3% ≈ #2b7fff): white button text on it is 3.76:1, which FAILS WCAG 2.1
 * AA (needs 4.5:1 for normal text) — axe-core flags .kt-btn-primary as a serious
 * color-contrast violation across every consumer (#93). Darken --primary to
 * var(--color-blue-600) (oklch 54.6%), the value the dist already uses in its
 * other theme context, which clears AA with white text.
 *
 * This MUST be unlayered: styles.css declares --primary in an UNLAYERED :root
 * rule, and an unlayered declaration beats any @layer rule regardless of order —
 * so a layered override here (the rest of this file lives in @layer components)
 * would LOSE to the dist token. Being unlayered too, this override wins by source
 * order: _head loads styles.css then layout.css, so this later :root declaration
 * takes precedence. styles.css is generated/do-not-edit, so the fix lives here as
 * a token override — the README's sanctioned per-engine theming mechanism. */
:root {
  --primary: var(--color-blue-600);
}

/* Self-hosted Inter (#104) — UNLAYERED, like the --primary override above.
 *
 * Metronic's whole type scale is Inter. The engine used to load it from
 * fonts.googleapis.com via a _head <link> + two preconnects, but on a consumer's
 * auth/admin pages that leaks sign-in metadata off-origin and hard-depends on
 * Google for a security-sensitive surface (CodeRabbit, arqu-co/id#96). Post-#92
 * the engine owns the base + Inter, so we vendor the four static woff2 weights
 * Metronic uses (app/assets/fonts/arqu_ui/inter/, shipped by the gemspec's
 * app/**/* glob) and declare them here. Propshaft flattens the asset load path,
 * so a font at app/assets/fonts/arqu_ui/inter/Inter-Regular.woff2 is served at
 * arqu_ui/inter/Inter-Regular.woff2 — from this stylesheet (logical arqu_ui/)
 * the relative url("/assets/arqu_ui/inter/Inter-Regular-0c7c28ff.woff2") resolves there, mirroring exactly
 * how keenicons.css references its own vendored faces. font-display: swap paints
 * text immediately in the fallback then swaps to Inter once loaded. */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/arqu_ui/inter/Inter-Regular-0c7c28ff.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/arqu_ui/inter/Inter-Medium-061ba9f2.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/arqu_ui/inter/Inter-SemiBold-adf8f9dc.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/arqu_ui/inter/Inter-Bold-ebe00c8d.woff2") format("woff2");
}

/* Make Inter the first family of the resolved sans stack. The generated
 * styles.css resolves the body through --default-font-family: var(--font-sans),
 * but its --font-sans is the bare Tailwind system stack (no Inter) — so without
 * this the self-hosted face would never paint and the body would fall back to
 * the system UA face, not Inter. UNLAYERED so it beats the dist's unlayered
 * :root --font-sans by source order (styles.css then layout.css), the same
 * precedence mechanism the --primary override above relies on. The tail mirrors
 * the dist's stack so a host with Inter unavailable still degrades gracefully. */
:root, :host {
  --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji",
    "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}

/* Keenicon hide override — UNLAYERED on purpose.
 *
 * keenicons.css sets `display: inline-flex` on the icon base classes
 * (.ki-filled / .ki-duotone / .ki-outline / .ki-solid) with NO @layer, while the
 * generated styles.css emits Tailwind's `.hidden { display: none }` inside
 * `@layer utilities`. An UNLAYERED declaration beats ANY layered rule regardless
 * of specificity, so the icon's `display: inline-flex` wins over `.hidden` — a
 * keenicon tagged `hidden` still renders. That silently breaks every component
 * that toggles an icon's visibility with `.hidden`: CopyButtonComponent ships its
 * check icon as `ki-filled ki-check hidden` and swaps the two on copy, so without
 * this fix BOTH icons show at once and the checkmark leaks in the idle state.
 *
 * Restore the intent by re-asserting the hide UNLAYERED (so it is not outranked by
 * the unlayered icon rule) and at specificity 0,2,0 (`.ki-*.hidden` beats the
 * 0,1,0 `.ki-*` base), which wins on specificity among the two unlayered rules.
 * styles.css/keenicons.css are generated/do-not-edit, so the fix lives here. */
.ki-filled.hidden,
.ki-duotone.hidden,
.ki-outline.hidden,
.ki-solid.hidden {
  display: none;
}

@layer components {
  /* ---- admin sidebar-shell layout (semantic .kt-admin-layout scope) ------- */
  .kt-admin-layout {
    --sidebar-transition-duration: 0.3s;
    --sidebar-transition-timing: ease;
    --sidebar-width: 280px;
    --header-height: 70px;
  }
  @media (max-width: 64rem) {
    .kt-admin-layout {
      --header-height: 60px;
    }
  }
  .kt-admin-layout .kt-header {
    height: var(--header-height);
    transition: inset-inline-start var(--sidebar-transition-duration) var(--sidebar-transition-timing);
  }
  .kt-admin-layout .kt-sidebar {
    width: var(--sidebar-width);
  }
  .kt-admin-layout .kt-wrapper {
    transition: padding-inline-start var(--sidebar-transition-duration) var(--sidebar-transition-timing);
  }

  /* Desktop (>=64rem): pin the sidebar + header and offset the content. The dist
     shipped the offsets (padding/inset) but never the fixed positioning they
     assume, so the chrome never actually pinned. */
  @media (min-width: 64rem) {
    .kt-admin-layout.kt-sidebar-fixed .kt-sidebar {
      position: fixed;
      inset-block: 0;
      inset-inline-start: 0;
      z-index: 20;
      transition: width var(--sidebar-transition-duration) var(--sidebar-transition-timing);
    }
    .kt-admin-layout.kt-header-fixed .kt-header {
      position: fixed;
      inset-block-start: 0;
      inset-inline-end: 0;
      z-index: 10;
    }
    .kt-admin-layout .kt-sidebar .kt-sidebar-header {
      height: var(--header-height);
    }
    .kt-admin-layout.kt-sidebar-fixed .kt-wrapper {
      padding-inline-start: var(--sidebar-width);
    }
    .kt-admin-layout.kt-sidebar-fixed.kt-header-fixed .kt-header {
      inset-inline-start: var(--sidebar-width);
    }
    /* Reserve the fixed header's height. Scoped to the desktop breakpoint where
       the header is actually position: fixed — below it the header is in normal
       flow, so a base-scope padding-top is a dead gap above the header on mobile
       (#101). Keyed on .kt-header-fixed (not sidebar-fixed): the offset pairs
       with the fixed header regardless of the sidebar's fixed state. */
    .kt-admin-layout.kt-header-fixed .kt-wrapper {
      padding-top: var(--header-height);
    }
    /* Fill the viewport below the fixed header so the footer pins to the bottom
       of the page on short content. The header is fixed here (out of flow) and
       the wrapper reserves its height via padding-top, so the content column
       fills exactly the remaining viewport: 100dvh - header. */
    .kt-admin-layout.kt-header-fixed .kt-sidebar-wrapper {
      min-block-size: calc(100dvh - var(--header-height));
    }
  }

  /* Sidebar as a flex column with a scrollable content region; this is also the
     off-canvas drawer surface below the breakpoint (KTDrawer adds
     .kt-drawer/.kt-drawer-start, which styles.css positions + slides). */
  .kt-admin-layout .kt-sidebar {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    background-color: var(--background);
    border-inline-end: 1px solid var(--border);
  }
  /* Brand header: center the brand mark with the page padding, like Metronic's
     kt-sidebar-header (flex items-center px-6). Without this the brand sits at
     the sidebar's top-left and clips. shrink-0 keeps it from collapsing. */
  .kt-admin-layout .kt-sidebar-header {
    display: flex;
    align-items: center;
    flex-shrink: 0;
    padding-inline: 1.5rem;
  }
  .kt-admin-layout .kt-sidebar-content {
    flex: 1 1 auto;
    overflow-y: auto;
  }
  /* The content column: the sidebar's sibling that stacks the header + main.
     Engine-owned here (the dist only defined a same-named class as a .kt-sidebar
     descendant — a different DOM position — and that inert rule was curated out
     of styles.css). min-width:0 lets long content shrink instead of overflowing. */
  .kt-admin-layout .kt-sidebar-wrapper {
    display: flex;
    flex-direction: column;
    min-width: 0;
  }
  /* The main region grows to fill the content column, so a short page still
     pushes the footer to the bottom instead of letting it float up under the
     content (Metronic's kt-wrapper is `flex grow flex-col`; the engine content
     column mirrors that growth here). */
  .kt-admin-layout .kt-admin-main {
    flex: 1 0 auto;
    padding: calc(var(--spacing) * 5);
  }

  /* ---- engine-owned responsive drawer primitives (no dist Tailwind) -------- */
  /* KTDrawer reads --kt-drawer-enable off the element: enabled (live off-canvas
     drawer) below the breakpoint, disabled (static chrome) at/above it. Replaces
     the dist's lg:[--kt-drawer-enable:false] utility so it survives regen and
     works without Tailwind in the host. */
  .kt-drawer-responsive {
    --kt-drawer-enable: true;
  }
  @media (min-width: 64rem) {
    .kt-drawer-responsive {
      --kt-drawer-enable: false;
    }
  }
  /* The drawer toggle (hamburger) is only needed while the drawer is off-canvas;
     hide it once the sidebar is the static fixed chrome. Replaces lg:hidden. */
  @media (min-width: 64rem) {
    .kt-drawer-toggle {
      display: none;
    }
  }

  /* ---- shared chrome: referenced by BOTH the admin and app layouts --------- */
  .kt-admin-layout .kt-header,
  .kt-app-layout .kt-header {
    display: flex;
    align-items: center;
    background-color: var(--background);
    border-block-end: 1px solid var(--border);
  }
  .kt-admin-layout .kt-header-container,
  .kt-app-layout .kt-header-container {
    display: flex;
    align-items: center;
    gap: calc(var(--spacing) * 2);
    width: 100%;
  }
  /* The header's primary nav region (the optional :header_nav slot) sits in the
     header flex row, left of the auto-pushed user menu. */
  .kt-admin-layout .kt-header-nav,
  .kt-app-layout .kt-header-nav {
    display: flex;
    align-items: center;
  }
  .kt-admin-layout .kt-header-user-menu,
  .kt-app-layout .kt-header-user-menu {
    margin-inline-start: auto;
  }
  .kt-brand {
    display: inline-flex;
    align-items: center;
    gap: calc(var(--spacing) * 2);
    font-weight: 600;
    color: var(--foreground);
  }
  .kt-brand-name {
    font-weight: 600;
  }
  .kt-brand-logo {
    height: calc(var(--spacing) * 8);
    width: auto;
  }

  /* ---- app-layout-only chrome: the top-nav brand + horizontal primary nav ----
     The app (top-nav) layout references kt-header-brand, kt-header-nav and
     kt-app-main; the shared chrome above styles the header shell, but these
     app-only regions need their own structural rules or they render unstyled. */
  .kt-app-layout {
    --header-height: 70px;
  }
  @media (max-width: 64rem) {
    .kt-app-layout {
      --header-height: 60px;
    }
  }
  .kt-app-layout .kt-header {
    height: var(--header-height);
  }
  /* The app layout is a fixed top-nav surface (no sidebar/drawer), so — unlike
     the admin header, which only pins at the desktop breakpoint — the app header
     pins at every width when kt-header-fixed is set, and the content region
     offsets by the header height so nothing hides behind it. */
  .kt-app-layout.kt-header-fixed .kt-header {
    position: fixed;
    inset-block-start: 0;
    inset-inline: 0;
    z-index: 10;
  }
  .kt-app-layout .kt-header-brand {
    display: flex;
    align-items: center;
    flex-shrink: 0;
    margin-inline-end: calc(var(--spacing) * 4);
  }
  /* The horizontal primary nav: space the top-level items so host-supplied
     links (or a horizontal MenuComponent) read as a nav row, not a run-on. */
  .kt-app-layout .kt-header-nav {
    gap: calc(var(--spacing) * 5);
  }
  /* The content region below the top bar: vertical breathing room to match the
     admin main (kt-container-fixed already supplies the centered max-width +
     horizontal padding). */
  .kt-app-layout .kt-app-main {
    padding-block: calc(var(--spacing) * 5);
  }
  /* When the header is fixed it leaves the flow, so the content must clear it:
     offset the top by the header height plus the same breathing room. */
  .kt-app-layout.kt-header-fixed .kt-app-main {
    padding-top: calc(var(--header-height) + var(--spacing) * 5);
  }

  /* ---- app top-nav responsive drawer -------------------------------------
     The app shell has no sidebar, so below the desktop breakpoint the horizontal
     primary nav has nowhere to go and its trailing items clip off-screen. Mirror
     the admin shell's proven off-canvas story: the SAME engine-owned
     .kt-drawer-responsive enable + .kt-drawer-toggle hide primitives above drive
     KTDrawer, and these rules give the collapsed .kt-header-nav its panel shape.
     KTDrawer's .kt-drawer (from the dist) supplies the fixed position, --popover
     background, and slide transition but sizes only to content; scope the panel
     shape to .kt-header-nav.kt-drawer so it applies ONLY when KTDrawer has added
     the off-canvas class (below 64rem) — at/above the breakpoint the drawer is
     disabled and the nav renders inline in the header exactly as before. */
  .kt-app-layout {
    --app-drawer-width: 280px;
  }
  /* The collapsed nav as an off-canvas panel: a definite width and a vertical
     stack with breathing room, overriding the inline header-row rules
     (align-items: center, the wide row gap) that only make sense horizontally. */
  .kt-app-layout .kt-header-nav.kt-drawer {
    width: var(--app-drawer-width);
    max-width: 90%;
    align-items: stretch;
    gap: calc(var(--spacing) * 1);
    padding: calc(var(--spacing) * 4);
  }
  /* A host's horizontal MenuComponent (.kt-menu, a flex row) reflows to a
     vertical list inside the narrow drawer so every top-level item stacks and
     stays reachable — the whole point of the collapse. */
  .kt-app-layout .kt-header-nav.kt-drawer .kt-menu {
    flex-direction: column;
    align-items: stretch;
    width: 100%;
  }

  /* ---- auth-layout chrome: the centered sign-in card surface --------------
     The auth layout is a chrome-free, single-card surface — no header, sidebar,
     nav or user-menu (an unauthenticated visitor has none). Its four regions
     (kt-auth-layout on <body>, kt-auth-wrapper, kt-auth-brand, kt-auth-card)
     carried NO styling and rendered the sign-in card full-bleed at the top-left
     of the viewport; this section is that missing chrome. The kt-card
     already ships a border + subtle shadow, so painting the page in the semantic
     var(--background) is enough to read the card as a distinct surface —
     separation comes from the card's own edge, not a heavy backdrop. */
  .kt-auth-layout {
    display: grid;
    place-items: center;
    min-block-size: 100dvh;
    background-color: var(--background);
  }
  /* The centered column stacks the brand above the card with vertical rhythm,
     capped at a comfortable sign-in width so the card never stretches full-bleed.
     Inline padding keeps the card off the edges on a narrow viewport; block
     padding gives breathing room when a tall form makes the page scroll. */
  .kt-auth-wrapper {
    display: flex;
    flex-direction: column;
    gap: calc(var(--spacing) * 6);
    inline-size: 100%;
    max-inline-size: 25rem;
    padding-block: calc(var(--spacing) * 10);
    padding-inline: calc(var(--spacing) * 5);
  }
  /* Center the brand mark above the card. */
  .kt-auth-brand {
    display: flex;
    justify-content: center;
  }
  /* The card fills the column's constrained width. */
  .kt-auth-card {
    inline-size: 100%;
  }
}

/* Visually-hidden utility — re-authored here because the generated styles.css is
 * Tailwind-tree-shaken from the design-system dist, whose curated templates never
 * use `.sr-only`, so the utility is absent from the shipped stylesheet. But engine
 * components DO depend on it: CopyButtonComponent hides its live-region status text
 * (and an icon-only button's label) with `.sr-only`, and PaginationComponent labels
 * its prev/next controls the same way. Without this rule those nodes render visibly
 * (e.g. a duplicated "Copied" next to the button, a leaked "Previous"/"Next"),
 * breaking both fidelity and the accessible-name contract. Same precedent as the
 * tree-shaken responsive utilities re-authored above: the engine owns what the dist
 * dropped. This is the standard accessible-hiding rule (Tailwind's `.sr-only`);
 * placed in @layer utilities so it composes with the dist's utility layer. */
@layer utilities {
  .sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border-width: 0;
  }
}

/* Popover token utilities — engine-owned because the dist tree-shook them.
 *
 * styles.css is a compiled Tailwind v4 artifact: a `bg-X` / `text-X` utility
 * survives into the shipped stylesheet only if something INSIDE arqu-ui used it,
 * so Tailwind could keep it. Nothing here uses `bg-popover`, so `.bg-popover` and
 * `.text-popover-foreground` were tree-shaken out of the dist entirely — and every
 * consumer that composes a popover with `bg-popover` (loading styles.css as a plain
 * design layer, the README-documented path) rendered it TRANSPARENT (#194).
 *
 * Re-author the pair here, pointing at the theme-scoped RUNTIME vars exactly the way
 * the dist's own `.bg-muted { background-color: var(--muted) }` does — not the
 * `--color-*` alias — so light and dark are covered for free (the runtime var flips
 * per theme context: white in light, zinc-950 in dark). Same precedent as the
 * tree-shaken responsive/.sr-only utilities above: the engine owns what the dist
 * dropped. @layer utilities so they compose with the dist's utility layer (order
 * after component rules, overridable by a caller's more-specific utility). */
@layer utilities {
  .bg-popover {
    background-color: var(--popover);
  }
  .text-popover-foreground {
    color: var(--popover-foreground);
  }
}

/* Popover theme-alias parity — UNLAYERED :root, like the --font-sans/--primary
 * overrides above. styles.css exports the --color-popover theme alias but NOT its
 * --color-popover-foreground companion (muted/primary ship both). Register the
 * missing half so the exported popover token pair reaches parity (#194). */
:root {
  --color-popover-foreground: var(--popover-foreground);
}

/* Read-only input styling — engine-owned because the dist ships none.
 *
 * The generated styles.css styles `.kt-input:disabled` (muted: opacity 60% +
 * cursor not-allowed) but declares NO read-only rule, so a readonly field —
 * which FormFieldComponent(readonly: true) emits, and which stays focusable and
 * form-submitting unlike a disabled one — would render identical to an editable
 * input, giving no signal it can't be typed into. The DS field is the opinionated
 * default, so it owns that affordance: mirror the dist's own non-editable
 * treatment (opacity + not-allowed) for read-only inputs.
 *
 * Target the `[readonly]` ATTRIBUTE, not the `:read-only` pseudo-class: the
 * pseudo-class also matches inputs that are inherently non-editable regardless of
 * the attribute — notably `type="file"`, which the dist explicitly styles as
 * `.kt-input[type=file]` — so `.kt-input:read-only` would wrongly mute a normal
 * file input. `[readonly]` matches only a control the component actually marked
 * readonly. Same precedent as the rules above: the engine owns what the dist
 * dropped. @layer components so it orders after (and matches the specificity
 * origin of) the dist's `.kt-input:disabled` rule in that same layer.
 *
 * The textarea shares this: the dist styles `.kt-textarea:disabled` but no
 * read-only rule, so a read-only `<textarea>` (which TextareaComponent forwards
 * via a `readonly` attribute, and FormFieldComponent(multiline: true, readonly:
 * true) emits) would render identical to an editable one. Cover it with the same
 * selector list so both non-editable controls carry the one muted, not-allowed
 * treatment (#168). */
@layer components {
  .kt-input[readonly],
  .kt-textarea[readonly] {
    cursor: not-allowed;
    opacity: 60%;
  }
}

/* InputGroup state bridge — engine-owned because the dist ships kt-input-group as
 * six layout/radius seam rules only (styles.css:9236): no :focus-within, no
 * :has([aria-invalid]), no :has(:disabled). The group seams an addon and a control
 * into ONE bordered field (the addon's inner border is removed at the seam), so a
 * state painted on the inner control alone reads wrong: its focus ring and
 * destructive border stop at the seam, cutting a line through the middle of the
 * visual field, and a disabled control leaves the addon fully lit. Lift each state
 * to the whole group. The dist's OWN precedent for ringing a multi-element field is
 * `.kt-select-display[data-multiple]:focus-within` (styles.css:9937) — mirror that
 * idiom here. @layer components so these order after the dist's kt-input-group
 * rules in that same layer. Additive; every selector is a new state the dist never
 * styled, so no existing group changes (#168). */
@layer components {
  /* Focus — the group owns the ring so it hugs the whole field's rounded outer
     shape instead of the inner control's seam edge. Match .kt-input:focus-visible
     (styles.css:8922): the --ring border plus a 3px ring in --ring at 30%. The
     radius shapes the box-shadow to the field (the addon/control corners round to
     the same calc(var(--radius) - 2px)). */
  .kt-input-group:focus-within {
    border-radius: calc(var(--radius) - 2px);
    box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring) 30%, transparent);
  }

  /* The seamed field's border turns --ring across BOTH parts, so the ring color
     runs unbroken through the addon seam. */
  .kt-input-group:focus-within .kt-input,
  .kt-input-group:focus-within .kt-input-addon {
    border-color: var(--ring);
  }

  /* The group now owns the ring, so clear the inner control's own focus ring — it
     would otherwise double-paint and reintroduce the seam-cutting line. */
  .kt-input-group:focus-within .kt-input:focus-visible {
    box-shadow: none;
  }

  /* Invalid — reachable now that #147 makes the inner control emit
     aria-invalid="true". Paint the destructive border across the whole seamed
     field (addon + control), mirroring .kt-input[aria-invalid] (styles.css:8984)
     rather than leaving the destructive edge stopping at the inner control's seam. */
  .kt-input-group:has([aria-invalid="true"]) .kt-input,
  .kt-input-group:has([aria-invalid="true"]) .kt-input-addon {
    border-color: var(--destructive);
  }

  /* Disabled — a group holding a disabled control mutes as one field (the addon is
     part of the same disabled affordance), mirroring the dist .kt-input:disabled
     treatment (opacity 60% + not-allowed, styles.css:8941) across the whole group. */
  .kt-input-group:has(:disabled) {
    cursor: not-allowed;
    opacity: 60%;
  }

  /* The group now owns the single mute. Reset any inner disabled control's OWN
     opacity (the dist mutes .kt-input:disabled / .kt-btn:disabled to 60%/50%) back
     to 1: opacity multiplies through the group's stacking context, so a stacked
     0.6 × 0.6 would render the control at 36% while the addon sits at 60% — reading
     as two mismatched mutes, not one field. With the control at full opacity inside
     the buffer, the whole group composites uniformly at the group's 60%. */
  .kt-input-group:has(:disabled) :disabled {
    opacity: 1;
  }
}

/* Progress indeterminate — engine-owned because the dist ships kt-progress as a
 * DETERMINATE bar only (styles.css:10716): the indicator is positioned from
 * --progress-value, with no busy/unknown mode. ProgressComponent's indeterminate:
 * variant needs a "working, progress unknown" affordance — a partial bar that
 * sweeps the track under aria-busy. Author the sweep here so a styles.css
 * regeneration can never drop it. The indicator carries no inline width in this
 * mode (the component omits it), so the rule sizes it and the keyframes move it.
 * Semantic tokens only (the indicator's --primary/variant fill is inherited from
 * the dist). @layer components so it orders after the dist kt-progress rules in
 * that same layer (#170). */
@layer components {
  .kt-progress-indeterminate .kt-progress-indicator {
    width: 40%;
    animation: kt-progress-indeterminate 1.4s ease-in-out infinite;
  }

  /* Sweep the partial bar from just off the leading edge to just off the trailing
     edge (translateX is relative to the indicator's own 40% width: -100% hides it
     to the left, 250% clears the right edge of the 100% track). */
  @keyframes kt-progress-indeterminate {
    from {
      transform: translateX(-100%);
    }
    to {
      transform: translateX(250%);
    }
  }

  /* Honor a reduced-motion preference: hold a static full-width busy bar rather
     than sweeping it, so the aria-busy state still reads without motion. */
  @media (prefers-reduced-motion: reduce) {
    .kt-progress-indeterminate .kt-progress-indicator {
      width: 100%;
      animation: none;
    }
  }
}

/* FormFieldComponent presentation + layout intents — engine-owned because the
 * dist ships no modifier for any of them (#126). Same precedent as the read-only
 * rule above: the opinionated DS field owns the treatments its callers need, as
 * first-class named intents rather than a per-app `**attributes`/utility splat.
 * @layer components so they order after the dist's `.kt-input`/`.kt-textarea`
 * component rules in that same layer and can override the base font/alignment.
 * The selectors are class-only (not element-qualified) so a monospace multiline
 * field — `kt-textarea kt-input-code` — is covered by the same rule. */
@layer components {
  /* variant: :code — the monospace treatment for identifier-ish values (URIs,
     scopes, keys). Monospace disambiguates l/1/O/0 and keeps structure legible. */
  .kt-input-code {
    font-family: var(--font-mono);
  }

  /* variant: :otp — the one-time-code treatment: centered, wide-tracking,
     larger monospace digits for a short numeric code. */
  .kt-input-otp {
    text-align: center;
    letter-spacing: 0.5em;
    font-family: var(--font-mono);
    font-size: var(--text-lg);
    font-weight: var(--font-weight-medium);
  }

  /* layout: :inline — let the field grow inside a `flex flex-wrap` toolbar row
     (its own basis, sharing/filling leftover space) instead of the default
     full-width block a stacked kt-form-item renders as. min-width:0 lets the
     control shrink so a long placeholder can't overflow the toolbar. */
  .kt-form-item-inline {
    flex: 1 1 auto;
    min-width: 0;
  }

  /* CodeInputComponent — segmented one-character-per-box code entry. The boxes
     are real .kt-input controls, so the dist's border/:focus-visible ring and
     [aria-invalid] destructive styling apply unchanged; these rules only add
     the box geometry (fixed square-ish width overriding kt-input's full-width
     default, centered oversized monospace glyph — the same type treatment as
     .kt-input-otp) and the group chrome. No transitions are declared, so the
     control is reduced-motion-safe by construction. */
  .kt-code-input-segments {
    display: flex;
    align-items: center;
    gap: 0.5rem;
  }

  /* Boxes cap at a 2.5rem square-ish geometry but may shrink evenly (flex-basis
     0 + min-width 0) so the longest allowed code still fits a narrow mobile
     viewport instead of overflowing the group. */
  .kt-code-input-segment {
    flex: 1 1 0;
    min-width: 0;
    max-width: 2.5rem;
    /* 2.75rem (44px) over kt-input's default: the minimum touch-target height,
       and the taller-than-wide proportion the segmented pattern reads as. */
    height: 2.75rem;
    padding-inline: 0;
    text-align: center;
    font-family: var(--font-mono);
    font-size: var(--text-lg);
    font-weight: var(--font-weight-medium);
  }

  /* format: :alphanumeric_upcase — preview a lowercase keystroke in canonical
     uppercase even before (or without) the controller's JS upcasing. */
  .kt-code-input-upcase {
    text-transform: uppercase;
  }

  /* Visual group separator (aria-hidden; the accessible value is the composed
     field). Muted so the boxes stay the dominant reading rhythm. */
  .kt-code-input-separator {
    flex: 0 0 auto;
    color: var(--muted-foreground);
    user-select: none;
  }
}

/* Engine-owned :focus-visible ring (#138 — WCAG 2.4.7).
 *
 * The generated styles.css defines :focus-visible ONLY for the form primitives
 * and kt-btn/kt-link, so every navigation surface (menu links, accordion-menu
 * links + toggles, tab toggles, pagination, breadcrumb, dropdown links), the
 * overlay close controls (the modal/drawer × — the focus trap's initial focus
 * target), the image-input controls and the interactive rating paint NO
 * keyboard-focus indicator — a keyboard user cannot see where they are.
 * (The datepicker is NOT here: the dist already rings its .kt-input wrapper via
 * .kt-input:not(input):has(input:focus-visible) at styles.css:9047, higher
 * specificity than a bare .kt-input:has(...), so it keeps the dist's input-style
 * focus ring — consistent with every other form field — and needs no rule here.)
 * This pass restores one consistent ring across them, matching the dist
 * .kt-btn:focus-visible treatment (styles.css:7402): a 2px ring in the --ring
 * token over a 2px --background offset, cleared outline, plus a forced-colors
 * outline fallback. It is additive — the dist primitives already ringed are
 * untouched — and drives the ring off the semantic --ring token so it stays
 * visible in both themes. box-shadow follows each element's own border-radius,
 * so no per-surface radius is set here.
 *
 * @layer components, loaded after styles.css in that same layer. .kt-link is
 * folded into the group so its ring is re-pointed from the dist's near-invisible
 * --color-neutral-200 (styles.css:7301) to --ring: because this rule sets the
 * `box-shadow` PROPERTY to a literal (not via --tw-ring-color), it overrides the
 * dist `box-shadow` at equal specificity by source order AND is immune to the
 * dist's higher-specificity dark override `.kt-link:is(.dark *):focus-visible`
 * (styles.css:7313), which only re-sets the --tw-ring-color variable this rule
 * never reads — so the ring paints correctly in both themes.
 *
 * Wrapper/:has() selectors: the image input's focusable is a visually-collapsed
 * `<input type=file>` inside `.kt-image-input`, and the interactive rating's
 * radios are parked off-screen (styles.css:14887). In each the ring must land on
 * the container, not the invisible focusable, so the container matches
 * `:has(:focus-visible)`. The dist rings no `.kt-image-input`/`.kt-rating` focus
 * state, so these are uncontested. */
@layer components {
  .kt-menu-link:focus-visible,
  .kt-accordion-menu-link:focus-visible,
  .kt-accordion-menu-toggle:focus-visible,
  .kt-accordion-toggle:focus-visible,
  .kt-tab-toggle:focus-visible,
  a.kt-pagination-item:focus-visible,
  .kt-breadcrumb-link:focus-visible,
  .kt-dropdown-menu-link:focus-visible,
  .kt-modal-close:focus-visible,
  .kt-drawer-close:focus-visible,
  .kt-link:focus-visible,
  .kt-image-input-remove:focus-visible,
  .kt-image-input:has(input[type="file"]:focus-visible),
  .kt-rating:has(input:focus-visible) {
    outline: none;
    box-shadow:
      0 0 0 2px var(--background),
      0 0 0 4px var(--ring);
  }

  /* Forced-colors (Windows High Contrast) strips box-shadow, so the ring above
     vanishes there. Back it with a transparent outline the OS repaints in a
     system color — the same fallback the dist gives .kt-btn (styles.css:7406). */
  @media (forced-colors: active) {
    .kt-menu-link:focus-visible,
    .kt-accordion-menu-link:focus-visible,
    .kt-accordion-menu-toggle:focus-visible,
    .kt-accordion-toggle:focus-visible,
    .kt-tab-toggle:focus-visible,
    a.kt-pagination-item:focus-visible,
    .kt-breadcrumb-link:focus-visible,
    .kt-dropdown-menu-link:focus-visible,
    .kt-modal-close:focus-visible,
    .kt-drawer-close:focus-visible,
    .kt-link:focus-visible,
    .kt-image-input-remove:focus-visible,
    .kt-image-input:has(input[type="file"]:focus-visible),
    .kt-rating:has(input:focus-visible) {
      outline: 2px solid transparent;
      outline-offset: 2px;
    }
  }
}

/* Disabled tab — engine-owned because the dist styles
 * `.kt-tabs.kt-tabs-line .kt-tab-toggle` (styles.css:10207) with NO :disabled
 * treatment. #148 added TabsComponent::Tab(disabled:), which emits the native
 * `disabled` attribute — that blocks focus/activation (KTTabs never switches to
 * it), but the toggle still paints the full --secondary-foreground text, so a
 * disabled tab reads identical to an enabled one. Mirror the dist .kt-btn:disabled
 * muting (opacity 50%, styles.css:7412) so the unavailable tab reads as such. The
 * bare class carries no opacity in the dist, so this is additive and uncontested.
 * @layer components so it orders after the dist tab rules in that same layer
 * (#170). */
@layer components {
  .kt-tab-toggle:disabled {
    cursor: not-allowed;
    opacity: 50%;
  }
}

/* Engine-owned badge contrast overrides (#139 — WCAG 1.4.3, ≥4.5:1 normal text).
 *
 * The generated styles.css ships Metronic's semantic-badge fills with white text
 * (styles.css ~6888): white on --color-green-500 is 2.22:1, on --color-yellow-400
 * 1.57:1, and on --color-violet-500 4.40:1 — all below the 4.5:1 AA floor, so a
 * consumer's axe-core system test flags .kt-badge-{success,warning,info} as a
 * serious color-contrast violation across every app. Correct each pair with the
 * SMALLEST on-palette change that clears AA while keeping the Metronic look:
 *   - success: darken the fill one step (green-500 -> green-700), keep white text
 *     -> 4.94:1. Preserves the white-on-green convention shared with the primary,
 *     destructive and mono badges.
 *   - warning: yellow is intrinsically too light for white text at AA, so keep
 *     the Metronic yellow-400 fill and switch the label to the dark --foreground
 *     token -> 12.68:1 (the road-sign dark-on-amber warning convention).
 *   - info: darken the fill one step (violet-500 -> violet-600), keep white text
 *     -> 5.88:1.
 * Ratios verified against axe-core 4.12.1; see test/layouts/arqu_ui/
 * badge_contrast_css_test.rb, which recomputes them from the resolved tokens.
 *
 * @layer components, loaded after styles.css in that same layer, so each rule
 * wins over the dist's solid .kt-badge-* rule by source order at equal
 * specificity — WITHOUT disturbing the higher-specificity
 * .kt-badge-outline.kt-badge-* variants, which keep their own transparent-fill
 * treatment. (Unlike the --primary token override above, which had to be
 * UNLAYERED to beat the dist's unlayered :root token, these class rules are
 * layered because the dist's badge rules are layered too.) Each rule changes
 * only the one property that fails; the untouched property inherits from the
 * dist. Light-only, semantic tokens only — a contrast nudge, not a restyle. */
@layer components {
  .kt-badge-success {
    background-color: var(--color-green-700);
  }

  .kt-badge-warning {
    color: var(--foreground);
  }

  .kt-badge-info {
    background-color: var(--color-violet-600);
  }
}

/* Engine-owned current-page highlights for the sidebar and pagination.
 *
 * Sidebar: the dist styles the current item ONLY as
 * `[data-kt-accordion-initialized] .kt-accordion-menu-link.selected`
 * (styles.css:6336) — keyed on `.selected` and gated on a data-attribute KTUI JS
 * injects at runtime. The arqu_ui components are light-only, so that highlight
 * never paints server-side. Restate the same treatment (background var(--accent))
 * WITHOUT the JS-init precondition, so the current item (which the component marks
 * with `.selected`) highlights whether or not a host inits the accordion. The
 * dist's higher-specificity JS-gated rule uses the identical color, so when both
 * apply there is no visible conflict.
 *
 * Pagination: the dist ships `.kt-pagination-item` as a bare flex box with no
 * size, hover, current-page, or disabled treatment (styles.css:10760), so the
 * current page is indistinguishable from its neighbours and an aria-disabled arrow
 * looks identical to an enabled one. Port the dist's OWN pagination look — the
 * `.kt-datatable-pagination-button` treatment (styles.css:8238) — so the two
 * pagination surfaces read the same: a square tap target that fills with --accent
 * on the current page and on hover, dark --foreground on the interactive arrows,
 * and a muted, dimmed, non-interactive arrow at the bounds. Additive over the
 * dist's flex base; semantic tokens only; a fix, not a restyle. */
@layer components {
  .kt-accordion-menu-link.selected {
    background-color: var(--accent);
  }

  .kt-pagination-item {
    height: calc(var(--spacing) * 7);
    min-width: calc(var(--spacing) * 7);
    justify-content: center;
    border-radius: calc(var(--radius) - 2px);
    padding-inline: calc(var(--spacing) * 0.5);
    color: var(--muted-foreground);
  }

  /* The directional arrows are interactive chrome, so they carry the darker
     --foreground (matching .kt-datatable-pagination-prev/next, styles.css:8254);
     the numbers stay --muted-foreground. Only the enabled anchors carry rel — the
     disabled arrows render as rel-less spans — so this never lifts a disabled arrow. */
  a.kt-pagination-item[rel="prev"],
  a.kt-pagination-item[rel="next"] {
    color: var(--foreground);
  }

  .kt-pagination-item.active,
  a.kt-pagination-item:hover {
    background-color: var(--accent);
    color: var(--accent-foreground);
  }

  /* A bound arrow: muted (not the enabled --foreground) and dimmed, so it can
     never be mistaken for an enabled control. */
  .kt-pagination-item[aria-disabled="true"] {
    cursor: default;
    pointer-events: none;
    color: var(--muted-foreground);
    opacity: 50%;
  }
}

/* Stepper — the kt-stepper family shipped with NO CSS: it lives in
 * neither the generated styles.css nor any reference/metronic page, so
 * StepperComponent rendered as a bare vertical stack of unstyled divs — no
 * circles, no connector line, no flex row. Author the whole family here (the
 * engine-owned layout layer) so a styles.css regeneration can never drop it,
 * exactly like the pagination/sidebar current-page highlights above. A
 * horizontal step nav: numbered circles joined by a connector line, carrying the
 * active / completed / pending state treatments the component + KTStepper seed.
 * Semantic tokens only; additive over the component's emitted classes; @layer
 * components so it orders after the dist at its own specificity. */
@layer components {
  .kt-stepper-nav {
    display: flex;
    align-items: center;
    margin: 0;
    padding: 0;
    list-style: none;
  }

  .kt-stepper-item {
    display: flex;
    align-items: center;
    gap: calc(var(--spacing) * 2);
    min-width: 0;
  }

  /* Every step past the first is prefixed by a growing connector line, so the
     circles read as one joined sequence rather than a loose row. The line stays
     the muted --border until its step is reached; the reached-connector rule
     below fills it with --primary. */
  .kt-stepper-item:not(:first-child) {
    flex: 1;
  }

  .kt-stepper-item:not(:first-child)::before {
    content: "";
    flex: 1;
    min-width: calc(var(--spacing) * 6);
    height: 2px;
    border-radius: 9999px;
    background-color: var(--border);
  }

  /* The connector leading INTO a reached step (active = the current step, or
     completed = a passed one) fills with --primary, so the traversed path reads
     as done at a glance. */
  .kt-stepper-item.active::before,
  .kt-stepper-item.completed::before {
    background-color: var(--primary);
  }

  /* The numbered circle. Pending (the base) is a muted outline on the surface
     fill with a legible muted numeral; position:relative anchors the completed
     checkmark. */
  .kt-stepper-number {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: calc(var(--spacing) * 8);
    height: calc(var(--spacing) * 8);
    border: 1px solid var(--border);
    border-radius: 9999px;
    background-color: var(--background);
    color: var(--muted-foreground);
    font-size: 0.8125rem;
    font-weight: 500;
    line-height: 1;
  }

  .kt-stepper-label {
    color: var(--secondary-foreground);
    font-size: 0.875rem;
    font-weight: 500;
    white-space: nowrap;
  }

  /* Active — the current step: a filled --primary circle with a soft primary
     ring halo so it reads as "you are here", decisively distinct from a plain
     completed circle. */
  .kt-stepper-item.active .kt-stepper-number {
    border-color: var(--primary);
    background-color: var(--primary);
    color: var(--primary-foreground);
    box-shadow: 0 0 0 4px color-mix(in oklch, var(--primary) 20%, transparent);
  }

  .kt-stepper-item.active .kt-stepper-label {
    color: var(--foreground);
  }

  /* Completed — a filled --primary circle showing a checkmark in place of the
     numeral. The numeral stays in the DOM (transparent, not removed) so assistive
     tech still reads the step position. */
  .kt-stepper-item.completed .kt-stepper-number {
    border-color: var(--primary);
    background-color: var(--primary);
    color: transparent;
  }

  .kt-stepper-item.completed .kt-stepper-number::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 0.375rem;
    height: 0.625rem;
    border: solid var(--primary-foreground);
    border-width: 0 2px 2px 0;
    transform: translate(-50%, -60%) rotate(45deg);
  }

  .kt-stepper-item.completed .kt-stepper-label {
    color: var(--foreground);
  }

  /* Invalid — the step's panel has validation errors (aria-invalid). Paint the
     circle and label with --destructive so the error is visible, never
     colour-on-colour. An invalid step must read as consistently destructive
     across every element, so this also resets the active-step's primary halo
     (box-shadow) — otherwise a step that is BOTH current and invalid would show
     a destructive circle wearing a primary ring. The invalid rules order after
     the active/completed rules above, so at equal specificity they win. */
  .kt-stepper-item[aria-invalid="true"] .kt-stepper-number {
    border-color: var(--destructive);
    background-color: var(--destructive);
    color: var(--destructive-foreground);
    box-shadow: none;
  }

  /* A step that is BOTH current and invalid keeps an elevation halo (it is still
     "you are here"), but tinted --destructive to match its circle rather than
     the primary ring. Higher specificity than the plain active + plain invalid
     rules, so it wins for the combination. */
  .kt-stepper-item.active[aria-invalid="true"] .kt-stepper-number {
    box-shadow: 0 0 0 4px color-mix(in oklch, var(--destructive) 20%, transparent);
  }

  .kt-stepper-item[aria-invalid="true"] .kt-stepper-label {
    color: var(--destructive);
  }

  /* The connector leading INTO an invalid step is --destructive too, so a
     traversed (primary) line never runs into a destructive circle. Ordered after
     the reached-connector rule above, so at equal specificity it wins for an
     active/completed invalid step; for a pending invalid step it overrides the
     muted --border base. */
  .kt-stepper-item[aria-invalid="true"]::before {
    background-color: var(--destructive);
  }

  /* Disabled — a step that cannot be reached yet (aria-disabled). Dim it and
     drop pointer interaction, mirroring the pagination disabled-arrow treatment. */
  .kt-stepper-item[aria-disabled="true"] {
    opacity: 50%;
    pointer-events: none;
  }

  /* The back/next controls sit in a trailing-aligned row below the panels,
     matching the dist wizard footer. */
  .kt-stepper-actions {
    display: flex;
    justify-content: flex-end;
    gap: calc(var(--spacing) * 2);
    margin-top: calc(var(--spacing) * 5);
  }
}

/* Dropdown non-interactive rows — engine-owned because the dist ships none.
 *
 * DropdownComponent composes a real account menu: a non-interactive HEADER
 * (avatar + email) and a NOTE row (a "read-only"/"no URL" hint) alongside the
 * dist's `.kt-dropdown-menu-link`. The generated styles.css has a class only for
 * the LINK; a real Metronic account header is hand-authored per-page with raw
 * spacing utilities (px-2.5/py-1.5) that the tree-shaken dist does not ship — so a
 * component that leaned on them would render unpadded in a consumer. Give the two
 * rows their own semantic classes here, padded to match the dist link
 * (`padding-inline: 2.5 * --spacing`, `padding-block: 2 * --spacing`) so the header,
 * links, and note align — depending only on tokens the dist always defines, never
 * on an incidentally-retained utility. @layer components so they order after the
 * dist's `.kt-dropdown-menu-*` rules in that same layer. */
@layer components {
  .kt-dropdown-menu-header {
    display: flex;
    align-items: center;
    gap: calc(var(--spacing) * 2.5);
    padding-inline: calc(var(--spacing) * 2.5);
    padding-block: calc(var(--spacing) * 2);
  }
  .kt-dropdown-menu-note {
    padding-inline: calc(var(--spacing) * 2.5);
    padding-block: calc(var(--spacing) * 1.5);
    font-size: var(--text-2sm);
    line-height: var(--text-2sm--line-height);
    color: var(--muted-foreground);
  }
}

/* Foundation — the page-surface preflight (#151).
 *
 * The styles.css header, the README, and CLAUDE.md all describe a preflight that
 * paints the body background, but the generated dist ships NO `body` selector at
 * all: `--background`/`--foreground` are defined and never applied to the page.
 * It looks right today only because the UA default surface is white; the moment
 * `.dark` is applied, the page would stay white behind the dark cards. Paint the
 * surface here to complete the preflight.
 *
 * @layer base — this is preflight, not chrome: in the base layer it orders after
 * the dist's base reset (both files declare `@layer base`; layout.css loads
 * second) yet stays BENEATH any host component/utility rule, so a consumer that
 * themes its own body still wins. The tokens resolve per theme, so the surface
 * follows `.dark` automatically. */
@layer base {
  body {
    background-color: var(--background);
    color: var(--foreground);
  }
}

/* Fidelity + dead-class cleanup (#151). @layer components so these order after
 * the dist's component rules, exactly like the pagination/stepper chrome above. */
@layer components {
  /* Avatar status dot — corner placement. The dist ships .kt-avatar-indicator as
     `position: absolute` with NO offsets; Metronic pins it per-usage with inline
     utilities (reference/metronic demo1: `-end-2 -bottom-2`). AvatarComponent
     hard-codes the class, so the component must OWN the corner placement or the
     dot lands at its static position in every consumer. Author the reference
     offsets here (spacing*-2 on the trailing + bottom edges). */
  .kt-avatar-indicator {
    inset-inline-end: calc(var(--spacing) * -2);
    bottom: calc(var(--spacing) * -2);
  }

  /* Footer chrome — the dist ships NO .kt-footer rule, so the class FooterComponent
     emits as its root was dead. In the admin shell the footer is a flex-column
     child of .kt-sidebar-wrapper alongside the header and the growing
     .kt-admin-main (`flex: 1 0 auto`), so it must hold its height rather than be
     squeezed. Structural only — the footer's visual padding stays on the inner
     kt-container-fixed utilities, matching the borderless reference footer 1:1. */
  .kt-footer {
    flex-shrink: 0;
  }

  /* Nav-group icon sizing — retargeted from `svg` to the emitted keenicon `<i>`.
     The dist keys these rules on `.kt-…-item svg`, but pagination, breadcrumb, and
     the accordion menu all emit keenicon `<i>` font glyphs, so the dist rules
     match nothing and the arrows/separators render unsized (`.ki-filled` sets no
     font-size, so the glyph size IS the element font-size). Re-target to `<i>` and
     translate the dist svg width/height to the equivalent font-size.

     Color: pagination color is already state-aware on the item itself (muted /
     --foreground for the prev-next arrows / --accent-foreground on hover+active),
     so the glyph must INHERIT it — setting a color here would freeze the arrow
     muted through hover. Breadcrumb glyphs likewise inherit (the dist svg rules
     set none). Only the accordion menu glyph carries a color, replicating the dist
     `.kt-accordion-menu-link svg` rule (its `<i>` carries no color otherwise). */
  .kt-pagination-item i {
    font-size: calc(var(--spacing) * 4);
  }
  .kt-breadcrumb-link i {
    font-size: calc(var(--spacing) * 4);
  }
  .kt-breadcrumb-separator i {
    font-size: calc(var(--spacing) * 3.5);
  }
  .kt-accordion-menu-link i {
    font-size: calc(var(--spacing) * 4);
    flex-shrink: 0;
    color: var(--muted-foreground);
  }
}
