/* ============================================================================
   Clinic Care — Motion System
   ----------------------------------------------------------------------------
   Loaded AFTER output.css / main.css. Everything here is unlayered CSS, so it
   wins over Tailwind's @layer utilities on equal specificity — keep selectors
   tight and intentional.

   The whole system is tuned LOUD on purpose: big travel distances, blur-in,
   springy overshoot, and every hover answers with several things at once
   (movement + colour + an icon reaction + a highlight). Subtle was the old
   default and it read as static.

   Conventions
     .cc-in-*        one-shot entrance animation, plays on every page load
     .cc-stagger     parent whose direct children cascade in
     .cc-reveal      entrance deferred until scrolled into view (motion.js)
     .cc-title       page heading: tracking + blur settle
     .cc-nav-pill    horizontal nav link (navbar quick links, section tabs)
     .cc-dropdown    Flowbite/Popper dropdown panel — pops from its anchor
     .cc-dd-toggle   button that opens a .cc-dropdown
     .cc-link        inline text link with a drawing underline
     .cc-page-link   pagination button
     .cc-ping        status dot that radiates
     .cc-sticky-head sticky header that gains a shadow once stuck (scroll-driven)
     .cc-loading     htmx target mid-request (motion.js)
     .cc-busy        element whose request is in flight (motion.js)
     .cc-ripple      pointer-down ripple (motion.js injects it)
     html.cc-js      scripting is alive — gates anything that starts hidden
     html.cc-leaving a full-page navigation has started (motion.js)

   Every entrance that blurs ends on `filter: none`, never `blur(0)`: a
   non-none filter creates a stacking context and would trap dropdowns that
   live inside the animated container.

   Entrances use `animation-fill-mode: backwards`, not `both`: they end at
   the element's natural state anyway, and a `both`/`forwards` fill keeps
   overriding `transform` forever — which silently disables every hover
   lift on that element. Only animations whose end state differs from the
   natural one (focus ring, ticks, scroll-stuck header) hold `both`.
   ============================================================================ */

:root {
    /* Durations */
    --cc-d-xs: 140ms;
    --cc-d-sm: 240ms;
    --cc-d-md: 460ms;
    --cc-d-lg: 720ms;
    --cc-d-xl: 1000ms;

    /* Easings */
    --cc-ease: cubic-bezier(0.16, 1, 0.3, 1);
    /* expo-out, snappy    */
    --cc-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
    /* overshoot           */
    --cc-bounce: cubic-bezier(0.22, 1.9, 0.36, 1);
    /* hard overshoot      */
    --cc-smooth: cubic-bezier(0.65, 0, 0.35, 1);
    /* symmetrical         */
    --cc-back: cubic-bezier(0.68, -0.35, 0.27, 1.35);
    /* anticipate + over   */

    /* Cascade step between siblings */
    --cc-stagger-step: 80ms;

    /* Shadow recipes */
    --cc-shadow-pop: var(--shadow-pop, 0 4px 6px -2px rgb(15 35 41 / 0.06), 0 12px 32px -8px rgb(15 35 41 / 0.14));
    --cc-shadow-float: 0 18px 40px -14px rgb(15 35 41 / 0.32), 0 6px 14px -6px rgb(15 35 41 / 0.12);
    --cc-glow: 0 0 0 4px color-mix(in oklab, var(--color-primary-500) 22%, transparent);
}

/* ============================================================================
   0. Reduced motion — one switch disables the whole system
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {

    *,
    *::before,
    *::after {
        animation-duration: 1ms !important;
        animation-delay: 0ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 1ms !important;
        transition-delay: 0ms !important;
        scroll-behavior: auto !important;
    }

    .cc-reveal {
        opacity: 1 !important;
        transform: none !important;
        filter: none !important;
    }

    .cc-card::before,
    .cc-ripple {
        display: none !important;
    }

    /* The universal selector above can't reach view-transition pseudos */
    ::view-transition-group(*),
    ::view-transition-old(*),
    ::view-transition-new(*) {
        animation: none !important;
    }
}

/* ============================================================================
   1. Keyframes
   ========================================================================== */
@keyframes cc-fade-up {
    from {
        opacity: 0;
        transform: translate3d(0, 44px, 0);
        filter: blur(8px);
    }

    to {
        opacity: 1;
        transform: none;
        filter: none;
    }
}

@keyframes cc-fade-down {
    from {
        opacity: 0;
        transform: translate3d(0, -36px, 0);
        filter: blur(8px);
    }

    to {
        opacity: 1;
        transform: none;
        filter: none;
    }
}

@keyframes cc-fade-left {
    from {
        opacity: 0;
        transform: translate3d(-56px, 0, 0);
        filter: blur(8px);
    }

    to {
        opacity: 1;
        transform: none;
        filter: none;
    }
}

@keyframes cc-fade-right {
    from {
        opacity: 0;
        transform: translate3d(56px, 0, 0);
        filter: blur(8px);
    }

    to {
        opacity: 1;
        transform: none;
        filter: none;
    }
}

@keyframes cc-fade-in {
    from {
        opacity: 0;
        filter: blur(10px);
    }

    to {
        opacity: 1;
        filter: none;
    }
}

@keyframes cc-pop-in {
    0% {
        opacity: 0;
        transform: scale(0.4);
    }

    55% {
        opacity: 1;
        transform: scale(1.18);
    }

    75% {
        transform: scale(0.94);
    }

    100% {
        opacity: 1;
        transform: scale(1);
    }
}

@keyframes cc-rise {
    from {
        opacity: 0;
        transform: translate3d(0, 56px, 0) scale(0.9);
        filter: blur(10px);
    }

    to {
        opacity: 1;
        transform: none;
        filter: none;
    }
}

/* Page heading: letters spread and blurred, then snap into place */
@keyframes cc-title-in {
    from {
        opacity: 0;
        transform: translate3d(-40px, 0, 0);
        letter-spacing: 0.14em;
        filter: blur(10px);
    }

    to {
        opacity: 1;
        transform: none;
        letter-spacing: -0.025em;
        filter: none;
    }
}

/* Row cascade — rows deal in from the leading edge so a long table reads as
   a wipe of data streaming in rather than a block that simply appears. */
@keyframes cc-row-in {
    0% {
        opacity: 0;
        transform: translate3d(-110px, 0, 0);
    }

    55% {
        opacity: 1;
    }

    100% {
        opacity: 1;
        transform: none;
    }
}

/* Each row glows teal as it lands. Deliberately no fill-mode: once this
   finishes the row must fall back to its own background, or flagged rows
   (.cc-row-warn / -danger / -notice) would lose their tint. */
@keyframes cc-row-land {
    0% {
        background-color: color-mix(in oklab, var(--color-primary-500) 45%, transparent);
    }

    100% {
        background-color: transparent;
    }
}

/* A deleted row slides off to the right and flushes red on its way out */
@keyframes cc-row-out {
    0% {
        opacity: 1;
        transform: none;
    }

    100% {
        opacity: 0;
        transform: translate3d(96px, 0, 0);
        background-color: color-mix(in oklab, var(--color-red-500) 30%, transparent);
    }
}

/* Header cells sweep in left-to-right ahead of the rows */
@keyframes cc-th-in {
    from {
        opacity: 0;
        transform: translate3d(0, -26px, 0);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

/* Attention flash for a row that was just created or edited: two pulses */
@keyframes cc-flash {
    0% {
        background-color: color-mix(in oklab, var(--color-primary-500) 55%, transparent);
        box-shadow: inset 6px 0 0 0 var(--color-primary-500);
    }

    35% {
        background-color: color-mix(in oklab, var(--color-primary-500) 18%, transparent);
    }

    55% {
        background-color: color-mix(in oklab, var(--color-primary-500) 45%, transparent);
        box-shadow: inset 6px 0 0 0 var(--color-primary-500);
    }

    100% {
        background-color: transparent;
        box-shadow: inset 6px 0 0 0 transparent;
    }
}

/* Specular sweep across buttons and cards on hover */
@keyframes cc-shine {
    from {
        transform: translateX(-180%) skewX(-18deg);
    }

    to {
        transform: translateX(280%) skewX(-18deg);
    }
}

@keyframes cc-sheen {
    from {
        background-position: 200% 0;
    }

    to {
        background-position: -100% 0;
    }
}

@keyframes cc-shake {

    0%,
    100% {
        transform: translateX(0);
    }

    15% {
        transform: translateX(-12px);
    }

    30% {
        transform: translateX(10px);
    }

    45% {
        transform: translateX(-7px);
    }

    60% {
        transform: translateX(5px);
    }

    80% {
        transform: translateX(-3px);
    }
}

/* Icon wobble — animates `rotate` only, so it composes with whatever
   `transform: scale()` the hover rule applies at the same time */
@keyframes cc-wiggle {

    0%,
    100% {
        rotate: 0deg;
    }

    20% {
        rotate: -16deg;
    }

    40% {
        rotate: 13deg;
    }

    60% {
        rotate: -9deg;
    }

    80% {
        rotate: 5deg;
    }
}

/* Caret bob: 4px, small enough to stay inside the caret's own slot */
@keyframes cc-nudge-x {

    0%,
    100% {
        translate: 0 0;
    }

    50% {
        translate: 4px 0;
    }
}

/* Chevron bob on collapsible groups and dropdown toggles */
@keyframes cc-nudge-y {

    0%,
    100% {
        translate: 0 0;
    }

    50% {
        translate: 0 4px;
    }
}

/* Checkmark stroke draw */
@keyframes cc-draw {
    from {
        stroke-dashoffset: 32;
    }

    to {
        stroke-dashoffset: 0;
    }
}

@keyframes cc-progress-grow {
    from {
        width: 0%;
    }

    to {
        width: 88%;
    }
}

@keyframes cc-progress-shimmer {
    from {
        background-position: 200% 0;
    }

    to {
        background-position: -200% 0;
    }
}

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

/* Expanding ring in the element's own colour */
@keyframes cc-ring-pulse {
    0% {
        box-shadow: 0 0 0 0 color-mix(in oklab, currentColor 65%, transparent);
    }

    70% {
        box-shadow: 0 0 0 16px transparent;
    }

    100% {
        box-shadow: 0 0 0 0 transparent;
    }
}

/* Radar ping behind a status dot */
@keyframes cc-ping {
    0% {
        transform: scale(1);
        opacity: 0.9;
    }

    80%,
    100% {
        transform: scale(3.4);
        opacity: 0;
    }
}

/* Pointer-down ripple */
@keyframes cc-ripple {
    from {
        transform: scale(0);
        opacity: 1;
    }

    to {
        transform: scale(1);
        opacity: 0;
    }
}

/* Keyboard focus ring snaps out then settles */
@keyframes cc-focus-pop {
    0% {
        box-shadow: 0 0 0 0 color-mix(in oklab, var(--color-primary-500) 70%, transparent);
    }

    100% {
        box-shadow: 0 0 0 5px color-mix(in oklab, var(--color-primary-500) 28%, transparent);
    }
}

@keyframes cc-float {

    0%,
    100% {
        transform: translateY(0) rotate(0deg);
    }

    50% {
        transform: translateY(-14px) rotate(-5deg);
    }
}

@keyframes cc-count-blur {
    from {
        filter: blur(10px);
        opacity: 0.2;
        transform: scale(0.7);
    }

    to {
        filter: none;
        opacity: 1;
        transform: scale(1);
    }
}

/* The counter lands: a quick bump once the final value is reached */
@keyframes cc-count-land {
    0% {
        transform: scale(1);
    }

    40% {
        transform: scale(1.22);
    }

    100% {
        transform: scale(1);
    }
}

@keyframes cc-busy {

    0%,
    100% {
        opacity: 1;
    }

    50% {
        opacity: 0.45;
    }
}

/* Active nav rail breathes */
@keyframes cc-rail-glow {

    0%,
    100% {
        box-shadow: 0 0 8px color-mix(in oklab, var(--color-primary-500) 60%, transparent);
    }

    50% {
        box-shadow: 0 0 18px color-mix(in oklab, var(--color-primary-500) 100%, transparent);
    }
}

/* ============================================================================
   2. Entrance utilities
   ========================================================================== */
.cc-in-up {
    animation: cc-fade-up var(--cc-d-lg) var(--cc-ease) backwards;
}

.cc-in-down {
    animation: cc-fade-down var(--cc-d-md) var(--cc-ease) backwards;
}

.cc-in-left {
    animation: cc-fade-left var(--cc-d-lg) var(--cc-ease) backwards;
}

.cc-in-right {
    animation: cc-fade-right var(--cc-d-lg) var(--cc-ease) backwards;
}

.cc-in-fade {
    animation: cc-fade-in var(--cc-d-lg) ease backwards;
}

.cc-in-pop {
    animation: cc-pop-in var(--cc-d-lg) var(--cc-bounce) backwards;
}

.cc-in-rise {
    animation: cc-rise var(--cc-d-lg) var(--cc-ease) backwards;
}

.cc-title {
    animation: cc-title-in 800ms var(--cc-ease) backwards;
}

/* Hand-tuned offsets so the page reads top-to-bottom */
.cc-delay-1 {
    animation-delay: 90ms;
}

.cc-delay-2 {
    animation-delay: 180ms;
}

.cc-delay-3 {
    animation-delay: 280ms;
}

.cc-delay-4 {
    animation-delay: 380ms;
}

/* Generic cascade: children rise one after another.
   motion.js sets --i on children beyond the 12 covered here. */
.cc-stagger>* {
    animation: cc-rise var(--cc-d-lg) var(--cc-ease) backwards;
    animation-delay: calc(var(--i, 0) * var(--cc-stagger-step));
}

.cc-stagger>*:nth-child(1) {
    --i: 0;
}

.cc-stagger>*:nth-child(2) {
    --i: 1;
}

.cc-stagger>*:nth-child(3) {
    --i: 2;
}

.cc-stagger>*:nth-child(4) {
    --i: 3;
}

.cc-stagger>*:nth-child(5) {
    --i: 4;
}

.cc-stagger>*:nth-child(6) {
    --i: 5;
}

.cc-stagger>*:nth-child(7) {
    --i: 6;
}

.cc-stagger>*:nth-child(8) {
    --i: 7;
}

.cc-stagger>*:nth-child(9) {
    --i: 8;
}

.cc-stagger>*:nth-child(10) {
    --i: 9;
}

.cc-stagger>*:nth-child(11) {
    --i: 10;
}

.cc-stagger>*:nth-child(12) {
    --i: 11;
}

/* Scroll-triggered reveal (motion.js flips .cc-revealed).
   Gated on html.cc-js so that if scripting is off or motion.js fails to
   load, the content is simply visible instead of stuck at opacity 0. */
html.cc-js .cc-reveal {
    opacity: 0;
    transform: translate3d(0, 64px, 0) scale(0.93);
    filter: blur(10px);
    transition:
        opacity var(--cc-d-lg) var(--cc-ease),
        transform var(--cc-d-lg) var(--cc-ease),
        filter var(--cc-d-lg) var(--cc-ease);
    transition-delay: calc(var(--i, 0) * var(--cc-stagger-step));
}

html.cc-js .cc-reveal.cc-revealed {
    opacity: 1;
    transform: none;
    filter: none;
}

/* ============================================================================
   3. App shell — sidebar & navbar
   ----------------------------------------------------------------------------
   The shell replays its intro on every page load, not once per session. It's
   the most-looked-at surface in the app, so it's what sells the "alive" feel;
   holding it still made navigation read as a static page swap.
   ========================================================================== */
#app-navbar {
    animation: cc-navbar-in 560ms var(--cc-ease) backwards;
}

@keyframes cc-navbar-in {
    from {
        opacity: 0;
        transform: translate3d(0, -100%, 0);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

#drawer-navigation {
    animation: cc-sidebar-in 640ms var(--cc-ease) backwards;
}

@keyframes cc-sidebar-in {
    from {
        opacity: 0;
        transform: translate3d(-100%, 0, 0);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

/* Brand mark drops in and settles with a bounce */
#drawer-navigation>div>a:first-child {
    animation: cc-fade-down 640ms var(--cc-bounce) 120ms backwards;
}

/* Section labels and nav items deal in from the left, one after another.
   --g is the group index, set on each direct child of <nav>; because custom
   properties inherit, the <ul>'s value reaches its <li>s, where --n adds the
   item's own offset. Doing this in CSS rather than JS keeps the delays
   correct before first paint — a deferred script would set them after the
   animations had already started and the items would visibly jump. */
#drawer-navigation .cc-nav-section,
#drawer-navigation nav>ul>li {
    animation: cc-nav-deal 620ms var(--cc-spring) backwards;
    animation-delay: calc(160ms + var(--g, 0) * 70ms + var(--n, 0) * 55ms);
}

@keyframes cc-nav-deal {
    from {
        opacity: 0;
        transform: translate3d(-64px, 0, 0);
        filter: blur(6px);
    }

    to {
        opacity: 1;
        transform: none;
        filter: none;
    }
}

/* Group index — nav children alternate: label, <ul>, label, <ul>… */
#drawer-navigation nav>*:nth-child(1) {
    --g: 0;
}

#drawer-navigation nav>*:nth-child(2) {
    --g: 1;
}

