/* ═══════════════════════════════════════════════════════════════
   Vertwo Premium — Works archive page styles (v0.25.0)
   ───────────────────────────────────────────────────────────────
   Verbatim port of /html/04-works.html lines 3420-6402 (the
   page-specific style block that follows fonts/global/components).
   Sections covered, in order of appearance below:
     #works-hero        - aliased through universal vw-hero-* layer
                          (page-hero.php emits .ah-* alias classes
                           since v0.20.0); any leftover .ah-* tweaks
                          here just refine the works-page palette.
     #works-gallery     - "our works" refracted-glass slideshow
                          (.stage / .track / .glass-wall / .hero /
                           .meta + supporting variables)
     #portfolio-module  - "the full archive" .pm-* grid with
                          filter chips, mobile <select>, sort, A4
                          card geometry, 3D tilt vars, gloss, spine
     #clients           - .vt-clients head + .vt-grid (logos
                          rendered server-side via
                          vertwo_render_client_logo() — verbatim
                          .vt-clients shell stays)
     #testimonials      - .vt / .vt-slot / .vt-panel / .vt-marquee
                          (panels + tabs come from
                           vertwo_get_testimonials())
     #bokami            - .bk-* original-IP showcase: hero blobs,
                          polaroid + cast, parallax slider, IG
                          feed slot (Behold widget swap-in via
                          [data-bk-ig-slot] container), social
                          cards, lightbox
     #work-with-us      - .wwu-* closing CTA (mirrors works-hero
                          atmosphere palette, brighter)

   Note on inline imagery:
   The .pm-card cover textures, .bk-polaroid-img background, and
   .bk-gal-slide background-image rules carry inline data:image
   payloads from the prototype. They are kept verbatim here so the
   theme has zero new asset-file dependencies for round 4D. When
   Vic uploads real Bokami photography (open task #10 in
   handoff-post-v0.24.5) we'll graduate those rules to use ACF
   image URLs.

   PATCH (v0.25.0) overlay sits at the very bottom of this file,
   matching the cadence in page-about.css PATCH 12 and
   page-services.css PATCH (v0.23.0).
   ═══════════════════════════════════════════════════════════════ */

/* ───── WORKS HERO — verbatim from About hero, copy swapped for Works ───── */
#works-hero{
  position:relative;
  min-height:50vh;
  height:auto;
  display:flex;align-items:center;justify-content:center;
  overflow:hidden;
  background:#0a0a0c;
  isolation:isolate;
  padding:120px 24px 70px;
}
.ah-bg{
  position:absolute;inset:-10% -5%;
  z-index:0;
  pointer-events:none;
  filter:blur(70px) saturate(140%);
  opacity:.85;
  will-change:transform;
}
.ah-blob{
  position:absolute;border-radius:50%;
  mix-blend-mode:screen;
}
.ah-blob--a{
  width:55vw;height:55vw;left:-10%;top:-10%;
  background:radial-gradient(circle at 30% 30%, var(--blob-color, #F27A0F) 0%, transparent 60%);
  animation:ahDriftA 22s ease-in-out infinite alternate;
}
.ah-blob--b{
  width:50vw;height:50vw;right:-12%;top:8%;
  background:radial-gradient(circle at 70% 50%, var(--blob-color, #EC2F7A) 0%, transparent 60%);
  animation:ahDriftB 28s ease-in-out infinite alternate;
}
.ah-blob--c{
  width:62vw;height:62vw;left:18%;bottom:-22%;
  background:radial-gradient(circle at 50% 50%, var(--blob-color, #F5A623) 0%, transparent 60%);
  animation:ahDriftC 34s ease-in-out infinite alternate;
}
@keyframes ahDriftA{
  0%{transform:translate3d(0,0,0) scale(1)}
  100%{transform:translate3d(8vw,4vh,0) scale(1.12)}
}
@keyframes ahDriftB{
  0%{transform:translate3d(0,0,0) scale(1.05)}
  100%{transform:translate3d(-6vw,5vh,0) scale(1)}
}
@keyframes ahDriftC{
  0%{transform:translate3d(0,0,0) scale(1)}
  100%{transform:translate3d(-4vw,-6vh,0) scale(1.15)}
}
.ah-grain{
  position:absolute;inset:0;z-index:1;
  pointer-events:none;
  opacity:.10;
  background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' /%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size:200px 200px;
  mix-blend-mode:overlay;
}
.ah-veil{
  position:absolute;inset:0;z-index:2;pointer-events:none;
  background:
    radial-gradient(ellipse at 50% 80%, rgba(0,0,0,0.55) 0%, rgba(0,0,0,0) 60%),
    linear-gradient(180deg, rgba(10,10,12,0.0) 0%, rgba(10,10,12,0.45) 100%);
}
.ah-inner{
  position:relative;z-index:3;
  max-width:920px;width:100%;
  text-align:center;
  color:#fff;
}
.ah-eyebrow{
  font-size:0.72rem;font-weight:800;letter-spacing:0.2em;
  text-transform:uppercase;color:var(--eyebrow);
  margin-bottom:18px;display:inline-block;
}
.ah-title{
  font-family:var(--font-title);
  font-size:clamp(2.2rem, 4.4vw, 4.4rem);
  font-weight:900;letter-spacing:-0.045em;line-height:1.02;
  color:#fff;
  margin-bottom:22px;
}
.ah-title em{font-style:normal;color:var(--orange)}
.ah-sub{
  font-size:clamp(0.95rem,1.25vw,1.1rem);
  font-weight:300;line-height:1.7;
  color:rgba(255,255,255,0.75);
  max-width:580px;margin:0 auto;
}
@media(max-width:600px){
  #works-hero{min-height:42vh;padding:100px 20px 50px}
}


  /* ════════════════════════════════════════════════════════════
     WORKS GALLERY — minimal wrapper around the source-spec gallery.
     Dark background, top padding to clear floating nav, bottom padding
     so the gallery's dark base doesn't sit flush against the next
     (white) section. The gallery itself is fully self-contained
     in vertwo-refracted-glass-FIX-final — no overrides on .stage,
     .glass-wall, or #gl-hero needed.
     ════════════════════════════════════════════════════════════ */
  #works-gallery {
    background: #0d0d0d;
    padding-top: 80px;
    padding-bottom: 60px;
  }
  /* Section header — title sits just above the stage's
     'DRAG OR SCROLL' hint band. */
  #works-gallery .wg-head {
    text-align: center;
    max-width: 720px;
    margin: 0 auto 8px;
    padding: 0 24px;
    color: #fff;
  }
  @media (max-width: 720px) {
    #works-gallery {
      padding-top: 60px;
      padding-bottom: 40px;
    }
    #works-gallery .wg-head {
      margin-bottom: 6px;
      padding: 0 20px;
    }
  }


/* ───── refracted glass gallery ───── */

  /* :root keeps the structural tokens JS writes to (--cover-h, --panel-height-pct).
     Brand tokens scoped to .stage to avoid collision with --vt-clients
     in the universal stylesheet. Otherwise this stylesheet is verbatim
     from vertwo-refracted-glass-FIX-final.html. */
  :root {
    --stage-h:           640px;
    --panel-height-pct:  80;
    --cover-h:           512px;
  }
  .stage {
    --vt-orange: #F27A0F;
    --vt-gold:   #F5A623;
    --vt-ink:    #0d0d0d;
    --vt-paper:  #FFFFFF;
    --dim:       rgba(255, 255, 255, 0.18);
    color: var(--vt-paper);
    font-family: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", system-ui, sans-serif;
  }
  /* (resets and body{} rule omitted — universal stylesheet handles those.) */

  /* ── Top hint — absolutely positioned INSIDE the stage's top
     dark band (above the wall, in the vignette zone) ─────── */
  .hint {
    position: absolute;
    top: 14px;
    left: 0; right: 0;
    z-index: 8;
    text-align: center;
    font-size: 0.7rem;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.55);
    pointer-events: none;     /* don't block drag */
  }
  /* scoped to .stage to avoid leaking to other kbd elements on the page */
  .stage kbd {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 38px; height: 22px; padding: 0 8px;
    border: 1px solid var(--dim);
    border-radius: 4px;
    color: rgba(255, 255, 255, 0.85);
    font-family: inherit;
    font-size: 0.65rem; font-weight: 700;
    letter-spacing: 0.14em; text-transform: uppercase;
    background: rgba(255, 255, 255, 0.03);
    margin: 0 6px;
  }

  /* ── Stage ──────────────────────────────────────────────── */
  .stage {
    position: relative;
    width: 100%;
    height: var(--stage-h);
    overflow: hidden;
    cursor: grab;
    touch-action: pan-y;
    user-select: none;
    -webkit-user-select: none;
    isolation: isolate;
  }
  .stage.dragging { cursor: grabbing; }

  /* ── Track + covers — covers fit pane height ────────────── */
  .track {
    position: absolute;
    top: 50%; left: 0;
    display: flex;
    transform: translate3d(0, -50%, 0);
    will-change: transform;
    z-index: 1;
  }
  .cover {
    height: var(--cover-h);
    aspect-ratio: 210 / 297;
    flex-shrink: 0;
    overflow: hidden;
    background: #1a1a1a;
    position: relative;
  }
  .cover img {
    width: 100%; height: 100%;
    object-fit: cover; object-position: center top;
    display: block;
    pointer-events: none;
    -webkit-user-drag: none;
    /* Background covers slightly dimmed + a hint of softness so the
       hero (which loads its own un-blurred <img>s) reads sharp by
       contrast. The 0.8px blur barely registers per pane but adds
       atmospheric softness across the wall. */
    filter: brightness(0.85) contrast(1.03) blur(0.8px);
  }

  /* Chrome mobile gets BRIGHTER covers (above 1.0 = boosted) so the
     wall has more life on small screens where panes are narrower. */
  @media (max-width: 720px) {
    body:not(.no-refraction) .cover img {
      filter: brightness(1.05) contrast(1.0) blur(0.8px);
    }
  }

  /* ── Glass wall — spans full stage width edge-to-edge ──── */
  .glass-wall {
    position: absolute;
    top:    calc((100% - (var(--panel-height-pct) * 1%)) / 2);
    bottom: calc((100% - (var(--panel-height-pct) * 1%)) / 2);
    left: 0;
    right: 0;
    z-index: 2;
    display: flex;
    pointer-events: none;
  }
  .glass {
    position: relative;
    height: 100%;
    flex-shrink: 0;
    overflow: hidden;
    /* width set by JS, derived from baseW (=stageW/ratiosSum) × ratio */
  }
  .glass-refract {
    position: absolute; inset: 0; z-index: 0;
    -webkit-backdrop-filter: blur(0px);
            backdrop-filter: blur(0px);
    isolation: isolate;
    will-change: filter;
  }
  .glass-tint {
    position: absolute; inset: 0; z-index: 1;
    background-color: rgba(255, 255, 255, 0.03);
    pointer-events: none;
  }
  .glass-edge {
    position: absolute; inset: 0; z-index: 2;
    box-shadow:
      inset 0 0 0 1px rgba(255, 255, 255, 0.10),
      inset 1px 0 0 rgba(255, 255, 255, 0.05),
      inset -1px 0 0 rgba(0, 0, 0, 0.10);
    pointer-events: none;
  }

  /* ── Desaturation overlay ────────────────────────────────
     100% mono at edges → 30% mono at center via alpha mask
     on a backdrop-filter: grayscale(1) layer. */
  .desat-overlay {
    position: absolute;
    top: 0; left: 0; right: 0; bottom: 0;
    z-index: 3;
    pointer-events: none;
    -webkit-backdrop-filter: grayscale(1);
            backdrop-filter: grayscale(1);
    -webkit-mask-image: linear-gradient(to right,
      rgba(0,0,0,1)    0%,
      rgba(0,0,0,1)    12%,
      rgba(0,0,0,0.30) 38%,
      rgba(0,0,0,0.30) 62%,
      rgba(0,0,0,1)    88%,
      rgba(0,0,0,1)    100%);
            mask-image: linear-gradient(to right,
      rgba(0,0,0,1)    0%,
      rgba(0,0,0,1)    12%,
      rgba(0,0,0,0.30) 38%,
      rgba(0,0,0,0.30) 62%,
      rgba(0,0,0,1)    88%,
      rgba(0,0,0,1)    100%);
  }
  /* Mobile gets less monochrome — the center band stays more colorful
     since the wall is narrower and there's less mass to balance. */
  @media (max-width: 720px) {
    .desat-overlay {
      -webkit-mask-image: linear-gradient(to right,
        rgba(0,0,0,1)    0%,
        rgba(0,0,0,1)    14%,
        rgba(0,0,0,0.18) 38%,
        rgba(0,0,0,0.18) 62%,
        rgba(0,0,0,1)    86%,
        rgba(0,0,0,1)    100%);
              mask-image: linear-gradient(to right,
        rgba(0,0,0,1)    0%,
        rgba(0,0,0,1)    14%,
        rgba(0,0,0,0.18) 38%,
        rgba(0,0,0,0.18) 62%,
        rgba(0,0,0,1)    86%,
        rgba(0,0,0,1)    100%);
    }
  }

  /* ── Wall-spanning duotone gradient (blue → orange) ────── */
  .wall-tint {
    position: absolute;
    z-index: 4;
    pointer-events: none;
    background: linear-gradient(to right,
      rgba(30, 90, 190, 0.75)   0%,
      rgba(30, 90, 190, 0.55)   16%,
      rgba(30, 90, 190, 0.20)   34%,
      rgba(0, 0, 0, 0)          47%,
      rgba(0, 0, 0, 0)          53%,
      rgba(242, 122, 15, 0.20)  66%,
      rgba(242, 122, 15, 0.55)  84%,
      rgba(242, 122, 15, 0.75)  100%);
    mix-blend-mode: overlay;
  }

  /* ── Side vignettes ─────────────────────────────────────── */
  .vignette-overlay {
    position: absolute;
    top: 0; left: 0; right: 0; bottom: 0;
    z-index: 5;
    pointer-events: none;
    background: linear-gradient(to right,
      rgba(0, 0, 0, 0.95)  0%,
      rgba(0, 0, 0, 0.65)  4%,
      rgba(0, 0, 0, 0.25)  10%,
      rgba(0, 0, 0, 0)     18%,
      rgba(0, 0, 0, 0)     82%,
      rgba(0, 0, 0, 0.25)  90%,
      rgba(0, 0, 0, 0.65)  96%,
      rgba(0, 0, 0, 0.95)  100%);
  }

  /* ── Hero placeholder — A4 portrait, thin outline, no rounding,
     slightly smaller than covers, centered on STAGE ─────── */
  .hero {
    position: absolute;
    z-index: 6;
    pointer-events: none;
  }
  .hero-frame {
    position: relative;
    width: 100%;
    height: 100%;
    overflow: hidden;
    border-radius: 0;
    background: #060606;
    box-shadow:
      /* Inner highlight (keeps edges crisp against the white outline) */
      0 1px 0 rgba(255, 255, 255, 0.08) inset,
      /* Tight close-shadow that sells the lift */
      0 6px 18px -6px rgba(0, 0, 0, 0.65),
      /* Mid-distance ambient shadow */
      0 20px 50px -18px rgba(0, 0, 0, 0.85),
      /* Deep wide ambient shadow — this is what makes the card "float" */
      0 50px 130px -30px rgba(0, 0, 0, 0.98),
      /* Very wide soft halo so the hero detaches from the wall behind */
      0 80px 180px -60px rgba(0, 0, 0, 0.6);
    isolation: isolate;
  }
  .hero-frame::before {
    content: "";
    position: absolute; inset: 0;
    border: 1px solid rgba(255, 255, 255, 0.55);
    pointer-events: none;
    z-index: 5;
  }
  /* Orange pulse outline — fired on every hero swap. JS adds .pulsing
     to .hero-frame, the animation runs, then JS removes the class so
     it can fire again on the next swap. */
  .hero-frame::after {
    content: "";
    position: absolute; inset: 0;
    pointer-events: none;
    z-index: 6;
    border: 2px solid rgba(242, 122, 15, 0);
    box-shadow: 0 0 0 0 rgba(242, 122, 15, 0);
  }
  .hero-frame.pulsing::after {
    animation: hero-orange-pulse 950ms cubic-bezier(0.2, 0.8, 0.3, 1);
  }
  @keyframes hero-orange-pulse {
    0% {
      border-color: rgba(242, 122, 15, 0);
      box-shadow:
        0 0 0  0 rgba(242, 122, 15, 0),
        0 0 0  0 rgba(242, 122, 15, 0),
        inset 0 0 0  0 rgba(242, 122, 15, 0);
    }
    18% {
      border-color: rgba(242, 122, 15, 1);
      /* Three layers of outer glow at increasing radii — gives a
         soft falloff halo (close-bright, mid-warm, far-faint) instead
         of a single hard ring. Plus a subtle inner glow for depth. */
      box-shadow:
        0 0 22px  3px rgba(242, 122, 15, 0.85),
        0 0 60px  8px rgba(242, 122, 15, 0.45),
        0 0 110px 16px rgba(242, 122, 15, 0.20),
        inset 0 0 30px 0 rgba(242, 122, 15, 0.30);
    }
    100% {
      border-color: rgba(242, 122, 15, 0);
      box-shadow:
        0 0 0  0 rgba(242, 122, 15, 0),
        0 0 0  0 rgba(242, 122, 15, 0),
        inset 0 0 0  0 rgba(242, 122, 15, 0);
    }
  }
  .hero-layer {
    position: absolute; inset: 0;
    opacity: 0;
    transition: opacity 0.55s cubic-bezier(0.4, 0, 0.2, 1);
    will-change: opacity;
    z-index: 1;
  }
  .hero-layer.active { opacity: 1; z-index: 2; }
  .hero-backdrop {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
    filter: blur(32px) brightness(0.55) saturate(1.15);
    transform: scale(1.15);
    user-select: none;
    -webkit-user-drag: none;
  }
  .hero-main {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
    user-select: none;
    -webkit-user-drag: none;
  }

  /* ── Meta strip — absolutely positioned INSIDE the stage's bottom
     dark band (below the wall, in the vignette zone) ─────── */
  .meta {
    position: absolute;
    bottom: 12px;
    left: 0; right: 0;
    z-index: 8;
    display: flex; flex-direction: column;
    align-items: center; gap: 4px;
    text-align: center;
    pointer-events: none;
  }
  .meta-title {
    font-size: 1.35rem;
    font-weight: 900;
    letter-spacing: -0.02em;
    line-height: 1.1;
    color: #fff;
    text-transform: lowercase;
    transition: opacity 0.25s ease;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 80vw;
  }
  .meta-jp {
    font-size: 0.78rem;
    font-weight: 400;
    letter-spacing: 0.04em;
    line-height: 1.3;
    color: rgba(255, 255, 255, 0.55);
    transition: opacity 0.25s ease;
    font-family: "Hiragino Sans", "Yu Gothic", "Meiryo", sans-serif;
  }
  .meta.swapping .meta-title,
  .meta.swapping .meta-jp { opacity: 0; }

  @media (max-width: 720px) {
    .meta-title { font-size: 1.15rem; }
    .meta-jp    { font-size: 0.7rem;  }
  }
  @media (max-width: 560px) {
    .meta-title { font-size: 1rem; }
    .meta-jp    { font-size: 0.66rem; letter-spacing: 0.02em; }
  }

  @media (prefers-reduced-motion: reduce) {
    .hero-layer, .meta-title, .meta-jp { transition: none; }
  }

  /* ════════════════════════════════════════════════════════════
     SAFARI / NO-REFRACTION FALLBACK
     Per-pane blur is set inline by JS from hand-tuned arrays
     (heaviest at the far edges, lightest near the center). A
     uniform glass-thickness highlight gradient on each pane
     gives strip-by-strip 3D edge presence regardless of its
     blur depth. Plus reduced duotone tint and lighter vignette
     so the layered overlays don't compound into visual noise.
     ════════════════════════════════════════════════════════════ */
  body.no-refraction .glass-refract {
    /* Per-pane blur set inline by JS via BLUR_DESKTOP / TABLET / MOBILE.
       The glass-thickness highlight here stays uniform — every pane
       gets the same edge highlight regardless of its blur amount. */
    background: linear-gradient(90deg,
      rgba(255, 255, 255, 0.16) 0%,
      rgba(255, 255, 255, 0.00) 28%,
      rgba(255, 255, 255, 0.00) 72%,
      rgba(255, 255, 255, 0.16) 100%);
  }
  body.no-refraction .glass-tint {
    /* Skip frost layer's white tint in fallback — the glass-refract
       gradient already adds the highlights we need. */
    background-color: transparent;
  }
  body.no-refraction .glass-edge {
    box-shadow:
      inset 0 0 0 1px rgba(255, 255, 255, 0.10),
      inset 1px 0 0 rgba(255, 255, 255, 0.06),
      inset -1px 0 0 rgba(0, 0, 0, 0.16);
  }
  /* Duotone tint at ~50% of the previous strength so it adds color
     without dominating. */
  body.no-refraction .wall-tint {
    background: linear-gradient(to right,
      rgba(30, 90, 190, 0.55)   0%,
      rgba(30, 90, 190, 0.42)   16%,
      rgba(30, 90, 190, 0.18)   34%,
      rgba(0, 0, 0, 0)          47%,
      rgba(0, 0, 0, 0)          53%,
      rgba(242, 122, 15, 0.18)  66%,
      rgba(242, 122, 15, 0.42)  84%,
      rgba(242, 122, 15, 0.55)  100%);
  }
  /* Lighter vignette — the per-pane heavy blur was adding darkness
     of its own; now that blur is uniform and light, this can be too. */
  body.no-refraction .vignette-overlay {
    background: linear-gradient(to right,
      rgba(0, 0, 0, 0.65)  0%,
      rgba(0, 0, 0, 0.35)  4%,
      rgba(0, 0, 0, 0.12)  10%,
      rgba(0, 0, 0, 0)     20%,
      rgba(0, 0, 0, 0)     80%,
      rgba(0, 0, 0, 0.12)  90%,
      rgba(0, 0, 0, 0.35)  96%,
      rgba(0, 0, 0, 0.65)  100%);
  }
  /* Parallax disabled in this pass — was contributing to the noise. */


/* ───── portfolio module ───── */

/* ════════════════════════════════════════
   VERTWO PORTFOLIO MODULE — v2 (2026)
   ─ FLIP-animated filter
   ─ Hardcover-book card treatment
   ─ Mouse-tracked 3D tilt (CSS-var driven)
   ════════════════════════════════════════ */

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

/* @property declarations — typed CSS custom properties.
   Critical: this is what makes --tilt-x/y/--lift-z transition smoothly.
   Without @property, custom properties default to <*> syntax (any string),
   and CSS can't interpolate strings. Declaring as <angle>/<length> turns
   them into real animatable types — the .pm-book transform eases in
   lockstep from one tilt to the next. */
@property --tilt-x { syntax: '<angle>';  initial-value: 0deg; inherits: true; }
@property --tilt-y { syntax: '<angle>';  initial-value: 0deg; inherits: true; }
@property --lift-z { syntax: '<length>'; initial-value: 0px;  inherits: true; }

#portfolio-module{
  --orange:#F27A0F;
  --gold:#F5A623;
  --black:#1A1A1A;
  --white:#fff;
  --off:#F5F5F7;
  --mid:#6E6E73;
  --line:#E5E5E7;
  --line-soft:#EFEFF2;
  --pm-max:1280px;

  /* Type stacks. Replace 'Neue Haas Grotesk Display' import in prod.
     Falls back to Helvetica Neue stack which is visually closest. */
  --font-display:'Neue Haas Grotesk Display','Neue Haas Grotesk Display Pro','Helvetica Neue',Helvetica,Arial,sans-serif;
  --font-body:'Inter',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;

  /* Easing — matches Key Persons module. The strong ease-in-out
     (.83,0,.17,1) is the "buttery card movement" curve: slow to start,
     fast through the middle, gentle decel. Key for grid reflows. */
  --ease:      cubic-bezier(.83,0,.17,1);   /* card position changes */
  --ease-out:  cubic-bezier(.22,1,.36,1);   /* micro-interactions, hovers */
  --dur-card:  720ms;   /* matches kp-card */
  --dur-fade:  480ms;   /* exit fade-out — slowed from 380, was too quick on Safari */
  --dur-enter: 640ms;   /* new entry fade-in */

  /* Tilt vars (--tilt-x/y/--lift-z) are declared via @property at the top
     of this file and transition on .pm-card. Initial values come from
     @property, so no fallback declarations needed here. */
}

html{scroll-behavior:smooth}
/* (body{} rule stripped — universal stylesheet handles page-wide reset) */

/* Scroll reveal — preserved from design system */
.r{opacity:0;transform:translateY(28px);transition:opacity .75s cubic-bezier(.22,1,.36,1),transform .75s cubic-bezier(.22,1,.36,1)}
.r.in{opacity:1;transform:none}
.d1{transition-delay:.06s}
.d2{transition-delay:.12s}
.d3{transition-delay:.18s}

/* ══════════════════════════════════════════════════════════════
   PORTFOLIO SECTION
   ══════════════════════════════════════════════════════════════ */
#portfolio-module{
  background:var(--white);
  padding:120px 32px;
  position:relative;
}
.pm-wrap{max-width:var(--pm-max);margin:0 auto}

/* Intro */
.pm-intro{max-width:680px;margin-bottom:64px}
.pm-eyebrow{
  font-family:var(--font-body);
  font-size:0.72rem;font-weight:800;
  letter-spacing:0.18em;text-transform:uppercase;
  color:var(--eyebrow);
  margin-bottom:14px;
}
.pm-title{
  font-family:var(--font-display);
  font-size:clamp(2.2rem,4.6vw,3.4rem);
  font-weight:900;
  letter-spacing:-0.035em;
  line-height:1.02;
  color:var(--black);
}
.pm-body{
  font-size:1.0625rem;
  font-weight:300;
  color:var(--mid);
  line-height:1.65;
  margin-top:18px;
  max-width:560px;
}

/* ─── Toolbar ───
   v0.32.96 — Sticky-floating glass bar, design ported VERBATIM from
   news-archive.css .st-filter (lines 399-416). The outer .pm-toolbar
   becomes the full-bleed sticky surface (negative margin pushes past
   the #portfolio-module padding so the glass extends edge-to-edge);
   the new .pm-toolbar-inner re-centers content at max-width:1200px.
   Pre-v0.32.96 the toolbar was a plain flex row with a bottom border;
   that left the filter controls inaccessible the moment the user
   scrolled the grid into view. The sticky bar keeps them at hand
   throughout the archive browse — same UX as the news archive. */
