/* shadcn-inspired design tokens. UnoCSS theme.colors map utility class names
   (bg-background, text-foreground, border-border, ...) to these HSL variables. */

:root {
    --background: 0 0% 100%;
    --foreground: 240 10% 3.9%;
    --card: 0 0% 100%;
    --muted: 240 4.8% 95.9%;
    --muted-foreground: 240 3.8% 46.1%;
    --border: 240 5.9% 90%;
    --primary: 221 83% 53%;
    --primary-foreground: 0 0% 100%;
    --ring: 221 83% 53%;
    /* Content links share the same accent FluentUI, icons, and primary controls
       use: --link tracks --primary so a single hue drives them all. There is no
       visited distinction — a followed link keeps the exact same accent. */
    --link: var(--primary);

    /* Semantic status hues shared by build/PR analysis rails and badges across the
       Pull Requests / Builds tables and the dashboard repeater. */
    --status-error: 0 74% 58%;
    --status-success: 142 62% 45%;
    --status-success-foreground: 142 55% 45%;

    --radius: 0.5rem;

    /* Shared input surface — a depth model. Every Fluent input (the text-entry controls
       plus the checkbox / switch / radio boxes, see overrides.css) is a recessed well:
       its fill sits at --background so it reads *below* the raised group-header band
       (--foreground over the card), which the old flat --muted fill collided with. In
       dark mode the well is visibly darker than the card; in light mode the fill equals
       the white card body, so the border — a real line meeting WCAG 1.4.11 non-text
       contrast (>=3:1 against the fill, the card, and the filter-row backdrop) — carries
       the edge. --input-shadow insets a top shadow that reinforces the recess and
       --input-height sizes every single-line control. Hover lifts the fill a step and
       strengthens the border; focus returns to the resting fill, switches the border to
       the accent, and adds --input-ring. Separate from FluentUI's own foreground-colored
       host outline, which is killed in overrides.css. */
    --input-bg: hsl(var(--background));
    --input-bg-hover: hsl(240 5% 94%);
    --input-border: hsl(240 5% 55%);
    --input-border-hover: hsl(240 5% 44%);
    --input-ring: 0 0 0 3px hsl(var(--primary) / 0.22);
    --input-shadow: inset 0 1px 2px hsl(var(--foreground) / 0.08);
    --input-height: 2rem;

    --app-header-height: 4.5rem;
    --app-sidebar-width: 16rem;
}

.dark {
    --background: 240 10% 3.9%;
    --foreground: 0 0% 98%;
    --card: 240 10% 5.5%;
    --muted: 240 3.7% 15.9%;
    --muted-foreground: 240 5% 64.9%;
    --border: 240 3.7% 15.9%;
    --primary: 217 91% 60%;
    --primary-foreground: 0 0% 100%;
    --ring: 217 91% 60%;

    /* Recessed well: fill drops to --background, one step darker than --card, so the
       input reads below the raised header band; the top inset shadow deepens it. */
    --input-bg: hsl(var(--background));
    --input-bg-hover: hsl(240 8% 9%);
    --input-border: hsl(240 5% 44%);
    --input-border-hover: hsl(240 5% 52%);
    --input-ring: 0 0 0 3px hsl(var(--primary) / 0.22);
    --input-shadow: inset 0 1px 2px hsl(0 0% 0% / 0.5);
}

html,
body {
    height: 100%;
    margin: 0;
    padding: 0;
    overflow: hidden;
    font-family: "Inter", "Segoe UI", ui-sans-serif, system-ui, sans-serif;
    background-color: hsl(var(--background));
    color: hsl(var(--foreground));
    -webkit-font-smoothing: antialiased;
}

* {
    box-sizing: border-box;
}

a {
    color: inherit;
    font-weight: 400;
    text-decoration: none;
}

/* Global visited reset. Textual links inherit their unvisited color instead of
   the browser's default purple, so any anchor not carrying a component class
   (e.g. links emitted by rendered markdown) can never leak a visited hue. Its
   low specificity (0,1,1) leaves every intentional component untouched: brand,
   sidebar nav, content links, and markdown links each override with a
   class-based :visited or scoped rule of equal-or-higher specificity. */
a:visited {
    color: inherit;
}

