/*
 * Wordmark logo (gradient text), replacing v2's `.gradient-logo-text`/`.gradient-logo-text-dark`
 * (two separate, clashing palettes — flagged in docs/brand.md's brand audit). v3 uses one consistent
 * gradient, anchored on the purchased template's own primary color (--bs-primary: #0066ff), so it reads
 * correctly on both light and dark section backgrounds without needing a second variant.
 * See resources/views/components/logo.blade.php and docs/phases/phase-03-auth-accounts/specs.md.
 */
.brand-logo-gradient {
    background-image: linear-gradient(90deg, #0066ff, #4da6ff);
    background-size: 100%;
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
    display: inline-block;
    font-weight: 800;
    letter-spacing: -0.02em;
    line-height: 1.1;
}

.brand-logo-sm {
    font-size: 1.25rem;
}

.brand-logo-md {
    font-size: 1.53125rem;
}

.brand-logo-lg {
    font-size: 3rem;
}

/*
 * Gold sign-up/login button pair for the landing navbar, replacing the single "Get Started" CTA.
 * See resources/views/partials/landing-header.blade.php.
 */
.btn-gold {
    background-color: #d4af37;
    border-color: #d4af37;
    color: #1a1a1a;
}

.btn-gold:hover,
.btn-gold:focus {
    background-color: #c19a2e;
    border-color: #c19a2e;
    color: #1a1a1a;
}

.btn-outline-gold {
    background-color: transparent;
    border-color: #d4af37;
    color: #d4af37;
}

.btn-outline-gold:hover,
.btn-outline-gold:focus {
    background-color: #d4af37;
    border-color: #d4af37;
    color: #1a1a1a;
}

/* Same gold as .btn-gold/.btn-outline-gold above, as a plain text/icon color utility (hero
   "Result" card's trophy icon, project owner direction). */
.text-gold {
    color: #d4af37;
}

/* Same gold, as a background utility (Premium subscription-tier badge — sidebar/profile page,
   project owner direction: Premium badges gold, Elite badges green). Dark text for contrast,
   matching .btn-gold's own foreground color choice. */
.bg-gold {
    background-color: #d4af37;
    color: #1a1a1a;
}

/*
 * The template's own app.min.css hardcodes `option{color:#000}` AND the more specific
 * `.form-select:not([multiple]) option{color:#000}` (which matches every plain <select> in this app,
 * including ones inside modals — the actual bug report), with no dark-mode awareness at all. Both need
 * overriding with the theme-aware body-color variable — the plain `option` rule alone loses to the more
 * specific one, so its bug survives despite the generic override. This file loads after app.min.css in
 * every layout, so equal-specificity rules win by source order; the second selector matches the
 * vendored rule's specificity exactly so it wins there too.
 */
option {
    color: var(--bs-body-color);
}

.form-select:not([multiple]) option {
    color: var(--bs-body-color);
}

/*
 * Flex layout + spacing for `.nav-tabs-v2` tab bars used outside profile.html itself (e.g. the
 * settings page). The template's own spacing rules for this pattern are scoped as `.profile
 * .profile-tab {...}` — they require a `.profile` ancestor wrapper we don't use here (that class
 * also carries unrelated profile-page-shell styling), so without this, the tab items have no flex
 * layout or gap and run together as plain inline text. `.nav-tabs-v2`'s own underline/color rules
 * are unscoped and already work standalone; this only replaces the missing `.profile`-scoped part.
 */
.settings-tabs {
    display: flex;
    flex-wrap: wrap;
}

.settings-tabs .nav-item + .nav-item {
    margin-inline-start: 1.5rem;
}

/* `.nav-tabs-v2` styles the active tab's underline/color but not its weight — make it bold too. */
.nav-tabs-v2 > .nav-item > .nav-link.active {
    font-weight: 700;
}

/*
 * Sticky footer: `.app` (the outer wrapper in every layout) has no flex/min-height rule of its own in
 * the template's CSS, so on short-content pages the footer sits right under the content instead of at
 * the bottom of the viewport. Making `.app` a min-height:100vh flex column, combined with
 * `<x-site-footer>`'s own `mt-auto` (see resources/views/components/site-footer.blade.php), pushes the
 * footer to the bottom regardless of content height. `.app-header`/`.app-sidebar` are `position:fixed`
 * in the template's CSS, so they're unaffected by `.app` becoming a flex container.
 */
.app {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

/*
 * Sidebar-aware footer offset (project owner direction: the admin layout's footer overlapped the
 * left main menu). `<x-site-footer>` (id="footer", shared by the customer app layout and the admin
 * layout — both have a fixed `.app-sidebar`) has no margin of its own, unlike `.app-content`, which
 * the template's own CSS already offsets by the sidebar's width (`margin-inline-start: 15.625rem`
 * at desktop, `0` below the template's own sidebar-collapse breakpoint, `max-width: 767.98px`).
 * Without a matching offset here, the fixed sidebar visually covers the footer's left edge once a
 * page is scrolled far enough to reveal it — reusing the same two values makes the footer resize
 * exactly like `.app-content` does. Landing's own footer (`id="landing-footer"`) is untouched —
 * layouts/landing.blade.php has no sidebar at all.
 */
#footer {
    margin-inline-start: 15.625rem;
}

@media (max-width: 767.98px) {
    #footer {
        margin-inline-start: 0;
    }
}

/*
 * The template's `.widget-reminder-time` column is `width: 5rem`, sized for its own demo content
 * ("09:00", "All day"). Our activity log shows HH:MM:SS, which wraps onto two lines at that width.
 * Scoped to `.activity-log-reminder` (settings/show.blade.php) rather than overriding the generic
 * class everywhere `.widget-reminder` might be reused later with the template's own shorter content.
 */
