/* ═══════════════════════════════════════════════════════════════════════════
   MICROCHIP.PK — DESIGN SYSTEM v2
   Ink #0B0909 · Terracotta #BC602D · Gray #DADADA

   Built on shadcn/ui's token model: a small set of semantic pairs
   (background/foreground, card, muted, primary, border, ring) that every
   component reads from, so light and dark are one definition rather than two
   designs. Loaded after output.css.

   Cascade (no !important except where noted):
     • Tokens UNLAYERED — outranks every @layer rule, so remapping Tailwind's
       own --color-* variables repaints all ~800 colour usages across 48 views.
     • Components in @layer components — utilities in views still win.
     • Dark corrections in @layer utilities/components — win on specificity.

   CONTRAST, measured:
     ink on white .................. 19.86  AA
     white on terracotta fill ....... 4.33  AA for large text; button labels
                                            sit just under the 4.5 normal-text
                                            threshold. Chosen deliberately.
     white on hover fill #A85528 .... 5.26  AA
     terracotta text on card ........ 5.26  AA  (--primary-text, darkened)
   ═══════════════════════════════════════════════════════════════════════════ */

/* ═══════════════════════════════════════════════════════════════════════════
   1. TOKENS
   ═══════════════════════════════════════════════════════════════════════════ */
