/* ==========================================================================
   Quick View - Polaris-flavoured UI
   --------------------------------------------------------------------------
   Modelled on the BOGOS admin, which is built on @shopify/polaris v13. Quick
   View is server-rendered Blade with no build step, so rather than pulling in
   React + Polaris this reimplements Polaris' design tokens in plain CSS and
   restyles the widgets the app already uses (Bootstrap 3, bootstrap-switch,
   bootstrap-colorpicker).

   Loaded LAST so it wins the cascade over bootstrap.min.css / dashboard.css.
   No markup contract is changed: every form field keeps its name and id, and
   the existing JS keeps working.
   ========================================================================== */

:root {
    /* Surface + text, Polaris v13 palette */
    --qv-bg: #f1f1f1;
    --qv-surface: #ffffff;
    --qv-surface-hover: #f7f7f7;
    --qv-surface-selected: #f1f1f1;
    --qv-text: #303030;
    --qv-text-secondary: #616161;
    --qv-text-disabled: #b5b5b5;
    --qv-border: #e3e3e3;
    --qv-border-strong: #8a8a8a;

    /* Interactive */
    --qv-fill-brand: #303030;
    --qv-fill-brand-hover: #1a1a1a;
    --qv-focus: #005bd3;

    /* Status */
    --qv-success-bg: #cdfee1;
    --qv-success-text: #0c5132;
    --qv-warning-bg: #ffd6a4;
    --qv-warning-text: #5e4200;
    --qv-critical-bg: #ffd6d6;
    --qv-critical-text: #8e0b21;
    --qv-info-bg: #d6e5ff;
    --qv-info-text: #00366d;

    /* Shape + depth */
    --qv-radius: 8px;
    --qv-radius-lg: 12px;
    --qv-shadow-card: 0 1px 0 rgba(0, 0, 0, .05);
    --qv-shadow-pop: 0 4px 12px rgba(0, 0, 0, .12);

    /* 4px spacing scale */
    --qv-sp-1: 4px;
    --qv-sp-2: 8px;
    --qv-sp-3: 12px;
    --qv-sp-4: 16px;
    --qv-sp-5: 20px;
    --qv-sp-6: 24px;
    --qv-sp-8: 32px;

    --qv-topbar-h: 56px;
}

/* --------------------------------------------------------------- base --- */

body.qv-app {
    background: var(--qv-bg);
    color: var(--qv-text);
    font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
        "Helvetica Neue", Arial, sans-serif;
    font-size: 13px;
    line-height: 20px;
    -webkit-font-smoothing: antialiased;
}

body.qv-app h1, body.qv-app h2, body.qv-app h3,
body.qv-app h4, body.qv-app h5 {
    color: var(--qv-text);
    font-weight: 650;
    letter-spacing: -.01em;
    margin: 0;
}

body.qv-app a { color: var(--qv-focus); }
body.qv-app a:hover, body.qv-app a:focus { color: #004299; text-decoration: underline; }

body.qv-app :focus-visible {
    outline: 2px solid var(--qv-focus);
    outline-offset: 1px;
    border-radius: var(--qv-radius);
}

/* ------------------------------------------------------------- topbar --- */

.qv-topbar {
    position: sticky;
    top: 0;
    z-index: 30;
    height: var(--qv-topbar-h);
    display: flex;
    align-items: center;
    gap: var(--qv-sp-3);
    padding: 0 var(--qv-sp-5);
    background: var(--qv-surface);
    border-bottom: 1px solid var(--qv-border);
}

/* The bar ships hidden and is revealed only outside the admin. Needed
   explicitly: an author `display: flex` beats the UA stylesheet's
   `[hidden] { display: none }`, so the attribute alone would not hide it. */
.qv-topbar[hidden] { display: none; }

.qv-topbar__brand {
    display: flex;
    align-items: center;
    gap: var(--qv-sp-2);
    font-weight: 650;
    font-size: 14px;
    color: var(--qv-text);
}

.qv-topbar__mark {
    width: 24px;
    height: 24px;
    border-radius: 6px;
    background: var(--qv-fill-brand);
    color: #fff;
    display: grid;
    place-items: center;
    font-size: 12px;
}

.qv-topbar__shop {
    color: var(--qv-text-secondary);
    font-size: 12px;
    margin-left: var(--qv-sp-2);
    padding-left: var(--qv-sp-3);
    border-left: 1px solid var(--qv-border);
}

.qv-topbar__spacer { flex: 1; }

.qv-topbar__link {
    font-size: 13px;
    font-weight: 500;
    color: var(--qv-text);
    padding: 6px var(--qv-sp-3);
    border-radius: var(--qv-radius);
}
.qv-topbar__link:hover { background: rgba(0, 0, 0, .05); color: var(--qv-text); text-decoration: none; }

/* ------------------------------------------------------------- shell ---- */

.qv-shell {
    max-width: 1000px;
    margin: 0 auto;
    padding: var(--qv-sp-6) var(--qv-sp-5) var(--qv-sp-8);
}

.qv-main { flex: 1; min-width: 0; }

/* -------------------------------------------------------------- card ---- */

.qv-card {
    background: var(--qv-surface);
    border: 1px solid var(--qv-border);
    border-radius: var(--qv-radius-lg);
    box-shadow: var(--qv-shadow-card);
    padding: var(--qv-sp-5);
}

.qv-card + .qv-card { margin-top: var(--qv-sp-4); }

.qv-card__header {
    display: flex;
    align-items: flex-start;
    gap: var(--qv-sp-3);
    margin: calc(var(--qv-sp-5) * -1) calc(var(--qv-sp-5) * -1) var(--qv-sp-4);
    padding: var(--qv-sp-4) var(--qv-sp-5);
    border-bottom: 1px solid var(--qv-border);
}

.qv-card__title { font-size: 14px; font-weight: 650; }
.qv-card__subtitle { color: var(--qv-text-secondary); margin-top: 2px; font-size: 13px; }

/* -------------------------------------------------------- page header --- */

.qv-page-header {
    display: flex;
    align-items: center;
    gap: var(--qv-sp-4);
    margin-bottom: var(--qv-sp-5);
}

.qv-page-header__title { font-size: 20px; font-weight: 650; letter-spacing: -.02em; }
.qv-page-header__sub { color: var(--qv-text-secondary); margin-top: 2px; }
.qv-page-header__actions { margin-left: auto; display: flex; gap: var(--qv-sp-2); }

/* ------------------------------------------------------------ badges ---- */

.qv-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px var(--qv-sp-2);
    border-radius: var(--qv-radius);
    font-size: 12px;
    font-weight: 500;
    background: var(--qv-surface-selected);
    color: var(--qv-text);
}

