/* Tile look -------------------------------------------------------------
   Everything a shop can change about how a row or a grid of tiles looks.
   It is all .tile-look--* modifiers on the root — the strip's or the
   grid's — and variables the tile reads, so nothing here reaches a tile
   outside a styled root, and a root with no modifiers renders exactly what
   it rendered before this block existed.

   The strip and the grid share one list because a tile is a tile wherever
   a visitor meets one: a hand-picked list switched from a row to a grid
   keeps its shape, glow and caption. The few rules that name the strip's
   track are the ones a grid has no need of — the seam of a joined run, the
   bleed a scrolling track would clip — and where the grid needs its own
   line, it stands beside the track's.

   How many tiles fit is deliberately NOT here — that is the strip's
   behaviour, it lives on --strip-per-view, and two knobs writing the same
   track width would leave the operator guessing which one wins. */

/* Shape. The first three change the box; the last three also change what
   the box is clipped to, which is why the tile's outline and its hover
   lift stand down for them — a border traces the box, not the clip, so a
   star with a hairline is a star inside a visible square. */
.tile-look--shape-circle {
  --tile-ratio: 1;
  --tile-radius: 50%;
}

.tile-look--shape-square {
  --tile-ratio: 1;
  --tile-radius: var(--radius-lg);
}

.tile-look--shape-rect {
  --tile-ratio: 3 / 4;
  --tile-radius: var(--radius-md);
}

/* One run of covers with no seams. --strip-gap is the strip's own spacing
   variable; the explicit gap beside it is what closes the seam on a track
   that still carries a literal gap. */
.tile-look--shape-joined {
  --tile-ratio: 3 / 4;
  --tile-radius: 0;
  --tile-border: 0;
  --strip-gap: 0;
}

.tile-look--shape-joined .product-strip__track,
.product-grid.tile-look--shape-joined {
  gap: 0;
}

/* Only the two ends of the run are rounded — that is what makes it read as
   one object rather than as tiles that happen to touch. */
.tile-look--shape-joined .product-strip__track > :first-child .product-tile__media {
  border-radius: var(--radius-md) 0 0 var(--radius-md);
}

.tile-look--shape-joined .product-strip__track > :last-child .product-tile__media {
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
}

/* The cover is cut to shape, so the box that holds it must stop drawing
   itself: an outline and a fill would show as the square the star was cut
   out of. */
.tile-look--shape-star,
.tile-look--shape-sparkle {
  --tile-ratio: 1;
  --tile-radius: 0;
  --tile-border: 0;
  --tile-bg: transparent;
  --tile-hover-shadow: none;
}

.tile-look--shape-star {
  --tile-clip: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);
}

.tile-look--shape-sparkle {
  --tile-clip: polygon(50% 0%, 58% 32%, 72% 12%, 66% 34%, 88% 28%, 68% 43%, 100% 50%, 68% 57%, 88% 72%, 66% 66%, 72% 88%, 58% 68%, 50% 100%, 42% 68%, 28% 88%, 34% 66%, 12% 72%, 32% 57%, 0% 50%, 32% 43%, 12% 28%, 34% 34%, 28% 12%, 42% 32%);
}

/* A clipped tile has no box to lift, so its hover feedback is a glow that
   follows the clip. A shop that sets its own glow overrides this below. */
.tile-look--shape-star .product-tile:hover,
.tile-look--shape-sparkle .product-tile:hover {
  --tile-filter: drop-shadow(0 0 14px color-mix(in srgb, var(--primary) 55%, transparent));
}

/* A badge needs a corner to sit in. These three shapes have none, so the
   badge is dropped rather than left floating over a point of the star. */
.tile-look--shape-circle .product-tile__badges,
.tile-look--shape-star .product-tile__badges,
.tile-look--shape-sparkle .product-tile__badges {
  display: none;
}

/* ...and with it the Soldout badge, which left the grey cover as the only
   sign. The caption says it in words instead. */
.tile-look--shape-circle .product-tile__soldout,
.tile-look--shape-star .product-tile__soldout,
.tile-look--shape-sparkle .product-tile__soldout {
  display: block;
}

/* A shape with its caption switched off would lose the words as well, so a
   sold-out tile there keeps the caption box for that one line alone. */
.tile-look--cap-off:is(.tile-look--shape-circle, .tile-look--shape-star, .tile-look--shape-sparkle) .product-tile--soldout .product-tile__body {
  display: block;
  padding-top: var(--space-2);
}

