/* =====================================================================
   HAULAT - COMPONENT LIBRARY          (assets/css/haulat-ui.css)

   Requires haulat-tokens.css.

   Not one rule below names a colour. Every component reads --ink,
   --surface, --line and --accent from the nearest surface ancestor. Put
   a button on the red field and it comes out white with dark type; put
   the same markup on the merchant canvas and it comes out near-black
   with white type. Same class, no modifier, no override, and no way to
   get it wrong.

   Which is why there is no dark-mode section in this file. There is
   nothing here that could need one.
   ===================================================================== */

/* =====================================================================
   1 - LAYOUT
   ===================================================================== */
.hx-shell { display: flex; flex-direction: column; min-height: 100svh; position: relative; }
.hx-main  { flex: 1 1 auto; width: 100%; position: relative; z-index: 1; }

.hx-wrap {
    width: 100%; max-width: var(--maxw); margin-inline: auto;
    padding-inline: clamp(var(--sp-4), 3vw, var(--sp-8));
}
.hx-wrap-tight { max-width: 780px; }
.hx-wrap-wide  { max-width: 1440px; }

.hx-stack   { display: flex; flex-direction: column; gap: var(--sp-4); }
.hx-stack-sm{ display: flex; flex-direction: column; gap: var(--sp-2); }
.hx-stack-lg{ display: flex; flex-direction: column; gap: var(--sp-8); }
.hx-row     { display: flex; align-items: center; gap: var(--sp-3); }
.hx-row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
.hx-row-wrap{ display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); }
.hx-spacer  { flex: 1 1 auto; }

/* the bento grid. Auto-fit so a shop with four pieces and a shop with
   four hundred both look composed, with no breakpoint list to maintain. */
.hx-bento {
    display: grid; gap: var(--sp-4);
    grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr));
}
.hx-bento-sm { grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr)); }
.hx-bento-lg { grid-template-columns: repeat(auto-fit, minmax(min(360px, 100%), 1fr)); }
.hx-span-2 { grid-column: span 2; }
.hx-span-3 { grid-column: span 3; }
@media (max-width: 767px) { .hx-span-2, .hx-span-3 { grid-column: span 1; } }

.hx-section { padding-block: clamp(var(--sp-10), 7vw, var(--sp-20)); }
.hx-divider { height: 1px; border: 0; background: var(--line); margin-block: var(--sp-6); }

/* =====================================================================
   2 - PAGE HEADER
   The title block every surface opens with. Eyebrow, title, one line of
   plain explanation, actions on the right.
   ===================================================================== */
.hx-page-head {
    display: flex; align-items: flex-end; justify-content: space-between;
    gap: var(--sp-6); flex-wrap: wrap;
    padding-block: var(--sp-8) var(--sp-6);
}
.hx-page-head__title { font-size: var(--t-2xl); color: var(--ink); }
.hx-page-head__sub {
    margin-top: var(--sp-2); font-size: var(--t-sm);
    color: var(--ink-2); max-width: 62ch; line-height: 1.65;
}
.hx-page-head__actions { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }

/* =====================================================================
   3 - BUTTONS
   .hx-btn is the solid one and it inverts the surface it sits on, which
   is what makes it read as the primary action anywhere without a
   variant per context.
   ===================================================================== */
.hx-btn {
    --btn-h: 46px;
    display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
    min-height: var(--btn-h); padding-inline: var(--sp-6);
    border: 1px solid transparent; border-radius: var(--r-sm);
    background: var(--ink); color: var(--surface);
    font-family: var(--f-sans); font-size: var(--t-xs); font-weight: 800;
    letter-spacing: var(--track-wider); text-transform: uppercase;
    white-space: nowrap; cursor: pointer; user-select: none;
    box-shadow: var(--shadow-2);
    transition: transform var(--dur-1) var(--ease),
                filter var(--dur-1) var(--ease),
                background var(--dur-1) var(--ease),
                opacity var(--dur-1) var(--ease);
}
.hx-btn:hover  { transform: translateY(-1px); filter: brightness(1.12); box-shadow: var(--shadow-3); }
.hx-btn:active { transform: translateY(1px); box-shadow: var(--shadow-1); }

/* ghost: reads as an action, does not compete with the primary */
.hx-btn--ghost {
    background: transparent; color: var(--ink);
    border-color: var(--line-strong); box-shadow: none;
}
.hx-btn--ghost:hover { background: var(--surface-2); filter: none; }

/* quiet: for the third and fourth actions in a row */
.hx-btn--quiet {
    background: transparent; color: var(--ink-2);
    border-color: transparent; box-shadow: none;
    letter-spacing: var(--track-wide);
}
.hx-btn--quiet:hover { background: var(--surface-2); color: var(--ink); filter: none; }

/* accent: exactly one per screen. If a screen has two, one of them is
   not actually the most important thing on it. */
.hx-btn--accent {
    background: var(--accent); color: var(--accent-ink); border-color: transparent;
}

/* danger: destructive and irreversible only. Cancelling a reservation
   is not danger; deleting a store's entire catalogue is. */
.hx-btn--danger { background: var(--bad-ink); color: var(--bad-bg); border-color: transparent; }

.hx-btn--sm { --btn-h: 36px; padding-inline: var(--sp-4); font-size: var(--t-2xs); }
.hx-btn--lg { --btn-h: 56px; padding-inline: var(--sp-8); font-size: var(--t-sm); }
.hx-btn--block { width: 100%; }
.hx-btn--pill { border-radius: var(--r-pill); }

/* icon buttons keep a 44px hit area even when they look 36px, because
   the alternative is a shopper missing the heart three times in a row */
.hx-btn--icon {
    padding: 0; width: var(--btn-h); min-width: var(--btn-h);
    border-radius: var(--r-sm); position: relative;
}
.hx-btn--icon::after {
    content: ''; position: absolute; inset: 50% auto auto 50%;
    width: 44px; height: 44px; transform: translate(-50%, -50%);
}
.hx-btn[disabled], .hx-btn[aria-disabled="true"] {
    opacity: .42; cursor: not-allowed; transform: none !important;
    box-shadow: none; filter: none;
}

/* a button that is working says so, and stops accepting taps */
.hx-btn[data-busy="true"] { pointer-events: none; color: transparent !important; position: relative; }
.hx-btn[data-busy="true"]::before {
    content: ''; position: absolute; inset: 50% auto auto 50%;
    width: 16px; height: 16px; margin: -8px 0 0 -8px;
    border: 2px solid currentColor; border-top-color: transparent;
    border-radius: 50%; color: var(--surface);
    animation: hx-spin .6s linear infinite;
}
@keyframes hx-spin { to { transform: rotate(360deg); } }

