/* ==========================================================================
   LiQUiD Paradise — mobile refinements (2026-08-15, owner feedback)
   Loaded AFTER style.css. Everything here is an override; the original
   stylesheet is left untouched so the port stays easy to re-generate.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. THE BIG ONE — two-column sections never collapsed on phones.
   "Our Story" and "Every wave since 2019" set grid-template-columns in an
   INLINE style attribute, which outranks every stylesheet rule including
   media queries. Result: a ~1fr text column and a squeezed timeline on a
   390px screen. Force them to a single column, and stop the heading sticking
   (sticky makes no sense once the layout is stacked).
   -------------------------------------------------------------------------- */
@media (max-width: 900px) {
  .wrap[style*="grid-template-columns"] {
    grid-template-columns: 1fr !important;
    gap: 2.4rem !important;
  }
  .shead[style*="sticky"] {
    position: static !important;
    top: auto !important;
  }
  /* Two photos side by side above the timeline, as requested — but ONLY the
     photo grid. An earlier blanket .grid.c2 rule also pinned the VIP villa
     cards two-across on phones, squeezing Japanese text to one or two
     characters per line and clipping the prices to "¥200". Content grids
     stack, exactly as the original stylesheet intended. */
  .grid.c2:has(.photo-card) {
    grid-template-columns: 1fr 1fr !important;
    gap: .8rem !important;
  }
  .grid.c2:not(:has(.photo-card)) {
    grid-template-columns: 1fr !important;
  }
}

/* --------------------------------------------------------------------------
   8. VIP page on phones.
   -------------------------------------------------------------------------- */
