/*
 * Omnx Flow — base application styles (Material Design 3 dark mode)
 * ---------------------------------------------------------------------------
 * Port of the etcX app/main.css base layer, fully tokenized: reset, body,
 * Roboto stack, 56px top-bar, 40px icon-button, keyframes (ripple / shimmer /
 * fadeIn / badge-appear), 8px scrollbars, badge, status dots, dark-native
 * pickers. All colour values come from tokens.css — no raw palette hex here.
 */

/* ---------------------------------------------------------------------------
   Dark is the identity (the etcX v2 sheet) and the default. Declaring the
   scheme on :root makes EVERY piece of UA chrome — scrollbars, selects,
   autofill, form controls — render to match instead of flashing defaults
   through. The LIGHT theme (tokens.css section 6) is selected by
   data-theme="light", which theme.js stamps before first paint; the UA
   chrome follows it here, and the picker glyph stops being inverted.
   --------------------------------------------------------------------------- */
:root {
    color-scheme: dark;
}
:root[data-theme="light"] {
    color-scheme: light;
}

/* ---------------------------------------------------------------------------
   Reduced motion (a11y) — one global switch. Every keyframe animation and
   transition in the app collapses to (near) zero for users who asked for it;
   individual sheets do not need their own blocks. The 1ms (not 0) keeps
   animation/transition end events firing for any JS that awaits them.
   --------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 1ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 1ms !important;
        scroll-behavior: auto !important;
    }
}

/* ---------------------------------------------------------------------------
   Dark-mode native date/time pickers (app-wide base).
   color-scheme:dark makes the browser's built-in calendar / clock popup render
   dark to match the app, for EVERY native date/time input regardless of class.
   --------------------------------------------------------------------------- */
input[type="date"],
input[type="datetime-local"],
input[type="month"],
input[type="week"],
input[type="time"] {
    color-scheme: inherit;
}
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="datetime-local"]::-webkit-calendar-picker-indicator,
input[type="month"]::-webkit-calendar-picker-indicator,
input[type="week"]::-webkit-calendar-picker-indicator,
input[type="time"]::-webkit-calendar-picker-indicator {
    filter: invert(0.7);
    cursor: pointer;
}
:root[data-theme="light"] input::-webkit-calendar-picker-indicator {
    filter: none;
}

/* ---------------------------------------------------------------------------
   Reset + body
   --------------------------------------------------------------------------- */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
}

/* FORM CONTROLS START FROM NOTHING. A <button> beside a <span> wearing the
   same class must render identically: the browser's own font size, line
   height, colour, border and appearance are gone here at element
   specificity, so every class rule in the app decides the rest. Measured
   2026-09-17 on three surfaces -- the app switcher's Preferences row, the
   imports table's issue-count badge -- before this existed. */
button, input, select, textarea {
    font: inherit;
    color: inherit;
    letter-spacing: inherit;
    text-align: inherit;
}
button {
    border: 0;
    background: none;
    appearance: none;
    -webkit-appearance: none;
    cursor: pointer;
}

/* A DOCUMENT SCROLLS. This used to read `overflow: hidden`, which is the
   console shell's requirement -- and a body overflow value PROPAGATES to the
   viewport, so it froze every OTHER page at one screenful. `overflow: hidden`
   on the viewport still permits window.scrollTo(), which is why the headless
   screenshots never caught it: only a person dragging finds out. /feedback had
   already patched it locally for itself; /setup, /me, /login and /onboarding
   had not, so the same shell (.app-mobile) scrolled on one page and not the
   other. The lock now belongs to the shell that wants it -- see body.app-shell
   below -- and the default is what a document does. */
body {
    margin: 0;
    padding: 0;
    font-family: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
    background-color: var(--surface);
    color: var(--text-primary);
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    font-feature-settings: 'kern' 1;
    text-rendering: optimizeLegibility;
}

code, pre, .monospace {
    font-family: 'Roboto Mono', 'Courier New', monospace;
}

/* ============================================
   TOP BAR
   ============================================ */

/* The bar GROWS by the top inset rather than being pushed down by it, so the
   status-bar strip is painted in the bar's own colour instead of showing the
   page behind it. The side insets matter in LANDSCAPE on a notched phone,
   where the cutout eats into one edge. All four read 0 in a browser tab. */
.top-bar {
    height: var(--top-bar-total);
    padding-top: var(--safe-top);
    background-color: var(--surface-bar);
    border-bottom: 1px solid var(--line);
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-left: calc(var(--spacing-md) + var(--safe-left));
    padding-right: calc(var(--spacing-md) + var(--safe-right));
    z-index: var(--z-sticky);
}

/* Headings and display numbers sit tight; a column of figures is tabular so
   digits line up and never jitter as they change. Declared once. */
