/*
 * Growth Radar — the panel's own layer.
 *
 * This file is hand-written plain CSS, not Tailwind, and it is served straight
 * from `public/`: the panel has no Node build (plan §11, docs/DECISIONS.md
 * D-036), so a utility class invented here would never be compiled into
 * anything. It is loaded from the PanelsRenderHook::STYLES_AFTER hook, which
 * puts it after Filament's own stylesheet — that ordering is what lets these
 * rules win without `!important`.
 *
 * Most of the brand arrives without any help from this file: App\Filament\Brand
 * registers the ramps, and Filament's stylesheet already draws everything from
 * `--primary-*`, `--gray-*` and friends. What is left is the part the framework
 * has no opinion about — navigation that stays Midnight in both themes, the
 * radar motifs, and a few restraints — and the `--gr-*` tokens the rest of the
 * application refers to instead of repeating a hex.
 *
 * Contents:
 *   1. Tokens
 *   2. Restraint — radii, elevation, type
 *   3. Navigation: the Midnight chrome
 *   4. The brand mark
 *   5. Focus
 *   6. Radar motifs
 *   7. Dense views on small screens
 */

/* ------------------------------------------------------------------ *
 * 1. Tokens
 * ------------------------------------------------------------------ */

:root {
    /* The brand's seven colours. App\Filament\Brand holds the same values for
       PHP; BrandTest fails if the two drift apart. */
    --gr-midnight: #0b1f1a;
    --gr-radar-green: #00e676;
    --gr-mint: #7cffb2;
    --gr-pale: #e6f9f0;
    --gr-slate: #94a3b8;
    --gr-charcoal: #1e293b;
    --gr-white: #ffffff;

    /*
     * Semantic tokens. These follow the theme; the brand colours above do not.
     * They are the names the rest of the application should reach for — a
     * Blade view, a future custom page — rather than a hex or a `--gray-N`.
     *
     * Deliberately a superset of what this file consumes: Filament's own
     * stylesheet already paints most surfaces from its variables, so several of
     * these have no rule here yet. They are the published vocabulary, not dead
     * declarations, and BrandTest holds the seven brand colours above them.
     */
    --gr-background: var(--gray-50);
    --gr-surface: var(--gr-white);
    --gr-surface-raised: var(--gray-50);
    --gr-primary: var(--primary-600);
    --gr-primary-hover: var(--primary-700);
    --gr-text-primary: var(--gray-950);
    --gr-text-secondary: var(--gray-500);
    --gr-border: var(--gray-200);
    --gr-success: var(--success-600);
    --gr-warning: var(--warning-600);
    --gr-danger: var(--danger-600);
    --gr-info: var(--info-600);

    /* The navigation is Midnight in both themes, so its tokens do not flip. */
    --gr-nav-bg: var(--gr-midnight);
    --gr-nav-raised: #16302a;
    --gr-nav-text: #dce3e4;
    --gr-nav-text-strong: var(--gr-white);
    --gr-nav-text-muted: var(--gr-slate);
    --gr-nav-accent: var(--gr-radar-green);
    --gr-nav-border: rgb(255 255 255 / 0.08);

    /*
     * Copies of the theme ramp, taken here at the root. §3 redefines `--gray-*`
     * inside the navigation, and a popover in there needs the page's values
     * back; it cannot say `--gray-50: var(--gray-50)`, which is circular. These
     * resolve once against the root and are inherited as plain colours, so they
     * survive the override below them.
     */
    --gr-page-gray-50: var(--gray-50);
    --gr-page-gray-100: var(--gray-100);
    --gr-page-gray-200: var(--gray-200);
    --gr-page-gray-300: var(--gray-300);
    --gr-page-gray-400: var(--gray-400);
    --gr-page-gray-500: var(--gray-500);
    --gr-page-gray-600: var(--gray-600);
    --gr-page-gray-700: var(--gray-700);
    --gr-page-gray-800: var(--gray-800);
    --gr-page-gray-900: var(--gray-900);
    --gr-page-gray-950: var(--gray-950);
    --gr-page-primary-600: var(--primary-600);
    --gr-page-primary-700: var(--primary-700);
}

