/* ── Type ───────────────────────────────────────────────────────────────────
   Handmade Alphabet, self-hosted so there is no round trip to a font CDN from
   Korea and it keeps working offline. 1001Fonts FFC licence — free for personal
   and commercial use; the licence text ships beside it in src/fonts/.

   `swap` so text is readable immediately in the fallback and reflows when the
   face arrives, rather than sitting invisible while a 63 kB file downloads on
   a phone connection.                                                        */

@font-face {
  font-family: 'Handmade Alphabet';
  src: url('./fonts/handmade-alphabet.otf') format('opentype');
  font-weight: 100 900;
  font-display: swap;
}

/* ── Palette ───────────────────────────────────────────────────────────────
   Her favourite colour is "clear", so the interface is built out of frosted
   translucent panels floating over soft pastel blobs. What you see through a
   card depends on what is drifting behind it, and it changes as you scroll.
   Nothing is a flat solid fill.                                             */

:root {
  color-scheme: light dark;

  --blush:    #ffd9e4;
  --peach:    #ffe4cf;
  --butter:   #fff3c8;
  --mint:     #cdf0e3;
  --sky:      #d6ecff;
  --lilac:    #e6dcff;

  --ink:      #4a3f4a;
  --ink-soft: #8b7d8b;
  --pink:     #f3809f;
  --pink-deep:#d95c80;

  --line:         rgba(150, 118, 140, .32);
  --glass:        rgba(255, 255, 255, .82);
  --glass-strong: rgba(255, 255, 255, .93);
  --glass-edge:   rgba(255, 255, 255, .95);
  --shadow:       0 8px 28px rgba(180, 140, 165, .18);
  --page:         #fff8fb;
}

