/* ============================================================
   FIXLOSOPHY – Booking & Admin Styles
   ============================================================ */

/* ---------- Step Indicator ---------- */
.steps-indicator {
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: var(--space-10);
    flex-wrap: nowrap;
    gap: 0;
}

.step-indicator {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
}

.step-indicator__dot {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: var(--dark-3);
    border: 2px solid var(--border-dark);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--fs-xs);
    font-weight: 700;
    color: rgba(255,255,255,0.4);
    transition: all 0.3s;
}

.step-indicator.active .step-indicator__dot {
    background: var(--green);
    border-color: var(--green);
    color: var(--white);
    box-shadow: 0 0 0 4px var(--green-glow);
}

.step-indicator.done .step-indicator__dot {
    background: var(--green-dark);
    border-color: var(--green-dark);
    color: var(--white);
}

.step-indicator__label {
    font-size: var(--fs-2xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: rgba(255,255,255,0.35);
}

.step-indicator.active .step-indicator__label,
.step-indicator.done .step-indicator__label {
    color: var(--green);
}

.step-indicator__line {
    flex: 1;
    height: 2px;
    background: var(--border-dark);
    margin: 0 var(--space-1);
    margin-bottom: var(--space-5);
    min-width: 24px;
    max-width: 80px;
    transition: background 0.3s;
}

.step-indicator__line.done { background: var(--green-dark); }

/* ---------- Booking Card ---------- */
.booking-card {
    background: var(--dark-2);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
    padding: var(--space-8);
    max-width: 860px;
    margin: 0 auto;
}

.booking-card__title {
    margin-bottom: var(--space-1);
}

.booking-card__subtitle {
    color: rgba(255,255,255,0.5);
    margin-bottom: var(--space-8);
    font-size: var(--fs-body);
}

.booking-error {
    background: rgba(220,38,38,0.12);
    border: 1px solid rgba(220,38,38,0.3);
    color: #f87171;
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-sm);
    margin-bottom: var(--space-5);
    font-size: var(--fs-sm);
}

/* ---------- Account page ---------- */
.account-section { padding-top: var(--space-8); }
.account-section .booking-card { margin-bottom: var(--space-6); }

/* The saved-bike list sits directly above the add-a-bike field. .photo-picker-list
   carries no bottom margin of its own because the booking wizard's copy of it takes
   its spacing from the .form-group around it — which this row isn't in, so without
   this the last bike sat flush against the input. Scoped to the account page for the
   same reason: unscoped it would double the wizard's spacing. */
.account-section .photo-picker-list { margin-bottom: var(--space-5); }

.account-booking-list { display: flex; flex-direction: column; gap: var(--space-3); }

.account-booking-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-4);
    padding: var(--space-3) var(--space-4);
    background: var(--dark-3);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
}

.account-booking-row__info {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: var(--space-05);
    font-size: var(--fs-xs);
}

.account-booking-row__info span { color: rgba(255,255,255,0.5); }

/* Status badge plus the cancel controls. Wraps under the booking details on a
   narrow screen rather than squeezing the service name. */
.account-booking-row__actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
    justify-content: flex-end;
}

/* Why the Cancel button isn't there — work already started, or the two-hour cutoff
   passed. Its own full-width line, like the job notes below it, and order 3 so it
   still lands under the actions once the row reorders on a phone. Said on the page
   rather than in a title attribute: the reason is the whole point of the change. */
.account-booking-row__blocked {
    flex: 1 1 100%;
    order: 3;
    margin: 0;
    font-size: var(--fs-xs);
    color: rgba(255,255,255,0.5);
}

@media (max-width: 479px) {
    .account-booking-row {
        flex-wrap: wrap;
        align-items: flex-start;
    }
    .account-booking-row__info { flex: 1 1 100%; order: 2; }
    .account-booking-row__actions { flex: 1 1 100%; order: 3; justify-content: flex-start; }
}

/* An input with a button beside it. Not .form-row: that's a two-column grid whose
   cells stretch to the row height, which left the button taller than the field and
   misaligned against the field's bottom margin. */
.inline-add-row {
    display: flex;
    gap: var(--space-3);
    align-items: flex-start;
    flex-wrap: wrap;
}
.inline-add-row .form-control { flex: 1 1 12rem; }
.inline-add-row .btn { flex: 0 0 auto; }

/* Account → Your Data */
.account-data-actions {
    display: flex;
    gap: var(--space-3);
    flex-wrap: wrap;
}

.account-danger-zone {
    margin-top: var(--space-4);
    padding: var(--space-4) var(--space-5);
    background: rgba(220,38,38,0.06);
    border: 1px solid rgba(220,38,38,0.25);
    border-radius: var(--radius);
}

.account-danger-zone p {
    color: rgba(255,255,255,0.7);
    font-size: var(--fs-sm);
    line-height: 1.6;
    margin-bottom: var(--space-4);
}

.account-danger-zone__warning { color: #f87171 !important; }

@media (max-width: 479px) {
    .account-data-actions { flex-direction: column; }
    .account-data-actions .btn { width: 100%; justify-content: center; }
}

.booking-nav {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: var(--space-8);
    padding-top: var(--space-6);
    border-top: 1px solid var(--border-dark);
    gap: var(--space-4);
    flex-wrap: wrap;
}

/* Back and Start over travel together, so the primary action keeps the right-hand
   end of the row to itself and "Start over" never reads as the way forward. */
.booking-nav__left {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-wrap: wrap;
}

/* Says the booking cap has been reached before any of the wizard is filled in, so
   it can't be discovered at the Confirm click. Amber rather than red: nothing has
   gone wrong, there's just a limit and a way past it. */
.booking-limit-notice {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-3);
    margin-bottom: var(--space-6);
    padding: var(--space-5);
    background: rgba(234,179,8,0.08);
    border: 1px solid rgba(234,179,8,0.28);
    border-radius: var(--radius);
}

.booking-limit-notice h2 {
    margin: 0;
    font-size: var(--fs-lg);
    color: #fbbf24;
}

.booking-limit-notice p {
    margin: 0;
    color: rgba(255,255,255,0.72);
    line-height: 1.55;
}

/* ---------- Customer Auth Strip ---------- */
.customer-auth-strip {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-wrap: wrap;
    background: var(--dark-3);
    border: 1px solid var(--border-dark);
    border-radius: .5rem;
    padding: var(--space-3) var(--space-4);
    margin-bottom: var(--space-6);
    font-size: var(--fs-body-sm);
    color: rgba(255,255,255,0.65);
}
.customer-auth-strip strong { color: var(--white); }
.customer-auth-strip__link {
    background: none;
    border: none;
    color: var(--green);
    font-size: var(--fs-body-sm);
    font-weight: 600;
    cursor: pointer;
    padding: 0;
}
@media (hover: hover) and (pointer: fine) {
    .customer-auth-strip__link:hover { color: var(--green-dark); text-decoration: underline; }
}

/* ---------- Service Picker ---------- */
.service-category-group {
    margin-bottom: var(--space-8);
}

.service-category-label {
    font-size: var(--fs-caption);
    text-transform: uppercase;
    letter-spacing: 2px;
    color: var(--green);
    font-weight: 700;
    margin-bottom: var(--space-3);
}

.service-pick-grid {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

/* A <button> since the a11y pass — width/text-align/color are reset explicitly
   because a button doesn't inherit them the way the old <div> did. */
.service-pick-card {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    width: 100%;
    text-align: left;
    color: inherit;
    font-family: inherit;
    padding: var(--space-4) var(--space-5);
    background: var(--dark-3);
    border: 1.5px solid var(--border-dark);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: all 0.2s;
    position: relative;
}

.service-pick-card:focus-visible,
.cal-day:focus-visible,
.timeslot:focus-visible {
    outline: 2px solid var(--green);
    outline-offset: 2px;
}

/* An unavailable day is a disabled <button> now, so the cursor and pointer-events
   follow from the attribute rather than from a handler that quietly did nothing. */
.cal-day:disabled {
    cursor: default;
}

@media (hover: hover) and (pointer: fine) {
    .service-pick-card:hover {
        border-color: var(--green);
        background: var(--dark-4);
    }
}

.service-pick-card.selected {
    border-color: var(--green);
    background: rgba(61,187,72,0.08);
}

.service-pick-card__icon {
    font-size: 1.3rem;
    flex-shrink: 0;
    width: 36px;
    text-align: center;
}

.service-pick-card__info {
    flex: 1;
    min-width: 0;
}

.service-pick-card__info strong {
    display: block;
    font-size: var(--fs-body);
    color: var(--white);
}

.service-pick-card__info span {
    font-size: var(--fs-caption);
    color: rgba(255,255,255,0.45);
}

.service-pick-card__price {
    font-size: var(--fs-sm);
    font-weight: 700;
    color: var(--green);
    white-space: nowrap;
}

.service-pick-card__check {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--green);
    color: var(--white);
    font-size: var(--fs-tiny);
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

/* ---------- Calendar ---------- */
.schedule-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-8);
}

.calendar-wrapper {
    flex: 1;
}

.cal-nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: var(--space-4);
}

.cal-nav__title {
    font-weight: 700;
    font-size: 1rem;
    color: var(--white);
}

/* line-height: 1 matters — flex centres the line box, not the glyph inside it,
   so the UA's default `normal` line-height left the arrows sitting visibly high
   in the square. The icons are inline SVG rather than text arrows for the same
   reason: a symmetric viewBox centres exactly, where U+2190/2192 are drawn on
   the font's math axis and shift with whatever fallback face resolves them. */
.cal-nav__btn {
    background: var(--dark-3);
    border: 1px solid var(--border-dark);
    color: var(--white);
    width: 34px;
    height: 34px;
    border-radius: 8px;
    cursor: pointer;
    font-size: 1rem;
    line-height: 1;
    transition: background-color 0.2s, border-color 0.2s, color 0.2s;
    display: flex;
    align-items: center;
    justify-content: center;
}

.cal-nav__btn svg {
    width: 16px;
    height: 16px;
    display: block;
}

@media (hover: hover) and (pointer: fine) {
    .cal-nav__btn:hover:not(:disabled) { background: var(--green); border-color: var(--green); }
}
.cal-nav__btn:disabled { opacity: 0.3; cursor: not-allowed; }

.cal-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: var(--space-1);
}

