@view-transition {
    navigation: auto;
}

/* Backdrops get their own transition names so they hold still while only the content moves. */
.sidebar {
    view-transition-name: sidebar;
}

.aurora {
    view-transition-name: aurora;
}

@media (prefers-reduced-motion: no-preference) {
    ::view-transition-old(root) {
        animation: 180ms ease-in both page-out;
    }

    ::view-transition-new(root) {
        animation: 320ms cubic-bezier(.2, .8, .2, 1) 60ms both page-in;
    }

    @keyframes page-out {
        to { opacity: 0; transform: translateY(-16px); filter: blur(2px); }
    }

    @keyframes page-in {
        from { opacity: 0; transform: translateY(16px); }
    }
}

/* Rescales every Bootstrap rem-based measurement (font sizes, spacing, component
   sizing) proportionally — Bootstrap's own CSS was compiled assuming a 16px root. */
html {
    font-size: 14px;
}

:root {
    --caution-tape-height: 0.5rem;
}

.caution-tape {
    width: 100%;
    height: var(--caution-tape-height);
    position: fixed;
    top: 0;
    left: 0;
    z-index: 1;
    background-image: repeating-linear-gradient(135deg, #303030 0 1rem, #e2b91b 1rem 2rem);
    box-shadow: 0 0 20px #0007;
}

/* Spacing scales with the content area's width, not the viewport's, so the sidebar doesn't
   skew it. cqw can't measure the element that declares the container, hence the inner wrapper
   carrying the padding. --gutter is unregistered, so cqw in it resolves where it's used —
   always inside .content. */
.content {
    container-type: inline-size;
    --gutter: clamp(2rem, 5cqw, 5rem);
}

.content-body {
    padding: var(--gutter) var(--gutter) calc(var(--gutter) * 2);
}

.page-header {
    margin-bottom: calc(var(--gutter) / 2);
}

/* Trims the extra space line-height reserves above capital letters and below the
   baseline — tightens vertical centering for short bits of text like a badge. */
.text-box-trim {
    text-box: trim-both cap alphabetic;
}

/* Classic table trick: width: 1% makes the browser's table layout treat a column as
   needing minimal space, so it shrinks to fit its content instead of stretching. */
.w-min {
    width: 1%;
    white-space: nowrap;
}

/* Chip look for a copyable value (see copy-code.js) — border and subtle background make
   it clear what's being copied. Bootstrap's own custom properties, not hardcoded colors,
   so it stays correct across light/dark mode without a media query of its own. */
code[data-copy-value] {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.1rem 0.4rem;
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius-sm);
    background-color: var(--bs-tertiary-bg);
}

/* Sizing taken from the Appstack 4 Bootstrap theme (its src/scss/1-variables/_app.scss), applied
   on top of Bootstrap's compiled CSS. Appstack sets these in SCSS, where one variable reaches every
   component; here only the radius and line height are runtime variables, so buttons and form fields
   (which share Appstack's $input-btn-* values) are restated one by one. Font sizes stay Bootstrap's,
   except .btn-sm's. */
:root {
    --bs-body-line-height: 1.625;
    --bs-border-radius: 0.2rem;
    --bs-border-radius-sm: 0.1rem;
    --bs-border-radius-lg: 0.3rem;
}

.btn {
    --bs-btn-padding-y: 0.25rem;
    --bs-btn-padding-x: 0.7rem;
    --bs-btn-line-height: 1.625;
}

.btn-sm {
    --bs-btn-padding-y: 0.15rem;
    --bs-btn-padding-x: 0.5rem;
    --bs-btn-font-size: 0.75rem;
}

.btn-lg {
    --bs-btn-padding-y: 0.45rem;
    --bs-btn-padding-x: 1rem;
}

/* Appstack's secondary, a darker gray than Bootstrap's. Theme variables reach .text-secondary and
   the subtle badge; buttons bake the color in, so they're restated. Dark mode uses the contratos
   system's lighter, neutral gray instead (Appstack's would read too dim there). Light comes first:
   :root also matches a dark <html> at the same specificity. */
