/* ============================================================
   Legion Comps — Site layout patterns
   Extracted from Homepage prototype inline styles.
   Requires design-system token CSS to be loaded first.
   ============================================================ */

/* ---- Promo bar -------------------------------------------- */
.lc-promo {
  background: var(--accent-gold);
  color: var(--text-on-gold);
}
.lc-promo-inner {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 4px var(--gutter);
  display: flex;
  justify-content: center;
  gap: 0 28px;
  flex-wrap: wrap;
}
.lc-promo-item {
  display: inline-flex;
  align-items: center;
  /* 24px is the WCAG 2.2 AA (2.5.8) floor for a target; the bar's padding was
     trimmed by the same amount so one row is about as tall as it was. */
  min-height: 24px;
  gap: 8px;
  font-size: 12px;
  font-weight: 600;
}

/* ---- Navigation -------------------------------------------- */
.lc-nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: color-mix(in srgb, var(--surface-page) 90%, transparent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border-soft);
  transition: box-shadow var(--dur-base) var(--ease-out);
}
.lc-nav-inner {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 var(--gutter);
  height: 64px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.lc-navlink {
  padding: 0.5rem 0.85rem;
  border-radius: var(--radius-sm);
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--text-display);
  text-decoration: none;
  transition: background var(--dur-fast), color var(--dur-fast);
  white-space: nowrap;
}
.lc-navlink:hover { background: var(--surface-sunken); }
.lc-navlink-active { color: var(--text-display); font-weight: 600; }
.lc-navlink-gold { color: var(--accent-gold-ink); }
.lc-navlink-gold:hover { color: var(--accent-gold-ink); background: var(--accent-gold-tint); }
.lc-navlink-secondary { color: var(--text-secondary); }
.lc-basket-btn {
  position: relative;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-sm);
  border: none;
  background: none;
  cursor: pointer;
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background var(--dur-fast);
}
.lc-basket-btn:hover { background: var(--surface-sunken); }
/* .lc-cart-count is the shop cart's badge: a class of its own, because competition.html
   rewrites every .lc-basket-count when a competition is added to the basket. */
.lc-basket-count,
.lc-cart-count {
  position: absolute;
  top: 4px;
  right: 4px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--accent-action);
  color: var(--text-on-action);
  font-size: 10px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}
.lc-mobile-menu {
  background: var(--surface-card);
  border-top: 1px solid var(--border-soft);
}

/* ---- Hero (Split spotlight) -------------------------------- */
.lc-hero {
  position: relative;
  background: var(--surface-dark);
  color: var(--text-on-dark-body);
  overflow: hidden;
}
/* Blueprint field lifted from the social cover art: a 64px orthogonal grid
   with 45° hatching laid over it. The diagonal period is the *perpendicular*
   spacing, so 64 / √2 = 45.25px is what makes the hatching pass through the
   grid intersections rather than drifting against them — keep the two numbers
   locked together if either changes. Each layer needs its own background-size
   entry; tiling the repeating gradient into 64px boxes would chop it into
   dashes because 64 is not a multiple of 45.25. */
.lc-hero-grid {
  position: absolute;
  inset: 0;
  opacity: 0.5;
  background-image:
    linear-gradient(color-mix(in srgb, var(--accent-gold) 4%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in srgb, var(--accent-gold) 4%, transparent) 1px, transparent 1px),
    repeating-linear-gradient(135deg, color-mix(in srgb, var(--accent-gold) 6%, transparent) 0 1px, transparent 1px 45.25px);
  background-size: 64px 64px, 64px 64px, auto;
  pointer-events: none;
}
/* Oversized circle outlines, also from the cover. .lc-hero clips them, and a
   1px border stays 1px at any radius. Sized in vw rather than % because the
   hero's height is content-driven — a %-sized ring would resize with the
   spotlight card beside the copy. */
.lc-hero-ring {
  position: absolute;
  width: clamp(520px, 46vw, 900px);
  aspect-ratio: 1;
  border: 1px solid color-mix(in srgb, var(--accent-gold) 7%, transparent);
  border-radius: 50%;
  pointer-events: none;
}
.lc-hero-ring-1 { top: -230px; left: -170px; }
.lc-hero-ring-2 {
  width: clamp(760px, 72vw, 1400px);
  right: -300px;
  bottom: -430px;
  border-color: color-mix(in srgb, var(--accent-gold) 5.5%, transparent);
}
/* Two large arcs plus 45px hatching is busy on a phone; drop the rings at the
   same breakpoint the how-it-works warrior uses. */
@media (max-width: 1024px) {
  .lc-hero-ring { display: none; }
}
.lc-hero-glow {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 80% 50% at 50% 0%, color-mix(in srgb, var(--accent-action) 15%, transparent) 0%, transparent 65%);
  pointer-events: none;
}
/* Lossless WebP silhouette with a real alpha channel, so it composites
   over any section background without the mix-blend-mode: multiply trick
   the old white-backed JPEG needed. The image lives here rather than inline
   so a theme can swap it (themes/pixel.css does). */
.lc-howitworks-warrior {
  position: absolute;
  left: -12px;
  bottom: 0;
  width: clamp(150px, 15vw, 240px);
  aspect-ratio: 297 / 307;
  background-image: url("../images/warrior.webp");
  background-repeat: no-repeat;
  background-position: bottom left;
  background-size: contain;
  opacity: 0.85;
  mask-image: linear-gradient(to right, black 55%, transparent 100%);
  -webkit-mask-image: linear-gradient(to right, black 55%, transparent 100%);
  pointer-events: none;
}
@media (max-width: 1024px) {
  .lc-howitworks-warrior { display: none; }
}
.lc-hero-inner {
  position: relative;
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 3rem var(--gutter) 3.25rem;
}
/* Split spotlight (docs/New Design/Homepage.dc.html, HERO B): brand copy beside
   one featured quest. The 1.1/0.9 ratio is the canvas's — the copy column holds a
   460px paragraph, the card column a 16:9 image, and an even split leaves the
   headline short of its measure.

   Stacking at 900px rather than a token breakpoint: below that the card column is
   under ~380px, at which point the 30px price and the two-up progress row start
   colliding. `align-items: center` only makes sense side by side, so it goes with
   the columns. The spotlight's `sizes` attribute is keyed to this breakpoint and
   this ratio — change one and the card fetches the wrong rendition. */
.lc-hero-split {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: 48px;
  align-items: center;
}
/* A grid item's min-width is auto, so it refuses to shrink below its widest
   unbreakable child. The mini rail's titles are `white-space: nowrap`, which made
   the copy column as wide as the longest quest title — on a phone that pushed the
   whole hero past the viewport instead of the title ellipsising. */
.lc-hero-split > * {
  min-width: 0;
}
@media (max-width: 900px) {
  .lc-hero-split {
    grid-template-columns: 1fr;
    gap: 32px;
  }
  /* Capped, and left-aligned under the copy rather than centred. Stacked, the card
     would otherwise take the container's full width, and a 16:9 image 1200px wide
     is a 675px-tall block of prize photo that pushes everything below it off a
     tablet screen. 560px keeps it the size it is beside the copy. */
  .lc-spotlight {
    max-width: 560px;
  }
}
/* The card is .lc-compcard-dark, which the trio used inside a 3-column grid that
   capped its width. On its own in a hero column there is no such cap, so the hover
   lift is dropped: a 540px card rising 4px reads as the page twitching, not as a
   card responding. */
.lc-spotlight:hover { transform: none; }

/* ---- Hero mini quest rail ---------------------------------- */
/* The upsell row under the hero copy. Sized against the copy column rather than
   the card column: the paragraph above it is capped at 460px, so the rail is too,
   and the two line up down their left edge. */
.lc-minicard {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 12px 8px 8px;
  border: 1px solid var(--border-dark);
  border-radius: var(--radius-md);
  background: var(--surface-dark-card);
  text-decoration: none;
  color: inherit;
  transition:
    border-color var(--dur-base) var(--ease-out),
    background var(--dur-base) var(--ease-out);
}
.lc-minicard:hover { border-color: var(--accent-gold-strong); background: var(--surface-dark-raised); }
.lc-minicard:focus-visible {
  outline: none;
  border-color: var(--accent-gold);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-gold) 45%, transparent);
}
/* Fixed, not aspect-ratio: these sit in a flex row where a percentage width would
   let a long title squeeze the thumbnail down to nothing. */
.lc-minicard-thumb {
  position: relative;
  flex: none;
  width: 76px;
  height: 48px;
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.lc-minicard-title {
  font-size: var(--minicard-title-size, 13.5px);
  font-weight: var(--minicard-title-weight, 700);
  line-height: 1.25;
  color: var(--text-on-dark);
  /* One line. Two competing titles at two lines each is the trio's mistake at a
     smaller size — the rail is a glance, not a read. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.lc-minicard-meta {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 3px;
  font-size: var(--text-caption);
  color: var(--text-on-dark-muted);
}
.lc-minicard-hook {
  flex: none;
  font-size: var(--text-kicker);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: var(--radius-pill);
  white-space: nowrap;
}
.lc-minicard-hook-urgent  { background: color-mix(in srgb, var(--accent-action) 18%, transparent); color: var(--accent-action-on-dark); }
.lc-minicard-hook-instant { background: var(--status-arcane-dark-tint); color: var(--status-arcane-on-dark-hi); }
.lc-minicard-hook-quiet   { background: var(--surface-dark-tint); color: var(--text-on-dark-muted); }
/* Scoped through .lc-minicard for the extra specificity: the arrow is a Font
   Awesome <i>, and .fa-solid sets its own display at the same weight as a bare
   .lc-minicard-arrow but later in the cascade, so the unscoped rule lost. */
.lc-minicard .lc-minicard-arrow {
  flex: none;
  font-size: 11px;
  color: var(--text-on-dark-muted);
  transition: transform var(--dur-base) var(--ease-out), color var(--dur-base);
}
.lc-minicard:hover .lc-minicard-arrow { color: var(--accent-gold-on-dark); transform: translateX(2px); }
/* Under 420px something has to give, and it is not the hook: "Almost gone" is the
   reason the row exists. The arrow is decorative — the whole row is the link — so
   that goes instead, and the title ellipsises into whatever is left. */
@media (max-width: 420px) {
  .lc-minicard .lc-minicard-arrow { display: none; }
  /* And the thumbnail gives up a little so the title is not ellipsised to two
     words — at 390px it was showing "Knight House…". */
  .lc-minicard { gap: 10px; }
  .lc-minicard-thumb { width: 60px; height: 40px; }
}

/* ---- Eyebrow live dot ------------------------------------- */
.lc-live-dot {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent-action-on-dark);
  animation: lc-dot-pulse 1.5s ease-in-out infinite;
}
@keyframes lc-dot-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}

/* ---- Competition card (light grid) ------------------------- */
.lc-compcard {
  background: var(--surface-card);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card), var(--bevel);
  overflow: hidden;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  transition:
    transform var(--dur-base) var(--ease-out),
    box-shadow var(--dur-base) var(--ease-out),
    border-color var(--dur-base) var(--ease-out);
}
.lc-compcard:hover { transform: translateY(-3px); box-shadow: var(--shadow-card-hover), var(--bevel); border-color: var(--border-strong); }

/* ---- Competition card (dark — hero spotlight) -------------- */
.lc-compcard-dark {
  position: relative;
  background: var(--surface-dark-card);
  border: 1px solid var(--border-dark);
  border-radius: var(--radius-card);
  overflow: hidden;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  box-shadow: var(--shadow-dark);
  transition:
    transform var(--dur-base) var(--ease-out),
    border-color var(--dur-base) var(--ease-out);
}
.lc-compcard-dark:hover { transform: translateY(-4px); border-color: var(--accent-gold-strong); }
/* The whole card is an <a> — strip link styling, keep the card's own colours. */
a.lc-compcard-dark { text-decoration: none; color: inherit; }
a.lc-compcard-dark:focus-visible {
  outline: none;
  border-color: var(--accent-gold);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-gold) 45%, transparent);
}

