/* Ensure body fills viewport height so footer sticks at bottom */
html, body {
    min-height: 100vh;
}

body {
    display: flex;
    flex-direction: column;
}

body > main {
    flex: 1;
}

/* Navbar */
.navbar-brand {
    letter-spacing: 0.04em;
    text-transform: none;
}

/* Active nav link: bold; its colour comes from the skin (skins/light, skins/dark) */
.navbar .nav-link.active {
    font-weight: 700;
}

/* Lux's weight utilities use lighter values; make the published results explicitly bold. */
.competition-result-link {
    --bs-btn-font-weight: 700;
}
.competition-protocol-heading {
    font-weight: 700;
}

/* Offset scrollIntoView and anchor jumps by the sticky navbar height so elements
   never land hidden underneath it. Applied to the scroll container (viewport). */
html {
    scroll-padding-top: 70px;
}

/* Richtext content */
.richtext img {
    max-width: 100%;
    height: auto;
}

/* Hero image */
.hero-image {
    width: 100%;
    max-height: 400px;
    object-fit: cover;
}

/* Map placeholder (for Phase 6) */
.map-container {
    min-height: 300px;
}

/* Competition list table: a fixed layout makes the (many) columns share the available
   width and wrap their content, so the table always fits without a horizontal scroll
   regardless of how long the titles / location names are. */
.calendar-list-table {
    table-layout: fixed;
    width: 100%;
}
.calendar-list-table th,
.calendar-list-table td {
    overflow-wrap: anywhere;
    word-break: break-word;
}
.calendar-list-table .btn {
    white-space: normal;
}
/* On phones the 10-column table cannot stay readable even with a fixed layout (columns
   collapse to one character per line). Below the md breakpoint each row becomes a stacked
   "card" with the column name shown as a label, keeping the page free of horizontal scroll. */
@media (max-width: 767.98px) {
    .calendar-list-table,
    .calendar-list-table tbody,
    .calendar-list-table tr {
        display: block;
        width: 100%;
    }
    .calendar-list-table thead {
        display: none;
    }
    .calendar-list-table tr {
        margin-bottom: 1rem;
        padding: 0.5rem 0.75rem;
        border: 1px solid var(--bs-border-color);
        border-radius: 0.375rem;
    }
    .calendar-list-table td {
        display: flex;
        gap: 0.75rem;
        justify-content: space-between;
        align-items: baseline;
        border: 0;
        padding: 0.2rem 0;
        text-align: right;
    }
    .calendar-list-table td::before {
        content: attr(data-label);
        flex: 0 0 8rem;
        font-weight: 600;
        text-align: left;
        color: var(--bs-secondary-color);
    }
}

/* Participant list: many (conditional) columns. Let long names / team / city values wrap
   instead of widening the table past its container and forcing a horizontal scrollbar. Only
   data cells break inside words; headers wrap between words so a heading word is never split. */
.participant-list-table {
    table-layout: fixed;
    width: 100%;
}
.participant-list-table th,
.participant-list-table td {
    overflow-wrap: anywhere;
    word-break: break-word;
}
/* Keep action-button labels on one line and give the actions column enough width so Edit/Delete
   sit side by side instead of collapsing into a vertical stack when other columns wrap. */
.participant-list-table .btn {
    white-space: nowrap;
}
.participant-list-table .reg-actions {
    width: 11rem;
}
.participant-list-table .reg-actions .btn {
    padding-left: 0.4rem;
    padding-right: 0.4rem;
}
/* On phones the participant table (up to ~12 columns) collapses to one character per line.
   Below the md breakpoint each row becomes a stacked "card" with the column name as a label,
   mirroring the calendar list above. The actions cell stays a full-width block so its button
   group and the inline reject form are not forced into the label/value flex row. */
