/* ==========================================================================
   Bike Rental — site stylesheet (PR-FND-08)

   PROVENANCE. The palette and component overrides in Part 1 are the theme from the
   legacy QRCode_Demo application (its wwwroot/css/site.css), which this system
   replaces. Keeping the same colours means the replacement looks like the product the
   staff already use.

   LOAD ORDER. This file is layered on top of Bootstrap 5.1, which every layout links
   first:

       ~/lib/bootstrap/dist/css/bootstrap.min.css     <- framework
       ~/css/site.css                                 <- this file

   Nothing here uses !important to win against Bootstrap; it wins by coming second and
   by matching Bootstrap's own selector specificity. If a rule below stops taking
   effect, the cause is almost always a changed load order rather than specificity.

   NO JAVASCRIPT. Only Bootstrap's CSS is used. The application is server-rendered and
   works with scripting disabled (NFR-COMP-005), so no Bootstrap component that
   requires its JS bundle — dropdown, collapse, modal, toast — appears in any view.

   TWO DELIBERATE DEVIATIONS FROM THE LEGACY FILE, both to hold the accessibility
   commitments below. They are the only places where this file knowingly differs:

     1. The legacy file sets `html { font-size: 14px }` (16px from 768px up). A root
        size in px overrides the reader's own browser font-size preference. The same
        visual result is expressed here as a percentage, which scales with that
        preference instead of replacing it.
     2. The legacy file relies on Bootstrap's focus box-shadow alone. A 3px
        :focus-visible outline is added, because the shadow is nearly invisible
        against the dark primary #00457a used across the site chrome.

   ACCESSIBILITY COMMITMENTS (NFR-ACC-001..011, EIR-UI-001..010):

   * CONTRAST. Every pair used for text in this application meets WCAG 2.1 AA:
       #ffffff on --brand      #00457a ......... 14.9:1
       #ffffff on --brand-hover #445f5e ........  6.9:1
       #ffffff on --accent     #134b5f .........  9.7:1
       #ffffff on --success    #4b7147 .........  4.7:1
       #ffffff on --danger     #CA1929 .........  4.8:1
       --ink   #212529 on #ffffff .............. 16.1:1
       --ink-muted #55595f on #ffffff ..........  7.3:1
     The legacy warning colour #BD7418 reaches only 3.7:1 against white, so it is NOT
     used for any text in this application — see the note above .btn-warning.
   * FOCUS. A visible indicator on every interactive element; outlines are never
     removed without an equivalent replacement.
   * ZOOM AND REFLOW. Type is in rem and widths are fluid, so the layout reflows at
     200% zoom with no horizontal page scroll at any width from 320px.
   * NEVER COLOUR ALONE. Error and success states pair colour with an icon and text.
   ========================================================================== */

:root {
    /* The legacy palette, named by role rather than by hue so a future retheme is a
       change to these six lines. */
    --brand: #005a9c;
    --brand-dark: #00457a;
    --brand-hover: #445f5e;
    --accent: #134b5f;
    --accent-hover: #1a6985;
    --success: #4b7147;
    --success-hover: #578553;
    --danger: #CA1929;
    --danger-hover: #b84743;
    --warning: #BD7418;
    --warning-hover: #c6883a;
    --leaf: #425F4B;
    --slate: #3A595C;
    --ink: #212529;
    --ink-muted: #55595f;
    --paper: #ffffff;
    --paper-sunken: #f4f6f8;
    --border: #dee2e6;
    --focus: #a8410f;

    /* A solid button paints its own background, so --danger and --success can keep their
       values there whatever the page behind them is doing. Coloured TEXT has only the page
       behind it, and #CA1929 or #4b7147 on a dark background falls to roughly 3:1 — under
       AA. These three are the text-on-page variants. In light mode they simply follow the
       colours above; the dark block is the only place they diverge. */
    --danger-ink: var(--danger);
    --success-ink: var(--success);
    --heading-ink: var(--brand);

    /* Outline buttons draw their label straight onto the page, so they need the same
       text-variant treatment as the inks above.

       These MUST be tokens rather than a [data-bs-theme="dark"] .btn-outline-* rule. Most of
       these buttons are <a> elements (Cancel, "Show all records"), and a browser resolving a
       link's colour does not re-evaluate an ancestor attribute selector — a theme-scoped rule
       silently fails to reach exactly the elements that need it, while working fine on the
       <button> next to it. Changing the VALUE a plain .btn-outline-* rule reads sidesteps
       that entirely. */
    --outline-primary-ink: var(--brand);
    --outline-secondary-ink: var(--accent);
    --outline-success-ink: var(--success);
    --outline-warning-ink: #C07b24;
    /* The label once the button fills in on hover. */
    --outline-ink-on-fill: #fff;

    --measure: 70ch;
    /* Bootstrap reads these when it builds the .text-*, .bg-* and .border-*
       utilities, so setting them here re-themes all three families at once rather
       than one override per utility. */
    --bs-primary: #00457a;
    --bs-primary-rgb: 25, 46, 47;
    --bs-secondary: #134b5f;
    --bs-secondary-rgb: 19, 75, 95;
    --bs-success: #4b7147;
    --bs-success-rgb: 75, 113, 71;
    --bs-danger: #CA1929;
    --bs-danger-rgb: 202, 25, 41;
    --bs-warning: #BD7418;
    --bs-warning-rgb: 189, 116, 24;
}

