/* ============================================================================
   components.css — the interaction layer. Loaded AFTER site.css.
   ----------------------------------------------------------------------------
   Added 1 Oct 2026 (P4 of the restructure). site.css says what things ARE; this
   says how they respond. Everything here is state change — a thing opening, a
   thing selected, a thing pressed. Nothing animates on its own and nothing moves
   on scroll: the brand promise is "leaves you alone", and four outside reviewers
   on 1 Oct 2026 independently warned that ambient motion would make it ironic.

   RULES THAT BIND EVERY BLOCK BELOW
     · Tokens, not hex. Sage and ink on paper; gold only as a fill or a hairline
       on dark, never as type.
     · Motion lives inside `prefers-reduced-motion:no-preference`, so reduced
       motion is honoured by DOING NOTHING, not by animating faster. The end
       STATES still apply without it: the burger still becomes an X.
     · No <style>, no style="" — the CSP drops both silently. Anything indexed
       is done with :nth-child.
     · The global :focus-visible ring is never removed.
     · `.ux-` prefix on any class this file introduces.

   THIRD-PARTY CODE, AND WHERE IT IS RECORDED. Two blocks below are adapted from
   Uiverse.io components, published under the MIT License. They are marked
   "ADAPTED FROM", name the author and the page, and are logged with the licence
   text in THIRD-PARTY.md at the repository root. The attribution lives in
   comments, not in an <a href>: tools/verify_site.py's allowlist refuses a link
   to uiverse.io, and rightly. The sheen is original and says so.
   ========================================================================== */

:root{
  /* The two curves the adapted components shipped with, named once. */
  --ux-ease:cubic-bezier(.4,0,.2,1);
  --ux-ease-out:cubic-bezier(.16,1,.3,1);
}

/* The sticky masthead is 73px. Without this an anchor lands UNDER it — /#keep did
   exactly that — and every shelf chip now links to an anchor. */
html{scroll-padding-top:92px}


/* ── 1. the burger becomes an X ──────────────────────────────────────────────
   ADAPTED FROM "Checkbox" by JulanDeAlb, https://uiverse.io/JulanDeAlb/tall-swan-6
   (MIT, retrieved 1 Oct 2026): the two-path SVG, its stroke-dasharray values
   (12 63 closed, 20 300 with a -32.42 offset open) and the -45° turn.

   WHAT CHANGED, AND WHY. The original is a hidden checkbox, `display:none`,
   which takes the control out of the tab order and out of the accessibility
   tree. Here the same morph is driven by `.nav-fold[open]` on the <details> that
   was already there — still a real <summary>, so Enter and Space work, the
   expanded state is announced, and it survives scripts being blocked. Stroke is
   `currentColor` (the summary's own ink), not the original's white. */
.nav-fold summary svg{
  width:20px;height:20px;
  stroke-width:3;stroke-linejoin:round;
}
.ux-burger-tb{stroke-dasharray:12 63}
.nav-fold[open] summary svg{transform:rotate(-45deg)}
.nav-fold[open] .ux-burger-tb{stroke-dasharray:20 300;stroke-dashoffset:-32.42}
@media(prefers-reduced-motion:no-preference){
  .nav-fold summary svg{transition:transform 450ms var(--ux-ease)}
  .nav-fold summary svg path{
    transition:stroke-dasharray 450ms var(--ux-ease),stroke-dashoffset 450ms var(--ux-ease);
  }
}


/* ── 2. the tick on a selected chip ──────────────────────────────────────────
   ADAPTED FROM "Checkbox" by 00Kubi, https://uiverse.io/00Kubi/hot-dragonfly-56
   (MIT, retrieved 1 Oct 2026): the check path (M3,12.5l7,7L21,5), drawn by
   stroke-dashoffset from 40 to 0, with that component's easing.

   WHAT WAS LEFT OUT, ON PURPOSE. The original is a neon (#00ffaa) box that throws
   twelve particles, three rings and four sparks when ticked. None of that is here:
   it is decoration on a control, it clashes with the flair budget (a device may
   frame an INSTRUMENT, never decorate), and a site that promises to leave you
   alone should not fireworks a filter chip. The tick is the whole idea.

   The SVG is built by shelf-filter.js into each chip, so with scripts off there
   is no tick and no filter, and the chips are plain links to each shelf. */