h1, h2, h3, h4 {
    letter-spacing: var(--tracking-tight);
}
.num, .tabular {
    font-variant-numeric: tabular-nums;
}

.top-bar-left,
.top-bar-center,
.top-bar-right {
    display: flex;
    align-items: center;
    gap: var(--spacing-md);
}

/* Signed-in chip (rendered by session-chip.js only when a session exists). */
.session-chip {
    display: flex;
    align-items: center;
    gap: var(--spacing-sm);
    padding-left: var(--spacing-sm);
    border-left: 1px solid var(--line);
    font-size: var(--text-md);
    color: var(--text-secondary);
}

.session-chip .material-symbols-outlined {
    font-size: var(--text-2xl);
    color: var(--text-muted-2);
}

.session-chip .session-user {
    color: var(--text-primary);
    font-weight: 500;
}

.top-bar-center {
    flex: 1;
    max-width: 600px;
}

/* Logo / brand */
.logo {
    display: flex;
    align-items: center;
    gap: var(--spacing-xs);
    font-weight: 500;
    font-size: var(--text-xl);
    color: var(--primary);
}

/* Wordmark image — the served file is header-logo.trimmed.png, a margin-trimmed
   128px-tall copy of header-logo.png (the untouched master beside it).
   ONE mark, app-wide (owner ruling 2026-08-31, card frontend-makeover-D1): the
   auth pages used to letter their own lockup out of a generic Material glyph. */
.logo-img {
    display: block;
    height: 24px;
    width: auto;
}

/* The sign-in card and the onboarding header are not dense the way a top bar
   is, so the same mark is shown larger there. */
.logo-img.lg {
    height: 40px;
}

/* The auth lockup lives HERE, not in login.css: Onboarding uses it and does not
   load that sheet, so its brand row rendered unstyled until this moved. */
.login-brand {
    display: flex;
    align-items: center;
    gap: var(--spacing-sm);
    margin-bottom: var(--spacing-md);
}

/* Icon Button — the 40px top-bar variant (base layer). The 36px admin variant
   lives with the admin sheets; components/buttons.css deliberately does NOT
   redefine .icon-button so this base spec stays authoritative app-wide. */
.icon-button {
    width: 40px;
    height: 40px;
    border-radius: var(--radius-xl);
    background: transparent;
    border: none;
    color: var(--text-secondary);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background-color var(--transition-fast), color var(--transition-fast);
    position: relative;
    overflow: hidden;
}

/* A link wearing the button (the admin bar's feedback link) never underlines. */
a.icon-button {
    text-decoration: none;
}

.icon-button:hover {
    background-color: var(--surface-hover);
    color: var(--text-primary);
}

.icon-button:active {
    background-color: var(--line);
}

/* Material Design Ripple Effect */
.icon-button::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    background: var(--accent-tint-20);
    transform: scale(0);
    pointer-events: none;
}

.icon-button:active::before {
    animation: ripple var(--duration-slower) ease-out;
}

/* User Profile */
.user-profile {
    display: flex;
    align-items: center;
    gap: var(--spacing-xs);
    cursor: pointer;
    padding: var(--spacing-xs);
    border-radius: var(--radius-xl);
    transition: background-color var(--transition-fast), color var(--transition-fast);
}

.user-profile:hover {
    background-color: var(--md-sys-color-surface-container-highest);
}

.user-avatar {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--primary), var(--tertiary));
    color: var(--on-primary);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--text-base);
    font-weight: 500;
}

/* ============================================
   MAIN CONTAINER
   ============================================ */

/* THE CONSOLE SHELL OPTS OUT OF DOCUMENT SCROLLING. /  and /admin are a
   fixed 56px bar plus a viewport-height container with its own inner
   scrollers; a second scrollbar on the document would scroll that whole fixed
   shell out of the window. Declared in the markup (`<body class="app-shell">`)
   rather than inferred, so a page states which kind it is. */
body.app-shell {
    overflow: hidden;
}

.main-container {
    display: flex;
    /* 100dvh, not 100vh: on a phone browser 100vh is the LARGE viewport (URL
       bar hidden), so a 100vh shell is taller than what the person can see and
       its last row hides under the URL bar until they scroll -- which this
       shell does not do. The 100vh line stays first as the fallback for a
       browser that has never heard of dvh. */
    height: calc(100vh - var(--top-bar-total));
    height: calc(100dvh - var(--top-bar-total));
    overflow: hidden;
}

/* ============================================
   ADVANCED ANIMATIONS
   ============================================ */

/* Shimmer Animation (for loading states) */
@keyframes shimmer {
    0% {
        background-position: -200% 0;
    }
    100% {
        background-position: 200% 0;
    }
}