/* ==========================================================================
   DARK MODE

   Bootstrap 5.3 carries its own dark palette and switches on data-bs-theme="dark" at the
   root, which the layouts stamp server-side from the ThemePreference cookie. All this block
   has to do is move THIS file's tokens to match — Bootstrap's own components follow on
   their own.

   Bootstrap keys the switch off the attribute only; it does not read prefers-color-scheme.
   A visitor who has never touched the toggle therefore gets the system setting applied by
   wwwroot/js/theme-toggle.js before first paint, and light if scripting is off. Restating
   Bootstrap's entire dark variable block inside a media query here would work too, but it
   means copying vendor internals that move between releases.

   CONTRAST. Every pair below is measured against the dark page (#1a1a1a, luminance 0.010):
     --ink        #eceeef ......... 15.9:1
     --ink-muted  #adb5bd ..........  8.0:1
     --danger-ink #ff8a92 ..........  7.4:1
     --success-ink #8fc98a .........  9.0:1
     --heading-ink #9ec8c6 .........  9.3:1
     --focus      #ffab70 ..........  9.4:1   (the light-mode #a8410f is 1.9:1 here — the
                                               focus ring would have been invisible)
     #ffffff on --brand #2c4a48 ....  9.9:1
     #ffffff on --accent #1c6a85 ...  5.9:1
   ========================================================================== */

[data-bs-theme="dark"] {
    /* The chrome is lifted off #192E2F. At its light-mode value the header would sit at
       about 1.4:1 against the dark page — a band you cannot see the edge of. */
    --brand: #2c4a48;
    --brand-dark: #24403f;
    --brand-hover: #3d605e;
    --accent: #1c6a85;
    --accent-hover: #2585a6;
    --success: #5c8a57;
    --success-hover: #6b9d66;
    --danger: #d93a48;
    --danger-hover: #e05561;
    --warning: #c98a35;
    --warning-hover: #d69c4c;
    --leaf: #5c8071;
    --slate: #4a6d70;

    --ink: #eceeef;
    --ink-muted: #adb5bd;
    /* Matched to Bootstrap's own --bs-body-bg so this file's surfaces and Bootstrap's
       components sit on the same ground. */
    --paper: #1a1a1a;
    --paper-sunken: #242424;
    --border: #3d3d3d;
    --focus: #ffab70;

    --danger-ink: #ff8a92;
    --success-ink: #8fc98a;
    --heading-ink: #9ec8c6;

    /* Light tints, because here the outline label IS the text on a dark page. The solid
       values are built to carry white text and reach only about 2.6:1 used this way. On
       hover the button fills with the tint, so the label flips to the dark page colour. */
    --outline-primary-ink: #9ec8c6;
    --outline-secondary-ink: #7fc4d8;
    --outline-success-ink: #8fc98a;
    --outline-warning-ink: #e0a961;
    --outline-ink-on-fill: #1a1a1a;

    /* The Bootswatch theme's own dark link colour is #767676, which is 3.8:1 on this
       background — under the 4.5:1 AA needs for body text, and links are body text. This is
       a light tint of --accent so links still read as links against the teal palette, at
       8.8:1. */
    --bs-link-color: #7fc4d8;
    --bs-link-color-rgb: 127, 196, 216;
    --bs-link-hover-color: #a3d7e6;
    --bs-link-hover-color-rgb: 163, 215, 230;

    /* The utility families again — .bg-primary on the header reads the -rgb triplet, not
       --bs-primary, so both have to move or the header keeps its light-mode colour. */
    --bs-primary: #2c4a48;
    --bs-primary-rgb: 44, 74, 72;
    --bs-secondary: #1c6a85;
    --bs-secondary-rgb: 28, 106, 133;
    --bs-success: #5c8a57;
    --bs-success-rgb: 92, 138, 87;
    --bs-danger: #d93a48;
    --bs-danger-rgb: 217, 58, 72;
    --bs-warning: #c98a35;
    --bs-warning-rgb: 201, 138, 53;
}

