/* ==========================================================================
   COMPONENTS — design-rule.md §8.
   Global law: border-radius 0 everywhere except the custom cursor.
   Shadows only on physical objects (polaroids, crumpled sheets).
   ========================================================================== */

/* ------------------------------------------------------------------ BUTTONS */

.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
  padding: 18px 34px;
  border: 1px solid transparent;
  border-radius: 0;
  background: var(--ink);
  color: var(--fg-invert);
  font-family: var(--f-micro);
  font-size: var(--t-micro);
  text-transform: uppercase;
  letter-spacing: var(--tr-micro);
  cursor: pointer;
  transition:
    background var(--d-base) var(--ease-out),
    color var(--d-base) var(--ease-out),
    transform var(--d-base) var(--ease-out);
}
.btn:hover { background: var(--accent); transform: translateY(-4px); }
/* `display: inline-flex` above outranks the UA sheet's `[hidden] { display:none }`,
   so without this the "Load more" button stays visible on a filter that has no
   second page. */
.btn[hidden] { display: none; }

.substrate--dark .btn,
.substrate--solid .btn { background: var(--fg-invert); color: var(--ink); }
.substrate--dark .btn:hover,
.substrate--solid .btn:hover { background: var(--accent); color: var(--fg-invert); }

.btn--ghost {
  background: transparent;
  border-color: currentColor;
  color: inherit;
}
.btn--ghost:hover {
  background: transparent;
  border-color: var(--accent);
  color: var(--accent);
}

/* -------------------------------------------------------------------- CHIPS */

.chip {
  display: inline-block;
  padding: 5px 11px;
  border: 1px solid var(--line);
  border-radius: 0;
  font-family: var(--f-micro);
  font-size: var(--t-micro);
  text-transform: uppercase;
  letter-spacing: var(--tr-micro);
  color: var(--fg-mute);
  background: transparent;
  cursor: pointer;
  transition:
    background var(--d-fast) var(--ease-out),
    color var(--d-fast) var(--ease-out),
    border-color var(--d-fast) var(--ease-out);
}
.substrate--dark .chip { color: var(--fg-mute-dark); }
.chip:hover { border-color: var(--accent); color: var(--accent); }
.chip[aria-pressed="true"],
.chip.is-active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--paper);
}
.chip--static { cursor: default; pointer-events: none; }

/* ----------------------------------------------------------- SECTION HEADER */

.sec-head {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  margin-bottom: var(--s8);
}
.sec-head__meta {
  display: flex;
  align-items: center;
  gap: var(--s2);
}
.sec-head__meta::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--line);
}

/* ------------------------------------------------------------ STICKER GLYPHS */
/* Inline SVG only — never emoji, never an icon font (§4.5). */

.glyph {
  display: inline-block;
  width: 0.62em;
  height: 0.62em;
  vertical-align: baseline;
  fill: currentColor;
  margin-inline: 0.12em;
}
.glyph--accent { fill: var(--accent); }

/* --------------------------------------------------------------- WORK TILE */

/* No content-visibility here, deliberately.
   It looks like an easy win for a long grid, but on an element that also runs a
   reveal transition it backfires twice: a skipped element's styles are not
   updated, so the transition restarts every time the tile flips back to
   relevant and the tile can sit stuck at opacity 0; and contain-intrinsic-size
   is a guess (420px) against real tile heights of 450–650px, so the document
   height and scrollbar drift as you scroll.
   The heavy lifting is already done by three cheaper mechanisms that do not
   fight the CSS: pagination keeps the DOM small, images are loading="lazy",
   and videos carry no src until they approach the viewport. */
.tile {
  position: relative;
  display: block;
  border: 1px solid var(--line-strong);
  background: var(--surface);
  box-shadow: var(--card-lift);
  overflow: hidden;
  transition:
    box-shadow var(--d-base) var(--ease-out),
    transform var(--d-base) var(--ease-out);
}
.tile:hover {
  box-shadow: var(--card-lift-offset) var(--accent);
  transform: translate(-2px, -2px);
}
.substrate--dark .tile {
  background: var(--surface-dark);
  border-color: var(--line-invert);
  box-shadow: 5px 5px 0 rgba(0, 0, 0, 0.35);
}

