@font-face {
  font-family: "Pretendard";
  src: local("Pretendard Bold"), url("./src/Pretendard-Bold.woff2") format("woff2");
  font-style: normal;
  font-weight: 700;
  font-display: swap;
}

:root {
  --desk: #e0e0e0;
  --ink: #000;
  --paper: #fff;
  --link-blue: #98c1ff;
  --link-green: #9ee458;
  --next-offset: 182px;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html,
body {
  width: 100%;
  height: 100%;
  margin: 0;
  overflow: hidden;
}

body {
  color: var(--ink);
  background: var(--desk);
  font-family: "Pretendard", "Apple SD Gothic Neo", "Noto Sans KR", sans-serif;
  font-weight: 700;
  overscroll-behavior: none;
  -webkit-font-smoothing: antialiased;
}

button,
a {
  font: inherit;
}

button {
  color: inherit;
}

#app,
.card-table {
  position: fixed;
  inset: 0;
}

#app {
  isolation: isolate;
  background: var(--desk);
}

.card-table {
  z-index: 1;
  overflow: hidden;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}

.next-card {
  position: fixed;
  z-index: 10000;
  top: calc(50% + var(--next-offset));
  left: 50%;
  display: grid;
  width: 30px;
  min-width: 30px;
  height: 30px;
  padding: 5px 8px;
  overflow: hidden;
  place-items: center;
  border: 0;
  border-radius: 0;
  background: var(--paper);
  cursor: pointer;
  transform: translateX(-50%);
  transition: color 120ms ease, background-color 120ms ease, transform 120ms ease;
}

.next-card span {
  display: block;
  font-size: 13px;
  line-height: 1.5;
}

.next-card:hover {
  color: #fff;
  background: #000;
}

.next-card:active {
  transform: translateX(-50%) scale(0.92);
}

.next-card:focus-visible,
.quick-link:focus-visible,
.card-action:focus-visible,
.card-body:focus-visible {
  outline: 2px solid #000;
  outline-offset: 3px;
}

.quick-links {
  position: fixed;
  z-index: 10000;
  bottom: 18px;
  left: 18px;
  display: flex;
  align-items: center;
  gap: 9px;
}

.quick-link {
  display: inline-flex;
  min-height: 30px;
  align-items: center;
  justify-content: center;
  padding: 5px 8px;
  color: #000;
  font-size: 13px;
  line-height: 1.5;
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
}

.quick-link--green {
  padding-inline: 10px;
  color: #fff;
  border-radius: 999px;
  background: #389a2f;
  transition: opacity 180ms ease;
}

.quick-link--white {
  background: #fff;
}

.quick-link--green:hover {
  opacity: 0.66;
}

.status {
  position: fixed;
  z-index: 11000;
  right: 18px;
  bottom: 18px;
  max-width: min(320px, calc(100vw - 36px));
  margin: 0;
  padding: 7px 10px;
  color: #fff;
  background: #000;
  font-size: 13px;
  line-height: 1.45;
  opacity: 0;
  pointer-events: none;
  transform: translateY(6px);
  transition: opacity 160ms ease, transform 160ms ease;
}

.status.is-visible {
  opacity: 1;
  transform: translateY(0);
}

/* Off-screen rather than display:none, so the heading still reaches crawlers
   and screen readers without taking part in the layout. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  border: 0;
  clip-path: inset(50%);
  white-space: nowrap;
}

.noscript {
  position: fixed;
  z-index: 12000;
  top: 18px;
  left: 18px;
  margin: 0;
  padding: 8px 10px;
  color: #fff;
  background: #000;
  font-size: 13px;
}

/* The outer element is the 2D rigid body; the plane stays visually flat. */
.card-body {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 2;
  margin: 0;
  padding: 0;
  border: 0;
  outline: 0;
  cursor: grab;
  touch-action: none;
  transform-origin: 50% 50%;
  will-change: transform;
  -webkit-tap-highlight-color: transparent;
}

.card-plane {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: #fff;
  box-shadow: none;
  transform: translateZ(0);
  will-change: transform;
}

.card-body.is-dragging {
  cursor: grabbing;
}

.card-body.is-arriving .card-plane {
  animation: card-arrive 360ms cubic-bezier(0.2, 0.82, 0.25, 1) both;
}

