/**
 * SMKCloud — WHMCS bridge stylesheet.
 *
 * smk.css is the design system and is a verbatim copy of the approved frontend.
 * It is never edited here. This file is everything WHMCS itself needs on top:
 *
 *   1. A handful of classes the theme templates need that the marketing site
 *      never did (a custom logo image, the mobile account links).
 *   2. Bootstrap 3 markup emitted by WHMCS core and by payment, domain and
 *      provisioning modules, which this theme cannot rewrite — mapped onto the
 *      design tokens so a gateway's own form does not look pasted in.
 *
 * Kept separate so a design-system update is a file copy, not a merge.
 *
 * @package   SMKCloud
 * @author    Shahid Malla <life@shahidmalla.com>
 * @link      https://shahidmalla.com
 * @copyright 2026 https://smkcloud.tech/
 */

/* ==========================================================================
   1. Theme additions
   ========================================================================== */

/* A configured logo image replaces the built-in mark. Height is fixed and width
   is free so a wordmark of any aspect ratio sits on the same baseline. */
.logo__img {
  display: block;
  height: 32px;
  width: auto;
  max-width: 190px;
  object-fit: contain;
}
.logo--sm .logo__img { height: 26px; }
.logo--lg .logo__img { height: 40px; }

/* Below 900px the header's ghost buttons are hidden and the nav list becomes
   the entire menu, so the account links live inside it. */
.nav__account { display: none; }
.nav__sep {
  display: block;
  padding: 14px 10px 6px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink3);
}

@media (max-width: 900px) {
  .nav__account { display: block; }
  .nav__account + .nav__account { margin-top: 2px; }
}

/* Body-level state classes set by includes/head.tpl. */

/* Set when the sticky-header setting is off. The design's header is sticky by
   default, so this is the opt-out rather than the opt-in. */
.header--static { position: static; }

/* Set when scroll animations are disabled in the addon. The reveal module in
   smk.js also honours prefers-reduced-motion on its own; this is the operator's
   switch, and it has to win even for visitors who have not asked for reduced
   motion — hence the !important, which is load-bearing here. */
.no-reveal [data-reveal],
.no-reveal [data-reveal-child] {
  opacity: 1 !important;
  transform: none !important;
  transition: none !important;
}

/* CMS pages. The landing layout deliberately imposes no measure or padding —
   its body supplies its own <section class="wrap"> blocks, so anything here
   would fight the page an editor built. The other layouts use .prose, which
   smk.css already defines. */
.smkpage { display: block; }
.smkpage--landing > :first-child { margin-top: 0; }
.smkpage--landing > :last-child { margin-bottom: 0; }

/* An editor pasting a wide table or a code block must not be able to make the
   whole document scroll sideways. */
.prose table { display: block; overflow-x: auto; max-width: 100%; }
.prose pre { overflow-x: auto; max-width: 100%; }
.prose img { max-width: 100%; height: auto; }

/* Captcha.
   .recaptcha-container, .captchaimage, #inputCaptcha and #inputCaptchaImage are
   the names core's own JavaScript and templates bind to. They are kept verbatim
   so a captcha still works if the core client-area bundle is ever loaded
   alongside this theme; renaming one breaks the captcha with no error. */

/* The wrapper includes/captcha.tpl emits. It carries no look of its own — it
   exists so the optional $containerClass a caller passes has somewhere to land. */
.smk-captcha { display: block; }
.stack > .smk-captcha:empty { display: none; }

/* The reCAPTCHA / hCaptcha widget is a fixed-size vendor iframe we cannot
   restyle, so it is only centred and — below the width at which a 304px widget
   would force the page to scroll sideways — scaled down. The scaled box needs an
   explicit height because a transform does not affect layout. */
.recaptcha-container { display: flex; justify-content: center; }
.recaptcha-container > div { max-width: 100%; }

@media (max-width: 360px) {
  .recaptcha-container {
    transform: scale(.86);
    transform-origin: 0 0;
    height: 68px;
  }
}

/* The image WHMCS's own captcha generates. Height is fixed and width is free so
   an admin-configured image of a different size is never distorted. */
.captchaimage {
  display: block;
  flex: none;
  height: 46px;
  width: auto;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: #fff;
}

/* The identity-verification iframe. A fixed 700px frame overflows a laptop
   viewport inside a dialog that is already capped at 88vh, so it is capped
   against the viewport as well. */
.validation-frame {
  display: block;
  width: 100%;
  height: min(700px, 62vh);
  border: 0;
}

/**
 * CASCADE LAYER — this is the mechanism that stops WHMCS from overriding the design.
 *
 * Both this theme and Bootstrap use generic class names. Three collide outright:
 * .panel is a DARK panel in the design system but a white card in Bootstrap;
 * .table and .progress are likewise defined by both. Because a bridge rule is
 * written as `.is-public .panel` it carries higher specificity than the design's
 * `.panel`, so it silently won — turning the homepage support panel white and
 * leaving its light-on-dark text unreadable.
 *
 * Specificity cannot arbitrate this safely: any number we pick, a future rule can
 * beat. Layers can. An UNLAYERED rule always beats a LAYERED one no matter what
 * its specificity is, so putting the Bootstrap bridge in a layer makes smk.css —
 * which is unlayered — win every collision permanently, with no !important and no
 * specificity arms race.
 *
 * Consequence: these rules only take effect where the design system has no
 * opinion, which is exactly what a bridge should do.
 */
@layer whmcs-bootstrap {
/* ==========================================================================
   2. Bootstrap 3 bridge
   Scoped under .is-public and .is-app so nothing here can leak into the WHMCS
   admin area, which shares the same Bootstrap classes.
   ========================================================================== */

.is-public .btn-primary,
.is-app .btn-primary,
.is-public .btn-default,
.is-app .btn-default,
.is-public .btn-success,
.is-app .btn-success,
.is-public .btn-danger,
.is-app .btn-danger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border-radius: var(--r-pill);
  border: 1px solid transparent;
  padding: 10px 18px;
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  line-height: 1.2;
  text-shadow: none;
  background-image: none;
  box-shadow: none;
  transition: background .2s, border-color .2s, color .2s;
  min-height: 38px;
}

.is-public .btn-primary,
.is-app .btn-primary {
  background: var(--amber);
  border-color: var(--amber);
  color: var(--on-amber);
}
.is-public .btn-primary:hover, .is-app .btn-primary:hover,
.is-public .btn-primary:focus, .is-app .btn-primary:focus {
  background: var(--amber);
  border-color: var(--amber);
  color: var(--on-amber);
  /* Darkening the rendered colour rather than hard-coding a second amber keeps
     the hover correct after an admin changes the brand colour. */
  filter: brightness(.94);
}

.is-public .btn-default, .is-app .btn-default {
  background: var(--surface);
  border-color: var(--line);
  color: var(--ink);
}
.is-public .btn-default:hover, .is-app .btn-default:hover {
  background: var(--surface2);
  border-color: var(--line2, var(--line));
  color: var(--ink);
}

/* The label is --surface, not white. --ok and --warn invert between themes: they
   are a dark green / dark rust in light mode but a light lime / pale peach in
   dark mode, so a fixed white label reads 5.04:1 in light and 1.26:1 in dark —
   invisible. --surface inverts with them and stays legible on both (4.91:1 and
   5.77:1 light, 14.91:1 and 11.36:1 dark). */
.is-public .btn-success, .is-app .btn-success {
  background: var(--ok); border-color: var(--ok); color: var(--surface);
}
.is-public .btn-danger, .is-app .btn-danger {
  background: var(--warn); border-color: var(--warn); color: var(--surface);
}
.is-public .btn-sm, .is-app .btn-sm { padding: 7px 14px; font-size: 13px; min-height: 32px; }
.is-public .btn-lg, .is-app .btn-lg { padding: 13px 24px; font-size: 15.5px; min-height: 46px; }
.is-public .btn-block, .is-app .btn-block { display: flex; width: 100%; }

/* Focus must remain visible — several WHMCS modules set outline:none. */
.is-public .btn-primary:focus-visible, .is-app .btn-primary:focus-visible,
.is-public .btn-default:focus-visible, .is-app .btn-default:focus-visible,
.is-public .form-control:focus-visible, .is-app .form-control:focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 2px;
}

.is-public .form-control, .is-app .form-control {
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 11px 14px;
  font: inherit;
  font-size: 14.5px;
  color: var(--ink);
  box-shadow: none;
  transition: border-color .2s, box-shadow .2s;
  min-height: 42px;
}
.is-public .form-control:focus, .is-app .form-control:focus {
  border-color: var(--amber);
  box-shadow: 0 0 0 3px var(--amber-t);
  outline: none;
}
.is-public .form-control[disabled], .is-app .form-control[disabled],
.is-public .form-control[readonly], .is-app .form-control[readonly] {
  background: var(--surface2);
  color: var(--ink3);
  cursor: not-allowed;
}
.is-public select.form-control, .is-app select.form-control {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' fill='none'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23888' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  padding-right: 36px;
}
.is-public textarea.form-control, .is-app textarea.form-control { min-height: 120px; line-height: 1.6; }

.is-public .control-label, .is-app .control-label,
.is-public .form-group > label, .is-app .form-group > label {
  display: block;
  margin-bottom: 6px;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink2);
}
.is-public .form-group, .is-app .form-group { margin-bottom: 18px; }
.is-public .help-block, .is-app .help-block { font-size: 12.5px; color: var(--ink3); margin-top: 6px; }
.is-public .has-error .form-control, .is-app .has-error .form-control { border-color: var(--warn); }
.is-public .has-error .help-block, .is-app .has-error .help-block { color: var(--warn); }

/* The layer stops this bridge from beating a design declaration, but it cannot
 * stop it where the design declares nothing. smk.css .panel sets background,
 * colour, radius and padding — no border and no margin — so `.is-public .panel`
 * still painted a 1px --line border and 22px of bottom margin onto the design's
 * dark homepage support panel, which the approved page renders borderless and
 * flush. Core marks its own Bootstrap panels with a contextual class or a panel
 * part, and the design's .panel is always bare, so matching on those keeps the
 * bridge on core markup only. */
.is-public .panel[class*="panel-"], .is-app .panel[class*="panel-"] {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: none;
  margin-bottom: 22px;
}
/* Kept as a separate rule: an unsupported :has() invalidates the whole selector
 * list it appears in, which would take the contextual-class case above down
 * with it. This one covers a core panel whose only marker is a child part. */
.is-public .panel:has(> .panel-heading), .is-app .panel:has(> .panel-heading),
.is-public .panel:has(> .panel-body), .is-app .panel:has(> .panel-body),
.is-public .panel:has(> .panel-footer), .is-app .panel:has(> .panel-footer) {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: none;
  margin-bottom: 22px;
}
.is-public .panel-heading, .is-app .panel-heading {
  background: transparent;
  border-bottom: 1px solid var(--line);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  padding: 16px 20px;
  color: var(--ink);
}
.is-public .panel-title, .is-app .panel-title { font-size: 15.5px; font-weight: 500; margin: 0; }
.is-public .panel-body, .is-app .panel-body { padding: 20px; }
.is-public .panel-footer, .is-app .panel-footer {
  background: var(--surface2);
  border-top: 1px solid var(--line);
  border-radius: 0 0 var(--r-lg) var(--r-lg);
  padding: 14px 20px;
}