.tile__frame {
  position: relative;
  overflow: hidden;      /* the frame never moves; only the media scales */
  aspect-ratio: var(--ratio, 4 / 3);
  background: var(--paper-2);
}
.substrate--dark .tile__frame { background: var(--ink-2); }

.tile__frame > .media-img,
.tile__frame > .media-video {
  transition: transform var(--d-slow) var(--ease-out);
}
.tile:hover .media-img,
.tile:hover .media-video { transform: scale(1.04); }

/* Spec-sheet corner labels (§4.7).
   The labels are near-white, so they need their own contrast guarantee — over a
   pale poster or a still-loading frame, white-on-light is unreadable. A scrim
   at both ends supplies it without dimming the middle of the image. */
.tile__corners {
  position: absolute;
  inset: 0;
  padding: var(--s2);
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-rows: auto 1fr auto;
  pointer-events: none;
  color: var(--fg-invert);
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.7);
  background:
    linear-gradient(to bottom, rgba(13,13,13,0.55) 0%, rgba(13,13,13,0) 22%),
    linear-gradient(to top,    rgba(13,13,13,0.55) 0%, rgba(13,13,13,0) 22%);
}
.tile__corners .micro { color: inherit; }
.tile__tl { grid-area: 1 / 1; }
.tile__tr { grid-area: 1 / 2; text-align: right; }
.tile__bl { grid-area: 3 / 1; align-self: end; }
.tile__br { grid-area: 3 / 2; align-self: end; text-align: right; }

/* Flat vector artwork is not a photograph and must not be cropped like one:
   `cover` would slice the figure out of frame. It sits on a panel with padding
   and `contain` instead, which also means the dark scrim and near-white corner
   labels would be unreadable over light artwork — so those invert here. */
/* One fixed frame ratio for the whole set. The artwork's own ratios span 2.0:1
   to 0.83:1, and honouring each one made every tile a different size — which in
   turn rendered the same-sized figures at wildly different scales. A uniform
   frame plus `contain` normalises both: every illustration is presented at the
   same scale in the same box. */
.tile[data-accent="illustration"] .tile__frame {
  aspect-ratio: 4 / 3;
  background: var(--paper-2);
  padding: var(--s4);
}
.substrate--dark .tile[data-accent="illustration"] .tile__frame { background: var(--ink); }
.tile[data-accent="illustration"] .media-img {
  aspect-ratio: auto;
  object-fit: contain;
  background: none;
}
.tile[data-accent="illustration"] .tile__corners {
  color: var(--fg);
  text-shadow: none;
  background: none;
}
.tile[data-accent="illustration"] .tile__corners .micro { color: var(--fg-mute); }

/* Accent rule that floods in on hover */
.tile::after {
  content: '';
  position: absolute;
  left: 0; bottom: 0;
  width: 100%; height: 4px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--d-base) var(--ease-out);
}
.tile:hover::after { transform: scaleX(1); }

.tile__body {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  padding: var(--s3);
}
/* Blurbs run one or two lines depending on wording; clamping them keeps every
   card body the same height so a row of tiles ends on a straight line. */
.tile__body > p.micro {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: calc(2 * 1.4em);
}
.tile__tools { display: flex; flex-wrap: wrap; gap: 6px; margin-top: var(--s1); }

/* Playing indicator */
.tile__badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 8px;
  background: rgba(13, 13, 13, 0.65);
  backdrop-filter: none;
}
.tile__badge::before {
  content: '';
  width: 6px; height: 6px;
  background: var(--accent);
}
.tile.is-playing .tile__badge::before { animation: pulse 1.6s var(--ease-in-out) infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }

/* Filtering uses FLIP; this is only the exit/enter paint state. */
.tile.is-hidden { display: none; }

/* --------------------------------------------------------- TORN DIVIDER */
/* A full-bleed band that carries the colour of the section above as its own
   background, with the section below torn through it. The PNG is a mask, so the
   paper colour comes from --paper and can never drift from the section it meets.
   Height is fluid: the tear should stay a ragged edge, not become a feature. */

