/* ===== Site cursor (liquid-glass pointer), used on every page =====
   Replaces the system pointer with a small disc of grey glass in the nav's
   family (the same warm grey, saturate and rim light) but much lighter than the
   nav: about #d7d7d7 on white (zaidsyed.com's is #e8e8e8; this is a notch
   darker), a lighter dot, no outline.
   cursor.js builds .site-cursor and toggles these modifier classes on it:
     (none)  a 44px disc with a small, soft dot in the middle
     --link  over anything clickable the whole cursor draws in a little, to
             focus on it, and turns more transparent so the thing under it
             stays readable, while the dot grows
     --text  over a text field, or close to selectable text (never a link or the
             nav) the disc becomes a tall, narrow frosted capsule (7x36), the same
             size over every size of type (iPadOS's I-beam pointer). A style
             change only: the cursor always stays under the pointer
     --down  while the button is held it scales to 0.9 of whatever it was
             (mikematas.com's cursor does the same) and springs back
     --on    shown (after the first real mouse move; hidden again when the pointer
             leaves the window or goes into an embedded page)
   Class names are all prefixed on purpose: the BNY demo pages draw their own
   scripted pointer and already use .cursor.
   Springs are CSS linear() curves generated from Apple-style response /
   damping pairs (morph .34s / .9, dot .30s / .82, release .26s / .64, hover
   .51s / .8 -- the last is mikematas.com's hover spring); the
   cubic-beziers are the fallback where linear() is not supported.
   Fine pointers that can hover only (mouse, trackpad): touch keeps its own
   behaviour, and so does forced-colors mode. The glass fades in and out on
   itself, never on .site-cursor: an ancestor with opacity < 1 would be the
   backdrop root and flatten the blur mid-fade. */
:root{
  --cur-morph:   cubic-bezier(.22,1,.36,1);
  --cur-dot:     cubic-bezier(.34,1.3,.64,1);
  --cur-release: cubic-bezier(.34,1.56,.64,1);
  --cur-press:   cubic-bezier(.2,.9,.3,1);
  --cur-hover:   cubic-bezier(.22,1,.36,1);
}
@supports (transition-timing-function:linear(0, 1)){
  :root{
    --cur-morph:   linear(0, 0.03, 0.103, 0.198, 0.301, 0.404, 0.501, 0.589, 0.667, 0.733, 0.789, 0.836, 0.874, 0.905, 0.929, 0.948, 0.963, 0.974, 0.982, 0.988, 0.993, 0.996, 0.998, 1, 1.001, 1.001, 1.001, 1.002, 1);
    --cur-dot:     linear(0, 0.038, 0.128, 0.244, 0.368, 0.488, 0.597, 0.692, 0.772, 0.837, 0.888, 0.927, 0.956, 0.977, 0.992, 1.001, 1.007, 1.01, 1.011, 1.011, 1.01, 1.009, 1.008, 1.006, 1.005, 1.004, 1.003, 1.002, 1);
    --cur-release: linear(0, 0.056, 0.188, 0.355, 0.527, 0.684, 0.816, 0.917, 0.99, 1.037, 1.063, 1.072, 1.071, 1.063, 1.051, 1.039, 1.027, 1.016, 1.008, 1.002, 0.998, 0.996, 0.995, 0.995, 0.995, 0.996, 0.997, 0.998, 1);
    --cur-hover:   linear(0, 0.037, 0.125, 0.241, 0.365, 0.486, 0.597, 0.694, 0.776, 0.842, 0.895, 0.935, 0.964, 0.985, 0.999, 1.008, 1.013, 1.016, 1.016, 1.015, 1.014, 1.012, 1.01, 1.008, 1.006, 1.005, 1.003, 1.002, 1);
  }
}

