/* ---- Shared styles for the portfolio (homepage + work detail pages) ---- */

* { box-sizing: border-box; margin: 0; padding: 0; }
/* headings/paragraphs share the body's 14px / 460 per the design */
h1, h2, h3, p { font-size: inherit; font-weight: inherit; }

:root {
  font-family: Inter, sans-serif;
  font-feature-settings: 'liga' 1, 'calt' 1; /* fix for Chrome */
  --textprimary: #111;
  --textsecondary: rgba(0, 0, 0, 0.4);
  --select: rgba(161, 213, 246, 0.6);
  --selectText: #2b8fd6;
  --underline: rgba(0, 0, 0, 0.12);
  --divider: rgba(0, 0, 0, 0.09);
}
@supports (font-variation-settings: normal) {
  :root { font-family: InterVariable, sans-serif; }
}

/* plain sky gradient shows while three.js / the grass load */
html, body {
  min-height: 100%;
  background: linear-gradient(180deg, #46a6d8 0%, #9fd2ec 55%, #bce0f1 100%);
}
body {
  font-family: inherit;
  font-weight: 460;
  font-size: 14px;
  line-height: 1.4;
  color: var(--textprimary);
  -webkit-font-smoothing: antialiased;
}

/* sky-blue text selection, echoing the scene's sky */
::selection { background: var(--select); color: var(--textprimary); }

/* grass scene sits behind everything as a fixed backdrop. 100vw/100vh so the
   canvas never resizes when a scrollbar appears (avoids background jitter). */
#scene {
  position: fixed; top: 0; left: 0;
  width: 100vw; height: 100vh;
  display: block; z-index: 0;
}

/* centred overlay that holds a card */
.page {
  position: relative; z-index: 1;
  min-height: 100vh;
  display: flex; align-items: center; justify-content: center;
  padding: 16px 16px 32px;
  /* keep the card window-centred whether or not a scrollbar is present, so it
     doesn't shift when the scrollbar appears mid-morph. (100vw - 100%) equals
     the scrollbar width: 0 when absent, the scrollbar width when present. */
  padding-left: calc(16px + (100vw - 100%));
}

/* the white card shell, shared by every page */
.card {
  width: min(560px, 100%);
  display: flex; flex-direction: column;
  position: relative;
  background: #fff;
  border-radius: 12px;
  padding: 24px 20px 16px;
  box-shadow:
    0px 0px 0px 1px rgba(0, 0, 0, 0.06),
    0px 1px 2px -1px rgba(0, 0, 0, 0.06),
    0px 2px 4px 0px rgba(0, 0, 0, 0.04);
}

/* ---- Homepage (intro card) -------------------------------------- */
.home.card { min-height: 705px; }

/* decorative art on the card surface, behind the text content (z-index -1
   within the card's stacking context), in the bottom-right, inside the card */
.corner-art {
  position: absolute;
  /* inset to the card's own gutter so it reads as a motif stamped on the page
     rather than artwork bleeding out of the corner */
  right: 20px; bottom: 16px;
  /* native 351×164; still scales down on narrow screens */
  width: min(174px, 34vw);
  height: auto; /* keeps the SVG's native aspect ratio */
  z-index: -1; /* behind the card's text content, on its white surface */
  display: block;
  pointer-events: none;
}

.name { margin-top: 0; }
.subtitle { margin-top: 2px; color: var(--textsecondary); }

.intro {
  margin-top: 24px;
  display: flex; flex-direction: column; gap: 16px;
}

/* inline links, shared by the home intro and the case-study body so the two
   can't drift apart */
.intro a,
.doc-body a {
  color: var(--textprimary);
  text-decoration: underline;
  text-decoration-color: var(--underline);
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
  transition: background 0.12s ease;
}
/* hover highlights the link like a text selection */
.intro a:hover,
.doc-body a:hover { background: var(--select); }

.work { margin-top: 52px; }