/* ---- Prize image placeholder ------------------------------- */
.lc-thumb {
  position: relative;
  background: radial-gradient(120% 120% at 30% 20%, var(--surface-dark-card) 0%, var(--surface-dark) 60%, var(--surface-dark-deep) 100%);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.lc-thumb-grid {
  position: absolute;
  inset: 0;
  opacity: 0.5;
  background-image:
    linear-gradient(color-mix(in srgb, var(--accent-gold) 5%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in srgb, var(--accent-gold) 5%, transparent) 1px, transparent 1px);
  background-size: 40px 40px;
  pointer-events: none;
}

/* ---- Stat bar (dark) --------------------------------------- */
.lc-statbar {
  background: var(--surface-dark-raised);
  border-top: 1px solid var(--border-dark);
  border-bottom: 1px solid var(--border-dark);
}
.lc-statbar-inner {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 20px var(--gutter);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}
.lc-statbar-item {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
}
.lc-statbar-item > i { font-size: 38px; }
.lc-statbar-item + .lc-statbar-item { border-left: 1px solid var(--border-dark); }
.lc-statbar-num {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 28px;
  color: var(--text-on-dark);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.lc-statbar-label {
  font-size: var(--statbar-label-size, 10.5px);
  letter-spacing: var(--statbar-label-tracking, 0.18em);
  text-transform: uppercase;
  color: var(--text-on-dark-muted);
  margin-top: 5px;
}

/* ---- Winners ticker --------------------------------------- */
.lc-ticker-wrap {
  background: var(--surface-dark-deep);
  overflow: hidden;
  border-bottom: 1px solid var(--border-dark-soft);
  position: relative;
}
.lc-ticker-fade-l {
  position: absolute; left: 0; top: 0; bottom: 0; width: 100px;
  background: linear-gradient(90deg, var(--surface-dark-deep), transparent);
  z-index: 2; pointer-events: none;
}
.lc-ticker-fade-r {
  position: absolute; right: 0; top: 0; bottom: 0; width: 100px;
  background: linear-gradient(270deg, var(--surface-dark-deep), transparent);
  z-index: 2; pointer-events: none;
}
.lc-ticker {
  display: flex;
  align-items: center;
  padding: 11px 0;
  width: max-content;
  animation: lc-ticker 38s linear infinite;
}
.lc-ticker-item {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 0 22px;
  border-right: 1px solid var(--border-dark-soft);
  white-space: nowrap;
}
.lc-ticker-victor {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px;
  border-radius: var(--radius-sm);
  background: var(--accent-gold);
  color: var(--text-on-gold);
  font-size: 9.5px;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
@keyframes lc-ticker { from { transform: translateX(0); } to { transform: translateX(-50%); } }
/* Firefox only runs a transform animation on the compositor when the moving
   element is about viewport-sized; the ticker is several viewports wide, so
   it repainted on the main thread every frame. base.html measures the ticker
   and moves each item by the same distance instead (items are small enough);
   without JS the whole strip still moves as above. */
.lc-ticker.lc-ticker-split { animation-name: none; }
.lc-ticker-split .lc-ticker-item {
  animation: lc-ticker-item 38s linear infinite;
  animation-duration: inherit;
}
@keyframes lc-ticker-item { from { transform: translateX(0); } to { transform: translateX(calc(-1 * var(--lc-ticker-shift))); } }
/* The ticker and the hero (with any seasonal skin's scenery) stop animating
   while they're scrolled out of view; base.html toggles the class. */
.lc-offscreen,
.lc-offscreen *,
.lc-offscreen *::before,
.lc-offscreen *::after { animation-play-state: paused !important; }

/* ---- Section container ------------------------------------ */
.lc-section {
  padding: var(--section-y) 0;
}
.lc-section-inner {
  max-width: var(--container-max);
  margin: 0 auto;
  padding-inline: var(--gutter);
}
/* Canonical page container — aligns content edges with nav, footer & heroes */
.lc-container {
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.lc-section-narrow {
  max-width: 920px;
  margin: 0 auto;
  padding-inline: var(--gutter);
}

/* ---- Filter bar ------------------------------------------- */
.lc-filterbar {
  background: var(--surface-card);
  border-bottom: 1px solid var(--border-soft);
  position: sticky;
  top: 64px;
  z-index: 20;
}
.lc-filterbar-inner {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 12px var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.lc-chip {
  padding: 8px 16px;
  border-radius: var(--radius-chip);
  cursor: pointer;
  font-family: var(--font-label);
  font-size: var(--chip-size, 13px);
  font-weight: var(--chip-weight, 500);
  text-transform: var(--chip-case, inherit);
  letter-spacing: var(--chip-tracking, inherit);
  background: var(--surface-card);
  color: var(--text-secondary);
  border: 1px solid var(--border-strong);
  transition: border-color var(--dur-fast), color var(--dur-fast), background var(--dur-fast);
}
.lc-chip:hover { border-color: var(--accent-gold-strong); }
.lc-chip-active {
  background: var(--surface-dark);
  color: var(--text-on-dark);
  border-color: var(--surface-dark);
  font-weight: 700;
}

/* ---- How it works steps ----------------------------------- */
.lc-step { text-align: center; }
.lc-step-icon {
  width: 56px;
  height: 56px;
  margin: 0 auto 16px;
  border-radius: var(--radius-card);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface-card);
  border: 1px solid var(--border-soft);
  box-shadow: var(--shadow-card), var(--bevel);
}

/* ---- Trust pillars ---------------------------------------- */
.lc-pillars {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
  max-width: var(--container-max);
  margin: 40px auto 0;
}
.lc-pillar {
  display: flex;
  gap: 16px;
  padding: 20px;
  border-radius: var(--radius-card);
  background: var(--surface-dark-card);
  border: 1px solid var(--border-dark);
}
.lc-pillar-icon {
  width: 46px;
  height: 46px;
  flex-shrink: 0;
  border-radius: var(--radius-md);
  display: flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--accent-gold) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent-gold) 30%, transparent);
  color: var(--accent-gold-on-dark);
}
.lc-pillar-body h3 { color: var(--text-on-dark); font-size: var(--text-base); margin-bottom: 6px; }
.lc-pillar-body p  { font-size: 13px; line-height: 1.55; color: var(--text-on-dark-muted); margin: 0; }

/* ---- Basket dropdown -------------------------------------- */
/* The panel is a column: a header that stays, an item list that scrolls, and a
   foot carrying the subtotal and Proceed to Checkout that never leaves.

   `max-height`, not `height` — a one-item basket stays a short panel, and only a
   full one grows to the cap and starts scrolling its list. 78vh rather than the
   old 384px because the list now shares the panel with a cross-sell row, and
   because the taller it is the less the list has to scroll at all.

   Not a viewport-height flyout, tempting as it is: this panel is a descendant of
   .lc-nav, which carries backdrop-filter and is therefore the containing block
   for any fixed descendant — the same trap documented on .lc-menu-backdrop
   below, where `position: fixed; inset: 0` resolved against the header. A true
   full-height flyout has to be teleported out of the header first. */
.lc-basket-dropdown {
  position: absolute;
  right: 0;
  top: calc(100% + 8px);
  width: 420px;
  max-height: 78vh;
  display: flex;
  flex-direction: column;
  background: var(--surface-card);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-modal);
  z-index: 50;
  overflow: hidden;
}
.lc-basket-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border-soft);
}

/* ---- Alert banners ---------------------------------------- */
.lc-alert {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 12px 16px;
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  font-size: 14px;
}
.lc-alert-error   { background: var(--status-danger-fill); border-color: var(--status-danger-border); color: var(--status-danger-ink); }
.lc-alert-success { background: var(--status-success-tint); border-color: var(--status-success-border); color: var(--status-success-ink); }
.lc-alert-warning { background: var(--status-warning-tint); border-color: var(--status-warning-border); color: var(--status-warning-ink); }
.lc-alert-info    { background: var(--surface-sunken); border-color: var(--border-strong); color: var(--text-body); }

/* The dismiss control is a bare "×" glyph, which measured 12x18 — the smallest
   tap target on any logged-in page. Sized unconditionally rather than under the
   640px tap-target block: an alert is dismissed on every viewport, and a
   landscape phone is 667px wide, which is exactly how the mobile-menu buttons
   slipped through that gate. Negative margin keeps the larger hit area from
   pushing the alert's text or padding around. */
.lc-alert-dismiss {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  margin: -12px -12px -12px 0;
  background: none;
  border: none;
  cursor: pointer;
  color: inherit;
}

/* Floating toast notifications: opaque fills + shadow so page content
   doesn't show through the tint backgrounds. Its z-index lives with the rest
   of the overlay layer scale at the end of this file, not here — set in CSS
   rather than via a Tailwind z-[..] utility because the compiled
   tailwind.css has no such class. */
.lc-toast-stack .lc-alert {
  box-shadow: var(--shadow-lg, 0 10px 30px rgba(0, 0, 0, 0.18));
}
.lc-toast-stack .lc-alert-error   { background: var(--status-danger-fill); }
.lc-toast-stack .lc-alert-success { background: linear-gradient(var(--status-success-tint), var(--status-success-tint)), var(--surface-card); }
.lc-toast-stack .lc-alert-warning { background: linear-gradient(var(--status-warning-tint), var(--status-warning-tint)), var(--surface-card); }
.lc-toast-stack .lc-alert-info    { background: var(--surface-page); }

/* ---- Footer ----------------------------------------------- */
.lc-footer {
  background: var(--surface-dark-deep);
  color: var(--text-on-dark-body);
  margin-top: auto;
}
.lc-footer-inner {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 4rem var(--gutter) 2.5rem;
  display: grid;
  grid-template-columns: 1.6fr 1fr 1fr;
  gap: 3rem;
}
.lc-footer-col-head {
  font-size: var(--label-size, 11px);
  font-weight: var(--label-weight, 600);
  letter-spacing: var(--label-tracking, 0.2em);
  text-transform: uppercase;
  color: var(--accent-gold-on-dark);
  margin: 0 0 1.1rem;
  font-family: var(--font-label);
}
.lc-footer-link {
  /* inline-flex + min-height rather than padding: it gives a 24px target
     (WCAG 2.2 AA 2.5.8) at every width, and the phone rules below raise it to 44. */
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  font-size: 13px;
  color: var(--text-on-dark-muted);
  text-decoration: none;
  transition: color var(--dur-fast);
}
.lc-footer-link:hover { color: var(--text-on-dark); }
.lc-footer-badge {
  opacity: 0.85;
  transition: opacity var(--dur-fast);
}
.lc-footer-badge:hover { opacity: 1; }
.lc-footer-badge img {
  display: block;
  width: 84px;
  height: 84px;
}
/* Trust marks sit on one row, vertically centred so the wide Trustpilot wordmark
   lines up with the middle of the square Rafflers badge rather than its top.
   Wraps on narrow screens instead of overflowing the column. */
.lc-footer-badges {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 1.25rem;
  margin-top: 1.25rem;
}
/* The Trustpilot mark is a wide wordmark, not a square badge, so it opts out of
   the 84px square above and keeps its own aspect ratio. */
.lc-footer-trustpilot img {
  width: 152px;
  height: auto;
}
/* "Built by Tagg Software" mark — its own flex item in the footer bottom bar.
   margin-left:auto parks it hard right on whichever wrapped row it lands on,
   which is the empty space beside the legal links. */
.lc-footer-builtby {
  display: inline-flex;
  align-items: center;
  margin-left: auto;
  min-height: 24px;
  opacity: 0.55;
  transition: opacity var(--dur-fast);
}
.lc-footer-builtby:hover { opacity: 1; }
.lc-footer-builtby img { display: block; height: 17px; width: auto; }
.lc-footer-bottom {
  border-top: 1px solid var(--border-dark-soft);
}
.lc-footer-bottom-inner {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 1.25rem var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  font-size: 12px;
  color: var(--text-on-dark-faint);
}

/* ---- Logo lockup ------------------------------------------ */
.lc-logo {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
}

