/* ============================================================
   PSG HOSTING — WHMCS client-area BODY styles (child of twenty-one)
   Dark restyle of twenty-one's Bootstrap 4 body/component markup
   (cards, tables, forms, buttons, badges, alerts, dashboard tiles,
   dropdowns, …) to match the marketing site.

   The header + footer are NOT here — they're rendered by the shared
   PSGChrome module (public/chrome/, loaded via head.tpl), the SAME
   one the marketing site uses. Edit chrome.css for header/footer.

   WHMCS auto-loads this after theme.min.css (head.tpl's
   {assetExists file="custom.css"}), so these win. The @import pulls
   in the site's design tokens (baked from src/styles/tokens/* by
   scripts/gen-whmcs-theme-css.js) and MUST stay first — CSS requires
   @import before any style rule. Never hardcode hex/px; use the
   tokens (var(--orange-500), …). Selectors mirror
   twenty-one/css/theme.css so specificity matches.
   ============================================================ */
@import url('psg-tokens.generated.css');

/* ---------- Base / page ---------- */
body,
body.primary-bg-color {
  background-color: var(--surface-base);
  color: var(--text-primary);
  font-family: var(--font-body);
}

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-display);
  color: var(--text-primary);
  letter-spacing: var(--tracking-tight);
}

a {
  color: var(--orange-500);
}
a:hover {
  color: var(--orange-400);
}

hr {
  border-color: var(--border-subtle);
}

/* ---------- Content width ----------
   Match the marketing site's content grid and the shared chrome header/footer
   (chrome.css), so the WHMCS body content lines up under the same-width nav.
   All three read the same --content-max-width token (baked in from
   src/styles/tokens/spacing.css), so there's one source of truth. Bootstrap's
   .container caps at 1140px from the xl breakpoint up; raise just that cap —
   smaller breakpoints keep their stock stepped widths. */
@media (min-width: 1200px) {
  .container {
    max-width: var(--content-max-width);
  }
}

/* Square brand corners across the common controls. */
.btn,
.card,
.mc-promo-manage,
.mc-promo-login,
.form-control,
.input-group,
.input-group .btn,
.alert,
.badge,
.dropdown-menu,
.list-group-item {
  border-radius: var(--radius-none);
}

:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

/* Twenty-one's Bootstrap focus styles use a blue shadow, and its primary
   button rules restore blue whenever a control enters an active, dropdown, or
   disabled state. Keep the keyboard affordance, but make every interaction
   state use the brand ring. Suppressing only the WebKit tap flash prevents a
   second, browser-drawn blue overlay on touch devices; keyboard focus remains
   clearly visible through the ring above. */
.btn {
  -webkit-tap-highlight-color: transparent;
}
.btn:focus,
.btn.focus {
  outline: none;
  box-shadow: var(--focus-ring) !important;
}

/* ---------- Sidebar ---------- */
.sidebar .card {
  background-color: var(--surface-card);
}
.sidebar .list-group-item {
  background-color: transparent;
  border-color: var(--border-subtle);
  color: var(--text-secondary);
}
.sidebar .list-group-item a {
  color: var(--text-secondary);
}
.sidebar .list-group-item a:hover,
.sidebar .list-group-item.active {
  color: var(--orange-400);
}

/* ---------- Nav sidebar card (the WHMCS menu, see includes/nav-sidebar.tpl) ----------
   The primary + secondary navbars flattened into the left rail. Reuses the
   generic `.sidebar .list-group-item` treatment above; these rules add the
   flattened-dropdown affordances: a muted, non-interactive group heading for a
   navbar item that had children, and a small indent on that item's children. */
/* Each category is a collapsible <details>; its <summary> is the group label —
   collapsed by default, click to expand, one open at a time (see
   nav-sidebar.tpl + js/psg-nav-sidebar.js). `display:block` removes the native
   disclosure triangle; the chevron is our own and rotates when open. */
.psg-nav-card .psg-nav-group-label {
  display: block;
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  background-color: transparent;
  border-color: var(--border-subtle);
  cursor: pointer;
  padding-top: 12px;
  padding-bottom: 12px;
}
.psg-nav-card .psg-nav-group-label::-webkit-details-marker { display: none; }
.psg-nav-card .psg-nav-group-label:hover { color: var(--text-secondary); }
.psg-nav-card .psg-nav-chevron {
  font-size: 10px;
  margin-top: 3px;
  color: var(--text-muted);
  transition: transform 0.15s ease;
}
.psg-nav-card .psg-nav-group[open] > .psg-nav-group-label .psg-nav-chevron {
  transform: rotate(180deg);
}
.psg-nav-card .psg-nav-child {
  padding-left: 26px;
}

/* ---------- Cards / panels ---------- */
.card,
.mc-promo-manage,
.mc-promo-login {
  background-color: var(--surface-card);
  border: 1px solid var(--border-subtle);
  box-shadow: var(--shadow-sm);
}
.card-header,
.card-footer {
  background-color: var(--surface-inset);
  border-color: var(--border-subtle);
}
.primary-content .card-title,
.card-title {
  color: var(--text-primary);
}
.text-muted {
  color: var(--text-muted) !important;
}

/* ---------- Tables ---------- */
.table {
  color: var(--text-secondary);
}
.table th,
.table td {
  border-color: var(--border-subtle);
}
.table thead th {
  border-bottom-color: var(--border-default);
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
}
.table-hover tbody tr:hover {
  background-color: var(--surface-card-hover);
  color: var(--text-primary);
}
/* DataTables paints table rows + the sorted column white/near-white once it
   initialises (`table.dataTable tbody tr` and `… tbody tr > .sorting_N`, higher
   specificity than our base `.table`), which flashes light over the dark card
   on list pages (Services, Invoices, Tickets). Sit them transparently on the
   card instead; keep the hover accent. */
table.dataTable tbody tr {
  background-color: transparent;
}
/* The per-cell whites are the stubborn part — the non-sorted `.text-center`
   cells keep a base white, and the sorted `.sorting_N` cells a DataTables
   highlight, both out-weighing a plain override. Force every body cell
   transparent so the row/stripe colour shows through; keep the row-hover accent
   (which then shows through the transparent cells). */
table.dataTable tbody td,
table.dataTable tbody th,
table.dataTable tbody tr > .sorting_1,
table.dataTable tbody tr > .sorting_2,
table.dataTable tbody tr > .sorting_3 {
  background-color: transparent !important;
}
table.dataTable.table-hover tbody tr:hover {
  background-color: var(--surface-card-hover) !important;
}

/* ---------- Forms ---------- */
label {
  color: var(--text-secondary);
}
.form-control,
.custom-select {
  background-color: var(--surface-inset);
  border-color: var(--border-default);
  color: var(--text-primary);
}
.form-control:focus {
  background-color: var(--surface-inset);
  border-color: var(--orange-400);
  color: var(--text-primary);
  box-shadow: var(--focus-ring);
}
.form-control::placeholder {
  color: var(--text-muted);
}
.input-group-text {
  background-color: var(--surface-overlay);
  border-color: var(--border-default);
  color: var(--text-secondary);
}
/* Registration form (register.php): twenty-one's theme.min.css paints these
   inputs white via an ID rule `#registration .field, #registration .form-control`
   (1,1,0) that the base `.form-control` (0,1,0) can't outrank. Only the
   `.field.form-control` inputs escape (via the order-form !important rule); the
   `.field`-only ones (passwords) and `.form-control`-only one (captcha) stay
   white. Match the ID specificity — custom.css loads after theme.min.css, so
   this wins. */
#registration .field,
#registration .form-control {
  background-color: var(--surface-inset);
  border-color: var(--border-default);
  color: var(--text-primary);
}
#registration .field:focus,
#registration .form-control:focus {
  border-color: var(--orange-400);
  box-shadow: var(--focus-ring);
}
#registration .field::placeholder,
#registration .form-control::placeholder {
  color: var(--text-muted);
}

/* Disabled / readonly inputs (e.g. the pre-filled Name/Email on ticket forms).
   Bootstrap paints these #e9ecef via `.form-control:disabled, .form-control[readonly]`
   (0,2,0), out-ranking the base `.form-control` (0,1,0) — match it so they read
   as flat, muted dark fields rather than light boxes. */
.form-control:disabled,
.form-control[readonly],
.form-control.disabled,
.custom-select:disabled {
  background-color: var(--surface-base);
  border-color: var(--border-subtle);
  color: var(--text-muted);
  opacity: 1;
}

/* Markdown editor (ticket create + reply — WHMCS's `.md-editor`). twenty-one's
   editor CSS hardcodes the toolbar, textarea, footer + preview to white/#f5f5f5;
   the toolbar + textarea rules carry enough weight (higher-specificity /
   !important) that a plain `.md-editor .md-*` override loses to them, so force
   the backgrounds. The toolbar's own buttons (.btn-default) are already themed
   by the button rules below. */
.md-editor {
  border-color: var(--border-default);
}
.md-editor .md-header,
.md-editor .md-footer {
  background-color: var(--surface-inset) !important;
  border-color: var(--border-default);
}
.md-editor .md-input,
.md-editor textarea.markdown-editor,
.md-editor .md-preview {
  background-color: var(--surface-inset) !important;
  border-color: var(--border-default);
  color: var(--text-primary) !important;
}

/* Custom file input (ticket attachments): BS4's `.custom-file-label` + its
   ::after "Browse" button default to white / #e9ecef. */
.custom-file-label {
  background-color: var(--surface-inset);
  border-color: var(--border-default);
  color: var(--text-secondary);
}
.custom-file-label::after {
  background-color: var(--surface-overlay);
  border-color: var(--border-default);
  color: var(--text-secondary);
}

/* ---------- Buttons ---------- */
.btn-primary {
  background-color: var(--orange-500);
  border-color: var(--orange-500);
  color: var(--ink-950);
  font-weight: var(--weight-semibold);
}
.btn-primary:hover,
.btn-primary:focus {
  background-color: var(--orange-400);
  border-color: var(--orange-400);
  color: var(--ink-950);
}
.btn-primary:active,
.btn-primary:not(:disabled):not(.disabled):active,
.btn-primary:not(:disabled):not(.disabled).active,
.show > .btn-primary.dropdown-toggle {
  background-color: var(--orange-600);
  border-color: var(--orange-600);
  color: var(--ink-950);
}
.btn-primary.disabled,
.btn-primary:disabled {
  background-color: var(--orange-500);
  border-color: var(--orange-500);
  color: var(--ink-950);
}

.btn-default,
.btn-light {
  background-color: var(--surface-overlay);
  border-color: var(--border-default);
  color: var(--text-primary);
}
.btn-default:hover,
.btn-default:focus,
.btn-light:hover,
.btn-light:focus {
  background-color: var(--surface-card-hover);
  border-color: var(--border-strong);
  color: var(--text-primary);
}
.btn-default:active,
.btn-default.active,
.show > .btn-default.dropdown-toggle,
.btn-light:active,
.btn-light:not(:disabled):not(.disabled):active,
.btn-light:not(:disabled):not(.disabled).active,
.show > .btn-light.dropdown-toggle {
  background-color: var(--surface-base);
  border-color: var(--border-strong);
  color: var(--text-primary);
}

.btn-secondary {
  background-color: var(--violet-500);
  border-color: var(--violet-500);
  color: var(--text-on-violet);
}
.btn-secondary:hover,
.btn-secondary:focus {
  background-color: var(--violet-400);
  border-color: var(--violet-400);
  color: var(--text-on-violet);
}
.btn-secondary:active,
.btn-secondary:not(:disabled):not(.disabled):active,
.btn-secondary:not(:disabled):not(.disabled).active,
.show > .btn-secondary.dropdown-toggle {
  background-color: var(--violet-600);
  border-color: var(--violet-600);
  color: var(--text-on-violet);
}
.btn-secondary.disabled,
.btn-secondary:disabled {
  background-color: var(--violet-500);
  border-color: var(--violet-500);
  color: var(--text-on-violet);
}

.btn-link,
.btn-link:hover,
.btn-link:focus {
  color: var(--orange-500);
}