.shimmer {
    background: linear-gradient(
        90deg,
        var(--surface-hover) 0%,
        var(--surface-sheen) 20%,
        var(--surface-hover) 40%,
        var(--surface-hover) 100%
    );
    background-size: 200% 100%;
    animation: shimmer var(--duration-loop) infinite linear;
}

/* Ripple Effect Animation */
@keyframes ripple {
    0% {
        transform: scale(0);
        opacity: 0.5;
    }
    100% {
        transform: scale(2);
        opacity: 0;
    }
}

/* Badge Appear Animation */
@keyframes badge-appear {
    from {
        opacity: 0;
        transform: scale(0.8);
    }
    to {
        opacity: 1;
        transform: scale(1);
    }
}

/* Fade In Animation */
@keyframes fadeIn {
    from {
        opacity: 0;
        transform: translateY(10px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* ============================================
   SCROLLBAR STYLING
   ============================================ */

::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}

::-webkit-scrollbar-track {
    background: transparent;
}

::-webkit-scrollbar-thumb {
    background: var(--line-strong);
    border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
    background: var(--md-sys-color-outline);
}

/* ============================================
   UTILITY CLASSES
   ============================================ */

.hidden {
    display: none !important;
}

/* The `hidden` ATTRIBUTE must actually hide.
 *
 * The UA stylesheet implements [hidden] as `display: none`, which any author
 * rule with a real display value silently beats — and `.tab-button`,
 * `.mobile-nav-btn` and `.permission-checkbox` all set `display: flex`. The
 * consequence was not cosmetic: permission-gated tabs are hidden by setting
 * `el.hidden = true`, so without this rule a Scheduler was shown every tab in
 * the admin console and a staff member saw the approvals tab.
 *
 * Declared once, here, rather than per-component: the next component to set a
 * display value would reintroduce the same bug, and it would look like an
 * access-control failure rather than a CSS specificity one. */
[hidden] {
    display: none !important;
}

.material-symbols-outlined {
    font-variation-settings:
        'FILL' 0,
        'wght' 400,
        'GRAD' 0,
        'opsz' 24;
}

/* Badge */
.badge {
    background-color: var(--md-sys-color-error);
    color: var(--md-sys-color-on-error);
    font-size: var(--text-xs);
    font-weight: 500;
    padding: var(--space-2) var(--space-6);
    border-radius: var(--radius-pill);
    min-width: 18px;
    text-align: center;
}

/* Status Dot */
.status-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    display: inline-block;
    margin-right: var(--space-6);
}

.status-dot.saved {
    background-color: var(--dot-saved);
}

.status-dot.unsaved {
    background-color: var(--dot-unsaved);
}

.status-dot.error {
    background-color: var(--md-sys-color-error);
}

/* ============================================
   FOCUS (a11y) — keyboard-only ring, per the v2 polish spec
   ============================================ */

button:focus-visible {
    outline: var(--focus-ring);
    outline-offset: var(--focus-ring-offset);
}

input:focus-visible,
select:focus-visible,
textarea:focus-visible {
    outline: var(--focus-ring);
    outline-offset: 1px;
}

/* ============================================
   RESPONSIVE DESIGN
   ============================================ */

@media (max-width: 1024px) {
    .top-bar-center {
        max-width: 400px;
    }
}

@media (max-width: 768px) {
    .top-bar {
        /* The safe-area insets MUST be repeated here. A `padding` shorthand in a
       media query resets padding-top to 0, which silently drops the notch
       reservation the base rule made -- measured: the console bar was back
       under the status bar at 390px while the desktop rule looked correct. */
        padding: var(--safe-top) calc(var(--spacing-sm) + var(--safe-right))
                 0 calc(var(--spacing-sm) + var(--safe-left));
        gap: var(--spacing-sm);
    }

    .logo-img {
        height: 24px;
    }

    /* MEASURED 2026-09-03: at 390px the admin console laid out 624px wide,
       because the title, the user chip and the sign-out button all refused
       to shrink and the whole page scrolled sideways. The centre column now
       yields, the title truncates, and the role list is dropped first. */
    .top-bar-center {
        flex: 1 1 auto;
        min-width: 0;
        gap: var(--spacing-xs);
    }

    .top-bar-center .page-title {
        min-width: 0;
        font-size: var(--text-lg);
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    .top-bar-right {
        flex-shrink: 0;
        gap: var(--spacing-xs);
    }

    .session-chip .session-roles {
        display: none;
    }
}

@media (max-width: 600px) {
    /* The icon stays as the surface's mark; the words go. */
    .top-bar-center .page-title-text {
        display: none;
    }

    .session-chip .session-user {
        display: none;
    }

    .session-chip {
        padding-left: 0;
        border-left: 0;
    }
}
