/* ─────────────────────────────────────────────────────────────────────────────
   Mobile corrections.

   Loaded after the theme's inline <style>, so equal-specificity rules win on
   order and !important is only used where the rule being corrected used it.
   ───────────────────────────────────────────────────────────────────────────── */

/* ══ MODALS ═══════════════════════════════════════════════════════════════════
   Every panel was sized in vh. On iOS Safari and Chrome for Android vh resolves
   to the viewport with the browser chrome RETRACTED, so a 95vh panel is taller
   than what you can actually see: the top of the card hides behind the URL bar
   and the bottom behind the toolbar, which is what the screenshots showed.

   dvh tracks the visible area as the chrome comes and goes. The vh line stays
   first as the fallback for anything that does not know dvh.                  */

.bo-lb__box      { height: 95vh; height: 95dvh; }
.bo-pipe-lb__box { max-height: 88vh; max-height: 88dvh; }
.bo-pipe-lb__card{ max-height: 86vh; max-height: 86dvh; }
.bo-pipe-lb__box video, .bo-pipe-lb__media video, .bo-pipe-lb__media img {
  max-height: 76vh !important; max-height: 76dvh !important;
}
.cpc-lb__box video { max-height: 76vh !important; max-height: 76dvh !important; }

/* The overlay itself keeps clear of the notch and the home indicator. */
.bo-lb, .cpc-lb, .bo-pipe-lb, .bp-modal {
  /* 28px, not 12: this is the gap that keeps the panel clear of the URL bar,
     and modal-ui.css sizes the panel against this box so the close pill below it
     is always on screen too. */
  padding-top:    max(28px, calc(env(safe-area-inset-top) + 20px));
  padding-bottom: max(12px, env(safe-area-inset-bottom));
  padding-left:   max(12px, env(safe-area-inset-left));
  padding-right:  max(12px, env(safe-area-inset-right));
}

/* Scrolling inside a modal was reaching the page behind it and then stopping
   at the ends. Contain it, and let the panel itself scroll when it is taller
   than the viewport. */
