/*
 * Paytrack product site (kirbyservice.net): the database's component (round
 * 3, plan.md). A floating 3D card holds a shrunk diagram of the tables and
 * the links between them; five everyday scenarios play on it, one per step.
 * Markup in obj/part_database.php, the beats and the packets in
 * js/parts/database.js, the runner in js/parts/part.js. Keep this file ASCII.
 *
 * Two units. The stage's em (from the column's width and the screen's
 * height, as in the other components) sizes the card, its caption and its
 * label. The drawing has its own em, the drawing unit (du): 1/100 of the
 * drawing's width, so the whole diagram shrinks as one, tables, lines,
 * packets and text alike. How readable its tiny text is does not matter
 * (Dimitar, 02/10/2026); the effects do. Then ("it's ok to place the bubble
 * over the speed label - that way we have a bigger tables"): the tables take
 * all the room the card has, and the card takes all the width its column
 * has (up to 18px a unit, not the other components' 16). Motion is transform
 * and opacity only: every glow is drawn once (box-shadows, wide soft
 * strokes, radial gradients) and only fades in and out.
 *
 * Colours say what happened to a field: mist read, sage written, a soft red
 * deleted, sand written by a trigger. The lines: bone solid, a foreign key;
 * lichen dashed, a value matched in code; sand dashed, a trigger's write.
 *
 * Two compositions: wide (from 576 wide, 35 x 23.6 em) and phones (25.6 em
 * wide, the same drawing smaller). Spacing: nothing of the scene touches
 * anything else, but for two meant overlaps: Db on the card's corner, and
 * the caption passing over the label. Checked with
 * tools/browser/paytrack_overlap_check.js.
 */

/* ---------- the stage: the unit, the depth ------------------------------- */
.pt-part-database { container-type: inline-size; }
.pt-part-database .pt-part-stage { position: relative; font-size: 14px;
  font-size: max(12.5px, min(18px, calc(100cqw / 35), 2.6vh));
  perspective: 1100px; perspective-origin: 54% 40%; }
.pt-part-database .pt-part-tilt { transform-style: preserve-3d; transition: transform .6s cubic-bezier(.2, .8, .3, 1); }
.pt-part-database .db-set { position: relative; width: 35em; height: 23.6em; margin: 0 auto; transform-style: preserve-3d; line-height: 1.2; }
.pt-part-database .db-set i { display: block; font-style: normal; }

/* The light in the room: a mist pool behind the card, and the card's shadow
   on the floor, smaller as the card rises (its bob's clock). Pseudo-elements,
   so no check counts them as objects of the scene. */
.pt-part-database .db-set::before { content: ""; position: absolute; inset: -5% -3%; pointer-events: none;
  background: radial-gradient(48% 42% at 50% 56%, rgba(147, 183, 198, .11), rgba(147, 183, 198, 0) 72%);
  transform: translateZ(-3em); }
.pt-part-database .db-set::after { content: ""; position: absolute; left: 3em; right: 3em; top: 21.7em; height: 1.4em; pointer-events: none;
  background: radial-gradient(closest-side, rgba(0, 0, 0, .55), rgba(0, 0, 0, 0));
  transform: translateZ(-.6em); animation: db-shadow 6.4s ease-in-out infinite; }
@keyframes db-shadow { 0%, 100% { opacity: 1; transform: translateZ(-.6em) scale(1); } 50% { opacity: .7; transform: translateZ(-.6em) scale(.92); } }

/* ---------- the card: floats, turned a little towards the copy -------------- */
.pt-part-database .db-float { position: absolute; left: 0; top: .45em; width: 35em; height: 21.8em; transform-style: preserve-3d;
  animation: db-bob 6.4s ease-in-out infinite; }
@keyframes db-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-.32em); } }
/* Raised, a thin mist band along the top (the layer's tone; a background, so
   it follows the corners), a soft static shadow, and a real edge on the two
   sides that turn towards the eye. Its padding is narrow: the room is the
   tables'. */
.pt-part-database .db-card { position: absolute; inset: 0; padding: .55em .45em .4em; border-radius: .95em; color: var(--pt-bone);
  transform-style: preserve-3d;
  background:
    linear-gradient(var(--pt-mist), var(--pt-mist)) 0 0 / 100% .26em no-repeat,
    linear-gradient(180deg, rgba(236, 230, 218, .05), rgba(236, 230, 218, 0) 40%),
    var(--pt-raised);
  box-shadow: 0 1.8em 2.6em -1.2em rgba(0, 0, 0, .75), 0 .2em .5em rgba(0, 0, 0, .25), inset 0 0 0 1px rgba(236, 230, 218, .07);
  transform: rotateY(-7deg) rotateX(4deg); }