@media (prefers-color-scheme: dark) {
  :root {
    --blush: #4a2f3d; --peach: #4a3a30; --butter: #4a452c;
    --mint: #2c4a41; --sky: #2f3f4f; --lilac: #3c3450;

    --ink: #f6ecf2;
    --ink-soft: #b7a7b4;
    --pink: #ff9db8;
    --pink-deep: #ffb3c8;

    --line:         rgba(255, 255, 255, .22);
    --glass:        rgba(44, 35, 46, .84);
    --glass-strong: rgba(52, 42, 54, .94);
    --glass-edge:   rgba(255, 255, 255, .16);
    --shadow:       0 8px 28px rgba(0, 0, 0, .3);
    --page:         #241d26;
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100dvh;
  background: var(--page);
  color: var(--ink);
  /* CJK fallbacks sit AFTER system-ui deliberately: they are there to catch
     Korean characters, not to render the whole interface. Putting them earlier
     meant Chromium fell through to Apple SD Gothic Neo — installed on every
     Mac — and set the entire page in a Korean face. */
  font-family: 'Handmade Alphabet', ui-rounded, "SF Pro Rounded", system-ui,
               -apple-system, "Segoe UI", "Apple SD Gothic Neo", "Malgun Gothic",
               sans-serif;
  font-size: 17px;
  line-height: 1.5;
  overflow-x: hidden;
}

/* ── Background artwork ─────────────────────────────────────────────────────
   Drop an image at src/background.png and it becomes the backdrop. The build
   copies it to dist/ automatically. If it is absent nothing breaks — the
   pastel blobs below simply show through instead.

   It is treated as texture, not as a picture: softened, slightly desaturated
   and washed, because bold high-contrast art at full strength fights the text
   sitting on top of it. Everything above is frosted glass, so the artwork
   blurs behind the panels and stays sharp in the gutters — which is the whole
   reason this combination works.

   Tune with the three values below. --art-opacity is the one to reach for. */

:root {
  --art-opacity: .92;
  --art-blur: 2.5px;
  --art-saturate: 1.05;
}
@media (prefers-color-scheme: dark) {
  :root {
    --art-opacity: .55;
    --art-saturate: .95;
  }
}

.artwork {
  position: fixed;
  inset: -2vmax;
  z-index: -1;
  pointer-events: none;
  background-image: url("./background.png");
  background-size: cover;
  background-position: center;
  opacity: var(--art-opacity);
  filter: blur(var(--art-blur)) saturate(var(--art-saturate));
}

/* Drifting pastel blobs. These sit *below* the artwork, so they still provide
   colour where the image is pale, and carry the whole design on their own if
   no image has been added. */
body::before, body::after {
  content: "";
  position: fixed;
  inset: -20vmax;
  z-index: -2;
  pointer-events: none;
  background:
    radial-gradient(38vmax 38vmax at 12% 8%,  var(--blush) 0%, transparent 62%),
    radial-gradient(32vmax 32vmax at 88% 14%, var(--sky)   0%, transparent 60%),
    radial-gradient(34vmax 34vmax at 78% 82%, var(--lilac) 0%, transparent 60%),
    radial-gradient(30vmax 30vmax at 18% 88%, var(--mint)  0%, transparent 58%),
    radial-gradient(26vmax 26vmax at 50% 46%, var(--butter) 0%, transparent 55%);
  animation: drift 34s ease-in-out infinite alternate;
}
body::after {
  animation-duration: 47s;
  animation-direction: alternate-reverse;
  opacity: .55;
}
@keyframes drift {
  from { transform: translate3d(-2%, -1%, 0) scale(1); }
  to   { transform: translate3d(3%, 2%, 0) scale(1.12); }
}
@media (prefers-reduced-motion: reduce) {
  body::before, body::after { animation: none; }
}

#app { max-width: 30rem; margin: 0 auto; padding: 1.5rem 1.1rem 11rem; }

/* ── Header ─────────────────────────────────────────────────────────────── */

header { text-align: center; margin-bottom: 1.75rem; }
header h1 {
  font-size: 1.55rem; font-weight: 700; margin: .6rem 0 .2rem; letter-spacing: -.01em;
}
header p { margin: 0; color: var(--ink-soft); font-size: .95rem; }
.mascot { width: 92px; height: 92px; display: block; margin: 0 auto; }
.mascot--sm { width: 64px; height: 64px; }

/* The header mascot has a real alpha channel, so it floats free — no plate
   behind it. The drop-shadow follows the actual silhouette rather than a box. */
.mascot-img {
  height: 96px;
  width: auto;
  max-width: 100%;
  display: block;
  margin: 0 auto .4rem;
  filter: drop-shadow(0 6px 14px rgba(150, 120, 140, .28));
}
@media (prefers-color-scheme: dark) {
  .mascot-img { filter: drop-shadow(0 6px 16px rgba(0, 0, 0, .5)); }
}

/* ── Frosted panels ─────────────────────────────────────────────────────── */

.step {
  background: var(--glass);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
  backdrop-filter: blur(18px) saturate(150%);
  border: 1px solid var(--glass-edge);
  border-radius: 1.4rem;
  box-shadow: var(--shadow);
  padding: 1.1rem 1.1rem 1.25rem;
  margin-bottom: 1rem;
  transition: opacity .3s ease, transform .3s ease;
}
.step h2 {
  font-size: .95rem; font-weight: 700; margin: 0 0 .9rem;
  display: flex; align-items: center; gap: .6rem;
  line-height: 1.2;
}
.step h2 .num {
  width: 1.55rem; height: 1.55rem; flex: none;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--glass-strong);
  border: 1px solid var(--line);
  font-size: .8rem; font-weight: 700; line-height: 1;
  color: var(--ink-soft);
  /* Optical centring: digits sit slightly high in most rounded faces. */
  padding-top: .05em;
}
.step--inert { opacity: .82; }
.step--inert * { pointer-events: none; }
.step--inert .card, .step--inert .field, .step--inert .cal { opacity: .62; }

/* ── Chips and cards ────────────────────────────────────────────────────── */

.chips { display: flex; gap: .6rem; }
.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr)); gap: .55rem; }

.chip, .card {
  font: inherit; color: inherit; cursor: pointer; text-align: left;
  background: var(--glass-strong);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  border: 1.5px solid var(--line);
  border-radius: 1rem;
  padding: .8rem .9rem;
  min-height: 3rem;
  transition: transform .18s cubic-bezier(.34,1.56,.64,1), border-color .18s, box-shadow .18s;
}
.chip { flex: 1; text-align: center; font-weight: 600; }
.chip:active, .card:active { transform: scale(.96); }

.chip--on, .card--on {
  border-color: var(--pink);
  box-shadow: 0 0 0 4px rgba(243, 128, 159, .18);
  animation: pop .32s cubic-bezier(.34,1.56,.64,1);
}
@keyframes pop {
  0% { transform: scale(1); } 45% { transform: scale(1.06); } 100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .chip--on, .card--on { animation: none; }
}