.bo-lb__box, .cpc-lb__box, .bo-pipe-lb__box, .bo-pipe-lb__card, .bp-modal__card {
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

@media (max-width: 900px) {
  /* The close buttons used to be corrected here — three pushed outside the panel
     corner, the fourth pulled inside it. They are now relocated out of the panel
     entirely by modal-ui.js and styled as one pill by modal-ui.css, because
     hanging a child outside a scroll container is what clipped them and what put
     6px of horizontal scroll inside every modal. */

  /* A media lightbox on a phone was a 94vh black panel with a 16:9 video
     floated in the middle of it — two thick black bars and a small video. Let
     the panel take the height of what is in it, and put the close button on
     the media rather than on the bar above it. */
  .bo-lb__box {
    height: auto !important;
    width: min(96vw, 100%);
    justify-content: center;
  }
  .bo-lb__stage { flex: 0 1 auto !important; background: transparent !important; }
  .bo-lb__box video {
    height: auto !important;
    max-height: calc(100dvh - 150px) !important;
    object-fit: contain;
  }
  .bo-lb__box img { max-height: calc(100dvh - 150px) !important; }
  .cpc-lb__box, .bo-pipe-lb__box { overflow-y: auto; }
}

/* ══ #3 THE HERO'S 300px OF NOTHING ═══════════════════════════════════════════
   Measured on an iPhone 13: the CTA row ends at 868 and the logo strip starts
   at 1108 — 240px of empty black, from a 300px padding-bottom on the hero
   container that a desktop layout needs and a stacked one does not. */
@media (max-width: 900px) {
  .elementor-element-b4499db { padding-bottom: 140px !important; }
}

/* ══ #6 THE PLATFORM MARKS IN THE ONE-BUILDING CARD ═══════════════════════════
   "Files arrive", "The twin" and "Handover" each carried a Scope3D or Clickpad
   lockup beside the heading. On a phone the row wraps and the mark reads as
   part of the title. The step names carry it on their own. */
@media (max-width: 768px) {
  .exp-timelines .exp-logo { display: none !important; }
}

/* ══ #7 THE ONE-BUILDING PRODUCT TOGGLE ═══════════════════════════════════════
   The unselected half was unreadable. Three separate causes:

     - a blanket opacity:.55 on .exp-tab, which dims the fill, the border AND the
       wordmark together — the Clickpad indigo at 55% on a near-black pill is what
       made it vanish;
     - the theme fills .exp-tab with var(--jkd-border), 12.5% white on #1c1c1c;
     - the intended active tint here was dead code. .exp-tab.active is (0,2,0) and
       lost to the theme's .jkd-experience .exp-tabs .exp-tab.active at (0,4,0),
       so the selected half is green only because the ::after knob shows through
       the transparent button. That is fine, and is left alone.

   The ink follows the ground rather than the scheme. The knob is #00ff85 in BOTH
   schemes, so the selected half always wants the dark-ink artwork; the unselected
   half sits on the panel and follows the page theme like every other logo. */
.exp-tab.active .exp-tab-logo--dark        { display: none  !important; }
.exp-tab.active .exp-tab-logo--light       { display: block !important; }
.exp-tab:not(.active) .exp-tab-logo--dark  { display: block !important; }
.exp-tab:not(.active) .exp-tab-logo--light { display: none  !important; }
:root[data-color-scheme="light"] .exp-tab:not(.active) .exp-tab-logo--dark  { display: none  !important; }
:root[data-color-scheme="light"] .exp-tab:not(.active) .exp-tab-logo--light { display: block !important; }

.exp-tab {
  /* the stronger of the two border tokens, so the unselected half reads as a
     half rather than as empty track */
  border: 1px solid var(--jkd-border-active, rgba(255,255,255,.3));
  transition: border-color .2s ease, background-color .2s ease;
}
/* the theme's only hover rule changes `color`, and these buttons contain no text,
   so there was no hover feedback at all that this is a switch */
.exp-tab:not(.active):hover,
.exp-tab:not(.active):focus-visible {
  background-color: color-mix(in srgb, var(--jkd-heading) 15%, transparent) !important;
  border-color: rgb(var(--jkd-accent-rgb, 0,255,133)) !important;
}
.exp-tab.active,
.exp-tab[aria-selected="true"] {
  /* the knob supplies the fill; a border on top of it just draws a green line
     on green */
  border-color: transparent !important;
}

/* and the panel takes the height of its content rather than clipping it */
@media (max-width: 900px) {
  .exp-timelines, .exp-timelines .exp-timeline { height: auto !important; min-height: 0 !important; }
  .exp-timelines .exp-item { overflow: visible !important; }
}

/* ══ #2 FOOTER EXTERNAL LINKS ═════════════════════════════════════════════════
   The ↗ was a bare character, which several mobile fonts render as a coloured
   emoji arrow. It is a mask-image glyph now, inheriting the link's own colour,
   and the rows are set slightly tighter so they hold one line. */
.ft-nav a .bo-ext,
.bo-plat__link .bo-ext,
.bo-cta__ext.bo-ext {
  display: inline-block;
  width: .72em; height: .72em;
  margin-left: .32em;
  vertical-align: -.02em;
  background: currentColor;
  opacity: .62;
  -webkit-mask: var(--bo-ext-mask) center / contain no-repeat;
          mask: var(--bo-ext-mask) center / contain no-repeat;
}
.ft-nav a:hover .bo-ext { opacity: 1; }
:root {
  --bo-ext-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 17 17 7M9 7h8v8'/%3E%3C/svg%3E");
}
/* The three footer columns share 390px on a phone, which leaves each about
   110px for labels that measure up to 149px — "Scope3D pricing" was running
   50px past the viewport and being clipped. nowrap was making that worse, not
   better: the row cannot hold one line at this width, so let the columns take
   the full width and stack instead, and keep the type a touch smaller. */
@media (max-width: 900px) {
  .ft-nav a { font-size: .92em; letter-spacing: -.005em; white-space: normal; }
  .ft-bottom { flex-wrap: wrap; }
  .ft-bottom .ft-menu { flex: 1 1 46%; min-width: 46%; }
}
@media (max-width: 560px) {
  .ft-bottom .ft-menu { flex: 1 1 100%; min-width: 100%; }
}

/* The tail under the legal row, which on a phone is just dead space. The 50px
   it inherits is not declared against `.site-footer` anywhere greppable — the
   footer is assembled at runtime — so this is one of the few places !important
   is the honest tool rather than a shortcut. */
@media (max-width: 900px) {
  footer.site-footer, .site-footer { padding-bottom: 24px !important; }
}

/* ══ #5 THE MOBILE DRAWER'S BOTTOM ════════════════════════════════════════════
   The overlay was 100vh, so its last row — the social icons — sat under
   Chrome's toolbar and the LinkedIn mark was never reachable. dvh plus the
   safe-area inset gives it back, and the cloned theme toggle sits above it. */
#nav-bar .nav-overlay {
  height: 100vh; height: 100dvh;
  max-height: 100vh; max-height: 100dvh;
  padding-bottom: calc(18px + env(safe-area-inset-bottom));
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* ══ CASE STUDY: THE FRAME MOVES TO THE STORY IT ILLUSTRATES ══════════════════
   The single screenshot or clip used to sit in the Summary beside a two-
   sentence precis; it now sits above the Overview body, which is where the
   thing it shows is actually described. The Summary keeps a way in. */
.project-content .bo-ovm--article { margin: 22px 0 26px; }
.project-content .content-top { scroll-margin-top: 96px; }
.bo-readmore { margin-top: 22px; }
@media (max-width: 900px) {
  .bo-readmore { margin-top: 18px; width: 100%; justify-content: center; }
  /* the nav is 80px from the top un-scrolled at this width, 65 scrolled */
  .project-content .content-top { scroll-margin-top: 84px; }
  .project-content .bo-ovm--article { margin-bottom: 20px; }
}

/* The drawer's close, taken off the animation's critical path. !important is
   deliberate: gsap animates `opacity` as an inline style, and an inline
   declaration is exactly what an author !important is defined to beat.

   pointer-events:none on the overlay alone does NOT do it. The theme carries
   `#nav-bar .nav-overlay .menu-item.is-active{pointer-events:auto!important}`,
   so the children opt themselves back in and keep catching taps through a
   parent that has opted out — measured: with the parent at none and opacity 0,
   elementFromPoint at the centre of the screen still returned a menu <a>.
   Hence the descendant rules, the last of which is (1,4,0) against the theme's
   (1,3,0) so it actually wins, and `visibility` as the backstop: it is
   inherited and cannot be opted out of the same way. It flips only after the
   fade, via a 0s transition with a delay, so the close still animates. */
#nav-bar .nav-overlay.bo-drawer-closing {
  opacity: 0 !important;
  visibility: hidden !important;
  transition: opacity .16s ease, visibility 0s linear .16s !important;
}
#nav-bar .nav-overlay.bo-drawer-closing,
#nav-bar .nav-overlay.bo-drawer-closing *,
#nav-bar .nav-overlay.bo-drawer-closing .menu-item.is-active {
  pointer-events: none !important;
}

