/* =====================================================================
   AMJAD DEVELOPMENTS — سابقة الأعمال, the full-screen film gallery
   ---------------------------------------------------------------------
   The client asked for this page to be nothing but video, the way AL
   HISHAM's portfolio page is. So it is: one slide fills the viewport, you
   move between slides, and the only things drawn on top are the caption,
   the arrows, the sound button and the counter.

   FOUR DECISIONS WORTH RECORDING

   1. THE PAGE DOES NOT SCROLL. `overflow:hidden` on the html element while
      this page is open. A full-viewport slide that also scrolls gives you a
      second slide half-visible at the bottom, which is the one thing that
      makes a gallery feel like a list.

   2. ONLY ONE SLIDE HAS A src. The rest carry data-src and are given a real
      source when they are two slides away — see js/portfolio.js. Six videos
      at ~600 KB and one at 7 MB is 10 MB; loading that on arrival would be
      indefensible for a page most visitors will look at for ten seconds.

   3. THE FIRST SLIDE IS THE WHOLE FILM AND IT HAS SOUND. The client asked
      for that specifically. It cannot AUTOPLAY with sound — every browser
      blocks that, and the block is right. So it starts muted, and the
      speaker button unmutes it on a real click. The button says which state
      it is in, because a muted video with no indication is read as broken.

   4. THE CONTROLS ARE INSET FROM THE SAFE AREA. On a phone in landscape —
      which is how anyone actually watches this — the notch and the home
      indicator both eat into the corners the arrows want.
   ===================================================================== */

/* the shell ------------------------------------------------------------- */
html.fs-lock, html.fs-lock body{ overflow:hidden; height:100%; }

.fs-gallery{
  position:fixed; inset:0;
  background:#0b0601;
  overflow:hidden;
  z-index:1;
}
/* The header needs no z-index of its own here. It is already fixed at 900 in
   base.css, and the gallery is a stacking context at z-index:1 — so every
   z-index inside the gallery (up to and including the intro veil at 7) is
   clamped beneath it. An earlier draft set the header to 6 "so it floats over
   the gallery", which did nothing except move it below the phone drawer's
   1000 by a smaller margin. Left as a note rather than a rule.

   The header stays TRANSPARENT on this page, because the page never scrolls
   and so `.scrolled` is never added. That is not an oversight — the top of the
   gallery scrim is at .74 opacity, where cream measures 11:1 against the
   brightest frame, so the logo and the nav are legible without a bar. */

/* ------------------------------------------------------------- a slide ---
   All slides are stacked. The current one is opaque, the others are not and
   are pulled out of the accessibility tree by the script. A crossfade rather
   than a slide, because these are films of the same places and sliding them
   past each other reads as a carousel of adverts. */
.fs-slide{
  position:absolute; inset:0;
  opacity:0; visibility:hidden;
  transition:opacity .9s var(--ease), visibility .9s;
}
.fs-slide.on{ opacity:1; visibility:visible; }
.fs-slide video,
.fs-slide .fs-poster{
  position:absolute; inset:0;
  width:100%; height:100%;
  object-fit:cover;
}
.fs-slide .fs-poster{ background:center/cover no-repeat; }
/* the video fades up over its own poster, so a slow first frame is never a
   black hole */
.fs-slide video{ opacity:0; transition:opacity .8s var(--ease); }
.fs-slide video.ready{ opacity:1; }

/* The scrim, and it was MEASURED against the brightest clip in the set —
   mean luminance 0.57, a midday aerial, so a representative bright pixel
   under the caption is about (150,142,128).

   The first draft ran .72 at the top down to .30 by 18% of the height, on the
   assumption that a caption sitting near the top is over the strong part of
   the gradient. It is not: the caption starts at header-height + 34px, which
   on a laptop is 14% down and its last line is near 28% — i.e. exactly where
   the first draft had already faded to .30. There, beige body copy measured
   2.86:1. A comfortable fail, and completely invisible to look at, because
   the eye reads white-ish text on a photograph as "fine".

   So the strong band now covers the caption and only then opens up:

       0%    .74    cream 11.07  beige 7.10
       30%   .54    cream  7.38  beige 4.73   <- the last line of the caption
       46%   .24    nothing is drawn here — this is the picture
       70%   .40
       100%  .90    the counter and the dots

   The middle 20% of the frame is the clearest part of the image, which is
   where the building is in every one of these clips. */
.fs-gallery::after{
  content:""; position:absolute; inset:0; z-index:2; pointer-events:none;
  background:
    linear-gradient(180deg,
      rgba(12,7,2,.74) 0%,
      rgba(12,7,2,.54) 30%,
      rgba(12,7,2,.24) 46%,
      rgba(12,7,2,.24) 58%,
      rgba(12,7,2,.40) 70%,
      rgba(12,7,2,.90) 100%),
    linear-gradient(0deg, rgba(74,45,16,.16), rgba(74,45,16,.16));
}