.torn-divider {
  position: relative;
  width: 100%;
  height: clamp(40px, 6vw, 96px);
  background: var(--divider-from, var(--ink));
  /* The tear is decoration; keep it out of the flow of text selection. */
  user-select: none;
  pointer-events: none;
}

.torn-divider::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--divider-to, var(--paper));
  -webkit-mask-image: var(--divider-src);
  mask-image: var(--divider-src);
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: bottom center;
  mask-position: bottom center;
}

/* Mirror for a dark-over-light boundary. */
.torn-divider--flip::after { transform: scaleY(-1); }

/* Without mask support the band would render as a solid slab of the section
   above — worse than no divider. Collapse it instead so the substrate switch
   remains the divider, exactly as design-rule §5.4 has it. */
@supports not ((mask-image: url('')) or (-webkit-mask-image: url(''))) {
  .torn-divider { display: none; }
}

/* ---------------------------------------------------------------- POLAROID */
/* Physical object — shadow allowed (§4.3). */

.polaroid {
  background: #FBFAF7;
  padding: 14px 14px 52px;
  box-shadow: 0 18px 40px -12px rgba(13, 13, 13, 0.38);
  transform: rotate(var(--tilt, -2deg));
  transition: transform var(--d-slow) var(--ease-out);
  will-change: auto;
}
.polaroid:hover { transform: rotate(0deg) translateY(-6px); }
.polaroid__img { aspect-ratio: 4 / 5; object-fit: cover; width: 100%; }
.polaroid__cap {
  margin-top: 12px;
  font-family: var(--f-hand);
  font-size: 1.25rem;
  line-height: 1.15;
  color: #35322C;
  transform: rotate(-1.5deg);
}

@media (max-width: 768px) {
  /* Rotations halve on small screens (§5.5) */
  .polaroid { transform: rotate(calc(var(--tilt, -2deg) * 0.5)); }
}

/* ---------------------------------------------------------- CRUMPLED SHEET */

.sheet {
  position: relative;
  background: #F7F6F2;
  color: var(--fg);
  padding: clamp(24px, 4vw, 56px);
  box-shadow: 0 30px 70px -20px rgba(0, 0, 0, 0.6);
  transform: rotate(-0.6deg);
  background-image:
    linear-gradient(rgba(0,0,0,0.045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(0,0,0,0.045) 1px, transparent 1px);
  background-size: 22px 22px;
}
.sheet .micro { color: var(--fg-mute); }

/* ------------------------------------------------------------------- FORMS */
/* Underline only. No boxes, no radius (§8). */

.field { position: relative; display: block; margin-bottom: var(--s6); }
.field__label { display: block; margin-bottom: var(--s1); }
.field__input {
  width: 100%;
  padding: var(--s2) 0;
  border: 0;
  border-bottom: 1px solid var(--line);
  border-radius: 0;
  background: transparent;
  color: inherit;
  font-size: var(--t-lead);
  transition: border-color var(--d-base) var(--ease-out);
}
.field__input:focus { outline: none; border-bottom-color: var(--accent); }
.field__input::placeholder { color: var(--fg-mute); opacity: 0.6; }
textarea.field__input { resize: vertical; min-height: 120px; }

/* Accent rule drawn left→right on focus */
.field::after {
  content: '';
  position: absolute;
  left: 0; bottom: 0;
  width: 100%; height: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--d-base) var(--ease-out);
}
.field:focus-within::after { transform: scaleX(1); }

.form-status {
  font-family: var(--f-micro);
  font-size: var(--t-micro);
  text-transform: uppercase;
  letter-spacing: var(--tr-micro);
  margin-top: var(--s3);
  min-height: 1.4em;
}
.form-status[data-state="error"] { color: var(--acc-brand); }
.form-status[data-state="ok"]    { color: var(--acc-3d); }

/* Honeypot — must be reachable by bots, invisible to humans and AT. */
.hp { position: absolute; left: -9999px; opacity: 0; pointer-events: none; }

/* --------------------------------------------------------------------- NAV */

