/* ==========================================================================
   Vishakh Thampi — Portfolio
   style.css
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Design tokens
   -------------------------------------------------------------------------- */

:root {
  /* Colour */
  --c-bg:            #ffffff;
  --c-ink:           #000000;
  --c-ink-muted:     #6a6a6a;
  --c-banner:        #000000;
  --c-banner-text:   #ffffff;
  --c-placeholder:   #d9d9d9;
  --c-rule:          #e2e2e2;

  /*
     TWO MARGINS.

     --gutter    PRIMARY. The main column, aligned to the wordmark and the
                 nav. Most content sits here.
     --gutter-2  SECONDARY. A much thinner margin used when content should
                 deliberately break the grid and run wider. Derived from the
                 primary so the two always stay in proportion (~26%).

     A third case exists: no margin at all — the decorative patterns, which
     run the full viewport width and ignore both (see `.case-pattern`).

     Use the `.break` utility to move an element from primary to secondary.
  */
  --gutter:          clamp(24px, 9vw, 180px);
  --gutter-2:        max(8px, calc(var(--gutter) * 0.26));

  /* Grid */
  --grid-gap:        clamp(10px, 1.1vw, 22px);

  /* Type scale. Tied to viewport height as well as width so the home screen
     keeps its proportions when it is locked to 100vh. */
  --fs-wordmark:     clamp(22px, 2.1vw, 40px);
  --fs-tagline:      clamp(10px, 0.85vw, 15px);
  --fs-nav:          clamp(13px, 1.05vw, 21px);
  --fs-meta:         clamp(11px, 0.95vw, 18px);
  --fs-card-title:   clamp(14px, 1.35vw, 26px);
  --fs-card-sub:     clamp(10px, 0.8vw, 15px);
  --fs-body:         clamp(14px, 1vw, 18px);

  /* Motion */
  --ease:            cubic-bezier(0.16, 1, 0.3, 1);
  --dur:             420ms;
  --dur-fast:        220ms;

  /* Card aspect ratio — matches the source thumbnails (402 x 299) */
  --card-ratio:      402 / 299;

  /* Vertical gap between case-study sections. */
  --section-gap:     clamp(48px, 11vw, 180px);

  /*
     Pattern2 is 3557 x 457, so at full-bleed its height is 100vw / 7.7834
     = 12.848vw. `.case-pattern--mode` pushes it down by 76% of that, which
     means it hangs 9.76vw BELOW its section's box. Any section following it
     has to add that back, or the visual gap collapses.
  */
  --pattern-overhang: 9.76vw;
}

/* --------------------------------------------------------------------------
   2. Reset
   -------------------------------------------------------------------------- */

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

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

body {
  background: var(--c-bg);
  color: var(--c-ink);
  font-family: "Poppins", "Helvetica Neue", Arial, sans-serif;
  font-size: var(--fs-body);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img { display: block; max-width: 100%; }

a {
  color: inherit;
  text-decoration: none;
}

ul { list-style: none; margin: 0; padding: 0; }

/* Visible focus for keyboard users, invisible for mouse users. */
:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--c-ink);
  outline-offset: 3px;
}

/* --------------------------------------------------------------------------
   3. Page shell
   -------------------------------------------------------------------------- */

.page {
  display: flex;
  flex-direction: column;
  padding: 0 var(--gutter);
}

/* The home screen is pinned to exactly one viewport height. */
.page--fixed {
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
}

/* Inner pages scroll normally. */
.page--flow {
  min-height: 100vh;
}

/* --------------------------------------------------------------------------
   4. Header
   -------------------------------------------------------------------------- */

.site-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 24px;
  padding-top: clamp(28px, 5.5vh, 84px);
  padding-bottom: clamp(24px, 5vh, 70px);
  flex: 0 0 auto;
}

.wordmark {
  display: block;
  line-height: 1;
}

.wordmark__name {
  display: block;
  font-family: "Black Han Sans", "Poppins", sans-serif;
  font-weight: 400;
  font-size: var(--fs-wordmark);
  letter-spacing: 0.01em;
  line-height: 1;
  margin: 0;
  text-transform: uppercase;
}

.wordmark__role {
  display: block;
  font-family: "Poppins", sans-serif;
  font-weight: 600;
  font-size: var(--fs-tagline);
  letter-spacing: 0.045em;
  text-transform: uppercase;
  margin-top: 0.28em;
  line-height: 1.2;
}

/* --- Nav ---------------------------------------------------------------- */

.nav {
  display: flex;
  align-items: center;
  gap: clamp(10px, 1vw, 20px);
  font-size: var(--fs-nav);
  letter-spacing: 0.03em;
  text-transform: uppercase;
  padding-top: 0.15em;
}

.nav__link {
  font-family: "Poppins", sans-serif;
  font-weight: 500;
  color: var(--c-ink);
  position: relative;
  padding-bottom: 2px;
  transition: opacity var(--dur-fast) var(--ease);
}

/* Underline that wipes in on hover. */
.nav__link::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1.5px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--dur-fast) var(--ease);
}

.nav__link:hover::after,
.nav__link:focus-visible::after { transform: scaleX(1); }

/* The page you are currently on. */
.nav__link[aria-current="page"] {
  font-family: "Poppins", sans-serif;
  font-weight: 700;
}

.nav__link[aria-current="page"]::after { display: none; }

/* Divider is the supplied Header_Divider.png (143 x 222), sized off the
   nav's font-size so it always tracks the type. */
.nav__sep {
  display: block;
  height: 1.2em;
  width: auto;
  user-select: none;
  pointer-events: none;
  /* Optical nudge so the slanted marks read as centred against the caps. */
  margin: 0 0.1em;
  transform: translateY(0.05em);
}

/* --------------------------------------------------------------------------
   5. Project grid (home)
   -------------------------------------------------------------------------- */

.work {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 1fr;
  gap: var(--grid-gap);
  flex: 1 1 auto;
  min-height: 0;
}

