:root {
  --card-radius: 1rem;

  --font-body: 'Inclusive Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;

  /* Plain values only. Deliberately NOT var(--pico-*) — Pico redefines
     --pico-color locally on components — and NOT light-dark(), which resolves
     per element against whatever color-scheme that element inherited. */
  --paper: #fffdf7;
  --ink: #1b1b1a;
  --muted: #55554f;
  --hairline: #d9d7cd;
  --chip: #e9e6da;

  --badge-solo: #3f6446;
  --badge-pair: #96461f;
  --badge-group: #2f4f96;
  --badge-text: #fff;

  /* Barely-there tint for the backdrop pattern — it should read as texture,
     not as a second surface competing with the card. */
  --pattern: color-mix(in srgb, var(--ink) 4.5%, transparent);
  --pattern-size: 64px;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper: #16181c;
    --ink: #f0efe9;
    --muted: #b3b2aa;
    --hairline: #34363b;
    --chip: #2a2d33;

    --badge-solo: #8fbe96;
    --badge-pair: #e8a184;
    --badge-group: #9db8ec;
    --badge-text: #16181c;
  }
}

:root[data-theme="dark"] {
  --paper: #16181c;
  --ink: #f0efe9;
  --muted: #b3b2aa;
  --hairline: #34363b;
  --chip: #2a2d33;

  --badge-solo: #8fbe96;
  --badge-pair: #e8a184;
  --badge-group: #9db8ec;
  --badge-text: #16181c;
}

* { -webkit-tap-highlight-color: transparent; }

/* The deck paints outside its box during the flip, which gives the document
   somewhere to scroll. Nothing here should ever scroll but the card back. */
html {
  height: 100%;
  overflow: hidden;
  overscroll-behavior: none;
}

/* Pico sets its own family on headings, so inheriting from body isn't enough. */
body,
h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-body);
}

body {
  margin: 0;
  padding: 0;
  height: 100dvh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  color: var(--ink);

  /* Four corner-gradients tile into a diamond lattice. The colour comes from
     --pattern, so it tints with the theme instead of being a fixed blue. */
  background-color: var(--paper);
  background-image:
    linear-gradient(135deg, var(--pattern) 25%, transparent 25%),
    linear-gradient(225deg, var(--pattern) 25%, transparent 25%),
    linear-gradient(315deg, var(--pattern) 25%, transparent 25%),
    linear-gradient( 45deg, var(--pattern) 25%, transparent 25%);
  background-position:
    calc(var(--pattern-size) / -2) 0,
    calc(var(--pattern-size) / -2) 0,
    0 0,
    0 0;
  background-size: var(--pattern-size) var(--pattern-size);
  background-repeat: repeat;   /* Pico sets no-repeat on body — only one tile painted */
}

/* ---------- top bar ---------- */

.topbar {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: calc(.5rem + env(safe-area-inset-top)) 1rem .5rem;
}

/* The wordmark is inline SVG with no fills of its own, so it inherits --ink
   and follows the colour mode. */
.wordmark-wrap {
  margin: 0;
  line-height: 0;
  color: var(--ink);
}

.wordmark {
  display: block;
  height: 2.1rem;
  width: auto;
  fill: currentColor;
}

.icon-btn {
  width: 2.75rem;
  height: 2.75rem;
  flex: none;
  padding: 0;
  font-size: 1.05rem;
  line-height: 1;
  border-radius: .75rem;
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--hairline);
}

.icon-btn:active { background: var(--chip); }

/* ---------- deck ---------- */

main#deck {
  flex: 1;
  min-height: 0;
  padding: .5rem 1rem;
  display: grid;
  place-items: stretch;
  touch-action: pan-y;
  /* Not hidden: a card mid-rotation sweeps outside its layout box, and hidden
     would shear the near edge off. body already clips at the viewport. */
  overflow: visible;
}

.loading { align-self: center; text-align: center; color: var(--muted); }

/* ---------- intro ---------- */

.intro {
  align-self: center;
  justify-self: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
  text-align: center;
}

.intro-clown {
  width: 7.5rem;
  height: 7.5rem;
  margin: 0;
  padding: 0;
  font-size: 3.75rem;
  line-height: 1;
  border-radius: 50%;
  background: var(--chip);
  border: 1px solid var(--hairline);
  color: var(--ink);
  animation: breathe 3.2s ease-in-out infinite;
  transition: transform .15s ease;
}

.intro-clown:active { transform: scale(.94); }

/* A slow pulse so the one thing on screen reads as the thing to press. */
@keyframes breathe {
  0%, 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(0,0,0,0); }
  50%      { transform: scale(1.045); box-shadow: 0 6px 22px rgba(0,0,0,.09); }
}

