/* The Macro Notebook: touch and feel (November 2026).
   The design and composition stay as they are; this layer changes only how the site answers the reader.
   One movement language: things arrive from the left and leave to the right, like a line of type being set.
   Underlines and button fills wipe in from the left and out to the right; pages slide a little upward; nothing
   bounces. Quick in, quicker out. Hover effects only on devices with a pointer that hovers; on touch, the press
   shows instead. Readers who ask for less motion get every state at once, with no movement. */

:root {
  --e-out: cubic-bezier(.2, .75, .25, 1);
  --e-in-out: cubic-bezier(.65, 0, .35, 1);
  --t-fast: 140ms;
  --t: 220ms;
  --t-slow: 420ms;
}

/* ---------- Touch: no grey tap flash, no double-tap zoom wait on controls ---------- */
a, button, summary, label, select, [role="button"] { -webkit-tap-highlight-color: transparent; }
button, .btn, .tool-link, .sect, .c-chip, .c-btn, summary, select { touch-action: manipulation; }

/* ---------- Selection in the brand amber ---------- */
::selection { background: var(--amber); color: #000000; }
.chrome ::selection, .rail ::selection { background: var(--amber); color: #000000; }

/* ---------- Reading: no single word left alone on a paragraph's last line ---------- */
.prose p, .prose li, .story-text, .art-deck, .ear-cap, .tool-what { text-wrap: pretty; }
.story-head, .row h3, .tool-title { text-wrap: balance; }
.prose blockquote, .prose q { hanging-punctuation: first; }

/* ---------- Focus: the ring snaps in close to the control ---------- */
.motion :where(a, button, input, select, textarea, summary, [tabindex]) {
  outline-offset: 7px;
  transition-property: outline-offset;
  transition-duration: var(--t-fast);
  transition-timing-function: var(--e-out);
}
.motion :where(a, button, input, select, textarea, summary, [tabindex]):focus-visible { outline-offset: 2px; }

/* ---------- Buttons: the ink wipes in from the left and out to the right ----------
   --base is the button's resting fill, --fill the fill it wipes to, --on the text on the fill. */
.motion :is(.btn, .tool-link, .strip-btn, .save-btn, .c-btn, .tools-list a, .cmd-opt) {
  --base: transparent; --fill: var(--ink); --on: var(--bg);
  background-color: var(--base);
  background-image: linear-gradient(var(--fill), var(--fill));
  background-repeat: no-repeat;
  background-position: 100% 0;
  background-size: 0% 100%;
  transition: background-size var(--t) var(--e-out), color var(--t-fast) ease-out, border-color var(--t-fast) ease-out,
    transform 90ms ease-out, box-shadow var(--t) var(--e-out), outline-offset var(--t-fast) var(--e-out);
}
.motion .tool-link, .motion .save-btn, .motion .tools-list a, .motion .cmd-opt { --on: var(--page); }
.motion .tools-list a, .motion .cmd-opt { --on: var(--box); }
.motion .mail .btn { --base: var(--amber); --on: #000000; }
.motion .cal-page .c-btn { background-color: var(--base); background-image: linear-gradient(var(--fill), var(--fill)); background-repeat: no-repeat; background-position: 100% 0; background-size: 0% 100%; }
.motion .cal-page .c-btn.solid { --base: var(--ink); --fill: var(--amber); --on: #000000; color: var(--page); }
.motion .save-btn[aria-pressed="true"] { --base: var(--ink); color: var(--page); }
.motion .strip-btn:disabled, .motion .btn:disabled { background-size: 0% 100% !important; }

@media (hover: hover) {
  .motion :is(.btn, .tool-link, .strip-btn:not(:disabled), .save-btn, .c-btn, .tools-list a, .cmd-opt):is(:hover, :focus-visible),
  .motion .cal-page :is(.c-btn, .c-btn.solid):is(:hover, :focus-visible),
  .motion .mail .btn:is(:hover, :focus-visible) {
    background-color: var(--base);
    background-image: linear-gradient(var(--fill), var(--fill));
    background-repeat: no-repeat;
    background-position: 0 0;
    background-size: 100% 100%;
    color: var(--on);
  }
  .motion .cal-page .c-btn.solid:is(:hover, :focus-visible) { border-color: var(--amber); }
  .motion .tools-list a:is(:hover, :focus-visible) span { color: inherit; }
}
/* The keyboard choice in the Find list fills the same way. */
.motion .cmd-opt[aria-selected="true"] { background-position: 0 0; background-size: 100% 100%; }

/* Touch screens: the site's hover fills would stay on after a tap, so they show only while the finger is down. */
@media (hover: none) {
  .motion :is(.btn, .tool-link, .strip-btn, .save-btn, .c-btn, .tools-list a):hover {
    background-color: var(--base); background-image: linear-gradient(var(--fill), var(--fill)); background-size: 0% 100%;
    color: inherit;
  }
  .motion .cal-page .c-btn.solid:hover { color: var(--page); border-color: var(--ink); }
  .motion .sect:hover { background: none; }
  .motion .sect:hover::after { transform: scaleX(0); }
  .motion .sect[aria-current="page"]::after { transform: scaleX(1); }
  .motion :is(.btn, .tool-link, .strip-btn:not(:disabled), .save-btn, .c-btn, .tools-list a):active {
    background-position: 0 0; background-size: 100% 100%; color: var(--on); transition-duration: 60ms;
  }
}

/* Every control gives a little under the finger or the click. */
.motion :is(.btn, .tool-link, .strip-btn:not(:disabled), .save-btn, .c-btn, .c-chip, .theme button, .c-seg button, .sect, .linkish):active {
  transform: translateY(1px);
}

/* A button that is waiting (sending a message) shows that it works, with ink running through it. */
.motion :is(.btn, .c-btn)[aria-busy="true"], .motion .contact-send .btn:disabled, .motion .mail .btn:disabled {
  background-image: linear-gradient(90deg, transparent 0 30%, color-mix(in srgb, currentColor 22%, transparent) 50%, transparent 70% 100%);
  background-size: 250% 100% !important; background-position: 100% 0;
  animation: busy 1.1s linear infinite;
}
@keyframes busy { to { background-position: -150% 0; } }

/* ---------- Segmented controls: the dark block slides to the chosen side (Paper / Terminal, Month / Week / List) */
.seg-host { position: relative; isolation: isolate; }
.seg-ind {
  position: absolute; z-index: -1; top: 0; left: 0; height: 100%;
  background: var(--ink); pointer-events: none;
  transition: transform var(--t-slow) var(--e-out), width var(--t-slow) var(--e-out);
}
.seg-ready > button { position: relative; transition: color var(--t) ease-out; }
.seg-ready > button[aria-pressed="true"] { background: transparent !important; color: var(--page); }
.cal-page .seg-ready > button[aria-pressed="true"] { color: var(--page); }
@media (hover: hover) {
  .seg-ready > button[aria-pressed="false"]:hover { box-shadow: inset 0 -3px 0 var(--amber); }
}

/* ---------- Links ----------
   The drawn underline (app.js) now leaves to the right when the pointer moves away, instead of shrinking back. */
.motion .u-line, .motion .u-wrap .u { background-position: 100% 100%; }
.motion .u-fl .fl { background-position: 100% 96%; }
.motion .u-line:is(:hover, :focus-visible), .motion .u-wrap:is(:hover, :focus-visible) .u { background-position: 0 100%; }
.motion .u-fl:is(:hover, :focus-visible) .fl { background-position: 0 96%; }
/* Links underlined at rest: the line thickens and closes up under the words. */
.motion a { transition: color var(--t-fast) ease-out, text-decoration-color var(--t-fast) ease-out, text-decoration-thickness var(--t-fast) ease-out, text-underline-offset var(--t) var(--e-out), outline-offset var(--t-fast) var(--e-out); }
@media (hover: hover) {
  .motion :is(.prose, .src, .ear-src, .share, .fine, .backmatter) a:hover { text-decoration-thickness: 3px; text-underline-offset: 2px; }
}

/* ---------- The Tools menu: it opens from its button, the rows follow one after the other ---------- */
.motion .tools-list:not([hidden]) { animation: menu-open 200ms var(--e-out) both; transform-origin: top left; }
.motion .tools-list:not([hidden]) a { animation: menu-row 260ms var(--e-out) both; animation-delay: calc(var(--i, 0) * 22ms + 40ms); }
@keyframes menu-open { from { opacity: 0; clip-path: inset(0 0 100% 0); } to { opacity: 1; clip-path: inset(0 0 0 0); } }
@keyframes menu-row { from { opacity: 0; transform: translateX(-6px); } }
.motion .account-menu:not([hidden]) { animation: menu-open 200ms var(--e-out) both; }

/* The Find list opens the same way; the choice moves with the arrow keys. */
.motion .cmd-list:not([hidden]) { animation: menu-open 180ms var(--e-out) both; }
.motion .cmd-opt { transition: background-size 160ms var(--e-out), color 120ms ease-out; }
.cmd input.ph-out { transition: opacity 180ms ease-in; opacity: 0.25; }
.cmd input.ph-in { transition: opacity 260ms ease-out; opacity: 1; }

/* ---------- The sign-in window: it rises into place over a darkening page ---------- */
.motion .signin-dialog[open] { animation: dialog-in 260ms var(--e-out) both; }
.motion .signin-dialog[open]::backdrop { animation: fade-in 260ms ease-out both; }
@keyframes dialog-in { from { opacity: 0; transform: translateY(10px) scale(0.985); } }
@keyframes fade-in { from { opacity: 0; } }

/* ---------- Section tabs: the icon lifts, a new-stories badge arrives ---------- */
.motion .sect-icon { transition: transform var(--t) var(--e-out); }
@media (hover: hover) { .motion .sect:hover .sect-icon { transform: translateY(-2px); } }
.motion .sect { transition: background-color var(--t) ease-out, transform 90ms ease-out; }
.motion .sect-new:not([hidden]) { animation: badge-in 360ms var(--e-out) 400ms both; }
@keyframes badge-in { from { opacity: 0; transform: translateY(4px); } }

/* ---------- The ticker: items fade at the edges instead of being cut ---------- */
.ticker-viewport {
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 28px, #000 calc(100% - 56px), transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0, #000 28px, #000 calc(100% - 56px), transparent 100%);
}

/* ---------- Live figures: a cell that changes glows amber for a moment ---------- */
.motion .tick-flash { animation: tick-flash 1.4s ease-out; }
@keyframes tick-flash { 0%, 20% { background-color: color-mix(in srgb, var(--amber) 45%, transparent); } }

/* ---------- Pictures arrive instead of popping in ---------- */
.motion img.img-wait { opacity: 0; }
.motion img.img-in { opacity: 1; transition: opacity 420ms ease-out; }

/* ---------- Articles ---------- */
/* A heading reached from the contents, or from a link, is marked for a moment so the eye finds it. */
.motion .prose :is(h2, h3):target, .motion .prose :is(h2, h3).is-target { animation: target-mark 1.8s var(--e-out); }
@keyframes target-mark { 0%, 35% { box-shadow: inset 6px 0 0 var(--amber); padding-left: 14px; } }
.motion .prose :is(h2, h3) { transition: padding-left var(--t-slow) var(--e-out); }
/* The reading line moves smoothly. */
.read-line i { transition: transform 120ms linear; }
/* Save: the bookmark pops when an article is saved. */
.motion .save-btn[aria-pressed="true"] .save-icon { animation: pop 360ms var(--e-out); }
@keyframes pop { 40% { transform: scale(1.3); } }
/* Copy link: the words change in place, with a tick, then go back. */
.copy-done { position: relative; }
.copy-done::before { content: "✓ "; color: var(--up); }
.motion .copy-done { animation: copy-in 300ms var(--e-out); }
@keyframes copy-in { from { opacity: 0.2; transform: translateY(3px); } }

/* ---------- Forms: fields answer the focus, and say clearly what is missing ---------- */
.motion :is(input, textarea, select) { transition: border-color var(--t-fast) ease-out, box-shadow var(--t) var(--e-out), background-color var(--t-fast) ease-out, outline-offset var(--t-fast) var(--e-out); }
.contact .field input:focus, .contact .field textarea:focus { box-shadow: 0 0 0 1px var(--spot), inset 0 -3px 0 var(--spot); }
:is(.contact .field input, .contact .field textarea):user-invalid { border-color: var(--down); }
.motion :is(.contact .field input, .contact .field textarea):user-invalid { animation: nudge 300ms var(--e-out); }
@keyframes nudge { 25% { transform: translateX(-3px); } 60% { transform: translateX(2px); } }
.motion .contact-about .chip { transition: background-color var(--t-fast) ease-out, color var(--t-fast) ease-out, transform 90ms ease-out; }
.motion .contact-about .chip:active { transform: translateY(1px); }
/* A row of options takes the page's width, not the width of the fields under it: it wraps only when the page is
   too narrow for one line. The fields and the rest keep their 44em measure. */
.contact { max-width: none; }
.contact > :not(.contact-form), .contact-form > :not(.contact-about) { max-width: 44em; }

/* ---------- The calendar ---------- */
.motion .cal-page .c-chip { transition: border-color var(--t-fast) ease-out, background-color var(--t-fast) ease-out, color var(--t-fast) ease-out, transform 90ms ease-out; }
.motion .cal-page .c-chip::before { transition: background-color var(--t-fast) ease-out, box-shadow var(--t-fast) ease-out; }
.motion .cal-page .c-chip[aria-pressed="true"]::before { animation: tick-box 260ms var(--e-out); }
@keyframes tick-box { 40% { transform: scale(1.35); } }
.motion .cal-page :is(.c-cell, .c-arow, .c-ev, .c-tick) { transition: background-color var(--t-fast) ease-out, box-shadow var(--t-fast) ease-out; }

/* ---------- Pages: the bar, the nameplate and the section row stay; the page under them slides up ----------
   Going back slides the other way. A story's headline and picture travel from the front page into the article. */
::view-transition-old(root) { animation: 150ms ease-in both page-out; }
::view-transition-new(root) { animation: 340ms var(--e-out) 40ms both page-in; }
html:active-view-transition-type(back)::view-transition-old(root) { animation-name: page-out-back; }
html:active-view-transition-type(back)::view-transition-new(root) { animation-name: page-in-back; }
@keyframes page-out { to { opacity: 0; transform: translateY(-10px); } }
@keyframes page-in { from { opacity: 0; transform: translateY(18px); } }
@keyframes page-out-back { to { opacity: 0; transform: translateY(10px); } }
@keyframes page-in-back { from { opacity: 0; transform: translateY(-18px); } }

::view-transition-group(nameplate), ::view-transition-group(sections), ::view-transition-group(ticker),
::view-transition-group(story-title), ::view-transition-group(story-image) {
  animation-duration: 460ms; animation-timing-function: var(--e-out);
}
::view-transition-old(nameplate), ::view-transition-new(nameplate),
::view-transition-old(story-title), ::view-transition-new(story-title) { height: 100%; width: auto; object-fit: contain; }
::view-transition-old(sections), ::view-transition-new(sections),
::view-transition-old(ticker), ::view-transition-new(ticker) { animation: none; mix-blend-mode: normal; }
::view-transition-old(story-title) { animation: 200ms ease-in both vt-fade-out; }
::view-transition-new(story-title) { animation: 300ms ease-out 120ms both vt-fade-in; }
::view-transition-old(story-image) { animation: 260ms ease-in both vt-fade-out; }
::view-transition-new(story-image) { animation: 300ms ease-out both vt-fade-in; }
@keyframes vt-fade-out { to { opacity: 0; } }
@keyframes vt-fade-in { from { opacity: 0; } }

/* The theme: the new paper (or terminal) is printed down the page from the top bar. */
html:active-view-transition-type(theme)::view-transition-old(root) { animation: none; }
html:active-view-transition-type(theme)::view-transition-new(root) { animation: theme-print 560ms var(--e-in-out) both; }
@keyframes theme-print { from { clip-path: inset(0 0 100% 0); } to { clip-path: inset(0 0 0 0); } }
html.vt-theme * { view-transition-name: none !important; }
html.vt-theme, html.vt-theme body { transition: none !important; }

/* ---------- In-page jumps glide ---------- */
@media (prefers-reduced-motion: no-preference) { html.motion { scroll-behavior: smooth; } }

/* ---------- Less motion: every state at once ---------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0s !important; animation-delay: 0s !important; transition-duration: 0s !important; }
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
}

/* The panel text under a heading: no single word left on the last line. */
.mail-sub { text-wrap: pretty; }
