/* ==========================================================================
   FANGIN — Custom cursor system
   Desktop pointer devices only (see assets/js/cursor.js — the script
   itself refuses to run at all on touch/tablet, checking `(pointer: fine)`
   and `(hover: hover)` before it ever creates the DOM this file styles).
   That means every rule below only ever matters once
   `html.has-fangin-cursor` is present, which cursor.js is the only thing
   that adds — on a touch device this whole file is dead weight the
   browser parses once and never matches, the native cursor and default
   `:hover` styling behave exactly as they would if this file didn't exist.

   One cursor, several states, never a different mechanism per state —
   two concentric children (the small "F." mark, and a circle+label)
   that each state just resizes/recolours/fades, so morphing between
   states is a size and colour transition, never a swap-out. See
   cursor.js for what triggers each `data-state` value.
   ========================================================================== */

/* ---- Native cursor handoff ----
   Hidden everywhere except genuine text-entry contexts, where the native
   caret/I-beam is what people actually need (see FORM_SELECTOR in
   cursor.js, which also fades the custom cursor out — via .is-form below —
   whenever the pointer is over one of these, so the native cursor isn't
   competing with a redundant custom one sitting on top of it). `!important`
   is deliberate here: this only ever needs to beat other `cursor:` values
   (e.g. .ipl-hotspot's own `cursor: pointer`), never anything with a
   legitimate reason to win a specificity fight. */
html.has-fangin-cursor,
html.has-fangin-cursor * {
  cursor: none !important;
}

html.has-fangin-cursor input,
html.has-fangin-cursor textarea,
html.has-fangin-cursor select,
html.has-fangin-cursor [contenteditable],
html.has-fangin-cursor [contenteditable] * {
  cursor: auto !important;
}

/* ---- Root ----
   Fixed at the viewport origin; every frame, cursor.js writes a single
   translate3d(...) here and every child centres itself on that point via
   its own translate(-50%,-50%) — one position write per frame instead of
   three. `pointer-events: none` all the way down means this never once
   steals a click, a hover, a drag, a text selection or a form focus from
   the real page underneath it; cursor.js's own context detection relies
   on that same property to let elementFromPoint() see straight through
   this to whatever's actually there.
   z-index is a deliberately absurd value — this must always sit above
   literally everything else on the page (header included, at z-index:100),
   and picking something this far above any real stacking context on this
   site removes any need to keep it in sync as new sections get added. */
.fangin-cursor {
  position: fixed;
  top: 0;
  left: 0;
  width: 0;
  height: 0;
  pointer-events: none;
  z-index: 2147483000;
  opacity: 0;
  transition: opacity 0.2s ease;
}

.fangin-cursor.is-active {
  opacity: 1;
}

/* Pointer is over a genuine text-entry field — defer to the native caret
   entirely rather than layering a redundant custom cursor on top of it. */
.fangin-cursor.is-form {
  opacity: 0;
}

.fangin-cursor-mark,
.fangin-cursor-circle {
  position: absolute;
  top: 0;
  left: 0;
  transform: translate(-50%, -50%);
  transition:
    width 0.22s var(--ease-editorial),
    height 0.22s var(--ease-editorial),
    font-size 0.22s var(--ease-editorial),
    transform 0.15s var(--ease-editorial),
    opacity 0.22s var(--ease-editorial),
    color 0.22s var(--ease-editorial),
    background-color 0.22s var(--ease-editorial);
}

.fangin-cursor-circle {
  border-radius: 50%;
}

/* ---- Default state: "F." — an initial, not a generic dot ----
   A plain dot+ring read as a filled blemish with a halo around it at this
   size ("looks like a pimple" — exactly right, first round of feedback).
   A lowercase "f." replaced it next, but at cursor size Anton's lowercase
   f is mostly a thin vertical stroke — easy to lose against busy content
   (second round of feedback: "i cant see the f") — so this is uppercase,
   matching how the header logo itself is always cased ("FANGIN.", never
   "fangin.", see .site-logo/[data-nav-logo] in index.html). Same face as
   that wordmark (--font-display/Anton). The period is always periwinkle
   regardless of light/dark zone (see .is-dark below) — the one colour
   accent that doesn't adapt — while the "F" itself is what swaps for
   contrast against light vs. dark backgrounds. */
.fangin-cursor-mark {
  display: flex;
  align-items: baseline;
  font-family: var(--font-display);
  text-transform: none;
  font-size: 17px;
  line-height: 1;
  color: var(--color-ink);
  opacity: 1;
}

.fangin-cursor-mark-dot {
  color: var(--color-periwinkle);
}