#drawer-navigation nav>*:nth-child(3) {
    --g: 2;
}

#drawer-navigation nav>*:nth-child(4) {
    --g: 3;
}

#drawer-navigation nav>*:nth-child(5) {
    --g: 4;
}

#drawer-navigation nav>*:nth-child(6) {
    --g: 5;
}

#drawer-navigation nav>*:nth-child(7) {
    --g: 6;
}

#drawer-navigation nav>*:nth-child(8) {
    --g: 7;
}

/* Item index within its group */
#drawer-navigation nav>ul>li:nth-child(1) {
    --n: 0;
}

#drawer-navigation nav>ul>li:nth-child(2) {
    --n: 1;
}

#drawer-navigation nav>ul>li:nth-child(3) {
    --n: 2;
}

#drawer-navigation nav>ul>li:nth-child(4) {
    --n: 3;
}

#drawer-navigation nav>ul>li:nth-child(5) {
    --n: 4;
}

#drawer-navigation nav>ul>li:nth-child(6) {
    --n: 5;
}

/* Sub-menu items must not inherit --n from their parent <li> and re-delay */
#drawer-navigation nav ul ul>li {
    --n: 0;
}

/* Footer button */
#drawer-navigation>div>div:last-child {
    animation: cc-fade-up 560ms var(--cc-ease) 620ms backwards;
}

/* The main content rises into place slowly, overshoots upward, then settles
   back down. The overshoot stays clear of the fixed navbar: the gap
   above the first content is the wrapper's padding (p-3 = 12px, lg:p-5 =
   20px). The navbar is translucent with a blurred backdrop, so the beat
   leaves a clear margin under it rather than just a few px. Transform and opacity
   only, no filter, so nothing inside gets a new stacking context once it
   ends. */
#app-main {
    --cc-main-beat: -6px;
    animation: cc-main-rise 1300ms backwards;
}

@media (min-width: 64rem) {
    #app-main {
        --cc-main-beat: -11px;
    }
}

@keyframes cc-main-rise {
    0% {
        opacity: 0;
        transform: translate3d(0, 40px, 0);
        animation-timing-function: cubic-bezier(0.33, 1, 0.68, 1);
    }

    65% {
        opacity: 1;
        transform: translate3d(0, var(--cc-main-beat), 0);
        animation-timing-function: cubic-bezier(0.45, 0, 0.55, 1);
    }

    100% {
        transform: none;
    }
}

/* Collapsing / expanding the desktop sidebar (main.css sets the states) */
#drawer-navigation {
    transition:
        translate 520ms var(--cc-ease),
        transform 520ms var(--cc-ease);
}

#app-navbar {
    transition: left 520ms var(--cc-ease);
}

#navbar-quick-links,
.sidebar-hidden #navbar-brand {
    transition-duration: 520ms;
    transition-timing-function: var(--cc-ease);
}

/* --- Nav items: filled hover, sliding rail, travelling icon, chevron -----
   The tinted fill is a background-image whose width animates, not a
   pseudo-element: `.cc-nav-item` sets no z-index, so a `z-index:-1` layer
   would sit behind the element's own background and never be seen.
   isolation + overflow make the item a ripple host (see section 6). */
.cc-nav-item {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    background-image: linear-gradient(90deg,
            color-mix(in oklab, var(--color-primary-500) 30%, transparent),
            color-mix(in oklab, var(--color-primary-500) 6%, transparent));
    background-repeat: no-repeat;
    background-size: 0% 100%;
    transition:
        background-size 380ms var(--cc-ease),
        color 200ms ease,
        padding-left 360ms var(--cc-spring),
        transform 320ms var(--cc-spring),
        box-shadow 320ms ease;
}

.cc-nav-item:hover,
.cc-nav-item.active {
    background-size: 100% 100%;
}

/* Rail on the leading edge — thick, springy, and always visible on hover */
.cc-nav-item::before {
    content: "";
    position: absolute;
    left: 0;
    top: 50%;
    width: 5px;
    height: 0;
    border-radius: 0 5px 5px 0;
    background: var(--color-primary-500);
    transform: translateY(-50%);
    transition: height 360ms var(--cc-spring);
    z-index: 2;
}

.cc-nav-item:hover::before {
    height: 72%;
    box-shadow: 0 0 12px color-mix(in oklab, var(--color-primary-500) 80%, transparent);
}

.cc-nav-item.active::before {
    height: 84%;
    animation: cc-rail-glow 2.4s ease-in-out infinite;
}

/* Sidebar links: the icon and label slide right by growing the left
   padding, so the item's tinted box, its rail and its caret all stay flush
   with the nav — the item never moves toward the sidebar's right border and
   the scrollable area never grows a horizontal scrollbar. Top-level items
   carry px-3, sub-items pl-11. */
#drawer-navigation .cc-nav-item:hover {
    color: var(--color-primary-700);
}

#drawer-navigation nav>ul>li>.cc-nav-item:hover {
    padding-left: calc(0.75rem + 10px);
}

#drawer-navigation nav ul ul>li>.cc-nav-item:hover {
    padding-left: calc(2.75rem + 10px);
}

.dark #drawer-navigation .cc-nav-item:hover {
    color: var(--color-primary-300);
}

#drawer-navigation .cc-nav-item:active {
    transform: scale(0.97);
    transition-duration: 90ms;
}

/* A caret slides in at the trailing edge of every sidebar link and settles
   1rem inside it — one spring, no looping nudge, so it never creeps toward
   the border. */
#drawer-navigation a.cc-nav-item::after {
    content: "";
    position: absolute;
    right: 1rem;
    top: 50%;
    width: 0.5rem;
    height: 0.5rem;
    border-top: 2px solid currentColor;
    border-right: 2px solid currentColor;
    opacity: 0;
    transform: translate(-24px, -50%) rotate(45deg);
    transition:
        transform 520ms var(--cc-spring),
        opacity 260ms ease;
}

#drawer-navigation a.cc-nav-item:hover::after {
    opacity: 1;
    transform: translate(0, -50%) rotate(45deg);
    animation: cc-nudge-x 1s ease-in-out 520ms infinite;
}

/* Icon leads the movement: swells and wobbles */
.cc-nav-item svg {
    transition: transform 380ms var(--cc-spring);
    position: relative;
    z-index: 1;
}

.cc-nav-item:hover>svg:first-child {
    transform: scale(1.4);
    animation: cc-wiggle 700ms var(--cc-ease);
}

.cc-nav-item.active>svg:first-child {
    transform: scale(1.2);
}

/* Chevrons on collapsible groups */
.cc-nav-item svg.rotate-180 {
    transform: rotate(180deg);
}

.cc-nav-item:hover>svg.rotate-180 {
    transform: rotate(180deg) scale(1.35);
}

.cc-nav-item:hover>svg:last-child:not(:first-child) {
    transform: scale(1.35);
    animation: cc-nudge-y 700ms ease-in-out infinite;
}

/* Horizontal pills (navbar quick links, visit section tabs): no rail, an
   underline grows out from the centre and the pill lifts */
.cc-nav-pill::before {
    display: none;
}