/* --- Card --------------------------------------------------------------- */

.card {
  position: relative;
  display: block;
  overflow: hidden;
  background: var(--c-placeholder);
  min-height: 0;
}

.card__media {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  transition: transform var(--dur) var(--ease);
  will-change: transform;
}

/* --- Hover banner ------------------------------------------------------- */
/* Grows vertically outward from a hairline at the card's centre. */

.card__banner {
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 50%;                 /* final height — matches BannerBlack.png ratio */
  transform: translateY(-50%) scaleY(0);
  transform-origin: center center;
  background: var(--c-banner);
  color: var(--c-banner-text);

  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 0 8%;

  transition: transform var(--dur) var(--ease);
  will-change: transform;
  pointer-events: none;
}

/* The 1px seed line that the banner appears to grow out of. */
.card__banner::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 1px;
  background: var(--c-banner);
  transform: translateY(-50%) scaleX(0);
  transform-origin: center center;
  transition: transform var(--dur-fast) var(--ease);
}

.card__title {
  font-family: "Black Han Sans", "Poppins", sans-serif;
  font-weight: 400;
  font-size: var(--fs-card-title);
  line-height: 1.1;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  margin: 0;
}

.card__sub {
  font-family: "Poppins", sans-serif;
  font-weight: 400;
  font-size: var(--fs-card-sub);
  line-height: 1.35;
  margin: 0.45em 0 0;
  max-width: 40ch;
  text-wrap: balance;
}

.card__title,
.card__sub {
  opacity: 0;
  transform: translateY(6px);
  transition: opacity var(--dur-fast) var(--ease),
              transform var(--dur-fast) var(--ease);
}

/* --- Hover / focus state ------------------------------------------------ */

.card:hover .card__banner,
.card:focus-visible .card__banner { transform: translateY(-50%) scaleY(1); }

.card:hover .card__banner::before,
.card:focus-visible .card__banner::before { transform: translateY(-50%) scaleX(1); }

.card:hover .card__title,
.card:hover .card__sub,
.card:focus-visible .card__title,
.card:focus-visible .card__sub {
  opacity: 1;
  transform: translateY(0);
  /* Text arrives just after the banner has opened. */
  transition-delay: 150ms;
}

/* Keep the artwork perfectly still — the banner is the whole hover gesture.
   Swap `none` for `scale(1.03)` here if you ever want a subtle push-in. */
.card:hover .card__media,
.card:focus-visible .card__media { transform: none; }

/* --------------------------------------------------------------------------
   6. Meta row (below the grid)
   -------------------------------------------------------------------------- */

.meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  flex: 0 0 auto;
  padding-top: clamp(8px, 1.4vh, 20px);
  font-size: var(--fs-meta);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-weight: 500;
}

.meta__right { text-transform: none; letter-spacing: 0.03em; }

/* --------------------------------------------------------------------------
   7. Footer — arc + socials
   -------------------------------------------------------------------------- */

/* The whole footer cluster (arc + icons) is scaled to 80% of its
   original size — see the values below. */
.site-footer {
  position: relative;
  flex: 0 0 auto;
  height: clamp(88px, 12.8vh, 184px);
  display: flex;
  align-items: center;
  justify-content: center;
}

/* The arc bleeds off the bottom of the viewport, so it is absolutely
   positioned and allowed to run past the footer box. */
.site-footer__arc {
  position: absolute;
  left: 50%;
  top: 0;
  transform: translateX(-50%);
  width: clamp(224px, 20vw, 400px);
  height: auto;
  pointer-events: none;
  user-select: none;
}

.socials {
  position: relative;
  display: flex;
  align-items: center;
  gap: clamp(6px, 0.72vw, 14px);
  /* Nudged down so the icons sit inside the arc as in the design. */
  margin-top: clamp(19px, 3.6vh, 50px);
}

.socials__link {
  display: block;
  width: clamp(27px, 2.56vw, 51px);
  height: clamp(27px, 2.56vw, 51px);
  transition: transform var(--dur-fast) var(--ease),
              opacity var(--dur-fast) var(--ease);
}

.socials__link img {
  width: 100%;
  height: 100%;
}

.socials__link:hover,
.socials__link:focus-visible {
  transform: translateY(-3px);
  opacity: 0.75;
}

/* --------------------------------------------------------------------------
   8. Inner pages (project + about) — placeholder shell
   -------------------------------------------------------------------------- */

.page-body {
  flex: 1 1 auto;
  padding-bottom: clamp(48px, 8vh, 120px);
}

.page-title {
  font-family: "Black Han Sans", "Poppins", sans-serif;
  font-weight: 400;
  font-size: clamp(32px, 5vw, 88px);
  line-height: 1.05;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  margin: 0 0 0.25em;
}

.page-lede {
  font-size: clamp(15px, 1.15vw, 22px);
  max-width: 60ch;
  color: var(--c-ink-muted);
  margin: 0 0 clamp(32px, 5vh, 64px);
}

