/* ═══════════════════════════════════════════════════════════════
   Vertwo Premium — Hero (Step 3a)
   ───────────────────────────────────────────────────────────────
   PORTED VERBATIM from prototype (01-homepage.html lines 605-928,
   1864-1955, 2071-2076). Do not refactor without checking the
   prototype first. Includes Lenis required CSS at the bottom.
   ═══════════════════════════════════════════════════════════════ */


/* ─── Lenis required CSS (inlined per official docs) ─── */
html.lenis,html.lenis body{height:auto}
.lenis.lenis-smooth{scroll-behavior:auto !important}
.lenis.lenis-smooth [data-lenis-prevent]{overscroll-behavior:contain}
.lenis.lenis-stopped{overflow:hidden}
.lenis.lenis-smooth iframe{pointer-events:none}


/* ═══════════════════════════════════════
   HERO — split layout with iPhone
═══════════════════════════════════════ */
#hero{
  position:relative;
  /* v0.3.3 (Vic feedback): back to svh from dvh. dvh sounded clever in
     theory (hero adapts as iOS toolbar shows/hides) but in practice the
     hero — and therefore the absolutely-positioned video bg — visibly
     grew/shrunk on every scroll, making the artwork "jump and enlarge"
     when scrolling out of the hero on iPad Safari. svh = small viewport
     height, fixed at the smallest possible viewport (toolbar visible),
     so the hero is stable across all scroll positions.

     v0.11.0 — locked to FIXED height (height:100svh) instead of
     min-height so all slides were identical-height. Goal: kill the
     "frosted panel jumpy" feel from slides growing/shrinking with
     copy length.

     v0.13.0 — REVERTED to min-height. The fixed-height lock caused
     overflow bugs on short viewports (laptop ~790px tall) — the
     title would slide above the viewport behind the floating nav.
     Three rounds of v0.12.x fixes (padding shrink, font-size shrink,
     position-swap layout) tried to compensate but each introduced
     new bugs (typography size mismatches between viewports, mobile
     content overflow). Min-height is the prototype behavior; if a
     short-copy slide and a long-copy slide end up at slightly
     different heights, that's an acceptable trade for a hero that
     reliably contains its own content. The .55s slide-content
     opacity transition smooths the residual height delta well
     enough that the "jump" is barely perceptible. */
  min-height:100svh;
  overflow:hidden;background:#000;
  display:flex;align-items:center;
  flex-direction:column;
  /* v0.12.0 — `safe center` so content sticks to the top (clearing the
     floating nav) instead of overflowing symmetrically into it on short
     viewports. Falls back to plain `center` on browsers that don't parse
     the safe keyword. */
  justify-content:center;
  justify-content:safe center;
}

/* slide backgrounds */
.hero-slides{position:absolute;inset:0}
/* v0.12.0 — BG opacity transition aligned with slide-content (.55s).
   Previous 1.6s caused the prior slide's chrome lettering / character
   art to ghost under the new slide for ~1s after the text had already
   swapped in (Vic flagged on the LINE/Mr.War transition). */
.hero-slide{position:absolute;inset:0;opacity:0;transition:opacity .55s cubic-bezier(.4,0,.2,1)}
.hero-slide.active{opacity:1}
/* v0.14.10 — slide-bg geometry unified with .slide-video.
   ─────────────────────────────────────────────────────────────
   Through v0.14.9 .slide-bg was positioned `top:-15%; bottom:-15%`
   on desktop — making the BG container 130% as tall as the hero.
   The original intent (v0.5/v0.6) was parallax headroom: when scroll
   parallax shifted the BG ±15%, the cover-scaled image still filled
   the hero without exposing edges. Parallax was retired several
   versions ago (no JS in /assets/js touches slide-bg transforms),
   but the 130%-tall container stayed.

   Side-effect Vic flagged this round: image slides cover-scaled into
   a 130%-tall box, while video slides (`.slide-video-wrap` inset:0,
   `.slide-video` width/height:100%) cover-scaled into a 100%-tall
   box. When crossfading between an image slide and a video slide
   (or vice-versa) the eye saw two different portions of the source
   media at the same on-screen geometry → image appeared to "enlarge"
   and video appeared to "shrink" during the transition. The slides
   themselves weren't actually scaling; the visible crop just
   changed because the BG containers were different shapes.

   Fix: align the slide-bg container to inset:0 on desktop, matching
   the slide-video container exactly. Same crop math on both, no
   perceived size change on slide swap. The mobile media query at
   line ~683 already overrode to `top:0; bottom:0` for the iPad-
   portrait over-zoom diagnostic — it's now consistent with desktop.
   If parallax is ever revived, the JS layer should drive a transform
   on the image element rather than re-extending the container. */
