/* Consolidated global stylesheet - merged from app.css, prose.css, rich-text-editor.css and
   fonts.css (in that order, each kept as its own section below) so the site loads one CSS
   request instead of four; PageSpeed Insights flagged the four separate files as render-blocking
   requests. Purely a request-count consolidation - no rule was changed or reordered relative to
   its original file.

   The last section, the Bootstrap Icons subset between the <bootstrap-icons> markers, is generated
   by Assets/icons (npm run build) and rewritten wholesale on each build - don't hand-edit it. */

/* ===== app.css ===== */

html {
    height: 100%;
    font-family: 'Poppins', sans-serif;
    scroll-behavior: smooth;
    /* Solid canvas colour under the gradient (body::before below). Must equal the gradient's
       END colour: mobile browsers briefly expose the canvas below the layer while the URL bar
       collapses, and this is also the colour browsers paint outside the page (Android Chrome
       reports it as the tab background for the 3-button nav bar; Safari uses it for overscroll
       and letterboxing) - so it has to read as a seamless continuation, never a line or white. */
    background-color: #32006e;
}

/* Users who've asked the OS/browser to reduce motion get an instant jump instead - smooth-scroll
   is a nicety, not something to override an accessibility preference for. */
@media (prefers-reduced-motion: reduce) {
    html {
        scroll-behavior: auto;
    }
}

/* If NavMenu (Components/Shared/NavMenu.razor) ever becomes sticky/fixed, give every
   scroll-anchor target breathing room under it so the header doesn't cover the scrolled-to
   element - e.g.: [id^="pagecomponent-"] { scroll-margin-top: <header-height>px; } */
html body h1, html body h2, html body h3, html body h4, html body h5, html body h6 {
    font-family: 'Sora', sans-serif;
}

/* FocusOnNavigate (Routes.razor) programmatically focuses the page's h1 for
   accessibility after navigation; suppress the resulting visible focus ring
   since h1 isn't an interactive element. */
html body h1:focus {
    outline: none;
    box-shadow: none;
}
html body {
    min-height: 100vh;
    margin: 0;
    font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    /* Must stay explicit - bootstrap.min.css (loaded after this) sets body to #fff. */
    background: transparent;
    /* App.razor sets viewport-fit=cover, so iPhones in landscape lay the page out under the
       notch: keep content clear of it (the gradient layer below is viewport-relative and still
       fills those strips). Resolves to 0 on Android and desktop. */
    padding-left: env(safe-area-inset-left, 0px);
    padding-right: env(safe-area-inset-right, 0px);
}

/* The page gradient lives on a fixed pseudo-element rather than background-attachment: fixed,
   which Android Chrome re-lays out on every URL-bar show/hide and iOS Safari ignores outright.
   With viewport-fit=cover (App.razor) the viewport ends at the physical bottom edge, so the
   gradient's end colour lands exactly on the edge; html carries that same colour underneath so
   the strip Chrome exposes while resizing this layer mid-scroll is indistinguishable from it. */
body::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    background: linear-gradient( 180deg, #020024 0%, #180552 80%, #32006e 100% );
    pointer-events: none;
}

.menu {
    background: #020024;
    border-bottom: 2px solid rgba(255, 255, 255, 0.1);
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}




.card {
    background-color: #201e34 !important;
}

.nav-link{
    color: rgb(255 255 255);
    font-size: 1.4rem;
}

.nav-link:hover{
    color: #6000ff;
}

.nav-link.active {
    color: #b973f7;
}

.content{
    min-width: 0;
}

.casino-table {
    border-radius: 8px;
    overflow: hidden;
}

    .casino-table td,
    .casino-table th {
        padding: 0.6rem 0.75rem;
    }

.casino-logo {
    width: 45px;
    height: 45px;
    object-fit: contain;
    padding: 4px;
}

.casino-logo-placeholder {
    width: 45px;
    height: 45px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #343a40;
    border-radius: 8px;
    color: #888;
}




