/* ajiths.in -- the liquid-glass card that floats over the rain.

   Port of the Liquid Glass II (Dynamics) study: spring physics, spin, squash
   and stretch, impact feedback. Same CSP as the rest of the site
   (`default-src 'none'; style-src 'self'`), so no webfonts and no CDN -- the
   card borrows --mono and the green ramp from style.css.

   It reads as glass because the layers are stacked in a fixed order and never
   allowed to do each other's job:

     refract   a live second copy of #rain, displaced -- the only optics
     print     the lettering, which is ON the glass and so is NOT refracted
     tint      the body colour of the slab
     glare     specular highlight, tracks the pointer
     caustic   the bright seam that swings with the rim angle
     sweep     the slow travelling sheen
     flash     rim light-up on a wall hit
     bevel     the edge, which is what actually sells the thickness
     rim       a 1.4px gradient outline, brightest at the corners

   Refraction is a rigid translation (see glass.js), so nothing here blurs or
   warps the view -- a frosted `backdrop-filter` would read as plastic. */

.glass-stage {
  position: fixed;
  inset: 0;
  z-index: 3;                    /* over the CRT layers, under the HUD */
  perspective: 1400px;           /* must match PERSP in glass.js */
  perspective-origin: 50% 45%;   /* ...and EYE_Y in the incidence angle */
  pointer-events: none;
}

/* Impact ripples live in their own fixed overlay so they are not clipped by
   the card's overflow and do not inherit its rotation. */
.glass-fx {
  position: fixed;
  inset: 0;
  z-index: 3;
  overflow: hidden;
  pointer-events: none;
}

.glass-ripple {
  position: absolute;
  width: 130px;
  height: 130px;
  border-radius: 50%;
  border: 1.5px solid rgba(190, 255, 222, .5);
  box-shadow: 0 0 24px rgba(25, 242, 122, .3), inset 0 0 18px rgba(157, 255, 205, .2);
  transform: translate(-50%, -50%) scale(.25);
  opacity: 0;
}

/* ---------------------------------------------------------------- the slab */

.glass-card {
  --gx: 50%;
  --gy: 50%;
  --rz: 0deg;
  --rim-angle: 135deg;
  --shx: 0px;
  --shy: 30px;
  --shb: 70px;

  position: absolute;
  top: 0;
  left: 0;
  width: 480px;                  /* glass.js overwrites all four */
  height: 303px;
  font-size: 16px;
  border-radius: 28px;

  overflow: hidden;              /* clips the refraction bleed */
  pointer-events: auto;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  cursor: grab;
  outline: none;
  isolation: isolate;
  transform-origin: 50% 50%;

  /* height-driven depth shadow -- no spin or full-velocity terms, those threw
     a huge drifting shadow while spinning */
  box-shadow:
    var(--shx) var(--shy) var(--shb) -18px rgba(0, 6, 3, .62),
    calc(var(--shx) * .4) calc(var(--shy) * .4) 28px -12px rgba(0, 6, 3, .45),
    0 0 72px -12px rgba(25, 242, 122, .18);   /* phosphor bounce onto the page */

  opacity: 0;
  transition: opacity .9s ease;
  will-change: transform;
}

.glass-card.is-in   { opacity: 1; }
.glass-card.grabbed { cursor: grabbing; }

/* Children must never swallow the drag. */
.glass-card > * { pointer-events: none; }

/* Focus has to survive `outline: none` -- underline the wordmark instead, so
   the ring never fights the rim. */
.glass-card:focus-visible .glass-brand__name {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: .3em;
}

/* ------------------------------------------------------------- refraction */

/* A square layer centred on the card, side 2*(RC+20) where RC is the card's
   circumradius -- so it covers the card at ANY rotation. The spec's uniform
   260px bleed is sized for a static background-image div; this one is a canvas
   repainted every frame, so the wasted fill is worth trimming.

   Counter-rotated by -rz, which is what keeps the rain seen through the glass
   locked to the page while the card spins. glass.js owns the geometry. */
.glass-refract {
  position: absolute;
  z-index: 0;
  display: block;
  transform-origin: 50% 50%;
  transform: rotateZ(calc(-1 * var(--rz)));
  filter: saturate(1.12) brightness(1.05);   /* a grade -- it moves no pixels */
  will-change: transform;
}