:root,
[data-bs-theme="light"] {
    --bs-secondary: #495057;
    --bs-secondary-rgb: 73, 80, 87;
    --bs-secondary-text-emphasis: #1d2023;
    --bs-secondary-bg-subtle: #dbdcdd;
    --bs-secondary-border-subtle: #b6b9bc;
}

[data-bs-theme="dark"] {
    --bs-secondary: #7d7d7d;
    --bs-secondary-rgb: 125, 125, 125;
    --bs-secondary-text-emphasis: #92969a;
    --bs-secondary-bg-subtle: #0f1011;
    --bs-secondary-border-subtle: #2c3034;
}

.btn-secondary {
    --bs-btn-bg: #495057;
    --bs-btn-border-color: #495057;
    --bs-btn-hover-bg: #3e444a;
    --bs-btn-hover-border-color: #3a4046;
    --bs-btn-focus-shadow-rgb: 100, 106, 112;
    --bs-btn-active-bg: #3a4046;
    --bs-btn-active-border-color: #373c41;
    --bs-btn-disabled-bg: #495057;
    --bs-btn-disabled-border-color: #495057;
}

[data-bs-theme="dark"] .btn-secondary {
    --bs-btn-color: #fff;
    --bs-btn-bg: #7d7d7d;
    --bs-btn-border-color: #7d7d7d;
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: #8a8a8a;
    --bs-btn-hover-border-color: #8a8a8a;
    --bs-btn-focus-shadow-rgb: 125, 125, 125;
    --bs-btn-active-color: #fff;
    --bs-btn-active-bg: #969696;
    --bs-btn-active-border-color: #969696;
    --bs-btn-disabled-color: #fff;
    --bs-btn-disabled-bg: #7d7d7d;
    --bs-btn-disabled-border-color: #7d7d7d;
}

.btn-link:not(:hover) {
    text-decoration: none;
}

.form-control,
.form-select {
    padding: 0.25rem 0.7rem;
    line-height: 1.625;
}

.form-select {
    padding-right: 2.1rem;
    background-position: right 0.7rem center;
}

textarea.form-control {
    min-height: calc(1.625em + 0.5rem + calc(var(--bs-border-width) * 2));
}

/* Appstack's cool gray page background. Surfaces sitting on it (cards, form fields, menus) stay
   white through --surface-bg — Bootstrap paints them with --bs-body-bg, which would turn them
   gray along with the page. Comes before the dark block: :root also matches a dark <html>, at the
   same specificity, so order decides. */
:root,
[data-bs-theme="light"] {
    --bs-body-bg: #f7f9fc;
    --bs-body-bg-rgb: 247, 249, 252;
    --surface-bg: #fff;
}

/* Neutral gray dark mode taken from the contratos system (an Appstack app with its own dark
   palette), replacing Bootstrap's bluish grays: flat gray text, white headings. The page goes much
   darker than contratos' (5% lightness), so cards sit lighter than it. Applies wherever
   data-bs-theme="dark" lands, whether the page-wide toggle or a locally forced scope (sidebar,
   login screen). */
[data-bs-theme="dark"] {
    --bs-body-bg: #0d0d0d;
    --bs-body-bg-rgb: 13, 13, 13;
    --surface-bg: #313131;
    --bs-body-color: #a8a8a8;
    --bs-body-color-rgb: 168, 168, 168;
    --bs-heading-color: var(--bs-emphasis-color);
    --bs-secondary-color: rgb(168 168 168 / 0.75);
    --bs-secondary-color-rgb: 168, 168, 168;
    --bs-tertiary-color: rgb(168 168 168 / 0.5);
    --bs-tertiary-color-rgb: 168, 168, 168;
    --bs-secondary-bg: #3a3a3a;
    --bs-secondary-bg-rgb: 58, 58, 58;
    --bs-tertiary-bg: #2e2e2e;
    --bs-tertiary-bg-rgb: 46, 46, 46;
    --bs-border-color: #515151;
}

.form-control,
.form-select,
.form-check-input {
    --bs-form-check-bg: var(--surface-bg);
    background-color: var(--surface-bg);
}

.dropdown-menu {
    --bs-dropdown-bg: var(--surface-bg);
}