.intro-hint {
  margin: 0;
  font-size: 1.05rem;
  color: var(--muted);
}

/* The deck controls have nothing to act on yet. */
body.is-intro .chrome {
  opacity: 0;
  pointer-events: none;
}

.chrome { transition: opacity .35s ease; }

/* The card is the perspective box and the drag target. Card chrome (paper,
   border, radius, clipping) lives on each side so the backface reads right. */
.card {
  position: relative;
  width: 100%;
  /* The artwork is a full-bleed square, so card width IS image height. Cap the
     width by viewport height too, or the front's square plus its title can't
     fit the screen. */
  max-width: min(30rem, calc(100dvh - 21rem));
  max-height: 100%;
  margin: 0 auto;
  /* .card is an <article>: Pico gives those padding AND a background. The
     background would sit behind both faces and show through as a slab at the
     midpoint of the flip, so it has to go — the sides carry the paper. */
  padding: 0;
  background: transparent;
  min-height: 0;
  perspective: 2800px;   /* flatter: less exaggeration, so less sweep to clip */
  will-change: transform, opacity;
  /* One shadow on the card, not one per side — two would double-paint. */
  border-radius: var(--card-radius);
  box-shadow: 0 1px 3px rgba(0,0,0,.12);
}

.card.dragging { transition: none; }
.card.settle { transition: transform .25s ease, opacity .25s ease; }

/* Both sides share one grid cell, so the card is as tall as the taller side
   and nothing jumps mid-flip. */
.flip {
  display: grid;
  height: 100%;
  min-height: 0;
  transform-style: preserve-3d;
  transition: transform .62s cubic-bezier(.2, .75, .25, 1);
}

.card.is-back .flip { transform: rotateY(180deg); }

.side {
  position: relative;
  grid-area: 1 / 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: var(--card-radius);
  /* Each side does its own masking, so the artwork meets the rounded corner. */
  overflow: hidden;
}

.side.back { transform: rotateY(180deg); }

/* Padding lives here, not on the side, so the artwork can run edge to edge. */
.card-body { padding: 1rem; }

.back-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  scroll-behavior: smooth;
}

