@charset "UTF-8";
/* @charset phải là byte ĐẦU TIÊN của file. Máy chủ phục vụ CSS bằng "text/css" không
   kèm charset (đo trên cả local lẫn production 12/08/2026), nên khi không có dòng này
   trình duyệt tự đoán mã hoá — và nó đoán Latin-1, biến "\2713" thành ba ký tự rác.
   Các ký tự hiển thị bên dưới còn được viết dạng escape ASCII cho chắc. */
/**
 * Storefront styles.
 *
 * Rules this file lives by, because it runs inside somebody else's theme:
 *  - every selector starts with .mecom-; no bare element selectors, ever.
 *  - !important on exactly three properties (box-sizing, float, list-style), which are the
 *    ones themes reset globally in a way that breaks any grid dropped into them.
 *  - layout reacts to CONTAINER width, not viewport width — this grid is as often in a
 *    narrow sidebar as across a full page, and a viewport media query gets that wrong.
 *  - colours come from custom properties so a theme can restyle without fighting us.
 *
 * .mecom-shell carries all of that: the custom properties AND the container-type. A template
 * override that drops the class — usually because the theme's header.php already opened the
 * <main> our template wanted to be — leaves every variable empty and no container query
 * firing, with nothing anywhere reporting an error. The element does not matter; a
 * <div class="mecom-shell"> works exactly as well. The class does.
 */

.mecom-shell {
  --mecom-ink: #1B1F2A;
  --mecom-mut: #6b7280;
  --mecom-line: #e6e8ef;
  --mecom-paper: #F5F6FA;
  --mecom-cta: #F79009;
  --mecom-sale: #E23164;
  --mecom-ok: #0f7a3d;
  --mecom-radius: 14px;
  --mecom-cols: 4;

  container-type: inline-size;
  color: var(--mecom-ink);
}

.mecom-shell *,
.mecom-shell *::before,
.mecom-shell *::after { box-sizing: border-box !important; }

/* ── grids ── */

.mecom-grid-products,
.mecom-grid-cats {
  display: grid;
  grid-template-columns: repeat(var(--mecom-cols), minmax(0, 1fr));
  gap: 22px;
  margin: 0 0 28px;
  padding: 0;
  list-style: none !important;
}

