/* ============================================================
   Hoàng Sao — core primitives

   THE THEME OWNS THIS FILE. build.sh copies it into
   hoangsao-widgets/assets/css/fallback/core.css — same one-source rule as
   tokens.css. Registered as `hoangsao-core` on both sides behind a
   wp_style_is() guard.

   What belongs here: anything a WIDGET also needs. Reset, container, buttons,
   eyebrow, chips, forms, modal, the colour-control variable map. Everything
   theme-only (chrome, page shells, article rail) lives in theme.css.

   ── EVERY CLASS IS NAMESPACED `hs-`, AND THAT IS NOT COSMETIC ──
   The prototype used bare `.btn`, `.p`, `.b`, `.t`, `.cat`, `.tag`, `.price`,
   `.stars`. In a static file that is fine. On this site those would collide
   with WooCommerce (`.price`, `.stars`, `.button`), Elementor, Essential
   Addons, YITH Catalog Mode and YITH Colour Variations — all confirmed active.
   A bare `.p { background: var(--hs-card) }` would repaint paragraphs inside
   every third-party widget on the site. The prototype→theme class map is in
   docs/class-map.md.

   ── THE box-sizing RESET IS LOAD-BEARING ──
   Slides set height:100dvh and then add their own padding. With content-box
   every slide overflows the viewport and the full-page hero stops being
   full-page. The widgets must not depend on a reset that ships in a different
   package, which is the other reason this file is mirrored into the plugin.
   ============================================================ */

*,
*::before,
*::after{box-sizing:border-box}

/* ---------- Document ---------- */
html{scroll-behavior:smooth}
@media(prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
}

/* Cormorant-style oldstyle figures are not a risk with Be Vietnam Pro, but
   declaring lining figures costs nothing and inherits, so a future display
   face swap cannot silently ship digits that hang below the baseline.
   font-variant-numeric, NEVER font-feature-settings:"lnum" — the low-level
   property resets every OpenType feature not named in it, so a later author
   using it elsewhere silently loses kerning and ligatures. */
html{font-variant-numeric:lining-nums}

body{
  margin:0;
  background:var(--hs-bg);
  color:var(--hs-text);
  font-family:var(--hs-font);
  font-size:16px;
  line-height:1.65;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
}

img{max-width:100%;height:auto;display:block}
a{color:inherit;text-decoration:none}
button{font-family:inherit}

/* ---------- Container ---------- */
.hs-wrap{
  width:100%;
  max-width:var(--hs-maxw);
  margin-inline:auto;
  padding-inline:var(--hs-gutter);
}

/* A NESTED wrap must not pad or cap again.
 *
 * A wrap's whole job is to centre the page's content column and hold it off the
 * viewport edge. Nesting one inside another applies the gutter twice, and the inner
 * column silently comes out 2 × --hs-gutter narrower than every other band on the
 * page — which is a misalignment, not an obvious bug.
 *
 * Found on the product page: the "Đặt mua" section is emitted by
 * inc/woocommerce.php on a WooCommerce hook, so it renders INSIDE `div.product`,
 * which is already inside the template's wrap. Its own
 * `<div class="hs-wrap hs-pdp-order__grid">` therefore resolved to 1184px of content
 * instead of 1232px, and its two columns measured 572px against the 596px of the
 * gallery/summary grid directly above them and the related-products grid directly
 * below. Measured at a 1440px viewport, prototype/product.html for comparison:
 *
 *   div.product grid       596px 596px   ← the reference
 *   #order grid (before)   572px 572px   ← 24px short on each side
 *   #order grid (after)    596px 596px
 *
 * Fixed here rather than by deleting `hs-wrap` from that one div, because a section
 * added on a hook cannot know whether its caller already wrapped it — that is
 * precisely the assumption that produced the bug. Audited across the home page, the
 * shop archive, the sponsors page and the product page: this was the only nested
 * wrap on the site, so nothing was relying on the double gutter.
 */
.hs-wrap .hs-wrap{
  max-width:none;
  padding-inline:0;
}

/* ---------- Headings ---------- */
.hs-h1,.hs-h2,.hs-h3,.hs-h4{
  font-weight:800;
  line-height:1.08;
  letter-spacing:-.01em;
  margin:0;
}
.hs-h1{font-size:clamp(36px,6vw,64px)}
.hs-h2{font-size:clamp(28px,4.2vw,44px)}
.hs-h3{font-size:clamp(20px,2.6vw,24px)}
.hs-h4{font-size:18px}

