/* Kpopownia base styles: reset, typography, layout helpers and the stage
   atmosphere. Requires tokens.css. */

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

/* The hidden attribute wins over any component display rule — the empty cart
   counter (.badge--count[hidden]) must not paint as a bare dot. */
[hidden] {
  display: none !important;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Room the browser leaves when it scrolls a focused element into view:
     the sticky navbar covers the top of the window, the product page's buy
     bar and the cookie banner the bottom, and a field tabbed to under
     either of them was hidden (WCAG 2.4.11). */
  scroll-padding-top: 5rem;
  scroll-padding-bottom: 6rem;
  /* The scrollbar's room is kept on every page, whether it scrolls or not.
     Without it a page too short to scroll (the cart) was 15px wider than
     one that scrolls, the centred navbar jumped sideways between them, and
     the page transition turned that jump into a slide. Overlay scrollbars
     (phones, macOS) take no room and are unaffected. */
  scrollbar-gutter: stable;
}

body {
  margin: 0;
  min-height: 100dvh;
  background: var(--stage);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--leading-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

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

/* A guarded photo (public-web/ui Image sets the attribute on every one the
   shop draws) cannot be selected, and a long press on a phone opens no
   callout, so the browser offers no "save image" there. The panel's
   pictures carry no such attribute, so its crop editor and pickers keep
   theirs. Friction, not protection — the address stays in the page source. */
img[draggable="false"] {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

/* Under a mouse the photo also takes no pointer events: a right click lands
   on the tile or the button under it, so the menu is the link's, with no
   "save image" in it. Only under a mouse — on iOS a finger that lands on a
   picture with no pointer events does not scroll the strip it sits in, so
   the product carousels and the gallery's thumbnails would not swipe. On a
   touch screen the callout rule above and client.ts's contextmenu guard do
   the job instead. */
@media (hover: hover) and (pointer: fine) {
  img[draggable="false"] {
    pointer-events: none;
  }
}

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

button {
  cursor: pointer;
}

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

ul,
ol {
  margin: 0;
  padding: 0;
  list-style: none;
}

p,
h1,
h2,
h3,
h4 {
  margin: 0;
}

/* A control only a browser without a script needs — the submit button of a
   form that submits itself on change once client.js runs. ThemeScript in
   the head marks <html> with .js before the first paint. */
html.js .js-fallback {
  display: none;
}

/* Typography ---------------------------------------------------------- */

h1,
h2,
h3,
.h1,
.h2,
.h3 {
  font-family: var(--font-heading);
  font-weight: 700;
  line-height: var(--leading-heading);
  letter-spacing: 0.01em;
}

h1,
.h1 {
  font-size: var(--text-3xl);
}

h2,
.h2 {
  font-size: var(--text-2xl);
}

h3,
.h3 {
  font-size: var(--text-xl);
}

h4,
.h4 {
  font-family: var(--font-body);
  font-weight: 700;
  font-size: var(--text-lg);
  line-height: var(--leading-heading);
}

.text-soft {
  color: var(--ink-soft);
}

.text-muted {
  color: var(--ink-muted);
}

/* A short line that says what is still missing before the form can go —
   the checkout's "choose a locker first", set in place of the muted hint. */
.text-danger {
  color: var(--danger);
  font-weight: 700;
}

.text-sm {
  font-size: var(--text-sm);
}

.text-xs {
  font-size: var(--text-xs);
}

/* Brand text: the "Kpopownia" treatment from kpopownia.pl — gradient-clipped
   Comfortaa with a pink/blue glow. Use for the logo, hero titles and the
   product page's title only (that page has no hero, so its h1 is the hero). */
.brand-text {
  font-family: var(--font-brand);
  font-weight: 700;
  letter-spacing: var(--tracking-brand);
  background: var(--gradient-brand);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: var(--glow-brand);
}

/* Layout -------------------------------------------------------------- */

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--space-4);
}

@media (min-width: 1024px) {
  .container {
    padding-inline: var(--space-8);
  }
}

