/* Micro-interactions: buttons, links, cards, focus, image reveals (20 Oct 2026). Loaded last on every page.
   The owner: «кнопки надо: праймари залитые, секондари рамкой… и кнопки нам надо прикольные какие-то поделать»;
   the navigation: «давай навигашку… может в рамку взять».

   Buttons, second pass (20 Oct; the owner: «яж говорил что-то обводкой сделай, чего они все разные… посмотри
   референсы» and «может не надо везде иконки стрелок»). One button, the same everywhere:
   - the frame's shape: a full pill, 48 px, a 15 px label;
   - outlined by default: 1 px of ink on the page's own ground (never transparent over the dots);
   - filled only for the contact, «Let's talk», as it stands filled in the frame's tab;
   - no arrow, except where the link leaves the site (↗: «Let's talk», a case's live site);
   - the hover swaps them, outlined to filled and filled to outlined: the fill arrives in 4 px cells from the pointer's
     entry point in the world's Bayer order (js/micro-2026.js; the cells are drawn in difference over the button);
   - tertiary: a drawn link, no box («Back to top ↑»).
   References measured on 20 Oct (scratchpad probe/): Cuberto (pills, filled and outlined, swapped on hover; an arrow
   on one secondary link only), Dennis Snellenberg (the outlined «More work» pill), Exo Ape (no arrow until the hover).
   The first pass's 4 px blocks went back: next to the frame's pills they read as a second family, and every visible
   action had been filled, so no outline showed. */

:root{--btn-h:48px;--btn-px:24px;--btn-fs:15px;--btn-r:999px}

