/* ============================================================
   Nova Pharma storefront — design tokens from DESIGN.md
   Identity: corporate blues on white, Tajawal, square corners,
   uppercase chrome. Signature: shelf load-in + blue underline.
   ============================================================ */
/* Self-hosted Plus Jakarta Sans.
   The @import this replaces was the worst way to load a webfont: the browser had
   to fetch and parse nova.css before it even discovered the Google CSS, then
   fetch that, then fetch the font — measured as a serial 137ms -> 158ms -> 188ms
   chain across two extra origins. One variable file per subset, same origin,
   covers every weight we use (200-800), and latin-ext only downloads if a glyph
   needs it. */
@font-face {
  font-family: 'Plus Jakarta Sans';
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url('/assets/nova_shop/fonts/plus-jakarta-sans-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Plus Jakarta Sans';
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url('/assets/nova_shop/fonts/plus-jakarta-sans-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --nova-radius: 10px;
  --nova-radius-sm: 8px;
  /* cards: product card, delivery, brand, hero, account, list rows.
     Was hardcoded 14px in 7 places while .nova-account-card used the
     10px default, so two visually identical cards sat side by side on
     the home page with different corners. */
  --nova-radius-lg: 14px;
  --nova-primary: #265faf;
  --nova-secondary: #4893d5;
  --nova-link: #265faf;           /* was #357cbe — 4.40:1, under AA */
  --nova-success: #1ca04c;
  --nova-text: #30303a;
  --nova-heading: #222222;
  /* Muted and link text must clear 4.5:1 on all three grounds we paint them on:
     white, --nova-wash and --nova-ghost. #7a7a85 managed only 3.75 on ghost. */
  --nova-muted: #6b6b78;          /* 5.25 white / 4.93 wash / 4.65 ghost */
  --nova-border: #dddddd;
  --nova-ghost: #eaf2fa;          /* primary-tinted wash */
  --nova-wash: #f5f8fb;           /* page wash */
  /* one step deeper than --nova-ghost: a disc filled with ghost is invisible
     when the surface it sits on is already the wash. */
  --nova-tint: rgba(38, 95, 175, .14);
  --nova-shadow: rgba(38, 95, 175, .12);
  --nova-ease: cubic-bezier(.2, .6, .3, 1);
}

body {
  font-family: 'Plus Jakarta Sans', 'Inter', sans-serif !important;
  color: var(--nova-text);
  background: #fff;
}
/* The bare `body` rule above never won on the shop pages: webshop ships
   `body.product-page { background: var(--body-bg-color) }` (#f8f8f8, higher
   specificity) and /me injects an inline <style> setting #f3f3f3, which comes
   later in the document. Both left the body grey against the "white body"
   design rule, so this needs both the class and !important. */
body,
body.product-page { background: #fff !important; }

/* Stock pins the empty cart to min-height:760px. Against the old grey body that
   read as a deliberate panel; on a white body it is a huge faintly-outlined
   void with one line of text floating in it. The card stays centred, just
   sized to its content. */
.cart-empty.frappe-card { min-height: 340px; }

/* ---- header: white bar, logo, uppercase links, blue signature line ---- */
.navbar {
  background: #fff !important;
  box-shadow: none;
  border-bottom: 3px solid var(--nova-primary);
  padding-top: .65rem;
  padding-bottom: .65rem;
}
.navbar .nav-link,
.navbar .dropdown-toggle {
  color: var(--nova-text) !important;
  font-weight: 600;
  font-size: 14px;
  letter-spacing: .1px;
  padding: .55rem .8rem !important;
  position: relative;
  transition: color .15s var(--nova-ease);
}
.navbar .nav-link::after {
  content: "";
  position: absolute;
  left: .8rem; right: .8rem; bottom: 4px;
  height: 2px;
  background: var(--nova-secondary);
  border-radius: 2px;
  transform: scaleX(0);
  transition: transform .18s var(--nova-ease);
}
.navbar .nav-link:hover { color: var(--nova-primary) !important; }
.navbar .nav-link:hover::after { transform: scaleX(1); }
.navbar .dropdown-menu {
  border: 1px solid #e8ecf1;
  box-shadow: 0 12px 32px var(--nova-shadow);
  padding: 8px;
}
.navbar .dropdown-item { border-radius: var(--nova-radius-sm); padding: 8px 12px; font-weight: 500; }
.navbar .dropdown-item:hover { background: var(--nova-ghost); color: var(--nova-primary); }
/* The logo was rendering 49x22 from a 300x134 source — unreadable. Our height
   rule applied, but stock `.navbar-brand img` caps it with max-height: 22px,
   which clamps whatever height we set, so the cap has to be lifted too. */
.navbar .navbar-brand img {
  height: 40px;
  width: auto;
  max-height: none;
  max-width: 220px;
  object-fit: contain;
}
.navbar .form-control { border-radius: var(--nova-radius-sm); }

/* ---- login: brand panel left, form right ------------------------------------
   The first design, restored. Stock put the form on bare white with no
   container and a stray "Login" title in the far corner.

   NOTE ON FIELD HEIGHT: there is deliberately no height override here. A later
   revision forced 44px, which is what "the fields got taller" referred to; this
   version leaves them at the stock height, which is what was originally seen.
   login.bundle sets it at
   `.for-login .page-card .page-card-body input[type=text]` (0,4,1) — so any
   plain descendant selector loses to it, which is exactly what happened the
   first time round. */
body.nova-login { background: var(--nova-wash) !important; }
.nova-login .nova-login-hidehead { display: none !important; }

.nova-login-shell {
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  gap: 48px;
  align-items: center;
  max-width: 980px;
  margin: 0 auto;
  padding: 56px 20px 72px;
  min-height: calc(100vh - 240px);
}

.nova-login-brand { max-width: 42ch; }
.nova-login-logo { height: 52px; width: auto; margin-bottom: 26px; display: block; }
.nova-login-brand h2 {
  font-size: 30px; line-height: 1.2; font-weight: 800; letter-spacing: -.02em;
  color: var(--nova-heading); margin: 0 0 14px; text-wrap: balance;
}
.nova-login-brand > p {
  color: var(--nova-muted); font-size: 15px; line-height: 1.6; margin: 0 0 22px;
}
.nova-login-points { list-style: none; margin: 0 0 26px; padding: 0; }
.nova-login-points li {
  position: relative; padding-left: 34px; margin-bottom: 12px;
  color: var(--nova-text); font-size: 14px; line-height: 22px; min-height: 22px;
}
/* Tick drawn in CSS: no sprite dependency, and it inherits the brand colour.
   The disc pops in, then the tick strokes itself. clip-path is resolved in the
   pseudo-element's OWN box, before the rotation is applied, so the reveal runs
   along the tick's arms — short arm first, then the long one — and reads as a
   pen stroke rather than a left-to-right wipe. */
.nova-login-points li::before,
.nova-login-points li::after { content: ""; position: absolute; }
.nova-login-points li::before {
  left: 0; top: 0; width: 22px; height: 22px; border-radius: 50%;
  background: var(--nova-tint);
  opacity: 0; transform: scale(.4);
  animation: nova-tick-pop .34s var(--nova-ease) var(--nova-tick-d, 0s) forwards;
}
.nova-login-points li::after {
  left: 6px; top: 8px; width: 10px; height: 5px;
  border-left: 2px solid var(--nova-primary);
  border-bottom: 2px solid var(--nova-primary);
  transform: rotate(-45deg);
  clip-path: inset(0 100% 0 0);
  animation: nova-tick-draw .3s var(--nova-ease)
             calc(var(--nova-tick-d, 0s) + .14s) forwards;
}
.nova-login-points li:nth-child(1) { --nova-tick-d: .06s; }
.nova-login-points li:nth-child(2) { --nova-tick-d: .17s; }
.nova-login-points li:nth-child(3) { --nova-tick-d: .28s; }
@keyframes nova-tick-pop  { to { opacity: 1; transform: scale(1); } }
@keyframes nova-tick-draw { to { clip-path: inset(0 0 0 0); } }
.nova-login-help { color: var(--nova-muted); font-size: 13px; line-height: 1.6; margin: 0; }
.nova-login-help a { font-weight: 600; }

body.nova-login .page-card.nova-login-card {
  background: #fff;
  border: 1px solid #e6ebf1;
  border-radius: var(--nova-radius-lg) !important;
  box-shadow: 0 12px 40px rgba(16, 24, 40, .08);
  padding: 34px 32px;
  margin: 0;
  width: 100%;
  max-width: none;
}
body.nova-login .nova-login-card img { display: none; }   /* logo lives on the brand side */
body.nova-login .nova-login-card h4 {
  font-size: 21px; font-weight: 800; color: var(--nova-heading); margin-bottom: 4px;
}
body.nova-login .nova-login-card label {
  font-size: 13px; font-weight: 600; color: var(--nova-text); margin-bottom: 6px;
}
body.nova-login .nova-login-card .form-control:focus {
  border-color: var(--nova-secondary);
  box-shadow: 0 0 0 3px rgba(72, 147, 213, .18);
}
body.nova-login .nova-login-card .btn-login,
body.nova-login .nova-login-card .btn-primary {
  width: 100%; font-size: 15px; font-weight: 700;
  text-transform: none;                    /* stock shouts "CONTINUE" */
}

/* The stock submit is `btn btn-sm btn-default btn-block btn-login` — no
   `btn-primary`, so the global `.btn-primary:hover` never matched it and the
   CTA had no hover state whatsoever. Its fill is set !important at the
   "login page branding" rule below, so the hover fill must be too.
   #1f4f92 is the same 8.10:1 hover fill the rest of the shop uses. */
body.nova-login .nova-login-card .btn-login:hover,
body.nova-login .nova-login-card .btn-login:focus-visible {
  background: #1f4f92 !important;
  border-color: #1f4f92 !important;
  /* login.bundle's `.for-login .page-card .page-card-actions .btn-login:hover`
     is (0,5,0) and sets box-shadow:none — it outranks this rule's (0,4,1), so
     the glow needs !important the same way the fill above does. */
  box-shadow: 0 6px 18px rgba(38, 95, 175, .28) !important;
  transform: translateY(-1px);
}
body.nova-login .nova-login-card .btn-login:active { transform: translateY(0) scale(.985); }

/* stock ships "Forgot password?" as display:none, leaving someone who has lost
   their password no visible route. The outcome is kept honest server-side while
   this site has no Email Account — nova_shop.visibility.reset_password. */
body.nova-login .forgot-password-message { display: block !important; margin-top: 12px; }
body.nova-login .forgot-password-message a {
  display: inline-block !important; font-size: 13px; font-weight: 600; color: var(--nova-link);
}
body.nova-login .forgot-password-message a:hover { text-decoration: underline; }

@media (max-width: 900px) {
  .nova-login-shell {
    grid-template-columns: 1fr; gap: 30px; padding: 32px 16px 48px;
    min-height: 0; max-width: 460px;
  }
  .nova-login-brand { max-width: none; }
  .nova-login-brand h2 { font-size: 24px; }
  .nova-login-points, .nova-login-help { display: none; }   /* keep the form above the fold */
  .nova-login-logo { height: 44px; margin-bottom: 18px; }
  body.nova-login .page-card.nova-login-card { padding: 26px 20px; }
}

/* ---- global shape + focus ---- */
.btn, .form-control, .card, .card-img-container, .card-img,
.website-image, .modal-content, .dropdown-menu { border-radius: var(--nova-radius) !important; }
a { color: var(--nova-link); transition: color .15s var(--nova-ease); }
a:hover { color: var(--nova-primary); text-decoration: none; }
:focus-visible { outline: 2px solid var(--nova-secondary); outline-offset: 2px; }

/* ---- buttons ---- */
/* Filled buttons carry white text, so their background sets the contrast.
   --nova-secondary gave 3.28:1 and the old #3d86c9 hover 3.85:1 — both under
   AA, on Add to Cart and Place Order. Desktop hid this because the listing
   button is hover-revealed; at 390px it is on screen the whole time. Fills use
   the primary (6.29:1); the secondary stays for accents that carry no text. */
.btn { transition: background .15s var(--nova-ease), transform .12s var(--nova-ease), box-shadow .15s var(--nova-ease); }
.btn-primary,
.btn-add-to-cart,
.btn-add-to-cart-list,
.btn-place-order,
.btn-get-quote {
  background-color: var(--nova-primary) !important;
  border-color: var(--nova-primary) !important;
  color: #fff !important;
  font-weight: 700;
  font-size: 14px;
  letter-spacing: .1px;
}
.btn-primary:hover, .btn-add-to-cart-list:hover, .btn-add-to-cart:hover {
  background-color: #1f4f92 !important;   /* 8.10:1 — hover must clear AA too */
  border-color: #1f4f92 !important;
  box-shadow: 0 4px 14px var(--nova-shadow);
}
.btn:active { transform: scale(.975); }

/* Frappe's icon sprites carry fill:#000, so the cart glyph rendered near-black
   on the blue button while the label beside it was white. These are FILLED
   symbols (computed stroke is none), so fill is the property that matters —
   currentColor makes the icon follow whatever colour the button sets. */
.btn-primary .icon,
.btn-add-to-cart .icon,
.btn-add-to-cart-list .icon,
.btn-place-order .icon,
.btn-get-quote .icon,
.nova-mc-order .icon,
.btn-primary .icon use,
.btn-add-to-cart-list .icon use { fill: currentColor; }

/* ---- layout: wider stage, sticky filters ---- */
@media (min-width: 1200px) {
  /* `main .container` (descendant) matched nothing — the element IS
     <main class="container">, so this rule never applied and the stage sat at
     Bootstrap's 1290px. main.container is (0,1,1) against Bootstrap's (0,1,0),
     so it wins on specificity without !important. */
  .page_content > .container,
  main.container { max-width: 1320px; }
}
.item-card-group-section { position: relative; }
@media (min-width: 992px) {
  .filters-section, .col-md-3.order-1 { position: sticky; top: 16px; align-self: flex-start; }
}

/* ---- toolbar ---- */
.toolbar { align-items: center; margin-bottom: 1.25rem; }
.toolbar .input-group, .toolbar input[type="search"], #search-box {
  border-radius: var(--nova-radius-sm);
  border-color: var(--nova-border);
  font-size: 14.5px;
}
#search-box:focus { border-color: var(--nova-secondary); box-shadow: 0 0 0 3px rgba(72, 147, 213, .18); }
.btn-list-view, .btn-grid-view, #image-view, #list {
  background: #fff !important;
  border: 1px solid var(--nova-border) !important;
  color: var(--nova-muted) !important;
}
.btn-list-view.btn-primary, .btn-grid-view.btn-primary,
#image-view.btn-primary, #list.btn-primary {
  border-color: var(--nova-primary) !important;
  color: var(--nova-primary) !important;     /* was secondary: 2.90 on ghost, under 3.0 */
  background: var(--nova-ghost) !important;
  box-shadow: none;
}