.pm-toolbar{
  position:sticky;top:88px;z-index:50;
  /* v0.32.97 — opacity 0.85 → 0.72 (more glassy, Vic's request)
     + drop shadow so the floating bar reads as a layer above the
     scrolled content. Same values applied to .st-filter (news) and
     .pmo-toolbar (works other-services) for visual consistency. */
  background:rgba(245,245,247,0.72);
  backdrop-filter:saturate(180%) blur(20px);
  -webkit-backdrop-filter:saturate(180%) blur(20px);
  border-top:1px solid var(--line);
  border-bottom:1px solid var(--line);
  box-shadow:0 10px 28px -14px rgba(0,0,0,0.18);
  /* #portfolio-module has padding:120px 32px on desktop, so the bar
     bleeds -32px on each side to extend edge-to-edge. Responsive
     blocks below re-match the bleed to the parent's narrower padding. */
  margin:0 -32px 48px;
  padding:14px 32px;
}
.pm-toolbar-inner{
  max-width:1200px;margin:0 auto;
  display:flex;align-items:center;
  gap:24px;flex-wrap:wrap;
}
.pm-filters{
  display:flex;align-items:center;
  gap:6px;flex-wrap:wrap;flex:1;min-width:0;
}
.pm-filter-label{
  font-size:0.68rem;font-weight:700;
  letter-spacing:0.14em;text-transform:uppercase;
  color:var(--mid);
  margin-right:4px;
  white-space:nowrap;
}
.pm-chip{
  position:relative;
  padding:7px 16px;
  border-radius:980px;
  border:1px solid var(--line);
  background:transparent;
  font-family:var(--font-body);
  font-size:0.8125rem;
  font-weight:500;
  color:var(--black);
  cursor:pointer;
  transition:color .25s,border-color .25s,background .25s;
  white-space:nowrap;
  -webkit-tap-highlight-color:transparent;
}
.pm-chip:hover{border-color:#cfcfd4;color:var(--orange)}
.pm-chip:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(242,122,15,0.3)}
.pm-chip[aria-pressed="true"]{
  background:var(--orange);
  border-color:var(--orange);
  color:#fff;
}
.pm-chip[aria-pressed="true"]:hover{color:#fff}
.pm-chip-count{
  display:inline-block;margin-left:6px;
  font-size:0.7rem;font-weight:600;
  color:var(--mid);
  font-variant-numeric:tabular-nums;
}
.pm-chip[aria-pressed="true"] .pm-chip-count{color:rgba(255,255,255,0.7)}

.pm-sort-wrap{
  display:flex;align-items:center;gap:8px;
  flex-shrink:0;
}
.pm-sort-label{
  font-size:0.68rem;font-weight:700;
  letter-spacing:0.14em;text-transform:uppercase;
  color:var(--mid);
}
.pm-sort{
  appearance:none;-webkit-appearance:none;
  font-family:var(--font-body);
  font-size:0.8125rem;font-weight:500;
  color:var(--black);
  background:transparent
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6' fill='none'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%231A1A1A' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")
    no-repeat right 12px center;
  border:1px solid var(--line);
  border-radius:980px;
  padding:7px 32px 7px 14px;
  cursor:pointer;
  transition:border-color .2s;
}
.pm-sort:hover,.pm-sort:focus{border-color:#cfcfd4;outline:none}

/* Mobile filter dropdown — hidden by default, shown at narrow widths.
   See media queries below — we toggle .pm-filters / .pm-filter-mobile
   at the same breakpoint where the chip row would otherwise wrap. */
.pm-filter-mobile{
  display:none;
  align-items:center;
  gap:8px;
  flex-shrink:0;
}

/* Result count */
.pm-count{
  font-size:0.8125rem;
  font-weight:400;
  color:var(--mid);
  margin-bottom:28px;
  font-variant-numeric:tabular-nums;
}
.pm-count strong{color:var(--black);font-weight:600}

/* ════════════════════════════════════════
   GRID
   ════════════════════════════════════════ */
.pm-grid{
  display:grid;
  grid-template-columns:repeat(4,1fr);
  gap:36px 28px;
  position:relative;
}

/* ════════════════════════════════════════
   CARD — the "hardcover book"
   Layered structure:
     .pm-card           (perspective container for the tilt)
       .pm-book         (rotates in 3D — receives the tilt)
         .pm-pageblock  (right-edge paper detail — peeks past the cover)
         .pm-cover      (front face — image + spine + gloss + meta)
   Drop shadow + accent-colored hover glow live as multi-layer
   box-shadow stacks on .pm-cover. The spine is painted as a flat
   decoration on .pm-cover's left edge — the card's tilt sells the
   dimension; no Z-stacking gymnastics.
   ════════════════════════════════════════ */
/* v0.28.15 - .pm-card-link wraps .pm-book in the WP archive markup
   (the prototype 04-works.html had .pm-card > .pm-book directly with
   no <a> wrapper). The intermediate <a> breaks the perspective chain
   from .pm-card down to .pm-book - perspective propagates to direct
   3D-transformed children only, and any non-preserve-3d parent in
   between flattens the transform back to 2D. That's why .pm-card's
   tilt looked like a skew/squash instead of a true perspective rotation
   (Vic feedback: "the current tilt angle is skewing only, the prototype
   and the other services covers are tilting in perspective").

   The .pmo-card chain doesn't have this issue because the rotateY/X
   transform lives ON .pmo-card-link itself (the perspective parent's
   direct child). For .pm-card we keep the transform on .pm-book (per
   prototype), so .pm-card-link needs preserve-3d to pass the
   perspective through to .pm-book. display:block to ensure the <a>
   participates in the 3D context as a block. */
.pm-card-link{
  display:block;
  transform-style:preserve-3d;
}

.pm-card{
  position:relative;
  cursor:pointer;
  /* Per-card perspective for the book tilt. NO transform-style:preserve-3d
     — the previous version had it on every card, forcing Safari to maintain
     18 nested 3D rendering contexts at all times even when cards weren't
     interacted with. That's why filter fade-outs lagged: snapshotting
     14 separate 3D scenes is far slower than 18 flat 2D layers.
     perspective alone is enough — .pm-book's rotateY/X/translateZ still
     work in 3D because the parent's perspective creates the viewing
     context. preserve-3d would only matter if .pm-book's children had
     their own 3D transforms, which they don't. */
  perspective:1000px;
  perspective-origin:50% 40%;
  /* small bottom padding gives box-shadow some room without
     overflowing into the next grid row */
  padding:0 0 14px;
  /* Single source of truth for tilt + lift animation. JS sets target
     values for --tilt-x/y; :hover changes --lift-z. Both interpolate
     smoothly because of the @property declarations above. The .pm-book
     transform expression evaluates new vars each frame — no separate
     transform transition needed, no double-buffered animations. */
  --lift-z:0px;
  transition:
    --tilt-x .3s cubic-bezier(.22,1,.36,1),
    --tilt-y .3s cubic-bezier(.22,1,.36,1),
    --lift-z .35s cubic-bezier(.22,1,.36,1);
}

@media (hover:hover) and (pointer:fine){
  .pm-card:hover{ --lift-z:32px; }
}

/* ground/lift shadow + colored hover glow now live as box-shadow stacks
   on .pm-cover (see below). No separate shadow elements to render —
   box-shadow is GPU-only, never repainted, and the layer count drops
   significantly. Critical for Safari smoothness during filter fades. */

/* book wrapper that holds tilt — A4 ratio.
   No own transform transition: --tilt-x/y/--lift-z are typed
   custom properties that transition on .pm-card, so each frame
   evaluates a new transform with smoothly-tweened var values. */
.pm-book{
  position:relative;
  aspect-ratio:1/1.414;
  transform:
    rotateY(var(--tilt-y))
    rotateX(var(--tilt-x))
    translateZ(var(--lift-z));
  will-change:transform;
  /* No transform-style:preserve-3d here either — the spine, vignette,
     gloss, and meta children stack via z-index in 2D and rotate WITH the
     book as a flat surface, which is exactly the visual we want. */
}

/* Right-edge page-block — the "paper" peek showing the book has thickness.
   Sits beneath .pm-cover at z-index:1, peeks 4-5px past cover's right edge.
   Subtle horizontal striations suggest folded pages. */
.pm-pageblock{
  position:absolute;
  top:2px;bottom:2px;right:-3px;
  width:6px;
  background:
    repeating-linear-gradient(
      to bottom,
      #f4ecdc 0px, #f4ecdc 1.5px,
      #d9cfb8 1.5px, #d9cfb8 2px
    );
  border-radius:0 3px 3px 0;
  box-shadow:
    inset -1px 0 0 rgba(0,0,0,0.18),
    inset 1px 0 0 rgba(255,255,255,0.4),
    1px 1px 4px rgba(0,0,0,0.15);
  z-index:1;
  pointer-events:none;
}

/* cover — the actual front face */
.pm-cover{
  position:absolute;
  inset:0;
  border-radius:3px 6px 6px 3px;  /* tighter on the spine side, rounder on the open side */
  overflow:hidden;
  background:#1a1a1a;
  isolation:isolate;              /* Safari border-radius + transform safety */
  z-index:2;
  /* Multi-layer ambient drop shadow — fakes the soft "floating in space"
     feel that the previous filter:blur shadow had, but stays GPU-only.
     Three stacked shadows: close+sharp drop, mid ambient bleed, wide soft
     glow. Hover state below adds a deeper version + colored bloom. */
  box-shadow:
    inset 1px 0 0 rgba(0,0,0,0.4),
    inset -1px 0 0 rgba(255,255,255,0.06),
    inset 0 1px 0 rgba(255,255,255,0.10),
    inset 0 -1px 0 rgba(0,0,0,0.45),
    0 4px 10px -2px rgba(0,0,0,0.18),
    0 16px 32px -8px rgba(0,0,0,0.20),
    0 28px 56px -12px rgba(0,0,0,0.16);
  transition:box-shadow .45s cubic-bezier(.22,1,.36,1);
}

.pm-cover-img{
  position:absolute;inset:0;
  width:100%;height:100%;
  object-fit:cover;
  display:block;
  filter:saturate(1.02) contrast(1.02);
}

/* Spine — flat decoration painted on the cover's left edge.
   Dark gradient + thin highlight line creates the optical illusion
   of an extruded spine without Z-stacking. */
.pm-spine{
  position:absolute;
  top:0;bottom:0;left:0;
  width:18px;
  pointer-events:none;
  z-index:3;
  background:
    /* main shading: darker on far left, fades to nothing */
    linear-gradient(to right,
      rgba(0,0,0,0.65) 0%,
      rgba(0,0,0,0.32) 28%,
      rgba(0,0,0,0.12) 60%,
      rgba(0,0,0,0)    100%
    ),
    /* top + bottom corner shading on the spine itself */
    linear-gradient(to bottom,
      rgba(255,255,255,0.20) 0%,
      rgba(255,255,255,0)    6%,
      rgba(255,255,255,0)    94%,
      rgba(0,0,0,0.30)       100%
    );
}
.pm-spine::after{
  /* Embossed inner-fold highlight — single bright pixel-line */
  content:'';
  position:absolute;
  top:5%;bottom:5%;right:0;
  width:1px;
  background:linear-gradient(to bottom,
    transparent 0%,
    rgba(255,255,255,0.28) 22%,
    rgba(255,255,255,0.28) 78%,
    transparent 100%);
}

/* GLOSS LAYER — angle responds to tilt, mix-blend-mode: overlay for contrast.
   The bright sheen pivots ±27° as the cover tilts. Now safe on Safari because:
   - No preserve-3d anywhere (was the real culprit, fixed earlier)
   - No transform on the gloss element itself
   - No background-position changes
   - Just a gradient angle calc consuming the typed --tilt-x/y vars,
     which already transition on .pm-card via @property
   mix-blend-mode: overlay both brightens light areas of the cover AND
   darkens dark ones — adds contrast rather than just lightening like
   screen did. More punch on textured covers, more "polished" feel. */
.pm-gloss-tilt{
  position:absolute;inset:0;
  background:linear-gradient(
    calc(115deg + var(--tilt-y) * 3 - var(--tilt-x) * 1.2),
    rgba(255,255,255,0)    0%,
    rgba(255,255,255,0)    28%,
    rgba(255,255,255,0.13) 40%,
    rgba(255,255,255,0.55) 50%,
    rgba(255,255,255,0.13) 60%,
    rgba(255,255,255,0)    72%,
    rgba(255,255,255,0)    100%
  );
  mix-blend-mode:overlay;
  pointer-events:none;
  z-index:4;
}

/* gloss layer 3 — vignette so center pops, edges hold the form */
.pm-vignette{
  position:absolute;inset:0;
  background:
    radial-gradient(120% 90% at 50% 30%,transparent 55%,rgba(0,0,0,0.20) 100%),
    linear-gradient(to top,rgba(0,0,0,0.55) 0%,rgba(0,0,0,0.10) 32%,rgba(0,0,0,0) 55%);
  pointer-events:none;
  z-index:6;
}

/* metadata sits over the bottom of the cover */
.pm-meta{
  position:absolute;
  left:0;right:0;bottom:0;
  padding:18px 18px 16px 26px;   /* +8px left padding to clear the spine */
  z-index:7;
  pointer-events:none;
  /* v0.32.39 — flex column-reverse so the visual order top→bottom is:
     tagline (small italic, above) → title (big, at the very bottom).
     DOM order stays title-first/tagline-second (natural reading order
     for screen readers + the v0.32.37 ACF help text "appears under the
     title in cards" — semantically still "under" relative to the
     <h3> in the document).
     Why: with the legacy column-flow, a 2-line title pushed the tagline
     down behind the cover's bottom edge / vignette gradient so it
     looked invisible on real cards (Vic's "promise me three meals and
     naps, duke!" screenshot). Pinning the title to the visible bottom
     edge guarantees the tagline always renders ABOVE it and stays
     visible no matter how many lines the title takes. */
  display:flex;
  flex-direction:column-reverse;
  /* Pin gap explicitly (was implicit via margin-bottom on title +
     margin-top on tagline — irrelevant with column-reverse). */
  gap:4px;
}
.pm-genre{
  display:inline-block;
  padding:3px 9px;
  border-radius:980px;
  background:rgba(242,122,15,0.18);
  border:1px solid rgba(242,122,15,0.45);
  font-size:0.6rem;
  font-weight:700;
  letter-spacing:0.12em;
  text-transform:uppercase;
  color:var(--gold);
  margin-bottom:8px;
}
.pm-card-title{
  font-family:var(--font-display);
  font-size:1.0625rem;
  font-weight:700;
  letter-spacing:-0.02em;
  line-height:1.2;
  color:#fff;
  /* v0.32.39 — margin-bottom dropped (used to be 8px), gap on .pm-meta
     now controls spacing. */
  margin-bottom:0;
  text-shadow:0 1px 8px rgba(0,0,0,0.4);
  /* v0.32.38 — keep 2-line clamp so very long titles can't push the
     .pm-meta block past the top of the cover. With v0.32.39's
     column-reverse layout the title now sits at the bottom edge and
     the tagline rides above it; clamping the title is still useful as
     a runaway-text guard. */
  display:-webkit-box;
  -webkit-line-clamp:2;
  -webkit-box-orient:vertical;
  overflow:hidden;
}
.pm-card-meta-row{
  display:flex;align-items:center;
  gap:8px;flex-wrap:wrap;
}
.pm-year{
  font-size:0.72rem;
  font-weight:500;
  color:rgba(255,255,255,0.6);
  font-variant-numeric:tabular-nums;
}
.pm-meta-dot{
  width:3px;height:3px;border-radius:50%;
  background:rgba(255,255,255,0.35);
}
.pm-client{
  display:inline-flex;align-items:center;gap:5px;
  background:rgba(255,255,255,0.14);
  border:1px solid rgba(255,255,255,0.22);
  border-radius:4px;
  padding:3px 9px;
  font-size:0.7rem;font-weight:600;
  letter-spacing:0.02em;
  color:#fff;
  transition:background .25s,border-color .25s;
}
.pm-client-dot{
  width:6px;height:6px;border-radius:1px;
  background:var(--orange);flex-shrink:0;
}

/* v0.32.35 — top-left client pill on webtoon covers. Mirrors
   .pmo-card-service-pill on other-services cards (same brand-orange pill
   with backdrop-blur). Replaces the previous bottom-meta-row cluster
   per Vic ask: webtoon covers now show only the title at bottom + the
   client pill at top-left. The legacy `.pm-genre`, `.pm-year`,
   `.pm-meta-dot`, `.pm-client`, `.pm-client-dot`, `.pm-card-meta-row`
   rules above are now dead code (markup no longer emits them) — left
   in place as visual archeology, safe to clean up in a future pass. */
/* v0.32.37 — tagline line under the title on webtoon cards. ACF help text
   advertises "Appears under the title in cards". Sits below .pm-card-title
   inside .pm-meta (absolute-bottom block on the cover). Small, dim, italic
   so the title stays dominant. */
.pm-card-tagline{
  /* v0.32.39 — margin-top dropped (used to be 2px); .pm-meta gap owns
     the spacing now. */
  margin:0;
  /* v0.32.38 — bumped 0.72 → 0.82 per Vic ("make tagline a bit bigger,
     but smaller than title"). Still ~77% of .pm-card-title at 1.0625rem
     so hierarchy reads correctly. */
  font-size:0.82rem;
  font-weight:400;
  font-style:italic;
  color:rgba(255,255,255,0.86);
  line-height:1.3;
  text-shadow:0 1px 4px rgba(0,0,0,0.45);
  /* 1-line clamp on the tagline (v0.32.39 tightened from 2): with
     column-reverse, the tagline sits ABOVE the title; a 2-line tagline
     above a 2-line title would push the .pm-meta block too high. One
     line keeps the cover composition clean. */
  display:-webkit-box;
  -webkit-line-clamp:1;
  -webkit-box-orient:vertical;
  overflow:hidden;
}

.pm-client-pill{
  /* v0.32.36 — moved top-left → top-right per Vic. Matches .pmo-card-service-pill
     after its symmetric move in the same version. */
  position:absolute;top:10px;right:10px;z-index:7;
  display:inline-block;
  padding:4px 9px;border-radius:980px;
  background:rgba(242,122,15,0.92);color:#fff;
  font-family:var(--font-title);font-weight:700;font-size:0.6rem;
  letter-spacing:0.08em;
  text-transform:uppercase;
  backdrop-filter:blur(6px);
  -webkit-backdrop-filter:blur(6px);
  pointer-events:none;
}

/* ─── HOVER STATE ─── */
@media (hover:hover) and (pointer:fine){
  /* Multi-layer ambient drop + accent-colored bloom. The wide outer
     blurs do the soft 3D-floating work; the close drop keeps it
     grounded. All compositor-only — no filter, no DOM, no per-frame
     paint. */
  .pm-card:hover .pm-cover{
    box-shadow:
      inset 1px 0 0 rgba(0,0,0,0.4),
      inset -1px 0 0 rgba(255,255,255,0.06),
      inset 0 1px 0 rgba(255,255,255,0.10),
      inset 0 -1px 0 rgba(0,0,0,0.45),
      0 6px 14px -2px rgba(0,0,0,0.20),
      0 24px 50px -8px rgba(0,0,0,0.30),
      0 42px 80px -12px rgba(0,0,0,0.18),
      0 32px 70px -8px rgba(var(--accent-rgb,30,30,40),0.55);
  }
  .pm-card:hover .pm-client{
    background:rgba(242,122,15,0.28);
    border-color:rgba(242,122,15,0.6);
  }
  /* book lift now driven by --lift-z var on .pm-card:hover (above) */
}

/* ════════════════════════════════════════
   FILTER ANIMATION
   ─ THREE-PHASE choreography (no overlap):
     Phase 1: leavers fade out IN PLACE (380ms)
     Phase 2: leavers go display:none → grid reflows →
              survivors FLIP to new positions (720ms)
              Newcomers occupy their slots but stay invisible.
     Phase 3: newcomers fade in at their final positions (640ms)
   ─ All movement on cubic-bezier(.83,0,.17,1)
   ════════════════════════════════════════ */
.pm-card.is-hidden{display:none}

/* LEAVERS: stay in grid flow, just fade out. Same ease as FLIP (ease-in-out)
   so the leave feels deliberate, not snappy. */
.pm-card.is-leaving{
  animation:pmLeave var(--dur-fade) var(--ease) both;
  pointer-events:none;
}
@keyframes pmLeave{
  to{opacity:0;transform:scale(.94)}
}

/* PENDING ENTER: newcomer occupies its grid slot but is invisible.
   Holds the layout for survivor FLIP without showing yet. */
.pm-card.is-pending-enter{
  opacity:0;
  pointer-events:none;
}

/* ENTRANTS: cards fading in during phase 3 (after FLIP completes) */
.pm-card.is-entering{
  animation:pmEnter var(--dur-enter) var(--ease) both;
}
@keyframes pmEnter{
  from{opacity:0;transform:translateY(20px) scale(.94)}
  to{opacity:1;transform:none}
}

/* ════════════════════════════════════════════════════════════
   v0.32.134 — OTHER SERVICES filter animation (verbatim port of
   the .pm-card three-phase animation above with the .pmo- prefix).
   Durations and easing hardcoded (not via the --ease / --dur-*
   custom props from #portfolio-module above) because .pmo lives
   inside #portfolio-other, a sibling section without those vars
   in scope. Values copied 1:1 from the .pm definitions.
   ════════════════════════════════════════════════════════════ */
.pmo-card.is-leaving{
  animation: pmoLeave 480ms cubic-bezier(.83, 0, .17, 1) both;
  pointer-events: none;
}
@keyframes pmoLeave{
  to{ opacity: 0; transform: scale(.94); }
}
.pmo-card.is-pending-enter{
  opacity: 0;
  pointer-events: none;
}
.pmo-card.is-entering{
  animation: pmoEnter 640ms cubic-bezier(.83, 0, .17, 1) both;
}
@keyframes pmoEnter{
  from{ opacity: 0; transform: translateY(20px) scale(.94); }
  to  { opacity: 1; transform: none; }
}

/* No results state */
.pm-empty{
  display:none;
  text-align:center;
  padding:80px 24px;
  border:1px dashed var(--line);
  border-radius:18px;
  margin-top:24px;
}
.pm-empty.is-visible{display:block}
.pm-empty-title{
  font-family:var(--font-display);
  font-size:1.25rem;font-weight:700;
  letter-spacing:-0.02em;
  color:var(--black);
  margin-bottom:6px;
}
.pm-empty-body{
  font-size:0.875rem;font-weight:300;
  color:var(--mid);
}

/* ════════════════════════════════════════
   RESPONSIVE
   ════════════════════════════════════════ */
@media (max-width:1180px){
  .pm-grid{grid-template-columns:repeat(3,1fr);gap:32px 24px}
}

/* Filter chip → dropdown swap.
   v0.32.99 — breakpoint LOWERED from 1100 → 700. Was eating iPad
   portrait (768-1024px) which Vic showed gave "weird and very long"
   stretched-dropdown layout. With 700, iPad portrait keeps the
   desktop chips + sort dropdown row; only phones (≤700px) get the
   collapsed mobile dropdown layout. The genre chip count is small
   enough (typically 3-5) that they fit on one line at iPad widths.
   v0.32.97 — at this breakpoint we ALSO collapse the toolbar layout
   to two dropdowns side-by-side without separate label chips (mirrors
   news-archive .st-filter-mobile design at the same breakpoint).
   Hide the standalone "Filter" + "Sort" labels; let the dropdown
   placeholder text ("All Genres", "Latest first") carry the label
   information instead. */
@media (max-width:700px){
  .pm-filters{display:none}
  .pm-filter-mobile{display:flex; flex:1; min-width:140px}
  .pm-filter-mobile .pm-sort-label{display:none}
  .pm-sort-wrap{flex:1; min-width:140px}
  .pm-sort-wrap .pm-sort-label{display:none}
  .pm-filter-mobile .pm-sort,
  .pm-sort-wrap .pm-sort{width:100%}
  .pm-toolbar-inner{flex-wrap:nowrap; gap:8px}
}

@media (max-width:860px){
  #portfolio-module{padding:80px 24px}
  .pm-grid{grid-template-columns:repeat(2,1fr);gap:28px 18px}
  /* v0.32.96 — sticky bar bleeds to the narrower parent padding (24px). */
  .pm-toolbar{
    margin:0 -24px 32px;
    padding:12px 24px;
  }
  .pm-toolbar-inner{gap:12px}
  .pm-card-title{font-size:0.95rem}
  .pm-meta{padding:14px 12px 12px 16px}
  .pm-spine{width:10px}
}
@media (max-width:520px){
  #portfolio-module{padding:64px 18px}
  .pm-grid{grid-template-columns:repeat(2,1fr);gap:22px 14px}
  .pm-title{font-size:2rem}
  .pm-body{font-size:0.95rem}
  /* v0.32.96 — narrower parent padding (18px). */
  .pm-toolbar{
    margin:0 -18px 24px;
    padding:10px 18px;
  }
  .pm-toolbar-inner{gap:10px}
  .pm-filters{gap:6px}
  .pm-chip{padding:6px 12px;font-size:0.75rem}
  .pm-chip-count{display:none}
  .pm-card-title{font-size:0.8125rem;margin-bottom:5px}
  .pm-genre{font-size:0.55rem;padding:2px 7px;margin-bottom:5px}
  .pm-year{font-size:0.65rem}
  .pm-client{font-size:0.62rem;padding:2px 6px}
  .pm-meta{padding:10px 9px 9px 12px}
  .pm-spine{width:8px}
  .pm-cover{border-radius:3px 5px 5px 3px}
}

/* Reduced motion — disable tilt + heavy animations */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:0.01ms!important;
    animation-iteration-count:1!important;
    transition-duration:0.01ms!important;
    scroll-behavior:auto!important;
  }
  .pm-book{transform:none!important}
}

/* Touch devices — drop hover-dependent things */
@media (hover:none),(pointer:coarse){
  .pm-card{cursor:default}
}


