/* =====================================================================
 * About page styles for KH Portfolio.
 * Reference viewport: 1440px. Matches Figma node 2:3 at this width.
 *
 * Covers: About page shell (page gradient), hero (Kelly photo), Just Keep
 * Swimming prose, Dive Log rows, Surface Time (caught-objects — static
 * resting state only), Shelf + Contact bar footer.
 *
 * Landing lives in layout.css; case-study pages live in case.css. Do not
 * add rules across files.
 *
 * All values reference tokens from tokens.css. Never hard-code a color,
 * size, or spacing without reading it from Figma first.
 *
 * No motion, transitions, or interactive states in this file. Motion
 * belongs in motion.css.
 * ===================================================================== */


/* -------------------- Reset (mirrors layout.css / case.css) -------------------- */

*, *::before, *::after { box-sizing: border-box; }

html, body { margin: 0; padding: 0; }

html { scroll-padding-top: var(--nav-h); }

body {
  font-family: var(--font-body);
  color: var(--ink-title);
  background: #f9fbfa;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  min-width: var(--layout-max);
}

img, video { display: block; max-width: 100%; }
ul, ol { list-style: none; padding: 0; margin: 0; }
a { color: inherit; text-decoration: none; }
h1, h2, h3, h4, p { margin: 0; }


/* -------------------- Nav (mirrors case.css so About stands alone) -------------------- */
/* Nav fills the top strip of the About page gradient (#bde7e2). motion.js
   toggles .nav--scrolled once the user scrolls; motion.css handles the
   backdrop-blur transition. */

.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  width: 100%;
  color: var(--ink-title);
  background-color: #bde7e2;
}

.nav__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  max-width: var(--layout-max);
  margin: 0 auto;
  padding: var(--nav-pad-y) var(--nav-pad-x);
}

.nav__logo {
  font-family: var(--font-display);
  font-size: var(--fs-title-l);
  letter-spacing: var(--track-kh-logo);
  line-height: var(--lh-tight);
}

.nav__links {
  display: flex;
  gap: var(--nav-gap);
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--fs-nav);
  letter-spacing: var(--track-nav);
}

.nav__link { opacity: var(--opacity-nav-link); }


/* -------------------- Page shell -------------------- */
/* .about-page carries the full page gradient authored in Figma (2:3):
   near-white at top, easing to pale teal at the bottom. Below 1440 the
   viewport shows body's #f9fbfa fallback rather than collapsing. */

.about-page {
  width: 100%;
  background: linear-gradient(180deg,
    rgb(189, 231, 226)   0%,
    rgb(189, 231, 226)   2.4563%,
    rgb(249, 251, 250)  14.569%,
    rgb(236, 245, 243)  42%,
    rgb(217, 237, 236)  78%,
    rgb(189, 224, 225) 100%);
  color: var(--ink-title);
  display: flex;
  flex-direction: column;
  padding-top: var(--nav-h);
}


/* -------------------- Header: Kelly photo + hover-me caption -------------------- */
/* Figma 4:133: 360 tall, own gradient overlay (teal top → cream bottom)
   that fights the page gradient at this height so the photo sits over a
   soft aqua wash. */