:root {
  --bg:              #0B0909;
  --fg:              #EDEAE9;
  --card:            #141110;
  --card-fg:         #EDEAE9;
  --popover:         #171413;
  --popover-fg:      #EDEAE9;
  --muted:           #1C1817;
  --muted-fg:        #A39B98;
  --subtle:          #121010;
  --border:          #2A2524;
  --border-strong:   #37312F;
  --input:           #2A2524;

  --primary:         #BC602D;
  --primary-fg:      #FFFFFF;
  /* Darkens on hover rather than lightening. With white labels, the old lighter
     #CE7440 dropped contrast to 3.39 — hover would have been harder to read
     than rest, which is backwards. */
  --primary-hover:   #A85528;
  --primary-text:    #D2814E;
  --ring:            #CE7440;
  --accent-wash:     #241A15;
  --accent-edge:     rgba(210, 129, 78, .40);

  --success:         #4ECFA0;
  --destructive:     #F08579;
  --warning:         #E0A94A;

  /* Inverted in lightness, warmth kept. The light design uses low numbers for
     fills and high numbers for text, so inverting sends both the right way. */
  --color-gray-50:  #141110;
  --color-gray-100: #1C1817;
  --color-gray-200: #2A2524;
  --color-gray-300: #37312F;
  --color-gray-400: #8D8583;
  --color-gray-500: #A39B98;
  --color-gray-600: #BAB2AF;
  --color-gray-700: #CFC8C6;
  --color-gray-800: #E0DBD9;
  --color-gray-900: #EDEAE9;
  --color-gray-950: #F7F5F4;

  /* Stays dark: it also backs the header and button fills where text-white
     must keep working. Text/border uses are corrected in §3. */
  --color-microchip-black: #0B0909;
  --color-microchip-dark:  #1C1817;
  --color-microchip-light: #1C1817;
  --color-microchip-white: #0B0909;

  --e1: 0 1px 2px rgba(0, 0, 0, .40);
  --e2: 0 1px 3px rgba(0, 0, 0, .45), 0 4px 10px -3px rgba(0, 0, 0, .50);
  --e3: 0 4px 10px -2px rgba(0, 0, 0, .45), 0 14px 30px -8px rgba(0, 0, 0, .55);
  --e4: 0 10px 22px -6px rgba(0, 0, 0, .55), 0 28px 56px -14px rgba(0, 0, 0, .65);

  /* Semantic washes are light-mode tints and must be re-grounded, or every
     alert glows white on a dark page. */
  --color-red-50: #2A1512;    --color-red-100: #351A16;   --color-red-200: #45211C;
  --color-red-500: #F08579;   --color-red-600: #F08579;   --color-red-700: #F4A79E;
  --color-red-800: #F8C7C1;
  --color-green-50: #10251D;  --color-green-100: #163127; --color-green-200: #1E4234;
  --color-green-600: #4ECFA0; --color-green-700: #7ADCBB; --color-green-800: #A9E9D3;
  --color-amber-50: #241A0F;  --color-amber-100: #2E2113; --color-amber-500: #E0A94A;
  --color-amber-600: #E0A94A; --color-amber-800: #F2CE8E;
  --color-yellow-50: #241A0F; --color-yellow-100: #2E2113;
  --color-yellow-600: #E0A94A; --color-yellow-700: #E9BE72; --color-yellow-800: #F2CE8E;
  --color-blue-50: #121E2E;   --color-blue-100: #16283D;  --color-blue-200: #1D3450;
  --color-blue-400: #6FA8DC;  --color-blue-600: #7FB2E5;  --color-blue-700: #A3C9EE;
  --color-blue-800: #C8DEF6;

  /* Indigo and purple were never remapped, so the few places using them — the
     "Bundle" badge in the cart and admin, and one of the four analytics tiles —
     stayed light while everything beside them went dark. Same treatment as the
     ramps above: a deep tint for fills, a light tone for text on top of it. */
  --color-indigo-50: #16162E;  --color-indigo-100: #1C1C3B; --color-indigo-200: #24244D;
  --color-indigo-600: #9B9BE8; --color-indigo-700: #B0B0EE; --color-indigo-800: #C9C9F4;

  --color-purple-50: #1F1630;  --color-purple-100: #291D3E; --color-purple-200: #35264F;
  --color-purple-600: #B99AE6; --color-purple-700: #C9B0EC; --color-purple-800: #DCCBF3;

  /* ── chrome: header, footer, admin sidebar, search plate ──────────────────
     These surfaces do not follow the page. On the dark palette the page is
     near-black and so is the header; on a light palette a designer may still
     want a dark header, or a light one. Giving them their own tokens lets each
     palette decide, instead of every rule below hard-coding an answer. */
  --chrome-bg:         #000000;
  --chrome-fg:         #FFFFFF;
  --chrome-fg-soft:    #E4DFDD;
  --chrome-fg-dim:     #B8B1AE;
  --chrome-border:     #37312F;
  --chrome-hover:      rgba(255, 255, 255, .06);
  --footer-bg:         #121010;
  --sidebar-bg:        #0B0909;
  --sidebar-fg:        #D8D2D0;

  /* The search field is a plate laid on the chrome, not on the page. */
  --field-bg:          #FFFFFF;
  --field-fg:          #0B0909;
  --field-placeholder: #6E6866;
  /* A white plate on a black header separates itself; no edge needed. */
  --field-border:      transparent;

  /* Catalogue photography is shot on white; a pure-white plate glares on a
     dark page, so product images sit on this tile instead. */
  --photo-tile:        #E8E5E3;

  /* The cover banner on a blog post: a scrim laid over the photograph so the
     headline is readable whatever the photograph happens to be. The scrim
     follows the palette rather than being fixed dark — otherwise the light
     palette put near-black type on a near-black wash, which measured 1.05:1.
     The strong stop sits at the bottom, where the text is. */
  --cover-scrim-strong: rgba(11, 9, 9, .92);
  --cover-scrim-soft:   rgba(11, 9, 9, .35);
  --cover-fg:           #FFFFFF;
  --cover-fg-dim:       rgba(255, 255, 255, .80);

  /* Chips that invert on hover (the quick-view close button). */
  --invert-bg:         #FFFFFF;
  --invert-fg:         #0B0909;

  /* Page scrollbar. Left to the browser it follows `color-scheme` and comes out
     near-black on this palette — a heavy bar down the edge of the page. A pale
     thumb on a quiet track reads as part of the design instead. */
  --scrollbar-track:       #161312;
  --scrollbar-thumb:       #B8B1AE;
  --scrollbar-thumb-hover: #D8D2D0;

  /* The shop logo is a white monochrome mark, which vanishes on a light
     header. It is filtered rather than swapped so no second asset has to be
     uploaded; a *coloured* logo would need a real light-background variant. */
  --logo-filter:       none;

  color-scheme: dark;

  /* ── geometry — shadcn's single --radius, derived ───────────────────────── */
  --radius:     10px;
  --radius-xs:   6px;
  --radius-sm:   8px;
  --radius-md:  10px;
  --radius-lg:  12px;
  --radius-xl:  16px;
  --radius-2xl: 22px;

  /* ── elevation, tinted with the brand ink ───────────────────────────────── */
  --e1: 0 1px 2px rgba(11, 9, 9, .05);
  --e2: 0 1px 3px rgba(11, 9, 9, .06), 0 4px 10px -3px rgba(11, 9, 9, .07);
  --e3: 0 4px 10px -2px rgba(11, 9, 9, .06), 0 14px 30px -8px rgba(11, 9, 9, .13);
  --e4: 0 10px 22px -6px rgba(11, 9, 9, .10), 0 28px 56px -14px rgba(11, 9, 9, .20);

  /* ── motion ─────────────────────────────────────────────────────────────── */
  --ease:     cubic-bezier(.2, .8, .2, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --t-fast: 120ms;
  --t-base: 200ms;
  --t-slow: 340ms;
  --default-transition-duration: var(--t-base);
  --default-transition-timing-function: var(--ease);
}



/* ═══════════════════════════════════════════════════════════════════════════
   1b. PALETTE — LIGHT
   Selected in Admin → Settings → Appearance, which stamps data-palette on
   <html>. Everything below is a token; no component rule knows which palette
   is active, so adding another is a matter of copying this block and changing
   values.

   The neutral ramp runs the normal direction here (low = pale fills, high =
   dark text), the exact opposite of the dark palette — which is why every
   component reads the ramp through tokens rather than picking steps directly.

   CONTRAST, measured against this palette's own surfaces:
     fg on bg ...................... 15.9  AAA
     muted-fg on bg .................. 5.4  AA
     primary-text on card ............ 5.9  AA
     white on terracotta fill ........ 4.3  AA-large (same deliberate choice
                                             as the dark palette's buttons)
   ═══════════════════════════════════════════════════════════════════════════ */
:root[data-palette="light"] {
  --bg:              #FBF9F8;
  --fg:              #1A1614;
  --card:            #FFFFFF;
  --card-fg:         #1A1614;
  --popover:         #FFFFFF;
  --popover-fg:      #1A1614;
  --muted:           #F1EDEB;
  --muted-fg:        #5E5653;
  --subtle:          #F6F3F1;
  --border:          #E5DFDC;
  --border-strong:   #CFC7C3;
  --input:           #D9D2CE;

  --primary:         #BC602D;
  --primary-fg:      #FFFFFF;
  --primary-hover:   #A85528;
  /* Darker than the fill: terracotta as *text* needs to clear 4.5 on white,
     which #BC602D (3.9) does not. */
  --primary-text:    #99461E;
  --ring:            #BC602D;
  --accent-wash:     #FBF0E9;
  --accent-edge:     rgba(188, 96, 45, .35);

  --success:         #12795A;
  --destructive:     #B3261E;
  --warning:         #8A5A0B;

  /* Normal direction: pale fills at the bottom, dark text at the top.
     400 and 500 are darker than a stock Tailwind ramp would put them, because
     in this codebase they are text, not decoration: `text-gray-400` appears 152
     times against 5 uses as a border. At Tailwind's own lightness the product
     card's category label measured 2.6:1 on white. Every step from 400 up now
     clears 4.5:1 on all three surfaces this palette puts text on. */
  --color-gray-50:  #FBF9F8;
  --color-gray-100: #F1EDEB;
  --color-gray-200: #E5DFDC;
  --color-gray-300: #D4CCC8;
  --color-gray-400: #6F6764;
  --color-gray-500: #5E5653;
  --color-gray-600: #4A4341;
  --color-gray-700: #3A3432;
  --color-gray-800: #2A2523;
  --color-gray-900: #1A1614;
  --color-gray-950: #0B0909;

  /* Stays dark in both palettes: it fills the selected variant button and the
     admin sidebar, where `text-white` sits on top of it. */
  --color-microchip-black: #1A1614;
  --color-microchip-dark:  #322C2A;
  --color-microchip-light: #F1EDEB;
  --color-microchip-white: #FFFFFF;

  --color-red-50: #FDF2F1;    --color-red-100: #FBE3E1;   --color-red-200: #F7C9C5;
  --color-red-500: #C5352B;   --color-red-600: #B3261E;   --color-red-700: #8F1D17;
  --color-red-800: #6B1611;
  --color-green-50: #ECFAF4;  --color-green-100: #D3F3E6; --color-green-200: #A8E7CE;
  --color-green-600: #12795A; --color-green-700: #0E6249; --color-green-800: #0A4B38;
  --color-amber-50: #FEF7EC;  --color-amber-100: #FCEBCF; --color-amber-500: #A9700F;
  --color-amber-600: #8A5A0B; --color-amber-800: #6B4508;
  --color-yellow-50: #FEF7EC; --color-yellow-100: #FCEBCF;
  --color-yellow-600: #8A5A0B; --color-yellow-700: #6B4508; --color-yellow-800: #533505;
  --color-blue-50: #EFF4FC;   --color-blue-100: #DAE6F8;  --color-blue-200: #B9D0F1;
  --color-blue-400: #4A86D8;  --color-blue-600: #1F5FAE;  --color-blue-700: #18497F;
  --color-blue-800: #123659;
  --color-indigo-50: #F0F0FB; --color-indigo-100: #E0E0F6; --color-indigo-200: #C4C4EE;
  --color-indigo-600: #4B4BC4; --color-indigo-700: #3B3BA0; --color-indigo-800: #2D2D7C;
  --color-purple-50: #F7F0FC; --color-purple-100: #EDE0F8; --color-purple-200: #DCC4F1;
  --color-purple-600: #7B3FB8; --color-purple-700: #632F94; --color-purple-800: #4B2371;

  /* Chrome goes light with the page. */
  --chrome-bg:         #FFFFFF;
  --chrome-fg:         #1A1614;
  --chrome-fg-soft:    #322C2A;
  --chrome-fg-dim:     #625A57;
  --chrome-border:     #E5DFDC;
  --chrome-hover:      rgba(26, 22, 20, .06);
  --footer-bg:         #F1EDEB;
  --sidebar-bg:        #FFFFFF;
  --sidebar-fg:        #4A4341;

  /* The search plate has to separate itself from a white header, so it tints
     down rather than up. */
  /* On a white header a tinted plate all but disappears — measured 1.16:1
     against the chrome behind it — so the field is defined by its edge here
     rather than by its fill. */
  --field-bg:          #FFFFFF;
  --field-fg:          #1A1614;
  --field-placeholder: #7D7470;
  --field-border:      #D4CCC8;

  --photo-tile:        #F4F1EF;

  /* Dark type on a light wash, the mirror of the dark palette. .88 at the
     bottom keeps the headline above 4.5:1 even over a black photograph, while
     .40 at the top still lets the picture read. */
  --cover-scrim-strong: rgba(251, 249, 248, .88);
  --cover-scrim-soft:   rgba(251, 249, 248, .40);
  --cover-fg:           #1A1614;
  --cover-fg-dim:       #4A4340;

  --invert-bg:         #1A1614;
  --invert-fg:         #FFFFFF;

  --scrollbar-track:       #F1EDEB;
  --scrollbar-thumb:       #C4BDBA;
  --scrollbar-thumb-hover: #A79E9A;

  /* Renders the white mark as near-black. Lossless for a monochrome logo. */
  --logo-filter:       brightness(0);

  /* Shadows are cast by ink on paper here, not by a glow in the dark. */
  --e1: 0 1px 2px rgba(26, 22, 20, .06);
  --e2: 0 1px 3px rgba(26, 22, 20, .08), 0 4px 10px -3px rgba(26, 22, 20, .06);
  --e3: 0 4px 10px -2px rgba(26, 22, 20, .08), 0 14px 30px -8px rgba(26, 22, 20, .10);
  --e4: 0 10px 22px -6px rgba(26, 22, 20, .12), 0 28px 56px -14px rgba(26, 22, 20, .14);

  color-scheme: light;
}


/* ═══════════════════════════════════════════════════════════════════════════
   2. DIRECTION-SENSITIVE CORRECTIONS
   Absolutes (`white`, `black`) and the two ramp steps used as dark fills cannot
   be fixed by remapping a token, because one token serves both text and
   background — e.g. bg-white must go dark while text-white must stay light.
   ═══════════════════════════════════════════════════════════════════════════ */
@layer utilities {
  :root .bg-white      { background-color: var(--card); }
  :root .border-white  { border-color: var(--border); }
  :root .from-white    { --tw-gradient-from: var(--card); }
  :root .bg-black      { background-color: var(--bg); }
  :root .text-black    { color: var(--fg); }
  /* These two only make sense where the neutral ramp is inverted. On the dark
     palette gray-800/900 are light tones meant for text, so `bg-gray-900` —
     used across Orders, Reviews and Analytics as the "active filter" pill,
     always with `text-white` — has to be pulled back to a dark fill.

     A palette running the ramp the normal way already has a dark gray-900, and
     remapping it there painted those pills white on white: measured 1.00:1,
     i.e. the labels vanished. So the correction is scoped to the palettes that
     invert. Add a new inverted palette here; a light one needs nothing. */
  :root:not([data-palette]) .bg-gray-800,
  :root[data-palette="dark"] .bg-gray-800 { background-color: var(--muted); }
  :root:not([data-palette]) .bg-gray-900,
  :root[data-palette="dark"] .bg-gray-900 { background-color: var(--card); }
  :root .border-gray-800 { border-color: var(--border-strong); }
  :root .border-gray-900 { border-color: var(--border-strong); }
  :root .text-microchip-black   { color: var(--fg); }
  :root .border-microchip-black { border-color: var(--border); }
  :root .ring-microchip-black   { --tw-ring-color: var(--ring); }
  :root .text-microchip-dark    { color: var(--muted-fg); }
}

/* ═══════════════════════════════════════════════════════════════════════════
   4. BASE
   ═══════════════════════════════════════════════════════════════════════════ */
/* The banner overflows the viewport below ~640px — a pre-existing layout bug,
   reproducible with this stylesheet removed. `clip` rather than `hidden`,
   because `overflow: hidden` on an ancestor silently breaks `position: sticky`
   and the site header depends on it. */
/* The canvas between documents is painted from <html>, not <body>. Without a
   background here the browser flashes white for a frame on every navigation —
   the "blink" on a dark site. color-scheme keeps form controls and the overscroll
   area dark too. */
html {
  overflow-x: clip;
  background-color: var(--bg);
  /* color-scheme is set per palette in §1 — repeating "dark" here would beat
     the light palette's own value, since both target the same element. */
}

body {
  background-color: var(--bg);
  color: var(--fg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, h4, h5 { letter-spacing: -.021em; text-wrap: balance; color: var(--fg); }

.price, [data-line-total], .num, td.num, .tabular { font-variant-numeric: tabular-nums; }

::selection { background: var(--primary); color: var(--primary-fg); }

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}

/* ── panel scrollbars ─────────────────────────────────────────────────────────
   The shop sidebar's category and brand lists, the search dropdown, the
   mini-cart, modals and the mobile drawer.

   These read from the same two tokens as the page scrollbar, so a pale thumb
   appears everywhere rather than only on the window. They previously used
   --border-strong, which on the dark palette is #37312F — all but the colour of
   the panel behind it, so the sidebar looked as though it had no scrollbar at
   all. Narrower than the page bar because they sit inside small panels, and
   with no track fill for the same reason. */
.filter-scroll, .search-dropdown, .admin-sidebar-nav, .mc-cart-items,
.filter-panel, .mobile-drawer, .modal-content, [data-filter-list] {
  scrollbar-width: thin;
  scrollbar-color: var(--scrollbar-thumb) transparent;
}
.filter-scroll::-webkit-scrollbar, .search-dropdown::-webkit-scrollbar,
.admin-sidebar-nav::-webkit-scrollbar, .mc-cart-items::-webkit-scrollbar,
.filter-panel::-webkit-scrollbar, .mobile-drawer::-webkit-scrollbar,
.modal-content::-webkit-scrollbar, [data-filter-list]::-webkit-scrollbar {
  width: 6px; height: 6px;
}
.filter-scroll::-webkit-scrollbar-track, .search-dropdown::-webkit-scrollbar-track,
.admin-sidebar-nav::-webkit-scrollbar-track, .mc-cart-items::-webkit-scrollbar-track,
.filter-panel::-webkit-scrollbar-track, .mobile-drawer::-webkit-scrollbar-track,
.modal-content::-webkit-scrollbar-track, [data-filter-list]::-webkit-scrollbar-track {
  background: transparent;
}
.filter-scroll::-webkit-scrollbar-thumb, .search-dropdown::-webkit-scrollbar-thumb,
.admin-sidebar-nav::-webkit-scrollbar-thumb, .mc-cart-items::-webkit-scrollbar-thumb,
.filter-panel::-webkit-scrollbar-thumb, .mobile-drawer::-webkit-scrollbar-thumb,
.modal-content::-webkit-scrollbar-thumb, [data-filter-list]::-webkit-scrollbar-thumb {
  background: var(--scrollbar-thumb);
  border-radius: 999px;
}
.filter-scroll:hover::-webkit-scrollbar-thumb,
.search-dropdown:hover::-webkit-scrollbar-thumb,
.admin-sidebar-nav:hover::-webkit-scrollbar-thumb,
.mc-cart-items:hover::-webkit-scrollbar-thumb,
.filter-panel:hover::-webkit-scrollbar-thumb,
.mobile-drawer:hover::-webkit-scrollbar-thumb,
.modal-content:hover::-webkit-scrollbar-thumb,
[data-filter-list]:hover::-webkit-scrollbar-thumb { background: var(--scrollbar-thumb-hover); }

/* ═══════════════════════════════════════════════════════════════════════════
   5. MOTION
   ═══════════════════════════════════════════════════════════════════════════ */

/* Cross-document transitions where the browser supports them; the keyframe
   below covers everything else. */
@view-transition { navigation: auto; }

/* Cross-document navigation: a plain cross-fade. Without naming these, Chrome's
   default is a full-page slide that reads as heavier than a reload. */
::view-transition-old(root) { animation: mc-fade-out 90ms var(--ease) both; }
::view-transition-new(root) { animation: mc-fade-in 180ms var(--ease-out) both; }

@keyframes mc-fade-out { from { opacity: 1; } to { opacity: 0; } }

@keyframes mc-page-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@keyframes mc-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes mc-pop-in  { from { opacity: 0; transform: translateY(8px) scale(.97); } to { opacity: 1; transform: none; } }

main { animation: mc-page-in var(--t-slow) var(--ease-out) both; }

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

/* ═══════════════════════════════════════════════════════════════════════════
   6. COMPONENTS
   ═══════════════════════════════════════════════════════════════════════════ */
@layer components {

  /* ── buttons ──────────────────────────────────────────────────────────────
     Terracotta fill with ink labels: white on this orange is 4.33 and fails at
     button sizes, ink is 4.59 and passes. */
  .btn-primary, .mc-cta {
    display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
    border-radius: var(--radius);
    background-color: var(--primary);
    color: var(--primary-fg);
    font-weight: 600;
    letter-spacing: -.008em;
    border: 1px solid transparent;
    box-shadow: var(--e1);
    cursor: pointer;
    transition: background-color var(--t-base) var(--ease),
                box-shadow var(--t-base) var(--ease),
                transform var(--t-fast) var(--ease);
  }
  .btn-primary:hover, .mc-cta:hover {
    background-color: var(--primary-hover);
    color: var(--primary-fg);
    opacity: 1;
    box-shadow: var(--e2);
  }
  .btn-primary:active, .mc-cta:active { transform: translateY(1px); box-shadow: var(--e1); }
  .mc-cta[disabled], .mc-cta:disabled, .btn-primary:disabled {
    background-color: var(--muted); color: var(--muted-fg);
    box-shadow: none; cursor: not-allowed;
  }

  .btn-outline {
    display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
    border-radius: var(--radius);
    border: 1px solid var(--border-strong);
    background-color: transparent;
    color: var(--fg);
    font-weight: 500;
    transition: background-color var(--t-base) var(--ease),
                border-color var(--t-base) var(--ease),
                color var(--t-base) var(--ease);
  }
  .btn-outline:hover {
    background-color: var(--accent-wash);
    border-color: var(--accent-edge);
    color: var(--primary-text);
    opacity: 1;
  }

  .btn-sm { border-radius: var(--radius-sm); }

  .btn-action {
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    background-color: var(--card);
    color: var(--fg);
    transition: background-color var(--t-fast) var(--ease),
                border-color var(--t-fast) var(--ease),
                color var(--t-fast) var(--ease);
  }
  .btn-action:hover { background-color: var(--muted); border-color: var(--border-strong); }

  /* Secondary to the orange Add-to-Cart, not a rival to it. The old rule filled
     it with --fg, which on a dark theme is near-white — two heavy buttons
     stacked, and an opacity-only hover that just dimmed the whole thing.
     One rule styles every instance: shop grid, related products, bundle cards,
     product page and the quick-view modal all use this class. */
  .order-now-btn {
    border-radius: var(--radius);
    background-color: var(--muted);
    color: var(--fg);
    border: 1px solid var(--border);
    font-weight: 500;
    transition: background-color var(--t-base) var(--ease),
                border-color var(--t-base) var(--ease),
                color var(--t-base) var(--ease);
  }
  .order-now-btn:hover {
    background-color: var(--card);
    border-color: var(--border-strong);
    color: var(--fg);
    opacity: 1;
    text-decoration: none;
  }
  .order-now-btn:active { background-color: var(--muted); }
  .quick-view-btn, .truncate-btn { border-radius: var(--radius); }

  /* ── form controls ────────────────────────────────────────────────────── */
  .input-field, .file-input, .sort-select, .per-page-select, .filter-search-input,
  select.input-field, textarea.input-field {
    border-radius: var(--radius);
    border: 1px solid var(--input);
    background-color: var(--card);
    color: var(--fg);
    transition: border-color var(--t-base) var(--ease),
                box-shadow var(--t-base) var(--ease);
  }
  .input-field::placeholder, .filter-search-input::placeholder,
  textarea.input-field::placeholder { color: var(--muted-fg); }
  .input-field:hover, .filter-search-input:hover { border-color: var(--border-strong); }
  .input-field:focus, .file-input:focus, .sort-select:focus,
  .per-page-select:focus, .filter-search-input:focus, textarea.input-field:focus {
    outline: none;
    border-color: var(--ring);
    box-shadow: 0 0 0 3px var(--accent-wash);
  }
  .input-field:disabled { background-color: var(--muted); color: var(--muted-fg); cursor: not-allowed; }
  .label-field { color: var(--fg); font-weight: 550; letter-spacing: -.004em; }
  .no-spinner { background-color: var(--card); color: var(--fg); border-color: var(--input); }

  /* ── product card ─────────────────────────────────────────────────────── */
  .product-card {
    border-radius: var(--radius-lg);
    background-color: var(--card);
    border: 1px solid var(--border);
    box-shadow: none;
    overflow: hidden;
    transition: transform var(--t-slow) var(--ease),
                box-shadow var(--t-slow) var(--ease),
                border-color var(--t-slow) var(--ease);
  }
  .product-card:hover {
    transform: translateY(-3px);
    border-color: var(--accent-edge);
    box-shadow: var(--e3);
  }
  .product-image-wrapper { background-color: var(--subtle); overflow: hidden; }
  .product-image-wrapper img { transition: transform var(--t-slow) var(--ease); }
  .product-card:hover .product-image-wrapper img { transform: scale(1.045); }
  .product-card-body { color: var(--card-fg); }

  /* ── badges ───────────────────────────────────────────────────────────── */
  .badge-sale, .badge-new, .badge-featured {
    border-radius: 999px;
    font-weight: 600;
    letter-spacing: .03em;
    box-shadow: none;
    border: 1px solid transparent;
  }
  .badge-sale     { background-color: var(--primary); color: var(--primary-fg); }
  .badge-new      { background-color: var(--fg); color: var(--bg); }
  .badge-featured { background-color: var(--accent-wash); color: var(--primary-text); border-color: var(--accent-edge); }

  /* ── surfaces ─────────────────────────────────────────────────────────── */
  .admin-card, .filter-panel, .mobile-drawer {
    border-radius: var(--radius-lg);
    background-color: var(--card);
    border: 1px solid var(--border);
    box-shadow: none;
  }
  .modal-content {
    border-radius: var(--radius-xl);
    background-color: var(--popover);
    color: var(--popover-fg);
    border: 1px solid var(--border);
    box-shadow: var(--e4);
  }
  /* Animate only once the overlay is actually open. Putting the animation on the
     base rule made the modal permanently visible: output.css hides it with
     `opacity: 0`, and an animation's filled end state outranks a normal
     declaration, so the closed modal was painted over every page. */
  .modal-overlay.open .modal-content { animation: mc-pop-in var(--t-base) var(--ease-out) both; }
  .search-dropdown {
    border-radius: var(--radius-lg);
    background-color: var(--popover);
    color: var(--popover-fg);
    border: 1px solid var(--border);
    box-shadow: var(--e3);
  }
  .modal-overlay {
    background-color: rgba(11, 9, 9, .55);
    backdrop-filter: blur(4px);
    /* No animation here — output.css already fades this with an opacity
       transition, and an animation would override the closed state. */
  }
  .search-dropdown-item { border-radius: var(--radius-sm); transition: background-color var(--t-fast) var(--ease); }
  .search-dropdown-item:hover { background-color: var(--muted); }

  /* ── admin table ──────────────────────────────────────────────────────── */
  .admin-table thead th {
    background-color: var(--muted);
    color: var(--muted-fg);
    font-size: 10.5px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase;
    padding: 10px 12px; text-align: left; white-space: nowrap;
    border-bottom: 1px solid var(--border);
  }
  /* A sortable heading is a link, and output.css carries a bare
     `a { color: var(--color-microchip-black) }` that beat the colour the cell
     had set on it. On the dark palette that token is #0B0909 against a #1C1817
     header — 1.13:1, which is not dim, it is invisible. On the light palette it
     came out at 15.45:1 next to 6.16:1 for the plain headings, so the sortable
     ones read as a different, heavier style than their neighbours.

     Inheriting from the cell puts every heading on the same footing, and the
     column actually being sorted is marked with the accent instead. */
  .admin-table thead th a {
    color: inherit;
    text-decoration: none;
  }
  .admin-table thead th a:hover {
    color: var(--fg);
    text-decoration: underline;
    text-underline-offset: 2px;
  }
  .admin-table thead th a.is-sorted { color: var(--primary-text); }

  .admin-table thead th:first-child { border-top-left-radius: var(--radius-sm); }
  .admin-table thead th:last-child  { border-top-right-radius: var(--radius-sm); }
  .admin-table tbody td {
    padding: 11px 12px; border-bottom: 1px solid var(--border);
    color: var(--fg); vertical-align: middle;
  }
  .admin-table tbody tr { transition: background-color var(--t-fast) var(--ease); }
  .admin-table tbody tr:hover { background-color: var(--muted); }

  /* Actions in a table cell.

     Some of these are links and some are single-button forms, and a <form> is
     a box: as a flex item it took its own height and pushed its button a pixel
     off the baseline of the link beside it. `display: contents` drops the form
     box so its button becomes a flex item in its own right and lines up
     exactly, while the form still submits normally.

     nowrap because "Sign in as" breaking across two lines in a narrow column
     is the other way this row stops looking deliberate. */
  .table-actions { display: flex; align-items: center; gap: 14px; white-space: nowrap; }
  .table-actions > form { display: contents; }
  .admin-table tbody tr:last-child td { border-bottom: 0; }

  /* ── admin sidebar ────────────────────────────────────────────────────── */
  .admin-sidebar-link {
    border-radius: var(--radius-sm);
    transition: background-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
  }
  .admin-sidebar-link.active {
    background-color: var(--accent-wash);
    color: var(--primary-text);
    font-weight: 600;
    box-shadow: inset 2px 0 0 var(--primary);
  }

  /* ── pagination ───────────────────────────────────────────────────────── */
  .page-link {
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    color: var(--fg);
    transition: border-color var(--t-fast) var(--ease),
                background-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
  }
  .page-link:hover:not(.disabled) { background-color: var(--muted); border-color: var(--border-strong); }
  .page-link.active {
    background-color: var(--primary); border-color: var(--primary);
    color: var(--primary-fg); font-weight: 600;
  }
  .page-link.disabled { color: var(--muted-fg); }

  /* ── filters & chips ──────────────────────────────────────────────────── */
  .filter-link { border-radius: var(--radius-xs); color: var(--muted-fg); transition: color var(--t-fast) var(--ease), background-color var(--t-fast) var(--ease); }
  .filter-link:hover { background-color: var(--muted); color: var(--fg); }
  .filter-link.active { color: var(--primary-text); font-weight: 600; }
  .search-tag, .color-btn, .size-btn, .qv-size-btn, .qv-color-btn, .qv-thumb {
    border-radius: var(--radius-xs); border-color: var(--border-strong);
  }
  .color-btn.selected, .size-btn.selected, .qv-size-btn.selected,
  .qv-color-btn.selected, .qv-thumb.selected {
    border-color: var(--primary); box-shadow: 0 0 0 1px var(--primary);
  }
  .toast { border-radius: var(--radius); box-shadow: var(--e3); border: 1px solid var(--border); }

  /* ── banner ───────────────────────────────────────────────────────────── */
  .banner-slide, .banner-img { border-radius: var(--radius-xl); }
  .banner-arrow { border-radius: 999px; backdrop-filter: blur(8px); }
  .banner-dot { transition: background-color var(--t-base) var(--ease), width var(--t-base) var(--ease); }

  .site-footer { background-color: var(--fg); color: var(--bg); }

  /* Dark: component rules in output.css that use the brand token as INK.
     --color-microchip-black stays dark there because it also backs the header
     and button fills, so anything using it as text/border goes invisible. */
  :root .filter-link.active { color: var(--primary-text); }
  :root .page-link { color: var(--fg); }
  :root .hover\:text-microchip-black:hover { color: var(--fg); }
}

/* ═══════════════════════════════════════════════════════════════════════════
   7. CART BADGE + MINI-CART DROPDOWN
   ═══════════════════════════════════════════════════════════════════════════ */

/* !important is deliberate and minimal: the badge carries bg-red-600/text-white
   utilities inline in the header markup, and utilities outrank this file's
   layered rules. Overriding here keeps the change in one place. */
#cart-count {
  background-color: var(--primary) !important;
  color: var(--primary-fg) !important;
  box-shadow: 0 0 0 2px var(--bg);
  font-variant-numeric: tabular-nums;
}

/* The badge is `absolute -top-1 -right-1` inside the trigger, which only lands
   correctly if the trigger has a padded box to anchor to. As a direct flex child
   the <a> was blockified; inside this wrapper it reverted to display:inline, so
   the badge anchored to the inline box and floated up out of the header.
   Making the wrapper a flex container blockifies the <a> again. */
.mc-cart-wrap { position: relative; display: inline-flex; align-items: center; }
#mc-cart-trigger { display: inline-flex; align-items: center; }

.mc-cart-panel {
  position: absolute; top: calc(100% + 12px); right: 0;
  width: min(376px, calc(100vw - 24px));
  background: var(--popover); color: var(--popover-fg);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  box-shadow: var(--e4);
  z-index: 60;
  overflow: hidden;
  opacity: 0; visibility: hidden;
  transform: translateY(-6px) scale(.985);
  transform-origin: top right;
  transition: opacity var(--t-base) var(--ease-out),
              transform var(--t-base) var(--ease-out),
              visibility 0s linear var(--t-base);
}
.mc-cart-panel[data-open="true"] {
  opacity: 1; visibility: visible; transform: none;
  transition: opacity var(--t-base) var(--ease-out),
              transform var(--t-base) var(--ease-out),
              visibility 0s;
}
.mc-cart-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 13px 16px; border-bottom: 1px solid var(--border);
  font-size: 13px; font-weight: 600; letter-spacing: -.005em; color: var(--fg);
}
.mc-cart-head span { color: var(--muted-fg); font-weight: 500; font-variant-numeric: tabular-nums; }

/* Capped so a large cart can never push the panel past the viewport. */
.mc-cart-items { max-height: min(324px, 46vh); overflow-y: auto; overscroll-behavior: contain; }