.activity-log-reminder .widget-reminder-time {
    width: 7rem;
    white-space: nowrap;
}

/*
 * Landing hero column split (resources/views/landing/index.blade.php) — reversed to 40%/60%
 * (project owner direction, 2026-09-05; originally 60%/40% the other way) once the secondary
 * column started holding the 3-card Manual→Automated→Result cascade instead of a single reserved
 * animation slot — that cascade needs more room than the tagline column now. Bootstrap's 12-column
 * grid has no exact 40/60 pair, so this is a small custom pair instead of col-lg-5/col-lg-7.
 * Scoped to Bootstrap's own `lg` breakpoint (992px) so it stacks to full-width on mobile exactly
 * like the col-lg-* classes it replaces did.
 */
@media (min-width: 992px) {
    .hero-col-primary {
        flex: 0 0 40%;
        max-width: 40%;
    }

    .hero-col-secondary {
        flex: 0 0 60%;
        max-width: 60%;
    }
}

/*
 * Hero "Beginner → Automated → Result" orbit layout (resources/views/landing/index.blade.php,
 * Phase 15, project owner direction — replaces the earlier side-by-side, forced-equal-height
 * overlapping cascade). The three cards sit on a loose semi-orbit path (top-left → middle-right →
 * bottom-center) rather than a linear row: `.hero-orbit` is given a fixed `min-height` so the
 * percentage-based `top`/`bottom`/`left`/`right` offsets below and the connecting SVG's own
 * `viewBox="0 0 100 100"` share the same coordinate space (a `top: 36%` card offset lines up with
 * the SVG path's `y="36"`, regardless of the column's actual rendered width). Each card's height is
 * its own natural content height now (no `h-100`, project owner direction: no longer forced equal).
 * The percentages below (tightened, project owner direction: cards 2 and 3 pushed higher so the
 * whole orbit occupies less vertical space than the box's full height) were chosen so no two cards'
 * horizontal *and* vertical spans overlap for this copy's actual content length — card 1 stays
 * within the top ~34% of the box, card 2 within roughly 20–50%, and card 3 within roughly 21–55%
 * (centered on card 1's right border, x: 38%), so even though cards 1/3 and 2/3 share a few
 * percentage points of horizontal span, their vertical spans don't meet. Below `lg` there's no room
 * for 2D placement next to the now-full-width primary column, so it drops back to plain static
 * vertical stacking and the connecting curve is hidden.
 */
@media (min-width: 992px) {
    .hero-orbit {
        min-height: 480px;
    }

    .hero-orbit-svg {
        position: absolute;
        inset: 0;
        width: 100%;
        height: 100%;
        z-index: 0;
        pointer-events: none;
    }

    .hero-orbit-card {
        position: absolute;
        z-index: 1;
        max-width: 340px;
    }

    .hero-orbit-card-1 {
        top: 0;
        left: 0;
        width: 38%;
    }

    /* Pushed higher (project owner direction, was top: 36%) now that the box overall is shorter. */
    .hero-orbit-card-2 {
        top: 20%;
        right: 0;
        width: 38%;
    }

    /* Left-aligned with card 1 (project owner direction: "3rd card should be on the same x
       level as the 1st card") — both now sit at left: 0, no more translateX(-50%) centering
       trick. This makes the card 2 → card 3 connector a true rotated/mirrored counterpart of the
       card 1 → card 2 connector (same true-circle construction, opposite bulge phase — see the
       connector path comment in index.blade.php) instead of the two arcs having unrelated
       shapes. */
    .hero-orbit-card-3 {
        top: 62%;
        left: 0;
        width: 45%;
        max-width: 420px;
    }
}

@media (max-width: 991.98px) {
    .hero-orbit {
        display: flex;
        flex-direction: column;
        gap: 1rem;
    }

    /* No 2D orbit path once the cards stack vertically. */
    .hero-orbit-svg {
        display: none;
    }
}

/*
 * Hero testimonial wall (resources/views/landing/index.blade.php, assets/js/testimonialWall.js,
 * project owner direction, 2026-09-07) — a vertically auto-scrolling "love wall" of mock
 * testimonial cards, replacing the removed particle-canvas background. `.hero-testimonial-wall` is
 * a fixed-height viewport (`overflow: hidden`); the `mask-image` (+ `-webkit-` prefix for Safari)
 * fades its top/bottom edges to transparent so cards visibly scroll in/out rather than clipping
 * abruptly. `.hero-testimonial-wall-track` holds two back-to-back copies of the same mock card set
 * (see the Blade `@for` loop) — the JS only needs to translate it upward and reset by exactly half
 * its scrollHeight for a seamless loop, no cloning/measuring needed at runtime.
 *
 * `.hero-col-secondary` stretches to the primary column's full height (`.row.align-items-stretch`)
 * — given `display: flex; flex-direction: column; justify-content: center` here so its content
 * (the testimonial wall, then the credit line + Discord button below it) sits vertically centered
 * as a stacked group instead of pinned to the top. `flex-direction: column` matters: without it,
 * the wall and the credit line below it would sit side by side as flex-row siblings instead of
 * stacking.
 */
.hero-col-secondary {
    display: flex;
    flex-direction: column;
    justify-content: center;
}

/* Height matches the left hero column exactly (project owner direction) — set in JS
   (assets/js/testimonialWall.js`syncHeightToPrimaryColumn()`), not pure CSS: a CSS-only
   flex-stretch approach was tried and reverted — it created a runaway feedback loop (the wall's
   own unbounded ~36-card-tall scrolling track inflated `.hero-col-secondary`'s natural height
   *before* any stretching happened, which then stretched the row itself to match that inflated
   height, making the whole hero thousands of pixels tall). `320px` here is only the pre-JS/no-JS
   fallback. */
