
/* ============================================================
   SRB Broadcast — 2026
   Design system: Trust & Authority + Conversion / Accessible & Ethical
   Type: Poppins (heading) + Open Sans (body)
   ============================================================ */
:root{
  color-scheme:dark;
  /* Dark canvas. The token NAMES are unchanged so every rule keeps working;
     only the values are inverted. Two names now read against their old sense
     and are kept deliberately rather than renamed across 900 lines:
       --ink  was the darkest text, now the lightest text
       --navy was a dark heading colour, now the ground of the even sections */
  --ink:#EDEFF2; --navy:#161A20; --slate:#C3C9D2; --muted:#9AA1AC;
  --bg:#07080A; --card:#101318; --border:#1E232B; --border-strong:#2A313B; --subtle:#161A20;
  /* input wells sit below the card they live in, not level with it */
  --field:#0B0D11;
  /* brand (from SRB logo) */
  --blue:#2E86E0; --blue-dark:#8FBEEE; --blue-soft:rgba(46,134,224,.16);
  --red:#ED1D24;
  /* On the dark ground the brand red is the mark colour; for small text it is
     lifted to a tint that clears AA on both the page and the card. */
  --red-ink:#FF8B85;
  --amber:#FBB12C;
  /* spacing scale (4/8) */
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:24px; --s6:32px;
  --radius-lg:18px;
  --header-h:68px;
  --shadow-sm:0 1px 2px rgba(0,0,0,.55), 0 1px 3px rgba(0,0,0,.45);
  --shadow-md:0 4px 12px rgba(0,0,0,.5), 0 12px 28px rgba(0,0,0,.45);
  --dur:380ms; --dur-fast:200ms; --ease:cubic-bezier(.22,.61,.36,1);
}
*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth}
body{
  margin:0; background:var(--bg); color:var(--ink);
  font-family:"Open Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
  font-size:16px; line-height:1.6; -webkit-font-smoothing:antialiased;
}
h1,h2,h3{font-family:Poppins,-apple-system,"Segoe UI",Roboto,sans-serif; margin:0; line-height:1.18; letter-spacing:-.01em}
p{margin:0}
a{color:inherit}
img{max-width:100%; display:block}
button{font:inherit; color:inherit}
:focus-visible{outline:3px solid var(--blue); outline-offset:3px; border-radius:6px}
.sr-only{position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0}
.skip{position:absolute; left:var(--s4); top:-60px; z-index:100; background:var(--blue); color:var(--bg);
  padding:10px 16px; border-radius:8px; text-decoration:none; transition:top var(--dur-fast) var(--ease)}
.skip:focus{top:10px}