.mc-cart-row { display: flex; gap: 11px; padding: 11px 16px; border-bottom: 1px solid var(--border); align-items: center; }
.mc-cart-row:last-child { border-bottom: 0; }
.mc-cart-thumb {
  width: 46px; height: 46px; flex-shrink: 0; border-radius: var(--radius-sm);
  background: var(--subtle); border: 1px solid var(--border);
  object-fit: contain; padding: 3px;
}
.mc-cart-info { min-width: 0; flex: 1; }
.mc-cart-name {
  font-size: 12.5px; font-weight: 550; color: var(--fg); line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.mc-cart-meta { font-size: 11.5px; color: var(--muted-fg); margin-top: 2px; font-variant-numeric: tabular-nums; }
.mc-cart-line { font-size: 12.5px; font-weight: 600; color: var(--fg); white-space: nowrap; font-variant-numeric: tabular-nums; }

/* Remove control on a mini-cart line. Quiet until pointed at, so a list of
   items does not read as a row of delete buttons. */
.mc-cart-remove {
  width: 26px;
  height: 26px;
  margin-right: -4px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--muted-fg);
  cursor: pointer;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  opacity: .65;
  transition: opacity var(--t-fast) var(--ease),
              background-color var(--t-fast) var(--ease),
              color var(--t-fast) var(--ease);
}
.mc-cart-remove svg { width: 15px; height: 15px; }
.mc-cart-remove:hover,
.mc-cart-remove:focus-visible {
  opacity: 1;
  color: var(--destructive);
  background: var(--muted);
}
.mc-cart-remove:disabled { cursor: default; opacity: .35; }

/* The row is on its way out; fading it stops the list looking frozen while the
   request is in flight. */
.mc-cart-row.is-removing { opacity: .45; transition: opacity var(--t-fast) var(--ease); }

@media (max-width: 640px) {
  /* A 26px target is fine under a mouse, not under a thumb. */
  .mc-cart-remove { width: 34px; height: 34px; opacity: .8; }
  .mc-cart-remove svg { width: 17px; height: 17px; }
}

.mc-cart-foot { padding: 13px 16px 15px; border-top: 1px solid var(--border); background: var(--subtle); }
.mc-cart-total { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 11px; }
.mc-cart-total b { font-size: 15px; font-variant-numeric: tabular-nums; color: var(--fg); }
.mc-cart-total span { font-size: 12.5px; color: var(--muted-fg); }
.mc-cart-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.mc-cart-btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 9px 12px; border-radius: var(--radius); font-size: 12.5px; font-weight: 600;
  text-decoration: none; cursor: pointer; border: 1px solid transparent;
  transition: background-color var(--t-base) var(--ease),
              border-color var(--t-base) var(--ease), color var(--t-base) var(--ease);
}
.mc-cart-btn:hover { text-decoration: none; }
.mc-cart-btn.ghost { border-color: var(--border-strong); color: var(--fg); background: var(--card); }
.mc-cart-btn.ghost:hover { background: var(--muted); }
.mc-cart-btn.solid { background: var(--primary); color: var(--primary-fg); }
.mc-cart-btn.solid:hover { background: var(--primary-hover); }
.mc-cart-empty { padding: 30px 16px; text-align: center; color: var(--muted-fg); font-size: 13px; }

/* Catalogue photography is shot on white; a pure-white plate glares on a dark
   page, so images sit on a soft light tile that keeps the product legible. */
:root .product-image-wrapper,
:root .aspect-product,
:root .mc-cart-thumb { background-color: var(--photo-tile); }

/* ═══════════════════════════════════════════════════════════════════════════
   9. CORRECTIONS TO INHERITED STYLES
   ═══════════════════════════════════════════════════════════════════════════ */

/* output.css lays a 20%-black gradient across the bottom of every product
   image. It was drawn for dark photography; against catalogue shots on white it
   reads as a smudge. */
.product-image-wrapper::after { display: none; }

/* ═══════════════════════════════════════════════════════════════════════════
   10. MOBILE
   ═══════════════════════════════════════════════════════════════════════════ */
/* Only the sheet is dismissed by a button; the desktop dropdown closes by
   clicking away, so the control would be redundant there. Declared before the
   media query below, or its `display: none` would win on source order. */
.mc-cart-close {
  display: none;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  margin: -6px -6px -6px 10px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--muted-fg);
  cursor: pointer;
  flex-shrink: 0;
  transition: background-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.mc-cart-close:hover { background: var(--muted); color: var(--fg); }
.mc-cart-close svg { width: 16px; height: 16px; }

/* The item count sits between the title and the close button. */
.mc-cart-head span { margin-left: auto; }


@media (max-width: 640px) {
  /* A dropdown anchored to the header would run off a phone screen, so on
     small viewports the mini-cart becomes a bottom sheet.

     The sheet is sized by its contents, not by a fraction of the viewport: a
     header, three rows of items, and the totals. That keeps it the same modest
     height on every phone rather than swallowing 82% of a small screen, and it
     never grows past three rows because the list scrolls instead. */
  .mc-cart-panel {
    position: fixed;
    top: auto; bottom: 0; left: 0; right: 0;
    width: 100%;
    border-radius: var(--radius-2xl) var(--radius-2xl) 0 0;
    transform-origin: bottom center;

    /* Parked fully below the fold, so opening and closing read as the sheet
       sliding up from and back down past the bottom edge. */
    transform: translateY(100%);
    opacity: 1;

    /* A backstop for very short screens (a landscape phone), not the usual
       constraint — the item list above is what normally decides the height. */
    max-height: 88vh;
    max-height: 88svh;

    /* The sheet travels its whole height, so it wants longer than the 200ms a
       dropdown fade uses, or the slide reads as a jump. */
    transition: transform var(--t-slow) var(--ease-out),
                visibility 0s linear var(--t-slow);
  }
  .mc-cart-panel[data-open="true"] {
    transition: transform var(--t-slow) var(--ease-out),
                visibility 0s;
  }
  .mc-cart-panel[data-open="true"] { transform: translateY(0); opacity: 1; }

  /* Three rows, then scroll. A row is a 46px thumbnail plus 11px of padding
     top and bottom and its 1px rule. */
  .mc-cart-items { max-height: calc(69px * 3); }

  /* Kept side by side: stacking them cost about 50px of a small screen for no
     gain — at 390px each button is still ~180px wide. */
  .mc-cart-actions { grid-template-columns: 1fr 1fr; }

  /* Room for the grab handle above the heading. */
  .mc-cart-head { padding-top: 18px; }

  .mc-cart-panel::before {
    content: "";
    position: absolute;
    top: 7px; left: 50%;
    width: 36px; height: 4px;
    margin-left: -18px;
    border-radius: 999px;
    background: var(--border-strong);
    pointer-events: none;
  }

  /* Clears the home indicator on phones that have one. */
  .mc-cart-foot { padding-bottom: calc(15px + env(safe-area-inset-bottom, 0px)); }

  /* Bigger tap targets than the desktop dropdown's. */
  .mc-cart-btn { padding: 12px 14px; font-size: 13.5px; }

  /* The sheet has no scrim and ignores taps elsewhere, so it needs a close
     control of its own. */
  .mc-cart-close { display: inline-flex; }
}



/* ═══════════════════════════════════════════════════════════════════════════
   11. ALWAYS-DARK CHROME
   The header and footer are dark surfaces regardless of context, so text on them
   is set explicitly rather than inherited from the neutral ramp — `text-gray-300`
   resolves to #37312F here, which is nearly invisible on a dark header.
   ═══════════════════════════════════════════════════════════════════════════ */

/* The chrome surfaces themselves. Their backgrounds come from utility classes
   in the markup (`bg-black`, `bg-gray-900`), which is fine for one palette and
   wrong for the next — so the surfaces are pinned to chrome tokens here. An ID
   or an unlayered class beats a layered utility, so no !important is needed. */
#site-header {
  background-color: var(--chrome-bg);
  border-bottom-color: var(--chrome-border);
}
#mobile-nav,
#mobile-search-bar {
  background-color: var(--chrome-bg);
  border-color: var(--chrome-border);
}
#mobile-nav .border-gray-800,
#site-header .border-gray-800 { border-color: var(--chrome-border); }
#mobile-nav .text-white { color: var(--chrome-fg); }

/* Admin sidebar: same reasoning, and it carries its own text colour so the
   `text-white` that used to be in the markup is no longer needed. */
.admin-sidebar {
  background-color: var(--sidebar-bg);
  color: var(--sidebar-fg);
  border-right: 1px solid var(--chrome-border);
}
.admin-sidebar h1 { color: var(--chrome-fg); }
.admin-sidebar .border-white\/10 { border-color: var(--chrome-border); }
.admin-sidebar .text-gray-400 { color: var(--chrome-fg-dim); }

.site-logo { filter: var(--logo-filter); }

/* ID beats the utility class, so these win without !important. */
#site-header .text-gray-300,
#site-header .text-gray-400 { color: var(--chrome-fg); }
#site-header a:hover .text-gray-300,
#site-header .text-gray-300:hover,
/* Not the header's filled controls or its popover: those carry their own
   colours, and this rule was repainting them on hover — to white on the dark
   palette, where it went unnoticed, and to near-black on the light one, where
   the label appeared to darken as the pointer arrived.

   It caught the mini-cart's Checkout button first, then the account avatar: the
   chip is an <a> inside the header, so `a:hover:not(.mc-cart-btn)` at (1,2,1)
   out-ranked `.account-chip:hover` at (1,2,0) and turned the initials near-black
   on the orange circle. Anything in the header that paints itself has to be
   named here — it cannot out-specify a selector carrying an ID. */
#site-header a:hover:not(.mc-cart-btn):not(.account-chip):not(.account-dropdown-link) { color: var(--chrome-fg); }
#site-header .text-white { color: var(--chrome-fg); }

/* Search field keeps a light plate, so its text must be dark to be legible. */
/* The wrapper is a square, unrounded div; it used to carry `shadow-sm`, so a
   rectangular shadow sat behind a 10px-rounded input and its corners showed
   through at all four edges. The shadow lives on the input now, which means it
   follows the same curve. */
.search-wrapper input,
#search-input {
  background-color: var(--field-bg);
  color: var(--field-fg);
  border: 1px solid var(--field-border);
  border-radius: var(--radius);
  box-shadow: var(--e1);
}

/* The magnifier and clear control sit inside that curve, so the wrapper needs
   the same radius for any focus ring drawn on it to line up. */
.search-wrapper { border-radius: var(--radius); }
.search-wrapper input::placeholder,
#search-input::placeholder { color: var(--field-placeholder); }
.search-wrapper input:focus,
#search-input:focus {
  color: var(--field-fg);
  border-color: var(--primary);
  box-shadow: var(--e1), 0 0 0 3px var(--accent-wash);
  outline: none;
}
.search-wrapper svg { color: var(--field-placeholder); }
.search-clear { color: var(--field-placeholder); }
.search-clear:hover { color: var(--field-fg); }

/* Footer: dark ground, light content, separated from the page by a hairline
   rather than a colour change. */
.site-footer {
  background-color: var(--footer-bg);
  color: var(--chrome-fg-soft);
  border-top: 1px solid var(--chrome-border);
}
.site-footer .footer-heading { color: var(--chrome-fg); }
.site-footer .footer-link,
.site-footer a { color: var(--chrome-fg-dim); }
.site-footer .footer-link:hover,
.site-footer a:hover { color: var(--chrome-fg); }
.site-footer .text-gray-400,
.site-footer .text-gray-500,
.site-footer .text-gray-600 { color: var(--chrome-fg-dim); }
.site-footer .text-white,
.site-footer .text-gray-900 { color: var(--chrome-fg); }

/* Quick-view close: a quiet chip that inverts to a dark glyph on hover. */
.qv-close {
  background-color: var(--muted);
  color: var(--fg);
  border: 1px solid var(--border);
  transition: background-color var(--t-fast) var(--ease),
              color var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease);
}
.qv-close:hover {
  background-color: var(--invert-bg);
  color: var(--invert-fg);
  border-color: var(--invert-bg);
}


/* ═══════════════════════════════════════════════════════════════════════════
   12. DEFAULT BORDER COLOUR
   ═══════════════════════════════════════════════════════════════════════════ */

/* Tailwind v4's preflight is `border: 0 solid` with no colour, so a bare
   `class="border"` inherits currentColor. On a dark theme that is near-white,
   which is why the cart and checkout cards were outlined in glaring white.
   v3 defaulted these to grey; this restores that behaviour using the theme's
   own border token.

   Deliberately inside @layer base: unlayered here would outrank every
   `border-gray-*` / `border-red-*` utility in the views and flatten them all
   to one colour. In base, explicit utilities still win. */
@layer base {
  *, ::before, ::after, ::backdrop, ::file-selector-button {
    border-color: var(--border);
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   13. SECONDARY BUTTON
   ═══════════════════════════════════════════════════════════════════════════ */

/* Same treatment as .order-now-btn, so every quiet action on the site reads the
   same. Replaces links that used `hover:text-black`, which on a dark ground
   faded the label to near-invisible on hover instead of highlighting it. */
.btn-secondary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  width: 100%;
  padding: .625rem 1rem;
  border-radius: var(--radius);
  background-color: var(--muted);
  color: var(--fg);
  border: 1px solid var(--border);
  font-size: .875rem;
  font-weight: 500;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--t-base) var(--ease),
              border-color var(--t-base) var(--ease),
              color var(--t-base) var(--ease);
}
.btn-secondary:hover {
  background-color: var(--card);
  border-color: var(--border-strong);
  color: var(--fg);
  text-decoration: none;
  opacity: 1;
}
.btn-secondary:active { background-color: var(--muted); }

/* Safety net for any remaining `hover:text-black` in the views — on a dark
   ground that resolves to near-invisible. */
.hover\:text-black:hover { color: var(--fg); }


/* ═══════════════════════════════════════════════════════════════════════════
   14. FORM CONTROLS
   ═══════════════════════════════════════════════════════════════════════════ */

/* Native radios and checkboxes render in the browser's own blue, which is the
   one thing on the page that does not belong to the palette. */
input[type="radio"],
input[type="checkbox"] {
  accent-color: var(--primary);
  width: 1rem;
  height: 1rem;
  cursor: pointer;
  flex-shrink: 0;
}

/* Selected shipping/payment rows had no visual state beyond the dot itself —
   the whole row is the click target, so the whole row should show selection.

   Toggle rows are excluded. A label wrapping a switch is not a selectable card;
   it is a control plus its name, and washing the entire row in accent colour
   the moment the switch came on made it look as though the row itself had been
   selected — which is what it looked like across Banners, Shipping Methods,
   Payment Methods and Blog Categories. The switch alone carries the state.

   Both markup shapes have to be excluded: `.toggle-switch` sits on the <label>
   in the wrapper pattern and on the <input> in the bare one, so the selector
   rules out the label carrying the class and the label containing it. */
label:has(input[type="radio"]:checked),
label:has(input[type="checkbox"]:checked):not(.toggle-switch):not(:has(.toggle-switch)) {
  border-color: var(--accent-edge);
  background-color: var(--accent-wash);
}
label:has(input[type="radio"]):hover,
label:has(input[type="checkbox"]):not(.toggle-switch):not(:has(.toggle-switch)):hover {
  background-color: var(--muted);
}
label:has(input[type="radio"]:checked):hover,
label:has(input[type="checkbox"]:checked):not(.toggle-switch):not(:has(.toggle-switch)):hover {
  background-color: var(--accent-wash);
}
/* The switch draws its own focus ring, so a second one around the whole row
   would just be noise. */
label:has(input[type="radio"]:focus-visible),
label:has(input[type="checkbox"]:focus-visible):not(.toggle-switch):not(:has(.toggle-switch)) {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
}

/* Autofill repaints the field with the browser's own light plate and dark text,
   which on a dark form reads as a white box mid-column. */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
textarea:-webkit-autofill,
select:-webkit-autofill {
  -webkit-text-fill-color: var(--fg);
  -webkit-box-shadow: 0 0 0 1000px var(--card) inset;
  caret-color: var(--fg);
}

textarea.input-field { min-height: 5rem; resize: vertical; }


/* ═══════════════════════════════════════════════════════════════════════════
   15. MEDIA PICKER MODAL
   ═══════════════════════════════════════════════════════════════════════════ */

/* The shell already centred itself, but the scrim was `bg-black/50` — half
   opacity of a colour that is nearly the page background. On a dark admin page
   that dims almost nothing, so the sidebar and form stayed legible behind the
   dialog and it never read as modal. Solid scrim + blur, and a z-index clearly
   above the sidebar and any sticky header. */
#media-picker-modal { position: fixed; inset: 0; z-index: 200; }

#media-picker-modal > div:first-child {
  position: fixed;
  inset: 0;
  background-color: rgba(4, 3, 3, .82);
  backdrop-filter: blur(6px) saturate(.6);
  -webkit-backdrop-filter: blur(6px) saturate(.6);
}

/* The centring layer. Kept as its own stacking level so the panel always sits
   above the scrim regardless of source order. */
#media-picker-modal > div:last-child {
  position: fixed;
  inset: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(12px, 3vw, 32px);
}

/* The dialog itself. A resolved height (not max-height) is what lets the grid
   and the action bar size against it — see the note in the markup. */
#media-picker-modal > div:last-child > div {
  width: 100%;
  max-width: 1060px;
  height: min(85vh, 820px);
  background-color: var(--popover);
  color: var(--popover-fg);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  box-shadow: var(--e4);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  animation: mc-pop-in var(--t-base) var(--ease-out) both;
}

/* The picker fragment is authored in light-mode Tailwind utilities; these pull
   its panels onto the theme so the dialog is not a white slab on a dark page. */
#media-picker-modal .bg-gray-50,
#media-picker-modal #picker-grid { background-color: var(--subtle); }
#media-picker-modal .border-gray-200 { border-color: var(--border); }
#media-picker-modal input[type="text"],
#media-picker-modal input[type="search"] {
  background-color: var(--card);
  color: var(--fg);
  border-color: var(--input);
  border-radius: var(--radius-sm);
}
#media-picker-modal input::placeholder { color: var(--muted-fg); }
#media-picker-modal .group { background-color: var(--card); border-color: var(--border); }
#media-picker-modal .group.selected { border-color: var(--primary); box-shadow: 0 0 0 2px var(--primary); }

/* Nothing behind the dialog should be reachable while it is open. */
body.mc-modal-open { overflow: hidden; }


/* ═══════════════════════════════════════════════════════════════════════════
   16. IN-PAGE GRID SWAPS
   Sorting, per-page and sidebar filters replace #product-grid-area over AJAX.
   Both innerHTML writes used to land instantly — skeleton in, results in — so
   the grid snapped twice per interaction. This eases each step instead.
   ═══════════════════════════════════════════════════════════════════════════ */
#product-grid-area {
  transition: opacity var(--t-base) var(--ease), filter var(--t-base) var(--ease);
}
#product-grid-area.is-loading {
  opacity: .45;
  filter: saturate(.6);
  pointer-events: none;
}
#product-grid-area.is-entering { animation: mc-grid-in var(--t-base) var(--ease-out) both; }

@keyframes mc-grid-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: none; }
}