/* WHMCS uses btn-success (green) for positive actions (Update, Add, …); the
   brand reserves green for status, so make these the orange primary too. */
.btn-success {
  background-color: var(--orange-500);
  border-color: var(--orange-500);
  color: var(--ink-950);
}
.btn-success:hover,
.btn-success:focus {
  background-color: var(--orange-400);
  border-color: var(--orange-400);
  color: var(--ink-950);
}
.btn-success:active,
.btn-success:not(:disabled):not(.disabled):active,
.btn-success:not(:disabled):not(.disabled).active,
.show > .btn-success.dropdown-toggle {
  background-color: var(--orange-600);
  border-color: var(--orange-600);
  color: var(--ink-950);
}
.btn-success.disabled,
.btn-success:disabled {
  background-color: var(--orange-500);
  border-color: var(--orange-500);
  color: var(--ink-950);
}

/* ---------- Badges ---------- */
/* Bare (uncoloured) .badge — e.g. the product-status count pills in the
   services sidebar — default to twenty-one's white; give them a dark base.
   The .badge-* modifiers below still win for status colours. The sidebar
   selector matches twenty-one's `.sidebar .list-group-item .badge` (0,3,0)
   specificity so source order lets ours win. */
.badge,
.sidebar .list-group-item .badge {
  background-color: var(--surface-inset);
  color: var(--text-secondary);
}
.badge-success { background-color: var(--green-500); color: var(--ink-950); }
.badge-warning { background-color: var(--amber-400); color: var(--ink-950); }
.badge-danger  { background-color: var(--red-500);   color: var(--white); }
.badge-info    { background-color: var(--cyan-400);  color: var(--ink-950); }

/* ---------- Alerts ---------- */
.alert {
  border-width: 1px;
  border-style: solid;
}
.alert-success {
  background-color: var(--green-900);
  border-color: var(--green-500);
  color: var(--green-400);
}
.alert-info {
  background-color: rgba(56, 214, 224, 0.10);
  border-color: var(--cyan-400);
  color: var(--cyan-400);
}
.alert-warning {
  background-color: var(--amber-900);
  border-color: var(--amber-400);
  color: var(--amber-400);
}
.alert-danger {
  background-color: var(--red-900);
  border-color: var(--red-500);
  color: var(--red-400);
}

/* ---------- Modals / dropdowns ---------- */
.modal-content,
.dropdown-menu {
  background-color: var(--surface-overlay);
  border: 1px solid var(--border-default);
  color: var(--text-primary);
}
.modal-header,
.modal-footer {
  border-color: var(--border-subtle);
}
.dropdown-item {
  color: var(--text-secondary);
}
.dropdown-item:hover,
.dropdown-item:focus {
  background-color: var(--surface-card-hover);
  color: var(--text-primary);
}
.dropdown-header {
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
}
.close {
  color: var(--text-primary);
  text-shadow: none;
}

/* ============================================================
   SECOND PASS — utilities + components that twenty-one's .tpl
   files actually emit (counts from grep). These are the usual
   dark-theme leaks: Bootstrap background/text UTILITIES are
   `!important`, so overriding them requires `!important` too.
   ============================================================ */

/* Light background utilities → dark surfaces (the #1 leak: bg-white
   appears in 18 tpls, bg-light in 7). Text stays inherited (light). */
.bg-white,
.bg-light,
.bg-body {
  background-color: var(--surface-card) !important;
}
.bg-secondary {
  background-color: var(--surface-overlay) !important;
}
.text-dark,
.text-black-50,
.text-body {
  color: var(--text-secondary) !important;
}
.border,
.border-top,
.border-bottom,
.border-left,
.border-right {
  border-color: var(--border-subtle) !important;
}
.dropdown-divider {
  border-top-color: var(--border-subtle);
}

/* Striped / bordered tables (table-striped in 13 tpls). BS4's odd-row
   stripe is rgba(0,0,0,.05) — invisible on dark; use a light overlay. */
.table-striped tbody tr:nth-of-type(odd) {
  background-color: var(--grid-line);
}
.table-bordered,
.table-bordered th,
.table-bordered td {
  border-color: var(--border-subtle);
}

/* Tabs (nav-tabs in 8 tpls) */
.nav-tabs {
  border-bottom-color: var(--border-default);
}
.nav-tabs .nav-link {
  color: var(--text-secondary);
}
.nav-tabs .nav-link:hover {
  border-color: var(--border-subtle) var(--border-subtle) var(--border-default);
  color: var(--text-primary);
}
.nav-tabs .nav-link.active {
  color: var(--text-primary);
  background-color: var(--surface-card);
  border-color: var(--border-default) var(--border-default) var(--surface-card);
}
/* Hovering a tab (which includes the tab you just clicked — the pointer stays
   on it) turned it white: twenty-one's
   `.responsive-tabs-sm .nav-item a:hover {background-color:#f7f7f7;border-bottom:0}`
   is (0,3,1), so it outranks a `.nav-tabs .nav-link:hover` (0,3,0) override no
   matter the load order. Match that selector shape to win on source order, and
   keep the active tab's dark treatment while hovered/focused. */
.responsive-tabs-sm .nav-item a:hover,
.responsive-tabs-sm .nav-item a:focus {
  background-color: transparent;
  color: var(--text-primary);
}
.responsive-tabs-sm .nav-item a.active:hover,
.responsive-tabs-sm .nav-item a.active:focus {
  background-color: var(--surface-card);
  color: var(--text-primary);
  border-color: var(--border-default) var(--border-default) var(--surface-card);
}
.nav-pills .nav-link.active {
  background-color: var(--orange-500);
  color: var(--ink-950);
}

/* Product-details overview image box (clientarea.php?action=productdetails).
   The `.product-icon` placeholder ships a light-gray (#efefef) background with
   the product name overlaid in white — unreadable. Make it a dark surface so
   the white label + icon show. */
.product-icon {
  /* twenty-one sets `.product-status .product-icon` #efefef at a specificity a
     plain `.product-icon` can't reach — force it. */
  background-color: var(--surface-inset) !important;
  border: 1px solid var(--border-subtle);
  color: var(--text-primary);
}
/* The glyph is a FontAwesome stack: a solid circle with an "inverse" (knocked
   out) icon on top. Both default to white, so it read as a big white disc on
   the dark card. Sink the circle into a raised surface and let the icon carry
   the brand accent. */
.product-icon .fa-stack .fa-circle {
  color: var(--surface-card-hover);
}
.product-icon .fa-stack .fa-inverse {
  color: var(--orange-400);
}

/* List groups (list-group in 12 tpls) */
.list-group-item {
  background-color: var(--surface-card);
  border-color: var(--border-subtle);
  color: var(--text-secondary);
}
.list-group-item-action:hover,
.list-group-item-action:focus {
  background-color: var(--surface-card-hover);
  color: var(--text-primary);
}
.list-group-item.active {
  background-color: var(--orange-500);
  border-color: var(--orange-500);
  color: var(--ink-950);
}
/* Sidebar list is flush/transparent (see Sidebar block above). */
.sidebar .list-group-item {
  background-color: transparent;
}
/* Active sidebar nav item (current page): twenty-one paints it flat gray (#666),
   which reads off-theme. Use a subtle raised surface + orange text + an orange
   left-accent bar (inset shadow, so no layout shift). Solid orange is reserved
   for action buttons, so a nav item gets the lighter accent treatment. Matches
   theme.min.css's (0,3,0) specificity; custom.css loads after, so this wins. */
.sidebar .list-group-item.active,
.sidebar .list-group-item.active:hover,
.sidebar .list-group-item.active:focus {
  background-color: var(--surface-raised);
  border-color: var(--border-subtle);
  color: var(--orange-400);
  box-shadow: inset 3px 0 0 var(--orange-500);
}

/* Progress bars (progress in 5 tpls) */
.progress {
  background-color: var(--surface-inset);
}
.progress-bar {
  background-color: var(--orange-500);
}

/* Pagination */
.page-link {
  background-color: var(--surface-card);
  border-color: var(--border-subtle);
  color: var(--text-secondary);
}
.page-link:hover {
  background-color: var(--surface-card-hover);
  border-color: var(--border-default);
  color: var(--text-primary);
}
.page-item.active .page-link {
  background-color: var(--orange-500);
  border-color: var(--orange-500);
  color: var(--ink-950);
}
.page-item.disabled .page-link {
  background-color: var(--surface-inset);
  border-color: var(--border-subtle);
  color: var(--text-disabled);
}

/* BS3-style contextual labels (label-* in 10 tpls) + outline buttons */
.label {
  display: inline-block;
  padding: 0.25em 0.5em;
  font-size: 85%;
  font-weight: var(--weight-semibold);
  line-height: 1;
}
.label-success { background-color: var(--green-500); color: var(--ink-950); }
.label-warning { background-color: var(--amber-400); color: var(--ink-950); }
.label-danger  { background-color: var(--red-500);   color: var(--white); }
.label-info    { background-color: var(--cyan-400);  color: var(--ink-950); }
.label-default { background-color: var(--surface-overlay); color: var(--text-secondary); }

.btn-outline-primary {
  color: var(--orange-400);
  border-color: var(--orange-500);
}
.btn-outline-primary:hover {
  background-color: var(--orange-500);
  border-color: var(--orange-500);
  color: var(--ink-950);
}
.btn-outline-secondary {
  color: var(--text-secondary);
  border-color: var(--border-default);
}
.btn-outline-secondary:hover {
  background-color: var(--surface-card-hover);
  border-color: var(--border-strong);
  color: var(--text-primary);
}

/* BS3 well (1 tpl) */
.well {
  background-color: var(--surface-inset);
  border: 1px solid var(--border-subtle);
  color: var(--text-secondary);
}

/* ============================================================
   DASHBOARD (clientareahome) — twenty-one styles the stat tiles and
   home-card headers with light-specific, higher-specificity classes
   (#f8f8f8 / #fff) that the generic .card overrides don't reach.
   ============================================================ */
.tiles .tile {
  background-color: var(--surface-card);
  border-right-color: var(--border-subtle);
}
.tiles .tile:hover {
  background-color: var(--surface-card-hover);
}
.tiles .tile .stat {
  color: var(--text-primary);
}
.tiles .tile .title {
  color: var(--text-muted);
}
.tiles .tile i {
  color: var(--border-strong);
}
.tiles .tile:hover i {
  color: var(--text-muted);
}
.client-home-cards .card-header {
  background-color: var(--surface-inset);
  color: var(--text-primary);
}
.client-home-cards .list-group {
  border-top-color: var(--border-subtle) !important;
  border-bottom-color: var(--border-subtle) !important;
}
/* The rule BETWEEN rows. WHMCS writes this one as an INLINE style on every
   dashboard panel row (`<div id="ClientAreaHomePagePanels-…" style="border-bottom:
   1px solid rgb(221,221,221)">`), not from a stylesheet — so no selector, however
   specific, can outrank it and `!important` is the only lever. That left a bright
   white line between dashboard entries (Active Products, Recent Tickets/News).
   `.card …` covers the same list pattern in other cards. */
.client-home-cards .list-group .list-group-item,
.card .list-group .list-group-item {
  border-bottom-color: var(--border-subtle) !important;
}

/* ============================================================
   ORDER FORM (standard_cart) — the cart.php pages the site's "Deploy"
   deep-links land on. That order form is a separate WHMCS template
   with its own light/blue/green styling that leaks over the dark
   system theme; override its (cart-specific) classes here.
   ============================================================ */
/* NOTE: standard_cart ships its own stylesheet loaded AFTER custom.css (via
   $headoutput), so these need !important to win. */
/* Drop the order form's LEFT sidebar (.cart-sidebar — the "Categories" product-
   group nav + "Actions" panel). Product groups are funnelled to the marketing
   site (see psg-funnel.php), so that catalog nav is redundant on the cart. Its
   column-sibling .cart-body (cart items, promo, Order Summary) then spans full
   width. Cart-scoped classes, so this only affects cart.php pages. The float/width
   overrides matter where .cart-body is a plain block (the empty-cart view) rather
   than a flex row child — the stock float:right + width:75% would otherwise shove
   its content to the right of the now-hidden sidebar. */