/* a text link that still looks like a link without being blue */
.hx-link {
    color: var(--ink); text-decoration: none;
    border-bottom: 1px solid var(--line-strong); padding-bottom: 1px;
    transition: border-color var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
.hx-link:hover { color: var(--accent); border-color: var(--accent); }

/* =====================================================================
   4 - FORM CONTROLS
   ===================================================================== */
.hx-field { display: flex; flex-direction: column; gap: var(--sp-2); }
.hx-field__label {
    font-family: var(--f-sans); font-size: var(--t-2xs); font-weight: 700;
    letter-spacing: var(--track-wider); text-transform: uppercase;
    color: var(--ink-2);
}
.hx-field__hint { font-size: var(--t-xs); color: var(--ink-3); line-height: 1.55; }
.hx-field__error { font-size: var(--t-xs); color: var(--bad-ink); font-weight: 600; }

.hx-input, .hx-select, .hx-textarea {
    width: 100%; min-height: 46px; padding: var(--sp-3) var(--sp-4);
    background: var(--surface-2); color: var(--ink);
    border: 1px solid var(--line); border-radius: var(--r-sm);
    font-family: var(--f-sans); font-size: var(--t-base); line-height: 1.5;
    -webkit-appearance: none; appearance: none;
    transition: border-color var(--dur-1) var(--ease), background var(--dur-1) var(--ease);
}
.hx-input::placeholder, .hx-textarea::placeholder { color: var(--ink-3); }
.hx-input:hover, .hx-select:hover, .hx-textarea:hover { border-color: var(--line-strong); }
.hx-input:focus, .hx-select:focus, .hx-textarea:focus {
    outline: none; border-color: var(--ink); background: var(--surface-3);
    box-shadow: var(--focus-ring);
}
.hx-input[aria-invalid="true"], .hx-textarea[aria-invalid="true"] { border-color: var(--bad-ink); }
.hx-textarea { min-height: 120px; resize: vertical; line-height: 1.65; }

.hx-select {
    padding-right: var(--sp-10); cursor: pointer;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1.5 6 6.5l5-5' stroke='currentColor' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right var(--sp-4) center;
}
/* native option lists are painted by the OS and ignore our tokens, so
   they get explicit values, keyed off the theme rather than the surface */
.hx-select option { background: var(--n-0); color: var(--n-900); }
html[data-theme="dark"] .hx-select option { background: var(--n-850); color: #F4F2F2; }

/* search field with a leading glyph */
.hx-search { position: relative; }
.hx-search .hx-input { padding-left: var(--sp-10); }
.hx-search__icon {
    position: absolute; left: var(--sp-4); top: 50%; transform: translateY(-50%);
    color: var(--ink-3); pointer-events: none;
}

/* checkbox and radio, drawn rather than native, so they honour tokens */
.hx-check { display: inline-flex; align-items: flex-start; gap: var(--sp-3); cursor: pointer; }
.hx-check input { position: absolute; opacity: 0; width: 0; height: 0; }
.hx-check__box {
    flex: 0 0 auto; width: 20px; height: 20px; margin-top: 1px;
    border: 1.5px solid var(--line-strong); border-radius: 6px;
    background: var(--surface-2);
    display: grid; place-items: center;
    transition: background var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease);
}
.hx-check input:checked + .hx-check__box { background: var(--ink); border-color: var(--ink); }
.hx-check input:checked + .hx-check__box::after {
    content: ''; width: 10px; height: 6px;
    border-left: 2px solid var(--surface); border-bottom: 2px solid var(--surface);
    transform: rotate(-45deg) translate(1px, -1px);
}
.hx-check input:focus-visible + .hx-check__box { box-shadow: var(--focus-ring); }
.hx-check--radio .hx-check__box { border-radius: 50%; }
.hx-check input:checked + .hx-check__box.hx-check__box--radio::after {
    content: ''; width: 8px; height: 8px; border: 0; border-radius: 50%;
    background: var(--surface); transform: none;
}
.hx-check__text { font-size: var(--t-sm); color: var(--ink); line-height: 1.5; }

/* switch, for settings that take effect immediately */
.hx-switch { display: inline-flex; align-items: center; gap: var(--sp-3); cursor: pointer; }
.hx-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.hx-switch__track {
    flex: 0 0 auto; width: 46px; height: 27px; border-radius: var(--r-pill);
    background: var(--surface-2); border: 1px solid var(--line-strong);
    position: relative; transition: background var(--dur-2) var(--ease), border-color var(--dur-2) var(--ease);
}
.hx-switch__track::after {
    content: ''; position: absolute; top: 2px; left: 2px;
    width: 21px; height: 21px; border-radius: 50%;
    background: var(--ink-2); box-shadow: var(--shadow-1);
    transition: transform var(--dur-2) var(--ease), background var(--dur-2) var(--ease);
}
.hx-switch input:checked + .hx-switch__track { background: var(--ink); border-color: var(--ink); }
.hx-switch input:checked + .hx-switch__track::after { transform: translateX(19px); background: var(--surface); }
.hx-switch input:focus-visible + .hx-switch__track { box-shadow: var(--focus-ring); }

/* =====================================================================
   5 - CARDS
   ===================================================================== */
.hx-card {
    background: var(--surface); color: var(--ink);
    border: 1px solid var(--line); border-radius: var(--r-lg);
    padding: var(--sp-6);
    box-shadow: var(--shadow-2);
}
.hx-card--flush { padding: 0; overflow: hidden; }
.hx-card--tight { padding: var(--sp-4); }
.hx-card--glass {
    background: var(--surface-2);
    backdrop-filter: blur(var(--blur-md)) saturate(1.25);
    -webkit-backdrop-filter: blur(var(--blur-md)) saturate(1.25);
    border-color: var(--line-strong);
    box-shadow: var(--shadow-4), inset 0 1px 0 var(--line-strong);
}
.hx-card--link {
    display: block; text-decoration: none;
    transition: transform var(--dur-2) var(--ease), box-shadow var(--dur-2) var(--ease), border-color var(--dur-2) var(--ease);
}
.hx-card--link:hover { transform: translateY(-3px); box-shadow: var(--shadow-4); border-color: var(--line-strong); }
.hx-card__head {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: var(--sp-4); margin-bottom: var(--sp-4);
}
.hx-card__title { font-size: var(--t-md); color: var(--ink); }
.hx-card__body { font-size: var(--t-sm); color: var(--ink-2); line-height: 1.7; }
.hx-card__foot {
    margin-top: var(--sp-5); padding-top: var(--sp-4);
    border-top: 1px solid var(--line);
    display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
}

/* stat tile. Merchant lives on these, so the number is the loudest
   thing and everything else gets out of its way. */
.hx-stat {
    background: var(--surface); border: 1px solid var(--line);
    border-radius: var(--r-lg); padding: var(--sp-5);
    display: flex; flex-direction: column; gap: var(--sp-1);
}
.hx-stat__label {
    font-family: var(--f-sans); font-size: var(--t-2xs); font-weight: 700;
    letter-spacing: var(--track-wider); text-transform: uppercase; color: var(--ink-3);
}
.hx-stat__value {
    font-family: var(--f-display); font-size: var(--t-2xl);
    line-height: 1; color: var(--ink); font-variant-numeric: tabular-nums;
}
.hx-stat__meta { font-size: var(--t-xs); color: var(--ink-2); }
.hx-stat__delta { font-size: var(--t-xs); font-weight: 700; font-variant-numeric: tabular-nums; }
.hx-stat__delta--up   { color: var(--ok-ink); }
.hx-stat__delta--down { color: var(--bad-ink); }
.hx-stat__delta--flat { color: var(--ink-3); }

/* =====================================================================
   6 - PIECE CARD
   The single most-repeated object in Haulat. Photo on top, facts below,
   exactly as the lookbook established.
   ===================================================================== */
.hx-piece {
    display: flex; flex-direction: column;
    background: var(--surface); color: var(--ink);
    border: 1px solid var(--line); border-radius: var(--r-lg);
    overflow: hidden; text-decoration: none;
    box-shadow: var(--shadow-2);
    transition: transform var(--dur-2) var(--ease), box-shadow var(--dur-2) var(--ease);
}
.hx-piece:hover { transform: translateY(-4px); box-shadow: var(--shadow-4); }
.hx-piece__media {
    position: relative; aspect-ratio: 3 / 4;
    background: var(--surface-2); overflow: hidden;
}
.hx-piece__media img {
    width: 100%; height: 100%; object-fit: cover;
    transition: transform var(--dur-4) var(--ease);
}
.hx-piece:hover .hx-piece__media img { transform: scale(1.045); }
.hx-piece__badges {
    position: absolute; top: var(--sp-3); left: var(--sp-3);
    display: flex; flex-wrap: wrap; gap: var(--sp-1); max-width: calc(100% - var(--sp-6));
}
.hx-piece__save {
    position: absolute; top: var(--sp-3); right: var(--sp-3);
    width: 38px; height: 38px; border-radius: var(--r-pill);
    display: grid; place-items: center; cursor: pointer;
    background: var(--surface); color: var(--ink);
    border: 1px solid var(--line); box-shadow: var(--shadow-2);
    transition: transform var(--dur-1) var(--ease);
}
.hx-piece__save:hover { transform: scale(1.08); }
.hx-piece__save[aria-pressed="true"] { background: var(--accent); color: var(--accent-ink); border-color: transparent; }
.hx-piece__body { padding: var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-1); flex: 1; }
.hx-piece__store {
    font-family: var(--f-sans); font-size: var(--t-2xs); font-weight: 700;
    letter-spacing: var(--track-wider); text-transform: uppercase; color: var(--ink-3);
}
.hx-piece__title {
    font-family: var(--f-sans); font-weight: 600; font-size: var(--t-base);
    line-height: 1.35; color: var(--ink); text-transform: none; letter-spacing: 0;
}
.hx-piece__foot {
    margin-top: auto; padding-top: var(--sp-3);
    display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-2);
}
.hx-piece__price {
    font-family: var(--f-mono); font-size: var(--t-sm);
    color: var(--ink); font-variant-numeric: tabular-nums;
}
.hx-piece__dist { font-family: var(--f-mono); font-size: var(--t-2xs); color: var(--ink-3); }