@media (max-width: 767.98px) {
    .participant-list-table,
    .participant-list-table tbody,
    .participant-list-table tr {
        display: block;
        width: 100%;
    }
    .participant-list-table thead {
        display: none;
    }
    .participant-list-table tr {
        margin-bottom: 1rem;
        padding: 0.5rem 0.75rem;
        border: 1px solid var(--bs-border-color);
        border-radius: 0.375rem;
    }
    .participant-list-table td {
        display: flex;
        gap: 0.75rem;
        justify-content: space-between;
        align-items: baseline;
        border: 0;
        padding: 0.2rem 0;
        text-align: right;
    }
    .participant-list-table td::before {
        content: attr(data-label);
        flex: 0 0 7rem;
        font-weight: 600;
        text-align: left;
        color: var(--bs-secondary-color);
    }
    .participant-list-table td.reg-actions {
        display: block;
        width: auto;
        text-align: left;
        margin-top: 0.25rem;
    }
    .participant-list-table td.reg-actions::before {
        content: none;
    }
    /* The category heading used to be the only element here without a border, so in a column of
       bordered cards it read as less important than the rows it introduces. Inverted into a solid
       band it becomes the heaviest thing on the screen; sticking it under the navbar answers the
       other half of the problem, that ten cards down nobody remembers which category they are in.

       Inverted rather than coloured because this palette has no accent to borrow -- --bs-primary
       is #1a1a1a. The two variables below swap with the theme, so one rule gives a white band in
       the dark theme and a near-black one in the light theme, each with legible text. */
    .participant-category {
        position: sticky;
        /* Set from the navbar's measured height (cycling_site.js); the fallback is what it
           measures at every width except the narrowest, where the brand wraps to two lines. */
        top: var(--ubt-sticky-top, 88px);
        z-index: 3;
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 0.5rem;
        margin: 1.5rem calc(-1 * var(--bs-gutter-x, 1.5rem) / 2) 0.85rem;
        padding: 0.55rem 0.9rem;
        background: var(--bs-emphasis-color);
        color: var(--bs-body-bg);
        font-size: 0.85rem;
        font-weight: 700;
        letter-spacing: 0.06em;
        text-transform: uppercase;
        box-shadow: 0 2px 12px rgba(0, 0, 0, 0.35);
    }
    .participant-category .badge {
        background: var(--bs-body-bg) !important;
        color: var(--bs-emphasis-color) !important;
        font-size: 0.78rem;
    }
}

/* Content images stay within their column so large uploads (e.g. the home-page logo) never
   force a horizontal scrollbar. Leaflet sizes its own map tiles, so exclude the map. */
main img {
    max-width: 100%;
    height: auto;
}
.leaflet-container img {
    max-width: none;
    height: auto;
}

/* Rich article bodies (news / knowledge) can hold author-pasted tables, wide media and long
   URLs; keep them inside the page instead of overflowing it on narrow screens. */
.article-body {
    overflow-wrap: anywhere;
    max-width: 760px;
}
.article-body table {
    display: block;
    max-width: 100%;
    overflow-x: auto;
}
.article-body pre {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* User comments are plain text: keep the author's line breaks and runs of spaces (HTML would
   otherwise collapse them), and wrap long unbroken strings so they don't overflow. */
.comment-body {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* Keep each top-level nav item on one line (avoid a "База / знаний" mid-item wrap). */
.navbar .nav-link {
    white-space: nowrap;
}

/* Protocol iframe wrapper (for Phase 9) */
.protocol-iframe-wrapper {
    width: 100%;
    overflow-x: auto;
}

/* Pager jump-to-page field: hide the number spinner arrows -- clicking them would fire the
   change handler and reload the page one step at a time, which looks jarring. */
input.page-jump::-webkit-outer-spin-button,
input.page-jump::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
input.page-jump {
    -moz-appearance: textfield;
    appearance: textfield;
}

/* Map pages: the filter dropdowns sit above a Leaflet map whose zoom (+/-) and layer controls
   have z-index ~1000. Lift the open filter dropdowns above them so the controls don't show
   through the menu - on the calendar map filter panel and the standalone locations map filter. */
#filter-panel .dropdown-menu,
#loc-filter-form .dropdown-menu {
    z-index: 1100;
}

/* Rich-text block alignment and indentation from the editor toolbar. Quill emits these as
   ql-align-* / ql-indent-* classes and styles them only in its own (editor-only) CSS, so mirror
   the same rules here for the public article pages, where Quill is not loaded. Values match
   Quill: 3em of padding per indent level. Text direction is left-to-right for all our locales. */
main .ql-align-center { text-align: center; }
main .ql-align-right { text-align: right; }
main .ql-align-justify { text-align: justify; }
main .ql-indent-1 { padding-left: 3em; }
main .ql-indent-2 { padding-left: 6em; }
main .ql-indent-3 { padding-left: 9em; }
main .ql-indent-4 { padding-left: 12em; }
main .ql-indent-5 { padding-left: 15em; }
main .ql-indent-6 { padding-left: 18em; }
main .ql-indent-7 { padding-left: 21em; }
main .ql-indent-8 { padding-left: 24em; }