.cal-head {
    text-align: center;
    font-size: var(--fs-2xs);
    font-weight: 700;
    text-transform: uppercase;
    color: rgba(255,255,255,0.35);
    padding: var(--space-2) 0;
}

.cal-day {
    aspect-ratio: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 8px;
    font-size: var(--fs-xs);
    font-weight: 500;
    transition: all 0.15s;
    cursor: default;
    /* Button resets — these are <button> elements since the a11y pass. */
    width: 100%;
    padding: 0;
    border: none;
    background: transparent;
    font-family: inherit;
    color: inherit;
}

.cal-day--empty { background: transparent; }

.cal-day--disabled {
    color: rgba(255,255,255,0.2);
    background: transparent;
}

/* Shut, as opposed to merely full. A struck-through number distinguishes the two at
   a glance — "come back tomorrow" versus "try another time today" — without adding
   colour the calendar doesn't otherwise use. The reason itself is on the title and
   the aria-label, so it reaches a screen reader too. */
.cal-day--closed {
    text-decoration: line-through;
    text-decoration-thickness: 1px;
    color: rgba(255,255,255,0.28);
    cursor: not-allowed;
}

.cal-day--available {
    color: var(--white);
    background: var(--dark-3);
    cursor: pointer;
}

@media (hover: hover) and (pointer: fine) {
    .cal-day--available:hover { background: var(--green-glow); color: var(--green); }
}

.cal-day--today {
    border: 1.5px solid rgba(255,255,255,0.2);
}

.cal-day--selected {
    background: var(--green) !important;
    color: var(--dark) !important;
    font-weight: 700;
}

.cal-legend {
    display: flex;
    gap: var(--space-4);
    margin-top: var(--space-3);
    font-size: var(--fs-tiny);
    color: rgba(255,255,255,0.4);
    align-items: center;
    flex-wrap: wrap;
}

.cal-legend__dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    display: inline-block;
    margin-right: var(--space-1);
}

.cal-legend__dot--available { background: var(--dark-4); border: 1px solid var(--border-dark); }
.cal-legend__dot--selected { background: var(--green); }
.cal-legend__dot--disabled { background: transparent; border: 1px solid var(--border-dark); opacity: 0.3; }

/* The legend's "closed" marker mirrors the day itself rather than being a dot — a
   struck-through swatch is what the calendar actually shows. */
.cal-legend__closed {
    text-decoration: line-through;
    text-decoration-thickness: 1px;
    color: rgba(255,255,255,0.4);
    margin-right: var(--space-1);
}

/* ---------- Time Slots ---------- */
.timeslot-wrapper h4 {
    margin-bottom: var(--space-4);
    font-size: 1rem;
    color: var(--white);
}

.timeslot-prompt {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-3);
    padding: var(--space-12) var(--space-4);
    color: rgba(255,255,255,0.3);
    text-align: center;
    background: var(--dark-3);
    border-radius: var(--radius);
    border: 1px dashed var(--border-dark);
}

.timeslot-prompt span { font-size: 2rem; }
.timeslot-prompt p { font-size: var(--fs-sm); margin: 0; }

.timeslot-none {
    color: rgba(255,255,255,0.4);
    font-size: var(--fs-sm);
    padding: var(--space-6);
    text-align: center;
    background: var(--dark-3);
    border-radius: var(--radius-sm);
}

.timeslot-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-3);
}

.timeslot {
    padding: var(--space-3) var(--space-2);
    text-align: center;
    background: var(--dark-3);
    border: 1.5px solid var(--border-dark);
    border-radius: var(--radius-sm);
    font-size: var(--fs-body-sm);
    font-weight: 600;
    color: var(--white);
    cursor: pointer;
    transition: all 0.15s;
    /* Button reset — a <button> since the a11y pass. */
    width: 100%;
    font-family: inherit;
}

@media (hover: hover) and (pointer: fine) {
    .timeslot:hover { border-color: var(--green); color: var(--green); }
}

.timeslot--selected {
    background: var(--green) !important;
    border-color: var(--green) !important;
    color: var(--dark) !important;
}

/* ---------- Details Form ---------- */
.details-form { display: flex; flex-direction: column; gap: 0; }

.bike-chip-row { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-3); }

.bike-chip {
    padding: var(--space-2) var(--space-3);
    background: var(--dark-3);
    border: 1px solid var(--border-dark);
    border-radius: 50px;
    color: rgba(255,255,255,0.65);
    font-size: var(--fs-caption);
    font-weight: 500;
    cursor: pointer;
    transition: all 0.15s;
}

@media (hover: hover) and (pointer: fine) {
    .bike-chip:hover { border-color: var(--green); color: var(--white); }
}
.bike-chip--selected { background: var(--green); border-color: var(--green); color: var(--dark); font-weight: 700; }

.photo-picker-list {
    list-style: none;
    margin: var(--space-3) 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.photo-picker-list li {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3);
    background: var(--dark-3);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
    font-size: var(--fs-xs);
}

.photo-picker-remove {
    background: none;
    border: none;
    color: rgba(255,255,255,0.5);
    font-size: 1.1rem;
    line-height: 1;
    cursor: pointer;
    padding: 0 var(--space-1);
}

@media (hover: hover) and (pointer: fine) {
    .photo-picker-remove:hover { color: var(--white); }
}

/* ---------- Review ---------- */
.review-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 0;
    background: var(--dark-3);
    border-radius: var(--radius);
    border: 1px solid var(--border-dark);
    overflow: hidden;
    margin-bottom: var(--space-5);
}

.review-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: var(--space-3) var(--space-5);
    border-bottom: 1px solid var(--border-dark);
    gap: var(--space-4);
}

.review-item:last-child { border-bottom: none; }

.review-label {
    font-size: var(--fs-caption);
    color: rgba(255,255,255,0.45);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    flex-shrink: 0;
}

.review-value {
    font-weight: 600;
    font-size: var(--fs-body-sm);
    text-align: right;
}

.review-value--green { color: var(--green); }

.review-note {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    background: rgba(61,187,72,0.07);
    border: 1px solid var(--green-glow);
    border-radius: var(--radius-sm);
    font-size: var(--fs-caption);
    color: rgba(255,255,255,0.55);
    margin-bottom: var(--space-2);
}

/* ---------- Confirmed ---------- */
.booking-confirmed {
    text-align: center;
    max-width: 560px;
    margin: 0 auto;
    padding: var(--space-4) 0 var(--space-8);
}

.confirmed-icon {
    width: 72px;
    height: 72px;
    border-radius: 50%;
    background: var(--green);
    color: var(--dark);
    font-size: 2rem;
    font-weight: 900;
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0 auto var(--space-6);
    box-shadow: 0 0 0 8px rgba(61,187,72,0.15);
}

.confirmed-subtitle {
    color: rgba(255,255,255,0.55);
    margin-bottom: var(--space-8);
}

.confirmed-ref {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-1);
    background: var(--dark-3);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
    padding: var(--space-4) var(--space-8);
    margin-bottom: var(--space-8);
}

.confirmed-ref span { font-size: var(--fs-tiny); color: rgba(255,255,255,0.4); text-transform: uppercase; letter-spacing: 1px; }
.confirmed-ref strong { font-size: 1.3rem; color: var(--green); letter-spacing: 1px; }

.confirmed-summary {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    background: var(--dark-3);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
    padding: var(--space-5);
    margin-bottom: var(--space-8);
    text-align: left;
}

.confirmed-row {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    font-size: 1.2rem;
}

.confirmed-row > span { flex-shrink: 0; }

.confirmed-row div {
    display: flex;
    flex-direction: column;
}

.confirmed-row div strong { font-size: var(--fs-body-sm); color: var(--white); }
.confirmed-row div span { font-size: var(--fs-caption); color: rgba(255,255,255,0.45); }

.confirmed-actions {
    display: flex;
    justify-content: center;
    gap: var(--space-4);
    flex-wrap: wrap;
}

/* ---------- Admin Login ---------- */
.admin-login {
    min-height: calc(100svh - var(--nav-h));
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-8) var(--space-5);
    background: var(--dark);
}

.admin-login__card {
    background: var(--dark-2);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
    padding: var(--space-10);
    width: 100%;
    max-width: 420px;
    text-align: center;
}

.admin-login__logo {
    display: flex;
    justify-content: center;
    margin-bottom: var(--space-6);
}
.admin-logo-img { height: 64px; width: auto; display: block; margin: 0 auto; mix-blend-mode: screen; }

.admin-login__card h2 { margin-bottom: var(--space-2); }
.admin-login__card p { color: rgba(255,255,255,0.5); margin-bottom: var(--space-6); font-size: var(--fs-body-sm); }
.admin-login__card .form-group { text-align: left; margin-bottom: var(--space-5); }

.remember-row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    text-align: left;
    margin-bottom: var(--space-5);
    color: rgba(255,255,255,0.7);
    font-size: var(--fs-body-sm);
    cursor: pointer;
}
.remember-row input { width: auto; margin: 0; }

.auth-alt { margin-top: var(--space-5); margin-bottom: 0; font-size: var(--fs-body-sm); color: rgba(255,255,255,0.5); }
.auth-alt a { color: var(--green); font-weight: 600; }

/* ---------- Admin Dashboard ---------- */
/* Tighter than the public page heroes so the dashboard header takes ~50px less
   vertical space (and stays compact on phones). */
.page-hero--admin { padding-top: var(--space-8); padding-bottom: var(--space-6); }
.admin-section { padding-top: var(--space-8); }