/* Glows spill past their boxes on purpose — a gallery's light, a glowing
   row of tiles padded out so its halo is not sawn off — and where the page's
   gutter is narrower than the spill, it ran past the screen and the whole
   page scrolled sideways on a phone. The page clips its own sides instead.
   clip, not hidden: it makes no scroll container, so sticky elements inside
   and the rows that scroll on their own keep working. */
main {
  overflow-x: clip;
}

/* A page body is a .container right under <main>; it sits --space-6 away from
   the navbar and the footer, as every page demo in the styleguide does. The
   order confirmation carries its own, larger padding. */
main > .container:not(.order-confirmation) {
  padding-block: var(--space-6);
}

.stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.stack--lg {
  gap: var(--space-8);
}

.cluster {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
}

.section {
  padding-block: var(--space-10);
}

/* Focus ----------------------------------------------------------------- */

/* One ring for everything that takes focus, in --focus, drawn only for a
   keyboard (:focus-visible). The ring follows the element's own corners —
   a browser rounds an outline with the border-radius it finds — so the
   rule sets none of its own: a radius here changed the shape of a focused
   pill. */
:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* A search hit's matched text. Tinted, not recoloured: the primary at 30%
   over whatever surface the row sits on, so it reads in both themes and a
   marked word keeps its own ink. */
/* A matched word in a search result: underlined in the brand's pink rather
   than boxed — a filled box on a tile's title read as a selection artefact. */
mark {
  background: none;
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--primary);
  text-decoration-thickness: 2px;
  text-underline-offset: 0.15em;
}

/* Accessibility helpers ------------------------------------------------- */

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* The first thing Tab meets on every storefront page: a jump past the bar,
   the header and the category menu to <main>. Off screen until it has focus,
   then drawn over the corner of the page. */
.skip-link {
  position: absolute;
  top: var(--space-2);
  left: var(--space-2);
  z-index: calc(var(--z-toast) + 1);
  padding: var(--space-2) var(--space-4);
  background: var(--surface-raised);
  color: var(--ink);
  font-weight: 700;
  border: 1px solid var(--primary);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-2);
  transform: translateY(calc(-100% - var(--space-4)));
}

.skip-link:focus {
  transform: none;
}

/* <main> takes focus from the skip link and after the cookie banner goes
   (tabindex="-1"); the region itself is not a control, so it draws no ring. */
main[tabindex="-1"]:focus {
  outline: none;
}

/* While the cookie banner stands over the bottom of the screen, its height
   (--cookie-banner-h, set by the consent script) is reserved under the page
   and as scroll padding, so a control focused near the bottom scrolls clear
   of the banner instead of under it. */
html {
  scroll-padding-bottom: var(--cookie-banner-h, 0px);
}

body {
  padding-bottom: var(--cookie-banner-h, 0px);
}

/* Stage atmosphere ------------------------------------------------------
   The concert-stage backdrop from kpopownia.pl: two coloured beams, three
   blurred orbs, a breathing floor glow and a handful of sparkles. Mount it as
   the first child of a positioned, overflow-hidden container (.hero--stage on
   the home page, .product-stage on a product page that asks for a scene). It
   is decorative: aria-hidden, pointer-events none.

   The scene plays for about five seconds after the page loads and then holds
   where it stopped: movement that starts by itself and runs longer than
   five seconds needs a way to stop it (WCAG 2.2.2), and ending it is the
   way that asks nothing of the visitor. Each layer's iteration count is its
   five seconds (plus the part of the cycle a negative delay skips) divided
   by its duration; forwards keeps the frame it ended on, so nothing jumps. */

.stage {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--gradient-hero);
}

