/* CASA66 motion and feedback ("rustig", docs/design/motion-proposal, docs/design/huisstijl.md → Motion).
   Tokens live in app.css. Only transform and opacity move (the GPU does those; 60 fps on a mid phone); every state
   change also changes shape, size, weight or position, never colour alone (Tim's phone is greyscale).
   prefers-reduced-motion: nothing travels, shakes or loops; things still fade (bottom of this file). */

/* ── press: everything tappable answers the finger within one frame ──────────────────────────────────── */
:is(button.primary, button.secondary, button.bad, .btn-primary, .primary-link, .chip, .scene, .remove, .check, .more, .presence-buttons button,
    .presence-pill, .servings button, .mini > button, .np-controls button, .add-row button, .dot, .who-chip, .who-option, .feedback-button) {
    transition: transform var(--dur-m) var(--ease-spring), background var(--dur-s) var(--ease), color var(--dur-s) var(--ease);
}
:is(button.primary, button.secondary, button.bad, .btn-primary, .primary-link, .chip, .scene, .remove, .check, .more, .presence-buttons button,
    .presence-pill, .servings button, .mini > button, .np-controls button, .add-row button, .dot, .who-chip, .who-option, .feedback-button):active:not(:disabled) {
    transform: scale(var(--press-small));
    transition-duration: var(--dur-xs);
    transition-timing-function: var(--ease);
}
.shortcut, .cards li, .mini, .tile { transition: transform var(--dur-m) var(--ease-spring); }
.shortcut:active, .cards li:active, .mini:has(.mini-open:active), .tile:has(> .open:active) { transform: scale(var(--press)); transition: transform var(--dur-xs) var(--ease); }
.tabs a i { transition: transform var(--dur-m) var(--ease-spring), background var(--dur-s) var(--ease); }
.tabs a:active i { transform: scale(0.9); transition-duration: var(--dur-xs); }
.tabs a.active { font-weight: 800; }
.tabs a.active i { animation: tab-pill var(--dur-m) var(--ease-spring); }

/* ── room tile: switching on fills it with its pastel from the switch outwards, like a lamp coming on ───── */
.tile, .tile.on { background: var(--surface); overflow: hidden; isolation: isolate; }
/* the light: a circle centred on the switch (the tile's switch sits 4 + 22 px from the top, 8 + 29 px from the right) */
.tile::before { content: ""; position: absolute; z-index: -1; width: 30rem; height: 30rem; top: calc(1.625rem - 15rem); right: calc(2.3125rem - 15rem);
    border-radius: 50%; background: var(--c); transform: scale(0); transition: transform var(--dur-m) var(--ease-in); }
.tile.on::before { transform: scale(1); transition: transform var(--dur-l) var(--ease-spring); transition-delay: calc(var(--i, 0) * 45ms); }
/* off = an empty ring, on = a filled dot with a halo, a bit bigger: readable without colour */
.tile .blob { background: transparent; box-shadow: inset 0 0 0 2px var(--muted); transition: transform var(--dur-m) var(--ease-spring), box-shadow var(--dur-s) var(--ease); }
.tile.on .blob { background: #FFFFFF; box-shadow: 0 0 0 0.3rem rgba(255, 255, 255, 0.45); transform: scale(1.25); }
.tile.on .state-text { font-weight: 700; }
/* how bright, as a length: a thin bar along the bottom */
.tile .level { position: absolute; left: var(--s-4); right: var(--s-4); bottom: 0.625rem; height: 0.25rem; border-radius: var(--r-pill); background: var(--room-ink);
    opacity: 0.5; transform-origin: left center; transform: scaleX(0); transition: transform var(--dur-l) var(--ease-spring); pointer-events: none; }
.tile.on .level { transform: scaleX(var(--lvl, 1)); }
/* waiting for the house: the dot breathes */
.tile.pending .blob { animation: breathe 1.1s var(--ease) infinite; }
/* the house said no: a short shake (the switch is already back) */
.shake { animation: shake 360ms var(--ease); }

/* ── switch: the knob stretches while pressed and lands on the curve ───────────────────────────────────── */
.switch span::before { transition: transform var(--dur-m) var(--ease-spring), background var(--dur-s) var(--ease); transform-origin: left center; }
.switch:active span::before { transform: scaleX(1.2); transition-duration: var(--dur-xs); }
.switch input:checked + span::before { transform-origin: right center; }
.switch:active input:checked + span::before { transform: translateX(1.375rem) scaleX(1.2); }
.switch.small:active input:checked + span::before { transform: translateX(1.25rem) scaleX(1.2); }

/* ── dimmer: a thumb you can see beside your finger; the number rides above it while dragging ──────────── */
.dim { position: relative; }
input[type=range]::-webkit-slider-thumb { width: 0.5rem; height: 1.75rem; margin-top: 0.375rem; border-radius: 0.25rem;
    box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.75); transition: transform var(--dur-s) var(--ease-spring); }
