:root {
  /* Duopharma v3 palette (2026-09-02) — navy ink, green workhorse, red brand moments. Names are
     the ones pages already reference in inline styles; only the values moved — --forest holds
     the action green and --brand the brand red. --amber is the WARNING colour, not a brand
     accent; it deliberately kept its value. The green is #11803F, two steps deeper than the
     export's #17934F: the export value measures ~3.9:1 on white, and the first step (#128643)
     passed on white but measured 4.30:1 on --pg, where body links actually sit — under AA for
     link text. #11803F is 4.64:1 on --pg / 5.02:1 on white. #17934F survives only as a badge
     colour (--vert-fmcg, --cat1).
     See docs/superpowers/specs/2026-09-02-duopharma-v3-navy-palette-design.md (which amends
     the 2026-08-29 spec). */
  --pg:#F4F6FA; --sf:#FFFFFF; --tint:#EEF2F8; --ink:#12264C; --sec:#3A4A6B; --mut:#5B6A85;
  --faint:#7E8BA3; --forest:#11803F; --forest2:#0E7434; --amber:#D9821E; --pos:#11803F;
  --brand:#C8102E; --brand2:#A60D26;
  --dang:#B3372B; --info:#2D62A8; --line:#E2E7F0; --line2:#ECF0F7; --line3:#E2E7F0; --btnln:#E2E7F0;

  /* Sidebar is the export's deep navy — a shade below --ink, not a tint of any accent.
     --sidebar-dim is the tagline under the logo (~5.1:1 on the sidebar). */
  --sidebar:#102547; --sidebar-idle:#BCC9E0; --sidebar-on:#F4F6FA; --sidebar-dim:#8296BC;

  /* Vertical accents — CONSUMED as of 2026-08-15 by TdVertical.For in Pages/TradeDesk.cs, which
     badges the item vertical on Cart and Catalogue/Detail. Project B made verticals real data
     (ECom_ItemCatalogue.Vertical). They were reserved-but-unused before that, and were once
     borrowed by .td-cat-bg-E/B/P for the telco product-type accents; that hack went on
     2026-08-12 when the catalogue moved to real item categories with their own palette below.
     ⚠️ TdVertical maps these by NAME — "TELCO"/"PHARMA"/"FMCG" — so renaming a token silently
     drops that vertical back to the neutral --mut fallback with nothing failing. */
  --vert-fmcg:#17934F; --vert-pharma:#C8102E; --vert-telco:#B26A1F;
  --tint-fmcg:#EAF6EE; --tint-pharma:#FBEAED; --tint-telco:#F5EEE1;
  --incart:#F1F8F2;

  /* Catalogue category accents (accent + its tint background).
     ⚠️ These are NOT named per category on purpose. Categories are DATA — ItemMaster.Category,
     named by ItemSubCategoryMaster Type=1 — so the set differs per tenant and changes without a
     deploy. TdCat.AccentFor() picks a pair deterministically FROM THE CATEGORY CODE, so a given
     category keeps the same colour across the grid and across page loads (the catalogue was
     grid-only as of 2026-08-13; this used to say "the three catalogue views" from when ledger
     and board also rendered it).
     Assigning by position in the list would have re-coloured everything whenever a category was
     added or its last item sold out. Six pairs stay comfortably distinguishable; past six they
     repeat, which is cosmetic only. */
  --cat1:#17934F; --cat1-bg:#EAF6EE;
  --cat2:#2D62A8; --cat2-bg:#E8F0FA;
  --cat3:#B26A1F; --cat3-bg:#F5EEE1;
  --cat4:#7A3E9D; --cat4-bg:#F2EAF7;
  --cat5:#0E7C86; --cat5-bg:#E4F2F3;
  --cat6:#B3372B; --cat6-bg:#F8EAE8;

  /* --serif keeps its NAME (pages reference it inline) but holds Rubik now — headings render
     at weight 300 via the h1,h2,h3 rule below. --label is the Montserrat micro-label face;
     --mono stays IBM Plex for every figure (tabular-nums alignment is load-bearing). */
  --serif:'Rubik', -apple-system, Segoe UI, Roboto, sans-serif;
  --sans:'Rubik', -apple-system, Segoe UI, Roboto, sans-serif;
  --mono:'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  --label:'Montserrat', -apple-system, Segoe UI, Roboto, sans-serif;
}
* { box-sizing:border-box; }
html, body { margin:0; padding:0; }
body { background:var(--pg); color:var(--ink); font-family:var(--sans); font-size:14px; line-height:1.5; -webkit-font-smoothing:antialiased; }
a { color:var(--forest); text-decoration:none; } a:hover { color:var(--brand); }
h1,h2,h3 { font-family:var(--serif); font-weight:300; letter-spacing:-.01em; margin:0; }
.mono { font-family:var(--mono); font-variant-numeric:tabular-nums; }
.tdlabel { font-family:var(--label); font-weight:600; font-size:9px; letter-spacing:.18em; text-transform:uppercase; color:var(--mut); }
::selection { background:var(--brand); color:var(--pg); }
/* Keyboard focus. Sharp-cornered like everything else; amber on the dark sidebar where
   forest would disappear. Never remove this with outline:none on an individual field. */