.tile-look--cap-off .product-tile--soldout .product-tile__body > :not(.product-tile__soldout) {
  display: none;
}

/* Glow. --strip-glow is 0..1 and scales both the reach and the opacity, so
   one number covers "barely there" and "lightstick at a stadium". The near
   shadow draws the edge, the far one is the halo. A lit strip drops the
   tile's own hover lift: two glows on one cover is a smear, not an accent.

   The value is defined once and pointed at twice, because "always" and
   "under the cursor" are the same light in two different places. */
.tile-look--glow,
.tile-look--glow-hover {
  --tile-hover-shadow: none;
  --strip-glow-filter:
    drop-shadow(0 0 calc(2px + 8px * var(--strip-glow, 0.5)) color-mix(in srgb, var(--strip-glow-color, var(--primary)) calc(90% * var(--strip-glow, 0.5)), transparent))
    drop-shadow(0 0 calc(6px + 24px * var(--strip-glow, 0.5)) color-mix(in srgb, var(--strip-glow-color, var(--primary)) calc(45% * var(--strip-glow, 0.5)), transparent));
}

.tile-look--glow {
  --tile-filter: var(--strip-glow-filter);
}

.tile-look--glow .product-tile:hover,
.tile-look--glow-hover .product-tile:hover {
  --tile-filter: var(--strip-glow-filter);
}

/* The track scrolls, which means it clips: overflow-x: auto computes
   overflow-y to auto as well, so anything painted outside the track's
   padding box is cut off — and a glow is painted entirely outside its
   tile. With the track's usual 8px of padding, a halo reaching 50px came
   out with a hard rectangular edge sawn through it.

   So a lit strip pads its track by the glow's own reach and pulls the same
   amount back in margin: the halo gets room, and the strip occupies
   exactly the space it did before. The two clipped shapes are in here too
   — their hover glow is fixed rather than configurable, and --strip-glow
   falls back to a value that clears it.

   The bleed is half again the far shadow's blur, not equal to it: a blur
   radius is the Gaussian's parameter, and the light it puts down still
   reads about 1.5x further out. Padded to the blur alone, the halo came
   out with a rectangular edge sawn through its faint half. */
.tile-look--glow .product-strip__track,
.tile-look--glow-hover .product-strip__track,
.tile-look--shape-star .product-strip__track,
.tile-look--shape-sparkle .product-strip__track {
  --strip-bleed: calc(12px + 40px * var(--strip-glow, 0.4));
  padding: var(--strip-bleed);
  margin: calc(-1 * var(--strip-bleed));
  /* Snapping measures from the scrollport, which is the padding box, so a
     track that grows its padding snaps the first tile that far past its
     own start — the strip loaded already scrolled, with the first cover
     half off the edge. scroll-padding tells the snap where the content
     really begins. */
  scroll-padding: var(--strip-bleed);
}

/* Everything but the tile under the cursor steps back. :has on the track
   rather than on the strip, so the arrows and the section heading keep
   their colour — it is the row of covers that recedes, not the section.
   A grid has no track and no arrows, so there :has sits on the grid itself.

   The filter sits on the tile while the glow sits on its cover, so the two
   compose instead of overwriting each other. */
.tile-look--dim .product-strip__track:has(.product-tile:hover) .product-tile:not(:hover),
.product-grid.tile-look--dim:has(.product-tile:hover) .product-tile:not(:hover) {
  filter: grayscale(var(--strip-dim, 0.6)) opacity(calc(1 - 0.5 * var(--strip-dim, 0.6)));
  transition: filter var(--dur-base);
}

/* Caption. --cap-off hides the box outright and closes the gap it left;
   without that the tile keeps a row of empty space under every cover. */
.tile-look--cap-off .product-tile__body {
  display: none;
}

.tile-look--cap-off .product-tile {
  gap: 0;
}

/* The caption on the cover instead of under it. The box leaves the flow, so
   the tile is exactly as tall as its cover, and a band of the page's own
   surface carries the text (below) — a photograph is not a background you
   can rely on for contrast. On the band the caption reads in the page's
   ink, in both themes; a shop that picks its own caption colour still
   wins, because that colour is set on the name itself. */
.tile-look--cap-over .product-tile {
  position: relative;
}