.cart-sidebar { display: none !important; }
.cart-body {
  flex: 0 0 100% !important;
  max-width: 100% !important;
  width: 100% !important;
  float: none !important;
}
/* Product-table header (stock WHMCS blue #005588). */
.view-cart-items-header {
  background-color: var(--surface-inset) !important;
  color: var(--text-secondary);
  border: 1px solid var(--border-subtle);
}
/* Order Summary: outer wrapper is a gray (#666) heading bar; inner panel is
   light #f8f8f8. Wrapper → dark heading bar, inner → dark card. */
.order-summary {
  background-color: var(--surface-inset) !important;
}
.order-summary > h2,
.order-summary .font-size-30 {
  color: var(--text-primary);
}
.summary-container {
  background-color: var(--surface-card) !important;
  border: 1px solid var(--border-subtle) !important;
  color: var(--text-secondary);
}
.summary-container .cart-total,
.summary-container #recurring,
.summary-container .cost,
.summary-container .cart-total-due {
  color: var(--text-primary);
}
.summary-container .subtotal,
.summary-container .recurring-totals,
.summary-container .tax {
  border-color: var(--border-subtle) !important;
}
/* Checkout = brand primary (was btn-success green). */
.btn-checkout,
.btn-checkout.btn-success {
  background-color: var(--orange-500) !important;
  border-color: var(--orange-500) !important;
  color: var(--ink-950) !important;
}
.btn-checkout:hover,
.btn-checkout:focus {
  background-color: var(--orange-400) !important;
  border-color: var(--orange-400) !important;
  color: var(--ink-950) !important;
}
/* Promo-code panel (stock light tab + white input), scoped to the cart. */
.secondary-cart-body,
.secondary-cart-body .tab-content,
.view-cart-tabs .tab-content {
  background-color: transparent !important;
}
.view-cart-tabs .nav-link.active,
.secondary-cart-body .nav-link.active {
  background-color: var(--surface-card) !important;
  color: var(--text-primary) !important;
}
.view-cart-tabs .form-control,
.secondary-cart-body .form-control,
.field.form-control {
  background-color: var(--surface-inset) !important;
  border-color: var(--border-default) !important;
  color: var(--text-primary) !important;
}

/* ============================================================
   ORDER FORM (standard_cart) — CONFIGURE PRODUCT page
   (cart.php?a=confproduct — where the site's store/Deploy links land).
   Zero-price hiding on the summary is done in js/psg-cart.js.
   ============================================================ */
/* The mobile sidebar. standard_cart swaps the desktop `.cart-sidebar` (hidden
   above) for `.sidebar-collapsed` (the Categories/Actions panels) on small
   screens. Product groups are funnelled to the marketing site, so drop it at
   every width, same as the desktop sidebar. */
.sidebar-collapsed { display: none !important; }

/* Product-info panel (product name/description above the options) ships a light
   `#f8f8f8` box with a near-white border — a bright slab on the dark theme. */
.product-info {
  background-color: var(--surface-inset) !important;
  border-color: var(--border-subtle) !important;
}
.product-info .product-title {
  color: var(--text-primary);
  font-family: var(--font-display);
  font-weight: var(--weight-semibold);
}

/* Remove the "Configurable Options" divider — the options sit directly under the
   product panel, so the labelled rule line is just noise. */
.secondary-cart-body > .sub-heading { display: none !important; }

/* standard_cart lays each option out in a half-width Bootstrap column; let each
   span the full config width so its buttons fill across in one row. */
.product-configurable-options .row > [class*="col-"] {
  flex: 0 0 100%;
  max-width: 100%;
}

/* Render radio/checkbox config options as a horizontal fill-across button group
   instead of a vertical iCheck radio list. Each option is a <label> wrapping an
   iCheck control + text, separated by <br>; the field-name label carries a
   `for`. Scope the flex layout to groups that actually contain option labels so
   dropdown/quantity options are left as-is. */
.product-configurable-options .form-group:has(> label:not([for])) {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.product-configurable-options .form-group:has(> label:not([for])) > br { display: none; }
.product-configurable-options .form-group:has(> label:not([for])) > label[for] {
  flex: 0 0 100%;
  margin: 0 0 10px;
  font-family: var(--font-display);
  /* standard_cart sets label font-size/colour/weight at higher specificity. */
  font-size: var(--text-lg) !important;
  font-weight: var(--weight-semibold) !important;
  color: var(--text-primary) !important;
}
.product-configurable-options .form-group > label:not([for]) {
  position: relative;
  overflow: hidden;
  flex: 1 1 0;
  min-width: 130px;
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 12px 14px;
  border: 1px solid var(--border-default);
  background: var(--surface-inset);
  /* standard_cart colours labels #666 at higher specificity — override it. */
  color: var(--text-secondary) !important;
  font-size: var(--text-sm) !important;
  font-weight: var(--weight-medium) !important;
  cursor: pointer;
  transition: border-color 120ms, background-color 120ms, color 120ms;
}
.product-configurable-options .form-group > label:not([for]):hover {
  border-color: var(--border-strong);
  color: var(--text-primary) !important;
}
/* Turn the iCheck control into an invisible full-button overlay: it keeps being
   the click target (its input/ins scale to fill), but the sprite circle is gone,
   so the whole button toggles the option. */
.product-configurable-options .form-group > label:not([for]) > [class*="iradio_"],
.product-configurable-options .form-group > label:not([for]) > [class*="icheckbox_"] {
  position: absolute !important;
  top: 0 !important;
  left: 0 !important;
  width: 100% !important;
  height: 100% !important;
  margin: 0 !important;
  background: none !important;
}
/* Selected option — iCheck toggles `.checked` on the inner control live. Show it
   with an orange outline + accent text (no solid fill). The inset shadow thickens
   the edge to ~2px without shifting layout. */
.product-configurable-options .form-group > label:not([for]):has(.checked) {
  border-color: var(--orange-500);
  box-shadow: inset 0 0 0 1px var(--orange-500);
  color: var(--orange-400) !important;
  font-weight: var(--weight-semibold) !important;
}

/* Drop standard_cart's "Have questions? Contact our sales team" notice
   (.info-text-sm) — the site already routes support through its own Contact
   page/chrome, so it's redundant here. Order-form-scoped + its unique class. */
#order-standard_cart .alert.info-text-sm { display: none !important; }

/* Inline alert links (e.g. in validation notices) inherit a dark Bootstrap link
   colour that's unreadable on the tinted alerts. Match the alert text + underline
   so they stay legible across the client area. */
.alert .alert-link {
  color: inherit !important;
  text-decoration: underline;
  font-weight: var(--weight-semibold);
}

/* Order Summary (#producttotal) — JS-rendered on the configure page; different
   markup from the view-cart summary, so it needs its own polish. ID-scoped, so
   it outranks standard_cart without !important. */
#producttotal { padding: 16px 18px; }
#producttotal .product-name {
  display: block;
  font-family: var(--font-display);
  font-weight: var(--weight-semibold);
  font-size: var(--text-lg);
  color: var(--text-primary);
}
#producttotal .product-group {
  display: block;
  margin-bottom: 12px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--border-subtle);
  font-size: var(--text-xs);
  color: var(--text-muted);
}
#producttotal .clearfix {
  display: flex;
  justify-content: space-between;
  gap: 14px;
  padding: 7px 0;
  font-size: var(--text-sm);
  color: var(--text-secondary);
}
#producttotal .clearfix .float-right,
#producttotal .clearfix .pull-right {
  color: var(--text-primary);
  white-space: nowrap;
}
#producttotal .summary-totals {
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px solid var(--border-subtle);
}
#producttotal .total-due-today {
  margin-top: 12px;
  padding-top: 14px;
  border-top: 1px solid var(--border-default);
  text-align: right;
}
#producttotal .total-due-today .amt {
  display: block;
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
  font-size: var(--text-2xl);
  color: var(--text-primary);
  line-height: 1.1;
}
#producttotal .total-due-today > span:last-child {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--text-muted);
}
/* When JS hides a zero price, drop the empty gap so the label sits alone. */
#producttotal .clearfix .psg-price-hidden { display: none; }

/* ---------- Product addons (configure step, #productAddonsContainer) ----------
   standard_cart ships these as bare `.panel-addon` boxes: name, description,
   price and "Add to Cart" all stack as same-weight text with no padding on the
   last two, so the price reads like a caption and the call to action reads like
   a label rather than something you can click. Nothing marks the selected card
   either — only the checkbox sprite changes — and the cards in a row don't line
   up because each is only as tall as its own copy.

   Restyle to the site's card language: one padded surface, a clear name →
   description → footer hierarchy, price and action side by side on a divided
   footer row, and an unmistakable selected state.

   Interaction is stock and must not be fought (standard_cart/js/scripts.js,
   registerAddonEvents): the WHOLE card is the click target, iCheck owns the
   checkbox, and the JS rewrites `.panel-add` between "Add to Cart" and "Added
   to Cart (Remove)" while toggling `.panel-addon-selected` on the card. So
   selection is styled off that class — never off a text string — and the real
   <input> stays in the DOM and focusable, with the card taking a :focus-within
   ring so keyboard users get the same affordance the mouse gets. */

/* Equal-height cards across a row, and a gutter between wrapped rows. */
#productAddonsContainer .addon-products {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4);
}
#productAddonsContainer .addon-products > [class*="col-"] {
  display: flex;
  width: auto !important;
  max-width: none !important;
  padding: 0 !important;
  margin: 0;
}
#productAddonsContainer .addon-products > .col-sm-12 {
  grid-column: 1 / -1;
}

#productAddonsContainer .panel-addon {
  display: grid !important;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-rows: minmax(0, 1fr) auto;
  width: 100%;
  margin: 0 !important;
  padding: 0 !important;
  background: var(--surface-card) !important;
  border: 1px solid var(--border-default) !important;
  border-radius: var(--radius-none) !important;
  box-shadow: none !important;
  cursor: pointer;
  transition:
    border-color var(--duration-fast) var(--ease-out),
    background-color var(--duration-fast) var(--ease-out);
}
#productAddonsContainer .panel-addon:hover {
  background: var(--surface-card-hover) !important;
  border-color: var(--border-strong) !important;
}
/* Body grows so the footer row below pins to the bottom of every card. */
#productAddonsContainer .panel-addon .panel-body {
  grid-column: 1 / -1;
  grid-row: 1;
  min-width: 0;
  padding: var(--space-5) !important;
  /* The divider lives here, not on the two footer children: they're separate
     elements pushed apart by auto margins, so a border-top on each draws the
     line in two pieces with a gap in the middle. The body spans the full card
     width, so its bottom edge is one continuous rule. */
  border-bottom: 1px solid var(--border-subtle);
}

/* Addon name — the label carries the checkbox, so lay it out as a row and let
   the name take display type. `<br>` after it is neutralised; spacing is ours. */
#productAddonsContainer .panel-addon .panel-body > label {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: 0 0 var(--space-2) !important;
  cursor: pointer;
  font-family: var(--font-display);
  font-size: var(--text-base);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-snug);
  color: var(--text-primary) !important;
}
#productAddonsContainer .panel-addon .panel-body > br { display: none; }
/* Description: everything after the label in the body. */
#productAddonsContainer .panel-addon .panel-body {
  font-size: var(--text-sm);
  line-height: 1.55;
  color: var(--text-secondary) !important;
}

/* The checkbox. iCheck swaps the native input for a sprite div whose skin name
   varies by page, so match on the class prefix the way the card-selection
   radios above do, and drop the sprite for a square box in brand colours.
   `accent-color` covers the native input for any context where iCheck hasn't
   initialised, so the control is never invisible. */
#productAddonsContainer .panel-addon input[type="checkbox"] {
  accent-color: var(--orange-500);
  width: 16px;
  height: 16px;
  margin: 0 !important;
  flex-shrink: 0;
  cursor: pointer;
}
#productAddonsContainer .panel-addon [class*="icheckbox_"] {
  position: relative !important;
  flex-shrink: 0;
  width: 16px !important;
  height: 16px !important;
  margin: 0 !important;
  background: none !important;
  border: 2px solid var(--border-strong) !important;
}
#productAddonsContainer .panel-addon [class*="icheckbox_"].checked {
  background: var(--orange-500) !important;
  border-color: var(--orange-500) !important;
}
/* CSS-only tick (FontAwesome isn't guaranteed inside the sprite element). */
#productAddonsContainer .panel-addon [class*="icheckbox_"].checked::after {
  content: '';
  position: absolute;
  top: 1px;
  left: 4px;
  width: 4px;
  height: 8px;
  border: solid var(--text-on-brand);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