.publisher-dropdown {
    background: #fff;
    border: 1px solid #dee2e6;
}

/* No width here: NavMenu writes the logo's display size into width/height, and width:auto would
   override the attribute (as img-fluid's height:auto does the other one), leaving no box reserved
   before the image loads. img-fluid's max-width:100% still scales it down in a narrow navbar. */
.navbar-logo {
    max-height: 48px;
}

.navIcon {
    color: #6F4CFF;
}

/* Nav links are laid out as a single-line flex row so the icon can never be pushed above its
   label when the row runs short of space - the row wraps as whole items instead. Scoped to
   .menu because .nav-link above is global and also covers AdminMenu, EditorTabs and ProfileNav. */
.menu .nav-link {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    white-space: nowrap;
}

.menu .navbar-nav {
    flex-wrap: wrap;
    row-gap: 0.25rem;
}

.menu .nav-item {
    flex-shrink: 0;
}

/* Fixed square so an image icon takes the same box as a glyph and labels stay aligned when the
   menu is stacked; em rather than px so both track the link's font size, as in HeroBannerRender. */
.menu .nav-link > .navIcon,
.menu .navmenu__icon-img {
    width: 1.25em;
    height: 1.25em;
    flex-shrink: 0;
    text-align: center;
}

.menu .navmenu__icon-img {
    display: inline-block;
    background-color: #6F4CFF; /* same purple as .navIcon */
    -webkit-mask-image: var(--nav-icon-url);
    mask-image: var(--nav-icon-url);
    -webkit-mask-size: contain;
    mask-size: contain;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
}

/* Once the menu is stacked in the collapsed panel a link owns its whole row, so a long label is
   free to wrap onto a second line - the icon stays beside it, level with the first line. */
@media (max-width: 1199.98px) {
    .menu .nav-link {
        white-space: normal;
        align-items: flex-start;
    }
}

/* Just above the xl collapse point there's room for the row but not at full size. */
@media (min-width: 1200px) and (max-width: 1399.98px) {
    .menu .nav-link {
        font-size: 1.15rem;
    }
}

/* Stand-in shown while the page is still prerendering, so the field keeps its height and the
   form doesn't jump when the real editor takes its place on circuit connect. */
.cg-rte-loading {
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid rgba(255, 255, 255, 0.15);
    border-radius: 8px;
    background-color: #201e34;
    color: rgba(255, 255, 255, 0.4);
}


/* Editor chrome is in the rich-text-editor.css section below. */

/* ===== prose.css ===== */

/* Typography for admin-authored HTML.

   Applied by adding "prose" to whatever element wraps a MarkupString - rich text blocks,
   callouts, verdicts, accordion bodies, FAQ answers and text sections all share it, so
   formatting looks the same wherever content is placed.

   This is a global stylesheet rather than scoped component CSS on purpose: the markup comes
   from a MarkupString, so it carries no scope attribute and scoped rules would need ::deep at
   every call site. Component .razor.css files still layer local overrides on top - a scoped
   ".callout__body ::deep a" compiles to a two-class selector and outranks ".prose a" here, so
   those overrides keep winning without !important. */

.prose {
    font-size: 1rem;
    line-height: 1.7;
    color: rgba(255, 255, 255, 0.8);
}