/* ───── testimonials ───── */

    :root{
      --orange:#F27A0F; --ink:#1A1A1A; --ink-2:#6b6b70; --ink-3:#9a9aa0;
      --line-2:rgba(26,26,26,.14); --line-3:rgba(26,26,26,.22);
      --bg:#fff;
      --orange-glow:rgba(242,122,15,.32);
      --display:"Neue Haas Grotesk Display","Inter",-apple-system,BlinkMacSystemFont,system-ui,sans-serif;
      --body:"Inter",-apple-system,BlinkMacSystemFont,system-ui,sans-serif;

      --ease-mask:cubic-bezier(.55,0,.15,1);
      --ease-out:cubic-bezier(.2,.7,.2,1);
      --dur-mask:600ms;

      /* Marquee tuning */
      --rail-h:64px;             /* outer arrow row height */
      --pill-h:38px;             /* button pill height */
      --gate-h:68px;             /* hairline mask boundary — extends 2px above/below the rail for an architectural read */
      --rail-gap:14px;           /* gap between arrow and gate hairline */
    }
    *,*::before,*::after{box-sizing:border-box}
    /* (body{} rule stripped — universal stylesheet handles page-wide reset) */

    .vt{background:var(--bg);color:var(--ink);font-family:var(--body);
      padding:clamp(80px,10vw,160px) clamp(20px,5vw,80px);overflow:hidden}
    .vt__inner{max-width:1280px;margin:0 auto}

    /* ===== Eyebrow + header ===== */
    .vt-eyebrow{display:inline-flex;align-items:baseline;gap:14px;
      font-size:11px;font-weight:800;letter-spacing:.18em;text-transform:uppercase;
      color:var(--ink-3);margin-bottom:32px}
    .vt-eyebrow__rule{width:32px;height:1px;background:var(--eyebrow);
      display:inline-block;align-self:center}
    .vt-eyebrow__num{font-feature-settings:"tnum";color:var(--ink-2)}

    .vt-header{display:grid;grid-template-columns:1.3fr 1fr;gap:48px;
      align-items:end;margin-bottom:clamp(56px,7vw,96px)}
    .vt-header__title{font-family:var(--display);font-weight:900;
      font-size:clamp(40px,5.4vw,84px);line-height:.96;letter-spacing:-.025em;margin:0}
    .vt-header__sub{font-size:16px;line-height:1.55;color:var(--ink-2);
      max-width:42ch;margin:0}

    /* ===== Stage: the centered quote zone ===== */
    .vt-stage{position:relative;
      padding:clamp(40px,6vw,72px) 0 clamp(16px,2vw,28px);
      min-height:clamp(280px,32vw,420px);
      /* touch-action:pan-y lets the browser handle vertical page scroll while
         our pointer-event handler captures horizontal swipes for panel switch */
      touch-action:pan-y}

    /* Stack of panels — only one is active.
       v0.32.142 — scoped to section.vt so the rule no longer leaks onto
       the clients section's .vt-slot, which shares the same class name
       but is a CSS-grid item that doesn't want width:100% / position:relative
       imposed on top of its clients.css defaults. */
    section.vt .vt-slot{position:relative;width:100%}
    section.vt .vt-panel{position:absolute;inset:0;pointer-events:none;
      display:flex;flex-direction:column;justify-content:center;align-items:stretch}
    section.vt .vt-panel.is-active{pointer-events:auto}
    section.vt .vt-slot--measured{height:var(--slot-h)}

    /* Client logo, sits centered above the quote */
    .vt-feature__client{display:flex;justify-content:center;
      margin-bottom:28px;height:24px}
    .vt-feature__client .qline,
    .vt-feature__client .qline__inner{height:24px;line-height:24px}
    .vt-feature__logo{height:24px;display:inline-flex;align-items:center;
      color:var(--ink-2);font-family:var(--display);font-weight:700;
      font-size:13px;letter-spacing:.08em;text-transform:uppercase;
      white-space:nowrap;line-height:1}

    /* Centered quote body */
    .vt-feature__quote{font-family:var(--display);font-weight:700;
      font-size:clamp(26px,3vw,42px);line-height:1.25;letter-spacing:-.018em;
      color:var(--ink);margin:0 auto;max-width:32ch;
      text-align:center;position:relative}

    /* Decorative quote mark — stage-level, sits ABOVE the slot.
       Was previously a ::before pseudo on the quote, which collided with
       the client logo above it (the glyph at 1.6em with top:-.55em rose
       through the 28px gap and overlapped the SUNRISE/KODANSHA wordmark).
       Lifted out as its own element so it has guaranteed breathing room.

       Vertical metrics: padding-top nudges the glyph downward in its space,
       line-height:.55 crops the natural empty space below the glyph (the
       `"` character sits in the upper portion of any em-square; line-height
       of 1 leaves dead space below it that reads as extra gap), and a small
       margin-bottom completes the tightened relationship to the client logo. */
    .vt-feature__mark{
      display:block;text-align:center;
      padding-top:clamp(4px,0.5vw,8px);
      margin:0 auto clamp(8px,1vw,16px);
      font-family:var(--display);font-weight:900;
      font-size:clamp(40px,5vw,64px);line-height:.55;
      letter-spacing:-.04em;
      color:var(--orange);
      user-select:none;pointer-events:none}

    /* Author group — sits in normal flow, immediately below the quote,
       centered horizontally. Was previously absolute bottom-right of the
       stage; that read as "signature" but the user prefers it in the
       reading flow under the quote. */
    .vt-feature__author-wrap{margin:clamp(28px,3.5vw,44px) auto 0;
      display:flex;justify-content:center}
    .vt-feature__author-wrap > .qline{display:inline-block}
    .vt-feature__author{display:flex;align-items:center;gap:14px;
      justify-content:center;min-width:0;text-align:left}
    .vt-feature__avatar{position:relative;width:44px;height:44px;flex:0 0 44px;
      border-radius:999px;overflow:hidden;background:#f3f3f3;
      display:inline-flex;align-items:center;justify-content:center}
    .vt-feature__avatar::after{content:"";position:absolute;inset:0;
      box-shadow:inset 0 0 0 1px rgba(0,0,0,.06);border-radius:inherit;pointer-events:none}
    .vt-feature__avatar span{font-family:var(--display);font-weight:700;
      font-size:14px;letter-spacing:.02em;color:var(--ink);text-transform:uppercase}
    .vt-feature__avatar img{position:absolute;inset:0;width:100%;height:100%;
      object-fit:cover;background:#f3f3f3}
    .vt-feature__name{font-size:14px;font-weight:600;line-height:1.3;
      letter-spacing:-.005em;white-space:nowrap}
    .vt-feature__role{font-size:12px;color:var(--ink-2);line-height:1.35;
      white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

    /* ===== Line-mask state machine — preserved verbatim from v8 =====
       waiting (default) → active (in view) → leaving (exited above)
       After leaving, JS silently snaps back to waiting (no transition).
       Transforms don't affect layout, so heights remain stable throughout.
       overflow:hidden (not clip) avoids the compositor-layer hairline bug.
       translate3d forces GPU layer with integer-pixel snapping. */
    .qline{display:block;overflow:hidden;
      border:0;margin:0;padding:0}
    .qline--inline{display:inline-block;vertical-align:top}
    .qline__inner{display:block;
      transform:translate3d(0,105%,0);
      transition:transform var(--dur-mask) var(--ease-mask);
      border:0;margin:0;padding:0}
    .qline--inline > .qline__inner{display:inline-block;vertical-align:top}
    .vt-panel.is-active .qline__inner{transform:translate3d(0,0,0)}
    .vt-panel.is-leaving .qline__inner{transform:translate3d(0,-105%,0)}

    /* ===== Marquee rail — the navigator below the quote =====
       Layout: [arrow]──gap──[viewport]──gap──[arrow]
       The viewport carries the hairlines as ::before/::after pseudo-elements
       at left:0 and right:0. Because the viewport itself has overflow:hidden
       and the lines are absolutely positioned at its edges, the hairlines ARE
       the clip boundary — buttons literally disappear behind them. */
    .vt-marquee{position:relative;display:flex;align-items:center;
      gap:var(--rail-gap);height:var(--rail-h);
      margin-top:clamp(16px,2vw,28px)}

    /* Arrow buttons — far ends. Squircle shape (12px radius on 40px button)
       matches the standardized arrow style across Vertwo pages — see jobs,
       portfolios, and news page navigators. */
    .vt-marquee__arrow{all:unset;flex:0 0 40px;width:40px;height:40px;
      display:inline-flex;align-items:center;justify-content:center;
      border:1px solid var(--line-2);border-radius:12px;background:#fff;
      color:var(--ink-2);cursor:pointer;
      transition:border-color 220ms var(--ease-out),
                 color 220ms var(--ease-out),
                 background 220ms var(--ease-out),
                 box-shadow 220ms var(--ease-out),
                 transform 220ms var(--ease-out)}
    .vt-marquee__arrow:hover,
    .vt-marquee__arrow:focus-visible{
      border-color:var(--orange);background:var(--orange);color:#fff;
      box-shadow:0 6px 22px rgba(242,122,15,.45)}
    .vt-marquee__arrow:active{transform:scale(.94)}
    .vt-marquee__arrow svg{width:14px;height:10px;display:block}

    /* Viewport — hard-clipped at the hairlines horizontally, but glow
       box-shadows must escape vertically. overflow:hidden clips both axes
       (creates a scroll container), which butchered the orange hover glow.
       clip-path:inset(-60px 0) clips exactly at left:0 / right:0 (where
       the hairlines sit) while extending the clip rect 60px above and
       below — generous room for any pill's box-shadow to render.

       min-width:0 is critical: the flex track inside has ~3000px of natural
       content (8 buttons × ~200px × 2 clone passes). Without min-width:0,
       flexbox refuses to shrink the viewport below its content's min-content,
       and the viewport overflows the marquee, pushing the right arrow and
       rightmost buttons past the section's overflow:hidden clip. */
    .vt-marquee__viewport{flex:1 1 auto;height:var(--rail-h);
      min-width:0;
      position:relative;
      clip-path:inset(-60px 0);
      -webkit-clip-path:inset(-60px 0);
      /* Drag affordance + scroll cooperation. cursor:grab signals draggability
         on desktop; switches to grabbing on active. touch-action:pan-y allows
         vertical page scroll to pass through while we capture horizontal. */
      cursor:grab;
      touch-action:pan-y}
    .vt-marquee__viewport.is-grabbing{cursor:grabbing}
    .vt-marquee__viewport::before,
    .vt-marquee__viewport::after{
      content:"";position:absolute;top:50%;
      width:1px;height:var(--gate-h);
      background:var(--line-3);
      transform:translateY(-50%);
      pointer-events:none;
      z-index:2}
    .vt-marquee__viewport::before{left:0}
    .vt-marquee__viewport::after{right:0}

    /* Track — flex row, transform-driven */
    .vt-marquee__track{display:flex;align-items:center;gap:10px;
      height:100%;will-change:transform;padding:0}

    /* Pill buttons */
    .vt-marquee__btn{all:unset;flex:0 0 auto;
      display:inline-flex;align-items:center;gap:10px;
      height:var(--pill-h);padding:0 18px;
      border:1px solid var(--line-2);border-radius:999px;background:#fff;
      cursor:pointer;white-space:nowrap;
      font-family:var(--body);font-size:13px;font-weight:500;color:var(--ink-2);
      transition:border-color 240ms var(--ease-out),
                 color 240ms var(--ease-out),
                 background 240ms var(--ease-out),
                 box-shadow 240ms var(--ease-out)}
    .vt-marquee__btn-name{font-weight:600;color:var(--ink);
      font-family:var(--display);letter-spacing:.04em;text-transform:uppercase;
      font-size:12px}
    .vt-marquee__btn-sep{width:3px;height:3px;border-radius:999px;
      background:var(--line-3);flex:0 0 3px}
    .vt-marquee__btn-meta{font-size:12px;color:var(--ink-3);
      font-feature-settings:"tnum"}

    .vt-marquee__btn:hover,
    .vt-marquee__btn:focus-visible{
      border-color:var(--orange);
      color:var(--orange);
      box-shadow:0 6px 24px var(--orange-glow), 0 0 0 1px var(--orange) inset}
    .vt-marquee__btn:hover .vt-marquee__btn-name,
    .vt-marquee__btn:focus-visible .vt-marquee__btn-name{color:var(--orange)}
    .vt-marquee__btn:hover .vt-marquee__btn-sep,
    .vt-marquee__btn:focus-visible .vt-marquee__btn-sep{background:var(--orange)}
    .vt-marquee__btn:hover .vt-marquee__btn-meta,
    .vt-marquee__btn:focus-visible .vt-marquee__btn-meta{color:var(--orange)}

    .vt-marquee__btn.is-active{
      border-color:var(--ink);background:var(--ink);color:#fff;
      box-shadow:0 8px 28px rgba(0,0,0,.18)}
    .vt-marquee__btn.is-active .vt-marquee__btn-name{color:#fff}
    .vt-marquee__btn.is-active .vt-marquee__btn-sep{background:rgba(255,255,255,.45)}
    .vt-marquee__btn.is-active .vt-marquee__btn-meta{color:rgba(255,255,255,.7)}

    /* Hover/active conflict resolution: hover wins glow, active wins fill.
       When both apply, keep the dark fill + white text and add an orange ring
       outside. Without these explicit color overrides, the hover rule's orange
       text (lower specificity but later cascade) bleeds through. */
    .vt-marquee__btn.is-active:hover,
    .vt-marquee__btn.is-active:focus-visible{
      background:var(--ink);
      border-color:var(--ink);
      color:#fff;
      box-shadow:0 8px 28px rgba(0,0,0,.18), 0 0 0 2px var(--orange)}
    .vt-marquee__btn.is-active:hover .vt-marquee__btn-name,
    .vt-marquee__btn.is-active:focus-visible .vt-marquee__btn-name{color:#fff}
    .vt-marquee__btn.is-active:hover .vt-marquee__btn-sep,
    .vt-marquee__btn.is-active:focus-visible .vt-marquee__btn-sep{background:rgba(255,255,255,.45)}
    .vt-marquee__btn.is-active:hover .vt-marquee__btn-meta,
    .vt-marquee__btn.is-active:focus-visible .vt-marquee__btn-meta{color:rgba(255,255,255,.7)}

    /* ===== Reveal ===== */
    .r{opacity:0;transform:translateY(24px);
      transition:opacity 800ms var(--ease-out),transform 800ms var(--ease-out)}
    .r.in{opacity:1;transform:none}
    .r[data-delay="1"]{transition-delay:80ms}
    .r[data-delay="2"]{transition-delay:160ms}

    /* ===== Tablet portrait & below ===== */
    /* ===== Tablet portrait & below =====
       Mobile rhythm needs more breathing room than desktop — vertical reading
       benefits from clear separations between elements, where on desktop the
       same gaps would feel disjointed. Each spacing here is proportionally
       larger than its desktop counterpart. */
    @media (max-width:860px){
      .vt-header{grid-template-columns:1fr;gap:24px;align-items:start;
        margin-bottom:clamp(40px,6vw,64px)}

      .vt-stage{padding:clamp(32px,5vw,48px) 0 clamp(28px,4vw,40px);
        min-height:0}
      .vt-panel{position:absolute;inset:0;
        display:flex;flex-direction:column;justify-content:center;align-items:stretch}

      /* Mobile reads more naturally with left-aligned text in a single column.
         Center alignment was a desktop-stage decision that doesn't translate. */
      .vt-feature__mark{text-align:left;margin:0 0 clamp(16px,2.5vw,24px)}
      .vt-feature__client{justify-content:flex-start;
        margin-bottom:clamp(28px,4vw,36px)}
      .vt-feature__quote{font-size:clamp(22px,4.6vw,30px);max-width:none;
        text-align:left}
      .vt-feature__author-wrap{justify-content:flex-start;
        margin-top:clamp(16px,3vw,24px)} /* v0.32.213 — was 32-44, too airy on mobile */

      .vt-marquee{margin-top:clamp(16px,3vw,24px)} /* v0.32.213 — was 28-40 */

      /* Marquee compresses but keeps the architecture */
      :root{--rail-gap:10px}
      .vt-marquee__arrow{flex:0 0 36px;width:36px;height:36px;border-radius:11px}
      .vt-marquee__btn{padding:0 14px;font-size:12px}
      .vt-marquee__btn-name{font-size:11px}
    }

    @media (max-width:520px){
      .vt-feature__quote{font-size:20px;line-height:1.32}
      .vt-feature__avatar{width:38px;height:38px;flex:0 0 38px}
      .vt-feature__name{font-size:13px}
      .vt-feature__role{font-size:11px}
      /* On very narrow screens the meta text bloats the pills; keep just the
         company name and drop the project meta. The marquee still works. */
      .vt-marquee__btn-sep,
      .vt-marquee__btn-meta{display:none}
    }

    @media (prefers-reduced-motion:reduce){
      .qline__inner,
      .r,.vt-marquee__btn,.vt-marquee__arrow{transition:none!important}
      .r{opacity:1;transform:none}
    }
  

/* ───── bokami ───── */

*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth}

#bokami{
  --orange:#F27A0F;
  --gold:#F5A623;
  --black:#1A1A1A;
  --white:#fff;
  --mid:#6E6E73;
  --line:#D2D2D7;

  /* Bokami palette */
  --bk-blue:#CFE7F0;
  --bk-blue-deep:#A6D2E0;
  --bk-pink:#F8D4D4;
  --bk-pink-deep:#F2B8B8;
  --bk-yellow:#FFE9B0;
  --bk-yellow-deep:#FFD980;
  --bk-cream:#FFF8EE;
  --bk-cream-deep:#F4E8D6;
  --bk-ink:#5A2020;
  --bk-ink-soft:rgba(90,32,32,0.72);
  --bk-line:rgba(160,80,60,0.18);
}

/* (body{} rule stripped — bokami now scoped, inherits page-wide font/background) */
a{text-decoration:none;color:inherit}

.r{opacity:0;transform:translateY(28px);transition:opacity .75s ease,transform .75s ease}
.r.in{opacity:1;transform:none}
.d1{transition-delay:.08s}.d2{transition-delay:.16s}.d3{transition-delay:.24s}
.d4{transition-delay:.32s}.d5{transition-delay:.40s}

/* ═══════════════════════════════════════════════════
   #bokami — section frame (no border now)
═══════════════════════════════════════════════════ */
#bokami{
  position:relative;
  padding:88px 24px 96px;
  background:var(--bk-cream);
  overflow:hidden;
  isolation:isolate;
}
.bk-wrap{
  position:relative;
  z-index:3;
  max-width:1180px;
  margin:0 auto;
}

/* ─── Pastel blobs — soft color wash, lower presence ─── */
.bk-bg{
  position:absolute;inset:0;
  z-index:1;
  pointer-events:none;
  overflow:hidden;
}
.bk-blob{
  position:absolute;
  border-radius:50%;
  filter:blur(90px);
  will-change:transform;
  opacity:0.85;          /* main bg presence */
  mix-blend-mode:multiply;
  transition:opacity .8s ease,transform .8s ease;
}
.bk-blob-blue{
  width:560px;height:560px;
  /* Right-mid orbit, tied to Boro */
  top:25%;right:-200px;
  background:radial-gradient(circle,var(--bk-blue-deep) 0%,var(--bk-blue) 55%,transparent 75%);
  animation:bkDriftA 32s ease-in-out infinite alternate;
}
.bk-blob-pink{
  width:640px;height:640px;
  /* Top-left orbit, tied to Mico */
  top:-160px;left:-140px;
  background:radial-gradient(circle,var(--bk-pink-deep) 0%,var(--bk-pink) 55%,transparent 75%);
  animation:bkDriftB 38s ease-in-out infinite alternate;
}
.bk-blob-yellow{
  width:520px;height:520px;
  bottom:-140px;left:30%;
  background:radial-gradient(circle,var(--bk-yellow-deep) 0%,var(--bk-yellow) 55%,transparent 75%);
  animation:bkDriftC 35s ease-in-out infinite alternate;
}
@keyframes bkDriftA{0%{transform:translate(0,0) scale(1)}50%{transform:translate(80px,60px) scale(1.08)}100%{transform:translate(40px,140px) scale(.96)}}
@keyframes bkDriftB{0%{transform:translate(0,0) scale(1)}50%{transform:translate(-90px,80px) scale(1.05)}100%{transform:translate(-40px,-60px) scale(1.1)}}
@keyframes bkDriftC{0%{transform:translate(0,0) scale(1)}50%{transform:translate(60px,-90px) scale(1.07)}100%{transform:translate(-50px,-40px) scale(.94)}}
#bokami[data-active="boro"] .bk-blob-blue   {opacity:1;transform:scale(1.08)}
#bokami[data-active="kato"] .bk-blob-yellow {opacity:1;transform:scale(1.08)}
#bokami[data-active="mico"] .bk-blob-pink   {opacity:1;transform:scale(1.08)}

@media(prefers-reduced-motion:reduce){
  .bk-blob{animation:none}
}

/* ═══════════════════════════════════════════════════
   1. SECTION HEADER
═══════════════════════════════════════════════════ */
/* Section header — 2-col grid mirroring the .bk-intro grid below it.
   Title sits in the left column (above the polaroid below it).
   Logo sits in the right column (above the explanation text below it),
   so its left edge aligns with the text column's left edge.
   align-items:start → both top-aligned. */
.bk-section-head{
  display:grid;
  grid-template-columns:minmax(0, 420px) minmax(0, 1fr);
  gap:64px;
  align-items:start;
  margin-bottom:0;
}
.bk-section-title{
  font-size:clamp(2rem,4vw,3rem);
  font-weight:800;letter-spacing:-0.04em;line-height:1.0;
  color:var(--bk-ink);
}
.bk-section-title em{
  font-style:normal;
  background:linear-gradient(120deg,#E26F2D 0%,#F5A623 60%,#E89A6E 100%);
  -webkit-background-clip:text;background-clip:text;
  -webkit-text-fill-color:transparent;
}

/* Bokami brand logo — sits in the right column of section-head on desktop.
   On tablet/mobile the section-head reflows so the logo sits below the
   title (handled in the responsive overrides). */
.bk-brand-logo{
  display:inline-block;
  width:clamp(200px, 25vw, 290px);
  filter:drop-shadow(0 6px 18px rgba(160,80,60,0.22));
}
.bk-brand-logo-svg{
  display:block;
  width:100%;
  height:auto;
}

/* ═══════════════════════════════════════════════════
   2. INTRO — polaroid (left) + character text (right)
═══════════════════════════════════════════════════ */
.bk-intro{
  display:grid;
  grid-template-columns:minmax(0,420px) minmax(0,1fr);
  gap:64px;align-items:center;
  margin-bottom:48px;
}

/* ── LEFT column: polaroid wrapper holds polaroid + circle ── */
.bk-intro-left{
  position:relative;
  display:flex;justify-content:center;
  padding:8px 12px 36px;
}
.bk-polaroid-wrap{
  position:relative;
  width:100%;
  max-width:340px;
}

.bk-polaroid{
  position:relative;
  width:100%;
  background:#fff;
  padding:14px 14px 56px;
  /* No aspect-ratio here — Safari's aspect-ratio+padding+flex bug clips
     the inner image. Aspect-ratio lives on .bk-polaroid-img instead.
     No overflow:hidden either — the tape pieces sit at top:-9px and
     need to overflow naturally. .bk-polaroid-img has its own clip. */
  transform:rotate(-2.5deg);
  box-shadow:
    0 30px 60px -22px rgba(160,80,60,0.42),
    0 18px 36px -14px rgba(0,0,0,0.22),
    inset 0 0 0 1px rgba(255,255,255,0.6);
  cursor:zoom-in;
  transition:transform .45s cubic-bezier(.16,1,.3,1),box-shadow .45s ease;
}
.bk-polaroid:hover{
  transform:rotate(0deg) scale(1.02);
  box-shadow:
    0 40px 70px -22px rgba(160,80,60,0.5),
    0 22px 44px -14px rgba(0,0,0,0.25);
}
/* Tape pieces */
.bk-polaroid::before,
.bk-polaroid::after{
  content:"";position:absolute;
  width:74px;height:18px;
  background:rgba(255,235,200,0.7);
  border:1px solid rgba(160,80,60,0.18);
  z-index:3;
  box-shadow:0 2px 6px rgba(0,0,0,0.06);
}
.bk-polaroid::before{top:-9px;left:24px;transform:rotate(-7deg)}
.bk-polaroid::after{top:-9px;right:24px;transform:rotate(6deg)}

.bk-polaroid-img{
  position:relative;
  width:100%;
  aspect-ratio:1/1;           /* ← square crop area, owned by inner div */
  overflow:hidden;
  background:#FFE9B0;
  border-radius:2px;
}
.bk-polaroid-img img{
  width:100%;height:100%;
  object-fit:cover;
  object-position:50% 50%;
  transition:object-position .8s cubic-bezier(.16,1,.3,1);
  display:block;
}
#bokami[data-active="boro"] .bk-polaroid-img img{object-position:0% 50%}
#bokami[data-active="kato"] .bk-polaroid-img img{object-position:100% 50%}
#bokami[data-active="mico"] .bk-polaroid-img img{object-position:50% 50%}

/* v0.32.26 — per-character polaroid mode. When 3 imgs are stacked inside
   .bk-polaroid-img (one per character with data-bk-polaroid="{slug}"),
   only the active character's img is opaque. Cross-fade on character
   switch via opacity transition. The object-position rules above don't
   meaningfully apply here (each img is the character's own square
   portrait, no pan needed) — they're harmless on per-character imgs.
   For backwards compat, untagged <img> inside .bk-polaroid-img (the old
   shared trio mode) still pans normally. */
.bk-polaroid-img img[data-bk-polaroid]{
  position:absolute; inset:0;
  width:100%; height:100%;
  object-fit:cover;
  object-position:50% 50%;
  opacity:0; pointer-events:none;
  transition:opacity .5s cubic-bezier(.16,1,.3,1);
}
#bokami[data-active="boro"] .bk-polaroid-img img[data-bk-polaroid="boro"],
#bokami[data-active="kato"] .bk-polaroid-img img[data-bk-polaroid="kato"],
#bokami[data-active="mico"] .bk-polaroid-img img[data-bk-polaroid="mico"]{
  opacity:1; pointer-events:auto;
}

/* Caption: left-aligned so the profile circle on the right
   doesn't overlap the text. */
.bk-polaroid-caption{
  position:absolute;
  left:18px;right:90px;       /* leaves clear space on the right for the circle */
  bottom:18px;
  font-size:0.78rem;font-weight:700;letter-spacing:0.08em;
  color:var(--bk-ink-soft);
  text-transform:uppercase;
  white-space:nowrap;
  overflow:hidden;
  text-overflow:ellipsis;
}
.bk-polaroid-caption span{display:inline-block;transition:color .3s ease}
#bokami[data-active="boro"] .bk-polaroid-caption span{color:#2D5C75}
#bokami[data-active="kato"] .bk-polaroid-caption span{color:#7A5A10}
#bokami[data-active="mico"] .bk-polaroid-caption span{color:#8A3030}

/* Profile circle — outside the polaroid (so it overlaps the corner from
   outside), pinned to the wrapper. Slowly rotates. */
.bk-profile-circle{
  position:absolute;
  bottom:-22px;
  right:-26px;
  width:128px;height:128px;
  border-radius:50%;
  border:6px solid #fff;
  overflow:hidden;
  box-shadow:0 14px 32px -6px rgba(160,80,60,0.32);
  background:#FFE9B0;
  z-index:4;                  /* above the polaroid (which sits at z-index from box-shadow) */
  animation:bkSpin 30s linear infinite;
}
.bk-profile-circle img{width:100%;height:100%;object-fit:cover;display:block}
@keyframes bkSpin{from{transform:rotate(0)}to{transform:rotate(360deg)}}
@media(prefers-reduced-motion:reduce){.bk-profile-circle{animation:none}}

/* ── RIGHT column: character text + selector ── */
.bk-intro-right{
  position:relative;
}

.bk-char-tag-row{
  display:flex;align-items:center;gap:10px;
  margin-bottom:14px;
  font-size:0.66rem;font-weight:700;letter-spacing:0.16em;
  text-transform:uppercase;
  color:var(--bk-ink-soft);
}
.bk-char-tag-row::before{
  content:"";display:block;
  width:24px;height:1px;
  background:rgba(160,80,60,0.4);
}

/*  Stage uses CSS grid with all panels in the same cell.
    Active panel sits at opacity 1; others at 0. No transform, no
    position-flipping → true crossfade in place, no layout shift.  */
.bk-stage-text{
  display:grid;
}
.bk-char-panel{
  grid-area:1/1;              /* stack all panels in one cell */
  opacity:0;
  transition:opacity .45s ease;
  pointer-events:none;
}
.bk-char-panel.active{
  opacity:1;
  pointer-events:auto;
}

.bk-char-role{
  display:inline-block;
  padding:5px 13px;border-radius:980px;
  font-size:0.66rem;font-weight:700;letter-spacing:0.16em;
  text-transform:uppercase;
  margin-bottom:14px;
}
.bk-char-role.boro{background:var(--bk-blue);color:#2D5C75}
.bk-char-role.kato{background:var(--bk-yellow);color:#7A5A10}
.bk-char-role.mico{background:var(--bk-pink);color:#8A3030}

.bk-char-name{
  font-family:var(--font-title);   /* NHG — matches About / Works headline voice */
  font-size:clamp(2.4rem,4.5vw,3.4rem);
  font-weight:900;
  /* v0.32.28 — pulled letter-spacing + line-height into the global
     typography-system tokens. v0.31.7-9 settled the system on
     --vw-title-letter-spacing: -0.02em (loosened from prototype's -0.04
     after Vic flagged that as too cramped), and --vw-title-line-height: 1.06.
     This was hardcoded at -0.05em / 0.95 from the original prototype port,
     drifting from the system. Now matches every other big title. */
  letter-spacing:var(--vw-title-letter-spacing);
  line-height:var(--vw-title-line-height);
  color:var(--bk-ink);
  margin-bottom:14px;
}
.bk-char-bio{
  font-size:1rem;font-weight:300;
  color:var(--bk-ink);
  line-height:1.65;
  margin-bottom:14px;
  max-width:480px;
}
.bk-char-quirk{
  display:flex;align-items:flex-start;gap:10px;
  font-size:0.92rem;font-style:italic;font-weight:400;
  color:var(--bk-ink-soft);line-height:1.55;
  padding-left:14px;
  border-left:2px solid rgba(160,80,60,0.32);
  max-width:460px;
}
.bk-char-quirk strong{font-weight:700;font-style:normal;color:var(--bk-ink)}

/* Selector pills */
.bk-cast-selector{
  display:flex;gap:10px;flex-wrap:wrap;
  margin-top:24px;
}
.bk-cast-btn{
  position:relative;
  display:inline-flex;align-items:center;gap:9px;
  padding:11px 22px;border-radius:980px;
  border:2px solid var(--bk-line);
  background:rgba(255,255,255,0.85);
  font-family:inherit;
  font-size:0.85rem;font-weight:800;letter-spacing:0.06em;
  color:var(--bk-ink);
  cursor:pointer;text-transform:uppercase;
  transition:transform .25s,border-color .25s,background .25s,box-shadow .25s;
}
.bk-cast-btn::before{
  content:"";display:block;
  width:11px;height:11px;border-radius:50%;
  background:var(--bk-blue-deep);
  transition:background .25s,transform .25s;
}
.bk-cast-btn[data-char="kato"]::before{background:var(--bk-yellow-deep)}
.bk-cast-btn[data-char="mico"]::before{background:var(--bk-pink-deep)}
.bk-cast-btn:hover{
  transform:translateY(-2px) rotate(-1.5deg);
  box-shadow:0 8px 18px -4px rgba(160,80,60,0.22);
}
.bk-cast-btn:hover::before{transform:scale(1.3)}
.bk-cast-btn.active{
  background:var(--bk-ink);border-color:var(--bk-ink);color:#fff;
  transform:translateY(-2px);
  box-shadow:0 12px 24px -6px rgba(90,32,32,0.4);
}
.bk-cast-btn.active::before{transform:scale(1.4)}
.bk-cast-btn:focus-visible{outline:2px solid var(--orange);outline-offset:3px}

/* ═══════════════════════════════════════════════════
   3. PARALLAX SLIDER (smaller + continuous)
═══════════════════════════════════════════════════ */
/* The gallery section breaks out of .bk-wrap's max-width:1180px so that
   slides slide in/out at the literal browser edges (matches the tc-* pattern
   from the About page). The negative margin trick keeps the structure flat:
   a single direct child of .bk-wrap that visually spans 100vw. */
.bk-gal{
  margin-bottom:80px;
  margin-left:calc(50% - 50vw);
  margin-right:calc(50% - 50vw);
}
/* Constrain head + pills back to the same max-width as the rest of the section */
.bk-gal-head,
.bk-gal-pills-row{
  max-width:1180px;
  margin-left:auto;
  margin-right:auto;
  padding-left:24px;
  padding-right:24px;
  box-sizing:border-box;
}
.bk-gal-head{
  display:flex;align-items:flex-end;justify-content:space-between;
  gap:24px;flex-wrap:wrap;
  margin-bottom:24px;
}
.bk-gal-head-left{max-width:540px}
.bk-gal-eyebrow{
  font-size:0.66rem;font-weight:800;letter-spacing:0.16em;
  text-transform:uppercase;color:var(--eyebrow);
  margin-bottom:8px;display:inline-block;
}
.bk-gal-title{
  font-size:clamp(1.4rem,2.4vw,1.85rem);
  font-weight:800;letter-spacing:-0.03em;line-height:1.12;
  color:var(--bk-ink);
}
.bk-gal-controls{display:flex;align-items:center;gap:8px;flex-shrink:0}
.bk-arrow{
  width:42px;height:42px;border-radius:12px;
  background:#fff;border:1px solid var(--bk-line);
  cursor:pointer;display:flex;align-items:center;justify-content:center;
  color:var(--bk-ink);
  transition:background .25s,border-color .25s,color .25s,
             transform .35s cubic-bezier(.16,1,.3,1),box-shadow .35s;
}
.bk-arrow:hover{
  background:var(--orange);border-color:var(--orange);color:#fff;
  transform:translateY(-2px);
  box-shadow:0 10px 24px rgba(242,122,15,0.35);
}
.bk-arrow svg{width:16px;height:16px}

.bk-gal-viewport{
  position:relative;
  margin:-14px 0 0;
  padding:14px 0 22px;
  overflow:hidden;
}
.bk-gal-track{
  display:flex;gap:14px;
  padding:0 14px;
  transition:transform .85s cubic-bezier(.7,0,.3,1);
  will-change:transform;
}
/* Compact slide size — keeps gallery as a horizontal strip without dominating */
.bk-gal-slide{
  flex:0 0 auto;
  width:clamp(170px,20vw,260px);
  height:clamp(120px,15vw,190px);
  border-radius:12px;
  overflow:hidden;
  position:relative;
  /* v0.32.28 — bg-color restored. With v0.32.28's contain mode on the bg
     layer, the slide is the natural letterbox color — cream #FFE9B0
     matches the polaroid frame aesthetic across the bokami section so
     aspect-mismatch letterboxing looks intentional. (v0.32.18 had set
     this to transparent to avoid cream-bleed on transparent PNGs in
     cover mode; with contain mode the cream letterbox IS the design.) */
  background:#FFE9B0;
  box-shadow:0 6px 20px rgba(160,80,60,0.10);
  isolation:isolate;
  transform:translateZ(0);
  cursor:zoom-in;
  transition:transform .5s cubic-bezier(.16,1,.3,1),box-shadow .5s cubic-bezier(.16,1,.3,1);
}
.bk-gal-slide:hover{
  transform:translateY(-3px);
  /* v0.32.13 — hover ring bumped 1px → 2px per Vic, and opacity 0.18 → 0.55
     so the orange outline reads clearly. */
  box-shadow:
    0 10px 24px rgba(242,122,15,0.32),
    0 0 0 2px rgba(242,122,15,0.55);
}

/* v0.32.27 — ▶ play-icon overlay for slides with a video uploaded.
   Centered circle with backdrop blur; pulls in orange on slide hover. */
.bk-gal-slide.is-video::after{
  content:'';
  position:absolute;top:50%;left:50%;
  width:44px;height:44px;border-radius:50%;
  background:rgba(0,0,0,0.55);
  backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
  border:1px solid rgba(255,255,255,0.28);
  /* Centered ▶ triangle drawn with a CSS gradient-mask substitute:
     simplest approach — a Unicode triangle character via pseudo content.
     Actually we use a clip-path triangle for sharper rendering. */
  background-image:none;
  z-index:3;pointer-events:none;
  transform:translate(-50%,-50%);
  transition:transform .3s cubic-bezier(.16,1,.3,1),background .3s ease;
  /* Inset triangle drawn with clip-path */
  clip-path:circle(50% at 50% 50%);
}
.bk-gal-slide.is-video::before{
  content:'';
  position:absolute;top:50%;left:50%;
  width:0;height:0;
  border-left:12px solid #fff;
  border-top:8px solid transparent;
  border-bottom:8px solid transparent;
  margin-left:-4px;  /* visual nudge to optically center the triangle */
  transform:translate(-50%,-50%);
  z-index:4;pointer-events:none;
  transition:transform .3s cubic-bezier(.16,1,.3,1);
  filter:drop-shadow(0 1px 2px rgba(0,0,0,0.4));
}
.bk-gal-slide.is-video:hover::after{
  background:rgba(242,122,15,0.85);
  transform:translate(-50%,-50%) scale(1.08);
}
.bk-gal-slide.is-video:hover::before{
  transform:translate(-50%,-50%) scale(1.08);
}
.bk-gal-slide-bg{
  position:absolute;
  /* v0.32.10 — overflow margin reduced -50% → -8%. The 200%×200% bg layer was
     causing user-uploaded images to render at 2× zoom (only the center ~50%
     visible at rest, parallax exposing left/right halves). Vic flagged: "images
     don't fit exactly on the placeholder frame, they're drifting." The 2× zoom
     was acceptable for the prototype's hand-art-directed base64 polaroid set
     (focal point composed dead-center) but uploaded photos got faces cropped
     and obvious focal points pushed off-frame. New 116% × 116% layer keeps a
     subtle parallax overflow but the image effectively renders at ~1.16×,
     close enough to feel like it fits the slide. Parallax cap in
     works-archive.js MODULE 4 reduced from 0.45 → 0.07 (and coefficient
     260 → 30) to stay within the new overflow margin. */
  /* v0.32.23 — parallax RESTORED. inset back to -25% (150%×150% bg layer
     for the parallax shift to swing in). The "cream stripes" Vic kept
     seeing through v0.32.10-19 weren't actually CSS bg-color exposure —
     they were the prototype fallback JPEGs' intrinsic POLAROID FRAME
     design content. The inline base64 prototype slides are illustrated
     as cream polaroid prints, so the frame is BAKED INTO the image. As
     parallax shifted the cover-mode bg, more or less of the frame
     became visible, looking like drift. Once real CMS slides are
     uploaded (without polaroid frames), parallax just shifts the actual
     image content with no cream visible. */
  /* v0.32.28 — switched cover → contain + parallax killed. Vic's bokami
     posters are portrait/square (character + name vertical), but the
     slide is landscape (1.4:1). Now: contain fits the whole image
     inside the slide, letterboxing with the slide's bg-color on the
     aspect-mismatch axis.
     v0.32.30 — `inset: 8px` so the image floats inside the card with a
     small cream margin around it. Vic asked the thumbs to "look smaller,
     fit to width of each card" — the inset gives the polaroid frame
     breathing room around the image content. */
  inset:8px;
  background-size:contain;
  background-position:center;
  background-repeat:no-repeat;
}
.bk-gal-slide-overlay{
  position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(0,0,0,0) 45%,rgba(40,15,10,0.72) 100%);
  pointer-events:none;
  transform:translate3d(0,0,0);
  backface-visibility:hidden;
  will-change:transform;
}
.bk-gal-slide-cap{
  position:absolute;left:14px;right:14px;bottom:12px;
  color:#fff;z-index:2;pointer-events:none;
  transform:translate3d(0,0,0);
  backface-visibility:hidden;will-change:transform;
}
.bk-gal-slide-tag{
  display:inline-block;
  font-size:0.58rem;font-weight:700;letter-spacing:0.18em;
  text-transform:uppercase;
  /* v0.32.31 — tag color was gold; Vic wants in-card text in white. The
     text-shadow keeps both tag + title readable when the bottom of the
     slide shows the cream margin (contain mode leaves the bottom partially
     uncovered for portrait images, where the dark overlay alone isn't
     enough contrast for white text). */
  color:#fff;
  margin-bottom:3px;
  text-shadow:0 1px 3px rgba(0,0,0,0.55);
}
.bk-gal-slide-title{
  font-size:0.82rem;font-weight:800;line-height:1.25;
  letter-spacing:-0.01em;
  color:#fff;
  text-shadow:0 1px 3px rgba(0,0,0,0.55);
}

.bk-gal-pills-row{
  display:flex;align-items:center;justify-content:center;
  gap:18px;margin-top:8px;
}
.bk-gal-pills{display:flex;gap:6px;flex-wrap:wrap;justify-content:center}
.bk-gal-pill{
  width:24px;height:5px;border-radius:980px;
  background:rgba(160,80,60,0.18);
  cursor:pointer;border:none;padding:0;
  transition:background .35s,width .45s cubic-bezier(.16,1,.3,1);
}
.bk-gal-pill.active{background:var(--orange);width:42px}
.bk-gal-pill:hover:not(.active){background:rgba(160,80,60,0.32)}

/* ═══════════════════════════════════════════════════
   4. COMBINED IG FEED + SOCIALS
   Layout: side-by-side — IG feed (left) + 4 social cards (right). */
.bk-social-card{
  position:relative;
  display:grid;
  grid-template-columns:minmax(0, 1fr) minmax(0, 1fr);
  gap:24px;
  padding:28px;
  border-radius:24px;
  background:linear-gradient(135deg,#0F0F12 0%,#1A1A1F 100%);   /* dark surface */
  border:1px solid rgba(255,255,255,0.07);
  box-shadow:
    0 24px 60px -20px rgba(0,0,0,0.55),
    inset 0 1px 0 rgba(255,255,255,0.06);
}

/* IG feed col — left half */
.bk-feed-col{display:flex;flex-direction:column;min-width:0}
.bk-feed-head{
  display:flex;align-items:center;justify-content:space-between;
  gap:12px;flex-wrap:wrap;
  margin-bottom:14px;
}
.bk-feed-handle-block{display:flex;flex-direction:column;gap:2px;min-width:0}
.bk-feed-eyebrow{
  font-size:0.6rem;font-weight:800;letter-spacing:0.16em;
  text-transform:uppercase;color:#FFC773;   /* brighter than --orange/--gold for legibility on dark */
}
.bk-feed-handle{
  font-size:0.95rem;font-weight:800;
  color:#fff;
  letter-spacing:-0.01em;
  display:inline-flex;align-items:center;gap:7px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.bk-feed-handle svg{width:14px;height:14px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0}
.bk-feed-follow{
  display:inline-flex;align-items:center;gap:6px;
  padding:7px 13px;border-radius:980px;
  background:linear-gradient(135deg,#F58529 0%,#DD2A7B 50%,#8134AF 100%);
  color:#fff;font-weight:700;font-size:0.72rem;
  box-shadow:0 6px 14px -4px rgba(221,42,123,0.4);
  transition:transform .25s,box-shadow .25s;
  flex-shrink:0;
}
.bk-feed-follow:hover{
  transform:translateY(-2px);
  box-shadow:0 12px 22px -6px rgba(221,42,123,0.55);
}
.bk-feed-follow svg{width:11px;height:11px;stroke:currentColor;fill:none;stroke-width:2.6;stroke-linecap:round;stroke-linejoin:round}

/* IG feed grid: 3 cols x 2 rows = 6 cells (constrained by left column width) */
.bk-feed-grid{
  display:grid;
  grid-template-columns:repeat(3, minmax(0, 1fr));
  gap:6px;
}
.bk-feed-cell{
  position:relative;
  aspect-ratio:1/1;
  border-radius:8px;
  overflow:hidden;
  background:linear-gradient(135deg,#FFE9B0 0%,#F8D4D4 50%,#CFE7F0 100%);
  border:1px solid var(--bk-line);
  cursor:pointer;
  transition:transform .3s,box-shadow .3s;
  display:flex;align-items:center;justify-content:center;
  flex-direction:column;gap:5px;
}
.bk-feed-cell:hover{
  transform:translateY(-2px);
  box-shadow:0 8px 18px -8px rgba(160,80,60,0.28);
}
.bk-feed-cell svg{
  width:20px;height:20px;
  stroke:rgba(160,80,60,0.45);
  fill:none;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;
}
.bk-feed-cell span{
  font-size:0.55rem;font-weight:700;letter-spacing:0.16em;
  text-transform:uppercase;color:rgba(90,32,32,0.45);
}
.bk-feed-cell:nth-child(2n)  {background:linear-gradient(135deg,#CFE7F0 0%,#F8D4D4 100%)}
.bk-feed-cell:nth-child(3n)  {background:linear-gradient(135deg,#FFE9B0 0%,#CFE7F0 100%)}
.bk-feed-cell:nth-child(5n)  {background:linear-gradient(135deg,#F8D4D4 0%,#FFE9B0 100%)}

/* RIGHT: 4 social CARDS — 3D tilt, cursor-tracked border + shine,
          brand-colored gradients. Pattern mirrors vertwo_vmv_cards. */
.bk-soc-col{
  display:flex;flex-direction:column;
  min-width:0;
}
/* Single row of 4 cards — width similar to IG feed cells */
.bk-soc-list{
  display:grid;
  grid-template-columns:repeat(2, minmax(0, 1fr));
  gap:12px;
}

/* Tilt motion variables — set by JS via setProperty */
.bk-soc-card{
  --rx:0; --ry:0; --mx:50%; --my:50%;
  position:relative;
  display:block;
  perspective:1100px;
  text-decoration:none;
  color:#fff;
  /* Slight portrait — gives each card visual weight in a 4-wide row */
  aspect-ratio:5/4;
}
.bk-soc-card-inner{
  position:relative;
  width:100%;height:100%;
  border-radius:18px;
  transform-style:preserve-3d;
  transform:rotateX(calc(var(--rx) * 1deg)) rotateY(calc(var(--ry) * 1deg)) translateZ(0);
  transition:transform .7s cubic-bezier(.22,1,.36,1);
  will-change:transform;
  overflow:hidden;
  box-shadow:0 1px 0 rgba(255,255,255,0.06) inset;
}
.bk-soc-card.is-hovering .bk-soc-card-inner{
  /* Snappier tilt while hover-active; resting interpolates back slowly */
  transition:transform .12s ease-out;
}

/* Brand-colored bg layer (z-index 1) */
.bk-soc-card-bg{
  position:absolute;inset:0;
  z-index:1;
  border-radius:inherit;
}
.bk-soc-card--ig .bk-soc-card-bg{
  background:linear-gradient(135deg,#FEDA77 0%,#F58529 25%,#DD2A7B 60%,#8134AF 100%);
}
.bk-soc-card--fb .bk-soc-card-bg{
  background:linear-gradient(135deg,#1877F2 0%,#0B5FCC 100%);
}
.bk-soc-card--tt .bk-soc-card-bg{
  background:linear-gradient(135deg,#000 0%,#1a1a1a 50%,#25F4EE 110%);
  /* No position override — base .bk-soc-card-bg is already position:absolute,
     and the ::after will anchor to it correctly. */
}
.bk-soc-card--tt .bk-soc-card-bg::after{
  content:"";position:absolute;inset:0;
  background:linear-gradient(135deg,transparent 0%,transparent 50%,rgba(254,44,85,0.4) 100%);
}
.bk-soc-card--web .bk-soc-card-bg{
  background:linear-gradient(135deg,var(--orange) 0%,var(--gold) 100%);
}

/* Shine (z-2) — warm radial highlight that follows cursor */
.bk-soc-card-shine{
  position:absolute;inset:0;
  z-index:2;border-radius:inherit;
  background:radial-gradient(circle 240px at var(--mx) var(--my),
    rgba(255,255,255,0.22) 0%,
    rgba(255,255,255,0.06) 30%,
    transparent 60%);
  opacity:0;
  transition:opacity .5s cubic-bezier(.4,0,.2,1);
  pointer-events:none;
}
.bk-soc-card.is-hovering .bk-soc-card-shine{opacity:1}

/* Border (z-3) — bright edge highlight masked to a frame, tracking cursor */
.bk-soc-card-border{
  position:absolute;inset:0;
  z-index:3;border-radius:inherit;
  padding:1.5px;
  background:radial-gradient(circle 280px at var(--mx) var(--my),
    rgba(255,255,255,1) 0%,
    rgba(255,255,255,0.7) 25%,
    rgba(255,255,255,0.15) 55%,
    rgba(255,255,255,0.04) 100%);
  -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
          mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;
          mask-composite:exclude;
  opacity:0.32;
  transition:opacity .5s cubic-bezier(.4,0,.2,1);
  pointer-events:none;
}
.bk-soc-card.is-hovering .bk-soc-card-border{opacity:1}

/* Content (z-4) */
.bk-soc-card-content{
  position:absolute;inset:0;z-index:4;
  padding:16px 18px;
  display:flex;flex-direction:column;justify-content:space-between;
  transform-style:preserve-3d;
}
.bk-soc-card-icon{
  width:38px;height:38px;border-radius:11px;
  background:rgba(255,255,255,0.18);
  display:flex;align-items:center;justify-content:center;
  flex-shrink:0;
  transform:translateZ(18px);     /* float forward in 3D */
  align-self:flex-start;
}
.bk-soc-card-icon svg{
  width:20px;height:20px;
  stroke:#fff;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;
}
.bk-soc-card-foot{
  display:flex;align-items:flex-end;justify-content:space-between;gap:10px;
  transform:translateZ(12px);     /* slight forward float */
}
.bk-soc-card-text{display:flex;flex-direction:column;gap:1px;min-width:0;flex:1}
.bk-soc-card-name{
  font-size:1.05rem;font-weight:800;letter-spacing:-0.02em;line-height:1.15;
  color:#fff;
}
.bk-soc-card-handle{
  font-size:0.7rem;font-weight:500;
  color:rgba(255,255,255,0.78);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.bk-soc-card-arrow{
  width:30px;height:30px;border-radius:50%;
  background:rgba(255,255,255,0.18);
  display:flex;align-items:center;justify-content:center;
  flex-shrink:0;
  transition:transform .35s cubic-bezier(.16,1,.3,1),background .3s;
}
.bk-soc-card.is-hovering .bk-soc-card-arrow{
  transform:rotate(-45deg);
  background:rgba(255,255,255,0.32);
}
.bk-soc-card-arrow svg{
  width:13px;height:13px;
  stroke:#fff;fill:none;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round;
}

/* ═══════════════════════════════════════════════════
   LIGHTBOX — no backdrop blur (Safari perf), continuous
═══════════════════════════════════════════════════ */
.bk-lightbox{
  position:fixed;inset:0;
  background:rgba(8,8,12,0.96);    /* solid dark surface — no backdrop-filter for Safari perf */
  display:flex;align-items:center;justify-content:center;
  z-index:1000;
  opacity:0;
  pointer-events:none;
  transition:opacity 220ms cubic-bezier(.16,1,.3,1);
}
.bk-lightbox.is-open{opacity:1;pointer-events:auto}
.bk-lightbox:not(.is-open),
.bk-lightbox:not(.is-open) *{pointer-events:none !important}
.bk-lb-viewport{
  position:relative;
  width:92vw;height:84vh;
  overflow:hidden;
}
.bk-lb-track{
  position:absolute;top:0;left:0;
  height:100%;
  display:flex;
  will-change:transform;
  transition:transform .5s cubic-bezier(.16,1,.3,1);
}
.bk-lb-slide{
  flex:0 0 92vw;width:92vw;height:84vh;
  display:flex;align-items:center;justify-content:center;
}
.bk-lb-image{
  display:block;
  max-width:92vw;max-height:84vh;
  width:auto;height:auto;
  object-fit:contain;
  user-select:none;-webkit-user-drag:none;
  cursor:pointer;
  border-radius:8px;
  box-shadow:0 30px 80px rgba(0,0,0,0.5);
}
.bk-lb-close,.bk-lb-nav{
  position:absolute;
  width:48px;height:48px;border-radius:50%;
  background:transparent;
  border:1px solid rgba(255,255,255,0.24);
  color:#fff;
  display:inline-flex;align-items:center;justify-content:center;
  cursor:pointer;z-index:2;
  transition:background .3s,border-color .3s,transform .3s,box-shadow .35s;
}
.bk-lb-close{top:24px;right:24px}
.bk-lb-close svg{width:20px;height:20px}
.bk-lb-close:hover{
  background:var(--orange);border-color:var(--orange);
  transform:rotate(90deg);
  box-shadow:0 10px 30px -6px rgba(242,122,15,0.6);
}
.bk-lb-nav{top:50%;width:52px;height:52px;transform:translateY(-50%)}
.bk-lb-nav svg{width:22px;height:22px}
.bk-lb-prev{left:24px}
.bk-lb-next{right:24px}
.bk-lb-nav:hover{
  background:var(--orange);border-color:var(--orange);
  box-shadow:0 10px 30px -6px rgba(242,122,15,0.6);
}
.bk-lb-prev:hover{transform:translateY(-50%) translateX(-3px)}
.bk-lb-next:hover{transform:translateY(-50%) translateX(3px)}
.bk-lb-footer{
  position:absolute;left:50%;bottom:24px;
  transform:translateX(-50%);
  display:flex;align-items:center;gap:16px;
  z-index:2;
}
.bk-lb-pills{display:flex;gap:6px}
.bk-lb-pill{
  width:28px;height:4px;
  background:rgba(255,255,255,0.22);
  border-radius:2px;
  cursor:pointer;border:none;padding:0;
  transition:background .3s,width .3s;
}
.bk-lb-pill.is-active{background:var(--orange);width:40px}
.bk-lb-pill:hover{background:rgba(242,122,15,0.75)}
.bk-lb-counter{
  color:rgba(255,255,255,0.7);
  font-size:12px;font-weight:600;letter-spacing:0.06em;
  font-variant-numeric:tabular-nums;
}

/* ═══════════════════════════════════════════════════
   RESPONSIVE
═══════════════════════════════════════════════════ */
/* ─── Tablet-portrait band (641-960px) ───
   Keeps the desktop two-column layout (title-left + logo-right above
   polaroid-left + bio-right), but scales the left column down so the
   right column has more breathing room. The polaroid + circle move
   left; the bio column gets pulled left a little to follow. */
@media(min-width:761px) and (max-width:960px){
  .bk-intro{
    grid-template-columns:minmax(0, 280px) minmax(0, 1fr);
    gap:36px;
  }
  .bk-section-head{
    grid-template-columns:minmax(0, 280px) minmax(0, 1fr);
    gap:36px;
  }
  /* Title shrinks here so "Vertwo Original IP." fits one line in the
     280px left column. Base is clamp(2rem, 4vw, 3rem); at this viewport
     it computes to ~38px which wraps. 1.65rem (~26px) fits cleanly. */
  .bk-section-title{font-size:clamp(1.5rem, 3vw, 1.75rem)}
  /* Polaroid scales smaller and anchors LEFT in its column */
  .bk-intro-left{justify-content:flex-start;padding:8px 0 28px}
  .bk-polaroid-wrap{max-width:260px}
  .bk-polaroid{padding:11px 11px 44px}
  .bk-polaroid::before,.bk-polaroid::after{width:60px;height:14px}
  .bk-polaroid-caption{left:14px;right:80px;bottom:14px;font-size:0.7rem}
  .bk-profile-circle{width:108px;height:108px;border-width:5px;bottom:-18px;right:-18px}
  /* Right column: smaller name + cast buttons */
  .bk-char-name{font-size:clamp(2rem, 4vw, 2.6rem)}
  .bk-char-bio{font-size:0.92rem}
  .bk-char-quirk{font-size:0.85rem}
  .bk-cast-btn{padding:9px 18px;font-size:0.76rem}
  .bk-cast-btn::before{width:9px;height:9px}
}

@media(max-width:760px){
  /* Section header: single-column stack. Logo sits below title. */
  .bk-section-head{grid-template-columns:1fr;gap:8px}
  .bk-section-head .bk-section-title{order:0}
  .bk-section-head .bk-brand-logo{order:1}
  .bk-intro{grid-template-columns:1fr;gap:14px}
  /* Wider top padding here = breathing space between logo and polaroid */
  .bk-intro-left{order:-1;padding:56px 0 14px}
  .bk-intro-right{text-align:center}
  .bk-char-tag-row{justify-content:center}
  /* Constrain bio width so centered lines don't sprawl across tablet */
  .bk-char-bio{max-width:380px;margin-left:auto;margin-right:auto}
  /* Quirk on small screens: switch from left-bar + flex to centered
     block with TOP rule so it sits cleanly below centered bio */
  .bk-char-quirk{
    margin-left:auto;margin-right:auto;
    max-width:380px;
    padding:10px 0 0;
    border-left:none;
    border-top:1px solid rgba(160,80,60,0.32);
    text-align:center;
    display:block;
  }
  .bk-cast-selector{justify-content:center}
  .bk-social-card{grid-template-columns:1fr;gap:24px}
}
@media(max-width:640px){
  #bokami{padding:64px 18px 72px}
  .bk-section-head{margin-bottom:0}
  .bk-intro{margin-bottom:64px}
  .bk-gal{margin-bottom:64px}
  /* Wider gap between logo and polaroid on mobile (overrides the 32px
     from the 760px block) */
  .bk-intro-left{padding:56px 0 14px}
  .bk-polaroid-wrap{max-width:280px}
  .bk-polaroid{padding:12px 12px 48px}
  .bk-polaroid::before,.bk-polaroid::after{width:60px;height:14px}
  .bk-profile-circle{width:96px;height:96px;border-width:5px;bottom:-18px;right:-18px}
  .bk-char-name{font-size:2.4rem}
  .bk-arrow{width:38px;height:38px;border-radius:10px}
  .bk-arrow svg{width:14px;height:14px}
  .bk-gal-slide{width:62vw;height:38vw}
  .bk-social-card{padding:22px}
}
@media(max-width:520px){
  .bk-cast-btn{padding:9px 16px;font-size:0.78rem}
  .bk-feed-head{gap:8px}
}



      .cls-1, .cls-2 {
        fill: none;
      }

      .cls-3 {
        fill: #b26362;
      }

      .cls-4 {
        fill: #a4d6e0;
      }

      .cls-5 {
        fill: #f7b589;
      }

      .cls-6 {
        fill: #fcbfb5;
      }

      .cls-7 {
        fill: #b87e7d;
      }

      .cls-8 {
        fill: #fbf6f0;
      }

      .cls-9 {
        clip-path: url(#clippath-1);
      }

      .cls-10 {
        clip-path: url(#clippath-2);
      }

      .cls-2 {
        stroke: #b36363;
        stroke-miterlimit: 10;
        stroke-width: 2.07px;
      }

      .cls-11 {
        fill: #eaf8f3;
      }

      .cls-12 {
        fill: #9fc5cb;
      }

      .cls-13 {
        fill: #f8ecdc;
      }

      .cls-14 {
        fill: #f4ab9a;
      }

      .cls-15 {
        fill: #ffbf75;
      }

      .cls-16 {
        clip-path: url(#clippath);
      }
    

/* ───── work-with-us ───── */
  /* ════════════════════════════════════════════════════════════
     WORK WITH US — closing CTA section
     Dark canvas to mirror the hero (page opens dark, lands light
     through the body, closes dark into the footer).
     Animated gradient blobs lift the energy without hijacking
     focus from the CTA itself.
     ════════════════════════════════════════════════════════════ */
  #work-with-us {
    position: relative;
    background: #0a0a0c;
    overflow: hidden;
    isolation: isolate;
    padding: clamp(96px, 14vh, 160px) 24px;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  /* Background blobs — same language as hero but tighter, less drift */
  #work-with-us .wwu-bg {
    position: absolute;
    inset: -10% -5%;
    z-index: 0;
    pointer-events: none;
    filter: blur(80px) saturate(135%);
    opacity: 0.78;
    will-change: transform;
  }
  #work-with-us .wwu-blob {
    position: absolute;
    border-radius: 50%;
    mix-blend-mode: screen;
  }
  #work-with-us .wwu-blob--a {
    width: 50vw; height: 50vw; left: -8%; top: -10%;
    background: radial-gradient(circle at 35% 35%, #F27A0F 0%, rgba(242,122,15,0) 60%);
    animation: wwuDriftA 26s ease-in-out infinite alternate;
  }
  #work-with-us .wwu-blob--b {
    width: 46vw; height: 46vw; right: -10%; top: 12%;
    background: radial-gradient(circle at 65% 50%, #EC2F7A 0%, rgba(236,47,122,0) 60%);
    animation: wwuDriftB 32s ease-in-out infinite alternate;
  }
  #work-with-us .wwu-blob--c {
    width: 56vw; height: 56vw; left: 22%; bottom: -28%;
    background: radial-gradient(circle at 50% 50%, var(--blob-color, #F5A623) 0%, transparent 60%);
    animation: wwuDriftC 38s ease-in-out infinite alternate;
  }
  @keyframes wwuDriftA {
    0%   { transform: translate3d(0,0,0) scale(1); }
    100% { transform: translate3d(7vw,3vh,0) scale(1.10); }
  }
  @keyframes wwuDriftB {
    0%   { transform: translate3d(0,0,0) scale(1.04); }
    100% { transform: translate3d(-5vw,4vh,0) scale(1); }
  }
  @keyframes wwuDriftC {
    0%   { transform: translate3d(0,0,0) scale(1); }
    100% { transform: translate3d(-3vw,-5vh,0) scale(1.12); }
  }
  /* Subtle grain over the gradient */
  #work-with-us .wwu-grain {
    position: absolute; inset: 0; z-index: 1;
    pointer-events: none;
    opacity: 0.10;
    background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
    background-size: 200px 200px;
    mix-blend-mode: overlay;
  }
  /* Veil for contrast */
  #work-with-us .wwu-veil {
    position: absolute; inset: 0; z-index: 2;
    pointer-events: none;
    background:
      radial-gradient(ellipse at 50% 80%, rgba(0,0,0,0.50) 0%, rgba(0,0,0,0) 60%),
      linear-gradient(180deg, rgba(10,10,12,0) 0%, rgba(10,10,12,0.40) 100%);
  }
  /* Inner content column */
  #work-with-us .wwu-inner {
    position: relative;
    z-index: 3;
    max-width: 760px;
    width: 100%;
    text-align: center;
    color: #fff;
  }
  /* (.wwu-eyebrow font/spacing now standardized via the
     SECTION TITLE STANDARDIZATION block — only margin/display
     overrides live here.) */
  #work-with-us .wwu-eyebrow {
    margin-bottom: 18px;
  }
  /* (.wwu-title font/spacing now standardized; only color + margin here.) */
  #work-with-us .wwu-title {
    color: #fff;
    margin: 0 0 22px;
  }
  /* (.wwu-sub metrics standardized; only color/maxw/margin here.) */
  #work-with-us .wwu-sub {
    color: rgba(255,255,255,0.78);
    max-width: 580px;
    margin: 0 auto 38px;
  }
  /* CTA row — primary button + secondary text link */
  #work-with-us .wwu-cta-row {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 28px;
    margin-bottom: 44px;
    flex-wrap: wrap;
  }
  #work-with-us .wwu-btn {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    padding: 16px 32px;
    border-radius: 980px;
    background: var(--orange);
    color: #fff;
    font-family: var(--font-body);
    font-size: 0.95rem;
    font-weight: 600;
    letter-spacing: 0.005em;
    text-decoration: none;
    border: none;
    cursor: pointer;
    box-shadow:
      0 8px 32px rgba(242,122,15,0.28),
      inset 0 1px 0 rgba(255,255,255,0.18);
    transition: transform .35s cubic-bezier(.16,1,.3,1), box-shadow .35s;
  }
  #work-with-us .wwu-btn:hover {
    transform: translateY(-2px) scale(1.02);
    box-shadow:
      0 16px 48px rgba(242,122,15,0.42),
      inset 0 1px 0 rgba(255,255,255,0.20);
  }
  #work-with-us .wwu-btn svg {
    width: 16px; height: 16px;
    transition: transform .35s cubic-bezier(.16,1,.3,1);
  }
  #work-with-us .wwu-btn:hover svg { transform: translateX(3px); }
  #work-with-us .wwu-link {
    color: rgba(255,255,255,0.65);
    font-size: 0.95rem;
    font-weight: 400;
    text-decoration: none;
    border-bottom: 1px solid rgba(255,255,255,0.22);
    padding-bottom: 2px;
    transition: color .25s, border-color .25s;
  }
  #work-with-us .wwu-link:hover {
    color: #fff;
    border-bottom-color: rgba(255,255,255,0.6);
  }
  /* Service chips strip — quiet recap of the offering */
  #work-with-us .wwu-chips {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 10px;
  }
  #work-with-us .wwu-chip {
    display: inline-flex;
    align-items: center;
    padding: 8px 16px;
    border-radius: 999px;
    background: rgba(255,255,255,0.04);
    border: 1px solid rgba(255,255,255,0.10);
    font-family: var(--font-body);
    font-size: 0.78rem;
    font-weight: 500;
    letter-spacing: 0.02em;
    color: rgba(255,255,255,0.72);
    transition: background .25s, border-color .25s, color .25s;
  }
  #work-with-us .wwu-chip:hover {
    background: rgba(255,255,255,0.08);
    border-color: rgba(255,255,255,0.22);
    color: #fff;
  }
  /* Mobile */
  @media (max-width: 600px) {
    #work-with-us { padding: 80px 20px; }
    #work-with-us .wwu-cta-row { gap: 18px; margin-bottom: 36px; }
    #work-with-us .wwu-btn { padding: 14px 26px; font-size: 0.9rem; }
    #work-with-us .wwu-chips { gap: 8px; }
    #work-with-us .wwu-chip { font-size: 0.72rem; padding: 6px 13px; }
  }


/* ───── per-section background overrides ───── */
#portfolio-module { background: #fff; }
#clients { background: #fff; padding: 0; }
#bokami { background: var(--bk-cream, #FFF8EE); }

  /* ════════════════════════════════════════════════════════════════════
     SECTION TITLE STANDARDIZATION — Works page
     Mirrors About's standardization pattern. Every section title across
     every module ends up with the same NHG type, same scale, same weight,
     same letter-spacing, same line-height. Fixes Vic's complaint about
     each module shipping its own eyebrow/title/sub style. Also fixes the
     bokami descender-clipping bug — line-height: 1.0 was cutting the
     period and the 'p' in 'IP' below the baseline. line-height: 1.12
     gives descenders room.

     Targets every existing title selector across every module — they
     keep their own colors (light vs dark bg) and margins, but the
     scale/weight/spacing now match About's section-title spec.
     ════════════════════════════════════════════════════════════════════ */

  /* ─── Section TITLES (h2 scale) ─── */
  .pm-title,
  .vt-clients__title,
  .vt-header__title,
  .bk-section-title,
  .wg-title,
  .wwu-title {
    font-family: var(--font-title) !important;
    font-size: clamp(1.9rem, 4vw, 2.9rem) !important;
    font-weight: 900 !important;
    letter-spacing: -0.04em !important;
    line-height: 1.12 !important;             /* fixes bokami descender clip */
    text-transform: lowercase !important;
  }
  /* <em> inside any standardized title — orange accent, normal style */
  .pm-title em,
  .vt-clients__title em,
  .vt-header__title em,
  .bk-section-title em,
  .wg-title em,
  .wwu-title em {
    font-style: normal !important;
    color: var(--orange) !important;
    font-weight: inherit !important;
  }

  /* ─── EYEBROWS ─── */
  .pm-eyebrow,
  .vt-clients__eyebrow,
  .vt-eyebrow,
  .wg-eyebrow,
  .wwu-eyebrow {
    font-family: var(--font-body) !important;
    font-size: 0.72rem !important;
    font-weight: 800!important;
    letter-spacing: 0.2em !important;
    text-transform: uppercase !important;
    color: var(--eyebrow) !important;
    margin-bottom: 14px !important;
    display: inline-block !important;
  }
  /* Bokami doesn't ship an eyebrow class — its section header is just
     a single h2. So nothing to standardize on the bokami side. */

  /* ─── SUB / BODY copy under titles ─── */
  .pm-body,
  .vt-clients__sub,
  .vt-header__sub,
  .wg-sub,
  .wwu-sub {
    font-family: var(--font-body) !important;
    font-size: clamp(0.95rem, 1.25vw, 1.1rem) !important;
    font-weight: 800!important;
    line-height: 1.7 !important;
  }

  /* ─── TESTIMONIALS eyebrow has a numbered rule line — keep its
     structure but normalize the type metric on the inner span. ─── */
  .vt-eyebrow {
    /* the testimonials eyebrow is a flex container with __rule and __num
       children — keep flex layout, just normalize font metrics */
    display: inline-flex !important;
    align-items: center !important;
    gap: 12px !important;
  }

  /* ════════════════════════════════════════════════════════════════════
     WORKS GALLERY HEADER — sits above the glass module
     Centered text on dark canvas, mirrors About hero proportions but at
     section-title scale (matches the standardization above).
     ════════════════════════════════════════════════════════════════════ */
  .wg-head {
    text-align: center;
    max-width: 720px;
    margin: 0 auto;
    padding: 0 24px 48px;
    color: #fff;
  }
  .wg-title {
    color: #fff !important;
    margin: 0 0 16px !important;
  }
  .wg-sub {
    color: rgba(255,255,255,0.65) !important;
    max-width: 560px;
    margin: 0 auto !important;
  }
  @media (max-width: 720px) {
    .wg-head { padding: 0 20px 32px; }
  }


  /* ════════════════════════════════════════════════════════════════════
     CLIENTS — STATIC GRID OVERRIDE (Vic v6: 4-per-row, all visible, larger)
     Kills the v25 slideshow (cycle/swap/grayscale) AND the v25 mobile
     rule that hid the 9th slot.
     ════════════════════════════════════════════════════════════════════ */

  /* Always color — no grayscale, no chromatic swap, no cycle */
  #clients[data-clients-mode="static"] .vt-logo,
  #clients[data-clients-mode="static"] .vt-logo.is-color {
    filter: none !important;
    opacity: 1 !important;
    transition: none !important;
    animation: none !important;
  }
  #clients[data-clients-mode="static"] .vt-slot__inner,
  #clients[data-clients-mode="static"] .vt-slot__inner.is-swapping,
  #clients[data-clients-mode="static"] .vt-slot__inner.is-chroma {
    animation: none !important;
    filter: none !important;
  }
  #clients[data-clients-mode="static"] .vt-slot { pointer-events: none; }

  /* [7] FIX missing logo — the v25 mobile rule `.vt-slot:nth-child(9) { display: none }`
         was hiding the 9th rendered logo (minto). In static mode we render all 14, so
         this rule must be off across ALL viewports. Stronger selector to win the
         cascade against the v25 rule which lives in About's universal stylesheet. */
  #clients[data-clients-mode="static"] .vt-slot:nth-child(n) {
    display: flex !important;
  }

  /* [8] Responsive client grid:
         desktop  ≥ 1025px: 4 per row
         tablet portrait 761-1024px: 3 per row (Vic v8)
         mobile  ≤ 760px:  2 per row
         Slots get larger as columns get fewer to keep visual presence. */
  /* v0.32.138 → v0.32.139 → v0.32.140 → v0.32.141 — Vic kept flagging
     that the client logos rendered at different sizes between homepage
     and /works/, despite multiple rounds of slot-height alignment.
     Root cause: the GRID itself differs. The homepage uses
     clients.css's default 3-col grid with `--vt-slot-h: 128px`;
     this section overrode to 4-col on desktop + 3-col on tablet +
     2-col on mobile with different slot heights at each break. Even
     when slot heights matched, the column count change made each
     slot's WIDTH different, so logos sized as % of slot-width
     rendered at a different physical pixel size.

     v0.32.141 — full surrender. We drop EVERY override on .vt-grid
     here so clients.css's selectors are the only ones that match —
     identical grid math, identical slot dimensions, identical logo
     percentages. (v0.32.140 still capped .vt-grid at max-width:1100px
     while homepage inherited 1152px from the .vt-clients parent, a
     ~17px-per-slot width drift that explained the residual size
     mismatch on width-bound logos like Minto.)
     Only the animation-disabling rules above (filter/opacity/
     transition/animation = none) are kept so the works archive
     stays static while the homepage keeps its slideshow rhythm.
     The per-slug duplicates further down (line ~3510+) still apply
     for the static-mode scoping but they read the same values that
     clients.css would give the homepage, so the visual result is
     identical. */


  /* ════════════════════════════════════════════════════════════════════
     SECTION GAP TIGHTENING (Vic feedback v6)
     Each !important here narrows the visual gap between adjacent sections.
     Rule of thumb: kept the inner content paddings; only reduced the
     section-edge paddings that were creating the doubled-gap effect.
     ════════════════════════════════════════════════════════════════════ */

  /* [3] Gallery → Portfolio: portfolio's section-top padding tightened.
         Was 120px → 64px so the gallery's dark base meets the portfolio
         section without a second 'breathing room' band.
         Bottom padding was 56px but the .pm-card hover shadow extends
         ~80px below the card — getting clipped by #clients (next section)
         which paints its background over the overflowing shadow.
         Bumping to 96px gives the shadow room to render entirely within
         #portfolio-module's own bounds. */
  #portfolio-module {
    padding-top: 64px !important;
    padding-bottom: 96px !important;
  }

  /* [4] Portfolio → Clients: balanced section padding.
         v0.32.222 — the "big empty gap above the hairline" Vic chased for
         several rounds turned out NOT to be a gap: he has 30 client logos,
         so the grid is legitimately ~17 rows / 1964px tall, and the
         screenshot crops just showed the lower logo rows below the fold.
         The v0.32.215/217 padding-collapse attempts were chasing a phantom;
         restored to a normal, balanced value now that we know the section
         is full of real content. */
  #clients .vt-clients {
    padding-top: 40px !important;
    padding-bottom: 32px !important;
  }

  /* [5] Clients → Testimonials: v0.32.224 — Vic asked for MORE room above
         the testimonials so the clients↔testimonials hairline can breathe.
         Bumped the testimonials top padding 32 → 64. (Clients bottom stays
         32, so the line sits with generous, slightly-top-weighted spacing.) */
  section.vt {
    padding-top: 64px !important;
    padding-bottom: clamp(40px, 6vw, 64px) !important;
  }

  /* [6] Testimonials title → quote gap tightened.
         .vt-header margin-bottom was clamp(56,7vw,96) → halve it. */
  .vt-header {
    margin-bottom: clamp(28px, 3.5vw, 48px) !important;
  }


  /* [2] Portfolio: nudge the category toolbar UP closer to the title.
         Was 64px gap from .pm-intro bottom → 32px. */
  #portfolio-module .pm-intro {
    margin-bottom: 32px !important;
  }


  /* [3] Hairline separators (3/4 width, centered) at the boundaries:
         portfolio→clients and clients→testimonials.
         Implemented as pseudo-elements on the SECOND section of each pair so
         no body markup needs to change. */
  #clients,
  #testimonials {
    position: relative;
  }
  /* v0.32.214 — RESTORED (Vic reverted the v0.32.213 removal). */
  #clients::before,
  #testimonials::before {
    content: '';
    position: absolute;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 75%;
    max-width: 1100px;
    height: 1px;
    background: rgba(26, 26, 26, 0.10);   /* near-black at 10% — hairline weight */
    pointer-events: none;
    z-index: 1;
  }


  /* [5] Mobile testimonials: tighten gap between company name and quote.
         Was clamp(28px,4vw,36px) → drop to clamp(10px,2vw,16px). */
  @media (max-width: 860px) {
    .vt-feature__client {
      margin-bottom: clamp(10px, 2vw, 16px) !important;
    }
  }


  /* [2v8] Tighter mobile testimonials. The JS measures the tallest panel and pins
     all panels to that height, then `justify-content:center` on .vt-panel centers
     each panel's content vertically — which leaves big empty gaps around shorter
     quotes (e.g. SB Creative's). Switching to `flex-start` keeps content tight at
     the top of the panel; the spacing between elements is then exactly the
     margin/padding we set, not flex-distributed empty space. */
  @media (max-width: 860px) {
    .vt-panel {
      justify-content: flex-start !important;
    }
    .vt-feature__client {
      margin-bottom: 14px !important;
    }
    /* Bring stage padding-top down too — was clamp(32,5vw,48) up to 48px. */
    .vt-stage {
      padding-top: 24px !important;
    }
    /* And tighten the orange " mark's bottom margin slightly */
    .vt-feature__mark {
      margin-bottom: 12px !important;
    }
  }


  /* v0.32.140 — removed this duplicate tablet-portrait override
     entirely. It re-imposed `--vt-slot-h` and grid-template-columns
     overrides at the 761-1024px range, undoing the alignment with
     homepage. With the v0.32.140 simplification above, clients.css
     drives grid layout on both pages — no need to override here
     anymore. */


  /* [2 v8] Mobile testimonials — even more aggressive gap reduction.
     The panel is `display:flex; flex-direction:column; justify-content:center;`
     which centers the stack vertically inside the .vt-stage.
     Stage min-height effectively determines how much empty space surrounds
     the centered stack. Reducing stage min-height + tightening the inter-element
     margins both contribute to less visible vertical space. */
  @media (max-width: 860px) {
    /* Tighten the company-name → quote gap (was already tightened in v7,
       restating with stronger specificity). */
    section.vt .vt-feature__client {
      margin-bottom: 12px !important;
    }
    /* Tighten the quote → author gap. */
    section.vt .vt-feature__author-wrap {
      margin-top: 20px !important;
    }
    /* Reduce the " mark → first child gap. */
    section.vt .vt-feature__mark {
      margin-bottom: 12px !important;
    }
    /* Reduce stage padding so the centered stack sits closer to the marquee. */
    section.vt .vt-stage {
      padding-top: 24px !important;
      padding-bottom: 16px !important;
      min-height: 0 !important;
    }
  }



