/* Archivo carries a width axis, so wdth 125 + wght 900 stands in for Akira Expanded
   (Akira is a commercial face and cannot be served from Google Fonts). */
/* Everything is scoped to .ajx so it cannot touch the rest of the Squarespace theme. */
@property --ajx-c1{ syntax:'<color>'; inherits:true; initial-value:hsl(262 52% 63%) }
@property --ajx-c2{ syntax:'<color>'; inherits:true; initial-value:hsl(294 47% 75%) }
/* The hover tilt and the highlight position are registered so they can be
   TRANSITIONED. The card itself cannot carry a transform transition any more (the
   motion loop writes transform every frame and a transition would lag behind it),
   so the smoothing has to happen one level down, on the variables the transform
   reads. Without this the tilt snapped to the cursor on enter and snapped back on
   leave. */
@property --hx{ syntax:'<angle>'; inherits:false; initial-value:0deg }
@property --hy{ syntax:'<angle>'; inherits:false; initial-value:0deg }
/* --mx/--my MUST inherit: the highlight lives on .card::before, and a pseudo-element
   inherits from its originating element. Registered with inherits:false they resolve
   to the 50% initial value inside ::before and the highlight sits dead centre no
   matter where the cursor is. --hx/--hy are read by the card's own transform, so
   they do not need to inherit. */
@property --mx{ syntax:'<percentage>'; inherits:true; initial-value:50% }
@property --my{ syntax:'<percentage>'; inherits:true; initial-value:50% }
/* The showcase cards read every one of these into ONE transform and the transform
   itself carries NO transition - each part eases on its own variable instead. That
   is the only way the slow reveal (--rise, .58s with a per-card delay) and the fast
   cursor tilt (--hx/--hy, .18s) can live in the same transform without one dragging
   the other: a transition on the transform would re-ease an already-eased value and
   the tilt would trail the pointer. */
@property --rise{ syntax:'<length>'; inherits:false; initial-value:26px }
@property --pz  { syntax:'<length>'; inherits:false; initial-value:0px }
@property --scl { syntax:'<number>'; inherits:false; initial-value:1 }

.ajx{
  --paper:#0a0a0c; --paper-2:#121216; --ink:#f4f3f1; --ink-2:#c6c4c0;
  --muted:#9d9a95; --faint:#7e7b75; --line:#26262c;
  --serif:var(--ff-display);
  --body:var(--ff-body);
  font-family:var(--body); color:var(--ink); font-size:16px; line-height:1.6;
  background:var(--paper); width:100%; overflow-x:hidden;
}

/* Squarespace drops a code block into a small fluid-grid cell; these rules let it
   span the whole section and grow to its natural height. */
.fluid-engine:has(.ajx){ display:block !important; padding:0 !important; margin:0 !important; row-gap:0 !important }
.fe-block:has(.ajx){ grid-area:auto !important; width:100% !important; height:auto !important; position:static !important }
.fe-block:has(.ajx) .sqs-block,
.fe-block:has(.ajx) .sqs-block-content,
.fe-block:has(.ajx) .sqs-code-container{ width:100% !important; height:auto !important; padding:0 !important }
/* kill the section's own padding so the colour wash reaches the very top edge */
section:has(.ajx){ padding:0 !important; min-height:0 !important }
section:has(.ajx) .section-border,
section:has(.ajx) .content-wrapper,
section:has(.ajx) .content{ padding:0 !important; margin:0 !important }

/* site header becomes a glass bar floating over the wash (this page only) */
body:has(.ajx) #header{
  position:fixed !important; top:0; left:0; right:0; z-index:9999;
  background:rgba(10,10,12,.42) !important;
  backdrop-filter:blur(16px) saturate(1.4); -webkit-backdrop-filter:blur(16px) saturate(1.4);
  border-bottom:none;
}
/* the theme paints header text dark, which would vanish on black */
body:has(.ajx) #header a,
body:has(.ajx) #header .header-title-text a,
body:has(.ajx) #header .header-nav-item a,
body:has(.ajx) #header .header-nav-item a:visited{ color:#f4f3f1 !important }
body:has(.ajx) #header svg{ fill:#f4f3f1 !important; color:#f4f3f1 !important }
body:has(.ajx) #header .header-burger-inner .top-bun,
body:has(.ajx) #header .header-burger-inner .patty,
body:has(.ajx) #header .header-burger-inner .bottom-bun{ background:#f4f3f1 !important }

/* the page itself goes black behind and below the block */
body:has(.ajx),
body:has(.ajx) #page,
body:has(.ajx) #sections,
body:has(.ajx) #footer-sections,
body:has(.ajx) .page-section,
section:has(.ajx){ background-color:#0a0a0c !important }
/* logo font now lives in Settings > Advanced > Code Injection so it applies site-wide */


.ajx *{box-sizing:border-box}
.ajx a{color:inherit; text-decoration:none}
.ajx :focus-visible{outline:2px solid var(--ink); outline-offset:3px}

/* ---- ambient wash + glass ---- */
.ajx .top-shell{position:relative; isolation:isolate; overflow:hidden;
  --ajx-c1:hsl(262 52% 63%); --ajx-c2:hsl(294 47% 75%);
  transition:--ajx-c1 .9s ease, --ajx-c2 .9s ease}
.ajx .ambient{position:absolute; inset:-26% -12% -14%; z-index:0; pointer-events:none;
  background:
    radial-gradient(52% 44% at 20% 14%, var(--ajx-c1) 0%, transparent 70%),
    radial-gradient(50% 46% at 82% 30%, var(--ajx-c2) 0%, transparent 72%),
    radial-gradient(72% 58% at 50% 66%, var(--ajx-c1) 0%, transparent 74%);
  /* KEEP the blur. It is not redundant with .glass's backdrop-filter: it is what
     dithers these wide gradients. Removing it looked identical on a still frame but
     banded badly on the lighter palettes. Reverted 09-02, do not "optimise" again. */
  filter:blur(34px) saturate(1.15); opacity:.92}
.ajx .glass{position:absolute; inset:0; z-index:1; pointer-events:none;
  background:rgba(10,10,12,.62);
  backdrop-filter:blur(58px) saturate(1.35); -webkit-backdrop-filter:blur(58px) saturate(1.35)}
.ajx .top-shell > .intro, .ajx .top-shell > .deck{position:relative; z-index:2}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .ajx .glass{background:rgba(10,10,12,.82)}
}

