/* ===========================================================================
   YSB Designs — portfolio layout
   Two states: A (index, split + fixed desk render) and B (case study).
   Spec: CLAUDE.md. Tokens are verbatim from assets/styles.css.
   =========================================================================== */

:root{
  --bg:#093526; --bg-deep:#062219; --panel:#0E4433; --panel-2:#0B3A2A;
  --text:#F5EAE3; --muted:rgba(245,234,227,.64);
  --gold:#E8B44A; --gold-soft:#F3D28A; --wa:#25D366; --wa-deep:#1DA851;
  --line:rgba(245,234,227,.14); --radius:16px; --maxw:1080px;

  /* new for this layout */
  --radius-lg:28px;
  --pill:rgba(245,234,227,.07);
  --scrim:#041A13;

  --display:"Bricolage Grotesque","Instrument Sans",system-ui,sans-serif;
  --body:"Instrument Sans",system-ui,-apple-system,sans-serif;
  --mono:"JetBrains Mono",ui-monospace,monospace;

  --ease:cubic-bezier(.2,.8,.2,1);
  --swap-out:100ms;              /* screenshot fades to a blank screen */
  --swap-in:100ms;               /* then the next one fades up */
}

*{box-sizing:border-box;margin:0;padding:0}
html{-webkit-text-size-adjust:100%}
body{
  background:var(--bg);color:var(--text);
  font-family:var(--body);font-size:17px;line-height:1.6;
  -webkit-font-smoothing:antialiased;overflow-x:hidden;
}
a{color:inherit;text-decoration:none}
img{display:block;max-width:100%}
:focus-visible{outline:2px solid var(--gold);outline-offset:3px;border-radius:6px}

.mono{font-family:var(--mono);font-size:12.5px;letter-spacing:.16em;text-transform:uppercase}
.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

/* ===========================================================================
   STATE A — index
   =========================================================================== */

.index{
  min-height:100svh;
  display:grid;
  grid-template-columns:40vw 60vw;
  align-items:stretch;
}

/* --- left rail: identity + project list --------------------------------- */

/* The identity block does not move between State A and A2, so the rail is
   anchored from the top rather than centred — everything to the right of it
   is what changes. */
.rail{
  padding:24vh 4vw 6vh 10vw;
  display:flex;flex-direction:column;align-items:flex-start;gap:7vh;
  min-width:0;
}

.identity h1{
  font-family:var(--display);font-weight:800;
  font-size:clamp(1.7rem,2.4vw,2.05rem);
  line-height:1.05;letter-spacing:-.02em;
}
/* No Instrument Sans on the index — the positioning statement stays in the
   display face, recessed by colour rather than by scale (CLAUDE.md §3.3). */
.positioning{
  font-family:var(--display);font-weight:500;
  font-size:clamp(1rem,1.35vw,1.14rem);
  line-height:1.35;letter-spacing:-.01em;
  color:var(--muted);
  max-width:38ch;margin-top:14px;
}
/* The bio keeps the rail's own type voice — the spec's fixed point is that
   type size and position do not change between A and A2, so this stays in
   the display face rather than switching to Instrument Sans. */
.bio{
  font-family:var(--display);font-weight:500;
  font-size:clamp(1rem,1.35vw,1.14rem);
  line-height:1.5;letter-spacing:-.01em;
  color:var(--muted);
  max-width:28ch;                  /* the narrow measure, ~28 chars a line */
  margin-top:14px;display:none;
}
.bio strong{color:var(--text);font-weight:600}

.disclose{
  margin-top:18px;
  font-family:var(--mono);font-size:12.5px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--muted);background:none;border:0;padding:0;cursor:pointer;
  display:inline-flex;align-items:center;gap:9px;
  transition:color .15s ease;
}
.disclose svg{width:13px;height:13px;transition:transform .15s ease}
.disclose:hover{color:var(--text)}
.caps{
  list-style:none;display:none;flex-wrap:wrap;gap:7px 18px;
  max-width:34ch;margin-top:16px;
}
.caps li{
  font-family:var(--mono);font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--muted);white-space:nowrap;
}
.is-about .caps{display:flex}

.wa-btn.rail-wa{margin-top:22px;display:none}

/* --- State A2 ---------------------------------------------------------- */
.about{
  grid-column:2;grid-row:1;position:relative;
  height:100svh;overflow:hidden;display:none;
}
.portrait{
  position:absolute;top:0;left:0;right:-9%;          /* bleeds off the edge */
  height:100%;width:109%;
  object-fit:cover;object-position:52% 32%;
}
/* soft handover from the photograph into the canvas */
.about::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(90deg,var(--bg) 0%,rgba(9,53,38,.55) 16%,rgba(9,53,38,0) 42%);
}

