  /* ==========================================================================
     DESIGN TOKENS — the de facto system, extracted from repeated values.
     Shared by styles.css AND css/case-study.css (loaded alongside).
     Canvas colors in js/plinko.js are intentionally hardcoded, not tokened.
     ========================================================================== */
  :root{
    /* core theme */
    --bg:#000;
    --fg:#fff;
    --line:rgba(255,255,255,0.16);       /* hairlines: frame, dividers, control borders */
    --line-strong:rgba(255,255,255,0.3); /* emphasized hairlines: link underline, off dots */
    --mono:"Space Mono", ui-monospace, Menlo, Consolas, monospace;
    --display:"Archivo","Helvetica Neue",Arial,sans-serif;

    /* text emphasis ramp (white-on-black alphas, brightest → dimmest) */
    --text-strong:rgba(255,255,255,0.85);  /* emphasized UI text, hover states, on dots */
    --text-mid:rgba(255,255,255,0.7);      /* resting links + control icons */
    --text-body:rgba(255,255,255,0.55);    /* secondary copy, captions, footer */
    --text-faint:rgba(255,255,255,0.45);   /* eyebrows, .badge, .muted */
    --text-reading:rgba(255,255,255,0.78); /* long-form case-study prose (deliberate off-ramp) */

    /* glass controls (fab, carousel arrows, lightbox close, panel) */
    --glass-bg:rgba(20,20,20,0.7);
    --border-hover:rgba(255,255,255,0.4); /* border brighten on control hover */

    /* surfaces */
    --surface-tint:rgba(255,255,255,0.02); /* fill behind shots/figures */

    /* mono UI type sizes (display sizes are per-element clamps, not tokens) */
    --fs-micro:11px;
    --fs-small:12px;

    /* letter-spacing (doubling ramp; display tracking stays per-element, it's optical) */
    --track-xs:0.04em;  /* mono body text + links */
    --track-sm:0.08em;  /* mono accents (cc-link, cite) */
    --track-md:0.12em;  /* badge, panel heading */
    --track-lg:0.2em;   /* uppercase eyebrows */
    --track-xl:0.4em;   /* hero label */

    /* motion (the .12s press-scale is a distinct gesture, not tokened) */
    --t-fast:.16s;  /* hover color fades */
    --t-med:.24s;   /* panel / lightbox open */
    --t-slow:.32s;  /* carousel slide + active zoom */

    /* backdrop blur */
    --blur-sm:8px;
    --blur-md:12px;
    --blur-lg:16px;

    /* layout */
    --frame-pad:10px;                             /* body padding = frame inset */
    --frame-inset:calc(var(--frame-pad) + 1px);   /* + .site border; js/snap.js
                                                     mirrors this as INSET = 11 */
    --gutter:clamp(26px, 5vw, 68px);              /* section side padding */

    /* shape */
    --radius-sm:8px;
    --radius-md:16px;
    --radius-lg:24px;
    --radius-pill:999px;
  }

  *{ box-sizing:border-box; margin:0; padding:0; }
  /* html is the single scroll + snap container. (Putting scroll-snap-type on
     body while body had height:100% + overflow:auto left the real scroller
     ambiguous, so snapping silently no-op'd.) */
  html{
    height:100%;
    overflow-x:hidden;
    overflow-y:auto;
    /* Native scroll-snap is intentionally OFF — js/snap.js drives a slower,
       gentler proximity glide and treats the footer as a reachable target. */
    scroll-padding-top:var(--frame-inset);
    -webkit-overflow-scrolling:touch;
  }
  body{
    /* The 10px frame inset lives here as padding (not a margin on .site) so it
       contributes reliably to scroll height — a bottom margin on .site would
       collapse through the scroll root and leave no gap below the footer. */
    padding:var(--frame-pad);
    background:var(--bg);
    color:var(--fg);
    font-family:var(--mono);
    -webkit-font-smoothing:antialiased;
    -moz-osx-font-smoothing:grayscale;
  }

  /* The whole page lives inside one hairline-bordered block, inset from the
     page edge (via body padding) and sized to the full content height. Its side
     lines read as the brutalist "frame" the whole way down; top/bottom only show
     at the ends. Total inset = 10px body padding + 1px border = 11px. */
  .site{
    border:1px solid var(--line);
  }

  /* HERO — first full screen; stays the flex container for .top + #plinko.
     applyLayout() in JS toggles flex-direction (row/column) at runtime. */
  .page{
    position:relative;
    /* fills the first screen, but inset by the 11px frame (10px body padding +
       1px border) at BOTH top and bottom — so 100vh minus 22px shows the frame
       gap above and below the hero. */
    height:calc(100vh - 2 * var(--frame-inset));
    height:calc(100dvh - 2 * var(--frame-inset));
    display:flex; flex-direction:row;
    background:var(--bg);
  }

  /* ---------- TOP SECTION ---------- */
  .top{
    flex:0 0 50%;
    position:relative;
    display:flex; flex-direction:column; justify-content:space-between;
    padding: var(--gutter);
    border-right:1px solid var(--line);
    background:var(--bg);
  }
  .hero{
    display: flex;
    flex-direction: column;
    gap: 20px;
  }
  .accent{
    width:clamp(36px,5vw,56px); height:1px;
    background:var(--fg);
    margin-bottom: clamp(18px, 2.4vw, 30px);
    transform-origin:left center;
    animation: grow .65s cubic-bezier(.22,1,.36,1) forwards;
  }
  .name{
    font-family:var(--display);
    font-weight:500;
    font-size: clamp(56px, 10vw, 96px);
    line-height:1;
    letter-spacing:-0.025em;
    animation: rise .75s .08s cubic-bezier(.22,1,.36,1) both;
    margin-left: -4px;
  }
  .label{
    font-family:var(--mono);
    font-size: clamp(10px, 1.3vw, 12px);
    text-transform:uppercase;
    letter-spacing:var(--track-xl);
    color:var(--text-body);
    animation: rise .75s .22s cubic-bezier(.22,1,.36,1) both;
  }
  .label.left {
    padding-left: 2px;
  }
  .body{
    font-family:var(--display); 
    font-size:22px; 
    font-weight: 300;
    line-height:1.5;
    color:var(--text-body);
    max-width: 36ch;
    animation: rise .75s .16s cubic-bezier(.22,1,.36,1) both;
  }
  .buttons{
    display: flex;
    flex-direction: row;
    gap: 12px;
    align-items: start;
  }
  .cta{
    align-self:flex-start;   /* width: auto — fits content */
    width:auto;
    padding:16px 24px;       /* horizontal padding 16px */
    font-family:var(--display); font-size:18px; font-weight:500; 
    letter-spacing:var(--track-xs);
    color:var(--fg); background:transparent;
    border-radius:var(--radius-pill);
    cursor:pointer; text-decoration:none; line-height:1;
    transition:background var(--t-fast) ease, color var(--t-fast) ease;
    animation: rise .75s .36s cubic-bezier(.22,1,.36,1) both;
  }
  .cta:hover{ background:var(--fg); color:var(--bg); }
  .cta.primary{
    border:2px solid var(--fg); 
  }

  /* ---------- BOTTOM SECTION (Plinko) ---------- */
  #plinko{
    flex:1 1 auto;
    position:relative;
    overflow:hidden;
  }
  /* pan-y lets a vertical finger-drag scroll the page; a clean tap still
     interacts (see the pointerup tap-detection in js/plinko.js). */
  #plinko canvas{ display:block; cursor:pointer; touch-action:pan-y; }

  .badge{
    position:absolute; top:18px; left:22px; z-index:10;
    font-family:var(--mono);
    font-size:var(--fs-small); font-weight:700; letter-spacing:var(--track-md);
    color:var(--text-faint); text-transform:uppercase;
    pointer-events:none;
  }

  .fab{
    position:absolute; bottom:22px; z-index:20;
    width:44px; height:44px; display:flex; align-items:center; justify-content:center;
    background:var(--glass-bg); border:1px solid var(--line);
    border-radius:var(--radius-pill); color:var(--text-mid); cursor:pointer;
    backdrop-filter:blur(var(--blur-md)); -webkit-backdrop-filter:blur(var(--blur-md));
    transition:color var(--t-fast) ease, transform .12s ease;
  }
  .fab:hover{ color:var(--fg); }
  .fab:active{ transform:scale(0.92); }
  .fab svg{ display:block; }
  #settingsBtn{ left:22px; }
  #reset{ right:22px; }

  .panel{
    position:absolute; bottom:78px; left:22px; z-index:19;
    width:min(360px, calc(100vw - 44px)); padding:18px 20px 20px;
    max-height:80vh; overflow-y:auto; overflow-x:hidden;
    background:rgba(18,18,18,0.88); border:1px solid var(--line);
    border-radius:var(--radius-md); backdrop-filter:blur(var(--blur-lg)); -webkit-backdrop-filter:blur(var(--blur-lg));
    opacity:0; transform:translateY(8px) scale(0.98); transform-origin:bottom left;
    pointer-events:none; transition:opacity var(--t-med) ease, transform var(--t-med) ease;
  }
  .panel::-webkit-scrollbar{ width:8px; }
  .panel::-webkit-scrollbar-thumb{ background:var(--line); border-radius:var(--radius-sm); }
  .panel.open{ opacity:1; transform:translateY(0) scale(1); pointer-events:auto; }
  .panel h2{
    font-family:var(--mono);
    font-size:var(--fs-micro); font-weight:700; letter-spacing:var(--track-md); text-transform:uppercase;
    color:var(--text-faint); margin-bottom:16px;
  }
  .row{ margin-bottom:15px; }
  .row:last-child{ margin-bottom:0; }
  .grid2{ display:grid; grid-template-columns:1fr 1fr; column-gap:16px; }
  .grid2 .row{ margin-bottom:13px; }
  .grid2 .row:nth-last-child(-n+2){ margin-bottom:0; }
  .row .label{
    display:flex; justify-content:space-between; align-items:baseline; gap:8px;
    font-size:var(--fs-small); color:var(--text-strong); margin-bottom:7px;
  }
  .grid2 .row .label span:first-child{
    min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  .row .val{ font-size:var(--fs-micro); color:var(--text-body); font-variant-numeric:tabular-nums; flex:none; }
  input[type=range]{
    -webkit-appearance:none; appearance:none; width:100%; height:3px;
    background:var(--line); border-radius:var(--radius-pill); outline:none; cursor:pointer;
  }
  input[type=range]::-webkit-slider-thumb{
    -webkit-appearance:none; appearance:none; width:15px; height:15px;
    background:var(--fg); border-radius:50%; cursor:pointer;
  }
  input[type=range]::-moz-range-thumb{
    width:15px; height:15px; background:var(--fg); border:none; border-radius:50%; cursor:pointer;
  }

  .seg{ display:flex; gap:6px; }
  .seg button{
    flex:1; padding:7px 0;
    font-family:var(--mono); font-size:var(--fs-micro); letter-spacing:var(--track-xs);
    background:transparent; color:var(--text-body);
    border:1px solid var(--line); border-radius:var(--radius-sm); cursor:pointer;
    transition:background var(--t-fast) ease, color var(--t-fast) ease, border-color var(--t-fast) ease;
  }
  .seg button:hover:not(.active){ color:var(--fg); border-color:var(--border-hover); }
  .seg button.active{ background:var(--fg); color:var(--bg); border-color:var(--fg); }
  .panel .divider{ height:1px; background:var(--line); margin:4px 0 16px; }

  /* ---------- SECTION SHELL (Work + About) ---------- */
  /* 100vh-22px tall (like the hero): js/snap.js snaps these vertically centered
     — absolute top minus the 11px frame inset — so the 22px of slack splits into
     an ~11px gap above and below, matching the hero frame. */
  .work, .about{
    position:relative;
    min-height: 100vh;
    min-height: 100dvh;
    min-height:calc(100vh - 2 * var(--frame-inset));
    min-height:calc(100dvh - 2 * var(--frame-inset));
    border-top:1px solid var(--line);
    display:flex; flex-direction:column; justify-content:space-between;
    padding-top: clamp(26px, 4vw, 68px);
    padding-bottom: clamp(26px, 4vw, 68px);
    background:var(--bg);
  }
  .about{
    padding-right: var(--gutter);
    padding-left: var(--gutter);
  }


  /* ---------- WORK / PROJECT CAROUSEL ---------- */
  .work{ align-items:center; overflow:hidden; }
  
  .work-inner{
    width:100%;
    display:flex; flex-direction:column; align-items:center; justify-content: space-between;
    /* gap: clamp(32px, 4vh, 48px); */
  }
  
  .carousel-caption{
    width:100%;
    min-height:72px;             /* reserve room so per-project swaps don't reflow */
    max-width:60ch;
    display:flex; 
    flex-direction:column;
    text-align:center;
    align-items:center
  }
  .cc-title{
    font-family:var(--display); font-weight:500;
    font-size: clamp(28px, 3vw, 32px);
    line-height:1.1;
  }
  .cc-desc{
    font-family:var(--display); font-size:18px; line-height:1.5;
    color:var(--text-body);
    margin-top: 8px;
    max-width: 48ch;
  }
  /* "View case study →" — only shown for projects with a data-href (see
     setActive in js/carousel.js, which toggles the [hidden] attribute). */
  .cc-link{
    margin-top: 14px;
    font-family:var(--mono); font-size:var(--fs-small); letter-spacing:var(--track-sm);
    color:var(--text-mid); text-decoration:none;
    border-bottom:1px solid var(--line-strong);
    padding-bottom:2px;
    transition:color var(--t-fast) ease, border-color var(--t-fast) ease;
  }
  .cc-link:hover{ color:var(--fg); border-bottom-color:var(--fg); }
  .cc-link[hidden]{ display:none; }

  /* Inset (not full-bleed): the track stays within the .work section padding, so
     the peeking neighbours are clipped with breathing room *before* the frame
     border rather than hard-cut right at it. The .work padding is that gap. */
  .carousel-wrap{
    position:relative; width:100%;
  }
  .carousel{
    display:flex; gap: clamp(28px, 6vw, 92px);
    overflow-x:auto; overflow-y:hidden;
    /* Native scroll-snap is OFF — js/carousel.js drives its own slower eased
       glide (mirrors js/snap.js) and would fight a mandatory native snap. */
    /* small end breathing-room; the section padding already insets the track
       from the frame, so keep this modest to avoid doubling the side gap */
    padding-inline: clamp(8px, 2vw, 20px);
    scrollbar-width:none;
    -webkit-overflow-scrolling:touch;
  }
  .carousel::-webkit-scrollbar{ display:none; }
  /* Kill-switch the JS toggles during the invisible clone→middle teleport so the
     active-project zoom doesn't replay at the same screen position. */
  .carousel.no-anim .project{ transition:none; }

  /* Each project hugs its 1–3 shots, so a snippet of the adjacent project is
     always visible at the sides regardless of how many screenshots it holds. */
  .project{
    flex:0 0 auto;
    max-width:92vw;
    display:flex; justify-content:center; align-items:center;
    gap: clamp(10px, 1.4vw, 20px);
    opacity:0.5; transform:scale(0.94);
    transition:opacity var(--t-slow) ease, transform var(--t-slow) ease;
  }
  .project.active{ opacity:1; transform:none; }
  .project .shot{
    height: clamp(320px, 60vh, 680px);
    cursor:zoom-in;
    aspect-ratio:9 / 19.5;
    border:1px solid var(--line);
    border-radius:var(--radius-lg);
    background:var(--surface-tint);
    object-fit:cover;          /* <img> fills the rounded phone frame */
    display:block;
  }

  /* "has more screenshots" hint — hidden on desktop (the desktop carousel already
     shows every shot of a project side by side); shown only on mobile, below the
     screenshot in the gutter so it never covers screenshot content. */
  .shot-dots{
    display:none;
    align-items:center; justify-content:center; gap:7px;
    pointer-events:none;
  }
  .shot-dots span{
    width:6px; height:6px; border-radius:50%;
    background:var(--line-strong);
  }
  .shot-dots span.on{ background:var(--text-strong); }

  .carousel-arrow{
    position:absolute; top:50%; transform:translateY(-50%); z-index:5;
    width:48px; height:48px; display:flex; align-items:center; justify-content:center;
    background:var(--glass-bg); border:1px solid var(--line);
    border-radius:var(--radius-pill); color:var(--text-mid); cursor:pointer;
    backdrop-filter:blur(var(--blur-md)); -webkit-backdrop-filter:blur(var(--blur-md));
    transition:color var(--t-fast) ease, transform .12s ease, border-color var(--t-fast) ease;
  }
  .carousel-arrow:hover{ color:var(--fg); border-color:var(--border-hover); }
  .carousel-arrow:active{ transform:translateY(-50%) scale(0.92); }
  .carousel-arrow.prev{ left: clamp(4px, 2vw, 24px); }
  .carousel-arrow.next{ right: clamp(4px, 2vw, 24px); }
  .carousel-arrow svg{ display:block; }

  /* ---------- ABOUT SECTION ---------- */
  .about-text{
    font-family:var(--display);
    font-weight:500;
    font-size: clamp(32px, 5vw, 64px);
    line-height:1.1;
    letter-spacing:-0.02em;
    max-width: 28ch;
    margin-left: -2px;
  }
  .muted{ color:var(--text-faint); }
  .about .cta{ margin-top: clamp(30px, 4vw, 48px); }

  /* ---------- FOOTER ---------- */
  .site-footer{
    border-top:1px solid var(--line);
    padding: clamp(20px, 3vw, 40px) var(--gutter);
    display:flex; flex-wrap:wrap; align-items:center;
    justify-content:space-between; gap:14px 28px;
    background:var(--bg);
    color:var(--text-body);
    font-family:var(--mono); font-size:var(--fs-small); letter-spacing:var(--track-xs);
  }
  .link{
    color:var(--text-mid); text-decoration:none;
    transition:color var(--t-fast) ease;
    font-family:var(--mono); font-size:var(--fs-small); letter-spacing:var(--track-xs);
    
  }
  .link:hover{ color:var(--fg); text-decoration:underline; text-underline-offset:3px; }

  /* ---------- LIGHTBOX (fullscreen screenshot overlay) ---------- */
  .lightbox{
    position:fixed; inset:0; z-index:60;
    display:flex; align-items:center; justify-content:center;
    padding: clamp(20px, 5vw, 64px);
    background:rgba(0,0,0,0.92);
    backdrop-filter:blur(var(--blur-sm)); -webkit-backdrop-filter:blur(var(--blur-sm));
    opacity:0; pointer-events:none;
    transition:opacity var(--t-med) ease;
  }
  .lightbox.open{ opacity:1; pointer-events:auto; }
  .lightbox-stage{
    display:flex; align-items:center;
    gap: clamp(14px, 2vw, 32px);
    max-width:100%; max-height:100%;
    overflow-x:auto; overflow-y:hidden;
    scrollbar-width:none;
    /* brief slide+fade when arrow keys change projects (see js/carousel.js) */
    transition: transform var(--t-slow) ease, opacity var(--t-slow) ease;
  }
  .lightbox-stage::-webkit-scrollbar{ display:none; }
  .lightbox-stage img{
    flex:0 0 auto;
    /* natural aspect, capped to the height left after the lightbox padding so the
       whole screenshot shows with no top/bottom crop, even on short viewports */
    max-height: calc(100vh - 2 * clamp(20px, 5vw, 64px));
    max-height: calc(100dvh - 2 * clamp(20px, 5vw, 64px));
    width:auto; height:auto;
    object-fit:contain;
    border:1px solid var(--line);
    border-radius:var(--radius-lg);
    background:var(--surface-tint);
    display:block;
  }
  .lightbox-close{
    position:absolute; top: clamp(16px, 3vw, 28px); right: clamp(16px, 3vw, 28px);
    z-index:1;
    width:44px; height:44px; display:flex; align-items:center; justify-content:center;
    background:var(--glass-bg); border:1px solid var(--line);
    border-radius:var(--radius-pill); color:var(--text-mid); cursor:pointer;
    backdrop-filter:blur(var(--blur-md)); -webkit-backdrop-filter:blur(var(--blur-md));
    transition:color var(--t-fast) ease, transform .12s ease, border-color var(--t-fast) ease;
  }
  .lightbox-close:hover{ color:var(--fg); border-color:var(--border-hover); }
  .lightbox-close:active{ transform:scale(0.92); }
  .lightbox-close svg{ display:block; }

  /* Project label under the mobile one-at-a-time stream; hidden on desktop. */
  .lightbox-caption{
    display:none;
    position:absolute; left:0; right:0; bottom: clamp(16px, 4vw, 28px);
    text-align:center;
    font-family:var(--mono); font-size:var(--fs-micro); letter-spacing:var(--track-lg);
    text-transform:uppercase; color:var(--text-body);
    pointer-events:none;
  }

  /* ---------- MOBILE (≤700px) ----------
     One breakpoint. Kept in sync with mqMobile in js/carousel.js. The hero
     stacks vertically 1:1, the carousel shows one project at a time (first shot
     only) and the lightbox becomes a one-at-a-time swipe stream. */
  @media (max-width: 700px){
    /* HERO — force vertical 1:1. !important overrides the inline flex styles
       applyLayout() writes in js/plinko.js; the #plinko ResizeObserver refits the
       canvas automatically when the section resizes. */
    .page{ flex-direction:column !important; }
    .top{
      flex:0 0 66.6667% !important;   /* hero : plinko = 2 : 1 */
      border-right:none !important;
      border-bottom:1px solid var(--line) !important;
      padding:24px;
    }
    #plinko{ flex:1 1 auto; }
    .hero{ gap:12px; }
    .name{ font-size:clamp(38px, 11vw, 56px); }
    .body{ font-size:16px; line-height:1.45; }
    /* "Let's chat" full-width on line 1; Portfolio + Resume share line 2. */
    .buttons{ flex-direction:row; flex-wrap:wrap; align-items:stretch; gap:10px; }
    .cta{
      text-align:center;
      padding:14px 20px; font-size:16px;
    }
    .cta.primary{ flex:1 1 100%; }                          /* full-width, line 1 */
    .buttons .cta:not(.primary){ flex:1 1 0; min-width:0; } /* equal halves, line 2 */

    /* Settings — the Layout / Section ratio controls are meaningless once the
       hero is force-stacked. Hide their rows (and the divider that followed). */
    .panel .row:has(#seg-orient),
    .panel .row:has(#seg-ratio),
    .panel .divider{ display:none; }

    /* WORK CAROUSEL — one project per viewport (no peek), first shot only, with
       the multi-shot dots stacked beneath it. */
    .project{
      flex:0 0 100% !important; max-width:100%;
      flex-direction:column; gap:14px;
    }
    .project .shot:not(:first-child){ display:none; }
    .project .shot{ height:clamp(280px, 54vh, 520px); }
    .shot-dots{ display:flex; }
    .carousel-arrow{ width:42px; height:42px; }

    .cc-title{ font-size:26px; }
    .cc-desc{ font-size:16px; }

    /* LIGHTBOX — single shot + project caption (filled by js/carousel.js). */
    .lightbox{ padding:48px 16px 56px; }
    .lightbox-caption{ display:block; }

    /* ABOUT */
    .about-text{ font-size:clamp(30px, 8vw, 44px); }

    /* FOOTER — stack the links + copyright. */
    .site-footer{
      flex-direction:column; align-items:flex-start; gap:10px;
      padding:24px;
    }
  }

  @keyframes rise{ from{ opacity:0; transform:translateY(14px);} to{ opacity:1; transform:none;} }
  @keyframes grow{ from{ transform:scaleX(0);} to{ transform:scaleX(1);} }
  @media (prefers-reduced-motion:reduce){
    .accent,.name,.label,.body,.cta{ animation:none !important; }
    .lightbox,.lightbox-stage,.project{ transition:none !important; }
  }