/* ---- Page hero (dark — unified with competitions hero) ---- */
.lc-page-hero {
  position: relative;
  overflow: hidden;
  background: var(--surface-dark);
  color: var(--text-on-dark-body);
  padding: 2.5rem 0 3rem;
  border-bottom: 1px solid var(--border-soft);
}
/* Faint gold grid overlay */
.lc-page-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  opacity: 0.5;
  pointer-events: none;
  background-image:
    linear-gradient(color-mix(in srgb, var(--accent-gold) 4%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in srgb, var(--accent-gold) 4%, transparent) 1px, transparent 1px);
  background-size: 58px 58px;
}
/* Crimson radial glow */
.lc-page-hero::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(ellipse 70% 60% at 20% 0%, color-mix(in srgb, var(--accent-action) 13%, transparent) 0%, transparent 62%);
}
.lc-page-hero-inner {
  position: relative;
  z-index: 1;
  max-width: var(--container-max);
  margin: 0 auto;
  padding-inline: var(--gutter);
}
/* Breadcrumb (dark hero) */
.lc-breadcrumb {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--text-on-dark-muted);
  margin-bottom: 16px;
}
.lc-breadcrumb a { color: var(--text-on-dark-muted); text-decoration: none; transition: color var(--dur-fast, 0.15s); }
/* Breadcrumb links are 18px tall. Pad them to a 24px target (WCAG 2.2 AA 2.5.8) and
   cancel the padding with a negative margin, so the row does not move. */
.lc-breadcrumb a,
.comp-crumb a { display: inline-block; padding-block: 3px; margin-block: -3px; }
.lc-breadcrumb a:hover { color: var(--accent-gold-on-dark); }
.lc-breadcrumb i { font-size: 9px; color: var(--text-on-dark-faint); }
.lc-breadcrumb-current { color: var(--accent-gold-on-dark); }
/* Eyebrow on the dark hero: full eyebrow treatment + brighter gold */
.lc-page-hero .lc-eyebrow-gold {
  font-family: var(--font-label);
  font-size: var(--text-2xs, 0.6875rem);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--accent-gold-on-dark);
}
.lc-page-hero-title {
  margin: 0;
  font-weight: 800;
  font-size: clamp(2rem, 3.6vw, 3rem);
  line-height: 1.05;
  color: var(--text-on-dark);
}
.lc-page-hero-sub {
  margin: 0.75rem 0 0;
  max-width: 560px;
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--text-on-dark-body);
}

html { scroll-padding-top: 4.5rem; scroll-behavior: smooth; }

/* ---- Scrollbar ---------------------------------------------
   Custom ::-webkit-scrollbar rules force mobile WebKit off its
   auto-hiding overlay scrollbar onto a permanent classic one that
   reserves layout width, showing up as a phantom horizontal gutter.
   Scope to fine-pointer/hover-capable (desktop) so mobile keeps the
   overlay scrollbar. */
@media (hover: hover) and (pointer: fine) {
  ::-webkit-scrollbar { width: 5px; }
  ::-webkit-scrollbar-track { background: var(--surface-page); }
  ::-webkit-scrollbar-thumb { background: var(--accent-action); border-radius: 3px; }
  ::-webkit-scrollbar-thumb:hover { background: var(--accent-action-hover); }
}

/* ---- Reduced motion --------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .lc-ticker, .lc-ticker-split .lc-ticker-item { animation: none; }
  .lc-live-dot { animation: none; }
  .lc-compcard:hover { transform: none; }
  .lc-compcard-dark:hover { transform: none; }
}

/* ---- Competition card grid (responsive) -------------------- */
.lc-comp-grid { display: grid; grid-template-columns: repeat(4,1fr); gap: 18px; }
@media (max-width: 1024px) { .lc-comp-grid { grid-template-columns: repeat(2,1fr); } }
@media (max-width: 480px)  { .lc-comp-grid { grid-template-columns: 1fr; } }

.lc-comp-grid-3 { display: grid; grid-template-columns: repeat(3,1fr); gap: 18px; }
@media (max-width: 1024px) { .lc-comp-grid-3 { grid-template-columns: repeat(2,1fr); } }
@media (max-width: 640px)  { .lc-comp-grid-3 { grid-template-columns: 1fr; } }

/* ---- How It Works step grid (responsive) ------------------- */
.lc-steps-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 24px; max-width: 920px; margin: 0 auto; }
@media (max-width: 640px) { .lc-steps-grid { grid-template-columns: 1fr; gap: 20px; } }

/* ---- Responsive ------------------------------------------- */
@media (max-width: 1024px) {
  /* Hero: stack text + buttons vertically on tablet */
  .lc-hero-row { flex-direction: column; align-items: flex-start !important; }
  .lc-pillars { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 768px) {
  .lc-pillars { grid-template-columns: 1fr; }
  .lc-statbar-inner { grid-template-columns: repeat(3, 1fr); gap: 0; }
  .lc-footer-inner { grid-template-columns: 1fr; gap: 2rem; }
  .lc-hero-inner { padding: 2rem var(--gutter) 2.5rem; }
}
@media (max-width: 640px) {
  .lc-statbar-inner { grid-template-columns: repeat(3, 1fr); gap: 8px; }
  .lc-statbar-item + .lc-statbar-item { border-left: none; }
  .lc-footer-badge img { width: 68px; height: 68px; }
  .lc-footer-trustpilot img { width: 112px; height: auto; }
}

/* ---- Mobile (<=640px) refinements -------------------------- */
/* The promo bar wraps to one row per item at phone widths because the
   28px flex `gap` applies to the row axis too. Tighten both axes and the
   type so the three trust items settle into two compact lines instead of
   a ~290px tall block. */
/* Icon size moved out of the three inline `style="font-size: 11px"` attributes
   in fragments/promo_bar.html — inline styles beat media queries, so the icons
   would have stayed at 11px while the labels shrank below them, inverting the
   hierarchy. */
.lc-promo-item i { font-size: 11px; }
@media (max-width: 640px) {
  .lc-promo-inner { padding: 3px 12px; column-gap: 14px; row-gap: 0; }
  .lc-promo-item { font-size: 11px; gap: 6px; }
  .lc-promo-item i { font-size: 10px; }
}
@media (max-width: 380px) {
  .lc-promo-inner { column-gap: 10px; }
  .lc-promo-item { font-size: 10.5px; }
  .lc-promo-item i { font-size: 9.5px; }
}

/* Stat bar on phones: three side-by-side boxes rather than one stacked
   column. Each item becomes its own card (own background + rounded corners)
   since there's no room left for divider lines between three narrow
   columns; icon sits above the number/label so it fits a ~100px-wide box. */
@media (max-width: 640px) {
  .lc-statbar-inner { padding: 12px var(--gutter); }
  .lc-statbar-item {
    flex-direction: column;
    justify-content: center;
    gap: 6px;
    background: var(--surface-dark-card);
    border-radius: var(--radius-md);
    padding: 12px 6px;
  }
  .lc-statbar-item > i { font-size: 22px; }
  .lc-statbar-num { font-size: 17px; text-align: center; }
  .lc-statbar-label { letter-spacing: var(--statbar-label-tracking, 0.1em); text-align: center; }
}

/* Form controls must compute to at least 16px on mobile or iOS Safari zooms
   the viewport on focus and never zooms back out — the layout is then wider
   than the screen for the rest of the session. .lc-input is 0.9375rem
   (15px) in legion-components.css; this file loads after it, so equal
   specificity + later source order wins without !important. */
/* ---- The touch gate ---------------------------------------
   Everything below that sizes a *touch affordance* — 44px targets, the 16px
   iOS zoom threshold, the 12px functional-text floor — is gated on

       @media (max-width: 1023px), (hover: none) and (pointer: coarse)

   and not on the 640px breakpoint the layout rules use. A landscape phone is
   667px WIDE, so a max-width: 640px gate switches off in exactly the
   orientation the user is most likely to be typing in. Measured at 667x375
   before this changed: 2-13 controls per page under 44px, 18 more in every
   footer, and all six /complaints/new/ fields back at 14px — the iOS
   zoom-on-focus defect, reintroduced by rotating the phone.

   1023px covers every phone in both orientations (SE landscape 667, 14
   landscape 844, Pro Max landscape 932) and every iPad in portrait, and is
   measurable in a desktop harness. The pointer branch adds tablet landscape
   and any touchscreen wider than 1024px; it cannot be verified here (desktop
   Chrome reports pointer: fine) so it is only ever additive — no rule depends
   on it alone.

   Layout rules deliberately stay at 640px: .lc-section padding, --gutter, the
   grid collapses, the hero CTA stack, .lc-table-scroll cell padding. 40px
   section padding in a 900px desktop window would be wrong. Touch affordance
   and layout are different questions and want different breakpoints. */
@media (max-width: 1023px), (hover: none) and (pointer: coarse) {
  .lc-input,
  select.lc-input,
  textarea.lc-input { font-size: 16px; }
}

/* 48px of fixed gutter is 13% of a 360px viewport. Retargeting the token
   rather than .lc-container reaches inside the many inline styles that
   write `var(--gutter)` directly, so it needs no template edits. */
@media (max-width: 480px) {
  :root { --gutter: 1rem; }
}

/* Long unbreakable tokens — emails, voucher codes, tracking numbers, repo
   URLs — otherwise set a min-content floor that pushes their container, and
   then the page, wider than the viewport. `anywhere` (not `break-word`) is
   deliberate: only `anywhere` participates in intrinsic sizing, which is what
   lets the offending grid/flex track actually shrink. */
/* Scoped to phone widths deliberately: `anywhere` alters min-content sizing
   and `min-width: 0` removes the automatic minimum on every button and input
   as a flex/grid item. Both were derived from 360–412px analysis only, so they
   stay inside the media query rather than changing tablet/desktop sizing. */
@media (max-width: 640px) {
  .lc-container,
  .lc-section-inner,
  .lc-card { overflow-wrap: anywhere; }

  /* A grid/flex item's min-width defaults to `auto`, so a 1fr track cannot
     shrink below its content's min-content width — for a <select> that is its
     longest <option>, for a bare <input> its size=20 default. `width: 100%`
     does not help, as percentages resolve to auto during intrinsic sizing. */
  .lc-input, .lc-btn { min-width: 0; }
}

@media (max-width: 1023px), (hover: none) and (pointer: coarse) {
  .lc-btn { min-height: 44px; }
  .lc-input { min-height: 44px; }
  .lc-chip { display: inline-flex; align-items: center; min-height: 44px; padding: 8px 14px; }
  .lc-footer-inner .lc-footer-link { display: inline-block; padding: 10px 0; }
}

/* clamp(2rem, 3.6vw, 3rem) pins at its 32px floor below ~832px, so the page
   hero never scales on a phone and long titles eat the first viewport. */
@media (max-width: 480px) {
  .lc-page-hero { padding: 1.75rem 0 2rem; }
  .lc-page-hero-title { font-size: clamp(1.5rem, 7vw, 2rem); }
  .lc-page-hero-sub { font-size: 14px; }
  .lc-breadcrumb { flex-wrap: wrap; row-gap: 0.25rem; }
  .lc-ticker-fade-l, .lc-ticker-fade-r { width: 32px; }
  .lc-ticker-item { padding: 0 14px; }
  .lc-legal .p-8 { padding: 1.25rem; }
  .lc-alert > .lc-container { padding-inline: 0; }
  .lc-footer-bottom-inner > div { flex-wrap: wrap; }
}

/* Waitlist inputs are raw Tailwind with no lc-* hook; 0,1,1 beats .text-sm. */
@media (max-width: 1023px), (hover: none) and (pointer: coarse) {
  #waitlist-form-container input { font-size: 16px; }
}
#waitlist-form-container .font-mono { overflow-wrap: anywhere; }

/* payments/payment_failed.html sits on a dark surface but inherits the
   light-theme ink colours, so its <h1> rendered near-black on near-black. */
.lc-on-dark { color: var(--text-on-dark); }
.lc-link-gold { color: var(--accent-gold-on-dark); }

/* Checkout controls. The steppers are w-6 (24px) and the remove button wraps a
   bare 12px glyph — both far under a 44px target, on destructive actions that
   sit 12px apart mid-checkout. Attribute selectors beat the Tailwind utilities
   on specificity, and legion-site.css loads last, so no !important is needed. */
@media (max-width: 1023px), (hover: none) and (pointer: coarse) {
  form[data-lc-quantity] { gap: 0.25rem; }
  form[data-lc-quantity] button[type="submit"] {
    width: 44px; height: 44px; min-width: 44px; font-size: 1rem;
  }
  form[data-lc-quantity] > span { min-width: 2rem; }

  button[aria-label^="Remove "] {
    display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px;
    margin: -10px; /* expand the hit area without shifting the glyph */
  }

  #discount-section input[name="code"],
  #store-credit-section input[name="amount"] { font-size: 16px; min-height: 44px; }
}

/* Below 1024px the checkout sidebar collapses to full width, but the line-item
   title stays clamped at 160px — half the column wasted, competition names
   ellipsised at the moment the user confirms what they are paying for. */
@media (max-width: 1023px) {
  #checkout-summary span.truncate { max-width: none; }
}

#checkout-order-review p { overflow-wrap: anywhere; }

/* A nowrap badge beside a long title means neither can shrink and the row
   overflows its card — "Partially Fulfilled" inside a flex-shrink-0 cell in
   order_history is the reproducing case. Single-word overlay badges (Live,
   Instant, Upcoming) have no break opportunity, so this is a no-op for them. */
@media (max-width: 480px) {
  .lc-badge { white-space: normal; max-width: 100%; }
}

/* Partial mitigation only: the canvas sets touch-action: none, so a vertical
   swipe scratches instead of scrolling and can force-reveal a ticket the user
   was scrolling past. scratch_card.js still calls preventDefault() on a
   non-passive touchmove, so a complete fix needs axis detection in the JS. */
/* ---- Extracted layout classes ------------------------------------------
   These replace inline `style=` declarations in the templates. Inline styles
   beat every media query, so geometry had to move out of the attribute before
   any of it could respond. Desktop values are carried over verbatim; only the
   breakpoints below are new. */

/* `minmax(0, 1fr)`, not `1fr`: a grid track's min-width defaults to auto, so a
   1fr column floors at its content's min-content width and pushes the page
   wider than the viewport instead of shrinking. That is the mechanism behind
   the sideways scroll on the competitions list. */
.lc-list-layout {
  display: grid;
  grid-template-columns: 248px minmax(0, 1fr);
  gap: 28px;
  align-items: start;
}
.lc-list-toolbar { position: sticky; top: 64px; z-index: 30; }
/* The flex column was an inline `style=` on the aside. It had to come out of
   the attribute before the `display: contents` below could win. */
.lc-list-sidebar {
  position: sticky;
  top: 128px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
/* The 14px flex gap reads fine between filter rows, but leaves the dark
   promo card sitting flush under the filter card with no visual break.
   Only applies where the sidebar stays a flex column — below 900px it
   dissolves to `display: contents` and this would just nudge the promo's
   reordered position instead. */
@media (min-width: 901px) {
  .lc-list-promo { margin-top: var(--space-6); }
}

@media (max-width: 900px) {
  /* 248px + 28px + gutters leaves ~88px for the card column at 412px. Stack
     instead, and drop both sticky offsets — two stacked sticky bars eat most
     of a phone viewport. */
  .lc-list-layout { grid-template-columns: minmax(0, 1fr); gap: 20px; }
  .lc-list-toolbar,
  .lc-list-sidebar { position: static; top: auto; }

  /* Stacked, the aside put the free-postal card between the filters and the
     first competition — 181px of a 390px-wide viewport spent before a single
     card was visible. `display: contents` dissolves the aside so its children
     become grid items in their own right, and the promo can then be ordered
     past the card grid. Ordering the aside itself would have taken the filters
     down with it. Desktop keeps the aside intact, so the promo goes on
     scrolling with the sticky sidebar exactly as before. */
  .lc-list-sidebar { display: contents; }
  .lc-list-promo { order: 1; }
}

/* ---- Chip rails ---------------------------------------------------------
   Desktop keeps the wrapping rows it has always had. On phones the chips
   become one horizontally scrolling rail instead: six 44px chips wrapping to
   three rows cost ~180px of a ~760px viewport, and on the home page they are
   links off to /competitions/ — navigation, not filtering — so pinning them
   to the screen spent a third of the viewport on a control used once. */
.lc-chip-rail { display: flex; gap: 8px; flex-wrap: wrap; }

/* `display` lives here rather than inline on the element: an inline
   declaration outranks every media query, so `display: none` below could
   never have hidden it. */
.lc-filterbar-sort { display: inline-flex; }

/* ---- Sort dropdown ------------------------------------------------------
   Replaces a static span that looked like a control, said "Ending soonest",
   and did nothing — cursor: default, no handler, while the list was always
   ordered by newest first. So it also mislabelled the order it was not
   applying. */
.lc-sort { position: relative; }
.lc-sort-trigger {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 38px;
  padding: 7px 13px;
  font-size: 13px;
  color: var(--text-secondary);
  background: var(--surface-card);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-input);
  cursor: pointer;
}
.lc-sort-trigger:hover { background: var(--surface-raised); color: var(--text-heading); }
.lc-sort-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 40;
  min-width: 190px;
  padding: 5px;
  background: var(--surface-card);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
}
.lc-sort-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  /* 40px, not the 44px used for the chips: this is a menu row, and WCAG 2.5.8
     asks 24px minimum. Full-width rows are easy targets even at 40. */
  min-height: 40px;
  padding: 8px 10px;
  font-size: 13px;
  text-align: left;
  color: var(--text-secondary);
  background: none;
  border: 0;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.lc-sort-option:hover { background: var(--surface-page); color: var(--text-heading); }