/* ---------- header ---------- */
.header{
  position:fixed; inset:0 0 auto 0; height:var(--header-h); z-index:50;
  display:flex; align-items:center; gap:var(--s5);
  padding:0 clamp(16px,3vw,36px);
  /* No backdrop-filter. At 97% opacity the blur behind it is not visible, but
     it still forces the browser to re-blur a full-width strip on every scroll
     frame - the most expensive thing on the page for something nobody can
     see. The extra 5% opacity replaces it. */
  background:rgba(7,8,10,.97);
  border-bottom:1px solid var(--border);
}
.brand{display:flex; align-items:center; gap:var(--s3); text-decoration:none; flex:0 0 auto}
.brand img{height:30px; width:auto}
.brand-txt{display:flex; flex-direction:column; line-height:1.15}
.brand-txt b{font-family:Poppins; font-size:14px; font-weight:600; color:var(--ink); letter-spacing:.02em}
.brand-txt span{font-size:11px; color:var(--muted); letter-spacing:.08em; text-transform:uppercase}
.nav{display:flex; gap:var(--s1); margin-left:auto}
.nav a{
  position:relative; display:flex; align-items:center; min-height:44px; padding:0 var(--s4);
  text-decoration:none; color:var(--muted); font-size:14px; font-weight:600; border-radius:10px;
  transition:color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.nav a:hover{color:var(--ink); background:var(--subtle); cursor:pointer}
.nav a .long{display:inline}
.nav a[aria-current="page"]{color:var(--ink)}
.nav a[aria-current="page"]::after{
  content:""; position:absolute; left:var(--s4); right:var(--s4); bottom:8px; height:2px;
  background:var(--blue); border-radius:2px;
}
.cta{
  display:inline-flex; align-items:center; gap:var(--s2); min-height:44px; padding:0 var(--s5);
  background:var(--blue); color:var(--bg); border:0; border-radius:10px; text-decoration:none;
  font-size:14px; font-weight:600; cursor:pointer; white-space:nowrap;
  box-shadow:var(--shadow-sm); transition:background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.cta:hover{background:var(--blue-dark); transform:translateY(-1px)}
.cta:active{transform:translateY(0)}
.cta svg{width:16px; height:16px; flex:0 0 auto}
.cta--ghost{background:transparent; color:var(--ink); border:1px solid var(--border-strong); box-shadow:none}
.cta--ghost:hover{background:var(--subtle); border-color:var(--muted)}
.progress{position:fixed; top:var(--header-h); left:0; right:0; height:2px; z-index:49; background:transparent}
.progress i{display:block; height:100%; width:0; background:var(--blue); transition:width 120ms linear}

/* ---------- page + sticky stage ---------- */
.page[hidden]{display:none}
.track{position:relative}
.stage{
  position:sticky; top:var(--header-h);
  height:calc(100vh - var(--header-h)); height:calc(100svh - var(--header-h));
  display:grid; grid-template-rows:auto 1fr auto;
  padding:clamp(14px,2.2vh,26px) clamp(16px,3vw,36px) clamp(12px,2vh,20px);
  gap:clamp(10px,1.6vh,18px);
  opacity:1;
}
/* Transform-only, no fill-mode. A paused compositor (background tab) holds an
   animation at its first keyframe, so animating opacity here could leave the
   whole page invisible; moving it instead can never hide content. */
.page.is-entering .stage{animation:pageIn 420ms var(--ease)}
@keyframes pageIn{from{transform:translateY(12px)} to{transform:none}}

/* page heading row */
.phead{display:flex; align-items:center; gap:var(--s4); flex-wrap:wrap}
.chip{
  display:inline-flex; align-items:center; gap:var(--s2); padding:5px 11px; border-radius:999px;
  background:var(--blue-soft); color:var(--blue-dark); font-size:11px; font-weight:700;
  letter-spacing:.1em; text-transform:uppercase;
}
.chip i{width:6px; height:6px; border-radius:50%; background:var(--blue); flex:0 0 auto}
.phead h1{font-size:clamp(15px,1.5vw,18px); font-weight:600; color:var(--slate)}
.phead h1:focus{outline:none}
.phead h1:focus-visible{outline:3px solid var(--blue); outline-offset:4px}

/* two-column body */
.cols{
  display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1.06fr);
  gap:clamp(20px,3.4vw,56px); align-items:stretch; min-height:0;
}
/* an auto row is free to exceed .cols' own height; pinning it to the box
   means the columns can only ever be as tall as the stage allows */
@media (min-width:1024px){
  .cols{grid-template-rows:minmax(0,1fr)}
  .viz{min-height:0}
}
.slides{display:grid; align-content:center; min-width:0}
.slide{
  grid-area:1/1; min-width:0;
  opacity:0; transform:translateY(14px); pointer-events:none; visibility:hidden;
  /* exit is faster than enter so outgoing text clears before the next arrives.
     visibility flips only after the fade has finished, so the 17 inactive
     slides cost nothing to paint without shortening the exit by a frame. */
  transition:opacity 150ms var(--ease), transform 150ms var(--ease),
             visibility 0s linear 150ms;
}
.slide.is-active{
  opacity:1; transform:none; pointer-events:auto; visibility:visible;
  transition:opacity 320ms var(--ease) 130ms, transform 420ms var(--ease) 130ms,
             visibility 0s;
}
.kicker{
  display:flex; align-items:center; gap:var(--s3); margin-bottom:var(--s3);
  font-size:11px; font-weight:700; letter-spacing:.14em; text-transform:uppercase; color:var(--muted);
}
.kicker::before{content:""; width:22px; height:2px; background:var(--blue); border-radius:2px}
.slide h2{font-size:clamp(26px,3.1vw,42px); font-weight:700; color:var(--ink); margin-bottom:var(--s3)}
/* second half of every headline carries the brand blue - 5.4:1 on the page ground */
.slide h2 .ac,.phead h1 .ac{color:var(--blue)}
.slide h2.script .ac{color:var(--blue-dark)}
/* script headline — wedding slide only */
.slide h2.script{
  font-family:"Great Vibes",cursive; font-weight:400; letter-spacing:0;
  font-size:clamp(38px,4.8vw,64px); line-height:1.05; color:var(--ink);
  margin-bottom:var(--s4);
}
.lede{color:var(--slate); font-size:clamp(14px,1.05vw,16px); max-width:56ch; margin-bottom:var(--s4)}
/* Personal and Celebration carry an amber accent instead of the brand blue,
   with a sheen that runs across the letters on a loop. Two ambers: the deep
   one clears 4.5:1 on the light ground, the bright one is for the navy panel. */
.slide h2.ac-amber .ac{
  --a1:#FBB12C; --a2:#FFE7BE;
  background-image:linear-gradient(100deg,var(--a1) 45%,var(--a2) 50%,var(--a1) 55%);
  background-size:300% 100%; background-repeat:repeat-x;
  -webkit-background-clip:text; background-clip:text;
  color:transparent; -webkit-text-fill-color:transparent;
  animation:acSheen 4.2s linear infinite;
}
@keyframes acSheen{from{background-position:100% 0} to{background-position:0% 0}}
@media (prefers-reduced-motion:reduce){
  .slide h2.ac-amber .ac{animation:none}
}
/* without background-clip the fill colour is the only thing painting the glyphs,
   so never leave them transparent */
@supports not ((-webkit-background-clip:text) or (background-clip:text)){
  .slide h2.ac-amber .ac{
    background:none; color:var(--a1); -webkit-text-fill-color:currentColor; animation:none;
  }
}

/* ---------- subsections: several panels inside one section --------------
   Projects groups Arena, Panggung and Korporat & Institusi under one section.
   Scroll moves between sections; the rail buttons move between subsections,
   so a reader picks the field they care about instead of scrolling past two
   of them. The panels stack in one grid cell and cross-fade, exactly like the
   sections do, so the section keeps one height and the stage never jumps. */
.subs{display:grid; min-width:0}
.sub{
  grid-area:1/1; min-width:0;
  opacity:0; transform:translateY(10px); visibility:hidden; pointer-events:none;
  transition:opacity 150ms var(--ease), transform 150ms var(--ease),
             visibility 0s linear 150ms;
}
.sub.is-active{
  opacity:1; transform:none; visibility:visible; pointer-events:auto;
  transition:opacity 300ms var(--ease) 120ms, transform 380ms var(--ease) 120ms,
             visibility 0s;
}
/* detail list */
.facts{display:flex; flex-wrap:wrap; gap:var(--s2); list-style:none; padding:0; margin:0 0 var(--s4)}
.facts li{
  display:inline-flex; align-items:center; gap:7px; padding:6px 12px; border-radius:999px;
  background:var(--card); border:1px solid var(--border); font-size:12.5px; color:var(--slate); font-weight:600;
}
.facts li::before{content:""; width:5px; height:5px; border-radius:50%; background:var(--blue); flex:0 0 auto}
.facts.amber li::before{background:var(--amber)}

/* package rows (wedding) */
.pkgs{display:grid; gap:var(--s2); margin:0 0 var(--s4); list-style:none; padding:0}
.pkgs li{
  display:grid; align-items:center; gap:var(--s3);
  padding:10px var(--s4); background:var(--card); border:1px solid var(--border); border-radius:12px;
}
.pkgs b{font-family:Poppins; font-size:14px; color:var(--ink); font-weight:600; display:block}
.pkgs small{color:var(--muted); font-size:12px}

/* stat row */
.stats{display:flex; flex-wrap:wrap; gap:var(--s5); margin:0 0 var(--s4); padding:0; list-style:none}
.stats div{display:flex; flex-direction:column}
.stats b{
  font-family:Poppins; font-size:clamp(22px,2.4vw,30px); font-weight:700; color:var(--ink);
  line-height:1.1; font-variant-numeric:tabular-nums;
}
.stats span{font-size:12px; color:var(--muted); font-weight:600}
/* composition bars */
.bars{display:grid; gap:var(--s2); margin:0 0 var(--s4)}
.bar{display:grid; grid-template-columns:1fr; gap:5px}
.bar-top{display:flex; justify-content:space-between; font-size:12.5px; font-weight:600; color:var(--slate)}
.bar-top b{font-family:Poppins; color:var(--ink); font-variant-numeric:tabular-nums}
.bar-rail{height:7px; border-radius:99px; background:var(--subtle); overflow:hidden}
.bar-rail i{display:block; height:100%; border-radius:99px; background:var(--blue); width:0;
  transition:width 700ms var(--ease)}
.slide.is-active .bar-rail i{width:var(--w)}
.bar:nth-child(2) .bar-rail i{background:var(--amber)}
.bar:nth-child(3) .bar-rail i{background:var(--red)}

.actions{display:flex; flex-wrap:wrap; gap:var(--s3)}

/* right: image panel */
.viz{position:relative; display:flex; min-width:0}
.frame{
  position:relative; flex:1; display:flex; flex-direction:column; min-height:0;
  background:var(--card); border:1px solid var(--border); border-radius:var(--radius-lg);
  padding:10px; box-shadow:var(--shadow-md);
}
.frame-top{display:flex; align-items:center; gap:var(--s2); padding:2px 4px 9px; font-size:11px; color:var(--muted)}
.dot-live{width:7px; height:7px; border-radius:50%; background:var(--blue); flex:0 0 auto}
.frame-top b{font-family:Poppins; font-weight:600; color:var(--slate); letter-spacing:.06em; font-size:10.5px; text-transform:uppercase}
.frame-top .count{margin-left:auto; font-variant-numeric:tabular-nums; font-weight:700; color:var(--slate); letter-spacing:.04em}
/* The panel fills the stage row instead of being capped in viewport-height
   units: min-height:0 lets it shrink on short windows rather than overflow, so
   nothing is clipped and no empty band is left under it. */
.shots{
  position:relative;
  display:grid; border-radius:11px; overflow:hidden; background:var(--subtle);
  flex:1 1 auto; min-height:0;
}
/* Taking the photos out of flow is what keeps the panel honest. As in-flow
   grid items their `height:100%` has no definite height to resolve against,
   so each one falls back to the image's intrinsic 675px, that becomes the
   min-content height of .viz, and the auto row grows past the stage - the
   panel was rendering 539px tall inside a 386px row and spilling off-screen
   on short windows. Absolute children contribute no intrinsic height, so the
   panel takes exactly the room the stage gives it. The note cards in Insights
   are text and must still size to their content, hence the exclusion. */
.shots:not(.shots--note) .shot{position:absolute; inset:0}
/* No per-photo transition: the whole panel - box and photo as one element
   - is what fades, so the two can never drift apart in time. */
/* visibility, not opacity alone. An element at opacity 0 is still laid out,
   painted and composited: with 15 embedded photos on the page that is 7.7
   megapixels of decoded bitmap being pushed every frame for the 14 nobody can
   see. visibility:hidden takes them out of paint entirely. Safe here because
   the fade lives on .viz, one level up - .shot itself has no transition to
   cut short. */
.shot{grid-area:1/1; opacity:0; visibility:hidden}
.shot.is-active{opacity:1; visibility:visible}
.shot img{width:100%; height:100%; object-fit:cover}
.caption{
  display:grid; min-height:34px; align-content:center; padding:8px 4px 2px;
}
.caption span{grid-area:1/1; font-size:12px; color:var(--muted); opacity:0; transition:opacity 150ms var(--ease)}
.caption span.is-active{opacity:1; transition:opacity 320ms var(--ease) 130ms}

/* ---------- insights: note panel replaces the photo panel ---------- */
.shots--note{background:transparent; overflow:visible}
.shots--note .shot{
  background:var(--subtle); border:1px solid var(--border); border-radius:11px;
  padding:clamp(16px,2.6vh,28px) clamp(14px,1.7vw,24px);
  display:flex; flex-direction:column; justify-content:center;
}
.note-k{
  font-size:11px; font-weight:700; letter-spacing:.12em; text-transform:uppercase;
  color:var(--blue-dark); margin:0 0 var(--s3);
}
.viz--note{align-items:center}
.viz--note .frame{flex:0 1 auto}
.note-list{list-style:none; margin:0; padding:0; display:grid; gap:clamp(11px,1.9vh,20px)}
.note-list li{position:relative; padding-left:15px}
.note-list li::before{
  content:""; position:absolute; left:0; top:7px;
  width:5px; height:5px; border-radius:50%; background:var(--blue);
}
.note-list li:nth-child(4)::before{background:var(--amber)}
.note-list b{display:block; font-family:Poppins; font-size:clamp(13px,1.02vw,15px); font-weight:600; color:var(--ink)}
.note-list span{display:block; font-size:clamp(12.5px,.94vw,14px); color:var(--slate); line-height:1.5}

/* ---------- even sections: the blue-box layout -------------------------
   The alternation is per SECTION, not per page. Inside one page, section 1
   (Identitas) is the red box - text left, photo right - and section 2 (Visi &
   Misi) is the blue box, with the photo box sliding leftward into a dark panel
   and the supporting detail moving to a light panel on the right. Section 3
   returns to red, and so on. `.sec-even` is toggled on the page element by
   setSlide(), so one class drives the whole swap.
   Guarded to >=1024px: below that every section collapses to one column. */
@media (min-width:1024px){
  .sec-even .cols,
  .sec-even .slide{--c3:minmax(0,1fr) minmax(0,.63fr) minmax(0,1.32fr)}
  .sec-even.page--note .cols,
  .sec-even.page--note .slide{--c3:minmax(0,1fr) minmax(0,1.18fr)}

  .sec-even .cols{grid-template-columns:var(--c3); column-gap:0}
  /* the dark ground runs behind the text and photo columns as one block, so
     they must not be separated by a gap - the detail panel gets its own
     padding instead */
  .sec-even .cols::before{
    content:""; grid-row:1; grid-column:1/3; z-index:0;
    background:var(--navy); border-radius:var(--radius-lg);
  }
  .sec-even.page--note .cols::before{grid-column:1/2}
  .sec-even .slides{grid-row:1; grid-column:1/-1; z-index:1}
  .sec-even .viz{grid-row:1; grid-column:2; z-index:2}

  /* .side is built by JS at boot - see the sidePanels() block */
  .sec-even .slide{
    display:grid; grid-template-columns:var(--c3); column-gap:0; align-content:center;
  }
  .sec-even .slide > *{grid-column:1}
  .sec-even .slide > .side{grid-column:3; grid-row:1/span 99; align-self:center;
    padding-left:clamp(22px,2.8vw,48px)}
  .sec-even .slide > *:not(.side){
    padding-left:clamp(20px,2.3vw,36px); padding-right:clamp(14px,1.6vw,26px);
  }

  /* on navy: every pairing below is >=6:1, the accent >=7:1 */
  .sec-even .slide h2{color:#fff}
  .sec-even .slide h2 .ac{color:#8FBEEE}
  .sec-even .slide h2.script{color:#fff}
  .sec-even .slide h2.script .ac{color:#8FBEEE}
  .sec-even .slide h2.ac-amber .ac{--a1:#FBB12C; --a2:#FFE7BE; color:transparent}
  .sec-even .slide .lede{color:#CBD5E1}
  .sec-even .slide .kicker{color:#94A3B8}
  .sec-even .slide .kicker::before{background:var(--blue)}
  .sec-even .caption span{color:#94A3B8}

  /* the photo sits flush inside the dark panel, no card chrome */
  .sec-even:not(.page--note) .frame{
    background:transparent; border:0; box-shadow:none; padding:0;
    padding-bottom:clamp(10px,1.4vh,16px);
  }
  .sec-even:not(.page--note) .frame-top{display:none}
  .sec-even:not(.page--note) .shots{border-radius:var(--radius-lg); background:rgba(255,255,255,.06)}
  .sec-even:not(.page--note) .caption{padding:8px 10px 0}

  .sec-even .side .stats{margin:0 0 var(--s3)}
}
/* On odd sections the wrapper must not exist as a box at all, or the detail
   would sit in its own column instead of following the copy. display:contents
   lifts the children back into the slide's own flow. */
.side{display:contents}
@media (min-width:1024px){
  .sec-even .side{display:block}
  .sec-even .side .pkgs{margin:0 0 var(--s3)}
}

/* ---------- detail rows: icon, label, and a disclosure -------------------
   Chips carry a name and nothing else. In the reference's right-hand panel
   each line also says who it was for and opens to what was actually done, so
   the panel answers "what is this?" without leaving the slide. Row styling is
   not scoped to the parity block: Projects still renders these below 1024px,
   where the three-zone layout does not apply. */
.facts--rows{display:grid; gap:0; margin:0 0 var(--s4); padding:0; list-style:none}
.facts--rows li{
  display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:center;
  gap:var(--s3); padding:10px 2px; border-top:1px solid var(--border);
  background:none; border-radius:0;
}
/* opened, the row is taller than its title, so icon and button go back to the
   top line instead of floating to the middle of the detail text */
.facts--rows li.is-open{align-items:start}
.facts--rows li:first-child{border-top:0}
.facts--rows li::before{display:none}
.fx-i{width:19px; height:19px; flex:0 0 auto; color:var(--blue);
  fill:none; stroke:currentColor; stroke-width:1.6; stroke-linecap:round; stroke-linejoin:round}
.fx-b b{display:block; font-family:Poppins; font-size:13.5px; font-weight:600; color:var(--ink)}
.fx-b > span{display:block; margin-top:2px; font-size:12px; color:var(--muted)}
.fx-d{margin:7px 0 1px; font-size:12.5px; line-height:1.5; color:var(--slate)}
.fx-more{
  display:grid; place-items:center; width:32px; height:32px; margin:0 -4px 0 0; padding:0;
  background:none; border:1px solid var(--border); border-radius:9px; cursor:pointer;
  color:var(--muted);
}
.facts--rows li.is-open .fx-i{margin-top:2px}
.facts--rows li.is-open .fx-more{margin-top:-5px}
.fx-more:hover{border-color:var(--border-strong); color:var(--ink)}
.fx-more svg{width:15px; height:15px; fill:none; stroke:currentColor; stroke-width:1.8;
  stroke-linecap:round; transition:transform 220ms var(--ease)}
.fx-more[aria-expanded="true"]{color:var(--ink); border-color:var(--border-strong)}
.fx-more[aria-expanded="true"] svg{transform:rotate(45deg)}
.sprite{position:absolute; width:0; height:0; overflow:hidden}
/* short windows: the supporting line is the first thing to go, the row label
   and its disclosure stay */
@media (max-height:720px) and (min-width:1024px){
  .facts--rows li{padding:7px 2px}
  .fx-b > span{display:none}
  .fx-d{margin-top:5px; font-size:12px}
}

/* ---------- project rows: the list is the panel's remote control -------
   In the grouped section a row is not a disclosure any more. Pressing one puts
   that project's photo in the frame and writes its own caption under the
   photo, so the reading order is name -> picture -> what was actually done,
   and only one project is ever on screen. The whole row is the target: 44px
   tall, not a 32px glyph. Personal keeps the original open-in-place rows. */
.slide--group .facts--rows li{display:block; padding:0; border-top:0; background:none}
.fx-row{
  position:relative; display:grid; grid-template-columns:auto minmax(0,1fr) auto;
  align-items:center; gap:var(--s3); width:100%; margin:0;
  padding:10px 8px 10px 11px; text-align:left; cursor:pointer;
  background:none; border:0; border-top:1px solid var(--border); border-radius:10px;
  color:inherit; font:inherit;
  transition:background var(--dur) var(--ease);
}
.facts--rows li:first-child .fx-row{border-top:0}
.fx-row:hover{background:var(--subtle)}
.fx-row:focus-visible{outline:3px solid var(--blue); outline-offset:-3px}
.fx-b{display:block; min-width:0}
.fx-m{display:block; margin-top:2px; font-size:12px; color:var(--muted)}
/* the open project keeps a red rule at its edge - the same marker the kicker
   uses, so "this is the one on screen" reads the same way in both places */
.fx-row[aria-current="true"]{background:var(--blue-soft)}
.fx-row[aria-current="true"]::before{
  content:""; position:absolute; left:0; top:8px; bottom:8px; width:3px;
  border-radius:3px; background:var(--red);
}
.fx-row[aria-current="true"] .fx-b b{color:var(--blue-dark)}
.fx-row[aria-current="true"] .fx-go{background:var(--blue); border-color:var(--blue); color:var(--bg)}
/* the arrow points at the panel it fills, which is what the control does */
.fx-go{
  display:grid; place-items:center; width:32px; height:32px;
  border:1px solid var(--border); border-radius:9px; color:var(--muted);
  transition:background var(--dur) var(--ease), border-color var(--dur) var(--ease),
             color var(--dur) var(--ease);
}
.fx-go svg{width:16px; height:16px; fill:none; stroke:currentColor; stroke-width:1.8;
  stroke-linecap:round; stroke-linejoin:round}
.fx-row:hover .fx-go{border-color:var(--border-strong); color:var(--ink)}

/* ---------- caption under the photo, inside the frame ---------------- */
figure.frame{margin:0}
.fcap{display:grid; gap:2px; padding:11px 4px 2px; min-height:0}
.fcap b{
  font-family:Poppins; font-weight:600; color:var(--ink); line-height:1.25;
  font-size:clamp(13.5px,1.05vw,15.5px);
}
.fcap > span{font-size:11.5px; font-weight:600; color:var(--muted)}
.fcap p{margin:4px 0 0; font-size:12.5px; line-height:1.5; color:var(--slate); max-width:62ch}
.fcap p:empty{display:none}
.sec-even .fcap b{color:#fff}
.sec-even .fcap > span{color:#94A3B8}
.sec-even .fcap p{color:#CBD5E1}
.sec-even:not(.page--note) .fcap{padding:10px 10px 0}
@media (max-height:760px) and (min-width:1024px){
  .fcap p{margin-top:2px; font-size:12px; line-height:1.45}
  .fcap{gap:1px; padding-top:9px}
}

/* the page heading and the section under it were running together - space
   alone separates them, no rule: the rail already draws one at the bottom */
#projects .stage{gap:clamp(12px,2.1vh,22px)}
#projects .cols{padding-top:clamp(12px,2.3vh,26px)}

/* Slide and page changes fade the CONTENTS of the panel out and the next set
   in. The box itself - card ground, border, shadow, padding - never fades: it
   is the fixed window the work is shown through, so the reader's eye keeps one
   anchor while the photo and caption change behind it. The header bar is part
   of that window and never fades either - it names the panel, and a name that
   blinks on every swap reads as the panel itself leaving. Its text is rewritten
   in place instead. What is left fades as one group, so photo and caption
   cannot fall out of step, and nothing is animated on the compositor except
   opacity. The swap happens while those contents are invisible - see
   setSlide(). */
.frame > *:not(.frame-top){transition:opacity 260ms var(--ease)}
.viz.is-swapping .frame > *:not(.frame-top){opacity:0; transition-duration:170ms}

/* ---------- bottom rail ---------- */
.rail{display:flex; align-items:center; gap:var(--s4); flex-wrap:wrap; border-top:1px solid var(--border); padding-top:var(--s3)}
.dots{display:flex; gap:var(--s1); list-style:none; margin:0; padding:0}
.dots button{
  display:grid; place-items:center; width:34px; height:44px; padding:0;
  background:none; border:0; cursor:pointer;
}
.dots button i{display:block; height:4px; width:16px; border-radius:99px; background:var(--border-strong);
  transition:width var(--dur) var(--ease), background var(--dur) var(--ease)}
.dots button:hover i{background:var(--muted)}
.dots button[aria-current="true"] i{width:30px; background:var(--blue)}
/* subsection buttons — the only way between subsections, so they are labelled
   rather than reduced to dots */
.subnav{display:flex; flex-wrap:wrap; gap:6px}
.subnav[hidden]{display:none}
.subnav button{
  min-height:36px; padding:0 13px; cursor:pointer;
  background:var(--card); border:1px solid var(--border); border-radius:999px;
  font-family:Poppins; font-size:12.5px; font-weight:600; color:var(--slate);
  transition:background var(--dur) var(--ease), border-color var(--dur) var(--ease),
             color var(--dur) var(--ease);
}
.subnav button:hover{border-color:var(--border-strong); color:var(--ink)}
.subnav button[aria-current="true"]{background:var(--blue); border-color:var(--blue); color:var(--bg)}
.rail-hint{display:flex; align-items:center; gap:7px; font-size:12px; color:var(--muted)}
.rail-hint svg{width:14px; height:14px; animation:nudge 1.9s var(--ease) infinite}
@keyframes nudge{0%,100%{transform:translateY(0); opacity:.55} 50%{transform:translateY(3px); opacity:1}}
.rail-contact{margin-left:auto; display:flex; align-items:center; gap:var(--s4); font-size:12.5px; color:var(--muted)}
.rail-contact a{text-decoration:none; font-weight:600; color:var(--slate)}
.rail-contact a:hover{color:var(--blue); text-decoration:underline}
.rail-contact .ico{display:grid; place-items:center; width:40px; height:44px; color:var(--muted)}
.rail-contact .ico svg{width:18px; height:18px}
.rail-contact .ico:hover{color:var(--blue)}
.rail-social{display:flex; align-items:center; gap:2px; margin-left:-6px}
/* prev/next controls — only shown where scroll no longer drives the slides */
.slidenav{display:none; gap:var(--s2); margin-left:auto}
.slidenav button{
  display:grid; place-items:center; width:44px; height:44px; padding:0; cursor:pointer;
  background:var(--card); border:1px solid var(--border-strong); border-radius:10px; color:var(--ink);
}
.slidenav button:disabled{opacity:.4; cursor:default}
.slidenav svg{width:18px; height:18px}

/* ---------- contact: a static page, content scrolls normally ----------
   The form is taller than one screen on purpose. Pinning it inside the sticky
   stage like the slide pages would clip fields on short windows. */
.cwrap{
  max-width:1180px; margin:0 auto;
  padding:calc(var(--header-h) + clamp(20px,3.4vh,36px)) clamp(16px,3vw,36px) clamp(40px,6vh,72px);
}
.clede{color:var(--slate); font-size:clamp(14px,1.05vw,16px); max-width:62ch; margin:var(--s4) 0 var(--s6)}
.cgrid{
  display:grid; grid-template-columns:minmax(0,1.3fr) minmax(0,.85fr);
  gap:clamp(20px,3vw,44px); align-items:start;
}
.card{
  background:var(--card); border:1px solid var(--border); border-radius:var(--radius-lg);
  padding:clamp(18px,2.4vw,28px); box-shadow:var(--shadow-sm);
}
.card h2{font-size:clamp(19px,1.7vw,22px); font-weight:600; color:var(--ink); margin-bottom:var(--s2)}
.form-note{font-size:13px; color:var(--muted); margin-bottom:var(--s5)}
.req{color:var(--red-ink); font-weight:700}

/* fields */
.fgrid{display:grid; grid-template-columns:1fr 1fr; gap:var(--s4)}
.field{display:flex; flex-direction:column; gap:6px; margin:0; min-width:0}
.field.wide{grid-column:1/-1}
.field label{font-size:13px; font-weight:600; color:var(--ink)}
.field input,.field select,.field textarea{
  font:inherit; font-size:16px; color:var(--ink); background:var(--field);
  width:100%; min-height:48px; padding:12px var(--s3);
  border:1px solid var(--border-strong); border-radius:10px;
  transition:border-color var(--dur-fast) var(--ease);
}
.field textarea{min-height:120px; resize:vertical; line-height:1.55}
.field select{
  appearance:none; -webkit-appearance:none; padding-right:38px; cursor:pointer;
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239AA1AC' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 12px center; background-size:18px 18px;
}
.field input:hover,.field select:hover,.field textarea:hover{border-color:var(--muted)}
.field input:focus,.field select:focus,.field textarea:focus{border-color:var(--blue)}
.field .hint{font-size:12px; color:var(--muted)}
.field .err{display:none; align-items:flex-start; gap:7px; font-size:12.5px; font-weight:600; color:var(--red-ink)}
/* shape + text, never colour alone */
.field .err::before{
  content:"!"; flex:0 0 auto; display:grid; place-items:center; margin-top:1px;
  width:15px; height:15px; border-radius:50%; background:var(--red); color:#fff;
  font-family:Poppins; font-size:11px; font-weight:700; line-height:1;
}
.field.is-bad .err{display:flex}
.field.is-bad input,.field.is-bad select,.field.is-bad textarea{border-color:var(--red-ink); background:rgba(237,29,36,.10)}

/* error summary + success */
.errsum{border:1px solid rgba(255,139,133,.45); background:rgba(237,29,36,.10); border-radius:12px; padding:var(--s4); margin-bottom:var(--s5)}
.errsum[hidden]{display:none}
.errsum h3{font-family:Poppins; font-size:15px; font-weight:600; color:var(--red-ink); margin:0 0 var(--s2)}
.errsum ul{margin:0; padding-left:19px; display:grid; gap:5px}
.errsum a{color:var(--red-ink); font-size:13.5px; font-weight:600}
.okmsg{
  display:none; align-items:flex-start; gap:9px; margin-top:var(--s4);
  padding:var(--s3) var(--s4); border-radius:12px;
  background:rgba(46,212,122,.12); border:1px solid rgba(46,212,122,.38); color:#6EE7A8; font-size:13.5px; font-weight:600;
}
.okmsg.is-on{display:flex}
.okmsg svg{width:17px; height:17px; flex:0 0 auto; margin-top:1px}
.privacy{
  display:flex; align-items:flex-start; gap:9px; margin-top:var(--s5);
  font-size:12.5px; color:var(--muted); line-height:1.5;
}
.privacy svg{width:15px; height:15px; flex:0 0 auto; margin-top:2px}
.form-actions{display:flex; flex-wrap:wrap; gap:var(--s3); margin-top:var(--s5)}

/* contact channels */
.channels ul{list-style:none; margin:0; padding:0; display:grid; gap:var(--s1)}
.chan{
  display:flex; gap:var(--s3); align-items:flex-start; min-height:44px;
  padding:10px 12px; margin:0 -12px; border-radius:12px; text-decoration:none;
  border:1px solid transparent; transition:background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
a.chan:hover{background:var(--subtle); border-color:var(--border)}
.chan .ic{
  display:grid; place-items:center; width:38px; height:38px; flex:0 0 auto;
  border-radius:10px; background:var(--blue-soft); color:var(--blue-dark);
}
.chan .ic svg{width:18px; height:18px}
.chan .tx{min-width:0}
.chan b{
  display:block; font-family:Poppins; font-size:10.5px; font-weight:600;
  letter-spacing:.1em; text-transform:uppercase; color:var(--muted);
}
.chan strong{
  display:block; font-family:Poppins; font-size:15px; font-weight:600; color:var(--ink);
  overflow-wrap:anywhere;
}
.chan small{display:block; font-size:12.5px; color:var(--muted); line-height:1.45}
.resp{margin-top:var(--s5); padding-top:var(--s4); border-top:1px solid var(--border)}
.resp h3{font-family:Poppins; font-size:14px; font-weight:600; color:var(--ink); margin:0 0 6px}
.resp p{font-size:13px; color:var(--slate); line-height:1.55}

/* ---------- tall viewports: use the room instead of leaving it blank ------ */
@media (min-height:800px) and (min-width:1200px){
  .slide h2{font-size:clamp(34px,3.5vw,52px)}
  .slide h2.script{font-size:clamp(46px,5.4vw,74px)}
  .lede{font-size:17px; margin-bottom:var(--s5)}
  .kicker{margin-bottom:var(--s4)}
  .facts{gap:var(--s3); margin-bottom:var(--s5)}
  .facts li{padding:8px 14px; font-size:13.5px}
  .pkgs li{padding:14px var(--s5)}
  .pkgs b{font-size:16px}
  .pkgs small{font-size:13px}
  .stats b{font-size:clamp(28px,2.7vw,38px)}
  .stats span{font-size:13px}
  .bars{gap:var(--s4)}
  .bar-top{font-size:14px}
}

/* ---------- short viewports: compact so nothing is cut off ---------- */
@media (max-height:840px) and (min-width:1024px){
  .slide h2{font-size:clamp(24px,2.5vw,34px)}
  .slide h2.script{font-size:clamp(34px,3.6vw,48px)}
  .lede{font-size:14px; margin-bottom:var(--s3)}
  .facts{margin-bottom:var(--s3)}
  .facts li{padding:5px 10px; font-size:12px}
  .pkgs li{padding:8px var(--s3)}
  .pkgs{margin-bottom:var(--s3)}
  .bars{margin-bottom:var(--s3)}
  .stats{margin-bottom:var(--s3); gap:var(--s4)}
}
@media (max-height:720px) and (min-width:1024px){
  .phead h1{font-size:14px}
  .slide h2{font-size:clamp(22px,2.2vw,28px); margin-bottom:var(--s2)}
  .slide h2.script{font-size:clamp(30px,3vw,40px); margin-bottom:var(--s3)}
  .lede{font-size:13.5px; margin-bottom:var(--s2)}
  .kicker{margin-bottom:var(--s2)}
  .stats b{font-size:22px}
  .frame-top{padding-bottom:6px}
  .caption{min-height:24px; padding-top:6px}
  .shots--note .shot{padding:var(--s3) var(--s4)}
  .note-list{gap:8px}
  .note-list span{font-size:12px}
  .rail-hint{display:none}
}
@media (max-height:620px) and (min-width:1024px){
  .slide h2{font-size:clamp(20px,2vw,25px)}
  .slide h2.script{font-size:clamp(27px,2.6vw,34px); margin-bottom:var(--s2)}
  .lede{font-size:13px; margin-bottom:var(--s1); max-width:52ch}
  .facts li{padding:4px 9px; font-size:11.5px}
  .facts{gap:6px; margin-bottom:var(--s2)}
  .pkgs li{padding:6px var(--s3)}
  .pkgs{gap:6px; margin-bottom:var(--s2)}
  .actions .cta{min-height:40px}
  .stats{margin-bottom:var(--s2)}
  .shots--note .shot{padding:var(--s3)}
  .note-k{margin-bottom:var(--s2)}
  .note-list{gap:6px}
  .note-list b{font-size:12px}
  .note-list span{font-size:11.5px; line-height:1.45}
}

/* ---------- responsive ---------- */
@media (max-width:1023px){
  .cols{grid-template-columns:1fr; align-content:start; gap:var(--s4)}
  .viz{order:-1; display:block}
  .frame{display:block; flex:none}
  .shots{display:grid; aspect-ratio:16/10; flex:none}
  .stage{gap:var(--s3)}
  .slide h2{font-size:clamp(23px,5.4vw,32px)}
  .rail-contact .email{display:none}
  /* 5 nav items: shorten the labels and drop the CTA text so the row still fits */
  .nav a{padding:0 10px; font-size:13px}
  .nav a .long{display:none}
  .header .cta span{display:none}
  .header .cta{padding:0 var(--s4)}
  .cgrid{grid-template-columns:1fr}
}
/* Mobile: scroll no longer drives the slides — the stage grows with its content
   and the dots / prev-next buttons change slides instead. */
@media (max-width:719px){
  /* backdrop-filter on .header would make it the containing block for the
     fixed bottom nav, trapping it inside the header — keep the header opaque */
  .header{backdrop-filter:none; -webkit-backdrop-filter:none; background:var(--bg)}
  .nav{position:fixed; left:0; right:0; bottom:0; top:auto; z-index:50; margin:0; justify-content:space-around;
    background:var(--bg); border-top:1px solid var(--border);
    padding:4px 6px calc(4px + env(safe-area-inset-bottom))}
  .nav a{flex:1; justify-content:center; padding:0 var(--s2); font-size:13px}
  .nav a[aria-current="page"]::after{left:var(--s2); right:var(--s2); bottom:5px}
  /* static stage: nothing pushes it below the fixed header any more, so
     reserve that space or the page heading renders underneath it */
  .stage{position:static; height:auto; min-height:calc(100svh - var(--header-h) - 60px);
    padding-top:calc(var(--header-h) + 14px);
    padding-bottom:calc(72px + env(safe-area-inset-bottom))}
  .cols{align-items:start}
  /* .facts li paints a full box, and .facts--rows only resets its top edge, so
     the rows keep left/right rules. At this width the + button's -4px pull sat
     it on top of that right rule - pad the row instead and drop the pull. */
  .facts--rows li{padding:10px}
  .fx-more{margin-right:0}
  .shots{aspect-ratio:16/9}
  .caption{min-height:0}
  .slide{transition:opacity 180ms var(--ease)}
  .slide.is-active{transition:opacity 260ms var(--ease) 80ms}
  .slidenav{display:flex}
  .rail-hint{display:none}
  .rail-contact{display:none}
  .brand-txt span{display:none}
  .nav a{font-size:12px; padding:0 4px}
  .fgrid{grid-template-columns:1fr}
  .cwrap{padding-bottom:calc(84px + env(safe-area-inset-bottom))}
}

/* ---------- opening: the mark fills with water, then the curtain lifts ----------
   Rebuilt from SRB-OpeningPage-dan-Warna.md. The waterline is an SVG mask:
   one long wave path drifts sideways inside its group while the group itself
   is driven upward, so drift and rise are one transform pair instead of three
   animations competing for the same pixels. The mark underneath is the same
   logo twice - a dimmed monochrome ground, and the colour layer that the mask
   opens - so the fill reads as colour arriving rather than a second picture
   fading in. The curtain is five strips of the page colour; lifting them in
   sequence is what actually reveals the site. */
.pre{
  position:fixed; inset:0; z-index:200;
  display:grid; place-items:center; overflow:hidden;
  background:transparent;
}
.pre[hidden]{display:none}
.pre.is-hidden{display:none}
.pre.is-done{pointer-events:none}
.pre__grid{
  position:absolute; inset:0; z-index:1; opacity:.5;
  background-image:linear-gradient(var(--border) 1px,transparent 1px),
                   linear-gradient(90deg,var(--border) 1px,transparent 1px);
  background-size:88px 88px;
  -webkit-mask-image:radial-gradient(120% 90% at 50% 50%,#000 15%,transparent 72%);
          mask-image:radial-gradient(120% 90% at 50% 50%,#000 15%,transparent 72%);
}
.pre__stage{position:relative; width:min(66vw,470px); z-index:3}
.pre__logo{position:relative; width:100%; aspect-ratio:1100/495}
.pre__logo svg{width:100%; height:100%; display:block}
.pre__mono{filter:grayscale(1) brightness(1.9); opacity:.26}
.pre__meta{margin-top:34px; display:flex; align-items:flex-end; justify-content:space-between; gap:16px}
.pre__label{
  font-family:"SFMono-Regular",Consolas,"Liberation Mono",Menlo,monospace;
  font-size:.62rem; letter-spacing:.24em; text-transform:uppercase; color:var(--muted);
}
.pre__pct{
  font-family:Poppins; font-weight:700; font-size:clamp(1.7rem,4.6vw,2.5rem);
  line-height:1; letter-spacing:-.03em; font-variant-numeric:tabular-nums; color:var(--ink);
}
.pre__pct sup{font-size:.42em; color:var(--muted); margin-left:3px; top:-.9em}
.pre__bar{position:relative; height:2px; background:var(--border); margin-top:16px; overflow:hidden}
.pre__bar i{
  position:absolute; inset:0 auto 0 0; width:0%; display:block;
  background:linear-gradient(90deg,var(--blue),var(--red),var(--amber));
}
.pre__skip{
  display:block; margin:22px auto 0; cursor:pointer; background:transparent;
  font-family:"SFMono-Regular",Consolas,"Liberation Mono",Menlo,monospace;
  font-size:.6rem; letter-spacing:.2em; text-transform:uppercase;
  color:var(--muted); border:1px solid var(--border); border-radius:999px;
  padding:9px 18px; min-height:34px;
  opacity:0; pointer-events:none;
  transition:opacity .5s var(--ease), color .25s var(--ease), border-color .25s var(--ease);
}
.pre__skip.is-ready{opacity:1; pointer-events:auto}
.pre__skip:hover{color:var(--ink); border-color:var(--border-strong)}

/* the curtain is the opener's ground, not decoration on top of it */
.pre__curtain{position:absolute; inset:0; z-index:0; display:flex; pointer-events:none}
.pre__curtain i{flex:1; background:var(--bg); transform:translateY(0); box-shadow:1px 0 0 rgba(255,255,255,.025)}

/* the shine is clipped to the mark, so it never reads as a lit rectangle */
.pre__shine{
  position:absolute; inset:0; z-index:4; pointer-events:none; opacity:0;
  -webkit-mask-image:var(--logo);  mask-image:var(--logo);
  -webkit-mask-size:100% 100%;     mask-size:100% 100%;
  -webkit-mask-repeat:no-repeat;   mask-repeat:no-repeat;
  -webkit-mask-position:center;    mask-position:center;
}
.pre__shine i{
  position:absolute; top:-25%; bottom:-25%; left:0; width:46%;
  display:block; transform:translateX(var(--glint,-170%)) skewX(-16deg);
  background:linear-gradient(90deg,
    rgba(255,255,255,0) 0%, rgba(255,255,255,.20) 28%,
    rgba(255,255,255,.92) 50%, rgba(255,255,255,.20) 72%, rgba(255,255,255,0) 100%);
  filter:blur(1px);
}
.pre__shine i + i{
  width:64%;
  background:linear-gradient(90deg,
    rgba(237,29,36,0) 0%, rgba(251,177,44,.34) 42%,
    rgba(18,92,167,.34) 62%, rgba(18,92,167,0) 100%);
  filter:blur(7px);
}
.pre.is-full .pre__shine{opacity:1}
.pre.is-full .pre__logo{
  transform:scale(calc(1 + .026 * var(--bloom,0)));
  filter:drop-shadow(0 0 calc(26px * var(--bloom,0)) rgba(255,255,255,calc(.30 * var(--bloom,0))));
}
.pre.is-full .pre__mono{opacity:0; transition:opacity .7s var(--ease)}
.pre.is-full .pre__bar i{box-shadow:0 0 16px rgba(255,255,255,.45)}
.pre.is-done .pre__stage,
.pre.is-done .pre__grid{
  opacity:0; transform:translateY(-26px);
  transition:opacity .45s var(--ease), transform .7s cubic-bezier(.76,0,.24,1);
}
/* the opener runs on phones too; 66vw leaves the mark small there, and the
   skip button has to clear the home indicator */
@media (max-width:719px){
  .pre__stage{width:min(80vw,360px)}
  .pre__meta{margin-top:26px}
  .pre__skip{min-height:44px; padding:12px 20px; margin-bottom:env(safe-area-inset-bottom)}
}

/* ---------- ground: a collage of the work ----------
   The page ground was flat black. It is now twelve production stills: eight
   frames tiled edge to edge so several different jobs are plainly visible at
   once, plus four wide frames in screen blend drifting over the seams. The
   second set is what makes it a double exposure instead of a contact sheet -
   where it crosses a seam the brighter picture wins and the join disappears.
   Desaturated, held at about a third opacity and softened by a small blur so
   it stays a ground and never competes with the cards on top of it. The
   frames already run past every viewport edge, so the blur has picture to
   sample at the rim and no dark halo forms there. z-index:-1 puts it above the canvas
   colour and below every element, so no positioned/static ordering question
   arises with the content.
   The frames are deck shots the page has already decoded, so the ground costs
   no new bytes. */
.bgmark{display:none}
.bgmark.is-on{
  display:block;
  position:fixed; inset:0; z-index:-1; pointer-events:none;
  opacity:.34; filter:grayscale(1) brightness(.58) contrast(1.15) blur(3px);
}
.bgmark__band{position:absolute; inset:0}
.bgmark i{
  position:absolute; display:block;
  background-repeat:no-repeat; background-position:center; background-size:cover;
}
/* Each collage frame is solid well past its own middle and softens only at the
   rim. Nothing outside holds the shape of this layer, so a gentler falloff
   reads as eight vignettes rather than one ground. */
.bgmark i:nth-child(-n+9){
  -webkit-mask-image:radial-gradient(86% 86% at 50% 50%,#000 70%,transparent 100%);
          mask-image:radial-gradient(86% 86% at 50% 50%,#000 70%,transparent 100%);
}
/* Nine frames of deliberately unequal size and shape, overlapping so no grid
   line runs through the wall. Three stand, six lie down, and the widest is
   nearly four times the proportion of the narrowest - the contrast between
   shapes is what makes it read as a composed wall rather than brickwork.
   Every source photograph is landscape (4:3 or 16:10; there is not one
   portrait frame in the deck), so a standing cell always crops. The client
   asked for the composition over the whole frame, so it crops - and the
   script below still hands the widest photographs to the widest cells, which
   spends the cropping where it costs least. */
.bgmark i:nth-child(1){left:-3%; top:-4%; width:23%; height:70%}
.bgmark i:nth-child(2){left:18%; top:-4%; width:35%; height:37%}
.bgmark i:nth-child(3){left:51%; top:-4%; width:33%; height:33%}
.bgmark i:nth-child(4){left:82%; top:-4%; width:22%; height:62%}
.bgmark i:nth-child(5){left:18%; top:30%; width:22%; height:50%}
.bgmark i:nth-child(6){left:38%; top:26%; width:46%; height:36%}
.bgmark i:nth-child(7){left:-3%; top:63%; width:27%; height:41%}
.bgmark i:nth-child(8){left:22%; top:60%; width:38%; height:44%}
.bgmark i:nth-child(9){left:58%; top:55%; width:46%; height:49%}
/* the double exposure: four wide frames laid over the seams in screen blend,
   so the brighter picture wins wherever two of them meet */
.bgmark i:nth-child(n+10){
  mix-blend-mode:screen;
  -webkit-mask-image:radial-gradient(58% 58% at 50% 50%,#000 10%,transparent 84%);
          mask-image:radial-gradient(58% 58% at 50% 50%,#000 10%,transparent 84%);
}
.bgmark i:nth-child(10){left:-8%; top:-14%; width:56%; height:130%; opacity:.46}
.bgmark i:nth-child(11){left:28%; top:-10%; width:56%; height:122%; opacity:.42}
.bgmark i:nth-child(12){left:60%; top:-14%; width:52%; height:130%; opacity:.46}
.bgmark i:nth-child(13){left:8%;  top:6%;   width:84%; height:90%;  opacity:.30}
/* A phone is portrait, so the wall is rebuilt rather than rescaled: nine
   frames again, still unequal, but the rows run down the screen and the
   columns inside each row are split unevenly. */
@media (max-width:719px){
  .bgmark i:nth-child(1){left:-4%; top:-3%; width:50%; height:30%}
  .bgmark i:nth-child(2){left:44%; top:-3%; width:60%; height:21%}
  .bgmark i:nth-child(3){left:44%; top:15%; width:60%; height:15%}
  .bgmark i:nth-child(4){left:-4%; top:25%; width:50%; height:33%}
  .bgmark i:nth-child(5){left:36%; top:28%; width:68%; height:20%}
  .bgmark i:nth-child(6){left:36%; top:45%; width:68%; height:17%}
  .bgmark i:nth-child(7){left:-4%; top:55%; width:56%; height:25%}
  .bgmark i:nth-child(8){left:-4%; top:77%; width:56%; height:27%}
  .bgmark i:nth-child(9){left:50%; top:59%; width:54%; height:45%}
  .bgmark i:nth-child(10){left:-14%; top:-3%; width:128%; height:39%; opacity:.46}
  .bgmark i:nth-child(11){left:-12%; top:22%; width:124%; height:44%; opacity:.42}
  .bgmark i:nth-child(12){left:-14%; top:54%; width:128%; height:48%; opacity:.46}
  .bgmark i:nth-child(13){left:-10%; top:12%; width:120%; height:76%; opacity:.30}
}

/* ---------- reduced motion ---------- */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.01ms !important; animation-iteration-count:1 !important;
    transition-duration:.01ms !important}
  .slide{transform:none}
  .shot{transform:none}
  /* The opener stays, the movement goes. Removing it outright was wrong for
     phones in particular: reduce-motion is switched on far more often there
     than on desktop, and those visitors were landing on the site with no
     opening at all. What is dropped is motion - rising water, shine sweep,
     a counter ticking to 100 - not the page. The mark is shown already
     filled, and the strip that would have crawled is not drawn at all. */
  .pre__grid{display:none}
  .pre__mono{display:none}
  .pre__shine{display:none}
  .pre__bar,.pre__pct{display:none}
  .pre__logo{opacity:1 !important; transform:none !important; filter:none !important}
}

/* Last word on the note panel: the responsive blocks above re-assert an
   aspect-ratio on .shots for photos, but this card is text and must size to
   its content at every width. */
.shots--note{aspect-ratio:auto}