.is-about .stage,
.is-about .plist,
.is-about .positioning{display:none}
.is-about .about{display:block}
.is-about .bio{display:block}
.is-about .wa-btn.rail-wa{display:inline-flex}
/* --wa stays on exactly one element at a time: in A2 the rail carries it */
.is-about .index-foot .wa-btn{display:none}
.is-about .index-foot{justify-content:flex-end}
.is-about .disclose svg{transform:rotate(180deg)}

/* project list — a menu, not a table. Pitch ≈ 1.9× the line box. */
.projects{list-style:none;display:flex;flex-direction:column;gap:10px}

.project{
  display:inline-flex;align-items:center;gap:14px;
  width:max-content;max-width:100%;
  padding:10px 15px;                 /* h-padding = 1.5 × v-padding */
  border-radius:100px;               /* radius = half height */
  background:transparent;
  color:var(--muted);
  transition:color .15s ease,background .15s ease,transform .15s ease;
}
.p-name{
  font-family:var(--display);font-weight:700;
  font-size:clamp(1.45rem,2.1vw,1.8rem);
  line-height:1.05;letter-spacing:-.02em;
  white-space:nowrap;
}
.p-tag{
  font-family:var(--mono);font-size:11px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--muted);white-space:nowrap;
  opacity:0;transition:opacity .15s ease;
}
.p-expand{
  width:17px;height:17px;flex:none;
  color:var(--gold);                 /* gold's single job in this layout */
  opacity:0;transform:translateX(-4px);
  transition:opacity .15s ease,transform .15s ease;
}
.project.is-active{
  background:var(--pill);
  color:var(--text);
  padding-right:38px;                /* pill runs wider than the label */
}
.project.is-active .p-tag{opacity:1}
.project.is-active .p-expand{opacity:1;transform:translateX(0)}
.project:hover{transform:translateY(-2px)}

/* --- right stage: the desk render --------------------------------------- */

.stage{
  position:sticky;top:0;
  height:100svh;
  display:grid;place-items:center;
  padding:0 6vw 0 2vw;
}
.desk{
  position:relative;
  width:100%;max-width:820px;
  aspect-ratio:1120/784;
}
.desk-svg{position:absolute;inset:0;width:100%;height:100%;z-index:1}

/* Content-keyed ambient light. The scene's brightness tracks the screen,
   which is what makes the render read as an object rather than a mockup.
   Clamped toward the brand's warm axis so a bright screenshot does not
   wash the forest canvas grey-green (CLAUDE.md §3.1b). */
.bloom-layer{position:absolute;pointer-events:none}
.bloom{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  opacity:0;filter:blur(60px) saturate(.6);
  transition:opacity var(--swap-out) var(--ease);
}
.bloom.is-active{transition:opacity var(--swap-in) var(--ease)}

/* behind the monitor — the wall */
.bloom-back{left:8%;top:0;width:84%;height:70%;z-index:0;transform:scale(1.22)}
.bloom-back .bloom.is-active{opacity:.30}
/* in front of the chassis — spill across the plinth and keyboard */
.bloom-front{left:0;top:66%;width:100%;height:34%;z-index:2;mix-blend-mode:screen}
.bloom-front .bloom.is-active{opacity:.16}
/* the warm clamp: gold at very low opacity over the whole spill */
.bloom-layer::after{
  content:"";position:absolute;inset:-15%;
  background:radial-gradient(58% 56% at 50% 50%,rgba(232,180,74,.09),rgba(232,180,74,0) 72%);
  mix-blend-mode:screen;
}

/* Screen slot. Rect matches the SVG screen within viewBox "40 58 1120 784":
   x 221 → 16.161%, y 94 → 4.592%, w 758 → 67.679%, h 474 → 60.459% */
.screen-slot{
  position:absolute;z-index:3;
  left:16.161%;top:4.592%;width:67.679%;height:60.459%;
  border-radius:6px;overflow:hidden;
  background:var(--scrim);
}
.shot{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  opacity:0;transition:opacity var(--swap-out) var(--ease);
}
.shot.is-active{opacity:1;transition:opacity var(--swap-in) var(--ease)}
/* glass: one soft diagonal sheen, nothing that reads as a filter */
.screen-slot::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(115deg,rgba(245,234,227,.09) 0%,rgba(245,234,227,0) 42%);
  box-shadow:inset 0 0 0 1px rgba(4,26,19,.55);
}

/* --- index footer -------------------------------------------------------- */

