/* Mobile refinements.
   styles.css is generated from the Claude Design artboard, which styles elements
   inline, so overriding one from a stylesheet needs !important. This file is
   hand-maintained and loaded after it, and a re-import never touches it.

   Where a rule has to reach a generated element it matches on the inline style
   string. That is deliberate: the markup is machine-generated with consistent
   values, and if the artboard changes one the rule stops matching rather than
   misfiring. */

/* Safari enlarges text in landscape unless this is pinned. */
html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

/* The default tap flash is a blue box that fights the palette. */
* {
  -webkit-tap-highlight-color: rgba(201, 164, 76, 0.18);
}

/* A horizontal swipe inside the quote table must not chain to the browser's
   back gesture. */
div[style*="overflow-x:auto"] {
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
}

@media (max-width: 900px) {

  /* ---- touch targets ----
     Everything the thumb has to hit clears 44px. The controls are laid out with
     vertical padding, so the height is reset and re-centred rather than added to. */
  [data-act="toggleLang"],
  [data-act="toggleNav"],
  [data-act="setFilterAll"],
  [data-act="setFilterOpen"],
  [data-act="setFilterBlocked"],
  [data-act="setV1"],
  [data-act="setV2"],
  [data-act="setV3"],
  [data-act="toggleRole"],
  [data-act^="setTab"] {
    min-height: 44px !important;
    padding-top: 0 !important;
    padding-bottom: 0 !important;
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
  }

  /* Without a floor, a short label plus a 44px height renders as a circle
     rather than a pill, next to its wider siblings. */
  [data-act="toggleLang"],
  [data-act="setFilterAll"],
  [data-act="setV1"],
  [data-act="setV2"],
  [data-act="setV3"] {
    min-width: 56px !important;
  }

  /* The wordmark is a link, and at rest it is only as tall as its text. */
  #lm-root a[href="#top"] {
    min-height: 44px !important;
    align-items: center !important;
  }

  /* Footer links sit in a 12px-gap grid at text height. Give each a real target
     and drop the gap, so the column grows by less than the padding adds.

     The target is sized to the word, not the column. A full-width row is a 334px
     by 44px hit area for the word "Team", which reads as a button nobody drew and
     makes the list feel like a menu. fit-content keeps the 44px height where the
     thumb needs it and takes the width back. */
  footer a {
    min-height: 44px !important;
    /* short labels like "Work" come out under 44 wide once sized to content */
    min-width: 44px !important;
    display: inline-flex !important;
    width: fit-content !important;
    align-items: center !important;
  }

  footer > div:first-child > div + div {
    gap: 2px !important;
  }

  /* ---- the mobile menu ----
     It is the only navigation on a phone, so it gets the weight of one: rows
     sized like the buttons that opened them, separated rather than stacked flush,
     and a panel that fills the bar it drops out of. */
  #lm-navmenu > div {
    padding: 10px !important;
    gap: 4px !important;
    border-radius: 26px !important;
    /* a short viewport, a landscape phone above all, cannot seat six rows:
       cap the panel to what is left under the bar and let it scroll */
    max-height: calc(100svh - 88px) !important;
    overflow-y: auto !important;
    overscroll-behavior: contain !important;
  }

  /* The rows carry their own display:flex inline, but the call to action is a
     plain block relying on line-height to sit centred. Once the vertical padding
     goes it needs real centring, so every row is made flex here. */
  #lm-navmenu a {
    display: flex !important;
    align-items: center !important;
    font-size: 16px !important;
    min-height: 54px !important;
    padding: 0 18px !important;
    border-radius: 16px !important;
  }

  /* the call to action closes the list rather than sitting flush against it */
  #lm-navmenu a:last-child {
    justify-content: center !important;
    margin-top: 10px !important;
    min-height: 56px !important;
    font-size: 15px !important;
  }

  /* Contact details are links now, and a 19px or 32px row is not a thumb target.
     Sized to the address rather than the column, same as the footer. */
  #about a[href^="mailto"],
  #about a[href^="tel"],
  #contact a[href^="mailto"],
  #contact a[href^="tel"] {
    min-height: 44px !important;
    display: inline-flex !important;
    width: fit-content !important;
    max-width: 100% !important;
    align-items: center !important;
  }

  /* ---- form fields ----
     iOS Safari zooms the viewport when a focused field is under 16px, which
     leaves the page scrolled sideways after the keyboard closes. */
  input,
  textarea,
  select {
    font-size: 16px !important;
  }

  /* ---- breathing room ----
     The artboard's gutters and vertical rhythm are tuned for the desktop canvas
     and read as cramped once the columns collapse. */
  section:not(#top),
  footer {
    padding-left: 28px !important;
    padding-right: 28px !important;
  }

  section:not(#top) {
    padding-top: 104px !important;
    padding-bottom: 104px !important;
  }

  /* stacked cards need more room between them than side-by-side ones did */
  [style*="minmax(220px,1fr)"],
  [style*="minmax(230px,1fr)"],
  [style*="minmax(250px,1fr)"],
  [style*="minmax(260px,1fr)"],
  [style*="minmax(280px,1fr)"],
  [style*="minmax(290px,1fr)"],
  [style*="minmax(300px,1fr)"] {
    gap: 34px !important;
  }

  /* A card tuned to a desktop column is tight once it spans the screen. */
  [style*="border-radius:18px"][style*="padding:clamp(26px"],
  [style*="border-radius:18px"][style*="padding:clamp(24px"],
  [style*="border-radius:18px"][style*="padding:clamp(22px"],
  [style*="border-radius:14px"][style*="padding:24px"],
  [style*="border-radius:14px"][style*="padding:28px"] {
    padding: 30px !important;
  }

  /* Nothing goes between a heading and its rule. Every h2 is margin:0 and the
     rule carries its own 9px offset, and it reads as the heading's underline, so a
     margin on the h2 pushes it away from the word it belongs to. Room below the
     block comes from the wrapper instead.

     That 9px is a fixed value tuned against 34px type. The heading clamps down to
     23px here while the gap does not, so it lands proportionally half again as
     large: measured 0.70 of the type size against 0.58 on desktop, and a rule
     that far off its heading stops reading as an underline. Scaled back to sit
     just inside the desktop ratio. Only the top is touched: each rule carries a
     different bottom margin, and those still separate the block from what
     follows. */
  [data-litrule] {
    margin-top: 5px !important;
  }

  /* The centred section intros sit closer to their headings than they should. */
  [style*="text-align:center"][style*="margin-bottom:48px"],
  [style*="text-align:center"][style*="margin-bottom:44px"] {
    margin-bottom: 60px !important;
  }

  /* ---- legibility ----
     The artboard's micro-labels bottom out below 10px, which is under any
     readable floor on a phone. The 10px+ steps are left as drawn. */
  [style*="font-size:8.5px"],
  [style*="font-size:9px"] {
    font-size: 10.5px !important;
  }
}