@media (max-width: 479px) {
    .page-hero--admin { padding-top: var(--space-6); padding-bottom: var(--space-5); }
    .admin-section { padding-top: var(--space-5); }

    /* Booking card: reduce padding */
    .booking-card { padding: var(--space-5); }

    /* Step indicator: shrink dots and hide labels on tiny screens */
    .step-indicator__dot { width: 28px; height: 28px; font-size: var(--fs-tiny); }
    .step-indicator__label { display: none; }
    .step-indicator__line { margin-bottom: 0; min-width: 16px; }
    .steps-indicator { margin-bottom: var(--space-8); }

    /* Booking nav buttons: stack on tiny screens */
    .booking-nav { flex-direction: column-reverse; }
    .booking-nav .btn { width: 100%; justify-content: center; }
    .booking-nav > span { display: none; }
    /* The grouped Back/Start over pair stacks too, rather than trying to sit two
       full-width buttons side by side. */
    .booking-nav__left { width: 100%; flex-direction: column; gap: var(--space-2); }

    /* Calendar: tighten up */
    .cal-day { font-size: var(--fs-caption); border-radius: var(--radius-sm); }
    /* Was 0.6rem — the smallest text anywhere on the site, for the one row that
       has to stay readable at arm's length while you pick a date. The weekday
       initials are two characters, so --fs-2xs still fits the cell at 320px. */
    .cal-head { font-size: var(--fs-2xs); }

    /* Service picker: reduce padding */
    .service-pick-card { padding: var(--space-3); }

    /* Review items: allow wrapping */
    .review-item { flex-wrap: wrap; gap: var(--space-1); }
    .review-value { text-align: left; }

    /* Confirmed page */
    .confirmed-ref { padding: var(--space-3) var(--space-5); }
    .confirmed-ref strong { font-size: 1.1rem; }
    .confirmed-actions { flex-direction: column; align-items: center; }
    .confirmed-actions .btn { width: 100%; max-width: 320px; justify-content: center; }
}

/* Small utilities for the admin dashboard markup — replace repeated inline
   styles in Admin.razor rather than hand-writing the same declarations per
   element. */
.text-center { text-align: center; }
.text-muted-sm { font-size: var(--fs-xs); color: rgba(255,255,255,0.45); }
.text-faint-sm { font-size: var(--fs-xs); color: rgba(255,255,255,0.35); }

.admin-header-actions {
    display: flex;
    align-items: center;
    gap: var(--space-4);
}
.admin-header-actions__name {
    color: rgba(255,255,255,0.7);
    font-size: var(--fs-sm);
}
.admin-header-actions__name .status-badge { font-size: var(--fs-tiny); }

.form-control--compact { font-size: var(--fs-xs); padding: var(--space-1) var(--space-2); }
.form-control--price   { font-size: var(--fs-sm); padding: var(--space-1) var(--space-2); width: 100px; }
.form-control--assign  { min-width: 130px; }
.admin-subheading      { font-size: var(--fs-h3-compact); margin: var(--space-8) 0 var(--space-4); }

/* Replaces repeated inline styles in Admin.razor. */
.admin-inline-error    { margin-bottom: var(--space-3); }
.admin-checkbox-label  { margin-left: var(--space-1); }
.price-current         { font-weight: 700; color: var(--green); }

/* Pricing table column widths, previously inline on the <th>. */
/* Enough for the primary button plus two icon buttons and the menu toggle on one
   line — otherwise the cell squeezes and the set wraps. */
.admin-table .col-actions     { width: 1%; white-space: nowrap; }
.admin-table .col-price       { width: 130px; }
.admin-table .col-price-input { width: 140px; }
.admin-table .col-save        { width: 80px; }

.tab-bar-wrapper { margin-bottom: var(--space-6); }

.pricing-tab-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: var(--space-4);
    flex-wrap: wrap;
    gap: var(--space-3);
}
.pricing-tab-header h3 { margin: 0; }

/* Below 768px the sticky bar at the foot of the list provides Save All, so this one
   is a second button doing the same job a few pixels away. */
@media (max-width: 767px) {
    .pricing-tab-header .btn { display: none; }
}

.admin-stats {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-4);
    margin-bottom: var(--space-6);
}

.admin-stat {
    background: var(--dark-2);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
    padding: var(--space-5);
    text-align: center;
}

.admin-stat--highlight {
    border-color: var(--green);
    background: rgba(61,187,72,0.05);
}

.admin-stat__number {
    font-size: 2.2rem;
    font-weight: 900;
    color: var(--green);
    line-height: 1;
    margin-bottom: var(--space-1);
}

.admin-stat__label {
    font-size: var(--fs-tiny);
    text-transform: uppercase;
    letter-spacing: 1px;
    color: rgba(255,255,255,0.4);
}

/* Filters */
.admin-filters {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    margin-bottom: var(--space-6);
}

/* The sort picker sits beside the search box. Fixed width so it doesn't stretch to
   fill the row on desktop and dwarf the thing being sorted. */
.admin-sort { flex-shrink: 0; }
.admin-sort .form-control { width: 100%; }

@media (min-width: 640px) {
    .admin-sort .form-control { width: auto; min-width: 11rem; }
}

.admin-filter-scroll {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding-bottom: var(--space-05);
}
.admin-filter-scroll::-webkit-scrollbar { display: none; }

.admin-filter-tabs {
    display: flex;
    gap: var(--space-1);
    flex-wrap: nowrap;
    width: max-content;
}

.filter-tab {
    padding: var(--space-2) var(--space-3);
    background: var(--dark-3);
    border: 1px solid var(--border-dark);
    border-radius: 50px;
    color: rgba(255,255,255,0.6);
    font-size: var(--fs-caption);
    font-weight: 500;
    cursor: pointer;
    /* Named properties, not `all`. The active state used to also change font-weight,
       and `all` happily animated the resulting width change — so selecting a tab made
       it grow by ~3px over 150ms and shunt every tab after it sideways. Listing the
       properties means a future style that affects layout can't quietly become an
       animation. */
    transition: background-color 0.15s, border-color 0.15s, color 0.15s;
    white-space: nowrap;
}

@media (hover: hover) and (pointer: fine) {
    .filter-tab:hover { border-color: var(--green); color: var(--white); }
}

/* Deliberately no font-weight change. Bold text is wider, so it resized the button
   and shifted its neighbours every time the selection moved. The weight was
   redundant anyway: a solid green pill with near-black text on a dark bar is already
   about as loud as an active state gets. */
.filter-tab.active { background: var(--green); border-color: var(--green); color: var(--dark); }

.admin-search { width: 100%; }
.admin-search .form-control { font-size: var(--fs-sm); }

/* Today schedule */
.today-schedule {
    background: var(--dark-2);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
    padding: var(--space-5);
    margin-bottom: var(--space-6);
}

.today-schedule h3 { font-size: var(--fs-h3-compact); margin-bottom: var(--space-4); }

.today-slots { display: flex; flex-direction: column; gap: var(--space-2); }

.today-slot {
    display: grid;
    grid-template-columns: 48px 1fr auto;
    align-items: center;
    gap: var(--space-1) var(--space-3);
    padding: var(--space-3);
    background: var(--dark-3);
    border-radius: var(--radius-sm);
    font-size: var(--fs-sm);
}

.today-slot__time { font-weight: 700; color: var(--green); }
.today-slot__name { font-weight: 600; }
.today-slot__service {
    color: rgba(255,255,255,0.55);
    font-size: var(--fs-caption);
    grid-column: 2;
}

/* Table */
.admin-table-wrapper { overflow-x: auto; border-radius: var(--radius); border: 1px solid var(--border-dark); }

.admin-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--fs-sm);
}

.admin-table thead tr {
    background: var(--dark-3);
    border-bottom: 1px solid var(--border-dark);
}

.admin-table th {
    padding: var(--space-3) var(--space-4);
    text-align: left;
    font-size: var(--fs-2xs);
    text-transform: uppercase;
    letter-spacing: 1px;
    color: rgba(255,255,255,0.45);
    white-space: nowrap;
}

.admin-table tbody tr {
    background: var(--dark-2);
    border-bottom: 1px solid var(--border-dark);
    cursor: pointer;
    transition: background 0.15s;
}

.admin-table tbody tr:last-child { border-bottom: none; }
/* An expanded row stays highlighted on touch too — it marks which row is open,
   which is state rather than hover feedback. Only the hover half is gated. */
.admin-table tbody tr.row-expanded { background: var(--dark-3); }

@media (hover: hover) and (pointer: fine) {
    .admin-table tbody tr:hover { background: var(--dark-3); }
}

.admin-table td { padding: var(--space-3) var(--space-4); vertical-align: middle; }

.row-detail td { background: rgba(61,187,72,0.04); padding: 0; }

.booking-detail {
    padding: var(--space-4);
    border-top: 1px solid var(--border-dark);
}

.booking-detail__grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-3) var(--space-8);
}

.booking-detail__grid > div {
    display: flex;
    flex-direction: column;
    gap: var(--space-05);
}

.booking-detail__grid strong { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.5px; color: rgba(255,255,255,0.35); }
.booking-detail__grid span { font-size: var(--fs-sm); color: rgba(255,255,255,0.75); }

.booking-detail__photos { margin-top: var(--space-3); }
.booking-detail__photos strong {
    display: block;
    margin-bottom: var(--space-1);
    font-size: var(--fs-2xs);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: rgba(255,255,255,0.35);
}

.admin-photo-thumbs {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-1);
}

.admin-photo-thumb {
    width: 56px;
    height: 56px;
    object-fit: cover;
    border-radius: var(--radius);
    border: 1px solid var(--border-dark);
}

/* Table cells */
.ref-badge {
    font-size: var(--fs-tiny);
    font-family: monospace;
    background: var(--dark-3);
    border: 1px solid var(--border-dark);
    padding: var(--space-1) var(--space-2);
    border-radius: 4px;
    white-space: nowrap;
    color: var(--green);
}

.customer-cell, .service-cell, .date-cell {
    display: flex;
    flex-direction: column;
    gap: var(--space-05);
}

.customer-cell strong, .service-cell strong, .date-cell strong { color: var(--white); font-size: var(--fs-sm); }
.customer-cell span, .service-cell span, .date-cell span { font-size: var(--fs-tiny); color: rgba(255,255,255,0.4); }

