/* ============================================================
   FIXLOSOPHY – Global Styles
   Mobile-first, responsive bike shop theme
   ============================================================ */

/* ---------- Design Tokens ---------- */
:root {
    --green: #3DBB48;
    --green-dark: #2D9A38;
    --green-glow: rgba(61,187,72,0.20);
    --dark: #0A0A0A;
    --dark-2: #111111;
    --dark-3: #1C1C1C;
    --dark-4: #2A2A2A;
    --mid: #888888;
    --white: #ffffff;
    --border-dark: #2A2A2A;
    --font: 'Helvetica Neue', Helvetica, Arial, sans-serif;
    --radius: 12px;
    --radius-sm: 8px;
    --shadow: 0 4px 24px rgba(0,0,0,0.10);
    --shadow-lg: 0 12px 40px rgba(0,0,0,0.20);
    --nav-h: 64px;

    /* Spacing scale. Every padding, margin and gap in both stylesheets now comes
       from here. There used to be ~35 distinct literals doing the work of seven —
       0.4 next to 0.45, 0.6 next to 0.65 next to 0.7, .75rem next to 0.75rem — and
       that drift is what made the admin dashboard in particular look unfinished.
       Off-scale values were snapped to the nearest step (ties toward the tighter
       one), so a few elements moved by up to 0.25rem; nothing moved further.

       The step numbers are multiples of --space-1 (0.25rem), so --space-8 is 2rem
       and --space-12 is 3rem. There is deliberately no --space-7: 1.75rem was a
       one-off that belonged at 2rem. */
    --space-05: 0.125rem;
    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-5: 1.25rem;
    --space-6: 1.5rem;
    --space-8: 2rem;
    --space-10: 2.5rem;
    --space-12: 3rem;
    --space-16: 4rem;

    /* The "this row is highlighted" left bar. Was 3px in two places and 2px in two
       others, via two different mechanisms (border-left and an inset box-shadow). */
    --rule-accent: 3px;

    /* Height of the mobile sticky action bar. The list above it needs matching
       bottom clearance or the last card sits under the bar; keeping both ends on
       one token stops them drifting apart. */
    --sticky-actions-h: 4.5rem;

    /* Font-size scale. Every size below 1rem in both stylesheets now comes from
       here — there used to be nine different literals (0.7 / 0.75 / 0.8 / 0.82 /
       0.85 / 0.875 / 0.9 / 0.95rem, plus .9rem and .875rem written without the
       leading zero) doing what is really about five jobs, which is where the
       "everything is slightly different" feeling came from.

       Values are unchanged from what was already on the page — this was a
       consolidation, not a re-scale — except 0.82rem, a one-off that is now --fs-xs
       like every other caption. */
    --fs-2xs: 0.7rem;        /* micro caps: table headers, badges, step labels */
    --fs-tiny: 0.75rem;      /* status/reference badges, stat labels */
    --fs-caption: 0.8rem;    /* captions and meta text under a heading */
    --fs-xs: 0.85rem;        /* muted caption/meta text */
    --fs-sm: 0.875rem;       /* small body text: labels, footer copy, error text */
    --fs-body-sm: 0.9rem;    /* small body copy */
    --fs-body: 0.95rem;      /* form controls, buttons, card titles */
    --fs-h3-compact: 1.05rem; /* card/table sub-heading, smaller than default h3 */

    /* Semantic alias — the uppercase "eyebrow" label above a heading. */
    --fs-eyebrow: var(--fs-caption);

    /* Minimum comfortable touch target. Anything a finger has to hit on a phone
       should reach this in both dimensions, padding included. */
    --touch-min: 44px;
}

/* ---------- Reset / Base ---------- */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* Form controls don't inherit font from their ancestors — the UA applies its own
   `font` shorthand instead. Without this, <button class="btn"> rendered in the
   browser's default face while <a class="btn"> used --font, so the same class
   produced two different-looking buttons. The shorthand also resets line-height
   to `normal`, which is what threw the calendar's arrow glyphs off-centre. */
button, input, select, textarea { font: inherit; }

/* Every :hover rule in this file and in booking.css is wrapped in
   `@media (hover: hover) and (pointer: fine)`. A touchscreen has no hover state, so
   the browser fakes one on tap and leaves it applied until you tap something else —
   which left service cards, gallery tiles, booking rows and filter chips visibly
   stuck in their hover styling after every touch. The wrap means those styles only
   ever apply to a real pointer. Press feedback on touch comes from :active and from
   the .selected / .active classes, which are unaffected. */

/* Suppresses the grey/blue box the browser flashes over a tapped element. The site
   has a lot of <div @onclick> controls, so this was firing constantly. */
* { -webkit-tap-highlight-color: transparent; }

html { scroll-behavior: smooth; }

/* Honour the OS "reduce motion" setting: no smooth scrolling, no bouncing scroll
   hint, and transitions collapse to something imperceptible rather than being
   removed outright (0s can make state changes read as glitches). */
@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }

    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

body {
    font-family: var(--font);
    background: var(--dark);
    color: rgba(255,255,255,0.85);
    line-height: 1.6;
    font-size: 16px;
    -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }
a { color: var(--green); text-decoration: none; }
@media (hover: hover) and (pointer: fine) {
    a:hover { color: var(--green-dark); }
}
ul { list-style: none; }

/* The gutter grows to clear the notch in landscape, where the safe-area insets are
   horizontal. max() rather than addition so the normal gutter is unchanged on every
   device without insets. */
.container {
    width: 100%;
    max-width: 1200px;
    margin: 0 auto;
    padding-inline:
        max(var(--space-5), env(safe-area-inset-left))
        max(var(--space-5), env(safe-area-inset-right));
}

/* ---------- Typography ---------- */
h1, h2, h3, h4 {
    font-weight: 700;
    line-height: 1.2;
    color: var(--white);
}

h1 { font-size: clamp(2rem, 6vw, 3.5rem); }
h2 { font-size: clamp(1.5rem, 4vw, 2.4rem); }
h3 { font-size: clamp(1.1rem, 3vw, 1.4rem); }

/* Routes.razor has <FocusOnNavigate Selector="h1">, which moves focus to the page
   heading after every navigation so a screen reader announces the new page. That
   gives the h1 tabindex="-1", and Chromium counts the programmatic focus as
   :focus-visible — so the browser was painting its default focus ring around the
   main heading on every single page load, on every page.

   The focus move is worth keeping; the ring is not. Nobody pressed Tab to get here,
   and the heading isn't a control (tabindex="-1" keeps it out of the tab order), so
   suppressing the ring costs no keyboard affordance. Interactive elements keep
   theirs — see .btn:focus-visible and the lightbox controls. */
h1[tabindex="-1"]:focus,
h1[tabindex="-1"]:focus-visible {
    outline: none;
}

/* ---------- Buttons ---------- */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-6);
    border-radius: 50px;
    font-size: var(--fs-body);
    font-weight: 600;
    cursor: pointer;
    border: 2px solid transparent;
    transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease,
                box-shadow 0.2s ease, transform 0.2s ease;
    white-space: nowrap;
    text-decoration: none;
}