/* Footer row: price left, action right, divided off the body. Both are direct
   children of the card, so they share the flex row that .panel-body's
   `flex-basis: 100%` pushed them onto. */
#productAddonsContainer .panel-addon .panel-price,
#productAddonsContainer .panel-addon .panel-add {
  padding: var(--space-3) var(--space-5) !important;
  background: none !important;
}
#productAddonsContainer .panel-addon .panel-price {
  grid-column: 1;
  grid-row: 2;
  margin-right: 0;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--text-primary) !important;
  white-space: nowrap;
}
#productAddonsContainer .panel-addon .panel-add {
  grid-column: 2;
  grid-row: 2;
  margin-left: 0;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--orange-400) !important;
  white-space: nowrap;
}

/* Selected: orange edge + a wash, so a scan of the list shows what's already on
   without reading any text. The inset shadow thickens the border to ~2px
   without shifting layout — same trick as the configurable options above. */
#productAddonsContainer .panel-addon-selected,
#productAddonsContainer .panel-addon-selected:hover {
  border-color: var(--orange-500) !important;
  box-shadow: inset 0 0 0 1px var(--orange-500) !important;
  background: color-mix(in srgb, var(--orange-500) 7%, var(--surface-card)) !important;
}
#productAddonsContainer .panel-addon-selected .panel-body {
  border-bottom-color: color-mix(in srgb, var(--orange-500) 30%, transparent);
}

/* Keyboard parity: the card is a click target for the mouse, so it takes the
   same visible state when the input inside it is focused. Placed AFTER the
   selected rules and restated for the selected case on purpose — both set
   box-shadow with !important, so whichever comes last wins outright, and a
   selected card would otherwise swallow the focus ring entirely. */
#productAddonsContainer .panel-addon:focus-within {
  box-shadow: var(--focus-ring) !important;
}
#productAddonsContainer .panel-addon-selected:focus-within {
  box-shadow: inset 0 0 0 1px var(--orange-500), var(--focus-ring) !important;
}

/* Single addon (WHMCS emits col-sm-12) — cap the measure so the description
   doesn't run the full content width as one long line. */
#productAddonsContainer .addon-products > .col-sm-12 .panel-addon {
  max-width: 640px;
}

@media (max-width: 575px) {
  #productAddonsContainer .addon-products { grid-template-columns: minmax(0, 1fr); }
  #productAddonsContainer .panel-addon .panel-body { padding: var(--space-4) !important; }
  #productAddonsContainer .panel-addon .panel-price,
  #productAddonsContainer .panel-addon .panel-add { padding: var(--space-3) var(--space-4) !important; }
}

/* ============================================================
   ORDER FORM (standard_cart) — VIEW CART + CHECKOUT pages
   (cart.php?a=view / a=checkout). standard_cart ships light panels,
   a white cart row, a white payment box + blue section labels that
   leak over the dark theme. standard_cart's sheet loads AFTER
   custom.css, so these need !important.
   ============================================================ */

/* Cart line-item row — a solid white `.item` panel → dark card (stock view-cart;
   the combined order form uses its own .psg-summary-item, styled below). */
#order-standard_cart .item {
  background-color: var(--surface-card) !important;
  border: 1px solid var(--border-subtle) !important;
  color: var(--text-secondary) !important;
}
#order-standard_cart .item .item-title,
#order-standard_cart .item .item-title > span:first-child {
  color: var(--text-primary) !important;
}

/* Section headings (`.sub-heading`): stock is a centred lined divider with a
   blue chip — dated. Modernize to a clean, left-aligned brand section label,
   no rule line. (The configure page's own .sub-heading is hidden above.) */
.sub-heading {
  border: 0 !important;
  text-align: left !important;
  margin: 30px 0 14px !important;
  line-height: 1 !important;
}
.sub-heading .primary-bg-color {
  background: none !important;
  padding: 0 !important;
  color: var(--text-muted) !important;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
}

/* Clean, consistent form fields across the order form: drop the prepend input
   icons (person/mail/lock…); force dark inputs + ONE themed border on every
   input/select/textarea (fixes stock light-#ddd borders + the white state
   select); branded focus ring. StripeElement card fields are excluded (they
   stay light — see below). Tel inputs keep intl-tel-input's own flag padding. */
#order-standard_cart .prepend-icon .field-icon { display: none !important; }
#order-standard_cart .prepend-icon .form-control:not([type="tel"]) { padding-left: 12px !important; }
#order-standard_cart .form-control:not(.StripeElement),
#order-standard_cart .field:not(.StripeElement),
#order-standard_cart .custom-select,
#order-standard_cart select,
#order-standard_cart textarea {
  background-color: var(--surface-inset) !important;
  border: 1px solid var(--border-default) !important;
  color: var(--text-primary) !important;
  height: auto !important;
}
#order-standard_cart .form-control:not(.StripeElement) {
  padding-top: 9px !important;
  padding-bottom: 9px !important;
}
#order-standard_cart .form-control:focus,
#order-standard_cart .custom-select:focus,
#order-standard_cart textarea:focus {
  border-color: var(--orange-400) !important;
  box-shadow: var(--focus-ring) !important;
}

/* Signed-in checkout account selector. standard_cart otherwise leaves these as
   white/gray rows with the stock blue iCheck sprite. Treat each account as a
   dark selectable card and use the same orange radio language as saved cards. */
#order-standard_cart #containerExistingAccountSelect {
  display: grid !important;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3);
  margin: 0 !important;
  border: 0 !important;
}
#order-standard_cart #containerExistingAccountSelect > [class*="col-"] {
  width: auto !important;
  max-width: none !important;
  padding: 0 !important;
}
#order-standard_cart #containerExistingAccountSelect > .col-sm-12:last-child {
  grid-column: 1 / -1;
}
#order-standard_cart #containerExistingAccountSelect > .col-sm-12:first-child:nth-last-child(2) {
  grid-column: 1 / -1;
}
#order-standard_cart #containerExistingAccountSelect .account {
  height: 100%;
  padding: 0 !important;
  background: var(--surface-inset) !important;
  border: 1px solid var(--border-default) !important;
  color: var(--text-secondary) !important;
  transition: background-color var(--duration-fast), border-color var(--duration-fast);
}
#order-standard_cart #containerExistingAccountSelect .account:hover {
  background: var(--surface-card-hover) !important;
  border-color: var(--border-strong) !important;
}
#order-standard_cart #containerExistingAccountSelect .account.active {
  background: var(--surface-card) !important;
  border-color: var(--orange-500) !important;
  box-shadow: inset 0 0 0 1px var(--orange-500);
}
#order-standard_cart #containerExistingAccountSelect .radio-inline {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  width: 100%;
  height: 100%;
  margin: 0 !important;
  padding: var(--space-4) !important;
  color: var(--text-primary) !important;
  cursor: pointer;
}
#order-standard_cart #containerExistingAccountSelect .radio-inline > [class*="iradio_"] {
  position: relative !important;
  flex: 0 0 18px;
  width: 18px !important;
  height: 18px !important;
  margin: 2px 0 0 !important;
  background: none !important;
  border: 2px solid var(--border-strong) !important;
  border-radius: 50%;
}
#order-standard_cart #containerExistingAccountSelect .radio-inline > [class*="iradio_"].checked,
#order-standard_cart #containerExistingAccountSelect .radio-inline > [class*="iradio_"].focus {
  border-color: var(--orange-500) !important;
}
#order-standard_cart #containerExistingAccountSelect .radio-inline > [class*="iradio_"].checked::after {
  content: '';
  position: absolute;
  inset: 0;
  width: 8px;
  height: 8px;
  margin: auto;
  background: var(--orange-500);
  border-radius: 50%;
}
#order-standard_cart #containerExistingAccountSelect .address {
  flex: 1 1 auto;
  min-width: 0;
  color: var(--text-secondary) !important;
  line-height: var(--leading-normal);
}
#order-standard_cart #containerExistingAccountSelect .address strong {
  color: var(--text-primary) !important;
  font-family: var(--font-display);
  font-weight: var(--weight-semibold);
}
#order-standard_cart #containerExistingAccountSelect .address .small {
  display: inline-block;
  margin-top: var(--space-1);
  color: var(--text-muted) !important;
}
#order-standard_cart #containerExistingAccountSelect .label-info {
  margin-left: var(--space-2);
  background: var(--surface-overlay) !important;
  border: 1px solid var(--orange-500);
  border-radius: 0 !important;
  color: var(--orange-400) !important;
  font-family: var(--font-mono);
}
#order-standard_cart #containerExistingAccountSelect .disabled {
  cursor: not-allowed !important;
  opacity: 0.55;
}
@media (max-width: 767px) {
  #order-standard_cart #containerExistingAccountSelect {
    grid-template-columns: 1fr;
  }
  #order-standard_cart #containerExistingAccountSelect > .col-sm-12:last-child {
    grid-column: auto;
  }
}

/* Phone field escapes the dark `.form-control` styling (intl-tel-input keeps it
   white + a light flag dropdown / country list). */
#order-standard_cart input[type="tel"],
.intl-tel-input input.form-control {
  background-color: var(--surface-inset) !important;
  color: var(--text-primary) !important;
}
.intl-tel-input .selected-flag { background-color: var(--surface-overlay) !important; }
.intl-tel-input .country-list {
  background-color: var(--surface-overlay) !important;
  color: var(--text-primary) !important;
  border-color: var(--border-default) !important;
}
.intl-tel-input .country-list .country.highlight,
.intl-tel-input .country-list .country:hover {
  background-color: var(--surface-card-hover) !important;
}
.intl-tel-input .country-list .divider { border-color: var(--border-subtle) !important; }
.intl-tel-input .country-list .country .dial-code { color: var(--text-muted) !important; }

/* Payment-method panel ships a light #f3f3f3 box → dark it. Keep the Stripe
   Element inputs light: their text renders in a cross-origin iframe we can't
   style, so a light field keeps the typed card number readable. */
.cc-input-container {
  background-color: var(--surface-inset) !important;
  border: 1px solid var(--border-subtle) !important;
}
.cc-input-container label,
.cc-input-container p,
.cc-input-container .card-preview-name {
  color: var(--text-secondary) !important;
}
/* Only the card INPUT fields (form-control StripeElement) stay light — NOT the
   bare .StripeElement Link button, which keeps its own (transparent) frame.
   These MUST stay light: Stripe renders the typed card number as DARK text
   inside a cross-origin iframe (its colour is set by the Stripe gateway module,
   not our theme), so a dark field would be unreadable. Softened off-white. */
.cc-input-container .form-control.StripeElement {
  background-color: #f3f3f6 !important;
  border: 1px solid #c9c9d2 !important;
}
/* Total Due Today green bar — hidden: the total lives in the order-summary
   sidebar now. (The live #totalCartPrice inside it still updates and is mirrored
   to the sidebar by psg-cart.js, so hiding the bar doesn't break the sync.) */
#order-standard_cart #totalDueToday { display: none !important; }
/* Remove standard_cart's "secure environment / your IP is logged" notice. */
.checkout-security-msg { display: none !important; }
/* Remove the Additional Notes field (its heading + textarea row). Scoped via the
   notes textarea + child combinators so it targets ONLY that row, not the outer
   .row that wraps the whole form. */
#order-standard_cart .row:has(> .col-sm-12 > .form-group > textarea[name="notes"]) { display: none !important; }
#order-standard_cart .sub-heading:has(+ .row > .col-sm-12 > .form-group > textarea[name="notes"]) { display: none !important; }

/* Payment-method selector: iCheck radios → themed segmented buttons (the same
   treatment as the configure-product options). */