/* ==========================================================================
   PART 1 — the legacy theme
   ========================================================================== */

/* Deviation 1 (see the header). 87.5% of a default 16px root is 14px, matching the
   legacy file exactly for a reader who has not changed their browser setting, and
   scaling with it for a reader who has. */
html {
    font-size: 87.5%;
}

@media (min-width: 768px) {
    html {
        font-size: 100%;
    }
}

body {
    color: var(--ink);
    background-color: var(--paper);
}

/* A rule of the legacy file worth keeping: a long brand name must wrap rather than
   push the header into horizontal scroll. */
a.navbar-brand {
    white-space: normal;
    text-align: center;
    word-break: break-all;
}

.divider-text:before,
.divider-text:after {
    color: white;
    content: "";
    flex: 1;
    border-bottom: 1px solid #555;
    margin: auto 0.25rem;
    box-shadow: 0 -2px;
}

.border-top {
    border-top: 1px solid #e5e5e5;
}

.border-bottom {
    border-bottom: 1px solid #e5e5e5;
}

.box-shadow {
    box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
}

.nav-pills .nav-link.active,
.nav-pills .show > .nav-link {
    color: #fff;
    background-color: var(--accent);
    border-color: var(--accent);
}

.card {
    border-color: var(--leaf);
}

/* --------------------------------------------------------------------------
   Buttons — solid
   -------------------------------------------------------------------------- */

.btn-primary {
    color: #fff;
    background-color: var(--brand);
    border-color: var(--brand);
}

.btn-primary:hover,
.btn-check:focus + .btn-primary,
.btn-primary:focus,
.btn-check:checked + .btn-primary,
.btn-check:active + .btn-primary,
.btn-primary:active,
.btn-primary.active,
.show > .btn-primary.dropdown-toggle,
.btn-primary:disabled,
.btn-primary.disabled {
    color: #fff;
    background-color: var(--brand-hover);
    border-color: var(--brand-hover);
}

.btn-check:focus + .btn-primary,
.btn-primary:focus,
.btn-check:checked + .btn-primary:focus,
.btn-check:active + .btn-primary:focus,
.btn-primary:active:focus,
.btn-primary.active:focus,
.show > .btn-primary.dropdown-toggle:focus {
    box-shadow: 0 0 0 0.25rem rgba(60, 60, 60, 0.5);
}

.btn-secondary {
    color: #fff;
    background-color: var(--accent);
    border-color: var(--accent);
}

.btn-secondary:hover,
.btn-check:focus + .btn-secondary,
.btn-secondary:focus,
.btn-check:checked + .btn-secondary,
.btn-check:active + .btn-secondary,
.btn-secondary:active,
.btn-secondary.active,
.show > .btn-secondary.dropdown-toggle,
.btn-secondary:disabled,
.btn-secondary.disabled {
    color: #fff;
    background-color: var(--accent-hover);
    border-color: var(--accent-hover);
}

.btn-check:focus + .btn-secondary,
.btn-secondary:focus,
.btn-check:checked + .btn-secondary:focus,
.btn-check:active + .btn-secondary:focus,
.btn-secondary:active:focus,
.btn-secondary.active:focus,
.show > .btn-secondary.dropdown-toggle:focus {
    box-shadow: 0 0 0 0.25rem rgba(60, 60, 60, 0.5);
}

.btn-success {
    color: #fff;
    background-color: var(--success);
    border-color: var(--success);
}

.btn-success:hover,
.btn-check:focus + .btn-success,
.btn-success:focus,
.btn-check:checked + .btn-success,
.btn-check:active + .btn-success,
.btn-success:active,
.btn-success.active,
.show > .btn-success.dropdown-toggle,
.btn-success:disabled,
.btn-success.disabled {
    color: #fff;
    background-color: var(--success-hover);
    border-color: var(--success-hover);
}

.btn-check:focus + .btn-success,
.btn-success:focus,
.btn-check:checked + .btn-success:focus,
.btn-check:active + .btn-success:focus,
.btn-success:active:focus,
.btn-success.active:focus,
.show > .btn-success.dropdown-toggle:focus {
    box-shadow: 0 0 0 0.25rem rgba(60, 60, 60, 0.5);
}