.hero-testimonial-wall {
    position: relative;
    width: 100%;
    height: 320px;
    overflow: hidden;
    mask-image: linear-gradient(to bottom, transparent, #000 12%, #000 88%, transparent);
    -webkit-mask-image: linear-gradient(to bottom, transparent, #000 12%, #000 88%, transparent);
}

.hero-testimonial-wall-track {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    will-change: transform;
}

/* Testimonial screenshot cards (project owner direction) — `#3e3f45` is close to the Discord
   dark-theme background baked into the screenshots themselves, a deliberate theme-independent color
   (not a `var(--bs-*)` token) so the card's own background matches the image with no visible seam
   around
   it, in both light and dark site themes. `overflow: hidden` clips nothing in practice (the image
   itself is capped to the card's width below) but guards against a stray screenshot with
   whitespace baked into its own edges. */
.hero-testimonial-card {
    background-color: #3e3f45;
    flex-shrink: 0;
    overflow: hidden;
    padding: 0.75rem;
    /* Bootstrap's `.card` is itself `display: flex; flex-direction: column`, whose default cross-
       axis alignment (`stretch`) was stretching the image to the card's full width regardless of
       its own natural size — upscaling every screenshot narrower than the card. `flex-start` sizes
       the image to its own (`max-width`-capped) natural width instead, which also left-aligns it. */
    align-items: flex-start;
}

/* Left-aligned, not centered or stretched — the screenshots don't share a common width, so a
   narrower one simply leaves the card's own (matching) background visible to its right rather
   than being centered or stretched to fill the card. */
.hero-testimonial-card img {
    display: block;
    max-width: 100%;
    height: auto;
}

@media (max-width: 991.98px) {
    /* Below `lg` the two hero columns stack (full-width each, no more side-by-side split) — there's
       no "left column height" to match anymore, so `testimonialWall.js` doesn't set an inline
       height at this width; this fixed height is what actually applies. */
    .hero-testimonial-wall {
        height: 220px;
    }
}

/*
 * Landing page product-browsing section background (resources/views/landing/index.blade.php) —
 * superseded the original CSS gradient with an image overlay (project owner direction) —
 * public/assets/img/landing/bg1.webp on top of a solid base color (shows while the image loads,
 * and behind any transparent edges). The base color is `var(--bs-tertiary-bg)` — the same
 * Bootstrap variable the hero section's `.bg-body-tertiary` class reads — rather than a hardcoded
 * hex, so it automatically matches the hero's own background whenever both render in dark mode
 * (project owner direction: an earlier draft hardcoded an unrelated purple here instead). The
 * section is separately scoped data-bs-theme="dark" so Bootstrap's text-color utilities stay
 * readable over the image regardless of the site's own light/dark toggle, and that same scoping is
 * what makes the variable resolve to its dark value here — a deliberate, intentional exception to
 * this file's usual "don't hardcode theme colors" rule.
 */
.product-browse-section {
    background-color: var(--bs-tertiary-bg);
    /*
     * A background-image has no native opacity property (element opacity would also fade the
     * text/cards on top of it) — a translucent gradient layer blended over the image is the
     * standard way to fade just the image. Uses the same --bs-tertiary-bg-rgb variable as the
     * background-color above (its dark-mode value, per this section's forced data-bs-theme="dark")
     * so the fade lands on the identical color the image sits on, at 50% opacity (project owner
     * direction).
     */
    background-image: linear-gradient(rgba(var(--bs-tertiary-bg-rgb), 0.5), rgba(var(--bs-tertiary-bg-rgb), 0.5)), url('../img/landing/bg1.webp');
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
}

.product-browse-card-logo {
    height: 32px;
    width: auto;
    max-width: 100%;
}

/* Category icon badge (indicator/strategy/tool/dev_library), top-right corner of the product-browse
   card's thumbnail — same icon per category as the sidebar's platform dropdown groups (see
   landing/index.blade.php's renderCard() for the category->icon mapping). */
.product-browse-card-category-badge {
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    width: 28px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background-color: rgba(0, 0, 0, 0.5);
    color: #ffffff;
    font-size: 0.8rem;
}

/*
 * Product-browse result cards (resources/views/landing/index.blade.php's renderCard()) — `#productBrowseGrid`
 * is a plain `d-flex flex-wrap` (not a Bootstrap `.row`/`.col-*` grid), and each card tile has a fixed
 * pixel width instead of a percentage-of-container width. With `col-sm-6 col-xl-3`, a card's width
 * was a fraction of the (already-fluid) container, so widening the browser window visibly stretched
 * every thumbnail — project owner direction: thumbnails must stay a fixed size regardless of
 * viewport/container width, with more tiles wrapping onto the row as space allows instead of fewer,
 * larger tiles.
 */
.product-browse-card-tile {
    width: 260px;
}

/*
 * Landing product-browse filter groups (resources/views/landing/index.blade.php) — each of the
 * Platform/Category/Plan switch groups gets its own rounded, bordered card so the three groups
 * read as distinct controls rather than a loose wall of switches. `.landing-filter-row`'s children
 * (the three cards plus the plain Sort By select) size to their own content rather than stretching
 * to an even width — Platform/Plan (3 items) and Category (4 items) end up narrower or wider than
 * each other on purpose, matching how much they actually contain. Below the `sm` breakpoint each
 * item goes full-width instead, since content-sized cards next to each other get cramped once they
 * no longer fit two-plus per line.
 */
.landing-filter-row > * {
    flex: 0 0 auto;
}

@media (max-width: 575.98px) {
    .landing-filter-row > * {
        flex: 1 1 100%;
    }
}

.landing-filter-group {
    border: 1px solid rgba(255, 255, 255, 0.15);
    border-radius: var(--bs-border-radius-lg, 0.5rem);
    padding: 1rem 1.25rem;
}

/*
 * The Sort By select-picker (#productSortBy) keeps a constant "Sort By" trigger label
 * (data-pc-static-label, see the global picker init in layouts/*.blade.php) — the plugin's own
 * `.pc-select` has a hardcoded `max-width: 200px` (src/picker.less) with no `white-space: nowrap`,
 * which can wrap that label onto two lines inside the narrow box. Widened and pinned to one line.
 */
.landing-sort-select + .picker .pc-select {
    min-width: 220px;
    max-width: none;
}

.landing-sort-select + .picker .pc-trigger {
    white-space: nowrap;
}

/* Downloads page Sort By (resources/views/downloads/index.blade.php) — same fix as
   .landing-sort-select above (the plugin's own `.pc-select` hardcodes `max-width: 200px`), widened
   further still per project owner direction. */
.downloads-sort-select + .picker .pc-select {
    min-width: 260px;
    max-width: none;
}

.downloads-sort-select + .picker .pc-trigger {
    white-space: nowrap;
}

/*
 * Select-picker plugin (public/assets/plugins/select-picker/) theme overrides. Its own CSS
 * (src/picker.less) hardcodes every color (#ffffff background, #666666 text/border, #DDDDDD
 * search input) with no dark-mode awareness — same class of bug already fixed for native
 * <option> elements (Phase 3.12). Rules below re-point those to Bootstrap's theme-aware custom
 * properties so the plugin's rendered markup follows the page's own light/dark state, including
 * when nested inside a section with its own data-bs-theme="dark" (e.g. .product-browse-section) —
 * the vars simply inherit the cascaded value from whichever ancestor scope applies, no extra
 * [data-bs-theme] selector needed here.
 */
.picker .pc-element,
.picker .pc-trigger {
    color: var(--bs-body-color);
    background-color: var(--bs-body-bg);
    border-color: var(--bs-border-color);
}

.picker .pc-element:hover,
.picker .pc-trigger:hover {
    color: var(--bs-emphasis-color);
    background-color: var(--bs-tertiary-bg);
}

.picker .pc-select .pc-list {
    background-color: var(--bs-body-bg);
    border-color: var(--bs-border-color);
}

.picker .pc-select .pc-list input[type="search"] {
    color: var(--bs-body-color);
    background-color: var(--bs-tertiary-bg);
    border-bottom-color: var(--bs-border-color);
}

.picker .pc-select .pc-list li {
    color: var(--bs-body-color);
}

.picker .pc-select .pc-list li:nth-child(even) {
    background-color: var(--bs-tertiary-bg);
}

.picker .pc-select .pc-list li:hover {
    color: var(--bs-emphasis-color);
    background-color: var(--bs-secondary-bg);
}

/*
 * Landing product-browse filter switches (resources/views/landing/index.blade.php) — platform,
 * category, and plan filters are multi-select `.form-check.form-switch` toggles (form_elements.html
 * pattern) rather than single-value <select> dropdowns, since a visitor can combine more than one
 * value per filter. The unchecked track's default color is transparent-ish against this section's
 * dark image background (unlike a flat surface color, so Bootstrap's own dark-theme contrast
 * doesn't fully carry over) — tinted here for visibility.
 *
 * Bug fixed here: this rule and Bootstrap's own `.form-check-input:checked` (app.min.css) have
 * equal selector specificity (two simple selectors each) — this file loads after app.min.css, so
 * an unscoped `.landing-filter-switch .form-check-input` rule was winning the cascade even on
 * `:checked` elements and silently overwriting Bootstrap's blue checked track with the same muted
 * unchecked color, making toggled-on switches look barely different from toggled-off ones. Scoped
 * to `:not(:checked)` so the checked state is untouched by this rule, and the checked state now
 * gets its own explicit styling below — including recoloring the knob itself (`--bs-form-switch-bg`,
 * the SVG circle Bootstrap draws as the switch's background-image), not just the track, since a
 * plain white knob on a blue track read as a thin colored ring around the button rather than a
 * clearly toggled control.
 */
.landing-filter-switch .form-check-input:not(:checked) {
    background-color: rgba(255, 255, 255, 0.25);
    border-color: rgba(255, 255, 255, 0.4);
}

.landing-filter-switch .form-check-input:checked {
    background-color: var(--bs-primary);
    border-color: var(--bs-primary);
    --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23d4af37'/%3e%3c/svg%3e");
}

.landing-filter-switch-icon {
    height: 18px;
    width: auto;
    object-fit: contain;
}

/*
 * The landing product-browse filters (resources/views/landing/index.blade.php) previously used
 * custom glassy classes (bg-white bg-opacity-25 border-0 text-white) directly on the <select>,
 * which the plugin now hides in favor of its own .pc-trigger markup. Re-applied here, scoped to
 * this section only, so the filter controls keep the same look against the dark image background.
 * Scoped by class (not the section's own `id`, which is now a configurable prop on
 * `<x-product-browser>` — resources/views/components/product-browser.blade.php) so a second
 * instance of that component elsewhere still picks this up regardless of what `sectionId` it uses.
 */
.product-browse-section .picker .pc-element,
.product-browse-section .picker .pc-trigger {
    color: #fff;
    background-color: rgba(255, 255, 255, 0.25);
    border-color: transparent;
}

.product-browse-section .picker .pc-element:hover,
.product-browse-section .picker .pc-trigger:hover {
    color: #fff;
    background-color: rgba(255, 255, 255, 0.35);
}

/*
 * Product detail page sidebar (resources/views/layouts/product.blade.php, Phase 4.5) — the
 * scrollspy nav (#sidebar-bootstrap) and the Quick Facts card below it are wrapped in one
 * `.product-sidebar-sticky` container so they scroll together and the Quick Facts card never
 * disappears above the fold once you scroll past the nav (project owner-reported bug: it should
 * "always be below the spy panel"). The template's own `.navbar-sticky` class (top: 5.5rem) only
 * ever stuck the nav element itself, leaving the card below it to scroll away normally — this
 * class moves that same offset to the shared wrapper instead, and the nav's left border/spacing
 * (previously bundled into `.navbar-sticky`) is preserved on #sidebar-bootstrap directly so it
 * still looks identical without also re-triggering its own (now redundant) sticky positioning.
 */
.product-sidebar-sticky {
    position: sticky;
    top: 5.5rem;
}

#sidebar-bootstrap {
    padding: 0;
    border-inline-start: 1px solid var(--bs-border-color);
    margin-inline-start: 1rem;
}

/*
 * The template's `.navbar-sticky .nav`/`.nav-link` rules (app.min.css) are what actually stack the
 * scrollspy links vertically and color them — losing the `.navbar-sticky` class here (its
 * positioning moved to `.product-sidebar-sticky` above) silently lost this styling too, so the
 * links fell back to Bootstrap's default `.nav` (horizontal, two-per-line) and default link blue.
 * Replicated directly against `#sidebar-bootstrap` instead of restoring the class, since restoring
 * the class would also re-apply its own (now redundant) `position: sticky`.
 */
#sidebar-bootstrap .nav {
    width: 100%;
    flex-direction: column;
}