.atmosphere {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

.atmosphere__beam {
  position: absolute;
  top: -10%;
  left: 50%;
  width: min(42vw, 320px);
  height: 130%;
  transform-origin: top;
  filter: blur(18px);
  opacity: 0.35;
  mix-blend-mode: screen;
  animation: beam-pulse 7s ease-in-out 0.71 forwards;
}

.atmosphere__beam--pink {
  background: linear-gradient(color-mix(in srgb, var(--primary) 55%, transparent) 0%, color-mix(in srgb, var(--primary) 12%, transparent) 55%, transparent 100%);
  transform: translateX(-120%) rotate(-16deg);
}

.atmosphere__beam--blue {
  background: linear-gradient(color-mix(in srgb, var(--secondary) 55%, transparent) 0%, color-mix(in srgb, var(--secondary) 12%, transparent) 55%, transparent 100%);
  transform: translateX(20%) rotate(16deg);
  animation-delay: -2.2s;
  animation-iteration-count: 1.03;
}

.atmosphere__beam--center {
  width: min(28vw, 220px);
  background: linear-gradient(color-mix(in srgb, var(--white) 28%, transparent) 0%, color-mix(in srgb, var(--primary-tint) 10%, transparent) 45%, transparent 100%);
  transform: translateX(-50%);
  opacity: 0.28;
  filter: blur(22px);
  animation-duration: 9s;
  animation-delay: -1s;
  animation-iteration-count: 0.67;
}

.atmosphere__orb {
  position: absolute;
  border-radius: 50%;
  filter: blur(40px);
  opacity: 0.55;
  mix-blend-mode: screen;
  animation: orb-float 14s ease-in-out 0.36 forwards;
}

.atmosphere__orb--pink {
  left: -12%;
  bottom: -8%;
  width: min(50vw, 420px);
  height: min(50vw, 420px);
  background: radial-gradient(circle at 40% 40%, color-mix(in srgb, var(--primary-tint) 90%, transparent) 0%, color-mix(in srgb, var(--primary-deep) 35%, transparent) 40%, transparent 70%);
  animation-duration: 16s;
  animation-iteration-count: 0.31;
}

.atmosphere__orb--blue {
  right: -14%;
  bottom: -10%;
  width: min(48vw, 400px);
  height: min(48vw, 400px);
  background: radial-gradient(circle at 40% 40%, color-mix(in srgb, var(--secondary-tint) 90%, transparent) 0%, color-mix(in srgb, var(--secondary-deep) 35%, transparent) 42%, transparent 70%);
  animation-duration: 18s;
  animation-delay: -4s;
  animation-iteration-count: 0.5;
}

.atmosphere__orb--white {
  top: 8%;
  left: 50%;
  width: min(30vw, 240px);
  height: min(30vw, 240px);
  transform: translateX(-50%);
  opacity: 0.4;
  background: radial-gradient(circle, color-mix(in srgb, var(--white) 35%, transparent) 0%, color-mix(in srgb, var(--primary-tint) 20%, transparent) 40%, transparent 70%);
  animation-duration: 12s;
  animation-delay: -2s;
  animation-iteration-count: 0.58;
}

.atmosphere__floor {
  position: absolute;
  bottom: 0;
  left: 50%;
  width: min(90vw, 900px);
  height: 28%;
  transform: translateX(-50%);
  filter: blur(8px);
  background: radial-gradient(at 50% 100%, color-mix(in srgb, var(--primary-tint) 28%, transparent) 0%, color-mix(in srgb, var(--secondary) 12%, transparent) 40%, transparent 70%);
  animation: floor-breathe 6s ease-in-out 0.83 forwards;
}

.atmosphere__sparkle {
  position: absolute;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--white);
  box-shadow: 0 0 8px 2px color-mix(in srgb, var(--white) 90%, transparent), 0 0 20px 6px color-mix(in srgb, var(--primary) 35%, transparent);
  /* 0.8 of a twinkle: the last sparkle starts 2.4 s late and is done by 5 s. */
  animation: sparkle-twinkle 3.2s ease-in-out 0.8 both;
}

.atmosphere__sparkle:nth-child(1) { top: 16%; left: 18%; }
.atmosphere__sparkle:nth-child(2) { top: 24%; right: 20%; width: 3px; height: 3px; animation-delay: 0.6s; }
.atmosphere__sparkle:nth-child(3) { top: 58%; left: 12%; width: 4px; height: 4px; animation-delay: 1.3s; }
.atmosphere__sparkle:nth-child(4) { top: 42%; right: 12%; animation-delay: 2s; }
.atmosphere__sparkle:nth-child(5) { bottom: 26%; left: 42%; width: 3px; height: 3px; animation-delay: 1s; }
.atmosphere__sparkle:nth-child(6) { top: 12%; left: 62%; width: 4px; height: 4px; animation-delay: 2.4s; }