/* ---------- Eyebrow ---------- */
.hs-eyebrow{
  display:block;
  font-weight:700;
  font-size:12px;
  letter-spacing:.22em;
  text-transform:uppercase;
  color:var(--hs-accent);
}

/* ---------- Buttons ----------
   `background:transparent` on the base is load-bearing. Every .hs-btn used to
   be an <a>, which has no default background, so a variant that only sets a
   colour worked by accident. A <button> carries the UA's `background:
   ButtonFace` — measured rgb(239,239,239), which against the ghost variant's
   cream text is about 1.05:1: a near-white pill with invisible text. Declaring
   it here fixes every present and future variant at once. */
.hs-btn{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  gap:9px;
  border:1px solid transparent;
  border-radius:999px;
  background:transparent;
  padding:13px 26px;
  font-family:var(--hs-font);
  font-weight:700;
  font-size:14px;
  line-height:1.2;
  letter-spacing:.01em;
  cursor:pointer;
  transition:background var(--hs-t),color var(--hs-t),border-color var(--hs-t),transform var(--hs-t);
}
.hs-btn--accent{background:var(--hs-accent);color:var(--hs-accent-ink)}
.hs-btn--accent:hover,
.hs-btn--accent:focus-visible{background:var(--hs-accent-2);transform:translateY(-2px)}