/* =====================================================================
   7 - CHIPS, BADGES, PILLS
   ===================================================================== */
.hx-chip {
    display: inline-flex; align-items: center; gap: var(--sp-2); flex: 0 0 auto;
    min-height: 34px; padding: 0 var(--sp-4); border-radius: var(--r-pill);
    background: var(--surface-2); color: var(--ink-2);
    border: 1px solid var(--line);
    font-family: var(--f-sans); font-size: var(--t-2xs); font-weight: 700;
    letter-spacing: var(--track-wide); text-transform: uppercase;
    cursor: pointer; white-space: nowrap; user-select: none;
    transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease);
}
.hx-chip:hover { background: var(--surface-3); color: var(--ink); border-color: var(--line-strong); }
.hx-chip[aria-pressed="true"], .hx-chip.is-on {
    background: var(--ink); color: var(--surface); border-color: var(--ink);
}
.hx-chip--sm { min-height: 28px; padding: 0 var(--sp-3); font-size: 9.5px; }

.hx-badge {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 3px var(--sp-2); border-radius: 6px;
    font-family: var(--f-sans); font-size: var(--t-2xs); font-weight: 700;
    letter-spacing: var(--track-wide); text-transform: uppercase;
    background: var(--surface-2); color: var(--ink-2); border: 1px solid var(--line);
    white-space: nowrap;
}
.hx-badge--ok   { background: var(--ok-bg);   color: var(--ok-ink);   border-color: var(--ok-line); }
.hx-badge--warn { background: var(--warn-bg); color: var(--warn-ink); border-color: var(--warn-line); }
.hx-badge--bad  { background: var(--bad-bg);  color: var(--bad-ink);  border-color: var(--bad-line); }
.hx-badge--info { background: var(--info-bg); color: var(--info-ink); border-color: var(--info-line); }
.hx-badge--solid{ background: var(--ink); color: var(--surface); border-color: var(--ink); }

/* a dot that carries state without needing a word next to it */
.hx-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; background: var(--ink-3); }
.hx-dot--ok { background: var(--ok-ink); }
.hx-dot--warn { background: var(--warn-ink); }
.hx-dot--bad { background: var(--bad-ink); }
.hx-dot--live { background: var(--ok-ink); animation: hx-pulse 2s var(--ease-in-out) infinite; }
@keyframes hx-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

.hx-count {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 19px; height: 19px; padding: 0 5px; border-radius: var(--r-pill);
    background: var(--accent); color: var(--accent-ink);
    font-family: var(--f-mono); font-size: 9.5px; font-weight: 700; line-height: 1;
    font-variant-numeric: tabular-nums;
}

/* =====================================================================
   8 - SEGMENTED CONTROL AND TABS
   ===================================================================== */
.hx-seg {
    display: inline-flex; padding: 3px; gap: 2px;
    background: var(--surface-2); border: 1px solid var(--line);
    border-radius: var(--r-pill);
}
.hx-seg__opt {
    display: inline-flex; align-items: center; justify-content: center; gap: 6px;
    min-height: 32px; padding: 0 var(--sp-4); border: 0; border-radius: var(--r-pill);
    background: transparent; color: var(--ink-3); cursor: pointer;
    font-family: var(--f-sans); font-size: var(--t-2xs); font-weight: 700;
    letter-spacing: var(--track-wide); text-transform: uppercase;
    transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
.hx-seg__opt:hover { color: var(--ink); }
.hx-seg__opt[aria-selected="true"], .hx-seg__opt.is-on {
    background: var(--surface-3); color: var(--ink); box-shadow: var(--shadow-1);
}

.hx-tabs {
    display: flex; gap: var(--sp-6); border-bottom: 1px solid var(--line);
    overflow-x: auto; scrollbar-width: none;
}
.hx-tabs::-webkit-scrollbar { display: none; }
.hx-tab {
    position: relative; padding: var(--sp-3) 0 var(--sp-4);
    background: none; border: 0; cursor: pointer; white-space: nowrap;
    color: var(--ink-3);
    font-family: var(--f-sans); font-size: var(--t-xs); font-weight: 700;
    letter-spacing: var(--track-wide); text-transform: uppercase;
    transition: color var(--dur-1) var(--ease);
}
.hx-tab:hover { color: var(--ink-2); }
.hx-tab[aria-selected="true"] { color: var(--ink); }
.hx-tab[aria-selected="true"]::after {
    content: ''; position: absolute; left: 0; right: 0; bottom: -1px;
    height: 2px; background: var(--ink); border-radius: 2px;
}

/* =====================================================================
   9 - TABLE
   Merchant reads these for hours. Sticky head, tabular figures,
   right-aligned numbers, and a mobile mode that turns each row into a
   stacked card rather than a horizontal scroll nobody discovers.
   ===================================================================== */
.hx-table-wrap {
    border: 1px solid var(--line); border-radius: var(--r-lg);
    overflow: hidden; background: var(--surface);
}
.hx-table { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
.hx-table thead th {
    position: sticky; top: 0; z-index: 2;
    background: var(--surface-2); color: var(--ink-3);
    text-align: left; padding: var(--sp-3) var(--sp-4);
    font-family: var(--f-sans); font-size: var(--t-2xs); font-weight: 700;
    letter-spacing: var(--track-wider); text-transform: uppercase;
    border-bottom: 1px solid var(--line); white-space: nowrap;
}
.hx-table tbody td {
    padding: var(--sp-4); color: var(--ink);
    border-bottom: 1px solid var(--line); vertical-align: middle;
}
.hx-table tbody tr:last-child td { border-bottom: 0; }
.hx-table tbody tr { transition: background var(--dur-1) var(--ease); }
.hx-table tbody tr:hover { background: var(--surface-2); }
.hx-table .hx-num, .hx-table [data-num] { text-align: right; font-variant-numeric: tabular-nums; font-family: var(--f-mono); }
.hx-table__sub { display: block; font-size: var(--t-2xs); color: var(--ink-3); margin-top: 2px; }

@media (max-width: 767px) {
    .hx-table, .hx-table tbody, .hx-table tr, .hx-table td { display: block; width: 100%; }
    .hx-table thead { display: none; }
    .hx-table tbody tr {
        border: 1px solid var(--line); border-radius: var(--r-md);
        margin-bottom: var(--sp-3); padding: var(--sp-2); background: var(--surface);
    }
    .hx-table tbody td {
        display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4);
        padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--line);
    }
    .hx-table tbody td:last-child { border-bottom: 0; }
    /* the header text comes back as a label per cell, from data-label */
    .hx-table tbody td::before {
        content: attr(data-label); flex: 0 0 auto;
        font-family: var(--f-sans); font-size: var(--t-2xs); font-weight: 700;
        letter-spacing: var(--track-wider); text-transform: uppercase; color: var(--ink-3);
    }
    .hx-table tbody td:not([data-label])::before { content: none; }
    .hx-table .hx-num, .hx-table [data-num] { text-align: right; }
}