#paymentGatewaysContainer { text-align: center; }
#paymentGatewaysContainer > .text-center {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
  margin-top: 8px;
}
#paymentGatewaysContainer .radio-inline {
  position: relative;
  overflow: hidden;
  margin: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 160px;
  padding: 11px 22px;
  border: 1px solid var(--border-default);
  background: var(--surface-inset);
  color: var(--text-secondary);
  font-size: var(--text-sm);
  cursor: pointer;
  transition: border-color 120ms, color 120ms;
}
#paymentGatewaysContainer .radio-inline:hover { border-color: var(--border-strong); color: var(--text-primary); }
#paymentGatewaysContainer .radio-inline > [class*="iradio_"] {
  position: absolute !important;
  top: 0 !important;
  left: 0 !important;
  width: 100% !important;
  height: 100% !important;
  margin: 0 !important;
  background: none !important;
}
#paymentGatewaysContainer .radio-inline:has(.checked) {
  border-color: var(--orange-500);
  box-shadow: inset 0 0 0 1px var(--orange-500);
  color: var(--orange-400);
  font-weight: var(--weight-semibold);
}

/* Card-selection radios (Enter new card / saved cards) → a themed orange radio
   indicator, replacing iCheck's blue sprite. */
#creditCardInputFields ul { list-style: none; padding: 0; margin: 0 0 16px; }
#creditCardInputFields .radio-inline { display: inline-flex; align-items: center; gap: 9px; }
#creditCardInputFields .radio-inline > [class*="iradio_"],
#creditCardInputFields .radio-inline > [class*="icheckbox_"] {
  position: relative !important;
  background: none !important;
  width: 16px !important;
  height: 16px !important;
  margin: 0 !important;
  border: 2px solid var(--border-strong) !important;
  border-radius: 50%;
}
#creditCardInputFields .radio-inline > [class*="iradio_"].checked,
#creditCardInputFields .radio-inline > [class*="icheckbox_"].checked { border-color: var(--orange-500) !important; }
#creditCardInputFields .radio-inline > [class*="iradio_"].checked::after {
  content: '';
  position: absolute;
  inset: 0;
  margin: auto;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--orange-500);
}

/* Mailing-list opt-in: no card/header — just a plain orange checkbox beside the
   copy. psg-cart.js destroys the stock bootstrap-switch to reveal the native
   checkbox this styles. */
.marketing-email-optin {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 22px 0;
}
.marketing-email-optin h4 { display: none; }
.marketing-email-optin p { order: 2; margin: 0 !important; font-size: var(--text-sm); color: var(--text-secondary); }
.marketing-email-optin input[name="marketingoptin"] {
  order: 1;
  flex: 0 0 auto;
  width: 17px;
  height: 17px;
  margin: 2px 0 0;
  accent-color: var(--orange-500);
  cursor: pointer;
}

/* Space the Complete Order button off the payment panel above it. */
#order-standard_cart .text-center:has(#btnCompleteOrder) { margin-top: 34px; }

/* Secondary buttons — the login/register toggle (Already Registered? = .btn-info,
   Create a New Account = .btn-warning) and Empty Cart (#btnEmptyCart, a btn-link
   standard_cart paints #005588) → one neutral dark treatment. #btnEmptyCart is
   unscoped (unique id) so it works in the combined review too. */
#order-standard_cart .btn-info,
#order-standard_cart .btn-warning,
#btnEmptyCart {
  background-color: var(--surface-overlay) !important;
  border-color: var(--border-default) !important;
  color: var(--text-secondary) !important;
}
#order-standard_cart .btn-info:hover,
#order-standard_cart .btn-info:focus,
#order-standard_cart .btn-warning:hover,
#order-standard_cart .btn-warning:focus,
#btnEmptyCart:hover,
#btnEmptyCart:focus {
  background-color: var(--surface-card-hover) !important;
  border-color: var(--border-strong) !important;
  color: var(--text-primary) !important;
}
/* ...except the "Sign in" toggle (renamed from "Already Registered?" by
   psg-cart.js) — highlighted orange to stand out as the returning-customer path.
   Two ids to outrank the neutral .btn-info rule above. */
#order-standard_cart #btnAlreadyRegistered {
  background-color: var(--orange-500) !important;
  border-color: var(--orange-500) !important;
  color: var(--ink-950) !important;
  font-weight: var(--weight-semibold) !important;
}
#order-standard_cart #btnAlreadyRegistered:hover,
#order-standard_cart #btnAlreadyRegistered:focus {
  background-color: var(--orange-400) !important;
  border-color: var(--orange-400) !important;
  color: var(--ink-950) !important;
}

/* ============================================================
   COMBINED ORDER FORM (orderforms/psg-orderform) — two-column checkout:
   the account/billing/payment form (inherited checkout.tpl) is the MAIN
   column; a sticky Order Summary card (items + promo + totals) is the
   aside. See orderforms/psg-orderform/viewcart.tpl.
   ============================================================ */
.psg-checkout-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 380px;
  gap: 44px;
  align-items: start;
}
/* checkout.tpl's Bootstrap grid (.row has -15px gutters) isn't wrapped in a
   15px-padded .container inside our grid column, so its outer row would bleed
   15px past the column — a horizontal overflow, most visible on mobile. Zero the
   OUTER row's negative gutters (the .cart-body col keeps its 15px padding, which
   absorbs the inner field rows' gutters and aligns fields with the headings). */
.psg-checkout-main > #order-standard_cart > .row {
  margin-left: 0 !important;
  margin-right: 0 !important;
}

/* Sticky Order Summary card */
.psg-checkout-summary { position: sticky; top: 76px; }
.psg-summary-card {
  background: var(--surface-card);
  border: 1px solid var(--border-subtle);
  padding: 22px 22px 24px;
}
.psg-summary-title {
  font-family: var(--font-display);
  font-weight: var(--weight-semibold);
  font-size: var(--text-lg);
  color: var(--text-primary);
  margin: 0;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--border-subtle);
}

/* Line items */
.psg-summary-item {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 16px 0;
  border-bottom: 1px solid var(--border-subtle);
}
.psg-summary-item-info { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.psg-summary-item-name { font-size: var(--text-sm); font-weight: var(--weight-semibold); color: var(--text-primary); display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.psg-summary-actions { display: inline-flex; align-items: center; gap: 8px; }
.psg-summary-edit { display: inline-flex; align-items: center; padding: 0; background: none; border: 0; cursor: pointer; font-size: var(--text-xs); line-height: 1; color: var(--text-muted); text-decoration: none; }
.psg-summary-edit:hover { color: var(--orange-400); text-decoration: none; }
.psg-summary-actions .psg-summary-remove { margin-top: 0; font-size: var(--text-xs); }
.psg-summary-item-group { font-size: var(--text-xs); color: var(--text-secondary); }
.psg-summary-item-sub { font-size: var(--text-xs); color: var(--text-muted); }
/* Config options: a two-column grid so every value lines up in the same column */
.psg-summary-options { display: grid; grid-template-columns: auto 1fr; column-gap: 12px; row-gap: 3px; margin-top: 3px; }
.psg-summary-opt-name { font-size: var(--text-xs); color: var(--text-muted); }
.psg-summary-opt-val { font-size: var(--text-xs); color: var(--text-secondary); font-weight: var(--weight-medium); min-width: 0; word-break: break-word; }
.psg-summary-item-price { display: flex; flex-direction: column; align-items: flex-end; gap: 1px; text-align: right; white-space: nowrap; }
.psg-summary-item-amt { font-size: var(--text-sm); color: var(--text-primary); }
.psg-summary-item-cycle,
.psg-summary-item-setup { font-size: var(--text-xs); color: var(--text-muted); }
.psg-summary-remove { margin-top: 4px; padding: 0; background: none; border: 0; cursor: pointer; color: var(--text-muted); font-size: var(--text-sm); }
.psg-summary-remove:hover { color: var(--red-400); }
.psg-summary-empty { padding: 16px 0; color: var(--text-muted); }

/* Promo */
.psg-summary-promo { padding: 16px 0; border-bottom: 1px solid var(--border-subtle); }
.psg-summary-promo-form { display: flex; gap: 8px; }
.psg-summary-promo-form .form-control { flex: 1 1 auto; min-width: 0; }
.psg-summary-promo-form .btn { flex: 0 0 auto; }
.psg-summary-promo-applied { display: flex; justify-content: space-between; align-items: center; gap: 12px; font-size: var(--text-sm); color: var(--text-secondary); }
.psg-summary-promo-applied a { font-size: var(--text-xs); color: var(--text-muted); }
.psg-summary-promo-applied a:hover { color: var(--orange-400); }
.psg-summary-promo-msg { font-size: var(--text-xs); margin-bottom: 8px; }

/* Totals */
.psg-summary-totals { padding-top: 16px; }
.psg-summary-row { display: flex; justify-content: space-between; gap: 12px; padding: 5px 0; font-size: var(--text-sm); color: var(--text-secondary); }
.psg-summary-grand {
  margin-top: 10px;
  padding-top: 16px;
  border-top: 1px solid var(--border-default);
  color: var(--text-primary);
  font-weight: var(--weight-semibold);
}
.psg-summary-grand > span:last-child { font-family: var(--font-display); font-size: var(--text-xl); }

/* Single column below 1200px — the 380px summary column only leaves the form
   enough room (≥~776px, no cramped card fields; no short-login-form-vs-tall-
   sidebar awkwardness) once the content grid is at its 1200px max. Below that,
   stack with the summary on top. */
@media (max-width: 1199.98px) {
  .psg-checkout-grid { grid-template-columns: 1fr; gap: 8px; }
  .psg-checkout-summary { position: static; order: -1; margin-bottom: 24px; }
}

/* ============================================================
   DATATABLES (invoices, emails, and other paginated lists) — the
   jQuery DataTables plugin adds white/light search inputs, sort
   headers, and empty-cells over the dark theme. Its stylesheet loads
   after custom.css, so !important.
   ============================================================ */
.dataTables_wrapper .form-control,
table.dataTable .form-control-sm,
.dataTables_filter input,
.dataTables_length select {
  background-color: var(--surface-inset) !important;
  border-color: var(--border-default) !important;
  color: var(--text-primary) !important;
}
/* twenty-one bakes the search magnifying-glass onto a WHITE tile as the input's
   background-image — a light square against the dark input. Replace it with a
   transparent, light-stroke SVG (a data-URI can't reference CSS vars, so the
   stroke hex mirrors --text-secondary #b8b6c6). */
.dataTables_filter input[type="search"] {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23b8b6c6' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cpath d='m21 21-4.3-4.3'/%3E%3C/svg%3E") !important;
  background-repeat: no-repeat !important;
  background-position: 10px center !important;
  background-size: 15px !important;
  padding-left: 32px !important;
}
table.dataTable thead .sorting,
table.dataTable thead .sorting_asc,
table.dataTable thead .sorting_desc,
table.dataTable thead .sorting_disabled,
.sorting, .sorting_asc, .sorting_desc, .sorting_disabled {
  background-color: transparent !important;
  color: var(--text-muted);
}
table.dataTable td.dataTables_empty,
.dataTables_empty {
  background-color: transparent !important;
  color: var(--text-muted);
}
/* The DataTable itself carries a light #ddd border on all sides. */
table.dataTable,
table.dataTable.no-footer,
.table-list {
  border-color: var(--border-subtle) !important;
}
.dataTables_wrapper .dataTables_info,
.dataTables_wrapper .dataTables_length,
.dataTables_wrapper .dataTables_filter {
  color: var(--text-secondary);
}
.dataTables_wrapper .dataTables_paginate .paginate_button {
  color: var(--text-secondary) !important;
}
.dataTables_wrapper .dataTables_paginate .paginate_button.current {
  background: var(--surface-card) !important;
  border-color: var(--border-default) !important;
  color: var(--text-primary) !important;
}

/* ---------- Preview-only helpers (harmless in prod: these classes
   exist in whmcs/_preview/index.html to stand in for dashboard tiles;
   real WHMCS uses its own dashboard markup). ---------- */
.stat-value {
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
  font-size: var(--text-2xl);
  color: var(--text-primary);
}
.stat-label {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* ============================================================================
   EMBED MODE — the Configure step framed on a marketing-site game page
   ----------------------------------------------------------------------------
   The psg-embed.php hook sets {$psgEmbed} → header.tpl adds `.psg-embed` to
   <html> and <body>. THESE frame-fitting overrides apply only in that framed
   context (the Configure REDESIGN below is `.psg-cfg`, applied on every Configure
   page — embed and the full WHMCS view), fixing two iframe-only issues:

   1. Background: the iframe document canvas is transparent, so the marketing
      section remains the single background layer. This keeps both the flat game
      sections and the VPS gradient continuous behind the Configure content.
   2. Height: the stock layout lets html/body stretch to the iframe's height, so
      the parent's height-sync feeds back into an ever-growing iframe. Sizing to
      content (height:auto) makes scrollHeight report the true content height, so
      the frame settles instead of expanding. #main-body's min-height is dropped
      too (its viewport-ish floor would otherwise pad the frame). */
html.psg-embed,
html.psg-embed body,
body.primary-bg-color.psg-embed {
  background: transparent !important;
  color-scheme: dark;
}
html.psg-embed,
html.psg-embed body {
  height: auto !important;
  min-height: 0 !important;
}
html.psg-embed #main-body {
  min-height: 0 !important;
}
/* Full-bleed inside the frame: the stock Bootstrap .container caps width (960px)
   and adds auto side-margins + 15px padding. Drop them so the Configure content
   spans the iframe's full width — the marketing OrderSection supplies the outer
   horizontal padding around the frame. */
html.psg-embed #main-body .container {
  max-width: none !important;
  width: 100% !important;
  margin-left: 0 !important;
  margin-right: 0 !important;
  padding-left: 0 !important;
  padding-right: 0 !important;
}
/* Zero the rest of the nested Bootstrap horizontal padding/margins too. Once the
   container padding is gone, the .row negative margins (-15px) have nothing to
   absorb them and spill 15px past the edge → a horizontal scrollbar on mobile.
   Flattening the whole chain keeps content truly edge-to-edge with no overflow
   (and preserves the option-card grid, which needs the full width to stay 2-up). */
html.psg-embed #main-body .cart-body,
html.psg-embed #main-body .secondary-cart-body {
  padding-left: 0 !important;
  padding-right: 0 !important;
}
html.psg-embed #main-body .row {
  margin-left: 0 !important;
  margin-right: 0 !important;
}
html.psg-embed #main-body [class*="col-"] {
  padding-left: 0 !important;
  padding-right: 0 !important;
}