.hs-btn--ghost{border-color:#4a4f55;color:var(--hs-text)}
.hs-btn--ghost:hover,
.hs-btn--ghost:focus-visible{border-color:var(--hs-accent);color:var(--hs-accent)}

.hs-btn--block{width:100%}
.hs-btn:focus-visible{outline:2px solid var(--hs-accent-2);outline-offset:3px}
@media(prefers-reduced-motion:reduce){
  .hs-btn{transition:none}
  .hs-btn--accent:hover{transform:none}
}

/* ---------- Chips / filter pills ---------- */
.hs-chip{
  border:1px solid var(--hs-line);
  border-radius:999px;
  background:none;
  padding:8px 16px;
  font-family:var(--hs-font);
  font-size:13px;
  font-weight:600;
  color:var(--hs-muted);
  cursor:pointer;
  transition:border-color var(--hs-t),color var(--hs-t);
}
.hs-chip.is-on,
.hs-chip:hover{border-color:var(--hs-accent);color:var(--hs-accent)}

/* ---------- Badges ----------
   Three meanings, three colours. `--hs-red` for Hot is semantic, not a second
   accent — see tokens.css. */
.hs-badge{
  display:inline-block;
  padding:5px 10px;
  border-radius:999px;
  font-size:10px;
  font-weight:800;
  letter-spacing:.08em;
  line-height:1.5;
  text-transform:uppercase;
  white-space:nowrap;
}
.hs-badge--new{background:var(--hs-felt);color:#fff}
.hs-badge--hot{background:var(--hs-red);color:#fff}
.hs-badge--best{background:var(--hs-accent);color:var(--hs-accent-ink)}

/* ---------- Callout ---------- */
.hs-callout{
  background:var(--hs-surface);
  border:1px solid var(--hs-line);
  border-left:3px solid var(--hs-accent);
  border-radius:0 12px 12px 0;
  padding:18px 20px;
  margin:20px 0;
}

/* ---------- Forms ---------- */
.hs-form{
  background:var(--hs-card);
  border:1px solid var(--hs-line);
  border-radius:18px;
  padding:32px;
}
.hs-field{margin-bottom:16px}
.hs-field label{
  display:block;
  margin-bottom:7px;
  font-size:13px;
  font-weight:600;
  color:var(--hs-muted);
}
.hs-field input,
.hs-field select,
.hs-field textarea{
  width:100%;
  background:var(--hs-bg);
  border:1px solid var(--hs-line);
  /* 10px, not the 999px of a button. A pill-shaped text input reads as a
     search box and invites a single word; these take names and addresses. */
  border-radius:10px;
  padding:12px 14px;
  color:var(--hs-text);
  font-family:var(--hs-font);
  font-size:14px;
}
.hs-field input:focus,
.hs-field select:focus,
.hs-field textarea:focus{outline:0;border-color:var(--hs-accent)}
.hs-field-row{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px}
@media(max-width:640px){
  .hs-field-row{grid-template-columns:minmax(0,1fr)}
}

/* ---------- Modal ----------
   Used by the booking form and the sponsor detail sheets.

   CENTRED WHEN IT FITS, PINNED TO THE TOP WHEN IT DOES NOT. That combination is the
   whole problem here, and getting it wrong fails in one of two visible ways: a short
   sheet stuck against the header (which is what the sponsor modal did), or a tall
   sheet whose top is scrolled off the viewport with no way to reach it.

   The answer is `align-items:flex-start` on the overlay PLUS `margin:auto` on the
   box. Per the flexbox spec auto margins take priority over align-items when there
   is positive free space, and resolve to ZERO when free space is negative — so the
   box centres itself while it fits and falls back to the top when it does not. No
   rule here has to know the box's height.

   Measured at 1280x720 against a 266px sheet and a 1466px one:

     align-items   box margin   short sheet          tall sheet
     flex-start    0 auto       top=24, NOT centred   top=24   reachable
     center        0 auto       centred               top=-373 UNREACHABLE
     flex-start    auto         centred               top=24   reachable    <-- this

   The middle row is the trap, and it is worth being precise about why: the overlay
   scrolls, but scrollable overflow only extends DOWNWARD from the scroll origin.
   Anything a centring rule pushes above that origin is not merely off-screen, it is
   unreachable — scrollHeight measured 1117 against the 1514 the box needs, so 373px
   of it does not exist as far as scrolling is concerned.

   An earlier version of this comment blamed `margin:auto` for that clipping. It was
   `align-items:center` doing it; `margin:auto` is the fix, not the cause.

   The overlay owning the scroll (with JS locking the page behind it) is the other
   half: a tall form inside a non-scrolling overlay is unreachable below the fold. */
.hs-modal{
  position:fixed;
  inset:0;
  z-index:200;
  display:none;
  padding:24px 18px;
  overflow-y:auto;
  background:rgba(5,6,7,.82);
  -webkit-backdrop-filter:blur(5px);
  backdrop-filter:blur(5px);
}
.hs-modal.is-open{display:flex;align-items:flex-start;justify-content:center}
.hs-modal__box{
  position:relative;
  width:100%;
  max-width:620px;
  /* See the block comment above: `auto`, not `0 auto`. This is what centres it. */
  margin:auto;
  background:var(--hs-surface);
  border:1px solid var(--hs-line);
  border-radius:18px;
  padding:34px 30px 30px;
  box-shadow:var(--hs-shadow-modal);
}
.hs-modal__box--wide{max-width:860px}
.hs-modal__close{
  position:absolute;
  top:14px;
  right:14px;
  display:grid;
  place-items:center;
  width:38px;
  height:38px;
  padding:0;
  border:1px solid var(--hs-line);
  border-radius:999px;
  background:var(--hs-card);
  color:var(--hs-text);
  font-size:20px;
  line-height:1;
  cursor:pointer;
}
.hs-modal__close:hover,
.hs-modal__close:focus-visible{
  background:var(--hs-accent);
  border-color:var(--hs-accent);
  color:var(--hs-accent-ink);
  outline:0;
}
/* The form is already inside a card, so drop the second frame. */
.hs-modal__box .hs-form{background:none;border:0;border-radius:0;padding:0}

/* ---------- Screen-reader only ---------- */
.hs-sr-only,
.screen-reader-text{
  position:absolute!important;
  width:1px;height:1px;
  padding:0;margin:-1px;
  overflow:hidden;
  clip:rect(0,0,0,0);
  white-space:nowrap;
  border:0;
}
/* A skip link that stays invisible when focused is an accessibility failure
   dressed as a feature. */
.hs-skip-link:focus{
  position:fixed;
  top:8px;left:8px;
  z-index:300;
  width:auto;height:auto;
  clip:auto;
  padding:12px 20px;
  background:var(--hs-accent);
  color:var(--hs-accent-ink);
  border-radius:8px;
  font-weight:700;
}

/* ============================================================
   Widget colour controls — the variable map

   Every Hoàng Sao Elementor widget writes its colour controls as CSS custom
   properties onto {{WRAPPER}} (see Hoangsao_Widget_Base::hs_add_color_control),
   and this block is the ONE place a variable is mapped to a property.

   Why: it keeps the generated Elementor CSS tiny, it lets a single control
   restyle every element referencing the variable, and it makes an empty
   control mean "use the design system" rather than "paint it black".

   Every widget's root element must carry `.hs-w` or none of its colour
   controls will do anything.
   ============================================================ */
.hs-w{
  --hs-w-heading:var(--hs-text);
  --hs-w-body:var(--hs-muted);
  --hs-w-accent:var(--hs-accent);
  --hs-w-surface:var(--hs-card);
  --hs-w-border:var(--hs-line);
}
.hs-w .hs-w-heading{color:var(--hs-w-heading)}
.hs-w .hs-w-body{color:var(--hs-w-body)}
.hs-w .hs-w-accent{color:var(--hs-w-accent)}
.hs-w .hs-w-surface{background:var(--hs-w-surface)}
.hs-w .hs-w-bordered{border-color:var(--hs-w-border)}

/* ---------- Section rhythm ----------
 *
 * The prototype's `.block { padding: 84px 0 }`. Every content widget's root
 * carries `.hs-section` (see Hoangsao_Widget_Base::$hs_own_spacing) because the
 * seeded Elementor sections are written with zero padding — the rhythm lives here,
 * in version control, not in saved page data.
 *
 * ORDER MATTERS. The modifiers MUST come after the media queries.
 *
 * They used to sit above them, so `--flush-top`'s `padding-top:0` was beaten by
 * `@media(max-width:820px){.hs-section{padding:60px 0}}` — same specificity, later
 * in the file wins. Flush-top therefore worked on desktop and silently stopped
 * working on every tablet and phone, which is the width where a doubled 120px gap
 * is most obviously wrong.
 */
.hs-section{padding:84px 0}

@media(max-width:820px){
  .hs-section{padding:60px 0}
}
@media(max-width:560px){
  .hs-section{padding:44px 0}
}

/* Modifiers, after the media queries so they survive every breakpoint. */
.hs-section--tight{padding:56px 0}
.hs-section--flush-top{padding-top:0}
.hs-section--none{padding:0}

@media(max-width:820px){
  .hs-section--tight{padding:40px 0}
  .hs-section--flush-top{padding-top:0}
  .hs-section--none{padding:0}
}
@media(max-width:560px){
  .hs-section--tight{padding:32px 0}
  .hs-section--flush-top{padding-top:0}
  .hs-section--none{padding:0}
}

/* ---------- Section head ---------- */
.hs-sec-head{
  display:flex;
  align-items:flex-end;
  justify-content:space-between;
  gap:20px;
  flex-wrap:wrap;
  margin-bottom:32px;
}
.hs-sec-head__t{flex:1 1 420px;min-width:0}
.hs-sec-head .hs-eyebrow{margin-bottom:10px}
.hs-sec-head p{color:var(--hs-muted);font-size:15px;margin:10px 0 0;max-width:640px}

/* ---------- Brand chips: the prototype's `.chips` / `.chip` ----------
 *
 * SHARED, which is why they live here rather than in the widget's own stylesheet.
 * Two things render them: the plugin's product-grid widget
 * (Hoangsao_Product_Grid_Widget::hs_brand_chips) and the theme's shop-archive
 * filter bar (hoangsao_woo_shop_filters). core.css is the one stylesheet both
 * packages already load — the plugin declares `hoangsao-core` as a dependency and
 * build.sh syncs a fallback copy into it — so this is one definition, not two that
 * drift.
 *
 * They are LINKS, not buttons, so they need the text-decoration and line-height
 * reset a <button> came with for free.
 */
.hs-chips{
  display:flex;
  gap:10px;
  flex-wrap:wrap;
  margin-bottom:28px;
}

.hs-chip{
  border:1px solid var(--hs-w-border,var(--hs-line));
  border-radius:999px;
  padding:8px 16px;
  font-size:13px;
  font-weight:600;
  line-height:1.2;
  color:var(--hs-muted);
  background:none;
  text-decoration:none;
  white-space:nowrap;
  transition:color .18s ease,border-color .18s ease;
}

/* `is-on` and hover share one appearance, exactly as the prototype's
   `.chip.on,.chip:hover` does. */
.hs-chip:hover,
.hs-chip:focus-visible,
.hs-chip.is-on{
  border-color:var(--hs-w-accent,var(--hs-accent));
  color:var(--hs-w-accent,var(--hs-accent));
}

/* The chips sit under a `.hs-sec-head` that already carries 32px of bottom
   margin, which together with the chips' own 28px opens a 60px hole before the
   grid. Pull them up against the heading they belong to. */
.hs-sec-head + .hs-chips{margin-top:-8px}

@media(max-width:560px){
  /*
   * Scroll rather than wrap. Eight chips wrap to three rows on a phone and push
   * the first product card below the fold, which is the one thing this band
   * exists to show.
   */
  .hs-chips{
    flex-wrap:nowrap;
    overflow-x:auto;
    scrollbar-width:none;
    /* Bleed to the viewport edges so the row reads as scrollable. */
    margin-inline:calc(var(--hs-gutter) * -1);
    padding-inline:var(--hs-gutter);
  }
  .hs-chips::-webkit-scrollbar{display:none}
}

@media(prefers-reduced-motion:reduce){
  .hs-chip{transition:none}
}

/* ---------- Product card ----------
 *
 * SHARED, which is why it lives here rather than in the product-grid widget's own
 * stylesheet. FOUR things render this card now:
 *
 *   · the hs_product_grid widget          (home page, /ban-bida-rasson/)
 *   · the WooCommerce shop loop           (via woo.css, same visual language)
 *   · the sponsor modal                   (hs_sponsor_grid)
 *   · the linked-products rail on a post  (single.php)
 *
 * core.css is the one stylesheet BOTH packages already load — the plugin declares
 * `hoangsao-core` as a dependency and build.sh syncs a fallback copy into it — so a
 * card rendered outside the product-grid widget is still styled. It was not: the
 * sponsor modal used a `.hs-mini` shape with its own CSS in TWO places (article.css
 * and sponsor-grid-widget.css), and on the sponsors page it read as an unstyled list
 * inside the modal's framed panel.
 *
 * `.hs-pgrid__grid` stays in the widget stylesheet. That is the widget's LAYOUT — the
 * column count comes from a widget control — and the modal supplies its own.
 *
 * Markup comes from hoangsao_render_product_card() in the theme's inc/template-tags.php.
 * One renderer, one stylesheet.
 */
.hs-pgrid__card{
  display:flex;
  flex-direction:column;
  overflow:hidden;
  background:var(--hs-w-surface,var(--hs-card));
  border:1px solid var(--hs-w-border,var(--hs-line));
  border-radius:16px;
  transition:border-color var(--hs-t),transform var(--hs-t);
}
.hs-pgrid__card:hover{border-color:#3a3f45;transform:translateY(-4px)}
.hs-pgrid__ph{
  position:relative;
  display:block;
  aspect-ratio:1/1;
  background:#0c0d0e;
  overflow:hidden;
}
.hs-pgrid__ph img{width:100%;height:100%;object-fit:cover}
/* Brand chip, top-left. Reads the brand from product_brand. */
.hs-pgrid__tag{
  position:absolute;
  top:10px;left:10px;
  z-index:2;
  padding:5px 9px;
  background:rgba(14,15,17,.82);
  border:1px solid var(--hs-line);
  border-radius:6px;
  font-size:10px;
  font-weight:700;
  letter-spacing:.06em;
  text-transform:uppercase;
}
/* Badge stack, top-right — opposite the brand chip so they never collide even
   when a product has both a long brand name and two badges. */
.hs-pgrid__badges{
  position:absolute;
  top:10px;right:10px;
  z-index:2;
  display:flex;
  flex-direction:column;
  align-items:flex-end;
  gap:6px;
}
.hs-pgrid__body{
  display:flex;
  flex-direction:column;
  flex:1;
  padding:16px;
}
.hs-pgrid__stars{
  font-size:12px;
  letter-spacing:1px;
  color:var(--hs-w-accent,var(--hs-accent));
}
.hs-pgrid__title{
  /* min-height keeps the price and button aligned across a row when one title
     wraps to two lines and its neighbour does not. */
  min-height:2.6em;
  margin:6px 0;
  font-size:15px;
  font-weight:600;
  line-height:1.3;
}
.hs-pgrid__title a:hover{color:var(--hs-w-accent,var(--hs-accent))}
.hs-pgrid__price{font-size:16px;font-weight:800}
/* WooCommerce wraps prices in <bdi>, <ins>, <del> and .amount. Inherit rather
   than restyle each, or a sale price arrives in Woo's default blue. */
.hs-pgrid__price .amount,
.hs-pgrid__price bdi,
.hs-pgrid__price ins{color:inherit;font:inherit;text-decoration:none}
.hs-pgrid__price del{color:var(--hs-muted);font-weight:500;opacity:.7}
.hs-pgrid__acts{margin-top:auto;padding-top:14px}
.hs-pgrid__acts .hs-btn{padding:11px 8px;font-size:13px}
@media(prefers-reduced-motion:reduce){
  .hs-pgrid__card{transition:none}
.hs-pgrid__card:hover{transform:none}