.prose h1,
.prose h2,
.prose h3,
.prose h4,
.prose h5,
.prose h6 {
    font-family: 'Sora', sans-serif;
    font-weight: 700;
    color: #ffffff;
    line-height: 1.2;
    margin: 0 0 1rem;
    position: relative;
    padding-bottom: 0.75rem;
}

    .prose h1::after,
    .prose h2::after,
    .prose h3::after,
    .prose h4::after,
    .prose h5::after,
    .prose h6::after{
        content: "";
        position: absolute;
        left: 0;
        bottom: 0;
        width: 5.5rem;
        height: 3px;
        border-radius: 999px;
        background: linear-gradient(135deg, #6000ff 0%, #b973f7 100%);
    }

.prose > :first-child {
    margin-top: 0;
}

/* Every heading inside an embedded component must fall back to non-prose defaults for whatever
   the component doesn't style itself, WITHOUT being able to override what it does style (e.g.
   .games-list__title fully owns color/size/its own underline bar, but .games-list__card-title
   only sets font/color and relies on not being underlined by default - both must work). Scoped
   Blazor CSS always adds a class-or-attribute specificity token on top of any selector (its
   [b-xxxxxxxx] scope attribute), so keeping this selector at exactly ONE such token (the
   "[id^=...]" attribute) guarantees it can never outrank ANY scoped component rule, on any
   property, while still outranking the plain ".prose h1" rules above (which are also only one
   token) via the extra type selectors here. ":where(.prose)" keeps the "only inside rich text"
   condition without spending a specificity token on it - :where() always counts as zero. */
:where(.prose) div[id^="pagecomponent-"] h1,
:where(.prose) div[id^="pagecomponent-"] h2,
:where(.prose) div[id^="pagecomponent-"] h3,
:where(.prose) div[id^="pagecomponent-"] h4,
:where(.prose) div[id^="pagecomponent-"] h5,
:where(.prose) div[id^="pagecomponent-"] h6 {
    all: revert;
}

:where(.prose) div[id^="pagecomponent-"] h1::after,
:where(.prose) div[id^="pagecomponent-"] h2::after,
:where(.prose) div[id^="pagecomponent-"] h3::after,
:where(.prose) div[id^="pagecomponent-"] h4::after,
:where(.prose) div[id^="pagecomponent-"] h5::after,
:where(.prose) div[id^="pagecomponent-"] h6::after {
    content: none;
}

/* An embedded component sitting between two runs of prose. PageNodeRenderer wraps every node in
   a div carrying this id, which is what makes it addressable here without the render component
   having to add a class of its own. Margins rather than padding so they collapse against the
   paragraph margins either side instead of stacking on top of them. */
.prose > div[id^="pagecomponent-"] {
    margin: 2rem 0;
}

.prose h1 { font-size: clamp(1.5rem, 3vw, 2rem); }
.prose h2 { font-size: clamp(1.35rem, 2.6vw, 1.75rem); }
.prose h3 { font-size: clamp(1.15rem, 2.2vw, 1.4rem); }
.prose h4 { font-size: 1.1rem; }
.prose h5 { font-size: 1rem; }
.prose h6 { font-size: 0.95rem; }

.prose p {
    margin-bottom: 1rem;
}

.prose a {
    color: #b973f7;
    text-decoration: underline;
    text-underline-offset: 2px;
}

.prose a:hover {
    color: #ffffff;
}

.prose ul,
.prose ol {
    padding-left: 1.35rem;
    margin-bottom: 1rem;
}

.prose li {
    margin-bottom: 0.4rem;
}

.prose strong,
.prose b {
    color: #ffffff;
    font-weight: 600;
}

.prose blockquote {
    margin: 1.25rem 0;
    padding: 0.75rem 1.25rem;
    border-left: 3px solid #b973f7;
    background: rgba(255, 255, 255, 0.04);
    border-radius: 0 12px 12px 0;
    color: rgba(255, 255, 255, 0.75);
}

.prose code {
    font-size: 0.9em;
    padding: 0.15rem 0.4rem;
    border-radius: 6px;
    background: rgba(255, 255, 255, 0.08);
    color: #e9d5ff;
}

.prose pre {
    padding: 1rem;
    border-radius: 12px;
    background: rgba(2, 0, 36, 0.6);
    border: 1px solid rgba(185, 115, 247, 0.2);
    overflow-x: auto;
}

.prose pre code {
    padding: 0;
    background: transparent;
}

.prose hr {
    border: 0;
    border-top: 1px solid rgba(185, 115, 247, 0.25);
    margin: 2rem 0;
}

/* Wide tables scroll inside the block instead of stretching the page. */
.prose table {
    width: 100%;
    margin-bottom: 1rem;
    border-collapse: collapse;
}

.prose th,
.prose td {
    padding: 0.6rem 0.75rem;
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
    text-align: left;
}

.prose th {
    color: #ffffff;
    font-weight: 600;
    background: rgba(255, 255, 255, 0.04);
}

.prose img {
    max-width: 100%;
    height: auto;
    border-radius: 12px;
}

.prose figure {
    margin: 1.25rem 0;
}

.prose figcaption {
    margin-top: 0.5rem;
    font-size: 0.875rem;
    color: rgba(255, 255, 255, 0.6);
}

/* ===== rich-text-editor.css ===== */

/* Chrome for the TipTap-based admin rich text editor.

   Only the toolbar and the frame live here. The editing surface carries the "prose" class, so it
   picks up the exact typography from prose.css that the published page uses - what the author sees
   while writing is what ships. Colours are the admin palette used by .card.bg-dark.border-secondary
   and the accent from the render components.

   This is a global stylesheet rather than scoped component CSS because TipTap creates the
   .tiptap/.ProseMirror elements itself at runtime, so they never receive Blazor's scope attribute. */

.cg-rte {
    border: 1px solid rgba(255, 255, 255, 0.15);
    border-radius: 8px;
    background-color: #201e34;
    overflow: hidden;
}

.cg-rte--disabled {
    opacity: 0.6;
    pointer-events: none;
}

/* Toolbar ------------------------------------------------------------------------------------ */

.cg-rte-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 2px;
    padding: 4px 6px;
    background-color: #191730;
    border-bottom: 1px solid rgba(255, 255, 255, 0.12);
}