/* ----------------------------------------------------------------------------
   CONFIGURE redesign (embed + standalone full WHMCS view)
   Makes the Configure step read like the marketing site (not a utility form):
   brand eyebrows, option cards, capped inputs, a decluttered layout, and a
   single "price + Continue" action. Scoped to `.psg-cfg`, which header.tpl sets
   on EVERY Configure page (embed pages get `.psg-embed` too, for the frame
   fitting above). The single-cycle-hide and "$X /cycle" price are done in
   js/psg-cart.js (also on every Configure page).
   ---------------------------------------------------------------------------- */

/* Declutter: the WHMCS product slab + intro subtext on the Configure page. */
html.psg-cfg .product-info { display: none !important; }
html.psg-cfg .secondary-cart-body > p { display: none !important; }
/* The "Powered by WHMCS" attribution line WHMCS injects into .primary-content
   (Configure, cart view, etc.) — off-brand under the PSG chrome; hide it wherever
   it appears (it's the only .primary-content paragraph that links to whmcs.com). */
html .primary-content p:has(a[href*="whmcs.com"]) { display: none !important; }

/* The stock "Configure" heading is dropped only IN THE EMBED — the marketing site
   supplies the lead-in there. On the standalone page (e.g. editing a cart item) it
   stays as a real heading, retitled "Edit your <game> configuration" by
   js/psg-cart.js and restyled below to match the site's section headings. */
html.psg-embed .cart-body > .header-lined { display: none !important; }
html.psg-cfg:not(.psg-embed) .cart-body > .header-lined {
  border-bottom: none !important;
  margin: 0 0 26px !important;
  padding: 0 !important;
}
html.psg-cfg:not(.psg-embed) .cart-body > .header-lined h1 {
  position: relative !important;
  margin: 0 !important;
  padding-bottom: 12px !important;
  font-family: var(--font-display) !important;
  font-size: var(--text-2xl) !important;
  font-weight: var(--weight-bold) !important;
  letter-spacing: -0.01em !important;
  color: var(--text-primary) !important;
}
html.psg-cfg:not(.psg-embed) .cart-body > .header-lined h1::after {
  content: "" !important;
  position: absolute !important;
  left: 0 !important;
  bottom: 0 !important;
  width: 36px !important;
  height: 2px !important;
  background: var(--orange-500) !important;
}

/* Open tight to the top: the first configurable option is the first thing in the
   frame (the intro copy + billing-cycle selector above it are hidden/gated), so
   drop the padding + margin that otherwise pads it down from the top edge. Also
   trim the bottom: the footer is suppressed in-frame, so the stock 40px cart +
   20px body bottom padding is just dead space below the Continue button — the
   marketing OrderSection supplies the gap to the next section. */
html.psg-cfg .secondary-cart-body > .product-configurable-options { margin-top: 0 !important; }
html.psg-cfg .product-configurable-options > .row:first-child { margin-top: 0 !important; }
html.psg-embed #main-body { padding-top: 0 !important; padding-bottom: 0 !important; }
html.psg-embed #order-standard_cart { padding-bottom: 4px !important; }

/* ---- Progressive reveal (see js/psg-cart.js): the Configure step opens on the
   FIRST option only, with nothing preselected. Every deferred block is marked
   .psg-gate-hidden and hidden while <html> carries .psg-gated; picking the first
   option drops .psg-gated and the rest fades in. ---- */
html.psg-cfg.psg-gated .psg-gate-hidden { display: none !important; }
html.psg-cfg:not(.psg-gated) .psg-gate-hidden { animation: psgGateReveal 280ms ease both; }
@keyframes psgGateReveal {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
/* The admin masquerade "return to admin area" floating button — an admin-only
   artifact that shouldn't sit inside the framed customer view. */
html.psg-embed .btn-return-to-admin { display: none !important; }

/* Field labels -> brand eyebrows (ALL CAPS mono, wide tracking). */
html.psg-cfg .primary-content .form-group > label[for] {
  display: block !important;
  margin: 0 0 10px !important;
  font-family: var(--font-mono) !important;
  font-size: var(--text-base) !important;
  font-weight: var(--weight-semibold) !important;
  letter-spacing: var(--tracking-caps) !important;
  text-transform: uppercase !important;
  color: var(--text-primary) !important;
}
html.psg-cfg .primary-content .form-group { margin-bottom: var(--space-10) !important; }

/* Quantity options use WHMCS's Ion RangeSlider. Its stock Modern skin loads
   after this theme, so leave the input behaviour alone and redraw only the
   generated slider with the same dark/orange controls used by the site. */
html.psg-cfg .product-configurable-options .form-group:has(.irs) {
  width: 75% !important;
  padding: var(--space-4) var(--space-5) var(--space-3) !important;
  background: var(--surface-inset) !important;
  border: var(--border-1) solid var(--border-default) !important;
}
html.psg-cfg .product-configurable-options .form-group:has(.irs) > .irs {
  width: 100% !important;
  height: var(--space-16) !important;
  font-family: var(--font-mono) !important;
}
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-line,
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-bar {
  top: var(--space-10) !important;
  height: var(--space-3) !important;
}
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-line {
  background: var(--surface-card) !important;
  border: var(--border-1) solid var(--border-strong) !important;
  border-radius: var(--radius-pill) !important;
}
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-line-left,
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-line-mid,
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-line-right {
  background: transparent !important;
}
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-bar,
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-bar-edge {
  background: var(--orange-500) !important;
  border: 0 !important;
  border-radius: var(--radius-pill) !important;
}
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-bar-edge {
  top: var(--space-10) !important;
  height: var(--space-3) !important;
}
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-handle,
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-slider {
  top: calc(var(--space-10) - var(--space-2) + var(--space-2) / 4) !important;
  width: calc(var(--control-sm) - var(--space-2)) !important;
  height: calc(var(--control-sm) - var(--space-2)) !important;
  background: var(--surface-base) !important;
  border: var(--border-2) solid var(--orange-400) !important;
  border-radius: 50% !important;
  box-shadow: 0 0 0 var(--space-1) color-mix(in srgb, var(--orange-500) 16%, transparent) !important;
  cursor: grab !important;
}
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-handle:active,
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-slider:active {
  cursor: grabbing !important;
}
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-handle > i:first-child,
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-single::before,
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-single::after,
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-from,
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-to {
  display: none !important;
}

/* Optional admin-authored copy for Quantity controls. The slider itself keeps
   using WHMCS's selected value, range, and price calculation. */
html.psg-cfg .product-configurable-options .form-group:has(.irs) .psg-quantity-subtitle {
  margin: 0 0 var(--space-3) !important;
  color: var(--text-muted) !important;
  font-size: var(--text-sm) !important;
  line-height: var(--leading-normal) !important;
}
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-single.psg-quantity-has-unit::after {
  content: " " attr(data-psg-unit) !important;
  display: inline !important;
  position: static !important;
  width: auto !important;
  height: auto !important;
  margin: 0 !important;
  background: transparent !important;
  border: 0 !important;
  border-radius: var(--radius-none) !important;
  box-shadow: none !important;
  color: var(--text-muted) !important;
  transform: none !important;
}
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-handle.state_hover,
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-handle:hover,
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-slider.state_hover,
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-slider:hover {
  border-color: var(--orange-300) !important;
  background: var(--surface-card) !important;
}
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-single,
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-min,
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-max,
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-grid-text {
  font-family: var(--font-mono) !important;
  font-size: var(--text-xs) !important;
  letter-spacing: var(--tracking-wide) !important;
}
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-single {
  top: 0 !important;
  padding: var(--space-1) var(--space-2) !important;
  background: var(--surface-card) !important;
  border: var(--border-1) solid var(--orange-500) !important;
  border-radius: var(--radius-none) !important;
  color: var(--orange-300) !important;
  line-height: var(--leading-normal) !important;
}
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-min,
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-max,
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-grid-text {
  background: transparent !important;
  color: var(--text-muted) !important;
}
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-grid-pol {
  background: var(--border-strong) !important;
}
html.psg-cfg .product-configurable-options .form-group:has(.irs) .irs-grid-pol.small {
  background: var(--border-default) !important;
}
@media (max-width: 767px) {
  html.psg-cfg .product-configurable-options .form-group:has(.irs) {
    width: 100% !important;
    padding-right: var(--space-4) !important;
    padding-left: var(--space-4) !important;
  }
  html.psg-cfg .product-configurable-options .form-group:has(.irs) > .irs {
    width: 100% !important;
  }
}

/* The radio option group is a fixed 4-COLUMN grid: four options fit in a single
   row at the site's content width, each exactly a quarter of it — and a card never
   grows wider than that, so a lone option stays quarter-width instead of stretching
   to fill the row. Fewer/leftover options leave the trailing columns empty. Cards
   stretch to the tallest in their row (equal-height compare). Collapses to 2 then 1
   column on smaller screens. */
html.psg-cfg .product-configurable-options .form-group:has(> label:not([for])) {
  display: grid !important;
  grid-template-columns: repeat(4, minmax(0, 1fr)) !important;
  /* Gap absorbs the 10px the base label margin-right used to add between cards
     (that margin is zeroed on the cards below), so spacing is unchanged but there's
     no leftover margin strip after the last card in a row. */
  gap: 18px !important;
  align-items: stretch !important;
}
/* The option name spans the full width above its cards. */
html.psg-cfg .product-configurable-options .form-group:has(> label:not([for])) > label[for] {
  grid-column: 1 / -1 !important;
}
/* ...unless the addon hides this option's heading (psg-cart.js adds the class). */
html.psg-cfg .product-configurable-options .form-group > label[for].psg-opt-heading-hidden {
  display: none !important;
}

/* Location cards are already visually substantial. Keep their heading close to
   the choices, while the larger form-group margin preserves separation from
   the preceding and following Configure sections. */
html.psg-cfg .product-configurable-options .form-group:has(> label.psg-opt-card) {
  row-gap: var(--space-2) !important;
}
html.psg-cfg .product-configurable-options .form-group:has(> label.psg-opt-card) > label[for] {
  margin-bottom: 0 !important;
}

/* Long initial plan lists stay on one horizontal row. Four cards are fully visible
   on desktop and half of the fifth remains in view as a visual continuation cue;
   edge shadows track hidden choices on either side while the native scrollbar
   provides direct desktop control. Applied by
   psg-cart.js only to the FIRST radio group and only when it has 5+ options. */
html.psg-cfg .product-configurable-options .form-group.psg-option-strip:has(> label:not([for])) {
  display: block !important;
  overflow-x: auto !important;
  overflow-y: hidden !important;
  /* The cards lift 1px on hover; reserve that pixel inside the scrollport so
     overflow clipping does not shave off their top border. */
  padding-top: 1px !important;
  padding-bottom: 12px !important;
  font-size: 0 !important; /* remove inline-card whitespace; cards restore type */
  white-space: nowrap !important;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  /* Keep native scrollbar gutters transparent too; otherwise Chromium can
     paint a light canvas underneath this transparent embed. */
  background: transparent !important;
  color-scheme: dark;
  scrollbar-color: var(--orange-500) transparent;
  scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
}
html.psg-cfg .product-configurable-options .form-group.psg-option-strip::-webkit-scrollbar {
  height: 8px;
  background: transparent;
}
html.psg-cfg .product-configurable-options .form-group.psg-option-strip::-webkit-scrollbar-track {
  background: transparent;
}
html.psg-cfg .product-configurable-options .form-group.psg-option-strip::-webkit-scrollbar-thumb {
  background: var(--orange-500);
  border: 2px solid var(--surface-inset);
}
html.psg-cfg .product-configurable-options .psg-option-strip-frame {
  position: relative;
}
html.psg-cfg .product-configurable-options .psg-option-strip-shadow {
  position: absolute;
  z-index: 4;
  top: 0;
  bottom: 0;
  width: var(--space-8);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--duration-base) var(--ease-standard);
}
html.psg-cfg .product-configurable-options .psg-option-strip-shadow-left {
  left: 0;
  background: linear-gradient(to right, var(--surface-base), transparent);
}
html.psg-cfg .product-configurable-options .psg-option-strip-shadow-right {
  right: 0;
  background: linear-gradient(to left, var(--surface-base), transparent);
}
html.psg-cfg .product-configurable-options .psg-option-strip-frame.psg-can-scroll-left > .psg-option-strip-shadow-left,
html.psg-cfg .product-configurable-options .psg-option-strip-frame.psg-can-scroll-right > .psg-option-strip-shadow-right {
  opacity: 0.72;
}
@media (prefers-reduced-motion: reduce) {
  html.psg-cfg .product-configurable-options .psg-option-strip-shadow {
    transition: none;
  }
}
html.psg-cfg .product-configurable-options .form-group.psg-option-strip > label:not([for]) {
  display: inline-flex !important;
  width: calc(22.2222% - 16px) !important;
  min-width: calc(22.2222% - 16px) !important;
  margin: 0 18px 0 0 !important;
  vertical-align: top !important;
  white-space: normal !important;
  scroll-snap-align: start;
}
html.psg-cfg .product-configurable-options .form-group.psg-option-strip > label:not([for]):last-of-type {
  margin-right: 0 !important;
}