/* Canonical content-link vocabulary. One reusable class drives every in-content
   hyperlink (data-table cells, dashboard cards, detail pages) so they share the
   exact FluentUI / icon / primary accent and sit at normal weight (never bold).
   There is deliberately no visited distinction: the color is identical in the
   default, visited, hover, active, and focus-visible states. Hover / focus add
   an underline (and the shared focus ring below) as non-color affordances so a
   cue never depends on color alone. Structural or control-like anchors (app
   brand, sidebar nav, icon-only external links, badge/pill links) opt out by
   simply not carrying this class. */
.content-link {
    color: hsl(var(--link));
    font-weight: 400;
    text-decoration: none;
    text-underline-offset: 0.15em;
    transition: color 120ms ease-out;
}

.content-link:visited,
.content-link:active {
    color: hsl(var(--link));
}

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

/* FocusOnNavigate programmatically focuses the page <h1> on each navigation
   for screen-reader accessibility. Suppress only the visual focus ring on
   non-interactive heading elements — keyboard focus rings on buttons,
   links, and inputs are preserved. */
h1:focus,
h2:focus,
h3:focus,
h4:focus,
h5:focus,
h6:focus,
h1:focus-visible,
h2:focus-visible,
h3:focus-visible,
h4:focus-visible,
h5:focus-visible,
h6:focus-visible {
    outline: none;
}

/* Standardized focus indicator: a single calm accent ring for every keyboard-
   focusable element, replacing the browser's default (heavy, sometimes orange)
   outline so focus looks consistent across the app. Fluent web components draw
   their own token-driven focus stroke (see overrides.css), so the host outline
   is suppressed for them to avoid a doubled ring. */
:focus-visible {
    outline: 2px solid hsl(var(--ring) / 0.55);
    outline-offset: 2px;
}

fluent-button:focus-visible,
fluent-anchor:focus-visible,
fluent-nav-menu:focus-visible,
fluent-menu-item:focus-visible,
fluent-select:focus-visible,
fluent-search:focus-visible,
fluent-text-field:focus-visible,
fluent-switch:focus-visible,
fluent-checkbox:focus-visible,
fluent-tab:focus-visible,
fluent-option:focus-visible {
    outline: none;
}

/* Critical layout CSS — guarantees the admin shell renders with correct
   dimensions on first paint, before the UnoCSS runtime has generated any
   utility classes. */

.app-shell {
    height: 100vh;
    height: 100dvh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    background-color: hsl(var(--background));
    color: hsl(var(--foreground));
}

.app-header {
    height: var(--app-header-height);
    display: grid;
    /* Align the page-header separator with the sidebar edge. */
    grid-template-columns: calc(var(--app-sidebar-width) - 2rem) 1fr auto;
    align-items: center;
    gap: 1rem;
    padding: 0 1rem;
    border-bottom: 1px solid hsl(var(--border));
    background-color: hsl(var(--background));
    color: hsl(var(--foreground));
    flex-shrink: 0;
    z-index: 30;
}

.brand {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-weight: 400;
    font-size: 1.125rem;
    letter-spacing: -0.01em;
    color: hsl(var(--foreground) / 0.75);
    text-decoration: none;
}

.brand:hover,
.brand:visited {
    color: hsl(var(--foreground) / 0.75);
    text-decoration: none;
}

.brand__mark {
    display: inline-block;
    width: 1.5rem;
    height: 1.5rem;
    border-radius: 0.375rem;
    background-color: hsl(var(--primary));
}

.app-header__center {
    justify-self: start;
    align-self: center;
    min-width: 0;
    width: 100%;
}

.page-header {
    position: relative;
    display: flex;
    align-items: center;
    gap: 1rem;
    min-width: 0;
    height: 100%;
    /* Match the main content's 1rem (p-4) inset so the page title lines up
       exactly with the content below it. */
    padding-left: 1rem;
}

.page-header::before {
    content: "";
    position: absolute;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    width: 1px;
    height: 2.5rem;
    background-color: hsl(var(--foreground) / 0.08);
}

.page-header__titles {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 0.125rem;
    min-width: 0;
}