.lc-sort-option[aria-selected="true"] { color: var(--text-heading); font-weight: 600; }

/* ---- Sidebar status radios ----------------------------------------------
   Single-select, so circles rather than the square check-boxes that were
   here: two of them could never both be ticked. */
.lc-radio-group { display: flex; flex-direction: column; gap: 4px; }
.lc-radio {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 36px;
  padding: 5px 0;
  font-size: 13.5px;
  text-align: left;
  color: var(--text-secondary);
  background: none;
  border: 0;
  cursor: pointer;
}
.lc-radio-mark {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: var(--surface-card);
  /* The inner dot is drawn with a ring rather than a child element so the
     selected state needs no extra markup in the loop. */
  box-shadow: inset 0 0 0 4px var(--surface-card);
}
.lc-radio:hover { color: var(--text-heading); }
.lc-radio:hover .lc-radio-mark { border-color: var(--accent-action); }
.lc-radio-on { color: var(--text-heading); font-weight: 600; }
.lc-radio-on .lc-radio-mark {
  border-color: var(--accent-action);
  background: var(--accent-action);
}

/* ---- Sidebar on/off switch ------------------------------------------------
   The whole row is the button, so the label is a tap target too. State comes
   from the server-rendered aria-checked, like the radios' lc-radio-on. */
.lc-switch-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  min-height: 36px;
  padding: 0;
  font-size: 13.5px;
  text-align: left;
  color: var(--text-secondary);
  background: none;
  border: 0;
  cursor: pointer;
}
.lc-switch-row:hover,
.lc-switch-row[aria-checked="true"] { color: var(--text-heading); }
.lc-switch-row[aria-checked="true"] { font-weight: 600; }
.lc-switch {
  position: relative;
  width: 38px;
  height: 22px;
  flex-shrink: 0;
  border-radius: 999px;
  background: var(--border-strong);
  transition: background var(--dur-fast) var(--ease-out);
}
.lc-switch::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--surface-card);
  box-shadow: var(--shadow-sm);
  transition: transform var(--dur-fast) var(--ease-out);
}
.lc-switch-row[aria-checked="true"] .lc-switch { background: var(--status-arcane); }
.lc-switch-row[aria-checked="true"] .lc-switch::after { transform: translateX(16px); }

/* ---- Collapsible filter card ---------------------------------------------
   Desktop: the header button is inert decoration and the body is always
   shown — the sidebar has room, and hiding filters there helps no one. The
   divider under the header lives on the body as border-top, not on the
   header as border-bottom, so a collapsed card does not end on a stray rule.
   Collapse is class-driven (`is-open`, toggled by Alpine) rather than
   x-show: the server can render the open state inline for zero flash before
   Alpine boots, and no inline display style has to be out-fought here. */
.lc-filter-toggle {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  gap: 8px;
  padding: 0;
  background: none;
  border: 0;
  color: inherit;
  cursor: default;
}
.lc-filter-caret { display: none; }
.lc-filter-body { border-top: 1px solid var(--border-soft); }