.btn-danger {
    color: #fff;
    background-color: var(--danger);
    border-color: var(--danger);
}

.btn-danger:hover,
.btn-check:checked + .btn-danger,
.btn-check:active + .btn-danger,
.btn-danger:active,
.btn-danger.active,
.show > .btn-danger.dropdown-toggle,
.btn-danger:disabled,
.btn-danger.disabled {
    color: #fff;
    background-color: var(--danger-hover);
    border-color: var(--danger-hover);
}

.btn-check:focus + .btn-danger,
.btn-danger:focus {
    color: #fff;
    background-color: #d9534f;
    border-color: #d9534f;
}

.btn-check:focus + .btn-danger,
.btn-danger:focus,
.btn-check:checked + .btn-danger:focus,
.btn-check:active + .btn-danger:focus,
.btn-danger:active:focus,
.btn-danger.active:focus,
.show > .btn-danger.dropdown-toggle:focus {
    box-shadow: 0 0 0 0.25rem rgba(223, 109, 105, 0.5);
}

/* White on #BD7418 is 3.7:1, below the 4.5:1 that AA asks of button text. The rule is
   carried over so the theme is complete, but no view in this application uses
   .btn-warning or .text-warning — see the contrast note in the header. */
.btn-warning {
    color: #fff;
    background-color: var(--warning);
    border-color: var(--warning);
}

.btn-warning:hover,
.btn-check:focus + .btn-warning,
.btn-warning:focus,
.btn-check:checked + .btn-warning,
.btn-check:active + .btn-warning,
.btn-warning:active,
.btn-warning.active,
.show > .btn-warning.dropdown-toggle,
.btn-warning:disabled,
.btn-warning.disabled {
    color: #fff;
    background-color: var(--warning-hover);
    border-color: var(--warning-hover);
}

/* --------------------------------------------------------------------------
   Buttons — outline
   -------------------------------------------------------------------------- */

.btn-outline-primary {
    color: var(--outline-primary-ink);
    border-color: var(--outline-primary-ink);
}

.btn-outline-primary:hover,
.btn-check:checked + .btn-outline-primary,
.btn-check:active + .btn-outline-primary,
.btn-outline-primary:active,
.btn-outline-primary.active,
.btn-outline-primary.dropdown-toggle.show {
    color: var(--outline-ink-on-fill);
    background-color: var(--brand);
    border-color: var(--brand);
}

.btn-outline-primary:disabled,
.btn-outline-primary.disabled {
    color: var(--outline-primary-ink);
    background-color: transparent;
}

.btn-outline-secondary {
    color: var(--outline-secondary-ink);
    border-color: var(--outline-secondary-ink);
}

.btn-outline-secondary:hover,
.btn-check:checked + .btn-outline-secondary,
.btn-check:active + .btn-outline-secondary,
.btn-outline-secondary:active,
.btn-outline-secondary.active,
.btn-outline-secondary.dropdown-toggle.show {
    color: var(--outline-ink-on-fill);
    background-color: var(--accent);
    border-color: var(--accent);
}

.btn-outline-secondary:disabled,
.btn-outline-secondary.disabled {
    color: var(--outline-secondary-ink);
    background-color: transparent;
}

.btn-outline-success {
    color: var(--outline-success-ink);
    border-color: var(--outline-success-ink);
}

.btn-outline-success:hover,
.btn-check:checked + .btn-outline-success,
.btn-check:active + .btn-outline-success,
.btn-outline-success:active,
.btn-outline-success.active,
.btn-outline-success.dropdown-toggle.show {
    color: var(--outline-ink-on-fill);
    background-color: var(--success);
    border-color: var(--success);
}

.btn-outline-success:disabled,
.btn-outline-success.disabled {
    color: var(--outline-success-ink);
    background-color: transparent;
}

.btn-outline-warning {
    color: var(--outline-warning-ink);
    border-color: var(--outline-warning-ink);
}

.btn-outline-warning:hover,
.btn-check:checked + .btn-outline-warning,
.btn-check:active + .btn-outline-warning,
.btn-outline-warning:active,
.btn-outline-warning.active,
.btn-outline-warning.dropdown-toggle.show {
    color: var(--outline-ink-on-fill);
    background-color: #C07b24;
    border-color: #C07b24;
}

