:root {
  --motion-touch: 140ms;
  --motion-local: 200ms;
  --motion-object: 260ms;
  --motion-structural: 290ms;
  --motion-out: cubic-bezier(.23, 1, .32, 1);
  --motion-move: cubic-bezier(.77, 0, .175, 1);
  --motion-drawer: cubic-bezier(.32, .72, 0, 1);
}

/* Tactile layer: transient feedback only; resting component visuals stay unchanged. */
:is(button, label.composer-tool, .intake-choice) {
  -webkit-tap-highlight-color: transparent;
}

:is(
  .primary,
  .secondary,
  .quiet-button,
  .danger-button,
  .compact-button,
  .composer-send,
  .ask-command-request,
  .home-task-complete,
  .intake-choice,
  label.composer-tool
) {
  transition-timing-function: var(--motion-out);
  transition-duration: var(--motion-touch);
}

html:not([data-motion-input='keyboard']) :is(
  .primary,
  .secondary,
  .compact-button,
  .composer-send,
  .ask-command-request,
  .home-task-complete,
  .intake-choice,
  label.composer-tool
):not(:disabled):active {
  transform: scale(.97);
}

@media (hover: hover) and (pointer: fine) {
  :is(.primary,.secondary,.compact-button,.ask-command-request,.intake-choice):not(:disabled):hover {
    transform: translateY(-1px);
  }

  :is(.primary,.ask-command-request):not(:disabled):hover .icon:last-child {
    transform: translateX(2px);
  }
}

.motion-ripple-host {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}

.motion-ripple-host > :not(.motion-ripple) {
  position: relative;
  z-index: 1;
}

.motion-ripple {
  position: absolute;
  z-index: 0;
  border-radius: 50%;
  pointer-events: none;
  background: currentColor;
  opacity: 0;
  transform: scale(.18);
  animation: motion-ripple 440ms var(--motion-out);
}

@keyframes motion-ripple {
  25% { opacity: .13; }
  to { opacity: 0; transform: scale(1); }
}

/* Object transformation: the action itself becomes pending, success, or retry. */
.motion-action {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  transition:
    transform var(--motion-touch) var(--motion-out),
    background-color var(--motion-object) var(--motion-out),
    color var(--motion-object) var(--motion-out),
    border-color var(--motion-object) var(--motion-out);
}

.motion-action-content {
  position: relative;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  min-width: 0;
  white-space: nowrap;
}

.motion-action-spinner {
  width: 16px;
  height: 16px;
  flex: none;
  border: 2px solid color-mix(in srgb, currentColor 28%, transparent);
  border-top-color: currentColor;
  border-radius: 50%;
  animation: motion-spin 620ms linear infinite;
}