/* The selects that drive those swaps. */
.sort-select, .per-page-select {
  cursor: pointer;
  transition: border-color var(--t-base) var(--ease),
              background-color var(--t-base) var(--ease),
              box-shadow var(--t-base) var(--ease),
              color var(--t-base) var(--ease);
}
.sort-select:hover, .per-page-select:hover { border-color: var(--border-strong); background-color: var(--muted); }
.sort-select:focus-visible, .per-page-select:focus-visible {
  outline: none;
  border-color: var(--ring);
  box-shadow: 0 0 0 3px var(--accent-wash);
}


/* ═══════════════════════════════════════════════════════════════════════════
   17. TOASTS
   `.toast` set colour:white on background:--color-gray-900 — which the inverted
   ramp turned into #EDEAE9. White on near-white: every cart notification was
   invisible.
   ═══════════════════════════════════════════════════════════════════════════ */
.toast {
  background-color: var(--popover);
  color: var(--fg);
  border: 1px solid var(--border);
  border-left: 3px solid var(--border-strong);
  border-radius: var(--radius);
  box-shadow: var(--e3);
  font-size: 13.5px;
  line-height: 1.45;
  max-width: min(380px, calc(100vw - 32px));
}
.toast-success { border-left-color: var(--success); }
.toast-error   { border-left-color: var(--destructive); }
.toast-stack   { gap: 8px; }

/* ═══════════════════════════════════════════════════════════════════════════
   18. ADMIN SIDEBAR
   Links read --color-gray-300, which the inverted ramp made #37312F — nearly
   the sidebar's own colour, so the menu looked switched off.
   ═══════════════════════════════════════════════════════════════════════════ */
.admin-sidebar-link {
  color: var(--sidebar-fg);
  font-weight: 500;
  border-radius: var(--radius-sm);
  margin-inline: 8px;
  padding-inline: 12px;
}
.admin-sidebar-link:hover { color: var(--chrome-fg); background-color: var(--chrome-hover); }
.admin-sidebar-link svg { opacity: .85; }
.admin-sidebar-link:hover svg { opacity: 1; }
.admin-sidebar-link.active { color: var(--primary-text); background-color: var(--accent-wash); font-weight: 600; }
.admin-sidebar-link.active svg { opacity: 1; }

/* ═══════════════════════════════════════════════════════════════════════════
   19. TOGGLES
   The track had no fixed box, so rows with a toggle sat at different heights
   and the knob drifted out of the track at some zoom levels.
   ═══════════════════════════════════════════════════════════════════════════ */
.toggle-switch {
  position: relative;
  display: inline-block;
  width: 40px;
  height: 22px;
  flex-shrink: 0;
  vertical-align: middle;
  cursor: pointer;
}
.toggle-slider {
  position: absolute;
  inset: 0;
  border-radius: 999px;
  background-color: var(--border-strong);
  border: 1px solid var(--border);
  transition: background-color var(--t-base) var(--ease), border-color var(--t-base) var(--ease);
}
.toggle-slider::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 3px;
  width: 16px;
  height: 16px;
  margin-top: -8px;
  border-radius: 50%;
  background-color: #FFFFFF;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .45);
  transition: transform var(--t-base) var(--ease);
}
.toggle-switch input:checked + .toggle-slider {
  background-color: var(--primary);
  border-color: var(--primary);
}
.toggle-switch input:checked + .toggle-slider::before { transform: translateX(18px); }
.toggle-switch input:focus-visible + .toggle-slider {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
}

/* ═══════════════════════════════════════════════════════════════════════════
   20. FORM GEOMETRY — SITE-WIDE
   Admin filter bars and one-off forms style their fields with raw utilities
   (`border border-gray-300 px-4 py-2`) and never picked up .input-field's
   radius, so half the site had square fields next to rounded ones.
   In @layer components so a deliberate `rounded-*` utility in a view still wins
   — the cart quantity stepper relies on rounded-l / rounded-r.
   ═══════════════════════════════════════════════════════════════════════════ */
@layer components {
  input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="submit"]):not([type="button"]),
  select,
  textarea {
    border-radius: var(--radius);
  }

  /* Same treatment for fields that never had .input-field on them. */
  input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="submit"]):not([type="button"]):not(.search-wrapper input),
  select,
  textarea {
    background-color: var(--card);
    color: var(--fg);
    border-color: var(--input);
  }
  input::placeholder, textarea::placeholder { color: var(--muted-fg); }
  input:focus-visible, select:focus-visible, textarea:focus-visible {
    outline: none;
    border-color: var(--ring);
    box-shadow: 0 0 0 3px var(--accent-wash);
  }
  select { cursor: pointer; }
}

/* The navbar search keeps its light plate — set after the block above so it
   is not pulled onto the dark surface. */
.search-wrapper input, #search-input { background-color: var(--field-bg); color: var(--field-fg); }

/* ═══════════════════════════════════════════════════════════════════════════
   21. PRICES
   ═══════════════════════════════════════════════════════════════════════════ */
/* One treatment for every price on the site.

   Before this, a discounted price was accent-coloured and a full price was not,
   so a grid of products showed two different-looking prices for no reason a
   shopper could see. The accent now marks "this is the price"; a sale is shown
   by what sits beside it — the struck original and the saving — rather than by
   the current figure changing colour. */
.price-row {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 8px;
}

.price-now {
  color: var(--primary-text);
  font-variant-numeric: tabular-nums;
  letter-spacing: -.012em;
}

.price-was {
  color: var(--muted-fg);
  font-weight: 400;
  text-decoration: line-through;
  /* The browser default hairline all but disappears on a small muted figure. */
  text-decoration-thickness: 1px;
  text-decoration-color: color-mix(in srgb, var(--muted-fg) 70%, transparent);
  font-variant-numeric: tabular-nums;
}

/* The saving, stated rather than left for the shopper to work out. */
.price-save {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--accent-wash);
  border: 1px solid var(--accent-edge);
  color: var(--primary-text);
  font-size: 11px;
  font-weight: 600;
  line-height: 1.5;
  letter-spacing: .01em;
  white-space: nowrap;
  /* Baseline alignment drags a bordered chip off the line it shares. */
  align-self: center;
}


/* Row links in admin tables: readable as text, obviously clickable on hover. */
.admin-row-link {
  color: var(--fg);
  text-decoration: none;
  transition: color var(--t-fast) var(--ease);
}
.admin-row-link:hover { color: var(--primary-text); text-decoration: underline; text-underline-offset: 2px; }

/* The login screen sits outside the admin shell and had its own light styling. */
.login-shell { background: var(--bg); }
.login-card {
  background-color: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  box-shadow: var(--e4);
}


/* ═══════════════════════════════════════════════════════════════════════════
   22. TOGGLES — BARE-INPUT VARIANT
   .toggle-switch was written as a wrapper <label> holding a visually-hidden
   checkbox plus a .toggle-slider span. Nine toggles across Banners, Shipping
   Methods and Payment Methods put the class straight on the <input> with no
   slider, so `.toggle-switch input` and `.toggle-slider` never matched and the
   rules only succeeded in stretching a native checkbox to 40x22.

   Styling the input directly covers that markup without touching nine pieces of
   conditional PHP — the name/value/checked attributes stay exactly as they are,
   so nothing about form submission changes. Both patterns now render the same
   control.
   ═══════════════════════════════════════════════════════════════════════════ */
input[type="checkbox"].toggle-switch {
  appearance: none;
  -webkit-appearance: none;
  position: relative;
  display: inline-block;
  width: 40px;
  height: 22px;
  margin: 0;
  padding: 0;
  flex-shrink: 0;
  vertical-align: middle;
  cursor: pointer;
  border-radius: 999px;
  background-color: var(--border-strong);
  border: 1px solid var(--border);
  transition: background-color var(--t-base) var(--ease),
              border-color var(--t-base) var(--ease);
}
input[type="checkbox"].toggle-switch::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 3px;
  width: 16px;
  height: 16px;
  margin-top: -8px;
  border-radius: 50%;
  background-color: #FFFFFF;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .45);
  transition: transform var(--t-base) var(--ease);
}
input[type="checkbox"].toggle-switch:checked {
  background-color: var(--primary);
  border-color: var(--primary);
}
input[type="checkbox"].toggle-switch:checked::before { transform: translateX(18px); }
input[type="checkbox"].toggle-switch:hover:not(:checked) { border-color: var(--color-gray-400); }
input[type="checkbox"].toggle-switch:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
}
input[type="checkbox"].toggle-switch:disabled { opacity: .5; cursor: not-allowed; }

/* Rows that pair a toggle with a label need the two on one baseline. */
.toggle-row { display: flex; align-items: center; gap: 10px; }


/* ═══════════════════════════════════════════════════════════════════════════
   23. PRODUCT IMAGERY — CORNERS
   The product and bundle pages build their main image and thumbnail strip from
   utility classes that clip overflow but never set a radius, so those were the
   last square corners on the storefront. Done in CSS rather than in the markup
   because the thumbnail strip is also rebuilt in JS when a variant is picked —
   styling the container covers both the server-rendered and the scripted ones.
   In @layer components so a deliberate rounded-* utility still wins.
   ═══════════════════════════════════════════════════════════════════════════ */
@layer components {
  /* Main image on the product and bundle pages. */
  .aspect-product,
  #main-image-container {
    border-radius: var(--radius-lg);
    overflow: hidden;
  }

  /* Thumbnail strip — server-rendered and JS-built alike. */
  #pdt-thumbnails > button,
  #pdt-thumbnails > a {
    border-radius: var(--radius-sm);
    overflow: hidden;
  }
  #pdt-thumbnails > button img { border-radius: inherit; }

  /* Quick-view modal: main plate and its thumbnails. */
  #qv-image { border-radius: var(--radius-lg); }
  .qv-thumb { border-radius: var(--radius-sm); overflow: hidden; }
  .qv-thumb img { border-radius: inherit; }

  /* Line-item thumbnails: cart rows and the bundle contents list. */
  .cart-row img,
  .bundle-item-img { border-radius: var(--radius-sm); }
}


/* ═══════════════════════════════════════════════════════════════════════════
   24. BLOG
   ═══════════════════════════════════════════════════════════════════════════ */
/* ── page hero ────────────────────────────────────────────────────────────────
   Heading, optional subheading, optional count, and a rule beneath that the
   page content starts under. Written for the blog first; the About, Contact and
   Bundles pages now open the same way, so every top-level page has the same
   first impression instead of three different ones.

   .blog-* are kept as aliases so the blog markup did not have to change and the
   two can never drift apart — there is one set of values here, not two. */
.page-hero,
.blog-header {
  padding: 40px 0 28px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 32px;
}
.page-hero-title,
.blog-title {
  font-size: clamp(28px, 4vw, 42px);
  letter-spacing: -.028em;
  /* The About heading is a full sentence rather than one word, so it needs a
     line height and a wrap that a one-word title never exercised. */
  line-height: 1.14;
  text-wrap: balance;
  max-width: 20ch;
}
.blog-title { max-width: none; }
.page-hero-sub,
.blog-subtitle {
  color: var(--muted-fg);
  margin-top: 8px;
  max-width: 62ch;
}
.page-hero-meta,
.blog-count {
  color: var(--muted-fg);
  font-size: 12.5px;
  margin-top: 10px;
  font-variant-numeric: tabular-nums;
}
@media (max-width: 640px) {
  .page-hero, .blog-header { padding: 28px 0 22px; margin-bottom: 26px; }
  .page-hero-title, .blog-title { max-width: none; }
}

/* Two-column shell shared by the listing and the post page. */
.blog-shell { display: grid; grid-template-columns: minmax(0, 1fr); gap: 40px; align-items: start; }
.blog-shell.has-sidebar { grid-template-columns: minmax(0, 1fr) 300px; }
.blog-shell.has-sidebar.sidebar-left { grid-template-columns: 300px minmax(0, 1fr); }
.blog-shell.has-sidebar.sidebar-left .blog-main { order: 2; }
.blog-shell.has-sidebar.sidebar-left .blog-sidebar { order: 1; }
@media (max-width: 980px) {
  .blog-shell.has-sidebar,
  .blog-shell.has-sidebar.sidebar-left { grid-template-columns: minmax(0, 1fr); }
  .blog-shell.has-sidebar.sidebar-left .blog-main { order: 1; }
  .blog-shell.has-sidebar.sidebar-left .blog-sidebar { order: 2; }
}