.cc-nav-pill::after {
    content: "";
    position: absolute;
    left: 50%;
    bottom: 3px;
    width: 0;
    height: 2px;
    border-radius: 2px;
    background: var(--color-primary-500);
    transform: translateX(-50%);
    transition: width 320ms var(--cc-ease);
}

.cc-nav-pill:hover::after,
.cc-nav-pill.active::after {
    width: calc(100% - 1.5rem);
}

.cc-nav-pill:hover {
    transform: translateY(-3px);
    color: var(--color-primary-700);
}

.dark .cc-nav-pill:hover {
    color: var(--color-primary-300);
}

.cc-nav-pill:active {
    transform: translateY(0) scale(0.95);
    transition-duration: 90ms;
}

/* --- Collapsible sub-menus: real accordion -----------------------------
   Alpine's x-show only flips display, which can't be transitioned. Forcing
   display back on (with !important, so it beats the inline style) and
   driving max-height instead gives a genuine slide. motion.js measures each
   list and writes --cc-submenu-h so the collapse lands exactly on time. */
#drawer-navigation nav ul ul {
    display: block !important;
    overflow: hidden;
    max-height: 0;
    opacity: 0;
    margin-top: 0;
    /* visibility keeps collapsed links out of the tab order — display is
       forced on, so without it they'd stay focusable while invisible.
       Held until the collapse finishes so the links don't blink out early. */
    visibility: hidden;
    transition:
        max-height var(--cc-d-md) var(--cc-ease),
        opacity var(--cc-d-sm) ease,
        margin-top var(--cc-d-md) var(--cc-ease),
        visibility 0s linear var(--cc-d-md);
}

/* Open = Alpine has booted (x-cloak gone) and hasn't hidden it.
   Before Alpine boots everything stays collapsed, so nothing flashes. */
#drawer-navigation nav ul ul:not([x-cloak]):not([style*="display: none"]) {
    max-height: var(--cc-submenu-h, 26rem);
    opacity: 1;
    margin-top: 0.125rem;
    visibility: visible;
    transition:
        max-height var(--cc-d-md) var(--cc-ease),
        opacity var(--cc-d-sm) ease,
        margin-top var(--cc-d-md) var(--cc-ease),
        visibility 0s linear 0s;
}

/* Sub-links slide in from the parent as the group opens */
#drawer-navigation nav ul ul>li>a {
    transition:
        transform var(--cc-d-md) var(--cc-spring),
        opacity var(--cc-d-md) ease,
        padding-left 360ms var(--cc-spring),
        background-size 380ms var(--cc-ease),
        background-color var(--cc-d-sm) ease,
        color var(--cc-d-sm) ease;
    transition-delay: calc(var(--i, 0) * 50ms);
}

#drawer-navigation nav ul ul[x-cloak]>li>a,
#drawer-navigation nav ul ul[style*="display: none"]>li>a {
    transform: translateX(-32px);
    opacity: 0;
    transition-delay: 0ms;
}

/* --- Navbar controls ---------------------------------------------------- */
/* The grow/press effect lives on the button's content, never the button: a
   button that shrinks on :active slides out from under a pointer pressed near
   its edge, the mouseup lands outside and the click is lost. */
#app-navbar button {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    transition:
        background-color var(--cc-d-sm) ease,
        color var(--cc-d-sm) ease;
}

#app-navbar button > * {
    transition: scale 360ms var(--cc-spring);
}

#app-navbar button:not(#theme-toggle, #user-menu-button):hover > :not(.cc-ripple) {
    scale: 1.18;
}

#app-navbar button:not(#theme-toggle, #user-menu-button):active > :not(.cc-ripple) {
    scale: 0.86;
    transition-duration: 90ms;
}

/* Avatar: the halo needs room outside the button, so the ripple moves to the
   avatar itself (.cc-ripple-host) */
#app-navbar #user-menu-button {
    overflow: visible;
}

#user-menu-button .cc-avatar {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    transition:
        scale 360ms var(--cc-spring),
        background-color var(--cc-d-sm) ease,
        box-shadow var(--cc-d-md) ease;
}

#app-navbar #user-menu-button:hover > .cc-avatar,
#app-navbar #user-menu-button[aria-expanded="true"] > .cc-avatar {
    scale: 1.12;
    background-color: var(--color-primary-700);
    box-shadow:
        0 0 0 4px color-mix(in oklab, var(--color-primary-500) 28%, transparent),
        0 10px 24px -6px color-mix(in oklab, var(--color-primary-500) 80%, transparent);
}

#app-navbar #user-menu-button:active > .cc-avatar {
    scale: 0.9;
}

/* Theme toggle: the icon spins as it swaps */
#theme-toggle svg {
    transition: transform var(--cc-d-lg) var(--cc-back);
}

#theme-toggle:hover svg {
    transform: rotate(90deg) scale(1.25);
}

#theme-toggle:active svg {
    transform: rotate(270deg) scale(0.85);
}

/* Hamburger squashes sideways */
#app-navbar button[aria-controls="drawer-navigation"] svg {
    transition: transform 360ms var(--cc-spring), scale 360ms var(--cc-spring);
}

#app-navbar button[aria-controls="drawer-navigation"]:hover svg {
    transform: scaleX(1.2) scaleY(0.9);
}

/* Brand mark wobbles */
#navbar-brand img,
#drawer-navigation a img {
    transition: transform var(--cc-d-lg) var(--cc-spring);
}

#navbar-brand:hover img,
#drawer-navigation a:hover img {
    transform: scale(1.22);
    animation: cc-wiggle 800ms var(--cc-ease);
}

/* Dropdown panels (Flowbite + Popper toggle .hidden; the animation restarts
   every time display flips back on).
   Popper positions these with an inline `transform: translate(x, y)`. The
   individual `scale` / `rotate` properties are applied BEFORE that inline
   transform, so they would multiply Popper's offset too and the panel would
   start far from its anchor and slide into place. Only `translate` is
   additive, so the pop here is: an unfold from the anchor edge (clip-path),
   a spring drop (translate) and a blur settle — never scale. */
@keyframes cc-dropdown-in {
    from {
        opacity: 0;
        translate: 0 -18px;
        clip-path: inset(0 0 100% 0 round 0.75rem);
        filter: blur(6px);
    }

    45% {
        opacity: 1;
        clip-path: inset(0 0 0 0 round 0.75rem);
    }

    to {
        opacity: 1;
        translate: 0 0;
        clip-path: inset(0 0 0 0 round 0.75rem);
        filter: none;
    }
}

.cc-dropdown {
    animation: cc-dropdown-in 420ms var(--cc-spring) backwards;
}

/* Items inside cascade */
.cc-dropdown li {
    animation: cc-fade-left 380ms var(--cc-ease) backwards;
    animation-delay: calc(60ms + var(--i, 0) * 40ms);
}

.cc-dropdown li:nth-child(1) {
    --i: 0;
}

.cc-dropdown li:nth-child(2) {
    --i: 1;
}

.cc-dropdown li:nth-child(3) {
    --i: 2;
}

.cc-dropdown li:nth-child(4) {
    --i: 3;
}

.cc-dropdown li:nth-child(5) {
    --i: 4;
}

.cc-dropdown li:nth-child(6) {
    --i: 5;
}

.cc-dropdown li:nth-child(7) {
    --i: 6;
}

.cc-dropdown li:nth-child(n + 8) {
    --i: 7;
}

/* User menu (Alpine x-show + x-transition). It sits in normal absolute
   flow, no Popper, so it can really scale out of its top-right corner. */
.cc-menu {
    transform-origin: top right;
}

.cc-menu-enter {
    transition:
        opacity 260ms ease,
        scale 520ms var(--cc-bounce),
        translate 520ms var(--cc-spring),
        filter 380ms ease;
}

.cc-menu-leave {
    transition:
        opacity 160ms ease-in,
        scale 180ms ease-in,
        translate 180ms ease-in,
        filter 160ms ease-in;
}

.cc-menu-from {
    opacity: 0;
    scale: 0.55;
    translate: 6px -14px;
    filter: blur(6px);
}

.cc-menu > div,
.cc-menu li {
    animation: cc-fade-left 420ms var(--cc-ease) backwards;
    animation-delay: calc(90ms + var(--i, 0) * 55ms);
}

.cc-menu li:nth-child(1) { --i: 1; }
.cc-menu li:nth-child(2) { --i: 2; }
.cc-menu li:nth-child(3) { --i: 3; }
.cc-menu li:nth-child(n + 4) { --i: 4; }

.cc-menu-item {
    position: relative;
    display: block;
    width: 100%;
    padding: 0.55rem 1rem;
    text-align: left;
    cursor: pointer;
    transition:
        background-color var(--cc-d-sm) ease,
        padding-left var(--cc-d-md) var(--cc-spring),
        color var(--cc-d-sm) ease;
}

.cc-menu-item::before {
    content: "";
    position: absolute;
    left: 0.7rem;
    top: 50%;
    width: 0.4rem;
    height: 0.4rem;
    border-top: 2px solid currentColor;
    border-right: 2px solid currentColor;
    opacity: 0;
    transform: translate(-8px, -50%) rotate(45deg);
    transition: transform 300ms var(--cc-spring), opacity 200ms ease;
}

.cc-menu-item:hover,
.cc-menu-item:focus-visible {
    outline: none;
    padding-left: 2rem;
    color: var(--color-primary-700);
    background-color: var(--color-gray-100);
}

.cc-menu-item-danger:hover,
.cc-menu-item-danger:focus-visible {
    color: var(--color-red-600);
    background-color: color-mix(in oklab, var(--color-red-500) 8%, transparent);
}

.dark .cc-menu-item:hover,
.dark .cc-menu-item:focus-visible {
    color: var(--color-primary-300);
    background-color: var(--color-gray-700);
}

.dark .cc-menu-item-danger:hover,
.dark .cc-menu-item-danger:focus-visible {
    color: var(--color-red-400);
    background-color: color-mix(in oklab, var(--color-red-500) 14%, transparent);
}

.cc-menu-item:hover::before,
.cc-menu-item:focus-visible::before {
    opacity: 1;
    transform: translate(0, -50%) rotate(45deg);
}

/* ============================================================================
   4. Cards & surfaces
   ----------------------------------------------------------------------------
   Every card carries a spotlight that follows the pointer (motion.js writes
   --mx/--my) and glows at its border. Clickable cards also lift, scale and
   get a specular sweep.
   ========================================================================== */
.cc-card {
    --mx: 50%;
    --my: 50%;
    transition:
        box-shadow var(--cc-d-md) var(--cc-ease),
        transform var(--cc-d-md) var(--cc-ease),
        border-color var(--cc-d-sm) ease;
}

/* Spotlight needs a positioned box; leave alone cards that are positioned
   by their own utilities (the visit-template dropdown is absolute) */
.cc-card:not(.absolute):not(.fixed):not(.sticky) {
    position: relative;
}

.cc-card::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    opacity: 0;
    background: radial-gradient(460px circle at var(--mx) var(--my),
            color-mix(in oklab, var(--color-primary-500) 18%, transparent),
            transparent 62%);
    transition: opacity 400ms ease;
    z-index: 0;
}

.dark .cc-card::before {
    background: radial-gradient(460px circle at var(--mx) var(--my),
            color-mix(in oklab, var(--color-primary-400) 22%, transparent),
            transparent 62%);
}

.cc-card:hover::before {
    opacity: 1;
}

.cc-card:hover {
    border-color: color-mix(in oklab, var(--color-primary-500) 45%, transparent);
    box-shadow: var(--cc-shadow-pop);
}

/* A section being edited lights up */
.cc-card:focus-within {
    border-color: color-mix(in oklab, var(--color-primary-500) 60%, transparent);
    box-shadow:
        0 0 0 4px color-mix(in oklab, var(--color-primary-500) 12%, transparent),
        var(--cc-shadow-pop);
}

/* Only cards that are actually clickable lift */
a.cc-card:hover,
button.cc-card:hover {
    transform: translateY(-8px) scale(1.02);
    box-shadow:
        var(--cc-shadow-float),
        0 0 0 1px color-mix(in oklab, var(--color-primary-500) 35%, transparent);
    border-color: color-mix(in oklab, var(--color-primary-500) 55%, transparent);
}

a.cc-card:active,
button.cc-card:active {
    transform: translateY(-3px) scale(1);
    transition-duration: 90ms;
}

