/* ==========================================================================
   AI Horizon — design system
   Rebuilt against technolife.com, measured element-by-element in the browser
   (computed styles at 1440px). Polish pass: labels, targets, cart, lockups, fixed navy surfaces. Palette, radii, type scale and spacing below
   are the reference site's own values, applied to this shop's markup.
   Layers keep specificity predictable.
   ========================================================================== */
@layer tokens, base, layout, components, utilities;

@layer tokens {
  /* Technolife palette, extracted from computed styles:
     Red primary      #DA1E28   (buttons, active sort, prices accent)
     Red dark         #A2191F   (discount badge bg, hover)
     Red deep         #750E13   (countdown text)
     Wine section     #520408   (deals container)
     Navy text        #223C78   (login border, nav accents, headings)
     Navy surface     #1B3570   (footer, primary navy buttons)
     Sky              #14A0DE   (links, focus)
     Page bg          #FCFEFF
     Field gray       #F0F0F0   (search box, hover fills)
     Border           #D7DEE0   (0.8px in header)
     Text             #333333 / #161616; soft #666666; mute #9C9D9E; unit #8D8D8D
  */
  :root {
    color-scheme: light;

    --brand: #223c78;
    --brand-ink: #1b3570;
    --brand-deep: #16295c;
    --accent-blue: #14a0de;
    --accent-blue-hover: #0f8ac2;
    --discount-red: #da1e28;
    --discount-red-dark: #d00e0e;
    --discount-red-deep: #750e13;
    --wine: #dc0001;
    --badge-dark: #393939;
    --discount-red-bg: #fdecee;

    --bg: #fcfeff;
    --bg-deep: #f5f7f8;
    --surface: #ffffff;
    --surface-2: #f0f0f0;
    --surface-3: #f7f8fa;
    --card: #ffffff;
    --sheet: #ffffff;
    --line: #d7dee0;
    --line-soft: #ebedf0;
    --line-strong: #b9c4c8;

    --primary-action: #1b3570;
    --primary-action-hover: #16295c;

    --ring-blue: #d3d8e4;
    /* AA on the light surfaces. --text-mute/#9c9d9e measured 2.7:1 and
       --text-unit/#8d8d8d 3.4:1, both under the 4.5:1 floor for the 12-13px
       text they are used for. */
    --text: #333333;
    --text-strong: #161616;
    --text-soft: #5f6266;
    --text-mute: #6c7075;
    --text-unit: #6f6f6f;
    /* --accent-blue (#14a0de) is 2.9:1 on white, so it stays a border/ring
       colour and a hover accent; text links get the darker pair. */
    --link: #0f7bab;
    --link-hover: #0b5f85;

    --emerald: #0a7a3e;
    --emerald-bg: #e8f7ef;
    --gold: #a15b06;
    --gold-bg: #fdf3e0;
    --rose: #c81621;
    --rose-bg: #fdecee;

    --ring: rgba(20, 160, 222, 0.18);
    --shadow-sm: 0 1px 4px rgba(23, 46, 84, 0.06);
    --shadow-md: 0 4px 16px rgba(23, 46, 84, 0.08);
    --shadow-lg: 0 8px 40px rgba(23, 46, 84, 0.14);
    --shadow-pop: 0 4px 24px rgba(0, 0, 0, 0.1), 0 12px 48px rgba(0, 0, 0, 0.08);
  }

  /* Shared, theme-independent scales. Reference is a fixed-px system; the two
     clamps only guard the page gutter and the base step on tiny phones. */
  :root {
    --radius-xs: 4px;
    --radius-sm: 8px;
    --radius-md: 12px;
    --radius-lg: 16px;
    --radius-pill: 999px;

    --fs-xs: 12px;
    --fs-sm: 13px;
    --fs-md: 14px;
    --fs-base: 15px;
    --fs-lg: 16px;
    --fs-xl: 18px;
    --fs-2xl: 20px;
    --fs-3xl: 24px;

    --pad: clamp(1rem, 0.55rem + 1.75vw, 2rem);
    --gap: 1rem;
    --shell: 1382px;

    /* Vertical rhythm, one scale instead of the 4/6/8/10/12/14/16/20/24 spread. */
    --sp-1: 4px;
    --sp-2: 8px;
    --sp-3: 12px;
    --sp-4: 16px;
    --sp-5: 24px;
    --sp-6: 32px;
    --sp-7: 48px;

    /* The header's real height. The tiers add up to these numbers, but the
       mobile bar wraps differently at every width, so app.js overwrites
       --header-h with the measured height; these are the no-script fallbacks.
       Everything that sticks or anchors below the header reads this. */
    --header-h: 173px;
    /* Height the fixed bottom bar steals from the viewport. It already carries
       the home-indicator inset, so nothing adds it a second time. */
    --nav-h: env(safe-area-inset-bottom, 0px);
    --topbar-h: 4.25rem;

    --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
    --dur: 0.2s;
  }

  /* Below the 62rem line the category row is gone, the search moves into its own
     band and the bottom nav takes over primary navigation. */
  @media (width < 62rem) {
    :root {
      --header-h: 199px;
      --nav-h: calc(56px + env(safe-area-inset-bottom, 0px));
    }
  }

  /* The dark skin: same layout, night surfaces. Kept as a feature of the shop;
     the reference site ships light-only, so this maps the same components onto
     a dark field without changing any dimensions. */
  [data-theme="dark"] {
    color-scheme: dark;

    --brand: #7d9bff;
    --brand-ink: #9db4ff;
    --brand-deep: #3350a0;
    --accent-blue: #38bdf8;
    --accent-blue-hover: #7dd3fc;

    --primary-action: #da1e28;
    --primary-action-hover: #a2191f;

    --discount-red: #da1e28;
    --discount-red-dark: #d00e0e;
    --discount-red-deep: #f87171;
    --wine: #bb1b1b;
    --badge-dark: #2b2f36;
    --discount-red-bg: rgba(218, 30, 40, 0.18);

    --bg: #0d1220;
    --bg-deep: #090d18;
    --surface: #161d2f;
    --surface-2: #1e2740;
    --surface-3: #1a2238;
    --card: #161d2f;
    --sheet: #161d2f;
    --line: rgba(255, 255, 255, 0.1);
    --line-soft: rgba(255, 255, 255, 0.06);
    --line-strong: rgba(255, 255, 255, 0.22);

    --ring-blue: #33415e;
    --text: #e8ecf5;
    --text-strong: #ffffff;
    --text-soft: #b6c0d4;
    --text-mute: #8e9ab1;
    --text-unit: #9aa6bc;
    --link: #5cc6f5;
    --link-hover: #8ed9fa;

    --emerald: #34d399;
    --emerald-bg: rgba(52, 211, 153, 0.12);
    --gold: #fbbf24;
    --gold-bg: rgba(251, 191, 36, 0.12);
    --rose: #f87171;
    --rose-bg: rgba(248, 113, 113, 0.12);

    --ring: rgba(56, 189, 248, 0.25);
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
    --shadow-md: 0 8px 24px -6px rgba(0, 0, 0, 0.5);
    --shadow-lg: 0 20px 48px -10px rgba(0, 0, 0, 0.6);
    --shadow-pop: 0 12px 48px rgba(0, 0, 0, 0.55);
  }
}

@layer base {
  *, *::before, *::after { box-sizing: border-box; }

  * { margin: 0; padding: 0; }

  html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
    scroll-padding-block-start: calc(var(--header-h) + 1rem);
    /* `clip`, not `hidden`, on both boxes. `overflow-x: hidden` turns the
       element into a scroll container, and a sticky descendant then resolves
       against a box that never scrolls: the header silently stopped sticking.
       `clip` hides the same overflow without creating the scroll container. */
    overflow-x: clip;
    /* Reserve the gutter up front, so a page that grows past one screen no
       longer shifts every width by the scrollbar's 15px -- and the
       viewport-width breakouts stay centred instead of ending up off by half
       a gutter. */
    scrollbar-gutter: stable;
  }

  body {
    font-family: Vazirmatn, "Segoe UI", system-ui, sans-serif;
    font-size: var(--fs-base);
    line-height: 1.65;
    color: var(--text);
    min-height: 100dvh;
    overflow-x: clip;
    font-variant-numeric: tabular-nums;
    -webkit-font-smoothing: antialiased;
    background-color: var(--bg);
    /* Room for the fixed bottom bar; it is 0 until the mobile breakpoint. */
    padding-block-end: var(--nav-h);
  }

  img, svg, video { display: block; max-width: 100%; height: auto; }

  a { color: inherit; text-decoration: none; }

  button, input, select, textarea { font: inherit; color: inherit; }

  button { background: none; border: 0; cursor: pointer; }

  ul, ol { list-style: none; }

  h1, h2, h3, h4 { line-height: 1.45; font-weight: 700; color: var(--text); }

  [hidden] { display: none !important; }

  /* No border-radius here: it re-shaped every pill and circle it touched, so
     focusing a round dot or a 999px pill visibly squared it off. */
  :focus-visible {
    outline: 2px solid var(--accent-blue);
    outline-offset: 2px;
  }

  ::selection { background: rgba(20, 160, 222, 0.25); }

  ::-webkit-scrollbar { width: 10px; height: 10px; }
  ::-webkit-scrollbar-track { background: transparent; }
  ::-webkit-scrollbar-thumb {
    background: var(--line-strong);
    border-radius: var(--radius-pill);
    border: 2px solid transparent;
    background-clip: content-box;
  }

  @media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    *, *::before, *::after {
      animation-duration: 0.001ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: 0.001ms !important;
    }
  }

  @keyframes vt-out { to { opacity: 0; translate: 0 -6px; } }
  @keyframes vt-in { from { opacity: 0; translate: 0 10px; } }
}

/* `@view-transition` is only recognised at the top level of a stylesheet, so it
   sits outside the layer stack rather than inside `base` where it was ignored. */
@view-transition { navigation: auto; }
::view-transition-old(page) { animation: vt-out 0.18s var(--ease-out) both; }
::view-transition-new(page) { animation: vt-in 0.26s var(--ease-out) both; }