@keyframes card-arrive {
  0% {
    opacity: 0;
    transform: translateY(8px) scale(0.975);
  }
  100% {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

.card-face {
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  color: #000;
  background: #fff;
  font-size: 13px;
  font-weight: 700;
  line-height: 1.45;
  transform: translateZ(0);
  word-break: keep-all;
}

.card-name,
.card-subtitle,
.card-footer,
.card-services,
.card-contact,
.card4-copy {
  position: absolute;
  margin: 0;
  overflow-wrap: break-word;
}

.card-name {
  top: 17px;
  left: 18px;
  width: 184px;
  font-size: 17px;
  line-height: 1.45;
}

.card-subtitle {
  top: 44px;
  left: 18px;
  width: 184px;
  line-height: 1.45;
}

.card-footer {
  right: 18px;
  bottom: 18px;
  left: 18px;
}

.card-footer p,
.card-subtitle p,
.card-services p,
.card-contact p,
.card4-copy p {
  margin: 0;
}

.brand-mark {
  position: absolute;
  top: 17px;
  left: 166px;
  display: block;
  width: 36px;
  height: 32px;
  object-fit: contain;
}

.card-action {
  display: inline;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  color: var(--link-blue);
  background: transparent;
  font: inherit;
  line-height: inherit;
  text-decoration: underline;
  text-decoration-thickness: from-font;
  text-underline-position: from-font;
  cursor: pointer;
}

a.card-action {
  color: var(--link-blue);
}

.card-action--green,
a.card-action--green {
  color: var(--link-green);
}

.card-action:hover {
  color: #000;
  background: var(--link-blue);
  text-decoration: none;
}

.card-2 .card-name,
.card-2 .card-subtitle {
  text-align: center;
}

.card-services {
  top: 157px;
  right: 18px;
  left: 18px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.service-list {
  margin: 0;
}

.service--busy {
  color: var(--link-blue);
  text-decoration: line-through;
  text-decoration-skip-ink: none;
}

.service-state {
  color: #000;
  text-decoration: none;
}

.hairline {
  width: 100%;
  height: 1px;
  margin: 0;
  border: 0;
  background: rgb(0 0 0 / 0.1);
}

.contact-block {
  line-height: 1.45;
}

.card-3 .card-name,
.card-3 .card-subtitle {
  text-align: right;
}

.card-3 .card-contact {
  right: 18px;
  bottom: 18px;
  left: 18px;
}

.card-4 .card-face {
  width: 340px;
  height: 220px;
}

.card4-logo {
  position: absolute;
  top: 18px;
  right: 18px;
  width: 154px;
  height: 24px;
  object-fit: contain;
}

.card4-copy {
  top: 113px;
  left: 18px;
}

.card4-name {
  font-size: 17px;
  line-height: 1.45;
}

.card4-email {
  margin-top: 8px !important;
}

/* Holographic foil.
   A single virtual lamp hangs over the desk at the pointer. Every foil layer
   is painted in card-local space, so the diffraction pattern is printed on the
   card: it travels and turns with the card as it is dragged, and each card on
   the desk catches the same lamp from its own distance and angle.
   JS writes --glare-*, --shift-*, --foil-power and --foil-hue each frame, and
   --foil-phase once, when the card is dealt. */
.card-plane {
  --glare-x: 50%;
  --glare-y: 50%;
  --shift-x: 0px;
  --shift-y: 0px;
  --foil-power: 0;
  --foil-hue: 0deg;
  --foil-phase: 0px;
  /* Every pattern layer is painted oversized and never tiled. A gradient
     rendered at card size and then repeated shows a seam the moment --shift
     slides a neighbouring tile into view, because the phase does not carry
     across the tile edge. Bleeding well past the largest possible shift keeps
     every seam off the card. */
  --foil-bleed: 420px;

  /* The primary rainbow: broad, soft spectral bands. Under hard-light the
     darker stops tint the white paper and the brighter stops tint the black
     ink, so paper and type pick up complementary halves of the spectrum. */
  --foil-bands: repeating-linear-gradient(
    161deg,
    hsl(338 94% 38%) 0px,
    hsl(14 96% 60%) 6px,
    hsl(48 96% 64%) 13px,
    hsl(104 78% 42%) 20px,
    hsl(166 88% 54%) 27px,
    hsl(202 96% 48%) 34px,
    hsl(256 88% 40%) 41px,
    hsl(308 90% 56%) 47px,
    hsl(338 94% 38%) 54px
  );
  /* The grating itself — dense micro lines that give the bands their fizz. */
  --foil-grain: repeating-linear-gradient(
    97deg,
    rgb(0 0 0 / 0.55) 0px,
    rgb(255 255 255 / 0.6) 2.5px,
    rgb(0 0 0 / 0.55) 5.5px
  );
  /* Crossing band set, coarser and at a different angle. */
  --foil-moire: repeating-linear-gradient(
    -37deg,
    hsl(320 90% 46%) 0px,
    hsl(38 94% 62%) 8px,
    hsl(140 82% 48%) 16px,
    hsl(196 92% 58%) 23px,
    hsl(268 86% 46%) 31px,
    hsl(320 90% 46%) 39px
  );
  /* Broad wash from the Figma spec, restated in px so it repeats seamlessly
     instead of being a one-shot gradient stretched to the box. Blended as
     `color`, it re-hues only what the bands have already lifted off pure white
     or pure black. */
  --foil-wash: repeating-linear-gradient(
    147.18deg,
    #ffa1a1 0px,
    #fff582 62px,
    #87fe5c 152px,
    #98c1ff 252px,
    #ffa1a1 360px
  );
  /* Layers that carry the printed pattern share this geometry: oversized,
     never tiled, offset back by the bleed so the shift only ever slides the
     interior of the image across the card. */
  --foil-extent: calc(100% + var(--foil-bleed) * 2);
}

.foil-layer {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: none;
  border-radius: inherit;
  pointer-events: none;
  background-repeat: no-repeat;
  background-size: var(--foil-extent) var(--foil-extent);
}

.rarity-holo .foil-layer,
.rarity-gold .foil-layer {
  display: block;
}

/* The foil as it is printed, seen in ambient light. `lighten` cannot touch
   white stock, so the paper stays paper and only the type takes the spectrum —
   this is the layer that is always on, and it is what the Figma comp shows. */
.foil-ink {
  background-image: var(--foil-bands);
  background-position:
    calc(var(--foil-bleed) * -1 + var(--foil-phase) + var(--shift-x))
    calc(var(--foil-bleed) * -1 + var(--shift-y));
  mix-blend-mode: lighten;
  opacity: 0.56;
  filter: brightness(1.12) saturate(1.55) hue-rotate(var(--foil-hue));
}

/* The pattern on the paper. hard-light keeps the stock white where the bands
   are bright and tints it where they are dark, doing the exact opposite to the
   ink — that reciprocity is what reads as interference rather than as striped
   paper. Its strength is fixed: the foil is printed at one density, and the
   lamp changes how brightly it is lit, not how much of it there is. */
.foil-spectrum {
  background-image: var(--foil-bands);
  background-position:
    calc(var(--foil-bleed) * -1 + var(--foil-phase) + var(--shift-x))
    calc(var(--foil-bleed) * -1 + var(--shift-y));
  mix-blend-mode: hard-light;
  opacity: 0.09;
  filter: saturate(1.35) hue-rotate(var(--foil-hue));
}

/* Micro grating over the bands. Held to a whisper: at this pitch it is the
   texture you notice only once the card moves. */
.foil-grain {
  background-image: var(--foil-grain);
  background-position:
    calc(var(--foil-bleed) * -1 + var(--foil-phase) + var(--shift-x) * 1.7)
    calc(var(--foil-bleed) * -1 + var(--shift-y) * 1.7);
  mix-blend-mode: overlay;
  opacity: 0.05;
}

/* `color` leaves pure white paper and pure black ink alone and re-hues only
   the bands the grating lit up — so which colour dominates where drifts across
   the card with the lamp, instead of the whole sheet being one rainbow. */
.foil-wash {
  background-image: var(--foil-wash);
  background-position:
    calc(var(--foil-bleed) * -1 + var(--shift-x) * 2.6)
    calc(var(--foil-bleed) * -1 + var(--shift-y) * 2.6);
  mix-blend-mode: color;
  opacity: 0.24;
  filter: saturate(1.2) hue-rotate(var(--foil-hue));
}

/* A second set of bands, crossing the first at a different pitch and drifting
   the opposite way. The interference between the two is what gives the foil
   depth — the pattern never resolves into one flat repeat. */
.foil-moire {
  background-image: var(--foil-moire);
  background-position:
    calc(var(--foil-bleed) * -1 + var(--shift-x) * -1.5)
    calc(var(--foil-bleed) * -1 + var(--shift-y) * -1.5);
  mix-blend-mode: soft-light;
  opacity: 0.12;
  filter: saturate(1.2) hue-rotate(var(--foil-hue));
}

/* The spotlight. This is now the whole of the proximity response: the printed
   pattern holds its density and the lamp simply lights it, washing the bands
   towards white as it closes in. Stepped down in long, shallow stages, because
   a short falloff to `transparent` leaves a visible ring on the paper. */
.foil-glare {
  background: radial-gradient(
    78% 78% at var(--glare-x) var(--glare-y),
    rgb(255 255 255 / 0.72) 0%,
    rgb(255 253 244 / 0.46) 20%,
    rgb(255 251 236 / 0.25) 40%,
    rgb(255 251 236 / 0.11) 60%,
    rgb(255 251 236 / 0.04) 78%,
    rgb(255 251 236 / 0.01) 90%,
    transparent 100%
  );
  background-repeat: no-repeat;
  background-size: 100% 100%;
  mix-blend-mode: screen;
  opacity: calc(1.0 + var(--foil-power) * 0.4);
}

.foil-glitter {
  display: none;
}

/* Gold. The base stock changes, and the bands run bronze-to-champagne rather
   than merely yellow — metal reads as metal because it is dark and bright at
   once, not because it is saturated. */
.rarity-gold .card-plane,
.rarity-gold .card-face {
  background: #ffeda7;
}

.rarity-gold .card-plane {
  --foil-bands: repeating-linear-gradient(
    140deg,
    hsl(30 92% 26%) 0px,
    hsl(40 96% 54%) 9px,
    hsl(50 100% 84%) 19px,
    hsl(43 96% 62%) 30px,
    hsl(26 90% 30%) 41px,
    hsl(38 94% 50%) 52px,
    hsl(56 96% 88%) 63px,
    hsl(36 92% 40%) 72px,
    hsl(30 92% 26%) 82px
  );
  --foil-moire: repeating-linear-gradient(
    -37deg,
    hsl(28 88% 32%) 0px,
    hsl(45 96% 66%) 17px,
    hsl(54 98% 88%) 34px,
    hsl(38 92% 46%) 51px,
    hsl(24 86% 30%) 68px,
    hsl(28 88% 32%) 86px
  );
  --foil-wash: linear-gradient(
    147.18deg,
    #b0812c 0%,
    #ffe9a8 16%,
    #fff8d6 32%,
    #cf9c33 50%,
    #fff1b8 68%,
    #bd8a2c 84%,
    #ffeec0 100%
  );
}

/* Gold stock is already bright, so every layer has to give ground to keep the
   type readable — the metal has to be legible before it is impressive. */
/* Gold stock is already bright and already figured, so it carries a heavier
   standing pattern than white paper does — but, like the holo card, a fixed
   one. The soft-light moiré does most of that work: it sculpts the sheet
   without dragging it down into brown. */
.rarity-gold .foil-ink {
  opacity: 0.38;
  filter: brightness(0.94) saturate(1.35) hue-rotate(var(--foil-hue));
}

.rarity-gold .foil-spectrum {
  opacity: 0.16;
  filter: saturate(1.24) contrast(1.1) hue-rotate(var(--foil-hue));
}

.rarity-gold .foil-moire {
  opacity: 0.18;
}

.rarity-gold .foil-grain {
  opacity: 0.07;
}

.rarity-gold .foil-wash {
  opacity: 0.26;
}

.rarity-gold .foil-glare {
  opacity: calc(1.0 + var(--foil-power) * 0.3);
}

.rarity-gold .foil-glitter {
  display: block;
  background-image:
    radial-gradient(circle at 50% 50%, rgb(255 255 255 / 0.95) 0 0.6px, transparent 1.3px),
    radial-gradient(circle at 50% 50%, rgb(255 244 198 / 0.85) 0 0.5px, transparent 1.1px);
  /* The one layer that must tile: a dot grid repeats seamlessly by
     construction, and it has to cover the whole card. */
  background-repeat: repeat;
  background-size: 9px 9px, 14px 14px;
  background-position:
    calc(var(--foil-phase) + var(--shift-x) * 0.7) calc(var(--shift-y) * 0.7),
    calc(var(--shift-x) * -0.45) calc(var(--shift-y) * -0.45);
  mix-blend-mode: color-dodge;
  opacity: calc(var(--foil-power) * 0.85);
  animation: glitter-drift 7s ease-in-out infinite alternate;
  -webkit-mask-image: radial-gradient(
    46% 46% at var(--glare-x) var(--glare-y),
    #000 0%,
    rgb(0 0 0 / 0.45) 46%,
    transparent 82%
  );
  mask-image: radial-gradient(
    46% 46% at var(--glare-x) var(--glare-y),
    #000 0%,
    rgb(0 0 0 / 0.45) 46%,
    transparent 82%
  );
}

@keyframes glitter-drift {
  to {
    opacity: calc(var(--foil-power) * 0.4);
    filter: brightness(1.35);
  }
}

@media (max-width: 480px) {
  .quick-links {
    right: 18px;
    overflow-x: auto;
  }

  .status {
    right: 18px;
    bottom: 60px;
    left: 18px;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    scroll-behavior: auto !important;
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
  }
}