.index-foot{
  position:fixed;right:3vw;bottom:2.4vh;z-index:20;
  display:flex;align-items:center;gap:22px;
}
.foot-links{display:flex;align-items:center;gap:20px}
.foot-links a{
  font-family:var(--mono);font-size:12px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--muted);transition:color .15s ease;
}
.foot-links a:hover{color:var(--text)}
.wa-btn{
  display:inline-flex;align-items:center;gap:9px;
  background:var(--wa);color:#04231a;                 /* --wa's only job */
  font-family:var(--body);font-weight:600;font-size:14.5px;
  padding:11px 18px;border-radius:100px;
  transition:transform .15s ease,background .15s ease,box-shadow .15s ease;
}
.wa-btn svg{width:17px;height:17px;flex:none}
.wa-btn:hover{background:var(--wa-deep);color:#fff;transform:translateY(-2px);box-shadow:0 10px 24px rgba(29,168,81,.3)}

/* ===========================================================================
   STATE A — mobile. The split reorders vertically; the render is kept,
   full width, and pins to the top of the viewport (CLAUDE.md §1 mobile).
   =========================================================================== */

@media(max-width:900px){
  /* Everything fits the first screen — the render is sized from what the
     identity block and the list leave over, so there is nothing to scroll
     to at any phone height. */
  .index{
    display:flex;flex-direction:column;
    height:100svh;min-height:0;
    padding-bottom:calc(78px + env(safe-area-inset-bottom));
  }
  .rail{display:contents;padding:0}
  .identity{order:1;flex:none;padding:3.4vh 6vw 1.8vh;}
  .identity h1{font-size:clamp(2rem,8vw,2.6rem)}
  .positioning{font-size:clamp(.88rem,3.9vw,1.2rem);max-width:none}  /* scales so the two authored lines never wrap */

  .stage{
    order:2;
    position:relative;z-index:5;
    flex:1 1 auto;height:auto;min-height:150px;
    max-height:70vw;                /* never taller than the scene is wide */
    padding:0;overflow:hidden;
    background:linear-gradient(180deg,var(--panel) 0%,var(--panel-2) 74%,var(--bg-deep) 100%);
    border-radius:0 0 34px 34px;
  }
  /* gradient falloff into the canvas, inset so it cannot extend the page */
  .stage::after{
    content:"";position:absolute;left:0;right:0;bottom:0;height:38px;
    background:linear-gradient(180deg,rgba(6,34,25,0) 0%,rgba(6,34,25,.6) 100%);
    border-radius:0 0 34px 34px;pointer-events:none;
  }
  /* width-driven and complete: min-width used to force it wider than the
     block, which cropped the keyboard, mouse and plinth off the bottom.
     The scale trims the scene's dead margin so the monitor reads larger,
     and takes the screen slot with it — they share the same box. */
  /* height-driven so the full scene always fits the block — min-width used
     to force it wider and crop the keyboard, mouse and plinth off. */
  .desk{
    height:100%;width:auto;max-width:none;min-width:0;aspect-ratio:1120/784;
  }

  .about{
    order:2;
    flex:1 1 auto;height:auto;min-height:130px;
    border-radius:0 0 34px 34px;
  }
  .bio{font-size:clamp(.94rem,3.8vw,1.08rem);line-height:1.45;max-width:none}
  .caps{max-width:none;margin-top:12px;gap:6px 14px}
  .caps li{font-size:10px;letter-spacing:.1em}
  .portrait{right:0;width:100%;object-position:52% 26%}
  .about::after{
    background:linear-gradient(180deg,rgba(9,53,38,0) 58%,rgba(9,53,38,.9) 100%);
    border-radius:0 0 34px 34px;
  }
  .rail-wa{margin-top:20px}

  /* very short viewports (SE-class): reclaim height from the chrome so the
     list still clears the contact bar */
  @media(max-height:620px){
    .identity{padding:3vh 6vw 1.6vh}
    .identity h1{font-size:clamp(1.7rem,7vw,2.1rem)}
    .positioning{font-size:clamp(.78rem,3.6vw,1rem)}
    .stage{min-height:120px}
    .plist{padding-top:1.6vh}
  }

  .plist{
    order:3;
    flex:none;position:relative;z-index:1;
    padding:1.8vh 6vw 0;
  }
  .projects{gap:clamp(6px,1vh,13px)}
  .project{
    width:100%;                       /* full content width on mobile */
    justify-content:flex-start;
    padding:clamp(9px,1.45vh,16px) 20px;
    border-radius:100px;
  }
  .project.is-active{padding-right:20px}
  .project:hover{transform:none}
  .p-expand{margin-left:auto}
  .p-tag{display:none}

  /* fixed footer: contact action bottom-left, links bottom-right.
     The theme control slot is given to WhatsApp (CLAUDE.md §3.5). */
  .index-foot{
    left:0;right:0;bottom:0;
    padding:12px max(14px,3vw) calc(12px + env(safe-area-inset-bottom));
    justify-content:space-between;
    background:linear-gradient(180deg,rgba(9,53,38,0) 0%,rgba(6,34,25,.92) 46%);
  }
  .foot-links{gap:16px}
  .foot-links a{font-size:11px;letter-spacing:.12em}
}

@media(min-width:901px){
  .plist{display:contents}
}

/* ===========================================================================
   STATE B — case study
   =========================================================================== */

.case{
  display:grid;
  grid-template-columns:38vw 1fr;
  gap:0;
  min-height:100svh;
}

.case-rail{
  position:sticky;top:0;align-self:start;
  height:100svh;
  padding:9vh 4vw 9vh 8vw;
  display:flex;flex-direction:column;justify-content:center;gap:20px;
}
.back{
  width:44px;height:44px;flex:none;
  display:inline-flex;align-items:center;justify-content:center;
  background:var(--panel);border:1px solid var(--line);border-radius:50%;
  color:var(--muted);
  transition:transform .15s ease,background .15s ease,color .15s ease;
}
.back svg{width:18px;height:18px}
.back:hover{background:var(--panel-2);color:var(--text);transform:translateY(-2px)}

.case-rail h1{
  font-family:var(--display);font-weight:800;
  font-size:clamp(1.9rem,3vw,2.6rem);line-height:1.05;letter-spacing:-.02em;
  max-width:14ch;
}
.case-role{
  font-family:var(--mono);font-size:11.5px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--muted);
}
/* Petersen ships no body copy here. YSB must — nobody recognises the
   products in these screenshots (CLAUDE.md §3.6). */