.card { display: flex; flex-direction: column; gap: .1rem; }
.card__name { font-weight: 700; font-size: .92rem; }
.card__meta { font-size: .72rem; color: var(--ink-soft); }
.card__note { font-size: .72rem; color: var(--ink-soft); opacity: .85; margin-top: .15rem; }

/* ── Inputs ─────────────────────────────────────────────────────────────── */

.field {
  font: inherit; color: inherit; width: 100%; min-height: 3rem;
  background: var(--glass-strong);
  border: 1.5px solid var(--line); border-radius: 1rem;
  padding: .75rem .9rem;
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
}
.field:focus { outline: none; border-color: var(--pink); box-shadow: 0 0 0 4px rgba(243,128,159,.18); }
.hint { font-size: .8rem; color: var(--ink-soft); margin: .5rem 0 0; }

/* ── The summary, styled as a little ticket ─────────────────────────────── */

.summary {
  background: var(--glass-strong);
  border: 1px dashed var(--pink);
  border-radius: 1.1rem;
  padding: .9rem 1rem;
  margin-bottom: .9rem;
}
.clock { display: flex; align-items: baseline; gap: .6rem; padding: .2rem 0; }
.clock__time { font-size: 1.7rem; font-weight: 700; font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.clock__rest { font-size: .8rem; color: var(--ink-soft); }
.clock + .clock { border-top: 1px dashed rgba(243,128,159,.35); margin-top: .3rem; padding-top: .5rem; }

.refusal {
  background: rgba(255, 214, 226, .5);
  border: 1px solid rgba(243,128,159,.4);
  border-radius: 1.1rem; padding: .8rem .95rem; margin: 0 0 .9rem;
  font-size: .9rem;
}
@media (prefers-color-scheme: dark) {
  .refusal { background: rgba(120, 60, 80, .35); }
}

/* ── Buttons ────────────────────────────────────────────────────────────── */

.button {
  font: inherit; font-weight: 700; width: 100%; min-height: 3.25rem;
  border: 0; border-radius: 1.1rem; cursor: pointer;
  background: linear-gradient(135deg, var(--pink), var(--pink-deep));
  color: #fff;
  box-shadow: 0 6px 18px rgba(217, 92, 128, .3);
  display: block; text-align: center; text-decoration: none;
  padding: .9rem; margin-bottom: .55rem;
  transition: transform .18s cubic-bezier(.34,1.56,.64,1), opacity .2s;
}
.button:active { transform: scale(.97); }
.button:disabled { opacity: .38; cursor: default; box-shadow: none; }
.link {
  display: block; width: 100%; text-align: center;
  color: var(--pink-deep); background: none; border: 0;
  font: inherit; font-weight: 600; cursor: pointer; padding: .6rem;
  text-decoration: none;
}

/* ── Outcome panels ─────────────────────────────────────────────────────── */

.panel {
  background: var(--glass);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
  backdrop-filter: blur(18px) saturate(150%);
  border: 1px solid var(--glass-edge);
  border-radius: 1.6rem;
  box-shadow: var(--shadow);
  padding: 1.6rem 1.2rem;
  text-align: center;
  animation: rise .4s cubic-bezier(.22,1,.36,1);
}
@keyframes rise { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .panel { animation: none; } }

.panel h1 { font-size: 1.4rem; margin: .5rem 0 .8rem; }
.panel .clock { justify-content: center; }
.panel .summary { text-align: left; margin: 1rem 0; }
.warn { color: var(--pink-deep); font-size: .88rem; }
.notice { font-size: 1.2rem; }

/* ── Scattered stickers ─────────────────────────────────────────────────────
   A decorative layer behind everything, filling the empty space either side of
   the flow. Never interactive, never announced to a screen reader.            */

body { position: relative; }

.stickers {
  position: absolute;
  inset: 0;
  min-height: 100dvh;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}

#app { position: relative; z-index: 1; }

.sticker {
  position: absolute;
  /* An identical square for every sticker, with the artwork fitted inside.
     Two things are needed for them to look equal: this box, and source images
     trimmed to their alpha bounding box. Without the trim, object-fit fits the
     whole image — margins included — so a mostly-empty PNG renders its creature
     at a fraction of the box while a tight one fills it. */
  width: calc(76px * var(--scale, 1));
  height: calc(76px * var(--scale, 1));
  object-fit: contain;
  line-height: 1;
  user-select: none;
  -webkit-user-drag: none;
  filter: drop-shadow(0 4px 10px rgba(180, 140, 165, .22));
  /* Starts small and flat; the pop-in below gives it its size and tilt. */
  transform: translateY(var(--drift, 0)) rotate(var(--tilt, 0deg)) scale(.4);
  opacity: 0 !important;
  transition: transform .55s cubic-bezier(.34, 1.56, .64, 1), opacity .5s ease;
}

.sticker--in {
  opacity: var(--shown, 1) !important;
  transform: translateY(var(--drift, 0)) rotate(var(--tilt, 0deg)) scale(1);
  animation: bob var(--bob-time, 7s) ease-in-out var(--bob-delay, 0s) infinite alternate;
}

@keyframes bob {
  from { translate: 0 -5px; }
  to   { translate: 0 6px; }
}

/* On a phone the free space is only above and below the content, so they
   shrink to suit. Placement itself is measured in JS and needs no breakpoint. */
@media (max-width: 40rem) {
  .sticker {
    width: calc(52px * var(--scale, 1)) !important;
    height: calc(52px * var(--scale, 1)) !important;
  }
}

@media (prefers-reduced-motion: reduce) {
  .sticker { transition: none; }
  .sticker--in { animation: none; }
}

/* ── The calendar ───────────────────────────────────────────────────────────
   Hand-built rather than a native date input: the platform control is a grey
   system widget that ignores every other decision on the page. This one is
   still entirely tap-driven and needs no typing.                             */

.cal { user-select: none; }

.cal__head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: .6rem;
}
.cal__month { font-weight: 700; font-size: 1rem; }
.cal__arrow {
  font: inherit; font-size: 1.3rem; line-height: 1;
  width: 2.2rem; height: 2.2rem; flex: none;
  display: grid; place-items: center;
  color: var(--ink); cursor: pointer;
  background: var(--glass-strong);
  border: 1.5px solid var(--line); border-radius: 50%;
  transition: transform .15s cubic-bezier(.34,1.56,.64,1), border-color .15s;
}
.cal__arrow:active { transform: scale(.9); }
.cal__arrow:disabled { opacity: .3; cursor: default; }