/* ---- Dark zone (hero video, Selected Work's media panel) ----
   Tagged via data-cursor-theme="dark" in index.html (see cursor.js's
   DARK_ZONE_SELECTOR) — swaps the "f" from ink-on-cream to cream-on-dark
   so the default cursor stays visible over the site's own dark/video
   imagery, exactly the light/dark behaviour the brief asks for. Only the
   default + link states need this swap; the four special circle states
   already carry their own deliberately strong-contrast colour treatment
   regardless of what's underneath them (see below). */
.fangin-cursor.is-dark .fangin-cursor-mark {
  color: var(--color-bone);
}

/* ---- Generic links / nav / standard buttons ----
   The mark simply scales up a touch — same restrained "something is
   interactive" signal as everywhere else on this site (compare the nav's
   own underline-sweep hover in header.css), no ring, no arrow, no text
   added, per the brief ("do not show unnecessary text inside the cursor
   for every normal link"). */
.fangin-cursor[data-state='link'] .fangin-cursor-mark {
  font-size: 22px;
}

/* ---- Pressed ----
   A small, quick press-down cue — scale doesn't need its own transition
   override since it inherits .fangin-cursor-mark's existing one above. */
.fangin-cursor.is-down .fangin-cursor-mark {
  transform: translate(-50%, -50%) scale(0.88);
}

/* ---- Special states: circle + label ----
   All four (IPL diagram, IPL hotspot, portfolio CTA, final CTA) share one
   mechanism — the circle grows from 0 to a fixed size and the label fades
   in inside it, while the "F." mark above fades out — so switching
   directly from one special state to another (e.g. crossing straight from
   the IPL diagram into a hotspot) is a colour/label cross-fade at a
   constant circle size, never a collapse-then-reopen. Sized to
   comfortably fit the longest label ("Let's go →") on two lines if needed
   without looking like a speech bubble — still reads as a circle, not a
   pill. */
.fangin-cursor[data-state='ipl'] .fangin-cursor-mark,
.fangin-cursor[data-state='hotspot'] .fangin-cursor-mark,
.fangin-cursor[data-state='cta-portfolio'] .fangin-cursor-mark,
.fangin-cursor[data-state='cta-final'] .fangin-cursor-mark,
.fangin-cursor[data-state='drag'] .fangin-cursor-mark,
.fangin-cursor[data-state='visit'] .fangin-cursor-mark,
.fangin-cursor[data-state='view'] .fangin-cursor-mark,
.fangin-cursor[data-state='grab'] .fangin-cursor-mark,
.fangin-cursor[data-state='hold'] .fangin-cursor-mark {
  opacity: 0;
}

.fangin-cursor-circle {
  width: 0;
  height: 0;
  opacity: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
}

.fangin-cursor[data-state='ipl'] .fangin-cursor-circle,
.fangin-cursor[data-state='hotspot'] .fangin-cursor-circle,
.fangin-cursor[data-state='cta-portfolio'] .fangin-cursor-circle,
.fangin-cursor[data-state='cta-final'] .fangin-cursor-circle,
.fangin-cursor[data-state='drag'] .fangin-cursor-circle,
.fangin-cursor[data-state='visit'] .fangin-cursor-circle,
.fangin-cursor[data-state='view'] .fangin-cursor-circle,
.fangin-cursor[data-state='grab'] .fangin-cursor-circle,
.fangin-cursor[data-state='hold'] .fangin-cursor-circle {
  width: 104px;
  height: 104px;
  opacity: 1;
}

.fangin-cursor-label {
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-align: center;
  line-height: 1.3;
  max-width: 74px;
  opacity: 0;
  transition: opacity 0.22s var(--ease-editorial);
}

.fangin-cursor[data-state='ipl'] .fangin-cursor-label,
.fangin-cursor[data-state='hotspot'] .fangin-cursor-label,
.fangin-cursor[data-state='cta-portfolio'] .fangin-cursor-label,
.fangin-cursor[data-state='cta-final'] .fangin-cursor-label,
.fangin-cursor[data-state='drag'] .fangin-cursor-label,
.fangin-cursor[data-state='visit'] .fangin-cursor-label,
.fangin-cursor[data-state='view'] .fangin-cursor-label,
.fangin-cursor[data-state='grab'] .fangin-cursor-label,
.fangin-cursor[data-state='hold'] .fangin-cursor-label {
  opacity: 1;
}

/* IPL diagram — "Explore": the lightest-weight of the four, a cream fill
   with a thin ink outline rather than a solid block, reading as a hint to
   look closer rather than a hard call to action. */
.fangin-cursor[data-state='ipl'] .fangin-cursor-circle {
  background: var(--color-bone);
  box-shadow: inset 0 0 0 1.5px var(--color-ink);
}

.fangin-cursor[data-state='ipl'] .fangin-cursor-label {
  color: var(--color-ink);
}