.tile-look--cap-over .product-tile__body {
  position: absolute;
  inset: 0;
  z-index: 1;
  justify-content: var(--tile-cap-anchor, flex-end);
  padding: var(--space-3);
  border-radius: var(--tile-radius, var(--radius-lg));
  overflow: hidden;
  /* The whole tile is the link; the caption must not sit between the
     pointer and it. */
  pointer-events: none;
}

/* The caption sits on a band of the page's own surface, exactly as tall as
   its lines: each line carries the fill, the first and the last round the
   band's corners. It replaced a gradient scrim that ran the tile's full
   height, which on a white packshot — most album covers — read as a grey
   smear and put the name across the cover's own lettering. Where the band
   stands (top, middle, bottom), how the text is set and what colour it
   wears are the section's settings, untouched: they live on the box above
   and on the lines themselves. */
.tile-look--cap-over .product-tile__body > * {
  padding-inline: var(--space-3);
  background: color-mix(in srgb, var(--surface) 92%, transparent);
}

.tile-look--cap-over .product-tile__body > :first-child {
  padding-top: var(--space-2);
  border-start-start-radius: var(--radius-md);
  border-start-end-radius: var(--radius-md);
}

.tile-look--cap-over .product-tile__body > :last-child {
  padding-bottom: var(--space-2);
  border-end-start-radius: var(--radius-md);
  border-end-end-radius: var(--radius-md);
}

/* The band is opaque, so a badge in the cover's corner must stand above
   it — a presale mark under a top-anchored caption would vanish. */
.tile-look--cap-over .product-tile__badges {
  z-index: 2;
}

/* On the band the price is one line among two: the gap it keeps under a
   tile would split the band, so it becomes padding inside it. */
.tile-look--cap-over .product-tile__body > .price {
  margin-top: 0;
  padding-top: var(--space-1);
}

.tile-look--cap-top {
  --tile-cap-anchor: flex-start;
}

.tile-look--cap-middle {
  --tile-cap-anchor: center;
}

.tile-look--cap-bottom {
  --tile-cap-anchor: flex-end;
}

/* A star has no middle wide enough to hold a line of text and no edge to
   run it along, so these two keep their caption under the shape however the
   section is set. Refusing the setting beats printing a name across three
   points of a star. */
.tile-look--shape-star.tile-look--cap-over .product-tile__body > *,
.tile-look--shape-sparkle.tile-look--cap-over .product-tile__body > * {
  padding: 0;
  background: none;
  border-radius: 0;
}

.tile-look--shape-star.tile-look--cap-over .product-tile__body,
.tile-look--shape-sparkle.tile-look--cap-over .product-tile__body {
  position: static;
  padding: 0;
  background: none;
  color: inherit;
}

.tile-look--no-meta .product-tile__meta,
.tile-look--no-meta .price {
  display: none;
}

.tile-look--text-left {
  --tile-text-align: left;
  --tile-text-justify: flex-start;
}

.tile-look--text-center {
  --tile-text-align: center;
  --tile-text-justify: center;
}

.tile-look--text-right {
  --tile-text-align: right;
  --tile-text-justify: flex-end;
}

/* Justified text has no flex counterpart, so the price line starts where a
   left-aligned one would. It is also the one alignment that does nothing
   to a caption short enough to fit on one line — which most of them are. */
.tile-look--text-justify {
  --tile-text-align: justify;
  --tile-text-justify: flex-start;
}

.tile-look--text-xs { --tile-text-size: var(--text-xs); }
.tile-look--text-s  { --tile-text-size: var(--text-sm); }
.tile-look--text-m  { --tile-text-size: var(--text-base); }
.tile-look--text-l  { --tile-text-size: var(--text-lg); }
.tile-look--text-xl { --tile-text-size: var(--text-xl); }

/* The glow's palette. The shop picks a name, the stylesheet owns the value —
   nothing that reaches a page here was typed into a form.

   Violet and mint come from their own tokens rather than the interface
   palette: they belong to the pictures, so they do not turn with the theme,
   for the same reason --hero-ink does not. */
.tile-look--glow-primary   { --strip-glow-color: var(--primary); }
.tile-look--glow-secondary { --strip-glow-color: var(--secondary); }
.tile-look--glow-violet    { --strip-glow-color: var(--accent-violet); }
.tile-look--glow-mint      { --strip-glow-color: var(--accent-mint); }
.tile-look--glow-gold      { --strip-glow-color: var(--warning); }
.tile-look--glow-white     { --strip-glow-color: var(--white); }