/* ═══════════════════════════════════════════════════════════════
   PATCH (v0.25.0) — Works archive overlay
   ───────────────────────────────────────────────────────────────
   Mirrors page-about.css PATCH 12 / page-services.css PATCH (v0.23.0)
   so /works/ feels at home next to /about/ and /services/.

   1. Section padding cadence — prototype uses several paddings on
      its own sections; we normalise top+bottom on the new wrappers
      to match the rest of the site (80px on desktop, 64/52 on
      tablet/mobile). Inner content padding stays prototype-default.
   2. Hero shell — the universal hero (page-hero.php) already pads
      itself; we suppress any duplicated #works-hero padding from
      the prototype, since the wrapper is now the universal hero.
   3. Bokami IG slot — a small reservation for the [data-bk-ig-slot]
      container so the Behold widget mounts to a min-height instead
      of collapsing during fetch. Keeps layout from jumping.
   ═══════════════════════════════════════════════════════════════ */

#works-gallery,
#portfolio-module,
#clients,
#testimonials,
#bokami,
#work-with-us {
  padding-top: 80px;
  padding-bottom: 80px;
}
@media (max-width: 1100px) {
  #works-gallery,
  #portfolio-module,
  #clients,
  #testimonials,
  #bokami,
  #work-with-us {
    padding-top: 64px;
    padding-bottom: 64px;
  }
}
@media (max-width: 720px) {
  #works-gallery,
  #portfolio-module,
  #clients,
  #testimonials,
  #bokami,
  #work-with-us {
    padding-top: 52px;
    padding-bottom: 52px;
  }
}