/* --------------------------------------------------------------- printing */

/* The card is always 30em wide and 18.92em tall (480/1.586 at font-size
   W/480*16), so em and % are interchangeable here and the whole face scales
   as one unit. Blocks are placed absolutely at their real ISO 7810 ID-1
   heights -- chip 30.6% down, number centred at 57%, name at 85% -- because
   distributing them evenly with space-between drifts the number too low. */
.glass-print {
  position: absolute;
  z-index: 2;
  inset: 0;
  color: var(--head);
  text-shadow: 0 1px 2px rgba(0, 8, 4, .66), 0 0 10px rgba(0, 10, 5, .34);
}

/* Insets are % of card width, NOT em: the number sets its own font-size, and
   an em inset would resolve against that and indent it past the chip. The
   card is always 30em wide, so 5% is exactly the 1.5em gutter. */
.glass-print > * {
  position: absolute;
  left: 5%;
  right: 5%;
}

.glass-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1em;
}

.glass-row--top  { top: 1.3em; }

/* Lifted clear of the bottom edge so the name lands at 85% of card height,
   where it sits on a real card -- there is a real margin below the embossing. */
.glass-row--foot { bottom: 2.4em; align-items: flex-end; }

.glass-brand__name {
  margin: 0;
  font-size: 1em;
  font-weight: 600;
  letter-spacing: .34em;
  line-height: 1;
}

.glass-brand__sub {
  margin-top: .6em;
  color: rgba(206, 250, 228, .7);
  font-size: .44em;
  font-weight: 500;
  letter-spacing: .26em;
  line-height: 1.25;
}

.glass-nfc {
  width: 2.1em;
  height: 2.1em;
  margin-top: -.1em;
  flex: 0 0 auto;
  filter: drop-shadow(0 1px 2px rgba(0, 10, 5, .45));
}

/* ~12.3% of card width, as on a real card; right: auto so it keeps its own
   width instead of stretching to the print box. */
.glass-chip {
  top: 30.5%;
  right: auto;
  width: 3.7em;
  height: 2.88em;
  filter: drop-shadow(0 .18em .22em rgba(28, 16, 4, .4));
}

/* The card number. Four groups of four, sitting so the row is centred at 57%
   of card height, which is where it falls on a real card.

   The site's CSP is `font-src 'self'` and there is no webfont in the docroot,
   so this is the same --mono stack as everything else -- but tabular figures,
   wide tracking and an embossed relief are what actually make digits read as
   a card number rather than as body text. Light catches the top-left of each
   stroke and the shadow falls bottom-right, as on a real embossed card. */
.glass-number {
  top: 52.5%;
  display: flex;
  gap: .8em;
  margin: 0;
  font-size: 1.88em;
  font-weight: 500;
  letter-spacing: .075em;
  line-height: 1;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  color: #f6fffb;
  text-shadow:
    -0.5px -0.5px 0   rgba(255, 255, 255, .55),
     1px    1.5px 1px rgba(0, 10, 5, .78),
     0      0     18px rgba(25, 242, 122, .38);
}

.glass-fields {
  display: flex;
  align-items: flex-end;
  gap: 2.4em;
  min-width: 0;
}

.glass-label {
  margin-bottom: .4em;
  color: rgba(200, 246, 224, .64);
  font-size: .44em;
  font-weight: 500;
  letter-spacing: .28em;
  line-height: 1;
  white-space: nowrap;
}

/* Embossed like the number, but at a fraction of the relief -- on a real card
   the name is raised too, just not as proud as the digits. */
.glass-value {
  font-size: .82em;
  font-weight: 500;
  letter-spacing: .14em;
  line-height: 1;
  white-space: nowrap;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  color: #f2fff8;
  text-shadow:
    -0.5px -0.5px 0 rgba(255, 255, 255, .38),
     .8px   1px   1px rgba(0, 10, 5, .7),
     0      0     12px rgba(25, 242, 122, .28);
}

/* Where a payment-network mark would sit: the site's own brightness ramp.
   Placed independently of the name row, so it can sit lower as a real mark
   does without dragging the embossing down with it. */
