/* =========================================================================
   All Projects page — hero, sticky category nav, category groups, tile grid.
   Reuses theme.css tokens. The tiles are this page's own fixed-size cards
   (partials/project-tile.blade.php), not the ring cards from showcase.css.
   Everything here is prefixed `pi-` and scoped to this page only.
   ========================================================================= */

.pi-section {
    position: relative;
    z-index: 1;
    padding: 128px 0 96px;
}

/* Ambient soft blue/cyan wash at the top of the page */
.pi-glow {
    position: absolute;
    inset: 0 0 auto 0;
    height: 760px;
    z-index: -1;
    pointer-events: none;
    background:
        radial-gradient(52% 60% at 14% 0%, rgba(25, 194, 255, 0.14) 0%, transparent 62%),
        radial-gradient(44% 50% at 86% 4%, rgba(10, 99, 246, 0.08) 0%, transparent 60%);
}

/* ----------------------------- Breadcrumb ------------------------------ */
.pi-crumbs {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--vs-ink-faint);
    margin-bottom: 26px;
}
.pi-crumbs a {
    color: var(--vs-ink-soft);
    transition: color 0.25s var(--vs-ease);
}
.pi-crumbs a:hover { color: var(--vs-brand); }
.pi-crumb-sep { font-size: 0.75rem; opacity: 0.5; }
.pi-crumbs [aria-current] { color: var(--vs-ink); }

/* -------------------------------- Hero --------------------------------- */
.pi-hero {
    position: relative;
    overflow: hidden;
    scroll-margin-top: 110px; /* "All" chip target sits clear of the fixed topbar */
    border-radius: 28px;
    border: 1px solid var(--vs-glass-border);
    background: var(--vs-glass-bg);
    -webkit-backdrop-filter: blur(var(--vs-glass-blur));
    backdrop-filter: blur(var(--vs-glass-blur));
    box-shadow: var(--vs-shadow);
    margin-bottom: 36px;
}
.pi-hero::before {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    background:
        radial-gradient(70% 90% at 0% 0%, rgba(25, 194, 255, 0.12) 0%, transparent 55%),
        radial-gradient(50% 70% at 100% 0%, rgba(10, 99, 246, 0.07) 0%, transparent 55%);
}

.pi-hero-inner {
    position: relative;
    z-index: 2;
    display: grid;
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
    gap: 40px;
    align-items: center;
    padding: 56px 56px 48px;
}

.pi-title {
    font-size: clamp(2.2rem, 5vw, 3.6rem);
    line-height: 1.06;
    font-weight: 800;
    letter-spacing: -0.03em;
    color: var(--vs-ink);
    margin: 18px 0 16px;
}

.pi-lead {
    font-size: clamp(1rem, 1.7vw, 1.15rem);
    color: var(--vs-ink-soft);
    max-width: 560px;
    margin: 0 0 28px;
}

.pi-hero-cta {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
}
.pi-hero-cta .vs-btn-primary:hover .fa { transform: translateY(4px); }

/* Orbiting category icons */
.pi-orbit {
    position: relative;
    width: min(320px, 100%);
    aspect-ratio: 1;
    margin: 0 auto;
}
.pi-orbit-ring {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    border: 1px dashed rgba(10, 22, 40, 0.18);
    animation: pi-spin 42s linear infinite;
}
.pi-orbit-ring--outer {
    border-style: solid;
    border-color: rgba(10, 22, 40, 0.07);
    animation-direction: reverse;
    animation-duration: 60s;
}
.pi-orbit-ring--inner { inset: 14%; }

.pi-orbit-sat {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 54px;
    height: 54px;
    margin: -27px 0 0 -27px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--vs-brand);
    font-size: 1.2rem;
    background: var(--vs-glass-bg-strong);
    border: 1px solid var(--vs-glass-border);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
    box-shadow: var(--vs-shadow);
    /* Place on the ring at angle --a; the ring's spin carries it around. */
    transform: rotate(var(--a)) translateX(calc(min(320px, 100%) * 0.36)) rotate(calc(-1 * var(--a)));
}
/* Counter-spin the icon so it stays upright while it orbits. */
.pi-orbit-sat .fa {
    animation: pi-spin 42s linear infinite reverse;
    color: var(--vs-brand);
}

.pi-orbit-core {
    position: absolute;
    inset: 33%;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-size: 2.3rem;
    background: var(--vs-brand);
    box-shadow: 0 20px 50px -12px rgba(10, 99, 246, 0.5);
    animation: pi-pulse 4.5s ease-in-out infinite;
}
.pi-orbit-core::after {
    content: "";
    position: absolute;
    inset: -14px;
    border-radius: 50%;
    border: 1px solid rgba(10, 99, 246, 0.35);
    animation: pi-ripple 4.5s ease-out infinite;
}

@keyframes pi-spin { to { transform: rotate(360deg); } }
@keyframes pi-pulse {
    0%, 100% { transform: scale(1); }
    50% { transform: scale(1.06); }
}
@keyframes pi-ripple {
    0% { transform: scale(0.8); opacity: 0.9; }
    100% { transform: scale(1.7); opacity: 0; }
}