@media (max-width: 760px) {
  /* The closing band sets a whole sentence at heading size. In Japanese that
     is a wall of large serif type — cramped and hard to read. */
  .cta-band h2,
  .cta-band .vip-hero-title {
    font-size: clamp(1.3rem, 5vw, 1.9rem) !important;
    line-height: 1.65;
    max-width: 22em;
    margin-inline: auto;
  }
  .vip-hero-title {
    font-size: clamp(1.6rem, 6.4vw, 2.4rem) !important;
    line-height: 1.45;
  }

  /* Long bilingual button labels overflowed their pills and were cut off at
     both edges. Let them wrap and fill the width instead. */
  .cta-band .btn,
  .vip-cta .btn {
    width: 100%;
    white-space: normal;
    text-align: center;
    line-height: 1.4;
    padding-inline: 1.2rem;
  }

  /* The VIP hero's three figures (5 villas / 10 rooms / 0 steps) are laid
     out with a 3.2rem flex gap, which on a phone pushed the third onto its
     own line with a large hole beside it. Put them in three even columns. */
  .page-hero .wrap > div[style*="display:flex"] {
    display: grid !important;
    grid-template-columns: repeat(3, 1fr);
    gap: 1rem .6rem !important;
    margin-top: 1.8rem !important;
    text-align: center;
  }
  .page-hero .wrap > div[style*="display:flex"] > div { min-width: 0; }
  .page-hero .wrap > div[style*="display:flex"] b {
    font-size: 2rem !important;
    line-height: 1.1;
  }
  .page-hero .wrap > div[style*="display:flex"] .kicker {
    font-size: .58rem;
    letter-spacing: .1em;
    line-height: 1.45;
    display: block;
    margin-top: .3rem;
    opacity: .7;
  }
  /* The hero scrim fades to 15% opacity across its middle — which on a phone
     is exactly where the heading and the paragraph sit, and on the VIP page
     that band of the photograph is bright sky. Deepen it: the picture still
     reads, the words stop competing with it. */
  .page-hero::after{
    background:linear-gradient(180deg,
      rgba(4,6,15,.62) 0%,
      rgba(4,6,15,.34) 28%,
      rgba(4,6,15,.58) 60%,
      var(--ink) 97%);
  }
  .page-hero h1,
  .vip-hero-title,
  .page-hero p.lead{
    text-shadow:0 1px 4px rgba(4,6,15,.9),0 3px 20px rgba(4,6,15,.7);
  }
  .page-hero p.lead{color:#e7e3d8}
  /* Bigger and a touch tighter, so the label reads as a label rather than a
     smudge. Not the stat captions below it — those keep their own size. */
  .page-hero > .wrap > .kicker{
    font-size:.8rem;
    letter-spacing:.3em;
  }

  /* The tickets hero used to fill the whole first screen, pushing the price
     ladder to 1.5 screens down — just past where four out of five people stop
     scrolling. Shorter here only; the desktop hero is untouched. */
  .page-hero--tight{min-height:44vh; padding-bottom:5vh}
  .page-hero--tight h1{font-size:clamp(2rem,8.5vw,2.9rem); margin:.2em 0 .15em}
  .page-hero--tight p.lead{font-size:.95rem; max-width:34ch}
  .page-hero--tight .kicker{font-size:.62rem}
  /* and the heading above the ladder gives back the rest */
  .shead{margin-bottom:1rem}
  .shead h2{font-size:1.02rem; line-height:1.55}

  /* The hero now carries the offer as well as the picture, so every block in
     it gives back a little room. Measured: the copy was 623px inside a 763px
     screen with the scroll hint 6px away — on a 667px phone that overflows.
     Nothing is removed; the spacing simply stops being generous. */
  #ch-hero .datebar{margin-top:1rem}
  /* The tagline goes on phones (owner, 2026-08-23). It is the most beautiful
     line in the hero and the only one not doing a job the price, the date and
     the buttons are not already doing better — and on a screen this small,
     what it costs is the villa line sitting near the fold. Desktop keeps it. */
  #ch-hero .copy p.serif{display:none}
  #ch-hero .cta-row{margin-top:.95rem; gap:.55rem}
  #ch-hero .cta-row .btn{padding:.82em 1.3em; font-size:.88rem}

  .hero-offer{margin-top:.8rem; padding-top:.75rem; gap:.25em .5em}
  .hero-offer .amt{font-size:1.08rem}
  .hero-offer .lbl{font-size:.6rem; letter-spacing:.14em}
  .hero-offer .left{font-size:.72rem; padding:.1em .55em}

  /* The villa line is the quietest thing in the hero: the button directly
     above already names the villas, so this is only the price and the count.
     Kept to one line — on a real phone it wrapped and left a lone 室 stranded
     underneath, which was the worst thing on the screen. */
  .hero-villa{
    font-size:.72rem; margin-top:.55rem; opacity:.85;
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  }
  .hero-villa b{font-weight:600}

  /* The gaps around the buttons were doing nothing but adding scroll. */
  #ch-hero .cta-row{margin-top:.8rem; gap:.45rem}
  #ch-hero .cta-row .btn{padding:.78em 1.2em}
  /* The datebar separator is only meaningful when date and venue share a line;
     stacked on a phone it becomes a stray dot in the middle of the hero. */
  #ch-hero .datebar span[aria-hidden]{display:none}




  /* Keep a two-line title from dropping a stray character onto line two. */
  .page-hero h1, .vip-hero-title, .cta-band h2 { text-wrap: balance; }

  /* Nothing in a card may spill past its rounded edge. Use break-word, not
     anywhere: "anywhere" permits a break at any character, which is what
     chopped words mid-way. */
  .card, .tier, .vip-card { overflow-wrap: break-word; }
  .card .btn, .tier .btn { width: 100%; white-space: normal; }
}

/* --------------------------------------------------------------------------
   9. Japanese line breaking.
   Japanese may wrap at almost any character, so in a narrow column it breaks
   words apart — ロケーション became ロケーシ / ョン, プライベートホテル became
   プライベ / ートホテ / ル. `line-break: strict` applies the proper Japanese
   rules: a line may not begin with a small kana (ゃゅょっ), a long vowel mark
   (ー) or closing punctuation. Applied site-wide on the festival pages, in
   both languages — it only affects CJK text.
   -------------------------------------------------------------------------- */
.is-liquid,
.is-liquid .compare,
.is-liquid .card,
.is-liquid .tier,
.is-liquid p,
.is-liquid h1, .is-liquid h2, .is-liquid h3,
.is-liquid li, .is-liquid td, .is-liquid .val {
  line-break: strict;
  word-break: normal;
  overflow-wrap: break-word;
}

/* --------------------------------------------------------------------------
   9b. Japanese headlines break between words, not through them.

   `line-break: strict` above only governs which characters may START a line —
   small kana, the long-vowel mark, closing punctuation. It says nothing about
   where a line may END, so Japanese still breaks between any two kanji, and
   the tickets headline あなたの楽園を選ぼう。 was splitting the compound 楽園
   down the middle: あなたの楽 / 園を選ぼう。 English never does this, which is
   why the two languages looked like different pages.

   `word-break: auto-phrase` is the fix built for exactly this — the browser
   segments the sentence into phrases and only breaks between them. Chrome and
   Safari honour it; anywhere else it is ignored and the text wraps as it does
   today, so there is nothing to fall back to.

   Headlines and leads only. Body copy in a narrow column needs every break
   opportunity it can get, and phrase breaking there would leave ragged lines.
   -------------------------------------------------------------------------- */
html[lang="ja"] .is-liquid h1,
html[lang="ja"] .is-liquid h2,
html[lang="ja"] .is-liquid h3,
html[lang="ja"] .is-liquid .lead,
html[lang="ja"] .is-liquid .kicker,
html[lang="ja"] .is-liquid .btn,
html[lang="ja"] .is-liquid .shead p {
  word-break: auto-phrase;
}

/* --------------------------------------------------------------------------
   9c. The date is one thing, in both languages.

   The kicker reads "LIQUID PARADISE 2026 — NOV 7–8", and at 390px it was
   breaking after the en dash: "NOV 7–" on one line, "8" alone on the next.
   Japanese did the same to 11月7日–8日. `text-wrap: balance` evens the two
   lines out instead of filling the first one to the edge, which keeps the
   date together at every width we support.
   -------------------------------------------------------------------------- */
.is-liquid .kicker {
  text-wrap: balance;
}

@media (max-width: 760px) {
  /*
   * The kicker's .42em letter spacing is what forces the date onto a second
   * line: "LIQUID PARADISE 2026 — NOV 7-8" needs about 490px to set at that
   * tracking, on a 390px screen. Tightening it on phones lets the line fit,
   * and the date stops being split at all. The wide tracking is the look, so
   * it is reduced rather than removed, and desktop keeps the full .42em.
   */
  .is-liquid .kicker,
  .is-liquid .page-hero .kicker,
  .is-liquid .page-hero .kicker.gold-text {
    letter-spacing: .18em;
    font-size: .7rem;
  }

  /*
   * And in Japanese, never break between kanji here. `keep-all` would be wrong
   * for a paragraph — a long Japanese sentence with no break opportunities
   * overflows — but a kicker is a handful of words with real spaces in it, and
   * this is what stops 11月7日 becoming 11月 / 7日.
   *
   * Section headings get it too, for the same reason and after the same check:
   * `auto-phrase` segments 11月 and 7日 as separate phrases, so it happily broke
   * "11月7日15:00開場" in half. Measured across all eight Japanese pages first —
   * the longest space-free run in any `.shead h2` is 14 characters (217px in a
   * 319px column), so nothing can overflow.
   *
   * Deliberately NOT applied to h3 or paragraphs: the same measurement found
   * runs there that would overflow, such as オーシャンフロントヴィラ at 274px in a
   * 246px column on the VIP page. Those keep `auto-phrase`.
   */
  html[lang="ja"] .is-liquid .kicker,
  html[lang="ja"] .is-liquid .page-hero .kicker,
  html[lang="ja"] .is-liquid .shead h2 {
    word-break: keep-all;
  }

  /*
   * A Japanese button label is wider than its English twin, and the villa CTA
   * buttons are `white-space: nowrap` inside a grid track sized for "Buy
   * ticket". チケットを購入 needs 131px and was given 100, so the last two
   * characters were sliced off — on the two villa pages, right on the buy
   * button. Pre-dates the wrapping work; found while checking it.
   */
  html[lang="ja"] .is-liquid .btn {
    /*
     * 2026-08-24. The `max-content` above was the right idea and the wrong
     * lever: it also applied to the two villa CTAs on the VIP page, and
     * ¥500,000 — ウルトラVIP オーシャンフロントヴィラ measures 433px, so the pill
     * was forced 22px past both edges of a 390px screen and its rounded ends
     * were cut off.
     *
     * `nowrap` does the same job as `max-content` did — a flex item that may
     * not wrap cannot be squeezed below its own text, so チケットを購入 still
     * gets its 131px in a track sized for 100 — while leaving `max-width`
     * free to keep the pill on the screen.
     *
     * The label span is then the one part allowed to wrap, so a long name
     * breaks into tidy lines beside the price instead of orphaning the dash.
     *
     * .22em is Latin display tracking. Japanese does not want it: it is what
     * made this label 433px in the first place, and CJK reads better tight.
     */
    max-width: calc(100vw - 3rem);
    white-space: nowrap;
    letter-spacing: .10em;
  }

  html[lang="ja"] .is-liquid .btn > span {
    white-space: normal;
    text-align: left;
  }

  /*
   * The one button that still needs its intrinsic width forced. It sits in a
   * three-up grid whose tracks are 100px, and the label needs 117 in both
   * languages — チケットを購入 and "Buy ticket" measure the same here, and
   * both lose their last characters. Not scoped to a language for that
   * reason. Scoped to `.buy` because it is the only button on any of the
   * thirteen pages that measured short — a blanket `min-width: max-content`
   * is what pushed the VIP villa pills off the screen, since min-width
   * outranks max-width.
   */
  .is-liquid .btn.buy {
    min-width: max-content;
  }

  /*
   * A safety net in both languages: no button may ever be wider than the
   * screen it is drawn on. Costs nothing when the label already fits.
   */
  .is-liquid .btn {
    max-width: calc(100vw - 3rem);
  }
}

/* --------------------------------------------------------------------------
   10. The VIP comparison table on phones.
   Three columns on a 390px screen leaves roughly 100px each — every label
   wrapped to four or five characters per line. Give the label its own
   full-width line and let the two plans sit side by side beneath it.
   -------------------------------------------------------------------------- */
@media (max-width: 760px) {
  .compare .row {
    grid-template-columns: 1fr 1fr !important;
    gap: .45rem .9rem !important;
    padding: 1rem 1rem !important;
    align-items: start;
  }
  /* label (with its icon) spans the full width, on its own line */
  .compare .row > :first-child {
    grid-column: 1 / -1;
    font-size: .9rem;
    letter-spacing: .04em;
    margin-bottom: .1rem;
  }
  .compare .val {
    font-size: .84rem;
    line-height: 1.55;
    padding: .55rem .5rem .6rem;
    border-radius: 12px;
  }
  /* the header row: price above plan name, centred under each column */
  .compare .row:first-child .head b { font-size: 1.05rem; }

  /* Each row now names its columns. Without this you scroll past the header
     and see a bare "— ✓" with no clue which villa is which. The plan names
     follow the language, because applyLang() sets <html lang>. */
  .compare .row:not(:first-child) > :nth-child(2)::before,
  .compare .row:not(:first-child) > :nth-child(3)::before {
    display: block;
    font-family: var(--font-d);
    font-size: .58rem;
    letter-spacing: .12em;
    line-height: 1.3;
    opacity: .5;
    margin-bottom: .35rem;
    white-space: nowrap;
  }
  html[lang="ja"] .compare .row:not(:first-child) > :nth-child(2)::before { content: "オーシャンフロント"; }
  html[lang="ja"] .compare .row:not(:first-child) > :nth-child(3)::before { content: "ウルトラVIP"; }
  html:not([lang="ja"]) .compare .row:not(:first-child) > :nth-child(2)::before { content: "OCEAN-FRONT"; }
  html:not([lang="ja"]) .compare .row:not(:first-child) > :nth-child(3)::before { content: "ULTRA VIP"; }

  /* Tint the premium column so the eye can track it down the page, and give
     the two columns a hairline between them. */
  .compare .row:not(:first-child) > :nth-child(3) {
    background: rgba(217, 179, 106, .07);
    box-shadow: inset 1px 0 0 rgba(217, 179, 106, .18);
  }
  .compare .row:not(:first-child) > :nth-child(3)::before { opacity: .7; color: var(--gold); }

  /* A "not included" dash reads as a mistake; make it explicitly absent. */
  .compare .val.no { opacity: .3; }
}

/* --------------------------------------------------------------------------
   2. Timeline — now full width, so let it breathe instead of wrapping to
   three words per line.
   -------------------------------------------------------------------------- */
@media (max-width: 900px) {
  .timeline { margin-top: 2.8rem; }
  .tl-item { padding: 0 0 2.2rem 2.6rem; }
  .tl-item h3 { font-size: 1.15rem; line-height: 1.25; text-wrap: balance; }
  .tl-item p  { font-size: .92rem; line-height: 1.6; max-width: 34em; }
  .tl-item .tl-year { font-size: .72rem; letter-spacing: .26em; }
}

/* --------------------------------------------------------------------------
   3. Hero — the SCROLL hint was printed straight through the
   "OCEAN-FRONT VIP VILLAS" button: the hint is pinned to the bottom of the
   screen while the stacked buttons grew down into it.
   Keep the hint (it is the scroll affordance) but RESERVE its space at the
   foot of the hero, so the two can never occupy the same pixels. Sits above
   the iPhone home indicator via the safe-area inset.
   -------------------------------------------------------------------------- */
@media (max-width: 760px) {
  /* The hero gets its OWN safe zone rather than padding the copy: clear of
     the fixed nav at the top, clear of the scroll hint at the foot. Because
     .chapter is a centred flex box and box-sizing is border-box globally,
     this padding shrinks the area the copy centres inside — so the kicker
     can never slide up under the language switcher, and the buttons can
     never reach down into the scroll hint. */
  #ch-hero {
    padding-top: calc(var(--nav-h) + 14px);
    padding-bottom: 86px;
  }
  #ch-hero .copy { padding-bottom: 0; }

  /* Tighten the vertical rhythm so all of it still lands on one screen. */
  #ch-hero .copy .kicker {
    margin-bottom: 1rem;
    letter-spacing: .22em;
    font-size: .62rem;
  }
  #ch-hero .hero-title { margin: .2em 0 .25em; }
  #ch-hero .genre-line { margin-top: .7rem; }
  #ch-hero .cta-row { margin-top: 1.7rem; gap: .8rem; }
  #ch-hero .cta-row .btn { width: 100%; justify-content: center; }

  .scroll-hint {
    display: flex !important;
    bottom: calc(1.4vh + env(safe-area-inset-bottom, 0px));
    font-size: .58rem;
    letter-spacing: .34em;
    gap: .5rem;
    opacity: .5;
  }
  .scroll-hint::after { height: 30px; }
}