.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: var(--z-nav);
  height: var(--nav-h);
  display: flex;
  align-items: center;
  background: transparent;
  transition: background var(--d-base) var(--ease-out),
              border-color var(--d-base) var(--ease-out);
  border-bottom: 1px solid transparent;
}
.nav.is-stuck {
  background: rgba(13, 13, 13, 0.92);
  border-bottom-color: var(--line-invert);
  color: var(--fg-invert);
}
.nav__inner {
  width: 100%;
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
}
.nav__mark {
  font-family: var(--f-display);
  font-size: 1.05rem;
  text-transform: uppercase;
  letter-spacing: -0.02em;
}
.nav__links { display: flex; align-items: center; gap: var(--s4); }
.nav__link { position: relative; font-family: var(--f-micro); font-size: var(--t-micro);
             text-transform: uppercase; letter-spacing: var(--tr-micro); }
.nav__link::after {
  content: '';
  position: absolute; left: 0; bottom: -6px;
  width: 100%; height: 1px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--d-base) var(--ease-out);
}
.nav__link:hover::after,
.nav__link[aria-current="true"]::after { transform: scaleX(1); }

.nav__toggle { display: none; background: none; border: 0; cursor: pointer; padding: var(--s1); }

@media (max-width: 860px) {
  .nav__toggle { display: block; }
  .nav__links {
    position: fixed;
    inset: var(--nav-h) 0 auto 0;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--s3);
    padding: var(--s6) var(--gutter);
    background: var(--ink);
    color: var(--fg-invert);
    border-bottom: 1px solid var(--line-invert);
    transform: translateY(-120%);
    transition: transform var(--d-base) var(--ease-out);
  }
  .nav__links.is-open { transform: none; }
}

/* ---------------------------------------------------------------- MARQUEE */

.marquee { display: flex; overflow: hidden; user-select: none; gap: var(--s6); }
.marquee__track {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  gap: var(--s6);
  min-width: 100%;
  animation: scroll-x 40s linear infinite;
}
.marquee:hover .marquee__track { animation-play-state: paused; }
@keyframes scroll-x {
  from { transform: translateX(0); }
  to   { transform: translateX(calc(-100% - var(--s6))); }
}
@media (prefers-reduced-motion: reduce) {
  .marquee__track { animation: none; }
}

/* ------------------------------------------------------------------ CURSOR */
/* The one and only place a border-radius is permitted. */