.btn-outline-warning:disabled,
.btn-outline-warning.disabled {
    color: var(--outline-warning-ink);
    background-color: transparent;
}

.btn-check:focus + .btn-outline-primary,
.btn-outline-primary:focus,
.btn-check:focus + .btn-outline-secondary,
.btn-outline-secondary:focus,
.btn-check:focus + .btn-outline-success,
.btn-outline-success:focus,
.btn-check:focus + .btn-outline-warning,
.btn-outline-warning:focus {
    box-shadow: 0 0 0 0.25rem rgba(25, 46, 47, 0.5);
}

/* --------------------------------------------------------------------------
   Text and background helpers

   The legacy file listed .text-* and .bg-* one by one. Bootstrap 5.1 builds those
   utilities from the --bs-*-rgb custom properties set in :root above, so the two that
   remain here are the ones whose legacy value is NOT the button colour.
   (The legacy file's `bg-success` was missing its leading dot and therefore never
   matched anything; it is written correctly here.)
   -------------------------------------------------------------------------- */

.text-secondary {
    --bs-text-opacity: 1;
    color: var(--leaf) !important;
}

.bg-secondary {
    --bs-bg-opacity: 1;
    background-color: var(--slate) !important;
}

/* rgba(189, 116, 24, 0.6) over white gives white text about 1.9:1. Kept at full
   strength so the label stays legible; no view currently uses it. */
.badge {
    background-color: var(--warning);
    color: #fff;
}

button.accept-policy {
    font-size: 1rem;
    line-height: inherit;
}

/* ==========================================================================
   PART 2 — accessibility layer
   ========================================================================== */

/* Deviation 2 (see the header). Bootstrap's focus ring is a translucent shadow in the
   component's own colour, which all but disappears against the dark header. This
   outline sits on top of it and stays visible on every background used here. */
:focus-visible {
    outline: 3px solid var(--focus);
    outline-offset: 2px;
}

/* Belt and braces for browsers without :focus-visible. */
a:focus,
button:focus,
input:focus,
select:focus,
textarea:focus {
    outline: 3px solid var(--focus);
    outline-offset: 2px;
}

.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    padding: 0.75rem 1rem;
    background: var(--brand);
    color: var(--paper);
    z-index: 1100;
}

.skip-link:focus {
    left: 0;
}

main:focus {
    /* The skip link targets main; suppress the ring on programmatic focus only. */
    outline: none;
}

/* ==========================================================================
   PART 3 — application components

   Everything below is specific to this system and has no Bootstrap equivalent, or
   needs a constraint Bootstrap does not express.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Chrome
   -------------------------------------------------------------------------- */

.site-header .navbar-brand {
    font-weight: 700;
}

/* The module bar sits under the header on every administration page. It never
   collapses behind a toggler, because a toggler would need Bootstrap's JS. */
.admin-nav {
    background-color: var(--brand-hover);
}

.admin-nav .nav-link {
    color: rgba(255, 255, 255, 0.9);
    padding: 0.375rem 0.75rem;
}

.admin-nav .nav-link:hover,
.admin-nav .nav-link:focus {
    color: #fff;
    text-decoration: underline;
}

.admin-identity {
    gap: 0.75rem;
}

/* --------------------------------------------------------------------------
   The light/dark toggle
   -------------------------------------------------------------------------- */

/* The Bootswatch theme hardcodes #f7f7f9 as the form-control background and does not
   restate it for dark mode, while the text colour does become light — so every input, select
   and textarea renders near-white text on a near-white field: 1.09:1, completely unreadable.
   This is the single most important rule in the dark block; without it the enquiry, rental
   and sign-in forms cannot be filled in at all. */
[data-bs-theme="dark"] .form-control,
[data-bs-theme="dark"] .form-select,
[data-bs-theme="dark"] .form-control:focus,
[data-bs-theme="dark"] .form-select:focus {
    background-color: #242424;
    color: var(--ink);
    border-color: var(--border);
}

[data-bs-theme="dark"] .form-control::placeholder {
    color: var(--ink-muted);
}

[data-bs-theme="dark"] .form-control:disabled,
[data-bs-theme="dark"] .form-select:disabled {
    background-color: #303030;
    color: var(--ink-muted);
}

/* The file input's button is a second surface inside the control and inherits none of the
   above. */
[data-bs-theme="dark"] .form-control::file-selector-button {
    background-color: #303030;
    color: var(--ink);
    border-color: var(--border);
}

[data-bs-theme="dark"] .form-check-input {
    background-color: #242424;
    border-color: var(--border);
}