@media (hover:hover) and (pointer:fine){
  /* [data-cursor="native"] marks a live prototype: inside it the system pointer comes back (with the prototype's own
     pointer / text / grab cursors) and this cursor steps aside, as it does over an embedded page */
  html.has-site-cursor:not(.cursor-grab),
  html.has-site-cursor:not(.cursor-grab) *:not([data-cursor="native"], [data-cursor="native"] *){ cursor:none !important; }
  /* .cursor-grab is on <html> while the pointer is over something you drag (the About
     sphere, the mind map canvas, the loose tiles and chips -- a page names its own with
     data-grab on the cursor.js tag, and data-cursor="grab" works anywhere). Dropping the
     rule above is the whole trick: with nothing forcing "none", the surface's own
     cursor:grab / :grabbing comes through, so the hand says "you can move this". */
  /* cursor is inherited: without this the prototype would inherit "none" from the page around it, and the pointer
     would vanish everywhere inside except on elements that set their own cursor (it flickered in and out) */
  html.has-site-cursor [data-cursor="native"]{ cursor:auto !important; }
}

.site-cursor{
  /* 2026-11-05: every size below is the pre-November set scaled by 0.89 -- first
     0.78 ("20-25% smaller"), then back up to halfway between that and the old size.
     The disc, the dot, the text capsule and the project pill move together, so the
     states stay in proportion; only these numbers change, never --rest. */
  --d:39px;                 /* the glass disc (was 44, then 34) */
  --dot:9px;                /* the dot at rest (was 10, then 8) */
  --dot-link:27px;          /* the dot once it has grown over something clickable (was 30, then 23) */
  --bar:6.2px; --line:32px; /* the text capsule: one fixed size over every size of type (was 7 / 36) */
  --w:var(--d); --h:var(--d); --dot-d:var(--dot); --rest:.88; --hover:var(--rest); --press:1;   /* at rest it sits at 0.88 (a ~34px disc); a link draws it in by the same .85 from there */
  position:fixed; left:0; top:0; z-index:2147483647;
  width:0; height:0; pointer-events:none;
  will-change:transform;
}
/* over something clickable the whole cursor draws in a little (to focus on it) while the dot grows */
.site-cursor.site-cursor--link{ --dot-d:var(--dot-link); --hover:calc(var(--rest) * .85); --scale-dur:490ms; --scale-ease:var(--cur-hover); }
.site-cursor.site-cursor--text{ --w:var(--bar); --h:var(--line); --dot-d:0px; --hover:1; }
/* over a case study the disc grows into a pill of the nav's own glass that reads "View project" */
/* 2026-11-08: the pill is as tall as the disc's full size (--d, 39px): the disc, which
   rests at 0.88 (~34px), relaxes to 1 and opens sideways, so opening always reads as
   growing (at belleduffner.com's 29px it shrank). Fully round, label 15px / 400,
   icon 17px, 8px between them, 16px of room either side of the label. */
.site-cursor.site-cursor--project{ --w:var(--pw, 150px); --h:var(--d); --dot-d:0px; --hover:1; --morph-dur:300ms; --scale-dur:300ms; --scale-ease:var(--cur-morph); }   /* opens in 300ms (200 was too quick, the first 400 too slow) */
/* data-cursor="snap" (after zaidsyed.com's "View" button): the disc locks to the element and opens to its hovered size (--snap-w/-h from cursor.js, x --snap-grow, the
   button's own hover scale) while its tint, blur and rim dissolve; the dot grows from its small circle to fill the same shape on the same spring, then fades. */
.site-cursor.site-cursor--snap{ --snap-grow:1.05; --w:calc(var(--snap-w, 80px) * var(--snap-grow)); --h:calc(var(--snap-h, 36px) * var(--snap-grow)); --hover:1; --morph-dur:300ms; --scale-dur:300ms; --scale-ease:var(--cur-morph); }
/* a press is 0.9x of whatever size it already is (hovering or not), quick in, a springy release */
.site-cursor.site-cursor--down{ --press:.9; --scale-dur:110ms; --scale-ease:var(--cur-press); }