.page-header__title {
    margin: 0;
    font-size: 1.5rem;
    font-weight: 600;
    line-height: 1.2;
    letter-spacing: -0.01em;
    color: hsl(var(--foreground) / 0.78);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.page-header__subtitle {
    margin: 0;
    font-size: 0.85rem;
    line-height: 1.3;
    color: hsl(var(--muted-foreground));
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.page-header__actions {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.app-body {
    display: flex;
    flex: 1;
    min-height: 0;
    overflow: hidden;
}

.app-sidebar {
    width: var(--app-sidebar-width);
    flex-shrink: 0;
    border-right: 1px solid hsl(var(--border));
    background-color: hsl(var(--background));
    overflow-x: hidden;
    overflow-y: auto;
    transition: width 200ms ease-out;
    scrollbar-gutter: stable;
    scrollbar-width: thin;
    scrollbar-color: hsl(var(--foreground) / 0.25) transparent;
}

.app-sidebar::-webkit-scrollbar {
    width: 8px;
}

.app-sidebar::-webkit-scrollbar-thumb {
    background-color: hsl(var(--foreground) / 0.25);
    border-radius: 4px;
}

.app-sidebar--collapsed {
    width: 3.75rem;
}

/* Pre-paint collapsed state set by the inline script in App.razor, applied
   before Blazor hydrates. Removed automatically once AdminLayout takes over
   and the .app-sidebar--collapsed class on <aside> drives the width. */
html.sidebar-collapsed .app-sidebar {
    width: 3.75rem;
}

html.sidebar-collapsed .app-sidebar .nav-item__label {
    display: none;
}

/* Keep the nav block at the expanded width regardless of the sidebar's
   outer width. Combined with `overflow: hidden` on .app-sidebar, this
   pins every nav item — and its icon — at a fixed horizontal position
   during the open/close animation, so icons never shift sideways. */
.app-sidebar > nav {
    width: var(--app-sidebar-width);
}

.app-main {
    flex: 1;
    min-width: 0;
    min-height: 0;
    overflow: auto;
    background-color: hsl(var(--muted) / 0.4);
    color: hsl(var(--foreground));
}

/* Sidebar nav item — fixed height + nowrap so animating sidebar width never
   reflows or shifts the items vertically. */
.nav-item {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    height: 2.5rem;
    padding: 0 0.75rem;
    border-radius: calc(var(--radius) - 2px);
    font-size: 0.875rem;
    font-weight: 400;
    color: hsl(var(--muted-foreground));
    text-decoration: none;
    white-space: nowrap;
    overflow: hidden;
    transition: background-color 120ms ease-out, color 120ms ease-out;
}

.nav-item:visited {
    color: hsl(var(--muted-foreground));
    text-decoration: none;
}

.nav-item:hover {
    background-color: hsl(var(--muted));
    color: hsl(var(--foreground));
    text-decoration: none;
}

.nav-item__icon {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.25rem;
    height: 1.25rem;
}

.nav-item__label {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

.nav-link.active.nav-item,
.nav-item.nav-item-active {
    background-color: hsl(var(--foreground) / 0.08);
    color: hsl(var(--foreground));
    font-weight: 400;
}

.nav-link.active.nav-item:hover,
.nav-item.nav-item-active:hover {
    background-color: hsl(var(--foreground) / 0.12);
    color: hsl(var(--foreground));
}

/* When the sidebar is collapsed, hide labels entirely so only the icon
   column shows. Labels reappear instantly when the sidebar starts to
   expand, so the text is fully present during the width animation. */
.app-sidebar--collapsed .nav-item__label {
    display: none;
}

.card {
    background-color: hsl(var(--card));
    border: 1px solid hsl(var(--border));
    border-radius: var(--radius);
}

/* Pre-hydration loader shown inside #app until blazor.webassembly.js mounts
   the App component. Uses the same HSL design tokens so it respects the
   pre-paint .dark class set by the inline theme script in index.html. */
.app-loading {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1rem;
    background-color: hsl(var(--background));
    color: hsl(var(--foreground));
}

.app-loading-spinner {
    width: 2.5rem;
    height: 2.5rem;
    border-radius: 50%;
    border: 3px solid hsl(var(--muted));
    border-top-color: hsl(var(--primary));
    animation: app-loading-spin 0.8s linear infinite;
}

.app-loading-text {
    margin: 0;
    font-size: 0.875rem;
    color: hsl(var(--muted-foreground));
}

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

/* Default-hidden Blazor error UI. blazor.webassembly.js flips display to
   block on an unhandled error and wires the .reload / .dismiss children. */
#blazor-error-ui {
    color-scheme: light only;
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 0.75rem;
    top: 0.5rem;
}