/* ── listing layouts ─────────────────────────────────────────────────────── */
.blog-grid { display: grid; gap: 22px; }
.blog-grid-grid { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
.blog-grid-list { grid-template-columns: minmax(0, 1fr); }
.blog-grid-list .blog-card { flex-direction: row; }
.blog-grid-list .blog-card-media { width: 230px; flex-shrink: 0; aspect-ratio: 4/3; }
.blog-grid-magazine { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
.blog-grid-magazine > .blog-card:first-child { grid-column: 1 / -1; flex-direction: row; }
.blog-grid-magazine > .blog-card:first-child .blog-card-media { width: 50%; aspect-ratio: 16/10; }
.blog-grid-magazine > .blog-card:first-child .blog-card-title { font-size: 22px; }
@media (max-width: 720px) {
  .blog-grid-list .blog-card,
  .blog-grid-magazine > .blog-card:first-child { flex-direction: column; }
  .blog-grid-list .blog-card-media,
  .blog-grid-magazine > .blog-card:first-child .blog-card-media { width: 100%; }
}

.blog-card {
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--card); border: 1px solid var(--border); border-radius: var(--radius-lg);
  transition: transform var(--t-slow) var(--ease), box-shadow var(--t-slow) var(--ease), border-color var(--t-slow) var(--ease);
}
.blog-card:hover { transform: translateY(-3px); border-color: var(--accent-edge); box-shadow: var(--e3); }
.blog-card-media { display: block; aspect-ratio: 16/10; background: var(--subtle); overflow: hidden; }
.blog-card-media img { width: 100%; height: 100%; object-fit: cover; transition: transform var(--t-slow) var(--ease); }
.blog-card:hover .blog-card-media img { transform: scale(1.045); }
.blog-card-body { padding: 16px 18px 18px; display: flex; flex-direction: column; gap: 7px; flex: 1; }
.blog-card-cat { font-size: 10.5px; text-transform: uppercase; letter-spacing: .08em; font-weight: 600; color: var(--primary-text); text-decoration: none; }
.blog-card-title { font-size: 16.5px; line-height: 1.35; letter-spacing: -.012em; }
.blog-card-title a { color: var(--fg); text-decoration: none; }
.blog-card-title a:hover { color: var(--primary-text); }
.blog-card-excerpt { color: var(--muted-fg); font-size: 13.5px; line-height: 1.55; }
.blog-card-meta { margin-top: auto; padding-top: 6px; display: flex; gap: 14px; font-size: 11.5px; color: var(--muted-fg); }

.blog-pagination { display: flex; gap: 8px; justify-content: center; margin-top: 36px; flex-wrap: wrap; }
.blog-empty { text-align: center; padding: 64px 20px; color: var(--muted-fg); border: 1px dashed var(--border); border-radius: var(--radius-lg); }

/* ── sidebar ─────────────────────────────────────────────────────────────── */
.blog-widget { background: var(--card); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 18px; }
.blog-widget-title { font-size: 12px; text-transform: uppercase; letter-spacing: .09em; color: var(--muted-fg); margin-bottom: 14px; font-weight: 600; }
.blog-mini { display: flex; gap: 11px; align-items: center; text-decoration: none; }
.blog-mini-img { width: 52px; height: 52px; border-radius: var(--radius-sm); object-fit: cover; background: var(--subtle); flex-shrink: 0; }
.blog-mini-title { display: block; font-size: 13px; font-weight: 550; color: var(--fg); line-height: 1.35; }
.blog-mini:hover .blog-mini-title { color: var(--primary-text); }
.blog-mini-date { display: block; font-size: 11px; color: var(--muted-fg); margin-top: 2px; }
.blog-cat-link { display: flex; justify-content: space-between; gap: 10px; padding: 6px 0; font-size: 13.5px; color: var(--fg); text-decoration: none; border-bottom: 1px solid var(--border); }
.blog-cat-link:hover { color: var(--primary-text); }
.blog-cat-count { color: var(--muted-fg); font-size: 12px; font-variant-numeric: tabular-nums; }
.blog-tag {
  display: inline-block; padding: 4px 10px; border-radius: 999px; font-size: 11.5px;
  background: var(--muted); color: var(--fg); border: 1px solid var(--border); text-decoration: none;
  transition: background-color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.blog-tag:hover { background: var(--accent-wash); border-color: var(--accent-edge); color: var(--primary-text); }

/* ── single post ─────────────────────────────────────────────────────────── */
.blog-post-header { padding: 36px 0 20px; max-width: 74ch; }
.blog-post-cat { font-size: 11px; text-transform: uppercase; letter-spacing: .09em; font-weight: 600; color: var(--primary-text); text-decoration: none; }
.blog-post-title { font-size: clamp(28px, 4.4vw, 46px); line-height: 1.1; letter-spacing: -.03em; margin-top: 10px; }
.blog-post-standfirst { color: var(--muted-fg); font-size: 17px; line-height: 1.6; margin-top: 14px; max-width: 62ch; }
.blog-post-meta { display: flex; gap: 16px; flex-wrap: wrap; font-size: 12.5px; color: var(--muted-fg); margin-top: 16px; }

.blog-hero { margin: 8px 0 34px; border-radius: var(--radius-xl); overflow: hidden; background: var(--subtle); }
.blog-hero img { width: 100%; height: auto; display: block; max-height: 460px; object-fit: cover; }
.blog-hero-wide { margin-inline: 0; }
.blog-hero-wide img { max-height: 560px; }

/* The scrim is built here rather than inline in the view, so it can follow the
   palette. The view supplies only --cover-image. */
.blog-cover {
  position: relative; border-radius: var(--radius-xl); overflow: hidden;
  background-image: linear-gradient(to top, var(--cover-scrim-strong), var(--cover-scrim-soft)), var(--cover-image, none);
  background-size: cover; background-position: center; min-height: 380px;
  display: flex; align-items: flex-end; margin: 24px 0 34px;
}
.blog-cover-inner { padding: 34px; max-width: 74ch; }

/* Text on the scrim takes its own colours: --fg is the colour for the *page*
   background, which is the opposite of what the scrim needs on one palette or
   the other depending on how the scrim is built. */
.blog-cover .blog-post-title { color: var(--cover-fg); }
.blog-cover .blog-post-meta,
.blog-cover .blog-post-meta span { color: var(--cover-fg-dim); }
.blog-cover .blog-post-standfirst { color: var(--cover-fg-dim); }

/* The category is the one label that cannot simply take a palette colour: it
   sits high in the banner where the scrim is thinnest, and terracotta type
   measured 1.91:1 on the dark palette and 2.48:1 on the light — unreadable
   either way, and worse over a busier photograph. Given its own solid backing
   it keeps the accent and stops depending on the picture behind it. */
.blog-cover .blog-post-cat {
  display: inline-block;
  padding: 4px 10px;
  border-radius: 999px;
  /* The deeper accent, not --primary: this is 11px text, and white on #BC602D
     is 4.33:1 — fine for a big button label, short of AA for something this
     small. --primary-hover is already in the palette and measures 5.26:1. */
  background: var(--primary-hover);
  color: var(--primary-fg);
  margin-bottom: 2px;
}
/* Darkening by filter rather than by another token keeps the hover honest on
   both palettes without a second pair of values to keep in step. */
.blog-cover .blog-post-cat:hover {
  filter: brightness(.92);
  color: var(--primary-fg);
  text-decoration: none;
}

/* Reading column. The measure is capped for comfort even on a wide screen. */
.blog-content { max-width: 74ch; font-size: 16.5px; line-height: 1.75; color: var(--fg); }
.blog-layout-centered .blog-content { margin-inline: auto; }
.blog-content > * + * { margin-top: 1.15em; }
.blog-content h2 { font-size: 25px; margin-top: 1.9em; letter-spacing: -.02em; }
.blog-content h3 { font-size: 20px; margin-top: 1.6em; letter-spacing: -.015em; }
.blog-content h4 { font-size: 17px; margin-top: 1.4em; }
.blog-content p { color: var(--fg); }
.blog-content a { color: var(--primary-text); text-decoration: underline; text-underline-offset: 2px; }
.blog-content ul, .blog-content ol { padding-left: 1.4em; }
.blog-content li + li { margin-top: .4em; }
.blog-content img { max-width: 100%; height: auto; border-radius: var(--radius-lg); }
.blog-content blockquote {
  border-left: 3px solid var(--primary); padding: 4px 0 4px 18px;
  color: var(--muted-fg); font-style: italic;
}
.blog-content pre {
  background: var(--muted); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 14px 16px; overflow-x: auto; font-size: 13.5px;
}
.blog-content code { background: var(--muted); padding: .12em .38em; border-radius: 5px; font-size: .9em; }
.blog-content pre code { background: none; padding: 0; }
.blog-content table { width: 100%; border-collapse: collapse; font-size: 14px; display: block; overflow-x: auto; }
.blog-content th, .blog-content td { border: 1px solid var(--border); padding: 8px 12px; text-align: left; }
.blog-content th { background: var(--muted); }
.blog-content hr { border: 0; border-top: 1px solid var(--border); }

.blog-gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; margin-top: 32px; }
.blog-gallery img { width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: var(--radius); background: var(--subtle); }
.blog-post-tags { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 32px; }

.blog-share { margin-top: 32px; padding-top: 22px; border-top: 1px solid var(--border); display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.blog-share-label { font-size: 12px; text-transform: uppercase; letter-spacing: .09em; color: var(--muted-fg); font-weight: 600; }
.blog-share-buttons { display: flex; flex-wrap: wrap; gap: 8px; }
.share-btn {
  display: inline-flex; align-items: center; padding: 7px 14px; border-radius: var(--radius);
  background: var(--muted); color: var(--fg); border: 1px solid var(--border);
  font-size: 12.5px; font-weight: 550; text-decoration: none; cursor: pointer;
  transition: background-color var(--t-base) var(--ease), border-color var(--t-base) var(--ease), color var(--t-base) var(--ease);
}
.share-btn:hover { background: var(--accent-wash); border-color: var(--accent-edge); color: var(--primary-text); text-decoration: none; }

.blog-section-title { font-size: 19px; margin: 44px 0 18px; letter-spacing: -.018em; }
.blog-related { margin-top: 12px; }

.blog-comments { margin-top: 12px; }
.blog-comment { background: var(--card); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 15px 17px; margin-bottom: 12px; }
.blog-comment-head { display: flex; gap: 12px; align-items: baseline; margin-bottom: 6px; }
.blog-comment-head strong { font-size: 13.5px; }
.blog-comment-head span { font-size: 11.5px; color: var(--muted-fg); }
.blog-comment p { font-size: 14px; line-height: 1.6; color: var(--muted-fg); }
.blog-comment-empty { color: var(--muted-fg); font-size: 14px; padding: 8px 0 16px; }
.blog-comment-form { background: var(--card); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 20px; margin-top: 22px; }


/* ── blog live search ─────────────────────────────────────────────────────── */
.blog-search-wrap { position: relative; }
.blog-search-dropdown {
  position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 40;
  background: var(--popover); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--e3);
  max-height: 340px; overflow-y: auto; overscroll-behavior: contain;
}
.blog-search-dropdown[hidden] { display: none; }
.blog-search-item {
  display: flex; gap: 10px; align-items: center; padding: 9px 11px;
  text-decoration: none; border-bottom: 1px solid var(--border);
  transition: background-color var(--t-fast) var(--ease);
}
.blog-search-item:last-of-type { border-bottom: 0; }
.blog-search-item:hover, .blog-search-item.is-active { background: var(--accent-wash); text-decoration: none; }
.blog-search-img {
  width: 40px; height: 40px; flex-shrink: 0; border-radius: var(--radius-sm);
  object-fit: cover; background: var(--subtle); border: 1px solid var(--border); display: block;
}
.blog-search-text { min-width: 0; }
.blog-search-title {
  display: block; font-size: 13px; font-weight: 550; color: var(--fg); line-height: 1.35;
  overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.blog-search-meta { display: block; font-size: 11px; color: var(--muted-fg); margin-top: 2px; }
.blog-search-empty { padding: 18px 12px; text-align: center; font-size: 13px; color: var(--muted-fg); }
.blog-search-all {
  display: block; padding: 9px 11px; text-align: center; font-size: 12.5px; font-weight: 600;
  color: var(--primary-text); text-decoration: none; border-top: 1px solid var(--border); background: var(--subtle);
}
.blog-search-all:hover, .blog-search-all.is-active { background: var(--accent-wash); text-decoration: none; }

/* Unique readers, shown under the running total in the admin post list. */
.admin-view-readers {
  display: block; font-size: 11px; color: var(--muted-fg); margin-top: 1px; white-space: nowrap;
}


/* ── header navigation ────────────────────────────────────────────────────────
   The menu used to signal the current page by being the only white item, with
   every other link dimmed to a warm grey — which read as dull rather than
   deliberate. Links are white now, and the current page is marked with an
   accent rule underneath instead, so nothing is greyed out to make that point.
*/
.nav-link {
  position: relative;
  /* Capitalised rather than shouted, and the wide tracking that suited all-caps
     pulled back — at sentence case it read as gappy. */
  font-size: .8125rem;
  line-height: 1rem;
  font-weight: 500;
  text-transform: none;
  letter-spacing: .005em;
  color: var(--chrome-fg);
  white-space: nowrap;
  transition: opacity var(--t-fast) var(--ease);
}
#site-header .nav-link { color: var(--chrome-fg); }
.nav-link:hover { opacity: .72; text-decoration: none; }

.nav-link::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -7px;
  height: 2px;
  border-radius: 2px;
  background: var(--primary);
  transform: scaleX(0);
  transform-origin: center;
  transition: transform var(--t-base) var(--ease);
}
.nav-link.is-active::after { transform: scaleX(1); }
.nav-link.is-active { opacity: 1; }

/* Someone navigating by keyboard gets the same underline, plus a real ring. */
.nav-link:focus-visible::after { transform: scaleX(1); }

/* Social icons in the header: white, dimming on hover like the menu. */
/* Every icon in the header sits in the same 36px box. The cart carried `p-2`
   from the markup while the social icons had none, so with one flex gap the
   cart looked pushed away from its neighbours. Equal boxes, equal gaps. */
.header-social,
#site-header #mc-cart-trigger {
  width: 36px;
  height: 36px;
  padding: 0;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

/* `display` is the one property here that has to stay beatable. The header
   carries two sets of these icons — `hidden lg:inline-flex` for desktop and
   `lg:hidden` for mobile — and those utilities live in @layer utilities. An
   unlayered `display: inline-flex` outranks a layered rule whatever its
   specificity, which showed both sets at once on desktop. Declaring it inside
   @layer components puts it back underneath the utilities that switch it. */
@layer components {
  .header-social,
  #site-header #mc-cart-trigger {
    display: inline-flex;
  }
}
.header-social {
  color: var(--chrome-fg);
  transition: opacity var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
/* Should any header icon ever gain a text label, this lets it size to content
   without breaking the equal-box rule above. */
.header-social.has-label { width: auto; gap: 6px; padding-inline: 8px; }
#site-header .header-social { color: var(--chrome-fg); }
.header-social:hover { opacity: .72; transform: translateY(-1px); text-decoration: none; }

/* ── mobile drawer navigation ─────────────────────────────────────────────── */
.mobile-nav-link {
  display: block;
  padding: .5rem 0;
  font-size: .875rem;
  line-height: 1.25rem;
  font-weight: 500;
  color: var(--chrome-fg);
  transition: opacity var(--t-fast) var(--ease);
}
#mobile-nav .mobile-nav-link { color: var(--chrome-fg); }
.mobile-nav-link:hover { opacity: .72; text-decoration: none; }

/* The categories toggle is the one <button> wearing this class. It needs to be
   a row — label then chevron, side by side.
   This rule has to exist because the `display: block` above is unlayered and so
   outranks Tailwind's own `flex` utility on the element, which left the chevron
   wrapping onto a line of its own. Overriding `justify-content` here for the
   same reason: the markup's `justify-between` would otherwise fling the chevron
   to the far edge of the drawer. */
.mobile-nav-link:is(button) {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: .375rem;
  width: 100%;
}

/* The current page is marked with an accent bar, not by tinting the label —
   same reasoning as the desktop underline: every item stays white. */
.mobile-nav-link.is-active {
  color: var(--chrome-fg);
  padding-left: .625rem;
  border-left: 2px solid var(--primary);
  margin-left: -.75rem;
}

.mobile-subnav-link {
  display: block;
  padding: .375rem 0;
  font-size: .875rem;
  line-height: 1.25rem;
  color: var(--chrome-fg);
  transition: opacity var(--t-fast) var(--ease);
}
#mobile-nav .mobile-subnav-link { color: var(--chrome-fg); }
.mobile-subnav-link:hover { opacity: .72; text-decoration: none; }
.mobile-subnav-link.is-active {
  color: var(--chrome-fg);
  padding-left: .625rem;
  border-left: 2px solid var(--primary);
  margin-left: -.75rem;
}


/* ── informational side panel ─────────────────────────────────────────────────
   The "what happens next" card on the order confirmation. It was written
   against a light theme — teal/emerald tints on near-white — which the dark
   remap never touched, because only the gray/green/red/amber/blue/yellow ramps
   are redefined. The result was a bright panel sitting on a near-black page.
   Rebuilt on the same tokens as the rest of the site, so it inherits any future
   palette change instead of pinning its own colours.
*/
.info-panel {
  background: linear-gradient(158deg, var(--accent-wash) 0%, var(--card) 62%);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 22px;
}

.info-panel-icon {
  width: 48px;
  height: 48px;
  border-radius: 999px;
  background: var(--accent-wash);
  border: 1px solid var(--border-strong);
  color: var(--primary-text);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 16px;
}

.info-panel-title {
  font-size: 15px;
  font-weight: 700;
  color: var(--fg);
  margin-bottom: 8px;
}

.info-panel-text {
  font-size: 13.5px;
  line-height: 1.65;
  color: var(--muted-fg);
}
/* The shop name is the one thing worth lifting out of the body copy. */
.info-panel-text strong { color: var(--fg); font-weight: 600; }

.info-panel-divider {
  border: 0;
  border-top: 1px solid var(--border);
  margin: 18px 0;
}

.info-panel-note { font-size: 12px; line-height: 1.6; color: var(--muted-fg); }
.info-panel-note a {
  color: var(--primary-text);
  text-decoration: underline;
  text-underline-offset: 2px;
  transition: color var(--t-fast) var(--ease);
}
.info-panel-note a:hover { color: var(--primary); }

/* ── palette picker (Admin → Settings → Appearance) ───────────────────────── */
.palette-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 12px;
}

.palette-option {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 40px 12px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--card);
  cursor: pointer;
  transition: border-color var(--t-fast) var(--ease),
              background-color var(--t-fast) var(--ease);
}
.palette-option:hover { border-color: var(--border-strong); }

/* Selection follows the radio, not a class printed by the server.

   `.is-active` was written once at render time, so clicking the other card
   moved the radio but left the orange border, the wash and the tick sitting on
   the previous choice — the picker gave no sign it had registered the click
   until the form was saved and the page came back.

   `:has(:checked)` tracks the live state, so the highlight moves the instant a
   card is clicked. A ring rather than a thicker border, so nothing shifts by a
   pixel as the selection moves. `.is-active` is kept alongside it and is now
   maintained by script, which is what carries a browser with no :has(). */
.palette-option.is-active,
.palette-option:has(input[type="radio"]:checked) {
  border-color: var(--primary);
  background: var(--accent-wash);
  box-shadow: 0 0 0 1px var(--primary);
}
.palette-option.is-active .palette-name,
.palette-option:has(input[type="radio"]:checked) .palette-name { color: var(--primary-text); }

/* The radio itself is the accessible control; it is placed over the whole card
   rather than hidden, so keyboard focus and screen readers still find it. */
.palette-option input[type="radio"] {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.palette-option:focus-within { border-color: var(--primary); box-shadow: 0 0 0 3px var(--ring); }

.palette-swatches {
  display: flex;
  flex-shrink: 0;
  border-radius: var(--radius-sm);
  overflow: hidden;
  border: 1px solid var(--border);
}
.palette-swatch { width: 16px; height: 40px; display: block; }

.palette-meta { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.palette-name { font-size: 13.5px; font-weight: 600; color: var(--fg); }
.palette-desc { font-size: 11.5px; line-height: 1.45; color: var(--muted-fg); }

.palette-check {
  position: absolute;
  top: 50%;
  right: 12px;
  width: 20px;
  height: 20px;
  margin-top: -10px;
  color: var(--primary);
  opacity: 0;
  transition: opacity var(--t-fast) var(--ease);
}
.palette-option.is-active .palette-check,
.palette-option:has(input[type="radio"]:checked) .palette-check { opacity: 1; }
.palette-check svg { width: 100%; height: 100%; }


/* ── page scrollbar ───────────────────────────────────────────────────────────
   Applies everywhere except the admin panel, which keeps the browser default:
   long admin tables scroll inside their own containers, and those already have
   the thin treatment further up.

   Both syntaxes are needed — `scrollbar-color` covers Firefox and Chrome 121+,
   the ::-webkit- pseudo-elements cover Safari and older Chrome. */
:root:not([data-surface="admin"]) {
  scrollbar-width: thin;
  scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
}
:root:not([data-surface="admin"])::-webkit-scrollbar {
  width: 11px;
  height: 11px;
}
:root:not([data-surface="admin"])::-webkit-scrollbar-track {
  background: var(--scrollbar-track);
}
:root:not([data-surface="admin"])::-webkit-scrollbar-thumb {
  background: var(--scrollbar-thumb);
  border-radius: 999px;
  /* Inset by the track colour so the thumb reads as a capsule rather than
     filling the gutter edge to edge. */
  border: 3px solid var(--scrollbar-track);
  background-clip: padding-box;
}
:root:not([data-surface="admin"])::-webkit-scrollbar-thumb:hover {
  background: var(--scrollbar-thumb-hover);
  background-clip: padding-box;
}
:root:not([data-surface="admin"])::-webkit-scrollbar-corner { background: var(--scrollbar-track); }


/* ── accent-filled controls ───────────────────────────────────────────────────
   Anything carrying the terracotta fill keeps a white label in every state and
   in both palettes. Stated here, unlayered and with the header's own selectors
   matched, because a filled button dropped inside the header would otherwise
   inherit the header's link colour on hover. Only the fill moves on hover; the
   label never does. */
.btn-primary, .btn-primary:hover, .btn-primary:focus, .btn-primary:active,
.mc-cta, .mc-cta:hover, .mc-cta:focus, .mc-cta:active,
.mc-cart-btn.solid, .mc-cart-btn.solid:hover, .mc-cart-btn.solid:focus,
#site-header .mc-cart-btn.solid,
#site-header .mc-cart-btn.solid:hover,
#site-header a.btn-primary,
#site-header a.btn-primary:hover,
.badge-sale, .announcement-bar, .announcement-bar a {
  color: var(--primary-fg);
}

/* The fill itself, so hover is a visible change of shade rather than nothing. */
.mc-cart-btn.solid:hover,
#site-header .mc-cart-btn.solid:hover { background-color: var(--primary-hover); }

/* ── quantity stepper ─────────────────────────────────────────────────────────
   One control for the product page, the product grid, bundle cards and the cart
   rows. They were three different builds: the grid and cart glued a button, a
   bare-bordered input and another button together with `rounded-l`/`rounded-r`,
   which left sharp inner corners and doubled borders where the pieces met; the
   cart even put a gap between them so they read as three separate controls.
   This is a single bordered group with dividers inside it. */
.qty-stepper {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--input);
  border-radius: var(--radius);
  background: var(--card);
  overflow: hidden;
}

.qty-stepper button {
  width: 34px;
  height: 34px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--muted-fg);
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
  transition: background-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.qty-stepper button:hover { background: var(--muted); color: var(--fg); }
.qty-stepper button:disabled { opacity: .4; cursor: not-allowed; background: transparent; }

.qty-stepper input {
  width: 46px;
  height: 34px;
  min-width: 0;
  padding: 0;
  text-align: center;
  border: 0;
  border-left: 1px solid var(--input);
  border-right: 1px solid var(--input);
  border-radius: 0;
  background: transparent;
  color: var(--fg);
  font-size: 13px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.qty-stepper input:focus,
.qty-stepper input:focus-visible {
  outline: none;
  box-shadow: none;
  background: var(--muted);
}

/* Grid and bundle cards run the stepper the full width of the card. */
.qty-stepper.is-block { display: flex; width: 100%; }
.qty-stepper.is-block input { flex: 1 1 auto; width: auto; }

/* Cart rows are tighter. */
.qty-stepper.is-sm button { width: 28px; height: 28px; font-size: 15px; }
.qty-stepper.is-sm input { width: 40px; height: 28px; font-size: 12.5px; }

/* ── clear cart ───────────────────────────────────────────────────────────────
   Was an unadorned line of grey text that turned red on hover, easy to miss and
   easy to hit by accident. It is a real button now, quiet until pointed at. */
.btn-clear-cart {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 8px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: transparent;
  color: var(--muted-fg);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  transition: color var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease),
              background-color var(--t-fast) var(--ease);
}
.btn-clear-cart svg { width: 15px; height: 15px; }
.btn-clear-cart:hover {
  color: var(--destructive);
  border-color: color-mix(in srgb, var(--destructive) 45%, transparent);
  background: color-mix(in srgb, var(--destructive) 10%, transparent);
}

/* ── about page ───────────────────────────────────────────────────────────────
   The hero used the storefront's generic banner block, which on a page with no
   banner image was an empty dark slab. It is a drawn panel now: circuit traces
   built from the palette's own tokens, so it costs no image, needs nothing
   uploaded, and re-colours with the theme like everything else. */
/* .about-hero is now .page-hero; see the page hero component above. */



/* At-a-glance figures. Real counts, not decoration. */
.about-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 12px;
}
.about-stat {
  padding: 18px 20px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--card);
}
.about-stat b {
  display: block;
  font-size: 26px;
  font-weight: 700;
  line-height: 1.1;
  color: var(--primary-text);
  font-variant-numeric: tabular-nums;
  letter-spacing: -.02em;
}
.about-stat span { display: block; margin-top: 5px; font-size: 12.5px; color: var(--muted-fg); }

