/* Small overrides on top of Tailwind (loaded via CDN in _Layout.cshtml) */

/* Turns off the browser's own "scroll anchoring" (its automatic habit of nudging your
   scroll position to compensate whenever content above the viewport changes size — meant
   to stop pages jumping as ads/images load). It actively fights the scroll-memory restore
   in custom.js's initScrollMemory(): the custom date/dropdown pickers finishing their
   styling pass after load grows content above the table, and the browser "helpfully"
   re-adjusts scrollY to compensate — overriding our own deliberate restore right after it
   runs. Confirmed via an isolated Playwright test (page settles 200px taller after load,
   restore consistently landed short until this was added). See ajax-navigation-wishlist
   memory note. */
html, body {
    overflow-anchor: none;
    /* Belt-and-suspenders alongside #main-content's own scrollbar-gutter below (2026-09-20) —
       reserves the vertical scrollbar's space on the TOP-level document too, so any page/
       popup that isn't confined to #main-content's own scroll region (the app shell wraps
       everything in a fixed h-screen/overflow-hidden div, but a future page or a browser
       quirk could still let html/body itself grow past the viewport) doesn't shift the whole
       page left the instant a scrollbar first appears. */
    scrollbar-gutter: stable;
}

::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}
::-webkit-scrollbar-thumb {
    background: #d4d4d8;
    border-radius: 9999px;
}
::-webkit-scrollbar-track {
    background: transparent;
}
/* Darken the two lightest border shades app-wide for better visibility —
   these were too subtle for users with low vision to see input/card edges */
.border-zinc-200 {
    border-color: #a1a1aa !important; /* was #e4e4e7 — much easier to see */
}
.border-zinc-100 {
    border-color: #d4d4d8 !important; /* was #f4f4f5 — still subtle but visible */
}
/* Always reserve space for the vertical scrollbar, even when it's not
   needed — prevents the page content from shifting a few pixels when
   switching between sub-tabs where one scrolls and another doesn't */
#main-content {
    scrollbar-gutter: stable;
}
/* App-wide focus ring + hover/transition polish for form controls and buttons.
   A couple of forms (Login, Forms.cshtml) already hand-apply a zinc focus ring
   per-input — this makes a focus effect automatic everywhere else too, instead
   of only where someone remembered to type the utility classes, and adds the
   smooth transition so it animates in instead of snapping. Blue by default
   (sir monster bro's original call) even though the rest of the app's buttons/
   tabs/nav stay zinc — a scoped choice, not a rebrand.

   Driven by --focus-accent/--focus-ring custom properties (default blue, right
   below) instead of a hardcoded color, so the theme picker can retint it. This
   used to be a hardcoded #3b82f6 that WON on <input> elements specifically
   (the :not([type=...]) chain here gives `input:focus` higher specificity than
   dark-mode.css/theme-*.css's `.focus\:border-zinc-400:focus` override) while
   losing on <select>/<textarea> (lower specificity, no :not() chain) — so a
   themed page showed blue rings on plain text inputs but the theme's own color
   on textareas and the custom dropdown trigger buttons. Routing everything
   through one shared variable removes the specificity race entirely: every
   themed surface now redefines these two variables in its own `html.theme-*`
   block instead of fighting this rule for the border-color/box-shadow itself. */