/* Status badges */
.status-badge {
    display: inline-block;
    padding: var(--space-1) var(--space-3);
    border-radius: 50px;
    font-size: var(--fs-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    white-space: nowrap;
}

.status-badge--pending    { background: rgba(234,179,8,0.15);  color: #eab308; border: 1px solid rgba(234,179,8,0.3);  }
.status-badge--confirmed  { background: rgba(61,187,72,0.15);  color: var(--green); border: 1px solid rgba(61,187,72,0.3); }
.status-badge--inprogress { background: rgba(59,130,246,0.15); color: #60a5fa; border: 1px solid rgba(59,130,246,0.3); }
.status-badge--completed  { background: rgba(107,114,128,0.15);color: #9ca3af; border: 1px solid rgba(107,114,128,0.3);}
.status-badge--cancelled  { background: rgba(220,38,38,0.12);  color: #f87171; border: 1px solid rgba(220,38,38,0.25); }
/* Not a booking status — a derived "the slot has passed and the bike isn't in".
   Amber rather than red on purpose: most late arrivals turn up, so this is a prompt
   to ring them, not a verdict. Distinct from --pending, which is a real status. */
.status-badge--late       { background: rgba(249,115,22,0.15); color: #fb923c; border: 1px solid rgba(249,115,22,0.35); }

/* Action buttons */
.action-buttons { display: flex; gap: var(--space-2); flex-wrap: wrap; }

.action-btn {
    /* inline-flex + inherited font + fixed line-height so <button> and <a>
       action buttons render at an identical height everywhere (the calendar
       row centres them, so any intrinsic size difference would show). */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-1) var(--space-3);
    min-height: 28px;
    border-radius: 6px;
    font-family: inherit;
    font-size: var(--fs-tiny);
    font-weight: 600;
    line-height: 1.35;
    cursor: pointer;
    /* Every colour below comes from a --variant class, so a button wearing none of
       them used to fall through to the user agent's button chrome — a white pill on
       the dark card. These two make the base neutral instead of undefined. */
    background: rgba(255,255,255,0.06);
    color: rgba(255,255,255,0.75);
    border: 1px solid transparent;
    transition: all 0.15s;
    white-space: nowrap;
    gap: var(--space-1);
}

/* The one primary action on a booking. Confirm / Start work / Mark complete were
   three separately-coloured buttons for what is one control — they were already
   mutually exclusive, so the colour change only ever signalled "the button moved". */
.action-btn--primary { background: rgba(61,187,72,0.15); color: var(--green); border-color: rgba(61,187,72,0.3); }
@media (hover: hover) and (pointer: fine) {
    .action-btn--primary:hover { background: var(--green); color: var(--dark); }
}

/* Confirming a destructive step, at the point of confirming it — never as the
   resting state of a button that merely opens the confirmation. */
.action-btn--danger { background: rgba(220,38,38,0.12); color: #f87171; border-color: rgba(220,38,38,0.3); }
@media (hover: hover) and (pointer: fine) {
    .action-btn--danger:hover { background: #dc2626; color: var(--white); }
}

/* Opening the cancel confirmation — quiet red, so it reads as destructive without
   claiming to be the destruction itself. --danger stays reserved for the button that
   actually fires inside the confirmation, and the two sit next to each other often
   enough that the difference in weight is the whole point. */
.action-btn--cancel {
    background: rgba(220,38,38,0.08);
    color: #f87171;
    border-color: rgba(220,38,38,0.22);
}
@media (hover: hover) and (pointer: fine) {
    .action-btn--cancel:hover { background: rgba(220,38,38,0.2); border-color: rgba(220,38,38,0.45); }
}

/* Icons inside action buttons sit beside a label, so they take a touch more than the
   1em the base .icon rule gives them or they read as a smudge next to 600-weight text. */
.action-btn .icon { width: 1.05em; height: 1.05em; }


/* An empty result inside a table that stays on the page, rather than in place of it.
   Matches .admin-empty so the phone and desktop views read the same. */
.admin-table__empty-row td {
    padding: var(--space-12) var(--space-4);
    text-align: center;
    color: rgba(255,255,255,0.35);
    font-size: var(--fs-body-sm);
}

.admin-table__empty-row:hover td { background: none; }

.admin-empty {
    padding: var(--space-12);
    text-align: center;
    color: rgba(255,255,255,0.35);
    font-size: var(--fs-body-sm);
}

/* Staff management / Pricing tab */
.staff-mgmt { margin-top: var(--space-2); }
.staff-add-card {
    background: var(--dark-2);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
    padding: var(--space-6);
    margin-bottom: var(--space-6);
}
.staff-add-card h3 { margin: 0 0 var(--space-4); font-size: var(--fs-h3-compact); color: var(--white); }

/* ── Availability tab ────────────────────────────────────────────────────────
   Closures and staff absence, side by side above 900px. They're read together —
   "are we shut, and who's in" is one question — but each is self-contained, so on a
   phone they stack rather than interleave. */
.availability-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-6);
}

@media (min-width: 900px) {
    .availability-grid { grid-template-columns: 1fr 1fr; align-items: start; }
}

.availability-panel {
    background: var(--dark-2);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
    padding: var(--space-6);
}

.availability-panel h3 { margin: 0 0 var(--space-2); font-size: var(--fs-h3-compact); color: var(--white); }
.availability-panel > .text-muted-sm { display: block; margin-bottom: var(--space-4); }

/* The add form, separated from the list below it by a rule rather than only whitespace —
   they're both lists of fields otherwise, and ran together. */
.availability-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding-bottom: var(--space-5);
    margin-bottom: var(--space-5);
    border-bottom: 1px solid var(--border-dark);
}

.availability-form .form-group { margin: 0; }
.availability-form .btn { align-self: flex-start; }

.availability-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.availability-list__item {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-3);
    background: var(--dark-3);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius-sm);
    padding: var(--space-3) var(--space-4);
}

.availability-list__item strong { color: var(--white); }

/* Bookings a closure has just stranded. Amber, above both panels, and deliberately
   loud: this is the screen's real job — the shop said "close that week", and the
   thing that matters next is the people who had booked into it. */
.availability-affected {
    background: rgba(234,179,8,0.08);
    border: 1px solid rgba(234,179,8,0.35);
    border-radius: var(--radius);
    padding: var(--space-5);
    margin-bottom: var(--space-6);
}

.availability-affected h3 {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin: 0 0 var(--space-2);
    font-size: var(--fs-h3-compact);
    color: #eab308;
}

.availability-affected > .text-muted-sm { display: block; margin-bottom: var(--space-4); }

.availability-affected__row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    background: var(--dark-2);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius-sm);
    padding: var(--space-3) var(--space-4);
}

.availability-affected__row + .availability-affected__row { margin-top: var(--space-2); }

.availability-affected__info {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    min-width: 0;
}

/* Wraps rather than squeezing the inputs to nothing on a narrow window — the date
   and time pickers have a floor below which they stop being usable. */
.availability-affected__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
}

.availability-affected__actions .form-control { width: auto; min-width: 8.5rem; }

/* Admin utility: success banner & info banner */
.admin-success {
    background: rgba(61,187,72,0.10);
    border: 1px solid rgba(61,187,72,0.3);
    color: var(--green);
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-sm);
    margin-bottom: var(--space-6);
    font-size: var(--fs-sm);
}

/* Amber counterpart to .admin-success — something that needs attention but isn't an
   error, like nobody being marked as a mechanic. */
.admin-inline-warning {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    background: rgba(234,179,8,0.10);
    border: 1px solid rgba(234,179,8,0.3);
    color: #eab308;
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-sm);
    margin-bottom: var(--space-6);
    font-size: var(--fs-sm);
}

.admin-inline-warning strong { color: inherit; }

/* Why the day isn't bookable, above the slot list on the Calendar tab. Wraps rather
   than truncates: the closure reason is the whole point of it. */
.day-cal__notice {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2);
    background: rgba(234,179,8,0.10);
    border: 1px solid rgba(234,179,8,0.3);
    color: #eab308;
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-sm);
    margin-bottom: var(--space-4);
    font-size: var(--fs-sm);
}

.pricing-info-banner {
    background: var(--dark-3);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius-sm);
    padding: var(--space-3) var(--space-4);
    margin-bottom: var(--space-6);
    font-size: var(--fs-sm);
    color: rgba(255,255,255,0.5);
}
.pricing-info-banner strong { color: var(--white); }

/* ============================================================
   ADMIN CARD VIEW (mobile only)
   ============================================================ */

/* Mobile: show cards, hide the table.
   The hide is on the --desktop-only modifier, not on .admin-table-wrapper itself.
   It used to apply to every wrapper, but only the bookings table had a card
   fallback — so the Pricing and Staff tabs, which reuse the wrapper, rendered a
   completely blank page on every phone. All three now have card layouts, and a
   wrapper only opts into being hidden when one exists. */
.admin-cards {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.admin-table-wrapper--desktop-only { display: none; }

.admin-card {
    background: var(--dark-2);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
    overflow: hidden;
    cursor: pointer;
    transition: border-color 0.2s;
}

.admin-card--open { border-color: rgba(61,187,72,0.4); }
@media (hover: hover) and (pointer: fine) {
    .admin-card:hover { border-color: var(--border-dark); }
    .admin-card--open:hover { border-color: rgba(61,187,72,0.4); }
}

.admin-card__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-3) var(--space-4) var(--space-2);
    gap: var(--space-2);
}

.admin-card__header-right {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.admin-card__chevron {
    font-size: 1.3rem;
    color: rgba(255,255,255,0.3);
    line-height: 1;
    transition: transform 0.2s;
    display: inline-block;
}

.admin-card__chevron.open { transform: rotate(90deg); color: var(--green); }

/* An enquiry card has nothing to expand — everything it holds is already on it —
   so it doesn't offer the pointer the bookings cards do. */
.admin-card--static { cursor: default; }

/* Handled ones stay readable but stop competing with the ones still outstanding. */
.admin-card--handled { opacity: 0.6; }

.admin-card__name {
    padding: 0 var(--space-4) var(--space-1);
    font-weight: 700;
    font-size: var(--fs-body);
    color: var(--white);
}

.admin-card__meta {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0 var(--space-4) var(--space-3);
    gap: var(--space-2);
    flex-wrap: wrap;
}

.admin-card__meta span:first-child { font-size: var(--fs-xs); color: rgba(255,255,255,0.6); }
.admin-card__meta span:last-child  { font-size: var(--fs-caption); color: rgba(255,255,255,0.4); }

.admin-card__detail {
    border-top: 1px solid var(--border-dark);
    padding: var(--space-3) var(--space-4);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    background: rgba(61,187,72,0.03);
}

.admin-card__detail-row {
    display: flex;
    gap: var(--space-3);
    font-size: var(--fs-caption);
}

/* Direct child, not any descendant: as `span:first-child` this also matched the
   first <span> inside any nested markup in the value column, which uppercased and
   shrank the first job note on every expanded booking. */
.admin-card__detail-row > span:first-child {
    color: rgba(255,255,255,0.35);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    font-size: var(--fs-2xs);
    min-width: 48px;
    flex-shrink: 0;
    padding-top: var(--space-05);
}

/* Direct child for the same reason as the rule above. */
.admin-card__detail-row > span:last-child {
    color: rgba(255,255,255,0.75);
    word-break: break-word;
}

.admin-card__actions {
    display: flex;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4);
    border-top: 1px solid var(--border-dark);
    background: var(--dark-3);
    flex-wrap: wrap;
}