/* A floated image (left/right alignment from the image tool) needs breathing room so body text
   never touches it. blot-formatter writes an inline margin too, but keep a fallback here for
   images floated via the API or older content that lacks it. */
main .article-body img[style*="float"],
main .home-body img[style*="float"],
main .competition-description img[style*="float"] {
    margin: 0.25rem 1rem 0.5rem 0;
}
main .article-body img[style*="float: right"],
main .home-body img[style*="float: right"],
main .competition-description img[style*="float: right"] {
    margin: 0.25rem 0 0.5rem 1rem;
}

/* A left/right-floated image lets body text wrap beside it, but a list's number/bullet markers
   are drawn in the list's left padding, which sits at the column edge *under* a left float -- so
   they render on top of the image. Move the markers inside the content flow: they then sit with
   the text beside the float (no overlap) while the list stays in normal flow and reflows to full
   width once the image ends (no empty gap under it, which display:flow-root would leave). */
main .article-body ul, main .article-body ol,
main .home-body ul, main .home-body ol,
main .competition-description ul, main .competition-description ol {
    list-style-position: inside;
}

/* Social login buttons. Each provider keeps its own brand treatment so the "Connect with
   Strava" button matches Strava's API brand guidelines (orange #FC4C02, 48px tall, logo +
   fixed wording), and Google/GitHub carry their own marks. The buttons stack full-width in a
   column so differently-sized labels line up instead of wrapping unevenly. */
.social-btn-list {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.social-btn-list a {
    width: 100%;
}
.social-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    height: 48px;
    padding: 0 1.15rem;
    border-radius: 0.375rem;
    border: 1px solid transparent;
    font-weight: 600;
    line-height: 1;
    text-decoration: none;
    white-space: nowrap;
}
.social-btn:hover {
    text-decoration: none;
}
.social-btn__icon {
    width: 20px;
    height: 20px;
    flex: 0 0 auto;
}
.social-btn--strava {
    background-color: #FC4C02;
    color: #fff;
}
.social-btn--strava:hover {
    background-color: #e34402;
    color: #fff;
}
.social-btn--google {
    background-color: #fff;
    color: #3c4043;
    border-color: #dadce0;
}
.social-btn--google:hover {
    background-color: #f7f8f8;
    color: #3c4043;
}
.social-btn--github {
    background-color: #24292f;
    color: #fff;
}
.social-btn--github:hover {
    background-color: #1b1f24;
    color: #fff;
}

/* Photo gallery (gallery/templates/gallery/). Neutral here, painted from the theme's own colours;
   a skin restyles it with the same class names. */
