/* Influencer Marketing Examples - the endless board.
 *
 * Light paper, one pinned title, and a field of collab tiles that runs in every
 * direction behind it. Fraunces for display, Newsreader for reading, Source Code Pro for
 * the small labels.
 *
 * The page itself never scrolls. The board is a single transformed layer and the browser's
 * scrollbars are gone on purpose, because two scroll models on one screen fight each other.
 */

:root{
  --bg:#efece6;            /* warm paper, so white tiles read as objects on it */
  --surface:#ffffff;
  /* Warm near blacks rather than cool greys. On a cream ground a blue grey text colour
     reads as slightly dirty; the same value warmed up reads as ink. */
  --ink:#241f1a;
  --body:#4a423a;
  --head:#17120e;
  --mut:#8a7f73;
  --accent:#1b3a6b;
  --accent-hi:#2f5fd0;
  --hairline:rgba(35,28,20,.13);
  --shadow:0 1px 2px rgba(16,24,40,.05),0 8px 24px rgba(16,24,40,.08);
  --shadow-hi:0 2px 6px rgba(16,24,40,.08),0 18px 46px rgba(16,24,40,.16);
  --display:'Fraunces',Georgia,serif;
  --serif:'Newsreader',Georgia,serif;
  --mono:'Source Code Pro','Courier New',monospace;
}

*{box-sizing:border-box}
html,body{height:100%}
body{
  margin:0;overflow:hidden;background:var(--bg);color:var(--body);
  font:400 17px/1.6 var(--serif);
  -webkit-font-smoothing:antialiased;
  overscroll-behavior:none;
}
a{color:var(--accent);text-decoration:none}
a:hover{color:var(--accent-hi);text-decoration:underline}

/* ---------- the board ---------- */
#board{
  position:fixed;inset:0;cursor:grab;touch-action:none;
  background:
    radial-gradient(900px 620px at 50% 50%,rgba(255,255,255,.85),rgba(255,255,255,0) 70%),
    var(--bg);
}
#board.dragging{cursor:grabbing}
#canvas{position:absolute;top:0;left:0;will-change:transform;
  transform:translate3d(0,0,0);backface-visibility:hidden}
/* Picking a shelf REPLACES the wall, it does not travel across it, so a jump cut would be
   a lie about what happened. A short fade says the content changed under you. */
@keyframes swapIn{from{opacity:0}to{opacity:1}}
#canvas.swap{animation:swapIn .22s ease}

.tile{
  position:absolute;background:var(--surface);border-radius:12px;overflow:hidden;
  box-shadow:var(--shadow);cursor:pointer;
  display:flex;flex-direction:column;
  /* The tile is positioned with translate3d, so it already has a layer. contain tells the
     browser nothing inside a tile can affect anything outside it, which is what keeps a
     throw across sixty tiles from re-costing layout on every frame. */
  contain:layout paint style;
}
/* Hover lift lives on an inner wrapper, never on the element carrying the pan transform:
   animating the same property the board is driving is what makes a lift fight a drag. */