.qv-badge--success { background: var(--qv-success-bg); color: var(--qv-success-text); }
.qv-badge--warning { background: var(--qv-warning-bg); color: var(--qv-warning-text); }
.qv-badge--critical { background: var(--qv-critical-bg); color: var(--qv-critical-text); }
.qv-badge--info { background: var(--qv-info-bg); color: var(--qv-info-text); }

/* ----------------------------------------------------------- banners ---- */

.qv-banner {
    display: flex;
    gap: var(--qv-sp-3);
    padding: var(--qv-sp-3) var(--qv-sp-4);
    border-radius: var(--qv-radius-lg);
    margin-bottom: var(--qv-sp-4);
    border: 1px solid transparent;
}

.qv-banner--success { background: var(--qv-success-bg); color: var(--qv-success-text); }
.qv-banner--warning { background: var(--qv-warning-bg); color: var(--qv-warning-text); }
.qv-banner--critical { background: var(--qv-critical-bg); color: var(--qv-critical-text); }
.qv-banner--info { background: var(--qv-info-bg); color: var(--qv-info-text); }
.qv-banner a { color: inherit; text-decoration: underline; }

.qv-banner__text { flex: 1; font-size: 13px; line-height: 1.5; }

.qv-banner__close {
    flex: none;
    align-self: flex-start;
    background: none;
    border: 0;
    padding: 0 2px;
    font-size: 18px;
    line-height: 1;
    color: inherit;
    opacity: .6;
    cursor: pointer;
}

.qv-banner__close:hover { opacity: 1; }

/* ============================ Bootstrap 3 overrides ====================== */
/* The pages still use Bootstrap markup; these make it read as Polaris.      */

body.qv-app .btn {
    border-radius: var(--qv-radius);
    font-size: 13px;
    font-weight: 600;
    padding: 6px 14px;
    border: 1px solid var(--qv-border);
    background: var(--qv-surface);
    color: var(--qv-text);
    box-shadow: 0 1px 0 rgba(0, 0, 0, .05);
    text-shadow: none;
    transition: background .12s ease, border-color .12s ease;
}

body.qv-app .btn:hover,
body.qv-app .btn:focus { background: var(--qv-surface-hover); color: var(--qv-text); }

body.qv-app .btn-primary,
body.qv-app .btn-success,
body.qv-app .btn-info {
    background: var(--qv-fill-brand);
    border-color: var(--qv-fill-brand);
    color: #fff;
}

body.qv-app .btn-primary:hover, body.qv-app .btn-primary:focus,
body.qv-app .btn-success:hover, body.qv-app .btn-success:focus,
body.qv-app .btn-info:hover, body.qv-app .btn-info:focus {
    background: var(--qv-fill-brand-hover);
    border-color: var(--qv-fill-brand-hover);
    color: #fff;
}

body.qv-app .btn-danger {
    background: #e51c00;
    border-color: #e51c00;
    color: #fff;
}

body.qv-app .btn-link { border-color: transparent; background: none; box-shadow: none; color: var(--qv-focus); }

/* Inputs */
body.qv-app .form-control {
    height: auto;
    min-height: 32px;
    padding: 6px var(--qv-sp-3);
    font-size: 13px;
    color: var(--qv-text);
    background: var(--qv-surface);
    border: 1px solid var(--qv-border-strong);
    border-radius: var(--qv-radius);
    box-shadow: none;
    transition: border-color .12s ease, box-shadow .12s ease;
}

body.qv-app .form-control:focus {
    border-color: var(--qv-focus);
    box-shadow: 0 0 0 2px rgba(0, 91, 211, .25);
}

body.qv-app .form-control[disabled],
body.qv-app .form-control[readonly] {
    background: #fafafa;
    color: var(--qv-text-disabled);
    border-color: var(--qv-border);
}