/* Headline metrics */
.pi-stats {
    position: relative;
    z-index: 2;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    border-top: 1px solid var(--vs-glass-border);
    background: rgba(255, 255, 255, 0.5);
}
.pi-stat {
    position: relative;
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 22px 30px;
    border-right: 1px solid var(--vs-glass-border);
    cursor: pointer;
    outline-offset: -3px;
    transition: background-color 0.3s var(--vs-ease);
}
.pi-stat:last-child { border-right: 0; }
.pi-stat:hover,
.pi-stat:focus-within,
.pi-stat.is-open { background: rgba(255, 255, 255, 0.7); }

/* Little "more" cue at the far right of each stat */
.pi-stat-hint {
    margin-left: auto;
    width: 26px;
    height: 26px;
    border-radius: 999px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 0.8rem;
    color: var(--vs-ink-faint);
    border: 1px solid var(--vs-glass-border);
    transition: transform 0.3s var(--vs-ease), color 0.3s var(--vs-ease), border-color 0.3s var(--vs-ease);
}
.pi-stat:hover .pi-stat-hint,
.pi-stat:focus-within .pi-stat-hint,
.pi-stat.is-open .pi-stat-hint {
    color: var(--vs-brand);
    border-color: rgba(10, 99, 246, 0.5);
    transform: translateY(-2px);
}

/* Popover opens upward (the hero clips overflow, and there is room above) */
.pi-stat-pop {
    position: absolute;
    left: 18px;
    bottom: calc(100% + 12px);
    z-index: 40;
    width: min(360px, calc(100vw - 48px));
    max-height: 320px;
    /* The panel itself must not scroll. Its arrow (::after) is an absolutely
       positioned child hanging ~10px below the box, and under overflow:auto
       that overhang alone raised a scrollbar on all three popovers even when
       the content fitted with room to spare. The list inside scrolls instead,
       which also keeps the title pinned while the rows move. */
    display: flex;
    flex-direction: column;
    overflow: visible;
    padding: 16px;
    border-radius: 16px;
    background: #fff;
    border: 1px solid var(--vs-line);
    box-shadow: 0 24px 60px -20px rgba(10, 22, 40, 0.35);
    -webkit-backdrop-filter: blur(18px);
    backdrop-filter: blur(18px);
    cursor: default;
    opacity: 0;
    visibility: hidden;
    transform: translateY(10px) scale(0.98);
    transition: opacity 0.28s var(--vs-ease), transform 0.32s var(--vs-ease), visibility 0s linear 0.32s;
}
.pi-stat:last-child .pi-stat-pop { left: auto; right: 18px; }
.pi-stat-pop::after {
    content: "";
    position: absolute;
    left: 34px;
    bottom: -7px;
    width: 12px;
    height: 12px;
    background: #fff;
    border-right: 1px solid var(--vs-line);
    border-bottom: 1px solid var(--vs-line);
    transform: rotate(45deg);
}
.pi-stat:last-child .pi-stat-pop::after { left: auto; right: 34px; }
.pi-stat:hover .pi-stat-pop,
.pi-stat:focus-within .pi-stat-pop,
.pi-stat.is-open .pi-stat-pop {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition-delay: 0s;
}

.pi-pop-title {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 10px;
    font-size: 0.74rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--vs-ink-faint);
}
.pi-pop-title .fa { color: var(--vs-brand); }

.pi-pop-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

/* ------- The one scrolling region, shared by all three popovers ---------
   With the phantom overflow above gone, a bar only appears when the list is
   genuinely longer than the panel — a short catalogue shows none at all.
   When it does appear it is a slim brand-coloured rail rather than the
   browser's default chrome, and it no longer drags the page along with it
   once it bottoms out. */