#sidebar-bootstrap .nav .nav-link {
    color: var(--bs-body-color);
    padding: 0.25rem 1.5rem;
}

#sidebar-bootstrap .nav .nav-link:hover {
    color: var(--bs-tertiary-color);
}

#sidebar-bootstrap .nav .nav-link.active {
    color: var(--bs-theme);
    font-weight: 600;
}

/*
 * Product detail page header (resources/views/layouts/product.blade.php) — the trading platform's
 * logo, shown directly below the page title, is pinned to a fixed height with preserved aspect
 * ratio so platforms with differently-shaped logo files don't visually dominate the header.
 */
.product-platform-logo {
    height: 28px;
    width: auto;
    max-width: 100%;
}

/*
 * Sidebar menu icon images (resources/views/partials/sidebar-menu-item.blade.php, Phase 4.5) — a
 * platform's logo used as a per-platform product group's icon, sized to match the template's own
 * `.menu-icon i` font-icon footprint (roughly 1em) so it doesn't dominate the row next to a
 * fallback Font Awesome icon on other rows.
 */
.sidebar-menu-icon-image {
    height: 16px;
    width: 16px;
    object-fit: contain;
}

/*
 * The template dims every sidebar `.menu-icon` to 25% opacity by default, brightening to 100% only
 * when its `.menu-item` is `.active` (app.min.css) — a deliberate look for a generic Font Awesome
 * glyph, but a real platform logo image reads as broken/disabled at 25% rather than stylistically
 * muted. `.menu-icon-image` (sidebar-menu-item.blade.php, added only when the item carries a real
 * logo, not a generic icon) keeps those at full opacity regardless of active state. Same selector
 * depth as the template's own rule so this wins on specificity without `!important`.
 */
