      /* Self-hosted fonts (latin subset). Inter Tight is variable, so one file
         covers 400 and 500. */
      @font-face {
        font-family: "Inter Tight";
        font-style: normal;
        font-weight: 400 500;
        font-display: swap;
        src: url("/fonts/inter-tight-latin.woff2") format("woff2");
        unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
      }
      @font-face {
        font-family: "Fragment Mono";
        font-style: normal;
        font-weight: 400;
        font-display: swap;
        src: url("/fonts/fragment-mono-400-latin.woff2") format("woff2");
        unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
      }

/* ---------------------------------------------------------------------
   talhaahmad.fi - single page.

   Type and button geometry come from values measured off the reference
   (cantor8.io) at a 1512px viewport, not eyeballed:

     button   198x45, white ground, #044AB3 text, radius 2.69px,
              padding 3.591 3.591 3.591 14.364, font 14.364px/500
     arrow    38x38 box, background #044AB3, radius 2.69px
     layout   content anchored bottom-left, 28px in, 38px up

   Expressed in em/vw so the proportions hold at any width.

   The reference sets its headline in PP Neue Montreal, a commercial
   Pangram Pangram face. Inter Tight is the closest freely licensed
   neo-grotesque: a substitute, not a match.

   webgl.js is the reference capture's own animation, used with two
   changes: the ring labels are Talha's, and it now paints a frame on
   init because its context is alpha:false and it refuses to run while
   the tab is hidden, which showed an opaque black rectangle to anyone
   opening the page in a background tab.
   --------------------------------------------------------------------- */

      :root {
        /* Ground also has to be set as PAGE_BG in webgl.js, which fills the
           canvas every frame. Change one without the other and a seam shows. */
        --ground: #0b0c10;
        --ground-2: #06070a;
        --white: #ffffff;
        --edge-x: clamp(1.25rem, 1.85vw, 2rem); /* 28px at 1512 */
        --edge-y: clamp(1.5rem, 2.51vw, 2.75rem); /* 38px at 1512 */
        --ease: cubic-bezier(0.22, 1, 0.36, 1);
        --arrow: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 12L12 4M12 4H5.5M12 4v6.5' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='square'/%3E%3C/svg%3E");
      }

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

      html,
      body {
        height: 100%;
      }

      body {
        margin: 0;
        background: var(--ground);
        color: var(--white);
        font-family:
          "Inter Tight",
          system-ui,
          -apple-system,
          "Segoe UI",
          sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        overflow: hidden;
        -webkit-text-size-adjust: 100%;
      }

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

      ::selection {
        background: var(--white);
        color: var(--ground);
      }

      ::-webkit-scrollbar {
        width: 0;
      }
      ::-webkit-scrollbar-track,
      ::-webkit-scrollbar-thumb {
        background: transparent;
      }

      .sr-only {
        position: absolute;
        width: 1px;
        height: 1px;
        padding: 0;
        margin: -1px;
        overflow: hidden;
        clip: rect(0, 0, 0, 0);
        white-space: nowrap;
        border: 0;
      }

      /* --- background canvas ------------------------------------------------ */

      .background_webgl {
        position: fixed;
        inset: 0;
        z-index: 0;
        pointer-events: none;
      }

      .strings-viewport {
        width: 100%;
        height: 100vh;
        height: 100svh;
        min-height: 480px;
        overflow: hidden;
        display: flex;
        align-items: center;
        justify-content: center;
        background: var(--ground);
        --strings-scale: 0.72;
      }

      #strings-wrap {
        /* Flex item: without this, flex-shrink:1 overrides the calc() width and
     the canvas renders into a narrow slab instead of full bleed. */
        flex: 0 0 auto;
        width: calc(100% / var(--strings-scale));
        height: calc(100% / var(--strings-scale));
        transform: scale(var(--strings-scale));
        transform-origin: center center;
        will-change: transform;
      }

      #strings-canvas {
        display: block;
        width: 100%;
        height: 100%;
      }

      @media (min-width: 991px) {
        .strings-viewport {
          --strings-scale: 0.64;
        }
      }
      @media (max-width: 990px) {
        .strings-viewport {
          --strings-scale: 0.58;
        }
      }
      @media (max-width: 479px) {
        .strings-viewport {
          --strings-scale: 0.8;
        }
      }

      /* --- corner mark ------------------------------------------------------ */

      .header {
        position: fixed;
        top: 0;
        left: 0;
        z-index: 30;
        padding: var(--edge-y) var(--edge-x);
      }

      .wordmark {
        display: inline-flex;
        align-items: center;
        gap: 0.62rem;
        font-family:
          "Fragment Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
        /* 15px at 1280 up to 17px on a wide screen, never below 14px. */
        font-size: clamp(0.875rem, 0.52vw + 0.46rem, 1.0625rem);
        letter-spacing: 0.02em;
      }

      /* Markhor mark. Drawn as a CSS mask rather than inline SVG: the traced
         art is 13 paths and would add ~12KB to this file, and a mask still
         inherits the text colour, which an <img> cannot. The ?v= token is
         load-bearing, the bare logo.svg URL has a stale copy on the CDN. */
      .wordmark .cube {
        /* The traced mark is about 0.53 as wide as it is tall; keep that ratio
           or it sits in a box with dead space either side. */
        width: clamp(17px, 1.32vw, 22px);
        height: clamp(32px, 2.5vw, 41px);
        background: var(--white);
        -webkit-mask: url("/logo.svg?v=5") center / contain no-repeat;
        mask: url("/logo.svg?v=5") center / contain no-repeat;
      }

      @keyframes cubeFlicker {
        0%,
        100% {
          opacity: 1;
        }
        25% {
          opacity: 0.52;
        }
        50% {
          opacity: 0.25;
        }
        75% {
          opacity: 0.9;
        }
      }

      /* --- hero: anchored bottom-left ---------------------------------------
   svh rather than vh: on mobile, vh is the tall viewport, so a
   bottom-anchored block sits behind the browser's own chrome. */

      .hero {
        position: relative;
        z-index: 10;
        min-height: 100vh;
        min-height: 100svh;
        display: flex;
        flex-direction: column;
        justify-content: flex-end;
        padding: var(--edge-y) var(--edge-x);
        padding-bottom: calc(var(--edge-y) + env(safe-area-inset-bottom, 0px));
      }

      h1.h1 {
        margin: 0;
        font-weight: 400;
        /* Ramps 48px at 375 to 59px at 1512, matching the reference there,
     without collapsing to an unreadable size on a phone. */
        font-size: clamp(2.75rem, calc(0.97vw + 2.775rem), 4rem);
        line-height: 1;
        letter-spacing: -0.0026em;
        color: var(--white);
      }

      /* --- rotating role ---------------------------------------------------- */

      .roles {
        position: relative;
        display: block;
        height: 1.16em; /* room for descenders at line-height 1 */
      }

      /* Per-character reveal. Every letter carries its own transition-delay
         from --i, set in JS, so a word arrives and leaves as a wave instead of
         one solid block sliding. Three states are kept so the outgoing and
         incoming words still travel the SAME direction: idle below, active in
         place, leaving upward. */
      .role {
        position: absolute;
        left: 0;
        top: 0;
        white-space: nowrap;
      }

      /* Each word is one inline-block so a line can still break BETWEEN words
         on a phone. Without this, per-character inline-blocks would let a word
         break in the middle of itself. */
      .role .w {
        display: inline-block;
        white-space: nowrap;
      }

      .role .ch {
        display: inline-block;
        opacity: 0;
        transform: translateY(0.6em) rotate(3deg);
        filter: blur(4px);
        transition:
          opacity 0.5s var(--ease),
          transform 0.62s var(--ease),
          filter 0.5s var(--ease);
        transition-delay: calc(var(--i) * 26ms);
        will-change: transform, opacity, filter;
      }

      .role.is-active .ch {
        opacity: 1;
        transform: none;
        filter: blur(0);
      }

      .role.is-leaving .ch {
        opacity: 0;
        transform: translateY(-0.55em) rotate(-3deg);
        filter: blur(4px);
      }

      /* Crawlable one-line introduction under the rotating headline. */
      .intro {
        max-width: 34rem;
        margin: 1rem 0 0;
        font-size: clamp(0.9375rem, 0.3vw + 0.85rem, 1.0625rem);
        line-height: 1.45;
        color: rgba(255, 255, 255, 0.72);
      }

      /* 19px below a 58.968px headline at 1512. */
      .button_wrap {
        margin-top: clamp(0.75rem, 1.26vw, 1.25rem);
      }

      /* --- button ----------------------------------------------------------- */

      .button {
        display: inline-flex;
        align-items: center;
        justify-content: space-between;
        /* The reference bar was 198x45 (13.78em x 3.13em at this font size).
           Widened to 19.5em on request; the height stays on the reference. */
        width: 19.5em;
        height: 3.13em;
        min-height: 44px; /* keeps a usable tap target on phones */
        padding: 0.25em 0.25em 0.25em 1em;
        background: var(--white);
        color: var(--ground);
        border: 0;
        border-radius: 0.1875em;
        font-family: inherit;
        font-size: clamp(0.875rem, 0.95vw, 1.0625rem);
        font-weight: 500;
        cursor: pointer;
        -webkit-tap-highlight-color: transparent;
        transition:
          background-color 0.42s var(--ease),
          color 0.42s var(--ease);
      }

      .arrow_button {
        position: relative;
        /* 38 / 14.364 = 2.645em */
        width: 2.645em;
        height: 2.645em;
        flex: 0 0 auto;
        background: var(--ground);
        border-radius: 0.1875em;
        overflow: hidden;
      }

      /* Two arrows: the resting one exits top-right while its twin arrives from
   bottom-left. The reference pulled two PNGs from a CDN for this; the mask
   below is inline, so it cannot rot. */
      .arrow_button::before,
      .arrow_button::after {
        content: "";
        position: absolute;
        left: 50%;
        top: 50%;
        width: 1em;
        height: 1em;
        background: var(--white);
        -webkit-mask: var(--arrow) center/contain no-repeat;
        mask: var(--arrow) center/contain no-repeat;
        transition:
          transform 0.62s var(--ease),
          opacity 0.62s var(--ease);
      }

      .arrow_button::before {
        transform: translate(-50%, -50%);
        opacity: 1;
      }
      .arrow_button::after {
        transform: translate(calc(-50% - 1.4em), calc(-50% + 1.4em));
        opacity: 0;
      }

      /* Hover only where hovering is real, so touch devices do not get stuck
   in a hover state after a tap. */
      @media (hover: hover) and (pointer: fine) {
        .button:hover .arrow_button::before {
          transform: translate(calc(-50% + 1.4em), calc(-50% - 1.4em));
          opacity: 0;
        }
        .button:hover .arrow_button::after {
          transform: translate(-50%, -50%);
          opacity: 1;
        }
      }
      .button:focus-visible .arrow_button::before {
        transform: translate(calc(-50% + 1.4em), calc(-50% - 1.4em));
        opacity: 0;
      }
      .button:focus-visible .arrow_button::after {
        transform: translate(-50%, -50%);
        opacity: 1;
      }

      /* --- contact dialog --------------------------------------------------- */

      .modal {
        position: fixed;
        inset: 0;
        z-index: 50;
        display: grid;
        place-items: center;
        padding: 1.25rem;
        overflow-y: auto;
      }

      .modal[hidden] {
        display: none;
      }

      .modal_backdrop {
        position: absolute;
        inset: 0;
        background: rgba(0, 0, 0, 0.78);
        -webkit-backdrop-filter: blur(6px);
        backdrop-filter: blur(6px);
        opacity: 0;
        transition: opacity 0.38s var(--ease);
      }

      .modal_panel {
        position: relative;
        width: min(26rem, 100%);
        background: var(--white);
        color: var(--ground);
        border-radius: 3px;
        padding: 1.75rem 1.75rem 1.5rem;
        opacity: 0;
        transform: translateY(0.75rem) scale(0.985);
        transition:
          opacity 0.38s var(--ease),
          transform 0.38s var(--ease);
      }

      .modal.is-open .modal_backdrop {
        opacity: 1;
      }
      .modal.is-open .modal_panel {
        opacity: 1;
        transform: none;
      }

      .modal_title {
        margin: 0 0 0.35rem;
        font-size: 1.375rem;
        font-weight: 500;
        letter-spacing: -0.01em;
      }

      .modal_note {
        margin: 0 0 1.5rem;
        font-family:
          "Fragment Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
        font-size: 0.6875rem;
        letter-spacing: 0.14em;
        text-transform: uppercase;
        color: rgba(11, 12, 16, 0.72);
      }

      .modal_close {
        position: absolute;
        top: 0.6rem;
        right: 0.6rem;
        width: 2.75rem;
        height: 2.75rem;
        display: grid;
        place-items: center;
        background: transparent;
        border: 0;
        border-radius: 3px;
        color: var(--ground);
        cursor: pointer;
        opacity: 0.7;
        -webkit-tap-highlight-color: transparent;
        transition:
          opacity 0.28s var(--ease),
          background-color 0.28s var(--ease);
      }

      .modal_close:hover,
      .modal_close:focus-visible {
        opacity: 1;
        background: rgba(11, 12, 16, 0.08);
      }

      .modal_close svg {
        width: 14px;
        height: 14px;
      }

      .channels {
        display: grid;
        grid-template-columns: repeat(5, 1fr);
        gap: 0.5rem;
        list-style: none;
        margin: 0;
        padding: 0;
      }

      .channel {
        display: grid;
        place-items: center;
        aspect-ratio: 1;
        min-height: 44px;
        background: var(--ground);
        color: var(--white);
        border-radius: 3px;
        -webkit-tap-highlight-color: transparent;
        transition:
          background-color 0.3s var(--ease),
          transform 0.3s var(--ease);
      }

      .channel svg {
        width: 20px;
        height: 20px;
        fill: currentColor;
        display: block;
      }

      .channel:hover,
      .channel:focus-visible {
        background: var(--ground-2);
        transform: translateY(-2px);
      }

      /* --- focus / motion --------------------------------------------------- */

      :focus-visible {
        outline: 2px solid currentColor;
        outline-offset: 3px;
      }
      .hero :focus-visible,
      .header :focus-visible {
        outline-color: var(--white);
      }

      @media (prefers-reduced-motion: reduce) {
        *,
        *::before,
        *::after {
          animation: none !important;
          transition: none !important;
        }
      }

      /* --- phones ----------------------------------------------------------- */

      @media (max-width: 600px) {
        .modal_panel {
          padding: 1.5rem 1.25rem 1.25rem;
        }
        .modal_title {
          font-size: 1.25rem;
        }
        .modal_note {
          margin-bottom: 1.25rem;
        }
        .channels {
          gap: 0.4rem;
        }
        .channel svg {
          width: 18px;
          height: 18px;
        }
        .button {
          /* Phones: grow with the viewport but never past it. 320px viewport
             leaves 280px of content, and 15em at the 14px floor is 210px. */
          width: auto;
          min-width: 15em;
          max-width: 100%;
          gap: 1.25em;
        }

        /* Measured, not guessed. Role widths in Inter Tight at weight 400:
             Founder 3.543em   Entrepreneur 5.583em   AI Engineer 4.854em
             Full-Stack Developer 8.874em   Automation Architect 9.058em
           The two-word roles are allowed to wrap here, so the binding
           constraint is the longest UNBREAKABLE word, "Entrepreneur" at
           5.583em. 5.8em leaves a margin. Without this the longest role
           overflowed by 149px at 320px wide and was silently clipped,
           because .role is nowrap and body is overflow:hidden. */
        h1.h1 {
          font-size: min(3.25rem, calc((100vw - 2 * var(--edge-x)) / 5.8));
        }

        .roles {
          height: 2.2em;
          line-height: 1.02;
        }

        /* Anchored to the bottom so the last line always sits the same
           distance above the button, whether the role is one line or two. */
        .role {
          white-space: normal;
          overflow-wrap: break-word;
          top: auto;
          bottom: 0;
        }
      }

      /* Landscape phones: the viewport is short, so the ring crowds the type. */
      @media (max-height: 520px) and (orientation: landscape) {
        .strings-viewport {
          --strings-scale: 0.5;
        }
        h1.h1 {
          font-size: 2rem;
        }
        .header {
          padding: 1rem var(--edge-x);
        }
      }