.pi-pop-list,
.pi-pop-chips {
    min-height: 0;          /* lets the flex child shrink so it can scroll */
    overflow-y: auto;
    overscroll-behavior: contain;
    padding-right: 4px;
    scrollbar-width: thin;
    scrollbar-color: rgba(10, 99, 246, 0.5) transparent;
}
.pi-pop-list::-webkit-scrollbar,
.pi-pop-chips::-webkit-scrollbar { width: 6px; }
.pi-pop-list::-webkit-scrollbar-track,
.pi-pop-chips::-webkit-scrollbar-track {
    background: transparent;
    margin: 2px 0;
}
.pi-pop-list::-webkit-scrollbar-thumb,
.pi-pop-chips::-webkit-scrollbar-thumb {
    border-radius: 999px;
    background: var(--vs-brand-400);
}
.pi-pop-list::-webkit-scrollbar-thumb:hover,
.pi-pop-chips::-webkit-scrollbar-thumb:hover {
    background: var(--vs-brand);
}
.pi-pop-list a,
.pi-pop-list button {
    appearance: none;
    border: 0;
    font: inherit;
    text-align: left;
    width: 100%;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 10px;
    border-radius: 10px;
    color: var(--vs-ink);
    background: transparent;
    cursor: pointer;
    transition: background-color 0.25s var(--vs-ease), transform 0.25s var(--vs-ease);
}
.pi-pop-list a:hover,
.pi-pop-list button:hover,
.pi-pop-list a:focus-visible,
.pi-pop-list button:focus-visible {
    background: rgba(10, 22, 40, 0.05);
    transform: translateX(3px);
}
.pi-pop-dot {
    flex: 0 0 auto;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--brand, var(--vs-brand-400));
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand, var(--vs-brand-400)) 30%, transparent);
}
.pi-pop-icon {
    flex: 0 0 auto;
    width: 28px;
    height: 28px;
    border-radius: 8px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 0.85rem;
    color: #fff;
    background: var(--vs-brand);
}
.pi-pop-name {
    font-size: 0.92rem;
    font-weight: 700;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.pi-pop-meta {
    margin-left: auto;
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 0.74rem;
    font-weight: 600;
    color: var(--vs-ink-faint);
}
.pi-pop-meta .fa { color: var(--vs-brand); }

.pi-pop-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}
.pi-pop-chip {
    font-size: 0.76rem;
    font-weight: 600;
    padding: 5px 10px;
    border-radius: 999px;
    color: var(--vs-ink);
    background: var(--vs-paper);
    border: 1px solid var(--vs-line);
    transition: border-color 0.25s var(--vs-ease), background-color 0.25s var(--vs-ease);
}
.pi-pop-chip:hover {
    border-color: rgba(10, 99, 246, 0.5);
    background: rgba(10, 99, 246, 0.08);
}
.pi-pop-empty {
    margin: 0;
    font-size: 0.88rem;
    color: var(--vs-ink-soft);
}
.pi-stat .fa {
    font-size: 1.45rem;
    color: var(--vs-brand);
}
.pi-stat-num {
    display: block;
    font-size: 1.7rem;
    font-weight: 800;
    line-height: 1;
    color: var(--vs-ink);
    font-variant-numeric: tabular-nums;
}
.pi-stat-label {
    display: block;
    margin-top: 4px;
    font-size: 0.76rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--vs-ink-faint);
}

/* ------------------------- Sticky category nav ------------------------- */
.pi-nav {
    position: sticky;
    top: 84px;
    z-index: 30;
    display: flex;
    justify-content: center;
    margin: 0 0 48px;
}
.pi-nav-inner {
    display: inline-flex;
    /* Every category has to be readable, however many there are and however
       long their names run. The row is capped by the page container well
       before the viewport, so a single line cannot be made to fit by
       tightening it; it wraps instead, and nothing is ever cut off.
       26px is half the height of one row, so a single row still draws as the
       same pill it always was, and two or more round off as a panel. */
    flex-wrap: wrap;
    justify-content: center;
    gap: 6px;
    padding: 6px;
    /* The full width of the column, so the bar lines up with the hero card
       above it. That also settles the older problem: letting the pill
       shrink-wrap its chips made it change size on every visit, because which
       two categories it draws is decided fresh on each render. */
    width: 100%;
    position: relative;   /* the More menu hangs off the bar, not off the button */
    border-radius: 26px;
    background: var(--vs-glass-bg-strong);
    border: 1px solid var(--vs-glass-border);
    -webkit-backdrop-filter: blur(18px);
    backdrop-filter: blur(18px);
    box-shadow: var(--vs-shadow);
    scrollbar-width: none;
}
.pi-nav-inner::-webkit-scrollbar { display: none; }

.pi-chip {
    appearance: none;
    border: 0;
    background: transparent;
    font: inherit;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 10px 18px;
    border-radius: 999px;
    font-size: 0.88rem;
    font-weight: 700;
    color: var(--vs-ink-soft);
    white-space: nowrap;
    max-width: 420px;
    overflow: hidden;
    transition: background-color 0.3s var(--vs-ease), color 0.3s var(--vs-ease),
        transform 0.2s var(--vs-ease), box-shadow 0.3s var(--vs-ease),
        max-width 0.42s var(--vs-ease), padding 0.42s var(--vs-ease),
        margin 0.42s var(--vs-ease), opacity 0.26s var(--vs-ease);
}

/* Off the bar, but still on the page: a tucked chip squeezes to nothing
   instead of disappearing, so bringing it back is one class and reads as the
   row making room. The negative margin swallows the flex gap a zero-width
   item would otherwise still claim. */
/* Two category chips have to share one line inside that width. Rather than
   cap them at a number that stops being right the moment "All" gains a digit,
   let them give way: the label is the only part that may shrink, and it takes
   an ellipsis when it has to, so a long category name never pushes the bar
   onto a second row. */
.pi-chip[data-pi-chip]:not([data-pi-chip="all"]) {
    flex: 1 1 auto;
    min-width: 0;
    justify-content: center;   /* the label sits in the middle of its slot */
}
.pi-chip-icon,
.pi-chip-check,
.pi-chip-count { flex: 0 0 auto; }
.pi-chip-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

.pi-chip.is-tucked {
    max-width: 0;
    padding-left: 0;
    padding-right: 0;
    margin-left: -6px;
    opacity: 0;
    pointer-events: none;
}