@media (max-width: 900px) {
  .lc-filterbar { position: static; top: auto; }

  .lc-chip-rail {
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    /* A flex/grid item's min-width defaults to auto, so without this the rail
       reports the full un-wrapped chip row as its minimum and widens the page
       rather than scrolling inside it. */
    min-width: 0;
    /* Bleed to the screen edges so a half-cut chip is visible at the right —
       that clipped edge is the only affordance that there is more to scroll.
       The cancelling negative margin is applied per-parent below, because it
       is only safe where the parent's inline padding is exactly var(--gutter);
       putting it here would push the rails past the page edge on the
       competitions list, whose .lc-filter-chipset parent has no padding. */
    padding-inline: var(--gutter);
    scroll-padding-inline: var(--gutter);
    scroll-snap-type: x proximity;
    /* Keep a horizontal swipe inside the rail: without this it chains to the
       page and can trigger the browser's back gesture at the left edge. */
    overscroll-behavior-inline: contain;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .lc-chip-rail::-webkit-scrollbar { display: none; }
  /* `proximity`, not `mandatory` — mandatory fights a deliberate small nudge. */
  .lc-chip-rail > * { flex: 0 0 auto; scroll-snap-align: start; }

  .lc-filterbar-inner { padding-block: 8px; gap: 8px; }
  /* Safe here: .lc-filterbar-inner's inline padding IS var(--gutter), so the
     negative margin lands the rail exactly on the screen edge, not past it. */
  .lc-filterbar-inner > .lc-chip-rail {
    flex: 1 1 100%;
    margin-inline: calc(var(--gutter) * -1);
  }
  /* The shop's category rails sit straight in the section; its inline padding
     is var(--gutter) too, so the same bleed lines the first chip up with the
     product grid and runs the rail (and its fade) to the screen edge. */
  .lc-section-inner > .lc-chip-rail-wrap { margin-inline: calc(var(--gutter) * -1); }
  /* This link points at ?sort=ending_soon — the exact URL the "Ending Soon"
     chip already uses. Dropping the duplicate saves a whole row on mobile. */
  .lc-filterbar-sort { display: none; }

  /* The chipset — not its rails — carries the bleed here. Its own parent is the
     toolbar inner (padding: 11px var(--gutter)), so the negative margin cancels
     against real padding; the rails then stretch to the chipset and inset their
     chips with their own padding-inline. Putting the margin on the rails would
     resolve it against the chipset, which has no padding, and push them a full
     gutter past each screen edge — a horizontal page scrollbar. */
  .lc-filter-chipset {
    flex: 1 1 100%;
    min-width: 0;
    margin-inline: calc(var(--gutter) * -1);
  }
  /* The sort control was hidden on phones while it was a dead placeholder.
     Now that it sorts, it earns its row — but it lands under the chips rather
     than beside them, and the menu is anchored to the left so it cannot open
     off the right edge of a 360px screen. */
  .lc-sort-trigger { min-height: 44px; }
  .lc-sort-menu { right: auto; left: 0; }

  /* Phones: the card stacks above the grid, so it starts collapsed (the
     template opens it server-side when a category filter is active). */
  .lc-filter-toggle { cursor: pointer; min-height: 44px; }
  .lc-filter-caret {
    display: inline-block;
    font-size: 10px;
    color: var(--text-muted);
    transition: transform 0.15s ease;
  }
  .lc-filter-caret-open { transform: rotate(180deg); }
  .lc-filter-body { display: none; }
  .lc-filter-body.is-open { display: block; }
}

.lc-grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.lc-grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
.lc-nf-features { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
.lc-grid-2 > *,
.lc-grid-3 > *,
.lc-nf-features > * { min-width: 0; }

@media (max-width: 640px) {
  .lc-grid-3 { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 480px) {
  .lc-grid-2,
  .lc-grid-3,
  .lc-nf-features { grid-template-columns: 1fr; }
}

/* ---- Form action row --------------------------------------
   A primary submit sitting beside a secondary link (/contact/'s
   "Formal Complaint →", /complaints/new/'s "Cancel"). Side by
   side at 390px the nowrap secondary label is ~195px and leaves
   ~150px for the submit, which wraps its label onto two lines
   and reads as two crushed half-buttons. Stack them instead.

   Layout, not touch affordance, so it uses the 640px layout gate
   the grids above use — not the 1023px/coarse-pointer gate. */
.lc-form-actions { display: flex; align-items: center; gap: 0.75rem; }
.lc-form-actions > * { min-width: 0; }
.lc-form-actions-grow { flex: 1; }
@media (max-width: 640px) {
  .lc-form-actions { flex-direction: column; align-items: stretch; }
  .lc-form-actions > * { width: 100%; }
}

.lc-pagination-btn { width: 38px; height: 38px; }
.lc-pagination-num { min-width: 38px; height: 38px; }
/* 38px is below the 44px tap-target minimum, and page numbers sit 8px apart.
   On the touch gate, not 480px: these were still 38px on a 667px landscape
   phone, which is the same class of miss as the tap-target rules above. */
@media (max-width: 1023px), (hover: none) and (pointer: coarse) {
  .lc-pagination-btn { width: 44px; height: 44px; }
  .lc-pagination-num { min-width: 44px; height: 44px; }
}

.comp-media-countdown { top: 14px; right: 14px; }
.comp-media-caption { bottom: 12px; left: 14px; }
@media (max-width: 480px) {
  /* Status badges are pinned top-left of the same image; with an Instant Win
     badge alongside, the two collide. Move the countdown to the bottom edge. */
  .comp-media-countdown { top: auto; bottom: 12px; right: 12px; }

  /* The "prize photography coming soon" caption (no-image branch only) is
     pinned bottom-left of that same image. At 412px the countdown is ~195px
     wide and the caption ~165px, so bottom-left and bottom-right overlap by a
     few px — and worse at 360px. Lift the caption clear of the countdown row
     (badge ~34px tall + 12px offset) instead of letting them share the edge. */
  .comp-media-caption { bottom: 54px; }
}

.lc-section-pad { padding: 4rem var(--gutter); }
@media (max-width: 480px) {
  .lc-section-pad { padding: 2.5rem var(--gutter); }
}

/* Touch behaviour, not layout, so it follows the touch gate — at 640px a
   landscape phone reverted to touch-action: none and a vertical swipe
   scratched the card instead of scrolling past it. */
@media (max-width: 1023px), (hover: none) and (pointer: coarse) {
  .scratch-canvas { touch-action: pan-y; }
}

/* ============================================================
   Mobile optimisation pass
   Measured at 320 / 360 / 390 / 430px. Everything below is a fix
   for a defect observed at one of those widths.
   ============================================================ */

/* ---- Overlay layer scale ----------------------------------
   .lc-nav, the cookie banner and the purchase modal all sat at
   z-index 50, so the winner was decided by DOM order — and the
   consent banner is included last in base.html, which put it on
   top of the modal's confirm button for every first-time visitor.
   One scale, stated once, so ties cannot happen again.
   Nothing here creates a stacking context on a modal ancestor;
   if one is ever added (transform / filter / opacity / backdrop-
   filter, including mid-flight x-transition), these numbers stop
   meaning anything and the modal is trapped in its parent. */
.lc-nav            { z-index: 50; }
.lc-theme-preview  { z-index: 55; }   /* staff-only chip: yields to real UI */
.lc-consent-banner { z-index: 60; }
.lc-enter-bar      { z-index: 70; }
.lc-overlay        { z-index: 90; }   /* purchase modal, basket drawer */
.lc-toast-stack    { z-index: 100; }
/* Deliberately outside the scale: ticket_reveal.html's confetti is generated
   at z-index 1000 so it falls over everything. It is pointer-events: none and
   self-removing, so it cannot block a control. */

/* ---- Scrollable data tables -------------------------------
   Every table was `width: 100%` inside an `overflow-x: auto`
   wrapper, so the table could never exceed its scroll container
   and the columns crushed instead of scrolling — at 390px
   /winners/ rendered "Entra/nt54," and a three-line "DRA/W/DATE"
   header. The min-width is what makes the wrapper's overflow
   mean something.

   min-width: 0 on the wrapper is load-bearing, not defensive: as
   a flex or grid item it would otherwise inherit `auto` and
   report the table's 560px as its own minimum, blowing the page
   out horizontally — the same trap as .contact-layout below. */
.lc-table-scroll {
  overflow-x: auto;
  min-width: 0;
  -webkit-overflow-scrolling: touch;
}
/* 560px suits the 3-to-7 column tables. Narrow tables override with
   --lc-table-min so they are not forced to scroll for no reason. */
.lc-table-scroll > table {
  width: 100%;
  min-width: var(--lc-table-min, 560px);
  border-collapse: collapse;
}
@media (max-width: 640px) {
  /* 1.5rem of side padding per cell spends ~144px of a 390px
     screen on whitespace before any content is drawn. */
  .lc-table-scroll th,
  .lc-table-scroll td { padding: 0.75rem 0.85rem; }
}

/* ---- Collapsible card -------------------------------------
   A <details> styled to sit inside .lc-card. The native marker is
   removed in favour of a chevron we can rotate, and the summary
   carries the card's own padding so the closed state is a plain
   clickable header rather than a header with a gap under it. */
.lc-collapse > summary {
  display: flex;
  align-items: baseline;
  gap: 0.625rem;
  padding: 1.25rem 1.5rem;
  cursor: pointer;
  list-style: none;
}
.lc-collapse > summary::-webkit-details-marker { display: none; }
/* The title keeps its width (a wide display face would otherwise break it mid-word
   at 360px); the subtitle beside it wraps instead. */
.lc-collapse > summary > .font-display { flex-shrink: 0; }
/* Hover shows on the chevron rather than as a summary-wide background: the
   summary spans the full card width, and a background on it would square off
   the card's own top corners whenever the pointer passed over. */
.lc-collapse > summary:hover .lc-collapse-chevron { color: var(--text-body); }
.lc-collapse > summary:focus-visible { outline: 2px solid var(--accent-gold); outline-offset: -2px; }
.lc-collapse-chevron {
  color: var(--text-muted);
  font-size: 12px;
  transition: transform 0.15s ease;
}
.lc-collapse[open] > summary .lc-collapse-chevron { transform: rotate(90deg); }

/* ---- Form controls ----------------------------------------
   Two separate defects, same selector.

   min-width: bare (non-.lc-input) controls report Chrome's
   default `size` attribute as their intrinsic minimum — ~263px
   for a text input — which forced /contact/ to a 345px
   scrollWidth inside a 320px viewport. legion-site.css already
   does this for .lc-input and .lc-btn; contact.html uses neither
   on some controls.

   font-size: iOS Safari zooms the page in on focusing any
   control under 16px and does not zoom back out. max() rather
   than a flat 16px so a deliberately larger control is raised to
   the threshold but never shrunk. !important is load-bearing:
   the offender is an inline font-size on the /winners/ search
   box, which a bare element selector loses to. (It was also a
   Tailwind .text-sm on /complaints/new/ until that page was
   ported to .lc-input — whose 0.9375rem this still raises.)

   Global is safe here: admin_portal/base.html loads only
   tailwind.css and admin-portal.css, never this file. */
@media (max-width: 1023px), (hover: none) and (pointer: coarse) {
  input, select, textarea { min-width: 0; }
  input:not([type="checkbox"]):not([type="radio"]),
  select,
  textarea { font-size: max(16px, 1em) !important; }
}
.contact-layout,
.contact-main,
.contact-sidebar,
.complaint-layout,
.complaint-main,
.complaint-sidebar { min-width: 0; }

/* ---- Section vertical rhythm ------------------------------
   .lc-section was the one layout primitive with no mobile
   override anywhere in this file: a flat var(--section-y) (5rem)
   top and bottom even at 320px, while .lc-section-pad and
   .lc-page-hero both scale down. Every content page therefore
   spent 160px per section on padding alone on a phone screen,
   and read as far airier than the home page beside it.
   2.5rem matches .lc-section-pad so the two primitives agree.
   Set here rather than on --section-y, which desktop also uses. */
@media (max-width: 640px) {
  .lc-section { padding: 2.5rem 0; }
}

/* ---- Tap targets ------------------------------------------
   Measured at 390px: footer nav links 40px, the legal row 16px,
   "built by" 17px, password toggles 20x20, hamburger 40x40.
   44px is the floor both Apple and Google publish. */
@media (max-width: 1023px), (hover: none) and (pointer: coarse) {
  /* 12px either side of a 19.5px line is 43.5px — half a pixel short, on the
     most repeated link on the site (18 per page). min-height applies to the
     inline-block above without changing its display or the column's flow. */
  .lc-footer-inner .lc-footer-link { padding: 12px 0; min-height: 44px; }
  .lc-footer-bottom .lc-footer-link,
  .lc-footer-builtby {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
  /* The checkbox itself is 16px and cannot grow without looking
     wrong; the label wrapping it is the real target. */
  .lc-consent-label { min-height: 44px; }
  /* The wordmark is a 183x40 link — wide enough, 4px short. */
  .lc-logo { min-height: 44px; }
}
/* Below the phone breakpoint the rules here lift every target to 44px. At any width
   they must clear the 24px floor of WCAG 2.2 AA 2.5.8. */
.lc-tap {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
}
/* Links inside a list item or sentence that the audit cannot tell from prose
   (an em dash or a separator dot is the only text beside them). Same 24px floor. */
.lc-link-pad {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
}
/* Legal table-of-contents links were 22px tall. */
.lc-legal-link,
.lc-legal-toc-link { min-height: 24px; }
/* Small icon-only close buttons (the basket drawer's cross is 11px wide). The
   negative margin gives the box back so the glyph stays where it was. */
.lc-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 32px;
  min-height: 32px;
  margin: -8px -10px -8px 0;
}
/* Tailwind's w-4 h-4 makes a 16px box, under the 24px floor of WCAG 2.2 AA 2.5.8.
   The label around each box is the larger click area; this makes the box itself
   comply. mt-0.5 is replaced so it stays centred on a 20px text line. */
input[type="checkbox"].w-4.h-4 { width: 1.5rem; height: 1.5rem; margin-top: -2px; }

/* Standalone small text links that are real controls rather than prose:
   "Reset" on the filter panel (33x18) and "How it works" in the free-entry
   card (93x15). Padding rather than min-height, so the horizontal target
   grows too — both are narrow as well as short. Inline text links inside a
   paragraph are deliberately left alone; padding them would break line flow. */
@media (max-width: 1023px), (hover: none) and (pointer: coarse) {
  .lc-tap {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0 8px;
    margin: 0 -8px;
  }
  /* Icon-only share buttons render 39-42px wide from their glyph alone. */
  .lc-share .lc-btn { min-width: 44px; }
  /* Breadcrumb links are 34x18. Padding them would push every page's first
     row ~26px down, so grow the touch target with an overlay instead — the
     separators between them are chevrons, not links, so nothing collides. */
  .lc-breadcrumb a,
  .comp-crumb a { position: relative; }
  .lc-breadcrumb a::after,
  .comp-crumb a::after {
    content: "";
    position: absolute;
    inset: -13px -8px;
  }
  /* Purchase-modal dismiss is a 32x32 circle pinned to the sheet's top-right.
     Growing the box would drag the visible circle out towards the corner, so
     take the same overlay route: -6px on each side lands it on 44x44. */
  .lc-modal-close::after {
    content: "";
    position: absolute;
    inset: -6px;
  }
}
.lc-pw-toggle {
  min-width: 44px;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* Both of these carry `lg:hidden` in base.html, but legion-site.css is linked
   *after* tailwind.css and `.lc-nav-burger { display: flex }` has the same
   (0,1,0) specificity as `.lg\:hidden { display: none }` — so later-wins put the
   burger on screen at every width, taking 44px out of the desktop nav row. The
   old `w-10 h-10` markup never hit this because Tailwind sizing utilities set no
   `display`. Own the display property here instead of fighting the utility:
   none by default, shown only below Tailwind's own lg breakpoint (1024px), so
   these stay correct even if the `lg:hidden` in the template is ever dropped.
   Width, not the touch gate — the desktop nav appears at exactly 1024px, and a
   pointer-based rule would show the burger *and* the full nav on a large
   touchscreen. */
.lc-nav-burger,
.lc-nav-basket-mobile {
  display: none;
}
.lc-nav-burger {
  width: 44px;
  height: 44px;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 5px;
}
/* Phone-width basket affordance. Same visual language as .lc-basket-btn,
   sized to the 44px floor rather than that button's desktop 40px. */
.lc-nav-basket-mobile {
  position: relative;
  width: 44px;
  height: 44px;
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  text-decoration: none;
}
@media (max-width: 1023px) {
  .lc-nav-burger,
  .lc-nav-basket-mobile {
    display: flex;
  }
}

/* ---- Mobile menu ------------------------------------------
   .lc-btn is display: inline-flex, so the `block` class the menu
   buttons carried was a dead no-op and they sat at their content
   width (89px / 138px), left-packed on a 390px screen. */
.lc-mobile-menu .lc-btn {
  display: flex;
  width: 100%;
  justify-content: center;
  /* Not inside the max-width: 640px tap-target block — a landscape phone is
     667px wide, so these measured 38px there while every other control in
     the menu was 44+. The menu itself only exists below lg (1024px). */
  min-height: 44px;
  align-items: center;
}
/* Without this the page behind the open menu still scrolls under
   the user's finger.

   The class is applied by lcSyncMenuLock() in base.html, not directly by
   Alpine, and only when the whole open menu fits above the fold. On a
   landscape phone the open header is ~461px tall while the viewport is
   375-430px, so the last menu items sit below the fold; page scroll is the
   only way to reach them and this lock removed it (1-2 of 7 unreachable).

   Gating in JS rather than on a @media (min-height:) threshold, for two
   reasons. A fixed max-height on the menu is not an option because the menu's
   offset from the viewport top is not a constant — the promo bar above the
   sticky header scrolls independently, so the offset is ~92px at scrollTop 0
   and ~65px once it has gone. And a media threshold is measured against the
   *layout* viewport on iOS: an iPhone SE reports 568px with the toolbars
   retracted but shows ~458px, so it would match any sane threshold and still
   hide the bottom of the menu. getBoundingClientRect() vs innerHeight is true
   on every device. If the script never runs the class is never added and the
   page simply scrolls, which is the pre-lock behaviour. */
body.lc-menu-open { overflow: hidden; }

/* The entry bar is fixed at the bottom at z70, above the open menu's backdrop,
   so in landscape it lands on the bottom of the menu. Same mechanism as the
   consent rule below — suppress the bar rather than reshuffle the z-order.

   Keyed on lc-menu-visible, not lc-menu-open: the lock above is deliberately
   withheld in landscape, which is the only viewport where the bar and the menu
   overlap at all. Keying this on the lock inverted it — inert in landscape,
   firing in portrait where the bar sits 278px below the menu. */
body.lc-menu-visible .lc-enter-bar { display: none; }
/* The backdrop lives inside <header class="lc-nav">, and .lc-nav carries
   backdrop-filter — which makes the header a containing block for fixed
   descendants. `position: fixed; inset: 0` therefore resolved against the
   header, not the viewport: measured 390x380 sitting exactly over the open
   menu and, being the only positioned sibling, painting on top of it.
   Anchor it to the header instead and lift the menu above it explicitly. */
.lc-menu-backdrop {
  position: absolute;
  top: 100%;                      /* below the header *and* the open menu */
  left: 0;
  right: 0;
  height: 100vh;
  background: var(--surface-scrim);
  z-index: 40;
}
.lc-mobile-menu {
  position: relative;
  z-index: 45;                    /* over the backdrop, under .lc-nav's 50 */
}

/* ---- Filter chip rail -------------------------------------
   538px of chips in a 385px box. It scrolled correctly but the
   last chip was simply cut mid-word — nothing signalled that the
   rail moves. The fade goes on a static wrapper's pseudo-element
   rather than as a mask-image on the rail itself: a mask paints
   relative to the container box, not the scrolled content, so the
   last chip would stay faded even when scrolled fully right,
   which reads as a rendering bug. Same approach as
   .lc-ticker-fade-l / -r above.

   The class goes on the rail's existing parent (.lc-filterbar-inner on the
   home page, .lc-filter-chipset on the competitions list) rather than a new
   wrapper div — a wrapper would break the `.lc-filterbar-inner >
   .lc-chip-rail` direct-child selector that carries the edge bleed above.
   Both parents already span the full screen width on mobile, so their right
   edge is the rail's right edge.

   The fade is gated on .lc-rail-scrolls, set by a small script in base.html
   that compares scrollWidth to clientWidth: at 390 and 430 the rail already
   fits, and an unconditional fade just washed out the last chip of a rail
   that does not move. If the script never runs the fade simply never
   appears, which is the pre-fix state rather than a broken one. */
.lc-chip-rail-wrap { position: relative; }
@media (max-width: 900px) {
  .lc-chip-rail-wrap.lc-rail-scrolls::after {
    content: "";
    position: absolute;
    inset-block: 0;
    right: 0;
    width: 40px;
    pointer-events: none;
    background: linear-gradient(to right, transparent, var(--surface-card));
  }
}

/* ---- Minimum functional text size -------------------------
   Uppercase letter-spaced eyebrows at 11px are a deliberate
   style choice and stay. Form labels and stat labels are
   functional text and go to a 12px floor on phones. */
@media (max-width: 1023px), (hover: none) and (pointer: coarse) {
  .lc-label { font-size: 12px; }
  .lc-statbar-label { font-size: var(--statbar-label-size, 12px); }
}

/* ---- Hero CTA pair ----------------------------------------
   Centered and off the `lg` scale below 480px — full-width stacked
   buttons read oversized next to the compact hero copy at that
   width. .lc-btn's own min-height:44px (above) keeps the tap
   target; align-self beats .lc-hero-row's align-items:flex-start
   !important since it's a different property, not a specificity
   fight. */
@media (max-width: 480px) {
  .lc-hero-cta {
    align-self: center;
  }
  .lc-hero-cta > .lc-btn {
    padding: 0.7rem var(--hero-cta-pad-x, 1.4rem);
    font-size: var(--hero-cta-size, 0.875rem);
  }
}

/* ---- Share row --------------------------------------------
   Five 44px targets plus a "Copy link" label do not fit inside a
   390px gutter, so the device-share button wrapped onto a line of
   its own below the rest. One nowrap row of equal-width buttons
   instead, with the copy label dropped to its icon — every button
   already carries an aria-label, and the icon still swaps to a
   tick on copy. */
@media (max-width: 640px) {
  .lc-share { flex-wrap: nowrap; gap: 0.375rem; }
  .lc-share > .lc-btn { flex: 1 1 auto; padding-left: 0; padding-right: 0; }
  .lc-share-label { flex: 0 0 auto; }
  .lc-share-copy-text { display: none; }
}

/* ---- Sticky entry CTA -------------------------------------
   The competition page is ~4,950px tall at 390px with a single
   entry CTA at roughly y2700 and nothing persistent after it.
   The bar carries the whole purchase control on mobile — running
   total, ticket stepper, odds and the Enter button — so the rail
   further down the page never has to be scrolled back to.
   Layered at 70: above the nav and consent banner, below the
   modal, so tapping it opens the purchase sheet over the bar. */
.lc-enter-bar { display: none; }
@media (max-width: 1023px) {
  .lc-enter-bar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.625rem var(--gutter);
    padding-bottom: calc(0.625rem + env(safe-area-inset-bottom));
    background: var(--surface-card);
    border-top: 1px solid var(--border-soft);
    box-shadow: var(--shadow-bar-up);
  }
  .lc-enter-bar-price {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
    line-height: 1.15;
  }
  .lc-enter-bar-total {
    font-size: 1.75rem;
    font-weight: 800;
    color: var(--accent-gold-ink);
  }
  .lc-enter-bar-count {
    font-size: 0.8125rem;
    color: var(--text-muted);
    white-space: nowrap;
  }

  /* One scrolling row — a wrapped second row of tiers would push the CTA
     off a small screen. */
  .lc-enter-bar-tiers {
    flex-wrap: nowrap;
    overflow-x: auto;
    gap: 0.375rem;
    margin: -0.125rem 0 0.125rem;
    padding-bottom: 2px;
    scrollbar-width: none;
  }
  .lc-enter-bar-tiers::-webkit-scrollbar { display: none; }
  .lc-enter-bar-tiers .lc-chip { flex: 0 0 auto; padding: 6px 12px; font-size: var(--chip-size-sm, 12px); }

  .lc-enter-bar-step { display: flex; align-items: center; gap: 0.625rem; }
  .lc-enter-bar-step-btn {
    flex: 0 0 auto;
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-pill);
    border: 1px solid var(--border-strong);
    background: var(--surface-raised);
    color: var(--accent-gold-ink);
    font-size: 1.25rem;
    font-weight: 700;
    cursor: pointer;
    user-select: none;
  }
  .lc-enter-bar-step-btn:disabled { opacity: 0.35; cursor: not-allowed; }

  /* Track is painted on the runnable-track, not the input, so the input can
     stay 40px tall for the touch target while the line itself reads as 6px.
     --lc-slider-pct is bound from Alpine so the filled half tracks quantity. */
  .lc-enter-bar-slider {
    -webkit-appearance: none;
    appearance: none;
    flex: 1;
    min-width: 0;
    height: 40px;
    background: transparent;
    cursor: pointer;
  }
  .lc-enter-bar-slider::-webkit-slider-runnable-track {
    height: 6px;
    border-radius: var(--radius-pill);
    background: linear-gradient(
      to right,
      var(--accent-gold) var(--lc-slider-pct, 0%),
      var(--surface-sunken) var(--lc-slider-pct, 0%)
    );
  }
  .lc-enter-bar-slider::-moz-range-track {
    height: 6px;
    border-radius: var(--radius-pill);
    background: linear-gradient(
      to right,
      var(--accent-gold) var(--lc-slider-pct, 0%),
      var(--surface-sunken) var(--lc-slider-pct, 0%)
    );
  }
  .lc-enter-bar-slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 24px;
    height: 24px;
    margin-top: -9px;  /* centre the 24px thumb on the 6px track */
    border-radius: 50%;
    background: var(--accent-gold);
    border: 2px solid var(--surface-card);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-gold) 25%, transparent);
  }
  .lc-enter-bar-slider::-moz-range-thumb {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: var(--accent-gold);
    border: 2px solid var(--surface-card);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-gold) 25%, transparent);
  }
  .lc-enter-bar-slider:focus-visible::-webkit-slider-thumb {
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent-gold) 45%, transparent);
  }
  .lc-enter-bar-slider:focus-visible::-moz-range-thumb {
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent-gold) 45%, transparent);
  }
  .lc-enter-bar-slider:disabled { opacity: 0.45; cursor: not-allowed; }

  .lc-enter-bar-odds {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.375rem;
    margin: -0.125rem 0 0;
    font-size: 0.75rem;
    color: var(--text-secondary);
  }
  .lc-enter-bar-odds i { color: var(--status-arcane); }

  .lc-enter-bar .lc-btn-primary {
    width: 100%;
    min-height: 50px;
    justify-content: center;
    font-size: 1rem;
  }

  /* Short viewports (landscape phones) cannot spare ~160px of fixed chrome —
     drop the slider row there. */
  @media (max-height: 560px) {
    .lc-enter-bar-tiers,
    .lc-enter-bar-step,
    .lc-enter-bar-odds { display: none; }
    .lc-enter-bar-total { font-size: 1.375rem; }
  }

  /* Clear the bar so the footer is not hidden behind it. The height is
     measured at runtime into --lc-enter-bar-h (competition.html) because the
     bar now wraps differently by viewport; the fallback matches a 390px-wide
     render. The class goes on <body> via base.html's body_class block, not a
     spacer div inside {% block content %} — a spacer inside <main> cannot
     clear the footer, which is main's sibling, so the bar still covered it.
     The padding lands on .lc-footer rather than on <body>, because the
     footer is --slate-950 and body padding would show as a light strip
     under it whenever the bar is not there to cover the gap. */
  body.lc-has-enter-bar .lc-footer {
    padding-bottom: var(--lc-enter-bar-h, calc(184px + env(safe-area-inset-bottom)));
  }
}