.btn-lg { padding: var(--space-3) var(--space-8); font-size: 1rem; }
.btn-full { width: 100%; }

/* The booking wizard disables Continue/Confirm until each step is valid. Without
   this the button stayed fully saturated and still lifted on hover, so there was
   nothing to tell you why the click did nothing. */
.btn:disabled,
.btn[disabled] {
    opacity: 0.45;
    cursor: not-allowed;
    transform: none;
    box-shadow: none;
    pointer-events: none;
}

.btn:focus-visible {
    outline: 2px solid var(--white);
    outline-offset: 2px;
}

.btn-primary {
    background: var(--green);
    color: var(--white);
    border-color: var(--green);
}
@media (hover: hover) and (pointer: fine) {
    .btn-primary:hover {
        background: var(--green-dark);
        border-color: var(--green-dark);
        color: var(--white);
        transform: translateY(-1px);
        box-shadow: 0 4px 16px rgba(61,187,72,0.35);
    }
}

.btn-outline {
    background: transparent;
    color: var(--green);
    border-color: var(--green);
}
@media (hover: hover) and (pointer: fine) {
    .btn-outline:hover {
        background: var(--green);
        color: var(--white);
    }
}

.btn-ghost {
    background: transparent;
    color: var(--white);
    border-color: rgba(255,255,255,0.5);
}
@media (hover: hover) and (pointer: fine) {
    .btn-ghost:hover {
        background: rgba(255,255,255,0.12);
        border-color: var(--white);
        color: var(--white);
    }
}

/* Destructive actions (delete account, delete booking). Muted until hover so it
   doesn't shout from the page, matching .action-btn--delete's treatment. */
.btn-danger {
    background: transparent;
    color: #f87171;
    border-color: rgba(220,38,38,0.45);
}
@media (hover: hover) and (pointer: fine) {
    .btn-danger:hover {
        background: #dc2626;
        border-color: #dc2626;
        color: var(--white);
    }
}

/* Visually hidden but available to screen readers — for labels whose text would be
   redundant on screen next to a placeholder. */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* ---------- Navbar ---------- */
.navbar-wrapper {
    position: sticky;
    top: 0;
    z-index: 1000;
    background: var(--dark);
    border-bottom: 1px solid var(--dark-3);
    transition: box-shadow 0.3s;
}

.navbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    height: var(--nav-h);
}

.navbar-brand {
    font-size: 1.35rem;
    font-weight: 800;
    color: var(--white) !important;
    letter-spacing: -0.5px;
    display: flex;
    align-items: center;
}
@media (hover: hover) and (pointer: fine) {
    .navbar-brand:hover { color: var(--green) !important; }
}
/* logo.avif has a solid black background; screen blend hides it on dark surfaces */
.navbar-logo { height: 40px; width: auto; display: block; mix-blend-mode: screen; }

/* ---------- Footer logo ---------- */
.footer-logo-link {
    display: inline-block;
    margin-bottom: var(--space-3);
    line-height: 0;
}
.footer-logo-img  { height: 38px; width: auto; display: block; mix-blend-mode: screen; }
.footer-logo-text {
    font-size: 1.3rem;
    font-weight: 800;
    color: var(--white);
    margin-bottom: var(--space-3);
}

.nav-links {
    display: flex;
    align-items: center;
    gap: var(--space-1);
}

.nav-links a {
    color: rgba(255,255,255,0.75);
    font-weight: 500;
    font-size: var(--fs-body);
    padding: var(--space-2) var(--space-3);
    border-radius: 6px;
    transition: color 0.2s, background 0.2s;
}

/* The current page's link stays highlighted on every device — it's state, not
   feedback — so it sits outside the pointer guard. Only the hover styling is
   gated. These shared one selector list, which would have hidden the active
   highlight on every phone. */
.nav-links a.active {
    color: var(--white);
    background: rgba(255,255,255,0.08);
}

@media (hover: hover) and (pointer: fine) {
    .nav-links a:hover {
        color: var(--white);
        background: rgba(255,255,255,0.08);
    }
}

/* The secondary nav pill — Dashboard signed in, Sign in signed out. Same shape as
   .btn-book but outlined, so booking stays the one filled call to action. The
   !important flags are here for the same reason .btn-book's are: both have to beat
   `.nav-links a` above, which sets colour and padding on every link in the bar. */
.btn-nav {
    border: 1.5px solid var(--green);
    color: var(--green) !important;
    padding: var(--space-2) var(--space-5) !important;
    border-radius: 50px !important;
    font-weight: 600 !important;
    margin-left: var(--space-2);
}

/* `.nav-links a.active` paints a translucent panel behind the current page's link.
   Inside the pill that reads as a smudge against the border, so the active state
   fills it instead. Outside the pointer guard, like the rule it overrides: the
   current page is state, not hover feedback. */
.nav-links a.btn-nav.active {
    background: var(--green);
    color: var(--white) !important;
}

@media (hover: hover) and (pointer: fine) {
    .btn-nav:hover {
        background: var(--green);
        color: var(--white) !important;
    }
}

.btn-book {
    background: var(--green) !important;
    color: var(--white) !important;
    padding: var(--space-2) var(--space-5) !important;
    border-radius: 50px !important;
    font-weight: 600 !important;
    margin-left: var(--space-2);
}
@media (hover: hover) and (pointer: fine) {
    .btn-book:hover {
        background: var(--green-dark) !important;
        color: var(--white) !important;
    }
}

/* Hamburger */
/* The bars stay 24×2px; the padding grows the hit area to --touch-min in both
   directions. It was 40×32, which is a small target for the control that opens the
   whole menu on a phone. */
.hamburger {
    display: none;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: var(--space-1);
    background: none;
    border: none;
    cursor: pointer;
    padding: var(--space-2);
    min-width: var(--touch-min);
    min-height: var(--touch-min);
    border-radius: 6px;
}
.hamburger span {
    display: block;
    width: 24px;
    height: 2px;
    background: var(--white);
    border-radius: 2px;
    transition: all 0.25s ease;
    transform-origin: center;
}
.hamburger.open span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.hamburger.open span:nth-child(2) { opacity: 0; transform: scaleX(0); }
.hamburger.open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* ---------- Hero ---------- */
.hero {
    position: relative;
    min-height: calc(100svh - var(--nav-h));
    display: flex;
    align-items: center;
    background: var(--dark);
    overflow: hidden;
}

.hero::before {
    content: '';
    position: absolute;
    inset: 0;
    background: radial-gradient(ellipse 80% 60% at 70% 50%, rgba(61,187,72,0.15) 0%, transparent 70%);
    pointer-events: none;
}

/* A faint circle texture over the hero photo. The circles are drawn as strokes,
   so the opacity has to be stroke-opacity — fill-opacity alone leaves the rings
   at full white and tiles a hard lattice across the whole hero, hiding the photo
   behind it. */
