/* ===================================================================
   KNMS PREMIUM — finishing layer
   -------------------------------------------------------------------
   Loaded after app.css site-wide. Everything here is ADDITIVE polish on
   components app.css already builds: depth, light, and motion that
   responds to the visitor.

   Two rules kept this file from turning into a second stylesheet:

     1. Nothing here may change layout. No widths, no display, no
        flex/grid, no margins that move a box. app.css owns geometry;
        this file owns surface, light and motion. A regression here can
        make the site look flatter, never broken.

     2. No duplication. app.css already gives .btn a sheen sweep and
        .card / .stat-tile their hover lift — those are not re-declared
        below, only extended where the extension is genuinely new.

   Pairs with scene.css: that file owns the backdrop, this one owns
   everything standing in front of it.
   =================================================================== */

/* ===================================================================
   1. Surfaces — glass, edge light, and the sweep
   =================================================================== */

/* A hairline of light along the top edge. Real glass catches the light
   source above it; a flat border makes a panel look like a rectangle of
   colour. This is one pseudo-element and no repaint cost. */
.card::after,
.panel::after,
.page-card::after {
    content: '';
    position: absolute;
    inset: 0 0 auto 0;
    height: 1px;
    background: linear-gradient(90deg,
        transparent 0%,
        rgba(255, 255, 255, 0.28) 22%,
        rgba(255, 255, 255, 0.5) 50%,
        rgba(255, 255, 255, 0.28) 78%,
        transparent 100%);
    opacity: 0.55;
    pointer-events: none;
    border-radius: inherit;
    transition: opacity 0.3s ease;
}
.card, .panel, .page-card { position: relative; }
.card:hover::after,
.panel:hover::after,
.page-card:hover::after { opacity: 1; }

/* The sweep: a wide, soft diagonal highlight that crosses a card on hover.
   Distinct from the button sheen in app.css — that one is a narrow specular
   glint on a small control, this is a slow pass of light across a large
   surface. Transform-only, so it composites without repainting the card. */
.card,
.page-card,
.news-card {
    overflow: hidden;               /* contains the sweep, nothing else */
}
.card > .kn-sweep,
.page-card > .kn-sweep,
.news-card > .kn-sweep,
.card::before,
.page-card::before {
    pointer-events: none;
}
.page-card::before {
    content: '';
    position: absolute;
    top: -60%;
    left: -80%;
    width: 60%;
    height: 220%;
    z-index: 1;
    background: linear-gradient(100deg,
        transparent 0%,
        rgba(255, 255, 255, 0.06) 38%,
        rgba(255, 255, 255, 0.14) 50%,
        rgba(255, 255, 255, 0.06) 62%,
        transparent 100%);
    transform: translateX(0) skewX(-12deg);
    transition: transform 0.85s cubic-bezier(0.22, 0.7, 0.3, 1);
}
.page-card:hover::before { transform: translateX(320%) skewX(-12deg); }

/* Depth on hover. app.css already lifts these 1-2px; this adds the shadow
   that makes the lift readable, and a scale small enough to feel like
   proximity rather than a zoom. */
.page-card,
.news-card {
    transition: transform 0.35s cubic-bezier(0.22, 0.7, 0.3, 1),
                box-shadow 0.35s ease,
                border-color 0.35s ease;
}
.page-card:hover,
.news-card:hover {
    transform: translateY(-4px) scale(1.008);
    box-shadow:
        0 18px 40px -18px rgba(0, 0, 0, 0.7),
        0 0 0 1px rgba(139, 92, 246, 0.3),
        0 0 34px rgba(139, 92, 246, 0.18);
}

/* ===================================================================
   2. Headings — gradient accent and a rule that draws itself
   =================================================================== */

/* The section rule grows from the left as the section scrolls in, riding
   the .reveal class app.js already applies. No new observer, no new JS. */
