/* v110-motion.css — night build Oct 7–8 2026 (phase 1, plan I: micro-interactions and motion). Tokens only (v103-tokens.css: --dur-0…3,
   --ease-out, --press, --grow, --rise, --rise-x, --tr-ui). Loaded last. Fast and calm (120–200 ms), ease-out, no bounce, no spring, nothing iOS-like.

   The app draws every screen anew on each change (main.js render: the old nodes go, new ones come), so an entrance written as a plain keyframe
   would play again on every click inside an open menu or sheet. Two guards keep each opening a single event:
     · the overlay layer (#overlays persists): one number per layer (--in-menu …) eases 0 → 1 on #overlays itself while that layer is shown;
       the layer reads it (opacity, a short travel). A redraw inherits the settled 1 — nothing replays. Cheap: only #overlays' small tree restyles.
     · the page's own sheets and drawers (inside #root or on <body>): their keyframes run only while <body>'s --age-* is still 0, i.e. in the
       first --dur-3 after the layer appears (a container style query on <body>, named dp-app in linear.css). Later redraws get animation:none.
   Hover lives in @media (hover:hover) everywhere (I9: a tap never leaves a "stuck" hover); press works on touch too.
   Under Reduce motion (the system setting, or My account › Preferences › Reduce motion = .reduce-motion on <html>) nothing travels, scales or
   shimmers; the only motion left is an opacity fade of --dur-0 (100 ms) on the overlay layer. */

/* ---------- 1 · one interaction transition (fill · edge · ink · underline · shadow · fade · press) for every control, row and link ----------
   In a cascade layer of its own, so ANY rule of a component wins over it — a chevron's turn, a hover-only control's fade, the skip link keep
   theirs (unlayered beats layered, whatever the specificity) — and the browser files these selectors by tag and attribute (cheap to match). */
@layer dp-motion-base{
  a[href],button,summary,label,input,select,textarea,[role=button],[role=link],[role=tab],[role=menuitem],[role=menuitemradio],[role=option],[role=switch],
  [role=checkbox],[role=radio],[tabindex="0"]{transition:var(--tr-ui)}
}

/* ---------- 2 · press ----------
   A button, a small control or a card: a slight scale (--press .98 — the `scale` property, so a control already placed with `transform` keeps
   its place). A row, a list item, a tile or a nav item: a deeper fill — one more translucent layer over its hover fill (an inset shadow, so
   no row's own background rule has to be outranked). A row or card drawn as a box with controls inside never answers for a press on one of
   them (that control does). One selector per class: the browser files each under its class, so a press costs nothing on the rest of the page. */