body.qv-app select.form-control { padding-right: var(--qv-sp-6); }

body.qv-app .control-label,
body.qv-app label {
    font-size: 13px;
    font-weight: 500;
    color: var(--qv-text);
    padding-top: 6px;
}

body.qv-app .form-group { margin-bottom: var(--qv-sp-4); }

/* One label column width across both settings screens.
   The Free screen's rows are col-sm-3 label + col-sm-6 field; the Pro screen's
   are col-sm-2 + a mix of 2/3/4/6/8. At col-sm-2 (16.67%) labels like
   "Quick View Button Position:" wrap onto three lines while their field sits
   beside the first, so every row on the Pro screen was a different height and
   nothing lined up down the column.
   Fixed here rather than by rewriting 69 grid classes in the Pro tab partials:
   one rule, and the two screens cannot drift apart again.

   Scoped to col-sm-2 / col-sm-3 rather than to .control-label generally: the
   Promotion tab also uses `col-sm-12 control-label` as a full-width heading
   above the rich-text editor, and squeezing that to a quarter width left it
   floating in the middle of the row. */
@media (min-width: 768px) {
    body.qv-app .qv-settings-card .control-label.col-sm-2,
    body.qv-app .qv-settings-card .control-label.col-sm-3 {
        width: 25%;
        padding-right: var(--qv-sp-4);
    }
}

/* ------------------------------------------- button template picker ------ */
/* The three Quick View button previews on the Pro General tab.

   Left as three plain radio + image pairs - no tiles, no borders. The only
   problem being fixed is the alignment: a bare radio next to a 39px-tall image
   aligns to that image's text baseline, so it sat low and read as belonging to
   another row. vertical-align pulls both onto the same centre line, and the
   margin gives the radio some room instead of butting against the preview. */

body.qv-app input[name='bnt_template'] {
    vertical-align: middle;
    margin: 0 var(--qv-sp-2) 0 0;
}

body.qv-app input[name='bnt_template'] + img {
    vertical-align: middle;
}

/* A little air above and below, so the row is not pinched between the two
   selects it sits between. */
body.qv-app .qv-settings-card .form-group:has(input[name='bnt_template']) {
    padding-top: var(--qv-sp-1);
    padding-bottom: var(--qv-sp-1);
}

/* A full-width .control-label is a heading over the control below it, not a
   label beside one - the Promotion tab uses one above the rich-text editor.
   Bootstrap right-aligns every .control-label in a horizontal form, which
   pushed that heading to the far right, a screen away from what it labels. */
body.qv-app .qv-settings-card .control-label.col-sm-12 {
    text-align: left;
    margin-bottom: var(--qv-sp-2);
}

/* Help text under a field lines up with the field, not with the label. */
body.qv-app .qv-settings-card .help-block { margin-top: 6px; }

body.qv-app .help-block { color: var(--qv-text-secondary); font-size: 12px; }

/* Panels -> cards */
body.qv-app .panel {
    background: var(--qv-surface);
    border: 1px solid var(--qv-border);
    border-radius: var(--qv-radius-lg);
    box-shadow: var(--qv-shadow-card);
}
body.qv-app .panel-heading {
    background: var(--qv-surface);
    border-bottom: 1px solid var(--qv-border);
    border-radius: var(--qv-radius-lg) var(--qv-radius-lg) 0 0;
    font-weight: 650;
}

/* Modals */
body.qv-app .modal-content {
    border: none;
    border-radius: var(--qv-radius-lg);
    box-shadow: var(--qv-shadow-pop);
}
body.qv-app .modal-header { border-bottom: 1px solid var(--qv-border); padding: var(--qv-sp-4) var(--qv-sp-5); }
body.qv-app .modal-body { padding: var(--qv-sp-5); }
body.qv-app .modal-footer { border-top: 1px solid var(--qv-border); padding: var(--qv-sp-4) var(--qv-sp-5); }

/* Alerts */
body.qv-app .alert {
    border-radius: var(--qv-radius-lg);
    border: 1px solid transparent;
    font-size: 13px;
    padding: var(--qv-sp-3) var(--qv-sp-4);
}
body.qv-app .alert-success { background: var(--qv-success-bg); color: var(--qv-success-text); }
body.qv-app .alert-danger, body.qv-app .alert-error { background: var(--qv-critical-bg); color: var(--qv-critical-text); }
body.qv-app .alert-warning { background: var(--qv-warning-bg); color: var(--qv-warning-text); }
body.qv-app .alert-info { background: var(--qv-info-bg); color: var(--qv-info-text); }

/* ------------------------------------------------ horizontal tab strip -- */
/* The Settings panes (General / Popup / Colors / Translation) render as a
   Polaris-style tab row above the content. The anchors are the same Bootstrap
   tab triggers the page always used - only the direction changed, so the tab
   JS is untouched. */

.qv-tabstrip {
    background: var(--qv-surface);
    border: 1px solid var(--qv-border);
    border-radius: var(--qv-radius-lg);
    box-shadow: var(--qv-shadow-card);
    padding: var(--qv-sp-1) var(--qv-sp-2);
    margin-bottom: var(--qv-sp-4);
    overflow-x: auto;
}