/* ---- intro ---- */
.ajx .intro{text-align:center; max-width:1100px; margin:0 auto;
  padding:calc(var(--hdr, 88px) + clamp(24px,3.4vh,42px)) 26px clamp(10px,1.6vh,18px)}
.ajx .intro h1{font-family:var(--body); font-weight:900; font-variation-settings:'wdth' 125,'wght' 900;
  font-size:clamp(20px,2.9vw,40px); line-height:1.0; margin:0; letter-spacing:-.01em;
  text-transform:uppercase; color:var(--ink)}
.ajx .intro h1 em{font-style:normal}

/* ---- deck ---- */
.ajx .deck{padding:clamp(12px,1.6vh,20px) 0 clamp(26px,4vh,44px); background:transparent}
.ajx .deck-head{text-align:center; padding:0 24px}
.ajx .deck-head .eyebrow{font-size:10.5px; letter-spacing:.34em; text-transform:uppercase; color:var(--faint); margin:0}
.ajx .filters{display:flex; justify-content:center; gap:8px; flex-wrap:wrap; margin:clamp(34px,4.8vh,58px) 0 0}
.ajx .filters button{font-family:var(--body); font-size:10.5px; letter-spacing:.2em; text-transform:uppercase; color:var(--muted);
  background:rgba(255,255,255,.07); backdrop-filter:blur(8px); -webkit-backdrop-filter:blur(8px);
  border:1px solid rgba(255,255,255,.16); border-radius:999px; padding:9px 20px; cursor:pointer; transition:all .25s}
.ajx .filters button:hover{color:var(--ink); border-color:rgba(255,255,255,.38); background:rgba(255,255,255,.14)}
.ajx .filters button[aria-pressed="true"]{background:var(--ink); border-color:var(--ink); color:var(--paper)}

/* BASE STATE = a plain responsive grid. It needs no JavaScript, so the work is
   always visible (Squarespace disables code-block scripts for logged-in viewers). */
.ajx .stage-wrap{position:relative}
.ajx .stage{display:grid; grid-template-columns:repeat(auto-fill,minmax(300px,1fr));
  gap:18px; max-width:1280px; margin:clamp(16px,2.4vh,24px) auto 0; padding:0 24px}
.ajx .card{position:relative; width:100%; aspect-ratio:16/10; margin:0; display:block;
  border-radius:10px; overflow:hidden; background:var(--paper-2);
  box-shadow:0 14px 34px rgba(0,0,0,.55), 0 2px 10px rgba(0,0,0,.4)}
/* 3D tilt that follows the cursor; --hx/--hy are written by JS per card */
.ajx .stage-wrap:not(.is-live) .card{
  transform:perspective(900px) rotateX(var(--hx,0deg)) rotateY(var(--hy,0deg));
  transition:transform .3s cubic-bezier(.22,.8,.26,1), box-shadow .3s ease}
.ajx .stage-wrap:not(.is-live) .card:hover{box-shadow:0 28px 60px rgba(0,0,0,.68), 0 4px 14px rgba(0,0,0,.5)}
/* Soft highlight that follows the cursor. Sized in % so it scales with the card,
   with many stops so it feathers out gradually instead of ending in a hard edge. */
.ajx .card::before{content:""; position:absolute; inset:0; z-index:1; pointer-events:none; opacity:0;
  transition:opacity .5s ease;   /* slower out than in, so leaving the card settles */
  background:radial-gradient(62% 78% at var(--mx,50%) var(--my,50%),
    rgba(255,255,255,.19) 0%,
    rgba(255,255,255,.145) 12%,
    rgba(255,255,255,.10) 24%,
    rgba(255,255,255,.062) 38%,
    rgba(255,255,255,.034) 52%,
    rgba(255,255,255,.012) 68%,
    rgba(255,255,255,0) 85%)}