.btn:active,.ib:active,.tb-ic:active,.prop:active,.pl-chip:active,.sug-chip:active,.ac-chip:active,.hs-pchip:active,
.hs-star:active,.cp-send:active,.pal-x:active,.stg-sw:active,.db-why:active,.cl-ty:active,.ch-mk-b:active,.ch-fold:active,
.cal-more:active,.dp-prov:active,.dp-sg-btn:active,.rs-chip:active,.annot-n:active,.rp-th-b:active,.empty-a:active,.fd-back:active,
a.chip:active,a.stg-uc:active,.ob-ck:active,.cs-pp:active,.src:active,.cp-ch:active,.ob-open:active{scale:var(--press)}
[aria-disabled="true"]:active,.pl-vsw .prop:active,.pl-views .pl-chip:active{scale:none}   /* a disabled control never answers; a view switch reads as a tab */
.rp-type:active,.sug:active,.fl-opt:active,.cp-card:active,.ig-pr:active,.stg-theme:active,.sm-m:active,a.card:active,
button.card:active,.card[role=button]:active,.card[role=link]:active,
.kb-card:active:not(:has(:is(a[href],button,input,select,textarea,summary,[role=button],[role=checkbox]):active)),
.hs-card:active:not(:has(:is(a[href],button,input,select,textarea,summary,[role=button],[role=checkbox]):active)){scale:var(--press)}
.ig-card:has(.ig-card-a:active){scale:var(--press)}   /* an integration card is one stretched link (its name) — the card answers for it */
.nav-i:active,.sb-sec:active,.sb-user:active,.sb-pj:active,a.sb-brand:active,.stg-ni:active,a.stg-li:active,.ig-ri:active,
.cl-wk:active,.fold-hd:active,.kb-colh:active,.kb-more:active,.cs-sh2:active,.cs-ct:active,.sv-feat-r:active,.sv-pos-r:active,
a.sv-sov-r:active,.ask-rl-i:active,a.db-kr:active,.db-tail:active,.db-mb:active,.db-in:active,.db-ctr:active,a.rp-tr:active,
a.ag-rd:active,.lc-workrow:active,.lc-story:active,.cp-pgl:active,.ob-radio:active,.hs-radio:active,.tk-row:active,.vo-row:active,
button.kp:active,a.kp:active,.inv-s:active,.cal-ev:active,.td-hd:active,.ig-tg:active,.cn-ewh:active,.dn-hd:active,
a.obj-main:active,a.ch-row:active,button.ai-cmp-r:active,.menu-i:active,.pal-i:active,.np-row:active,
.tbl-r[role=link]:active:not(:has(:is(a[href],button,input,select,textarea,summary,[role=button],[role=checkbox]):active)),
.mn-g[role=link]:active:not(:has(:is(a[href],button,input,select,textarea,summary,[role=button],[role=checkbox]):active)),
.hs-row:active:not(:has(:is(a[href],button,input,select,textarea,summary,[role=button],[role=checkbox]):active)),
.cl-row[role=link]:active:not(:has(:is(a[href],button,input,select,textarea,summary,[role=button],[role=checkbox]):active)),
.cg-near[role=link]:active:not(:has(:is(a[href],button,input,select,textarea,summary,[role=button],[role=checkbox]):active)),
.ai-cmp-r[role=link]:active:not(:has(:is(a[href],button,input,select,textarea,summary,[role=button],[role=checkbox]):active)),
.apq-row:active:not(:has(:is(a[href],button,input,select,textarea,summary,[role=button],[role=checkbox]):active)),
.f-row[role=link]:active:not(:has(:is(a[href],button,input,select,textarea,summary,[role=button],[role=checkbox]):active)),
.r-row[role=link]:active:not(:has(:is(a[href],button,input,select,textarea,summary,[role=button],[role=checkbox]):active)),
.ch-row[role=link]:active:not(:has(:is(a[href],button,input,select,textarea,summary,[role=button],[role=checkbox]):active)),
.al-i:has(.al-t:active){box-shadow:inset 0 0 0 999px var(--fill-1)}
.stg-rd:active{background-color:var(--selected)}   /* its edge IS an inset shadow — the press takes the selected fill instead */

/* ---------- 3 · the overlay layer opens (#overlays: menus, ⌘K, the frame of a drawer, the notifications, the chat, Discuss and Ask panels) ----------
   The modal frame of ui/modals.js (.rs-ov.fb-frame) and the message stack (#dp-toasts) bring their own motion (v110-messages.css) — not here. */
@property --in-menu{syntax:"<number>";inherits:true;initial-value:1}
@property --in-dlg{syntax:"<number>";inherits:true;initial-value:1}
@property --in-pop{syntax:"<number>";inherits:true;initial-value:1}
@property --in-panel{syntax:"<number>";inherits:true;initial-value:1}
@supports selector(:has(*)){
  #overlays{--in-menu:0;--in-dlg:0;--in-pop:0;--in-panel:0;transition:--in-menu var(--dur-2) var(--ease-out),--in-dlg var(--dur-3) var(--ease-out),--in-pop var(--dur-2) var(--ease-out),--in-panel var(--dur-2) var(--ease-out)}
  #overlays:has(.menu){--in-menu:1}
  #overlays:has(.overlay:not(.rs-ov)){--in-dlg:1}
  #overlays:has(.notif-pop){--in-pop:1}
  #overlays:has(.chat-panel,.ap){--in-panel:1}
  #overlays .menu{opacity:var(--in-menu);scale:calc(var(--grow) + (1 - var(--grow)) * var(--in-menu))}
  #overlays .menu.is-side{scale:none;translate:calc((var(--in-menu) - 1) * var(--rise)) 0}   /* a rail flyout slides out of the rail */
  #overlays .overlay:not(.rs-ov){opacity:var(--in-dlg)}
  #overlays .overlay:not(.rs-ov) > .palette,#overlays .overlay:not(.rs-ov) > .modal{translate:0 calc((1 - var(--in-dlg)) * var(--rise))}
  #overlays .overlay:not(.rs-ov) > .drawer{translate:calc((1 - var(--in-dlg)) * var(--rise-x)) 0}
  #overlays .notif-pop{opacity:var(--in-pop);translate:0 calc((var(--in-pop) - 1) * var(--rise))}
  #overlays .chat-panel,#overlays .ap{opacity:var(--in-panel);translate:0 calc((1 - var(--in-panel)) * var(--rise))}
}
/* closing: a render removes a layer at once, so a close can only fade if the old node is kept a moment. The hook is ready — main.js moves the
   closing .menu / .overlay / .notif-pop / panel into #dp-leaving (inert, aria-hidden) and removes it after --dur-1 (the lines are in the night
   report). Under Reduce motion the leaving copy is never shown. */