/* ══ THE DRAWER'S THEME TOGGLE ════════════════════════════════════════════════
   Ported verbatim from the main branch, markup and rules both, so the two sites
   share one control rather than two that drift. The theme binds every
   [data-color-scheme-toggle] at init and syncs their aria-pressed and .is-light
   together, so a second one in the drawer needs no JavaScript of its own. */
#nav-overlay .builtos-drawer-theme{display:flex;align-items:center;justify-content:center;gap:14px;margin:0 0 20px}
#nav-overlay .builtos-drawer-theme__label{font-size:15px;font-weight:500;opacity:.85;letter-spacing:.01em;color:var(--jkd-heading)}
#nav-overlay .builtos-drawer-theme .jkd-scheme-toggle{display:flex!important;align-items:center;justify-content:center;cursor:pointer;border:none!important;box-shadow:none!important;outline:none!important;padding:0;opacity:1!important;visibility:visible!important}
#nav-overlay .builtos-drawer-theme .scheme-toggle-track{align-items:center;background:var(--jkd-border);border-radius:100px;display:flex;flex-shrink:0;height:26px;padding:0 3px;position:relative;transition:background .35s ease;width:48px}
#nav-overlay .builtos-drawer-theme .scheme-toggle-knob{align-items:center;background:var(--jkd-inverted);border-radius:50%;display:flex;flex-shrink:0;height:20px;justify-content:center;position:relative;transform:translateX(0);transition:transform .4s cubic-bezier(.34,1.4,.64,1),background .35s ease;width:20px}
#nav-overlay .builtos-drawer-theme .scheme-icon{color:var(--jkd-bg);position:absolute;transition:opacity .22s ease,transform .3s cubic-bezier(.34,1.4,.64,1)}
#nav-overlay .builtos-drawer-theme .scheme-icon--moon{height:12px;width:12px;opacity:1;transform:scale(1) rotate(0deg)}
#nav-overlay .builtos-drawer-theme .scheme-icon--sun{font-size:12px;line-height:1;opacity:0;transform:scale(0) rotate(-45deg)}
#nav-overlay .builtos-drawer-theme .jkd-scheme-toggle.is-light .scheme-toggle-knob{transform:translateX(22px)}
#nav-overlay .builtos-drawer-theme .jkd-scheme-toggle.is-light .scheme-icon--moon{opacity:0;transform:scale(0) rotate(45deg)}
#nav-overlay .builtos-drawer-theme .jkd-scheme-toggle.is-light .scheme-icon--sun{opacity:1;transform:scale(1) rotate(0deg)}
/* ══ A BLOG POST THAT HAS A CLIP PLAYS IT ═════════════════════════════════════
   Eight of the thirteen posts describe something the demo library actually
   shows, and those swap their stock photograph for that clip. It inherits the
   figure's own shape so nothing else about the header moves, and carries
   data-bo-autoplay rather than autoplay, so video-perf.js holds it at its
   poster until it is on screen. The other five keep the photograph — no clip
   in the library shows a QR tag being scanned or a text message arriving. */