/* Specular sweep across clickable cards */
a.cc-card::after,
button.cc-card::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    background: linear-gradient(115deg,
            transparent 32%,
            rgb(255 255 255 / 0.55) 46%,
            transparent 60%);
    background-size: 260% 100%;
    background-position: 200% 0;
    opacity: 0;
}

.dark a.cc-card::after,
.dark button.cc-card::after {
    background-image: linear-gradient(115deg,
            transparent 32%,
            color-mix(in oklab, var(--color-primary-300) 28%, transparent) 46%,
            transparent 60%);
}

a.cc-card:hover::after,
button.cc-card:hover::after {
    opacity: 1;
    animation: cc-sheen 900ms var(--cc-ease);
}

/* KPI tiles: the icon chip swells, tilts and wobbles; the number grows */
a.cc-card>div:last-child {
    transition:
        transform var(--cc-d-lg) var(--cc-spring),
        background-color var(--cc-d-md) ease,
        box-shadow var(--cc-d-md) ease;
}

a.cc-card:hover>div:last-child {
    transform: scale(1.3) rotate(-12deg);
    box-shadow: 0 10px 24px -8px color-mix(in oklab, currentColor 55%, transparent);
}

a.cc-card:hover>div:last-child svg {
    animation: cc-wiggle 800ms var(--cc-ease);
}

a.cc-card [data-countup] {
    transform-origin: left center;
    transition: transform var(--cc-d-lg) var(--cc-spring);
}

a.cc-card:hover [data-countup] {
    transform: scale(1.12);
}

/* Section heading icons react to the section being hovered */
.cc-card h2>svg {
    transition: transform var(--cc-d-lg) var(--cc-spring);
}

.cc-card:hover h2>svg {
    transform: scale(1.4);
    animation: cc-wiggle 800ms var(--cc-ease);
}

/* Counter roll-in — motion.js drives the value, this softens the landing */
[data-countup] {
    display: inline-block;
    font-variant-numeric: tabular-nums;
}

[data-countup].cc-counting {
    animation: cc-count-blur 620ms var(--cc-ease) both;
}

[data-countup].cc-counted {
    animation: cc-count-land 520ms var(--cc-spring);
}

/* ============================================================================
   5. Tables
   ========================================================================== */

/* Rows cascade in whenever the tbody is (re)rendered — page load, filter
   change, pagination, or an HTMX list refresh.
   Longhands, not the `animation` shorthand: the per-row stagger below sets
   animation-delay on its own, and a later shorthand would reset it to 0. */
#object-list>tr,
tbody.cc-cascade>tr {
    animation-name: cc-row-in;
    animation-duration: 640ms;
    animation-timing-function: var(--cc-ease);
    animation-fill-mode: backwards;
}

/* A teal glow decays as each row lands, so a filling table reads as data
   streaming in. Flagged rows sit this out — animating their background
   would wipe the amber/red tint and then snap it back when it ended. */
#object-list>tr:not(.cc-row-warn):not(.cc-row-danger):not(.cc-row-notice),
tbody.cc-cascade>tr:not(.cc-row-warn):not(.cc-row-danger):not(.cc-row-notice) {
    animation-name: cc-row-in, cc-row-land;
    animation-duration: 640ms, 1200ms;
    animation-fill-mode: backwards, none;
}

#object-list>tr:nth-child(1),
tbody.cc-cascade>tr:nth-child(1) {
    animation-delay: 40ms;
}

#object-list>tr:nth-child(2),
tbody.cc-cascade>tr:nth-child(2) {
    animation-delay: 105ms;
}

#object-list>tr:nth-child(3),
tbody.cc-cascade>tr:nth-child(3) {
    animation-delay: 170ms;
}

#object-list>tr:nth-child(4),
tbody.cc-cascade>tr:nth-child(4) {
    animation-delay: 235ms;
}

#object-list>tr:nth-child(5),
tbody.cc-cascade>tr:nth-child(5) {
    animation-delay: 300ms;
}

#object-list>tr:nth-child(6),
tbody.cc-cascade>tr:nth-child(6) {
    animation-delay: 365ms;
}

#object-list>tr:nth-child(7),
tbody.cc-cascade>tr:nth-child(7) {
    animation-delay: 430ms;
}

#object-list>tr:nth-child(8),
tbody.cc-cascade>tr:nth-child(8) {
    animation-delay: 495ms;
}

#object-list>tr:nth-child(9),
tbody.cc-cascade>tr:nth-child(9) {
    animation-delay: 560ms;
}

#object-list>tr:nth-child(10),
tbody.cc-cascade>tr:nth-child(10) {
    animation-delay: 625ms;
}

#object-list>tr:nth-child(11),
tbody.cc-cascade>tr:nth-child(11) {
    animation-delay: 690ms;
}

#object-list>tr:nth-child(12),
tbody.cc-cascade>tr:nth-child(12) {
    animation-delay: 755ms;
}

#object-list>tr:nth-child(13),
tbody.cc-cascade>tr:nth-child(13) {
    animation-delay: 820ms;
}

#object-list>tr:nth-child(14),
tbody.cc-cascade>tr:nth-child(14) {
    animation-delay: 885ms;
}

#object-list>tr:nth-child(15),
tbody.cc-cascade>tr:nth-child(15) {
    animation-delay: 950ms;
}

#object-list>tr:nth-child(16),
tbody.cc-cascade>tr:nth-child(16) {
    animation-delay: 1015ms;
}

#object-list>tr:nth-child(17),
tbody.cc-cascade>tr:nth-child(17) {
    animation-delay: 1080ms;
}

#object-list>tr:nth-child(n + 18),
tbody.cc-cascade>tr:nth-child(n + 18) {
    animation-delay: 1140ms;
}

/* Header cells sweep in left-to-right, arriving just before the first row */
#object-table thead th,
table:has(tbody.cc-cascade) thead th {
    animation: cc-th-in 520ms var(--cc-spring) backwards;
}

#object-table thead th:nth-child(1),
table:has(tbody.cc-cascade) thead th:nth-child(1) {
    animation-delay: 0ms;
}

#object-table thead th:nth-child(2),
table:has(tbody.cc-cascade) thead th:nth-child(2) {
    animation-delay: 55ms;
}

#object-table thead th:nth-child(3),
table:has(tbody.cc-cascade) thead th:nth-child(3) {
    animation-delay: 110ms;
}

#object-table thead th:nth-child(4),
table:has(tbody.cc-cascade) thead th:nth-child(4) {
    animation-delay: 165ms;
}

#object-table thead th:nth-child(5),
table:has(tbody.cc-cascade) thead th:nth-child(5) {
    animation-delay: 220ms;
}

#object-table thead th:nth-child(6),
table:has(tbody.cc-cascade) thead th:nth-child(6) {
    animation-delay: 275ms;
}

#object-table thead th:nth-child(7),
table:has(tbody.cc-cascade) thead th:nth-child(7) {
    animation-delay: 330ms;
}

#object-table thead th:nth-child(8),
table:has(tbody.cc-cascade) thead th:nth-child(8) {
    animation-delay: 385ms;
}

#object-table thead th:nth-child(9),
table:has(tbody.cc-cascade) thead th:nth-child(9) {
    animation-delay: 440ms;
}

#object-table thead th:nth-child(10),
table:has(tbody.cc-cascade) thead th:nth-child(10) {
    animation-delay: 495ms;
}

/* Row hover: the row tints, grows a thick teal edge and casts a shadow while
   its cells slide right — the row box itself stays put, so nothing is
   clipped at the scroll container's right edge and the edge bar stays flush
   with the table. position stays on the base rule — flipping it on :hover
   would relayout the row rather than just restacking it. */
#object-list>tr,
tbody.cc-cascade>tr {
    position: relative;
    cursor: pointer;
    transition:
        background-color var(--cc-d-sm) ease,
        box-shadow var(--cc-d-md) var(--cc-ease),
        transform var(--cc-d-md) var(--cc-ease);
}

/* The empty-state placeholder isn't a record — leave its cursor alone */
#object-list>tr.cc-empty-row,
tbody.cc-cascade>tr.cc-empty-row {
    cursor: default;
}

#object-list>tr:hover,
tbody.cc-cascade>tr:hover {
    background-color: color-mix(in oklab, var(--color-primary-500) 14%, transparent);
    box-shadow:
        inset 6px 0 0 0 var(--color-primary-500),
        0 14px 30px -14px rgb(15 35 41 / 0.6);
    /* lift the hovered row above its neighbours' borders */
    z-index: 1;
}

/* Flagged rows keep their own coloured edge on hover */
#object-list>tr.cc-row-warn:hover,
tbody.cc-cascade>tr.cc-row-warn:hover {
    box-shadow:
        inset 6px 0 0 0 var(--color-amber-400),
        0 14px 30px -14px rgb(15 35 41 / 0.6);
    background-color: color-mix(in oklab, var(--color-amber-400) 24%, transparent);
}

#object-list>tr.cc-row-danger:hover,
tbody.cc-cascade>tr.cc-row-danger:hover {
    box-shadow:
        inset 6px 0 0 0 var(--color-red-500),
        0 14px 30px -14px rgb(15 35 41 / 0.6);
    background-color: color-mix(in oklab, var(--color-red-500) 20%, transparent);
}

#object-list>tr.cc-row-notice:hover,
tbody.cc-cascade>tr.cc-row-notice:hover {
    box-shadow:
        inset 6px 0 0 0 var(--color-orange-400),
        0 14px 30px -14px rgb(15 35 41 / 0.6);
    background-color: color-mix(in oklab, var(--color-orange-400) 24%, transparent);
}

/* The cells carry the movement — all but the last one. A transformed cell
   does extend the scroll container's overflow, so shifting the trailing
   cell would grow the scroll width and summon a horizontal scrollbar. */
#object-list>tr td,
tbody.cc-cascade>tr td {
    transition:
        transform 360ms var(--cc-ease),
        color var(--cc-d-sm) ease;
}

#object-list>tr:hover td:not(:last-child),
tbody.cc-cascade>tr:hover td:not(:last-child) {
    transform: translateX(14px);
}

/* The leading cell (usually the name) turns teal */
#object-list>tr:hover td:first-child,
tbody.cc-cascade>tr:hover td:first-child {
    color: var(--color-primary-700);
}

.dark #object-list>tr:hover td:first-child,
.dark tbody.cc-cascade>tr:hover td:first-child {
    color: var(--color-primary-300);
}

/* Row action buttons: dimmed until the row is hovered, then they brighten
   and spring up. Ripple hosts too. */
.cc-action-edit,
.cc-action-delete {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    transition:
        color var(--cc-d-sm) ease,
        background-color var(--cc-d-sm) ease,
        transform var(--cc-d-md) var(--cc-spring),
        opacity var(--cc-d-sm) ease,
        box-shadow var(--cc-d-md) ease;
}

#object-list>tr .cc-action-edit,
#object-list>tr .cc-action-delete,
tbody.cc-cascade>tr .cc-action-edit,
tbody.cc-cascade>tr .cc-action-delete {
    opacity: 0.62;
}

#object-list>tr:hover .cc-action-edit,
#object-list>tr:hover .cc-action-delete,
#object-list>tr:focus-within .cc-action-edit,
#object-list>tr:focus-within .cc-action-delete,
tbody.cc-cascade>tr:hover .cc-action-edit,
tbody.cc-cascade>tr:hover .cc-action-delete {
    opacity: 1;
    transform: translateY(-2px);
}

.cc-action-edit:hover,
.cc-action-delete:hover,
#object-list>tr:hover .cc-action-edit:hover,
#object-list>tr:hover .cc-action-delete:hover {
    transform: translateY(-4px) scale(1.15);
    box-shadow: 0 10px 20px -10px rgb(15 35 41 / 0.45);
}

.cc-action-edit:active,
.cc-action-delete:active,
#object-list>tr:hover .cc-action-edit:active,
#object-list>tr:hover .cc-action-delete:active {
    transform: translateY(0) scale(0.92);
    transition-duration: 80ms;
}

.cc-action-edit svg,
.cc-action-delete svg {
    transition: transform var(--cc-d-md) var(--cc-spring);
}