.hero-overlay {
    position: absolute;
    inset: 0;
    background: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.02'%3E%3Ccircle cx='30' cy='30' r='28' stroke='%23ffffff' stroke-width='1' stroke-opacity='0.05'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
    pointer-events: none;
}

/* This element is both .container and .hero-content. Capping it at 680px let
   .container's `margin: 0 auto` centre the whole text block, which pushed the
   buttons into the middle of the hero where the scrim is thinnest — so they read
   as washed out. Keep the container's own width so the text lines up with the
   gutter every other section uses, and cap the children instead. */
.hero-content {
    position: relative;
    padding: var(--space-16) var(--space-5);
}

.hero-eyebrow,
.hero-title,
.hero-actions { max-width: 680px; }

.hero-eyebrow {
    color: var(--green);
    font-weight: 600;
    font-size: var(--fs-eyebrow);
    text-transform: uppercase;
    letter-spacing: 2px;
    margin-bottom: var(--space-4);
}

.hero-title {
    color: var(--white);
    margin-bottom: var(--space-5);
}

.hero-title span { color: var(--green); }

.hero-subtitle {
    color: rgba(255,255,255,0.7);
    font-size: 1.1rem;
    margin-bottom: var(--space-8);
    max-width: 520px;
    line-height: 1.7;
}

.hero-actions {
    display: flex;
    gap: var(--space-4);
    flex-wrap: wrap;
}

.hero-scroll-hint {
    position: absolute;
    bottom: 2rem;
    left: 50%;
    transform: translateX(-50%);
    color: rgba(255,255,255,0.3);
    font-size: 1.4rem;
    animation: bounce 1.8s infinite;
}

@keyframes bounce {
    0%, 100% { transform: translateX(-50%) translateY(0); }
    50% { transform: translateX(-50%) translateY(8px); }
}

/* ---------- Trust Bar ---------- */
.trust-bar {
    background: var(--dark-2);
    padding: var(--space-6) 0;
    border-bottom: 1px solid var(--dark-3);
}

.trust-bar-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-4);
}

.trust-item {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    color: var(--white);
}

/* ---------- Icons ---------- */
/* <Icon /> renders a 1em square, so the icon takes its size from whatever
   font-size the surrounding rule already set and its colour from currentColor. */
.icon {
    width: 1em;
    height: 1em;
    display: inline-block;
    vertical-align: -0.125em;
    flex-shrink: 0;
}

.trust-icon {
    font-size: 1.75rem;
    flex-shrink: 0;
    color: var(--green);
    display: flex;
    align-items: center;
}

.trust-item strong {
    display: block;
    font-size: var(--fs-body);
    color: var(--white);
    margin-bottom: var(--space-05);
}

.trust-item p {
    font-size: var(--fs-caption);
    color: rgba(255,255,255,0.5);
    margin: 0;
}

/* ---------- Sections ---------- */
.section {
    padding: var(--space-16) 0;
}

.section-header {
    text-align: center;
    margin-bottom: var(--space-12);
}

.section-eyebrow {
    color: var(--green);
    font-size: var(--fs-eyebrow);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 2px;
    margin-bottom: var(--space-2);
}

.section-subtitle {
    color: rgba(255,255,255,0.55);
    font-size: var(--fs-h3-compact);
    max-width: 540px;
    margin: var(--space-3) auto 0;
}

.section-cta {
    text-align: center;
    margin-top: var(--space-10);
}

/* ---------- Services Grid ---------- */
.services-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-5);
}

.service-card {
    background: var(--dark-3);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
    padding: var(--space-8);
    transition: all 0.25s ease;
    position: relative;
}

@media (hover: hover) and (pointer: fine) {
    .service-card:hover {
        transform: translateY(-3px);
        box-shadow: var(--shadow);
        border-color: var(--green);
    }
}

.service-card--featured {
    background: var(--dark-4);
    border-color: var(--green);
    border-width: 2px;
}

.service-card--featured h3,
.service-card--featured p { color: rgba(255,255,255,0.9); }

.service-badge {
    position: absolute;
    top: -1px;
    right: 1.5rem;
    background: var(--green);
    color: var(--white);
    font-size: var(--fs-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 1px;
    padding: var(--space-1) var(--space-3);
    border-radius: 0 0 8px 8px;
}

.service-icon {
    font-size: 2rem;
    margin-bottom: var(--space-3);
    color: var(--green);
    line-height: 1;
}

.service-card h3 { margin-bottom: var(--space-2); }

.service-card p {
    color: rgba(255,255,255,0.55);
    font-size: var(--fs-body-sm);
    margin-bottom: var(--space-4);
}

.service-price {
    display: inline-block;
    color: var(--green);
    font-weight: 700;
    font-size: var(--fs-body);
}

/* ---------- How It Works ---------- */
.how-it-works {
    background: var(--dark-2);
    border-top: 1px solid var(--dark-3);
    border-bottom: 1px solid var(--dark-3);
}

.steps-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-6);
}

.step {
    text-align: center;
    padding: var(--space-6);
    background: var(--dark-3);
    border-radius: var(--radius);
    border: 1px solid var(--border-dark);
}

.step-number {
    font-size: 2.5rem;
    font-weight: 900;
    color: var(--green);
    opacity: 0.8;
    line-height: 1;
    margin-bottom: var(--space-3);
}

.step h3 { margin-bottom: var(--space-2); }
.step p { color: rgba(255,255,255,0.55); font-size: var(--fs-body-sm); }

.step-arrow {
    display: none;
    color: var(--green);
    font-size: 1.5rem;
    align-items: center;
    justify-content: center;
}

/* ---------- Star row ---------- */
.stars {
    font-size: 1rem;
    margin-bottom: var(--space-3);
    color: #F5B301;
    display: flex;
    gap: var(--space-05);
}

/* ---------- Rating summary ---------- */
/* Replaces the invented testimonial cards on the home page: the shop's real
   aggregate rating, linked to the profile it comes from. */
.rating-summary {
    max-width: 420px;
    margin: 0 auto;
    padding: var(--space-8) var(--space-6);
    text-align: center;
    background: var(--dark-3);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
}

.rating-summary__score {
    font-size: 3rem;
    font-weight: 900;
    line-height: 1;
    color: var(--green);
}

.rating-summary .stars {
    justify-content: center;
    font-size: 1.15rem;
    margin: var(--space-2) 0 var(--space-3);
}

.rating-summary__count {
    color: rgba(255,255,255,0.55);
    font-size: var(--fs-sm);
    margin-bottom: var(--space-5);
}

/* ---------- CTA Banner ---------- */
.cta-banner {
    background: var(--dark-2);
    border-top: 1px solid var(--dark-3);
    border-bottom: 1px solid var(--dark-3);
    padding: var(--space-16) 0;
    position: relative;
    overflow: hidden;
}

.cta-banner::before {
    content: '';
    position: absolute;
    inset: 0;
    background: radial-gradient(ellipse 70% 80% at 50% 50%, rgba(61,187,72,0.10) 0%, transparent 70%);
    pointer-events: none;
}

.cta-banner-content {
    position: relative;
    text-align: center;
    color: var(--white);
}

