.single-service .icon-wrapper img {
  width: 60px !important;
  height: 60px !important;
  object-fit: contain !important;
}
.single-offer-product.verticle .promo-img {
  width: 100%;
  height: 680px !important;
  object-fit: cover !important;
}
.single-offer-product .promo-img {
  width: 100%;
  height: 328px !important;
  object-fit: cover !important;
}

.gs-footer-section .footer-row .left-info .social-links a i {
  color: black;
  transition: all 0.3s;
}
.gs-footer-section .footer-row .left-info .social-links a:hover i {
  color: white;
}

#cardNumber,
#securityCode,
#expirationDate {
  height: 60px;
  transition: color 0.25s, background-color 0.25s, border-color 0.25s;
  border: 1px solid #e0e0e0;
  border-radius: 5px;
  background-color: #fff;
  color: #505050;
  font-size: 14px;
  padding: 0px 18px;
}

#cardNumber,
#securityCode,
#expirationDate {
  margin-bottom: 18px;
}

@media screen and (max-width: 767px) {
  .single-offer-product .promo-img {
    height: 200px !important;
  }
  .single-offer-product.verticle .promo-img {
    height: 400px !important;
  }
}


.sidebar-active-color {
  color: #ff4c3b!important;
}

/* Partoo brand assets keep stable filenames; sizing belongs to the UI. */
.header-logo-wrapper .logo,
.navbar-brand .nav-logo {
  display: block;
  width: auto;
  height: auto;
  max-width: 190px;
  max-height: 58px;
  object-fit: contain;
}

.mobile-menu-top > img,
.vendor-res-header-logo {
  display: block;
  width: auto;
  height: auto;
  max-width: 170px;
  max-height: 52px;
  object-fit: contain;
}

.gs-vendor-sidebar-logo-wrapper img {
  display: block;
  width: auto;
  height: auto;
  max-width: 180px;
  max-height: 58px;
  object-fit: contain;
}

.footer-logo img,
.gs-footer-section .logo {
  display: block;
  width: auto;
  height: auto;
  max-width: 210px;
  max-height: 72px;
  object-fit: contain;
}

@media screen and (max-width: 575px) {
  .header-logo-wrapper .logo,
  .navbar-brand .nav-logo {
    max-width: 150px;
    max-height: 48px;
  }
}