:root {
    --focus-accent: #3b82f6; /* blue-500 */
    --focus-ring: rgb(59 130 246 / 0.25); /* blue-500 @ 25% ring */
}
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]),
select,
textarea {
    transition: border-color 150ms ease, box-shadow 150ms ease;
}
/* `[data-ascend-trigger]` marks the visible trigger BUTTON of every custom picker/dropdown
   widget custom.js builds (date, time, select — see enhanceDateInputs/enhanceTimeInputs/
   enhanceSelects) — buttons aren't matched by the input/select/textarea selector above, so
   without this they'd fall back to the browser's own plain default focus outline instead of
   this app's ring. Any FUTURE custom trigger widget gets this for free automatically as long
   as it's built the same way (stamp `data-ascend-trigger` on the trigger button) — this is the
   one convention to keep, everything else here (native <input>/<select>/<textarea>, including
   ones added later) is already covered with zero extra work by the selector above. */
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):focus,
select:focus,
textarea:focus,
[data-ascend-trigger]:focus {
    outline: none;
    border-color: var(--focus-accent) !important;
    /* Two layers, no blur, same technique Tailwind's own `ring` utility uses — a crisp solid
       1px line right at the border (full-opacity accent) reads as a distinct "bright ring",
       then a wider translucent halo (--focus-ring is a low-alpha rgb) reads as the softer glow
       around it. This used to be the glow layer alone; a themed page's plain inputs (no
       focus:ring-* utility classes in their own markup) looked flatter than fields that
       happened to carry Tailwind ring classes — this one rule now gives every field, selector,
       and dropdown the same two-part look regardless of what classes its markup carries. */
    box-shadow: 0 0 0 1px var(--focus-accent), 0 0 0 4px var(--focus-ring) !important;
}
button,
a.btn,
[role="tab"] {
    transition: background-color 150ms ease, color 150ms ease, border-color 150ms ease, box-shadow 150ms ease;
}
/* Subtle hover "lift" for dashboard/overview cards & tiles - nudges the whole
   card up a couple px on hover instead of only reacting with a border/shadow
   change, so it reads as the card rising off the page (sir monster bro's ask).
   Piggybacks on Tailwind's hover:shadow-sm utility class, which every one of
   these cards already carries (Home dashboard tiles, Department Overview,
   Admin) together with transition-all - so this needs no Razor view edits and
   automatically covers any future card built the same way. Buttons/nav tabs
   are untouched; they use their own hover treatment above, not this lift. */
.hover\:shadow-sm:hover {
    transform: translateY(-2px);
}
/* Sidebar nav items (Overview / Department / System sections + the footer's
   Documentation & Collapse links) all share this one Tailwind hover class -
   nudge them a few px to the right on hover instead of lifting them, since a
   vertical lift would collide with the row above/below at this list's tight
   spacing (space-y-0.5 = 2px gaps). !important on the transition matches
   this file's existing convention (see border-zinc-200 above) and guarantees
   it isn't lost to Tailwind's own transition-colors utility, whichever
   stylesheet ends up later in the DOM. */
.hover\:bg-zinc-200\/60 {
    transition: background-color 150ms ease, color 150ms ease, transform 150ms ease !important;
}
.hover\:bg-zinc-200\/60:hover {
    transform: translateX(3px);
}
/* Sidebar collapse/expand animation (2026-09-15, several same-day revisions) - the collapse
   toggle used to just snap `sidebar.style.width` between 16rem/4rem instantly, which read as
   flat/lifeless. Round 1 tried a "back ease-out" bounce on width itself, which turned out to
   race against `_Layout.cshtml`'s own pre-existing `md:transition-[width]` declaration AND
   overshoot past the collapsed target wide enough to spill nav content past the edge - both
   fixed by dropping the bounce for a plain smooth glide (`_Layout.cshtml`'s own
   `md:transition-[width] md:duration-500`, Tailwind's default easing, no duplicate declaration
   here). Round 2 (this one) is about what happens to the nav LABELS during that glide.

   Original approach here was a JS-timed opacity fade (fade to 0 over ~180ms, THEN toggle
   `hidden` after a `setTimeout`) - independent of, and much FASTER than, the sidebar's own
   500ms width glide. That produced a visibly disjointed two-step feel (label snaps away
   quickly, sidebar keeps narrowing afterward on its own) instead of one continuous motion - sir
   monster bro caught this by sending an actual frame-by-frame screenshot sequence of the real
   running animation and said it still didn't match YouTube Studio's clean single-motion collapse
   (text visibly getting swallowed into the icon AS the sidebar narrows, not fading away on its
   own separate schedule first).

   Fix: give each label its OWN max-width/opacity transition, using the EXACT SAME duration and
   easing curve as the sidebar's own width transition, so both animate in perfect lockstep from
   the same trigger instant - no JS timing/timers needed at all anymore (removed a whole
   Map-based pending-timer mechanism from custom.js). `overflow: hidden` + `white-space: nowrap`
   is what makes the text look like it's genuinely being "swallowed" as max-width shrinks toward
   0, matching the reference feel much more closely than an independent fade did.
   `display: inline-block` is required for `max-width` to have any effect at all on a `<span>`
   (plain inline elements ignore width/max-width entirely) - confirmed safe for every current
   `[data-sidebar-label]` element (a mix of `<span>`/`<p>`/a rounded notification-count badge):
   flex-item children get "blockified" regardless of their own display value per the CSS spec,
   and no two `[data-sidebar-label]` elements ever sit as bare adjacent siblings outside a flex
   parent, so nothing was at risk of suddenly flowing onto the same line. */