.qv-tabstrip .nav-tabs {
    border: none;
    display: flex;
    gap: var(--qv-sp-1);
    flex-wrap: nowrap;
    margin: 0;
}

.qv-tabstrip .nav-tabs > li { float: none; margin: 0; }

.qv-tabstrip .nav-tabs > li > a,
.qv-tabstrip .nav-tabs > li.active > a,
.qv-tabstrip .nav-tabs > li.active > a:hover,
.qv-tabstrip .nav-tabs > li.active > a:focus {
    display: flex;
    align-items: center;
    gap: var(--qv-sp-2);
    white-space: nowrap;
    border: none;
    border-radius: var(--qv-radius);
    margin: 0;
    padding: 6px var(--qv-sp-3);
    background: transparent;
    color: var(--qv-text-secondary);
    font-size: 13px;
    font-weight: 500;
    line-height: 20px;
    text-decoration: none;
}

.qv-tabstrip .nav-tabs > li > a:hover {
    background: rgba(0, 0, 0, .05);
    color: var(--qv-text);
}

.qv-tabstrip .nav-tabs > li.active > a,
.qv-tabstrip .nav-tabs > li.active > a:hover,
.qv-tabstrip .nav-tabs > li.active > a:focus {
    background: var(--qv-fill-brand);
    color: #fff;
    font-weight: 600;
}

.qv-tabstrip .icon-wrapper {
    display: inline-block;
    width: 16px;
    text-align: center;
    color: inherit;
}

/* Panels get card treatment and lose the old bordered tab-content box. */
body.qv-app .tab-content {
    border: none;
    padding: 0;
    background: transparent;
}

body.qv-app .tab-content > .tab-pane {
    background: var(--qv-surface);
    border: 1px solid var(--qv-border);
    border-radius: var(--qv-radius-lg);
    box-shadow: var(--qv-shadow-card);
    padding: var(--qv-sp-5);
}

/* ---------------------------------------------------- page transitions - */
/* Navigation is full page loads, so a click can sit for a moment with no
   feedback. This is the only thing that survives the unload: a bar painted at
   the top of the outgoing page while the next one is fetched. */

.qv-navbar {
    position: fixed;
    top: 0;
    left: 0;
    height: 3px;
    width: 0;
    z-index: 2147483647;
    background: var(--qv-focus);
    opacity: 0;
    pointer-events: none;
}

.qv-navbar.is-active {
    opacity: 1;
    /* Creeps towards 90% and waits there; the page unloading is what ends it. */
    animation: qv-nav-progress 8s cubic-bezier(.1, .8, .2, 1) forwards;
}

@keyframes qv-nav-progress {
    0%   { width: 0; }
    40%  { width: 65%; }
    100% { width: 90%; }
}

/* A badge waiting on data. Deliberately quiet - it is a placeholder, not an
   alert, and it is usually on screen for well under a second. */
.qv-badge.is-loading {
    color: var(--qv-text-secondary);
    animation: qv-badge-pulse 1.2s ease-in-out infinite;
}

@keyframes qv-badge-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: .55; }
}

@media (prefers-reduced-motion: reduce) {
    .qv-navbar.is-active { animation-duration: 0s; width: 90%; }
    .qv-badge.is-loading { animation: none; }
}

/* ------------------------------------------------------- button spinner - */
/* The spinner is in the markup from the start and only revealed by
   .is-loading, so nothing has to be injected at submit time. */

.qv-spinner {
    display: none;
    width: 13px;
    height: 13px;
    margin-right: 6px;
    vertical-align: -2px;
    border: 2px solid currentColor;
    border-right-color: transparent;
    border-radius: 50%;
    animation: qv-spin .6s linear infinite;
}

.btn.is-loading .qv-spinner { display: inline-block; }
.btn.is-loading { cursor: progress; opacity: .85; }

@keyframes qv-spin { to { transform: rotate(360deg); } }

/* ------------------------------------------------------- disabled button - */
/* Save is disabled until the form differs from what is stored, so this state
   is the one a merchant sees most of the time - it has to read as "nothing to
   do here", not as "broken". Muted fill rather than a low opacity, so the
   label keeps enough contrast to stay legible. The :disabled selector alone
   would lose to the .btn-primary rules above, hence the matching specificity. */

body.qv-app .btn-primary[disabled],
body.qv-app .btn-primary:disabled {
    background: var(--qv-surface-hover);
    border-color: var(--qv-border);
    color: var(--qv-text-disabled);
    cursor: default;
    box-shadow: none;
}

body.qv-app .btn-primary[disabled]:hover,
body.qv-app .btn-primary[disabled]:focus,
body.qv-app .btn-primary:disabled:hover,
body.qv-app .btn-primary:disabled:focus {
    background: var(--qv-surface-hover);
    border-color: var(--qv-border);
    color: var(--qv-text-disabled);
}

/* Submitting outranks being disabled: once the form is posting, the button is
   disabled too, and it must still look like work in progress. */
body.qv-app .btn-primary.is-loading[disabled] {
    background: var(--qv-fill-brand);
    border-color: var(--qv-fill-brand);
    color: #fff;
    cursor: progress;
}