.gallery-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: 0.75rem 1.5rem;
    margin-bottom: 1rem;
}
.gallery-kicker {
    margin: 0 0 0.25rem;
    padding-left: 0.6rem;
    border-left: 3px solid var(--bs-primary);
    color: var(--bs-secondary-color);
    letter-spacing: 0.06em;
}
.gallery-title { margin: 0; }
.gallery-head-side {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.5rem;
}
.gallery-tagline { margin: 0; color: var(--bs-secondary-color); letter-spacing: 0.08em; }
.gallery-tabs {
    display: flex;
    gap: 0.25rem;
    overflow-x: auto;
    margin-bottom: 1.25rem;
    padding: 0.25rem;
    border: 1px solid var(--bs-border-color);
    border-radius: 999px;
    scrollbar-width: thin;
}
.gallery-tab {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.45rem 1rem;
    border-radius: 999px;
    color: var(--bs-body-color);
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
}
.gallery-tab:hover { background: var(--bs-tertiary-bg); color: var(--bs-emphasis-color); }
.gallery-tab.active { background: var(--bs-emphasis-color); color: var(--bs-body-bg); }
.gallery-tab-more { margin-left: auto; }
.gallery-album-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.4rem 1rem;
    margin-bottom: 1rem;
    color: var(--bs-secondary-color);
}
.gallery-album-title { margin: 0; font-size: 1.25rem; color: var(--bs-emphasis-color); }
.gallery-event-options { max-height: 24rem; overflow-y: auto; }
.gallery-event-options [role="option"] { text-align: start; overflow-wrap: anywhere; }
.gallery-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr));
    gap: 1.25rem;
    margin-bottom: 1.5rem;
}
.gallery-card {
    position: relative;
    display: block;
    aspect-ratio: 3 / 2;
    overflow: hidden;
    border-radius: 0.5rem;
    background: var(--bs-tertiary-bg);
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12);
    color: #fff;
    text-decoration: none;
}
main .gallery-card img {
    width: 100%;
    height: 100%;
    max-width: none;
    object-fit: cover;
    transition: transform 0.3s ease;
}
.gallery-card:hover img, .gallery-card:focus-visible img { transform: scale(1.04); }
.gallery-card-caption {
    position: absolute;
    inset: auto 0 0 0;
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    padding: 0.7rem 3.75rem 0.7rem 1rem;
    background: rgba(17, 17, 17, 0.62);
    color: #fff;
}
.gallery-card-title {
    font-size: 1.15rem;
    line-height: 1.2;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.gallery-card-date { font-size: 0.8rem; opacity: 0.9; }
.gallery-card-arrow { position: absolute; right: 1rem; bottom: 0.8rem; color: #fff; }
.gallery-card-arrow .gallery-icon { width: 2.25rem; height: 2.25rem; }
.gallery-card-cover { position: absolute; top: 0.6rem; left: 0.6rem; background: #fff; color: #111; }
.gallery-card-blank {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    color: var(--bs-secondary-color);
}
.gallery-card-blank .gallery-icon { width: 3rem; height: 3rem; }
.gallery-empty {
    padding: 2.5rem 1rem;
    text-align: center;
    border: 1px dashed var(--bs-border-color);
    border-radius: 0.5rem;
    color: var(--bs-secondary-color);
}
.gallery-photo { margin: 0 0 1rem; text-align: center; }
main .gallery-photo img {
    max-height: 78vh;
    width: auto;
    max-width: 100%;
    border-radius: 0.5rem;
}
.gallery-photo figcaption { margin-top: 0.5rem; color: var(--bs-secondary-color); }
.gallery-photo-bar { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0.75rem; }
.gallery-dropzone {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.4rem;
    padding: 1.5rem 1rem;
    border: 2px dashed var(--bs-border-color);
    border-radius: 0.5rem;
    text-align: center;
    cursor: pointer;
}
.gallery-dropzone .gallery-icon { width: 2.25rem; height: 2.25rem; color: var(--bs-secondary-color); }
.gallery-dropzone-text { font-weight: 600; }
.gallery-dropzone input[type="file"] { max-width: 28rem; margin-top: 0.5rem; }
.gallery-pick-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 0.75rem;
}
.gallery-pick { position: relative; }
.gallery-pick-radio { position: absolute; opacity: 0; pointer-events: none; }
.gallery-pick-photo {
    position: relative;
    display: block;
    aspect-ratio: 3 / 2;
    overflow: hidden;
    border-radius: 0.5rem;
    outline: 3px solid transparent;
    outline-offset: 2px;
    cursor: pointer;
}
main .gallery-pick-photo img { width: 100%; height: 100%; max-width: none; object-fit: cover; }
.gallery-pick-badge {
    position: absolute;
    top: 0.4rem;
    left: 0.4rem;
    display: none;
    align-items: center;
    gap: 0.25rem;
    padding: 0.15rem 0.5rem;
    border-radius: 999px;
    background: var(--bs-emphasis-color);
    color: var(--bs-body-bg);
    font-size: 0.75rem;
    font-weight: 700;
}
.gallery-pick-badge .gallery-icon { width: 0.9rem; height: 0.9rem; }
.gallery-pick-radio:checked + .gallery-pick-photo { outline-color: var(--bs-emphasis-color); }
.gallery-pick-radio:checked + .gallery-pick-photo .gallery-pick-badge { display: inline-flex; }
.gallery-pick-radio:focus-visible + .gallery-pick-photo { outline-color: var(--bs-primary); }
.gallery-pick-delete { margin-top: 0.35rem; }
.btn .gallery-icon { width: 1.1em; height: 1.1em; vertical-align: -0.15em; }
@media (max-width: 575.98px) {
    .gallery-head-side { align-items: flex-start; }
    .gallery-tab-more { margin-left: 0; }
}