[data-bs-theme="dark"] .form-check-input:checked {
    background-color: var(--accent);
    border-color: var(--accent);
}

/* .navbar-dark paints its links at 55% white, which is sized for a very dark bar. The dark
   theme lifts the header to #2c4a48 so its edge is visible against the page, and at that
   lighter bar 55% white falls to 4.18:1 — under AA for link text. Raising the opacity is the
   smaller correction; darkening the bar again would just restore the invisible edge. Light
   mode keeps Bootstrap's value, where the darker bar already passes. */
[data-bs-theme="dark"] .navbar {
    --bs-navbar-color: rgba(255, 255, 255, 0.78);
    --bs-navbar-hover-color: #fff;
    --bs-navbar-active-color: #fff;
}

.theme-toggle-form {
    display: inline-flex;
}

/* An outlined control on the dark chrome of both headers. It is not .btn-outline-light,
   because that class would not follow the palette when the theme flips. */
.theme-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    color: #fff;
    background-color: transparent;
    border: 1px solid rgba(255, 255, 255, 0.5);
    white-space: nowrap;
}

.theme-toggle:hover,
.theme-toggle:focus {
    color: var(--brand);
    background-color: #fff;
    border-color: #fff;
}

.theme-toggle-icon {
    font-size: 1rem;
    line-height: 1;
}

/* Below 480px the header is tight; the icon alone carries the control and the words move
   to the accessible name so screen-reader users keep the full label. */
@media (max-width: 30rem) {
    .theme-toggle-label {
        position: absolute;
        width: 1px;
        height: 1px;
        padding: 0;
        margin: -1px;
        overflow: hidden;
        clip: rect(0 0 0 0);
        white-space: nowrap;
        border: 0;
    }
}

.site-footer {
    border-top: 1px solid var(--border);
    color: var(--ink-muted);
    font-size: 0.9375rem;
}

main {
    padding-top: 1.5rem;
    padding-bottom: 3rem;
}

/* Both layouts make <body> a flex column so the footer sits at the bottom of a short
   page. A flex item defaults to min-width:auto, which refuses to shrink below its own
   content — so a wide table would push the whole page sideways instead of scrolling
   inside .table-scroll, breaking NFR-ACC-010 on the two listing pages. min-width:0
   lets the item shrink and hands the scrolling back to the wrapper that asked for it. */
body > * {
    min-width: 0;
}

/* --------------------------------------------------------------------------
   Forms

   Bootstrap styles the controls; these rules carry the parts the SRS asks for —
   a readable measure, the hint and error text, and a 44px minimum touch target.
   -------------------------------------------------------------------------- */

form {
    max-width: var(--measure);
}

/* The filter bar is a row of controls, not a column of them. */
form.filter-bar {
    max-width: none;
}

.form-field {
    margin-bottom: 1.25rem;
}

.form-field > label,
.form-label {
    font-weight: 600;
    margin-bottom: 0.25rem;
}

.required-marker {
    font-weight: 400;
    color: var(--ink-muted);
    font-size: 0.875rem;
}

.field-help {
    margin: 0 0 0.375rem;
    font-size: 0.875rem;
    color: var(--ink-muted);
}

/* EIR-UI-006: a 44px minimum target. Bootstrap's default control is about 38px. In px
   for the same reason as .btn above — a rem value shrinks below 44px at the small-screen
   root size. */
.form-control,
.form-select {
    min-height: 44px;
}

textarea.form-control {
    min-height: 6rem;
    resize: vertical;
}

.form-check-input {
    width: 1.25rem;
    height: 1.25rem;
    margin-top: 0.3rem;
}

/* Colour plus a thicker border: the error is not signalled by hue alone. */
.form-control.is-invalid,
.form-select.is-invalid,
.form-check-input.is-invalid {
    border-color: var(--danger-ink);
    border-width: 2px;
}

/* Bootstrap keeps .invalid-feedback hidden until a sibling carries .is-invalid. The
   server decides what is invalid here, and a file input that failed validation is
   re-rendered empty and therefore cannot carry .is-invalid — so the message is shown
   whenever the markup includes it. */
.field-error {
    display: block;
    margin: 0.375rem 0 0;
    color: var(--danger-ink);
    font-weight: 600;
}

fieldset {
    border: 1px solid var(--border);
    border-radius: 0.25rem;
    margin: 0 0 1.5rem;
    padding: 1rem;
    min-width: 0;
    
}