@layer layout {
  .wrap {
    width: min(100% - 2 * var(--pad), var(--shell));
    margin-inline: auto;
    /* Landscape phone notches and rounded corners eat the first few px. */
    padding-inline: max(0px, env(safe-area-inset-left, 0px));
  }

  .skip {
    position: fixed;
    inset-block-start: 0.5rem;
    inset-inline-start: 50%;
    translate: 50% -200%;
    /* Above the header (120) and the bottom bar (100): at 90 it tied with the
       header and painted under it, so the link that appears on Tab was the
       one thing on the page you could not see. */
    z-index: 300;
    padding: 0.6rem 1.1rem;
    border-radius: var(--radius-sm);
    background: var(--brand-ink);
    color: #ffffff;
    font-weight: 600;
    transition: translate var(--dur) var(--ease-out);
  }
  .skip:focus-visible { translate: 50% 0; }

  /* ==========================================================================
     Header — three tiers, exactly the reference: campaign banner (44px),
     main bar (search 56px + identity buttons 40px), category row (52px).
     ========================================================================== */
  .site-header {
    position: sticky;
    inset-block-start: 0;
    /* The popovers inside are z-index 120, but a stacking context makes them
       local: at 90 the whole header sat under the fixed bottom bar (100) and a
       long search result list disappeared behind it. */
    z-index: 120;
    /* One field of colour from the top of the page down to the first banner:
     the pale grey the search box used to sit in is now the header itself, and
     the controls -- search, cart, theme, menu -- are the white islands on it.
     No bottom border either; the grey simply ends where the slider starts. */
    background: var(--bg-deep);
    border-block-end: 0;
  }
  .site-header[data-stuck="1"] { box-shadow: var(--shadow-sm); }

  /* Campaign banner — the reference runs a 44px image strip over a teal
     gradient. Ours is live text over the same gradient family, held deep enough
     that the white copy clears 4.5:1 across the whole strip: the old right-hand
     end (#1fc3ae) put it at 2.0:1 exactly where the sentence lands. */
  .festival-banner {
    background: linear-gradient(90deg, #08609c 0%, #0a7396 45%, #0b7a72 100%);
    color: #ffffff;
    block-size: 44px;
    display: flex;
    align-items: center;
  }
  .festival-link {
    flex: 1;
    /* stretch: the tappable area is the whole 44px strip, not the
       26px of text floating inside it. */
    align-self: stretch;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.75rem;
    color: #ffffff;
    min-inline-size: 0;
    padding-inline: var(--pad);
  }
  .festival-pill {
    background: #ffffff;
    color: #0a6f8c;
    font-weight: 700;
    font-size: 12px;
    line-height: 1.35;
    padding: 5px 12px;
    border-radius: var(--radius-pill);
    white-space: nowrap;
  }
  .festival-text {
    font-weight: 500;
    font-size: 15px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .festival-cta {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 12px;
    font-weight: 600;
    color: #eafdFB;
    white-space: nowrap;
    transition: gap var(--dur);
  }
  .festival-cta::after { content: "‹"; font-size: 14px; line-height: 1; }
  .festival-link:hover .festival-cta { gap: 8px; }
  @media (width < 48rem) {
    .festival-text { font-size: 13px; }
    .festival-cta { display: none; }
  }

  /* Main bar — 40px identity buttons, 56px search, logo left(size of ref 110x42) */
  .topbar { padding-block: 10px; background: none; }
  .topbar > .wrap {
    display: flex;
    align-items: center;
    gap: 16px;
    min-block-size: 56px;
  }

  /* Search box: a white field on the grey header, 56px tall, 24px icon on the
     far (inline-end) side — the same island as the cart and theme buttons. */
  .topsearch-wrapper {
    position: relative;
    flex: 1 1 auto;
    max-inline-size: 652px;
    margin-inline-start: clamp(0rem, 0rem + 2vw, 1rem);
  }
  .topsearch {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 0 24px;
    border-radius: var(--radius-xs);
    background: var(--surface);
    color: var(--text-mute);
    block-size: 56px;
    transition: box-shadow var(--dur), background var(--dur);
  }
  .topsearch:focus-within {
    background: var(--surface);
    box-shadow: 0 0 0 1px var(--line) inset, 0 2px 12px rgba(23, 46, 84, 0.08);
  }
  .topsearch > svg {
    inline-size: 24px;
    block-size: 24px;
    flex-shrink: 0;
    color: var(--text-mute);
    order: 2;
  }
  .topsearch-input {
    flex: 1;
    block-size: 100%;
    min-inline-size: 0;
    border: 0;
    background: none;
    color: var(--text-strong);
    font-size: 15px;
    font-family: inherit;
    order: 1;
  }
  .topsearch-input::placeholder { color: var(--text-mute); }
  .topsearch-input:focus { outline: none; }
  .topsearch-input::-webkit-search-cancel-button { display: none; }

  /* Search dropdown */
  .topsearch-dropdown {
    position: absolute;
    inset-block-start: calc(100% + 8px);
    inset-inline: 0;
    background: var(--surface);
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-pop);
    padding: 16px;
    z-index: 120;
    display: none;
    /* Eight live matches plus the tag cloud already passes a short phone's
       viewport; without a cap the list simply ran off the bottom of the screen. */
    max-block-size: min(70dvh, 30rem);
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  /* The panel belongs to the search field, not to everything sharing its row:
     the mobile theme button lives in this wrapper too, and on `wrapper:focus-within`
     a tap on that button focused it and opened the suggestion panel.
     `is-open` is what carries the open state across the hand-off from the field
     to a result: at pure CSS the panel would hide the instant the field blurred,
     and a link inside a hidden panel cannot be tabbed into. The :focus-within
     half stays so the panel still works with no script. */
  .topsearch:focus-within ~ .topsearch-dropdown,
  .topsearch-dropdown.is-open {
    display: block;
    animation: dropdownFadeIn 0.15s ease-out;
  }
  @keyframes dropdownFadeIn {
    from { opacity: 0; transform: translateY(-4px); }
    to { opacity: 1; transform: translateY(0); }
  }
  .search-popular-title {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-soft);
    margin-block-end: 10px;
  }
  .search-popular-tags { display: flex; flex-wrap: wrap; gap: 8px; }
  .search-tag {
    display: inline-flex;
    align-items: center;
    min-block-size: 36px;
    padding: 0 12px;
    border-radius: var(--radius-sm);
    background: var(--surface-2);
    font-size: 13px;
    color: var(--text-soft);
    transition: color var(--dur), background var(--dur);
  }
  .search-tag:hover { color: var(--rose); background: var(--rose-bg); }

  /* Live search matches */
  .search-live-matches { display: flex; flex-direction: column; gap: 2px; margin-block-start: 10px; }
  .search-live-matches:not(:empty) { padding-block-start: 10px; border-block-start: 1px solid var(--line-soft); }
  .search-live-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px;
    border-radius: var(--radius-sm);
    transition: background var(--dur);
  }
  .search-live-item:hover { background: var(--surface-2); }
  .search-live-thumb {
    inline-size: 48px;
    block-size: 48px;
    border-radius: var(--radius-xs);
    object-fit: contain;
    background: var(--surface-3);
    border: 1px solid var(--line-soft);
    flex-shrink: 0;
    padding: 2px;
  }
  .search-live-fallback {
    inline-size: 48px;
    block-size: 48px;
    flex-shrink: 0;
    border-radius: var(--radius-xs);
    background: var(--surface-2);
    color: var(--brand);
    display: grid;
    place-items: center;
    font-size: 13px;
    font-weight: 700;
  }
  .search-live-info { display: flex; flex-direction: column; gap: 2px; min-inline-size: 0; }
  .search-live-name {
    font-size: 14px;
    font-weight: 500;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .search-live-price { font-size: 13px; color: var(--text-soft); font-weight: 600; }
  .search-live-all {
    display: block;
    margin-block-start: 8px;
    padding-block: 8px 2px;
    border-block-start: 1px solid var(--line-soft);
    font-size: 13px;
    font-weight: 600;
    color: var(--link);
    text-align: center;
  }
  .search-live-all:hover { color: var(--link-hover); text-decoration: underline; }
  .search-no-match {
    color: var(--text-mute);
    font-size: 13px;
    text-align: center;
    padding: 10px 0;
  }

  /* Identity buttons — 40px tall, 8px radius, 0.8px borders (rendered 1px) */
  .techno-auth-wrap { flex-shrink: 0; }
  /* One bordered pill, two independent links: ورود fills navy on hover,
     ثبت‌نام fills dark red — each half answers for itself. */
  .techno-login-btn {
    display: inline-flex;
    align-items: center;
    block-size: 40px;
    border: 1px solid var(--brand);
    border-radius: var(--radius-sm);
    background: var(--surface);
    overflow: hidden;
  }
  .tl-btn-item {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    block-size: 100%;
    min-inline-size: 82px;
    padding: 0 16px;
    color: var(--text);
    font-weight: 500;
    font-size: 15px;
    line-height: 1.3;
    transition: background var(--dur), color var(--dur);
  }
  /* Pointer only: a tap must not leave a half of the pill filled navy. */
  @media (hover: hover) and (pointer: fine) {
    .tl-btn-item.tl-login:hover { background: #1b3570; color: #ffffff; }
    .tl-btn-item.tl-register:hover { background: #a2191f; color: #ffffff; }
  }
  .tl-btn-item:active { background: var(--surface-2); }
  .tl-btn-sep {
    inline-size: 1px;
    block-size: 16px;
    background: var(--line);
  }

  /* Profile button + popover */
  .techno-profile-wrap { position: relative; flex-shrink: 0; }
  .techno-profile-btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    block-size: 40px;
    padding: 0 12px;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text);
    font-weight: 500;
    font-size: 14px;
    transition: background var(--dur), border-color var(--dur);
  }
  .techno-profile-btn:hover { border-color: var(--brand); }
  .techno-profile-btn > svg { inline-size: 16px; block-size: 16px; color: var(--text-mute); }
  .tl-avatar {
    inline-size: 26px;
    block-size: 26px;
    border-radius: 50%;
    background: var(--surface-2);
    color: var(--text-soft);
    display: grid;
    place-items: center;
  }
  .tl-avatar svg { inline-size: 15px; block-size: 15px; }
  .tl-user-name {
    max-inline-size: 9rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .techno-profile-popover {
    position: absolute;
    inset-block-start: calc(100% + 8px);
    inset-inline-start: 0;
    inline-size: 264px;
    background: var(--surface);
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-pop);
    padding: 6px 0;
    z-index: 120;
    display: none;
  }
  /* Pointer only, plus focus: on a touchscreen a menu that opens on hover and
     closes on nothing is a menu that never closes. */
  @media (hover: hover) and (pointer: fine) {
    .techno-profile-wrap:hover .techno-profile-popover {
      display: block;
      animation: dropdownFadeIn 0.15s ease-out;
    }
  }
  .techno-profile-wrap:focus-within .techno-profile-popover {
    display: block;
    animation: dropdownFadeIn 0.15s ease-out;
  }
  .tl-pop-head {
    padding: 12px 16px;
    border-block-end: 1px solid var(--line-soft);
    margin-block-end: 4px;
  }
  .tl-pop-head b { display: block; font-size: 14px; font-weight: 700; color: var(--text); }
  .tl-pop-email { display: block; font-size: 12px; color: var(--text-soft); direction: ltr; text-align: end; margin-block-start: 2px; }
  .tl-pop-links { display: flex; flex-direction: column; padding-block: 2px; }
  .tl-pop-links a,
  .tl-pop-logout {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 11px 16px;
    min-block-size: 44px;
    color: var(--text);
    font-size: 14px;
    font-weight: 500;
    border: 0;
    background: none;
    width: 100%;
    text-align: start;
    cursor: pointer;
    font-family: inherit;
    transition: background var(--dur);
  }
  .tl-pop-links a:hover, .tl-pop-logout:hover { background: var(--surface-2); }
  .tl-pop-links a svg, .tl-pop-logout svg {
    inline-size: 20px;
    block-size: 20px;
    color: var(--text-soft);
    flex-shrink: 0;
  }
  .tl-pop-links .badge {
    margin-inline-start: auto;
    background: var(--discount-red);
    color: #ffffff;
    border: 0;
    min-inline-size: 18px;
    block-size: 18px;
    padding: 0 5px;
    border-radius: var(--radius-pill);
    font-size: 11px;
    display: inline-grid;
    place-items: center;
  }
  .tl-pop-div { height: 1px; background: var(--line-soft); margin: 4px 0; }
  .tl-pop-logout { color: var(--rose); }
  .tl-pop-logout svg { color: var(--rose); }
  .tl-pop-logout:hover { background: var(--rose-bg); }

  /* Cart box — 40x40, 0.8px border, 8px radius (measured) */
  .techno-cart-wrap { position: relative; flex-shrink: 0; }
  .techno-cart-box {
    display: grid;
    place-items: center;
    inline-size: 40px;
    block-size: 40px;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text);
    position: relative;
    transition: border-color var(--dur), color var(--dur);
  }
  .techno-cart-box:hover { border-color: var(--brand); color: var(--brand); }
  .techno-cart-box svg { inline-size: 22px; block-size: 22px; }
  .tl-cart-badge {
    position: absolute;
    inset-block-start: -7px;
    inset-inline-start: -7px;
    min-inline-size: 19px;
    block-size: 19px;
    padding-inline: 4px;
    border-radius: var(--radius-pill);
    background: var(--discount-red);
    color: #ffffff;
    font-size: 11px;
    font-weight: 700;
    display: grid;
    place-items: center;
    /* Ring matches the header field, not a white card: the badge sits on
       the grey, so a white ring read as a halo. */
    border: 2px solid var(--bg-deep);
  }
  .techno-cart-popover {
    position: absolute;
    inset-block-start: calc(100% + 8px);
    inset-inline-end: 0;
    inline-size: 320px;
    background: var(--surface);
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-pop);
    padding: 16px;
    z-index: 120;
    display: none;
  }
  /* Hover and focus only: a tap on the cart link must not latch the preview
     open -- the panel lives exactly as long as the cursor is over the cart, and
     a keyboard user gets it by focusing the button. */
  @media (hover: hover) and (pointer: fine) {
    .techno-cart-wrap:hover .techno-cart-popover {
      display: block;
      animation: dropdownFadeIn 0.15s ease-out;
    }
  }
  .techno-cart-wrap:focus-within .techno-cart-popover {
    display: block;
    animation: dropdownFadeIn 0.15s ease-out;
  }
  /* Not on the two pages the basket already fills. Tapping the cart button moves
     to /cart but leaves the focus there, so the preview of the basket stayed open
     over the full basket -- and it can sit on /checkout too, where it hides the
     line a payment is missing. `#view` carries the route name, so this follows
     the page rather than the pointer. */
  body:has(#view[data-view="cart"]) .techno-cart-popover,
  body:has(#view[data-view="checkout"]) .techno-cart-popover {
    display: none;
  }
  .tl-cp-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding-block-end: 10px;
    border-block-end: 1px solid var(--line-soft);
    font-size: 14px;
    font-weight: 700;
  }
  .tl-cp-count { color: var(--text-mute); font-weight: 500; font-size: 12px; }
  .tl-cp-body { padding-block: 10px; max-block-size: 256px; overflow-y: auto; }
  .tl-cp-empty { color: var(--text-mute); font-size: 13px; text-align: center; margin: 8px 0; }
  .tl-cp-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 0;
    border-block-end: 1px solid var(--line-soft);
  }
  .tl-cp-item:last-child { border-block-end: none; }
  .tl-cp-thumb {
    inline-size: 52px;
    block-size: 52px;
    border-radius: var(--radius-xs);
    object-fit: contain;
    background: var(--surface-3);
    border: 1px solid var(--line-soft);
    flex-shrink: 0;
    padding: 2px;
  }
  .tl-cp-thumb-fallback {
    inline-size: 52px;
    block-size: 52px;
    border-radius: var(--radius-xs);
    background: var(--surface-2);
    color: var(--brand);
    display: grid;
    place-items: center;
    font-size: 12px;
    font-weight: 700;
    flex-shrink: 0;
  }
  .tl-cp-info { display: flex; flex-direction: column; gap: 2px; min-inline-size: 0; flex: 1; }
  .tl-cp-title {
    font-size: 13px;
    font-weight: 500;
    color: var(--text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .tl-cp-price { font-size: 13px; color: var(--text-soft); font-weight: 600; }
  .tl-cp-more { font-size: 12px; color: var(--text-mute); text-align: center; margin-block-start: 6px; }
  .tl-cp-foot {
    border-block-start: 1px solid var(--line-soft);
    padding-block-start: 10px;
    display: grid;
    gap: 10px;
  }
  .tl-cp-total {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    font-size: 13px;
    color: var(--text-soft);
  }
  .tl-cp-total b { color: var(--text-strong); font-size: 15px; font-weight: 700; }

  /* Header icon buttons (theme, messages) — same 40x8 family as the cart box */
  .topbar .icon-btn {
    inline-size: 40px;
    block-size: 40px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--line);
    background: var(--surface);
    color: var(--text);
    display: grid;
    place-items: center;
    position: relative;
    flex-shrink: 0;
    transition: border-color var(--dur), color var(--dur);
  }
  .topbar .icon-btn:hover { border-color: var(--brand); color: var(--brand); }
  .topbar .icon-btn svg { inline-size: 22px; block-size: 22px; }

  /* Mobile header: the identity row sticks, and below it the search band — no
     field of its own any more, since the whole header is that pale grey now.
     The controls on it (search, theme, cart, menu) stay white. */
  @media (width < 62rem) {
    .topbar > .wrap { flex-wrap: wrap; column-gap: 10px; }
    /* The grey has to run all the way to the first banner, so the air under the
       search row belongs to the header, not to a margin on the slider. */
    .site-header { padding-block-end: 12px; }
    .topbar .push { display: none; }
    /* The cart and messages sit in the far corner, clear of the logo. */
    .topbar .brand { margin-inline-end: auto; }
    /* The row-one switch stands down; the band carries its own. */
    .topbar .wrap > .icon-btn[data-action="theme"] { display: none; }
    .topsearch-wrapper {
      order: 10;
      flex: 1 1 100%;
      display: flex;
      align-items: center;
      gap: 10px;
      max-inline-size: none;
      margin-inline-start: 0;
      padding-block: 2px;
    }
    .topsearch-wrapper .topsearch {
      flex: 1 1 auto;
      block-size: 46px;
      border-radius: 10px;
      background: var(--surface);
      box-shadow: 0 1px 8px rgba(23, 46, 84, 0.08);
    }
    .topsearch-wrapper .icon-btn {
      inline-size: 42px;
      block-size: 42px;
      border-radius: 12px;
      background: var(--surface);
      border: 1px solid var(--line-soft);
      flex-shrink: 0;
    }
    .topsearch-wrapper .topsearch > svg { inline-size: 20px; block-size: 20px; }
  }

  /* Category row — 52px items, 16px/500 #666, 24px icons, hover #F0F0F0.
     Transparent: it is part of the one grey header field now. */
  .catbar {
    background: none;
    /* Gets out of the way while the reader scrolls down into the page and comes
       back the moment they scroll up: the row is a way into the catalogue, not
       something to look at while reading it. Collapsing the header also moves
       --header-h, which app.js re-measures, so the sticky buy box and the
       in-page anchors follow without knowing about this at all. */
    max-block-size: 52px;
    transition: max-block-size 0.22s var(--ease-out);
  }
  .site-header[data-hide-cats="1"] .catbar {
    max-block-size: 0;
    overflow: hidden;
  }
  /* The mega-menu is anchored to the page container rather than to its own
     trigger, so it can be sized in percentages instead of `100vw` -- which is
     measured over the scrollbar and cut the panel's last column off. */
  .catbar > .wrap {
    position: relative;
    display: flex;
    align-items: center;
    gap: 8px;
    min-block-size: 52px;
  }
  .catbar-megamenu-wrap { position: static; }
  .catbar-all-btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 14px 12px;
    border: 0;
    background: none;
    color: var(--text-soft);
    font-weight: 500;
    font-size: 16px;
    line-height: 1.5;
    font-family: inherit;
    cursor: pointer;
    border-radius: 6px;
    white-space: nowrap;
    transition: background var(--dur), color var(--dur);
  }
  .catbar-all-btn:hover, .catbar-all-btn[aria-expanded="true"] {
    color: var(--text-strong);
    background: var(--surface-2);
  }
  .catbar-all-btn svg { inline-size: 22px; block-size: 22px; }
  .catbar-all-btn svg:last-child { inline-size: 16px; block-size: 16px; }

  .megamenu-dropdown {
    position: absolute;
    inset-block-start: 100%;
    /* Anchored to `.catbar > .wrap`, so the panel is the shelf's own width. */
    inset-inline: 0;
    max-inline-size: 72rem;
    background: var(--surface);
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-pop);
    z-index: 110;
    display: none;
    max-block-size: calc(100dvh - var(--header-h) - 1rem);
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  /* Same rule as the profile menu: hover belongs to pointers, and the tap path
     is the `.is-open` class app.js sets from the trigger button. */
  @media (hover: hover) and (pointer: fine) {
    .catbar-megamenu-wrap:hover .megamenu-dropdown {
      display: block;
      animation: dropdownFadeIn 0.15s ease-out;
    }
  }
  .catbar-megamenu-wrap:focus-within .megamenu-dropdown,
  .megamenu-dropdown.is-open {
    display: block;
    animation: dropdownFadeIn 0.15s ease-out;
  }
  .megamenu-panel { padding: 20px; }
  .megamenu-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding-block-end: 12px;
    margin-block-end: 12px;
    border-block-end: 1px solid var(--line-soft);
    font-weight: 700;
    font-size: 15px;
    color: var(--text-strong);
  }
  .megamenu-all-link {
    color: var(--link);
    font-size: 13px;
    font-weight: 500;
  }
  .megamenu-all-link:hover { color: var(--link-hover); text-decoration: underline; }
  .megamenu-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 4px;
  }
  .megamenu-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    border-radius: var(--radius-sm);
    color: var(--text);
    transition: background var(--dur);
  }
  .megamenu-item:hover { background: var(--surface-2); }
  .megamenu-glyph { font-size: 20px; line-height: 1; }
  .megamenu-name { font-weight: 500; font-size: 14px; flex: 1; }
  .megamenu-count { font-size: 12px; color: var(--text-mute); }
  .megamenu-item > svg:last-child { inline-size: 14px; block-size: 14px; color: var(--text-mute); }

  .catbar-nav {
    display: flex;
    align-items: center;
    gap: 2px;
    min-inline-size: 0;
    /* `overflow: hidden` cut the row dead: at 992px the last 97px of links were
       simply gone, with nothing to scroll them into view. Scrollable now, with
       a fade that says there is more the other way. */
    overflow-x: auto;
    scrollbar-width: none;
    overscroll-behavior-x: contain;
    scroll-padding-inline: 8px;
    mask-image: linear-gradient(90deg, transparent, #000 12px, #000 calc(100% - 12px), transparent);
  }
  .catbar-nav::-webkit-scrollbar { display: none; }
  .catbar-nav > * { flex-shrink: 0; }
  .catbar-link {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 14px 12px;
    border-radius: 6px;
    color: var(--text-soft);
    font-weight: 500;
    font-size: 16px;
    line-height: 1.5;
    white-space: nowrap;
    transition: background var(--dur), color var(--dur);
  }
  .catbar-link:hover { color: var(--text-strong); background: var(--surface-2); }
  .catbar-link.is-active { color: var(--brand); font-weight: 600; }
  .catbar-link svg { inline-size: 22px; block-size: 22px; flex-shrink: 0; }
  .catbar-link .nav-badge {
    background: #ffe8ec;
    color: var(--rose);
    font-size: 11.5px;
    font-weight: 700;
    line-height: 1;
    padding: 3px 7px;
    border-radius: var(--radius-pill);
  }
  /* The pill's field has to move with the theme: #ffe8ec is a light-pink wash,
     and the night-skin text red (#f87171) over it fell to 2.4:1. */
  [data-theme="dark"] .catbar-link .nav-badge {
    background: rgba(248, 113, 113, 0.16);
  }
  .catbar-special-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 14px 12px;
    font-size: 14px;
    font-weight: 600;
    color: var(--brand);
    white-space: nowrap;
    border-radius: 6px;
    transition: color var(--dur);
  }
  .catbar-special-link:hover { color: var(--brand-deep); }
  .catbar-special-link svg { inline-size: 18px; block-size: 18px; }

  /* Mobile bottom nav — white bar, red active (the reference app pattern) */
  .mobile-bottom-nav {
    position: fixed;
    inset-block-end: 0;
    inset-inline: 0;
    block-size: calc(56px + env(safe-area-inset-bottom, 0px));
    padding-block-end: env(safe-area-inset-bottom, 0px);
    background: var(--surface);
    border-block-start: 1px solid var(--line-soft);
    display: flex;
    align-items: stretch;
    z-index: 100;
  }
  .mb-nav-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    color: var(--text-soft);
    /* 10px Persian is not a label, it is texture; 11px with the softer grey
       reads and still clears AA on the bar's white field. */
    font-size: 11px;
    font-weight: 500;
    flex: 1;
    border: 0;
    background: none;
    cursor: pointer;
    font-family: inherit;
    transition: color var(--dur), background var(--dur);
  }
  .mb-nav-item:active { background: var(--surface-2); }
  .mb-nav-item.is-active { color: var(--rose); }
  .mb-nav-item svg { inline-size: 22px; block-size: 22px; }
  .mb-cart-icon-wrap { position: relative; display: inline-flex; }
  .mb-badge {
    top: -6px;
    inset-inline-start: -10px;
    min-inline-size: 16px;
    block-size: 16px;
    font-size: 10px;
    border: 1px solid var(--surface);
  }

  .site-main {
    padding-block: var(--sp-4) clamp(3rem, 2rem + 4vw, 5rem);
    min-block-size: 60vh;
  }
  /* The slider is a full-bleed band: on the home page it starts flush under the
     category row, with none of the body's usual top padding in between. */
  .site-main:has(> .hero:first-child) { padding-block-start: 0; }
  #alerts:not(:empty) { margin-block-start: var(--sp-3); }

  /* One rhythm for every band on the page: 24px was a desktop number applied at
     every width, so a phone got the same gap between a shelf and its heading as
     a 1440px monitor. */
  .section { margin-block-start: clamp(var(--sp-5), 1rem + 2.5vw, var(--sp-6)); }
  .section:first-child { margin-block-start: 0; }

  .section-head {
    display: flex;
    align-items: end;
    justify-content: space-between;
    gap: var(--gap);
    margin-block-end: var(--sp-4);
    flex-wrap: wrap;
  }
  .section-head :is(h1, h2) {
    font-size: var(--fs-2xl);
    font-weight: 700;
    color: var(--text-strong);
    text-wrap: balance;
  }
  .section-head p { color: var(--text-soft); font-size: 14px; margin-block-start: 4px; }

  .grid-cards {
    display: grid;
    gap: var(--sp-3);
    /* 200px rather than 228: one more column across a laptop, and a card that
       holds its own instead of sprawling. Below a phone's two columns the grid
       is left to the `:has` rule under it. */
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 200px), 1fr));
  }
  @media (width < 34rem) {
    .grid-cards:has(> .card) { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-2); }
    /* The four promise blurbs under the hero: one per line turned the home page
       into a scroll that never ended, so they go two-and-two like the reference.
       Their own type and padding come down with them in the `.feature` block. */
    .grid-cards:has(> .feature) { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-2); }
  }

  .grid-2 {
    display: grid;
    gap: var(--sp-3);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
  }

  .split {
    display: grid;
    gap: var(--sp-4);
    align-items: start;
  }
  @media (width >= 62rem) {
    .split { grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr); }
    .split.narrow-end { grid-template-columns: minmax(0, 1fr) 21rem; }
  }

  .stack { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--sp-4); }
  .stack-sm { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--sp-2); }
  .row { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
  .row-tight { display: flex; align-items: center; gap: 6px; }
  .push { margin-inline-start: auto; }
}