.ajx .card:hover::before{opacity:1; transition:opacity .26s ease}
.ajx .card img{width:100%; height:100%; object-fit:cover; display:block; margin:0}
.ajx .card::after{content:""; position:absolute; inset:0; background:linear-gradient(180deg, transparent 46%, rgba(10,12,14,.86) 100%)}
.ajx .card figcaption{position:absolute; left:0; right:0; bottom:0; z-index:2; padding:16px 18px; text-align:left; color:#fff}
.ajx .card .t{font-family:var(--serif); font-size:22px; line-height:1.15; display:block; color:#fff}
.ajx .card .r{font-size:10px; letter-spacing:.2em; text-transform:uppercase; color:rgba(255,255,255,.78); margin-top:5px; display:block}
.ajx .card.hidden{display:none}
/* The reflection: a flipped clone of the card image that JS appends (carousel only,
   so the no-JS / safe-mode grid never sees it). No blur, and the caption is stripped
   out of the clone - a mirrored copy of the title read as clutter. The mask runs
   transparent -> opaque because scaleY(-1) flips it along with the content: the LAST
   stop is the edge that touches the card, the FIRST is the far end. ::after repeats
   the card's own bottom gradient so the two meet on the same tone. */
.ajx .mirror{display:none; position:absolute; left:0; top:100%; width:100%; height:100%;
  margin-top:8px; z-index:0; pointer-events:none; overflow:hidden; border-radius:10px;
  transform:scaleY(-1); opacity:.4;
  -webkit-mask-image:linear-gradient(to bottom, rgba(0,0,0,0) 58%, rgba(0,0,0,.30) 74%, rgba(0,0,0,.70) 88%, rgba(0,0,0,1) 100%);
          mask-image:linear-gradient(to bottom, rgba(0,0,0,0) 58%, rgba(0,0,0,.30) 74%, rgba(0,0,0,.70) 88%, rgba(0,0,0,1) 100%)}
.ajx .mirror::after{content:""; position:absolute; inset:0;
  background:linear-gradient(180deg, transparent 46%, rgba(10,12,14,.86) 100%)}
/* nav + hint are meaningless until the carousel is running */
.ajx .nav, .ajx .hint{display:none}

/* ENHANCED STATE = the 3D carousel. Phones get it too (swipe-driven); only very
   narrow viewports fall back to the plain stack. The JS breakpoint must match. */
@media(min-width:360px){
  .ajx .stage-wrap.is-live{--cw:clamp(320px,40vw,580px)}
  .ajx .stage-wrap.is-live .stage{display:block; position:relative; height:clamp(320px,50vh,470px);
    max-width:none; margin-top:clamp(14px,2vh,22px); padding:0; perspective:1800px;
    touch-action:pan-y; cursor:grab; user-select:none}
  /* While a finger/cursor is down the deck must track it 1:1. Leaving the .6s
     transition on makes every pointermove ease toward its target instead, which
     is what made the swipe feel laggy and rubbery. */
  .ajx .stage-wrap.is-live .stage.dragging{cursor:grabbing}
  .ajx .stage-wrap.is-live .stage.dragging .card{transition:none}
  /* NO transform transition: the rAF loop writes the transform every frame, and a
     CSS transition on top of that would lag one easing behind it. box-shadow is
     left out of the transition too - it is the one property here that cannot be
     composited, so animating an 84px blurred shadow repaints the card each frame. */
  .ajx .stage-wrap.is-live .card{position:absolute; top:50%; left:50%; width:var(--cw);
    transform-style:preserve-3d; backface-visibility:hidden; will-change:transform;
    /* tilt eases a little slower than the highlight so the card feels weighted */
    transition:opacity .45s ease,
               --hx .34s cubic-bezier(.22,.61,.36,1), --hy .34s cubic-bezier(.22,.61,.36,1),
               --mx .22s ease-out, --my .22s ease-out}
  /* The card has to stop clipping for the reflection (which hangs below it) to show,
     so the rounding moves onto the layers that were relying on that clip. */
  .ajx .stage-wrap.is-live .card{overflow:visible}
  .ajx .stage-wrap.is-live .card > img,
  .ajx .stage-wrap.is-live .card::before,
  .ajx .stage-wrap.is-live .card::after{border-radius:10px}
  .ajx .stage-wrap.is-live .card .mirror{display:block}
  .ajx .stage-wrap.is-live .card.side{opacity:.62}
  .ajx .stage-wrap.is-live .card.active{box-shadow:0 36px 84px rgba(0,0,0,.72), 0 6px 20px rgba(0,0,0,.5)}
  .ajx .stage-wrap.is-live .card.hidden{display:block; opacity:0; pointer-events:none}
  .ajx .stage-wrap.is-live .card .t{font-size:25px}
  /* arrows sit level with the middle of the cards, flanking the deck */
  .ajx .stage-wrap.is-live .nav{display:grid; place-items:center; position:absolute; top:50%;
    transform:translateY(-50%); width:42px; height:42px; z-index:130}
  /* the 21px is half the button, so it still straddles the outer card's edge */
  .ajx .stage-wrap.is-live .nav.prev{left:max(10px, calc(50% - (var(--cw) * 1.105) - 21px))}
  .ajx .stage-wrap.is-live .nav.next{right:max(10px, calc(50% - (var(--cw) * 1.105) - 21px))}
  /* clear the reflection so the hint does not sit on top of it */
  .ajx .stage-wrap.is-live ~ .hint{display:block; margin-top:clamp(58px,7vh,92px)}
}

/* Phone tuning for that same deck. The arrows move OUT of the card row (48px
   buttons flanking a near-full-width card would sit on top of the artwork) and
   into their own row below the deck, with the hint under them. */
@media(min-width:360px) and (max-width:700px){
  .ajx .stage-wrap.is-live{--cw:min(80vw, 380px)}
  .ajx .stage-wrap.is-live .stage{height:clamp(238px,34vh,330px); margin-top:clamp(12px,1.8vh,20px)}
  .ajx .stage-wrap.is-live .card .t{font-size:19px}
  .ajx .stage-wrap.is-live .card figcaption{padding:13px 14px}
  .ajx .stage-wrap.is-live .card.active{box-shadow:0 22px 52px rgba(0,0,0,.7), 0 4px 14px rgba(0,0,0,.5)}
  /* level with the cards, pinned to the screen edges: the deck-anchored desktop
     offset resolves past the viewport at this width, so it would clamp here anyway */
  .ajx .stage-wrap.is-live .nav{display:grid; width:37px; height:37px; font-size:14px}
  .ajx .stage-wrap.is-live .nav.prev{left:8px; right:auto}
  .ajx .stage-wrap.is-live .nav.next{right:8px; left:auto}
  .ajx .stage-wrap.is-live .nav:active{background:var(--ink); color:var(--paper)}
  .ajx .stage-wrap.is-live ~ .hint{margin-top:clamp(46px,6vh,68px)}
  .ajx .deck{padding-bottom:clamp(34px,5vh,54px)}
}
/* the keyboard half of the hint is meaningless on a phone */
.ajx .hint .h-touch{display:none}
@media(max-width:700px){
  .ajx .hint .h-keys{display:none}
  .ajx .hint .h-touch{display:inline}
}

/* The arrows sit quiet until you reach for them: faint by default, half-up when the
   deck is hovered, solid on hover/focus. Touch has no hover, so on a phone they stay
   at the faint resting state, which is the intent there too. */
.ajx .nav{border-radius:50%; border:1px solid rgba(255,255,255,.18);
  background:rgba(255,255,255,.09); backdrop-filter:blur(10px); -webkit-backdrop-filter:blur(10px);
  color:var(--ink); cursor:pointer; font-size:16px; line-height:1; padding:0;
  box-shadow:0 6px 20px rgba(0,0,0,.45); opacity:.38;
  transition:opacity .28s ease, background .22s ease, color .22s ease, border-color .22s ease}
.ajx .stage-wrap:hover .nav{opacity:.72}
.ajx .nav:hover, .ajx .nav:focus-visible{opacity:1; background:var(--ink); color:var(--paper); border-color:var(--ink)}
.ajx .counter{font-family:var(--serif); font-size:15px; color:var(--muted); font-variant-numeric:tabular-nums; margin-right:12px}
/* Squarespace resets text-align on <p>, so this needs !important to stay centred */
.ajx .hint{text-align:center !important; width:100%; margin:16px 0 0;
  font-size:10px; letter-spacing:.24em; text-transform:uppercase; color:var(--faint)}

/* ---- statement: ONE horizontal accordion ----------------------------------
   The copy is the first panel in the strip, not a separate column. Opening a
   discipline collapses the copy to a narrow tab and vice versa, so the whole
   section is a single row of four panels that redistribute width. Panels run the
   full height of the section and carry no frame - the gutters are the section
   colour showing through. */
.ajx .rule{height:1px; background:var(--line); margin:0}
/* full-bleed: the row runs the whole viewport width, inset by the same 10px that
   separates the panels from each other so the gutter reads as one consistent frame */
.ajx .statement{width:100%; margin:0; padding:10px;
  /* taller than it was: the showcase went from two rows of cards to three, and the
     rows are sized from the available height, so the section has to give them room */
  min-height:clamp(560px,76vh,840px); display:flex; align-items:stretch;
  /* one inset shared by the overview copy and the discipline copy, so whichever
     panel is open its text lands on the same vertical axis */
  --copyInset:clamp(34px,5vw,104px)}
.ajx .disc-tabs{flex:1 1 auto; display:flex; gap:10px; min-width:0}

/* flex-grow is what animates: every panel keeps a 110px basis and the open one
   takes all the free space, so the four widths always add up to the row.
   A discipline is a WRAPPER, not just the button: the button is the clickable face
   and the project strip is its SIBLING, because real <a> links may not be nested
   inside a <button>. So the wrapper is what flexes, and the button fills it. */
.ajx .disc{position:relative; flex:0 1 110px; min-width:0; overflow:hidden;
  border-radius:12px; background:var(--paper-2);
  transition:flex-grow .62s cubic-bezier(.22,.8,.26,1)}
.ajx .disc:has(.disc-tab[aria-expanded="true"]){flex-grow:1}
/* These are landscape crops in portrait panels, so they are deliberately held back
   to read as texture rather than as a picture with half a word in it. */
/* The three images have very different native luminance - the garden render is
   nearly black, the Nike layout is nearly white - so ONE opacity cannot serve all
   three: the value that lifts the garden blows the Nike panel out into a glaring
   block on a dark page. --imgA / --imgAOpen are that knob per panel. RETUNE THEM
   IF AN IMAGE IS SWAPPED; the defaults below suit a mid-dark photograph.
   These sit on .disc, NOT on .disc-tab - a value declared directly on the button
   would beat the .t-* override inherited from the wrapper.
   The open values went back UP once the glass went in - see .disc-tab::after. */
.ajx .disc{--imgA:.22; --imgAOpen:.50;
  --dc1:hsl(262 52% 63%); --dc2:hsl(294 47% 75%)}
.ajx .disc.t-design{--imgA:.15; --imgAOpen:.25; --dc1:hsl(272 52% 62%); --dc2:hsl(304 47% 74%)}
/* Film stays the lowest of the three: its still is a high-contrast billboard covered
   in large type, and it duplicates the first card in its own strip. */
.ajx .disc.t-film{--imgA:.20; --imgAOpen:.30; --dc1:hsl(196 56% 58%); --dc2:hsl(222 52% 72%)}
.ajx .disc.t-3d{--imgA:.24; --imgAOpen:.42;   --dc1:hsl(116 42% 56%); --dc2:hsl(76 48% 66%)}

/* ---- the ambient wash, same material as the top of the page ----
   The panels used to take ALL their colour from the discipline's photograph, and a
   photograph is mostly dark - so this section read far darker and far less coloured
   than the hero above it, which gets its colour from a saturated gradient. This is
   that same gradient: identical three-radial structure and the same load-bearing
   blur. It sits UNDER the button, so the button's ::after glass blurs the wash and
   the image together as one backdrop, and the image opacities came down to let the
   colour through - the wash is the colour source now, the photo is texture on top.
   KEEP THE BLUR. Same reason as .ambient: it is what dithers these wide gradients,
   and without it they band on the lighter pairs. */
.ajx .disc-wash{position:absolute; inset:-22%; z-index:0; pointer-events:none;
  background:
    radial-gradient(52% 44% at 20% 14%, var(--dc1) 0%, transparent 70%),
    radial-gradient(50% 46% at 82% 30%, var(--dc2) 0%, transparent 72%),
    radial-gradient(72% 58% at 50% 66%, var(--dc1) 0%, transparent 74%);
  filter:blur(34px) saturate(1.15); opacity:.5;
  transition:opacity .6s ease}
.ajx .disc:has(.disc-tab[aria-expanded="true"]) .disc-wash{opacity:.95}
.ajx .disc-tab{position:absolute; inset:0; width:100%; height:100%; overflow:hidden;
  appearance:none; -webkit-appearance:none; border:0; padding:0; margin:0;
  border-radius:12px; background:none; cursor:pointer;
  font-family:var(--body); color:var(--ink); text-align:left}
.ajx .disc-tab[aria-expanded="true"]{cursor:default}
.ajx .disc-tab img{position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
  opacity:var(--imgA); transform:scale(1.06); filter:saturate(1.1);
  transition:opacity .6s ease, transform .6s ease}
.ajx .disc-tab[aria-expanded="true"] img{opacity:var(--imgAOpen); transform:scale(1)}
.ajx .disc-tab:hover img{opacity:calc(var(--imgA) + .12)}
.ajx .disc-tab[aria-expanded="true"]:hover img{opacity:var(--imgAOpen)}
/* Collapsed: a near opaque floor under the vertical label, which is what a narrow
   panel needs. Open: that same floor swallowed the whole image once the panel was
   an order of magnitude wider, so the weight moves to a HORIZONTAL wash that stays
   dark only across the band the copy occupies and clears to the right.
   The two stacks are deliberately IDENTICAL IN STRUCTURE - same layer count, same
   directions, same stop positions, only the colours differ - because CSS can only
   interpolate gradients that match that way. Mismatched stacks swap discretely at
   50% progress, which pops halfway through the panel opening.
   The first stop is min(px,%) so a phone-width panel does not end up wall-to-wall
   dark once the fixed 440px band is wider than the panel itself. */
/* This layer is also the GLASS: backdrop-filter blurs what is painted BEHIND it,
   which inside the button is exactly the background image and nothing else. The
   project strip is a SIBLING at a higher z-index, so it paints afterwards and stays
   perfectly sharp - the cards sit on frosted glass rather than on a photograph
   competing with them. Blurring away the detail is also what let the image opacities
   go back UP: it is the busy detail that fought the cards, not the brightness.
   The recipe is deliberately the SAME as .glass at the top of the page - blur 58px,
   saturate 1.35 - so the two halves of the site read as one material. Up there the
   colour under the glass is the .ambient gradient wash; here it is the discipline's
   own background image, blurred past legibility into the same kind of colour field. */
.ajx .disc-tab::after{content:""; position:absolute; inset:0; z-index:1;
  -webkit-backdrop-filter:blur(58px) saturate(1.35);
          backdrop-filter:blur(58px) saturate(1.35);
  background:
    linear-gradient(90deg, rgba(10,10,12,.10) 0px, rgba(10,10,12,.10) min(440px,52%), rgba(10,10,12,.10) 78%, rgba(10,10,12,.10) 100%),
    linear-gradient(180deg, rgba(10,10,12,.24) 0%, rgba(10,10,12,.80) 32%, rgba(10,10,12,.92) 64%, rgba(10,10,12,.97) 100%);
  transition:background .62s cubic-bezier(.22,.8,.26,1)}
/* Open, the tint flattens out towards the top of the page's .62: with the image
   blurred to a colour field there is no longer any DETAIL to suppress, so a heavy
   directional scrim would only be crushing the colour the glass exists to show. It
   stays a little denser on the left, where the heading sits over it. */
.ajx .disc-tab[aria-expanded="true"]::after{
  background:
    linear-gradient(90deg, rgba(10,10,12,.68) 0px, rgba(10,10,12,.62) min(440px,52%), rgba(10,10,12,.54) 78%, rgba(10,10,12,.50) 100%),
    linear-gradient(180deg, rgba(10,10,12,.10) 0%, rgba(10,10,12,0) 32%, rgba(10,10,12,0) 64%, rgba(10,10,12,.16) 100%)}
/* same fallback the top of the page uses: no backdrop-filter means no blur, so the
   tint has to carry it alone or the raw photograph shows through undimmed */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .ajx .disc-tab[aria-expanded="true"]::after{
    background:linear-gradient(90deg, rgba(10,10,12,.90) 0%, rgba(10,10,12,.82) 100%)}
}
.ajx .disc-tab .in{position:absolute; inset:0; z-index:2; display:flex; flex-direction:column;
  justify-content:flex-end; padding:18px}
/* open: the copy moves off the corner - inset from the left on the shared axis and
   sitting just below the vertical middle. Pinned to the floor at 18px it read as a
   caption stuck to the bottom of a very large panel. */
.ajx .disc-tab[aria-expanded="true"] .in{justify-content:center;
  padding:clamp(56px,11vh,132px) var(--copyInset) clamp(30px,5vh,66px)}
.ajx .tab-label{font-size:10.5px; letter-spacing:.24em; text-transform:uppercase; color:var(--ink); white-space:nowrap}
/* collapsed: the label reads bottom-to-top up the narrow panel */
.ajx .disc-tab[aria-expanded="false"] .tab-label{writing-mode:vertical-rl; transform:rotate(180deg)}
.ajx .tab-copy{display:block; font-size:14.5px; line-height:1.55; color:var(--ink-2);
  max-width:32ch; max-height:0; opacity:0; overflow:hidden;
  transition:max-height .6s cubic-bezier(.22,.8,.26,1), opacity .3s ease, margin .6s ease}
.ajx .disc-tab[aria-expanded="true"] .tab-copy{max-height:220px; opacity:1; margin-top:12px; transition-delay:0s,.18s,0s}

/* ---- the category showcase ----
   The projects tagged with this discipline, laid out on the right of the panel as it
   rolls out: a REGULAR GRID - rows and columns in line - skewed as a single plane in
   perspective. Three things worth knowing:
   (1) pointer-events are OFF on the containers and back ON per card, so the gaps
       between cards still toggle the panel and only a card itself takes a click;
   (2) VISIBILITY, not just opacity, is what keeps the links out of the tab order
       while the panel is closed - an opacity-0 link is still focusable;
   (3) it takes TWO elements, and that is not incidental. `overflow`, `mask` and
       `opacity` all force transform-style back to FLAT, so the element that clips and
       fades cannot also be the element that holds a 3D plane - the cards' own hover
       tilt would be flattened into it and read as a skew. So .proj-strip clips, fades
       and owns the perspective; .proj-plane holds the rotation and preserve-3d. */
.ajx .proj-strip{position:absolute; z-index:3; top:0; bottom:0; right:0;
  left:clamp(300px,28%,500px); overflow:hidden; pointer-events:none;
  opacity:0; visibility:hidden; transition:opacity .34s ease, visibility 0s linear .34s}
.ajx .disc:has(.disc-tab[aria-expanded="true"]) .proj-strip{opacity:1; visibility:visible;
  transition:opacity .46s ease .2s, visibility 0s}
.ajx .proj-plane{position:absolute; inset:0; display:grid; align-content:center;
  pointer-events:none}
/* 16/10, matching the deck cards, because that is the native ratio of every source
   image. A portrait frame was tried first and it crops these landscape stills into
   fragments - the Spotify card literally read "DERGRO". Same trap the panel
   backgrounds fell into; do not put these images in a portrait box. */
/* No cursor tilt and no zoom on these - AJ asked for both off, so the only thing the
   transform still carries is the reveal rise. Hover is a shadow and a small lift in
   image opacity, nothing that moves the card. */
.ajx .proj{--rise:26px;
  position:relative; aspect-ratio:16/10;
  border-radius:10px; overflow:hidden; background:var(--paper); pointer-events:auto;
  text-decoration:none; color:var(--ink); box-shadow:0 22px 46px rgba(0,0,0,.62);
  transform:translateY(var(--rise));
  transition:--rise .58s cubic-bezier(.22,.8,.26,1), box-shadow .3s ease}
.ajx .disc:has(.disc-tab[aria-expanded="true"]) .proj{--rise:0px}
/* height-driven: the grid row sets the height and the aspect ratio derives the width,
   so a third row can never push the block out of the panel */
@media(min-width:821px){ .ajx .proj{height:100%; width:auto} }
.ajx .proj img{position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
  opacity:.94; transition:opacity .3s ease}
.ajx .proj .pt{position:absolute; left:0; right:0; bottom:0; z-index:1;
  padding:26px 10px 9px; font-size:10px; line-height:1.3; color:var(--ink);
  background:linear-gradient(180deg, rgba(10,10,12,0), rgba(10,10,12,.9) 62%)}
.ajx .proj:hover img{opacity:1}
.ajx .proj:focus-visible{outline:2px solid var(--ink); outline-offset:3px}

/* ---- the skewed grid (desktop only) ----
   AJ's brief: "imagine we had them all flat and then skewed in perspective". So the
   cards sit in a plain three column grid - rows and columns in direct line, no
   stagger, no overlap - and the WHOLE PLANE takes one rotation. A per-card scatter
   was built first and rejected: angling each card separately reads as a pile, not a
   sheet, and the grid version needs no hand-composed slots per category, so five
   cards and six cards lay themselves out.
   transform-origin sits on the right edge, so rotating swings the LEFT side away
   from the viewer - that is the receding direction, and the mask fades the same way.
   Three columns rather than two: the region is wide and short (about 2:1), so two
   columns would make each card half the width and three rows tall enough to overflow
   the panel. At three, six cards are 3x2 and the block still clears the height. */
@media(min-width:821px){
  /* A short perspective distance is what makes the cards KEYSTONE - each one drawn as
     a trapezoid, far edge shorter than near edge - instead of merely getting smaller
     towards the left. At 1050px the keystone across a 220px card was about 7%, which
     the eye reads as a flat scale, and the images looked pasted on rather than lying
     on the plane. 620px roughly doubles it. */
  .ajx .proj-strip{perspective:620px; perspective-origin:14% 50%}
  /* NOTE: the left falloff is a dark gradient painted OVER the grid, not a mask.
     A mask makes the far cards TRANSLUCENT, so the panel's background image shows
     through them and each card visibly dissolves into itself. Distance should read
     as cards receding into the dark, so this paints toward the panel colour instead
     and leaves every card fully opaque. */
  .ajx .proj-strip::after{content:""; position:absolute; inset:0; z-index:2;
    pointer-events:none;
    background:linear-gradient(90deg, rgba(10,10,12,.94) 0%, rgba(10,10,12,.80) 16%,
      rgba(10,10,12,.46) 36%, rgba(10,10,12,.14) 54%, rgba(10,10,12,0) 68%)}
  /* Dense, not spread. Capping the columns and spreading them with
     justify-content:space-between was tried so the grid would reach further left -
     it reads as three separate pairs, not a grid. Card size and how far left the
     block starts are the same knob (the grid is right-anchored, so its left edge is
     just right-minus-width): with only six cards, small cards mean open space beside
     the copy, and that space is the dark field the heading sits on. */
  /* Three rows of three. The rows are `1fr` and the cards take their HEIGHT from the
     row, deriving width from the aspect ratio - the reverse of the usual way round,
     and the reason nine cards can never overflow the panel however short it gets.
     Column width follows from that, so the columns are `auto` and the block is
     right-anchored with justify-content. Padding is generous and equal on all four
     sides so the grid sits inside a real margin rather than against the edges. */
  /* Rows are a THIRD OF THE HEIGHT each, not 1fr. With 1fr a category holding one row
     stretched that row to the full panel and its cards came out 845px wide against
     Design's 264 - the card size tracked the row count instead of being constant.
     Pinning the row to a third makes every section's cards identical, and align-content
     centres the shorter grids in the space. */
  .ajx .proj-plane{z-index:1; --rgap:clamp(10px,1.1vw,17px);
    grid-template-columns:repeat(3, auto);
    grid-auto-rows:calc((100% - 2 * var(--rgap)) / 3);
    justify-content:end; align-content:center; gap:var(--rgap);
    padding:clamp(24px,4vh,56px) clamp(24px,2.6vw,60px);
    transform-style:preserve-3d; transform-origin:100% 50%;
    /* no rotateZ - the rows read straight, the depth is all in the Y rotation */
    transform:rotateY(-20deg)}
  .ajx .proj:hover{box-shadow:0 34px 68px rgba(0,0,0,.74)}
}
.ajx .disc:has(.disc-tab[aria-expanded="true"]) .proj:nth-child(1){transition-delay:.20s}
.ajx .disc:has(.disc-tab[aria-expanded="true"]) .proj:nth-child(2){transition-delay:.26s}
.ajx .disc:has(.disc-tab[aria-expanded="true"]) .proj:nth-child(3){transition-delay:.32s}
.ajx .disc:has(.disc-tab[aria-expanded="true"]) .proj:nth-child(4){transition-delay:.38s}
.ajx .disc:has(.disc-tab[aria-expanded="true"]) .proj:nth-child(5){transition-delay:.44s}
.ajx .disc:has(.disc-tab[aria-expanded="true"]) .proj:nth-child(6){transition-delay:.50s}
/* Below this the panel cannot carry the copy AND a cluster side by side - the region
   drops under 300px - so the showcase stands down and the deck above carries the
   work. Under 820 the mobile rules take over and every project is back. */
@media(min-width:821px) and (max-width:1059px){ .ajx .proj-strip{display:none} }
@media(prefers-reduced-motion: reduce){
  .ajx .proj{--rise:0px; --rz:0deg; --ry:0deg; transition:none}
}

/* ---- the copy panel, first in the strip ---- */
.ajx .say{position:relative; flex:0 1 110px; min-width:0; overflow:hidden;
  border-radius:12px; background:var(--paper-2);
  transition:flex-grow .62s cubic-bezier(.22,.8,.26,1)}
.ajx .say[data-open="true"]{flex-grow:1.25}
/* the whole collapsed panel IS the button back to the copy; when open it is
   hidden so the real heading underneath is what people read */
.ajx .say-toggle{position:absolute; inset:0; z-index:3;
  appearance:none; -webkit-appearance:none; border:0; margin:0; background:none;
  display:flex; align-items:flex-end; padding:18px; cursor:pointer;
  font-family:var(--body); color:var(--ink); opacity:1; transition:opacity .3s ease}
.ajx .say[data-open="true"] .say-toggle{opacity:0; pointer-events:none}
.ajx .say-toggle .tab-label{writing-mode:vertical-rl; transform:rotate(180deg)}
.ajx .say-copy{position:absolute; inset:0; z-index:2; display:flex; flex-direction:column;
  justify-content:center; gap:20px; padding:clamp(26px,3.4vw,52px) var(--copyInset);
  opacity:0; pointer-events:none; transition:opacity .32s ease}
.ajx .say[data-open="true"] .say-copy{opacity:1; pointer-events:auto; transition-delay:.2s}
.ajx .say-copy h2{font-family:var(--serif); font-weight:300; font-size:clamp(24px,2.6vw,42px);
  line-height:1.16; margin:0; color:var(--ink); white-space:normal; max-width:24ch}
.ajx .say-copy h2 em{font-style:italic}
.ajx .say-copy .lede{color:var(--ink-2); margin:0; max-width:46ch}

@media(max-width:820px){
  /* back into normal flow on a phone: copy on top, panels stacked under it */
  .ajx .statement{display:block; min-height:0; padding:clamp(44px,7vh,72px) 30px}
  .ajx .disc-tabs{flex-direction:column; width:auto}
  .ajx .disc{flex:0 0 auto; width:auto; height:78px;
    transition:height .55s cubic-bezier(.22,.8,.26,1)}
  .ajx .disc:has(.disc-tab[aria-expanded="true"]){height:420px}
  /* the phone panel is short and wide, so the copy stays on its floor - centring it
     in 330px just floats it. The brighter image and lighter scrim still apply. */
  .ajx .disc-tab[aria-expanded="true"] .in{justify-content:flex-end; padding:18px}
  /* the copy holds the floor here, so the showcase takes the top band instead of
     the right half, and starts at the left because there is nothing to clear.
     A phone gets the same three column grid but flat on the glass - no skew, no
     mask - because at this size a receding plane just makes half the cards
     unreadable. The desktop rules are scoped to min-width:821px, so nothing here
     needs undoing beyond the row height. */
  .ajx .proj-strip{top:14px; bottom:auto; left:14px; right:14px; height:246px}
  .ajx .proj-plane{grid-template-columns:repeat(3, 1fr); gap:9px; padding:0}
  .ajx .disc-tab[aria-expanded="false"] .tab-label{writing-mode:horizontal-tb; transform:none}
  /* the copy is never a tab on mobile - it just sits above the stack */
  .ajx .say{flex:0 0 auto; background:transparent; border-radius:0;
    height:auto; overflow:visible; margin-bottom:16px}
  .ajx .say-toggle{display:none}
  .ajx .say-copy{position:static; opacity:1 !important; pointer-events:auto;
    padding:0; gap:18px}
}

/* ---- TEMPORARILY HIDDEN 09-03 ----
   AJ asked for the discipline strip and the contact section to be hidden "for now".
   The markup and all their CSS are left completely intact - this one rule is the only
   thing switching them off, so restoring them is deleting this block and nothing else.
   .rule goes too: it is the divider that introduced the strip, and on its own it reads
   as a stray hairline above the footer. */
.ajx .rule,
.ajx .statement,
.ajx .contact{display:none}

/* ---- contact ---- */
.ajx .contact{text-align:center; padding:clamp(58px,9vh,104px) 30px; background:var(--paper-2); border-top:1px solid var(--line)}
.ajx .contact h2{font-family:var(--serif); font-weight:300; font-size:clamp(32px,5.4vw,68px); line-height:1.08; margin:0 0 14px; color:var(--ink)}
.ajx .contact h2 em{font-style:italic}
.ajx .contact p{color:var(--muted); max-width:46ch; margin:0 auto}
.ajx .contact .mail{display:inline-block; margin-top:22px; font-family:var(--serif); font-size:clamp(19px,2.3vw,26px);
  border-bottom:1px solid var(--faint); padding-bottom:5px; transition:border-color .25s}
.ajx .contact .mail:hover{border-color:var(--ink)}

@media (prefers-reduced-motion: reduce){
  .ajx .stage-wrap.is-live .card{transition:none}
}

/* ---- type roles (AJ, 09-10): the display face for the headline, card titles and
   statements; the UI face for controls and labels; the body face for copy. The display
   face has one weight and no italic, so nothing here asks it for bold or slant. ---- */
/* the headline fills its line (AJ: "a bit larger to fill the space more"): the whole line
   is 8.9em wide and the intro keeps 26px either side, so (100vw - 76px) / 9.1 fills the
   width on one line, with room for the glass's edges and a desktop scrollbar */
.ajx .intro h1{font-family:var(--ff-display); font-weight:400; font-variation-settings:normal;
  font-size:clamp(28px, calc((100vw - 76px) / 9.1), 66px); letter-spacing:.005em; line-height:1}
.ajx .card .t{font-family:var(--ff-display); font-size:24px; line-height:1.05;
  letter-spacing:.01em; text-transform:uppercase}
.ajx .say-copy h2, .ajx .contact h2{font-family:var(--ff-display); font-weight:400;
  text-transform:uppercase; line-height:1.04}
.ajx .say-copy h2 em, .ajx .contact h2 em{font-style:normal}
.ajx .filters button, .ajx .disc-tab, .ajx .say-toggle, .ajx .counter, .ajx .contact .mail,
.ajx .hint, .ajx .deck-head .eyebrow, .ajx .card .r{font-family:var(--ff-ui)}
.ajx .filters button{font-weight:600}
/* on a phone, slimmer pills keep all three on one row */
@media (max-width:420px){ .ajx .filters{gap:6px} .ajx .filters button{padding:9px 13px; letter-spacing:.14em} }

/* Design and Film pills lead to their pages: links, dressed exactly as the filter pill */
.ajx .filters .pill-link{font-family:var(--ff-ui); font-weight:600; font-size:10.5px; letter-spacing:.2em;
  text-transform:uppercase; color:var(--muted); text-decoration:none; background:rgba(255,255,255,.07);
  -webkit-backdrop-filter:blur(8px); backdrop-filter:blur(8px); border:1px solid rgba(255,255,255,.16);
  border-radius:999px; padding:9px 20px; transition:all .25s}
.ajx .filters .pill-link:hover{color:var(--ink); border-color:rgba(255,255,255,.38); background:rgba(255,255,255,.14)}
.ajx .filters .pill-link:focus-visible{outline:2px solid hsl(38 96% 55%); outline-offset:3px}
@media (max-width:420px){ .ajx .filters .pill-link{padding:9px 13px; letter-spacing:.14em} }
/* phones: no label under 11px */
@media (max-width:767px){ .ajx .filters button, .ajx .filters .pill-link, .ajx .card .r,
  .ajx .hint, .ajx .hint *{font-size:11px} }
/* the header now sits in the flow, so the intro no longer clears Squarespace's overlay */
.ajx .intro{padding-top:clamp(28px,5vh,56px)}

/* "Multidisciplinary" set thin (AJ: "have this be thinner") in the extra-condensed cut of
   the Sofia Sans family - the display face has only one, heavy weight - and small (AJ:
   "make multidisciplinary smaller"), so the turning glass word carries the line */
.ajx .intro h1 .h1-thin{font-family:'Sofia Sans Extra Condensed',var(--ff-ui); font-weight:400;
  font-size:.62em; letter-spacing:.06em; margin-right:.1em}
/* the headline's last word turns over (designer / filmmaker / artist / director / editor) */
.ajx .intro h1 .rot{display:inline-grid; text-align:left; vertical-align:baseline}
.ajx .intro h1 .rot-w{grid-area:1/1; opacity:0; transform:translateY(.45em);
  transition:opacity .5s ease, transform .6s cubic-bezier(.2,.7,.2,1)}
.ajx .intro h1 .rot-w.is-on{opacity:1; transform:none}
.ajx .intro h1 .rot-w.is-out{opacity:0; transform:translateY(-.45em)}
@media (prefers-reduced-motion:reduce){ .ajx .intro h1 .rot-w{transition:none} }

.ajx .filters{margin-top:clamp(6px,1.2vh,14px)}
@media(min-width:360px){ .ajx .stage-wrap.is-live .stage{margin-top:clamp(34px,5vh,60px)} }
@media(min-width:360px) and (max-width:700px){ .ajx .stage-wrap.is-live .stage{margin-top:clamp(24px,3.2vh,36px)} }

.ajx .intro h1{--sx:32%}
/* only the glass word leans toward the pointer; "Multidisciplinary" stays still */
.ajx .intro h1 .rot{transform:perspective(900px) rotateX(var(--gx,0deg)) rotateY(var(--gy,0deg))}
.ajx .intro h1 .rot-w{
  color:transparent;
  background:
    linear-gradient(100deg, rgb(255 255 255 / .16) calc(var(--sx) - 26%), rgb(255 255 255 / .95) var(--sx),
      rgb(255 255 255 / .16) calc(var(--sx) + 26%)),
    linear-gradient(180deg, rgb(225 232 255 / .72) 0%, rgb(150 160 220 / .3) 52%, rgb(255 255 255 / .56) 100%);
  -webkit-background-clip:text; background-clip:text;
  -webkit-text-stroke:1px rgb(255 255 255 / .6);
  filter:drop-shadow(0 1px 0 rgb(170 180 240 / .5)) drop-shadow(0 1px 0 rgb(130 140 210 / .42))
    drop-shadow(0 1px 0 rgb(95 105 180 / .36)) drop-shadow(0 2px 0 rgb(60 66 125 / .32))
    drop-shadow(0 16px 24px rgb(0 0 0 / .45))}
.ajx .intro h1 .h1-thin{color:#fff}                /* AJ: "make this static white" - no glass, no shine */
/* the turning word: the shine across its faces softened, the reflection on its edges */
.ajx .intro h1 .rot-w{
  background:
    linear-gradient(100deg, rgb(255 255 255 / .1) calc(var(--sx) - 20%), rgb(255 255 255 / .28) var(--sx),
      rgb(255 255 255 / .1) calc(var(--sx) + 20%)),                /* AJ: "less shine" */
    linear-gradient(180deg, rgb(225 232 255 / .66) 0%, rgb(150 160 220 / .26) 52%, rgb(255 255 255 / .5) 100%);
  -webkit-background-clip:text; background-clip:text;
  -webkit-text-stroke-color:rgb(255 255 255 / .78);
  filter:url(#aj-glass) drop-shadow(0 1px 0 rgb(170 180 240 / .5)) drop-shadow(0 1px 0 rgb(130 140 210 / .42))
    drop-shadow(0 1px 0 rgb(95 105 180 / .36)) drop-shadow(0 2px 0 rgb(60 66 125 / .32))
    drop-shadow(0 16px 24px rgb(0 0 0 / .45))}
/* Apple's WebKit (Safari, and every browser on an iPhone) can stop repainting a word drawn
   through an SVG filter once its opacity starts changing, so the turning word froze on
   phones (AJ: "this isn't cycling on mobile"). There the word keeps its glass - fill,
   stroke, depth - without the SVG edge glints; home.js adds .glass-lite. */
.ajx .intro h1.glass-lite .rot-w{
  filter:drop-shadow(0 1px 0 rgb(170 180 240 / .5)) drop-shadow(0 1px 0 rgb(130 140 210 / .42))
    drop-shadow(0 1px 0 rgb(95 105 180 / .36)) drop-shadow(0 2px 0 rgb(60 66 125 / .32))
    drop-shadow(0 16px 24px rgb(0 0 0 / .45))}
/* the incoming word waits for the outgoing one to lift away, so the two never overlap */
.ajx .intro h1 .rot-w.is-on{transition-delay:.3s}
@media (prefers-reduced-motion:reduce){ .ajx .intro h1 .rot{transform:none} }