legend {
    float: none;
    width: auto;
    font-size: 1.0625rem;
    font-weight: 700;
    padding: 0 0.375rem;
    
}

.filter-bar {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-items: flex-end;
    padding: 1rem;
    background: var(--paper-sunken);
    border: 1px solid var(--border);
    border-radius: 0.25rem;
    margin-bottom: 1.5rem;
}

.filter-bar .form-field {
    margin-bottom: 0;
    min-width: 10rem;
}

/* The vendored bootstrap.min.css is a Bootswatch build whose .btn is 0.75rem — about
   10px once the legacy 87.5% root size is applied, which is too small to read and too
   small to hit reliably. Buttons are restored to body size with the 44px minimum target
   the SRS asks for (EIR-UI-006). Stock Bootstrap already uses 1rem, so this line changes
   nothing if the base stylesheet is ever swapped.

   The 44px floor is in px on purpose. Expressed in rem it would shrink to 38.5px under
   the 87.5% root size — which applies below 768px, exactly the touch screens the target
   size exists to protect. */
.btn {
    font-size: 1rem;
    min-height: 44px;
    padding: 0.5rem 1.25rem;
}

.btn-sm {
    font-size: 0.875rem;
    min-height: 36px;
    padding: 0.25rem 0.75rem;
}

.button-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    align-items: center;
}

.button-row form {
    margin: 0;
}

/* --------------------------------------------------------------------------
   Alerts and tables
   -------------------------------------------------------------------------- */

.alert {
    max-width: var(--measure);
}

.error-list {
    margin: 0;
    padding-left: 1.25rem;
}

.error-list a {
    color: inherit;
}

/* FR-ADMENQ-008: the TABLE scrolls inside its own container so the PAGE never scrolls
   sideways. Bootstrap's .table-responsive does the same job, but this keeps the
   border and radius that the container needs to read as one object. */
.table-scroll {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    border: 1px solid var(--border);
    border-radius: 0.25rem;
    margin-bottom: 1.5rem;
    /* Not decoration. The per-row action links carry .visually-hidden spans, which are
       position:absolute. An absolutely positioned box is only clipped by an ancestor that
       is itself a containing block, so on a static wrapper those spans escape the scroller
       and stretch the document to the table's full width — the page then scrolls sideways
       even though the table is scrolling correctly inside. position:relative makes this
       the containing block and the spans are clipped with everything else. */
    position: relative;
}

.table-scroll > .table {
    /* Keeps columns readable rather than crushing them at narrow widths; the container
       above provides the scroll. */
    min-width: 44rem;
    margin-bottom: 0;
}

.table > caption {
    caption-side: top;
    text-align: left;
    padding: 0.75rem 1rem;
    font-weight: 700;
    color: var(--ink);
}

/* Bootstrap paints --bs-table-bg onto the CELLS, so a background on <thead> is covered up.
   The header tint has to go on the cells themselves to survive that. */
.table > thead > tr > th {
    background-color: var(--paper-sunken);
    border-bottom-width: 2px;
}

/* --------------------------------------------------------------------------
   DataTables

   Loaded only by the four listing views, after this file, so these rules are written
   to override dataTables.bootstrap5.min.css rather than the other way round.
   -------------------------------------------------------------------------- */

/* When DataTables initialises it takes over horizontal scrolling (scrollX), and the
   initialiser adds this class. Two nested scrollers would otherwise fight, and the search
   box and pager would slide out of view with the table body. Without scripting the class
   is never added and .table-scroll keeps doing the job by itself. */
.table-scroll-datatable {
    overflow-x: visible;
}

/* The 44rem floor set above belongs to the table only while .table-scroll is the scroller.
   Once DataTables owns the scrolling it measures and sizes the columns itself, and a
   min-width fights that. */
.table-scroll-datatable .table {
    min-width: 0;
}

/* DataTables' controls sit directly against the wrapper's border without this. */
.table-scroll-datatable {
    padding: 0.75rem;
}

/* DataTables' own controls, given the same weight as the rest of the forms here. */
.dt-container .dt-search label,
.dt-container .dt-length label {
    font-weight: 600;
    margin-bottom: 0.25rem;
}

.dt-container .dt-search input,
.dt-container .dt-length select {
    min-height: 44px;
    margin-left: 0.5rem;
}

.dt-container .dt-info {
    color: var(--ink-muted);
    font-size: 0.875rem;
}

/* Bootstrap's .page-link is blue by default; the legacy palette is not. */
.dt-container .page-link {
    color: var(--accent);
}