.motion-action-check {
  width: 18px;
  height: 18px;
  flex: none;
  overflow: visible;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.motion-action-check path {
  stroke-dasharray: 24;
  stroke-dashoffset: 24;
  animation: motion-check 240ms var(--motion-out) forwards;
}

.motion-action-error-mark {
  display: grid;
  place-items: center;
  width: 18px;
  height: 18px;
  flex: none;
  border: 1.5px solid currentColor;
  border-radius: 50%;
  font-size: 12px;
  line-height: 1;
}

.motion-action[data-motion-state='error'] {
  animation: motion-shake 190ms var(--motion-out);
}

.motion-success-burst {
  position: absolute;
  z-index: 0;
  inset: -35%;
  border-radius: 50%;
  pointer-events: none;
  background: color-mix(in srgb, currentColor 18%, transparent);
  opacity: 0;
  transform: scale(.72);
  animation: motion-success-burst 420ms var(--motion-out);
}

@keyframes motion-spin { to { transform: rotate(360deg); } }
@keyframes motion-check { to { stroke-dashoffset: 0; } }
@keyframes motion-shake {
  25% { transform: translateX(-3px); }
  55% { transform: translateX(3px); }
  80% { transform: translateX(-1px); }
}
@keyframes motion-success-burst {
  28% { opacity: .42; }
  to { opacity: 0; transform: scale(1.28); }
}

/* Local state morphs: selections, checks, and disclosures. */
.primary .icon,
.ask-command-request .icon:last-child,
.service-select-option > span,
.request-issue-grid button > span,
.home-task-complete > span,
.theme-option > .icon {
  transition: transform var(--motion-local) var(--motion-out), opacity var(--motion-local) var(--motion-out);
}

.home-task-complete:active > span {
  transform: scale(.8);
}

/* State choreography: data changes carry their own motion instead of needing a toast. */
.motion-layout {
  position: relative;
  z-index: 1;
  will-change: transform;
}

.motion-layout-enter {
  transform-origin: 50% 0;
  animation: motion-layout-enter 440ms var(--motion-out) both;
}

.motion-count-change {
  position: relative;
  animation: motion-count-change 400ms var(--motion-out);
}

.motion-count-change::after {
  content: attr(data-motion-previous);
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  pointer-events: none;
  animation: motion-count-old 260ms cubic-bezier(.4,0,1,1) both;
}

.motion-message-enter {
  transform-origin: 82% 100%;
  animation: motion-message-enter 420ms var(--motion-out) both;
}

.request-lifecycle-event.motion-message-enter {
  transform-origin: 50% 50%;
  animation-name: motion-event-enter;
}

.motion-media-enter {
  overflow: clip;
  animation: motion-media-enter 440ms var(--motion-out) both;
}

.motion-error-feedback {
  animation: motion-error-feedback 340ms var(--motion-out);
}

[data-motion-exiting='true'] {
  pointer-events: none !important;
  overflow: clip;
}

@keyframes motion-count-change {
  0% { opacity: .45; transform: translateY(5px) scale(.82); }
  48% { transform: translateY(-1px) scale(1.14); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

@keyframes motion-count-old {
  from { opacity: .86; transform: translateY(0) scale(1); }
  to { opacity: 0; transform: translateY(-7px) scale(.8); }
}

@keyframes motion-layout-enter {
  from { opacity: 0; transform: translateY(-8px) scaleY(.72); filter: blur(2px); }
  68% { opacity: 1; transform: translateY(1px) scaleY(1.025); filter: blur(0); }
  to { opacity: 1; transform: translateY(0) scaleY(1); filter: blur(0); }
}

@keyframes motion-message-enter {
  from { opacity: 0; transform: translateY(10px) scale(.94); filter: blur(2px); }
  70% { opacity: 1; transform: translateY(-1px) scale(1.01); filter: blur(0); }
  to { opacity: 1; transform: translateY(0) scale(1); filter: blur(0); }
}

@keyframes motion-event-enter {
  from { opacity: 0; transform: scaleX(.82); }
  62% { opacity: 1; transform: scaleX(1.025); }
  to { opacity: 1; transform: scaleX(1); }
}

@keyframes motion-media-enter {
  from { opacity: 0; transform: translateY(8px) scale(.88) rotate(-1.4deg); filter: blur(3px); }
  72% { opacity: 1; transform: translateY(-1px) scale(1.015) rotate(.3deg); filter: blur(0); }
  to { opacity: 1; transform: translateY(0) scale(1) rotate(0); filter: blur(0); }
}

@keyframes motion-error-feedback {
  18% { transform: translateX(-4px); }
  42% { transform: translateX(3px); }
  66% { transform: translateX(-2px); }
  84% { transform: translateX(1px); }
}

/* Responsibility line: the current owner advances and the verified event lands. */
.motion-status-advance .scape-line-path > span {
  animation: motion-status-node 430ms var(--motion-out) both;
  animation-delay: calc(var(--motion-index, 0) * 46ms);
}

.motion-status-advance .scape-line-path > span::after {
  transform-origin: 0 50%;
  animation: motion-status-rail 520ms var(--motion-move) both;
}

.motion-status-advance .scape-line-path > span[aria-current='step'] i {
  animation: motion-status-current 620ms var(--motion-out);
}

.motion-latest-enter {
  animation: motion-latest-enter 500ms var(--motion-out) both;
}

@keyframes motion-status-node {
  from { opacity: .48; transform: translateX(-5px) scale(.94); }
  to { opacity: 1; transform: translateX(0) scale(1); }
}

@keyframes motion-status-rail {
  from { transform: scaleX(0); opacity: .38; }
  to { transform: scaleX(1); opacity: 1; }
}

@keyframes motion-status-current {
  0% { transform: scale(.76); box-shadow: 0 0 0 0 color-mix(in srgb, currentColor 30%, transparent); }
  48% { transform: scale(1.16); box-shadow: 0 0 0 8px transparent; }
  to { transform: scale(1); box-shadow: 0 0 0 0 transparent; }
}

@keyframes motion-latest-enter {
  from { opacity: 0; transform: translateY(5px) scale(.97); }
  68% { opacity: 1; transform: translateY(-1px) scale(1.01); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

/* Request scenes assemble from the information approved in the prior step. */
.motion-review-assemble > * {
  animation: motion-review-piece 460ms var(--motion-out) both;
  animation-delay: calc(var(--motion-index, 0) * 54ms);
}

.motion-review-assemble .request-review-heading > span {
  animation: motion-review-seal 620ms var(--motion-out) both;
}

@keyframes motion-review-piece {
  from { opacity: 0; transform: translateY(11px) scale(.975); filter: blur(2px); }
  72% { opacity: 1; transform: translateY(-1px) scale(1.005); filter: blur(0); }
  to { opacity: 1; transform: translateY(0) scale(1); filter: blur(0); }
}

@keyframes motion-review-seal {
  0% { opacity: 0; transform: scale(.55) rotate(-12deg); }
  58% { opacity: 1; transform: scale(1.12) rotate(3deg); }
  to { opacity: 1; transform: scale(1) rotate(0); }
}

.motion-voice-transform {
  transform-origin: 50% 100%;
  animation: motion-voice-composer 580ms var(--motion-out) both;
}

.motion-voice-transform textarea {
  animation: motion-voice-copy 480ms var(--motion-out) both;
}

.motion-map-point {
  animation: motion-map-point 320ms var(--motion-out);
}

@keyframes motion-voice-composer {
  0% { transform: scale(.985); }
  44% { transform: scale(1.012); box-shadow: 0 0 0 7px color-mix(in srgb, currentColor 8%, transparent); }
  to { transform: scale(1); box-shadow: 0 0 0 0 transparent; }
}

@keyframes motion-voice-copy {
  from { opacity: .55; transform: translateY(4px); }
  to { opacity: 1; transform: translateY(0); }
}

@keyframes motion-map-point {
  0% { transform: translateY(-4px) scale(.7); opacity: .4; }
  52% { transform: translateY(1px) scale(1.18); opacity: 1; }
  to { transform: translateY(0) scale(1); opacity: 1; }
}

/* Inputs acknowledge focus, toggles snap into place, and real busy states stay alive. */
:is(.input,.select,.textarea,textarea):focus-visible {
  transform: translateY(-1px);
  transition: transform var(--motion-touch) var(--motion-out), box-shadow var(--motion-local) var(--motion-out), border-color var(--motion-local) var(--motion-out);
}

.scan-state > .icon {
  transform-origin: 50% 50%;
  animation: motion-scan-orbit 1200ms var(--motion-move) infinite;
}

.site-map-cover:not(.error) > i {
  animation: motion-spin 780ms linear infinite;
}

.voice-status b {
  transform-origin: 50% 100%;
  animation: motion-voice-level 680ms ease-in-out infinite alternate;
}

.voice-status b:nth-child(2) { animation-delay: -220ms; }
.voice-status b:nth-child(3) { animation-delay: -440ms; }

@keyframes motion-scan-orbit {
  0% { transform: rotate(0deg) scale(.96); }
  45% { transform: rotate(176deg) scale(1.06); }
  100% { transform: rotate(360deg) scale(.96); }
}

@keyframes motion-voice-level {
  from { transform: scaleY(.42); opacity: .55; }
  to { transform: scaleY(1.28); opacity: 1; }
}

/* Rare delight: review artifacts have depth, bottom sheets can be physically dismissed. */
[data-motion-tilt='true'] {
  will-change: transform;
  transform-style: preserve-3d;
}

html[data-motion-dragging='drawer'],
html[data-motion-dragging='drawer'] * {
  cursor: grabbing !important;
  user-select: none !important;
}

.property-menu .sheet-handle {
  cursor: grab;
  touch-action: none;
}

/* Scroll changes hierarchy without introducing a second header design. */
.screen > .app-header {
  transform-origin: 50% 0;
}

.screen[data-motion-scrolled='true'] > .app-header {
  transform: translateY(var(--motion-header-y, 0)) scale(var(--motion-header-scale, 1));
  will-change: transform;
}

/* The motion coordinator owns these entrances; suppress legacy replay rules. */
.toast,
.ask-expanded-region {
  animation: none;
}

.motion-surface-enter {
  transform-origin: 50% 0;
  animation: motion-surface-enter var(--motion-object) var(--motion-out) both;
}

.property-menu.motion-surface-enter,
.site-area-editor.motion-surface-enter,
.drawing-guidance.motion-surface-enter {
  transform-origin: 50% 100%;
  animation-name: motion-drawer-enter;
  animation-timing-function: var(--motion-drawer);
}

.request-message-attachment-menu.motion-surface-enter {
  transform-origin: 12% 100%;
}

@keyframes motion-surface-enter {
  from { opacity: 0; translate: 0 -4px; scale: .97; }
  to { opacity: 1; translate: 0 0; scale: 1; }
}

@keyframes motion-drawer-enter {
  from { opacity: 0; translate: 0 12px; scale: .98; }
  to { opacity: 1; translate: 0 0; scale: 1; }
}

/* Structural layer: spatial continuity across routes and the request flow. */
.motion-route-enter > [data-motion-item] {
  animation: motion-route-item 300ms var(--motion-out) both;
  animation-delay: calc(var(--motion-index, 0) * 34ms);
}

@keyframes motion-route-item {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: translateY(0); }
}

@supports (view-transition-name: none) {
  html[data-motion-scope='route'] .screen { view-transition-name: scape-screen; }
  html[data-motion-scope='route'] .bottom-nav { view-transition-name: scape-nav; }

  ::view-transition-group(root) {
    animation: none;
    z-index: 0;
  }

  ::view-transition-group(scape-screen) {
    animation-duration: var(--motion-structural);
    animation-timing-function: var(--motion-move);
    overflow: clip;
    z-index: 1;
  }

  ::view-transition-group(scape-nav) {
    animation: none;
    z-index: 3;
  }

  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation: none;
  }

  html[data-motion-scope='theme']::view-transition-old(root) {
    animation: none;
    z-index: 1;
  }

  html[data-motion-scope='theme']::view-transition-new(root) {
    animation: motion-theme-reveal 560ms var(--motion-out) both;
    clip-path: circle(0 at var(--motion-theme-x) var(--motion-theme-y));
    z-index: 2;
  }

  html[data-motion-scope='route'][data-motion-direction='forward']::view-transition-old(scape-screen) {
    animation: motion-route-old-forward 180ms var(--motion-move) both;
  }

  html[data-motion-scope='route'][data-motion-direction='forward']::view-transition-new(scape-screen) {
    animation: motion-route-new-forward var(--motion-structural) var(--motion-out) both;
  }

  html[data-motion-scope='route'][data-motion-direction='back']::view-transition-old(scape-screen) {
    animation: motion-route-old-back 180ms var(--motion-move) both;
  }

  html[data-motion-scope='route'][data-motion-direction='back']::view-transition-new(scape-screen) {
    animation: motion-route-new-back var(--motion-structural) var(--motion-out) both;
  }

  ::view-transition-old(scape-nav),
  ::view-transition-new(scape-nav) {
    animation: none;
    mix-blend-mode: normal;
  }
}

@keyframes motion-route-old-forward {
  to { opacity: 0; transform: translateX(-2.4%) scale(.99); }
}
@keyframes motion-route-new-forward {
  from { opacity: 0; transform: translateX(3.8%) scale(.985); }
}
@keyframes motion-route-old-back {
  to { opacity: 0; transform: translateX(2.4%) scale(.99); }
}
@keyframes motion-route-new-back {
  from { opacity: 0; transform: translateX(-3.8%) scale(.985); }
}
@keyframes motion-theme-reveal {
  from { clip-path: circle(0 at var(--motion-theme-x) var(--motion-theme-y)); }
  to { clip-path: circle(var(--motion-theme-radius) at var(--motion-theme-x) var(--motion-theme-y)); }
}

/* Motion safety: preserve state feedback, remove spatial movement. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --motion-touch: 80ms;
    --motion-local: 120ms;
    --motion-object: 140ms;
    --motion-structural: 140ms;
  }

  .motion-ripple,
  .motion-success-burst,
  .scan-state > .icon,
  .site-map-cover:not(.error) > i,
  .voice-status b,
  .motion-action-check path {
    animation: none !important;
  }

  .motion-surface-enter,
  .motion-route-enter > [data-motion-item],
  .motion-layout-enter,
  .motion-message-enter,
  .motion-media-enter,
  .motion-latest-enter,
  .motion-review-assemble > *,
  .motion-voice-transform,
  .motion-map-point,
  .motion-status-advance .scape-line-path > span,
  .motion-count-change,
  .motion-error-feedback {
    animation: motion-reduced-fade 140ms ease-out both !important;
  }

  [data-motion-tilt='true'],
  :is(.input,.select,.textarea,textarea):focus-visible {
    transform: none !important;
  }

  html:not([data-motion-input='keyboard']) :is(
    .primary,.secondary,.compact-button,.composer-send,.ask-command-request,.home-task-complete,.intake-choice,label.composer-tool
  ):not(:disabled):active {
    transform: none;
  }
}

@keyframes motion-reduced-fade {
  from { opacity: .72; }
  to { opacity: 1; }
}

@media (forced-colors: active) {
  .motion-action-spinner,
  .motion-action-error-mark { border-color: ButtonText; }
  .motion-ripple { background: Highlight; }
}