@media (prefers-reduced-motion: reduce) {
    .qv-spinner { animation-duration: 2s; }
}

/* ------------------------------------------------- bootstrap-switch ----- */
/* Restyled rather than replaced: #qv_disable is wired to the
   switchChange.bootstrapSwitch event and a confirmation modal, so swapping
   the widget out would change behaviour. */

/* Fixed 36x20 pill. bootstrap-switch measures its ON/OFF labels in JS and
   writes inline widths on the container and handles, which is what made this
   render as a ~100px slab - a min-width cannot shrink an inline width. So the
   handles are dropped entirely and the knob is positioned by state class
   instead of by the container's margin-left slide. */
/* 38x22 with a 16px knob leaves an even 2px all round. It was 36x20, which
   left only 1px: geometrically symmetric, but at fractional zoom or on a
   non-integer DPI the two sides round differently and the knob reads as
   off-centre. 2px survives that rounding. */
body.qv-app .bootstrap-switch {
    border: 1px solid var(--qv-border-strong);
    border-radius: 999px;
    box-shadow: none;
    width: 38px !important;
    height: 22px;
    min-width: 0;
    /* vertical-align: middle leaves it ~1.5px above the centre of the
       control-label beside it, which has Bootstrap's 6px padding-top. Nudged
       visually rather than with margin, so it does not move the row. */
    vertical-align: middle;
    position: relative;
    top: 2px;
}

body.qv-app .bootstrap-switch .bootstrap-switch-container {
    width: 100% !important;
    height: 100%;
    margin-left: 0 !important;
    position: relative;
    display: block;
    transform: none;
}

/* bootstrap-switch binds its click-to-toggle handlers to the ON and OFF
   handles, and only drag handlers to the knob. Hiding the handles therefore
   took the toggle away with them: the whole 38px pill was dead except the
   16px knob, whose drag maths depends on a handle width that is now 0. Worse,
   the knob slides on state change, so the one live target moved out from under
   the pointer after every successful toggle.

   So the pill is made inert as far as bootstrap-switch is concerned - nothing
   inside it receives pointer events - and a single delegated click handler in
   main_layout drives toggleState() instead. One code path, whole-pill target,
   and the switchChange.bootstrapSwitch event the confirm modal listens for
   still fires exactly as before. */
body.qv-app .bootstrap-switch .bootstrap-switch-handle-on,
body.qv-app .bootstrap-switch .bootstrap-switch-handle-off {
    display: none !important;
}

body.qv-app .bootstrap-switch { cursor: pointer; }
body.qv-app .bootstrap-switch > * { pointer-events: none; }

body.qv-app .bootstrap-switch.bootstrap-switch-disabled,
body.qv-app .bootstrap-switch.bootstrap-switch-readonly,
body.qv-app .bootstrap-switch.bootstrap-switch-indeterminate {
    cursor: not-allowed;
    opacity: .6;
}

/* Visible focus ring: the real checkbox is buried inside the widget, so
   focus has to be painted on the wrapper. */
body.qv-app .bootstrap-switch.bootstrap-switch-focused {
    outline: 2px solid var(--qv-focus, #005bd3);
    outline-offset: 1px;
}

body.qv-app .bootstrap-switch .bootstrap-switch-label {
    position: absolute;
    top: 2px;
    left: 2px;
    background: var(--qv-surface);
    border: none;
    border-radius: 999px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, .2);
    width: 16px !important;
    height: 16px;
    margin: 0;
    padding: 0;
    transition: left .15s ease;
}

/* Container is 36px wide inside the border: 36 - 16 - 2 = 18. */
body.qv-app .bootstrap-switch.bootstrap-switch-on .bootstrap-switch-label { left: 18px; }
body.qv-app .bootstrap-switch.bootstrap-switch-off .bootstrap-switch-label { left: 2px; }