/* Bootstrap's .table and the design system's .table are DIFFERENT components
   that share a name — the same collision as .panel, which is why these rules
   are qualified with the element. Bootstrap's is always on a <table>; the design
   system's is a <div> grid card (.table__scroll/__head/__row/__foot), used by
   domain-pricing.tpl, domainregister.tpl and the CMS landing pages.

   The @layer guard alone does NOT protect it: a layer only decides which rule
   wins when both sheets declare the SAME property, and smk.css's `.table` sets
   only border/radius/overflow/background. So an unqualified `.is-public .table`
   applied font-size, colour and width to the design-system card unopposed —
   measured in-browser as 15px/23.25px line-height/--ink becoming
   14px/21.7px/--ink2 on every .table__row of the colocation, pricing and
   network pages, in both themes. Qualifying with `table.` makes these rules
   unable to match a <div>, which is what restores the approved rendering. */
.is-public table.table, .is-app table.table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: 14px;
  color: var(--ink2);
  margin-bottom: 0;
}
.is-public table.table > thead > tr > th, .is-app table.table > thead > tr > th {
  border-bottom: 1px solid var(--line);
  padding: 11px 14px;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink3);
  text-align: left;
  white-space: nowrap;
}
.is-public table.table > tbody > tr > td, .is-app table.table > tbody > tr > td {
  border-top: 1px solid var(--line);
  padding: 13px 14px;
  vertical-align: middle;
}
.is-public table.table > tbody > tr:hover > td, .is-app table.table > tbody > tr:hover > td { background: var(--surface2); }
.is-public .table-responsive, .is-app .table-responsive {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border: 0;
}

.is-public .alert, .is-app .alert {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  border: 1px solid var(--line);
  border-left-width: 3px;
  border-radius: var(--r-md);
  background: var(--surface);
  padding: 15px 18px;
  font-size: 14px;
  color: var(--ink2);
  line-height: 1.6;
  text-shadow: none;
  margin-bottom: 18px;
}
.is-public .alert-success, .is-app .alert-success { border-left-color: var(--ok); background: var(--ok-t); }
.is-public .alert-danger, .is-app .alert-danger,
.is-public .alert-warning, .is-app .alert-warning { border-left-color: var(--warn); background: var(--warn-t); }
.is-public .alert-info, .is-app .alert-info { border-left-color: var(--info); background: var(--info-t); }
.is-public .alert a, .is-app .alert a { color: var(--amber-d); text-decoration: underline; }

.is-public .label, .is-app .label {
  display: inline-block;
  border-radius: var(--r-pill);
  padding: 4px 10px;
  font-size: 11.5px;
  font-weight: 500;
  line-height: 1.4;
}
.is-public .label-success, .is-app .label-success { background: var(--ok-t); color: var(--ok); }
.is-public .label-danger, .is-app .label-danger { background: var(--warn-t); color: var(--warn); }
.is-public .label-warning, .is-app .label-warning { background: var(--amber-t); color: var(--amber-d); }
.is-public .label-info, .is-app .label-info { background: var(--info-t); color: var(--info); }
.is-public .label-default, .is-app .label-default { background: var(--surface2); color: var(--ink2); }

.is-public .pagination, .is-app .pagination { display: flex; gap: 4px; list-style: none; padding: 0; margin: 22px 0 0; }
.is-public .pagination > li > a, .is-app .pagination > li > a,
.is-public .pagination > li > span, .is-app .pagination > li > span {
  display: block;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 7px 13px;
  font-size: 13.5px;
  color: var(--ink2);
  background: var(--surface);
  min-width: 38px;
  text-align: center;
}
.is-public .pagination > .active > a, .is-app .pagination > .active > a,
.is-public .pagination > .active > span, .is-app .pagination > .active > span {
  background: var(--amber);
  border-color: var(--amber);
  color: var(--on-amber);
}

.is-public .modal-content, .is-app .modal-content {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow2);
}
.is-public .modal-header, .is-app .modal-header { border-bottom: 1px solid var(--line); padding: 18px 22px; }
.is-public .modal-body, .is-app .modal-body { padding: 22px; }
.is-public .modal-footer, .is-app .modal-footer { border-top: 1px solid var(--line); padding: 16px 22px; }

.is-public .nav-tabs, .is-app .nav-tabs {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  border-bottom: 1px solid var(--line);
  list-style: none;
  padding: 0;
  margin: 0 0 22px;
}
.is-public .nav-tabs > li > a, .is-app .nav-tabs > li > a {
  display: block;
  border: 0;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  padding: 11px 15px;
  font-size: 14px;
  color: var(--ink3);
  margin: 0;
}
.is-public .nav-tabs > li.active > a, .is-app .nav-tabs > li.active > a {
  background: transparent;
  border-bottom-color: var(--amber);
  color: var(--ink);
  font-weight: 500;
}

} /* end @layer whmcs-bootstrap */

/* ==========================================================================
   3. Classes core passes as template parameters
   ========================================================================== */

/* WHMCS core passes textcenter=true to includes/alert.tpl and bodyTextCenter /
   footerTextCenter to includes/panel.tpl from roughly thirty call sites, so the
   centred variant is part of those partials' parameter contract rather than a
   layout choice this theme gets to make. smk.css ships .t-right but no centring
   utility, so it is defined here instead. */
.text-center { text-align: center; }

/* The list furniture includes/tablelist.tpl inserts around a core-rendered
   <table>. Both are compositions of existing design-system pieces; only the
   spacing that joins them to the table is new. */
.tablebar { margin-bottom: 14px; }
.tablefoot { margin-top: 16px; }

/* Sortable column headings. smk.css styles .table__head .is-sortable for the
   design's own grid tables; the same control has to exist inside a real <thead>
   for the tables WHMCS core renders, so the affordance is repeated here against
   the element core actually emits. Same arrow, same active colour. */
.is-public thead .is-sortable,
.is-app thead .is-sortable {
  display: flex;
  align-items: center;
  gap: 6px;
  background: transparent;
  border: 0;
  color: inherit;
  font: inherit;
  padding: 0;
  cursor: pointer;
}
.is-public thead .is-sortable::after,
.is-app thead .is-sortable::after { content: '↕'; opacity: .4; font-size: 11px; }
.is-public thead [aria-sort="ascending"] .is-sortable::after,
.is-app thead [aria-sort="ascending"] .is-sortable::after {
  content: '↑'; opacity: 1; color: var(--amber);
}
.is-public thead [aria-sort="descending"] .is-sortable::after,
.is-app thead [aria-sort="descending"] .is-sortable::after {
  content: '↓'; opacity: 1; color: var(--amber);
}

.is-public .progress, .is-app .progress {
  height: 8px;
  border-radius: var(--r-pill);
  background: var(--surface2);
  box-shadow: none;
  overflow: hidden;
}
.is-public .progress-bar, .is-app .progress-bar {
  background: var(--amber);
  box-shadow: none;
}