.home-section > h2,
.home-section > h3 {
    position: relative;
    padding-bottom: 0.55rem;
}
.home-section > h2::after,
.home-section > h3::after {
    content: '';
    position: absolute;
    left: 0;
    bottom: 0;
    height: 2px;
    width: 100%;
    max-width: 220px;
    border-radius: 2px;
    background: linear-gradient(90deg,
        var(--knms-accent, #ffc94a),
        var(--gloss-purple, #8b3ff5) 55%,
        transparent 100%);
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform 0.8s cubic-bezier(0.22, 0.7, 0.3, 1) 0.15s;
}
.home-section.is-visible > h2::after,
.home-section.is-visible > h3::after,
/* Sections that never get the reveal treatment (no JS, or not observed)
   must still show the rule — an animation that only ever runs in one
   direction should never be the only thing making a mark visible. */
.home-section:not(.reveal) > h2::after,
.home-section:not(.reveal) > h3::after { transform: scaleX(1); }

/* Page titles get the accent as a gradient in the text itself. Applied
   only to h1 inside the main column so admin table headers and card
   headings are untouched. */
.page > h1 {
    background: linear-gradient(120deg,
        var(--knms-heading, #f4f0ff) 0%,
        var(--knms-heading, #f4f0ff) 42%,
        var(--knms-accent-light, #ffe08a) 72%,
        var(--knms-accent, #ffc94a) 100%);
    -webkit-background-clip: text;
            background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
}
/* -webkit-text-fill-color INHERITS, and it beats `color` on any descendant
   that sets one. The rule above therefore made every child of an h1
   invisible — which is exactly what happened to the avatar initials inside
   `h1.name-row` on the profile page: the circle rendered, the letters did not.
   Any element nested in a gradient heading has to opt back out. */
.page > h1 * {
    -webkit-text-fill-color: currentColor;
}
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
    .page > h1 { color: var(--knms-heading, #f4f0ff); -webkit-text-fill-color: currentColor; }
}

/* ===================================================================
   3. Motion — entrance, stagger, and page arrival
   =================================================================== */

/* A softer, longer reveal than app.css's default, with a touch of scale.
   Overriding rather than adding a second animation keeps one transition on
   the property and avoids the two fighting. */
.reveal {
    transition:
        opacity 0.75s cubic-bezier(0.22, 0.7, 0.3, 1),
        transform 0.75s cubic-bezier(0.22, 0.7, 0.3, 1);
}

/* Staggered arrival for grids that app.js does not observe individually.
   Six steps then stop: past that the last card waits long enough that the
   page feels slow rather than considered. */
.page-card-grid > *,
.stat-band > * {
    animation: kn-premium-rise 0.7s cubic-bezier(0.22, 0.7, 0.3, 1) both;
}
.page-card-grid > *:nth-child(1), .stat-band > *:nth-child(1) { animation-delay: 0.04s; }
.page-card-grid > *:nth-child(2), .stat-band > *:nth-child(2) { animation-delay: 0.10s; }
.page-card-grid > *:nth-child(3), .stat-band > *:nth-child(3) { animation-delay: 0.16s; }
.page-card-grid > *:nth-child(4), .stat-band > *:nth-child(4) { animation-delay: 0.22s; }
.page-card-grid > *:nth-child(5), .stat-band > *:nth-child(5) { animation-delay: 0.28s; }
.page-card-grid > *:nth-child(n + 6), .stat-band > *:nth-child(n + 6) { animation-delay: 0.34s; }

@keyframes kn-premium-rise {
    from { opacity: 0; transform: translateY(16px) scale(0.985); }
    to   { opacity: 1; transform: none; }
}

/* Flash messages slide in and settle rather than appearing. They report the
   result of something the member just did, so arriving with a small piece of
   motion is what makes them noticed at all. */
.flash {
    animation: kn-premium-flash 0.5s cubic-bezier(0.22, 0.7, 0.3, 1) both;
    position: relative;
    overflow: hidden;
}
@keyframes kn-premium-flash {
    from { opacity: 0; transform: translateY(-10px); }
    to   { opacity: 1; transform: none; }
}
/* One pass of light across a success message — a small celebration, once. */
.flash-success::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(100deg, transparent 20%, rgba(255, 255, 255, 0.18) 50%, transparent 80%);
    transform: translateX(-100%);
    animation: kn-premium-sweep 1.4s ease-out 0.3s both;
    pointer-events: none;
}
@keyframes kn-premium-sweep {
    to { transform: translateX(100%); }
}

/* ===================================================================
   4. Navigation — glass that reacts to scroll
   =================================================================== */

.topbar {
    backdrop-filter: blur(14px) saturate(1.35);
    -webkit-backdrop-filter: blur(14px) saturate(1.35);
    transition: background 0.35s ease, box-shadow 0.35s ease, border-color 0.35s ease;
}
/* app.js sets .is-scrolled on the body; the bar tightens and lifts off the
   page once content is passing under it. */
body.is-scrolled .topbar {
    background: rgba(14, 7, 34, 0.82);
    box-shadow: 0 10px 30px -16px rgba(0, 0, 0, 0.85);
}

/* Nav links grow an underline from the centre. The active link keeps it
   permanently, so hover and state use one visual language. */
.topnav a {
    position: relative;
}
.topnav a::after {
    content: '';
    position: absolute;
    left: 50%;
    right: 50%;
    bottom: 0.1rem;
    height: 2px;
    border-radius: 2px;
    background: linear-gradient(90deg, var(--knms-accent, #ffc94a), var(--gloss-cyan, #35dcff));
    transition: left 0.28s ease, right 0.28s ease, opacity 0.28s ease;
    opacity: 0;
}
.topnav a:hover::after { left: 0.35rem; right: 0.35rem; opacity: 0.9; }
.topnav a.is-active::after { left: 0.35rem; right: 0.35rem; opacity: 1; }

/* The brand mark catches the light on hover. */
.brand:hover .brand-mark-emblem,
.brand:hover .brand-logo {
    filter: drop-shadow(0 0 12px var(--glow-gold, rgba(255, 201, 74, 0.7)));
    transform: scale(1.05) rotate(-2deg);
}
.brand-mark-emblem,
.brand-logo {
    transition: filter 0.3s ease, transform 0.3s cubic-bezier(0.22, 0.7, 0.3, 1);
}

/* ===================================================================
   5. Controls — focus, fields and links
   =================================================================== */

/* One focus treatment for the whole site. Two rings — a dark spacer then
   the accent — so it stays visible on both the dark page and the pale
   surfaces inside cards. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
    outline: 2px solid var(--knms-accent, #ffc94a);
    outline-offset: 2px;
    border-radius: 6px;
}

.form-group input,
.form-group select,
.form-group textarea {
    transition: border-color 0.22s ease, box-shadow 0.22s ease, background 0.22s ease;
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
    border-color: var(--gloss-cyan, #35dcff);
    box-shadow: 0 0 0 3px rgba(53, 220, 255, 0.14), 0 0 18px rgba(53, 220, 255, 0.12);
}

/* Body links get an underline that wipes in, instead of appearing whole. */
.page p > a:not(.btn):not(.kn-pbtn),
.page li > a:not(.btn):not(.kn-pbtn) {
    background-image: linear-gradient(90deg, var(--knms-accent, #ffc94a), var(--knms-accent, #ffc94a));
    background-repeat: no-repeat;
    background-position: 0 100%;
    background-size: 0% 1.5px;
    transition: background-size 0.3s ease, color 0.2s ease;
}
.page p > a:not(.btn):not(.kn-pbtn):hover,
.page li > a:not(.btn):not(.kn-pbtn):hover {
    background-size: 100% 1.5px;
}

/* ===================================================================
   6. Loading states
   =================================================================== */

/* A shimmer for anything rendered as a placeholder. Uses a moving gradient
   rather than a pulsing opacity: opacity pulses read as "broken", a
   left-to-right shimmer reads as "arriving". */
.is-skeleton,
.skeleton {
    background-image: linear-gradient(100deg,
        rgba(255, 255, 255, 0.03) 30%,
        rgba(255, 255, 255, 0.10) 48%,
        rgba(255, 255, 255, 0.03) 66%);
    background-size: 220% 100%;
    animation: kn-premium-shimmer 1.5s linear infinite;
    border-radius: 8px;
    color: transparent !important;
}
@keyframes kn-premium-shimmer {
    from { background-position: 120% 0; }
    to   { background-position: -120% 0; }
}

/* ===================================================================
   7. Scrollbar
   =================================================================== */

::-webkit-scrollbar-thumb {
    background: linear-gradient(180deg, var(--gloss-purple, #8b3ff5), var(--gloss-cyan, #35dcff));
    border-radius: 999px;
}
::-webkit-scrollbar-thumb:hover {
    background: linear-gradient(180deg, var(--knms-accent, #ffc94a), var(--gloss-purple, #8b3ff5));
}

/* ===================================================================
   8. Motion policy
   -------------------------------------------------------------------
   Everything above is ornament. Under a reduced-motion preference it all
   stops — but note that each rule resets to the FINISHED state, never the
   starting one. An entrance animation that is merely cancelled leaves its
   element at opacity 0, which hides content from exactly the people who
   asked for less motion.
   =================================================================== */

@media (prefers-reduced-motion: reduce) {
    .page-card-grid > *,
    .stat-band > *,
    .flash {
        animation: none;
        opacity: 1;
        transform: none;
    }
    .flash-success::after,
    .is-skeleton, .skeleton { animation: none; }
    .page-card:hover,
    .news-card:hover { transform: none; }
    .page-card::before { transition: none; }
    .home-section > h2::after,
    .home-section > h3::after { transform: scaleX(1); transition: none; }
    .topnav a::after { transition: none; }
    .brand-mark-emblem, .brand-logo { transition: none; }
    .page p > a:not(.btn):not(.kn-pbtn),
    .page li > a:not(.btn):not(.kn-pbtn) { transition: none; }
}