/* Short screens (iPhone SE and similar): trim both ends so nothing clips. */
@media (max-width: 760px) and (max-height: 740px) {
  #ch-hero { padding-top: calc(var(--nav-h) + 8px); padding-bottom: 66px; }
  #ch-hero .copy .kicker { margin-bottom: .7rem; }
  #ch-hero .cta-row { margin-top: 1.2rem; }
  .scroll-hint::after { height: 20px; }

  /* The hero now carries the price and the villa line too. On a 667px screen
     that is about 35px more than fits, so two things give way: the scroll hint
     (the page plainly continues without being told) and the genre line, the
     least load-bearing element in the block. The !important is not decoration
     — the base rule above sets display:flex the same way. */
  .scroll-hint { display: none !important; }
  #ch-hero .genre-line { display: none; }
  #ch-hero .datebar { margin-top: .7rem; }
  .hero-offer { margin-top: .65rem; padding-top: .6rem; }
  .hero-villa { margin-top: .55rem; }
}

/* --------------------------------------------------------------------------
   4. Readability — light type over bright photography ("Leave Tokyo behind.
   Enter paradise." sat on a sunlit concrete/sea shot). A soft dark halo
   keeps the type legible without looking like a hard drop shadow.
   -------------------------------------------------------------------------- */
.chapter .copy h1,
.chapter .copy h2,
.chapter .copy .serif,
.chapter .copy .kicker,
.page-hero h1,
.page-hero h2,
.page-hero .serif,
.page-hero .kicker,
.cta-band h2,
.cta-band .serif {
  text-shadow:
    0 1px 2px rgba(0, 0, 0, .55),
    0 3px 14px rgba(0, 0, 0, .5),
    0 0 40px rgba(0, 0, 0, .35);
}
/* The kicker sits on the brightest part of the sky photo and is set small,
   light and widely letter-spaced — the Japanese version
   ("ラグジュアリーリゾートで開催されるプレミアムビーチフェス") was especially
   hard to read. Give it a stronger halo than the headings get. */