.is-public .well, .is-app .well {
  background: var(--surface2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: none;
  padding: 18px;
}

.is-public .list-group, .is-app .list-group { border-radius: var(--r-md); overflow: hidden; }
.is-public .list-group-item, .is-app .list-group-item {
  background: var(--surface);
  border-color: var(--line);
  padding: 13px 16px;
  font-size: 14px;
  color: var(--ink2);
}
.is-public .list-group-item.active, .is-app .list-group-item.active {
  background: var(--amber-t);
  border-color: var(--line);
  color: var(--ink);
}

.is-public .breadcrumb, .is-app .breadcrumb {
  background: transparent;
  padding: 0;
  margin: 0 0 12px;
  font-size: 12.5px;
  color: var(--ink3);
}

.is-public .input-group-addon, .is-app .input-group-addon {
  background: var(--surface2);
  border-color: var(--line);
  color: var(--ink3);
  font-size: 13.5px;
}

.is-public .checkbox label, .is-app .checkbox label,
.is-public .radio label, .is-app .radio label {
  font-weight: 400;
  font-size: 14px;
  color: var(--ink2);
}

.is-public .text-muted, .is-app .text-muted { color: var(--ink3); }
.is-public .text-danger, .is-app .text-danger { color: var(--warn); }
.is-public .text-success, .is-app .text-success { color: var(--ok); }
.is-public hr, .is-app hr { border-top-color: var(--line); }

/* WHMCS core renders several tables inside .table-responsive without a minimum
   width, which collapses columns to unreadable widths on a phone rather than
   scrolling. Give them a floor so the scroll actually engages. */
@media (max-width: 720px) {
  .is-public .table-responsive > table.table,
  .is-app .table-responsive > table.table { min-width: 560px; }
}

@media (prefers-reduced-motion: reduce) {
  .is-public *, .is-app * {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

@layer whmcs-bootstrap {
/* ==========================================================================
   3. Bootstrap 3 structural layer
   The core bundle (js/scripts.min.js) ships Bootstrap's JavaScript, which
   toggles classes that need matching CSS to do anything. We deliberately do NOT
   load Bootstrap's stylesheet — it resets typography and would fight the design
   system — so only the behavioural rules are reproduced here. Presentation for
   these components is in section 2 above.
   ========================================================================== */

.is-public .collapse, .is-app .collapse { display: none; }
.is-public .collapse.in, .is-app .collapse.in { display: block; }
.is-public tr.collapse.in, .is-app tr.collapse.in { display: table-row; }
.is-public tbody.collapse.in, .is-app tbody.collapse.in { display: table-row-group; }
.is-public .collapsing, .is-app .collapsing {
  position: relative; height: 0; overflow: hidden;
  transition: height .3s ease;
}

.is-public .fade, .is-app .fade { opacity: 0; transition: opacity .15s linear; }
.is-public .fade.in, .is-app .fade.in { opacity: 1; }

/* Bootstrap modals are toggled by core for confirmations, the domain-transfer
   prompt, the IRTP change-of-registrant reminder and several gateway flows.
   Positioned above this theme's own layers but below the toast stack, which
   lives in the top layer via the Popover API.

   The element selector is load-bearing. Core's Bootstrap modals are always a
   <div class="modal">, while this theme's own modal.tpl and confirmation.tpl are
   a native <dialog class="modal">. Written as a bare .modal these rules would
   also match the dialog and hold display:none over the UA's dialog[open] rule —
   an author declaration beats the UA sheet whatever the specificity — so every
   native dialog in both shells would open into nothing. Narrowing to div.modal
   keeps the Bootstrap bridge and leaves <dialog> to smk.css. */
.is-public .dropdown-menu, .is-app .dropdown-menu {
  display: none; position: absolute; top: 100%; left: 0; z-index: 700;
  min-width: 180px; margin: 4px 0 0; padding: 6px; list-style: none;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-md); box-shadow: var(--shadow2);
}
.is-public .open > .dropdown-menu, .is-app .open > .dropdown-menu { display: block; }
.is-public .dropdown-menu > li > a, .is-app .dropdown-menu > li > a {
  display: block; padding: 9px 12px; border-radius: var(--r-md);
  font-size: 14px; color: var(--ink2); white-space: nowrap;
}
.is-public .dropdown-menu > li > a:hover, .is-app .dropdown-menu > li > a:hover {
  background: var(--surface2); color: var(--ink);
}

.is-public .tab-content > .tab-pane, .is-app .tab-content > .tab-pane { display: none; }
.is-public .tab-content > .active, .is-app .tab-content > .active { display: block; }

/* Core uses .hidden and .sr-only extensively; both must behave even though the
   Bootstrap stylesheet is absent. */
.is-public .hidden, .is-app .hidden { display: none !important; }

/* .w-hidden is the same idea under the name the current core bundle uses.
   scripts.min.js toggles it on the gateway error box, the pay/please-wait button
   labels and the 3-D Secure form container, so it is not decoration: undefined,
   an empty red error panel and the hidden bank form both render on first paint.
   Not scoped to .is-public/.is-app because viewinvoice.php also reaches it. */
.w-hidden { display: none !important; }
.is-public .sr-only, .is-app .sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

} /* end @layer whmcs-bootstrap */

/* ==========================================================================
   4. Auth pages
   The approved design carries these as inline styles on login.html /
   register.html / forgot-password.html. Extracted into classes here because
   five templates share the layout and inline styles cannot express the media
   query that drops the status rail.
   ========================================================================== */

.auth { display: flex; flex-wrap: wrap; min-height: 100vh; }

.auth__form {
  flex: 1 1 420px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 48px 24px;
  background: var(--bg);
}
.auth__inner { width: 100%; max-width: 390px; }
.auth__inner--wide { max-width: 560px; }

/* Sign in / Create account segmented control. */
.auth__switch {
  display: flex;
  border: 1px solid var(--line2);
  border-radius: var(--r-md);
  overflow: hidden;
  margin-bottom: 26px;
}
.auth__switch a {
  flex: 1;
  padding: 12px;
  font-size: 15px;
  font-weight: 500;
  text-align: center;
  color: var(--ink2);
  background: transparent;
  transition: background .2s, color .2s;
}
.auth__switch a:hover { background: var(--surface2); color: var(--ink); }
.auth__switch a.is-active { background: var(--amber); color: var(--on-amber); }
.auth__switch a.is-active:hover { background: var(--amber); color: var(--on-amber); filter: brightness(.94); }

.auth__title {
  font-size: 28px;
  font-weight: 400;
  letter-spacing: -.02em;
  margin-bottom: 6px;
  color: var(--ink);
}

/* "OR" divider with a rule either side. */
.auth__or { gap: 12px; margin: 4px 0; align-items: center; }
.auth__or > span:empty { flex: 1; height: 1px; background: var(--line); }

.auth__aside {
  flex: 1 1 380px;
  display: flex;
  align-items: center;
  padding: 48px 40px;
  background: var(--panel);
  color: var(--panel-ink);
}
.auth__asidetitle {
  font-size: clamp(24px, 3vw, 34px);
  font-weight: 500;
  letter-spacing: -.02em;
  margin: 14px 0 26px;
  color: var(--panel-ink);
  line-height: 1.15;
}

/* Below 900px the rail is reassurance the visitor did not ask for, and stacking
   it pushes the actual form off the first screen. */
@media (max-width: 900px) {
  .auth { min-height: 0; }
  .auth__aside { display: none; }
  .auth__form { padding: 32px 20px 48px; min-height: 100vh; }
}

/* Core-parity hooks with no look of their own. Kept because third-party WHMCS
   hooks and addons target them by name; removing the class would break those
   integrations while changing nothing visually here.

   These MUST stay empty. .login-form sits alongside .stack on login.tpl's form
   and whmcs.css loads after smk.css, so at equal specificity any declaration
   here wins: `display:block` here silently cancelled `.stack`'s
   `display:flex`, which deleted the 18px gap between every field and left the
   submit button at its intrinsic width instead of stretching to the form. */
.login-form { /* inherits .stack */ }

/* The renewal price inside a cart line. Emphasised because it is the number a
   customer is actually agreeing to for next year, and it sits inside a sentence
   where it would otherwise read as body copy. */
.renewal-price {
  font-weight: 500;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* ==========================================================================
   5. Full-bleed auth shell
   In the approved design, login.html, register.html, forgot-password.html,
   reset-password.html and two-factor.html are standalone pages: no utility bar,
   no masthead, no footer. The split screen IS the viewport, and the status rail
   is what stands in for the marketing chrome.

   WHMCS includes header.tpl and footer.tpl on every page with no way for a
   template to opt out, so the suppression has to happen in CSS. head.tpl stamps
   tpl-<templatefile> onto <body>, which is the only hook that identifies the
   page before any of its own markup exists.

   The pages are listed one by one rather than matched with a wildcard: a
   template that loses its navigation by accident is a dead end for the visitor,
   so adding a page to this list has to be a deliberate act.

   The live-chat button and the cookie bar are deliberately NOT hidden — the
   approved login.html carries both.
   ========================================================================== */
body:is(.tpl-login,
        .tpl-clientregister,
        .tpl-password-reset-container,
        .tpl-two-factor-challenge,
        .tpl-two-factor-new-backup-code,
        .tpl-banned,
        .tpl-access-denied,
        .tpl-user-verify-email,
        .tpl-user-invite-accept,
        .tpl-user-switch-account,
        .tpl-user-switch-account-forced) > :is(.topbar, .header, .footer) {
  display: none;
}

/* The second factor's input is rendered by whichever 2FA module is active and
   arrives as core Bootstrap markup this theme cannot author, so the design's
   one-time-code treatment has to be applied from the outside. Scoped to the
   challenge form so no other core input is caught by it. */
.twofa-challenge input.form-control {
  font-family: var(--mono);
  font-size: 22px;
  letter-spacing: .35em;
  text-align: center;
}

/* The collapsed extension list on the domain step, switched on by the order
   form's "offer extensions in a dropdown" control.

   OUTSIDE @layer whmcs-bootstrap on purpose. This is not a Bootstrap class
   being corrected, it is a component of this theme that the approved design has
   no equivalent for — the design's TLD row is a flat .cluster of .chip, which is
   what still renders with the control off. A <details> is used rather than a
   scripted panel because open and closed is exactly what the browser already
   does, and this order form loads no JavaScript of its own.

   The marker is drawn here rather than left to the UA: Safari and Firefox draw
   different triangles in different places, and the disclosure sits inside a card
   whose other rows are flush. */
details.tlddrop {
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface2);
}
details.tlddrop > summary {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  font-size: 12px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink2);
  cursor: pointer;
  list-style: none;
}
details.tlddrop > summary::-webkit-details-marker { display: none; }
details.tlddrop > summary::after {
  content: "";
  width: 8px;
  height: 8px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
  transition: transform .2s;
}
details.tlddrop[open] > summary::after { transform: translateY(2px) rotate(-135deg); }
details.tlddrop > summary:focus-visible { outline: 2px solid var(--amber); outline-offset: -2px; }
.tlddrop__body { padding: 0 14px 14px; }

/* The account picker on user-switch-account. Each row is a submit button so the
   page needs no JavaScript to work; these rules give a <button> the look of the
   design's list rows. */
.acctpick { display: flex; flex-direction: column; gap: 10px; }
.acctpick__item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: left;
  font: inherit;
  font-size: 14.5px;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 14px 16px;
  cursor: pointer;
  transition: border-color .2s, transform .2s;
}
.acctpick__item:hover:not(:disabled) { border-color: var(--amber); transform: translateY(-1px); }
.acctpick__item:disabled { opacity: .5; cursor: not-allowed; }
.acctpick__name { flex: 1; min-width: 0; font-weight: 500; }

/* Grouped form controls.

   The design system carries no fieldset or legend rule, because the approved
   static pages never grouped controls. WHMCS forms have to: a set of radios
   (register / transfer / use my own domain) or a set of checkboxes (ID
   protection, DNS management, email forwarding) is meaningless to a screen
   reader without a <fieldset> and a <legend> naming the group. Left unstyled
   the browser default adds a groove border, an inset and a floated legend,
   which would visibly break the card the group sits in. This resets the box
   only — the legend takes its typography from the utility class on it
   (.t-eyebrow, .t-h5), exactly like any other heading in the design.

   min-width:0 is the long-standing fieldset bug: without it a fieldset refuses
   to shrink below its content inside a flex or grid parent, so a wide input
   inside one forces the page to scroll sideways. */
fieldset { border: 0; margin: 0; padding: 0; min-width: 0; }
legend { display: block; width: 100%; padding: 0; }

/* Gateway hand-off pages.
   invoice-payment.tpl and 3dsecure.tpl hand the visitor to a payment gateway.
   These two names are a contract with WHMCS core's scripts.min.js, which toggles
   them by name, so they cannot be renamed into the design system's vocabulary.
   (.gateway-errors and .cc-input-container are defined in section 5 below.) */

/* The submit button carries both labels and core swaps .w-hidden between them
   on submit, so neither may add its own box — they are plain inline text. */
.pay-text, .click-text { display: inline; }

/* The wrapper around a gateway's own select and card inputs. Kept under core's
   name because payment modules target it, and given the stack spacing the
   design uses between fields — the module's partials supply the fields but
   nothing to space them with. */
.cc-payment-form { display: flex; flex-direction: column; gap: 16px; }

/* The two gateway iframes — the 3-D Secure challenge and the remote card form.
   A fixed pixel width overflows a phone, so the frame is fluid and only its
   height is fixed; the bank's own page scrolls inside it. The white ground is
   deliberate and not themed: the content is the bank's, not ours. */
.auth3d-area, .submit-3d {
  display: block;
  width: 100%;
  max-width: 100%;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: #fff;
}

/* ==========================================================================
   5. WHMCS core class names used inside our own markup
   These are core's names, kept so its JavaScript and any third-party module
   still bind. The stock themes style them in their own stylesheets, which this
   theme does not load — so without definitions here they render completely
   unstyled. Every value below resolves through the design tokens.
   ========================================================================== */

/* Gateway chooser. A radio list where the whole row is the target, because a
   14px radio dot is well under the 24px minimum for a touch target. */
.payment-methods { display: flex; flex-direction: column; gap: 8px; }
.payment-methods.is-hidden { display: none; }

.paymethod-info {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  font-size: 14px;
  color: var(--ink2);
}
.paymethod-info img { flex: none; height: 22px; width: auto; }

/* Saved-card and account radios. The input stays in the accessibility tree and
   remains keyboard-operable; the label carries the visible affordance.
   .existing-account is the bank counterpart of .existing-card, emitted by
   payment/bank/select.tpl; gateway modules read both by name. Where the radio
   sits inside a .check label — which it does on every page this theme renders —
   `.check input` is the more specific selector and keeps the native control
   hidden behind the design's own dot, so these declarations only apply when a
   module drops one of these radios in on its own. */
.account-select,
.existing-account,
.existing-card {
  flex: none;
  width: 18px;
  height: 18px;
  margin: 0;
  accent-color: var(--amber);
  cursor: pointer;
}
.account-select:focus-visible,
.existing-account:focus-visible,
.existing-card:focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 2px;
}

/* Card capture block. Hidden until a card gateway is chosen; core toggles it. */
.cc-input-container {
  display: block;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface2);
  padding: 18px;
}
.cc-input-container[hidden],
.cc-input-container.w-hidden { display: none; }

/* Marker class core puts on the new-card number field, carried by
   payment/card/inputs.tpl. Several gateway modules collect the fields they must
   tokenise with a .newccinfo selector, so the name has to stay on the input; the
   look comes from .input and .cc-number-field beside it. Declared here so the
   name is defined rather than silently absent from every stylesheet. */
