/* ==========================================================================
   Royal Rider — theme layer
   Loaded AFTER bootstrap.min.css so these rules win.
   Palette: white / near-black / gold. Gold is an accent used with restraint.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Design tokens
   -------------------------------------------------------------------------- */
:root {
    --rr-bg: #ffffff;
    --rr-surface: #ffffff;
    --rr-surface-2: #f7f7f5;
    --rr-ink: #111111;
    --rr-black: #0E0E0E;
    /* Gold, measured on white. Pick by role — the three are not interchangeable.
       --rr-gold      2.42:1  fills, borders, decoration. NEVER text on white.
       --rr-gold-deep 3.82:1  large text only (>=24px, or >=18.7px bold), icon
                              glyphs (1.4.11 needs 3:1), focus rings, borders.
       --rr-gold-text 5.17:1  the only gold valid for normal-size text on white.
       On the near-black bands (#0E0E0E) --rr-gold reads 7.99:1, so the footer,
       hero and admin sidebar deliberately keep it as text. */
    --rr-gold: #C9A227; /* primary accent: CTA fills, active states */
    --rr-gold-deep: #A67C00; /* large text, icons, focus ring, hover borders */
    --rr-gold-text: #8A6800; /* normal-size gold text on white / surface-2 */
    --rr-gold-soft: #EFE3BE; /* tints, borders, badge backgrounds */
    --rr-muted: #6B7280; /* 4.83:1 on white and on surface-2 — passes */
    /* --rr-muted is measured against white. On the gold-soft tint (#EFE3BE) it drops to
       3.78:1 and fails AA for normal text, which is where the struck-through MRP sits on the
       product page. Use this instead for muted text on a gold-soft panel: 5.00:1. */
    --rr-muted-on-tint: #5A5F6B;
    --rr-outline: #E3E1D6;
    --rr-radius: .5rem; /* 8px — every corner in the system */
    --rr-font-head: "Barlow Condensed", system-ui, sans-serif;
    --rr-font-body: "Inter", system-ui, sans-serif;

    /* --- Type scale ------------------------------------------------------
       The storefront used to carry 26 distinct hard-coded px sizes with no
       relationship between them — 15px and 16px both existed, so did 17/18/19
       and 32/34/36/38 — which is a pile of one-off decisions, not a system.
       They are consolidated here into 15 steps and every storefront TEXT rule
       now resolves its size through one of them, so the whole site is
       retunable from this block.

       The pass also took the scale down. The old sizes were set at mockup
       scale (18px body copy, 20px card titles, 24px card prices, 40px detail
       price) which reads as large-print on a 1280px page and pushed the
       product grid below the fold. Roughly:

         old  10  11  12  13  14  15  16  17  18  19  20  22  24  26  28
         new  10  11  11  12  13  14  14  15  16  16  18  20  22  22  25
         old  30  32  34  36  38  40  42  48  64
         new  25  28  28  33  33  33  33  40  50

       Two deliberate non-shrinks:
         • 10px and 11px are the uppercase micro-label floor (eyebrows, SKU,
           legal, size chips). Letter-spaced caps already read small; taking
           them lower costs legibility and buys no density.
         • Icon glyph sizes are NOT on this scale. A .material-symbols-outlined
           font-size is geometry — it is paired with width/height on the same
           rule and answers to WCAG 1.4.11's 3:1 non-text contrast and to touch
           targets, not to reading comfort. Those stay in px on purpose. */
    --rr-fs-3xs: 10px; /* micro caps: quick-size, GST note, meta dt */
    --rr-fs-2xs: 11px; /* eyebrows, legal, size chips, labels */
    --rr-fs-xs: 12px; /* dense meta, counts, hints, form help */
    --rr-fs-sm: 13px; /* nav links, buttons, small UI text */
    --rr-fs-md: 14px; /* default UI text, inputs, table cells */
    --rr-fs-lg: 15px; /* emphasised UI text, menu links */
    --rr-fs-xl: 16px; /* body copy, ledes, prose */
    --rr-fs-2xl: 18px; /* card titles, value titles */
    --rr-fs-3xl: 20px; /* panel heads, drawer titles, tabs */
    --rr-fs-4xl: 22px; /* prices, sub-headings */
    --rr-fs-5xl: 25px; /* small display */
    --rr-fs-6xl: 28px; /* display */
    --rr-fs-7xl: 33px; /* large display, detail price */
    --rr-fs-8xl: 40px; /* empty-state and confirmation marks */
    --rr-fs-9xl: 50px; /* the one oversized decorative glyph */

    /* --- Tracking ---------------------------------------------------------
       Letter-spacing was the loosest thing in the system: 17 distinct values
       spread across ~170 rules, 83 of them at .1em or wider. Wide tracking on
       a small uppercase eyebrow reads as expensive; the same tracking on nav,
       buttons and body copy reads as stretched, scans badly, and forces every
       block taller than it needs to be. Four values now, and only four.

       Anything above --rr-ls-caps is a mistake, not a style choice. */
    --rr-ls-caps: .12em; /* uppercase eyebrows/labels at 11px and below ONLY */
    --rr-ls-wide: .06em; /* uppercase section titles, chips, small caps UI */
    --rr-ls-ui: .02em; /* buttons, nav links, table headers */
    --rr-ls-none: 0; /* body copy, product names, prices, inputs */
    --rr-ls-head: -.01em; /* Barlow Condensed display — condensed wants none */

    /* --- Leading ----------------------------------------------------------
       Body sat at 1.6-1.75. Paired with the tracking above it was the second
       half of why pages ran long. 1.55 is the readable floor for Inter at
       these sizes; display type is set solid. */
    --rr-lh-head: 1.1; /* Barlow Condensed display and headings */
    --rr-lh-tight: 1.3; /* card titles, multi-line UI labels */
    --rr-lh-body: 1.55; /* paragraphs, ledes, prose */
    --rr-lh-ui: 1.45; /* dense UI text, table cells, help text */

    /* --- Spacing scale ----------------------------------------------------
       The type pass consolidated 26 font sizes into 15 steps but left spacing
       alone, so the page still carried ~35 distinct hard-coded gap values and
       the bands stayed at mockup scale. That is why the site read as airy even
       after the text shrank: a 64px band around 14px copy is not generous, it
       is content adrift.

       Eight steps, geometric-ish, every block-level gap resolves through one.
       Anything not on this scale is either a hairline (1-2px), an optical
       nudge inside a component, or a mistake. */
    --rr-sp-1: 4px;
    --rr-sp-2: 8px;
    --rr-sp-3: 12px;
    --rr-sp-4: 16px;
    --rr-sp-5: 20px;
    --rr-sp-6: 24px;
    --rr-sp-7: 32px;
    --rr-sp-8: 40px;

    /* --- Layout rhythm ----------------------------------------------------
       Retuned down one step from the mockups. The mockups were drawn at 18px
       body copy where 80px bands are proportionate; at the current 14-16px
       scale the same bands leave a 1280px page ~27% vertical padding and push
       the product grid below the fold on a laptop.

       Density is a ratio, not an absolute — these move with the type scale.
       Retune the whole site from this block; do not shave individual rules. */
    --rr-container-max: 1280px;
    --rr-container-pad: 28px;
    --rr-container-pad-mobile: 16px;
    --rr-section-gap: 44px; /* desktop band padding (was 64) */
    --rr-section-gap-sm: 30px; /* mobile band padding (was 44) */
    --rr-gutter: 20px; /* grid/rail gutter (was 24) */
    --rr-head-gap: 20px; /* section head -> its content (was 28) */
    /* Card / panel interiors. The 2rem-3rem tier was the second-biggest
       consumer of height after the bands. */
    --rr-pad-panel: 20px;
    --rr-pad-panel-lg: 28px;
    --rr-header-h: 64px; /* sticky header height — sticky panels offset below it */
    /* Bootstrap overrides */
    --bs-body-font-family: var(--rr-font-body);
    --bs-body-color: var(--rr-ink);
    --bs-body-bg: var(--rr-bg);
    --bs-primary: var(--rr-black);
    --bs-primary-rgb: 14, 14, 14;
    --bs-border-color: var(--rr-outline);
    --bs-border-radius: var(--rr-radius);
    --bs-border-radius-sm: var(--rr-radius);
    --bs-border-radius-lg: var(--rr-radius);
    --bs-link-color: var(--rr-black);
    --bs-link-hover-color: var(--rr-gold-text);
    /* Bootstrap ships <code> in #d63384 — a pink that belongs to no part of this palette and
       reads as the mockup red the theme replaced. The admin hints use <code> for phone
       numbers and URLs, so it was visible on the Settings screen. */
    --bs-code-color: var(--rr-gold-text);
}

/* --------------------------------------------------------------------------
   2. Base
   -------------------------------------------------------------------------- */
body {
    background-color: var(--rr-bg);
    color: var(--rr-ink);
    font-family: var(--rr-font-body);
    /* Bootstrap's 16px default was the site's real base size: anything not
       carrying an rr-* class — plain <p>, <li>, <td>, form help — inherited it.
       Dropping it to the scale's --rr-fs-md is what makes the pass reach that
       text too, rather than only the classed components. The admin sets its own
       size on .rr-admin (a body class, so it outranks this) and is unaffected. */
    font-size: var(--rr-fs-md);
    -webkit-font-smoothing: antialiased;
    /* room for the sticky mobile bottom nav */
    padding-bottom: 0;
}

h1, h2, h3, h4, h5, h6,
.rr-head {
    font-family: var(--rr-font-head);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: var(--rr-ls-ui);
    color: var(--rr-black);
    line-height: 1.2;
}

.material-symbols-outlined {
    font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
    line-height: 1;
    user-select: none;
}

.material-symbols-outlined.rr-icon-filled {
    font-variation-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 24;
}

/* Visible focus everywhere — never remove the affordance. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
[tabindex]:focus-visible {
    outline: 2px solid var(--rr-gold-deep);
    outline-offset: 2px;
    border-radius: 2px;
}

::selection {
    background: var(--rr-gold-soft);
    color: var(--rr-black);
}

/* Skip to content — first tab stop, off-screen until focused (WCAG 2.4.1). */
.rr-skip-link {
    position: absolute;
    left: 1rem;
    top: -100px;
    z-index: 2000;
    display: inline-block;
    background: var(--rr-black);
    color: #ffffff;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-sm);
    text-decoration: none;
    padding: .625rem 1rem;
    border-radius: var(--rr-radius);
    transition: top .15s ease;
}

    .rr-skip-link:focus {
        top: 1rem;
        color: #ffffff;
    }

/* The skip link lands focus on <main tabindex="-1">; the ring on a whole page region is
   noise, and the heading it jumps to is the real confirmation. */
main:focus {
    outline: none;
}

/* Honour the OS setting everywhere rather than component by component: the hover zooms on
   product cards and category tiles are the main offenders (WCAG 2.3.3). */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
        /* The DELAYS have to go too, and this was a real defect rather than tidiness.
           Both motion layers stagger entrances by delaying them (section 34 out to .52s,
           section 35 further), and every one of those animations is fill-mode `backwards` —
           which holds the element at its `from` keyframe, i.e. opacity 0, for the whole
           delay. Zeroing only the duration therefore did not remove the animation for a
           reduced-motion visitor; it turned it into half a second of invisible content
           followed by a snap. Content appearing late is exactly what 2.3.3 is protecting
           against, so the delay is neutralised at the same time as the duration. */
        animation-delay: .01ms !important;
        transition-delay: .01ms !important;
        scroll-behavior: auto !important;
    }

    .rr-card:hover .rr-card__media img,
    .rr-card:focus-visible .rr-card__media img,
    .rr-cat-tile:hover .rr-cat-tile__media {
        transform: none;
    }
}

/* Borderless inputs inside a styled shell (header search, search overlay, sort dropdown,
   quantity stepper) each set outline:none to stop a ring appearing inside their own frame.
   That left them with NO visible focus at all, so the frame itself carries it — the same
   gold ring .form-control:focus uses, applied one level up.
   :has() keeps the stepper's ring tied to its input, not to its +/- buttons. */
.rr-search:focus-within,
.rr-search-bar:focus-within,
.rr-sort__shell:focus-within,
.rr-qty:has(input:focus-visible) {
    border-color: var(--rr-gold);
    box-shadow: 0 0 0 3px rgba(201, 162, 39, .3);
}

/* .rr-search-bar is a bottom rule, not a box — thicken it rather than ringing it. */
.rr-search-bar:focus-within {
    border-bottom-color: var(--rr-gold-deep);
    box-shadow: 0 3px 0 -1px rgba(201, 162, 39, .3);
}

/* Fallback for engines without :has() — an outline on the control itself is ugly next to
   the frame, but it is always better than no focus indicator. */
@supports not selector(:has(*)) {
    .rr-qty input:focus-visible {
        outline: 2px solid var(--rr-gold-deep);
        outline-offset: -2px;
    }
}

/* A13 — no horizontal scroll at 360px.
   .rr-container pads 16px a side on mobile, but a Bootstrap .row with g-5 pulls -24px a side,
   so every g-5 row sat 8px past both edges of the viewport: the home page, product detail,
   About and all three policy pages scrolled sideways. Capping the horizontal gutter at 1.5rem
   below 576px brings the pull inside the padding. Rows already at g-4 or tighter are
   unaffected, and --bs-gutter-y is untouched, so vertical rhythm does not change. */
@media (max-width: 575.98px) {
    .row {
        --bs-gutter-x: 1.5rem;
    }
}

/* --------------------------------------------------------------------------
   3. Layout primitives
   -------------------------------------------------------------------------- */
.rr-container {
    width: 100%;
    max-width: calc(var(--rr-container-max) + (var(--rr-container-pad) * 2));
    margin-inline: auto;
    padding-inline: var(--rr-container-pad-mobile);
}

@media (min-width: 768px) {
    .rr-container {
        padding-inline: var(--rr-container-pad);
    }
}

.rr-section {
    padding-block: var(--rr-section-gap-sm);
}

@media (min-width: 768px) {
    .rr-section {
        padding-block: var(--rr-section-gap);
    }
}

/* Listing pages (shop, category, search) open with a breadcrumb, not with content,
   and 80px of white above a 11px breadcrumb is a section gap doing a job nobody
   asked for. A listing page's entire purpose is the grid, so the top is tightened
   to the breadcrumb's own rhythm and the full gap is kept at the BOTTOM, where it
   separates the pagination from the footer. */
.rr-section--listing {
    padding-block: var(--rr-sp-5) var(--rr-section-gap-sm);
}

@media (min-width: 768px) {
    .rr-section--listing {
        padding-block: var(--rr-sp-6) var(--rr-section-gap);
    }
}

.rr-section--tint {
    background-color: var(--rr-surface-2);
}

.rr-section--ink {
    background-color: var(--rr-black);
    color: #ffffff;
}

    .rr-section--ink h1,
    .rr-section--ink h2,
    .rr-section--ink h3,
    .rr-section--ink h4 {
        color: #ffffff;
    }

.rr-divider {
    border: 0;
    border-top: 1px solid var(--rr-outline);
    opacity: 1;
    margin: 0;
}

/* Section heading: left-aligned title + gold rule, optional VIEW ALL on the right */
.rr-section-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: var(--rr-head-gap);
}

.rr-section-head__rule {
    display: block;
    width: 96px;
    height: 3px;
    background: var(--rr-gold);
    margin-top: .5rem;
}

.rr-link-gold {
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-sm);
    letter-spacing: var(--rr-ls-ui);
    text-transform: uppercase;
    color: var(--rr-gold-text);
    text-decoration: none;
    white-space: nowrap;
}

    .rr-link-gold:hover,
    .rr-link-gold:focus {
        color: var(--rr-gold-text);
        text-decoration: underline;
    }

    .rr-link-gold svg {
        width: 18px;
        height: 18px;
    }

.rr-eyebrow {
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
}

.rr-eyebrow--gold {
    color: var(--rr-gold-text);
}

/* --------------------------------------------------------------------------
   4. Buttons — rectangular, 8px radius, never pills
   -------------------------------------------------------------------------- */
.btn {
    --bs-btn-border-radius: var(--rr-radius);
    border-radius: var(--rr-radius);
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-sm);
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    /* Padding tracks the label. Holding 12px/24px around 13px uppercase text
       leaves a button that reads as mostly empty box, which is the giveaway
       that a type pass stopped at font-size. Still 40px tall — above the 24px
       AAA target minimum and comfortable on a phone. */
    padding: .625rem 1.25rem;
    box-shadow: none !important;
    white-space: nowrap;
}

.btn-gold {
    --bs-btn-color: var(--rr-black);
    --bs-btn-bg: var(--rr-gold);
    --bs-btn-border-color: var(--rr-gold);
    --bs-btn-hover-color: #ffffff;
    --bs-btn-hover-bg: var(--rr-gold-deep);
    --bs-btn-hover-border-color: var(--rr-gold-deep);
    --bs-btn-active-color: #ffffff;
    --bs-btn-active-bg: var(--rr-gold-deep);
    --bs-btn-active-border-color: var(--rr-gold-deep);
    --bs-btn-disabled-color: var(--rr-black);
    --bs-btn-disabled-bg: var(--rr-gold-soft);
    --bs-btn-disabled-border-color: var(--rr-gold-soft);
}

/* Secondary CTA on a dark band */
.btn-outline-light-rr {
    --bs-btn-color: #ffffff;
    --bs-btn-border-color: rgba(255,255,255,.5);
    --bs-btn-bg: transparent;
    --bs-btn-hover-color: var(--rr-black);
    --bs-btn-hover-bg: #ffffff;
    --bs-btn-hover-border-color: #ffffff;
    --bs-btn-active-color: var(--rr-black);
    --bs-btn-active-bg: #ffffff;
    --bs-btn-active-border-color: #ffffff;
}

.btn-outline-dark {
    --bs-btn-color: var(--rr-black);
    --bs-btn-border-color: var(--rr-black);
    --bs-btn-hover-bg: var(--rr-black);
    --bs-btn-hover-border-color: var(--rr-black);
    --bs-btn-active-bg: var(--rr-black);
    --bs-btn-active-border-color: var(--rr-black);
}

.btn-lg {
    padding: .875rem 1.75rem;
    font-size: var(--rr-fs-md);
}

/* Icon + label CTA */
.rr-btn-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .5rem;
}

    .rr-btn-icon .material-symbols-outlined,
    .rr-btn-icon svg {
        font-size: 20px;
        width: 20px;
        height: 20px;
    }

/* Icon-only control in the header / cards */
.rr-icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border: 0;
    background: transparent;
    color: var(--rr-black);
    border-radius: var(--rr-radius);
    transition: color .2s ease, background-color .2s ease;
}

    .rr-icon-btn:hover,
    .rr-icon-btn.active {
        color: var(--rr-gold-deep);
        background: var(--rr-surface-2);
    }

/* --------------------------------------------------------------------------
   5. Announcement bar
   -------------------------------------------------------------------------- */
.rr-announcement {
    background: var(--rr-black);
    color: #ffffff;
    font-family: var(--rr-font-body);
    font-size: var(--rr-fs-2xs);
    font-weight: 600;
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    text-align: center;
    padding: .5rem 0;
}

    .rr-announcement .rr-announcement__accent {
        color: var(--rr-gold);
    }

/* --------------------------------------------------------------------------
   6. Header
   -------------------------------------------------------------------------- */
.rr-header {
    background: var(--rr-surface);
    border-bottom: 1px solid var(--rr-outline);
    position: sticky;
    top: 0;
    z-index: 1030;
}

/* The bar owns the header's height, and it owns it through the same token the sticky
   panels offset by. Previously the height came from a `py-3` utility in the markup while
   --rr-header-h carried a hand-copied 72px, so the two could drift silently — and did:
   the bar measured 73px. Anything sticky sat a pixel under the header. Bound here so the
   header can only be resized from the token. */
.rr-header__bar {
    min-height: var(--rr-header-h);
}

/* The header brand is the horizontal lockup now, not type. The <a> is only the hit target and
   the flex anchor; the image inside it carries the size. Kept as a block with line-height 0 so
   the inline-image baseline gap does not add a stray pixel to the 72px bar. */
.rr-wordmark {
    display: block;
    line-height: 0;
    text-decoration: none;
    flex: 0 0 auto;
}

    .rr-wordmark img {
        display: block;
        width: auto;
        height: 28px;
    }

/* C4 — the tagline, set as the second line of the logo lockup and sitting directly under the
   gold rule the artwork itself ends on.

   IT COSTS THE STICKY BAR NOTHING, and that is why it could stay at full wordmark size. The
   header row is 40px tall because the ICON BUTTONS are 40px, not because the wordmark is 28px,
   so there were already 12px of unused height under the logo. 28px artwork + 2px gap + a 10px
   line box is exactly 40px, and the bar measured 73px before and 73px after at 360 / 768 /
   1280 / 1440.

   line-height:1 is what makes that arithmetic hold rather than merely happen to work: it pins
   the line box to the font size whatever metrics the loaded face reports, so a Barlow Condensed
   that arrives late cannot push a sticky header taller mid-load.

   --rr-gold-text (5.17:1 on white), never --rr-gold (2.42:1) — this is 10px text, and 10px is
   as small as the type scale's uppercase micro-label floor goes.

   padding-left compensates the trailing letter-space: centring counts the space after the last
   letter, so without it the words sit half a track left of the wordmark's centre. */
.rr-wordmark__tagline {
    display: block;
    /* The lockup is exactly as wide as the artwork, always. .rr-wordmark is a shrink-to-fit
       flex item, so a tagline that counted towards its max-content width would widen the
       whole header lead the moment the text measured wider than the logo - which is what the
       fallback face does for the instant before Barlow Condensed swaps in. width:0 keeps the
       text out of that measurement and min-width:100% then stretches it back over the image,
       so no font, on any platform, can move the nav rail by loading. */
    width: 0;
    min-width: 100%;
    margin-top: 2px;
    padding-left: .18em;
    font-family: var(--rr-font-head);
    font-weight: 600;
    font-size: var(--rr-fs-3xs);
    line-height: 1;
    letter-spacing: .18em;
    text-transform: uppercase;
    text-align: center;
    white-space: nowrap;
    color: var(--rr-gold-text);
}

/* The bar gets tight once the nav rail and the icon cluster are both in it. The lockup is the
   one element that can give up width without losing meaning - it stays above the 110px minimum
   the brand sheet sets for the horizontal cut. */
@media (max-width: 575.98px) {
    .rr-wordmark img {
        height: 23px;
    }

    /* The tagline goes with it, for WIDTH not height: at 23px the artwork draws 111px wide and
       the tagline does not shrink with it, so the lockup would end up wider than the wordmark
       it belongs to and would eat 20px the toggler, search, wishlist and cart icons need at
       360px. A tagline is not worth a cramped header — the footer carries it at every size. */
    .rr-wordmark__tagline {
        display: none;
    }
}

.rr-nav .nav-link {
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-sm);
    letter-spacing: var(--rr-ls-ui);
    text-transform: uppercase;
    color: var(--rr-muted);
    padding: .5rem 0;
    border-bottom: 2px solid transparent;
}

    .rr-nav .nav-link:hover,
    .rr-nav .nav-link:focus {
        color: var(--rr-black);
    }

    .rr-nav .nav-link.active {
        color: var(--rr-black);
        border-bottom-color: var(--rr-gold);
    }

/* The desktop category rail. The nav is the Category table now, so its width is the owner's
   business — the rail scrolls sideways instead of wrapping to a second row, which would make the
   header taller and drag the wordmark and cart icons out of line on every page.

   min-width:0 appears three times down this chain, and all three are load-bearing. A flex item
   defaults to min-width:auto, which refuses to shrink below its content; with any link missing,
   an overflowing rail pushes the search and cart icons off the right of the page instead of
   scrolling. Verified by cloning the nav up to ten categories at a 1000px viewport. */
.rr-header__lead {
    min-width: 0;
}

.rr-nav-rail {
    display: flex;
    align-items: center;
    gap: 1.5rem;
    min-width: 0;
}

.rr-nav-rail__track {
    overflow-x: auto;
    scrollbar-width: none;
    /* Shrink before the wordmark or the icons do — the rail is the part that can scroll. */
    flex: 1 1 auto;
    min-width: 0;
    /* Nothing wraps: items keep their width and the track scrolls past them. */
    flex-wrap: nowrap;
    /* Momentum scrolling stops between items rather than mid-label on a trackpad flick. */
    scroll-snap-type: x proximity;
}

    /* WebKit's bar would sit under the nav text and cut into the 3px gold underline. Hiding it is
       safe here because "All" is always visible outside the track, so the rail never looks like
       the end of the list. */
    .rr-nav-rail__track::-webkit-scrollbar {
        display: none;
    }

    .rr-nav-rail__track .nav-item {
        flex: 0 0 auto;
        scroll-snap-align: start;
    }

/* Pinned outside the scroller, so the route to every category is reachable without scrolling to
   look for it. It is a <button> now (it opens the category menu), so it also has to shed the
   browser's button chrome to keep sitting on the same baseline as the rail's links. */
.rr-nav-rail__menu {
    flex: 0 0 auto;
}

.rr-nav-rail__all {
    display: inline-flex;
    align-items: center;
    gap: .125rem;
    background: none;
    border: 0;
    border-radius: 0;
    flex: 0 0 auto;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-sm);
    letter-spacing: var(--rr-ls-ui);
    text-transform: uppercase;
    color: var(--rr-gold-text);
    text-decoration: none;
    padding: .5rem 0;
    border-bottom: 2px solid transparent;
}

    .rr-nav-rail__all:hover,
    .rr-nav-rail__all:focus {
        color: var(--rr-black);
    }

    .rr-nav-rail__all.active,
    .rr-nav-rail__all[aria-expanded="true"] {
        color: var(--rr-black);
        border-bottom-color: var(--rr-gold);
    }

.rr-nav-rail__all-caret {
    font-size: var(--rr-fs-xl);
    transition: transform .15s ease;
}

.rr-nav-rail__all[aria-expanded="true"] .rr-nav-rail__all-caret {
    transform: rotate(180deg);
}

/* The category menu behind "All". Bootstrap toggles it; the house style below is white surface,
   1px outline instead of a shadow, 8px radius.

   It hangs from the button itself — that is where someone who clicked "All" looks for it.
   [data-bs-popper] is what Bootstrap stamps on the menu when it skips Popper (which it always
   does inside a .navbar), and its own rule carries the same specificity, so matching it here is
   what makes these win. The translation is set by initCategoryMenu in royal-rider.js, and only
   when the panel would otherwise run off the right of the window — see the note there. */
.rr-cat-menu.dropdown-menu[data-bs-popper] {
    top: 100%;
    left: 0;
    right: auto;
    transform: translateX(var(--rr-cat-menu-shift, 0px));
}

.rr-cat-menu.dropdown-menu {
    --bs-dropdown-bg: var(--rr-surface);
    --bs-dropdown-border-color: var(--rr-outline);
    --bs-dropdown-border-radius: var(--rr-radius);
    --bs-dropdown-link-color: var(--rr-black);
    --bs-dropdown-link-hover-bg: var(--rr-surface-2);
    --bs-dropdown-link-hover-color: var(--rr-black);
    --bs-dropdown-link-active-bg: var(--rr-surface-2);
    --bs-dropdown-link-active-color: var(--rr-black);
    box-shadow: none;
    margin-top: .5rem;
    padding: var(--rr-cat-menu-pad);
    /* B-UX4: one column now, so the panel is a fixed narrow rail rather than a wide grid.
       min-width holds a two-word category on one line; max-width stops a long one from
       growing the panel into the search box. */
    min-width: 15rem;
    max-width: min(18rem, calc(100vw - 2rem));
    /* The flyout is positioned against a ROW, and a row is inset from the panel's edge by the
       panel's own padding and border. Naming both here is what lets the flyout clear the panel
       exactly rather than overlapping its outline by a few pixels. */
    --rr-cat-menu-pad: .5rem;
    --rr-cat-menu-clear: calc(var(--rr-cat-menu-pad) + 1px + .25rem);
}

/* --- B-UX4: vertical rows + flyout submenus ------------------------------------------------
   Desktop only. These rules live inside .rr-cat-menu, which is inside .rr-nav-rail (d-none
   d-lg-flex), so nothing here can reach the mobile collapse — that keeps its .rr-nav-group
   accordion below and never grows a flyout. */
/* One column, one category per row. */
.rr-cat-menu__list {
    display: flex;
    flex-direction: column;
    gap: .125rem;
}

.rr-cat-menu__item {
    /* The flyout is absolute against the ROW, which is what aligns it to the row it belongs
       to rather than to the top of the panel. */
    position: relative;
}

/* The name and the chevron are separate controls sharing one row: tapping the name goes to the
   category, tapping the chevron opens its children. A single combined target would force touch
   users to choose one or the other. */
.rr-cat-menu__row {
    display: flex;
    align-items: stretch;
    gap: .125rem;
}

.rr-cat-menu__link.dropdown-item {
    flex: 1 1 auto;
    min-width: 0;
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: var(--rr-fs-lg);
    letter-spacing: var(--rr-ls-ui);
    text-transform: uppercase;
    padding: .5rem .625rem;
    border-radius: var(--rr-radius);
    /* Long names wrap instead of running out of the panel. */
    white-space: normal;
}

.rr-cat-menu__toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 40px;
    background: none;
    border: 0;
    border-radius: var(--rr-radius);
    color: var(--rr-muted);
}

    .rr-cat-menu__toggle .material-symbols-outlined {
        font-size: 20px;
    }

    .rr-cat-menu__toggle:hover,
    .rr-cat-menu__toggle:focus-visible,
    .rr-cat-menu__toggle[aria-expanded="true"] {
        background: var(--rr-surface-2);
        color: var(--rr-black);
    }

/* The open row stays highlighted so it is obvious which parent the flyout belongs to. */
.rr-cat-menu__item:has(> .rr-cat-menu__row > .rr-cat-menu__toggle[aria-expanded="true"]) .rr-cat-menu__link.dropdown-item {
    background: var(--rr-surface-2);
}

/* Beside the panel, aligned to its row. --left is set by initCategoryFlyout when opening on the
   right would run off the window. */
.rr-cat-menu__sub {
    position: absolute;
    top: -.5rem;
    left: 100%;
    z-index: 1;
    min-width: 13rem;
    max-width: min(17rem, calc(100vw - 2rem));
    /* A category with many children scrolls rather than running off the bottom. */
    max-height: min(70vh, 28rem);
    overflow-y: auto;
    margin-left: var(--rr-cat-menu-clear);
    padding: var(--rr-cat-menu-pad);
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
}

.rr-cat-menu__sub--left {
    left: auto;
    right: 100%;
    margin-left: 0;
    margin-right: var(--rr-cat-menu-clear);
}

.rr-cat-menu__sub-link.dropdown-item,
.rr-cat-menu__sub-all.dropdown-item {
    font-family: var(--rr-font-body);
    font-size: var(--rr-fs-sm);
    color: var(--rr-muted);
    padding: .4375rem .625rem;
    border-radius: var(--rr-radius);
    white-space: normal;
}

    .rr-cat-menu__sub-link.dropdown-item:hover,
    .rr-cat-menu__sub-link.dropdown-item:focus,
    .rr-cat-menu__sub-all.dropdown-item:hover,
    .rr-cat-menu__sub-all.dropdown-item:focus {
        color: var(--rr-black);
    }

/* The parent repeated inside its own flyout — the touch route to "everything in this category"
   once the chevron has been used to open it. */
.rr-cat-menu__sub-all.dropdown-item {
    font-weight: 600;
    color: var(--rr-gold-text); /* 5.17:1 on white — the fill gold never carries text. */
    border-bottom: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius) var(--rr-radius) 0 0;
    margin-bottom: .25rem;
}

/* Kept last so /categories — and the imagery on it — stays one click away. */
.rr-cat-menu__all.dropdown-item {
    margin-top: .25rem;
    border-top: 1px solid var(--rr-outline);
    padding: .75rem .625rem .5rem;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-xs);
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    /* 5.17:1 on white — the fill gold is 2.42:1 and never carries text. */
    color: var(--rr-gold-text);
}

    .rr-cat-menu__all.dropdown-item:hover,
    .rr-cat-menu__all.dropdown-item:focus {
        color: var(--rr-black);
    }

/* The same tree in the mobile collapse. The parent keeps its own tap target and the chevron is a
   separate control beside it, so expanding children never steals the link. */
.rr-nav-group__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
}

.rr-nav-group__toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    flex: 0 0 auto;
    background: none;
    border: 0;
    color: var(--rr-muted);
}

    .rr-nav-group__toggle .material-symbols-outlined {
        transition: transform .15s ease;
    }

    .rr-nav-group__toggle:not(.collapsed) .material-symbols-outlined {
        transform: rotate(180deg);
        color: var(--rr-black);
    }

.rr-nav-group__children {
    padding-left: 1rem;
    border-left: 1px solid var(--rr-outline);
    margin-left: .25rem;
}

    .rr-nav-group__children .nav-link {
        font-family: var(--rr-font-body);
        font-weight: 500;
        font-size: var(--rr-fs-xs);
        letter-spacing: var(--rr-ls-ui);
        text-transform: none;
        padding-block: .375rem;
    }

.rr-search {
    display: flex;
    align-items: center;
    gap: .5rem;
    background: var(--rr-surface-2);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    padding: .375rem .75rem;
}

    .rr-search input {
        border: 0;
        background: transparent;
        outline: none;
        font-size: var(--rr-fs-sm);
        width: 12rem;
        color: var(--rr-ink);
    }

    .rr-search .material-symbols-outlined {
        font-size: 20px;
        color: var(--rr-muted);
    }

/* --------------------------------------------------------------------------
   7. Hero — the light warm band at the top of the home page

   This used to be the page's one deliberate DARK band. The Claude Design handoff
   turns it over: a warm off-white gradient, black type, and the photograph
   bleeding in from the right under a left-to-right wash. The footer is now the
   only near-black block on the storefront, which is what the handoff's comps
   show and what "gold used with restraint" wants — gold is the CTA fill here,
   not a colour the whole band is painted in.

   Every part of the band (media, scrim, eyebrow, title, body, CTAs) keeps its
   class name, so _HeroSlide.cshtml did not have to be restructured — only its
   secondary CTA changed, from the light outline to the dark one.
   -------------------------------------------------------------------------- */
.rr-hero {
    position: relative;
    background: linear-gradient(180deg, #F4F1E9 0%, #E8E4D8 100%);
    overflow: hidden;
    display: flex;
    align-items: center;
    min-height: 420px;
}

@media (min-width: 992px) {
    .rr-hero {
        min-height: 520px;
    }
}

/* Hero carousel — only present when the owner has more than one active banner. The slides
   themselves are plain .rr-hero sections, so a single banner needs none of this and renders
   exactly as the approved design does. Bootstrap supplies the behaviour; these rules only
   restyle its controls into the gold/black system. */
.rr-hero-carousel {
    position: relative;
}

    /* Two bordered squares in the bottom-right corner rather than Bootstrap's full-height side
       strips. On a light band a translucent white chevron is invisible, and the strips lay across
       the artwork the band exists to show. Prev is an outline, next is filled black — the
       handoff's pairing, and a non-colour signal of which way is "forward" (WCAG 1.4.1).

       Bootstrap paints both chevrons white, so the outlined one is flipped to black by filter.
       Off below lg, where the design shows dots only and the stacked CTAs leave no corner free. */
    .rr-hero-carousel__control {
        top: auto;
        bottom: 34px;
        width: 46px;
        height: 46px;
        border: 1px solid var(--rr-black);
        border-radius: var(--rr-radius);
        opacity: 1;
        transition: background-color .2s ease, border-color .2s ease;
    }

        .rr-hero-carousel__control.carousel-control-prev {
            left: auto;
            /* 46px box + the handoff's 10px gap, measured from the next button's 40px inset. */
            right: 96px;
        }

            .rr-hero-carousel__control.carousel-control-prev .carousel-control-prev-icon {
                filter: invert(1) grayscale(100);
            }

        .rr-hero-carousel__control.carousel-control-next {
            right: 40px;
            background-color: var(--rr-black);
        }

        .rr-hero-carousel__control:hover,
        .rr-hero-carousel__control:focus-visible {
            background-color: var(--rr-gold);
            border-color: var(--rr-gold);
        }

            /* On the gold fill both chevrons want to be black. */
            .rr-hero-carousel__control:hover .carousel-control-next-icon,
            .rr-hero-carousel__control:focus-visible .carousel-control-next-icon {
                filter: invert(1) grayscale(100);
            }

@media (max-width: 991.98px) {
    .rr-hero-carousel__control {
        display: none;
    }
}

/* Dashes rather than Bootstrap's dots. From lg they align to the CONTAINER's left edge, flush
   under the headline, which is where the handoff puts them; below that they stay centred, because
   the CTAs there are full-width and a left-hugging row under them looks dropped rather than
   placed. The margin also resets Bootstrap's own 15% side margins on .carousel-indicators. */
.rr-hero-carousel__dots {
    margin: 0 0 22px;
    justify-content: center;
}

@media (min-width: 992px) {
    .rr-hero-carousel__dots {
        left: 50%;
        right: auto;
        transform: translateX(-50%);
        width: 100%;
        max-width: calc(var(--rr-container-max) + (var(--rr-container-pad) * 2));
        padding-inline: var(--rr-container-pad);
        margin: 0 0 34px;
        justify-content: flex-start;
    }
}

    .rr-hero-carousel__dots [data-bs-slide-to] {
        width: 34px;
        height: 3px;
        border: 0;
        border-radius: 0;
        /* The handoff's inactive dash: a warm grey that reads as "off" against the band's own
           #E8E4D8 without going as dark as the ink. */
        background-color: #C6C2B4;
        opacity: 1;
        transition: background-color .2s ease;
    }

        .rr-hero-carousel__dots [data-bs-slide-to].active {
            background-color: var(--rr-gold);
        }

.rr-hero__media {
    position: absolute;
    inset: 0;
    background-size: cover;
    background-position: center;
}

/* Vertical on a phone, where the copy sits ABOVE the artwork; horizontal from lg, where it sits
   beside it. The same wash either way: the band's own off-white at full strength under the text,
   fading to nothing so the photograph is untouched where nothing is on top of it.

   THE STOPS ARE MEASURED AGAINST THIS SITE'S COPY, not copied from the handoff. The handoff's
   phone comp fades out by 62% because its own hero copy is a headline and one short line; the
   banners this shop actually runs carry a headline, a wrapped paragraph and two buttons, and
   that block reaches 74% of the band. With the handoff's stops the paragraph and the outlined
   secondary CTA landed on bare photograph — and the shop's banner photography is black-ground
   studio work, so black type on it was unreadable rather than merely low-contrast.

   So the wash is held high through the CTA row and released after it. Same picture, same light
   band, and the text stays legible whatever the owner uploads next — which matters more here
   than matching a gradient stop, because nothing in the admin constrains a banner's brightness. */
.rr-hero__scrim {
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, rgba(246,244,238,.97) 0%, rgba(246,244,238,.93) 42%, rgba(246,244,238,.72) 66%, rgba(246,244,238,.28) 84%, rgba(246,244,238,0) 100%);
}

@media (min-width: 992px) {
    .rr-hero__scrim {
        background: linear-gradient(90deg, rgba(246,244,238,.97) 0%, rgba(246,244,238,.93) 32%, rgba(246,244,238,.45) 56%, rgba(246,244,238,0) 76%);
    }
}

.rr-hero__inner {
    position: relative;
    z-index: 1;
    /* The extra bottom padding is a lane for the absolutely-positioned dots. Without it they
       land on top of the CTAs on a phone, where the buttons are full-width. */
    padding-block: 28px 48px;
    max-width: 35rem;
}

@media (min-width: 992px) {
    .rr-hero__inner {
        padding-block: 40px 60px;
    }
}

/* The eyebrow, no longer a gold chip. On white a --rr-gold fill beside black display type is the
   loudest thing in the band; the handoff demotes it to a tracked-out muted caption led in by a
   short gold rule. The rule is decoration, which is exactly what --rr-gold (2.42:1) is for; the
   words are normal-size text and take --rr-muted (4.83:1). */
.rr-hero__badge {
    display: inline-flex;
    align-items: center;
    gap: .625rem;
    background: none;
    color: var(--rr-muted);
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    padding: 0;
    border-radius: 0;
    margin-bottom: 1.375rem;
}

    .rr-hero__badge::before {
        content: "";
        flex: 0 0 34px;
        height: 2px;
        background: var(--rr-gold);
    }

.rr-hero__title {
    color: var(--rr-black);
    font-size: clamp(2.875rem, 6vw, 5.125rem);
    line-height: .92;
    letter-spacing: var(--rr-ls-head);
    margin-bottom: 1.25rem;
}

    /* The headline's last word was set in gold on the dark band, where --rr-gold reads 7.99:1.
       On white it is 2.42:1 — nowhere near legible — and the handoff sets the whole headline in
       ink anyway. The element stays so _HeroSlide.cshtml is unchanged and so the accent can be
       brought back by editing one declaration. */
    .rr-hero__title .rr-hero__title-accent {
        color: inherit;
    }

.rr-hero__body {
    color: var(--rr-ink);
    font-size: var(--rr-fs-xl);
    line-height: var(--rr-lh-body);
    max-width: 29rem;
    margin-bottom: 2.125rem;
}

/* --------------------------------------------------------------------------
   8. Category tiles
   -------------------------------------------------------------------------- */
/* The handoff's tile: a bordered white card, a 1:1 picture, and the name on a bordered strip
   BELOW it rather than laid over the photograph. Two things that buys —

     · the name is black on white instead of white on whatever the upload happens to put under
       it, so there is no scrim to tune and no contrast that depends on the photograph;
     · most categories have no photograph at all, and an empty tinted square with a label under
       it reads as "no picture yet", where an empty square with a label ON it read as broken.

   The scrim and the gold rule are no longer rendered (see Views/Home/Index.cshtml) and their
   rules have gone with them. Hover is the border going gold — the handoff's only tile state, and
   the same signal .rr-card now uses. */
.rr-cat-tile {
    position: relative;
    display: flex;
    flex-direction: column;
    height: 100%;
    overflow: hidden;
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    background: var(--rr-surface);
    text-decoration: none;
}

.rr-cat-tile__media {
    display: block;
    width: 100%;
    aspect-ratio: 1 / 1;
    background-color: var(--rr-surface-2);
    background-size: cover;
    background-position: center;
}

.rr-cat-tile__label {
    border-top: 1px solid var(--rr-outline);
    padding: .75rem .625rem;
    text-align: center;
}

    /* h3 on the home page (under an h2 section heading), h2 on /categories (under the page h1).
       Both are the correct level for their page, so the tile styles whichever it is given. */
    .rr-cat-tile__label h2,
    .rr-cat-tile__label h3 {
        color: var(--rr-black);
        font-size: var(--rr-fs-xl);
        letter-spacing: var(--rr-ls-caps);
        margin: 0;
    }

@media (min-width: 768px) {
    .rr-cat-tile__label {
        padding: .875rem .75rem;
    }
}

/* --- /categories browse-all page ------------------------------------------
   An INDEX, not a gallery. Each card is one parent WITH its children inside it,
   so the hierarchy is structural rather than implied by proximity.

   ONE component, TWO layouts, and the reason is scroll cost:

     · below 768px the card is horizontal — a small square, then the name and
       its chips. A column of tall picture tiles turned eleven categories into
       roughly four phone screens of scrolling to read a list of names.
     · from 768px it stands up: a 4:3 panel over the body, two across, three
       from 1200px. At that width the horizontal card wastes half its row on
       white, and the desktop page can afford the picture.

   Ten of the eleven categories have no photograph, so the no-photo state is
   the DESIGNED state here, not the fallback — see .rr-cat-card__plate. */
.rr-cat-index__masthead {
    /* The gold left rule .rr-page-header uses on the listing pages, so this page
       opens the way the shop does. */
    border-left: 4px solid var(--rr-gold);
    padding-left: 1.25rem;
    margin-block: 1.5rem 2.5rem;
}

@media (min-width: 768px) {
    .rr-cat-index__masthead {
        padding-left: 1.75rem;
        margin-block: 2rem 3rem;
    }
}

.rr-cat-index__eyebrow {
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    /* Normal-size gold text, so it is the 5.17:1 token and never --rr-gold. */
    color: var(--rr-gold-text);
    margin: 0 0 .5rem;
}

.rr-cat-index__masthead h1 {
    font-size: clamp(2rem, 4.8vw, 3.25rem);
    line-height: 1;
    margin: 0 0 .75rem;
}

.rr-cat-index__lede {
    font-size: var(--rr-fs-xl);
    line-height: var(--rr-lh-body);
    color: var(--rr-muted);
    max-width: 44rem;
    margin: 0;
}

/* The counts read as data, not as a sentence: the number in the heading face at
   the size of a small stat, the label beside it in the quiet uppercase used for
   metadata everywhere else. */
.rr-cat-index__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .375rem 1rem;
    margin: 1.25rem 0 0;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    color: var(--rr-muted);
}

.rr-cat-index__stat {
    display: inline-flex;
    align-items: baseline;
    gap: .375rem;
}

    .rr-cat-index__stat strong {
        font-family: var(--rr-font-head);
        font-weight: 700;
        font-size: var(--rr-fs-3xl);
        line-height: 1;
        letter-spacing: var(--rr-ls-ui);
        color: var(--rr-black);
    }

    /* A gold dot between the stats — decoration, so it is generated content and
       carries no meaning a screen reader has to hear. */
    .rr-cat-index__stat + .rr-cat-index__stat::before {
        content: "";
        display: inline-block;
        width: 4px;
        height: 4px;
        border-radius: 50%;
        background: var(--rr-gold);
        margin-right: .625rem;
        transform: translateY(-3px);
    }

.rr-cat-index__grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--rr-gutter);
}

@media (min-width: 768px) {
    .rr-cat-index__grid {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (min-width: 1200px) {
    .rr-cat-index__grid {
        grid-template-columns: repeat(3, 1fr);
    }
}

.rr-cat-card {
    position: relative;
    display: flex;
    flex-direction: row;
    align-items: stretch;
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    overflow: hidden;
    transition: border-color .2s ease;
}

@media (min-width: 768px) {
    .rr-cat-card {
        flex-direction: column;
    }
}

    /* Depth is the border warming to gold, not a shadow. :focus-within carries the
       same state for a keyboard, including while tabbing the chips inside. */
    .rr-cat-card:hover,
    .rr-cat-card:focus-within {
        border-color: var(--rr-gold-deep);
    }

/* Phone: square is the MINIMUM, not a fixed ratio. min-height matches the width,
   so a card whose body is shorter is exactly square — the usual case — and one
   pushed taller by a second row of chips grows the panel with it rather than
   leaving a strip of white below the picture. Both states crop, neither
   stretches, because the media is always background-size:cover. */
.rr-cat-card__media {
    position: relative;
    flex: 0 0 auto;
    width: 108px;
    min-height: 108px;
    background-color: var(--rr-surface-2);
    overflow: hidden;
}

@media (min-width: 576px) {
    .rr-cat-card__media {
        width: 132px;
        min-height: 132px;
    }
}

/* Desktop: full-width band at a fixed 4:3, so every card in a row starts its
   body on the same line whatever the picture is. */
@media (min-width: 768px) {
    .rr-cat-card__media {
        width: auto;
        min-height: 0;
        aspect-ratio: 4 / 3;
        border-bottom: 1px solid var(--rr-outline);
    }
}

/* cover: the photograph is cropped, never stretched. The slow scale on hover is
   the only movement on the card and the global reduced-motion block kills it. */
.rr-cat-card__photo {
    position: absolute;
    inset: 0;
    background-size: cover;
    background-position: center;
    transition: transform .45s ease;
}

.rr-cat-card:hover .rr-cat-card__photo,
.rr-cat-card:focus-within .rr-cat-card__photo {
    transform: scale(1.04);
}

/* The no-photograph state, DESIGNED rather than defaulted, because it is what
   most of this page is made of. A warm off-white field, a faint gold hatch at
   135° and one large gold glyph — restrained enough to sit ten times on a
   screen without the page turning into a pattern swatch, and distinct enough
   per category that the cards do not read as ten copies of one tile.

   Earlier this was a near-black panel with the name set in gold. It measured
   well and looked severe: ten black rectangles is not a white/gold storefront,
   and the name was already the h2 two lines below it. */
.rr-cat-card__plate {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: var(--rr-surface-2);
    background-image: repeating-linear-gradient( 135deg, rgba(201,162,39,.14) 0 1px, rgba(201,162,39,0) 1px 11px );
}

.rr-cat-card__glyph {
    /* --rr-gold-deep is 3.82:1 on white and 3.66:1 on this tint — over the 3:1
       that WCAG 1.4.11 asks of a graphical object, though the plate is
       aria-hidden and this is decoration either way. */
    color: var(--rr-gold-deep);
    font-size: var(--rr-fs-6xl);
    font-variation-settings: 'FILL' 0, 'wght' 300, 'GRAD' 0, 'opsz' 48;
    transition: transform .3s ease;
}

@media (min-width: 576px) {
    .rr-cat-card__glyph {
        font-size: var(--rr-fs-7xl);
    }
}

@media (min-width: 768px) {
    .rr-cat-card__glyph {
        font-size: var(--rr-fs-9xl);
    }
}

.rr-cat-card:hover .rr-cat-card__glyph,
.rr-cat-card:focus-within .rr-cat-card__glyph {
    transform: scale(1.07);
}

.rr-cat-card__body {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: .5rem;
    padding: .875rem 1rem 1rem;
}

@media (min-width: 768px) {
    .rr-cat-card__body {
        gap: .625rem;
        padding: 1.125rem 1.25rem 1.25rem;
    }
}

/* Subcategory count, or the state of a card that has none. Small, quiet, and
   always present, so the title sits on the same baseline in every card. */
.rr-cat-card__eyebrow {
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
    margin: 0;
}

.rr-cat-card__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    width: 100%;
}

.rr-cat-card__title {
    font-size: clamp(1rem, .5vw + .875rem, 1.25rem);
    margin: 0;
}

    .rr-cat-card__title a {
        color: var(--rr-black);
        text-decoration: none;
    }

/* The card's affordance. Decorative — the h2's link is the control, and this
   only says which way it goes. */
.rr-cat-card__arrow {
    flex: 0 0 auto;
    font-size: var(--rr-fs-2xl);
    color: var(--rr-muted);
    transition: transform .25s ease, color .2s ease;
}

.rr-cat-card:hover .rr-cat-card__arrow,
.rr-cat-card:focus-within .rr-cat-card__arrow {
    color: var(--rr-gold-deep);
    transform: translateX(4px);
}

/* The card's one gold rule. */
.rr-cat-card__rule {
    display: block;
    width: 40px;
    height: 3px;
    background: var(--rr-gold);
    transition: width .3s ease;
}

.rr-cat-card:hover .rr-cat-card__rule,
.rr-cat-card:focus-within .rr-cat-card__rule {
    width: 72px;
}

/* Children as chips, wrapping. A card with none simply ends after the rule —
   nothing reserves the space, so there is no empty gap to explain. */
.rr-cat-card__chips {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    margin-top: .125rem;
    width: 100%;
    /* Above the stretched link covering the card (Bootstrap gives that ::after
       z-index 1), so a chip is a link in its own right rather than a decoration
       painted under the parent's click target. */
    position: relative;
    z-index: 2;
}

    /* Reuses .rr-subcat, the chip already used for subcategory navigation on the
       listing pages: rectangular, 8px, 1px outline, near-black. Blockified here
       because the flex item is the <li>, not the <a>. */
    .rr-cat-card__chips .rr-subcat {
        display: inline-flex;
        align-items: center;
        max-width: 100%;
    }

        /* Gold text as well as a gold border, on hover and on focus alike. */
        .rr-cat-card__chips .rr-subcat:hover,
        .rr-cat-card__chips .rr-subcat:focus-visible {
            border-color: var(--rr-gold-deep);
            color: var(--rr-gold-text);
        }

        /* The global focus ring (section 2) also sets border-radius:2px, which shapes
           the OUTLINE on the mostly-borderless things it was written for. A chip has a
           visible 1px border, so there it squares the corners off for as long as the
           chip is focused — the one control in the system that changes shape when you
           tab to it. Held at 8px; the ring still draws, offset outside the border. */
        .rr-cat-card__chips .rr-subcat:focus-visible {
            border-radius: var(--rr-radius);
        }

@media (max-width: 575.98px) {
    /* A 328px card leaves ~190px for chips. Slightly smaller type keeps names
       like "English Willow Bats" on one line instead of wrapping inside a chip. */
    .rr-cat-card__chips .rr-subcat {
        font-size: var(--rr-fs-2xs);
        padding: .375rem .625rem;
    }
}

@media (min-width: 768px) {
    /* Grid rows stretch every card to the tallest in the row, so a card with two
       chips would otherwise leave its whitespace between the rule and the chips.
       Pushed to the bottom instead, the chip rows line up across the row and the
       slack collects under the title where it reads as breathing room. */
    .rr-cat-card__chips {
        margin-top: auto;
        padding-top: .25rem;
    }
}

/* Closing panel: the customer who did not find their category has one next move
   rather than the page just stopping at the last card. Same tinted panel as the
   section bands, so it reads as part of the page and not as an advert. */
.rr-cat-index__cta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1rem 1.5rem;
    margin-top: var(--rr-gutter);
    padding: 1.25rem;
    background: var(--rr-surface-2);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
}

@media (min-width: 768px) {
    .rr-cat-index__cta {
        padding: 1.5rem 1.75rem;
    }
}

    .rr-cat-index__cta h2 {
        font-size: var(--rr-fs-2xl);
        margin: 0 0 .25rem;
    }

    .rr-cat-index__cta p {
        margin: 0;
        font-size: var(--rr-fs-sm);
        line-height: 1.5;
        /* On the surface-2 fill --rr-muted drops to 4.51:1; the darker token is
           5.95:1 there and still plainly secondary. */
        color: var(--rr-muted-on-tint);
    }

/* --- Horizontal rail (home "Shop By Category") ----------------------------
   One row, never a grid that wraps: a wrapping grid turned five categories into
   "four and a stray", which reads as a broken layout rather than as more
   content to the right.

   The row is a grid when the tiles fit and a rail when they don't, and both
   come out of one flex rule on the item (see .rr-rail__item). Sizing the tiles
   at a fixed width regardless left the four categories the store actually has
   sitting short of the right edge — a lopsided margin, with nothing to scroll
   to that would have explained it.

   The whole thing works with JavaScript off: the track is a native scroll
   container, so touch, trackpad, and tabbing to an off-screen link all scroll
   it. The arrows and edge fades are enhancements the script adds only when the
   track genuinely overflows, which is also what keeps both off the grid. */
.rr-rail {
    position: relative;
}

.rr-rail__track {
    display: flex;
    gap: 1.5rem;
    overflow-x: auto;
    scrollbar-width: none;
    /* mandatory, not proximity: every item is the same width, so a snap point is always within
       reach and a flick can never rest with two half-cards showing. */
    scroll-snap-type: x mandatory;
    /* Keeps a snapped card flush with the container edge rather than against the viewport. */
    scroll-padding-inline: 0;
    /* Room for the focus ring on the last row of tiles, which would otherwise be clipped by
       overflow. */
    padding-block: 2px;
}

    .rr-rail__track::-webkit-scrollbar {
        display: none;
    }

.rr-rail__item {
    scroll-snap-align: start;
    /* Grid when they fit, rail when they don't — both halves are these three declarations, so
       there is no breakpoint to keep in step with the category count and no script deciding it.
         · They fit: the track has free space, every item grows by the same amount, and the row
           is an even grid — equal gutters, first and last tile flush with the container edges.
           Nothing overflows, so the arrows and the edge fades (script-driven, and both gated on
           the track actually overflowing) stay off it.
         · They don't: free space is negative, so grow contributes nothing and shrink 0 holds
           every tile at its natural width. The track overflows into the scrolling rail, and the
           partial card at the right edge is the cheapest possible signal that the row continues.
       The basis is what decides which one you get. In the 1280px container four tiles
       (4 x 280 + 3 x 24 gap = 1192) fit and five (1496) do not. On a phone 78vw is deliberately
       more than half the container, so a second tile always overflows and the one-and-a-peek
       that belongs there survives untouched. */
    flex-grow: 1;
    flex-shrink: 0;
    flex-basis: min(78vw, 280px);
    /* The cap is what makes flex-grow safe now that the tile is a 1:1 picture with a name strip
       under it. Growing was harmless while the tile had a fixed 320-400px height — the extra
       width just made a wider box. A square grows in BOTH directions: with one category in the
       table the single tile took the whole 1200px track and rendered a 1200px-tall picture,
       roughly a screen and a half of one photograph.

       340px leaves the four-across grid the container is tuned for intact (4 x 280 + 3 x 24 fits
       at 1280, so they settle around 292 each and stay flush with both edges) and only bites
       when there are so few tiles that stretching them was never going to look deliberate. A
       short row with white to the right of it is the honest picture of a small catalogue. */
    max-width: 340px;
}

    /* The tile sizes itself now — a 1:1 picture plus the name strip under it — so it fills the
       track's stretched height rather than being pinned to a figure that would letterbox the
       square. */
    .rr-rail__item .rr-cat-tile {
        height: 100%;
    }

/* Fades at the edges, toggled by the script so they only appear on the side that has more to
   show. pointer-events:none so they never swallow a click on the tile underneath. */
.rr-rail::before,
.rr-rail::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    width: 3rem;
    z-index: 2;
    pointer-events: none;
    opacity: 0;
    transition: opacity .2s ease;
}

.rr-rail::before {
    left: 0;
    background: linear-gradient(to right, var(--rr-surface), transparent);
}

.rr-rail::after {
    right: 0;
    background: linear-gradient(to left, var(--rr-surface), transparent);
}

.rr-rail.is-scrollable-start::before,
.rr-rail.is-scrollable-end::after {
    opacity: 1;
}

.rr-rail__btn {
    position: absolute;
    /* Centred on the PICTURE, not on the card: the name strip hangs below the square, so a flat
       50% would leave both arrows sitting noticeably low. */
    top: 44%;
    transform: translateY(-50%);
    z-index: 3;
    display: none;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border: 1px solid var(--rr-outline);
    border-radius: 50%;
    background: var(--rr-surface);
    color: var(--rr-black);
    cursor: pointer;
    transition: border-color .2s ease, color .2s ease, opacity .2s ease;
}

    /* The script adds this only when the track overflows, so an arrow never appears as a control
       that does nothing. */
    .rr-rail.has-controls .rr-rail__btn {
        display: flex;
    }

    .rr-rail__btn:hover,
    .rr-rail__btn:focus-visible {
        border-color: var(--rr-gold);
        color: var(--rr-gold-text);
    }

    /* Kept in the layout rather than removed at the ends: arrows that vanish make the row jump
       sideways as you scroll. */
    .rr-rail__btn[disabled] {
        opacity: .35;
        cursor: default;
    }

    .rr-rail__btn--prev {
        left: -22px;
    }

    .rr-rail__btn--next {
        right: -22px;
    }

/* No hover to reveal them and no room to hang them outside the container, so the arrows stay off
   on touch-sized screens — swiping is the native gesture there anyway. */
@media (max-width: 991.98px) {
    .rr-rail.has-controls .rr-rail__btn {
        display: none;
    }
}

@media (prefers-reduced-motion: no-preference) {
    .rr-rail__track {
        scroll-behavior: smooth;
    }
}

/* --------------------------------------------------------------------------
   9. Product card — 1:1 white tile, contained photo, no border, no shadow
   -------------------------------------------------------------------------- */
.rr-card {
    display: flex;
    flex-direction: column;
    text-decoration: none;
    color: inherit;
    height: 100%;
    /* The handoff frames the card: a 1px warm outline, the 8px radius, the picture on white and
       the words on a strip beneath it. Borders over shadows, as the design system asks. The
       hover state is the outline going gold — the same signal the category tiles use, so the two
       kinds of tile on the home page behave alike. */
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    background: var(--rr-surface);
    overflow: hidden;
    padding-bottom: .875rem;
    transition: border-color .25s ease;
}

/* :focus-within as well as :hover — the card contains a link, a wishlist button and two submit
   buttons, and a keyboard visitor inside it should see the same frame a pointer visitor does. */
.rr-card:hover,
.rr-card:focus-within {
    border-color: var(--rr-gold);
}

/* The card is a bordered box now, so its inner rows carry the side padding the borderless tile
   never needed. The media is deliberately NOT in this list: it runs flush to the frame. */
.rr-card__eyebrow,
.rr-card__name,
.rr-card__prices,
.rr-card__actions {
    padding-inline: .875rem;
}

/* The gap the media's old bottom margin used to provide, now on the first row of words. */
.rr-card__eyebrow {
    margin-top: .75rem;
}

.rr-card__media-wrap {
    position: relative;
}

.rr-card__media {
    display: block;
    position: relative;
    aspect-ratio: 1 / 1;
    background: var(--rr-surface);
    overflow: hidden;
    /* Flush to the card's own edge, separated from the words by the same warm rule that runs
       round the outside — not a floating rounded tile with a gap under it. */
    border-radius: 0;
    border-bottom: 1px solid var(--rr-outline);
    margin-bottom: 0;
}

    .rr-card__media img {
        width: 100%;
        height: 100%;
        object-fit: contain;
        padding: 1.5rem;
        transition: transform .5s ease;
    }

/* A product whose image path is missing renders NOTHING here — _ResponsiveImage
   emits no <img> at all rather than an src="" the browser would resolve to the
   current page. That is the right call in the partial, but it leaves this box
   as a blank white square, and on a grid beside cards showing the generated
   "photo coming soon" tile a blank square reads as a page that failed to load
   rather than as a picture the shop has not taken yet.

   Two products on /shop are in exactly this state today. The tint and rule give
   the empty slot the same deliberate look as the generated tile, so a gap in
   the catalogue never looks like a bug in the site. :has() is progressive — a
   browser without it simply shows the blank square it shows now. */
@supports selector(:has(img)) {
    /* Tint only. The card itself is bordered since the handoff, so the ring this used to draw
       would now read as a box inside a box. */
    .rr-card__media:not(:has(img)) {
        background: var(--rr-surface-2);
    }

        .rr-card__media:not(:has(img))::after {
            content: "Photo coming soon";
            position: absolute;
            inset: 0;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 1rem;
            font-family: var(--rr-font-body);
            font-size: var(--rr-fs-2xs);
            letter-spacing: var(--rr-ls-wide);
            text-transform: uppercase;
            text-align: center;
            color: var(--rr-muted);
        }
}

@media (min-width: 768px) {
    .rr-card__media img {
        padding: 1.25rem;
    }
}

.rr-card:hover .rr-card__media img,
.rr-card__media:focus-visible img {
    transform: scale(1.05);
}

.rr-card__save {
    position: absolute;
    top: .75rem;
    right: .75rem;
    z-index: 2;
}

.rr-card__save .rr-icon-btn {
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
}

.rr-card__save .rr-icon-btn:hover,
.rr-card__save .rr-icon-btn:focus-visible,
.rr-card__save .rr-icon-btn.is-active {
    color: var(--rr-black);
    border-color: var(--rr-gold);
    background: var(--rr-gold-soft);
}

.rr-card__eyebrow {
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
    margin-bottom: .25rem;
}

.rr-card__name {
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: var(--rr-fs-2xl);
    line-height: 1.2;
    text-transform: uppercase;
    color: var(--rr-black);
    margin-bottom: .5rem;
    /* Two lines' worth of box whether the name needs one line or two. Product
       names in this catalogue straddle the wrap point — "Grade1" beside "A11
       ReturnQuery Probe" — and without a floor the price row, the buttons and
       the quick-size caption all sit at a different height in each card of the
       same row. The eye reads that misalignment as the grid being broken. */
    min-height: calc(2 * 1.2 * var(--rr-fs-2xl));
}

.rr-card:hover .rr-card__name {
    color: var(--rr-gold-text);
}

.rr-card__name a {
    color: inherit;
    text-decoration: none;
}

.rr-card__name a:focus-visible {
    outline: 2px solid var(--rr-gold-deep);
    outline-offset: 3px;
}

.rr-card__prices {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: .5rem;
}

    /* The card's own price is a step BELOW the site-wide .rr-price, which the
       cart and summary panels still use at full size. In a four-up grid the
       price was the largest thing in the tile — larger than the product name
       above it — so the eye read a column of rupee figures and had to work back
       up to find out what each one was for. Name and price now sit at the same
       size and the price separates itself by weight and by the gold discount
       chip beside it, which is the job colour should be doing here. */
    .rr-card__prices .rr-price {
        font-size: var(--rr-fs-2xl);
    }

/* On a grid of cards a solid-gold chip in every tile is the brightest thing on the page — the
   same "gold with restraint" argument the action buttons below make. The handoff uses the soft
   tint here and keeps the solid fill for the single price on a product page. Black on #EFE3BE
   measures 14.9:1. */
.rr-card__prices .rr-discount {
    background: var(--rr-gold-soft);
}

.rr-card__actions {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: .5rem;
    margin-top: auto;
    padding-top: .75rem;
}

.rr-card__actions .btn {
    min-height: 38px;
    justify-content: center;
    padding-inline: .625rem;
    font-size: var(--rr-fs-2xs);
}

/* Gold restraint on a grid. One gold button is a call to action; the shop page
   renders eight of them above the fold and the effect inverts — the buttons
   became the brightest thing on the page and the product PHOTOGRAPHS, which are
   what a customer is actually there to look at, became the quietest. CLAUDE.md
   asks for gold "used with restraint", and a repeating grid is where that rule
   has to bite.

   So the card's primary drops to ink and gold is held back for the places where
   it is genuinely singular and genuinely the next step: the product page's Add
   to Cart, the cart and checkout CTAs, and Order on WhatsApp. The pair still
   reads as primary + secondary — solid vs outline — and gold returns on hover,
   which is where it now does the most work.

   To restore the previous look, delete this rule; nothing else depends on it. */
.rr-card__actions .btn-gold {
    --bs-btn-color: #ffffff;
    --bs-btn-bg: var(--rr-black);
    --bs-btn-border-color: var(--rr-black);
    --bs-btn-hover-color: var(--rr-black);
    --bs-btn-hover-bg: var(--rr-gold);
    --bs-btn-hover-border-color: var(--rr-gold);
    --bs-btn-active-color: var(--rr-black);
    --bs-btn-active-bg: var(--rr-gold);
    --bs-btn-active-border-color: var(--rr-gold);
}

.rr-card__actions .material-symbols-outlined {
    font-size: 17px;
}

.rr-card__quick-size {
    grid-column: 1 / -1;
    color: var(--rr-muted);
    font-size: var(--rr-fs-3xs);
    letter-spacing: var(--rr-ls-wide);
    text-align: center;
    text-transform: uppercase;
}

@media (max-width: 575.98px) {
    .rr-card__save {
        top: .5rem;
        right: .5rem;
    }

    .rr-card__save .rr-icon-btn {
        width: 38px;
        height: 38px;
    }

    /* The two actions cannot sit side by side in a ~180px column, so they stack
       — which made the control stack nearly as tall as the product photograph
       above it. Scrolling a phone, you saw buttons, not bats.

       The buttons stay (both paths still work, and nothing is hidden from
       assistive tech); they just stop competing with the product. Shorter, no
       icon — the labels already say "cart" and "now" — and the gaps closed up.
       Together that returns roughly 45px per card, which is most of a third
       card over a scroll. */
    .rr-card__actions {
        grid-template-columns: 1fr;
        gap: .375rem;
        padding-top: .625rem;
    }

    .rr-card__actions .btn {
        min-height: 34px;
    }

    .rr-card__actions .btn .material-symbols-outlined {
        display: none;
    }

    .rr-card__quick-size {
        grid-column: 1;
    }
}

/* --------------------------------------------------------------------------
   10. Prices
   -------------------------------------------------------------------------- */
.rr-price {
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: var(--rr-fs-4xl);
    line-height: 1;
    color: var(--rr-black);
}

.rr-price--lg {
    font-size: var(--rr-fs-7xl);
    color: var(--rr-gold-deep);
}

/* The product page's price — the largest figure on the site. Near-black rather
   than gold: the handoff gives the price the weight and the CTA the colour, so
   the two do not compete, and near-black on white sidesteps the gold-on-tint
   contrast problem the old panel had (see below) instead of managing it. */
.rr-price--hero {
    font-size: clamp(1.75rem, 2.4vw, 2.125rem);
    color: var(--rr-black);
}

.rr-price-mrp {
    font-family: var(--rr-font-body);
    font-size: var(--rr-fs-sm);
    color: var(--rr-muted);
    text-decoration: line-through;
}

.rr-price--lg + .rr-price-mrp {
    font-size: var(--rr-fs-xl);
}

.rr-price--hero + .rr-price-mrp {
    font-family: var(--rr-font-head);
    font-weight: 500;
    font-size: var(--rr-fs-3xl);
}

.rr-discount {
    display: inline-block;
    background: var(--rr-gold);
    color: var(--rr-black);
    font-family: var(--rr-font-body);
    font-weight: 700;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    padding: .25rem .5rem;
    border-radius: var(--rr-radius);
}

/* Beside the hero price, where a full-strength gold pill next to a gold CTA is
   two loud golds in one block. Black on gold-soft, so it is still a badge. */
.rr-discount--soft {
    background: var(--rr-gold-soft);
    font-family: var(--rr-font-head);
    font-size: var(--rr-fs-md);
    padding: .3125rem .625rem;
}

/* No panel any more. This was a gold-soft tinted box, and the handoff sets the
   price straight on the page — which also retires the two contrast overrides
   that used to live here: --rr-gold-deep measured 2.98:1 on gold-soft and
   --rr-muted 3.78:1, so both had to be darkened for the tint alone. On white
   the tokens are within their measured ranges as declared and neither needs
   scoping. The class stays because the size selector's flash targets it — and
   that flash is a ring drawn round the figures, which needs somewhere to be
   drawn. Hence padding, pulled back out by an equal negative margin on three
   sides: the block sits exactly where it would with neither, and the ring has
   room to appear. */
.rr-price-block {
    padding: .75rem;
    margin: -.75rem -.75rem 0;
}

.rr-price-block__figures {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: .875rem;
}

.rr-price-block__note {
    font-family: var(--rr-font-body);
    font-size: var(--rr-fs-xs);
    color: var(--rr-muted);
    margin: .25rem 0 0;
}

/* --------------------------------------------------------------------------
   11. Product detail — page grid, gallery, sizes, quantity, buy bar

   Rebuilt from the Claude Design handoff (2026-08-24). What changed and why:

     · ONE GRID, FOUR AREAS. The page was two independent Bootstrap rows with a
       tab strip under them. It is now a single grid — media / title / buy /
       specs / story — so the two columns are aligned by the same track list
       rather than by two rows that happen to use the same split. The gallery
       spans the title and specs rows, which is what puts the buy block
       directly beneath it.

     · THE TABS ARE GONE. Description and Specification each have a heading and
       a place on the page instead of one of them being hidden behind a control.
       .rr-tabs survives below because the admin product form extends it.

     · ONE GALLERY, TWO BEHAVIOURS. Every shot is rendered once. Above lg the
       stage shows the selected slide with a thumbnail rail beside it; below lg
       the same stage is a scroll-snap track with dots. No second markup for
       phones, and nothing rewrites a srcset at runtime.

   Not here, and not by omission: no stock line and no sold-out size chip (there
   is no stock tracking), and no bank-offer or GST-invoice panel (GST is
   included in the price and never broken out, and nothing here takes a card).
   -------------------------------------------------------------------------- */
/* The page block. It owns one thing — the gap under the breadcrumb — because
   everything else on the page belongs to the grid below, and a wrapper class
   round the shared breadcrumb partial just to hold a margin would be a class
   that exists for one declaration. */
.rr-pdp .rr-breadcrumb {
    margin-bottom: 1rem;
}

/* The page is a shopping screen, not an article: the customer wants the picture,
   the price and the size buttons in one view. Section padding is pulled in from
   the site default here (2026-08-25) for that reason and no other. */
.rr-pdp.rr-section {
    padding-block: var(--rr-sp-4) var(--rr-section-gap-sm);
}

@media (min-width: 992px) {
    .rr-pdp.rr-section {
        padding-block: var(--rr-sp-5) var(--rr-section-gap);
    }
}

.rr-pdp__grid {
    display: grid;
    gap: 1.75rem;
    grid-template-areas:
        "media"
        "title"
        "buy"
        "specs"
        "story";
}

/* The handoff's desktop split is 640 / 584 inside a 1280 container. Written as a
   ratio rather than a fixed 640px so the gallery keeps its share of a narrower
   container instead of pinning one column and letting the other absorb every
   pixel of the difference. */
@media (min-width: 992px) {
    .rr-pdp__grid {
        /* Was 1.1fr / 1fr. Evened up (2026-08-25): the picture is a 1:1 square, so
           every pixel of extra width it takes is a pixel of extra page height, and
           the specification beside it was the column doing the wrapping. */
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        column-gap: 2.75rem;
        row-gap: 2rem;
        /* Client's arrangement (2026-08-25): the picture and the buying controls
           are one column, the reading matter — name, specification, description —
           is the other. The gallery spans two rows, so the buy block lands
           directly under it and the specification runs down the column beside the
           picture rather than under it.

           THE GALLERY SPANS EVERY TEXT ROW, and that is load-bearing. It used to
           span two (title, specs), and a grid track pair that has to fit a 499px
           square but only 328px of text in it splits the surplus BETWEEN the two
           rows — which is what put ~117px of blank page under the name and another
           ~118px under the specification table. Spanning the whole stack means the
           text column is always the taller of the two, so there is no surplus to
           distribute and the blocks sit one row-gap apart as written. */
        grid-template-areas:
            "media title"
            "media specs"
            "media story"
            "media buy";
        /* Everything takes its content height; there is no track left over to be
           shared out between rows. */
        align-items: start;
    }
}

.rr-pdp__media {
    grid-area: media;
}

/* The gallery holds still while the text column moves past it, and stops at the
   end of the grid — which is where "From the same bench" begins, so the picture
   never travels into the next section. Sticky needs two things to work here: an
   item shorter than its own grid area (the gallery spans four rows and fills
   one), and align-self other than stretch, which the grid's align-items: start
   already gives it. Below lg there is one column and nothing to hold still. */
@media (min-width: 992px) {
    .rr-pdp__media {
        position: sticky;
        top: calc(var(--rr-header-h) + 1.5rem);
    }
}

.rr-pdp__title {
    grid-area: title;
}

.rr-pdp__buy {
    grid-area: buy;
}

/* From lg up the buy block sits under the gallery with the text column ending
   above it, so there is open page to its right — the picture and the controls
   are simply the taller of the two columns. A frame is what makes that read as
   a purchase panel with room around it rather than as a hole in the layout.
   Borders over shadows, per the design system. No frame on a phone, where the
   block is the full width of the screen and a border would only inset it. */
@media (min-width: 992px) {
    .rr-pdp__buy {
        background: var(--rr-surface);
        border: 1px solid var(--rr-outline);
        border-radius: var(--rr-radius);
        padding: 1.25rem;
    }
}

.rr-pdp__specs {
    grid-area: specs;
}

.rr-pdp__story {
    grid-area: story;
}

/* The prose and the highlight list are the same components the home page uses,
   at the home page's reading measure. In a half-width column beside the buy
   block they only need to be tighter, so the compaction is scoped here rather
   than done to the shared classes. */
.rr-pdp__story .rr-prose {
    font-size: var(--rr-fs-lg);
}

.rr-pdp .rr-checklist {
    margin-top: 1rem;
}

    .rr-pdp .rr-checklist li {
        gap: .5rem;
        margin-bottom: .5rem;
        font-size: var(--rr-fs-md);
    }

    .rr-pdp .rr-checklist .material-symbols-outlined {
        font-size: var(--rr-fs-2xl);
    }

/* Section heading inside the page — About this bat, Specification, From the
   same bench. One size for all three, so they read as peers. */
/* Section heading inside the page. Dropped from clamp(1.5rem, 2.5vw, 2rem) to
   a flat 20px (2026-08-25): at 32px these read as page titles competing with
   the product name, and each one cost a line of height in a column that has
   three of them. The short gold rule under the word is what now marks them as
   section heads — colour and a 28px line instead of six extra points of type. */
.rr-pdp__heading {
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: var(--rr-fs-3xl);
    line-height: 1.1;
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    color: var(--rr-black);
    margin: 0 0 .75rem;
    padding-bottom: .375rem;
    background-image: linear-gradient(var(--rr-gold), var(--rr-gold));
    background-repeat: no-repeat;
    background-size: 28px 2px;
    background-position: left bottom;
}

/* The rail arrows sit on the same baseline as this heading, and a rule hanging
   under it there would collide with the row's own bottom border. */
.rr-section-head .rr-pdp__heading {
    padding-bottom: 0;
    background-image: none;
}

.rr-pdp__sku {
    font-family: var(--rr-font-body);
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
    margin: .375rem 0 0;
}

.rr-pdp__field {
    margin-top: 1.125rem;
}

.rr-pdp__field-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .75rem;
    margin-bottom: .5rem;
}

/* Inline variant: the quantity stepper with its dispatch note beside it. Wraps
   rather than shrinking, because the note is a sentence and the stepper is a
   44px touch target that may not lose width to it. */
.rr-pdp__field--inline {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 1rem;
}

.rr-pdp__field-note {
    font-size: var(--rr-fs-xs);
    line-height: var(--rr-lh-body);
    color: var(--rr-muted);
    margin: .5rem 0 0;
}

.rr-pdp__cta {
    display: flex;
    flex-direction: column;
    gap: .5rem;
    margin-top: 1.25rem;
}

/* The handoff's two CTAs are deliberately different heights — the gold one is
   taller than the outline one, which is most of what makes it read as the
   primary action without a second colour being introduced. */
.rr-pdp__cta .btn {
    width: 100%;
    height: 3rem;
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: var(--rr-fs-2xl);
    letter-spacing: var(--rr-ls-wide);
}

.rr-pdp__cta-primary.btn {
    height: 3.25rem;
    font-size: var(--rr-fs-3xl);
}

.rr-pdp__cta-note {
    font-size: var(--rr-fs-2xs);
    line-height: var(--rr-lh-body);
    color: var(--rr-muted);
    text-align: center;
    margin: 0;
}

/* Wishlist. A quiet text control under the two CTAs rather than a third button
   beside them: the handoff has no wishlist on this screen at all, and the
   feature exists, so it is kept at the weight of a secondary action instead of
   being deleted or promoted. */
.rr-pdp__save {
    display: flex;
    justify-content: center;
}

.rr-textbtn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .375rem;
    /* 44px of target on a 13px label — the text is small, the control is not. */
    min-height: 44px;
    padding: 0 .5rem;
    border: 0;
    background: transparent;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-sm);
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    color: var(--rr-muted);
    transition: color .2s ease;
}

    .rr-textbtn .material-symbols-outlined {
        font-size: 20px;
    }

    .rr-textbtn:hover,
    .rr-textbtn:focus-visible {
        color: var(--rr-black);
    }

    .rr-textbtn[aria-pressed="true"] {
        color: var(--rr-gold-text);
    }

/* --------------------------------------------------------------------------
   11.1 Gallery
   -------------------------------------------------------------------------- */
.rr-gallery {
    display: flex;
    flex-direction: column;
    gap: .75rem;
}

@media (min-width: 992px) {
    .rr-gallery {
        flex-direction: row;
        gap: 1rem;
    }
}

/* Thumbnails. The handoff's vertical rail beside the picture from lg up, and
   nothing at all below it: on a phone the swipe IS the control and the dots
   report the position, so a strip of thumbnails would be a second, weaker copy
   of both. Nothing is lost to assistive tech by hiding them: below lg every
   slide is laid out and in the accessibility tree with its own alt text, which
   is what the thumbnails were standing in for above lg, where all but one slide
   is display: none. */
.rr-gallery__rail {
    display: none;
}

@media (min-width: 992px) {
    .rr-gallery__rail {
        display: flex;
        flex: 0 0 68px;
        flex-direction: column;
        gap: .5rem;
    }
}

.rr-thumb {
    aspect-ratio: 1 / 1;
    flex: 0 0 68px;
    width: 68px;
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    overflow: hidden;
    padding: .3125rem;
    cursor: pointer;
    transition: border-color .2s ease;
}

    .rr-thumb img {
        width: 100%;
        height: 100%;
        object-fit: contain;
    }

    .rr-thumb:hover {
        border-color: var(--rr-gold-deep);
    }

    .rr-thumb.is-active {
        border-color: var(--rr-black);
    }

/* The stage. On a phone this is a real scroll container — one slide wide, snap
   points on each — so the gallery swipes with the script absent. From lg up
   only the selected slide is laid out, so there is nothing to scroll and the
   thumbnail rail is the control. */
.rr-gallery__stage {
    position: relative;
    order: 1;
    display: flex;
    gap: .75rem;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
}

    .rr-gallery__stage::-webkit-scrollbar {
        display: none;
    }

@media (min-width: 992px) {
    .rr-gallery__stage {
        order: 0;
        flex: 1 1 0;
        min-width: 0;
        overflow: visible;
    }
}

.rr-gallery__slide {
    flex: 0 0 100%;
    scroll-snap-align: start;
    aspect-ratio: 1 / 1;
    background: var(--rr-surface-2);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    padding: 1rem;
    overflow: hidden;
}

    .rr-gallery__slide img {
        width: 100%;
        height: 100%;
        object-fit: contain;
    }

@media (min-width: 992px) {
    /* One slide at a time above lg — the rail is the control there, and a second
       square in the flow would halve the picture. */
    .rr-gallery__slide {
        display: none;
        padding: 1rem;
    }

    .rr-gallery__slide.is-active {
        display: block;
    }
}

/* The eyebrow, over the picture. Gold-soft with black on it, never gold text. */
.rr-gallery__badge {
    position: absolute;
    left: .875rem;
    top: .875rem;
    z-index: 1;
    background: var(--rr-gold-soft);
    color: var(--rr-black);
    font-family: var(--rr-font-body);
    font-weight: 700;
    font-size: var(--rr-fs-3xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    padding: .25rem .5rem;
    border-radius: var(--rr-radius);
}

/* Position dots. The phone's indicator only — above lg the thumbnail rail says
   the same thing and these would be a second, quieter copy of it. */
.rr-gallery__dots {
    order: 3;
    display: flex;
    justify-content: center;
    gap: .375rem;
}

@media (min-width: 992px) {
    .rr-gallery__dots {
        display: none;
    }
}

.rr-gallery__dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--rr-outline);
    transition: background-color .2s ease;
}

    .rr-gallery__dot.is-active {
        background: var(--rr-black);
    }

/* --------------------------------------------------------------------------
   11.2 Size, quantity
   -------------------------------------------------------------------------- */
.rr-field-label {
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
}

.rr-hint {
    font-size: var(--rr-fs-2xs);
    color: var(--rr-muted);
    font-style: italic;
}

.rr-sizes {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
}

/* A square tile, not a chip: the second line carries THAT SIZE'S price, because
   on this catalogue every size is separately priced and choosing one is
   choosing a price. */
.rr-size {
    display: grid;
    place-items: center;
    gap: 1px;
    /* 74px square was a tile the size of a thumbnail; 56px still clears the 44px
       touch minimum with room over and takes a third less of the buy column. */
    min-width: 3.75rem;
    height: 3.5rem;
    padding: 0 .5rem;
    background: var(--rr-surface);
    color: var(--rr-ink);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    cursor: pointer;
    transition: border-color .2s ease, background-color .2s ease, color .2s ease;
}

.rr-size__label {
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: var(--rr-fs-2xl);
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    line-height: 1;
}

.rr-size__note {
    font-family: var(--rr-font-body);
    font-size: var(--rr-fs-3xs);
    letter-spacing: var(--rr-ls-wide);
    line-height: 1;
    opacity: .72;
}

    .rr-size:hover {
        border-color: var(--rr-black);
    }

    /* Black, not gold. The handoff marks the chosen size by inverting the tile,
       which keeps gold for the one thing on the page that has to be the loudest
       — the CTA — and means the selection reads at a glance on a row of five. */
    .rr-size.is-active {
        background: var(--rr-black);
        border-color: var(--rr-black);
        color: #ffffff;
    }

.rr-qty {
    display: flex;
    align-items: center;
    width: 8.5rem;
    height: 3rem;
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    overflow: hidden;
}

    .rr-qty button {
        width: 2.75rem;
        height: 100%;
        border: 0;
        background: transparent;
        color: var(--rr-black);
        display: flex;
        align-items: center;
        justify-content: center;
    }

        .rr-qty button:hover {
            background: var(--rr-surface-2);
        }

    .rr-qty input {
        flex: 1 1 auto;
        width: 100%;
        min-width: 0;
        border: 0;
        text-align: center;
        font-family: var(--rr-font-body);
        font-weight: 600;
        background: transparent;
        outline: none;
        -moz-appearance: textfield;
    }

        .rr-qty input::-webkit-outer-spin-button,
        .rr-qty input::-webkit-inner-spin-button {
            -webkit-appearance: none;
            margin: 0;
        }

/* The product page's stepper: the handoff's 46px controls, a Barlow figure
   between them and a hairline on each side of it. A modifier rather than a new
   base, because the same component is the cart line's stepper and the drawer's
   (.rr-qty--sm) and neither of those wanted a 22px number. */
.rr-qty--lg {
    width: auto;
    height: 2.875rem;
}

    .rr-qty--lg button {
        width: 2.875rem;
        background: var(--rr-surface);
    }

        .rr-qty--lg button:first-child {
            border-right: 1px solid var(--rr-outline);
        }

        .rr-qty--lg button:last-child {
            border-left: 1px solid var(--rr-outline);
        }

    .rr-qty--lg input {
        flex: 0 0 3.625rem;
        width: 3.625rem;
        font-family: var(--rr-font-head);
        font-weight: 700;
        font-size: var(--rr-fs-4xl);
        color: var(--rr-black);
    }

/* Compacted 2026-08-25. The title was clamp(2.25rem, 4vw, 3rem) and at desktop
   width it ran to two lines of 48px caps, which pushed the SKU, the price and
   the size tiles below the fold on a laptop. 32px caps still reads as the
   loudest text on the page — nothing else here is above 22px. */
.rr-product-title {
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: clamp(1.75rem, 2.6vw, 2rem);
    line-height: 1.02;
    letter-spacing: var(--rr-ls-ui);
    text-transform: uppercase;
    color: var(--rr-black);
    margin: 0;
}

/* --------------------------------------------------------------------------
   11.3 Sticky buy bar — phones only

   Sits ABOVE the bottom nav rather than over it (bottom: 64px is that nav's
   height, section 14), and repeats the two things the customer has scrolled
   past: which size is chosen and what it costs. The button is the same submit
   as the one in the buy block, joined to the same hidden form, so there is one
   add-to-cart path and not two.
   -------------------------------------------------------------------------- */
.rr-buybar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 64px;
    z-index: 1034; /* under .rr-bottom-nav (1035) — it is the bar's floor */
    display: flex;
    align-items: center;
    gap: .75rem;
    padding: .75rem 1rem;
    background: var(--rr-surface);
    border-top: 1px solid var(--rr-outline);
}

@media (min-width: 992px) {
    .rr-buybar {
        display: none;
    }
}

.rr-buybar__price {
    flex: 0 0 auto;
}

.rr-buybar__size {
    display: block;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-3xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
}

.rr-buybar__amount {
    display: block;
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: var(--rr-fs-4xl);
    line-height: var(--rr-lh-head);
    color: var(--rr-black);
}

/* .btn is white-space: nowrap, so the label cannot wrap out of trouble — it has
   to fit. Measured on a 360px screen: 16px Barlow Condensed caps plus the glyph
   and the price beside it leave room to spare; 20px did not, and the label was
   clipped mid-word. */
.rr-buybar__cta.btn {
    flex: 1 1 auto;
    height: 3.25rem;
    padding-inline: .75rem;
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: var(--rr-fs-xl);
    letter-spacing: var(--rr-ls-wide);
}

/* Bottom nav (64) + bar (~76). Section 14 reserves the first; this adds the
   second, so the footer is not left under the bar.

   The bar also carries the same call to action as the floating WhatsApp button,
   in the same corner of the screen — that half of the problem is solved in
   section 14, beside .rr-fab itself, because a modifier of the same specificity
   declared before the block it modifies loses to it. */
@media (max-width: 991.98px) {
    body:has(.rr-buybar) {
        padding-bottom: 96px;
    }
}

/* --------------------------------------------------------------------------
   11.4 Rail navigation in a section head

   The arrows for "From the same bench". The home page's rail floats its
   controls over the ends of the track (.rr-rail__btn); this mounts them in the
   heading row instead, which is where the handoff draws them. The script picks
   one or the other from data-rr-rail-nav and adds .is-active only when the
   track genuinely overflows.
   -------------------------------------------------------------------------- */
.rr-railnav {
    display: flex;
    gap: .5rem;
    /* Hidden rather than absent so the heading row does not change height when
       the arrows appear. Nothing is rendered here at all without JS. */
    visibility: hidden;
}

    .rr-railnav.is-active {
        visibility: visible;
    }

.rr-railnav__btn {
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    background: var(--rr-surface);
    color: var(--rr-black);
    cursor: pointer;
    transition: border-color .2s ease, opacity .2s ease;
}

    .rr-railnav__btn .material-symbols-outlined {
        font-size: 20px;
    }

    .rr-railnav__btn:hover,
    .rr-railnav__btn:focus-visible {
        border-color: var(--rr-black);
    }

    /* Kept in the layout rather than removed at the ends — arrows that vanish
       make the heading row jump. */
    .rr-railnav__btn[disabled] {
        opacity: .35;
        cursor: default;
    }

/* Product tiles in a rail are wider than the category tiles it was built for,
   and they must not stretch: a grown 1:1 tile grows in both directions. */
.rr-rail__item--product {
    flex-basis: min(72vw, 304px);
    max-width: 304px;
}

@media (max-width: 991.98px) {
    .rr-railnav {
        display: none;
    }
}

/* --------------------------------------------------------------------------
   11.5 Prose, checklist, specification table
   -------------------------------------------------------------------------- */
.rr-related {
    margin-top: 40px;
}

@media (min-width: 768px) {
    .rr-related {
        margin-top: var(--rr-section-gap);
    }
}

/* A rule across the whole head rather than the short gold dash the other
   section headings carry — this one has controls on the right, and the rule is
   what ties the heading and the arrows into one row. */
.rr-section-head--ruled {
    align-items: center;
    border-bottom: 1px solid var(--rr-outline);
    padding-bottom: .625rem;
}

/* Kept for the admin product form, which extends it as .rr-tabs--admin
   (section 21). The storefront no longer has a tab strip: the product page's
   description and specification are now headings on the page. */
.rr-tabs.nav-tabs {
    border-bottom: 1px solid var(--rr-outline);
    gap: 3rem;
}

    .rr-tabs.nav-tabs .nav-link {
        font-family: var(--rr-font-head);
        font-weight: 700;
        font-size: var(--rr-fs-3xl);
        letter-spacing: var(--rr-ls-wide);
        text-transform: uppercase;
        color: var(--rr-muted);
        background: transparent;
        border: 0;
        border-bottom: 2px solid transparent;
        border-radius: 0;
        padding: 0 0 1rem;
    }

        .rr-tabs.nav-tabs .nav-link:hover {
            color: var(--rr-black);
        }

        .rr-tabs.nav-tabs .nav-link.active {
            color: var(--rr-black);
            border-bottom-color: var(--rr-gold);
        }

.rr-prose {
    font-size: var(--rr-fs-xl);
    line-height: var(--rr-lh-body);
    color: var(--rr-ink);
    max-width: 48rem;
}

    .rr-prose strong {
        color: var(--rr-black);
    }

.rr-checklist {
    list-style: none;
    padding: 0;
    margin: 1.5rem 0 0;
    max-width: 48rem;
}

    .rr-checklist li {
        display: flex;
        gap: .75rem;
        align-items: flex-start;
        margin-bottom: 1rem;
        color: #3f3f46;
    }

    .rr-checklist .material-symbols-outlined {
        color: var(--rr-gold-deep);
        font-size: var(--rr-fs-2xl);
        margin-top: .125rem;
    }

/* Specification key/value table. One full-width table down the column beside
   the gallery — not two half tables side by side, which is what it was, and
   which read as a layout rather than as a spec sheet. */
.rr-spec-table {
    width: 100%;
    margin: 0;
}

    /* Row padding was .9375rem top and bottom — 30px of air per row, so a six-row
       spec sheet stood as tall as the picture beside it. .4375rem keeps the rows
       separable (the rule under each one does that work) at half the height. */
    .rr-spec-table th,
    .rr-spec-table td {
        padding: .4375rem 0;
        border-bottom: 1px solid var(--rr-outline);
        vertical-align: top;
    }

    .rr-spec-table th {
        font-family: var(--rr-font-body);
        font-weight: 400;
        font-size: var(--rr-fs-2xs);
        letter-spacing: var(--rr-ls-caps);
        text-transform: uppercase;
        color: var(--rr-muted);
        text-align: left;
        /* 110px on a phone; 160px once there is room. The handoff's 210px key
           column left a gutter wide enough to read as a gap in the sheet. */
        width: 110px;
    }

@media (min-width: 992px) {
    .rr-spec-table th {
        width: 160px;
    }
}

    .rr-spec-table td {
        font-size: var(--rr-fs-md);
        line-height: 1.35;
        color: var(--rr-ink);
    }

    /* The last rule in the sheet doubles the block's own bottom edge. */
    .rr-spec-table tr:last-child th,
    .rr-spec-table tr:last-child td {
        border-bottom: 0;
    }

/* Breadcrumb */
.rr-breadcrumb {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .375rem;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
    margin: 0;
    padding: 0;
    list-style: none;
}

    .rr-breadcrumb a {
        color: var(--rr-muted);
        text-decoration: none;
    }

        .rr-breadcrumb a:hover {
            color: var(--rr-gold-text);
        }

    .rr-breadcrumb .material-symbols-outlined {
        font-size: 14px;
    }

    .rr-breadcrumb [aria-current="page"] {
        color: var(--rr-black);
    }

/* --------------------------------------------------------------------------
   11b. Listing pages — header band, toolbar, subcategory chips, pagination
   -------------------------------------------------------------------------- */
/* The vertical rhythm here was cut in half (B-UX2). On a 1366x768 laptop the old
   figures spent ~445px between the header and the first product row — breadcrumb
   gap, a 200px band, a chip row, a toolbar row — and pushed the grid entirely
   below the fold on the page that exists to show the grid. Nothing was removed;
   the blocks are simply no longer separated as though each were a section of its
   own. */
.rr-page-header {
    border-left: 4px solid var(--rr-gold);
    padding-left: 1.5rem;
    margin-block: 1.25rem 2rem;
}

    .rr-page-header h1 {
        font-size: clamp(1.75rem, 4vw, 2.625rem);
        line-height: 1;
        margin-bottom: .5rem;
    }

.rr-page-header__lede {
    font-size: var(--rr-fs-xl);
    line-height: var(--rr-lh-body);
    color: var(--rr-muted);
    max-width: 42rem;
    margin: 0;
}

/* --- The category head (Claude Design handoff, 2026-08-24) -----------------------------------
   Replaces .rr-category-hero, the scrimmed band that had the title laid over the photograph.
   Here the two sit side by side: display title and description on the left, the owner's banner
   in a bordered panel on the right.

   It still costs the page ONE block rather than the two the original .rr-category-banner cost,
   so the fold argument behind B-UX1 is intact — the panel is capped at 232px, which is close to
   the 168px band it replaces and nowhere near the ~400px ratio-driven strip that started all
   this. What it buys back is a description set as body copy in ink at a readable measure, and a
   banner presented as a photograph rather than as a texture behind white text.

   The copy column carries no left rule. The scrim version borrowed .rr-page-header's gold rule
   to keep the two openings alike, but against a bordered image panel a 4px gold bar on the
   opposite side reads as a third vertical element competing with the panel's own edge. */
.rr-category-intro {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    margin-block: 1rem 1.25rem;
}

.rr-category-intro__copy {
    /* flex:none while stacked, for the same reason as the panel below — a basis of 0 in a column
       container is a height, and this block's height is its text. */
    flex: none;
    min-width: 0;
    /* 600px in the handoff's 1280px container. Capped rather than fixed so the column still
       gives way on a narrow laptop instead of squeezing the panel beside it. */
    max-width: 37.5rem;
}

    /* Larger than .rr-page-header's h1 — this is the handoff's 64px display title, brought down
       to the type scale's top step the same way every other mockup size was (see the scale note
       in section 1: the mockups were set at large-print sizes for a 1440px artboard). */
    .rr-category-intro__copy h1 {
        font-size: clamp(2rem, 5vw, 3.125rem);
        line-height: .96;
        margin-bottom: .75rem;
    }

/* Ink, not muted, and no line clamp. On the scrim this was 14px white-on-photograph held to two
   lines; on white it is the page's opening paragraph and is allowed to be one. */
.rr-category-intro__lede {
    font-size: var(--rr-fs-xl);
    line-height: var(--rr-lh-body);
    color: var(--rr-ink);
    margin: 0;
    text-wrap: pretty;
}

/* flex:none below md, NOT flex:1 1 0. Stacked, the container is a COLUMN, so a flex-basis of 0
   resolves against the height — the panel collapsed to its 2px of border and the photograph
   vanished on every phone. The panel only becomes a flexible column at md, where the axis flips
   and a basis of 0 means what it was meant to mean. */
.rr-category-intro__media {
    flex: none;
    min-width: 0;
    height: 170px;
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    overflow: hidden;
}

    /* The <picture> _ResponsiveImage emits is an inline box by default and would not fill the
       panel; the rule targets both it and the <img> so a legacy bare <img> behaves the same. */
    .rr-category-intro__media picture,
    .rr-category-intro__img {
        display: block;
        width: 100%;
        height: 100%;
        object-fit: cover;
    }

@media (min-width: 768px) {
    .rr-category-intro {
        flex-direction: row;
        align-items: flex-start;
        gap: 2.5rem;
    }

    .rr-category-intro__copy {
        flex: 1 1 0;
    }

    .rr-category-intro__media {
        flex: 1 1 0;
        height: 232px;
    }
}

@media (min-width: 1200px) {
    .rr-category-intro {
        gap: 4rem;
    }
}

/* Subcategory chips — navigation, not filters. Rectangular at 8px like every control. */
.rr-subcats {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    margin-bottom: 1.25rem;
}

/* One control bar instead of two stacked rows. The subcategory chips and the
   count/sort row were separate blocks with 2rem between them and a rule under
   the second — three horizontal lines of furniture above the grid. Here they
   share a line (chips left, count and sort right) and one rule closes the whole
   head. Below ~992px they wrap onto their own lines again, which is the same
   layout as before, minus the gap. */
/* The handoff rules the bar top AND bottom, which is what turns it from "a line under the
   heading" into a band the count and the sort sit inside — and it is the one piece of furniture
   that separates the page head from the grid, so it should read as a band. */
.rr-listing-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: .75rem 1.5rem;
    padding-block: 1rem;
    margin-bottom: 1.5rem;
    border-top: 1px solid var(--rr-outline);
    border-bottom: 1px solid var(--rr-outline);
}

    .rr-listing-bar .rr-subcats {
        flex: 1 1 auto;
        /* A flex item is min-width:auto by default and refuses to shrink below its
           content. This one wraps internally, so letting it shrink is what keeps a
           long chip list inside the container instead of pushing the page sideways. */
        min-width: 0;
        margin-bottom: 0;
    }

    /* The toolbar keeps its own layout and gives up its frame — inside the bar the
       rule and the spacing belong to the bar. */
    /* 0 1 auto, not 0 0 auto: the count and the sort control together are ~420px of
       max-content, which is wider than the container on a small phone. Allowed to
       shrink, the toolbar wraps them internally; refused, it would push the whole
       page into a horizontal scroll. */
    .rr-listing-bar .rr-toolbar {
        flex: 0 1 auto;
        min-width: 0;
        gap: 1.25rem;
        padding-block: 0;
        margin-bottom: 0;
        border-top: 0;
        border-bottom: 0;
    }

        /* A category with no subcategories leaves the toolbar alone in the bar. It
           takes the full width back so the count and the sort return to opposite
           ends, exactly as they sit on the shop page. */
        .rr-listing-bar .rr-toolbar:only-child {
            flex: 1 1 auto;
            width: 100%;
        }

@media (max-width: 575.98px) {
    /* On a phone the bar is two stacked rows anyway, so the toolbar takes the whole
       line back and returns the count and the sort to opposite ends — the layout it
       had before the bar existed, minus the gap between the blocks. */
    .rr-listing-bar .rr-toolbar {
        flex: 1 1 100%;
    }
}

.rr-subcat {
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-xs);
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    color: var(--rr-black);
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    padding: .5rem 1rem;
    text-decoration: none;
    transition: border-color .2s ease, background-color .2s ease;
}

    .rr-subcat:hover {
        border-color: var(--rr-gold-deep);
        color: var(--rr-black);
    }

    .rr-subcat.is-active {
        background: var(--rr-gold);
        border-color: var(--rr-gold);
        color: var(--rr-black);
    }

/* Toolbar: product count + sort control */
.rr-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    /* Ruled top and bottom to match .rr-listing-bar — /shop and /search get the same band the
       category page does, so the three listing pages open identically. */
    padding-block: 1rem;
    margin-bottom: 2rem;
    border-top: 1px solid var(--rr-outline);
    border-bottom: 1px solid var(--rr-outline);
}

/* Sentence case, not letter-spaced caps. This line is a sentence now — "48 products · showing
   13 to 24" — and tracked uppercase is a label treatment: it slows down text that is meant to
   be read rather than scanned, and at .2em the string ran wide enough to crowd the sort control
   beside it. */
.rr-count {
    font-family: var(--rr-font-body);
    font-weight: 400;
    font-size: var(--rr-fs-md);
    letter-spacing: 0;
    color: var(--rr-muted);
}

/* The form is now just the row that holds the label and the shell; the frame moved onto
   .rr-sort__shell, which is the part the handoff actually draws a box around. */
.rr-sort {
    display: flex;
    align-items: center;
    gap: .75rem;
}

.rr-sort__label {
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
    margin: 0;
    white-space: nowrap;
}

/* The bordered control. Fixed minimum width so the bar does not reflow as the selected label
   changes length — "Relevance" and "Price: High to Low" differ by ~90px, and a control that
   resizes when you use it reads as the page jumping. */
.rr-sort__shell {
    position: relative;
    display: flex;
    align-items: center;
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    padding: .625rem .875rem;
    min-width: 14.125rem; /* 226px, the handoff's figure */
    transition: border-color .2s ease;
}

    .rr-sort__shell:hover {
        border-color: var(--rr-black);
    }

/* Decorative: the select carries the semantics and its own native arrow is suppressed below.
   --rr-gold-deep would be the token if this were gold; it is ink here, as the handoff has it. */
.rr-sort__chevron {
    font-size: 20px;
    color: var(--rr-black);
    margin-left: auto;
    pointer-events: none;
}

.rr-sort__select {
    /* Stretched over the whole shell so the chevron is part of the hit target rather than a
       dead 20px beside it — the native arrow it replaces was clickable, and this keeps that. */
    position: absolute;
    inset: 0;
    width: 100%;
    /* The shell's own padding, repeated here because the select is the element the text sits in.
       The right side leaves a lane for the chevron. */
    padding: .625rem 2.25rem .625rem .875rem;
    border: 0;
    background: transparent;
    outline: none;
    /* Suppress the platform arrow — the Material chevron beside it is the one the system uses.
       -webkit- and -moz- prefixes for older WebKit and Firefox, which still need them here. */
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    font-family: var(--rr-font-body);
    font-weight: 500;
    /* Sentence case, matching the handoff and the option labels as written. Uppercasing them in
       CSS gave "PRICE: LOW TO HIGH", which at .06em is wider than the shell on a phone. */
    font-size: var(--rr-fs-md);
    letter-spacing: 0;
    text-transform: none;
    color: var(--rr-ink);
    cursor: pointer;
}

    /* The options are drawn by the OS and inherit nothing useful from the transparent select —
       on Windows an unset background here renders black-on-black in dark mode. */
    .rr-sort__select option {
        color: var(--rr-ink);
        background: var(--rr-surface);
    }

/* Empty state */
.rr-empty {
    text-align: center;
    padding-block: 44px;
}

    /* Direct child only — the hero icon. A nested icon (e.g. inside the CTA)
       must keep its own sizing. */
    .rr-empty > .material-symbols-outlined {
        font-size: 48px;
        color: var(--rr-gold-deep);
    }

    .rr-empty h1,
    .rr-empty h2 {
        margin-block: 1rem .75rem;
    }

    .rr-empty p {
        color: var(--rr-muted);
        max-width: 32rem;
        margin: 0 auto 1.5rem;
    }

.rr-saved-head {
    display: flex;
    align-items: end;
    justify-content: space-between;
    gap: 2rem;
    margin-bottom: 2.5rem;
    padding-bottom: 1.5rem;
    border-bottom: 1px solid var(--rr-outline);
}

.rr-saved-head h1 {
    margin: .35rem 0 .5rem;
}

.rr-saved-head p {
    margin: 0;
    color: var(--rr-muted);
}

.rr-saved-head__count {
    flex: 0 0 auto;
    color: var(--rr-muted);
    font-size: var(--rr-fs-xs);
    font-weight: 600;
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
}

@media (max-width: 575.98px) {
    .rr-saved-head {
        display: block;
    }

    .rr-saved-head__count {
        display: block;
        margin-top: 1rem;
    }
}

/* Pagination */
.rr-pagination {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 1rem;
    margin-top: 4rem;
}

.rr-pagination__pages {
    display: flex;
    align-items: center;
    gap: .5rem;
}

.rr-page-step {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-xs);
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    color: var(--rr-black);
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    padding: .75rem 1.25rem;
    text-decoration: none;
    transition: border-color .2s ease, color .2s ease;
}

    .rr-page-step:hover {
        border-color: var(--rr-gold-deep);
        color: var(--rr-gold-text);
    }

    .rr-page-step .material-symbols-outlined {
        font-size: 18px;
    }

    .rr-page-step.is-disabled {
        color: var(--rr-muted);
        border-color: var(--rr-outline);
        opacity: .5;
        pointer-events: none;
    }

.rr-page-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-sm);
    color: var(--rr-black);
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    text-decoration: none;
    transition: border-color .2s ease;
}

    .rr-page-link:hover {
        border-color: var(--rr-gold-deep);
        color: var(--rr-black);
    }

    .rr-page-link.is-active {
        background: var(--rr-gold);
        border-color: var(--rr-gold);
        color: var(--rr-black);
    }

/* --------------------------------------------------------------------------
   12. Value points (The Royal Standard) + framed image
   -------------------------------------------------------------------------- */
.rr-value {
    display: flex;
    gap: 1.5rem;
}

.rr-value__icon {
    flex: 0 0 auto;
    width: 48px;
    height: 48px;
    border: 1px solid var(--rr-gold);
    border-radius: var(--rr-radius);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--rr-gold-deep);
}

.rr-value__title {
    font-size: var(--rr-fs-2xl);
    margin-bottom: .5rem;
}

.rr-value__body {
    color: var(--rr-muted);
    margin: 0;
}

.rr-framed {
    position: relative;
}

    .rr-framed::before {
        content: "";
        position: absolute;
        top: -16px;
        left: -16px;
        width: 120px;
        height: 120px;
        border-left: 2px solid var(--rr-gold);
        border-top: 2px solid var(--rr-gold);
        z-index: 0;
    }

    .rr-framed img {
        position: relative;
        z-index: 1;
        width: 100%;
        border-radius: var(--rr-radius);
    }

.rr-ratio-square img {
    aspect-ratio: 1 / 1;
    object-fit: cover;
}

.rr-ratio-portrait img {
    aspect-ratio: 4 / 5;
    object-fit: cover;
}

/* B4.1 — the frame for an uploaded editorial photograph. 16:9 to match the landscape
   image profile, so the stored crop and the displayed crop are the same picture and the
   browser has nothing left to trim. Applied only when a real photo is present; a slot
   still showing the square "photo coming soon" tile keeps .rr-ratio-square, which is what
   those sections looked like before any of this existed. See Models/ViewModels/ImageRatio. */
.rr-ratio-landscape img {
    aspect-ratio: 16 / 9;
    object-fit: cover;
}

/* --------------------------------------------------------------------------
   13. Footer — deliberate near-black block
   -------------------------------------------------------------------------- */
.rr-footer {
    background: var(--rr-black);
    color: rgba(255,255,255,.72);
}

/* The horizontal lockup replaced the footer's text wordmark. The width/height attributes on the
   tag reserve the right box before the image loads; this caps how large it actually draws.

   C4 wrapped the two cuts and the tagline in .rr-footer__lockup so all three share one box: the
   tagline centres on whatever width the artwork actually draws at, rather than on the column.
   The 1rem gap below the brand moved to the wrapper with them — left on the image it would have
   opened between the wordmark and its own tagline and split the lockup in two. */
.rr-footer__lockup {
    width: 100%;
    max-width: 180px;
    margin-bottom: 1rem;
}

.rr-footer__logo {
    display: block;
    width: 100%;
    max-width: 180px;
    height: auto;
}

/* Screen shows the reversed cut, print shows the black one — see the note in _Footer.cshtml. */
.rr-footer__logo--print {
    display: none;
}

/* The same lockup line as the header, two steps larger because the footer has the room the
   sticky bar does not — and so it is legible at every size here, including the 360px screens
   where the header drops it. --rr-gold reads 7.98:1 on the near-black band, which is the one
   place in the theme where the fill gold is also a valid text colour.

   Kept as a <p>, never a heading: this is the second line of a logo, not a section title. */
.rr-footer__tagline {
    margin: 0;
    padding-left: .22em;
    font-family: var(--rr-font-head);
    font-weight: 600;
    font-size: var(--rr-fs-xs);
    line-height: 1;
    letter-spacing: .22em;
    text-transform: uppercase;
    text-align: center;
    white-space: nowrap;
    color: var(--rr-gold);
}

@media print {
    .rr-footer__logo {
        display: none;
    }

    .rr-footer__logo--print {
        display: block;
    }

    /* The band's black ground does not print, and gold on white paper is 2.42:1 — the value
       CLAUDE.md rules out as text. The tagline follows the logo above it onto the single-colour
       black cut instead of being dropped: it is half of a lockup, and half a lockup on paper
       looks like a printing fault rather than a decision. */
    .rr-footer__tagline {
        color: var(--rr-black);
    }
}

    .rr-footer h2,
    .rr-footer h3,
    .rr-footer h4 {
        color: var(--rr-gold);
        font-size: var(--rr-fs-xl);
        letter-spacing: var(--rr-ls-caps);
        margin-bottom: 1.25rem;
    }

    .rr-footer a {
        color: rgba(255,255,255,.72);
        text-decoration: none;
    }

        .rr-footer a:hover,
        .rr-footer a:focus {
            color: var(--rr-gold);
        }

.rr-footer__social {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* flex-basis as well as width: the row is a flex container with gap and wrap, so without
       this a box could be shrunk by a neighbour and the four would stop matching. */
    flex: 0 0 40px;
    width: 40px;
    height: 40px;
    border: 1px solid rgba(255,255,255,.22);
    border-radius: var(--rr-radius);
    line-height: 1;
}

    /* B-UX4: one optical size for all four. The WhatsApp mark is an inline <svg> with no width,
       height or viewport-derived size of its own, so it filled the box while its Material Symbols
       neighbours sat at their inherited font-size — the mismatch the client reported. Both are
       pinned here, the same way .rr-btn-icon already pins the pair on the CTA buttons. */
    .rr-footer__social .material-symbols-outlined,
    .rr-footer__social svg {
        font-size: 20px;
        width: 20px;
        height: 20px;
        /* The glyph box is inline by default, so it sits on the text baseline and lands a
           pixel or two below the centre the flexbox worked out. */
        display: block;
        flex: 0 0 auto;
    }

/* The "Get In Touch" CTA. .rr-footer a paints every link in the column muted white, which would
   swallow the black label on the gold fill — so the button opts back out. Nothing else here:
   .btn-gold already carries the fill, hover and focus treatment. */
.rr-footer .btn-gold {
    color: var(--rr-black);
}

    .rr-footer .btn-gold:hover,
    .rr-footer .btn-gold:focus {
        color: #ffffff;
    }

/* B-UX6: the rule and the padding moved off .rr-footer__legal and onto this wrapper when the
   build credit became a second line. Left on the copyright, its 1.5rem bottom padding would have
   pushed the two fine-print lines a whole block apart. */
.rr-footer__bottom {
    border-top: 1px solid rgba(255,255,255,.12);
    padding-block: 1.5rem;
}

.rr-footer__legal {
    text-align: center;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: rgba(255,255,255,.45);
}

/* The IntelleScale build credit. rgba(255,255,255,.60) composites to #9F9F9F on the #0E0E0E
   band — 7.26:1, clear of AAA for normal text.

   It was .45 (#7A7A7A, 4.53:1): exactly the AA minimum for normal text, which at 11px is
   compliant on paper and invisible in practice. AA is a floor, not a target, and the smaller
   the type the less sense there is in sitting on the floor. A credit still has to be readable;
   "subtle" is not licence to make it grey mush.

   That does put the credit above the copyright line's .45, so spacing carries the hierarchy
   instead of dimness: the gap above is .75rem rather than .5rem, so this reads as a quieter
   line following the copyright rather than the brighter half of one two-line block. Case and
   tracking do the rest — the copyright is uppercase at .14em, this is sentence case and
   untracked, so it keeps far less width and shout at the same size. Contrast is the wrong
   lever for making something quiet; size, case and spacing are. */
.rr-footer__credit {
    margin-top: .75rem;
    text-align: center;
    font-size: var(--rr-fs-2xs);
    color: rgba(255,255,255,.60);
}

    /* The one link in the sentence. Brighter than the text around it (rgba(255,255,255,.72),
       10.1:1) and underlined, because inside a sentence a colour shift alone is not a reliable
       signal that a word is a link (WCAG 1.4.1). Hover and focus go gold at 7.98:1, and the
       global focus-visible ring in section 2 supplies the keyboard outline. */
    .rr-footer .rr-footer__credit-link {
        color: rgba(255,255,255,.72);
        text-decoration: underline;
        text-decoration-thickness: 1px;
        text-underline-offset: .2em;
    }

        .rr-footer .rr-footer__credit-link:hover,
        .rr-footer .rr-footer__credit-link:focus-visible {
            color: var(--rr-gold);
            text-decoration-thickness: 2px;
        }

/* The bottom bar prints as white text on white paper — browsers drop the footer's black
   background, which is why the logo above swaps to its black cut. The copyright already
   disappears there; a second invisible line would only add a blank gap to an order printout,
   and the agency credit is not something a customer needs on a receipt. */
@media print {
    .rr-footer__credit {
        display: none;
    }
}

/* --------------------------------------------------------------------------
   14. Floating WhatsApp button + mobile bottom nav
   -------------------------------------------------------------------------- */
.rr-fab {
    position: fixed;
    right: 16px;
    bottom: 80px;
    z-index: 1040;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    background: var(--rr-gold);
    color: var(--rr-black);
    border-radius: var(--rr-radius);
    border: 1px solid var(--rr-gold-deep);
    text-decoration: none;
    transition: background-color .2s ease, color .2s ease;
}

    .rr-fab:hover,
    .rr-fab:focus {
        background: var(--rr-gold-deep);
        color: #ffffff;
    }

    .rr-fab svg {
        width: 28px;
        height: 28px;
    }

/* A view that renders its own sticky WhatsApp bar (section 11.3) sets
   ViewData["FabDesktopOnly"], and the floating button stands down below lg
   rather than sitting on top of a bar saying the same thing.

   It has to be declared HERE, after .rr-fab, and not next to the bar in
   section 11: the modifier and the block are both one class, so the later of
   the two wins and .rr-fab { display: inline-flex } would take it back. */
@media (max-width: 991.98px) {
    .rr-fab--desktop-only {
        display: none;
    }
}

@media (min-width: 992px) {
    .rr-fab {
        bottom: 24px;
        right: 24px;
    }
}

.rr-bottom-nav {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1035;
    display: flex;
    background: var(--rr-surface);
    border-top: 1px solid var(--rr-outline);
}

    .rr-bottom-nav a {
        flex: 1 1 0;
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: .125rem;
        padding: .5rem 0 .625rem;
        color: var(--rr-muted);
        text-decoration: none;
        font-family: var(--rr-font-body);
        font-weight: 600;
        font-size: var(--rr-fs-3xs);
        letter-spacing: var(--rr-ls-wide);
        text-transform: uppercase;
    }

        .rr-bottom-nav a.active {
            color: var(--rr-gold-text);
        }

    .rr-bottom-nav .material-symbols-outlined {
        font-size: 22px;
    }

@media (max-width: 991.98px) {
    body {
        padding-bottom: 44px;
    }
}

@media (min-width: 992px) {
    .rr-bottom-nav {
        display: none;
    }
}

/* --------------------------------------------------------------------------
   14b. Editorial split band — full-bleed picture beside the copy

   The handoff's "Custom Bat Atelier" layout, which "The Royal Standard" on the home page now
   uses. One structural difference from the container-width row it replaces: the photograph runs
   to the PAGE edge rather than sitting inside .rr-container with a gold corner rule round it, so
   the picture reads as the band rather than as an illustration placed in it.

   Stacks below lg — picture first, then the copy — because a 50% column of body text at phone
   width is four words a line.
   -------------------------------------------------------------------------- */
.rr-split {
    display: flex;
    flex-direction: column;
    background: var(--rr-surface-2);
    border-top: 1px solid var(--rr-outline);
    border-bottom: 1px solid var(--rr-outline);
}

.rr-split__media {
    position: relative;
    min-height: 260px;
}

    /* _ResponsiveImage emits <picture><img>, and <picture> is an inline box with no size of its
       own — so the <img> is what gets positioned, against .rr-split__media above it. */
    .rr-split__media img {
        position: absolute;
        inset: 0;
        display: block;
        width: 100%;
        height: 100%;
        object-fit: cover;
    }

.rr-split__body {
    display: flex;
    align-items: center;
    padding: 28px var(--rr-container-pad-mobile);
}

.rr-split__inner {
    max-width: 30rem;
}

@media (min-width: 768px) {
    .rr-split__body {
        padding: 40px var(--rr-container-pad);
    }
}

@media (min-width: 992px) {
    .rr-split {
        flex-direction: row;
        align-items: stretch;
    }

    .rr-split__media,
    .rr-split__body {
        flex: 1 1 50%;
        min-width: 0;
    }

    .rr-split__media {
        min-height: 400px;
    }

    .rr-split__body {
        padding: 56px 72px 56px 56px;
    }
}

/* --------------------------------------------------------------------------
   15. Shipping banner — the light trust strip

   This was a second near-black band. The handoff keeps the footer as the storefront's ONLY
   near-black block, so the strip is now the same warm tint the other editorial bands use, ruled
   top and bottom. The highlights are separated by hairlines rather than by colour, which is the
   handoff's three-up treatment.
   -------------------------------------------------------------------------- */
.rr-shipping {
    background: var(--rr-surface-2);
    border-top: 1px solid var(--rr-outline);
    border-bottom: 1px solid var(--rr-outline);
    color: var(--rr-ink);
    padding-block: 28px;
}

    .rr-shipping h2 {
        color: var(--rr-black);
        font-size: clamp(1.5rem, 3.2vw, 2.0625rem);
        margin-bottom: .5rem;
    }

    /* --rr-muted is measured against white and drops to 4.51:1 on this tint — a hair above the
       AA floor. The darker token is 5.95:1 here and still plainly secondary. */
    .rr-shipping p {
        color: var(--rr-muted-on-tint);
        margin: 0;
    }

.rr-shipping__item {
    display: inline-flex;
    align-items: center;
    gap: .625rem;
    padding-left: 1.25rem;
    border-left: 1px solid var(--rr-outline);
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: var(--rr-fs-lg);
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    color: var(--rr-black);
}

    /* The handoff draws these icons at the Material Symbols 200 weight — a fine outline rather
       than the UI-weight glyph used in buttons and the header. Ink, not gold: three gold icons in
       a row is the fill doing decoration's job. */
    .rr-shipping__item .material-symbols-outlined {
        font-size: 32px;
        color: var(--rr-black);
        font-variation-settings: 'wght' 200;
    }

/* --------------------------------------------------------------------------
   16. Cart — page lines, order summary, header badge, offcanvas drawer
   -------------------------------------------------------------------------- */
.rr-cart-title {
    font-size: clamp(1.875rem, 4.8vw, 2.5rem);
    margin-bottom: 2.5rem;
}

.rr-cart-lines {
    list-style: none;
    margin: 0;
    padding: 0;
}

/* Two-row grid. Mobile keeps the image and copy together and drops the stepper
   and line total onto their own row; from md up it matches the mockup exactly:
   image | name/SKU/size | unit price over stepper | delete over line total. */
.rr-cart-line {
    display: grid;
    grid-template-columns: 96px minmax(0, 1fr) auto;
    grid-template-areas:
        "media info  remove"
        "media price price"
        "qty   qty   total";
    column-gap: 1rem;
    row-gap: .875rem;
    align-items: start;
    padding-bottom: 1.5rem;
    margin-bottom: 1.5rem;
    border-bottom: 1px solid var(--rr-outline);
}

@media (min-width: 768px) {
    .rr-cart-line {
        /* Fixed minimums on the price/stepper and remove/total columns so the
           three control columns line up across every row. */
        grid-template-columns: 128px minmax(0, 1fr) minmax(9rem, auto) minmax(6.5rem, auto);
        grid-template-areas:
            "media info price remove"
            "media info qty   total";
        column-gap: 1.5rem;
        row-gap: 1rem;
    }
}

.rr-cart-line__media {
    grid-area: media;
    aspect-ratio: 1 / 1;
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    overflow: hidden;
}

    .rr-cart-line__media img {
        width: 100%;
        height: 100%;
        object-fit: contain;
        padding: .5rem;
    }

.rr-cart-line__info {
    grid-area: info;
}

.rr-cart-line__name {
    font-size: var(--rr-fs-3xl);
    margin: 0 0 .375rem;
}

    .rr-cart-line__name a {
        color: var(--rr-black);
        text-decoration: none;
    }

        .rr-cart-line__name a:hover,
        .rr-cart-line__name a:focus {
            color: var(--rr-gold-text);
        }

.rr-cart-line__sku {
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    color: var(--rr-muted);
    margin: 0 0 .25rem;
}

.rr-cart-line__size {
    font-size: var(--rr-fs-sm);
    color: var(--rr-muted);
    margin: 0;
}

    .rr-cart-line__size strong {
        color: var(--rr-ink);
    }

.rr-cart-line__unit {
    grid-area: price;
    justify-self: start;
    margin: 0;
}

.rr-cart-line__qty {
    grid-area: qty;
    justify-self: start;
}

.rr-cart-line__remove {
    grid-area: remove;
    justify-self: end;
    color: var(--rr-muted);
}

.rr-cart-line__total {
    grid-area: total;
    justify-self: end;
    align-self: end;
    margin: 0;
}

@media (min-width: 768px) {
    .rr-cart-line__unit,
    .rr-cart-line__qty {
        justify-self: end;
    }
}

.rr-cart-back {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-xs);
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    color: var(--rr-muted);
    text-decoration: none;
    padding-top: .5rem;
}

    .rr-cart-back:hover,
    .rr-cart-back:focus {
        color: var(--rr-gold-text);
    }

    .rr-cart-back .material-symbols-outlined {
        font-size: 18px;
    }

/* Order summary panel */
.rr-cart-sticky {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
}

@media (min-width: 992px) {
    .rr-cart-sticky {
        position: sticky;
        top: calc(var(--rr-header-h) + 1.5rem);
    }
}

.rr-cart-summary {
    background: var(--rr-surface-2);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    padding: 1.75rem;
}

.rr-cart-summary__head {
    font-size: var(--rr-fs-3xl);
    border-bottom: 1px solid var(--rr-outline);
    padding-bottom: 1rem;
    margin-bottom: 1.5rem;
}

.rr-cart-summary__row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: .875rem;
    color: var(--rr-muted);
    font-size: var(--rr-fs-md);
}

    .rr-cart-summary__row .rr-price {
        font-size: var(--rr-fs-2xl);
    }

.rr-cart-summary__note {
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-2xs);
    text-align: right;
    color: var(--rr-ink);
}

.rr-cart-summary .rr-divider {
    margin-block: 1.25rem;
}

.rr-cart-summary__total {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 1.5rem;
}

.rr-cart-summary__total-label {
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: var(--rr-fs-3xl);
    text-transform: uppercase;
    letter-spacing: var(--rr-ls-ui);
    color: var(--rr-black);
}

.rr-cart-summary__total-value {
    text-align: right;
}

    .rr-cart-summary__total-value .rr-price--lg {
        display: block;
    }

.rr-cart-summary__gst {
    display: block;
    margin-top: .5rem;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-3xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
}

.rr-cart-summary__reassure {
    margin: 1rem 0 0;
    text-align: center;
    font-size: var(--rr-fs-xs);
    font-style: italic;
    color: var(--rr-muted);
}

/* The mockup's red-ruled callout, restated in gold */
.rr-note-gold {
    border-left: 4px solid var(--rr-gold);
    background: var(--rr-surface-2);
    border-radius: var(--rr-radius);
    padding: 1.25rem 1.5rem;
}

.rr-note-gold__head {
    font-family: var(--rr-font-body);
    font-weight: 700;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-gold-text);
    margin: 0 0 .5rem;
}

.rr-note-gold__body {
    font-size: var(--rr-fs-sm);
    color: var(--rr-muted);
    margin: 0;
}

/* Header cart icon badge */
.rr-cart-toggle,
.rr-counted-icon {
    position: relative;
}

.rr-wishlist-toast {
    position: fixed;
    z-index: 1085;
    top: calc(var(--rr-header-h) + 1rem);
    right: max(var(--rr-container-pad-mobile), calc((100vw - var(--rr-container-max)) / 2));
    max-width: min(22rem, calc(100vw - 2 * var(--rr-container-pad-mobile)));
    padding: .875rem 1rem;
    border: 1px solid var(--rr-outline);
    border-left: 4px solid var(--rr-gold);
    border-radius: var(--rr-radius);
    background: var(--rr-surface);
    color: var(--rr-ink);
    font-size: var(--rr-fs-xs);
    font-weight: 600;
}

.rr-wishlist-toast[hidden] {
    display: none;
}

.rr-cart-badge {
    position: absolute;
    top: 2px;
    right: 2px;
    min-width: 18px;
    height: 18px;
    padding: 0 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--rr-gold);
    color: var(--rr-black);
    border-radius: 9px;
    font-family: var(--rr-font-body);
    font-weight: 700;
    font-size: var(--rr-fs-3xs);
    line-height: 1;
}

    .rr-cart-badge[hidden] {
        display: none;
    }

/* Offcanvas drawer */
.rr-cart-drawer {
    width: 100%;
    max-width: 26rem;
    background: var(--rr-surface);
}

.rr-cart-drawer__head {
    /* The close control is .rr-icon-btn, not Bootstrap's .btn-close, so it needs
       the space-between that .btn-close's own margin-left:auto would have given. */
    justify-content: space-between;
    border-bottom: 1px solid var(--rr-outline);
    padding: 1rem 1.25rem;
}

.rr-cart-drawer__title {
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: var(--rr-fs-3xl);
    text-transform: uppercase;
    letter-spacing: var(--rr-ls-ui);
    color: var(--rr-black);
    margin: 0;
}

.rr-cart-drawer__body {
    padding: 1.25rem;
}

.rr-cart-drawer__lines {
    list-style: none;
    margin: 0;
    padding: 0;
}

.rr-drawer-line {
    display: grid;
    grid-template-columns: 96px minmax(0, 1fr) auto;
    grid-template-areas:
        "media info remove"
        "media qty  price";
    column-gap: .875rem;
    row-gap: .75rem;
    align-items: start;
    padding-bottom: 1rem;
    margin-bottom: 1rem;
    border-bottom: 1px solid var(--rr-outline);
}

.rr-drawer-line__media {
    grid-area: media;
    aspect-ratio: 1 / 1;
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    overflow: hidden;
}

    .rr-drawer-line__media img {
        width: 100%;
        height: 100%;
        object-fit: contain;
        padding: .375rem;
    }

.rr-drawer-line__info {
    grid-area: info;
    min-width: 0;
}

.rr-drawer-line__name {
    font-size: var(--rr-fs-lg);
    margin: 0 0 .25rem;
}

    .rr-drawer-line__name a {
        color: var(--rr-black);
        text-decoration: none;
    }

        .rr-drawer-line__name a:hover,
        .rr-drawer-line__name a:focus {
            color: var(--rr-gold-text);
        }

.rr-drawer-line__size {
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    color: var(--rr-muted);
    margin: 0;
}

.rr-drawer-line__remove {
    grid-area: remove;
    justify-self: end;
    width: 32px;
    height: 32px;
    color: var(--rr-muted);
}

    .rr-drawer-line__remove .material-symbols-outlined {
        font-size: 20px;
    }

.rr-drawer-line__qty {
    grid-area: qty;
    justify-self: start;
}

.rr-drawer-line__total {
    grid-area: price;
    justify-self: end;
    align-self: end;
    font-size: var(--rr-fs-2xl);
    margin: 0;
}

.rr-qty--sm {
    width: 6.25rem;
    height: 2.25rem;
}

    .rr-qty--sm button {
        width: 2rem;
    }

    .rr-qty--sm .material-symbols-outlined {
        font-size: 16px;
    }

    .rr-qty--sm input {
        font-size: var(--rr-fs-xs);
    }

.rr-cart-drawer__foot {
    border-top: 1px solid var(--rr-outline);
    background: var(--rr-surface-2);
    padding: 1.25rem;
}

    .rr-cart-drawer__foot .rr-cart-summary__row {
        margin-bottom: .5rem;
    }

.rr-cart-drawer__shipping {
    font-size: var(--rr-fs-2xs);
    font-style: italic;
    color: var(--rr-muted);
    margin: 0 0 .75rem;
}

.rr-cart-drawer__total span:first-child {
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: var(--rr-fs-2xl);
    text-transform: uppercase;
    letter-spacing: var(--rr-ls-ui);
    color: var(--rr-black);
}

.rr-cart-drawer__total .rr-price {
    font-size: var(--rr-fs-5xl);
    color: var(--rr-gold-deep);
}

.rr-cart-drawer__gst {
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-3xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
    margin: 0 0 1rem;
}

.rr-cart-drawer__link {
    display: block;
    margin-top: .875rem;
    text-align: center;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    color: var(--rr-muted);
    text-decoration: none;
}

    .rr-cart-drawer__link:hover,
    .rr-cart-drawer__link:focus {
        color: var(--rr-gold-text);
    }

.rr-empty--drawer {
    padding-block: 32px;
}

    .rr-empty--drawer h3 {
        margin-block: .875rem .5rem;
        font-size: var(--rr-fs-2xl);
    }

    .rr-empty--drawer p {
        font-size: var(--rr-fs-sm);
    }

/* --------------------------------------------------------------------------
   17. Forms — light fields on white
   The checkout mockup uses dark navy inputs on a light page, which contradicts
   the locked white/black/gold theme. Fields here are white with a 1px warm
   outline, near-black text and a gold focus ring, applied to every control.
   -------------------------------------------------------------------------- */
.form-control,
.form-select {
    background-color: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    color: var(--rr-ink);
    font-family: var(--rr-font-body);
    font-size: var(--rr-fs-md);
    padding: .875rem 1rem;
    box-shadow: none;
}

    .form-control::placeholder {
        color: var(--rr-muted);
        opacity: 1;
    }

    .form-control:focus,
    .form-select:focus {
        background-color: var(--rr-surface);
        border-color: var(--rr-gold);
        color: var(--rr-ink);
        box-shadow: 0 0 0 3px rgba(201, 162, 39, .3);
    }

    /* The gold ring is the focus affordance — the global outline would double it. */
    .form-control:focus-visible,
    .form-select:focus-visible {
        outline: none;
    }

.form-select {
    padding-right: 2.5rem;
    background-position: right 1rem center;
}

textarea.form-control {
    resize: vertical;
    min-height: 4rem; /* low enough that the rows attribute still drives the height */
}

/* Panel the form sits on, matching the summary panel beside it */
.rr-form-panel {
    background: var(--rr-surface-2);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    padding: var(--rr-pad-panel);
}

@media (min-width: 768px) {
    .rr-form-panel {
        padding: var(--rr-pad-panel-lg);
    }
}

.rr-form > * {
    margin-bottom: 1.5rem;
}

    .rr-form > *:last-child {
        margin-bottom: 0;
    }

/* One label + control + validation message. The single field primitive for the whole
   site — checkout, the enquiry form and both auth forms use it, so it is defined once
   here rather than restated per screen. Bare <div>s inside .rr-form get their spacing
   from `.rr-form > *`; .rr-field also works inside a Bootstrap column, where it is the
   only child and that rule cannot reach it. */
.rr-field {
    display: flex;
    flex-direction: column;
    /* Grid columns are min-width:auto by default, so a long placeholder or a wide
       select would otherwise widen the row rather than shrink the field. */
    min-width: 0;
}

    /* The control fills the field; the label and message sit tight against it. */
    .rr-field > .form-control,
    .rr-field > .form-select {
        width: 100%;
    }

.rr-label {
    display: block;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
    margin-bottom: .5rem;
}

.rr-label__optional {
    font-weight: 400;
    letter-spacing: var(--rr-ls-ui);
    text-transform: none;
}

/* The counterpart on a field that must be filled in. Same sentence-case treatment as
   __optional so the two read as a pair, but darker and heavier — required is the
   stronger statement, and it is said in words, never by colour alone. */
.rr-label__required {
    font-weight: 600;
    letter-spacing: var(--rr-ls-ui);
    text-transform: none;
    color: var(--rr-ink);
}

/* Country code + number read as one control */
.rr-phone {
    display: flex;
}

.rr-phone__code {
    flex: 0 0 5.5rem;
    width: 5.5rem;
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
    border-right-color: transparent;
    padding-right: 1.75rem;
    background-position: right .5rem center;
}

.rr-phone__number {
    flex: 1 1 auto;
    min-width: 0;
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
}

.rr-phone .form-control:focus,
.rr-phone .form-select:focus {
    position: relative;
    z-index: 1;
}

/* Validation. There is no red in this palette, so an invalid field is marked by a
   heavier near-black border and an iconned, bold message — never by colour alone. */
.field-validation-valid,
.validation-summary-valid {
    display: none;
}

.field-validation-error {
    display: flex;
    align-items: center;
    gap: .375rem;
    margin-top: .5rem;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-xs);
    color: var(--rr-ink);
}

    .field-validation-error::before {
        content: "error";
        font-family: "Material Symbols Outlined";
        font-size: var(--rr-fs-xl);
        font-weight: 400;
        line-height: 1;
        color: var(--rr-gold-deep);
    }

.form-control.input-validation-error,
.form-select.input-validation-error {
    border-color: var(--rr-ink);
    border-width: 2px;
    background-color: var(--rr-gold-soft);
    box-shadow: 0 0 0 3px var(--rr-outline);
}

.rr-field.has-validation-error .rr-label {
    color: var(--rr-black);
    font-weight: 700;
}

.rr-field.has-validation-error .rr-phone {
    border-radius: var(--rr-radius);
    box-shadow: 0 0 0 3px var(--rr-outline);
}

.rr-field.has-validation-error .rr-phone .form-control,
.rr-field.has-validation-error .rr-phone .form-select {
    box-shadow: none;
}

.rr-form__summary.validation-summary-errors {
    border-left: 4px solid var(--rr-ink);
    background: var(--rr-surface);
    border-radius: var(--rr-radius);
    padding: 1rem 1.25rem;
}

    .rr-form__summary.validation-summary-errors ul {
        margin: 0;
        padding-left: 1.1rem;
        font-size: var(--rr-fs-sm);
        font-weight: 600;
    }

/* --------------------------------------------------------------------------
   18. Checkout — progress steps, page head, compact order lines
   -------------------------------------------------------------------------- */
/* <nav> around the step list. The list does the layout; the wrapper's job is to keep
   the indicator inside the viewport on the narrowest phones, where three numbered
   steps, two connectors and the current step's label come close to 320px. */
.rr-steps {
    max-width: 100%;
}

@media (max-width: 400px) {
    .rr-steps .rr-steps__list {
        gap: .625rem;
    }

    .rr-steps .rr-step {
        gap: .5rem;
    }

    .rr-steps .rr-step + .rr-step::before {
        width: 20px;
    }
}

.rr-steps__list {
    list-style: none;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1rem;
    margin: 0 0 3rem;
    padding: 0;
}

@media (min-width: 768px) {
    .rr-steps__list {
        gap: 1.5rem;
        margin-bottom: 4rem;
    }
}

.rr-step {
    display: flex;
    align-items: center;
    gap: .75rem;
    color: var(--rr-muted);
}

    /* Connector rule between steps */
    .rr-step + .rr-step::before {
        content: "";
        display: block;
        width: 32px;
        height: 1px;
        background: var(--rr-outline);
    }

@media (min-width: 768px) {
    .rr-step + .rr-step::before {
        width: 64px;
    }
}

/* The upcoming step recedes, as in the mockup */
.rr-step:not(.rr-step--active):not(.rr-step--done) {
    opacity: .55;
}

.rr-step__num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    flex: 0 0 32px;
    border: 1px solid var(--rr-outline);
    border-radius: 50%;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-sm);
}

.rr-step__label {
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
}

.rr-step__link {
    display: flex;
    align-items: center;
    gap: .75rem;
    color: var(--rr-ink);
    text-decoration: none;
}

    .rr-step__link:hover,
    .rr-step__link:focus {
        color: var(--rr-gold-text);
    }

/* The mockup's red active step, in gold */
.rr-step--active {
    color: var(--rr-gold-text);
}

    .rr-step--active .rr-step__num {
        background: var(--rr-gold);
        border-color: var(--rr-gold);
        color: var(--rr-black);
    }

/* Only the current step keeps its label on small screens */
@media (max-width: 767.98px) {
    .rr-step:not(.rr-step--active) .rr-step__label {
        display: none;
    }
}

.rr-checkout-head {
    margin-bottom: 2.5rem;
}

    .rr-checkout-head h1 {
        font-size: clamp(1.875rem, 4.8vw, 2.5rem);
        margin: 0 0 .5rem;
    }

    .rr-checkout-head p {
        color: var(--rr-muted);
        margin: 0;
    }

/* Compact order lines, shared by the checkout panel and the confirmation summary */
.rr-order-lines {
    list-style: none;
    margin: 0;
    padding: 0;
    max-height: 400px;
    overflow-y: auto;
}

.rr-order-line {
    display: grid;
    grid-template-columns: 64px minmax(0, 1fr) auto;
    align-items: center;
    gap: 1rem;
    padding-bottom: 1rem;
    margin-bottom: 1rem;
    border-bottom: 1px solid var(--rr-outline);
}

    .rr-order-line:last-child {
        padding-bottom: 0;
        margin-bottom: 0;
        border-bottom: 0;
    }

.rr-order-line__media {
    aspect-ratio: 1 / 1;
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    overflow: hidden;
}

    .rr-order-line__media img {
        width: 100%;
        height: 100%;
        object-fit: contain;
        padding: .25rem;
    }

/* Middle cell of the line grid. minmax(0, 1fr) on the track is not enough on its own:
   the cell itself defaults to min-width:auto, so a long product name or SKU would push
   the price out of the panel instead of wrapping. */
.rr-order-line__info {
    min-width: 0;
    /* SKUs have no spaces to break at. */
    overflow-wrap: anywhere;
}

.rr-order-line__name {
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-xs);
    letter-spacing: var(--rr-ls-ui);
    text-transform: uppercase;
    color: var(--rr-black);
    margin: 0 0 .25rem;
}

.rr-order-line__meta {
    font-family: var(--rr-font-body);
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    color: var(--rr-muted);
    margin: 0;
}

.rr-order-line__price {
    margin: 0;
    font-size: var(--rr-fs-xl);
    white-space: nowrap;
}

/* The same callout holding a short stack of controls rather than a paragraph — a value to
   read, a button that acts on it, a line explaining what the button does. Tighter than the
   prose variant, and the gap replaces the children's own margins. */
.rr-note-gold--stack {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: .75rem;
    margin-top: .75rem;
    padding: 1rem 1.125rem;
}

    .rr-note-gold--stack > * {
        margin: 0;
        max-width: 100%;
    }

    /* The value being checked spans the panel; the button stays its own width. */
    .rr-note-gold--stack > .rr-url-readout {
        align-self: stretch;
    }

/* Info callout with a leading icon */
.rr-note-gold--icon {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
}

    .rr-note-gold--icon > .material-symbols-outlined {
        color: var(--rr-gold-deep);
        font-size: var(--rr-fs-3xl);
    }

/* --------------------------------------------------------------------------
   19. Order confirmation
   -------------------------------------------------------------------------- */
.rr-confirm {
    max-width: 40rem;
    margin-inline: auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
}

/* Gold-soft circle with a gold check — the mockup's green success mark, retoned */
.rr-confirm__mark {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 96px;
    height: 96px;
    border-radius: 50%;
    background: var(--rr-gold-soft);
    color: var(--rr-gold-text);
    margin-bottom: 1.5rem;
    animation: rr-mark-in .5s cubic-bezier(.175, .885, .32, 1.275) both;
}

    .rr-confirm__mark .material-symbols-outlined {
        font-size: 48px;
        font-variation-settings: 'FILL' 0, 'wght' 700, 'GRAD' 0, 'opsz' 48;
    }

@keyframes rr-mark-in {
    from {
        transform: scale(0);
        opacity: 0;
    }

    to {
        transform: scale(1);
        opacity: 1;
    }
}

@media (prefers-reduced-motion: reduce) {
    .rr-confirm__mark {
        animation: none;
    }
}

.rr-confirm__title {
    font-size: clamp(2.125rem, 6.4vw, 3.25rem);
    margin: 0 0 1.25rem;
}

.rr-confirm__ref {
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    padding: .5rem 1.5rem;
    margin: 0 0 2rem;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-xs);
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    color: var(--rr-muted);
}

    .rr-confirm__ref strong {
        color: var(--rr-black);
    }

.rr-confirm__message {
    font-size: var(--rr-fs-xl);
    line-height: var(--rr-lh-body);
    color: var(--rr-muted);
    max-width: 28rem;
    margin: 0 0 2rem;
}

.rr-confirm__cta {
    width: 100%;
    max-width: 24rem;
}

.rr-confirm__help {
    margin: 1.5rem 0 3rem;
    font-size: var(--rr-fs-md);
    color: var(--rr-muted);
}

    .rr-confirm__help strong {
        color: var(--rr-black);
    }

/* B6 — the fallback panel shown when no WhatsApp number is configured in Settings. The order is
   saved either way; this swaps the handoff for the shop's other contact details. Constrained and
   left-aligned inside the otherwise centred confirmation column. */
.rr-confirm__fallback {
    width: 100%;
    max-width: 30rem;
    margin: 0 0 3rem;
    text-align: left;
}

    .rr-confirm__fallback strong {
        color: var(--rr-black);
    }

.rr-confirm__summary {
    width: 100%;
    text-align: left;
    margin-bottom: 3rem;
}

.rr-confirm__summary-link {
    display: inline-block;
    margin-top: 1.25rem;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-xs);
    letter-spacing: var(--rr-ls-wide);
    color: var(--rr-gold-text);
}

/* Accordion, themed off the blue Bootstrap default */
.accordion {
    --bs-accordion-bg: var(--rr-surface);
    --bs-accordion-border-color: var(--rr-outline);
    --bs-accordion-border-radius: var(--rr-radius);
    --bs-accordion-inner-border-radius: var(--rr-radius);
    --bs-accordion-active-bg: var(--rr-surface-2);
    --bs-accordion-active-color: var(--rr-black);
    --bs-accordion-btn-focus-box-shadow: 0 0 0 3px rgba(201, 162, 39, .3);
    --bs-accordion-btn-active-icon: var(--bs-accordion-btn-icon);
}

.accordion-button {
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-black);
    padding: 1.25rem 1.5rem;
}

.accordion-body {
    padding: 1.5rem;
    border-top: 1px solid var(--rr-outline);
}

    /* Same rhythm the summary panel gives its dividers */
    .accordion-body .rr-divider {
        margin-block: 1.25rem;
    }

/* --------------------------------------------------------------------------
   20. Public order summary — receipt document
   -------------------------------------------------------------------------- */
.rr-receipt {
    max-width: 56rem;
    margin-inline: auto;
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    padding: var(--rr-pad-panel);
}

@media (min-width: 768px) {
    .rr-receipt {
        padding: var(--rr-pad-panel-lg);
    }
}

.rr-receipt__head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1.5rem;
    padding-bottom: 1.5rem;
    margin-bottom: 1.5rem;
    border-bottom: 1px solid var(--rr-outline);
}

.rr-receipt__title {
    font-size: clamp(1.5rem, 3.2vw, 1.875rem);
    margin: .25rem 0 0;
}

.rr-receipt__meta {
    display: flex;
    flex-wrap: wrap;
    gap: 2rem;
    margin: 0;
}

    .rr-receipt__meta dt {
        font-family: var(--rr-font-body);
        font-weight: 600;
        font-size: var(--rr-fs-2xs);
        letter-spacing: var(--rr-ls-caps);
        text-transform: uppercase;
        color: var(--rr-muted);
    }

    .rr-receipt__meta dd {
        font-family: var(--rr-font-head);
        font-weight: 700;
        font-size: var(--rr-fs-xl);
        color: var(--rr-black);
        margin: .25rem 0 0;
    }

.rr-receipt__table {
    --bs-table-border-color: var(--rr-outline);
    margin: 0;
}

    .rr-receipt__table thead th {
        font-family: var(--rr-font-body);
        font-weight: 600;
        font-size: var(--rr-fs-2xs);
        letter-spacing: var(--rr-ls-caps);
        text-transform: uppercase;
        color: var(--rr-muted);
        white-space: nowrap;
    }

    .rr-receipt__table tbody th {
        font-family: var(--rr-font-body);
        font-weight: 600;
        font-size: var(--rr-fs-md);
        color: var(--rr-black);
    }

    .rr-receipt__table td {
        font-size: var(--rr-fs-sm);
        color: var(--rr-ink);
        vertical-align: middle;
    }

    .rr-receipt__table .rr-price {
        font-size: var(--rr-fs-xl);
    }

    .rr-receipt__table tfoot th,
    .rr-receipt__table tfoot td {
        border-top: 2px solid var(--rr-ink);
        padding-top: 1rem;
    }

    .rr-receipt__table tfoot th {
        font-family: var(--rr-font-head);
        font-weight: 700;
        font-size: var(--rr-fs-2xl);
        text-transform: uppercase;
    }

.rr-receipt__sku {
    font-family: var(--rr-font-body);
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    color: var(--rr-muted);
    white-space: nowrap;
}

.rr-receipt__gst {
    margin: 1rem 0 0;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-3xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
}

.rr-receipt__parties {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1.5rem;
    margin-block: 2.5rem;
}

@media (min-width: 576px) {
    .rr-receipt__parties {
        grid-template-columns: 1fr 1fr;
    }
}

.rr-receipt__subhead {
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
    margin: 0 0 .5rem;
}

.rr-receipt__body {
    margin: 0;
    font-style: normal;
    font-size: var(--rr-fs-md);
    line-height: var(--rr-lh-body);
    color: var(--rr-ink);
}

.rr-receipt__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    padding-top: 1.5rem;
    border-top: 1px solid var(--rr-outline);
}

/* --------------------------------------------------------------------------
   21. Search — overlay (offcanvas-top) + results page
   Mockup deviations: gold underline and gold eyebrow instead of red, no stock
   labels, no popular/trending blocks, ₹ prices, and the thumbnail keeps the
   standard contained 1:1 treatment (no grayscale→colour hover).
   -------------------------------------------------------------------------- */
/* ⚠ TWO class names in this selector, deliberately. Do not "tidy" it back to a single
   .rr-search-overlay.

   Bootstrap 5.3 sizes the drawer with

       .offcanvas.offcanvas-top { height: var(--bs-offcanvas-height); max-height: 100% }

   which is specificity (0,2,0). A bare .rr-search-overlay is (0,1,0) and therefore LOSES
   to it however late this stylesheet is loaded — loading order only breaks ties between
   selectors of EQUAL specificity. That is not a theoretical point: it shipped, and the
   symptom was the search drawer stuck at Bootstrap's default 30vh, so on a ~620px-tall
   window the live results were clipped to about one and a half rows with two competing
   scrollbars. Matching (0,2,0) and relying on load order is what makes these declarations
   apply at all. */
.offcanvas.rr-search-overlay {
    /* The drawer sizes to its contents rather than to a fixed slice of the viewport. */
    height: auto;

    /* ⚠ Required, and not obvious. Bootstrap's base .offcanvas sets `bottom: 0`, and
       .offcanvas-top sets `top: 0` without ever resetting it. With Bootstrap's own definite
       height the box is over-constrained, so CSS ignores `bottom` and nobody notices. The
       moment `height: auto` takes effect, top AND bottom both apply and the drawer stretches
       to the full viewport — a correct-looking search bar with a huge empty white field
       under the results. `height: auto` on its own does NOT mean "size to content" here. */
    bottom: auto;

    /* dvh, not vh: on mobile the browser chrome shrinks the visible viewport, and vh keeps
       measuring the tall version — so the bottom of the drawer sits under the address bar.
       This also has to beat Bootstrap's own max-height:100% from the same rule above. */
    max-height: 100dvh;

    overflow-y: auto;
    background: var(--rr-surface);
    border-bottom: 1px solid var(--rr-outline);
}

/* The mockup's blurred scrim, over Bootstrap's flat backdrop. */
.offcanvas-backdrop.show {
    backdrop-filter: blur(6px);
    opacity: .6;
}

.rr-search-overlay__inner {
    max-width: calc(56rem + (var(--rr-container-pad) * 2));
    padding-block: 1.25rem 1.5rem;
}

@media (min-width: 768px) {
    .rr-search-overlay__inner {
        padding-block: 1.75rem 2rem;
    }
}

/* Results body.

   ⚠ This deliberately does NOT scroll. It used to cap at min(614px, 60dvh) with its own
   overflow-y, while .rr-search__list below capped at a fixed 26rem with another — two
   nested scroll regions over the same content. On a tall screen only the inner one moved;
   on a short one BOTH did, which is the pair of scrollbars visible in the bug report. One
   scroll region is the list (see below), so the "Live Results (n)" heading and the
   "View all" footer stay put while the rows move under them.

   Room for the list's scrollbar so it never sits on top of the results — the mockup's pr-2. */
.rr-search-overlay__body {
    padding-right: .5rem;
}

/* Input row — the mockup's rule, in gold */
.rr-search-bar {
    display: flex;
    align-items: center;
    gap: 1rem;
    border-bottom: 2px solid var(--rr-gold);
    padding-bottom: 1rem;
    margin-bottom: 2rem;
}

.rr-search-bar__icon {
    font-size: var(--rr-fs-5xl);
    color: var(--rr-gold-deep);
}

.rr-search-bar__input {
    flex: 1 1 auto;
    min-width: 0;
    border: 0;
    background: transparent;
    outline: none;
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: var(--rr-fs-4xl);
    letter-spacing: var(--rr-ls-ui);
    color: var(--rr-black);
    padding: 0;
}

@media (min-width: 768px) {
    .rr-search-bar__input {
        font-size: var(--rr-fs-5xl);
    }
}

    .rr-search-bar__input::placeholder {
        color: var(--rr-muted);
        opacity: .6;
    }

    /* Chrome's own clear button clashes with the close control beside it. */
    .rr-search-bar__input::-webkit-search-cancel-button {
        display: none;
    }

.rr-search-bar--page {
    max-width: 42rem;
    margin-inline: auto;
}

/* Prompt / status copy */
.rr-search__hint {
    font-family: var(--rr-font-body);
    font-size: var(--rr-fs-md);
    color: var(--rr-muted);
    margin: 0 0 1.5rem;
}

.rr-search__loading {
    display: flex;
    align-items: center;
    gap: .625rem;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
    margin: 1rem 0 0;
}

    .rr-search__loading .spinner-border {
        color: var(--rr-gold-deep);
    }

.rr-search__results-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 1rem;
}

/* LIVE RESULTS (n) / BROWSE BY CATEGORY */
.rr-search__results-title {
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
    margin: 0 0 1rem;
}

.rr-search__cats {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
}

/* Results list — the drawer's single scroll region, with a thin themed scrollbar.

   The cap is viewport-aware. A fixed 26rem (416px) is fine on a desktop but taller than the
   whole drawer on a laptop with a short window, which is what forced the body above to
   scroll as well.

   The 300px subtracted is the drawer's own furniture that the list has to share the screen
   with — the inner padding, the search bar, the "Live Results (n)" heading and the "View
   all" footer. Measured, not guessed: at a 522px-tall window a plain 45dvh cap left the
   drawer 9px taller than the viewport, which put a second scrollbar back on the overlay.
   Expressing it as "whatever the chrome leaves" is what keeps that true at other heights.

   max() is the floor: on a very short window the list keeps one usable row rather than
   collapsing, and the overlay's own overflow-y takes over as the safety valve.
   On any normal desktop the 26rem branch still wins, so nothing there changes. */
.rr-search__list {
    list-style: none;
    margin: 0;
    padding: 0 .5rem 0 0;
    display: flex;
    flex-direction: column;
    gap: .5rem;
    max-height: max(8rem, min(26rem, calc(100dvh - 300px)));
    overflow-y: auto;
    scrollbar-width: thin;
    scrollbar-color: var(--rr-gold-soft) transparent;
}

    .rr-search__list::-webkit-scrollbar {
        width: 6px;
    }

    .rr-search__list::-webkit-scrollbar-track {
        background: var(--rr-surface-2);
        border-radius: var(--rr-radius);
    }

    .rr-search__list::-webkit-scrollbar-thumb {
        background: var(--rr-gold-soft);
        border-radius: var(--rr-radius);
    }

        .rr-search__list::-webkit-scrollbar-thumb:hover {
            background: var(--rr-gold);
        }

/* One result row. The whole row is the link. */
.rr-search-row {
    display: grid;
    grid-template-columns: 80px minmax(0, 1fr);
    align-items: center;
    gap: 1rem;
    padding: .75rem;
    background: var(--rr-surface-2);
    border-left: 2px solid transparent;
    border-radius: var(--rr-radius);
    text-decoration: none;
    color: inherit;
    transition: background-color .2s ease, border-color .2s ease;
}

@media (min-width: 576px) {
    .rr-search-row {
        grid-template-columns: 80px minmax(0, 1fr) auto;
    }
}

    .rr-search-row:hover,
    .rr-search-row:focus-visible {
        background: var(--rr-gold-soft);
        border-left-color: var(--rr-gold);
        color: inherit;
    }

.rr-search-row__media {
    aspect-ratio: 1 / 1;
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    overflow: hidden;
}

    .rr-search-row__media img {
        width: 100%;
        height: 100%;
        object-fit: contain;
        padding: .25rem;
    }

.rr-search-row__info {
    display: block;
    min-width: 0;
}

.rr-search-row__eyebrow {
    display: block;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-3xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-gold-text);
    margin-bottom: .125rem;
}

.rr-search-row__name {
    display: block;
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: var(--rr-fs-2xl);
    line-height: 1.2;
    text-transform: uppercase;
    color: var(--rr-black);
}

.rr-search-row__support {
    display: flex;
    flex-wrap: wrap;
    gap: .25rem .875rem;
    font-family: var(--rr-font-body);
    font-size: var(--rr-fs-xs);
    color: var(--rr-muted);
    margin-top: .125rem;
}

.rr-search-row__sku {
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
}

.rr-search-row__prices {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: flex-start;
    gap: .5rem;
    grid-column: 2;
}

@media (min-width: 576px) {
    .rr-search-row__prices {
        grid-column: auto;
        justify-content: flex-end;
        text-align: right;
    }
}

    .rr-search-row__prices .rr-price {
        font-size: var(--rr-fs-3xl);
    }

/* View-all footer */
.rr-search__foot {
    display: flex;
    justify-content: center;
    border-top: 1px solid var(--rr-outline);
    margin-top: 1.5rem;
    padding-top: 1.5rem;
}

.rr-search__view-all {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-black);
    text-decoration: none;
    transition: color .2s ease;
}

    .rr-search__view-all:hover,
    .rr-search__view-all:focus {
        color: var(--rr-gold-text);
    }

    .rr-search__view-all .material-symbols-outlined {
        font-size: 18px;
        transition: transform .2s ease;
    }

    .rr-search__view-all:hover .material-symbols-outlined {
        transform: translateX(4px);
    }

/* No-results, inside the overlay */
.rr-search__empty {
    text-align: center;
    padding-block: 1.5rem 1rem;
}

    .rr-search__empty > .material-symbols-outlined {
        font-size: 40px;
        color: var(--rr-gold-deep);
    }

    .rr-search__empty h3 {
        font-size: var(--rr-fs-3xl);
        margin-block: .75rem .5rem;
    }

    .rr-search__empty p {
        font-size: var(--rr-fs-sm);
        color: var(--rr-muted);
        margin: 0 auto 1.25rem;
        max-width: 26rem;
    }

/* No-query state on the results page */
.rr-search-page__prompt {
    max-width: 42rem;
    margin-inline: auto;
    text-align: center;
}

    .rr-search-page__prompt .rr-search__cats {
        justify-content: center;
    }

/* --------------------------------------------------------------------------
   22. Auth — shared forms, desktop modal, full-page login/register
   The mockups drive the layout; the palette is the locked white/black/gold, so
   every red CTA, red focus accent and the register modal's red bottom bar are
   gold here. Fields inherit section 17 unchanged, so they match checkout.
   -------------------------------------------------------------------------- */

/* Checkbox: gold when ticked, gold ring on focus, square-ish at the system radius */
.form-check-input {
    border: 1px solid var(--rr-outline);
    border-radius: 3px;
    width: 1.15em;
    height: 1.15em;
    margin-top: .18em;
    background-color: var(--rr-surface);
    box-shadow: none;
}

    .form-check-input:checked {
        background-color: var(--rr-gold);
        border-color: var(--rr-gold);
    }

    .form-check-input:focus {
        border-color: var(--rr-gold);
        box-shadow: 0 0 0 3px rgba(201, 162, 39, .3);
    }

    .form-check-input:focus-visible {
        outline: none;
    }

.rr-check .form-check-label {
    font-size: var(--rr-fs-sm);
    color: var(--rr-ink);
    cursor: pointer;
}

.rr-check .form-check-input {
    cursor: pointer;
}

/* Password field + its visibility toggle */
.rr-password {
    position: relative;
}

.rr-password__input {
    padding-right: 3rem;
}

.rr-password__toggle {
    position: absolute;
    top: 0;
    right: 0;
    height: 100%;
    width: 3rem;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 0;
    background: transparent;
    color: var(--rr-muted);
    border-radius: 0 var(--rr-radius) var(--rr-radius) 0;
    transition: color .2s ease;
}

    .rr-password__toggle:hover,
    .rr-password__toggle[aria-pressed="true"] {
        color: var(--rr-gold-deep);
    }

    .rr-password__toggle .material-symbols-outlined {
        font-size: 20px;
    }

/* Remember me + Forgot password row */
.rr-auth-options {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}

.rr-auth-terms .form-check {
    display: flex;
    align-items: flex-start;
    gap: .625rem;
    padding-left: 0;
    margin: 0;
}

    .rr-auth-terms .form-check .form-check-input {
        flex: 0 0 auto;
        margin-left: 0;
    }

.rr-auth-terms .form-check-label {
    line-height: var(--rr-lh-ui);
}

.rr-auth-terms a {
    color: var(--rr-gold-text);
    font-weight: 600;
    text-underline-offset: 3px;
}

/* Headings and the switch-pane footer, shared by the modal and the pages */
.rr-auth__head {
    font-size: var(--rr-fs-6xl);
    margin: 0 0 .5rem;
}

.rr-auth__head--page {
    font-size: var(--rr-fs-7xl);
}

.rr-auth__lede {
    font-size: var(--rr-fs-sm);
    color: var(--rr-muted);
    margin: 0 0 1.75rem;
    max-width: 34rem;
}

.rr-auth__foot {
    margin-top: 1.75rem;
    padding-top: 1.5rem;
    border-top: 1px solid var(--rr-outline);
    text-align: center;
}

    .rr-auth__foot p {
        font-size: var(--rr-fs-sm);
        color: var(--rr-muted);
        margin: 0;
    }

/* The in-modal pane switch is a button, not a link — it navigates nothing. */
.rr-auth__switch,
.rr-auth__switch-link {
    display: inline-block;
    border: 0;
    background: transparent;
    padding: 0;
    margin-left: .25rem;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-sm);
    letter-spacing: var(--rr-ls-ui);
    text-transform: uppercase;
    color: var(--rr-gold-text);
    text-decoration: none;
}

    .rr-auth__switch:hover,
    .rr-auth__switch:focus,
    .rr-auth__switch-link:hover,
    .rr-auth__switch-link:focus {
        color: var(--rr-gold-text);
        text-decoration: underline;
        text-underline-offset: 4px;
    }

/* The mobile mockup's "OR" rule */
.rr-auth__or {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-top: 2.5rem;
    color: var(--rr-muted);
    font-size: var(--rr-fs-2xs);
    font-weight: 600;
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
}

    .rr-auth__or::before,
    .rr-auth__or::after {
        content: "";
        flex: 1 1 auto;
        border-top: 1px solid var(--rr-outline);
    }

.rr-auth__foot--page {
    border-top: 0;
    padding-top: 1rem;
    margin-top: 0;
}

    .rr-auth__foot--page p {
        margin-bottom: .375rem;
    }

    .rr-auth__foot--page .rr-auth__switch-link {
        margin-left: 0;
        font-family: var(--rr-font-head);
        font-weight: 700;
        font-size: var(--rr-fs-4xl);
        letter-spacing: var(--rr-ls-ui);
        color: var(--rr-black);
        border-bottom: 1px solid var(--rr-outline);
        padding-bottom: .125rem;
    }

        .rr-auth__foot--page .rr-auth__switch-link:hover,
        .rr-auth__foot--page .rr-auth__switch-link:focus {
            color: var(--rr-gold-text);
            border-bottom-color: var(--rr-gold);
            text-decoration: none;
        }

/* Desktop modal */
.rr-auth-modal .modal-dialog {
    max-width: 30rem;
}

.rr-auth-modal__content {
    position: relative;
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    overflow: hidden;
}

.rr-auth-modal__close {
    position: absolute;
    top: .75rem;
    right: .75rem;
    z-index: 2;
}

.rr-auth-modal__body {
    padding: 1.75rem 1.5rem 1.5rem;
}

    /* The pages get their breathing room from the lede paragraph; the modal has no
       lede, so the heading carries the mockup's gap to the first field itself. */
    .rr-auth-modal__body .rr-auth__head {
        margin-bottom: 1.75rem;
    }

/* The register mockup's red accent bar, restated in gold */
.rr-auth-modal__bar {
    flex: 0 0 auto;
    height: 6px;
    background: var(--rr-gold);
}

/* Full-page login / register.
   The mockups' decorative red glow becomes a barely-there gold wash. */
.rr-auth-page {
    position: relative;
    padding-block: 32px 48px;
    background:
        radial-gradient(60% 45% at 85% 0%, rgba(201, 162, 39, .10), transparent 70%),
        radial-gradient(50% 40% at 0% 100%, rgba(201, 162, 39, .06), transparent 70%);
}

.rr-auth-page__inner {
    width: 100%;
    max-width: 27rem;
    margin-inline: auto;
    padding-inline: var(--rr-container-pad-mobile);
}

.rr-auth-page__inner--wide {
    max-width: 34rem;
}

@media (min-width: 768px) {
    .rr-auth-page {
        padding-block: 48px 64px;
    }

    .rr-auth-page__inner {
        padding-inline: var(--rr-container-pad);
    }
}

/* One-shot notice carried across a redirect */
.rr-flash {
    display: flex;
    align-items: flex-start;
    gap: .625rem;
    margin-top: 1rem;
    padding: .875rem 1.125rem;
    background: var(--rr-surface-2);
    border: 1px solid var(--rr-outline);
    border-left: 4px solid var(--rr-gold);
    border-radius: var(--rr-radius);
}

    .rr-flash .material-symbols-outlined {
        font-size: 20px;
        color: var(--rr-gold-deep);
    }

    .rr-flash p {
        margin: 0;
        font-size: var(--rr-fs-sm);
        color: var(--rr-ink);
    }

    /* A cart revalidation can report more than one thing at once (a price move and a withdrawn
       item), so the flash stacks its messages rather than running them together. */
    .rr-flash p + p {
        margin-top: .375rem;
    }

/* B11 — the same notice when something did NOT work. The contact form can now fail for real (the
   enquiry could not be stored), and the default flash carries a gold rule and a tick, which would
   put a success mark next to an apology. Muted grey rather than red: nothing is broken from the
   customer's side and they are being handed an alternative, not warned. */
.rr-flash--problem {
    border-left-color: var(--rr-muted);
}

    .rr-flash--problem .material-symbols-outlined {
        color: var(--rr-muted);
    }

/* --------------------------------------------------------------------------
   23. My Account — shell, nav, logged-out panel, wishlist tiles, order cards
   (A8: my_wishlist_*, my_orders_*, no_orders_empty_state, order_details_desktop)
   -------------------------------------------------------------------------- */

/* Shell: tab strip over content on small screens, sidebar beside it from lg up. */
.rr-account {
    display: grid;
    gap: 2rem;
    /* A13: an implicit grid track is auto-sized, i.e. never narrower than its min-content. The
       mobile tab strip below is a flex row of three items whose min-content is ~470px, so the
       track — and with it the whole account page — grew past a 360px viewport and scrolled
       sideways. minmax(0, 1fr) lets the track shrink and hands the overflow to the strip's own
       overflow-x: auto, which is where it was always meant to live. */
    grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 992px) {
    .rr-account {
        grid-template-columns: 240px minmax(0, 1fr);
        gap: 3rem;
        align-items: start;
    }
}

.rr-account__main {
    min-width: 0;
}

/* Nav */
.rr-account-nav__head {
    display: none;
}

@media (min-width: 992px) {
    .rr-account-nav {
        position: sticky;
        top: calc(var(--rr-header-h) + 1.5rem);
    }

    .rr-account-nav__head {
        display: block;
        margin-bottom: 1.5rem;
    }
}

.rr-account-nav__title {
    font-size: var(--rr-fs-3xl);
    margin: 0;
}

.rr-account-nav__welcome {
    font-size: var(--rr-fs-xs);
    color: var(--rr-muted);
    margin: .25rem 0 0;
}

.rr-account-nav__list {
    list-style: none;
    margin: 0;
    padding: 0;
    /* Mobile: a horizontal, scrollable tab strip under a hairline, per my_orders_mobile. */
    display: flex;
    gap: .5rem;
    overflow-x: auto;
    scrollbar-width: none;
    border-bottom: 1px solid var(--rr-outline);
}

    .rr-account-nav__list::-webkit-scrollbar {
        display: none;
    }

@media (min-width: 992px) {
    .rr-account-nav__list {
        display: block;
        overflow: visible;
        border-bottom: 0;
    }
}

.rr-account-nav__link {
    display: inline-flex;
    align-items: center;
    gap: .625rem;
    white-space: nowrap;
    padding: .75rem 1rem;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-xs);
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    color: var(--rr-muted);
    text-decoration: none;
    border-bottom: 2px solid transparent;
    transition: color .2s ease, border-color .2s ease, background-color .2s ease;
}

    .rr-account-nav__link .material-symbols-outlined {
        font-size: 20px;
    }

    .rr-account-nav__link:hover {
        color: var(--rr-black);
    }

    /* The mockups' red active state, in gold. */
    .rr-account-nav__link.is-active {
        color: var(--rr-gold-text);
        border-bottom-color: var(--rr-gold);
    }

    .rr-account-nav__link.is-disabled {
        color: var(--rr-muted);
        opacity: .6;
        cursor: default;
    }

.rr-account-nav__soon {
    font-size: var(--rr-fs-3xs);
    font-style: normal;
    letter-spacing: var(--rr-ls-caps);
    color: var(--rr-black);
    background: var(--rr-gold-soft);
    border-radius: var(--rr-radius);
    padding: .125rem .375rem;
}

@media (min-width: 992px) {
    .rr-account-nav__link {
        display: flex;
        width: 100%;
        border-bottom: 0;
        border-left: 3px solid transparent;
        border-radius: 0 var(--rr-radius) var(--rr-radius) 0;
    }

        .rr-account-nav__link:hover {
            background: var(--rr-surface-2);
        }

        .rr-account-nav__link.is-active {
            background: var(--rr-surface-2);
            border-left-color: var(--rr-gold);
        }

        .rr-account-nav__link.is-disabled:hover {
            background: transparent;
        }
}

/* Page head */
.rr-account-head {
    border-bottom: 1px solid var(--rr-outline);
    padding-bottom: 1.25rem;
    margin-bottom: 2rem;
}

    .rr-account-head h1 {
        font-size: clamp(1.75rem, 4vw, 2.25rem);
        margin: 0;
    }

.rr-account-head__lede,
/* A13: this was declared twice, six lines apart. The first block (15px, muted) was entirely
   overridden by the second and never rendered; the two are merged, keeping the second's
   intent and the first's margin. */
.rr-account-head__count {
    margin: .5rem 0 0;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-gold-text);
}

/* Logged-out panel */
.rr-locked {
    text-align: center;
    max-width: 34rem;
    margin-inline: auto;
    padding: 2rem 1.25rem;
    background: var(--rr-surface-2);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
}

.rr-locked__icon {
    font-size: var(--rr-fs-7xl);
    color: var(--rr-gold-deep);
}

.rr-locked__head {
    font-size: var(--rr-fs-4xl);
    margin: 1rem 0 .5rem;
}

.rr-locked__body {
    color: var(--rr-muted);
    margin: 0 0 2rem;
}

.rr-locked__actions {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1rem;
}

    .rr-locked__actions .btn {
        min-width: 14rem;
    }

/* Empty-state extras — the orders empty state's WhatsApp alternative */
.rr-empty__alt {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .375rem;
    margin-top: 1.5rem;
}

.rr-empty__phone {
    font-size: var(--rr-fs-xs);
    color: var(--rr-muted);
}

/* Wishlist tile */
.rr-wishlist-tile {
    position: relative;
    height: 100%;
    display: flex;
    flex-direction: column;
}

    .rr-wishlist-tile .rr-card {
        height: auto;
        flex-grow: 1;
    }

/* Over the 1:1 image field, as in both wishlist mockups — but a labelled remove
   control rather than the mockups' filled heart. */
.rr-wishlist-tile__remove {
    position: absolute;
    top: .5rem;
    right: .5rem;
    z-index: 2;
}

    .rr-wishlist-tile__remove .rr-icon-btn {
        background: var(--rr-surface);
        border: 1px solid var(--rr-outline);
        border-radius: var(--rr-radius);
    }

        .rr-wishlist-tile__remove .rr-icon-btn:hover {
            border-color: var(--rr-gold-deep);
            color: var(--rr-gold-deep);
        }

/* Order history cards */
.rr-order-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
}

/* 1px outline, no glass and no hover scale — the mockup's order-glass-card and
   hover:scale-[1.01] are deliberately not carried over. */
.rr-order-card {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
    padding: 1.5rem;
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    transition: border-color .2s ease;
}

    .rr-order-card:hover,
    .rr-order-card:focus-within {
        border-color: var(--rr-gold);
    }

@media (min-width: 768px) {
    .rr-order-card {
        flex-direction: row;
        align-items: center;
        justify-content: space-between;
        padding: var(--rr-pad-panel);
        gap: 2rem;
    }
}

.rr-order-card__body {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
    min-width: 0;
}

.rr-order-card__meta {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem 2rem;
    margin: 0;
}

    .rr-order-card__meta dt {
        font-family: var(--rr-font-body);
        font-weight: 700;
        font-size: var(--rr-fs-3xs);
        letter-spacing: var(--rr-ls-caps);
        text-transform: uppercase;
        color: var(--rr-muted);
    }

    .rr-order-card__meta dd {
        margin: .125rem 0 0;
        font-size: var(--rr-fs-sm);
        color: var(--rr-ink);
    }

.rr-order-card__ref {
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: var(--rr-fs-3xl);
    letter-spacing: var(--rr-ls-ui);
    color: var(--rr-black);
}

.rr-order-card__thumbs {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
}

.rr-order-card__thumb {
    width: 72px;
    height: 72px;
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    overflow: hidden;
}

    .rr-order-card__thumb img {
        width: 100%;
        height: 100%;
        object-fit: contain;
        padding: .25rem;
    }

.rr-order-card__thumb--more {
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-sm);
    color: var(--rr-muted);
    background: var(--rr-surface-2);
}

.rr-order-card__side {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-shrink: 0;
    border-top: 1px solid var(--rr-outline);
    padding-top: 1.25rem;
}

@media (min-width: 768px) {
    .rr-order-card__side {
        flex-direction: column;
        align-items: flex-end;
        justify-content: center;
        text-align: right;
        border-top: 0;
        padding-top: 0;
    }
}

.rr-order-card__total-label {
    margin: 0;
    font-family: var(--rr-font-body);
    font-weight: 700;
    font-size: var(--rr-fs-3xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
}

.rr-order-card__side .rr-price--lg {
    margin: .25rem 0 0;
    font-size: var(--rr-fs-5xl);
}

.rr-order-card__side .material-symbols-outlined {
    font-size: 18px;
}

/* Order detail panels */
.rr-order-panel {
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    padding: var(--rr-pad-panel);
}

@media (min-width: 768px) {
    .rr-order-panel {
        padding: var(--rr-pad-panel-lg);
    }
}

.rr-order-panel__head {
    display: flex;
    align-items: center;
    gap: .75rem;
    font-size: var(--rr-fs-3xl);
    border-bottom: 1px solid var(--rr-outline);
    padding-bottom: 1rem;
    margin-bottom: 1.5rem;
}

.rr-order-panel__count {
    font-family: var(--rr-font-body);
    font-weight: 700;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-wide);
    color: var(--rr-black);
    background: var(--rr-gold-soft);
    border-radius: var(--rr-radius);
    padding: .125rem .5rem;
}

.rr-order-panel__address {
    margin: 0;
    font-style: normal;
    font-size: var(--rr-fs-md);
    line-height: var(--rr-lh-body);
    color: var(--rr-muted);
}

    .rr-order-panel__address strong {
        color: var(--rr-ink);
    }

.rr-order-panel__phone {
    margin: 1rem 0 0;
    font-size: var(--rr-fs-md);
    color: var(--rr-ink);
}

    .rr-order-panel__phone .rr-eyebrow {
        display: block;
        margin-bottom: .125rem;
    }

/* The detailed density of _OrderLines: a roomier thumbnail and no scroll cap —
   an order record is read, not glanced at. */
.rr-order-lines--detailed {
    max-height: none;
    overflow: visible;
}

    .rr-order-lines--detailed .rr-order-line {
        grid-template-columns: 88px minmax(0, 1fr) auto;
        gap: 1.25rem;
        padding-bottom: 1.25rem;
        margin-bottom: 1.25rem;
    }

    .rr-order-lines--detailed .rr-order-line__name {
        font-family: var(--rr-font-head);
        font-weight: 700;
        font-size: var(--rr-fs-2xl);
        letter-spacing: var(--rr-ls-ui);
        margin-bottom: .5rem;
    }

    .rr-order-lines--detailed .rr-order-line__meta + .rr-order-line__meta {
        margin-top: .125rem;
    }

@media (min-width: 768px) {
    .rr-order-lines--detailed .rr-order-line {
        grid-template-columns: 112px minmax(0, 1fr) auto;
    }
}

/* --------------------------------------------------------------------------
   24. Content pages — static-page template, About, Contact
   (A9: privacy_policy_royal_rider, about_royal_rider, contact_us)
   -------------------------------------------------------------------------- */

/* Prose written in the admin. .rr-prose already styles the product description;
   this layer adds the block elements a full page body uses, so owner-authored
   HTML always lands on-brand whatever the editor emits. */
.rr-prose--page {
    max-width: 52rem;
}

    /* Jumping from the contents list must not park a heading under the sticky header. */
    .rr-prose--page h2,
    .rr-prose--page h3 {
        scroll-margin-top: calc(var(--rr-header-h) + 24px);
    }

html {
    scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
    html {
        scroll-behavior: auto;
    }
}

    .rr-prose--page h2 {
        font-size: clamp(1.25rem, 2.4vw, 1.625rem);
        border-left: 4px solid var(--rr-gold);
        padding-left: 1rem;
        margin: 3rem 0 1.25rem;
    }

    .rr-prose--page > :first-child {
        margin-top: 0;
    }

    .rr-prose--page h3 {
        font-size: var(--rr-fs-2xl);
        margin: 2rem 0 .75rem;
    }

    .rr-prose--page h4 {
        font-size: var(--rr-fs-xl);
        margin: 1.5rem 0 .5rem;
    }

    .rr-prose--page p,
    .rr-prose--page ul,
    .rr-prose--page ol {
        margin-bottom: 1.25rem;
    }

    .rr-prose--page li {
        margin-bottom: .5rem;
    }

    .rr-prose--page a {
        color: var(--rr-gold-text);
        text-decoration: underline;
        text-underline-offset: 2px;
    }

        .rr-prose--page a:hover,
        .rr-prose--page a:focus {
            color: var(--rr-black);
        }

    .rr-prose--page blockquote {
        border-left: 2px solid var(--rr-outline);
        padding-left: 1.25rem;
        color: var(--rr-muted);
        margin: 0 0 1.25rem;
    }

    .rr-prose--page table {
        width: 100%;
        margin-bottom: 1.5rem;
        border-collapse: collapse;
    }

    .rr-prose--page th,
    .rr-prose--page td {
        border: 1px solid var(--rr-outline);
        padding: .625rem .75rem;
        text-align: left;
        font-size: var(--rr-fs-md);
    }

/* Sticky table of contents. Built by royal-rider.js from the rendered headings
   (the body is admin-editable, so a hardcoded list would go stale) and hidden
   below lg, per the mockup. */
.rr-toc-rail {
    position: sticky;
    top: calc(var(--rr-header-h) + 24px);
    display: flex;
    flex-direction: column;
    gap: 2rem;
    border-left: 1px solid var(--rr-outline);
    padding-left: 1.5rem;
}

/* The contents block itself. The rail owns the sticky position, the border rule and the
   gap to the help card below; this is only the block royal-rider.js unhides once it has
   found headings to list — so it must stay hidden while empty. */
.rr-toc[hidden] {
    display: none;
}

.rr-toc__head {
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-black);
    margin-bottom: 1.25rem;
}

.rr-toc__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: .875rem;
}

.rr-toc__link {
    display: flex;
    align-items: center;
    gap: .5rem;
    font-family: var(--rr-font-head);
    font-weight: 600;
    font-size: var(--rr-fs-lg);
    letter-spacing: var(--rr-ls-ui);
    text-transform: uppercase;
    color: var(--rr-muted);
    text-decoration: none;
    transition: color .2s ease;
}

    .rr-toc__link::before {
        content: "";
        width: 8px;
        height: 8px;
        flex: 0 0 auto;
        background: var(--rr-gold);
        opacity: 0;
        transition: opacity .2s ease;
    }

    .rr-toc__link:hover,
    .rr-toc__link:focus,
    .rr-toc__link.is-active {
        color: var(--rr-gold-text);
    }

        .rr-toc__link:hover::before,
        .rr-toc__link.is-active::before {
            opacity: 1;
        }

/* One entry. Built by royal-rider.js, which adds --sub for an h3. The rail is a fixed
   narrow column, so a long heading has to wrap inside it rather than widen it. */
.rr-toc__item {
    min-width: 0;
    overflow-wrap: anywhere;
}

/* Second-level entries sit in from their h2. */
.rr-toc__item--sub .rr-toc__link {
    font-size: var(--rr-fs-md);
    padding-left: 1rem;
}

.rr-toc-help {
    border: 1px solid var(--rr-outline);
    background: var(--rr-surface-2);
    border-radius: var(--rr-radius);
    padding: 1rem 1.125rem;
}

.rr-toc-help__head {
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-black);
    margin-bottom: .375rem;
}

.rr-toc-help__body {
    font-size: var(--rr-fs-sm);
    color: var(--rr-muted);
    margin: 0;
}

/* Closing panel on a policy page — the mockup's red block, in gold-soft. */
.rr-page-cta {
    margin-top: 3.5rem;
    background: var(--rr-gold-soft);
    border-radius: var(--rr-radius);
    padding: var(--rr-pad-panel-lg);
    max-width: 52rem;
}

    .rr-page-cta h2 {
        font-size: var(--rr-fs-4xl);
        margin-bottom: .75rem;
    }

    .rr-page-cta p {
        color: #4b4432;
        margin-bottom: 1.5rem;
    }

/* --- About ---------------------------------------------------------------- */

/* Full-bleed dark band, the same treatment as the homepage hero. */
.rr-about-hero {
    background: var(--rr-black);
    color: #ffffff;
    text-align: center;
    padding-block: 48px;
}

@media (min-width: 992px) {
    .rr-about-hero {
        padding-block: 68px;
    }
}

.rr-about-hero__title {
    color: #ffffff;
    font-size: clamp(2.25rem, 7vw, 4.25rem);
    line-height: .95;
    margin-bottom: 1.25rem;
}

.rr-about-hero__title-accent {
    color: var(--rr-gold);
}

.rr-about-hero__tagline {
    font-family: var(--rr-font-body);
    font-size: var(--rr-fs-sm);
    font-weight: 600;
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: rgba(255,255,255,.72);
    margin: 0;
}

/* The About stats strip. .rr-section--tint already supplies the mockup's
   bg-surface-container-lowest; this adds its border-y rules, which are what make the
   band read as a band rather than as a colour change. */
.rr-stats-band {
    border-top: 1px solid var(--rr-outline);
    border-bottom: 1px solid var(--rr-outline);
    overflow: hidden;
}

.rr-stats {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 2.5rem 1.5rem;
    text-align: center;
}

@media (min-width: 768px) {
    .rr-stats {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }
}

.rr-stat__value {
    display: block;
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: clamp(1.75rem, 4vw, 2.5rem);
    line-height: 1;
    letter-spacing: var(--rr-ls-ui);
    text-transform: uppercase;
    /* Large display text (>=32px even at the clamp floor), so --rr-gold-deep at 3.82:1 is
       within AA. Normal-size gold text uses --rr-gold-text. */
    color: var(--rr-gold-deep);
    margin-bottom: .625rem;
    /* The values are words, not numbers ("Worldwide", "Hand-made"), and at 32px one of them
       is wider than a 360px column. Wrap rather than push the page sideways. */
    overflow-wrap: anywhere;
}

.rr-stat__label {
    display: block;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
}

.rr-section-head--center {
    justify-content: center;
    text-align: center;
}

    .rr-section-head--center .rr-section-head__rule {
        margin-inline: auto;
    }

/* The mockup's "glass cards", rebuilt as white cards on the light theme. */
.rr-value-card {
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    padding: var(--rr-pad-panel);
    transition: border-color .3s ease;
}

    .rr-value-card:hover {
        border-color: var(--rr-gold);
    }

.rr-value-card__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    border-radius: var(--rr-radius);
    background: var(--rr-gold-soft);
    color: var(--rr-gold-deep);
    margin-bottom: 1.5rem;
}

    .rr-value-card__icon .material-symbols-outlined {
        font-size: 28px;
    }

.rr-value-card h3 {
    font-size: var(--rr-fs-2xl);
    margin-bottom: .75rem;
}

.rr-value-card p {
    color: var(--rr-muted);
    margin: 0;
}

.rr-cta-band {
    background: var(--rr-black);
    color: rgba(255,255,255,.78);
    text-align: center;
    padding-block: 48px;
}

    .rr-cta-band h2 {
        color: #ffffff;
        font-size: clamp(1.75rem, 4vw, 2.5rem);
        margin-bottom: 1rem;
    }

    .rr-cta-band p {
        max-width: 40rem;
        margin: 0 auto 2.5rem;
    }

/* --- Contact -------------------------------------------------------------- */
.rr-contact-card {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    padding: 1.5rem;
    transition: border-color .3s ease;
}

    .rr-contact-card:hover {
        border-color: var(--rr-gold);
    }

.rr-contact-card__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    flex: 0 0 auto;
    border-radius: var(--rr-radius);
    background: var(--rr-gold-soft);
    color: var(--rr-gold-deep);
}

/* A13: the card is a flex row (icon + text). A flex item defaults to min-width:auto, so the
   text block refused to shrink below the width of the address on one line and pushed /contact
   3px past a 360px viewport. Long values wrap instead. */
.rr-contact-card > div {
    min-width: 0;
    overflow-wrap: anywhere;
}

.rr-contact-card__label {
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
    margin-bottom: .25rem;
}

.rr-contact-card__value {
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: var(--rr-fs-2xl);
    letter-spacing: var(--rr-ls-ui);
    text-transform: uppercase;
    color: var(--rr-black);
    margin: 0;
}

    .rr-contact-card__value a {
        color: inherit;
        text-decoration: none;
    }

        .rr-contact-card__value a:hover {
            color: var(--rr-gold-text);
        }

.rr-contact-card__detail {
    color: var(--rr-muted);
    font-size: var(--rr-fs-md);
    margin: .375rem 0 0;
}

/* WhatsApp panel. Gold, not the mockup's green — the CTA rule is site-wide. */
.rr-whatsapp-panel {
    background: var(--rr-surface-2);
    border: 1px solid var(--rr-gold-soft);
    border-radius: var(--rr-radius);
    padding: var(--rr-pad-panel);
    text-align: center;
}

.rr-whatsapp-panel__mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    border-radius: 50%;
    background: var(--rr-gold);
    color: var(--rr-black);
    margin-bottom: 1.25rem;
}

    .rr-whatsapp-panel__mark svg {
        width: 30px;
        height: 30px;
    }

.rr-whatsapp-panel h2 {
    font-size: var(--rr-fs-3xl);
    margin-bottom: .75rem;
}

.rr-whatsapp-panel p {
    color: var(--rr-muted);
    font-size: var(--rr-fs-md);
    margin-bottom: 1.5rem;
}

/* Location band: an overlay card over either a live Google Maps embed (when the owner has
   configured one in admin Settings) or the mockup's styled image. See Views/Page/Contact.cshtml. */
.rr-location {
    position: relative;
    overflow: hidden;
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    min-height: 340px;
}

@media (min-width: 768px) {
    .rr-location {
        min-height: 480px;
    }
}

.rr-location__media {
    position: absolute;
    inset: 0;
    background-size: cover;
    background-position: center;
    filter: grayscale(1) contrast(1.1);
    opacity: .55;
}

.rr-location__pin {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 16px;
    height: 16px;
    margin: -8px 0 0 -8px;
    border-radius: 50%;
    background: var(--rr-gold);
    border: 2px solid #ffffff;
    box-shadow: 0 0 0 10px rgba(201, 162, 39, .25);
}

/* The map is a real block in the flow rather than an absolute backdrop, so on phones the card
   stacks BELOW it instead of covering the pin the visitor came here to see. From 768px up the
   card returns to its absolute bottom-left position over the map, as the mockup has it. */
.rr-location--map {
    display: flex;
    flex-direction: column;
}

.rr-location__frame {
    display: block;
    width: 100%;
    height: 340px;
    border: 0;
}

@media (min-width: 768px) {
    .rr-location__frame {
        height: 480px;
    }
}

.rr-location__card {
    position: relative;
    margin: 1rem;
    background: rgba(255, 255, 255, .94);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    padding: 1.5rem;
    max-width: 26rem;
}

@media (min-width: 768px) {
    .rr-location__card {
        position: absolute;
        bottom: 2rem;
        left: 2rem;
        margin: 0;
    }
}

.rr-location__card h3 {
    font-size: var(--rr-fs-3xl);
    margin: .375rem 0 .5rem;
}

.rr-location__address {
    color: var(--rr-muted);
    font-size: var(--rr-fs-md);
    margin-bottom: .75rem;
}

.rr-location__hours {
    display: flex;
    align-items: center;
    gap: .5rem;
    font-size: var(--rr-fs-sm);
    color: var(--rr-ink);
    margin-bottom: 1.25rem;
}

    .rr-location__hours .material-symbols-outlined {
        font-size: 18px;
        color: var(--rr-gold-deep);
    }

.rr-location__directions {
    display: inline-flex;
    align-items: center;
    gap: .375rem;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-xs);
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    color: var(--rr-black);
    text-decoration: none;
    border-bottom: 2px solid var(--rr-gold);
    padding-bottom: .25rem;
}

    .rr-location__directions:hover,
    .rr-location__directions:focus {
        color: var(--rr-gold-text);
    }

    .rr-location__directions .material-symbols-outlined {
        font-size: 16px;
    }

/* Honeypot. Off-screen rather than display:none (which bots detect) and out of
   both the tab order and the accessibility tree — nobody real ever meets it. */
.rr-honeypot {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

/* --------------------------------------------------------------------------
   Admin (A10) — shell, dashboard, orders list, order detail

   White canvas with black/gold accents (CLAUDE.md). The sidebar takes the same
   deliberate near-black block treatment as the storefront footer; everything
   right of it stays white so dense tables read cleanly. There is no red and no
   status colour anywhere — this project has no order status to colour-code.
   -------------------------------------------------------------------------- */
.rr-admin {
    background: var(--rr-surface-2);
    --rr-admin-sidebar-w: 272px;
    --rr-admin-topbar-h: 68px;
}

/* --- Sidebar -------------------------------------------------------------- */
.rr-admin-sidebar {
    /* Below lg this IS a Bootstrap offcanvas; feeding its own variables keeps the
       drawer on-theme without fighting the component. */
    --bs-offcanvas-width: 272px;
    --bs-offcanvas-bg: var(--rr-black);
    --bs-offcanvas-color: #ffffff;
    --bs-offcanvas-padding-x: 0;
    --bs-offcanvas-padding-y: 0;
    color: #ffffff;
}

@media (min-width: 992px) {
    .rr-admin-sidebar {
        position: fixed;
        inset-block: 0;
        left: 0;
        width: var(--rr-admin-sidebar-w);
        display: flex;
        flex-direction: column;
        z-index: 1030;
        /* Bootstrap forces background-color:transparent !important on .offcanvas-lg
           above the breakpoint, so this one has to answer in kind. */
        background-color: var(--rr-black) !important;
    }
}

.rr-admin-sidebar__brand {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    padding: 1.5rem 1.5rem 1.75rem;
    border-bottom: 1px solid rgba(255, 255, 255, .1);
}

/* Now the outlined SVG wordmark rather than live type, so this sizes an image: the height
   matches the 24px cap-height the text rule set, and the width follows the artwork. */
.rr-admin-sidebar__wordmark {
    display: block;
    width: auto;
    height: 26px;
    margin: 0;
}

.rr-admin-sidebar__sub {
    font-size: 10px;
    font-weight: 600;
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-gold);
    margin: .25rem 0 0;
}

.rr-admin-nav {
    display: flex;
    flex-direction: column;
    padding-block: 1rem;
    flex: 1 1 auto;
    overflow-y: auto;
}

.rr-admin-nav__item {
    display: flex;
    align-items: center;
    gap: .75rem;
    padding: .75rem 1.5rem;
    border-left: 3px solid transparent;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: 14px;
    letter-spacing: var(--rr-ls-ui);
    color: rgba(255, 255, 255, .72);
    text-decoration: none;
    transition: background-color .15s ease, color .15s ease;
}

    .rr-admin-nav__item .material-symbols-outlined {
        font-size: 20px;
    }

    a.rr-admin-nav__item:hover,
    a.rr-admin-nav__item:focus-visible {
        background: rgba(255, 255, 255, .06);
        color: #ffffff;
    }

    /* Active item in gold — the one place gold fills anything in the sidebar. */
    .rr-admin-nav__item.is-active {
        background: rgba(201, 162, 39, .16);
        border-left-color: var(--rr-gold);
        color: var(--rr-gold);
    }

    /* Sections that arrive in A11: present, legible, plainly not clickable. */
    .rr-admin-nav__item.is-disabled {
        color: rgba(255, 255, 255, .34);
        cursor: default;
    }

/* Logout is a <button> inside a POST form, never a link — a GET logout fires from any prefetch.
   These rules only undo the button chrome so it sits in the nav like every sibling. */
.rr-admin-nav__logout {
    margin: 0;
}

button.rr-admin-nav__item {
    width: 100%;
    background: none;
    border: 0;
    /* .rr-admin-nav__item's own border-left is wiped by the reset above; restate it so the
       button lines up with the links and the active marker has somewhere to go. */
    border-left: 3px solid transparent;
    text-align: left;
    font-family: inherit;
    cursor: pointer;
}

    button.rr-admin-nav__item:hover,
    button.rr-admin-nav__item:focus-visible {
        background: rgba(255, 255, 255, .06);
        color: #ffffff;
    }

.rr-admin-nav__soon {
    margin-left: auto;
    font-size: 9px;
    font-weight: 700;
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: rgba(255, 255, 255, .4);
    border: 1px solid rgba(255, 255, 255, .18);
    border-radius: 999px;
    padding: .15rem .5rem;
}

.rr-admin-nav__group {
    margin: 1.5rem 0 .25rem;
    padding-inline: 1.5rem;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: rgba(255, 255, 255, .35);
}

.rr-admin-sidebar__foot {
    display: flex;
    align-items: center;
    gap: .75rem;
    padding: 1.25rem 1.5rem;
    border-top: 1px solid rgba(255, 255, 255, .1);
}

    .rr-admin-sidebar__foot .material-symbols-outlined {
        font-size: 24px;
        color: var(--rr-gold);
    }

.rr-admin-sidebar__account {
    margin: 0;
    font-weight: 700;
    font-size: 13px;
    color: #ffffff;
}

.rr-admin-sidebar__note {
    margin: 0;
    font-size: 11px;
    color: rgba(255, 255, 255, .5);
}

/* --- Sign-in screen (B2) -------------------------------------------------- */
/* Professional split-screen gateway using the same black, white and gold admin language. */
.rr-admin-auth {
    min-width: 320px;
    background: #080808;
}

.rr-admin-auth__stage {
    position: relative;
    min-height: 100vh;
    display: grid;
    place-items: center;
    padding: 48px var(--rr-container-pad-mobile) 32px;
    overflow: hidden;
    isolation: isolate;
}

.rr-admin-auth__stage::before {
    content: "";
    position: absolute;
    z-index: -1;
    width: 560px;
    height: 560px;
    top: -280px;
    right: -180px;
    border: 1px solid rgba(201, 162, 39, .18);
    border-radius: 50%;
    box-shadow: 0 0 0 80px rgba(201, 162, 39, .025), 0 0 0 160px rgba(201, 162, 39, .018);
}

.rr-admin-auth__shell {
    width: min(100%, 1120px);
    min-height: 640px;
    display: grid;
    grid-template-columns: minmax(0, 1.05fr) minmax(390px, .95fr);
    background: var(--rr-surface);
    border: 1px solid rgba(255, 255, 255, .16);
    border-radius: 12px;
    overflow: hidden;
}

.rr-admin-auth__brand-panel {
    position: relative;
    display: flex;
    flex-direction: column;
    min-width: 0;
    overflow: hidden;
    padding: 30px 32px 28px;
    background: var(--rr-black);
    color: #ffffff;
}

.rr-admin-auth__brand-panel::before,
.rr-admin-auth__brand-panel::after {
    content: "";
    position: absolute;
    pointer-events: none;
}

.rr-admin-auth__brand-panel::before {
    width: 310px;
    height: 430px;
    right: -88px;
    bottom: -105px;
    border: 1px solid rgba(201, 162, 39, .22);
    transform: rotate(24deg);
}

.rr-admin-auth__brand-panel::after {
    width: 86px;
    height: 210px;
    right: 67px;
    bottom: -42px;
    border: 1px solid rgba(255, 255, 255, .1);
    border-top: 3px solid rgba(201, 162, 39, .65);
    transform: rotate(24deg);
}

.rr-admin-auth__brand {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: center;
    gap: .875rem;
    margin: 0;
    text-align: left;
}

/* The icon tile replaced the lettered "RR". The artwork carries its own black fill and 8px-
   equivalent radius, so the border and type rules are gone; only the box remains. */
.rr-admin-auth__monogram {
    display: block;
    width: 48px;
    height: 48px;
    flex: 0 0 48px;
    border-radius: var(--rr-radius);
}

/* Outlined SVG wordmark, sized to the 26px the type rule used to set. */
.rr-admin-auth__wordmark {
    display: block;
    width: auto;
    height: 30px;
    margin: 0;
}

.rr-admin-auth__sub {
    margin: .35rem 0 0;
    color: rgba(255, 255, 255, .56);
    font-size: 9px;
}

.rr-admin-auth__intro {
    position: relative;
    z-index: 1;
    margin: auto 0;
    max-width: 440px;
    padding-block: 40px;
}

.rr-admin-auth__eyebrow {
    display: flex;
    align-items: center;
    gap: .75rem;
    margin: 0 0 1.25rem;
    color: var(--rr-gold);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
}

.rr-admin-auth__eyebrow::before {
    content: "";
    width: 28px;
    height: 2px;
    background: var(--rr-gold);
}

.rr-admin-auth__intro h1 {
    margin: 0;
    color: #ffffff;
    font-size: clamp(42px, 4.3vw, 62px);
    line-height: .98;
}

.rr-admin-auth__intro > p:last-child {
    max-width: 390px;
    margin: 1.5rem 0 0;
    color: rgba(255, 255, 255, .66);
    font-size: 15px;
    line-height: var(--rr-lh-body);
}

.rr-admin-auth__capabilities {
    position: relative;
    z-index: 1;
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem;
    padding-top: 1.5rem;
    border-top: 1px solid rgba(255, 255, 255, .1);
}

.rr-admin-auth__capabilities > div {
    display: flex;
    align-items: center;
    gap: .45rem;
    color: rgba(255, 255, 255, .72);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
}

.rr-admin-auth__capabilities .material-symbols-outlined {
    color: var(--rr-gold);
    font-size: 17px;
}

.rr-admin-auth__brand-note {
    position: relative;
    z-index: 1;
    margin: 1rem 0 0;
    color: rgba(255, 255, 255, .4);
    font-size: 10px;
}

.rr-admin-auth__form-panel {
    display: grid;
    place-items: center;
    padding: 32px clamp(32px, 5vw, 48px);
    background: var(--rr-surface);
}

.rr-admin-auth__form-wrap {
    width: 100%;
    max-width: 390px;
}

.rr-admin-auth__access-label {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    margin-bottom: 2rem;
    color: var(--rr-gold-text);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
}

.rr-admin-auth__access-label .material-symbols-outlined {
    font-size: 18px;
}

.rr-admin-auth__form-head {
    margin-bottom: 2rem;
}

.rr-admin-auth__form-head h2 {
    margin: 0;
    font-size: 38px;
    line-height: 1.05;
}

.rr-admin-auth__form-head p {
    margin: .75rem 0 0;
    color: var(--rr-muted);
    font-size: 14px;
    line-height: var(--rr-lh-body);
}

.rr-admin-auth__form > * {
    margin-bottom: 1.25rem;
}

.rr-admin-auth__control {
    position: relative;
    display: flex;
    align-items: center;
}

.rr-admin-auth__control > .material-symbols-outlined:first-child {
    position: absolute;
    z-index: 2;
    left: 1rem;
    color: var(--rr-muted);
    font-size: 19px;
    pointer-events: none;
}

.rr-admin-auth__control .form-control {
    min-height: 52px;
    padding-left: 3rem;
    border-color: #d7d7d2;
    background: #fbfbfa;
    font-size: 14px;
}

.rr-admin-auth__control .form-control::placeholder {
    color: #93979f;
}

.rr-admin-auth__control:focus-within > .material-symbols-outlined:first-child {
    color: var(--rr-gold-deep);
}

.rr-admin-auth__control.rr-password .rr-password__input {
    padding-right: 3.25rem;
}

.rr-admin-auth__submit {
    min-height: 54px;
    justify-content: center;
    margin-top: .25rem;
}

.rr-admin-auth__foot {
    justify-content: flex-start;
    gap: .5rem;
    margin: 1.75rem 0 0;
    padding-top: 1.5rem;
    font-size: 11px;
    line-height: 1.5;
    text-align: left;
}

.rr-admin-auth__foot .material-symbols-outlined {
    flex: 0 0 auto;
    color: var(--rr-gold-deep);
    font-size: 17px;
}

.rr-admin-auth__back {
    position: absolute;
    z-index: 3;
    top: 24px;
    left: 28px;
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    margin: 0;
    color: rgba(255, 255, 255, .68);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: var(--rr-ls-wide);
    text-decoration: none;
    text-transform: uppercase;
}

.rr-admin-auth__back .material-symbols-outlined {
    font-size: 17px;
    transition: transform .15s ease;
}

.rr-admin-auth__back:hover,
.rr-admin-auth__back:focus-visible {
    color: var(--rr-gold);
}

.rr-admin-auth__back:hover .material-symbols-outlined {
    transform: translateX(-3px);
}

@media (max-width: 840px) {
    .rr-admin-auth__stage {
        display: block;
        padding-top: 44px;
    }

    .rr-admin-auth__shell {
        width: min(100%, 560px);
        min-height: 0;
        margin: 0 auto;
        grid-template-columns: 1fr;
    }

    .rr-admin-auth__brand-panel {
        padding: 28px 30px;
    }

    .rr-admin-auth__intro {
        margin: 2.5rem 0 0;
        padding: 0;
    }

    .rr-admin-auth__intro h1 {
        font-size: 42px;
    }

    .rr-admin-auth__intro > p:last-child,
    .rr-admin-auth__capabilities,
    .rr-admin-auth__brand-note {
        display: none;
    }

    .rr-admin-auth__form-panel {
        padding: 38px 30px 34px;
    }
}

@media (max-width: 480px) {
    .rr-admin-auth__stage {
        padding: 40px 0 0;
        background: var(--rr-surface);
    }

    .rr-admin-auth__shell {
        border: 0;
        border-radius: 0;
    }

    .rr-admin-auth__brand-panel {
        padding: 24px var(--rr-container-pad-mobile) 28px;
    }

    .rr-admin-auth__intro {
        margin-top: 2rem;
    }

    .rr-admin-auth__intro h1 {
        font-size: 36px;
    }

    .rr-admin-auth__eyebrow {
        margin-bottom: .875rem;
    }

    .rr-admin-auth__form-panel {
        padding: 32px var(--rr-container-pad-mobile) 36px;
    }

    .rr-admin-auth__access-label,
    .rr-admin-auth__form-head {
        margin-bottom: 1.5rem;
    }

    .rr-admin-auth__form-head h2 {
        font-size: 32px;
    }

    .rr-admin-auth__back {
        top: 19px;
        left: var(--rr-container-pad-mobile);
    }
}

/* --- Shell + topbar ------------------------------------------------------- */
.rr-admin__shell {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

@media (min-width: 992px) {
    .rr-admin__shell {
        margin-left: var(--rr-admin-sidebar-w);
    }
}

.rr-admin-topbar {
    position: sticky;
    top: 0;
    z-index: 1020;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    min-height: var(--rr-admin-topbar-h);
    padding: .75rem 1rem;
    background: var(--rr-surface);
    border-bottom: 1px solid var(--rr-outline);
}

@media (min-width: 768px) {
    .rr-admin-topbar {
        padding-inline: 1.75rem;
    }
}

.rr-admin-topbar__lead {
    display: flex;
    align-items: center;
    gap: .75rem;
    min-width: 0;
}

.rr-admin-topbar__menu {
    background: none;
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    color: var(--rr-black);
    padding: .25rem .35rem;
    line-height: 0;
}

.rr-admin-topbar__title {
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: 22px;
    text-transform: uppercase;
    letter-spacing: var(--rr-ls-ui);
    color: var(--rr-black);
    margin: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.rr-admin-crumbs {
    align-items: center;
    gap: .25rem;
    padding-left: .75rem;
    margin-left: .25rem;
    border-left: 1px solid var(--rr-outline);
    font-size: 12px;
    color: var(--rr-muted);
}

    .rr-admin-crumbs a {
        color: var(--rr-muted);
        text-decoration: none;
    }

        .rr-admin-crumbs a:hover {
            color: var(--rr-gold-text);
        }

    .rr-admin-crumbs .material-symbols-outlined {
        font-size: 14px;
    }

    .rr-admin-crumbs [aria-current] {
        font-weight: 600;
        color: var(--rr-black);
    }

.rr-admin-search {
    position: relative;
    display: none;
    align-items: center;
    flex: 0 0 auto;
}

@media (min-width: 768px) {
    .rr-admin-search {
        display: flex;
    }
}

    .rr-admin-search .material-symbols-outlined {
        position: absolute;
        left: .625rem;
        font-size: 18px;
        color: var(--rr-muted);
        pointer-events: none;
    }

    .rr-admin-search .form-control {
        width: 15rem;
        padding-left: 2.25rem;
        font-size: 14px;
        border-color: var(--rr-outline);
        border-radius: var(--rr-radius);
        background: var(--rr-surface-2);
    }

        .rr-admin-search .form-control:focus {
            box-shadow: none;
            border-color: var(--rr-gold-deep);
            background: var(--rr-surface);
        }

.rr-admin__main {
    flex: 1 1 auto;
    padding: 1.25rem 1rem 2.5rem;
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
}

@media (min-width: 768px) {
    .rr-admin__main {
        padding-inline: 1.75rem;
    }
}

.rr-admin-notice {
    display: flex;
    align-items: center;
    gap: .625rem;
    padding: .75rem 1rem;
    background: var(--rr-gold-soft);
    border-radius: var(--rr-radius);
    border: 1px solid var(--rr-gold);
}

    .rr-admin-notice p {
        margin: 0;
        font-weight: 600;
        font-size: 14px;
        color: var(--rr-black);
    }

    .rr-admin-notice .material-symbols-outlined {
        font-size: 20px;
        color: var(--rr-gold-deep);
    }

/* --- Dashboard stat cards -------------------------------------------------
   A13: every rule below is scoped to .rr-admin (the admin <body> class). The About page's
   stats strip uses the SAME .rr-stat / .rr-stat__value / .rr-stat__label names, defined
   earlier in section 30, and these rules were overriding them by source order: each About
   stat was drawn as a white bordered card with 1.5rem of padding it was never designed for,
   and the padding pushed long values ("Worldwide") past a 360px viewport. Scoping ends the
   collision without renaming anything on either side.
   TODO: the duplicate names are still a trap. Renaming the admin block to .rr-admin-stat*
   is the real fix and belongs with the next admin change. */
.rr-stat-grid {
    display: grid;
    gap: 1.25rem;
    grid-template-columns: 1fr;
}

@media (min-width: 576px) {
    .rr-stat-grid {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (min-width: 1200px) {
    .rr-stat-grid {
        grid-template-columns: repeat(4, 1fr);
    }
}

.rr-admin .rr-stat {
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    padding: 1.5rem;
}

.rr-admin .rr-stat__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
}

.rr-admin .rr-stat__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border-radius: var(--rr-radius);
    background: var(--rr-gold-soft);
    color: var(--rr-gold-deep);
    margin-bottom: 1rem;
}

.rr-admin .rr-stat__tag {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
    background: var(--rr-surface-2);
    border: 1px solid var(--rr-outline);
    border-radius: 999px;
    padding: .2rem .6rem;
}

.rr-admin .rr-stat__label {
    margin: 0 0 .25rem;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
}

.rr-admin .rr-stat__value {
    margin: 0;
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: 40px;
    line-height: 1;
    color: var(--rr-black);
}

/* --- Panels --------------------------------------------------------------- */
.rr-admin-panel {
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    overflow: hidden;
}

.rr-admin-panel--pad {
    padding: 1.5rem;
}

.rr-admin-panel__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: .5rem 1rem;
    padding: 1.125rem 1.5rem;
    border-bottom: 1px solid var(--rr-outline);
}

.rr-admin-panel__head--flush {
    padding: 0 0 1rem;
    justify-content: flex-start;
    gap: .5rem;
}

    .rr-admin-panel__head--flush .material-symbols-outlined {
        font-size: 20px;
        color: var(--rr-gold-deep);
    }

    .rr-admin-panel__head--flush .rr-hint {
        margin-left: auto;
    }

.rr-admin-panel__title {
    margin: 0;
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: 19px;
    letter-spacing: var(--rr-ls-ui);
    text-transform: uppercase;
    color: var(--rr-black);
}

/* --- Tables --------------------------------------------------------------- */
.rr-admin-table {
    margin: 0;
    --bs-table-bg: transparent;
}

    .rr-admin-table > thead th {
        background: var(--rr-surface-2);
        border-bottom: 1px solid var(--rr-outline);
        font-family: var(--rr-font-body);
        font-weight: 700;
        font-size: 10px;
        letter-spacing: var(--rr-ls-caps);
        text-transform: uppercase;
        color: var(--rr-muted);
        padding: .875rem 1rem;
        white-space: nowrap;
    }

    .rr-admin-table > tbody td,
    .rr-admin-table > tbody th {
        padding: .875rem 1rem;
        border-bottom: 1px solid var(--rr-outline);
        font-weight: 400;
        font-size: 14px;
        vertical-align: middle;
        color: var(--rr-ink);
    }

    .rr-admin-table > tfoot th,
    .rr-admin-table > tfoot td {
        padding: 1rem;
        border-top: 1px solid var(--rr-outline);
        font-family: var(--rr-font-body);
        font-weight: 700;
        font-size: 12px;
        letter-spacing: var(--rr-ls-wide);
        text-transform: uppercase;
        color: var(--rr-black);
    }

    .rr-admin-table > tbody tr:last-child > * {
        border-bottom: 0;
    }

    .rr-admin-table > tbody tr:hover > * {
        background: var(--rr-surface-2);
    }

/* Rows that navigate on click. The reference and View links stay the real controls. */
.rr-admin-table--rows > tbody tr {
    cursor: pointer;
}

.rr-admin-table__ref a {
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: 16px;
    letter-spacing: var(--rr-ls-ui);
    color: var(--rr-black);
    text-decoration: none;
}

    .rr-admin-table__ref a:hover {
        color: var(--rr-gold-text);
    }

.rr-admin-table__name {
    display: block;
    font-weight: 600;
    color: var(--rr-black);
}

.rr-admin-table__sub {
    display: block;
    font-size: 12px;
    color: var(--rr-muted);
}

.rr-admin-table__phone {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    color: var(--rr-muted);
}

.rr-admin-table__sku {
    font-size: 12px;
    letter-spacing: var(--rr-ls-ui);
    color: var(--rr-muted);
    white-space: nowrap;
}

.rr-admin-table__date {
    display: block;
    white-space: nowrap;
}

.rr-admin-table__time {
    display: block;
    font-size: 11px;
    color: var(--rr-muted);
    white-space: nowrap;
}

.rr-admin-table__action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: var(--rr-radius);
    color: var(--rr-muted);
    text-decoration: none;
}

    .rr-admin-table__action:hover {
        background: var(--rr-gold-soft);
        color: var(--rr-black);
    }

.rr-admin-count {
    display: inline-block;
    min-width: 2rem;
    padding: .15rem .5rem;
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    font-variant-numeric: tabular-nums;
    font-size: 13px;
}

/* --- Orders toolbar ------------------------------------------------------- */
.rr-admin-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 1rem;
}

.rr-admin-toolbar__search {
    position: relative;
    display: flex;
    align-items: center;
    gap: .5rem;
    flex: 1 1 22rem;
    max-width: 34rem;
}

    .rr-admin-toolbar__search > .material-symbols-outlined {
        position: absolute;
        left: .75rem;
        font-size: 20px;
        color: var(--rr-muted);
        pointer-events: none;
    }

    .rr-admin-toolbar__search .form-control {
        padding-left: 2.5rem;
        border-color: var(--rr-outline);
        background: var(--rr-surface);
    }

        .rr-admin-toolbar__search .form-control:focus {
            box-shadow: none;
            border-color: var(--rr-gold-deep);
        }

.rr-admin-toolbar__clear {
    font-size: 13px;
    font-weight: 600;
    color: var(--rr-muted);
    text-decoration: none;
    white-space: nowrap;
}

    .rr-admin-toolbar__clear:hover {
        color: var(--rr-gold-text);
    }

.rr-admin-toolbar__count {
    margin: 0;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    color: var(--rr-muted);
}

.rr-empty--admin {
    padding-block: 32px;
}

    .rr-empty--admin h2 {
        font-size: 22px;
    }

.rr-pagination--admin {
    margin: 0;
    padding: 1rem;
    border-top: 1px solid var(--rr-outline);
}

/* --- Order detail --------------------------------------------------------- */
.rr-admin-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 1rem;
}

.rr-admin-head__title {
    margin: 0;
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: 32px;
    letter-spacing: var(--rr-ls-ui);
    text-transform: uppercase;
    color: var(--rr-black);
}

.rr-admin-head__meta {
    display: flex;
    align-items: center;
    gap: .375rem;
    margin: .375rem 0 0;
    font-size: 13px;
    color: var(--rr-muted);
}

    .rr-admin-head__meta .material-symbols-outlined {
        font-size: 16px;
    }

.rr-admin-head__actions {
    display: flex;
    align-items: center;
    gap: 1rem;
    flex-wrap: wrap;
}

.rr-admin-back {
    display: inline-flex;
    align-items: center;
    gap: .375rem;
    font-size: 13px;
    font-weight: 600;
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    color: var(--rr-black);
    text-decoration: none;
}

    .rr-admin-back:hover {
        color: var(--rr-gold-text);
    }

    .rr-admin-back .material-symbols-outlined {
        font-size: 18px;
    }

.rr-admin-tag {
    /* inline-block, so a two-word category ("English Willow Bats") wraps INSIDE one pill
       instead of breaking into two pills with two separate backgrounds. */
    display: inline-block;
    margin-left: .5rem;
    padding: .1rem .5rem;
    border: 1px solid var(--rr-outline);
    border-radius: 999px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
}

    /* B7 — "WhatsApp not opened" on an order. Gold, deliberately NOT red: nothing has gone wrong
       and this is not an alert, it is a note that the handoff never fired (no WhatsApp number was
       configured) so the owner may need to make the first move. --rr-gold-text is the 5.17:1
       token; --rr-gold at 2.42:1 would fail as text (CLAUDE.md). NOT an order-status chip. */
    .rr-admin-tag--warn {
        border-color: var(--rr-gold);
        background: var(--rr-gold-soft);
        color: var(--rr-gold-text);
    }

.rr-admin-bento {
    display: grid;
    gap: 1.25rem;
    grid-template-columns: 1fr;
}

@media (min-width: 992px) {
    .rr-admin-bento {
        grid-template-columns: repeat(3, 1fr);
    }

    /* B11 — a bento panel taking the remaining two columns. The enquiry detail pairs a narrow
       sender card with the message itself, and the message is the thing being read: a third of
       the width would wrap somebody's paragraph into a ribbon. Only at this breakpoint, because
       below it the bento is a single column and every panel is already full width. */
    .rr-admin-panel--wide {
        grid-column: span 2;
    }
}

.rr-admin-facts {
    margin: 0;
    display: grid;
    gap: 1rem;
}

    .rr-admin-facts dt {
        font-size: 10px;
        font-weight: 700;
        letter-spacing: var(--rr-ls-caps);
        text-transform: uppercase;
        color: var(--rr-muted);
    }

    .rr-admin-facts dd {
        margin: .25rem 0 0;
        font-size: 15px;
        color: var(--rr-ink);
        overflow-wrap: anywhere;
    }

.rr-admin-facts__none {
    color: var(--rr-muted);
    font-style: italic;
}

.rr-admin-address {
    margin: 0 0 1rem;
    font-size: 15px;
    line-height: var(--rr-lh-body);
    font-style: normal;
    color: var(--rr-ink);
}

/* Items → subtotal → order total, inside the order-detail Summary panel. The rows carry
   their own rules; the block exists to hold them off the panel heading. */
.rr-admin-summary {
    margin-top: .25rem;
}

.rr-admin-summary__row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    padding-block: .375rem;
    font-size: 14px;
    color: var(--rr-muted);
}

.rr-admin-summary__total {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    margin-top: .75rem;
    padding-top: .875rem;
    border-top: 1px solid var(--rr-outline);
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: 17px;
    letter-spacing: var(--rr-ls-ui);
    text-transform: uppercase;
    color: var(--rr-black);
}

.rr-admin-summary__gst {
    margin: .625rem 0 0;
    font-size: 12px;
    color: var(--rr-muted);
}

.rr-admin-item {
    display: flex;
    align-items: center;
    gap: .875rem;
}

.rr-admin-item__media {
    flex: 0 0 auto;
    width: 48px;
    height: 48px;
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    background: var(--rr-surface);
    overflow: hidden;
}

    .rr-admin-item__media img {
        width: 100%;
        height: 100%;
        object-fit: cover;
        display: block;
    }

.rr-admin-item__name {
    font-weight: 600;
    color: var(--rr-black);
}

/* --- Order notes ---------------------------------------------------------- */
.rr-admin-note-form {
    margin-bottom: 1.5rem;
}

    .rr-admin-note-form .form-control:focus {
        box-shadow: none;
        border-color: var(--rr-gold-deep);
    }

.rr-admin-note-form__foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: .75rem;
    margin-top: .75rem;
}

    .rr-admin-note-form__foot .rr-hint {
        margin: 0;
    }

.rr-admin-notes {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: .75rem;
}

.rr-admin-note {
    padding: .875rem 1rem;
    background: var(--rr-surface-2);
    border-left: 3px solid var(--rr-gold);
    border-radius: 0 var(--rr-radius) var(--rr-radius) 0;
}

.rr-admin-note__time {
    display: block;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    color: var(--rr-muted);
}

.rr-admin-note__body {
    margin: .375rem 0 0;
    font-size: 14px;
    line-height: var(--rr-lh-body);
    color: var(--rr-ink);
    overflow-wrap: anywhere;

    /* B7 — the owner pastes tracking numbers and multi-line WhatsApp exchanges in here, and HTML
       would otherwise collapse every newline into a space, running the whole thing together.
       pre-line rather than pre-wrap: it keeps the line breaks that were typed while still
       collapsing the incidental leading whitespace that comes with a paste. Razor's @ encodes the
       text, so this is a rendering choice only — no raw HTML is ever involved. */
    white-space: pre-line;
}

.rr-admin-notes__empty {
    margin: 0;
    font-size: 14px;
    color: var(--rr-muted);
}

/* --------------------------------------------------------------------------
   21. Admin catalog (A11) — products list + product form
   White canvas, gold accents, 8px radius, borders over shadows. The mockups'
   red/tertiary accent is replaced throughout; nothing here uses colour as the
   only signal, so every state is also named in words.
   -------------------------------------------------------------------------- */

/* An informational counterpart to .rr-admin-notice: something happened that the
   owner should know about but that was not a failure. */
.rr-admin-notice--warn {
    background: var(--rr-surface-2);
    border-color: var(--rr-outline);
    border-left: 4px solid var(--rr-gold-deep);
}

    .rr-admin-notice--warn .material-symbols-outlined {
        color: var(--rr-gold-deep);
    }

/* --- Products list -------------------------------------------------------- */
.rr-admin-toolbar__filters {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .75rem;
    flex: 1 1 30rem;
}

    .rr-admin-toolbar__filters .rr-admin-toolbar__search {
        flex: 1 1 16rem;
        max-width: none;
    }

.rr-admin-toolbar__select {
    flex: 0 1 14rem;
}

    .rr-admin-toolbar__select .form-select {
        padding-block: .6rem;
        font-size: 14px;
    }

/* Thumbnail column: 1:1 on white, matching the storefront card exactly. */
.rr-admin-thumb-cell {
    width: 4.5rem;
}

.rr-admin-thumb {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    overflow: hidden;
}

    .rr-admin-thumb img {
        width: 100%;
        height: 100%;
        object-fit: contain;
    }

    /* No picture yet — an outlined placeholder, not a broken image frame. */
    .rr-admin-thumb .material-symbols-outlined {
        font-size: 22px;
        color: var(--rr-muted);
    }

.rr-admin-actions {
    display: inline-flex;
    align-items: center;
    justify-content: flex-end;
    gap: .25rem;
}

    .rr-admin-actions form {
        display: inline-flex;
        margin: 0;
    }

button.rr-admin-table__action {
    background: none;
    border: 0;
    padding: 0;
}

.rr-admin-table__action.is-disabled {
    opacity: .35;
    pointer-events: none;
}

/* In the products table the pill is the cell's only content, so it carries no
   leading gap (unlike the order-detail tag, which trails a sentence). */
.rr-admin-table .rr-admin-tag {
    margin-left: 0;
}

/* Active / Inactive. NOT a draft chip and NOT an order status — the only state a
   product has is whether the storefront shows it. */
.rr-admin-state {
    display: inline-flex;
    align-items: center;
    gap: .375rem;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    color: var(--rr-muted);
}

    .rr-admin-state::before {
        content: "";
        width: 7px;
        height: 7px;
        border-radius: 999px;
        border: 1px solid var(--rr-muted);
    }

    .rr-admin-state.is-on {
        color: var(--rr-black);
    }

        .rr-admin-state.is-on::before {
            background: var(--rr-gold);
            border-color: var(--rr-gold-deep);
        }

/* --- Product form shell --------------------------------------------------- */
.rr-product-form {
    display: grid;
    gap: 1.5rem;
    grid-template-columns: 1fr;
    align-items: start;
    margin-top: 1.5rem;
}

@media (min-width: 1200px) {
    .rr-product-form {
        /* The mockup's 8/4 split. The side column sits outside the tab content so
           the visibility switch is reachable from all five tabs. */
        grid-template-columns: minmax(0, 8fr) minmax(18rem, 4fr);
    }
}

.rr-product-form__main {
    min-width: 0;
}

.rr-product-form__side {
    display: grid;
    gap: 1.25rem;
    position: sticky;
    top: calc(var(--rr-admin-topbar-h) + 1rem);
}

/* Tabs: the storefront's .rr-tabs at admin scale. */
.rr-tabs--admin.nav-tabs {
    gap: 1.75rem;
    flex-wrap: wrap;
}

    .rr-tabs--admin.nav-tabs .nav-link {
        font-size: 16px;
        letter-spacing: var(--rr-ls-wide);
        display: inline-flex;
        align-items: center;
        gap: .375rem;
    }

    .rr-tabs--admin.nav-tabs .nav-link.has-error {
        color: var(--rr-black);
    }

.rr-tab-flag .material-symbols-outlined {
    font-size: 16px;
    color: var(--rr-gold-deep);
}

.rr-tab-content {
    padding-top: 1.5rem;
}

.rr-tab-content .tab-pane {
    margin-bottom: 1.5rem;
}

.rr-panel-note {
    margin: 0;
    padding: 1rem 1.5rem;
    background: var(--rr-surface-2);
    border-bottom: 1px solid var(--rr-outline);
    font-size: 13px;
    line-height: var(--rr-lh-body);
    color: var(--rr-muted);
}

.rr-grid-2 {
    display: grid;
    gap: 1.5rem;
    grid-template-columns: 1fr;
}

@media (min-width: 768px) {
    .rr-grid-2 {
        grid-template-columns: 1fr 1fr;
    }
}

/* Slug field with its /product/ prefix baked into the control. */
.rr-slug {
    display: flex;
    align-items: stretch;
}

.rr-slug__prefix {
    display: inline-flex;
    align-items: center;
    padding-inline: .75rem;
    background: var(--rr-surface-2);
    border: 1px solid var(--rr-outline);
    border-right: 0;
    border-radius: var(--rr-radius) 0 0 var(--rr-radius);
    font-size: 13px;
    color: var(--rr-muted);
    white-space: nowrap;
}

.rr-slug .form-control {
    border-radius: 0 var(--rr-radius) var(--rr-radius) 0;
    min-width: 0;
}

/* A URL the owner reads but cannot edit — a static page's fixed address, the wa.me link
   every order will open. Deliberately not a field: it looks like the value it is, in the
   same monospace as .rr-slug__prefix and .rr-editor, and it wraps rather than truncating,
   because a half-shown URL is worse than no URL when the point is to check it. */
.rr-url-readout {
    margin: 0;
    padding: .625rem .75rem;
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 13px;
    line-height: var(--rr-lh-body);
    color: var(--rr-ink);
    overflow-wrap: anywhere;
}

/* A hint that is a caution rather than an explanation. No red in this palette,
   so the weight and the icon carry it. */
.rr-hint--nudge {
    display: flex;
    align-items: flex-start;
    gap: .375rem;
    margin-top: .5rem;
    font-weight: 500;
    color: var(--rr-ink);
}

    .rr-hint--nudge .material-symbols-outlined {
        font-size: 17px;
        color: var(--rr-gold-deep);
        flex: 0 0 auto;
    }

/* --- Description editor --------------------------------------------------- */
.rr-editor {
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    overflow: hidden;
}

.rr-editor__bar {
    display: flex;
    align-items: center;
    gap: .25rem;
    padding: .375rem;
    background: var(--rr-surface-2);
    border-bottom: 1px solid var(--rr-outline);
}

.rr-editor__btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    background: none;
    border: 1px solid transparent;
    border-radius: var(--rr-radius);
    color: var(--rr-ink);
}

    .rr-editor__btn:hover {
        background: var(--rr-surface);
        border-color: var(--rr-outline);
    }

    .rr-editor__btn .material-symbols-outlined {
        font-size: 19px;
    }

.rr-editor .form-control {
    border: 0;
    border-radius: 0;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 13px;
    line-height: var(--rr-lh-body);
}

    .rr-editor .form-control:focus {
        box-shadow: none;
    }

/* --- Repeatable rows (specs / variants / images) -------------------------- */
.rr-row__actions {
    display: inline-flex;
    align-items: center;
    gap: .125rem;
}

.rr-row__btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    background: none;
    border: 1px solid transparent;
    border-radius: var(--rr-radius);
    color: var(--rr-muted);
}

    .rr-row__btn:hover {
        background: var(--rr-gold-soft);
        border-color: var(--rr-gold);
        color: var(--rr-black);
    }

    .rr-row__btn .material-symbols-outlined {
        font-size: 18px;
    }

    /* "Replace" is a <label> around a visually-hidden file input, so it takes the
       pointer while the input keeps the keyboard. The global focus ring lands on
       the input, which has no box to ring — put it on the label instead. */
    .rr-row__btn:focus-within {
        outline: 2px solid var(--rr-gold-deep);
        outline-offset: 2px;
        background: var(--rr-gold-soft);
        color: var(--rr-black);
    }

    label.rr-row__btn {
        cursor: pointer;
    }

.rr-kv-head,
.rr-kv-row {
    display: grid;
    gap: .75rem;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr) auto;
    align-items: start;
}

/* The two input cells of a spec row. Both tracks are minmax(0, …), but the cells
   themselves still default to min-width:auto, so a long placeholder or a validation
   message would widen the row and push the action buttons off the panel. */
.rr-kv-row__key,
.rr-kv-row__value {
    min-width: 0;
}

.rr-kv-head {
    padding-bottom: .5rem;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
}

.rr-kv-rows {
    display: grid;
    gap: .75rem;
    margin-bottom: 1.25rem;
}

.rr-kv-row .form-control {
    padding-block: .625rem;
    font-size: 14px;
}

.rr-kv-row .rr-row__actions {
    padding-top: .25rem;
}

/* --- Variants table ------------------------------------------------------- */
.rr-variant-table > tbody td {
    vertical-align: top;
    padding-block: .75rem;
}

.rr-variant-table .form-control {
    padding: .5rem .625rem;
    font-size: 14px;
    min-width: 6rem;
}

/* The rupee sign sits inside the control, so the column reads as money without
   a second label. */
.rr-money {
    display: flex;
    align-items: stretch;
}

.rr-money__unit {
    display: inline-flex;
    align-items: center;
    padding-inline: .5rem;
    background: var(--rr-surface-2);
    border: 1px solid var(--rr-outline);
    border-right: 0;
    border-radius: var(--rr-radius) 0 0 var(--rr-radius);
    font-family: var(--rr-font-head);
    font-weight: 700;
    color: var(--rr-black);
}

.rr-money .form-control {
    border-radius: 0 var(--rr-radius) var(--rr-radius) 0;
    min-width: 5.5rem;
}

.rr-discount-cell {
    display: inline-block;
    padding: .25rem .5rem;
    border-radius: var(--rr-radius);
    background: var(--rr-gold-soft);
    font-family: var(--rr-font-body);
    font-weight: 700;
    font-size: 12px;
    letter-spacing: var(--rr-ls-ui);
    white-space: nowrap;
    color: var(--rr-black);
}

    /* Selling price above MRP — named, not coloured. */
    .rr-discount-cell.is-warn {
        background: var(--rr-black);
        color: #ffffff;
    }

.rr-check-plain {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 0;
    padding: 0;
    margin: 0;
}

    .rr-check-plain .form-check-input {
        float: none;
        margin: 0;
        width: 1.15rem;
        height: 1.15rem;
        border-color: var(--rr-outline);
    }

        .rr-check-plain .form-check-input:checked {
            background-color: var(--rr-gold-deep);
            border-color: var(--rr-gold-deep);
        }

        .rr-check-plain .form-check-input:focus {
            box-shadow: 0 0 0 3px rgba(201, 162, 39, .3);
            border-color: var(--rr-gold);
        }

/* --- Media tab ------------------------------------------------------------ */
.rr-media-grid {
    display: grid;
    gap: 1rem;
    grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
    margin-bottom: 1.5rem;
}

.rr-media-card {
    display: flex;
    flex-direction: column;
    margin: 0;
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    overflow: hidden;
}

    /* The primary image, marked by a gold outline AND the word "Primary" — the
       outline alone would be colour as the only signal. */
    .rr-media-card.is-primary {
        border-color: var(--rr-gold);
        box-shadow: inset 0 0 0 1px var(--rr-gold);
    }

.rr-media-card__frame {
    position: relative;
    aspect-ratio: 1 / 1;
    background: #ffffff;
    border-bottom: 1px solid var(--rr-outline);
}

    .rr-media-card__frame img {
        width: 100%;
        height: 100%;
        object-fit: contain;
    }

.rr-media-card__flag {
    position: absolute;
    inset-block-start: .5rem;
    inset-inline-start: .5rem;
    padding: .15rem .5rem;
    background: var(--rr-gold);
    border-radius: var(--rr-radius);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-black);
}

/* The chosen replacement, laid over the stored picture until the form is saved.
   Same box and same object-fit as the image underneath, so the slot does not
   change shape when one covers the other. */
.rr-media-card__preview {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: contain;
    background: #ffffff;
}

/* Opposite corner from __flag, so a primary image being replaced shows both. */
.rr-media-card__pending {
    position: absolute;
    inset-block-start: .5rem;
    inset-inline-end: .5rem;
    padding: .15rem .5rem;
    background: var(--rr-black);
    border-radius: var(--rr-radius);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: #ffffff;
}

.rr-media-card__body {
    padding: .75rem;
}

    .rr-media-card__body .rr-label {
        margin-bottom: .375rem;
    }

.rr-media-card__controls {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    margin-top: .75rem;
}

.rr-primary-toggle {
    display: inline-flex;
    align-items: center;
    gap: .375rem;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    color: var(--rr-ink);
    cursor: pointer;
}

    .rr-primary-toggle input {
        width: 1.05rem;
        height: 1.05rem;
        accent-color: var(--rr-gold-deep);
    }

/* The chosen replacement named under the card, with the only way back out of it.
   overflow-wrap because a phone camera filename is long and unbreakable. */
.rr-media-card__swap {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .5rem;
    margin: .625rem 0 0;
    padding-top: .625rem;
    border-top: 1px solid var(--rr-outline);
    font-size: 11px;
    line-height: 1.4;
    color: var(--rr-muted);
    overflow-wrap: anywhere;
}

.rr-media-card__undo {
    flex: 0 0 auto;
    padding: 0;
    background: none;
    border: 0;
    font-weight: 700;
    font-size: 11px;
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    color: var(--rr-gold-text);
    text-decoration: underline;
}

/* An icon read as a word inside a sentence of hint text. */
.rr-hint__icon {
    font-size: 15px;
    vertical-align: -3px;
    color: var(--rr-gold-deep);
}

/* Upload seam. */
.rr-upload {
    padding-top: 1.25rem;
    border-top: 1px solid var(--rr-outline);
}

.rr-upload__drop {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .25rem;
    padding: 1.75rem 1rem;
    background: var(--rr-surface-2);
    border: 2px dashed var(--rr-outline);
    border-radius: var(--rr-radius);
    cursor: pointer;
    text-align: center;
}

    .rr-upload__drop:hover {
        border-color: var(--rr-gold);
    }

    .rr-upload__drop .material-symbols-outlined {
        font-size: 30px;
        color: var(--rr-gold-deep);
    }

.rr-upload__label {
    font-family: var(--rr-font-body);
    font-weight: 700;
    font-size: 12px;
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-black);
}

/* Dragging files over the zone. The dashed box has looked droppable since the
   mockup; this is the state that confirms it actually is. */
.rr-upload.is-dropping .rr-upload__drop {
    border-color: var(--rr-gold);
    border-style: solid;
    background: var(--rr-gold-soft);
}

/* The queue bar: what is about to be uploaded, and the way to call it off. */
.rr-upload__queue {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    flex-wrap: wrap;
    margin-top: 1rem;
    padding: .625rem .875rem;
    background: var(--rr-gold-soft);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
}

.rr-upload__count {
    margin: 0;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: 13px;
    color: var(--rr-ink);
}

.rr-upload__clear {
    padding: 0;
    background: none;
    border: 0;
    font-family: var(--rr-font-body);
    font-size: 12px;
    font-weight: 600;
    /* --rr-gold-text, never --rr-gold: 5.17:1 against the tint, which normal-size
       text needs. See the palette note in CLAUDE.md. */
    color: var(--rr-gold-text);
    text-decoration: underline;
    cursor: pointer;
}

.rr-upload__preview {
    display: grid;
    gap: .75rem;
    grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
    margin-top: 1rem;
}

.rr-upload__item {
    position: relative;
    margin: 0;
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    overflow: hidden;
}

/* Take one picture back out of the queue without re-choosing the other nineteen. */
.rr-upload__remove {
    position: absolute;
    top: .25rem;
    right: .25rem;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    padding: 0;
    background: var(--rr-black);
    border: 0;
    border-radius: 50%;
    color: #ffffff;
    cursor: pointer;
    opacity: .82;
}

    .rr-upload__remove:hover,
    .rr-upload__remove:focus-visible {
        opacity: 1;
    }

    .rr-upload__remove .material-symbols-outlined {
        font-size: 17px;
    }

    .rr-upload__item.is-over {
        border-color: var(--rr-black);
        border-width: 2px;
    }

    .rr-upload__item img {
        display: block;
        width: 100%;
        aspect-ratio: 1 / 1;
        object-fit: contain;
        background: #ffffff;
    }

    .rr-upload__item figcaption {
        padding: .375rem .5rem;
        border-top: 1px solid var(--rr-outline);
        font-size: 11px;
        line-height: 1.4;
        color: var(--rr-muted);
        overflow-wrap: anywhere;
    }

.rr-notice-inline {
    display: flex;
    align-items: flex-start;
    gap: .5rem;
    margin: 1rem 0 0;
    padding: .75rem 1rem;
    background: var(--rr-surface-2);
    border-left: 4px solid var(--rr-gold-deep);
    border-radius: 0 var(--rr-radius) var(--rr-radius) 0;
    font-size: 13px;
    line-height: var(--rr-lh-body);
    color: var(--rr-ink);
}

    .rr-notice-inline .material-symbols-outlined {
        font-size: 18px;
        color: var(--rr-gold-deep);
        flex: 0 0 auto;
    }

/* --- SEO tab -------------------------------------------------------------- */
.rr-serp {
    padding: 1rem 1.25rem;
    background: var(--rr-surface-2);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    margin-bottom: 1.5rem;
}

.rr-serp__url {
    margin: 0 0 .25rem;
    font-size: 12px;
    color: var(--rr-muted);
    overflow-wrap: anywhere;
}

.rr-serp__title {
    margin: 0 0 .25rem;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: 18px;
    line-height: 1.3;
    color: var(--rr-black);
}

.rr-serp__desc {
    margin: 0;
    font-size: 13px;
    line-height: var(--rr-lh-body);
    color: var(--rr-muted);
    min-height: 1.3em;
}

.rr-counter {
    margin: .375rem 0 0;
    font-size: 11px;
    letter-spacing: var(--rr-ls-ui);
    color: var(--rr-muted);
}

    /* Over the recommended length is advice, never a block — so it is stated
       plainly and the form still saves. */
    .rr-counter.is-over {
        font-weight: 600;
        color: var(--rr-ink);
    }

/* --- Visibility panel + form actions -------------------------------------- */
.rr-switch {
    padding-left: 3rem;
    margin-bottom: .75rem;
    min-height: 1.75rem;
}

    .rr-switch .form-check-input {
        width: 2.5rem;
        height: 1.35rem;
        margin-left: -3rem;
        background-color: var(--rr-surface-2);
        border-color: var(--rr-outline);
    }

        .rr-switch .form-check-input:checked {
            background-color: var(--rr-gold);
            border-color: var(--rr-gold-deep);
        }

        .rr-switch .form-check-input:focus {
            box-shadow: 0 0 0 3px rgba(201, 162, 39, .3);
            border-color: var(--rr-gold);
        }

    .rr-switch .form-check-label {
        font-family: var(--rr-font-body);
        font-weight: 700;
        font-size: 12px;
        letter-spacing: var(--rr-ls-wide);
        text-transform: uppercase;
        color: var(--rr-black);
    }

.rr-status-note {
    margin: 0 0 .75rem;
    padding: .75rem 1rem;
    background: var(--rr-gold-soft);
    border-radius: var(--rr-radius);
    font-size: 13px;
    line-height: 1.5;
    font-weight: 500;
    color: var(--rr-black);
}

.rr-status-note--off {
    background: var(--rr-surface-2);
    border: 1px solid var(--rr-outline);
    color: var(--rr-ink);
}

.rr-form-actions {
    display: grid;
    gap: .625rem;
    justify-items: stretch;
}

    .rr-form-actions .btn {
        justify-content: center;
    }

    .rr-form-actions .rr-admin-toolbar__clear {
        text-align: center;
        padding-top: .25rem;
    }

/* --------------------------------------------------------------------------
   22. Admin content (A12/A14) — category tree + banner cards
   A12 shipped the markup for these two screens without the CSS, so both rendered
   as raw stacked HTML. Everything below is what could NOT be reused: the tree row
   and the banner card frame are patterns the admin did not already have. The
   thumbnails, panels, grid, action buttons, state pills, inline notices and count
   lines are all existing components the markup was moved onto instead.
   -------------------------------------------------------------------------- */

/* --- Store settings (A12) -------------------------------------------------
   Mockup admin_banners_settings: "grid grid-cols-1 lg:grid-cols-2 gap-12" over the
   grouped panels, then a bordered-off save block at the foot. The panels themselves
   are .rr-admin-panel and the fields are .rr-form — only the page-level arrangement
   is new. */
.rr-settings-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1.5rem;
    /* Panels are different heights; none of them should stretch to match a neighbour. */
    align-items: start;
}

@media (min-width: 992px) {
    .rr-settings-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 2rem;
    }
}

/* Social links hold four fields in their own two-column grid, so the panel takes the
   full row rather than squeezing them into half of one. */
.rr-settings-grid__wide {
    grid-column: 1 / -1;
}

.rr-settings-save {
    margin-top: 2rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--rr-outline);
    text-align: center;
}

    /* The mockup's w-full button, capped so it does not stretch across a wide desktop. */
    .rr-settings-save .btn {
        width: 100%;
        max-width: 24rem;
        justify-content: center;
    }

    .rr-settings-save .rr-hint {
        margin: 1rem auto 0;
        max-width: 40rem;
    }

/* Generic chip. Distinct from .rr-admin-tag (a bare outline pill trailing a
   sentence) because a chip sits ON a photograph and needs its own ground. Gold
   fill carries black text — 7.98:1 — and the state is always written in words. */
.rr-chip {
    display: inline-flex;
    align-items: center;
    padding: .2rem .5rem;
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    font-family: var(--rr-font-body);
    font-weight: 700;
    font-size: 10px;
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-black);
}

.rr-chip--on {
    background: var(--rr-gold);
    border-color: var(--rr-gold);
    color: var(--rr-black);
}

/* Extends .rr-admin-thumb (56px, 1:1, contain) to form-preview size. */
.rr-admin-thumb--lg {
    width: 120px;
    height: 120px;
    margin-bottom: 1.25rem;
}

/* Extends .rr-admin-bento with the mockup's card glyph — same well as the
   dashboard stat icon, without borrowing .rr-stat's already-overloaded names. */
.rr-admin-bento__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    margin-bottom: 1rem;
    border-radius: var(--rr-radius);
    background: var(--rr-gold-soft);
    color: var(--rr-gold-deep);
}

/* Extends .rr-admin-actions: a reorder pair reads as one control, so the two
   buttons sit flush rather than spaced like unrelated actions. */
.rr-admin-actions--tight {
    gap: 0;
}

/* --- Category tree -------------------------------------------------------- */
.rr-cat-tree,
.rr-cat-children {
    list-style: none;
    margin: 0;
    padding: 0;
}

.rr-cat-branch + .rr-cat-branch {
    border-top: 1px solid var(--rr-outline);
}

/* Children are a tinted, indented band under their parent. The indent is capped
   on small screens — at 360px a desktop indent would push the actions off-screen. */
.rr-cat-children {
    background: var(--rr-surface-2);
    border-top: 1px solid var(--rr-outline);
    box-shadow: inset 3px 0 0 var(--rr-gold-soft);
}

    .rr-cat-children > li + li {
        border-top: 1px solid var(--rr-outline);
    }

.rr-cat-head,
.rr-cat-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: .5rem 1rem;
    align-items: center;
    padding: .875rem 1rem;
}

/* Column captions are decoration for the wide layout only — below lg the row
   stacks and every value is already labelled in words. */
.rr-cat-head {
    display: none;
    background: var(--rr-surface-2);
    border-bottom: 1px solid var(--rr-outline);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
}

.rr-cat-row--child {
    padding-left: 1.75rem;
}

.rr-cat-row.is-hidden .rr-admin-thumb {
    opacity: .5;
}

/* Small screens: name across the top, facts down the left, actions to the right. */
.rr-cat-row__main {
    grid-area: 1 / 1 / 2 / 3;
    display: flex;
    align-items: center;
    gap: .75rem;
    min-width: 0;
}

.rr-cat-row__elbow {
    flex: 0 0 auto;
    font-size: 18px;
    color: var(--rr-muted);
}

.rr-cat-row__names {
    min-width: 0;
}

.rr-cat-row__name {
    display: block;
    font-weight: 600;
    color: var(--rr-black);
    text-decoration: none;
    overflow-wrap: anywhere;
}

    .rr-cat-row__name:hover {
        color: var(--rr-gold-text);
    }

.rr-cat-row__slug {
    display: block;
    font-size: 12px;
    color: var(--rr-muted);
    overflow-wrap: anywhere;
}

    /* .rr-admin-tag carries a left margin because it normally trails a sentence. Here it starts
       its own line under the slug, so the margin only pushes it out of alignment with the name
       above it — the same reset the products table makes for the same reason. */
    .rr-cat-row__names .rr-admin-tag {
        margin: .25rem 0 0;
    }

.rr-cat-row__count {
    grid-area: 2 / 1 / 3 / 2;
    display: flex;
    align-items: baseline;
    gap: .3rem;
    font-size: 13px;
    color: var(--rr-muted);
}

.rr-cat-row__count-value {
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: 18px;
    color: var(--rr-black);
}

.rr-cat-row__status {
    grid-area: 3 / 1 / 4 / 2;
    display: grid;
    gap: .125rem;
    justify-items: start;
}

    /* .rr-admin-table__time keeps its hint on one line inside a table cell, where the
       column can grow. Here the column is fixed, so the hint has to wrap or it runs
       under the action buttons. */
    .rr-cat-row__status .rr-admin-table__time {
        white-space: normal;
        line-height: 1.4;
    }

.rr-cat-row__actions {
    grid-area: 2 / 2 / 4 / 3;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    flex-wrap: wrap;
    gap: .25rem;
}

@media (min-width: 992px) {
    .rr-cat-head {
        display: grid;
    }

    /* Every track is fixed except the name: the header is a separate grid from the
       rows, so an `auto` action column would size to "ACTIONS" in one and to six
       buttons in the other, and the two would not line up. */
    .rr-cat-head,
    .rr-cat-row {
        grid-template-columns: minmax(0, 1fr) 7rem 11rem 14rem;
    }

        .rr-cat-head > :last-child {
            justify-self: end;
        }

    .rr-cat-row--child {
        padding-left: 3rem;
    }

    /* Wide enough for one row per category: drop the stacked placement and let the
       four parts fall into the four tracks in source order. */
    .rr-cat-row__main,
    .rr-cat-row__count,
    .rr-cat-row__status,
    .rr-cat-row__actions {
        grid-area: auto;
    }
}

.rr-cat-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: .5rem 1.5rem;
    padding: 1rem;
    background: var(--rr-surface-2);
    border-top: 1px solid var(--rr-outline);
}

/* --- Banner cards --------------------------------------------------------- */
/* The card itself is an .rr-admin-panel in a .rr-admin-bento; this only adds the
   column flow and the switched-off treatment. */
.rr-banner-card {
    display: flex;
    flex-direction: column;
}

    /* Off is stated in the chip; the dashed edge is a second, redundant signal. */
    .rr-banner-card.is-off {
        border-style: dashed;
    }

        .rr-banner-card.is-off .rr-banner-card__frame {
            opacity: .6;
        }

/* Both image frames: a fixed 16:9 box that crops. Nothing renders at natural size. */
.rr-banner-card__frame,
.rr-banner-preview {
    position: relative;
    aspect-ratio: 16 / 9;
    background: var(--rr-surface-2);
    overflow: hidden;
}

/* A card can be wide when it is the only one in the row; cap it so the picture
   never dwarfs the words underneath. (Not on the form preview — there the cap
   would shrink the width too, per aspect-ratio, and leave a gap beside it.) */
.rr-banner-card__frame {
    max-height: 260px;
    border-bottom: 1px solid var(--rr-outline);
}

    .rr-banner-card__frame > img,
    .rr-banner-preview > img {
        display: block;
        width: 100%;
        height: 100%;
        object-fit: cover;
    }

/* The form preview stands alone rather than inside a card, so it carries its own frame. */
.rr-banner-preview {
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    margin-bottom: 1.25rem;
}

/* --- Content images (B4.1) ------------------------------------------------ */
/* One card per editorial slot. The card itself is an .rr-admin-panel in the
   .rr-settings-grid two-column flow and the preview is .rr-banner-preview, so
   almost nothing is needed here — only the column flow that pins the two
   actions to the foot, so five cards of differing text length still line their
   buttons up rather than each ending wherever its own prose stopped. */
.rr-content-slot {
    display: flex;
    flex-direction: column;
}

    .rr-content-slot form {
        margin: 0;
    }

/* The preview frame. Deliberately NOT .rr-banner-preview, whose `> img` rule is a
   DIRECT-child selector: _ResponsiveImage wraps its <img> in a <picture>, so that
   rule never matches and the picture renders at whatever size it happens to be.
   (That is a pre-existing quirk of the banner form, left alone here rather than
   changed underneath a screen this prompt did not touch.) */
.rr-content-slot__preview {
    position: relative;
    aspect-ratio: 16 / 9;
    background: var(--rr-surface-2);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    overflow: hidden;
    margin-bottom: 1.25rem;
}

    .rr-content-slot__preview picture {
        display: block;
        width: 100%;
        height: 100%;
    }

    .rr-content-slot__preview img {
        display: block;
        width: 100%;
        height: 100%;
        /* An uploaded photo is already 16:9, so cover trims nothing — it shows the
           owner the real stored crop rather than a flattering fit. */
        object-fit: cover;
    }

/* The placeholder tile is SQUARE, so cover would crop its "photo coming soon"
   caption off — which is the one thing the tile is there to say. Contained and
   centred instead, which also reads as "nothing here yet" at a glance. */
.rr-content-slot__preview--placeholder img {
    object-fit: contain;
    padding: .5rem;
}

/* Pushed to the bottom of the card, above the remove link. */
.rr-content-slot__actions {
    margin-top: 1.25rem;
}

/* The destructive action, deliberately quiet: it is .rr-admin-toolbar__clear (a
   plain text button, not a red one) because the confirmation dialog is what
   protects the files, and a loud button here would out-shout the save. */
.rr-content-slot__remove {
    margin-top: .75rem;
    padding-top: .75rem;
    border-top: 1px solid var(--rr-outline);
}

    .rr-content-slot__remove .rr-admin-toolbar__clear {
        display: inline-flex;
        align-items: center;
        gap: .375rem;
    }

        .rr-content-slot__remove .material-symbols-outlined {
            font-size: 18px;
        }

.rr-banner-card__chips {
    position: absolute;
    inset-block-start: .5rem;
    inset-inline-end: .5rem;
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: .375rem;
    max-width: calc(100% - 1rem);
}

/* No picture yet — an outlined placeholder, not a broken image frame. */
.rr-banner-card__empty {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .25rem;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
}

    .rr-banner-card__empty .material-symbols-outlined {
        font-size: 28px;
    }

.rr-banner-card__body {
    flex: 1 1 auto;
    padding: 1rem;
}

.rr-banner-card__sub,
.rr-banner-card__link {
    margin: .375rem 0 0;
    font-size: 13px;
    line-height: 1.5;
    color: var(--rr-muted);
    overflow-wrap: anywhere;
}

.rr-banner-card__link {
    display: flex;
    align-items: flex-start;
    gap: .375rem;
}

    .rr-banner-card__link .material-symbols-outlined {
        flex: 0 0 auto;
        font-size: 16px;
        color: var(--rr-gold-deep);
    }

.rr-banner-card__foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: .5rem;
    padding: .625rem 1rem;
    border-top: 1px solid var(--rr-outline);
}

/* ==========================================================================
   34. Admin UI — compact console layer
   ==========================================================================

   A redesign pass over sections 20/21. Everything below is scoped to the admin
   body classes (.rr-admin, .rr-admin-auth) so the storefront is untouched: the
   two surfaces share tokens but no longer share density.

   Three things changed, and they are the whole brief:

   1. DENSITY. The old admin was laid out at storefront scale — 1.5rem padding
      on every card, 40px stat numbers, .875rem table cells. On the screens the
      owner actually lives in (orders, products, the product form) that meant
      four rows per fold. The scale below is roughly 25-30% tighter: 56px topbar,
      .5rem/.75rem cells, 13px body text, 28px stat numbers.

   2. COLOUR. CLAUDE.md pins the *storefront* to white/black/gold. The admin now
      carries four extra accents alongside gold, and they are strictly functional
      — each admin section owns one, so Orders is always teal and Products is
      always gold wherever they appear (nav icon, stat card, panel rule, tag).
      Colour is never the only signal: every state that uses it is also named in
      words, and gold remains the only fill allowed on a button.

      Contrast, measured on white:
        --rr-a-indigo-text  #3730A3   9.4:1
        --rr-a-teal-text    #0F6B62   5.4:1
        --rr-a-violet-text  #5B21B6   8.6:1
        --rr-a-rose-text    #A3113A   7.6:1
        --rr-a-amber-text   #92400E   6.3:1
      The -base tokens are fills, rules and icon glyphs only (all clear 3:1 for
      1.4.11); the -soft tokens are tints and never carry text.

   3. MOTION. Entrance, hover and state transitions throughout. All of it is
      transform/opacity so nothing triggers layout, and the global
      prefers-reduced-motion block in section 2 already neutralises every
      animation and transition here — no per-rule guard needed.
   -------------------------------------------------------------------------- */

.rr-admin,
.rr-admin-auth {
    /* --- Type scale opt-out ----------------------------------------------
       The storefront type scale in section 1 was taken down a step; the admin
       was already compacted by this section and does not want a second pass.
       Admin's own rules are still hard px, but the surfaces SHARE components
       (.btn, .rr-eyebrow, .rr-label, .form-control, .rr-price), and those
       resolve through the scale. Pinning the tokens back to their pre-pass
       values here means every shared component renders in the admin at exactly
       the size it did before — including the sign-in screen's 15px submit,
       which .rr-admin:not(.rr-admin-auth) .btn deliberately does not cover. */
    --rr-fs-3xs: 10px;
    --rr-fs-2xs: 11px;
    --rr-fs-xs: 13px;
    --rr-fs-sm: 14px;
    --rr-fs-md: 15px;
    --rr-fs-lg: 17px;
    --rr-fs-xl: 18px;
    --rr-fs-2xl: 20px;
    --rr-fs-3xl: 22px;
    --rr-fs-4xl: 24px;
    --rr-fs-5xl: 28px;
    --rr-fs-6xl: 34px;
    --rr-fs-7xl: 40px;
    --rr-fs-8xl: 48px;
    --rr-fs-9xl: 64px;

    /* --- Section accents ------------------------------------------------- */
    --rr-a-indigo: #4F46E5;
    --rr-a-indigo-soft: #EEF0FE;
    --rr-a-indigo-text: #3730A3;
    --rr-a-teal: #0D9488;
    --rr-a-teal-soft: #E4F5F2;
    --rr-a-teal-text: #0F6B62;
    --rr-a-violet: #7C3AED;
    --rr-a-violet-soft: #F2ECFE;
    --rr-a-violet-text: #5B21B6;
    --rr-a-rose: #E11D48;
    --rr-a-rose-soft: #FDECF0;
    --rr-a-rose-text: #A3113A;
    --rr-a-amber: #D97706;
    --rr-a-amber-soft: #FEF1E0;
    --rr-a-amber-text: #92400E;

    /* Every card and panel resolves its accent from these two, so a single
       [data-accent] on an ancestor recolours the whole block. Gold is the
       default, which keeps anything not explicitly tagged on-brand. */
    --rr-accent: var(--rr-gold);
    --rr-accent-soft: var(--rr-gold-soft);
    --rr-accent-text: var(--rr-gold-text);

    /* --- Density scale ---------------------------------------------------- */
    --rr-ad-gap: .875rem; /* between top-level blocks in <main> */
    --rr-ad-pad: 1rem; /* panel body padding */
    --rr-ad-pad-tight: .75rem; /* panel head, table cells (block) */
    --rr-ad-cell-x: .75rem; /* table cell inline padding */
    --rr-ad-text: 13px; /* admin body size */
    --rr-ad-control-h: 36px; /* every input, select and button */

    /* --- Elevation --------------------------------------------------------
       Borders over shadows is still the rule (CLAUDE.md); these are hover-only
       lifts, not resting decoration. */
    --rr-ad-lift: 0 6px 16px -8px rgba(14, 14, 14, .22), 0 2px 4px -2px rgba(14, 14, 14, .08);
    --rr-ad-lift-lg: 0 14px 34px -14px rgba(14, 14, 14, .3), 0 3px 8px -4px rgba(14, 14, 14, .1);

    /* --- Motion ------------------------------------------------------------ */
    --rr-ease: cubic-bezier(.22, .61, .36, 1); /* decelerate — entrances, lifts */
    --rr-ease-spring: cubic-bezier(.34, 1.4, .64, 1); /* slight overshoot — icons, pops */
    --rr-t-fast: .14s;
    --rr-t: .22s;
    --rr-t-slow: .38s;
}

/* Accent selector. One attribute on a nav item, stat card or panel repoints the
   three variables above; everything inside then paints itself. */
.rr-admin [data-accent="indigo"] {
    --rr-accent: var(--rr-a-indigo);
    --rr-accent-soft: var(--rr-a-indigo-soft);
    --rr-accent-text: var(--rr-a-indigo-text);
}

.rr-admin [data-accent="teal"] {
    --rr-accent: var(--rr-a-teal);
    --rr-accent-soft: var(--rr-a-teal-soft);
    --rr-accent-text: var(--rr-a-teal-text);
}

.rr-admin [data-accent="violet"] {
    --rr-accent: var(--rr-a-violet);
    --rr-accent-soft: var(--rr-a-violet-soft);
    --rr-accent-text: var(--rr-a-violet-text);
}

.rr-admin [data-accent="rose"] {
    --rr-accent: var(--rr-a-rose);
    --rr-accent-soft: var(--rr-a-rose-soft);
    --rr-accent-text: var(--rr-a-rose-text);
}

.rr-admin [data-accent="amber"] {
    --rr-accent: var(--rr-a-amber);
    --rr-accent-soft: var(--rr-a-amber-soft);
    --rr-accent-text: var(--rr-a-amber-text);
}

.rr-admin [data-accent="gold"] {
    --rr-accent: var(--rr-gold);
    --rr-accent-soft: var(--rr-gold-soft);
    --rr-accent-text: var(--rr-gold-text);
}

/* --------------------------------------------------------------------------
   34.1 Keyframes
   -------------------------------------------------------------------------- */
@keyframes rr-rise {
    from {
        opacity: 0;
        transform: translateY(10px);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

@keyframes rr-slide-in {
    from {
        opacity: 0;
        transform: translateX(-12px);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

@keyframes rr-drop {
    from {
        opacity: 0;
        transform: translateY(-10px) scaleY(.96);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

@keyframes rr-pop {
    0% {
        transform: scale(.7);
        opacity: 0;
    }

    60% {
        transform: scale(1.08);
        opacity: 1;
    }

    100% {
        transform: scale(1);
    }
}

/* The loading placeholder sweep. Not wired to anything yet — .rr-skeleton is
   here so a future async panel has a house style to reach for. */
@keyframes rr-shimmer {
    from {
        background-position: -200% 0;
    }

    to {
        background-position: 200% 0;
    }
}

/* The sheen that crosses a gold button on hover. */
@keyframes rr-sheen {
    from {
        transform: translateX(-120%) skewX(-18deg);
    }

    to {
        transform: translateX(320%) skewX(-18deg);
    }
}

/* --------------------------------------------------------------------------
   34.2 Shell — sidebar, topbar, main
   -------------------------------------------------------------------------- */
/* IMPORTANT — why every console rule below carries :not(.rr-admin-auth).

   The sign-in screen's body is `class="rr-admin rr-admin-auth"` (see
   _AdminAuthLayout), so it matches BOTH. Without the guard the console density
   landed on the sign-in screen and broke it in four visible ways:

     • `.rr-admin .form-control { padding: … }` is a shorthand at the same
       specificity as `.rr-admin-auth__control .form-control { padding-left: 3rem }`
       and declared later, so it reset the left padding to .6rem and the leading
       person/lock icons sat on top of the typed text;
     • the 36px control height overrode the sign-in screen's deliberate 52px;
     • `.rr-admin .btn-lg` overrode the 54px submit;
     • this rule's light canvas gradient overrode `.rr-admin-auth`'s near-black
       stage, washing the whole page out.

   The sign-in screen is a gateway, not a console: it is meant to be roomy. The
   guard keeps the two scales genuinely separate rather than patching the symptoms.

   The token block above deliberately does NOT carry the guard — the sign-in
   screen still wants the accent palette and the motion easings. */
.rr-admin:not(.rr-admin-auth) {
    --rr-admin-sidebar-w: 244px;
    --rr-admin-sidebar-w-mini: 68px;
    --rr-admin-topbar-h: 56px;
    font-size: var(--rr-ad-text);
    /* A very slight warm wash rather than the flat #f7f7f5, so white panels have
       something to sit on. Fixed, so it does not scroll with the content. */
    background: radial-gradient(1100px 520px at 85% -10%, rgba(201, 162, 39, .07), transparent 60%), radial-gradient(900px 480px at -5% 0%, rgba(79, 70, 229, .05), transparent 55%), var(--rr-surface-2);
    background-attachment: fixed;
}

/* --- Sidebar -------------------------------------------------------------- */
.rr-admin-sidebar {
    --bs-offcanvas-width: 244px;
    /* Near-black stays (it is the storefront footer's treatment) but gains a
       vertical gradient and a hairline gold edge, so it reads as a panel rather
       than a black rectangle. */
    background-image: linear-gradient(180deg, #16161A 0%, #0E0E0E 55%, #0A0A0C 100%);
    border-right: 1px solid rgba(201, 162, 39, .16);
}

@media (min-width: 992px) {
    .rr-admin-sidebar {
        background-color: var(--rr-black) !important;
        transition: width var(--rr-t-slow) var(--rr-ease);
    }
}

.rr-admin-sidebar__brand {
    padding: .875rem 1rem 1rem;
    border-bottom: 1px solid rgba(255, 255, 255, .08);
    align-items: center;
}

.rr-admin-sidebar__wordmark {
    height: 22px;
    transition: transform var(--rr-t) var(--rr-ease-spring);
}

    .rr-admin-sidebar__brand:hover .rr-admin-sidebar__wordmark {
        transform: scale(1.03);
    }

.rr-admin-sidebar__sub {
    font-size: 9px;
    letter-spacing: var(--rr-ls-caps);
    margin-top: .2rem;
}

.rr-admin-nav {
    padding: .625rem .5rem;
    gap: 1px;
    /* overflow-y:auto (inherited from section 20) makes this a scroll container
       on BOTH axes. The nav items translate 2px right on hover, which was enough
       to give the sidebar its own horizontal scrollbar. Nothing here should ever
       scroll sideways. */
    overflow-x: hidden;
    /* Thin, dark scrollbar — the default light one is a bright stripe down a
       near-black panel. */
    scrollbar-width: thin;
    scrollbar-color: rgba(255, 255, 255, .18) transparent;
}

    .rr-admin-nav::-webkit-scrollbar {
        width: 6px;
    }

    .rr-admin-nav::-webkit-scrollbar-thumb {
        background: rgba(255, 255, 255, .16);
        border-radius: 999px;
    }

/* Items are now pills rather than full-bleed rows with a left rule: at this
   density a 3px border on every row was the loudest thing in the panel. */
.rr-admin-nav__item {
    position: relative;
    gap: .625rem;
    padding: .5rem .625rem;
    margin: 0;
    border-left: 0;
    border-radius: 7px;
    font-size: 13px;
    color: rgba(255, 255, 255, .68);
    /* Each item is dealt in on load, staggered by position. */
    animation: rr-slide-in var(--rr-t-slow) var(--rr-ease) backwards;
    transition: background-color var(--rr-t) var(--rr-ease), color var(--rr-t) var(--rr-ease), transform var(--rr-t) var(--rr-ease);
}

.rr-admin-nav > *:nth-child(1) .rr-admin-nav__item,
.rr-admin-nav > .rr-admin-nav__item:nth-child(1) {
    animation-delay: .02s;
}

.rr-admin-nav > .rr-admin-nav__item:nth-child(2) {
    animation-delay: .05s;
}

.rr-admin-nav > .rr-admin-nav__item:nth-child(3) {
    animation-delay: .08s;
}

.rr-admin-nav > .rr-admin-nav__item:nth-child(4) {
    animation-delay: .11s;
}

.rr-admin-nav > .rr-admin-nav__item:nth-child(5) {
    animation-delay: .14s;
}

.rr-admin-nav > .rr-admin-nav__item:nth-child(6) {
    animation-delay: .17s;
}

.rr-admin-nav > .rr-admin-nav__item:nth-child(7) {
    animation-delay: .2s;
}

.rr-admin-nav > .rr-admin-nav__item:nth-child(8) {
    animation-delay: .23s;
}

.rr-admin-nav > .rr-admin-nav__item:nth-child(n+9) {
    animation-delay: .26s;
}

/* The section's own colour, on the icon only. On the near-black panel the -base
   tokens all clear 4.5:1, so they are safe as glyphs here. */
.rr-admin-nav__item .material-symbols-outlined {
    font-size: 19px;
    color: var(--rr-accent);
    opacity: .85;
    transition: transform var(--rr-t) var(--rr-ease-spring), opacity var(--rr-t) var(--rr-ease);
}

a.rr-admin-nav__item:hover,
a.rr-admin-nav__item:focus-visible,
button.rr-admin-nav__item:hover,
button.rr-admin-nav__item:focus-visible {
    background: rgba(255, 255, 255, .07);
    color: #ffffff;
    transform: translateX(2px);
}

    a.rr-admin-nav__item:hover .material-symbols-outlined,
    button.rr-admin-nav__item:hover .material-symbols-outlined {
        opacity: 1;
        transform: scale(1.12);
    }

/* Active: a tinted pill in the section's own colour with a growing left bar.
   aria-current on the link is the accessible signal; this is the visual one. */
.rr-admin-nav__item.is-active {
    background: color-mix(in srgb, var(--rr-accent) 18%, transparent);
    border-left: 0;
    color: #ffffff;
    font-weight: 700;
}

    .rr-admin-nav__item.is-active::before {
        content: "";
        position: absolute;
        left: 0;
        top: 50%;
        width: 3px;
        height: 60%;
        border-radius: 0 3px 3px 0;
        background: var(--rr-accent);
        transform: translateY(-50%);
        animation: rr-pop var(--rr-t-slow) var(--rr-ease-spring) backwards;
    }

    .rr-admin-nav__item.is-active .material-symbols-outlined {
        opacity: 1;
        font-variation-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 24;
    }

/* Fallback for engines without color-mix(): a flat white tint rather than none. */
@supports not (background: color-mix(in srgb, red 10%, blue)) {
    .rr-admin-nav__item.is-active {
        background: rgba(255, 255, 255, .12);
    }
}

.rr-admin-nav__group {
    margin: .875rem 0 .25rem;
    padding-inline: .625rem;
    font-size: 9px;
    letter-spacing: var(--rr-ls-caps);
    color: rgba(255, 255, 255, .3);
}

/* Logout sits apart from the sections above it. */
.rr-admin-nav__logout {
    margin-top: .25rem;
    padding-top: .375rem;
    border-top: 1px solid rgba(255, 255, 255, .07);
}

button.rr-admin-nav__item {
    border-left: 0;
}

.rr-admin-sidebar__foot {
    padding: .75rem 1rem;
    gap: .625rem;
    background: rgba(0, 0, 0, .3);
}

    .rr-admin-sidebar__foot .material-symbols-outlined {
        font-size: 20px;
        /* A ring around the shield, so the footer has a subject rather than a
           loose glyph next to two lines of text. */
        padding: .3rem;
        border-radius: 999px;
        background: rgba(201, 162, 39, .14);
        border: 1px solid rgba(201, 162, 39, .3);
    }

.rr-admin-sidebar__account {
    font-size: 12px;
}

.rr-admin-sidebar__note {
    font-size: 10px;
    display: flex;
    align-items: center;
    gap: .3rem;
}

    /* A live dot, because "Signed in" is a state and this panel has room for
       exactly one pixel of confirmation. */
    .rr-admin-sidebar__note::before {
        content: "";
        width: 5px;
        height: 5px;
        border-radius: 999px;
        background: #34D399;
        box-shadow: 0 0 0 0 rgba(52, 211, 153, .55);
        animation: rr-ping 2.4s ease-out infinite;
    }

@keyframes rr-ping {
    0% {
        box-shadow: 0 0 0 0 rgba(52, 211, 153, .5);
    }

    70%, 100% {
        box-shadow: 0 0 0 5px rgba(52, 211, 153, 0);
    }
}

/* --- Rail (desktop collapse) ----------------------------------------------
   Toggled by the topbar button and remembered in localStorage by admin-ui.js.
   Below lg the sidebar is a Bootstrap offcanvas and the class does nothing. */
@media (min-width: 992px) {
    .rr-rail-pref .rr-admin {
        --rr-admin-sidebar-w: var(--rr-admin-sidebar-w-mini);
    }

        /* The hover label below is drawn OUTSIDE the 68px rail, and .rr-admin-nav
           is a scroll container — so without this the tooltip was clipped at the
           rail's edge and never appeared at all. Nine icon rows at ~38px clear any
           realistic window height, so dropping the scroll here costs nothing; the
           short-viewport rule at the end of this block puts it back. */
        .rr-rail-pref .rr-admin .rr-admin-nav {
            overflow: visible;
        }

        .rr-rail-pref .rr-admin .rr-admin-sidebar__sub,
        .rr-rail-pref .rr-admin .rr-admin-nav__group,
        .rr-rail-pref .rr-admin .rr-admin-nav__item > span:not(.material-symbols-outlined),
        .rr-rail-pref .rr-admin .rr-admin-sidebar__foot > div {
            display: none;
        }

        .rr-rail-pref .rr-admin .rr-admin-sidebar__wordmark {
            /* The wordmark cannot survive 68px; the icon mark is not in this
               partial, so crop to the monogram end of the artwork instead. */
            width: 36px;
            object-fit: cover;
            object-position: left center;
        }

        .rr-rail-pref .rr-admin .rr-admin-nav__item,
        .rr-rail-pref .rr-admin .rr-admin-sidebar__brand,
        .rr-rail-pref .rr-admin .rr-admin-sidebar__foot {
            justify-content: center;
        }

        .rr-rail-pref .rr-admin .rr-admin-nav__item {
            padding-inline: 0;
        }

        /* The label the collapsed rail dropped, returned on hover. */
        .rr-rail-pref .rr-admin .rr-admin-nav__item::after {
            content: attr(data-label);
            position: absolute;
            left: calc(100% + 10px);
            top: 50%;
            transform: translateY(-50%) scale(.9);
            transform-origin: left center;
            padding: .3rem .5rem;
            border-radius: 6px;
            background: var(--rr-black);
            border: 1px solid rgba(255, 255, 255, .14);
            color: #ffffff;
            font-size: 12px;
            font-weight: 600;
            white-space: nowrap;
            opacity: 0;
            pointer-events: none;
            z-index: 40;
            transition: opacity var(--rr-t-fast) var(--rr-ease), transform var(--rr-t-fast) var(--rr-ease);
        }

        .rr-rail-pref .rr-admin .rr-admin-nav__item:hover::after,
        .rr-rail-pref .rr-admin .rr-admin-nav__item:focus-visible::after {
            opacity: 1;
            transform: translateY(-50%) scale(1);
        }
}

/* Short windows: the nav genuinely may not fit, so scrolling wins over the
   tooltip. The tooltip is suppressed rather than left to be clipped — a label
   sliced in half is worse than no label. */
@media (min-width: 992px) and (max-height: 660px) {
    .rr-rail-pref .rr-admin .rr-admin-nav {
        overflow-y: auto;
        overflow-x: hidden;
    }

        .rr-rail-pref .rr-admin .rr-admin-nav__item::after {
            content: none;
        }
}

/* --- Topbar --------------------------------------------------------------- */
.rr-admin__shell {
    transition: margin-left var(--rr-t-slow) var(--rr-ease);
}

.rr-admin-topbar {
    padding: .5rem .75rem;
    /* Frosted rather than solid: the tinted canvas shows through as the page
       scrolls under it, which is the only cue that the bar is fixed. */
    background: rgba(255, 255, 255, .82);
    backdrop-filter: saturate(1.6) blur(12px);
    -webkit-backdrop-filter: saturate(1.6) blur(12px);
    border-bottom: 1px solid var(--rr-outline);
    transition: box-shadow var(--rr-t) var(--rr-ease), border-color var(--rr-t) var(--rr-ease);
}

/* admin-ui.js adds this past 4px of scroll. */
.rr-admin-topbar.is-stuck {
    box-shadow: 0 4px 18px -10px rgba(14, 14, 14, .35);
    border-bottom-color: transparent;
}

@media (min-width: 768px) {
    .rr-admin-topbar {
        padding-inline: 1.125rem;
    }
}

.rr-admin-topbar__lead {
    gap: .5rem;
}

/* Shared chrome for the two square icon buttons in the bar. */
.rr-admin-topbar__menu,
.rr-admin-rail-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    padding: 0;
    background: none;
    border: 1px solid var(--rr-outline);
    border-radius: 7px;
    color: var(--rr-black);
    line-height: 0;
    transition: background-color var(--rr-t) var(--rr-ease), border-color var(--rr-t) var(--rr-ease), transform var(--rr-t) var(--rr-ease);
}

    .rr-admin-topbar__menu .material-symbols-outlined,
    .rr-admin-rail-toggle .material-symbols-outlined {
        font-size: 20px;
        transition: transform var(--rr-t) var(--rr-ease-spring);
    }

    .rr-admin-topbar__menu:hover,
    .rr-admin-rail-toggle:hover {
        background: var(--rr-surface-2);
        border-color: var(--rr-gold-deep);
    }

        .rr-admin-topbar__menu:hover .material-symbols-outlined,
        .rr-admin-rail-toggle:hover .material-symbols-outlined {
            transform: scale(1.12);
        }

/* Desktop only — below lg the offcanvas button is the control that matters. */
.rr-admin-rail-toggle {
    display: none;
}

@media (min-width: 992px) {
    .rr-admin-rail-toggle {
        display: inline-flex;
    }
}

.rr-rail-pref .rr-admin .rr-admin-rail-toggle .material-symbols-outlined {
    transform: rotate(180deg);
}

.rr-admin-topbar__title {
    font-size: 17px;
    letter-spacing: var(--rr-ls-ui);
}

.rr-admin-crumbs {
    font-size: 11px;
    padding-left: .5rem;
}

    .rr-admin-crumbs a {
        transition: color var(--rr-t-fast) var(--rr-ease);
    }

/* The search must be allowed to shrink, or the topbar overflows: the bar is a
   flex row of menu button + rail button + title + breadcrumb + this, and at
   768-900px a fixed 12.5rem field pushed the row past the viewport and put a
   horizontal scrollbar on every admin page. */
.rr-admin-search {
    flex: 0 1 auto;
    min-width: 0;
}

/* Global order search. Grows on focus — the bar is tight, and the field only
   needs the width while it is being typed into. */
.rr-admin-search .form-control {
    width: 12.5rem;
    max-width: 100%;
    height: var(--rr-ad-control-h);
    padding-left: 2.1rem;
    font-size: 13px;
    border-radius: 999px;
    transition: width var(--rr-t-slow) var(--rr-ease), background-color var(--rr-t) var(--rr-ease), border-color var(--rr-t) var(--rr-ease), box-shadow var(--rr-t) var(--rr-ease);
}

    .rr-admin-search .form-control:focus {
        border-color: var(--rr-gold);
        box-shadow: 0 0 0 3px rgba(201, 162, 39, .22);
    }

/* The focus-grow only where there is room to give. Below this the field keeps
   its resting width and the row stays inside the viewport. */
@media (min-width: 1200px) {
    .rr-admin-search .form-control:focus {
        width: 17rem;
    }
}

.rr-admin-search .material-symbols-outlined {
    left: .7rem;
    font-size: 17px;
    transition: color var(--rr-t) var(--rr-ease);
}

.rr-admin-search:focus-within .material-symbols-outlined {
    color: var(--rr-gold-text);
}

/* --- Main ----------------------------------------------------------------- */
.rr-admin__main {
    padding: var(--rr-ad-gap) .75rem 2rem;
    gap: var(--rr-ad-gap);
}

@media (min-width: 768px) {
    .rr-admin__main {
        padding-inline: 1.125rem;
    }
}

@media (min-width: 1600px) {
    /* Beyond ~1600px the tables stop gaining anything from more width and the
       eye has to travel the whole monitor to pair a name with its actions. */
    .rr-admin__main {
        max-width: 1680px;
        width: 100%;
        margin-inline: auto; /* the cap centres the column instead of pinning it left */
    }
}

/* Page entrance. Direct children of <main> rise in sequence, so a screen
   assembles top-down instead of appearing all at once. */
.rr-admin__main > * {
    animation: rr-rise var(--rr-t-slow) var(--rr-ease) backwards;
}

.rr-admin__main > *:nth-child(1) {
    animation-delay: .02s;
}

.rr-admin__main > *:nth-child(2) {
    animation-delay: .07s;
}

.rr-admin__main > *:nth-child(3) {
    animation-delay: .12s;
}

.rr-admin__main > *:nth-child(4) {
    animation-delay: .17s;
}

.rr-admin__main > *:nth-child(n+5) {
    animation-delay: .21s;
}

/* --- Flash notices -------------------------------------------------------- */
.rr-admin-notice {
    position: relative;
    gap: .5rem;
    padding: .625rem .875rem;
    background: var(--rr-gold-soft);
    border: 1px solid var(--rr-gold);
    border-left: 3px solid var(--rr-gold-deep);
    animation: rr-drop var(--rr-t-slow) var(--rr-ease) backwards;
}

    .rr-admin-notice p {
        font-size: 13px;
    }

    .rr-admin-notice .material-symbols-outlined {
        font-size: 18px;
    }

.rr-admin-notice--warn {
    background: var(--rr-a-amber-soft);
    border-color: #F0D6AE;
    border-left-color: var(--rr-a-amber);
}

    .rr-admin-notice--warn .material-symbols-outlined {
        color: var(--rr-a-amber-text);
    }

/* Added by admin-ui.js — a notice the owner has read should be dismissable. */
.rr-admin-notice__close {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    padding: 0;
    background: none;
    border: 0;
    border-radius: 6px;
    color: inherit;
    opacity: .55;
    line-height: 0;
    cursor: pointer;
    transition: opacity var(--rr-t) var(--rr-ease), background-color var(--rr-t) var(--rr-ease), transform var(--rr-t) var(--rr-ease-spring);
}

    .rr-admin-notice__close .material-symbols-outlined {
        font-size: 17px;
    }

    .rr-admin-notice__close:hover {
        opacity: 1;
        background: rgba(14, 14, 14, .07);
        transform: rotate(90deg);
    }

.rr-admin-notice.is-leaving {
    opacity: 0;
    transform: translateY(-6px);
    transition: opacity var(--rr-t) var(--rr-ease), transform var(--rr-t) var(--rr-ease);
}

/* --------------------------------------------------------------------------
   34.3 Dashboard stat cards
   -------------------------------------------------------------------------- */
.rr-stat-grid {
    gap: .75rem;
}

@media (min-width: 768px) {
    .rr-stat-grid {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (min-width: 1200px) {
    .rr-stat-grid {
        grid-template-columns: repeat(4, 1fr);
    }
}

.rr-admin .rr-stat {
    position: relative;
    padding: .875rem 1rem 1rem;
    overflow: hidden;
    border-color: var(--rr-outline);
    transition: transform var(--rr-t) var(--rr-ease), box-shadow var(--rr-t) var(--rr-ease), border-color var(--rr-t) var(--rr-ease);
}

    /* The accent rail across the top. Grows to full width on hover — the card's
       one piece of colour, and the thing that ties it to its nav item. */
    .rr-admin .rr-stat::before {
        content: "";
        position: absolute;
        inset: 0 auto auto 0;
        height: 3px;
        width: 34%;
        background: linear-gradient(90deg, var(--rr-accent), color-mix(in srgb, var(--rr-accent) 35%, transparent));
        transition: width var(--rr-t-slow) var(--rr-ease);
    }

    /* A large ghost of the accent bleeding out of the corner. Very low alpha —
       it should register as warmth, not as a shape. */
    .rr-admin .rr-stat::after {
        content: "";
        position: absolute;
        right: -38px;
        top: -38px;
        width: 118px;
        height: 118px;
        border-radius: 999px;
        background: var(--rr-accent);
        opacity: .06;
        transition: transform var(--rr-t-slow) var(--rr-ease), opacity var(--rr-t-slow) var(--rr-ease);
    }

    .rr-admin .rr-stat:hover {
        transform: translateY(-3px);
        box-shadow: var(--rr-ad-lift);
        border-color: color-mix(in srgb, var(--rr-accent) 45%, var(--rr-outline));
    }

        .rr-admin .rr-stat:hover::before {
            width: 100%;
        }

        .rr-admin .rr-stat:hover::after {
            transform: scale(1.3);
            opacity: .1;
        }

.rr-admin .rr-stat__head {
    gap: .5rem;
    margin-bottom: .5rem;
}

.rr-admin .rr-stat__icon {
    position: relative;
    z-index: 1;
    width: 34px;
    height: 34px;
    border-radius: 9px;
    background: var(--rr-accent-soft);
    color: var(--rr-accent-text);
    margin-bottom: .5rem;
    transition: transform var(--rr-t) var(--rr-ease-spring), background-color var(--rr-t) var(--rr-ease), color var(--rr-t) var(--rr-ease);
}

    .rr-admin .rr-stat__icon .material-symbols-outlined {
        font-size: 19px;
    }

.rr-admin .rr-stat:hover .rr-stat__icon {
    transform: scale(1.08) rotate(-5deg);
    background: var(--rr-accent);
    color: #ffffff;
}

/* The icon sits above the label in three of the four cards and inside __head in
   the fourth; kill the doubled gap when it is in the head. */
.rr-admin .rr-stat__head .rr-stat__icon {
    margin-bottom: 0;
}

.rr-admin .rr-stat__tag {
    position: relative;
    z-index: 1;
    font-size: 9px;
    padding: .15rem .5rem;
    background: var(--rr-surface);
    border-color: var(--rr-outline);
}

.rr-admin .rr-stat__label {
    position: relative;
    z-index: 1;
    font-size: 10px;
    letter-spacing: var(--rr-ls-caps);
    margin-bottom: .1rem;
}

.rr-admin .rr-stat__value {
    position: relative;
    z-index: 1;
    font-size: 30px;
    letter-spacing: var(--rr-ls-ui);
    font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------------------------
   34.4 Panels
   -------------------------------------------------------------------------- */
.rr-admin-panel {
    background: var(--rr-surface);
    border-color: var(--rr-outline);
    transition: box-shadow var(--rr-t) var(--rr-ease), border-color var(--rr-t) var(--rr-ease);
}

    .rr-admin-panel:hover {
        border-color: #D8D5C6;
        box-shadow: var(--rr-ad-lift);
    }

.rr-admin-panel--pad {
    padding: var(--rr-ad-pad);
}

.rr-admin-panel__head {
    position: relative;
    padding: .625rem var(--rr-ad-pad);
    gap: .375rem .75rem;
    /* A hair of the accent behind the heading strip. */
    background: linear-gradient(90deg, var(--rr-accent-soft), transparent 62%);
}

.rr-admin-panel__head--flush {
    padding: 0 0 .625rem;
    background: none;
}

    .rr-admin-panel__head--flush .material-symbols-outlined {
        font-size: 18px;
        color: var(--rr-accent-text);
    }

.rr-admin-panel__title {
    display: flex;
    align-items: center;
    gap: .45rem;
    font-size: 15px;
    letter-spacing: var(--rr-ls-ui);
}

    /* A short accent tick in front of every panel title. */
    .rr-admin-panel__title::before {
        content: "";
        flex: 0 0 auto;
        width: 3px;
        height: 13px;
        border-radius: 2px;
        background: var(--rr-accent);
    }

.rr-admin-panel__head--flush .rr-admin-panel__title::before {
    display: none;
}

/* --------------------------------------------------------------------------
   34.5 Tables
   -------------------------------------------------------------------------- */
.rr-admin-table > thead th {
    padding: .5rem var(--rr-ad-cell-x);
    font-size: 9px;
    letter-spacing: var(--rr-ls-caps);
    background: var(--rr-surface-2);
    /* Keeps the header line legible when a long table scrolls inside a panel. */
    position: sticky;
    top: 0;
    z-index: 2;
}

.rr-admin-table > tbody td,
.rr-admin-table > tbody th {
    padding: var(--rr-ad-pad-tight) var(--rr-ad-cell-x);
    font-size: var(--rr-ad-text);
    border-bottom-color: #EEEDE6;
}

.rr-admin-table > tbody tr {
    position: relative;
    transition: background-color var(--rr-t-fast) var(--rr-ease);
}

    /* A left accent bar that grows in on hover, drawn on the first cell so it
       does not need a pseudo-element on the <tr> (which Chrome will not
       position reliably against a table row). */
    .rr-admin-table > tbody tr > *:first-child {
        position: relative;
    }

        .rr-admin-table > tbody tr > *:first-child::before {
            content: "";
            position: absolute;
            left: 0;
            top: 0;
            bottom: 0;
            width: 3px;
            background: var(--rr-accent);
            transform: scaleY(0);
            transform-origin: center;
            transition: transform var(--rr-t) var(--rr-ease);
        }

/* Zebra, very faint — at this row height the eye needs help tracking across
   eight columns, and a hover state alone does not survive a mouse-less scan.

   This MUST stay above the :hover rule below. :nth-child() and :hover carry
   identical specificity, so whichever is declared last wins — with the order
   reversed, every even row silently lost its hover state. */
.rr-admin-table > tbody tr:nth-child(even) > * {
    background: rgba(247, 247, 245, .55);
}

.rr-admin-table > tbody tr:hover > * {
    background: linear-gradient(90deg, var(--rr-accent-soft), var(--rr-surface-2) 45%);
}

.rr-admin-table > tbody tr:hover > *:first-child::before {
    transform: scaleY(1);
}

.rr-admin-table__ref a {
    font-size: 14px;
    transition: color var(--rr-t-fast) var(--rr-ease);
}

.rr-admin-table__sub,
.rr-admin-table__time {
    font-size: 11px;
}

.rr-admin-table__sku {
    font-family: ui-monospace, SFMono-Regular, "Cascadia Mono", Menlo, monospace;
    font-size: 11px;
}

/* Row actions rest at half opacity and come up on hover or keyboard focus, so a
   dense table is not a wall of icons. :focus-within keeps them reachable by tab. */
.rr-admin-actions {
    gap: .125rem;
    opacity: .5;
    transition: opacity var(--rr-t) var(--rr-ease);
}

tr:hover .rr-admin-actions,
tr:focus-within .rr-admin-actions {
    opacity: 1;
}

.rr-admin-table__action {
    width: 28px;
    height: 28px;
    border-radius: 7px;
    transition: background-color var(--rr-t-fast) var(--rr-ease), color var(--rr-t-fast) var(--rr-ease), transform var(--rr-t-fast) var(--rr-ease-spring);
}

    .rr-admin-table__action .material-symbols-outlined {
        font-size: 18px;
    }

    .rr-admin-table__action:hover {
        background: var(--rr-accent-soft);
        color: var(--rr-accent-text);
        transform: translateY(-1px) scale(1.08);
    }

.rr-admin-thumb {
    width: 42px;
    height: 42px;
    border-radius: 7px;
    transition: transform var(--rr-t) var(--rr-ease-spring), border-color var(--rr-t) var(--rr-ease);
}

    .rr-admin-thumb .material-symbols-outlined {
        font-size: 18px;
    }

tr:hover .rr-admin-thumb {
    transform: scale(1.07);
    border-color: var(--rr-accent);
}

.rr-admin-thumb-cell {
    width: 3.5rem;
}

/* --- Tag pills ------------------------------------------------------------ */
.rr-admin-tag {
    padding: .12rem .45rem;
    font-size: 9px;
    background: var(--rr-surface-2);
    transition: background-color var(--rr-t) var(--rr-ease), border-color var(--rr-t) var(--rr-ease), color var(--rr-t) var(--rr-ease);
}

tr:hover .rr-admin-tag {
    background: var(--rr-accent-soft);
    border-color: color-mix(in srgb, var(--rr-accent) 40%, transparent);
    color: var(--rr-accent-text);
}

.rr-admin-tag--warn {
    background: var(--rr-a-amber-soft);
    border-color: var(--rr-a-amber);
    color: var(--rr-a-amber-text);
}

/* Active / Inactive. Still named in words; the dot is now green/grey rather
   than gold/hollow, which is the one place a literal on-off reading helps. */
.rr-admin-state {
    font-size: 11px;
    letter-spacing: var(--rr-ls-wide);
}

    .rr-admin-state::before {
        width: 6px;
        height: 6px;
        transition: background-color var(--rr-t) var(--rr-ease), box-shadow var(--rr-t) var(--rr-ease);
    }

    .rr-admin-state.is-on::before {
        background: #10B981;
        border-color: #059669;
        box-shadow: 0 0 0 3px rgba(16, 185, 129, .16);
    }

/* --- Toolbar -------------------------------------------------------------- */
.rr-admin-toolbar {
    gap: .625rem;
    padding: .625rem .75rem;
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
}

.rr-admin-toolbar__filters {
    gap: .5rem;
}

.rr-admin-toolbar__search .form-control {
    height: var(--rr-ad-control-h);
    padding-left: 2.25rem;
    font-size: 13px;
    background: var(--rr-surface-2);
    transition: background-color var(--rr-t) var(--rr-ease), border-color var(--rr-t) var(--rr-ease), box-shadow var(--rr-t) var(--rr-ease);
}

    .rr-admin-toolbar__search .form-control:focus {
        background: var(--rr-surface);
        border-color: var(--rr-gold);
        box-shadow: 0 0 0 3px rgba(201, 162, 39, .2);
    }

.rr-admin-toolbar__search > .material-symbols-outlined {
    font-size: 18px;
    left: .65rem;
}

.rr-admin-toolbar__select .form-select {
    height: var(--rr-ad-control-h);
    padding-block: 0;
    font-size: 13px;
}

.rr-admin-toolbar__count {
    font-size: 11px;
}

/* --------------------------------------------------------------------------
   34.6 Buttons + controls
   -------------------------------------------------------------------------- */
/* Motion applies to both surfaces — the lift and the gold sheen are as welcome
   on the sign-in button as in the console. */
.rr-admin .btn,
.rr-admin-auth .btn {
    transition: transform var(--rr-t) var(--rr-ease), box-shadow var(--rr-t) var(--rr-ease), background-color var(--rr-t) var(--rr-ease), border-color var(--rr-t) var(--rr-ease), color var(--rr-t) var(--rr-ease);
}

/* Type size does not. This sits above .btn-lg in specificity, so leaving the
   sign-in screen in would have shrunk its submit from the theme's 15px to 13px. */
.rr-admin:not(.rr-admin-auth) .btn {
    font-size: 13px;
}

.rr-admin:not(.rr-admin-auth) .btn {
    min-height: var(--rr-ad-control-h);
    padding: .35rem .8rem;
}

/* The sign-in submit is deliberately 54px (.rr-admin-auth__submit) and is left
   alone — this only compacts the console's own large buttons. */
.rr-admin:not(.rr-admin-auth) .btn-lg {
    min-height: 44px;
}

.rr-admin .btn:hover,
.rr-admin-auth .btn:hover {
    transform: translateY(-1px);
    box-shadow: var(--rr-ad-lift);
}

.rr-admin .btn:active,
.rr-admin-auth .btn:active {
    transform: translateY(0);
    box-shadow: none;
}

/* The gold CTA gets a sheen that crosses it once per hover. Gold stays the only
   button fill in the admin (CLAUDE.md) — the accents never reach a button. */
.rr-admin .btn-gold,
.rr-admin-auth .btn-gold {
    position: relative;
    overflow: hidden;
}

    .rr-admin .btn-gold::after,
    .rr-admin-auth .btn-gold::after {
        content: "";
        position: absolute;
        top: -50%;
        left: 0;
        width: 40%;
        height: 200%;
        background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .5), transparent);
        transform: translateX(-120%) skewX(-18deg);
        pointer-events: none;
    }

    .rr-admin .btn-gold:hover::after,
    .rr-admin-auth .btn-gold:hover::after {
        animation: rr-sheen .7s var(--rr-ease);
    }

.rr-admin:not(.rr-admin-auth) .rr-btn-icon .material-symbols-outlined {
    font-size: 18px;
}

/* The icon nudge is motion, so it stays on both surfaces. */
.rr-admin .rr-btn-icon .material-symbols-outlined {
    transition: transform var(--rr-t) var(--rr-ease-spring);
}

.rr-admin .rr-btn-icon:hover .material-symbols-outlined {
    transform: scale(1.14);
}

/* --- Inputs ---------------------------------------------------------------
   NOTE: `padding-block` only, never the `padding` shorthand. Several admin
   inputs are icon-inset — the topbar order search, the products/orders toolbar
   search, and the sign-in username/password — and each carries its own
   `padding-left` from a `.rr-x .form-control` rule of identical specificity
   declared EARLIER in this file. A shorthand here silently resets that left
   padding to the horizontal value, dropping the text back under its own icon.
   That is exactly what happened, on all three. Vertical padding is where the
   density gain is anyway; horizontal stays at Bootstrap's .75rem. */
.rr-admin:not(.rr-admin-auth) .form-control,
.rr-admin:not(.rr-admin-auth) .form-select {
    font-size: 13px;
    padding-block: .4rem;
    border-color: var(--rr-outline);
    transition: border-color var(--rr-t) var(--rr-ease), box-shadow var(--rr-t) var(--rr-ease), background-color var(--rr-t) var(--rr-ease);
}

.rr-admin:not(.rr-admin-auth) input.form-control,
.rr-admin:not(.rr-admin-auth) .form-select {
    min-height: var(--rr-ad-control-h);
}

.rr-admin:not(.rr-admin-auth) .form-control:focus,
.rr-admin:not(.rr-admin-auth) .form-select:focus {
    border-color: var(--rr-gold);
    box-shadow: 0 0 0 3px rgba(201, 162, 39, .2);
}

.rr-admin:not(.rr-admin-auth) textarea.form-control {
    min-height: 4.5rem;
    line-height: var(--rr-lh-body);
}

/* --------------------------------------------------------------------------
   34.7 Forms
   -------------------------------------------------------------------------- */
/* Guarded like the inputs above: the sign-in form is also a .rr-form with
   .rr-label fields, and it wants its own roomier rhythm. */
.rr-admin:not(.rr-admin-auth) .rr-form > * {
    margin-bottom: .875rem;
}

.rr-admin:not(.rr-admin-auth) .rr-label {
    font-size: 10px;
    letter-spacing: var(--rr-ls-wide);
    margin-bottom: .3rem;
    transition: color var(--rr-t-fast) var(--rr-ease);
}

.rr-admin .rr-field:focus-within .rr-label {
    color: var(--rr-gold-text);
}

.rr-admin .rr-hint {
    font-size: 11px;
    line-height: var(--rr-lh-ui);
    margin-top: .3rem;
}

.rr-admin .rr-grid-2,
.rr-admin .rr-settings-grid {
    gap: .75rem;
}

/* NOTE on two-column forms: a `.rr-form--split` was tried here and dropped. The
   admin forms are deliberately hint-heavy — nearly every field carries a .rr-hint
   paragraph explaining what it does to the storefront — so flowing them two-up
   produced badly ragged columns wherever a one-line field sat beside a four-line
   one. The compaction above (10px labels, .875rem field spacing, 36px controls,
   11px hints) takes roughly a third off the same forms without that cost. Where a
   genuine pair of short fields exists, the screens already use .rr-grid-2. */

/* Sticky action bar. Save is the one control the owner reaches for on a 570-line
   product form and it was at the very bottom of it — on the product and category
   screens you had to scroll past every panel to reach a button you wanted the
   whole time. It stays a stacked grid (the block lives in the narrow right-hand
   column, where a row of buttons would not fit); only the position changes. */
.rr-admin .rr-form-actions {
    position: sticky;
    bottom: .625rem;
    z-index: 5;
    gap: .5rem;
    margin-top: .25rem;
    padding: .625rem .875rem;
    background: rgba(255, 255, 255, .9);
    backdrop-filter: saturate(1.6) blur(10px);
    -webkit-backdrop-filter: saturate(1.6) blur(10px);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    box-shadow: 0 -4px 18px -12px rgba(14, 14, 14, .3);
}

/* --- Switches / chips ----------------------------------------------------- */
.rr-admin .form-switch .form-check-input {
    transition: background-color var(--rr-t) var(--rr-ease), border-color var(--rr-t) var(--rr-ease), background-position var(--rr-t) var(--rr-ease);
}

.rr-admin .form-check-input:checked {
    background-color: var(--rr-gold);
    border-color: var(--rr-gold-deep);
}

.rr-admin .form-check-input:focus {
    border-color: var(--rr-gold);
    box-shadow: 0 0 0 3px rgba(201, 162, 39, .22);
}

.rr-admin .rr-chip {
    font-size: 11px;
    transition: background-color var(--rr-t) var(--rr-ease), border-color var(--rr-t) var(--rr-ease), transform var(--rr-t) var(--rr-ease-spring);
}

    .rr-admin .rr-chip:hover {
        transform: translateY(-1px);
    }

/* --- Tabs ----------------------------------------------------------------- */
.rr-admin .rr-tabs--admin.nav-tabs .nav-link {
    position: relative;
    font-size: 12px;
    padding: .45rem .75rem;
    transition: color var(--rr-t) var(--rr-ease), background-color var(--rr-t) var(--rr-ease);
}

    /* The active underline slides rather than jumping between tabs. */
    .rr-admin .rr-tabs--admin.nav-tabs .nav-link::after {
        content: "";
        position: absolute;
        left: .5rem;
        right: .5rem;
        bottom: -1px;
        height: 2px;
        border-radius: 2px;
        background: var(--rr-gold);
        transform: scaleX(0);
        transition: transform var(--rr-t) var(--rr-ease);
    }

    .rr-admin .rr-tabs--admin.nav-tabs .nav-link:hover::after {
        transform: scaleX(.45);
    }

    .rr-admin .rr-tabs--admin.nav-tabs .nav-link.active::after {
        transform: scaleX(1);
    }

/* --------------------------------------------------------------------------
   34.8 Detail screens — head, bento, facts, notes
   -------------------------------------------------------------------------- */
.rr-admin-head {
    gap: .625rem;
}

.rr-admin-head__title {
    font-size: 23px;
}

.rr-admin-head__meta {
    font-size: 12px;
    margin-top: .25rem;
}

.rr-admin-head__actions {
    gap: .625rem;
}

.rr-admin-back {
    font-size: 12px;
    transition: color var(--rr-t-fast) var(--rr-ease), transform var(--rr-t) var(--rr-ease);
}

    .rr-admin-back:hover {
        transform: translateX(-2px);
    }

    .rr-admin-back .material-symbols-outlined {
        font-size: 16px;
    }

.rr-admin-bento {
    gap: .875rem;
}

.rr-admin-bento__icon {
    transition: transform var(--rr-t) var(--rr-ease-spring);
}

.rr-admin-panel:hover .rr-admin-bento__icon {
    transform: scale(1.08);
}

.rr-admin-facts {
    gap: .625rem;
}

    .rr-admin-facts dt {
        font-size: 9px;
        letter-spacing: var(--rr-ls-caps);
    }

    .rr-admin-facts dd {
        font-size: 13px;
        margin-top: .1rem;
    }

.rr-admin-address {
    font-size: 13px;
    line-height: var(--rr-lh-body);
    margin-bottom: .75rem;
}

.rr-admin-summary__row {
    font-size: 13px;
    padding-block: .25rem;
}

.rr-admin-summary__total {
    font-size: 15px;
    margin-top: .5rem;
    padding-top: .625rem;
}

.rr-admin-summary__gst {
    font-size: 11px;
    margin-top: .5rem;
}

.rr-admin-item {
    gap: .625rem;
}

.rr-admin-item__media {
    width: 38px;
    height: 38px;
    border-radius: 7px;
}

/* Notes read as a timeline: each is a card off a shared rail. */
.rr-admin-notes {
    gap: .5rem;
    position: relative;
}

.rr-admin-note {
    padding: .625rem .75rem;
    border-left-width: 2px;
    border-left-color: var(--rr-a-indigo);
    border-radius: 0 var(--rr-radius) var(--rr-radius) 0;
    animation: rr-rise var(--rr-t) var(--rr-ease) backwards;
    transition: background-color var(--rr-t) var(--rr-ease), border-left-color var(--rr-t) var(--rr-ease);
}

    .rr-admin-note:hover {
        background: var(--rr-a-indigo-soft);
    }

.rr-admin-note__time {
    font-size: 10px;
}

.rr-admin-note__body {
    font-size: 13px;
    margin-top: .25rem;
}

.rr-admin-note-form {
    margin-bottom: 1rem;
}

/* --------------------------------------------------------------------------
   34.9 Empty states + pagination
   -------------------------------------------------------------------------- */
.rr-empty--admin {
    padding-block: 30px;
}

    .rr-empty--admin h2,
    .rr-empty--admin h3 {
        font-size: 18px;
    }

    .rr-empty--admin p {
        font-size: 13px;
    }

    /* The icon breathes, so an empty screen still has a heartbeat. */
    .rr-empty--admin > .material-symbols-outlined {
        animation: rr-float 4s ease-in-out infinite;
    }

@keyframes rr-float {
    0%, 100% {
        transform: translateY(0);
    }

    50% {
        transform: translateY(-6px);
    }
}

.rr-pagination--admin {
    padding: .625rem .75rem;
}

.rr-admin .rr-page-link {
    transition: background-color var(--rr-t-fast) var(--rr-ease), border-color var(--rr-t-fast) var(--rr-ease), transform var(--rr-t-fast) var(--rr-ease);
}

    .rr-admin .rr-page-link:hover {
        transform: translateY(-1px);
    }

/* --------------------------------------------------------------------------
   34.10 Media, category tree, banners
   -------------------------------------------------------------------------- */
.rr-admin .rr-media-card,
.rr-admin .rr-banner-card,
.rr-admin .rr-content-slot {
    transition: transform var(--rr-t) var(--rr-ease), box-shadow var(--rr-t) var(--rr-ease), border-color var(--rr-t) var(--rr-ease);
}

    .rr-admin .rr-media-card:hover,
    .rr-admin .rr-banner-card:hover,
    .rr-admin .rr-content-slot:hover {
        transform: translateY(-2px);
        box-shadow: var(--rr-ad-lift);
        border-color: var(--rr-gold);
    }

.rr-admin .rr-cat-row {
    transition: background-color var(--rr-t) var(--rr-ease), border-color var(--rr-t) var(--rr-ease);
}

    .rr-admin .rr-cat-row:hover {
        background: var(--rr-a-violet-soft);
        border-color: color-mix(in srgb, var(--rr-a-violet) 35%, transparent);
    }

.rr-admin .rr-row__btn,
.rr-admin .rr-editor__btn {
    transition: background-color var(--rr-t-fast) var(--rr-ease), color var(--rr-t-fast) var(--rr-ease), transform var(--rr-t-fast) var(--rr-ease-spring);
}

    .rr-admin .rr-row__btn:hover,
    .rr-admin .rr-editor__btn:hover {
        transform: translateY(-1px) scale(1.06);
    }

.rr-admin .rr-upload__drop {
    transition: background-color var(--rr-t) var(--rr-ease), border-color var(--rr-t) var(--rr-ease);
}

    .rr-admin .rr-upload__drop:hover {
        border-color: var(--rr-gold);
        background: var(--rr-gold-soft);
    }

/* --- Loading placeholder (available, not yet wired) ----------------------- */
.rr-skeleton {
    border-radius: var(--rr-radius);
    background: linear-gradient(90deg, #EFEFEA 25%, #F8F8F5 50%, #EFEFEA 75%);
    background-size: 200% 100%;
    animation: rr-shimmer 1.4s linear infinite;
    color: transparent;
    user-select: none;
}

/* --------------------------------------------------------------------------
   34.11 Sign-in screen
   -------------------------------------------------------------------------- */
.rr-admin-auth__stage {
    /* Two slow drifting colour fields behind the split panel. */
    background-image: radial-gradient(760px 460px at 12% 8%, rgba(79, 70, 229, .2), transparent 62%), radial-gradient(700px 440px at 88% 92%, rgba(201, 162, 39, .2), transparent 60%);
    animation: rr-drift 22s ease-in-out infinite alternate;
}

@keyframes rr-drift {
    from {
        background-position: 0% 0%, 100% 100%;
    }

    to {
        background-position: 14% 8%, 86% 92%;
    }
}

.rr-admin-auth__shell {
    animation: rr-rise .5s var(--rr-ease) backwards;
    animation-delay: .06s;
    box-shadow: var(--rr-ad-lift-lg);
}

/* The left panel was a flat black rectangle with a logo floating in it. It now
   carries the workshop photograph — the bat maker shaping willow — which is
   already monochrome and so sits inside the black/gold palette without fighting
   it. Copied to img/admin/ rather than referenced from wwwroot/uploads: uploads
   is owner-managed content that the admin can replace or delete, and the sign-in
   screen must not be able to lose its own background.

   Two layers, image underneath. The gradient is heaviest down the left and
   bottom, which is exactly where the wordmark, headline and capability row sit —
   white on those stops stays above 12:1, so the text is never relying on a
   lucky part of the photograph. It lightens toward the top right, where nothing
   is drawn, so the picture is still legible as a picture.

   image-set() serves the WebP and falls back to the JPEG; the plain url() line
   before it covers engines that do not know image-set at all. */
.rr-admin-auth__brand-panel {
    background-color: var(--rr-black);
    background-image: linear-gradient(155deg, rgba(10, 10, 12, .93) 0%, rgba(14, 14, 14, .88) 38%, rgba(14, 14, 14, .72) 68%, rgba(10, 10, 12, .95) 100%), url("../img/admin/login-panel.jpg");
    background-image: linear-gradient(155deg, rgba(10, 10, 12, .93) 0%, rgba(14, 14, 14, .88) 38%, rgba(14, 14, 14, .72) 68%, rgba(10, 10, 12, .95) 100%), image-set(url("../img/admin/login-panel.webp") type("image/webp"), url("../img/admin/login-panel.jpg") type("image/jpeg"));
    background-size: cover;
    background-position: 62% center;
    background-repeat: no-repeat;
}

.rr-admin-auth__monogram {
    animation: rr-pop .55s var(--rr-ease-spring) backwards;
    animation-delay: .18s;
}

.rr-admin-auth__intro > * {
    animation: rr-rise .5s var(--rr-ease) backwards;
}

.rr-admin-auth__intro > *:nth-child(1) {
    animation-delay: .2s;
}

.rr-admin-auth__intro > *:nth-child(2) {
    animation-delay: .27s;
}

.rr-admin-auth__intro > *:nth-child(3) {
    animation-delay: .34s;
}

.rr-admin-auth__capabilities > div {
    animation: rr-rise .5s var(--rr-ease) backwards;
    transition: transform var(--rr-t) var(--rr-ease), border-color var(--rr-t) var(--rr-ease);
}

    .rr-admin-auth__capabilities > div:nth-child(1) {
        animation-delay: .4s;
    }

    .rr-admin-auth__capabilities > div:nth-child(2) {
        animation-delay: .46s;
    }

    .rr-admin-auth__capabilities > div:nth-child(3) {
        animation-delay: .52s;
    }

    .rr-admin-auth__capabilities > div:hover {
        transform: translateY(-2px);
        border-color: var(--rr-gold);
    }

.rr-admin-auth__form-wrap > * {
    animation: rr-rise .5s var(--rr-ease) backwards;
}

.rr-admin-auth__form-wrap > *:nth-child(1) {
    animation-delay: .24s;
}

.rr-admin-auth__form-wrap > *:nth-child(2) {
    animation-delay: .3s;
}

.rr-admin-auth__form-wrap > *:nth-child(3) {
    animation-delay: .36s;
}

.rr-admin-auth__form-wrap > *:nth-child(4) {
    animation-delay: .44s;
}

.rr-admin-auth__control {
    transition: border-color var(--rr-t) var(--rr-ease), box-shadow var(--rr-t) var(--rr-ease), background-color var(--rr-t) var(--rr-ease);
}

    .rr-admin-auth__control:focus-within {
        border-color: var(--rr-gold);
        box-shadow: 0 0 0 3px rgba(201, 162, 39, .22);
    }

    .rr-admin-auth__control > .material-symbols-outlined:first-child {
        transition: transform var(--rr-t) var(--rr-ease-spring), color var(--rr-t) var(--rr-ease);
    }

    .rr-admin-auth__control:focus-within > .material-symbols-outlined:first-child {
        transform: scale(1.12);
    }

.rr-admin-auth__back {
    transition: transform var(--rr-t) var(--rr-ease), color var(--rr-t) var(--rr-ease);
}

    .rr-admin-auth__back:hover {
        transform: translateX(-3px);
    }

/* --------------------------------------------------------------------------
   34.12 Small screens
   Below md the density above is too tight for touch: controls go back to 40px
   and the tables get a little of their padding back.
   -------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
    .rr-admin {
        --rr-ad-control-h: 40px;
        --rr-ad-cell-x: .625rem;
    }

    .rr-admin-table__action {
        width: 34px;
        height: 34px;
    }

    .rr-admin-actions {
        opacity: 1; /* no hover on touch — the actions must always be visible */
    }

    .rr-admin-head__title {
        font-size: 20px;
    }

    .rr-admin .rr-stat__value {
        font-size: 26px;
    }

    .rr-admin .rr-form-actions {
        flex-wrap: wrap;
    }
}

/* ==========================================================================
   35. Storefront motion & polish layer
   ==========================================================================

   Section 34 gave the ADMIN a motion system. The storefront never got one - it
   had eight transitions in total (a card zoom, a tile zoom, four link colours,
   the skip link, the hero dots) and no entrances at all, which is why the shop
   read as flat next to its own back office.

   This section is that layer for the customer-facing side. Four rules govern
   all of it:

   1. TRANSFORM AND OPACITY ONLY. Nothing here animates width, height, top or
      margin, so nothing triggers layout and the whole layer stays on the
      compositor.

   2. THE RESTING STATE IS UNCHANGED. CLAUDE.md's "borders over shadows, keep it
      flat" describes the page at rest, and it still does - every lift, glow and
      shadow below is on :hover, :focus-visible or :active. Take the pointer
      away and the design is the approved one.

   3. HOVER EFFECTS ARE GATED ON @media (hover: hover). A tap on a touch screen
      leaves :hover latched until the next tap elsewhere, so an ungated hover
      lift means a phone user acquires a stuck raised card. The existing card
      and tile zooms had exactly that bug; they are re-gated here.

   4. ENTRANCES ARE PROGRESSIVE ENHANCEMENT. The reveal rules are scoped to
      .rr-js on <html>, set by an inline script in _Layout's <head>. With JS off
      or broken, .rr-js is absent, nothing is ever hidden, and the page renders
      exactly as it does today. royal-rider.js additionally declines to tag
      anything when prefers-reduced-motion is set, so those visitors never enter
      the reveal path at all - the global block in section 2 is the second net,
      not the only one.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   35.1 Motion tokens
   --------------------------------------------------------------------------
   Section 34 declared these on .rr-admin only, so no storefront rule could
   reach them. Promoted to :root at the same values; .rr-admin still redeclares
   them, which is now a harmless no-op rather than the only declaration. */
:root {
    --rr-ease: cubic-bezier(.22, .61, .36, 1); /* decelerate - entrances, lifts */
    --rr-ease-spring: cubic-bezier(.34, 1.4, .64, 1); /* slight overshoot - icons, pops */
    --rr-ease-inout: cubic-bezier(.65, .05, .36, 1); /* symmetric - drawers, panels */
    --rr-t-fast: .14s;
    --rr-t: .22s;
    --rr-t-slow: .38s;
    --rr-t-reveal: .72s; /* scroll entrances - long enough to read as intentional */
    --rr-stagger: 70ms; /* per-item delay inside a revealed group */
    /* Hover elevation. Warm-tinted rather than neutral grey, so it reads as light
       falling on a white studio wall and not as a Material drop shadow. */
    --rr-lift: 0 10px 26px -14px rgba(28, 22, 6, .3), 0 2px 6px -3px rgba(28, 22, 6, .1);
    --rr-lift-lg: 0 20px 46px -22px rgba(28, 22, 6, .36), 0 4px 10px -5px rgba(28, 22, 6, .12);
    --rr-glow-gold: 0 0 0 1px rgba(201, 162, 39, .38), 0 12px 30px -16px rgba(201, 162, 39, .55);
}

/* --------------------------------------------------------------------------
   35.2 Keyframes
   --------------------------------------------------------------------------
   rr-rise / rr-pop / rr-sheen / rr-shimmer / rr-float / rr-drift / rr-drop
   already exist in section 34 and are global, so they are reused rather than
   restated. Only what the storefront needs beyond them is declared here. */

/* The hero's copy: further and slower than rr-rise, because it is the largest
   type on the site and a 10px move on a 64px headline is invisible. */
@keyframes rr-hero-in {
    from {
        opacity: 0;
        transform: translateY(22px);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

/* A very slow push-in on the hero photograph. 1.0 to 1.06 over 20s is under a
   third of a pixel per frame: felt rather than seen, which is the point. */
@keyframes rr-kenburns {
    from {
        transform: scale(1);
    }

    to {
        transform: scale(1.06);
    }
}

/* The wishlist heart when it becomes saved - overshoot, because a save is a
   moment of commitment and deserves one. */
@keyframes rr-heartbeat {
    0% {
        transform: scale(1);
    }

    30% {
        transform: scale(1.32);
    }

    55% {
        transform: scale(.92);
    }

    100% {
        transform: scale(1);
    }
}

/* --------------------------------------------------------------------------
   35.3 Scroll reveal
   --------------------------------------------------------------------------
   royal-rider.js tags a fixed list of storefront blocks with .rr-reveal and an
   optional --rr-i index, then swaps in .is-revealed as each enters the viewport.

   The .rr-js guard is the load-bearing part: the hidden state exists ONLY when
   the script that can un-hide it is present. A reveal system that hides content
   from a browser it cannot then reach is not a nice animation, it is a blank
   page, and that failure mode is removed structurally here rather than being
   guarded against inside the script. */
.rr-js .rr-reveal {
    opacity: 0;
    transform: translateY(20px);
    transition: opacity var(--rr-t-reveal) var(--rr-ease), transform var(--rr-t-reveal) var(--rr-ease);
    transition-delay: calc(var(--rr-i, 0) * var(--rr-stagger));
}

.rr-js .rr-reveal--fade {
    transform: none;
}

.rr-js .rr-reveal--zoom {
    transform: scale(.955);
}

.rr-js .rr-reveal--left {
    transform: translateX(-26px);
}

.rr-js .rr-reveal--right {
    transform: translateX(26px);
}

.rr-js .rr-reveal.is-revealed {
    opacity: 1;
    transform: none;
}

/* The section heading's gold rule draws itself once its heading has arrived.
   transform-origin left, so it grows out of the words rather than out of its
   own centre. */
.rr-js .rr-reveal .rr-section-head__rule {
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform .6s var(--rr-ease) .18s;
}

.rr-js .rr-reveal.is-revealed .rr-section-head__rule {
    transform: scaleX(1);
}

/* --------------------------------------------------------------------------
   35.4 Header
   --------------------------------------------------------------------------
   The header is position:sticky at top:0, so it is "stuck" from the very first
   pixel and CSS alone cannot tell the top of the page from a scrolled page.
   royal-rider.js sets .is-stuck past 8px; everything here keys off that. */
.rr-header {
    transition: box-shadow var(--rr-t-slow) var(--rr-ease), background-color var(--rr-t-slow) var(--rr-ease), border-color var(--rr-t-slow) var(--rr-ease);
}

    .rr-header.is-stuck {
        border-bottom-color: rgba(201, 162, 39, .32);
        box-shadow: 0 8px 28px -20px rgba(14, 14, 14, .5);
    }

/* ⚠ NO backdrop-filter ON THE STUCK HEADER, deliberately, and this was measured
   rather than assumed. A full-width blurred strip has to re-sample everything
   scrolling beneath it on every frame; on a long page it is the single most
   expensive thing on screen and it is what made scrolling stutter. The site is
   white behind the header anyway, so an opaque white bar and a .88 frosted one
   are all but indistinguishable at rest — the blur cost real smoothness to buy
   an effect nobody can see. The stuck state is carried by the border and the
   shadow above.

   (An earlier version wrapped a translucent background in @supports so that a
   browser without backdrop-filter would not show the page scrolling through the
   header. With no translucency left there is nothing to guard.) */
.rr-header.is-stuck {
    background: var(--rr-surface);
}

/* Nav underline. Section 6 draws the active state with border-bottom-color,
   which cannot animate from nothing - a border either is or is not there. A
   pseudo-element can scale, so the indicator moves to ::after and the border
   goes transparent in both states. Identical at rest; it now grows. */
.rr-nav .nav-link {
    position: relative;
    transition: color var(--rr-t) var(--rr-ease);
}

    .rr-nav .nav-link::after {
        content: "";
        position: absolute;
        left: 0;
        right: 0;
        bottom: -2px;
        height: 2px;
        background: var(--rr-gold);
        transform: scaleX(0);
        transform-origin: left center;
        transition: transform var(--rr-t-slow) var(--rr-ease);
    }

    .rr-nav .nav-link.active {
        border-bottom-color: transparent;
    }

    .rr-nav .nav-link.active::after,
    .rr-nav .nav-link:hover::after,
    .rr-nav .nav-link:focus-visible::after {
        transform: scaleX(1);
    }

/* The same treatment for the pinned "All" control, which is a <button> and so
   was never covered by the .nav-link rules. */
.rr-nav-rail__all {
    position: relative;
    transition: color var(--rr-t) var(--rr-ease);
}

    .rr-nav-rail__all::after {
        content: "";
        position: absolute;
        left: 0;
        right: 0;
        bottom: -2px;
        height: 2px;
        background: var(--rr-gold);
        transform: scaleX(0);
        transform-origin: left center;
        transition: transform var(--rr-t-slow) var(--rr-ease);
    }

    .rr-nav-rail__all:hover::after,
    .rr-nav-rail__all[aria-expanded="true"]::after,
    .rr-nav-rail__all:focus-visible::after {
        transform: scaleX(1);
    }

.rr-wordmark {
    transition: transform var(--rr-t) var(--rr-ease-spring);
}

body:not(.rr-admin) .rr-icon-btn .material-symbols-outlined {
    transition: transform var(--rr-t) var(--rr-ease-spring);
}

body:not(.rr-admin) .rr-icon-btn:active {
    transform: scale(.94);
}

@media (hover: hover) {
    .rr-wordmark:hover {
        transform: scale(1.025);
    }

    body:not(.rr-admin) .rr-icon-btn:hover .material-symbols-outlined {
        transform: scale(1.12);
    }
}

/* --------------------------------------------------------------------------
   35.5 Announcement bar
   -------------------------------------------------------------------------- */
.rr-announcement {
    /* A slow gold sweep across the near-black band. Animating background-position
       is cheap here because the element is 32px tall and carries no children that
       need repainting with it. */
    background-image: linear-gradient(100deg, transparent 35%, rgba(201, 162, 39, .16) 50%, transparent 65%);
    background-size: 220% 100%;
    animation: rr-shimmer 9s linear infinite;
}

/* --------------------------------------------------------------------------
   35.6 Hero
   --------------------------------------------------------------------------
   The entrance is applied to .rr-hero__inner's children generically rather than
   to .rr-hero__badge / __title / __body by name, so a slide with no eyebrow and
   no second CTA still staggers correctly - _HeroSlide omits those elements
   entirely rather than rendering them empty, and :nth-child counts what is
   actually in the DOM. */
.rr-hero__inner > * {
    animation: rr-hero-in .85s var(--rr-ease) backwards;
}

.rr-hero__inner > :nth-child(1) {
    animation-delay: .08s;
}

.rr-hero__inner > :nth-child(2) {
    animation-delay: .2s;
}

.rr-hero__inner > :nth-child(3) {
    animation-delay: .32s;
}

.rr-hero__inner > :nth-child(4) {
    animation-delay: .44s;
}

/* Carousel slides. Every slide is in the DOM from the start, so the load-time
   animation above would burn on the hidden ones and every slide after the first
   would arrive already finished. Re-scoping to .active makes the browser
   re-match - and therefore restart - the animation each time Bootstrap moves
   the class, so slide four animates in the way slide one did. */
.rr-hero-carousel .carousel-item .rr-hero__inner > * {
    animation: none;
}

.rr-hero-carousel .carousel-item.active .rr-hero__inner > * {
    animation: rr-hero-in .85s var(--rr-ease) backwards;
}

/* The photograph drifts. Only on the active slide, for the same reason. */
.rr-hero__media {
    animation: rr-kenburns 20s var(--rr-ease-inout) both;
    transform-origin: center;
}

.rr-hero-carousel .carousel-item .rr-hero__media {
    animation: none;
}

.rr-hero-carousel .carousel-item.active .rr-hero__media {
    animation: rr-kenburns 20s var(--rr-ease-inout) both;
}

/* Two rules lived here and both described the OLD dark hero: a black double-wash on the scrim,
   and a highlight-plus-glow on the eyebrow while it was still a gold chip. Section 7 now paints
   a light band with a plain tracked-out caption, and this block sits LATER in the file - so
   leaving either rule in place would have quietly won the cascade and put the black wash back.
   Deliberately empty rather than deleted, so the next person reading 35.6 top to bottom can see
   that the omission is a decision. */

/* The hide-until-hover treatment existed because the controls were translucent chevrons lying
   across the artwork. Section 7 has since moved them into the bottom-right corner as two solid
   bordered squares clear of the photograph, so there is nothing left to hide them FROM - and a
   control that only appears on hover is one a touch visitor never finds. They are simply present
   now, and section 7 supplies their hover and focus states.

   The transform declarations had to go with the opacity ones: the slide-in offsets would have
   pushed both squares off the corner they are now positioned into. */

.rr-hero-carousel__dots [data-bs-slide-to] {
    transition: background-color var(--rr-t) var(--rr-ease), width var(--rr-t-slow) var(--rr-ease);
}

    /* The active dash is longer as well as gold - a second, non-colour signal
       for which slide is showing (WCAG 1.4.1). */
    .rr-hero-carousel__dots [data-bs-slide-to].active {
        width: 44px;
    }

/* --------------------------------------------------------------------------
   35.7 Category tiles
   --------------------------------------------------------------------------
   The tile is a bordered card since the Claude Design handoff - no scrim, no gold rule, and no
   media zoom (a scaled background inside a card that clips would have run under the name strip).
   What is left here is the lift and the gold edge, which is the whole of the handoff's hover
   state. Gated on a real pointer, as everything in section 35 is. */
.rr-cat-tile {
    transition: transform var(--rr-t-slow) var(--rr-ease), box-shadow var(--rr-t-slow) var(--rr-ease), border-color var(--rr-t-slow) var(--rr-ease);
}

/* The gold edge is a pseudo-element rather than a real border: appearing costs
   no layout, where a border would resize the tile by 2px on hover. */
.rr-cat-tile::after {
    content: "";
    position: absolute;
    inset: 0;
    border: 2px solid transparent;
    border-radius: inherit;
    pointer-events: none;
    transition: border-color var(--rr-t-slow) var(--rr-ease);
}

@media (hover: hover) {
    .rr-cat-tile:hover {
        transform: translateY(-6px);
        box-shadow: var(--rr-lift-lg);
    }

        .rr-cat-tile:hover::after {
            border-color: rgba(201, 162, 39, .75);
        }

        .rr-cat-tile:hover {
            border-color: var(--rr-gold);
        }
}

/* Keyboard parity - the tile is an <a>, so it takes focus, and a focused tile
   should read the same as a hovered one. */
.rr-cat-tile:focus-visible {
    transform: translateY(-6px);
    box-shadow: var(--rr-lift-lg);
}

    .rr-cat-tile:focus-visible::after {
        border-color: rgba(201, 162, 39, .75);
    }

.rr-cat-tile:focus-visible {
    border-color: var(--rr-gold);
}

/* The rail's scroll buttons, which JS adds only when the track overflows. */
.rr-rail__btn {
    transition: transform var(--rr-t) var(--rr-ease-spring), background-color var(--rr-t) var(--rr-ease), border-color var(--rr-t) var(--rr-ease), opacity var(--rr-t) var(--rr-ease);
}

    .rr-rail__btn:active {
        transform: scale(.92);
    }

@media (hover: hover) {
    .rr-rail__btn:hover {
        transform: scale(1.1);
        border-color: var(--rr-gold);
    }
}

/* --------------------------------------------------------------------------
   35.8 Product card
   --------------------------------------------------------------------------
   The most repeated component on the site - the shop, every category page, the
   home rail, search results and the wishlist are all grids of this - so it gets
   the most attention.

   The zoom in section 9 is re-gated here. `.rr-card:hover .rr-card__media img`
   was unconditional, so tapping a card on a phone latched it at scale(1.05)
   until the next tap landed somewhere else, and the same for the gold name. */
@media (hover: none) {
    .rr-card:hover .rr-card__media img {
        transform: none;
    }

    .rr-card:hover .rr-card__name {
        color: var(--rr-black);
    }
}

/* The media used to LIFT on hover. It cannot any more: since the handoff the card is a bordered
   box with overflow:hidden and the picture runs flush to its top edge, so a 4px translate tore a
   gap open under the frame and clipped the bottom of the photograph against the name strip. The
   lift's job - "this card is live" - is done by the frame going gold instead. What survives is
   the pool of light and the zoom, both of which happen INSIDE the picture. */
.rr-card__media {
    transition: box-shadow var(--rr-t-slow) var(--rr-ease);
}

    .rr-card__media img {
        transition: transform .6s var(--rr-ease);
    }

    /* The pool of light under the product. Behind the photograph, so it reads as
       the studio light the shot was taken in; at rest it is fully transparent and
       the 1:1 field is the plain white the design system asks for. */
    .rr-card__media::before {
        content: "";
        position: absolute;
        inset: 0;
        background: radial-gradient(58% 52% at 50% 62%, rgba(201, 162, 39, .2), transparent 70%);
        opacity: 0;
        transition: opacity var(--rr-t-slow) var(--rr-ease);
        pointer-events: none;
    }

.rr-card__name {
    transition: color var(--rr-t) var(--rr-ease);
}

.rr-card__actions .btn .material-symbols-outlined {
    transition: transform var(--rr-t) var(--rr-ease-spring);
}

@media (hover: hover) {
    .rr-card:hover .rr-card__media::before {
        opacity: 1;
    }

        .rr-card:hover .rr-card__media img {
            transform: scale(1.07);
        }

    /* The "Buy now" arrow leans toward its destination. */
    .rr-card:hover .rr-card__actions .btn-outline-dark .material-symbols-outlined {
        transform: translateX(3px);
    }
}

/* Focus parity again: the media is wrapped in an <a> and section 9 already scales its image on
   :focus-visible, so the pool lights for the keyboard too. No lift here either, for the reason
   given above - and .rr-card:focus-within already turns the frame gold. */
.rr-card__media:focus-visible::before {
    opacity: 1;
}

/* The wishlist heart. .is-active is set by the server on render AND by the
   optimistic toggle in initWishlist, so the beat fires on the save the customer
   just made and, unavoidably, once per already-saved card on load. Scoped to the
   card's own button so the header and drawer controls are unaffected. */
.rr-card__save .rr-icon-btn {
    transition: color var(--rr-t) var(--rr-ease), background-color var(--rr-t) var(--rr-ease), border-color var(--rr-t) var(--rr-ease), transform var(--rr-t) var(--rr-ease-spring);
}

    .rr-card__save .rr-icon-btn:active {
        transform: scale(.9);
    }

    .rr-card__save .rr-icon-btn.is-active .material-symbols-outlined {
        animation: rr-heartbeat .5s var(--rr-ease-spring);
    }

@media (hover: hover) {
    .rr-card__save .rr-icon-btn:hover {
        transform: scale(1.08);
    }
}

/* A save in flight. initWishlist marks the form data-rr-busy for the duration of
   the fetch and there was no visual acknowledgement of it at all, so on a slow
   connection the control looked dead. */
form[data-rr-busy] .rr-icon-btn {
    opacity: .55;
}

/* The discount badge is the one piece of gold fill on an otherwise white card,
   which makes it the piece worth drawing the eye to. */
.rr-discount {
    background-image: linear-gradient(180deg, rgba(255,255,255,.3), transparent 60%);
    transition: transform var(--rr-t) var(--rr-ease-spring), box-shadow var(--rr-t) var(--rr-ease);
}

@media (hover: hover) {
    .rr-card:hover .rr-discount {
        transform: translateY(-1px) scale(1.04);
        box-shadow: 0 6px 14px -8px rgba(201, 162, 39, .9);
    }
}

/* --------------------------------------------------------------------------
   35.9 Buttons and links
   --------------------------------------------------------------------------
   Scoped to the storefront body throughout: section 34 already has its own,
   tighter treatment of the same classes inside .rr-admin and the two must not
   fight.

   NOTE for anyone extending this - section 4 sets `box-shadow: none !important`
   on .btn. Elevation on a button therefore has to come from transform, not from
   a shadow, and every rule below respects that rather than stacking a second
   !important on top of the first. */
body:not(.rr-admin) .btn {
    position: relative;
    overflow: hidden;
    transition: transform var(--rr-t-fast) var(--rr-ease), background-color var(--rr-t) var(--rr-ease), color var(--rr-t) var(--rr-ease), border-color var(--rr-t) var(--rr-ease);
}

    body:not(.rr-admin) .btn:active {
        transform: translateY(0) scale(.985);
    }

    /* One pass of light across the gold, on hover only. The keyframe is section
       34's rr-sheen, reused rather than restated. */
    body:not(.rr-admin) .btn-gold::after {
        content: "";
        position: absolute;
        top: 0;
        bottom: 0;
        left: 0;
        width: 45%;
        background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .55), transparent);
        transform: translateX(-140%) skewX(-18deg);
        pointer-events: none;
    }

    body:not(.rr-admin) .btn .material-symbols-outlined,
    body:not(.rr-admin) .btn svg {
        transition: transform var(--rr-t) var(--rr-ease-spring);
    }

@media (hover: hover) {
    body:not(.rr-admin) .btn:hover {
        transform: translateY(-2px);
    }

    body:not(.rr-admin) .btn-gold:hover::after {
        animation: rr-sheen .75s var(--rr-ease);
    }

    body:not(.rr-admin) .btn-lg:hover .material-symbols-outlined,
    body:not(.rr-admin) .btn-lg:hover svg {
        transform: scale(1.1);
    }
}

/* The gold text links get the drawn underline the nav uses, replacing
   text-decoration:underline - which cannot animate and lands at a different
   offset at every font size. */
.rr-link-gold {
    position: relative;
}

    .rr-link-gold:hover,
    .rr-link-gold:focus {
        text-decoration: none;
    }

    .rr-link-gold::after {
        content: "";
        position: absolute;
        left: 0;
        right: 0;
        bottom: -3px;
        height: 2px;
        background: currentColor;
        transform: scaleX(0);
        transform-origin: left center;
        transition: transform var(--rr-t-slow) var(--rr-ease);
    }

    .rr-link-gold:hover::after,
    .rr-link-gold:focus-visible::after {
        transform: scaleX(1);
    }

/* --------------------------------------------------------------------------
   35.10 Product detail
   -------------------------------------------------------------------------- */
.rr-gallery__slide img {
    transition: transform .8s var(--rr-ease);
}

@media (hover: hover) {
    .rr-gallery__slide:hover img {
        transform: scale(1.06);
    }
}

.rr-thumb {
    transition: border-color var(--rr-t) var(--rr-ease), transform var(--rr-t) var(--rr-ease), box-shadow var(--rr-t) var(--rr-ease);
}

    .rr-thumb.is-active {
        /* The selection used to be marked with border-width: 2px, which changes the
           box - so the whole thumbnail strip shifted by a pixel every time the
           selection moved. An inset ring is the same signal with no layout. Section
           11 now sets the border to --rr-black on selection; this thickens it. */
        box-shadow: inset 0 0 0 1px var(--rr-black);
    }

@media (hover: hover) {
    .rr-thumb:hover {
        transform: translateY(-3px);
    }
}

/* Size tiles. The fill grows from the centre instead of switching, which makes
   "this size, therefore this price" legible as a cause and an effect. The fill
   is near-black now rather than gold — section 11 inverts the chosen tile — and
   the overshoot easing is gone with it: the brand motion spec forbids bounce,
   and a 74px tile popping past its own edge was the most visible place it
   was still happening. */
.rr-size {
    position: relative;
    z-index: 0;
    overflow: hidden;
    transition: border-color var(--rr-t) var(--rr-ease), color var(--rr-t) var(--rr-ease), transform var(--rr-t) var(--rr-ease);
}

    .rr-size::before {
        content: "";
        position: absolute;
        inset: 0;
        z-index: -1;
        background: var(--rr-black);
        border-radius: inherit;
        transform: scale(0);
        transition: transform var(--rr-t-slow) var(--rr-ease);
    }

    /* Section 11 fills .is-active with a flat near-black background. Left in place
       it paints under the ::before at full opacity from the first frame, so the
       growing fill would have nothing to grow over. The surface colour goes back
       to white and the ::before becomes the only source of the fill — the same
       end state, reached visibly. Under reduced motion the transition collapses
       to .01ms and the fill simply appears, so nothing depends on the animation
       actually playing. */
    .rr-size.is-active {
        background: var(--rr-surface);
    }

    .rr-size.is-active::before {
        transform: scale(1);
    }

    .rr-size:active {
        transform: translateY(0) scale(.97);
    }

@media (hover: hover) {
    .rr-size:hover {
        transform: translateY(-2px);
    }
}

/* The price panel acknowledges a size change. initSizeSelector rewrites the
   figure in place, so without this the largest number on the page changes
   silently and a customer can miss that their choice moved the price. Driven by
   .is-updated, which the script adds and then removes. */
.rr-price-block {
    border-radius: var(--rr-radius);
    transition: box-shadow var(--rr-t-slow) var(--rr-ease);
}

    .rr-price-block .rr-price--hero {
        display: inline-block;
        transform-origin: left center;
        transition: transform var(--rr-t-slow) var(--rr-ease);
    }

    /* The panel behind this is gone (section 10), so the glow now reads as a ring
       drawn round the figures for a moment rather than as a box lighting up. */
    .rr-price-block.is-updated {
        box-shadow: var(--rr-glow-gold);
    }

        .rr-price-block.is-updated .rr-price--hero {
            transform: scale(1.06);
        }

.rr-qty button {
    transition: background-color var(--rr-t) var(--rr-ease), transform var(--rr-t) var(--rr-ease);
}

    .rr-qty button:active {
        transform: scale(.88);
    }

/* --------------------------------------------------------------------------
   35.11 Editorial blocks
   -------------------------------------------------------------------------- */
.rr-value__icon,
.rr-value-card__icon {
    transition: transform var(--rr-t-slow) var(--rr-ease-spring), background-color var(--rr-t-slow) var(--rr-ease), color var(--rr-t-slow) var(--rr-ease);
}

.rr-value-card {
    transition: transform var(--rr-t-slow) var(--rr-ease), box-shadow var(--rr-t-slow) var(--rr-ease), border-color var(--rr-t-slow) var(--rr-ease);
}

.rr-framed {
    overflow: hidden;
}

    .rr-framed img {
        transition: transform .9s var(--rr-ease);
    }

@media (hover: hover) {
    .rr-value:hover .rr-value__icon,
    .rr-value-card:hover .rr-value-card__icon {
        transform: translateY(-3px) rotate(-6deg) scale(1.08);
    }

    .rr-value-card:hover {
        transform: translateY(-4px);
        box-shadow: var(--rr-lift);
        border-color: var(--rr-gold);
    }

    .rr-framed:hover img {
        transform: scale(1.045);
    }
}

/* --------------------------------------------------------------------------
   35.12 Shipping banner - the second near-black band
   -------------------------------------------------------------------------- */
.rr-shipping {
    position: relative;
    overflow: hidden;
}

    /* A very slow gold aurora behind the copy, so the band is not a flat black
       rectangle with white text on it. rr-drift is section 34's keyframe. */
    .rr-shipping::before {
        content: "";
        position: absolute;
        inset: -30%;
        background: radial-gradient(38% 60% at 15% 40%, rgba(201, 162, 39, .18), transparent 70%), radial-gradient(30% 50% at 85% 65%, rgba(201, 162, 39, .12), transparent 70%);
        animation: rr-drift 26s ease-in-out infinite alternate;
        pointer-events: none;
    }

    /* The gradient above is positioned, so the content needs a stacking context
       of its own or it paints underneath it. */
    .rr-shipping > * {
        position: relative;
        z-index: 1;
    }

.rr-shipping__item {
    transition: transform var(--rr-t) var(--rr-ease), color var(--rr-t) var(--rr-ease);
}

@media (hover: hover) {
    .rr-shipping__item:hover {
        transform: translateY(-2px);
        color: var(--rr-gold);
    }
}

/* --------------------------------------------------------------------------
   35.13 Footer
   -------------------------------------------------------------------------- */
/* The social boxes are excluded from `display: inline-block`. This rule outweighs
   .rr-footer__social on specificity, so it was quietly turning the 40x40 boxes back into
   inline-block and cancelling the flex centring that puts their icons on one baseline. They
   keep the transition; only the display and the sideways hover are the column links' own. */
.rr-footer a:not(.btn):not(.rr-footer__social) {
    display: inline-block;
}

.rr-footer a:not(.btn) {
    transition: color var(--rr-t) var(--rr-ease), transform var(--rr-t) var(--rr-ease), border-color var(--rr-t) var(--rr-ease);
}

/* C4 moved this from .rr-footer__logo to the wrapper. Scaling the image alone left the tagline
   sitting still underneath a growing wordmark, which is exactly the "two stacked elements"
   reading the lockup exists to avoid. */
.rr-footer__lockup {
    transition: transform var(--rr-t-slow) var(--rr-ease-spring);
}

@media (hover: hover) {
    /* The credit link is excluded as well as the social boxes: it sits mid-sentence, and a
       3px sideways slide on one word inside a line of text detaches it from the words either
       side. Its own hover (colour + a thicker underline, section 13) does the work instead. */
    .rr-footer a:not(.btn):not(.rr-footer__social):not(.rr-footer__credit-link):hover {
        color: var(--rr-gold);
        transform: translateX(3px);
    }

    /* The social row is a row of icons, so sliding them sideways would break its
       rhythm - they lift instead.

       This read `.rr-footer__social a:hover` until B-UX4, which matched nothing:
       .rr-footer__social IS the <a>, so the descendant combinator was looking for a
       link inside a link. The lift never fired, and the sideways slide above applied
       to the icons instead. Both halves are corrected together.

       Reduced motion is handled globally (section 2 zeroes every transition), so
       there is no per-rule guard here. */
    .rr-footer__social:hover {
        transform: translateY(-3px);
        border-color: var(--rr-gold);
    }

    .rr-footer__lockup:hover {
        transform: scale(1.03);
    }
}

/* --------------------------------------------------------------------------
   35.14 Floating controls
   -------------------------------------------------------------------------- */
.rr-fab {
    transition: transform var(--rr-t) var(--rr-ease-spring), background-color var(--rr-t) var(--rr-ease), color var(--rr-t) var(--rr-ease), box-shadow var(--rr-t-slow) var(--rr-ease);
    box-shadow: 0 10px 24px -12px rgba(201, 162, 39, .9);
}

    .rr-fab:active {
        transform: scale(.94);
    }

    /* A slow gold halo. 4.5s and an 18px spread: present enough to find on a long
       page, quiet enough not to compete with a product photograph. This is the one
       looping animation on the storefront that is not behind a hover, which is
       exactly why it is this restrained. */
    .rr-fab::after {
        content: "";
        position: absolute;
        inset: 0;
        border-radius: inherit;
        box-shadow: 0 0 0 0 rgba(201, 162, 39, .5);
        animation: rr-fab-halo 4.5s var(--rr-ease) infinite;
        pointer-events: none;
    }

@keyframes rr-fab-halo {
    0% {
        box-shadow: 0 0 0 0 rgba(201, 162, 39, .5);
    }

    55%, 100% {
        box-shadow: 0 0 0 18px rgba(201, 162, 39, 0);
    }
}

@media (hover: hover) {
    .rr-fab:hover {
        transform: scale(1.08) rotate(-4deg);
        box-shadow: 0 16px 32px -12px rgba(166, 124, 0, .95);
    }
}

/* The mobile bottom nav. */
.rr-bottom-nav a {
    position: relative;
    transition: color var(--rr-t) var(--rr-ease), transform var(--rr-t-fast) var(--rr-ease);
}

    .rr-bottom-nav a .material-symbols-outlined {
        transition: transform var(--rr-t) var(--rr-ease-spring), font-variation-settings var(--rr-t) var(--rr-ease);
    }

    .rr-bottom-nav a.active .material-symbols-outlined {
        transform: translateY(-2px);
        font-variation-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 24;
    }

    /* The active tab's marker sits on top of the nav's top border, so it replaces
       nothing and costs no layout. */
    .rr-bottom-nav a::before {
        content: "";
        position: absolute;
        top: -1px;
        left: 50%;
        width: 28px;
        height: 2px;
        background: var(--rr-gold);
        transform: translateX(-50%) scaleX(0);
        transition: transform var(--rr-t-slow) var(--rr-ease-spring);
    }

    .rr-bottom-nav a.active::before {
        transform: translateX(-50%) scaleX(1);
    }

    .rr-bottom-nav a:active {
        transform: scale(.96);
    }

/* --------------------------------------------------------------------------
   35.15 Overlays - drawer, search, flash, toast
   --------------------------------------------------------------------------
   Bootstrap animates the panels themselves; what it does not do is animate what
   is inside them, so a drawer full of cart lines arrived as one solid block. */
.offcanvas {
    transition-timing-function: var(--rr-ease-inout) !important;
}

.offcanvas.show .rr-drawer-line {
    animation: rr-rise .45s var(--rr-ease) backwards;
}

    .offcanvas.show .rr-drawer-line:nth-child(1) {
        animation-delay: .06s;
    }

    .offcanvas.show .rr-drawer-line:nth-child(2) {
        animation-delay: .12s;
    }

    .offcanvas.show .rr-drawer-line:nth-child(3) {
        animation-delay: .18s;
    }

    .offcanvas.show .rr-drawer-line:nth-child(4) {
        animation-delay: .24s;
    }

    /* Everything past the fourth line shares one delay. A per-item stagger on a
       twenty-line cart would still be arriving three seconds after the drawer
       opened, which stops being an entrance and becomes a wait. */
    .offcanvas.show .rr-drawer-line:nth-child(n+5) {
        animation-delay: .3s;
    }

.rr-drawer-line {
    transition: background-color var(--rr-t) var(--rr-ease);
}

.rr-drawer-line__media img {
    transition: transform var(--rr-t-slow) var(--rr-ease);
}

.rr-drawer-line__remove {
    transition: color var(--rr-t) var(--rr-ease), transform var(--rr-t) var(--rr-ease-spring);
}

@media (hover: hover) {
    .rr-drawer-line:hover .rr-drawer-line__media img {
        transform: scale(1.06);
    }

    .rr-drawer-line__remove:hover {
        transform: scale(1.15) rotate(90deg);
    }
}

/* Flash notices and the wishlist toast drop in rather than simply appearing. */
.rr-flash {
    animation: rr-drop .45s var(--rr-ease-spring) backwards;
}

.rr-wishlist-toast:not([hidden]) {
    animation: rr-drop .4s var(--rr-ease-spring) backwards;
}

/* --------------------------------------------------------------------------
   35.16 Forms, pagination, empty states
   -------------------------------------------------------------------------- */
body:not(.rr-admin) .form-control,
body:not(.rr-admin) .form-select {
    transition: border-color var(--rr-t) var(--rr-ease), box-shadow var(--rr-t) var(--rr-ease), background-color var(--rr-t) var(--rr-ease);
}

body:not(.rr-admin) .pagination .page-link {
    transition: background-color var(--rr-t) var(--rr-ease), color var(--rr-t) var(--rr-ease), transform var(--rr-t) var(--rr-ease-spring);
}

@media (hover: hover) {
    body:not(.rr-admin) .pagination .page-link:hover {
        transform: translateY(-2px);
    }
}

/* The empty states get the breathing icon the admin ones have. An empty cart or
   an empty wishlist is the screen most in need of feeling alive. */
body:not(.rr-admin) .rr-empty > .material-symbols-outlined {
    animation: rr-float 4s ease-in-out infinite;
}

/* --------------------------------------------------------------------------
   35.17 Print
   --------------------------------------------------------------------------
   All of the above is decoration, and decoration caught mid-transition prints as
   whatever the element happened to look like at that instant. A half-revealed
   order confirmation is a genuinely broken printout, not merely an ugly one, and
   the confirmation page is the one page on this site people do print. */
@media print {
    .rr-js .rr-reveal {
        opacity: 1 !important;
        transform: none !important;
        transition: none !important;
    }

    .rr-fab::after,
    .rr-shipping::before,
    .rr-announcement,
    .rr-hero__media,
    .rr-hero__inner > * {
        animation: none !important;
    }
}

/* --------------------------------------------------------------------------
   36. Custom Bat configurator (C1)
   --------------------------------------------------------------------------
   The single-page builder at /custom-bat and the read-only specification
   blocks that carry a configured bat through the cart, the public order summary
   and the admin.

   THE PAGE IS A PRODUCT DETAIL PAGE. A sticky stage on the left showing what is
   being made, a rail of every attribute scrolling on the right, and the price
   docked to the bottom of that rail. It replaced a four-step wizard, and the
   reason was the money: a customer weighing a ₹1,250 knocking-in against a ₹900
   cover could see neither against the running total when each decision lived on
   its own request.

   Four things shape this layer and are worth stating before the rules:

   1. OPTION CARDS MUST READ AS FINISHED WITH NO IMAGE. Royal Rider has no
      product photography for the catalogue at large (the standing launch
      blocker), and most seeded options still have a null ImagePath. The nine
      hand-placed files in wwwroot/img/custom-bat are the exception, not a
      change of plan: handle construction, sweet spot and toe shape have
      pictures; every other group on this page is still text. The card is
      therefore laid out around TEXT: label, help, lead-time note, price.
      .rr-cfg-option__media is an optional block that sits above them when a
      picture exists, and nothing below it moves or resizes when it does. There
      is no fixed image box reserving empty space, because an empty box is what
      makes a text-only card look broken rather than deliberate.

   2. THE NATIVE INPUT IS THE CONTROL. The radio and the checkbox are visually
      hidden but never display:none and never removed from the tab order: the
      label IS the card, :checked drives the selected state, and
      :focus-visible on the input draws the ring on the card. That keeps the
      keyboard behaviour, the radio-group semantics and the required-field
      validation the browser already gives us, none of which is reimplemented.

   3. ONE COLUMN AT 360px. The option grid is auto-fill from 11rem, so a 360px
      phone lands on a single column with full-width cards rather than two
      cramped ones, and nothing on the page scrolls sideways.

   4. THE DOCK IS position:sticky, NOT position:fixed, and it is the last child
      of the rail. Sticky keeps it inside the form (every button in it submits
      the whole rail), keeps it inside the page's own scroll container, and lets
      it settle under the final section at the end of the scroll instead of
      hovering over the footer forever. Fixed would need a body padding hack to
      stop it covering content, and would still cover it on a short viewport.

   Everything reuses the existing tokens; no new hex literal appears below. */

/* --- Page shell ----------------------------------------------------------- */
.rr-cfg__head {
    max-width: 44rem;
    margin-bottom: 2rem;
}

.rr-cfg__title {
    font-size: var(--rr-fs-7xl);
    margin: .25rem 0 .75rem;
}

.rr-cfg__lede {
    font-size: var(--rr-fs-xl);
    line-height: var(--rr-lh-body);
    color: var(--rr-muted);
    margin: 0;
}

.rr-cfg__form {
    margin-top: 2rem;
}

.rr-cfg__reset {
    margin-top: 1.5rem;
}

/* "Start again". It used to wear .rr-page-link — the PAGINATION chip, a fixed
   44×44 square sized to hold "7" — so the label wrapped to two cramped lines
   inside a box the width of a single digit. Its own class, deliberately quiet:
   throwing a build away is not the action this page is encouraging, so it reads
   as a link rather than as a second button competing with Add to cart. */
.rr-cfg__reset-btn {
    display: inline-flex;
    align-items: center;
    gap: .375rem;
    padding: .375rem 0;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-sm);
    color: var(--rr-muted);
    background: none;
    border: 0;
    border-bottom: 1px solid var(--rr-outline);
    border-radius: 0;
    cursor: pointer;
    transition: color .2s ease, border-color .2s ease;
}

    .rr-cfg__reset-btn .material-symbols-outlined {
        font-size: 1.125rem;
    }

    .rr-cfg__reset-btn:hover {
        color: var(--rr-black);
        border-bottom-color: var(--rr-black);
    }

    .rr-cfg__reset-btn:focus-visible {
        outline: 2px solid var(--rr-gold-deep);
        outline-offset: 3px;
    }

/* --- One decision cluster ------------------------------------------------- */
.rr-cfg-group {
    border: 0;

    /* Tightened from 1.25rem/2rem, and again from .875rem/1.25rem. Sixteen
       groups each paying 34px of rule and air was still ~540px of the rail
       spent on separators — more than the three photo groups put together. The
       divider does the separating; it does not need that much room around it. */
    padding: 0 0 .625rem;
    margin: 0 0 .875rem;
    border-bottom: 1px solid var(--rr-outline);

    /* ⚠ THE ANCHOR OFFSET BELONGS HERE, NOT ONLY ON THE SECTION. Every line in
       the dock's breakdown links to a GROUP id (#rr-group-anti-scuff), never to
       a section id — so the section's scroll-margin-top never applied to any of
       them and each jump landed the fieldset's legend behind the sticky header.
       Same token as .rr-cfg-section so the two landings agree. */
    scroll-margin-top: calc(var(--rr-header-h) + 1.5rem);
}

    .rr-cfg-group:last-of-type {
        border-bottom: 0;
        margin-bottom: 1rem;
    }

    /* A group behind a toggle. Hidden ONLY under .rr-js, which _Layout sets in an inline script
       above the stylesheet — so with JavaScript the grips questions appear the instant the toggle
       is ticked, and WITHOUT it they are always visible rather than unreachable. Every one of them
       renders with a default already chosen, so the form submits either way, and the server drops
       the answers when the toggle ends up off. Same progressive-enhancement contract as the
       scroll-reveal layer in section 35.

       ⚠ :not([data-rr-depends-on=""]) is load-bearing. Razor writes the attribute with an EMPTY
       value on a group that has no dependency, and without this half of the selector such a group
       matches, never gains .is-shown, and vanishes — which took the "extra grips" toggle itself
       off the page the first time this was written. */
    .rr-js .rr-cfg-group[data-rr-depends-on]:not([data-rr-depends-on=""]):not(.is-shown) {
        display: none;
    }

.rr-cfg-group__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: .75rem;

    /* A <legend> is inline-ish by default and will not fill the fieldset, which
       would pull the size-guide button in against the heading instead of to the
       far edge. float:none + width:100% is the reliable way to make a legend
       behave as a block in every engine. */
    float: none;
    width: 100%;
    padding: 0;
    margin-bottom: .5rem;
}

/* ⚠ ONE STEP BELOW .rr-cfg-section__title, WHICH IT WAS NOT. Both sat at
   --rr-fs-4xl in the same uppercase condensed face, so "TOE SHAPE" (a question)
   and "FINISH & PROTECTION" (the part containing six of them) were typograph-
   ically the same thing, and the rail read as sixteen flat headings rather than
   as four parts. The eyebrow above the part carries the rest of the distinction;
   this carries the size difference. */
.rr-cfg-group__name {
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: var(--rr-fs-3xl);
    letter-spacing: var(--rr-ls-ui);
    text-transform: uppercase;
    color: var(--rr-black);
}

.rr-cfg-group__guide {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    padding: 0;
    background: none;
    border: 0;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-sm);

    /* --rr-gold-text, not --rr-gold: this is normal-size text on white and the
       lighter golds do not reach AA there. */
    color: var(--rr-gold-text);
    text-decoration: underline;
    text-underline-offset: .2em;
    cursor: pointer;
}

    .rr-cfg-group__guide .material-symbols-outlined {
        font-size: 18px;
    }

    .rr-cfg-group__guide:hover {
        color: var(--rr-black);
    }

.rr-cfg-group__help {
    font-size: var(--rr-fs-sm);
    line-height: var(--rr-lh-body);
    color: var(--rr-muted);
    margin: 0 0 .625rem;
    max-width: 42rem;
}

.rr-cfg-group__none {
    font-size: var(--rr-fs-md);
    color: var(--rr-muted);
    margin: 0;
}

/* --- Option cards --------------------------------------------------------- */
.rr-cfg-options {
    display: grid;

    /* auto-fill from 11rem: one column on a 360px phone, two on a large phone,
       three or four in the wizard's 8-column desktop track. */
    grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
    gap: .5rem;
}

.rr-cfg-option {
    position: relative;
}

    .rr-cfg-option[hidden] {
        display: none;
    }

.rr-cfg-option__input {
    /* Hidden, NOT removed. It keeps its place in the tab order and its radio
       semantics; the card below is only its label. */
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    margin: 0;
    cursor: pointer;
    z-index: 1;
}

.rr-cfg-option__body {
    display: flex;
    flex-direction: column;
    gap: .375rem;
    height: 100%;
    padding: .625rem .75rem;
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    cursor: pointer;
    transition: border-color .2s ease, background-color .2s ease;
}

@media (hover: hover) {
    .rr-cfg-option__input:hover + .rr-cfg-option__body {
        border-color: var(--rr-gold-deep);
    }
}

.rr-cfg-option__input:checked + .rr-cfg-option__body {
    border-color: var(--rr-gold);

    /* A tint rather than a gold fill: these cards sit twelve to a step and a
       solid gold selection on each would swamp the page. Gold stays an accent. */
    background: var(--rr-gold-soft);
}

.rr-cfg-option__input:focus-visible + .rr-cfg-option__body {
    outline: 2px solid var(--rr-gold-deep);
    outline-offset: 2px;
}

/* Only rendered when an option actually has a picture. No reserved empty box:
   see note 1 at the top of this section. */
.rr-cfg-option__media {
    display: block;

    /* ⚠ CAPPED, and the cap is the point. Left to fill the card the box was a
       173px square, which made a photo card 287px tall and put barely one of
       the three side-by-side options on screen at a time — the comparison these
       pictures exist for was the thing the pictures were breaking. At 6rem all
       three fit in a glance and the card comes in under 170px.

       The cap sits on the shared box rather than on the image or the SVG, so
       the photo path and the diagram path cannot disagree about it: whichever
       one a card falls back to, it is this square. */
    width: 100%;
    max-width: 6rem;
    aspect-ratio: 1 / 1;
    overflow: hidden;
    border-radius: var(--rr-radius);

    /* ⚠ NO PLATE, and this replaces the --rr-surface-2 one that used to be here
       plus the --diagram modifier that used to turn it off again. The plate was
       meant to back an image that did not fill its box, but every file in
       img/custom-bat is square on a near-white ground and object-fit: contain
       covers the box exactly — so on a white card the plate was invisible, and
       the only place it ever rendered was on a SELECTED card, where it drew a
       white square onto the gold tint. It was decoration that appeared only
       when it was wrong. Photo and drawing now share one transparent box. */
    background: transparent;
}

    .rr-cfg-option__media img {
        width: 100%;
        height: 100%;
        object-fit: contain;

        /* The white the photograph was SHOT on, dissolved into the card.
           Removing the CSS plate did not remove the white square on a selected
           card, because that square is baked into the file — every image in
           img/custom-bat is a subject on a near-white ground, which is exactly
           what the README asks for and what makes them consistent. multiply
           leaves white alone wherever the card behind it is white (an unselected
           card is untouched, mathematically) and lets the gold tint through
           wherever it is not, so the selected card reads as one tinted surface
           instead of a photo pasted onto it.

           ⚠ This is safe only while the backgrounds stay near-white, which the
           README specifies. A photograph on a dark ground would come out
           muddy — recrop it rather than special-casing it here. */
        mix-blend-mode: multiply;
    }

    /* An UPLOADED option photo (C6) comes through _ResponsiveImage, which wraps
       the <img> in a <picture> so the WebP source can outrank the JPEG. A
       <picture> is inline by default, so the img's height: 100% above would have
       no resolved parent height to work from and the picture would collapse to
       its intrinsic size inside the 6rem square. Same two lines
       .rr-content-slot__preview already carries, and for the same reason. The
       hand-placed seeded files emit a bare <img> and never match this. */
    .rr-cfg-option__media picture {
        display: block;
        width: 100%;
        height: 100%;
    }

.rr-cfg-option__text {
    display: flex;
    flex-direction: column;
    gap: .25rem;
    flex: 1 1 auto;
}

.rr-cfg-option__label {
    font-family: var(--rr-font-body);
    font-weight: 600;

    /* Down from --rr-fs-lg. With the subline gone the label is the only text on
       most cards, and at the old size a two-word choice was set larger than the
       group heading it sits under — which inverted the hierarchy of the page. */
    font-size: var(--rr-fs-base);
    line-height: 1.35;
    color: var(--rr-black);
}

/* .rr-cfg-option__help was here. The per-option subline was removed from every
   card, so the rule went with it rather than sitting unreferenced in a 14k-line
   stylesheet. BatOptionValue.HelpText still holds the copy — see the note in
   _CustomBatOptionBody.cshtml — so restoring it is one line of Razor and a
   handful of declarations, not a data recovery job. */

.rr-cfg-option__note {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    font-weight: 600;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-ui);
    color: var(--rr-gold-text);
}

    .rr-cfg-option__note .material-symbols-outlined {
        font-size: 14px;
        width: 14px;
        height: 14px;
    }

.rr-cfg-option__price {
    font-family: var(--rr-font-head);
    font-weight: 700;

    /* Down from --rr-fs-2xl. Now that only the options which actually cost
       something print a figure, the price no longer needs size to separate
       itself from a column of INCLUDED — being the only number on the card
       does that. */
    font-size: var(--rr-fs-lg);
    color: var(--rr-black);
}

    /* ⚠ AN OPTION THAT COSTS NOTHING SHOWS NOTHING. Twenty-four grey INCLUDED
       tags were the single most repeated thing on the page — three per group,
       on most groups, saying the same word about a choice that has no price
       attached. Silence carries it better: a "+ ₹ 450" beside its neighbours
       reads as the one that costs, which is the only comparison a customer is
       making here. The dock's breakdown still lists every line, priced or not,
       so nothing is hidden from the total — only from the card.

       display:none takes it out of the accessibility tree as well as off the
       screen, which is deliberate: a screen reader then announces the option
       exactly as a sighted customer reads it, rather than the two getting
       different information about the same card. The markup is unchanged, so
       reverting this is deleting one declaration. */
    .rr-cfg-option__price--free {
        display: none;
    }

/* --- Compact groups: chips rather than cards ------------------------------
   Bat size, weight, spare-grip count and grip colour. See
   CustomBatGroupViewModel.IsCompact for why these four and not a rule.

   The label on one of these is a token — "Size 4", "1240–1270gm", "Maroon" —
   with no picture, no lead-time note and nothing to read. A full card gives two
   words a 173px media-less box and three lines of leading, and eleven bat sizes
   then fill the viewport before the customer reaches the second question. The
   chip puts the label and the price on one line and lets the row wrap.

   ⚠ NOTHING HERE TOUCHES THE INPUT, the :checked rule or the :focus-visible
   ring. Those all hang off .rr-cfg-option__body, which a chip still is — so the
   keyboard behaviour, the radio-group semantics and the gold selected state are
   the same objects doing the same job at a different size, not a second
   implementation that can drift from the first. */
.rr-cfg-options--compact {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
}

    /* Width from the content, so "Size 1" and "LB LH — Long Blade, Long Handle"
       each take what they need instead of every chip stretching to the widest.
       max-width keeps the longest label from running off a 360px phone. */
    .rr-cfg-options--compact .rr-cfg-option {
        flex: 0 1 auto;
        max-width: 100%;
    }

    .rr-cfg-options--compact .rr-cfg-option__body {
        flex-direction: row;
        align-items: baseline;
        gap: .5rem;
        height: auto;
        padding: .5rem .75rem;
    }

    .rr-cfg-options--compact .rr-cfg-option__text {
        flex: 0 1 auto;
    }

    .rr-cfg-options--compact .rr-cfg-option__label {
        font-size: var(--rr-fs-sm);
        line-height: 1.3;
    }

    /* Barlow Condensed at body size rather than the card's 2xl: on a chip the
       price sits beside the label instead of anchoring the bottom of a card, so
       it reads as part of the same line and must not outweigh the choice. */
    .rr-cfg-options--compact .rr-cfg-option__price {
        font-size: var(--rr-fs-base);
        white-space: nowrap;
    }

/* A yes/no toggle is one full-width row, not a card in a grid. */
.rr-cfg-option--toggle .rr-cfg-option__body {
    flex-direction: row;
    align-items: center;
    gap: 1rem;
}

.rr-cfg-option--toggle .rr-cfg-option__text {
    order: 2;
}

.rr-cfg-option--toggle .rr-cfg-option__price {
    order: 3;
    margin-left: auto;
}

/* The tick box, drawn on the label rather than shown as a native checkbox so it
   matches the card treatment. ::before because the label already has children. */
.rr-cfg-option--toggle .rr-cfg-option__body::before {
    content: "";
    order: 1;
    flex: 0 0 auto;
    width: 1.25rem;
    height: 1.25rem;
    border: 1px solid var(--rr-outline);
    border-radius: .25rem;
    background: var(--rr-surface);
}

.rr-cfg-option--toggle .rr-cfg-option__input:checked + .rr-cfg-option__body::before {
    background: var(--rr-gold);
    border-color: var(--rr-gold);

    /* The tick, drawn rather than fetched: an inline SVG data URI needs no
       request and no icon font, and this is the only checkbox in the system. */
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%230E0E0E' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 8.5l3.5 3.5L13 5'/%3E%3C/svg%3E");
    background-size: 80%;
    background-position: center;
    background-repeat: no-repeat;
}

/* --- The two-column page --------------------------------------------------
   minmax(0,…) on BOTH tracks, not 42%/1fr: without the zero minimum a long
   unbroken option label sets the track's min-content width, the grid refuses to
   shrink below it and the whole page scrolls sideways on a phone. */
.rr-cfg-page__grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 2rem;
    align-items: start;
}

/* ⚠ 38%/2.5rem, NOT 42%/3.5rem. Six percent of the container and a rem of gutter
   sound like nothing and are worth ~110px of rail width, which is a whole extra
   option column: "Anti-scuff sheet" laid its four cards out 3 + 1, orphaning
   "Premium English" on a row of its own, and the eleven size chips took three
   rows. Both now fit a row shorter, and the stage is still a 1:1 frame big
   enough to read a bat in. Every pixel of height taken out of the rail is a
   pixel the customer does not scroll past. */
@media (min-width: 992px) {
    .rr-cfg-page__grid {
        grid-template-columns: minmax(0, 38%) minmax(0, 1fr);
        gap: 2.5rem;
    }
}

/* ⚠ THE STAGE STICKS ONLY WHERE IT FITS — hence the height query, which is not
   decoration. The stage is a 1:1 frame at 42% of the container plus a caption,
   a spec strip and a note: on a 1440px page it comes to ~760px. Stuck to a
   viewport shorter than that, its bottom half is permanently below the fold and
   the customer scrolls the rail while a column they can never finish reading
   sits frozen beside it — which is what "scrolling isn't smooth" describes. The
   860px floor is stage height + header + the 1.5rem offset, rounded up. Below
   it the stage simply scrolls away with the page, which is honest and costs the
   browser nothing.

   Height media queries do not react to the URL bar collapsing on mobile, but
   this is inside a min-width: 992px context where there is no URL bar to
   collapse. */
@media (min-width: 992px) and (min-height: 860px) {
    /* Offset below the sticky header by the same token the header itself is
       sized from, so the two cannot drift. */
    .rr-cfg-page__stage {
        position: sticky;
        top: calc(var(--rr-header-h) + 1.5rem);
    }
}

.rr-cfg-page__rail {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

/* --- The stage ------------------------------------------------------------ */
.rr-cfg-stage {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.rr-cfg-stage__frame {
    position: relative;
    aspect-ratio: 1 / 1;
    margin: 0;
    overflow: hidden;

    /* White, not the off-white band: the stage holds a technical drawing now,
       and ink line art wants paper under it rather than a tinted plate. */
    background: var(--rr-surface);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
}

    .rr-cfg-stage__frame img {
        width: 100%;
        height: 100%;
        object-fit: cover;
        display: block;
    }

/* The chosen size's photograph, where the owner has uploaded one — it takes the
   square from the drawing rather than sitting in the option card at 96px.

   <picture> is stretched to the frame itself: the rule above sizes the <img> at
   height:100%, and a percentage height resolved against an auto-height inline
   parent computes to auto, so without this the picture collapses to its natural
   height and the caption plate floats over whitespace.

   contain, not the cover above: a bat is a tall thin object in a square frame,
   and cover crops the toe — which on a SIZE picture is precisely the end being
   compared. The white ground behind it is the frame's own. */
.rr-cfg-stage__frame > picture {
    position: absolute;
    inset: 0;
    display: block;
}

/* Scoped to the frame ON PURPOSE, not left as a bare .rr-cfg-stage__photo: the
   cover above is (0,1,1) and would otherwise outrank a (0,1,0) class sitting
   later in the file, which is the silent kind of override that renders a
   cropped bat and passes review. */
.rr-cfg-stage__frame .rr-cfg-stage__photo {
    object-fit: contain;
}

/* --- The bat itself ------------------------------------------------------- */
/*
   THE SCHEMATIC (C5). A flat orthographic front elevation of the build, drawn
   as line art — ink outlines, flat fills, no gradient pretending to be light,
   no wood grain, no drop shadow simulating depth and no rotation.

   ⚠ THIS REPLACED A PSEUDO-3-D RENDER THE CLIENT REJECTED TWICE AS FAKE, and
   the cause was structural rather than a tuning problem: CSS and SVG cannot
   make wood grain, subsurface light and edge highlights convincing, and every
   extra gradient moved it further into the uncanny valley. On a ₹30,000
   handmade bat a drawing that is TRYING to be a photograph reads as a shop
   faking its own product photography. For a configurator a schematic is not a
   downgrade — a customer choosing between a square and a round toe is served
   better by a clear labelled diagram than by a picture of one bat.

   ⚠ GOLD MARKS THE PART BEING CONFIGURED AND NOTHING ELSE. The whole drawing is
   ink on white until a group scrolls into view, at which point that one piece
   of anatomy goes gold and names itself. Colour is never the only signal: the
   marked part also thickens its stroke and gains a label, and the choice itself
   is stated in words on its option card and in the spec strip.

   ⚠ THE FOCUS TOKEN LIVES ON THE LIVE-UPDATE HOST, NOT ON THE BAT — the same
   reason the old yaw did. The re-price replaces everything inside
   [data-rr-live-host="stage"] a quarter-second after any option is clicked; the
   host element itself survives. On the bat the mark would clear itself exactly
   when the customer had just chosen something.

   Geometry is Services/CustomBatArt's and comes off the seeded size guide, so
   an LB LH really is longer than a Size 3. The viewBox is square to match the
   frame, so nothing letterboxes and no unit needs a container query. */
/* Inset from the bottom by the height of the caption plate that sits over the
   frame. The drawing is 8:1 and fills its box top to bottom, so without this the
   toe is printed under "YOUR BUILD — SH, 1160–1190 g". The viewBox is square and
   fits by its smaller side, so the whole sheet simply scales down and centres. */
.rr-cfg-bat {
    position: absolute;
    inset: 0 0 3.25rem;
}

.rr-cfg-bat__art {
    display: block;
    width: 100%;
    height: 100%;
}

/* Every silhouette in the drawing — handle, splice, blade. One weight, one
   colour, no fill: this is ink on paper. */
.rr-cfg-bat__line {
    fill: none;
    stroke: var(--rr-ink);
    stroke-width: 2.4;
    stroke-linejoin: round;
    stroke-linecap: round;
}

/* The one place the customer's own colour appears, and the only fill in the
   drawing that is not a token. Flat and outlined, never shaded — a gradient
   here would be the render coming back. */
.rr-cfg-bat__grip {
    fill: var(--rr-bat-grip, var(--rr-ink));
    fill-opacity: .88;
    stroke: var(--rr-ink);
    stroke-width: 2.4;
    stroke-linejoin: round;
}

/* The cane laminations: three lines for a 4-piece handle, eleven for a
   12-piece. This is the thing a photograph of a finished bat cannot show at
   all, which is the whole argument for drawing it. */
.rr-cfg-bat__lam {
    fill: none;
    stroke: var(--rr-ink);
    stroke-width: 1.6;
    stroke-linecap: round;
}

/* The magnified handle section and its leader — a detail view, the way any
   drawing carries a small feature on a large part. */
.rr-cfg-bat__detail {
    fill: var(--rr-surface-2);
    stroke: none;
}

.rr-cfg-bat__rim {
    fill: none;

    /* Muted grey, not the outline token: this is a structural part of the
       drawing rather than a divider, and at 1.6 units the hairline token
       disappears on white. */
    stroke: var(--rr-muted);
    stroke-width: 1.6;
}

/* The sweet spot, stated as a band across the blade with its centre line
   marked. A band is how a spec sheet states a region; a glow is how a render
   fakes one. Quiet until the sweet-spot group is in view. */
.rr-cfg-bat__band {
    fill: var(--rr-surface-2);

    /* Legible in grey before it is ever gold. The band is one of the four
       things the drawing exists to state, so it may not depend on being the
       part in focus to be visible at all. */
    stroke: var(--rr-muted);
    stroke-width: 1.6;
}

.rr-cfg-bat__band-axis {
    fill: none;
    stroke: var(--rr-muted);
    stroke-width: 1.6;
    stroke-dasharray: 10 7;
}

/* The toe, traced over the blade outline so it can be marked without redrawing
   the silhouette underneath it. */
.rr-cfg-bat__toe {
    fill: none;
    stroke: var(--rr-ink);
    stroke-width: 2.4;
    stroke-linejoin: round;
    stroke-linecap: round;
}

/* --- Marking the part being configured ------------------------------------ */
/* One group at a time, driven by data-rr-bat-focus on the stage's live-update
   host. Everything else stays ink, so the drawing is a quiet elevation rather
   than a busy blueprint. */
[data-rr-bat-focus="grip"] .rr-cfg-bat__grip,
[data-rr-bat-focus="toe"] .rr-cfg-bat__toe,
[data-rr-bat-focus="splice"] .rr-cfg-bat__lam,
[data-rr-bat-focus="splice"] .rr-cfg-bat__rim {
    stroke: var(--rr-gold-deep);
    stroke-width: 3.4;
}

[data-rr-bat-focus="splice"] .rr-cfg-bat__detail {
    fill: var(--rr-gold-soft);
}

[data-rr-bat-focus="sweet"] .rr-cfg-bat__band {
    fill: var(--rr-gold-soft);
    stroke: var(--rr-gold-deep);
    stroke-width: 2.4;
}

[data-rr-bat-focus="sweet"] .rr-cfg-bat__band-axis {
    stroke: var(--rr-gold-deep);
}

/* The anatomy labels. Present in the markup for every part and shown only for
   the one in view — see the note in _CustomBatArt.cshtml. */
.rr-cfg-bat__cue {
    opacity: 0;
    transition: opacity .18s ease;
}

[data-rr-bat-focus="grip"] .rr-cfg-bat__cue--grip,
[data-rr-bat-focus="splice"] .rr-cfg-bat__cue--splice,
[data-rr-bat-focus="sweet"] .rr-cfg-bat__cue--sweet,
[data-rr-bat-focus="toe"] .rr-cfg-bat__cue--toe {
    opacity: 1;
}

.rr-cfg-bat__leader {
    fill: none;
    stroke: var(--rr-gold-deep);
    stroke-width: 1.6;
}

/* Brand type, at the size the drawing scales it to: the viewBox is 760 units
   tall and the frame is square, so 25 units lands between 10px on a 360px phone
   and 17px on a desktop stage. --rr-gold-text is the 5.17:1 token — the only
   gold that is legal for text at a normal size on white. */
.rr-cfg-bat__tag {
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: 25px;
    letter-spacing: 2px;
    text-transform: uppercase;
    fill: var(--rr-gold-text);
}

/* A label that fades in is motion nobody asked for. It still arrives — it
   simply stops travelling. */
@media (prefers-reduced-motion: reduce) {
    .rr-cfg-bat__cue {
        transition: none;
    }
}

/* --- The photo inset ------------------------------------------------------ */
/* A thumbnail opens its workshop photograph OVER the bat, with a way back. The
   server never renders this open, so any re-price returns to the drawing. */
.rr-cfg-stage__inset {
    position: absolute;
    inset: 0;
    background: var(--rr-surface);
}

    .rr-cfg-stage__inset[hidden] {
        display: none;
    }

    .rr-cfg-stage__inset img {
        width: 100%;
        height: 100%;

        /* contain, not cover: these are square detail crops on a near-white
           ground and cropping them cuts the very detail they exist to show. */
        object-fit: contain;
        display: block;
    }

.rr-cfg-stage__inset-back {
    position: absolute;
    top: .75rem;
    left: .75rem;
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    padding: .375rem .75rem;
    font-family: var(--rr-font-body);
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-black);
    background: rgba(255, 255, 255, .92);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    cursor: pointer;
}

    .rr-cfg-stage__inset-back:hover {
        border-color: var(--rr-gold);
    }

    .rr-cfg-stage__inset-back .material-symbols-outlined {
        font-size: 1rem;
    }

/* Part of the component, not a footnote to be trimmed — see the note in
   _CustomBatStage.cshtml. */
.rr-cfg-stage__disclaimer {
    margin: 0;
    font-size: var(--rr-fs-2xs);
    line-height: var(--rr-lh-body);
    color: var(--rr-muted);
}

/* The live caption. Near-black rather than gold-on-white: it sits over a
   photograph whose colours are unknown, and only a solid plate is legible
   against every one of them. */
.rr-cfg-stage__live {
    position: absolute;
    inset: auto .75rem .75rem;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .5rem;
    padding: .625rem .875rem;
    background: rgba(14, 14, 14, .88);
    border-radius: var(--rr-radius);
    color: #fff;
}

.rr-cfg-stage__live-head {
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: var(--rr-fs-xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;

    /* Gold on near-black, which is the one place --rr-gold is legible as text. */
    color: var(--rr-gold);
}

.rr-cfg-stage__live-body {
    font-size: var(--rr-fs-xs);
    line-height: var(--rr-lh-body);
}

.rr-cfg-stage__thumbs {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    list-style: none;
    padding: 0;
    margin: 0;
}

.rr-cfg-stage__thumb {
    width: 4.5rem;
    height: 4.5rem;
    padding: 0;
    overflow: hidden;
    background: var(--rr-surface-2);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    cursor: pointer;
}

    .rr-cfg-stage__thumb img {
        width: 100%;
        height: 100%;
        object-fit: cover;
        display: block;
    }

    .rr-cfg-stage__thumb.is-active,
    .rr-cfg-stage__thumb:hover {
        border-color: var(--rr-gold);
    }

/* --- The spec strip under the stage --------------------------------------- */
/* Hairline grid: one background, 1px gaps, cells painted over it — so the rules
   are always exactly 1px and never double up where two cells meet. */
.rr-cfg-spec {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1px;
    margin: 0;
    overflow: hidden;
    background: var(--rr-outline);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
}

@media (min-width: 576px) {
    .rr-cfg-spec {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }
}

/* ⚠ A COLUMN WITH THE VALUE PUSHED TO THE BOTTOM, not two stacked blocks. The
   four tiles are grid siblings and stretch to a common height, but each one laid
   its own dt and dd out independently — so "Handle thickness", the one label
   long enough to wrap, pushed "Medium" a line below the other three values and
   the strip read as misaligned. margin-top:auto on the dd sits every value on
   the same line regardless of how many lines its label took. */
.rr-cfg-spec__cell {
    display: flex;
    flex-direction: column;
    padding: .75rem .875rem;
    background: var(--rr-surface);
}

    .rr-cfg-spec__cell dt {
        font-family: var(--rr-font-body);
        font-weight: 600;
        font-size: var(--rr-fs-2xs);
        letter-spacing: var(--rr-ls-caps);
        text-transform: uppercase;
        color: var(--rr-muted);
        margin-bottom: .25rem;
    }

    .rr-cfg-spec__cell dd {
        font-family: var(--rr-font-head);
        font-weight: 700;
        font-size: var(--rr-fs-lg);
        letter-spacing: var(--rr-ls-ui);
        color: var(--rr-black);
        margin: auto 0 0;

        /* Four tiles across 38% of the page: a long size label wraps rather
           than pushing the grid wider than its track. */
        overflow-wrap: anywhere;
    }

/* --- A section of the rail ------------------------------------------------ */
.rr-cfg-section {
    padding-top: 1.25rem;
    /* The anchor's landing offset: the dock's breakdown links jump here, and a
       heading must not land under the sticky header. Only the header now — the
       sticky jump nav that used to add 4.5rem to this is gone. */
    scroll-margin-top: calc(var(--rr-header-h) + 1.5rem);
}

    .rr-cfg-section + .rr-cfg-section {
        margin-top: 1rem;
        border-top: 1px solid var(--rr-outline);
    }

.rr-cfg-section__head {
    margin-bottom: 1.125rem;
}

.rr-cfg-section__count {
    font-family: var(--rr-font-body);
    font-weight: 700;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-gold-text);
    margin: 0 0 .375rem;
}

/* A step up, to sit clearly above .rr-cfg-group__name — see the note there. */
.rr-cfg-section__title {
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: var(--rr-fs-5xl);
    letter-spacing: var(--rr-ls-ui);
    text-transform: uppercase;
    color: var(--rr-black);
    margin: 0 0 .375rem;
}

.rr-cfg-section__lede {
    font-size: var(--rr-fs-md);
    line-height: var(--rr-lh-body);
    color: var(--rr-muted);
    margin: 0;
}

/* "Required", beside the group name. Muted rather than an alarm colour: on a
   page where eleven of seventeen groups are required, red would be the page. */
.rr-cfg-group__req {
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
}

/* --- The price dock ------------------------------------------------------- */
.rr-cfg-page__dock {
    position: sticky;
    bottom: 0;
    z-index: 3;
    margin-top: 2rem;

    /* The rail's own background, so sections scrolling underneath are covered
       rather than showing through the dock's rounded corners. */
    background: var(--rr-surface);
    padding-bottom: .75rem;
}

@media (max-width: 991.98px) {
    /* Clear of the fixed mobile tab bar, which the dock would otherwise sit
       underneath — its Add to cart button half-covered by the Shop/Cart row.

       ⚠ The 58px is MEASURED, not derived, because .rr-bottom-nav has no
       explicit height: it is whatever its padded icons come to. It therefore
       does not match the 44px that section reserves as body padding-bottom, nor
       the 64px .rr-buybar offsets itself by. Those three disagreeing predates
       this page and is not fixed here; this value is the one that is actually
       true, so the dock at least covers nothing. */
    .rr-cfg-page__dock {
        bottom: 58px;
    }
}

.rr-cfg-dock {
    background: var(--rr-surface);
    border: 1px solid var(--rr-black);
    border-radius: var(--rr-radius);
    padding: 1rem 1.25rem;

    /* The one shadow in this layer. The dock floats over content by design and
       needs to read as a layer above it; everywhere else the site uses borders. */
    box-shadow: 0 -2px 16px rgba(14, 14, 14, .08);
}

.rr-cfg-dock__main {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1rem;
}

.rr-cfg-dock__figure {
    flex: 1 1 8rem;
    min-width: 0;
}

.rr-cfg-dock__label {
    font-family: var(--rr-font-body);
    font-weight: 700;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
    margin: 0;
}

.rr-cfg-dock__value {
    margin: 0;
    line-height: 1;
}

.rr-cfg-dock__gst {
    font-size: var(--rr-fs-2xs);
    color: var(--rr-muted);
    margin: .25rem 0 0;
}

.rr-cfg-dock__cta {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
}

/* With script the dock re-prices itself on every change, so a button that does
   nothing visible is worse than no button. Scoped to .rr-js, which means it is
   present and working for anyone without script. */
.rr-js .rr-cfg-dock__reprice {
    display: none;
}

@media (max-width: 575.98px) {
    .rr-cfg-dock__cta,
    .rr-cfg-dock__cta .btn {
        width: 100%;
    }
}

.rr-cfg-dock__pending,
.rr-cfg-dock__problem {
    display: flex;
    align-items: flex-start;
    gap: .5rem;
    font-size: var(--rr-fs-xs);
    line-height: var(--rr-lh-body);
    color: var(--rr-muted);
    margin: .75rem 0 0;
}

    .rr-cfg-dock__pending .material-symbols-outlined,
    .rr-cfg-dock__problem .material-symbols-outlined {
        font-size: var(--rr-fs-lg);
        color: var(--rr-gold-deep);
    }

.rr-cfg-dock__problem {
    color: var(--rr-black);
}

    .rr-cfg-dock__problem p {
        margin: 0 0 .25rem;
    }

    .rr-cfg-dock__problem .material-symbols-outlined {
        color: var(--rr-black);
    }

/* --- The breakdown -------------------------------------------------------- */
/* A native <details>: it opens with no JavaScript and is keyboard-operable for
   free. This is the only place a ₹30,000 order is itemised now that the review
   screen is gone, so it must not depend on script. */
.rr-cfg-dock__breakdown {
    margin-top: .75rem;
    border-top: 1px solid var(--rr-outline);
    padding-top: .75rem;
}

    .rr-cfg-dock__breakdown > summary {
        display: flex;
        align-items: center;
        gap: .5rem;
        cursor: pointer;
        font-weight: 600;
        font-size: var(--rr-fs-xs);
        color: var(--rr-gold-text);
    }

    .rr-cfg-dock__breakdown > summary:hover {
        color: var(--rr-black);
    }

    .rr-cfg-dock__breakdown > summary .material-symbols-outlined {
        font-size: var(--rr-fs-lg);
    }

.rr-cfg-dock__count {
    font-weight: 400;
    color: var(--rr-muted);
}

.rr-cfg-dock__panel {
    /* Capped and scrollable: a seventeen-line breakdown must not push the Add
       to cart button off a laptop screen, which is the whole point of a dock. */
    max-height: 40vh;
    overflow-y: auto;
    margin-top: .75rem;
}

.rr-cfg-dock__lines {
    list-style: none;
    padding: 0;
    margin: 0;
}

.rr-cfg-dock__line {
    display: flex;
    align-items: baseline;
    gap: 1rem;
    padding: .5rem 0;
    border-bottom: 1px dashed var(--rr-outline);
}

    .rr-cfg-dock__line:last-child {
        border-bottom: 0;
    }

.rr-cfg-dock__line-main {
    display: flex;
    flex: 1 1 auto;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .25rem .5rem;
    min-width: 0;
    text-decoration: none;
    color: inherit;
}

    .rr-cfg-dock__line-main:hover .rr-cfg-dock__line-value {
        color: var(--rr-gold-text);
    }

.rr-cfg-dock__line-group {
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    color: var(--rr-muted);
    flex: 0 0 9rem;
}

.rr-cfg-dock__line-value {
    font-size: var(--rr-fs-xs);
    font-weight: 600;
    color: var(--rr-black);
    overflow-wrap: anywhere;
}

.rr-cfg-dock__line-price {
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: var(--rr-fs-md);
    white-space: nowrap;
    color: var(--rr-black);
}

    .rr-cfg-dock__line-price--free {
        font-family: var(--rr-font-body);
        font-weight: 600;
        font-size: var(--rr-fs-2xs);
        letter-spacing: var(--rr-ls-wide);
        text-transform: uppercase;
        color: var(--rr-muted);
    }

.rr-cfg-dock__totals {
    margin: .75rem 0 0;
    padding-top: .75rem;
    border-top: 2px solid var(--rr-black);
}

.rr-cfg-dock__total-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 1rem;
    padding: .25rem 0;
    font-size: var(--rr-fs-xs);
}

    .rr-cfg-dock__total-row dt {
        font-weight: 400;
        color: var(--rr-muted);
    }

    .rr-cfg-dock__total-row dd {
        margin: 0;
    }

.rr-cfg-dock__total-row--grand {
    margin-top: .25rem;
}

    .rr-cfg-dock__total-row--grand dt {
        font-family: var(--rr-font-head);
        font-weight: 700;
        font-size: var(--rr-fs-xl);
        letter-spacing: var(--rr-ls-ui);
        text-transform: uppercase;
        color: var(--rr-black);
    }

.rr-cfg-dock__notes {
    margin-top: 1rem;
    padding-top: .75rem;
    border-top: 1px solid var(--rr-outline);
}

.rr-cfg-dock__notes-head {
    font-family: var(--rr-font-body);
    font-weight: 700;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-black);
    margin: 0 0 .375rem;
}

.rr-cfg-dock__notes ul {
    list-style: none;
    padding: 0;
    margin: 0;
    font-size: var(--rr-fs-xs);
    line-height: var(--rr-lh-body);
    color: var(--rr-muted);
}

.rr-cfg-dock__reassure {
    font-size: var(--rr-fs-2xs);
    line-height: var(--rr-lh-body);
    color: var(--rr-muted);
    margin: 1rem 0 0;
}

/* The dock is being replaced by a live re-price. Dimmed rather than removed, so
   the layout does not jump and the old figure stays readable until the new one
   lands — a total that blanks out mid-decision reads as a failure. */
.rr-cfg-dock.is-updating {
    opacity: .55;
    transition: opacity .15s ease;
}

@media (prefers-reduced-motion: reduce) {
    .rr-cfg-dock.is-updating {
        transition: none;
    }
}

/* --- Option diagrams ------------------------------------------------------
   The drawings on the handle-construction, sweet-spot and toe-shape cards. See
   _CustomBatDiagram.cshtml for why these are drawn rather than photographed.

   ⚠ EVERY COLOUR HERE IS A TOKEN, none is a literal. That is the whole reason
   the diagrams are inline SVG rather than files: a PNG of a gold-on-white
   drawing is wrong the day the palette moves, and nobody notices because it
   still renders. These follow the palette by construction. */
.rr-cfg-dia {
    display: block;
    width: 100%;
    height: 100%;

    /* ⚠ NO max-height, deliberately. The drawing fills the same square media box
       a photograph gets, so a card that has fallen back to its diagram is exactly
       the size of the two beside it that did find a file. A cap here made the
       drawn card shorter than its siblings the moment the set was incomplete —
       and an incomplete set is the normal, designed-for state of this folder. The
       viewBox is square and preserveAspectRatio is the default, so the drawing
       letterboxes inside the box rather than distorting. */
}

    /* The cane blank — a warm neutral rather than a literal willow colour,
       because this is a diagram of a handle, not a picture of one. */
    .rr-cfg-dia__cane {
        fill: var(--rr-surface-2);
        stroke: none;
    }

    /* The rubber laminations. These ARE the option: piece count is the only
       thing that differs between the three cards, so they carry the ink. */
    .rr-cfg-dia__lam {
        stroke: var(--rr-black);
        stroke-width: 5;
        stroke-linecap: round;
    }

    /* Thinner across the grain, so a 12-piece handle reads as a grid rather
       than as a solid black square. */
    .rr-cfg-dia__lam--cross {
        stroke-width: 3.5;
    }

    .rr-cfg-dia__rim {
        fill: none;
        stroke: var(--rr-outline);
        stroke-width: 2;
    }

    /* The blade, front or side elevation — a quiet fill under a firm outline.
       stroke-linejoin: round so a toe corner never comes to a hard mitre point,
       which at 40px reads as a chip in the wood. */
    .rr-cfg-dia__blade {
        fill: var(--rr-surface-2);
        stroke: var(--rr-black);
        stroke-width: 2.5;
        stroke-linejoin: round;
        stroke-linecap: round;
    }

    /* The handle stub on the sweet-spot cards. A shade lighter than the blade so
       the shoulder step is legible without a second outline weight. */
    .rr-cfg-dia__handle {
        fill: var(--rr-surface-2);
        stroke: var(--rr-black);
        stroke-width: 2.5;
        stroke-linejoin: round;
    }

    /* The edge chamfer — where the flat face rolls over into the rounded edge.
       Deliberately lighter than the outline: it is a surface change, not a
       silhouette, and at the same weight it would read as a second bat inside
       the first. */
    .rr-cfg-dia__edge {
        fill: none;
        stroke: var(--rr-black);
        stroke-width: 1.5;
        stroke-linecap: round;
        stroke-linejoin: round;
        opacity: .4;
    }

    /* The sweet spot, stated the way a spec sheet states a region: a band with
       its centre line marked, clipped to the blade so it can never float off it.
       This is the one place on these cards gold is a fill, doing the job gold
       does everywhere on this site — marking the thing that matters. The same
       language as the stage drawing's .rr-cfg-bat__band, on purpose. */
    .rr-cfg-dia__band {
        fill: var(--rr-gold-soft);
    }

    .rr-cfg-dia__band-axis {
        stroke: var(--rr-gold);
        stroke-width: 2.5;
    }

/* .rr-cfg-option__media--diagram was here. It existed to turn off a grey plate
   that .rr-cfg-option__media no longer paints, so it had nothing left to do and
   the class went from the markup with it. A diagram and a photograph now sit in
   the same box, at the same size, on the same ground, by sharing one rule
   rather than by keeping two rules in agreement. */

/* --- Personalisation: the field ------------------------------------------- */
.rr-cfg-text {
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px dashed var(--rr-outline);
}

    /* The personalisation field, hidden until its own checkbox is ticked.

       ⚠ SAME PROGRESSIVE-ENHANCEMENT CONTRACT AS .rr-cfg-group ABOVE, and for
       the same reason: scoped to .rr-js, so without JavaScript the field is
       always visible rather than unreachable. The server drops the wording when
       the toggle ends up off, so an untouched field costs nobody anything.

       ⚠ :not([data-rr-text-group=""]) is load-bearing exactly as it is on the
       group rule — Razor writes the attribute with an EMPTY value on a field
       that hangs off nothing, and without this half of the selector such a field
       would match, never gain .is-shown, and vanish. */
    .rr-js .rr-cfg-text[data-rr-text-group]:not([data-rr-text-group=""]):not(.is-shown) {
        display: none;
    }

.rr-cfg-text__hint {
    margin: .375rem 0 0;
    font-size: var(--rr-fs-2xs);
    color: var(--rr-muted);
}

/* --- Personalisation: the preview ----------------------------------------- */
/* ⚠ THE SKETCH AND ITS CAVEAT SIT SIDE BY SIDE, NOT STACKED. The blade is a
   60px-wide drawing and the rail is ~750px, so a centred column left a band of
   empty grey the width of the page above a line of small print — it read as a
   panel that had failed to load rather than as a sketch. Beside it, the drawing
   is the same size, the caveat is next to the thing it qualifies, and the block
   costs roughly half the height. The sketch is only ~51px wide (an 8rem-tall
   120×300 viewBox), so the pairing survives a phone; it stacks only below 22rem,
   where the caveat would be down to two words a line. */
.rr-cfg-preview {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas:
        "head head"
        "art  note";
    column-gap: 1.25rem;
    align-items: center;
    margin: 1rem 0 0;
    padding: 1rem;
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    background: var(--rr-surface-2);
}

@media (max-width: 21.99rem) {
    .rr-cfg-preview {
        grid-template-columns: minmax(0, 1fr);
        grid-template-areas:
            "head"
            "art"
            "note";
        row-gap: .75rem;
    }
}

.rr-cfg-preview__head {
    grid-area: head;
    font-family: var(--rr-font-body);
    font-weight: 700;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
    margin: 0 0 .75rem;
}

.rr-cfg-preview__stage {
    grid-area: art;
    display: flex;
    justify-content: center;
}

.rr-cfg-preview__art {
    width: auto;

    /* Down from 13rem. This is a sketch of where the engraving sits, captioned
       as "not a proof" — at 316px the panel was the tallest block on the rail
       and made a confirm-the-placing hint look like the main event. 8rem still
       shows the name running up the blade, which is the only thing it is for. */
    height: 8rem;
    max-width: 100%;
}

    /* The blade and the cover, drawn the same way as the option diagrams so the
       two never look like they came from different sites. */
    .rr-cfg-preview__blade,
    .rr-cfg-preview__cover {
        fill: var(--rr-surface);
        stroke: var(--rr-black);
        stroke-width: 2;
        stroke-linejoin: round;
    }

    .rr-cfg-preview__cover {
        fill: var(--rr-ink);
    }

    .rr-cfg-preview__cover-pocket,
    .rr-cfg-preview__cover-flap {
        fill: none;
        stroke: var(--rr-outline);
        stroke-width: 1.5;
        opacity: .5;
    }

    .rr-cfg-preview__cover-seam {
        stroke: var(--rr-outline);
        stroke-width: 1;
        opacity: .35;
    }

    /* The customer's own words. Barlow Condensed because that is what engraving
       on a Royal Rider bat is set in, and because a condensed face is what
       actually fits down a blade. */
    .rr-cfg-preview__text {
        font-family: var(--rr-font-head);
        font-weight: 700;
        font-size: 22px;
        letter-spacing: var(--rr-ls-ui);
        text-transform: uppercase;
        fill: var(--rr-gold-deep);
    }

    /* On the near-black cover the deep gold loses its contrast, so the text
       goes to the light gold that is legible on it. Neither is body copy — both
       are display sizes — so the large-text contrast rule is the one that
       applies. */
    .rr-cfg-preview__text--cover {
        fill: var(--rr-gold);
        font-size: 18px;
    }

/* Nothing typed yet: the placeholder wording is shown at reduced strength so
   the panel reads as working-but-empty rather than as finished. */
.rr-cfg-preview.is-empty .rr-cfg-preview__text {
    opacity: .38;
}

.rr-cfg-preview__note {
    grid-area: note;
    margin: 0;
    font-size: var(--rr-fs-2xs);
    line-height: var(--rr-lh-body);
    color: var(--rr-muted);
}

/* --- Size guide modal ----------------------------------------------------- */
.rr-cfg-guide {
    border-radius: var(--rr-radius);
    border: 1px solid var(--rr-outline);
}

.rr-cfg-guide__head {
    border-bottom: 1px solid var(--rr-outline);
}

.rr-cfg-guide__title {
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: var(--rr-fs-4xl);
    letter-spacing: var(--rr-ls-ui);
    text-transform: uppercase;
    color: var(--rr-black);
}

.rr-cfg-guide__lede {
    font-size: var(--rr-fs-md);
    line-height: var(--rr-lh-body);
    color: var(--rr-muted);
    margin: 0 0 1.25rem;
}

/* --- Read-only specification ---------------------------------------------
   One block, three places: the cart line's disclosure, the public order
   summary and the admin order detail. On an order it renders the immutable
   OrderItemOption snapshot. */
.rr-buildspec {
    margin: 0;
}

.rr-buildspec__row {
    display: grid;
    grid-template-columns: minmax(8rem, 12rem) 1fr;
    gap: .5rem 1rem;
    padding: .5rem 0;
    border-bottom: 1px solid var(--rr-outline);
}

    .rr-buildspec__row:last-child {
        border-bottom: 0;
    }

@media (max-width: 575.98px) {
    .rr-buildspec__row {
        grid-template-columns: 1fr;
        gap: .125rem;
    }
}

.rr-buildspec__group {
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    color: var(--rr-muted);
    align-self: center;
}

.rr-buildspec__value {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    margin: 0;
    font-size: var(--rr-fs-md);
    color: var(--rr-black);

    /* Wraps so the personalisation line below can claim a full row of its own.
       With two children this changes nothing — there has never been anything to
       wrap — so no existing spec row moves. */
    flex-wrap: wrap;
}

.rr-buildspec__label {
    font-weight: 600;
}

/* The engraving wording, under the option it belongs to.

   ⚠ flex-basis: 100% is what puts it on its own line rather than squeezing it
   between the label and the price. It is the line the owner works from — the
   one thing on an order that cannot be re-derived from the catalogue — so it
   gets a full row and the gold that marks it as content rather than chrome. */
.rr-buildspec__text {
    flex-basis: 100%;
    display: flex;
    align-items: center;
    gap: .375rem;
    margin-top: .25rem;
    font-family: var(--rr-font-body);
    font-weight: 600;
    font-size: var(--rr-fs-sm);

    /* --rr-gold-text, not --rr-gold: this is normal-size text on white and the
       measured 5.17:1 token is the only gold that passes there (CLAUDE.md). */
    color: var(--rr-gold-text);
}

    .rr-buildspec__text .material-symbols-outlined {
        font-size: 1rem;
        color: var(--rr-muted);
    }

.rr-buildspec__price {
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: var(--rr-fs-lg);
    color: var(--rr-black);
    white-space: nowrap;
}

    .rr-buildspec__price--free {
        font-family: var(--rr-font-body);
        font-weight: 600;
        font-size: var(--rr-fs-2xs);
        letter-spacing: var(--rr-ls-wide);
        text-transform: uppercase;
        color: var(--rr-muted);
    }

/* The cart line's "View the full build" disclosure. A native <details>, so it
   works with no JavaScript and is keyboard-operable for free. */
.rr-cart-line__spec {
    margin-top: .5rem;
}

    .rr-cart-line__spec > summary {
        display: inline-flex;
        align-items: center;
        gap: .25rem;
        font-weight: 600;
        font-size: var(--rr-fs-xs);
        color: var(--rr-gold-text);
        cursor: pointer;
    }

    .rr-cart-line__spec > summary:hover {
        color: var(--rr-black);
    }

    .rr-cart-line__spec[open] > summary {
        margin-bottom: .5rem;
    }

    .rr-cart-line__spec .rr-buildspec {
        padding: .75rem 1rem;
        background: var(--rr-surface-2);
        border: 1px solid var(--rr-outline);
        border-radius: var(--rr-radius);
    }

/* The specification row underneath a custom line on the public order summary. */
.rr-receipt__spec-row > td {
    background: var(--rr-surface-2);
    padding: 1rem 1.25rem;
}

    .rr-receipt__spec-row .rr-receipt__subhead {
        margin-bottom: .5rem;
    }

/* The same, in the admin order-items table. */
.rr-admin-spec-cell {
    background: var(--rr-surface-2);
}

.rr-admin-spec-cell__head {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: .5rem;
    font-family: var(--rr-font-body);
    font-weight: 700;
    font-size: var(--rr-fs-2xs);
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-black);
    margin: 0 0 .5rem;
}

/* A problem, not a note. Same block as .rr-notice-inline with the accent and the
   icon switched to the error treatment the flash partial already uses. */
.rr-notice-inline--problem {
    border-left-color: var(--rr-black);
}

    .rr-notice-inline--problem .material-symbols-outlined {
        color: var(--rr-black);
    }

    .rr-notice-inline--problem p:last-of-type {
        margin-bottom: .5rem;
    }

/* ==========================================================================
   40. BILLING
   ==========================================================================
   Three surfaces, one document.

     • .rr-bill-line*   the admin editor — the only place a bill is changed.
     • .rr-bill-doc*    the DOCUMENT, shared verbatim by the customer's page at
                        /bill/{token} and the owner's print view. Its markup is
                        Views/Shared/_BillDocument.cshtml and its data comes from
                        Services/BillDocument — one partial and one mapper, so the
                        two copies of a bill cannot disagree.
     • .rr-bill-page /  the chrome around the document on each of those two
       .rr-bill-print*  surfaces. All of it is .rr-no-print.

   ⚠ The document rules must NOT depend on the --rr-a-* section accents: those
   are scoped to the admin body class, and this block also renders on the public
   storefront and on a layout-less print page. Everything below resolves from the
   base tokens or from local literals declared here. */

/* --- Payment-state pills --------------------------------------------------
   Derived from the money on every render (Services/BillTotals.StateOf), never
   from a stored column. NOT an order-status chip: the client declined that
   workflow, and this says only what has been received against what is owed.

   Colour reads as information, not alarm. Unpaid is GOLD rather than red for
   the same reason "WhatsApp not opened" is: nothing has gone wrong, a bill just
   has not been settled yet, and a red pill on every new bill would train the
   owner to ignore the colour entirely. --rr-gold-text (5.17:1) is the only gold
   valid as text; --rr-gold at 2.42:1 would fail. */
.rr-admin-tag--paid {
    border-color: #0D9488;
    background: #E4F5F2;
    color: #0F6B62; /* 5.4:1 on its tint */
}

.rr-admin-tag--part {
    border-color: #D97706;
    background: #FEF1E0;
    color: #92400E; /* 7.6:1 on its tint */
}

.rr-admin-tag--unpaid {
    border-color: var(--rr-gold);
    background: var(--rr-gold-soft);
    color: var(--rr-gold-text);
}

/* Row hover in the admin tables repoints .rr-admin-tag to the section accent,
   which would wash all three states to the same teal. The state is the one thing
   in the row that must never change meaning on hover. */
tr:hover .rr-admin-tag--paid {
    background: #E4F5F2;
    border-color: #0D9488;
    color: #0F6B62;
}

tr:hover .rr-admin-tag--part {
    background: #FEF1E0;
    border-color: #D97706;
    color: #92400E;
}

tr:hover .rr-admin-tag--unpaid {
    background: var(--rr-gold-soft);
    border-color: var(--rr-gold);
    color: var(--rr-gold-text);
}

/* --- Bills list ----------------------------------------------------------- */
/* The bills list carries three figures, not the dashboard's four. Without this
   the shared four-column grid leaves a hole at 1200px and up. */
@media (min-width: 1200px) {
    .rr-stat-grid--three {
        grid-template-columns: repeat(3, 1fr);
    }
}

/* --- Money inputs ---------------------------------------------------------
   A ₹ sitting inside the field rather than in the label. Every money field on
   the bill screen is a bare number to type into, and the symbol is what stops
   "500" being read as anything else at a glance. */
.rr-money-input {
    position: relative;
}

    .rr-money-input__symbol {
        position: absolute;
        top: 50%;
        left: .75rem;
        transform: translateY(-50%);
        font-family: var(--rr-font-head);
        font-weight: 600;
        color: var(--rr-muted);
        pointer-events: none;
    }

    .rr-money-input .form-control {
        padding-left: 1.85rem;
    }

/* Number spinners are noise on a price and actively harmful on a quantity the
   owner is scrolling past. Hidden, and the field stays type=number so phones
   still raise the numeric keypad. */
.rr-money-input .form-control::-webkit-outer-spin-button,
.rr-money-input .form-control::-webkit-inner-spin-button {
    margin: 0;
    -webkit-appearance: none;
}

.rr-field--qty {
    flex: 0 0 5rem;
}

.rr-field--money {
    flex: 0 0 9rem;
}

/* --- Bill editor: the line rows ------------------------------------------- */
.rr-bill-lines {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    margin-bottom: 1.25rem;
}

.rr-bill-line {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    padding: 1rem;
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    background: var(--rr-surface-2);
}

.rr-bill-line__main {
    flex: 1 1 22rem;
    display: flex;
    flex-direction: column;
    gap: .75rem;
    min-width: 0;
}

/* Two fields side by side that collapse to one column on a phone. Reused for
   the paired bill-to fields (city/state, pincode/country) — same shape, and a
   second class name for the same rule would be worse. */
.rr-bill-line__codes {
    display: grid;
    gap: .75rem;
    grid-template-columns: 1fr;
}

@media (min-width: 576px) {
    .rr-bill-line__codes {
        grid-template-columns: 1fr 1fr;
    }
}

.rr-bill-line__money {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: .75rem;
}

.rr-bill-line__total {
    display: flex;
    flex-direction: column;
    gap: .35rem;
    justify-content: flex-start;
    min-width: 6rem;
    padding-top: .1rem;
}

/* Removes a line without saving — the whole form round-trips and comes back a
   row shorter. Quiet until hovered: on a bill with eight lines, eight red
   buttons would be the loudest thing on the screen. */
.rr-bill-line__remove {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    margin-top: 1.65rem;
    padding: 0;
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    background: var(--rr-surface);
    color: var(--rr-muted);
    transition: color var(--rr-t) var(--rr-ease), border-color var(--rr-t) var(--rr-ease);
}

    .rr-bill-line__remove:hover,
    .rr-bill-line__remove:focus-visible {
        border-color: #E11D48;
        color: #A3113A; /* 6.9:1 on white */
    }

    .rr-bill-line__remove .material-symbols-outlined {
        font-size: 18px;
    }

.rr-bill-addline {
    margin-top: .25rem;
}

.rr-bill-save {
    position: sticky;
    bottom: 1rem;
    display: flex;
    flex-direction: column;
    gap: .5rem;
    align-items: stretch;
    text-align: center;
}

/* --- Bill editor: the share panel ----------------------------------------- */
.rr-bill-share {
    margin-top: 1.25rem;
}

.rr-bill-share__actions {
    display: grid;
    gap: 1.25rem;
    grid-template-columns: 1fr;
    align-items: start;
    margin-top: .75rem;
}

@media (min-width: 992px) {
    .rr-bill-share__actions {
        grid-template-columns: auto 1fr;
        gap: 2rem;
    }
}

.rr-bill-share__link {
    min-width: 0;
}

.rr-bill-share__copy {
    display: flex;
    gap: .5rem;
}

    /* The URL is ~40 characters of token. It must be allowed to shrink rather
       than push the Copy button off a narrow screen. */
    .rr-bill-share__copy .form-control {
        min-width: 0;
        font-size: 13px;
    }

    .rr-bill-share__copy .btn {
        flex: 0 0 auto;
    }

/* ==========================================================================
   THE DOCUMENT
   ==========================================================================
   Everything below renders on three grounds — the storefront layout, the
   layout-less print page, and paper — so it paints its own white background and
   its own borders rather than inheriting any. */
.rr-bill-doc {
    display: block;
    max-width: 56rem;
    margin-inline: auto;
    padding: clamp(1.25rem, 4vw, 2.5rem);
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    background: #ffffff;
    color: var(--rr-ink);
}

.rr-bill-doc__head {
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem;
    justify-content: space-between;
    padding-bottom: 1.5rem;
    border-bottom: 1px solid var(--rr-outline);
}

.rr-bill-doc__brand {
    min-width: 0;
}

.rr-bill-doc__logo {
    display: block;
    width: 180px;
    height: auto;
    margin-bottom: .875rem;
}

.rr-bill-doc__from {
    display: flex;
    flex-direction: column;
    gap: .1rem;
    margin: 0;
    font-size: 13px;
    font-style: normal;
    line-height: var(--rr-lh-body);
    color: var(--rr-muted);
}

.rr-bill-doc__meta {
    text-align: left;
}

@media (min-width: 576px) {
    .rr-bill-doc__meta {
        text-align: right;
    }
}

.rr-bill-doc__title {
    margin: 0 0 .5rem;
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: clamp(28px, 5vw, 38px);
    letter-spacing: var(--rr-ls-wide);
    text-transform: uppercase;
    color: var(--rr-black);
}

.rr-bill-doc__facts {
    display: flex;
    flex-direction: column;
    gap: .2rem;
    margin: 0 0 .75rem;
    font-size: 13px;
}

    .rr-bill-doc__facts > div {
        display: flex;
        gap: .5rem;
    }

@media (min-width: 576px) {
    .rr-bill-doc__facts > div {
        justify-content: flex-end;
    }
}

    .rr-bill-doc__facts dt {
        font-weight: 500;
        color: var(--rr-muted);
    }

    .rr-bill-doc__facts dd {
        margin: 0;
        font-weight: 600;
        color: var(--rr-ink);
    }

/* The payment state on the document itself. Same three meanings as the admin
   pill, sized as a document stamp rather than a table chip. */
.rr-bill-state {
    display: inline-block;
    margin: 0;
    padding: .2rem .6rem;
    border: 1px solid var(--rr-outline);
    border-radius: 999px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
}

.rr-bill-state--paid {
    border-color: #0D9488;
    background: #E4F5F2;
    color: #0F6B62;
}

.rr-bill-state--part {
    border-color: #D97706;
    background: #FEF1E0;
    color: #92400E;
}

.rr-bill-state--unpaid {
    border-color: var(--rr-gold);
    background: var(--rr-gold-soft);
    color: var(--rr-gold-text);
}

.rr-bill-doc__party {
    padding-block: 1.5rem;
}

.rr-bill-doc__label {
    margin: 0 0 .4rem;
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: 12px;
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: var(--rr-muted);
}

.rr-bill-doc__address {
    margin: 0;
    font-style: normal;
    line-height: var(--rr-lh-body);
    color: var(--rr-ink);
}

/* The customer's phone, under their address. Muted because it is reference data
   on this document rather than something to read - unlike the shipping label,
   where the number is oversized precisely because it is what gets a parcel
   delivered. Same string, opposite emphasis, and that is deliberate. */
.rr-bill-doc__phone {
    color: var(--rr-muted);
}

/* --- The itemised table --------------------------------------------------- */
.rr-bill-table {
    margin-bottom: 0;
}

    .rr-bill-table > thead > tr > th {
        padding: .5rem .75rem;
        border-bottom: 1px solid var(--rr-ink);
        font-family: var(--rr-font-head);
        font-weight: 700;
        font-size: 12px;
        letter-spacing: var(--rr-ls-caps);
        text-transform: uppercase;
        color: var(--rr-black);
        background: transparent;
    }

    .rr-bill-table > tbody > tr > th,
    .rr-bill-table > tbody > tr > td {
        padding: .7rem .75rem;
        border-bottom: 1px solid var(--rr-outline);
        font-weight: 400;
        background: transparent;
    }

.rr-bill-table__name {
    display: block;
    font-weight: 600;
    color: var(--rr-ink);
}

.rr-bill-table__detail {
    display: block;
    margin-top: .1rem;
    font-size: 12px;
    color: var(--rr-muted);
}

/* --- Note and totals ------------------------------------------------------ */
.rr-bill-doc__foot {
    display: grid;
    gap: 1.5rem;
    grid-template-columns: 1fr;
    margin-top: 1.5rem;
}

@media (min-width: 768px) {
    .rr-bill-doc__foot {
        grid-template-columns: 1fr minmax(16rem, 20rem);
    }
}

.rr-bill-doc__note p {
    margin: 0;
    font-size: 14px;
    line-height: var(--rr-lh-body);
    color: var(--rr-ink);
}

/* --- How to pay -----------------------------------------------------------
   The payment block, from SiteSetting. Boxed rather than free text so it reads
   as a distinct instruction after the total, and kept off a page break with it:
   a bill whose "how to pay" landed alone on sheet two would be a phone call.

   Outline and a soft tint, never a gold fill - this is information, and giving
   it a button's weight is exactly how a document starts looking like a checkout
   the customer can pay from. There is no online payment (CLAUDE.md). */
.rr-bill-doc__pay {
    break-inside: avoid;
    margin-top: 1.5rem;
    padding: .9rem 1rem;
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    background: var(--rr-surface-2);
}

.rr-bill-doc__paytext {
    margin: 0;
    /* The owner's own line breaks survive without any markup in the setting, so
       "UPI: ..." and "Bank: ..." stay on separate lines as typed. */
    white-space: pre-line;
    font-size: 14px;
    line-height: 1.7;
    color: var(--rr-ink);
}

.rr-bill-totals {
    /* Kept whole across a page break: totals split over two sheets is the one
       formatting failure on a bill that actually costs the owner a phone call. */
    break-inside: avoid;
}

.rr-bill-totals__row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    padding-block: .3rem;
    font-size: 14px;
    color: var(--rr-muted);
}

/* Money coming off the bill — a discount, an amount already received. */
.rr-bill-totals__row--credit {
    color: var(--rr-ink);
}

.rr-bill-totals__total {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    margin-top: .6rem;
    padding-top: .75rem;
    border-top: 1px solid var(--rr-outline);
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: 18px;
    letter-spacing: var(--rr-ls-ui);
    text-transform: uppercase;
    color: var(--rr-black);
}

/* What is still owed — the number the customer is actually looking for, so it
   carries the gold rule that nothing else on the document does. */
.rr-bill-totals__balance {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    margin-top: .6rem;
    padding: .6rem .75rem;
    border: 1px solid var(--rr-gold);
    border-radius: var(--rr-radius);
    background: var(--rr-gold-soft);
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: 17px;
    letter-spacing: var(--rr-ls-ui);
    text-transform: uppercase;
    color: var(--rr-black);
}

.rr-bill-totals__gst {
    margin: .7rem 0 0;
    font-size: 12px;
    color: var(--rr-muted);
}

/* --- The customer's page around the document ------------------------------ */
.rr-bill-page__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-items: flex-end;
    justify-content: space-between;
    max-width: 56rem;
    margin: 0 auto 1.25rem;
}

.rr-bill-page__heading {
    margin: 0;
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: clamp(26px, 5vw, 34px);
    letter-spacing: var(--rr-ls-ui);
    text-transform: uppercase;
    color: var(--rr-black);
}

.rr-bill-page__sub {
    margin: .2rem 0 0;
    font-size: 14px;
    color: var(--rr-muted);
}

.rr-bill-page__buttons {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
}

.rr-bill-page__settle {
    display: flex;
    gap: .6rem;
    max-width: 56rem;
    margin: 1.25rem auto 0;
    padding: .875rem 1rem;
    border: 1px solid var(--rr-gold);
    border-radius: var(--rr-radius);
    background: var(--rr-gold-soft);
    font-size: 14px;
    line-height: var(--rr-lh-body);
    /* --rr-muted drops below AA on the gold tint; this is the token measured
       against it (see the note beside --rr-muted-on-tint). */
    color: var(--rr-muted-on-tint);
}

    .rr-bill-page__settle strong {
        color: var(--rr-black);
    }

    .rr-bill-page__settle .material-symbols-outlined {
        flex: 0 0 auto;
        font-size: 20px;
        color: var(--rr-gold-text);
    }

/* --- The owner's layout-less print page ----------------------------------- */
.rr-bill-print {
    margin: 0;
    padding: 2rem 1rem 4rem;
    background: var(--rr-surface-2);
}

.rr-bill-print__bar {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-items: center;
    justify-content: space-between;
    max-width: 56rem;
    margin: 0 auto 1.25rem;
}

.rr-bill-print__sheet {
    display: block;
}

/* --- Print ----------------------------------------------------------------
   The document is the page. Everything that exists to navigate to it, share it
   or print it is chrome, and chrome on paper is wasted ink. */
.rr-no-print {
    /* No effect on screen — the rule that matters is in the @media print below.
       Declared here so the class is not a bare hook with no definition. */
    display: revert;
}

@media print {
    .rr-no-print,
    .rr-bill-print__bar {
        display: none !important;
    }

    .rr-bill-print {
        padding: 0;
        background: #ffffff;
    }

    /* THE STOREFRONT CHROME, on the customer's copy only.
       ------------------------------------------------------------------
       /bill/{token} renders inside the full storefront layout so the customer
       lands somewhere recognisably Royal Rider - but every one of those pieces
       is navigation, and none of it belongs on a bill. Verified in a real print
       render before this rule existed: the announcement bar, the header, the
       mobile bottom nav and the near-black footer all printed, the footer alone
       taking roughly half the output, and the floating WhatsApp button landed
       ON TOP of the Received and Balance-due figures - covering money, which is
       the one thing on this document nobody may misread.

       The footer is the case CLAUDE.md and the C2 brief both single out. With
       the browser's default "background graphics off" its black ground silently
       disappears and its white text prints invisibly, leaving a large blank
       region with a few stray gold headings; with backgrounds ON it is a solid
       black rectangle. Neither is a bill, so it comes off the page entirely
       rather than being re-coloured.

       Scoped with :has() to the bill page rather than applied globally: the
       order confirmation is also printed sometimes and is a different decision,
       and _Layout carries no per-page body class to hang this on. The admin
       print view at /admin/bills/{number}/print has none of this chrome to
       begin with - it was already clean - so this rule is about the customer's
       copy only. */
    body:has(.rr-bill-page) .rr-announcement,
    body:has(.rr-bill-page) .rr-header,
    body:has(.rr-bill-page) .rr-footer,
    body:has(.rr-bill-page) .rr-fab,
    body:has(.rr-bill-page) .rr-bottom-nav,
    body:has(.rr-bill-page) .rr-skip-link,
    body:has(.rr-bill-page) .rr-cart-drawer,
    body:has(.rr-bill-page) .rr-search-overlay {
        display: none !important;
    }

    /* The layout reserves space for the sticky header and the fixed bottom nav.
       With both gone the document would otherwise print inside their gap.

       The body padding is the one that actually cost a sheet of paper. The rule
       reserving it is scoped to max-width:991.98px, which looks like a phone-only
       concern until you notice that an A4 page at 96dpi is about 717px of CSS
       width — so PRINTING lands inside that breakpoint on every machine. The
       document measured 1010px against 1032px of usable page and still spilled,
       because 64px of dead space for a nav that is not printed sat underneath it.
       Measured, not guessed; re-measure if the layout gains another fixed bar. */
    body:has(.rr-bill-page) {
        padding-bottom: 0 !important;
    }

    body:has(.rr-bill-page) main {
        padding: 0 !important;
        margin: 0 !important;
    }

    .rr-bill-page {
        padding: 0 !important;
        margin: 0 !important;
        max-width: none;
    }

    /* Borders and rounded corners are screen affordances; on paper the sheet
       edge already does that job, and a printed hairline box around a full page
       reads as a defect. */
    .rr-bill-doc {
        max-width: none;
        margin: 0;
        padding: 0;
        border: 0;
        border-radius: 0;
    }

    /* Tints do not survive most printers' default "background graphics off"
       setting — the fill silently vanishes and takes the contrast with it. On
       paper these carry their weight with a border and the type instead. */
    .rr-bill-state,
    .rr-bill-totals__balance,
    .rr-bill-doc__pay {
        background: transparent !important;
        color: #000000 !important;
    }

    /* Tightened for paper, and by a measured amount rather than by eye.
       -----------------------------------------------------------------
       A4 at the 12mm margins PublicBillViewModel.PageRule sets leaves 1032px
       of content height at 96dpi. A two-line order with a note and the payment
       block measured 1050px with the screen's spacing — 18px over, which spent
       a whole second sheet on nothing. These three cuts take it to about
       1010px, so an ordinary bill lands on one page with room to spare.

       Screen spacing is deliberately unchanged: a document wants more air on a
       monitor than it does on paper, and this is the only place that trade-off
       differs. Re-measure before adding another block to the document. */
    .rr-bill-doc__party {
        padding-top: 1rem;
        padding-bottom: 1rem;
    }

    .rr-bill-doc__foot {
        margin-top: .875rem;
    }

    .rr-bill-doc__pay {
        margin-top: .625rem;
    }
}

/* ==========================================================================
   41. SHIPPING LABEL
   ==========================================================================
   The sheet the owner prints, cuts out and sticks on the box. Not the bill —
   the bill goes IN the box, this goes ON it.

   One set of components serving two very different stocks:

     • A4          a normal inkjet/laser sheet with a dashed cut guide. The
                   default, because that is the printer a single shop has.
     • 100×150mm   the 4×6in stock a courier thermal printer takes, selected
                   with ?size=thermal. There the label IS the paper, so the cut
                   guide disappears and the type tightens to fit.

   The @page size itself is written inline by Views/Admin/OrderLabel.cshtml,
   because @page is document-level and cannot be scoped by a body class — see
   AdminOrderLabelViewModel.PageRule. Everything else lives here.

   ⚠ Nothing below may rely on the --rr-a-* section accents: they are scoped to
   the admin body class and this page has its own. */
.rr-parcel-page {
    margin: 0;
    padding: 2rem 1rem 4rem;
    background: var(--rr-surface-2);
}

.rr-parcel-bar {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-items: center;
    justify-content: space-between;
    max-width: 46rem;
    margin: 0 auto 1.25rem;
}

.rr-parcel-bar__tools {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
    align-items: center;
}

.rr-parcel-sheet {
    display: block;
    max-width: 46rem;
    margin-inline: auto;
}

/* On screen the label is shown at roughly its printed proportions, on a dashed
   cut guide so the owner can see what they are about to cut out. */
.rr-parcel {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    padding: 1.5rem;
    border: 2px dashed var(--rr-outline);
    border-radius: var(--rr-radius);
    background: #ffffff;
    color: #000000;
}

.rr-parcel__head {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-items: flex-start;
    justify-content: space-between;
    padding-bottom: .75rem;
    border-bottom: 2px solid #000000;
}

.rr-parcel__logo {
    display: block;
    width: 150px;
    height: auto;
}

.rr-parcel__ref {
    text-align: right;
}

.rr-parcel__ref-tag {
    display: block;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: #444444;
}

.rr-parcel__ref-value {
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: 20px;
    letter-spacing: var(--rr-ls-ui);
}

/* Section captions. Small and grey — they must not compete with the address,
   which is the only thing on this sheet a courier actually reads. */
.rr-parcel__label {
    margin: 0 0 .3rem;
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: 11px;
    letter-spacing: var(--rr-ls-caps);
    text-transform: uppercase;
    color: #444444;
}

.rr-parcel__to {
    padding-bottom: 1rem;
    border-bottom: 1px solid #999999;
}

.rr-parcel__name {
    margin: 0 0 .25rem;
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: 26px;
    line-height: 1.15;
    letter-spacing: var(--rr-ls-ui);
}

.rr-parcel__address {
    margin: 0;
    font-size: 17px;
    font-style: normal;
    line-height: 1.5;
}

/* The pincode decides which depot the parcel is sorted to, and it is the single
   value most often misread off a handwritten or cramped label. */
.rr-parcel__pincode {
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: 22px;
    letter-spacing: var(--rr-ls-wide);
}

.rr-parcel__phone {
    display: flex;
    align-items: center;
    gap: .35rem;
    margin: .5rem 0 0;
    font-weight: 600;
    font-size: 16px;
}

    .rr-parcel__phone .material-symbols-outlined {
        font-size: 18px;
    }

.rr-parcel__from {
    font-size: 13px;
    line-height: 1.5;
}

.rr-parcel__from-name {
    margin: 0;
    font-weight: 700;
}

.rr-parcel__from-address,
.rr-parcel__from-phone {
    margin: 0;
    font-style: normal;
    color: #333333;
}

/* Screen-only: shown when the owner has not set a store address in Settings, so
   they find out before the parcel goes out rather than after it comes back. */
.rr-parcel__warn {
    margin: .25rem 0 0;
    font-size: 12px;
    color: var(--rr-gold-text);
}

.rr-parcel__foot {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    justify-content: space-between;
    padding-top: .6rem;
    border-top: 1px solid #999999;
    font-size: 12px;
    letter-spacing: var(--rr-ls-ui);
    text-transform: uppercase;
    color: #444444;
}

/* --- Thermal (100 × 150 mm) ----------------------------------------------
   Two thirds the area of the A4 block, so the type steps down and the padding
   comes off. The address stays the largest thing on the label at every size —
   that ordering is the whole design and it does not bend to fit. */
.rr-parcel-page--thermal .rr-parcel {
    gap: .6rem;
    padding: .75rem;
    border-style: solid;
    border-width: 1px;
}

.rr-parcel-page--thermal .rr-parcel-sheet {
    /* ~100mm at 96dpi, so the on-screen preview is close to the real stock. */
    max-width: 378px;
}

.rr-parcel-page--thermal .rr-parcel__logo {
    width: 104px;
}

.rr-parcel-page--thermal .rr-parcel__ref-value {
    font-size: 15px;
}

.rr-parcel-page--thermal .rr-parcel__name {
    font-size: 19px;
}

.rr-parcel-page--thermal .rr-parcel__address {
    font-size: 14px;
}

.rr-parcel-page--thermal .rr-parcel__pincode {
    font-size: 17px;
}

.rr-parcel-page--thermal .rr-parcel__phone {
    font-size: 13px;
}

.rr-parcel-page--thermal .rr-parcel__from {
    font-size: 11px;
}

.rr-parcel-page--thermal .rr-parcel__foot {
    font-size: 10px;
}

@media print {
    .rr-parcel-bar {
        display: none !important;
    }

    .rr-parcel-page {
        padding: 0;
        background: #ffffff;
    }

    .rr-parcel-sheet {
        max-width: none;
    }

    /* Pure black on white, always. Thermal printers are one-colour devices, and
       on an inkjet a grey rule can come out too faint to guide a cut. */
    .rr-parcel {
        border-color: #000000;
        break-inside: avoid;
    }

    /* On thermal stock the label IS the paper, so a cut guide would be drawing a
       line where no cut happens. The @page box is the edge. */
    .rr-parcel-page--thermal .rr-parcel {
        border: 0;
        padding: 0;
    }
}

/* ==========================================================================
   42. TESTIMONIALS
   ==========================================================================
   The "What our customers say" band (C3), shared by the home page and by a
   product page through Views/Shared/_Testimonials.cshtml.

   ⚠ These are quotes the OWNER typed in from WhatsApp — curated, not verified
   reviews. Nothing in this block styles an average, a score badge, a review
   count or a sort control, because none of those exist and none may be added;
   the reasoning is on Models/Entities/Testimonial and in CLAUDE.md.

   Borders over shadows, 8px radius, gold used sparingly — the same treatment as
   every other card in this stylesheet. The band sits on the warm tint on the
   home page, so the cards are white to lift off it. */
.rr-testimonials {
    display: grid;
    gap: var(--rr-gutter);
    grid-template-columns: 1fr;

    /* It is a <ul> — the quotes are a list, and a screen reader announcing "list,
       3 items" is exactly right. The bullets are not. */
    list-style: none;
    margin: 0;
    padding: 0;
}

@media (min-width: 576px) {
    .rr-testimonials {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (min-width: 992px) {
    .rr-testimonials {
        grid-template-columns: repeat(3, 1fr);
    }
}

.rr-testimonial {
    display: flex;
    flex-direction: column;
    gap: .75rem;
    padding: 1.5rem;
    border: 1px solid var(--rr-outline);
    border-radius: var(--rr-radius);
    background: var(--rr-surface);
}

/* Stars, and ONLY when the customer actually gave a rating — a quote without one
   renders no row here at all rather than five empty outlines, which would read
   as a zero-star rating instead of an absent one.

   --rr-gold-deep, not --rr-gold: these are glyphs, and at 2.42:1 the primary
   gold is a fill colour only. The deep gold is 3.82:1 and is the token this
   system designates for icons. */
.rr-testimonial__rating {
    display: flex;
    gap: .125rem;
    margin: 0;
    color: var(--rr-gold-deep);
}

    .rr-testimonial__rating .material-symbols-outlined {
        font-size: 18px;
    }

.rr-testimonial__quote {
    /* <blockquote> carries a default indent that fights the card padding. */
    margin: 0;
    flex: 1 1 auto;
}

    .rr-testimonial__quote p {
        margin: 0;
        font-size: var(--rr-fs-xl);
        line-height: 1.7;
        color: var(--rr-ink);
    }

    /* The opening quotation mark, drawn rather than typed — so the stored text
       stays exactly the customer's words with no punctuation added to it. */
    .rr-testimonial__quote p::before {
        content: "\201C";
        color: var(--rr-gold-deep);
    }

    .rr-testimonial__quote p::after {
        content: "\201D";
        color: var(--rr-gold-deep);
    }

.rr-testimonial__by {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .5rem;
    padding-top: .75rem;
    border-top: 1px solid var(--rr-outline);
}

.rr-testimonial__name {
    font-family: var(--rr-font-head);
    font-weight: 700;
    font-size: var(--rr-fs-2xl);
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--rr-black);

    /* <cite> is italic by default; the name is a name, not a title of a work. */
    font-style: normal;
}

.rr-testimonial__meta {
    font-size: var(--rr-fs-sm);
    color: var(--rr-muted);
}

/* The product a quote is attached to, on the home page only. --rr-gold-text is
   the ONLY gold valid for normal-size text on white (5.17:1); --rr-gold would be
   2.42:1 and --rr-gold-deep 3.82:1, both of which fail here. */
.rr-testimonial__product {
    margin-left: auto;
    font-size: var(--rr-fs-sm);
    color: var(--rr-gold-text);
    text-decoration: none;
}

    .rr-testimonial__product:hover,
    .rr-testimonial__product:focus {
        color: var(--rr-gold-deep);
        text-decoration: underline;
    }

/* The optional line beside the section heading. Sits in .rr-section-head, which
   is a baseline-aligned flex row, so it needs no alignment of its own. */
.rr-testimonials__intro {
    margin: 0;
    max-width: 32rem;
    font-size: var(--rr-fs-sm);
    color: var(--rr-muted);
}

@media print {
    /* Social proof is marketing, and marketing is not what anyone is printing —
       the pages people print here are the bill and the shipping label. */
    .rr-testimonials,
    .rr-testimonials__intro {
        display: none;
    }
}

/* A switched-off testimonial row (C3). `is-off` is the state class the banner
   cards already use; this extends it to a table row, which had no treatment
   before — the row rendered identically to a live one and only the eye icon
   differed, so a hidden quote was indistinguishable at a glance.

   Muted rather than hidden: the owner needs to see it in order to switch it back
   on, which is the whole point of the soft delete. */
.rr-admin-table > tbody > tr.is-off > * {
    opacity: .55;
}

    /* The actions stay at full strength — they are how the row is brought back,
       and a dimmed "show" button is the one control that must not look disabled. */
    .rr-admin-table > tbody > tr.is-off .rr-admin-actions {
        opacity: 1;
    }

/* --------------------------------------------------------------------------
   43. ADMIN TABLES - ONE LINE PER ROW (dense mode)

   Every admin list is a scanning surface: the owner is looking for one order,
   one product, one enquiry among many. Stacked cells (name over email, date
   over time) doubled every row's height and halved how many rows fit on a
   laptop screen, so this layer flattens each row onto a single line and lets
   the table scroll sideways inside its .table-responsive wrapper instead of
   growing downwards.

   It is the LAST admin-table layer in the file on purpose — it has to win over
   the §34.5 block above, which shares its specificity.
   -------------------------------------------------------------------------- */
.rr-admin-table > thead th {
    padding: .4375rem var(--rr-ad-cell-x);
}

.rr-admin-table > tbody td,
.rr-admin-table > tbody th {
    padding: .4375rem var(--rr-ad-cell-x);
    line-height: 1.4;
    /* The rule that makes the row one line. Everything the cell holds now sits
       side by side; the wrapper scrolls if the set of columns is too wide. */
    white-space: nowrap;
}

/* The spans that used to stack. Scoped to `span.` deliberately: the same class
   names are also used on whole <td>s (Enquiries' preview, Pages' length), and
   `display: inline` on a table cell would collapse the table. */
.rr-admin-table span.rr-admin-table__name,
.rr-admin-table span.rr-admin-table__sub,
.rr-admin-table span.rr-admin-table__date,
.rr-admin-table span.rr-admin-table__time {
    display: inline;
}

/* A separator only when the secondary span actually follows something in the
   same cell. :not(:first-child) covers every pairing in the admin views
   (name+email, date+time, product+slug, state+hint) without this file having
   to know which view produced it — and correctly renders nothing when the
   secondary span is a cell's only content. */
.rr-admin-table span.rr-admin-table__sub:not(:first-child)::before,
.rr-admin-table span.rr-admin-table__time:not(:first-child)::before {
    content: "·";
    margin: 0 .375rem;
    /* Muted at half strength: visible enough to read as a deliberate divider
       between two facts, quiet enough not to compete with either of them. */
    color: var(--rr-muted);
    opacity: .5;
}

/* The same three class names are also put straight on a <td> in a few views
   (Pages' length and last-edited, Enquiries' preview, Testimonials' body). The
   §34 base sets `display: block` on them for the stacked layout, which on a
   table cell drops it out of the row's column grid — the Pages "Length" column
   rendered floating half a row high. Nothing stacks any more, so put the cells
   back into the table. */
.rr-admin-table > tbody td.rr-admin-table__name,
.rr-admin-table > tbody td.rr-admin-table__sub,
.rr-admin-table > tbody td.rr-admin-table__date,
.rr-admin-table > tbody td.rr-admin-table__time {
    display: table-cell;
}

/* Free-text cells are the one thing that cannot be allowed to set the table's
   width — an enquiry preview or a testimonial body would push Actions off the
   far right of the scroll area. They clip with an ellipsis; the full text is on
   the detail screen the row links to. */
.rr-admin-table > tbody td.rr-admin-table__sub {
    max-width: 22rem;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Thumbnails and icon buttons set the floor on row height, so they come down
   with everything else. 30px still reads as a product picture at a glance. */
.rr-admin-table .rr-admin-thumb {
    width: 30px;
    height: 30px;
    border-radius: 6px;
}

    .rr-admin-table .rr-admin-thumb .material-symbols-outlined {
        font-size: 16px;
    }

.rr-admin-table .rr-admin-thumb-cell {
    width: 2.5rem;
}

.rr-admin-table .rr-admin-table__action {
    width: 26px;
    height: 26px;
}

    .rr-admin-table .rr-admin-table__action .material-symbols-outlined {
        font-size: 17px;
    }

/* The count pill and price sit inside the same single line. The storefront
   .rr-price is display type at --rr-fs-4xl; in a dense list it out-shouted the
   product name and set the row height on its own, so here it drops to body
   size and keeps only the Barlow Condensed face. */
.rr-admin-table .rr-admin-count {
    padding: .05rem .4rem;
    font-size: 12px;
}

.rr-admin-table > tbody .rr-price {
    font-size: 14px;
}

/* Form controls inside an editable admin table (product variants, bill lines)
   must not re-inflate the row they sit in. */
.rr-admin-table > tbody .form-control,
.rr-admin-table > tbody .form-select {
    padding: .25rem .5rem;
    font-size: var(--rr-ad-text);
    line-height: 1.4;
}
