/* ============================================================================
   The grid
   ---------------------------------------------------------------------------
   96 cells at every breakpoint:  12x8 desktop / 8x12 tablet / 6x16 mobile.
   Each spot keeps its exact cell count across all three, so a sponsor holds the
   same share of the screen on a phone as on a 27" display. Only the shape
   changes. One DOM order packs all three layouts via `grid-auto-flow: dense`.

   Spans arrive as inline custom properties from js/grid.js:
     --dc/--dr  desktop   --tc/--tr  tablet   --mc/--mr  mobile
   ========================================================================= */

.grid {
  --cols: 12;
  --rows: 8;
  --gap: clamp(5px, 0.62vw, 9px);
  --grid-h: clamp(400px, 58vh, 580px);

  display: grid;
  grid-template-columns: repeat(var(--cols), minmax(0, 1fr));
  grid-template-rows: repeat(var(--rows), minmax(0, 1fr));
  grid-auto-flow: row dense;
  gap: var(--gap);
  height: var(--grid-h);
  padding: var(--gap);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--bg-sunken);
  transition: background-color var(--t-base) var(--ease), border-color var(--t-base) var(--ease);
}

@media (max-width: 1023px) {
  .grid {
    --cols: 8;
    --rows: 12;
    --grid-h: clamp(520px, 74vh, 720px);
  }
}

@media (max-width: 639px) {
  .grid {
    --cols: 6;
    --rows: 16;
    --grid-h: clamp(540px, 78vh, 700px);
  }
}

/* ------------------------------------------------------------------- Spot */

.spot {
  --c: var(--dc);
  --r: var(--dr);
  grid-column: span var(--c);
  grid-row: span var(--r);

  position: relative;
  container-type: size;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border-radius: var(--r-md);
  text-align: center;
  cursor: pointer;
  transition: transform var(--t-base) var(--ease), box-shadow var(--t-base) var(--ease),
    background-color var(--t-base) var(--ease), border-color var(--t-base) var(--ease);
}

@media (max-width: 1023px) {
  .spot {
    --c: var(--tc);
    --r: var(--tr);
  }
}

@media (max-width: 639px) {
  .spot {
    --c: var(--mc);
    --r: var(--mr);
  }
}

.spot:hover {
  z-index: 3;
}

.spot:focus-visible {
  z-index: 4;
  outline-offset: 2px;
}

/* ------------------------------------------------------------ Taken spots */

.spot--taken {
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-sm);
}

.spot--taken:hover {
  transform: translateY(-3px);
  border-color: var(--line-strong);
  box-shadow: var(--shadow-md);
}

/* Every length in here is a container-query unit, deliberately. Percentages
   resolve against the container's INLINE size even for padding-block, which on
   a 553x120 marquee tile means 8% padding = 43px top and bottom — it crushes
   the logo on exactly the widest, most expensive squares. cqmin/cqh/cqw scale
   against the axis actually meant. */
.spot__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(2px, 5cqmin, 9px);
  width: 100%;
  height: 100%;
  padding: clamp(4px, 9cqmin, 18px);
}

/* Logo caps are per tier so every brand reads at a comparable visual weight
   whatever the aspect ratio of the tile it sits in. */
.spot__logo {
  width: auto;
  height: auto;
  max-width: var(--logo-w, 60cqw);
  max-height: var(--logo-h, 50cqh);
  object-fit: contain;
  transition: transform var(--t-base) var(--ease);
}

.spot[data-tier='marquee'] {
  --logo-w: 32cqw;
  --logo-h: 54cqh;
}
.spot[data-tier='hero'] {
  --logo-w: 44cqw;
  --logo-h: 54cqh;
}
.spot[data-tier='feature'] {
  --logo-w: 60cqw;
  --logo-h: 50cqh;
}
.spot[data-tier='starter'] {
  --logo-w: 62cqw;
  --logo-h: 58cqh;
}

.spot--taken:hover .spot__logo {
  transform: scale(1.05);
}

/* --------------------------------------------------------- Reserved spots
   Paid, not yet on the grid — Checkout collects money, not artwork, so the
   spot sits here until an admin finishes the assignment with the logo the
   sponsor emails over. Stays clickable while bidding is open (it can still
   be outbid); only once the auction closes, or this one square is locked
   early, does the button disable — see grid.js. */
.spot--reserved {
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--ink-3);
}

.spot--reserved:hover {
  transform: translateY(-3px);
  border-color: var(--line-strong);
}

.spot--reserved:disabled {
  cursor: default;
}

.spot--reserved:disabled:hover {
  transform: none;
}

.spot__lock {
  width: clamp(11px, 16cqmin, 20px);
  height: clamp(11px, 16cqmin, 20px);
  opacity: 0.55;
}

.spot__reserved-label {
  display: none;
  font-size: clamp(8px, 8cqmin, 10px);
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  opacity: 0.7;
}

@container (min-height: 72px) and (min-width: 104px) {
  .spot__reserved-label {
    display: block;
  }
}

.spot__name {
  display: none;
  font-size: clamp(9px, 11cqmin, 14px);
  font-weight: 550;
  letter-spacing: -0.01em;
  line-height: 1.2;
  color: var(--ink-2);
  max-width: 92%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Only show the wordmark where there is genuinely room for it. Container
   queries mean this decides per tile, not per breakpoint. */
@container (min-height: 72px) and (min-width: 104px) {
  .spot__name {
    display: block;
  }
}

/* Wide tiles — the Marquee above all — stack badly: a square logo centred in a
   534x116 box leaves two empty halves. Lay those out as a horizontal lockup
   instead, which is also the shape a sponsor's real artwork will take. */
@container (min-aspect-ratio: 5 / 2) {
  .spot__inner {
    flex-direction: row;
    gap: clamp(5px, 7cqmin, 14px);
  }

  .spot__logo {
    max-width: 30cqw;
    max-height: 64cqh;
  }

  .spot__name {
    font-size: clamp(10px, 15cqmin, 19px);
    color: var(--ink);
  }
}

/* Visit affordance, revealed on hover. */
.spot__visit {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  padding: 4px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--accent-ink);
  background: var(--accent);
  opacity: 0;
  transform: translateY(100%);
  transition: opacity var(--t-fast) var(--ease), transform var(--t-base) var(--ease);
}