/* ---- the thread, moved onto the compositor ----
   Painting the rail from JS means the main thread is always chasing a page the
   compositor has already moved, which reads as jitter during a fast flick, and
   the faster the scroll, the further behind it falls. A scroll-driven animation
   is evaluated on the compositor instead, so the light tracks the page exactly,
   at any speed, without the main thread involved at all.

   app.js still measures the geometry and feeds it in as custom properties, and
   skips its own painting wherever this is supported. */
@keyframes lm-thread-fill {
  from { transform: scaleY(0); }
  to   { transform: scaleY(1); }
}

@keyframes lm-thread-head {
  from { transform: translateY(0); opacity: 0; }
  2%   { opacity: 1; }
  98%  { opacity: 1; }
  to   { transform: translateY(var(--thread-len, 0px)); opacity: 0; }
}

@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    [data-thread-fill],
    [data-thread-head] {
      animation-timeline: scroll(root block);
      animation-range: var(--thread-from, 0px) var(--thread-to, 100%);
      animation-timing-function: linear;
      animation-fill-mode: both;
    }

    [data-thread-fill] { animation-name: lm-thread-fill; }
    [data-thread-head] { animation-name: lm-thread-head; }
  }
}

/* Without scroll-driven animations app.js writes the transform instead, and on a
   phone those writes land at whatever rate the main thread can manage mid-flick.
   A transition hands the interpolation between them back to the compositor, so
   the light glides rather than stepping. */
@supports not (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    [data-thread-fill],
    [data-thread-head] {
      transition: transform 240ms linear, opacity 240ms linear;
    }
  }
}

/* ---- the team row ----
   Three cards over the artboard's auto-fit minmax(260px,1fr) fit two columns
   between roughly 700 and 1000px, which leaves the third adrift on a row of its
   own. Pin three columns above the breakpoint the rest of this file uses, and
   stack outright below it. An orphan is worse than either, and three cards
   squeezed into a 768px tablet leave a portrait barely 150px wide. */
@media (min-width: 901px) {
  #about [style*="minmax(260px,1fr)"] {
    grid-template-columns: repeat(3, 1fr) !important;
  }
}

@media (max-width: 900px) {
  #about [style*="minmax(260px,1fr)"] {
    grid-template-columns: 1fr !important;
  }
}

/* ---- the work row ----
   Three cards over the artboard's auto-fit minmax(290px,1fr) fit two columns on a
   tablet, which leaves the third adrift on a row of its own. Same call as the team
   row above: stack outright below the breakpoint rather than orphan one. The row was
   four cards when the artboard set this up, and four split evenly. */
@media (max-width: 900px) {
  #work [style*="minmax(290px,1fr)"] {
    grid-template-columns: 1fr !important;
  }
}

/* ---- hero balance ----
   The artboard pins the hero block to 13% of a portrait frame, which on a tall
   phone leaves it crowded against the nav bar. Dropping it clear of the bar also
   opens up the gap above the converging beams. */
@media (max-width: 820px) {
  [data-type] {
    top: 21% !important;
  }
}

/* A landscape phone has no height to give away, so it only gets a nudge. */
@media (max-width: 900px) and (max-height: 560px) and (orientation: landscape) {
  [data-type] {
    top: 9% !important;
  }

  /* Tighter menu rows fit more of the list before the panel has to scroll. */
  #lm-navmenu a {
    min-height: 46px !important;
    font-size: 15px !important;
  }

  #lm-navmenu a:last-child {
    min-height: 48px !important;
    margin-top: 6px !important;
  }
}

/* ---- notched displays ----
   index.html ships viewport-fit=cover so the dark hero fills the cutout instead
   of letterboxing. Everything that carries content is inset back out of it. */
@supports (padding: max(0px, env(safe-area-inset-left))) {
  @media (max-width: 900px) {
    #lm-root > div[style*="position:fixed"] {
      padding-left: max(16px, env(safe-area-inset-left)) !important;
      padding-right: max(16px, env(safe-area-inset-right)) !important;
    }

    /* #top is the full-bleed hero and keeps its own edges. */
    section:not(#top),
    footer {
      padding-left: max(28px, env(safe-area-inset-left)) !important;
      padding-right: max(28px, env(safe-area-inset-right)) !important;
    }

    footer {
      padding-bottom: calc(36px + env(safe-area-inset-bottom)) !important;
    }
  }
}
