/* ========================================
   CARDS
   ======================================== */

/* Category Cards
   Every card in a row must share one silhouette, so the image frame owns its
   size and the caption only ever hangs below it. The frame needs flex-basis
   and min-height: 0 because it is a flex item — a portrait photo's intrinsic
   height is that box's automatic minimum size, so without them a 3:4 photo
   stretches the "square" and the whole row goes ragged. The photo is taken out
   of flow for the same reason: it crops into the frame, it never dictates it. */
.category-card {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--color-white);
  border-radius: var(--radius-xl);
  overflow: hidden;
  box-shadow: var(--shadow-light);
  transition: var(--transition-smooth);
  cursor: pointer;
}

.category-card:hover,
.category-card:focus-visible {
  transform: translateY(-8px);
  box-shadow: var(--shadow-card);
}

.category-card:focus-visible {
  outline: var(--border-width-focus) solid var(--color-dusty-rose-strong);
  outline-offset: 3px;
}

.category-card__image {
  position: relative;
  flex: 0 0 auto;
  min-height: 0;
  aspect-ratio: var(--aspect-square);
  overflow: hidden;
  background: var(--gradient-cream);
  display: flex;
  align-items: center;
  justify-content: center;
}

.category-card__icon {
  width: 60px;
  height: 60px;
  color: var(--color-dusty-rose);
  opacity: var(--opacity-icon-faint);
}

.category-card__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: var(--transition-smooth);
}

/* The frame zooms from inside its own clipping edge, so the crop enlarges
   instead of the block growing over the caption. */
.category-card:hover .category-card__img,
.category-card:focus-visible .category-card__img {
  transform: scale(1.05);
}

.category-card__content {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Caption hangs from the bottom edge: the count keeps one position across a
     row, and a one-line name stays 4px above it instead of floating with a
     two-line gap punched between the pair. The spare line goes above the
     title, where it reads as padding. */
  justify-content: flex-end;
  padding: var(--space-sm) var(--space-md) var(--space-md);
  text-align: center;
}

.category-card__title {
  font-family: var(--font-display);
  font-size: 1.1rem;
  font-weight: var(--font-weight-medium);
  line-height: var(--line-height-tight-label);
  color: var(--color-charcoal);
  margin-bottom: 4px;
  transition: var(--transition-fast);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

.category-card:hover .category-card__title,
.category-card:focus-visible .category-card__title {
  color: var(--color-dusty-rose-strong);
}

.category-card__count {
  font-size: 0.85rem;
  letter-spacing: var(--letter-spacing-xs);
  color: var(--color-gray-600);
}

@media (max-width: 768px) {
  /* Narrow columns: truncating a catalogue name is worse than a taller card,
     so below the tablet line the name shows in full. */
  .category-card__title {
    display: block;
    -webkit-line-clamp: unset;
    line-clamp: unset;
    overflow: visible;
  }
}

@media (prefers-reduced-motion: reduce) {
  .category-card,
  .category-card__img,
  .category-card__title {
    transition: none;
  }

  .category-card:hover,
  .category-card:focus-visible,
  .category-card:hover .category-card__img,
  .category-card:focus-visible .category-card__img {
    transform: none;
  }
}

/* Product Cards
   The card is the object; the photograph lies on it. The card used to be painted the same cream as
   the page and the photograph the same grey as the card, so a shelf of goods read as one continuous
   wash and only the hover told you where one thing ended and the next began. A white surface, a
   hairline of a border, and a picture inset within it say the same thing before the cursor moves.

   The same markup serves the rows of the list view (see `products-grid--list` in _category.css).
   That is why the price, the certificate and the call to action live in an "aside" the grid
   collapses with `display: contents`: one card, two shapes, and no second copy of a product
   waiting to fall out of date. */
.product-card {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--color-white);
  border: var(--border-width) solid var(--color-gray-200);
  border-radius: var(--radius-xl);
  transition: var(--transition-smooth);
}

.product-card:hover,
.product-card:focus-within {
  border-color: var(--color-dusty-rose-light);
  box-shadow: var(--shadow-md);
  transform: translateY(-4px);
}

/* The frame keeps a margin of its own, so the photograph reads as a thing laid on the card rather
   than as the card. Its corners are cut one step inside the card's. */
.product-card__image {
  position: relative;
  aspect-ratio: var(--aspect-square);
  margin: 10px 10px 0;
  overflow: hidden;
  background: var(--gradient-canvas);
  border-radius: var(--radius-lg);
}

.product-card__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: var(--transition-smooth);
}

/* Stays out of flow itself: the photograph positions itself against the frame, and a link that
   became a containing block would collapse to nothing. */