/* The bin rattles, the pencil scribbles */
.cc-action-delete:hover svg {
    transform: scale(1.3);
    animation: cc-wiggle 600ms var(--cc-ease);
}

.cc-action-edit:hover svg {
    transform: rotate(-15deg) scale(1.3);
}

/* Freshly inserted / updated row (motion.js tags it). The shorthand resets
   the cascade delay on purpose: the changed row appears first. */
tr.cc-flash {
    animation: cc-row-in 640ms var(--cc-ease) backwards, cc-flash 2s var(--cc-ease) backwards;
}

/* A row being deleted: htmx keeps it around for the swap delay set on the
   delete button and tags it .htmx-swapping meanwhile */
#object-list>tr.htmx-swapping,
tbody.cc-cascade>tr.htmx-swapping {
    animation: cc-row-out 360ms var(--cc-smooth) forwards;
    pointer-events: none;
}

/* Empty state bobs so the page never looks broken */
.cc-empty-row td,
.cc-empty-state {
    animation: cc-fade-up 520ms var(--cc-ease) backwards;
}

.cc-empty-row td svg,
.cc-empty-state svg {
    animation: cc-float 3s ease-in-out infinite;
    transition: transform var(--cc-d-lg) var(--cc-spring), color var(--cc-d-sm) ease;
}

.cc-empty-row:hover td svg,
.cc-empty-state:hover svg {
    color: var(--color-primary-500);
    animation: cc-wiggle 900ms var(--cc-ease);
    transform: scale(1.3);
}

/* ============================================================================
   6. Buttons & ripple
   ========================================================================== */
.cc-btn,
.cc-btn-primary,
.cc-btn-secondary,
.cc-btn-ghost,
.cc-btn-danger {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    transition:
        background-color var(--cc-d-sm) ease,
        color var(--cc-d-sm) ease,
        border-color var(--cc-d-sm) ease,
        box-shadow var(--cc-d-md) var(--cc-ease),
        transform 260ms var(--cc-spring);
}

.cc-btn-primary:hover {
    transform: translateY(-4px) scale(1.04);
    box-shadow:
        0 16px 32px -10px color-mix(in oklab, var(--color-primary-600) 95%, transparent),
        0 0 0 3px color-mix(in oklab, var(--color-primary-500) 22%, transparent);
}

.cc-btn-danger:hover {
    transform: translateY(-4px) scale(1.04);
    box-shadow:
        0 16px 32px -10px color-mix(in oklab, var(--color-red-600) 95%, transparent),
        0 0 0 3px color-mix(in oklab, var(--color-red-500) 22%, transparent);
}

.cc-btn-secondary:hover {
    transform: translateY(-4px) scale(1.03);
    border-color: var(--color-primary-400);
    color: var(--color-primary-700);
    box-shadow:
        0 14px 28px -12px rgb(15 35 41 / 0.35),
        0 0 0 3px color-mix(in oklab, var(--color-primary-500) 14%, transparent);
}

.dark .cc-btn-secondary:hover {
    color: var(--color-primary-300);
}

.cc-btn-ghost:hover {
    transform: scale(1.1);
}

.cc-btn-primary:active,
.cc-btn-secondary:active,
.cc-btn-danger:active,
.cc-btn-ghost:active {
    transform: translateY(0) scale(0.94);
    transition-duration: 80ms;
}

/* Specular sweep on primary and danger actions, a soft teal one on secondary */
.cc-btn-primary::after,
.cc-btn-danger::after,
.cc-btn-secondary::after {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    width: 45%;
    height: 100%;
    pointer-events: none;
    background: linear-gradient(90deg,
            transparent,
            rgb(255 255 255 / 0.55),
            transparent);
    transform: translateX(-180%) skewX(-18deg);
}

.cc-btn-secondary::after {
    background: linear-gradient(90deg,
            transparent,
            color-mix(in oklab, var(--color-primary-500) 22%, transparent),
            transparent);
}

.cc-btn-primary:hover::after,
.cc-btn-danger:hover::after,
.cc-btn-secondary:hover::after {
    animation: cc-shine 900ms var(--cc-ease);
}

/* Icons inside buttons animate independently of the label */
.cc-btn-primary svg,
.cc-btn-secondary svg,
.cc-btn-ghost svg,
.cc-btn-danger svg {
    transition: transform var(--cc-d-md) var(--cc-spring);
}

.cc-btn-primary:hover svg,
.cc-btn-secondary:hover svg,
.cc-btn-danger:hover svg,
.cc-btn-ghost:hover svg {
    transform: scale(1.3);
    animation: cc-wiggle 700ms var(--cc-ease);
}

/* "+" icons spin a half turn instead of wobbling — opt in via .cc-icon-plus */
.cc-btn-primary:hover svg.cc-icon-plus,
.cc-btn-secondary:hover svg.cc-icon-plus,
.cc-btn-danger:hover svg.cc-icon-plus,
a:hover>svg.cc-icon-plus,
button:hover>svg.cc-icon-plus {
    transform: rotate(180deg) scale(1.3);
    animation: none;
}

svg.cc-icon-plus {
    transition: transform var(--cc-d-lg) var(--cc-spring);
}

/* In-flight buttons (motion.js adds .cc-busy to the element that fired the
   request, or to the submit button of a form): dim, shrink, breathe */
.cc-busy {
    pointer-events: none;
    cursor: progress;
    animation: cc-busy 900ms ease-in-out infinite;
    transform: scale(0.96) !important;
    filter: saturate(0.6);
}

/* Keyboard focus snaps a ring out */
.cc-btn-primary:focus-visible,
.cc-btn-secondary:focus-visible,
.cc-btn-ghost:focus-visible,
.cc-btn-danger:focus-visible,
.cc-nav-item:focus-visible,
.cc-action-edit:focus-visible,
.cc-action-delete:focus-visible,
.cc-page-link:focus-visible,
.cc-dd-toggle:focus-visible {
    animation: cc-focus-pop 320ms var(--cc-ease) both;
}

/* Pointer-down ripple. motion.js drops a .cc-ripple span into the host at
   the pointer position; hosts are isolated so z-index:-1 sits above their
   own background but under their text. */
.cc-ripple {
    position: absolute;
    border-radius: 50%;
    pointer-events: none;
    z-index: -1;
    background: radial-gradient(circle,
            color-mix(in oklab, currentColor 40%, transparent) 0%,
            color-mix(in oklab, currentColor 20%, transparent) 45%,
            transparent 70%);
    transform: scale(0);
    animation: cc-ripple 720ms var(--cc-ease) forwards;
}

/* ============================================================================
   7. Badges, pills & status
   ========================================================================== */
.cc-badge,
.cc-badge-gray,
.cc-badge-teal,
.cc-badge-amber,
.cc-badge-red,
.cc-badge-green {
    transition:
        transform var(--cc-d-md) var(--cc-spring),
        box-shadow var(--cc-d-md) ease;
}

.cc-badge-gray:hover,
.cc-badge-teal:hover,
.cc-badge-amber:hover,
.cc-badge-red:hover,
.cc-badge-green:hover {
    transform: translateY(-3px) scale(1.15);
    box-shadow: 0 8px 18px -8px color-mix(in oklab, currentColor 70%, transparent);
}

/* Critical badges announce themselves when they appear: pop, then two ring
   pulses in their own red. Not infinite — a page full of alerts shouldn't
   strobe while someone is reading it. */
.cc-badge-red {
    animation:
        cc-pop-in 520ms var(--cc-bounce) backwards,
        cc-ring-pulse 1.5s var(--cc-ease) 520ms 2;
}

/* Amber badges pop too, without the pulse */
.cc-badge-amber {
    animation: cc-pop-in 520ms var(--cc-bounce) backwards;
}

/* Status dot that radiates (draft treatments) */
.cc-ping {
    position: relative;
}

.cc-ping::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background-color: inherit;
    animation: cc-ping 1.6s cubic-bezier(0, 0, 0.2, 1) infinite;
}

/* Autosave status (visit form): every new badge pops, a green one rings */
#save-status>span {
    animation: cc-pop-in 520ms var(--cc-bounce) backwards;
}

#save-status>span.cc-badge-green {
    animation:
        cc-pop-in 520ms var(--cc-bounce) backwards,
        cc-ring-pulse 1.4s var(--cc-ease) 480ms 1;
}

/* Checklist ticks draw themselves in and the circle pops */
.cc-check-draw path {
    stroke-dasharray: 32;
    animation: cc-draw 640ms var(--cc-ease) both;
    animation-delay: calc(220ms + var(--i, 0) * 110ms);
}

.cc-list-row .cc-avatar:has(.cc-check-draw) {
    animation: cc-pop-in 520ms var(--cc-bounce) backwards;
    animation-delay: calc(140ms + var(--i, 0) * 110ms);
}

/* Pending check circle pulses to draw the eye (its hover reaction lives
   with the list-row rules in section 14) */
.cc-pending-dot {
    color: var(--color-primary-500);
    animation: cc-ring-pulse 2.2s var(--cc-ease) infinite;
}

/* ============================================================================
   8. Forms
   ========================================================================== */
.cc-input,
input,
select,
textarea {
    transition:
        border-color var(--cc-d-sm) ease,
        box-shadow var(--cc-d-md) var(--cc-ease),
        background-color var(--cc-d-sm) ease,
        transform var(--cc-d-md) var(--cc-ease);
}

/* Fields answer the pointer before they're even focused */
.cc-input:hover:not(:focus):not(:disabled),
input:not([type="checkbox"]):not([type="radio"]):hover:not(:focus):not(:disabled),
select:hover:not(:focus):not(:disabled),
textarea:hover:not(:focus):not(:disabled) {
    border-color: var(--color-primary-400);
    box-shadow: 0 0 0 3px color-mix(in oklab, var(--color-primary-500) 12%, transparent);
    transform: translateY(-1px);
}

.cc-input:focus,
input:not([type="checkbox"]):not([type="radio"]):focus,
select:focus,
textarea:focus {
    transform: translateY(-2px);
    box-shadow:
        0 0 0 4px color-mix(in oklab, var(--color-primary-500) 22%, transparent),
        0 12px 26px -12px color-mix(in oklab, var(--color-primary-500) 55%, transparent);
}

/* The label brightens and steps forward while its field is focused */
.cc-label {
    transition:
        color var(--cc-d-sm) ease,
        transform var(--cc-d-md) var(--cc-spring);
    transform-origin: left center;
}

.cc-field:focus-within .cc-label {
    color: var(--color-primary-600);
    transform: translateX(6px) scale(1.06);
}

.dark .cc-field:focus-within .cc-label {
    color: var(--color-primary-400);
}

/* Server- or browser-flagged fields shake hard and stay tinted */
.cc-input.cc-invalid,
input.cc-invalid,
select.cc-invalid,
textarea.cc-invalid {
    animation: cc-shake 560ms var(--cc-smooth) backwards;
    border-color: var(--color-red-500);
    box-shadow: 0 0 0 4px color-mix(in oklab, var(--color-red-500) 18%, transparent);
}

/* Form errors drop into place */
.errorlist,
.cc-form-error {
    animation: cc-fade-down 420ms var(--cc-spring) backwards;
}

/* Checkbox / radio: swell on hover, pop and ring when ticked (the ring uses
   the control's own accent colour) */
input[type="checkbox"],
input[type="radio"] {
    transition:
        transform var(--cc-d-md) var(--cc-spring),
        background-color var(--cc-d-sm) ease,
        box-shadow var(--cc-d-md) ease;
}

input[type="checkbox"]:hover,
input[type="radio"]:hover {
    transform: scale(1.3);
}

input[type="checkbox"]:checked,
input[type="radio"]:checked {
    animation:
        cc-pop-in 420ms var(--cc-bounce),
        cc-ring-pulse 900ms var(--cc-ease) 1;
}

/* Choice tiles (labels wrapping a checkbox / radio) lift like buttons */
label:has(>input[type="checkbox"]),
label:has(>input[type="radio"]) {
    transition:
        transform var(--cc-d-md) var(--cc-spring),
        box-shadow var(--cc-d-md) ease,
        border-color var(--cc-d-sm) ease,
        background-color var(--cc-d-sm) ease;
}