/* ------------------------------ More menu ------------------------------ */
.pi-chip-more { font-weight: 700; }
.pi-chip-more:hover,
.pi-nav-inner.is-more-open .pi-chip-more {
    color: var(--vs-ink);
    background: rgba(10, 22, 40, 0.06);
}
.pi-more-caret {
    font-size: 0.82rem;
    transition: transform 0.3s var(--vs-ease);
}
.pi-nav-inner.is-more-open .pi-more-caret { transform: rotate(180deg); }

/* Two elements, not one. The outer box reaches all the way up to the bar and
   its padding is the gap, so a pointer travelling down from More never leaves
   the menu and the card cannot close under the cursor halfway. The card has
   to be the inner one: it scrolls, and anything the outer box tried to put in
   that gap would be clipped by its own overflow. */
.pi-more-menu {
    position: absolute;
    top: 100%;
    right: 4px;
    z-index: 40;
    width: 264px;
    max-width: calc(100vw - 48px);
    padding-top: 10px;
    transform-origin: top right;
    animation: pi-more-in 0.24s var(--vs-ease);
}
.pi-more-menu[hidden] { display: none; }

.pi-more-card {
    display: flex;
    flex-direction: column;
    gap: 2px;
    max-height: min(58vh, 340px);
    overflow-y: auto;
    padding: 6px;
    border-radius: 18px;
    /* Opaque on purpose. The bar above already carries a backdrop-filter,
       which makes it a backdrop root, so a blur declared down here has
       nothing to sample — glass would just be a tinted window onto the
       project cards behind, and the category names stop being readable. */
    background: #fff;
    border: 1px solid var(--vs-line);
    box-shadow: 0 24px 50px -16px rgba(10, 22, 40, 0.35);
    scrollbar-width: thin;
}

@keyframes pi-more-in {
    0% { opacity: 0; transform: translateY(-8px) scale(0.97); }
    100% { opacity: 1; transform: none; }
}

.pi-more-item {
    appearance: none;
    border: 0;
    background: transparent;
    font: inherit;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 10px 12px;
    border-radius: 12px;
    font-size: 0.86rem;
    font-weight: 600;
    text-align: left;
    color: var(--vs-ink-soft);
    transition: background-color 0.25s var(--vs-ease), color 0.25s var(--vs-ease);
}
.pi-more-item:hover { background: rgba(10, 22, 40, 0.05); color: var(--vs-ink); }
.pi-more-item.is-active {
    color: #fff;
    background: var(--vs-navy);
}
.pi-more-item.is-active .pi-chip-count { background: rgba(255, 255, 255, 0.22); }
.pi-more-icon { width: 16px; text-align: center; }
.pi-more-label {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.pi-chip:hover {
    color: var(--vs-ink);
    background: rgba(10, 22, 40, 0.06);
    transform: translateY(-1px);
}
.pi-chip.is-active {
    color: #fff;
    background: var(--vs-navy);
    box-shadow: var(--vs-shadow-brand);
}
.pi-chip-count {
    font-size: 0.7rem;
    padding: 2px 8px;
    border-radius: 999px;
    background: rgba(10, 22, 40, 0.07);
}
.pi-chip.is-active .pi-chip-count { background: rgba(255, 255, 255, 0.22); }
.pi-chip:active { transform: scale(0.97); }

/* Selected chips swap their icon for a check so the multi-select reads clearly */
.pi-chip-check { display: none; }
.pi-chip.is-active .pi-chip-icon { display: none; }
.pi-chip.is-active .pi-chip-check {
    display: inline-block;
    animation: pi-pop-in 0.3s var(--vs-ease);
}
@keyframes pi-pop-in {
    0% { transform: scale(0.4) rotate(-30deg); opacity: 0; }
    100% { transform: none; opacity: 1; }
}

/* Live result line under the filter row */
.pi-result {
    margin: -30px 0 40px;
    text-align: center;
    font-size: 0.86rem;
    font-weight: 600;
    color: var(--vs-ink-faint);
    transition: color 0.3s var(--vs-ease);
}

/* ------------------------------ Groups --------------------------------- */
.pi-group {
    scroll-margin-top: var(--pi-stop, 156px);
    margin-bottom: 76px;
}
.pi-group:last-of-type { margin-bottom: 0; }
.pi-group.is-hidden { display: none; }
.pi-group.is-entering { animation: pi-group-in 0.45s var(--vs-ease); }
@keyframes pi-group-in {
    0% { opacity: 0; transform: translateY(18px); }
    100% { opacity: 1; transform: none; }
}

.pi-group-head {
    display: flex;
    align-items: center;
    gap: 18px;
    margin-bottom: 28px;
}

.pi-group-icon {
    flex: 0 0 auto;
    width: 56px;
    height: 56px;
    border-radius: 16px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.4rem;
    color: #fff;
    background: var(--vs-navy);
    box-shadow: var(--vs-shadow-brand);
    transition: transform 0.4s var(--vs-ease);
}
.pi-group-head:hover .pi-group-icon { transform: rotate(-6deg) scale(1.06); }
.pi-group[data-hue="2"] .pi-group-icon {
    background: var(--vs-brand);
    box-shadow: 0 14px 30px -12px rgba(10, 99, 246, 0.55);
}
.pi-group[data-hue="3"] .pi-group-icon {
    background: linear-gradient(135deg, var(--vs-cyan), var(--vs-brand));
    box-shadow: 0 14px 30px -12px rgba(10, 99, 246, 0.45);
}

.pi-group-text { min-width: 0; }

.pi-group-title {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
    margin: 0;
    font-size: clamp(1.4rem, 2.6vw, 1.9rem);
    font-weight: 800;
    letter-spacing: -0.02em;
    color: var(--vs-ink);
}

.pi-count {
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    color: var(--vs-brand-600);
    background: rgba(10, 99, 246, 0.08);
    border: 1px solid rgba(10, 99, 246, 0.22);
    padding: 3px 10px;
    border-radius: 999px;
}

.pi-group-desc {
    margin: 4px 0 0;
    font-size: 0.95rem;
    color: var(--vs-ink-faint);
}

/* Hairline rule that draws itself in once the header is revealed */
.pi-group-line {
    flex: 1;
    height: 1px;
    margin-left: 10px;
    background: linear-gradient(90deg, rgba(10, 22, 40, 0.18), rgba(10, 99, 246, 0.2), transparent);
    transform-origin: left center;
    transform: scaleX(0);
    transition: transform 1.1s var(--vs-ease) 0.25s;
}
.pi-group-head.is-visible .pi-group-line { transform: scaleX(1); }

/* ------------------------------- Grid ---------------------------------- */
/* Every tile is the same size: the tracks share one width and the row is
   pinned to a single height, so the catalogue reads as an even wall of cards
   however long any one project's copy runs. */
.pi-grid {
    /* Taller than the copy strictly needs: the extra room is what lets the
       description breathe over three lines and carry a couple of real
       features instead of only a count. */
    --pi-tile-h: 560px;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 320px), 1fr));
    gap: 26px;
    align-items: stretch;
}