.pt-part-database .db-edge { position: absolute; pointer-events: none; }
.pt-part-database .db-edge-r { right: 0; top: .75em; bottom: .75em; width: .42em; transform-origin: 100% 50%; transform: rotateY(-90deg);
  background: linear-gradient(180deg, #33413A, #1A221E 55%, #121815); }
.pt-part-database .db-edge-b { left: .75em; right: .75em; bottom: 0; height: .42em; transform-origin: 50% 100%; transform: rotateX(90deg);
  background: linear-gradient(90deg, #121815, #232D28 50%, #161D1A); }
/* At every new scenario the card swings a little (two names in turn, so the
   swing starts again each time; from and to are its own pose). */
.pt-part-database[data-step="0"]:not(.db-quick) .db-card,
.pt-part-database[data-step="2"]:not(.db-quick) .db-card,
.pt-part-database[data-step="4"]:not(.db-quick) .db-card { animation: db-swing-a 1.1s ease-in-out; }
.pt-part-database[data-step="1"]:not(.db-quick) .db-card,
.pt-part-database[data-step="3"]:not(.db-quick) .db-card { animation: db-swing-b 1.1s ease-in-out; }
@keyframes db-swing-a { 40% { transform: rotateY(-11deg) rotateX(6deg) translateZ(-.35em); } }
@keyframes db-swing-b { 40% { transform: rotateY(-11deg) rotateX(6deg) translateZ(-.35em); } }

/* ---------- the drawing: 100 du wide, 53.2 du high ------------------------------ */
/* Layers, back to front: the lines (z 0), the packets (just above), the
   tables (.15 du; a lit one lifts to 2.8 du), the scan band (.6 du). */
.pt-part-database .db-draw { position: relative; width: 100em; height: 53.2em; font-size: .341em; transform-style: preserve-3d; }
.pt-part-database .db-lines,
.pt-part-database .db-flow { position: absolute; left: 0; top: 0; width: 100em; height: 53.2em; overflow: visible; pointer-events: none; }
.pt-part-database .db-lines { transform: translateZ(0); }
.pt-part-database .db-flow { transform: translateZ(.08em); }
/* A band of light scans down the drawing as each scenario starts. */
.pt-part-database .db-draw::after { content: ""; position: absolute; left: -.5em; right: -.5em; top: 0; height: 7em; pointer-events: none;
  background: linear-gradient(180deg, rgba(147, 183, 198, 0), rgba(147, 183, 198, .13) 60%, rgba(207, 227, 234, .3) 92%, rgba(147, 183, 198, 0));
  opacity: 0; transform: translate3d(0, -7em, .6em); }
.pt-part-database[data-step="0"]:not(.db-quick) .db-draw::after,
.pt-part-database[data-step="2"]:not(.db-quick) .db-draw::after,
.pt-part-database[data-step="4"]:not(.db-quick) .db-draw::after { animation: db-scan-a 1.5s cubic-bezier(.45, .05, .55, .95) .05s; }
.pt-part-database[data-step="1"]:not(.db-quick) .db-draw::after,
.pt-part-database[data-step="3"]:not(.db-quick) .db-draw::after { animation: db-scan-b 1.5s cubic-bezier(.45, .05, .55, .95) .05s; }
@keyframes db-scan-a {
  0% { opacity: 0; transform: translate3d(0, -7em, .6em); }
  15%, 85% { opacity: 1; }
  100% { opacity: 0; transform: translate3d(0, 53.2em, .6em); }
}
@keyframes db-scan-b {
  0% { opacity: 0; transform: translate3d(0, -7em, .6em); }
  15%, 85% { opacity: 1; }
  100% { opacity: 0; transform: translate3d(0, 53.2em, .6em); }
}

/* ---------- the lines -------------------------------------------------------- */
/* Each link is four copies of one path: two soft strokes for its glow, the
   line itself, and a bright core. The glow and the core fade in while a beat
   runs along the link (.is-hot). */
.pt-part-database .db-rel path { fill: none; stroke-linecap: round; stroke-linejoin: round; }
.pt-part-database .db-ln { stroke-width: .15; }
.pt-part-database .db-fk .db-ln { stroke: rgba(236, 230, 218, .34); }
.pt-part-database .db-val .db-ln { stroke: rgba(167, 175, 164, .72); stroke-width: .13; stroke-dasharray: .55 .42; stroke-linecap: butt; }
.pt-part-database .db-trg .db-ln { stroke: rgba(217, 197, 154, .8); stroke-dasharray: .8 .45; stroke-linecap: butt; }
.pt-part-database .db-lg1 { stroke: #93B7C6; stroke-width: 1.6; stroke-opacity: .11; }
.pt-part-database .db-lg2 { stroke: #93B7C6; stroke-width: .75; stroke-opacity: .26; }
.pt-part-database .db-trg .db-lg1,
.pt-part-database .db-trg .db-lg2 { stroke: #E9D4A2; }
.pt-part-database .db-lh { stroke: #D6E8EE; stroke-width: .28; }
.pt-part-database .db-val .db-lh { stroke-dasharray: .55 .42; stroke-linecap: butt; }
.pt-part-database .db-trg .db-lh { stroke: #F6E6BC; stroke-dasharray: .8 .45; stroke-linecap: butt; }
.pt-part-database .db-lg1,
.pt-part-database .db-lg2,
.pt-part-database .db-lh { opacity: 0; transition: opacity .35s ease; }
.pt-part-database .db-rel.is-hot .db-lg1,
.pt-part-database .db-rel.is-hot .db-lg2,
.pt-part-database .db-rel.is-hot .db-lh { opacity: 1; }

/* Packets (js/parts/database.js places them): a glow and a core in the colour
   of the beat's role. The pulses down the foreign keys are fainter. */
.pt-part-database .db-pk .db-pk-g { fill: url(#db-pg-r); }
.pt-part-database .db-pk .db-pk-c { fill: #E3F0F4; }
.pt-part-database .db-pk.is-w .db-pk-g { fill: url(#db-pg-w); }
.pt-part-database .db-pk.is-w .db-pk-c { fill: #E8F2E8; }
.pt-part-database .db-pk.is-d .db-pk-g { fill: url(#db-pg-d); }
.pt-part-database .db-pk.is-d .db-pk-c { fill: #F8DCD5; }
.pt-part-database .db-pk.is-t .db-pk-g { fill: url(#db-pg-t); }
.pt-part-database .db-pk.is-t .db-pk-c { fill: #FFF4DA; }
.pt-part-database .db-am .db-pk-g { fill: url(#db-pg-a); }
.pt-part-database .db-am .db-pk-c { fill: #D3DFDB; }

/* ---------- the tables --------------------------------------------------------- */
/* What happened to a field or a table, as its colours: the tint of a field,
   the glow and edge of a table. */
.pt-part-database .is-r { --hc: rgba(147, 183, 198, .3); --gl: rgba(147, 183, 198, .55); --gb: #93B7C6; }
.pt-part-database .is-w { --hc: rgba(157, 184, 159, .3); --gl: rgba(157, 184, 159, .55); --gb: #9DB89F; }
.pt-part-database .is-d { --hc: rgba(214, 112, 98, .32); --gl: rgba(214, 112, 98, .55); --gb: #D67062; }
.pt-part-database .is-t { --hc: rgba(217, 197, 154, .32); --gl: rgba(217, 197, 154, .6); --gb: #D9C59A; }
/* A table's place on the card (left, top and height from the PHP), and its
   shadow there, seen only while the table is lifted. */
.pt-part-database .db-tp { position: absolute; width: 14em; transform-style: preserve-3d; }
.pt-part-database .db-tp::before { content: ""; position: absolute; left: .7em; right: -.3em; top: 1.7em; bottom: -1.4em; border-radius: 1em; pointer-events: none;
  background: rgba(0, 0, 0, .45); box-shadow: 0 0 1.4em .5em rgba(0, 0, 0, .45); opacity: 0; transition: opacity .4s ease; }
.pt-part-database .db-tp:has(> .db-t.is-on)::before { opacity: 1; }
/* The table: a dark card with a header band. Lit, it lifts off the card and
   glows in the colour of what happened; done, a faint edge stays. */
.pt-part-database .db-t { position: relative; height: 100%; border-radius: .8em; background: #1E2723;
  box-shadow: inset 0 0 0 .1em rgba(236, 230, 218, .1);
  transform: translateZ(.15em); transition: transform .55s var(--pt-spring); }
.pt-part-database .db-t.is-on { transform: translate3d(0, -.55em, 2.8em); }
.pt-part-database .db-t::before { content: ""; position: absolute; inset: -.13em; border-radius: .9em; pointer-events: none;
  box-shadow: 0 0 1.6em .22em var(--gl, rgba(147, 183, 198, .55)), inset 0 0 0 .17em var(--gb, #93B7C6);
  opacity: 0; transition: opacity .35s ease; }
.pt-part-database .db-t.is-on::before { opacity: 1; }
.pt-part-database .db-t.is-past::before { opacity: .28; }
.pt-part-database .db-th { position: relative; display: flex; align-items: center; height: 3.2em; padding: 0 .85em; border-radius: .8em .8em 0 0;
  background: linear-gradient(180deg, #2B3631, #252F2A); }
.pt-part-database .db-th b { font: 700 1.45em/1.1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; letter-spacing: -.02em;
  color: var(--pt-bone); white-space: nowrap; }
.pt-part-database .db-rs { padding-top: .5em; }
/* A row: its column's name, its tag on the right (PK, FK, UNIQUE, TRIGGER
   in sand ...). Touched, a tint in the role's colour wipes across it and a
   band of light sweeps it once (clipped to the row); done, a faint tint. */
.pt-part-database .db-r { position: relative; display: flex; align-items: center; justify-content: space-between; gap: .55em;
  height: 2.3em; padding: 0 .85em; overflow: hidden; }
.pt-part-database .db-r::before { content: ""; position: absolute; inset: .18em .32em; border-radius: .5em; pointer-events: none;
  background: var(--hc, rgba(147, 183, 198, .3)); box-shadow: inset 0 0 0 .11em var(--gb, #93B7C6);
  opacity: 0; transform: scaleX(.3); transform-origin: 0 50%; transition: opacity .3s ease, transform .5s cubic-bezier(.2, .8, .3, 1); }
.pt-part-database .db-r.is-now::before { opacity: 1; transform: none; }
.pt-part-database .db-r.is-past::before { opacity: .36; transform: none; }
.pt-part-database .db-r::after { content: ""; position: absolute; top: .18em; bottom: .18em; left: 0; width: 34%; pointer-events: none;
  background: linear-gradient(100deg, rgba(255, 255, 255, 0), rgba(255, 255, 255, .62) 50%, rgba(255, 255, 255, 0));
  opacity: 0; transform: translateX(-100%); }
.pt-part-database:not(.db-quick) .db-r.is-now::after { animation: db-sweep .8s ease-out .08s; }
@keyframes db-sweep { 0% { opacity: 1; transform: translateX(-100%); } 80% { opacity: 1; } 100% { opacity: 0; transform: translateX(300%); } }
.pt-part-database .db-cn { position: relative; font: 500 1.24em/1.2 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; color: #C3C9BE; white-space: nowrap; }
.pt-part-database .db-r.is-now .db-cn { color: #FFFFFF; }
.pt-part-database .db-tag { position: relative; font: 700 .88em/1.1 var(--pt-text); letter-spacing: .05em; color: #848D81; white-space: nowrap; }
.pt-part-database .db-tag.is-trg { color: var(--pt-sand); }

/* ---------- the key, in the drawing's free corner ------------------------------ */
.pt-part-database .db-key { position: absolute; left: .6em; top: 21.5em; width: 14em; display: flex; flex-direction: column; align-items: flex-start; gap: .55em; }
.pt-part-database .db-k { display: flex; align-items: center; gap: .6em; font: 500 1.12em/1.2 var(--pt-text); color: #A3AB9F; white-space: nowrap; }
.pt-part-database .db-k i { flex: 0 0 auto; width: 1.05em; height: 1.05em; border-radius: .26em; }
.pt-part-database .db-k-r i { background: #93B7C6; }
.pt-part-database .db-k-w i { background: #9DB89F; }
.pt-part-database .db-k-d i { background: #D67062; }
.pt-part-database .db-k-t i { background: #D9C59A; }
.pt-part-database .db-k-fk { margin-top: .9em; }
.pt-part-database .db-k-fk i,
.pt-part-database .db-k-val i,
.pt-part-database .db-k-trg i { width: 2.6em; height: .16em; border-radius: 0; }
.pt-part-database .db-k-fk i { background: rgba(236, 230, 218, .5); }
.pt-part-database .db-k-val i { background: repeating-linear-gradient(90deg, #A7AFA4 0 .5em, rgba(167, 175, 164, 0) .5em .9em); }
.pt-part-database .db-k-trg i { background: repeating-linear-gradient(90deg, #D9C59A 0 .75em, rgba(217, 197, 154, 0) .75em 1.2em); }

/* ---------- Db, on a spring at the card's bottom left corner ---------------------- */
/* Dimitar, 02/10/2026: "database put a springy db character in the left
   bottom corner sticking out a bit", "with a shadow for 3d effect". Small, in
   front of the card (1.6 em), a little past its left and bottom edges: his
   overlap with the card's corner is meant. He bounces on a spring, squash and
   stretch about his feet (transform only), and springs harder at every new
   scenario (two names in turn, as the card's swing). His own bob (site.css)
   is off here: the spring is his motion. The box is the SVG's: his platters
   fill x 17 to 83% and y 11 to 87.5% of it. */
.pt-part-database .db-me { position: absolute; left: -1.3em; top: 18.35em; width: 4.4em; transform: translateZ(1.6em); pointer-events: none;
  transform-style: preserve-3d; }
.pt-part-database .db-me .pt-char { width: 100%; animation: none; }
.pt-part-database .db-me-step,
.pt-part-database .db-me-spring { transform-origin: 50% 87.5%; }
.pt-part-database .db-me-spring { animation: db-spring 2.8s ease-in-out infinite; }
/* The loop: crouch (squash), up (stretch), the top, down (stretch), land
   (squash), a small rebound, then a rest. */
@keyframes db-spring {
  0%, 80%, 100% { transform: translateY(0) scale(1, 1); }
  8% { transform: translateY(0) scale(1.1, .86); }
  22% { transform: translateY(-22%) scale(.92, 1.1); }
  33% { transform: translateY(-30%) scale(1, 1); }
  45% { transform: translateY(0) scale(.95, 1.06); }
  53% { transform: translateY(0) scale(1.12, .84); }
  63% { transform: translateY(-6%) scale(.97, 1.04); }
  71% { transform: translateY(0) scale(1.03, .97); }
}
.pt-part-database[data-step="0"]:not(.db-quick) .db-me-step,
.pt-part-database[data-step="2"]:not(.db-quick) .db-me-step,
.pt-part-database[data-step="4"]:not(.db-quick) .db-me-step { animation: db-boing-a 1.2s ease-in-out .1s; }
.pt-part-database[data-step="1"]:not(.db-quick) .db-me-step,
.pt-part-database[data-step="3"]:not(.db-quick) .db-me-step { animation: db-boing-b 1.2s ease-in-out .1s; }
/* At a new scenario: a deep crouch, a higher leap with a little twist, a hard
   landing and two rebounds. */
@keyframes db-boing-a {
  0%, 100% { transform: none; }
  10% { transform: scale(1.2, .76); }
  30% { transform: translateY(-26%) scale(.86, 1.18) rotate(-7deg); }
  46% { transform: translateY(-32%) scale(1, 1) rotate(5deg); }
  62% { transform: translateY(0) scale(.92, 1.1); }
  72% { transform: scale(1.18, .8); }
  84% { transform: translateY(-9%) scale(.96, 1.05); }
  92% { transform: scale(1.04, .96); }
}
@keyframes db-boing-b {
  0%, 100% { transform: none; }
  10% { transform: scale(1.2, .76); }
  30% { transform: translateY(-26%) scale(.86, 1.18) rotate(7deg); }
  46% { transform: translateY(-32%) scale(1, 1) rotate(-5deg); }
  62% { transform: translateY(0) scale(.92, 1.1); }
  72% { transform: scale(1.18, .8); }
  84% { transform: translateY(-9%) scale(.96, 1.05); }
  92% { transform: scale(1.04, .96); }
}
/* His shadow, on the card under him and a little to his right, as from a
   light up on the left (inside the card, so it lies on it and moves with
   it): smaller and fainter as he rises, bigger and darker as he lands, on
   the same clocks as the spring and the leap. */
.pt-part-database .db-me-sh { position: absolute; left: .2em; top: 20.8em; width: 4.4em; height: 1.4em; pointer-events: none;
  transform: translateZ(.05em); }
.pt-part-database .db-me-sh > i { width: 100%; height: 100%; border-radius: 50%;
  background: radial-gradient(closest-side, rgba(0, 0, 0, .8), rgba(0, 0, 0, .35) 55%, rgba(0, 0, 0, 0)); animation: db-spring-sh 2.8s ease-in-out infinite; }
@keyframes db-spring-sh {
  0%, 80%, 100% { opacity: .9; transform: scale(1); }
  8% { opacity: 1; transform: scale(1.12, 1.05); }
  22% { opacity: .55; transform: scale(.78); }
  33% { opacity: .45; transform: scale(.7); }
  45% { opacity: .85; transform: scale(.96); }
  53% { opacity: 1; transform: scale(1.14, 1.06); }
  63% { opacity: .8; transform: scale(.92); }
  71% { opacity: .95; transform: scale(1.02); }
}
.pt-part-database[data-step="0"]:not(.db-quick) .db-me-sh,
.pt-part-database[data-step="2"]:not(.db-quick) .db-me-sh,
.pt-part-database[data-step="4"]:not(.db-quick) .db-me-sh { animation: db-boing-sh-a 1.2s ease-in-out .1s; }
.pt-part-database[data-step="1"]:not(.db-quick) .db-me-sh,
.pt-part-database[data-step="3"]:not(.db-quick) .db-me-sh { animation: db-boing-sh-b 1.2s ease-in-out .1s; }
@keyframes db-boing-sh-a {
  0%, 100% { opacity: 1; transform: translateZ(.05em) scale(1); }
  10% { opacity: 1; transform: translateZ(.05em) scale(1.2, 1.08); }
  46% { opacity: .5; transform: translateZ(.05em) scale(.72); }
  72% { opacity: 1; transform: translateZ(.05em) scale(1.18, 1.06); }
  84% { opacity: .85; transform: translateZ(.05em) scale(.94); }
}
@keyframes db-boing-sh-b {
  0%, 100% { opacity: 1; transform: translateZ(.05em) scale(1); }
  10% { opacity: 1; transform: translateZ(.05em) scale(1.2, 1.08); }
  46% { opacity: .5; transform: translateZ(.05em) scale(.72); }
  72% { opacity: 1; transform: translateZ(.05em) scale(1.18, 1.06); }
  84% { opacity: .85; transform: translateZ(.05em) scale(.94); }
}

/* ---------- the footer: the caption, over Dimitar's line --------------------------- */
/* One row under the drawing. The caption (a small frosted panel that says
   what ran, a label and a line) slides under the table the beat is about
   (js: translateX in px), its tail points up at that table, and it swings
   open on its top edge at every beat (two names in turn), then floats. In
   the row's bottom right corner, opposite Db, Dimitar's line (02/10/2026: "on
   the db page - fit somwhere - optimised for speed & simplicity", "bottom
   right", "it's ok to place the bubble over the speed label", "use a
   different bg color for the optimised for"): a solid sand chip with night
   text (11.1:1), readable at every size (.8 em: 14.2px on a 1440 desktop,
   10px on a small phone). The caption stands in front of it and may cover
   it, as allowed. It pops in as the first scenario starts; at every
   scenario a ring spreads from it and a shine crosses it (two names in turn). */
.pt-part-database .db-foot { position: relative; height: 2.5em; margin-top: .2em; transform-style: preserve-3d; }
.pt-part-database .db-chip { position: absolute; right: .05em; top: .75em; display: inline-flex; align-items: center; gap: .45em; padding: .36em .95em .38em .7em; border-radius: 999px;
  background: var(--pt-sand); color: var(--pt-night); box-shadow: 0 .45em 1em -.3em rgba(0, 0, 0, .65), inset 0 1px 0 rgba(255, 255, 255, .35);
  font: 600 .8em/1.2 var(--pt-text); letter-spacing: .01em; white-space: nowrap;
  transform: translateZ(.4em); transform-origin: 100% 50%; }
.pt-part-database .db-chip > span { position: relative; }
.pt-part-database .db-bolt { position: relative; flex: 0 0 auto; width: .75em; height: 1em; fill: var(--pt-night); }
.pt-part-database .db-pop::before { content: ""; position: absolute; inset: -.24em; border-radius: inherit; border: .12em solid var(--pt-sand);
  opacity: 0; pointer-events: none; }
.pt-part-database .db-shine { position: absolute; inset: 0; border-radius: inherit; overflow: hidden; pointer-events: none; }
.pt-part-database .db-shine::after { content: ""; position: absolute; top: -20%; bottom: -20%; left: 0; width: 45%;
  background: linear-gradient(105deg, rgba(255, 255, 255, 0), rgba(255, 255, 255, .55) 50%, rgba(255, 255, 255, 0));
  transform: translateX(-120%); }
.pt-part-database[data-step="0"]:not(.db-quick) .db-chip { animation: db-chip-pop .7s var(--pt-spring) .1s both; }
.pt-part-database[data-step="0"]:not(.db-quick) .db-chip::before,
.pt-part-database[data-step="2"]:not(.db-quick) .db-chip::before,
.pt-part-database[data-step="4"]:not(.db-quick) .db-chip::before { animation: db-ring-a .8s ease-out .35s; }
.pt-part-database[data-step="1"]:not(.db-quick) .db-chip::before,
.pt-part-database[data-step="3"]:not(.db-quick) .db-chip::before { animation: db-ring-b .8s ease-out .2s; }
.pt-part-database[data-step="0"]:not(.db-quick) .db-shine::after,
.pt-part-database[data-step="2"]:not(.db-quick) .db-shine::after,
.pt-part-database[data-step="4"]:not(.db-quick) .db-shine::after { animation: db-shine-a 1s ease-in-out .5s; }
.pt-part-database[data-step="1"]:not(.db-quick) .db-shine::after,
.pt-part-database[data-step="3"]:not(.db-quick) .db-shine::after { animation: db-shine-b 1s ease-in-out .35s; }
@keyframes db-chip-pop { 0% { opacity: 0; transform: translateZ(.4em) scale(.55); } 45% { opacity: 1; } 100% { opacity: 1; transform: translateZ(.4em) scale(1); } }
@keyframes db-ring-a { 0% { opacity: .85; transform: scale(.94); } 100% { opacity: 0; transform: scale(1.08, 1.6); } }
@keyframes db-ring-b { 0% { opacity: .85; transform: scale(.94); } 100% { opacity: 0; transform: scale(1.08, 1.6); } }
@keyframes db-shine-a { 0% { transform: translateX(-120%); } 100% { transform: translateX(300%); } }
@keyframes db-shine-b { 0% { transform: translateX(-120%); } 100% { transform: translateX(300%); } }
.pt-part-database .db-cap { position: absolute; left: 0; top: .4em; transform-style: preserve-3d;
  transition: transform .6s var(--pt-spring), opacity .3s ease; }
.pt-part-database .db-cap.is-off { opacity: 0; }
/* Without JS the page shows the last step: its caption is about the right
   hand table (parts), so it sits at the right, its tail near its right end. */
html:not(.js) .pt-part-database .db-cap { left: auto; right: 0; }
html:not(.js) .pt-part-database .db-cap-in { --tail: calc(100% - 3em); }
.pt-part-database .db-cap-fl { transform-style: preserve-3d; animation: db-capfl 2.8s ease-in-out infinite; }
@keyframes db-capfl { 0%, 100% { transform: translate3d(0, 0, .7em); } 50% { transform: translate3d(0, -.1em, .7em); } }
.pt-part-database .db-cap-in { position: relative; display: flex; align-items: baseline; gap: .55em; padding: .34em .78em .38em; border-radius: .6em; white-space: nowrap;
  background: linear-gradient(180deg, rgba(54, 73, 80, .97), rgba(28, 37, 41, .97));
  box-shadow: 0 .8em 1.4em -.5em rgba(0, 0, 0, .75), inset 0 0 0 1px rgba(147, 183, 198, .45), inset 0 1px 0 rgba(255, 255, 255, .1);
  transform-origin: var(--tail, 2em) 0; }
.pt-part-database .db-cap-in::before { content: ""; position: absolute; top: -.3em; left: calc(var(--tail, 2em) - .3em); width: .6em; height: .6em;
  background: #36494F; box-shadow: inset 1px 1px 0 rgba(147, 183, 198, .45); transform: rotate(45deg); clip-path: polygon(0 0, 100% 0, 0 100%); }
.pt-part-database .db-cap-l { font: 700 .74em/1.2 var(--pt-text); letter-spacing: .12em; text-transform: uppercase; color: var(--pt-mist); }
.pt-part-database .db-cap-t { font: 500 .82em/1.25 var(--pt-text); color: var(--pt-bone); }
.pt-part-database .db-cap-in.is-a { animation: db-open-a .55s cubic-bezier(.2, 1.15, .35, 1) both; }
.pt-part-database .db-cap-in.is-b { animation: db-open-b .55s cubic-bezier(.2, 1.15, .35, 1) both; }
@keyframes db-open-a { from { opacity: 0; transform: rotateX(80deg) scale(.92); } 40% { opacity: 1; } to { opacity: 1; transform: none; } }
@keyframes db-open-b { from { opacity: 0; transform: rotateX(80deg) scale(.92); } 40% { opacity: 1; } to { opacity: 1; transform: none; } }

/* ---------- the steps under the scene ------------------------------------------ */
/* Desktop: small floating pills in a compact wrap; the one in play is lit in
   mist, the layer's tone. */
.pt-part-database .pt-part-steps { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; margin: 12px 0 0; padding: 0; list-style: none; }
.pt-part-database .pt-part-steps li { position: relative; isolation: isolate; display: inline-flex; align-items: center; gap: 7px;
  padding: 6px 12px 6px 9px; border-radius: 999px; background: rgba(236, 230, 218, .06); color: var(--pt-bone);
  font: 500 var(--pt-fs-say)/1.3 var(--pt-text); cursor: default;
  animation: db-pill 6s ease-in-out infinite; animation-delay: calc(var(--i, 0) * -1.2s); }
.pt-part-database .pt-part-steps li::before { content: ""; flex: 0 0 auto; width: 7px; height: 7px; border-radius: 50%;
  background: var(--tone, var(--pt-mist)); opacity: .45; transform: scale(.85); transition: opacity .3s ease, transform .4s var(--pt-spring); }
/* The lit state: a brighter ground and the dot's glow, one layer that fades in. */
.pt-part-database .pt-part-steps li::after { content: ""; position: absolute; z-index: -1; inset: 0; border-radius: inherit;
  background: radial-gradient(circle at 12.5px 50%, rgba(147, 183, 198, .55), rgba(147, 183, 198, 0) 15px), rgba(236, 230, 218, .07);
  opacity: 0; transition: opacity .35s ease; }
.pt-part-database .pt-part-steps li > span { opacity: .7; transition: opacity .3s ease; }
.pt-part-database .pt-part-steps li.is-on::before { opacity: 1; transform: scale(1.2); }
.pt-part-database .pt-part-steps li.is-on::after,
.pt-part-database .pt-part-steps li.is-on > span { opacity: 1; }
.pt-part-database .pt-part-steps li:focus-visible { outline: 2px solid var(--pt-sand); outline-offset: 3px; }
@keyframes db-pill { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-3px); } }

/* Nothing moves in a layer that is not the one in front (the packets and the
   pulses stop with their script). */
.pt-layer:not(.is-current) .pt-part-database .db-float,
.pt-layer:not(.is-current) .pt-part-database .db-set::after,
.pt-layer:not(.is-current) .pt-part-database .db-cap-fl,
.pt-layer:not(.is-current) .pt-part-database .db-me-spring,
.pt-layer:not(.is-current) .pt-part-database .db-me-sh > i,
.pt-layer:not(.is-current) .pt-part-database .pt-part-steps li { animation-play-state: paused; }

/* ---------- the lg column (992 to 1199, about 444 wide): the steps as dots ------- */
/* Five pills would stack here; five dots and the lit step's pill centred
   under them. Only with JS (it lights the step). */
@media (min-width: 992px) and (max-width: 1199.98px) {
  html.js .pt-part-database .pt-part-steps { position: relative; flex-wrap: nowrap; justify-content: center; gap: 0; height: 50px; margin-top: 12px; }
  html.js .pt-part-database .pt-part-steps li { position: static; isolation: auto; justify-content: center; align-items: flex-start;
    width: 22px; height: 14px; padding: 4px 0 0; background: none; animation: none; }
  html.js .pt-part-database .pt-part-steps li::after { display: none; }
  html.js .pt-part-database .pt-part-steps li.is-on::before { transform: scale(1.45); }
  html.js .pt-part-database .pt-part-steps li > span { position: absolute; left: 0; right: 0; top: 21px; width: max-content; margin: 0 auto;
    padding: .4em 1em .45em; border-radius: 999px; background: rgba(236, 230, 218, .08); line-height: 1.3; white-space: nowrap;
    opacity: 0; transform: translateY(5px); pointer-events: none; transition: opacity .3s ease, transform .45s var(--pt-spring); }
  html.js .pt-part-database .pt-part-steps li.is-on > span { opacity: 1; transform: none; pointer-events: auto; }
  html.js .pt-part-database[data-step="-1"] .pt-part-steps li:first-child > span { opacity: .5; transform: none; }
}

/* ---------- phones ------------------------------------------------------------------ */
/* The same drawing, smaller: the card nearly the whole width (25.15 em at
   12.5px, about 202 px high), turned less, and set in .45 em so that Db,
   smaller too, can stick out past its left edge and still keep clear of the
   screen's. The steps become one line: five dots and the caption of the one
   in play. */
@media (max-width: 575.98px) {
  .pt-part-database .pt-part-stage { font-size: 12.5px; perspective: 820px; perspective-origin: 50% 45%; }
  .pt-part-database .db-set { width: 25.6em; height: 17.55em; }
  .pt-part-database .db-set::after { left: 2.4em; right: 2em; top: 16em; height: 1.1em; }
  .pt-part-database .db-float { left: .45em; top: .3em; width: 25.15em; height: 16.15em; }
  .pt-part-database .db-card { padding: .3em; border-radius: .85em; transform: rotateY(-4deg) rotateX(3deg); }
  .pt-part-database .db-edge-r { top: .6em; bottom: .6em; width: .32em; }
  .pt-part-database .db-edge-b { left: .6em; right: .6em; height: .32em; }
  .pt-part-database .db-draw { font-size: .2455em; }
  .pt-part-database .db-me { left: -.5em; top: 13.75em; width: 3.2em; transform: translateZ(1.2em); }
  .pt-part-database .db-me-sh { left: .15em; top: 15.3em; width: 3.2em; height: 1.1em; }
  .pt-part-database .db-foot { height: 2.3em; margin-top: .15em; }
  .pt-part-database .db-cap { top: .38em; }
  .pt-part-database .db-chip { top: .62em; }
  .pt-part-database .db-cap-l { font-size: .8em; letter-spacing: .08em; }
  .pt-part-database .db-cap-t { font-size: .84em; }
}
@media (max-width: 575.98px) {
  html.js .pt-part-database .pt-part-steps { position: relative; flex-wrap: nowrap; justify-content: flex-start; gap: 0; height: 22px; margin-top: 3px; }
  html.js .pt-part-database .pt-part-steps li { position: static; isolation: auto; flex: 0 0 15px; justify-content: center; width: 15px; height: 22px; padding: 0;
    background: none; animation: none; }
  html.js .pt-part-database .pt-part-steps li::after { display: none; }
  html.js .pt-part-database .pt-part-steps li::before { width: 6px; height: 6px; }
  html.js .pt-part-database .pt-part-steps li.is-on::before { transform: scale(1.5); }
  html.js .pt-part-database .pt-part-steps li > span { position: absolute; left: 81px; top: 0; font-size: calc(var(--pt-fs-say) - 1px); line-height: 22px; white-space: nowrap;
    opacity: 0; transform: translateY(6px); pointer-events: none; transition: opacity .3s ease, transform .45s var(--pt-spring); }
  html.js .pt-part-database .pt-part-steps li.is-on > span { opacity: 1; transform: none; pointer-events: auto; }
  html.js .pt-part-database[data-step="-1"] .pt-part-steps li:first-child > span { opacity: .5; transform: none; }
}
/* Short phones (360x640, 375x667): the copy above is tall (a three line
   heading), so about 7 px back for the arrow down, the tables untouched: Db
   hangs .3 em under the card (not .5), the footer and the card's foot are
   slimmer, the steps sit right under the scene, still clear of Db's box. */
@media (max-width: 575.98px) and (max-height: 759.98px) {
  .pt-part-database .db-set { height: 17.25em; }
  .pt-part-database .db-set::after { top: 15.75em; }
  .pt-part-database .db-float { height: 15.9em; }
  .pt-part-database .db-card { padding-bottom: .2em; }
  .pt-part-database .db-foot { height: 2.15em; }
  .pt-part-database .db-chip { top: .55em; }
  .pt-part-database .db-me { top: 13.3em; }
  .pt-part-database .db-me-sh { top: 15.05em; }
  html.js .pt-part-database .pt-part-steps { margin-top: 0; }
}
/* Tall phones (390x844): the unit grows with the width, up to 13.6px. */
@media (max-width: 575.98px) and (min-height: 760px) {
  .pt-part-database .pt-part-stage { font-size: max(12.5px, min(13.6px, calc(100cqw / 25.6))); }
}

/* ---------- the rewind, and the instant draws ------------------------------------ */
/* Before the story starts again, what is lit fades; the drawing stays. */
.pt-part-database.is-rewind .db-r::before,
.pt-part-database.is-rewind .db-t::before,
.pt-part-database.is-rewind .db-tp::before,
.pt-part-database.is-rewind .db-lg1,
.pt-part-database.is-rewind .db-lg2,
.pt-part-database.is-rewind .db-lh,
.pt-part-database.is-rewind .db-cap { opacity: 0; }
.pt-part-database.is-rewind .db-t { transform: translateZ(.15em); }
/* A jump to a step (a hover, a tap, the test hook) glides there quickly with
   no delays and no one-off effects; the reset snaps. Last, to win the ties. */
.pt-part-database.db-quick .db-t,
.pt-part-database.db-quick .db-t::before,
.pt-part-database.db-quick .db-tp::before,
.pt-part-database.db-quick .db-r::before,
.pt-part-database.db-quick .db-lg1,
.pt-part-database.db-quick .db-lg2,
.pt-part-database.db-quick .db-lh,
.pt-part-database.db-quick .db-cap { transition-duration: .3s; transition-delay: 0s; }
.pt-part-database.db-quick .db-r::after,
.pt-part-database.db-quick .db-cap-in { animation: none; }
.pt-part-database.db-snap .db-t,
.pt-part-database.db-snap .db-t::before,
.pt-part-database.db-snap .db-tp::before,
.pt-part-database.db-snap .db-r::before,
.pt-part-database.db-snap .db-lg1,
.pt-part-database.db-snap .db-lg2,
.pt-part-database.db-snap .db-lh,
.pt-part-database.db-snap .db-cap { transition: none; }
.pt-part-database.db-snap .db-r::after,
.pt-part-database.db-snap .db-cap-in { animation: none; }