.dark {
    --gr-background: var(--gray-950);
    --gr-surface: var(--gray-900);
    --gr-surface-raised: var(--gray-800);
    --gr-primary: var(--primary-400);
    --gr-primary-hover: var(--primary-300);
    --gr-text-primary: var(--gr-white);
    --gr-text-secondary: var(--gray-400);
    --gr-border: rgb(255 255 255 / 0.1);
    --gr-success: var(--success-400);
    --gr-warning: var(--warning-400);
    --gr-danger: var(--danger-400);
    --gr-info: var(--info-400);
}

/* ------------------------------------------------------------------ *
 * 2. Restraint — radii, elevation, type
 * ------------------------------------------------------------------ */

/* Every large radius in the panel comes from this one token, so softening the
   whole interface by an eighth of a rem is a single edit rather than a sweep
   through component rules. */
:root {
    --radius-xl: 0.625rem;
}

/* Sections carry a border and no shadow. An intelligence view is mostly
   stacked panels; drop shadows on all of them turn the page to soup. */
.fi-body .fi-section:not(.fi-section-not-contained):not(.fi-aside),
.fi-body .fi-section:not(.fi-section-not-contained).fi-aside > .fi-section-content-ctn {
    box-shadow: none;
}

/* Category and group labels: the one place the brand uses uppercase, and it
   needs the tracking to stay readable at 12px. */
.fi-sidebar-group-label,
.fi-ta-header-cell-sort-btn,
.fi-ta-header-cell > span {
    letter-spacing: 0.04em;
}

.fi-sidebar-group-label {
    font-size: 0.6875rem;
    text-transform: uppercase;
}

/* Headings lose the default tracking-normal so they read as the board's
   wordmark does — tight, not condensed. */
.fi-section-header-heading,
.fi-header-heading,
.fi-ta-header-heading,
.fi-wi-stats-overview-stat-value {
    letter-spacing: -0.011em;
}

/* ------------------------------------------------------------------ *
 * 3. Navigation: the Midnight chrome
 * ------------------------------------------------------------------ */

/*
 * The sidebar and topbar are Midnight whichever theme is on.
 *
 * `.fi-body` on the front of each selector is deliberate. It outweighs
 * Filament's own `.fi-sidebar { lg:bg-transparent }` without `!important`.
 */
.fi-body .fi-sidebar,
.fi-body .fi-topbar,
.fi-body-has-topbar .fi-sidebar-header {
    background-color: var(--gr-nav-bg);
    color: var(--gr-nav-text);
}

/*
 * In dark mode Filament's own rules already suit a dark surface and are left
 * alone. In light mode they do not: the navigation would be dark text on a dark
 * ground. Rather than chase every `text-gray-700` Filament writes into its
 * navigation rules, this inverts the ramp for that subtree — the compiled CSS
 * reads `var(--gray-700)` directly, so redefining it here re-tints every
 * descendant at once, including the rules not named below.
 */
html:not(.dark) .fi-body .fi-sidebar,
html:not(.dark) .fi-body .fi-topbar,
html:not(.dark) .fi-body-has-topbar .fi-sidebar-header {
    --gray-950: var(--gr-nav-text-strong);
    --gray-900: #eef2f2;
    --gray-800: #dce3e4;
    --gray-700: var(--gr-nav-text);
    --gray-600: #c2cdd1;
    --gray-500: var(--gr-nav-text-muted);
    --gray-400: var(--gr-nav-text-muted);
    --gray-300: #5f707f;
    --gray-200: #384753;
    --gray-100: var(--gr-nav-raised);
    --gray-50: var(--gr-charcoal);

    /* Light-mode rules reach for primary-600/700, which are the dark greens and
       vanish on Midnight. In here the accent is Radar Green, which clears
       10:1 against it. */
    --primary-700: var(--gr-nav-accent);
    --primary-600: var(--gr-nav-accent);
}