/* ---- product grid: 4-across shelf ---- */
@media (min-width: 1200px) {
  .item-card { flex: 0 0 25% !important; max-width: 25% !important; }
}
@media (min-width: 768px) and (max-width: 1199px) {
  .item-card { flex: 0 0 33.333% !important; max-width: 33.333% !important; }
}
.item-card { min-width: 0 !important; }  /* flexbox: let cells shrink below image width */
.products-list { row-gap: 8px; }

/* uniform shelf: fixed image stage, contained artwork */
.card-img-container {
  height: 200px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.card-img-container a { display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; }
.card-img { max-width: 100%; max-height: 190px; width: auto; height: auto; object-fit: contain; }

/* entrance: shelf fill (JS sets --i per card) */
.item-card {
  animation: novaShelf .38s var(--nova-ease) both;
  animation-delay: calc(var(--i, 0) * 42ms);
}
@keyframes novaShelf {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

/* cards */
.item-card .card, .card.text-left {
  border: 1px solid #eef1f4;
  border-radius: var(--nova-radius-lg) !important;
  overflow: hidden;
  box-shadow: none;
  position: relative;
  transition: box-shadow .2s var(--nova-ease), transform .2s var(--nova-ease);
  padding-bottom: 4px;
}
.item-card .card::after {           /* the blue underline, brand motif */
  border-radius: 0 0 var(--nova-radius-lg) var(--nova-radius-lg);
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  height: 2px; width: 100%;
  background: var(--nova-secondary);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .22s var(--nova-ease);
}
.item-card .card:hover {
  transform: translateY(-4px);
  box-shadow: 0 10px 28px var(--nova-shadow);
}
.item-card .card:hover::after { transform: scaleX(1); }
.card-img-container { background: #fff; overflow: hidden; border-radius: var(--nova-radius-lg) var(--nova-radius-lg) 0 0 !important; }
.card-img { transition: transform .3s var(--nova-ease); }
.item-card .card:hover .card-img { transform: scale(1.045); }

.product-category {
  text-transform: uppercase;
  font-size: 11px;
  color: var(--nova-muted);
  letter-spacing: .55px;
  font-weight: 700;
}
.product-title, a .product-title, .product-title a {
  color: var(--nova-link) !important;
  font-size: 15px;
  font-weight: 500;
  line-height: 1.4;
}
.product-price {
  color: var(--nova-heading);
  font-weight: 800;
  font-size: 17px;
  font-variant-numeric: tabular-nums;
}
.product-price .startingFrom { color: var(--nova-muted); font-weight: 400; }

/* ---- skeleton shelf ---- */
.nova-skel-grid { display: flex; flex-wrap: wrap; width: 100%; }
.nova-skel { flex: 0 0 25%; max-width: 25%; padding: 0 12px 28px; }
@media (max-width: 1199px) { .nova-skel { flex: 0 0 33.333%; max-width: 33.333%; } }
@media (max-width: 767px)  { .nova-skel { flex: 0 0 50%; max-width: 50%; } }
.nova-skel .ph {
  background: linear-gradient(90deg, var(--nova-ghost) 25%, var(--nova-wash) 50%, var(--nova-ghost) 75%);
  background-size: 400% 100%;
  animation: novaShimmer 1.15s linear infinite;
}
@keyframes novaShimmer {
  0%   { background-position: 100% 0; }
  100% { background-position: 0 0; }
}

/* refetch dim while filtering */
.nova-loading .products-list { opacity: .45; transition: opacity .18s var(--nova-ease); }

/* ---- filters sidebar ---- */
.filter-label {
  text-transform: uppercase;
  font-weight: 700;
  font-size: 13px;
  color: var(--nova-heading);
  letter-spacing: .5px;
  position: relative;
  padding-bottom: 8px;
  margin-bottom: 12px !important;
}
.filter-label::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 30px; height: 2px;
  background: var(--nova-secondary);
}
.filter-block { border-bottom: 1px solid #eef1f4; padding-bottom: 1.4rem !important; margin-bottom: 1.4rem !important; }
.filter-block .checkbox { padding: 3px 6px; margin-left: -6px; transition: background .12s var(--nova-ease); }
.filter-block .checkbox:hover { background: var(--nova-ghost); }
.filter-block .checkbox .label-area { font-size: 14px; color: var(--nova-text); cursor: pointer; }
input[type="checkbox"] { accent-color: var(--nova-secondary); width: 15px; height: 15px; }
.clear-filters, a.clear-filters { font-size: 12.5px; text-transform: uppercase; font-weight: 700; letter-spacing: .3px; }

/* ---- breadcrumbs & page headings ---- */
.page-breadcrumbs, .breadcrumb { font-weight: 600; font-size: 13.5px; }
.page-breadcrumbs a, .breadcrumb a { color: var(--nova-muted); }
.page-breadcrumbs a:hover, .breadcrumb a:hover { color: var(--nova-primary); }
h1, h2, h3, .item-group-header { color: var(--nova-heading); font-family: 'Plus Jakarta Sans', sans-serif; font-weight: 800; }

/* ---- product page ---- */
.product-container { border: none; }
.product-container .product-title, .item-details h1 { color: var(--nova-heading) !important; font-size: 26px; font-weight: 800; }
.product-price-section .product-price, .item-details .product-price { font-size: 26px; }
.product-image, .website-image-lazy { background: #fff; }
.item-details .btn-add-to-cart { min-width: 220px; padding: .7rem 1.4rem; }
.product-description { color: var(--nova-text); line-height: 1.7; }

/* ---- inputs ---- */
.form-control { border-color: var(--nova-border); transition: border-color .15s var(--nova-ease), box-shadow .15s var(--nova-ease); }
.form-control:focus { border-color: var(--nova-secondary); box-shadow: 0 0 0 3px rgba(72, 147, 213, .18); }

/* ---- toasts / alerts ---- */
.alert, .frappe-toast, .desk-alert {
  border-radius: var(--nova-radius) !important;
  border-left: 3px solid var(--nova-secondary);
  font-family: 'Plus Jakarta Sans', sans-serif;
  box-shadow: 0 8px 24px var(--nova-shadow);
}

/* ---- footer ---- */
.web-footer { background: var(--nova-wash); border-top: 1px solid #eceef1; }

/* The band did not contain its own content: body is a flex column and the
   footer is a flex item, so on any page shorter than the viewport it was
   shrunk to its stock min-height (140px) while the content ran 261px — the
   company block spilled out onto the white page background below the band.
   Taller pages like /contact hid the bug, which is why it survived this long. */
.web-footer { flex-shrink: 0; }

/* Stock lays the footer out as two rows that each fill only one half: the links
   sit in a right column beside an empty left one, and the company block sits in
   a left column whose right half is the ERPNext credit we hide. That reads as
   two disconnected strips. Put them on one line — company left, links right. */
.web-footer .container {
  /* match .nova-home (1320px wide, 20px padding) so the footer sits on the same
     grid as the page — stock's 1290px + 80px padding indented it 75px further */
  max-width: 1320px;
  padding-left: 20px;
  padding-right: 20px;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 12px 48px;
}
/* Both are stock features this storefront does not use (a footer logo and
   grouped link columns). They render empty but still count as flex items, and
   each one then adds a 48px gap that pushed the company block 126px past the
   page grid. `:empty` does not match them — they contain an empty .row. */
.web-footer .footer-logo-extension,
.web-footer .footer-grouped-links { display: none; }
.web-footer .footer-info { flex: 1 1 320px; order: 1; }
.web-footer .footer-links { flex: 0 0 auto; order: 2; margin-left: auto; }
.web-footer .footer-links .row,
.web-footer .footer-info .row { display: block; margin: 0; }
.web-footer .footer-links .footer-col-left { display: none; }  /* empty spacer half */
.web-footer .footer-links .footer-col-right,
.web-footer .footer-info .footer-col-left {
  flex: none;
  width: auto;
  max-width: none;
  padding: 0;
}
.web-footer .footer-links .footer-col-right {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 20px;
  justify-content: flex-end;
}
.web-footer .footer-info p { margin-bottom: 0; }
@media (max-width: 767px) {
  /* stacked: links read better under the company block than floated right */
  .web-footer .footer-links { margin-left: 0; }
  .web-footer .footer-links .footer-col-right { justify-content: flex-start; }
}



/* ---- journey polish v16 ---- */
/* footer: quiet, no engine credits */
.footer-powered { display: none !important; }

/* product page: buy box */
.item-details .product-title, .product-page h1 { letter-spacing: -.2px; }
.product-image { border: 1px solid #eef1f4; }
.item-details .btn-add-to-cart, .item-details .btn-primary { padding: .72rem 1.6rem; }
.product-description { max-width: 68ch; }
.number-spinner, .cart-qty { border-radius: var(--nova-radius) !important; }
.number-spinner .btn { border: 1px solid var(--nova-border); background: #fff !important; color: var(--nova-heading) !important; }
.number-spinner input { border-color: var(--nova-border); text-align: center; font-weight: 700; }

/* cart page */
.cart-items-header, .cart-table th {
  text-transform: uppercase;
  font-size: 12px;
  letter-spacing: .5px;
  color: var(--nova-muted);
  font-weight: 700;
}
.cart-item-title, .cart-items .item-title { color: var(--nova-link); font-weight: 500; }
.cart-tax-items .item-grand-total, .grand-total { font-weight: 800; font-size: 18px; }
.cart-container .btn-place-order, .cart-container .btn-primary { width: 100%; padding: .8rem; font-size: 14.5px; }
.cart-empty.frappe-card, .cart-empty { box-shadow: none; }
.cart-empty-state img { opacity: .8; }

/* navbar cart link */
.nova-cart-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: 6px;
}
.nova-cart-link svg { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width: 2; }


/* ---- login page branding ---- */
.page-card .btn-primary, .btn-login, .page-card .btn-dark, button.btn-login {
  background: var(--nova-primary) !important;   /* white on secondary was 3.28:1 */
  border-color: var(--nova-primary) !important;
  color: #fff !important;
  text-transform: uppercase;
  font-weight: 700;
  letter-spacing: .35px;
  border-radius: var(--nova-radius) !important;
}
.btn-login-with-email-link, .login-with-email-link,
a[href*="login-with-email-link"], button[data-submit="login-with-email-link"] {
  display: none !important;
}
.page-card .page-card-head img, img.app-logo {
  /* was hotlinked from uae.novapharma.com — the login page would have lost its
     logo the day WordPress is switched off. The navbar already uses this local
     copy; point the login page at the same file. */
  content: url("/files/site/novapharma-logo.png");
  height: 52px !important;
  width: auto !important;
}
.for-login .page-card, .page-card { border-radius: var(--nova-radius-sm); }
.nova-place-order { width: 100%; padding: .8rem; font-size: 14.5px; }

/* ================= mini cart drawer ================= */
.nova-overlay {
  position: fixed; inset: 0;
  background: rgba(34, 34, 34, .38);
  opacity: 0; pointer-events: none;
  transition: opacity .22s var(--nova-ease);
  z-index: 1049;
}
.nova-overlay.show { opacity: 1; pointer-events: auto; }

.nova-drawer {
  position: fixed; top: 0; right: 0; bottom: 0;
  width: 400px; max-width: 96vw;
  background: #fff;
  z-index: 1050;
  transform: translateX(102%);
  transition: transform .28s var(--nova-ease);
  display: flex; flex-direction: column;
  box-shadow: -12px 0 40px rgba(38, 95, 175, .18);
  border-left: 3px solid var(--nova-primary);
}
.nova-drawer.show { transform: translateX(0); }

.nova-drawer-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid #eef1f4;
}
.nova-drawer-head h5 {
  margin: 0; font-weight: 800; text-transform: uppercase;
  font-size: 14px; letter-spacing: .5px; color: var(--nova-heading);
}
.nova-drawer-close {
  border: none; background: none; font-size: 22px; line-height: 1;
  color: var(--nova-muted); cursor: pointer; padding: 4px 8px;
}
.nova-drawer-close:hover { color: var(--nova-heading); }

.nova-drawer-body { flex: 1; overflow-y: auto; padding: 8px 20px; }
.nova-mc-item {
  display: flex; gap: 12px; align-items: center;
  padding: 12px 0; border-bottom: 1px solid #f2f4f7;
}
.nova-mc-item img {
  width: 54px; height: 54px; object-fit: contain; flex: 0 0 54px;
  border: 1px solid #eef1f4; background: #fff;
}
.nova-mc-info { flex: 1; min-width: 0; }
.nova-mc-name {
  font-size: 13.5px; color: var(--nova-link); line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  margin-bottom: 6px;
}
.nova-mc-line { display: flex; align-items: center; justify-content: space-between; }
.nova-mc-qty { display: inline-flex; align-items: center; border: 1px solid var(--nova-border); }
.nova-mc-qty button {
  border: none; background: #fff; width: 26px; height: 26px;
  font-weight: 700; color: var(--nova-heading); cursor: pointer;
}
.nova-mc-qty button:hover { background: var(--nova-ghost); }
.nova-mc-qty span { min-width: 30px; text-align: center; font-weight: 700; font-size: 13px; }
.nova-mc-amount { font-weight: 800; font-size: 14px; font-variant-numeric: tabular-nums; }

.nova-drawer-foot { border-top: 1px solid #eef1f4; padding: 16px 20px; }
.nova-mc-subtotal {
  display: flex; justify-content: space-between;
  font-weight: 800; font-size: 16px; margin-bottom: 6px;
}
.nova-mc-hint { font-size: 12.5px; color: var(--nova-muted); margin-bottom: 10px; }
.nova-mc-hint.short { color: #b25e09; }
.nova-drawer-foot .btn { width: 100%; margin-bottom: 8px; padding: .7rem; }
.nova-mc-view {
  /* outline button: the SECONDARY blue as text on white is 3.28:1, under AA.
     Same root cause as the filled buttons, inverted — and only rendered when
     the drawer has items, so an empty-cart audit never saw it. */
  background: #fff !important; color: var(--nova-primary) !important;
  border: 1px solid var(--nova-primary) !important;
  text-transform: uppercase; font-weight: 700; font-size: 13px;
}
.nova-mc-empty { text-align: center; color: var(--nova-muted); padding: 48px 12px; }
@media (max-width: 576px) { .nova-drawer { width: 100vw; } }

/* ================= active filter chips ================= */
.nova-chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 14px; }
.nova-chip {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--nova-ghost); color: var(--nova-primary);
  font-size: 12.5px; font-weight: 700; padding: 4px 10px;
  cursor: pointer; border: 1px solid #d5e5f5;
}
.nova-chip:hover { background: #ddebf9; }
.nova-chip .x { font-weight: 800; }

/* ================= mobile grid ================= */
@media (max-width: 767px) {
  .item-card { flex: 0 0 50% !important; max-width: 50% !important; }
  .card-img-container { height: 150px; }
  .card-img { max-height: 140px; }
}

/* ================= portal / order pages ================= */
.web-list-header, .portal-page h1 { font-weight: 800; }
.transaction-list-item, .web-list-item {
  border: 1px solid #eef1f4 !important; border-radius: var(--nova-radius) !important;
  transition: box-shadow .15s var(--nova-ease);
}
.transaction-list-item:hover, .web-list-item:hover { box-shadow: 0 6px 18px var(--nova-shadow); }
.indicator-pill { border-radius: var(--nova-radius) !important; text-transform: uppercase; font-size: 11px; letter-spacing: .4px; }


/* unpriced = not orderable for this account */
.nova-unavailable .btn-add-to-cart-list, .nova-unavailable .go-to-cart-grid,
.nova-unavailable .cart-indicator { display: none !important; }
.nova-na-note {
  border-left: 3px solid var(--nova-border);
  background: var(--nova-wash);
  padding: 12px 16px;
  color: var(--nova-muted);
  font-size: 14px;
  margin: 12px 0;
  max-width: 420px;
}


/* product page qty stepper */
.nova-pdp-qty { margin-right: 12px; height: 44px; }
.nova-pdp-qty button { width: 38px; height: 42px; font-size: 16px; }
.nova-pdp-qty span { min-width: 40px; font-size: 15px; }


/* RRP: reference retail price, plain text (no strike-through).
   Must sit on its own line: inline, the line wrapped between the "RRP" label
   and its figure, so a card read "AED 217.00 RRP" over "AED 342.00" — the
   label attached to the trade price, which is exactly the wrong number. */
.striked-price, .striked-item-price {
  color: var(--nova-muted);
  font-weight: 400;
  font-size: 12.5px;
  display: block;
  margin-top: 3px;
}
.striked-price s, .striked-item-price s { text-decoration: none; color: inherit; }
/* On the listing the block above never applied at all: stock webshop ships
   `.item-card-group-section .product-price .striked-price` (0,3,0) which beats
   our `.striked-price` (0,1,0) and paints the RRP var(--gray-500) = #999, i.e.
   2.85:1. Match that specificity for the colour only — this is a contrast fix,
   so leave stock's sizing where it is. */
.item-card-group-section .product-price .striked-price,
.item-card-group-section .product-price .striked-price s {
  color: var(--nova-muted);
}
.striked-price::before, .striked-item-price::before { content: "RRP "; }
.product-info-green, .discount { color: var(--nova-success); font-weight: 700; font-size: 12.5px; }
.cart-icon { display: flex !important; align-items: center; cursor: pointer; }
.nova-rrp { color: var(--nova-muted); font-size: 13.5px; margin-top: 2px; }

/* ================= product page layout polish ================= */
.product-container { padding: 8px 4px 24px; }
.item-details .product-title, .product-page h1 { font-size: 24px; line-height: 1.3; }
.item-details .product-price { margin: 10px 0 4px; }
.product-code, .item-details .product-code { color: var(--nova-muted); font-size: 13px; }
/* specifications table */
.product-specifications, .item-website-specification { margin-top: 8px; }
.product-specifications .table, table.product-specs {
  border: 1px solid #eef1f4; border-radius: var(--nova-radius); overflow: hidden;
}
.product-specifications td, .product-specifications th { border-color: #f0f2f5 !important; padding: 10px 14px; font-size: 14px; }
.product-specifications tr td:first-child { color: var(--nova-muted); width: 220px; font-weight: 600; }
/* description prose */
.product-description, .web-long-description {
  line-height: 1.75; color: var(--nova-text); font-size: 15px;
}
.product-description b, .product-description strong { color: var(--nova-heading); }
/* tabs (webshop uses bootstrap tabs for description/specs/reviews) */
.nav-tabs { border-bottom: 1px solid #e8ecf1; }
.nav-tabs .nav-link { border: none; font-weight: 600; color: var(--nova-muted); padding: 10px 16px; }
.nav-tabs .nav-link.active { color: var(--nova-primary); border-bottom: 2px solid var(--nova-secondary); background: none; }


/* B2B: no retail-style discount badges */
.product-info-green { display: none !important; }

/* account hub */
.nova-account-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 14px; margin: 18px 0 26px;
}
/* Card tier, so it matches .nova-delivery-card and .nova-brand-tile — the same
   bordered content card, which sat at 10px next to their 14px. Used by the home
   quick actions, /account and /contact. */
.nova-account-card {
  border: 1px solid #e8ecf1; border-radius: var(--nova-radius-lg);
  padding: 18px; display: flex; flex-direction: column; gap: 4px;
  color: var(--nova-text); transition: box-shadow .15s var(--nova-ease), transform .15s var(--nova-ease);
}
.nova-account-card:hover { box-shadow: 0 8px 24px var(--nova-shadow); transform: translateY(-2px); color: var(--nova-text); }
.nova-account-card strong { color: var(--nova-primary); font-size: 15.5px; }
.nova-account-card span { color: var(--nova-muted); font-size: 13px; }
.nova-reorder { margin-left: 8px; }

/* footer */
.web-footer { padding: 28px 0; }
.web-footer .footer-col-left, .web-footer a { color: var(--nova-muted); }
.web-footer a:hover { color: var(--nova-primary); }


/* ================= clean menu v29 ================= */
.navbar .nav-link { white-space: nowrap; }
.navbar .navbar-nav { align-items: center; }
.nova-search { display: flex; align-items: center; }
.nova-search-btn {
  border: none; background: none; padding: 8px; cursor: pointer;
  color: var(--nova-text); display: flex; align-items: center;
}
.nova-search-btn svg { width: 18px; height: 18px; }
.nova-search-btn:hover { color: var(--nova-primary); }
.nova-search-form { width: 0; overflow: hidden; transition: width .22s var(--nova-ease); }
.nova-search.open .nova-search-form { width: 220px; }
.nova-search-form input {
  width: 100%; border: 1px solid var(--nova-border); border-radius: var(--nova-radius-sm);
  padding: 7px 12px; font-size: 13.5px;
}

/* ================= clean filters ================= */
.nova-filter-scroll { max-height: 300px; overflow-y: auto; padding-right: 6px; }
.nova-filter-scroll::-webkit-scrollbar { width: 6px; }
.nova-filter-scroll::-webkit-scrollbar-thumb { background: #dfe5ec; border-radius: 3px; }
.nova-collapsible { cursor: pointer; user-select: none; }
.nova-collapsible::before {
  content: ""; float: right; width: 8px; height: 8px; margin-top: 4px;
  border-right: 2px solid var(--nova-muted); border-bottom: 2px solid var(--nova-muted);
  transform: rotate(45deg); transition: transform .18s var(--nova-ease);
}
.nova-collapsed .nova-collapsible::before { transform: rotate(-45deg); }
.nova-collapsed .nova-filter-scroll, .nova-collapsed .checkbox,
.nova-collapsed input[type="text"] { display: none !important; }


/* ================= B2B home ================= */
.nova-home { max-width: 1320px; margin: 0 auto; padding: 28px 20px 40px; }
.nova-home-greet { font-size: 26px; font-weight: 800; color: var(--nova-heading); margin-bottom: 4px; }
.nova-home-sub { color: var(--nova-muted); margin-bottom: 26px; }
.nova-home-actions { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 14px; margin-bottom: 34px; }
.nova-home-cats { margin: 8px 0 34px; }
.nova-home-cats h2, .nova-home-recent h2 { font-size: 18px; font-weight: 800; margin-bottom: 14px; }
/* 240px fitted 5 columns on a wide screen, so the 8 categories wrapped 5+3 and
   left a ragged half-row. 280px settles on 4 (an even 4+4) above ~1300px and
   changes nothing at 1200 and below — still auto-fit, so it adapts if the
   category list grows. */
.nova-cat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 12px; }
.nova-cat-tile {
  display: flex; justify-content: space-between; align-items: center;
  border: 1px solid #e8ecf1; border-radius: var(--nova-radius);
  padding: 16px 18px; color: var(--nova-heading); font-weight: 600; font-size: 14.5px;
  transition: box-shadow .15s var(--nova-ease), transform .15s var(--nova-ease), border-color .15s var(--nova-ease);
}
.nova-cat-tile::after { content: "\2192"; color: var(--nova-primary); font-weight: 700; }
.nova-cat-tile:hover { box-shadow: 0 8px 24px var(--nova-shadow); transform: translateY(-2px); border-color: #d5e5f5; color: var(--nova-primary); }
.nova-home-order {
  display: flex; justify-content: space-between; align-items: center;
  border: 1px solid #e8ecf1; border-radius: var(--nova-radius);
  padding: 14px 18px; margin-bottom: 10px; color: var(--nova-text);
}
.nova-home-order:hover { box-shadow: 0 6px 18px var(--nova-shadow); color: var(--nova-text); }
.nova-home-order strong { color: var(--nova-link); }
.nova-home-order .amt { text-align: right; font-weight: 700; }
.nova-home-empty { color: var(--nova-muted); }

/* ---- home: hero banner slider (cloned from previous weborder) ---- */
.nova-hero { position: relative; border-radius: var(--nova-radius-lg); overflow: hidden; margin-bottom: 30px; background: #eef3f9; box-shadow: 0 6px 18px var(--nova-shadow); }
.nova-hero-track { display: flex; transition: transform .6s ease; }
.nova-hero-track img { flex: 0 0 100%; width: 100%; display: block; object-fit: cover; aspect-ratio: 1140 / 420; }
.nova-hero-dots { position: absolute; left: 0; right: 0; bottom: 12px; display: flex; justify-content: center; gap: 8px; }
.nova-hero-dots button { width: 10px; height: 10px; border-radius: 50%; border: 0; padding: 0; background: rgba(255,255,255,.55); cursor: pointer; transition: background .2s, transform .2s; }
.nova-hero-dots button.on { background: #fff; transform: scale(1.2); }
/* Pause/play for the auto-rotating banner — WCAG 2.2.2 (Level A). Sits bottom
   right, clear of the centred dots. Solid white ground so it stays legible over
   whatever artwork the slide happens to carry. */
.nova-hero-pause {
	position: absolute; right: 12px; bottom: 10px;
	width: 34px; height: 34px; border-radius: 50%;
	border: 1px solid rgba(38, 95, 175, .25);
	background: #fff; color: var(--nova-primary);
	font-size: 13px; line-height: 1; font-weight: 700;
	display: flex; align-items: center; justify-content: center;
	cursor: pointer; padding: 0;
	box-shadow: 0 2px 8px rgba(0, 0, 0, .18);
	transition: background .15s var(--nova-ease), transform .12s var(--nova-ease);
}
.nova-hero-pause:hover { background: var(--nova-ghost); }
.nova-hero-pause:active { transform: scale(.94); }
/* The banners are 1600x591 (2.71:1) and the desktop box matches them exactly,
   so nothing crops there. Forcing 4/3 on a phone made object-fit:cover discard
   51% of the image WIDTH — enough to cut the slide's own headline mid-word
   ("The Adva... Choice fo... Routine Ear Hygie..."). 16/9 keeps the hero tall
   enough to read while dropping the crop to ~34%.
   The real fix is phone-proportioned banner artwork; this only limits the damage. */
@media (max-width: 640px) { .nova-hero-track img { aspect-ratio: 16 / 9; } }

/* ---- home: brand logo grid ---- */
.nova-home-brands { margin: 8px 0 34px; }
.nova-home-brands h2 { font-size: 18px; font-weight: 800; margin-bottom: 14px; }
.nova-brand-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 14px; }
.nova-brand-tile { display: flex; align-items: center; justify-content: center; background: #fff; border: 1px solid var(--nova-border); border-radius: var(--nova-radius-lg); padding: 18px 14px; min-height: 104px; transition: box-shadow .2s, transform .2s, border-color .2s; }
.nova-brand-tile:hover { box-shadow: 0 8px 20px var(--nova-shadow); transform: translateY(-2px); border-color: var(--nova-secondary); }
.nova-brand-tile img { max-width: 100%; max-height: 72px; object-fit: contain; }

/* ---- home: delivery options strip ---- */
.nova-home-delivery { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 14px; margin: 8px 0 34px; }
.nova-delivery-card { display: flex; gap: 12px; align-items: flex-start; background: #f6f9fd; border: 1px solid var(--nova-border); border-radius: var(--nova-radius-lg); padding: 18px; }
.nova-delivery-card svg { flex: 0 0 auto; margin-top: 2px; }
.nova-delivery-card strong { display: block; color: var(--nova-heading); margin-bottom: 4px; font-size: 14px; }
.nova-delivery-card span { color: var(--nova-muted); font-size: 13px; line-height: 1.5; }

/* unpriced items: explain the blank instead of leaving a dead card */
.nova-card-note {
	display: inline-block; margin-top: 6px; padding: 3px 10px;
	font-size: 12px; font-weight: 600; color: var(--nova-primary);
	background: #eef3fa; border-radius: 999px;
}

/* order blocked until the customer has a delivery address */
.nova-need-address {
	margin-top: 10px; padding: 10px 12px; border-radius: var(--nova-radius);
	background: #fff6e8; border: 1px solid #f0d6a8;
	font-size: 13px; color: #7a5510; line-height: 1.5;
}
.nova-need-address a { color: var(--nova-primary); font-weight: 700; text-decoration: underline; }
.nova-disabled { opacity: .55; cursor: not-allowed; pointer-events: none; }

/* ---- mobile: keep wide content inside its own box, size taps for fingers ---- */
.nova-tablewrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

@media (max-width: 767px) {
	/* p-5 left a 393px cart table only 312px of card to sit in */
	.frappe-card.p-5 { padding: 16px !important; }
	/* add-to-cart on product cards renders ~28px tall on phones */
	.btn-add-to-cart-list, .btn-add-to-cart {
		min-height: 40px; display: inline-flex; align-items: center; justify-content: center;
	}
	.btn-list-view, .btn-grid-view, .nova-drawer-close { min-width: 40px; min-height: 40px; }
}

@media (max-width: 767px) {
	/* cart controls a buyer actually uses on a phone: qty steppers were 24x28,
	   Place Order 29px tall */
	.cart-btn { min-width: 40px; min-height: 40px; }
	.nova-place-order, .btn-place-order { min-height: 46px; font-size: 15px; }
	.btn-primary-light, .btn-change-address, .btn-remove-cart-item { min-height: 40px; }
}

@media (max-width: 767px) {
	/* A 3-column cart table cannot fit a phone: scrolling it inside a wrapper
	   hid the price and the + button off-screen. Stack each line instead. */
	.cart-table thead { display: none; }
	.cart-table, .cart-table tbody, .cart-table tr, .cart-table td { display: block; width: 100%; }
	.cart-table tr { padding: 14px 0; border-bottom: 1px solid var(--nova-border); }
	.cart-table tr:last-child { border-bottom: 0; }
	.cart-table td { border: 0 !important; padding: 6px 0; text-align: left !important; }
	.cart-table td.item-subtotal { display: none; }  /* same figure as the qty cell */
	.nova-tablewrap { overflow-x: visible; }
}

@media (max-width: 767px) {
	/* the totals row is <th>, not <td>: stacking the cells ran the label into
	   the figure ("NET TOTALAED 299.00") */
	.cart-table tfoot tr {
		display: flex; justify-content: space-between; align-items: baseline;
		gap: 10px; padding-top: 12px;
	}
	.cart-table tfoot th { display: block; border: 0 !important; padding: 0; text-align: left !important; }
	.cart-table tfoot th:empty { display: none; }
	/* these were squeezed to 62px wide and wrapped over three lines */
	.btn-primary-light { width: 100%; margin: 0 0 8px !important; }
}

@media (max-width: 767px) {
	.nova-col-full { width: 100% !important; max-width: 100% !important; flex: 0 0 100% !important; margin-bottom: 8px; }
}

/* enquiry-only products (analyzers, vaccines): no price anywhere, so give the
   buyer a route to ask instead of a dead card */
.nova-quote-btn {
	display: inline-block; margin-top: 12px; border-radius: var(--nova-radius);
	padding: 10px 20px; font-weight: 700;
}
.nova-quote-alt { display: inline-block; margin: 12px 0 0 14px; color: var(--nova-link); font-weight: 600; }

/* login: password help replaces a reset link that cannot deliver mail yet */
.nova-pw-help {
	margin: 14px 0 4px; padding: 12px 14px; border-radius: var(--nova-radius);
	background: #f4f8fd; border: 1px solid var(--nova-border);
	font-size: 13px; line-height: 1.55; color: var(--nova-muted);
}
.nova-pw-help a { color: var(--nova-primary); font-weight: 700; }
/* Frappe's login theme paints its field labels and help text var(--ink-gray-5)
   (#7c7c7c, 4.17:1) from `.for-login .page-card .page-card-body …`, which
   outranks the rule above — so the help box lost its token too. Reclaim both.
   /login is easy to miss in a sweep: anything that signs in first never sees it. */
.for-login .page-card .page-card-body .form-label,
.for-login .page-card .page-card-body .nova-pw-help,
.for-login .page-card .page-card-body .forgot-password-message {
	color: var(--nova-muted) !important;
}

/* order page: printable copy sits beside Order Again */
.nova-print-order {
	margin-left: 10px; border: 1px solid var(--nova-primary); color: var(--nova-primary);
	background: #fff; border-radius: var(--nova-radius); font-weight: 700; padding: 8px 18px;
}
.nova-print-order:hover { background: #eef3fa; color: var(--nova-primary); }

/* cart: minimum-order warning shown before the customer clicks Place Order */
.nova-min-order {
	margin-bottom: 10px; padding: 10px 12px; border-radius: var(--nova-radius);
	background: #fff6e8; border: 1px solid #f0d6a8;
	font-size: 13px; line-height: 1.5; color: #7a5510;
}
.nova-min-order b { color: #6b4708; }

/* friendly recovery on the "order not permitted / not found" page */
.nova-err-help {
	max-width: 620px; margin: 24px auto; padding: 22px 24px;
	background: #fff; border: 1px solid var(--nova-border); border-radius: var(--nova-radius-lg);
	box-shadow: 0 6px 18px var(--nova-shadow); text-align: left;
}
.nova-err-help p { color: var(--nova-muted); margin-bottom: 16px; line-height: 1.6; }
.nova-err-help .btn-primary { border-radius: var(--nova-radius); padding: 10px 20px; font-weight: 700; }
.nova-err-alt { margin-left: 14px; color: var(--nova-link); font-weight: 600; }

/* /me is Frappe's own settings page: no navbar and a full-width container, so
   the hub cards ran edge-to-edge. Constrain them there; /account has proper
   site chrome and gets its width from the normal page container. */
.page_content > .nova-account-grid,
.container > .nova-account-grid {
	max-width: 1290px; margin-left: auto; margin-right: auto;
	padding-left: 20px; padding-right: 20px;
}

/* contact cards are informational, not links — same card, no hover lift */
.nova-contact-card { cursor: default; }
.nova-contact-card:hover { transform: none; box-shadow: 0 1px 2px var(--nova-shadow); }
.nova-contact-card a { color: var(--nova-link); font-weight: 600; }

/* listing sort control */
.nova-sort {
	margin: 0 0 14px auto; display: block;
	border: 1px solid var(--nova-border); border-radius: var(--nova-radius);
	background: #fff; color: var(--nova-text);
	font-family: inherit; font-size: 13.5px; font-weight: 600;
	padding: 8px 30px 8px 12px; min-height: 38px; cursor: pointer;
	appearance: none;
	background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23265faf' stroke-width='2'><path d='M6 9l6 6 6-6'/></svg>");
	background-repeat: no-repeat; background-position: right 9px center; background-size: 14px;
}
.nova-sort:focus { outline: 2px solid var(--nova-secondary); outline-offset: 1px; }

@media (max-width: 767px) {
	/* right-aligned under the search box it sat indented and crowded the
	   view toggles; full width reads as its own control */
	.nova-sort { width: 100%; margin: 10px 0 14px; }
}

/* Mobile menu button rendered as an empty box: the sprite loads and the <use>
   resolves, but the SVG inherits fill:transparent / stroke:none, and lucide
   icons are stroke-drawn — so nothing painted. Phones saw a blank rounded
   rectangle where the navigation is. */
.navbar-toggler svg,
.navbar-toggler .icon {
	width: 22px; height: 22px;
	stroke: var(--nova-primary) !important;
	fill: none !important;
	stroke-width: 2;
	stroke-linecap: round;
	stroke-linejoin: round;
}
.navbar-toggler { border-color: var(--nova-border); border-radius: var(--nova-radius); }

/* ---- contrast: stock greys that never cleared WCAG AA ---- */
/* Bootstrap's .text-muted (#7c7c7c, 4.17:1) carries real information here —
   order dates and the item summary under each order number — and the avatar
   initials painted #0289f7 on a pale disc at 3.41:1. Both route through the
   tokens now, which are checked against white, wash and ghost. */
.text-muted, .small.text-muted, .page-breadcrumbs .breadcrumb-item,
.page-breadcrumbs .breadcrumb-item a { color: var(--nova-muted) !important; }
/* /me keeps stock's "my account" list under our hub cards, and it paints the
   label span #7c7c7c (4.17:1) INSIDE a link — so the text read grey while the
   anchor around it was blue. These are links; let them look like links. */
/* The product page's specification table paints its label column #7c7c7c
   (4.17:1) — "Brand", "Item Code", "Category", every attribute row. On every
   product page, and missed until now because earlier PDP checks covered layout
   but never contrast. */
.item-website-specification .spec-label,
.spec-label { color: var(--nova-muted); }
/* The cart's payment summary labels ("Net Total (5.0 Items)", "VAT 5%") are
   #7c7c7c, 4.17:1. Only visible with something IN the cart, which is why an
   audit run against an empty cart never saw them. */
/* stock uses an ID selector here — #page-cart .cart-container .payment-summary
   .bill-label (1,3,0) — so a plain .bill-label rule never applies. */
#page-cart .cart-container .payment-summary .bill-label,
.bill-label { color: var(--nova-muted); }
.my-account-item-link .item-link-text { color: inherit; }
.portal-footer a { color: var(--nova-link); }
/* The error page ("Not Permitted", which is what a Customer correctly gets at
   /desk and /apps) paints its heading and body var(--text-light) = #7c7c7c,
   3.93:1, via `.error-page p, .error-page h4`. There is no .page-card wrapper
   here despite the page-card-head class on the heading. */
.error-page h4, .error-page .page-card-head { color: var(--nova-heading); }
.error-page p { color: var(--nova-muted); }
.avatar-frame, .avatar .avatar-frame {
	background: var(--nova-ghost) !important;
	color: var(--nova-primary) !important;
	font-weight: 600;
}


/* Skip link — WCAG 2.4.1. Off-screen until it takes focus. */
.nova-skip {
  position: absolute; left: 12px; top: -64px; z-index: 2000;
  background: #fff; color: var(--nova-primary);
  border: 2px solid var(--nova-primary); border-radius: var(--nova-radius);
  padding: 10px 18px; font-weight: 700; font-size: 14px; text-decoration: none;
  box-shadow: 0 4px 14px var(--nova-shadow);
  transition: top .15s var(--nova-ease);
}
.nova-skip:focus { top: 12px; color: var(--nova-primary); }
/* the skip target must not draw a ring of its own when focused programmatically */
main:focus, .page_content:focus, #page-cart:focus { outline: none; }

/* The active view toggle sets box-shadow:none, which also removed its focus
   indicator — measured as the one stop in a full tab pass with no visible ring. */
.btn-list-view:focus-visible, .btn-grid-view:focus-visible,
#image-view:focus-visible, #list:focus-visible,
.btn-list-view.btn-primary:focus-visible, .btn-grid-view.btn-primary:focus-visible,
#image-view.btn-primary:focus-visible, #list.btn-primary:focus-visible {
  outline: 2px solid var(--nova-primary) !important;
  outline-offset: 2px;
}


/* Page title inserted where a page renders none of its own (currently /me).
   Matches .nova-home-greet so it reads as part of the same family. */
.nova-page-title {
  font-size: 26px; font-weight: 800; color: var(--nova-heading);
  /* the content grid it sits above is a centred container, so a full-width
     sibling heading lands flush at x=0 — match the container instead */
  max-width: 1290px;
  margin: 24px auto 18px;
  padding: 0 20px;
}


/* /me ships <main class="container"> with a grey background, which survived the
   white-body rule because that rule targets <body>. */
main.container { background: transparent; }


/* ---- listing card: make it a product, not a link with a picture ----------
   Three problems, all visible in a screenshot of one card:
   1. webshop sets `visibility: hidden` on the grid Add to Cart until hover, so
      the primary action of a reorder-driven B2B shop was invisible — and it
      still reserved 29px, which is the empty gap at the bottom of every card.
   2. The product name was link-blue at 15px, reading as a hyperlink rather
      than the name of the thing being sold, and ran to 3 lines on some cards
      and 2 on others, so prices never lined up across a row.
   3. Cards were white on white with a hairline border and no resting shadow,
      so the grid read as loose floating text rather than a shelf. */
.item-card .btn-add-to-cart-list { visibility: visible !important; }

.product-title, a .product-title, .product-title a {
  color: var(--nova-text) !important;
  font-weight: 600;
  font-size: 14.5px;
  line-height: 1.4;
  /* clamp to two lines so every card's price sits on the same baseline */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: 2.8em;
}
.item-card .card:hover .product-title { color: var(--nova-primary) !important; }

/* the category is context, not a headline — let the product name lead */
.product-category { font-size: 10.5px; letter-spacing: .4px; font-weight: 600; margin-bottom: 2px; }

/* enough edge to read as an object, not so much that 24 of them shout */
.item-card .card { border-color: #e6eaef; box-shadow: 0 1px 2px rgba(16, 24, 40, .04); }


/* ---- listing toolbar: one row, one baseline ------------------------------
   Search, sort and the view toggles sat at three different vertical positions
   (y=248, y=280, y=275) because the search wrapper is `w-100`, which forced
   the sort control onto a second line inside the same flex parent and made the
   toolbar 84px tall. Same row, vertically centred, sort next to search. */
.toolbar .input-group { align-items: center; gap: 10px; }
/* one row only where there is room for one: at 390px forcing nowrap crushed
   the search field into the sort control until they overlapped. */
@media (min-width: 768px) {
  .toolbar .input-group { flex-wrap: nowrap; }
  .toolbar .input-group > .dropdown.w-100 {
    width: auto !important;
    flex: 1 1 auto;
    min-width: 0;
  }
  .nova-sort { margin: 0 !important; flex: 0 0 auto; height: 38px; }
}
@media (max-width: 767px) {
  .toolbar .input-group { flex-wrap: wrap; }
  .toolbar .input-group > .dropdown.w-100 { width: 100% !important; flex: 1 1 100%; }
  .nova-sort { width: 100%; margin: 10px 0 0 !important; height: 38px; }
}
.toolbar #search-box { height: 38px; }
.toolbar .form-group { display: flex; align-items: center; }


/* the empty Shipping Address card on /cart now says why it is empty */
.nova-ship-hint {
  margin: 6px 0 2px;
  font-size: 13px;
  line-height: 1.55;
  color: var(--nova-muted);
}
/* the control that resolves it was plain text beside a bare checkbox */
label[for="input_same_billing"], #input_same_billing + span,
.cart-addresses .checkbox label { font-size: 13.5px; color: var(--nova-text); }


/* ---- cart: secondary buttons that look clickable -------------------------
   "See your past orders" and "Continue Shopping" rendered as grey-on-grey
   (#e2e2e2 fill, near-black text) — the universal signal for a DISABLED
   control, on two links that work perfectly well. Same outline treatment the
   mini-cart already uses, so the page has one filled button (Place Order) and
   the rest read as secondary. */
.btn-primary-light {
  background: #fff !important;
  color: var(--nova-primary) !important;
  border: 1px solid var(--nova-primary) !important;
  font-weight: 600;
}
.btn-primary-light:hover {
  background: var(--nova-ghost) !important;
  color: var(--nova-primary) !important;
}

/* the subtotal cell was 113px wide holding "AED 598.00 Rate: AED 299.00",
   so the rate wrapped mid-figure. Give the money room and keep the unit
   rate on its own quiet line. */
.cart-items td:last-child, .cart-items th:last-child { min-width: 150px; }
.cart-items .item-rate, .cart-items td:last-child small {
  display: block;
  white-space: nowrap;
  font-size: 12.5px;
  color: var(--nova-muted);
  font-variant-numeric: tabular-nums;
}


/* ---- home: order the page for the person actually using it ---------------
   This is a reorder-driven B2B shop: a vet signs in to buy the same things
   again. Their recent orders were the LAST block on the page, below the hero,
   the quick actions, eight category tiles, seventeen brand logos and the
   delivery strip — roughly 1500px of scrolling to reach the one thing they
   came for. Brands and delivery notes are reference material and move below.

   Done with flex `order` rather than by rewriting the page content, because
   the home page body lives in a Web Page record in the database. */
.nova-home { display: flex; flex-direction: column; }
/* anything unnamed (the hero) stays at the top; the named blocks below carry a
   higher specificity so they win regardless — declared first so the intent
   reads in source order rather than relying on that. */
.nova-home > * { order: 0; flex: 0 0 auto; }
.nova-home > .nova-home-greet    { order: 1; }
.nova-home > .nova-home-sub      { order: 2; }
/* /account and /contact reuse this container with their own block names, so
   every child that can appear must be named here. `.nova-account-grid` was not,
   fell to the catch-all order:0, and jumped ABOVE the greeting on /account —
   it only looked right on the home page. */
.nova-home > .nova-home-actions,
.nova-home > .nova-account-grid   { order: 3; }
.nova-home > .nova-home-recent   { order: 4; }   /* was last */
.nova-home > .nova-home-cats     { order: 5; }
.nova-home > .nova-home-delivery { order: 6; }
.nova-home > .nova-home-brands   { order: 7; }   /* was third */

/* Flex does not collapse margins, so each section's own margin now stands on
   its own and the rhythm went ragged: 34px, then 8px, then 42px, then 42px.
   Zero the top margins and state one spacing value, so the page has a beat.
   The greeting and its subtitle stay tight — they are one unit, not two. */
.nova-home > * { margin-top: 0; }
.nova-home > .nova-home-greet    { margin-bottom: 4px; }
.nova-home > .nova-home-sub      { margin-bottom: 26px; }
.nova-home > .nova-home-actions,
.nova-home > .nova-account-grid,
.nova-home > .nova-home-recent,
.nova-home > .nova-home-cats,
.nova-home > .nova-home-delivery { margin-bottom: 34px; }
.nova-home > .nova-home-brands   { margin-bottom: 0; }


/* Order again, per row on /orders. Sits above the invisible overlay link that
   makes the whole row clickable, or the click would open the order instead. */
.nova-row-reorder {
  position: relative;
  z-index: 2;
  /* the row is display:block, so the button lands as a block sibling under the
     Bootstrap row — margin-left:auto only pushes it right once it is a block
     box with an intrinsic width. Sits under the amount, on the right. */
  display: block;
  width: fit-content;
  margin: 2px 0 0 auto;
  background: #fff;
  color: var(--nova-primary);
  border: 1px solid var(--nova-primary);
  border-radius: var(--nova-radius-sm);
  font-size: 12.5px;
  font-weight: 700;
  padding: 5px 12px;
  white-space: nowrap;
}
.nova-row-reorder:hover { background: var(--nova-ghost); }
.nova-row-reorder[disabled] { opacity: .7; }
@media (max-width: 767px) { .nova-row-reorder { width: 100%; margin-top: 8px; } }


/* ---- product page: tighten the spec sheet ------------------------------
   Five label/value rows were spread over 318px with a 26px heading competing
   with the product name above it. Denser, quieter, and the value column gets
   the emphasis since that is what the buyer is scanning for.
   NOT re-flowed into two columns: `display: grid` on table elements strips
   table semantics from screen readers, which is a 1.3.1 regression to buy
   density the page does not need. */
.product-container .product-title.mb-5,
.product-page .product-title.mb-5 {
  font-size: 19px !important;
  font-weight: 800;
  margin-bottom: 14px !important;
  margin-top: 8px !important;
}
.item-website-specification td { padding: 9px 12px 9px 0 !important; }
.item-website-specification tr:not(:last-child) { border-bottom: 1px solid var(--nova-rule, #eef1f5); }
.item-website-specification .spec-value,
.item-website-specification td:last-child { color: var(--nova-text); font-weight: 500; }
/* Product details sat flush: stock gives the spec table only `mt-2` (8px) above
   it, it is the FIRST child of .product-page-content so nothing else separates
   it from the card above, and that parent has padding-bottom: 0 — so it also
   ran straight into the footer. Give the section its own breathing room, on the
   same 34-40px rhythm the home page sections use. */
.item-website-specification { margin-top: 40px !important; margin-bottom: 40px; }
@media (max-width: 767px) {
  .item-website-specification { margin-top: 28px !important; margin-bottom: 28px; }
}

/* the label column does not need half the page */
.item-website-specification td:first-child { width: 220px; }

/* ---- phones: stop nesting padding three deep -------------------------------
   Measured on /cart at 390px: the cart table rendered 278px wide, inset 56px on
   each side. That inset is three separate paddings stacking —
   main .container (24) + .col-md-8 (15) + .frappe-card.p-5 (16) = 55px a side,
   so 28% of the screen was gone before any content. The product title was left
   wrapping onto five lines and "AED 1,495.00" split across two.

   Bootstrap's grid gutters exist to separate side-by-side columns; at this width
   the columns are already stacked, so they only shrink the content. */
@media (max-width: 767px) {
  /* Two traps in one selector.
     1. It is <main class="container">, so `main .container` (descendant)
        matches nothing — it has to be `main.container`. The desktop max-width
        rule higher up carries the same typo and has therefore never applied;
        left as found, since fixing it would widen every desktop page by 30px.
     2. website.bundle ships
          @media (max-width: 992px) { .page-content-wrapper .container { padding: 1.5rem } }
        at (0,2,0), which out-specifies `main.container` at (0,1,1) — that 1.5rem
        IS the 24px. Naming the wrapper takes it to (0,2,1) and wins on merit
        rather than with !important. */
  .page-content-wrapper main.container { padding-left: 16px; padding-right: 16px; }
  .cart-container .col-md-8,
  .cart-container .col-md-4 { padding-left: 0; padding-right: 0; }
  .cart-container .frappe-card.p-5 { padding-left: 14px !important; padding-right: 14px !important; }

  /* a 98px thumbnail on a 390px screen crowds out the name it belongs to */
  .cart-items img, .cart-table img { max-width: 64px; height: auto; }

  /* money must never break across lines */
  .cart-table td.text-right, .cart-table .item-subtotal,
  .bill-content, .bill-label { white-space: nowrap; }

  /* webshop already linearises this table (display:block on table/tr/td below
     768px) but leaves each cell at its old COLUMN width — 197px and 82px inside
     a 328px row — so every cell had dead space beside it. Stacked cells should
     span the row. Knock-on: the item cell is a flex row whose details column was
     112px, which left the notes field 78px and showing only "Add". */
  /* !important is earned here: webshop writes style="width:60%" INLINE on the
     item cell, and an inline style outranks any selector without it. */
  .cart-table tr, .cart-table td { width: 100% !important; }
  .cart-table textarea, .cart-items textarea { width: 100%; max-width: 100%; }
}

/* ---- address book page chrome ---------------------------------------------
   Stock pins .web-list-container to min-height: 470px, so a customer with one
   address got 179px of blank white below their single card. It also draws a
   bordered, 32px-padded panel AROUND cards that already have their own border —
   a card inside a card, which no other page here does. Flattened so the page
   reads like /orders and /me: heading, action, cards. */
/* Stock's rule is
     [data-doctype="Web Form"] .page-content-wrapper .container .page_content .web-list-container
   at (0,5,0) — a plain `.nova-addressbook .web-list-container` (0,2,0) loses to
   it. Matching the chain and adding `body` takes this to (0,5,1), so it wins on
   specificity rather than on !important. */
body.nova-addressbook .page-content-wrapper .container .page_content .web-list-container {
  min-height: 0;
  border: 0;
  padding: 0;
  border-radius: 0;
}
/* stock ships this at 4px 8px, which crowds "Add address" against its own edges */
body.nova-addressbook .button-new { padding: 7px 14px; font-size: 13px; font-weight: 700; }

/* ---- account: address book ------------------------------------------------
   Replaces Frappe's generic list view (select-all checkbox, "Sr." column, the
   internal "Customer-Type" document name in an ellipsis). */
.nova-addr-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 16px;
  padding: 4px 0 8px;
}
.nova-addr-card {
  border: 1px solid #e8ecf1;
  border-radius: var(--nova-radius-lg);
  padding: 18px 20px;
  background: #fff;
  position: relative;
}
.nova-addr-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
.nova-addr-head h2 { font-size: 15.5px; font-weight: 700; color: var(--nova-heading); margin: 0; }
.nova-addr-tags { display: flex; gap: 6px; flex-wrap: wrap; }
.nova-addr-badge {
  font-size: 10.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  padding: 3px 8px; border-radius: 999px;
  background: var(--nova-wash); color: var(--nova-muted);
}
.nova-addr-badge.is-default { background: var(--nova-ghost); color: var(--nova-primary); }
.nova-addr-lines { color: var(--nova-text); font-size: 13.5px; line-height: 1.65; margin: 0 0 6px; }
.nova-addr-phone { color: var(--nova-muted); font-size: 13px; margin: 0 0 10px; }
.nova-addr-edit { font-size: 13px; font-weight: 600; color: var(--nova-link); }
.nova-addr-edit:hover { text-decoration: underline; }
.nova-addr-empty { padding: 26px 4px 34px; }
.nova-addr-empty p { margin: 0 0 6px; color: var(--nova-heading); font-weight: 600; }
.nova-addr-empty .nova-sub { color: var(--nova-muted); font-weight: 400; margin-bottom: 16px; }

/* ---- empty states that name the next action ------------------------------ */
.nova-empty { padding: 28px 0 40px; max-width: 46ch; }
.nova-empty h2 { font-size: 17px; font-weight: 700; color: var(--nova-heading); margin: 0 0 8px; }
.nova-empty p { color: var(--nova-muted); font-size: 14px; line-height: 1.6; margin: 0 0 18px; }

/* ---- mini-cart: remove control ------------------------------------------- */
.nova-mc-item { position: relative; }
.nova-mc-del {
  position: absolute; top: 6px; right: 0;
  width: 24px; height: 24px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  border: 0; background: none; border-radius: 50%;
  color: var(--nova-muted); cursor: pointer;
  transition: background .15s var(--nova-ease), color .15s var(--nova-ease);
}
.nova-mc-del svg { width: 13px; height: 13px; }
.nova-mc-del:hover { background: var(--nova-wash); color: #b3261e; }
.nova-mc-del:disabled { opacity: .5; cursor: default; }
.nova-mc-item.is-going { opacity: .45; }

/* ---- /me: stock centres the Settings card in a .portal-container capped at
   540px with a 390px left margin, while our own account cards span the full
   1320px grid directly above it — the page reads as two unrelated layouts.
   Scoped by the .nova-me body class so other portal pages that legitimately
   want a narrow centred column are untouched. */
/* 15px inset matches the account grid above it (grid 15→1305, this was
   0→1320, so the card visibly overhung the cards it belongs with) */
.nova-me .portal-container { max-width: none; margin-left: 15px; margin-right: 15px; }
/* stock leaves a bordered but empty row under the last settings link */
.nova-me .portal-container li:empty,
.nova-me .portal-container .list-group-item:empty { display: none; }

/* ---- footer: four columns over a copyright bar -----------------------------
   Mirrors the old weborder's footer (Who We Are / Menu / Informations /
   Newsletter). The fourth column is Delivery, not a newsletter — see nova.js
   block 32 for why. */
.nova-foot { padding: 40px 0 0; }
.nova-foot-grid {
  display: grid;
  grid-template-columns: 1.5fr 1fr 1.35fr 1.35fr;
  gap: 34px 40px;
  align-items: start;
}
.nova-foot h2 {
  font-size: 13px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
  color: var(--nova-heading); margin: 0 0 14px;
}
.nova-foot p { color: var(--nova-muted); font-size: 13.5px; line-height: 1.65; margin: 0 0 16px; max-width: 34ch; }
.nova-foot ul { list-style: none; margin: 0; padding: 0; }

.nova-foot-menu li { margin-bottom: 9px; }
.nova-foot-menu a { color: var(--nova-muted); font-size: 13.5px; }
.nova-foot-menu a:hover { color: var(--nova-primary); }

.nova-foot-info li {
  display: flex; align-items: flex-start; gap: 10px;
  color: var(--nova-muted); font-size: 13.5px; line-height: 1.55; margin-bottom: 12px;
}
.nova-foot-info a { color: var(--nova-muted); }
.nova-foot-info a:hover { color: var(--nova-primary); }
.nova-foot-ic { width: 16px; height: 16px; flex: 0 0 16px; margin-top: 2px; color: var(--nova-primary); }

.nova-foot-social { display: flex; gap: 10px; }
.nova-foot-social a {
  width: 32px; height: 32px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--nova-primary); color: #fff;
  transition: background .15s var(--nova-ease), transform .15s var(--nova-ease);
}
.nova-foot-social a:hover { background: #1f4f92; transform: translateY(-2px); color: #fff; }
.nova-foot-social svg { width: 15px; height: 15px; }

.nova-foot-bar {
  margin-top: 34px; padding: 18px 0 24px;
  border-top: 1px solid #e3e9f0;
  text-align: center; color: var(--nova-muted); font-size: 12.5px;
}

@media (max-width: 991px) {
  .nova-foot-grid { grid-template-columns: 1fr 1fr; gap: 30px; }
}
@media (max-width: 575px) {
  .nova-foot-grid { grid-template-columns: 1fr; gap: 26px; }
  .nova-foot { padding-top: 30px; }
  .nova-foot p { max-width: none; }
}

/* ---- cart: payment method ------------------------------------------------- */
.nova-pay { margin: 4px 0 14px; }
.nova-pay-label {
  display: block; font-size: 11px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--nova-muted); margin-bottom: 6px;
}
.nova-pay-select {
  width: 100%; padding: 9px 12px; font-size: 14px;
  border: 1px solid var(--nova-border); border-radius: var(--nova-radius);
  background: #fff; color: var(--nova-text);
}
.nova-pay-select:focus { border-color: var(--nova-secondary); outline: none;
  box-shadow: 0 0 0 3px rgba(72, 147, 213, .18); }
.nova-pay-select:disabled { opacity: .6; }

/* ---- cart addresses: one by default --------------------------------------
   Hiding with a class rather than removing the node, so "Deliver to a different
   address" can bring the card straight back without a reload. */
.nova-addr-hidden { display: none !important; }
/* The stock header row is two .col-6 halves. Tried making the heading flexible
   so "Delivery & billing address" would fit one line; flex-shrink let it
   collapse to 65px and FOUR lines instead — worse than the two it started with.
   Reverted: the heading wraps to two lines and that is fine. */
.nova-addr-split {
  display: inline-block;
  margin-top: 12px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--nova-link);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  text-align: left;
}
.nova-addr-split:hover { color: var(--nova-primary); text-decoration: underline; }

/* ---- announcement bar -----------------------------------------------------
   The text lives in Website Settings.banner_html so a manager can edit it at
   /app/website-settings. It used to carry six inline CSS declarations, which
   meant changing "4:30pm" risked breaking the styling; the field now holds a
   plain sentence and the appearance is here. */
.nova-announce {
  background: var(--nova-primary);
  color: #fff;
  text-align: center;
  padding: 8px 14px;
  font-size: 13.5px;
  font-weight: 500;
  line-height: 1.45;
}
.nova-announce a { color: #fff; text-decoration: underline; }

/* ---- continuous loading ---------------------------------------------------- */
.nova-pager-hidden { display: none !important; }
.nova-more { min-height: 8px; margin: 8px 0 40px; text-align: center; }
.nova-more-spin {
  display: none; padding: 14px 0; color: var(--nova-muted); font-size: 13px;
}
.nova-more.is-loading .nova-more-spin { display: block; }
.nova-more.is-done .nova-more-spin { display: none; }

/* ---- related products on the PDP ------------------------------------------ */
.nova-related { margin: 8px 0 48px; }
.nova-related h2 {
  font-size: 19px; font-weight: 800; color: var(--nova-heading); margin: 0 0 18px;
}
.nova-related-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px;
}
.nova-related-card {
  display: flex; flex-direction: column; gap: 8px;
  border: 1px solid #e8ecf1; border-radius: var(--nova-radius-lg);
  padding: 14px; background: #fff; color: var(--nova-text);
  transition: box-shadow .2s var(--nova-ease), transform .2s var(--nova-ease);
}
.nova-related-card:hover {
  box-shadow: 0 8px 24px var(--nova-shadow); transform: translateY(-2px); color: var(--nova-text);
}
.nova-related-card img {
  width: 100%; height: 120px; object-fit: contain; background: #fff;
}
.nova-related-name {
  font-size: 13.5px; line-height: 1.4; color: var(--nova-text);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.nova-related-price {
  font-size: 15px; font-weight: 800; color: var(--nova-heading);
  font-variant-numeric: tabular-nums; margin-top: auto;
}
@media (max-width: 575px) {
  .nova-related-grid { grid-template-columns: repeat(2, 1fr); gap: 12px; }
  .nova-related-card img { height: 96px; }
}

/* ---- card quantity + result count ------------------------------------------ */
.nova-cardqty {
  display: flex; align-items: center; justify-content: center; gap: 2px;
  margin: 0 0 8px;
}
.nova-cardqty-btn {
  width: 26px; height: 26px; padding: 0; line-height: 1;
  border: 1px solid var(--nova-border); background: #fff; color: var(--nova-text);
  border-radius: var(--nova-radius-sm); font-size: 15px; font-weight: 600; cursor: pointer;
  transition: background .12s var(--nova-ease), border-color .12s var(--nova-ease);
}
.nova-cardqty-btn:hover:not(:disabled) { background: var(--nova-ghost); border-color: var(--nova-secondary); }
.nova-cardqty-btn:disabled { opacity: .4; cursor: default; }
.nova-cardqty-n {
  min-width: 34px; text-align: center; font-size: 13.5px; font-weight: 700;
  font-variant-numeric: tabular-nums; color: var(--nova-text);
}

.nova-count {
  margin: -4px 0 14px; font-size: 13px; color: var(--nova-muted);
  font-variant-numeric: tabular-nums;
}

/* ---- product badges -------------------------------------------------------
   Sale / New / Trending, set per product by a webshop manager on the Website
   Item form (Custom Field nova_badge) — no deploy needed to change one. */
.nova-badge {
  position: absolute;
  top: 10px; left: 10px;
  z-index: 3;
  padding: 4px 9px;
  border-radius: 999px;
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: #fff;
  background: var(--nova-primary);
  box-shadow: 0 2px 6px rgba(16, 24, 40, .18);
  pointer-events: none;      /* never steal the card's own click */
}
.nova-badge-sale        { background: #c62828; }   /* 5.9:1 on white */
.nova-badge-new         { background: #1ca04c; }
.nova-badge-trending    { background: #b8541a; }
.nova-badge-best-seller { background: #6a3fb5; }
/* the card's image box is the positioning context */
.card-img-container { position: relative; }

/* ---- header search: visible, not hidden behind a toggle --------------------
   Measured: the navbar left nav ended at 275px and the right-hand links began
   at 739px — 464px of empty bar between them, while the search hid behind a
   magnifier. On a 600-SKU catalogue search is the primary way in, so it gets a
   permanent field. The toggle survives only below 992px, where the space
   genuinely is not there. */
@media (min-width: 992px) {
  .nova-search-btn { display: none; }
  .nova-search-form {
    width: 420px;
    overflow: visible;
    position: relative;
    transition: none;          /* nothing left to animate open */
  }
  .nova-search.open .nova-search-form { width: 420px; }
  .nova-search-form input { width: 100%; padding-left: 38px; }
  .nova-search-inline {
    position: absolute; left: 13px; top: 50%; transform: translateY(-50%);
    width: 15px; height: 15px; color: var(--nova-muted); pointer-events: none;
  }
}
@media (max-width: 991px) {
  .nova-search-inline { display: none; }
}

/* ---- orders / invoices rows: content sat OUTSIDE its own border ------------
   Stock gives the row `padding: 16px 0` — vertical only — while the Bootstrap
   .row inside carries the usual -15px side margins. Measured: card left edge
   428px, content left edge 414px, so every row's text crossed its own border.
   Pad the card and the negative margin lands inside it. */
.transaction-list-item { padding-left: 18px; padding-right: 18px; }

/* Rows had margin: 0, so consecutive cards shared an edge and the list read as
   one striped block rather than a set of orders. */
.transaction-list-item { margin-bottom: 12px; }
.transaction-list-item:last-child { margin-bottom: 0; }

/* The list lives in .website-list, which carries a 20px left padding the page
   header does not — so "Orders" sat at 408px and every row at 428px. Dropping
   it lines the list up with its own heading. */
.website-list { padding-left: 0; padding-right: 0; }

/* download link sits beside the row action */
.nova-row-pdf {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 12px; font-weight: 600; color: var(--nova-link);
  background: none; border: 0; padding: 0; cursor: pointer;
}
.nova-row-pdf:hover { color: var(--nova-primary); text-decoration: underline; }
.nova-row-pdf svg { width: 13px; height: 13px; }
.nova-row-actions {
  display: flex; align-items: center; gap: 14px;
  width: fit-content; margin: 2px 0 0 auto; z-index: 2; position: relative;
}

/* ---- navbar search typeahead ----------------------------------------------
   The list is a child of .nova-search, not of .nova-search-form: the form
   animates from width:0 with overflow:hidden and would clip it. */
.nova-search { position: relative; }
.nova-ac {
  position: absolute;
  top: calc(100% + 8px);
  /* Left-aligned to the field and widened from 360px: once each row carried a
     price and an Add button, the name column collapsed to ~90px and every
     product read "Hill's Science Plan Sterilise…". The name is the thing being
     scanned, so it gets the space. */
  left: 0;
  right: auto;
  z-index: 1050;              /* above the sticky filter rail and the navbar */
  width: 520px;
  max-width: calc(100vw - 32px);
  max-height: 380px;
  overflow-y: auto;
  margin: 0;
  padding: 6px;
  list-style: none;
  background: #fff;
  border: 1px solid #e8ecf1;
  border-radius: var(--nova-radius-lg);
  box-shadow: 0 12px 32px var(--nova-shadow);
}
.nova-ac li {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: var(--nova-radius-sm);
  cursor: pointer;
  font-size: 13.5px;
  line-height: 1.35;
  color: var(--nova-text);
}
.nova-ac li.on { background: var(--nova-ghost); color: var(--nova-primary); }
.nova-ac img,
.nova-ac .nova-ac-noimg {
  width: 34px; height: 34px; flex: 0 0 34px;
  object-fit: contain;
  border-radius: var(--nova-radius-sm);
  background: var(--nova-wash);
}
.nova-ac .nova-ac-t {
  flex: 1 1 auto;
  min-width: 0;               /* let the title ellipsis instead of pushing the code out */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.nova-ac .nova-ac-price {
  flex: 0 0 auto; font-size: 12px; font-weight: 700;
  color: var(--nova-heading); font-variant-numeric: tabular-nums;
}
.nova-ac .nova-ac-price.is-quote { font-weight: 500; color: var(--nova-muted); }
.nova-ac .nova-ac-add {
  flex: 0 0 auto; border: 1px solid var(--nova-primary); background: #fff;
  color: var(--nova-primary); font-size: 11.5px; font-weight: 700;
  padding: 4px 10px; border-radius: var(--nova-radius-sm); cursor: pointer;
  transition: background .15s var(--nova-ease), color .15s var(--nova-ease);
}
.nova-ac .nova-ac-add:hover { background: var(--nova-primary); color: #fff; }
.nova-ac .nova-ac-add.is-added,
.nova-ac .nova-ac-add:disabled { background: var(--nova-ghost); color: var(--nova-primary); cursor: default; }
.nova-ac .nova-ac-note {
  padding: 6px 10px 8px; font-size: 12px; color: var(--nova-muted);
  border-bottom: 1px solid #eef1f4; margin-bottom: 4px; cursor: default;
}
.nova-ac .nova-ac-note:hover { background: none; }
/* flex: 0 0 auto, NOT 0 1 auto — letting the code shrink made it overflow
   under the price ("60731AED 80.00"). The name is the flexible column here;
   it already carries min-width: 0. */
.nova-ac .nova-ac-c {
  flex: 0 0 auto;
  color: var(--nova-muted);
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* Eight results run past the 380px cap, which left "See all results" reachable
   only by scrolling — the one row that guarantees a way out of the dropdown.
   Sticky keeps it on screen without cutting the result count. */
.nova-ac .nova-ac-all {
  position: sticky;
  bottom: -6px;               /* cancels the list's 6px padding */
  justify-content: center;
  font-weight: 700;
  font-size: 12.5px;
  color: var(--nova-link);
  background: #fff;
  border-top: 1px solid #eef1f4;
  border-radius: 0 0 var(--nova-radius-sm) var(--nova-radius-sm);
  margin-top: 4px;
}
.nova-ac .nova-ac-all.on { background: var(--nova-ghost); }
@media (max-width: 767px) {
  /* the collapsed navbar gives the <li> no useful anchor, so pin to the viewport */
  .nova-ac { position: fixed; left: 16px; right: 16px; width: auto; top: 128px; }
  /* At 358px the thumbnail, name, code, price and Add all competed on one line
     and the name collapsed to 97px — the same squeeze that made desktop
     unreadable. Rather than dropping the item code (B2B buyers order by it),
     give the name its own row and put code + price underneath. */
  .nova-ac li:not(.nova-ac-all):not(.nova-ac-note) {
    display: grid;
    grid-template-columns: 34px auto minmax(0, 1fr) auto;
    grid-template-areas:
      "img name  name  add"
      "img code  price add";
    column-gap: 10px;
    row-gap: 3px;
    align-items: center;
  }
  .nova-ac img, .nova-ac .nova-ac-noimg { grid-area: img; align-self: start; }
  .nova-ac .nova-ac-t { grid-area: name; }
  .nova-ac .nova-ac-c { grid-area: code; justify-self: start; }
  .nova-ac .nova-ac-price { grid-area: price; justify-self: start; }
  .nova-ac .nova-ac-add { grid-area: add; }
}

/* ---- orders list: give the item names the room the other columns waste ----
   Measured on /orders at 1400px. The stock split is 4/3/2/3, which spends
   191px on a 95px status badge and 191px on a ~110px right-aligned total,
   while the item preview gets 127px for a string that needs 459px — so an
   order showed as "Cephacare f…" and you could not tell one from another.
   Re-split 26/15/37/22 (still 100%) and let the name use the two lines the
   110px-tall row already has spare.

   Scoped to .transaction-list-item because .col-sm-* are global Bootstrap
   classes, and held behind Bootstrap's own 576px breakpoint so the stacked
   phone layout — where each column is full width — is untouched. */
@media (min-width: 576px) {
  .transaction-list-item .row > .col-sm-4 { flex: 0 0 26%; max-width: 26%; }
  .transaction-list-item .row > .col-sm-3 { flex: 0 0 15%; max-width: 15%; }
  .transaction-list-item .row > .col-sm-2 { flex: 0 0 37%; max-width: 37%; }
  .transaction-list-item .row > .col-sm-3.item-total { flex: 0 0 22%; max-width: 22%; }
}
/* .ellipsis pins this to one nowrap line; two lines fit the row as it stands */
.transaction-list-item .items-preview {
  white-space: normal;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* "Add a new address" broke onto two lines ("Add a new" / "address") in both
   the Shipping and Billing cards. Measured: the link needs 142px unwrapped and
   its slot is 152px on desktop but only 139px on a phone — so nowrap fixes the
   desktop break and would cause an overflow if applied at every width. */
@media (min-width: 768px) {
  .btn-new-address { white-space: nowrap; }
}

/* ---- accessibility: calm everything down when asked ----------------------
   This block MUST stay last in the file. A media query adds no specificity, so
   source order decides: while it sat mid-file, `.nova-hero-track` and
   `.nova-drawer` were defined ~300 lines below it and simply overrode it. The
   rules looked right and did nothing, measurably. */
@media (prefers-reduced-motion: reduce) {
  .item-card { animation: none; }
  .item-card .card, .card-img, .btn, .item-card .card::after { transition: none; }
  .nova-skel .ph { animation: none; }

  /* The rules above were written when only the listing moved. The home page and
     the cart drawer arrived later and were never covered — measured identical
     under `reduce` and normal. These two travel across the viewport, which is
     precisely what a vestibular-sensitive visitor asked to avoid: the hero
     slides a full width over 600ms, the drawer a full height over 280ms.
     (Auto-rotation is already off under reduce; this is the manual slide.) */
  .nova-hero-track,
  .nova-drawer { transition: none; }

  /* Everything else keeps its colour and shadow feedback and loses only the
     movement — suppressing feedback entirely makes the interface feel dead. */
  .nova-account-card, .nova-cat-tile, .nova-brand-tile, .nova-home-order,
  .nova-hero-dots button, .nova-hero-pause {
    transition-property: background-color, border-color, box-shadow, color;
  }
  .nova-account-card:hover, .nova-cat-tile:hover, .nova-brand-tile:hover,
  .nova-home-order:hover, .item-card .card:hover,
  .item-card .card:hover .card-img,
  .nova-hero-dots button.on { transform: none; }
  .btn:active, .nova-hero-pause:active { transform: none; }
  /* the ticks must still be VISIBLE under reduce, not merely unanimated:
     both pseudo-elements start hidden, so `animation: none` alone would
     leave the list with no tick marks at all. */
  .nova-login-points li::before { animation: none; opacity: 1; transform: none; }
  .nova-login-points li::after  { animation: none; clip-path: none; }
  body.nova-login .nova-login-card .btn-login:hover,
  body.nova-login .nova-login-card .btn-login:active { transform: none; }
}