.newccinfo { /* inherits .input */ }

/* Card number. Tabular figures and a wider tracking so a 16-digit string stays
   readable in groups, and never wraps mid-number. */
.cc-number-field {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: .06em;
  white-space: nowrap;
}

/* Core renders gateway validation failures into this container. It can be empty
   on load, so it must not reserve vertical space until it has content. */
.gateway-errors:empty { display: none; }
.gateway-errors {
  display: block;
  border: 1px solid var(--line);
  border-left: 3px solid var(--warn);
  border-radius: var(--r-md);
  background: var(--warn-t);
  padding: 13px 16px;
  margin-bottom: 16px;
  font-size: 14px;
  color: var(--warn);
}

/* SSL status badge beside a domain name. */
.ssl-info {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  vertical-align: middle;
  margin-left: 6px;
}
.ssl-info img { display: block; flex: none; }

/* .ssl-inactive is core's class, not this theme's. It arrives two ways: from
   SslStatus::getClass(), which emits "ssl-state ssl-inactive" onto the status
   image, and from the templates themselves onto the block that wraps it —
   clientareadomaindetails.tpl marks the status row with it exactly as core's
   themes mark their column. templates/smkcloud/js/whmcs.js binds a click on
   .ssl-state.ssl-inactive and sends the visitor to the SSL purchase route, so
   the affordance has to read as clickable; core's own stylesheet declares
   nothing but the cursor for it and this matches. */
.ssl-inactive { cursor: pointer; }

/* The SSL list's resend-approver-email result banner. Bound by
   templates/smkcloud/js/whmcs.js, which writes the outcome into this element
   with .text() and reveals it with removeClass('hidden') — so the name, and the
   fact that it starts hidden, are both part of core's contract. The look comes
   from .alert plus the alert-success / alert-danger core adds; the only thing
   owned here is the guarantee that an unfilled banner takes no space, since
   core never puts the hidden class back. */
.is-public .alert-table-ssl-manage:empty,
.is-app .alert-table-ssl-manage:empty { display: none; }

/* The button in each row that fires that request. Its look is .btn .btn--ghost
   .btn--sm; this class exists only as the handler's selector in the same
   bundle, and must survive any restyle of the row. */
.btn-resend-approver-email { cursor: pointer; }

/* Password fields core's strength meter and reveal toggle bind to. No look of
   its own — .input already carries that — but it must not be undefined, or a
   future rule targeting it would have nothing to extend. */
.pw-input { /* inherits .input */ }

/* Option tiles that carry a real radio.
   The approved design draws the payment chooser as .option-grid tiles, and in
   the static frontend each tile is a <button data-pick> that smk.js activates.
   A payment method has to POST its value, so the order form's checkout renders
   a real radio inside each tile instead. These three rules are what make that
   swap invisible: the native control is taken out of the flow but left in the
   accessibility tree, the checked tile gets the same look smk.js gives
   .option.is-active (so the state is correct before any script runs, and still
   correct in a browser without :has()), and keyboard focus is shown on the tile
   rather than on a control nobody can see. */
label.option { cursor: pointer; }
.option > input[type="radio"],
.option > input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}
.option:has(> input:checked) { background: var(--amber-t); border-color: var(--amber); }
.option:focus-within { outline: 2px solid var(--amber); outline-offset: 2px; }

/* ==========================================================================
   5. Order form — the pickers that have to post
   The approved order-configure design draws the location, size, operating
   system, add-on and billing-term controls as <button> elements whose selected
   state smk.js maintains. The WHMCS cart is a normal <form> POST, so each of
   those controls has to be a real radio or checkbox: the design's class moves
   onto a <label> that wraps the input, and these rules give that label the
   state styling smk.css defines for the button.

   Everything is keyed off :checked as well as .is-active, so the picker reads
   correctly with scripting off and before smk.js has run — the server decides
   what is selected, not the script.
   ========================================================================== */

/* A <fieldset> only appears where a radio or checkbox group needs its <legend>.
   The design has no grouped controls, so smk.css never had to reset the UA
   border, padding and min-width. Scoped to a class rather than the element so a
   payment gateway's own fieldset markup is left exactly as the gateway built
   it. min-width:0 stops a wide child from stretching the whole page — a
   fieldset ignores the usual flex/grid minimum. */
.smk-group { border: 0; margin: 0; padding: 0; min-width: 0; }

/* The label that stands in for the design's picker <button>. position:relative
   keeps the visually hidden input inside its own row, so focusing it with the
   keyboard scrolls to the row rather than to the top of the card. */
.pickrow, .option, .toggle, .seg > label { position: relative; cursor: pointer; }
.pickrow > input, .option > input, .toggle > input, .seg > label > input {
  position: absolute; width: 1px; height: 1px; margin: 0; opacity: 0;
}

.pickrow:has(> input:checked) { background: var(--amber-t); }
.pickrow:has(> input:checked) .pickrow__dot { border-color: var(--amber); background: var(--amber); }
.option:has(> input:checked) { background: var(--amber-t); border-color: var(--amber); }

/* The input is invisible, so its focus ring has to be drawn on the row. */
.pickrow:has(> input:focus-visible),
.option:has(> input:focus-visible) { outline: 2px solid var(--amber); outline-offset: -2px; }

/* upgrade.php prices one package per POST — it needs pid and that package's own
   billingcycle together — so each row of the upgrade list is its own <form>
   carrying the design's .pickrow class stack. A <form> is a block box that some
   engines still give a UA margin, and .pickrow draws the hairline between rows,
   so the margin has to go or the separators drift and the row grows taller than
   the design draws it. cursor:pointer belongs to the rows that are one big
   control; a form row is clicked on its own button, so it keeps the arrow. */
form.pickrow { margin: 0; cursor: auto; }

/* The design's tiles hold <div>s. A <label> may only contain phrasing content,
   so the same two lines are <span>s here and need the block display back. */
.option__title, .option__note { display: block; }

/* The add-on switch. smk.css keys the "on" look off aria-pressed, which only a
   button can carry; here the checkbox is the state. data-toggle is deliberately
   NOT used on these: smk.js would set aria-pressed on click, and that attribute
   would then style an "on" switch whose checkbox is off. */
.toggle:has(> input:checked) { background: var(--amber); }
.toggle:has(> input:checked) > span { transform: translateX(18px); }
.toggle:has(> input:focus-visible) { outline: 2px solid var(--amber); outline-offset: 2px; }

/* The billing-term control. smk.css styles `.seg > button`; the label wrapping
   each radio needs the same box and the same selected state. A WHMCS product
   can offer six terms where the design drew two, so the strip is allowed to
   wrap rather than overflow the column.

   Scoped to the label variant: unscoped, this also reached the design's own
   button-based .seg — the vps-hosting billing switch — which smk.css draws as a
   single nowrap pill and which the approved page never wraps. */
.seg:has(> label) { flex-wrap: wrap; }
.seg > label {
  display: inline-flex; align-items: center; padding: 10px 20px;
  font-size: 14px; font-weight: 500; color: var(--ink2);
  transition: background .25s ease, color .2s ease;
}
.seg > label:has(> input:checked) { background: var(--amber); color: var(--on-amber); }
.seg > label:hover:not(:has(> input:checked)) { color: var(--ink); }
.seg > label:has(> input:focus-visible) { outline: 2px solid var(--amber); outline-offset: -2px; }

/* Order form: category navigation, wide vs narrow.

   WHMCS marks one $secondarySidebar panel with the `mobileSelect` extra, which
   means "offer this panel as a <select> on small screens instead of a list".
   The order form honours that: sidebar-categories.tpl paints the panel as the
   design's tab strip and, for a flagged panel, also emits the dropdown from
   sidebar-categories-selector.tpl, with these two helpers deciding which one is
   on screen. smk.css has no breakpoint-scoped visibility utility — .hidden is
   unconditional — and this is the only place in the theme that needs one, so it
   lives here rather than in the design system.

   700px is where a strip of five or more category names stops fitting on one
   line and starts wrapping into a block that pushes the catalogue below the
   fold. */
.smk-wide-only { display: flex; }
.smk-narrow-only { display: none; }

@media (max-width: 700px) {
  .smk-wide-only { display: none; }
  .smk-narrow-only { display: block; }
}

/* Ticket reply rating.

   The design system has no star control, because the approved static pages
   never showed one — WHMCS's per-reply rating is a core feature this theme has
   to render, and viewticket.tpl is the only page that uses it. Both reference
   themes mark up five <span class="star"> elements and let core's jQuery
   handler navigate on click; this theme renders anchors instead so the control
   works from the keyboard and without JavaScript, which is also why core's
   "span.star" selector deliberately does not match them.

   The colours are the design's own amber and muted-ink tokens, so the control
   follows the light and dark palettes with no variant of its own. */
.rating { display: inline-flex; align-items: center; gap: 2px; }
.rating .star {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  font-size: 17px;
  line-height: 1;
  color: var(--line2);
  border-radius: var(--r-md);
  transition: color .15s ease, transform .15s ease;
}
a.star:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }
/* Hovering a star fills it and every star to its left, which is how a rating
   control is expected to read. Achieved without script by painting the whole
   row on hover and clearing the ones after the pointer. Scoped away from the
   already-rated state, which has no interactive stars to preview. */
.rating:not(.rating--done):hover .star { color: var(--amber); }
.rating:not(.rating--done) .star:hover ~ .star { color: var(--line2); }
.rating .star.is-on { color: var(--amber); }
.rating--done { gap: 8px; }
.rating--done .star { width: auto; height: auto; }

/* ==========================================================================
   Payment-method capture — account-paymentmethods-manage.tpl
   and account-paymentmethods-billing-contacts.tpl

   Every selector below is a WHMCS core name. templates/smkcloud/js/scripts.min.js
   (scrollToGatewayInputError, showCheckoutError, WHMCS.payment.display.*) and the
   page's own handler find the capture form and its five field groups by these
   exact class names, and a gateway module's JavaScript reveals them by animating
   an inline display. Renaming any of them breaks card and bank capture silently,
   so they are kept and given a look here, in tokens, rather than in smk.css.
   ========================================================================== */

/* The capture form and its two detail wrappers. The design classes on the same
   elements (.stack, .form-grid) supply the layout; min-width:0 is what stops a
   long card number, or a wide field a gateway injects into #paymentGatewayInput,
   from stretching the whole column instead of scrolling inside it. */
.frm-credit-card-input,
.cc-details,
.bank-details,
.submit-container { min-width: 0; }

/* The five mutually exclusive groups. jQuery .show() restores an element to its
   stylesheet display, so declaring block here is what makes a group come back
   as a block rather than as whatever jQuery guessed. */
.fieldgroup-creditcard,
.fieldgroup-bankaccount,
.fieldgroup-auxfields,
.fieldgroup-remoteinput { display: block; }

/* Shown between choosing a tokenised gateway and its remote form arriving. */
.fieldgroup-loading { display: block; padding: 24px 0; color: var(--ink3); }

/* Gateway validation feedback. Core writes the message into .gateway-errors or
   .assisted-cc-input-feedback and slides it open, so it must not be hidden by a
   class it cannot remove — the template hides it with an inline display instead.
   .notice and .notice--warn on the same element carry the look. */
.assisted-cc-input-feedback { min-width: 0; }

/* Per-field validation message. Carries .field__error alongside for the design's
   look; these two declarations are what a bare .field-error-msg emitted by a
   payment module still gets. */
