/* ═══════════════ SHARED TAPE STYLES ═══════════════
   The cassette's own stylesheet — Kat's, and still the only place its artwork is
   drawn. It was extracted from the two prototype pages so they couldn't drift
   apart; those pages and the single-file app are gone, and this is now loaded
   once by nuxt.config alongside main.css. Edit here, not in a component.

   The four faces are root-absolute, so they resolve against public/fonts at dev
   and build time alike — a relative url() inside a bundled stylesheet would
   resolve against the bundle's own directory and 404. */

@font-face { font-family:'TapePencil'; src:url('/fonts/pencil.otf') format('opentype'); font-display:swap; }
@font-face { font-family:'TapeBlocky'; src:url('/fonts/blocky.otf') format('opentype'); font-display:swap; }
@font-face { font-family:'TapeScript'; src:url('/fonts/script.otf') format('opentype'); font-display:swap; }
@font-face { font-family:'TapeBrush';  src:url('/fonts/brush.otf')  format('opentype'); font-display:swap; }

  /* ═══════════════ THE CASSETTE ═══════════════
     Four stacked layers, every one pinned in % of the tape's own box:
       1  shell art      (SVG, takes colour from TAPE_SHELLS)
       2  paper label    (DOM, live text — font stays switchable)
       3  stickers       (DOM, absolutely placed)
       4  hub assembly   (SVG overlay: capsule + hubs + tape window)
     Layer 4 sits ON TOP of the label, which is what the reference does and
     what makes the silhouette read as a cassette. */
  @keyframes hubSpin { from{transform:rotate(0)} to{transform:rotate(360deg)} }

  .tape { position:relative; aspect-ratio:1.604; filter:drop-shadow(0 6px 16px rgba(60,40,30,0.28)); }
  .tape-layer { position:absolute; inset:0; width:100%; height:100%; display:block; }
  .tape-over { pointer-events:none; }

  .tape-label { position:absolute; overflow:hidden;
    box-shadow:0 1px 2px rgba(0,0,0,0.30), inset 0 0 0 1px rgba(0,0,0,0.10); }
  /* Spans the writing band and centres within it, rather than being pinned by
     `top`. Pinning made the name read top-anchored and pushed it toward the
     rule as it got bigger; centred, it grows symmetrically. */
  /* No overflow:hidden here — script faces put ink outside their content box
     (Vanquisher reaches 5% above it) and the band was shearing the tops off.
     The label's own overflow:hidden still contains anything that runs long. */
  .tape-title { position:absolute; left:3.5%; right:3.5%; white-space:nowrap;
    line-height:1.05; display:flex; align-items:center; }
  /* inline-block so its box hugs the glyphs — the write sweep has to start at
     the first letter, not at the far edge of a full-width centred container.
     padding-right leaves room for script tails so the clip can't shear them. */
  /* Padding on BOTH sides now: Vanquisher's swashes reach 4.5% left of origin
     and 5.3% past the advance width, and a left-padding of 0 sheared them. */
  .tape-ink { display:inline-block; padding:0 0.16em; }

  /* write — continuous ink sweep, eased like a real stroke. Top/bottom/left
     insets are negative so the sweep only ever clips from the right; at 0 they
     trimmed ink that sits outside the content box. */
  @keyframes inkIn {
    from { clip-path:inset(-40% 100% -40% -12%); }
    to   { clip-path:inset(-40% -12% -40% -12%); }
  }
  .tape-ink.write { animation-name:inkIn; animation-timing-function:cubic-bezier(0.33,0.72,0.36,1);
    animation-fill-mode:both; }

  /* type — one glyph at a time. All glyphs stay in the layout and only reveal,
     so a centred label doesn't reflow on every keystroke of the animation. */
  .ch { display:inline-block; opacity:0; }
  .ch.on { opacity:1; }
  @keyframes stampIn { from { opacity:0; transform:scale(1.22); } to { opacity:1; transform:scale(1); } }
  .ch.stamp.on { animation:stampIn 130ms ease-out both; }
  .tape-rule { position:absolute; left:3.5%; right:3.5%; height:1px; opacity:0.55; }
  .tape-foot { position:absolute; left:3.5%; right:3.5%; display:flex;
    justify-content:space-between; align-items:center; }
  .tape-foot span { font-family:'Courier New',monospace; letter-spacing:0.1em; white-space:nowrap; }

  /* The sticker layer is clipped to the shell's own silhouette, so a sticker
     pushed over an edge is cut by the plastic instead of hanging off the tape —
     what a real one does when it wraps round the back.
     The radius is ShellArt's `rx:30` on its 1000 × 623 body, restated as a pair
     of percentages: 3% of the width and 4.816% of the height both come to
     0.03 × W, so it resolves to the same circle the SVG draws at every tape size
     rather than to an ellipse the way one percentage would. Kept in step with
     the body rect by hand — there is no CSS that can read a viewBox.
     `pointer-events:none` here and `auto` on `.sticker.live` below: the layer
     itself must never eat a tap meant for the label under it. */
  .sticker-layer { position:absolute; inset:0; overflow:hidden;
    pointer-events:none; border-radius:3% / 4.816%; }

  /* A sticker sits flat on the shell: a tight contact shadow and no lift.
     `live` is only added when the parent passes drag handlers, so a display-only
     tape keeps the layer inert and unclickable. */
  .sticker { position:absolute; pointer-events:none; transform-origin:50% 50%;
    transition:filter .16s ease, box-shadow .16s ease; }
  .sticker.live { pointer-events:auto; cursor:grab; touch-action:none; }
  .sticker img, .sticker svg {
    width:100%; height:auto; display:block; user-select:none; -webkit-user-drag:none;
    filter:drop-shadow(0 1px 1.5px rgba(0,0,0,0.34));
  }
  .sticker.sel img { filter:drop-shadow(0 0 0 2px rgba(255,255,255,0.9))
                            drop-shadow(0 1px 2px rgba(0,0,0,0.4)); }

  /* Peeling: while dragged it lifts off the surface — grows a little, tips out
     of plane, and trades the tight contact shadow for a wide soft one cast
     further away. The shadow moving away from the art is what reads as height. */
  .sticker.peel { cursor:grabbing; z-index:9; }
  .sticker.peel img {
    filter:drop-shadow(0 10px 12px rgba(40,20,10,0.42));
    transform:scale(1.1) rotate(-2.5deg);
    transition:transform .13s cubic-bezier(0.2,0.8,0.3,1), filter .13s ease;
  }
  .sticker img { transition:transform .16s cubic-bezier(0.2,0.8,0.3,1), filter .16s ease; }

  /* Dropping: a just-placed sticker falls onto the shell — it starts big and
     high (the peel's own vocabulary: scale + a far soft shadow), lands past
     flat, and settles. The class is on for the animation's length only, so the
     fill can't sit on the `sel` ring that follows it. */
  @keyframes stickerDrop {
    0%   { opacity:0; transform:scale(2.1) rotate(-5deg);
           filter:drop-shadow(0 16px 18px rgba(40,20,10,0)); }
    55%  { opacity:1; transform:scale(0.94);
           filter:drop-shadow(0 2px 4px rgba(40,20,10,0.4)); }
    100% { transform:scale(1); filter:drop-shadow(0 1px 1.5px rgba(0,0,0,0.34)); }
  }
  .sticker.drop img { animation:stickerDrop 0.38s cubic-bezier(0.2,0.8,0.3,1) both; }

  /* Shimmer: glitter catching the light — a faint band sweeping the sticker
     once every 8 seconds, masked to the artwork's own silhouette (the mask URL
     is written inline by Cassette, since it's the sticker's own file). The
     sweep spends the first ~0.7s of the cycle moving and the rest at rest, and
     every sticker on the tape mounts together so they catch it together. */
  .sticker-shimmer {
    position:absolute; inset:0; pointer-events:none;
    -webkit-mask-size:100% 100%; mask-size:100% 100%;
    -webkit-mask-repeat:no-repeat; mask-repeat:no-repeat;
    background:linear-gradient(115deg, transparent 42%, rgba(255,255,255,0.8) 50%, transparent 58%);
    background-size:300% 100%; background-repeat:no-repeat;
    opacity:0.5;
    animation:stickerShimmer 8s linear infinite;
  }
  @keyframes stickerShimmer {
    0%   { background-position:0% 0; }
    9%   { background-position:100% 0; }
    100% { background-position:100% 0; }
  }

  /* The key's own box — the selected sticker's geometry, outside the clip, and
     it draws nothing itself. `z-index` puts it over the layer it escaped. */
  .sticker-key { z-index:10; }

  .sticker-x {
    position:absolute; top:-9px; right:-9px; width:22px; height:22px; padding:0;
    border-radius:50%; cursor:pointer; border:1.5px solid #fff;
    background:#c2402c; color:#fff; font:600 14px/1 'Figtree',sans-serif;
    display:flex; align-items:center; justify-content:center;
    box-shadow:0 1px 4px rgba(0,0,0,0.4); pointer-events:auto;
  }

  /* Typing straight onto the label — an input wearing the label's typography,
     so there is no second field to keep in sync. */
  .tape-input {
    width:100%; border:none; background:transparent; padding:0; margin:0;
    font:inherit; color:inherit; letter-spacing:inherit; text-transform:inherit;
    outline:none; min-width:0;
  }
  .tape-input::placeholder { color:currentColor; opacity:0.30; }

  /* Tapping the name is how a tape gets renamed, and only where a parent passed
     `onNameTap`. It fills the writing band — measured at 283 × 48 on the
     customiser's 340px tape, so it clears 44 on both axes with no expander at
     all — and it draws
     nothing: the affordance is the name, and a box around it would be furniture
     printed on artwork. */
  .tape-name-hit {
    position:absolute; inset:0; padding:0; border:none; background:transparent;
    cursor:pointer;
  }

  .hub-g { transform-box:fill-box; transform-origin:center; }
  .hub-g.spin      { animation:hubSpin 1.15s linear infinite; }
  .hub-g.spin-slow { animation:hubSpin 2.3s linear infinite; }
  .hub-g.spin-fast { animation:hubSpin 0.75s linear infinite; }

  .sheen { position:absolute; inset:0; pointer-events:none; border-radius:2.6%;
    background:linear-gradient(118deg,rgba(255,255,255,0.32) 0%,rgba(255,255,255,0.09) 30%,rgba(255,255,255,0) 50%); }