/* The caption's palette. No white: a caption sits on the page's surface —
   under the cover, or on the band over it — and white on the light theme's
   surface cannot be read. */
.tile-look--ink-muted     { --tile-text-color: var(--ink-muted); }
.tile-look--ink-primary   { --tile-text-color: var(--primary); }
.tile-look--ink-secondary { --tile-text-color: var(--secondary); }
.tile-look--ink-gold      { --tile-text-color: var(--warning); }

.tile-look--text-brand { --tile-text-font: var(--font-brand); }
.tile-look--text-w400 { --tile-text-weight: 400; }
.tile-look--text-w900 { --tile-text-weight: 900; }
.tile-look--text-italic { --tile-text-style: italic; }

.tile-look--text-caps {
  --tile-text-transform: uppercase;
  --tile-text-tracking: 0.06em;
}

/* Reduced motion takes away the movement, not the information: the glow
   still lights and the other tiles still recede, they just stop easing
   into it. Same rule the strip's autoplay follows. */
@media (prefers-reduced-motion: reduce) {
  .product-tile__media,
  .tile-look--dim .product-strip__track:has(.product-tile:hover) .product-tile:not(:hover),
  .product-grid.tile-look--dim:has(.product-tile:hover) .product-tile:not(:hover) {
    transition: none;
  }
}

/* Tile grid ------------------------------------------------------------
   An item-backed image grid: the number of columns and the shape of the box
   come from the section, so one stylesheet draws a row of groups, a wall of
   categories and a strip of lookbook shots. The hover treatment is the
   product tile's, because a tile is a tile wherever a visitor meets one. */

.tile-grid {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: 1fr;
}

@media (min-width: 640px) {
  .tile-grid--2 {
    grid-template-columns: repeat(2, 1fr);
  }

  .tile-grid--3 {
    grid-template-columns: repeat(3, 1fr);
  }

  .tile-grid--4 {
    grid-template-columns: repeat(4, 1fr);
  }
}

.tile {
  display: block;
  color: inherit;
  text-decoration: none;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  transition: box-shadow var(--dur-base), border-color var(--dur-base);
}

a.tile:hover {
  border-color: var(--primary);
  box-shadow: var(--glow-pink);
}

.tile__media {
  aspect-ratio: 1;
  overflow: hidden;
  background: var(--surface-raised);
}

/* A tile with no photo is drawn, not left blank: in the light theme the
   raised surface is the same white as the tile itself, so the box would
   vanish and the grid would lose the rhythm the empty cell is there to
   keep. Hatching on --line reads as an intentional gap in both themes. */
.tile__media--empty {
  background: repeating-linear-gradient(45deg, transparent 0 8px, var(--line) 8px 9px);
  border-bottom: 1px dashed var(--line);
}

.tile-grid--portrait .tile__media {
  aspect-ratio: 4 / 5;
}

.tile-grid--wide .tile__media {
  aspect-ratio: 16 / 9;
}

.tile__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--dur-base) var(--ease-out);
}

a.tile:hover .tile__media img {
  transform: scale(1.03);
}

.tile__caption {
  padding: var(--space-3) var(--space-4);
}

.tile__title {
  margin: 0;
  font-size: var(--text-lg);
  font-weight: 700;
  line-height: 1.3;
}

.tile__body {
  margin: var(--space-1) 0 0;
  font-size: var(--text-sm);
  color: var(--ink-muted);
}

/* An overlaid caption sits on a scrim over the photo. The body would need a
   taller scrim than a picture can carry, so the overlay shows the title
   alone — the option label itself carries that warning ("title only"). */
.tile-grid--caption-overlay .tile {
  position: relative;
}

.tile-grid--caption-overlay .tile__caption {
  position: absolute;
  inset: auto 0 0 0;
  padding: var(--space-6) var(--space-4) var(--space-4);
  color: var(--hero-ink);
  background: linear-gradient(to top, color-mix(in srgb, var(--scrim-ink) 65%, transparent), transparent);
}

.tile-grid--caption-overlay .tile__body {
  display: none;
}

@media (prefers-reduced-motion: reduce) {
  .tile__media img {
    transition: none;
  }

  a.tile:hover .tile__media img {
    transform: none;
  }
}