.cal__row { display: grid; grid-template-columns: repeat(7, 1fr); gap: .18rem; }
.cal__row--names { margin-bottom: .25rem; }
.cal__name {
  text-align: center; font-size: .68rem; font-weight: 700;
  color: var(--ink-soft); text-transform: uppercase; letter-spacing: .04em;
}

.cal__day {
  font: inherit; font-size: .85rem; font-weight: 600;
  aspect-ratio: 1; width: 100%;
  display: grid; place-items: center;
  color: var(--ink); cursor: pointer;
  background: transparent;
  border: 1.5px solid transparent; border-radius: .7rem;
  transition: transform .16s cubic-bezier(.34,1.56,.64,1), background .16s, border-color .16s;
}
.cal__day:hover:not(:disabled) { background: var(--glass-strong); }
.cal__day:active:not(:disabled) { transform: scale(.88); }
.cal__day--faint { opacity: .3; }
.cal__day:disabled { opacity: .22; cursor: default; }

/* Today gets a quiet dot rather than a ring, so it never competes with the
   selected day. */
.cal__day--today:not(.cal__day--on)::after {
  content: ""; position: absolute; transform: translateY(.72rem);
  width: 4px; height: 4px; border-radius: 50%; background: var(--pink);
}
.cal__day { position: relative; }

.cal__day--on {
  background: linear-gradient(135deg, var(--pink), var(--pink-deep));
  color: #fff;
  border-color: transparent;
  box-shadow: 0 4px 12px rgba(217, 92, 128, .35);
  animation: pop .3s cubic-bezier(.34,1.56,.64,1);
}
@media (prefers-reduced-motion: reduce) { .cal__day--on { animation: none; } }

.mascot-img--sm { height: 76px; margin-bottom: .2rem; }

/* ── The time picker ────────────────────────────────────────────────────────
   The familiar scrolling wheel, built rather than borrowed. The native control
   behaves fine; it just looks like a system dialog dropped onto the page.     */

:root {
  --wheel-item: 44px;
}