/* The consent banner is fixed to the bottom as well, and the entry bar sits
   above it at z70 — measured covering both of its buttons, so a first-time
   visitor on a competition page could not accept or reject cookies at all.
   Suppress the bar while a consent choice is pending rather than swapping
   the z-order, which would only bury the CTA instead. The inline entry CTA
   further up the page is unaffected. Specificity (0,2,1) beats the
   .lc-enter-bar rules above regardless of the media query. */
body.lc-consent-pending .lc-enter-bar {
  display: none;
}

/* ---- Rendered markdown (comp/prize descriptions) -----------
   Tailwind's preflight zeroes p/pre margins and strips ol/ul list-style —
   needed for the rest of the site, but it flattens the markdown renderer's
   output (app/lib/markdown.py) back into a run-on block. Scoped to
   .lc-markdown so the reset stays untouched everywhere else. */
.lc-markdown p {
  margin: 0 0 0.75em;
}
.lc-markdown p:last-child {
  margin-bottom: 0;
}
.lc-markdown ul,
.lc-markdown ol {
  margin: 0 0 0.75em;
  padding-left: 1.25em;
}
.lc-markdown ul {
  list-style: disc;
}
.lc-markdown ol {
  list-style: decimal;
}
.lc-markdown li {
  margin: 0 0 0.25em;
}
.lc-markdown blockquote {
  margin: 0 0 0.75em;
  padding-left: 0.875em;
  border-left: 2px solid var(--border-soft);
  color: var(--text-muted);
}
.lc-markdown pre {
  margin: 0 0 0.75em;
  padding: 0.6em 0.75em;
  overflow-x: auto;
  background: var(--surface-sunken);
  border-radius: 6px;
}
.lc-markdown code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.9em;
}
.lc-markdown a {
  color: var(--accent-gold-ink);
  text-decoration: underline;
}
.lc-markdown h3,
.lc-markdown h4 {
  margin: 0.75em 0 0.5em;
  color: var(--text-heading);
  line-height: 1.3;
}
.lc-markdown h3 {
  font-size: 1.05em;
}
.lc-markdown h4 {
  font-size: 1em;
}