.post-thumbnail__vid {
  display: block;
  width: 100%;
  height: auto;
  border-radius: inherit;
  background: #0b0d0c;
}

/* ══ THE PHASE DOTS KEEP THEIR COLOUR ═════════════════════════════════════════
   Forced :hover on the selected dot, measured: background goes from the accent
   to rgba(127,127,127,.12) while the text stays --jkd-on-accent, which is
   #0f0f0f. Near-black on a grey wash over a dark page is the "impossible to
   read" — and on a phone a tap leaves :hover stuck there afterwards, so it is
   the state you are left looking at rather than one you pass through.

   The selected dot now holds the accent through hover, focus and active. */
.jkd-process-steps .steps-nav .step-dot.is-active,
.jkd-process-steps .steps-nav .step-dot.is-active:hover,
.jkd-process-steps .steps-nav .step-dot.is-active:focus,
.jkd-process-steps .steps-nav .step-dot.is-active:focus-visible,
.jkd-process-steps .steps-nav .step-dot.is-active:active {
  background-color: var(--jkd-accent) !important;
  border-color: var(--jkd-accent) !important;
  color: var(--jkd-on-accent) !important;
}
/* and where there is no real pointer, an unselected dot should not keep a
   hover skin it can never leave */
@media (hover: none) {
  .jkd-process-steps .steps-nav .step-dot:not(.is-active):hover {
    background-color: transparent;
    border-color: var(--jkd-border);
    color: var(--jkd-heading);
  }
}

/* ══ THE EXPAND ICON BECOMES A REAL TARGET ════════════════════════════════════
   .bmd__play is pointer-events:none, so a tap anywhere on a card — the icon
   included — lands on the <video> and the whole card behaves as one button.
   That is why "tap to pause" and "tap the expand icon" could not both be had:
   there was only ever one target.

   On touch the icon takes its own hits. A tap on it bubbles to the
   [data-bo-open] button and opens the lightbox, exactly as before; a tap
   anywhere else on the picture reaches the video, where mobile-fix.js pauses it
   rather than opening anything. Pointer devices are untouched — there the
   whole card opens, which is what a cursor and a hover state already promise. */
@media (hover: none) {
  .bmd__play { pointer-events: auto !important; }
}