label:has(>input[type="checkbox"]):hover,
label:has(>input[type="radio"]):hover {
    transform: translateY(-2px) translateX(3px);
    box-shadow: 0 10px 22px -12px rgb(15 35 41 / 0.4);
}

label:has(>input[type="checkbox"]):active,
label:has(>input[type="radio"]):active {
    transform: scale(0.97);
    transition-duration: 80ms;
}

/* Filter panel fields cascade in behind the panel itself
   (aside.cc-filter-panel > form > div.grid > field) */
.cc-filter-panel form>div>* {
    animation: cc-fade-right 520ms var(--cc-ease) backwards;
}

.cc-filter-panel form>div>*:nth-child(1) {
    animation-delay: 380ms;
}

.cc-filter-panel form>div>*:nth-child(2) {
    animation-delay: 440ms;
}

.cc-filter-panel form>div>*:nth-child(3) {
    animation-delay: 500ms;
}

.cc-filter-panel form>div>*:nth-child(4) {
    animation-delay: 560ms;
}

.cc-filter-panel form>div>*:nth-child(5) {
    animation-delay: 620ms;
}

.cc-filter-panel form>div>*:nth-child(6) {
    animation-delay: 680ms;
}

.cc-filter-panel form>div>*:nth-child(7) {
    animation-delay: 740ms;
}

.cc-filter-panel form>div>*:nth-child(n + 8) {
    animation-delay: 800ms;
}

/* The search magnifier tints and swells while its field is focused */
.cc-filter-panel .relative>div:first-child svg {
    transition: transform var(--cc-d-md) var(--cc-spring), color var(--cc-d-sm) ease;
}

.cc-filter-panel .relative:focus-within>div:first-child svg {
    transform: scale(1.35) rotate(-12deg);
    color: var(--color-primary-500);
}

/* Filter dropdown toggles: lift, tint, and the chevron bobs / flips */
.cc-dd-toggle {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    transition:
        transform var(--cc-d-md) var(--cc-spring),
        box-shadow var(--cc-d-md) ease,
        border-color var(--cc-d-sm) ease,
        background-color var(--cc-d-sm) ease,
        color var(--cc-d-sm) ease;
}

.cc-dd-toggle:hover {
    transform: translateY(-3px);
    border-color: var(--color-primary-400);
    box-shadow: 0 12px 24px -12px rgb(15 35 41 / 0.35);
}

.cc-dd-toggle:active {
    transform: translateY(0) scale(0.97);
    transition-duration: 80ms;
}

.cc-dd-toggle svg {
    transition: transform var(--cc-d-md) var(--cc-spring), color var(--cc-d-sm) ease;
}

.cc-dd-toggle:hover svg:first-child {
    color: var(--color-primary-500);
    transform: scale(1.3);
    animation: cc-wiggle 700ms var(--cc-ease);
}

.cc-dd-toggle:hover svg:last-child {
    animation: cc-nudge-y 700ms ease-in-out infinite;
}

.cc-dd-toggle[aria-expanded="true"] {
    border-color: var(--color-primary-500);
    box-shadow: var(--cc-glow);
}

.cc-dd-toggle[aria-expanded="true"] svg:last-child {
    transform: rotate(180deg);
    animation: none;
}

/* ============================================================================
   9. Modal
   ========================================================================== */

/* Big springy entrance from below with a blur-in, plus a backdrop blur ramp */
@keyframes cc-modal-in {
    0% {
        opacity: 0;
        transform: scale(0.8) translateY(80px);
        filter: blur(12px);
    }

    100% {
        opacity: 1;
        transform: scale(1) translateY(0);
        filter: none;
    }
}

@keyframes cc-modal-out {
    0% {
        opacity: 1;
        transform: scale(1) translateY(0);
        filter: none;
    }

    100% {
        opacity: 0;
        transform: scale(0.85) translateY(48px);
        filter: blur(8px);
    }
}

@keyframes cc-backdrop-in {
    from {
        opacity: 0;
        backdrop-filter: blur(0);
    }

    to {
        opacity: 1;
        backdrop-filter: blur(8px);
    }
}

.cc-modal-backdrop {
    backdrop-filter: blur(8px);
}

@media (prefers-reduced-motion: no-preference) {
    .cc-modal-backdrop {
        animation: cc-backdrop-in 380ms var(--cc-ease) backwards;
    }

    #defaultModal.flex .cc-modal-content {
        animation: cc-modal-in 560ms var(--cc-spring);
    }

    #defaultModal.cc-modal-closing .cc-modal-content {
        animation: cc-modal-out 280ms var(--cc-smooth) forwards;
    }

    /* Title slides in (it's OOB-swapped fresh with every form) */
    #defaultModal.flex #modal-header {
        animation: cc-fade-left 480ms var(--cc-ease) 80ms backwards;
    }

    /* Form fields cascade in once the card has landed */
    #defaultModal.flex #object-form>* {
        animation: cc-fade-up 480ms var(--cc-ease) backwards;
    }

    #defaultModal.flex #object-form>*:nth-child(1) {
        animation-delay: 80ms;
    }

    #defaultModal.flex #object-form>*:nth-child(2) {
        animation-delay: 140ms;
    }

    #defaultModal.flex #object-form>*:nth-child(3) {
        animation-delay: 200ms;
    }

    #defaultModal.flex #object-form>*:nth-child(4) {
        animation-delay: 260ms;
    }

    #defaultModal.flex #object-form>*:nth-child(5) {
        animation-delay: 320ms;
    }

    #defaultModal.flex #object-form>*:nth-child(6) {
        animation-delay: 380ms;
    }

    #defaultModal.flex #object-form>*:nth-child(n + 7) {
        animation-delay: 440ms;
    }
}

/* Close button spins and reddens */
#closeModalButton {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    transition:
        transform var(--cc-d-md) var(--cc-spring),
        color var(--cc-d-sm) ease,
        background-color var(--cc-d-sm) ease;
}

#closeModalButton:hover {
    transform: rotate(90deg) scale(1.2);
    color: var(--color-red-600);
    background-color: color-mix(in oklab, var(--color-red-500) 12%, transparent);
}

#closeModalButton:active {
    transform: rotate(90deg) scale(0.9);
}

/* ============================================================================
   10. Toast
   ========================================================================== */
@keyframes cc-toast-in {
    0% {
        opacity: 0;
        transform: translateX(140%) scale(0.7) rotate(6deg);
    }

    60% {
        opacity: 1;
        transform: translateX(-22px) scale(1.05) rotate(-1.5deg);
    }

    82% {
        transform: translateX(8px) scale(0.99) rotate(0.5deg);
    }

    100% {
        opacity: 1;
        transform: translateX(0) scale(1) rotate(0);
    }
}

@keyframes cc-toast-out {
    0% {
        opacity: 1;
        transform: translateX(0) scale(1);
    }

    25% {
        transform: translateX(-18px) scale(1.04);
    }

    100% {
        opacity: 0;
        transform: translateX(140%) scale(0.8) rotate(5deg);
    }
}

@keyframes cc-toast-timer {
    from {
        transform: scaleX(1);
    }

    to {
        transform: scaleX(0);
    }
}

.toast-slide-in {
    animation: cc-toast-in 720ms var(--cc-spring) forwards;
}

.toast-slide-out {
    animation: cc-toast-out 480ms var(--cc-smooth) forwards;
}

#defaultToast {
    transition:
        box-shadow var(--cc-d-md) ease,
        transform var(--cc-d-md) var(--cc-spring);
}

#defaultToast:hover {
    transform: translateY(-6px) scale(1.03);
    box-shadow: var(--cc-shadow-float);
}

/* Countdown bar pinned to the bottom edge of the toast */
#cc-toast-timer {
    position: absolute;
    left: 0;
    bottom: 0;
    height: 4px;
    width: 100%;
    border-radius: 0 0 0.75rem 0.75rem;
    background: linear-gradient(90deg,
            var(--color-primary-400),
            var(--color-primary-600));
    box-shadow: 0 0 10px color-mix(in oklab, var(--color-primary-500) 70%, transparent);
    transform-origin: left center;
    transform: scaleX(0);
}

#defaultToast.cc-toast-active #cc-toast-timer {
    animation: cc-toast-timer 5s linear forwards;
}

/* Hovering pauses the countdown so the message can be read */
#defaultToast.cc-toast-active:hover #cc-toast-timer {
    animation-play-state: paused;
}

/* The status icon lands with a pop and rings in its own colour, the message
   follows it in */
#toast-content>div.inline-flex {
    animation:
        cc-pop-in 560ms var(--cc-bounce) 220ms backwards,
        cc-ring-pulse 1.3s var(--cc-ease) 640ms 1;
}

#toast-content>div.ms-3 {
    animation: cc-fade-left 480ms var(--cc-ease) 300ms backwards;
}

/* ============================================================================
   11. Confirm dialog
   ----------------------------------------------------------------------------
   Alpine's x-show toggles display, and a CSS animation restarts every time
   the element is displayed again — so the entrance is pure keyframes; Alpine
   keeps the leave transition.
   ========================================================================== */
#confirmModal {
    backdrop-filter: blur(8px);
    animation: cc-backdrop-in 320ms var(--cc-ease) backwards;
}

.cc-confirm-card {
    animation: cc-modal-in 560ms var(--cc-spring) backwards;
}

.cc-confirm-card .shrink-0 {
    animation: cc-ring-pulse 1.4s var(--cc-ease) 360ms 2;
}

.cc-confirm-card .shrink-0>svg {
    animation: cc-shake 800ms var(--cc-smooth) 200ms backwards;
}

.cc-confirm-card h3 {
    animation: cc-fade-left 480ms var(--cc-ease) 120ms backwards;
}

.cc-confirm-card p {
    animation: cc-fade-left 480ms var(--cc-ease) 200ms backwards;
}

.cc-confirm-card .mt-6 {
    animation: cc-fade-up 480ms var(--cc-ease) 260ms backwards;
}

/* ============================================================================
   12. Global HTMX progress bar, loaders & in-flight states
   ========================================================================== */
#cc-progress {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 4px;
    z-index: 9999;
    pointer-events: none;
    opacity: 0;
    transition: opacity 240ms ease;
}

#cc-progress.cc-active {
    opacity: 1;
}

/* Width, not scaleX: the glowing head at the right end keeps its shape */
#cc-progress>div {
    position: relative;
    height: 100%;
    width: 0%;
    background: linear-gradient(90deg,
            var(--color-primary-400),
            var(--color-primary-600),
            var(--color-primary-300),
            var(--color-primary-500));
    background-size: 200% 100%;
    box-shadow: 0 0 14px color-mix(in oklab, var(--color-primary-500) 90%, transparent);
    transition: width 240ms var(--cc-ease);
}

#cc-progress>div::after {
    content: "";
    position: absolute;
    right: 0;
    top: -4px;
    width: 90px;
    height: 12px;
    border-radius: 12px;
    background: radial-gradient(closest-side,
            color-mix(in oklab, var(--color-primary-300) 90%, white),
            transparent);
    filter: blur(4px);
}

#cc-progress.cc-active>div {
    animation:
        cc-progress-grow 2.4s var(--cc-ease) forwards,
        cc-progress-shimmer 1.2s linear infinite;
}

#cc-progress.cc-done>div {
    animation: none;
    width: 100%;
}

/* Centre spinner: delay only the *appearance*, so quick requests never flash
   it but dismissal still feels instant. htmx puts .htmx-request on the
   indicator itself (body carries hx-indicator="#loader"). */
#loader.htmx-indicator {
    transition: opacity 180ms ease;
}

#loader.htmx-request.htmx-indicator {
    transition: opacity 200ms ease 350ms;
    animation: cc-loader-breathe 1.2s ease-in-out 350ms infinite;
}

@keyframes cc-loader-breathe {

    0%,
    100% {
        transform: translate(-50%, -50%) scale(1);
    }

    50% {
        transform: translate(-50%, -50%) scale(1.18);
    }
}

/* Note: no rule on .htmx-added — htmx strips that class within a few ms of
   the settle step, which would cut any animation off mid-flight. Swapped
   content is covered by the row cascade and the panel entrances instead. */

/* The target of an in-flight request softens and blurs until the new
   content lands (motion.js toggles .cc-loading — htmx only marks indicators) */