.chapter .copy .kicker,
.page-hero .kicker,
#ch-hero .copy .kicker {
  text-shadow:
    0 1px 2px rgba(0, 0, 0, .8),
    0 2px 8px rgba(0, 0, 0, .7),
    0 0 22px rgba(0, 0, 0, .6);
}

/* EXCEPTION — gradient-clipped text must never take a text-shadow.
   .chrome-text ("Choose your paradise.") and friends paint a moving chrome
   gradient through transparent glyphs (-webkit-text-fill-color:transparent
   + background-clip:text). A text-shadow on those renders as a solid dark
   silhouette BEHIND the letters, showing through the gradient and muddying
   the shine — which is exactly what made it look unlike the original.
   The gradient already carries its own drop-shadow for depth. */
.chrome-text,
.chapter .copy .chrome-text,
.page-hero .chrome-text,
.datebar b,
.marquee span i,
.grad-text {
  text-shadow: none !important;
}

/* The hero wordmark is an image — give it the same grounding. */
#ch-hero .hero-title img {
  filter:
    drop-shadow(0 0 14px rgba(255, 255, 255, .55))
    drop-shadow(0 0 42px rgba(214, 236, 255, .38))
    drop-shadow(0 2px 10px rgba(0, 0, 0, .45));
}

/* --------------------------------------------------------------------------
   5. Mobile menu sometimes showed only "HOME". The overlay is a child of the
   fixed <nav>, so it competes inside that stacking context and page sections
   could paint over it. Lift both, and guarantee full viewport coverage on
   iOS (100dvh accounts for the collapsing address bar).
   -------------------------------------------------------------------------- */
