/* Provisoire - chrome around the mosaic. The work itself is never filtered. */

/* Both faces are the studio's own files from _RSC/font, subsetted to what the
   site actually sets. CAT Arena is the wordmark only; Plus Jakarta is everything
   else. See the README for the command that rebuilds them. */
@font-face{
  font-family:"CAT Arena";
  src:url("../fonts/cat-arena.woff2") format("woff2");
  font-weight:400;font-style:normal;font-display:swap;
}
@font-face{
  font-family:"Plus Jakarta Sans";
  src:url("../fonts/plusjakarta-400.woff2") format("woff2");
  font-weight:400;font-style:normal;font-display:swap;
}

:root{
  --ink:#f4f2ef;
  --dim:rgba(244,242,239,.46);
  --hair:rgba(244,242,239,.15);
  --bg:#0a0a0a;
  --pad:clamp(14px,2.4vw,32px);
  --ease:cubic-bezier(.22,1,.36,1);
  --font:"Plus Jakarta Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
  --display:"CAT Arena","Plus Jakarta Sans",Helvetica,Arial,sans-serif;
}

*,*::before,*::after{box-sizing:border-box}
html,body{height:100%}
html{
  /* No blue/grey flash when a finger touches anything. */
  -webkit-tap-highlight-color:transparent;
}
body{
  margin:0;background:var(--bg);color:var(--ink);
  font-family:var(--font);font-size:13px;line-height:1.35;
  -webkit-font-smoothing:antialiased;
  overflow:hidden;overscroll-behavior:none;
  /* Dragging across the mosaic or a caption should pan, not highlight text. */
  -webkit-user-select:none;user-select:none;
}
/* The media itself: no long-press callout, no drag ghost. */
img,video,canvas{
  -webkit-touch-callout:none;
  -webkit-user-drag:none;user-drag:none;
}
/* Any `display` rule of our own outranks the UA stylesheet's [hidden]{display:none},
   so an element toggled with el.hidden stays on screen while its flag reads true.
   That has bitten this file twice - declare it once, globally, and be done. */
[hidden]{display:none!important}

/* Suppressing the tap highlight must not cost keyboard users their focus ring. */
:focus-visible{outline:1px solid var(--dim);outline-offset:3px}
/* The crawlable fallback is a document, so leave its text selectable. */
.fallback{-webkit-user-select:text;user-select:text}

#stage{
  position:fixed;inset:0;display:block;
  width:100%;height:100%;
  touch-action:none;            /* we own pinch + drag */
  cursor:grab;
}
#stage:active{cursor:grabbing}

/* ---------- loader ---------- */
.loader{
  position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);z-index:60;
  transition:opacity .5s var(--ease),visibility .5s;
}
.loader i{
  display:block;width:26px;height:26px;border-radius:50%;
  border:1px solid var(--hair);border-top-color:var(--ink);
  animation:spin 700ms linear infinite;
}
@keyframes spin{to{transform:rotate(360deg)}}
.loader.is-done{opacity:0;visibility:hidden}

/* ---------- bars ---------- */
.bar{
  position:fixed;left:0;right:0;z-index:40;
  display:flex;align-items:center;justify-content:space-between;
  gap:16px;padding:var(--pad);
  pointer-events:none;
  opacity:0;transition:opacity .8s var(--ease) .1s;
}
body.is-ready .bar{opacity:1}
/* The wordmark scales with the viewport and the nav does not, so centring the
   two line boxes puts them on different baselines at every width but one. Share
   a baseline instead and they line up everywhere. */
.bar--top{top:0;align-items:baseline}
.bar--bottom{bottom:0;align-items:flex-end}
/* Once zoomed in the chrome sits over photography - give it something to sit on. */
.bar::before{
  content:"";position:absolute;left:0;right:0;height:190%;z-index:-1;
  pointer-events:none;opacity:0;transition:opacity .45s var(--ease);
}
.bar--top::before{top:0;background:linear-gradient(to bottom,rgba(10,10,10,.85),transparent)}
.bar--bottom::before{bottom:0;background:linear-gradient(to top,rgba(10,10,10,.85),transparent)}
body.is-zoomed .bar::before{opacity:1}
.bar a,.bar button{pointer-events:auto}