/* Why-choose-us cards. */
.about-card {
  padding: 26px 22px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--card);
  transition: border-color var(--t-base) var(--ease), transform var(--t-base) var(--ease);
}
.about-card:hover { border-color: var(--border-strong); transform: translateY(-2px); }
.about-card-icon {
  width: 44px;
  height: 44px;
  margin-bottom: 16px;
  border-radius: var(--radius);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--accent-wash);
  border: 1px solid var(--accent-edge);
  color: var(--primary-text);
}
.about-card-icon svg { width: 21px; height: 21px; }
.about-card h3 { font-size: 15px; font-weight: 650; color: var(--fg); margin-bottom: 7px; }
.about-card p { font-size: 13.5px; line-height: 1.6; color: var(--muted-fg); }

/* Story column. The placeholder that used to sit here was an external image
   service; this is drawn locally and cannot fail to load. */
.about-figure {
  border-radius: var(--radius-xl);
  border: 1px solid var(--border);
  overflow: hidden;
  background: var(--photo-tile);
}
.about-figure img { display: block; width: 100%; height: auto; }
.about-figure-fallback {
  aspect-ratio: 4 / 3;
  background:
    radial-gradient(100% 120% at 20% 0%, var(--accent-wash) 0%, transparent 60%),
    linear-gradient(150deg, var(--card), var(--muted));
  background:
    radial-gradient(100% 130% at 18% 0%,
      color-mix(in srgb, var(--primary) 26%, transparent) 0%, transparent 62%),
    linear-gradient(150deg, var(--card), var(--muted));
}

.about-prose { color: var(--muted-fg); line-height: 1.75; font-size: 14.5px; }
.about-prose p + p { margin-top: 14px; }
.about-prose strong { color: var(--fg); font-weight: 600; }

/* Closing call to action. */
.about-cta {
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  background:
    radial-gradient(110% 160% at 85% 0%, var(--accent-wash) 0%, transparent 60%),
    var(--card);
  padding: 34px 28px;
  text-align: center;
}

/* ── quick view ───────────────────────────────────────────────────────────────
   The modal reused none of the site's own components: its own price colours, a
   third build of the quantity stepper, a tiny underlined "details" link beside
   two real buttons. It now borrows the same pieces every other surface uses. */
.qv-media-col { background: var(--photo-tile); }

/* The hover zoom the product cards have, so the image behaves the same here. */
.qv-media { overflow: hidden; }
.qv-media img { transition: transform var(--t-slow) var(--ease); will-change: transform; }
.qv-media:hover img { transform: scale(1.06); }

.qv-eyebrow {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .09em;
  color: var(--muted-fg);
}
.qv-title {
  font-size: 19px;
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -.018em;
  color: var(--fg);
}
@media (min-width: 1024px) { .qv-title { font-size: 22px; } }
.qv-sku { font-size: 12px; color: var(--muted-fg); font-variant-numeric: tabular-nums; }

/* Kit contents on a bundle quick view. */
.qv-kit { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.qv-kit > * + * { border-top: 1px solid var(--border); }

/* Quantity. This used to lean on `mt-5`, which output.css does not contain —
   its scale jumps mt-4 to mt-6 — so the class resolved to nothing and the row
   sat hard against the price with zero space between them. Every other block in
   this panel owns its spacing here, and now so does this one. */
.qv-qty-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 18px;
}
.qv-qty-label {
  font-size: 13px;
  font-weight: 500;
  color: var(--muted-fg);
}

/* The two real actions. A third bordered button beside them made "view full
   details" look like an equal choice; it is a way out of the panel, so it is a
   link below the description instead. */
.qv-actions { display: flex; flex-direction: column; gap: 10px; margin-top: 20px; }

/* Description sits under the actions, separated by a rule rather than just
   whitespace so the block reads as reference material. */
.qv-description-wrap {
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
}

.qv-details-link {
  display: inline-block;
  margin-top: 16px;
  font-size: 13px;
  font-weight: 500;
  color: var(--primary-text);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  transition: color var(--t-fast) var(--ease);
}
.qv-details-link:hover { color: var(--primary); text-decoration: underline; }
.qv-description {
  font-size: 13.5px;
  line-height: 1.7;
  color: var(--muted-fg);
}
/* An empty description would otherwise leave a stray heading and rule behind. */
.qv-description-wrap:has(.qv-description:empty) { display: none; }

/* The About page had its own banner type. It now opens on .page-hero like the
   blog, contact and bundles pages, so those rules are gone rather than being
   left here to drift. */

/* ── every remaining scroll area ──────────────────────────────────────────────
   output.css carries a universal fallback:

       *                     { scrollbar-color: var(--color-microchip-dark) ... }
       ::-webkit-scrollbar-thumb { background: var(--color-microchip-dark); }

   Those microchip-* tokens stay deliberately dark in both palettes, because
   they also fill the variant buttons and the admin sidebar where white text
   sits on them. As a scrollbar that reads as a dark bar — and because `*` beats
   inheritance from :root, it won every scroll area not named individually
   above. The shop sidebar showed it plainly: the category list had the pale
   thumb while the sidebar's own outer scrollbar stayed dark.

   This routes the rest through the same two tokens. Scoped away from the admin
   surface, which keeps the treatment it had. */
:root:not([data-surface="admin"]) * {
  scrollbar-width: thin;
  scrollbar-color: var(--scrollbar-thumb) transparent;
}
:root:not([data-surface="admin"]) ::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}
:root:not([data-surface="admin"]) ::-webkit-scrollbar-track {
  background: transparent;
}
:root:not([data-surface="admin"]) ::-webkit-scrollbar-thumb {
  background: var(--scrollbar-thumb);
  border-radius: 999px;
}
:root:not([data-surface="admin"]) ::-webkit-scrollbar-thumb:hover {
  background: var(--scrollbar-thumb-hover);
}

/* The page gutter keeps its own slightly wider bar and visible track; the rule
   above would otherwise flatten it to match the inner panels. */
:root:not([data-surface="admin"]) {
  scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
}

/* ═══════════════════════════════════════════════════════════════════════════
   CUSTOMER ACCOUNTS
   Built on the same tokens as everything else, so the portal follows whichever
   palette the shop is running.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Sign-in, registration and password-reset styling lives in §26 — those pages
   run on their own split-screen shell rather than inside the site layout. What
   stays here is shared with the portal forms. */
.field-hint { margin-top: 5px; font-size: 11.5px; color: var(--muted-fg); }
.label-optional { color: var(--muted-fg); font-weight: 400; }

/* ── portal shell ───────────────────────────────────────────────────────── */
.account-shell { display: grid; grid-template-columns: 240px minmax(0, 1fr); gap: 28px; padding-bottom: 56px; }
@media (max-width: 860px) { .account-shell { grid-template-columns: 1fr; gap: 20px; } }

.account-nav {
  align-self: start; padding: 16px; border: 1px solid var(--border);
  border-radius: var(--radius-lg); background: var(--card);
}
@media (min-width: 861px) { .account-nav { position: sticky; top: 96px; } }

.account-user { display: flex; align-items: center; gap: 11px; padding: 4px 6px 16px; border-bottom: 1px solid var(--border); }
.account-avatar {
  width: 40px; height: 40px; flex-shrink: 0; border-radius: 999px;
  display: flex; align-items: center; justify-content: center;
  background: var(--accent-wash); border: 1px solid var(--accent-edge);
  color: var(--primary-text); font-weight: 700; font-size: 16px;
}
.account-user-meta { min-width: 0; display: flex; flex-direction: column; }
.account-user-meta strong { font-size: 13.5px; color: var(--fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.account-user-meta span { font-size: 11.5px; color: var(--muted-fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.account-links { display: flex; flex-direction: column; gap: 2px; padding-top: 12px; }
.account-link {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 9px 10px; border: 0; border-radius: var(--radius-sm);
  background: transparent; color: var(--muted-fg);
  font-size: 13.5px; font-weight: 500; text-align: left; cursor: pointer;
  transition: background-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.account-link svg { width: 17px; height: 17px; flex-shrink: 0; }
.account-link:hover { background: var(--muted); color: var(--fg); text-decoration: none; }
.account-link.is-active { background: var(--accent-wash); color: var(--primary-text); font-weight: 600; }
.account-signout { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); width: 100%; }
.account-link.is-signout:hover { color: var(--destructive); background: color-mix(in srgb, var(--destructive) 10%, transparent); }

.account-body { min-width: 0; }
.account-title { font-size: 25px; font-weight: 700; letter-spacing: -.02em; color: var(--fg); margin-bottom: 6px; }
.account-lead { font-size: 14px; color: var(--muted-fg); margin-bottom: 22px; }
/* A second heading inside a portal page — "Saved bundles" under the products. */
.account-subtitle { font-size: 17px; font-weight: 600; letter-spacing: -.015em; color: var(--fg); margin-bottom: 12px; }
.account-back { display: inline-block; margin-bottom: 12px; font-size: 13px; color: var(--muted-fg); }
.account-back:hover { color: var(--primary-text); }

.account-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px; margin-bottom: 22px; }
.account-stat {
  padding: 16px 18px; border: 1px solid var(--border); border-radius: var(--radius-lg);
  background: var(--card); transition: border-color var(--t-fast) var(--ease);
}
.account-stat:hover { border-color: var(--border-strong); text-decoration: none; }
.account-stat b { display: block; font-size: 24px; font-weight: 700; color: var(--primary-text); font-variant-numeric: tabular-nums; }
.account-stat span { display: block; margin-top: 3px; font-size: 12px; color: var(--muted-fg); }

.account-card {
  padding: 20px 22px; margin-bottom: 18px;
  border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--card);
}
.account-card-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 16px; }

/* Closing the account. Marked out by an edge rather than by a red panel: it
   belongs on this page, it just should not be reachable by accident. */
.account-danger { border-color: color-mix(in srgb, var(--destructive) 35%, var(--border)); }
.account-danger .account-card-head h2 { color: var(--destructive); }
.account-card-note { font-size: 13px; line-height: 1.65; color: var(--muted-fg); margin-bottom: 10px; }
.account-danger .auth-form { margin-top: 16px; }
.account-card-head h2 { font-size: 15px; font-weight: 650; color: var(--fg); }
.account-more { font-size: 12.5px; color: var(--primary-text); }
.account-empty { font-size: 13.5px; color: var(--muted-fg); line-height: 1.6; }
.account-empty a { color: var(--primary-text); text-decoration: underline; text-underline-offset: 2px; }
.account-note { font-size: 12.5px; color: var(--muted-fg); line-height: 1.6; margin-bottom: 16px; }

/* ── order rows and tracking ────────────────────────────────────────────── */
.account-orders { display: flex; flex-direction: column; }
.account-order {
  display: grid; grid-template-columns: 1.1fr 1fr auto auto; align-items: center; gap: 12px;
  padding: 12px 4px; border-bottom: 1px solid var(--border);
  transition: background-color var(--t-fast) var(--ease);
}
.account-order:last-child { border-bottom: 0; }
.account-order:hover { background: var(--muted); text-decoration: none; }
.account-order-no { font-size: 13px; font-weight: 600; color: var(--fg); }
.account-order-date { font-size: 12.5px; color: var(--muted-fg); }
.account-order-total { font-size: 13.5px; font-weight: 600; color: var(--primary-text); font-variant-numeric: tabular-nums; text-align: right; }
@media (max-width: 640px) {
  .account-order { grid-template-columns: 1fr auto; row-gap: 4px; }
  .account-order-date { order: 3; }
  .account-order-total { order: 4; }
}

.order-status {
  display: inline-flex; padding: 3px 9px; border-radius: 999px;
  font-size: 11px; font-weight: 600; white-space: nowrap;
  background: var(--muted); color: var(--muted-fg); border: 1px solid var(--border);
}
.order-status.is-confirmed { background: var(--color-blue-50); color: var(--color-blue-700); border-color: var(--color-blue-200); }
.order-status.is-delivered { background: var(--color-green-50); color: var(--color-green-700); border-color: var(--color-green-200); }
.order-status.is-cancelled { background: var(--color-red-50); color: var(--color-red-700); border-color: var(--color-red-200); }

.track-steps { display: flex; flex-direction: column; gap: 0; margin: 4px 0 18px; padding: 0; list-style: none; }
.track-step { position: relative; display: flex; align-items: center; gap: 12px; padding: 0 0 22px 0; }
.track-step:last-child { padding-bottom: 0; }
/* The connector between dots; the last step has nothing to join to. */
.track-step:not(:last-child)::before {
  content: ""; position: absolute; left: 8px; top: 18px; bottom: 4px;
  width: 2px; background: var(--border);
}
.track-step.is-done:not(:last-child)::before { background: var(--primary); }
.track-dot {
  width: 18px; height: 18px; flex-shrink: 0; border-radius: 999px;
  border: 2px solid var(--border-strong); background: var(--card); z-index: 1;
}
.track-step.is-done .track-dot { border-color: var(--primary); background: var(--primary); }
.track-step.is-cancelled .track-dot { border-color: var(--destructive); background: var(--destructive); }
.track-label { font-size: 13.5px; color: var(--muted-fg); }
.track-step.is-done .track-label { color: var(--fg); font-weight: 550; }

.track-detail { display: flex; flex-direction: column; gap: 9px; }
.track-detail p { display: flex; gap: 14px; font-size: 13.5px; align-items: baseline; }
.track-detail p > span { flex: 0 0 120px; color: var(--muted-fg); font-size: 12.5px; }
.track-detail p > strong { color: var(--fg); font-weight: 550; min-width: 0; }
.track-detail a { color: var(--primary-text); text-decoration: underline; text-underline-offset: 2px; }
.track-note { display: block !important; padding-top: 10px; border-top: 1px solid var(--border); color: var(--muted-fg); line-height: 1.6; }

.account-items { display: flex; flex-direction: column; }
.account-item { display: grid; grid-template-columns: 1fr auto auto; gap: 14px; align-items: center; padding: 11px 0; border-bottom: 1px solid var(--border); }
.account-item:last-child { border-bottom: 0; }
.account-item-name { font-size: 13.5px; color: var(--fg); display: flex; flex-direction: column; gap: 2px; }
.account-item-name small { font-size: 11.5px; color: var(--muted-fg); }
.account-item-qty { font-size: 12.5px; color: var(--muted-fg); font-variant-numeric: tabular-nums; }
.account-item-total { font-size: 13.5px; font-weight: 600; color: var(--fg); font-variant-numeric: tabular-nums; }

/* The portal cart is the one list whose lines can be removed in place, so it
   takes a fourth column. Order pages keep the three-column layout. */
#portal-cart-items .account-item { grid-template-columns: 1fr auto auto auto; }
.account-item-remove {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border: 0; border-radius: 8px;
  background: transparent; color: var(--muted-fg); cursor: pointer;
  transition: color .15s ease, background-color .15s ease;
}
.account-item-remove svg { width: 16px; height: 16px; }
.account-item-remove:hover { color: var(--destructive); background: color-mix(in srgb, var(--destructive) 12%, transparent); }
.account-item-remove:disabled { cursor: default; opacity: .5; }
/* Mid-request, so a second click cannot fire against a line already going. */
.account-item.is-removing { opacity: .5; }

/* A button for an action that cannot be undone — the admin review modal's
   Delete. Outlined rather than filled: destructive should read as deliberate,
   not as the obvious thing to click. */
.btn-danger {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  padding: 8px 16px; border-radius: var(--radius);
  border: 1px solid color-mix(in srgb, var(--destructive) 45%, transparent);
  background: transparent; color: var(--destructive);
  font-size: 13px; font-weight: 500; line-height: 1; cursor: pointer;
  transition: background-color .15s ease, border-color .15s ease, color .15s ease;
}
.btn-danger:hover {
  background: var(--destructive);
  border-color: var(--destructive);
  /* The fill is dark on the light palette and light on the dark one, so the
     label is taken from the page background rather than fixed to white. */
  color: var(--bg);
}
.btn-danger.btn-sm { padding: 6px 13px; font-size: 12.5px; }
.account-totals { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: 7px; }
.account-totals p { display: flex; justify-content: space-between; font-size: 13.5px; color: var(--muted-fg); }
.account-totals p strong { color: var(--fg); font-variant-numeric: tabular-nums; }
.account-totals p.is-total { font-size: 15px; padding-top: 7px; border-top: 1px solid var(--border); }
.account-totals p.is-total strong { color: var(--primary-text); font-weight: 700; }

/* ── wishlist ───────────────────────────────────────────────────────────── */
.wishlist-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px; }
.wishlist-card { border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--card); overflow: hidden; display: flex; flex-direction: column; }
.wishlist-media { display: block; aspect-ratio: 1/1; background: var(--photo-tile); overflow: hidden; }
.wishlist-media img { width: 100%; height: 100%; object-fit: contain; padding: 8px; transition: transform var(--t-slow) var(--ease); }
.wishlist-card:hover .wishlist-media img { transform: scale(1.05); }
.wishlist-info { padding: 13px; display: flex; flex-direction: column; gap: 7px; flex: 1; }
.wishlist-cat { font-size: 10.5px; text-transform: uppercase; letter-spacing: .08em; color: var(--muted-fg); }
.wishlist-info h3 { font-size: 13.5px; font-weight: 600; line-height: 1.35; }
.wishlist-info h3 a { color: var(--fg); }
.wishlist-info h3 a:hover { color: var(--primary-text); text-decoration: none; }
.wishlist-actions { margin-top: auto; display: flex; flex-direction: column; gap: 7px; }