.topic .ux-tick{
  flex:none;width:1em;height:1em;margin-right:.45em;vertical-align:-.15em;
  fill:none;stroke:currentColor;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;
  stroke-dasharray:40;stroke-dashoffset:40;
}
.topic[aria-pressed="true"] .ux-tick{stroke-dashoffset:0}
@media(prefers-reduced-motion:no-preference){
  .topic .ux-tick{transition:stroke-dashoffset 400ms var(--ux-ease-out)}
}
/* A script hides a section with `hidden`; any rule that sets `display` on it
   needs this escape (README, "`.dm-note[hidden]`"). */
[data-shelf][hidden]{display:none}


/* ── 3. a sheen across the filled button ─────────────────────────────────────
   ORIGINAL. A highlight crosses the primary button once on hover or keyboard
   focus. (The Uiverse "shine" button on the profile this was meant to come from
   turned out to be a shake animation, which is the opposite of the brand, so it
   was not ported.)

   Hover-capable devices only: a sheen that plays on a thumb-tap is noise. It
   resets instantly rather than animating back, so there is one sweep, not a
   to-and-fro. Only the filled `.btn`; the ghost and quiet buttons stay still. */
.btn{position:relative;overflow:hidden}
.btn::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(100deg,transparent 35%,rgba(240,239,236,.30) 50%,transparent 65%);
  transform:translateX(-130%);
}
@media (hover:hover) and (prefers-reduced-motion:no-preference){
  .btn:hover::after,.btn:focus-visible::after{
    transform:translateX(130%);
    transition:transform 700ms var(--ux-ease);
  }
}


/* ── 4. a directory row answers the pointer ──────────────────────────────────
   The hairline under a row darkens on hover and on keyboard focus anywhere inside
   it. No lift, no shadow: a row is a hairline, not a box ("no boxed cards"), and
   moving a row would move the rules above and below it. */
@media (hover:hover){
  .row-dir:hover{border-bottom-color:var(--ink-muted)}
}
.row-dir:focus-within{border-bottom-color:var(--ink-muted)}
@media(prefers-reduced-motion:no-preference){
  .row-dir{transition:border-bottom-color .2s ease}
}


/* ── 5. the planner covers scroll sideways on a phone ────────────────────────
   Two columns of six covers was ~1,100px of a ~7,000px mobile page. One row that
   scrolls, snapping to a cover, is a third of that and shows by its cut-off edge
   that there is more. `proximity`, not `mandatory`: mandatory snapping fights a
   thumb that wants to stop between two. Above 34rem the grid is unchanged. */
@media(max-width:33.99rem){
  .shelf{
    display:flex;overflow-x:auto;gap:14px;padding-bottom:12px;
    scroll-snap-type:x proximity;overscroll-behavior-x:contain;
    /* The native bar is a white slab on the dark band. A thin gold hairline is the
       one allowed use of gold on dark (a fill, never type) and is still a visible
       scrollbar for a mouse, which a hidden one would not be. */
    scrollbar-width:thin;scrollbar-color:rgba(201,168,76,.55) transparent;
  }
  .shelf .cover{flex:0 0 44%;min-width:0;scroll-snap-align:start}
}


/* ── 6. an email field that says it is wrong ─────────────────────────────────
   :user-invalid, not :invalid — it waits until the person has actually typed and
   left the field, so the box is not red at them on arrival. No new hue: ink, the
   darker border the focus state already uses, and a heavier underline. */
.field:user-invalid{border-color:var(--ink);box-shadow:inset 0 -2px 0 var(--ink)}


/* ── 7. a thumb-sized target on a touch screen ───────────────────────────────
   WCAG 2.2's floor is 24px and the chips were 29, so they passed — and were still
   the smallest thing on the page that a thumb has to hit, in a row of five that
   wraps. 44px is the size a thumb actually wants, applied only where the pointer
   is coarse so the desktop page does not grow. The secondary store links are 12px
   mono text; padding, not a bigger font, makes them reachable. */