.mark{display:flex;align-items:center;color:inherit;text-decoration:none}
.mark span{
  font-family:var(--display);
  /* Presentation only - data/site.php keeps the name properly cased for share
     previews and structured data. */
  text-transform:uppercase;
  font-size:clamp(16px,1.4vw,20px);
  letter-spacing:.06em;   /* all-caps needs more air than mixed case */
  line-height:1;
  white-space:nowrap;
  opacity:.95;
  transition:opacity .25s;
}
.mark:hover span{opacity:1}

.nav{display:flex;gap:clamp(14px,1.4vw,22px);align-items:baseline}
/* letter-spacing also adds a gap AFTER the last letter, which pushes the text
   away from the right edge by a space the wordmark has no equivalent of on the
   left. Take it back so both insets are the frame padding. */
.nav>:last-child{margin-right:-.06em}
.nav button,.nav a{
  appearance:none;background:none;border:0;padding:0;margin:0;cursor:pointer;font:inherit;
  /* Sized to match the wordmark's CAP HEIGHT, not its font-size. CAT Arena's
     caps are .61 of its size and Plus Jakarta's are .75, so equal font-sizes
     would leave the nav looking a fifth too big. .81x the wordmark puts the two
     on the same cap line - which is what reads as "the same size".
     Wordmark is clamp(16px,1.4vw,20px); this is that x .813. */
  font-size:clamp(13px,1.12vw,16px);
  letter-spacing:.06em;   /* was .13em - label tracking is too airy at this size,
                             and .06em is the wordmark's, so the two rhyme */
  text-transform:uppercase;
  color:var(--dim);text-decoration:none;transition:color .25s;
}
.nav button:hover,.nav a:hover,.nav button[aria-expanded=true]{color:var(--ink)}

.hint{
  margin:0;font-size:11px;letter-spacing:.13em;text-transform:uppercase;color:var(--dim);
}
.hint{transition:opacity .5s var(--ease)}
.hint.is-gone{opacity:0}

/* Bottom-right of the frame, matching the hint on the left so the two read as
   one line of chrome rather than a button. */
.social{
  display:flex;color:var(--dim);transition:color .25s;
  /* Padding makes a finger-sized target; the matching negative margin pulls the
     box back so the glyph still sits on the frame's padding line. The glyph is
     26-32px, so the target lands between 50 and 56 - well past the 44px thumb
     minimum, and the mark reads as a mark rather than as small print. */
  padding:12px;margin:-12px -12px -12px 0;
}
/* Scales with the frame the way the wordmark does, rather than sitting at one
   fixed size that is right on exactly one screen. */
.social .ig{display:block;width:clamp(26px,2.2vw,32px);height:clamp(26px,2.2vw,32px)}
.social:hover{color:var(--ink)}

/* The Contact panel is the one overlay that keeps the link - it is a contact
   route, and this is the view people go looking for it in. .bar is its own
   stacking context, so the whole bottom bar has to rise, which means muting the
   two things that have no business over the panel. */
body.is-contact .bar--bottom{z-index:53}
body.is-contact .bar--bottom::before{opacity:0}
body.is-contact .hint{opacity:0;pointer-events:none}

/* Under 360px the wordmark and the nav stop fitting on one line. Scale both by
   the same factor rather than only the nav, so they stay on a common cap line. */
@media (max-width:359.98px){
  .mark span{font-size:14px}
  .nav{gap:12px}
  .nav button,.nav a{font-size:11.4px}
}

/* ---------- zoom-out button ---------- */
.out{
  position:fixed;left:50%;bottom:calc(var(--pad) + 30px);transform:translateX(-50%);
  z-index:45;appearance:none;cursor:pointer;font:inherit;
  display:flex;align-items:center;gap:9px;
  padding:9px 16px;border-radius:100px;
  border:1px solid var(--hair);background:rgba(10,10,10,.72);
  backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);
  color:var(--dim);font-size:11px;letter-spacing:.13em;text-transform:uppercase;
  transition:color .25s,border-color .25s;
}
.out[hidden]{display:none}
.out:hover{color:var(--ink);border-color:var(--dim)}
.out span{
  width:9px;height:9px;border-left:1px solid currentColor;border-bottom:1px solid currentColor;
  transform:rotate(45deg);display:block;
}