input[type=range]::-moz-range-thumb { width: 0.5rem; height: 1.75rem; box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.75); }
.dim.dragging input[type=range]::-webkit-slider-thumb { transform: scale(1.35, 1.2); }
.dim-value { position: absolute; left: 0; bottom: calc(100% - 0.25rem); transform: translateX(var(--x, 0)); pointer-events: none; }
.dim-value span { display: block; min-width: 3.25rem; padding: 0.25rem 0.625rem; border-radius: var(--r-pill); background: var(--text); color: var(--bg);
    font-family: var(--display); font-weight: 800; font-size: 1.125rem; text-align: center; opacity: 0; transform: translate(-50%, 0.5rem) scale(0.6);
    transition: opacity var(--dur-s) var(--ease), transform var(--dur-m) var(--ease-spring); }
.dim.dragging .dim-value span { opacity: 1; transform: translate(-50%, 0) scale(1); }
.dim output { transition: opacity var(--dur-s) var(--ease); }
.dim.dragging output { opacity: 0.35; }
/* disabled (room or lamp off, lamp offline): a neutral fill and no thumb, so it reads as "can't slide" in greyscale too */
input[type=range]:disabled, .sheet .dim input[type=range]:disabled { opacity: 1; cursor: default; --fill: color-mix(in srgb, var(--muted) 35%, var(--surface-2)); }
input[type=range]:disabled::-webkit-slider-thumb { opacity: 0; }
input[type=range]:disabled::-moz-range-thumb { opacity: 0; }

/* ── bottom sheets: come up, follow the finger down, leave quicker than they came (js/sheet.js) ──────── */
.sheet { bottom: calc(-1 * var(--sheet-overhang)); padding-bottom: calc(var(--s-5) + var(--sheet-overhang) + env(safe-area-inset-bottom));
    animation: slide-up var(--dur-l) var(--ease-spring); }
.sheet > .handle, .sheet > .sheet-head { touch-action: none; }
.sheet.dragging { animation: none; transition: none; }
.sheet.settling { transition: transform var(--dur-m) var(--ease-spring); }
.sheet.closing, .who-sheet.closing { animation: slide-down var(--dur-out) var(--ease-in) forwards; }
.scrim.closing, .who-backdrop.closing { animation: fade-out var(--dur-out) var(--ease-in) forwards; }

/* ── scene chip: busy at once, a tick when the house is done ───────────────────────────────────────────── */
.scene { position: relative; overflow: hidden; isolation: isolate; }
.scene.pending { background: var(--accent-soft); }
.scene.pending::after { content: ""; position: absolute; inset: 0; z-index: -1; background: linear-gradient(90deg, transparent, var(--sheen), transparent);
    animation: sweep 0.9s var(--ease) infinite; }
.scene .tick { display: inline-block; margin-right: 0.375rem; animation: pop var(--dur-m) var(--ease-spring); }

/* ── toast: one line above the tab bar (and the mini player), with Terug / Opnieuw (Toasts.razor) ─────── */
.toasts { position: absolute; z-index: 40; left: 0; right: 0; max-width: 35rem; margin: 0 auto; padding: 0 var(--s-2);
    bottom: calc(var(--tabbar-h) + var(--s-2) + env(safe-area-inset-bottom) + var(--mini-h, 0px)); display: grid; gap: var(--s-2); pointer-events: none; }
#app:has(> .mini:not(.leaving)) .toasts { --mini-h: 4.25rem; } /* above the mini player while it shows */
.toast { display: flex; align-items: center; gap: var(--s-2); min-height: 3.25rem; padding: var(--s-1) var(--s-1) var(--s-1) var(--s-4); border-radius: var(--r-l);
    background: var(--text); color: var(--bg); font-weight: 600; box-shadow: var(--shadow-sheet); pointer-events: auto; animation: toast-in var(--dur-m) var(--ease-spring); }