body.qv-app .bootstrap-switch.bootstrap-switch-on { background: var(--qv-fill-brand); border-color: var(--qv-fill-brand); }
body.qv-app .bootstrap-switch.bootstrap-switch-off { background: #e3e3e3; }

/* ----------------------------------------------- bootstrap-colorpicker -- */

body.qv-app .input-group-addon {
    border: 1px solid var(--qv-border-strong);
    border-left: none;
    border-radius: 0 var(--qv-radius) var(--qv-radius) 0;
    background: var(--qv-surface);
    padding: 4px 8px;
}

body.qv-app .input-group .form-control:first-child { border-radius: var(--qv-radius) 0 0 var(--qv-radius); }

body.qv-app .input-group-addon i {
    display: block;
    width: 18px;
    height: 18px;
    border-radius: 4px;
    border: 1px solid rgba(0, 0, 0, .1);
}

/*
 * The picker itself was fine - it just looked broken.
 *
 * bootstrap-colorpicker's default panel is 130px wide holding a 100px
 * saturation square plus the hue and alpha strips, so the strips ended up
 * pressed against the panel edge and the whole thing read as a cropped image
 * sitting on top of the field. Same widget, given room to breathe and a gap
 * below the swatch it hangs from.
 */
body.qv-app .colorpicker {
    border-radius: var(--qv-radius-lg);
    box-shadow: var(--qv-shadow-pop);
    border: 1px solid var(--qv-border);
    padding: 8px;
    margin-top: 6px;
}

body.qv-app .colorpicker-saturation {
    width: 140px;
    height: 140px;
    border-radius: var(--qv-radius);
}

body.qv-app .colorpicker-hue,
body.qv-app .colorpicker-alpha {
    height: 140px;
    margin-left: 8px;
    border-radius: var(--qv-radius);
}

body.qv-app .colorpicker-color { margin-top: 8px; }

/* ------------------------------------------------------------ layout ---- */

@media (max-width: 900px) {
    .qv-shell { padding: var(--qv-sp-4); }
}

/* The legacy right-hand menu column and the old horizontal tab bar are
   replaced by the sidebar; hide any leftovers rather than deleting markup
   the pages still rely on. */
body.qv-app .container-fluid > .menu { display: none; }
body.qv-app .qv-main .nav-tabs.label-tabs { display: none; }

/* ------------------------------------------------- App Bridge elements -- */
/* <ui-nav-menu> is consumed by Shopify, which renders the links in the admin
   sidebar. It must never paint inside the iframe - without this it would show
   as a stack of raw links, and it would still be visible when App Bridge is
   absent (app opened outside the admin). */
ui-nav-menu { display: none !important; }

/* With page navigation living in the admin sidebar, the in-app sidebar only
   carries sub-navigation, so it can be narrower. */


/* ------------------------------------------------------- status strip --- */
/* Two panels sharing one card: theme app extension state, and plan. Mirrors
   the BOGOS admin. */

.qv-status {
    display: flex;
    background: var(--qv-surface);
    border: 1px solid var(--qv-border);
    border-radius: var(--qv-radius-lg);
    box-shadow: var(--qv-shadow-card);
    margin-bottom: var(--qv-sp-4);
    overflow: hidden;
}

.qv-status__panel {
    flex: 1 1 0;
    min-width: 0;
    padding: var(--qv-sp-4) var(--qv-sp-5);
}

.qv-status__panel + .qv-status__panel { border-left: 1px solid var(--qv-border); }

.qv-status__head {
    display: flex;
    align-items: center;
    gap: var(--qv-sp-2);
    margin-bottom: var(--qv-sp-2);
}

.qv-status__title { font-size: 13px; font-weight: 650; color: var(--qv-text); }

.qv-status__text {
    margin: 0;
    color: var(--qv-text-secondary);
    font-size: 13px;
}

.qv-status__meta {
    display: block;
    margin-top: 2px;
    font-size: 12px;
    color: var(--qv-text-disabled);
}

.qv-status__link {
    display: inline-block;
    margin-top: var(--qv-sp-2);
    font-size: 13px;
    font-weight: 500;
}

/* Needed explicitly: this rule's `display` beats the UA stylesheet's
   `[hidden] { display: none }`, so the theme-editor link stayed on screen even
   when the status came back enabled. */
.qv-status__link[hidden] { display: none; }

@media (max-width: 700px) {
    .qv-status { flex-direction: column; }
    .qv-status__panel + .qv-status__panel {
        border-left: none;
        border-top: 1px solid var(--qv-border);
    }
}

/* ---------------------------------------------- settings card w/ tabs --- */
/* Tabs and the panel they switch are one card, rather than a floating tab
   strip sitting above a separate box. */

.qv-settings-card {
    background: var(--qv-surface);
    border: 1px solid var(--qv-border);
    border-radius: var(--qv-radius-lg);
    box-shadow: var(--qv-shadow-card);
    overflow: hidden;
}

.qv-settings-card__tabs {
    padding: var(--qv-sp-2) var(--qv-sp-3);
    border-bottom: 1px solid var(--qv-border);
    overflow-x: auto;
}

.qv-settings-card__tabs .nav-tabs {
    border: none;
    display: flex;
    gap: var(--qv-sp-1);
    flex-wrap: nowrap;
    margin: 0;
}

.qv-settings-card__tabs .nav-tabs > li { float: none; margin: 0; }

.qv-settings-card__tabs .nav-tabs > li > a,
.qv-settings-card__tabs .nav-tabs > li.active > a,
.qv-settings-card__tabs .nav-tabs > li.active > a:hover,
.qv-settings-card__tabs .nav-tabs > li.active > a:focus {
    display: flex;
    align-items: center;
    gap: var(--qv-sp-2);
    white-space: nowrap;
    border: none;
    border-radius: var(--qv-radius);
    margin: 0;
    padding: 6px var(--qv-sp-3);
    background: transparent;
    color: var(--qv-text-secondary);
    font-size: 13px;
    font-weight: 500;
    line-height: 20px;
    text-decoration: none;
}

.qv-settings-card__tabs .nav-tabs > li > a:hover {
    background: rgba(0, 0, 0, .05);
    color: var(--qv-text);
}

.qv-settings-card__tabs .nav-tabs > li.active > a,
.qv-settings-card__tabs .nav-tabs > li.active > a:hover,
.qv-settings-card__tabs .nav-tabs > li.active > a:focus {
    background: var(--qv-fill-brand);
    color: #fff;
    font-weight: 600;
}

.qv-settings-card__tabs .icon-wrapper {
    display: inline-block;
    width: 16px;
    text-align: center;
    color: inherit;
}

/* Panes sit flush inside the card now, so they drop their own frame. */
.qv-settings-card .tab-content > .tab-pane {
    background: transparent;
    border: none;
    border-radius: 0;
    box-shadow: none;
    padding: var(--qv-sp-5);
}

/* ------------------------------------------------------ promo banner --- */

.qv-promo-banner {
    display: block;
    margin-top: var(--qv-sp-4);
    border-radius: var(--qv-radius-lg);
    overflow: hidden;
    line-height: 0;
}

.qv-promo-banner img { width: 100%; height: auto; }

/* -------------------------------------------------- recommended apps --- */

.qv-reco { margin-top: var(--qv-sp-4); }

.qv-reco__head {
    display: flex;
    align-items: center;
    margin-bottom: var(--qv-sp-4);
}

.qv-reco__title { font-size: 14px; font-weight: 650; }

.qv-reco__close {
    margin-left: auto;
    border: none;
    background: none;
    font-size: 20px;
    line-height: 1;
    color: var(--qv-text-secondary);
    cursor: pointer;
    padding: 0 var(--qv-sp-1);
}

.qv-reco__close:hover { color: var(--qv-text); }

.qv-reco__grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--qv-sp-4);
}