.glass-ramp {
  bottom: 1.3em;
  left: auto;
  right: 5%;
  width: 3.5em;
  height: .34em;
  border-radius: .17em;
  background: linear-gradient(90deg,
    #03301a, #065b2c, #098c44, #0cb957, #12e070, #2bf98a, #7dffbd, #ccffe4, #f2fff8);
  box-shadow: 0 0 .8em rgba(25, 242, 122, .45);
}

/* ---------------------------------------------------------------- lighting */

.glass-light {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
}

.glass-tint {
  z-index: 3;
  background:
    radial-gradient(115% 95% at var(--gx) var(--gy),
      rgba(233, 255, 244, .1), transparent 52%),
    linear-gradient(118deg,
      rgba(240, 255, 248, .17) 0%, rgba(226, 255, 240, .07) 27%, transparent 61%),
    linear-gradient(to top,
      rgba(0, 16, 8, .2) 0%, rgba(2, 20, 11, .06) 28%, transparent 49%);
}

.glass-glare {
  z-index: 4;
  background:
    radial-gradient(13% 10% at var(--gx) var(--gy),
      rgba(255, 255, 255, .36) 0%, rgba(215, 255, 236, .15) 42%, transparent 100%),
    radial-gradient(52% 44% at var(--gx) var(--gy),
      rgba(196, 252, 224, .19) 0%, rgba(157, 255, 205, .06) 38%, transparent 78%);
  mix-blend-mode: screen;
}

/* The seam swings to face the pointer. */
.glass-caustic {
  z-index: 5;
  background:
    radial-gradient(30% 22% at var(--gx) var(--gy),
      rgba(234, 255, 244, .19), transparent 74%),
    linear-gradient(var(--rim-angle),
      transparent 25%, rgba(157, 255, 205, .08) 45%, rgba(240, 255, 248, .17) 50%,
      rgba(204, 255, 228, .06) 56%, transparent 74%);
  mix-blend-mode: screen;
  opacity: .22;
}

.glass-sweep {
  z-index: 6;
  overflow: hidden;
}

.glass-sweep::before {
  content: '';
  position: absolute;
  top: -25%;
  bottom: -25%;
  left: 0;
  width: 34%;
  background: linear-gradient(90deg,
    transparent 0%, rgba(240, 255, 248, .1) 42%, rgba(240, 255, 248, .28) 50%,
    rgba(240, 255, 248, .1) 58%, transparent 100%);
  mix-blend-mode: screen;
  transform: skewX(-16deg) translateX(-160%);
  animation: glass-sweep 7s cubic-bezier(.55, .08, .35, 1) infinite;
}

/* Rim lights up briefly on a wall hit; glass.js animates the opacity. */
.glass-flash {
  z-index: 7;
  box-shadow:
    inset 0 0 0 1.5px rgba(220, 255, 238, .55),
    inset 0 0 34px rgba(157, 255, 205, .4);
  opacity: 0;
}

/* No rim displacement anywhere, so the bevel is the whole edge illusion. */
.glass-bevel {
  z-index: 8;
  box-shadow:
    inset 0 1.6px 1.2px -.6px rgba(240, 255, 248, .6),
    inset 1.2px 0 1px -.6px rgba(226, 255, 240, .32),
    inset -1.2px 0 1px -.6px rgba(226, 255, 240, .32),
    inset 0 2.5px 8px -2px rgba(240, 255, 248, .1),
    inset 0 -16px 26px -14px rgba(0, 12, 6, .5),
    inset 0 -1.4px 1.2px -.6px rgba(180, 235, 210, .22);
}

/* A 1.4px gradient outline, via the padding-box mask trick. */
.glass-rim {
  z-index: 9;
  padding: 1.4px;
  background: linear-gradient(135deg,
    rgba(240, 255, 248, .78) 0%, rgba(226, 255, 240, .16) 30%,
    rgba(226, 255, 240, .06) 52%, rgba(226, 255, 240, .12) 72%,
    rgba(240, 255, 248, .5) 100%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask-composite: exclude;
}

@keyframes glass-sweep {
  0%   { transform: skewX(-16deg) translateX(-160%); }
  21%  { transform: skewX(-16deg) translateX(430%); }
  100% { transform: skewX(-16deg) translateX(430%); }
}

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

@media (prefers-reduced-motion: reduce) {
  .glass-sweep { display: none; }
  .glass-card  { transition: none; }
}