/* list row, for anything table-shaped that is not a table */
.hx-list { display: flex; flex-direction: column; }
.hx-list__row {
    display: flex; align-items: center; gap: var(--sp-4);
    padding: var(--sp-4); border-bottom: 1px solid var(--line);
    color: var(--ink); text-decoration: none;
    transition: background var(--dur-1) var(--ease);
}
.hx-list__row:last-child { border-bottom: 0; }
.hx-list__row:hover { background: var(--surface-2); }
.hx-list__row--unread { background: var(--surface-2); }
.hx-list__row--unread::before {
    content: ''; width: 7px; height: 7px; border-radius: 50%;
    background: var(--accent); flex: 0 0 auto;
}
.hx-list__main { flex: 1 1 auto; min-width: 0; }
.hx-list__title { font-size: var(--t-sm); font-weight: 600; color: var(--ink); }
.hx-list__meta { font-size: var(--t-xs); color: var(--ink-3); margin-top: 2px; }

/* =====================================================================
   10 - AVATAR
   ===================================================================== */
.hx-avatar {
    display: inline-grid; place-items: center; flex: 0 0 auto;
    width: 40px; height: 40px; border-radius: 50%; overflow: hidden;
    background: var(--surface-2); color: var(--ink-2);
    border: 1px solid var(--line);
    font-family: var(--f-sans); font-size: var(--t-sm); font-weight: 700;
    text-transform: uppercase;
}
.hx-avatar img { width: 100%; height: 100%; object-fit: cover; }
.hx-avatar--sm { width: 28px; height: 28px; font-size: var(--t-2xs); }
.hx-avatar--lg { width: 64px; height: 64px; font-size: var(--t-lg); }
.hx-avatar--xl { width: 96px; height: 96px; font-size: var(--t-xl); }
.hx-avatar-stack { display: flex; }
.hx-avatar-stack .hx-avatar { margin-left: -10px; border: 2px solid var(--surface); }
.hx-avatar-stack .hx-avatar:first-child { margin-left: 0; }

/* =====================================================================
   11 - HEADER, NAV, MENU
   Carried over from the landing so the portals read as the same
   product: a glass pill, a plus that spins into a close, a panel that
   drops straight underneath.
   ===================================================================== */
.hx-header {
    position: fixed; top: 0; left: 0; right: 0; z-index: 50;
    width: 100%; max-width: var(--maxw); margin-inline: auto;
    padding: var(--sp-3) clamp(var(--sp-3), 3vw, var(--sp-5)) 0;
    pointer-events: none;
}
.hx-header__row { display: flex; gap: var(--sp-2); align-items: stretch; pointer-events: auto; }
.hx-pill {
    display: flex; align-items: center;
    min-height: 54px; padding: 0 var(--sp-5);
    background: var(--surface); color: var(--ink);
    border: 1px solid var(--line-strong); border-radius: var(--r-pill);
    backdrop-filter: blur(var(--blur-md)) saturate(1.3);
    -webkit-backdrop-filter: blur(var(--blur-md)) saturate(1.3);
    box-shadow: var(--shadow-3), inset 0 1px 0 var(--line-strong);
}
.hx-pill--brand { flex: 1; justify-content: center; }
.hx-pill--tight { padding: 0 var(--sp-2); gap: 2px; }
.hx-pill__logo { height: 26px; width: auto; }
/* the logo asset ships black. Rather than maintain a second white file,
   it is knocked out to the surface's ink colour, so it is correct on
   every surface and in both themes automatically. */
.hx-pill__logo--knockout { filter: brightness(0) invert(1); }
html[data-theme="dark"] .sf-inverse .hx-pill__logo--knockout,
.sf-canvas .hx-pill__logo--knockout,
.sf-raised .hx-pill__logo--knockout { filter: none; }
html[data-theme="dark"] .sf-canvas .hx-pill__logo--knockout,
html[data-theme="dark"] .sf-raised .hx-pill__logo--knockout { filter: brightness(0) invert(1); }
.hx-pill__word {
    font-family: var(--f-display); font-size: var(--t-xs);
    letter-spacing: var(--track-widest); text-transform: uppercase; color: var(--ink);
}