/* A wide table (e.g. redirect URIs collection, many columns) inside a card would otherwise
   push past the card's own border instead of scrolling within it. Look and spacing from Appstack:
   no visible border, a soft shadow in light mode. Dark mode drops the shadow and sets the card
   apart by background alone (--surface-bg lighter than the page). */
.card {
    --bs-card-spacer-y: 1.25rem;
    --bs-card-spacer-x: 1.25rem;
    --bs-card-cap-padding-y: 1rem;
    --bs-card-cap-padding-x: 1.25rem;
    --bs-card-bg: var(--surface-bg);
    --bs-card-cap-bg: transparent;
    --bs-card-border-color: transparent;
    box-shadow: 0 0 0.875rem 0 rgb(41 48 66 / 0.05);
    overflow-x: auto;
}

[data-bs-theme="dark"] .card {
    box-shadow: none;
}

.table {
    --bs-table-bg: transparent;
}

.table > :not(caption) > * > * {
    padding: 0.75rem;
}

/* A table filling a card lines its outer columns up with the card's padding and leaves the edges
   to the card. */
.card > .table {
    margin-bottom: 0;
}

.card > .table tr > :first-child {
    padding-left: var(--bs-card-spacer-x);
}

.card > .table tr > :last-child {
    padding-right: var(--bs-card-spacer-x);
}

.card > .table > tbody > tr:last-child > * {
    border-bottom-width: 0;
}

/* Lifts a surface off a colorful backdrop (the login card over the aurora): shadow in light
   mode. In dark mode the regular surface gray sits too close to the backdrop's own lightness, so
   the card takes the page's near-black instead, with no shadow (it wouldn't show) and no border. */
.elevated {
    border: var(--bs-border-width) solid transparent;
    box-shadow: var(--bs-box-shadow-lg);
}

[data-bs-theme="dark"] .elevated {
    --bs-card-bg: var(--bs-body-bg);
    box-shadow: none;
}

/* Bare button in the emphasis color (white in dark, black in light) at partial opacity. Set
   through .btn's own variables so hover/active don't override it; --btn-subtle-opacity tunes
   it per use. */
.btn-subtle {
    --btn-subtle-opacity: 75%;
    --bs-btn-color: color-mix(in srgb, var(--bs-emphasis-color) var(--btn-subtle-opacity), transparent);
    --bs-btn-hover-color: var(--bs-btn-color);
    --bs-btn-active-color: var(--bs-btn-color);
    --bs-btn-padding-x: 0;
    --bs-btn-padding-y: 0;
    --bs-btn-border-width: 0;
}

/* The selected theme stands out by opacity only. Font size inherits so the wrapper sets it
   once — .btn would otherwise reset it. */
[data-change-theme] {
    --btn-subtle-opacity: 20%;
    font-size: inherit;
}

[data-change-theme][aria-pressed="true"] {
    --btn-subtle-opacity: 75%;
}

/* Fades out the edge of a scrollable box where there's more to scroll, driven by its own scroll
   position. With nothing to scroll the timeline is inactive and both fades stay at their 0px
   initial value. Registered properties, so the keyframes interpolate them instead of flipping. The
   @supports guard matters: without scroll timelines the animations would run on time instead,
   with zero duration, and stick at their final keyframe. */
@property --scroll-fade-top {
    syntax: "<length>";
    inherits: false;
    initial-value: 0px;
}

@property --scroll-fade-bottom {
    syntax: "<length>";
    inherits: false;
    initial-value: 0px;
}

@supports (animation-timeline: scroll()) {
    .scroll-fade {
        mask-image: linear-gradient(
            to bottom,
            transparent,
            #000 var(--scroll-fade-top),
            #000 calc(100% - var(--scroll-fade-bottom)),
            transparent
        );
        animation: scroll-fade-top linear both, scroll-fade-bottom linear both;
        animation-timeline: scroll(self), scroll(self);
    }

    @keyframes scroll-fade-top {
        0% { --scroll-fade-top: 0px; }
        10%, 100% { --scroll-fade-top: 1.5rem; }
    }

    @keyframes scroll-fade-bottom {
        0%, 90% { --scroll-fade-bottom: 1.5rem; }
        100% { --scroll-fade-bottom: 0px; }
    }
}