.section-label { color: var(--textsecondary); }

.divider {
  margin-top: 2px;
  height: 1px; border: 0;
  background: var(--divider);
}

.work-list {
  margin-top: 12px;
  display: flex; flex-direction: column; gap: 10px;
}

.work-row {
  display: flex; align-items: center;
  height: 32px; padding: 0 8px;
  margin: 0 -8px;
  border-radius: 8px;
  color: var(--textprimary); text-decoration: none;
  transition: color 0.15s ease;
  -webkit-tap-highlight-color: transparent; /* no blue tap overlay */
  user-select: none;
}
/* hover recolours only the role and the arrow (year stays muted) */
.work-row:hover .role,
.work-row:hover .arrow { color: var(--selectText); }
.work-row:focus { outline: none; }
.work-row:focus-visible { outline: 2px solid var(--select); outline-offset: 2px; }

.work-row .role { flex: 1; min-width: 0; }
.work-row .meta {
  display: flex; align-items: center; gap: 8px;
  flex: none; color: var(--textsecondary);
  transition: color 0.15s ease;
}
.work-row .arrow { line-height: 1; }

/* ---- Stickers: die-cut logos carrying the card's resting shadow; hover
       gives a slight lift + twist and reveals the label ---- */

/* the free canvas fills the space between the work list and the footer; each
   sticker is absolutely placed within it and dragged anywhere (see sticker.js) */
.sticker-canvas {
  position: relative;
  flex: 1 1 auto;
  min-height: 150px;
  margin-top: 20px;
}

.sticker {
  position: absolute;
  left: 12px; top: 16px;  /* default starting spot on every load */
  display: inline-flex; flex-direction: column;
  align-items: center; gap: 10px;
  color: var(--textsecondary);
  text-decoration: none;
  /* no touch-action:none while dragging is disabled — it would stop the page
     scrolling when a touch starts on a sticker. restore it with the drag. */
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
  cursor: pointer;  /* the sticker is just a link now */
}
.sticker:focus { outline: none; }
.sticker:focus-visible { outline: 2px solid var(--select); outline-offset: 4px; border-radius: 8px; }

/* Swiggy's mark is portrait where Google Pay's is landscape, so it's sized by
   height: 54px wide renders the logo 68px tall, matching Google Pay's */
.sticker--swiggy { left: 104px; top: 12px; }
.sticker--swiggy .sticker-art { width: 46px; }

/* the white die-cut border is baked into each SVG as a vector stroke, so only
   shadows are applied here. the resting shadow is a tight contact shadow —
   barely any offset or spread — so the sticker reads as stuck to the card.
   both states list three drop-shadows and no url() filter, which keeps the
   filter interpolable: mismatched lists (or a url()) make the browser swap
   discretely mid-transition, which looked like the border bulging on hover. */
/* width covers the artwork plus the viewBox's transparent slack, so the logo
   itself still renders 80px wide */
.sticker-art {
  width: 75px; height: auto; display: block;
  filter:
    drop-shadow(0 0 1px rgba(0, 0, 0, 0.12))
    drop-shadow(0 1px 1.5px rgba(0, 0, 0, 0.12))
    drop-shadow(0 0 0 rgba(0, 0, 0, 0));
  /* own compositor layer, kept through the hover transform: without it Safari
     sizes the filter region off the untransformed box and clips the larger
     hover shadow. same treatment as .lightbox img and #scene. */
  transform: translateZ(0);
  backface-visibility: hidden;
  transition: transform 0.3s ease, filter 0.3s ease;
  pointer-events: none;
}
/* hovering lifts it subtly: a small rise + twist while the shadow drops
   lower and diffuses, like the sticker peeled a couple millimetres off */