.qv-reco__item {
    border: 1px solid var(--qv-border);
    border-radius: var(--qv-radius-lg);
    padding: var(--qv-sp-4);
    display: flex;
    flex-direction: column;
}

.qv-reco__itemhead {
    display: flex;
    align-items: center;
    gap: var(--qv-sp-2);
    margin-bottom: var(--qv-sp-3);
}

.qv-reco__logo {
    width: 40px;
    height: 40px;
    flex: 0 0 40px;
    border-radius: 10px;
    object-fit: cover;
    border: 1px solid var(--qv-border);
    background: var(--qv-surface);
}

/* Fallback tile for apps whose icon file is not shipped yet. */
.qv-reco__logo--text {
    border: none;
    color: #fff;
    display: grid;
    place-items: center;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .02em;
}

.qv-reco__name { font-size: 13px; font-weight: 650; margin-bottom: var(--qv-sp-1); }

.qv-reco__blurb {
    color: var(--qv-text-secondary);
    font-size: 13px;
    margin: 0 0 var(--qv-sp-4);
    flex: 1;
}

.qv-reco__item .btn { align-self: flex-start; }

@media (max-width: 900px) { .qv-reco__grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) { .qv-reco__grid { grid-template-columns: 1fr; } }

/* ------------------------------------------------------- get support --- */

.qv-support { margin-top: var(--qv-sp-4); }

.qv-support__title { font-size: 14px; font-weight: 650; }

/* The gap under the title sits on the grid, not on the h3: the base reset
   `body.qv-app h3 { margin: 0 }` is more specific than a single class, so a
   margin-bottom on .qv-support__title is silently dropped. */
.qv-support__grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--qv-sp-4);
    margin-top: var(--qv-sp-5);
}

.qv-support__item {
    display: flex;
    align-items: flex-start;
    gap: var(--qv-sp-3);
    padding: var(--qv-sp-4);
    border: 1px solid var(--qv-border);
    border-radius: var(--qv-radius-lg);
    color: var(--qv-text);
    text-decoration: none !important;
    transition: background .12s ease;
}

.qv-support__item:hover { background: var(--qv-surface-hover); color: var(--qv-text); }

.qv-support__icon {
    flex: 0 0 36px;
    width: 36px;
    height: 36px;
    border-radius: var(--qv-radius);
    background: var(--qv-info-bg);
    color: var(--qv-info-text);
    display: grid;
    place-items: center;
    font-size: 15px;
}

.qv-support__body { flex: 1; min-width: 0; }
.qv-support__name { display: block; font-size: 13px; font-weight: 650; margin-bottom: 2px; }
.qv-support__blurb { display: block; color: var(--qv-text-secondary); font-size: 13px; }

.qv-support__chevron { color: var(--qv-text-secondary); font-size: 18px; line-height: 1; }

@media (max-width: 700px) { .qv-support__grid { grid-template-columns: 1fr; } }

/* ------------------------------------------------------------ pricing --- */
/* Card-led pricing, with the full comparison table folded away behind a
   toggle. Uses the app's own neutral palette rather than a second accent, so
   the page reads as part of the admin instead of a marketing site. */

.qv-pricing { max-width: 1100px; margin: 0 auto; }

/* Back to settings. Left-aligned against the grid rather than centred with
   the intro, so it reads as navigation and not as part of the heading. */
.qv-backlink {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-bottom: var(--qv-sp-4);
    font-size: 13px;
    font-weight: 550;
    color: var(--qv-text-secondary);
    text-decoration: none;
}

.qv-backlink:hover,
.qv-backlink:focus { color: var(--qv-text); text-decoration: none; }

.qv-backlink__arrow { font-size: 15px; line-height: 1; }

.qv-pricing__intro { text-align: center; margin-bottom: var(--qv-sp-8); }
.qv-pricing__title { font-size: 24px; font-weight: 650; margin: 0 0 var(--qv-sp-2); }
.qv-pricing__subtitle { color: var(--qv-text-secondary); font-size: 14px; margin: 0; }