.table-container,
#treatments-list {
    transition:
        opacity 220ms ease,
        filter 220ms ease,
        transform 220ms ease;
}

.table-container.cc-loading,
#treatments-list.cc-loading {
    opacity: 0.4;
    filter: blur(3px);
    transform: scale(0.992);
    pointer-events: none;
}

/* Leaving the page: the content drifts up and fades out completely while the
   next one loads, so the new page's rise (cc-main-rise) continues the same
   upward motion. No half-faded blurred state lingers between the two pages.
   The top progress bar shows that loading is under way. */
#app-main {
    transition:
        margin-left 520ms var(--cc-ease),
        opacity 240ms ease-in,
        transform 240ms ease-in;
}

html.cc-leaving #app-main {
    opacity: 0;
    transform: translateY(-10px);
    pointer-events: none;
}

/* ============================================================================
   13. View transitions
   ----------------------------------------------------------------------------
   Deliberately NOT using cross-document (`@view-transition { navigation }`)
   transitions for page navigation. They cross-fade the old page into the new
   one, which *replaces* the per-element choreography above — the table
   cascade and the sidebar intro never get to play. A plain load running the
   full entrance reads as far more alive, so navigation keeps the keyframes
   and view transitions are reserved for the theme switch below.
   ========================================================================== */

/* Theme switch gets its own circular wipe (toggle-theme.js). Nothing carries
   a view-transition-name, so the whole page is captured as one `root`
   snapshot and the circle sweeps across everything at once. */
html.cc-theme-switch::view-transition-old(root),
html.cc-theme-switch::view-transition-new(root) {
    animation: none;
    mix-blend-mode: normal;
}

html.cc-theme-switch::view-transition-old(root) {
    z-index: 1;
}

html.cc-theme-switch::view-transition-new(root) {
    z-index: 2;
}

/* ============================================================================
   14. Misc surfaces
   ========================================================================== */

/* Pagination */
.cc-page-link {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    transition:
        background-color var(--cc-d-sm) ease,
        color var(--cc-d-sm) ease,
        border-color var(--cc-d-sm) ease,
        transform var(--cc-d-md) var(--cc-spring),
        box-shadow var(--cc-d-md) ease;
}

.cc-page-link:not([aria-disabled="true"]):hover {
    transform: translateY(-4px) scale(1.12);
    z-index: 2;
    color: var(--color-primary-700);
    border-color: var(--color-primary-400);
    background-color: color-mix(in oklab, var(--color-primary-500) 12%, transparent);
    box-shadow: 0 12px 22px -12px rgb(15 35 41 / 0.45);
}

.dark .cc-page-link:not([aria-disabled="true"]):hover {
    color: var(--color-primary-300);
}

.cc-page-link:not([aria-disabled="true"]):active {
    transform: translateY(0) scale(0.94);
    transition-duration: 80ms;
}

.cc-page-link svg {
    transition: transform var(--cc-d-md) var(--cc-spring);
}

.cc-page-link:not([aria-disabled="true"]):hover svg {
    transform: scale(1.3);
}

/* The current page pops in with each render */
.cc-page-link[aria-current="page"] {
    animation: cc-pop-in 520ms var(--cc-bounce) 200ms backwards;
}

/* Dashboard list rows. The row box itself never moves: the tint and the
   edge bar stay flush with the card while the children slide right. That
   keeps every row's caret the same distance from the card's border
   regardless of the row's own padding (px-5 vs px-4). */
.cc-list-row {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    transition:
        background-color var(--cc-d-sm) ease,
        box-shadow var(--cc-d-md) ease;
}

.cc-list-row>* {
    transition: transform 400ms var(--cc-spring);
}

.cc-list-row:hover {
    background-color: color-mix(in oklab, var(--color-primary-500) 10%, transparent) !important;
    box-shadow: inset 5px 0 0 0 var(--color-primary-500);
}

.cc-list-row:hover>* {
    transform: translateX(12px);
}

.cc-list-row:active>* {
    transform: translateX(10px);
    transition-duration: 80ms;
}

/* Trailing caret. An in-flow flex item, not an absolutely positioned one:
   it reserves its own slot at the end of the row (pushed right by the auto
   margin, spaced by the row's gap) so it can never sit on top of the
   right-hand column, and its margin keeps it half a rem clear of the row's
   own padding. The glyph is a background image parked at the right end of
   a slot wider than its slide, so the slide-in happens entirely inside the
   slot and never crosses into the content beside it. It springs in once
   and settles — no looping nudge. Rows must be flex containers with their
   own padding. */
.cc-list-row::after {
    content: "";
    display: inline-block;
    flex: 0 0 auto;
    margin-left: auto;
    margin-right: 0.5rem;
    width: 1.5rem;
    height: 0.75rem;
    background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2314b8a6' stroke-width='2.75' stroke-linecap='round' stroke-linejoin='round'><path d='M9 5l7 7-7 7'/></svg>") right center / 0.75rem 0.75rem no-repeat;
    opacity: 0;
    transform: translateX(-16px);
    transition: transform 520ms var(--cc-spring), opacity 260ms ease;
}

.cc-list-row:hover::after {
    opacity: 1;
    transform: none;
    animation: cc-nudge-x 1s ease-in-out 520ms infinite;
}

/* Avatars and check circles ride along with the slide and add their own pop */
.cc-list-row .cc-avatar {
    transition:
        transform var(--cc-d-lg) var(--cc-spring),
        box-shadow var(--cc-d-md) ease;
}

.cc-list-row:hover .cc-avatar {
    transform: translateX(12px) scale(1.3) rotate(-10deg);
    box-shadow: 0 10px 22px -8px color-mix(in oklab, var(--color-primary-500) 90%, transparent);
}

.cc-list-row .cc-pending-dot {
    transition:
        transform var(--cc-d-md) var(--cc-spring),
        border-color var(--cc-d-sm) ease;
}

.cc-list-row:hover .cc-pending-dot {
    border-color: var(--color-primary-500);
    transform: translateX(12px) scale(1.25);
}

/* Inline links: underline draws in, text nudges */
.cc-link {
    position: relative;
    display: inline-block;
    transition: transform var(--cc-d-md) var(--cc-spring), color var(--cc-d-sm) ease;
}

.cc-link::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: -3px;
    width: 0;
    height: 2px;
    border-radius: 2px;
    background: currentColor;
    transition: width 320ms var(--cc-ease);
}

.cc-link:hover {
    transform: translateX(4px);
}

.cc-link:hover::after {
    width: 100%;
}

/* Page heading icon chip wobbles on hover */
h1>span:first-child {
    transition: transform var(--cc-d-lg) var(--cc-spring);
}

h1>span:first-child:hover {
    transform: scale(1.2);
    animation: cc-wiggle 800ms var(--cc-ease);
}

/* Search results / picker lists (visit form): items cascade in and slide on
   hover, the "+" spins */
#product-search-results li,
#batch-search-results li,
#visit-templates-list>div {
    animation: cc-fade-up 440ms var(--cc-ease) backwards;
    animation-delay: calc(var(--i, 0) * 50ms);
}

#product-search-results li:nth-child(2),
#batch-search-results li:nth-child(2),
#visit-templates-list>div:nth-child(2) {
    --i: 1;
}

#product-search-results li:nth-child(3),
#batch-search-results li:nth-child(3),
#visit-templates-list>div:nth-child(3) {
    --i: 2;
}

#product-search-results li:nth-child(4),
#batch-search-results li:nth-child(4),
#visit-templates-list>div:nth-child(4) {
    --i: 3;
}

#product-search-results li:nth-child(5),
#batch-search-results li:nth-child(5),
#visit-templates-list>div:nth-child(5) {
    --i: 4;
}

#product-search-results li:nth-child(6),
#batch-search-results li:nth-child(6),
#visit-templates-list>div:nth-child(6) {
    --i: 5;
}

#product-search-results li:nth-child(n + 7),
#batch-search-results li:nth-child(n + 7),
#visit-templates-list>div:nth-child(n + 7) {
    --i: 6;
}

.product-result,
#batch-search-results li,
#visit-templates-list>div {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    transition:
        transform var(--cc-d-md) var(--cc-spring),
        background-color var(--cc-d-sm) ease,
        box-shadow var(--cc-d-md) ease;
}

.product-result:hover,
#batch-search-results li:hover,
#visit-templates-list>div:hover {
    transform: translateX(10px);
}

/* Edge bar only where the row has padding to keep it clear of the text;
   template rows start their content 8px in and carry their own arrow. */
.product-result:hover,
#batch-search-results li:hover {
    box-shadow: inset 4px 0 0 0 var(--color-primary-500);
}

.product-result:hover svg.cc-icon-plus {
    transform: rotate(180deg) scale(1.4);
}

/* Treatment cards: cascade on every re-render, lift on hover */
#treatments-list>div {
    animation: cc-rise 520ms var(--cc-ease) backwards;
    animation-delay: calc(var(--i, 0) * 70ms);
    transition:
        transform var(--cc-d-md) var(--cc-spring),
        box-shadow var(--cc-d-md) ease,
        border-color var(--cc-d-sm) ease;
}

#treatments-list>div:nth-child(2) {
    --i: 1;
}

#treatments-list>div:nth-child(3) {
    --i: 2;
}

#treatments-list>div:nth-child(4) {
    --i: 3;
}

#treatments-list>div:nth-child(5) {
    --i: 4;
}

#treatments-list>div:nth-child(n + 6) {
    --i: 5;
}

#treatments-list>div:hover {
    transform: translateY(-4px) scale(1.01);
    box-shadow: var(--cc-shadow-pop);
    border-color: color-mix(in oklab, var(--color-primary-500) 45%, transparent);
}

/* Plotly report graphs fade up when they enter the viewport */
.report-graph {
    animation: cc-fade-up var(--cc-d-xl) var(--cc-ease) backwards;
}

/* Django messages banner */
#app-main .mb-4>div[class*="rounded-xl"] {
    animation: cc-fade-down 560ms var(--cc-bounce) backwards;
}

/* Sticky page header (visit form) gains a shadow and a tinted edge once it
   is actually stuck — scroll-driven, so nothing runs on browsers that lack
   scroll timelines. */
@supports (animation-timeline: scroll()) {
    @keyframes cc-stuck {
        to {
            box-shadow:
                0 12px 28px -14px rgb(15 35 41 / 0.45),
                inset 0 -2px 0 0 color-mix(in oklab, var(--color-primary-500) 60%, transparent);
        }
    }

    .cc-sticky-head {
        animation: cc-stuck linear both;
        animation-timeline: scroll(root);
        animation-range: 0 140px;
    }
}

/* Never animate anything on a printed page */
@media print {

    *,
    *::before,
    *::after {
        animation: none !important;
        transition: none !important;
        opacity: 1 !important;
        transform: none !important;
        filter: none !important;
    }

    #cc-progress,
    .cc-card::before,
    .cc-ripple {
        display: none !important;
    }
}

/* ============================================================================
   15. Auth screens (templates/allauth, templates/account)
   ========================================================================== */
@keyframes cc-aurora {
    0% {
        translate: 0 0;
        scale: 1;
    }

    50% {
        translate: 8% -10%;
        scale: 1.25;
    }

    100% {
        translate: -10% 6%;
        scale: 0.9;
    }
}

/* Heartbeat trace: draws in, holds, then erases forward */
@keyframes cc-ecg {
    0% {
        stroke-dashoffset: 1;
    }

    55% {
        stroke-dashoffset: 0;
    }

    100% {
        stroke-dashoffset: -1;
    }
}

@keyframes cc-auth-card-in {
    from {
        opacity: 0;
        translate: 0 48px;
        scale: 0.94;
        filter: blur(12px);
    }

    to {
        opacity: 1;
        translate: 0 0;
        scale: 1;
        filter: none;
    }
}

@keyframes cc-auth-spin {
    to {
        rotate: 360deg;
    }
}

/* Literal colours: Tailwind only emits theme variables something uses, and
   one undefined var() would void the whole background */