.cta-banner h2 {
    color: var(--white);
    margin-bottom: var(--space-3);
}

.cta-banner p {
    color: rgba(255,255,255,0.6);
    font-size: var(--fs-h3-compact);
    margin-bottom: var(--space-8);
    max-width: 480px;
    margin-left: auto;
    margin-right: auto;
}

.cta-actions {
    display: flex;
    justify-content: center;
    gap: var(--space-4);
    flex-wrap: wrap;
}

/* ---------- Page Hero ---------- */
.page-hero {
    background: linear-gradient(135deg, var(--dark) 0%, var(--dark-2) 100%);
    text-align: center;
    color: var(--white);
}

.page-hero--sm { padding: var(--space-16) var(--space-5) var(--space-12); }

.page-hero h1 { color: var(--white); margin-bottom: var(--space-3); }
.page-hero p { color: rgba(255,255,255,0.65); font-size: var(--fs-h3-compact); max-width: 500px; margin: 0 auto; }
.page-hero .section-eyebrow { margin-bottom: var(--space-2); }

/* ---------- Services Page ---------- */
.services-category {
    margin-bottom: var(--space-12);
}

.category-title {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-bottom: var(--space-6);
    padding-bottom: var(--space-3);
    border-bottom: 2px solid var(--border-dark);
}

.cat-icon { font-size: 1.4rem; }

.pricing-table { display: flex; flex-direction: column; gap: 0; }

.pricing-row {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: var(--space-4);
    padding: var(--space-4) 0;
    border-bottom: 1px solid var(--border-dark);
}

.pricing-row:last-child { border-bottom: none; }

.pricing-info h4 { font-size: 1rem; margin-bottom: var(--space-1); }
.pricing-info p { color: rgba(255,255,255,0.5); font-size: var(--fs-sm); margin: 0; }

.pricing-cost {
    color: var(--green);
    font-weight: 700;
    font-size: var(--fs-body);
    white-space: nowrap;
    flex-shrink: 0;
}

.package-grid {
    display: grid;
    /* auto-fit so cards reflow 1→2→3 across phone/tablet/desktop without ever
       forcing the bold name+price header wider than the column (which used to
       overflow the viewport at tablet widths). min(100%, …) keeps it safe on
       very narrow screens. */
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 270px), 1fr));
    gap: var(--space-5);
}

.package-card {
    background: var(--dark-3);
    border: 2px solid var(--border-dark);
    border-radius: var(--radius);
    padding: var(--space-8);
    position: relative;
    transition: all 0.25s;
    min-width: 0;
}

@media (hover: hover) and (pointer: fine) {
    .package-card:hover { border-color: var(--green); box-shadow: 0 0 0 1px var(--green); }
}

.package-card--popular {
    border-color: var(--green);
    background: var(--dark-4);
}

.package-badge {
    position: absolute;
    top: -14px;
    left: 50%;
    transform: translateX(-50%);
    background: var(--green);
    color: var(--white);
    font-size: var(--fs-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 1px;
    padding: var(--space-1) var(--space-4);
    border-radius: 50px;
    white-space: nowrap;
}

.package-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: var(--space-5);
    padding-bottom: var(--space-4);
    border-bottom: 1px solid var(--border-dark);
}

.package-price {
    font-size: 1.75rem;
    font-weight: 900;
    color: var(--green);
}

.package-card ul { margin-bottom: var(--space-6); }

.package-card li {
    color: rgba(255,255,255,0.7);
    font-size: var(--fs-body-sm);
    padding: var(--space-1) 0;
}

.services-note {
    background: var(--dark-3);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
    padding: var(--space-8);
    text-align: center;
    margin-top: var(--space-4);
}

.services-note p {
    color: rgba(255,255,255,0.5);
    margin-bottom: var(--space-5);
    font-size: var(--fs-body-sm);
}

/* ---------- About Page ---------- */
.about-intro {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-10);
    margin-bottom: var(--space-4);
}

.about-text h2 { margin-bottom: var(--space-4); }
.about-text p { color: rgba(255,255,255,0.6); margin-bottom: var(--space-4); line-height: 1.8; }

.about-stats {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-4);
}

.stat-card {
    background: var(--dark-3);
    border: 1px solid var(--border-dark);
    color: var(--white);
    border-radius: var(--radius);
    padding: var(--space-6);
    text-align: center;
}

.stat-number {
    font-size: 2.2rem;
    font-weight: 900;
    color: var(--green);
    line-height: 1;
    margin-bottom: var(--space-2);
}

.stat-label { color: rgba(255,255,255,0.6); font-size: var(--fs-xs); }

/* The star beside the rating figure. Without this rule .icon's `width: 1em` took
   its size from .stat-number's 2.2rem and rendered a 35px star next to "4.9". */
.stat-star {
    font-size: 0.45em;
    margin-left: 0.15em;
    vertical-align: 0.35em;
}

.team-section { background: var(--dark-2); border-top: 1px solid var(--dark-3); border-bottom: 1px solid var(--dark-3); }

.team-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-5);
}

/* Two-item variants. The generic .team-grid / .about-stats rules step up to three and
   four columns at wider breakpoints, which strands a two-item set across a very wide
   row; these cap the track count and keep the pair centred. */
.team-grid--pair,
.about-stats--pair {
    max-width: 620px;
    margin-inline: auto;
}

.team-card {
    background: var(--dark-3);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
    padding: var(--space-8);
    text-align: center;
    transition: all 0.25s;
}

@media (hover: hover) and (pointer: fine) {
    .team-card:hover { transform: translateY(-3px); box-shadow: var(--shadow); }
}

.team-avatar {
    width: 72px;
    height: 72px;
    border-radius: 50%;
    background: var(--green);
    color: var(--white);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.2rem;
    font-weight: 700;
    margin: 0 auto var(--space-4);
}

.team-role {
    color: var(--green);
    font-size: var(--fs-xs);
    font-weight: 600;
    margin-bottom: var(--space-3) !important;
}

.team-card p { color: rgba(255,255,255,0.55); font-size: var(--fs-sm); margin: 0; }

.values-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-5);
}

.value-card {
    padding: var(--space-8);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
    background: var(--dark-3);
    transition: all 0.25s;
}

@media (hover: hover) and (pointer: fine) {
    .value-card:hover { border-color: var(--green); box-shadow: 0 0 0 1px var(--green); }
}
.value-icon { font-size: 2rem; margin-bottom: var(--space-3); }
.value-card h3 { margin-bottom: var(--space-2); }
.value-card p { color: rgba(255,255,255,0.55); font-size: var(--fs-body-sm); }

/* ---------- Contact Page ---------- */
.contact-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-10);
}

/* h3 since this became a section of /about rather than a page of its own — the
   page's own h2 is the section header above it. */
.contact-form-wrapper h2,
.contact-form-wrapper h3 { margin-bottom: var(--space-3); }

/* Points repair requests at the wizard. The form used to ask for a service and a
   preferred date, which made it a second, worse booking flow; this says plainly
   which one you want. */