.cursor, .cursor-ring {
  position: fixed;
  top: 0; left: 0;
  border-radius: 50%;
  pointer-events: none;
  z-index: var(--z-cursor);
  mix-blend-mode: difference;
}
.cursor { width: 6px; height: 6px; background: #FFF; margin: -3px 0 0 -3px; }
.cursor-ring {
  width: 34px; height: 34px;
  border: 1px solid rgba(255, 255, 255, 0.7);
  margin: -17px 0 0 -17px;
  transition: width var(--d-base) var(--ease-out),
              height var(--d-base) var(--ease-out),
              margin var(--d-base) var(--ease-out),
              border-color var(--d-base) var(--ease-out);
}
.cursor-ring.is-hover {
  width: 62px; height: 62px; margin: -31px 0 0 -31px;
  border-color: var(--accent);
}
/* Pointer-coarse and reduced-motion users never see it. */
@media (hover: none), (pointer: coarse), (prefers-reduced-motion: reduce) {
  .cursor, .cursor-ring { display: none; }
}

/* ----------------------------------------------------------- LOAD-MORE / IO */

.loadmore { display: flex; justify-content: center; margin-top: var(--s8); }
.loadmore[hidden] { display: none; }

.spinner {
  width: 18px; height: 18px;
  border: 2px solid var(--line);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 700ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ------------------------------------------------------------ PLATE (TREEMAP)
   The card the mosaic is built from (design-rule §6.3).

   Unlike .tile it has no footer band. The artwork fills the cell and the mark
   is set over it, so the plate stays one uninterrupted rectangle at every size
   the treemap hands it — a footer of fixed height would eat a short cell whole.

   The plate is its own size container, so the mark can be sized in cqw/cqh:
   the caption grows with the cell as it swells and disappears on cells too
   small to carry it, without a single line of JS. */

.plate {
  position: relative;
  display: block;
  height: 100%;
  overflow: hidden;
  container: plate / size;
  background: var(--surface);
  border: 1px solid var(--line);
  color: var(--fg);
  text-decoration: none;
  transition:
    border-color var(--d-base) var(--ease-out),
    box-shadow var(--d-base) var(--ease-out);
}
.substrate--dark .plate,
.substrate--solid .plate { background: var(--surface-dark); color: var(--fg-invert); }

/* Registration mark — the printed-plate tell (§4.4). Sized in container units
   so it stays proportionate as the cell resizes. */
.plate::after {
  content: '';
  position: absolute;
  top: clamp(6px, 4cqmin, 14px);
  right: clamp(6px, 4cqmin, 14px);
  width: clamp(4px, 2.2cqmin, 8px);
  aspect-ratio: 1;
  background: var(--accent);
  opacity: 0.55;
  transition: opacity var(--d-base) var(--ease-out);
}

/* Flat vector artwork, so `contain` and never `cover` — cropping decapitates
   the figure. The extra bottom padding is the mark's headroom. */
/* Padding is keyed to `cqmin`, the *smaller* axis. Keying it to cqw let a wide,
   short cell take a big horizontal inset and squeeze the artwork down to a
   speck; cqmin keeps the inset proportionate to the space actually available. */
.plate__art-wrap {
  position: absolute;
  inset: 0;
  display: block;
  padding: clamp(8px, 7cqmin, 36px);
  padding-bottom: clamp(30px, 22cqmin, 84px);
}
.plate__art-wrap > .media-img,
.plate__art-wrap > .media-video {
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  object-fit: contain;
  object-position: center;
  background: none;
  transition: transform var(--d-slow) var(--ease-out);
}
.plate__art-wrap > .media-missing {
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
}

.plate__mark {
  position: absolute;
  left: clamp(8px, 7cqmin, 36px);
  right: clamp(8px, 7cqmin, 36px);
  bottom: clamp(8px, 6cqmin, 30px);
  display: flex;
  flex-direction: column;
  gap: 2px;
  pointer-events: none;
}
.plate__no {
  font-family: var(--f-micro);
  font-size: clamp(0.5rem, 5cqmin, var(--t-micro));
  letter-spacing: var(--tr-micro);
  /* Text, so it takes the checked form of the accent — see --accent-ink (§2.3).
     The un-darkened accent is reserved for the fills here: the registration
     square and the hover border. */
  color: var(--accent-ink);
  line-height: 1;
}
.plate__title {
  font-family: var(--f-display);
  text-transform: uppercase;
  letter-spacing: -0.02em;
  line-height: 0.92;
  font-size: clamp(0.75rem, 13cqmin, 1.75rem);
  overflow-wrap: anywhere;
}

/* Cells too small to carry a word keep only the plate number… */
@container plate ((max-width: 132px) or (max-height: 104px)) {
  .plate__title { display: none; }
  .plate__art-wrap { padding-bottom: clamp(22px, 20cqmin, 40px); }
}
/* …and cells narrower than the number itself keep only the artwork. Without
   this the label is clipped mid-word by the plate's own overflow. */
@container plate ((max-width: 62px) or (max-height: 52px)) {
  .plate__mark { display: none; }
  .plate::after { display: none; }
  .plate__art-wrap { padding: clamp(5px, 7cqmin, 12px); }
}

/* Focus is not gated on pointer type — a keyboard user on a touch device still
   needs to see where they are. */
.plate:focus-visible {
  border-color: var(--accent);
  box-shadow: var(--card-lift-offset) var(--accent);
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.plate:focus-visible::after { opacity: 1; }

@media (hover: hover) and (pointer: fine) {
  .plate:hover {
    border-color: var(--accent);
    box-shadow: var(--card-lift-offset) var(--accent);
  }
  .plate:hover::after { opacity: 1; }
  .plate:hover .media-img { transform: scale(1.03); }
}

@media (prefers-reduced-motion: reduce) {
  .plate__art-wrap > .media-img { transition: none; }
  .plate:hover .media-img { transform: none; }
}