.field-error-msg { font-size: 12.5px; color: var(--warn); }

/* Billing-address chooser. The page's handler looks a row up by
   .billing-contact-{id} and reads the address out of the seven spans inside it,
   so those generic names have to survive. They are scoped to the container so
   nothing else in the theme can collide with a class called .name or .city. */
.billing-contact-0,
[class*="billing-contact-"] { min-width: 0; }

#innerBillingContactsContainer .name { font-weight: 500; color: var(--ink); }
#innerBillingContactsContainer .address1,
#innerBillingContactsContainer .address2,
#innerBillingContactsContainer .city,
#innerBillingContactsContainer .state,
#innerBillingContactsContainer .postcode,
#innerBillingContactsContainer .country { color: var(--ink3); }

/* ==========================================================================
   Billing address on the gateway pages — payment/billing-address.tpl

   The same seven span names appear again in the chooser the card and bank
   capture partials include, under a different container. Core owns both names:
   hideNewCardInputFields() and hideNewAccountInputFields() in
   templates/smkcloud/js/scripts.min.js re-select a saved address by looking up
   `#billingAddressChoice label.billing-contact-{id}`, and .cc-billing-address is
   the group name a gateway module toggles when it collects the address itself.
   Scoped to the container so nothing else can collide with a class called
   .name or .city.
   ========================================================================== */

#billingAddressChoice .name { font-weight: 500; color: var(--ink); }
#billingAddressChoice .address1,
#billingAddressChoice .address2,
#billingAddressChoice .city,
#billingAddressChoice .state,
#billingAddressChoice .postcode,
#billingAddressChoice .country { color: var(--ink3); }

/* Declared for the same reason as .fieldgroup-*: jQuery .show() restores an
   element to its stylesheet display, so a module that hides and re-shows this
   group gets a grid back rather than whatever jQuery guessed. The layout itself
   comes from .form-grid on the same element. */
.cc-billing-address { display: grid; min-width: 0; }

/* ==========================================================================
   Two-factor modal shell — clientareasecurity.tpl, user-security.tpl

   The whole enable/disable/backup-code flow is driven by core's framework in
   templates/smkcloud/js/scripts.min.js, which addresses #modalAjax, .modal-title,
   .modal-body, .modal-submit, #modalAjaxClose and .loader by name. .system-modal
   is core's own marker on that dialog; .loader is the "working…" line the
   framework toggles while the modal's POST is in flight.
   ========================================================================== */

.system-modal .modal-footer { display: flex; align-items: center; gap: 10px; }

/* Hidden until core shows it, and pushed away from the buttons so the two do not
   read as one control. */
.system-modal .loader { display: none; margin-right: auto; color: var(--ink3); }
.system-modal .loader::before {
  content: '';
  display: inline-block;
  vertical-align: -2px;
  width: 12px;
  height: 12px;
  margin-right: 8px;
  border: 2px solid var(--line2);
  border-top-color: var(--amber);
  border-radius: 50%;
  animation: spin .7s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
  .system-modal .loader::before { animation: none; }
}

/* The trigger. assets/js/AjaxModal.js binds a delegated click on '.open-modal'
   (line 10) and calls preventDefault, so the element it opens from does not have
   to be an anchor and cannot rely on the UA's link cursor — MarketConnect.js
   puts the same class on plain <div>s. Everything else about the look comes from
   the .btn classes sitting beside it, which is why this is the only declaration. */
.open-modal { cursor: pointer; }

/* The name that labels the shell. AjaxModal.js copies data-modal-class onto
   #modalAjax in openModal() (line 69), so the two-factor enable and disable
   flows both open the dialog above under this class. It carries no look of its
   own — .system-modal is the whole dialog — but it must not be undefined, or a
   rule scoped to the two-factor flow would have nothing to extend. */
.twofa-setup { /* inherits .system-modal */ }

/* The enable/disable pair. Core's two-factor flow renders both copies of every
   status word and every button and swaps .w-hidden between them when the modal
   completes, so the two are always in the markup at once and exactly one is
   displayed. Visibility is .w-hidden's job (section 3); this name is only the
   handle core selects the pair by, and the look comes from the .btn or the
   .kv__v each copy sits in. */
.twofa-config-link { /* visibility is owned by .w-hidden */ }

/* ==========================================================================
   Password capture hooks — user-password.tpl, user-invite-accept.tpl,
   clientregister.tpl, password-reset-change-prompt.tpl

   Two names core puts on any form that carries a new-password field.
   .generate-password is bound in templates/smkcloud/js/whmcs.js (line 376),
   which reads data-targetfields off the button and opens the generator dialog
   in includes/generate-password.tpl. .using-password-strength is core's marker
   on the form element itself — the selector its own pwstrength include locks the
   submit button through. This theme locks by id instead, but the name is kept so
   a WHMCS hook or a gateway module that looks for it still finds the form.

   Neither carries a look: the trigger is a .btn and the form is a .stack.
   ========================================================================== */

.generate-password { /* inherits .btn */ }
.using-password-strength { /* inherits .stack */ }

/* The "passwords do not match" alert from includes/pwstrength.tpl. It ships
   empty and is filled by script only when the pair diverges, but it is a flex
   item of the surrounding .stack, so while empty it still collected a gap on
   each side — 36px of blank between the strength meter and the password tips
   that nothing was ever going to occupy. Same treatment as .smk-captcha:empty
   and .gateway-errors:empty; the moment script writes the message it is no
   longer :empty and takes its place in the rhythm. */
#inputNewPassword2Msg:empty { display: none; }

/* ==========================================================================
   Linked-account status text — includes/linkedaccounts.tpl,
   orderforms/smkcloud/linkedaccounts.tpl

   Core's remote-authn script clones the innerHTML of a
   p.providerLinkingMsg-preLink-<state> paragraph out of the hidden
   #providerLinkingMessages bank and drops it in here, so the name is core's and
   the element is empty until a link attempt reports back. Its sibling names need
   no rules — they only ever live inside the hidden bank — but this one paints,
   and undefined it renders as a bare unstyled paragraph next to the designed
   provider buttons.

   It is given the design's .notice look rather than a new one, so a linking
   result reads like every other inline status message in the theme. The cloned
   markup leads with <strong> on the error and success states, which .notice
   already treats as the message title.
   ========================================================================== */

.providerLinkingFeedback:not(:empty) {
  display: block;
  margin-top: 12px;
  border: 1px solid var(--line);
  border-left: 3px solid var(--amber);
  border-radius: var(--r-md);
  background: var(--surface);
  padding: 15px 18px;
  font-size: 14px;
  line-height: 1.6;
  color: var(--ink2);
}
.providerLinkingFeedback > p { margin: 0; }
.providerLinkingFeedback strong {
  display: block;
  margin-bottom: 2px;
  font-weight: 500;
  color: var(--ink);
}

/* ==========================================================================
   Nameserver inputs — clientareadomaindetails.tpl, bulkdomainmanagement.tpl

   The "use default nameservers" / "use custom nameservers" radio pair calls
   core's disableFields('domnsinputs', …) from templates/smkcloud/js/scripts.min.js,
   which collects the fields by this class name and toggles their disabled
   attribute. The name is a contract with that function and cannot be renamed,
   so it is kept on every nameserver field and given the one look the theme's
   own .input has no rule for: the switched-off state core puts them in.
   ========================================================================== */

.domnsinputs[disabled] {
  background: var(--surface2);
  border-color: var(--line);
  color: var(--ink3);
  cursor: not-allowed;
}

/* ==========================================================================
   WHOIS contact fields — clientareadomaincontactinfo.tpl,
   bulkdomainmanagement.tpl

   Core's useDefaultWhois() / useCustomWhois() in scripts.min.js take the id of
   the radio that was clicked, strip its trailing digit to recover the contact
   type, and then enable one of <type>defaultwhois / <type>customwhois while
   disabling the other. The class names are therefore built per contact type at
   render time — Registrantcustomwhois, Admindefaultwhois and so on — which is
   why the rules below match on the suffix rather than on a fixed name. The
   registrar decides which contact types exist, so no complete list can be
   written out here.

   They carry only the switched-off appearance: the theme's .input and .select
   have no [disabled] rule of their own, and without one half of every panel
   looks editable while core has in fact disabled it.
   ========================================================================== */

[class*="defaultwhois"][disabled],
[class*="customwhois"][disabled] {
  background: var(--surface2);
  border-color: var(--line);
  color: var(--ink3);
  cursor: not-allowed;
}

/* The fields an ICANN change-of-registrant is judged on. Core tags them here and
   compares each one's value against its data-original-value on submit; if any
   differs it opens #modalIRTPConfirmation instead of saving. It is a marker for
   that comparison and nothing paints it, so the only declaration it carries is
   the one that keeps it usable: core scrolls the first changed field into view,
   and the client-area header is sticky, so the field needs somewhere to land. */
.irtp-field { scroll-margin-block: 96px; }

/* ==========================================================================
   Ticket custom fields container — supportticketsubmit-steptwo.tpl

   #customFieldsContainer is a contract with refreshCustomFields() in
   templates/smkcloud/js/whmcs.js, which does jQuery("#customFieldsContainer")
   .load("submitticket.php", {action:"getcustomfields", …}) on every department
   change. The element therefore has to be in the form before a department is
   picked and has to survive each reload, which means it is present even for the
   many departments that define no custom fields at all.

   Left as an ordinary box it is still a flex item of the form's .stack, and an
   empty flex item consumes a gap on each side: the design's single 18px step
   between the attachments field and the submit row becomes 36px on every
   department without custom fields. display:contents drops the wrapper's own
   box while keeping its children, so the fields it holds are spaced by the
   form's own .stack gap exactly like every other field, and an empty container
   costs nothing. The .stack class stays on the element as the fallback for any
   engine that does not honour display:contents.
   ========================================================================== */
#customFieldsContainer { display: contents; }

/* ==========================================================================
   Knowledgebase suggestion list — supportticketsubmit-kbsuggestions.tpl

   .kbarticles is WHMCS's own wrapper name. Core's getTicketSuggestions() drops
   this fragment straight into #autoAnswerSuggestions, and both reference themes
   put the class on the list, so addons and custom stylesheets that target the
   suggestion list find it here too. The rows inside are the design's
   .listnav__item, which already carries the padding, hairline and hover, so the
   wrapper only has to hold them and clip its corners to the .card--flush it
   sits in. Nothing here paints a colour of its own.
   ========================================================================== */
.kbarticles { display: block; border-radius: 0 0 var(--r-lg) var(--r-lg); overflow: hidden; }

/* ==========================================================================
   MarketConnect promotions — orderforms/smkcloud/marketconnect-promo.tpl

   .mc-promo is WHMCS's own wrapper name. Core renders one promotion block per
   entry of $addonsPromoOutput and the standard cart bundle addresses each block
   by this class and by its #promo_<productKey> id — it fades the block out once
   the upsell has been taken. This theme loads no cart bundle, so the block is
   static, but the class is carried so a site that restores that bundle still
   finds it, and it must therefore be defined here or the block renders
   unstyled. The box itself is the design's .card; all this adds is the amber
   edge that marks a promoted item apart from the add-on rows beside it.
   $promotion->getClass() also lands on this element as a vendor slug (ssl,
   sitelock, weebly …); those are hooks for per-vendor overrides, not styles
   this theme defines.
   ========================================================================== */