/* Dashed box marking where the real design will go. */
.placeholder-block {
  border: 1.5px dashed var(--c-rule);
  background: #fafafa;
  min-height: clamp(220px, 42vh, 520px);
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 32px;
  color: var(--c-ink-muted);
  font-size: var(--fs-meta);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.back-link {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  font-size: var(--fs-meta);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: 500;
  margin-bottom: clamp(20px, 3vh, 40px);
}

.back-link:hover { opacity: 0.6; }

/* Footer on inner pages sits at the end of the flow, not pinned. */
.page--flow .site-footer {
  height: clamp(96px, 14.4vh, 192px);
  overflow: hidden;
}

/* --------------------------------------------------------------------------
   9. Black footer bar (all inner pages)
   -------------------------------------------------------------------------- */

.footer-bar {
  background: var(--c-ink);
  color: #fff;
  /* Full-bleed: escapes the page gutters. */
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  padding: clamp(22px, 3.2vw, 46px) var(--gutter);

  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex: 0 0 auto;
}

.footer-bar__name {
  font-family: "Black Han Sans", "Poppins", sans-serif;
  font-weight: 400;
  font-size: clamp(15px, 1.5vw, 30px);
  letter-spacing: 0.01em;
  text-transform: uppercase;
  line-height: 1;
}

/* Light icon set, sized to match the arc footer's icons. */
.footer-bar .socials { margin-top: 0; }

.footer-bar .socials__link {
  width: clamp(24px, 2.2vw, 44px);
  height: clamp(24px, 2.2vw, 44px);
}

/* --------------------------------------------------------------------------
   10. Project case-study pages
   -------------------------------------------------------------------------- */

/* Patterns bleed past the gutters, so clip sideways overflow at page level.
   `clip` (unlike `hidden`) does not create a scroll container. */
.page--case {
  overflow-x: hidden;
  overflow-x: clip;
}

.case-body {
  flex: 1 1 auto;
  padding-bottom: clamp(56px, 9vh, 130px);
}

/* --- Grid break ---------------------------------------------------------
   Pulls an element out of the primary column and onto the secondary margin.
   Applied to a grid container, its columns widen with it.
   ------------------------------------------------------------------------ */

.break {
  margin-left: calc(var(--gutter-2) - var(--gutter));
  margin-right: calc(var(--gutter-2) - var(--gutter));
}

/* --- Title block -------------------------------------------------------- */

/* The title block sits tight to the first row of imagery. */
.case-head {
  padding-bottom: 0px;
}

/* Case pages give the wordmark more air before the big title. */
.page--case .site-header {
  padding-bottom: clamp(34px, 6vw, 100px);
}

.case-title {
  font-family: "Black Han Sans", "Poppins", sans-serif;
  font-weight: 400;
  font-size: clamp(30px, 5.4vw, 104px);
  line-height: 1;
  letter-spacing: 0.005em;
  text-transform: uppercase;
  margin: 0;
}

.case-roles {
  font-weight: 600;
  font-size: var(--fs-meta);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  margin: 1.4em 0 0;
}

/* Deliberately long measure — matches the design, which runs the intro
   nearly the full content width at a small size. */
.case-lede {
  font-weight: 300;
  font-size: clamp(13px, 0.95vw, 18px);
  line-height: 1.6;
  max-width: 84%;
  margin: 1.7em 0 0;
}

/* A second intro paragraph sits closer to the first than to the roles line. */
.case-lede--tight { margin-top: 1.1em; }

.case-collage img { width: 100%; height: auto; }

/* --- Section ------------------------------------------------------------ */

.case-section {
  position: relative;
  padding-top: var(--section-gap);
}

/* Follows a section whose pattern overhangs — see `--pattern-overhang`. */
.case-section--after-pattern {
  padding-top: calc(var(--section-gap) + var(--pattern-overhang));
}

/* The first section follows the title block directly, so it sits close. */
.case-section--first {
  padding-top: clamp(16px, 2.2vw, 38px);
}

/*
   Quarter gap. For sections following artwork that carries its drop shadow
   inside its own box (the Mystery Labs hero): the shadow reads as space, so
   a full `--section-gap` on top of it opens a hole. Ivona set this by eye
   over two passes — 1/2 was still too loose, 1/4 is the one she signed off.
   ~40px at 1440, and it still scales with the gap token.
*/
.case-section--tight {
  padding-top: calc(var(--section-gap) / 4);
}

/* The narrow label + paragraph column that heads each section. */
.case-section__head {
  max-width: 50%;
  margin-bottom: clamp(16px, 1.8vw, 32px);
}

.case-section__label {
  font-weight: 800;
  font-size: clamp(14px, 1.25vw, 18px);
  letter-spacing: 0;
  text-transform: uppercase;
  margin: 0;
}

.case-section__text {
  font-weight: 300;
  font-size: clamp(13px, 0.95vw, 17px);
  line-height: 1.6;
  margin: 0;
}

/* --- Shadow-compensated image box --------------------------------------- */
/*
   The screen exports carry a baked-in drop shadow, so the PNG canvas is
   larger than the artwork. `.bleed` sizes and offsets the <img> so the box
   wraps the ARTWORK exactly and the shadow spills outside it — which means
   these line up on a grid like any other element. The per-asset numbers are
   derived from each PNG's opaque bounding box; see section 10b below.
*/
/*
   `flow-root`, not `block`. The correction below gives the <img> negative
   top/bottom margins; against a plain block parent those COLLAPSE THROUGH it,
   so the box keeps the canvas height and the escaped margin drags the next
   sibling up. Grid items are already BFC roots, which is why this only bit
   when a `.bleed` sat directly in a section. `flow-root` makes every instance
   behave the same: box hugs the artwork, nothing leaks out.
*/
.bleed {
  position: relative;
  display: flow-root;
}

.bleed > img {
  display: block;
  width: 100%;
  height: auto;
  /* Must override the global `img { max-width: 100% }`, or the computed
     width below (always >100%) gets clamped and the correction is lost. */
  max-width: none;
}

/* --- Layout rows -------------------------------------------------------- */

.case-grid {
  display: grid;
  gap: clamp(14px, 2vw, 40px);
}

.case-grid--2 { grid-template-columns: repeat(2, 1fr); }

/* Vertical spacing between stacked rows inside one section.
   `.break` also sets margin-left/right, so these only touch margin-top. */
.case-grid--stack    { margin-top: clamp(14px, 2vw, 40px); }
.case-grid--stack-sm { margin-top: clamp(12px, 1.6vw, 30px); }
.case-grid--stack-lg { margin-top: clamp(34px, 5.5vw, 96px); }

/* Battle Pass: the screen runs wider than the other screenshots on purpose,
   with the reward column narrow beside it. */
.case-grid--bp {
  grid-template-columns: minmax(0, 3.2fr) minmax(0, 1fr);
  align-items: stretch;
}

/* Badge + coins bottom-align with the screen, forming one tight block. */
.bp-side {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: clamp(12px, 2vw, 40px);
}

.bp-side__entry { width: 100%; }

.bp-side__coins {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(4px, 0.7vw, 14px);
  width: 100%;
}

/* Game mode: screen takes most of the row, ingredients top-aligned beside it. */
.case-grid--mode {
  grid-template-columns: minmax(0, 1.9fr) minmax(0, 1fr);
  align-items: start;
}

/* --- Icon grids --------------------------------------------------------- */
/*
   Icon PNGs keep their own shadow padding — they read as loose objects
   with consistent optical spacing that way, so they are NOT bleed-corrected.
*/
.icon-grid {
  display: grid;
  gap: clamp(2px, 0.5vw, 12px);
}

/* A compact cluster, centred under the image and note above it. */
.icon-grid--dojo {
  grid-template-columns: repeat(3, 1fr);
  max-width: 56%;
  margin-inline: auto;
}

/* 12 ingredients, so this lands as a clean 4 x 3. */
.icon-grid--mode { grid-template-columns: repeat(4, 1fr); }

.icon-grid img {
  width: 100%;
  height: auto;
  transition: transform var(--dur-fast) var(--ease);
}

.icon-grid img:hover { transform: translateY(-4px) scale(1.04); }

/* --- Dojo sub-blocks ---------------------------------------------------- */

/* Spacing below the note is owned by the icon row's own top margin. */
.dojo-note {
  margin-bottom: 0;
}

.dojo-note__title {
  font-weight: 600;
  font-size: var(--fs-meta);
  letter-spacing: 0;
  text-transform: uppercase;
  margin: 0;
}

.dojo-note__text {
  font-weight: 300;
  font-size: clamp(12px, 0.95vw, 14px);
  line-height: 1.6;
  margin: 0;
  max-width: 74%;
}

/* --- Full-bleed decorative pattern --------------------------------------- */
/*
   Sits behind the imagery of its section. Anchored to the bottom of the
   block and pushed down so part of it emerges below the artwork.
*/
.case-pattern {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 100vw;
  /* Same reason as `.bleed > img` — the global cap would hold it to the
     content column instead of letting it run the full viewport. */
  max-width: none;
  height: auto;
  z-index: 0;
  pointer-events: none;
  user-select: none;
}

.case-pattern--hero { transform: translate(-50%, 34%); }

/* Clips only the bottom slice of the game-mode screen, and stays clear of
   the ingredient grid, which ends higher than the screen does. */
.case-pattern--mode { transform: translate(-50%, 76%); }

/* Content must sit above the pattern. */
.case-section > *:not(.case-pattern) {
  position: relative;
  z-index: 1;
}

/* --- Logo explorations --------------------------------------------------- */

.case-logo img { width: 100%; height: auto; }

/* --- See more ------------------------------------------------------------ */

.see-more {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--grid-gap);
}