[data-sidebar-collapse-icon] {
    transition: transform 500ms cubic-bezier(0.4, 0, 0.2, 1);
}
[data-sidebar-label] {
    display: inline-block;
    max-width: 12rem;
    /* Flex items default to `min-width: auto`, which means "never shrink below your own
       content's natural width" - so WITHOUT this, the label refused to shrink past however
       wide "Human Resources"/"Administrative"/etc. naturally is, no matter what the max-width
       transition above said, and only the aside's own `overflow: hidden` was left to hide the
       difference - abruptly, not smoothly, and only for the ROWS whose text happened to be
       long enough to still overflow the shrinking row at that instant. Shorter labels (already
       narrower than the shrinking row) never hit that wall, so they kept truncating smoothly
       via max-width the whole time - which is exactly the "some rows truncate nicely, some just
       vanish early" unevenness sir monster bro caught in his frame-by-frame screenshots. With
       `min-width: 0`, flexbox is free to shrink the label below its content size every single
       frame to whatever the row ACTUALLY has left (its real-time available space, driven by the
       aside's own width transition) - so it's never wider than what fits, regardless of how the
       max-width transition above is separately pacing itself. Both mechanisms now cooperate:
       max-width still drives the nice half-word "swallow" look while there's room for it, and
       min-width:0 is the real-time backstop that guarantees nothing can ever overflow. */
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    vertical-align: middle;
    opacity: 1;
    transition: max-width 500ms cubic-bezier(0.4, 0, 0.2, 1), opacity 500ms cubic-bezier(0.4, 0, 0.2, 1);
}
[data-sidebar-label].sidebar-label-collapsed {
    max-width: 0;
    opacity: 0;
}
/* Section headers ("OVERVIEW"/"DEPARTMENT"/"SYSTEM") sit alone on their own full-width line -
   never next to an icon - so there's no "swallow into the icon" moment worth animating for
   them. Animating their width anyway left a dead empty gap in the fully-collapsed icon list:
   they still reserved their own line-height + `mb-1.5` margin even at max-width:0/opacity:0,
   since only WIDTH/opacity were ever touched, never their presence in the layout - sir monster
   bro caught this as uneven vertical spacing between icon groups once fully collapsed. These go
   back to a plain instant hide/show instead (matching how every OTHER "-init" state in this app
   already works, and how these specifically behaved before this whole animation feature) -
   clean and gap-free, no in-between state to get wrong. */
p[data-sidebar-label] {
    max-width: none;
    min-width: 0;
    overflow: visible;
    transition: none;
}
p[data-sidebar-label].sidebar-label-collapsed {
    display: none;
    max-width: none;
    opacity: 1;
}

/* Tab bars - the top Overview/System tab strip in the layout AND every
   module's _SubTabs partial (RMS, ATS, DTR, Deposit Request, CAPA Tracker,
   Purchase Order, Biometrics, SpeedTrack, Tools, ...) - all build their
   inactive tabs from this same class combo. Small upward nudge plus the
   bottom border previewing in, so hovering hints "this is about to become
   the active tab" before you even click. */