.hx-iconbtn {
    display: grid; place-items: center; flex: 0 0 auto;
    width: 40px; height: 40px; border-radius: var(--r-pill);
    background: transparent; border: 0; cursor: pointer;
    color: var(--ink-2);
    transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
.hx-iconbtn:hover { background: var(--surface-2); color: var(--ink); }
.hx-iconbtn[aria-current="page"], .hx-iconbtn.is-on { background: var(--surface-3); color: var(--ink); }
.hx-iconbtn__badge {
    position: absolute; top: 2px; right: 2px;
    min-width: 16px; height: 16px; padding: 0 4px;
    display: grid; place-items: center; border-radius: var(--r-pill);
    background: var(--accent); color: var(--accent-ink);
    font-family: var(--f-mono); font-size: 9px; font-weight: 700; line-height: 1;
}
.hx-iconbtn--badged { position: relative; }

/* the plus that becomes a close */
.hx-plus { position: relative; width: 17px; height: 17px; display: block;
    transition: transform var(--dur-3) var(--ease); }
.hx-plus span {
    position: absolute; top: 50%; left: 50%; background: currentColor;
    border-radius: 2px; transform-origin: center;
}
.hx-plus span:nth-child(1) { width: 17px; height: 2px; transform: translate(-50%, -50%); }
.hx-plus span:nth-child(2) { width: 2px; height: 17px; transform: translate(-50%, -50%); }
.hx-header.is-open .hx-plus { transform: rotate(135deg); }

.hx-menu {
    pointer-events: none; opacity: 0;
    transform: translateY(-10px) scale(.985); transform-origin: top center;
    transition: opacity var(--dur-2) var(--ease), transform var(--dur-3) var(--ease);
    margin-top: var(--sp-2); padding: var(--sp-3) var(--sp-2) var(--sp-4);
    border-radius: var(--r-xl);
    background: var(--surface); color: var(--ink);
    border: 1px solid var(--line-strong);
    backdrop-filter: blur(var(--blur-lg)) saturate(1.3);
    -webkit-backdrop-filter: blur(var(--blur-lg)) saturate(1.3);
    box-shadow: var(--shadow-5), inset 0 1px 0 var(--line-strong);
    max-height: calc(100svh - 140px); overflow-y: auto;
}
.hx-header.is-open .hx-menu { opacity: 1; pointer-events: auto; transform: translateY(0) scale(1); }
.hx-menu__row {
    display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4);
    width: 100%; padding: var(--sp-3) var(--sp-5);
    background: none; border: 0; cursor: pointer; text-align: left;
    font-family: var(--f-sans); font-weight: 700;
    font-size: clamp(1.05rem, 4vw, 1.35rem); letter-spacing: var(--track-tight);
    color: var(--ink); text-transform: uppercase;
    transition: padding-left var(--dur-2) var(--ease), opacity var(--dur-2) var(--ease);
}
.hx-menu__row:hover { padding-left: var(--sp-8); opacity: .82; }
.hx-menu__arrow { font-size: .78em; color: var(--ink-3); }
.hx-menu__meta {
    display: block; padding: var(--sp-4) var(--sp-5) 2px;
    font-family: var(--f-mono); font-size: var(--t-2xs);
    letter-spacing: var(--track-widest); color: var(--ink-3);
}
html[lang="zh-Hant"] .hx-menu__row, html[lang="ja"] .hx-menu__row { text-transform: none; }

/* the floating bottom nav: thumb country on mobile, a resting pill on
   desktop. Same geometry as the landing footer so they read as siblings. */
.hx-nav {
    position: fixed; left: var(--sp-3); right: var(--sp-3);
    bottom: calc(env(safe-area-inset-bottom, 0px) + var(--sp-5));
    z-index: 45; max-width: var(--maxw); margin-inline: auto;
    display: flex; align-items: center; justify-content: space-between;
    padding: var(--sp-3) var(--sp-4);
    background: var(--surface); color: var(--ink);
    border: 1px solid var(--line-strong); border-radius: var(--r-pill);
    backdrop-filter: blur(var(--blur-md)) saturate(1.3);
    -webkit-backdrop-filter: blur(var(--blur-md)) saturate(1.3);
    box-shadow: var(--shadow-3), inset 0 1px 0 var(--line-strong);
}
.hx-nav__item {
    position: relative; flex: 1 1 0; min-width: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
    min-height: 42px; border-radius: var(--r-pill);
    color: var(--ink-3); text-decoration: none;
    transition: color var(--dur-1) var(--ease), background var(--dur-1) var(--ease);
}
.hx-nav__item:hover { color: var(--ink-2); }
.hx-nav__item[aria-current="page"] { color: var(--ink); background: var(--surface-3); }
.hx-nav__label {
    font-family: var(--f-sans); font-size: 9px; font-weight: 700;
    letter-spacing: var(--track-wide); text-transform: uppercase;
}
@media (max-width: 400px) { .hx-nav__label { display: none; } }

/* =====================================================================
   12 - SHEETS AND MODALS
   Bento cards that slide down from the header against a heavy frosted
   scrim, as established on the landing.
   ===================================================================== */
.hx-scrim {
    position: fixed; inset: 0; z-index: 60;
    background: rgba(var(--red-shadow), .55);
    backdrop-filter: blur(var(--blur-sm)); -webkit-backdrop-filter: blur(var(--blur-sm));
    opacity: 0; pointer-events: none;
    transition: opacity var(--dur-2) var(--ease);
}
.hx-scrim.is-open { opacity: 1; pointer-events: auto; }

.hx-sheet {
    position: fixed; z-index: 61;
    left: 50%; top: var(--sp-4); transform: translate(-50%, -14px);
    width: min(94vw, 620px); max-height: calc(100svh - var(--sp-8));
    overflow-y: auto; overscroll-behavior: contain;
    padding: var(--sp-6);
    background: var(--surface); color: var(--ink);
    border: 1px solid var(--line-strong); border-radius: var(--r-2xl);
    backdrop-filter: blur(var(--blur-lg)) saturate(1.3);
    -webkit-backdrop-filter: blur(var(--blur-lg)) saturate(1.3);
    box-shadow: var(--shadow-5), inset 0 1px 0 var(--line-strong);
    opacity: 0; pointer-events: none;
    transition: opacity var(--dur-2) var(--ease), transform var(--dur-3) var(--ease);
}
.hx-sheet.is-open { opacity: 1; pointer-events: auto; transform: translate(-50%, 0); }
.hx-sheet--wide { width: min(96vw, 1020px); }
.hx-sheet__head {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: var(--sp-4); margin-bottom: var(--sp-5);
}
.hx-sheet__title { font-size: var(--t-lg); color: var(--ink); }
.hx-sheet__close {
    flex: 0 0 auto; width: 36px; height: 36px; border-radius: var(--r-pill);
    display: grid; place-items: center; cursor: pointer;
    background: var(--surface-2); color: var(--ink-2); border: 1px solid var(--line);
    transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
.hx-sheet__close:hover { background: var(--surface-3); color: var(--ink); }
.hx-sheet__foot {
    margin-top: var(--sp-6); padding-top: var(--sp-4);
    border-top: 1px solid var(--line);
    display: flex; justify-content: flex-end; gap: var(--sp-2); flex-wrap: wrap;
}

/* the split modal the brief calls for: gallery left, facts and booking
   right. Stacks on mobile, gallery first. */
.hx-split { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-6); }
@media (max-width: 860px) { .hx-split { grid-template-columns: 1fr; } }

/* on phones a sheet is a bottom sheet: it comes from where the thumb is */
@media (max-width: 640px) {
    .hx-sheet {
        top: auto; bottom: 0; left: 0; transform: translateY(18px);
        width: 100%; max-height: 92svh;
        border-radius: var(--r-2xl) var(--r-2xl) 0 0;
        padding-bottom: calc(env(safe-area-inset-bottom, 0px) + var(--sp-6));
    }
    .hx-sheet.is-open { transform: translateY(0); }
    .hx-sheet::before {
        content: ''; display: block; width: 40px; height: 4px;
        margin: 0 auto var(--sp-4); border-radius: var(--r-pill); background: var(--line-strong);
    }
}

/* =====================================================================
   13 - TOAST
   Replaces the browser alert() the brief specifically called out.
   ===================================================================== */