/* A popover rendered inside the navigation is still a popover: it sits on the
   page, not on the chrome, so it gets the page's ramp back. Without this the
   global-search results would be white on white. */
html:not(.dark) .fi-body .fi-sidebar .fi-dropdown-panel,
html:not(.dark) .fi-body .fi-topbar .fi-dropdown-panel,
html:not(.dark) .fi-body .fi-sidebar .fi-global-search-results-ctn,
html:not(.dark) .fi-body .fi-topbar .fi-global-search-results-ctn {
    --gray-950: var(--gr-page-gray-950);
    --gray-900: var(--gr-page-gray-900);
    --gray-800: var(--gr-page-gray-800);
    --gray-700: var(--gr-page-gray-700);
    --gray-600: var(--gr-page-gray-600);
    --gray-500: var(--gr-page-gray-500);
    --gray-400: var(--gr-page-gray-400);
    --gray-300: var(--gr-page-gray-300);
    --gray-200: var(--gr-page-gray-200);
    --gray-100: var(--gr-page-gray-100);
    --gray-50: var(--gr-page-gray-50);
    --primary-700: var(--gr-page-primary-700);
    --primary-600: var(--gr-page-primary-600);
    color: var(--gr-text-primary);
}

/*
 * The seam. In dark mode the page behind the navigation is Midnight too, so
 * without a hairline the sidebar has no edge at all and the layout reads as one
 * undivided field.
 */
.fi-body .fi-topbar {
    box-shadow: none;
    border-block-end: 1px solid var(--gr-nav-border);
}

.fi-body .fi-sidebar {
    border-inline-end: 1px solid var(--gr-nav-border);
}

.fi-body .fi-sidebar.fi-sidebar-open {
    box-shadow: none;
}

/* The search field has to stop being a white slab on a Midnight bar. */
.fi-body .fi-topbar .fi-input-wrp,
.fi-body .fi-sidebar .fi-input-wrp {
    background-color: rgb(255 255 255 / 0.06);
    box-shadow: none;
    --tw-ring-color: var(--gr-nav-border);
}

.fi-body .fi-topbar .fi-input-wrp:focus-within,
.fi-body .fi-sidebar .fi-input-wrp:focus-within {
    --tw-ring-color: var(--gr-nav-accent);
}

.fi-body .fi-topbar .fi-input::placeholder,
.fi-body .fi-sidebar .fi-input::placeholder {
    color: var(--gr-nav-text-muted);
}

/*
 * The active item. Colour alone does not say "you are here" (WCAG 1.4.1), so
 * the accent arrives three ways: a rail, a lifted surface and a heavier label.
 */
.fi-sidebar-item.fi-active > .fi-sidebar-item-btn {
    position: relative;
    background-color: var(--gr-nav-raised);
}

.fi-sidebar-item.fi-active > .fi-sidebar-item-btn::before {
    content: '';
    position: absolute;
    inset-block: 0.25rem;
    inset-inline-start: -0.5rem;
    width: 0.1875rem;
    border-radius: 0 2px 2px 0;
    background-color: var(--gr-nav-accent);
}

.fi-sidebar-item.fi-active > .fi-sidebar-item-btn > .fi-sidebar-item-label {
    font-weight: 600;
}

.fi-sidebar-item.fi-sidebar-item-has-url > .fi-sidebar-item-btn:hover,
.fi-sidebar-item.fi-sidebar-item-has-url > .fi-sidebar-item-btn:focus-visible {
    background-color: rgb(255 255 255 / 0.06);
}

/* The divider under the sidebar header, and the footer's. */
.fi-sidebar-header-controls::after,
.fi-sidebar-footer {
    border-color: var(--gr-nav-border);
}

/* ------------------------------------------------------------------ *
 * 4. The brand mark
 * ------------------------------------------------------------------ */