/* Admin hero row */
.admin-hero-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-4);
}

/* ============================================================
   RESPONSIVE
   ============================================================ */

@media (min-width: 480px) {
    .timeslot-grid { grid-template-columns: repeat(3, 1fr); }
    .admin-stats { grid-template-columns: repeat(4, 1fr); }
}

@media (min-width: 640px) {
    .service-pick-grid { display: grid; grid-template-columns: repeat(2, 1fr); }
    .schedule-grid { grid-template-columns: 1fr 1fr; }
    .booking-detail__grid { grid-template-columns: repeat(4, 1fr); }
}

/* At 768px+ switch to table view */
@media (min-width: 768px) {
    .timeslot-grid { grid-template-columns: repeat(4, 1fr); }
    .booking-card { padding: var(--space-10); }

    /* Show tables, hide the bookings card list. (.admin-mobile-cards is hidden
       next to its own definition further down — see the note there.) */
    .admin-cards { display: none; }
    .admin-table-wrapper--desktop-only { display: block; }

    /* Filters: horizontal row */
    .admin-filters {
        flex-direction: row;
        align-items: center;
    }
    .admin-filter-scroll { flex: 1; }
    .admin-search { width: auto; min-width: 220px; flex: 0 1 260px; }
}

/* ============================================================
   ADMIN PRICING & STAFF — mobile card layouts
   ------------------------------------------------------------
   Below 768px the Pricing and Staff tables are replaced by these, rather than
   hidden with nothing behind them (which is what used to happen). Hidden at
   768px+ by the .admin-mobile-cards rule in the breakpoint block above.
   ============================================================ */
.admin-mobile-cards {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

/* The hide has to live here, immediately after the rule it overrides, rather than
   with the other breakpoint rules further up the file: this block is defined below
   them, and at equal specificity the later declaration wins regardless of the media
   query. Putting it up there left the cards AND the table both rendering at 768px+,
   so the Pricing and Staff tabs showed every row twice on desktop. */
@media (min-width: 768px) {
    .admin-mobile-cards { display: none; }
}

.admin-edit-card {
    background: var(--dark-2);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
    padding: var(--space-5);
}

.admin-edit-card__title {
    font-weight: 700;
    font-size: var(--fs-body);
    color: var(--white);
    margin-bottom: var(--space-05);
}

.admin-edit-card__meta {
    font-size: var(--fs-xs);
    color: rgba(255,255,255,0.45);
    margin-bottom: var(--space-3);
}

/* Label and control on one line, wrapping to two when the label is long. */
.admin-edit-card__field {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    flex-wrap: wrap;
    padding: var(--space-2) 0;
    border-top: 1px solid var(--border-dark);
}

.admin-edit-card__field:first-of-type { border-top: none; }

.admin-edit-card__field > span {
    font-size: var(--fs-sm);
    color: rgba(255,255,255,0.6);
}

.admin-edit-card__field .form-control { width: auto; min-width: 7rem; }

.admin-edit-card__actions {
    display: flex;
    gap: var(--space-2);
    margin-top: var(--space-3);
}

.admin-edit-card__actions .btn,
.admin-edit-card__actions .action-btn { flex: 1; justify-content: center; }

/* A checkbox big enough to hit. The bare 13px checkboxes in the staff table were
   unusable on touch. */
.admin-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3) 0;
    border-top: 1px solid var(--border-dark);
    font-size: var(--fs-sm);
    color: rgba(255,255,255,0.7);
    cursor: pointer;
    min-height: var(--touch-min);
}

.admin-toggle input[type="checkbox"] {
    width: 22px;
    height: 22px;
    flex-shrink: 0;
    accent-color: var(--green);
    cursor: pointer;
}

/* The sticky bar floats over the list, so the list needs room to scroll clear of it.
   Without this the last service's own Save button sits permanently underneath and is
   unreachable on a phone — you could edit its price but never save it. */
.admin-mobile-cards--sticky-actions { padding-bottom: var(--sticky-actions-h); }

@media (min-width: 768px) {
    .admin-mobile-cards--sticky-actions { padding-bottom: 0; }
}

/* Save All follows the list down the page instead of scrolling off the top. */
.pricing-sticky-actions {
    position: sticky;
    bottom: 0;
    z-index: 5;
    display: flex;
    align-items: center;
    min-height: var(--sticky-actions-h);
    gap: var(--space-2);
    padding: var(--space-3) 0;
    margin-top: var(--space-4);
    background: linear-gradient(to top, var(--dark) 60%, transparent);
}

.pricing-sticky-actions .btn { flex: 1; justify-content: center; }

@media (min-width: 768px) {
    .pricing-sticky-actions { display: none; }
}

/* ============================================================
   ADMIN DAY CALENDAR
   ============================================================ */
/* Stacked on a phone: the date and its two arrows on one line, the jump controls
   beneath. This was a single wrapping row, which broke wherever it ran out of width —
   typically leaving "previous day" on one line and "next day" on the next. */
.day-cal__nav {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    margin-bottom: var(--space-4);
}

.day-cal__nav-main {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.day-cal__nav-tools {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-wrap: wrap;
}

/* Wide enough for one row again. */
@media (min-width: 640px) {
    .day-cal__nav {
        flex-direction: row;
        align-items: center;
        gap: var(--space-3);
        flex-wrap: wrap;
    }
    .day-cal__nav-main { flex: 1 1 auto; }
}

/* min-width: 0 rather than the old 240px: that floor is what forced the nav row to
   wrap on a phone and split the two arrows across separate lines. The title takes the
   space between the arrows and wraps within it if the date is long. */
.day-cal__title {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-3);
    flex: 1 1 auto;
    min-width: 0;
    font-size: var(--fs-h3-compact);
    text-align: center;
}

/* Only one of the two date lengths is ever shown. Below 640px the short form keeps
   the arrows and the date on a single line; above it there's room for the full one. */
.day-cal__date--full  { display: none; }
.day-cal__date--short { display: inline; }

@media (min-width: 640px) {
    .day-cal__title {
        justify-content: flex-start;
        text-align: left;
    }
    .day-cal__date--full  { display: inline; }
    .day-cal__date--short { display: none; }
}

.day-cal__picker {
    width: auto;
    padding: var(--space-1) var(--space-3);
    font-size: var(--fs-xs);
    color-scheme: dark;
}

.day-cal__summary {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-bottom: var(--space-5);
    color: rgba(255,255,255,0.6);
    font-size: var(--fs-body-sm);
}

.day-cal__closed {
    color: #f87171;
    font-size: var(--fs-xs);
    font-weight: 600;
}

.day-cal__slots {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.day-cal-slot {
    display: grid;
    grid-template-columns: 64px 1fr;
    gap: var(--space-3);
    background: var(--dark-2);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
    padding: var(--space-3);
}

.day-cal-slot--free {
    background: transparent;
    opacity: 0.55;
}

.day-cal-slot__time {
    font-weight: 700;
    color: var(--green);
    font-size: var(--fs-body-sm);
    padding-top: var(--space-05);
}

.day-cal-slot--free .day-cal-slot__time { color: rgba(255,255,255,0.35); }

.day-cal-slot__content {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    min-width: 0;
}

.day-cal-slot__free {
    color: rgba(255,255,255,0.3);
    font-size: var(--fs-xs);
    padding-top: var(--space-05);
}

.day-cal-booking {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: var(--space-3);
    flex-wrap: wrap;
    background: var(--dark-4);
    border: 1px solid var(--border-dark);
    border-radius: 8px;
    padding: var(--space-3);
}

.day-cal-booking--cancelled { opacity: 0.45; }

.day-cal-booking__info { min-width: 0; flex: 1 1 260px; }

.day-cal-booking__head {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
    margin-bottom: var(--space-1);
}

.day-cal-booking__meta {
    font-size: var(--fs-xs);
    color: rgba(255,255,255,0.55);
}

.day-cal-booking__notes {
    margin-top: var(--space-1);
    font-size: var(--fs-caption);
    color: rgba(255,255,255,0.4);
    font-style: italic;
}

.day-cal-booking__actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
}

a.action-btn { text-decoration: none; }

/* ── One booking's controls, in two rows ─────────────────────────────────────
   Was a single flex-wrap row holding up to nine pills of equal weight; then one
   primary action with contact demoted to bare icon squares and Cancel hidden behind
   a "…" menu, which cost three taps to reach the second most common thing done to a
   booking. Now: the primary action across the top, and under it the three things you
   might do instead, all labelled. The menu is gone — Reopen was the only other thing
   in it, and it has the primary slot to itself on a cancelled or completed booking. */
.booking-actions {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    /* Its parents (.admin-card__actions, .action-buttons, .day-cal-booking__actions)
       are themselves flex, so without this it sizes to its content and the primary
       button never actually reaches full width. */
    flex: 1 1 100%;
    min-width: 0;
}

/* The thing tapped twenty times a day gets the full width and a full touch target.
   Column direction stretches it; the width is not set here. */
.booking-actions__primary { min-height: var(--touch-min); }

.booking-actions__row {
    display: flex;
    gap: var(--space-2);
    /* Wraps rather than overflowing if a translation ever outgrows a third of a
       narrow card — the automatic minimum size keeps each label readable first. */
    flex-wrap: wrap;
}

/* flex-basis 0 makes the grow values a straight ratio of the row, rather than a
   share of whatever is left over after the labels. */
.booking-actions__row .action-btn {
    flex: 1 1 0;
    min-height: var(--touch-min);
}

/* Cancel is the least used of the three by a wide margin, so it takes the smallest
   share. Not so small that the word crowds its own padding: flex's automatic minimum
   size stops it shrinking under the label, so on a very narrow card the ratio gives
   out before the text does. */
.booking-actions__row .action-btn--cancel {
    flex-grow: 0.62;
    padding-inline: var(--space-2);
}