.case-copy{display:flex;flex-direction:column;gap:14px;max-width:42ch}
.case-copy p{font-family:var(--body);font-size:16px;line-height:1.6;color:var(--muted)}
.case-copy strong{color:var(--text);font-weight:600}

.live-link{
  font-family:var(--mono);font-size:12.5px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--muted);
  display:inline-flex;align-items:center;gap:9px;width:max-content;
  transition:color .15s ease,transform .15s ease;
}
.live-link svg{width:14px;height:14px}
a.live-link:hover{color:var(--text);transform:translateY(-2px)}
/* a missing live link is a state, not an error */
.live-link.is-absent{color:rgba(245,234,227,.4);cursor:default}
.live-link.is-absent svg{display:none}

.case-stack{
  padding:9vh 6vw 12vh 0;
  display:flex;flex-direction:column;
  gap:clamp(30px,3.4vw,56px);        /* ≈ 6% of card height */
  max-width:820px;
}
/* Never a raw white rectangle on --bg — every screenshot is framed
   in a --panel card (CLAUDE.md §3.1). */
.shot-card{
  background:var(--panel);
  border:1px solid var(--line);
  border-radius:var(--radius-lg);
  padding:12px;
  overflow:hidden;
}
.shot-card img{
  width:100%;height:auto;
  border-radius:calc(var(--radius-lg) - 14px);
}
.shot-card figcaption{
  font-family:var(--mono);font-size:11px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--muted);padding:14px 6px 4px;
}
.shot-card.is-portrait{align-self:flex-start}   /* card hugs the phone, stays on the gutter */
.shot-card.is-portrait img{max-width:320px}

@media(max-width:900px){
  .case{grid-template-columns:1fr}
  /* State B at phone width is unverified against the reference — the rail
     unsticks and becomes a header block rather than guessing at a
     collapsed sticky variant. */
  .case-rail{
    position:static;height:auto;
    padding:8vh 7vw 5vh;justify-content:flex-start;gap:18px;
  }
  .case-rail h1{font-size:clamp(2rem,8vw,2.6rem);max-width:none}
  .case-stack{padding:0 6vw 22vh;gap:26px;max-width:none}
  .case-foot{padding-bottom:calc(16px + env(safe-area-inset-bottom))}
}

/* case-study footer reuses the fixed contact bar */
.case-foot{
  position:fixed;left:0;right:0;bottom:0;z-index:20;
  display:flex;align-items:center;justify-content:space-between;
  padding:12px max(14px,3vw) calc(12px + env(safe-area-inset-bottom));
  background:linear-gradient(180deg,rgba(9,53,38,0) 0%,rgba(6,34,25,.92) 46%);
}
@media(min-width:901px){
  .case-foot{
    left:auto;bottom:2.4vh;right:3vw;
    gap:20px;padding:7px 7px 7px 22px;
    border:1px solid var(--line);border-radius:100px;
    background:rgba(6,34,25,.74);backdrop-filter:blur(12px);
  }
}

/* ===========================================================================
   Motion
   =========================================================================== */

@view-transition{navigation:auto}

@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}
  .shot{transition:none}
  /* kills the A→B state change too, not just decorative motion.
     @view-transition itself is only valid at the top level, so the
     transition is disabled at its pseudo-elements instead. */
  ::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){animation:none!important}
}