.cc-auth-hero {
    background:
        radial-gradient(120% 90% at 0% 0%, #0d9488 0%, transparent 60%),
        linear-gradient(160deg, #115e59 0%, #042f2e 70%, #021a19 100%);
}

.cc-auth-aurora span {
    position: absolute;
    border-radius: 9999px;
    filter: blur(70px);
    opacity: 0.55;
    animation: cc-aurora 16s ease-in-out infinite alternate;
}

.cc-auth-aurora span:nth-child(1) {
    width: 26rem;
    height: 26rem;
    top: -6rem;
    left: -4rem;
    background: var(--color-primary-400);
}

.cc-auth-aurora span:nth-child(2) {
    width: 22rem;
    height: 22rem;
    bottom: -5rem;
    right: -3rem;
    background: #38bdf8;
    animation-duration: 21s;
    animation-delay: -6s;
}

.cc-auth-aurora span:nth-child(3) {
    width: 16rem;
    height: 16rem;
    top: 40%;
    left: 45%;
    background: #a7f3d0;
    opacity: 0.3;
    animation-duration: 13s;
    animation-delay: -3s;
}

.cc-auth-grid {
    background-image: radial-gradient(rgb(255 255 255 / 0.16) 1px, transparent 1px);
    background-size: 22px 22px;
    mask-image: radial-gradient(ellipse at 30% 40%, #000 0%, transparent 70%);
}

.cc-auth-ecg path {
    stroke-dasharray: 1;
    animation: cc-ecg 3.6s cubic-bezier(0.45, 0, 0.25, 1) infinite;
}

.cc-auth-ecg path:nth-child(2) {
    filter: blur(4px);
    opacity: 0.7;
}

.cc-auth-gradient-text {
    background: linear-gradient(90deg, var(--color-primary-200), #e0f2fe, var(--color-primary-300), var(--color-primary-200));
    background-size: 200% 100%;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    animation: cc-sheen 6s linear infinite;
}

.cc-auth-feature {
    transition: translate 400ms var(--cc-spring), background-color var(--cc-d-sm) ease;
}

.cc-auth-feature:hover {
    translate: 10px 0;
    background-color: rgb(255 255 255 / 0.08);
}

.cc-auth-feature:hover .cc-auth-feature-icon {
    scale: 1.2;
    rotate: -8deg;
    background-color: rgb(255 255 255 / 0.22);
}

.cc-auth-feature-icon {
    transition: scale 420ms var(--cc-bounce), rotate 420ms var(--cc-bounce), background-color var(--cc-d-sm) ease;
}

.cc-auth-float {
    animation: cc-float 6s ease-in-out infinite;
}

.cc-auth-card {
    animation: cc-auth-card-in 900ms var(--cc-spring) backwards;
}

.cc-auth-card.cc-auth-shake {
    animation:
        cc-auth-card-in 900ms var(--cc-spring) backwards,
        cc-shake 600ms var(--cc-ease) 700ms;
}

/* Floating-label input: the label sits inside until focus or a value */
.cc-auth-field {
    position: relative;
}

.cc-auth-input {
    display: block;
    width: 100%;
    height: 3.5rem;
    padding: 1.35rem 1rem 0.4rem;
    border-radius: 0.875rem;
    border: 1.5px solid var(--color-gray-300);
    background: rgb(255 255 255 / 0.9);
    font-size: 0.95rem;
    color: var(--color-gray-900);
    outline: none;
    transition:
        border-color var(--cc-d-sm) ease,
        box-shadow var(--cc-d-md) var(--cc-ease),
        background-color var(--cc-d-sm) ease,
        translate 360ms var(--cc-spring);
}

.cc-auth-field:has(.cc-auth-eye) .cc-auth-input {
    padding-right: 3rem;
}

.cc-auth-input:hover {
    border-color: var(--color-primary-300);
}

.cc-auth-input:focus {
    border-color: var(--color-primary-500);
    background: #fff;
    box-shadow:
        0 0 0 5px color-mix(in oklab, var(--color-primary-500) 18%, transparent),
        0 12px 28px -14px color-mix(in oklab, var(--color-primary-600) 70%, transparent);
    translate: 0 -2px;
}

.dark .cc-auth-input {
    border-color: var(--color-gray-600);
    background: rgb(16 28 34 / 0.6);
    color: #fff;
}

.dark .cc-auth-input:hover {
    border-color: var(--color-primary-700);
}

.dark .cc-auth-input:focus {
    border-color: var(--color-primary-400);
    background: rgb(16 28 34 / 0.9);
}

.cc-auth-field.is-invalid .cc-auth-input {
    border-color: var(--color-red-400);
}

.cc-auth-label {
    position: absolute;
    left: 1rem;
    top: 1.1rem;
    font-size: 0.95rem;
    color: var(--color-gray-500);
    pointer-events: none;
    transform-origin: left top;
    transition:
        translate 300ms var(--cc-spring),
        scale 300ms var(--cc-spring),
        color var(--cc-d-sm) ease;
}

.cc-auth-input:focus + .cc-auth-label,
.cc-auth-input:not(:placeholder-shown) + .cc-auth-label,
.cc-auth-input:autofill + .cc-auth-label {
    translate: 0 -0.62rem;
    scale: 0.78;
    font-weight: 600;
    letter-spacing: 0.01em;
}

.cc-auth-input:focus + .cc-auth-label {
    color: var(--color-primary-600);
}

.dark .cc-auth-label {
    color: var(--color-gray-400);
}

.dark .cc-auth-input:focus + .cc-auth-label {
    color: var(--color-primary-300);
}

.cc-auth-field.is-invalid .cc-auth-label {
    color: var(--color-red-500);
}

.cc-auth-eye {
    position: absolute;
    right: 0.5rem;
    top: 50%;
    translate: 0 -50%;
    display: grid;
    place-items: center;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: 0.65rem;
    color: var(--color-gray-400);
    cursor: pointer;
    transition: color var(--cc-d-sm) ease, background-color var(--cc-d-sm) ease;
}

.cc-auth-eye svg {
    transition: scale 360ms var(--cc-bounce), rotate 360ms var(--cc-bounce);
}

.cc-auth-eye:hover {
    color: var(--color-primary-600);
    background: var(--color-primary-50);
}

.cc-auth-eye:hover svg {
    scale: 1.25;
    rotate: -10deg;
}

.dark .cc-auth-eye:hover {
    color: var(--color-primary-300);
    background: rgb(45 212 191 / 0.1);
}

.cc-auth-hint {
    animation: cc-fade-down 360ms var(--cc-spring) backwards;
}

/* Remember-me switch */
.cc-auth-switch {
    position: relative;
    flex: none;
    width: 2.6rem;
    height: 1.5rem;
    border-radius: 9999px;
    background: var(--color-gray-300);
    transition: background-color var(--cc-d-sm) ease, box-shadow var(--cc-d-md) ease;
}

.cc-auth-switch::after {
    content: "";
    position: absolute;
    top: 0.2rem;
    left: 0.2rem;
    width: 1.1rem;
    height: 1.1rem;
    border-radius: 9999px;
    background: #fff;
    box-shadow: 0 2px 6px rgb(15 35 41 / 0.3);
    transition: translate 420ms var(--cc-bounce), width 200ms ease;
}

.dark .cc-auth-switch {
    background: var(--color-gray-600);
}

input:checked + .cc-auth-switch {
    background: var(--color-primary-500);
    box-shadow: 0 0 0 4px color-mix(in oklab, var(--color-primary-500) 20%, transparent);
}

input:checked + .cc-auth-switch::after {
    translate: 1.1rem 0;
}

input:focus-visible + .cc-auth-switch {
    outline: 2px solid var(--color-primary-500);
    outline-offset: 2px;
}

label:active .cc-auth-switch::after {
    width: 1.4rem;
}

/* Primary action: gradient pill with a light sweep, lift and arrow dash */
.cc-auth-submit {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    display: inline-flex;
    flex: 1 1 100%;
    width: 100%;
    align-items: center;
    justify-content: center;
    gap: 0.6rem;
    height: 3.25rem;
    padding: 0 1.5rem;
    border-radius: 0.875rem;
    font-weight: 600;
    color: #fff;
    cursor: pointer;
    background: linear-gradient(135deg, var(--color-primary-500), var(--color-primary-700));
    box-shadow: 0 10px 24px -10px color-mix(in oklab, var(--color-primary-600) 90%, transparent);
    transition:
        translate 380ms var(--cc-spring),
        scale 200ms ease,
        box-shadow var(--cc-d-md) var(--cc-ease),
        filter var(--cc-d-sm) ease;
}

.cc-auth-submit::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 40%;
    z-index: -1;
    background: linear-gradient(90deg, transparent, rgb(255 255 255 / 0.45), transparent);
    transform: translateX(-180%) skewX(-18deg);
}

.cc-auth-submit:hover {
    translate: 0 -3px;
    filter: saturate(1.15) brightness(1.05);
    box-shadow:
        0 0 0 5px color-mix(in oklab, var(--color-primary-500) 20%, transparent),
        0 20px 36px -12px color-mix(in oklab, var(--color-primary-600) 95%, transparent);
}

.cc-auth-submit:hover::before {
    animation: cc-shine 900ms var(--cc-ease);
}

.cc-auth-submit:active {
    scale: 0.97;
    translate: 0 0;
}

.cc-auth-submit .cc-auth-arrow {
    transition: translate 380ms var(--cc-spring);
}

.cc-auth-submit:hover .cc-auth-arrow {
    translate: 6px 0;
}

.cc-auth-spinner {
    display: none;
    width: 1.15rem;
    height: 1.15rem;
    border-radius: 9999px;
    border: 2.5px solid rgb(255 255 255 / 0.35);
    border-top-color: #fff;
    animation: cc-auth-spin 700ms linear infinite;
}

form.is-busy .cc-auth-submit {
    pointer-events: none;
    filter: saturate(0.85);
}

form.is-busy .cc-auth-submit .cc-auth-spinner {
    display: block;
}

form.is-busy .cc-auth-submit .cc-auth-arrow {
    display: none;
}

.cc-auth-btn {
    display: inline-flex;
    flex: 1 1 auto;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    height: 3rem;
    padding: 0 1.25rem;
    border-radius: 0.875rem;
    border: 1.5px solid var(--color-gray-300);
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--color-gray-700);
    cursor: pointer;
    transition:
        translate 380ms var(--cc-spring),
        border-color var(--cc-d-sm) ease,
        color var(--cc-d-sm) ease,
        background-color var(--cc-d-sm) ease;
}

.cc-auth-btn:hover {
    translate: 0 -2px;
    border-color: var(--color-primary-400);
    color: var(--color-primary-700);
    background: var(--color-primary-50);
}

.dark .cc-auth-btn {
    border-color: var(--color-gray-600);
    color: var(--color-gray-200);
}

.dark .cc-auth-btn:hover {
    border-color: var(--color-primary-400);
    color: var(--color-primary-300);
    background: rgb(45 212 191 / 0.08);
}

.cc-auth-btn-danger,
.cc-auth-btn-danger:hover {
    border-color: transparent;
    color: #fff;
    background: linear-gradient(135deg, var(--color-red-500), var(--color-red-700));
}

.dark .cc-auth-btn-danger,
.dark .cc-auth-btn-danger:hover {
    color: #fff;
    border-color: transparent;
    background: linear-gradient(135deg, var(--color-red-500), var(--color-red-700));
}

.cc-auth-btn-danger:hover {
    box-shadow: 0 14px 28px -12px var(--color-red-600);
}

/* Inline text links inside auth copy */
.cc-auth-prose a,
.cc-auth-link {
    font-weight: 600;
    color: var(--color-primary-700);
    background: linear-gradient(currentColor, currentColor) no-repeat 0 100% / 0 2px;
    transition: background-size 420ms var(--cc-ease), color var(--cc-d-sm) ease;
}

.cc-auth-prose a:hover,
.cc-auth-link:hover {
    background-size: 100% 2px;
    color: var(--color-primary-600);
}

.dark .cc-auth-prose a,
.dark .cc-auth-link {
    color: var(--color-primary-300);
}

@media (prefers-reduced-motion: reduce) {
    .cc-auth-ecg path {
        animation: none;
        stroke-dashoffset: 0;
    }

    .cc-auth-aurora span,
    .cc-auth-gradient-text,
    .cc-auth-float {
        animation: none;
    }
}
