/* =====================================================================
   Ravid Scale · alive kit v1.2 (2026-11-11). Owner rule, every site: alive from the first frame, motion and
   interaction that invite discovery, never a state that looks stuck; no circles/pills/rings, no arrows, no emoji.
   Full rule: ~/Work/kb/rules/site-design.md. Visual reference: the 11-11 sketch board (design/README.md, refs table).
   Pair with alive.js (deferred). Everything is driven by data-alive-* attributes; nothing here needs inline JS.
   Text is never faded: reveals use clip-path; opacity only on decoration.
   ===================================================================== */
:root {
  --shape: 18px 5px 18px 18px;          /* one sharp corner at the reading start (RTL: top-right) */
  --shape-sm: 12px 3px 12px 12px;
  --alive-ease: cubic-bezier(.16, .72, .24, 1);
  --alive-dur: .8s;
}
[dir="ltr"] { --shape: 5px 18px 18px 18px; --shape-sm: 3px 12px 12px 12px; }

/* P14 wipe: content uncovers from the reading start as it enters; the resting state is the final frame, so a page
   without JS (or a thumbnail) is complete. alive.js adds .alive-pending only to what is still below the fold. */
.alive-js [data-alive-wipe].alive-pending { clip-path: inset(-40px -40px -40px 100%); translate: 0 14px; }
[dir="ltr"].alive-js [data-alive-wipe].alive-pending, .alive-js [dir="ltr"] [data-alive-wipe].alive-pending { clip-path: inset(-40px 100% -40px -40px); }
.alive-js [data-alive-wipe].alive-in { animation: alive-wipe var(--alive-dur) var(--alive-ease) both; animation-delay: calc(var(--i, 0) * 90ms); }
@keyframes alive-wipe { from { clip-path: inset(-40px -40px -40px 100%); translate: 0 14px; } to { clip-path: inset(-40px); translate: none; } }

/* P15 tilt + sheen: an image or card leans toward the pointer; with data-alive-sheen a soft light follows it */
[data-alive-tilt] { transform-style: preserve-3d; transition: transform .5s var(--alive-ease); will-change: transform; }
[data-alive-sheen] { position: relative; isolation: isolate; }
[data-alive-sheen]::after { content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; z-index: 2;
  background: radial-gradient(34% 44% at var(--sx, 50%) var(--sy, 40%), var(--alive-sheen, rgba(255, 236, 190, .55)), transparent 70%);
  mix-blend-mode: soft-light; opacity: 0; transition: opacity .3s; }
[data-alive-sheen]:hover::after { opacity: 1; }

/* P16 magnet: the primary button leans a few px toward the pointer */
[data-alive-magnet] { transition: transform .35s var(--alive-ease); will-change: transform; }

/* P17 drag rail: a track you throw with a finger or mouse; items lean with the speed (data-alive-skew) */
[data-alive-drag] { overflow: hidden; cursor: grab; touch-action: pan-y; user-select: none; -webkit-user-select: none; }
[data-alive-drag]:active { cursor: grabbing; }
[data-alive-drag] > * { display: flex; width: max-content; will-change: transform; }
[data-alive-drag] img { pointer-events: none; -webkit-user-drag: none; }
[data-alive-skew] > * > * { transform: skewX(var(--alive-sk, 0deg)); transition: transform .25s; }

/* P18 follow: hovering a row (price list, services) brings its photo next to the pointer */
.alive-follow { position: fixed; left: 0; top: 0; width: min(22vw, 260px); aspect-ratio: 3 / 4; z-index: 60; pointer-events: none;   /* rdc:allow physical: follows pointer clientX/Y, which are measured from the left */
  border-radius: var(--shape); overflow: hidden; box-shadow: 0 24px 44px -20px rgba(0, 0, 0, .55);
  transform: translate(-50%, -50%) scale(.6) rotate(-6deg); clip-path: inset(50%); transition: clip-path .4s var(--alive-ease), transform .4s var(--alive-ease); }
.alive-follow.on { clip-path: inset(0); transform: translate(-50%, -50%) scale(1) rotate(var(--alive-r, -4deg)); }
.alive-follow img { width: 100%; height: 100%; object-fit: cover; }
[data-alive-follow] [data-img] { cursor: default; transition: padding .35s var(--alive-ease), color .25s; }
[data-alive-follow] [data-img].on { padding-inline-start: .8em; }