.product-card__image-link {
  display: block;
  text-decoration: none;
}

.product-card:hover .product-card__img {
  transform: scale(1.035);
}

.product-card__badge {
  position: absolute;
  z-index: 1;
  top: var(--space-xs);
  left: var(--space-xs);
  padding: 4px 10px;
  background: var(--color-terracotta-strong);
  border-radius: var(--radius-pill);
  color: var(--color-white);
  font-size: 0.72rem;
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--letter-spacing-xs);
}

/* The discount sits in one corner of the picture and the heart in the other, which is the pair a
   shopper already reads on any shelf. It stays hidden until the card is looked at or landed on —
   except under a finger, where there is no hover to wait for. */
.product-card__wishlist {
  position: absolute;
  z-index: 2;
  top: var(--space-xs);
  right: var(--space-xs);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  background: var(--color-white);
  border: var(--border-width) solid var(--color-gray-200);
  border-radius: 50%;
  color: var(--color-gray-600);
  opacity: 0;
  transform: scale(0.85);
  transition: var(--transition-fast);
}

.product-card:hover .product-card__wishlist,
.product-card:focus-within .product-card__wishlist {
  opacity: 1;
  transform: scale(1);
}

.product-card__wishlist:hover,
.product-card__wishlist.is-active {
  color: var(--color-terracotta-strong);
  border-color: var(--color-dusty-rose);
}

.product-card__wishlist.is-active svg {
  fill: currentColor;
}

.product-card__wishlist:focus-visible {
  opacity: 1;
  transform: scale(1);
  outline: var(--border-width-focus) solid var(--color-dusty-rose-strong);
  outline-offset: 2px;
}

@media (hover: none) {
  .product-card__wishlist {
    opacity: 1;
    transform: none;
  }
}

/* The mark under the photograph, and so under nothing at all when a picture is still to be
   taken. Above the sold-out wash and below the label, so it never competes with either. */
.product-card__placeholder {
  position: absolute;
  top: 50%;
  left: 50%;
  width: clamp(48px, 22%, 76px);
  height: auto;
  aspect-ratio: 1;
  color: var(--color-dusty-rose);
  opacity: var(--opacity-icon-soft);
  transform: translate(-50%, -50%);
}

.product-card__content {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: var(--space-sm) var(--space-sm) var(--space-md);
}

/* In a grid the two halves are not halves: their children are the card's own lines. */
.product-card__details,
.product-card__aside {
  display: contents;
}

.product-card__category {
  margin-bottom: 2px;
  color: var(--color-dusty-rose-strong);
  font-size: 0.72rem;
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--letter-spacing-sm);
  text-transform: uppercase;
}

.product-card__title-link {
  text-decoration: none;
  transition: var(--transition-fast);
}

.product-card__title-link:hover .product-card__title {
  color: var(--color-dusty-rose-strong);
}

.product-card__title {
  font-family: var(--font-display);
  font-size: 1.16rem;
  font-weight: var(--font-weight-medium);
  line-height: var(--line-height-tight-label);
  color: var(--color-charcoal);
  transition: var(--transition-fast);
  /* Three lines and no more. The names in this catalogue lead with the shelf they belong to, so
     the words an ellipsis would take are the words that tell one toy from the next; three lines
     is the most they need. The price is pinned to the bottom of the card either way, so a long
     name does not push it out of the line the row is scanned along. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
}

/* The two things weighed before the click — is it any good, and is there a choice in it — on one
   quiet line, so a row of cards reads as a shelf instead of a stack of opinions. */
.product-card__facts {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 10px;
  color: var(--color-gray-600);
  font-size: 0.76rem;
}

.product-card__rating {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--color-warning);
}

.product-card__stars {
  display: inline-flex;
  gap: 1px;
}

/* Stars say "good"; the figure says how good. */
.product-card__score {
  color: var(--color-charcoal);
  font-weight: var(--font-weight-semibold);
  font-variant-numeric: tabular-nums;
}

.product-card__review-count {
  color: var(--color-gray-600);
  font-weight: var(--font-weight-regular);
}

.product-card__choice {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--color-gray-600);
}

/* Availability
   The grid has no room for a line three quarters of its cards would repeat; the rows do, and a
   row is where a shopper compares. Written positively: the green says what a thing is, and a
   thing that is not in stock says so in its own way, on the picture. */
.product-card__stock {
  display: none;
  align-items: center;
  gap: 6px;
  color: var(--color-success-strong);
  font-weight: var(--font-weight-semibold);
  font-size: 0.78rem;
  font-weight: var(--font-weight-medium);
}