/* Each card fills its grid cell. Column layout so a priced option stacks its name
   over its price (psg-cart.js splits WHMCS's "Name $Price" label into
   .psg-opt-name / .psg-opt-price). */
html.psg-cfg .product-configurable-options .form-group > label:not([for]) {
  flex-direction: column !important;
  gap: 3px !important;
  /* Zero the base #order-standard_cart label margin-right:10px — the grid gap
     spaces the cards, and that stray margin was the dead strip after the last
     card in a row. */
  margin: 0 !important;
  /* border-box + min-width:0 let the card match its column instead of content
     forcing it wider. */
  box-sizing: border-box !important;
  min-width: 0 !important;
  min-height: 58px !important;
  padding: 14px 16px !important;
  background: var(--surface-card) !important;
  border: 1px solid var(--border-default) !important;
  color: var(--text-secondary) !important;
  font-size: var(--text-sm) !important;
  font-weight: var(--weight-semibold) !important;
  transition: transform 120ms ease, border-color 120ms, background-color 120ms, color 120ms !important;
}
/* Option name (14px) over price (12px, dimmer). */
html.psg-cfg .product-configurable-options label .psg-opt-name {
  font-size: var(--text-sm) !important;
}
html.psg-cfg .product-configurable-options label .psg-opt-price {
  font-size: var(--text-xs) !important;
  font-weight: var(--weight-medium) !important;
  color: var(--text-muted) !important;
}
html.psg-cfg .product-configurable-options .form-group > label:not([for]):hover {
  border-color: var(--border-strong) !important;
  background: var(--surface-card-hover) !important;
  color: var(--text-primary) !important;
  transform: translateY(-1px);
}
html.psg-cfg .product-configurable-options .form-group > label:not([for]):has(.checked) {
  border-color: var(--orange-500) !important;
  box-shadow: inset 0 0 0 1px var(--orange-500) !important;
  background: color-mix(in srgb, var(--orange-500) 14%, var(--surface-card)) !important;
  color: var(--text-primary) !important;
}

/* --- Per-value marketing content, rendered INSIDE each option card by
   js/psg-cart.js (window.PSG_OPTMETA + window.PSG_OPT_ICONS): a headline, an
   optional background image, a "suggested uses" line and a features list. Cards
   with content become richer comparison cards, shown together and forced to equal
   height (the flex row stretches them). --- */

/* Content cards: top-/left-aligned plan-card layout instead of the centered
   thin-button look. (The grid container above already stretches the row to the
   tallest card.) */
html.psg-cfg .product-configurable-options .form-group > label.psg-opt-card {
  justify-content: flex-start !important;
  align-items: stretch !important;
  text-align: left !important;
  gap: 6px !important;
  padding: 16px 16px 18px !important;
}
html.psg-cfg .psg-opt-card .psg-opt-name,
html.psg-cfg .psg-opt-card .psg-opt-headline {
  align-self: flex-start !important;
  font-size: var(--text-base) !important;
  font-weight: var(--weight-semibold) !important;
  color: var(--text-primary) !important;
  line-height: 1.2 !important;
}
html.psg-cfg .psg-opt-card .psg-opt-price { align-self: flex-start !important; }
/* When a marketing Name is the headline, the WHMCS value name drops to a subtitle
   beneath it. */
html.psg-cfg .psg-opt-card.psg-has-headline .psg-opt-name {
  font-size: var(--text-sm) !important;
  font-weight: var(--weight-medium) !important;
  color: var(--text-muted) !important;
}
/* Config toggle: hide the WHMCS value name entirely. */
html.psg-cfg .psg-opt-card.psg-hide-valuename .psg-opt-name { display: none !important; }

/* Optional background image -> a faint gradient wash, mirroring the site's game
   hero (a low-opacity image under surface->transparent gradients). The URL is a
   CSS var set by psg-cart.js; ::before layers the wash behind the card content. */
html.psg-cfg .product-configurable-options .form-group > label.psg-has-bg::before {
  content: "" !important;
  position: absolute !important;
  inset: 0 !important;
  z-index: 0 !important;
  pointer-events: none !important;
  background-image:
    linear-gradient(to top, var(--surface-card) 12%, transparent 72%),
    linear-gradient(to right, var(--surface-card) 0%, transparent 62%),
    linear-gradient(
      color-mix(in srgb, var(--surface-card) 52%, transparent),
      color-mix(in srgb, var(--surface-card) 52%, transparent)
    ),
    var(--psg-optbg) !important;
  background-size: cover !important;
  background-position: center !important;
}
/* Lift the card's content above the wash (::before is z-index 0). The <label>
   still handles clicks natively, so the iCheck overlay needs no z-index change. */
html.psg-cfg .product-configurable-options .form-group > label.psg-opt-card > .psg-opt-headline,
html.psg-cfg .product-configurable-options .form-group > label.psg-opt-card > .psg-opt-name,
html.psg-cfg .product-configurable-options .form-group > label.psg-opt-card > .psg-opt-price,
html.psg-cfg .product-configurable-options .form-group > label.psg-opt-card > .psg-opt-info,
html.psg-cfg .product-configurable-options .form-group > label.psg-opt-card > .psg-opt-btn {
  position: relative !important;
  z-index: 1 !important;
}

/* Content block (uses + features) inside the card. */
html.psg-cfg .psg-opt-card .psg-opt-info {
  display: flex !important;
  flex: 1 1 auto !important;
  flex-direction: column !important;
  min-height: 0 !important;
  width: 100% !important;
  padding-top: 10px !important;
}
html.psg-cfg .psg-opt-card .psg-opt-uses {
  margin: 0 0 10px !important;
  font-size: var(--text-sm) !important;
  line-height: 1.45 !important;
  font-weight: var(--weight-medium) !important;
  color: var(--text-secondary) !important;
}
html.psg-cfg .psg-opt-card .psg-opt-uses:last-child { margin-bottom: 0 !important; }
html.psg-cfg .psg-opt-card .psg-opt-features {
  /* Fill the card's remaining content space so features align across cards even
     when one option includes a longer subheading. */
  margin: auto 0 0 !important;
  padding: 0 !important;
  list-style: none !important;
  display: grid !important;
  gap: 7px !important;
}
html.psg-cfg .psg-opt-card .psg-feat {
  display: flex !important;
  align-items: flex-start !important;
  gap: 8px !important;
  font-size: var(--text-sm) !important;
  line-height: 1.35 !important;
  color: var(--text-secondary) !important;
}
html.psg-cfg .psg-opt-card .psg-feat-icon {
  flex: 0 0 auto !important;
  display: inline-flex !important;
  margin-top: 1px !important;
  color: var(--orange-400) !important;
}
html.psg-cfg .psg-opt-card .psg-feat-icon svg {
  width: 15px !important;
  height: 15px !important;
  display: block !important;
}
/* Over a background image, lift text to the brighter primary for contrast (the
   value-name subtitle to secondary so it still reads as secondary). */
html.psg-cfg .psg-opt-card.psg-has-bg .psg-opt-headline,
html.psg-cfg .psg-opt-card.psg-has-bg .psg-opt-uses,
html.psg-cfg .psg-opt-card.psg-has-bg .psg-feat { color: var(--text-primary) !important; }
html.psg-cfg .psg-opt-card.psg-has-bg.psg-has-headline .psg-opt-name { color: var(--text-secondary) !important; }

/* --- "Graphic" style: an icon to the LEFT of a heading + subheading — an
   alternative presentation to the Card layout above (window.PSG_OPTMETA
   entry.style === 'graphic'). js/psg-cart.js replaces the stock value name/price
   with its own heading, so this lays the card out as a horizontal icon+text row. */
html.psg-cfg .product-configurable-options .form-group > label.psg-opt-graphic {
  flex-direction: row !important;
  align-items: center !important;
  justify-content: flex-start !important;
  text-align: left !important;
  gap: 12px !important;
  padding: 16px !important;
}
html.psg-cfg .psg-opt-graphic > .psg-opt-name,
html.psg-cfg .psg-opt-graphic > .psg-opt-price { display: none !important; }
html.psg-cfg .psg-opt-graphic .psg-opt-graphic-row {
  display: flex !important;
  align-items: center !important;
  gap: 12px !important;
  width: 100% !important;
  min-width: 0 !important;
}
html.psg-cfg .psg-opt-graphic .psg-opt-graphic-icon {
  flex: 0 0 auto !important;
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  width: 34px !important;
  height: 34px !important;
  color: var(--orange-400) !important; /* tints stroke-only Lucide icons */
}
html.psg-cfg .psg-opt-graphic .psg-opt-graphic-icon svg {
  width: 28px !important;
  height: 28px !important;
  display: block !important;
}
/* Flags are full-colour, ~square SVGs (and uploaded images): show as a rounded
   chip, untinted, so they read as an icon without recolouring the artwork. */