.time { user-select: none; }

.time__display {
  display: flex; align-items: baseline; justify-content: center; gap: .6rem;
  padding: .3rem 0 .8rem;
}
.time__big {
  font-size: 2.5rem; font-weight: 700; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums; line-height: 1;
}
.time__big--empty { color: var(--ink-soft); opacity: .45; }
.time__aside { font-size: .78rem; color: var(--ink-soft); }

.wheels__frame { position: relative; }

/* The band marks the selected row. It sits behind the columns and ignores
   pointer events so it never intercepts a scroll. */
.wheels__band {
  position: absolute; left: 0; right: 0; top: 50%;
  height: var(--wheel-item); transform: translateY(-50%);
  border-radius: .8rem;
  background: linear-gradient(135deg, var(--pink), var(--pink-deep));
  box-shadow: 0 4px 14px rgba(217, 92, 128, .3);
  pointer-events: none;
}

.wheels {
  position: relative;
  display: flex; align-items: stretch; justify-content: center; gap: .2rem;
  height: calc(var(--wheel-item) * 5);
  /* Fade the rows away from the centre, so the wheel reads as a cylinder. */
  -webkit-mask-image: linear-gradient(transparent, #000 22%, #000 78%, transparent);
  mask-image: linear-gradient(transparent, #000 22%, #000 78%, transparent);
}
.wheels__colon {
  align-self: center; font-size: 1.4rem; font-weight: 700;
  color: var(--ink); opacity: .5; padding-bottom: .1rem;
}

.wheel {
  height: 100%;
  overflow-y: auto;
  scroll-snap-type: y mandatory;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  outline: none;
}
.wheel::-webkit-scrollbar { display: none; }
.wheel:focus-visible { border-radius: .8rem; box-shadow: 0 0 0 2px var(--pink); }

/* Two blank rows top and bottom, so the first and last values can reach the
   centre band. */
.wheel__pad { height: calc(var(--wheel-item) * 2); }

.wheel__item {
  height: var(--wheel-item);
  min-width: 3.2rem;
  display: grid; place-items: center;
  scroll-snap-align: center;
  font-size: 1.15rem; font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  opacity: .45;
  transition: opacity .15s ease, color .15s ease, transform .15s ease;
}
.wheel__item--on {
  opacity: 1; color: #fff; transform: scale(1.06);
}

.step--inert .wheel { overflow: hidden; }

/* Rows are tappable too, so a visible value never needs a precise flick. */
.wheel__item--tappable { cursor: pointer; }
@media (hover: hover) {
  .wheel__item--tappable:not(.wheel__item--on):hover { opacity: .8; }
}

/* The success mascot is a wide group rather than a single figure, so it gets
   its own height — matching the tall ones would make it enormous. */
.mascot-img--wide { height: 92px; margin-bottom: .3rem; }

/* ── The asking ─────────────────────────────────────────────────────────────
   The No button is not a real choice. It moves whenever a pointer comes near,
   which needs pointerdown as well as hover — a finger has no hover, so the
   press has to be dodged before it lands.                                    */

.ask { padding: 2rem 1.4rem 2.2rem; }
.ask h1 { font-size: 1.5rem; line-height: 1.25; margin: .6rem 0 1.6rem; }

.ask__buttons {
  display: flex; align-items: center; justify-content: center; gap: .8rem;
}
.ask__yes { width: auto; min-width: 7.5rem; margin: 0; }

.ask__no {
  font: inherit; font-weight: 700;
  min-width: 7.5rem; min-height: 3.25rem;
  padding: .9rem 1.2rem;
  color: var(--ink); cursor: pointer;
  background: var(--glass-strong);
  border: 1.5px solid var(--line); border-radius: 1.1rem;
  /* Free to leave its slot without pushing anything else around. */
  position: relative; z-index: 2;
  transition: transform .32s cubic-bezier(.34, 1.56, .64, 1);
  touch-action: manipulation;
}

/* The dodge is the joke, so it still moves under reduced-motion — it just
   arrives instantly rather than springing. */
@media (prefers-reduced-motion: reduce) {
  .ask__no { transition: none; }
}

/* The write-your-own field, revealed under the cards when "Something else" is
   chosen. */
.custom { margin-top: .55rem; }
.custom::placeholder { color: var(--ink-soft); opacity: .7; }