/* ---------- index ---------- */
.index{
  position:fixed;inset:0;z-index:50;
  background:rgba(10,10,10,.9);
  backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);
  padding:calc(var(--pad)*3.2) var(--pad) var(--pad);
  overflow:hidden;                 /* the list is sized to fit - never scrolls */
  display:flex;
  opacity:0;visibility:hidden;transition:opacity .4s var(--ease),visibility .4s;
}
.index.is-open{opacity:1;visibility:visible}
.index__list{
  list-style:none;margin:0 auto;padding:0;max-width:1100px;width:100%;
  display:flex;flex-direction:column;flex:1 1 auto;min-height:0;
}
.index__list li{
  border-top:1px solid var(--hair);
  flex:1 1 0;min-height:0;display:flex;align-items:stretch;
}
.index__list li:last-child{border-bottom:1px solid var(--hair)}
.index__list button{
  appearance:none;background:none;border:0;cursor:pointer;font:inherit;width:100%;
  color:var(--ink);text-align:left;
  display:grid;grid-template-columns:3.2em minmax(0,1fr);
  align-items:center;gap:clamp(8px,2vw,26px);
  padding:0;min-height:0;
  opacity:.6;transition:opacity .25s var(--ease),padding-left .35s var(--ease);
}
.index__list button:hover,.index__list button:focus-visible{opacity:1;padding-left:12px}
.index__list em{font-style:normal;font-size:11px;letter-spacing:.1em;color:var(--dim)}
.index__list strong{
  font-weight:400;letter-spacing:-.02em;line-height:1.05;
  font-size:clamp(14px,min(2.4vw,3.2vh),28px);   /* shrinks so 15 rows always fit */
}

/* ---------- contact sheet ---------- */
/* Sized in vh as well as vw so the whole panel fits a short phone: the site
   never scrolls. */
.sheet{
  position:fixed;inset:0;z-index:52;
  background:rgba(10,10,10,.94);
  backdrop-filter:blur(22px);-webkit-backdrop-filter:blur(22px);
  padding:calc(var(--pad)*3.4) var(--pad) calc(var(--pad)*1.4);
  overflow:hidden;display:flex;
  opacity:0;visibility:hidden;transition:opacity .4s var(--ease),visibility .4s;
}
.sheet.is-open{opacity:1;visibility:visible}
.sheet__inner{
  margin:auto;width:100%;max-width:1000px;
  display:flex;flex-direction:column;
  gap:clamp(18px,min(3.4vw,4.2vh),46px);
}
.sheet__lead{
  margin:0;
  font-size:clamp(19px,min(3.1vw,4.1vh),40px);
  line-height:1.16;letter-spacing:-.025em;text-wrap:balance;
  max-width:20ch;
}
.sheet__cols{
  display:grid;grid-template-columns:1fr 1fr;
  gap:clamp(16px,3vw,52px);
}
.sheet__block h3{
  margin:0 0 .7em;font-weight:400;
  font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--dim);
}
.sheet__block p{
  margin:0;color:var(--ink);
  font-size:clamp(12px,min(1.15vw,1.7vh),15px);
  line-height:1.5;max-width:38ch;
}
.sheet__foot{
  display:flex;flex-direction:column;gap:.5em;
  padding-top:clamp(10px,2vh,22px);
  border-top:1px solid var(--hair);
}
.sheet__invite{
  margin:0;color:var(--dim);
  font-size:11px;letter-spacing:.13em;text-transform:uppercase;
}
.sheet__mail{
  color:var(--ink);text-decoration:none;
  font-size:clamp(17px,min(2.4vw,3.2vh),30px);
  letter-spacing:-.02em;line-height:1.1;
  width:max-content;max-width:100%;word-break:break-word;
  border-bottom:1px solid transparent;
  transition:border-color .25s;
}
.sheet__mail:hover{border-bottom-color:var(--ink)}