/* Prototype #works-hero shipped its own .ah-* layout; the universal
   hero (vw-hero-*) already controls sizing/padding so suppress any
   leftover prototype padding/min-height to keep the two from fighting. */
#works-hero {
  padding-top: 0;
  padding-bottom: 0;
}

/* Bokami IG widget mount-point — keep layout from jumping while
   Behold's iframe fetches. Matches roughly the 6-cell fallback grid
   the prototype renders inline. */
.bk-feed-grid[data-bk-ig-slot] {
  min-height: 220px;
}
.bk-feed-grid[data-bk-ig-slot] behold-widget {
  display: block;
  width: 100%;
}


/* ═══════════════════════════════════════════════════════════════
   PATCH (v0.25.2) - Defensive overflow guards
   ───────────────────────────────────────────────────────────────
   v0.25.0/0.25.1 had an enqueue-dep bug that prevented this file
   from loading; the visible symptom was the bokami section's
   verbatim inline polaroid + 7 gallery slide images rendering at
   full intrinsic resolution (~1200-1500px), bleeding off the
   viewport edges. With the dep fixed in v0.25.2 the gallery is
   contained again, but these guards make the failure mode
   self-limiting if the file ever fails to load again - the user
   sees an empty box, not a wall of bleeding cover art.

   Belt and suspenders. The .stage / .cover / .bk-* rules above
   are the actual layout; these only kick in if those rules don't.
   ═══════════════════════════════════════════════════════════════ */

/* Prevent the works-gallery slideshow from ever bleeding past
   its parent section. Even if .stage's overflow:hidden somehow
   doesn't apply, the section caps it at viewport width.
   v0.32.9 — `clip` instead of `hidden` for consistency with the
   design-system.css v0.32.8 root-level scroll-context fix. Sections
   aren't on body so they don't directly cause the Android touch bug,
   but consistency is cheap and avoids future confusion. */
#works-gallery {
  overflow-x: clip;
  max-width: 100vw;
}

/* Same defense for bokami: any verbatim inline <img> or
   background-image media stays inside the section. The bokami
   polaroid + .bk-gal-slide-bg tiles carry inline base64 from the
   prototype - left alone, those images are 1200-1500px wide.
   v0.32.9 — `clip` (see #works-gallery rule above for rationale). */
#bokami {
  overflow-x: clip;
  max-width: 100vw;
}
#bokami img,
#bokami .bk-gal-slide-bg {
  max-width: 100%;
  height: auto;
}

/* Catch-all for any verbatim section in this file: nothing should
   ever extend past the viewport horizontally on /works/.

   v0.32.9 — CRITICAL FIX. This selector targets the <body> element on
   /works/ (the body carries class `archive post-type-archive-vertwo_work`).
   v0.32.8 fixed the same bug at design-system.css level by switching
   html + body from `overflow-x: hidden` to `overflow-x: clip` — but
   this rule was undoing that fix specifically on the Works archive,
   re-creating the document-root scroll context that broke Android
   touch scroll. Vic confirmed every other page worked after v0.32.8
   but /works/ remained unscrollable on Android handsets. Switched to
   `clip` so the same Android-touch-fix applies here too. */
.archive.post-type-archive-vertwo_work {
  overflow-x: clip;
}


/* ═══════════════════════════════════════════════════════════════
   PATCH (v0.25.4) - Per-aspect client logo sizing + bokami slide
   bg position + Behold widget sizing
   ───────────────────────────────────────────────────────────────
   Three Vic-flagged polish fixes after v0.25.3 unblocked the page:

   1. Clients grid: the static-mode block above (lines ~2845)
      sets a uniform `max-width:78%; max-height:78%` on every
      .vt-logo. That overrode the per-aspect rules in clients.css
      (.vt-logo[data-aspect="ultra-wide"] / "wide" / "balanced" /
      "tall") and the per-slug refinements
      (.vt-logo[data-slug="kodansha"] etc.). Result: huge Free
      Fire next to tiny Hacktrace next to oversized Naver. We
      restore per-aspect sizing by raising the SPECIFICITY of
      those rules to beat the static-mode defaults, scoped to
      this page only.

   2. Bokami gallery slides: the prototype's
      `.bk-gal-slide-bg { inset: -50% }` 200%-overflow trick
      seems to fail in Hostinger's CSS pipeline (or Safari
      shorthand parsing). The bg div renders at the slide's
      actual dimensions instead of 200%, exposing the slide's
      yellow `background-color` on whichever side the parallax
      JS shifts the bg toward. Replace `inset:-50%` with
      explicit width/height/top/left so no shorthand
      interpretation can fail.

   3. Behold IG widget: behold-widget renders its own internal
      grid that needs a real container size. Our v0.25.2 PATCH
      kept .bk-feed-grid as a CSS grid (3 cols of 1fr each), so
      Behold rendered into the FIRST grid cell only - hence the
      tiny 6 thumbnails in a corner. When Behold is active,
      flip .bk-feed-grid to a normal block container so the
      widget gets the full column width to lay out its own grid.
   ═══════════════════════════════════════════════════════════════ */