.site-cursor__glass{
  position:absolute; left:0; top:0;
  width:var(--w); height:var(--h);
  translate:-50% -50%;       /* centred on the pointer; its own property, so the press scale below never fights it */
  scale:calc(var(--hover) * var(--press));
  border-radius:999px;
  /* Mostly clear. zaidsyed's disc is rgba(23,23,23,.1), which is #e8e8e8 on white; this is the nav's warm grey at about
     twice that strength, so it lands on #d7d7d7 (a notch darker on purpose), with a soft sheen across the top left so
     it reads as glass and not a flat chip. No outline: the edge is light catching the rim (the two inset glows), and the shadow
     is only there to lift it off the page. Five shadow layers in every state, so they can animate into each other. */
  background:
    radial-gradient(120% 120% at 30% 15%, rgba(255,255,255,0.22) 0%, rgba(255,255,255,0) 60%),
    rgba(66,64,62,0.21);
  -webkit-backdrop-filter:blur(10px) saturate(180%);
  backdrop-filter:blur(10px) saturate(180%);
  box-shadow:
    inset 1px 1.5px 2px -0.5px rgba(255,255,255,0.7),
    inset -1px -1.5px 2px -0.5px rgba(255,255,255,0.35),
    inset 0 0 0 0.5px rgba(0,0,0,0.04),
    0 1px 2px rgba(0,0,0,0.04),
    0 3px 10px -4px rgba(0,0,0,0.06);
  opacity:0;
  transition:
    width var(--morph-dur, 400ms) var(--cur-morph),
    height var(--morph-dur, 400ms) var(--cur-morph),
    scale var(--scale-dur, 330ms) var(--scale-ease, var(--cur-release)),
    -webkit-backdrop-filter 240ms ease-out,
    backdrop-filter 240ms ease-out,
    background-color 240ms ease-out,
    box-shadow 240ms ease-out,
    opacity 180ms ease-out;
}
.site-cursor.site-cursor--on .site-cursor__glass{ opacity:1; }
/* Over something clickable the glass is more transparent (and a little less blurred), so the label or link under it
   stays readable while the cursor draws in around it; the dot is at full strength to make up for the fade. */
.site-cursor.site-cursor--on.site-cursor--link .site-cursor__glass{
  opacity:0.55;
  -webkit-backdrop-filter:blur(6px) saturate(180%);
  backdrop-filter:blur(6px) saturate(180%);
}
/* Over text the capsule is a light frost, not a dark tint. A dark tint over black type just turns the strokes into a
   smudge; a pale, fairly opaque grey lifts whatever is under it (black type reads mid grey) and over a white page it
   lands on about the same grey as the disc. The glass is also thinner here (the line stays readable through it) and
   the hairline is firmer, so it is a clear shape against type. */
.site-cursor.site-cursor--text .site-cursor__glass{
  -webkit-backdrop-filter:blur(4px) saturate(170%);
  backdrop-filter:blur(4px) saturate(170%);
  background-color:rgba(188,188,188,0.55);
  box-shadow:
    inset 1px 1px 1.5px -0.5px rgba(255,255,255,0.95),
    inset -1px -1px 1.5px -0.5px rgba(255,255,255,0.6),
    inset 0 0 0 0.5px rgba(0,0,0,0.13),
    0 1px 2px rgba(0,0,0,0.07),
    0 3px 8px -3px rgba(0,0,0,0.12);
}
/* A glass bead set into the disc. Its body fades toward its own edge, so it melts into the disc instead of sitting on
   it like a sticker, with a little light across the top left. Its strength is the element's opacity, so a state can
   change it without the gradients having to animate (gradients cannot transition). */