.product-card__dot {
  width: 7px;
  height: 7px;
  background: var(--color-success);
  border-radius: 50%;
}

/* The other answer is still an answer, so it is written in the same place and in the quieter voice. */
.product-card__stock--out {
  color: var(--color-gray-600);
}

.product-card__stock--out .product-card__dot {
  background: var(--color-gray-300);
}

.product-card__price {
  display: flex;
  align-items: baseline;
  gap: var(--space-xs);
  /* A card in a row is as tall as its tallest neighbour, and a price that floats under a two-line
     name lands at a different height than one under a single line. Pinned to the bottom edge, a
     row reads as a shelf of prices — the one thing a shopper scans across. */
  margin-top: auto;
  padding-top: 2px;
}

.product-card__price-current {
  color: var(--color-charcoal);
  font-size: 1.05rem;
  font-weight: var(--font-weight-semibold);
  font-variant-numeric: tabular-nums;
}

.product-card__price-old {
  color: var(--color-gray-600);
  font-size: 0.88rem;
  text-decoration: line-through;
}

/* A second badge hangs below the first, in the quieter of the two voices: a discount is a price and
   gets the loud colour, a new arrival is not. */
.product-card__badge--new {
  top: calc(var(--space-xs) + 30px);
  background: var(--color-white);
  border: var(--border-width) solid var(--color-dusty-rose);
  color: var(--color-dusty-rose-strong);
}

/* Only the row view has the width for a second call to the same address; in a grid the whole card
   is already that call. */
.product-card__cta {
  display: none;
}

/* A third of this catalogue is made to order, so the certificate is a reason to choose this card
   over that one — and it belongs where it is seen before the click, not in a table after it. */
.product-card__mark {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-top: 2px;
  color: var(--color-success-strong);
  font-size: 0.7rem;
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--letter-spacing-xs);
}

/* Sold out
   Named on the card, and the picture steps back so the row does not read as a shelf of things one
   could order. The link stays live: a sold-out crinkle toy is restocked by hand, and its detail
   page is where a shopper waits for it. */
.product-card--sold-out .product-card__image::after {
  position: absolute;
  inset: 0;
  background: rgba(254, 254, 254, 0.55);
  content: "";
}

.product-card--sold-out .product-card__img {
  filter: saturate(0.25);
}

.product-card__soldout {
  position: absolute;
  z-index: 1;
  top: 50%;
  left: 50%;
  max-width: calc(100% - var(--space-sm));
  padding: 6px 14px;
  text-align: center;
  background: var(--color-charcoal);
  border-radius: var(--radius-pill);
  color: var(--color-cream);
  font-size: 0.72rem;
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--letter-spacing-sm);
  text-transform: uppercase;
  transform: translate(-50%, -50%);
}

@media (max-width: 480px) {
  /* A label the width of the picture it stands on is a second picture, not a label. */
  .product-card__soldout {
    padding: 5px 10px;
    font-size: 0.66rem;
    letter-spacing: var(--letter-spacing-xs);
  }

  .category-card__image {
    aspect-ratio: 4 / 3;
  }

  .category-card__content {
    padding: var(--space-xs) var(--space-sm) var(--space-sm);
  }

  .category-card__title {
    font-size: 1rem;
  }

  .product-card__content {
    padding: var(--space-xs) var(--space-xs) var(--space-sm);
  }

  .product-card__title {
    font-size: 1.05rem;
  }

  .product-card__wishlist {
    width: 38px;
    height: 38px;
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .product-card,
  .product-card__img,
  .product-card__wishlist {
    transition: none;
  }

  .product-card:hover,
  .product-card:focus-within {
    transform: none;
  }

  .product-card:hover .product-card__img {
    transform: none;
  }
}

/* Floating Cards (Hero Section) */
.hero__floating-card {
  position: absolute;
  background: var(--color-white);
  border-radius: var(--radius-lg);
  padding: var(--space-sm);
  box-shadow: var(--shadow-float);
  animation: float 4s ease-in-out infinite;
}

.hero__floating-card--1 {
  bottom: 20%;
  left: -10%;
  animation-delay: 0s;
}

.hero__floating-card--2 {
  top: 15%;
  right: -5%;
  animation-delay: 1s;
}

.floating-card__content {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
}

.floating-card__icon {
  width: 40px;
  height: 40px;
  background: var(--color-cream);
  border-radius: var(--radius-md);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-dusty-rose);
}

.floating-card__text {
  font-size: 0.8rem;
}

.floating-card__label {
  color: var(--color-gray-600);
}

.floating-card__value {
  font-weight: var(--font-weight-semibold);
  color: var(--color-charcoal);
}