/* ---------- contact form ---------- */
/* Directly under the address, as its second half: the same invitation offered
   two ways. Held to a readable measure rather than the full width of the panel,
   so the fields sit under the address instead of sprawling past it. */
.sheet__form{
  display:flex;flex-direction:column;
  gap:clamp(8px,1.5vh,18px);
  margin-top:clamp(10px,1.8vh,20px);
  width:100%;max-width:min(100%,600px);
}
.sheet__row{display:grid;grid-template-columns:1fr 1fr;gap:clamp(14px,3vw,52px)}

.field{display:flex;flex-direction:column;gap:.45em;margin:0;min-width:0}
.field label{
  font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--dim);
}
.field input,.field textarea{
  appearance:none;-webkit-appearance:none;border-radius:0;
  background:transparent;border:0;border-bottom:1px solid var(--hair);
  color:var(--ink);font:inherit;
  font-size:clamp(13px,min(1.3vw,1.9vh),16px);
  padding:.4em 0;width:100%;
  transition:border-color .25s;
}
.field input:focus,.field textarea:focus{outline:none;border-bottom-color:var(--ink)}
.field textarea{resize:none;line-height:1.45;height:clamp(46px,9vh,96px)}
/* On a short phone the panel has single-figure millimetres to spare, so the
   message box gives some back. It scrolls inside itself as you type, which is a
   textarea doing its job - not the page scrolling. */
