/* PRFactory Web UI Styles
   ----------------------------------------------------------------------
   The single :root token block that used to live here (Phase 17.0) has
   been removed. All design tokens now come from the canonical token
   system (tokens.css → tokens/*); legacy token names used by the rules
   below resolve through the variables.css compatibility alias shim,
   which is imported once by site.css. This file is now global element
   styling ONLY.
   ---------------------------------------------------------------------- */

body {
    font-family: var(--font-family-base);
    background-color: var(--bg-color-alt);
}

/* Loading state */
.loading-overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: rgba(0, 0, 0, 0.5);
    display: flex;
    justify-content: center;
    align-items: center;
    z-index: var(--z-index-modal);
}

/* Standalone (overlay/page-level) spinners get the large size. The small
   in-button variant must keep Bootstrap's 1rem box — app.css loads after
   bootstrap.min.css at equal specificity, so an unscoped .spinner-border
   rule silently wins over .spinner-border-sm and blows the spinner past
   the button's own height (--control-h 38px / --control-h-sm 30px). */
.spinner-border:not(.spinner-border-sm) {
    width: var(--space-2xl);
    height: var(--space-2xl);
}

/* Cards */
.card {
    border: none;
    box-shadow: var(--shadow-sm);
    margin-bottom: var(--space-lg);
}

.card-header {
    background-color: var(--bg-color);
    border-bottom: var(--border-width-thick) solid var(--border-color);
    font-weight: var(--font-weight-semibold);
}

/* Tables */
.table {
    background-color: var(--bg-color);
}

.table thead th {
    border-top: none;
    border-bottom: var(--border-width-thick) solid var(--border-color);
    font-weight: var(--font-weight-semibold);
}

/* Forms */
.form-label {
    font-weight: var(--font-weight-medium);
    margin-bottom: var(--space-sm);
}

/* Animations */
@keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

.fade-in {
    animation: fadeIn var(--transition-slow) ease-in;
}

/* Real-time update indicator */
.update-indicator {
    position: fixed;
    bottom: var(--space-md);
    right: var(--space-md);
    padding: var(--space-sm) var(--space-md);
    background-color: var(--success-color);
    color: white;
    border-radius: var(--border-radius-sm);
    box-shadow: var(--shadow-md);
    z-index: var(--z-index-base);
    animation: fadeIn var(--transition-slow) ease-in;
}

/* Responsive utilities */
@media (max-width: 768px) {
    .table-responsive {
        font-size: var(--font-size-sm);
    }
}

/* Blazor error UI */
#blazor-error-ui {
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: var(--z-index-base);
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 0.75rem;
    top: var(--space-sm);
}

/* App-shell boot overlay. Sibling of the <component> marker, so Blazor never
   removes it — it hides itself the moment ANY layout root exists in the DOM.
   Same class of selector as #blazor-error-ui above: a shell id, not a component. */
#prf-boot {
    position: fixed;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-sm);
    background: var(--color-bg-base);
    color: var(--color-text-secondary);
    font-family: var(--font-sans);
    z-index: var(--z-index-boot);
}

/* A circuit that never starts renders no layout root, so the hide rule below never
   fires and the overlay stays up. It must not then paint over the one element that
   says why: Blazor reveals #blazor-error-ui by setting display inline. Yielding to it
   turns "Still connecting" — which would be wrong — back into the error and its
   Reload link. The z-index above is the belt to this rule's braces. */
body:has(#blazor-error-ui[style*="display"]) #prf-boot {
    display: none;
}

.prf-boot-spinner {
    width: 2rem;
    height: 2rem;
    border: 2px solid var(--color-border);
    border-top-color: var(--color-accent);
    border-radius: 50%;
    animation: prf-boot-spin 0.9s linear infinite;
}

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

/* The boot hint is the ONLY thing that turns ISSUE-09's permanently blank page
   into text. It reveals itself after 10s with a pure animation delay — no JS. */
.prf-boot-hint {
    opacity: 0;
    animation: prf-boot-reveal 0s linear 10s forwards;
}

@keyframes prf-boot-reveal { to { opacity: 1; } }

/* The circuit has rendered as soon as ANY layout root exists. All three are
   enumerated deliberately; MainLayout's #main-content alone would leave the
   overlay burning on the four EmptyLayout auth pages. */
body:has(.page, .public, .empty-layout) #prf-boot {
    display: none;
}