@media (max-width: 760px) {
  .nav { z-index: 9990; }
  .nav-links {
    z-index: 9999;
    min-height: 100dvh;
    background: rgba(4, 6, 15, .985);
    justify-content: center;
    gap: 1.6rem;
    padding-top: calc(var(--nav-h) + 4vh);
  }
  .nav-links a { font-size: 1.25rem; }
  .nav-links .btn { margin-top: .6rem; }
  .burger { z-index: 10000; position: relative; }
}

/* --------------------------------------------------------------------------
   7. Card rows ("Good to Know" and friends) read as uneven when one card's
   text runs several lines longer than its neighbours, or when a stray last
   word dangles alone. The copy is balanced in i18n.js; these rules do the
   rest — even top alignment, no orphan words, and consistent breathing room.
   -------------------------------------------------------------------------- */
.grid .card {
  display: flex;
  flex-direction: column;
}
.card h3,
.card p {
  text-wrap: pretty;          /* avoids a single word alone on the last line */
}
.card p {
  hyphens: none;
  overflow-wrap: break-word;
}

@media (max-width: 900px) {
  /* Four across is far too narrow on a phone; two reads much better and the
     cards stop looking ragged. */
  .grid.c4 { grid-template-columns: repeat(2, 1fr) !important; gap: 1rem !important; }
  .grid .card { padding: 1.4rem 1.2rem; }
}
@media (max-width: 560px) {
  .grid.c4 { grid-template-columns: 1fr !important; }
}

/* --------------------------------------------------------------------------
   6. General mobile polish — stop long headings from cramping.
   -------------------------------------------------------------------------- */
@media (max-width: 760px) {
  .shead h2, .section h2 { text-wrap: balance; }
  .wrap { padding-left: 20px; padding-right: 20px; }
}

/* --------------------------------------------------------------------------
   11. "Strangers become friends" chapter — brighter photo, readable type.

   The Niijima onsen photo is flatter and hazier than the other chapter images,
   and the standard veil pushed it to a grey wash: you could not make out the
   faces, the sea or the hills. Lift the photo and thin the veil HERE ONLY, so
   the other chapters keep the moodier grade they were designed with.

   Brightening the picture costs contrast against the white type, so the words
   get a real OUTLINE rather than a heavier shadow — `paint-order: stroke fill`
   draws the stroke BEHIND the glyph, which keeps the letterforms at full
   weight instead of eating into them the way a plain text-stroke does.

   Deliberately NO wide glow shadows here. The first attempt carried them
   (30–46px blur at ~.9 alpha) and on a phone they ruined the thing they were
   meant to help: the heading wraps to three lines and the paragraph to three
   more, so every glyph's halo overlapped its neighbours and summed into a grey
   rectangle of fog sitting on top of the photo — exactly the "muffled by the
   shades" the owner reported on 2026-08-15. An outline does not accumulate
   that way: it stays welded to the letter at any size or line count. Keep the
   remaining shadows tight (≤4px) — they seat the type without hazing the
   picture. If legibility ever needs more, thicken the stroke, never the blur.

   Keyed to the chapter's i18n key, not its photo, so re-cropping or swapping
   the image later does not silently switch these rules off.
   -------------------------------------------------------------------------- */
.chapter:has([data-i18n="ch4.h"]) .bg {
  filter: brightness(1.3) saturate(1.16) contrast(1.03);
}
.chapter:has([data-i18n="ch4.h"]) .veil {
  background: radial-gradient(120% 90% at 50% 88%,
              rgba(4, 6, 15, .42) 0%, rgba(4, 6, 15, .12) 45%, rgba(4, 6, 15, .06) 100%);
}
.chapter:has([data-i18n="ch4.h"]) .copy h2 {
  -webkit-text-stroke: 3px rgba(4, 6, 15, .68);
  paint-order: stroke fill;
  text-shadow: 0 1px 3px rgba(4, 6, 15, .55);
  /* Japanese broke mid-compound (家族 split across lines); balancing moves the
     break to the sentence boundary. English is unaffected. */
  text-wrap: balance;
}
.chapter:has([data-i18n="ch4.h"]) .copy p {
  color: #fff;
  -webkit-text-stroke: 1.2px rgba(4, 6, 15, .62);
  paint-order: stroke fill;
  text-shadow: 0 1px 2px rgba(4, 6, 15, .6);
}
.chapter:has([data-i18n="ch4.h"]) .copy .kicker {
  -webkit-text-stroke: 1px rgba(4, 6, 15, .55);
  paint-order: stroke fill;
  text-shadow: 0 1px 3px rgba(4, 6, 15, .6);
}
/* The heading shrinks to ~2.2rem on a phone, where a 3px stroke is
   proportionally much heavier than it is on the desktop display size. Scale it
   back so the letterforms stay open. */
@media (max-width: 760px) {
  .chapter:has([data-i18n="ch4.h"]) .copy h2 { -webkit-text-stroke-width: 2px; }
  .chapter:has([data-i18n="ch4.h"]) .copy p  { -webkit-text-stroke-width: .8px; }
}

/* --------------------------------------------------------------------------
   12. "Sleep inside the music" — VIP villas CTA.
   Was a bespoke sweep/pulse treatment; the owner chose one consistent gold
   CTA instead (2026-08-18), so the button now simply carries .btn-solid-gold
   with its shared gradShift movement, like every other gold CTA.
   -------------------------------------------------------------------------- */