.pi-item { height: var(--pi-tile-h); }

/* Staggered reveal on the wrapper (the tile keeps its own hover transition) */
.pi-item[data-reveal] {
    opacity: 0;
    transform: translateY(34px) scale(0.97);
    transition: opacity 0.5s var(--vs-ease), transform 0.6s var(--vs-ease);
    transition-delay: calc(min(var(--i, 0), 6) * 55ms);
}
.pi-item[data-reveal].is-visible {
    opacity: 1;
    transform: none;
}

/* ------------------------------- Tile ---------------------------------- */
.pi-tile {
    position: relative;
    /* A flex column with the body at the foot. The body is deliberately left
       unpositioned so the stretched link inside it resolves against this box
       and covers the whole tile; z-index still applies to it as a flex item. */
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    height: 100%;
    border-radius: 20px;
    overflow: hidden;
    isolation: isolate;
    background: var(--brand-dark, #0a2b3d);
    border: 1px solid rgba(var(--brand-rgb, 0, 154, 255), 0.34);
    box-shadow: 0 30px 60px -30px rgba(10, 22, 40, 0.45),
                0 0 34px -18px rgba(var(--brand-rgb, 0, 154, 255), 0.45);
    transition: transform 0.5s var(--vs-ease), box-shadow 0.5s var(--vs-ease),
        border-color 0.5s var(--vs-ease);
}
/* Brand accent along the top edge — the one bit of pure colour that survives
   the veil, so a wall of tiles reads as a set of distinct products. */
.pi-tile::before {
    content: "";
    position: absolute;
    z-index: 6;
    /* Above the stretched link, so it must not swallow clicks on the top edge. */
    pointer-events: none;
    top: 0;
    left: 0;
    right: 0;
    height: 3px;
    background: linear-gradient(90deg,
        var(--brand-light, #7fd2f5), var(--brand, #009aff), transparent);
    opacity: 0.75;
    transition: opacity 0.4s var(--vs-ease);
}
.pi-tile:hover::before,
.pi-tile:focus-within::before { opacity: 1; }

.pi-tile:hover,
.pi-tile:focus-within {
    transform: translateY(-8px);
    border-color: rgba(var(--brand-rgb, 0, 154, 255), 0.7);
    box-shadow: 0 40px 70px -30px rgba(10, 22, 40, 0.5),
                0 0 60px -12px rgba(var(--brand-rgb, 0, 154, 255), 0.6);
}

/* Media -------------------------------------------------------------- */
/* A banner band across the top rather than the whole tile. The banners are
   landscape (roughly 1.5:1 to 1.75:1) and the tile is portrait, so stretching
   one over the full height cropped it to a narrow middle column and enlarged
   it ~2.7x — enough that "School Management" arrived reading "NAGEMENT".
   At this height the band is close to the banners' own shape, so the artwork
   reads as the picture it was cropped from. */
.pi-tile-media {
    position: absolute;
    inset: 0 0 auto 0;
    height: 42%;
    z-index: 0;
    /* The hover zoom below scales the image past the band; without this it
       would spill over the copy underneath. */
    overflow: hidden;
    background: linear-gradient(150deg, var(--brand, #009aff), var(--brand-dark, #072736));
}
.pi-tile-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    transition: transform 0.7s var(--vs-ease);
}
.pi-tile:hover .pi-tile-img,
.pi-tile:focus-within .pi-tile-img { transform: scale(1.09); }

/* Banner-less projects: the brand panel is the artwork. */
.pi-tile-emblem {
    position: absolute;
    /* The band is the artwork area now, so this fills its parent. */
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: transform 0.7s var(--vs-ease);
}
/* A logo is artwork drawn for a light surface — the ones in this catalogue
   run dark — so on a dark brand panel it needs one of its own to sit on.
   The icon fallback below is drawn light and already reads against the
   panel, so it is deliberately left on it. */
.pi-tile-emblem img {
    width: auto;
    max-width: 74%;
    max-height: 68%;
    object-fit: contain;
    padding: 14px 18px;
    border-radius: 16px;
    background: rgba(255, 255, 255, 0.94);
    box-shadow: 0 12px 28px rgba(0, 0, 0, 0.38);
}
.pi-tile-emblem .fa {
    font-size: 4.2rem;
    color: rgba(255, 255, 255, 0.9);
}
.pi-tile:hover .pi-tile-emblem,
.pi-tile:focus-within .pi-tile-emblem { transform: scale(1.07); }

/* Two jobs, top to bottom: a scrim under the category badge so it stays
   readable over a bright banner, and the brand panel the copy sits on, which
   rises to meet the foot of the band and feathers into it rather than ending
   on a hard line. The band is 42%, so the panel is opaque from 46% down. */
.pi-tile-veil {
    position: absolute;
    inset: 0;
    z-index: 1;
    background:
        /* Scrim under the category badge. */
        linear-gradient(to bottom,
            rgba(6, 8, 12, 0.58) 0%,
            rgba(6, 8, 12, 0.16) 14%,
            rgba(6, 8, 12, 0) 26%),
        /* Depth, so the panel does not read as one flat wash of brand — and
           so white copy keeps its contrast even on a light brand colour. */
        linear-gradient(to bottom,
            rgba(4, 6, 10, 0) 30%,
            rgba(4, 6, 10, 0.20) 44%,
            rgba(4, 6, 10, 0.46) 64%,
            rgba(4, 6, 10, 0.58) 100%),
        /* The brand panel itself. One unbroken ramp to full opacity: an
           earlier draft jumped from a translucent brand to an opaque
           --brand-dark across 5%, which read as a seam on the tiles that
           show an emblem rather than a photograph. */
        linear-gradient(to bottom,
            rgba(var(--brand-rgb, 0, 154, 255), 0) 24%,
            rgba(var(--brand-rgb, 0, 154, 255), 0.45) 34%,
            rgba(var(--brand-rgb, 0, 154, 255), 0.86) 42%,
            rgba(var(--brand-rgb, 0, 154, 255), 1) 48%,
            rgba(var(--brand-rgb, 0, 154, 255), 1) 100%);
    transition: opacity 0.5s var(--vs-ease);
}
.pi-tile:hover .pi-tile-veil,
.pi-tile:focus-within .pi-tile-veil { opacity: 0.92; }

/* Top row ------------------------------------------------------------ */
.pi-tile-top {
    position: absolute;
    z-index: 3;
    top: 14px;
    left: 14px;
    right: 14px;
    display: flex;
    align-items: center;
    gap: 8px;
}
.pi-tile-badge {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    max-width: 100%;
    padding: 6px 11px;
    border-radius: 999px;
    font-size: 0.68rem;
    font-weight: 800;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: #fff;
    background: rgba(8, 10, 14, 0.6);
    border: 1px solid rgba(255, 255, 255, 0.18);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.pi-tile-badge .fa { color: var(--brand-light, #7fd2f5); }

.pi-tile-mobile {
    margin-left: auto;
    flex: 0 0 auto;
    width: 28px;
    height: 28px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 9px;
    font-size: 0.9rem;
    color: #fff;
    background: rgba(8, 10, 14, 0.6);
    border: 1px solid rgba(255, 255, 255, 0.18);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
}

/* Body --------------------------------------------------------------- */
.pi-tile-body {
    /* Unpositioned on purpose — see .pi-tile above. */
    z-index: 2;
    display: flex;
    flex-direction: column;
    padding: 18px;
    color: #fff;
}

.pi-tile-name {
    margin: 0;
    font-size: 1.32rem;
    font-weight: 800;
    letter-spacing: -0.02em;
    line-height: 1.2;
    /* Two lines maximum, so a long product name cannot push the tile's
       lower half around. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.pi-tile-link {
    color: inherit;
    text-decoration: none;
}
/* Stretches the anchor across the whole tile without nesting links. */
.pi-tile-link::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 4;
}
.pi-tile-link:focus-visible { outline: none; }
.pi-tile:focus-within .pi-tile-name { text-decoration: underline; text-underline-offset: 3px; }

.pi-tile-meta {
    margin: 6px 0 0;
    font-size: 0.78rem;
    font-weight: 600;
    color: rgba(255, 255, 255, 0.82);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.pi-tile-desc {
    margin: 9px 0 0;
    font-size: 0.86rem;
    line-height: 1.55;
    color: rgba(255, 255, 255, 0.78);
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* Feature preview: two lines of what the product actually does. */
.pi-tile-feats {
    list-style: none;
    margin: 12px 0 0;
    padding: 0;
    display: grid;
    gap: 6px;
}
.pi-tile-feats li {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    font-size: 0.79rem;
    line-height: 1.4;
    color: rgba(255, 255, 255, 0.86);
    /* Grid items default to min-width:auto, which refuses to shrink below the
       text and pushed long features past the tile's edge. */
    min-width: 0;
    overflow: hidden;
}
.pi-tile-feats li span {
    /* One line each: a long feature is trimmed rather than shoving the CTA
       past the foot of a fixed-height tile. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}
.pi-tile-feats .fa {
    flex: 0 0 auto;
    margin-top: 2px;
    font-size: 0.68rem;
    color: var(--brand-light, #7fd2f5);
}

/* Sheen: a slow diagonal highlight that crosses the tile on hover. */
.pi-tile-sheen {
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    background: linear-gradient(115deg,
        transparent 38%,
        rgba(255, 255, 255, 0.13) 50%,
        transparent 62%);
    transform: translateX(-120%);
    opacity: 0;
    transition: transform 0.85s var(--vs-ease), opacity 0.35s var(--vs-ease);
}
.pi-tile:hover .pi-tile-sheen,
.pi-tile:focus-within .pi-tile-sheen {
    transform: translateX(120%);
    opacity: 1;
}

.pi-tile-tech {
    display: flex;
    gap: 6px;
    margin-top: 12px;
    /* One row only — the overflow is counted by .pi-tile-chip--more. */
    flex-wrap: nowrap;
    overflow: hidden;
}
.pi-tile-chip {
    flex: 0 1 auto;
    padding: 4px 9px;
    border-radius: 999px;
    font-size: 0.68rem;
    font-weight: 700;
    color: #fff;
    background: rgba(255, 255, 255, 0.14);
    border: 1px solid rgba(255, 255, 255, 0.2);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.pi-tile-chip--more {
    flex: 0 0 auto;
    background: rgba(255, 255, 255, 0.06);
    color: rgba(255, 255, 255, 0.8);
}

/* Call to action ------------------------------------------------------ */
.pi-tile-cta {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-top: 16px;
    padding: 11px 14px;
    border-radius: 12px;
    font-size: 0.82rem;
    font-weight: 800;
    letter-spacing: 0.02em;
    color: #fff;
    background: rgba(255, 255, 255, 0.14);
    border: 1px solid rgba(255, 255, 255, 0.22);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
    transition: background-color 0.35s var(--vs-ease), border-color 0.35s var(--vs-ease);
}
.pi-tile:hover .pi-tile-cta,
.pi-tile:focus-within .pi-tile-cta {
    background: rgba(255, 255, 255, 0.26);
    border-color: rgba(255, 255, 255, 0.42);
}
.pi-tile-cta .fa { transition: transform 0.35s var(--vs-ease); }
.pi-tile:hover .pi-tile-cta .fa,
.pi-tile:focus-within .pi-tile-cta .fa { transform: translateX(5px); }

/* Live demo: its own layer above the stretched link. */
.pi-tile-demo {
    position: absolute;
    z-index: 5;
    right: 14px;
    top: 52px;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 7px 11px;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 800;
    text-decoration: none;
    color: #fff;
    background: rgba(8, 10, 14, 0.66);
    border: 1px solid rgba(255, 255, 255, 0.2);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
    opacity: 0;
    transform: translateY(-6px);
    transition: opacity 0.35s var(--vs-ease), transform 0.35s var(--vs-ease),
        background-color 0.35s var(--vs-ease);
}
.pi-tile:hover .pi-tile-demo,
.pi-tile:focus-within .pi-tile-demo,
.pi-tile-demo:focus-visible {
    opacity: 1;
    transform: none;
}
.pi-tile-demo:hover {
    background: var(--brand, #009aff);
    border-color: rgba(255, 255, 255, 0.4);
}

/* Touch devices have no hover to reveal it, so it stays put. */
@media (hover: none) {
    .pi-tile-demo { opacity: 1; transform: none; }
}

/* ----------------------------- Empty state ----------------------------- */
.pi-empty {
    text-align: center;
    padding: 80px 24px;
    border-radius: 24px;
    border: 1px dashed rgba(10, 22, 40, 0.2);
    background: var(--vs-glass-bg);
    color: var(--vs-ink-soft);
}
.pi-empty .fa {
    font-size: 2.6rem;
    margin-bottom: 14px;
    color: var(--vs-brand);
}
.pi-empty-title {
    margin: 0 0 8px;
    font-size: 1.4rem;
    font-weight: 800;
    color: var(--vs-ink);
}
.pi-empty p { margin: 0; }

/* ----------------------------- Closing band ---------------------------- */
.pi-band {
    margin-top: 72px;
    padding: 40px 44px;
    border-radius: 24px;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 24px;
    /* Dark navy card with a blue glow, as the CTA on appdeft.ai. */
    --vs-ink: #ffffff;
    --vs-ink-soft: rgba(255, 255, 255, 0.74);
    --vs-ink-faint: rgba(255, 255, 255, 0.58);
    --vs-line: rgba(255, 255, 255, 0.12);
    --vs-btn-bg: #ffffff;
    --vs-btn-fg: var(--vs-navy);
    background:
        radial-gradient(90% 160% at 0% 0%, rgba(10, 99, 246, 0.45) 0%, transparent 60%),
        radial-gradient(60% 120% at 100% 100%, rgba(25, 194, 255, 0.18) 0%, transparent 60%),
        var(--vs-navy);
    color: var(--vs-ink);
    border: 1px solid rgba(255, 255, 255, 0.10);
    box-shadow: 0 30px 60px -30px rgba(10, 22, 40, 0.6);
}
.pi-band-title {
    margin: 0 0 6px;
    font-size: clamp(1.3rem, 2.4vw, 1.7rem);
    font-weight: 800;
    letter-spacing: -0.02em;
    color: var(--vs-ink);
}
.pi-band-sub {
    margin: 0;
    color: var(--vs-ink-soft);
}
.pi-band-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
}

/* ------------------------------ Back to top ---------------------------- */
.pi-top {
    position: fixed;
    right: 26px;
    bottom: 26px;
    z-index: 900;
    width: 48px;
    height: 48px;
    border-radius: 999px;
    border: 1px solid var(--vs-glass-border);
    background: var(--vs-navy);
    color: #fff;
    font-size: 1.2rem;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    -webkit-backdrop-filter: blur(12px);
    backdrop-filter: blur(12px);
    box-shadow: 0 14px 34px -10px rgba(10, 22, 40, 0.5);
    opacity: 0;
    transform: translateY(14px);
    pointer-events: none;
    transition: opacity 0.3s var(--vs-ease), transform 0.3s var(--vs-ease), background-color 0.3s;
}
.pi-top.is-shown {
    opacity: 1;
    transform: none;
    pointer-events: auto;
}
.pi-top:hover {
    background: var(--vs-brand);
    border-color: transparent;
}

/* ------------------------------ Responsive ----------------------------- */
@media (max-width: 960px) {
    .pi-hero-inner {
        grid-template-columns: 1fr;
        padding: 44px 36px 40px;
    }
    .pi-orbit { width: min(240px, 100%); }
    .pi-orbit-sat { transform: rotate(var(--a)) translateX(86px) rotate(calc(-1 * var(--a))); }
}

@media (max-width: 767px) {
    .pi-section { padding: 108px 0 72px; }
    .pi-hero-inner { padding: 36px 24px 32px; }
    .pi-orbit { display: none; }
    .pi-stats { grid-template-columns: 1fr; }
    .pi-stat { border-right: 0; border-bottom: 1px solid var(--vs-glass-border); }
    .pi-stat:last-child { border-bottom: 0; }
    .pi-stat-pop,
    .pi-stat:last-child .pi-stat-pop { left: 12px; right: 12px; width: auto; max-height: 260px; }
    .pi-stat:last-child .pi-stat-pop::after { left: 34px; right: auto; }
    .pi-result { margin: -22px 0 32px; }
    /* The bar is four items now, so it wraps to two rows at worst and no
       longer needs to scroll sideways — which also means it can stop clipping
       its own overflow and let the More menu out. */
    .pi-nav { top: 72px; }
    .pi-more-menu { left: 4px; right: 4px; width: auto; max-width: none; }
    .pi-group { scroll-margin-top: var(--pi-stop, 140px); margin-bottom: 56px; }
    .pi-group-head { gap: 14px; }
    .pi-group-line { display: none; }
    .pi-grid { gap: 22px; --pi-tile-h: 520px; }
    .pi-band { padding: 30px 24px; }
    .pi-band-actions .vs-btn { width: 100%; }
    .pi-top { right: 16px; bottom: 16px; }
}

/* ---------------------------- Reduced motion --------------------------- */
@media (prefers-reduced-motion: reduce) {
    .pi-orbit-ring,
    .pi-orbit-sat .fa,
    .pi-orbit-core,
    .pi-orbit-core::after { animation: none; }
    .pi-item[data-reveal],
    .pi-group-line { opacity: 1; transform: none; transition: none; }
    .pi-group.is-entering,
    .pi-more-menu,
    .pi-chip.is-active .pi-chip-check { animation: none; }
    .pi-chip { transition: none; }
    .pi-more-caret { transition: none; }

    /* Tiles keep their colour changes but drop every movement. */
    .pi-tile,
    .pi-tile-img,
    .pi-tile-emblem,
    .pi-tile-cta .fa { transition: none; }
    .pi-tile:hover,
    .pi-tile:focus-within { transform: none; }
    .pi-tile:hover .pi-tile-img,
    .pi-tile:focus-within .pi-tile-img,
    .pi-tile:hover .pi-tile-emblem,
    .pi-tile:focus-within .pi-tile-emblem { transform: none; }
    .pi-tile:hover .pi-tile-cta .fa,
    .pi-tile:focus-within .pi-tile-cta .fa { transform: none; }
    .pi-tile-demo { transition: none; }
    .pi-tile-sheen { display: none; }
}