@media (prefers-reduced-motion: reduce) {
    .prf-boot-spinner { animation: none; }
    .prf-boot-hint { animation-delay: 0s; opacity: 1; }
}

/* blazor.web.js toggles one state class at a time on the element. "retrying" is only ever
   added beside "show" (the show rules already apply), and "hide" is the default hidden state;
   every other state is styled below. "paused" reuses the failed message and "resume-failed" the
   rejected one: both end in "Reload the page", so no state needs copy of its own. */
#components-reconnect-modal { display: none; }

#components-reconnect-modal.components-reconnect-show,
#components-reconnect-modal.components-reconnect-failed,
#components-reconnect-modal.components-reconnect-rejected,
#components-reconnect-modal.components-reconnect-paused,
#components-reconnect-modal.components-reconnect-resume-failed {
    display: block;
    position: fixed;
    inset-block-end: 0;
    inset-inline: 0;
    padding: var(--space-sm) var(--space-md);
    background: var(--color-warning-soft);
    color: var(--color-warning-text);
    border-block-start: 1px solid var(--color-warning-line);
    z-index: var(--z-index-modal);
}

#components-reconnect-modal .prf-reconnect-show,
#components-reconnect-modal .prf-reconnect-failed,
#components-reconnect-modal .prf-reconnect-rejected { display: none; }

#components-reconnect-modal.components-reconnect-show          .prf-reconnect-show,
#components-reconnect-modal.components-reconnect-failed        .prf-reconnect-failed,
#components-reconnect-modal.components-reconnect-rejected      .prf-reconnect-rejected,
#components-reconnect-modal.components-reconnect-paused        .prf-reconnect-failed,
#components-reconnect-modal.components-reconnect-resume-failed .prf-reconnect-rejected {
    display: inline;
}

/* =========================================================================
   Theme toggle — directional affordance show/hide (GLOBAL, not scoped)
   -------------------------------------------------------------------------
   The no-JS ThemeToggle renders BOTH directional anchors; exactly one is
   shown for the EFFECTIVE theme. The server cannot read the OS
   prefers-color-scheme media query (no JS, CLAUDE.md), so the correct
   affordance is selected purely in CSS by the [data-theme] /
   prefers-color-scheme state of the <html> ANCESTOR.

   These rules MUST live in this global stylesheet, NOT in
   ThemeToggle.razor.css: Blazor component-scoped CSS cannot reliably
   target ancestor (<html>) state, and the show/hide depends entirely on
   that ancestor. The four cases mirror EXACTLY the 4-case cascade in
   tokens/_color.css so the affordance always matches the rendered theme:

     effective theme  state                                shown anchor
     ---------------   -----------------------------------  ------------
     dark              :root:not([data-theme])  (no cookie, OS dark / default)  -> "switch to light"
     light             @media prefers light + :root:not([data-theme])           -> "switch to dark"
     dark              [data-theme="dark"]      (explicit cookie)                -> "switch to light"
     light             [data-theme="light"]     (explicit cookie)                -> "switch to dark"
   ========================================================================= */

/* Case 1 — canonical default (no cookie, OS dark / no OS preference):
   effective theme is dark => offer "switch to light".
   !important ensures this wins any component-scoped or Bootstrap rule. */
.theme-toggle .theme-toggle-to-light { display: inline-flex !important; }
.theme-toggle .theme-toggle-to-dark  { display: none !important; }

/* Case 2 — no cookie + OS prefers light: effective theme is light =>
   offer "switch to dark". Matches tokens/_color.css's
   @media (prefers-color-scheme: light) :root:not([data-theme]) block. */
@media (prefers-color-scheme: light) {
    :root:not([data-theme]) .theme-toggle .theme-toggle-to-light { display: none !important; }
    :root:not([data-theme]) .theme-toggle .theme-toggle-to-dark  { display: inline-flex !important; }
}

/* Case 3 — explicit cookie = dark: effective theme is dark =>
   offer "switch to light" (higher specificity always wins case 2). */
[data-theme="dark"] .theme-toggle .theme-toggle-to-light { display: inline-flex !important; }
[data-theme="dark"] .theme-toggle .theme-toggle-to-dark  { display: none !important; }

/* Case 4 — explicit cookie = light: effective theme is light =>
   offer "switch to dark". */
[data-theme="light"] .theme-toggle .theme-toggle-to-light { display: none !important; }
[data-theme="light"] .theme-toggle .theme-toggle-to-dark  { display: inline-flex !important; }