/* ---- Long-form article body (War Table posts) --------------
   .lc-article is layered on top of .lc-markdown, not instead of it: the
   description rules above already handle p/ul/ol/blockquote/pre/code/a, and
   these add only what the wider article profile in app/lib/markdown.py can
   emit — h2/h5, images, tables and rules. Type is scaled up because an
   article is the page, not a paragraph inside one. */
.lc-article h2,
.lc-article h3,
.lc-article h4,
.lc-article h5 {
  margin: 1.75em 0 0.6em;
  color: var(--text-heading);
  font-family: var(--font-display, inherit);
  font-weight: 700;
  line-height: 1.3;
}
.lc-article h2 {
  font-size: 1.4em;
}
.lc-article h3 {
  font-size: 1.2em;
}
.lc-article h4 {
  font-size: 1.05em;
}
.lc-article h5 {
  font-size: 1em;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.lc-article p,
.lc-article ul,
.lc-article ol {
  margin-bottom: 1.1em;
}
.lc-article li {
  margin-bottom: 0.4em;
}
.lc-article img {
  max-width: 100%;
  height: auto;
  display: block;
  margin: 1.5em 0;
  border-radius: 8px;
}
.lc-article hr {
  border: 0;
  border-top: 1px solid var(--border-default);
  margin: 2em 0;
}
.lc-article blockquote {
  margin: 1.5em 0;
  padding: 0.25em 0 0.25em 1.1em;
  border-left: 3px solid var(--accent-gold-ink);
  font-style: italic;
}
.lc-article pre {
  margin: 1.5em 0;
}
/* Tables can outgrow the measure on a phone; the wrapper keeps the overflow
   inside the article instead of scrolling the whole page sideways. */
.lc-article table {
  width: 100%;
  margin: 1.5em 0;
  border-collapse: collapse;
  display: block;
  overflow-x: auto;
  font-size: 0.92em;
}
.lc-article th,
.lc-article td {
  border: 1px solid var(--border-default);
  padding: 0.5em 0.75em;
  text-align: left;
}
.lc-article th {
  background: var(--surface-sunken);
  font-weight: 700;
}

/* ---- Basket quantity input --------------------------------- */
/* The stepper's middle used to be a <span>. As a real field it has to hold the
   same 24px rhythm as the two buttons either side, and lose the spinners: the
   arrows are the − and + already there, and a native spinner inside a 40px box
   leaves no room for a two-digit quantity. */
.lc-qty-input {
  width: 3rem;
  height: 1.5rem;
  padding: 0 0.25rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface-card);
  -moz-appearance: textfield;
  appearance: textfield;
}
.lc-qty-input::-webkit-outer-spin-button,
.lc-qty-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.lc-qty-input:focus-visible {
  outline: none;
  border-color: var(--accent-gold);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-gold) 35%, transparent);
}

/* ---- Basket panel column ----------------------------------- */
/* Three elements share the height: .lc-basket-header (fixed), .lc-basket-body
   holding the HTMX-swapped fragment, and inside that fragment an item list that
   takes the slack and a foot that does not.

   min-height: 0 on every flex child in the chain is load-bearing. A flex item's
   default min-height is auto, so each one refuses to shrink below its content
   and the overflow pushes out of the panel instead of scrolling inside it — the
   list would grow, the foot would be shoved past the bottom edge, and the
   Proceed button would be exactly as unreachable as it was before. */
.lc-basket-body {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}
.lc-basket-panel {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}
.lc-basket-items {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 0.5rem 1rem;
  /* The list is the only scroller in the panel, so an overscroll at either end
     must not hand the gesture to the page behind it. */
  overscroll-behavior: contain;
}
.lc-basket-foot {
  flex: none;
  padding: 1rem;
  border-top: 1px solid var(--border-soft);
  background: var(--surface-card);
}

/* ============================================================
   Theme preview chip — staff only, while ?theme= is active
   ============================================================ */
.lc-theme-preview {
  position: fixed; left: 0.75rem; bottom: 0.75rem;   /* z-index: overlay scale */
  display: inline-flex; align-items: center; gap: 0.5rem;
  padding: 0.4rem 0.75rem; font-size: 0.75rem;
  background: var(--surface-dark-card); color: var(--text-on-dark-body);
  border: 1px solid var(--border-dark-strong); border-radius: var(--radius-chip);
  box-shadow: var(--shadow-btn);
}
.lc-theme-preview strong { color: var(--text-on-dark); }
.lc-theme-preview a {
  color: var(--accent-gold-on-dark); font-weight: var(--fw-semibold);
  min-height: 44px; display: inline-flex; align-items: center; padding: 0 0.25rem;
}

/* Help page "Contact us" button: white on the action fill (was dark text on a raw crimson). */
.lc-contact-btn { background: var(--accent-action); color: var(--text-on-action); }
.lc-contact-btn:hover { background: var(--accent-action-hover); }

/* Help FAQ rows: hover fill follows the theme (was a raw navy that put dark text on dark). */
.lc-faq-btn:hover { background: var(--surface-raised); }

/* ============================================================
   Storefront and checkout: product cards, line items, the order summary,
   selectable options, the quantity stepper and the mobile pay bar.
   Generic names in core so any feature can use them (the shop is the first);
   colour, shape and type come only from the semantic tokens, so every theme
   restyles them. Pixel's notched frames for these live in themes/pixel.css.
   ============================================================ */

.lc-nav-basket-mobile > i { font-size: 16px; }

/* Breadcrumb on the page ground. .lc-breadcrumb is drawn for the dark hero
   and its on-dark colours fail contrast on a light page. */
.lc-crumb { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 1.25rem; font-size: 12px; color: var(--text-muted); }
.lc-crumb a { color: var(--text-muted); text-decoration: none; }
.lc-crumb a:hover { color: var(--text-display); }
.lc-crumb i { font-size: 9px; }
.lc-crumb [aria-current] { color: var(--text-body); }

.lc-price {
  font-family: var(--font-label);
  font-weight: var(--fw-card-title);
  color: var(--text-display);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* ---- Product card: the whole card is one link ---- */
a.lc-product-card { text-decoration: none; color: inherit; }
a.lc-product-card:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 4px; }
.lc-product-media { position: relative; aspect-ratio: 4 / 3; background: var(--surface-sunken); }
.lc-product-media > img { width: 100%; height: 100%; object-fit: cover; display: block; }
.lc-product-media > .lc-thumb { position: absolute; inset: 0; }
.lc-product-media > .lc-thumb i { font-size: 1.75rem; color: var(--text-on-dark-muted); }
.lc-product-badges { position: absolute; top: 0.75rem; left: 0.75rem; display: flex; gap: 0.375rem; flex-wrap: wrap; }
.lc-product-body { padding: 1.125rem 1.25rem 1.25rem; display: flex; flex-direction: column; gap: 0.375rem; flex: 1; }
.lc-product-title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--fw-card-title);
  font-size: 1.0625rem;
  line-height: 1.3;
  color: var(--text-heading);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.lc-product-summary {
  margin: 0;
  font-size: 0.875rem;
  line-height: 1.55;
  color: var(--text-secondary);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.lc-product-foot { margin-top: auto; padding-top: 0.75rem; display: flex; align-items: baseline; justify-content: space-between; gap: 0.75rem; }
.lc-product-foot .lc-price { font-size: 1.125rem; }
.lc-product-foot-note { font-size: 0.8125rem; color: var(--text-muted); }

.lc-pager { display: flex; align-items: center; justify-content: center; gap: 0.75rem; margin-top: 2.5rem; font-size: 0.875rem; color: var(--text-secondary); }
.lc-stack { display: flex; flex-direction: column; gap: 0.75rem; }

/* ---- Product page ---- */
.lc-product-layout { display: grid; gap: 2rem; align-items: start; }
@media (min-width: 900px) { .lc-product-layout { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 3rem; } }
.lc-gallery-main {
  position: relative;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  border-radius: var(--radius-card);
  background: var(--surface-sunken);
  box-shadow: var(--shadow-card);
}
.lc-gallery-main > img { width: 100%; height: 100%; object-fit: cover; display: block; }
.lc-gallery-main > .lc-thumb { position: absolute; inset: 0; }
.lc-gallery-main > .lc-thumb i { font-size: 2.5rem; color: var(--text-on-dark-muted); }
.lc-gallery-thumbs { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 0.625rem; margin-top: 0.875rem; }
.lc-gallery-thumb {
  padding: 0;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  border: 2px solid transparent;
  border-radius: var(--radius-input);
  background: var(--surface-sunken);
  cursor: pointer;
}
.lc-gallery-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.lc-gallery-thumb[aria-pressed="true"] { border-color: var(--accent-gold); }
.lc-gallery-thumb:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.lc-product-heading {
  margin: 0 0 0.5rem;
  font-family: var(--font-display);
  font-weight: var(--fw-card-title);
  font-size: clamp(1.625rem, 3vw, 2.125rem);
  line-height: 1.15;
  color: var(--text-display);
}
.lc-product-lede { margin: 0 0 1.25rem; font-size: 1rem; line-height: 1.65; color: var(--text-secondary); }
.lc-buybox { padding: 1.25rem; display: flex; flex-direction: column; gap: 1rem; }
.lc-buybox-form { display: flex; flex-direction: column; gap: 1rem; margin: 0; }
.lc-buybox .lc-buybox-row > .lc-price { font-size: 1.75rem; line-height: 1; }
.lc-product-tags { display: flex; gap: 0.375rem; flex-wrap: wrap; margin-bottom: 0.875rem; }
.lc-product-copy { margin-top: 2rem; }
.lc-buybox-row { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.lc-buybox-row > .lc-btn { flex: 1 1 12rem; justify-content: center; }
.lc-buybox-note { display: flex; align-items: flex-start; gap: 0.5rem; margin: 0; font-size: 0.875rem; line-height: 1.5; color: var(--text-secondary); }
.lc-buybox-note i { margin-top: 0.2rem; color: var(--text-muted); width: 1rem; text-align: center; }
.lc-stock { font-size: 0.875rem; color: var(--text-secondary); }
.lc-stock-low { color: var(--status-warning-ink); }
.lc-stock-out { color: var(--text-muted); }

/* ---- Selectable option: a radio (or checkbox) inside a whole-row label ---- */
.lc-options { display: flex; flex-direction: column; gap: 0.5rem; margin: 0; padding: 0; border: 0; min-width: 0; }
.lc-options > legend {
  padding: 0;
  margin-bottom: 0.5rem;
  font-family: var(--font-label);
  font-size: var(--label-size, 0.8125rem);
  letter-spacing: var(--label-tracking, 0.02em);
  font-weight: var(--fw-semibold);
  color: var(--text-heading);
}
.lc-option {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-height: 44px;
  padding: 0.75rem 0.875rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-input);
  background: var(--surface-card);
  color: var(--text-body);
  cursor: pointer;
  transition: border-color var(--dur-fast), background var(--dur-fast);
}
.lc-option:hover { border-color: var(--accent-gold-strong); }
.lc-option:has(input:checked) { border-color: var(--accent-gold); background: var(--surface-raised); }
.lc-option:has(input:focus-visible) { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.lc-option:has(input:disabled) { cursor: not-allowed; color: var(--text-muted); background: var(--surface-sunken); }
.lc-option input { width: 20px; height: 20px; margin: 0; flex: none; accent-color: var(--accent-action); }
.lc-option-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.125rem; }
.lc-option-title { font-weight: var(--fw-semibold); color: var(--text-heading); }
.lc-option-meta { font-size: 0.8125rem; color: var(--text-secondary); }
.lc-option .lc-price { font-size: 1rem; }

/* ---- Quantity stepper: 44px buttons either side of a number field ---- */
.lc-stepper {
  display: inline-flex;
  align-items: stretch;
  height: 44px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-input);
  background: var(--surface-card);
  overflow: hidden;
}
.lc-stepper-btn {
  width: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: var(--surface-raised);
  color: var(--text-heading);
  font-size: 0.8125rem;
  cursor: pointer;
}
.lc-stepper-btn:hover:not(:disabled) { background: var(--surface-sunken); }
.lc-stepper-btn:disabled { color: var(--text-disabled); cursor: not-allowed; }
.lc-stepper-btn:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -2px; }
.lc-stepper-value {
  width: 3.25rem;
  border: 0;
  border-inline: 1px solid var(--border-soft);
  background: transparent;
  color: var(--text-heading);
  text-align: center;
  font-family: var(--font-label);
  font-size: 1rem;
  font-variant-numeric: tabular-nums;
  -moz-appearance: textfield;
  appearance: textfield;
}
.lc-stepper-value::-webkit-inner-spin-button,
.lc-stepper-value::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
.lc-stepper-value:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -2px; }