:focus-visible { outline:2px solid var(--forest); outline-offset:1px; }
.td-side :focus-visible { outline-color:var(--amber); }

/* Layout shell */
.td-shell { display:flex; align-items:stretch; min-height:100vh; background:var(--pg); }
.td-side { width:238px; flex:none; background:var(--sidebar); color:var(--sidebar-on); display:flex; flex-direction:column; position:sticky; top:0; height:100vh; z-index:40; }
.td-brand { display:flex; align-items:center; gap:11px; padding:22px; border-bottom:1px solid rgba(244,246,250,.1); }
.td-brand__mark { width:32px; height:32px; flex:none; background:var(--brand); border:1px solid rgba(244,246,250,.22); display:flex; align-items:center; justify-content:center; font-family:var(--mono); font-size:12px; font-weight:600; border-radius:8px; }
/* The hexagon brand mark (Pages/Shared/_BrandMark.cshtml). .td-brand__mark stays for the staff "ST" square. */
.td-brand__hex { flex:none; display:block; }
.td-side-nav { display:flex; flex-direction:column; padding:14px 12px; gap:1px; }
.td-side-nav a { position:relative; display:flex; align-items:center; justify-content:space-between; padding:11px 12px; text-decoration:none; font-family:var(--label); font-weight:600; font-size:11px; letter-spacing:.15em; text-transform:uppercase; color:var(--sidebar-idle); border-radius:8px; }
.td-side-nav a:hover { background:rgba(244,246,250,.07); color:var(--sidebar-on); }
.td-side-nav a.active { color:var(--sidebar-on); }
.td-side-nav a.active::before { content:""; position:absolute; left:0; top:9px; bottom:9px; width:2px; background:var(--brand); }
.td-orderpad { display:block; width:calc(100% - 32px); margin:auto 16px 16px; text-align:left; background:var(--brand); border:0; padding:14px 15px; color:#FFFFFF; text-decoration:none; border-radius:8px; }
.td-orderpad:hover { background:var(--brand2); color:#FFFFFF; }
/* .td-header lives inside .td-main now (rendered by a second <vc:chrome section="header"> call
   there, above .td-page -- see _Layout.cshtml), the same spot the original static placeholder
   occupied, so it stays a normal flex-column child: position:sticky, not fixed. A fixed header
   was tried and reverted -- it hardcoded left:238px against .td-side's width with no shared
   token, and a later 1080px scroll rule on .td-shell would have scrolled the sidebar out from
   under it (overflow does not establish a containing block for position:fixed), detaching the
   two. z-index stays below .td-side's 40 so the sidebar wins on the rare occasion they'd overlap. */
.td-header { height:60px; flex:none; background:var(--sf); border-bottom:1px solid var(--line); display:flex; align-items:center; gap:22px; padding:0 30px; position:sticky; top:0; z-index:30; }
.td-main { flex:1; min-width:0; display:flex; flex-direction:column; }
.td-main > .td-page { flex:1; min-width:0; }
/* The handoff sets the signed-in app to a 1080px minimum with horizontal scroll below it.
   NOTE: .td-main alone is NOT scoped to the authenticated shell -- _Layout.cshtml renders the
   same .td-shell > .td-main > .td-page wrapper on every page, including Login, Register and
   Terms; only .td-side (the sidebar) is conditional on User.Identity.IsAuthenticated. Scoping
   the rule to plain ".td-main > .td-page" (as originally drafted) reproduces exactly the bug it
   was meant to prevent: confirmed by measurement, Register's fields rendered at x=208..871 at a
   375px viewport, off-screen. Scope on ".td-side + .td-main" instead -- the sidebar is .td-main's
   immediate preceding sibling only when authenticated -- so the rule can never reach Login,
   Register or Terms, which must stay responsive: applying it globally re-breaks the Register
   mobile layout fixed in 560271fe4, where 22 fields rendered 136px wide at 375px. */
.td-side + .td-main > .td-page { min-width:1080px; }
@media (max-width:1099px) { .td-shell:has(.td-side) { overflow-x:auto; } }

/* Header components: credit meter, cut-off countdown. (The search well that used to sit to their
   left was removed -- see the comment in Header.cshtml -- so no .td-search rule lives here any
   more; do not re-add one without also re-adding the markup and the storefront.js behaviour.) */
.td-meter { min-width:150px; }
.td-meter__bar { height:3px; background:var(--line); border-radius:2px; margin-top:6px; overflow:hidden; }
.td-meter__fill { height:100%; display:block; }
.td-cutoff { display:flex; align-items:center; gap:7px; white-space:nowrap; }
.td-cutoff__dot { width:7px; height:7px; border-radius:50%; background:var(--amber); flex:none; animation:adamsPulse 2.2s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { .td-cutoff__dot { animation:none; } }

.td-toast { background:var(--ink); color:var(--pg); padding:14px 18px; font-size:13px; display:flex; align-items:center; gap:12px; border-radius:10px; box-shadow:0 18px 44px rgba(14,31,61,.3); opacity:1; transition:opacity .3s; }
.td-toast::before { content:""; width:6px; height:6px; background:var(--brand); flex:none; }
.td-toast--out { opacity:0; }

@keyframes adamsPulse { 0%,100% { opacity:1; } 50% { opacity:.35; } }

/* Component library: .td-* */
.td-wrap { padding:34px 44px 70px; max-width:1340px; }
.td-h1 { font-family:var(--serif); font-size:42px; letter-spacing:-.015em; }
.td-h2 { font-family:var(--serif); font-size:26px; }
.td-sub { font-size:13px; color:var(--mut); margin-top:5px; }
.td-btn { display:inline-flex; align-items:center; gap:8px; background:transparent; border:1px solid var(--btnln); padding:11px 18px; cursor:pointer; font-size:13px; color:var(--ink); font-family:var(--sans); border-radius:8px; }
.td-btn:hover { border-color:var(--ink); background:var(--sf); }
.td-btn--dark { background:var(--ink); color:var(--pg); border-color:var(--ink); }
.td-btn--dark:hover { background:var(--forest); color:var(--pg); border-color:var(--forest); }
.td-btn--mono { font-family:var(--label); font-weight:600; font-size:10px; letter-spacing:.12em; text-transform:uppercase; padding:8px 14px; }
/* overflow:hidden is what makes the 12px radius actually clip. Without it a child that paints its
   own background to the panel edge — a .td-ledger-head as the FIRST child, which is tinted —
   squares the top corners off. That was live on Account/Index and Orders/Detail until 2026-08-14.
   Verified safe: nothing inside any .td-panel uses position:absolute or sticky, so there is
   nothing that needs to escape the box. */
.td-panel { border:1px solid var(--line); background:var(--sf); border-radius:12px; overflow:hidden; }
.td-grid1 { display:grid; gap:1px; background:var(--line); border:1px solid var(--line); border-radius:12px; overflow:hidden; }
.td-grid1 > * { background:var(--sf); }
.td-grid1 > *:hover { background:#fff; }
.td-ledger-row { display:grid; align-items:center; padding:0 20px; border-bottom:1px solid var(--line2); background:var(--sf); }
.td-ledger-row:hover { background:#fff; }
.td-ledger-head { background:var(--tint); border-bottom:1px solid var(--line); }
.td-ledger-head .tdlabel { padding:11px 0; }
.td-qty { display:inline-flex; align-items:center; border:1px solid var(--ink); background:var(--sf); border-radius:8px; }
.td-qty button { width:34px; height:36px; background:transparent; border:0; cursor:pointer; font-size:16px; color:var(--ink); }
.td-qty button:hover { background:var(--tint); }
.td-qty .v { min-width:56px; text-align:center; font-family:var(--mono); font-size:14px; font-weight:500; font-variant-numeric:tabular-nums; }
.td-qty input { width:56px; border:0; text-align:center; background:transparent; font-family:var(--mono); font-size:14px; font-variant-numeric:tabular-nums; }
.td-status { display:inline-block; white-space:nowrap; font-family:var(--mono); font-size:9px; letter-spacing:.09em; text-transform:uppercase; padding:3px 7px; border:1px solid currentColor; line-height:1.35; border-radius:6px; }
/* Sentence-length status text. `.td-status` is a BADGE — 9px, uppercase, wide-tracked mono — which
   stops being readable the moment the content is a sentence rather than a word, and CLAUDE.md section 5 is
   explicit that UPPERCASE belongs to the `.tdlabel` micro-labels only. This modifier hands those
   blocks back the body face and sentence case. It carries `white-space:normal` itself, because the
   badge default `nowrap` clips a sentence inside `.td-panel`'s `overflow:hidden`. Used by the
   password-reset pages, where the message IS the entire visible output of the flow. */
.td-status--prose { font-family:var(--sans); font-size:13px; line-height:1.5; letter-spacing:normal; text-transform:none; white-space:normal; }
.td-status--pos { color:var(--pos); } .td-status--amber { color:var(--amber); } .td-status--info { color:var(--info); } .td-status--mut { color:var(--mut); } .td-status--dang { color:var(--dang); }
/* Aliases, not new colours -- `--ok` reads the same as `--pos`, `--warn` reads the same as
   `--amber`. `--warn` backs the licence-entitlement badges (TdLicence.For, project M / Task 8):
   an expired item on the catalogue tile/detail page/cart line, and the header's before-it-expires
   notice (ChromeViewComponent.BuildLicenceExpiryNotice). */
.td-status--ok { color:var(--pos); } .td-status--warn { color:var(--amber); }
.td-empty { margin-top:56px; text-align:center; padding:70px 20px; border:1px dashed var(--btnln); border-radius:12px; }
.td-empty h2 { font-family:var(--serif); font-size:30px; color:var(--mut); }
/* ⚠️ No border-radius here, deliberately. .td-metric is only ever a cell inside a .td-grid1
   (Desk and Account), i.e. a gap:1px grid over a --line background. A rounded child in that
   construction shows the grey container background through all four of its corners — 16 visible
   wedges across the four cells — and the container's own 12px radius is what should be rounding
   the group. Fixed 2026-08-14; it had this radius and rendered exactly that way. */
.td-metric { background:var(--sf); padding:20px; }
.td-metric .tdlabel { color:var(--mut); }
.td-metric .v { font-family:var(--mono); font-size:24px; font-weight:500; letter-spacing:-.02em; font-variant-numeric:tabular-nums; margin-top:9px; }
.td-price { font-family:var(--mono); font-weight:500; font-variant-numeric:tabular-nums; }
.td-summary { position:sticky; top:84px; border:1px solid var(--line); background:var(--sf); border-radius:12px; }
.td-summary__row { display:flex; align-items:baseline; justify-content:space-between; padding:6px 0; font-size:13px; }
.td-tile { background:var(--sf); display:flex; flex-direction:column; border-radius:12px; }
.td-tile__cap { padding:18px; border-bottom:1px solid var(--line); min-height:120px; display:flex; flex-direction:column; justify-content:space-between; }
.td-tile__mark { font-family:var(--serif); font-size:34px; line-height:1; }
/* Catalogue: filters + one rendered view */
.td-cat-layout { display:flex; align-items:flex-start; gap:28px; }
/* .td-cat-rail is the left column -- the facet panel plus, below it, the bulk-order paste panel.
   .td-cat-layout is a row-flex, so two stacked .td-panels need this shared flex-column ancestor to
   read as one column at all; without it a second panel would sit beside .td-cat-body as a third
   row item instead of stacking under the first. */
.td-cat-rail { width:220px; flex:none; display:flex; flex-direction:column; gap:22px; }
.td-cat-body { flex:1; min-width:0; }
/* auto-fill, not a hardcoded column count, so the grid reflows instead of overflowing */
.td-grid-tiles { display:grid; grid-template-columns:repeat(auto-fill,minmax(210px,1fr)); gap:1px; background:var(--line); border:1px solid var(--line); margin-top:20px; border-radius:12px; overflow:hidden; }

/* The catalogue's ledger and board views were removed on 2026-08-13 (grid only), taking
   .td-ledger-scroll, .td-ledger-table, .td-ledger--cat, .td-board-items, .td-board-group,
   .td-board-item, .td-viewtabs and .td-viewtab with them.
   ⚠️ .td-ledger-row and .td-ledger-head above are NOT catalogue-specific and MUST stay —
   Account, Orders/Index, Orders/Detail and Promotions all lay out their tables with them. */

.td-facet { display:flex; align-items:center; justify-content:space-between; width:100%; padding:8px 10px; background:transparent; border:0; border-left:2px solid transparent; cursor:pointer; text-align:left; font-size:13px; color:var(--sec); font-family:var(--sans); border-radius:8px; }
.td-facet[aria-pressed="true"] { background:var(--tint); color:var(--ink); }
/* Facet groups are separated by a rule drawn from adjacency, not by a fixed divider element:
   the groups are DATA (a tenant whose items share one vertical sees only Category), so a
   hand-placed separator would be drawn above a group that is not there. */
.td-facet-group + .td-facet-group { border-top:1px solid var(--line); padding-top:16px; }
/* A tile for an item already in the cart. --incart has sat unconsumed in :root since project A;
   this is its first use.

   ⚠️ The catalogue tint is driven by :has() off the ORDER CELL, not by a class on the tile. The
   htmx cart controls swap only `.td-ordercell` (hx-swap="outerHTML" on `closest .td-ordercell`),
   so a class on the tile would go stale the moment a customer stepped an item to zero -- and
   swapping the tile instead would resurrect tiles the catalogue filter has hidden with an inline
   display:none, because the filter hides rather than removes. Deriving the tint from the cell's
   own data-incart keeps the swap as small as it can be and self-consistent.
   :has() is already relied on above (.td-shell:has(.td-side)); a browser without it simply misses
   the tint, which is decoration -- the stepper itself still renders.
   ⚠️ There is deliberately no `.td-tile--incart` class here. Nothing emits it -- the tile itself
   never gets a class toggled, per the swap-boundary reasoning above -- so a version of this rule
   that also matched `.td-tile--incart` would read as a working fallback and never fire. */
.td-tile:has(.td-ordercell[data-incart="true"]) { background:var(--incart); }
/* The order cell is the swap target, so it has to be an element in its own right rather than the
   form it wraps -- the fragment returned after a cart change replaces this box whole. */
.td-ordercell { display:flex; align-items:center; }

/* Form fields. font-size:16px is deliberate and load-bearing: iOS Safari zooms the viewport
   on focus for any input below 16px, which on a long form means a zoom jolt per tap. */
.td-input { width:100%; border:1px solid var(--line3); background:#fff; padding:11px 13px; font-family:var(--sans); font-size:16px; border-radius:8px; }
.td-input--mono { font-family:var(--mono); }
/* Checkboxes carrying consent. 13px (the UA default) is under every touch-target minimum;
   the associated label is clickable too, so the real target is larger than the box. */
.td-check { width:20px; height:20px; accent-color:var(--forest); flex:none; }
/* Required-field marker. Amber, not --dang: red is this app's error colour, and a form at
   rest should not look like it already has nine errors. The marker is decoration only --
   assistive tech gets the requirement from the input's own `required` attribute, which is
   why the asterisk lives in ::after rather than in the label text. */
.td-req::after { content:" *"; color:var(--amber); font-weight:600; }

/* Field grouping. A bare <fieldset> carries a browser border, padding and -- the one that
   actually breaks things -- a min-width of min-content, which stops grid children from
   collapsing and would undo the responsive field layout. Reset all three. */
.td-group { border:0; padding:0; margin:0; min-width:0; }
.td-group > legend { padding:0; }

/* Step indicator for multi-step forms. Used by BOTH wizards — Register since it was built, and
   Checkout since 2026-08-11, when its hand-rolled inline-styled copy was migrated onto these
   classes. Layout and responsive behaviour only: the active-state colours are set inline by the
   wizard IIFE in storefront.js, so a colour rule here would be overridden by those inline styles
   and is deliberately absent. Adding one will silently do nothing — change the IIFE instead. */
.td-stepbar { display:flex; align-items:center; gap:0; flex-wrap:nowrap; overflow-x:auto; }
.td-stepbar__item { display:flex; align-items:center; gap:10px; padding-right:18px; }
.td-stepbar__item:last-child { padding-right:0; }
.td-stepbar__badge { width:22px; height:22px; flex:none; display:flex; align-items:center; justify-content:center; border:1px solid var(--btnln); font-family:var(--mono); font-size:10px; font-variant-numeric:tabular-nums; border-radius:6px; }
.td-stepbar__label { font-size:12.5px; white-space:nowrap; }
.td-stepbar__rule { width:26px; height:1px; background:var(--line3); flex:none; }
.td-stepbar__count { display:none; font-family:var(--mono); font-size:10px; letter-spacing:.12em; text-transform:uppercase; color:var(--mut); font-variant-numeric:tabular-nums; }

/* Sized for Register, the wider of the two wizards. Five labelled badges plus their connectors
   measure 578px intrinsically. Add the page's 32px outer padding, the panel's 56px padding and
   its 2px border and the bar needs about 668px of viewport before it fits without crowding the
   panel edge. Below that, drop the labels and rules and let the "Step N of 5" caption carry the
   same information in one line.

   Checkout's two badges fit comfortably and never needed this, but they inherit it, so below
   699px Checkout also collapses to badges plus "Step 1 of 2". Left that way on purpose: Checkout
   sits in the authenticated shell, which carries min-width:1080px and is therefore already
   scrolling horizontally at any width where this fires, and scoping the query away from it would
   mean editing a rule Register depends on and that has been visually verified. */
@media (max-width:699px) {
  .td-stepbar__label, .td-stepbar__rule { display:none; }
  .td-stepbar__item { padding-right:10px; }
  .td-stepbar__count { display:block; }
}

/* Back/Continue/Skip controls only work once the wizard script in storefront.js is running —
   they are type="button" and never submit by themselves, and on Register the Continue button is
   styled identically to the real submit button. Hidden by default so a no-JS visitor never sees
   a dead — or, worse, deceptive-looking — control; the wizard IIFE adds "js" to <html> as soon
   as it confirms it's driving the page, which reveals them via the rule below. Applied to the
   whole nav row on the four steps whose row is entirely Back/Continue/Skip; applied to just the
   Back button on Register's final step, since that row's other control is the real submit button
   and must stay visible and reachable without JS.

   ⚠️ Used by BOTH wizards since 2026-08-14. Checkout's two nav rows were the exception until
   then and rendered two dead buttons with JS off — measured, not assumed. Its real submit
   ("Place order") sits in the summary panel and correctly does not carry this class.

   ⚠️ A row using this MUST NOT also set `display` inline. Inline wins on specificity, so an
   inline `display:flex` silently defeats the `display:none` and the row stays visible with JS
   off — which is exactly the state this class exists to prevent, and it looks like the class is
   broken rather than misused. The `.js` rule below supplies both flex and gap. */
.td-wizard-nav { display:none; }
.js .td-wizard-nav { display:flex; gap:10px; }

@media (max-width:1100px) {
  .td-cat-layout { flex-direction:column; }
  .td-cat-rail { width:100%; }
  .td-cat-body { width:100%; align-self:stretch; }
}

/* The per-product-type accent classes (.td-cat-E/B/P, .td-cat-bg-E/B/P) were removed on
   2026-08-12. They keyed off the telco E/B/P codes, which no tenant on the FMCG base uses — every
   item came through as the neutral fallback. The catalogue now colours by real item category, and
   because categories are data there can be no class per value: the accent is applied inline from
   the --cat1..6 pairs at the top of this file, chosen by TdCat.AccentFor(). */

/* Sidebar blocks. The nav's active marker is a 2px bar in the brand red (--brand). */
.td-brand__eyebrow { font-family:var(--label); font-weight:600; font-size:9px; letter-spacing:.2em; text-transform:uppercase; color:var(--sidebar-dim); }
.td-side-shortcuts { display:flex; flex-direction:column; padding:4px 12px 0; gap:1px; }
.td-side-shortcuts a, .td-side-shortcuts button { display:flex; align-items:center; justify-content:space-between; width:100%; padding:9px 12px; background:transparent; border:0; cursor:pointer; text-align:left; font-family:var(--sans); font-size:12px; color:var(--sidebar-idle); text-decoration:none; border-radius:8px; }
.td-side-shortcuts a:hover, .td-side-shortcuts button:hover { background:rgba(244,246,250,.07); color:var(--sidebar-on); }
.td-side-count { font-family:var(--mono); font-size:10px; font-variant-numeric:tabular-nums; color:var(--sidebar-idle); }

/* /Help/{slug} article body. h1,h2,h3 inherit margin:0 from the global rule above, which reads
   fine for a single page heading but leaves FaqMarkdownRenderer's inline headings flush against
   the surrounding text -- these give them breathing room and reset the first block flush to the
   top of the body so there is no dead gap under the page's own <h1>. */
.td-article-body h2 { font-size:21px; margin-top:30px; }
.td-article-body h3 { font-size:16px; margin-top:22px; }
.td-article-body > :first-child { margin-top:0; }
.td-article-body p { margin:14px 0 0; }
.td-article-body ul { margin:14px 0 0; padding-left:22px; }
.td-article-body li { margin-top:6px; }

/* Product detail gallery (project K). Main image on the item's category tint, a thumbnail
   strip below, arrows over the main image. --gallery-tint is set inline by _ImageGallery. */
.td-gallery { margin-bottom:26px; }
.td-gallery__main { position:relative; background:var(--gallery-tint, var(--tint)); border-radius:12px; overflow:hidden; height:380px; display:flex; align-items:center; justify-content:center; }
.td-gallery__main img { max-width:100%; max-height:100%; object-fit:contain; display:block; }
.td-gallery__nav { position:absolute; top:50%; transform:translateY(-50%); width:36px; height:36px; border-radius:8px; background:var(--sf); border:1px solid var(--line); color:var(--ink); display:flex; align-items:center; justify-content:center; font-size:22px; line-height:1; text-decoration:none; }
.td-gallery__nav--prev { left:12px; }
.td-gallery__nav--next { right:12px; }
.td-gallery__caption { font-size:12.5px; color:var(--mut); margin-top:8px; min-height:18px; }
.td-gallery__thumbs { display:flex; gap:8px; margin-top:10px; flex-wrap:wrap; }
.td-gallery__thumb { width:64px; height:64px; border-radius:8px; border:2px solid transparent; background:var(--gallery-tint, var(--tint)); overflow:hidden; display:flex; align-items:center; justify-content:center; }
.td-gallery__thumb img { max-width:100%; max-height:100%; object-fit:contain; display:block; }
.td-gallery__thumb.is-active { border-color:var(--forest); }

/* Catalogue tile image (project K). The cap keeps its label row; the image replaces the
   initial glyph. With any image on the page every cap is the same height. */
.td-tile__img { width:100%; height:130px; object-fit:contain; display:block; margin-top:10px; }
.td-grid-tiles--img .td-tile__cap { height:190px; min-height:0; }