/* header search live-suggestions dropdown */
.autocomplete {
  position: relative;
}
.autocomplete-items {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 50;
  max-height: 360px;
  overflow-y: auto;
  background: #fff;
  border: 1px solid #e5e5e5;
  border-top: none;
  border-radius: 0 0 6px 6px;
  box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
}
.autocomplete-items:empty {
  display: none;
  border: none;
  box-shadow: none;
}
.autocomplete-items .docname {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 14px;
  color: #1f0300;
  text-decoration: none;
}
.autocomplete-items .docname:hover {
  background: #f5f5f5;
}
.autocomplete-items .docname img {
  width: 44px;
  height: 44px;
  object-fit: cover;
  flex-shrink: 0;
  border-radius: 4px;
}
.autocomplete-items .docname .search-content {
  min-width: 0;
}
.autocomplete-items .docname p {
  margin: 0;
  font-size: 14px;
  line-height: 1.3;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.autocomplete-items .docname .suggest-price {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: #ff5722;
}
/* Customer/rider panel pages (gs-user-panel-review) wrap the sidebar +
   content in Bootstrap's .container, which caps out at 1320px on wide
   screens and leaves grey page background on both sides. These are app
   panels, not marketing content, so they should use the full viewport
   like .container-fluid. */
.gs-user-panel-review > .container {
  max-width: 100%;
}

/* Stretch the sidebar's white card to the full height of the content
   column instead of shrink-wrapping its own links, so tall pages
   (order details, etc.) don't leave grey page background below a short
   sidebar. */
.gs-user-panel-review .d-flex {
  align-items: stretch;
}
.gs-dashboard-user-sidebar-wrapper {
  min-height: 100%;
}

/* Order-tracking stepper: the final "Delivered" step gets a distinct
   completed look (green, matching the completed order-status color used
   in STATUS_CLASSES elsewhere) instead of the same orange every other
   active-but-still-in-progress step uses, so "done" reads differently
   from "on the way". */
.gs-checkout-wrapper .checkout-step-wrapper .single-step.completed .step-btn {
  background-color: #27be69;
  color: #ffffff;
}
.gs-checkout-wrapper .checkout-step-wrapper .single-step.completed .step-txt {
  color: #27be69;
}

/* Order-details Status History: a real vertical timeline instead of a
   flat bulleted list, with the most recent entry emphasized and repeated
   consecutive statuses folded into one row (see order_detail()'s
   _grouped_tracks()). */
.order-timeline {
  position: relative;
  margin: 0;
  padding-left: 28px;
}
.order-timeline::before {
  content: "";
  position: absolute;
  left: 5px;
  top: 6px;
  bottom: 6px;
  width: 2px;
  background: #e9e6e6;
}
.order-timeline-item {
  position: relative;
  padding-bottom: 18px;
}
.order-timeline-item:last-child {
  padding-bottom: 0;
}
.order-timeline-dot {
  position: absolute;
  left: -28px;
  top: 5px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: #e9e6e6;
  border: 2px solid #ffffff;
  box-shadow: 0 0 0 1px #e9e6e6;
}
.order-timeline-item-latest .order-timeline-dot {
  background: #ff4c3b;
  box-shadow: 0 0 0 1px #ff4c3b;
}
.order-timeline-item-latest strong {
  color: #ff4c3b;
}
.order-timeline-repeat {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 8px;
  border-radius: 10px;
  background: #e9e6e6;
  color: #796866;
  font-size: 12px;
  font-weight: 600;
}
.order-timeline-date {
  display: block;
  font-size: 13px;
}
.order-timeline-text {
  color: #4c3533;
  font-size: 14px;
  margin-top: 2px;
}

/* Status History collapse toggle — closed by default, per the order-details
   page reorder that moved this section to the bottom as archival detail
   rather than the first thing a customer sees. A visible bar (not just a
   plain heading) so it reads as clickable before the customer ever
   touches it. */
.order-status-history-toggle {
  cursor: pointer;
  color: inherit;
  text-decoration: none;
  padding: 14px 18px;
  border-radius: 8px;
  background: #f8f7f7;
  border: 1px solid #e9e6e6;
  transition: background-color 0.2s ease-in, border-color 0.2s ease-in;
}
.order-status-history-toggle:hover,
.order-status-history-toggle:focus {
  background: #fff2f0;
  border-color: #ff4c3b;
  color: #ff4c3b;
}
.order-status-history-toggle[aria-expanded="true"] {
  background: #fff2f0;
  border-color: #ff4c3b;
  color: #ff4c3b;
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}
.order-status-history-toggle .fa-angle-down {
  transition: transform 0.3s ease-in;
}
.order-status-history-toggle[aria-expanded="true"] .fa-angle-down {
  transform: rotate(180deg);
}
.order-status-history .collapse.show {
  border: 1px solid #e9e6e6;
  border-top: none;
  border-bottom-left-radius: 8px;
  border-bottom-right-radius: 8px;
  padding: 0 18px 14px;
}

.autocomplete-items .docname-empty {
  padding: 14px;
  font-size: 14px;
  color: #6b6b6b;
  text-align: center;
}

/* ==========================================================================
   PARTOO DESIGN SYSTEM
   Conventions layer for the shared front theme (storefront, customer, rider,
   vendor). Documented in .claude/skills/frontend-ux/SKILL.md.

   This file loads AFTER style.css, so equal-specificity rules here win. It
   ADDS conventions; it does not restyle existing components wholesale. The
   compiled style.css is never edited by hand.
   ========================================================================== */

:root {
  /* --- Spacing: 4px base. Use these, not arbitrary px. ------------------- */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;
  --sp-6: 32px;
  --sp-7: 48px;
  --sp-8: 64px;

  /* --- Radius: three steps only ----------------------------------------- */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-pill: 999px;

  /* --- Elevation: three steps only -------------------------------------- */
  --shadow-1: 0 1px 2px rgba(31, 3, 0, 0.06);
  --shadow-2: 0 4px 20px rgba(31, 3, 0, 0.07);
  --shadow-3: 0 8px 40px rgba(31, 3, 0, 0.12);

  /* --- Type scale ------------------------------------------------------- */
  --fs-xs: 12px;
  --fs-sm: 14px;
  --fs-md: 16px;
  --fs-lg: 18px;
  --fs-xl: 20px;
  --fs-2xl: 24px;
  --fs-3xl: 32px;

  /* --- Ink and surface -------------------------------------------------- */
  --ink: #1f0300;
  --ink-2: #4c3533;
  --ink-muted: #796866;      /* lightest tone permitted for text (5.27:1) */
  --surface: #ffffff;
  --surface-2: #f8f7f7;
  --border: #e9e6e6;
  --border-strong: #cfc7c5;

  /* --- Semantic color --------------------------------------------------- */
  /* Brand orange #ff4c3b is 3.31:1 - fine for fills/accents and large text,
     NOT for normal-size text. --primary-text is the accessible companion. */
  --primary: #ff4c3b;
  --primary-hover: #bd1000;
  --primary-text: #b3301f;
  --primary-bg: #fdecea;

  --success: #27be69;
  --success-text: #1c6b40;
  --success-bg: #e7f4ec;

  --warning: #fac03c;
  --warning-text: #7a5c05;
  --warning-bg: #fdf3d9;

  --danger: #f2415a;
  --danger-solid: #b3253c;   /* white text passes at 6.47:1 */
  --danger-text: #a92034;
  --danger-bg: #fceaed;

  --info: #24627f;
  --info-text: #1f5673;
  --info-bg: #e8f1f7;

  --neutral: #030712;
  --neutral-text: #3f3a39;
  --neutral-bg: #eeeceb;

  --focus-ring: #1f0300;
}

/* ==========================================================================
   1. LAYOUT
   Panel pages use .container (the full-width override is above). One vertical
   rhythm between sections: use .ui-section rather than ad-hoc margins.
   ========================================================================== */

.ui-section { margin-bottom: var(--sp-6); }
.ui-section:last-child { margin-bottom: 0; }
.ui-stack   { display: flex; flex-direction: column; gap: var(--sp-4); }
.ui-row     { display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: center; }
.ui-row-end { justify-content: flex-end; }

/* ==========================================================================
   2. TYPOGRAPHY
   Heading LEVEL is document rank; these classes carry the size. That split is
   what lets a page keep exactly one h1 without the type going wrong.
   ========================================================================== */

.ui-page-title {
  font-size: var(--fs-2xl);
  font-weight: 700;
  color: var(--ink);
  line-height: 1.25;
  margin: 0;
}
.ui-section-title {
  font-size: var(--fs-lg);
  font-weight: 600;
  color: var(--ink);
  line-height: 1.35;
  margin: 0 0 var(--sp-3);
}
.ui-subsection-title {
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--ink-2);
  line-height: 1.4;
  margin: 0 0 var(--sp-2);
}
.ui-body  { font-size: var(--fs-md); color: var(--ink-2); line-height: 1.6; }
.ui-muted { font-size: var(--fs-sm); color: var(--ink-muted); line-height: 1.5; }
.ui-label {
  display: inline-block;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--ink-2);
  margin-bottom: var(--sp-2);
}