html.psg-cfg .psg-opt-graphic .psg-opt-graphic-icon--flag svg,
html.psg-cfg .psg-opt-graphic .psg-opt-graphic-icon img {
  width: 30px !important;
  height: 30px !important;
  border-radius: 999px !important;
  object-fit: cover !important;
}
html.psg-cfg .psg-opt-graphic .psg-opt-graphic-text {
  display: flex !important;
  flex-direction: column !important;
  gap: 2px !important;
  min-width: 0 !important;
}
html.psg-cfg .psg-opt-graphic .psg-opt-headline {
  font-size: var(--text-base) !important;
  font-weight: var(--weight-semibold) !important;
  color: var(--text-primary) !important;
  line-height: 1.2 !important;
}
html.psg-cfg .psg-opt-graphic .psg-opt-subhead {
  font-size: var(--text-sm) !important;
  font-weight: var(--weight-medium) !important;
  color: var(--text-muted) !important;
  line-height: 1.35 !important;
}

/* --- Optional select "button" at the bottom-centre of a card. psg-cart.js adds
   .psg-has-btn to the card + a non-interactive <span.psg-opt-btn>; clicking it
   selects the option via the label's native behaviour (no JS). Outline by
   default; fills orange when the card is hovered or selected. --- */
html.psg-cfg .psg-opt-card .psg-opt-btn {
  margin-top: auto !important;     /* pin to the bottom of the flex-column card */
  align-self: center !important;   /* centre horizontally */
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  max-width: 100% !important;
  white-space: nowrap !important;
  padding: 9px 20px !important;
  border: 1px solid var(--orange-500) !important;
  border-radius: 0 !important;     /* square corners per the design system */
  background: transparent !important;
  color: var(--orange-400) !important;
  font-size: var(--text-sm) !important;
  font-weight: var(--weight-semibold) !important;
  line-height: 1 !important;
  cursor: pointer !important;
  transition: background-color 120ms, color 120ms, border-color 120ms !important;
}
/* Clear space above the button, in BOTH styles. margin-top:auto (above) pins it
   to the card bottom so buttons align across a row; when content fills the card
   that auto collapses to the flex gap, so top up the gap on whatever content sits
   directly above the button (:nth-last-child(2) = the element before the button,
   which is always the card's last child). ~16px total in the filled case. */
html.psg-cfg .product-configurable-options .form-group > label.psg-has-btn > *:nth-last-child(2) {
  margin-bottom: 10px !important;
}
html.psg-cfg .product-configurable-options .form-group > label.psg-has-btn:hover .psg-opt-btn,
html.psg-cfg .product-configurable-options .form-group > label.psg-has-btn:has(.checked) .psg-opt-btn {
  background: var(--orange-500) !important;
  color: #fff !important;
}
/* Graphic cards switch to a column when they carry a button, so it sits below
   the icon+text row instead of beside it. Base gap matches the card style (6px)
   so the shared nth-last-child(2) spacing rule above lands both at ~16px. */
html.psg-cfg .product-configurable-options .form-group > label.psg-opt-graphic.psg-has-btn {
  flex-direction: column !important;
  align-items: stretch !important;
  justify-content: flex-start !important;
  gap: 6px !important;
}

/* Four-up gets too tight before mobile: drop to two columns, then one. */
@media (max-width: 900px) {
  html.psg-cfg .product-configurable-options .form-group:has(> label:not([for])) {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
  }
  html.psg-cfg .product-configurable-options .form-group.psg-option-strip > label:not([for]) {
    width: calc(40% - 14.4px) !important;
    min-width: calc(40% - 14.4px) !important;
  }
}
@media (max-width: 560px) {
  html.psg-cfg .product-configurable-options .form-group:has(> label:not([for])) {
    grid-template-columns: minmax(0, 1fr) !important;
  }
  html.psg-cfg .product-configurable-options .form-group.psg-option-strip > label:not([for]) {
    width: calc(80% - 14.4px) !important;
    min-width: calc(80% - 14.4px) !important;
  }
}

/* Inputs — text fields + billing select. */
html.psg-cfg .primary-content .form-control {
  height: auto !important;
  padding: 12px 14px !important;
  background: var(--surface-inset) !important;
  border: 1px solid var(--border-default) !important;
  color: var(--text-primary) !important;
  font-size: var(--text-sm) !important;
  box-shadow: none !important;
}
html.psg-cfg .primary-content .form-control:focus {
  border-color: var(--orange-500) !important;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--orange-500) 22%, transparent) !important;
  outline: none !important;
}
/* Cap field width so text inputs / select don't span the whole form (still
   full-width on mobile, which is narrower than the cap). */
html.psg-cfg .primary-content input.form-control,
html.psg-cfg .primary-content textarea.form-control,
html.psg-cfg .primary-content select.form-control {
  width: 100% !important;
  max-width: 440px !important;
}
html.psg-cfg .primary-content select.custom-select {
  appearance: none !important;
  -webkit-appearance: none !important;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23f5822a' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E") !important;
  background-repeat: no-repeat !important;
  background-position: right 14px center !important;
  padding-right: 40px !important;
}
html.psg-cfg .primary-content .field-help-text {
  display: block !important;
  margin-top: 8px !important;
  font-size: var(--text-xs) !important;
  color: var(--text-muted) !important;
}

/* Collapse the form + summary columns into one stacked column. */
html.psg-cfg form#frmConfigureProduct > .row { display: block !important; }
html.psg-cfg .secondary-cart-body,
html.psg-cfg #scrollingPanelContainer.secondary-cart-sidebar {
  width: 100% !important;
  max-width: 100% !important;
  flex: none !important;
  position: static !important;
}

/* Strip the itemised summary + "Order Summary" heading; keep only the live price
   (the form already shows what was configured). */
html.psg-cfg #orderSummary .order-summary > h2,
html.psg-cfg #producttotal .product-name,
html.psg-cfg #producttotal .product-group,
html.psg-cfg #producttotal > .clearfix,
html.psg-cfg #producttotal .summary-totals { display: none !important; }

/* Action cluster — desktop: price inline to the LEFT of Continue, pair
   right-aligned. Mobile: price centered above a full-width button. No divider
   or background. */
html.psg-cfg #orderSummary {
  display: flex !important;
  flex-direction: row !important;
  align-items: center !important;
  justify-content: flex-end !important;
  gap: 20px !important;
  margin-top: 30px !important;
  padding-top: 0 !important;
  border-top: none !important;
}
html.psg-cfg #orderSummary .order-summary,
html.psg-cfg #orderSummary .text-center {
  margin: 0 !important;
  width: auto !important;
  max-width: 100% !important;
}
html.psg-cfg #orderSummary .order-summary {
  background: transparent !important;
  border: none !important;
  box-shadow: none !important;
  padding: 0 !important;
  min-height: 0 !important;
}
html.psg-cfg #producttotal.summary-container {
  background: transparent !important;
  border: none !important;
  padding: 0 !important;
  min-height: 0 !important;
}

/* Price -> "$X /mo": amount in display type, the /cycle suffix (added by
   psg-cart.js) small + muted; the "Total Due Today" caption is dropped. */
html.psg-cfg #producttotal .total-due-today {
  margin: 0 !important;
  padding: 0 !important;
  border-top: none !important;
  display: flex !important;
  align-items: baseline !important;
  justify-content: flex-end !important;
  gap: 6px !important;
}
html.psg-cfg #producttotal .total-due-today > span:not(.amt) { display: none !important; }
html.psg-cfg #producttotal .total-due-today .amt {
  display: inline-flex !important;
  align-items: baseline !important;
  gap: 5px !important;
  white-space: nowrap !important;
  font-family: var(--font-display) !important;
  font-size: var(--text-2xl) !important;
  font-weight: var(--weight-bold) !important;
  color: var(--text-primary) !important;
  line-height: 1 !important;
}
html.psg-cfg #producttotal .total-due-today .amt .psg-cycle {
  font-family: var(--font-sans) !important;
  font-size: var(--text-sm) !important;
  font-weight: var(--weight-medium) !important;
  color: var(--text-muted) !important;
}

/* Continue — the anchor CTA next to the price. */
html.psg-cfg #btnCompleteProductConfig {
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  gap: 8px !important;
  width: auto !important;
  min-width: 200px !important;
  padding: 14px 28px !important;
  font-family: var(--font-display) !important;
  font-weight: var(--weight-bold) !important;
  font-size: var(--text-base) !important;
}

/* Mobile: stack — price centered above a full-width button. */
@media (max-width: 640px) {
  html.psg-cfg #orderSummary {
    flex-direction: column !important;
    align-items: center !important;
  }
  html.psg-cfg #orderSummary .order-summary,
  html.psg-cfg #orderSummary .text-center { width: 100% !important; }
  html.psg-cfg #producttotal .total-due-today { justify-content: center !important; }
  html.psg-cfg #btnCompleteProductConfig { width: 100% !important; min-width: 0 !important; }
}

/* Price-recalc spinner. WHMCS fades in #orderSummaryLoader over the summary while
   recalculating the price — in our single-line price+Continue cluster the loader
   (height:0, positioned over the total) overlaps the price. Hide it, and instead
   show the spinner inside the Continue button in place of its arrow, disabling the
   button while it spins so a stale/loading price can't be submitted. psg-cart.js
   toggles .psg-btn-loading + the disabled state to mirror the loader.
   NB: hide the loader with visibility, NOT display:none — jQuery's fadeOut skips
   writing display:none on an element it already sees as hidden, which would strand
   the spinner on forever. Visibility keeps jQuery's display cycle intact. */
html.psg-cfg #orderSummaryLoader { visibility: hidden !important; }
html.psg-cfg #btnCompleteProductConfig.psg-btn-loading {
  cursor: not-allowed !important;
  opacity: 0.9 !important;
  /* Hold the primary orange while disabled — Bootstrap's .btn-primary:disabled
     (a 2-class selector) otherwise outranks the theme's .btn-primary and repaints
     the button blue. */
  background-color: var(--orange-500) !important;
  border-color: var(--orange-500) !important;
  color: var(--ink-950) !important;
}
html.psg-cfg #btnCompleteProductConfig.psg-btn-loading .fa-arrow-circle-right { display: none !important; }
html.psg-cfg #btnCompleteProductConfig.psg-btn-loading::after {
  content: "" !important;
  width: 0.95em !important;
  height: 0.95em !important;
  border: 2px solid currentColor !important;
  border-right-color: transparent !important;
  border-radius: 50% !important;
  box-sizing: border-box !important;
  animation: psg-cfg-spin 0.6s linear infinite !important;
}
@keyframes psg-cfg-spin { to { transform: rotate(360deg); } }

/* ============================================================================
   EMPTY CART VIEW (cart.php?a=view with nothing in the cart)
   ----------------------------------------------------------------------------
   A clean, centred empty state (markup in psg-orderform/viewcart.tpl) in place
   of the stock heading + blue underline. The "Powered by WHMCS" line is hidden
   by the .primary-content rule above; the stock blue `.view-cart-items` underline
   no longer renders (the template dropped that element). */
.psg-cart-empty {
  max-width: 460px;
  margin: 48px auto 72px;
  padding: 0 20px;
  text-align: center;
}
.psg-cart-empty-icon {
  color: var(--text-muted);
  font-size: 42px;
  line-height: 1;
  margin-bottom: 20px;
}
.psg-cart-empty-title {
  margin: 0 0 10px;
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-weight: var(--weight-bold);
  letter-spacing: -0.01em;
  color: var(--text-primary);
}
.psg-cart-empty-text {
  margin: 0 0 28px;
  color: var(--text-secondary);
  font-size: var(--text-base);
  line-height: 1.5;
}
.psg-cart-empty .psg-cart-empty-cta {
  display: inline-flex;
  align-items: center;
  padding: 13px 28px !important;
  font-family: var(--font-display) !important;
  font-weight: var(--weight-bold) !important;
  font-size: var(--text-base) !important;
}