/* ── custom requests ────────────────────────────────────────────────────── */
.req-list { display: flex; flex-direction: column; gap: 14px; }
.req-item { padding: 14px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--subtle); }
.req-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 6px; }
.req-head strong { font-size: 14px; color: var(--fg); }
.req-status { display: inline-flex; padding: 3px 9px; border-radius: 999px; font-size: 11px; font-weight: 600; background: var(--muted); color: var(--muted-fg); border: 1px solid var(--border); white-space: nowrap; }
.req-status.is-quoted { background: var(--accent-wash); color: var(--primary-text); border-color: var(--accent-edge); }
.req-status.is-closed { opacity: .75; }
.req-meta { font-size: 11.5px; color: var(--muted-fg); margin-bottom: 8px; }
.req-body { font-size: 13px; line-height: 1.6; color: var(--muted-fg); }
.req-reply { margin-top: 11px; padding-top: 11px; border-top: 1px solid var(--border); }
.req-reply span { display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .08em; color: var(--primary-text); margin-bottom: 5px; font-weight: 600; }
.req-reply p { font-size: 13px; line-height: 1.6; color: var(--fg); }

.pending-email { padding: 14px; border: 1px solid var(--accent-edge); border-radius: var(--radius); background: var(--accent-wash); }
.pending-email p { font-size: 13px; color: var(--fg); margin-bottom: 11px; }

/* ── checkout: optional account ─────────────────────────────────────────── */
.signup-block { padding: 16px; border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--subtle); }
.signup-toggle { display: flex; align-items: flex-start; gap: 11px; cursor: pointer; }
.signup-toggle input { margin-top: 3px; flex-shrink: 0; }
.signup-toggle strong { display: block; font-size: 13.5px; color: var(--fg); }
.signup-toggle small { display: block; margin-top: 2px; font-size: 12px; color: var(--muted-fg); line-height: 1.5; }
.signup-fields { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--border); }
.signup-fields[hidden] { display: none; }
.signup-hint { font-size: 12px; color: var(--muted-fg); line-height: 1.55; margin-bottom: 11px; }
.signup-code-row { display: flex; gap: 8px; align-items: flex-start; }
.signup-code-row .input-field { flex: 1; }
.signup-status { margin-top: 8px; font-size: 12px; line-height: 1.5; min-height: 1em; color: var(--muted-fg); }
.signup-status.is-ok { color: var(--success); }
.signup-status.is-error { color: var(--destructive); }
.signup-signedin { font-size: 13px; color: var(--muted-fg); padding: 12px 14px; border: 1px solid var(--accent-edge); background: var(--accent-wash); border-radius: var(--radius); }
.signup-signedin a { color: var(--primary-text); text-decoration: underline; text-underline-offset: 2px; }

/* ── admin: order tracking panel ────────────────────────────────────────── */
.tracking-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 14px; }


/* ── quick view on a phone ────────────────────────────────────────────────────
   The panel was laid out for a desktop split and simply stacked on a phone: a
   square image at full width, then the whole detail column beneath it, which
   ran off the bottom of the screen. The image is capped to a share of the
   viewport height, the inner column scrolls, and the whole thing is inset from
   all four edges so nothing touches the screen border. */
@media (max-width: 640px) {
  .modal-overlay { padding: 12px; }

  .modal-content {
    width: 100%;
    max-width: 100%;
    max-height: 92vh;
    max-height: 92svh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }
  /* The two columns become one scrolling stack. */
  .modal-content > .flex { flex-direction: column; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }

  /* The photo gets the full width of the panel.

     This box used to be a fixed 29svh strip — 449px wide but only 195 tall on a
     phone, a 2.3:1 letterbox. `contain` then sized the photo by its height, so a
     4:3 catalogue shot rendered 260px wide inside a 449px box: 58% of the space,
     with empty background either side. Giving the box the shape the photography
     actually has lets `contain` size by width instead, and the picture reaches
     both edges.

     4:3 because that is what two thirds of the catalogue is; a square shot still
     comes out far larger than it did. max-height keeps the stack inside the
     viewport on a short screen, which is why the ratio is a target and not a
     promise. */
  .qv-media-col { flex: 0 0 auto; }
  .qv-media {
    aspect-ratio: 4 / 3;
    height: auto;
    min-height: 0;
    max-height: 54svh;
  }
  .qv-media img { object-fit: contain; padding: 6px; }
  /* Hover zoom is meaningless without a pointer and only causes overflow. */
  .qv-media:hover img { transform: none; }

  #qv-gallery { padding: 8px 10px; gap: 6px; }
  .qv-thumb { width: 42px; height: 42px; }

  .modal-content > .flex > div:last-child { padding: 14px 16px 18px; }
  .qv-title { font-size: 17px; }
  .qv-qty-row { margin-top: 14px; }
  .qv-actions { margin-top: 16px; gap: 8px; }
  .qv-description-wrap { margin-top: 16px; padding-top: 14px; }
  .qv-description { font-size: 13px; }
  .qv-details-link { margin-top: 12px; }

  /* Clear of the notch and the home indicator. */
  .qv-close { top: 8px; right: 8px; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   26. SIGN-IN PAGES  (layouts/auth.php — customer and admin)
   ───────────────────────────────────────────────────────────────────────────
   A two-column split: artwork holding the left half at full height, the form
   on the right. No header, no footer — there is nothing to navigate to from a
   sign-in screen, and site chrome only competes with the one control that
   matters.

   The halves are equal on desktop. Below 900px the artwork would be a strip
   of nothing useful, so it collapses and the form takes the whole width.
   ═══════════════════════════════════════════════════════════════════════════ */

.auth-page {
  min-height: 100vh;
  min-height: 100svh;
}

.auth-split {
  display: grid;
  /* Equal halves, and `minmax(0, …)` so a wide image cannot push the form
     column narrower than its content. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  min-height: 100vh;
  min-height: 100svh;
}

/* ── the artwork half ────────────────────────────────────────────────────── */

.auth-visual {
  position: relative;
  overflow: hidden;
  background-color: var(--muted);
}

.auth-visual img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Portrait art, cropped to whatever the viewport gives it. Centring the
     focal point is what the recommended size in the admin note is about. */
  object-position: center;
  display: block;
}

/* No photograph uploaded: the globe, on nothing.
   
   The panel deliberately carries no background of its own — the artwork is a
   transparent render, so it sits on the page itself and the two halves read as
   one surface rather than as a picture in a frame. */
.auth-visual.is-globe {
  background: none;
}

/* The globe is drawn already cut off at its left edge, so it is anchored there
   and bleeds off the side of the page exactly as the artwork intends.

   Height-led with `width: auto`: the sphere stays whole and keeps its shape at
   every panel size, and whatever is left over on the right is page background,
   not a crop. Selector out-ranks the `.auth-visual img` photograph rule above,
   which would otherwise stretch this to fill and clip it. */
.auth-visual img.auth-visual-globe {
  position: absolute;
  top: 50%;
  left: 0;
  transform: translateY(-50%);
  width: auto;
  /* Over 100% so the sphere reads larger and carries the panel. The overflow
     goes off the top and bottom, which the panel clips — the globe already
     bleeds off the left, so bleeding off three edges is the same idea, not a
     new one. Vertically centred, so it is trimmed evenly rather than sliding
     off one end. */
  height: 122%;
  max-width: none;
  object-fit: contain;
  object-position: left center;
}

/* A hairline between the halves, so a pale photograph does not bleed into a
   pale form panel. Not drawn for the globe: there are no longer two surfaces to
   divide, and a line down the middle of one surface is just a line. */
.auth-visual:not(.is-globe)::after {
  content: "";
  position: absolute;
  inset: 0 0 0 auto;
  width: 1px;
  background: var(--border);
}

/* ── the form half ───────────────────────────────────────────────────────── */

.auth-panel {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 48px 40px;
  background-color: var(--bg);
  /* Long forms (registration) scroll inside their own half rather than
     scrolling the artwork off the top of the screen. */
  overflow-y: auto;
  max-height: 100vh;
  max-height: 100svh;

  /* The scrollbar this can produce is taken out of the content box on one side
     only, which pushed the form 13px left of centre on a tall signup form and
     left it centred on a short sign-in form — the panel appeared to shift as
     you moved between the two. Reserving the gutter on both edges keeps the
     form on the centre line whether a scrollbar is there or not. */
  scrollbar-gutter: stable both-edges;
}

.auth-panel-inner {
  width: 100%;
  max-width: 380px;
}

.auth-brand {
  display: inline-flex;
  align-items: center;
  margin-bottom: 32px;
}

.auth-brand img {
  width: auto;
  height: auto;
  max-width: 170px;
  max-height: 52px;
  object-fit: contain;
}

.auth-brand-text {
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -.02em;
  color: var(--fg);
}

.auth-title {
  font-size: 26px;
  line-height: 1.2;
  font-weight: 700;
  letter-spacing: -.022em;
  color: var(--fg);
}

.auth-sub {
  margin-top: 8px;
  font-size: 14px;
  line-height: 1.55;
  color: var(--muted-fg);
}

.auth-form {
  margin-top: 28px;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.auth-divider {
  margin: 26px 0;
  border: 0;
  border-top: 1px solid var(--border);
}

/* ── the reveal toggle on the password field ─────────────────────────────── */

.auth-password { position: relative; }
/* Room for the button so a long password never slides underneath it. */
.auth-password .input-field { padding-right: 42px; }

.auth-reveal {
  position: absolute;
  top: 50%;
  right: 6px;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--muted-fg);
  cursor: pointer;
  transition: color .15s ease, background-color .15s ease;
}
.auth-reveal svg { width: 17px; height: 17px; }
.auth-reveal:hover { color: var(--fg); background: var(--muted); }
.auth-reveal.is-on { color: var(--primary-text); }

/* ── the links under the form ────────────────────────────────────────────── */

.auth-links {
  margin-top: 22px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  font-size: 13.5px;
  color: var(--muted-fg);
}

.auth-links a {
  color: var(--primary-text);
  text-decoration: none;
  transition: color .15s ease;
}
.auth-links a:hover { color: var(--primary); text-decoration: underline; }

.auth-back {
  margin-top: 4px;
  color: var(--muted-fg) !important;
}
.auth-back:hover { color: var(--fg) !important; text-decoration: none !important; }

/* ── flash messages ──────────────────────────────────────────────────────── */

.auth-flash {
  margin-bottom: 20px;
  padding: 11px 14px;
  border: 1px solid transparent;
  border-radius: 10px;
  font-size: 13.5px;
  line-height: 1.5;
}
.auth-flash.is-error {
  background: color-mix(in srgb, var(--destructive) 12%, var(--bg));
  border-color: color-mix(in srgb, var(--destructive) 40%, transparent);
  color: var(--destructive);
}
.auth-flash.is-ok {
  background: color-mix(in srgb, var(--success) 12%, var(--bg));
  border-color: color-mix(in srgb, var(--success) 40%, transparent);
  color: var(--success);
}

/* ── one column ──────────────────────────────────────────────────────────── */

/* Below this the artwork is a sliver rather than a picture, and the form is
   what the visitor came for, so the artwork steps aside entirely. */
@media (max-width: 899px) {
  .auth-split { grid-template-columns: 1fr; }
  .auth-visual { display: none; }
  .auth-panel { max-height: none; padding: 40px 24px 56px; }
}

@media (max-width: 480px) {
  .auth-panel { padding: 32px 20px 48px; }
  .auth-brand { margin-bottom: 26px; }
  .auth-title { font-size: 23px; }
}

/* Short landscape screens (a phone turned sideways): the panel is already
   scrollable, but the generous vertical padding wastes what little there is. */
@media (max-height: 620px) {
  .auth-panel { align-items: flex-start; padding-top: 32px; padding-bottom: 40px; }
}

/* ── the sign-in screens, pinned to the viewport ─────────────────────────────
   `.auth-fit` is set by the layout for the customer and admin sign-in pages
   only. They are short enough to fit, so they are held at exactly one screen
   and the page itself cannot scroll.

   The panel keeps `overflow-y: auto` underneath as a last resort. At 1280x360 —
   a laptop window dragged down to a strip — nothing would make this content fit,
   and clipping the password field to honour "no scrolling" would leave a screen
   nobody can sign in on. Scrolling inside the panel is the failure mode; the
   spacing below is what stops it being reached at any ordinary size. */
.auth-page.auth-fit {
  height: 100vh;
  height: 100svh;
  overflow: hidden;
}
.auth-fit .auth-split {
  height: 100vh;
  height: 100svh;
  min-height: 0;
}
.auth-fit .auth-panel {
  max-height: 100vh;
  max-height: 100svh;
}

/* Below this the sign-in form needs its spacing back to fit one screen. The
   values are the same design, tightened — nothing is removed. */
@media (max-height: 700px) {
  .auth-fit .auth-panel { padding-top: 24px; padding-bottom: 24px; align-items: center; }
  .auth-fit .auth-brand { margin-bottom: 18px; }
  .auth-fit .auth-brand img { max-height: 40px; }
  .auth-fit .auth-title { font-size: 22px; }
  .auth-fit .auth-sub { margin-top: 6px; font-size: 13px; }
  .auth-fit .auth-form { margin-top: 18px; gap: 13px; }
  .auth-fit .auth-links { margin-top: 15px; gap: 7px; }
  .auth-fit .auth-flash { margin-bottom: 14px; padding: 9px 12px; }
}

/* Shorter still — a landscape phone. The subheading is the one thing here that
   can go: the heading above it already says what the screen is for. */
@media (max-height: 560px) {
  .auth-fit .auth-panel { padding-top: 16px; padding-bottom: 16px; }
  .auth-fit .auth-sub { display: none; }
  .auth-fit .auth-brand { margin-bottom: 14px; }
  .auth-fit .auth-form { margin-top: 14px; gap: 11px; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   27. WISHLIST BUTTON
   ───────────────────────────────────────────────────────────────────────────
   The mirror image of .quick-view-btn (which output.css pins bottom-right):
   same 40px pill, same reveal on card hover, same lift on its own hover — but
   anchored bottom-left, so the two controls balance across the tile.
   ═══════════════════════════════════════════════════════════════════════════ */

.wishlist-btn {
  position: absolute;
  left: 8px;
  bottom: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border: 0;
  border-radius: 9999px;
  background: color-mix(in srgb, #FFFFFF 90%, transparent);
  color: #1A1614;
  cursor: pointer;
  opacity: 0;
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, .1), 0 2px 4px -2px rgba(0, 0, 0, .1);
  transition: opacity .3s, scale .3s, background-color .3s, box-shadow .3s, color .2s;
  /* The card image sits inside an <a>; the heart must not follow it. */
  z-index: 2;
}

.wishlist-btn svg {
  width: 17px;
  height: 17px;
  /* Filled by the saved state below, so both states share one path. */
  fill: transparent;
  transition: fill .2s ease, stroke .2s ease;
}

.product-card:hover .wishlist-btn,
.wishlist-btn.always-visible,
.wishlist-btn:focus-visible {
  opacity: 1;
}

@media (hover: hover) {
  .wishlist-btn:hover {
    background: #FFFFFF;
    scale: 1.1;
    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -4px rgba(0, 0, 0, .1);
  }
}

/* Saved: the heart fills with the brand colour. The pill stays white so the
   control reads the same on any photograph behind it. */
.wishlist-btn.is-saved { color: var(--primary); }
.wishlist-btn.is-saved svg { fill: currentColor; }

/* Mid-request. Pointer events are off so a double click cannot race itself. */
.wishlist-btn.is-busy { opacity: .6; pointer-events: none; }

/* No hover to reveal it on a touch screen, so it is simply always there —
   matching what output.css already does for the quick-view button. */
@media (max-width: 767px) {
  .wishlist-btn { opacity: 1; }
}

/* On the product page it sits on the main photograph rather than a card, where
   the corners are further apart and the image is much larger. */
#main-image-container .wishlist-btn {
  left: auto;
  right: 12px;
  top: 12px;
  bottom: auto;
  width: 42px;
  height: 42px;
}
#main-image-container .wishlist-btn svg { width: 19px; height: 19px; }

/* ═══════════════════════════════════════════════════════════════════════════
   28. IMPERSONATION BAR
   ───────────────────────────────────────────────────────────────────────────
   Shown across the storefront while an administrator is signed in as a
   customer. Deliberately loud and deliberately fixed to the bottom: it must be
   impossible to scroll past and forget, and the bottom edge is the one place
   it does not fight the site header on either desktop or mobile.
   ═══════════════════════════════════════════════════════════════════════════ */

.impersonation-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 90;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 16px;
  padding-bottom: max(10px, env(safe-area-inset-bottom));
  background: #1A1614;
  color: #FFFFFF;
  font-size: 13px;
  line-height: 1.35;
  box-shadow: 0 -4px 14px rgba(0, 0, 0, .28);
}

/* Fixed colours, not palette tokens: this warning has to look the same on
   every theme, and it is chrome rather than page. */
.impersonation-bar strong { font-weight: 650; }
.impersonation-email { color: rgba(255, 255, 255, .62); margin-left: 6px; }

.impersonation-dot {
  width: 8px;
  height: 8px;
  border-radius: 999px;
  background: var(--warning, #E0A94A);
  flex-shrink: 0;
}

.impersonation-exit {
  border: 1px solid rgba(255, 255, 255, .28);
  background: transparent;
  color: #FFFFFF;
  font-size: 12.5px;
  font-weight: 500;
  padding: 5px 12px;
  border-radius: 8px;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color .15s ease, border-color .15s ease;
}
.impersonation-exit:hover { background: rgba(255, 255, 255, .12); border-color: rgba(255, 255, 255, .45); }

/* The mobile cart sheet also parks at the bottom of the screen; without this
   the two overlap and the sheet's controls sit under the bar. */
body:has(.impersonation-bar) .mc-cart-panel[data-sheet="true"] { bottom: 54px; }

@media (max-width: 560px) {
  .impersonation-bar { font-size: 12px; padding-inline: 12px; gap: 8px; }
  .impersonation-email { display: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   29. PASSWORD FIELDS
   ───────────────────────────────────────────────────────────────────────────
   The reveal button and the strength meter, both applied by password-ui.js to
   every password field on the site rather than being written into each form.
   ═══════════════════════════════════════════════════════════════════════════ */

/* The wrapper the script puts around each field. display:block so it takes the
   field's place in whatever grid or flex row it was sitting in. */
.pw-field { position: relative; display: block; }

/* Room for the button, so a long password never slides underneath it. */
.pw-field > .input-field { padding-right: 42px; }

.pw-reveal {
  position: absolute;
  top: 50%;
  right: 6px;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--muted-fg);
  cursor: pointer;
  transition: color .15s ease, background-color .15s ease;
}
.pw-reveal svg { width: 17px; height: 17px; }
.pw-reveal:hover { color: var(--fg); background: var(--muted); }
.pw-reveal.is-on { color: var(--primary-text); }

/* ── strength meter ──────────────────────────────────────────────────────── */

.pw-strength { margin-top: 8px; }

.pw-strength-track {
  height: 4px;
  border-radius: 999px;
  background: var(--muted);
  overflow: hidden;
}

.pw-strength-fill {
  display: block;
  height: 100%;
  width: 0;
  border-radius: 999px;
  background: var(--destructive);
  transition: width .2s ease, background-color .2s ease;
}

.pw-strength-label {
  margin-top: 5px;
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--muted-fg);
}

/* Weak stays red, medium takes the warning amber, strong the success green.
   The label repeats the reading in words, so the bar is never the only thing
   carrying it — colour alone is not a message everyone can read. */
.pw-strength[data-level="0"] .pw-strength-fill { background: var(--destructive); }
.pw-strength[data-level="1"] .pw-strength-fill { background: var(--warning); }
.pw-strength[data-level="2"] .pw-strength-fill { background: var(--success); }

/* ── the code field on the verification screen ───────────────────────────── */

.code-input {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
  font-size: 17px;
  letter-spacing: .28em;
  text-transform: uppercase;
  text-align: center;
}
/* The tracking above would otherwise push the placeholder off-centre. */
.code-input::placeholder { letter-spacing: .06em; text-transform: none; font-size: 14px; }

/* Links that are really buttons, because the action they run sends mail or
   changes server state and must not be reachable by a prefetch. */
.auth-inline-form { display: inline; }
.auth-linkbutton {
  border: 0;
  background: transparent;
  padding: 0;
  font: inherit;
  color: var(--primary-text);
  cursor: pointer;
  text-decoration: none;
}
.auth-linkbutton:hover { color: var(--primary); text-decoration: underline; }
.auth-resend { display: block; }
/* break-all split the address mid-character ("example.invali d"); anywhere lets
   the browser pick a sensible point and only breaks a word when it has to. */
.auth-email { color: var(--fg); overflow-wrap: anywhere; }

/* ═══════════════════════════════════════════════════════════════════════════
   30. HEADER ACCOUNT AVATAR + DROPDOWN
   ───────────────────────────────────────────────────────────────────────────
   Replaces the person icon once someone is signed in: their initials in a
   brand-coloured circle, with the portal menu underneath on hover.

   The circle takes --primary, so it follows whichever palette the shop is
   running rather than carrying a colour of its own.
   ═══════════════════════════════════════════════════════════════════════════ */

.account-menu {
  position: relative;
  display: inline-flex;
  align-items: center;
}

.account-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 999px;
  background: var(--primary);
  color: var(--primary-fg);
  font-size: 11.5px;
  font-weight: 650;
  letter-spacing: .02em;
  line-height: 1;
  flex-shrink: 0;
  text-decoration: none;
  transition: background-color var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
/* The header's own link rules would otherwise repaint this on hover. */
#site-header .account-chip,
#site-header .account-chip:hover { color: var(--primary-fg); text-decoration: none; }
.account-chip:hover { background: var(--primary-hover); transform: translateY(-1px); }
.account-chip.is-static { pointer-events: none; transform: none; }

/* ── the dropdown ────────────────────────────────────────────────────────── */

.account-dropdown {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  z-index: 60;
  width: 244px;
  /* Never wider than the screen it opens on. The chip sits near the right edge,
     so the panel grows leftward and this is what stops it running off the left
     on a narrow phone. */
  max-width: calc(100vw - 24px);
  padding: 6px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--popover);
  box-shadow: var(--e3, 0 12px 28px rgba(0, 0, 0, .28));
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity var(--t-fast) var(--ease),
              transform var(--t-fast) var(--ease),
              visibility var(--t-fast);
}

/* A gap between the avatar and the panel would close the menu as the pointer
   crossed it; this bridges it without being visible. */
.account-dropdown::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: -12px;
  height: 12px;
}

/* Hover only where there is a pointer that can hover. On a touch screen the
   first tap would otherwise both open and follow the link. */
@media (hover: hover) {
  .account-menu:hover .account-dropdown {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
  }
}
.account-menu:focus-within .account-dropdown,
.account-menu.is-open .account-dropdown {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.account-dropdown-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 8px 10px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--border);
}
.account-dropdown-who { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.account-dropdown-who strong {
  font-size: 13px; font-weight: 600; color: var(--fg);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.account-dropdown-who span {
  font-size: 11.5px; color: var(--muted-fg);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.account-dropdown-link {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 9px;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--fg);
  font-size: 13px;
  font-family: inherit;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.account-dropdown-link svg { width: 16px; height: 16px; flex-shrink: 0; color: var(--muted-fg); }
.account-dropdown-link:hover { background: var(--muted); text-decoration: none; }
.account-dropdown-link:hover svg { color: var(--primary-text); }
/* The header paints its own links white; these sit on a popover, not on chrome. */
#site-header .account-dropdown-link { color: var(--fg); }
#site-header .account-dropdown-link:hover { color: var(--fg); }

.account-dropdown-foot { margin-top: 4px; padding-top: 4px; border-top: 1px solid var(--border); }
.account-dropdown-link.is-signout { color: var(--destructive); }
#site-header .account-dropdown-link.is-signout,
#site-header .account-dropdown-link.is-signout:hover { color: var(--destructive); }
.account-dropdown-link.is-signout svg { color: var(--destructive); }

/* Visually hidden but still read aloud — the circle shows initials, which on
   their own tell a screen reader nothing. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* ── the profile page ────────────────────────────────────────────────────── */

.profile-head { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.profile-avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 56px; height: 56px; border-radius: 999px; flex-shrink: 0;
  background: var(--primary); color: var(--primary-fg);
  font-size: 18px; font-weight: 650; letter-spacing: .02em;
}
.profile-identity { min-width: 0; flex: 1; }
.profile-identity h2 { font-size: 18px; font-weight: 650; color: var(--fg); }
.profile-identity p { font-size: 13px; color: var(--muted-fg); margin-top: 2px; word-break: break-word; }
.profile-since { font-size: 12px !important; }
.profile-verified { color: var(--success); }
.profile-edit { flex-shrink: 0; }

.profile-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 12px;
  margin: 16px 0;
}
.profile-stat { text-align: left; padding: 14px 16px; }
.profile-stat b { display: block; font-size: 20px; font-weight: 700; color: var(--fg); font-variant-numeric: tabular-nums; }
.profile-stat span { display: block; font-size: 11.5px; color: var(--muted-fg); margin-top: 2px; }

/* A description list rather than a table: these are label/value pairs, and on a
   phone they need to stack, which a table fights. */
.profile-details { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px 24px; }
.profile-details > div { min-width: 0; }
.profile-details dt { font-size: 11px; text-transform: uppercase; letter-spacing: .08em; color: var(--muted-fg); }
.profile-details dd { font-size: 13.5px; color: var(--fg); margin-top: 3px; word-break: break-word; }
.profile-empty { color: var(--muted-fg); font-style: italic; }

.account-card-link { font-size: 12.5px; color: var(--primary-text); text-decoration: none; }
.account-card-link:hover { color: var(--primary); text-decoration: underline; }

@media (max-width: 560px) {
  .profile-head { gap: 12px; }
  .profile-avatar { width: 48px; height: 48px; font-size: 16px; }
  .profile-edit { width: 100%; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   31. VERIFICATION CODE UI
   ───────────────────────────────────────────────────────────────────────────
   One box per character, the expiry countdown, and the resend control. Built by
   verification.js; the markup ships a plain field as the no-script fallback.
   ═══════════════════════════════════════════════════════════════════════════ */

.otp {
  display: grid;
  /* Equal columns rather than fixed widths, so eight boxes fit whatever the
     panel is: they shrink together instead of overflowing the form. */
  grid-template-columns: repeat(var(--otp-count, 8), minmax(0, 1fr));
  gap: 6px;
  margin-top: 4px;
}

.otp-cell {
  width: 100%;
  min-width: 0;
  aspect-ratio: 4 / 5;
  padding: 0;
  border: 1px solid var(--input);
  border-radius: var(--radius);
  background: var(--card);
  color: var(--fg);
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
  font-size: 18px;
  font-weight: 600;
  text-align: center;
  text-transform: uppercase;
  caret-color: var(--primary);
  transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}

.otp-cell:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--accent-wash);
}

/* The cells carry no placeholder, so :not(:placeholder-shown) and :valid both
   match every one of them — that pair claimed to style a filled box and styled
   all eight. The one state worth marking is a complete code, which the script
   sets a class for. */
.otp.is-complete .otp-cell { border-color: var(--primary); }

.otp-countdown {
  margin-top: 10px;
  font-size: 12px;
  color: var(--muted-fg);
  font-variant-numeric: tabular-nums;
}
.otp-countdown.is-urgent { color: var(--warning); }
.otp-countdown.is-expired { color: var(--destructive); }

.otp-resend { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; }
.otp-resend > span { color: var(--muted-fg); }

/* A resend on cooldown is still readable — it is showing a countdown, which is
   information, not a dead control. */
.auth-linkbutton:disabled {
  color: var(--muted-fg);
  cursor: default;
  text-decoration: none;
  font-variant-numeric: tabular-nums;
}
.auth-linkbutton:disabled:hover { color: var(--muted-fg); text-decoration: none; }

/* Eight boxes on a narrow phone need to give up some width. */
@media (max-width: 420px) {
  .otp { gap: 4px; }
  .otp-cell { font-size: 15px; }
}

/* ── the checkout's code field, with its send button inside ──────────────── */

.code-field { position: relative; display: block; }
/* Room for the button, so a typed code never runs underneath it. */
.code-field .input-field { padding-right: 108px; }

.code-field-btn {
  position: absolute;
  top: 50%;
  right: 5px;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  padding: 0 12px;
  height: 34px;
  border: 0;
  border-radius: var(--radius-sm);
  background: var(--primary);
  color: var(--primary-fg);
  font-size: 12.5px;
  font-weight: 500;
  font-family: inherit;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--t-fast) var(--ease);
}
.code-field-btn:hover:not(:disabled) { background: var(--primary-hover); color: var(--primary-fg); }
.code-field-btn:disabled { opacity: .6; cursor: default; }

/* Countdown on the left, resend on the right, stacking when there is no room. */
.code-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 6px 14px;
  margin-top: 8px;
}
.code-meta .otp-countdown { margin-top: 0; }
.code-resend { font-size: 12px; color: var(--muted-fg); display: inline-flex; gap: 5px; align-items: baseline; }
.code-resend .auth-linkbutton { font-size: 12px; }