.mc-promo { border-left: 3px solid var(--amber); }

/* Social share widgets on announcements.

   announcements.tpl and viewannouncement.tpl render these only when the matching
   WHMCS setting is switched on (Facebook Recommend / Twitter Tweet on the
   announcement pages). Both class names are bound by the vendor libraries and
   cannot be renamed: Facebook's all.js finds every .fb-like and replaces it with
   an iframe, and Twitter's widgets.js upgrades every a.twitter-share-button in
   place. Only the pre-upgrade state is styled here — the space the widget will
   occupy, and a token-coloured button for the second or two before the vendor
   script arrives — so neither can flash as unstyled markup in either palette. */
.fb-like {
  display: block;
  max-width: 100%;
  min-height: 20px;
  overflow: hidden;
}

a.twitter-share-button {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 32px;
  padding: 0 14px;
  border: 1px solid var(--line2);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--ink2);
  font-size: 13px;
  line-height: 1;
  text-decoration: none;
}

a.twitter-share-button:hover {
  border-color: var(--amber);
  color: var(--amber-d);
}

/* Gateway hand-off button.

   forwardpage.tpl drops the payment module's own <form> into #frmPayment
   untouched — the markup is generated by the gateway and the ids and field names
   are its contract, so nothing there can be given a class from the template.
   That form's submit control is the only way forward when scripting is off, so
   it is matched by shape rather than by class and given the primary button's own
   treatment in design tokens. */
#frmPayment input[type="submit"],
#frmPayment input[type="image"],
#frmPayment button[type="submit"],
#frmPayment button:not([type]) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 42px;
  padding: 11px 22px;
  border: 1px solid var(--amber);
  border-radius: var(--r-md);
  background: var(--amber);
  color: var(--on-amber);
  font-family: inherit;
  font-size: 14.5px;
  font-weight: 500;
  line-height: 1;
  cursor: pointer;
}

#frmPayment input[type="image"] {
  padding: 0;
  border: 0;
  background: none;
  min-height: 0;
}

#frmPayment input[type="submit"]:hover,
#frmPayment button[type="submit"]:hover,
#frmPayment button:not([type]):hover {
  background: var(--amber-d);
  border-color: var(--amber-d);
}

#frmPayment input[type="submit"]:focus-visible,
#frmPayment button[type="submit"]:focus-visible,
#frmPayment button:not([type]):focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 2px;
}

/* ---------------------------------------------------------------------------
   Metered-usage pricing dialog — usagebillingpricing.tpl.

   .modal-metric-pricing is not a style of ours; it is the hook name the stock
   themes put on this dialog, and billing addons that widen or annotate the
   pricing staircase select on it. The design's own .modal/.modal--sm carry the
   whole look, so the only job here is to keep the staircase readable when a
   metric reports many tiers: the dialog body scrolls instead of growing past
   the viewport, which a plain .modal--sm would otherwise do.
   --------------------------------------------------------------------------- */
.modal-metric-pricing .modal__body {
  max-height: min(60vh, 520px);
  overflow-y: auto;
}

/* ---------------------------------------------------------------------------
   Server module output — clientareaproductdetails.tpl.

   .module-client-area wraps $moduleclientarea, which is HTML the server module
   built against the stock Bootstrap themes and which we neither control nor
   rewrite. It is given a normal flow context and a floor on its controls so a
   module that ships bare <a>/<button> elements is still clickable at a touch
   target size, without this theme trying to restyle markup it has not seen.
   --------------------------------------------------------------------------- */
.module-client-area > *:last-child {
  margin-bottom: 0;
}

.module-client-area a,
.module-client-area button {
  max-width: 100%;
}

/* Card brand mark on the number field.

   jquery.payment.js (assets/js/jquery.payment.js, loaded by
   account-paymentmethods-manage.tpl and payment/card/validate.tpl) puts the
   detected brand on .cc-number-field as a bare class — visa, mastercard, amex
   and the rest — and account-paymentmethods-manage.tpl also renders the saved
   card's brand there server-side, from the CreditCard model's own card type.
   Neither stylesheet defined those names, so the class was emitted and did
   nothing. The marks are WHMCS core's own artwork in /assets/img/payment, four
   directories up from this file.

   .unsupported is the same contract read from the other end: the templates'
   validation calls cardNumber.hasClass('unsupported') to pick the error message,
   so the field has to show that state before the form is submitted, not only
   after. */
.cc-number-field {
  background-repeat: no-repeat;
  background-position: right 16px center;
  background-size: 34px auto;
  padding-right: 62px;
}

.cc-number-field.visa { background-image: url("../../../../assets/img/payment/visa.png"); }
.cc-number-field.mastercard { background-image: url("../../../../assets/img/payment/mastercard.png"); }
.cc-number-field.amex { background-image: url("../../../../assets/img/payment/amex.png"); }
.cc-number-field.dinersclub { background-image: url("../../../../assets/img/payment/dinersclub.png"); }
.cc-number-field.discover { background-image: url("../../../../assets/img/payment/discover.png"); }
.cc-number-field.unionpay { background-image: url("../../../../assets/img/payment/unionpay.png"); }
.cc-number-field.jcb { background-image: url("../../../../assets/img/payment/jcb.png"); }
.cc-number-field.maestro { background-image: url("../../../../assets/img/payment/maestro.png"); }
.cc-number-field.forbrugsforeningen { background-image: url("../../../../assets/img/payment/forbrugsforeningen.png"); }
.cc-number-field.dankort { background-image: url("../../../../assets/img/payment/dankort.png"); }
.cc-number-field.troy { background-image: url("../../../../assets/img/payment/troy.png"); }
.cc-number-field.unknown { background-image: url("../../../../assets/img/payment/unknown.png"); }

/* A card we cannot take. No brand mark to show, and the border says so before
   the visitor reaches the submit button. */
.cc-number-field.unsupported {
  border-color: var(--warn);
  background-image: none;
  padding-right: 18px;
}

/* MarketConnect store markup. These are core's own class names — .service is
   used ~930 times across the stock themes and .store-promoted-product /
   .cart-total-amount are read by core's cart JavaScript — so they are kept
   verbatim and given a look here in tokens. */
.store-promoted-product {
  display: flex;
  flex-direction: column;
  gap: 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  padding: 20px;
}
.store-promoted-product.is-featured { border-color: var(--amber); }

.cart-total-amount {
  font-variant-numeric: tabular-nums;
  font-weight: 500;
  color: var(--ink);
  white-space: nowrap;
}

/* A provisioned service row inside a store/addon panel. */
.service {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 12px 0;
  border-top: 1px solid var(--line);
  font-size: 14px;
  color: var(--ink2);
}
.service:first-child { border-top: 0; }

/* ---------------------------------------------------------------------------
   MarketConnect — OX App Suite mailbox manager (store/ox/manage.tpl).

   Every name below is read or written by the script inside that template, which
   is core's own logic carried over from the reference theme: it clones the
   hidden prototype rows once per mailbox, fills the named spans from the JSON
   the list-accounts endpoint returns, and toggles the two delete questions. So
   none of them may be renamed, and they are defined here rather than in
   smk.css because they belong to WHMCS, not to the design system.

   Three of them are not even written by the template: .number, .limit and
   .email arrive inside WHMCS's own language strings — ox.accountCount and
   ox.setPasswordFor carry the markup — so the bridge is the only place they can
   be styled at all.
   ------------------------------------------------------------------------ */

/* The mailbox table. Each mailbox is one <tbody> and its aliases a second one
   directly beneath, so the separator belongs between groups, not between rows. */
.ox-table-accounts tbody + tbody { border-top: 1px solid var(--line); }
.ox-table-accounts tr.loading > td,
.ox-table-accounts tr.no-accounts > td { color: var(--ink3); text-align: center; }
.ox-table-accounts .account,
.ox-table-accounts .alias { font-weight: 500; color: var(--ink); }
.ox-table-accounts .limit,
.ox-table-accounts .alias-count { font-variant-numeric: tabular-nums; }
.ox-table-accounts .email-aliases { color: var(--ink3); }
.ox-table-accounts tr.create-alias > td { background: var(--surface2); }

/* .number and .limit come out of the ox.accountCount language string. */
#accountCount .number,
#accountCount .limit { font-variant-numeric: tabular-nums; color: var(--ink); }

/* The prototypes the script clones. They must not be visible themselves, and
   the clone re-enables display by removing w-hidden. */
.cloneAccountsBody,
.cloneAliasesBody,
.cloneAliasRow { display: none; }

/* Row actions. The button styling is the design system's; these only stop a
   three-button group from wrapping mid-label inside a narrow cell. */
.ox-table-accounts .manage-account,
.ox-table-accounts .set-ox-password,
.ox-table-accounts .ox-delete,
.ox-table-accounts .ox-create-alias,
.ox-table-accounts .ox-delete-alias { white-space: nowrap; }
.ox-create-alias .create-string { display: inline; }

/* Exactly one of the two delete questions is revealed, by jQuery .toggle(),
   which sets an inline display — so both have to start hidden from CSS. */
.delete-question,
.delete-aliases-question { display: none; }

/* Connection settings, filled in once the configuration endpoint answers. A
   hostname and a port are values to be copied, so both are set in the mono
   face the design system uses for anything transcribable. */
.pop-hostname,
.incoming-hostname,
.outgoing-hostname,
.calendar-server { font-family: var(--mono); font-size: 13px; color: var(--ink); }
.pop-port,
.incoming-port,
.outgoing-port { font-family: var(--mono); font-size: 12px; color: var(--ink3); }

/* Revealed with its tab only once the endpoint returns a migration URL. */
.migration-url { text-decoration: none; }

/* .required and .match are the script's markers for "must not be empty" and
   "must equal the field named in data-smk-match-field". They carry no appearance of
   their own — the visible state is .field plus .field-error-msg, both already
   defined — so this only keeps the marked inputs filling their field. */
#frmOxAddAccount .required,
#frmOxAddAccount .match,
#frmOxManageAccount .required,
#frmOxSetPassword .required,
#frmOxSetPassword .match { width: 100%; }

/* The dialogs' confirm button. A fixed floor stops it resizing between "Add",
   "Save Changes" and "Delete" as the same control is reused across dialogs. */
.ox-submit-button { min-width: 96px; }

/* The mailbox email address, printed by ox.setPasswordFor and friends. */
.modal__title .email,
.modal__body .email { font-family: var(--mono); }

/* ==========================================================================
   6. Accessibility corrections to the approved design
   Each of these is a deviation from the signed-off stylesheet, made only where
   the original fails WCAG AA and the change is imperceptible. They live here
   rather than in smk.css so that file stays a byte-identical copy — the
   fidelity check depends on that, and a design-system update stays a file copy.
   ========================================================================== */

/* The closing CTA band prints dark ink on brand amber at 78% opacity, which
   composites to 4.43:1 — just under the 4.5:1 required for body text, and it
   was measured failing in a real browser rather than inferred. Raising the
   alpha to 82% reaches 4.78:1. A 4% opacity delta is not perceptible; text a
   reader cannot resolve is. Full opacity would be 6.37:1, but that loses the
   deliberate softening the design intends. */
.cta-band p { color: rgba(26, 26, 28, .82); }