.cg-rte-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 1px solid transparent;
    border-radius: 6px;
    background: transparent;
    color: rgba(255, 255, 255, 0.85);
    font-size: 0.95rem;
    line-height: 1;
    cursor: pointer;
    transition: background-color .12s ease, border-color .12s ease, color .12s ease;
}

.cg-rte-btn:hover:not(:disabled) {
    background: rgba(185, 115, 247, 0.18);
    border-color: rgba(185, 115, 247, 0.4);
}

.cg-rte-btn.active {
    background: rgba(185, 115, 247, 0.32);
    border-color: rgba(185, 115, 247, 0.6);
    color: #ffffff;
}

.cg-rte-btn:disabled {
    opacity: 0.35;
    cursor: default;
}

/* Keyboard users still need to see focus; mouse users don't, since :hover already responds. */
.cg-rte-btn:focus-visible,
.cg-rte-select:focus-visible {
    outline: 2px solid #b973f7;
    outline-offset: 1px;
}

.cg-rte-sep {
    width: 1px;
    align-self: stretch;
    margin: 2px 4px;
    background: rgba(255, 255, 255, 0.12);
}

.cg-rte-select {
    height: 30px;
    max-width: 140px;
    padding: 0 6px;
    border: 1px solid rgba(255, 255, 255, 0.15);
    border-radius: 6px;
    background-color: #201e34;
    color: rgba(255, 255, 255, 0.85);
    font-size: 0.85rem;
    cursor: pointer;
}

.cg-rte-select--narrow {
    max-width: 90px;
}

.cg-rte-select:hover {
    border-color: rgba(185, 115, 247, 0.4);
}

/* Context bars: the link bar (manually toggled) and the image-size bar (shown whenever an image is
   selected) share this styling - both are a row of controls under the toolbar, just with different
   contents. -------------------------------------------------------------------------------------- */

.cg-rte-contextbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    padding: 6px;
    background: #191730;
    border-bottom: 1px solid rgba(255, 255, 255, 0.12);
}

.cg-rte-contextbar .form-control {
    flex: 1 1 220px;
    min-width: 0;
    background-color: #201e34;
    border-color: rgba(255, 255, 255, 0.2);
    color: #ffffff;
}

.cg-rte-contextbar .form-control:focus {
    background-color: #201e34;
    border-color: #b973f7;
    box-shadow: none;
    color: #ffffff;
}