@container (min-height: 70px) {
  .spot--taken:hover .spot__visit,
  .spot--taken:focus-visible .spot__visit {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Current-bid pill on a taken/reserved tile — the reminder that it's still
   biddable, not settled, until the deadline. */
.spot__bid-badge {
  position: absolute;
  top: clamp(4px, 6cqmin, 8px);
  inset-inline-end: clamp(4px, 6cqmin, 8px);
  padding: 3px 7px;
  border-radius: var(--r-pill);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: clamp(7px, 8cqmin, 10px);
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--accent-ink);
  background: var(--accent);
  box-shadow: var(--shadow-sm);
}

/* Marks a seeded placeholder logo so it never reads as a real sponsor —
   opposite corner from the bid badge (a demo tile can carry both at once,
   once a real bid lands on a square still showing example art). Muted on
   purpose: informational, not another call-to-action-colored highlight.
   Hidden in Final Look, which already declares the whole grid an example
   via its own banner (see .final__demo-note). */
.spot__example-badge {
  position: absolute;
  top: clamp(4px, 6cqmin, 8px);
  inset-inline-start: clamp(4px, 6cqmin, 8px);
  padding: 3px 7px;
  border-radius: var(--r-pill);
  font-size: clamp(7px, 8cqmin, 10px);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--ink-2);
  background: var(--surface-2);
  border: 1px solid var(--line);
}

.grid--final .spot__example-badge {
  display: none;
}

/* ------------------------------------------------------------- Open spots */

.spot--open {
  background: var(--accent-soft);
  border: 1px dashed var(--accent-line);
  color: var(--accent-text);
}

.spot--open:hover {
  transform: translateY(-3px);
  background: var(--accent-soft-2);
  border-style: solid;
  box-shadow: var(--shadow-accent);
}

.spot__plus {
  font-size: clamp(12px, 16cqmin, 24px);
  font-weight: 400;
  line-height: 1;
  opacity: 0.85;
  transition: transform var(--t-base) var(--ease-out-back);
}

.spot--open:hover .spot__plus {
  transform: rotate(90deg) scale(1.15);
}

.spot__price {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: clamp(10px, 13cqmin, 15px);
  font-weight: 500;
  letter-spacing: -0.02em;
}

.spot__tier {
  display: none;
  font-size: clamp(8px, 8cqmin, 10px);
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  opacity: 0.6;
}

@container (min-height: 72px) and (min-width: 104px) {
  .spot__tier {
    display: block;
  }
}

/* --------------------------------------------------- Entrance choreography
   Cells resolve in reading order. One curve, one duration, index-based delay
   supplied by JS as --i. This is the page's signature motion. */

.grid--enter .spot {
  opacity: 0;
  transform: translateY(10px) scale(0.965);
  animation: spot-in var(--t-slow) var(--ease) forwards;
  animation-delay: calc(var(--i, 0) * 26ms);
}

@keyframes spot-in {
  to {
    opacity: 1;
    transform: none;
  }
}

/* ------------------------------------------------------------------- Hunt
   Fired by the "Claim a square" buttons: every still-available square pulses
   once so the eye lands on the offer rather than on a form. */

.grid--hunt .spot--open {
  animation: hunt 900ms var(--ease) 2;
  animation-delay: calc(var(--i, 0) * 14ms);
}

@keyframes hunt {
  40% {
    background: var(--accent);
    border-color: var(--accent);
    border-style: solid;
    color: var(--accent-ink);
    transform: scale(1.03);
  }
}

/* ------------------------------------------------------------- Final look
   The rendering a sponsor is actually buying: no prices, no dashes, no chrome.
   Free spots stay visible but go quiet so they read as vacancy, not as noise. */

/* Vacancy, not inventory: no price, no plus, no tier name — those are campaign
   language and have no place in the rendering a sponsor is buying. A dashed
   hairline on the page ground is enough to read as "empty". */
.grid--final .spot--open {
  background: transparent;
  border: 1px dashed var(--line-strong);
  color: var(--ink-3);
  box-shadow: none;
}

.grid--final .spot--open:hover {
  transform: none;
  background: var(--surface);
  border-color: var(--line-strong);
  box-shadow: none;
}

.grid--final .spot--open .spot__price,
.grid--final .spot--open .spot__plus,
.grid--final .spot--open .spot__tier {
  display: none;
}

.grid--final .spot__bid-badge {
  display: none;
}

.spot__available {
  display: none;
}

.grid--final .spot__available {
  display: block;
  font-size: clamp(8px, 9cqmin, 11px);
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  opacity: 0.7;
}

.grid--final .spot--taken {
  background: var(--surface);
}

.grid--final .spot__visit {
  display: none;
}

/* --------------------------------------------------------- Reduced motion */

@media (prefers-reduced-motion: reduce) {
  .grid--enter .spot,
  .grid--hunt .spot--open {
    animation: none;
    opacity: 1;
    transform: none;
  }
  .spot,
  .spot__logo,
  .spot__plus,
  .spot__visit {
    transition-duration: 1ms;
  }
  .spot:hover {
    transform: none;
  }
}