.gr-logo {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    min-width: 0;
}

.gr-logo-mark {
    flex: none;
    width: 2rem;
    height: 2rem;
    /* The mark's own padding is inside its viewBox, so it needs to run a little
       larger than the cap height to look the same weight as the wordmark. */
    margin-block: -0.125rem;
}

/* The wordmark is live text, not part of the SVG: it inherits `.fi-logo`'s
   colour, so one mark serves the Midnight chrome, the login page and a light
   surface without a second asset to keep in step. */
.gr-logo-word {
    font-weight: 700;
    font-size: 1.0625rem;
    letter-spacing: -0.02em;
    line-height: 1.2;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/*
 * A portfolio site's own accent, beside its name (docs/DECISIONS.md, D-037).
 *
 * A dot rather than the name's colour: across six properties the same text
 * could not clear 4.5:1 in both themes, and the name already names the site —
 * the dot only speeds up scanning. The colour arrives as a custom property set
 * by App\Filament\SiteAccent, which takes it from Site::brandColour() and so
 * can only ever be `#rrggbb`.
 *
 * The class lands on the text item rather than the cell — that is where
 * Filament puts a column's extra attributes — so the dot hangs off that.
 */
.gr-site::before {
    background-color: var(--gr-site-accent, transparent);
    border-radius: 50%;
    content: '';
    display: inline-block;
    flex: none;
    height: 0.5rem;
    margin-inline-end: 0.5rem;
    width: 0.5rem;

    /* The ring is what makes an arbitrary accent legible on an arbitrary
       surface. Benefit Badger's is a near-black taken from its own logo, and
       on the dark-mode panel the dot vanished without it; a site's colour is
       the operator's to choose, so the marker cannot depend on it being
       bright. */
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--gray-500) 55%, transparent);
}

/* ------------------------------------------------------------------ *
 * 5. Focus
 * ------------------------------------------------------------------ */

/*
 * Filament removes the UA outline in favour of its own rings, which it does not
 * draw on every element. This restores a visible one everywhere, in a green
 * dark enough to clear 3:1 on a light surface — Radar Green itself reaches
 * 1.6:1 there and cannot serve as a focus indicator (WCAG 1.4.11).
 */
.fi-body :focus-visible {
    outline-width: 2px;
    outline-style: solid;
    outline-color: var(--primary-700);
    outline-offset: 2px;
}

.dark .fi-body :focus-visible,
.fi-body .fi-sidebar :focus-visible,
.fi-body .fi-topbar :focus-visible {
    outline-color: var(--gr-nav-accent);
}

.fi-body .fi-skip-link:focus {
    position: fixed;
    inset-block-start: 0.75rem;
    inset-inline-start: 0.75rem;
    z-index: 50;
    padding: 0.5rem 0.875rem;
    border-radius: var(--radius-lg);
    background-color: var(--gr-nav-bg);
    color: var(--gr-nav-text-strong);
}

/* ------------------------------------------------------------------ *
 * 6. Radar motifs
 * ------------------------------------------------------------------ */

/*
 * Used in three places and nowhere else: behind the login form, behind an empty
 * state, and as the sidebar's origin dot. The metaphor stops being a signal if
 * it is on every surface.
 */

/* The login page. Midnight in both themes, with the rings drawn as a gradient
   rather than an image so nothing extra is fetched. */
.fi-body .fi-simple-layout {
    position: relative;
    isolation: isolate;
    background-color: var(--gr-midnight);
    color: var(--gr-nav-text);
}

.fi-body .fi-simple-layout::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    background:
        radial-gradient(
            circle at 50% 38%,
            rgb(0 230 118 / 0.14) 0,
            transparent 42%
        ),
        repeating-radial-gradient(
            circle at 50% 38%,
            transparent 0,
            transparent 5.4rem,
            rgb(0 230 118 / 0.10) 5.4rem,
            rgb(0 230 118 / 0.10) calc(5.4rem + 1px)
        );
}