/* --------------------------------------------------------------------------
   10b. Per-asset bleed corrections (auto-derived from each PNG's opaque box)
   -------------------------------------------------------------------------- */

/* canvas 2146x1344 · artwork 1886x867 */
.bleed--homescreen > img {
  width: 113.7858%;
  margin-left: -6.8929%;
  margin-top: -1.2725%;
  margin-bottom: -24.0191%;
}

/* canvas 2137x1340 · artwork 1876x863 */
.bleed--ninjas > img {
  width: 113.9126%;
  margin-left: -6.9296%;
  margin-top: -1.2793%;
  margin-bottom: -24.1471%;
}

/* canvas 2156x1349 · artwork 1895x872 */
.bleed--blades > img {
  width: 113.7731%;
  margin-left: -6.8602%;
  margin-top: -1.2665%;
  margin-bottom: -23.9050%;
}

/* canvas 2159x1350 · artwork 1899x873 */
.bleed--newblade > img {
  width: 113.6914%;
  margin-left: -6.8457%;
  margin-top: -1.2638%;
  margin-bottom: -23.8547%;
}

/* canvas 2141x1341 · artwork 1881x865 */
.bleed--hoshiui > img,
.bleed--kenui > img {
  width: 113.8224%;
  margin-left: -6.9112%;
  margin-top: -1.2759%;
  margin-bottom: -24.0298%;
}

/* canvas 2034x1234 · artwork 1815x835 */
.bleed--gamemodeui > img {
  width: 112.0661%;
  margin-left: -6.0055%;
  margin-top: -1.1019%;
  margin-bottom: -20.8815%;
}

/* canvas 2088x1237 · artwork 1885x867 */
.bleed--battlepass > img {
  width: 110.7692%;
  margin-left: -5.3581%;
  margin-top: -1.0080%;
  margin-bottom: -18.6207%;
}

/* canvas 899x603 · artwork 826x534 — so the tiles read as full column width */
.bleed--tileset > img {
  width: 108.8378%;
  margin-left: -4.2373%;
  margin-top: -3.7530%;
  margin-bottom: -4.6005%;
}

/* Alpha Punch phones. canvas 798x1656 · artwork 576x1251 (ratio 0.4604) */
.bleed--apphone > img {
  width: 138.5417%;
  margin-left: -19.2708%;
  margin-top: -3.4722%;
  margin-bottom: -66.8403%;
}

/* Alpha Punch result screens. canvas 798x1659 · artwork 577x1256 (0.4594) */
.bleed--apscreen > img {
  width: 138.3016%;
  margin-left: -19.0641%;
  margin-top: -3.4662%;
  margin-bottom: -66.3778%;
}

/* Flora. Every screen's artwork is ~463x1005 (ratio 0.461); only the
   canvas padding around it differs, so they share a ratio but not a class. */

/* canvas 621x1294 · artwork 463x1005 */
.bleed--flphone > img {
  width: 134.1253%;
  margin-left: -17.0626%;
  margin-top: -3.0238%;
  margin-bottom: -59.3952%;
}