/* The asterisk on a field the form will now refuse to submit empty. */
.label-required { color: var(--destructive); font-weight: 600; margin-left: 1px; }

/* ═══════════════════════════════════════════════════════════════════════════
   32. THE PORTAL ON A PHONE
   ───────────────────────────────────────────────────────────────────────────
   On a wide screen the portal nav is a sidebar beside the content. Below 860px
   the grid collapses to one column, which put the whole nav card — identity,
   six links and sign out, 410px of it — directly above the content on every
   page. Reaching "Your orders" meant scrolling past 529px of navigation you had
   already used to get there.

   It becomes a compact identity row and a single scrolling strip of tabs, so
   the page content starts near the top where it belongs, and the strip still
   holds every destination.
   ═══════════════════════════════════════════════════════════════════════════ */

@media (max-width: 860px) {
  .account-nav {
    padding: 12px 0 0;
    border: 0;
    border-radius: 0;
    background: transparent;
    /* A grid item defaults to min-width:auto, which refuses to shrink below its
       content — the strip pushed the page to 884px instead of scrolling inside
       it. Both the item and the scroller itself have to be allowed to shrink. */
    min-width: 0;
    /* Full-bleed, so the strip can run to both screen edges and read as
       something that scrolls rather than a box that happens to be cut off. */
    margin-inline: calc(var(--site-pad, 1rem) * -1);
  }

  .account-user {
    padding: 0 var(--site-pad, 1rem) 12px;
    border-bottom: 0;
    gap: 10px;
  }
  .account-avatar { width: 36px; height: 36px; font-size: 14px; }

  .account-links {
    flex-direction: row;
    min-width: 0;
    gap: 6px;
    padding: 4px var(--site-pad, 1rem) 10px;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x proximity;
    /* The strip is its own scroll region; the thin bar it would otherwise show
       sits under the tabs and adds nothing. */
    scrollbar-width: none;
    border-bottom: 1px solid var(--border);
  }
  .account-links::-webkit-scrollbar { display: none; }

  .account-link {
    width: auto;
    flex: 0 0 auto;
    scroll-snap-align: start;
    padding: 9px 13px;
    min-height: 40px;              /* a comfortable target on glass */
    border: 1px solid var(--border);
    border-radius: 999px;
    white-space: nowrap;
    background: var(--card);
  }
  .account-link.is-active {
    border-color: var(--primary);
    background: var(--accent-wash);
  }

  /* Sign out belongs with the rest rather than under its own rule, which on a
     horizontal strip would have been a vertical line in the middle of nowhere. */
  .account-signout {
    margin: 0;
    padding: 0;
    border-top: 0;
    /* width:100% from the desktop rule stretched this to 1201px inside the
       flex row; on the strip it is a chip that sizes to its label. */
    width: auto;
    flex: 0 0 auto;
  }
  .account-signout .account-link {
    min-height: 40px;
    border-color: color-mix(in srgb, var(--destructive) 30%, var(--border));
    color: var(--destructive);
  }

  .account-shell { gap: 16px; }
  .account-title { font-size: 21px; }
  .account-lead { font-size: 13.5px; margin-bottom: 16px; }
}

/* --site-pad is the container's own horizontal padding, declared in output.css
   and stepped per breakpoint. Bleeding by exactly that keeps the strip flush to
   the screen edge at every width without a second variable to keep in step. */

/* ── the account menu on a phone ─────────────────────────────────────────── */

@media (max-width: 1023px) {
  /* Room for a thumb, and enough of a target that it is not a lottery. */
  .account-chip { width: 34px; height: 34px; font-size: 12px; }

  /* Anchored to the screen, not to the avatar.

     The avatar is the leftmost icon in the header row, so a panel aligned to
     its right edge opened leftward and hung 19px off the side of the screen.
     Fixed to the viewport instead: it sits under the header, inside the page
     gutter, and cannot leave the screen at any width. */
  .account-dropdown {
    position: fixed;
    top: calc(var(--header-h, 64px) + 8px);
    right: var(--site-pad, 1rem);
    left: auto;
    width: 260px;
    max-width: calc(100vw - (var(--site-pad, 1rem) * 2));
    padding: 8px;
    /* It no longer moves with the chip, so it must not slide either. */
    transform: none;
  }
  .account-menu.is-open .account-dropdown,
  .account-menu:focus-within .account-dropdown { transform: none; }
  /* Rows sized for a fingertip rather than a cursor. */
  .account-dropdown-link { padding: 11px 10px; font-size: 14px; min-height: 44px; }
  .account-dropdown-link svg { width: 18px; height: 18px; }
  .account-dropdown-head { padding: 8px 8px 12px; }

  /* The bridge that keeps a hover alive across the gap is pointless on touch
     and would swallow taps just below the avatar. */
  .account-dropdown::before { content: none; }
}

/* TikTok is the one header icon that also lives in the footer and the drawer,
   so it is the one that gives way when the row gets tight. */
@media (max-width: 400px) {
  #site-header .header-social[aria-label="TikTok"] { display: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   33. TARGETS FOR A FINGERTIP
   ───────────────────────────────────────────────────────────────────────────
   A pointer lands where you put it; a fingertip covers about 9mm and lands
   roughly where you aimed. The audit found controls at 30px and 19px across the
   portal — reachable with a mouse, a coin-toss on glass.

   The comma is an OR: a touch device at any width, or a narrow window on a
   desktop, where the layout is the phone layout anyway. Density on a pointer
   desktop is untouched.
   ═══════════════════════════════════════════════════════════════════════════ */

@media (hover: none), (max-width: 860px) {

  /* Password reveal — 30px, and there are five of them on the settings page. */
  .pw-reveal { width: 40px; height: 40px; right: 4px; }
  .pw-reveal svg { width: 19px; height: 19px; }
  .pw-field > .input-field { padding-right: 50px; }

  /* Removing a line from the saved cart. */
  .account-item-remove { width: 40px; height: 40px; }
  .account-item-remove svg { width: 18px; height: 18px; }

  /* Small buttons stay visually small but stop being hard to hit. */
  .btn-sm,
  .btn-danger.btn-sm,
  .btn-outline.btn-sm { min-height: 40px; }
  .btn-danger { min-height: 40px; }

  /* Quantity steppers — the one control people press repeatedly. */
  .qty-stepper button { width: 40px; height: 40px; }
  .qty-stepper input { height: 40px; }

  /* Text links that act as buttons: 19px tall is a line of text, not a target. */
  .account-more,
  .account-card-link {
    display: inline-flex;
    align-items: center;
    min-height: 36px;
  }

  /* Footer links sat at 20px in a stacked column. */
  .footer-link {
    display: flex;
    align-items: center;
    min-height: 38px;
  }

  /* The heart and quick-view on a product tile. */
  .wishlist-btn, .quick-view-btn { width: 42px; height: 42px; }

  /* Closing a panel is the one control someone reaches for when they already
     want out; it should not be the smallest thing on the screen. */
  .qv-close, .mc-cart-close { width: 40px; height: 40px; }

  /* The way out of the quick view. */
  .qv-details-link { display: inline-flex; align-items: center; min-height: 38px; }

  /* The avatar. 34px was the last control still under the line. */
  .account-chip { width: 38px; height: 38px; font-size: 12.5px; }

  /* Removing a line from the mini-cart. */
  .mc-cart-remove { width: 40px; height: 40px; }

  /* A saved item's title is the label people aim at; as a bare line of text it
     was a 16px-tall target. The card image above it is also a link, so this was
     never a dead end — it was just the harder of the two ways in. */
  .wishlist-info h3 a { display: flex; align-items: center; min-height: 36px; }

  /* The two things a product tile exists to be pressed for, at 30px and 34px on
     a grid where two tiles share the screen width. */
  .product-card .mc-cta { min-height: 42px; }
  .order-now-btn { min-height: 42px; display: flex; align-items: center; justify-content: center; }
}