/* ==========================================================================
   3. BUTTONS
   Base .template-btn and its size/color modifiers live in style.css and are
   unchanged. These fill the gaps the audit found: ghost, icon-only, loading,
   and a real disabled state.
   ========================================================================== */

/* Ghost - lowest-emphasis action. Text only until hovered. */
.template-btn.ghost-btn {
  background: transparent;
  border: 1px solid transparent;
  color: var(--ink-2);
}
.template-btn.ghost-btn:hover,
.template-btn.ghost-btn:focus-visible {
  background: var(--surface-2);
  color: var(--ink);
}

/* Icon-only. ALWAYS pair with aria-label - see the a11y rules in the skill. */
.template-btn.icon-btn {
  width: 40px;
  height: 40px;
  padding: 0;
  flex-shrink: 0;
  border-radius: var(--radius-sm);
  gap: 0;
}
.template-btn.icon-btn.sm-btn { width: 32px; height: 32px; }
.template-btn.icon-btn.lg-btn { width: 48px; height: 48px; }

/* Disabled - covers button[disabled], a.disabled, and .is-loading. */
.template-btn:disabled,
.template-btn.disabled,
.template-btn.is-loading {
  opacity: 0.6;
  cursor: not-allowed;
  pointer-events: none;
}

/* Loading - set by ui.js on submit. Hides the label but keeps its box, so the
   button does not change width and the click target stays put. */
.template-btn.is-loading {
  position: relative;
  color: transparent !important;
}
.template-btn.is-loading > * { visibility: hidden; }
.template-btn.is-loading::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 16px;
  height: 16px;
  margin: -8px 0 0 -8px;
  border: 2px solid rgba(255, 255, 255, 0.45);
  border-top-color: #ffffff;
  border-radius: 50%;
  animation: ui-spin 0.6s linear infinite;
}
.template-btn.outline-btn.is-loading::after,
.template-btn.ghost-btn.is-loading::after,
.template-btn.dark-outline.is-loading::after {
  border-color: rgba(31, 3, 0, 0.25);
  border-top-color: #1f0300;
}
@keyframes ui-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .template-btn.is-loading::after { animation-duration: 2s; }
}

/* ==========================================================================
   4. STATUS BADGES
   A badge is NOT a button: it is a span and it never takes a click. The tone
   comes from one Python map (core/status_display.py) via {% status_badge %}.
   Tinted fill + dark text, following the yellow-btn precedent - the one
   status color in the old app that passed contrast. All tones >= 5.4:1.
   ========================================================================== */