/* ==========================================================================
   Bootstrap modal — deliberately OUTSIDE @layer whmcs-bootstrap.

   WHMCS core emits <div class="modal"> for its validation and confirmation
   dialogs. The design system also defines .modal, but for a native
   <dialog>, which the user-agent stylesheet positions when showModal() runs —
   so smk.css sets no position at all.

   Inside the layer these rules lost to that unlayered .modal, and a core
   warning rendered inline in the document flow: transparent, unpositioned,
   sitting on top of the page content. div.modal and .modal-* cannot match a
   <dialog>, so there is no collision here to arbitrate and no layer is
   wanted — plain specificity is both correct and sufficient.
   ========================================================================== */
.is-public div.modal, .is-app div.modal {
  display: none; position: fixed; inset: 0; z-index: 900;
  overflow: hidden auto; outline: 0; -webkit-overflow-scrolling: touch;
}
/* No `div.modal.in { display: block }` here, and that omission is deliberate —
   Bootstrap 3's own stylesheet has no such rule either. modal.js reveals a
   dialog with $element.show(), which writes display:block as an inline style,
   and only then adds `in`; hide() reverses both. Adding the class rule made
   `in` sufficient on its own, so any markup that ships the class statically —
   the registration page's third-party e-mail-verification hook emits
   <div class="modal fade in" id="emailrequerido"> — painted an untriggered
   warning dialog over the page on first load. */
.is-public .modal-open, .is-app .modal-open { overflow: hidden; }
.is-public .modal-dialog, .is-app .modal-dialog {
  position: relative; width: auto; max-width: 620px; margin: 40px auto; padding: 0 16px;
}
.is-public .modal-lg, .is-app .modal-lg { max-width: 900px; }
.is-public .modal-sm, .is-app .modal-sm { max-width: 400px; }
.is-public div.modal.fade .modal-dialog, .is-app div.modal.fade .modal-dialog {
  transform: translateY(-18px); transition: transform .28s ease-out;
}
.is-public div.modal.in .modal-dialog, .is-app div.modal.in .modal-dialog { transform: none; }
/* Bootstrap paints its own backdrop element where the design's dialog uses the
   native ::backdrop. Same scrim values as smk.css .modal::backdrop, so a core
   modal and a designed one dim the page identically. */
.is-public .modal-backdrop, .is-app .modal-backdrop {
  position: fixed; inset: 0; z-index: 890;
  background: rgba(16, 20, 28, .55); backdrop-filter: blur(2px);
}
.is-public .modal-backdrop.fade, .is-app .modal-backdrop.fade { opacity: 0; }
.is-public .modal-backdrop.in, .is-app .modal-backdrop.in { opacity: 1; }

/* Bootstrap's dismiss button, also outside the layer for the same reason as the
   modal it sits in. Core renders it as <button class="close">&times;</button>
   with no other class, so unstyled it inherits the browser's default button
   chrome — a grey bordered box, which is exactly how it appeared on the
   registration page. The design system has no .close of its own, so there is no
   collision and specificity alone is right. */
.is-public .close,
.is-app .close {
  appearance: none;
  border: 0;
  background: transparent;
  padding: 4px;
  margin: -4px -4px -4px 8px;
  font-size: 22px;
  line-height: 1;
  font-weight: 400;
  color: var(--ink3);
  cursor: pointer;
  border-radius: var(--r-md);
  /* 24px is the minimum target size; the glyph alone is well under it. */
  min-width: 32px;
  min-height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  opacity: 1;
  text-shadow: none;
  transition: background .2s, color .2s;
}
.is-public .close:hover, .is-app .close:hover { background: var(--surface2); color: var(--ink); }
.is-public .close:focus-visible, .is-app .close:focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 2px;
}

/* The header core builds around that button. */
.is-public .modal-header, .is-app .modal-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
}
.is-public .modal-title, .is-app .modal-title {
  font-size: 18px;
  font-weight: 500;
  letter-spacing: -.01em;
  color: var(--ink);
  margin: 0;
}

/* ==========================================================================
   7. Client-area rail — sticky, self-scrolling, account menu pinned
   The approved design's sidebar assumed a short nav. A real WHMCS install has
   five groups (Account, Billing, Support, Profile, plus any panel a module
   injects), which makes the rail taller than the viewport. With the stock
   `min-height:100vh` + `margin-top:auto` the account block was pushed below the
   fold, so signing out meant scrolling the whole page to find it.

   The rail is now its own scroll container, exactly one viewport tall, with the
   navigation scrolling inside it and the account row sticky at its foot. The
   page content scrolls independently, which is the behaviour every dashboard a
   customer already uses has.
   ========================================================================== */

@media (min-width: 901px) {
  .app__side {
    position: sticky;
    top: 0;
    height: 100vh;
    min-height: 0;              /* overrides the design's min-height:100vh */
    overflow: hidden;           /* the inner nav owns the scrolling */
  }

  /* Only the navigation scrolls, so the wordmark and account row stay put. */
  .app__side > .snav {
    flex: 1 1 auto;
    overflow-y: auto;
    overscroll-behavior: contain;
    /* Room for the sticky account block plus a little breathing space. */
    padding-bottom: 8px;
    scrollbar-width: thin;
  }
  .app__side > .snav::-webkit-scrollbar { width: 6px; }
  .app__side > .snav::-webkit-scrollbar-thumb {
    background: var(--line2);
    border-radius: var(--r-pill);
  }
}

.app__user {
  position: sticky;
  bottom: 0;
  background: var(--surface);
  /* Sits above the scrolling nav so rows do not show through the gap. */
  z-index: 2;
  padding-bottom: 4px;
}

/* The collapsed account row that opens the menu. */
.app__userbtn {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  background: transparent;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background .2s, border-color .2s;
}
.app__userbtn:hover { background: var(--surface2); border-color: var(--line); }
.app__userbtn:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }

.app__userid { flex: 1; min-width: 0; }
.app__username {
  display: block;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.app__useracct { display: block; font-size: 10.5px; color: var(--ink3); }

.app__usercaret {
  width: 9px;
  height: 9px;
  flex: none;
  color: var(--ink3);
  transition: transform .25s var(--ease);
}
[aria-expanded="true"] .app__usercaret { transform: rotate(180deg); }

/* The menu opens upward — it is anchored to the foot of the viewport, so a
   downward menu would be clipped by the rail's own overflow. */
.menu--up {
  top: auto;
  bottom: calc(100% + 6px);
  max-height: min(70vh, 520px);
  overflow-y: auto;
}
.menu-wrap.w-full { display: block; width: 100%; }

/* Below 900px the rail is an off-canvas drawer that scrolls as one column, so
   the sticky treatment is neither needed nor wanted — a sticky footer inside a
   drawer covers the last nav item on a short screen. */
@media (max-width: 900px) {
  .app__user { position: static; }
  .menu--up { bottom: auto; top: calc(100% + 6px); }
}

/* --------------------------------------------------------------------------
   Header and footer options
   --------------------------------------------------------------------------
   Three pieces of chrome that only exist when an operator switches them on, on
   the Header and Footer tabs of the addon. They live here rather than in
   smk.css because the approved design has no search box in the masthead and no
   payment row in the footer — this is the theme adding to the system, not
   changing it.
   -------------------------------------------------------------------------- */

/* The masthead search. Hidden below the width at which the masthead's own
   buttons give way to the drawer: a search field squeezed beside a burger is
   two controls fighting for the same 60px, and the knowledgebase page carries
   the same search one tap away. */
.header__search {
  display: flex;
  align-items: center;
  gap: 6px;
}

.header__search .input {
  width: clamp(140px, 18vw, 220px);
  height: 38px;
  padding-block: 0;
  font-size: 14px;
}

@media (max-width: 1080px) {
  .header__search { display: none; }
}

/* Accepted payment methods. A row of named marks above the legal line, each
   one icon plus its own word — a lone glyph tells a customer nothing, and
   these are deliberately generic rather than scheme trademarks. */
.footer__pay {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 20px;
  padding: 22px 0 0;
}

.footer__pay-item {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 24px;
  padding: 6px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  font-size: 13px;
  color: var(--ink2);
}

/* The social row's target area is 38px, comfortably past the 24px minimum, and
   its colour is a defined token in both palettes rather than an inherited one
   that only happens to work in light mode. What was missing was the focus
   ring: the row is a list of links reached by keyboard, and the reset strips
   the browser's own outline from anchors. */
.footer__social a:focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 2px;
  border-color: var(--amber);
  color: var(--amber-d);
}

/* An icon inside the row must not be shrunk by the flex line to a sliver. */
.footer__social a svg { flex: none; }

/* ==========================================================================
   PRODUCT DESCRIPTIONS WRITTEN AS MARKUP
   --------------------------------------------------------------------------
   The spec list on a plan card is not template markup — it is whatever the
   operator typed into the product's Description field in admin, and this
   catalogue's products carry two different conventions, both inherited from
   the Lagom theme this install came from:

     .list-info / .spec        the Dell builds — a label above its value
     .package-features         the Xeon builds — an icon beside a label/value

   Neither is a design-system class, so neither belongs in smk.css; but with no
   rules at all they rendered as unstyled inline text, which put a plan's CPU,
   RAM, disk and bandwidth on one unreadable line at the very top of the order
   funnel. These rules restate them in the design's own spec-row shape — the
   amber-keyed label over a muted value — so a card looks the same whichever
   convention its description happens to use.

   Scoped to .smk-productdesc, the wrapper products.tpl puts round a description
   it has decided to print as written, so nothing here can reach an unrelated
   page that happens to use the same generic class names.
   ========================================================================== */

/* Both description conventions render as one horizontal strip of chips: an
   icon, a small uppercase label and the value. flex-wrap means a single line on
   desktop and a tidy two-or-three-line wrap on a phone, with no breakpoints to
   maintain. Previously these stacked into a ~400px tower that pushed the whole
   order funnel down the page. */
.smk-productdesc .package-features,
.smk-productdesc .smk-specrow {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  margin: 10px 0 0;
  padding: 0;
  list-style: none;
}

.smk-productdesc .package-features li,
.smk-productdesc .list-info {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin: 0;
  padding: 5px 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface);
  white-space: nowrap;
}

/* The label. .spec is the Dell convention; the Xeon convention leaves its label
   unclassed, so the <span> holding it is matched by position instead. */
.smk-productdesc .spec,
.smk-productdesc .package-features li > span {
  /* --ink2, not --ink3. The chips shrank, and --ink3 is the token that already
     measured 4.42:1 elsewhere — pairing the weakest colour with the smallest
     type is where a label stops being readable. Size carries the hierarchy
     here; contrast does not have to pay for it. */
  color: var(--ink2);
  font-size: 9px;
  letter-spacing: .05em;
  text-transform: uppercase;
  line-height: 1.35;
}

/* The value. <b> is the Xeon convention's emphasis; the Dell convention puts it
   in the second bare <span>. */
.smk-productdesc .package-features b,
.smk-productdesc .list-info > span:not(.spec) {
  display: block;
  color: var(--ink);
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  line-height: 1.35;
}

/* WHMCS runs the stored description through nl2br, so the newlines between
   list items arrive as <br> elements. Left alone they become flex items and
   blow the row apart. Only strays between chips are dropped — the <br> inside a
   chip still separates its label from its value. */
.smk-productdesc .package-features > br,
.smk-productdesc > br { display: none; }