/* canvas 548x1159 · artwork 463x1004 — the tighter of the two canvases.
   Shop.png was re-exported on this canvas (2026-08-16) while the rest of
   the first row stayed on the 621x1293 one, so it needs these numbers,
   not `--flphone`'s. Wrong class here reads as the screen floating higher
   than its neighbours, since the correction pulls up by a percentage of a
   canvas that no longer matches. */
.bleed--flplantina1 > img,
.bleed--flshop > img {
  width: 118.3585%;
  margin-left: -9.0713%;
  margin-top: -1.7279%;
  margin-bottom: -31.7495%;
}

/* canvas 582x1219 · artwork 466x1007 */
.bleed--flherbarium > img {
  width: 124.8927%;
  margin-left: -12.4464%;
  margin-top: -2.3605%;
  margin-bottom: -43.1330%;
}

/* Composite row. canvas 1412x1058 · artwork 1369x907 (ratio 1.509) */
.bleed--flnurseryshop > img {
  width: 103.1410%;
  margin-left: -2.7757%;
  margin-top: -1.3148%;
  margin-bottom: -9.7151%;
}

/* --------------------------------------------------------------------------
   10c. Super Krishna Crush
   -------------------------------------------------------------------------- */

/* --- Hero: splash art with game blocks scattered around it --------------- */

.krishna-hero { position: relative; }

/* The splash is the positioning context for the scattered blocks, so their
   offsets stay locked to the artwork at every viewport width. */
.splash {
  position: relative;
  width: 52%;
  margin-inline: auto;
}

/* Class-scoped, not `.splash > img` — the scattered blocks are also direct
   children and a type selector would out-specify their own width. */
.splash__art { width: 100%; height: auto; }

/*
   Scattered game blocks. These follow NO margin — they are placed relative
   to the splash art and deliberately spill past both the primary and the
   secondary columns.
*/
.hero-tile {
  position: absolute;
  width: 14%;
  height: auto;
  z-index: 1;
  pointer-events: none;
  user-select: none;
}

.hero-tile--a { left: -54%; top:  8.5%; transform: rotate(-14deg); width: 12%; }
.hero-tile--b { left: -47%; top: 74.5%; transform: rotate(11deg);  width: 10.5%; }
.hero-tile--c { left: 131%; top: 39.6%; transform: rotate(-8deg);  width: 13%; }

/* App icons sit under the splash, matching its width. */
.app-icons {
  width: 52%;
  margin: clamp(14px, 2vw, 40px) auto 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(6px, 1vw, 20px);
}

.app-icons img { width: 100%; height: auto; }

/* --- Board & tile explorations ------------------------------------------ */
/*
   Column widths are set so the tall Level1 board and the two stacked boards
   beside it finish at roughly the same height.
*/
/* 1.495 : 1 solves Level1's 0.720 ratio against the two stacked ~1.0 boards
   (788x1094 vs 540x543 + 532x531 + gap), so both columns finish level. */
.case-grid--board {
  grid-template-columns: minmax(0, 1.495fr) minmax(0, 1fr) minmax(0, 1.495fr);
  align-items: start;
}

.board-stack {
  display: grid;
  gap: clamp(10px, 1.4vw, 28px);
}

.board-stack img,
.case-grid--board > img { width: 100%; height: auto; }

/*
   Full column height, so the tile cluster can sit in the middle of the free
   space while the label and paragraph pin to the bottom — level with the
   bottom edge of the second board.
*/
.board-aside {
  align-self: stretch;
  display: grid;
  grid-template-rows: 1fr auto;
}

.board-aside__tiles {
  align-self: center;
  width: 100%;
}

/* --- Equal-column rows --------------------------------------------------- */

.case-grid--3 { grid-template-columns: repeat(3, 1fr); }
.case-grid--4 { grid-template-columns: repeat(4, 1fr); }

.case-grid--3 > img,
.case-grid--4 > img { width: 100%; height: auto; }

/* --- Icon / power-up strips ---------------------------------------------- */
/*
   Each strip is one flat image of several icons. Giving the columns `fr`
   values equal to each image's aspect ratio makes every icon render at the
   same size across the row, whatever the strips' native proportions.
*/
.strip-row {
  display: grid;
  align-items: center;
  gap: clamp(12px, 2vw, 44px);
}

.strip-row + .strip-row { margin-top: clamp(14px, 2vw, 40px); }

.strip-row img { width: 100%; height: auto; }

/* --- UI screens ---------------------------------------------------------- */
/*
   The eight phone exports share an identical artwork height (910px) but vary
   in width by up to 2%. Sizing them by WIDTH would leave the row's bottoms
   ragged, so they are sized by HEIGHT instead and centred in their cell —
   tops and bottoms line up exactly and the 2% width difference is invisible.
   The `top`/`height` percentages resolve against the box's height, which the
   aspect-ratio below makes definite.
*/
.ui-shot {
  position: relative;
  aspect-ratio: 514 / 910;
}

.ui-shot > img {
  position: absolute;
  left: 50%;
  top: -2.0879%;      /* -19 / 910  — trims the transparent cap above */
  height: 141.0%;     /* 1283 / 910 — canvas height over artwork height */
  width: auto;
  max-width: none;
  transform: translateX(-50%);
}

/* --------------------------------------------------------------------------
   10d. Alpha Punch
   -------------------------------------------------------------------------- */

/* --- Screen rows --------------------------------------------------------- */

/* Three phones plus the button panel. 1.526 is the phone artwork ratio
   (0.4594) over the panel's (0.7010), so all four finish the same height. */
.case-grid--ap-screens {
  grid-template-columns: repeat(3, minmax(0, 1fr)) minmax(0, 1.526fr);
  align-items: start;
}

.case-grid--ap-screens > img { width: 100%; height: auto; }