@layer components {
  /* --- brand ------------------------------------------------------------- */
  .brand { display: flex; align-items: center; flex-shrink: 0; min-block-size: 44px; }
  /* The whole lockup — cart, AI and Horizon in one picture — swaps per theme:
     navy Horizon on the light header, glowing silver on the dark one. */
  .brand-logo {
    block-size: 38px;
    flex-shrink: 0;
    background: url("/assets/logo-word-dark.d7859d3707.webp") left center / contain no-repeat;
    aspect-ratio: 1671 / 417;
  }
  [data-theme="light"] .brand-logo {
    background-image: url("/assets/logo-word-light.d7859d3707.webp");
    aspect-ratio: 1599 / 345;
  }
  @media (width < 62rem) {
    .brand-logo { block-size: 32px; }
  }

  /* --- buttons ----------------------------------------------------------- */
  /* Reference buttons: flat fills, 8px radius, no lift-on-hover. */
  .btn {
    --btn-bg: var(--surface-2);
    --btn-fg: var(--text);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    block-size: 40px;
    min-inline-size: 44px;
    padding: 0 16px;
    border-radius: var(--radius-sm);
    border: 1px solid transparent;
    background: var(--btn-bg);
    color: var(--btn-fg);
    font-weight: 600;
    font-size: 14px;
    line-height: 1.3;
    white-space: nowrap;
    /* No double-tap-zoom wait on the site's most repeated control. */
    touch-action: manipulation;
    transition: background var(--dur), color var(--dur), border-color var(--dur), opacity var(--dur);
  }
  /* A dimmed-by-opacity button keeps its own colours, so white-on-navy became
     washed-out-white-on-pale-navy at ~2.5:1. Disabled now reads disabled and
     still passes. */
  .btn[disabled], .btn[aria-disabled="true"] {
    --btn-bg: var(--surface-2);
    --btn-fg: var(--text-mute);
    border-color: var(--line);
    cursor: not-allowed;
    pointer-events: none;
  }
  .btn svg { width: 18px; height: 18px; flex-shrink: 0; }
  /* Press feedback: the reference has none, but on a touch screen a button that
     does not answer the finger is a button people tap twice. */
  .btn:active:not([disabled], [aria-disabled="true"]) { transform: translateY(1px); filter: brightness(0.94); }

  .btn-primary {
    --btn-fg: #ffffff;
    --btn-bg: var(--primary-action);
  }
  @media (hover: hover) and (pointer: fine) {
    .btn-primary:hover { --btn-bg: var(--primary-action-hover); }
  }

  .btn-ghost { --btn-bg: transparent; color: var(--text-soft); }
  @media (hover: hover) and (pointer: fine) {
    .btn-ghost:hover { --btn-bg: var(--surface-2); color: var(--text-strong); }
  }

  .btn-outline {
    --btn-bg: transparent;
    border-color: var(--line);
    color: var(--text);
  }
  @media (hover: hover) and (pointer: fine) {
    .btn-outline:hover { --btn-bg: var(--surface-2); }
  }

  .btn-danger { --btn-bg: var(--rose-bg); --btn-fg: var(--rose); border-color: color-mix(in srgb, var(--rose) 30%, transparent); }
  .btn-good { --btn-bg: var(--emerald-bg); --btn-fg: var(--emerald); border-color: color-mix(in srgb, var(--emerald) 30%, transparent); }

  .btn-lg { block-size: 48px; padding: 0 24px; font-size: 15px; border-radius: var(--radius-sm); }
  .btn-sm { block-size: 36px; padding: 0 12px; font-size: 13px; gap: 4px; }
  .btn-sm svg { width: 15px; height: 15px; }
  .btn-block { width: 100%; }

  .link-btn {
    text-align: start;
    font-weight: 600;
    color: var(--text);
    text-underline-offset: 3px;
    transition: color var(--dur);
  }
  .link-btn:hover { color: var(--link-hover); text-decoration: underline; }

  /* Generic icon button outside the header */
  .icon-btn:not(.topbar .icon-btn) {
    display: inline-grid;
    place-items: center;
    inline-size: 40px;
    block-size: 40px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--line);
    background: var(--surface);
    color: var(--text-soft);
    transition: color var(--dur), border-color var(--dur);
  }
  .icon-btn:not(.topbar .icon-btn):hover { color: var(--brand); border-color: var(--brand); }
  .icon-btn:not(.topbar .icon-btn) svg { width: 20px; height: 20px; }

  /* --- chips & badges ---------------------------------------------------- */
  .chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    block-size: 38px;
    padding: 0 14px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--line);
    background: var(--surface);
    color: var(--text-soft);
    font-size: 13px;
    font-weight: 500;
    white-space: nowrap;
    transition: color var(--dur), background var(--dur), border-color var(--dur);
  }
  @media (hover: hover) and (pointer: fine) {
    .chip:hover { border-color: var(--line-strong); color: var(--text-strong); }
  }
  .chip:active { background: var(--surface-2); }
  .chip[aria-pressed="true"] {
    color: var(--rose);
    background: var(--surface);
    border-color: var(--discount-red);
    font-weight: 600;
  }

  .badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 4px 10px;
    border-radius: var(--radius-sm);
    font-size: 12px;
    font-weight: 600;
    border: 1px solid transparent;
    line-height: 1.2;
  }
  .badge svg { width: 13px; height: 13px; }
  .badge-good { color: var(--emerald); background: var(--emerald-bg); }
  .badge-warn { color: var(--gold); background: var(--gold-bg); }
  .badge-bad { color: var(--rose); background: var(--rose-bg); }
  .badge-mute { color: var(--text-soft); background: var(--surface-2); }
  .badge-brand { color: var(--brand); background: color-mix(in srgb, var(--brand) 8%, var(--surface)); }
  .badge-off {
    background: var(--discount-red);
    color: #ffffff;
    font-weight: 700;
    font-size: 12px;
    border-radius: var(--radius-sm);
    padding: 4px 10px;
  }
  .badge-hot {
    background: var(--gold-bg);
    color: var(--gold);
    font-weight: 600;
    font-size: 12px;
    border-radius: var(--radius-sm);
    padding: 4px 10px;
  }

  /* --- surfaces ---------------------------------------------------------- */
  .panel {
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-md);
    background: var(--card);
    box-shadow: var(--shadow-sm);
    padding: 20px;
  }
  .panel-tight { padding: 14px 16px; }

  .divider { height: 1px; background: var(--line-soft); margin-block: 16px; }

  /* --- hero -------------------------------------------------------------- */
  /* Full-bleed: the slider breaks out of the page container and runs edge to
     edge — no white gutters, no rounded corners — the way a campaign banner owns
     the whole width. `50% - 50vw` centres the element on the viewport and
     stretches it to the full width; body already clips overflow-x, so the bleed
     itself cannot open a scrollbar. */
  .hero {
    position: relative;
    margin-inline: calc(50% - 50vw);
  }

  .hero-slider { position: relative; overflow: hidden; }

  /* The strip is pushed along by a measured translateX and nothing about it is
     scrolled. A scroll-snap track looked like less code and was not: the browser
     held the position, so which banner was showing had to be guessed back out of a
     scroll offset that snapping, a tap or a late font could all move -- and a guess
     that came out wrong left the row sitting on a duplicate with the dots pointing
     elsewhere. Here the position is an input rather than an output. */
  .hero-track {
    display: flex;
    /* Horizontal gestures belong to the script, vertical ones to the page: without
       this the browser claims a swipe as its own scroll and cancels the pointer
       halfway through it. */
    touch-action: pan-y;
    will-change: transform;
    transition: transform 420ms cubic-bezier(0.22, 0.61, 0.36, 1);
  }
  .hero-track:focus-visible { outline: 2px solid var(--brand); outline-offset: -4px; }
  /* Set by the script for the length of a drag, so the strip follows the finger
     instead of chasing it through the transition. */
  .hero-track.is-dragging { transition: none; user-select: none; }
  @media (prefers-reduced-motion: reduce) { .hero-track { transition: none; } }

  .hero-slide {
    flex: 0 0 100%;
    display: grid;
    gap: 20px;
    align-items: center;
    padding: clamp(20px, 2.4vw, 36px) clamp(24px, 5vw, 76px);
    min-block-size: clamp(280px, 27vw, 400px);
    overflow: hidden;
  }
  @media (width >= 62rem) {
    .hero-slide { grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr); }
    /* The call to action sat on the headline's heels. On a banner this wide it can
       afford a breath between the two; the phone keeps the tight stack because
       its banner is only 1.9:1 and every pixel of height is borrowed from the
       artwork. */
    .hero-slide .btn-primary { margin-block-start: 10px; }
  }

  .slide-copy {
    display: grid;
    justify-items: start;
    gap: 12px;
    color: #ffffff;
    min-inline-size: 0;
  }
  .slide-eyebrow {
    display: inline-flex;
    align-items: center;
    padding: 6px 12px;
    border-radius: var(--radius-pill);
    background: rgba(255, 255, 255, 0.16);
    color: #ffffff;
    font-size: 13px;
    font-weight: 600;
  }
  .slide-title {
    font-size: clamp(20px, 1rem + 1.6vw, 30px);
    line-height: 1.35;
    color: #ffffff;
    font-weight: 800;
  }
  .hero-slide .btn-primary {
    --btn-bg: #ffffff;
    --btn-fg: #161616;
    block-size: 40px;
  }
  .hero-slide .btn-primary:hover { --btn-bg: #f1f3f5; }

  /* Campaign colour fields — flat, deep enough for white copy, the way the
     reference banners read as printed artwork. The fields themselves are fixed;
     which slide wears which is HERO_SLIDES' `tone` in src/shared.js — currently
     slide 1 teal (a), slide 2 violet (c), slide 3 red (b). */
  .banner-a {
    background:
      radial-gradient(80% 110% at 14% 12%, rgba(255, 255, 255, 0.12), transparent 60%),
      linear-gradient(105deg, #27a5a4 0%, #026e7b 55%, #00546f 100%);
  }
  .banner-b {
    background:
      radial-gradient(80% 110% at 14% 12%, rgba(255, 255, 255, 0.14), transparent 60%),
      linear-gradient(105deg, #e8483a 0%, #d12e2f 45%, #a00926 100%);
  }
  .banner-c {
    background:
      radial-gradient(80% 110% at 14% 12%, rgba(255, 255, 255, 0.12), transparent 60%),
      linear-gradient(105deg, #874fd8 0%, #7645c5 50%, #482b97 100%);
  }

  /* --- hero art: the orbiting logos --------------------------------------- */
  /* No card, no plate: the four service logos are the picture, riding the orbit
     straight over the slide's colour field. The orbit box is centred in the art
     column; its tiles run ~25% smaller than the previous design's. */
  .hero-art {
    position: relative;
    inline-size: min(100%, 30rem);
    margin-inline: auto;
    aspect-ratio: 1.45;
  }

  /* The orbit gets its own square box so the tiles keep circling in a circle:
     `left` resolves against width and `top` against height, so centring them in
     the landscape frame directly would have squashed the ring. */
  .art-orbit {
    position: absolute;
    left: 50%;
    top: 50%;
    translate: -50% -50%;
    inline-size: 48%;
    aspect-ratio: 1;
    /* The tiles turn in place or in orbit depending on the slide, but either way
       they all turn, so they have to share one vanishing point or the cluster
       stops looking like a single scene. */
    perspective: 1000px;
  }
  /* A laptop only. The phone's logos were measured against its own short banner and
     are right where they should be; on a desktop the same box fills the art column,
     so the whole scene steps back ten percent. Scaling the orbit box rather than the
     tiles keeps the in-place cluster and the flat ring in step with each other and
     leaves every measured gap between two logos exactly as it was, because the
     radius of both is a share of the tile. */
  @media (width >= 62rem) {
    .art-orbit { inline-size: 43.2%; }
  }

  /* Twelve tiles turn in this hero and only one slide's four are ever on screen,
     but the browser animates all twelve: each one animates a `filter`, so each one
     is its own compositor layer, and a phone that runs out of layers drops one --
     which on screen is a logo simply missing from the circle. So a slide the strip
     is not showing is parked. `is-live` is maintained by bindHeroSlider, which
     lights the seat and both of its neighbours; `is-managed` is on the same class
     for the same reason the no-JS case is served by the server's markup: without
     the script there is no strip to move, so every slide keeps its animation and
     only the first is ever in view. Parked rather than cancelled, so a slide
     resumes exactly where it stopped and its four tiles keep the quarter-turn
     spacing their delays gave them. */
  .hero-slider.is-managed .hero-slide:not(.is-live) .hero-art * {
    animation-play-state: paused;
    /* A parked animation still holds its layer for as long as this promises one. */
    will-change: auto;
  }

  /* --- the tiles that circle in place (second slide) ------------------------ */
  .hero-tile {
    position: absolute;
    /* The orbit's radius, as a share of the tile's own box. It has to be a
       variable because the keyframes travel in percentages of the tile, so
       growing a logo would otherwise widen the circle by the same amount and
       push the cluster out of the slide -- which is exactly what the phone
       sizes need to avoid. */
    /* 104%, not more: the cluster is 2*radius + tile wide, and past that
       it runs into the slide box, which clips. */
    --orbit-r: 104%;
    --orbit-d: 73.5%;
    /* Centred by offset alone - half a tile is 27.6% - because translate is
       spent on the orbit. */
    left: 22.4%;
    top: 22.4%;
    inline-size: 55.2%;
    aspect-ratio: 1;
    background: center / contain no-repeat;
    /* No shadow is baked into the artwork, so it is cast here instead and then
       follows the tile's own silhouette as it moves. */
    filter: drop-shadow(0 14px 20px oklch(0% 0 0 / 0.5));
    /* Two animations, no conflict: the orbit owns translate/scale/z-index and the
       tilt owns rotate. */
    animation:
      tile-orbit 20s linear infinite,
      tile-tilt var(--tilt, 8s) ease-in-out infinite;
    animation-delay: var(--orbit-delay, 0s), var(--tilt-delay, 0s);
    will-change: translate, scale, rotate;
  }
  [data-theme="light"] .hero-tile { filter: drop-shadow(0 14px 22px oklch(0% 0 0 / 0.26)); }

  /* Clockwise circle: top, right, bottom, left, and back to the top. Eight steps
     rather than four so the path curves instead of cutting corners. A tile also
     grows and climbs the stack as it comes forward at the bottom of the circle
     and shrinks as it goes behind at the top, which is what turns a flat shuffle
     into a carousel. The travel is written in --orbit-r / --orbit-d so a larger
     logo can keep the circle it already had.
     The scale tops out at 1.0 rather than 1.06: a front tile larger than its own
     box is what made it lean on its neighbour once the logos grew, and the depth
     still reads from the 0.88 at the back. */
  @keyframes tile-orbit {
    0% { translate: 0 calc(var(--orbit-r) * -1); scale: 0.88; z-index: 1; }
    12.5% { translate: var(--orbit-d) calc(var(--orbit-d) * -1); scale: 0.9; z-index: 2; }
    25% { translate: var(--orbit-r) 0; scale: 0.94; z-index: 3; }
    37.5% { translate: var(--orbit-d) var(--orbit-d); scale: 0.98; z-index: 4; }
    50% { translate: 0 var(--orbit-r); scale: 1; z-index: 5; }
    62.5% { translate: calc(var(--orbit-d) * -1) var(--orbit-d); scale: 0.98; z-index: 4; }
    75% { translate: calc(var(--orbit-r) * -1) 0; scale: 0.94; z-index: 3; }
    87.5% { translate: calc(var(--orbit-d) * -1) calc(var(--orbit-d) * -1); scale: 0.9; z-index: 2; }
    100% { translate: 0 calc(var(--orbit-r) * -1); scale: 0.88; z-index: 1; }
  }

  /* Mostly a Y turn with a little X and Z mixed in, which reads as a tile turning
     in the air rather than a flat picture being skewed. */
  @keyframes tile-tilt {
    0%, 100% { rotate: 0.2 1 0.08 -8deg; }
    50% { rotate: 0.2 1 0.08 8deg; }
  }

  /* A quarter of the orbit apart, so the four hold the cluster's shape while they
     travel. The timing lives on the slot and the picture lives on the tile class,
     so either set of four logos can be dropped into either motion. The tilt
     periods are deliberately unrelated to the orbit and to each other, so no two
     tiles ever move in step. */
  .spot-a { --orbit-delay: 0s; --tilt: 8s; --tilt-delay: -1.2s; }
  .spot-b { --orbit-delay: -5s; --tilt: 10.5s; --tilt-delay: -4s; }
  .spot-c { --orbit-delay: -10s; --tilt: 9.5s; --tilt-delay: -2.6s; }
  .spot-d { --orbit-delay: -15s; --tilt: 7.2s; --tilt-delay: -5.1s; }

  /* Where a slot sits when the orbit is not running, so a reader who asks for
     reduced motion - which collapses every animation to nothing - still gets the
     four logos at their four points instead of stacked in the middle. */
  @media (prefers-reduced-motion: reduce) {
    .spot-a { translate: 0 calc(var(--orbit-r) * -1); }
    .spot-b { translate: var(--orbit-r) 0; }
    .spot-c { translate: 0 var(--orbit-r); }
    .spot-d { translate: calc(var(--orbit-r) * -1) 0; }
  }

  /* --- the orbiting tiles (first and third slides) --------------------------- */
  /* One flat orbit shared by every tile on it: a real circle lying flat in space,
     tipped towards the reader, so on screen it reads as a wide shallow ellipse -
     the tiles at the sides furthest out, the far one riding high and small behind
     the middle, the near one low and large in front of it. One spin carries them
     all, so they travel as a set and never drift out of step. The third slide is
     this same orbit turned the other way. */
  .orbit-ring {
    position: absolute;
    left: 50%;
    top: 50%;
    translate: -50% -50%;
    inline-size: 46%;
    aspect-ratio: 1;
    /* How far each tile stands out from the ring's centre, as a share of the
       ring box. Unlike the in-place orbit this is independent of the tile size,
       so the loop opens without touching the logos. */
    --arm-step: 145%;
    /* How far the orbit is tipped towards the reader. It is a variable because
       the tile's counter-turn has to undo exactly this much, and the two halves
       drifting apart is what makes the logos lean as they pass the front. */
    --ring-tilt: 44deg;
    transform-style: preserve-3d;
  }

  /* Flat on would be a circle and edge-on would be a straight line; the angle is
     the view from above the plane, and it sets more than the look. A tile at the
     side of the ring and a tile at the back pass at right angles to each other,
     so their closest approach is 1.414 * radius * sin(tilt) -- at 34 degrees that
     is 0.79 of the radius, less than one logo, which is why the four read as
     touching however far the arms reached. 44 degrees puts that floor just above
     a tile. Negative, so the far half of the orbit is the top half, the way a
     solar system is always drawn. */
  .ring-sun { transform: rotateX(calc(var(--ring-tilt) * -1)); --period: 24s; }

  /* The third slide's orbit: same circle, same tilt, same speed, running backwards.
     The ring's spin and the tile's counter-turn are exact opposites, so reversing
     the pair together keeps the tile face-on and only flips the travel direction. */
  .ring-back > .orbit-spin,
  .ring-back .orbit-tile { animation-direction: reverse; }

  .orbit-spin {
    position: absolute;
    inset: 0;
    transform-style: preserve-3d;
    animation: ring-year var(--period, 24s) linear infinite;
  }

  /* An arm turns to its place on the circle, steps out along that direction, then
     turns back by the same amount so the tile arrives square to the ring instead
     of splayed. a/b/c/d are the four quarters of the orbit, a tile and a quarter
     out. */
  .orbit-arm { position: absolute; inset: 0; transform-style: preserve-3d; }
  .arm-a { transform: translateX(var(--arm-step)); }
  .arm-b { transform: rotateY(90deg) translateX(var(--arm-step)) rotateY(-90deg); }
  .arm-c { transform: rotateY(180deg) translateX(var(--arm-step)) rotateY(-180deg); }
  .arm-d { transform: rotateY(270deg) translateX(var(--arm-step)) rotateY(-270deg); }

  /* The tile turns against its ring by exactly as much as the ring turns, and
     undoes the ring's tilt on top of that, so it keeps facing the reader all the
     way round - like a car on a Ferris wheel - while the far side of the loop
     still shrinks with distance and the near one passes in front of it. */
  .orbit-tile {
    position: absolute;
    /* -7.5% on every side: the tile is 115% of its slot and stays exactly
       centred on its arm, so the ring's radius is untouched. The phone block
       below widens this further. */
    inset: -7.5%;
    background: center / contain no-repeat;
    filter: drop-shadow(0 14px 20px oklch(0% 0 0 / 0.5));
    animation: tile-face var(--period, 24s) linear infinite;
    will-change: transform;
  }
  [data-theme="light"] .orbit-tile { filter: drop-shadow(0 14px 22px oklch(0% 0 0 / 0.26)); }

  @keyframes ring-year {
    from { transform: rotateY(0deg); }
    to { transform: rotateY(360deg); }
  }
  @keyframes tile-face {
    from { transform: rotateY(0deg) rotateX(var(--ring-tilt)); }
    to { transform: rotateY(-360deg) rotateX(var(--ring-tilt)); }
  }

  /* Mobile: the slider leaves the full-bleed and becomes a wide, short banner
     inside the page gutters — measured off the reference at 1.9:1, copy and
     orbit side by side rather than stacked, and the next banner's edge peeking
     in from the side so the row still reads as swipeable. */
  @media (width < 62rem) {
    .hero {
      /* Exactly the wrap's own gutter, taken back: the card then spans the full
         viewport the way the reference's does, without ever naming `vw` and
         overshooting the scrollbar. No top margin either -- the header's grey
         ends where the banner begins. */
      margin-inline: calc(-1 * var(--pad));
      margin-block: 0 14px;
    }
    .hero-slider {
      /* Room under the banner for the dots, so they no longer sit on the CTA. */
      padding-block-end: 18px;
    }
    .hero-track { gap: 9px; }
    .hero-slide {
      /* Measured off the reference: on a 467px-wide screen the card is 427px and
         ~11px of the neighbour shows at BOTH edges. The card gives twenty pixels
         of the viewport back and the gap eats nine of them, which is what the
         padding and the centred snap used to do between them -- now the seat is
         computed from the card's own centre, so there is nothing left to fight
         with. */
      flex: 0 0 calc(100% - 40px);
      display: grid;
      grid-template-columns: minmax(0, 1fr) minmax(0, 0.92fr);
      align-items: center;
      aspect-ratio: 1.9 / 1;
      min-block-size: 0;
      border-radius: 14px;
      padding: 12px 14px;
      gap: 6px;
    }
    /* 14px, not the desktop's 22: at this height the headline is two lines and
       the button is 32px tall, so the same absolute breath would read as a gap
       twice as wide as it looks on a laptop. */
    .slide-copy { gap: 14px; }
    /* The eyebrow chip is the first thing to go at this height; the headline and
       the button are what the banner has to carry. */
    .slide-eyebrow { display: none; }
    .slide-title { font-size: clamp(16px, 4.8vw, 21px); line-height: 1.3; }
    .hero-slide .btn-primary { block-size: 32px; font-size: 12.5px; padding: 0 14px; }
    /* The orbit is sized off the art box's width, so on a short banner the
       cluster's top tile grazed the ceiling while the red below it stood empty.
       Fourteen pixels of drop puts the orbit back in the middle of the space it
       actually has. */
    .hero-art { inline-size: 100%; aspect-ratio: 1.5; margin-block-start: 14px; }
    /* The phone gets the bigger logos; the desktop sizes stay as they were.
       The orbit radius is deliberately NOT compensated here: holding the
       circle still while the tiles grew halved the clearance between them
       (10.9% of the orbit box against the desktop's 22%), and that reads as the
       tiles brushing each other. Radius tracks tile size, so the spacing feels
       the same on both. */
    .hero-tile {
      left: 16.9%;
      top: 16.9%;
      inline-size: 66.2%;
      /* This orbit is measured in tile widths, so it has to come in with the
         tile: at 108% the top tile stood 18px from the banner's edge on a phone
         and grazed it on a shorter screen. 100% keeps the four logos clear of
         each other (measured +1.6px at the tightest point of the turn) and buys
         ten pixels of ceiling back. */
      --orbit-r: 100%;
      --orbit-d: 70.7%;
    }
    .orbit-tile { inset: -19%; }
    /* Longer arm than the desktop, not shorter: on a phone the ring is only
       ~33px across while a logo is 46px, so the tiles need more room relative to
       the circle to keep from touching. 178% is the point where the closest pair
       clears and the cluster still stops short of the headline. */
    .orbit-ring { --arm-step: 178%; }
    /* Two classes, not one: the base `.slide-dots` rule sits below this block,
       so at equal specificity it would win and the dots would stay on top of
       the shorter banner. */
    .hero .slide-dots { inset-block-end: 0; gap: 7px; }
  }

  /* Arrows: 32px white circles with the reference's own soft shadow */
  .slide-arrow { display: none; }
  @media (width >= 62rem) {
    .slide-arrow {
      display: grid;
      place-items: center;
      position: absolute;
      top: 50%;
      translate: 0 -50%;
      inline-size: 36px;
      block-size: 36px;
      padding: 0;
      border: 0;
      border-radius: 50%;
      background: transparent;
      color: rgba(255, 255, 255, 0.72);
      filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.35));
      cursor: pointer;
      transition: color 0.18s, background 0.18s;
      z-index: 5;
    }
    .slide-arrow:hover { background: rgba(255, 255, 255, 0.14); color: #ffffff; }
    .slide-arrow:active { scale: 0.94; }
    .slide-arrow svg { inline-size: 18px; block-size: 18px; }
    /* RTL: the carousel travels right-to-left, so «بعدی» lives on the LEFT edge
       and «قبلی» on the right. Each head points the way its button goes -- the
       base glyph points right, so the forward one is the flipped one. */
    .arrow-next { inset-inline-end: 16px; }
    .arrow-next svg { scale: -1 1; }
    .arrow-prev { inset-inline-start: 16px; }
  }

  /* Indicators: small round dots, centred under the slider */
  .slide-dots {
    position: absolute;
    inset-block-end: 14px;
    inset-inline: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    /* No direction override: the document's RTL order puts slide one's dot at
       the right, which is where a Persian reader expects page one. */
  }
  .slide-dot {
    /* The visible dot is a pseudo-element inside a 28px hit box, so the tappable
       area meets the 24px minimum (and comfortably more) while the dot stays
       dainty. */
    inline-size: 28px;
    block-size: 28px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: none;
    display: grid;
    place-items: center;
    cursor: pointer;
  }
  .slide-dot::before {
    content: "";
    inline-size: 9px;
    block-size: 9px;
    border-radius: 50%;
    /* 0.45 white over the campaign fields measured under 3:1, which is the
       floor for a control rather than for text. */
    background: rgba(255, 255, 255, 0.62);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
    transition: background 0.2s, scale 0.2s;
  }
  .slide-dot:hover::before { background: rgba(255, 255, 255, 0.9); }
  .slide-dot[aria-current="true"]::before {
    background: #ffffff;
    scale: 1.25;
  }

  /* --- brand ticker ------------------------------------------------------ */
  .ticker {
    overflow: hidden;
    border-block: 1px solid var(--line-soft);
    padding-block: 14px;
    margin-block: 8px 0;
  }
  .ticker-track {
    display: flex;
    gap: 40px;
    inline-size: max-content;
    animation: tickerMove 36s linear infinite;
  }
  @media (hover: hover) and (pointer: fine) {
    .ticker:hover .ticker-track { animation-play-state: paused; }
  }
  .ticker-track span {
    font-size: 15px;
    font-weight: 600;
    color: var(--text-mute);
    white-space: nowrap;
    direction: ltr;
  }
  @keyframes tickerMove { to { transform: translateX(50%); } }

  /* Header search (admin and other inner toolbars) */
  .search-wrap { position: relative; flex: 1 1 14rem; }
  .search-wrap svg {
    position: absolute;
    inset-inline-start: 12px;
    inset-block-start: 50%;
    translate: 0 -50%;
    width: 18px;
    height: 18px;
    color: var(--text-mute);
    pointer-events: none;
  }
  .search-wrap .input { padding-inline-start: 40px; }
  .field > label.search-wrap { font-weight: 400; color: var(--text); }

  /* --- Circle Categories — 120px ringed circles, 2px #D3D8E4 ring,
         14px label, one scrolling line (measured) ---------------------------- */
  .circle-cat-section { margin-block: 20px 8px; }
  .circle-cat-rail {
    display: flex;
    /* One line and no wrapping back: the fourth circle comes after the third,
       the fifth after the fourth, and so on to the end of the list. Centring
       stays `safe` because plain `center` on an overflowing line pushes the
       first circle past the scroll origin, where no swipe can reach it; a row
       that fits is still centred, which is what a phone with three of them
       wants. */
    justify-content: safe center;
    gap: clamp(20px, 1rem + 2vw, 44px);
    overflow-x: auto;
    /* At the end of the row, a continued swipe goes on scrolling the page
       instead of bouncing the strip. */
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    padding: 4px 2px 8px;
    scroll-snap-type: x proximity;
  }
  .circle-cat-rail::-webkit-scrollbar { display: none; }
  .circle-cat-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 10px;
    flex: 0 0 auto;
    scroll-snap-align: start;
  }
  .circle-cat-ring {
    inline-size: 100px;
    block-size: 100px;
    border-radius: 50%;
    padding: 4px;
    border: 2px solid var(--ring-blue);
    background: var(--surface);
    display: grid;
    place-items: center;
    transition: border-color 0.25s ease, box-shadow 0.25s ease;
  }
  @media (hover: hover) and (pointer: fine) {
    .circle-cat-card:hover .circle-cat-ring {
      border-color: var(--accent-blue);
      box-shadow: 0 0 0 4px rgba(20, 160, 222, 0.12);
    }
  }
  .circle-cat-inner {
    inline-size: 100%;
    block-size: 100%;
    border-radius: 50%;
    background: var(--surface);
    display: grid;
    place-items: center;
    overflow: hidden;
  }
  .cat-glyph { font-size: 44px; line-height: 1; }
  /* What stands inside a category circle: an uploaded picture, a typed glyph, or
     one of the drawn defaults. Sized against the ring so the phone's smaller
     circles shrink it without a second rule. */
  .cat-mark {
    inline-size: 56%;
    block-size: 56%;
    max-inline-size: 46px;
    max-block-size: 46px;
    object-fit: contain;
    border-radius: 10px;
  }
  .cat-mark-svg { display: inline-flex; color: var(--brand); opacity: 0.82; }
  .cat-mark-svg svg { inline-size: 34px; block-size: 34px; }
  .megamenu-glyph .cat-mark { inline-size: 26px; block-size: 26px; }
  .megamenu-glyph .cat-mark-svg svg { inline-size: 22px; block-size: 22px; }
  .megamenu-glyph .cat-glyph { font-size: 20px; }
  .cat-mark-preview {
    inline-size: 56px;
    block-size: 56px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--line);
    border-radius: 50%;
    flex-shrink: 0;
    overflow: hidden;
  }
  .cat-mark-preview .cat-glyph { font-size: 26px; }
  .cat-mark-preview .cat-mark { inline-size: 60%; block-size: 60%; }
  .cat-mark-preview .cat-mark-svg svg { inline-size: 30px; block-size: 30px; }
  .cat-letter {
    font-size: 32px;
    font-weight: 800;
    color: var(--brand);
  }
  .circle-cat-title {
    font-size: 14px;
    font-weight: 500;
    color: var(--text);
    line-height: 1.5;
    max-inline-size: 130px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .circle-cat-card:hover .circle-cat-title { color: var(--link); }
  @media (width < 62rem) {
    .circle-cat-ring { inline-size: 70px; block-size: 70px; }
    .cat-glyph { font-size: 31px; }
    /* One line here too, and tighter: at a 70px ring the label is what sets a
       card's width, so three fit across a 390px phone and the fourth waits to be
       scrolled to. It used to fold onto a centred second line, which is the
       "not in the middle" the shop saw -- and the answer was not to fold. */
    .circle-cat-rail { gap: 14px; }
    .circle-cat-title { font-size: 12px; max-inline-size: 84px; }
    .cat-letter { font-size: 23px; }
  }
  .circle-cat-count { display: none; }
  .circle-cat-head, .circle-cat-divider, .circle-cat-title-text, .circle-cat-view-all { display: none; }

  /* --- Wonder Deals — the reference's crimson shelf: mascot, title and a
         boxed countdown in a panel on the right, compact cards flowing left.
         The panel's content sits in the vertical middle of the box, the way
         the reference holds it lower than the cards' tops. */
  .wonder-deals-section { margin-block: 24px; }
  .wonder-deals-container {
    display: flex;
    align-items: stretch;
    gap: 14px;
    background: var(--wine);
    border-radius: 16px;
    padding: 14px;
    color: #ffffff;
    overflow: hidden;
  }
  .wonder-panel {
    flex: 0 0 196px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 13px;
    text-align: center;
    padding: 10px 6px 10px 14px;
  }
  .wonder-title {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 21px;
    font-weight: 800;
    color: #ffffff;
  }
  .wonder-title svg {
    inline-size: 24px;
    block-size: 24px;
    color: #ffd7de;
    flex-shrink: 0;
  }
  .wonder-timer { display: flex; align-items: center; gap: 7px; direction: ltr; }
  .wt-cell {
    min-inline-size: 46px;
    block-size: 40px;
    display: grid;
    place-items: center;
    background: #ffffff;
    color: var(--wine);
    border-radius: 8px;
    font-size: 19px;
    font-weight: 800;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.14);
  }
  .wt-sep { color: #ffffff; font-size: 20px; font-weight: 800; }
  .wonder-all-btn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    background: #ffffff;
    color: #a2191f;
    border-radius: 7px;
    padding: 4px 11px;
    font-size: 12.5px;
    font-weight: 600;
    white-space: nowrap;
  }
  .wonder-all-btn::after { content: "←"; font-size: 12.5px; line-height: 1; }
  @media (hover: hover) and (pointer: fine) {
    .wonder-all-btn:hover { background: #ffecee; }
  }
  .wonder-rail-wrapper { flex: 1; min-inline-size: 0; }
  .wonder-rail {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: clamp(196px, 14.5vw, 214px);
    gap: 10px;
    justify-content: safe center;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    align-items: stretch;
  }
  .wonder-rail::-webkit-scrollbar { display: none; }

  /* On a laptop the row overflows and its scrollbar is hidden, but a wheel over
     it scrolls the page, not the row -- so the cards at the end had no way to be
     reached. It is dragged instead, and the hand is what promises that before the
     first press. The cursor is set on the cards as much as on the row: both are
     links, and a hand that turns back into a pointer the moment it crosses a card
     reads as two different things to grab. Wired in app.js, and needed nowhere on
     a phone, where a swipe already pages the row. */
  @media (hover: hover) and (pointer: fine) {
    .wonder-rail, .circle-cat-rail,
    .wonder-rail .card, .wonder-rail .tl-card,
    .circle-cat-rail .circle-cat-card { cursor: grab; }
    .wonder-rail.is-dragging,
    .circle-cat-rail.is-dragging {
      cursor: grabbing;
      scroll-snap-type: none;
      user-select: none;
    }
    /* Out of the way for the length of the drag, so the row is not also a set of
       hover states lighting up under the moving cursor. */
    .wonder-rail.is-dragging .card,
    .wonder-rail.is-dragging .tl-card,
    .circle-cat-rail.is-dragging .circle-cat-card { pointer-events: none; }
  }
  /* Reference cards are compact: no corner tag, no per-card countdown, no
     inline button — the whole card opens the product page, where the add
     button lives. */
  .wonder-rail .tl-discount-pill,
  .wonder-rail .card-timer,
  .wonder-rail .card-tag,
  .wonder-rail .tl-card-actions { display: none; }
  .wonder-rail .card-body, .wonder-rail .tl-card-body { padding: 2px 10px 12px; }
  @media (width < 56rem) {
    /* On the phone the shelf glues itself to both screen edges — no page
       gutters, no rounding — exactly the reference strip. */
    .wonder-deals-container {
      flex-direction: column;
      margin-inline: calc(50% - 50vw);
      border-radius: 0;
      padding: 10px 0 12px;
    }
    /* The reference keeps the shelf header on ONE line — title, countdown and
       the «همه» link side by side. Stacking those three is what made the red
       band so tall on a phone, so the panel goes back to a row and its parts
       shrink with it. */
    .wonder-panel {
      flex-direction: row;
      flex-basis: auto;
      flex-wrap: wrap;
      align-items: center;
      justify-content: space-between;
      gap: 8px;
      padding: 2px 12px 10px;
    }
    .wonder-title { font-size: 16px; }
    @media (width < 27rem) {
      /* Below ~430px the row is title + countdown + link, and it is the title's
         own single line that does not fit. Capping it costs the title a line the
         reference's shelf breaks anyway, and pulls the link back up beside the
         countdown instead of onto a row of its own. */
      .wonder-title { max-inline-size: 7.5em; }
    }
    .wonder-title svg { inline-size: 19px; block-size: 19px; }
    .wonder-timer { gap: 5px; }
    .wt-cell {
      min-inline-size: 28px;
      block-size: 26px;
      border-radius: 6px;
      font-size: 13.5px;
    }
    .wt-sep { font-size: 15px; }
    /* No button on the phone shelf. The reference puts a bare link with a
       chevron there, and the white box was the widest thing in this row -- it
       fought the countdown for room and pushed itself onto a second line. The
       desktop keeps the boxed button. */
    .wonder-all-btn {
      padding: 2px 0;
      background: none;
      border-radius: 0;
      color: #ffffff;
      font-size: 12.5px;
      font-weight: 600;
      /* On the narrowest phones the row still cannot hold title, timer and link,
         so the link drops to a line of its own -- at the far end, under the
         cards, rather than back under the title. */
      margin-inline-start: auto;
    }
    .wonder-all-btn::after { font-size: 13px; }
    @media (hover: hover) and (pointer: fine) {
      .wonder-all-btn:hover { background: none; color: #ffffff; text-decoration: underline; }
    }
    /* 12px of red on both sides so the first card is not glued to the screen
       edge, and a real gap between cards instead of a hairline. */
    .wonder-rail {
      grid-auto-columns: calc((100% - 24px - 16px) / 3);
      gap: 8px;
      padding-inline: 12px;
      justify-content: start;
    }
    .wonder-rail .card-media, .wonder-rail .tl-card-media {
      /* The shelf is a wall of pictures edge to edge, so the art keeps its own
         frame: no padding, no cap, and the card's rounding on the top corners. */
      --art-pad: 0px;
      --art-cap: 1;
      padding: 0;
    }
    .wonder-rail .card-media img, .wonder-rail .tl-card-media img {
      border-radius: 8px 8px 0 0;
    }
    .wonder-rail .card-body, .wonder-rail .tl-card-body { padding: 6px 8px 10px; gap: 6px; }
    /* The reference's 11px/10.5px numbers are legible in Latin; Persian
       descenders (ژ ی ک) close up at that size. Two steps up, still a compact
       card, and the struck-out price stops being decorative. */
    .wonder-rail .card-title, .wonder-rail .tl-card-title {
      font-size: 12px;
      line-height: 18px;
      block-size: 36px;
      font-weight: 500;
    }
    .wonder-rail .tl-price-was { font-size: 11.5px; color: var(--text-soft); }
    .wonder-rail .tl-price-final b { font-size: 12.5px; }
    .wonder-rail .tl-price-unit b { font-size: 11.5px; }
    .wonder-rail .tl-price-usd { display: none; }
    .wonder-rail .tl-discount-pill-price {
      block-size: 16px;
      min-inline-size: 32px;
      font-size: 10px;
    }
    /* Reference cards carry no inline button, corner tag or per-card countdown
       — the card itself opens the product page. */
    .wonder-rail .tl-discount-pill,
    .wonder-rail .card-timer,
    .wonder-rail .card-tag,
    .wonder-rail .tl-card-actions { display: none; }
  }

  /* --- Product card ---------------------------------------------------------
     The artwork is the loud part of a card, so the card's own job is to frame
     it quietly: a tinted stage, the logo capped well inside it, and a body that
     says name, price and action without repeating itself.

     Two things here are load-bearing rather than taste.

     The image is absolutely positioned and capped by a calc. A logo left in the
     flow with `block-size: 100%` inside a frame whose height comes from
     `aspect-ratio` is a percentage against an indefinite size: it resolves
     against nothing, and the art paints edge to edge -- which is how a 512px
     square logo filled a 147px card and made every card shout. Out of the flow
     the inset box is definite, so the cap is a number the frame can honour.

     The hover lift is a `transform`, not a `translate`: this element is also a
     `.reveal`, whose animation owns `translate`, and an animation beats a
     declaration in the cascade -- the lift would simply never have happened. */
  .card, .tl-card {
    position: relative;
    container: card / inline-size;
    display: flex;
    flex-direction: column;
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-md);
    background: var(--card);
    overflow: hidden;
    transition: transform 0.26s var(--ease-out), box-shadow 0.26s var(--ease-out), border-color var(--dur);
  }
  @media (hover: hover) and (pointer: fine) {
    .card:hover, .tl-card:hover {
      transform: translateY(-3px);
      border-color: var(--line);
      box-shadow: var(--shadow-md);
    }
    .card:hover .card-media img, .tl-card:hover .tl-card-media img { scale: 1.05; }
  }
  /* The whole card is a tap target on this site (app.js routes it to the PDP),
     so it has to answer to a press on the devices where it has no hover. */
  .card:active, .tl-card:active { border-color: var(--line-strong); background: var(--surface-3); }
  /* Out of stock steps back from the shelf: the card dims and its art loses its
     colour, so the eye passes over it on the way to what can be bought. Keyed on
     the flag the card already carries rather than on the badge's text. */
  .card[data-available="0"] { opacity: 0.66; }
  .card[data-available="0"] .card-media img,
  .card[data-available="0"] .tl-card-media img,
  .card[data-available="0"] .media-fallback { filter: grayscale(1); }

  .tl-card-topbar { display: contents; }

  .card-media, .tl-card-media {
    /* Both feed the art rule below, so the air and the ceiling travel together as
       a card changes size. `cqi` is the card's own width rather than the screen's:
       the 147px phone card and the 214px shelf card are one rule at two sizes,
       not two rules. */
    --art-pad: clamp(8px, 6cqi, 14px);
    --art-cap: 0.92;
    position: relative;
    inline-size: 100%;
    aspect-ratio: 5 / 4;
    display: grid;
    place-items: center;
    padding: var(--art-pad);
    overflow: hidden;
    border-block-end: 1px solid var(--line-soft);
    /* A wash, not a solid panel: service logos arrive as white squares and as
       transparent ones, and a flat tint on the night skin reads as a hole cut
       in the card. */
    background:
      radial-gradient(120% 92% at 50% -14%,
        color-mix(in oklab, var(--accent-blue) 12%, transparent), transparent 64%),
      var(--surface-3);
  }
  /* Every product-art frame on the site -- the card, the product page, the cart
     line -- obeys one rule, because the same bug lived in all three: a
     percentage height on a grid item whose row is sized by that very item is a
     percentage against an indefinite size, so `max-height: 88%` never applied at
     all and a 1024px logo painted at its natural size and hung out below the
     frame, clipped by `overflow` and visibly off-centre. Out of the flow the box
     is the frame's own padding box, which is definite; `margin: auto` centres the
     art inside it, and the cap becomes the number it always looked like. */
  .card-media img, .tl-card-media img,
  .pdp-figure img,
  .cart-thumb img {
    position: absolute;
    inset: var(--art-pad);
    margin: auto;
    inline-size: auto;
    block-size: auto;
    /* The percentages resolve against the frame's padding box, so the padding the
       `inset` already took is subtracted again here -- and then the cap leaves the
       ring of air that keeps a logo off the edges. */
    max-inline-size: calc((100% - 2 * var(--art-pad)) * var(--art-cap));
    max-block-size: calc((100% - 2 * var(--art-pad)) * var(--art-cap));
    object-fit: contain;
    border-radius: var(--radius-xs);
    transition: scale 0.3s var(--ease-out);
  }

  /* A product with no art gets its own initials, scaled to the card rather than
     to the viewport. */
  .media-fallback {
    display: grid;
    place-items: center;
    inline-size: 100%;
    block-size: 100%;
    font-size: clamp(19px, 22cqi, 34px);
    font-weight: 800;
    letter-spacing: -0.02em;
    color: color-mix(in oklab, var(--text) 52%, transparent);
  }

  .card-tag {
    position: absolute;
    inset-block-start: 9px;
    inset-inline-start: 9px;
    z-index: 2;
    display: inline-flex;
    align-items: center;
    min-block-size: 21px;
    padding: 0 8px;
    border-radius: var(--radius-pill);
    font-size: 11.5px;
    font-weight: 600;
    line-height: 1.2;
    white-space: nowrap;
  }
  .card-tag.sold-out { background: var(--surface-2); color: var(--text-soft); }
  .card-tag.in-stock { background: var(--emerald-bg); color: var(--emerald); }

  .card-timer {
    position: absolute;
    inset-block-end: 9px;
    inset-inline-start: 9px;
    z-index: 2;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    background: rgba(255, 255, 255, 0.94);
    /* Literal ink, not a token: the reds go pale on the night skin, and this
       pill is white in both skins, so its text has to stay dark either way. */
    color: #0a3daa;
    font-size: 11px;
    font-weight: 600;
    padding: 3px 8px;
    border-radius: var(--radius-pill);
    border: 1px solid var(--line-soft);
  }
  .card-timer svg { inline-size: 12px; block-size: 12px; }

  /* Corner label, the reference's dark tag: 20px tall, 8px radius, #393939 */
  .tl-discount-pill {
    position: absolute;
    inset-block-start: 9px;
    inset-inline-end: 9px;
    z-index: 2;
    display: inline-flex;
    align-items: center;
    min-block-size: 20px;
    justify-content: center;
    padding: 0 6px;
    border-radius: var(--radius-sm);
    background: var(--badge-dark);
    color: #ffffff;
    font-size: 11.5px;
    font-weight: 600;
    line-height: 1;
    white-space: nowrap;
  }
  .tl-discount-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    block-size: 19px;
    min-inline-size: 36px;
    padding: 0 6px;
    border-radius: 3px;
    background: var(--discount-red-dark);
    color: #ffffff;
    font-size: 12px;
    font-weight: 600;
    line-height: 1;
    flex-shrink: 0;
  }

  .card-body, .tl-card-body {
    padding: 10px 10px 11px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    flex: 1;
  }
  .card-title, .tl-card-title {
    font-size: 13px;
    font-weight: 500;
    line-height: 19px;
    /* Two lines and no more, fixed, so a one-line name and a two-line name sit
       on the same baseline across a grid of cards. */
    block-size: 38px;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-wrap: pretty;
    overflow-wrap: break-word;
  }
  .tl-card-title a, .card-link {
    color: var(--text-strong);
    text-decoration: none;
    transition: color var(--dur);
  }
  @media (hover: hover) and (pointer: fine) {
    .tl-card-title a:hover { color: var(--link); }
  }

  .tl-card-perks { display: none; }

  .tl-price-box {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-block-start: auto;
  }
  .tl-price-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
  }
  .tl-price-row.single-row { justify-content: flex-end; }
  .tl-price-final {
    display: flex;
    align-items: baseline;
    gap: 3px;
    min-inline-size: 0;
  }
  .tl-price-final b {
    font-size: 15px;
    font-weight: 700;
    color: var(--text-strong);
  }
  .tl-price-final small {
    font-size: 11.5px;
    color: var(--text-unit);
    font-weight: 500;
  }
  .tl-discount-pill-price {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    block-size: 19px;
    min-inline-size: 36px;
    padding: 0 6px;
    border-radius: 3px;
    background: var(--discount-red-dark);
    color: #ffffff;
    font-size: 12px;
    font-weight: 600;
    line-height: 1;
  }
  .tl-price-was {
    font-size: 13px;
    color: var(--text-mute);
    text-align: end;
    text-decoration: line-through;
    overflow-wrap: anywhere;
  }
  .tl-price-was s { text-decoration: line-through; }
  .tl-price-usd {
    font-size: 12px;
    color: var(--text-mute);
    text-align: start;
    direction: ltr;
  }

  .tl-card-actions {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-block-start: 2px;
    padding-block-start: 8px;
    border-block-start: 1px solid var(--line-soft);
  }
  .tl-card-btn {
    flex: 1;
    block-size: 36px;
    font-size: 12.5px;
    border-radius: var(--radius-sm);
    gap: 4px;
  }
  .tl-card-btn svg { width: 15px; height: 15px; }
  .tl-card-detail-link {
    flex: 0 0 auto;
    block-size: 36px;
    font-size: 12.5px;
    padding: 0 9px;
    border-radius: var(--radius-sm);
  }

  /* --- breadcrumbs ------------------------------------------------------- */
  .crumbs {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    font-size: 12px;
    color: var(--text-mute);
    margin-block-end: 12px;
  }
  .crumbs a { color: var(--text-mute); transition: color var(--dur); }
  .crumbs a:hover { color: var(--link); text-decoration: underline; }
  .crumbs .here { color: var(--text-soft); font-weight: 500; }
  /* One scrolling line on a phone. The product page ends its trail with the
     product's own name, and letting that wrap took two lines of height away
     from the picture -- the reference scrolls its breadcrumb for the same
     reason. */
  @media (width < 62rem) {
    .crumbs {
      flex-wrap: nowrap;
      overflow-x: auto;
      scrollbar-width: none;
      padding-block-end: 2px;
    }
    .crumbs > * { flex: 0 0 auto; }
    .crumbs::-webkit-scrollbar { display: none; }
  }

  /* --- PDP (product detail) ---------------------------------------------- */
  .pdp-section { margin-block-start: 8px; }

  .pdp-container {
    display: grid;
    grid-template-columns: 1fr;
    gap: 16px;
    align-items: start;
  }
  @media (width >= 62rem) {
    .pdp-container { grid-template-columns: minmax(0, 1fr) 21rem; }
    .pdp-media-col { order: 2; }
    .pdp-info-col { order: 1; }
    .pdp-buybox-col { order: 3; }
  }
  @media (width >= 75rem) {
    .pdp-container { grid-template-columns: 24rem minmax(0, 1fr) 21rem; }
    .pdp-media-col { order: 1; }
    .pdp-info-col { order: 2; }
    .pdp-buybox-col { order: 3; }
  }

  .pdp-media-col { display: flex; flex-direction: column; gap: 12px; }
  .pdp-figure {
    --art-pad: 24px;
    --art-cap: 0.88;
    position: relative;
    container: media / inline-size;
    aspect-ratio: 1;
    background: var(--surface);
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-md);
    display: grid;
    place-items: center;
    overflow: hidden;
  }
  /* On a phone the header is sticky above and the buy bar is pinned below, so
     the picture has to live in the space between them -- the reference's product
     page shows the whole artwork before anything scrolls, and this one did not:
     the frame ran 99px under the bar. `svh`, not `dvh`, because a frame that
     grew when the browser's address bar collapsed would jump under the thumb. */
  @media (width < 62rem) {
    .pdp-figure {
      --art-pad: 16px;
      aspect-ratio: 4 / 3;
      max-block-size: calc(100svh - var(--header-h) - var(--buy-bar-h, 0px) - 88px);
    }
    /* A product with no art still has to fill its frame rather than float a
       couple of letters in the middle of a box. */
    .pdp-figure .media-fallback { font-size: clamp(36px, 24cqi, 84px); }
  }

  .pdp-info-col { display: flex; flex-direction: column; gap: 16px; min-inline-size: 0; }
  .pdp-head { display: flex; flex-direction: column; gap: 8px; }
  .pdp-title {
    font-size: var(--fs-2xl);
    font-weight: 700;
    line-height: 1.5;
    color: var(--text-strong);
  }
  .pdp-sub {
    font-size: 14px;
    color: var(--text-soft);
    line-height: 1.9;
    white-space: pre-line;
  }
  .pdp-badges { display: flex; flex-wrap: wrap; gap: 6px; }

  .pdp-specs-box {
    background: var(--surface);
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-sm);
    padding: 16px;
    display: flex;
    flex-direction: column;
    gap: 12px;
  }
  .pdp-specs-title { font-size: 15px; font-weight: 700; color: var(--text-strong); }
  .pdp-specs { display: flex; flex-direction: column; gap: 4px; }
  .pdp-specs li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    font-size: 14px;
    color: var(--text-soft);
    padding-block: 8px;
    border-block-end: 1px solid var(--line-soft);
  }
  .pdp-specs li:last-child { border-block-end: none; }
  .pdp-specs svg { inline-size: 18px; block-size: 18px; color: var(--emerald); flex-shrink: 0; }

  /* Sticky buy box, the reference pattern: seller row, hairlines, big price,
     full-width red 48px button */
  .tl-buybox {
    position: sticky;
    inset-block-start: calc(var(--header-h) + 8px);
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-md);
    background: var(--surface);
    box-shadow: var(--shadow-sm);
    padding: 16px;
    display: flex;
    flex-direction: column;
    gap: 12px;
    max-block-size: calc(100dvh - var(--header-h) - 16px);
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  .bb-seller-row { display: flex; align-items: center; gap: 10px; }
  .bb-seller-icon {
    inline-size: 40px;
    block-size: 40px;
    border-radius: var(--radius-sm);
    background: var(--surface-2);
    color: var(--brand);
    display: grid;
    place-items: center;
    flex-shrink: 0;
  }
  .bb-seller-icon svg { inline-size: 20px; block-size: 20px; }
  .bb-seller-info { display: flex; flex-direction: column; gap: 2px; }
  .bb-seller-info b { font-size: 14px; font-weight: 700; color: var(--text-strong); }
  .bb-seller-info span { font-size: 12px; color: var(--text-mute); }
  .bb-divider { height: 1px; background: var(--line-soft); }
  .bb-feature-row {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    color: var(--text-soft);
  }
  .bb-feature-row svg { inline-size: 18px; block-size: 18px; color: var(--emerald); flex-shrink: 0; }
  .bb-req-info {
    display: flex;
    align-items: start;
    gap: 8px;
    font-size: 13px;
    color: var(--text-soft);
    background: var(--surface-3);
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    line-height: 1.7;
  }
  .bb-req-info svg { inline-size: 16px; block-size: 16px; color: var(--accent-blue); flex-shrink: 0; margin-block-start: 3px; }
  .bb-price-box { display: flex; flex-direction: column; gap: 8px; }
  .bb-was-row {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    color: var(--text-mute);
  }
  .bb-was-row s { text-decoration: line-through; }
  .bb-final-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
  }
  .bb-final-row > span { font-size: 14px; color: var(--text-soft); }
  .bb-final-price { display: flex; align-items: baseline; gap: 4px; }
  .bb-final-price b { font-size: var(--fs-3xl); font-weight: 700; color: var(--text-strong); line-height: 1.1; }
  .bb-final-price small { font-size: 14px; color: var(--text-unit); font-weight: 500; }
  .bb-dollar-calc {
    font-size: 12px;
    color: var(--text-mute);
    background: var(--surface-3);
    border-radius: var(--radius-xs);
    padding: 6px 10px;
    text-align: left;
    direction: ltr;
  }
  .bb-actions { display: grid; gap: 8px; }
  .bb-incart-hint { font-size: 13px; color: var(--emerald); font-weight: 600; text-align: center; }

  /* --- Mobile buy bar -------------------------------------------------------
     The reference's product page answers «چقدر؟» and «بخر» before the first
     swipe: price and action live in a bar pinned to the bottom edge for the whole
     length of the page. The desktop already carries the price in a column beside
     the picture, so this is the phone's answer to the same question.

     On the phone it also retires the bottom navigation, because 56px of nav under
     a 66px bar would hold a fifth of the screen hostage and the bar's own button
     is where the cart lives once something is in it. Everything below reads
     `--nav-h` -- the page's bottom padding, the bar's own seat, the toasts -- so
     retargeting that one variable moves all three together, and `--buy-bar-h`
     hands back the room the bar takes. */
  .bb-sticky { display: none; }
  @media (width < 62rem) {
    body:has(.bb-sticky) {
      --nav-h: env(safe-area-inset-bottom, 0px);
      --buy-bar-h: 66px;
      padding-block-end: calc(var(--nav-h) + var(--buy-bar-h));
    }
    body:has(.bb-sticky) .mobile-bottom-nav { display: none; }
    .bb-sticky {
      position: fixed;
      inset-inline: 0;
      inset-block-end: var(--nav-h);
      z-index: 120;
      display: flex;
      align-items: center;
      gap: 10px;
      block-size: 66px;
      /* The action sits at the thumb's start -- the right, in this direction --
         with the price beside it, the reference's arrangement. The markup keeps
         prose order, price then action, so the row is reversed here rather than
         the document rewritten for a layout. */
      flex-direction: row-reverse;
      padding-inline: 12px;
      background: var(--surface);
      border-block-start: 1px solid var(--line-soft);
      box-shadow: 0 -8px 22px rgba(0, 0, 0, 0.12);
    }
    .bb-sticky-price {
      display: flex;
      flex-direction: column;
      gap: 1px;
      min-inline-size: 0;
    }
    .bb-sticky-was {
      display: flex;
      align-items: center;
      gap: 6px;
      font-size: 11.5px;
      line-height: 1.2;
      color: var(--text-mute);
    }
    .bb-sticky-was s { text-decoration: line-through; }
    /* No discount to show, so the line carries what the bar is the price OF --
       the bar stays on screen long after the headline has scrolled away. */
    .bb-sticky-note {
      font-size: 11px;
      line-height: 1.3;
      color: var(--text-mute);
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      max-inline-size: 9.5rem;
    }
    .bb-sticky-final { display: flex; align-items: baseline; gap: 3px; }
    .bb-sticky-final b { font-size: 17px; font-weight: 800; color: var(--text-strong); }
    .bb-sticky-final small { font-size: 11.5px; color: var(--text-unit); }
    .bb-sticky-cta { flex: 1; min-inline-size: 0; }
    .bb-sticky-btn { block-size: 46px; font-size: 14px; }
  }

  /* PDP tabs — reference pattern: white box, active tab red with 2px underline */
  .pdp-tabs-wrapper {
    margin-block-start: 16px;
    background: var(--surface);
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-md);
    overflow: hidden;
  }
  .pdp-tabs-header {
    display: flex;
    align-items: center;
    border-block-end: 1px solid var(--line-soft);
    padding: 0 8px;
    gap: 4px;
    overflow-x: auto;
    scrollbar-width: none;
  }
  .pdp-tabs-header::-webkit-scrollbar { display: none; }
  .pdp-tab-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 14px 16px;
    border: 0;
    border-block-end: 2px solid transparent;
    margin-block-end: -1px;
    background: none;
    font-size: 14px;
    font-weight: 500;
    color: var(--text-soft);
    cursor: pointer;
    font-family: inherit;
    transition: color var(--dur), border-color var(--dur);
    white-space: nowrap;
  }
  .pdp-tab-btn:hover { color: var(--text-strong); }
  .pdp-tab-btn.is-active {
    color: var(--rose);
    border-block-end-color: var(--rose);
    font-weight: 600;
  }
  .pdp-tab-btn svg { inline-size: 18px; block-size: 18px; }
  .pdp-tabs-body { padding: 20px; }
  .pdp-tab-content { display: none; }
  .pdp-tab-content.is-active { display: block; animation: dropdownFadeIn 0.2s ease-out; }
  .pdp-tab-heading { font-size: 16px; font-weight: 700; color: var(--text-strong); margin-block-end: 12px; }
  .pdp-table-wrap { overflow-x: auto; border: 1px solid var(--line-soft); border-radius: var(--radius-sm); }
  .pdp-specs-table {
    inline-size: 100%;
    border-collapse: collapse;
    font-size: 14px;
    text-align: right;
  }
  .pdp-specs-table tr:nth-child(even) { background: var(--surface-3); }
  .pdp-specs-table tr { border-block-end: 1px solid var(--line-soft); }
  .pdp-specs-table tr:last-child { border-block-end: 0; }
  .pdp-th {
    padding: 12px 16px;
    font-weight: 600;
    color: var(--text-soft);
    inline-size: 32%;
    white-space: nowrap;
    border-inline-end: 1px solid var(--line-soft);
  }
  .pdp-td { padding: 12px 16px; color: var(--text); line-height: 1.7; }
  .pdp-guide-text { font-size: 14px; color: var(--text-soft); line-height: 2; }

  /* Promise tiles: four small bordered cards. On the product page they close it,
     full width, so all four fit one line; on a phone they fold to two and two. */
  .promises { display: grid; gap: 8px; grid-template-columns: repeat(3, minmax(0, 1fr)); }
  @media (width >= 62rem) {
    .pdp-promises { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  }
  @media (width < 30.6rem) {
    .promises { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .promise:last-child:nth-child(odd) { grid-column: 1 / -1; }
  }
  .promise {
    display: grid;
    justify-items: center;
    text-align: center;
    gap: 6px;
    padding: 14px 8px;
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-sm);
    background: var(--surface);
  }
  .promise b { font-size: 13px; font-weight: 600; color: var(--text-strong); }
  .promise span:last-child { font-size: 12px; color: var(--text-mute); line-height: 1.7; }
  .promise-icon {
    inline-size: 40px;
    block-size: 40px;
    border-radius: var(--radius-sm);
    background: var(--surface-2);
    display: grid;
    place-items: center;
    color: var(--brand);
  }
  .promise-icon svg { width: 20px; height: 20px; }

  /* Tick list (used under the PDP picture and inside admin rows) */
  .specs { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  .specs li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 9px 12px;
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-sm);
    background: var(--surface);
    font-size: 13px;
    font-weight: 500;
    color: var(--text-soft);
    line-height: 1.7;
  }
  .specs svg { width: 16px; height: 16px; flex-shrink: 0; color: var(--emerald); }
  .specs li:last-child:nth-child(odd) { grid-column: 1 / -1; }

  /* --- Catalog (products list) -------------------------------------------- */
  .catalog-section { margin-block-start: 8px; }
  .catalog-layout {
    display: grid;
    grid-template-columns: 1fr;
    gap: 16px;
    align-items: start;
  }
  @media (width >= 62rem) {
    .catalog-layout { grid-template-columns: 17rem minmax(0, 1fr); }
  }

  .catalog-sidebar {
    display: flex;
    flex-direction: column;
    gap: 12px;
  }
  @media (width >= 62rem) {
    .catalog-sidebar {
      position: sticky;
      inset-block-start: calc(var(--header-h) + 16px);
      /* The filter rail is taller than a laptop viewport; without this the
         sticky box pinned its own bottom out of reach. */
      max-block-size: calc(100dvh - var(--header-h) - 24px);
      overflow-y: auto;
      overscroll-behavior: contain;
      scrollbar-width: thin;
    }
  }
  .sidebar-card {
    background: var(--surface);
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
    padding: 14px 16px;
    display: flex;
    flex-direction: column;
    gap: 8px;
  }
  .sidebar-head {
    font-size: 14px;
    font-weight: 700;
    color: var(--text-strong);
    padding-block-end: 10px;
    border-block-end: 1px solid var(--line-soft);
  }
  .cat-filter-list { display: flex; flex-direction: column; gap: 2px; }
  .cat-filter-btn {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    inline-size: 100%;
    padding: 9px 10px;
    border-radius: var(--radius-sm);
    border: 0;
    background: none;
    font-size: 14px;
    font-weight: 500;
    color: var(--text);
    cursor: pointer;
    font-family: inherit;
    text-align: right;
    transition: background var(--dur), color var(--dur);
  }
  .cat-filter-btn:hover { background: var(--surface-2); }
  .cat-filter-btn.is-active,
  .cat-filter-btn[aria-pressed="true"] {
    background: var(--rose-bg);
    color: var(--rose);
    font-weight: 600;
  }
  .cat-filter-count {
    font-size: 12px;
    color: var(--text-mute);
    font-weight: 500;
  }
  .cat-filter-btn.is-active .cat-filter-count,
  .cat-filter-btn[aria-pressed="true"] .cat-filter-count { color: var(--rose); }

  /* In-stock switch — iOS-style, reference sizing (40x22 track) */
  .tl-switch-label {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    cursor: pointer;
    user-select: none;
    font-size: 14px;
    font-weight: 500;
    color: var(--text);
  }
  .tl-switch-title { font-size: 14px; font-weight: 500; }
  .tl-switch-track {
    position: relative;
    inline-size: 40px;
    block-size: 22px;
    background: var(--line-strong);
    border-radius: var(--radius-pill);
    transition: background 0.2s ease;
    flex-shrink: 0;
  }
  .tl-switch-input { opacity: 0; position: absolute; width: 0; height: 0; }
  /* The real control is zero-sized, so the global focus ring lands on nothing:
     the track has to carry it or the switch is invisible to a keyboard. */
  .tl-switch-label:has(.tl-switch-input:focus-visible) .tl-switch-track {
    outline: 2px solid var(--accent-blue);
    outline-offset: 2px;
  }
  .tl-switch-thumb {
    position: absolute;
    top: 2px;
    /* RTL: the knob rests at the inline start (the right) and travels inward
       when checked, which is what the translateX(-18px) below assumes. */
    inset-inline-start: 2px;
    width: 18px;
    height: 18px;
    background: #ffffff;
    border-radius: 50%;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
    transition: transform 0.2s ease;
  }
  .tl-switch-input:checked ~ .tl-switch-thumb { transform: translateX(-18px); }
  .tl-switch-label:has(.tl-switch-input:checked) .tl-switch-track { background: var(--discount-red); }
  .filter-clear-btn {
    block-size: 36px;
    font-size: 13px;
    --btn-bg: transparent;
    --btn-fg: var(--rose);
    border-color: color-mix(in srgb, var(--rose) 40%, transparent);
  }
  @media (hover: hover) and (pointer: fine) {
    .filter-clear-btn:hover { --btn-bg: var(--rose-bg); }
  }

  .sidebar-trust-card { background: var(--surface-3); }
  .st-line {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    font-weight: 500;
    color: var(--text-soft);
  }
  .st-line svg { inline-size: 18px; block-size: 18px; color: var(--brand); flex-shrink: 0; }

  .search-wrap-catalog { position: relative; display: flex; align-items: center; }
  .search-wrap-catalog svg {
    position: absolute;
    inset-inline-start: 12px;
    inline-size: 16px;
    block-size: 16px;
    color: var(--text-mute);
    pointer-events: none;
  }
  .search-wrap-catalog input { padding-inline-start: 38px; block-size: 38px; font-size: 13px; }

  /* Sort bar — white, 8px radius, soft shadow, red active tab (measured) */
  .catalog-main { display: flex; flex-direction: column; gap: 12px; min-inline-size: 0; }
  .catalog-sort-bar {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 6px 16px;
    min-block-size: 52px;
    background: var(--surface);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-sm);
    font-size: 14px;
  }
  .sort-bar-label {
    display: flex;
    align-items: center;
    gap: 6px;
    font-weight: 600;
    color: var(--text-strong);
    white-space: nowrap;
    font-size: 14px;
  }
  .sort-bar-label svg { inline-size: 18px; block-size: 18px; }
  .sort-tabs { display: flex; align-items: center; gap: 4px; overflow-x: auto; scrollbar-width: none; }
  .sort-tabs::-webkit-scrollbar { display: none; }
  .sort-tab {
    min-block-size: 40px;
    padding: 6px 10px;
    border: 0;
    background: none;
    font-size: 14px;
    font-weight: 500;
    color: var(--text-soft);
    cursor: pointer;
    border-radius: var(--radius-sm);
    font-family: inherit;
    white-space: nowrap;
    transition: color var(--dur);
  }
  .sort-tab:hover { color: var(--text-strong); }
  .sort-tab.is-active { color: var(--rose); font-weight: 600; }
  .catalog-count-pill {
    font-size: 13px;
    font-weight: 500;
    color: var(--text-mute);
    white-space: nowrap;
  }

  .cat-mobile-strip {
    display: flex;
    gap: 8px;
    overflow-x: auto;
    scrollbar-width: none;
    padding-block-end: 4px;
  }
  .cat-mobile-strip::-webkit-scrollbar { display: none; }

  .catalog-grid-inner {
    display: grid;
    gap: 12px;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 216px), 1fr));
  }
  @media (width < 34rem) {
    .catalog-grid-inner { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  }

  /* --- sections, features, steps, faq ------------------------------------- */
  .feature {
    display: grid;
    gap: 8px;
    padding: 20px;
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-md);
    background: var(--surface);
    transition: border-color var(--dur);
  }
  .feature:hover { border-color: var(--line); }
  .feature h3 { font-size: 15px; font-weight: 700; color: var(--text-strong); }
  .feature p { font-size: 13px; color: var(--text-soft); line-height: 1.8; }
  .feature-icon {
    inline-size: 44px;
    block-size: 44px;
    border-radius: var(--radius-sm);
    background: var(--surface-2);
    color: var(--brand);
    display: grid;
    place-items: center;
  }
  .feature-icon svg { width: 22px; height: 22px; }
  /* Two columns at this width leaves a card ~165px across, so the padding and
     the type come down with the grid -- 13px body copy in a 125px box wraps one
     word per line and the four cards end up taller than they were in one column. */
  @media (width < 34rem) {
    .feature { padding: 12px; gap: 6px; }
    .feature h3 { font-size: 13px; }
    .feature p { font-size: 11.5px; line-height: 1.65; }
    .feature-icon { inline-size: 34px; block-size: 34px; }
    .feature-icon svg { width: 18px; height: 18px; }
  }

  .steps { display: grid; gap: 12px; counter-reset: step; }
  @media (width >= 55rem) { .steps { grid-template-columns: repeat(4, 1fr); } }
  .step {
    counter-increment: step;
    display: grid;
    gap: 6px;
    padding: 20px;
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-md);
    background: var(--surface);
    position: relative;
  }
  .step::before {
    content: counter(step);
    inline-size: 28px;
    block-size: 28px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: var(--surface-2);
    color: var(--brand);
    font-size: 14px;
    font-weight: 700;
    margin-block-end: 4px;
  }
  .step h3 { font-size: 15px; font-weight: 700; color: var(--text-strong); }
  .step p { font-size: 13px; color: var(--text-soft); line-height: 1.8; }

  .faq { display: grid; gap: 8px; }
  .faq details {
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-sm);
    background: var(--surface);
    overflow: hidden;
  }
  .faq details[open] { border-color: var(--line); }
  .faq summary {
    list-style: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 14px 16px;
    font-size: 14px;
    font-weight: 600;
    color: var(--text-strong);
  }
  .faq summary::-webkit-details-marker { display: none; }
  .faq summary::after {
    content: "";
    inline-size: 9px;
    block-size: 9px;
    border-inline-end: 1.8px solid var(--text-mute);
    border-block-end: 1.8px solid var(--text-mute);
    rotate: 45deg;
    transition: rotate var(--dur);
    flex-shrink: 0;
  }
  .faq details[open] summary::after { rotate: 225deg; }
  .faq .answer {
    padding: 0 16px 16px;
    font-size: 14px;
    color: var(--text-soft);
    line-height: 2;
  }

  .cta-band {
    display: grid;
    gap: 10px;
    justify-items: center;
    text-align: center;
    padding: clamp(28px, 2rem + 2vw, 48px);
    border-radius: var(--radius-md);
    background: linear-gradient(135deg, #1b3570, #16295c);
    color: #ffffff;
  }
  .cta-band h2 { color: #ffffff; font-size: var(--fs-xl); }
  .cta-band p { color: rgba(255, 255, 255, 0.8); font-size: 14px; }
  .cta-band .btn-ghost { --btn-fg: #ffffff; }
  .cta-band .btn-ghost:hover { --btn-bg: rgba(255, 255, 255, 0.12); color: #ffffff; }

  /* Document pages (privacy/terms) */
  .doc { display: grid; gap: 20px; font-size: 14px; }
  .doc h3 { font-size: 16px; font-weight: 700; color: var(--text-strong); }
  .doc p, .doc li { color: var(--text-soft); line-height: 2; }
  .doc ul { display: grid; gap: 6px; padding-inline-start: 18px; list-style: disc; }
  .doc a { color: var(--link); text-decoration: underline; text-underline-offset: 3px; }
  .doc a:hover { color: var(--link-hover); }
  .stamp {
    display: inline-block;
    font-size: 12px;
    color: var(--text-mute);
    background: var(--surface-2);
    border-radius: var(--radius-pill);
    padding: 4px 12px;
  }

  /* --- forms --------------------------------------------------------------- */
  .field { display: grid; gap: 6px; }
  .field > label, .label {
    font-size: 13px;
    font-weight: 600;
    color: var(--text);
  }
  .label .req { color: var(--discount-red); }

  .input, .textarea, .select {
    width: 100%;
    min-inline-size: 0;
    block-size: 44px;
    padding: 0 14px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--line);
    background: var(--surface);
    color: var(--text-strong);
    font-size: 14px;
    transition: border-color var(--dur), box-shadow var(--dur);
  }
  .textarea { block-size: auto; min-block-size: 104px; padding: 10px 14px; line-height: 1.8; resize: vertical; }
  .textarea-sm { min-block-size: 60px; }
  .input-sm { block-size: 38px; font-size: 13px; }
  .input::placeholder, .textarea::placeholder { color: var(--text-mute); font-weight: 400; }
  .input:focus, .textarea:focus, .select:focus {
    outline: none;
    border-color: var(--accent-blue);
    box-shadow: 0 0 0 3px var(--ring);
  }
  .input:disabled, .textarea:disabled, .select:disabled {
    background: var(--surface-2);
    color: var(--text-mute);
    cursor: not-allowed;
  }
  /* Rejected input has to read as rejected where it is, not only in a toast
     that has already faded: the client marks the field, the browser marks the
     half-typed one. */
  .input[aria-invalid="true"], .textarea[aria-invalid="true"], .select[aria-invalid="true"],
  .input:user-invalid, .textarea:user-invalid, .select:user-invalid {
    border-color: var(--rose);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--rose) 16%, transparent);
  }
  .input[aria-invalid="true"]:focus, .textarea[aria-invalid="true"]:focus {
    border-color: var(--rose);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--rose) 24%, transparent);
  }

  .select {
    appearance: none;
    background-image:
      linear-gradient(45deg, transparent 49%, var(--text-mute) 50%),
      linear-gradient(-45deg, transparent 49%, var(--text-mute) 50%);
    background-size: 5px 5px;
    background-position: left 14px top 55%, left 20px top 55%;
    background-repeat: no-repeat;
    padding-inline-end: 36px;
    text-overflow: ellipsis;
  }
  .select option { background: var(--surface); color: var(--text); }
  .select-inline { flex: 0 0 auto; width: auto; min-inline-size: 11rem; block-size: 38px; }
  @media (width < 34rem) { .select-inline { flex: 1 1 100%; } }

  .hint { font-size: 12px; color: var(--text-mute); }
  .error-text { font-size: 12px; color: var(--rose); font-weight: 600; }
  .ok-text { font-size: 12px; color: var(--emerald); font-weight: 600; }

  .check {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    color: var(--text-soft);
    cursor: pointer;
  }
  .check input[type="checkbox"] {
    inline-size: 18px;
    block-size: 18px;
    accent-color: var(--discount-red);
    cursor: pointer;
  }

  /* --- auth ---------------------------------------------------------------- */
  .auth {
    display: grid;
    gap: 16px;
    max-inline-size: 400px;
    margin-inline: auto;
    padding: 28px 24px;
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-md);
    background: var(--card);
    box-shadow: var(--shadow-sm);
  }
  .auth-head { display: grid; gap: 6px; text-align: center; }
  .auth-head h1 { font-size: var(--fs-xl); font-weight: 700; color: var(--text-strong); }
  .auth-head p { color: var(--text-mute); font-size: 14px; }
  .auth form { display: grid; gap: 14px; }
  .auth-alt {
    text-align: center;
    font-size: 14px;
    color: var(--text-soft);
    border-block-start: 1px solid var(--line-soft);
    padding-block: 14px 0;
  }
  .auth-alt a { color: var(--link); font-weight: 600; }
  .auth-sep {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-block: -2px;
    color: var(--text-mute);
    font-size: 13px;
  }
  .auth-sep::before, .auth-sep::after {
    content: "";
    flex: 1;
    block-size: 1px;
    background: var(--line-soft);
  }
  .auth-note {
    margin: 0;
    padding: 12px 14px;
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-sm);
    background: var(--surface-3);
    color: var(--text-soft);
    font-size: 13px;
    line-height: 1.9;
  }
  .auth-note[data-tone="bad"] {
    border-color: color-mix(in srgb, var(--rose) 40%, transparent);
    background: var(--rose-bg);
    color: var(--rose);
  }

  /* --- cart ----------------------------------------------------------------- */
  .cart-row {
    position: relative;
    display: grid;
    grid-template-columns: minmax(0, 1fr) 188px;
    gap: 8px 24px;
    padding: 20px 0 16px;
    border-block-end: 1px solid var(--line-soft);
  }
  .cart-row:first-child { padding-block-start: 6px; }
  .cart-row:last-child { border-block-end: 0; }
  .cart-thumb {
    --art-pad: 12px;
    --art-cap: 0.9;
    position: relative;
    grid-column: 2;
    grid-row: 1;
    inline-size: 188px;
    block-size: 150px;
    border-radius: 10px;
    border: 1px solid var(--line-soft);
    background: var(--surface-3);
    display: grid;
    place-items: center;
    overflow: hidden;
    align-self: start;
  }
  .cart-body { grid-column: 1; grid-row: 1; display: grid; gap: 4px; min-inline-size: 0; align-content: start; }
  .cart-title { font-size: 15px; font-weight: 600; line-height: 1.7; color: var(--text-strong); }
  .cart-title a:hover { color: var(--link); }
  .cart-specs { display: grid; gap: 7px; margin-block-start: 6px; }
  .cart-specs li {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12.5px;
    color: var(--text-soft);
  }
  .cart-specs svg { inline-size: 16px; block-size: 16px; color: var(--brand); flex-shrink: 0; }
  /* The pale panel: quantity, unit price, line total and delete in one strip,
     with the info field folding away under «نمایش جزئیات». */
  .cart-buy {
    grid-column: 1 / -1;
    margin-block-start: 14px;
    display: flex;
    align-items: center;
    gap: 14px 18px;
    flex-wrap: wrap;
    background: #eef6fd;
    border-radius: var(--radius-sm);
    padding: 12px 16px;
  }
  [data-theme="dark"] .cart-buy { background: rgba(56, 189, 248, 0.08); }
  .cart-buy-label {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    font-weight: 600;
    color: var(--text);
  }
  .cart-buy-label svg { inline-size: 19px; block-size: 19px; color: var(--emerald); }
  .cart-buy-controls { display: flex; align-items: center; gap: 16px; margin-inline-start: auto; flex-wrap: wrap; }
  .cart-unit { font-size: 13px; color: var(--text-soft); }
  .cart-unit b { font-size: 14px; font-weight: 600; color: var(--text-strong); }
  .cart-line { font-size: 15px; font-weight: 700; color: var(--text-strong); }
  /* Deleting a line has to read as a deletion. As plain grey text it hid among
     the row's own words -- the only hint it was a control at all was the cursor.
     It gets the site's red: a soft fill at rest, the solid one under the cursor.
     The fill goes through `--btn-bg` because that is what `.btn` reads, and the
     ink is set directly because `.btn-ghost` claims `color` for itself. */
  .cart-remove {
    --btn-bg: var(--rose-bg);
    color: var(--rose);
    border-color: color-mix(in oklab, var(--rose) 26%, transparent);
    font-weight: 600;
  }
  @media (hover: hover) and (pointer: fine) {
    .cart-remove:hover { --btn-bg: var(--rose); color: #ffffff; border-color: var(--rose); }
  }
  .cart-remove:active { --btn-bg: var(--discount-red); color: #ffffff; }
  .cart-details-toggle {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    margin-inline-start: auto;
    padding: 8px 2px;
    font-size: 13px;
    font-weight: 500;
    color: var(--text-soft);
    background: none;
    border: 0;
    cursor: pointer;
    font-family: inherit;
  }
  .cart-details-toggle:hover { color: var(--text-strong); }
  .cart-details-toggle svg {
    inline-size: 14px;
    block-size: 14px;
    scale: -1 1;
    transition: rotate 0.2s var(--ease-out);
  }
  .cart-details-toggle.is-open { color: var(--text-strong); }
  .cart-details-toggle.is-open svg { rotate: -90deg; }
  /* The old `max-block-size: 420px` ceiling clipped any product that asked for
     more than one field, with no scrollbar to reach the rest. The cap is now
     viewport-relative and the overflow scrollable, so the panel can never hide
     part of itself. */
  .cart-details {
    grid-column: 1 / -1;
    display: grid;
    gap: 10px;
    overflow: hidden;
    max-block-size: 0;
    opacity: 0;
    margin-block-start: 0;
    transition: max-block-size 0.25s var(--ease-out), opacity 0.2s, margin 0.2s;
  }
  .cart-details.is-open {
    max-block-size: min(70dvh, 40rem);
    overflow-y: auto;
    overscroll-behavior: contain;
    opacity: 1;
    margin-block-start: 12px;
  }
  .cart-details .info-request {
    background: var(--surface-3);
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    display: grid;
    gap: 6px;
  }
  @media (width < 56rem) {
    .cart-row { grid-template-columns: 1fr; }
    .cart-thumb {
      grid-column: 1;
      grid-row: auto;
      order: -1;
      inline-size: 100%;
      block-size: 210px;
    }
  }
  .qty {
    display: inline-flex;
    align-items: center;
    gap: 0;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    overflow: hidden;
  }
  .qty button {
    inline-size: 40px;
    block-size: 40px;
    display: grid;
    place-items: center;
    font-size: 16px;
    color: var(--text-soft);
    transition: background var(--dur), color var(--dur);
  }
  .qty output, .qty b {
    min-inline-size: 40px;
    text-align: center;
    font-size: 14px;
    font-weight: 600;
    line-height: 1;
  }
  /* The reset gives every button `cursor: pointer`; a maxed-out «+» has to look
     finished, not merely unresponsive. */
  .qty button:disabled, .stepper button:disabled {
    opacity: 0.4;
    cursor: not-allowed;
    background: none;
  }
  @media (hover: hover) and (pointer: fine) {
    .qty button:hover:not(:disabled) { background: var(--surface-2); color: var(--text-strong); }
  }
  .qty button:active:not(:disabled) { background: var(--line-soft); }

  .stepper { display: inline-flex; align-items: center; gap: 0; border: 1px solid var(--line); border-radius: var(--radius-sm); overflow: hidden; }
  .stepper button { inline-size: 40px; block-size: 40px; display: grid; place-items: center; color: var(--text-soft); }
  .stepper input, .stepper b { min-inline-size: 40px; text-align: center; border: 0; font-size: 14px; font-weight: 600; background: none; }
  @media (hover: hover) and (pointer: fine) {
    .stepper button:hover:not(:disabled) { background: var(--surface-2); }
  }

  /* The order-summary column is built by app.js as `<aside class="summary">`
     and had no rule at all: every row under it sat in a card while the money
     column floated as bare text. */
  .summary {
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-md);
    background: var(--surface);
    box-shadow: var(--shadow-sm);
    padding: 16px;
    display: grid;
    gap: var(--sp-2);
    align-content: start;
  }
  @media (width >= 62rem) {
    .summary {
      position: sticky;
      inset-block-start: calc(var(--header-h) + 16px);
    }
  }

  .sum-line, .sum-coupon, .sum-discount, .sum-off {
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 14px;
    color: var(--text-soft);
    padding-block: 6px;
  }
  .sum-total {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    padding-block-start: 10px;
    border-block-start: 1px solid var(--line-soft);
    font-size: 14px;
    color: var(--text);
  }
  .sum-total b { font-size: var(--fs-xl); font-weight: 700; color: var(--text-strong); }
  .sum-saving { color: var(--emerald); font-weight: 600; }

  .pay-card {
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-md);
    background: var(--surface-3);
    padding: 16px;
    display: grid;
    gap: 8px;
  }
  /* app.js emits these two and the stylesheet never answered: the card number
     row had no layout (the copy button wrapped under the digits) and the
     «مرحله ۱» label rendered at body size like the rest of the text. */
  .eyebrow {
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.02em;
    color: var(--text-soft);
  }
  .card-number {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 8px 12px;
    padding: 10px 12px;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface);
  }
  .card-number .mono {
    font-size: 17px;
    font-weight: 700;
    color: var(--text-strong);
    direction: ltr;
    letter-spacing: 0.06em;
    unicode-bidi: isolate;
  }
  .pay-card .k { font-size: 13px; color: var(--text-mute); }
  .pay-card .v { font-size: 16px; font-weight: 700; color: var(--text-strong); direction: ltr; text-align: left; }
  .pay-card .cap { font-size: 12px; color: var(--text-mute); }
  .k { font-size: 13px; color: var(--text-mute); font-weight: 500; }
  .v { font-size: 14px; color: var(--text-strong); font-weight: 600; }
  .cap { font-size: 12px; color: var(--text-mute); }
  .unit { font-size: 12px; color: var(--text-unit); }

  /* Receipt dropzone */
  .drop {
    border: 1.5px dashed var(--line-strong);
    border-radius: var(--radius-md);
    background: var(--surface-3);
    padding: 24px;
    display: grid;
    gap: 8px;
    justify-items: center;
    text-align: center;
    color: var(--text-mute);
    font-size: 13px;
    cursor: pointer;
    transition: border-color var(--dur), color var(--dur), background var(--dur);
  }
  .drop:hover, .drop.is-over { border-color: var(--accent-blue); color: var(--link); background: color-mix(in srgb, var(--accent-blue) 4%, var(--surface)); }
  .drop svg { inline-size: 28px; block-size: 28px; }
  .receipt-preview {
    position: relative;
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-sm);
    overflow: hidden;
    inline-size: fit-content;
  }
  .receipt-preview img { max-block-size: 180px; }
  .receipt-thumb {
    inline-size: 56px;
    block-size: 56px;
    object-fit: cover;
    border-radius: var(--radius-xs);
    border: 1px solid var(--line-soft);
  }

  /* --- orders ---------------------------------------------------------------- */
  .order {
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-md);
    background: var(--surface);
    padding: 16px;
    display: grid;
    gap: 12px;
  }
  .order-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 16px;
    padding-block-end: 10px;
    border-block-end: 1px solid var(--line-soft);
  }
  .order-code { font-size: 14px; font-weight: 700; color: var(--text-strong); }
  .order-date { font-size: 12px; color: var(--text-mute); }
  .order-items { display: grid; gap: 10px; }
  .order-item { display: grid; gap: 4px; }
  .order-item-top {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    font-size: 14px;
  }
  .order-item-top b { font-weight: 600; color: var(--text-strong); }
  .order-item-top span { color: var(--text-soft); font-size: 13px; }
  .order-note, .order-discount { font-size: 13px; color: var(--text-soft); }

  /* --- tabs (generic) --------------------------------------------------------- */
  /* The row scrolls sideways and a tab is never squeezed below its own label.
     The two used to be one rule shared by the row and its buttons, so
     `overflow-x` and `gap` landed on the buttons too and `flex-shrink` kept its
     default: on a phone the eight admin labels were crushed to 48-68px while
     their text needed 64-99px, and each word spilled over its neighbour. The
     missing line is `flex: 0 0 auto` on the tab -- with it the row cannot shrink
     the labels, so it scrolls instead. */
  .tabs {
    display: flex;
    flex-wrap: nowrap;
    gap: 4px;
    border-block-end: 1px solid var(--line-soft);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
  }
  .tabs::-webkit-scrollbar { display: none; }
  .tab {
    display: inline-flex;
    align-items: center;
    flex: 0 0 auto;
    /* A tab is never narrower than the word it carries. `flex: 0 0 auto` asks
       for that; this makes it a rule no other declaration can undo. */
    min-inline-size: max-content;
    padding: 12px 14px;
    border: 0;
    border-block-end: 2px solid transparent;
    margin-block-end: -1px;
    background: none;
    font-size: 14px;
    font-weight: 500;
    color: var(--text-soft);
    font-family: inherit;
    cursor: pointer;
    white-space: nowrap;
    transition: color var(--dur), border-color var(--dur);
  }
  .tab:hover { color: var(--text-strong); }
  .tab.is-active { color: var(--rose); border-block-end-color: var(--rose); font-weight: 600; }
  .tab-count {
    background: var(--surface-2);
    border-radius: var(--radius-pill);
    font-size: 11px;
    padding: 1px 7px;
    margin-inline-start: 4px;
    color: var(--text-soft);
  }
  .tab.is-active .tab-count { background: var(--rose-bg); color: var(--rose); }

  /* --- tables ------------------------------------------------------------------ */
  .table-wrap { overflow-x: auto; border: 1px solid var(--line-soft); border-radius: var(--radius-sm); }
  .table { inline-size: 100%; border-collapse: collapse; font-size: 13px; }
  .table th {
    background: var(--surface-3);
    font-size: 12px;
    font-weight: 600;
    color: var(--text-soft);
    text-align: right;
    padding: 10px 12px;
    white-space: nowrap;
    border-block-end: 1px solid var(--line-soft);
  }
  .table td {
    padding: 10px 12px;
    border-block-end: 1px solid var(--line-soft);
    color: var(--text);
    vertical-align: middle;
  }
  .table tr:last-child td { border-block-end: 0; }

  /* --- folds (collapsibles in admin/orders) ------------------------------------ */
  .fold { border: 1px solid var(--line-soft); border-radius: var(--radius-sm); background: var(--surface); overflow: hidden; }
  .fold-head {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 12px 14px;
    cursor: pointer;
    font-size: 14px;
    font-weight: 600;
    color: var(--text-strong);
  }
  .fold-head::after {
    content: "";
    margin-inline-start: auto;
    inline-size: 8px;
    block-size: 8px;
    border-inline-end: 1.8px solid var(--text-mute);
    border-block-end: 1.8px solid var(--text-mute);
    rotate: 45deg;
    transition: rotate var(--dur);
  }
  .fold[open] > .fold-head::after, .fold-show .fold-head::after { rotate: 225deg; }
  .fold-body { padding: 0 14px 14px; display: grid; gap: 10px; font-size: 13px; }
  .fold-hide { display: none; }

  /* --- messages / conversations -------------------------------------------------- */
  .convo-layout { display: grid; gap: 16px; }
  @media (width >= 62rem) {
    .convo-layout { grid-template-columns: 18rem minmax(0, 1fr); align-items: start; }
  }
  .convo-list { display: flex; flex-direction: column; gap: 8px; }
  .thread {
    display: grid;
    gap: 6px;
    padding: 12px 14px;
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-sm);
    background: var(--surface);
    cursor: pointer;
    transition: border-color var(--dur);
    text-align: right;
    width: 100%;
    font-family: inherit;
  }
  .thread:hover { border-color: var(--line); }
  .thread.is-active { border-color: var(--accent-blue); }
  .convo-head { display: flex; flex-direction: column; gap: 2px; }
  .convo-subject { font-size: 14px; font-weight: 600; color: var(--text-strong); }
  .convo-date { font-size: 12px; color: var(--text-mute); }
  .convo-meta { display: flex; align-items: center; gap: 8px; }
  .msg { display: grid; gap: 4px; max-inline-size: min(100%, 42rem); }
  .msg-body, .msg {
    font-size: 14px;
    line-height: 1.9;
  }
  .msg { padding: 12px 14px; border-radius: var(--radius-md); background: var(--surface-3); border: 1px solid var(--line-soft); }
  .msg.mine { background: color-mix(in srgb, var(--brand) 6%, var(--surface)); border-color: color-mix(in srgb, var(--brand) 18%, transparent); }
  .msg-head { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text-mute); }
  .msg-head b { color: var(--text-soft); font-weight: 600; }
  .msg-new { color: var(--rose); font-size: 12px; font-weight: 600; }
  .notice {
    border: 1px solid color-mix(in srgb, var(--gold) 35%, transparent);
    background: var(--gold-bg);
    border-radius: var(--radius-sm);
    padding: 12px 14px;
    display: grid;
    gap: 4px;
  }
  .notice-title { font-size: 14px; font-weight: 700; color: var(--text-strong); }
  .notice-body { font-size: 13px; color: var(--text-soft); line-height: 1.9; }
  .notice-date { font-size: 12px; color: var(--text-mute); }
  .convo-ways { font-size: 13px; color: var(--text-soft); }
  .credentials {
    background: var(--surface-3);
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    font-size: 13px;
    line-height: 1.9;
    white-space: pre-line;
    word-break: break-word;
  }
  .req { color: var(--discount-red); }
  .info-request, .req-info {
    font-size: 13px;
    color: var(--text-soft);
    background: var(--surface-3);
    border-radius: var(--radius-sm);
    padding: 8px 10px;
  }
  .coupon-row {
    display: flex;
    /* The note under the field has to have a line of its own: on one line it
       took its width from the row and squeezed the code box to a sliver. */
    flex-wrap: wrap;
    gap: 8px;
    align-items: start;
  }
  .coupon-row .input { flex: 1; min-inline-size: 0; }
  .coupon-row .error-text { flex: 1 0 100%; }
  .user-facts { display: grid; gap: 8px; font-size: 13px; }
  .user-facts .row { justify-content: space-between; }
  .dot { inline-size: 8px; block-size: 8px; border-radius: 50%; background: var(--text-mute); display: inline-block; }
  .dot.good { background: var(--emerald); }
  .dot.warn { background: var(--gold); }
  .dot.bad { background: var(--rose); }
  .check svg { color: var(--emerald); }

  /* --- admin ---------------------------------------------------------------------- */
  .stat-grid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
  .stat-grid-sm { grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); }
  .stat {
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-sm);
    background: var(--surface);
    padding: 14px;
    display: grid;
    gap: 4px;
  }
  .stat .k { font-size: 12px; }
  .stat .v { font-size: var(--fs-xl); font-weight: 700; }

  .img-pick { display: grid; gap: 8px; }
  .img-pick-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
  .img-pick-view {
    inline-size: 72px;
    block-size: 72px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--line-soft);
    background: var(--surface-3) center / contain no-repeat;
  }
  .img-pick-body { font-size: 12px; color: var(--text-mute); }

  .lightbox {
    position: fixed;
    inset: 0;
    z-index: 200;
    background: rgba(0, 0, 0, 0.8);
    display: grid;
    place-items: center;
    padding: 24px;
    cursor: zoom-out;
  }
  .lightbox img { max-inline-size: min(90vw, 64rem); max-block-size: 88vh; border-radius: var(--radius-sm); }

  /* --- dialogs / modals ------------------------------------------------------------- */
  dialog.modal, dialog.sheet {
    margin: auto;
    border: 0;
    border-radius: var(--radius-lg);
    background: var(--sheet);
    color: var(--text);
    inline-size: min(92vw, 28rem);
    padding: 0;
    box-shadow: var(--shadow-pop);
    /* Admin edit dialogs run taller than a laptop screen; with no cap the form
       simply overran the viewport and the save button was unreachable. */
    max-block-size: min(88dvh, 46rem);
    overflow-y: auto;
    overscroll-behavior: contain;
    padding-inline: max(0px, env(safe-area-inset-left, 0px));
  }
  dialog.sheet { margin-inline-end: 0; margin-block: 0; margin-inline-start: auto; inline-size: min(92vw, 24rem); block-size: 100dvh; max-block-size: 100dvh; border-radius: 0; }
  dialog::backdrop {
    background: rgba(10, 16, 28, 0.55);
    backdrop-filter: blur(2px);
  }
  .modal-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 16px 20px;
    border-block-end: 1px solid var(--line-soft);
    font-size: 15px;
    font-weight: 700;
    color: var(--text-strong);
  }
  .modal-body { padding: 20px; display: grid; gap: 14px; font-size: 14px; line-height: 1.9; }
  .modal-foot {
    display: flex;
    gap: 8px;
    justify-content: flex-end;
    padding: 0 20px 20px;
  }

  /* --- arrange products (admin) ------------------------------------------------------ */
  dialog.modal-arrange { inline-size: min(94vw, 34rem); }
  .arrange-body { gap: 18px; }
  .arrange-body .hint { line-height: 1.9; margin: 0; }
  .arrange-body [data-arrange] { display: grid; gap: 22px; }
  .arrange-group { display: grid; gap: 8px; }
  .arrange-title {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin: 0;
    font-size: 13px;
    font-weight: 700;
    color: var(--text-strong);
  }
  .arrange-title .muted { font-size: 12px; font-weight: 400; }
  .arrange-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
  .arrange-row {
    display: flex;
    align-items: center;
    gap: 10px;
    block-size: 50px;
    padding-inline: 6px 8px;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    transition: transform 0.18s ease, border-color 0.3s ease, box-shadow 0.18s ease;
    user-select: none;
    -webkit-user-select: none;
  }
  .arrange-row.is-dragging {
    position: relative;
    z-index: 2;
    border-color: var(--brand);
    box-shadow: var(--shadow-lg);
    transition: none;
  }
  .arrange-row.is-moved { border-color: var(--brand); }
  .arrange-grip {
    touch-action: none;
    cursor: grab;
    border: 0;
    background: transparent;
    color: var(--text-mute);
    font-size: 18px;
    line-height: 1;
    padding: 12px 6px;
    border-radius: var(--radius-sm);
  }
  .arrange-grip:hover { color: var(--brand); background: var(--surface-2); }
  .arrange-row.is-dragging .arrange-grip { cursor: grabbing; color: var(--brand); }
  .arrange-pos {
    min-inline-size: 1.4em;
    text-align: center;
    font-size: 12px;
    font-weight: 700;
    color: var(--brand);
  }
  .arrange-name {
    flex: 1;
    min-inline-size: 0;
    font-size: 14px;
    color: var(--text-strong);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .arrange-name.is-off { color: var(--text-mute); }
  .arrange-arrows { display: flex; gap: 4px; }
  .arrange-arrow {
    inline-size: 32px;
    block-size: 32px;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface-3);
    color: var(--text-soft);
    font-size: 11px;
    cursor: pointer;
  }
  .arrange-arrow:hover:not(:disabled) { color: var(--brand); border-color: var(--brand); }
  .arrange-arrow:disabled { opacity: 0.3; cursor: default; }
  .arrange-foot {
    position: sticky;
    inset-block-end: 0;
    padding-block-start: 12px;
    background: var(--sheet);
    border-block-start: 1px solid var(--line-soft);
  }

  /* --- password recovery ---------------------------------------------------------- */
  .auth-badge {
    inline-size: 64px;
    block-size: 64px;
    margin: 0 auto 6px;
    display: grid;
    place-items: center;
    border-radius: 20px;
    color: #ffffff;
    background: linear-gradient(135deg, var(--brand), var(--accent-blue));
    box-shadow: 0 10px 28px -10px color-mix(in srgb, var(--brand) 70%, transparent);
    animation: badge-pop 0.5s cubic-bezier(0.2, 1.4, 0.4, 1) both;
  }
  .auth-badge svg { inline-size: 30px; block-size: 30px; }
  .auth-badge[data-tone="good"] { background: linear-gradient(135deg, var(--emerald), #19b56b); box-shadow: 0 10px 28px -10px color-mix(in srgb, var(--emerald) 70%, transparent); }
  .auth-badge[data-tone="bad"] { background: linear-gradient(135deg, var(--rose), #ee5a63); box-shadow: 0 10px 28px -10px color-mix(in srgb, var(--rose) 70%, transparent); }
  @keyframes badge-pop { from { transform: scale(0.6) rotate(-8deg); opacity: 0; } to { transform: none; opacity: 1; } }
  .auth-head p b { color: var(--text-strong); }
  .auth-forgot {
    justify-self: end;
    margin-block-start: -6px;
    font-size: 13px;
    font-weight: 600;
    color: var(--link);
  }
  .auth-forgot:hover { color: var(--link-hover); }
  .auth-points { display: grid; gap: 8px; margin: 0; padding: 0; list-style: none; }
  .auth-points li {
    display: flex;
    gap: 10px;
    align-items: flex-start;
    font-size: 13px;
    line-height: 1.9;
    color: var(--text-soft);
  }
  .auth-points svg { flex: none; inline-size: 18px; block-size: 18px; margin-block-start: 3px; color: #229ed9; }
  .auth-steps {
    display: grid;
    gap: 10px;
    margin: 0;
    padding: 0;
    list-style: none;
  }
  .auth-steps li {
    position: relative;
    padding-inline-start: 38px;
    font-size: 14px;
    line-height: 1.9;
    color: var(--text-soft);
  }
  .auth-steps li::before {
    content: attr(data-n);
    position: absolute;
    inset-inline-start: 0;
    inset-block-start: 1px;
    inline-size: 26px;
    block-size: 26px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: color-mix(in srgb, var(--brand) 12%, transparent);
    color: var(--brand);
    font-size: 13px;
    font-weight: 700;
  }
  .auth-help {
    display: grid;
    gap: 10px;
    padding: 14px;
    border-radius: var(--radius-sm);
    background: var(--surface-3);
    font-size: 13px;
    line-height: 1.9;
    color: var(--text-soft);
  }
  .auth-help p { margin: 0; }
  .auth-row { align-items: center; }
  .pass-wrap { position: relative; }
  .pass-wrap .input { padding-inline-end: 64px; }
  .pass-eye {
    position: absolute;
    inset-inline-end: 6px;
    inset-block: 6px;
    padding-inline: 10px;
    border: 0;
    border-radius: var(--radius-sm);
    background: var(--surface-3);
    color: var(--text-soft);
    font: inherit;
    font-size: 12px;
    cursor: pointer;
  }
  .pass-eye:hover { color: var(--brand); }
  .pass-meter { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; margin-block-start: 8px; }
  .pass-meter i { block-size: 5px; border-radius: 99px; background: var(--line-soft); transition: background 0.25s ease; }
  .pass-meter[data-score="0"] i:nth-child(-n+1) { background: var(--rose); }
  .pass-meter[data-score="1"] i:nth-child(-n+1) { background: #f59e0b; }
  .pass-meter[data-score="2"] i:nth-child(-n+2) { background: #f59e0b; }
  .pass-meter[data-score="3"] i:nth-child(-n+3) { background: var(--accent-blue); }
  .pass-meter[data-score="4"] i { background: var(--emerald); }
  .pass-meter-label { margin: 4px 0 0; min-block-size: 1.4em; font-size: 12px; color: var(--text-mute); }

  /* --- Telegram -------------------------------------------------------------------- */
  .btn-tg {
    --btn-bg: #229ed9;
    color: #ffffff;
    border-color: transparent;
    gap: 8px;
  }
  .btn-tg:hover { --btn-bg: #1b8cc2; color: #ffffff; }
  .btn-tg svg, .btn svg.tg { inline-size: 18px; block-size: 18px; }
  .btn svg { flex: none; inline-size: 16px; block-size: 16px; }
  .tg-card {
    position: relative;
    display: grid;
    gap: 14px;
    overflow: hidden;
    isolation: isolate;
  }
  .tg-card::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    block-size: 4px;
    background: linear-gradient(90deg, #229ed9, var(--accent-blue), var(--brand));
    z-index: -1;
  }
  .tg-card-head { display: flex; align-items: center; gap: 12px; }
  .tg-card-head h2 { margin: 0; }
  .tg-mark {
    flex: none;
    inline-size: 44px;
    block-size: 44px;
    display: grid;
    place-items: center;
    border-radius: 14px;
    color: #ffffff;
    background: linear-gradient(135deg, #2aabee, #229ed9);
    box-shadow: 0 8px 20px -8px #229ed9;
  }
  .tg-mark svg { inline-size: 24px; block-size: 24px; transform: translateX(-1px); }
  .tg-status { display: flex; align-items: center; gap: 6px; margin: 2px 0 0; font-size: 12px; color: var(--text-mute); }
  .tg-dot { inline-size: 8px; block-size: 8px; border-radius: 50%; background: var(--text-mute); }
  .tg-card[data-state="linked"] .tg-status { color: var(--emerald); font-weight: 600; }
  .tg-card[data-state="linked"] .tg-dot { background: var(--emerald); box-shadow: 0 0 0 4px color-mix(in srgb, var(--emerald) 20%, transparent); }
  .tg-card[data-state="waiting"] .tg-status { color: #229ed9; font-weight: 600; }
  .tg-card[data-state="waiting"] .tg-dot { background: #229ed9; animation: tg-pulse 1.2s ease-in-out infinite; }
  @keyframes tg-pulse { 0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, #229ed9 55%, transparent); } 50% { box-shadow: 0 0 0 6px transparent; } }
  .tg-perks { display: grid; gap: 7px; margin: 0; padding: 0; list-style: none; }
  .tg-perks li { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text-soft); }
  .tg-perks svg { flex: none; inline-size: 16px; block-size: 16px; padding: 2px; border-radius: 50%; color: #229ed9; background: color-mix(in srgb, #229ed9 12%, transparent); }
  .tg-perks.is-on svg { color: var(--emerald); background: var(--emerald-bg); }
  .tg-wait { block-size: 4px; border-radius: 99px; background: var(--line-soft); overflow: hidden; }
  .tg-wait-bar { display: block; inline-size: 40%; block-size: 100%; border-radius: inherit; background: #229ed9; animation: tg-slide 1.4s ease-in-out infinite; }
  @keyframes tg-slide { from { transform: translateX(160%); } to { transform: translateX(-260%); } }

  /* --- automatic reply after a message ----------------------------------------------- */
  .sent-card {
    position: relative;
    display: flex;
    gap: 14px;
    align-items: flex-start;
    padding: 18px 20px;
    margin-block-end: 18px;
    border: 1px solid color-mix(in srgb, var(--emerald) 35%, var(--line-soft));
    border-radius: var(--radius-md);
    background:
      linear-gradient(135deg, color-mix(in srgb, var(--emerald) 10%, transparent), transparent 60%),
      var(--card);
    box-shadow: var(--shadow-sm);
    animation: sent-in 0.45s cubic-bezier(0.2, 1, 0.3, 1) both;
  }
  @keyframes sent-in { from { opacity: 0; transform: translateY(-8px) scale(0.98); } to { opacity: 1; transform: none; } }
  .sent-badge {
    flex: none;
    inline-size: 42px;
    block-size: 42px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    color: #ffffff;
    background: var(--emerald);
    box-shadow: 0 0 0 6px var(--emerald-bg);
  }
  .sent-badge svg { inline-size: 22px; block-size: 22px; }
  .sent-body { display: grid; gap: 6px; min-inline-size: 0; }
  .sent-body h2 { margin: 0; font-size: var(--fs-xl); color: var(--text-strong); }
  .sent-body p { margin: 0; font-size: 14px; line-height: 1.9; color: var(--text-soft); }
  .sent-body b { color: var(--text-strong); }
  .sent-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-block-start: 4px; }
  .sent-close {
    position: absolute;
    inset-block-start: 10px;
    inset-inline-end: 10px;
    inline-size: 28px;
    block-size: 28px;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--text-mute);
    cursor: pointer;
  }
  .sent-close:hover { background: var(--surface-3); color: var(--text-strong); }
  .eta-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    justify-self: start;
    margin: -4px 0 0;
    padding: 4px 12px;
    border-radius: 99px;
    background: color-mix(in srgb, var(--emerald) 12%, transparent);
    color: var(--emerald);
    font-size: 12px;
    font-weight: 600;
  }
  .eta-chip::before { content: ""; inline-size: 7px; block-size: 7px; border-radius: 50%; background: currentColor; }
  .reset-link {
    padding: 12px;
    border-radius: var(--radius-sm);
    background: var(--surface-3);
    font-size: 12px;
    word-break: break-all;
    user-select: all;
  }
  @media (max-width: 560px) {
    .sent-card { padding: 16px; gap: 12px; }
    .sent-badge { inline-size: 36px; block-size: 36px; }
    .sent-actions .btn { flex: 1 1 100%; justify-content: center; }
    .auth { padding: 22px 16px; }
  }
  @media (prefers-reduced-motion: reduce) {
    .auth-badge, .sent-card, .tg-wait-bar, .tg-card[data-state="waiting"] .tg-dot { animation: none; }
  }

  /* --- warranty, seen, delivered accounts ---------------------------------------- */
  .warranty-card {
    display: flex;
    gap: 12px;
    align-items: flex-start;
    padding: 14px 16px;
    border: 1px solid color-mix(in srgb, var(--emerald) 35%, var(--line-soft));
    border-radius: var(--radius-md);
    background: linear-gradient(135deg, color-mix(in srgb, var(--emerald) 9%, transparent), transparent 70%), var(--card);
  }
  .warranty-icon {
    flex: none;
    inline-size: 38px;
    block-size: 38px;
    display: grid;
    place-items: center;
    border-radius: 12px;
    color: #ffffff;
    background: var(--emerald);
  }
  .warranty-icon svg { inline-size: 20px; block-size: 20px; }
  .warranty-card b { display: block; font-size: 14px; color: var(--text-strong); }
  .warranty-card p { margin: 2px 0 0; font-size: 13px; line-height: 1.9; color: var(--text-soft); white-space: pre-line; }
  .order-item .warranty-card { margin-block-start: 10px; padding: 10px 12px; }
  .seen-tick {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-inline-start: 6px;
    padding: 1px 8px;
    border-radius: 99px;
    font-size: 11px;
    font-weight: 600;
    white-space: nowrap;
    color: var(--text-mute);
    background: var(--surface-3);
  }
  .seen-tick.is-seen { color: #229ed9; background: color-mix(in srgb, #229ed9 12%, transparent); }
  .msg-seen { margin: 6px 0 0; }
  .delivered-list { display: grid; gap: 10px; }
  .delivered-order { display: grid; gap: 8px; padding: 12px; border: 1px solid var(--line-soft); border-radius: var(--radius-sm); background: var(--surface-3); }
  .delivered-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
  .delivered-item { display: grid; gap: 6px; padding-block-start: 8px; border-block-start: 1px dashed var(--line); }
  .delivered-item:first-of-type { border-block-start: 0; padding-block-start: 0; }
  .delivered-name { font-weight: 700; font-size: 13px; color: var(--text-strong); }
  .delivered-pre {
    margin: 0;
    padding: 10px 12px;
    border-radius: var(--radius-sm);
    background: var(--card);
    border: 1px solid var(--line-soft);
    font-family: var(--font-mono, monospace);
    font-size: 12px;
    line-height: 1.8;
    white-space: pre-wrap;
    word-break: break-word;
    direction: ltr;
    text-align: left;
  }
  .delivered-item .btn { justify-self: start; }
  .delivered-warranty { margin: 0; font-size: 12px; color: var(--emerald); }
  /* A dialog with many actions (the customer dialog) wraps them on a phone instead of
     letting the labels run into each other. */
  .modal-foot { flex-wrap: wrap; row-gap: 8px; }
  @media (max-width: 560px) {
    .modal-foot .btn { flex: 1 1 auto; }
    .modal-foot .push { display: none; }
  }

  /* --- toasts -------------------------------------------------------------------------- */
  .toasts {
    position: fixed;
    /* --nav-h is 0 on the desktop and the bottom bar's full height on a phone:
       at a flat 16px the toast sat on top of the navigation and ate taps on it
       for its whole 3.2s, at z-index 210 versus the bar's 100. --buy-bar-h is the
       product page's own pinned bar, which is 0 everywhere else. */
    inset-block-end: calc(16px + var(--nav-h) + var(--buy-bar-h, 0px));
    inset-inline: 16px;
    z-index: 210;
    display: grid;
    gap: 8px;
    justify-items: center;
    pointer-events: none;
  }
  .toast {
    pointer-events: auto;
    display: flex;
    align-items: center;
    gap: 10px;
    background: var(--surface);
    color: var(--text-strong);
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-pop);
    border-inline-start: 3px solid var(--brand);
    padding: 12px 16px;
    font-size: 14px;
    font-weight: 500;
    max-inline-size: 32rem;
    animation: toastIn 0.25s var(--ease-out);
  }
  .toast[data-tone="good"] { border-inline-start-color: var(--emerald); }
  .toast[data-tone="bad"] { border-inline-start-color: var(--discount-red); }
  .toast[data-tone="warn"] { border-inline-start-color: var(--gold); }
  @keyframes toastIn {
    from { opacity: 0; transform: translateY(8px); }
    to { opacity: 1; transform: translateY(0); }
  }

  /* --- empty / skeleton ------------------------------------------------------------------- */
  .empty {
    display: grid;
    gap: 8px;
    justify-items: center;
    text-align: center;
    padding: clamp(32px, 2rem + 3vw, 64px) 16px;
    border: 1px dashed var(--line);
    border-radius: var(--radius-md);
    background: var(--surface);
  }
  .empty .emoji { font-size: 40px; line-height: 1; }
  .empty h2 { font-size: 16px; font-weight: 700; color: var(--text-strong); }
  .empty p { font-size: 14px; color: var(--text-mute); }

  .skeleton {
    border-radius: var(--radius-sm);
    background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 50%, var(--surface-2) 75%);
    background-size: 200% 100%;
    animation: shimmer 1.4s linear infinite;
    min-block-size: 14px;
  }
  .skeleton.line { block-size: 14px; }
  @keyframes shimmer { to { background-position: -200% 0; } }

  /* --- footer — flat #1B3570 band, the reference's own footer -------------- */
  .site-footer {
    margin-block-start: clamp(2.5rem, 2rem + 3vw, 4.5rem);
    /* Fixed navy: the footer is the brand's dark anchor, never a themed token
       that turns pale when the lights go out. */
    background: #1b3570;
    color: #ffffff;
  }
  .site-footer-card {
    padding-block: clamp(32px, 2rem + 2vw, 56px) 24px;
    display: flex;
    flex-direction: column;
    gap: 24px;
  }
  .footer-top-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 16px;
    padding-block-end: 24px;
    border-block-end: 1px solid rgba(255, 255, 255, 0.12);
  }
  /* The footer sits on navy in both themes, so it always wears the glowing
     silver lockup. */
  .site-footer .footer-brand .brand-logo {
    background-image: url("/assets/logo-word-dark.d7859d3707.webp");
    aspect-ratio: 1671 / 417;
    block-size: 46px;
  }
  .tl-back-to-top {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    block-size: 40px;
    padding: 0 18px;
    border-radius: var(--radius-sm);
    background: #ffffff;
    /* Literal navy, not --brand-ink: the button's white field does not follow
       the theme, and on the night skin the token is a pale periwinkle that sat
       at 2.0:1 on it. */
    color: #1b3570;
    font-size: 13px;
    font-weight: 600;
    font-family: inherit;
    border: 0;
    cursor: pointer;
    transition: background var(--dur);
  }
  @media (hover: hover) and (pointer: fine) {
    .tl-back-to-top:hover { background: #eef1f6; }
  }
  .tl-back-to-top svg { inline-size: 16px; block-size: 16px; }

  .footer-contact-cta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px 20px;
    font-size: 14px;
    color: rgba(255, 255, 255, 0.85);
  }
  .fc-lead { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; }
  .fc-label { color: rgba(255, 255, 255, 0.7); font-size: 13px; }
  /* nowrap + no shrink: as a plain flex item the number was squeezed to
     min-content and broke one glyph per line on a phone. */
  .fc-phone {
    color: #ffffff;
    font-size: 20px;
    font-weight: 700;
    direction: ltr;
    white-space: nowrap;
    flex-shrink: 0;
    unicode-bidi: isolate;
  }
  .fc-phone:hover { text-decoration: underline; }
  .fc-sep { color: rgba(255, 255, 255, 0.3); }
  .fc-desc { font-size: 13px; color: rgba(255, 255, 255, 0.7); }

  .trust-strip {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 12px;
  }
  @media (width >= 56rem) { .trust-strip { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
  .trust-item {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 12px;
    padding: 14px 16px;
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: var(--radius-sm);
  }
  .trust-icon {
    inline-size: 34px;
    block-size: 34px;
    border-radius: var(--radius-sm);
    background: rgba(255, 255, 255, 0.1);
    color: #ffffff;
    display: grid;
    place-items: center;
    flex-shrink: 0;
    align-self: center;
  }
  .trust-icon svg { inline-size: 17px; block-size: 17px; }
  .trust-item b { display: block; font-size: 13px; font-weight: 600; color: #ffffff; }
  /* 0.6 white on the navy band measured 4.4:1 for 12px text; the footer is the
     one surface that stays navy in both themes, so it has to clear AA on its
     own terms. Scoped to the text column: `.trust-icon` is a span too, and
     `display: block` landing on it switched off the grid that centres its glyph. */
  .trust-item div span { display: block; font-size: 12px; color: rgba(255, 255, 255, 0.74); line-height: 1.7; }

  .footer-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 24px;
  }
  @media (width >= 44rem) { .footer-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
  @media (width >= 62rem) { .footer-grid { grid-template-columns: 1.4fr 1fr 1fr 1fr; } }
  .footer-head {
    font-size: 15px;
    font-weight: 700;
    color: #ffffff;
    margin-block-end: 12px;
  }
  .footer-col { display: flex; flex-direction: column; gap: 10px; }
  .footer-col p { font-size: 13px; color: rgba(255, 255, 255, 0.76); line-height: 2; overflow-wrap: break-word; }
  .footer-col a {
    min-block-size: 24px;
    display: inline-flex;
    align-items: center;
    font-size: 13px;
    color: rgba(255, 255, 255, 0.88);
    transition: color var(--dur);
  }
  .footer-col a:hover { color: #ffffff; }
  .socials { display: flex; gap: 8px; margin-block-start: 4px; }
  .socials a {
    display: grid;
    place-items: center;
    inline-size: 36px;
    block-size: 36px;
    border-radius: var(--radius-sm);
    background: rgba(255, 255, 255, 0.08);
    color: #ffffff;
    transition: background var(--dur);
  }
  .socials a:hover { background: rgba(255, 255, 255, 0.2); }
  .socials a svg { inline-size: 18px; block-size: 18px; }

  .footer-cert-row {
    display: flex;
    justify-content: center;
    gap: 12px;
    padding-block: 16px;
    border-block-start: 1px solid rgba(255, 255, 255, 0.12);
    flex-wrap: wrap;
  }
  .cert-box {
    padding: 10px 20px;
    border-radius: var(--radius-sm);
    background: #ffffff;
    font-size: 12px;
    font-weight: 600;
    /* Literal navy, same reason as the back-to-top button. */
    color: #1b3570;
    text-align: center;
    min-inline-size: 128px;
    display: grid;
    place-items: center;
  }

  .footer-base {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding-block-start: 16px;
    border-block-start: 1px solid rgba(255, 255, 255, 0.12);
    font-size: 12px;
    color: rgba(255, 255, 255, 0.74);
  }
  .footer-legal { display: flex; gap: 20px; }
  .footer-legal a { min-block-size: 24px; display: inline-flex; align-items: center; color: rgba(255, 255, 255, 0.82); transition: color var(--dur); }
  .footer-legal a:hover { color: #ffffff; text-decoration: underline; }
  .footer-legal a, .footer-col a { text-underline-offset: 3px; }

  /* --- 404 — the reference's navy panel with the white pill button -------- */
  .notfound {
    display: grid;
    gap: 10px;
    justify-items: center;
    text-align: center;
    padding: clamp(48px, 3rem + 6vw, 120px) 24px;
    border-radius: var(--radius-md);
    background: #1b3570;
    color: #ffffff;
  }
  .notfound-code {
    font-size: clamp(56px, 3rem + 5vw, 120px);
    font-weight: 800;
    line-height: 1;
    color: #ffffff;
  }
  .notfound h1 { color: #ffffff; font-size: var(--fs-xl); }
  .notfound p { color: rgba(255, 255, 255, 0.75); font-size: 14px; }
  .nf-home {
    margin-block-start: 16px;
    display: inline-flex;
    align-items: center;
    block-size: 44px;
    padding: 0 28px;
    border-radius: var(--radius-pill);
    background: #ffffff;
    /* --text-strong is #ffffff on the night skin, which over this white pill
       made the only way home on a 404 invisible in dark mode. */
    color: #161616;
    font-size: 14px;
    font-weight: 600;
    transition: background var(--dur);
  }
  @media (hover: hover) and (pointer: fine) {
    .nf-home:hover { background: #eef1f6; }
  }

  /* --- mobile drawer ----------------------------------------------------- */
  .drawer {
    margin: 0;
    margin-inline-end: auto;
    border: 0;
    border-inline-end: 1px solid var(--line-soft);
    background: var(--surface);
    color: var(--text);
    inline-size: min(88%, 19rem);
    block-size: 100dvh;
    max-block-size: 100dvh;
    /* The nav list plus four footer buttons is taller than a small phone, and
       with no scroll the «ورود» button at the bottom simply did not exist. */
    padding: 16px;
    padding-block-end: max(16px, env(safe-area-inset-bottom, 0px));
    padding-inline: max(16px, env(safe-area-inset-left, 0px)) max(16px, env(safe-area-inset-right, 0px));
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  .drawer::backdrop {
    background: rgba(10, 16, 28, 0.55);
    backdrop-filter: blur(2px);
  }
  .drawer[open] {
    display: flex;
    flex-direction: column;
    animation: drawer-in var(--dur) var(--ease-out);
  }
  @keyframes drawer-in { from { translate: 100% 0; } }
  .drawer-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding-block-end: 12px;
    border-block-end: 1px solid var(--line-soft);
  }
  .drawer-nav { display: grid; gap: 2px; padding-block: 12px; }
  .drawer-nav a {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px;
    border-radius: var(--radius-sm);
    color: var(--text);
    font-size: 14px;
    font-weight: 500;
    transition: background var(--dur);
  }
  .drawer-nav a:hover { background: var(--surface-2); }
  /* The unread count at the far end of its row, so the label reads as a sentence
     and the number as the news attached to it. Round rather than the badge's
     rectangle because it holds one or two digits and nothing else. */
  .drawer-count {
    margin-inline-start: auto;
    min-inline-size: 22px;
    block-size: 22px;
    padding: 0 6px;
    border-radius: var(--radius-pill);
    font-size: 11.5px;
    line-height: 1;
    justify-content: center;
  }
  .drawer-nav a[aria-current="page"] {
    background: var(--rose-bg);
    color: var(--rose);
    font-weight: 600;
  }
  .drawer-foot { margin-block-start: auto; display: grid; gap: 8px; }

  /* --- scroll reveal ------------------------------------------------------ */
  @supports (animation-timeline: view()) {
    @media (prefers-reduced-motion: no-preference) {
      .reveal {
        animation: reveal linear both;
        animation-timeline: view();
        animation-range: entry 5% cover 22%;
      }
    }
  }
  @keyframes reveal {
    from { opacity: 0; translate: 0 14px; }
    to { opacity: 1; translate: 0 0; }
  }

  /* --- state hooks the client emits ---------------------------------------- */
  /* app.js and src/shared.js drive these with attributes and data-* values
     while the stylesheet only answered to classes, so the state changed and
     nothing on screen moved. Each rule here is the missing half of a pair. */

  /* `cartSummary()` appends the word `hidden` as a class, not the attribute,
     so the checkout button stayed live under the «remove unavailable items»
     error. One utility covers both spellings. */
  .hidden { display: none !important; }

  /* Admin tabstrips set aria-selected only; the visual state lives on
     .is-active, so the open tab looked unselected. */
  .tab[aria-selected="true"],
  .tab[aria-current="true"] {
    color: var(--rose);
    border-block-end-color: var(--rose);
    font-weight: 600;
  }
  .tab[aria-selected="true"] .tab-count { background: var(--rose-bg); color: var(--rose); }
  /* The order and ticket strips are filters wearing the tab skin. */
  .tab[aria-pressed="true"] { color: var(--rose); font-weight: 600; }

  /* The receipt dropzone sets data-over="1" while a file is dragged in. */
  .drop[data-over="1"] {
    border-color: var(--accent-blue);
    color: var(--accent-blue);
    background: color-mix(in srgb, var(--accent-blue) 6%, var(--surface));
  }

  /* `withBusy()` marks the button; without a mark a pending submit looked like
     a dead button, which reads as a broken form rather than a working one. */
  .btn[data-busy="1"] { opacity: 1; cursor: progress; }
  .btn[data-busy="1"]::after {
    content: "";
    inline-size: 14px;
    block-size: 14px;
    flex-shrink: 0;
    border-radius: 50%;
    border: 2px solid currentColor;
    border-block-start-color: transparent;
    animation: spin 0.7s linear infinite;
  }
  @keyframes spin { to { rotate: 360deg; } }

  /* The checkout stepper: done steps and the current one were bare text. */
  .stepper li {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 10px;
    border-radius: var(--radius-pill);
    font-size: 13px;
    color: var(--text-mute);
  }
  .stepper li[aria-current="step"] {
    background: var(--rose-bg);
    color: var(--rose);
    font-weight: 700;
  }
  .stepper li[data-done],
  .stepper li[data-done="1"] { color: var(--emerald); font-weight: 600; }
  .stepper li[data-done]::before,
  .stepper li[data-done="1"]::before {
    content: "✓";
    font-size: 12px;
    inline-size: 16px;
    block-size: 16px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: var(--emerald-bg);
  }

  /* Admin KPI tiles carry data-tone; nothing read it, so «pending review»
     looked exactly like «users». */
  .stat[data-tone="warn"] { border-color: color-mix(in srgb, var(--gold) 40%, transparent); background: var(--gold-bg); }
  .stat[data-tone="good"] { border-color: color-mix(in srgb, var(--emerald) 40%, transparent); background: var(--emerald-bg); }
  .stat[data-tone="brand"] { border-color: color-mix(in srgb, var(--brand) 30%, transparent); }

  /* Threads: the reader's own messages were styled by .msg.mine, but the
     client marks them with data-from. */
  .msg[data-from="user"], .msg.mine {
    background: color-mix(in srgb, var(--brand) 6%, var(--surface));
    border-color: color-mix(in srgb, var(--brand) 18%, transparent);
  }

  /* A toast's dot was always grey, including on a red error; the tone lives on
     the toast itself. */
  .toast .dot { flex-shrink: 0; }
  .toast[data-tone="good"] .dot { background: var(--emerald); }
  .toast[data-tone="bad"] .dot { background: var(--discount-red); }
  .toast[data-tone="warn"] .dot { background: var(--gold); }

  /* `<details class="fold">` label swap: the open state had no rule, so an
     expanded thread still invited you to expand it. */
  .fold[open] .fold-hide { display: none; }
  .fold[open] .fold-show { display: inline; }
  .fold-show { display: none; }

  /* Catalogue results grid: filters repaint it in place, and with no reserved
     height the viewport snapped upward on every keystroke. */
  .catalog-grid-wrap { min-block-size: 24rem; }

  /* Unread rows carried no mark. */
  .thread.is-unread, .is-unread { border-inline-start: 3px solid var(--discount-red); }
  .is-broadcast { background: var(--gold-bg); }

  /* --- text safety --------------------------------------------------------- */
  /* Two cheap wins across the whole document: headings stop breaking after one
     or two words, and a long Latin token (an email, a slug, a bot token) can no
     longer push a column wider than the screen. */
  .pdp-title, .slide-title, .empty h2, .notfound h1, .feature h3, .step h3,
  .auth-head h1, .doc h3, .notice-title, .modal-head, .circle-cat-title,
  .footer-head, .wonder-title { text-wrap: balance; }

  .doc p, .doc li, .msg, .order-note, .pdp-td, .pdp-th, .pdp-sub, .cart-title,
  .feature p, .step p, .faq .answer, .hint, .error-text, .bb-req-info,
  .st-line, .convo-subject, .tl-cp-title, .search-live-name, .user-facts {
    overflow-wrap: break-word;
    word-break: normal;
  }
}

@layer utilities {
  .sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }
  /* Visibility by breakpoint. The hide side carries !important; the visible
     side is untouched, so each element keeps its own display (block wrappers,
     flex bars, hover-shown popovers). */
  @media (width < 62rem) {
    .only-desktop { display: none !important; }
  }
  @media (width >= 62rem) {
    .only-mobile { display: none !important; }
  }
  .mono { font-variant-numeric: tabular-nums; letter-spacing: 0.01em; }
  .soft { color: var(--text-soft); }
  .muted { color: var(--text-mute); }
  .bold { font-weight: 700; }
  .font-bold { font-weight: 700; }
  .nowrap { white-space: nowrap; }
  .center { text-align: center; }
  .fs--1 { font-size: 13px; }
  .fs-1 { font-size: var(--fs-xl); font-weight: 700; }
  .gap-1 { gap: 8px; }
  .gap-2 { gap: 16px; }
  .mt-1 { margin-block-start: 6px; }
  .mt-2 { margin-block-start: 12px; }
  .mt-3 { margin-block-start: 16px; }
  .flex-1 { flex: 1 1 0; min-inline-size: 0; }
  .wrap-row { flex-wrap: wrap; }
  .justify-between { justify-content: space-between; }

  /* Every tap target gets the same double-tap-zoom opt-out the buttons have;
     without it the browser waits ~300ms on the qty stepper, the chips and the
     slider dots before firing. */
  a, button, input, select, textarea, summary,
  .chip, .qty button, .stepper button, .slide-dot, .slide-arrow,
  .mb-nav-item, .tab, .sort-tab, .cat-filter-btn, .tl-switch-label, .check {
    touch-action: manipulation;
  }

  /* Windows high contrast / forced colours: the flat fills carry meaning here
     and the OS replaces them, so the boundaries have to come back. */
  @media (forced-colors: active) {
    .btn, .chip, .badge, .card, .tl-card, .panel, .stat, .cert-box, .toast {
      border: 1px solid CanvasText;
    }
    .tl-cart-badge, .mb-badge, .nav-badge {
      forced-color-adjust: none;
      background: Mark;
      color: MarkText;
      border-color: MarkText;
    }
    .slide-dot::before { background: CanvasText; }
    .slide-dot[aria-current="true"]::before { background: Highlight; }
    .festival-banner, .site-footer, .notfound, .wonder-deals-container { forced-color-adjust: auto; }
  }

  /* Hero slide logo tiles (fingerprinted URLs — bundle.py fills d7859d3707) */
  .tile-claude { background-image: url("/assets/hero-claude.d7859d3707.webp"); }
  .tile-grok { background-image: url("/assets/hero-grok.d7859d3707.webp"); }
  .tile-openai { background-image: url("/assets/hero-openai.d7859d3707.webp"); }
  .tile-gemini { background-image: url("/assets/hero-gemini.d7859d3707.webp"); }
  .tile-figma { background-image: url("/assets/hero-figma.d7859d3707.webp"); }
  .tile-canva { background-image: url("/assets/hero-canva.d7859d3707.webp"); }
  .tile-duolingo { background-image: url("/assets/hero-duolingo.d7859d3707.webp"); }
  .tile-coursera { background-image: url("/assets/hero-coursera.d7859d3707.webp"); }
  .tile-codex { background-image: url("/assets/hero-codex.d7859d3707.webp"); }
  .tile-antigravity { background-image: url("/assets/hero-antigravity.d7859d3707.webp"); }
  .tile-opencode { background-image: url("/assets/hero-opencode.d7859d3707.webp"); }
  .tile-claudecode { background-image: url("/assets/hero-claudecode.d7859d3707.webp"); }
}

/* --- SEO pages: category landing, catalogue heading, Persian alias --- */
.catalog-h1 { font-size: var(--fs-xl, 1.25rem); font-weight: 800; color: var(--text-strong); margin: 4px 0 12px; line-height: 1.6; }
.cat-hero { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; padding: 18px 20px; margin-block: 4px 16px;
  background: var(--surface); border: 1px solid var(--line-soft); border-radius: var(--radius-md); }
.cat-hero h1 { font-size: var(--fs-2xl, 1.5rem); font-weight: 800; color: var(--text-strong); line-height: 1.5; margin: 0; }
.cat-intro { color: var(--text-soft); line-height: 2; margin: 0; max-width: 72ch; white-space: pre-line; }
.cat-others { margin-block-start: 24px; display: flex; flex-direction: column; gap: 10px; }
.cat-others h2 { color: var(--text-strong); margin: 0; }
.pdp-alias { color: var(--text-mute); font-size: 13px; margin: -2px 0 4px; }
@media (max-width: 640px) {
  .cat-hero { padding: 14px 14px; }
  .cat-hero h1 { font-size: 1.2rem; }
}
.fc-desc a { color: #fff; text-decoration: underline; text-underline-offset: 3px; }

/* --- Footer support hub: Telegram, ticket, e-mail as cards --- */
.support-hub { display: grid; gap: 16px; }
.hub-head { display: flex; flex-direction: column; gap: 4px; }
.hub-title { margin: 0; font-size: 18px; font-weight: 800; color: #fff; }
.hub-lead { margin: 0; font-size: 13px; color: rgba(255, 255, 255, 0.7); }
.hub-grid { display: grid; grid-template-columns: 1fr; gap: 12px; }
@media (width >= 48rem) { .hub-grid { grid-template-columns: 1.5fr 1fr 1fr; } }
.hub-card {
  position: relative; display: flex; align-items: center; gap: 14px; min-width: 0;
  padding: 16px 18px; border-radius: 16px; color: #fff; text-decoration: none;
  background: rgba(255, 255, 255, 0.06); border: 1px solid rgba(255, 255, 255, 0.12);
  transition: transform var(--dur, .2s) ease, background var(--dur, .2s) ease, border-color var(--dur, .2s) ease, box-shadow var(--dur, .2s) ease;
}
.hub-card:hover { transform: translateY(-2px); background: rgba(255, 255, 255, 0.1); border-color: rgba(255, 255, 255, 0.25); }
.hub-card:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.hub-icon {
  flex-shrink: 0; display: grid; place-items: center; inline-size: 46px; block-size: 46px; border-radius: 14px;
  background: rgba(255, 255, 255, 0.12); color: #fff;
}
.hub-icon svg { inline-size: 22px; block-size: 22px; }
.hub-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1; }
.hub-text b { display: flex; align-items: center; gap: 8px; font-size: 15px; font-weight: 800; }
.hub-text > span { font-size: 12.5px; color: rgba(255, 255, 255, 0.75); overflow-wrap: anywhere; }
.hub-handle { unicode-bidi: isolate; font-weight: 700; color: #fff; }
/* Telegram: the one to press, in Telegram's own blue. */
.hub-tg {
  background: linear-gradient(135deg, #2aabee 0%, #1e88d6 55%, #1769b8 100%);
  border-color: rgba(255, 255, 255, 0.28); box-shadow: 0 10px 30px -12px rgba(42, 171, 238, 0.65);
}
.hub-tg:hover { background: linear-gradient(135deg, #36b5f5 0%, #2393e0 55%, #1b74c4 100%); box-shadow: 0 14px 34px -12px rgba(42, 171, 238, 0.8); }
.hub-tg .hub-icon { background: #fff; color: #229ed9; border-radius: 50%; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18); }
.hub-tg .hub-icon svg { inline-size: 26px; block-size: 26px; transform: translateX(-1px); }
.hub-tg .hub-text > span { color: rgba(255, 255, 255, 0.92); }
.hub-cta {
  flex-shrink: 0; display: inline-flex; align-items: center; gap: 6px; padding: 8px 14px; border-radius: 999px;
  background: #fff; color: #1769b8; font-size: 13px; font-weight: 800; white-space: nowrap;
}
.hub-cta svg { inline-size: 16px; block-size: 16px; transition: transform var(--dur, .2s) ease; }
.hub-card:hover .hub-cta svg { transform: translateX(-3px); }
.hub-live { position: relative; inline-size: 8px; block-size: 8px; border-radius: 50%; background: #4ade80; box-shadow: 0 0 0 3px rgba(74, 222, 128, 0.25); }
.hub-live::after { content: ""; position: absolute; inset: 0; border-radius: 50%; background: #4ade80; animation: hub-ping 1.8s ease-out infinite; }
@keyframes hub-ping { from { transform: scale(1); opacity: .7; } to { transform: scale(2.6); opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .hub-live::after { animation: none; } .hub-card, .hub-card:hover { transform: none; } }
@media (width < 30rem) {
  .hub-card { padding: 14px; gap: 12px; }
  .hub-tg { flex-wrap: wrap; }
  .hub-tg .hub-cta { inline-size: 100%; justify-content: center; }
}