.tile .shot,.tile .foot{transition:none}
.tile::before{
  content:"";position:absolute;inset:0;z-index:4;pointer-events:none;border-radius:12px;
  box-shadow:0 0 0 0 rgba(16,24,40,0);
  transition:box-shadow .22s ease;
}
.board-still .tile:hover::before{box-shadow:var(--shadow-hi)}
.tile .shot{position:relative;flex:1;overflow:hidden;background:#e9e6e1}
.tile img,.tile video{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;
}
/* content-visibility:auto was here as a paint optimisation and it cost more than it
   saved: it let the browser skip painting tile images entirely, which showed up as a
   wall of blank cards in a screen recording. The tile already has contain:layout paint
   style, which is the part that actually helps. */
.tile video{opacity:0;z-index:1;transition:opacity .45s ease}
.tile.playing video{opacity:1}
/* The tile the board chose to play on its own gets a quiet ring, so it reads as
   deliberate rather than as one tile behaving oddly. */
.tile.ambient::before{box-shadow:0 0 0 2px var(--accent),var(--shadow-hi)}
.tile.ambient .kind{background:var(--accent);color:#fff}
.tile .kind{
  position:absolute;top:9px;left:9px;z-index:3;
  background:rgba(255,255,255,.92);border-radius:999px;padding:3px 9px;
  font:600 .62rem var(--mono);letter-spacing:.1em;color:var(--accent);
}
.tile .num{
  position:absolute;top:9px;right:9px;z-index:3;
  background:rgba(10,10,10,.72);border-radius:999px;padding:3px 9px;
  font:600 .64rem var(--mono);letter-spacing:.03em;color:#fff;
}
/* The tiles whose creative we could not fetch. Designed on purpose so the board reads
   as complete rather than half loaded. */
.tile.noshot .shot{background:linear-gradient(160deg,#efe9df,#e6ded1)}
.tile .stand{position:absolute;inset:0;display:flex;flex-direction:column;
  align-items:center;justify-content:center;padding:20px;text-align:center}
.tile .stand .q{font:400 3.2rem/1 var(--display);color:rgba(21,58,128,.28);margin-bottom:-6px}
.tile .sname{font:400 1.15rem/1.25 var(--display);color:#2b2a27;margin:0 0 8px}
.tile .sline{font:600 .58rem var(--mono);letter-spacing:.14em;text-transform:uppercase;
  color:#8b8378;margin:0}

/* The first tile of every shelf. It is what makes a block read as a block when you
   arrive, and what tells you where you are once the panel is out of the way. */
.tile.marker{
  background:var(--accent);border:none;
  align-items:flex-start;justify-content:flex-end;padding:22px 20px;cursor:default;
}
.tile.marker:hover{box-shadow:var(--shadow)}
.tile.marker .mnum{
  font:300 3.4rem/1 var(--display);color:rgba(255,255,255,.5);margin-bottom:auto;
}
.tile.marker .mname{
  font:400 1.85rem/1.08 var(--display);color:#fff;margin:0 0 8px;
  letter-spacing:-.02em;text-transform:capitalize;
}
.tile.marker .mline{
  font:600 .6rem/1.4 var(--mono);letter-spacing:.14em;text-transform:uppercase;
  color:rgba(255,255,255,.72);
}

/* The click affordance. It appears on hover, so nobody has to guess that a tile opens. */
.tile:not(.marker) .shot::after{
  content:"OPEN";position:absolute;left:50%;top:50%;z-index:4;
  transform:translate(-50%,-50%) scale(.9);
  background:rgba(255,255,255,.95);color:var(--accent);
  font:600 .64rem var(--mono);letter-spacing:.16em;
  padding:9px 16px;border-radius:999px;
  opacity:0;transition:opacity .18s ease,transform .18s ease;pointer-events:none;
}
.board-still .tile:not(.marker):hover .shot::after{opacity:1;transform:translate(-50%,-50%) scale(1)}

/* Brand, a multiplication sign, then the creator. One line, because the pairing is the
   fact and splitting it across two lines makes it read as two separate labels. */
/* Two lines, not one. "The Farmers Dog x @adam_christman" cannot fit across 184px at a
   readable size, and squeezing it truncated BOTH halves, which is the one outcome worth
   avoiding: the whole point of the line is that you can read who paid whom. The brand
   gets the full width on top, the creator the full width underneath. */
.tile .pair{margin:0;display:block;min-width:0}
.tile .pair .b{
  display:block;font:600 1rem/1.25 var(--display);color:var(--head);
  letter-spacing:-.012em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.tile .pair .x{
  display:inline-block;font:400 .8rem var(--serif);color:var(--accent);
  margin-right:5px;vertical-align:baseline;
}
.tile .pair .c{
  font:500 .9rem/1.35 var(--serif);color:var(--body);
  display:inline;
}
.tile .foot{
  padding:9px 12px 11px;border-top:1px solid var(--hairline);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}

/* ---------- the pinned middle ---------- */
.centre{
  position:fixed;inset:0;display:flex;align-items:center;justify-content:center;
  pointer-events:none;z-index:20;
}
.centre[hidden]{display:none}
/* Fades rather than vanishing, so the eye follows it up to the pill instead of losing it. */
.centre.gone .panel{opacity:0;transform:translateY(-10px) scale(.985);transition:opacity .26s ease,transform .26s ease}
.panel{transition:opacity .26s ease,transform .26s ease}
.panel{
  pointer-events:auto;text-align:center;width:min(430px,calc(100vw - 32px));
  padding:26px 30px 22px;
  background:rgba(255,255,255,.86);backdrop-filter:blur(14px) saturate(1.2);
  border:1px solid var(--hairline);border-radius:18px;
  box-shadow:0 4px 12px rgba(16,24,40,.06),0 28px 70px rgba(16,24,40,.14);
  /* Never taller than the screen. On a laptop the shelves were falling off the bottom,
     which is the one place they must not be, since they are the navigation. */
  max-height:calc(100vh - 40px);overflow-y:auto;
}
.eyebrow{
  font:600 .6rem/1.4 var(--mono);letter-spacing:.16em;text-transform:uppercase;
  color:var(--accent);margin:0 0 10px;
}
h1{
  font:600 clamp(1.75rem,3.1vw,2.35rem)/1.02 var(--display);
  font-variation-settings:'SOFT' 0,'WONK' 1;
  letter-spacing:-.028em;color:var(--head);margin:0 0 10px;
}
h1 a{color:inherit;text-decoration:none}
h1 a:hover{color:var(--accent);text-decoration:none}
.by{font:500 .88rem/1.4 var(--serif);color:var(--mut);margin:0 0 14px}
.by a{
  color:var(--accent);font-weight:600;text-decoration:none;
  border-bottom:1px solid rgba(27,58,107,.34);
}
.by a:hover{color:var(--accent-hi);border-bottom-color:var(--accent-hi);text-decoration:none}
.lede{font-size:1.04rem;line-height:1.5;color:var(--body);margin:0 0 24px}
.pick{
  font:600 .66rem/1 var(--mono);letter-spacing:.16em;text-transform:uppercase;
  color:var(--mut);margin:0 0 14px;
}
/* Two ways to read the same board: what got watched, and what has been running longest.
   They are the two questions a swipe file gets asked, so they sit above the shelves. */
.sorts{display:inline-flex;gap:3px;padding:3px;margin:0 0 12px;
  background:#e7e2d9;border-radius:999px}
.sort{
  background:transparent;border:0;border-radius:999px;cursor:pointer;
  font:600 .64rem var(--mono);letter-spacing:.07em;text-transform:uppercase;
  color:var(--mut);padding:7px 12px;transition:background .16s,color .16s;
}
.sort:hover{color:var(--ink)}
.sort.on{background:#fff;color:var(--accent);box-shadow:0 1px 3px rgba(35,28,20,.12)}

.chips{display:flex;flex-wrap:wrap;gap:6px;justify-content:center;margin:0 0 16px}
.chip{
  display:inline-flex;align-items:center;gap:6px;
  background:#fff;border:1px solid var(--hairline);border-radius:999px;
  color:var(--ink);font:500 .84rem var(--serif);padding:6px 11px;cursor:pointer;
  transition:border-color .16s,background .16s,color .16s;
}
.chip:hover{border-color:var(--accent);color:var(--accent)}
.chip.on{background:var(--accent);border-color:var(--accent);color:#fff}
.chip .cn{
  font:600 .6rem var(--mono);letter-spacing:.03em;color:var(--mut);
  background:#f2f0ec;border-radius:999px;padding:2px 6px;
}
.chip:hover .cn{color:var(--accent)}
.chip.on .cn{background:rgba(255,255,255,.22);color:#fff}

.find{display:flex;gap:9px;margin:0 0 18px}
#q{
  flex:1;background:#fff;border:1px solid var(--hairline);border-radius:999px;
  color:var(--ink);font:400 .98rem var(--serif);padding:11px 18px;outline:none;
}
#q::placeholder{color:var(--mut)}
#q:focus{border-color:var(--accent);box-shadow:0 0 0 3px rgba(21,58,128,.09)}
#cat{
  background:#fff;border:1px solid var(--hairline);border-radius:999px;color:var(--ink);
  font:400 .92rem var(--serif);padding:11px 14px;cursor:pointer;outline:none;max-width:150px;
}
.counts{
  font:500 .66rem/1.6 var(--mono);letter-spacing:.05em;color:var(--mut);margin:0 0 14px;
}
.counts span{color:var(--head);font-weight:600}
.fold{
  background:transparent;border:1px solid var(--hairline);border-radius:999px;
  color:var(--mut);font:600 .64rem var(--mono);letter-spacing:.1em;text-transform:uppercase;
  padding:7px 16px;cursor:pointer;
}
.fold:hover{border-color:var(--accent);color:var(--accent)}

/* ---------- the pill, and the menu it opens ---------- */
/* Panning collapses the card to this pill EVERY time, not only the first. It used to
   re-open the full card and then never get out of the way again, so the introduction sat
   over the middle of the board for the rest of the session. */
.pillwrap{position:fixed;z-index:24;left:50%;top:22px;transform:translateX(-50%);
  display:flex;flex-direction:column;align-items:center;gap:8px}
.pillwrap[hidden]{display:none}
.unfold{
  display:inline-flex;align-items:center;gap:9px;cursor:pointer;
  background:rgba(255,255,255,.94);backdrop-filter:blur(10px);
  border:1px solid var(--hairline);border-radius:999px;
  box-shadow:0 4px 16px rgba(16,24,40,.10);
  font:500 .86rem var(--display);color:var(--head);padding:10px 20px;
  transition:border-color .16s,box-shadow .16s;
}
.unfold:hover{border-color:var(--accent);box-shadow:0 6px 20px rgba(16,24,40,.16)}
.unfold .tag{
  font:600 .58rem var(--mono);letter-spacing:.12em;text-transform:uppercase;
  background:var(--accent);color:#fff;border-radius:999px;padding:4px 9px;
}
.unfold .tag[hidden]{display:none}
.unfold .caret{font-size:.7rem;color:var(--accent);transition:transform .2s ease}
.unfold[aria-expanded="true"] .caret{transform:rotate(180deg)}

.menu{
  width:min(340px,calc(100vw - 28px));
  background:rgba(255,255,255,.96);backdrop-filter:blur(14px) saturate(1.2);
  border:1px solid var(--hairline);border-radius:16px;padding:16px 18px 16px;
  box-shadow:0 4px 12px rgba(16,24,40,.06),0 22px 56px rgba(16,24,40,.16);
  max-height:calc(100vh - 96px);overflow-y:auto;
  animation:menuIn .18s ease;
}
.menu[hidden]{display:none}
@keyframes menuIn{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}
.mlab{
  font:600 .58rem/1 var(--mono);letter-spacing:.16em;text-transform:uppercase;
  color:var(--mut);margin:0 0 9px;text-align:left;
}
.menu .chips{justify-content:flex-start;margin:0 0 16px}
.menu .sorts{display:flex;width:100%;margin:0 0 16px}
.menu .sort{flex:1}
.mback{
  width:100%;background:var(--accent);border:1px solid var(--accent);border-radius:999px;
  color:#fff;font:600 .68rem var(--mono);letter-spacing:.1em;text-transform:uppercase;
  padding:11px 14px;cursor:pointer;transition:background .16s;
}
.mback:hover{background:var(--accent-hi);border-color:var(--accent-hi)}

/* The corner note sits over moving tiles, so it carries its own paper. Without it the
   text lands on a photograph and becomes unreadable at exactly the moment someone is
   trying to work out what the page is. The centre bottom hint that used to live here is
   gone: it said in two sentences what the arrows around the card now say silently. */
.corner{
  position:fixed;right:18px;bottom:16px;z-index:20;max-width:320px;text-align:right;
  pointer-events:none;
  background:rgba(255,255,255,.86);backdrop-filter:blur(8px);
  border:1px solid var(--hairline);border-radius:12px;padding:10px 14px;
}
.corner .mono{font:500 .64rem/1.5 var(--mono);letter-spacing:.04em;color:var(--mut)}

/* ---------- the sheet ---------- */
.sheet{
  position:fixed;inset:0;z-index:100;background:rgba(28,26,24,.5);
  backdrop-filter:blur(7px);display:flex;align-items:center;justify-content:center;
}
.sheet[hidden]{display:none}
.sheetin{
  display:flex;max-width:1000px;width:calc(100% - 132px);max-height:90vh;
  background:var(--surface);border-radius:18px;overflow:hidden;
  box-shadow:0 30px 90px rgba(16,24,40,.34);
}
/* Sized to a 9:16 reel so the pane is nearly filled rather than framed in black. */
.player{background:#111;flex:0 0 min(38%,368px);display:flex;align-items:center;justify-content:center}
.player video,.player img{width:100%;height:100%;max-height:90vh;object-fit:contain;display:block}
.meta{flex:1;padding:34px 38px;overflow-y:auto}
.meta h2{
  font:600 1.62rem/1.2 var(--display);color:var(--head);margin:0 0 8px;letter-spacing:-.024em;
  display:flex;align-items:baseline;gap:9px;flex-wrap:wrap;
}
.meta h2 .hx{font:400 1.1rem var(--serif);color:var(--accent)}
.meta h2 .hc{font:500 1.28rem var(--serif);color:var(--mut)}
.meta .sub{font:600 .68rem var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--accent);margin:0 0 26px}
.meta dl{display:grid;grid-template-columns:auto 1fr;gap:10px 24px;margin:0 0 24px}
.meta dt{font:600 .64rem var(--mono);letter-spacing:.11em;text-transform:uppercase;color:var(--mut);padding-top:4px}
.meta dd{margin:0;color:var(--ink);font-size:1rem}
.meta .cap{
  color:var(--body);font-size:1rem;line-height:1.6;margin:0 0 24px;
  border-left:2px solid var(--hairline);padding-left:18px;white-space:pre-wrap;
}
.meta .links{display:flex;gap:11px;flex-wrap:wrap}
.meta .links a{
  border:1px solid var(--accent);color:var(--accent);border-radius:999px;padding:11px 22px;
  font:600 .86rem var(--mono);letter-spacing:.04em;text-decoration:none;
}
.meta .links a:hover{background:var(--accent);color:#fff;text-decoration:none}
.meta .links a.ghost{border-color:var(--hairline);color:var(--mut)}
.meta .links a.ghost:hover{background:#f2f0ec;color:var(--ink)}

.close,.nav{
  position:absolute;z-index:110;background:rgba(255,255,255,.94);
  border:1px solid var(--hairline);color:var(--ink);cursor:pointer;
  border-radius:50%;width:46px;height:46px;font-size:1.5rem;line-height:1;
  display:flex;align-items:center;justify-content:center;
}
.close{top:24px;right:24px}
.nav{top:50%;transform:translateY(-50%);font-size:1.9rem}
.prev{left:24px}
.next{right:24px}
.close:hover,.nav:hover{background:var(--accent);color:#fff;border-color:var(--accent)}

@media (max-width:820px){
  .panel{max-width:calc(100% - 36px);padding:28px 26px 22px}
  h1{font-size:clamp(2rem,9vw,2.6rem)}
  .lede{font-size:.98rem}
  .corner{display:none}
  .chips{gap:5px}
  .chip{font-size:.84rem;padding:7px 11px}
  .sheetin{flex-direction:column;width:calc(100% - 24px);max-height:92vh}
  .player{flex:0 0 auto;max-height:50vh}
  .meta{padding:22px 20px}
  .nav{display:none}
}

/* Someone who asks for less motion gets no coasting, no lift and no drifting arrows. */
@media (prefers-reduced-motion: reduce){
  .tile,.tile::before,.tile video{transition:none}
  .pan{animation:none}
  #canvas.swap{animation:none}
  html{scroll-behavior:auto}
}

/* ---------- the arrows around the card ---------- */
/* They used to be pinned to the four screen edges, where they read as four unrelated
   buttons and needed a line of text at the bottom explaining that the board runs forever.
   Anchored to the card and drifting outward from it, they say the same thing without the
   sentence: this card sits in the middle of something that carries on past it. They are
   still real pan buttons. The drift stops the moment the board is used, because by then
   the point is made and nothing should be animating over the creative. */
.hub{position:relative;pointer-events:none;display:flex;align-items:center;justify-content:center}
.pans{position:absolute;inset:0;pointer-events:none;z-index:1}
/* Solid, not translucent. Over a wall of photographs a 92% white disc with a hairline
   border disappeared into whatever was behind it, which is how four buttons ended up
   needing a caption at the bottom of the screen to explain them. */
.pan{
  position:absolute;pointer-events:auto;cursor:pointer;
  width:44px;height:44px;border-radius:50%;
  background:#fff;border:1.5px solid rgba(35,28,20,.2);
  box-shadow:0 3px 14px rgba(16,24,40,.18);
  color:var(--accent);font-size:.8rem;line-height:1;
  display:flex;align-items:center;justify-content:center;
  transition:background .16s,color .16s,box-shadow .16s,border-color .16s;
}
.pan:hover{background:var(--accent);color:#fff;border-color:var(--accent);
  box-shadow:0 5px 20px rgba(16,24,40,.28)}
.pan.up   {left:50%;top:-58px;margin-left:-22px;  animation:driftUp    2.6s ease-in-out infinite}
.pan.down {left:50%;bottom:-58px;margin-left:-22px;animation:driftDown  2.6s ease-in-out infinite .3s}
.pan.left {top:50%;left:-58px;margin-top:-22px;   animation:driftLeft  2.6s ease-in-out infinite .6s}
.pan.right{top:50%;right:-58px;margin-top:-22px;  animation:driftRight 2.6s ease-in-out infinite .9s}
@keyframes driftUp   {0%,100%{transform:translateY(0);opacity:.8}50%{transform:translateY(-13px);opacity:1}}
@keyframes driftDown {0%,100%{transform:translateY(0);opacity:.8}50%{transform:translateY(13px);opacity:1}}
@keyframes driftLeft {0%,100%{transform:translateX(0);opacity:.8}50%{transform:translateX(-13px);opacity:1}}
@keyframes driftRight{0%,100%{transform:translateX(0);opacity:.8}50%{transform:translateX(13px);opacity:1}}
.pans.used .pan{animation:none;opacity:.85}

/* A narrow screen has no room outside the card, so there the arrows go back to the frame. */
@media (max-width:620px){
  .pans{position:fixed}
  .pan{width:38px;height:38px;font-size:.7rem}
  .pan.up{top:16px}
  /* Clear of the source badge, which is pinned to the same bottom strip. */
  .pan.down{bottom:64px}
  .pan.left{left:16px;margin-left:0}
  .pan.right{right:16px;margin-right:0}
}

/* ---------- where the data comes from ---------- */
.source{
  position:fixed;left:18px;bottom:16px;z-index:20;
  display:inline-flex;align-items:center;gap:9px;text-decoration:none;
  background:rgba(255,255,255,.95);backdrop-filter:blur(8px);
  border:1.5px solid rgba(35,28,20,.22);border-radius:999px;padding:10px 17px 10px 13px;
  box-shadow:0 3px 14px rgba(16,24,40,.12);
  font:700 .7rem var(--mono);letter-spacing:.1em;text-transform:uppercase;
  color:var(--ink);
}
.source:hover{color:var(--accent);border-color:var(--accent);text-decoration:none}
.source svg{flex:0 0 auto;display:block}

@media (max-width:820px){
  .source{left:10px;bottom:10px;font-size:.6rem;padding:8px 13px 8px 10px;letter-spacing:.07em}
}

/* ---------- low power ---------- */
/* Set by app.js on a small machine or when reduced motion is asked for. Stills only: no
   ambient autoplay, no hover previews, no lift. A wall of stills on a slow laptop beats a
   wall that stutters. */
body.low-power .tile video{display:none}
body.low-power .tile:hover{transform:none}
body.low-power .tile::before{transition:none}
body.low-power .pan{animation:none}

/* ---------- the quiet door to /list ---------- */
/* Under the Explore button on the centre card, and again in the pill menu. Deliberately
   not a button: the board is what this page is for, and a second button beside Explore
   would read as two equal choices. */
.alt{margin:14px 0 0;text-align:center;font-size:14.5px}
.alt a{color:var(--mut);border-bottom:1px solid transparent}
.alt a:hover{color:var(--accent);border-bottom-color:var(--accent);text-decoration:none}
.mlist{display:block;font:500 14.5px/1.4 var(--serif);color:var(--accent);
  border:1px solid var(--hairline);border-radius:10px;padding:11px 14px;margin-bottom:14px}
.mlist:hover{border-color:var(--accent);background:rgba(27,58,107,.05);
  text-decoration:none}