.border-transparent.hover\:text-zinc-700 {
    transition: color 150ms ease, border-color 150ms ease, transform 150ms ease !important;
}
.border-transparent.hover\:text-zinc-700:hover {
    border-color: #d4d4d8; /* zinc-300 */
    transform: translateY(-1px);
}
/* Non-clickable KPI/stat tiles (Dashboard "Active in Pipeline", CAPA Tracker's
   NC-count cards, RMS/SpeedTrack summary tiles, etc.) - same base classes as
   the clickable cards above, so :has() picks out just the ones holding a big
   stat number (p.text-2xl) to avoid also lifting full content panels that
   happen to share the same box styling (e.g. the Pipeline Funnel panel).
   Lift only, no border-color change - these aren't links, so they shouldn't
   borrow the clickable cards' "about to navigate" signal. Needs a browser
   with :has() support (all current Edge/Chrome/Firefox) - silently does
   nothing extra on anything older, never breaks the layout. */
div.rounded-xl.border-zinc-200.bg-white.p-5:has(p.text-2xl) {
    transition: transform 150ms ease, box-shadow 150ms ease;
}
div.rounded-xl.border-zinc-200.bg-white.p-5:has(p.text-2xl):hover {
    transform: translateY(-2px);
    box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
}
/* Table row hover tint - every data table row that doesn't already define its
   own hover treatment gets the same subtle "which row is my mouse on"
   highlight the app already uses in a few tables like ATS's Stuck-in-Stage
   list - applied automatically instead of needing hover:bg-zinc-50/60 typed
   on every <tr>. CAPA Tracker's NC Report table is excluded here (its rows
   are grouped into one <tbody class="capa-nc-group"> per report via rowspan,
   with its own group-wide hover rule in Views/CapaTracker/Index.cshtml +
   dark-mode.css - that one got a visibility bump instead, see there). */
tbody:not(.capa-nc-group) tr:not([class*="hover:bg"]) {
    transition: background-color 150ms ease;
}
tbody:not(.capa-nc-group) tr:not([class*="hover:bg"]) td:first-child {
    transition: box-shadow 150ms ease;
}
tbody:not(.capa-nc-group) tr:not([class*="hover:bg"]):hover {
    /* Third pass at this value - a white tint (invisible on white), then a
       dark zinc tint, then sky-blue @ 12% - all still too subtle for sir
       monster bro to actually see. Nearly doubled the opacity here, AND
       added a solid (non-transparent) accent stripe below on the row's first
       cell as a second, independent cue - a solid color doesn't rely on alpha
       blending being perceptible at all, so even if the tint itself is still
       hard to see on some display/connection, the stripe won't be. */
    background-color: rgba(14, 165, 233, 0.22); /* sky-500 @ 22%, was 12% */
}
tbody:not(.capa-nc-group) tr:not([class*="hover:bg"]):hover td:first-child {
    box-shadow: inset 3px 0 0 0 #0ea5e9; /* solid sky-500 left accent bar */
}
/* Status badges/pills/chips app-wide (Done/Overdue, ARCHIVED, NEEDS PUSH,
   department Active/Inactive, DTR/RMS/ATS status labels, CAPA's assigned-
   person chips, ...) - a small hover "pop" so scanning a busy table feels
   alive, same 150ms language as everything else here. Scoped to rounded-full
   elements that also carry horizontal padding (px-*) - only real text
   pills use that combo; avatar circles, status dots, progress-bar tracks
   and toggle switches are rounded-full too but have no px-*, so those are
   untouched. Purely decorative - most of these badges aren't clickable, and
   that's fine, it's the same "mouse passed over this" feedback as the row
   tint above, not a click affordance. */
[class*="rounded-full"][class*="px-"] {
    transition: transform 150ms ease;
}
[class*="rounded-full"][class*="px-"]:hover {
    transform: scale(1.08);
}