@media (pointer:coarse){
  .topic-row{gap:10px}
  .topic{display:inline-flex;align-items:center;min-height:44px;padding-inline:16px}
  .row-also{display:inline-block;padding-block:12px}
  .btn,.btn-ghost{min-height:44px;display:inline-flex;align-items:center;justify-content:center}
}


/* ── 8. the parts of the browser nobody drew ─────────────────────────────────
   Text selection and the caret ship with browser defaults that belong to no design
   system, and they are the cheapest signal that a page was assembled, not built.
   Selection takes the site's own ink on paper (gold on the dark band, a fill, never
   type); the caret takes sage, the one colour the site already uses for "this is
   interactive". */
::selection{background:var(--ink);color:var(--paper)}
.block ::selection{background:var(--gold);color:var(--ink)}
input,textarea{caret-color:var(--sage)}


/* ── 9. a panel that is a link ───────────────────────────────────────────────
   ADAPTED FROM "NEXT" by mobinkakei, https://uiverse.io/mobinkakei/cold-bat-89
   (MIT, retrieved 1 Oct 2026): the three chevron outlines (go-link.njk) and the
   idea that they start bunched at the left (-60%, -30%, 0) and spread to rest on
   hover.

   WHAT WAS LEFT OUT. The original is a skewed purple box with a hard 6px offset
   shadow that turns yellow, and a colour animation that loops forever on hover. The
   craft floor names the hard offset shadow as a costume, the loop is ambient motion,
   and none of it is this site's palette. Only the sliding chevrons are kept.

   The anchor draws nothing itself. Its ::after stretches over the whole panel (the
   same technique as `.cover-buy`), so the panel is the target and the pill in the
   corner is what says so. The pill sits where the panel's state label ("SAVED",
   "TODAY") would, so the title bar reserves room for it. */
.trio-item{position:relative;isolation:isolate}
.trio-item .dm-head{padding-right:62px}
.ux-go{position:static;text-decoration:none}
/* ⚠️ z-index:2 IS LOAD-BEARING, AND I SHIPPED WITHOUT IT. The demo paints its content
   at z-index:1 (its text, bars and hint sit above the scanline layer), and a stretched
   link at the default level sits BELOW that. A click then lands on the demo's own text,
   not the link. It still worked for a mouse, by accident: hovering lifts the demo with a
   transform, which makes it its own stacking context and puts the whole panel back
   underneath the overlay. With no hover (a touch screen, or any test with the pointer
   away) most of each panel was not a link. `isolation` on the item keeps this level
   from leaking into the rest of the page. */
.ux-go::after{content:"";position:absolute;inset:0;z-index:2;border-radius:12px;cursor:pointer}
.ux-go:focus-visible{outline:none}
.ux-go:focus-visible::after{outline:2px solid var(--sage);outline-offset:3px}
.ux-go-ico{
  position:absolute;top:9px;right:10px;z-index:1;pointer-events:none;
  display:grid;place-items:center;width:40px;height:24px;overflow:hidden;
  border:1px solid rgba(255,255,255,.22);border-radius:999px;
  background:rgba(10,10,10,.55);color:var(--on-dark-mid);
}
.ux-go-ico svg{width:24px;height:12px;fill:currentColor;overflow:visible}
.ux-chev-1{transform:translateX(-60%)}
.ux-chev-2{transform:translateX(-30%)}
.trio-item:hover .ux-go-ico,.trio-item:focus-within .ux-go-ico{color:var(--on-dark);border-color:var(--dm-accent,#7a9e7e)}
.trio-item:hover .ux-chev-1,.trio-item:hover .ux-chev-2,
.trio-item:focus-within .ux-chev-1,.trio-item:focus-within .ux-chev-2{transform:none}
@media(prefers-reduced-motion:no-preference){
  .ux-go-ico{transition:color .25s ease,border-color .25s ease}
  .ux-chev-1{transition:transform .4s var(--ux-ease)}
  .ux-chev-2{transition:transform .5s var(--ux-ease)}
  .trio-item .demo{transition:transform .35s var(--ux-ease)}
}
/* A lift of 3px, on hover-capable devices only, says "this is clickable" without
   moving the page's layout: a transform does not reflow. */
@media (hover:hover){
  .trio-item:hover .demo{transform:translateY(-3px)}
}