.about-header {
  position: relative;
  width: 100%;
  height: 360px;
  overflow: hidden;
  background: linear-gradient(180deg, #bde7e2 0%, #f9fbfa 100%);
}

/* Caps the positioning context to the 1440 design column so absolutely-  */
/* placed children (photo, hint) stay at their exact Figma coords on any  */
/* viewport width, while the parent gradient bleeds to the edges.        */
.about-header__inner {
  position: relative;
  width: 100%;
  max-width: var(--layout-max);
  height: 100%;
  margin: 0 auto;
}

.about-header__photo {
  position: absolute;
  left: 613.67px;
  top: 64px;
  width: 206.27px;
  height: 247.15px;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* The photo card itself is 200x242, sits inside the tilted bounding box. */
.about-header__card {
  width: 200px;
  height: 242px;
  border-radius: 10px;
  overflow: hidden;
  box-shadow: 0 12px 34px rgba(6, 40, 52, 0.2);
  transform: rotate(1.5deg);
}

.about-header__card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  pointer-events: none;
}

.about-header__hint {
  position: absolute;
  left: 693.5px;
  top: 326px;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 12px;
  line-height: var(--lh-tight);
  color: #3f6f78;
  white-space: nowrap;
}


/* -------------------- Just Keep Swimming (Figma 4:143) -------------------- */
/* Centered prose block, 640 wide content, big horizontal padding. */

.about-intro {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  width: 100%;
  padding: 44px 0 32px;
}

.about-intro__title {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 34px;
  line-height: var(--lh-tight);
  color: var(--ink-title);
  white-space: nowrap;
}

.about-intro__sub {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 14px;
  line-height: var(--lh-tight);
  color: #6b8b93;
  white-space: nowrap;
}

.about-intro__body {
  width: 640px;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 16.5px;
  line-height: 1.75;
  color: #2c4f59;
}

.about-intro__body em,
.about-intro__body b {
  font-style: normal;
  font-weight: 500;
  color: var(--ink-title);
}


/* -------------------- Dive Log (Figma 4:149) -------------------- */

.dive-log {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 40px 0 24px;
}

.dive-log__title {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 28px;
  line-height: var(--lh-tight);
  color: var(--ink-title);
  white-space: nowrap;
}

.dive-log__sub {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 15px;
  line-height: var(--lh-tight);
  color: #6b8b93;
  white-space: nowrap;
}

/* Row structure: date | rule | logo | text | link */
.dive-log__row {
  display: flex;
  align-items: center;
  gap: 28px;
  width: 904px;
  padding: 14px 0;
  border-bottom: 0.5px solid rgba(11, 58, 70, 0.13);
}

/* Rows in the "earlier dives" archive strip drop the border. */
.dive-log__row--archive { border-bottom: none; }

.dive-log__date {
  width: 110px;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 13.5px;
  line-height: var(--lh-tight);
  letter-spacing: 2.43px;
  color: #6b878c;
  white-space: nowrap;
}

.dive-log__rule {
  flex: 0 0 1px;
  width: 1px;
  height: 96px;
  background: #d9e3e3;
}

.dive-log__logo {
  flex: 0 0 96px;
  width: 96px;
  height: 96px;
  background: #ffffff;
  border: 0.64px solid #d9e3e3;
  border-radius: 8.96px;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
}

.dive-log__logo img { display: block; }

.dive-log__text {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.dive-log__job {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 17px;
  line-height: var(--lh-tight);
  color: var(--ink-title);
}

.dive-log__blurb {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 15px;
  line-height: 1.5;
  color: #3d626d;
}

.dive-log__link {
  flex: 0 0 40px;
  width: 40px;
  height: 40px;
  border: 0.833px solid rgba(11, 58, 70, 0.28);
  border-radius: 15px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 15px;
  line-height: var(--lh-tight);
  color: #2c4f59;
}


/* Earlier dives disclosure: 1px rule + toggle chip that opens the
   archive rows. Uses <details>/<summary> for native a11y — no JS.
   Chevron rotates 180deg via the [open] state selector. */

.dive-log__earlier {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 18px;
  width: 904px;
}

.dive-log__earlier-rule {
  width: 100%;
  height: 1px;
  background: #d9e3e3;
}

.dive-log__earlier-toggle {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px 9px;
  background: #f6f9f9;
  border: 1px solid #d9e3e3;
  border-radius: 999px;
  color: #6b878c;
  cursor: pointer;
  list-style: none;                       /* strip <summary>'s default disclosure triangle */
  -webkit-tap-highlight-color: transparent;
}
.dive-log__earlier-toggle::-webkit-details-marker { display: none; }
.dive-log__earlier-toggle::marker { display: none; }
.dive-log__earlier-toggle:focus-visible {
  outline: 2px solid #4f7b82;
  outline-offset: 2px;
}

.dive-log__earlier-label {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 11.5px;
  line-height: var(--lh-tight);
  letter-spacing: 1.61px;
}

.dive-log__earlier-count {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 11.5px;
  line-height: var(--lh-tight);
}

.dive-log__earlier-chevron {
  display: block;
  width: 14px;
  height: 8px;
  transition: transform 0.2s ease;
}

/* When expanded, flip the chevron so it reads as an "up" collapse cue.  */
/* <details> exposes the state via the [open] attribute — pure CSS, no JS. */
.dive-log__earlier[open] .dive-log__earlier-chevron {
  transform: rotate(180deg);
}

@media (prefers-reduced-motion: reduce) {
  .dive-log__earlier-chevron { transition: none; }
}

/* The archive rows sit inside <details> after <summary>. When collapsed,
   the browser hides them; when open, they render as siblings after the
   chip. Give them the same 18px gap the header uses. */
.dive-log__earlier[open] .dive-log__row--archive {
  margin-top: 18px;
}
.dive-log__earlier[open] .dive-log__row--archive + .dive-log__row--archive {
  margin-top: 0;
}


/* Per-logo positioning inside the 96x96 white card. Figma authors each
   logo at a specific x/y/width/height inside its card; those values are
   preserved here so nothing rescales or drifts. */

.logo-disney img            { width: 67.84px; height: 35.14px; }
.logo-cjenm img             { width: 67.84px; height: 51.08px; }
.logo-apr img               { width: 67.84px; height: 67.56px; }
.logo-designconsulting img  { width: 36.25px; height: 67.84px; }
.logo-vb img                { width: 67.84px; height: 67.84px; }
.logo-37degree img          { width: 67.84px; height: 67.84px; }
.logo-dsd img               { width: 67.84px; height: 67.84px; }

/* Cornell Perfect Match card: heart SVG glyph beside stacked text label,
   both authored at exact offsets inside the 96x96 card (Figma 283:22). */
.logo-pm {
  position: relative;
}
.logo-pm__glyph {
  position: absolute;
  left: 14.08px;
  top: 36.95px;
  width: 31.62px;
  height: 22.12px;
}
.logo-pm__label {
  position: absolute;
  left: 48.66px;
  top: 36.36px;
  width: 33.26px;
  height: 23.28px;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 6.85px;
  line-height: var(--lh-tight);
  color: #f4001f;
}


/* -------------------- Surface Time (Figma 4:233) — static resting state -------------------- */
/* 470 tall, absolute-positioned interior. The floating-objects canvas
   sits inside with its own gradient. Nothing here is wired; everything
   is authored exactly at the resting positions in Figma. */

.surface-time {
  position: relative;
  width: 100%;
  height: 470px;
  overflow: hidden;
}

/* Full-bleed aqua canvas: bleeds edge to edge so the water color is       */
/* continuous with the page gradient above and below it. The 1440-based    */
/* interior (glints, objects, caught note) lives inside its own inner box. */
.surface-time__canvas {
  position: absolute;
  left: 0;
  top: 188px;
  width: 100%;
  height: 282px;
  overflow: hidden;
  background: linear-gradient(180deg,
    rgba(189, 224, 225, 0) 0%,
    rgba(180, 219, 222, 0.6) 50%,
    #a4d3d8 100%);
}

/* Caps the positioning context inside .surface-time to the 1440 column     */
/* so title, sub, and the 1440-wide sparkle PNG land at their exact Figma   */
/* coords on any viewport width. The band bg above lives on the parent.    */
/* pointer-events: none — this layer paints ON TOP of .surface-time__canvas */
/* (later sibling in DOM) and holds only decorative content (glisten PNG,   */
/* title, sub); without this, it would eat clicks meant for the floating    */
/* objects inside the canvas below.                                          */
.surface-time__inner {
  position: relative;
  width: 100%;
  max-width: var(--layout-max);
  height: 100%;
  margin: 0 auto;
  pointer-events: none;
}

/* Full-viewport sparkle band. The source PNG is authored at 1440 wide;   */
/* stretching to 100% widens each sparkle by ~1.33x on a 1920 monitor,    */
/* which is imperceptible because the sparkles are decorative shapes with */
/* no precise horizontal geometry — and it beats leaving bare gradient    */
/* strips on the sides of wide viewports.                                 */
.surface-time__glisten {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 128px;
  display: block;
  pointer-events: none;
}

.surface-time__title {
  position: absolute;
  left: 616.5px;
  top: 116px;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 34px;
  line-height: var(--lh-tight);
  color: var(--ink-title);
  white-space: nowrap;
}

.surface-time__sub {
  position: absolute;
  left: 631px;
  top: 168px;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 14px;
  line-height: var(--lh-tight);
  color: #4f7b82;
  white-space: nowrap;
}

/* Canvas interior: positioning context capped to 1440 so the glints, the  */
/* floating objects, and the caught-note render at their exact Figma       */
/* coords. Wider viewports show the aqua bleed on either side of this box. */
.surface-time__canvas-inner {
  position: relative;
  width: 100%;
  max-width: var(--layout-max);
  height: 100%;
  margin: 0 auto;
}

.surface-time__glints {
  position: absolute;
  left: 60px;
  top: 14px;
  width: 743.2px;
  height: 132px;
  pointer-events: none;
}

/* Floating objects — each authored as (bounding box left/top/width/height)
   with the inner image tilted to the exact rotation from Figma. */

.object {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
}

.object img { display: block; }

.object-glasses {
  left: 111px;
  top: 94px;
  width: 143.78px;
  height: 117.52px;
}
.object-glasses img {
  width: 124px;
  height: 82px;
  transform: rotate(-18.76deg);
  filter: drop-shadow(0 4px 4px rgba(0, 0, 0, 0.25));
}

.object-camera {
  left: 331px;
  top: 96px;
  width: 142.44px;
  height: 108.2px;
}
.object-camera img {
  width: 126.96px;
  height: 80.2px;
  transform: rotate(13.82deg);
  filter: drop-shadow(0 4px 4px rgba(0, 0, 0, 0.25));
}

.object-cheese {
  left: 647px;
  top: 134px;
  width: 117px;
  height: 78px;
}
.object-cheese img {
  width: 117px;
  height: 78px;
  filter: drop-shadow(0 4px 4px rgba(0, 0, 0, 0.25));
}

.object-pen {
  left: 897px;
  top: 59px;
  width: 174.43px;
  height: 128.88px;
}
.object-pen img {
  width: 30.15px;
  height: 188.88px;
  transform: rotate(123.29deg);
  filter: drop-shadow(0 4px 4px rgba(0, 0, 0, 0.25));
}

.object-starfish {
  left: 1204px;
  top: 96px;
  width: 121px;
  height: 120px;
}
.object-starfish img {
  width: 121px;
  height: 120px;
  filter: drop-shadow(0 4px 4px rgba(0, 0, 0, 0.25));
}


/* Caught note (tooltip) — Figma 297:11, 400x81. Position is set by
   about.js (per-catch, clamped to canvas). One note visible at a time;
   fades after 4s or on the next catch (spec 290:11 · A). */

.caught-note {
  position: absolute;
  left: 470px;
  top: 164px;
  width: 400px;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 20px 12px 14px;
  background: rgba(255, 255, 255, 0.92);
  border-radius: 16px;
  box-shadow: 0 8px 22px rgba(10, 38, 46, 0.14);
  pointer-events: none;
  transition: opacity 250ms ease, transform 250ms ease;
}
.caught-note--hidden {
  opacity: 0;
  transform: translateY(4px);
}

.caught-note__thumb {
  position: relative;
  flex: 0 0 46px;
  width: 46px;
  height: 46px;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Thumb renders any object's icon upright and contained in the 46-square.
   The original static example rotated the camera; the JS-driven flow
   receives all 5 objects with different aspect ratios, so we fit-inside
   without rotation. */
.caught-note__thumb img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  filter: drop-shadow(0 1.3px 1.3px rgba(0, 0, 0, 0.25));
}

.caught-note__text {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.caught-note__label {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 9.5px;
  line-height: var(--lh-tight);
  letter-spacing: 1.33px;
  color: #4f7b82;
}

.caught-note__body {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-title);
}


/* -------------------- Shelf (Figma 4:237) -------------------- */
/* Sand strip beneath Surface Time. Row of caught-object slots plus a
   "Throw them back" pill. Static: no wiring. */

.about-shelf {
  width: 100%;
  height: 115px;
  padding: 20px 0;
  background: var(--surface-sand-day);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 24px;
}

.about-shelf__label {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 12.5px;
  line-height: var(--lh-tight);
  color: #4f7b82;
  white-space: nowrap;
}

.shelf-slots {
  display: flex;
  gap: 14px;
  align-items: center;
}

.shelf-slot {
  position: relative;
  width: 72px;
  height: 72px;
  border-radius: 999px;
  overflow: hidden;
}

.shelf-slot--filled {
  background: rgba(255, 255, 255, 0.75);
}

/* Empty slot renders as an authored PNG sprite (Figma 297:24) — a soft */
/* dashed ring with a subtle inner sheen. Kept as an <img> child so the */
/* wrapper stays consistent with .shelf-slot--filled.                   */
.shelf-slot--empty {
  background: transparent;
}
.shelf-slot--empty img {
  display: block;
  width: 100%;
  height: 100%;
}

/* Filled-slot icon: small rotated copy of the object PNG, positioned per
   Figma so the object reads as "resting inside" the circle. */

.shelf-slot__icon {
  position: absolute;
  display: block;
  filter: drop-shadow(0 1.83px 1.83px rgba(0, 0, 0, 0.25));
}

.shelf-slot--camera .shelf-slot__icon {
  width: 58px;
  height: 36.64px;
  left: 7px;
  top: 24.36px;
  transform: rotate(13.82deg);
}

.shelf-slot--glasses .shelf-slot__icon {
  width: 58px;
  height: 38.3px;
  left: 7px;
  top: 16.85px;
  transform: rotate(-18.72deg);
}

.shelf-slot--cheese .shelf-slot__icon {
  width: 58px;
  height: 38.73px;
  left: 7px;
  top: 16.64px;
}

/* Pen and starfish weren't in the authored 3-slot shelf; sizes/positions
   chosen so each reads as its own object at 72px slot. Pen preserves its
   canvas rotation so "caught pen" reads the same as in-water pen. */
.shelf-slot--pen .shelf-slot__icon {
  width: 12px;
  height: 75px;
  left: 30px;
  top: -1px;
  transform: rotate(123.29deg);
  transform-origin: 50% 50%;
}

.shelf-slot--starfish .shelf-slot__icon {
  width: 55px;
  height: 55px;
  left: 8.5px;
  top: 8.5px;
}

/* Throw them back — inert until 5 caught, then live. Reset browser button
   defaults since the pill is authored as a plain styled element. */
.about-shelf__throw {
  display: inline-flex;
  align-items: center;
  padding: 4px 12px;
  background: rgba(255, 255, 255, 0.6);
  border: 1px solid rgba(79, 123, 130, 0.35);
  border-radius: 999px;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 12.5px;
  line-height: var(--lh-tight);
  color: #4f7b82;
  white-space: nowrap;
  cursor: default;
  opacity: 0.55;
  transition: opacity 250ms ease, background 200ms ease;
}
.about-shelf__throw:not([disabled]) {
  cursor: pointer;
  opacity: 1;
}
.about-shelf__throw:not([disabled]):hover {
  background: rgba(255, 255, 255, 0.92);
}
.about-shelf__throw:focus-visible {
  outline: 2px solid rgba(79, 123, 130, 0.6);
  outline-offset: 3px;
}


/* -------------------- Contact bar (Figma 296:11) -------------------- */
/* Sand-colored strip: LinkedIn/Email buttons over the kh. sign-off.
   Uses the day-footer button tokens shared with case pages. */

.contact-bar {
  width: 100%;
  height: 162px;
  padding: 30px 0 38px;
  background: var(--surface-sand-day);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
}

.contact-bar__buttons {
  display: flex;
  gap: var(--footer-btn-gap);
}

.contact-bar .btn {
  display: inline-flex;
  align-items: center;
  padding: var(--footer-btn-pad);
  border-radius: var(--radius-btn);
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--fs-btn);
  line-height: var(--lh-tight);
  white-space: nowrap;
}

.contact-bar .btn--day-outline {
  background: var(--surface-linkedin-day);
  border: 1px solid var(--ink-title);
  color: var(--ink-title);
}

.contact-bar .btn--day-solid {
  background: var(--ink-title);
  color: var(--ink-day-email);
}

.contact-bar__signoff {
  display: flex;
  align-items: baseline;
  gap: var(--footer-signoff-gap);
  color: var(--ink-day-signature);
  white-space: nowrap;
}

.contact-bar__monogram {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--fs-monogram-footer);
  line-height: var(--lh-tight);
}

.contact-bar__tag {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: var(--fs-bubble);
  line-height: var(--lh-tight);
}


/* =====================================================================
 * Surface Time interaction (about.js).
 *
 * All of the motion below is scroll- and click-driven, so per the project
 * split it belongs beside the interaction that owns it — the actual
 * ambient patterns (bubbles, plankton, sunrays, ripples) still live in
 * motion.css. Here we own only the About-specific drift/bob, the
 * cursor-acknowledgement hover, the catch lift, and the completion
 * signature reveal.
 * ===================================================================== */

/* Bubble palette for the surface-time canvas. Same recipe the aquarium  */
/* day footer uses (see motion.css → .aquarium block) so the bubbles     */
/* read as one system with the day footer.                                */
.surface-time__canvas {
  --kh-motion-fg: color-mix(in srgb, var(--ink-day-signature) 45%, transparent);
}


/* -------------------- Object drift + bob + hover -------------------- */
/* Two independent CSS animations compose via typed custom properties so */
/* the composite transform stays clean. about.js writes per-object       */
/* --bob-dur/--drift-dur/etc. so nothing pulses in unison — same         */
/* treatment as the seaweed sway system.                                  */

@property --kh-obj-bob      { syntax: '<length>'; initial-value: 0px; inherits: false; }
@property --kh-obj-drift    { syntax: '<length>'; initial-value: 0px; inherits: false; }
@property --kh-hover-scale  { syntax: '<number>'; initial-value: 1;   inherits: false; }

@keyframes kh-obj-bob {
  0%, 100% { --kh-obj-bob: 0px; }
  50%      { --kh-obj-bob: var(--bob-amp, 6px); }
}

@keyframes kh-obj-drift {
  0%, 100% { --kh-obj-drift: 0px; }
  50%      { --kh-obj-drift: var(--drift-amp, 10px); }
}

.object {
  transform:
    translate(var(--kh-obj-drift, 0px), var(--kh-obj-bob, 0px))
    scale(var(--kh-hover-scale, 1));
  transform-origin: 50% 50%;
  cursor: pointer;
  will-change: transform;
  animation:
    kh-obj-bob   var(--bob-dur,   6s)  ease-in-out infinite,
    kh-obj-drift var(--drift-dur, 10s) ease-in-out infinite;
  animation-delay: var(--bob-delay, 0s), var(--drift-delay, 0s);
  transition: --kh-hover-scale 180ms ease, filter 180ms ease;
}

.object:hover,
.object:focus-visible {
  --kh-hover-scale: 1.06;
}
.object:hover img,
.object:focus-visible img {
  filter: drop-shadow(0 6px 8px rgba(0, 0, 0, 0.32));
}

.object:focus-visible {
  outline: 2px solid rgba(11, 58, 70, 0.55);
  outline-offset: 4px;
  border-radius: 10px;
}


/* -------------------- Catch fade ---------------------------------- */
/* Simple opacity fade — reliable regardless of where drift/bob has the  */
/* object at click time. A transform-lift would need to compose with the */
/* running --kh-obj-bob/drift vars and gets ugly at some positions.      */

.object--caught {
  opacity: 0;
  transition: opacity 400ms ease-out;
  pointer-events: none;
}


/* -------------------- Completion moment --------------------------- */
/* Placed inside .surface-time__canvas-inner near the bottom-center so    */
/* the signature and line surface from the water. Signature clip-paths    */
/* in from the left (reads as drawing). Line slides+fades in beneath.     */

.completion {
  position: absolute;
  left: 50%;
  bottom: 22px;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 500ms ease;
}
.completion--visible { opacity: 1; }

/* Signature draws in via clip-path. The Potassium Scandal glyphs have a  */
/* tail that extends past the text bounding box on the right — padding    */
/* and a slightly negative final inset give the flourish room to render    */
/* without being cropped.                                                  */
.completion__signature {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 44px;
  line-height: 1;
  color: var(--ink-title);
  padding-right: 12px;
  clip-path: inset(0 110% 0 0);
  transition: clip-path 800ms cubic-bezier(0.4, 0, 0.2, 1);
}
.completion--visible .completion__signature {
  clip-path: inset(0 -6px 0 0);
}

.completion__line {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 14px;
  color: var(--ink-day-signature);
  opacity: 0;
  transform: translateY(6px);
  transition:
    opacity   400ms ease 900ms,
    transform 400ms ease 900ms;
}
.completion--visible .completion__line {
  opacity: 1;
  transform: translateY(0);
}


/* -------------------- Scroll reveal --------------------------------- */
/* Elements tagged [data-reveal] start hidden + 24px low, then rise into  */
/* place with a slight-overshoot ease when about.js flips .is-revealed on */
/* first-viewport intersection (~20% visible, once only, no re-animate).  */
/*                                                                        */
/* Grouped members (children of [data-reveal-group]) get a per-index      */
/* --reveal-delay written by about.js so the group arrives in sequence.   */
/*                                                                        */
/* @media (scripting: enabled) gates the hidden state so no-JS visitors   */
/* see the final render — nothing gets stuck offscreen if the script      */
/* doesn't load. Reduced-motion users bypass the block entirely.          */

@media (scripting: enabled) and (prefers-reduced-motion: no-preference) {
  [data-reveal] {
    opacity: 0;
    transform: translateY(24px);
    transition:
      opacity   540ms cubic-bezier(0.34, 1.56, 0.64, 1),
      transform 540ms cubic-bezier(0.34, 1.56, 0.64, 1);
    transition-delay: var(--reveal-delay, 0ms);
  }
  [data-reveal].is-revealed {
    opacity: 1;
    transform: translateY(0);
  }
}


/* -------------------- Reduced motion -------------------------------- */
/* Objects stay catchable (hover feedback stays too, but instant — the   */
/* eased transitions are dropped so state changes snap). Drift, bob,     */
/* catch lift, signature draw all off. Bubbles are gated out in about.js */
/* and further neutered by motion.css's kh-bubble reduced-motion rule.   */

@media (prefers-reduced-motion: reduce) {
  .object {
    animation: none !important;
    transform: none !important;
    transition: none !important;
  }
  .object:hover img,
  .object:focus-visible img {
    filter: none !important;
  }
  .object--caught { animation: none !important; }

  .caught-note { transition: none; }

  .completion,
  .completion__signature,
  .completion__line {
    transition: none !important;
    clip-path: none !important;
    transform: translateX(-50%) !important;
  }

  .about-shelf__throw { transition: none; }
}