/* v0.32.148 — the actual fix Vic's diagnostic dump nailed down.
   ─────────────────────────────────────────────────────────────
   The block that was here re-declared per-aspect bucket rules AND
   per-slug rules under `#clients[data-clients-mode="static"]` with
   a compound `.is-color` selector to win specificity against an
   older `#clients[data-clients-mode="static"] .vt-logo { max-w:78% }`
   rule. That older rule was removed in v0.32.140, leaving these
   declarations ORPHANED — and worse, the per-aspect-with-.is-color
   compound (1,4,0) BEAT the per-slug rules in clients.css (1,3,0).
   So on works archive, ultra-wide-bucketed logos (Naver, Freefire,
   SB Creative, etc.) got the generic 86%×40% bucket cap instead of
   their hand-tuned per-slug values that clients.css defines and
   the homepage uses correctly. Hence Vic's sized-different bug.

   Fix is just to delete the duplicates entirely. With nothing here:
     • clients.css's per-aspect bucket rules apply at (0,2,0)
     • clients.css's per-slug rules apply at (0,2,0), declared later
       in clients.css → source-order wins for the slugs that have
       a per-slug rule
   Result: both pages resolve to the same max-w/max-h for every
   logo, both bucketed and hand-tuned. The DOM is already shared
   via clients.js static mode (v0.32.146); now the cascade matches
   too. ─────────────────────────────────────────────────────── */

/* (2) Bokami gallery slide bg - explicit sizing instead of inset
       shorthand. Same effect (200% overflow on each axis) but
       guaranteed to work across CSS pipelines. The parallax JS's
       max ±45% shift then stays comfortably within the 50%
       overflow margin on each side. */
#bokami .bk-gal-slide-bg {
  position: absolute !important;
  top: -50% !important;
  left: -50% !important;
  right: auto !important;
  bottom: auto !important;
  inset: auto !important;
  width: 200% !important;
  height: 200% !important;
  background-size: cover !important;
  background-position: center center !important;
  background-repeat: no-repeat !important;
}

/* (3) Behold IG widget - when active, give it the full column
       to render its own grid. The 6-cell static fallback grid
       only applies when no Behold widget is present. */
.bk-feed-grid[data-bk-ig-slot]:has(behold-widget) {
  display: block !important;
  grid-template-columns: none !important;
  gap: 0 !important;
  min-height: 280px;
}
.bk-feed-grid[data-bk-ig-slot] behold-widget {
  display: block !important;
  width: 100% !important;
  min-height: 280px;
}
/* Behold's internal element gets a sensible min so its
   first-paint placeholder doesn't collapse to 0px. */
.bk-feed-grid[data-bk-ig-slot] behold-widget > * {
  width: 100%;
}


/* ═══════════════════════════════════════════════════════════════
   PATCH (v0.25.6) - Bokami slide bg: restore parallax verbatim
   ───────────────────────────────────────────────────────────────
   v0.25.5 disabled the parallax to avoid yellow leaks. Vic asked
   for verbatim prototype behavior - parallax restored. The
   prototype's CSS is `position:absolute; inset:-50%` which makes
   the bg div 200% slide width and 200% slide height, centered.
   Combined with the bokami JS's max ±45% slide-width shift, the
   bg image always covers the slide (50% margin > 45% shift).

   The yellow we saw in Vic's earlier screenshots was actually the
   source images' OWN built-in cream padding (polaroid design
   intent). When the parallax shifts, more of that padding becomes
   visible - that's the prototype's behavior with these images.
   When Vic uploads edge-to-edge gallery photography (open task,
   v0.25.0 handoff item #11), the parallax will look smoother.

   We use explicit width/height/top/left instead of the `inset`
   shorthand because v0.25.4 found `inset` shorthand sometimes
   fails through Hostinger's CSS pipeline. Explicit values are
   bullet-proof. Same effect as `inset:-50%`.
   ═══════════════════════════════════════════════════════════════ */

#bokami .bk-gal-slide-bg {
  /* Verbatim prototype behavior: 200% bg div, parallax JS-driven.
     transform is NOT pinned - JS sets it via bg.style.transform
     in js_bokami.js applyParallax() each frame. */
  position: absolute !important;
  top: -50% !important;
  left: -50% !important;
  right: auto !important;
  bottom: auto !important;
  inset: auto !important;
  width: 200% !important;
  height: 200% !important;
  background-size: cover !important;
  background-position: center center !important;
  background-repeat: no-repeat !important;
}


/* ═══════════════════════════════════════════════════════════════
   PATCH (v0.25.7) - .pm-card hover bloom: tighter + lighter
                  + mobile bokami cast nudge + arrows-right
   ───────────────────────────────────────────────────────────────
   Three surgical polish fixes Vic flagged after seeing v0.25.6:

   1. .pm-card hover bloom: prototype's stacked dark drops were
      heavy and the colored bloom was wide+diffuse. Tighten the
      colored bloom (smaller blur, less spread) and lighten the
      dark drops so the colored accent reads more vivid against
      a softer base shadow. Override the entire :hover .pm-cover
      box-shadow stack.

   2. Mobile bokami cast buttons (boro/kato/mico) sit too far
      below the character bio - large empty gap. Trim the
      .bk-cast-selector top margin at narrow widths so the
      selector cuddles up to the bio.

   3. Mobile bokami gallery arrows render at the LEFT edge of
      the wrapped .bk-gal-head. Move them to the RIGHT for
      visual consistency with the desktop layout (arrows
      always on the right of the title block).
   ═══════════════════════════════════════════════════════════════ */

/* (1) Tighter, less-dark hover bloom on .pm-card
       Note: v0.25.8 split-the-difference rule REPLACES this v0.25.7 block.
       v0.25.6 was 0 32px 70px -8px (too wide); v0.25.7 was
       0 18px 38px -10px (too small). The v0.25.8 rule below picks the
       middle. We leave this v0.25.7 block here as documentation; it's
       overridden by the v0.25.8 PATCH at the bottom. */
@media (hover:hover) and (pointer:fine) {
  .pm-card:hover .pm-cover{
    box-shadow:
      inset 1px 0 0 rgba(0,0,0,0.32),
      inset -1px 0 0 rgba(255,255,255,0.06),
      inset 0 1px 0 rgba(255,255,255,0.10),
      inset 0 -1px 0 rgba(0,0,0,0.36),
      0 4px 10px -2px rgba(0,0,0,0.10),
      0 16px 36px -8px rgba(0,0,0,0.14),
      0 28px 56px -12px rgba(0,0,0,0.08),
      0 18px 38px -10px rgba(var(--accent-rgb,30,30,40),0.65) !important;
  }
}

/* (2) Mobile: nudge bokami cast buttons up toward the bio */
@media (max-width: 760px) {
  .bk-cast-selector {
    margin-top: 16px !important; /* was implicit ~32-48px from stage padding */
    padding-top: 4px;
  }
  .bk-stage-text {
    margin-bottom: 0 !important;
  }
}

/* (3) Mobile: move bokami gallery arrows to the right of the
       wrapped header. The default .bk-gal-head uses
       `display:flex; justify-content:space-between; flex-wrap:wrap`
       which sends the controls to the start of the next line
       when wrapped. Force them to align right via margin-left:auto
       so they hug the right edge regardless of wrap behavior. */
@media (max-width: 760px) {
  .bk-gal-head {
    /* Stay as flex-wrap, but ensure controls land on the right
       even when they wrap to a new row. */
    align-items: flex-start;
  }
  .bk-gal-controls {
    margin-left: auto !important;
    align-self: flex-start;
    order: 2;
  }
  .bk-gal-head-left {
    flex: 1 1 auto;
    order: 1;
  }
}


/* ═══════════════════════════════════════════════════════════════
   PATCH (v0.25.8) - Bloom mid-radius + iPad gallery hang fix
                  + double-tap zoom block + bokami mobile separator
   ───────────────────────────────────────────────────────────────
   Four fixes Vic flagged after seeing v0.25.7:

   (1) .pm-card hover bloom: v0.25.6 (0 32px 70px -8px, 0.55) was
       too wide+diffuse; v0.25.7 (0 18px 38px -10px, 0.65) too
       tight. v0.25.8 picks the middle: 0 24px 52px -10px with
       0.60 opacity. Spreads enough to feel ambient, not so much
       that it floods.

   (2) iPad landscape "Our Works" gallery hangs on init - the
       refracted-glass slideshow's 20-pane CSS backdrop-filter
       fallback (when SUPPORTS_REFRACTION is false in
       works-archive.js MODULE 1/4) is still expensive at iPad
       landscape resolutions. The hang happens because the
       backdrop-filter recomposite blocks the main thread until
       the page settles. Fix: hide the .glass-wall entirely on
       iPad-class viewports - the slideshow still works, you
       just lose the frosted-glass visual overlay (acceptable
       trade-off vs. multi-second hang).

   (3) iOS double-tap-to-zoom on the slideshow zooms into the
       inline base64 covers (~100-200KB each x ~20 in DOM),
       blowing memory and blanking other sections. Fix:
       `touch-action: manipulation` on the .stage prevents
       double-tap zoom while preserving pan/pinch. Plus
       `pointer-events: none` on the whole hero+wall so taps
       only land on the .track for drag scrolling.

   (4) Bokami mobile: gap between the IG feed (top stack) and
       the 4 social cards (bottom stack) is too big, and there
       was no visual divider between the two zones. Add a
       subtle horizontal separator + tighten the gap.
   ═══════════════════════════════════════════════════════════════ */

/* (1) Mid-radius hover bloom - splits v0.25.6 / v0.25.7 difference */
@media (hover:hover) and (pointer:fine) {
  .pm-card:hover .pm-cover{
    box-shadow:
      /* Inset edges - same paper-feel from v0.25.7 */
      inset 1px 0 0 rgba(0,0,0,0.32),
      inset -1px 0 0 rgba(255,255,255,0.06),
      inset 0 1px 0 rgba(255,255,255,0.10),
      inset 0 -1px 0 rgba(0,0,0,0.36),
      /* Drop shadows - keep the v0.25.7 lightening for the dark
         base, just bump opacities slightly for the wider bloom */
      0 4px 10px -2px rgba(0,0,0,0.12),
      0 16px 36px -8px rgba(0,0,0,0.16),
      0 28px 56px -12px rgba(0,0,0,0.10),
      /* Colored bloom - mid-radius. Blur 38 -> 52 (between
         v0.25.7's 38 and v0.25.6's 70). Negative spread held at
         -10 (between v0.25.6's -8 and v0.25.7's -10). Opacity
         0.60 (between v0.25.6's 0.55 and v0.25.7's 0.65). */
      0 24px 52px -10px rgba(var(--accent-rgb,30,30,40),0.60) !important;
  }
}

/* (2) iPad landscape gallery hang - hide .glass-wall on iPad-class
       viewports. The wall's 20 backdrop-filter panes are the
       hot path for the hang; without them the slideshow renders
       cleanly with covers + hero + meta only.
       Targeting:
         (a) iPad landscape: 1024-1366 wide
         (b) iPad portrait: also 1024 wide (covered)
         (c) Any tablet-class touch device with coarse pointer
       Modern desktop browsers (>=1367 wide, fine pointer) keep
       the full glass effect. */
@media (max-width: 1366px) and (pointer: coarse),
       (max-width: 1366px) and (hover: none) {
  #works-gallery .glass-wall { display: none !important; }
  #works-gallery .desat-overlay { display: none !important; }
}

/* (3) Disable double-tap zoom on the slideshow. touch-action:
       manipulation tells iOS to skip the double-tap zoom delay
       and zoom gesture, while still allowing single-tap and pan.
       The covers and hero get pointer-events: none so taps only
       fall through to the .track (the drag-scroll target). */
#works-gallery .stage,
#works-gallery .track,
#works-gallery .cover,
#works-gallery .hero,
#works-gallery .glass-wall {
  touch-action: manipulation !important;
}
#works-gallery .cover img,
#works-gallery .hero-backdrop,
#works-gallery .hero-main {
  /* Discourage iOS from offering a context menu on long-press,
     which is another path to image zoom. */
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
  pointer-events: none;
}
#works-gallery .glass-wall,
#works-gallery .hero,
#works-gallery .desat-overlay,
#works-gallery .vignette-overlay {
  /* These layers are decorative; tap targeting on .track only. */
  pointer-events: none !important;
}

/* (4) Bokami mobile: separator + gap tightening between IG feed
       and the 4 social cards. The .bk-social-card flips from
       2-col (desktop) to 1-col (mobile) at max-width:760px; we
       use the existing media query and add the divider via
       .bk-feed-col::after pseudo so it sits exactly between the
       two columns when stacked. */
@media (max-width: 760px) {
  /* Tighten the mobile gap inside .bk-social-card from 24px to 18px */
  .bk-social-card {
    gap: 18px !important;
  }
  /* Subtle white horizontal separator between IG feed and social
     cards. White at low opacity blends with the dark social-card
     background; the line sits flush with the feed grid's left/right
     edges (which are the inner content edges of .bk-social-card's
     padding box). */
  .bk-feed-col {
    position: relative;
    padding-bottom: 18px; /* room for the separator + a little air */
    border-bottom: 1px solid rgba(255,255,255,0.16);
  }
  .bk-soc-col {
    padding-top: 4px;
  }
}


/* ═══════════════════════════════════════════════════════════════
   PATCH (v0.25.9) - Revert mobile glass kill + harder iPad fix
                  + bokami IG bottom gap + .pm-card year/client chips
   ───────────────────────────────────────────────────────────────
   Four fixes Vic flagged after seeing v0.25.8:

   (1) v0.25.8 hid .glass-wall on `(max-width:1366px) and
       (pointer:coarse)` which catches mobile + iPad portrait
       too. Vic confirmed the hang is iPad landscape ONLY -
       portrait + mobile work fine. Restore the glass effect
       on mobile / iPad portrait by overriding that with a more
       specific selector that re-enables the wall everywhere
       EXCEPT iPad landscape, then re-disable + harden iPad
       landscape via a tighter media query.

   (2) v0.25.8's iPad landscape hide-glass-wall fix didn't
       actually stop the hang. The remaining hot path is
       likely the slideshow init itself - 20 cover img loads
       + parallax rAF loop. Trying a more aggressive fix:
       hide the entire .stage (slideshow body) on iPad
       landscape, leaving just the wg-head with a friendly
       note. Vic loses the slideshow on iPad landscape but
       gets a working page; rotating to portrait restores it.

   (3) Bokami IG section bottom gap on mobile is still huge
       because PATCH (v0.25.4) set `.bk-feed-grid[data-bk-ig-slot]
       { min-height: 280px }` and `behold-widget { min-height:
       280px }`. When the rendered widget is shorter than
       280px, the empty space is the gap. Drop the min-height
       when the widget is present (let intrinsic size win)
       and only keep min-height for the EMPTY-state fallback
       grid.

   (4) Webtoon cover meta row chips: year becomes a solid
       black chip at 90% opacity; client becomes a solid
       orange chip at 90% opacity. Hide the .pm-meta-dot
       separator and the .pm-client-dot prefix since the
       chips themselves provide enough visual separation.
   ═══════════════════════════════════════════════════════════════ */

/* (1a) Restore .glass-wall + .desat-overlay on mobile / iPad portrait
        by re-enabling them. Specificity tournament: the v0.25.8
        rule used `#works-gallery .glass-wall { display:none !important }`
        - we override at the same specificity, but with a media query
        that's MORE restrictive (the v0.25.8 rule has been left in
        place above; our :not(orientation:landscape) wins because
        we add this overlay LATER in cascade order at same
        specificity, AND we include the CSS-cascade tiebreaker
        of being later). To be bulletproof, we use a higher-
        specificity selector. */
@media (max-width: 1366px) and (pointer: coarse) and (orientation: portrait),
       (max-width: 1366px) and (hover: none) and (orientation: portrait),
       (max-width: 760px) {
  #works-gallery .glass-wall { display: block !important; }
  #works-gallery .desat-overlay { display: block !important; }
}

/* (1b/2) iPad landscape ONLY - hide the entire slideshow stage.
          The slideshow JS still runs but on a hidden element so
          the heavy backdrop-filter composite never lands on screen.
          Visible UX: just the section header + a small "rotate to
          view" hint. Trade-off: no slideshow on iPad landscape;
          rotate to portrait to view. The hang is unblocked. */
@media (min-width: 1024px) and (max-width: 1366px) and (orientation: landscape) and (pointer: coarse),
       (min-width: 1024px) and (max-width: 1366px) and (orientation: landscape) and (hover: none) {
  /* Hide the whole stage - removes the heavy composite cost. */
  #works-gallery .stage {
    display: none !important;
  }
  /* Friendly note in the section so it's not blank. */
  #works-gallery::after {
    content: "Best viewed in portrait or on desktop \2014  rotate your iPad to see the gallery.";
    display: block;
    text-align: center;
    color: rgba(255,255,255,0.55);
    font-size: 0.9rem;
    padding: 80px 24px;
    font-style: italic;
    max-width: 520px;
    margin: 0 auto;
    line-height: 1.55;
  }
}

/* (3) Bokami IG section gap fix - drop min-height when widget
       actually rendered. Lets the widget's own intrinsic size
       drive vertical space. Behold widgets typically render
       ~200-260px tall depending on layout/columns. */
.bk-feed-grid[data-bk-ig-slot]:has(behold-widget) {
  min-height: 0 !important;
}
.bk-feed-grid[data-bk-ig-slot]:has(behold-widget) behold-widget {
  min-height: 0 !important;
}
/* Keep the 220-280px min-height only when NO behold-widget is
   present (i.e., the static 6-cell fallback grid). */
.bk-feed-grid[data-bk-ig-slot]:not(:has(behold-widget)) {
  min-height: 220px;
}

/* (4) .pm-card meta-row: year as solid black chip 90%, client
       as solid orange chip 90%. Hide the dot/separator. */
.pm-year {
  background: rgba(0, 0, 0, 0.9) !important;
  color: #fff !important;
  padding: 3px 8px !important;
  border-radius: 4px !important;
  font-size: 0.66rem !important;
  font-weight: 700 !important;
  letter-spacing: 0.04em !important;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  display: inline-block;
}
.pm-meta-dot {
  display: none !important;
}
.pm-client {
  background: rgba(242, 122, 15, 0.9) !important; /* var(--orange) at 90% */
  border: 1px solid rgba(242, 122, 15, 0.95) !important;
  color: #fff !important;
  padding: 3px 8px !important;
  border-radius: 4px !important;
  font-size: 0.66rem !important;
  font-weight: 700 !important;
  letter-spacing: 0.04em !important;
  text-transform: uppercase;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  gap: 0;
}
.pm-client-dot {
  display: none !important;
}
/* On hover, slightly intensify (matches v0.25.7's bloom mood) */
@media (hover:hover) and (pointer:fine) {
  .pm-card:hover .pm-client {
    background: rgba(242, 122, 15, 1) !important;
    border-color: rgba(242, 122, 15, 1) !important;
  }
}


/* ═══════════════════════════════════════════════════════════════
   PATCH (v0.25.10) - Robust glass-wall restore + iPad touch fix
   ───────────────────────────────────────────────────────────────
   Two issues Vic flagged after v0.25.9:

   (1) Glass-wall STILL hidden on iPad portrait. Investigation:
       v0.25.8's `display:none !important` rule and v0.25.9's
       `display:block !important` rule have equal specificity
       (0,1,1 = one ID + one class). Source-order tiebreaker
       should make v0.25.9 win on iPad portrait. But cross-
       browser cascade interpretation of competing !important
       at equal specificity isn't always reliable, and iPad
       Chrome may interpret `pointer: coarse` differently.

       Cleanest fix: make glass-wall DEFAULT visible (override
       at higher selector specificity, no media query), and
       ONLY hide it on iPad landscape via a more specific
       inline override below. Forget the cascade tiebreaker;
       use specificity instead.

   (2) iPad landscape: even with .stage hidden, horizontal
       swipes inside the (now hidden) gallery zone cause iOS
       to bounce-scroll the page vertically. The .track is
       inside the now-hidden .stage but iOS may still register
       touch events on the section. Apply touch-action: pan-y
       to the entire #works-gallery so horizontal swipes are
       refused completely - browser falls back to default
       no-action and no vertical bounce kicks in.

   (3) iPad Chrome refraction "missing": NOT a bug. iOS
       browsers all use Apple's WebKit (Apple's policy since
       App Store rules). iPad Chrome has the same backdrop-
       filter:url() limitation as iPad Safari. The
       SUPPORTS_REFRACTION JS check correctly returns false
       and the per-pane CSS backdrop-filter fallback runs.
       Same look as iPad Safari - intentional, not avoidable
       without WebGL workaround. Documented in handoff §X.
   ═══════════════════════════════════════════════════════════════ */

/* (1) Robust glass-wall restore via HIGHER specificity selector.
       Two ID selectors (#works-gallery #glass-wall via the wall's
       id="glass-wall") = specificity (2,0,0). Beats the
       (1,1,0) of "#works-gallery .glass-wall" from PATCH v0.25.8.
       No media query needed - this becomes the unconditional
       baseline for non-iPad-landscape devices. */
#works-gallery #glass-wall {
  display: flex !important;
}
#works-gallery #wall-tint,
#works-gallery .desat-overlay,
#works-gallery .vignette-overlay {
  /* same specificity bump for the related decorative layers */
  display: block !important;
}

/* (1b) iPad landscape ONLY: hide the entire .stage with an even
        higher specificity override (still using ID selector chain
        for parity with the restore rule above). */
@media (min-width: 1024px) and (max-width: 1366px) and (orientation: landscape) and (pointer: coarse),
       (min-width: 1024px) and (max-width: 1366px) and (orientation: landscape) and (hover: none) {
  #works-gallery #stage {
    display: none !important;
  }
}

/* (2) iPad landscape touch fix: refuse horizontal pan on the
       #works-gallery section so iOS doesn't bounce-scroll. */
@media (min-width: 1024px) and (max-width: 1366px) and (orientation: landscape) and (pointer: coarse),
       (min-width: 1024px) and (max-width: 1366px) and (orientation: landscape) and (hover: none) {
  #works-gallery {
    touch-action: pan-y !important;
    overflow: hidden !important; /* belt+suspenders against any horizontal scroll */
  }
  /* Belt: ensure no descendant tries to claim a manipulation/none
     touch-action that would override the section's pan-y. */
  #works-gallery * {
    touch-action: pan-y !important;
  }
}


/* ═══════════════════════════════════════════════════════════════
   PATCH (v0.25.11) - Desktop-only #works-gallery
   ───────────────────────────────────────────────────────────────
   After 4 rounds of trying to make the refracted-glass slideshow
   render correctly + performantly on iPad / mobile (v0.25.8 +
   v0.25.9 + v0.25.10), Vic's call: just hide the slideshow on
   anything that isn't a desktop. Less headache, no perf surface
   on touch devices, no compositor edge cases.

   On non-desktop, mobile/tablet visitors scroll: Hero -> Portfolio
   Module (the .pm-* "full archive" grid) -> Clients -> ... The
   slideshow (a "featured titles" showcase on top of the archive)
   is decorative; the actual catalog (.pm-*) is always reachable.

   Trigger: hide the entire #works-gallery section on:
     - mobile (any width <= 1024px)
     - iPad portrait (max-width 1366 + portrait + coarse pointer)
     - iPad landscape (max-width 1366 + landscape + coarse pointer)
     - any tablet-class touch device (coarse pointer + max-width 1366)
   Show on:
     - desktop only (min-width 1367px AND fine pointer)

   The previous PATCH (v0.25.8/9/10) glass-wall + #stage toggling
   rules become dead code on mobile+tablet (the whole section is
   hidden) but stay valid on desktop, where they don't conflict
   with this section-level rule.
   ═══════════════════════════════════════════════════════════════ */

/* Default: hide the entire works-gallery section.
   Specificity: single ID (1,0,0). Plus !important. */
#works-gallery {
  display: none !important;
}

/* Show ONLY on desktop: width >= 1367 AND fine pointer (mouse-class).
   Specificity same (1,0,0); media query restricts to desktop. */
@media (min-width: 1367px) and (pointer: fine) {
  #works-gallery {
    display: block !important;
  }
}


/* ═══════════════════════════════════════════════════════════════
   PATCH (v0.25.12) - Smash Balloon Instagram Feed wrapper
   ───────────────────────────────────────────────────────────────
   Switched bokami IG integration from Behold.so to Smash Balloon
   Instagram Feed plugin (free WP plugin) for traffic-tier reasons:
   Behold meters by views (paid tier needed at the 265K/month
   bokami.world traffic); Smash Balloon caches feed locally on
   the WP server, no view threshold.

   Smash Balloon renders its feed inside a wrapper div with class
   `.sbi` (and id `#sb_instagram` in some configurations). The
   v0.25.4 / v0.25.9 PATCHes targeted `behold-widget` via :has();
   we add the equivalent rules for `.sbi` here. The legacy
   behold-widget rules stay in place but are dead code now (the
   element no longer renders since archive-vertwo_work.php was
   updated to use shortcode_exists('instagram-feed') instead).

   Two effects to mirror from the Behold rules:
   (a) When Smash Balloon's feed renders, flip .bk-feed-grid from
       its CSS-grid (3 cols) layout to display:block so the plugin
       can lay out its own internal grid full-width.
   (b) Drop any min-height on .bk-feed-grid when Smash Balloon
       is present (let the widget's own intrinsic height drive
       vertical space). Keep the 220px min-height only when no
       widget is present (the static 6-cell @bokami.world fallback).
   ═══════════════════════════════════════════════════════════════ */

.bk-feed-grid[data-bk-ig-slot]:has(.sbi),
.bk-feed-grid[data-bk-ig-slot]:has(#sb_instagram) {
  display: block !important;
  grid-template-columns: none !important;
  gap: 0 !important;
  min-height: 0 !important;
}
.bk-feed-grid[data-bk-ig-slot] .sbi,
.bk-feed-grid[data-bk-ig-slot] #sb_instagram {
  display: block !important;
  width: 100% !important;
  min-height: 0 !important;
}


/* ═══════════════════════════════════════════════════════════════
   PATCH (v0.25.13) - Bokami social card: 4-stack relayout
   ───────────────────────────────────────────────────────────────
   Vic wants the right column's 4 social cards (IG / FB / TT /
   bokami.world) to stack vertically on desktop instead of the
   2x2 grid - frees the left column (IG feed) to be wider so
   more Smash Balloon thumbnails fit.

   Desktop (>=1024px wide):
     - .bk-social-card columns: 1fr 1fr -> 1.7fr 1fr
       (IG feed gets ~63% of width, social column ~37%)
     - .bk-soc-list: 2 cols -> 1 col (single vertical stack)
     - .bk-soc-card aspect: 5/4 portrait -> 16/9 wide
       (shorter vertically so 4 stacked cards fit comfortably)

   Mobile (<= 760px wide):
     Try 1x4 horizontal row first. If too cramped (cards under
     ~80px wide each), fall back to 2x2.
       - >= 580px: 1x4 horizontal (each card ~120px+ wide)
       - <  580px: 2x2 (fallback)

   The grid auto-stacks the .bk-social-card 2-col grid to 1-col
   at the existing max-width:760px breakpoint (already in CSS),
   which puts feed-col on top of soc-col. The soc-col then
   chooses between 1x4 horizontal vs 2x2 grid based on width.
   ═══════════════════════════════════════════════════════════════ */