.smk-productdesc .custom-icon,
.smk-productdesc .smk-specicon {
  width: 15px;
  height: 15px;
  flex: 0 0 auto;
  opacity: .9;
}

@media (max-width: 560px) {
  .smk-productdesc .package-features li,
  .smk-productdesc .list-info { padding: 5px 9px; }
  .smk-productdesc .package-features b,
  .smk-productdesc .list-info > span:not(.spec) { font-size: 11px; }
}

.smk-productdesc :is(ul, ol):not(.package-features) {
  margin: 0;
  padding-left: 18px;
}

.smk-productdesc p { margin: 0 0 7px; }
.smk-productdesc p:last-child { margin-bottom: 0; }

/* --------------------------------------------------------------------------
   .disabled — the class WHMCS's cart bundle toggles.
   --------------------------------------------------------------------------
   scripts.js flips a bare .disabled class on buttons fifteen times, and twelve
   of those never set the disabled ATTRIBUTE alongside it. Bootstrap would
   normally supply the rule; no Bootstrap CSS is loaded here, and smk.css styles
   only the attribute forms — so a button core believes it has disabled during
   an AJAX round trip stayed bright and clickable, inviting a double submit.
   Matched to smk.css's own disabled treatment so the two cannot drift.
   -------------------------------------------------------------------------- */
.btn.disabled,
.disabled > .btn {
  opacity: .5;
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   .smk-tabcount — the plan count on a catalogue tab.
   --------------------------------------------------------------------------
   The tab strip names each product line; the count tells the customer how much
   is behind it before they click, which is the whole reason the strip beats a
   dropdown. Sized down and set in the muted ink so it reads as an annotation on
   the tab rather than as part of the line's name, and given its own tabular
   figures so a two-digit count does not shift the tab's width against a
   one-digit neighbour.
   -------------------------------------------------------------------------- */
.smk-tabcount {
  margin-left: 6px;
  color: var(--ink3);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}

.tabs > .is-active .smk-tabcount { color: var(--ink2); }

/* --------------------------------------------------------------------------
   The reCAPTCHA badge and the chat button share a corner.
   --------------------------------------------------------------------------
   Google pins .grecaptcha-badge to the bottom-right, which is exactly where
   this design puts .chat-fab — so on checkout, the one page carrying an
   invisible reCAPTCHA, the badge rendered underneath the chat button with its
   own text clipped. The badge cannot be removed (Google's terms require either
   it or an equivalent notice), so it is lifted clear of the button instead and
   left below it in stacking order, keeping the chat control clickable.
   -------------------------------------------------------------------------- */
.grecaptcha-badge {
  bottom: 84px !important;
  z-index: 40 !important;
}

@media (max-width: 700px) {
  .grecaptcha-badge { bottom: 76px !important; }
}

/* ---------------------------------------------------------------------------
   Configure step — sparse-content fallback
   ---------------------------------------------------------------------------
   The order form renders one numbered step per thing the customer can actually
   choose: each configurable option group, the addons, the billing cycle, the
   server fields. A product carrying none of those still has a billing cycle, so
   it renders a single step — and a single step sitting beside a full-height
   order summary leaves a tall empty column that reads as a broken page.

   Unlayered on purpose: these rules have to beat the Bootstrap block in
   @layer whmcs-bootstrap above, and unlayered CSS wins over layered regardless
   of specificity.

   The template counts the steps it emitted and adds .split--solo at one or
   fewer, which stacks the summary under the steps in one centred column. The
   page then looks deliberate at any content length rather than only when a
   product happens to be fully configured.
   --------------------------------------------------------------------------- */
.split--solo {
  display: block;
  max-width: 760px;
  /* Deliberately NOT centred. The product header above this block sits in the
     page's normal .wrap gutter, so centring the steps put the title on one left
     edge and "01 Choose Billing Cycle" on another. Left-aligning keeps every
     element on the page sharing a single left edge. */
  margin-inline: 0;
}

.split--solo .split__main { width: auto; }

.split--solo .split__side {
  max-width: none;
  margin-top: 28px;
  /* Beats the inline position:sticky the sticky sidebar carries. Sticking is
     pointless once the summary is below the content it summarises. */
  position: static !important;
  top: auto !important;
}

/* Small pill. smk.css ships .pill at 14px/6px 14px, which is sized for a page
   hero; on the configure step it is a step marker above a deliberately quiet
   title, so it reads as an eyebrow rather than a heading. Unlayered so it beats
   the Bootstrap layer without needing !important. */
.pill--sm {
  padding: 3px 9px;
  font-size: 10.5px;
  letter-spacing: .05em;
  text-transform: uppercase;
}

/* ---------------------------------------------------------------------------
   Header currency switcher
   ---------------------------------------------------------------------------
   Sits in .header__actions beside the theme toggle: both are display
   preferences, and a shopper comparing prices looks for currency in the chrome
   at the top rather than at the foot of the page.

   Rendered only inside the order flow (see header.tpl) because WHMCS processes
   a currency change in cart.php alone.
   --------------------------------------------------------------------------- */
.header__currency { display: inline-flex; align-items: center; }

/* A compact input, sized for chrome rather than for a form. smk.css has no
   small variant, and the header row would look wrong carrying a full-height
   control beside the .btn--sm buttons. */
.input--sm {
  padding: 7px 12px;
  font-size: 13px;
}

/* .select paints its caret with background-image at a fixed offset, so the
   small variant has to restate both the right padding and the caret position or
   the arrow lands on top of the text. */
.select.input--sm {
  padding-right: 32px;
  background-position: calc(100% - 15px) 55%, calc(100% - 10px) 55%;
}

.header__currency .select {
  min-width: 78px;
  /* Matches the ghost buttons either side of it rather than the form fields
     further down the page. */
  background-color: transparent;
}

/* The header collapses to the burger well before this, and a currency code is
   the first thing to lose when space runs out. */
@media (max-width: 900px) {
  .header__currency { display: none; }
}


/* ---------------------------------------------------------------------------
   Order progress rail — numbered badges with connectors
   ---------------------------------------------------------------------------
   Replaces the top-border bar treatment with the requested badge-and-connector
   form: a circled number, its label, and a rule running to the next step.

   Every selector is two classes deep (.steps--rail .steps__item) so it outranks
   the single-class base in smk.css on specificity alone — smk.css stays
   byte-identical and the base .steps treatment still works anywhere it is used
   without the modifier.
   --------------------------------------------------------------------------- */
.steps--rail {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 0;
}

.steps--rail .steps__item {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  gap: 10px;
  /* The base rule paints a 3px top bar; the badge carries state here. */
  border-top: 0;
  padding: 0;
  margin: 0;
  min-width: 0;
}

.steps--rail .steps__item:last-child { flex: 0 0 auto; }

/* The connector. It lives on the item rather than between items so it can
   absorb whatever space is left after the badges and labels are laid out. */
.steps--rail .steps__item::after {
  content: '';
  flex: 1 1 auto;
  height: 1px;
  min-width: 18px;
  margin-left: 14px;
  background: var(--line);
}

.steps--rail .steps__item:last-child::after { display: none; }

.steps--rail .steps__n {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 1px solid var(--line2);
  background: var(--surface);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  letter-spacing: 0;
  color: var(--ink2);
}

.steps--rail .steps__label {
  margin-top: 0;
  font-size: 14px;
  color: var(--ink2);
  white-space: nowrap;
}

/* Completed: filled badge, connector carries the brand colour through. */
.steps--rail .steps__item.is-done .steps__n {
  background: var(--amber);
  border-color: var(--amber);
  color: #fff;
}
.steps--rail .steps__item.is-done .steps__label { color: var(--ink); }
.steps--rail .steps__item.is-done::after { background: var(--amber); }

/* Current: outlined rather than filled, so "where I am" reads differently from
   "what I finished" without relying on colour alone. */
.steps--rail .steps__item[aria-current="step"] .steps__n {
  border-color: var(--amber);
  background: var(--amber-t);
  color: var(--amber-d);
  font-weight: 600;
}
.steps--rail .steps__item[aria-current="step"] .steps__label {
  color: var(--ink);
  font-weight: 500;
}

a.steps--rail .steps__item:hover .steps__label,
.steps--rail a.steps__item:hover .steps__label { color: var(--amber-d); }

/* Below ~720px the connectors stop earning their space: labels wrap and the
   line ends up longer than the text it joins. Two per row, no rules. */
@media (max-width: 720px) {
  .steps--rail { gap: 12px 16px; }
  .steps--rail .steps__item { flex: 0 0 calc(50% - 8px); }
  .steps--rail .steps__item::after { display: none; }
  .steps--rail .steps__label { font-size: 13px; white-space: normal; }
}

/* ---------------------------------------------------------------------------
   Positive-state highlighting (--ok-t / #e8fbc6)
   ---------------------------------------------------------------------------
   The token, never the raw hex: --ok-t is #e8fbc6 in the light palette and a
   translucent lime in the dark one. Hard-coding the hex would leave a bright
   mint block sitting in a dark page.
   --------------------------------------------------------------------------- */

/* An add-on the customer has switched on. The toggle alone reports state at one
   small point in the row; tinting the whole row makes "this is being added to
   my bill" legible at a glance, which matters most on a page whose whole job is
   confirming what is about to be charged. :has() carries this with no template
   change and no JS, so the highlight cannot drift out of sync with the input. */
.card__row:has(input[type="checkbox"][name^="addons"]:checked) {
  background: var(--ok-t);
  box-shadow: inset 3px 0 0 var(--ok);
}

/* Selected configurable options — the radio pickers for Location and OS. The
   chosen card already carries the brand outline; the tint is what survives a
   glance down a column of six. */
.pickrow:has(input[type="radio"]:checked),
.check--radio:has(input[type="radio"]:checked) {
  background: var(--ok-t);
}

/* The reassurance list beside the order summary. Plain text on the page
   background, it read as filler; on its own tinted panel it reads as the
   guarantee it is. */
.stack .tick { padding: 2px 0; }

.trust-panel {
  background: var(--ok-t);
  border: 1px solid color-mix(in srgb, var(--ok) 22%, transparent);
  border-radius: var(--r-lg);
  padding: 14px 16px;
}

.trust-panel .tick { font-size: 14px; color: var(--ink); }

/* Free or included line items. A zero price is easy to skim past as an
   oversight; naming it as included is the point. */
.badge--included {
  background: var(--ok-t);
  color: var(--ok);
  font-weight: 500;
}

/* ---------------------------------------------------------------------------
   Primary button — operator-selectable preset
   ---------------------------------------------------------------------------
   The four values come from the Branding → Primary button style setting, which
   hooks.php emits as custom properties: the dark palette on bare :root and the
   light palette on :root:not([data-theme="dark"]), matching how every other
   colour in this theme is split.

   Each property falls back to the design system's own token, so a site running
   without the addon — or with the setting unset — still paints its primary
   action exactly as smk.css intends.

   Unlayered and loaded after smk.css, so this wins on source order at equal
   specificity without needing !important.
   --------------------------------------------------------------------------- */
.btn--primary {
  background: var(--btn-bg, var(--amber));
  color: var(--btn-fg, var(--on-amber));
}

.btn--primary:hover,
.btn--primary:focus-visible {
  background: var(--btn-bg-hover, var(--amber-d));
  color: var(--btn-fg-hover, var(--on-amber-d));
}