.site-cursor__dot{
  position:absolute; left:50%; top:50%;
  width:var(--dot-d); height:var(--dot-d);
  translate:-50% -50%;
  border-radius:999px;
  background:
    radial-gradient(circle at 34% 26%, rgba(255,255,255,0.45) 0%, rgba(255,255,255,0) 52%),
    radial-gradient(closest-side, rgba(255,255,255,0.62) 0%, rgba(255,255,255,0.5) 58%, rgba(255,255,255,0.14) 100%);
  box-shadow:inset 0 1px 2px rgba(255,255,255,0.35);
  opacity:0.34;
  transition:
    width 295ms var(--cur-dot),
    height 295ms var(--cur-dot),
    opacity 180ms ease-out;
}
.site-cursor.site-cursor--link .site-cursor__dot{ opacity:1; }
.site-cursor.site-cursor--text .site-cursor__dot{ opacity:0; }
.site-cursor.site-cursor--snap .site-cursor__dot{
  width:var(--w); height:var(--h); opacity:.15;
  transition:width var(--morph-dur) var(--cur-morph), height var(--morph-dur) var(--cur-morph), opacity 260ms ease-out 90ms;   /* it grows first, then fades */
}
.site-cursor.site-cursor--on.site-cursor--snap .site-cursor__glass{
  background:rgba(66,64,62,0.06);   /* not quite gone: a trace of the tint stays, on light buttons and dark */
  -webkit-backdrop-filter:blur(0px) saturate(100%);
  backdrop-filter:blur(0px) saturate(100%);
  box-shadow:inset 0 0 0 0 rgba(0,0,0,0), inset 0 0 0 0 rgba(0,0,0,0), inset 0 0 0 0 rgba(0,0,0,0), 0 0 0 0 rgba(0,0,0,0), 0 0 0 0 rgba(0,0,0,0);
  transition:
    width var(--morph-dur) var(--cur-morph),
    height var(--morph-dur) var(--cur-morph),
    scale var(--scale-dur) var(--scale-ease),
    -webkit-backdrop-filter 300ms var(--cur-morph),
    backdrop-filter 300ms var(--cur-morph),
    background-color 300ms var(--cur-morph),
    box-shadow 300ms var(--cur-morph),
    opacity 180ms ease-out;
}
.site-cursor.site-cursor--project .site-cursor__dot{ opacity:0; }

/* "View project": the nav and the contact popup's glass (the warm grey at half strength, 28px blur, saturate 180%, a
   bright top rim) under white Inter 500, the nav's type. The words fade in a beat after the pill starts to open, and
   leave at once, so they never sit outside a pill too small for them. */
.site-cursor.site-cursor--on.site-cursor--project .site-cursor__glass{
  background:rgba(74,71,68,0.5);
  -webkit-backdrop-filter:blur(28px) saturate(180%);
  backdrop-filter:blur(28px) saturate(180%);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.3),
    inset 0 0 0 0.5px rgba(255,255,255,0.08),
    inset 0 0 0 0 rgba(0,0,0,0),
    0 5px 8px rgba(0,0,0,0.12),
    0 14px 20px -6px rgba(0,0,0,0.12);
}
.site-cursor__label{
  position:absolute; left:50%; top:50%; translate:-50% -50%;
  display:inline-flex; align-items:center; gap:8px;
  white-space:nowrap; color:#fff;
  font-family:'Inter','Helvetica Neue',Helvetica,Arial,sans-serif;
  font-size:15px; font-weight:400; letter-spacing:normal; line-height:1;
  opacity:0; transition:opacity 60ms ease-out;
  pointer-events:none;
}
.site-cursor__label svg{ flex:none; width:17px; height:17px; }
.site-cursor.site-cursor--project .site-cursor__label{ opacity:1; transition:opacity 160ms ease-out 90ms; }

@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .site-cursor__glass{ background:rgba(74,71,68,0.6); }
}
@media (prefers-reduced-transparency:reduce){
  .site-cursor__glass{ background:rgba(96,93,90,0.9); -webkit-backdrop-filter:none; backdrop-filter:none; }
}
@media (prefers-reduced-motion:reduce){
  .site-cursor__glass{ transition:opacity 120ms linear; }
  .site-cursor__dot{ transition:opacity 120ms linear; }
  .site-cursor__label{ transition:none !important; }
  .site-cursor.site-cursor--snap .site-cursor__dot, .site-cursor.site-cursor--on.site-cursor--snap .site-cursor__glass{ transition:opacity 120ms linear; }
}
@media print{ .site-cursor{ display:none; } }