/* Stays in view while the page scrolls; the nav inside scrolls on its own when it doesn't fit. */
.sidebar {
    position: sticky;
    top: 0;
    height: 100vh;
}

/* Layers listed top to bottom: vertical vignette, purple core, magenta bridge, red top,
   blue bottom, subtle indigo at the base, diagonal base. */
.sidebar {
    background-color: #080410;
    background-image:
        linear-gradient(180deg, rgb(6 3 14 / .65) 0%, transparent 22%, transparent 72%, rgb(6 3 14 / .85) 100%),
        radial-gradient(ellipse 160% 70% at 50% 50%, rgb(118 41 187 / .70) 0%, rgb(86 26 134 / .30) 45%, transparent 75%),
        radial-gradient(ellipse 110% 40% at 30% 34%, rgb(175 31 95 / .45) 0%, transparent 70%),
        radial-gradient(ellipse 135% 60% at 0% 12%, rgb(195 50 75 / .50) 0%, transparent 70%),
        radial-gradient(ellipse 135% 60% at 100% 88%, rgb(47 104 197 / .50) 0%, transparent 70%),
        radial-gradient(ellipse 150% 40% at 40% 94%, rgb(54 32 136 / .32) 0%, transparent 70%),
        linear-gradient(160deg, #1c0a2e 0%, #0e0925 55%, #17061a 100%);
}

/* Full-screen take on the sidebar gradient, behind the login screen. Fixed layer so it spans
   the viewport regardless of content height. Layers top to bottom: noise tile (breaks the
   banding large gradients show), radial vignette, purple core, magenta bridge, red top left,
   blue bottom right, subtle indigo at the base, diagonal base. */
.aurora {
    --aurora-noise:     url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .13 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
    position: fixed;
    inset: 0;
    z-index: -1;
    background-color: #0a0514;
    background-image:
        var(--aurora-noise),
        radial-gradient(ellipse 80% 85% at 50% 50%, transparent 55%, rgb(8 4 18 / .75) 100%),
        radial-gradient(ellipse 60% 75% at 50% 50%, rgb(147 51 234 / .65) 0%, rgb(107 33 168 / .30) 45%, transparent 75%),
        radial-gradient(ellipse 45% 50% at 32% 35%, rgb(219 39 119 / .40) 0%, transparent 70%),
        radial-gradient(ellipse 55% 65% at 0% 8%, rgb(244 63 94 / .50) 0%, transparent 70%),
        radial-gradient(ellipse 55% 65% at 100% 92%, rgb(59 130 246 / .50) 0%, transparent 70%),
        radial-gradient(ellipse 70% 40% at 42% 100%, rgb(67 40 170 / .32) 0%, transparent 70%),
        linear-gradient(135deg, #230c3a 0%, #110b2e 55%, #1d0720 100%);
    /* One size per layer: a shorter list repeats cyclically and would tile the gradients too. */
    background-size: 180px 180px, auto, auto, auto, auto, auto, auto, auto;
}

/* Portrait (phones) is closer to the sidebar's shape, so the ellipses go back to its proportions. */
@media (orientation: portrait) {
    .aurora {
        background-image:
            var(--aurora-noise),
            radial-gradient(ellipse 110% 80% at 50% 50%, transparent 55%, rgb(8 4 18 / .75) 100%),
            radial-gradient(ellipse 140% 60% at 50% 50%, rgb(147 51 234 / .65) 0%, rgb(107 33 168 / .30) 45%, transparent 75%),
            radial-gradient(ellipse 100% 38% at 30% 34%, rgb(219 39 119 / .40) 0%, transparent 70%),
            radial-gradient(ellipse 120% 50% at 0% 10%, rgb(244 63 94 / .50) 0%, transparent 70%),
            radial-gradient(ellipse 120% 50% at 100% 90%, rgb(59 130 246 / .50) 0%, transparent 70%),
            radial-gradient(ellipse 150% 35% at 40% 100%, rgb(67 40 170 / .32) 0%, transparent 70%),
            linear-gradient(160deg, #230c3a 0%, #110b2e 55%, #1d0720 100%);
    }
}

:root[data-bs-theme="dark"] .aurora {
    filter: brightness(.75);
}
