/* 2.3 Tiles with weight and 2.4 捉 Cat catches rat (Track B). Loaded after table-life.css on
   every table page. Timed styles read the variables table life writes on the element they move
   (--timed, --stamp-alpha…), never on the board. */

/* 2.3 Tiles with weight. The flights themselves are ghosts outside the board (discard-flight.js);
   these rules only touch the rack and the landing spot. data-tile-weight is set on the table's
   root while the TILE_WEIGHT switch is on. */

/* With a mouse, one click lifts a tile and a second throws it, so the lifted (selected) tile
   stands 7 px up: always higher than a tile under the pointer, which rises 4 px (silent, and
   never under reduced motion). Its outline is the strongest on the rack, so the tile under the
   pointer never looks more picked than the one a click lifted. */
@media (hover: hover) and (pointer: fine) {
  [data-tile-weight] .own-tiles > .tile.playable.selected {
    transform: none;
    translate: 0 -7px;
    outline: 3px solid #987526;
    outline-offset: 1px;
  }
}
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  [data-tile-weight] .own-tiles > .tile.playable:not(.selected):hover {
    transform: none;
    translate: 0 -4px;
    transition: translate 0.12s ease-out;
  }
  [data-tile-weight] .own-tiles > .tile.playable.selected {
    transition: translate 0.12s ease-out;
  }
  /* A click on the lifted tile presses it down a little: the first click on the tile the game
     lifted only lifts it, and the press says the click was felt. */
  [data-tile-weight] .own-tiles > .tile.playable.selected:active {
    translate: 0 -5px;
    scale: 0.98;
  }
}

/* A win on a discard: once the flying tile lands in the winning hand it stays open there,
   whatever the hand's own reveal has reached. */
.hand-won .winning-piece.winning-tile[data-landed] {
  --reveal: 1;
}

/* 2.4 捉 Cat catches rat. Table life writes --timed (0 to 1) on the two bonus tiles a pair moment
   moves (life.marks, dist/moments/pounce.js), on the desktop rack front and on the phone rows
   alike. Each tile's movement is a pure function of its own --timed, so a redraw of the board
   never restarts it, and at 1 (and under reduced motion, where it is always 1) every tile is in
   its place. The rows lay their tiles in tile order, so the cat sits just before the rat and the
   rooster just before the centipede, whichever way the seat faces. */
[data-life-stamp] [data-timed-at] {
  --t: var(--timed, 1);
  /* A hop: 0 at either end, 1 at the middle. */
  --hop: calc(var(--t) * (1 - var(--t)) * 4);
}

/* The cat leaps one tile along the row onto the rat (by 0.45), crouches there, and springs back
   to its place (from 0.62). With the timing in dist/moments/pounce.js it lands at 0.33 s. */
[data-life-stamp='pounce'] [data-timed-at]:has([data-face-id='42']) {
  --out: clamp(0, var(--t) / 0.45, 1);
  --back: clamp(0, (var(--t) - 0.62) / 0.38, 1);
  position: relative;
  z-index: 3;
  translate: calc((var(--out) - var(--back)) * 100%)
    calc(var(--out) * (1 - var(--out)) * -4 * 60% + var(--back) * (1 - var(--back)) * -4 * 25%);
  rotate: calc((var(--out) * (1 - var(--out)) - var(--back) * (1 - var(--back))) * 4 * 14deg);
}
/* The rat squashes as the cat lands on it. */
[data-life-stamp='pounce'] [data-timed-at]:has([data-face-id='43']) {
  --squash: min(clamp(0, (var(--t) - 0.36) / 0.09, 1), clamp(0, (0.64 - var(--t)) / 0.16, 1));
  transform-origin: 50% 100%;
  scale: calc(1 + var(--squash) * 0.06) calc(1 - var(--squash) * 0.16);
}

/* The rooster leans over and pecks the centipede twice (at 0.35 and 0.7 of its time, 0.3 s and
   0.53 s in), and the centipede flinches each time. */