/* ---------- phone hero: the villa ----------
   Since 2026-10-01 every *-m.jpg/-m.webp in uploads/2026/10 is the owner's
   own portrait (4:5) rendition of the same scene, made for phones, so the
   phone shows the whole picture centred — no low anchor, no sky extension.
   (mobius-stage-2 has no portrait version and keeps the 1200px landscape cut.) */
@media(max-width:760px){
  #ch-hero .bg{
    background-image:url('/wp-content/uploads/2026/10/villa-front-m.jpg')!important;
    background-position:50% 50%!important;
  }
}

/* --------------------------------------------------------------------------
   VIP villa rows on phones. The generic .tier grid halves the width for the
   name (wrapping every word) and stacks two full-width pills. Villas get a
   clean single column — name, price, note — and the two actions share one
   row, Details ghost beside the gradient Buy.
   -------------------------------------------------------------------------- */
@media(max-width:760px){
  .tier:has(.v-ctas){grid-template-columns:1fr;gap:.3rem;padding:1.4rem 1.3rem}
  .tier:has(.v-ctas) .t-name{font-size:1.08rem;line-height:1.45}
  .tier:has(.v-ctas) .t-sub{margin-top:.35rem;line-height:1.7}
  .tier:has(.v-ctas) .price{font-size:1.65rem;margin-top:.35rem;justify-self:start}
  .tier:has(.v-ctas) .v-ctas{display:flex;gap:.7rem;margin-top:.9rem;justify-content:stretch}
  .tier:has(.v-ctas) .v-ctas .btn{
    flex:1;width:auto;white-space:nowrap;text-align:center;
    padding:.85em .4em;font-size:.82rem;
  }
}

/* --------------------------------------------------------------------------
   11. Hero photographs, cut to the shape a phone actually shows.

   These fill a full-screen box — about 1170x2532 device pixels on a modern
   handset. `background-size: cover` was already cropping a tall slice out of a
   wide photograph and scaling it up nearly five times, which is what "the
   quality degraded" meant: the picture on screen was running at a fifth of the
   resolution it needed.

   So the phone files are cropped to that shape. The visible picture is
   identical — the browser was making the same crop — but at the right
   resolution, and in fewer bytes, because nothing is spent on parts of the
   frame a phone never shows. 1170x2532 matches the hand-made composite that was
   already here for the main hero and measured 0.86x sharp.

   WebP for everything since iOS 14, so including Instagram's in-app browser,
   with a JPEG underneath for anything that does not understand `image-set`.
   The ?v= is the file's own modified time: recut a picture and the URL changes,
   so nobody is served a stale copy from a 30-day cache.

   Re-keyed 2026-10-01 for the new venue photos (uploads/2026/10) plus the
   archive photos the pages still use. Desktop is untouched above 760px.
   -------------------------------------------------------------------------- */
@media (max-width: 760px) {
  .bg[style*="villa-front.jpg"]{background-image:url('/wp-content/uploads/2026/10/villa-front-m.jpg') !important}
  .bg[style*="sea-stage.jpg"]{background-image:url('/wp-content/uploads/2026/10/sea-stage-m.jpg') !important}
  .bg[style*="mobius-stage.jpg"]{background-image:url('/wp-content/uploads/2026/10/mobius-stage-m.jpg') !important}
  .bg[style*="mobius-stage-2.jpg"]{background-image:url('/wp-content/uploads/2026/10/mobius-stage-2-m.jpg') !important}
  .bg[style*="beach-camping.jpg"]{background-image:url('/wp-content/uploads/2026/10/beach-camping-m.jpg') !important}
  .bg[style*="secret-deck.jpg"]{background-image:url('/wp-content/uploads/2026/10/secret-deck-m.jpg') !important}
  .bg[style*="vip-room-ocean.jpg"]{background-image:url('/wp-content/uploads/2026/10/vip-room-ocean-m.jpg') !important}
  .bg[style*="vip-room-7.jpg"]{background-image:url('/wp-content/uploads/2026/10/vip-room-7-m.jpg') !important}
  .bg[style*="sauna-sky.jpg"]{background-image:url('/wp-content/uploads/2026/10/sauna-sky-m.jpg') !important}
  .bg[style*="sauna.jpg"]{background-image:url('/wp-content/uploads/2026/10/sauna-m.jpg') !important}
  .bg[style*="sauna-view.jpg"]{background-image:url('/wp-content/uploads/2026/10/sauna-view-m.jpg') !important}
  .bg[style*="sauna-plunge.jpg"]{background-image:url('/wp-content/uploads/2026/10/sauna-plunge-m.jpg') !important}
  .bg[style*="big-bath.jpg"]{background-image:url('/wp-content/uploads/2026/10/big-bath-m.jpg') !important}
  .bg[style*="past-lasers-green.jpg"]{background-image:url('/wp-content/uploads/2026/08/past-lasers-green-m.jpg') !important}
  .bg[style*="past-family-sunset.jpg"]{background-image:url('/wp-content/uploads/2026/08/past-family-sunset-m.jpg') !important}
  .bg[style*="past-island-aerial-hq.jpg"]{background-image:url('/wp-content/uploads/2026/08/past-island-aerial-hq-m.jpg') !important}
  .bg[style*="cta-river.jpg"]{background-image:url('/wp-content/uploads/2026/08/cta-river-m.jpg') !important}

  @supports (background-image: image-set(url("a.webp") type("image/webp"))) {
    .bg[style*="villa-front.jpg"]{background-image:url('/wp-content/uploads/2026/10/villa-front-m.webp') !important}
    .bg[style*="sea-stage.jpg"]{background-image:url('/wp-content/uploads/2026/10/sea-stage-m.webp') !important}
    .bg[style*="mobius-stage.jpg"]{background-image:url('/wp-content/uploads/2026/10/mobius-stage-m.webp') !important}
    .bg[style*="mobius-stage-2.jpg"]{background-image:url('/wp-content/uploads/2026/10/mobius-stage-2-m.webp') !important}
    .bg[style*="beach-camping.jpg"]{background-image:url('/wp-content/uploads/2026/10/beach-camping-m.webp') !important}
    .bg[style*="secret-deck.jpg"]{background-image:url('/wp-content/uploads/2026/10/secret-deck-m.webp') !important}
    .bg[style*="vip-room-ocean.jpg"]{background-image:url('/wp-content/uploads/2026/10/vip-room-ocean-m.webp') !important}
    .bg[style*="vip-room-7.jpg"]{background-image:url('/wp-content/uploads/2026/10/vip-room-7-m.webp') !important}
    .bg[style*="sauna-sky.jpg"]{background-image:url('/wp-content/uploads/2026/10/sauna-sky-m.webp') !important}
    .bg[style*="sauna.jpg"]{background-image:url('/wp-content/uploads/2026/10/sauna-m.webp') !important}
    .bg[style*="sauna-view.jpg"]{background-image:url('/wp-content/uploads/2026/10/sauna-view-m.webp') !important}
    .bg[style*="sauna-plunge.jpg"]{background-image:url('/wp-content/uploads/2026/10/sauna-plunge-m.webp') !important}
    .bg[style*="big-bath.jpg"]{background-image:url('/wp-content/uploads/2026/10/big-bath-m.webp') !important}
    .bg[style*="past-lasers-green.jpg"]{background-image:url('/wp-content/uploads/2026/08/past-lasers-green-m.webp') !important}
    .bg[style*="past-family-sunset.jpg"]{background-image:url('/wp-content/uploads/2026/08/past-family-sunset-m.webp') !important}
    .bg[style*="past-island-aerial-hq.jpg"]{background-image:url('/wp-content/uploads/2026/08/past-island-aerial-hq-m.webp') !important}
    .bg[style*="cta-river.jpg"]{background-image:url('/wp-content/uploads/2026/08/cta-river-m.webp') !important}
  }
}