.slide-bg{
  position:absolute;inset:0;
  background-size:cover;background-position:center;
  background-image:var(--bg-desktop);
  will-change:transform;
}
.sbg1{background:linear-gradient(135deg,#0d0d0d 0%,#1a0a00 40%,#2d1500 70%,#0d0d0d 100%)}
.sbg2{background-image:linear-gradient(to bottom,rgba(0,0,0,0.5) 0%,rgba(10,0,20,0.3) 50%,rgba(0,0,0,0.8) 100%),url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1400' height='900'%3E%3Cdefs%3E%3CradialGradient id='r2' cx='40%25' cy='50%25' r='60%25'%3E%3Cstop offset='0%25' stop-color='%234a0060'/%3E%3Cstop offset='100%25' stop-color='%23050010'/%3E%3C/radialGradient%3E%3C/defs%3E%3Crect width='1400' height='900' fill='url(%23r2)'/%3E%3Ccircle cx='400' cy='400' r='300' fill='rgba(245,166,35,0.06)'/%3E%3Ctext x='700' y='520' font-family='serif' font-size='260' font-weight='900' fill='rgba(255,255,255,0.025)' text-anchor='middle'%3E約束%3C/text%3E%3C/svg%3E")}
.sbg3{background-image:linear-gradient(to bottom,rgba(0,0,0,0.4) 0%,rgba(0,10,20,0.25) 50%,rgba(0,0,0,0.78) 100%),url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1400' height='900'%3E%3Cdefs%3E%3CradialGradient id='r3' cx='50%25' cy='60%25' r='60%25'%3E%3Cstop offset='0%25' stop-color='%23001a3a'/%3E%3Cstop offset='100%25' stop-color='%23000508'/%3E%3C/radialGradient%3E%3C/defs%3E%3Crect width='1400' height='900' fill='url(%23r3)'/%3E%3Ccircle cx='700' cy='300' r='350' fill='rgba(242,122,15,0.05)'/%3E%3Ccircle cx='700' cy='300' r='60' fill='rgba(255,200,50,0.12)'/%3E%3Ctext x='700' y='540' font-family='serif' font-size='240' fill='rgba(255,255,255,0.025)' text-anchor='middle'%3E世界%3C/text%3E%3C/svg%3E")}

.hero-overlay{position:absolute;inset:0;background:linear-gradient(to right,rgba(0,0,0,0.1) 0%,rgba(0,0,0,0.1) 50%,rgba(0,0,0,0.5) 100%);pointer-events:none}

/* two-column hero inner */
.hero-inner{
  position:relative;z-index:10;
  width:100%;max-width:1200px;
  margin:0 auto;padding:140px 48px 60px 96px;
  display:grid;grid-template-columns:1fr 1fr;
  align-items:center;gap:60px;
}

/* left: text */
.hero-eyebrow{
  font-size:0.72rem;font-weight:800;letter-spacing:0.2em;text-transform:uppercase;
  color:var(--eyebrow);margin-bottom:20px;
  /* v0.14.9 — reserve one line of vertical space even when the
     editor leaves eyebrow empty. Combined with the grid-stacking
     fix on .hero-left (see slide-content rule below), this is what
     keeps .hero-title's y-position identical across every slide.
     Without this, slides with an eyebrow had the title pushed
     ~32px (line + margin) below slides without one, and the swap
     read as "title jumps down when changing slides" even though
     the slide-content boxes themselves were perfectly aligned.
     1.5em chosen to match the eyebrow's natural single-line
     line-height; if eyebrows ever wrap to two lines on very narrow
     viewports, the cell auto-grows from there. */
  min-height:1.5em;
  opacity:0;animation:fadeUp .9s .4s cubic-bezier(.16,1,.3,1) forwards;
}
.hero-title{
  /* v0.14.0 — font size now driven by --title-plain-scale custom
     property (default 1, fallback). Per-slide ACF fields (Title —
     Plain size in the Hero focus page) emit a value on
     .slide-content; this calc() multiplies the base fluid clamp.
     Default behaviour with no override is identical to the
     original `clamp(2.4rem, 5vw, 4.2rem)`. */
  font-size:calc(clamp(2.4rem,5vw,4.2rem) * var(--title-plain-scale, 1));
  font-weight:900;font-family:var(--font-title);
  letter-spacing:-0.04em;line-height:1.0;
  color:#fff;margin-bottom:18px;
  opacity:0;animation:fadeUp .9s .6s cubic-bezier(.16,1,.3,1) forwards;
}
.hero-title em{font-style:normal;color:var(--orange)}
.hero-sub{
  font-size:1rem;font-weight:400;
  /* v0.8.2 (Vic feedback): bumped opacity from 0.52 → 0.85 and weight 300 → 400.
     v0.32.161 — opacity dropped entirely; default colour is now solid
     white per Vic's "make the color solid, no opacity" spec. The per-
     slide `subtitle_color` ACF picker (hero-page.php) overrides this
     with any solid hex the editor sets. */
  color:#FFFFFF;
  line-height:1.6;margin-bottom:36px;max-width:380px;
  opacity:0;animation:fadeUp .9s .75s cubic-bezier(.16,1,.3,1) forwards;
}
.hero-actions{
  display:flex;align-items:center;gap:14px;flex-wrap:wrap;
  opacity:0;animation:fadeUp .9s .9s cubic-bezier(.16,1,.3,1) forwards;
}
.btn-glass-fill{
  padding:12px 28px;border-radius:980px;font-size:0.88rem;font-weight:700;
  background:var(--orange);color:#fff;
  box-shadow:0 4px 18px rgba(242,122,15,0.32),inset 0 1px 0 rgba(255,200,100,0.4);
  border:1px solid rgba(255,200,100,0.3);
  transition:all .25s ease;
  display:inline-block;
}
.btn-glass-fill:hover{
  background:#d96d00;
  transform:translateY(-2px);
  box-shadow:
    0 4px 18px rgba(242,122,15,0.40),
    0 12px 28px rgba(242,122,15,0.50),
    inset 0 1px 0 rgba(255,200,100,0.5);
}
/* v0.14.7 — REBUILT WITHOUT BACKDROP-FILTER.
   ────────────────────────────────────────────────────────────
   The "frosted glass" approach on the secondary CTA was killed
   across multiple iterations chasing two related issues:
     - Chrome: blur barely visible across photographic BGs even
       at low alpha (v0.14.3 → v0.14.5 reduced bg from rgba 0.55
       → 0.12, never landed; v0.14.6 inverted to white tint with
       brightness lift, still didn't land)
     - Safari: visible "snap" on first paint (v0.14.4 dense-bg
       mask, v0.14.5 opacity fade with locked frosted props,
       v0.14.6 ::before opaque mask — Vic still saw it)

   v0.14.7 follows Vic's direct suggestion: drop the frosted
   glass entirely, make this a regular solid button mirroring
   the structure of .btn-glass-fill (CTA 1) but in black instead
   of orange. No backdrop-filter, no isolation, no contain, no
   GPU-cache hints, no first-paint mask. Paints exactly the same
   way at every frame from t=0 — no compositor sampling for any
   browser to choke on.

   Pattern matches .btn-glass-fill:
     - solid background (rgba(0,0,0,0.85) here vs var(--orange))
     - white text, defined white border
     - subtle outer drop shadow + inset top highlight
     - hover: brighten bg + lift translateY(-2px) + bigger shadow
   Black at 0.85 alpha (rather than 1.0) lets a sliver of BG
   bleed through so the button still feels "of the hero" rather
   than dropped on top of it — but it reads as solid at any
   angle and removes every compositor variable. */
.btn-glass-ghost{
  padding:12px 22px;border-radius:980px;font-size:0.88rem;font-weight:500;
  background:rgba(0, 0, 0, 0.85);color:#fff;
  box-shadow:
    0 4px 18px rgba(0, 0, 0, 0.32),
    inset 0 1px 0 rgba(255, 255, 255, 0.10);
  border:1px solid rgba(255, 255, 255, 0.45);
  transition:all .25s ease;
  display:inline-flex;align-items:center;gap:6px;
}
.btn-glass-ghost:hover{
  background:rgba(0, 0, 0, 1);
  border-color:rgba(255, 255, 255, 0.70);
  transform:translateY(-2px);
  box-shadow:
    0 4px 18px rgba(0, 0, 0, 0.40),
    0 12px 28px rgba(0, 0, 0, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.15);
}
.btn-glass-ghost svg{
  flex-shrink:0;
  transition:transform .3s ease;
}
.btn-glass-ghost:hover svg{transform:translateX(3px)}

/* v0.14.9 — CSS GRID STACKING.
   ──────────────────────────────────────────────────────────────
   Rationale (full history of attempts, kept for the next hand-
   off):

     v0.10.0  prototype port: inactive slides position:absolute,
              active slide position:relative — active drives
              hero-left's height in flow. Worked but each
              `.active` toggle did a layout swap.
     v0.12.0  all slides permanently position:absolute with
              explicit hero-left min-height — zero swap, but
              dropped natural height which made the typography
              clamp() the only sizing knob and caused viewport-
              size mismatches. Reverted in v0.13.0.
     v0.14.7  removed translateY from slide-content but kept the
              relative/absolute swap. Vic still saw a small
              "rectangle jumps down a bit" on slide change.
     v0.14.8  same as v0.14.7 — bug persisted. The two root
              causes are now identified:
                A) Even with both inactive (top:0, position:
                   absolute) and active (top:0, position:relative
                   inside a relative parent) anchored at the same
                   y-origin, browsers can render a 0.5–1px sub-
                   pixel offset on the active/relative element
                   vs the absolute clones. Subpixel rendering
                   differs between in-flow and out-of-flow boxes.
                B) Slide content varies — some slides have an
                   eyebrow line, others don't. The first child
                   of slide-content sits at top:0 of the slide,
                   so a slide WITHOUT eyebrow has its title
                   exactly where another slide's EYEBROW would
                   be — title visually drops by ~32px between
                   such slides.

   v0.14.9 fix has two parts that work together:

     1. Hero-left is a 1×1 CSS Grid. Every .slide-content child
        is placed at `grid-area: 1 / 1`, so all slides occupy
        the EXACT SAME cell. No position:absolute/relative
        swap, no flow/no-flow distinction. The grid cell sizes
        itself to the tallest slide automatically — hero-left's
        height naturally accommodates the longest slide without
        any min-height tuning.
     2. The eyebrow `<p>` is now ALWAYS rendered server-side
        (front-page.php), even when empty. CSS reserves a min-
        height matching one line of eyebrow text + its margin,
        so the title's y-position is identical across slides
        regardless of whether the editor filled the eyebrow
        field. See .hero-eyebrow rule above for the min-height.

   Result: pure opacity crossfade with zero geometric movement.
   Frosted panel (still rendered, now flat-tinted only) stays
   locked in place across every slide change. */
.hero-left{
  display:grid;
  grid-template-columns:minmax(0, 1fr);
  /* Single 1×1 grid cell — all .slide-content children stack
     at grid-area:1/1. Cell auto-sizes to tallest child. */
}
.slide-content{
  grid-area:1 / 1;
  /* v0.14.10 → v0.32.161 — `align-self:stretch` reverted to
     `align-self:start`. The v0.14.10 stretch enforced a uniform
     panel height across all slides (so cross-slide transitions
     had zero geometric jump), but at the cost of making short
     slides render their .slide-frosted-panel as if it were the
     length of the tallest slide. Vic's spec was "card readjusts
     its height based on the content length, aligned top" — so
     each slide's card now wraps tight to its content and sits
     at the top of the cell. There IS a small height change on
     slide swap now if content lengths differ; the opacity
     crossfade is still smooth, just no longer geometrically
     locked. */
  align-self:start;
  justify-self:stretch;
  opacity:0;
  pointer-events:none;
  transition:opacity .55s ease;
}
.slide-content.active{
  opacity:1;
  pointer-events:auto;
}

/* iPhone panel sets — stack absolutely, show one at a time */
.iphone-panels{
  position:absolute;inset:0;
  overflow-y:scroll;overflow-x:hidden;
  scrollbar-width:none;
  -webkit-overflow-scrolling:touch;
  border-radius:inherit;
  opacity:0;
  pointer-events:none;
  /* v0.12.0 — was .8s, lowered to .55s to align with slide-content.
     v0.14.1 — Phase 2 fix for Bug 1 residue: when transitioning
     between two slides that BOTH have an iPhone, the .iphone-panels
     cross-fade at .55s showed old-manga + new-manga overlapping at
     intermediate opacities for half a second — a "double-exposure"
     ghost separate from the (now-fixed) layout-collapse ghost.
     Snapping to .15s makes the panel content swap feel like a hard
     cut wrapped in a tiny fade — the eye reads it as a clean swap,
     not a crossfade. The surrounding .55s slide-content fade still
     handles the broader hero text/BG transition, so the overall
     beat feels coherent. */
  transition:opacity .15s ease;
}
.iphone-panels::-webkit-scrollbar{display:none}
.iphone-panels.active{opacity:1;pointer-events:auto}

/* right: iPhone frame */
.hero-right{
  display:flex;align-items:center;justify-content:center;
  opacity:0;animation:fadeUp .9s 1.0s cubic-bezier(.16,1,.3,1) forwards;
}

/* iPhone — minimal, slim border, no island, no buttons — LARGER */
.iphone-wrap{
  position:relative;
  filter:drop-shadow(0 40px 80px rgba(0,0,0,0.7));
  /* v0.8.3 (Vic feedback): nudge iPhone up on desktop so it sits at a more
     dynamic position relative to the BG character art. Negative margin-top
     pulls the wrap above the grid centerline without breaking the flex
     alignment on tablet/mobile (this rule is desktop-default and gets
     overridden by the responsive blocks below). */
  margin-top:-20px;
}
.iphone-frame{
  /* v0.8.3 (Vic feedback): bump desktop iPhone width 290 → 320, height
     proportionally 600 → 660. Slightly bigger feels more "real iPhone"
     and gives the panel art more breathing room. */
  width:320px;height:660px;
  border-radius:46px;
  background:linear-gradient(160deg,#2e2e2e 0%,#1c1c1c 50%,#111 100%);
  border:2.5px solid rgba(255,255,255,0.16);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.14),
    inset 0 -1px 0 rgba(0,0,0,0.6),
    0 0 0 1px rgba(0,0,0,0.6);
  position:relative;overflow:hidden;
}
.iphone-screen{
  position:absolute;
  top:10px;left:10px;right:10px;bottom:10px;
  border-radius:36px;
  background:#000;overflow:hidden;
}

/* webtoon scroll area — pure JS controlled */
.iphone-scroll{
  position:absolute;inset:0;
  overflow-y:scroll;overflow-x:hidden;
  scrollbar-width:none;
  -webkit-overflow-scrolling:touch;
  border-radius:inherit;
}
.iphone-scroll::-webkit-scrollbar{display:none}
.webtoon-panels{display:flex;flex-direction:column;width:100%}
.wt-panel{
  width:100%;flex-shrink:0;
  background-size:cover;background-position:center top;
}
/* panel mock images */
.wp1{height:280px;background:linear-gradient(160deg,#1a0820 0%,#3d1565 50%,#1a0820 100%)}
.wp2{height:230px;background:linear-gradient(160deg,#0a1a30 0%,#1a3d60 50%,#0a1a30 100%)}
.wp3{height:320px;background:linear-gradient(160deg,#1a0505 0%,#4a0f0f 50%,#1a0505 100%)}
.wp4{height:250px;background:linear-gradient(160deg,#041a06 0%,#0a3d10 50%,#041a06 100%)}
.wp5{height:300px;background:linear-gradient(160deg,#0a0a0a 0%,#1a1a08 50%,#0a0a0a 100%)}
.wp6{height:220px;background:linear-gradient(160deg,#1a0510 0%,#3d0a28 50%,#1a0510 100%)}
.wt-panel+.wt-panel{border-top:1.5px solid rgba(255,255,255,0.06)}

/* ─── v0.8.0 — ACF iPhone Showcase panel images ─────────────
   When a hero slide points to an iPhone Showcase post, panels
   are rendered as <img> tags inside .webtoon-panels-cms instead
   of the legacy CSS-mocked .wp1..wp6 divs. The images can be
   arbitrarily tall — entire webtoon episodes — so we let them
   keep their natural aspect ratio and let the iPhone scroll
   container handle the overflow naturally.

   width:100%   → image fills the iPhone screen width
   height:auto  → image preserves its uploaded aspect ratio,
                  no clipping, no forced sizing
   display:block → kills any inline-image baseline gap
   The +img border keeps the seam consistent with .wt-panel. */
.webtoon-panels-cms .wt-panel-img{
  display:block;
  width:100%;
  height:auto;
  flex-shrink:0;
  /* v0.8.2 (Vic feedback): kill ALL gaps between panels. Even with display:block
     and 0 borders, sub-pixel rendering on retina screens can introduce a 0.5px
     hairline between stacked images. margin-bottom:-1px overlaps each panel by
     1px so the seam is mathematically impossible. The image content itself
     doesn't lose any pixels because webtoons are designed to be read top-to-
     bottom with no panel separation anyway. */
  margin-bottom:-1px;
}
.webtoon-panels-cms .wt-panel-img:last-child{
  margin-bottom:0;
}
/* v0.8.2: explicitly remove the border that was inherited from .wt-panel rule.
   The CMS panel images should be PERFECTLY seamless — no border, no gap. */
.webtoon-panels-cms .wt-panel-img+.wt-panel-img{
  border-top:none;
}

/* When show_iphone is OFF for a slide, that .iphone-panels set
   gets the .iphone-hidden class server-side. We simply make sure
   it never becomes visible regardless of the .active class state.
   The other slides' iPhone panels keep working normally. */
.iphone-panels.iphone-hidden{
  visibility:hidden;
  pointer-events:none;
}

/* floating overlay scrollbar — sits on top of panels, right edge */
.iphone-scrollbar{
  position:absolute;
  right:7px;top:14px;bottom:14px;
  width:4px;
  background:rgba(255,255,255,0.07);
  border-radius:2px;
  z-index:10;
  cursor:pointer;
  user-select:none;
}
.iphone-scrollthumb{
  width:100%;
  background:rgba(255,255,255,0.38);
  border-radius:2px;
  position:absolute;top:0;left:0;
  height:28%;
  cursor:grab;
  transition:background .15s;
}
.iphone-scrollthumb:hover{background:rgba(255,255,255,0.65)}
.iphone-scrollthumb:active{cursor:grabbing;background:var(--orange)}

/* preview label */
.iphone-label{
  position:absolute;top:14px;left:0;right:0;
  z-index:5;text-align:center;pointer-events:none;
}
.iphone-label-text{
  display:inline-block;
  font-size:0.55rem;font-weight:700;letter-spacing:0.12em;text-transform:uppercase;
  color:rgba(255,255,255,0.6);
  background:rgba(0,0,0,0.45);backdrop-filter:blur(8px);
  padding:3px 10px;border-radius:980px;
  border:1px solid rgba(255,255,255,0.12);
}

/* ── hero paging bars — Raycast laser style ── */
/* v0.14.10 — paging chips pinned to absolute bottom-center of #hero.
   ────────────────────────────────────────────────────────────────────
   Through v0.14.9 .hero-pills was an in-flow flex child of #hero,
   sitting after .hero-inner in the flex column. With #hero using
   `justify-content:safe center`, the (hero-inner + hero-pills)
   group was vertically centered — meaning the chips' actual y-
   position depended on hero-inner's height, which changed slightly
   when the active slide had vs didn't have an iPhone (different
   row heights affecting hero-inner total height). Visible to the
   eye as the chips drifting up/down on slide change.

   Vic asked for the chips at a "fixed position" on the bottom
   across desktop and tablet (mobile still works via the natural
   flex flow — left intact in the @media(max-width:860px) block).
   position:absolute pulls the chips out of flex flow entirely;
   bottom:32px + left:0 + right:0 + justify-content:center pins
   them to a stable spot regardless of what hero-inner is doing. */
.hero-pills{
  position:absolute;
  bottom:32px;
  left:0;
  right:0;
  z-index:20;
  display:flex;align-items:center;gap:10px;
  justify-content:center;
  overflow:visible;
  opacity:0;animation:fadeIn 1s 1.3s forwards;
  pointer-events:none;
}
.hero-pills > *{ pointer-events:auto; }
/* v0.14.9 — prev/next arrows redesigned for visibility on busy BGs.
   ──────────────────────────────────────────────────────────────────
   Through v0.14.8 the arrows were chevron icons only (white, 50%
   opacity, no background). Vic flagged: "there's a visibility
   issue with the arrows if the background has too many colors."
   On Saretsuma slide 1 (purple-pink-orange palette) the white
   chevrons literally vanish into the gradient — no contrast.

   Why not a simple drop-shadow? A heavy white text-shadow makes
   the icon look fuzzy on high-density screens (iPhone 15 Pro,
   M-series Macs) and a heavy black drop-shadow turns the icon
   into a smudge. The cleanest fix is to give the icon its own
   contained surface with deterministic contrast against the
   icon — a circular semi-transparent dark disc, identical to
   the pattern used by Apple Photos, Apple Music, Vercel, and
   Linear for their carousel/media controls.

   Why not backdrop-filter? Same diagnostic that retired the
   frosted panel through v0.14.7 — backdrop-filter compositing
   is unreliable across Chrome/Safari first-paint and slide
   crossfade. Solid rgba bg is reliable everywhere.

   Composition:
     - 56px (desktop) / 44px (mobile) circle
     - background rgba(15, 18, 28, 0.36) — design-system near-
       black at 36% alpha. Reads as "smoked glass" without any
       blur: BG art subtly tints through, but every pixel of the
       disc is darker than the busiest BG region ever gets.
     - 1px border rgba(255, 255, 255, 0.16) — Apple-style hairline
       lift, keeps the disc readable against very dark BGs too.
     - inset 1px highlight + 4px outer shadow — shallow depth so
       the disc reads as a button rather than a flat overlay.
     - icon at ~22px (desktop) / 18px (mobile), 92% white, with
       drop-shadow 0 1px 2px black 40% — gives the chevron itself
       a contained-contrast guarantee against the disc.

   Hover: disc darkens (0.36 → 0.5 alpha), border brightens, icon
   shifts to brand orange. translateY(-50%) preserved in the
   transform so vertical centering doesn't break on hover scale.
*/
/* v0.14.10 — Bug 4: arrows now rounded-corner rectangles with
   orange-glow hover, matching the #backToTop button's hover state.
   ──────────────────────────────────────────────────────────────────
   v0.14.9 introduced the smoked-glass disc to fix the visibility
   issue Vic flagged (chevrons vanishing into busy BG art). That
   solved contrast but used border-radius:50% — a perfect circle.
   Vic asked this round for "rounded corner rectangle like the other
   arrows" + "on hover make it turns orange glow, same treatment
   like back to top button for consistency."

   Two design moves this version:

   1) Shape: border-radius:50% → 14px. Square footprint preserved
      (56×56 desktop, 44×44 mobile per the @media block) but the
      corners go from full-round to soft-rounded rectangle. Reads as
      a "control surface" rather than a "media-pill" — closer to
      how iOS and macOS render their inline buttons (e.g. Music
      mini-player skip controls).

   2) Hover: keeps the design system consistent with #backToTop.
      Background flips from smoked-glass to solid var(--orange);
      shadow stack matches the back-to-top hover layers exactly —
      0 4px 20px (orange 0.55) + 0 0 0 4px (orange 0.20 ring) +
      0 0 24px (orange 0.55) outer halo. Border lifts to a warmer
      tint so it reads as part of the orange surface rather than a
      neutral hairline. SVG color flips to white for contrast on
      the now-solid orange. Resting state still uses the smoked-
      glass surface from v0.14.9 — visibility on busy BGs is
      preserved when the user is just scanning, with the orange
      glow blooming only on intentional pointer interaction.

   transform on hover: translateY(-50%) is the vertical-centering
   transform; we layer scale(1.05) onto it (same multiplier as
   #backToTop) so the lift feels uniform across the two surfaces.
   The back-to-top button also adds translateY(-2px) for an upward
   pop, but our arrows are already centered via translateY(-50%) —
   adding another translateY would fight the centering, so we leave
   the lift as scale-only. */
.hero-arrow{
  position:absolute;
  top:50%;
  transform:translateY(-50%);
  width:56px;
  height:56px;
  padding:0;
  border-radius:14px;
  background:rgba(15, 18, 28, 0.36);
  border:1px solid rgba(255, 255, 255, 0.16);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.08),
    0 4px 12px rgba(0, 0, 0, 0.22);
  cursor:pointer;
  color:rgba(255, 255, 255, 0.92);
  display:flex;align-items:center;justify-content:center;
  transition:background .25s ease,
             border-color .25s ease,
             color .2s,
             box-shadow .25s ease,
             transform .2s;
  z-index:30;
}
.hero-arrow#heroPrev{left:28px;}
.hero-arrow#heroNext{right:28px;}
.hero-arrow:hover{
  background:var(--orange);
  border-color:rgba(255, 200, 120, 0.55);
  color:#fff;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.15),
    0 4px 20px rgba(242, 122, 15, 0.55),
    0 0 0 4px rgba(242, 122, 15, 0.20),
    0 0 24px rgba(242, 122, 15, 0.55);
  transform:translateY(-50%) scale(1.05);
}
.hero-arrow svg{
  display:block;
  width:22px;height:22px;
  /* Drop-shadow on the icon, not on the button. The smoked-glass
     surface gives the icon its base contrast; this shadow adds a
     final 1px lift so the chevron's edges read crisply against the
     translucent dark surface at rest. On hover the surface flips to
     solid orange and the shadow becomes a subtle inner-shape lift
     against the brighter fill — still helpful, just less load-bearing. */
  filter:drop-shadow(0 1px 2px rgba(0, 0, 0, 0.4));
}
.hero-arrow:hover svg{
  /* Slightly stronger drop-shadow on hover so the white chevron
     doesn't disappear against the bright orange surface — mirrors
     #backToTop's white-icon-on-orange contrast pattern. */
  filter:drop-shadow(0 1px 3px rgba(0, 0, 0, 0.55));
}
/* bars wrapper — only the bars, no arrows inside */
.hero-bars{display:flex;gap:10px;align-items:center;overflow:visible;}
/* each bar — 3px height, larger hit area via padding */
.pill{
  height:3px;
  width:56px;
  border-radius:2px;
  background:rgba(255,255,255,0.22);
  cursor:pointer;
  overflow:visible;
  position:relative;
  transition:background .3s,box-shadow .3s;
  flex-shrink:0;
}
/* larger hit area via transparent ::before pseudo — doesn't affect visual size */
.pill::before{
  content:'';
  position:absolute;
  top:-12px;bottom:-12px;
  left:0;right:0;
}
.pill-fill{
  position:absolute;
  top:0;left:0;
  width:100%;height:100%;
  background:transparent;
  border-radius:2px;
  overflow:visible;
  pointer-events:none;
  /* v0.14.12 — Bug 1: paging chip residue on iPad.
     ─────────────────────────────────────────────────────────────
     Vic flagged: "on tablet, on several click, it leaves a residue
     on the paging chips." The residue is a stuck compositor layer
     from the active-pill drop-shadow filter (four stacked drop-
     shadows up to 18px blur). When the user clicks rapidly through
     slides, the active-pill class is removed before the previous
     animation finishes, and iPad Safari occasionally leaves the
     last-rendered drop-shadow layer on the GPU as a "ghost" — a
     tiny bright dot near the laser's last sweep position.
     Two-part mitigation:
       1. CSS: `transition:filter .25s` smooths the filter from
          the four-shadow stack → none. Compositor evaluates the
          interpolation each frame, which forces a clean release
          of the shadow layer rather than an instant drop where
          Safari sometimes skips the cleanup.
       2. CSS: `transition:background .25s` fades out the laser
          gradient too, so the chip transitions smoothly back to
          its default transparent state. Without this, the
          gradient disappears in one frame, occasionally leaving
          a faint paint artifact at the laser's last position.
     The `--prog` custom property is also explicitly reset on
     non-active chips via JS (see assets/js/hero.js activatePill)
     so that even if Safari fails to clear the @property's last
     animated value, our JS authoritatively pins it to 0%. */
  transition:filter .25s ease, background .25s ease;
}
/* Active bar: orange base, no pointer events — can't click or hover while laser is running */
.pill.active-pill{
  background:rgba(242,122,15,0.40);
  cursor:default;
  pointer-events:none;
}
/* Active fill: orange sweep with white-hot yellow laser leading edge */
.pill.active-pill .pill-fill{
  background:linear-gradient(
    to right,
    rgba(242,122,15,0.85) 0%,
    rgba(255,180,40,1.0)  calc(var(--prog,0%) - 6px),
    rgba(255,240,100,1.0) calc(var(--prog,0%) - 1px),
    rgba(255,255,200,1.0) var(--prog,0%),
    rgba(255,240,100,0.8) calc(var(--prog,0%) + 1px),
    rgba(255,160,30,0.35) calc(var(--prog,0%) + 7px),
    rgba(255,120,10,0.0)  calc(var(--prog,0%) + 20px),
    transparent 100%
  );
  filter:
    drop-shadow(0 0 1px rgba(255,240,100,0.90))
    drop-shadow(0 0 4px rgba(255,200,60,0.70))
    drop-shadow(0 0 9px rgba(242,122,15,0.45))
    drop-shadow(0 0 18px rgba(242,122,15,0.22));
  animation:barProgress var(--slide-dur) linear forwards;
  animation-play-state:running;
}
@keyframes barProgress{
  0%  {--prog:0%}
  100%{--prog:100%}
}
@property --prog{syntax:'<percentage>';initial-value:0%;inherits:false}

/* inactive bars — clickable, hover = circular bloom with yellowish hot centre */
.pill.inactive{
  background:rgba(255,255,255,0.22);
}
.pill.inactive:hover{
  background:rgba(242,122,15,0.70);
  box-shadow:
    0 0 4px 2px rgba(255,200,60,0.90),
    0 0 10px 5px rgba(242,122,15,0.75),
    0 0 22px 9px rgba(242,122,15,0.50),
    0 0 42px 14px rgba(242,122,15,0.25);
}


/* ── Hero video slide ──
   v0.3.1: video at full width with natural composition (character left,
   dark right) bleeds into the iPhone area.
   v0.3.2 (Vic feedback): on portrait viewports (iPad/iPhone), default
   `object-position:center` was cropping the character off the LEFT edge
   because the video frame got scaled up to cover the tall container.
   Switched to `object-position:left center` — anchors the LEFT side of
   the source frame to the LEFT side of the viewport. Future CMS-uploaded
   media will inherit the same left-anchored behavior, so focal content
   stays visible regardless of device width. */
.slide-video-wrap{position:absolute;inset:0;overflow:hidden;z-index:3}
.slide-video{position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover;object-position:left center}
.slide-video-mask{
  position:absolute;inset:0;z-index:2;
  background:linear-gradient(
    to right,
    rgba(0,0,0,0.15) 0%,
    rgba(0,0,0,0.20) 30%,
    rgba(0,0,0,0.40) 55%,
    rgba(0,0,0,0.65) 75%,
    rgba(0,0,0,0.78) 100%
  );
}


/* ── Animations referenced above (fadeUp / fadeIn) ──
   These are commonly-used utility keyframes. The prototype defines them
   elsewhere in the file; we mirror the same values here so the hero is
   self-contained on a clean theme. */
@keyframes fadeUp{
  from{opacity:0;transform:translateY(14px)}
  to{opacity:1;transform:translateY(0)}
}
@keyframes fadeIn{
  from{opacity:0}
  to{opacity:1}
}


/* ═══════════════════════════════════════
   HERO RESPONSIVE — prototype lines 1898-1955
═══════════════════════════════════════ */
@media(max-width:860px){
  /* v0.11.0 — desktop locks #hero to 100svh, but on mobile the layout
     stacks vertically (text + iPhone in one column) and that column may
     legitimately need more than 100svh. Restore min-height behaviour
     here so content can expand without being clipped. */
  #hero{ height:auto; min-height:100svh; }
  /* v0.12.2 — Vic's bug 8: BG images looked massively over-zoomed on
     mobile portrait (only a face filled the entire viewport). Root
     cause was .slide-bg's desktop rule extending the BG container 15%
     above and below (top:-15%; bottom:-15%) for desktop parallax
     headroom, which on a tall narrow mobile viewport makes the cover-
     scaled image fill a 130%-tall box → big zoom-in. Restoring the
     BG container to the actual hero bounds on mobile keeps the image
     at a natural cover scale.
     v0.14.2 — also swap to a portrait-cropped variant when the slide
     supplies one. front-page.php emits two CSS variables on .slide-bg:
       --bg-desktop : the wide image URL (also set as background-image
                      directly so desktop renders it without needing a
                      var() lookup)
       --bg-mobile  : the portrait image URL, ONLY set when the editor
                      uploaded one. If absent, var() falls through to
                      --bg-desktop and mobile reuses the wide image
                      (backward compatible — pre-v0.14.2 slides see no
                      change in behaviour). */
  .slide-bg{
    top:0;
    bottom:0;
    background-image:var(--bg-mobile, var(--bg-desktop));
  }
  .hero-inner{
    grid-template-columns:1fr;gap:32px;
    padding:130px 24px 32px;
    text-align:center;
    align-content:center;
  }
  .hero-left{order:1}
  .hero-right{
    order:2;
    justify-content:center;
    position:relative;
    display:flex;
    align-items:center;
    gap:16px;
  }

  /* v0.14.9 — mobile hero height now CONSISTENT across all slides.
     ────────────────────────────────────────────────────────────────
     Through v0.14.8 mobile shipped a TWO-PART layout depending on
     whether the active slide had an iPhone:
       - With iPhone:  hero-left 50svh + 32px gap + iPhone (~75svh)
                       = ~125svh+ hero
       - Without iPhone: hero-right was display:none, hero collapsed
                         to ~50svh + padding (huge difference)
     Vic flagged the asymmetry — slides without iPhone felt like a
     completely different page. v0.14.9 keeps the hero-right slot
     reserved on EVERY slide so total hero height is constant. The
     iphone-wrap inside still gets `.iphone-wrap-off` (opacity:0 +
     transform:translateX(120%)) when the active slide doesn't want
     the iPhone — visually invisible, layout space preserved.
     The previous `#hero.hero-no-iphone .hero-right { display:none }`
     rule is removed; the slot stays in flex flow at all times.

     Text-area-lower rationale:
     With grid-stacking on hero-left (see desktop block), all slides
     occupy the same grid cell. The text used to sit at the top of
     hero-left (default grid alignment), which overlapped the
     character's face on slides 1 & 2. Vic asked for the title to
     sit lower so it lands on a calmer part of the BG art. We move
     all slide-content to the bottom of the hero-left cell via
     align-items:end on the grid. Combined with the explicit min-
     height of 50svh, text now sits in the lower band of the upper
     half of the viewport — clear of typical face/eye placement on
     a portrait-cropped key visual. */
  .hero-left{
    /* min-height ensures the cell is tall enough that align-items
       has meaningful space to push content into; without this the
       cell would shrink to fit content and align-items would have
       nothing to do. */
    min-height:50svh;
    align-items:end;
  }

  /* v0.14.9 — mobile arrows centered on the WHOLE hero height.
     ─────────────────────────────────────────────────────────────
     Through v0.14.8 mobile arrows sat at top:72% — chosen to
     center on the iPhone block specifically (which lives in the
     bottom half of the mobile hero). Vic's bug 4: "aligned center
     the arrows position to the whole hero height." Now that the
     hero is consistent across slides, top:50% places the arrows
     mid-hero on every slide. The padding/sizing tuning below is
     also re-balanced with the new circular background (see the
     base .hero-arrow rule for full diagnostic). */
  /* v0.14.10 — mobile arrow rule.
     ─────────────────────────────────────────────────────────────
     Inherits the rounded-rectangle shape (border-radius:14px),
     smoked-glass surface, and orange-glow hover from the desktop
     base rule — only the footprint shrinks. Through v0.14.9 a
     mobile-specific `:hover{ scale(1.08) }` lived here, which
     overrode the desktop scale(1.05) — out of sync with the new
     #backToTop-matched hover spec. Removed: mobile inherits the
     unified hover state (matters on tablets with trackpads, and
     on Android phones with a paired mouse). The other tuning
     stays — width/height 44, padding 8, smaller chevron. */
  .hero-arrow{
    position:absolute;
    top:50%;
    transform:translateY(-50%);
    width:44px;
    height:44px;
  }
  .hero-arrow#heroPrev{left:12px;}
  .hero-arrow#heroNext{right:12px;}
  .hero-arrow svg{width:18px;height:18px;}
  .hero-sub{max-width:100%;margin:0 auto 28px}
  .hero-actions{justify-content:center}
  /* v0.14.10 — desktop pins pills via position:absolute bottom:32px;
     mobile keeps the prototype's natural flex flow (Vic: "mobile
     already okay"). Reset to relative so the chips sit after hero-
     inner like before. padding-bottom retained for the bottom gap. */
  .hero-pills{
    position:relative;
    bottom:auto;
    padding-bottom:32px;
    pointer-events:auto;
  }

  /* v0.8.2 (Vic feedback): MAJOR iPhone size bump on tablet.
     Old size (240×500) felt cramped on iPad Pro — the red rectangle in Vic's
     reference screenshot showed roughly half the viewport occupied by the
     iPhone. New size (380×780) brings the device to a presence comparable to
     a real iPhone Pro Max held at arm's length, which makes the webtoon
     panels actually readable on tablet. The container uses dvh-aware sizing
     so the iPhone never exceeds the viewport on shorter tablets.
     v0.8.3: reset desktop margin-top:-20 since tablet stacks vertically. */
  .iphone-wrap{ margin-top:0; }
  /* v0.14.12 — Bug 2: aspect ratio locked.
     ─────────────────────────────────────────────────────────────
     Through v0.14.11 the tablet rule was `width: min(380px, 70vw);
     height: min(780px, 75svh)`. Width and height were independent
     bounds — fine on tall viewports, broken on shorter ones. On
     iPad in split-view, on a laptop browser with the window not
     fully tall, or on iPad with the keyboard up, the SVH cap shrunk
     height while width stayed at 380px → aspect ratio went from
     0.487 (iPhone-like) toward 0.85+ (tablet/iPad-like), which is
     what Vic flagged as "the iPhone placeholder changed shape
     again."
     Fix: aspect-ratio property pins the proportions, and width is
     computed as the SMALLER of:
       a) the static cap (380px)
       b) the viewport-width safety (70vw)
       c) the height-derived width: 75svh × (380/780) = the width
          that would result in the iPhone being exactly 75svh tall
     When viewport is short, option (c) wins → width shrinks AND
     height shrinks proportionally via aspect-ratio. iPhone shape
     preserved at every viewport. */
  .iphone-frame{
    aspect-ratio: 380 / 780;
    width: min(380px, 70vw, calc(75svh * 380 / 780));
    border-radius:48px;
    overflow:hidden;
  }
  .iphone-screen{border-radius:38px;top:10px;left:10px;right:10px;bottom:10px}

  /* v0.3.4: video sizing on tablet — see prior rationale */
  .slide-video{width:100%;height:55%}
  .slide-video-mask{
    background:linear-gradient(
      to bottom,
      rgba(0,0,0,0.10) 0%,
      rgba(0,0,0,0.25) 35%,
      rgba(0,0,0,0.65) 55%,
      rgba(0,0,0,0.92) 70%,
      #000 100%
    );
  }
}

@media(max-width:520px){
  /* v0.14.0 — mobile uses a fixed 2rem base instead of the desktop
     fluid clamp, but still respects the per-slide --title-*-scale
     variables. Default behaviour is unchanged. */
  .hero-title{font-size:calc(2rem * var(--title-plain-scale, 1));font-family:var(--font-title)}
  .hero-title-accent{font-size:calc(2rem * var(--title-accent-scale, 0.82))}
  .hero-inner{padding:120px 20px 24px}
  /* v0.8.3 (Vic feedback): mobile iPhone was stretching down too far when
     the URL bar collapsed (svh grew). New size is a fixed-aspect 280×580
     instead of viewport-dependent, so the frame stays proportional regardless
     of browser chrome state. The width cap (88vw) only kicks in on very
     narrow phones (<320px) where 280px would overflow. Vic feedback was
     "too tall makes the site hard to scroll past" — fixed sizes solve that. */
  .iphone-frame{
    width:min(280px, 88vw);
    height:580px;
    border-radius:40px;overflow:hidden;
  }
  .iphone-screen{border-radius:32px;top:9px;left:9px;right:9px;bottom:9px}
  .hero-pills{padding-bottom:24px}
}

/* v0.14.5 — iPad portrait BG fix.
   ───────────────────────────────────────────────────────────────
   The .slide-bg container is positioned top:-15%; bottom:-15% on
   desktop to give parallax some headroom — that means the BG box
   is 130% as tall as the hero. On wide landscape viewports, that
   extra 30% of vertical space is no problem because background-
   size:cover just paints a slightly larger area horizontally
   without distorting the visible content.

   On portrait viewports the math inverts: a wide image cover-
   scaled into a 130%-tall narrow box gets dramatically zoomed in,
   because cover always scales until the SHORTER axis fills (here:
   width fills, then the 130% height crops MORE of the image off
   top and bottom — the visible portion reduces to a small slice
   of the source). Vic flagged this as "BG image too big on
   portrait" on iPad.

   v0.14.5 reset the BG container to top:0/bottom:0. Vic re-flagged
   it. v0.14.6 added background-position:center 30% and locked
   hero to 100svh. Vic re-flagged it. The residual zoom isn't a
   CSS bug — it's geometric: cover-scaling a 16:9 source image into
   a 0.75-aspect viewport (1024×1366 on iPad Pro 12.9 portrait)
   shows about a 42% horizontal slice no matter what. Cover always
   crops; the only knob it doesn't have is "show me less".

   v0.14.7 stops using cover on iPad portrait. Switching to
   `background-size: 100% auto` means the image scales to the
   FULL WIDTH of the viewport with height auto — no horizontal
   crop at all. For a 1920×1080 source on a 1024-wide viewport,
   the rendered image is 1024×576: the entire image is visible at
   the top of the hero, and the remaining ~790px below letterboxes
   against the hero's #000 background. The eye reads this as
   "intentionally framed" rather than "zoomed in too far". For
   editor-uploaded portrait crops via --bg-mobile (typically
   ~750×1000 from a phone authoring), 100% auto fills the
   viewport vertically too — no letterbox, near-cover behaviour
   without the crop math fighting us.

   --bg-mobile fallback: v0.14.5 deliberately did NOT swap to the
   mobile crop variable here, on the reasoning that mobile crops
   are authored for narrow phone widths. Reversed in v0.14.7:
   ANY portrait crop the editor uploaded will look better on iPad
   portrait than a wide image at 100% auto with heavy letterbox.
   Falls through to --bg-desktop if --bg-mobile is absent
   (preserves backward compatibility for existing slides).

   bg-position:center top pins the image to the upper edge of the
   hero. Hero composition typically has its focal point in the
   upper portion (character art, key visual), so this puts the
   "interesting" part of the image where the eye naturally lands
   first. The lower portion of the hero — text/iPhone content
   centered against #000 — reads as a deliberate dark stage. */
@media (orientation: portrait) and (min-width: 861px) and (max-width: 1099px) {
  .slide-bg {
    top: 0;
    bottom: 0;
    background-image: var(--bg-mobile, var(--bg-desktop));
    background-size: 100% auto;
    background-position: center top;
  }
}



/* ═══════════════════════════════════════════════════════════════
   v0.8.2 — TEXT READABILITY TREATMENTS + UI POLISH
   ───────────────────────────────────────────────────────────────
   Per-slide overlay system. The .slide-content element gets a
   data-text-treatment attribute set server-side from ACF:
     - "none"          → default, no overlay panel, just opacity bumps
     - "frosted-card"  → blurred glass panel wraps the text content
     - "text-shadow"   → strong drop-shadow on text, no panel
   ═══════════════════════════════════════════════════════════════ */

/* ─── 1. Accent text on a new line ───────────────────────────
   The PHP template adds a <br/> before the .hero-title-accent
   <em> when title_plain doesn't already end with one. This rule
   makes the accent behave as a block so it sits cleanly below
   the plain title with consistent spacing, regardless of how
   the editor structured title_plain. */
.hero-title-accent{
  display:inline-block;
  margin-top:0.1em;
  /* v0.14.0 — accent renders smaller than plain title by default
     (0.82× of the same base clamp). Vic feedback: Japanese accent
     copy ("時を戻った私は別の人生を歩みたい") is typically longer
     than English equivalents and overflowed at the same size as
     plain. Per-slide override available via --title-accent-scale
     CSS variable (Title — Accent size dropdown in Hero focus
     page). 0.82× was tuned so a typical 12-character Japanese
     accent fits inside the same column as a 6-word English plain
     title at default settings. */
  font-size:calc(clamp(2.4rem,5vw,4.2rem) * var(--title-accent-scale, 0.82));
}

/* ─── 2. Default treatment: stronger base opacity ────────────
   Vic's Mr. War screenshot showed text that was readable on
   plain dark BG but ghosted out against busy key-visual art.
   Bumping eyebrow + title to full opacity, keeping subtle fade
   only on the subtitle (already addressed in .hero-sub above).
   These rules ONLY apply when treatment is "none" — the other
   two treatments override with their own contrast strategy. */
.slide-content[data-text-treatment="none"] .hero-eyebrow{
  /* Eyebrow stays solid orange — no opacity reduction. The
     orange is bright enough on dark BGs but invisible on bright
     BGs. We add a subtle text-shadow as defensive padding. */
  text-shadow:0 1px 4px rgba(0,0,0,0.5);
}
.slide-content[data-text-treatment="none"] .hero-title{
  /* Title stays solid white. Text-shadow adds enough silhouette
     to read on bright BG patches without changing the visual
     weight on dark patches. */
  text-shadow:0 2px 12px rgba(0,0,0,0.6), 0 1px 3px rgba(0,0,0,0.8);
}
.slide-content[data-text-treatment="none"] .hero-sub{
  /* Subtitle gets a softer shadow — the .hero-sub rule already
     bumped opacity to 0.85 globally. Together they read on any BG. */
  text-shadow:0 1px 6px rgba(0,0,0,0.7);
}

/* ─── 3. Frosted card treatment ──────────────────────────────
   PHP wraps the text content in a .slide-frosted-panel div so
   the BG art can breathe on the right side of the hero while
   text stays legible on the left.

   v0.14.7 — REBUILT WITHOUT BACKDROP-FILTER.
   ──────────────────────────────────────────────────────────────
   The "frosted glass" approach has been killed across five
   iterations and never landed:
     v0.10.0  — solid dark fill during load → frosted live tint.
                Visible density change at handoff.
     v0.14.3  — animate blur radius 0 → 28px. Visible blur growing.
     v0.14.4  — animate background colour from dense mask to live
                tint with constant blur. Visible density change.
     v0.14.5  — lock all frosted props, fade panel opacity 0→1.
                Compositor still re-samples blur every frame
                because the alpha-multiplied result depends on
                opacity. Vic still saw jumpiness.
     v0.14.6  — ::before mask covering the panel during load,
                fading 1→0 when BG ready. Vic STILL saw jumpiness.
                Diagnosis: the BG video isn't reliably composited
                at the moment .hero-loading drops on every device,
                so the backdrop-filter sample evolves visibly
                during the mask fade.

   v0.14.7 stops fighting the compositor. The panel is now a
   plain semi-transparent dark rectangle. NO backdrop-filter, no
   sampling, no GPU layer caching, no isolation, no first-paint
   mask. It paints exactly the same way at every frame from t=0
   onward, so there is literally no transition for the eye to
   read as jumpy.

   Aesthetic tradeoff: the BG visible through the panel is
   sharp instead of blurred. To compensate, the alpha is denser
   (rgba(15, 18, 28, 0.55) vs the old 0.05 base + heavy blur),
   the border is brighter to read as a defined panel edge, and
   the inset top highlight + inset bottom shadow give the panel
   a subtle "lit from above" feel. Reads as "tinted glass" rather
   than "frosted glass" — same FUNCTION (BG visible behind text,
   contrast lifted enough for legibility), different finish. */
/* v0.14.10 — base panel geometry applies to EVERY slide.
   ─────────────────────────────────────────────────────────────
   The .slide-frosted-panel wrapper is now emitted on every slide
   regardless of text_treatment (see front-page.php for the full
   rationale — short version: structural parity across slides
   eliminates the y-offset between non-frosted slides whose
   eyebrow sat at slide-content.top:0 and frosted slides whose
   eyebrow sat at panel.padding-top:32px, which read as the
   "ghost / hallucinating text box" Vic flagged in bug 3 and
   the "fix element positions" instruction in bug 2).

   This base rule is the same geometry that used to live ONLY
   inside the [data-text-treatment="frosted-card"] selector —
   width, height, padding, and box-sizing. By promoting it to
   the unscoped selector, every slide's panel occupies the
   identical box, so eyebrow / title / accent / sub / actions
   start at the same x AND y on every slide. The visible card
   styling (background, border, shadow) stays scoped to
   frosted-card only — see the next rule. Non-frosted slides
   render this wrapper as a transparent box.

   box-sizing:border-box keeps the 32×36 padding inside the
   100% dimensions so the panel never overflows the cell. */
.slide-frosted-panel{
  display:block;
  width:100%;
  /* v0.32.161 — `height: 100%` removed per Vic's "card readjust
     its height based on the content length" request. Now sizes to
     content; the surrounding .slide-content uses align-self:start
     so the card sits at the top of the hero column. */
  height:auto;
  box-sizing:border-box;
  padding:32px 36px;
}

.slide-content[data-text-treatment="frosted-card"] .slide-frosted-panel{
  /* v0.14.10 — visible card-only styling.
     ─────────────────────────────────────────────────────
     Geometry has been promoted to the unscoped rule above
     (so it applies on every slide, not just frosted-card).
     What's left here is purely visual: the background tint,
     rounded corner, hairline border, and the depth shadow /
     inset highlights that make the panel read as a discrete
     surface. None of these affect layout, so they can fade
     in/out via opacity transitions on the panel's parent
     (.slide-content) without shifting any text. */
  background:rgba(15, 18, 28, 0.55);
  border-radius:20px;
  border:1px solid rgba(255, 255, 255, 0.18);
  box-shadow:
    0 12px 48px rgba(0, 0, 0, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.18),
    inset 0 -1px 0 rgba(0, 0, 0, 0.30);
}

.slide-content:not(.active) .slide-frosted-panel{
  /* Skip: parent's opacity transition handles the crossfade.
     This rule explicitly cancels any leftover animation that would
     otherwise re-fire on .active toggle. */
  animation:none;
}
/* Inside the panel, text is fully opaque — the dense panel bg
   handles all the contrast work, no text-shadow needed. */
.slide-content[data-text-treatment="frosted-card"] .hero-eyebrow,
.slide-content[data-text-treatment="frosted-card"] .hero-title,
.slide-content[data-text-treatment="frosted-card"] .hero-sub{
  text-shadow:none;
}
.slide-content[data-text-treatment="frosted-card"] .hero-sub{
  color:rgba(255,255,255,0.92);
}

/* v0.14.7 — breadcrumb for future debuggers.
   The .hero-loading class is still set server-side on #hero
   (front-page.php) and removed by hero.js after BG paints, but
   no CSS rules currently depend on it. The infrastructure is
   left in place in case future paint-sensitive surfaces want
   to opt in. Earlier versions of this file had panel and
   button opacity-fades / masks tied to .hero-loading — see
   the v0.14.7 panel comment above for the full history. */

/* v0.14.10 — Tablet/mobile: panel padding scales down universally
   ───────────────────────────────────────────────────────────────
   Through v0.14.9 these padding overrides only applied to
   .slide-content[data-text-treatment="frosted-card"] .slide-frosted-panel
   because the panel only rendered on frosted-card slides. With v0.14.10's
   structural unification (panel renders on every slide regardless of
   treatment), the overrides need to apply to all panels — otherwise
   non-frosted slides on tablet/mobile would keep the desktop 32×36
   padding while frosted slides scale down, breaking the cross-slide
   y-alignment that the unification exists to guarantee. The selector
   is now the unscoped .slide-frosted-panel; the border-radius value
   is harmless on non-frosted (transparent) panels and identical on
   frosted ones. */
@media(max-width:860px){
  .slide-frosted-panel{
    padding:24px 26px;
    border-radius:18px;
  }
}
@media(max-width:520px){
  .slide-frosted-panel{
    padding:20px 22px;
    border-radius:16px;
  }
}

/* ─── 4. Text-shadow treatment (no panel) ────────────────────
   Most BG-respectful option: no panel, just heavy shadows. Used
   when the BG art is already mostly dark on the text side and
   the editor wants minimal visual interference. Slightly
   stronger shadows than the "none" default — the difference is
   intentional, this is the "I trust my BG" mode. */
.slide-content[data-text-treatment="text-shadow"] .hero-eyebrow{
  text-shadow:0 2px 6px rgba(0,0,0,0.85), 0 1px 2px rgba(0,0,0,1);
}
.slide-content[data-text-treatment="text-shadow"] .hero-title{
  text-shadow:0 3px 16px rgba(0,0,0,0.9), 0 1px 4px rgba(0,0,0,1);
}
.slide-content[data-text-treatment="text-shadow"] .hero-sub{
  text-shadow:0 2px 8px rgba(0,0,0,0.9), 0 1px 3px rgba(0,0,0,1);
  color:rgba(255,255,255,0.95);
}
/* v0.8.3 (Vic feedback): the secondary "ghost" CTA button was washing out
   on busy BG art because its label inherited the low-opacity .btn-glass-ghost
   default. Force higher opacity + a defensive text-shadow within text-shadow
   treatment AND frosted-card so both treatments handle the secondary CTA
   readability. The 'none' default also gets a small bump to be safe. */
.slide-content[data-text-treatment="text-shadow"] .btn-glass-ghost,
.slide-content[data-text-treatment="frosted-card"] .btn-glass-ghost,
.slide-content[data-text-treatment="none"]         .btn-glass-ghost{
  color:rgba(255,255,255,0.95);
  text-shadow:0 1px 4px rgba(0,0,0,0.7);
}
.slide-content[data-text-treatment="frosted-card"] .btn-glass-ghost{
  /* No text-shadow inside the frosted panel — the panel handles contrast */
  text-shadow:none;
}

/* ─── 5. Single-slide UI cleanup ─────────────────────────────
   When #hero has only one slide (data-slide-count="1"), the
   navigation arrows and paging pills are pointless visual noise.
   Hide them entirely. The slide-count attr is set server-side in
   front-page.php from PHP count($hero_slides). JS won't touch it
   because there's nowhere to navigate. */
#hero[data-slide-count="1"] .hero-arrow,
#hero[data-slide-count="1"] .hero-pills{
  display:none;
}


/* ─────────────────────────────────────────────────────────────
   v0.9.1 — Polish round (was v0.8.4)
   ─────────────────────────────────────────────────────────── */

/* v0.14.7 — the v0.9.1 !important override block that lived here
   through v0.14.6 has been retired. Its job was to force a
   consistent bg/border on the secondary CTA across every text-
   treatment scope (none / text-shadow / frosted-card), winning
   over the more specific `.slide-content[data-text-treatment="..."] 
   .btn-glass-ghost` rules above by way of !important. With the
   v0.14.7 button rebuild (solid black, no backdrop-filter), the
   treatment-scoped rules only set `color` and `text-shadow` —
   neither overrides bg/border, so the base rule near the top of
   this file wins on those properties without needing !important.
   Removing the block also kills a long history-comment chain
   that documented opacity tuning attempts that no longer apply.

   The opacity-lockdown that also lived in this neighbourhood
   through v0.14.4 → v0.14.5 — `#hero.hero-loading .btn-glass-ghost
   { opacity: 0 !important }` — was already removed in v0.14.6. */


/* v0.13.0 — iPhone wrap slide-off when active slide has
   show_iphone=false.
   ────────────────────────────────────────────────────────
   The .iphone-panels set is server-side hidden via .iphone-hidden,
   but the frame bezel is the .iphone-wrap parent which stays in
   the layout across slides. v0.8.x → v0.12.2 collapsed the wrap
   via max-height + opacity over .55s. That created Bug 1 (iPhone
   ghosting): during the .55s window, max-height interpolated from
   900px → 0, showing a progressively-clipped iPhone in the
   meantime. Unavoidable as long as max-height drives the
   transition — partial-clip is the literal interpolation state.

   v0.13.0 approach: transform-based slide-off. iPhone always
   renders at full size — just translates offscreen. No layout
   change, no max-height interpolation, no partial-clip window.
   Opacity fades faster (.25s) than translate (.55s) so the wrap
   visually disappears before its motion completes, hiding the
   final translateX(120%) snap.

   Trade-off vs v0.11.0 grid expansion: the previous code also
   did `#hero.hero-no-iphone .hero-inner { grid-template-columns:
   1fr }` so the text column could expand into the freed space.
   With translateX the iPhone is visually offscreen but still
   in the grid (transformed elements occupy their original layout
   space). Collapsing the grid to 1fr would now stack hero-right
   below hero-left vertically — wrong. So the grid expansion is
   dropped. Text column stays at 50% width on hero-no-iphone
   slides. If a future need arises, add a per-slide ACF "wide
   text" toggle that swaps a different layout class. */
.iphone-wrap{
  transition:transform .55s cubic-bezier(.4,0,.2,1),
             opacity .25s cubic-bezier(.4,0,.2,1);
}
.iphone-wrap.iphone-wrap-off{
  opacity:0;
  transform:translateX(120%);
  pointer-events:none;
}

/* v0.14.11 — DESKTOP wide-text-on-no-iphone REVERTED.
   ──────────────────────────────────────────────────────────────
   v0.14.9 added a desktop-only block here that, on slides where
   the active slide had no iPhone (`#hero.hero-no-iphone`),
   collapsed `.hero-inner` from `grid-template-columns:1fr 1fr`
   to `1fr`, hid `.hero-right` via `display:none`, and bumped
   `.hero-sub`'s max-width from 380→720px. Goal: let the text
   on no-iPhone slides "extend wide" across the freed space.

   It worked visually for uniform sites (every slide either has
   an iPhone or doesn't), but Vic's homepage is mixed — slide 1
   has the iPhone showcase, slide 3 doesn't. On slide change
   the hero-no-iphone class toggled, the grid collapsed/expanded
   instantly, and TWO regressions surfaced:

     Bug 1 ("slide 3 image enlarge, slide 2 video shrink a
     little") — when `.hero-right` collapses to display:none,
     the grid row's height drops from `max(hero-left, iphone-
     frame)` (the iPhone is ~75svh tall) to just hero-left's
     natural text height. `#hero` (min-height:100svh) stays at
     100svh, but the *content area* inside it shifts. With
     `.slide-bg` and `.slide-video-wrap` both inset:0 of
     `.hero-slide` (= inset:0 of #hero), changing the inner
     row math repositions the bg cover-scale anchors and the
     image/video appears to enlarge or shrink during the .55s
     opacity crossfade.

     Bug 2 + 3 (the "ghosting / text box hallucinating and
     change its size following the size of slide 3 text area")
     — when grid collapses to 1col, `.hero-left` widens from
     ~50% to ~100% of hero-inner. EVERY .slide-content child
     inside it widens at the same instant, including the slide
     that's just begun fading out. The user sees the outgoing
     slide's panel suddenly snap to the incoming slide's wider
     geometry — that's the morph Vic flagged.

   Vic asked specifically: "is this because of my request to
   extend text area to the right side ? if yes, please revert
   back to previous one." Reverting.

   Trade-off the user is consciously making: no-iPhone slides
   on desktop now keep `.hero-left` at 50% width with empty
   space on the right (where the iPhone-wrap is translateX-
   offscreen). The wide-text presentation goes away; the
   stable bg + stable panel comes back. If a future need
   for wide-text returns, the right approach is a per-slide
   ACF "wide layout" toggle on UNIFORM sites only (server-
   side check that NO slides want the iPhone), not a per-
   active-slide class swap that animates layout mid-
   transition. The hero-no-iphone class itself is left in
   place — JS still toggles it for any future hooks (e.g.
   the mobile slot-reservation rule still uses it), it
   just no longer drives any visual layout change on
   desktop. */

/* v0.14.9 — Mobile arrow lift block REMOVED.
   ────────────────────────────────────────────────────────────────
   Through v0.14.8 mobile arrows defaulted to top:72% (centered on
   the iPhone column) and lifted to 50% only when the active slide
   had no iPhone. With the v0.14.9 unification — hero is always
   ~125svh on mobile regardless of whether the iPhone is shown,
   and arrows now sit at top:50% of the whole hero at all times —
   the conditional lift is no longer needed. Arrows track a single
   anchor on every slide. The smooth `transition:top` that was
   tuned for the lift animation is also retired since `top` no
   longer changes. */


/* ═══════════════════════════════════════════════════════════════
   v0.13.0 — SHORT-VIEWPORT SHRINK BLOCK REMOVED
   ───────────────────────────────────────────────────────────────
   v0.12.0–.2 had an `@media(max-height:900px)` block here that
   shrunk hero padding and (in v0.12.0–.1) the hero-title font-
   size to compensate for the locked-height hero overflowing on
   short laptop viewports.

   v0.13.0 reverts the height lock to min-height. The hero now
   grows naturally on viewports where its content is taller than
   100svh — no overflow above the viewport, no title behind the
   floating nav, no need for a viewport-height-dependent shrink.

   Removed entirely (rather than kept as a polish pass) because
   per-viewport-height font-size or padding rules are exactly the
   pattern that produced cross-device inconsistency. Vic explicitly
   wants identical typography across laptop/PC/iPad. If a future
   short-viewport polish is needed, prefer viewport-WIDTH-based
   scaling or a single rule that applies on every viewport.
   ═══════════════════════════════════════════════════════════════ */


/* ═══════════════════════════════════════════════════════════════
   v0.8.4 — HERO TRANSITION VARIANTS
   ───────────────────────────────────────────────────────────────
   Customizer → Vertwo — Hero Behavior → Slide transition style.
   The default "fade" is what the prototype always did (opacity
   crossfade on .hero-slide). The other three are alternative
   transition styles that operate purely via CSS — JS still does
   the same .active class toggling, the visual change is just
   how the BG and content move during the transition.

   IMPORTANT: only the BG slides (.hero-slide) get the transform
   variants — the .slide-content (text) keeps its existing
   fade-up animation regardless of BG transition mode, because
   the text always benefits from the fade-up reveal.
   ═══════════════════════════════════════════════════════════════ */

/* --- slide-horizontal: BG slides shift in from the right, out to the left --- */
#hero[data-transition="slide-horizontal"] .hero-slide{
  transform:translateX(8%);
  transition:opacity 1.2s cubic-bezier(.4,0,.2,1),
             transform 1.2s cubic-bezier(.4,0,.2,1);
}
#hero[data-transition="slide-horizontal"] .hero-slide.active{
  transform:translateX(0);
}

/* --- slide-vertical: BG slides shift in from below, out to above --- */
#hero[data-transition="slide-vertical"] .hero-slide{
  transform:translateY(6%);
  transition:opacity 1.2s cubic-bezier(.4,0,.2,1),
             transform 1.2s cubic-bezier(.4,0,.2,1);
}
#hero[data-transition="slide-vertical"] .hero-slide.active{
  transform:translateY(0);
}

/* --- instant: hard cut, no animation --- */
#hero[data-transition="instant"] .hero-slide{
  transition:none;
}
#hero[data-transition="instant"] .slide-content{
  transition:none;
  /* override the parent fade so text changes instantly too */
}