.contact-form-note {
    color: rgba(255,255,255,0.55);
    font-size: var(--fs-sm);
    line-height: 1.6;
    margin-bottom: var(--space-6);
}
.contact-form-note a { color: var(--green); text-decoration: underline; }

/* The contact block sits between the values grid and the CTA banner, both of which
   bring their own top padding — without this the three run together. */
.contact-section { border-top: 1px solid var(--dark-3); }

.form-group {
    margin-bottom: var(--space-5);
}

.form-group label {
    display: block;
    font-size: var(--fs-sm);
    font-weight: 600;
    margin-bottom: var(--space-2);
    color: rgba(255,255,255,0.85);
}

.form-control {
    width: 100%;
    padding: var(--space-3) var(--space-4);
    border: 1.5px solid var(--border-dark);
    border-radius: var(--radius-sm);
    font-size: var(--fs-body);
    font-family: var(--font);
    color: rgba(255,255,255,0.85);
    background: var(--dark-3);
    transition: border-color 0.2s, box-shadow 0.2s;
    appearance: auto;
    color-scheme: dark;
}

.form-control:focus {
    outline: none;
    border-color: var(--green);
    box-shadow: 0 0 0 3px rgba(61,187,72,0.15);
}

.form-row {
    display: grid;
    grid-template-columns: 1fr;
    gap: 0;
}

textarea.form-control { resize: vertical; min-height: 130px; }

/* Consent / privacy note under a form's submit button. */
.form-consent {
    font-size: var(--fs-xs);
    color: rgba(255,255,255,0.45);
    line-height: 1.5;
    margin: 0 0 var(--space-4);
}
.form-consent a { color: var(--green); text-decoration: underline; }

/* Small explanatory text under a single field. */
.form-hint {
    font-size: var(--fs-xs);
    color: rgba(255,255,255,0.45);
    margin-top: var(--space-1);
}
.form-hint a { color: var(--green); }

/* A field whose value is fixed by the account rather than typed. */
.form-control[readonly] {
    background: var(--dark-2);
    color: rgba(255,255,255,0.6);
    cursor: default;
}

.validation-message {
    /* Per-field validation text (Blazor's <ValidationMessage>) is a different UI
       role from .booking-error's banner, so only the size converges, not the color. */
    color: #dc2626;
    font-size: var(--fs-sm);
    margin-top: var(--space-1);
}

.success-message {
    text-align: center;
    padding: var(--space-12) var(--space-6);
    background: var(--dark-3);
    border-radius: var(--radius);
    border: 1px solid var(--border-dark);
}

.success-icon {
    width: 64px;
    height: 64px;
    border-radius: 50%;
    background: var(--green);
    color: var(--white);
    font-size: 1.75rem;
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0 auto var(--space-4);
}

.success-message h3 { margin-bottom: var(--space-2); }
.success-message p { color: rgba(255,255,255,0.55); margin-bottom: var(--space-6); }

/* Info Cards */
.info-card {
    background: var(--dark-3);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
    padding: var(--space-6);
    margin-bottom: var(--space-5);
}

.info-card h3 { margin-bottom: var(--space-4); font-size: var(--fs-h3-compact); }

.info-item {
    display: flex;
    gap: var(--space-3);
    align-items: flex-start;
    margin-bottom: var(--space-4);
}

.info-item:last-child { margin-bottom: 0; }

.info-icon { font-size: 1.2rem; flex-shrink: 0; margin-top: var(--space-05); }
.info-item strong { display: block; font-size: var(--fs-xs); margin-bottom: var(--space-1); }
.info-item p, .info-item a { font-size: var(--fs-sm); color: rgba(255,255,255,0.55); margin: 0; }
@media (hover: hover) and (pointer: fine) {
    .info-item a:hover { color: var(--green); }
}

.turnaround-list { display: flex; flex-direction: column; gap: var(--space-2); }

.turnaround-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: var(--fs-sm);
    color: rgba(255,255,255,0.65);
    padding: var(--space-2) 0;
    border-bottom: 1px solid var(--border-dark);
}

.turnaround-item:last-child { border-bottom: none; }

.turnaround-time {
    font-weight: 600;
    color: var(--green);
    font-size: var(--fs-caption);
}

/* ---------- Error / not-found pages ---------- */
.error-page {
    max-width: 560px;
    text-align: center;
}

.error-page p {
    color: rgba(255,255,255,0.65);
    line-height: 1.7;
    margin-bottom: var(--space-8);
}

.error-page__actions {
    display: flex;
    gap: var(--space-3);
    justify-content: center;
    flex-wrap: wrap;
}

.error-page__ref {
    margin-top: var(--space-8);
    font-size: var(--fs-xs);
    color: rgba(255,255,255,0.35);
}

.error-page__ref code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    background: var(--dark-3);
    border: 1px solid var(--border-dark);
    border-radius: 4px;
    padding: var(--space-05) var(--space-1);
    word-break: break-all;
}

@media (max-width: 479px) {
    .error-page__actions { flex-direction: column; }
    .error-page__actions .btn { width: 100%; justify-content: center; }
}

/* ---------- Legal documents (privacy, terms) ---------- */
/* Long-form prose, so it gets a reading measure rather than the full container
   width — 12-word lines are hard to track back from at the end of each one. */
.legal-doc {
    max-width: 720px;
}

.legal-doc h2 {
    font-size: clamp(1.2rem, 3vw, 1.5rem);
    margin: var(--space-10) 0 var(--space-3);
}

.legal-doc h2:first-of-type { margin-top: 0; }

.legal-doc h3 {
    font-size: var(--fs-h3-compact);
    margin: var(--space-6) 0 var(--space-2);
    color: rgba(255,255,255,0.9);
}

.legal-doc p {
    color: rgba(255,255,255,0.65);
    margin-bottom: var(--space-4);
    line-height: 1.75;
}

.legal-doc__list {
    margin: 0 0 var(--space-4);
    padding-left: var(--space-4);
    list-style: disc;
}

.legal-doc__list li {
    color: rgba(255,255,255,0.65);
    margin-bottom: var(--space-3);
    line-height: 1.7;
    padding-left: var(--space-1);
}

.legal-doc__list strong { color: rgba(255,255,255,0.9); }

.legal-doc a { text-decoration: underline; }

.legal-doc code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.9em;
    background: var(--dark-3);
    border: 1px solid var(--border-dark);
    border-radius: 4px;
    padding: var(--space-05) var(--space-1);
}

.legal-doc__updated {
    font-size: var(--fs-xs);
    color: rgba(255,255,255,0.35);
    margin-bottom: var(--space-8);
}

/* Marks a passage that still needs the shop's own details filled in. Deliberately
   loud — it must be impossible to publish these without noticing. */
.legal-doc__todo {
    background: rgba(234,179,8,0.10);
    border: 1px solid rgba(234,179,8,0.35);
    border-radius: var(--radius-sm);
    padding: var(--space-3) var(--space-4);
    color: #eab308 !important;
    font-size: var(--fs-sm);
}

/* ---------- Footer ---------- */
/* The footer is the last thing on the page, so in landscape its bottom edge sits
   under the home indicator. */