/* --- Achievement icons --------------------------------------------------- */
/*
   Exported on a shared bounding box so they align, but the artwork inside
   each box varies — so the boxes are deliberately overlapped to close the
   optical gaps. Eyeballed, per the brief.
*/
.ap-icons {
  display: flex;
  justify-content: center;
  align-items: center;
}

.ap-icons img {
  width: 26%;
  height: auto;
  margin-inline: -4.4%;
}

/* --- Emoji stickers ------------------------------------------------------ */

.ap-emoji {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: clamp(10px, 1.6vw, 34px);
}

.ap-emoji img { width: 20%; height: auto; }

/* --- Animation mosaic ----------------------------------------------------- */
/*
   Tightly packed, and every cell's aspect-ratio comes from its source video,
   so the column `fr` values and the ratios agree and the rows sit flush with
   no rounding drift. Videos fill their cell; `cover` absorbs the fraction of
   a percent the stack column can differ by as the gap scales.
*/
.anim-mosaic {
  --anim-gap: clamp(5px, 1vw, 15px);
  display: grid;
  gap: var(--anim-gap);
}

.anim-row {
  display: grid;
  gap: var(--anim-gap);
}

/* Two gameplay captures, both 1372x888. */
.anim-row--pair { grid-template-columns: repeat(2, minmax(0, 1fr)); }

/* The closing clip sits centred and well short of the mosaic's full width. */
.anim-row--solo {
  grid-template-columns: minmax(0, 61%);
  justify-content: center;
}

/* streak (1:1) · leaderboard (1:1) · keypad (1.300) · button stack (1.276) */
.anim-row--icons {
  grid-template-columns:
    minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1.300fr) minmax(0, 1.276fr);
}

/*
   Two stacked buttons dividing the row height in proportion to their own
   aspect ratios: 1/3.193 and 1/2.317.

   These MUST sum to at least 1. Per the grid spec, when flex factors add up
   to less than 1 they are treated as a fraction of the leftover space
   instead of being normalised — so `0.3132fr 0.4316fr` would leave 26% of
   the row empty. Scaled by 10 they behave as intended.
*/
.anim-stack {
  display: grid;
  gap: var(--anim-gap);
  grid-template-rows: 3.132fr 4.316fr;
}

.anim-cell {
  position: relative;
  overflow: hidden;
  background: #cfd8c5;   /* matches the clips' green while they load */
}

.anim-cell video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Aspect ratios taken straight from each source file. */
.anim-cell--play  { aspect-ratio: 1372 / 888; }
.anim-cell--sq    { aspect-ratio: 1 / 1; }
.anim-cell--pad   { aspect-ratio: 1000 / 770; }
.anim-cell--wide  { aspect-ratio: 1720 / 490; }
.anim-stack .anim-cell { aspect-ratio: auto; }

/* --------------------------------------------------------------------------
   10e. Flora
   -------------------------------------------------------------------------- */

/*
   Centred cluster of loose artwork resting on a shared baseline. Column
   `fr` values are each image's natural width, so everything renders at one
   consistent scale and the relative sizes stay true to the source files.
*/
.plant-row {
  display: grid;
  align-items: end;
  gap: clamp(10px, 1.6vw, 34px);
  margin-inline: auto;
}

.plant-row img { width: 100%; height: auto; }

.plant-row--trio {
  grid-template-columns: 641fr 539fr 568fr;
  max-width: 65%;
}

/* Centred strips of loose objects. */
.flora-strip {
  display: block;
  height: auto;
  margin-inline: auto;
}

.flora-strip--pots { width: 66%; }

/* Sits well clear of the composite above it — more air than a normal
   stacked row, though still short of a full section break. */
.flora-strip--buttons {
  width: 64%;
  margin-top: clamp(52px, 9.5vw, 160px);
}

/* --- "I can paint" row --------------------------------------------------- */
/*
   This row is a single composite export (screens, pot studies, plant cards
   and seedlings all in one file), so it needs no grid — just the bleed
   correction in §10b. The individual assets it replaced are still in
   images/flora/ and unused.
*/

/* --------------------------------------------------------------------------
   10f. Mystery Labs
   -------------------------------------------------------------------------- */

/*
   Both the hero and the gameplay row arrived as single flat composites, so
   neither needs a grid. They are NOT bleed-corrected either: the canvas
   padding on these two is the drop shadow, and pulling the box in to hug the
   artwork would drag the next section up by ~17% of the column width. Here
   the shadow is meant to sit inside the box and let `--section-gap` do the
   spacing, which is how the design reads.

   The wrapper is not decoration: `.break` only widens an element whose width
   is auto, and a replaced element's auto width is its intrinsic width. Put
   `.break` straight on the <img> and it shifts left without ever growing.
*/
.ml-composite > img {
  display: block;
  width: 100%;
  height: auto;
}

/* --- Animation mosaic ---------------------------------------------------- */
/*
   4 x 4 grid of unit squares:

     +-----------+-----------+     A  ChestOpenGold      2x2
     |           |           |     B  ChestBounce_Square 2x2
     |     A     |     B     |     C  ChestBounceOpen    2x2
     |           |           |     1  GemFrenzy          1x1
     +-----+-----+-----------+     2  Gem_04             1x1
     |  1  |  2  |           |     3  Gem_05             1x1
     +-----+-----+     C     |     4  CoinRotation       1x1
     |  3  |  4  |           |
     +-----+-----+-----------+

   Every source clip is square (the three Gem_* files are 1114x1110, which
   rounds to square within a third of a percent), so one `aspect-ratio: 1`
   on the cell makes the rows land flush without any tuning: a 2x2 cell is
   2u + gap wide and therefore 2u + gap tall, which is exactly two stacked
   single cells. Ivona's exports are used as delivered — no re-encoding.
*/
.ml-mosaic {
  --ml-gap: clamp(5px, 1.05vw, 18px);
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--ml-gap);
}