@media (min-width: 768px) {
    /* Desktop puts the whole set back on one line — the table's Actions cell has no
       room for a stacked block, and a mouse does not need 44px targets.

       768px, not the 640px this block used to sit at, because that is where the card
       list gives way to the table (.admin-cards / .admin-table-wrapper--desktop-only).
       At 640–767px the cards were still on screen but these rules had already shrunk
       their buttons to 28px — a mouse-sized target on what is most often a tablet. */
    .booking-actions {
        flex-direction: row;
        align-items: center;
        justify-content: flex-end;
        flex-wrap: nowrap;
    }
    .booking-actions__primary { min-height: 28px; }
    .booking-actions__row { flex: 0 0 auto; flex-wrap: nowrap; }

    /* Four gaps at --space-2 is 32px of the Actions column spent on air. Half that is
       still legible at 28px tall and buys back the headroom the table needs to sit
       inside its wrapper without a scrollbar. */
    .booking-actions,
    .booking-actions__row { gap: var(--space-1); }
    .booking-actions__row .action-btn {
        flex: 0 0 auto;
        min-height: 28px;
    }
    .booking-actions__row .action-btn--cancel { padding-inline: var(--space-2); }

    /* The bookings table is a dense twelve-column grid inside an overflow-x wrapper,
       and two labelled contact pills pushed it 110px past that wrapper — a horizontal
       scrollbar under the whole dashboard. Above 768px they go back to icon squares
       with their title tooltips. The labels earn their width on the card list, where
       the target has to be finger-sized and a bare glyph is a guess; a mouse gets the
       tooltip instead. Cancel keeps its word at every width — it is the destructive
       one, and a symbol is not enough warning. */
    .booking-actions__row .action-btn--whatsapp,
    .booking-actions__row .action-btn--call {
        width: 32px;
        min-width: 32px;
        padding-inline: 0;
    }

    /* Hidden, not removed: the label is the button's accessible name, and dropping it
       would leave a link announced as nothing at all. */
    .booking-actions__row .action-btn--whatsapp .action-btn__label,
    .booking-actions__row .action-btn--call .action-btn__label {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
    }
}

/* ── An enquiry's controls ───────────────────────────────────────────────────
   Their own row rather than .booking-actions__row, which above 768px squeezes
   .action-btn--call to a 32px icon square and visually hides the label inside it.
   A booking survives that: the label is the word "Call" and the number is already
   in the row. An enquiry doesn't — the sender's address appears nowhere else, so
   there is nothing to hide it in favour of, and unhidden text in a 32px box with
   zero inline padding simply overprinted the button beside it. */
.enquiry-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-2);
    flex-wrap: wrap;
}

.enquiry-actions .action-btn {
    min-height: var(--touch-min);
    gap: var(--space-1);
}

/* An address can be arbitrarily long, and the Actions cell sits inside the table's
   overflow-x wrapper — so it truncates here rather than pushing a scrollbar under
   the whole dashboard. The full value stays in the title and in the mailto:. */
.enquiry-actions__contact { min-width: 0; }

.enquiry-actions__text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

@media (min-width: 768px) {
    /* Same reasoning as .booking-actions__row: a mouse doesn't need 44px, and the
       table cell has less room than a card. */
    .enquiry-actions {
        gap: var(--space-1);
        flex-wrap: nowrap;
    }
    .enquiry-actions .action-btn { min-height: 28px; }
    /* The contact pills give up width first; WhatsApp and Mark handled keep theirs,
       because a truncated verb is useless where a truncated address still reads. */
    .enquiry-actions__contact { flex: 0 1 auto; }
    .enquiry-actions .action-btn--whatsapp,
    .enquiry-actions .action-btn--primary,
    .enquiry-actions .action-btn--keep { flex: 0 0 auto; }
}

/* The confirm step for Cancel. It used to fire on a single click, despite being
   irreversible and now emailing the customer. */
.booking-confirm {
    flex: 1 1 100%;
    margin-top: var(--space-3);
    padding: var(--space-3);
    background: rgba(220,38,38,0.07);
    border: 1px solid rgba(220,38,38,0.25);
    border-radius: var(--radius-sm);
}

.booking-confirm__text {
    margin-bottom: var(--space-3);
    color: rgba(255,255,255,0.8);
    font-size: var(--fs-sm);
    line-height: 1.5;
}

.booking-confirm__actions { display: flex; gap: var(--space-2); flex-wrap: wrap; }

/* Delete's new home, at the foot of the expanded detail. Separated by a rule so it
   reads as a different category of thing, not the next button along. */
.booking-danger {
    margin-top: var(--space-4);
    padding-top: var(--space-3);
    border-top: 1px solid var(--border-dark);
}

.booking-danger__text {
    margin-bottom: var(--space-3);
    color: rgba(255,255,255,0.6);
    font-size: var(--fs-sm);
    line-height: 1.5;
}

.booking-danger__actions { display: flex; gap: var(--space-2); flex-wrap: wrap; }

.action-btn--whatsapp { background: rgba(37,211,102,0.12); color: #25d366; border-color: rgba(37,211,102,0.3); }
@media (hover: hover) and (pointer: fine) {
    .action-btn--whatsapp:hover { background: #25d366; color: var(--dark); }
}
.action-btn--call { background: rgba(59,130,246,0.12); color: #60a5fa; border-color: rgba(59,130,246,0.3); }
@media (hover: hover) and (pointer: fine) {
    .action-btn--call:hover { background: #3b82f6; color: var(--white); }
}
.action-btn--keep { background: rgba(255,255,255,0.06); color: rgba(255,255,255,0.7); border-color: rgba(255,255,255,0.15); }
@media (hover: hover) and (pointer: fine) {
    .action-btn--keep:hover { background: rgba(255,255,255,0.15); color: var(--white); }
}
/* Delete is permanent (two-step), so it stays muted until hover — distinct from
   the always-red "Cancel" which only changes a booking's status. */
.action-btn--delete { background: rgba(255,255,255,0.04); color: rgba(255,255,255,0.5); border-color: rgba(255,255,255,0.12); }
@media (hover: hover) and (pointer: fine) {
    .action-btn--delete:hover { background: #dc2626; color: var(--white); border-color: #dc2626; }
}

/* ============================================================
   iOS INPUT ZOOM — admin & booking controls
   ------------------------------------------------------------
   Companion to the block of the same name in app.css. It has to live here rather
   than there because this file is loaded second: the selectors below are defined
   in this file at 13.6–14px, media queries contribute no specificity, and at equal
   specificity the later stylesheet wins. Putting the override in app.css alone left
   every admin field still under the 16px threshold, so the page kept zooming on
   focus exactly where it was worst — the dashboard, on a phone.
   ============================================================ */
@media (max-width: 767px) {
    .form-control--compact,
    .form-control--price,
    .day-cal__picker,
    .admin-search .form-control {
        font-size: 16px;
    }

    /* 16px needs more room than the 13–14px these were spaced for. */
    .form-control--compact,
    .form-control--price {
        padding: var(--space-2) var(--space-3);
    }

    /* Fixed at 100px for a 14px font; too tight once the digits are 16px. */
    .form-control--price { width: 7.5rem; }
}

/* ============================================================
   TOUCH TARGETS (mobile)
   ------------------------------------------------------------
   Every control a finger has to hit gets at least --touch-min (44px) in the
   vertical direction, and as close to it horizontally as the layout allows. The
   visual sizes are unchanged on desktop, where a mouse makes small targets fine.

   The calendar day is the one compromise: it's a 7-column grid inside the booking
   card, so at a 390px viewport each cell can only be ~41px wide however the padding
   is tuned. Dropping aspect-ratio and setting an explicit min-height gives a
   41×44 target — short on one axis, but the axis that matters least, and far better
   than the ~31px square it collapsed to at 320px.
   ============================================================ */
@media (max-width: 767px) {
    .cal-nav__btn {
        width: var(--touch-min);
        height: var(--touch-min);
    }

    .cal-day {
        aspect-ratio: auto;
        min-height: var(--touch-min);
    }

    .cal-grid { gap: var(--space-05); }

    .timeslot { min-height: var(--touch-min); }

    .action-btn {
        min-height: var(--touch-min);
        padding: var(--space-2) var(--space-3);
    }

    .filter-tab {
        min-height: var(--touch-min);
        display: inline-flex;
        align-items: center;
        padding: var(--space-2) var(--space-4);
    }

    .bike-chip {
        min-height: var(--touch-min);
        display: inline-flex;
        align-items: center;
        padding: var(--space-2) var(--space-4);
    }

    /* The ✕ was an ~18px glyph with almost no padding around it. */
    .photo-picker-remove {
        min-width: var(--touch-min);
        min-height: var(--touch-min);
        display: inline-flex;
        align-items: center;
        justify-content: center;
        font-size: 1.35rem;
    }

    .service-pick-card { min-height: var(--touch-min); }

    .customer-auth-strip__link {
        min-height: var(--touch-min);
        display: inline-flex;
        align-items: center;
    }
}

/* ============================================================
   NOTIFICATION BELL (admin header)
   ------------------------------------------------------------
   Fed by NotificationService and pushed live over the circuit the dashboard already
   holds — see NotificationHub. The panel is absolutely positioned against .notif,
   so it needs the header row to allow overflow.
   ============================================================ */
.notif { position: relative; }

.notif__bell {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: var(--touch-min);
    min-height: var(--touch-min);
    padding: var(--space-2);
    font-size: 1.25rem;
    background: transparent;
    border: 1px solid var(--border-dark);
    border-radius: var(--radius-sm);
    color: rgba(255,255,255,0.75);
    cursor: pointer;
    transition: border-color 0.2s, color 0.2s;
}

.notif__bell--open { border-color: var(--green); color: var(--green); }

@media (hover: hover) and (pointer: fine) {
    .notif__bell:hover { border-color: var(--green); color: var(--green); }
}

/* The unread count. Sits on the bell's top-right corner. */
.notif__badge {
    position: absolute;
    top: 2px;
    right: 2px;
    min-width: 18px;
    height: 18px;
    padding: 0 var(--space-1);
    border-radius: 9px;
    background: var(--green);
    color: var(--dark);
    font-size: 0.65rem;
    font-weight: 800;
    line-height: 18px;
    text-align: center;
}

.notif__panel {
    position: absolute;
    top: calc(100% + 0.5rem);
    right: 0;
    z-index: 1200;          /* above the sticky navbar (1000) */
    /* The panel is rendered inside .page-hero, which is text-align: center — so
       without this every notification came out centred like a heading. */
    text-align: left;
    width: min(22rem, calc(100vw - 2rem));
    background: var(--dark-2);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
}

.notif__panel-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--border-dark);
    font-size: var(--fs-sm);
    color: var(--white);
}

.notif__mark {
    background: none;
    border: none;
    color: var(--green);
    font-size: var(--fs-xs);
    font-weight: 600;
    cursor: pointer;
    padding: var(--space-1);
}

.notif__empty {
    padding: var(--space-6) var(--space-4);
    text-align: center;
    color: rgba(255,255,255,0.35);
    font-size: var(--fs-sm);
}

.notif__list {
    list-style: none;
    margin: 0;
    padding: 0;
    max-height: 60vh;
    overflow-y: auto;
    overscroll-behavior: contain;
}

.notif__item {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 0 var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--border-dark);
}

.notif__item:last-child { border-bottom: none; }

/* A left bar rather than a background wash, so an unread item still reads as part
   of the same list. */
.notif__item--unread { box-shadow: inset var(--rule-accent) 0 0 var(--green); }

.notif__item-title {
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--white);
}

.notif__item-body {
    grid-column: 1;
    font-size: var(--fs-xs);
    color: rgba(255,255,255,0.55);
}

.notif__item-time {
    grid-row: 1;
    grid-column: 2;
    font-size: var(--fs-2xs);
    color: rgba(255,255,255,0.35);
    white-space: nowrap;
}

/* The panel is anchored to the bell, which sits in a right-aligned header row; on a
   phone that row wraps, so pin the panel to the viewport edge instead of the bell. */
@media (max-width: 479px) {
    /* A 22rem panel anchored to the bell would run off the left edge on a narrow
       screen, because the bell sits mid-row rather than at the edge. So the panel is
       anchored to the header row instead — still directly beneath the bell, but
       right-aligned to the container, where there is room for it.

       Note what this must NOT be: `position: static` on .notif alone. That removes
       the positioning context entirely, so the panel's `top: calc(100% + 0.5rem)`
       resolves against a far taller ancestor and it lands halfway down the page,
       nowhere near the bell that opened it. */
    .admin-header-actions { position: relative; }
    .notif { position: static; }
    .notif__panel {
        right: 0;
        left: auto;
        width: min(22rem, calc(100vw - 2rem));
    }
}

/* ---------- Pager (admin bookings list) ---------- */
/* Explicit prev/next rather than infinite scroll — on a phone, infinite scroll makes
   anything below the list unreachable. */
.pager {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    flex-wrap: wrap;
    margin-top: var(--space-5);
}

.pager__status {
    font-size: var(--fs-sm);
    color: rgba(255,255,255,0.5);
    text-align: center;
    flex: 1 1 auto;
}

.pager__btn { flex: 0 0 auto; }

@media (max-width: 479px) {
    .pager__status { order: 3; flex-basis: 100%; }
    .pager__btn { flex: 1 1 0; justify-content: center; }
}

/* The toggle between "unread only" and "read too". Sits under the list so the
   notifications themselves stay the first thing you read. */
.notif__panel-foot {
    padding: var(--space-2) var(--space-4) var(--space-3);
    border-top: 1px solid var(--border-dark);
    text-align: center;
}

/* ---------- Completion note (admin) ---------- */
/* Opens inline under the action buttons when a job is marked complete. flex-basis
   100% so it drops onto its own line rather than squeezing in beside the buttons. */
.complete-note {
    flex: 1 1 100%;
    margin-top: var(--space-3);
    padding: var(--space-3);
    background: var(--dark-2);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius-sm);
    text-align: left;
}