[data-life-stamp='peck'] [data-timed-at] {
  --peck: max(
    min(clamp(0, (var(--t) - 0.2) / 0.15, 1), clamp(0, (0.45 - var(--t)) / 0.1, 1)),
    min(clamp(0, (var(--t) - 0.55) / 0.15, 1), clamp(0, (0.8 - var(--t)) / 0.1, 1))
  );
}
[data-life-stamp='peck'] [data-timed-at]:has([data-face-id='44']) {
  position: relative;
  z-index: 3;
  transform-origin: 50% 100%;
  translate: calc(var(--peck) * 32%) calc(var(--peck) * 6%);
  rotate: calc(var(--peck) * 24deg);
}
[data-life-stamp='peck'] [data-timed-at]:has([data-face-id='45']) {
  transform-origin: 50% 100%;
  translate: calc(var(--peck) * 8%) 0;
  scale: 1 calc(1 - var(--peck) * 0.1);
}

/* A seat's flower pair, or a full set: the two tiles hop together once. */
:is([data-life-stamp='flowerPair'], [data-life-stamp='fullSet']) [data-timed-at] {
  position: relative;
  z-index: 3;
  translate: 0 calc(var(--hop) * -45%);
}

/* The latest discard in the middle of a desktop table steps aside while a pair's seal stands,
   as it does for the 连庄 seal, so it never sits on the chop's glyph; the tile is still in its
   river. */
.classic-table.life-stamping:is(
    [data-life-stamp='pounce'],
    [data-life-stamp='peck'],
    [data-life-stamp='flowerPair'],
    [data-life-stamp='fullSet']
  )
  .desktop-focus {
  visibility: hidden;
}

/* A pair's words stay on one line under the glyph. */
.call-stamp:is(.call-stamp-pounce, .call-stamp-peck, .call-stamp-flowerPair, .call-stamp-fullSet)
  .stamp-word {
  white-space: nowrap;
}

/* On a phone the rows are tiny: the pair's chop is pressed on the owner's seat chip beside the
   name, like the 听 chop, and the seal in the middle of the felt stands aside so it never covers
   the two tiles. Your own pair keeps its seal low on the felt, clear of your row. The chop comes
   and goes with the seal's own --stamp-alpha. */
:is(.landscape-centre, .portrait-centre)
  .call-stamp:is(
    .call-stamp-pounce,
    .call-stamp-peck,
    .call-stamp-flowerPair,
    .call-stamp-fullSet
  ):not([data-seat='bottom']) {
  display: none;
}
/* The chop sits on the corner of the portrait, where a speech bubble never reaches. */
:is(.portrait-name, .landscape-name):has(> .pair-chop) {
  position: relative;
}
.call-stamp.pair-chop {
  position: absolute;
  top: -7px;
  left: -7px;
  z-index: 7;
  display: inline-grid;
  flex: none;
  place-items: center;
  width: 24px;
  min-width: 0;
  height: 24px;
  padding: 0;
  gap: 0;
  font:
    400 16px/1 'Noto Serif SC',
    'Songti SC',
    'Noto Sans SC',
    serif;
  color: #fff7df;
  background: var(--club-red, #b7472f);
  border: 1px solid #fff7df;
  border-radius: 5px;
  box-shadow: 0 1px 4px #2b241c66;
  transform: rotate(-6deg) scale(var(--stamp-scale, 1));
}
:is(.portrait-left .portrait-name, .landscape-seat-left .landscape-name) .pair-chop {
  rotate: -90deg;
}
:is(.portrait-right .portrait-name, .landscape-seat-right .landscape-name) .pair-chop {
  rotate: 90deg;
}

@media (prefers-reduced-motion: reduce) {
  [data-life-stamp] [data-timed-at] {
    translate: none;
    rotate: none;
    scale: none;
  }
}