/* P19 spotlight: the subject sits in shadow, a soft light follows the pointer (markup: img + img.alive-lit) */
[data-alive-spot] { position: relative; overflow: hidden; }
[data-alive-spot] > img:not(.alive-lit) { filter: brightness(.4) saturate(.6); }
[data-alive-spot] > .alive-lit { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  -webkit-mask-image: radial-gradient(34% 44% at var(--lx, 46%) var(--ly, 38%), #000 30%, transparent 75%);
          mask-image: radial-gradient(34% 44% at var(--lx, 46%) var(--ly, 38%), #000 30%, transparent 75%); }

/* P20 split: drag across one image to move between two states (day/dusk, before/after with consent, sketch/build) */
[data-alive-split] { position: relative; overflow: hidden; cursor: ew-resize; touch-action: pan-y; --cut: 55%; }
[data-alive-split] > .alive-b { position: absolute; inset: 0; clip-path: inset(0 0 0 var(--cut)); }
/* RTL: the base state sits at the reading start (right); the second state grows from the left as you drag */
[dir="rtl"] [data-alive-split] > .alive-b { clip-path: inset(0 calc(100% - var(--cut)) 0 0); }
[data-alive-split]:focus-visible { outline: 3px solid currentColor; outline-offset: -3px; }
[data-alive-split] > .alive-handle { position: absolute; top: 0; bottom: 0; left: var(--cut); width: 2px; background: currentColor; z-index: 3; }   /* rdc:allow physical: follows pointer clientX/Y, which are measured from the left */
[data-alive-split] > .alive-handle > span { position: absolute; top: 50%; left: 1px; translate: -50% -50%; white-space: nowrap; padding: .5em 1em; border-radius: var(--shape-sm); background: #fff; color: #222; font-weight: 800; font-size: 13px; }   /* rdc:allow physical: follows pointer clientX/Y, which are measured from the left */

/* P21 free table: pick up an item and put it somewhere else (flash sheets, swatches, moodboard) */
[data-alive-table] { position: relative; }
[data-alive-table] > * { position: absolute; cursor: grab; touch-action: none; transition: scale .2s, box-shadow .2s; }
[data-alive-table] > .alive-lift { cursor: grabbing; scale: 1.06; z-index: 20; box-shadow: 0 26px 40px -14px rgba(0, 0, 0, .5); }
/* spring: items stay in the grid; a picked-up item follows the pointer and returns when dropped */
[data-alive-table="spring"] > * { position: relative; }
[data-alive-table] > .alive-back { transition: translate .5s cubic-bezier(.3, 1.4, .5, 1), scale .2s, box-shadow .2s; }

/* P22 stretch link: "read more" is a line that grows toward the reading direction, never an arrow glyph */
.alive-link::after { content: ""; display: inline-block; width: 16px; height: 2px; margin-inline-start: 10px; vertical-align: middle;
  background: currentColor; transition: width .35s var(--alive-ease); }
.alive-link:hover::after, a:hover > .alive-link::after, a:focus-visible > .alive-link::after { width: 34px; }

/* P24 curtain: two fabric panels part when the block enters (collections, a reveal of the work); decoration only,
   the content is underneath and complete. --alive-velvet sets the fabric. After it opens the panels are removed. */
.alive-js [data-alive-curtain]:not(.alive-curtain-done) { position: relative; overflow: clip; }
.alive-js [data-alive-curtain]:not(.alive-curtain-done)::before,
.alive-js [data-alive-curtain]:not(.alive-curtain-done)::after { content: ""; position: absolute; top: 0; bottom: 0; width: 50.5%; z-index: 6; pointer-events: none;
  background: var(--alive-velvet, repeating-linear-gradient(90deg, rgba(0, 0, 0, .3) 0 7px, rgba(255, 255, 255, .05) 7px 15px, rgba(0, 0, 0, .16) 15px 24px), linear-gradient(170deg, #5a1424, #2a0812));
  transition: transform 1.7s cubic-bezier(.7, 0, .25, 1) .2s; }   /* slow start: it reads as a curtain opening, not a flash */
.alive-js [data-alive-curtain]:not(.alive-curtain-done)::before { left: 0; transform-origin: 0 50%; }    /* rdc:allow physical: each half parts toward its own screen edge */
.alive-js [data-alive-curtain]:not(.alive-curtain-done)::after { right: 0; transform-origin: 100% 50%; } /* rdc:allow physical: each half parts toward its own screen edge */
.alive-js [data-alive-curtain].alive-open::before { transform: translateX(-100%) scaleX(.35); }
.alive-js [data-alive-curtain].alive-open::after { transform: translateX(100%) scaleX(.35); }

/* still: final frame everywhere, no pointer effects */
@media (prefers-reduced-motion: reduce) {
  .alive-js [data-alive-wipe].alive-pending, .alive-js [data-alive-wipe].alive-in { clip-path: none; translate: none; animation: none; }
  [data-alive-tilt], [data-alive-magnet] { transform: none !important; transition: none; }
  .alive-follow { display: none; }
  [data-alive-curtain]::before, [data-alive-curtain]::after { display: none !important; }
}
html.a11y-stop-motion [data-alive-wipe] { clip-path: none !important; translate: none !important; animation: none !important; }
html.a11y-stop-motion [data-alive-tilt], html.a11y-stop-motion [data-alive-magnet] { transform: none !important; }
html.a11y-stop-motion .alive-follow { display: none; }
html.a11y-stop-motion [data-alive-curtain]::before, html.a11y-stop-motion [data-alive-curtain]::after { display: none !important; }