/* --------------------------------------------------------------------------
   12. Effects a phone cannot afford.

   The page was still lagging inside Instagram's in-app browser after 3.4 MB of
   downloads had been removed, which meant the cost was no longer bytes but
   frames. Four things ran continuously on every page:

     .grain::after   a fixed SVG noise overlay at inset:-100%, i.e. NINE TIMES
                     the viewport, animated forever on an 8s step loop and
                     sitting above everything at z-index 2000
     .aurora         three half-viewport blobs under a 90px blur, each on its
                     own infinite animation
     .nav.scrolled   backdrop-filter: blur(18px) saturate(1.4) on a POSITION
                     FIXED bar — recomposited on every scroll frame, which is
                     about the most expensive thing available on iOS
     .btn-primary    a 220%-wide gradient animated forever, on every button

   Each is atmosphere. None carries information. On a phone they are removed or
   frozen; above 760px the design is exactly as it was.

   The grain and the aurora are kept as STILL layers rather than deleted — the
   texture and the colour wash are part of the look, it is the movement that
   costs.
   -------------------------------------------------------------------------- */
@media (max-width: 760px) {
  /* Still texture, no animation, and only as large as the screen it covers. */
  .grain::after {
    inset: 0;
    animation: none;
  }

  /* The colour wash stays; the blobs stop moving and the blur comes down.
     A 90px blur on a 52vw element is a full-screen gaussian every frame. */
  .aurora {
    filter: blur(48px) saturate(1.3);
  }
  .aurora i,
  .aurora i:nth-child(1),
  .aurora i:nth-child(2),
  .aurora i:nth-child(3) {
    animation: none;
  }

  /* A translucent bar without the live blur. The nav is fixed, so the blur was
     being recomputed against fresh content on every single scroll frame. */
  .nav.scrolled,
  .nav-links {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  .nav.scrolled { background: rgba(4, 6, 15, .92); }
  .nav-links    { background: rgba(4, 6, 15, .985); }

  /* Cards keep their glass tint, lose the live blur behind it. */
  .card, .tier, .vip-card, .btn-glass-gold {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }

  /* The gradient stays; it just stops sliding. */
  .btn-primary {
    animation: none;
    background-size: 100% 100%;
  }

  /* The hero title's 14px-blurred glow layer is a large live filter behind
     moving text. */
  #ch-hero .hero-glow-wrap::before,
  #ch-hero .hero-glow-wrap::after {
    filter: blur(8px);
  }

  /* Anything still looping decoratively can stop. */
  .marquee-track { animation-duration: 60s; }
}

/* --------------------------------------------------------------------------
   13. Owner review, 2026-08-24 — phone pass.

   Everything here came from screenshots taken in Instagram's in-app browser.
   -------------------------------------------------------------------------- */