.ml-cell {
  position: relative;
  overflow: hidden;
  aspect-ratio: 1 / 1;
  background: #000;   /* the clips are on black, so gaps never flash white */
}

.ml-cell video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Explicit placement, so the source order stays readable and the mobile
   reflow below can simply drop it. */
.ml-cell--big { grid-column: span 2; grid-row: span 2; }

.ml-cell--a { grid-area: 1 / 1 / 3 / 3; }
.ml-cell--b { grid-area: 1 / 3 / 3 / 5; }
.ml-cell--c { grid-area: 3 / 3 / 5 / 5; }

.ml-cell--s1 { grid-area: 3 / 1; }
.ml-cell--s2 { grid-area: 3 / 2; }
.ml-cell--s3 { grid-area: 4 / 1; }
.ml-cell--s4 { grid-area: 4 / 2; }

/* --------------------------------------------------------------------------
   10g. Illustrations
   -------------------------------------------------------------------------- */

/*
   Both grids are single flat composite exports — Ivona laid out the grid
   herself in the PNG, so, like Mystery Labs (§10f), they need no grid
   markup here, just the wrapper-div `.break` treatment: `.break` only
   widens an element whose width is auto, and a replaced element's auto
   width is its intrinsic width, so it has to go on the wrapper, not the
   <img> directly. No bleed correction either — these exports carry no
   baked-in drop shadow to compensate for; the canvas IS the artwork.
*/
.ill-composite > img {
  display: block;
  width: 100%;
  height: auto;
}

/* --------------------------------------------------------------------------
   10h. About page
   --------------------------------------------------------------------------

   Aims for one viewport height like the home screen, but it is NOT locked
   to it: the copy is long, so on a short window the page scrolls rather
   than squashing the type to nothing.

   Margins: the photo is the NO-MARGIN case (bleeds across the primary
   gutter to the viewport edge); the text column finishes on primary,
   aligned with the nav. The two blocks stretch to the same height.
   -------------------------------------------------------------------------- */

.page--about {
  min-height: 100vh;
  min-height: 100dvh;
  overflow-x: hidden;
  overflow-x: clip;
}

.about {
  /* Full visual width of the photo, the gutter it bleeds across included. */
  --photo-w: min(33.5vw, 640px);

  flex: 0 1 auto;
  display: grid;
  /* The photo's own grid column is short by the gutter it bleeds across. */
  grid-template-columns: calc(var(--photo-w) - var(--gutter)) minmax(0, 1fr);
  column-gap: clamp(16px, 2.5vw, 48px);
  /* Default `stretch` is deliberate: the photo takes its height from the
     text column, so the two blocks finish level. */
  align-items: stretch;
  /* Keeps the footer arc off the last line when the copy runs long enough
     that there is no slack left for `margin-top: auto` to eat. */
  padding-bottom: clamp(20px, 2.4vh, 44px);
}

.about__photo {
  margin-left: calc(-1 * var(--gutter));
  min-height: clamp(240px, 42vh, 900px);
  background: var(--c-placeholder);
  overflow: hidden;
}

/* Cropping happens here, not in the export: the box height follows the
   length of the copy, so it cannot be a fixed ratio. */
.about__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 52% 32%;
}

/*
   ONE SCALE KNOB. Everything in the text column is sized in `em` off this
   font-size, so the whole column scales as a unit.

   It reads viewport HEIGHT as well as width (1.42vh is the design's own
   width-to-height ratio) because a 16:9 window is far wider than the
   mockup's proportions — sizing on `vw` alone there would blow the copy
   past 100vh. `container-type` lets the headline size itself off this
   column's width rather than the viewport, which matters above 1900px
   where the gutter widens and the column stops tracking `vw`.
*/
.about__col {
  container-type: inline-size;
  font-size: clamp(13px, min(1vw, 1.42vh), 19px);
  line-height: 1.5;
}

.about__title {
  font-family: "Black Han Sans", "Poppins", sans-serif;
  font-weight: 400;
  /* 9.63cqw is solved so the line just fills the text column, as designed. */
  font-size: clamp(34px, 9.63cqw, 120px);
  line-height: 1;
  letter-spacing: 0.005em;
  margin: 0 0 0.42em;
}

.about__intro {
  margin: 0 0 1.1em;
}

.about__label {
  font-family: "Poppins", sans-serif;
  font-weight: 700;
  font-size: 1.2em;
  letter-spacing: 0;
  text-transform: uppercase;
  line-height: 1.3;
  margin: 1.1em 0 0.85em;
}

.jobs {
  display: flex;
  flex-direction: column;
  gap: 0.85em;
}

.job__head,
.job__text {
  margin: 0;
  line-height: 1.42;
}

.job__head  { font-size: 1.12em; font-weight: 400; }
.job__co    { font-weight: 700; }
.job__text  { font-size: 0.98em; }

/* Arc footer, identical to the home screen. `margin-top: auto` takes up the
   slack when the copy leaves room; `hidden` lets the arc bleed off the
   bottom the same way `.page--fixed` clips it on the home screen. */
.page--about .site-footer {
  margin-top: auto;
  overflow: hidden;
}

/* --------------------------------------------------------------------------
   11. Responsive
   -------------------------------------------------------------------------- */

/* Very large screens: more air on the sides, but cap the content width. */
@media (min-width: 1900px) {
  :root { --gutter: clamp(180px, 11vw, 320px); }
}

/* Short-and-wide laptops: trim the vertical padding so 100vh still fits. */
@media (max-height: 800px) {
  .site-header {
    padding-top: clamp(20px, 4vh, 40px);
    padding-bottom: clamp(16px, 3.5vh, 36px);
  }
  .socials { margin-top: clamp(13px, 2.4vh, 27px); }
}