/* DESKTOP - widen IG feed column + stack 4 social cards vertically */
@media (min-width: 1024px) {
  .bk-social-card {
    grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr) !important;
  }
  .bk-soc-list {
    grid-template-columns: 1fr !important;
    gap: 14px !important;
  }
  /* Wider, shorter cards fit a 1-col stack of 4 better than 5/4 */
  .bk-soc-card {
    aspect-ratio: 16 / 7 !important;
  }
}

/* TABLET (between mobile and desktop) - keep prototype 2x2 layout */
@media (min-width: 761px) and (max-width: 1023px) {
  /* Inherits prototype default (2x2 grid, 5/4 aspect). No override. */
}

/* MOBILE WIDE (580-760px) - try 1x4 horizontal row */
@media (min-width: 580px) and (max-width: 760px) {
  .bk-soc-list {
    grid-template-columns: repeat(4, minmax(0, 1fr)) !important;
    gap: 8px !important;
  }
  /* Wider+shorter cards fit a row of 4 on narrower screens */
  .bk-soc-card {
    aspect-ratio: 1 / 1 !important;
  }
  /* Trim padding so the row doesn't overflow */
  .bk-soc-card-inner {
    padding: 12px !important;
  }
}

/* MOBILE NARROW (<580px) - fall back to 2x2 (prototype default) */
@media (max-width: 579px) {
  .bk-soc-list {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
    gap: 10px !important;
  }
  .bk-soc-card {
    aspect-ratio: 5 / 4 !important;
  }
}


/* ═══════════════════════════════════════════════════════════════
   PATCH (v0.25.14) - Bokami social cards: shorter desktop + equal mobile
   ───────────────────────────────────────────────────────────────
   Two adjustments after Vic saw v0.25.13 live:

   (1) Desktop social cards too tall - the 16/7 aspect from
       v0.25.13 still left the 4-card stack quite tall. Halve
       the height by going to ~4/1 aspect (4x wider than tall),
       matching Vic's "small boxes are enough" feedback.

   (2) Mobile 2x2 grid: Vic noticed possible visual size
       difference between rows. The grid is 1fr/1fr with
       aspect-ratio:5/4 so cards SHOULD be identical, but iOS
       Safari sometimes rounds aspect-ratio + flex-children
       differently. Lock to width:100% + height:100% inside
       a fixed grid cell, and explicitly equal-row tracks
       (grid-auto-rows: 1fr) to guarantee row heights match.
   ═══════════════════════════════════════════════════════════════ */

/* (1) Desktop: shorter social cards */
@media (min-width: 1024px) {
  .bk-soc-card {
    aspect-ratio: 4 / 1 !important;
  }
}

/* (2) Mobile-narrow 2x2: lock to equal cell sizes */
@media (max-width: 579px) {
  .bk-soc-list {
    /* grid-auto-rows:1fr forces both rows to share equal height
       regardless of any per-card aspect quirks. */
    grid-auto-rows: 1fr !important;
    /* Plus explicit row template for the 2 rows */
    grid-template-rows: 1fr 1fr !important;
  }
  .bk-soc-card {
    /* Fill the grid cell completely - aspect-ratio still applies
       but width:100% + height:100% guarantees equal physical
       footprint per cell. */
    width: 100% !important;
    height: 100% !important;
    /* Drop the aspect-ratio constraint since the cell already has
       a uniform 1fr/1fr rhythm. Aspect-ratio was creating subtle
       per-card height differences from rounding. */
    aspect-ratio: auto !important;
    min-height: 110px;
  }
}


/* ═══════════════════════════════════════════════════════════════
   PATCH (v0.25.15) - Wider IG feed + tablet adopt + mobile equal-height
   ───────────────────────────────────────────────────────────────
   Three Vic-flagged adjustments after v0.25.14:

   (1) The 1.7fr / 1fr column split (~63% / 37%) was still too
       generous to the social column. Vic wants ~60% less width
       on social so IG feed gets the rest. Switch from fr-ratio
       to a FIXED 200px social column - IG feed stretches to
       fill everything else regardless of viewport width.

   (2) Apply the new desktop layout (1x4 social stack + wider
       IG feed) to TABLET (>=761px) too, not just desktop
       (>=1024px). Lower the breakpoint.

   (3) Mobile 2x2 still showing unequal card heights. Root cause
       in v0.25.14: `aspect-ratio: auto` on .bk-soc-card let
       content drive height per card, and IG/FB cards have
       longer text (handle + service name) than TikTok/web,
       making them taller. Fix: explicit fixed `height` on each
       card so all 4 lock to the same dimensions regardless of
       content.
   ═══════════════════════════════════════════════════════════════ */

/* (1) + (2) Tablet and desktop: fixed 200px social column.
       IG feed gets everything else (which is plenty of room
       for Smash Balloon's grid). */
@media (min-width: 761px) {
  .bk-social-card {
    grid-template-columns: minmax(0, 1fr) 200px !important;
    gap: 20px !important;
  }
  .bk-soc-list {
    grid-template-columns: 1fr !important;
    gap: 12px !important;
  }
  /* Keep cards short - 4/1 aspect from v0.25.14 */
  .bk-soc-card {
    aspect-ratio: 4 / 1 !important;
  }
  /* Inner padding pulled in slightly to fit the narrower column */
  .bk-soc-card-inner {
    padding: 14px !important;
  }
}

/* (3) Mobile 2x2: lock all 4 cards to the SAME explicit height.
       Drops any content-driven sizing - all cards are 130px tall,
       full grid-cell width. */
@media (max-width: 579px) {
  .bk-soc-list {
    /* No more grid-auto-rows / grid-template-rows tricks - just
       give each cell auto height; the cards themselves will be
       a fixed 130px tall via the rule below. */
    grid-auto-rows: auto !important;
    grid-template-rows: auto auto !important;
  }
  .bk-soc-card {
    width: 100% !important;
    height: 130px !important;       /* fixed - all cards identical */
    min-height: 130px !important;
    max-height: 130px !important;
    aspect-ratio: auto !important;
  }
}


/* ═══════════════════════════════════════════════════════════════
   PATCH (v0.25.16) - Social cards evenly distributed full-height
   ───────────────────────────────────────────────────────────────
   Vic's ask: 4 social cards should fill the social column from
   top (aligned with Follow button) to bottom (aligned with the
   bottom of the IG feed grid), distributed evenly.

   Mechanic: the .bk-social-card grid container already
   align-items:stretch'es its two columns to the same height.
   The social column just needs its inner .bk-soc-list to fill
   that height with 4 equal-height rows.

   Chain:
     .bk-soc-col { flex column, fills cell }
       .bk-soc-list { flex:1, grid 4 rows × 1 col }
         .bk-soc-card × 4 { fill cell, no aspect-ratio }

   Apply to tablet + desktop (>=761px) per Vic's request.
   Mobile rules from earlier PATCHes still apply unchanged.
   ═══════════════════════════════════════════════════════════════ */

@media (min-width: 761px) {
  .bk-social-card {
    /* CSS grid default is align-items: stretch - explicit for clarity */
    align-items: stretch !important;
  }

  .bk-feed-col,
  .bk-soc-col {
    /* Both cols are flex containers so children can flex:1 to fill */
    display: flex !important;
    flex-direction: column !important;
    min-height: 0 !important;
  }

  .bk-soc-list {
    /* 4 equal-height rows filling all available vertical space */
    display: grid !important;
    grid-template-columns: 1fr !important;
    grid-template-rows: repeat(4, 1fr) !important;
    gap: 12px !important;
    flex: 1 1 auto !important;       /* take all remaining height in soc-col */
    min-height: 0 !important;        /* allow grid to shrink-fit when needed */
    height: 100% !important;
  }

  .bk-soc-card {
    /* Drop the aspect-ratio - row height is now driven by the
       grid-template-rows: repeat(4, 1fr) above. Each card fills
       its grid cell completely. */
    aspect-ratio: auto !important;
    width: 100% !important;
    height: 100% !important;
    min-height: 0 !important;
    /* Sane minimum so cards don't get unreadable on very short
       feed columns (e.g., when Smash Balloon is set to fewer rows) */
    min-height: 72px !important;
  }
}


/* ═══════════════════════════════════════════════════════════════
   PATCH (v0.25.17) - Cap social stack height + animated hover icons
   ───────────────────────────────────────────────────────────────
   Two fixes:

   (1) Vic flagged the social column extending past the IG feed
       grid's bottom even after v0.25.16. Root cause: the parent
       grid stretches both columns to the height of the larger,
       and Smash Balloon's grid renders shorter than the soc-list's
       4 cards × 1fr fill. Net: cell height = soc-list natural,
       feed-col stretches to match (with empty space below the
       feed-grid), and social cards extend below the visible
       thumbnails.

       Fix: switch from "fill column height" to "fixed compact
       cards naturally stacked, anchored to top." 4 cards × 88px
       + 3 × 12px gap = 372px total - matches typical Smash
       Balloon 6-thumb feed-grid height closely. Cards no longer
       overflow the feed-grid bottom.

   (2) Vic asked for animated social icons. Added subtle CSS
       keyframe animations triggered on .bk-soc-card hover:
         - IG: lens pulse + ring rotation
         - FB: gentle bob
         - TikTok: subtle rotation
         - Globe: rotation
       No new JS library; vanilla CSS keyframes on the existing
       inline SVG paths. Respects prefers-reduced-motion.
   ═══════════════════════════════════════════════════════════════ */

/* (1) Cap social stack height with fixed-compact cards */
@media (min-width: 761px) {
  .bk-soc-col {
    /* Anchor stack to top so it doesn't drift visually if cell
       grows. The 88px × 4 + 12px × 3 = 372px stack will fit
       comfortably above the cell bottom. */
    justify-content: flex-start !important;
  }
  .bk-soc-list {
    /* Drop grid + flex:1 from v0.25.16 - use natural-height flex
       column instead so cards don't expand to fill. */
    display: flex !important;
    flex-direction: column !important;
    flex: 0 0 auto !important;
    height: auto !important;
    min-height: 0 !important;
    gap: 12px !important;
  }
  .bk-soc-card {
    /* Fixed compact height per card. Total: 4 × 88 + 3 × 12 = 372px.
       Matches typical Smash Balloon 6-thumb feed grid height
       (6 thumbs in 2 rows × ~150px = ~310px + ~50-60px header). */
    aspect-ratio: auto !important;
    width: 100% !important;
    height: 88px !important;
    min-height: 88px !important;
    flex: 0 0 88px !important;
  }
}

/* (2) Animated icons on hover.
       Each social card has class .bk-soc-card--{ig|fb|tt|web}.
       The .bk-soc-card-icon contains an inline SVG. We animate
       the SVG itself or its child shapes per platform. */

@keyframes vt-soc-ig-pulse {
  0%, 100% { transform: translateZ(18px) scale(1); }
  50%      { transform: translateZ(18px) scale(1.08); }
}
@keyframes vt-soc-fb-bob {
  0%, 100% { transform: translateZ(18px) translateY(0); }
  50%      { transform: translateZ(18px) translateY(-3px); }
}
@keyframes vt-soc-tt-rotate {
  0%   { transform: translateZ(18px) rotate(0deg); }
  100% { transform: translateZ(18px) rotate(360deg); }
}
@keyframes vt-soc-web-spin {
  0%   { transform: translateZ(18px) rotate(0deg); }
  100% { transform: translateZ(18px) rotate(360deg); }
}

@media (hover: hover) and (pointer: fine) {
  .bk-soc-card--ig:hover .bk-soc-card-icon {
    animation: vt-soc-ig-pulse 1.4s ease-in-out infinite;
  }
  .bk-soc-card--fb:hover .bk-soc-card-icon {
    animation: vt-soc-fb-bob 1.2s ease-in-out infinite;
  }
  .bk-soc-card--tt:hover .bk-soc-card-icon {
    animation: vt-soc-tt-rotate 2.4s linear infinite;
  }
  .bk-soc-card--web:hover .bk-soc-card-icon {
    animation: vt-soc-web-spin 3s linear infinite;
  }
}

/* Respect prefers-reduced-motion - kill animations entirely */
@media (prefers-reduced-motion: reduce) {
  .bk-soc-card--ig:hover .bk-soc-card-icon,
  .bk-soc-card--fb:hover .bk-soc-card-icon,
  .bk-soc-card--tt:hover .bk-soc-card-icon,
  .bk-soc-card--web:hover .bk-soc-card-icon {
    animation: none !important;
  }
}

/* Mobile: keep 130px card height from v0.25.15 (override v0.25.17's
   88px which was tablet+desktop-scoped via @media query already, but
   be explicit). */
@media (max-width: 760px) {
  .bk-soc-card {
    /* No height override here - lets v0.25.15's 130px (mobile-narrow)
       and v0.25.13's 1/1 aspect (mobile-wide 580-760) win. */
  }
}


/* ═══════════════════════════════════════════════════════════════
   PATCH (v0.25.18) - Even distribution to feed-grid bottom + flip-only-icon
   ───────────────────────────────────────────────────────────────
   Two fixes after v0.25.17:

   (1) Distribution. v0.25.17 went with fixed 88px cards stacked
       from top - too short, didn't reach feed-grid bottom
       (Vic's red line). v0.25.16's approach (flex:1 + grid 4
       rows × 1fr) was right in spirit but min-height: 72px on
       each card pushed soc-list's MIN-CONTENT to 324px, which
       sometimes exceeded feed-col content height and caused the
       cell to over-stretch.

       Real fix: keep flex:1 + grid 4 rows × 1fr distribution,
       but set min-height: 0 on cards (NO floor) so the soc-list's
       min-content is essentially 0. Cell then sizes to the
       feed-col content (head + Smash Balloon grid) exactly.
       4 cards distribute evenly across that height. Top of #1
       aligns with top of feed-head; bottom of #4 aligns with
       bottom of feed-grid.

   (2) Animate JUST the SVG, not the outer box. Removed v0.25.17's
       per-card pulse/bob/rotate that targeted .bk-soc-card-icon
       (which is the rounded-square box). Added a single looping
       horizontal flip animation on .bk-soc-card-icon svg,
       staggered by 0.4s between cards so they cascade.
       prefers-reduced-motion respected (animation: none).
   ═══════════════════════════════════════════════════════════════ */

/* (1) Even distribution - restore grid 4×1fr but with min-height: 0 */
@media (min-width: 761px) {
  .bk-soc-col {
    /* Allow children to fill column height */
    justify-content: stretch !important;
  }
  .bk-soc-list {
    /* Grid distribution - 4 equal rows fill available height */
    display: grid !important;
    grid-template-columns: 1fr !important;
    grid-template-rows: repeat(4, 1fr) !important;
    gap: 12px !important;
    flex: 1 1 auto !important;
    height: 100% !important;
    min-height: 0 !important;
  }
  .bk-soc-card {
    /* CRITICAL - min-height: 0 (no floor) lets the soc-list's
       MIN-CONTENT shrink so the parent grid cell sizes to feed-col's
       content height instead of the soc-list's natural content.
       Then 4 × 1fr distributes whatever height we get. */
    aspect-ratio: auto !important;
    width: 100% !important;
    height: 100% !important;
    min-height: 0 !important;        /* no floor */
    flex: 1 1 auto !important;       /* allow grow/shrink */
  }
}

/* (2) Cancel v0.25.17 box-targeted animations + add icon-only flip */
@media (hover: hover) and (pointer: fine) {
  .bk-soc-card--ig:hover .bk-soc-card-icon,
  .bk-soc-card--fb:hover .bk-soc-card-icon,
  .bk-soc-card--tt:hover .bk-soc-card-icon,
  .bk-soc-card--web:hover .bk-soc-card-icon {
    animation: none !important;        /* reset v0.25.17 box anims */
  }
}

/* Looping horizontal flip on the SVG itself (no :hover gate -
   always animating). Stagger per card so they cascade rather
   than syncing. */
@keyframes vt-soc-icon-flip {
  /* Hold flat for the first ~25%, then flip 360deg, hold flat
     again at the end. Smooth ease-in-out makes the flip feel
     deliberate, not frantic. */
  0%, 22%   { transform: rotateY(0deg); }
  78%, 100% { transform: rotateY(360deg); }
}

.bk-soc-card-icon svg {
  /* Apply to the inline SVG inside the icon box. The box itself
     stays put; only the SVG flips. */
  animation: vt-soc-icon-flip 4s ease-in-out infinite;
  transform-origin: center center;
  /* preserve-3d on the parent + perspective on the box gives
     the flip depth instead of a flat scale-X-to-0-and-back. */
}
.bk-soc-card-icon {
  perspective: 600px;
}

/* Stagger animation-delay so cards cascade. */
.bk-soc-card--ig  .bk-soc-card-icon svg { animation-delay: 0s;   }
.bk-soc-card--fb  .bk-soc-card-icon svg { animation-delay: 0.4s; }
.bk-soc-card--tt  .bk-soc-card-icon svg { animation-delay: 0.8s; }
.bk-soc-card--web .bk-soc-card-icon svg { animation-delay: 1.2s; }

/* Reduced motion - kill the flip */
@media (prefers-reduced-motion: reduce) {
  .bk-soc-card-icon svg {
    animation: none !important;
  }
}


/* ═══════════════════════════════════════════════════════════════
   PATCH (v0.25.19) - Remove social card arrows + fix text clipping
   ───────────────────────────────────────────────────────────────
   Two Vic-flagged issues:

   (1) Remove the small arrow indicator inside each social card
       (top-right of the foot). It was eating ~30px + gap of
       horizontal width that the title/handle text needs at the
       new narrow ~200px column width.

   (2) Title text was clipping at smaller card heights. With the
       arrow gone, the text gets full width but we still need
       ellipsis-on-overflow for safety + slightly trimmer
       padding so the foot text always has room even when the
       card squishes vertically (which can happen at certain
       feed-grid heights since cards are now 1fr-distributed).
   ═══════════════════════════════════════════════════════════════ */

/* (1) Hide the arrow indicator on every social card */
.bk-soc-card-arrow {
  display: none !important;
}

/* (2) Trimmer inner padding + ellipsis on the title */
.bk-soc-card-inner {
  padding: 12px !important;       /* was 14px desktop */
}
.bk-soc-card-text {
  /* min-width:0 already set inline (line 2228), but explicit
     for safety - lets the flex child shrink instead of overflow */
  min-width: 0 !important;
  flex: 1 1 auto !important;
  width: 100% !important;
  overflow: hidden;
}
.bk-soc-card-name {
  white-space: nowrap !important;
  overflow: hidden !important;
  text-overflow: ellipsis !important;
  max-width: 100%;
}
/* .bk-soc-card-handle already has nowrap+ellipsis from prototype - no override needed */

/* On very short cards (<70px tall, can happen at small feed-grid
   heights), reduce icon size so the foot text has vertical room. */
@media (min-width: 761px) {
  .bk-soc-card {
    container-type: inline-size; /* enable container queries below */
  }
}
@container (max-height: 70px) {
  .bk-soc-card-icon {
    width: 28px !important;
    height: 28px !important;
  }
  .bk-soc-card-icon svg {
    width: 16px !important;
    height: 16px !important;
  }
  .bk-soc-card-name {
    font-size: 0.92rem !important;
  }
  .bk-soc-card-handle {
    font-size: 0.62rem !important;
  }
}


/* ═══════════════════════════════════════════════════════════════
   PATCH (v0.25.20) - Icon flip without edge-on + mobile-wide 2x2
   ───────────────────────────────────────────────────────────────
   Two Vic-flagged tweaks:

   (1) The rotateY horizontal flip from v0.25.18 passes through
       90deg where any flat SVG renders edge-on as a thin line
       (the "clipped" look Vic flagged). Cleanest fix without
       restructuring markup to add a back-face element: keep
       the rotateY motion but FADE the icon out during the
       edge-on window so the line never visibly draws.

       Keyframes: opacity 1 at 0-22% (flat), fade through 22-44%,
       hidden at 44-56% (edge-on window), fade in 56-78%, opacity 1
       at 78-100% (back to flat at 360deg). User sees "icon, dissolve,
       reappear flipped" instead of "icon, line, mirrored, line,
       icon."

   (2) Mobile-wide (580-760px) was 1x4 horizontal row from v0.25.13.
       Vic wants 2x2 like the narrow-mobile layout. Change the
       grid to 2 columns + 2 rows in that breakpoint range.
   ═══════════════════════════════════════════════════════════════ */

/* (1) Override v0.25.18 keyframes with opacity-fade during edge-on */
@keyframes vt-soc-icon-flip {
  /* Hold flat at start */
  0%, 22% {
    opacity: 1;
    transform: rotateY(0deg);
  }
  /* Fade out as icon approaches edge-on */
  44% {
    opacity: 0;
    transform: rotateY(160deg);
  }
  /* Stay invisible through edge-on phase */
  56% {
    opacity: 0;
    transform: rotateY(200deg);
  }
  /* Fade back in past edge-on */
  78%, 100% {
    opacity: 1;
    transform: rotateY(360deg);
  }
}

/* (2) Mobile-wide (580-760px): 2x2 instead of 1x4 horizontal */
@media (min-width: 580px) and (max-width: 760px) {
  .bk-soc-list {
    /* Override v0.25.13's repeat(4, 1fr) horizontal row */
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
    grid-template-rows: 1fr 1fr !important;
    grid-auto-rows: 1fr !important;
    gap: 10px !important;
  }
  .bk-soc-card {
    /* Match the narrow-mobile rules: fixed 130px height + no aspect-ratio */
    aspect-ratio: auto !important;
    width: 100% !important;
    height: 130px !important;
    min-height: 130px !important;
    max-height: 130px !important;
    flex: none !important;
  }
  /* Inner padding back to compact */
  .bk-soc-card-inner {
    padding: 14px !important;
  }
}


/* ═══════════════════════════════════════════════════════════════
   PATCH (v0.25.21) - Back-face icons so flip stays visible
   ───────────────────────────────────────────────────────────────
   v0.25.20's fade-out kept the icon away from the edge-on line
   but caused the icon to literally disappear mid-flip - Vic
   flagged that.

   Real fix: add a SECOND face on the OTHER side of the icon
   that's identical to the front. As the front rotates past 90°
   and becomes invisible (backface-visibility: hidden), the back
   face is now front-facing and visible. User sees a continuous
   icon throughout the flip - it's just that the angle of the
   icon's "card" surface is rotating.

   No PHP markup change: the back face is a CSS ::before
   pseudo-element with the matching icon as a data-URI SVG
   background-image. Four rules (one per .bk-soc-card--XX class)
   because each platform has a different icon.

   Both faces use the SAME flip keyframe and run synchronously,
   each showing only during its visible half-rotation.
   ═══════════════════════════════════════════════════════════════ */

/* Reset v0.25.20's fade-out keyframe - restore simple rotateY 0→360 */
@keyframes vt-soc-icon-flip {
  0%, 22%   { transform: rotateY(0deg);   }
  78%, 100% { transform: rotateY(360deg); }
}

/* Both faces (front SVG + ::before back face) are absolutely
   positioned in the icon box and animate together. */
.bk-soc-card-icon {
  position: relative;
  transform-style: preserve-3d;
  perspective: 600px;
}

.bk-soc-card-icon svg {
  /* Front face */
  position: relative;
  z-index: 2;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  animation: vt-soc-icon-flip 4s cubic-bezier(0.65, 0, 0.35, 1) infinite;
}

.bk-soc-card-icon::before {
  /* Back face - mirrors the front, becomes visible when front
     rotates past 90deg. */
  content: '';
  position: absolute;
  inset: 0;
  margin: auto;
  width: 20px;
  height: 20px;
  background-position: center;
  background-repeat: no-repeat;
  background-size: contain;
  transform: rotateY(180deg);   /* mirror plane offset 180deg from front */
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  animation: vt-soc-icon-flip 4s cubic-bezier(0.65, 0, 0.35, 1) infinite;
  z-index: 1;
}

/* Per-platform back-face icons as data-URI SVGs.
   Mirror the front-side <svg> markup from archive-vertwo_work.php
   bokami section, with explicit white stroke/fill since these are
   on colored card backgrounds. */

/* Instagram */
.bk-soc-card--ig .bk-soc-card-icon::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='3' width='18' height='18' rx='5'/%3E%3Ccircle cx='12' cy='12' r='4'/%3E%3Ccircle cx='17.5' cy='6.5' r='0.6' fill='%23fff' stroke='none'/%3E%3C/svg%3E");
}

/* Facebook */
.bk-soc-card--fb .bk-soc-card-icon::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z'/%3E%3C/svg%3E");
}

/* TikTok */
.bk-soc-card--tt .bk-soc-card-icon::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M16 3v3a4 4 0 0 0 4 4M10 8a5 5 0 1 0 5 5V3'/%3E%3C/svg%3E");
}

/* Globe (bokami.world) */
.bk-soc-card--web .bk-soc-card-icon::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18'/%3E%3C/svg%3E");
}

/* Container query short-card adjustment for back face too */
@container (max-height: 70px) {
  .bk-soc-card-icon::before {
    width: 16px;
    height: 16px;
  }
}

/* Reduced motion - kill the flip on both faces */
@media (prefers-reduced-motion: reduce) {
  .bk-soc-card-icon svg,
  .bk-soc-card-icon::before {
    animation: none !important;
  }
}


/* ═══════════════════════════════════════════════════════════════
   PATCH (v0.25.22) - Sync back-face cascade delays with front
   ───────────────────────────────────────────────────────────────
   v0.25.18 added cascade animation-delays per card:
     IG: 0s, FB: 0.4s, TT: 0.8s, web: 1.2s
   But those only targeted `.bk-soc-card-icon svg` (front only).
   v0.25.21's `::before` back face had NO matching delay - so the
   back face was animating without delay while the front lagged
   by the cascade amount. Result: TT (0.8s desync) and web (1.2s
   desync) showed front and back simultaneously at different
   rotation angles = the "doubled icon" Vic flagged. IG (0 delay)
   was fine; FB (0.4s) was barely noticeable.

   Fix: mirror the cascade delays on the ::before pseudo so each
   card's front + back animate in lockstep.
   ═══════════════════════════════════════════════════════════════ */

.bk-soc-card--ig  .bk-soc-card-icon::before { animation-delay: 0s;   }
.bk-soc-card--fb  .bk-soc-card-icon::before { animation-delay: 0.4s; }
.bk-soc-card--tt  .bk-soc-card-icon::before { animation-delay: 0.8s; }
.bk-soc-card--web .bk-soc-card-icon::before { animation-delay: 1.2s; }


/* ═══════════════════════════════════════════════════════════
   v0.28.1 - OTHER SERVICES PORTFOLIO (#portfolio-other)
   ───────────────────────────────────────────────────────────
   Sits between #portfolio-module (webtoon archive) and #clients.
   Light gray bg, CSS-columns masonry, rounded-tile cards (no
   book frame, no 3D tilt). Filter pills toggle via JS.

   v0.28.1: rewritten to mirror .pm-* (webtoon section) verbatim
   for consistency per Vic's feedback - same eyebrow/title/body
   sizing, same chip styling + aria-pressed active state, same
   hover-bloom recipe (minus the 3D tilt, kept webtoon-only).
   Grid sizing switched from column-count:3 to columns:200px so
   cards size auto-responsive and end up ~40% narrower than
   v0.28.0's 3-up layout.
═══════════════════════════════════════════════════════════ */