@media (max-width: 760px) {

  /* -- the bar was 14px over budget, so flex squeezed its contents ----------
     The logo is a 1000x500 mark and was rendering 53x38 — an aspect of 1.4
     against a true 2.0, which is the "squished" look — and the language pill
     was clipping 日本語 to 日本. Neither is a sizing bug: they were being
     shrunk to make the row fit. Tighten the gap and forbid the shrink. */
  .nav-inner { gap: 12px; }
  .nav-logo,
  .lang-switch,
  .mobile-lang { flex: 0 0 auto; }
  .nav-logo img { height: 34px; width: auto; max-width: none; }
  .mobile-lang button { padding: .45em .7em; white-space: nowrap; }

  /* -- section headings were smaller than the body text under them ---------
     "Gain Entrance to Paradise" set at 16px, which is why it read as a label
     rather than a title. */
  .shead h2,
  .section .shead h2 { font-size: clamp(1.5rem, 6.2vw, 1.9rem); line-height: 1.25; }

  /* -- the two hero titles -- */
  .page-hero h1 { font-size: clamp(2.4rem, 10.5vw, 3rem); line-height: 1.12; }

  /* The VIP title carries its size in an INLINE style
     (`clamp(2.6rem,6.4vw,5rem)`), which no stylesheet rule can beat without
     !important — and on a phone that clamp was resolving to 25.6px, smaller
     than the paragraph beneath it. Explicit px so the result does not depend
     on how the clamp is resolved. */
  .vip-hero-title,
  .page-hero .vip-hero-title,
  h1.vip-hero-title { font-size: clamp(30px, 9vw, 40px) !important; line-height: 1.2; }

  /* -- the info page's black boxes -----------------------------------------
     Not ours. Storefront generates `table:not(.has-background) tbody td {
     background-color: … }` from the Customizer background, which lands on the
     festival's info table as a dark rectangle behind every value. The festival
     table draws its own dividers and wants no fill. */
  .info-table,
  .info-table th,
  .info-table td,
  .info-table tr { background: transparent !important; }

  /* -- the villa buy button, a little smaller -- */
  .villa-hero .btn-solid-gold,
  .vip-hero .btn-solid-gold,
  .page-hero .btn-solid-gold { font-size: .72rem; padding: .95em 2em; }

  /* -- Ask and Buy were touching on the Wolf card -- */
  .wolf-cta { display: flex; flex-direction: column; gap: .85rem; align-items: center; }
  .wolf-cta .btn { padding: .95em 2.6em; font-size: .85rem; margin: 0; }
}

/* --------------------------------------------------------------------------
   14. The hero photograph ends where the basket strip begins.

   The strip is fixed to the bottom of the screen and the story chapters fill
   the whole of it, so the strip sat on top of the picture and took the water
   with it -- the one part of the frame the owner had just spent three rounds
   choosing.

   Two things were eating the bottom of the photograph, and both are fixed
   here.

   The strip itself, about 67px and more on a notched phone. `--lq-basket-h`
   carries its measured height out of cart.js and the chapters stop there.
   `!important` is needed because home.js writes `inset:0` inline on every
   chapter as it promotes them to absolute.

   The parallax overhang. `.chapter .bg` is inset -8% on all four sides so the
   scroll has somewhere to travel, which quietly clipped the bottom eight per
   cent as well. The hero only ever travels downward -- `yPercent:8` in
   home.js -- so the bottom overhang was never doing any work. Removing it
   puts the last row of the picture on the last row of the hero, and it
   shortens the small gap that opens at the top at full scroll rather than
   lengthening it.

   The photograph is cut to match: 1170x2172 rather than 1170x2532, which is
   the shape of the shorter box. When the basket is empty there is no strip,
   the hero is full height again, and the same file fills the taller box with
   a little more taken off its sides.
   -------------------------------------------------------------------------- */
@media (max-width: 760px) {
  /*
   * Fallback first, for anything without dvh/svh. This is the original
   * geometry: the chapters are 100svh and the parallax overhang is what
   * covers the difference between that and the real bottom of the screen.
   * Nothing gained here, but nothing broken either.
   */
  #story .chapter .bg { inset: -8%; }

  @supports (height: 100dvh) {
    /*
     * `#story` is 100svh on purpose -- home.js sets it that way so that a
     * toolbar collapsing mid-scroll cannot resize the pinned section under
     * the finger. The basket strip, though, is `position:fixed` and sits on
     * the *real* bottom of the screen. Those two are not the same line: on
     * iOS they differ by whatever the collapsed toolbar gave back, and the
     * gap between them is dark.
     *
     * Subtracting `100dvh - 100svh` closes it. The chapters reach past the
     * bottom of `#story` by exactly that difference, which lands them on the
     * strip whatever the toolbar happens to be doing, and a further 24px so
     * that rounding can never open a hairline. Those 24px are underneath the
     * strip and cost nothing.
     */
    #story .chapter {
      inset: 0 0 calc(var(--lq-basket-h, 0px) - (100dvh - 100svh) - 24px) 0 !important;
      /* `.chapter` also carries `height:100svh`, and on an absolutely
         positioned box a stated height outranks `bottom`. */
      height: auto !important;
    }

    #story .chapter .bg { inset: -8% -8% 0 -8%; }

    /*
     * The chapters now hang below `#story`, and they carry z-index 2 to 7 for
     * the crossfade -- above the marquee (4) and the sections (5) that follow.
     * Giving `#story` its own stacking context keeps that ordering internal,
     * so the overhang cannot paint over the next section as the pin releases.
     */
    #story { isolation: isolate; }
  }

  /* The strip rises out of the picture rather than butting against it: a
     short wash above its gold hairline, dark enough to seat the join and
     light enough to leave the water visible through it. */
  #lq-basket-bar::before {
    content: "";
    position: absolute;
    left: 0; right: 0; bottom: 100%;
    height: 22px;
    background: linear-gradient(to top, rgba(10, 13, 22, .72), rgba(10, 13, 22, 0));
    pointer-events: none;
  }
}