/* ---- Checkout: lines on the left, a sticky summary rail on the right ---- */
.lc-checkout { display: grid; gap: 1.5rem; align-items: start; }
@media (min-width: 1024px) {
  .lc-checkout { grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); gap: 2rem; }
  .lc-checkout-aside { position: sticky; top: 5.25rem; }
}
.lc-checkout-main,
.lc-checkout-aside { display: flex; flex-direction: column; gap: 1.25rem; min-width: 0; }
.lc-checkout-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin-bottom: 1.5rem; }
.lc-checkout-title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--fw-card-title);
  font-size: clamp(1.75rem, 3.5vw, 2.25rem);
  line-height: 1.1;
  color: var(--text-display);
}
.lc-panel { padding: 1.25rem; }
.lc-panel-title,
.lc-options > legend.lc-panel-title {
  margin: 0 0 1rem;
  letter-spacing: normal;
  font-family: var(--font-display);
  font-weight: var(--fw-card-title);
  font-size: 1.125rem;
  color: var(--text-heading);
}
/* A panel that is a fieldset: floating the legend takes it off the frame and
   into the panel, where it sits like any other panel title. */
.lc-panel > legend { float: left; width: 100%; padding: 0; }
.lc-panel > legend + * { clear: both; }
.lc-panel-hint { margin: -0.5rem 0 1rem; font-size: 0.8125rem; color: var(--text-secondary); }

/* ---- Line item ---- */
.lc-lines { list-style: none; margin: 0; padding: 0; }
.lc-line { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 0.5rem 1rem; padding: 1.125rem 1.25rem; }
.lc-line + .lc-line { border-top: 1px solid var(--border-soft); }
.lc-line-thumb {
  grid-row: span 2;
  width: 64px;
  height: 64px;
  overflow: hidden;
  border-radius: var(--radius-input);
  background: var(--surface-sunken);
}
.lc-line-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.lc-line-thumb .lc-thumb { width: 100%; height: 100%; }
.lc-line-thumb .lc-thumb i { color: var(--text-on-dark-muted); }
.lc-line-body { min-width: 0; display: flex; flex-direction: column; gap: 0.25rem; }
.lc-line-title { font-weight: var(--fw-semibold); color: var(--text-heading); text-decoration: none; line-height: 1.35; }
a.lc-line-title:hover { color: var(--accent-gold-ink); }
.lc-line-meta { display: flex; align-items: center; gap: 0.375rem 0.75rem; flex-wrap: wrap; font-size: 0.8125rem; color: var(--text-secondary); }
.lc-line-meta i { color: var(--text-muted); margin-right: 0.25rem; }
.lc-line-total { text-align: right; font-size: 1.0625rem; }
.lc-line-controls { grid-column: 2 / -1; display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; flex-wrap: wrap; }
.lc-line-controls form { margin: 0; flex-shrink: 0; }
.lc-line-note { grid-column: 2 / -1; }
.lc-line-unavailable .lc-line-thumb img { opacity: 0.5; }
.lc-line-unavailable .lc-line-total { color: var(--text-muted); text-decoration: line-through; }
.lc-link-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  min-height: 44px;
  padding: 0 0.25rem;
  border: 0;
  background: none;
  color: var(--text-secondary);
  font-size: 0.875rem;
  text-decoration: underline;
  text-underline-offset: 3px;
  white-space: nowrap;
  cursor: pointer;
}
.lc-link-btn:hover { color: var(--text-heading); }
/* These set their own display, which would otherwise beat the hidden attribute. */
.lc-alert[hidden], .lc-link-btn[hidden] { display: none; }
@media (max-width: 480px) {
  .lc-line { padding: 1rem; column-gap: 0.75rem; }
  .lc-line-thumb { width: 56px; height: 56px; }
}

/* ---- Order summary ---- */
.lc-summary { margin: 0; display: flex; flex-direction: column; gap: 0.625rem; }
.lc-summary-row { display: flex; justify-content: space-between; gap: 1rem; font-size: 0.9375rem; }
.lc-summary-row dt { color: var(--text-secondary); }
.lc-summary-row dd { margin: 0; color: var(--text-body); font-variant-numeric: tabular-nums; }
.lc-summary-total { align-items: baseline; margin-top: 0.375rem; padding-top: 0.875rem; border-top: 1px solid var(--border-default); }
.lc-summary-total dt { color: var(--text-heading); font-weight: var(--fw-semibold); }
.lc-summary-total dd { font-size: 1.5rem; }

.lc-consent { display: flex; align-items: flex-start; gap: 0.75rem; cursor: pointer; font-size: 0.875rem; line-height: 1.55; color: var(--text-body); }
.lc-consent input { width: 22px; height: 22px; margin: 0.05rem 0 0; flex: none; accent-color: var(--accent-action); }
.lc-consent input:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.lc-pay-btn { width: 100%; justify-content: center; }
.lc-pay-trust { display: flex; align-items: center; justify-content: center; gap: 0.5rem; margin: 0; font-size: 0.8125rem; color: var(--text-muted); text-align: center; }
.lc-fixed-line { display: flex; justify-content: space-between; gap: 1rem; font-size: 0.875rem; color: var(--text-secondary); }
.lc-field-grid { display: grid; gap: 0.875rem; }
@media (min-width: 640px) { .lc-field-grid { grid-template-columns: 1fr 1fr; } .lc-field-wide { grid-column: 1 / -1; } }
.lc-field-grid .lc-label { display: block; margin-bottom: 0.375rem; }
.lc-field-grid .lc-input { width: 100%; }
.lc-field-optional { font-weight: var(--fw-regular); color: var(--text-muted); }

/* ---- Empty state ---- */
.lc-empty { padding: 3rem 1.5rem; display: flex; flex-direction: column; align-items: center; gap: 0.75rem; text-align: center; }
.lc-empty-icon { font-size: 2rem; color: var(--accent-gold-ink); }
.lc-empty-title { margin: 0; font-family: var(--font-display); font-weight: var(--fw-card-title); font-size: 1.375rem; color: var(--text-heading); }
.lc-empty p { margin: 0; max-width: 32rem; color: var(--text-secondary); line-height: 1.6; }
.lc-empty .lc-btn { margin-top: 0.5rem; }

/* ---- Status hero: icon, heading, one line, next steps ---- */
.lc-status { display: flex; gap: 1rem; align-items: flex-start; padding: 1.5rem; }
.lc-status-icon {
  flex: none;
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
  border: 1px solid currentColor;
  font-size: 1.25rem;
}
.lc-status-ok      .lc-status-icon { color: var(--status-success-ink); background: var(--status-success-tint); }
.lc-status-wait    .lc-status-icon { color: var(--status-warning-ink); background: var(--status-warning-tint); }
.lc-status-problem .lc-status-icon { color: var(--status-danger-ink);  background: var(--status-danger-fill); }
.lc-status-title { margin: 0 0 0.375rem; font-family: var(--font-display); font-weight: var(--fw-card-title); font-size: clamp(1.5rem, 3vw, 2rem); line-height: 1.15; color: var(--text-display); }
.lc-status p { margin: 0; color: var(--text-body); line-height: 1.6; }
.lc-status-actions { display: flex; gap: 0.75rem; flex-wrap: wrap; margin-top: 1rem; }

/* ---- Order history row ---- */
a.lc-order-row { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 1rem; align-items: center; padding: 1rem 1.25rem; text-decoration: none; color: inherit; }
/* ---- Shop page head: breadcrumb and title on the left, the shop cart on the right ----
   The site header's basket is the competitions', so the shop's cart lives here. */
.lc-shop-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 1rem 1.5rem; flex-wrap: wrap; margin-bottom: 1.5rem; }
.lc-page-hero-inner.lc-shop-head { margin-bottom: 0; }
.lc-shop-head > .lc-crumb { margin: 0; align-self: center; }
.lc-cartbox { display: flex; align-items: center; gap: 0.75rem; padding: 0.625rem 0.75rem 0.625rem 0.625rem; color: var(--text-body); }
.lc-cartbox-icon {
  position: relative;
  flex: none;
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
  background: var(--surface-sunken);
  color: var(--accent-gold-ink);
  font-size: 16px;
}
.lc-cartbox-icon .lc-cart-count { top: -6px; right: -6px; }
.lc-cartbox-text { display: flex; flex-direction: column; min-width: 0; margin-right: 0.25rem; }
.lc-cartbox-label { font-family: var(--font-label); font-size: var(--text-2xs, 0.6875rem); letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-secondary); }
.lc-cartbox-value { font-size: 14px; color: var(--text-heading); white-space: nowrap; }
@media (max-width: 639px) {
  .lc-cartbox { width: 100%; }
  .lc-cartbox .lc-btn { margin-left: auto; }
}
.lc-order-row-items { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lc-order-dates { margin: 1rem 0 0; }
.lc-address { margin: 0; font-style: normal; line-height: 1.6; color: var(--text-body); }
a.lc-order-row:hover { border-color: var(--border-strong); }
a.lc-order-row:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 3px; }
.lc-order-row-side { display: flex; flex-direction: column; align-items: flex-end; gap: 0.375rem; }
@media (max-width: 480px) { a.lc-order-row { grid-template-columns: auto minmax(0, 1fr); } .lc-order-row-side { grid-column: 2; align-items: flex-start; } }

/* ---- Pay bar: the total and Pay, fixed to the bottom under 1024px ---- */
.lc-paybar { display: none; }
@media (max-width: 1023px) {
  .lc-paybar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 70;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.75rem var(--gutter);
    padding-bottom: calc(0.75rem + env(safe-area-inset-bottom));
    background: var(--surface-card);
    border-top: 1px solid var(--border-soft);
    box-shadow: var(--shadow-bar-up);
  }
  .lc-paybar-total { display: flex; flex-direction: column; line-height: 1.15; }
  .lc-paybar-total span { font-size: 0.75rem; color: var(--text-muted); }
  .lc-paybar-total .lc-price { font-size: 1.375rem; }
  .lc-paybar .lc-btn { flex: 0 1 14rem; justify-content: center; }
  /* The in-page Pay is the bar's on a phone, so only one shows. */
  .lc-has-paybar .lc-checkout-aside .lc-pay-btn { display: none; }
  body.lc-has-paybar .lc-footer { padding-bottom: calc(76px + env(safe-area-inset-bottom)); }
  html:has(body.lc-has-paybar) { scroll-padding-bottom: calc(84px + env(safe-area-inset-bottom)); }
}
body.lc-menu-visible .lc-paybar,
body.lc-consent-pending .lc-paybar { display: none; }