.cg-rte-contextbar-label {
    font-size: 0.8rem;
    color: rgba(255, 255, 255, 0.6);
    margin-right: 2px;
}

/* LinkField hosted in the link bar. It is a form control in the block editors, so it brings
   Bootstrap's light defaults with it; these give it the bar's dark treatment and let its two
   controls share the row with the Apply/Remove/Cancel buttons. */
.cg-rte-contextbar .form-select,
.cg-rte-contextbar .list-group-item {
    background-color: #201e34;
    border-color: rgba(255, 255, 255, 0.2);
    color: #ffffff;
}

.cg-rte-contextbar .form-select:focus {
    border-color: #b973f7;
    box-shadow: none;
}

.cg-rte-contextbar .list-group-item-action:hover {
    background-color: #2b2848;
    color: #ffffff;
}

/* The picker's own layout wrapper must not stretch, or it pushes the buttons onto a second row. */
.cg-rte-contextbar > div:first-child {
    flex: 1 1 340px;
    min-width: 0;
}

.cg-rte-contextbar .form-check-label {
    color: rgba(255, 255, 255, 0.75);
    white-space: nowrap;
}

.cg-rte-contextbar--error {
    color: #ffb4b4;
    font-size: 0.85rem;
    padding-top: 0;
}

/* Editing surface ---------------------------------------------------------------------------- */

/* Height comes from an inline style set by the component; this scrolls within it rather than
   growing the page, so the toolbar stays put on long documents. */
.cg-rte-content {
    overflow-y: auto;
    padding: 12px 14px;
}

.cg-rte-content .tiptap {
    outline: none;
    min-height: 100%;
}

/* TipTap's Placeholder extension exposes the text as a data attribute on the first empty block. */
.cg-rte-content .tiptap p.is-editor-empty:first-child::before {
    content: attr(data-placeholder);
    float: left;
    height: 0;
    pointer-events: none;
    color: rgba(255, 255, 255, 0.4);
}

/* Selected images get a visible ring, otherwise there's no feedback that one is selected. */
.cg-rte-content .tiptap img.ProseMirror-selectednode {
    outline: 2px solid #b973f7;
    outline-offset: 2px;
}

.cg-rte-content .tiptap hr.ProseMirror-selectednode {
    border-top-color: #b973f7;
}

/* prose.css strips the top margin from its first child only. Inside the editor, blocks are added
   and removed constantly, so headings need their own guard against a leading gap. */
.cg-rte-content .tiptap > :first-child {
    margin-top: 0;
}

/* The embedded-component card. This is a node view, so none of it is what gets saved - the stored
   markup is a single empty div carrying data-embed-id. The card exists purely so an author can see
   what they dropped in and where it sits relative to their paragraphs. */
.cg-rte-content .tiptap .cg-rte-embed {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    padding: 0.75rem 1rem;
    margin: 1rem 0;
    border: 1px dashed rgba(185, 115, 247, 0.6);
    border-radius: 0.5rem;
    background: rgba(96, 0, 255, 0.12);
    cursor: pointer;

    /* An atom node holds no editable text, so a caret here would have nowhere to land. */
    user-select: none;
}

.cg-rte-content .tiptap .cg-rte-embed.ProseMirror-selectednode {
    outline: 2px solid #b973f7;
    outline-offset: 2px;
}

.cg-rte-embed__icon {
    color: #b973f7;
    font-size: 1.1rem;
}

.cg-rte-embed__label {
    font-weight: 600;
}

/* Pushed onto its own line at narrow widths by the flex-wrap above rather than truncated - it is
   the only thing telling the author how to reach Edit and Remove. */
.cg-rte-embed__hint {
    color: rgba(255, 255, 255, 0.55);
    font-size: 0.8rem;
}

/* ===== fonts.css ===== */