@container (max-width: 900px) {
  .mecom-grid-products,
  .mecom-grid-cats { grid-template-columns: repeat(min(var(--mecom-cols), 3), minmax(0, 1fr)); }
}
@container (max-width: 640px) {
  .mecom-grid-products,
  .mecom-grid-cats { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
}
@container (max-width: 380px) {
  .mecom-grid-products,
  .mecom-grid-cats { grid-template-columns: minmax(0, 1fr); }
}

/* Themes that predate container queries still get a sane layout from this fallback. */
@supports not (container-type: inline-size) {
  .mecom-grid-products,
  .mecom-grid-cats { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }
}

/* ── product card ── */

.mecom-card {
  display: flex;
  flex-direction: column;
  background: #fff;
  border: 1px solid var(--mecom-line);
  border-radius: var(--mecom-radius);
  overflow: hidden;
  float: none !important;
  transition: box-shadow .18s ease, transform .18s ease;
}
.mecom-card:hover { box-shadow: 0 10px 28px rgba(27, 31, 42, .09); transform: translateY(-2px); }
.mecom-card.is-out { opacity: .72; }

.mecom-card__media {
  position: relative;
  display: block;
  aspect-ratio: 1 / 1;
  background: var(--mecom-paper);
  overflow: hidden;
}
.mecom-card__img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
.mecom-card__noimg {
  display: block; width: 100%; height: 100%;
  background: repeating-linear-gradient(45deg, #eef0f6 0 12px, #f6f7fb 12px 24px);
}

.mecom-flag {
  position: absolute; top: 10px; left: 10px;
  border-radius: 999px;
  padding: 3px 10px;
  font-size: 11.5px; font-weight: 700; line-height: 1.6;
  color: #fff;
}
.mecom-flag--sale { background: var(--mecom-sale); }
.mecom-flag--out  { background: #4b5563; }

.mecom-card__body {
  display: flex; flex-direction: column; gap: 8px;
  padding: 14px 14px 16px;
  flex: 1 1 auto;
}
.mecom-card__title { margin: 0; font-size: 15px; line-height: 1.45; font-weight: 600; }
.mecom-card__title a { color: inherit; text-decoration: none; }
.mecom-card__title a:hover { text-decoration: underline; }

/* ── price ── */

.mecom-price { margin: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; }
.mecom-price__now { font-size: 17px; font-weight: 800; color: var(--mecom-ink); font-variant-numeric: tabular-nums; }
.mecom-price__was { font-size: 13.5px; color: var(--mecom-mut); text-decoration: line-through; }

.mecom-availability { margin: 6px 0 0; font-size: 13.5px; font-weight: 600; }
.mecom-availability.is-in  { color: var(--mecom-ok); }
.mecom-availability.is-out { color: #b3123c; }

/* ── buttons ── */

.mecom-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  border: 1px solid transparent;
  border-radius: 10px;
  padding: 10px 16px;
  font-size: 14px; font-weight: 700; line-height: 1.2;
  text-decoration: none !important;
  cursor: pointer;
  margin-top: auto;
}
/* White on orange — the house CTA. Never ink on orange: it reads as low contrast. */
.mecom-btn--primary { background: var(--mecom-cta); color: #fff; }
.mecom-btn--primary:hover,
.mecom-btn--primary:focus { background: #e07f04; color: #fff; }
.mecom-btn--muted { background: var(--mecom-paper); color: var(--mecom-mut); cursor: not-allowed; }

.mecom-add { display: flex; gap: 10px; align-items: center; margin-top: auto; flex-wrap: wrap; }

/* The two buy controls travel together: on a variable product .mecom-add turns into a block
   so the selector can stack above, and without this pair the buttons would stack with it. */
.mecom-add__go { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }

/* "Mua ngay" sits BESIDE the primary, so it is outlined rather than filled — two solid
   orange buttons in one row leave nothing to say which is the ordinary action. Orange on
   white here, which is the pair that keeps its contrast; the filled button stays the one
   with white text on orange. */
.mecom-btn--buy { background: #fff; color: var(--mecom-cta); border-color: var(--mecom-cta); }
.mecom-btn--buy:hover,
.mecom-btn--buy:focus { background: #fff6e8; color: #c86f04; border-color: #c86f04; }
.mecom-qty {
  width: 78px; padding: 9px 10px;
  border: 1px solid var(--mecom-line); border-radius: 10px;
  font-size: 14px;
}

/* ── wishlist ─────────────────────────────────────────────────────────────────
   Two shapes from one control. On a card it is an icon over the picture, where a
   heart is looked for and where a label would crowd the title; beside the buy
   button it is a labelled pill, because there it is one of two things the page is
   asking the customer to do and an unlabelled icon does not say which.
   The label is never REMOVED in the icon form — it is visually hidden, so the
   button still announces itself. */

.mecom-wish { margin: 0; }
.mecom-wish__btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  border: 1px solid var(--mecom-line);
  background: #fff;
  color: var(--mecom-mut);
  cursor: pointer;
  text-decoration: none !important;
  transition: color .15s ease, border-color .15s ease, background .15s ease;
}
.mecom-wish__icon { width: 18px; height: 18px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.7; }
.mecom-wish__n { font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums; }

.mecom-wish__btn:hover,
.mecom-wish__btn:focus-visible { color: var(--mecom-sale); border-color: var(--mecom-sale); }

/* Saved: the heart fills. Colour alone would not survive a greyscale print or a
   colour-blind reader, so the shape carries the state too. */
.mecom-wish.is-on .mecom-wish__btn { color: var(--mecom-sale); border-color: var(--mecom-sale); }
.mecom-wish.is-on .mecom-wish__icon { fill: currentColor; }

/* — over a product card — */
.mecom-wish--card,
.mecom-wish--list { position: absolute; top: 10px; right: 10px; z-index: 2; }
.mecom-wish--card .mecom-wish__btn,
.mecom-wish--list .mecom-wish__btn {
  width: 36px; height: 36px; padding: 0;
  border-radius: 999px;
  background: rgba(255, 255, 255, .92);
  box-shadow: 0 2px 8px rgba(27, 31, 42, .12);
}
.mecom-wish--card .mecom-wish__label,
.mecom-wish--list .mecom-wish__label {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
/* No rule hides the counter on a card: the template does not render one there, because
   asking for it is a query per product. See parts/wishlist-button.php. */

/* The card is the positioning context for the heart. Declared here rather than on
   .mecom-card__media because the button is a SIBLING of that anchor, not a child —
   nesting a button inside it would be invalid and unreachable by keyboard. */
.mecom-card { position: relative; }

/* — beside the buy button — */
.mecom-single__buy { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.mecom-single__buy .mecom-add { margin-top: 0; }
.mecom-wish--single .mecom-wish__btn {
  border-radius: 10px;
  padding: 10px 16px;
  font-size: 14px; font-weight: 700; line-height: 1.2;
}

/* ── archive furniture ── */

.mecom-archive__head { margin: 0 0 20px; }
.mecom-archive__title { margin: 0 0 8px; font-size: clamp(24px, 4cqi, 34px); line-height: 1.2; }
.mecom-archive__desc { color: var(--mecom-mut); font-size: 14.5px; }

.mecom-toolbar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 12px 0 18px;
  border-bottom: 1px solid var(--mecom-line);
  margin-bottom: 22px;
}
.mecom-toolbar__label { font-size: 13px; font-weight: 700; color: var(--mecom-mut); }
.mecom-toolbar select {
  padding: 8px 12px;
  border: 1px solid var(--mecom-line); border-radius: 10px;
  background: #fff; font-size: 14px;
}

.mecom-pager { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 32px; }
.mecom-pager .page-numbers {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 38px; height: 38px; padding: 0 10px;
  border: 1px solid var(--mecom-line); border-radius: 10px;
  text-decoration: none !important; color: var(--mecom-ink);
  font-size: 14px; font-weight: 600;
}
.mecom-pager .page-numbers.current { background: var(--mecom-ink); color: #fff; border-color: var(--mecom-ink); }

.mecom-empty {
  margin: 24px 0; padding: 22px;
  background: var(--mecom-paper); border: 1px dashed var(--mecom-line); border-radius: var(--mecom-radius);
  color: var(--mecom-mut); font-size: 14.5px; text-align: center;
}

/* ── category card ── */

.mecom-cat {
  display: flex; flex-direction: column; gap: 4px;
  padding: 18px;
  background: var(--mecom-paper);
  border: 1px solid var(--mecom-line); border-radius: var(--mecom-radius);
  text-decoration: none !important; color: var(--mecom-ink);
}
.mecom-cat:hover { background: #eef0f6; }
.mecom-cat__name { font-weight: 700; font-size: 15px; }
.mecom-cat__count { font-size: 12.5px; color: var(--mecom-mut); }

/* ── single ── */

.mecom-crumbs { display: flex; gap: 8px; flex-wrap: wrap; font-size: 13px; color: var(--mecom-mut); margin: 0 0 18px; }
.mecom-crumbs a { color: inherit; text-decoration: none; }
.mecom-crumbs a:hover { text-decoration: underline; }

.mecom-single__top { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 34px; margin-bottom: 40px; }
@container (max-width: 780px) { .mecom-single__top { grid-template-columns: minmax(0, 1fr); gap: 22px; } }
@supports not (container-type: inline-size) {
  @media (max-width: 780px) { .mecom-single__top { grid-template-columns: minmax(0, 1fr); } }
}

.mecom-single__media { background: var(--mecom-paper); border-radius: var(--mecom-radius); overflow: hidden; aspect-ratio: 1 / 1; }
.mecom-single__img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* ── gallery ──
   The strip is a real row of links; the script only decides which of them are on screen.
   `hidden` is a user-agent rule and loses to any display rule of ours, so the pair below is
   mandatory — without it the thumbnails of every variant stay visible at once and the toggle
   reads as broken (rules/brand-ui.md). */

.mecom-gal { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.mecom-gal__thumbs {
  display: flex; flex-wrap: wrap; gap: 8px;
  margin: 0; padding: 0; list-style: none !important;
}
.mecom-gal__t { margin: 0; padding: 0; }
.mecom-gal__t[hidden] { display: none; }

.mecom-gal__link {
  position: relative; display: block; width: 68px; height: 68px;
  border: 2px solid var(--mecom-line); border-radius: 10px; overflow: hidden;
  background: var(--mecom-paper); text-decoration: none; cursor: pointer;
}
.mecom-gal__timg { width: 100%; height: 100%; object-fit: cover; display: block; }
.mecom-gal__link:hover { border-color: var(--mecom-mut); }
.mecom-gal__link:focus-visible { outline: 2px solid var(--mecom-cta); outline-offset: 2px; }

/* The chosen thumbnail carries a border AND a tick: a border colour alone is a state some
   readers cannot see, and this one has to survive a greyscale screen. */
.mecom-gal__link.is-current { border-color: var(--mecom-ink); }
.mecom-gal__link.is-current::after {
  content: "\2713";
  position: absolute; right: 0; bottom: 0;
  min-width: 18px; height: 18px; padding: 0 3px;
  display: flex; align-items: center; justify-content: center;
  font-size: 12px; line-height: 1; color: #fff; background: var(--mecom-ink);
  border-top-left-radius: 8px;
}

.mecom-single__info { display: flex; flex-direction: column; gap: 14px; align-items: flex-start; }
.mecom-single__title { margin: 0; font-size: clamp(22px, 3.4cqi, 32px); line-height: 1.25; }
.mecom-single__excerpt { color: var(--mecom-mut); font-size: 14.5px; line-height: 1.7; }
.mecom-single__desc { margin: 0 0 40px; }
.mecom-single__desc h2,
.mecom-related h2 { font-size: 19px; margin: 0 0 14px; padding-bottom: 10px; border-bottom: 1px solid var(--mecom-line); }
.mecom-sku { font-size: 13px; color: var(--mecom-mut); margin: 0; }
.mecom-sku span { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }


.mecom-related { margin-bottom: 40px; }

/* ── safe layout: one option flips this on when the schema is not healthy ── */

.mecom-safe .mecom-card,
.mecom-safe .mecom-cat { box-shadow: none; transform: none; }
.mecom-safe .mecom-grid-products,
.mecom-safe .mecom-grid-cats { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }

/* ── forms, cart, checkout ── */

.mecom-flash {
  margin: 0 0 18px; padding: 12px 16px;
  border-radius: 10px; border: 1px solid transparent;
  font-size: 14px; font-weight: 600;
}
.mecom-flash.is-ok   { background: #e8f6ee; color: #0f7a3d; border-color: #c7ead6; }
.mecom-flash.is-err  { background: #fdecef; color: #b3123c; border-color: #f8ccd7; }
.mecom-flash.is-warn { background: #fff5e6; color: #b26a00; border-color: #ffe1b3; }

.mecom-table { width: 100%; border-collapse: collapse; margin: 0 0 20px; }
.mecom-table th, .mecom-table td { padding: 12px 10px; text-align: left; border-bottom: 1px solid var(--mecom-line); vertical-align: middle; }
.mecom-table thead th { font-size: 12.5px; text-transform: uppercase; letter-spacing: .03em; color: var(--mecom-mut); }
.mecom-table__thumb { width: 72px; }
.mecom-table__thumb img { width: 64px; height: 64px; object-fit: cover; border-radius: 10px; display: block; }
.mecom-table__attrs { display: block; font-size: 12.5px; color: var(--mecom-mut); margin-top: 2px; }

.mecom-remove {
  border: 1px solid var(--mecom-line); background: #fff; color: var(--mecom-mut);
  width: 32px; height: 32px; border-radius: 8px; cursor: pointer;
  font-size: 18px; line-height: 1;
}
.mecom-remove:hover { border-color: var(--mecom-sale); color: var(--mecom-sale); }

.mecom-cart__foot { display: flex; flex-wrap: wrap; gap: 16px; align-items: center; justify-content: space-between; margin-bottom: 22px; }
.mecom-cart__total { display: flex; gap: 12px; align-items: baseline; font-size: 16px; }
.mecom-cart__total strong { font-size: 20px; font-variant-numeric: tabular-nums; }
.mecom-cart__go { margin: 0 0 28px; }

.mecom-field { display: flex; flex-direction: column; gap: 6px; margin: 0 0 16px; }
.mecom-field label { font-size: 13.5px; font-weight: 700; }
.mecom-field input, .mecom-field textarea, .mecom-field select {
  width: 100%; padding: 11px 13px;
  border: 1px solid var(--mecom-line); border-radius: 10px;
  font-size: 15px; background: #fff; color: var(--mecom-ink);
}
.mecom-field input:focus, .mecom-field textarea:focus { outline: 2px solid var(--mecom-cta); outline-offset: 1px; }
.mecom-field .mecom-hint { font-size: 12.5px; color: var(--mecom-mut); font-weight: 400; }
.mecom-req { color: var(--mecom-sale); }
.mecom-field-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
/* `display:grid` above beats the browser's own [hidden] { display:none }, which is a UA rule
   and therefore the weakest one in the cascade. Without this the hidden half of the address
   form stays on screen and the customer is shown both shapes at once. Any layout rule on an
   element that also gets toggled with the hidden attribute needs this pair. */
.mecom-field-row[hidden] { display: none; }
@container (max-width: 640px) { .mecom-field-row { grid-template-columns: minmax(0, 1fr); } }

/* Honeypot: off-screen rather than display:none, which some bots skip. */
.mecom-hp { position: absolute; left: -9999px; top: auto; width: 1px; height: 1px; overflow: hidden; }

.mecom-checkout__cols { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr); gap: 34px; }
@container (max-width: 860px) { .mecom-checkout__cols { grid-template-columns: minmax(0, 1fr); gap: 26px; } }
@supports not (container-type: inline-size) {
  @media (max-width: 860px) { .mecom-checkout__cols { grid-template-columns: minmax(0, 1fr); } }
}
.mecom-checkout__summary {
  background: var(--mecom-paper);
  border: 1px solid var(--mecom-line); border-radius: var(--mecom-radius);
  padding: 22px; align-self: start;
}
.mecom-checkout h2, .mecom-thanks h3 { font-size: 18px; margin: 0 0 16px; }

.mecom-summary { width: 100%; border-collapse: collapse; margin: 0 0 18px; }
.mecom-summary th, .mecom-summary td { padding: 9px 0; text-align: left; font-weight: 400; font-size: 14px; vertical-align: top; }
.mecom-summary td { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.mecom-summary tfoot th, .mecom-summary tfoot td { border-top: 1px solid var(--mecom-line); font-weight: 600; }
.mecom-summary__qty { color: var(--mecom-mut); }
.mecom-summary__total th, .mecom-summary__total td { font-size: 17px; font-weight: 800; }

.mecom-choices { border: 0; padding: 0; margin: 0 0 20px; }
.mecom-choices legend { font-size: 13.5px; font-weight: 700; margin-bottom: 10px; padding: 0; }
.mecom-choice {
  display: flex; align-items: center; gap: 11px;
  padding: 12px 14px; margin-bottom: 8px;
  background: #fff; border: 1px solid var(--mecom-line); border-radius: 10px;
  cursor: pointer; font-size: 14px;
}
.mecom-choice:has(input:checked) { border-color: var(--mecom-cta); box-shadow: 0 0 0 1px var(--mecom-cta) inset; }
/* The payment brand mark. Logos arrive at wildly different aspect ratios — a square MoMo tile
   next to a wide VNPAY lockup — so the BOX is fixed and the picture fits inside it: that is what
   makes a column of them line up. Never set both width and height on the image itself. */
.mecom-choice__logo {
  /* 52x32, not smaller. Two constraints set it: MoMo's brand guidance forbids drawing their
     mark below 40px in its square form, which is why the badge variant is used and why the box
     has to give it real height; and a wide vendor lockup (VNPAY's is 2.5:1) is illegible in
     anything narrower. */
  flex: 0 0 auto; width: 52px; height: 32px;
  object-fit: contain; object-position: center;
  /* Cancels the width/height ATTRIBUTES, which are there to reserve space before the file
     arrives. Without this the aspect-ratio they imply fights the box above. */
  max-width: 52px;
}
.mecom-choice__body { flex: 1 1 auto; display: flex; flex-direction: column; gap: 2px; }
.mecom-choice__title { font-weight: 600; }
.mecom-choice__meta { font-size: 12.5px; color: var(--mecom-mut); }
.mecom-choice__cost { font-weight: 700; font-variant-numeric: tabular-nums; }

.mecom-btn--block { width: 100%; padding: 14px 16px; font-size: 15px; }

/* ── thank-you + lookup ── */

.mecom-thanks__head { margin-bottom: 28px; }
.mecom-thanks__lead { font-size: 15.5px; }
.mecom-thanks__cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 34px; margin-bottom: 28px; }
@container (max-width: 780px) { .mecom-thanks__cols { grid-template-columns: minmax(0, 1fr); gap: 24px; } }
.mecom-address { font-style: normal; line-height: 1.7; font-size: 14.5px; }

.mecom-pill { display: inline-block; border-radius: 999px; padding: 3px 12px; font-size: 12.5px; font-weight: 700; }
.mecom-pill--ok   { background: #e8f6ee; color: #0f7a3d; }
.mecom-pill--go   { background: #e8f0fd; color: #2E4EA8; }
.mecom-pill--wait { background: #fff5e6; color: #b26a00; }
.mecom-pill--bad  { background: #fdecef; color: #b3123c; }

.mecom-bank { margin-top: 18px; padding: 18px; background: var(--mecom-paper); border-radius: var(--mecom-radius); }
.mecom-bank h3 { margin: 0 0 14px; font-size: 16px; }
.mecom-bank__grid { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 22px; align-items: start; }
@container (max-width: 620px) { .mecom-bank__grid { grid-template-columns: minmax(0, 1fr); } }
.mecom-bank__info { display: grid; grid-template-columns: auto 1fr; gap: 6px 16px; margin: 0; font-size: 14px; }
.mecom-bank__info dt { color: var(--mecom-mut); }
.mecom-bank__info dd { margin: 0; font-weight: 600; }
.mecom-bank__qr { margin: 0; text-align: center; }
.mecom-bank__qr img { max-width: 260px; height: auto; border-radius: 10px; background: #fff; }
.mecom-bank__qr figcaption { font-size: 12px; color: var(--mecom-mut); margin-top: 6px; max-width: 260px; }

.mecom-lookup__form { max-width: 420px; margin-bottom: 24px; }
.mecom-lookup__result { padding: 20px; background: var(--mecom-paper); border-radius: var(--mecom-radius); }

.mecom-mini { display: inline-flex; align-items: center; gap: 8px; text-decoration: none !important; color: inherit; font-size: 14px; }
.mecom-mini__count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 22px; height: 22px; padding: 0 6px;
  border-radius: 999px; background: var(--mecom-cta); color: #fff;
  font-size: 12px; font-weight: 700;
}
.mecom-mini__total { font-weight: 700; font-variant-numeric: tabular-nums; }

.mecom-pay-note { font-size: 14px; color: var(--mecom-mut); }

@container (max-width: 620px) {
  .mecom-table thead { position: absolute; left: -9999px; }
  .mecom-table tr { display: grid; grid-template-columns: 72px 1fr; gap: 4px 12px; padding: 12px 0; border-bottom: 1px solid var(--mecom-line); }
  .mecom-table td, .mecom-table th { border: 0; padding: 2px 0; }
  .mecom-table td[data-label]::before { content: attr(data-label) ": "; color: var(--mecom-mut); font-size: 12.5px; }
}

/* ── variant selector ── */
.mecom-add--variable { display: block; }
.mecom-vpick { display: grid; gap: 12px; margin: 0 0 14px; max-width: 420px; }
.mecom-vpick__field { margin: 0; }
.mecom-vpick__state { margin: 0; font-size: 13px; color: var(--mecom-mut); min-height: 1.2em; }

/* Swatch groups (display types button / color / image).
   Wider than the select column: a row of buttons needs room the way a dropdown does not. */
.mecom-vpick:has(.mecom-vpick__group) { max-width: 560px; }
.mecom-vpick__group { margin: 0; padding: 0; border: 0; min-width: 0; }
.mecom-vpick__legend { padding: 0; margin: 0 0 7px; font-size: 13px; font-weight: 600; color: var(--mecom-ink); }
.mecom-swatches { display: flex; flex-wrap: wrap; gap: 8px; }

/* The radio is the control; the label is its coat. Kept in the DOM and focusable — this is
   what a keyboard moves between, what a screen reader announces, and what submits when the
   page has no scripting at all. Never display:none. */
.mecom-swatch { position: relative; display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 13px 7px 8px; border: 1px solid var(--mecom-line); border-radius: 999px;
  background: #fff; color: var(--mecom-ink); font-size: 13.5px; line-height: 1.35;
  cursor: pointer; transition: border-color .12s, box-shadow .12s, background .12s; }
.mecom-swatch--button { padding-left: 13px; }
.mecom-swatch input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }
.mecom-swatch:hover { border-color: #c9cfdd; background: var(--mecom-paper); }
.mecom-swatch__t { white-space: nowrap; }
.mecom-swatch__dot { flex: none; width: 22px; height: 22px; border-radius: 50%;
  background: #fff; border: 1px solid rgba(27, 31, 42, .18); }
.mecom-swatch__img { flex: none; width: 26px; height: 26px; border-radius: 50%;
  object-fit: cover; border: 1px solid var(--mecom-line); }
/* No colour or picture set for this value yet. Hatched, so it does not pass for white. */
.mecom-swatch__dot.is-unset { border-style: dashed; border-color: #c3c8d4;
  background: repeating-linear-gradient(45deg, #f4f5f9 0 4px, #e9ebf2 4px 8px); }

/* Chosen state carries THREE signals — a ring, a weight change and a tick — because a shop
   selling twelve shades of the same colour cannot rely on any one of them, and none of them
   may be the swatch's own colour. */
.mecom-swatch:has(input:checked) { border-color: var(--mecom-ink);
  box-shadow: 0 0 0 2px var(--mecom-ink) inset, 0 1px 3px rgba(20, 24, 40, .12);
  font-weight: 700; }
.mecom-swatch:has(input:checked)::after { content: "\2713"; margin-left: 1px; font-size: 12px;
  line-height: 1; color: var(--mecom-ink); }
.mecom-swatch:has(input:focus-visible) { outline: 2px solid var(--mecom-cta); outline-offset: 2px; }

/* Fallbacks for a browser without :has(). The tick is the part that cannot be expressed
   this way, so the ring and the weight carry the state on their own there. */
.mecom-swatch input:checked ~ .mecom-swatch__t { font-weight: 700; }
.mecom-swatch input:focus-visible ~ .mecom-swatch__t { text-decoration: underline; }

@media (prefers-reduced-motion: reduce) { .mecom-swatch { transition: none; } }
.mecom-add--variable .mecom-qty { margin-right: 10px; }
.mecom-btn.is-loading { opacity: .7; pointer-events: none; }
.mecom-btn:disabled { opacity: .5; cursor: not-allowed; }

/* Re-quoting: keep the old numbers visible and dim them, rather than blanking the panel —
   a summary that empties itself while you pick a province reads as a failure. */
[data-mecom-summary] { transition: opacity .15s ease; }
[data-mecom-summary].is-reloading { opacity: .55; pointer-events: none; }
.mecom-field-row--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@container (max-width: 640px) { .mecom-field-row--2 { grid-template-columns: minmax(0, 1fr); } }

/* ── coupon ── */
.mecom-coupon { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin: 0 0 22px; }
.mecom-coupon label { font-size: 13.5px; font-weight: 700; }
.mecom-coupon input[type="text"] {
  width: 190px; padding: 9px 12px;
  border: 1px solid var(--mecom-line); border-radius: 10px; font-size: 14px;
}
.mecom-coupon__on { margin: 0; display: flex; flex-wrap: wrap; gap: 10px; align-items: center; font-size: 14px; }
/* Its own line above the code box, which stays available: a code the shopper was given
   still outranks an offer the shop applies by itself. */
.mecom-coupon__auto {
  flex: 0 0 100%; margin: 0; font-size: 14px; color: var(--mecom-ok);
}
.mecom-coupon code {
  background: var(--mecom-paper); padding: 2px 8px; border-radius: 6px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px;
}
.mecom-btn--link {
  background: none; border: 0; padding: 0; margin: 0;
  color: var(--mecom-sale); font-size: 13px; font-weight: 600;
  text-decoration: underline !important; cursor: pointer;
}
.mecom-summary__disc td { color: var(--mecom-ok); }

/* Several validation failures list as several lines. Run together in one paragraph a
   customer reads the first clause, fixes it, submits, and meets the next one. */
.mecom-flash__list{margin:0;padding-left:20px;list-style:disc}
.mecom-flash__list li+li{margin-top:4px}
.mecom-flash p{margin:0}

/* The manual ward field sits under its select, and is toggled with the hidden attribute —
   which needs the same specificity pair as .mecom-field-row, or a display rule beats it. */
.mecom-ward-other { margin-top: 8px; }
.mecom-ward-other[hidden] { display: none; }

/* ── where the shop sells ────────────────────────────────────────────────────
   A shop with one selling country states it rather than asking a question with a single
   answer, so the country line is a value and a note instead of a select. */
.mecom-field .mecom-label { font-size: 13.5px; font-weight: 700; }
.mecom-locked { font-size: 15px; font-weight: 600; padding: 2px 0; }
.mecom-locked[hidden] { display: none; }

/* Per-field errors carry the same sentence the server raised, under the field that caused
   it — a list at the top of a long form says something was wrong, not which input. Empty
   means "nothing to say", and the [hidden] pair is the specificity trap noted above: a
   display rule of ours beats the browser's own [hidden] { display: none }. */
.mecom-field-err { color: var(--mecom-sale); font-size: 12.5px; font-weight: 600; }
/* Same slot under a field, but a statement of fact rather than a refusal — an address waiting
   to be confirmed is not an error, and colouring it like one says the save failed. */
.mecom-field-note { display: block; margin-top: 4px; color: var(--mecom-mut, #6b7280); font-size: 12.5px; line-height: 1.5; }
.mecom-field-err:empty { display: none; }
.mecom-field-err[hidden] { display: none; }
.mecom-field input.is-invalid,
.mecom-field select.is-invalid,
.mecom-field textarea.is-invalid { border-color: var(--mecom-sale); }

/* ── ratings and reviews ─────────────────────────────────────────────────────
   Two stacked rows of glyphs, the top one clipped by width, so an average of 4,3 looks like
   4,3 instead of being rounded to a whole star. The numbers behind this come from the browse
   index (MECOM_Review::summary), which is why a grid of cards can afford to print them. */
.mecom-stars { position: relative; display: inline-block; white-space: nowrap;
  font-size: 15px; line-height: 1; letter-spacing: 1px; color: #d6dae3; }
.mecom-stars__off { display: block; }
.mecom-stars__on { position: absolute; inset: 0 auto 0 0; overflow: hidden;
  color: var(--mecom-cta); display: block; }

.mecom-rating { margin: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 7px;
  font-size: 13.5px; color: var(--mecom-mut); }
.mecom-rating--sm { font-size: 12.5px; }
.mecom-rating--sm .mecom-stars { font-size: 13px; }
.mecom-rating__avg { font-weight: 700; color: var(--mecom-ink); font-variant-numeric: tabular-nums; }
.mecom-rating--link { text-decoration: none !important; color: var(--mecom-mut); }
.mecom-rating--link:hover .mecom-rating__n { text-decoration: underline; }

.mecom-reviews { margin: 34px 0 0; }
.mecom-reviews > h2 { margin: 0 0 16px; font-size: 20px; }

/* The average, and how the scores are spread. The bars answer what one number cannot:
   whether a 4,2 is everybody agreeing or a fight between 5s and 1s. */
.mecom-revsum { display: flex; flex-wrap: wrap; gap: 26px; align-items: center;
  padding: 18px 20px; margin: 0 0 22px;
  background: var(--mecom-paper); border: 1px solid var(--mecom-line);
  border-radius: var(--mecom-radius); }
.mecom-revsum__score { display: flex; flex-direction: column; gap: 4px; align-items: flex-start; }
.mecom-revsum__avg { font-size: 34px; font-weight: 800; line-height: 1;
  font-variant-numeric: tabular-nums; }
.mecom-revsum__score .mecom-stars { font-size: 18px; }
.mecom-revsum__n { font-size: 13px; color: var(--mecom-mut); }

.mecom-revbars { flex: 1 1 240px; min-width: 200px; margin: 0; padding: 0; list-style: none !important; }
.mecom-revbars__row { display: flex; align-items: center; gap: 10px; font-size: 12.5px;
  color: var(--mecom-mut); }
.mecom-revbars__k { flex: none; width: 52px; }
.mecom-revbars__n { flex: none; width: 34px; text-align: right; font-variant-numeric: tabular-nums; }
.mecom-revbars__track { flex: 1 1 auto; height: 8px; border-radius: 999px; background: #e4e7ef;
  overflow: hidden; }
.mecom-revbars__fill { display: block; height: 100%; background: var(--mecom-cta); }

.mecom-revlist { margin: 0 0 20px; padding: 0; list-style: none !important; }
.mecom-rev { padding: 16px 0; border-top: 1px solid var(--mecom-line); }
.mecom-rev:first-child { border-top: 0; padding-top: 0; }
.mecom-rev__head { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-bottom: 4px; }
.mecom-rev__who { font-weight: 700; font-size: 14.5px; }
.mecom-rev__when { font-size: 12.5px; color: var(--mecom-mut); }
.mecom-rev__badge { font-size: 11.5px; font-weight: 700; padding: 2px 9px; border-radius: 999px;
  background: #e8f6ee; color: var(--mecom-ok); border: 1px solid #c7ead6; }
.mecom-rev__body { margin-top: 6px; font-size: 14.5px; }
.mecom-rev__body p { margin: 0 0 8px; }
.mecom-rev__body p:last-child { margin-bottom: 0; }

/* What the customer photographed. A row of square tiles that wraps: the evidence is the point,
   so the tiles are big enough to read at a glance and small enough that six of them do not push
   the next review off the screen. A clip gets a wider tile because a video cropped to a square
   usually loses whatever it was showing. */
.mecom-rev__media { display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0 0; padding: 0;
  list-style: none !important; }
.mecom-rev__m { width: 84px; height: 84px; border-radius: 10px; overflow: hidden;
  border: 1px solid var(--mecom-line); background: var(--mecom-paper); }
.mecom-rev__m--video { width: 148px; }
.mecom-rev__m > a { display: block; width: 100%; height: 100%; text-decoration: none !important; }
.mecom-rev__m img, .mecom-rev__m video { display: block; width: 100%; height: 100%;
  object-fit: cover; background: #000; }

/* The shop answering in public. Indented and tinted so it reads as a reply and not as a
   second review. */
.mecom-rev__reply { margin: 12px 0 0 22px; padding: 12px 14px; border-radius: 10px;
  background: var(--mecom-paper); border-left: 3px solid var(--mecom-cta); }

.mecom-revpage { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 24px; }
.mecom-revpage a, .mecom-revpage__at { min-width: 34px; padding: 6px 10px; text-align: center;
  border: 1px solid var(--mecom-line); border-radius: 9px; font-size: 13.5px;
  text-decoration: none !important; color: var(--mecom-ink); }
.mecom-revpage__at { background: var(--mecom-ink); color: #fff; border-color: var(--mecom-ink); }

.mecom-revform { max-width: 620px; padding: 20px; border: 1px solid var(--mecom-line);
  border-radius: var(--mecom-radius); }
.mecom-revform h3 { margin: 0 0 14px; font-size: 16px; }
.mecom-revform .mecom-vpick__group { margin: 0 0 16px; }

/* The star picker, built like the variant swatches: the radio is the control, kept in the DOM,
   never display:none. DOM order is 1 → 5 so the arrow keys move up the scale. */
.mecom-revstars { display: flex; gap: 2px; }
.mecom-revstar { position: relative; display: inline-flex; cursor: pointer;
  font-size: 30px; line-height: 1; color: #d6dae3; transition: color .1s; }
.mecom-revstar input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }
.mecom-revstar__t { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* Fill every star up to the chosen one: the star holding the checked radio, plus every star
   that has a LATER sibling holding it. Same `:has()` the swatches use for their checked ring.
   Verified by screenshot, not by reading computed styles — the fill repaints on the click. */
.mecom-revstar:has(input:checked),
.mecom-revstar:has(~ .mecom-revstar input:checked) { color: var(--mecom-cta); }
/* Without :has(), the chosen star alone carries the state. The cumulative fill cannot be
   expressed in natural DOM order there, and a picker showing no answer would be worse. */
.mecom-revstar input:checked ~ .mecom-revstar__s { color: var(--mecom-cta); }

/* Hover preview, same shape, a lighter shade so "what I would pick" never reads as "picked". */
.mecom-revstar:hover,
.mecom-revstar:has(~ .mecom-revstar:hover) { color: #f0b429; }

.mecom-revstar:has(input:focus-visible) { outline: 2px solid var(--mecom-cta); outline-offset: 2px; }
.mecom-revstar input:focus-visible ~ .mecom-revstar__s { text-decoration: underline; }

/* A plain notice line, and the standalone hint: .mecom-hint is otherwise only styled inside
   .mecom-field, and the review form prints one outside any field. */
.mecom-note { margin: 0 0 14px; font-size: 13.5px; color: var(--mecom-mut); }
.mecom-reviews .mecom-hint { display: block; font-size: 12.5px; color: var(--mecom-mut); }

/* Picking files, and seeing what was picked.
   The tile is the same size as the one the review will print, so the preview is a preview and
   not a differently-shaped placeholder. The file name sits under the picture because the error
   messages name a file, and the customer has to be able to tell which tile they mean. */
.mecom-field-media input[type="file"] { font-size: 13.5px; }
.mecom-revmedia__preview { display: flex; flex-wrap: wrap; gap: 8px; }
.mecom-revmedia__preview:empty { display: none; }
.mecom-revmedia__item { position: relative; width: 84px; }
.mecom-revmedia__item img, .mecom-revmedia__item video { display: block; width: 84px; height: 84px;
  object-fit: cover; border-radius: 10px; border: 1px solid var(--mecom-line); background: #000; }
.mecom-revmedia__name { display: block; margin-top: 3px; font-size: 11px; line-height: 1.3;
  color: var(--mecom-mut); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The remove control sits on the tile. Solid, not translucent: it is printed over a photograph
   nobody chose, so a tint would leave it unreadable on about half of them. */
.mecom-revmedia__x { position: absolute; top: 4px; right: 4px; width: 22px; height: 22px;
  padding: 0; border: 0; border-radius: 999px; cursor: pointer;
  background: var(--mecom-ink); color: #fff; font-size: 15px; line-height: 22px; }
.mecom-revmedia__x:hover { background: var(--mecom-sale); }
.mecom-revmedia__x:focus-visible { outline: 2px solid var(--mecom-cta); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) { .mecom-revstar { transition: none; } }

/* ── live: nothing on this storefront reloads ────────────────────────────────
   Two states, and the difference is the point (rules/admin-ux.md). A MUTATION keeps the rows
   where they are and dims them under a progress bar, because what is arriving is the same list
   with one line changed. A NAVIGATION — sorting, paging — swaps in a skeleton, because the rows
   are a different set and there is nothing worth reading while they come. */

.mecom-cart, .mecom-checkout, .mecom-archive, .mecom-reviews { position: relative; }

.is-reloading { pointer-events: none; }
.is-reloading > *:not(.mecom-flash) { opacity: .55; transition: opacity .12s ease; }
.is-reloading::after,
.is-navigating::after {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 3px; border-radius: 3px;
  background: linear-gradient(100deg, #2E4EA8, #3B82D6 27%, #963FDA 54%, #E23164 77%, #F79009);
  background-size: 220% 100%;
  animation: mecom-livebar 1.1s ease-in-out infinite;
}
@keyframes mecom-livebar { 0% { background-position: 100% 0 } 100% { background-position: 0 0 } }

/* Skeletons. Shaped like the thing they stand in for — a card skeleton with a card's media box
   and a card's button, at the grid's own column count — so the page does not jump when the real
   rows land. */
.mecom-skel {
  display: block; border-radius: 8px; background: #eef0f6;
  background-image: linear-gradient(90deg, #eef0f6 0, #f7f8fb 40px, #eef0f6 80px);
  background-size: 600px 100%; animation: mecom-shimmer 1.2s infinite linear;
}
@keyframes mecom-shimmer { to { background-position: 600px 0 } }
.mecom-skel--media { aspect-ratio: 1 / 1; width: 100%; border-radius: var(--mecom-radius) var(--mecom-radius) 0 0; }
.mecom-skel--line  { height: 14px; margin: 10px 14px 0; }
.mecom-skel--short { width: 55%; }
.mecom-skel--btn   { height: 38px; margin: 14px; border-radius: 10px; }

.mecom-card--skel { border: 1px solid var(--mecom-line); border-radius: var(--mecom-radius);
  overflow: hidden; background: #fff; }
.mecom-rev--skel { padding: 16px 0; border-top: 1px solid var(--mecom-line); }
.mecom-rev--skel .mecom-skel--line { margin-left: 0; margin-right: 0; }

/* A button mid-action says so, and cannot be pressed twice. */
.mecom-btn.is-loading::before {
  content: ""; display: inline-block; width: 13px; height: 13px; margin-right: 7px;
  vertical-align: -2px; border: 2px solid rgba(255, 255, 255, .45);
  border-top-color: currentColor; border-radius: 50%;
  animation: mecom-spin .6s linear infinite;
}
@keyframes mecom-spin { to { transform: rotate(360deg) } }
.mecom-remove.is-loading { opacity: .55; pointer-events: none; }

/* The toast. Adding to cart from a card happens nowhere near a part of the page that could hold
   a message, which is the whole reason this exists. */
.mecom-toasts {
  position: fixed; z-index: 99999; right: 18px; bottom: 18px;
  display: flex; flex-direction: column; gap: 8px; max-width: min(360px, calc(100vw - 36px));
}
.mecom-toast {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 12px 16px; border-radius: 11px; border: 1px solid transparent;
  font-size: 14px; font-weight: 600; line-height: 1.45;
  background: #fff; color: var(--mecom-ink, #1B1F2A);
  box-shadow: 0 8px 24px rgba(20, 24, 40, .16);
  opacity: 0; transform: translateY(8px); transition: opacity .22s ease, transform .22s ease;
}
.mecom-toast.is-in { opacity: 1; transform: none; }
/* The message closes itself; this is for the reader who has finished before the clock has.
   `currentColor` so it stays legible on both the green and the red ground. */
.mecom-toast__m { flex: 1 1 auto; }
.mecom-toast__x {
  flex: 0 0 auto; margin: -2px -4px 0 0; padding: 0 2px; line-height: 1;
  background: none; border: 0; cursor: pointer; font-size: 17px;
  color: currentColor; opacity: .55;
}
.mecom-toast__x:hover, .mecom-toast__x:focus-visible { opacity: 1; }
.mecom-toast.is-ok  { background: #e8f6ee; color: #0f7a3d; border-color: #c7ead6; }
.mecom-toast.is-err { background: #fdecef; color: #b3123c; border-color: #f8ccd7; }

@media (prefers-reduced-motion: reduce) {
  .mecom-skel { animation: none }
  .mecom-toast { transition: none; opacity: 1; transform: none }
  .is-reloading::after, .is-navigating::after { animation: none }
}

/* Lời mời đăng nhập ở đầu trang thanh toán. Ở shop.css chứ không phải account.css: trang
   thanh toán không nạp sheet của khu tài khoản, nên để nhầm chỗ là luật không bao giờ áp. */
.mecom-checkout__signin {
  margin: 0 0 18px;
  padding: 12px 16px;
  border: 1px dashed var(--mecom-line);
  border-radius: 12px;
  background: var(--mecom-paper);
  font-size: 13.5px;
}

/* ── currency switcher ── */
/* A real form, so it works with scripting off. When a script is present it submits on change
   and the button is hidden — but the button is what the no-JS path needs, so it is present in
   the markup and removed by the script, never the other way round. */
.mecom-currency{display:inline-flex;align-items:center;gap:8px;flex-wrap:wrap}
.mecom-currency__label{font-size:13px;color:var(--mecom-mut,#5b6472)}
select.mecom-currency__pick,.mecom-currency__pick{width:auto;min-width:96px;padding:7px 30px 7px 10px;
  font-size:13.5px;line-height:1.4;height:auto;min-height:36px;box-sizing:border-box}
.mecom-currency__go{padding:7px 14px;font-size:13px}
.mecom-currency.is-live .mecom-currency__go{display:none}

/* The shipping method's name, under the word "Vận chuyển" in an order summary. A shop with one
   option never sees it; a shop offering a carrier's eight services needs it, or the customer has
   no way to check they got the service they picked. */
.mecom-sum__sub{display:block;margin-top:2px;font-size:12px;font-weight:400;color:var(--mecom-mut,#6b7280)}