#portfolio-other{
  background:var(--off);
  padding:88px 0 96px;
  border-top:1px solid rgba(247, 49, 130,0.08);
}

.pmo-wrap{
  max-width:1200px;margin:0 auto;
  padding:0 24px;
}

/* ─── Intro: matches .pm-intro/eyebrow/title/body verbatim ─ */
.pmo-intro{max-width:680px;margin-bottom:64px}
.pmo-eyebrow{
  font-family:var(--font-body);
  font-size:0.72rem;font-weight:800;
  letter-spacing:0.18em;text-transform:uppercase;
  color:var(--eyebrow);
  margin-bottom:14px;
}
.pmo-title{
  font-family:var(--font-display);
  font-size:clamp(2.2rem,4.6vw,3.4rem);
  font-weight:900;
  letter-spacing:-0.035em;
  line-height:1.02;
  color:var(--black);
}
.pmo-title em{font-style:normal;color:var(--orange)}
.pmo-body{
  font-size:1.0625rem;
  font-weight:300;
  color:var(--mid);
  line-height:1.65;
  margin-top:18px;
  max-width:560px;
}

/* ─── Toolbar: matches .pm-toolbar/filters/chip verbatim ──
   v0.32.97 — Sticky-floating glass bar, same treatment as .pm-toolbar
   (above) and news-archive .st-filter. The "Other Services" filter
   stays at hand throughout the masonry browse. Wrap content in
   .pmo-toolbar-inner so the bar bleeds full-width while content
   stays centered at max-width. */
.pmo-toolbar{
  position:sticky;top:88px;z-index:50;
  background:rgba(245,245,247,0.72);
  backdrop-filter:saturate(180%) blur(20px);
  -webkit-backdrop-filter:saturate(180%) blur(20px);
  border-top:1px solid var(--line);
  border-bottom:1px solid var(--line);
  box-shadow:0 10px 28px -14px rgba(0,0,0,0.18);
  /* .pmo-wrap parent has padding:0 24px on all breakpoints, so the
     bar bleeds -24px. (Distinct from .pm-toolbar which lives inside
     #portfolio-module with 32px horizontal padding.) */
  margin:0 -24px 32px;
  padding:14px 24px;
}
.pmo-toolbar-inner{
  max-width:1200px;margin:0 auto;
  display:flex;align-items:center;
  gap:24px;flex-wrap:wrap;
}
.pmo-filters{
  display:flex;align-items:center;
  gap:6px;flex-wrap:wrap;flex:1;min-width:0;
}
.pmo-filter-label{
  font-size:0.68rem;font-weight:700;
  letter-spacing:0.14em;text-transform:uppercase;
  color:var(--mid);
  margin-right:4px;
  white-space:nowrap;
}
.pmo-chip{
  position:relative;
  padding:7px 16px;
  border-radius:980px;
  border:1px solid var(--line);
  background:transparent;
  font-family:var(--font-body);
  font-size:0.8125rem;
  font-weight:500;
  color:var(--black);
  cursor:pointer;
  transition:color .25s,border-color .25s,background .25s;
  white-space:nowrap;
  -webkit-tap-highlight-color:transparent;
}
.pmo-chip:hover{border-color:#cfcfd4;color:var(--orange)}
.pmo-chip:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(242,122,15,0.3)}
.pmo-chip[aria-pressed="true"]{
  background:var(--orange);
  border-color:var(--orange);
  color:#fff;
}
.pmo-chip[aria-pressed="true"]:hover{color:#fff}
.pmo-chip-count{
  display:inline-block;margin-left:6px;
  font-size:0.7rem;font-weight:600;
  color:var(--mid);
  font-variant-numeric:tabular-nums;
}
.pmo-chip[aria-pressed="true"] .pmo-chip-count{color:rgba(255,255,255,0.7)}
.pmo-chip-count:empty{display:none}

/* v0.32.98 — mobile filter dropdown for .pmo. Mirror of .pm-filter-mobile.
   Hidden by default; shown ≤1100px in the @media block below alongside
   chip-hide. The dropdown reuses .pmo-sort styling, which doesn't exist
   yet — so define it here, copying the .pm-sort pattern verbatim. */
.pmo-filter-mobile{
  display:none;
  align-items:center;
  gap:8px;
  flex-shrink:0;
}
.pmo-sort{
  appearance:none;-webkit-appearance:none;
  font-family:var(--font-body);
  font-size:0.8125rem;font-weight:500;
  color:var(--black);
  background:transparent
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6' fill='none'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%231A1A1A' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")
    no-repeat right 12px center;
  border:1px solid var(--line);
  border-radius:980px;
  padding:7px 32px 7px 14px;
  cursor:pointer;
  transition:border-color .2s;
}
.pmo-sort:hover,.pmo-sort:focus{border-color:#cfcfd4;outline:none}

/* v0.32.182 — Sort dropdown wrapper, ported verbatim from
   .pm-sort-wrap on the webtoon archive. Sits on the RIGHT of the
   toolbar inner (flex-row); the chip rail / filter dropdown on the
   left grows via flex:1 so the sort wrapper always anchors to the
   trailing edge. At ≤700px the mobile block below collapses both
   wrappers into equal-width half-row dropdowns with their
   "Filter"/"Sort" text labels hidden — same treatment as webtoon. */
.pmo-sort-wrap{
  display:flex;align-items:center;gap:8px;
  flex-shrink:0;
}
.pmo-sort-label{
  font-size:0.68rem;font-weight:700;
  letter-spacing:0.14em;text-transform:uppercase;
  color:var(--mid);
  white-space:nowrap;
}

/* ─── Result count ─────────────────────────────────────── */
.pmo-count{
  font-family:var(--font-body);font-size:0.82rem;
  color:var(--mid);margin-bottom:24px;
}
.pmo-count strong{color:var(--black);font-weight:700}

/* ─── Masonry grid via CSS columns ──────────────────────
   v0.28.1: columns:200px (was column-count:3). Browser fits as
   many ~200px columns as the viewport allows - usually 5-6 on
   1200px+ desktop, 3-4 on tablet, 2 on small tablet, 1 on
   narrow mobile. Net effect: cards are ~40% narrower than the
   v0.28.0 3-up layout, per Vic feedback. */
.pmo-grid{
  /* v0.28.13 - switched from CSS Columns to CSS Grid at ALL widths.
     CSS Columns + the v0.28.12 3D tilt (perspective + transform on
     .pmo-card) caused layout breakage in Safari desktop + tablet -
     Safari can't reliably compute column heights for items that
     establish 3D rendering contexts. Grid is also more predictable
     for filter animations and avoids the "huge gap" bug from
     unbalanced columns with odd item counts. Trade-off: lose the
     pure Pinterest column-fill masonry feel, but cards still have
     their own aspect-ratios so the row-by-row layout reads as a
     dynamic portfolio grid. */
  display:grid;
  grid-template-columns:repeat(auto-fill, minmax(220px, 1fr));
  gap:24px;
  align-items:start;
}
/* Tablet landscape - force 3 columns. */
@media(max-width:1180px){
  .pmo-grid{grid-template-columns:repeat(3, 1fr);gap:20px}
}
/* Small tablet + mobile-large - 2 columns matching webtoon @860. */
@media(max-width:860px){
  .pmo-grid{grid-template-columns:repeat(2, 1fr);gap:18px}
}
/* Narrow mobile - tighter gap. */
@media(max-width:520px){
  .pmo-grid{grid-template-columns:repeat(2, 1fr);gap:14px}
}

/* v0.32.181 — True masonry packing for the Other-Services grid.
   ───────────────────────────────────────────────────────────────
   Before: equal-row grid tracks meant the tallest card in each row
   set the row height, so a 16:9 landscape next to a 3:4 portrait
   left a big empty band under the landscape. Vic flagged this once
   he started uploading Gundam key visuals (portrait) next to older
   brand reels (landscape) — the gap was unmistakable.

   Now: a fine 4px row track + per-card `grid-row-end: span N`
   computed in JS from each card's rendered height. Cards consume
   exactly the rows they need, so portraits and landscapes pack
   tightly underneath each other regardless of orientation.

   `.is-masonry` is added by works-archive.js once initial spans
   are computed, so the layout swaps in one frame rather than
   streaming as half-formed slivers. JS-off / pre-paint fallback:
   stays on the original equal-row grid (graceful degradation, ugly
   but never broken). The row-gap from the base rules survives —
   JS reads getComputedStyle().rowGap and folds it into the span
   math so the visible gutters remain 24px / 20px / 18px / 14px
   across the breakpoints above. */
.pmo-grid.is-masonry{
  grid-auto-rows:4px;
  align-items:start;
}
.pmo-grid.is-masonry .pmo-card{
  /* JS sets `--row-span` per card. 60 ≈ ~240px (a typical portrait
     card height) is the safety fallback in case JS hasn't written
     a value yet — prevents a brief 4px-sliver flash on first paint
     between class-add and the first recompute pass. */
  grid-row-end:span var(--row-span, 60);
}

/* ─── Cards ─────────────────────────────────────────────
   No 3D tilt (Vic-locked: skip the webtoon book-cover tilt).
   On hover: translateY lift + colored bloom shadow that mirrors
   the .pm-card:hover .pm-cover stack (line ~1069 of this file).
   Per-card --accent-rgb still drives the colored bloom; PHP
   passes it inline from the cover dominant-RGB sampler. */
.pmo-card{
  display:block;
  /* v0.28.13 - margin and break-inside dropped (were CSS Columns
     artifacts). Grid gap handles spacing now; break-inside is a
     no-op outside CSS Columns. */
  --accent-rgb:242,122,15;
  /* v0.28.12 - 3D tilt mechanism ported from .pm-card (webtoon
     archive). @property --tilt-x/y/--lift-z declared at top of
     this file already, so we just consume them here. .pmo-card
     hosts the perspective (3D context); .pmo-card-link is the
     "book" element that rotates. Hover changes --lift-z (book
     lifts towards the viewer). JS sets --tilt-x/y on mousemove
     to follow the cursor. All three vars are @property-typed so
     they tween smoothly between frames - the transition on
     .pmo-card below ties them together. Vic feedback: "or can
     you just apply the 3D tilt from webtoon covers to other
     services covers" - this is exactly that, with the rounded-
     tile card design kept intact (no book-frame spine/page-block
     borrowed from the webtoon version, just the tilt motion). */
  perspective:1000px;
  perspective-origin:50% 40%;
  --lift-z:0px;
  transition:
    --tilt-x .3s cubic-bezier(.22,1,.36,1),
    --tilt-y .3s cubic-bezier(.22,1,.36,1),
    --lift-z .35s cubic-bezier(.22,1,.36,1);
}
@media (hover:hover) and (pointer:fine){
  .pmo-card:hover{ --lift-z:24px; }
}
.pmo-card.is-hidden{display:none}

.pmo-card-link{
  display:block;
  background:var(--white);
  border-radius:14px;
  overflow:hidden;
  text-decoration:none;color:inherit;
  box-shadow:
    0 4px 10px -2px rgba(0,0,0,0.10),
    0 14px 28px -10px rgba(0,0,0,0.12);
  /* v0.28.12 - the "book" rotates here. Same expression as
     .pm-book in the webtoon archive. transform-style is intentionally
     2D (not preserve-3d): all child layers (image, veil, body) stack
     as a flat surface and rotate together. */
  transform:
    rotateY(var(--tilt-y))
    rotateX(var(--tilt-x))
    translateZ(var(--lift-z));
  will-change:transform;
  transition:box-shadow .45s cubic-bezier(.22,1,.36,1);
}
@media (hover:hover) and (pointer:fine){
  .pmo-card-link:hover{
    /* Hover bloom only changes box-shadow now; the LIFT comes
       from --lift-z on .pmo-card (animated smoothly). No more
       translateY on hover - the lift IS via translateZ which
       reads as forward-towards-viewer rather than up-along-page. */
    box-shadow:
      0 10px 24px -8px rgba(0,0,0,0.18),
      0 26px 56px -12px rgba(0,0,0,0.18),
      0 32px 64px -10px rgba(var(--accent-rgb,242,122,15),0.42);
  }
}

/* ─── Media area: image at natural aspect ratio ──────── */
/* v0.28.17 - gloss-tilt sheen as a real .pmo-card-gloss element
   (was ::after pseudo in v0.28.16). The pseudo wasn't repainting
   when --tilt-x/y changed - some browsers optimize pseudo-element
   gradients aggressively and miss the calc() recomputation. A real
   DOM element with the same gradient gets repainted reliably.
   Mirrors how .pm-gloss-tilt is a real <div> in the prototype.

   z-index:1 puts the sheen above the cover image (img is at default
   z:0) but below the service pill (z:3 since v0.28.16). The veil
   (z:0) renders below the sheen too. mix-blend-mode:overlay both
   brightens light areas of the cover AND darkens dark ones for
   proper contrast. */
.pmo-card-gloss{
  position:absolute;inset:0;
  background:linear-gradient(
    calc(115deg + var(--tilt-y) * 3 - var(--tilt-x) * 1.2),
    rgba(255,255,255,0)    0%,
    rgba(255,255,255,0)    28%,
    rgba(255,255,255,0.13) 40%,
    rgba(255,255,255,0.55) 50%,
    rgba(255,255,255,0.13) 60%,
    rgba(255,255,255,0)    72%,
    rgba(255,255,255,0)    100%
  );
  mix-blend-mode:overlay;
  pointer-events:none;
  z-index:1;
}

.pmo-card-media{
  position:relative;
  width:100%;
  aspect-ratio:var(--card-aspect, 4/5);
  background:linear-gradient(135deg,#e5e5ea,#d0d0d6);
  overflow:hidden;
}
.pmo-card-img{
  position:absolute;inset:0;
  width:100%;height:100%;
  object-fit:cover;
  display:block;
  transition:transform .7s cubic-bezier(.22,1,.36,1);
}
/* v0.28.10 - removed scale on hover. Safari was painting the image
   at a 0-sized intermediate frame during the transform interpolation,
   causing the image to "disappear" briefly on hover (Vic feedback).
   The card lift + bloom shadow already communicate hover state; the
   image zoom was a bonus that wasn't worth the Safari trade-off. */
/* (.pmo-card-link:hover .pmo-card-img scale removed) */

.pmo-card-veil{
  position:absolute;inset:auto 0 0 0;height:50%;
  pointer-events:none;
  background:linear-gradient(180deg,
    rgba(0,0,0,0) 0%,
    rgba(0,0,0,0.35) 100%);
  opacity:0;
  transition:opacity .35s ease;
}
.pmo-card-link:hover .pmo-card-veil{opacity:1}

.pmo-card-service-pill{
  /* v0.28.16: z bumped 2 -> 3 so the gloss sheen at z:1 doesn't
     mix-blend across the orange pill background.
     v0.32.36 — moved top-left → top-right per Vic so it matches the
     webtoon .pm-client-pill's same-version move. */
  position:absolute;top:10px;right:10px;z-index:3;
  display:inline-block;
  padding:4px 9px;border-radius:980px;
  /* v0.28.13 - orange pill (was rgba(0,0,0,0.55) black). Vic
     feedback: in-card type chip should be brand orange. Kept the
     backdrop-blur so the pill still reads cleanly over light
     artwork; bumped opacity to 0.92 so the orange is vibrant but
     not jarring. */
  background:rgba(242,122,15,0.92);color:#fff;
  font-family:var(--font-title);font-weight:700;font-size:0.6rem;
  letter-spacing:0.12em;text-transform:uppercase;
  backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);
}

/* ─── Body: title + meta ─────────────────────────────── */
.pmo-card-body{
  padding:12px 14px 14px;
}
.pmo-card-title{
  font-family:var(--font-title);font-weight:700;
  font-size:0.86rem;letter-spacing:-0.012em;line-height:1.25;
  color:var(--black);
  margin:0 0 5px;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
  overflow:hidden;
}
/* v0.32.37 — tagline line under the title on other-services cards.
   Sits between .pmo-card-title and .pmo-card-meta. Two-line clamp
   keeps card heights consistent across long taglines.
   v0.32.38 — font-size 0.74 → 0.78 per Vic ("a bit bigger, but smaller
   than title"). .pmo-card-title is 0.86rem; tagline at 0.78rem sits
   ~91% of title size — readable bump without crowding the hierarchy
   (the .pm-card variant goes to 0.82rem because its title is 1.0625rem
   so it has more headroom). */
.pmo-card-tagline{
  font-family:var(--font-body);
  font-size:0.78rem;
  font-style:italic;
  color:var(--mid);
  margin:0 0 6px;
  line-height:1.35;
  display:-webkit-box;
  -webkit-line-clamp:2;
  -webkit-box-orient:vertical;
  overflow:hidden;
}
.pmo-card-meta{
  font-family:var(--font-body);font-size:0.72rem;
  color:var(--mid);
  margin:0;
}
.pmo-card-dot{margin:0 6px;opacity:0.6}

/* ─── Empty state ─────────────────────────────────────── */
.pmo-empty{
  text-align:center;
  padding:48px 24px;
  background:var(--white);
  border:1px dashed var(--hairline);
  border-radius:14px;
}
.pmo-empty-title{
  font-family:var(--font-title);font-weight:700;
  font-size:1rem;color:var(--black);
  margin-bottom:6px;
}
.pmo-empty-body{
  font-family:var(--font-body);font-size:0.88rem;
  color:var(--mid);
}

/* ─── Responsive intro ─────────────────────────────────── */
/* v0.28.12 - dropped overflow:hidden (was v0.28.10's clip workaround
   for shadow leak). With the new 3D tilt + tighter hover bloom on
   .pmo-card, the shadow stays close enough to the card that it no
   longer bleeds past section padding. overflow:hidden was also
   making the section calculate height oddly in CSS Columns mode
   (Vic's "huge gap" on desktop), so removing it is a 2-for-1 win. */

@media(max-width:860px){
  #portfolio-other{padding:64px 0 56px}
  .pmo-intro{margin-bottom:40px}
  .pmo-card-link:hover{
    /* tighter bloom on smaller viewports so the shadow doesn't
       fan out 50+px into the section padding */
    box-shadow:
      0 8px 18px -6px rgba(0,0,0,0.14),
      0 14px 36px -10px rgba(var(--accent-rgb,242,122,15),0.40);
  }
}

/* v0.32.99 — chip → dropdown swap at ≤700px (was ≤1100 in v0.32.98).
   Same rationale as .pm-toolbar: iPad portrait (768+) keeps desktop
   chips; only phones swap to the compact dropdown.
   v0.32.182 — also collapse .pmo-sort-wrap to a half-row dropdown
   next to .pmo-filter-mobile and hide both "Filter"/"Sort" labels —
   verbatim port of the .pm-toolbar mobile pattern. The two dropdowns
   share the row equally via flex:1; min-width:140px stops them from
   shrinking past readable on the narrowest phones. */
@media(max-width:700px){
  .pmo-filters{display:none}
  .pmo-filter-mobile{display:flex; flex:1; min-width:140px}
  .pmo-filter-mobile .pmo-sort-label{display:none}
  .pmo-sort-wrap{flex:1; min-width:140px}
  .pmo-sort-wrap .pmo-sort-label{display:none}
  .pmo-filter-mobile .pmo-sort,
  .pmo-sort-wrap .pmo-sort{width:100%}
  .pmo-toolbar-inner{flex-wrap:nowrap; gap:8px}
}

@media(max-width:600px){
  #portfolio-other{padding:52px 0 40px}
  .pmo-intro{margin-bottom:36px}
  /* v0.32.97 — keep the sticky bleed at -24px (parent .pmo-wrap padding
     doesn't change at this breakpoint), just tighten padding+gap. */
  .pmo-toolbar{padding:10px 24px; margin:0 -24px 24px}
  .pmo-toolbar-inner{gap:10px}
  .pmo-chip{padding:6px 14px;font-size:0.76rem}
  .pmo-card-body{padding:10px 12px 12px}
}

/* ═══════════════════════════════════════════════════════════════
   v0.32.12 — BOKAMI LIGHTBOX-ONLY CSS (post-revert)
   ───────────────────────────────────────────────────────────────
   v0.32.11 ported the full nd-gallery (gallery viewport + thumb strip
   + pills + lightbox) into this file. Vic flagged the gallery viewport
   was "super huge" on /works/ (16:9 full-width vs the ~480px narrow
   column nd-gallery was designed for on news-single). v0.32.12 reverts
   the markup back to the v0.32.10 compact .bk-gal horizontal strip,
   keeping ONLY the lightbox feature — click a slide thumbnail to open
   it full-screen via the .pd-lightbox below.

   Therefore the v0.32.11 .nd-gallery / .nd-gv / .nd-gtrack / .nd-gslide
   / .nd-thumbs rules have been trimmed (their markup is gone, the rules
   would never apply). What remains is the .pd-lightbox + .nd-lb-* family
   used by both the markup at the end of #bokami section + the slim
   lightbox-attach IIFE in MODULE 5 of works-archive.js. When updating
   nd-lightbox in news-single.css, mirror changes here too. */

.pd-lightbox{
  position:fixed;inset:0;
  background:rgba(10,10,12,0.94);
  backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
  display:flex;align-items:center;justify-content:center;
  z-index:1000;opacity:0;pointer-events:none;
  transition:opacity 260ms var(--ease);
}

.pd-lightbox.is-open{opacity:1;pointer-events:auto}

.pd-lightbox:not(.is-open),.pd-lightbox:not(.is-open) *{pointer-events:none!important}

.pd-lb-viewport{position:relative;width:92vw;height:84vh;overflow:hidden}

.pd-lb-track{
  position:absolute;top:0;left:0;height:100%;display:flex;
  will-change:transform;
  /* Match the main gallery + About Team rail's slow-in/slow-out feel.
     .55s ease was too snappy — Vic asked for the smoother About-rail timing. */
  transition:transform .95s var(--ease-io);
}

.pd-lb-slide{
  /* v0.32.213 — REVERTED the v0.32.212 `100%` width: .pd-lb-track is
     position:absolute with no explicit width, so it shrink-to-fits its
     content; a flex child with basis 100% then resolves against that auto
     width → slides collapsed and images rendered black. Back to the absolute
     92vw unit (matches the viewport). The "peek" is handled below by making
     the viewport full-bleed on mobile + a hard overflow clip, so neighbours
     can't show through. */
  flex:0 0 92vw;width:92vw;height:84vh;
  display:flex;align-items:center;justify-content:center;
}

.pd-lb-image{
  display:block;max-width:92vw;max-height:84vh;width:auto;height:auto;
  object-fit:contain;user-select:none;-webkit-user-drag:none;cursor:pointer;
  border-radius:8px;box-shadow:0 30px 80px rgba(0,0,0,0.5);
}
/* v0.32.213 — on mobile, make the lightbox viewport + slide full 100vw so
   there's no side margin for the next slide to peek into; the image itself
   still caps a touch inside the edge for breathing room.
   v0.32.220 — extended 700 → 1024 so iPad PORTRAIT also gets the full-bleed.
   v0.32.222 — 1024 → 1100. Vic's device computed a 1032px viewport (slide was
   949.44px = 92vw), JUST over the 1024 cap, so it missed. The peek also had a
   JS rounding-drift cause (offsetWidth rounds 949.44→949, drifting ~0.44px
   per slide across the 3×N track) — now fixed by measuring slide width with
   getBoundingClientRect().width instead. */
@media (max-width: 1100px){
  .pd-lb-viewport{width:100vw}
  .pd-lb-slide{flex:0 0 100vw;width:100vw}
  .pd-lb-image{max-width:94vw}
}

/* v0.32.27 — lightbox video player. Same fit envelope as .pd-lb-image
   (max 92vw × 84vh, object-fit:contain so any aspect ratio centers
   nicely with letterbox). Browser controls visible. */
.pd-lb-video{
  display:block;max-width:92vw;max-height:84vh;width:auto;height:auto;
  background:#000;
  object-fit:contain;
  border-radius:8px;box-shadow:0 30px 80px rgba(0,0,0,0.5);
}

.pd-lb-block{
  width:min(680px,80vw);aspect-ratio:4/5;
  border-radius:12px;display:flex;align-items:center;justify-content:center;
  font-family:var(--font-title);font-weight:900;font-size:6rem;
  color:rgba(255,255,255,0.10);letter-spacing:-0.04em;
  box-shadow:0 30px 80px rgba(0,0,0,0.5);
  position:relative;overflow:hidden;
}

.pd-lb-close,.pd-lb-nav{
  position:absolute;width:48px;height:48px;border-radius:50%;
  background:transparent;border:1px solid rgba(255,255,255,0.24);color:#fff;
  display:inline-flex;align-items:center;justify-content:center;
  cursor:pointer;z-index:2;
  transition:background .3s ease,border-color .3s ease,transform .3s ease,box-shadow .35s ease;
}

.pd-lb-close{top:24px;right:24px}

.pd-lb-close svg{width:20px;height:20px}

.pd-lb-close:hover{
  background:var(--orange);border-color:var(--orange);
  transform:rotate(90deg);
  box-shadow:0 10px 30px -6px rgba(242,122,15,0.6);
}

/* v0.32.14 — reverted v0.32.13's 64px / 16px-radius / backdrop-blur tweak per
   Vic ask to copy verbatim from news-details. Values now exactly match
   news-single.css `.pd-lb-nav` (52x52, 14px radius, no backdrop fill).
   When updating the lightbox arrow style in news-single.css, mirror here. */
.pd-lb-nav{top:50%;width:52px;height:52px;border-radius:14px;transform:translateY(-50%)}

.pd-lb-nav svg{width:22px;height:22px}

.pd-lb-prev{left:24px}

.pd-lb-next{right:24px}

.pd-lb-nav:hover{
  background:var(--orange);border-color:var(--orange);
  box-shadow:0 12px 32px -6px rgba(242,122,15,0.6);
}

.pd-lb-prev:hover{transform:translateY(-50%) translateX(-3px)}

.pd-lb-next:hover{transform:translateY(-50%) translateX(3px)}

.pd-lb-footer{
  position:absolute;left:50%;bottom:24px;transform:translateX(-50%);
  display:flex;align-items:center;gap:16px;z-index:2;
}

.pd-lb-pills{display:flex;gap:6px}

.pd-lb-pill{
  width:28px;height:4px;background:rgba(255,255,255,0.22);
  border-radius:2px;cursor:pointer;border:none;padding:0;
  transition:background .3s ease,width .3s ease;
}

.pd-lb-pill.is-active{background:var(--orange);width:40px}

.pd-lb-pill:hover{background:rgba(242,122,15,0.75)}

.pd-lb-counter{
  color:rgba(255,255,255,0.7);font-size:12px;font-weight:600;letter-spacing:0.06em;
  font-variant-numeric:tabular-nums;
}

/* ═══════════════════════════════════════════════════════════════
   v0.32.203 — Testimonial tweaks (Vic).
   1. Brand word "vertwo" in quotes renders orange. The reveal
      animation (works-archive.js splitOne) tags matching .qword
      spans with .qword--accent.
   2. The already-selected marquee button is non-interactive, so
      re-clicking the active testimonial does nothing.
   ═══════════════════════════════════════════════════════════════ */
.vt-feature__quote .qword--accent{ color:var(--orange); }
.vt-marquee__btn.is-active{ pointer-events:none; cursor:default; }