/* ------------------------------------------------------------ the caption */
.fs-caption{
  position:absolute; z-index:4;
  inset-block-start:calc(var(--header-h) + 34px);
  inset-inline-start:clamp(20px,4vw,60px);
  max-width:min(46ch,72vw);
  pointer-events:none;
}
.fs-tag{
  display:inline-flex; align-items:center; gap:9px;
  font-family:"Jost",system-ui,sans-serif;
  font-size:12px; font-weight:300; letter-spacing:.30em; text-transform:uppercase;
  color:var(--gold);
  margin-bottom:12px;
}
.fs-tag::before{
  content:""; width:34px; height:1px; background:currentColor; opacity:.7;
}
.fs-title{
  font-family:"Aref Ruqaa",serif;
  font-size:clamp(28px,4.4vw,54px); line-height:1.25;
  color:var(--cream);
  margin:0 0 10px;
  text-shadow:0 2px 22px rgba(0,0,0,.55);
}
.fs-note{
  font-size:15px; line-height:1.85; color:var(--beige); margin:0;
  text-shadow:0 1px 14px rgba(0,0,0,.6);
}
/* the caption changes with the slide, so it animates in on its own */
.fs-caption.swap{ animation:fs-in .6s var(--ease) both; }
@keyframes fs-in{
  from{ opacity:0; transform:translateY(14px); }
  to  { opacity:1; transform:none; }
}
@media (prefers-reduced-motion: reduce){
  .fs-slide{ transition:none; }
  .fs-caption.swap{ animation:none; }
}

/* -------------------------------------------------------------- the arrows
   48px is the smallest a touch target may be; these are 56 so that a thumb
   holding a phone in landscape lands on them without aiming. */
.fs-nav{
  position:absolute; z-index:5; top:50%; transform:translateY(-50%);
  width:56px; height:56px;
  display:flex; align-items:center; justify-content:center;
  font-size:30px; line-height:1;
  color:var(--cream);
  background:rgba(255,255,255,.07);
  border:1px solid rgba(255,255,255,.17);
  border-radius:50%;
  -webkit-backdrop-filter:blur(18px) saturate(160%);
          backdrop-filter:blur(18px) saturate(160%);
  transition:background .3s var(--ease), border-color .3s var(--ease),
             transform .3s var(--spring,cubic-bezier(.34,1.4,.5,1));
}
.fs-nav:hover{
  background:rgba(255,255,255,.14);
  border-color:rgba(255,255,255,.32);
}
.fs-nav:active{ transform:translateY(-50%) scale(.93); }
/* RTL: "previous" is on the right. inset-inline handles it without a
   direction-specific rule. */
.fs-prev{ inset-inline-start:clamp(14px,2.4vw,34px); }
.fs-next{ inset-inline-end:clamp(14px,2.4vw,34px); }

/* ---------------------------------------------------------- the sound key
   Top-left in RTL terms = inset-inline-end. It is a real button with a real
   label, and the label changes, because "is the sound on" is the question a
   visitor actually has. */
.fs-sound{
  position:absolute; z-index:5;
  inset-block-start:calc(var(--header-h) + 34px);
  inset-inline-end:clamp(20px,4vw,60px);
  display:inline-flex; align-items:center; gap:9px;
  padding:11px 17px;
  font-size:14px; color:var(--cream);
  background:rgba(255,255,255,.07);
  border:1px solid rgba(255,255,255,.17);
  border-radius:999px;
  -webkit-backdrop-filter:blur(18px) saturate(160%);
          backdrop-filter:blur(18px) saturate(160%);
  transition:background .3s var(--ease), border-color .3s var(--ease);
}
.fs-sound:hover{ background:rgba(255,255,255,.14); border-color:rgba(255,255,255,.32); }
.fs-sound[aria-pressed="true"]{
  background:rgba(216,184,144,.20); border-color:rgba(216,184,144,.52); color:var(--gold);
}
.fs-sound .ico{ font-size:16px; line-height:1; }

/* ------------------------------------------------------- the bottom rail */
.fs-bottom{
  position:absolute; z-index:5;
  inset-inline:0; inset-block-end:max(24px, env(safe-area-inset-bottom));
  display:flex; flex-direction:column; align-items:center; gap:14px;
}
.fs-counter{
  font-family:"Jost",system-ui,sans-serif;
  font-size:13px; letter-spacing:.18em; color:var(--beige);
}
.fs-counter b{ color:var(--cream); font-weight:400; }
.fs-dots{ display:flex; gap:9px; }
.fs-dot{
  width:26px; height:3px; border-radius:2px;
  background:rgba(255,255,255,.24);
  transition:background .35s var(--ease), width .35s var(--ease);
}
.fs-dot[aria-current="true"]{ background:var(--gold); width:44px; }
.fs-dot:hover{ background:rgba(255,255,255,.5); }