.app-sidebar .app-sidebar-content .menu .menu-item .menu-link .menu-icon.menu-icon-image {
    opacity: 1;
}

/*
 * Installation steps (x-product-installation-step, Phase 4.5) — numbered circle badge replacing
 * the plain <ol> markers previously used in the Download & Installation Instructions section, so
 * a step's optional illustration image (once product image assets are migrated) can sit beside
 * the step text instead of just flowing inline with it.
 */
.installation-step-number {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    border-radius: 50%;
    background-color: var(--bs-theme, var(--bs-primary));
    color: #fff;
    font-weight: 600;
    font-size: 0.9rem;
}

.installation-step:not(:last-child) {
    margin-bottom: 1.25rem;
    padding-bottom: 1.25rem;
    border-bottom: 1px dashed var(--bs-border-color);
}

.installation-step-image {
    max-height: 220px;
    object-fit: cover;
}

/*
 * "Beautified" links inside the product-page card sections (License Requirements, Download,
 * Installation Steps) — plain default-blue underlined <a> tags read as out of place inside these
 * themed cards (project owner-reported), so links here pick up the same theme color already used
 * for buttons/active nav, with the underline only appearing on hover/focus.
 */
.product-body a:not(.btn) {
    color: var(--bs-theme, var(--bs-primary));
    text-decoration: none;
    font-weight: 600;
}

.product-body a:not(.btn):hover,
.product-body a:not(.btn):focus {
    text-decoration: underline;
}

/*
 * Performance Report Explorer (resources/views/components/performance-report-explorer.blade.php)
 * identification card — its secondary text (instrument/timeframe/period line, metric captions)
 * uses Bootstrap's `.text-body.text-opacity-50` combo, this project's usual convention for
 * de-emphasized text. In dark mode that combo reads poorly here specifically: `--bs-body-color`
 * dark-mode value (#aaaaaa) at 50% opacity blends down too close to the card background,
 * project owner-reported as "not clear". Bootstrap's own `--bs-secondary-color` is already tuned
 * per-theme by app.min.css (0.75 alpha in dark mode vs a plain opacity-50 combo) specifically for
 * this "de-emphasized but still legible" role — reusing it here, scoped to this one component,
 * fixes the contrast without touching the `.text-opacity-50` convention used elsewhere.
 */
[data-bs-theme="dark"] .performance-report-explorer .text-opacity-50 {
    color: var(--bs-secondary-color) !important;
}