/* The sweep. One slow pass; it is decoration, so it is hidden from assistive
   technology and stops entirely when the operator asks for less motion. */
.fi-body .fi-simple-layout::after {
    content: '';
    position: absolute;
    inset-block-start: 38%;
    inset-inline-start: 50%;
    z-index: -1;
    width: 44rem;
    height: 44rem;
    margin-block-start: -22rem;
    margin-inline-start: -22rem;
    border-radius: 50%;
    background: conic-gradient(
        from 0deg,
        rgb(0 230 118 / 0.16) 0deg,
        transparent 55deg,
        transparent 360deg
    );
    mask-image: radial-gradient(circle, #000 0, transparent 70%);
    animation: gr-sweep 14s linear infinite;
}

@keyframes gr-sweep {
    to {
        transform: rotate(360deg);
    }
}

@media (prefers-reduced-motion: reduce) {
    .fi-body .fi-simple-layout::after {
        display: none;
    }
}

.fi-body .fi-simple-main {
    border-radius: var(--radius-xl);
}

.fi-body .fi-simple-header-heading {
    color: var(--gr-text-primary);
}

/* The login page's logo and any text outside the card sit on Midnight. */
.fi-body .fi-simple-layout > .fi-simple-main-ctn > * > .fi-logo,
.fi-body .fi-simple-layout .fi-simple-layout-header {
    color: var(--gr-nav-text-strong);
}

/* An empty state is the honest answer "nothing was detected", so it gets a
   swept ring rather than a shrug. The container is 48px, so the rings have to
   be close together to read as rings at all. */
.fi-body .fi-ta-empty-state-icon-bg {
    background-color: color-mix(in srgb, var(--primary-500) 8%, transparent);
    background-image: repeating-radial-gradient(
        circle,
        transparent 0,
        transparent 0.3rem,
        color-mix(in srgb, var(--primary-500) 24%, transparent) 0.3rem,
        color-mix(in srgb, var(--primary-500) 24%, transparent) calc(0.3rem + 1px)
    );
}

.fi-body .fi-ta-empty-state-icon-bg .fi-icon {
    color: var(--gr-text-secondary);
}

/* ------------------------------------------------------------------ *
 * 7. Dense views on small screens
 * ------------------------------------------------------------------ */

/*
 * Desktop is the operator's screen, but a deadline is read on a phone. Filament
 * stacks its tables below `md` on its own; what it does not do is stop the
 * stat widgets from squeezing five figures onto one row, or keep a long
 * opportunity title from pushing the table sideways.
 */
@media (max-width: 40rem) {
    .fi-wi-stats-overview-stat-value {
        font-size: 1.375rem;
    }

    .fi-body .fi-main {
        padding-inline: 1rem;
    }

    .fi-section-header {
        flex-wrap: wrap;
    }
}

/*
 * A table that cannot stack — the evidence and score breakdowns are genuine
 * grids — scrolls inside its own box rather than widening the page.
 *
 * The minimum widths are the point of this rule. Left to distribute a narrow
 * container itself, the browser gives "Tech Worth" the room it asks for and
 * squeezes the wrapped opportunity title to one word per line. Holding the
 * table to a readable measure and scrolling it is the honest trade.
 */
.fi-body .fi-ta-ctn {
    max-width: 100%;
}

@media (max-width: 64rem) {
    .fi-body .fi-ta-table:not(.fi-ta-table-stacked-on-mobile) {
        min-width: 52rem;
    }

    /* The wrapped column — the opportunity's title and its recommended action —
       is the one the operator actually reads, and auto table layout gives it
       whatever is left after a site name has taken what it wants. This is the
       floor under it. A table that stacks has no columns to balance, so it is
       excluded from both rules. */
    .fi-body
        .fi-ta-table:not(.fi-ta-table-stacked-on-mobile)
        .fi-ta-cell:has(.fi-wrapped) {
        min-width: 20rem;
    }
}