.qv-pricing__grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--qv-sp-5);
    align-items: stretch;
}

/* Equal-height cards: the shorter plans keep the row square rather than
   leaving ragged bottoms next to the long Pro list. */
.qv-plan {
    position: relative;
    display: flex;
    flex-direction: column;
    background: var(--qv-surface);
    border: 1px solid var(--qv-border);
    border-radius: var(--qv-radius-lg);
    box-shadow: var(--qv-shadow-card);
    overflow: hidden;
}

/* The popular card is lifted rather than recoloured: one strong border and a
   deeper shadow reads as "this one" without a second brand colour. */
.qv-plan--popular {
    border-color: var(--qv-fill-brand);
    box-shadow: var(--qv-shadow-pop);
}

.qv-plan--current { border-color: var(--qv-success-text); }

.qv-plan__ribbon {
    background: var(--qv-fill-brand);
    color: #fff;
    text-align: center;
    font-size: 12px;
    font-weight: 650;
    letter-spacing: .02em;
    padding: var(--qv-sp-2);
}

.qv-plan__body { padding: var(--qv-sp-6); flex: 1; display: flex; flex-direction: column; }

.qv-plan__name { font-size: 15px; font-weight: 650; margin-bottom: var(--qv-sp-3); }

.qv-plan__price { display: flex; align-items: baseline; gap: 2px; margin-bottom: var(--qv-sp-3); }
.qv-plan__currency { font-size: 18px; font-weight: 650; align-self: flex-start; margin-top: 6px; }
.qv-plan__amount { font-size: 40px; font-weight: 700; line-height: 1.05; letter-spacing: -.02em; }
.qv-plan__period { font-size: 14px; color: var(--qv-text-secondary); }

.qv-plan__blurb {
    color: var(--qv-text-secondary);
    font-size: 13px;
    margin: 0 0 var(--qv-sp-5);
    min-height: 38px;
}

.qv-plan__cta {
    display: block;
    text-align: center;
    padding: 9px var(--qv-sp-4);
    border: 1px solid var(--qv-fill-brand);
    border-radius: var(--qv-radius);
    font-size: 13px;
    font-weight: 650;
    color: var(--qv-text);
    text-decoration: none !important;
    transition: background .12s ease, color .12s ease;
}

body.qv-app a.qv-plan__cta { color: var(--qv-text); }
body.qv-app a.qv-plan__cta:hover { background: var(--qv-surface-hover); color: var(--qv-text); }

.qv-plan__cta--solid { background: var(--qv-fill-brand); color: #fff !important; }
body.qv-app a.qv-plan__cta--solid:hover { background: var(--qv-fill-brand-hover); color: #fff !important; }

.qv-plan__cta--current {
    border-color: var(--qv-border);
    background: var(--qv-success-bg);
    color: var(--qv-success-text);
    cursor: default;
}

.qv-plan__features { list-style: none; margin: var(--qv-sp-5) 0 0; padding: 0; }

.qv-plan__features li {
    display: flex;
    gap: var(--qv-sp-2);
    font-size: 13px;
    line-height: 20px;
    margin-bottom: var(--qv-sp-3);
}

.qv-plan__tick { color: var(--qv-success-text); font-weight: 700; flex: 0 0 auto; }

@media (max-width: 900px) {
    .qv-pricing__grid { grid-template-columns: 1fr; max-width: 420px; margin: 0 auto; }
    .qv-plan__blurb { min-height: 0; }
}

/* ------------------------------------------------- number range pair ---- */
/* "Number change random from: [ 60 ] To [ 100 ]" on the Pro Promotion tab.

   The two bounds used to be col-sm-4 fields either side of a second col-sm-2
   label. That cannot fit: the rule above widens every col-sm-2 control-label
   to 25%, so the row asked for 116% of it and Bootstrap wrapped the second
   field onto its own line - narrower than the first and no longer reading as
   the other half of a pair.

   Both now share the single col-sm-6 the rest of the tab's fields use, laid
   out here so they stay equal whatever the column is worth. */

body.qv-app .qv-range {
    display: flex;
    align-items: center;
    gap: var(--qv-sp-3);
}

body.qv-app .qv-range .form-control {
    flex: 1 1 0;
    min-width: 0;
}

body.qv-app .qv-range__to {
    flex: 0 0 auto;
    font-size: 13px;
    font-weight: 500;
    color: var(--qv-text-secondary);
}

/* Marks both fields, not just the second: which of the two is wrong depends on
   what the merchant meant, and guessing puts the ring on the wrong one half
   the time. */
body.qv-app .qv-range .form-control.qv-range__field--error {
    border-color: var(--qv-critical-text);
}

body.qv-app .qv-range .form-control.qv-range__field--error:focus {
    border-color: var(--qv-critical-text);
    box-shadow: 0 0 0 1px var(--qv-critical-text);
}

body.qv-app .qv-range__error { color: var(--qv-critical-text); }

/* The message carries .settings-comment for its type scale, and that class sets
   display:inline-block - an author rule, so it beats the user agent's
   [hidden]{display:none} whatever the specificity. Without this the error is
   on screen from the moment the page loads. */
body.qv-app .qv-range__error[hidden] { display: none; }