.toast > span { flex: 1; }
.toast button { min-height: var(--tap); padding: 0 var(--s-4); border: 0; border-radius: var(--r-pill); background: rgba(255, 255, 255, 0.16); color: inherit; font-weight: 700; }
.toast.bad { background: var(--bad-soft); color: var(--bad); }
.toast.bad::before { content: "!"; display: grid; place-items: center; width: 1.5rem; height: 1.5rem; flex: none; border-radius: 50%; background: var(--bad); color: var(--bad-soft); font-weight: 800; }
.toast.bad button { background: var(--bad); color: var(--bad-soft); }
.toast.leaving { animation: toast-out var(--dur-out) var(--ease-in) forwards; }

/* ── segments: the chosen pill slides over (js/motion.js places .seg-ind) ──────────────────────────────── */
.segments { position: relative; }
.seg-ind { position: absolute; top: var(--s-1); left: 0; height: calc(100% - 2 * var(--s-1)); width: var(--w, 0); border-radius: var(--r-pill);
    background: var(--surface); box-shadow: 0 1px 3px rgba(42, 35, 53, 0.18); transform: translateX(var(--x, 0)); opacity: 0;
    transition: transform var(--dur-m) var(--ease-spring), width var(--dur-m) var(--ease-spring); } /* width: one absolute pill, nothing reflows */
.seg-ind.placed { opacity: 1; }
.segments:has(.seg-ind.placed) a.active { background: transparent; }
.segments a { position: relative; z-index: 1; transition: color var(--dur-s) var(--ease); }
.segments a.active { font-weight: 700; } /* bolder: white on havermout barely shows in greyscale */
.presence-buttons:has(.seg-ind.placed) { position: relative; }
.presence-buttons .seg-ind { background: var(--text); box-shadow: none; }
.presence-buttons:has(.seg-ind.placed) button { position: relative; z-index: 1; }
.presence-buttons:has(.seg-ind.placed) button.current { background: transparent; }
/* a new page comes in softly */
.page-in { animation: fade-up var(--dur-m) var(--ease) both; }

/* ── lists: ticking off draws the line, holds a moment, then the row goes ──────────────────────────────── */
.list li .name { position: relative; }
.list li.ticking .name::after { content: ""; position: absolute; left: 0; right: 0; top: 0.75em; height: 2px; border-radius: 1px; background: currentColor;
    transform-origin: left center; animation: strike var(--dur-m) var(--ease) both; }
.list li.ticking .name { color: var(--muted); }
.list li.ticking .check::before { background: var(--ok); box-shadow: none; animation: pop var(--dur-m) var(--ease-spring); }
.list li.ticking .check { color: var(--bg); }
.list li.leaving { animation: row-out var(--dur-out) var(--ease-in) forwards; }
.list li.arriving { animation: fade-up var(--dur-m) var(--ease-spring) both; }

/* ── reorder (Taken): the row lifts, the others make room, it settles into its place (js/reorder.js) ───── */
.reorderable li { position: relative; transition: transform var(--dur-m) var(--ease-spring); }
.reorderable li::after { content: ""; position: absolute; inset: 0; border-radius: inherit; box-shadow: var(--shadow-sheet); opacity: 0;
    transition: opacity var(--dur-s) var(--ease); pointer-events: none; }
.reorderable li.dragging, .reorderable li.settling { z-index: 2; }
.reorderable li.dragging { transition: none; }
.reorderable li.dragging::after, .reorderable li.settling::after { opacity: 1; }

/* ── loading: the shapes of what's coming (only after 250 ms, see Skeleton.razor) ─────────────────────── */
.skeleton { position: relative; overflow: hidden; background: var(--surface); border-radius: var(--r-xl); animation: fade-in var(--dur-m) var(--ease) both; }
.skeleton::after { content: ""; position: absolute; inset: 0; background: linear-gradient(90deg, transparent, var(--sheen), transparent);
    transform: translateX(-100%); animation: sweep 1.4s var(--ease) infinite; }