/*
 * "3D" pressable buttons for the pricing cards' Credit Card / PayPal subscribe buttons
 * (resources/views/components/pricing-plans.blade.php, Phase 5.2) — a pure-CSS reimplementation
 * of a React + Tailwind + Framer Motion component the project owner supplied as a design
 * reference. No React/Framer Motion/lucide-react were installed (this project is plain
 * JS/jQuery + plain CSS only, no build step, no new dependencies without approval) — the visual
 * effect (a colored "lip" below the button via box-shadow, hover raises it, :active presses it
 * down) is reproduced with plain CSS transitions/`:hover`/`:active`, no JS state needed at all.
 * Two color variants matching the two processors' own buttons on this page (and v2's real
 * `btn-info`/`btn-warning` choice for Stripe/PayPal, preserved here as blue/gold), plus a green
 * `-success` variant reused by the billing dashboard's "Upgrade Now" call-to-action
 * (resources/views/billing/index.blade.php).
 */
.btn-3d {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    white-space: nowrap;
    font-weight: 700;
    border: none;
    border-radius: 0.5rem;
    color: #fff;
    text-decoration: none;
    transition: transform 0.1s ease, box-shadow 0.1s ease, background-color 0.1s ease;
    transform: translateY(0);
}

/* Buttons are <a> tags when enabled — reset the browser's default link styling (underline,
   visited/hover color changes) so they read as buttons, not links, in every state. */
.btn-3d:hover,
.btn-3d:focus,
.btn-3d:active,
.btn-3d:visited {
    color: #fff;
    text-decoration: none;
}

.btn-3d:hover:not(:disabled) {
    transform: translateY(-1px) scale(1.02);
}

.btn-3d:active:not(:disabled) {
    transform: translateY(4px) scale(0.98);
}

.btn-3d:disabled {
    opacity: 0.5;
    cursor: not-allowed;
    transform: none;
    box-shadow: none;
}

.btn-3d-primary {
    background-color: #3b82f6;
    box-shadow: 0 6px 0 0 #1d4ed8;
}

.btn-3d-primary:hover:not(:disabled) {
    background-color: #2563eb;
    box-shadow: 0 8px 0 0 #1d4ed8;
}

.btn-3d-primary:active:not(:disabled) {
    box-shadow: 0 2px 0 0 #1d4ed8;
}

.btn-3d-warning {
    background-color: #eab308;
    color: #1f2937;
    box-shadow: 0 6px 0 0 #a16207;
}

.btn-3d-warning:hover,
.btn-3d-warning:focus,
.btn-3d-warning:active,
.btn-3d-warning:visited {
    color: #1f2937;
}

.btn-3d-warning:hover:not(:disabled) {
    background-color: #ca8a04;
    box-shadow: 0 8px 0 0 #a16207;
}

.btn-3d-warning:active:not(:disabled) {
    box-shadow: 0 2px 0 0 #a16207;
}

.btn-3d-success {
    background-color: #22c55e;
    box-shadow: 0 6px 0 0 #15803d;
}

.btn-3d-success:hover:not(:disabled) {
    background-color: #16a34a;
    box-shadow: 0 8px 0 0 #15803d;
}

.btn-3d-success:active:not(:disabled) {
    box-shadow: 0 2px 0 0 #15803d;
}

/*
 * Active payment-provider "tab" above the Stripe/PayPal checkout card
 * (resources/views/billing/subscribe-stripe.blade.php, subscribe-paypal.blade.php) — the current
 * processor's logo sits in a tab sharing the card's own background/border so it reads as an
 * attached tab title, not a floating logo; the negative margin-bottom overlaps the card's top
 * border by exactly its width so the seam between tab and card disappears.
 *
 * `--bs-card-bg`/`--bs-card-border-*` are only defined *within* `.card` itself (Bootstrap declares
 * them inside the `.card` rule, not at `:root`), so they resolve to nothing on this sibling `<div>`.
 * This theme also redefines `--bs-card-bg: var(--bs-component-bg)` (its own white/near-black
 * "component" surface color, `brand.css`/`app.min.css`) rather than reusing `--bs-body-bg` (the
 * page's own gray/near-black backdrop, a different color) — `--bs-component-bg` is the one actually
 * declared at `:root`/`[data-bs-theme="dark"]`, so it's the correct root-level equivalent to use here.
 */
.payment-provider-tab {
    background-color: var(--bs-component-bg);
    border: var(--bs-border-width) solid var(--bs-border-color);
    border-bottom: none;
    border-top-left-radius: var(--bs-border-radius);
    border-top-right-radius: var(--bs-border-radius);
    margin-bottom: calc(-1 * var(--bs-border-width));
    position: relative;
    z-index: 1;
}

/* The card sitting right under the tab — squares its top-left corner so the tab's own top-left
   corner is the only rounded corner in that spot, instead of two competing curves. */
.payment-provider-card {
    border-top-left-radius: 0;
}

/*
 * Onboarding checklist panel (Phase 6, docs/phases/phase-06-onboarding/specs.md). Modeled on the
 * template's own .app-theme-panel (resources/template/template_html/src/scss/app/_app-theme-panel.scss)
 * but anchored bottom-right instead of vertically centered, and collapsible down to just its header
 * (like a minimized chat widget) instead of sliding off-screen. Uses the theme's own CSS variables
 * so it follows the [data-bs-theme="dark"] toggle automatically, same as the rest of the app.
 */
.app-onboarding-panel {
    position: fixed;
    right: 1.25rem;
    bottom: 1.25rem;
    width: 640px;
    max-width: calc(100vw - 2.5rem);
    z-index: 1050;
    background: var(--bs-component-bg);
    border: var(--bs-border-width) solid var(--bs-border-color-translucent);
    border-radius: var(--bs-border-radius-lg);
    box-shadow: var(--bs-box-shadow-lg);
    overflow: hidden;
}

.app-onboarding-panel-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.75rem 1rem;
    cursor: pointer;
    font-weight: 600;
    color: var(--bs-component-color);
}