/* the elapsed line for the long film, so you can see it is 2:06 and not 12s.
   Drawn as a hairline under the dots rather than as a scrub bar: it is
   information, not a control, and a control here would fight the arrows. */
.fs-progress{
  width:min(420px,62vw); height:2px;
  background:rgba(255,255,255,.16); border-radius:2px; overflow:hidden;
}
.fs-progress i{
  display:block; height:100%; width:0;
  background:linear-gradient(90deg, var(--gold), var(--cream));
}

/* ---------------------------------------------------------- the intro veil
   Covers the first slide until its first frame has decoded, so the page never
   opens on a black rectangle. Removed by the script, and also on a timer, so
   a video that never loads cannot leave the veil up for ever. */
.fs-intro{
  position:absolute; inset:0; z-index:7;
  display:flex; align-items:center; justify-content:center;
  background:var(--brown-900);
  transition:opacity .8s var(--ease), visibility .8s;
}
.fs-intro.gone{ opacity:0; visibility:hidden; }
.fs-intro-logo{
  width:min(46vw,340px); aspect-ratio:1100/406;
  background:center/contain no-repeat url("../assets/logo.webp");
  animation:fs-pulse 2.4s var(--ease) infinite alternate;
}
@keyframes fs-pulse{ from{ opacity:.55; } to{ opacity:1; } }

/* ----------------------------------------------------------- empty state
   If a slide has no file behind it the monogram is drawn instead of a broken
   frame. Not decoration — this page is meant to grow as the client sends
   footage, and the shape of the page must not depend on how much has
   arrived. */
.fs-empty{
  position:absolute; inset:0; z-index:3;
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:16px; text-align:center; padding:24px;
}
.fs-empty::before{
  content:"أ"; font-family:"Aref Ruqaa",serif; font-size:clamp(70px,14vw,140px);
  color:rgba(216,184,144,.16); line-height:1;
}
.fs-empty p{ color:var(--beige); margin:0; max-width:40ch; }

/* --------------------------------------------------------------- phones ---
   The caption moves down and shrinks, the arrows move to the bottom corners
   where a thumb is, and the sound key sits beside the counter. In portrait
   there is not room for a 54px title AND a 56px arrow at mid-height without
   one landing on the other. */
@media (max-width:820px){
  .fs-caption{
    inset-block-start:auto;
    inset-block-end:calc(max(24px, env(safe-area-inset-bottom)) + 96px);
    inset-inline:clamp(16px,5vw,28px);
    max-width:none;
  }
  .fs-title{ font-size:clamp(23px,6.4vw,32px); }
  .fs-note{ font-size:14px; }
  .fs-nav{
    top:auto; transform:none;
    inset-block-end:calc(max(24px, env(safe-area-inset-bottom)) + 6px);
    width:48px; height:48px; font-size:26px;
  }
  .fs-nav:active{ transform:scale(.93); }
  .fs-prev{ inset-inline-start:16px; }
  .fs-next{ inset-inline-end:16px; }
  .fs-sound{
    inset-block-start:auto;
    inset-block-end:calc(max(24px, env(safe-area-inset-bottom)) + 62px);
    inset-inline-start:50%; inset-inline-end:auto;
    transform:translateX(50%);
    padding:9px 14px; font-size:13px;
  }
  .fs-bottom{ gap:10px; }
  .fs-progress{ width:min(300px,74vw); }
  /* the gallery scrim needs more weight at the bottom on a phone, because
     the caption is now down there over the picture */
  .fs-gallery::after{
    background:
      linear-gradient(180deg,
        rgba(12,7,2,.62) 0%, rgba(12,7,2,.22) 20%,
        rgba(12,7,2,.20) 40%, rgba(12,7,2,.62) 72%,
        rgba(12,7,2,.93) 100%),
      linear-gradient(0deg, rgba(74,45,16,.16), rgba(74,45,16,.16));
  }
}
/* landscape on a phone is the real viewing position: give the picture back
   as much room as possible */
@media (max-height:520px) and (orientation:landscape){
  .fs-caption{ inset-block-end:auto; inset-block-start:64px; max-width:min(40ch,52vw); }
  .fs-title{ font-size:22px; margin-bottom:6px; }
  .fs-note{ display:none; }
  .fs-sound{ inset-block-start:64px; inset-block-end:auto; inset-inline-end:16px; inset-inline-start:auto; transform:none; }
  .fs-bottom{ inset-block-end:12px; gap:8px; }
  .fs-progress{ display:none; }
}