/* Sparkles are scattered by nth-child, so wrap them in their own element. */
.atmosphere__sparkles {
  position: absolute;
  inset: 0;
}

[data-theme="light"] .atmosphere__beam,
[data-theme="light"] .atmosphere__orb {
  mix-blend-mode: multiply;
  opacity: 0.22;
}

[data-theme="light"] .atmosphere__sparkle {
  background: var(--primary);
  box-shadow: 0 0 8px 2px color-mix(in srgb, var(--primary) 50%, transparent);
}

@keyframes orb-float {
  0%, 100% { transform: translate(0, 0) scale(1); }
  50% { transform: translate(10px, -14px) scale(1.05); }
}

@keyframes beam-pulse {
  0%, 100% { opacity: 0.22; }
  50% { opacity: 0.42; }
}

@keyframes floor-breathe {
  0%, 100% { opacity: 0.7; transform: translateX(-50%) scaleX(1); }
  50% { opacity: 1; transform: translateX(-50%) scaleX(1.04); }
}

@keyframes sparkle-twinkle {
  0%, 100% { opacity: 0.2; transform: scale(0.7); }
  50% { opacity: 1; transform: scale(1.2); }
}

@keyframes rise-in {
  from { opacity: 0; transform: translateY(18px); }
  to { opacity: 1; transform: translateY(0); }
}

.rise-in {
  animation: rise-in 1.1s var(--ease-out) both;
}

/* Page transitions -----------------------------------------------------
   A link to another page of the shop cross-fades instead of cutting: the
   navbar stays where it is, the page under it leaves upward and the next one
   arrives from below. On a product tile the photo itself travels into the
   product's gallery (the client names the clicked tile on pageswap; the
   gallery carries the same name in packs/product.css).

   Desktop only, on purpose. Field measurements put the cost of a
   cross-document transition at about 70 ms of LCP on a phone, where the
   snapshot and the animation compete with decoding the largest image for
   one slow CPU, and at about 5 ms on a desktop. A phone keeps today's plain
   navigation. Firefox does not do cross-document transitions yet and
   navigates as before. admin.css switches it off for the panel. */

@media (min-width: 1024px) and (prefers-reduced-motion: no-preference) {
  @view-transition {
    navigation: auto;
  }

  .navbar {
    view-transition-name: navbar;
  }

  ::view-transition-old(root) {
    animation: page-out var(--dur-fast) var(--ease-in) both;
  }

  ::view-transition-new(root) {
    animation: page-in var(--dur-base) var(--ease-out) both;
  }

  ::view-transition-group(product-photo) {
    animation-duration: var(--dur-page);
    animation-timing-function: var(--ease-out);
  }
}

@keyframes page-out {
  to { opacity: 0; transform: translateY(-6px); }
}

@keyframes page-in {
  from { opacity: 0; transform: translateY(12px); }
}

/* Motion -------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .atmosphere *,
  .spotlights *,
  .ocean *,
  .hero__title--neon,
  .hero__title--holo,
  .hero--holo::after,
  .rise-in,
  .skeleton::after,
  .toast {
    animation: none !important;
  }

  *,
  *::before,
  *::after {
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  /* The drawer waits for its slide before hiding; with no slide there is
     nothing to wait for, and it would catch clicks for a quarter second. */
  .drawer {
    transition-delay: 0s !important;
  }
}

/* Styleguide-only opt-out so reviewers can preview the reduced-motion state. */
.reduce-motion .atmosphere *,
.reduce-motion .spotlights *,
.reduce-motion .ocean *,
.reduce-motion .hero__title--neon,
.reduce-motion .hero__title--holo,
.reduce-motion .hero--holo::after,
.reduce-motion .rise-in,
.reduce-motion .skeleton::after,
.reduce-motion .toast {
  animation: none !important;
}