/* Tablet — 2 columns, release the 100vh lock. */
@media (max-width: 900px) {
  .page--fixed {
    height: auto;
    min-height: 100vh;
    overflow: visible;
    padding-bottom: 0;
  }
  .grid {
    grid-template-columns: repeat(2, 1fr);
    grid-auto-rows: auto;
  }
  .card { aspect-ratio: var(--card-ratio); }
  .card__banner { height: 55%; }

  /* Case study: the side-by-side rows stack. */
  .case-grid--bp,
  .case-grid--mode { grid-template-columns: 1fr; }

  .bp-side { flex-direction: row; justify-content: center; }
  .bp-side__entry  { width: 42%; align-self: center; }
  .bp-side__coins  { width: 48%; }

  /* Ingredients stay 4 x 3 at every width. */

  /* Narrow measures were tuned for a wide column — relax them here. */
  .case-section__head { max-width: 62%; }
  .case-lede          { max-width: 100%; }
  .icon-grid--dojo    { max-width: 74%; }

  /* Krishna: give the splash more room, board row becomes two rows. */
  .splash, .app-icons { width: 66%; }
  .case-grid--board   { grid-template-columns: minmax(0, 1.33fr) minmax(0, 1fr); }
  .board-aside        { grid-column: 1 / -1; }
  .board-aside__tiles { width: 46%; }
  .case-grid--4       { grid-template-columns: repeat(3, 1fr); }

  /* Flora */
  .plant-row--trio    { max-width: 82%; }

  /* Alpha Punch: button panel drops below the three phones. */
  .case-grid--ap-screens { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .case-grid--ap-screens > img:last-child {
    grid-column: 2;
    align-self: center;
  }

  /* About: the two columns stack. The photo keeps its left bleed and its
     native proportions rather than going full width, which would force a
     hard crop on a portrait source. The headline needs no rule here — it
     sizes off its column via `cqw`. */
  .about {
    grid-template-columns: minmax(0, 1fr);
    row-gap: clamp(22px, 4vw, 44px);
  }
  .about__photo {
    width: min(64vw, 460px);
    aspect-ratio: 746 / 968;
    min-height: 0;
  }
}

/* Phone — single column stack. */
@media (max-width: 600px) {
  :root { --gutter: 20px; }

  .site-header {
    flex-direction: column;
    align-items: flex-start;
    gap: 18px;
    padding-top: 28px;
    padding-bottom: 28px;
  }
  .nav { padding-top: 0; }

  .grid { grid-template-columns: 1fr; }

  .meta { flex-direction: column; align-items: flex-start; gap: 4px; }

  /* Case study on phones. */
  .case-grid--2   { grid-template-columns: 1fr; }
  .see-more       { grid-template-columns: 1fr; }
  .bp-side        { flex-direction: column; }
  .bp-side__entry { width: 66%; }
  .bp-side__coins { width: 82%; }

  .case-section__head,
  .dojo-note__text,
  .icon-grid--dojo { max-width: 100%; }

  /* Krishna on phones. */
  .splash, .app-icons { width: 100%; }
  .hero-tile          { display: none; }  /* no room to scatter */
  .case-grid--board   { grid-template-columns: 1fr; }
  .board-stack        { grid-template-columns: repeat(2, 1fr); }
  .board-aside__tiles { width: 70%; }
  .case-grid--3       { grid-template-columns: 1fr; }
  .case-grid--4       { grid-template-columns: repeat(2, 1fr); }
  .strip-row          { grid-template-columns: 1fr !important; }

  /* Flora on phones. */
  .plant-row--trio      { max-width: 100%; }
  .flora-strip--pots,
  .flora-strip--buttons { width: 100%; }

  /* Alpha Punch on phones. */
  .case-grid--ap-screens { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .case-grid--ap-screens > img:last-child { grid-column: auto; }
  .ap-icons              { flex-wrap: wrap; }
  .ap-icons img          { width: 38%; margin-inline: -3%; }
  .ap-emoji img          { width: 30%; }
  /*
     The mosaic re-flows: gameplay clips full width, the two square icons
     side by side, then the keypad and the button stack full width each.
  */
  .anim-row--pair        { grid-template-columns: minmax(0, 1fr); }
  .anim-row--icons       { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .anim-row--icons > .anim-cell--pad { grid-column: 1 / -1; }
  .anim-stack            { grid-column: 1 / -1; }
  /* The closing clip's 61% inset leaves it too small to read here. */
  .anim-row--solo        { grid-template-columns: minmax(0, 1fr); }

  /*
     Mystery Labs mosaic on phones: drop the explicit placement and let it
     flow into two columns. Source order gives chest, chest, the four gems
     two-up, then the last chest — the single cells stay legible instead of
     shrinking to a quarter of a narrow column.
  */
  .ml-mosaic { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ml-cell--a,
  .ml-cell--b,
  .ml-cell--c   { grid-area: auto; grid-column: span 2; grid-row: auto; }
  .ml-cell--s1,
  .ml-cell--s2,
  .ml-cell--s3,
  .ml-cell--s4  { grid-area: auto; }

  .footer-bar {
    flex-direction: column;
    align-items: flex-start;
    gap: 18px;
  }

  /* About on phones: full-bleed both sides. 4:5 is close enough to the
     photo's own 746:968 that `cover` barely crops it. */
  .about__photo {
    width: calc(100% + var(--gutter) * 2);
    margin-right: calc(-1 * var(--gutter));
    aspect-ratio: 4 / 5;
  }
}

/* On touch/no-hover devices, reveal the label without requiring hover. */
@media (hover: none) {
  .card__banner {
    transform: translateY(-50%) scaleY(1);
    background: linear-gradient(to bottom,
                rgba(0, 0, 0, 0) 0%,
                rgba(0, 0, 0, 0.82) 45%);
    height: 60%;
    top: auto;
    bottom: 0;
    justify-content: flex-end;
    padding-bottom: 6%;
  }
  .card__banner::before { display: none; }
  .card__title,
  .card__sub { opacity: 1; transform: none; }
}

/* Respect reduced-motion preferences. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
  .card:hover .card__media { transform: none; }
}