.hx-toasts {
    position: fixed; z-index: 80;
    left: 50%; transform: translateX(-50%);
    bottom: calc(env(safe-area-inset-bottom, 0px) + var(--nav-h) + var(--sp-4));
    display: flex; flex-direction: column-reverse; gap: var(--sp-2);
    width: min(92vw, 420px); pointer-events: none;
}
.hx-toast {
    display: flex; align-items: center; gap: var(--sp-3);
    padding: var(--sp-4) var(--sp-5); border-radius: var(--r-md);
    background: var(--surface); color: var(--ink);
    border: 1px solid var(--line-strong);
    backdrop-filter: blur(var(--blur-md)) saturate(1.3);
    -webkit-backdrop-filter: blur(var(--blur-md)) saturate(1.3);
    box-shadow: var(--shadow-4);
    font-size: var(--t-sm); font-weight: 600;
    pointer-events: auto;
    animation: hx-toast-in var(--dur-3) var(--ease);
}
.hx-toast[data-leaving="true"] { animation: hx-toast-out var(--dur-2) var(--ease) forwards; }
@keyframes hx-toast-in  { from { opacity: 0; transform: translateY(14px) scale(.97); } }
@keyframes hx-toast-out { to   { opacity: 0; transform: translateY(8px) scale(.98); } }
.hx-toast__icon { flex: 0 0 auto; display: grid; place-items: center; }
.hx-toast--ok  .hx-toast__icon { color: var(--ok-ink); }
.hx-toast--bad .hx-toast__icon { color: var(--bad-ink); }

/* =====================================================================
   14 - EMPTY, LOADING, SKELETON
   An empty state is a first impression, not an error. Every one says
   what belongs here and offers the action that fills it.
   ===================================================================== */
.hx-empty {
    display: flex; flex-direction: column; align-items: center; text-align: center;
    gap: var(--sp-3); padding: var(--sp-16) var(--sp-6);
    border: 1px dashed var(--line-strong); border-radius: var(--r-lg);
    background: var(--surface-2); color: var(--ink-2);
}
.hx-empty__icon { color: var(--ink-3); margin-bottom: var(--sp-1); }
.hx-empty__title { font-size: var(--t-md); color: var(--ink); }
.hx-empty__body { font-size: var(--t-sm); color: var(--ink-2); max-width: 46ch; line-height: 1.7; }
.hx-empty__action { margin-top: var(--sp-3); }

.hx-skel {
    background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 37%, var(--surface-2) 63%);
    background-size: 400% 100%;
    animation: hx-shimmer 1.4s ease-in-out infinite;
    border-radius: var(--r-sm);
}
@keyframes hx-shimmer { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }
.hx-skel--text { height: 12px; margin-bottom: var(--sp-2); }
.hx-skel--title { height: 20px; width: 60%; margin-bottom: var(--sp-3); }
.hx-skel--media { aspect-ratio: 3 / 4; border-radius: var(--r-lg); }
.hx-skel--circle { border-radius: 50%; }

.hx-spinner {
    width: 20px; height: 20px; border-radius: 50%;
    border: 2px solid var(--line-strong); border-top-color: var(--ink);
    animation: hx-spin .62s linear infinite;
}

/* =====================================================================
   15 - CALLOUT
   ===================================================================== */
.hx-callout {
    display: flex; gap: var(--sp-3);
    padding: var(--sp-4) var(--sp-5); border-radius: var(--r-md);
    border: 1px solid var(--line); background: var(--surface-2);
    font-size: var(--t-sm); line-height: 1.65; color: var(--ink-2);
}
.hx-callout__icon { flex: 0 0 auto; margin-top: 2px; }
.hx-callout--ok   { background: var(--ok-bg);   border-color: var(--ok-line);   color: var(--ok-ink); }
.hx-callout--warn { background: var(--warn-bg); border-color: var(--warn-line); color: var(--warn-ink); }
.hx-callout--bad  { background: var(--bad-bg);  border-color: var(--bad-line);  color: var(--bad-ink); }
.hx-callout--info { background: var(--info-bg); border-color: var(--info-line); color: var(--info-ink); }
.hx-callout strong { color: inherit; font-weight: 800; }

/* =====================================================================
   16 - SPLIT AUTH LAYOUT
   Login and signup: a story pane and a form pane. The story pane is a
   surface in its own right, which is what stopped dark copy landing on
   raw red.
   ===================================================================== */
.hx-auth { display: grid; grid-template-columns: 1fr 1fr; min-height: 100svh; }
.hx-auth__story {
    position: relative; display: flex; flex-direction: column; justify-content: space-between;
    padding: clamp(var(--sp-8), 5vw, var(--sp-16));
    border-right: 1px solid var(--line); overflow: hidden;
}
.hx-auth__form {
    display: flex; align-items: center; justify-content: center;
    padding: clamp(var(--sp-6), 5vw, var(--sp-16));
}
.hx-auth__inner { width: 100%; max-width: 400px; }
@media (max-width: 900px) {
    .hx-auth { grid-template-columns: 1fr; }
    .hx-auth__story { border-right: 0; border-bottom: 1px solid var(--line); min-height: 34svh; }
}

/* =====================================================================
   17 - MERCHANT OPS SHELL
   A quiet, dense working surface. Sidebar on desktop, bottom nav on
   mobile, and no animated field behind a table anyone has to read.
   ===================================================================== */
.hx-ops { display: grid; grid-template-columns: 248px 1fr; min-height: 100svh; }
.hx-ops__side {
    position: sticky; top: 0; align-self: start; height: 100svh;
    display: flex; flex-direction: column; gap: var(--sp-2);
    padding: var(--sp-5) var(--sp-3);
    border-right: 1px solid var(--line); overflow-y: auto;
}
.hx-ops__brand { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-2) var(--sp-3) var(--sp-6); }
.hx-ops__navitem {
    display: flex; align-items: center; gap: var(--sp-3);
    padding: var(--sp-3) var(--sp-4); border-radius: var(--r-sm);
    color: var(--ink-2); text-decoration: none;
    font-size: var(--t-sm); font-weight: 600;
    transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
.hx-ops__navitem:hover { background: var(--surface-2); color: var(--ink); }
.hx-ops__navitem[aria-current="page"] { background: var(--surface-3); color: var(--ink); font-weight: 700; }
.hx-ops__navitem svg { flex: 0 0 auto; opacity: .82; }
.hx-ops__group {
    padding: var(--sp-5) var(--sp-4) var(--sp-2);
    font-family: var(--f-sans); font-size: var(--t-2xs); font-weight: 700;
    letter-spacing: var(--track-wider); text-transform: uppercase; color: var(--ink-3);
}
.hx-ops__body { min-width: 0; padding: 0 clamp(var(--sp-4), 3vw, var(--sp-8)) var(--sp-16); }
.hx-ops__topbar {
    position: sticky; top: 0; z-index: 30;
    display: flex; align-items: center; gap: var(--sp-4);
    padding: var(--sp-4) 0; margin-bottom: var(--sp-2);
    background: var(--surface); border-bottom: 1px solid var(--line);
}
@media (max-width: 1023px) {
    .hx-ops { grid-template-columns: 1fr; }
    .hx-ops__side {
        position: fixed; left: 0; top: 0; bottom: 0; z-index: 70;
        width: 268px; background: var(--surface);
        transform: translateX(-100%); transition: transform var(--dur-3) var(--ease);
        box-shadow: var(--shadow-5);
    }
    .hx-ops__side.is-open { transform: translateX(0); }
    .hx-ops__body { padding-bottom: calc(var(--nav-h) + var(--sp-8)); }
}

/* =====================================================================
   18 - THEME TOGGLE
   Three states, not two: someone who has set their laptop to switch at
   sunset should not have to set Haulat separately.
   ===================================================================== */
.hx-theme-toggle { display: inline-flex; padding: 3px; gap: 2px;
    background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-pill); }