/* --- 404 ---------------------------------------------------------------
   Shares this stylesheet rather than carrying its own: it is already in
   the visitor's cache from the page they arrived from, and CSP forbids an
   inline <style> block. The 404 loads no JS and no canvas, so it renders
   from two cached files. */

.notfound {
  position: relative;
  z-index: 10;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: var(--edge-y) var(--edge-x);
  padding-bottom: calc(var(--edge-y) + env(safe-area-inset-bottom, 0px));
}

.notfound .code {
  margin: 0;
  font-weight: 400;
  font-size: clamp(3.5rem, 11vw, 8rem);
  line-height: 0.92;
  letter-spacing: -0.02em;
  color: var(--white);
}

.notfound .msg {
  margin: 0.9rem 0 1.9rem;
  max-width: 34ch;
  font-family: "Fragment Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.8125rem;
  line-height: 1.5;
  letter-spacing: 0.02em;
  color: rgba(255, 255, 255, 0.62);
}

/* The button is an <a> here, not a <button>, so restate the bits that the
   base rule inherits from a form control. */
.notfound .button {
  text-decoration: none;
}

@media (max-width: 600px) {
  .notfound .code {
    font-size: min(5rem, calc((100vw - 2 * var(--edge-x)) / 2.4));
  }
}

/* On the 404 the wordmark is a link, so it needs a real tap target. The
   negative margin cancels the padding, so the mark does not move: only the
   hit area grows, from 26px tall to 44px. */
a.wordmark {
  padding: 7px 8px;
  margin: -7px -8px;
}