.complete-note__label {
    display: block;
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--white);
    margin-bottom: var(--space-2);
}

.complete-note__optional {
    font-weight: 400;
    color: rgba(255,255,255,0.4);
}

.complete-note__share {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin-top: var(--space-3);
    font-size: var(--fs-sm);
    color: rgba(255,255,255,0.75);
    cursor: pointer;
    min-height: var(--touch-min);
}

.complete-note__share input[type="checkbox"] {
    width: 22px;
    height: 22px;
    flex-shrink: 0;
    accent-color: var(--green);
    cursor: pointer;
}

.complete-note__hint {
    font-size: var(--fs-xs);
    color: rgba(255,255,255,0.4);
    margin-bottom: var(--space-3);
}

.complete-note__actions {
    display: flex;
    gap: var(--space-2);
    flex-wrap: wrap;
}

/* The mechanic's report on this job. Its own full-width line rather than another
   entry in the info column, so the reference and status badges stay level with the
   service name — the same shape .customer-booking__notes takes in the dashboard.
   order puts it last on a phone too, where the row reorders its other children. */
.account-booking-row__notes {
    flex: 1 1 100%;
    order: 4;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

/* Tinted green to read as something from the shop rather than more booking
   metadata, and headed so it is unmistakably the report on *this* job. */
.account-booking-row__note {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-3);
    background: rgba(61,187,72,0.07);
    border-left: var(--rule-accent) solid var(--green);
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
    color: rgba(255,255,255,0.75);
    line-height: 1.5;
}

.account-booking-row__note-head {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--fs-2xs);
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--green);
}

/* ============================================================
   CUSTOMERS TAB (admin)
   ============================================================ */
.admin-search--wide { flex: 1 1 100%; }

/* A list row that's a button — it opens the detail view. Inherits .admin-edit-card's
   box, so only the button resets and the stats row are needed here. */
.customer-card {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-05);
    width: 100%;
    text-align: left;
    font-family: inherit;
    color: inherit;
    cursor: pointer;
}

.customer-card__stats {
    display: flex;
    justify-content: space-between;
    gap: var(--space-3);
    width: 100%;
    margin-top: var(--space-2);
    font-size: var(--fs-xs);
    color: rgba(255,255,255,0.45);
}

.customer-card:focus-visible {
    outline: 2px solid var(--green);
    outline-offset: 2px;
}

@media (hover: hover) and (pointer: fine) {
    .customer-card:hover { border-color: var(--green); }
}

/* ── Bulk customer import ────────────────────────────────────────────────────
   Three states in one panel — pick a file, review what it will do, see what it did.
   The review step is the point of the whole thing, so it gets the room. */
.import-panel { max-width: 46rem; }

.import-panel__title { margin-bottom: var(--space-3); font-size: var(--fs-h3-compact); color: var(--white); }

.import-panel__intro {
    margin-bottom: var(--space-6);
    color: rgba(255,255,255,0.65);
    font-size: var(--fs-sm);
    line-height: 1.6;
}

.import-panel__intro code {
    padding: 0 var(--space-1);
    background: var(--dark-3);
    border-radius: var(--radius-sm);
    font-size: var(--fs-xs);
}

.import-panel__note {
    margin-top: var(--space-2);
    color: rgba(255,255,255,0.45);
    font-size: var(--fs-xs);
    line-height: 1.6;
}

.import-panel__error {
    margin: var(--space-4) 0;
    padding: var(--space-3) var(--space-4);
    background: rgba(220,38,38,0.1);
    border: 1px solid rgba(220,38,38,0.3);
    border-radius: var(--radius-sm);
    color: #f87171;
    font-size: var(--fs-sm);
}

.import-summary {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin: var(--space-6) 0;
    padding: var(--space-5);
    background: var(--dark-2);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
}

.import-summary__row {
    display: flex;
    align-items: baseline;
    gap: var(--space-3);
    color: rgba(255,255,255,0.7);
    font-size: var(--fs-sm);
}

/* Tabular figures so the counts line up in a column rather than jittering. */
.import-summary__count {
    min-width: 3ch;
    color: var(--white);
    font-size: var(--fs-h3-compact);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    text-align: right;
}

.import-summary__count--create { color: var(--green); }
.import-summary__count--bad { color: #f87171; }

.import-problems { margin-top: var(--space-6); margin-bottom: var(--space-6); }
.import-problems h4 { margin-bottom: var(--space-3); font-size: var(--fs-sm); color: var(--white); }

.import-panel__opt {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    margin-top: var(--space-4);
    color: rgba(255,255,255,0.8);
    font-size: var(--fs-sm);
    line-height: 1.5;
    cursor: pointer;
}

.import-panel__actions {
    display: flex;
    gap: var(--space-2);
    flex-wrap: wrap;
    margin-top: var(--space-6);
}

/* ---------- Detail ---------- */
.customer-detail__back { margin-bottom: var(--space-5); }

.customer-detail__head { margin-bottom: var(--space-5); }
.customer-detail__head h3 { margin-bottom: var(--space-1); }

.customer-detail__contact {
    display: flex;
    gap: var(--space-2);
    flex-wrap: wrap;
    margin-top: var(--space-3);
}

.customer-detail__stats { margin-bottom: var(--space-5); }
.customer-detail__empty { margin-bottom: var(--space-5); }
.customer-detail__add-note { margin-bottom: var(--space-5); }

/* ---------- Enquiries tab ---------- */
/* The message as it was typed, newlines and all. Capped so one long message can't
   push the actions on every other row off the bottom of the screen. */
.enquiry-message {
    margin: 0;
    font-size: var(--fs-sm);
    line-height: 1.6;
    color: rgba(255,255,255,0.8);
    white-space: pre-wrap;
    max-height: 12rem;
    overflow-y: auto;
}

.admin-table__row--handled { opacity: 0.6; }

/* The outstanding count, on the tab itself — the reason to open it. */
.filter-tab__count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.25rem;
    margin-left: var(--space-2);
    padding: 0 var(--space-1);
    border-radius: 50px;
    background: var(--green);
    color: var(--dark);
    font-size: var(--fs-2xs);
    font-weight: 700;
}

/* ---------- Notes ---------- */
.customer-notes,
.customer-bookings,
.customer-bikes {
    list-style: none;
    margin: 0 0 var(--space-6);
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.customer-note {
    padding: var(--space-3);
    background: var(--dark-2);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius-sm);
}

/* Shared notes get the same green edge the customer sees on their own booking, so
   staff can tell at a glance which ones the customer is reading. */
.customer-note--shared { border-left: var(--rule-accent) solid var(--green); }

.customer-note__head {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
    margin-bottom: var(--space-2);
}