/* every button on the page: an outlined pill */
html body :is(.text-button,.svc-cta,.years-more,.footer-contact,.oisy-live-link,.osbb-source-link):not(#_u):not(#_v):not(#_w){
 height:var(--btn-h)!important;min-height:0!important;padding:0 var(--btn-px)!important;border-radius:var(--btn-r)!important;
 font-size:var(--btn-fs)!important;line-height:1!important;letter-spacing:0!important;gap:10px!important;
 display:inline-flex!important;align-items:center!important;justify-content:center!important;box-shadow:none!important;
 background:var(--cool-ground,#ffffff)!important;color:var(--cool-ink,#000000)!important;border:1px solid var(--cool-ink,#000000)!important;
 -webkit-backdrop-filter:none!important;backdrop-filter:none!important;isolation:isolate;overflow:hidden;text-decoration:none!important}

/* the contact: filled, as it stands in the frame's tab */
html body .footer-contact:not(#_u):not(#_v):not(#_w){background:var(--cool-ink,#000000)!important;color:var(--cool-ground,#ffffff)!important}

/* an arrow only where the link leaves the site */
html body :is(.text-button,.svc-cta,.years-more):not([href^="http"]):not(#_u):not(#_v):not(#_w) :is(svg,span[aria-hidden="true"]){display:none!important}

/* tertiary: «Back to top ↑» is a link, no box; its line is drawn on hover like the footer's other links (js/micro-2026.js) */
html body .back-top:not(#_u):not(#_v):not(#_w){height:auto!important;min-height:0!important;padding:0!important;border:0!important;border-radius:0!important;
 background-color:transparent!important;color:var(--cool-ink,#000000)!important;font-size:var(--btn-fs)!important;line-height:1.4!important;letter-spacing:0!important;
 box-shadow:none!important;-webkit-backdrop-filter:none!important;backdrop-filter:none!important;overflow:visible!important;text-decoration:none!important}
/* its line: older layers (css/space-ui.css, css/portfolio-polish-v54.css) set the background shorthand with !important,
   which reset the line's size to the whole box (a black bar, 20 Oct), so size and position are pinned here too */
html body a.back-top[data-mi-ul]:not(#_u):not(#_v):not(#_w){background-size:0% 1px!important;background-position:100% calc(100% - .04em)!important;
 transition:background-size 250ms cubic-bezier(.4,0,.2,1)!important}
@media (hover:hover) and (pointer:fine){html body a.back-top[data-mi-ul]:hover:not(#_u):not(#_v):not(#_w){background-size:100% 1px!important;background-position:0% calc(100% - .04em)!important;
 transition:background-size 700ms cubic-bezier(.16,1,.3,1)!important}}
html body a.back-top:not([data-mi-ul]):not(#_u):not(#_v):not(#_w){background-image:none!important}

/* the label and the arrow take the button's colour (older rules painted them white or black) */
html body :is(.text-button,.back-top,.svc-cta,.years-more,.footer-contact,.oisy-live-link,.osbb-source-link):not(#_u):not(#_v):not(#_w) :is(span,svg,b,em){color:inherit!important;fill:none}
/* and their size: css/redesign.css still sets .text-button span to 26 px */
html body :is(.text-button,.back-top,.svc-cta,.years-more,.footer-contact,.oisy-live-link,.osbb-source-link):not(#_u):not(#_v):not(#_w) :is(span,b,em){font-size:inherit!important}
/* 15 px everywhere: css/portfolio-v42.css still sets the footer's arrow to 28-46 px with !important */
html body :is(.text-button,.back-top,.svc-cta,.years-more,.footer-contact,.oisy-live-link,.osbb-source-link):not(#_u):not(#_v):not(#_w) svg{width:15px!important;height:15px!important;transition:transform .26s cubic-bezier(.16,1,.3,1)}
html body :is(.text-button,.back-top,.svc-cta,.years-more,.footer-contact,.nav-contact,.wo-step,.oisy-live-link,.osbb-source-link).mi-on:not(#_u) svg{transform:translate(2px,-2px)}

/* the station panel's steps: framed circles */
html:is(.space-on,.airy) .work-orbit.is-stations .st-panel .wo-step:not(#_u):not(#_v){background:var(--cool-ground,#ffffff)!important;color:var(--cool-ink,#000000)!important;border:1px solid var(--cool-ink,#000000)!important;isolation:isolate;overflow:hidden;position:relative}
html:is(.space-on,.airy) .work-orbit.is-stations .st-panel .wo-step:hover:not(#_u):not(#_v){background:var(--cool-ground,#ffffff)!important}

/* the hover's cells (js/micro-2026.js): drawn in white, in difference over the button, so ink turns to paper and
   paper to ink, the label with them */
.mi-cells{position:absolute;inset:0;width:100%;height:100%;display:block;pointer-events:none;mix-blend-mode:difference;border-radius:inherit;z-index:3}
/* css/space-ui.css makes every child of a button relative (>*): the canvas then stood in the flow and a hovered button
   widened by the canvas's width (254 to 422 px on the About opener, 20 Oct) */
html body :is(.text-button,.back-top,.svc-cta,.years-more,.footer-contact,.nav-contact,.wo-step)>.mi-cells:not(#_u):not(#_v){position:absolute!important;inset:0!important;width:100%!important;height:100%!important;z-index:3!important;margin:0!important}

/* touch: no hover; a short press answers */
@media (hover:none){html body :is(.text-button,.back-top,.svc-cta,.years-more,.footer-contact,.nav-contact):active:not(#_u){transform:scale(.97)!important;transition:transform .12s ease-out!important}}

/* focus: 2 px of ink, 3 px off, on everything a keyboard reaches and only for the keyboard (the ring follows each
   element's radius, so it stays concentric). In the frame's tabs the ring takes the frame's opposite colour: an ink
   ring on the ink frame would vanish. A target focused by script (main after «Skip to content») gets no ring. */
html body :is(a,button,input,select,textarea,summary,[tabindex],[role=button],[role=tab],[role=switch],[role=link],[contenteditable=true]):focus-visible:not([tabindex="-1"]):not(#_u):not(#_v){outline:2px solid var(--cool-ink,#000000)!important;outline-offset:3px!important}
html.has-frame body .site-header :is(a,button,[tabindex],[role=button],[role=switch]):focus-visible:not([tabindex="-1"]):not(#_u):not(#_v){outline-color:var(--frame-on,#ffffff)!important}
/* the phone's bottom tab clips its own box (portfolio-v42: overflow:clip, for the sheet's rise) and its two buttons stand
   2 px inside it, so the ring 3 px off showed one edge of each: there it is drawn 5 px inside them, on «Let's talk»'s
   pill in the pill's own ink (devices, 22 Oct) */
@media (max-width:700px){html.has-frame body .site-header .glass-nav:not(#_u):not(#_v) :is(.nav-contact,.menu-toggle):focus-visible{outline-offset:-5px!important}html.has-frame body .site-header .glass-nav:not(#_u):not(#_v) .nav-contact:focus-visible{outline-color:var(--frame-ink,#000000)!important}}

/* Text links (js/micro-2026.js marks them: data-mi-ul="hover" for a link with no line at rest, "rest" for one the site
   already underlines). The line is a gradient in the link's own background, so a link over two lines draws across
   both: in from the left in 700 ms on the clear curve, out to the right in 250 ms. A link underlined at rest swaps its
   line: the old one leaves to the right, a new one draws in from the left (950 ms in all). Size and position stay
   unforced so the swap's keyframes can drive them. Fine pointers only: on touch the links stay as they were. */
@media (hover:hover) and (pointer:fine){
 html body a[data-mi-ul]:not(#_u):not(#_v){--mi-ul-y:calc(100% - .04em);text-decoration:none!important;
  background-image:linear-gradient(currentColor,currentColor)!important;background-repeat:no-repeat!important;
  background-size:0% 1px;background-position:100% var(--mi-ul-y);transition:background-size 250ms cubic-bezier(.4,0,.2,1),color 200ms ease-out}
 html body a[data-mi-ul]:hover:not(#_u):not(#_v){background-size:100% 1px;background-position:0% var(--mi-ul-y);
  transition:background-size 700ms cubic-bezier(.16,1,.3,1),color 200ms ease-out}
 html body a[data-mi-ul=rest]:not(#_u):not(#_v){background-size:100% 1px}
 html body a[data-mi-ul=rest]:hover:not(#_u):not(#_v){animation:mi-ul-swap 950ms both}
 /* the footer's links stand in 44 px rows: the line sits under the words, and each link is as wide as its words */
 html body .footer-links a[data-mi-ul]:not(#_u):not(#_v){--mi-ul-y:calc(50% + .72em);width:fit-content!important}
 /* the station panel's «Open the case»: under the words, as its underline sat */
 html body a.st-open[data-mi-ul]:not(#_u):not(#_v){--mi-ul-y:100%}
}
@keyframes mi-ul-swap{
 0%{background-size:100% 1px;background-position:100% var(--mi-ul-y);animation-timing-function:cubic-bezier(.45,0,.55,1)}
 26%{background-size:0% 1px;background-position:100% var(--mi-ul-y)}
 26.4%{background-size:0% 1px;background-position:0% var(--mi-ul-y);animation-timing-function:cubic-bezier(.16,1,.3,1)}
 100%{background-size:100% 1px;background-position:0% var(--mi-ul-y)}
}
/* the years' case names keep their own trigger ([data-on], from the name or its card) and take the links' timing */
html body .year-names a.yn:not(#_u){background-position:100% 94%;transition:opacity 320ms var(--ease,cubic-bezier(.16,1,.3,1)),background-size 250ms cubic-bezier(.4,0,.2,1)}
html body .year-names a.yn[data-on]:not(#_u){background-position:0% 94%;transition:opacity 320ms var(--ease,cubic-bezier(.16,1,.3,1)),background-size 700ms cubic-bezier(.16,1,.3,1)}

/* /work: while a card is under the pointer the others step back (js/micro-2026.js sets .mi-dim on the grid and
   .mi-hot on the card): .35 in 250 ms, back in 500 ms. The hovered cover leans in to 1.03 in 500 ms on the clear
   curve, the picture only, inside its rounded box, and settles in 700 ms. Fine pointers only. */
@media (hover:hover) and (pointer:fine){
 html body[data-page=work] .work-sheet #cardContainer .work-card:nth-child(n):not(#_u):not(#_v){transition:opacity 500ms cubic-bezier(.25,.46,.45,.94)}
 html body[data-page=work] .work-sheet #cardContainer.mi-dim .work-card:nth-child(n):not(.mi-hot):not(#_u):not(#_v){opacity:.35;transition-duration:250ms}
 html body[data-page=work] .work-sheet #cardContainer .work-card.mi-hot:nth-child(n):not(#_u):not(#_v){transition-duration:250ms}
 /* older layers pin the cover with transform:none!important (portfolio-v43, v44, polish-v54), so the lean never showed */
 html body[data-page=work] .work-sheet #cardContainer .work-card:nth-child(n) .work-image:not(#_u):not(#_v)>img{transition:transform 700ms cubic-bezier(.16,1,.3,1)!important}
 html body[data-page=work] .work-sheet #cardContainer .work-card.mi-hot:nth-child(n) .work-image:not(#_u):not(#_v)>img{transform:scale(1.03)!important;transition-duration:500ms!important}
}

/* reduced motion: every one of these answers at once */
@media (prefers-reduced-motion:reduce){
 html body a[data-mi-ul]:not(#_u):not(#_v),html body .year-names a.yn:not(#_u){transition:none!important;animation:none!important}
 html body[data-page=work] .work-sheet #cardContainer .work-card:nth-child(n):not(#_u):not(#_v),
 html body[data-page=work] .work-sheet #cardContainer .work-card:nth-child(n) .work-image:not(#_u):not(#_v)>img{transition:none!important}
}

/* The navigation in the frame (desktop). It stands in a tab of the frame at the top centre, between the brand's and the
   controls' tabs, in the frame's colours (white on the black frame by day, black on the white frame by night), as the
   phone's navigation stands in the frame's bottom tab. It no longer tucks away: the frame holds it, as it holds the
   brand and the controls (js/site-frame.js draws the tab round it). */
@media (min-width:701px){
 html.has-frame body .site-header:not(#_u):not(#_v){top:calc(var(--frame) + var(--frame-pad))!important;height:var(--frame-ctl)!important;pointer-events:none}
 html.has-frame body .site-header .glass-nav:not(#_u):not(#_v):not(#_w){pointer-events:auto;height:var(--frame-ctl)!important;min-height:0!important;padding:0!important;gap:2px!important;border-radius:999px!important;
  background:transparent!important;border:0!important;box-shadow:none!important;transform:none!important;opacity:1!important}
 html.has-frame body .site-header .glass-nav:not(#_u):not(#_v):not(#_w)::before{background:transparent!important}
 html.has-frame body .site-header.is-tucked .glass-nav:not(#_u):not(#_v):not(#_w){transform:none!important;opacity:1!important;pointer-events:auto!important}
 html.has-frame body .site-header .glass-nav:not(#_u):not(#_v):not(#_w) .nav-links{height:var(--frame-ctl)!important;gap:0!important}
 /* min-height 0: the phones' 46 px tap height (css/portfolio-craft.css) made each link 6 px taller than the tab's row,
    so the words sat 3 px under «Let's talk» (20 Oct) */
 html.has-frame body .site-header .glass-nav:not(#_u):not(#_v):not(#_w) .nav-links a{height:var(--frame-ctl)!important;min-height:0!important;padding:0 14px!important;display:inline-flex!important;align-items:center!important;font-size:14px!important;color:var(--frame-on)!important;border-radius:999px!important}
 html.has-frame body .site-header .glass-nav:not(#_u):not(#_v):not(#_w) .nav-drop{background:color-mix(in srgb,var(--frame-on) 16%,transparent)!important}
 html.has-frame body .site-header .glass-nav:not(#_u):not(#_v):not(#_w) .nav-contact{height:var(--frame-ctl)!important;min-height:0!important;padding:0 18px!important;border-radius:999px!important;font-size:14px!important;
  background:var(--frame-on)!important;color:var(--frame-ink)!important;border:1px solid var(--frame-on)!important;box-sizing:border-box!important;isolation:isolate;overflow:hidden;position:relative}
 /* the 1 px edge in the frame's opposite colour keeps the pill's outline while the hover's cells turn it to the tab's ink */
 html.has-frame body .site-header .glass-nav:not(#_u):not(#_v):not(#_w) .nav-contact :is(span,svg){color:var(--frame-ink)!important}
}
/* a split word never breaks between its letters (js/portfolio-motion.js letters(): each letter is an inline-block) */
.lm-word{white-space:nowrap}
/* covers print under the hand (js/micro-2026.js cardPrint): one pixel per 3 px cell, scaled without smoothing, over the
   picture; on /work it leans with the cover */
.card-print{position:absolute;display:block;image-rendering:pixelated;pointer-events:none;z-index:2;border-radius:inherit}
html body[data-page=work] .work-sheet #cardContainer .work-card .work-image>.card-print{transition:transform 700ms cubic-bezier(.16,1,.3,1)}
html body[data-page=work] .work-sheet #cardContainer .work-card.mi-hot .work-image>.card-print{transform:scale(1.03);transition-duration:500ms}

/* the gallery's lens (js/micro-2026.js galleryLens): above the page, under the frame (45); the view runs 12 % past the
   rim so the bend keeps sampling the picture; over a cover the lens is the cursor */
.gallery-lens{position:fixed;left:0;top:0;z-index:44;border-radius:50%;overflow:hidden;pointer-events:none;transform:scale(0);will-change:transform;contain:strict}
.gallery-lens-view{position:absolute;inset:-12%;background:#fff no-repeat;filter:url(#gallery-lens-warp)}
html[data-theme=dark] .gallery-lens-view{background-color:#000}
.gallery-lens-rim{position:absolute;inset:0;width:100%;height:100%}
html.lens-on body[data-page=gallery] .work-card .work-image{cursor:none}
/* -73 (24 Oct; the m2 motion report t03: «the dim clears within 240 ms»): after the way back lands, the grid comes back quickly */
html body[data-page=work] .work-sheet #cardContainer.mi-quick .work-card:nth-child(n):not(#_u):not(#_v):not(#_w){transition-duration:240ms}