/* IPL hotspot — "View part": periwinkle fill, echoing the hotspots'
   own periwinkle interaction colour (see .ipl-hotspot.is-active in
   mowermerch-story.css) so the cursor reads as part of that same system,
   not a new one layered on top of it. */
.fangin-cursor[data-state='hotspot'] .fangin-cursor-circle {
  background: var(--color-periwinkle);
}

.fangin-cursor[data-state='hotspot'] .fangin-cursor-label {
  color: var(--color-ink);
}

/* Portfolio CTA — "View all →": solid ink circle + acid text. The CTA
   itself is a solid acid block (see .story-bridge-cta in
   mowermerch-story.css), so the cursor deliberately inverts that — ink
   fill instead of acid fill — for strong contrast against the button's
   own background rather than disappearing into it. */
.fangin-cursor[data-state='cta-portfolio'] .fangin-cursor-circle {
  background: var(--color-ink);
}

.fangin-cursor[data-state='cta-portfolio'] .fangin-cursor-label {
  color: var(--color-acid);
}

/* Final CTA — "Let's go →": same ink-circle contrast logic as the
   portfolio CTA above (this button is acid too), but periwinkle text
   instead of acid so the two acid-button cursor moments read as related,
   not identical — the last thing the visitor sees before "Let's talk". */
.fangin-cursor[data-state='cta-final'] .fangin-cursor-circle {
  background: var(--color-ink);
}

.fangin-cursor[data-state='cta-final'] .fangin-cursor-label {
  color: var(--color-periwinkle);
}

/* Results page helix stage — "Drag ↔": the lightest of the special states
   (same cream-fill/ink-outline treatment as "Explore" on the IPL diagram —
   both are a hint to interact with a larger surface, not a single
   button), so it doesn't visually compete with a card's own border/hover
   treatment underneath it. */
.fangin-cursor[data-state='drag'] .fangin-cursor-circle {
  background: var(--color-bone);
  box-shadow: inset 0 0 0 1.5px var(--color-ink);
}

.fangin-cursor[data-state='drag'] .fangin-cursor-label {
  color: var(--color-ink);
}

/* Helix detail panel / mobile card — "Visit ↗": periwinkle fill, matching
   the IPL hotspot's own "View part" treatment — both mark "this specific
   thing is clickable," inside a larger draggable/explorable surface. */
.fangin-cursor[data-state='visit'] .fangin-cursor-circle {
  background: var(--color-periwinkle);
}

.fangin-cursor[data-state='visit'] .fangin-cursor-label {
  color: var(--color-ink);
}

/* Helix project card — "View": same periwinkle-fill language as "Visit ↗"
   above (both mean "click this specific thing"), kept as its own state
   rather than reusing 'visit' outright so cursor.js's RULES priority list
   stays legible about which selector produced which label. */
.fangin-cursor[data-state='view'] .fangin-cursor-circle {
  background: var(--color-periwinkle);
}

.fangin-cursor[data-state='view'] .fangin-cursor-label {
  color: var(--color-ink);
}

/* Result chapter's brain-dump board — "Drag": two related states, not one,
   so the cursor itself tells the visitor whether a piece is merely
   grabbable or already being carried (mowermerch-story.js calls
   window.FanginCursor.setDragState() on pointerdown/pointerup to switch
   between them, overriding the hover-only .bd-block rule in cursor.js's
   RULES). "Grab" reuses the light cream/ink-outline hint treatment (same
   family as "Explore"/"Drag ↔" above — an invitation, not a commitment).
   "Hold" inverts to a solid periwinkle fill and grows slightly, echoing
   the board's own magnetic centre-ring feedback (.story-result-center.
   is-magnet, mowermerch-story.css) so cursor and board agree on what
   "engaged" looks like. */
.fangin-cursor[data-state='grab'] .fangin-cursor-circle {
  background: var(--color-bone);
  box-shadow: inset 0 0 0 1.5px var(--color-ink);
}

.fangin-cursor[data-state='grab'] .fangin-cursor-label {
  color: var(--color-ink);
}

.fangin-cursor[data-state='hold'] .fangin-cursor-circle {
  background: var(--color-periwinkle);
  transform: translate(-50%, -50%) scale(1.08);
}

.fangin-cursor[data-state='hold'] .fangin-cursor-label {
  color: var(--color-ink);
}

/* ---- Reduced motion ----
   base.css's own global rule already collapses every transition-duration
   on the page to ~0, this cursor's included, so state morphs and the
   opacity/is-active fade all become instant rather than animated with no
   extra rule needed here. What that global rule can't reach is the
   per-frame JS position lerp and the magnetic pull — cursor.js checks
   `prefers-reduced-motion` itself and sets both to an instant 1:1 follow
   with no pull applied at all when it's active (see tick() there). */