.site-footer {
    background: var(--dark);
    color: rgba(255,255,255,0.65);
    padding: var(--space-12) 0 calc(var(--space-6) + env(safe-area-inset-bottom));
}

.footer-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-8);
    margin-bottom: var(--space-8);
}

/* .footer-logo replaced by .footer-logo-img */

.footer-brand p { font-size: var(--fs-sm); line-height: 1.6; }

.footer-links h6, .footer-contact h6 {
    color: var(--white);
    font-size: var(--fs-xs);
    text-transform: uppercase;
    letter-spacing: 1.5px;
    margin-bottom: var(--space-3);
}

.footer-links ul li { margin-bottom: var(--space-2); }
.footer-links a { color: rgba(255,255,255,0.6); font-size: var(--fs-sm); transition: color 0.2s; }
@media (hover: hover) and (pointer: fine) {
    .footer-links a:hover { color: var(--green); }
}

.footer-contact p { font-size: var(--fs-sm); margin-bottom: var(--space-2); }

.footer-bottom {
    border-top: 1px solid var(--dark-3);
    padding-top: var(--space-5);
    text-align: center;
    font-size: var(--fs-caption);
    color: rgba(255,255,255,0.35);
}

/* ---------- Main ---------- */
.site-main {
    min-height: calc(100svh - var(--nav-h));
}

/* ---------- Error UI ---------- */
/* Blazor reveals this with an inline `display: block`, so any flex properties here
   would never apply — hence no gap/align-items. It also used to be `white-space:
   nowrap` centred at left:50%, which ran off both edges of a phone; it's now a
   width-constrained bar that wraps, clear of the home indicator. */
#blazor-error-ui {
    display: none;
    position: fixed;
    bottom: calc(1rem + env(safe-area-inset-bottom));
    left: 1rem;
    right: 1rem;
    max-width: 32rem;
    margin: 0 auto;
    background: #b91c1c;
    color: var(--white);
    padding: var(--space-3) var(--space-5);
    border-radius: var(--radius);
    font-size: var(--fs-sm);
    line-height: 1.5;
    text-align: center;
    z-index: 9999;
    box-shadow: var(--shadow-lg);
}

#blazor-error-ui .reload { color: var(--white); font-weight: 600; text-decoration: underline; margin-left: var(--space-2); }
#blazor-error-ui .dismiss { cursor: pointer; margin-left: var(--space-2); }