.app-onboarding-panel-progress {
    font-weight: 400;
    opacity: 0.6;
    font-size: 0.8rem;
    margin-inline-start: 0.35rem;
}

.app-onboarding-panel-caret {
    transition: transform 0.2s ease-in-out;
}

.app-onboarding-panel-body {
    max-height: 55vh;
    overflow-y: auto;
    padding: 0 1rem 1rem;
    border-top: var(--bs-border-width) solid var(--bs-border-color-translucent);
}

.app-onboarding-panel.collapsed .app-onboarding-panel-body {
    display: none;
}

.app-onboarding-panel.collapsed .app-onboarding-panel-caret {
    transform: rotate(180deg);
}

.app-onboarding-step {
    display: flex;
    gap: 0.65rem;
    padding: 0.85rem 0;
    border-bottom: var(--bs-border-width) solid var(--bs-border-color-translucent);
}

.app-onboarding-step:last-of-type {
    border-bottom: none;
}

.app-onboarding-step-indicator {
    flex: 0 0 auto;
    width: 1.5rem;
    height: 1.5rem;
    border-radius: 50%;
    border: var(--bs-border-width) solid var(--bs-border-color);
    display: flex;
    align-items: center;
    justify-content: center;
    margin-top: 0.15rem;
    color: transparent;
}

.app-onboarding-step-completed .app-onboarding-step-indicator {
    background: var(--bs-success);
    border-color: var(--bs-success);
    color: #fff;
}

.app-onboarding-step-completed .app-onboarding-step-title {
    text-decoration: line-through;
    opacity: 0.6;
}

.app-onboarding-step-title {
    font-weight: 600;
    margin-bottom: 0.25rem;
}

.app-onboarding-step-body {
    /* Without this, a flex child defaults to shrink-to-fit — the "mark as done" toggle's ms-auto
       would then only push to the edge of the text's own width, not the panel's right edge. */
    flex: 1 1 auto;
    min-width: 0;
}

.app-onboarding-step-content {
    font-size: 0.85rem;
    color: var(--bs-component-color);
    opacity: 0.85;
}

.app-onboarding-hide-btn {
    display: block;
    width: 100%;
    text-align: center;
    padding-top: 0.5rem;
}

/*
 * Release Notes accordion (every product page's `#releasesAccordion`) — every entry is expanded by
 * default (project owner direction, 2026-09-04) so a visitor can read the full changelog without
 * clicking through each release. Left at Bootstrap's own defaults, an expanded entry's header gets
 * the accordion's "active" highlight color/background (`.accordion-button:not(.collapsed)`) — with
 * every single entry expanded at once, that reads as a solid wall of highlighted titles rather than
 * a plain readable list. Resets the header back to its normal (non-active) look, and puts a small
 * drop shadow on the expanded body instead, so an open entry still visually reads as "open" without
 * every title looking selected.
 */
#releasesAccordion .accordion-button:not(.collapsed) {
    color: var(--bs-accordion-btn-color);
    background-color: var(--bs-accordion-bg);
    box-shadow: none;
}

#releasesAccordion .accordion-collapse.show .accordion-body {
    box-shadow: var(--bs-box-shadow-sm);
}

/* Register page two-column split (project owner direction) — the template's own `.register-content`
   caps at 22.5rem (form_plugins/page_register.html's single-column card width), too narrow for a
   side-by-side layout, so this page uses its own wider wrapper instead. */
.register-content-split {
    width: 100%;
    max-width: 55rem;
    margin: 0 auto;
    position: relative;
}

.register-split .register-split-left {
    border-right: 1px solid var(--bs-border-color);
    padding-right: 2rem;
}

.register-split .register-split-right {
    padding-left: 2rem;
}

@media (max-width: 767.98px) {
    /* Stacked layout (project owner direction): the halves' own left/right padding is dropped since
       they no longer sit side by side, but that would leave the form fields flush against the
       viewport edges — replaced with horizontal padding on the shared wrapper instead, so there's
       still breathing room on both sides. */
    .register-content-split {
        padding-left: 1rem;
        padding-right: 1rem;
    }

    .register-split .register-split-left {
        border-right: none;
        border-bottom: 1px solid var(--bs-border-color);
        padding-right: 0;
        padding-bottom: 2rem;
        margin-bottom: 1rem;
    }

    .register-split .register-split-right {
        padding-left: 0;
    }
}

/* Hero CTA glow button (landing page, project owner direction) — deliberately color-locked to the
   template's own blue gradient (bg-gradient-cyan-blue: #0eb3ff -> --bs-primary #0066ff) rather than
   the arbitrary magenta/purple originally supplied, so the glow effect still reads as on-brand. */
.btn-glow {
    position: relative;
    overflow: hidden;
    color: #fff;
    background: linear-gradient(45deg, #0eb3ff, var(--bs-primary));
    border: none;
    border-radius: 50px;
    padding: 12px 28px;
    font-size: 1.1rem;
    font-weight: bold;
    transition: transform 0.2s ease-in-out;
    box-shadow: 0 0 15px rgba(14, 179, 255, 0.6);
}

.btn-glow:hover {
    color: #fff;
    transform: scale(1.08);
    box-shadow: 0 0 25px rgba(0, 102, 255, 0.9);
}

.btn-glow::before {
    content: "";
    position: absolute;
    top: -50%;
    left: -50%;
    width: 200%;
    height: 200%;
    background: radial-gradient(circle, rgba(255, 255, 255, 0.4) 20%, transparent 60%);
    transform: rotate(25deg);
    animation: btn-glow-shine 3s infinite linear;
}

@keyframes btn-glow-shine {
    from {
        transform: translateX(-100%) rotate(25deg);
    }
    to {
        transform: translateX(100%) rotate(25deg);
    }
}

/*
 * Featured Performance Reports carousel controls (resources/views/landing/index.blade.php,
 * project owner direction) — Bootstrap's own `.carousel-control-prev/-next` are absolutely
 * positioned `inset: 0`, i.e. layered on top of the slide itself, which read as controls "inside"
 * the report card rather than belonging to the carousel as a whole. Pulled outside the card here
 * (past its horizontal edge, into the section's own `.container-xxl` gutter) and vertically
 * centered instead. The icon itself is a theme-aware circle (`var(--bs-body-bg)`/`var(--bs-body-
 * color)`, following the site's light/dark toggle) with a FontAwesome chevron — Bootstrap's default
 * icon is a hardcoded white arrow `background-image`, invisible against a light-mode background.
 */
#featuredPerformanceReportsCarousel > .carousel-control-prev,
#featuredPerformanceReportsCarousel > .carousel-control-next {
    top: 50%;
    bottom: auto;
    width: auto;
    transform: translateY(-50%);
    opacity: 1;
}