#dp-leaving{position:fixed;inset:0;z-index:960;pointer-events:none}
#dp-leaving > *{animation:dp-leave var(--dur-1) var(--ease-in) both}
@keyframes dp-leave{to{opacity:0;translate:0 calc(var(--rise) / -2)}}

/* ---------- 4 · the page's own sheets and drawers open once (the --age gate) ----------
   <body> is the dp-app container (linear.css) and eases --age-sheet / --age-drawer from 0 to 1 with a step at --dur-3: while it is 0 the layer's
   keyframes run; from then on a redraw of the open layer draws it still. Closing resets the age after the same --dur-3. */
@property --age-sheet{syntax:"<number>";inherits:false;initial-value:0}
@property --age-drawer{syntax:"<number>";inherits:false;initial-value:0}
body:has(.stg-ov,.hs-sheet,.rp-sheet,.cw-scrim,.dg-scrim,#overlays > .rs-layer){--age-sheet:1}
body:has(.hs-dw-bd,.sb-bd,.mk-scrim){--age-drawer:1}
/* the sheets that had no entrance (Reports' sheet, Watch's and the digest's preview, the Dashboard's change panel) now take the same one as the
   others, on their own rules: the scrim fades (dp-fade), the box rises --rise (dp-rise) or the side panel slides in --rise-x (dp-slide) */
@keyframes dp-fade{from{opacity:0}}
@keyframes dp-rise{from{opacity:.6;translate:0 var(--rise)}}   /* night fix (Oct 8, I3): the box stays mostly opaque — the scrim carries the fade, the page's text never shows through it */
@keyframes dp-slide{from{opacity:.6;translate:var(--rise-x) 0}}
@container dp-app style(--age-sheet: 1){
  .stg-ov,.stg-sheet,.hs-sh,.rp-sheet,.rp-sh-box,.cw-scrim,.cw-sheet,.dg-scrim,.dg-sheet,#overlays > .rs-layer,#overlays > .rs-layer > .rs-sheet{animation:none}
}
@container dp-app style(--age-drawer: 1){
  .hs-dw,.app.v89 .sb,.app.v89 .sb-bd,.mk-scrim,.mk-panel{animation:none}
}

/* ---------- 5 · the tab indicator ----------
   A page's tab row (.pl-tab), Settings' tabs (.stg-st) and Integrations' views (.ig-vw): when another tab becomes the current one, its
   underline draws in (--dur-2). <body> remembers which tab is current (--tab-*) and only takes the new number --dur-2 later — so for that
   moment, and only then, the current tab differs from the number <body> holds. Any later redraw finds them equal and draws the line still. */
@property --tab-pl{syntax:"<integer>";inherits:false;initial-value:1}
@property --tab-st{syntax:"<integer>";inherits:false;initial-value:1}
@property --tab-vw{syntax:"<integer>";inherits:false;initial-value:1}
@keyframes dp-tab-in{from{box-shadow:inset 0 -2px 0 transparent}}
/* night fix (Oct 8, I6 · UX6-10): arriving on another page draws its current tab still — main.js marks <body> data-route-fresh for that moment */
body[data-route-fresh] .pl-tab.is-on:nth-child(n of .pl-tab),body[data-route-fresh] .stg-st.is-on:nth-child(n of .stg-st),body[data-route-fresh] .ig-vw.is-on:nth-child(n of .ig-vw){animation:none}
body:has(.pl-tab:nth-child(2 of .pl-tab).is-on){--tab-pl:2}
body:has(.pl-tab:nth-child(3 of .pl-tab).is-on){--tab-pl:3}
body:has(.pl-tab:nth-child(4 of .pl-tab).is-on){--tab-pl:4}
body:has(.pl-tab:nth-child(5 of .pl-tab).is-on){--tab-pl:5}
body:has(.pl-tab:nth-child(6 of .pl-tab).is-on){--tab-pl:6}
body:has(.pl-tab:nth-child(7 of .pl-tab).is-on){--tab-pl:7}
body:has(.pl-tab:nth-child(8 of .pl-tab).is-on){--tab-pl:8}
body:has(.stg-st:nth-child(2 of .stg-st).is-on){--tab-st:2}
body:has(.stg-st:nth-child(3 of .stg-st).is-on){--tab-st:3}
body:has(.stg-st:nth-child(4 of .stg-st).is-on){--tab-st:4}
body:has(.stg-st:nth-child(5 of .stg-st).is-on){--tab-st:5}
body:has(.stg-st:nth-child(6 of .stg-st).is-on){--tab-st:6}
body:has(.ig-vw:nth-child(2 of .ig-vw).is-on){--tab-vw:2}
body:has(.ig-vw:nth-child(3 of .ig-vw).is-on){--tab-vw:3}
body:has(.ig-vw:nth-child(4 of .ig-vw).is-on){--tab-vw:4}
body:has(.ig-vw:nth-child(5 of .ig-vw).is-on){--tab-vw:5}
@container dp-app not style(--tab-pl: 1){.pl-tab:nth-child(1 of .pl-tab).is-on{animation:dp-tab-in var(--dur-2) var(--ease-out)}}
@container dp-app not style(--tab-pl: 2){.pl-tab:nth-child(2 of .pl-tab).is-on{animation:dp-tab-in var(--dur-2) var(--ease-out)}}
@container dp-app not style(--tab-pl: 3){.pl-tab:nth-child(3 of .pl-tab).is-on{animation:dp-tab-in var(--dur-2) var(--ease-out)}}
@container dp-app not style(--tab-pl: 4){.pl-tab:nth-child(4 of .pl-tab).is-on{animation:dp-tab-in var(--dur-2) var(--ease-out)}}
@container dp-app not style(--tab-pl: 5){.pl-tab:nth-child(5 of .pl-tab).is-on{animation:dp-tab-in var(--dur-2) var(--ease-out)}}
@container dp-app not style(--tab-pl: 6){.pl-tab:nth-child(6 of .pl-tab).is-on{animation:dp-tab-in var(--dur-2) var(--ease-out)}}
@container dp-app not style(--tab-pl: 7){.pl-tab:nth-child(7 of .pl-tab).is-on{animation:dp-tab-in var(--dur-2) var(--ease-out)}}
@container dp-app not style(--tab-pl: 8){.pl-tab:nth-child(8 of .pl-tab).is-on{animation:dp-tab-in var(--dur-2) var(--ease-out)}}
@container dp-app not style(--tab-st: 1){.stg-st:nth-child(1 of .stg-st).is-on{animation:dp-tab-in var(--dur-2) var(--ease-out)}}
@container dp-app not style(--tab-st: 2){.stg-st:nth-child(2 of .stg-st).is-on{animation:dp-tab-in var(--dur-2) var(--ease-out)}}
@container dp-app not style(--tab-st: 3){.stg-st:nth-child(3 of .stg-st).is-on{animation:dp-tab-in var(--dur-2) var(--ease-out)}}
@container dp-app not style(--tab-st: 4){.stg-st:nth-child(4 of .stg-st).is-on{animation:dp-tab-in var(--dur-2) var(--ease-out)}}
@container dp-app not style(--tab-st: 5){.stg-st:nth-child(5 of .stg-st).is-on{animation:dp-tab-in var(--dur-2) var(--ease-out)}}
@container dp-app not style(--tab-st: 6){.stg-st:nth-child(6 of .stg-st).is-on{animation:dp-tab-in var(--dur-2) var(--ease-out)}}
@container dp-app not style(--tab-vw: 1){.ig-vw:nth-child(1 of .ig-vw).is-on{animation:dp-tab-in var(--dur-2) var(--ease-out)}}
@container dp-app not style(--tab-vw: 2){.ig-vw:nth-child(2 of .ig-vw).is-on{animation:dp-tab-in var(--dur-2) var(--ease-out)}}
@container dp-app not style(--tab-vw: 3){.ig-vw:nth-child(3 of .ig-vw).is-on{animation:dp-tab-in var(--dur-2) var(--ease-out)}}
@container dp-app not style(--tab-vw: 4){.ig-vw:nth-child(4 of .ig-vw).is-on{animation:dp-tab-in var(--dur-2) var(--ease-out)}}
@container dp-app not style(--tab-vw: 5){.ig-vw:nth-child(5 of .ig-vw).is-on{animation:dp-tab-in var(--dur-2) var(--ease-out)}}

/* ---------- 6 · folds (<details>, "Show details" and every other): the content fades in as it opens and out as it closes ----------
   No height animation — nothing is clipped while it moves (a focus ring inside stays whole); the content's room opens at once, and is given back
   when the fade-out ends. A fold drawn anew already open shows still. */
details::details-content{transition:opacity var(--dur-2) var(--ease-out),translate var(--dur-2) var(--ease-out),content-visibility var(--dur-2) allow-discrete}
details:not([open])::details-content{opacity:0;translate:0 calc(var(--rise) / -2)}

/* ---------- 7 · pop-ups that stay in the page while closed: they open AND close ----------
   (the editor's command menu .dp-cmdk and slash menu .sl-menu — v94-content.css; the calendar's day popover .cal-pop and the tooltip .tip-pop
   — linear.css): their transitions sit on their own rules; here only where they start from. */
@starting-style{
  .dp-cmdk:not([hidden]),.sl-menu:not([hidden]){opacity:0;translate:0 calc(var(--rise) * -1)}
  .cal-pop:popover-open{opacity:0;translate:0 calc(var(--rise) * -1)}
}

/* ---------- 8 · hover where a control had none (pointer only) ---------- */
@media (hover:hover){
  a.ag-in:hover,a.fl-ev-go:hover,.hs-lnk:not(.is-q):hover,.hs-dl:hover,a.more.bl-nm:hover,a.more.cg-fall:hover{text-decoration:underline;text-underline-offset:2px}
  .cm-dot:not(.is-on):hover{border-color:var(--fg-2)}
  button.gr-tg:hover > svg:last-child{color:var(--fg)}
  .ob-in:not(:focus):hover,.hs-ta:not(:focus):hover,.stg-find-w:not(:focus-within):hover{border-color:var(--line-ctl-h)}
  .ob-ck:not(.is-on):hover{border-color:var(--line-ctl-h);color:var(--fg)}
  .stg-ck:hover input:not(:checked):not(:disabled) + .stg-ck-b{box-shadow:inset 0 0 0 1.5px var(--fg-3)}
  :is(.tk-row,.vo-row):hover input:not(:checked):not(:disabled) + .tk-box{border-color:var(--fg-3)}
  label:hover > input:not(:checked):not(:disabled) + .tgl,label:hover > input:not(:checked):not(:disabled) + .stg-tg-k,label:hover > input:not(:checked):not(:disabled) + .tg-k{background:var(--line-ctl-h)}
  label:hover > input:checked:not(:disabled) + .tgl,label:hover > input:checked:not(:disabled) + .stg-tg-k,label:hover > input:checked:not(:disabled) + .tg-k{background:var(--accent-h)}
  .stg-theme:not(.is-on):hover .stg-theme-p{box-shadow:inset 0 0 0 1px var(--line-ctl-h)}
  .stg-tip:hover{color:var(--fg);border-bottom-color:var(--fg-3)}
  /* night fix (Oct 8, I1 · UX6-04): the links that had no hover at all */
  .pl-s a:not([class]):hover,.sv-foot .more:hover,.hx-ar .more:hover,.lc-tag .more:hover,.sec > a.more:hover,.hs-lane a:hover,.hs-foot a:hover{color:var(--fg);text-decoration:underline;text-underline-offset:2px}
  .db-seen.is-in:hover{text-decoration-color:currentColor}
  .hs-since-d > summary:hover{color:var(--fg)}
}
/* a link inside a page's subtitle looks like a link at rest too (WCAG 1.4.1) — as Home's already does */
.pl-s a:not([class]){color:var(--fg-2);text-decoration:underline;text-decoration-color:var(--bd-2);text-underline-offset:2px}

/* ---------- 9 · Reduce motion: nothing travels, scales or shimmers; an opacity fade of --dur-0 (100 ms) is all that is left ---------- */
@media (prefers-reduced-motion:reduce){
  :root{--rise:0px;--rise-x:0px;--press:1;--grow:1}
  #overlays{transition-duration:var(--dur-0)!important}
  .sk,.skel{animation:none;background:var(--bg-3)}
}
:root.reduce-motion{--rise:0px;--rise-x:0px;--press:1;--grow:1}
.reduce-motion #overlays{transition-duration:var(--dur-0)!important}
.reduce-motion .sk,.reduce-motion .skel{animation:none;background:var(--bg-3)}