.back-title {
  margin: 0 0 .9rem;
  font-size: 13px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Fades in only while there's more below. Sits above the back's action row. */
.scroll-hint {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 3.6rem;
  height: 2.6rem;
  display: grid;
  place-items: end center;
  padding-bottom: .35rem;
  pointer-events: none;
  border-radius: 0 0 var(--card-radius) var(--card-radius);
  background: linear-gradient(to top, var(--paper) 15%, transparent);
  opacity: 0;
  transition: opacity .25s ease;
}

.card.has-more .scroll-hint { opacity: 1; }

.scroll-hint span {
  font-size: .9rem;
  line-height: 1;
  color: var(--muted);
  animation: nudge 1.8s ease-in-out infinite;
}

@keyframes nudge {
  0%, 100% { transform: translateY(0); opacity: .55; }
  50%      { transform: translateY(.22rem); opacity: 1; }
}

.card h2 {
  margin: 0 0 .55rem;
  /* px for the same reason as the back copy: Pico scales the root font size
     on wide viewports, so rem here drifts by device. */
  font-size: clamp(26px, 7vw, 32px);
  line-height: 1.12;
  color: var(--ink);
}

/* ---------- card content ---------- */

/* Square and full-bleed. No radius or overflow of its own — the card clips it,
   which is what makes it read as sliding into the card rather than off it. */
.card figure {
  flex: none;
  width: 100%;
  aspect-ratio: 1 / 1;
  margin: 0;
  display: grid;
  place-items: center;
}

.card figure img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.card figure .placeholder-box {
  width: 100%;
  height: 100%;
  display: grid;
  place-items: center;
  background: var(--chip);
  font-size: 2rem;
  color: var(--muted);
}

.badges {
  display: flex;
  flex-wrap: wrap;
  gap: .35rem;
  margin-bottom: .85rem;
}

.badge {
  display: inline-block;
  font-size: .7rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  font-weight: 600;
  padding: .2rem .55rem;
  border-radius: 999px;
  /* Unlabelled badges (anything not solo/pair/group) get this neutral chip. */
  background: var(--chip);
  color: var(--ink);
}

.badge[data-badge="solo"]  { background: var(--badge-solo);  color: var(--badge-text); }
.badge[data-badge="pair"]  { background: var(--badge-pair);  color: var(--badge-text); }
.badge[data-badge="group"] { background: var(--badge-group); color: var(--badge-text); }

.bit-section + .bit-section { margin-top: 1.35rem; }

.bit-section h3 {
  margin: 0 0 .3rem;
  font-size: 13px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
}

/* This is the text people read at arm's length, outdoors, in a hurry — so it
   runs bigger and looser than the rest of the interface. */
.bit-section p {
  margin: 0;
  /* px, not rem: Pico scales the root font size up on wide viewports, which
     would inflate rem-based sizes by device. Phones land near 21px. */
  font-size: clamp(20px, 5.4vw, 24px);
  line-height: 1.45;
  color: var(--ink);
}

/* The wrapper carries no layout of its own — its buttons are pinned to the
   side itself, so the flip icon lands on identical coordinates on both faces
   and appears to stay put while the card turns around it. */
.back-actions { display: contents; }

.side .flip-btn,
.side .feedback-btn {
  position: absolute;
  bottom: 1rem;
  z-index: 2;
}

/* Identical offsets on both sides. When the back is the side facing you, the
   parent's 180deg cancels the side's own, so these render as authored — which
   is what keeps the flip icon on the same screen position through the turn. */
.side .flip-btn { right: 1rem; }
.side .feedback-btn { left: 1rem; }

/* Room so content never slides under the pinned controls. */
.front .card-body { padding-bottom: 4.5rem; }
.back .card-body { padding-bottom: 4.75rem; }

/* Same control, same corner, on both sides — it reads as one toggle. */
.flip-btn {
  margin: 0;
  width: 3rem;
  height: 3rem;
  padding: 0;
  font-size: 1.35rem;
  line-height: 1;
  border-radius: .75rem;
  background: var(--chip);
  color: var(--ink);
  border: 1px solid var(--hairline);
  transition: transform .18s ease, background-color .15s ease;
}

.flip-btn:active { transform: rotate(-25deg) scale(.94); }



.feedback-btn {
  margin: 0;
  width: 3rem;
  height: 3rem;
  padding: 0;
  font-size: 1.2rem;
  line-height: 1;
  border-radius: .75rem;
  background: var(--chip);
  color: var(--ink);
  border: 1px solid var(--hairline);
}

/* ---------- bottom chrome ---------- */

.chrome {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  padding: .75rem 1rem calc(.75rem + env(safe-area-inset-bottom));
}

.chrome .clown {
  font-size: 1.5rem;
  background: var(--chip);
}

/* ---------- dialogs ---------- */

dialog article { background: var(--paper); color: var(--ink); }
dialog h2 { color: var(--ink); }
dialog .muted { color: var(--muted); }
dialog textarea { margin-bottom: 0; }
dialog footer { display: flex; gap: .5rem; justify-content: flex-end; }
dialog footer button { width: auto; margin: 0; }

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

/* Dealing a new card: it slides in from the side you swiped toward, with a
   slight tilt that settles. */
@keyframes deal-right {
  from { transform: translateX(14%) rotate(2.2deg); opacity: 0; }
  to   { transform: translateX(0) rotate(0); opacity: 1; }
}
@keyframes deal-left {
  from { transform: translateX(-14%) rotate(-2.2deg); opacity: 0; }
  to   { transform: translateX(0) rotate(0); opacity: 1; }
}

/* Same reason as .boing — no fill, so a missed animationend can't leave a
   transform stuck on the card and kill the drag. */
.card.deal-from-right { animation: deal-right .34s cubic-bezier(.22, .8, .3, 1); }
.card.deal-from-left  { animation: deal-left  .34s cubic-bezier(.22, .8, .3, 1); }

/* Contents arrive just behind the card, staggered top to bottom.
   --rise-lead is the beat before anything appears: on a normal swipe it's
   nearly nothing, but a random draw holds so the artwork lands alone first and
   the rest unfolds after you've taken it in. */
@keyframes rise {
  from { transform: translateY(.6rem); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}

.card {
  --rise-lead: 0s;
  --rise-step: .07s;
}

/* The swap happens behind the shut iris, which then holds 260ms and takes
   720ms to open — so nothing may appear before ~0.98s, and the lead sits just
   past that to leave a beat where only the artwork is on screen. */
.card.boing {
  --rise-lead: 1.15s;
  --rise-step: .15s;
}

.card h2,
.card .badges .badge,
.card .bit-section,
.card .back-actions {
  animation: rise .42s cubic-bezier(.2, .7, .3, 1) both;
}

.card h2      { animation-delay: var(--rise-lead); }
.card .badges .badge:nth-child(1) { animation-delay: calc(var(--rise-lead) + var(--rise-step) * 1); }
.card .badges .badge:nth-child(2) { animation-delay: calc(var(--rise-lead) + var(--rise-step) * 1.5); }
.card .badges .badge:nth-child(3) { animation-delay: calc(var(--rise-lead) + var(--rise-step) * 2); }
.card .badges .badge:nth-child(n+4) { animation-delay: calc(var(--rise-lead) + var(--rise-step) * 2.5); }
.card .bit-section:nth-child(1) { animation-delay: calc(var(--rise-lead) + var(--rise-step) * 3); }
.card .bit-section:nth-child(2) { animation-delay: calc(var(--rise-lead) + var(--rise-step) * 4); }
.card .bit-section:nth-child(3) { animation-delay: calc(var(--rise-lead) + var(--rise-step) * 5); }
.card .bit-section:nth-child(n+4) { animation-delay: calc(var(--rise-lead) + var(--rise-step) * 6); }
.card .back-actions { animation-delay: calc(var(--rise-lead) + var(--rise-step) * 6.5); }

/* The clown winds up, then whips around — a touch of squash on the way. */
@keyframes clown-tumble {
  0%   { transform: rotate(0) scale(1); }
  18%  { transform: rotate(-38deg) scale(.88); }
  70%  { transform: rotate(320deg) scale(1.14); }
  88%  { transform: rotate(368deg) scale(.96); }
  100% { transform: rotate(360deg) scale(1); }
}

.chrome .clown.spin { animation: clown-tumble 1.05s cubic-bezier(.3, .8, .3, 1); }

/* The card lands with a small overshoot as the iris opens. */
@keyframes boing {
  0%   { transform: scale(.82); }
  55%  { transform: scale(1.045); }
  78%  { transform: scale(.985); }
  100% { transform: scale(1); }
}

/* Delayed to start as the iris opens, not while it's shut. `backwards` holds
   the small starting scale during that delay but — unlike `forwards`/`both` —
   releases the transform once it ends, so the swipe drag stays in control. */
.card.boing { animation: boing .62s cubic-bezier(.3, .9, .3, 1) .26s backwards; }

/* Controls give a little under the finger. */
.icon-btn,
.feedback-btn {
  transition: transform .12s ease, background-color .15s ease, border-color .15s ease;
}
.icon-btn:active,
.feedback-btn:active { transform: scale(.92); }

.feedback-btn:hover { background: var(--hairline); }

dialog[open] { animation: dialog-in .22s ease both; }

@keyframes dialog-in {
  from { transform: translateY(.75rem) scale(.98); opacity: 0; }
  to   { transform: translateY(0) scale(1); opacity: 1; }
}

/* Anyone who asked for less motion gets none of the above. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

.sheets { display: none; }

/* ---------- print: 9-up duplex, 2.5in x 3.5in ---------- */

@media print {
  @page { size: 8.5in 11in; margin: 0.25in; }

  body { height: auto; overflow: visible; display: block; background: #fff; color: #000; }
  .topbar, main#deck, .chrome, dialog { display: none !important; }

  .sheets { display: block; }

  .sheet {
    display: grid;
    grid-template-columns: repeat(3, 2.5in);
    grid-template-rows: repeat(3, 3.5in);
    justify-content: center;
    align-content: start;
    break-after: page;
  }
  .sheet:last-child { break-after: auto; }

  .slot {
    width: 2.5in;
    height: 3.5in;
    box-sizing: border-box;
    padding: 0.16in;
    outline: 0.5pt dashed #bbb;
    outline-offset: -0.5pt;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    font-size: 8pt;
    line-height: 1.3;
    color: #000;
  }
  .slot.empty { outline-color: #eee; }

  /* Face: image, then title, then badges (per the sketch). */
  .slot .p-img {
    width: 100%;
    aspect-ratio: 1 / 1;
    object-fit: cover;
    margin-bottom: 0.07in;
    border-radius: 0.06in;
  }

  .slot .p-title {
    font-size: 11pt;
    font-weight: 700;
    line-height: 1.1;
    margin: 0 0 0.05in;
  }

  .slot .p-badges { display: flex; flex-wrap: wrap; gap: 0.03in; }

  .slot .p-badges .badge {
    font-size: 5.5pt;
    padding: 0.015in 0.05in;
    background: #eee !important;
    color: #000 !important;
    border: 0.4pt solid #999;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }

  /* Back: sections fill, footer pinned to the bottom. */
  .slot-back { justify-content: space-between; }
  .slot .p-sections { overflow: hidden; }
  .slot .p-section + .p-section { margin-top: 0.09in; }

  .slot .p-heading {
    margin: 0 0 0.02in;
    font-size: 6pt;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: #555;
  }
  .slot .p-body { margin: 0; }

  .slot .p-footer {
    flex: none;
    display: flex;
    align-items: center;
    gap: 0.06in;
    padding-top: 0.06in;
    border-top: 0.4pt solid #ddd;
  }
  .slot .p-mark { font-size: 9pt; line-height: 1; }
  .slot .p-url { font-size: 7pt; flex: 1; }
  .slot .p-qr { width: 0.42in; height: 0.42in; flex: none; }
  .slot .p-qr svg { width: 100%; height: 100%; display: block; }
}