.status-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  padding: 3px 10px;
  border-radius: var(--radius-pill);
  font-size: var(--fs-xs);
  font-weight: 600;
  line-height: 1.5;
  letter-spacing: 0.01em;
  white-space: nowrap;
  text-transform: capitalize;
  border: 1px solid transparent;
  vertical-align: middle;
}
.status-badge::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}
.status-badge.is-neutral { color: #3f3a39; background: #eeeceb; border-color: #dcd8d6; }
.status-badge.is-info    { color: #1f5673; background: #e8f1f7; border-color: #cfe0ec; }
.status-badge.is-warning { color: #7a5c05; background: #fdf3d9; border-color: #f0e0ac; }
.status-badge.is-success { color: #1c6b40; background: #e7f4ec; border-color: #c3e2d1; }
.status-badge.is-danger  { color: #a92034; background: #fceaed; border-color: #f2ced5; }
.status-badge.is-primary { color: #b3301f; background: #fdecea; border-color: #f8d3ce; }

/* Larger variant for a detail-page header. */
.status-badge.status-badge-lg { font-size: var(--fs-sm); padding: 5px 12px; }

/* ==========================================================================
   5. FORMS
   One convention: .form-control for the input, a real label, an asterisk for
   required, help text under the field, error text under that.
   ========================================================================== */

.form-label,
label.ui-label {
  display: inline-block;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--ink-2);
  margin-bottom: var(--sp-2);
}
.form-label .req,
.ui-required {
  color: #a92034;
  margin-left: 2px;
  font-weight: 700;
}
.form-help {
  display: block;
  font-size: var(--fs-xs);
  color: var(--ink-muted);
  margin-top: var(--sp-1);
  line-height: 1.5;
}
/* Bootstrap ships .invalid-feedback/.is-invalid but the app never used them.
   These make them match the theme so field-level errors have one look. */
.form-control.is-invalid,
.is-invalid .form-control,
.form-control[aria-invalid="true"] {
  border-color: #a92034 !important;
}
.invalid-feedback,
.field-error {
  display: block;
  font-size: var(--fs-xs);
  color: #a92034;
  margin-top: var(--sp-1);
  line-height: 1.5;
}
.form-control.is-valid,
.is-valid .form-control { border-color: #1c6b40 !important; }
.valid-feedback,
.field-success {
  display: block;
  font-size: var(--fs-xs);
  color: #1c6b40;
  margin-top: var(--sp-1);
}
.ui-field { margin-bottom: var(--sp-4); }

/* ==========================================================================
   6. CARDS
   One primitive. A card means "this is a separate object". Do not nest cards,
   and do not wrap a whole page body in one - see the skill for the rule.
   ========================================================================== */

.panel-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--sp-5);
}
.panel-card + .panel-card { margin-top: var(--sp-4); }
.panel-card-flush { padding: 0; overflow: hidden; }
.panel-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding-bottom: var(--sp-3);
  margin-bottom: var(--sp-4);
  border-bottom: 1px solid var(--border);
}

/* ==========================================================================
   7. ALERTS
   Page-level messages that must persist. Transient feedback stays with
   toastr; this is for a form's error summary or a standing notice.
   ========================================================================== */

.ui-alert {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  border-left-width: 3px;
  font-size: var(--fs-sm);
  line-height: 1.55;
  margin-bottom: var(--sp-4);
}
.ui-alert p:last-child,
.ui-alert ul:last-child { margin-bottom: 0; }
.ui-alert ul { padding-left: var(--sp-4); margin-bottom: 0; }
.ui-alert-icon { flex-shrink: 0; line-height: 1.4; }
.ui-alert-success { color: #1c6b40; background: #e7f4ec; border-color: #c3e2d1; border-left-color: #1c6b40; }
.ui-alert-warning { color: #7a5c05; background: #fdf3d9; border-color: #f0e0ac; border-left-color: #7a5c05; }
.ui-alert-danger  { color: #a92034; background: #fceaed; border-color: #f2ced5; border-left-color: #a92034; }
.ui-alert-info    { color: #1f5673; background: #e8f1f7; border-color: #cfe0ec; border-left-color: #1f5673; }

/* ==========================================================================
   8. EMPTY STATES
   Heading + one supporting line + a CTA. .compare-empty was the original
   (markup-only) pattern; .ui-empty is the same shape with real styling, and
   .compare-empty is aliased onto it so existing call sites gain the styling.
   ========================================================================== */

.ui-empty,
.compare-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: var(--sp-7) var(--sp-4);
}
.ui-empty-icon {
  color: var(--border-strong);
  margin-bottom: var(--sp-4);
  line-height: 1;
}
.ui-empty-title,
.compare-empty h2 {
  font-size: var(--fs-xl);
  font-weight: 600;
  color: var(--ink);
  margin: 0 0 var(--sp-2);
}
.ui-empty-text,
.compare-empty p {
  font-size: var(--fs-sm);
  color: var(--ink-muted);
  max-width: 46ch;
  margin: 0 0 var(--sp-5);
}

/* Empty state inside a table cell: the cell hosts it, so drop the tall pad. */
.ui-empty-cell { padding: var(--sp-6) var(--sp-4) !important; text-align: center; }
.ui-empty-cell .ui-empty { padding: 0; }

/* ==========================================================================
   9. TABLES
   The wrapper stays .table-responsive (already 22/22 across the panel - the
   most consistent convention in the app). These only add readability.
   ========================================================================== */

.ui-table-actions {
  display: flex;
  gap: var(--sp-2);
  align-items: center;
  justify-content: flex-end;
  white-space: nowrap;
}
.ui-num { font-variant-numeric: tabular-nums; }

/* ==========================================================================
   10. LOADING
   .gocover is a full-screen blocking overlay. style.css never defined it for
   the front theme even though 8 customer-facing call sites drive it, so every
   one of those calls was a silent no-op. Defining it makes them work.
   ========================================================================== */

.gocover {
  display: none;
  position: fixed;
  left: 0;
  top: 0;
  z-index: 9999;
  width: 100%;
  height: 100%;
  overflow: visible;
  background: rgba(45, 45, 45, 0.5);
}
.ui-spinner {
  display: inline-block;
  width: 16px;
  height: 16px;
  border: 2px solid rgba(31, 3, 0, 0.2);
  border-top-color: var(--primary);
  border-radius: 50%;
  animation: ui-spin 0.6s linear infinite;
  vertical-align: -3px;
}
/* Skeleton for a region being replaced by AJAX. */
.ui-skeleton {
  background: linear-gradient(90deg, #f8f7f7 25%, #e9e6e6 37%, #f8f7f7 63%);
  background-size: 400% 100%;
  animation: ui-shimmer 1.4s ease infinite;
  border-radius: var(--radius-sm);
  min-height: 16px;
}
@keyframes ui-shimmer { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }
@media (prefers-reduced-motion: reduce) {
  .ui-spinner { animation-duration: 2s; }
  .ui-skeleton { animation: none; }
}

/* ==========================================================================
   11. FOCUS / HOVER / ACTIVE
   style.css sets `outline: 0` on inputs and selects globally and carries 13
   outline:none rules against a single :focus-visible, so keyboard focus was
   effectively invisible. This restores a visible ring everywhere without
   bringing back the mouse-click outline.
   ========================================================================== */

a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible,
.template-btn:focus-visible,
.nice-select:focus-visible {
  outline: 2px solid #1f0300 !important;
  outline-offset: 2px !important;
  border-radius: var(--radius-sm);
}
/* nice-select hides the real select, so the widget itself carries the ring. */
.nice-select.open,
.nice-select:focus-within {
  outline: 2px solid #1f0300 !important;
  outline-offset: 2px !important;
}
/* Inputs get a brand-tinted border on focus in addition to the ring. */
.form-control:focus {
  border-color: var(--primary) !important;
  box-shadow: 0 0 0 3px rgba(255, 76, 59, 0.12);
}
.template-btn:active { transform: translateY(1px); }
@media (prefers-reduced-motion: reduce) {
  .template-btn:active { transform: none; }
}

/* style.css sets a blanket `.nav-link:hover/:focus { color: #fff }`, meant for
   nav items that also pick up a dark/brand background on hover. Bootstrap's
   own .nav-tabs (e.g. vendor/review/index.html's "Awaiting your reply" /
   "Answered" / "All reviews" tabs) never sets a hover background, so that
   rule turned the tab label white-on-white - readable only while actually
   hovered is still unreadable. Two extra classes beat style.css's rule on
   specificity alone, so this wins regardless of load order. */
.nav-tabs .nav-link:hover,
.nav-tabs .nav-link:focus {
  color: var(--ink-2);
}

/* Screen-reader-only text. The app had no such utility, which is why 64 of 68
   icon-only controls had no accessible name. */
.visually-hidden,
.sr-only {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Skip link - first focusable element in the layout. */
.ui-skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 10000;
  padding: var(--sp-3) var(--sp-4);
  background: #1f0300;
  color: #ffffff;
  border-radius: 0 0 var(--radius-md) 0;
  font-size: var(--fs-sm);
  font-weight: 600;
}
.ui-skip-link:focus {
  left: 0;
  color: #ffffff;
}

/* ==========================================================================
   12. CONFIRMATION DIALOG
   includes/ui/confirm-dialog.html. One per page; the trigger describes the
   action. Cancel reads as the calm default, the destructive control is the
   only red thing in the dialog.
   ========================================================================== */

.ui-confirm {
  padding: var(--sp-6) var(--sp-5) var(--sp-5);
  border-radius: var(--radius-lg);
  border: none;
  text-align: center;
}
.ui-confirm-body { margin-bottom: var(--sp-5); }
.ui-confirm-title {
  font-size: var(--fs-xl);
  font-weight: 700;
  color: var(--ink);
  margin: 0 0 var(--sp-2);
  text-wrap: balance;
}
/* The object being acted on. Quoted rather than styled loudly: it is
   evidence, not a heading. */
.ui-confirm-text {
  font-size: var(--fs-sm);
  color: var(--ink-2);
  background: var(--surface-2);
  border-radius: var(--radius-sm);
  padding: var(--sp-2) var(--sp-3);
  margin: 0 0 var(--sp-3);
  word-break: break-word;
}
.ui-confirm-warning {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--danger-text);
  margin: 0;
}
.ui-confirm-actions {
  display: flex;
  gap: var(--sp-3);
}
.ui-confirm-actions > * { flex: 1 1 0; }
@media (max-width: 575.98px) {
  .ui-confirm-actions { flex-direction: column-reverse; }
}

/* ==========================================================================
   13. ACTION GROUPS
   A row of controls acting on one object. Consistent order, consistent gap,
   destructive last. Legacy wrappers are aliased on so existing table rows
   pick up the spacing without every template being rewritten.
   ========================================================================== */

.ui-actions,
.view-btn-wrapper,
.table-icon-btns-wrapper {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
}
.ui-actions-end { justify-content: flex-end; }
/* In a table the group must not wrap mid-row or the row height jumps. */
.user-table .ui-actions,
.user-table .view-btn-wrapper,
.user-table .table-icon-btns-wrapper { flex-wrap: nowrap; }

/* ==========================================================================
   14. PAGINATION
   One look for the front theme, applied to the shared partial.
   ========================================================================== */

.ui-pagination {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: var(--sp-2);
  margin-top: var(--sp-6);
  padding: 0;
  list-style: none;
}
.ui-pagination a,
.ui-pagination span {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 38px;
  height: 38px;
  padding: 0 var(--sp-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink-2);
  font-size: var(--fs-sm);
  font-weight: 600;
  text-decoration: none;
  transition: background-color 0.2s ease-in, border-color 0.2s ease-in;
}
.ui-pagination a:hover {
  border-color: var(--primary);
  color: var(--primary-text);
}
.ui-pagination .is-current {
  background: var(--primary);
  border-color: var(--primary);
  color: #ffffff;
}
.ui-pagination .is-disabled {
  opacity: 0.45;
  pointer-events: none;
}

/* ==========================================================================
   15. PAGE-LEVEL REFINEMENTS
   Small additions supporting the customer-page pass. No new visual identity —
   these give existing blocks the system's spacing and emphasis rules.
   ========================================================================== */

/* Dashboard stat value. Was an <h5>, which put a heading below the h4 table
   title and broke the page outline; it is a value, not a heading. */
.gs-single-statatics .stat-value {
  display: block;
  font-size: var(--fs-2xl);
  font-weight: 700;
  line-height: 1.2;
  color: var(--ink);
}

/* All four stat cards are links now (two used to be, with nothing to show it).
   Keep the card colour rather than inheriting link blue. */
a.text-decoration-none .gs-single-statatics,
a.text-decoration-none .gs-single-statatics p {
  color: inherit;
}

/* Order-details action row. The buttons wrap on narrow screens rather than
   overflowing, and the cancel form sits on its own line with its reason field
   instead of being squeezed between buttons. */
.ui-order-actions {
  gap: var(--sp-3);
  margin-top: var(--sp-4);
}
.ui-cancel-form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  min-width: 240px;
}
.ui-cancel-form .form-label { margin-bottom: 0; }
@media (max-width: 575.98px) {
  .ui-order-actions { flex-direction: column; align-items: stretch; }
  .ui-order-actions > * { width: 100%; }
  .ui-cancel-form { min-width: 0; }
}

/* A control that is present but not permitted. Distinct from a button that is
   merely low-emphasis: this one states why it cannot be used. */
.template-btn.disabled,
.template-btn[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
  pointer-events: none;
}

/* Saved-for-later sits under the cart; give it air so it does not read as
   part of the cart table above it. */
.gs-cart-row > .col-12 > .ui-section-title { margin-top: var(--sp-5); }

/* Cart-line badges are sentences ("We are confirming this one - ..."), not
   one-word statuses. The badge's nowrap pushed them out of the 290px product
   cell and over the Price/Quantity columns, so here they wrap inside the cell
   and keep sentence case. */
.cart-product > img { flex-shrink: 0; }
.cart-product-info { min-width: 0; }
.cart-product-info .status-badge {
  white-space: normal;
  text-transform: none;
  border-radius: var(--radius-md);
  align-items: flex-start;
  max-width: 100%;
  margin-top: var(--sp-1);
}
.cart-product-info .status-badge::before { margin-top: 6px; }

/* ==========================================================================
   16. UNAVAILABLE ACTIONS
   An action the current state does not allow is EXPLAINED, not hidden. The
   reason is the domain's own words (returns.services.returnable_error and
   friends), so the page and the endpoint can never disagree about why.

   Deliberately not styled as an error: nothing has gone wrong, the customer
   simply cannot do this yet or any more. Icon + text carry the meaning, so it
   does not depend on colour alone.
   ========================================================================== */

.ui-action-unavailable {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  margin: 0;
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--ink-muted);
  font-size: var(--fs-sm);
  line-height: 1.5;
  max-width: 52ch;
}
.ui-action-unavailable i {
  color: var(--info-text);
  margin-top: 2px;
  flex-shrink: 0;
}
.ui-action-unavailable strong { color: var(--ink-2); }

/* Object being deleted, in the seller panel's confirmation. */
#vendor-delete-object {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--ink-2);
  background: var(--surface-2);
  border-radius: var(--radius-sm);
  padding: var(--sp-2) var(--sp-3);
  margin: var(--sp-2) 0;
  word-break: break-word;
}

/* ==========================================================================
   17. PAGE TITLE LEVEL
   Panel pages used <h3 class="ud-page-title"> as the page heading, so 19 of 20
   audited pages had NO <h1> and the document outline started at level 3.

   .ud-page-title was never styled — the size came from the bare h3 element
   rule (40px/120%, 28px under 1200px). Pinning that size on the CLASS lets the
   tag become <h1> without the title jumping to the h1 default of 64px: heading
   level is document rank, the class carries the size. Same pixels, correct
   outline.
   ========================================================================== */

.ud-page-title {
  font-size: 40px;
  line-height: 120%;
  font-weight: 600;
  margin: 0;
}
@media (max-width: 1199.97px) {
  .ud-page-title { font-size: 28px; }
}

/* Storefront pages title their banner with <h2 class="breadcrumb-title">, so
   cart, checkout, product, compare and 18 others had no <h1> either. Same
   treatment as .ud-page-title: pin the size the h2 element rule was providing
   (48px, 32px under 1200px) onto the class, so the tag can become <h1> without
   the banner title growing to the h1 default of 64px. */
.breadcrumb-title {
  font-size: 48px;
  line-height: 120%;
  font-weight: 700;
  margin: 0 0 8px;
}
@media (max-width: 1199.97px) {
  .breadcrumb-title { font-size: 32px; }
}

/* Storefront pages title themselves with <h2 class="breadcrumb-title"> in the
   banner. .breadcrumb-title styles colour and alignment but never size, so the
   48px came from the bare h2 rule. Pin it on the class so the tag can become
   <h1> without the title growing — same rule as .ud-page-title above. */
.breadcrumb-title {
  font-size: 48px;
  line-height: 120%;
  font-weight: 700;
}
@media (max-width: 1199.97px) {
  .breadcrumb-title { font-size: 32px; }
}

/* ==========================================================================
   18. PRODUCT REVIEWS
   .all-comments/.single-comment/.left-area/.right-area (product.html,
   load/reviews.html) had ZERO rules anywhere in style.css or here - the
   avatar rendered at its raw upload size, the date floated wherever the
   browser's default block flow put it, nothing lined up. Replaced with a
   plain divided list (Amazon's review layout: no per-review card, a border
   between rows) rather than styling the old class names, since neither
   class was used anywhere else in the front theme.

   Star fill reuses the read-only bar already built once for the page-level
   average (load/reviews.html's .ratings/.empty-stars/.full-stars): a full
   gray row with a gold row clipped to width:{{ rating/5*100 }}% on top. */

.review-list { list-style: none; margin: 0; padding: 0; }
.review-item {
  display: flex;
  gap: var(--sp-4);
  padding: var(--sp-5) 0;
  border-bottom: 1px solid var(--border);
}
.review-item:first-child { padding-top: 0; }
.review-item:last-child { border-bottom: none; }
.review-avatar {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
  border: 1px solid var(--border);
  background: var(--surface-2);
}
.review-body { flex: 1 1 auto; min-width: 0; }
.review-header {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--sp-1) var(--sp-3);
  margin-bottom: var(--sp-1);
}
.review-name { font-size: var(--fs-md); font-weight: 600; color: var(--ink); }
.review-date { font-size: var(--fs-xs); color: var(--ink-muted); margin-left: auto; }
.review-stars {
  position: relative;
  display: inline-block;
  font-size: 15px;
  line-height: 1;
  letter-spacing: 2px;
  margin-bottom: var(--sp-2);
}
.review-stars-base,
.review-stars-fill { display: block; white-space: nowrap; }
.review-stars-base { color: var(--border-strong); }
.review-stars-fill {
  position: absolute;
  inset: 0;
  overflow: hidden;
  color: #eeae0b; /* same star gold as includes/frontend/home_product.html */
}
.review-text {
  margin: 0;
  color: var(--ink-2);
  font-size: var(--fs-md);
  line-height: 1.6;
}
.review-empty {
  padding: var(--sp-5) 0;
  color: var(--ink-muted);
}

/* ── Chassis / VIN on a cart line ─────────────────────────────────────────
   The cart's product column is narrow, and a 17-character VIN typed into a
   default-width input shows about eight of them - the customer cannot read
   back what they entered, which is the whole point of showing it. Given a
   floor wide enough for a full VIN, and allowed to wrap under the Save
   button on small screens rather than shrink further. */
.gs-line-vin .gs-vin-input {
  min-width: 15rem;
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  letter-spacing: 0.02em;
}
.gs-line-vin > .d-flex { flex-wrap: wrap; }
@media (max-width: 575.98px) {
  .gs-line-vin .gs-vin-input { min-width: 100%; }
}

/* ── Chassis / VIN step on the product page's fit checker ────────────────
   Shown only once a verdict says the part can actually be bought, directly
   below that verdict - the left border repeats whichever tone the verdict
   used (success/warning), so the field reads as the next step in the SAME
   answer rather than an unrelated required field the page also wants.
   Neutral until then, matching every other field on the page. */
.gs-compat-vin-step {
  padding: var(--sp-3) 0 var(--sp-3) var(--sp-4);
  border-left: 3px solid var(--border-strong, #dcd8d6);
  transition: border-color .15s ease;
}
.gs-compat-vin-step.is-success-step { border-left-color: var(--success); }
.gs-compat-vin-step.is-warning-step { border-left-color: var(--warning); }
#gs-compat-vin,
#gs-fit-vin {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  letter-spacing: 0.02em;
}

/* The fitment modal - the product CARD's route to the same vehicle + chassis
   step the product page shows inline. Same visual language on purpose: a
   shopper who has used one should recognise the other.

   The results list is capped in height because a make with many variants
   would otherwise push the Add to Cart button below the fold of a dialog
   that is meant to be answered in a few seconds. */
#gs-fit-modal .gs-fit-results .gs-compat-results {
  max-height: 15rem;
  overflow-y: auto;
}
#gs-fit-modal .gs-fit-vin-wrap { margin-top: var(--sp-4); }
#gs-fit-modal .modal-footer { border-top: 1px solid #e9e6e6; }

/* Seller application status card on the customer dashboard. A vendor waits
   here until Admin approves them, so the card has to read as a panel rather
   than as loose text above the order statistics. Border/radius match the
   .order-status-history block above. */
.gs-vendor-status-box {
  border: 1px solid #e9e6e6;
  border-radius: 8px;
  padding: 24px;
  background: #fff;
}
.gs-vendor-status-box h4 {
  font-size: 18px;
}
.gs-vendor-status-box ul li {
  padding: 4px 0;
  color: #6b6b6b;
}

/* Vendor subscription plan cards (vendor/subscription/index.html). Reuses
   the shared .order-info-card component - it only needs its wrapper class
   (.gs-order-info-cards-wrapper) to pick up the card look at all, which the
   template was missing entirely, so the plans rendered as bare stacked text
   with no card, no equal height, and buttons wherever their own content
   happened to end. The wrapper + .h-100 fixes the card/equal-height part;
   this pins each plan's CTA to the card's bottom edge so "Choose Plan" /
   "Renew Plan" line up across plans with different numbers of features. */
.subscription-plan-card form {
  margin-top: auto;
  width: 100%;
}

/* A seller's own social links, in the Contact Vendor widget on their shop
   page. The theme's existing .social-links rules are scoped to
   .gs-footer-section, so nothing here can inherit them -- but the look is
   deliberately the same 40px circle the footer uses, since it is the same
   thing in a different place. The base is bordered rather than white: the
   footer sits on a dark band where white circles read as raised, and this
   widget sits on white. */
.vendor-social-links {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 18px;
}
.vendor-social-links a {
  width: 40px;
  height: 40px;
  border-radius: 20px;
  border: 1px solid #e0e0e0;
  display: inline-flex;
  justify-content: center;
  align-items: center;
  transition: background-color 0.3s, border-color 0.3s;
}
.vendor-social-links a i {
  color: #1f0300;
  transition: color 0.3s;
}
.vendor-social-links a:hover {
  background-color: #ff4c3b;
  border-color: #ff4c3b;
}
.vendor-social-links a:hover i {
  color: #ffffff;
}

/* ==================================================================
   Rider panel top bar (BUG-030)

   Replaces the storefront header on /rider/ pages, which is where the
   nav drawer's only trigger used to live. Deliberately minimal -- logo
   and menu button -- because what else belongs up here is a product
   decision, not one to settle in a stylesheet.

   Below XL only: from XL up the rider screens render their own sidebar
   inline and the drawer is not used.
   ================================================================== */

.rider-topbar {
  align-items: center;
  gap: var(--sp-3, 12px);
  padding: var(--sp-3, 12px) var(--sp-4, 16px);
  background: #fff;
  border-bottom: 1px solid var(--line, #ecebea);
  position: sticky;
  top: 0;
  z-index: 1020;
}

.rider-topbar-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 1px solid var(--line, #ecebea);
  border-radius: var(--radius-sm, 4px);
  background: #fff;
  color: var(--ink, #1f0300);
}

.rider-topbar-toggle:focus-visible {
  outline: 2px solid var(--focus-ring, #2d3274);
  outline-offset: 2px;
}

.rider-topbar-brand {
  display: inline-flex;
  align-items: center;
}

.rider-topbar-brand img {
  height: 28px;
  width: auto;
}

/* Push notification opt-in (includes/push.html). Bottom-left, so it never
   sits over the cart and checkout actions on the right; full width on phones. */
.push-prompt {
  position: fixed;
  left: var(--sp-4);
  bottom: var(--sp-4);
  z-index: 1030;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  max-width: 360px;
  padding: var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-3);
}

/* display:flex above would otherwise beat the `hidden` attribute. */
.push-prompt[hidden] {
  display: none;
}

@media (max-width: 575.98px) {
  .push-prompt {
    right: var(--sp-4);
    max-width: none;
  }
}