.sticker:hover .sticker-art,
.sticker:focus-visible .sticker-art {
  transform: translateY(-3px) rotate(-2deg) translateZ(0);
  filter:
    drop-shadow(0 0 1px rgba(0, 0, 0, 0.08))
    drop-shadow(0 3px 4px rgba(0, 0, 0, 0.1))
    drop-shadow(0 6px 10px rgba(0, 0, 0, 0.09));
}

/* label in the page's own body text; hidden at rest, fading in on hover/focus
   in the page's hover blue */
.sticker-caption {
  text-align: center;
  color: var(--selectText);
  opacity: 0;
  transform: translateY(-2px);
  transition: opacity 0.3s ease, transform 0.3s ease;
}
.sticker:hover .sticker-caption,
.sticker:focus-visible .sticker-caption {
  opacity: 1;
  transform: translateY(0);
}

/* once a press-and-hold engages, the sticker reads as "picked up". these sit
   after the hover rules so the held state can't flicker back to hover while
   the pointer briefly outruns the sticker mid-drag */
/* the held sticker rises above its siblings so it never slides underneath one */
.sticker.dragging { cursor: grabbing; z-index: 2; }
.sticker.dragging .sticker-art {
  transform: translateY(-4px) scale(1.06);
  transition: transform 0.15s ease;
}
.sticker.dragging .sticker-caption { opacity: 1; transform: translateY(0); }

.card-footer {
  margin-top: auto;
  padding-top: 48px;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 12px;
  align-items: baseline;
}

.resume,
.email-link {
  color: var(--textsecondary);
  text-decoration: none;
  transition: color 0.15s ease;
}
.resume:hover,
.email-link:not(.is-copied):hover { color: var(--selectText); }

/* Email action styled as inline text; label fades when it swaps */
.email-link {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  cursor: pointer;
  transition: color 0.15s ease, opacity 0.2s ease, transform 0.2s ease;
}
/* the confirmation shows in the page's selection blue and isn't clickable */
.email-link.is-copied {
  color: var(--selectText);
  cursor: default;
  pointer-events: none;
}

@media (max-width: 640px) {
  .home.card { min-height: 640px; }
}

/* ---- Work detail (long-format article) -------------------------- */
/* long read: top-align so a tall card scrolls from the top */
.page:has(.card.doc) { align-items: flex-start; }

/* left/right match the home card's gutter so both pages align */
.doc { padding: 20px 20px; }

/* stacks the floating back chip above the card */
.doc-wrap {
  display: flex; flex-direction: column;
  gap: 16px;
  width: min(560px, 100%);
}

/* back button floats outside the card, styled as a chip on the grass */
.back {
  align-self: flex-start;
  display: inline-flex; align-items: center; gap: 4px;
  /* horizontal: the ← carries a 1px left side bearing while "Back" ends flush
     on the k, so the box padding is offset by 1px to read as ~12px both sides.
     vertical: Inter's ascent/descent aren't symmetric about the line box, so
     equal 7px padding left the glyphs sitting ~0.75px low — the half-pixel
     shift centres the ink without changing the height. */
  padding: 6.5px 12px 7.5px 11px;
  border-radius: 999px;
  background: #fff;
  color: var(--textsecondary);
  text-decoration: none;
  box-shadow:
    0px 0px 0px 1px rgba(0, 0, 0, 0.06),
    0px 1px 2px -1px rgba(0, 0, 0, 0.06),
    0px 2px 4px 0px rgba(0, 0, 0, 0.04);
  transition: color 0.15s ease;
}
.back:hover { color: var(--textprimary); }
/* inherit the text's line-height so both flex items are the same height and
   their baselines land exactly together; the ←'s ink still sits 1px below the
   word's optical centre, so nudge it up to match. */
.back-arrow { line-height: inherit; transform: translateY(-1px); }