/* =========================================================================
   Native form controls — token theming (GLOBAL fallback)
   -------------------------------------------------------------------------
   BARE native <input>/<select>/<textarea> (the search box + filter selects
   on /tenants and /agent-prompts, etc.) carry NO styling class, so they
   fall back to the Bootstrap/UA default WHITE background — unreadable in
   dark mode. These element-selector rules theme such controls to the
   canonical design tokens for BOTH dark and light, mirroring the mockup
   .input/.select/textarea.input rules (styles.css L750-790).

   The (tenants/agent-prompts) controls carry `class="input input-search"`
   / `class="select"` — class names lifted from the mockup that were never
   ported into the shipped CSS, so they have NO theming at all. The rule
   below themes them, but deliberately EXCLUDES any control already owned
   by another stylesheet so it never regresses already-correct fields:
     • .form-control/.form-select/.form-check-input/.form-range — owned by
       cascade-reset.css (the pure-UI FormTextField/FormSelectField/etc.
       components render Bootstrap-classed controls).
     • Radzen .rz-* — also owned by cascade-reset.css.
   Non-text inputs (checkbox/radio/range/file/color/button/submit/reset/
   image) are excluded by enumerating only text-like [type] values plus
   the untyped default. No !important. */
input:not([type]):not(.form-control):not(.rz-textbox):not(.rz-inputtext):not(.rz-spinner-input),
input[type="text"]:not(.form-control):not(.rz-textbox):not(.rz-inputtext):not(.rz-spinner-input),
input[type="search"]:not(.form-control):not(.rz-textbox):not(.rz-inputtext),
input[type="email"]:not(.form-control):not(.rz-textbox):not(.rz-inputtext),
input[type="url"]:not(.form-control):not(.rz-textbox):not(.rz-inputtext),
input[type="tel"]:not(.form-control):not(.rz-textbox):not(.rz-inputtext),
input[type="password"]:not(.form-control):not(.rz-textbox):not(.rz-inputtext),
input[type="number"]:not(.form-control):not(.rz-textbox):not(.rz-inputtext):not(.rz-spinner-input),
select:not(.form-select):not(.form-control):not(.rz-dropdown):not(.rz-listbox):not(.rz-select-bar),
textarea:not(.form-control):not(.rz-textarea) {
    height: var(--control-h);
    background: var(--color-bg-sunken);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    color: var(--color-text);
    font-family: var(--font-sans);
    font-size: var(--fs-sm);
    padding: 0 var(--space-3);
    transition: border-color .14s var(--ease), box-shadow .14s var(--ease);
}

textarea:not(.form-control):not(.rz-textarea) {
    height: auto;
    min-height: var(--control-h);
    padding: var(--space-3);
    resize: vertical;
    line-height: var(--lh-snug);
}

input:not([type]):not(.form-control):not(.rz-textbox):not(.rz-inputtext):not(.rz-spinner-input):focus,
input[type="text"]:not(.form-control):not(.rz-textbox):not(.rz-inputtext):not(.rz-spinner-input):focus,
input[type="search"]:not(.form-control):not(.rz-textbox):not(.rz-inputtext):focus,
input[type="email"]:not(.form-control):not(.rz-textbox):not(.rz-inputtext):focus,
input[type="url"]:not(.form-control):not(.rz-textbox):not(.rz-inputtext):focus,
input[type="tel"]:not(.form-control):not(.rz-textbox):not(.rz-inputtext):focus,
input[type="password"]:not(.form-control):not(.rz-textbox):not(.rz-inputtext):focus,
input[type="number"]:not(.form-control):not(.rz-textbox):not(.rz-inputtext):not(.rz-spinner-input):focus,
select:not(.form-select):not(.form-control):not(.rz-dropdown):not(.rz-listbox):not(.rz-select-bar):focus,
textarea:not(.form-control):not(.rz-textarea):focus {
    outline: none;
    border-color: var(--color-accent);
    box-shadow: var(--shadow-focus);
    background: var(--color-surface-1);
}

select:not(.form-select):not(.form-control):not(.rz-dropdown):not(.rz-listbox):not(.rz-select-bar) {
    appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M3 4.5 6 7.5 9 4.5' stroke='%236e7d8d' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 12px center;
    padding-right: 30px;
}

[data-theme="light"] select:not(.form-select):not(.form-control):not(.rz-dropdown):not(.rz-listbox):not(.rz-select-bar) {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M3 4.5 6 7.5 9 4.5' stroke='%236b7280' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
}