@media (max-height:620px){
  .field textarea{height:clamp(38px,7vh,58px)}
}
/* Chrome paints its own colour over autofilled fields; this keeps the panel dark. */
.field input:-webkit-autofill{
  -webkit-text-fill-color:var(--ink);
  transition:background-color 9999s;
}
.field.is-bad input,.field.is-bad textarea{border-bottom-color:#c2554a}
/* The honeypot: gone for people and for screen readers, still there for bots. */
.field--trap{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}

.sheet__send{display:flex;align-items:center;flex-wrap:wrap;gap:.7em 18px}
.btn{
  appearance:none;cursor:pointer;font:inherit;
  font-size:11px;letter-spacing:.13em;text-transform:uppercase;
  color:var(--ink);background:transparent;
  border:1px solid var(--hair);border-radius:100px;
  padding:.85em 1.7em;
  transition:border-color .25s,background-color .25s,color .25s;
}
.btn:hover{border-color:var(--ink)}
.btn[disabled]{opacity:.45;cursor:default}
.sheet__status{
  margin:0;font-size:11px;letter-spacing:.06em;color:var(--dim);
  line-height:1.4;flex:1 1 14ch;min-width:0;opacity:.8;
}
.sheet__status.is-bad,.sheet__status.is-good{opacity:1}
.sheet__status.is-bad{color:#c2554a}
.sheet__status.is-good{color:var(--ink)}

/* Typing is the one moment the panel may scroll. On a phone the keyboard eats
   half the viewport, and a message field you cannot reach is worse than a rule
   about scrolling - so the panel gets an overflow only while a field has focus. */
body.is-typing .sheet{overflow-y:auto;overscroll-behavior:contain}

@media (max-width:720px){
  .sheet{padding-top:calc(var(--pad)*3.8)}
  .sheet__cols{grid-template-columns:1fr;gap:clamp(14px,2.6vh,24px)}
  .sheet__lead{max-width:none}
  /* Name and Email stay side by side even here. Vertical space is the scarce
     one on a phone, and both labels are short enough to survive the squeeze. */
  .sheet__row{gap:16px}
}

/* Everything has to share one frame now, so the lead gives up the size it had
   when it was the only thing on the panel. */
@media (max-width:720px),(max-height:860px){
  .sheet__lead{
    font-size:clamp(15px,min(2.3vw,2.9vh),26px);
    max-width:46ch;
  }
}

/* ---------- viewer ---------- */
/* No filter, no blend mode, no opacity on the media itself - pixels as authored. */
.viewer{
  position:fixed;inset:0;z-index:70;background:var(--bg);
  opacity:0;visibility:hidden;transition:opacity .28s var(--ease),visibility .28s;
  touch-action:none;
}
.viewer.is-open{opacity:1;visibility:visible}
.viewer__stage{
  /* z-index here is load-bearing: without it the stage makes no stacking context
     and .viewer__still (z-index 3) paints OVER the arrows, swallowing their taps. */
  position:absolute;z-index:1;
  inset:calc(var(--pad) + 34px) calc(var(--pad) + 62px) calc(var(--pad) + 58px);
  cursor:zoom-out;
  display:flex;align-items:center;justify-content:center;
}
/* Three permanent layers, stacked. Only their opacity changes, so the browser
   never has to build a new media pipeline or compositing layer. */
.viewer__layer{
  position:absolute;inset:0;
  width:100%;height:100%;
  object-fit:contain;      /* fills the stage box exactly, keeps aspect */
  display:block;
  filter:none;             /* the work is never graded */
  opacity:0;
  transition:opacity .2s linear;
}
.viewer__layer.is-on{opacity:1}
.viewer__under{z-index:1}
.viewer__film{z-index:2;opacity:1}
.viewer__film.is-waiting{opacity:0}
.viewer__still{z-index:3}
/* Foot: the brand, and the way through to everything else they made. */
.viewer__foot{
  position:absolute;z-index:3;left:var(--pad);right:var(--pad);bottom:var(--pad);
  display:flex;align-items:flex-end;gap:16px;
}
.viewer__brand{
  appearance:none;background:none;border:0;padding:0;cursor:pointer;font:inherit;
  color:var(--ink);text-align:left;display:flex;flex-direction:column;gap:4px;
}
.viewer__brand strong{font-weight:400;font-size:clamp(16px,2.2vw,22px);letter-spacing:-.015em}
.viewer__brand span{
  display:flex;align-items:center;gap:7px;
  font-size:11px;letter-spacing:.13em;text-transform:uppercase;color:var(--dim);
  transition:color .25s;
}
/* An author display:flex beats the UA's [hidden]{display:none}, so the element
   stayed painted while its .hidden property read true. Say it explicitly. */
.viewer__brand span[hidden]{display:none}
.viewer__brand span i{
  width:14px;height:1px;background:currentColor;display:block;
  transition:width .3s var(--ease);
}
.viewer__brand:disabled{cursor:default}
.viewer__brand:hover span{color:var(--ink)}
.viewer__brand:hover span i{width:24px}

/* Arrows: the affordance that says you can move sideways through the work. */
.viewer__arrow{
  position:absolute;top:50%;transform:translateY(-50%);z-index:3;
  appearance:none;border:0;cursor:pointer;
  width:clamp(42px,5vw,58px);height:clamp(42px,5vw,58px);border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  background:rgba(24,24,24,.72);color:var(--ink);
  border:1px solid var(--hair);
  backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
  transition:color .25s,background .25s,border-color .25s,transform .25s var(--ease);
}
.viewer__arrow svg{width:22px;height:22px;display:block}
.viewer__arrow--prev{left:calc(var(--pad) * .6)}
.viewer__arrow--next{right:calc(var(--pad) * .6)}
.viewer__arrow:hover{background:rgba(48,48,48,.9);border-color:var(--dim)}
.viewer__arrow--prev:hover{transform:translateY(-50%) translateX(-3px)}
.viewer__arrow--next:hover{transform:translateY(-50%) translateX(3px)}
.viewer__close{
  position:absolute;top:var(--pad);right:var(--pad);z-index:4;
  appearance:none;background:none;border:0;cursor:pointer;
  width:34px;height:34px;color:var(--dim);transition:color .25s;
}
.viewer__close:hover{color:var(--ink)}
.viewer__close span{position:relative;display:block;width:14px;height:14px;margin:auto}
.viewer__close span::before,.viewer__close span::after{
  content:"";position:absolute;top:50%;left:0;width:100%;height:1px;background:currentColor;
}
.viewer__close span::before{transform:rotate(45deg)}
.viewer__close span::after{transform:rotate(-45deg)}

@media (max-width:620px){
  /* Too narrow to inset the stage for the arrows - let them float over instead,
     so the work still gets the full width. */
  .viewer__stage{inset:calc(var(--pad) + 30px) var(--pad) calc(var(--pad) + 52px)}
  .viewer__arrow{width:46px;height:46px;background:rgba(10,10,10,.55)}
  .viewer__arrow svg{width:19px;height:19px}
  .viewer__arrow--prev{left:5px}
  .viewer__arrow--next{right:5px}
  .viewer__brand strong{font-size:16px}
  .brand__head{padding:calc(var(--pad) * 1.1) 0 calc(var(--pad) * .7)}
  .viewer__nav{width:16%}
  .index__list button{grid-template-columns:2.4em minmax(0,1fr)}
}


/* ---------- brand page: every piece for one client ---------- */
.brand{
  position:fixed;inset:0;z-index:60;background:var(--bg);
  overflow:hidden;                 /* the work is packed to fit - never scrolls */
  display:flex;flex-direction:column;
  opacity:0;visibility:hidden;transition:opacity .32s var(--ease),visibility .32s;
  padding:0 var(--pad) var(--pad);
}
.brand.is-open{opacity:1;visibility:visible}
.brand__head{
  flex:none;
  display:flex;align-items:center;justify-content:space-between;gap:16px;
  padding:calc(var(--pad) * 1.2) 0 calc(var(--pad) * .8);
}
.brand__head h2{
  margin:0;font-weight:400;letter-spacing:-.025em;
  font-size:clamp(24px,4.4vw,52px);
}
.brand__close{
  appearance:none;background:none;border:0;cursor:pointer;flex:none;
  width:34px;height:34px;color:var(--dim);transition:color .25s;
}
.brand__close:hover{color:var(--ink)}
.brand__close span{position:relative;display:block;width:15px;height:15px;margin:auto}
.brand__close span::before,.brand__close span::after{
  content:"";position:absolute;top:50%;left:0;width:100%;height:1px;background:currentColor;
}
.brand__close span::before{transform:rotate(45deg)}
.brand__close span::after{transform:rotate(-45deg)}

/* Tiles are positioned by layoutBrand() in JS: rows justified to the width, the
   block scaled to the height available. Every piece is on screen at once. */
.brand__grid{position:relative;flex:1 1 auto;min-height:0}

.tile{
  appearance:none;background:none;border:0;padding:0;margin:0;cursor:pointer;
  position:absolute;left:0;top:0;width:0;height:0;
  border-radius:5px;overflow:hidden;
  transition:opacity .25s var(--ease);
}
.tile img{display:block;width:100%;height:100%;object-fit:cover;filter:none}
.tile:hover{opacity:.85}
.tile__play{
  position:absolute;left:12px;top:12px;      /* tiles can be very tall - keep it in view */
  width:28px;height:28px;border-radius:50%;
  background:rgba(10,10,10,.55);
  border:1px solid rgba(244,242,239,.35);
  backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
  pointer-events:none;
}
.tile__play::before{
  content:"";position:absolute;left:10px;top:8px;
  border-left:8px solid var(--ink);
  border-top:5px solid transparent;border-bottom:5px solid transparent;
}

/* ---------- crawlable fallback ---------- */
.fallback{
  position:absolute;width:1px;height:1px;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;
}
body.no-canvas{overflow:auto}
body.no-canvas #stage,body.no-canvas .loader,body.no-canvas .out,
body.no-canvas .bar--bottom{display:none}
body.no-canvas .fallback{
  position:static;width:auto;height:auto;overflow:visible;
  clip:auto;clip-path:none;white-space:normal;
  max-width:900px;margin:0 auto;padding:calc(var(--pad)*4) var(--pad);
}
body.no-canvas .fallback h1{font-size:clamp(26px,5vw,50px);letter-spacing:-.03em;font-weight:400;margin:0 0 .4em}
body.no-canvas .fallback section{margin:3.2em 0 0}
body.no-canvas .fallback img{width:100%;height:auto;display:block;margin-bottom:1em}
body.no-canvas .fallback h2{font-size:21px;font-weight:400;margin:0 0 .3em}
body.no-canvas .fallback p{color:var(--dim);margin:0}
body.no-canvas .fallback a{color:var(--ink)}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{transition-duration:.01ms !important;animation-duration:.01ms !important}
}