/* ---------- Blazor validation ---------- */
.valid.modified:not([type=checkbox]) { border-color: var(--green) !important; }
.invalid { border-color: #dc2626 !important; }

/* ---------- Shop Photography ---------- */
/* Photos are served from the public Supabase bucket via SiteImages, not wwwroot.
   Each one carries an onerror that hides itself, so if the bucket is unreachable
   the dark theme underneath shows through instead of a broken-image icon.
   Note: do NOT add mix-blend-mode here — that rule exists only to punch out the
   logo's solid black background and would wash a photograph out. */

.hero-photo {
    position: absolute;
    inset: 0;
    z-index: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* The hero title is white and the photo behind it is a bright overcast street,
   so it needs a scrim to stay readable. Heavier on the left, where the text sits.
   These two gradients multiply where they overlap, so keep them light: at 0.92
   and 0.80 the bottom-left corner composited to ~98% black and erased the photo
   entirely. The values below still clear AA for white text over the photo. */
.hero-scrim {
    position: absolute;
    inset: 0;
    z-index: 1;
    background:
        linear-gradient(to right, rgba(10,10,10,0.78) 0%, rgba(10,10,10,0.45) 55%, rgba(10,10,10,0.15) 100%),
        linear-gradient(to top, rgba(10,10,10,0.55) 0%, transparent 40%);
    pointer-events: none;
}

/* The horizontal gradient above assumes the text sits in the left half of the hero,
   which is true on desktop — .hero-eyebrow / .hero-title / .hero-actions cap at
   680px inside a 1200px container. On a phone the text is full-width, so it runs
   out into the thin end of that gradient and the subtitle lands over the brightest
   part of the photo (the bikes), where white-on-photo stops being readable.

   Below the point where that assumption holds, the scrim becomes vertical instead,
   which tracks where the text actually is. Kept well short of opaque: the earlier
   note on this element records that pushing these past ~0.9 composites to near-black
   and erases the photograph. */
@media (max-width: 767px) {
    .hero-scrim {
        background:
            linear-gradient(to bottom,
                rgba(10,10,10,0.55) 0%,
                rgba(10,10,10,0.72) 40%,
                rgba(10,10,10,0.80) 100%);
    }
}

/* The green wash sits under the scrim rather than over it — stacked on top it
   tinted the photo and the buttons along with everything else. */
.hero::before { z-index: 1; }
.hero-overlay { z-index: 2; }
.hero-content { z-index: 3; }

/* Columns rather than a grid: three of these photos are portrait and five are
   landscape, and forcing them all to one ratio would crop the opening-times sign
   past legibility. Columns let every photo keep its own shape with no ragged
   rows. break-inside stops a card splitting across a column. */
.gallery-grid {
    column-count: 1;
    column-gap: var(--space-5);
}

.gallery-item {
    margin: 0 0 var(--space-5);
    break-inside: avoid;
    background: var(--dark-3);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
    overflow: hidden;
    transition: transform 0.25s, border-color 0.25s, box-shadow 0.25s;
}

/* Colour-change only, deliberately without the translateY lift the other cards use:
   the Home teaser shares this class, and shifting a photo under the cursor meant
   chasing it. The border was always 1px — only its colour moves. */
@media (hover: hover) and (pointer: fine) {
    .gallery-item:hover {
        border-color: var(--green);
        box-shadow: var(--shadow);
    }
}

.gallery-item img {
    width: 100%;
    height: auto;
}

/* Click-to-enlarge, scoped to .gallery-page so the Home teaser — which shares
   .gallery-grid / .gallery-item but isn't clickable — is left alone. */
.gallery-page .gallery-item__trigger {
    display: block;
    width: 100%;
    padding: 0;
    border: 0;
    background: none;
    cursor: zoom-in;
}

.gallery-page .gallery-item__trigger:focus-visible {
    outline: 2px solid var(--green);
    outline-offset: 3px;
}

/* ---------- Gallery lightbox ---------- */
/* Above .navbar-wrapper (1000). At z-index 100 the sticky header rendered on top of
   the fullscreen photo, which was most obvious on a phone where the header takes a
   larger share of the screen. */
.lightbox {
    position: fixed;
    inset: 0;
    z-index: 1100;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    padding: var(--space-4);
    background: rgba(10,10,10,0.94);
    backdrop-filter: blur(4px);
    cursor: zoom-out;
    /* Stops a swipe on the overlay chaining through to the page behind it, which on
       a phone scrolled the gallery away underneath the open photo. */
    overscroll-behavior: contain;
}

.lightbox:focus { outline: none; }

/* The arrows are pinned to the viewport edges (below), so the photo keeps clear of
   them by reserving a gutter each side: 44px control + 1.25rem offset + breathing
   room. Without this a wide landscape shot slides underneath them. */
.lightbox__figure {
    max-width: min(92vw, calc(100vw - 176px));
    max-height: 88vh;
    cursor: default;
}

.lightbox__figure img {
    max-width: min(92vw, calc(100vw - 176px));
    max-height: 88vh;
    width: auto;
    height: auto;
    object-fit: contain;
    border-radius: var(--radius-sm);
}

.lightbox__nav,
.lightbox__close {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    border: 1px solid rgba(255,255,255,0.25);
    background: rgba(255,255,255,0.08);
    color: var(--white);
    font-size: 1.25rem;
    line-height: 1;
    cursor: pointer;
    transition: background-color 0.2s, border-color 0.2s;
}

@media (hover: hover) and (pointer: fine) {
    .lightbox__nav:hover,
    .lightbox__close:hover {
        background: var(--green);
        border-color: var(--green);
    }
}

.lightbox__nav:focus-visible,
.lightbox__close:focus-visible {
    outline: 2px solid var(--white);
    outline-offset: 2px;
}

/* The lightbox is inset:0 over the whole screen, so its controls are the one place
   that can land under the notch or the home indicator. */
.lightbox__close {
    position: absolute;
    top: calc(1rem + env(safe-area-inset-top));
    right: calc(1rem + env(safe-area-inset-right));
}

/* Pinned to the viewport rather than sitting in the flex row beside the photo.
   In flow they moved on every click: the figure is width:auto, so a portrait shot
   is far narrower than a landscape one, and centring the row slid both arrows in
   and out under the cursor. Anchoring them means only the photo changes size. */
.lightbox__nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
}

.lightbox__nav--prev { left: calc(1.25rem + env(safe-area-inset-left)); }
.lightbox__nav--next { right: calc(1.25rem + env(safe-area-inset-right)); }

.lightbox__count {
    position: absolute;
    bottom: 1rem;
    left: 50%;
    transform: translateX(-50%);
    color: rgba(255,255,255,0.55);
    font-size: var(--fs-sm);
}

/* On narrow screens the arrows would squeeze the photo, so they move to the
   bottom corners and the photo gets the full width. */
@media (max-width: 639px) {
    .lightbox__nav {
        top: auto;
        transform: none;
        bottom: calc(1rem + env(safe-area-inset-bottom));
    }
    .lightbox__nav--prev { left: calc(1rem + env(safe-area-inset-left)); }
    .lightbox__nav--next { right: calc(1rem + env(safe-area-inset-right)); }
    .lightbox__figure,
    .lightbox__figure img { max-width: 96vw; max-height: 74vh; }
}

/* About: photo sits above the stat cards in the narrower right-hand column. */
.about-aside {
    display: grid;
    gap: var(--space-6);
    align-content: start;
}

/* Both are portrait originals, and the opening-times photo is only useful if the
   sign stays readable — so never crop them. Capping the width keeps them from
   running absurdly tall while their column is still full-width below 1024px. */
.about-photo,
.contact-photo {
    margin: 0;
    max-width: 420px;
    background: var(--dark-3);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
    overflow: hidden;
}

.about-photo img,
.contact-photo img {
    width: 100%;
    height: auto;
}

.about-photo figcaption,
.contact-photo figcaption {
    padding: var(--space-3) var(--space-4);
    color: rgba(255,255,255,0.55);
    font-size: var(--fs-xs);
}

/* ============================================================
   iOS INPUT ZOOM
   ------------------------------------------------------------
   Safari on iOS zooms the viewport whenever a focused input's font-size is under
   16px, and never zooms back out — so every tap on a field left the page enlarged
   and the layout shifted. Every control on the site was between 13.6px and 15.2px,
   missing the threshold by as little as 0.8px, which is why it read as a mysterious
   jitter rather than an obvious mistake.

   The floor is applied at mobile widths only; desktop keeps the smaller, denser
   sizes, where the behaviour doesn't exist. Note the fix is NOT `maximum-scale=1`
   on the viewport meta: that also stops the zoom, but by disabling pinch-zoom for
   everybody, which fails WCAG 1.4.4.

   Note: booking.css loads AFTER this file and defines its own smaller sizes for the
   admin controls (.form-control--compact, .admin-search .form-control, and so on).
   Media queries add no specificity, so those would win on source order alone — the
   matching override for them lives at the end of booking.css. Keep the two in step.
   ============================================================ */
@media (max-width: 767px) {
    input,
    select,
    textarea,
    .form-control {
        font-size: 16px;
    }
}

/* ============================================================
   RESPONSIVE BREAKPOINTS
   ============================================================ */

/* ---- SM: 480px ---- */
@media (min-width: 480px) {
    .trust-bar-grid {
        grid-template-columns: 1fr 1fr;
    }
    .form-row {
        grid-template-columns: 1fr 1fr;
        gap: var(--space-5);
    }
}

/* ---- MD: 768px ---- */
@media (min-width: 768px) {
    .services-grid {
        grid-template-columns: repeat(2, 1fr);
    }
    .steps-grid {
        grid-template-columns: 1fr auto 1fr auto 1fr auto 1fr;
        align-items: start;
    }
    .step-arrow {
        display: flex;
        padding-top: var(--space-10);
    }
    .team-grid {
        grid-template-columns: repeat(2, 1fr);
    }
    .values-grid {
        grid-template-columns: repeat(2, 1fr);
    }
    .about-stats {
        grid-template-columns: repeat(4, 1fr);
    }
    .about-stats--pair {
        grid-template-columns: repeat(2, 1fr);
    }
    .about-intro {
        grid-template-columns: 1fr;
    }
    .footer-grid {
        grid-template-columns: 2fr 1fr 1fr;
    }
    .trust-bar-grid {
        grid-template-columns: repeat(4, 1fr);
    }
    .gallery-grid {
        column-count: 2;
    }
}

/* ---- LG: 1024px ---- */
@media (min-width: 1024px) {
    .services-grid {
        grid-template-columns: repeat(3, 1fr);
    }
    .team-grid {
        grid-template-columns: repeat(3, 1fr);
    }
    .team-grid--pair {
        grid-template-columns: repeat(2, 1fr);
    }
    .about-intro {
        grid-template-columns: 3fr 2fr;
        align-items: start;
    }
    .about-stats {
        grid-template-columns: 1fr 1fr;
    }
    .contact-grid {
        grid-template-columns: 3fr 2fr;
        align-items: start;
    }
    .gallery-grid {
        column-count: 3;
    }
    .section { padding: var(--space-16) 0; }
}

/* ---- Mobile Nav ---- */
@media (max-width: 767px) {
    .hamburger { display: flex; }

    .nav-links {
        display: none;
        position: absolute;
        top: var(--nav-h);
        left: 0;
        right: 0;
        flex-direction: column;
        background: var(--dark-2);
        padding: var(--space-4);
        gap: var(--space-1);
        border-top: 1px solid var(--dark-3);
        border-bottom: 1px solid var(--dark-3);
        box-shadow: 0 8px 24px rgba(0,0,0,0.3);
        /* Signed in, the menu holds 8 items at ~48px — taller than a phone in
           landscape, and previously with no way to reach the last entries.
           overscroll-behavior stops the scroll chaining to the page behind. */
        max-height: calc(100svh - var(--nav-h));
        overflow-y: auto;
        overscroll-behavior: contain;
    }

    .nav-links--open { display: flex; }

    .nav-links a {
        width: 100%;
        padding: var(--space-3) var(--space-4);
        border-radius: 8px;
        font-size: 1rem;
        /* Full-width already, but this guarantees the vertical half of the target. */
        min-height: var(--touch-min);
        display: flex;
        align-items: center;
    }

    .btn-book,
    .btn-nav {
        margin-left: 0 !important;
        margin-top: var(--space-1);
        text-align: center;
    }
    /* .nav-links a above makes every row a flex box, so text-align does nothing —
       the pill labels sat hard against the left edge while the desktop bar centres
       them. Both pills, not just the new one: .btn-book has had this since the flex
       row landed. */
    .btn-book,
    .btn-nav { justify-content: center; }

    .navbar-wrapper { position: sticky; }
}

/* ============================================================
   EXTRA MOBILE FIXES (< 480px)
   ============================================================ */
@media (max-width: 479px) {

    /* Tighter section padding on small phones */
    .section { padding: var(--space-10) 0; }

    /* Hero: reduce heading on very small screens */
    .hero-content { padding: var(--space-12) var(--space-5) var(--space-10); }
    .hero-actions { flex-direction: column; }
    .hero-actions .btn { width: 100%; justify-content: center; }

    /* Hero scroll hint hidden on short screens */
    .hero-scroll-hint { display: none; }

    /* Page hero: less padding */
    .page-hero--sm { padding: var(--space-10) var(--space-5) var(--space-8); }

    /* Trust bar: single column on tiny screens */
    .trust-bar { padding: var(--space-5) 0; }
    .trust-bar-grid { grid-template-columns: 1fr; }

    /* Pricing rows: stack label above price */
    .pricing-row { flex-direction: column; gap: var(--space-1); }
    .pricing-cost { align-self: flex-start; }

    /* Section header */
    .section-header { margin-bottom: var(--space-8); }

    /* Services note: stack button */
    .services-note { padding: var(--space-5); }

    /* CTA banner */
    .cta-banner { padding: var(--space-10) 0; }
    .cta-actions { flex-direction: column; align-items: center; }
    .cta-actions .btn { width: 100%; max-width: 320px; justify-content: center; }

    /* Footer */
    .footer-grid { gap: var(--space-6); }

    /* Form rows stay single column on tiny screens */
    .form-row { grid-template-columns: 1fr !important; gap: 0 !important; }

    /* Contact info cards: smaller padding */
    .info-card { padding: var(--space-4); }

    /* Turnaround table: wrap service name */
    .turnaround-item { flex-wrap: wrap; gap: var(--space-1); }
}

/* Booking-page, customer-auth, and admin-only styles live in booking.css,
   not here — app.css is the public-site stylesheet. */

/* ---------- Password field with reveal toggle ---------- */
/* Typing a password blind on a phone keyboard is the main reason people lock
   themselves out. The toggle is a real button (focusable, labelled) driven by
   wwwroot/site.js, not an inline handler. */
.password-field {
    position: relative;
    display: flex;
    align-items: center;
}

.password-field .form-control {
    /* Room for the toggle, so a long password doesn't run underneath it. */
    padding-right: var(--space-12);
}

.password-field__toggle {
    position: absolute;
    right: 0;
    top: 0;
    bottom: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 3rem;
    min-height: var(--touch-min);
    padding: 0;
    background: none;
    border: none;
    color: rgba(255,255,255,0.45);
    font-size: 1.1rem;
    cursor: pointer;
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

.password-field__toggle:focus-visible {
    outline: 2px solid var(--green);
    outline-offset: -2px;
}

/* Pressed = password currently visible. */
.password-field__toggle[aria-pressed="true"] { color: var(--green); }

@media (hover: hover) and (pointer: fine) {
    .password-field__toggle:hover { color: var(--white); }
}

/* ---------- /devinfo (development only) ---------- */
/* Deliberately plain: this page exists to be read on a phone in a hurry, and its
   own styling must not be what's under test. */
.devinfo { max-width: 640px; }

.devinfo__hero {
    background: var(--dark-3);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
    padding: var(--space-6) var(--space-5);
    text-align: center;
    margin-bottom: var(--space-6);
}

.devinfo__hero-label {
    font-size: var(--fs-2xs);
    text-transform: uppercase;
    letter-spacing: 1.5px;
    color: rgba(255,255,255,0.4);
}

.devinfo__hero-value {
    font-size: 3rem;
    font-weight: 900;
    line-height: 1.1;
    color: var(--green);
    font-variant-numeric: tabular-nums;
}

.devinfo__hero-note {
    font-size: var(--fs-sm);
    color: rgba(255,255,255,0.6);
    margin: var(--space-2) 0 var(--space-4);
    line-height: 1.5;
}

.devinfo__list {
    display: flex;
    flex-direction: column;
    gap: 0;
    margin-bottom: var(--space-6);
}

.devinfo__row {
    display: flex;
    flex-direction: column;
    gap: var(--space-05);
    padding: var(--space-3) 0;
    border-bottom: 1px solid var(--border-dark);
}

.devinfo__row:last-child { border-bottom: none; }

.devinfo__row dt {
    font-size: var(--fs-2xs);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: rgba(255,255,255,0.4);
}

.devinfo__row dd {
    margin: 0;
    font-size: var(--fs-sm);
    color: rgba(255,255,255,0.85);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    word-break: break-word;
}

.devinfo__ua { font-size: var(--fs-2xs); color: rgba(255,255,255,0.55); }

/* Rendered so they can be measured, but they aren't controls — the sample row is
   aria-hidden and these must not look clickable. */
.devinfo__samples {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-bottom: var(--space-6);
    pointer-events: none;
    opacity: 0.7;
}

.devinfo__samples .cal-day {
    width: 44px;
    flex: 0 0 auto;
}

.devinfo__actions {
    display: flex;
    gap: var(--space-3);
    flex-wrap: wrap;
}

.devinfo__actions .btn { flex: 1 1 auto; justify-content: center; }

.devinfo__copied {
    margin-top: var(--space-3);
    font-size: var(--fs-sm);
    color: var(--green);
}

/* The ErrorBoundary panel in MainLayout. Deliberately plain: it appears when
   something has already gone wrong, and a page that tries to look clever at that
   moment reads as a second failure. */
.page-error {
    max-width: 34rem;
    text-align: center;
}

.page-error__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    justify-content: center;
    margin-top: var(--space-5);
}

/* Honeypot field on the enquiry form (About.razor). Off-canvas rather than
   display:none — some bots skip hidden inputs and fill the rest, which is exactly
   the behaviour this is trying to catch. */
.hp-field {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}