.hx-theme-toggle button {
    display: grid; place-items: center; width: 34px; height: 30px;
    border: 0; border-radius: var(--r-pill); background: transparent;
    color: var(--ink-3); cursor: pointer;
    transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
.hx-theme-toggle button:hover { color: var(--ink); }
.hx-theme-toggle button[aria-pressed="true"] { background: var(--surface-3); color: var(--ink); box-shadow: var(--shadow-1); }

/* =====================================================================
   19 - LANGUAGE SWITCH
   ===================================================================== */
.hx-lang { display: flex; align-items: center; gap: 2px; }
.hx-lang a {
    display: grid; place-items: center; min-width: 32px; height: 28px;
    padding: 0 var(--sp-2); border-radius: var(--r-pill);
    font-family: var(--f-mono); font-size: var(--t-2xs);
    letter-spacing: var(--track-wide); color: var(--ink-3); text-decoration: none;
    transition: color var(--dur-1) var(--ease), background var(--dur-1) var(--ease);
}
.hx-lang a:hover { color: var(--ink); }
.hx-lang a[aria-current="true"] { background: var(--ink); color: var(--surface); font-weight: 700; }

/* =====================================================================
   20 - CHAT
   ===================================================================== */
.hx-chat { display: flex; flex-direction: column; gap: var(--sp-3); }
.hx-msg { display: flex; gap: var(--sp-3); max-width: 82%; }
.hx-msg--mine { margin-left: auto; flex-direction: row-reverse; }
.hx-msg__bubble {
    padding: var(--sp-3) var(--sp-4); border-radius: var(--r-md);
    background: var(--surface-2); color: var(--ink);
    border: 1px solid var(--line);
    font-size: var(--t-sm); line-height: 1.6; word-break: break-word;
}
.hx-msg--mine .hx-msg__bubble { background: var(--ink); color: var(--surface); border-color: transparent; }
.hx-msg__meta { font-size: var(--t-2xs); color: var(--ink-3); margin-top: 3px; }
.hx-chat__composer {
    display: flex; gap: var(--sp-2); align-items: flex-end;
    padding: var(--sp-3); border-top: 1px solid var(--line); background: var(--surface);
}

/* =====================================================================
   21 - MOTION UTILITIES
   ===================================================================== */
.hx-reveal { opacity: 0; transform: translateY(18px); transition: opacity var(--dur-4) var(--ease), transform var(--dur-4) var(--ease); }
.hx-reveal.is-in { opacity: 1; transform: none; }
.hx-acc { overflow: hidden; transition: max-height var(--dur-3) var(--ease), opacity var(--dur-2) var(--ease); }
.hx-acc.is-shut { max-height: 0 !important; opacity: 0; pointer-events: none; }
.hx-caret { transition: transform var(--dur-2) var(--ease); }
.hx-caret.is-turned { transform: rotate(180deg); }

/* =====================================================================
   22 - SCROLL AFFORDANCES
   ===================================================================== */
.hx-scroller {
    display: flex; gap: var(--sp-3); overflow-x: auto;
    scroll-snap-type: x proximity; scrollbar-width: none;
    padding-bottom: var(--sp-2); margin-inline: calc(var(--sp-4) * -1);
    padding-inline: var(--sp-4);
}
.hx-scroller::-webkit-scrollbar { display: none; }
.hx-scroller > * { scroll-snap-align: start; flex: 0 0 auto; }

/* =====================================================================
   23 - STATE UTILITIES
   Tailwind's hover: prefix only compiles for classes Tailwind itself
   generates, so `hover:hx-ink` would have been silently dead markup.
   These are real classes with a real :hover rule, and like everything
   else here they read the surface rather than naming a colour.
   ===================================================================== */
.hx-hover-ink:hover     { color: var(--ink); }
.hx-hover-accent:hover  { color: var(--accent); }
.hx-hover-bg2:hover     { background-color: var(--surface-2); }
.hx-hover-bg3:hover     { background-color: var(--surface-3); }
.hx-hover-line:hover    { border-color: var(--line-strong); }
.hx-hover-lift          { transition: transform var(--dur-2) var(--ease), box-shadow var(--dur-2) var(--ease); }
.hx-hover-lift:hover    { transform: translateY(-2px); box-shadow: var(--shadow-3); }

/* text laid directly over a photograph. The one place a fixed colour
   is correct: the "surface" is a merchant's uploaded image and there
   is no way to know how dark it is, so the type carries its own
   contrast with a scrim rather than trusting the pixels behind it. */
.hx-on-media {
    color: #FFFFFF;
    text-shadow: 0 1px 3px rgba(0, 0, 0, .55), 0 0 18px rgba(0, 0, 0, .35);
}
.hx-media-scrim {
    position: absolute; inset: auto 0 0 0; height: 62%;
    background: linear-gradient(to top, rgba(0, 0, 0, .72), rgba(0, 0, 0, 0));
    pointer-events: none;
}

/* a divided list, without Tailwind's divide-* colour utilities */
.hx-divide-y > * + * { border-top: 1px solid var(--line); }
.hx-divide-x > * + * { border-left: 1px solid var(--line); }

/* range sliders and native form accents. accent-color is one line and
   replaces the whole custom-track-and-thumb dance, and it is the only
   way a checkbox rendered by the OS picks up the brand at all. */
.hx-range { accent-color: var(--ink); cursor: pointer; }
input[type="range"].hx-range { height: 22px; }

/* a translucent version of whatever surface is applied alongside it.
   Replaces Tailwind's bg-white/60 slash syntax, which only compiles
   for Tailwind's own utilities and so was dead the moment the colour
   classes were renamed. */
.hx-translucent {
    background-color: color-mix(in srgb, var(--surface) 72%, transparent);
    backdrop-filter: blur(var(--blur-sm)) saturate(1.15);
    -webkit-backdrop-filter: blur(var(--blur-sm)) saturate(1.15);
}
/* color-mix is not in older Safari or Firefox ESR; those get the solid
   surface, which is a slightly heavier card and nothing worse */
@supports not (background-color: color-mix(in srgb, red 50%, transparent)) {
    .hx-translucent { background-color: var(--surface); }
}

.hx-focus-line:focus, .hx-focus-line:focus-visible { border-color: var(--ink); outline: none; }
.hx-hover-invert:hover      { background-color: var(--ink); color: var(--surface); }
.hx-hover-danger:hover      { background-color: var(--bad-ink); color: var(--bad-bg); }
.hx-hover-danger-ink:hover  { color: var(--bad-ink); }
.hx-hover-danger-line:hover { border-color: var(--bad-ink); }
.hx-placeholder::placeholder { color: var(--ink-3); }
.group:hover .hx-group-ink  { color: var(--ink); }
.group:hover .hx-group-line { border-color: var(--line-strong); }

/* an image that failed to load leaves a labelled placeholder rather
   than a browser's broken-image glyph, which reads as a broken site */
[data-noimg]::after {
    content: attr(data-noimg);
    position: absolute; inset: 0; display: grid; place-items: center;
    padding: var(--sp-4); text-align: center;
    font-family: var(--f-mono); font-size: var(--t-2xs);
    letter-spacing: var(--track-wide); color: var(--ink-3);
}

/* =====================================================================
   24 - MOBILE
   ===================================================================== */
@media (max-width: 767px) {
    body.hx-has-nav { padding-bottom: calc(env(safe-area-inset-bottom, 0px) + var(--nav-h) + var(--sp-5)); }
    .hx-input, .hx-select, .hx-textarea { font-size: 16px; }  /* stops iOS zooming on focus */
    .hx-card { padding: var(--sp-4); border-radius: var(--r-md); }
    .hx-page-head { padding-block: var(--sp-6) var(--sp-4); }
    .hx-btn { --btn-h: 48px; }
}

/* =====================================================================
   25 - THE APP FRAME
   One layout for both portals: a permanent left rail, a top bar, and a
   content column. Shopper and merchant differ only in which links the
   rail contains.

   This replaces fifteen pages that each carried their own header pill,
   their own sidebar and their own <head>. That is why the "+" menu was
   broken on some pages and not others, why the logo was knocked out on
   some and not others, and why 364 text nodes were unreadable: there
   was no single place where any of it was decided.

   Flat colours throughout. No gradient sits behind text, nothing loops,
   and the only blur left is on the mobile drawer scrim.
   ===================================================================== */
.hx-app { display: grid; grid-template-columns: var(--rail-w, 252px) 1fr; min-height: 100svh; }

.hx-skip {
    position: absolute; left: -9999px; top: 0; z-index: 200;
    padding: var(--sp-3) var(--sp-5); border-radius: 0 0 var(--r-sm) 0;
    background: var(--ink); color: var(--surface); font-weight: 700;
}
.hx-skip:focus { left: 0; }

/* ---------------------------------------------------------------- RAIL */
.hx-rail {
    position: sticky; top: 0; align-self: start;
    height: 100svh; display: flex; flex-direction: column;
    border-right: 1px solid var(--line);
    overflow-y: auto; overscroll-behavior: contain;
    scrollbar-width: thin;
}
.hx-rail__brand {
    display: flex; align-items: center; gap: var(--sp-3);
    padding: var(--sp-5) var(--sp-5) var(--sp-4);
    flex: 0 0 auto;
}
/* The logo ships as a black SVG. Rather than maintain a second white
   asset, or four conditional filter rules that got it wrong on half the
   pages, it is painted with the surface's own ink through a mask. It is
   correct on any surface, in any theme, by construction. */
.hx-logo {
    display: block; height: 20px; width: auto;
    /* The rail is dark in BOTH themes by design, so the knockout is
       unconditional. The per-page conditional version is what shipped a
       black logo on a black pill. */
    filter: brightness(0) invert(1);
}
.hx-logo--sm { height: 15px; }
/* If the asset is missing the mask paints nothing, so the wordmark
   behind it shows through rather than leaving an empty pill. */
.hx-logo-fallback {
    font-family: var(--f-display); font-size: var(--t-sm);
    letter-spacing: var(--track-widest); text-transform: uppercase; color: var(--ink);
}

.hx-rail__store {
    display: flex; align-items: center; gap: var(--sp-3);
    margin: 0 var(--sp-3) var(--sp-4); padding: var(--sp-3);
    border-radius: var(--r-md);
    background: var(--surface-2); border: 1px solid var(--line);
    text-decoration: none; color: var(--ink);
}
.hx-rail__store b { display: block; font-size: var(--t-sm); font-weight: 700; }
.hx-rail__store span {
    display: block; font-family: var(--f-mono); font-size: 9.5px;
    letter-spacing: var(--track-wide); color: var(--ink-3);
}

.hx-rail__group {
    padding: var(--sp-5) var(--sp-5) var(--sp-2);
    font-family: var(--f-sans); font-size: var(--t-2xs); font-weight: 700;
    letter-spacing: var(--track-wider); text-transform: uppercase;
    color: var(--ink-3);
}
.hx-rail__link {
    display: flex; align-items: center; gap: var(--sp-3);
    margin: 1px var(--sp-3); padding: var(--sp-3) var(--sp-4);
    border-radius: var(--r-sm);
    color: var(--ink-2); text-decoration: none;
    font-size: var(--t-sm); font-weight: 600; line-height: 1.3;
    transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
.hx-rail__link:hover { background: var(--surface-2); color: var(--ink); }
.hx-rail__link[aria-current="page"] {
    background: var(--surface-3); color: var(--ink); font-weight: 700;
}
.hx-rail__link svg { flex: 0 0 auto; opacity: .9; }
.hx-rail__link > span:first-of-type { flex: 1; min-width: 0; }
.hx-rail__foot {
    margin-top: auto; padding: var(--sp-3) 0 var(--sp-5);
    border-top: 1px solid var(--line);
}
.hx-rail__pref {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--sp-3); padding: var(--sp-3) var(--sp-5);
}

/* ------------------------------------------------------------- CONTENT */
.hx-col { min-width: 0; display: flex; flex-direction: column; }

.hx-topbar {
    position: sticky; top: 0; z-index: 30;
    display: flex; align-items: center; gap: var(--sp-4);
    padding: var(--sp-4) clamp(var(--sp-4), 3vw, var(--sp-8));
    background: var(--surface);
    border-bottom: 1px solid var(--line);
}
.hx-topbar__title { font-size: var(--t-lg); color: var(--ink); }
.hx-topbar__sub {
    font-family: var(--f-mono); font-size: var(--t-2xs);
    letter-spacing: var(--track-wide); color: var(--ink-3);
}
.hx-topbar__actions { margin-left: auto; display: flex; align-items: center; gap: var(--sp-2); }
.hx-burger { display: none; }

.hx-body { flex: 1; padding: clamp(var(--sp-5), 3vw, var(--sp-8)); }
.hx-body > .hx-wrap { padding-inline: 0; }

/* --------------------------------------------------------------- MOBILE
   The rail becomes a drawer and a bottom bar carries the five most used
   destinations. One pane at a time, no horizontal scrolling, and the
   drawer is the only place a blur survives. */
@media (max-width: 1023px) {
    .hx-app { grid-template-columns: 1fr; }
    .hx-rail {
        position: fixed; left: 0; top: 0; bottom: 0; z-index: 70;
        width: 274px; height: 100svh;
        transform: translateX(-100%);
        transition: transform var(--dur-3) var(--ease);
        box-shadow: var(--shadow-5);
    }
    .hx-rail.is-open { transform: none; }
    .hx-burger { display: grid; }
    .hx-body { padding-bottom: calc(env(safe-area-inset-bottom, 0px) + var(--nav-h) + var(--sp-6)); }
}
.hx-tabbar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 45;
    display: flex; align-items: stretch;
    padding: var(--sp-2) var(--sp-2) calc(env(safe-area-inset-bottom, 0px) + var(--sp-2));
    background: var(--surface); border-top: 1px solid var(--line);
}
.hx-tabbar__item {
    flex: 1 1 0; min-width: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
    min-height: 46px; border-radius: var(--r-sm);
    color: var(--ink-3); text-decoration: none;
}
.hx-tabbar__item[aria-current="page"] { color: var(--ink); background: var(--surface-2); }
.hx-tabbar__item span {
    font-family: var(--f-sans); font-size: 9.5px; font-weight: 700;
    letter-spacing: var(--track-wide); text-transform: uppercase;
}

/* The tab bar is a phone control. This rule has to come after the one
   that shows it: same specificity means source order decides, and with
   it written above, `display:flex` won and the mobile tab bar sat on
   every desktop page. */
@media (min-width: 1024px) {
    .hx-tabbar { display: none; }
}

/* Shown only when the logo image genuinely fails to load: Shell's
   onerror replaces the <img> with this, so there is never a moment
   where both are on screen. */

/* The merchant ops pages used to draw their own navigation column
   inside the rail. With that gone the grid is a single column: the rail
   is the app's, not the page's. */
.hx-ops--single { display: block; }