#featuredPerformanceReportsCarousel > .carousel-control-prev {
    left: -1.5rem;
}

#featuredPerformanceReportsCarousel > .carousel-control-next {
    right: -1.5rem;
}

/* A fixed dark circle in both themes (project owner direction: "on light theme the carousel
   button should be dark") rather than `var(--bs-body-bg)` — that followed the toggle correctly,
   but rendered as a near-white circle in light mode, which read as too light against the page. */
#featuredPerformanceReportsCarousel .carousel-control-icon {
    width: 2.5rem;
    height: 2.5rem;
    background-color: #1c2237;
    color: #fff;
    border: 1px solid rgba(255, 255, 255, 0.15);
    box-shadow: 0 0.125rem 0.5rem rgba(0, 0, 0, 0.25);
}

#featuredPerformanceReportsCarousel .carousel-control-icon:hover {
    background-color: #262d47;
}

@media (max-width: 575.98px) {
    /* Not enough gutter on narrow screens to sit fully outside without being clipped by the
       viewport — pulled back to just inside the card's own edge instead. */
    #featuredPerformanceReportsCarousel > .carousel-control-prev {
        left: 0.5rem;
    }

    #featuredPerformanceReportsCarousel > .carousel-control-next {
        right: 0.5rem;
    }
}

/*
 * Hero section — drop shadow onto the performance report showcase below it (project owner
 * direction) — the hero is the one casting the shadow, so the offset is a normal downward one (not
 * inverted) on the hero's own wrapper; `position: relative` + `z-index` lifts it above the next
 * section in stacking order so the shadow actually renders over that section's background instead
 * of being drawn underneath it.
 */
.hero-drop-shadow {
    position: relative;
    z-index: 1;
    box-shadow: 0 10px 15px -8px rgba(0, 0, 0, 0.1);
}

/*
 * Hero dot-grid background (project owner-supplied reference) — a fine copper-colored dot grid
 * behind the hero content, faded out toward the center via a two-axis mask so it only really reads
 * near the section's edges. `.hero-drop-shadow` is this decorative layer's positioning root
 * (already `position: relative`); `.hero-content` (the hero's actual content wrapper) is lifted
 * above it with its own `z-index`.
 *
 * Colors translated from the reference's `light-dark(...)` CSS function to this project's own
 * `[data-bs-theme="dark"]` toggle convention — `light-dark()` follows the browser/OS
 * `prefers-color-scheme`, not the site's manual dark-mode toggle, so using it directly would let
 * the pattern's color disagree with the rest of the page whenever a visitor's OS preference and
 * chosen site theme differ. Only the two colors the pattern actually uses (`--copper`/`--copper-hi`)
 * are carried over — the reference's much larger design-system variable dump (fonts, layout
 * max-widths, panel/surface/text colors, etc.) belongs to a different site's design system and
 * isn't reused here.
 */
.hero-dot-grid {
    --copper: #a75f14;
    --copper-hi: #8f5210;
    --pitch: 16px;
    --rise: 11px;
    --edge: 1.15px;
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background-image:
        radial-gradient(circle at var(--edge) var(--edge), color-mix(in srgb, var(--copper) 38%, transparent) 0 .65px, transparent var(--edge)),
        radial-gradient(circle at var(--edge) var(--edge), color-mix(in srgb, var(--copper) 38%, transparent) 0 .65px, transparent var(--edge));
    background-size: var(--pitch) var(--rise), var(--pitch) var(--rise);
    background-position: 0 0, calc(var(--pitch) / 2) calc(var(--rise) / 2);
    -webkit-mask-image:
        linear-gradient(90deg, #000 0%, #00000008 30% 70%, #000 100%),
        linear-gradient(#0000 0%, #00000024 4%, #00000008 20% 74%, #000 100%);
    -webkit-mask-composite: source-over;
    mask-image:
        linear-gradient(90deg, #000 0%, #00000008 30% 70%, #000 100%),
        linear-gradient(#0000 0%, #00000024 4%, #00000008 20% 74%, #000 100%);
    mask-composite: add;
}

[data-bs-theme="dark"] .hero-dot-grid {
    --copper: #d08f39;
    --copper-hi: #e5a855;
}

.hero-content {
    position: relative;
    z-index: 1;
}

/*
 * Landing mini Performance Report Explorer — Analysis chart fade-in (project owner direction) —
 * the chart otherwise pops in instantly the moment ECharts finishes its first render, which read
 * as "sudden" right after switching carousel slides. `is-visible` is added by
 * landing-performance-report-explorer.blade.php's own JS right after that first render.
 */
.hero-chart-fade-in {
    opacity: 0;
    transition: opacity 0.5s ease-out;
}

.hero-chart-fade-in.is-visible {
    opacity: 1;
}