/* Self-hosted Poppins (body) and Sora (headings) - see the app.css section above. Previously
   pulled from fonts.googleapis.com/fonts.gstatic.com; self-hosting drops two render-blocking
   external origins and lets these files ride the same @Assets[...] fingerprinted long-cache
   treatment as the rest of the site's CSS/JS (see App.razor). Subsetted to the "latin" range only
   (covers ASCII, Latin-1 punctuation/accents, and currency symbols incl. £/€) - this is an
   English-only site, so the extended-Latin/devanagari/etc. subsets Google also serves are
   unnecessary. Weights match what's actually declared across the component stylesheets (grepped
   for `font-weight` next to `font-family: 'Poppins'|'Sora'`), plus Bootstrap's default heading
   weight (500) as a fallback for any heading that doesn't set its own. */

@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('/fonts/poppins/poppins-400.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url('/fonts/poppins/poppins-500.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url('/fonts/poppins/poppins-600.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('/fonts/poppins/poppins-700.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 800;
    font-display: swap;
    src: url('/fonts/poppins/poppins-800.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Sora ships from Google as a single variable-weight file regardless of which static weight is
   requested (400/500/600/700/800 all resolved to the same URL) - one file, browser interpolates
   any weight in range via the font-weight: <min> <max> descriptor below, rather than 4 near-
   duplicate static files. */
@font-face {
    font-family: 'Sora';
    font-style: normal;
    font-weight: 500 800;
    font-display: swap;
    src: url('/fonts/sora/sora-variable.woff2') format('woff2-variations');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* <bootstrap-icons> */
/* Generated by Assets/icons (npm run build) - edit the icon list there, not here. 180 of the
   2050 Bootstrap Icons v1.11.3 glyphs: every bi-* name in the C#/Razor source plus
   extra-icons.txt. font-display:block rather than swap, because a fallback font's rendering of a
   private-use codepoint is a box. */
@font-face{font-display:block;font-family:bootstrap-icons;src:url("/fonts/bootstrap-icons/bootstrap-icons.d6247b8b.woff2") format("woff2")}
.bi::before,[class*=" bi-"]::before,[class^=bi-]::before{display:inline-block;font-family:bootstrap-icons!important;font-style:normal;font-weight:400!important;font-variant:normal;text-transform:none;line-height:1;vertical-align:-.125em;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}
.bi-airplane::before{content:"\f7cd"}
.bi-arrow-clockwise::before{content:"\f116"}
.bi-arrow-counterclockwise::before{content:"\f117"}
.bi-arrow-down::before{content:"\f128"}
.bi-arrow-down-up::before{content:"\f127"}
.bi-arrow-left::before{content:"\f12f"}
.bi-arrow-repeat::before{content:"\f130"}
.bi-arrow-right::before{content:"\f138"}
.bi-arrow-right-circle::before{content:"\f134"}
.bi-arrow-up::before{content:"\f148"}
.bi-award::before{content:"\f154"}
.bi-award-fill::before{content:"\f153"}
.bi-bank2::before{content:"\f62f"}
.bi-bar-chart-fill::before{content:"\f17a"}
.bi-bar-chart-line::before{content:"\f17c"}
.bi-bell::before{content:"\f18a"}
.bi-book-half::before{content:"\f193"}
.bi-box-arrow-right::before{content:"\f1c3"}
.bi-box-arrow-up-right::before{content:"\f1c5"}
.bi-broadcast::before{content:"\f1d6"}
.bi-building::before{content:"\f1dd"}
.bi-buildings::before{content:"\f87d"}
.bi-bullseye::before{content:"\f1de"}
.bi-calendar-check::before{content:"\f1e2"}
.bi-caret-down-fill::before{content:"\f229"}
.bi-caret-up-fill::before{content:"\f235"}
.bi-cash-coin::before{content:"\f632"}
.bi-chat-quote::before{content:"\f255"}
.bi-chat-quote-fill::before{content:"\f254"}
.bi-check::before{content:"\f26e"}
.bi-check-circle::before{content:"\f26b"}
.bi-check-circle-fill::before{content:"\f26a"}
.bi-check-lg::before{content:"\f633"}
.bi-check2-square::before{content:"\f271"}
.bi-chevron-down::before{content:"\f282"}
.bi-chevron-left::before{content:"\f284"}
.bi-chevron-right::before{content:"\f285"}
.bi-chevron-up::before{content:"\f286"}
.bi-circle::before{content:"\f28a"}
.bi-clipboard-data::before{content:"\f28c"}
.bi-clock-history::before{content:"\f292"}
.bi-code::before{content:"\f2c8"}
.bi-code-square::before{content:"\f2c7"}
.bi-coin::before{content:"\f634"}
.bi-collection::before{content:"\f2cc"}
.bi-collection-play-fill::before{content:"\f2ca"}
.bi-columns-gap::before{content:"\f2cd"}
.bi-compass::before{content:"\f2d1"}
.bi-cone-striped::before{content:"\f2d2"}
.bi-controller::before{content:"\f2d4"}
.bi-copy::before{content:"\f759"}
.bi-credit-card::before{content:"\f2dc"}
.bi-currency-dollar::before{content:"\f636"}
.bi-currency-pound::before{content:"\f639"}
.bi-cursor-fill::before{content:"\f2e1"}
.bi-dash-circle::before{content:"\f2e6"}
.bi-dash-lg::before{content:"\f63b"}
.bi-diagram-3::before{content:"\f2ee"}
.bi-diagram-3-fill::before{content:"\f2ed"}
.bi-dice-5::before{content:"\f2fb"}
.bi-dice-6::before{content:"\f2fd"}
.bi-emoji-smile::before{content:"\f327"}
.bi-envelope::before{content:"\f32f"}
.bi-envelope-check::before{content:"\f68c"}
.bi-envelope-fill::before{content:"\f32c"}
.bi-eraser::before{content:"\f331"}
.bi-exclamation-circle::before{content:"\f333"}
.bi-exclamation-square-fill::before{content:"\f338"}
.bi-exclamation-triangle::before{content:"\f33b"}
.bi-exclamation-triangle-fill::before{content:"\f33a"}
.bi-eye::before{content:"\f341"}
.bi-eye-fill::before{content:"\f33e"}
.bi-eye-slash::before{content:"\f340"}
.bi-file-earmark-text::before{content:"\f38b"}
.bi-file-richtext::before{content:"\f3b1"}
.bi-fire::before{content:"\f7f6"}
.bi-geo-alt::before{content:"\f3e8"}
.bi-geo-alt-fill::before{content:"\f3e7"}
.bi-gift::before{content:"\f3ec"}
.bi-gift-fill::before{content:"\f3eb"}
.bi-globe2::before{content:"\f3ef"}
.bi-graph-up-arrow::before{content:"\f673"}
.bi-grid-3x3-gap::before{content:"\f3f9"}
.bi-grid-3x3-gap-fill::before{content:"\f3f8"}
.bi-grip-vertical::before{content:"\f3fe"}
.bi-hand-index-thumb::before{content:"\f402"}
.bi-hand-thumbs-down-fill::before{content:"\f404"}
.bi-hand-thumbs-up::before{content:"\f407"}
.bi-hand-thumbs-up-fill::before{content:"\f406"}
.bi-headset::before{content:"\f414"}
.bi-heart-fill::before{content:"\f415"}
.bi-hourglass-split::before{content:"\f41f"}
.bi-house-door::before{content:"\f423"}
.bi-hr::before{content:"\f426"}
.bi-image::before{content:"\f42a"}
.bi-images::before{content:"\f42b"}
.bi-info-circle::before{content:"\f431"}
.bi-info-circle-fill::before{content:"\f430"}
.bi-journal-text::before{content:"\f444"}
.bi-joystick::before{content:"\f448"}
.bi-justify::before{content:"\f44b"}
.bi-key::before{content:"\f44f"}
.bi-layout-split::before{content:"\f460"}
.bi-layout-text-window::before{content:"\f464"}
.bi-layout-text-window-reverse::before{content:"\f463"}
.bi-lightbulb::before{content:"\f46b"}
.bi-lightbulb-fill::before{content:"\f468"}
.bi-lightning-charge::before{content:"\f46d"}
.bi-lightning-charge-fill::before{content:"\f46c"}
.bi-link-45deg::before{content:"\f470"}
.bi-list::before{content:"\f479"}
.bi-list-check::before{content:"\f473"}
.bi-list-nested::before{content:"\f474"}
.bi-list-ol::before{content:"\f475"}
.bi-list-ul::before{content:"\f478"}
.bi-lock::before{content:"\f47b"}
.bi-magic::before{content:"\f675"}
.bi-megaphone::before{content:"\f484"}
.bi-newspaper::before{content:"\f4a3"}
.bi-patch-check::before{content:"\f4b6"}
.bi-patch-check-fill::before{content:"\f4b5"}
.bi-patch-question::before{content:"\f4be"}
.bi-pencil::before{content:"\f4cb"}
.bi-pencil-square::before{content:"\f4ca"}
.bi-people::before{content:"\f4d0"}
.bi-percent::before{content:"\f4d1"}
.bi-person::before{content:"\f4e1"}
.bi-person-badge::before{content:"\f4d3"}
.bi-person-badge-fill::before{content:"\f4d2"}
.bi-person-check::before{content:"\f4d6"}
.bi-person-circle::before{content:"\f4d7"}
.bi-person-fill::before{content:"\f4da"}
.bi-phone::before{content:"\f4e7"}
.bi-piggy-bank::before{content:"\f64a"}
.bi-play-btn-fill::before{content:"\f4f0"}
.bi-play-fill::before{content:"\f4f4"}
.bi-plus-circle::before{content:"\f4fa"}
.bi-plus-lg::before{content:"\f64d"}
.bi-plus-square::before{content:"\f4fd"}
.bi-puzzle::before{content:"\f503"}
.bi-question-circle::before{content:"\f505"}
.bi-question-circle-fill::before{content:"\f504"}
.bi-quote::before{content:"\f6b0"}
.bi-rocket-takeoff::before{content:"\f845"}
.bi-search::before{content:"\f52a"}
.bi-send::before{content:"\f6c0"}
.bi-shield-check::before{content:"\f52f"}
.bi-shield-exclamation::before{content:"\f530"}
.bi-shield-fill-check::before{content:"\f531"}
.bi-shield-lock::before{content:"\f538"}
.bi-signpost-split::before{content:"\f549"}
.bi-speedometer2::before{content:"\f580"}
.bi-star::before{content:"\f588"}
.bi-star-fill::before{content:"\f586"}
.bi-star-half::before{content:"\f587"}
.bi-stars::before{content:"\f589"}
.bi-suit-heart-fill::before{content:"\f59d"}
.bi-suit-spade::before{content:"\f5a0"}
.bi-suit-spade-fill::before{content:"\f59f"}
.bi-table::before{content:"\f5aa"}
.bi-tag-fill::before{content:"\f5af"}
.bi-tags::before{content:"\f5b2"}
.bi-text-center::before{content:"\f5c4"}
.bi-text-left::before{content:"\f5c7"}
.bi-text-paragraph::before{content:"\f5c8"}
.bi-text-right::before{content:"\f5c9"}
.bi-three-dots::before{content:"\f5d4"}
.bi-ticket-perforated::before{content:"\f6ca"}
.bi-trash::before{content:"\f5de"}
.bi-trophy::before{content:"\f5e7"}
.bi-type-bold::before{content:"\f5f0"}
.bi-type-h1::before{content:"\f5f1"}
.bi-type-italic::before{content:"\f5f4"}
.bi-type-strikethrough::before{content:"\f5f5"}
.bi-type-underline::before{content:"\f5f6"}
.bi-upload::before{content:"\f603"}
.bi-wallet2::before{content:"\f615"}
.bi-window::before{content:"\f620"}
.bi-window-fullscreen::before{content:"\f6cf"}
.bi-x-lg::before{content:"\f659"}
/* </bootstrap-icons> */