.cover {
  margin-top: 20px;
  aspect-ratio: 32 / 9; /* half the height of a 16:9 block */
  border-radius: 8px;
  background: linear-gradient(135deg, #bfe0f6, #f6dd87);
}

.doc-title { margin-top: 0; }
.doc-meta { margin-top: 2px; color: var(--textsecondary); }

.doc-body { margin-top: 24px; line-height: 1.5; }
.doc-body p { margin-bottom: 16px; }
.doc-body p:last-child { margin-bottom: 0; }
.doc-body h2 {
  margin-top: 32px; margin-bottom: 2px;
  font-weight: 600;
}
/* divider below the heading uses the shared <hr class="divider"> */
.doc-body .divider { margin: 0 0 12px; }
/* sub-heading: same weight/size as h2 but in the secondary colour */
.doc-body h3 {
  margin-top: 24px; margin-bottom: 8px;
  font-weight: 600;
  color: var(--textsecondary);
}

/* ---- Chip tabs: show one panel at a time to keep the page short ---- */
.chips {
  display: flex; flex-wrap: wrap; gap: 8px;
  margin: 32px 0 36px;
}
.chip {
  appearance: none; cursor: pointer;
  display: inline-flex; align-items: center; gap: 7px;
  padding: 7px 14px;
  border-radius: 999px;
  border: 0;
  background: rgba(0, 0, 0, 0.04);
  color: rgba(0, 0, 0, 0.5);  /* muted grey until hovered/active */
  font: inherit;
  transition: background 0.15s ease, color 0.15s ease;
}
.chip:hover { background: rgba(0, 0, 0, 0.08); color: var(--textprimary); }
/* active state derives from the scene's sky blue */
.chip.is-active {
  background: rgba(161, 213, 246, 0.28); /* --select tint, no border needed */
  color: var(--selectText);
}

.chip-panel { display: none; }
.chip-panel.is-active { display: block; }

/* ---- Case-study footer: back-to-top + section pager ------------- */
.doc-footer {
  margin-top: 40px;
  padding-top: 20px;
  border-top: 1px solid var(--divider);
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
}
/* round icon buttons, filled the same way as the chips (no border) */
.scroll-top,
.pager-arrow {
  appearance: none; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px;
  border-radius: 50%;
  border: 0;
  background: rgba(0, 0, 0, 0.04);
  color: rgba(0, 0, 0, 0.5);
  font: inherit; line-height: 1;
  transition: background 0.15s ease, color 0.15s ease, opacity 0.15s ease;
}
.scroll-top:hover,
.pager-arrow:hover:not(:disabled) { background: rgba(0, 0, 0, 0.08); color: var(--textprimary); }
.pager-arrow:disabled { opacity: 0.4; cursor: default; }
.scroll-top-arrow { transform: translateY(-1px); }

/* 1 / x section pager */
.doc-pager { display: inline-flex; align-items: center; gap: 10px; margin-left: auto; }
.pager-count {
  color: rgba(0, 0, 0, 0.5);
  font-variant-numeric: tabular-nums;
  min-width: 2.6em; text-align: center;
}

.doc-body strong { font-weight: 600; }
.doc-body ul { margin: 0 0 16px 1.1em; }
.doc-body li { margin-bottom: 6px; }

/* outbound reference as a simple card so it stands out from body copy. scoped
   under .doc-body to outrank the shared inline-link rules, which would
   otherwise underline it and paint the blue hover highlight. */
.doc-body .link-card {
  display: flex; flex-direction: column; gap: 2px;
  margin: 24px 0 0;
  padding: 14px 16px;
  border-radius: 12px;
  background: rgba(0, 0, 0, 0.04);  /* same grey as the chips */
  color: var(--textprimary);
  text-decoration: none;
  transition: background 0.15s ease;
}
.doc-body .link-card:hover { background: rgba(0, 0, 0, 0.08); }
.doc-body .link-card:focus { outline: none; }
.doc-body .link-card:focus-visible { outline: 2px solid var(--select); outline-offset: 2px; }
.link-card-note { color: var(--textsecondary); }

.figure { margin: 24px 0; }
.figure .ph {
  aspect-ratio: 16 / 10;
  border-radius: 12px;
  background: #f2f2f2;
}
.figure img {
  width: 100%;
  height: auto;
  border-radius: 12px;
  border: 1px solid var(--divider);
  display: block;
}

/* ---- Zoomable image + lightbox --------------------------------- */
.zoomable { position: relative; display: block; cursor: zoom-in; }
.zoom-icon {
  position: absolute;
  right: 8px; bottom: 8px;
  display: flex; align-items: center; justify-content: center;
  width: 30px; height: 30px;
  padding: 0; border: 0; border-radius: 6px;
  background: rgba(0, 0, 0, 0.5);
  color: #fff;
  cursor: zoom-in;
  opacity: 0.85;
  transition: opacity 0.15s ease, background 0.15s ease;
}
.zoomable:hover .zoom-icon,
.zoom-icon:focus-visible { opacity: 1; background: rgba(0, 0, 0, 0.68); }

.lightbox {
  position: fixed; inset: 0; z-index: 1000;
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
  background: rgba(18, 22, 28, 0.82);
  cursor: zoom-out;
  opacity: 0; visibility: hidden;
  transition: opacity 0.2s ease, visibility 0.2s ease;
  /* own compositor layer: stops edge shimmer over the animated grass canvas */
  transform: translateZ(0);
}
.lightbox.open { opacity: 1; visibility: visible; }
.lightbox img {
  max-width: min(1100px, 94vw);
  max-height: 92vh;
  border-radius: 12px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
  transform: scale(0.97) translateZ(0);
  backface-visibility: hidden;
  transition: transform 0.2s ease;
}
.lightbox.open img { transform: scale(1) translateZ(0); }
@media (prefers-reduced-motion: reduce) {
  .lightbox, .lightbox img { transition: none; }
}
.figure figcaption {
  margin-top: 8px;
  color: var(--textsecondary);
}

/* ---- Page transition (container transform) ---------------------- */
/* Only the .page content swaps; the #scene canvas stays mounted, so the
   grass background never reloads between routes. The card is a shared
   element: its box morphs (expands) between pages while the content inside
   eases in, so the two pages feel like one continuous surface. */
.card { view-transition-name: card; }

@media (prefers-reduced-motion: no-preference) {
  /* The root snapshot includes the animated grass canvas, so ANY root animation
     dissolves two frames of a moving background against each other — that read
     as a background flicker. Holding all three root pseudos still cuts straight
     to the new background instead. It also keeps the root off the browser's
     default 250ms clock, which was fighting the 700ms crossfade set here. */
  ::view-transition-group(root),
  ::view-transition-old(root),
  ::view-transition-new(root) { animation: none; }

  /* The morphing box carries the card's surface (white + shadow + radius), so
     the empty card visibly grows FIRST. Content crossfades on top: the old
     leaves fast, the new eases in over the tail of the morph. */
  ::view-transition-group(card) {
    background: #fff;
    border-radius: 12px;
    box-shadow:
      0px 0px 0px 1px rgba(0, 0, 0, 0.06),
      0px 1px 2px -1px rgba(0, 0, 0, 0.06),
      0px 2px 4px 0px rgba(0, 0, 0, 0.04);
    animation-duration: 520ms;
    animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
  }
  ::view-transition-image-pair(card) { border-radius: 12px; }
  /* old content fades out as the box starts to grow */
  ::view-transition-old(card) { animation: 140ms ease both vt-fade-out; }
  /* new content eases in over the tail of the morph. this used to be a 0ms
     animation behind a 740ms delay, which snapped the content in — the pop
     itself looked like a flicker, and it stretched the whole transition. */
  ::view-transition-new(card) { animation: 240ms ease 340ms both vt-content-in; }
}
@keyframes vt-fade-out { to { opacity: 0; } }
@keyframes vt-content-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}