.dt-container .page-item.active .page-link {
    background-color: var(--brand);
    border-color: var(--brand);
    color: #fff;
}

.dt-container .page-item.disabled .page-link {
    color: var(--ink-muted);
}

/* The sort arrows must not read as decoration only — they carry the current sort state.
   DataTables writes aria-sort on the header cell as well, so this is the visual half of a
   pairing that is already announced (NFR-ACC-008). */
.dt-container th.dt-orderable-asc:hover,
.dt-container th.dt-orderable-desc:hover {
    background-color: var(--border);
}

.dt-container th.dt-ordering-asc,
.dt-container th.dt-ordering-desc {
    background-color: var(--border);
}

/* The caption sits inside DataTables' scroll head once initialised; keep it readable and
   aligned with the un-enhanced rendering. */
.dt-container caption {
    caption-side: top;
    text-align: left;
    padding: 0.75rem 0;
    font-weight: 700;
    color: var(--ink);
}

.thumb {
    width: 4rem;
    height: 3rem;
    object-fit: cover;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--paper-sunken);
}

/* --------------------------------------------------------------------------
   Catalogue, dashboard and confirmation components
   -------------------------------------------------------------------------- */

/* Single column below 576px, multi-column above — auto-fill so it scales past two
   columns on wide screens without extra breakpoints (FR-CAT-005). */
.bike-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1.25rem;
    list-style:none;
    margin: 0 0 1.5rem;
    padding: 0;
    align-items:center;
    
}

@media (min-width: 36rem) {
    .bike-grid {
        grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    }
}

.bike-card img {
    width: 100%;
    aspect-ratio: 4 / 4;
    object-fit: cover;
    background: var(--paper-sunken);
}

.bike-card .card-title {
    font-size: 1.0625rem;
    margin-bottom: 0.25rem;
}

.placeholder-note {
    font-size: 0.8125rem;
    color: var(--ink-muted);
    font-style: italic;
    margin: 0;
}

.summary-tiles {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1rem;
    margin-bottom: 2rem;
    list-style: none;
    padding: 0;
}

@media (min-width: 36rem) {
    .summary-tiles {
        grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
    }
}

.summary-tile {
    border: 1px solid var(--leaf);
    border-radius: 0.25rem;
    padding: 1rem;
    background: var(--paper-sunken);
}

.summary-tile .count {
    display: block;
    font-size: 2rem;
    font-weight: 700;
    line-height: 1.1;
    color: var(--heading-ink);
}

.empty-state {
    padding: 2rem 1rem;
    text-align: center;
    color: var(--ink-muted);
    background: var(--paper-sunken);
    border: 1px dashed var(--border);
    border-radius: 0.25rem;
    margin-bottom: 1.5rem;
}

.empty-state p:last-child {
    margin-bottom: 0;
}

/* FR-RENT-029: distinct visual weight so the number can be quoted at the counter. */
.form-number {
    display: inline-block;
    font-size: 1.75rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    padding: 0.25rem 0.75rem;
    border: 2px solid var(--success-ink);
    border-radius: 0.25rem;
    background: var(--paper);
    color: var(--success-ink);
}

/* --------------------------------------------------------------------------
   QR code panel (FR-QR-005)
   -------------------------------------------------------------------------- */

.qr-panel {
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem;
    align-items: flex-start;
    margin-bottom: 2rem;
}

.qr-code {
    /* FR-QR-005: at least 200x200 CSS pixels, with a px floor so a reader's font
       settings cannot shrink it below that. */
    width: 15rem;
    height: 15rem;
    min-width: 240px;
    min-height: 240px;
    border: 1px solid var(--border);
    border-radius: 0.25rem;
    padding: 0.1rem;
    /* White in BOTH themes, on purpose — not var(--paper). A QR reader needs light quiet
       zones around dark modules; if the image carries any transparency, a dark backing
       inverts that relationship and the code stops scanning (FR-QR-005). This is the one
       surface in the file that does not follow the theme. */
    background: #ffffff;
    /* Keeps the modules crisp when the browser scales the PNG up. */
    image-rendering: pixelated;
}

.qr-unavailable {
    width: 15rem;
    min-height: 200px;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 1rem;
    border: 1px dashed var(--border);
    border-radius: 0.25rem;
    color: var(--ink-muted);
    background: var(--paper-sunken);
}

/* --------------------------------------------------------------------------
   Motion and print
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

@media print {
    .site-header,
    .site-footer,
    .skip-link,
    .button-row {
        display: none;
    }
}