.customer-note__delete { margin-left: auto; }

.customer-note__body {
    font-size: var(--fs-sm);
    color: rgba(255,255,255,0.8);
    line-height: 1.6;
    white-space: pre-wrap;
}

/* ---------- Bookings & bikes ---------- */
.customer-booking {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-wrap: wrap;
    padding: var(--space-3);
    background: var(--dark-2);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius-sm);
}

/* Never linked to the account — shown so a guest booking doesn't make a returning
   customer look like a stranger. */
.customer-booking--unlinked { opacity: 0.75; border-style: dashed; }

/* A job's notes, on the job. Full width so they wrap onto their own line under the
   row rather than squeezing the service name, and one step lighter than the row that
   holds them — the same relationship .account-booking-row has with its card. */
.customer-booking__notes {
    flex: 1 1 100%;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}
.customer-booking__notes .customer-note { background: var(--dark-3); }

.customer-booking__main {
    display: flex;
    flex-direction: column;
    gap: var(--space-05);
    flex: 1 1 10rem;
    min-width: 0;
}

.customer-bikes li {
    padding: var(--space-3);
    background: var(--dark-2);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius-sm);
    font-size: var(--fs-sm);
}

/* Staff notes shown on a booking's expanded row. Also the only place a note on a
   guest booking is visible, since a guest has no customer page to put it on. */
.booking-notes {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.booking-note {
    display: flex;
    flex-direction: column;
    gap: var(--space-05);
    padding: var(--space-3);
    background: var(--dark-2);
    border-left: var(--rule-accent) solid var(--border-dark);
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
    line-height: 1.5;
    white-space: pre-wrap;
}

/* Green edge = the customer can see this one, matching how it looks on their own
   booking. */
.booking-note--shared { border-left-color: var(--green); }

/* ============================================================
   Insights tab
   ============================================================
   Charts are plain elements rather than SVG or a charting library: every
   figure here is a single magnitude against a maximum, which a div with a
   width already expresses, and the dashboard has no other client-side
   dependency to keep it company.

   The funnel uses one green ramp, light to dark, because its stages are
   ordered rather than distinct — a categorical palette there would imply the
   steps are five unrelated things. The steps are spaced so adjacent pairs stay
   apart for a colour-blind reader and the lightest still clears the dashboard's
   surface. */

.insights-stats { grid-template-columns: repeat(2, 1fr); }

.insights-delta {
    margin-top: var(--space-2);
    font-size: var(--fs-tiny);
    color: rgba(255,255,255,0.4);
}

.insights-delta--up   { color: var(--green); }
.insights-delta--down { color: #E29B4B; }

.insights-notice {
    background: rgba(226,155,75,0.08);
    border: 1px solid rgba(226,155,75,0.35);
    border-radius: var(--radius);
    padding: var(--space-4);
    margin-bottom: var(--space-5);
    font-size: var(--fs-sm);
    color: rgba(255,255,255,0.75);
}

.insights-notice strong { display: block; color: #E29B4B; margin-bottom: var(--space-1); }
.insights-notice code   { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }

.insights-panel {
    background: var(--dark-2);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
    padding: var(--space-5);
    margin-bottom: var(--space-5);
}

.insights-panel__title {
    margin: 0 0 var(--space-3);
    font-size: var(--fs-body);
    font-weight: 600;
}

.insights-lede { margin: 0 0 var(--space-4); }

.insights-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-5);
}

.insights-footnote {
    font-size: var(--fs-tiny);
    color: rgba(255,255,255,0.35);
    margin-top: var(--space-6);
    line-height: 1.6;
}

/* Funnel */

.funnel-stage {
    display: grid;
    grid-template-columns: 7.5rem 1fr;
    gap: var(--space-3);
    align-items: center;
}

.funnel-stage__name {
    font-size: var(--fs-xs);
    color: rgba(255,255,255,0.75);
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
    min-width: 0;
}

.funnel-stage__num {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: var(--fs-tiny);
    color: rgba(255,255,255,0.35);
    flex: none;
}

.funnel-stage__track { display: flex; align-items: center; height: 2.25rem; }

.funnel-stage__bar {
    height: 1.9rem;
    min-width: 3rem;
    border-radius: 0 4px 4px 0;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    padding-right: var(--space-2);
}

.funnel-stage__count {
    font-size: var(--fs-tiny);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    /* Dark ink on every step: the ramp's darkest rung still sits well above the
       surface, and one text colour beats four that each need checking. */
    color: #0A2A10;
}

.funnel-stage__bar--1 { background: #C6EFCA; }
.funnel-stage__bar--2 { background: #96E0A0; }
.funnel-stage__bar--3 { background: #63CB72; }
.funnel-stage__bar--4 { background: #34A845; }
.funnel-stage__bar--5 { background: #1F7A2E; }
.funnel-stage__bar--5 .funnel-stage__count { color: #EAF7EC; }

.funnel-drop {
    grid-column: 2;
    margin-left: 7.5rem;
    padding-left: var(--space-3);
    border-left: 1px solid var(--border-dark);
    font-size: var(--fs-tiny);
    color: rgba(255,255,255,0.4);
    font-variant-numeric: tabular-nums;
    line-height: 1.6;
}

.funnel-drop strong { color: rgba(255,255,255,0.7); font-weight: 600; }
.funnel-drop--worst strong { color: #E29B4B; }

/* Horizontal magnitude bars */

.insights-bars { display: flex; flex-direction: column; gap: var(--space-3); }
.insights-bars--tight { gap: var(--space-2); margin-top: var(--space-4); }

.insights-bar--weak { opacity: 0.45; }

.insights-bar__meta {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--space-3);
    margin-bottom: var(--space-1);
    font-size: var(--fs-xs);
}

.insights-bar__name {
    color: rgba(255,255,255,0.8);
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.insights-bar__value {
    flex: none;
    font-weight: 600;
    color: var(--white);
    font-variant-numeric: tabular-nums;
}

.insights-bar__sub {
    margin-left: var(--space-2);
    font-weight: 400;
    color: rgba(255,255,255,0.35);
}

.insights-bar__track {
    height: 6px;
    background: var(--dark-4);
    border-radius: 3px;
    overflow: hidden;
}

.insights-bar__fill { height: 100%; background: var(--green); border-radius: 3px; }

/* Weekly columns */

.insights-columns {
    display: flex;
    align-items: flex-end;
    gap: var(--space-2);
    overflow-x: auto;
    padding-bottom: var(--space-1);
}

.insights-column {
    flex: 1 1 0;
    min-width: 2.25rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-1);
}

.insights-column__value {
    font-size: var(--fs-tiny);
    font-variant-numeric: tabular-nums;
    color: rgba(255,255,255,0.55);
}

/* The plot is a fixed-height box so the bar's percentage height has something to
   resolve against — a percentage height against an auto-height parent collapses to
   nothing, which is the classic way a bar chart renders as a row of slivers. */
.insights-column__plot {
    width: 100%;
    height: 7rem;
    display: flex;
    align-items: flex-end;
}

.insights-column__bar {
    width: 100%;
    min-height: 2px;
    background: var(--green);
    border-radius: 3px 3px 0 0;
}

.insights-column__label {
    font-size: var(--fs-tiny);
    color: rgba(255,255,255,0.3);
    white-space: nowrap;
}

.insights-pair {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--space-3);
    padding: var(--space-2) 0;
    border-bottom: 1px solid var(--border-dark);
    font-size: var(--fs-xs);
}

.insights-pair__label { color: rgba(255,255,255,0.6); }
.insights-pair__value { font-weight: 600; font-variant-numeric: tabular-nums; }

@media (min-width: 768px) {
    .insights-stats { grid-template-columns: repeat(4, 1fr); }
    .insights-grid  { grid-template-columns: 1fr 1fr; }
    .funnel-stage   { grid-template-columns: 10rem 1fr; }
    .funnel-drop    { margin-left: 10rem; }
/* ── The guest manage-booking page ───────────────────────────────────────────
   Built on .admin-login__card, which is already the shape for a single-purpose
   page reached from a link rather than from the site's own navigation. What it
   adds is the details list and the two action panels.

   The card centres its text; the details read as a list of facts, so they are
   pulled back to the left here rather than fighting it everywhere else. */
.manage-booking { max-width: 480px; }

.manage-booking__details {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: var(--space-2) var(--space-4);
    margin: var(--space-6) 0;
    text-align: left;
}

.manage-booking__details dt {
    color: rgba(255,255,255,0.45);
    font-size: var(--fs-sm);
    align-self: center;
}

.manage-booking__details dd {
    margin: 0;
    color: rgba(255,255,255,0.9);
    font-size: var(--fs-body-sm);
    align-self: center;
}

/* Stacked rather than side by side: on a phone, two buttons sharing a row put
   "Cancel this booking" a thumb's width from the one they meant to press. */
.manage-booking__actions {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.manage-booking__reschedule { text-align: left; }
.manage-booking__reschedule .form-group { margin-bottom: var(--space-4); }

/* The shared .booking-confirm__actions is a wrapping row, which is right inside a
   dashboard table cell and wrong here: at phone width the two buttons wrap onto
   separate lines at whatever width their labels happen to need, and two stacked
   buttons of different widths read as a layout accident. Full width, stacked. */
.manage-booking .booking-confirm__actions {
    margin-top: var(--space-4);
    flex-direction: column;
    gap: var(--space-3);
}

.manage-booking .booking-confirm__actions .btn { width: 100%; }

@media (min-width: 480px) {
    .manage-booking .booking-confirm__actions { flex-direction: row; }
    .manage-booking .booking-confirm__actions .btn { width: auto; flex: 1; }
}

/* Not an error — the shop explaining why this one needs a person. Coloured as
   information rather than as a failure, because the customer has done nothing
   wrong and the next step is a phone call, not a retry. */
.manage-booking__blocked {
    margin: var(--space-4) 0;
    padding: var(--space-3);
    background: rgba(255,255,255,0.04);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius-sm);
    color: rgba(255,255,255,0.7);
    font-size: var(--fs-sm);
    line-height: 1.5;
    text-align: left;
}

/* The card's own rule dims every <p> inside it, which is right for a form's
   helper text and wrong for the one line confirming what just happened. */
.manage-booking .success-message p {
    color: rgba(255,255,255,0.85);
    margin-bottom: 0;
}