.skeleton.line { height: 1.1rem; border-radius: var(--r-pill); }
.skeleton.tile { min-height: 7.75rem; }
.list .skeleton.row { min-height: 3.5rem; background: var(--surface); }
.loading-line { margin: var(--s-4) 0 var(--s-3); color: var(--muted); animation: fade-in var(--dur-m) var(--ease) both; }
/* a page comes in softly (opacity only: a transform would make <main> the box its sheets are placed in) */
main.page, main.home, main.chat { animation: fade-in var(--dur-s) var(--ease); }
.arrive > * { animation: fade-up var(--dur-m) var(--ease) both; }
.arrive > :nth-child(2) { animation-delay: 30ms; } .arrive > :nth-child(3) { animation-delay: 60ms; } .arrive > :nth-child(4) { animation-delay: 90ms; }
.arrive > :nth-child(5) { animation-delay: 120ms; } .arrive > :nth-child(6) { animation-delay: 150ms; } .arrive > :nth-child(n+7) { animation-delay: 180ms; }

/* ── mini player: rises in, sinks out; three bars move while it plays ──────────────────────────────────── */
.mini { animation: mini-in var(--dur-m) var(--ease-spring); }
.mini.leaving { animation: mini-out var(--dur-out) var(--ease-in) forwards; }
.eq { display: inline-flex; align-items: flex-end; gap: 2px; height: 0.75rem; margin-right: 0.375rem; }
.eq i { width: 3px; height: 100%; border-radius: 1px; background: currentColor; transform-origin: bottom; animation: eq 0.9s var(--ease) infinite alternate; }
.eq i:nth-child(2) { animation-delay: -0.3s; } .eq i:nth-child(3) { animation-delay: -0.6s; }

/* ── presence: the Thuis · Weg · Slaapt switch unfolds from its pill ───────────────────────────────────── */
.presence-choice { animation: fade-down var(--dur-m) var(--ease-spring); transform-origin: top left; }

/* the hidden switch js/motion.js clicks for an iPhone tick (see casa66Haptics) */
.haptic-proxy { position: fixed; left: -100px; top: 0; width: 1px; height: 1px; overflow: hidden; opacity: 0; pointer-events: none; }

@keyframes breathe { 50% { opacity: 0.3; transform: scale(0.85); } }
@keyframes shake { 15% { transform: translateX(calc(-1 * var(--shake))); } 35% { transform: translateX(var(--shake)); }
    55% { transform: translateX(calc(-0.6 * var(--shake))); } 75% { transform: translateX(calc(0.4 * var(--shake))); } }
@keyframes slide-down { to { transform: translateY(calc(100% + var(--sheet-overhang))); } }
@keyframes fade-out { to { opacity: 0; } }
@keyframes fade-in { from { opacity: 0; } }
@keyframes sweep { to { transform: translateX(100%); } }
@keyframes pop { from { transform: scale(0.3); opacity: 0; } }
@keyframes strike { from { transform: scaleX(0); } }
@keyframes toast-in { from { transform: translateY(1rem) scale(0.96); opacity: 0; } }
@keyframes toast-out { to { transform: translateY(0.75rem); opacity: 0; } }
@keyframes tab-pill { from { transform: scaleX(0.55); opacity: 0.3; } }
@keyframes row-out { to { opacity: 0; transform: translateX(1.5rem) scale(0.98); } }
@keyframes fade-up { from { opacity: 0; transform: translateY(0.5rem); } }
@keyframes fade-down { from { opacity: 0; transform: translateY(-0.375rem) scale(0.97); } }
@keyframes mini-in { from { opacity: 0; transform: translateY(100%); } }
@keyframes mini-out { to { opacity: 0; transform: translateY(100%); } }
@keyframes eq { from { transform: scaleY(0.3); } to { transform: scaleY(1); } }

/* ── less motion: nothing travels, shakes, overshoots or loops; changes still fade (120 ms) so nothing pops.
   Haptics are separate (their own switch per person). ── */
@media (prefers-reduced-motion: reduce) {
    :root { --press: 1; --press-small: 1; --shake: 0px; --lift: 1; --dur-l: 120ms; --dur-m: 120ms; --dur-out: 120ms; }
    .sheet, .who-sheet, .mini, .toast, .page-in, .arrive > *, .list li.arriving, .presence-choice, .tabs a.active i { animation-name: fade-in; }
    .sheet.closing, .who-sheet.closing, .mini.leaving, .toast.leaving, .list li.leaving { animation-name: fade-out; }
    .tile::before { transition-duration: var(--dur-s); transition-delay: 0s; }
    .scene.pending::after, .skeleton::after, .eq i, .tile.pending .blob, .shake { animation: none; }
    .list li.ticking .name::after { animation: fade-in 120ms both; }
    .state::before { animation-duration: 2.4s; } /* the loading ring turns slowly instead of spinning */
}
