/* ===============================================================
   Dissolvd — two colours only: black and Silk (#EEE5D9).
   Every mid-tone is Silk at reduced alpha, never a third hue.
   Full-strength Silk is reserved for text and primary actions,
   so uploaded work is the only thing on screen carrying colour.
   =============================================================== */
:root{
  --silk:238,229,217;
  /* The ground is a dark grey rather than true black: pictures sit on it
     without the edge-of-frame contrast black gives them, and the panels
     above it can be lighter still without glowing. */
  --bg:#121212;
  --bg-0:rgba(18,18,18,0);      /* the same grey, gone — for fades */
  /* THE BOARD'S GREY, AS A NUMBER RATHER THAN A BLEND.

     A board used to be rgba(silk,.04) laid over the page, which is fine to
     paint and useless to fade onto: the banner's scrim has to end on exactly
     the colour behind it, and "whatever .04 of silk comes to over #121212"
     is not a colour anything else can be told. So the blend is done once,
     here — 18 + (238-18)×.04 and so on — and both the panel and the fade
     that lands on it read the same three numbers.

     The board is solid, and only the banner fades. A panel that dissolved at
     its own edges would stop being a surface things sit on. */
  --board-rgb:27,26,26;
  /* THE EDGE IS LIT, NOT DRAWN.

     A flat hairline is the same value the whole way round, which is what a
     drawn box looks like. A widget's edge is a bevel catching the light:
     brightest along the top, gone by the middle of the sides, and just
     picked up again underneath where the ground throws a little back. Same
     one pixel, but it is a gradient, and that is the entire difference
     between an outline and a sheet of glass lying on something.

     It goes on a masked pseudo-element rather than a real border, because a
     border would push every board's content in by a pixel; and rather than
     an inset shadow, because a shadow cannot hold a gradient. */
  --board-r:12px;
  --board-gloss:linear-gradient(
     180deg,
     rgba(var(--silk),.20) 0%,
     rgba(var(--silk),.075) 18%,
     rgba(var(--silk),.035) 45%,
     rgba(var(--silk),.03) 70%,
     rgba(var(--silk),.085) 100%);
  --board-lift:0 1px 2px rgba(0,0,0,.32), 0 5px 14px rgba(0,0,0,.28);
  /* THERE IS MORE OF THIS THAN YOU CAN SEE.

     Two places say it — the bio with a fourth line, and the odd row at the
     foot of a board — and they have to say it the same way or the site has
     two dialects for one sentence. Solid for the first half, out by the
     end: enough held at full strength to read what is under it, and no hard
     edge anywhere. Change it here and both move. */
  --more-fade:linear-gradient(180deg,#000 0,#000 52%,transparent 100%);
  /* A SHADE FIRMER, WHERE WHAT IS FADING IS NOT BEING READ.

     The bio holds solid to 52% because the last line under that gradient is
     a line you are meant to finish — a paragraph that dissolves mid-sentence
     is a paragraph with a fault in it, not one with more to come.

     The two that carry this instead are not being read. The odd row at the
     foot of a board is a strip of cover, and the sliver under a shut credits
     drawer is the top of a face and a name — both are there to say the run
     continues, and neither is anything you are partway through. They can go
     sooner and say it more plainly. Twelve points earlier, which is a hint
     rather than a different gesture: put them side by side and it reads as
     the same fade, put either next to nothing and it reads as more of one. */
  --more-fade-firm:linear-gradient(180deg,#000 0,#000 40%,transparent 100%);
  --board:rgb(var(--board-rgb));
  --board-0:rgba(var(--board-rgb),0);
  --surface:#1c1c1c;            /* menus, the suggestion list, the toast */
  --ink:#EEE5D9;
  --ink2:rgba(var(--silk),.55);
  --ink3:rgba(var(--silk),.33);
  --edge:rgba(var(--silk),.13);
  --line:rgba(var(--silk),.26);
  --plate:rgba(var(--silk),.10);

  /* The one gap. It is what a row of covers keeps between them, and it is
     what everything vertical keeps too — above a label, under a rule,
     between shelves. Change it and the whole grid breathes together. */
  --gap:12px;

  --gutter:20px;
  /* How far the wordmark is set in from the column's own edge. It lives here
     rather than on .brand because the forms line their fields up with it:
     "dissolvd" is the leftmost type on the page, and a form that starts
     anywhere else reads as a second, competing margin. One figure, two
     places, and they cannot drift apart. */
  --brand-in:20px;
  --col:100%;           /* phone: edge to edge. desktop: 60% of the window */
  --tab-h:64px;
  /* The top bar's own height — 12 above, 12 below, 36 of nav between. Not
     measured anywhere: it is here so the banner can be pulled up by exactly
     as much as the bar covers, and a banner is meant to reach the top of the
     window. It said 54 while the bar had grown to 60, which left the picture
     starting 6px down with a band of page ground above it. Change the bar's
     padding or its type and change this. */
  --bar:60px;

  /* Spacing scale. Generous rather than tight — the work wants room around
     it more than the page wants to fit on one screen, and a gap only says
     "these are separate things" if it is bigger than the gaps inside them.
     Everything on a project page picks from these four. */
  --s-tight:8px;        /* inside one block: a title and its own meta line */
  /* Between blocks that answer each other. The scale went 8 to 48 and
     nothing between, so anything too close at 48 had only 8 to fall back on
     — and 8 was tried under the title once and crowded it, because a 28px
     avatar 8px under a 25px title makes two lines read as one. Half the
     section gap: still plainly a step, no longer a break. */
  --s-step:24px;
  --s-sect:48px;        /* between every block on the page */
  /* No air above the first frame: the work starts at the bar. One value,
     read by the profile banner and the project gallery both, so the two
     pages open at the same height whatever it is set to. */
  --s-open:0px;

  --geo:"Avenir Next","Avenir","Futura","Century Gothic",system-ui,sans-serif;
}
/* Sixty percent of the window — 60% of what can actually be seen, which is
   not what 60vw measures. vw counts the scrollbar, so on any page long
   enough to scroll the column comes out its share of the scrollbar too wide.

   A fraction rather than a fixed measure, and the reason is worth keeping:
   this is read on a 4K display running unscaled beside a Retina laptop, so
   one screen offers about twice the css pixels of the other. Any single
   number is a third of one screen or the whole of the other. A fraction of
   whatever is there is the only rule that means the same thing on both.

   --sb is still in there because vw counts the scrollbar and the visible
   width does not: body is exactly as wide as the viewport minus the
   scrollbar, so 100vw less body's own 100% is the scrollbar. Both are
   declared on body rather than :root because a custom property is
   substituted where it is declared, and :root has no --sb to substitute. */
body{ --sb:calc(100vw - 100%) }
@media (min-width:900px){ body{ --col:calc(60vw - var(--sb) * .6)} }
*{box-sizing:border-box}
html{color-scheme:dark;-webkit-text-size-adjust:100%}
/* One flat ground, top to bottom. A gradient here read as a light source the
   page does not have, and it also made every fade that meets it — the
   banner's bottom and both its sides — depend on whatever colour the ground
   happened to be at that point. One value means one value everywhere. */
body{
  margin:0;color:var(--ink);background:var(--bg);
  font-family:var(--geo);-webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit;background:none;border:0;padding:0;cursor:pointer}

/* HIDDEN MEANS HIDDEN.

   The browser hides [hidden] with display:none from its own stylesheet,
   which any class of ours that sets display beats on specificity — so a
   .rrow or an .addbtn marked hidden stayed on the page, and the only sign
   was the element's .hidden property cheerfully reporting true.

   This has been patched one selector at a time down the file — .dropzone,
   .secblock, .sugs — each time it bit. One rule instead, with the only
   !important in here, because the whole point of the attribute is that it
   outranks whatever the element would otherwise do. */
[hidden]{display:none !important}
:focus-visible{outline:2px solid var(--ink);outline-offset:3px;border-radius:3px}
img{display:block;max-width:100%}

/* Any box that can receive a background-image resolves its sizing HERE.
   Declaring this per-component is how tiles kept rendering at natural
   size — keep new image boxes in this list. (.vplate overrides to contain.) */
.frame,.pstill,.avatar,.banner,.vplate,.crewp .av,.byline .av,.vmeta .av,.crow .av,.member .av,.nmem .av,.act .av,.crole .av,.sug .av,.preview,.grade{
  background-size:cover;background-position:center;background-repeat:no-repeat;
  /* display:block is load-bearing: these are <span>s, and width/aspect-ratio
     are ignored on inline boxes — dropping it renders them zero-height. */
  display:block;
}
/* NOT EVERY FRAME. COVERS ONLY.

   This put the hairline on .frame, which is the class both a still and a
   cover wear — so taking the line off the stills meant taking it off here,
   where it was reaching them, rather than off each still in turn.

   The covers ask for it themselves now (.card .frame, .tile .frame,
   .featcard .frame, .coverprev), which is the right way round: a cover is
   one of a set of equals and wants an edge, a still is a picture on a wall
   and does not. It stays a border rather than a box-shadow wherever it is
   used — an outer shadow gets clipped by the rows' overflow, which have no
   vertical padding, and an inset one paints over the picture and vanishes
   against a bright edge. background-clip keeps the image out from under it,
   so the line stays clean silk. */
.g1{background-image:radial-gradient(ellipse at 66% 74%,#f7d094 0,rgba(247,208,148,0) 46%),linear-gradient(163deg,#1d1208 0,#5c2e10 44%,#a75c1d 74%,#e29a45 100%)}
.g2{background-image:radial-gradient(circle at 26% 38%,#e0824a 0,rgba(224,130,74,0) 44%),linear-gradient(200deg,#03151a 0,#0c3540 58%,#1d6270 100%)}
.g3{background-image:radial-gradient(ellipse at 50% 84%,#5f86c4 0,rgba(95,134,196,0) 52%),linear-gradient(180deg,#03060e 0,#0e1d39 56%,#23406b 100%)}
.g4{background-image:linear-gradient(178deg,#8fa8b8 0 34%,#cbb28c 34%,#a97f52 68%,#68482a 100%)}
.g5{background-image:radial-gradient(circle at 72% 30%,#e26fb0 0,rgba(226,111,176,0) 40%),radial-gradient(circle at 24% 70%,#3fd0d6 0,rgba(63,208,214,0) 44%),linear-gradient(150deg,#090411 0,#1a0c26 62%,#2b1340 100%)}
.g6{background-image:linear-gradient(190deg,#c2c8c0 0,#8b958b 40%,#4d554e 74%,#242925 100%)}
.g7{background-image:radial-gradient(ellipse at 38% 56%,#ffd9a0 0,rgba(255,217,160,0) 38%),linear-gradient(155deg,#110b06 0,#2a1c0f 52%,#493317 100%)}
.g8{background-image:radial-gradient(ellipse at 60% 20%,#a8d6a0 0,rgba(168,214,160,0) 46%),linear-gradient(184deg,#05110b 0,#113221 58%,#1e5135 100%)}
.g9{background-image:linear-gradient(174deg,#e7edf0 0,#b8c7cf 42%,#7c909b 72%,#3e4c55 100%)}
.g11{background-image:radial-gradient(circle at 30% 26%,#ffe9b0 0,rgba(255,233,176,0) 40%),linear-gradient(196deg,#1a1307 0,#3e3014 54%,#6a5322 100%)}
.g12{background-image:radial-gradient(ellipse at 66% 44%,#7fc9ff 0,rgba(127,201,255,0) 44%),linear-gradient(172deg,#04090f 0,#0c1f32 58%,#173953 100%)}
.gp{background-image:radial-gradient(circle at 42% 33%,#f0c395 0,#b87c52 27%,rgba(184,124,82,0) 60%),linear-gradient(160deg,#1a1410 0,#2d221a 60%,#3d2c20 100%)}
.gc{background-image:radial-gradient(circle at 56% 35%,#dcae86 0,#9c6a48 29%,rgba(156,106,72,0) 62%),linear-gradient(200deg,#15100d 0,#2a1f18 100%)}
.gp2{background-image:radial-gradient(circle at 58% 30%,#cfd9de 0,#7d8e99 28%,rgba(125,142,153,0) 60%),linear-gradient(150deg,#10161a 0,#1e2a31 62%,#2b3a42 100%)}
.gc2{background-image:radial-gradient(circle at 38% 36%,#e8c2a4 0,#a4705a 30%,rgba(164,112,90,0) 62%),linear-gradient(190deg,#1a1211 0,#30201d 100%)}

/* ---------------- layout ---------------- */
.wrap{
  width:100%;max-width:var(--col);
  margin:0 auto;padding:0 var(--gutter);
}
.page{padding-bottom:calc(var(--tab-h) + 40px)}
@media (min-width:900px){ .page{padding-bottom:80px} }

/* ---------------- header ---------------- */
.topbar{
  position:sticky;top:0;z-index:30;
  /* Black, fading out through its own height. The work starts at the bar
     now, so a solid ground would cut the top off every picture; a scrim lets
     the frame come up through the bottom of the bar while still holding
     enough black under the type to read. Nothing marks where the bar ends —
     it stops being black, and that is the edge. */
  /* The scrim is on a layer of its own, not on the bar's own box, because it
     has to reach further down the picture than the bar does. Painted as the
     bar's background it could only ever be as tall as the bar, so the type
     had its cover and the frame below it went from shaded to bright in one
     line. Half again the bar's height carries it past the nav and lets it
     finish on the photograph. */
  background:none;
  /* Equal above and below, at the taller figure. It was 26/12 — the nav
     dropped low in its band — which read fine on a profile, where a banner
     runs up behind it, and wrong on a project, where the frame starts at the
     bar's bottom edge: 29px of air above the nav and 15 below it, so the
     work sat nearer the furniture than the furniture sat to the window.

     Centring it settles the scrim too. The fade is twice the drop from the
     top of the bar to the middle of the wordmark, so a wordmark on the bar's
     own centre line makes that exactly the bar's height — the fade ends
     where the bar ends, and the frame below it gets none of the tail. */
  padding:20px 0;
}
.topbar::before{
  content:"";position:absolute;left:0;right:0;top:0;z-index:-1;
  /* set by syncBar(): twice the drop from the top of the bar to the middle
     of the wordmark, so the type sits at the fade's own centre line */
  height:var(--scrimh, calc(var(--bar) * 1.6));
  background:var(--scrim);
  pointer-events:none;
}

/* Wordmark first, on the column's own left edge — the same line the banner,
   the covers, the avatar and every shelf label start from. The group used to
   be centred in the bar, which read fine on its own but left the site's name
   floating in the middle of a picture that begins 20px further left.

   The nav is centred in the bar rather than trailing the wordmark. Three
   grid columns do it — 1fr, the nav, 1fr — so the middle track is centred on
   the column no matter how long the name gets. Flex could only put the nav
   next to the wordmark or hard against the far edge; neither is the middle.

   The wordmark is set in from the edge rather than on it. Type hard against
   the line the picture starts on reads as crowded, and there is a second
   reason here: the banner turns a 20px corner exactly where the wordmark
   begins, so a name flush left sits in the one place the picture is curving
   away from it. 20px in is where that curve finishes. */
.topbar .wrap{
  display:grid;grid-template-columns:1fr auto 1fr;
  align-items:center;column-gap:24px;
}
.topbar .brand{margin-left:var(--brand-in);justify-self:start}
.brand{font-size:19px;font-weight:600;letter-spacing:-.005em;flex:none}
/* Small and plain rather than large: at this size the nav stops competing
   with the wordmark and reads as the site's furniture. Lower case, and the
   tracking goes with the capitals — letterspacing is what makes small
   uppercase legible and what makes lower case look pulled apart. 15 is this
   site's floor for anything you click. */
.navlinks{
  display:none;gap:24px;
  font-size:13px;font-weight:600;
}
.navlinks a{color:var(--ink2)}
.navlinks a.on{color:var(--ink)}
/* your own entry: glyph, name, and a menu that opens on hover or focus */
.menu{position:relative}
.navlinks .me{
  cursor:pointer;
  display:flex;align-items:center;gap:7px;color:var(--ink2);
}
.navlinks .me.on{color:var(--ink)}
.menu:hover .me,.menu:focus-within .me{color:var(--ink)}
.navlinks .me svg:first-child{flex:none;width:17px;height:17px}
/* 15, not 11. At 11 against Silk-33 it was 2.63:1 — under the 3:1 a non-text
   mark needs — and read as a smudge beside a 20px name rather than as the
   thing that says there is a menu here. Silk-55 with it, the same pair the
   byline's chevron settled on. */
.navlinks .me svg:last-child{
  flex:none;width:13px;height:13px;color:var(--ink2);
  transition:transform .2s ease;
}
.menu:hover .me svg:last-child,
.menu:focus-within .me svg:last-child{transform:rotate(180deg)}

/* the wrapper's padding bridges the visual gap, so moving the pointer
   from the name down to the panel never leaves the hover area */
.menubody{
  position:absolute;top:100%;left:0;z-index:50;
  padding-top:13px;
  opacity:0;visibility:hidden;transform:translateY(-6px);
  transition:opacity .18s ease,transform .18s ease,visibility .18s;
}
.menu:hover .menubody,.menu:focus-within .menubody{
  opacity:1;visibility:visible;transform:translateY(0);
}
.menucard{
  min-width:180px;padding:8px;
  background:var(--surface);border-radius:12px;
  box-shadow:inset 0 0 0 1px var(--line),0 22px 44px -14px rgba(0,0,0,.95);
  display:flex;flex-direction:column;
}
.menubody a{
  padding:9px 12px;border-radius:7px;
  font-size:15px;color:var(--ink2);
}
.menubody a:hover{color:var(--ink);background:rgba(var(--silk),.07)}
.menubody hr{border:0;border-top:1px solid var(--edge);margin:6px 4px}
@media (prefers-reduced-motion: reduce){
  .navlinks .me svg:last-child,.searchbox,.searchbox input,.upload,.upload span,.menubody{transition:none}
}
/* everything but the wordmark rides the right edge */
.hgroup{display:flex;align-items:center;gap:20px}

/* Search and upload sit as circles and stretch open on hover. Both also
   open on keyboard focus, so the field is not hover-only. */
.searchbox{
  /* CLOSED, IT IS A BUTTON WITH ONE MARK IN IT.

     The gap and the field were still in the flex line while the pill was
     shut — the input measured zero, but 8px of gap between it and the glyph
     did not, and with overflow hidden that pushed the scope half a pixel off
     the pill's centre. Collapsing the gap the way the upload pill collapses
     its label leaves the glyph alone in the line, and centring the line
     centres it. Open, the gap comes back as a margin on the field. */
  display:flex;align-items:center;justify-content:center;gap:0;flex:none;
  width:36px;height:36px;padding:0 10px;
  border-radius:99px;overflow:hidden;white-space:nowrap;
  box-shadow:inset 0 0 0 1px var(--line);
  color:var(--ink3);
  transition:width .26s ease,padding .26s ease,color .18s ease;
}
.searchbox:hover,.searchbox:focus-within{
  width:150px;padding:0 14px;color:var(--ink2);
  justify-content:flex-start;
}
.searchbox:focus-within{box-shadow:inset 0 0 0 1px var(--ink)}
/* THE SCOPE, NOT ITS BOUNDING BOX.

   A magnifier is a lens with a handle running off one corner, so the ink is
   heavier to the bottom right than to the top left — centre the box it is
   drawn in and the mark still reads low and right inside the ring. What is
   centred here is the mark itself: the whole glyph is nudged back by exactly
   the amount its own ink leans, measured rather than guessed.

   In a transform so the flex line is untouched — a margin would move the
   layout, and the layout is what was just squared up. */
.searchbox svg{flex:none;width:17px;height:17px;transform:translate(-0.35px, -0.35px)}
.searchbox input{
  flex:1;min-width:0;font:inherit;font-size:16px;color:var(--ink);
  background-color:transparent;border:0;padding:0;
  appearance:none;-webkit-appearance:none;
}
/* zero-width and no margin while shut, so it takes no room in the line at
   all — the same trick the upload pill's label uses */
.searchbox input{
  opacity:0;max-width:0;margin-left:0;
  transition:opacity .16s ease,max-width .26s ease,margin-left .26s ease;
}
.searchbox:hover input,.searchbox:focus-within input{opacity:1;max-width:120px;margin-left:8px}
.searchbox input::placeholder{color:var(--ink3)}
.searchbox input:focus{outline:0}
.searchbox input::-webkit-search-cancel-button{-webkit-appearance:none}

/* same collapse-and-stretch as the search field, centred in both states */
.upload{
  display:flex;align-items:center;justify-content:center;gap:0;flex:none;
  width:36px;height:36px;padding:0 10px;
  border-radius:99px;overflow:hidden;white-space:nowrap;
  font-size:16px;color:var(--ink2);
  box-shadow:inset 0 0 0 1px var(--line);
  transition:width .26s ease,padding .26s ease,color .18s ease;
}
/* the same parity fix: 36 - 14 is even, 36 - 15 was not */
.upload svg{flex:none;width:14px;height:14px}
/* the label collapses to zero width so the icon can centre on its own —
   left in the flow it would push the icon out of a 38px circle */
.upload span{
  opacity:0;max-width:0;margin-left:0;overflow:hidden;
  transition:opacity .16s ease,max-width .26s ease,margin-left .26s ease;
}
.upload:hover,.upload:focus-visible{
  width:108px;padding:0 15px;color:var(--ink);
  box-shadow:inset 0 0 0 1px var(--ink);
}
.upload:hover span,.upload:focus-visible span{opacity:1;max-width:104px;margin-left:7px}
/* BETWEEN A PHONE AND A WIDE WINDOW, THE NAME GOES.

   Half of a 1000px window is 500px, and the bar wants a wordmark, three nav
   links carrying your full name, a search pill and an upload pill inside it.
   They do not fit: measured, the group ran 20px past the column at 1000 and
   60px past at 920, which broke the one rule this layout has. The name is
   the widest thing in there and the least needed — the icon beside it is the
   same control and says the same thing — so it is the piece that gives way,
   and everything else keeps its size and its spacing. */
@media (max-width:1149px){
  .topbar .me span{display:none}
  /* and the spacing around the wordmark comes in with it: at 900, the exact
     width where this layout still shows a nav at all, the group was 9px
     longer than half the window. The wordmark keeps a clear edge, just a
     narrower one. */
  .topbar .wrap{column-gap:12px}
  body{--brand-in:10px}
}

/* phone: wordmark alone — search and upload live in the tab bar */
@media (max-width:899px){
  .topbar .wrap{justify-content:center}
  .hgroup{display:none}
}
@media (min-width:900px){
  .navlinks{display:flex}
  /* No rule: the space above the first frame is the edge. */
  .topbar{border-bottom:0}
}

/* black at the top, gone by the bottom — the bar's own ground */
:root{ --scrim:linear-gradient(180deg,rgba(0,0,0,.74) 0,rgba(0,0,0,.36) 56%,rgba(0,0,0,0)) }
/* the scrim carries most of it; the shadow is what saves muted nav type
   over the bright end of a frame */
.topbar .brand,
.topbar .navlinks,
.topbar .hgroup{filter:drop-shadow(0 1px 6px rgba(0,0,0,.85))}

/* ---------------- section headings ---------------- */
.sechead{
  display:flex;align-items:baseline;gap:10px;
  /* space between shelves; the two stills rows keep their own tight 8px,
     so a block still reads as one thing */
  margin:40px 0 14px;
  /* A rule under the heading, the same hairline the nav and Press carry. It
     spans the column rather than the label, so it reads as the top edge of
     the row beneath it rather than as an underlined word. The padding is
     what keeps the line off the descenders. */
  padding-bottom:10px;border-bottom:1px solid var(--edge);
}
/* The bio's size, and bold — the same label a profile's shelf carries. It
   was 13 in tracked capitals, a smaller number that reads considerably
   bigger: caps have no descenders to sink into the line and the tracking
   spreads a two-word row name across half the column. A shelf on a profile
   and a row on the homepage are the same object, so they say their name the
   same way. */
.sechead h2{
  margin:0;font-size:15px;font-weight:600;
  letter-spacing:normal;text-transform:none;
}
/* the tag is a count or a word beside the label, not a second label — it
   keeps the smaller size, and loses the tracking that belonged to the caps */
.sechead .tag{font-size:13px;color:var(--ink3)}

/* ---------------- featured shelf ---------------- */
/* one project at a time, at the width of the column — 4:3 like every
   other browse tile, so the grid keeps one ratio throughout. */
/* The strip still stops at the column edge; the leak comes from the card
   being 34px narrower than the strip instead, which shows ~42px of the
   next project without anything crossing the line. That edge dissolves
   rather than being cut off — the mask is on the strip, so it applies to
   whatever has scrolled under it. */
.featrow{
  display:flex;gap:12px;
  overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:none;
  margin:0 calc(var(--gutter) * -1);
  padding:0 var(--gutter);
  scroll-padding-left:var(--gutter);
}
.featrow::-webkit-scrollbar{display:none}
.featcard{flex:none;display:block;width:calc(100% - 34px);scroll-snap-align:start}
.featcard .frame{
  width:100%;aspect-ratio:4 / 3;border-radius:6px;
  background-color:var(--plate);
  /* its own hairline, now that .frame no longer hands one out */
  border:1px solid rgba(var(--silk),.42);
  background-clip:padding-box;
}
.featcard .t{display:block;font-size:21px;font-weight:600;margin-top:14px;letter-spacing:-.01em}
.featcard .c{display:block;font-size:15px;color:var(--ink2);margin-top:4px}
/* centre the arrows on the image: it is 100cqw * 3/4 tall, so half is *3/8 */
.featrow ~ .snav{top:calc(100cqw * 3 / 8)}

/* ---------------- popular stills ---------------- */
/* fixed height, width from each frame's own ratio — the row's ragged right
   edge is the point, unlike the uniform browse tiles */

.stillrow{
  display:flex;gap:var(--gap);align-items:flex-start;
  overflow-x:auto;scrollbar-width:none;
  margin:0 calc(var(--gutter) * -1);
  /* 2px of slack under the frames. A still's height comes off its ratio and
     is fractional at almost every window width — 118.2 at 1440 — and it sat
     flush against the bottom of a scroll container whose client height had
     rounded to 162. The sliver that gets dropped is exactly the tile's 1px
     bottom border, so the lower row lost its stroke while every computed
     style still reported it. Slack costs 2px and removes the dependency on
     how an engine rounds a scrollport. */
  padding:0 var(--gutter) 2px;
  scroll-padding-left:var(--gutter);
}
.stillrow::-webkit-scrollbar{display:none}
/* Every horizontal strip dissolves at the edge it can scroll toward,
   rather than being cut off at the gutter. The mask goes on the strip,
   not on the cards: a card's caption carries no mask of its own and
   would sit at full strength beside a fading picture. sync() in the
   arrow wiring adds .atstart/.atend as a strip reaches either limit,
   which is what drops the fade once there is nothing more that way.

   A project's own gallery is left out: there the next frame bleeding in at
   full strength is the point. A profile's stills wall takes the fade like
   any other strip — it only runs sideways in the across layout, and there it
   is a strip like the rest. */
/* THE SAME EDGE THE STILLS ROW HAS.

   This had the fade taken off and its bleed with it, back when a cover was
   the film's own shape and the sliver crossing the edge read as a fault. The
   stills row beside it kept both, and it is plainly the better of the two:
   the picture dissolves into the board rather than stopping at a line, and
   the row says it continues without a word.

   So the strip bleeds a gutter either side again — which inside a board is
   exactly its padding, so the fade happens on the panel's own edge — and the
   mask rules further up apply as they always did. */
.pshelf .tiles{
  /* 2PX OF SLACK UNDER THE COVERS.

     overflow-x:auto computes overflow-y to auto as well, so the strip clips
     vertically whether it means to or not. A cover's height comes off its
     width and is fractional at almost every window size — 195.53 at 1512 —
     while the scrollport's client height rounds to 196, and the sliver that
     gets dropped is exactly the 1px bottom border. The stroke goes and every
     computed style still reports it there.

     The stills row two rules down has carried this same 2px since the same
     thing happened to it. The covers only need it now because they have a
     border again. */
  /* ONE NUMBER: THE BOARD'S PADDING, THE BLEED, AND THE GAP.

     The board holds its contents 20px off its border. The strip bleeds back
     out by the same 20 so a cover crossing the edge dissolves into the
     panel's own border rather than into a margin short of it — at 12 the
     fade finished 8px inside the line with a dead strip of board after it.

     And the cover after the last visible one begins exactly one gap along,
     so the gap has to be that same 20 or the difference shows: at 12 it was
     8px of a fourth cover hanging in the fade, present enough to see and
     faint enough to look broken.

     Left and right only, never the shorthand — `margin:0 …` writes
     margin-top too, which is how the stills wall lost the seam between its
     two rows. */
  margin-left:calc(var(--board-pad) * -1);
  margin-right:calc(var(--board-pad) * -1);
  padding-left:var(--board-pad);
  padding-right:var(--board-pad);
  padding-bottom:2px;
  /* and taken straight back off the outside. The slack has to be inside the
     scrollport to do its job, but it is not spacing and should not be read
     as any: left to stand it put the covers 22px off the board's bottom edge
     where the board view sits at 20, and the same block appeared to change
     its footing when all that changed was whether it could scroll.

     The board, list and single overrides zero margin and padding wholesale,
     so this comes off with the rest of it there and those views are
     untouched. */
  margin-bottom:-2px;
  scroll-padding-left:var(--board-pad);
  gap:var(--board-pad);
}

.featrow,.row,.tiles,.stillrow,.newrow{
  -webkit-mask-image:
    linear-gradient(90deg,transparent 0,#000 44px,#000 calc(100% - 56px),transparent 100%);
  mask-image:
    linear-gradient(90deg,transparent 0,#000 44px,#000 calc(100% - 56px),transparent 100%);
}
.featrow.atstart,.row.atstart,.tiles.atstart,.stillrow.atstart,.newrow.atstart{
  -webkit-mask-image:linear-gradient(90deg,#000 0,#000 calc(100% - 56px),transparent 100%);
  mask-image:linear-gradient(90deg,#000 0,#000 calc(100% - 56px),transparent 100%);
}
.featrow.atend,.row.atend,.tiles.atend,.stillrow.atend,.newrow.atend{
  -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 44px,#000 100%);
  mask-image:linear-gradient(90deg,transparent 0,#000 44px,#000 100%);
}
.featrow.atstart.atend,.row.atstart.atend,.tiles.atstart.atend,
.stillrow.atstart.atend,.newrow.atstart.atend{
  -webkit-mask-image:none;mask-image:none;
}

/* The seam BETWEEN the two rows of a wall, matched to the 12px the frames
   already sit apart sideways — a grid with one gap horizontally and another
   vertically is not a grid. Ten, not twelve: the row above carries 2px of
   slack under its frames so a fractional height cannot round their bottom
   border away, and that slack is part of the gap you see.

   The adjacent sibling matters. Unqualified, this also fell on the first row
   of the wall, where there is no seam to make — .rowwrap has layout
   containment, so the margin could not collapse out of it and simply pushed
   the whole wall 10px down its own section. A shelf sat 14px under the rule
   and the stills sat 24px under an identical rule, which is the kind of
   difference you see long before you can name it. */
.stillrow + .stillrow{margin-top:10px}
/* One frame, repeated. The width comes from the column — a tile's width, so
   the wall lines up with the work below it — and the shape is the same for
   every frame in it.

   Letting each frame keep its own ratio made the rows ragged along the
   bottom, and a ragged bottom is what put 46px of air under a 1.85 and 14px
   under a 4:3 in the same seam. The gap was 12px the whole time; it only
   read as more because the frames above it stopped at different heights. A
   wall has one gap, sideways and down, and that costs a crop. */
/* A WALL IS LEVEL ALONG THE TOP AND THE BOTTOM, NOT DOWN THE SIDES.

   Every frame the same height, each as wide as its own shape makes it. The
   seam needs equal heights and nothing else, so this is what lets a 2.35 be
   a 2.35 in a two-row wall: the raggedness moves to the right-hand end,
   where a strip you scroll has no edge to keep anyway.

   The crop was never the point. Fixing the WIDTH is what forced it — three
   across a column is a tidy figure and a frame given a tidy width has only
   its height left to give, so the shapes went. Fix the height instead and
   the same three-across figure decides it: a 16/9 frame comes out exactly
   the size it always was, and the other shapes flex around it. */
.pstill{
  flex:none;
  height:calc((40vw - 18px) * 9 / 16);
  width:auto;
  aspect-ratio:var(--ar, 16 / 9);
  /* A FRAME IS THE PICTURE, NOT A FRAMED PICTURE.

     The hairline and the rounded corner were both doing the same job —
     saying where one still stops and the next begins — on a wall where the
     stills are already different shapes and the gaps between them are
     already the separation. Two devices for a break that needed neither, and
     an outline round every frame turned a run of pictures into a run of
     objects.

     Square and bare. The covers keep their line and their corner: a cover
     stands for a film and is one of a set of equals, which is exactly the
     thing a frame is not. */
  /* ROUNDER THAN A COVER, AND NO LINE AT ALL.

     Square was right while the frames still carried a hairline — an outline
     and a corner are two devices saying the same thing, and together they
     turned a run of pictures into a run of objects. With the line gone the
     corner can carry it alone, and carry more of it: 8px, where a cover
     keeps 6.

     That order is deliberate. A cover is bounded — it has an edge drawn
     round it because it stands for a whole film. A still is just a picture,
     and the softer corner is what says so without adding anything to it. */
  border-radius:8px;background-color:var(--plate);
  border:0;
  cursor:zoom-in;
}
/* the height a frame at a third of the column used to have, so the wall
   stands exactly as tall as it did and only the widths moved */
@media (min-width:900px){ .pstill{height:calc((100cqw - 24px) / 3 * 9 / 16)} }
/* the pair's arrows sit on the seam between the two rows */
.stillpair{position:relative;container-type:inline-size}

/* ---------------- browse rows ---------------- */
.row{
  /* THE PROFILE'S RULES, ON THE PAGE THAT LEADS TO IT.

     --gap rather than a literal 12, align-items rather than a stretch: both
     are what .tiles does on a profile, and a shelf of covers is the same
     object wherever you meet it. Tops line up and bottoms do not, which is
     what a row of covers at their own shapes looks like. */
  display:flex;gap:var(--gap);align-items:flex-start;
  overflow-x:auto;scroll-snap-type:x proximity;
  scrollbar-width:none;
  /* bleed past the gutter so the cut edge reads as "more" */
  margin:0 calc(var(--gutter) * -1);
  padding:0 var(--gutter);
  scroll-padding-left:var(--gutter);
}
.row::-webkit-scrollbar{display:none}
.card{flex:none;display:block;width:calc(40vw - 18px);scroll-snap-align:start}
.card .frame{
  /* .tile .frame, to the letter: 4:3, the same corner, and the same hairline
     — a cover is a picture on a dark ground, and a dark cover with no edge
     has no bottom to it. */
  width:100%;aspect-ratio:4 / 3;border-radius:6px;
  background-color:var(--plate);
  border:1px solid rgba(var(--silk),.42);
  background-clip:padding-box;
}
/* the tile's caption size. A card is four across where a profile tile is
   three, so it was naming a smaller picture in bigger type. */
/* 12, the board's own step and the same gap the covers keep from each other
   — a caption closer to its picture than the pictures are to one another
   reads as attached to the wrong thing. The profile's tiles take it too, so
   a film is captioned identically wherever it appears. */
.card .t{display:block;font-size:15px;font-weight:600;margin-top:var(--gap);letter-spacing:-.005em}
.card .c{display:block;font-size:15px;color:var(--ink2);margin-top:3px}
@media (min-width:900px){
  /* THREE, THE WIDTH A COVER TAKES ON A PROFILE.

     It said four here and claimed to be the profile's figure; the profile's
     is (100% - 24px) / 3. Four made a browse cover a quarter of the column
     against a third on the page it links to, so the same film was two sizes
     depending which shelf you found it on. Three, with the two gaps between
     them, and it is one size everywhere. */
  .card{width:calc((100% - 24px) / 3)}
}

/* The gap under the bar is the first field's own top padding now, the same
   as the profile form — this page used to ask for it separately because its
   first line was a bare heading rather than a field. */

/* ---------------- a shelf of people ---------------- */
/* Rides on .row, so the bleed, the fade, the snap and the arrows are the
   ones the work shelves already use — this only says what a card looks like
   when the thing in it is a person. */
.pcard{flex:none;display:block;width:calc(40vw - 18px);scroll-snap-align:start;text-align:center}
.pcard .face{
  /* AS TALL AS A COVER, WHATEVER THE CARD IS.

     A circle filling the card's width would stand a third taller than the
     covers on the shelf above, and a circle capped at a fixed size floated
     in the middle of the card as soon as the card grew. 75% is the cover's
     own height — a 4:3 frame is three quarters as tall as it is wide — so
     the face matches it at every width and the two cards come out the same
     height without either number being written down. */
  width:75%;aspect-ratio:1;border-radius:50%;margin:0 auto;
  background-color:var(--plate);
  /* a border rather than a shadow — the strip has no vertical padding for a
     shadow to spread into, the same reason the members strip draws one */
  border:1px solid rgba(var(--silk),.28);
  background-clip:padding-box;
}
.pcard .t{display:block;font-size:15px;font-weight:600;margin-top:var(--gap);letter-spacing:-.005em}
.pcard .c{display:block;font-size:15px;color:var(--ink2);margin-top:3px}
@media (min-width:900px){
  /* A FIFTH, SO ALL FIVE ARE THERE.

     A work shelf shows four of five and cuts the last one, because the cut
     is the offer — there is more this way. A shelf of people is not an
     offer, it is a set: these are the five who just joined, and showing four
     of them with the fifth sliced down the middle says the list carries on
     when it does not. So the card is a fifth of the shelf less the four gaps
     between, and the row ends where the shelf ends.

     The face stays 75% of whatever that comes to, so it is still exactly as
     tall as a cover — the cards are smaller than the work above them, and
     still built to the same rule. */
  .pcard{width:calc((100% - 48px) / 5)}
}

/* ---------------- a shelf that runs down the page ---------------- */
/* THE HOME SHELVES STACK.

   The same card, unchanged — cover, title, credit — simply dropping down the
   page instead of running across it. This is the profile's column layout,
   which is where the treatment was worked out, brought to the page that leads
   to it.

   Nothing moves sideways any more, so everything that existed to say "there
   is more this way" goes with it: the bleed past the gutter, the fade at the
   cut edge, the snap, the arrows. A strip that cannot scroll and still wears
   a fade at its right edge is a page telling you about content it does not
   have. */
.row.col,.featrow.col{
  display:flex;flex-direction:column;align-items:center;gap:var(--gap);
  overflow:visible;scroll-snap-type:none;
  margin:0;padding:0;
  -webkit-mask-image:none;mask-image:none;
}
/* On a phone the column IS the card — a third of a 390px screen is a
   thumbnail, not a cover, and there is no room either side to give back. */
.row.col .card,.featrow.col .featcard{
  flex:none;width:100%;scroll-snap-align:none;
}
@media (min-width:900px){
  /* The width a cover takes down the middle of a profile, (100% - 24px) / 3,
     rather than the quarter it takes four-across. A cover centred in a column
     is the same object on both pages, and showing it at two sizes would be
     the only reason to notice which page you were on. */
  .row.col .card{width:calc((100% - 24px) / 3)}
}
/* Featured keeps the whole column. Its entire claim is size — at a third it
   would be an ordinary card with bigger type on it, which is not a feature.
   The 34px it used to give up sideways was room for the next one to leak
   into view, and there is nothing across from it to leak. */
.featrow.col .featcard{width:100%}

/* profile shelves: image only, same rhythm as the browse rows */
.tiles{
  display:flex;gap:var(--gap);
  overflow-x:auto;scroll-snap-type:x proximity;scrollbar-width:none;
  margin:0 calc(var(--gutter) * -1);
  padding:0 var(--gutter);
  scroll-padding-left:var(--gutter);
}
.tiles::-webkit-scrollbar{display:none}
/* A COVER IS 4:3, WHATEVER SHAPE THE FILM IS.

   A still is a frame out of the film and has to be the shape it was shot; a
   cover is not a frame, it is the picture that stands for the work, and a
   shelf of them is a row of equal things. Left at each film's own ratio the
   row came out ragged — a scope cover half the height of the 4:3 beside it —
   and the eye read the shapes instead of the work.

   Every surface that shows a cover takes the same window: browse cards, the
   profile's shelf tiles, the preview in the two forms, and the thumbnail in
   the organize sheet. The picture fills it and is cropped to the centre,
   which is what the cover's own crop is for. Stills keep their true shapes
   everywhere they appear. */
.tile{flex:none;display:block;width:calc(40vw - 18px);scroll-snap-align:start}
.tile .frame{
  /* 6, not 12. A corner that round on a picture the size of a cover reads as
     a card with a picture in it rather than as the picture — and it was the
     only radius on the site that size: the stills, the featured cover and
     the preview in the forms are all 6 or under. One corner for every
     picture, and the roundest thing on a shelf stops being the box. */
  width:100%;aspect-ratio:4 / 3;border-radius:6px;
  background-color:var(--plate);
  /* THE HAIRLINE EVERY PICTURE IN THE APP WEARS.

     This said border:0, on the argument that an outline round every cover
     turns a wall of work into a set of framed items. That held while covers
     were each their own shape and the ragged edges did the separating; at a
     uniform 4:3 they no longer do, and a dark cover on the dark ground ran
     out with nothing to stop it.

     Silk at .42 over background-clip:padding-box — the same line the stills
     and the cover preview carry, so a picture is bounded the same way
     wherever it appears. */
  border:1px solid rgba(var(--silk),.42);
  background-clip:padding-box;
}
/* tops line up, bottoms do not — the covers are different heights now, and
   hanging them from a common top edge is what keeps the row a row */
.tiles{align-items:flex-start}
/* A caption, not a heading. At 17px and weight 600 it was the same type as a
   section title and competed with the picture it belongs to; at 15 and
   regular it reads as the picture's name. The two lines matter more than the
   sizes: with covers at different heights, a single line left every tile
   ending at a different place and nothing to end on. A title with the film's
   kind and year under it gives each tile a block of its own, and the ragged
   bottoms start looking meant. */
/* the caption, under the cover — the browse card's own treatment, so a film
   reads the same way on a profile as it does on a shelf you found it on.

   12 rather than the card's 9: on a shelf the covers are set in a page and
   the caption is close because there is air all round the pair. In a board
   the pictures sit 12 apart from each other, and a name closer to its cover
   than the covers are to each other reads as attached to the wrong thing. */
.tile .t{
  display:block;margin-top:var(--gap);
  font-size:15px;line-height:1.25;letter-spacing:-.005em;color:var(--ink);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.tile:hover .t{text-decoration:underline;text-underline-offset:3px}
/* the scrim it used to fade with is gone, so there is no transition left to
   silence */
@media (min-width:900px){
  /* Three across, not four — the covers carry their own ratios now, and at a
     quarter of the column a 2.35 came out 68px tall, which is a strip rather
     than a picture.

     Three fill the column exactly, the way the home page's rows do with
     four: the next cover clears the edge by a sliver and the strip's own
     fade dissolves the one before it. SHELF_COLS in the js is the same
     figure — a board has to hand its items out in as many columns. */
  /* measured as a count of gaps, so the row still fits when the gap moves —
     24 was two gaps at the 12px the strips used to keep */
  .tile{width:calc((100% + var(--board-pad) - var(--board-pad) * 3) / 3)}
}

/* ---------------- profile ---------------- */
/* the wrapper holds the column width so the contact row can be positioned
   against the banner's own right edge */
/* The banner runs up behind the bar. The bar is a scrim that fades to
   nothing at its own bottom edge, and a scrim needs something under it —
   with the picture starting where the bar ends it was fading over the page's
   own ground, which is a black gradient on black. Pulled up by the bar's
   height the fade happens over the photograph, which is the whole point of
   it, and the nav sits in the picture rather than above it.

   A project's gallery is deliberately not pulled up the same way: a banner
   is wide and shallow with room to spare at the top, where a 4:3 frame would
   put the work itself under the nav. */
/* The gutter, same as everything else. The wrapper holds the column so the
   contact row can sit against its right edge, but the picture inside it now
   takes the gutter too — it used to run the full width of the column while
   the rule under the tabs, the covers and the name all sat 20px inside it,
   so the one element with no border was the one sticking out past all of
   them. The strips still bleed to the column edge, but that bleed is scroll
   padding with nothing drawn in it. */
/* The one thing on the page allowed past the column, and only just. A gutter
   either side of the measure — enough that the picture is plainly wider than
   the work under it and reads as the ground the page opens on, while
   everything with a measure stays inside the rule.

   The min() is the phone case, where --col is already the whole width and
   adding to it would push a horizontal scrollbar under the page. */
/* The one thing on the page allowed past the column, and only just — a
   gutter either side of the measure. Enough that the picture is plainly
   wider than the work under it and reads as the ground the page opens on,
   while everything with a measure stays inside the rule.

   The min() is the phone case, where --col is already the whole width and
   adding to it would push a horizontal scrollbar under the page. */
/* THE BANNER STOPS WHERE THE PAGE STOPS.

   It was a gutter wider than the column on each side — 938 against the 858
   every other thing on the page lines up to: the name row, the rules over
   the shelves, the tabs, the covers. Forty pixels is not a bleed, it is a
   near miss, and a near miss reads as a mistake where a full bleed would
   have read as a decision.

   So it takes the column's own width. It still runs up under the bar, which
   is a different rule and still a good one — the work starts at the top of
   the window. */
.bannerwrap{
  /* it fills the board it is in, and the board is already the column's
     width — the max-width and the pull under the bar were both this
     element's way of being the page's own top edge, which it is not any more */
  position:relative;width:100%;padding:0;margin:0;
}
/* A BANNER THAT MOVES IS THE SAME BANNER.

   Everything about the frame — the 2.76, the scrim, the corners it does not
   have, the way it meets the top of the window — belongs to .banner and is
   the same whether what is inside it is a photograph or eight seconds of
   footage. Only the inside differs, so only the inside is written here.

   overflow:hidden because the clip is laid over the frame at whatever the
   crop asked for, which past 100% hangs off both edges; and pointer-events
   off it so the banner is still a picture you look at rather than a player
   with a scrubber under your cursor. It has no controls, but a <video> takes
   the pointer all the same. */
/* A COVER THAT MOVES. The frame keeps its ratio, its corner and its glow —
   what changes is that a clip is laid inside it at the crop the still would
   have had, and clipped by the box rather than fitted to it, so the two
   render identically at rest. */
/* No overflow here. The glow under every frame is a ::before with negative
   insets — it is an OUTER glow, it is supposed to leave the box — and
   hiding overflow on the frame cut it off flush with the picture's edge.
   The clip is cut back by .clipbox inside instead, which is the only thing
   that needs cutting. */
.ismotion{position:relative}
.ismotion > .clipbox{
  position:absolute;inset:0;overflow:hidden;border-radius:inherit;
  /* the frame's own corner, so the clip is rounded exactly where the
     picture beside it is */
}
.ismotion > .clipbox > video{position:absolute;display:block;height:auto;pointer-events:none}
.banner.motion{overflow:hidden}
.banner.motion > video,
.cropbox.motion > .cropvid{
  position:absolute;display:block;height:auto;pointer-events:none;
}
/* the framing window: the clip is dragged under it like a picture, so the
   box has to clip and the hint has to stay on top of what it is over */
.cropbox.motion{overflow:hidden}
.cropbox.motion > .crophint{position:relative;z-index:1}
.banner{
  aspect-ratio:2.76 / 1;   /* Ultra Panavision — a person, not a project */
  background-color:var(--plate);
  /* Between centre and bottom: the crop still takes more off the top,
     where the nav sits, without dropping to the floor of the frame. */
  background-position:center 68%;
  /* Square corners, no hairline, no mask. The picture is its own edge: it
     runs the full width of the column and meets the top of the window, and
     nothing is drawn around it — the bottom scrim below is the only thing
     that touches it. A radius on a frame this size only reads as a card
     someone laid on the page. */
  position:relative;
}
/* A DARK FADE, NOT A DISSOLVE.

   A mask takes the picture away — the pixels go transparent and whatever is
   behind them shows through, which on a page of one flat grey means the
   bottom of the banner simply stops existing. This is a scrim instead: the
   photograph stays, and it is darkened down to the page's own ground so it
   arrives at the page rather than disappearing before it. The difference is
   visible in a bright crop, where a dissolve leaves a pale haze thinning out
   and a scrim keeps the shapes and puts the light out.

   It is the top bar's scrim, turned over: same stops — .74 at the edge, .36
   at 56% of the way in, nothing at the end — over the same distance, the
   bar's own height. The banner is darkened by the bar along its top edge, so
   anything different along the bottom would read as two unrelated shadows on
   one picture.

   The colour is the page's own grey rather than the bar's black. Black is
   right at the top, where the scrim's job is to hold nav type over a bright
   crop; at the bottom the job is to arrive at the page, and the page is
   #121212. Black there put a darker band under the picture than the ground
   it was sitting on, which reads as a shadow rather than an ending.

   Full strength at the edge, not the bar's .74. The bar stops short because
   a photograph has to stay readable under it; the bottom has the opposite
   job, and stopping short left a step of four greys between the last row of
   picture and the page. At 1 it lands on the ground exactly and there is no
   edge to see. The midpoint scales with it — .49 is .36 in the same
   proportion — so the curve still matches the top's, only louder. */
/* Three edges of the same fade, stacked as three gradient layers on one
   pseudo-element. The fourth is the bar's own scrim, which already covers
   the top. Every one runs the same distance — the bar's height — so the
   picture sits in an even band of ground on all four sides rather than in a
   frame of four different widths.

   They add where they meet, so the bottom corners go down faster than either
   edge alone. That is right: a corner is where two edges are ending at once
   and it should read as the deepest part of the fade. */
.banner::after{
  content:"";position:absolute;inset:0;
  pointer-events:none;
  /* One length and one curve for all three, written once as --edge-fade and
     --edge-mid so the bottom and the sides cannot drift apart — they were
     already the same expression three times, which is the kind of sameness
     that survives until someone edits one of them.

     Longer and heavier than the bar's own scrim: half again its distance,
     and holding .62 at the midpoint where the top holds .36. The top has to
     stay light because nav type sits in it; these have nothing to protect
     and only have to put the picture down. */
  --edge-fade:calc(var(--bar) * 1.5);
  /* An eased ramp rather than a straight one — five stops approximating a
     smoothstep, ending in a long thin tail.

     The corners are why. Three linear bands are three layers, and where two
     of them cross their darkening compounds: leaving a corner you leave both
     bands at once, so the dark region ends over a shorter run diagonally
     than it does along either edge, and that shows up as a hard L. Two soft
     tails multiplied stay soft, so the same crossing comes out as a curve.
     The edges lose nothing — the ramp still starts at full --bg on the line
     and still reaches nothing at --edge-fade. */
  /* --board, not --bg. The banner sits on a panel now, and a scrim that
     ends on the page's own grey put a band four shades darker than the board
     along the bottom of the picture — an ending that read as a shadow. The
     stops and the curve are unchanged; only what they arrive at moved. */
  /* THE BOTTOM ONLY. THE SIDES HAVE NOTHING TO ARRIVE AT.

     There were three of these, and all three made sense while the banner was
     the page's own top edge: it ran the full width of the window with the
     grey either side of it, and a photograph that simply stopped at the
     window edge stopped like a cut. The scrim put it down instead.

     It is in a panel now, with a border and a radius, and .board clips it —
     so the sides already end on something. The two vertical bands were
     dissolving the picture into an edge that was going to hold it anyway,
     and what that buys is 84px at each end where the work is dimmed for no
     reason a reader could name. Nothing "arrives" there; it is simply less
     picture. The panel's own edge is a better ending than a fade to the
     colour of the thing doing the clipping.

     The bottom stays, and is the one that was always doing real work: under
     it is the name and the bio on the board's own ground, and the picture
     has to become that ground rather than stop against it. There is no line
     there to end on, which is exactly the difference.

     Put the two back and --edge-fade drives all three again; the curve was
     written once for that reason and is untouched. */
  background:
    linear-gradient(0deg,
      var(--board) 0, rgba(var(--board-rgb),.88) 14%, rgba(var(--board-rgb),.58) 34%,
      rgba(var(--board-rgb),.26) 60%, rgba(var(--board-rgb),.07) 80%, var(--board-0) var(--edge-fade));
  background-size:100% var(--edge-fade);
  background-position:bottom left;
  background-repeat:no-repeat;
}

/* No bleed: the banner ends where the column ends, like everything else
   on the page. */
/* Who this is, at the size that says it is a person rather than an account.
   At 62px with a 23px name the block read like a row in a follower list — a
   small round picture, a small name, three controls, next. The picture goes
   to 200 and the name to 30, which is the same size a film's title takes on
   its own page: a person is the subject of this page the way a project is
   the subject of that one.

   Bottom-aligned, and sitting deeper into the banner. The picture breaking
   the frame is what stops the two reading as a header bar and a row beneath
   it, and aligning on the baseline rather than the middle puts the name
   against the foot of the picture instead of floating in the middle of it. */
/* The picture's size and the gap beside it are declared on the page, not
   inside the row, because the bio lines up with the name and has to know
   both. One number to change, and the indent follows it. */
/* A portrait rectangle, 3:4, with a corner rather than a circle. A round
   crop is what an account looks like; an upright frame is what a person on a
   film's page looks like.

   138 x 175, which is what tmdb serves a cast photo at — w138_and_h175_face,
   and w276_and_h350 for retina. Not 4:5 and not 2:3 but 0.789, a ratio that
   exists because it is the size a face reads at in a row of faces, which is
   the same job this picture has.

   The width is the number everything else reads — the row's gap, the bio's
   indent, the lift into the banner — so the height is derived from it and
   only one value ever moves. */
/* A BOARD: A PANEL A SHADE OFF THE GROUND.

   Spotify's shape, and it is worth naming why it works — a page of sections
   separated by air is a document, where a page of panels is a set of things.
   Two claims live on a profile, "this is the person" and "this is the work",
   and a panel each says that before you read a word.

   Silk at .04 over #121212 lands about three points lighter — enough to read
   as a surface in a dark room and not enough to grey the pictures sitting on
   it. The hairline is what keeps the edge from dissolving where the panel
   meets the ground behind it.

   overflow:hidden so the banner can run to the panel's own corners: the
   picture is clipped by the board's radius rather than carrying one. */
.board{
  position:relative;
  background:var(--board);
  border-radius:var(--board-r);
  box-shadow:var(--board-lift);
  overflow:hidden;
}
/* the boards sit in the column the page already has */
.profileview > .board,
.project > .board{
  max-width:min(calc(var(--col) - var(--gutter) * 2), calc(100vw - var(--gutter) * 2));
  margin:0 auto;
}
/* the same field the profile's boards sit in: one gutter between them, and
   one from the top of the page */
/* THE GALLERY'S BOARD DOES NOT CLIP.

   .board hides its overflow so a banner can be cut by its corners. This one
   has no banner, and clipping it would round off the frame's own corners
   against the panel's.

   It no longer buys the fill any room, though: the mask further down ends
   everything at the board's edge, so the colour that used to run 266px onto
   the ground now lives in the 20px of padding. That was the price of the
   picture not travelling past the panel when you click through, and it is
   the mask that would have to move to buy the spread back. */
/* THE FILL STAYS IN THE PANEL.

   .boardgal used to be overflow:visible so the colour could spread onto the
   page around it. It went out about 295px sideways and 64 below, which is
   fill landing in the gutter and in the empty third beside the board —
   colour on the page's own ground, belonging to nothing, and what made the
   panel read as bleeding rather than lit.

   So the override is gone and .board's own overflow:hidden holds it. The
   spread is not wasted: the strip still pads itself by --ih and --vf, so the
   fill has its full run inside the panel and simply stops at the border
   instead of crossing it. The board's radius clips it too, which is the same
   corner every other panel on the site ends on.

   The mask below still ends the picture at the board's edge. That was never
   about the glow — it stops a frame travelling across the ground beside the
   panel while you click through — and it is still doing that job. */
/* AND THE GLOW SITS ON THE BOARD, NOT UNDER IT.

   The fill paints at z-index -1 so it goes behind the frame that throws it.
   With no stacking context between them, "behind the frame" also meant
   behind the board's own background — the colour only appeared where it had
   escaped the panel, so a board lit from inside came out as a halo round the
   outside with a flat grey middle.

   z-index:0 on the strip makes it the context the -1 is measured against, so
   the glow is behind the pictures and in front of the panel. */
.project .boardgal .stills{position:relative;z-index:0}

/* AND IT STOPS AT THE BOARD, EVEN THOUGH ITS BOX DOES NOT.

   The strip clips to its own padding box, and that box runs 266px past the
   panel on either side so the fill has somewhere to go. A frame is therefore
   visible the whole way across that band — standing still you never see it,
   because the gap is set so the next frame is parked outside the clip, but
   click through and the picture travels across the ground beside the board
   on its way in and out.

   The mask ends the picture where the board ends. Solid across the frame
   itself, then down to nothing across the panel's own 20px of padding, so a
   still appears at the board's edge and not before it.

   THE STOPS ARE IN cqw, NOT PER CENT. The strip's padding is --reach of its
   containing block — 35% of 818 — while a percentage in a mask resolves
   against the strip's own border box, which is the 1391 the padding just
   made. The same 35% would land 200px out. cqw asks the container, which is
   the basis the padding used. */
.project .boardgal .stills{
  --galstop:calc(var(--reach) * 100cqw);
  -webkit-mask-image:linear-gradient(90deg,
    transparent 0,
    transparent calc(var(--galstop) - var(--board-pad)),
    #000 var(--galstop),
    #000 calc(100% - var(--galstop)),
    transparent calc(100% - var(--galstop) + var(--board-pad)),
    transparent 100%);
          mask-image:linear-gradient(90deg,
    transparent 0,
    transparent calc(var(--galstop) - var(--board-pad)),
    #000 var(--galstop),
    #000 calc(100% - var(--galstop)),
    transparent calc(100% - var(--galstop) + var(--board-pad)),
    transparent 100%);
}
.project > .board{margin-top:var(--gutter)}
.project > .board > .wrap{max-width:none;padding:var(--board-pad)}
/* THE PADDING COMES FROM ONE PLACE OR THE OTHER, NEVER BOTH.

   The gallery board is a direct child of the page and pads through its wrap.
   The two below it sit in a field, and a board in a field is padded by the
   field's own rule — so their wraps take none, or the title starts 40px in
   while the gallery's starts at 20. */
.project .boardfield > .board > .wrap{max-width:none;padding:0}
/* THE FIRST BOARD IS THE TOP OF THE PAGE.

   It is the banner's old trick and it belongs to the board now: pulled up by
   the bar's own height so the panel starts at the top of the window and the
   nav sits inside it, over the picture. That is what the bar's scrim is for
   — it was written to hold nav type over a bright crop, and it only has a
   crop to sit on if the picture reaches it.

   Square along the top, then. A rounded corner tucked behind the bar is a
   corner nobody sees, and the two millimetres of it that peek out either
   side read as a mistake. The board keeps its curve where it can be seen —
   the two bottom corners, and all four of the one below. */
.profileview > .board:first-child{
  margin-top:calc(var(--s-open) - var(--bar));
  border-top-left-radius:0;border-top-right-radius:0;
}
/* THE GUTTER, THE SAME NUMBER THAT HOLDS THEM OFF THE PAGE'S EDGE.

   It was 48 — --s-sect, the gap between blocks that have only air to tell
   them apart, which these do not: a border, a corner and a shade of their
   own are already doing that.

   --gutter is what the boards keep from the edge of the window on either
   side. Using it here makes the space around a board one number in every
   direction. */
.profileview > .board + .board{margin-top:var(--gutter)}
/* THE SAME INSET ON ALL FOUR SIDES.

   The board is the padding now — the wrap inside it stops being the column
   and becomes the margin round the panel's contents. One number on every
   edge, so the distance from the work to the border is the distance from the
   name to it.

   It read `padding:0 26px` with the top and bottom set by two rules below,
   which the shorthand quietly beat — three classes against two — so the
   sides were 26 and the ends were 0, and what stood in for them was whatever
   margin the first and last child happened to carry: 28 under the id block,
   12 over the shelf. Those are the margins turned off below. */
.profileview > .board > .wrap{max-width:none;padding:var(--board-pad)}
/* the banner is this board's own top edge and runs to it, so nothing is
   asked of the space above the id block */
.boardid > .wrap{padding-top:0}
/* their own spacing was standing in for the padding that was missing; with
   the padding there it would be counted twice */
.boardid .idrow{margin-bottom:0}

/* A SHELF IS A BOARD.

   Each row carries the panel itself rather than sitting in one, because a
   wrapper would become the parent of every .pshelf — and the drag that
   reorders rows finds its siblings through that shared parent. One row per
   wrapper is one row per parent, which is no siblings and no reordering.

   The field they sit in takes no width of its own: the boards are the
   column, spaced by the same gutter that holds them off the window. */
/* A FIELD OF BOARDS: the column, holding panels instead of type, one gutter
   between each. Home lays its shelves out this way and a profile lays its
   rows out this way, so the rule is not either page's. */
/* BOARDS FLOW, THEY DO NOT ONLY STACK.

   The field is a wrapping row rather than a column, so two panels that
   together take a line will sit on it — a wide one and a narrow one — and
   anything full-width takes a line of its own. One gap governs both
   directions, which is what makes the grid read as a grid: the space between
   two boards side by side is the space between two boards stacked. */
.boardfield{
  /* the same column the boards above it keep — it is a .wrap by class, so
     its own max-width and padding have to be turned off before it can hold
     boards instead of type */
  max-width:min(calc(var(--col) - var(--gutter) * 2), calc(100vw - var(--gutter) * 2));
  margin:var(--gutter) auto 0;
  padding:0;
  display:flex;flex-wrap:wrap;align-items:flex-start;gap:var(--gutter);
}
/* a board says nothing about its width and takes the whole line */
.boardfield > .board,
.boardfield > .pshelf{width:100%}
/* two that share a line split it, less the one gap between them, so the pair
   lands exactly on the column's two edges */
.boardfield > [data-span="wide"]{width:calc((100% - var(--gutter)) * 2 / 3)}
.boardfield > [data-span="narrow"]{width:calc((100% - var(--gutter)) / 3)}
/* WHERE A DROP WILL LAND, WHILE YOU ARE STILL HOLDING IT.

   A board being dragged over the right third of another lights that board's
   right edge — the edge the dragged one is about to take. The row-reorder
   marker is a line across the top; this is a line up the side, and the
   difference in direction is the difference in what will happen. */
.profileview .boardfield > .pshelf.pairing{
  box-shadow:var(--board-lift), inset -3px 0 0 var(--ink);
}
/* on home the panel is a <section> of its own; on a profile the row carries
   it, so that the drag which reorders rows still finds its siblings */
.boardfield > .board{padding:var(--board-pad)}
.boardfield > .board > .sechead:first-child{margin-top:0}
/* THE GAP IS MEASURED FROM THE LETTERS, NOT FROM THE BOX ROUND THEM.

   A line box carries the font's own leading — about 5px of empty above the
   capitals and below the descenders at 15px — and every gap here was being
   set from that box. So "20px above the label" arrived as 25px of visible
   air, and the rule below sat 21.4px under a `g` but 24.8 under an `s`: two
   labels on the same band, looking a step apart.

   text-box trims the leading away. `cap` puts the top of the box on the
   capitals; `alphabetic` puts the bottom on the baseline.

   The baseline, not the descender line. Almost every letter in a shelf's
   name ends there — the o, the l, the n, the d — so that is the edge the eye
   reads as the bottom of the word. Measuring to the descender instead meant
   the band was set by the one letter in "Color Grading" that hangs below it,
   which pushed the whole name up in its own band and left it sitting high.
   The g still descends into the space under the line, which is what a g does
   and what the space is there for.

   So: 20 from the board's edge to the capitals, 20 from the baseline to the
   rule, and the name sits where those two agree. */
/* every label that sits on a rule, not only the ones inside a board — the
   drawers on a project page carry the same hairline under the same kind of
   name, and a heading whose spacing is measured from its leading is measured
   from nothing you can see */
.sechead h2,
.sechead .tag,
.shelftabs .xtab,
.dsum{
  text-box:trim-both cap alphabetic;
}
/* the tag rides the heading's baseline, and an untrimmed box beside a
   trimmed one is a taller flex item — which pushed the name it sits next to
   2.4px down the board while every other heading sat at 20 */

/* HOME'S HEADING RULE, ON THE SAME BAND.

   A shelf's tabs keep --board-pad above their divider and below it; this
   heading was on 10 and 14, numbers from when it was a section on a page
   rather than the top of a panel. Same figure both sides now, so a board
   with a heading and a board with tabs open identically.

   The rule is one of the two pixels above it, which is why the padding is
   the band less one. */
.boardfield > .board > .sechead{
  padding-bottom:calc(var(--board-pad) - 1px);
  margin-bottom:var(--board-pad);
}
/* THE GAP IS THE BLEED, ON EVERY STRIP IN EVERY BOARD.

   Two things have to be true at once and one number settles both.

   The strip bleeds to the board's own border, so a picture crossing the edge
   dissolves into the panel rather than into a margin. Bleeding less than the
   padding — which is what matching the gap did — left the fade finishing 8px
   inside the border with a dead strip of board after it, which reads as the
   row being badly centred.

   And the next item begins one gap after the last one ends, so a bleed wider
   than that gap puts the difference on screen: 8px of a fourth cover showing
   through the fade, present enough to see and faint enough to look broken.

   Setting the gap to the bleed rather than the bleed to the gap satisfies
   both — the fade reaches the border, and the item after the last visible
   one starts exactly where the strip stops being visible. */
.boardfield > .board .row,
.boardfield > .board .stillrow{
  /* LEFT AND RIGHT ONLY — NEVER THE SHORTHAND.

     `margin:0 …` also writes margin-top, and this rule is more specific than
     the one giving the second stills row its 10px seam. The two rows of the
     wall snapped together at 0 and the pair stopped being a pair. */
  margin-left:calc(var(--board-pad) * -1);
  margin-right:calc(var(--board-pad) * -1);
  padding-left:var(--board-pad);
  padding-right:var(--board-pad);
  padding-bottom:2px;
  scroll-padding-left:var(--board-pad);
}
/* THE WIDE GAP IS THE CARD ROWS' ALONE.

   A row of cards takes it so the item after the last visible one starts
   exactly where the strip stops, and nothing hangs half-faded over the edge.

   The stills wall cannot use it and does not want to. Its frames are every
   width, so something is always crossing — there is no leak to close — and
   its horizontal gap is set against the 10px seam between its two rows, a
   pair of numbers chosen together so the wall reads as one block. Widening
   one of them alone is what pulled it apart. */
/* ONE FIGURE FOR THE WHOLE BOARD.

   20 everywhere it can be: the border to the work, the rule to the name
   above it and the covers below it, and one cover to the next. A panel where
   every distance is the same distance needs no explaining — you read the
   pictures, not the spacing between them.

   The width sum above collapses when the gap equals the bleed:
   (W + bleed − 3g) / 3 becomes (W − 2g) / 3, which is plainly three covers
   and the two gaps between them. It only looks elaborate while the two are
   different, and it is written the long way so it still holds if they part
   again.

   The stills wall keeps 12: its horizontal gap is set against the 10px seam
   between its two rows, a pair chosen together. */
.boardfield > .board .row{gap:var(--board-pad)}
/* AND THE CARDS ARE MEASURED FROM THAT GAP.

   Their widths were written as "the column less 24px, over three" and "less
   48px, over five" — 24 and 48 being two gaps and four gaps at the 12px the
   strips used to keep. Widen the gap and those figures are simply wrong: the
   five faces stopped fitting and the fifth hung 142px over the edge.

   Written as a count of gaps instead, so the row fits whatever the gap is
   and one number still settles the whole strip. */
@media (min-width:900px){
  /* WIDE ENOUGH THAT THE NEXT ONE STARTS OFF THE EDGE.

     The strip shows its content box plus one bleed. The fourth cover begins
     three gaps and three covers along. Setting those equal and solving for
     the cover:

        3w + 3g = W + bleed      →      w = (W + bleed − 3g) / 3

     which is a little wider than a third, not narrower — the third cover
     runs into the bleed, and the gap after it lands exactly on the edge.
     Five across is the same sum with a five in it. */
  .boardfield > .board .card{
    width:calc((100% + var(--board-pad) - var(--board-pad) * 3) / 3);
  }
  .boardfield > .board .pcard{
    width:calc((100% + var(--board-pad) - var(--board-pad) * 5) / 5);
  }
}
/* ---------------- explore, as boards ---------------- */
/* EXPLORE JOINS THE OTHER TWO BROWSING PAGES.

   Home and a profile are both a field of panels; explore was still a
   document — headings down one column with air between them. The rules
   below are only the joins: what a strip, a drawer or a list has to give up
   to sit inside a panel instead of on a page. */

/* The faces bleed to the panel's edge like every other strip in a board.
   Their own rule bleeds to the page gutter, which is a wider number and
   would have hung the fade outside the panel. */
.boardfield > .board .newrow{
  margin-left:calc(var(--board-pad) * -1);
  margin-right:calc(var(--board-pad) * -1);
  padding-left:var(--board-pad);
  padding-right:var(--board-pad);
  scroll-padding-left:var(--board-pad);
}

/* A DRAWER INSIDE A PANEL IS THE PANEL'S OWN HEADING.

   Off the page it needed rules above and below to mark where one drawer
   stopped and the next began. In a board the board is that mark, so the
   rules come off and the summary sits flush at the top — the label of the
   thing you are looking at, which still happens to be the handle that
   closes it. */
.boardfield > .board > .drop{border:0;margin-top:0}
.boardfield > .board > .drop > .dsum{padding:0}
/* Open, it takes a heading's rule and a heading's gap under it, so a craft
   board and a shelf board are built to the same measure. Closed, there is
   nothing under the label to separate it from. */
.boardfield > .board > .drop[open] > .dsum{
  padding-bottom:calc(var(--board-pad) - 1px);
  border-bottom:1px solid var(--edge);
  margin-bottom:var(--board-pad);
}
.boardfield > .board > .drop .dbody{padding-bottom:0}
/* The first row's own top padding sat on top of that gap and made it 34.
   Same trim the press list takes on a project page: the list's ends are the
   board's padding, and the padding between rows is theirs alone. */
.boardfield > .board .members > .member:first-child{padding-top:0}
.boardfield > .board .members > .member:last-child{padding-bottom:0}

.profileview .boardfield > .pshelf{
  position:relative;
  background:var(--board);
  border-radius:var(--board-r);
  box-shadow:var(--board-lift);
  padding:var(--board-pad);
  margin:0;
}

/* THE RING ITSELF.

   Two masks, one subtracted from the other: the gradient fills the whole
   box, then everything inside the 1px padding is cut away, leaving the
   gradient only in the ring. It is the one way to get a border that changes
   colour along its length without spending a real border on it. */
.board::after,
.profileview .boardfield > .pshelf::after{
  content:"";
  position:absolute;
  inset:0;
  border-radius:inherit;
  padding:1px;
  background:var(--board-gloss);
  -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;
          mask-composite:exclude;
  pointer-events:none;
}
/* the drop marker is a box-shadow too, and a second box-shadow replaces the
   first — so the board's ring has to be restated or a row loses its edge the
   moment you drag another one over it */
.profileview .boardfield > .pshelf.over{
  box-shadow:var(--board-lift), 0 -2px 0 var(--ink);
}

/* THE EDGE GETS MORE ROOM THAN THE GAP BETWEEN COVERS.

   Tying the two together was tidy and read wrong: at 12 the work was pressed
   against the panel's border, with no more air at the edge of the board than
   between two pictures inside it. A board is a surface things sit on and it
   has to be visible as one.

   Which reopens the arithmetic the tie was there to settle. The strip bleeds
   by the padding, so a picture crossing the edge dissolves into the border
   rather than short of it. But the next cover begins one gap after the last,
   so a bleed wider than the gap puts the difference on screen — 8px of a
   fourth cover hanging in the fade.

   Both hold if the covers give the difference back: three of them, two gaps
   between, and one bleed-less-a-gap of slack, so the fourth starts exactly
   where the strip stops being visible. That is the width below. */
.page{--board-pad:20px}
.profileview{
  --avatar:138px;--avatar-h:calc(var(--avatar) * 175 / 138);
  /* the ring paints outside the picture's box: 5px of ground, then a 1px
     hairline. It is part of what you see, so it is part of what has to line
     up, and it is named here because two rules below have to subtract it. */
  --ring:6px;
  --idgap:26px;
  /* How far the controls sit in from the row's corner, on both axes. The
     share mark keeps this from the right edge and the pills keep it from the
     top, so the group is inset by one figure rather than by whatever each
     side happened to inherit — it was 22.5 across and nothing down. */
  --ctl-edge:22.5px;
}
.idrow{
  /* The gap gives back exactly what the picture's inset takes, so moving the
     picture onto the column line leaves the name and the bio where they
     were — the text column does not shift because a ring exists. */
  display:flex;align-items:flex-start;gap:calc(var(--idgap) - var(--ring));
  /* Up by half the picture, which is the overlap the picture is asked for —
     so the row's own top edge lands exactly where the photograph's does. The
     controls sit on that edge and the name starts on it, so the three begin
     on one line while the banner still crosses the square at its middle.

     The overlap is stated once, here. The picture then needs no offset of
     its own, which is also what pins its top edge: opening the bio adds the
     portrait's extra height to the bottom instead of climbing further into
     the banner. */
  margin-top:calc(var(--avatar) * -.5);position:relative;
  /* Air under the person, before the work. The 12 between shelves is right
     between two shelves — they are the same kind of thing — but the profile
     block is not a shelf, and running the first row up against it at the
     same gap made the page one undifferentiated column. This is the only
     break on the page, so it is the only place with its own figure. */
  margin-bottom:28px;
}
.avatar{
  /* A circle, so square at every state. The height used to grow to the
     portrait's when the bio opened — one gesture giving back both the text
     and the rest of the photograph — but a circle that grows on one axis is
     an ellipse, so the disclosure now belongs to the writing alone. */
  width:var(--avatar,138px);height:var(--avatar,138px);flex:none;
  /* In by the width of its own ring, so the outermost thing you can see —
     the hairline, not the box — starts on the same line as the tab rule, the
     covers and the banner. Aligning the box instead put the visible edge
     6px out past all of them. */
  margin-left:var(--ring);
  border-radius:50%;
  /* No offset of its own — the row carries the overlap now, so the picture's
     top edge is the row's top edge, the line the controls and the name both
     start on. Keeping it off the picture is also what pins that edge: open
     the bio and the portrait's extra height goes on the bottom rather than
     climbing further into the banner. */
  margin-top:0;
  background-color:var(--plate);
  /* a wider ring: at this size a hairline disappears against a busy banner,
     so the picture cuts itself out of the image with a ground gap first */
  box-shadow:0 0 0 5px var(--bg),0 0 0 6px var(--line);
  background-clip:padding-box;
}
/* The name starts on the same inset the controls do, so the two ends of the
   row begin on one line — the name at the left, the pills at the right, both
   --ctl-edge down from the row's top. Reading the same token is what keeps
   them together: move the corner and the name follows it. */
.idrow .who{
  flex:1;min-width:0;align-self:flex-start;
  margin-top:var(--ctl-edge);
}
/* Name and craft share a line, aligned on their baselines rather than their
   boxes. The height is stated rather than left to whatever a 30px name and a
   17px craft resolve to together, because the block above is positioned off
   half of it — a line whose height depends on its contents cannot be used to
   centre anything. */
.nameline{
  display:flex;align-items:baseline;gap:14px;flex-wrap:wrap;
  min-height:34px;
}
.idrow h1{margin:0;font-size:25px;font-weight:600;letter-spacing:-.015em}
.idrow .role{margin:0;font-size:15px;color:var(--ink2)}
/* Edit and play are one control in two jobs — go and change this, go and
   watch it — so they are one style. play used to be .watch, the filled
   button the forms use to publish and save, which made it a different shape
   with a different weight that then needed a hand-tuned padding to stop it
   reading larger than the pill beside it. Three rules to make two things
   look alike, when the answer was for them to be the same thing.

   600 rather than the 400 this carried before: 13px is small enough to need
   the weight, and every other label this size on the site — the nav, the
   field names, the shelf headings — is already 600. */
.pill{
  flex:none;font-family:inherit;font-size:13px;font-weight:600;
  line-height:normal;padding:8px 17px;border-radius:99px;
  box-shadow:inset 0 0 0 1px var(--line);
}
/* Out of the row's flow entirely, in the corner. Left in it they took the
   width the name needed and pushed the craft onto its own line. */
.idctl{
  position:absolute;right:0;top:var(--ctl-edge);
  display:flex;align-items:center;gap:16px;
}
.pill:hover{box-shadow:inset 0 0 0 1px var(--ink)}
/* The handles sit in the name row, just left of share; nothing carries them
   over a picture any more, so the absolute placement and the drop-shadow that
   went with it are both cancelled here. Without this the contacts fall back
   to the generic .links rule, which still positions them over the banner. */
.idrow .links{
  position:static;top:auto;right:auto;flex:none;
  filter:none;color:var(--ink2);gap:13px;
}
.idrow .links svg{width:17px;height:17px}
/* SHARE IS ONE OF THE MARKS, NOT A CONTROL BESIDE THEM.

   It is an .ord — a 34px round button with a hover plate — while mail and
   the socials are bare 19px glyphs. Two costs, both of them visible: the
   round plate made it a different kind of object in a row of plain marks,
   and its 7.5px of internal padding pushed the icon out of the rhythm. The
   contact marks sit 13px apart; share's glyph landed 23.5px from the last
   one (16px of row gap plus its own padding) and stopped 7.5px short of the
   right edge every other block on the page ends on.

   The button keeps its 34px target — that is a real hit area and shrinking
   it would be a regression on touch. Only its left is pulled in, enough to
   put the glyph 13px from its neighbour like the rest of them.

   Its right comes in rather than out. Flush against the column's edge an
   icon reads as crowded, where a line of type ending on that same line reads
   as measured.

   How far in is set by the corner it sits in, not by the row. The icons ride
   over the bottom right of the banner, and the last glyph already sits
   21.5px above the banner's bottom edge — so it comes 21.5px in from the
   right edge too, and is the same distance from both. At 13 it was nearer
   the side than the bottom, which is what makes a corner look wrong even
   when neither gap is wrong on its own. */
/* 8.5 is the .ord's own padding — half of 34 less the 17px glyph — so the
   mark, not the button box, lands on --ctl-edge. */
.idctl .share{margin-left:-10.5px;margin-right:calc(var(--ctl-edge) - 8.5px)}
.idctl .share:hover{background:none;color:var(--ink)}

/* a way back up out of a sub-list */
.backlink{
  display:inline-flex;align-items:center;gap:4px;
  margin-top:22px;font-size:15px;color:var(--ink2);
}
.backlink:hover{color:var(--ink)}
.backlink svg{width:15px;height:15px;flex:none}
.backlink + .sechead{margin-top:14px}

/* Inside the name's own block now, so it needs no indent to line up with
   it — it simply is in that column. */
.profileview .bio{margin-left:0}
/* A LONG BIO IS CLIPPED, NOT LET RUN.

   Three lines, then the last one fades out and a word takes you the rest of
   the way. Without it a paragraph pushes the work down the page and the
   profile block stops matching the picture beside it — three lines is what
   fits alongside the portrait.

   The clip is added in js and only when the text actually overflows, because
   css cannot ask. A mask applied unconditionally fades the foot of a
   one-line bio too, which reads as a rendering fault rather than a signal
   that there is more. */
/* margin-bottom:0 because this is a <p> and a paragraph's default bottom
   margin was setting the height of the whole identity row — 15px of nothing
   between the bio and the first shelf, coming from the user agent. */
.bio{margin:12px 0 0;font-size:15px;line-height:1.62;color:var(--ink2)}
.bio.clip{
  max-height:calc(15px * 1.62 * 3);overflow:hidden;
  -webkit-mask-image:var(--more-fade);mask-image:var(--more-fade);
  cursor:pointer;
}
.bio.open{max-height:none;-webkit-mask-image:none;mask-image:none;cursor:pointer}

/* contact row, top right of the banner. Icons sit over an image, so they
   carry a shadow rather than relying on a muted tone that a bright frame
   would swallow. */
.links{
  position:absolute;top:16px;right:20px;z-index:2;
  display:flex;align-items:center;gap:16px;
  color:rgba(var(--silk),.82);
  filter:drop-shadow(0 2px 7px rgba(0,0,0,.75));
}
.links a{color:inherit;line-height:0}
.links a:hover{color:var(--ink)}
.links svg{width:19px;height:19px;display:block}

/* ---------------- project ---------------- */
.ptitle{margin:0;min-width:0;font-size:25px;font-weight:600;letter-spacing:-.015em;text-wrap:balance}
.pmeta{margin:var(--s-tight) 0 0;font-size:15px;color:var(--ink2);text-transform:capitalize}
.pdesc{
  margin:var(--s-step) 0 0;font-size:15px;line-height:1.55;color:var(--ink2);
  /* two lines of this paragraph, at this paragraph's own size — it was
     still measuring 13px after the type came up and clamped short. */
  max-height:calc(15px * 1.55 * 2);overflow:hidden;
  -webkit-mask-image:linear-gradient(180deg,#000 0,#000 52%,transparent 100%);
  mask-image:linear-gradient(180deg,#000 0,#000 52%,transparent 100%);
}
@media (min-width:900px){
  .pdesc{max-height:none;-webkit-mask-image:none;mask-image:none}
}
/* who made it. The byline is the credits control: the lead credit shows,
   the chevron opens the rest. */
/* The role column, shared by the summary row and the crew list under it so
   every rule stops in the same place. Capped at 20 characters: across a
   50vw column an uncapped leader ran the better part of a foot of screen,
   which stops being a line carrying the eye and starts being a divider. */
/* THE PEOPLE ANSWER THE LINE ABOVE THEM.

   Title, kind and year, who made it, what it is about — four things about
   one film, not four sections of a page. At --s-sect they were spaced like
   the gallery and the press drawer, which is the gap that says "different
   subject", and the top of the page came apart into slices.

   Not --s-tight either: that was tried, and a 28px avatar 8px under the
   title crowded it and made two dot-separated lines read as one list. The
   step between is what this wants, and now there is one. Press keeps the
   full section gap — a drawer of links really is a different subject. */
.bydrop{margin-top:var(--s-step)}
/* IN ITS OWN BOARD IT IS THE FIRST THING, NOT THE THIRD.

   The step above the drawer was the gap from the meta line it used to follow.
   On the credits board there is nothing above it, so that 24px was landing
   on top of the board's own 20 and starting the faces 44px down while the
   title beside them started at 20. */
.project .boardcred .bydrop{margin-top:0}

/* THE LABEL WEARS THE BAND THE PROFILE'S BLOCKS WEAR.

   A sechead is built for a page, where it carries 40px above it to separate
   one section from the last. Inside a board it is the top of the board, and
   the board's padding is already that gap — so the step goes, and what is
   left is the same figure on both sides of the rule that a profile's tab
   strip keeps: --board-pad above the line, --board-pad under it. The label
   itself is already trimmed to its capitals, so the 20 above is measured
   from the letters and lands level with the title on the board beside it.

   The rule is that pixel, which is why the padding is one short of it. */
.project .boardfield .sechead{
  margin:0;
  padding-bottom:calc(var(--board-pad) - 1px);
}
.project .boardfield .sechead + .bydrop,
.project .boardfield .sechead + .presslist{margin-top:var(--board-pad)}

/* AND THE TITLE MEASURES FROM ITS CAPITALS, SO THE TWO BOARDS START LEVEL.

   A face is ink from its first pixel and sits at the padding edge. A line of
   25px type does not: seven pixels of leading sat above its capitals, so the
   title rode lower than the credits beside it — invisible while the credits
   were underneath, plain the moment the two shared a line.

   The meta line's step is restated because trimming shortens the box it was
   measured from; the figure below keeps the gap where it already was. */
.project .ptitle{text-box:trim-both cap alphabetic}
/* 17, not 8. The 8 was measured to the bottom of an untrimmed box, which sat
   nine pixels below the title's baseline; trimmed, the same 8 closed the gap
   to almost nothing. 17 puts the meta line back exactly where it was
   relative to the baseline above it, so only the title moved. */

/* A RULE UNDER THE NAME, IN THE BAND THE BOARD BESIDE IT KEEPS.

   The credits board puts its label over a hairline with --board-pad on both
   sides of the line. This board's label is two lines rather than one, but it
   is the same gesture, so it takes the same figures and the two rules end up
   on the page as one horizontal.

   trim-end, because the band has to be measured from the baseline and not
   from the descender space under it — untrimmed, the same 19 would have put
   this rule five pixels lower than the one across the gap. The padding is
   one short of --board-pad because the rule itself is that pixel. */
/* THE BAND BELONGS TO THE PAIR, NOT TO THE SECOND HALF OF IT.

   The hairline used to hang off .pmeta because .pmeta was the last line of
   the block. It is not a line any more, it is the end of one — so the rule,
   the padding under it and the baseline trimming all move up to the row that
   holds both.

   trim-end, because the band has to be measured from the baseline and not
   from the descender space under it; untrimmed, the same 19 sits five pixels
   lower under a `g` than under an `s`, and two labels on one band look a
   step apart.

   align-items:baseline so `short film · 2026` sits on the line the capitals
   sit on. Centred, the small type floats halfway up a 25px title and reads
   as a caption that has drifted. flex-wrap, so a title long enough to fill
   the measure puts the meta on the next line rather than squeezing it. */
/* THE BAND BELONGS TO THE PAIR, NOT TO THE SECOND HALF OF IT.

   The hairline used to hang off .pmeta because .pmeta was the last line of
   the block. It is not a line any more, it is the end of one — so the rule,
   the padding under it and the baseline trimming all belong to the row that
   holds both.

   AND THE TRIM GOES ON THE TYPE, NOT ON THE BOX AROUND IT.

   text-box-trim acts on an element's own line boxes. This row has none — it
   is a flex container holding two children — so declaring it on the row did
   nothing at all, and the row's foot stayed wherever the taller child's box
   ended. .ptitle was trimmed already; .pmeta was not, so the band was being
   measured from the descender space under `short film · 2026` rather than
   from its baseline.

   That is a gap that changes with the words in it. Trimmed, the rule sits 19
   under the baseline whatever letters are on the line; untrimmed it sits 19
   under whatever the font reserves for a q or a y, which is nearer 14 from
   the baseline — and the Credits label across the gutter is trimmed, so the
   two rules drawn to land on one horizontal were four or five pixels apart
   the whole time.

   Both children, and trim-both rather than trim-end. The tops matter as much
   here: align-items:baseline puts them on one line, and an untrimmed box
   beside a trimmed one is the taller flex item — the same thing that pushed
   a heading 2.4px down the board until .sechead .tag was trimmed to sit
   beside its own name. */
.project .ptitlerow{
  display:flex;align-items:baseline;flex-wrap:wrap;
  /* the page's own gap between a thing and the thing that qualifies it */
  gap:0 12px;
  padding-bottom:calc(var(--board-pad) - 1px);
  border-bottom:1px solid var(--edge);
}
.project .ptitlerow > *{text-box:trim-both cap alphabetic}
/* it carries none of it any more: no top gap, no rule, no band */
.project .ptitlerow .pmeta{margin:0;padding:0;border:0}
.project .ptitlerow + .pdesc{margin-top:var(--board-pad)}


/* ONE GRID FOR THE WHOLE LIST, NOT A ROW EACH.

   A rule between a name and a job is only worth drawing if it stops where
   every other rule stops. Laid out a row at a time it cannot: each row sizes
   its own job to its own content, so three rows put three jobs at three
   different left edges and the rules end wherever the names happen to. What
   you get is not a column, it is three dashes of three lengths.

   So the drawer holds the columns and every row borrows them by subgrid: the
   summary, the body, and each credit inside it. `auto` on the second track
   resolves once, against the longest job in the list, and every rule then
   stops at that one edge — which is the entire effect and the only reason
   the rule is there.

   Two tracks, not three. The rule had one of its own and so began at the
   widest name rather than at each name, which left every shorter name a gap
   of empty board before its own leader started. It belongs in the name's
   cell, taking whatever that cell has left. Only the job needs a column,
   because the job is the thing that has to line up.

   minmax(0, …) on both so a narrow board takes width off a name — which
   ellipsises and is still readable — rather than overflowing the panel. */
.bydrop{
  /* ONE GEOMETRY FOR EVERY PROJECT, NOT ONE PER LIST.

     Sized to its content, the job column resolved against the longest job in
     that project — so my-ultraman put its jobs at 135 and a-still-dance put
     its at 124, and two credit lists on two pages of the same site kept two
     different spacings. A reader moving between projects sees the column
     shift under them.

     A count of characters instead, fixed here and answering to nothing on
     the page. Every project now lays its credits out on the same measure,
     whatever it happens to contain, and a list of four short jobs keeps the
     same column as a list with `cinematographer` in it.

     THE NAME IS THE FIXED ONE, AND THAT IS A CHOICE.

     241.7px of words — a 28px picture, `Fang-Ke Zhou` at 95, and
     `cinematographer` at 118 — inside 242.4px of board. Nought point seven
     of a pixel for three gaps and the rule. There is no spacing that solves
     that, and every lever was measured before this was written: closing the
     gaps entirely and deleting the rule buys 42px and is still not enough to
     have both words whole with a picture in front of them.

     So the name gets the characters and the job column gets the remainder:
     190.4px less whatever this is set to. A name you can read is what a
     credit is for, and the jobs that outrun the column give way at the end.

     Fourteen, and it is the last value that is a step rather than a cliff.

     Twelve was the last that cost nothing at all: it cleared `Shang-Ze
     Zeng` at 104.1 — the longest name in the file — by three tenths of a
     pixel, and left the jobs 86, which `sound mixer` still fitted at 84.7.
     Nothing was clipped and nothing was spare, which is a fine place to sit
     until the first fifteen-character name arrives and gives way at its end
     on a board that had two pixels to spare.

     So two more characters of headroom, bought at the only price there is:
     the jobs drop to 68.6 and `sound mixer` gives way instead. Every job of
     eight letters — director, producer, colorist — is whole at 59.8 and
     stays whole, which is most of what a credit list says.

     Fifteen is where it stops. That leaves the jobs 59.9 against those
     59.8, which is not a margin, it is a coincidence; sixteen clips the
     three commonest jobs on the page. `cinematographer` at 118 and `sound
     designer` at 110 are past the column at every setting of this dial and
     always were — the board is 242px, and that is a fact about the board,
     not about this number.

     Widen this board and the jobs come whole again on their own: the name's
     side is fixed, so every pixel the board gains goes to them.

     minmax(0, …) on the job so a board too narrow for the full measure takes
     width off that column rather than overflowing the panel — and takes the
     same width off it on every project, which is the point. */
  /* ch, measured in the rows' own 15px rather than the 16 this element would
     otherwise inherit. Without it the track reserved 12 characters of 16px
     type while the name was capped at 12 of 15px, and the seven pixels
     between the two were held by the name's side and spent by nobody. */
  font-size:15px;
  --cred-name:14ch;
  /* the whole left-hand side: picture, gap, name, gap, and the rule's floor.
     Fixed, so it is the same on every project — the job column is then
     whatever the board has left, which is also the same on every project. */
  grid-template-columns:
    calc(28px + 10px + var(--cred-name) + 4px + 6px)
    minmax(0,1fr);
  display:grid;
  /* WHERE THE TIGHTENING GOES, NOW THAT THE PICTURE HAS ITS TEN BACK.

     The row is 244px of content in a 242px box, so the gaps are still the
     only slack there is — but they are not all the same gap. Ten between a
     picture and the name it belongs to is the page's spacing and it reads as
     wrong at anything less; four on either side of the rule does not, because
     a leader that starts close to the name is a leader doing its job.

     Four and four rather than six and six is exactly the four the picture
     took back, so the job column is untouched at 86 and `sound mixer` still
     fits it. */
  column-gap:4px;row-gap:14px;
  align-items:start;
}
/* THE BOX THE BROWSER PUTS BETWEEN THEM.

   A <details> does not hand its content straight to its own box: everything
   after the summary is wrapped in a UA box, ::details-content, which is what
   carries the open/closed state. So the body was never a grid item of the
   drawer, its `subgrid` had no parent tracks to borrow and computed to none,
   and every credit's three cells stacked down the column instead of sitting
   on one line.

   The columns are threaded through that box rather than around it. It keeps
   its own box — and with it the content-visibility that hides the list when
   the drawer is shut, which display:contents here would have thrown away
   along with the box. */
/* SHUT, IT SHOWS THE TOP OF THE NEXT NAME AND LETS GO OF IT.

   This used to be display:none, on the reasoning that an empty box still
   held the grid's second row and 14px of nothing under the byline. True, and
   it was the right answer while a chevron was doing the telling. With the
   chevron gone something has to say the list continues, and the honest thing
   to say it with is the list: fourteen pixels of the next credit, dissolving
   into the board.

   It is the bio's gesture and the description's, made by the same gradient.
   --more-fade holds full strength for the first half and is gone by the end,
   so what shows is the top of an avatar and the top of a name — enough to
   read as a row, not enough to read.

   content-visibility, because a closed <details> does not merely hide its
   content box, it skips rendering what is inside it; the sliver would be an
   empty 14px band without this. And the gap closes to 6, because 14 was the
   step between one credit and the next and this is not a next credit, it is
   the edge of one. */
/* WHY THE BIO READS AND THIS DID NOT, AND IT WAS NEVER THE GRADIENT.

   The bio shows three lines: two at full strength and the third dissolving.
   You read the two and the third tells you there are more. This showed one
   row and put the gradient across all of it, so there was no point anywhere
   in it where anything was solid — and the two were given the same fade and
   came out completely different, because the fade was never what was doing
   the work.

   So: one whole row, under --more-fade-firm.

   ONE ROW, BECAUSE THE SUMMARY IS ALREADY A LINE.

   The summary is not a heading over the list, it is the first credit — same
   three cells, same grid, drawn by the same rules. So the drawer shut is two
   lines, not one: the lead at full strength and this one going. Two rows
   here made it three, and the dissolve landed a line further down than it
   should, which is a lot of drawer for a signal.

   The gradient holds solid to 40% of the row and lets go over the rest.
   Names sit on a 28px line, so the glyphs run through the middle of that —
   their tops are at full strength and their feet dissolve, which is the
   thing worth having: a line you can read that is plainly on its way out.

   FIRM, NOT THE BIO'S OWN. A HAIR, AND ONE FEWER FIGURE.

   The bio's 52 left this a shade too legible: it is a signal, not something
   you are meant to get to the end of. 40 is 11px of a 28px row at full
   strength where 52 was 15 — three pixels, which is the size of the
   difference being asked for — and it is a figure this file already has,
   holding up the folded row at the foot of a board. Those two are the same
   kind of thing and now say so. The bio keeps 52 because the bio is read. 

   Stops tried and rejected on the way here: 14px, half a row cut and then
   faded, which is a cut and a fade ending the same line; a whole row under
   the banner's easing, which is a presence rather than a name; and two rows
   under this gradient, which reads correctly and is one line too many. */
/* THE GAP IS MEASURED FROM WHAT YOU CAN SEE.

   The box is a whole 28px row, but the gradient is at nothing across its
   last quarter — so the panel's --board-pad started from a bottom edge that
   is not visible, and the space under the credits read as about 27 where
   every other gap in the panel is 20.

   A quarter of the row back, which is the part of it that is not there. This
   is not the fade escaping the inset — the inset is intact and measured from
   the last of the type you can actually read. It is the difference between
   the height a mask occupies and the height it shows, and only a masked box
   has one.

   It started as compensation and is now a choice, and the comment should say
   so rather than keep quoting the gradient at you.

   A quarter — 7px — was the honest arithmetic: --more-fade-firm holds to 40%
   and falls linearly, so the last quarter of the row is under a tenth of its
   strength and reads as nothing. Taking that back made the gap under the
   credits measure 20 from the last thing you can see, like every other gap
   in the panel.

   .39 is 11px, and past the point where the tail is invisible. At the line
   it now cuts to, the type is still around two thirds opaque, so the credits
   overhang the reserved box and the gap comes out nearer 11 than 20. That is
   the panel deliberately sitting tighter than the board's step, which is a
   thing you can want and is not a thing the gradient decided.

   The sliver itself has not moved. Nothing is clipped, nothing fades sooner;
   only the room the panel keeps under it. Change the gradient and the first
   7px of this wants changing with it — the rest is taste. */
/* ONE CREDIT, WHATEVER A CREDIT HAPPENS TO BE TALL.

   max-height:28px was the row on a project where nobody holds two jobs.
   Where somebody does, the tags stack and the row is nearer 48 — so the same
   28 cut it through the middle of a person's second job, and the projects
   split into two looks: whole lines dissolving on `smoke gets on your mind`,
   halves on `my ultraman`.

   The row is not a height, it is a row. So the rule is said as one: every
   credit after the first is out while the drawer is shut, and the box is
   then exactly one credit tall by construction — 28 where that is a credit,
   48 where a credit is two jobs, and right on a project nobody has written
   yet. No figure to keep in step with the markup.

   The pull-back stays a quarter of 28. It is compensating for the part of
   the gradient you cannot see, and on a taller row it gives back slightly
   less than it takes — a couple of pixels on the projects where someone
   doubles up. A percentage cannot say it: percentage margins resolve against
   width, and this is a height. */
.bydrop:not([open]) .crewlist > .crewp:not(:first-child){display:none}
.bydrop:not([open])::details-content{
  content-visibility:visible;
  overflow:hidden;
  margin-bottom:calc(28px * -.39);
  /* it is the handle now, so it says so */
  cursor:pointer;
  -webkit-mask-image:var(--more-fade-firm);mask-image:var(--more-fade-firm);
}
.bydrop::details-content{
  grid-column:1 / -1;
  display:grid;
  grid-template-columns:subgrid;
  row-gap:14px;
}
/* Nothing may follow ::details-content in a selector — a pseudo-element
   takes no combinator — so the body and the list are pass-throughs instead
   and each credit row borrows the tracks directly. */
.bydrop > .byline,
.crewlist > .crewp{
  grid-column:1 / -1;
  display:grid;
  grid-template-columns:subgrid;
  align-items:start;
}
/* pass-throughs, so that each credit row is a grid row of the drawer itself
   and borrows the drawer's own three tracks */
.bybody,.crewlist{display:contents}
/* The whole row is the handle — it was even while there was an arrow on it,
   and now it is the only one. No position:relative any more: it was the
   ground the chevron was positioned against, and nothing is. */
.byline{list-style:none;cursor:pointer}
.byline::-webkit-details-marker{display:none}
/* The step from the byline into the first credit is the same as the step
   from one credit to the next — the summary row is the first credit, so the
   list must not hinge below it. One number now instead of two: the grid's
   own row-gap, which falls between the summary and the body and between one
   credit and the next alike. */
.bybody{padding:0}
.byline .av{
  width:28px;height:28px;border-radius:50%;flex:none;
  background-color:var(--plate);box-shadow:0 0 0 1px var(--line);
}
/* 10, matching a credit row's own gap. At 12 the byline's name started two
   pixels right of every name under it — the avatars lined up and the names
   did not, which reads as a mistake rather than as a summary row. */
/* ten, the same distance a picture sits from its name everywhere else on the
   site and the same distance .cwho gives it on the rows below */
.bywho{display:flex;align-items:center;gap:10px;min-width:0}
/* THE SUMMARY IS THE FIRST CREDIT, SO IT BREAKS LIKE ONE.

   In the narrow board this name had no nowrap and wrapped to two lines,
   which made the summary row 40 tall where every credit under it is 28 —
   the one row in the list that behaved differently, and the row the others
   are supposed to line up with. It takes .crewp .cn's treatment exactly: the
   picture's height for its line, and a name too long for the column giving
   way at the end rather than opening a second line. */
.byline .bn{
  font-size:15px;line-height:28px;min-width:0;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
/* the underline follows the link, not the whole row: the rest of the
   summary opens the credits and should not look like a profile link */
a.bywho:hover .bn{text-decoration:underline;text-underline-offset:3px}

/* The gallery starts where the bar ends, with nothing between them. The
   scrim's tail runs a little past the bar — it is centred on the wordmark —
   so the frame's first rows sit under the last of it, at about a tenth of
   its strength by then and gone within fourteen pixels. That is the tail of
   a fade, not a shadow on the work.

   A profile's banner goes further and runs up behind the bar entirely: it is
   a background and wants the nav in it. A project's frame stops at the bar
   because it is the work. */
/* THE GALLERY IS NARROWER THAN THE COLUMN, AND CENTRED IN IT.

   A column-wide frame is over half the height of a laptop screen, so the
   still, the bar under it, the title and the byline ran past the fold and
   the work arrived with nothing around it. Pulling it in off the column
   gives that back without touching the writing, which keeps the full measure
   — a line of type wants its measure and that has nothing to do with a
   picture. 88% is 80 taken back up a tenth: enough of an inset to read as
   deliberate, not so much that the work is sitting in a window.

   Centred rather than pinned left, because a narrower block sharing one edge
   with the text below reads as a mistake, where a narrower block centred in
   the same column reads as a decision.

   Width, not a cap on the frame. Capping the frame's height would cut the
   picture — it is a cover background — and capping its width would leave the
   strip's own measurements referring to a box that no longer exists. Every
   one of the fill's figures is a percentage of this box, so narrowing the
   box scales the bleed, the gap and the snap along with it, untouched.

   The bar keeps the full column, and the title with it. Narrowing the bar
   dragged the film's name inward to meet the picture, which put it out of
   line with every other line of type on the page — a title belongs to the
   work, not to the frame, and it reads down the column's own left edge.

   The dots do not need the bar narrowed to sit under the picture: they are
   centred in the bar, the picture is centred in the same column, so the two
   share a centre already.

   auto on the sides only. The shorthand would take the top margin with it,
   and that is what opens the page. */
/* the width as a bare fraction as well as a percentage: the percentage is
   what the gallery is sized with, the fraction is what anything that has to
   line up with it does its arithmetic from */
/* THE GALLERY IS THE BOARD'S FULL WIDTH, SO THE INSET IS NOTHING.

   --galf was the fraction of the column the gallery held itself to, back
   when it sat in a full-width page and pulled itself in to 88%. The board is
   that inset now — 20px of padding on every side — and the gallery fills
   what is left.

   It is still a fraction rather than a deleted rule because the bar's own
   controls read it: edit and share were sitting 49px in from the frame's
   right edge, which was 6% of the bar, which was half of the 12% the
   gallery used to give up. At 1 they land on the frame's edge, where the
   picture they act on ends. */
.project{--galf:1;--gal:100%}
.project .boardgal > .wrap > .rowwrap:first-child{
  /* the board is the inset now, so the 88% the gallery used to hold itself
     to inside a full-width column is the panel's padding instead */
  margin-top:0;width:100%;
}

/* Vertical rhythm. Every block below the meta line steps at --s-sect; the
   meta sits at --s-tight because it and the title are one thing said in two
   lines, not two blocks. */
/* EVEN BY THE MARKS, NOT BY THE BOXES.

   Four controls, one gap, and three different spacings on screen. .ord is a
   34px disc around a 17px glyph, so a heart brings 8.5px of its own empty
   space to each side of itself: at a flat 14 the row read 14 between edit
   and play, 22.5 between play and the heart, and 31 between the heart and
   the share. Boxes evenly spaced, marks not.

   So one figure says what the eye should see and each disc gives back the
   padding it brought: 8.5 a side, twice over between two discs, which is why
   they close to a 1px box gap and still measure the same as the two pills.

   18 rather than 22. 22 was the play-to-heart distance, which looked right
   only because it was the middle of three wrongs — taking it as the target
   pushed edit and play from 14 apart to 22 and spread the row. 18 is close
   to the pills' own 14 and is the tightest figure that leaves the two discs
   a hair apart rather than overlapping: below it their hover pills start to
   touch, and 34px circles sharing an edge is a worse fault than the one
   being fixed. One number, and the whole row moves with it.

   The ends are put back. A negative margin on the last child would hang the
   share's disc past the column the row is aligned to, so its hover pill
   would sit half outside the frame's own edge; the first is the same story
   in the other direction. Only the space between things is corrected — which
   is also where .gbar .share's old -4px nudge went: it was correcting this
   by hand at one end, and this rule now owns the whole row. */
.watchrow{display:flex;align-items:center;gap:18px;flex:none}
.watchrow .ord{margin-left:-8.5px;margin-right:-8.5px}
.watchrow > :first-child{margin-left:0}
.watchrow > :last-child{margin-right:0}
/* A MARK YOU HAVE MADE, FILLED IN.

   Outline until you press it, then the same fill the viewer's bookmark
   takes — one gesture for "this is mine now" wherever the site offers it.
   .ord already gives the disc, the size and the hover; all this adds is
   what being on looks like. */
.like.on{color:var(--ink)}
.like.on svg path{fill:currentColor}
.watch{
  background:var(--ink);color:#000;font-size:15px;font-weight:600;
  padding:10px 23px;border-radius:99px;
  /* the same pill whether it is a link out or a button that plays here */
  display:inline-block;line-height:normal;font-family:inherit;cursor:pointer;
}
.hint{font-size:15px;color:var(--ink2)}

.rowwrap{position:relative;container-type:inline-size}
.snav{
  position:absolute;top:50%;transform:translateY(-50%);z-index:2;
  width:38px;height:38px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  background:rgba(0,0,0,.5);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  box-shadow:inset 0 0 0 1px var(--line);
  color:var(--ink);opacity:.8;transition:opacity .18s ease;
}
.snav:hover{opacity:1}
.snav.off{opacity:0;pointer-events:none}
.snav svg{width:18px;height:18px}
.snav.l{left:8px}
.snav.r{right:8px}
@media (min-width:900px){
  /* HALF THE COVER'S HEIGHT, FROM THE WIDTH THAT MAKES IT.

     A cover is a third of the strip less the two gaps, and 4:3 makes it
     three quarters as tall as it is wide — so half of that is (100cqw - 24px)
     ÷ 8. Whatever changes the card width has to change this too: it has gone
     stale once already, when the cards went from four across to three and
     the arrows stayed 27px high.

     Covers are a fixed 4:3 now, so this is a real centre rather than an
     average of ragged heights. */
  .row ~ .snav{top:calc((100cqw - 24px) / 8)}
}
/* browse rows: arrows on wide screens only — a phone swipes, and a
   34pt button covers a quarter of a 139pt tile */
@media (max-width:899px){
  .row ~ .snav,.tiles ~ .snav,.stillrow ~ .snav{display:none}
}

/* one still at a time; the next bleeds in at the right */
.stills{
  display:flex;
  overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:none;
  /* THE GAP IS THE GUTTER, AND THAT IS WHAT ENDS THE LEAK.

     The strip clips its own padding box — overflow-x computes overflow-y to
     auto — so --vf of padding is what the fill has to spread into, above and
     below. The negative margin takes that room straight back out of the
     layout, and the gallery sits exactly where it always did.

     --reach is the profile's, and identical on purpose. It was a fixed 89px
     here, which on an 858px frame is 10% of its width where a stacked cover
     gets 35% of its own — so the same rule drew two visibly different
     things. Worse, the ellipse is sized to the box, so a small reach puts
     the frame's edge 83% of the way out into the falloff instead of 59%,
     and the fill arrived at the work already cut to .57 of strength. The
     fraction fixes both at once.

     Vertically it is .27 of the sideways reach, which is what 26px against
     97px is on a profile. In cqw because the strip's padding needs a length
     and .rowwrap is a container; a percentage there would resolve against
     the width and mean something else entirely.

     The gap is twice the sideways room, and the doubling is not arbitrary.
     It has to put two things past the clip at once: the next frame itself,
     or a sliver of it shows beside the one you are looking at; and the next
     frame's fill, which reaches --ih backwards out of it. Set the gap to
     --ih and the frame clears but its fill does not — it arrives at the clip
     already ramping up and gets sliced, which is a hard vertical edge. At
     twice --ih the neighbour's fill begins exactly where the strip stops, so
     both fall to nothing at the same place and the seam has nothing left in
     it to cut.

     The gap is never seen. It is only how far apart two frames have to be
     for neither to spill into the other's page.

     --vf, --ih and --m0 are the same three the profile's stacked fill uses,
     so one rule draws both. Lengths here rather than fractions of a picture:
     the strip's own padding is derived from them and has to be a length. */
  --reach:.35;
  --ih:calc(var(--reach) * 100%);
  --m0:calc(var(--reach) / (1 + 2 * var(--reach)) * 100%);
  --vf:calc(var(--reach) * 26.7cqw);
  /* every one of these is the same figure wearing the basis its property
     needs: padding and margin resolve against the column, gap against the
     content box inside it, scroll-padding against the scrollport */
  gap:calc(var(--reach) * 200%);
  margin:calc(var(--vf) * -1) calc(var(--ih) * -1);
  padding:var(--vf) var(--ih);
  /* the snap edge is the content edge, or a frame lands short of it */
  scroll-padding-left:var(--m0);
}
.stills::-webkit-scrollbar{display:none}
/* No mask at all. The gallery is hard-edged on every side now — bottom,
   where the title used to rise into a dissolve, and both sides, where a
   micro feather used to take the cut edge off the frame bleeding in. A
   frame has edges; softening them was softening the work. */
.stills .frame{
  flex:none;width:100%;
  aspect-ratio:var(--ar,16 / 9);
  border-radius:8px;border:0;background-color:var(--plate);
  background-size:cover;background-position:center;background-repeat:no-repeat;
  scroll-snap-align:start;
  /* a containing block for the glow below */
  position:relative;
}
/* THE WORK IS NOT LIT BY ITS OWN GLOW.

   The frame's hairline is rgba(silk,.42) over background-clip:padding-box —
   translucent, with nothing of its own behind it — so the glow sitting
   underneath came through it and tinted the one edge the picture has. Same
   at the four rounded corners, where the background stops short of the box.

   This is the page's own ground, laid over the glow and under the frame, at
   exactly the border box. The border then composites over --bg the way it
   always did, and the glow is strictly an outside thing. Painted after
   ::before at the same depth, so it covers it and the picture covers both. */
/* GONE WITH THE HAIRLINE IT WAS BACKING.

   This shield was under the frame's translucent silk border, so the glow
   would not shine up through the one edge the picture had. The stills carry
   no border now — the corner does that work instead — and with nothing
   translucent left to back, what remained was a 1px ring of the page's own
   black painted a pixel outside every frame.

   Which is exactly what a shield laid at inset:-1px looks like once the
   thing it was hiding behind stops existing: a hint of a black frame. */
/* YOUTUBE'S AMBIENT MODE.

   The same frame again, behind itself: pushed out past its own edge, blurred
   until it is only colour, and left to feather into the page. The ground
   around the work stops being flat grey and takes the light off the picture
   sitting on it. The gallery is the one place in the app this belongs — it
   is the largest picture there is, and the only one shown alone.

   background-image:inherit is the whole trick. A frame carries its picture
   as a background, so the pseudo-element takes that value rather than naming
   the file a second time; size and position come across the same way, from
   the crop the person set. A frame with no picture inherits `none` and
   nothing is drawn.

   The hard edge stays. This is not the dissolve the frame used to have on
   its sides — the picture still stops where it stops, and what carries past
   it is light, not the work. */
/* Which frame you are on, in the same marks the still viewer uses at its own
   foot — the two are the same gesture in two places, so they read the same.
   Under the strip rather than over it: laid on the picture they need a scrim
   or a shadow to survive a pale frame, and that is furniture on top of the
   work for something this small to say. */
/* The row under the frame: title at the left, which-frame in the middle,
   controls at the right. 1fr auto 1fr rather than space-between — the equal
   outer tracks are what centre the dots on the column instead of on whatever
   the title and the buttons happen to leave between them. */
/* ABOVE THE STRIP, BECAUSE THE STRIP HANGS OVER IT.

   The gallery carries 80px of padding under its frames — room for the
   colour to bleed out of them without the scroll container clipping it —
   and that padding is still part of the strip's box. It reaches down over
   this row and ate every click meant for edit or share: the button was
   there, lit, and answering to nothing, because .stills was on top of it.

   Raising this row is the whole fix. Nothing moves: the glow is painted
   behind its own frame and this has no ground of its own, so what changes is
   only which element the pointer finds first. */
.gbar{
  position:relative;z-index:1;
  display:grid;grid-template-columns:1fr auto 1fr;
  align-items:center;gap:18px;
  /* --board-pad, not the 24 this had from when the gallery was a section on
     a page rather than the contents of a board. Every other measurement
     inside a board here is 20 — the picture's own top edge, the foot of this
     row, the first ink on both boards below — and this was the one figure
     still keeping a page's rhythm inside a panel. */
  margin-top:var(--board-pad);
}
/* EDIT AND SHARE BELONG TO THE STILL, SO THEY END WHERE IT ENDS.

   The bar runs the full column because the title does, but these two act on
   the picture — and pinned to the column's right edge they floated past it,
   sitting over the ground beside the work rather than under the work itself.

   The inset is the same figure that centres the gallery: half of whatever
   the column has left over once the frame has taken --galf of it. One
   number, so moving the gallery moves these with it.

   IN cqw, NOT PER CENT. A percentage margin on a grid item resolves against
   its own grid area, not against the grid — this is one of three equal
   tracks, so the same figure came out a third of the size and the buttons
   landed 28px short. Making the bar a container gives the arithmetic the
   basis it meant all along: 100cqw is the bar.

   The dots do not shift. They hold a centre track of their own, and a margin
   on the track beside them does not move it. */
.gbar{container-type:inline-size}
.gbar .watchrow{
  justify-self:end;
  margin-right:calc((1 - var(--galf)) / 2 * 100cqw);
}

/* THE PAIR SITS IN THE ROW NOW, SO IT STOPS FLOATING.

   .snav is built to hang over a strip — absolute, pinned to a corner, on a
   black disc with a blur behind it so it survives whatever picture is under
   it. None of that applies in a row of chrome on a panel: there is nothing
   underneath to survive, and a 38px disc beside five-pixel dots is a button
   shouting at a caption. Static, bare, and at the dots' own weight. */
.gnav{display:flex;align-items:center;gap:14px}
.project .gnav .snav{
  position:static;transform:none;
  width:24px;height:24px;border-radius:50%;
  background:none;box-shadow:none;
  -webkit-backdrop-filter:none;backdrop-filter:none;
  color:var(--ink3);opacity:1;
}
.project .gnav .snav svg{width:16px;height:16px}
.project .gnav .snav:hover{color:var(--ink)}
/* gone, not invisible: an arrow that keeps its footprint holds the dots off
   centre at the two ends of the gallery, which is where the marks matter */
.project .gnav .snav.off{opacity:.25;pointer-events:none}
.gdots{display:flex;justify-content:center;gap:7px}
.gdots i{
  width:5px;height:5px;border-radius:50%;
  background:var(--line);transition:background .18s ease;
}
.gdots i.on{background:var(--ink)}
@media (prefers-reduced-motion: reduce){ .gdots i{transition:none} }

/* Play, centred on the gallery. It sits outside the scrolling strip, so it
   holds the middle of the frame you have scrolled to rather than travelling
   with the stills. */
/* ---------------- activity ---------------- */
.actlist{display:flex;flex-direction:column}
.act{
  display:flex;align-items:center;gap:13px;
  padding:14px 0;border-bottom:1px solid var(--edge);
}
.act:last-child{border-bottom:0}
.act .av{width:34px;height:34px;border-radius:50%;flex:none;background-color:var(--plate)}
.atext{flex:1;min-width:0;margin:0;font-size:15px;line-height:1.45;color:var(--ink2)}
.atext a{color:var(--ink)}
.atext a:hover{text-decoration:underline;text-underline-offset:3px}
.athumb{flex:none;width:58px;aspect-ratio:4 / 3;border-radius:4px;cursor:zoom-in}
.awhen{flex:none;font-size:13px;color:var(--ink3);font-variant-numeric:tabular-nums;min-width:22px;text-align:right}

/* ---------------- saved ---------------- */
/* individual frames, not projects — uniform 4:3 so a wall of saves stays
   scannable even though the projects behind them differ in ratio */
/* ---------------- explore ---------------- */
/* projects / members. Named apart from the edit-profile .etabs on
   purpose — these sit on a hairline rather than reading as buttons. */
/* Words on a rule, and the chosen one underlined. The strip carries the line
   and each tab carries its own, transparent until it is the one you are in,
   sitting on the strip's rule rather than beside it — which is why the tab
   pulls itself down a pixel to meet it. */
.xtabs{
  display:flex;align-items:flex-end;gap:24px;margin-top:22px;
  border-bottom:1px solid var(--line);
}
.xtab{
  font-size:15px;letter-spacing:.01em;color:var(--ink3);
  padding:0 1px 11px;margin-bottom:-1px;
  border-bottom:1px solid transparent;
}
.xtab:hover:not(:disabled){color:var(--ink2)}
/* A TAB THAT IS NO LONGER A CHOICE.

   Where a set of tabs is one format or the other, the one you did not take
   shuts rather than disappearing: gone, and you would not know the choice
   had ever been there or that emptying the pile brings it back. Dimmed and
   dead, and it says both at once. */
.xtab:disabled{color:var(--ink3);opacity:.4;cursor:default}
.xtab.on{color:var(--ink);border-bottom-color:var(--ink)}

/* EXPLORE'S THREE, AS A TRACK RATHER THAN A RULE.

   The underlined tabs are the site's navigation shape and they stay that
   way everywhere else — a profile's shelves, the library, the strip on a
   form. Explore is the one place where the three are not a heading you are
   scrolling under but a set of three you are choosing between, all of them
   equally available, none of them the default. That is a segmented control,
   and the site already owns one: the still/motion track under the pickers.
   So this is that shape, widened from two cells to three.

   Scoped to .xseg and not to .xtab. Four strips share that class and only
   this one is being restyled; going at the bare selector would have taken
   the shelf tabs and the library with it.

   NO THUMB.

   The form's track slides because switching is a state change inside a page
   that stays put. These are links — each one is a url, the hash changes and
   the view is rebuilt — so a thumb would be a fresh element starting at its
   destination, which is the same thing the segtrack comment warns about.
   The lit cell is painted, not travelled to.

   Equal cells from 1fr inside an inline grid: the track is as wide as three
   of the longest word, and "stills" does not get a smaller target than
   "projects" for being a shorter word. */
.xtabs.xseg{
  display:inline-grid;grid-template-columns:repeat(3,1fr);
  gap:0;align-items:stretch;
  padding:3px;border-bottom:0;border-radius:99px;
  box-shadow:inset 0 0 0 1px var(--line);
}
.xseg .xtab{
  padding:7px 18px;margin:0;border:0;border-radius:99px;
  text-align:center;font-size:13px;letter-spacing:0;color:var(--ink2);
  transition:color .16s ease,background-color .16s ease;
}
.xseg .xtab:hover:not(.on){color:var(--ink);background:none}
/* black on the lit cell, and heavier so the answer reads before the box —
   the segopt rule, because it is the same control */
.xseg .xtab.on{background:var(--ink);color:#000;font-weight:600}
/* Nothing sits directly under the tabs any more. Explore's three bodies are
   a field of boards in a wrap of their own, and that field sets its own gap
   from the tabs — the same gutter it keeps between two boards. */

/* A wall of frames, each at its own ratio. The columns are real
   elements filled in js (see stillsColumns) rather than css multicol,
   which is what guarantees the top line: every column starts flush,
   and its lead tile shares one ratio so that first row is even too. */
.stillgrid{display:flex;align-items:flex-start;gap:10px}
.stillcol{flex:1 1 0;min-width:0;display:flex;flex-direction:column;gap:10px}
.stillcol .frame{
  width:100%;aspect-ratio:var(--ar,4 / 3);
  border-radius:8px;border:0;
  background-color:var(--plate);cursor:zoom-in;
}

.exgrid{
  display:grid;grid-template-columns:repeat(2,1fr);gap:14px 12px;
  margin-top:16px;
}
@media (min-width:900px){ .exgrid{grid-template-columns:repeat(3,1fr)} }
/* the grid sizes its own cards, unlike the fixed-width row cards */
.exgrid .card{width:auto}

/* ---------------- members ---------------- */
/* newest joiners, faces first */
.newrow{
  display:flex;gap:18px;
  /* centred when the five fit; falls back to a normal scroll when they
     don't, since justify-content:center would otherwise clip the first
     tile out of reach on a narrow screen */
  justify-content:center;align-items:flex-start;
  overflow-x:auto;scrollbar-width:none;
  margin:0 calc(var(--gutter) * -1);
  padding:0 var(--gutter);
}
@media (max-width:520px){ .newrow{justify-content:flex-start} }
.newrow::-webkit-scrollbar{display:none}
.nmem{flex:none;display:block;width:86px;text-align:center}
.nmem .av{
  width:74px;height:74px;border-radius:50%;margin:0 auto;
  background-color:var(--plate);
  /* a border, not a shadow — the strip has no vertical padding to clip into */
  border:1px solid rgba(var(--silk),.28);
  background-clip:padding-box;
}
.nmn{
  display:block;font-size:14px;margin-top:10px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
/* a second craft stacks under the first rather than being dropped */
.nmr{
  display:block;font-size:10.5px;color:var(--ink2);margin-top:2px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.nmr + .nmr{margin-top:1px}
.nmem:hover .nmn{text-decoration:underline;text-underline-offset:3px}

.dcount{
  font-weight:400;letter-spacing:0;text-transform:none;
  font-size:13px;color:var(--ink3);
  font-variant-numeric:tabular-nums;
}
.members{display:flex;flex-direction:column}
.member{
  display:flex;align-items:center;gap:14px;
  padding:14px 0;border-bottom:1px solid var(--edge);
}
.member:last-child{border-bottom:0}
.member .av{width:44px;height:44px;border-radius:50%;flex:none;background-color:var(--plate)}
.mtext{flex:1;min-width:0}
.mn{display:block;font-size:18px;font-weight:600;letter-spacing:-.005em}
.mr{display:block;font-size:15px;color:var(--ink2);margin-top:3px}
.mcount{flex:none;font-size:15px;color:var(--ink3);font-variant-numeric:tabular-nums}
.member:hover .mn{text-decoration:underline;text-underline-offset:3px}

/* ---------------- edit profile ---------------- */
.etabs{display:flex;gap:4px;margin:24px 0 0;border-bottom:1px solid var(--edge)}
.etab{
  padding:11px 14px;margin-bottom:-1px;
  font-size:13px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
  color:var(--ink3);border-bottom:2px solid transparent;
}
.etab:hover{color:var(--ink2)}
.etab.on{color:var(--ink);border-bottom-color:var(--ink)}
.epanel.hidden{display:none}

/* a heading you can type in: it only looks like a field once you are in it */
.secname{outline:0;cursor:text;border-bottom:1px solid transparent}
.secname:hover{border-bottom-color:var(--edge)}
.secname:focus{border-bottom-color:var(--ink)}

/* controls that live on the row itself, on your own profile */
.sechead .secctl{display:flex;align-items:center;gap:4px;margin-left:auto}
/* the sections you are not showing — revealed by the plus on the top row */
.addsec{display:none;flex-wrap:wrap;gap:8px;margin-top:20px}
.addsec.open{display:flex}
.addsec .vis{color:var(--ink2)}
.addsec .vis:hover{color:var(--ink)}
/* a removed press item is still there, just struck out and restorable */
/* dragging */
.secblock{border-radius:6px}
.secblock.dragging{opacity:.4}
.secblock.over{box-shadow:0 -2px 0 var(--ink)}
.rrow.dragging{opacity:.4}
.rrow.over{box-shadow:0 -2px 0 var(--ink)}
/* share sits with the page's own actions, quiet until hovered */
.share{position:relative;color:var(--ink2)}
.share:hover{color:var(--ink)}
.share.copied::after{
  content:"link copied";
  position:absolute;top:calc(100% + 6px);right:0;
  padding:5px 9px;border-radius:6px;white-space:nowrap;
  font-size:13px;color:var(--ink);background:var(--surface);
  box-shadow:inset 0 0 0 1px var(--line);
}
.idrow .share{flex:none}

.grip{cursor:grab}
.grip:active{cursor:grabbing}
.grip svg{width:15px;height:15px}

/* Press: a section header that also opens */
.secdrop > summary{list-style:none;cursor:pointer}
.secdrop > summary::-webkit-details-marker{display:none}
.secdrop > summary > svg{
  flex:none;width:12px;height:12px;color:var(--ink3);
  transition:transform .22s ease;
}
.secdrop[open] > summary > svg{transform:rotate(180deg)}
.presslist{margin-top:0}
@media (prefers-reduced-motion: reduce){ .secdrop > summary > svg{transition:none} }
.prow{display:flex;align-items:flex-start;gap:12px}
.prow.off .pitem{opacity:.4;text-decoration:line-through;text-decoration-thickness:1px}
.prow .pitem{flex:1;min-width:0}
.prow .secctl{align-self:center;margin-left:0}
.pedit{
  display:flex;flex-direction:column;gap:8px;
  padding:11px 0;border-bottom:1px solid var(--edge);
}
.pedit input{
  width:100%;min-width:0;font:inherit;font-size:16px;color:var(--ink);
  background-color:transparent;border:0;border-bottom:1px solid var(--line);
  padding:7px 0;border-radius:0;
}
.pedit input.yr{flex:0 0 68px;width:68px}
.pedit .rline{display:flex;align-items:center;gap:12px}
.pedit .rline > input:first-child{flex:1}
.pedit input::placeholder{color:var(--ink3)}
.pedit input:focus{outline:0;border-bottom-color:var(--ink)}

.ord{
  flex:none;display:flex;align-items:center;justify-content:center;
  width:34px;height:34px;border-radius:99px;
  color:var(--ink2);
}
.ord svg{width:17px;height:17px}
.ord:hover:not(:disabled){color:var(--ink);background:rgba(var(--silk),.09)}
/* .25 of ink3 on black was invisible; a disabled control still has to read */
.ord:disabled{opacity:.45;cursor:default}
.vis{
  flex:none;font-size:13px;padding:6px 12px;border-radius:99px;
  color:var(--ink3);box-shadow:inset 0 0 0 1px var(--line);
}
.vis.on{color:var(--ink)}
.vis:hover{box-shadow:inset 0 0 0 1px var(--ink)}

.pick{position:relative;display:block;width:100%;text-align:left;cursor:pointer}
.pick .preview{
  background-color:var(--plate);
  border:1px solid rgba(var(--silk),.28);
  background-clip:padding-box;
}
.pickbanner .preview{width:100%;aspect-ratio:2.76 / 1;border-radius:5px}
/* the clip sits in the preview the way it sits in the banner: laid over the
   box at whatever the crop asked for, and clipped by it */
.pick .preview.motion{position:relative;overflow:hidden;display:block}
.pick .preview.motion > video{position:absolute;display:block;height:auto}
.pickavatar .preview{width:78px;height:78px;border-radius:50%}
.pickcover .preview{width:220px;aspect-ratio:4 / 3;border-radius:5px}
.picklabel{
  position:absolute;right:10px;bottom:10px;
  font-size:13px;padding:6px 12px;border-radius:99px;
  background:rgba(0,0,0,.6);color:var(--ink);
  box-shadow:inset 0 0 0 1px var(--line);
}
.pickavatar .picklabel{position:static;display:inline-block;margin-left:14px;vertical-align:middle}
.pick:hover .picklabel{box-shadow:inset 0 0 0 1px var(--ink)}
.pickavatar{display:flex;align-items:center}

/* One row per credit: who it is, then the only part this page can change.
   The name is not a field — reassigning a credit is an ingest job, where a
   handle can be checked against the member list. */
.croles{display:flex;flex-direction:column}
.crole{display:flex;align-items:center;gap:12px}
.crole .av{
  width:30px;height:30px;border-radius:50%;flex:none;
  background-color:var(--plate);box-shadow:0 0 0 1px var(--line);
}
.crole .nm{font-size:16px;flex:none}
.crole input{
  flex:1;min-width:0;font:inherit;font-size:17px;color:var(--ink);
  background:transparent;border:0;border-bottom:1px solid var(--line);
  padding:8px 0;border-radius:0;
}
.crole input::placeholder{color:var(--ink3)}
.crole input:focus{outline:0;border-bottom-color:var(--ink)}

/* Roles were a wall of chips you turned on and off; they are a list with a
   chooser now, the same shape as Links, so .toggles and .toggle have no user
   left. Removed rather than kept warm — a class nothing uses is a class the
   next person has to read before finding that out. */

/* the line that tells you a change landed */
.toast{
  position:fixed;left:50%;z-index:70;
  bottom:calc(var(--tab-h) + 22px + env(safe-area-inset-bottom));
  transform:translate(-50%,10px);
  padding:9px 16px;border-radius:99px;white-space:nowrap;
  font-size:14px;color:var(--ink);background:var(--surface);
  box-shadow:inset 0 0 0 1px var(--line),0 18px 36px -14px rgba(0,0,0,.95);
  opacity:0;pointer-events:none;
  transition:opacity .2s ease,transform .2s ease;
}
.toast.on{opacity:1;transform:translate(-50%,0)}
@media (min-width:900px){ .toast{bottom:26px} }
@media (prefers-reduced-motion: reduce){ .toast{transition:none} }

/* ---------------- settings ---------------- */
/* a value you can read but not set, sitting where its input would */
.readonly{margin:0;padding:8px 0;font-size:16px;color:var(--ink2)}
.setlist{display:flex;flex-direction:column}
.setrow{
  display:flex;align-items:center;gap:16px;width:100%;
  padding:14px 0;border-bottom:1px solid var(--edge);text-align:left;
}
.setrow:last-child{border-bottom:0}
.setlabel{flex:1;min-width:0;font-size:16px}
.setlabel em{
  display:block;margin-top:3px;
  font-style:normal;font-size:14px;color:var(--ink3);
}
/* the switch is the only place a filled shape means on */
.switch{
  flex:none;width:38px;height:22px;border-radius:99px;
  box-shadow:inset 0 0 0 1px var(--line);
  display:flex;align-items:center;padding:0 3px;
  transition:background .18s ease,box-shadow .18s ease;
}
.switch i{
  width:16px;height:16px;border-radius:50%;background:var(--ink2);
  transition:transform .18s ease,background .18s ease;
}
.switch.on{background:var(--ink);box-shadow:none}
.switch.on i{transform:translateX(16px);background:#000}
.setrow:hover .switch{box-shadow:inset 0 0 0 1px var(--ink)}
.setrow:hover .switch.on{box-shadow:none}
.inlink{color:var(--ink);text-decoration:underline;text-underline-offset:3px}
@media (prefers-reduced-motion: reduce){ .switch,.switch i{transition:none} }

/* ---------------- upload ---------------- */
.dropzone{
  margin-top:20px;padding:38px 20px;
  display:flex;align-items:center;justify-content:center;
  border-radius:6px;cursor:pointer;
  box-shadow:inset 0 0 0 1px var(--line);
  background:rgba(var(--silk),.03);
  transition:background .18s ease,box-shadow .18s ease;
}
/* display:flex outranks the hidden attribute's display:none, which is
   how the cover's dropzone kept sitting above the crop it had filled */
.dropzone[hidden]{display:none}
.dropzone:hover,.dropzone.over{background:rgba(var(--silk),.07);box-shadow:inset 0 0 0 1px var(--ink)}
.dropmsg{font-size:15px;color:var(--ink2)}
.dropzone.over .dropmsg{color:var(--ink)}

/* the empty cover: a 4:3 tile the size of the frame it will become */
.coverpick{
  width:180px;aspect-ratio:4 / 3;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;
  border-radius:4px;cursor:pointer;color:var(--ink2);
  box-shadow:inset 0 0 0 1px var(--line);
  background:rgba(var(--silk),.03);
  transition:background .18s ease,box-shadow .18s ease,color .18s ease;
}
.coverpick:hover,.coverpick.over{
  background:rgba(var(--silk),.07);box-shadow:inset 0 0 0 1px var(--ink);color:var(--ink);
}
.coverpick svg{width:20px;height:20px}
/* .arlabel, not .picklabel: the edit-profile picker owns that name and
   pins it absolutely, which threw this one to the corner of the page */
.arlabel{font-size:13px;letter-spacing:.08em;color:var(--ink3)}

/* the cover's framing window: a fixed 4:3 the picture moves under */
.cropbox{
  width:180px;aspect-ratio:4 / 3;
  border-radius:4px;background-color:var(--plate);
  background-repeat:no-repeat;
  border:1px solid rgba(var(--silk),.42);background-clip:padding-box;
  position:relative;cursor:grab;touch-action:none;
}
.cropbox.dragging{cursor:grabbing}
/* Thirds, over any framing window — the banner and the cover both. Drawn
   as gradients rather than four elements, and lifted while you drag so
   the lines are strongest at the moment you are judging against them. */
.cropbox::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  --line-c:rgba(var(--silk),.34);
  background-image:
    linear-gradient(90deg,
      transparent calc(33.333% - .5px), var(--line-c) calc(33.333% - .5px),
      var(--line-c) calc(33.333% + .5px), transparent calc(33.333% + .5px),
      transparent calc(66.667% - .5px), var(--line-c) calc(66.667% - .5px),
      var(--line-c) calc(66.667% + .5px), transparent calc(66.667% + .5px)),
    linear-gradient(180deg,
      transparent calc(33.333% - .5px), var(--line-c) calc(33.333% - .5px),
      var(--line-c) calc(33.333% + .5px), transparent calc(33.333% + .5px),
      transparent calc(66.667% - .5px), var(--line-c) calc(66.667% - .5px),
      var(--line-c) calc(66.667% + .5px), transparent calc(66.667% + .5px));
  /* a hair of shadow, so the lines hold over a bright frame too */
  filter:drop-shadow(0 0 1px rgba(0,0,0,.6));
  opacity:.75;transition:opacity .18s ease;
}
.cropbox.dragging::after{opacity:1}
@media (prefers-reduced-motion: reduce){ .cropbox::after{transition:none} }
/* the banner is framed at the width it will be shown at, not in a tile */
.cropbox.wide{width:100%;aspect-ratio:2.76 / 1;margin-top:0}
.cropbox.wide + .cropbar{max-width:none}
.pick[hidden]{display:none}
.crophint{
  position:absolute;left:10px;bottom:10px;
  padding:5px 9px;border-radius:99px;
  font-size:13px;color:var(--ink);background:rgba(0,0,0,.55);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  pointer-events:none;transition:opacity .18s ease;
}
.cropbox.dragging .crophint{opacity:0}
/* The cover as it is, at the film's own ratio. There is no window to aim any
   more, so the picture is the whole control and the button under it swaps
   one for another. */
.coverprev{
  /* 180, the width of the empty drop zone it replaces — picking a cover
     should not make the field jump to four times the size. It was taking the
     full column, which put the smallest decision on the page in the biggest
     box on it. */
  width:180px;aspect-ratio:4 / 3;border-radius:5px;
  background-color:var(--plate);
  background-size:cover;background-position:center;background-repeat:no-repeat;
  /* A HAIRLINE, BECAUSE THIS ONE IS A THING YOU ARE SETTING.

     A cover on a shelf has no outline on purpose — a wall of framed items
     reads as a set of frames rather than as the work. This is not that wall:
     it is one picture in a form, sitting on the same dark ground as the
     empty picker it replaced, and without an edge a dark cover simply
     dissolves into the field. The line says where the cover stops.

     Silk at .42 over background-clip:padding-box — the same hairline the
     stills wear, drawn on the border box so the picture does not print
     underneath it and dull it. */
  border:1px solid rgba(var(--silk),.42);
  background-clip:padding-box;
}

/* 12 under the picture, the same as everything else inside a field */
.cropbar{display:flex;align-items:center;gap:14px;margin-top:12px;max-width:180px}

/* BESIDE THE COVER, NOT UNDER IT.

   The three marks are a toolbar for the picture, and a toolbar under a
   picture reads as the next thing down the form rather than as part of the
   one above it — the 12px it sat at is the same 12 that separates a label
   from its control everywhere else here, so the eye had no way to tell
   "belongs to this" from "comes after this".

   To the right and stacked, it is unmistakably the picture's own. Three
   34px discs at the bar's own 14 gap come to 130, against a 4:3 cover 135
   tall: the column ends where the picture ends without either being told
   the other's height. */
.coverstage{display:flex;align-items:flex-start;gap:12px}
.coverstage > .cropbar{
  flex-direction:column;
  margin-top:0;
  max-width:none;
}
/* the banner's bar is the banner's width — 180 is the cover cropbox's figure
   and would put the bin under the left third of a picture that runs the whole
   field. Right, where a control that acts on the thing above it goes. */
.cropbar.wide{max-width:none;justify-content:flex-end}
.zoomwrap{
  flex:1;display:flex;align-items:center;gap:10px;
  font-size:13px;letter-spacing:.06em;text-transform:uppercase;color:var(--ink3);
}
.zoomwrap input{flex:1;accent-color:var(--ink);cursor:pointer}
.fieldhead.spaced{margin-top:30px}
.field.spaced{margin-top:26px}
/* the first heading under a page title needs the same air as the rest */
.ptitle + .fieldhead.spaced{margin-top:26px}
.shotn{font-size:13px;color:var(--ink3);font-variant-numeric:tabular-nums}

/* The strip stands in for the field's heading, so it takes the heading's
   place rather than adding to it: no top margin of its own, and the gap
   under it is the one the drop zone already keeps. */
/* SMALLER THAN A PAGE'S TABS, BECAUSE IT IS A FIELD'S.

   .xtab is sized for navigation — the strip across a profile that switches
   what the whole page is showing. This one sits inside a field and switches
   what one box collects, so it was speaking a size louder than its job.

   13px is the form's own label size, the one every fieldhead beside it
   already uses, so the strip now weighs what the word "Stills" weighed when
   it was a heading. The gap and the underline gap come down with it, or a
   smaller word sits in a strip built for a bigger one. */
.uptabs{margin-top:0;gap:18px}
/* A SEGMENTED TRACK, AND A HIGHLIGHT THAT TRAVELS.

   One rounded box holding both answers with the live one lit, rather than
   two pills side by side — this is a single setting, and the shape should
   say so. The lit half is a real element, so it slides from one side to the
   other instead of being repainted somewhere else, and you can see which way
   the answer moved.

   --at is 0 or 1 and the thumb translates by its own width, which is why the
   halves are equal: travel is exactly 100% and nothing has to be measured.
   The buttons sit above it and only carry type; the track carries the ring
   and the padding that inset the thumb from it. */
.segtrack{
  position:relative;display:grid;grid-template-columns:1fr 1fr;
  padding:3px;border-radius:99px;box-shadow:inset 0 0 0 1px var(--line);
}
.segthumb{
  position:absolute;z-index:0;top:3px;bottom:3px;left:3px;
  width:calc(50% - 3px);border-radius:99px;background:var(--ink);
  transform:translateX(calc(var(--at, 0) * 100%));
  transition:transform .24s cubic-bezier(.32,.72,0,1);
}
@media (prefers-reduced-motion:reduce){ .segthumb{transition:none} }
.segopt{
  position:relative;z-index:1;
  padding:7px 0;border-radius:99px;
  font-size:13px;color:var(--ink2);
  transition:color .16s ease;
}
.segopt:hover:not(.on):not(.locked){color:var(--ink)}
/* black on the lit half, and heavier so the answer reads before the box */
.segopt.on{color:#000;font-weight:600}

/* UNDER THE PICKER, NOT OVER IT.

   The stills strip is a heading — it names the field. This one is not: the
   field is called Project cover and this says what the next file may be, so
   it sits beneath the picture it governs. */
/* EVENLY BETWEEN THE NAME AND THE BOX.

   Three things stacked — what the field is, which kind, where it goes — and
   the track is the middle one, so it sits the same distance from each. It
   used to be 8 above and 12 or 20 below, which read as a second line of the
   heading; then 8 and 4, which glued it to the box. 8 either way and it is
   simply the middle of three.

   12, not the 8 a subtitle keeps under itself. 8 is the gap between a label
   and the thing it names — two parts of one object — and this is not that:
   it is a control of its own with a name over it and a box under it, and at
   8 it was crowded into both. Even on each side, and enough of it that the
   three read as three. */
/* The top margin is the heading's own 12 — adjoining margins collapse to
   the larger of the two, so writing it again here would change nothing and
   writing less would be silently ignored. Only the bottom is this rule's. */
.covertabs,.stilltabs{margin-bottom:12px;width:180px}
/* a question with one answer draws no track, and an empty box must not keep
   the 12 it was holding for one */
.covertabs:empty,.stilltabs:empty{display:none}
/* the drop zone's own 20px top margin would push the box away and leave the
   track sitting against the name again */
.stilltabs + .dropzone{margin-top:0}
/* a half the one-format rule has closed: dimmed and dead, and the thumb
   cannot travel to it */
.segopt:disabled{color:var(--ink3);opacity:.4;cursor:default}
/* THE SAME DIMMING, STILL PRESSABLE.

   The cover track's other half while a cover is in the slot: a clip and a
   still are not two labels for one picture, so the answer cannot be changed
   without emptying the slot the bin under it empties. It looks exactly as
   spent as a disabled half, because it is shut for the same reason — but a
   disabled button swallows the press and says nothing, and the moment
   somebody presses this is the one moment they need telling why. So it
   takes the press and answers it, and the cursor says there is something
   there to press. */
.segopt.locked{color:var(--ink3);opacity:.4}
/* a moving cover fills its box the way a still one does */
video.coverprev{display:block;object-fit:cover;background-color:var(--plate)}
.uptabs .xtab{font-size:13px;padding-bottom:9px}

/* A clip fills its frame the way a cover does — the box is the shape the
   film is, and the picture inside it should not letterbox itself. */
.shot video.frame{display:block;object-fit:cover;background-color:var(--plate)}
/* ONE HEIGHT, AND THE WIDTH FOLLOWS THE SHAPE.

   A grid of equal columns fixed the width and let the height fall out of
   each frame's ratio, which made a 2.35 a sliver and a 4:3 a slab — the
   frames were true but the wall was ragged, and what you are checking here
   is the set, not one picture.

   Turned round, they read as a strip of film: every frame the same height,
   each as wide as its own shape makes it, so a scope frame is visibly the
   wide one rather than the short one. This is the treatment the popular
   stills row already uses, for the same reason.

   Height on the frame and width auto, so aspect-ratio resolves the other
   way — set both and the ratio is ignored. */
.shots{display:flex;flex-wrap:wrap;gap:10px;margin-top:12px}
.shot{position:relative;flex:none}
/* The frame at the shape it was shot in. 4:3 is only the fallback, for the
   upload form where there is no project ratio yet — forcing it here cropped
   every tile to a thumbnail window, which made a 16:9 cover look zoomed in
   next to the film it came from. The 4:3 window belongs to the cover
   section, which is the one place it is the thing being set. */
.shot .frame{
  /* square and bare, like every other still — these are the frames you just
     dropped in, shown the way they will be shown */
  height:120px;width:auto;aspect-ratio:var(--ar, 4 / 3);
  border-radius:8px;border:0;background-color:var(--plate);
}
/* the bar under a frame carries a grip, a number and a bin; a tall narrow
   frame would squeeze them into each other, so the tile keeps a floor */
.shot{min-width:104px}

/* grip left, buttons right, the label taking whatever is between them — the
   cover slot carries one control more than the rest and must not crowd */
.shotbar{display:flex;align-items:center;justify-content:space-between;gap:6px;margin-top:6px}
.shotbar .shotn{flex:1;min-width:0;text-align:center;overflow:hidden;white-space:nowrap}
/* Slot one has no grip — the picture it holds is set in the cover section
   below, not dragged around here — so its number needs the empty half of
   the bar to stay under the middle of its own tile. */
.shot.cover .shotn{padding-left:19px}
.shotbar button{font-size:13px;color:var(--ink2)}
.shotbar .rdel svg,.shotbar .xdel svg{width:13px;height:13px}
.shotbar button:hover{color:var(--ink)}

.field{display:block;margin-top:20px}

/* ---------------- a link, as a row ---------------- */
/* Handle, kind, address, bin — left to right in the order you read them,
   and the address takes the slack because it is the only part that varies
   in length. The picker is sized to its longest label so the addresses all
   start at the same x down the list. */
.linkkind{
  flex:0 0 128px;font:inherit;font-size:15px;color:var(--ink);
  background:transparent;border:0;border-bottom:1px solid var(--line);
  padding:7px 0;border-radius:0;cursor:pointer;
}
.linkkind:focus{outline:0;border-bottom-color:var(--ink)}
.linkkind option{background:var(--surface);color:var(--ink)}
.linkurl{flex:1;min-width:0}
/* A role row has no address after the chooser, so the chooser is the row —
   it takes the width the url would have taken rather than sitting at 128
   with the bin adrift at the far end of an empty line. */
.rolekind{flex:1;min-width:0}
/* the add sits under the list, at the list's own left edge — it is the next
   row, not a control floating beside the heading */
/* CLOSE UNDER THE LIST, NOT ADRIFT BENEATH IT.

   One rule, not three. There were: a bare margin-top:14px, this, and a third
   left behind by a sheet that no longer exists — between them the add sat a
   clear gap below the links it adds to, far enough to read as a separate
   thing on the page rather than the next line of that list. */
.addrow{
  display:flex;align-items:center;gap:9px;width:100%;
  margin-top:2px;padding:8px 0;text-align:left;
  font-size:15px;color:var(--ink2);
}
.addrow svg{width:15px;height:15px;flex:none}
.addrow:hover{color:var(--ink)}

/* ---------------- how a form is spaced ---------------- */
/* ROOM, NOT RULES — AND THE SAME ROOM IN EVERY FORM.

   Fields sat 20px from each other while a label sits 8px from its own
   control: near enough the same figure that a form read as one long stack of
   things all the same distance apart, with no way to see where one question
   ended and the next began. A gap only says "these are separate" if it is
   bigger than the gaps inside them.

   So the separation is 22 above and below, and nothing else — a hairline on
   each seam said the same thing twice, and eight of them down a form is a
   page of lines with the fields in between.

   Padding rather than margin, so the two never collapse into one: adjoining
   margins give 22 where the eye needs 44.

   One class on the container, worn by the profile editor and the project
   sheet alike, because they are the same form asked in two places. */
.formflow > .field,
.formflow > .fieldrow{
  margin-top:0;
  /* 16 and 16. It was 22, which made 44 between one question and the next
     against 12 inside a field — a ratio that is right in principle and too
     big in practice, because a form is not a page of blocks: the fields are
     all the same shape and they already read as separate without being held
     that far apart. At 32 the separation is still comfortably more than
     twice the 12 within, and the form stops running a screen longer than the
     questions it asks. */
  padding:16px 0;
}
/* the title or the sheet's own head is already a separation */
.formflow > .ptitle + .field,
.formflow > .sheethead + .field{padding-top:4px}
/* a pair on one line is one field's worth: .fieldrow carried no margin at
   all, so Format and Year sat flush against Title — the one place with no
   gap, in a form whose whole problem was the gaps */
.formflow .fieldrow > .field{margin-top:0;padding:0}
/* the same 16 every other seam keeps. It was 24 against a 22 above it,
   which made the one gap on the form that nothing explained slightly the
   largest one on it. */
.formflow > .publishrow{margin-top:0;padding-top:16px}

/* A FORM IS NARROWER THAN THE PAGE, AND STARTS WHERE THE NAME DOES.

   Everything else on the site runs to the column's edge because it is
   pictures: a shelf of covers wants every pixel of the window, and the
   gutter is the only thing holding it off the glass. A form is type and
   boxes, and at the full 60vw the fields ran long enough that the label at
   the left and the control at the right stopped reading as one question.

   So the two form pages pull in by the wordmark's own inset. Not an
   arbitrary narrower figure — "dissolvd" is the first thing on the page and
   the leftmost type on it, and a field that begins on that same line reads
   as hanging off the name rather than as a second margin someone chose.
   --brand-in is the bar's figure, so the two move together at every width,
   including the narrower 10px it takes under 1150.

   The sheet is left out: it is a box on a backdrop with padding of its own,
   and it never had the page's gutter to narrow. */
.uploadpage > .wrap,
.editprofile > .wrap,
.settingspage > .wrap,
.authpage > .wrap{
  padding-left:calc(var(--gutter) + var(--brand-in));
  padding-right:calc(var(--gutter) + var(--brand-in));
}

/* SIGNING IN IS A FORM, AND NOTHING ELSE IS ON THE PAGE.

   Same shell as Upload and Settings — the indent, the titled rule, the pill
   at the foot — because it asks questions the same way they do and there is
   no reason for the first page anyone sees to have a rhythm of its own.

   What is different is what is missing. No bar and no tabs, so the page owes
   nothing at the bottom (.page reserves the tab bar's height, which here is
   dead space under a five-line form) and it has to open at a height someone
   chose, rather than flush against the top of the window where the bar used
   to be. Narrower than the other forms too: four fields at 60% of a wide
   screen is a login the width of a film still. */
.authpage{padding-bottom:60px}
.authpage > .wrap{max-width:min(var(--col), 420px);padding-top:56px}
@media (min-width:900px){ .authpage > .wrap{padding-top:12vh} }
/* The wordmark is the header here. It is the same type as the bar's — this
   is the site, not a page about it — set above the title with the section
   gap between them so it reads as the mark on the page rather than a first
   line of the form. */
.authbrand{display:block;color:var(--ink2)}
.formflow > .authbrand + .formtitle{margin-top:var(--s-step)}

/* THE HEADING SITS WITH THE FORM; ITS RULE GOES BACK OUT TO THE PAGE.

   The fields are indented to the wordmark, and the title is indented with
   them — it names the form, so it starts where the form starts. The line
   under it does not: negative margins hand back exactly the --brand-in the
   column took, so the hairline runs to the page's own edge, the line every
   shelf, cover and picture on the site begins and ends on.

   That is the whole point of it. A rule the same width as the fields would
   just be an underlined title, one more thing inside the indent. Crossing
   back out makes it the bottom of the header band and the top of the form,
   and it puts the site's two margins on screen at once — you can see the
   form has been pulled in, rather than wondering whether the page is narrow
   today. Same trick the shelves use to bleed a strip past the gutter, one
   step smaller.

   Padding restores the inset the negative margin removed, so the type stays
   on the form's line while its border box runs wider. */
.formflow > .formtitle{
  margin:8px calc(var(--brand-in) * -1) 0;
  padding:0 var(--brand-in) 14px;
  border-bottom:1px solid var(--edge);
}
/* 4px is what a bare title gives the field under it — that figure assumes
   the title's own descender-space is the whole separation. Here there is a
   rule in between, and type set straight onto a hairline reads as a caption
   for it. So it takes the form's own 16, and the first seam on the page is
   the same size as every seam after it. */
.formflow > .formtitle + .field{padding-top:16px}
/* and the same 16 when what follows the title is a section label rather than
   a field — Settings opens on one, and .ptitle + .fieldhead.spaced's 26 was
   measured against a title with no rule under it */
.formflow > .formtitle + .fieldhead.spaced{margin-top:16px}

/* THE PROJECT EDITOR OPENS THE WAY THE UPLOAD FORM DOES, RULE AND ALL.

   They are the same form: the same fields in the same order, one with the
   answers already in it. Both heads name the form rather than its contents
   — "Upload a new project" and "Edit a project" — both sit at the same size,
   and both have a hairline under them.

   The rule came off for a while on the argument that a card already has an
   edge, so a line 26px inside it would be a second edge parallel to the
   first. That reads better on paper than on screen: the card's edge is a
   hairline round a whole form and this one is a band across the top of it,
   and the two are doing different jobs at different scales. What you
   actually see without it is a title floating over a stack of fields.

   Bled to the card's own edge rather than stopping at the head, because
   that is the difference between the bottom of a header band and an
   underlined title — --sheet-pad handed back as negative margin and paid
   again as padding, so the type does not move.

   14 under the head and 16 above the first field is the 30 the page keeps
   across its own rule, so the two forms open at the same height. */
.formflow > .sheethead{
  margin:0 calc(var(--sheet-pad) * -1);
  padding:0 var(--sheet-pad) 14px;
  border-bottom:1px solid var(--edge);
}
/* 25, .ptitle's own size and the size the two form pages set their titles.
   The sheet trimmed it to 23 back when it was a card on a lighter ground
   and wanted to sit quieter than a page; it is on the page's own ground
   now, and a heading that is two points smaller for no reason on screen
   just reads as a different form. */
.formflow > .sheethead .ptitle{font-size:25px}
/* the same 16 under the rule that the pages keep under theirs */
.formflow > .sheethead + .field{padding-top:16px}

/* THE SHEET IS THE PAGE, NOT A CARD ON IT.

   It was --surface, the colour menus and toasts are, which made editing a
   project feel like a panel had opened over the site while editing a profile
   was just the site. Same ground as the page it edits; the backdrop behind
   it is what says it is a layer, and that has never needed the box to be
   lighter to make the point. */
#pe-sheet .sheetbox{background:var(--bg)}
/* [hidden] is a user-agent rule and loses to any class that sets display, so
   a field hidden by the attribute alone stayed on screen — an empty heading
   with nothing under it. */
.field[hidden]{display:none}
/* 13, the same as .fieldhead > span. These are one label in two markups —
   <label class=field><span> and <div class=fieldhead><span> — and they were
   set a pixel apart, so Name read larger than Links for no reason anyone
   could see. */
/* the same 12 .fieldhead keeps. A field whose label happens to be a bare
   span is the same object as one whose label is a .fieldhead, and Title
   sitting 8px above its input beside Credits at 12 was two spacings for one
   relationship. */
.field > span{
  display:block;font-size:13px;font-weight:600;
  letter-spacing:.09em;text-transform:uppercase;color:var(--ink3);
  margin-bottom:12px;
}
/* 15 — what a line of type is everywhere else on the site. At 17 the form
   was the largest reading on any page, and what you type into a field ought
   to be the size it will be when someone reads it. */
.field input,.field textarea{
  width:100%;font:inherit;font-size:15px;color:var(--ink);
  background:transparent;border:0;border-bottom:1px solid var(--line);
  padding:8px 0;border-radius:0;
}
/* the box grows with the writing, so there is no handle to drag and no
   scrollbar to hide the line you are on */
.field textarea{resize:none;overflow:hidden;line-height:1.5;display:block}
/* background-color, not the shorthand — `background:` would wipe the chevron */
.field select{
  width:100%;font:inherit;font-size:15px;color:var(--ink);
  background-color:transparent;border:0;border-bottom:1px solid var(--line);
  padding:8px 24px 8px 0;border-radius:0;cursor:pointer;
  appearance:none;-webkit-appearance:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23968f85' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 2px center;background-size:12px 12px;
}
.field select:focus{outline:0;border-bottom-color:var(--ink)}
.field select option{background:var(--surface);color:#EEE5D9}

.field input::placeholder,.field textarea::placeholder{color:var(--ink3)}
.field input:focus,.field textarea:focus{outline:0;border-bottom-color:var(--ink)}
.fieldrow{display:flex;gap:20px}
.fieldrow .field{flex:1}
/* wide enough that WATCH TIME stays on one line — at the larger label size
   it wrapped, and a wrapped label pushes its input out of line with the
   field beside it */
.fieldrow .field.short{flex:0 0 116px}

/* a label with an add button beside it */
/* 12, so one number covers every gap of this kind in the app.

   It was 8. The still/motion track needed more air than that — at 8 it was
   crowded between the name above it and the box below — and giving it 12
   made two subtitles in the form sit differently from the rest. Raising the
   gap itself rather than exempting a field keeps every subtitle at the same
   distance from what it names, and the track at the same distance from both
   its neighbours. */
/* THE PLUS IS CENTRED ON THE LETTERS, NOT ON THE BOX ROUND THEM.

   align-items:center lines up the flex items' boxes, and a line box is not
   the word in it: at 13px this font puts about 3.8px of empty above the
   capitals and 4.8px below the baseline, so the ink sits high inside its own
   box. Centre that box against a 24px ring and the ring comes out half a
   pixel low — measured dead centre, and visibly sitting under the word,
   which is exactly the amount a hairline circle beside sharp capitals will
   show you.

   text-box trims the box back to the letters, cap to baseline, the same way
   every heading that sits on a rule is trimmed further up this file. Then
   centred means centred on the word.

   min-height is the other half of it. With a plus in it a heading is 24px
   tall because the button is; without one it was whatever the type came to,
   so Project stills and Credits sat on two different bands and the 12px
   under them started from two different places — 16.8px of visible air in
   one and 19.9 in the other. Every heading is the button's height now,
   whether or not it has the button. */
.fieldhead{
  display:flex;align-items:center;gap:10px;margin-bottom:12px;
  min-height:24px;
}
/* EXCEPT WHEN THERE IS NOTHING UNDER IT YET.

   Press link with no links, and Embed video before you paste one, are a
   heading and an empty box: the list renders to nothing and the row is
   hidden. The 12 underneath stayed, so an unfilled field sat 12px taller
   than a filled one is above its own first row, and the two empty fields in
   the middle of the upload form each pushed the seam below them out by that
   much. The gap belongs to the thing it separates the heading from; with no
   such thing, there is nothing to separate. */
.field > .fieldhead:has(+ .rows:empty),
.field > .fieldhead:has(+ [hidden]){margin-bottom:0}
.fieldhead > span{
  font-size:13px;font-weight:600;letter-spacing:.09em;
  text-transform:uppercase;color:var(--ink3);
  text-box:trim-both cap alphabetic;
}
.field > span em,.fieldhead > span em{
  font-style:normal;letter-spacing:.04em;text-transform:none;
  color:var(--ink3);opacity:.7;
}
/* The ring stays. It marks the one thing on a heading you can press, which
   is worth a shape where the marks inside the rows are not. */
.addbtn{
  width:24px;height:24px;border-radius:50%;flex:none;
  display:flex;align-items:center;justify-content:center;
  color:var(--ink2);box-shadow:inset 0 0 0 1px var(--line);
}
.addbtn:hover{color:var(--ink);box-shadow:inset 0 0 0 1px var(--ink)}
/* 12, not 13. 13 in a 24px button leaves 5.5px either side, so the glyph
   started on a half pixel and its 2-unit stroke landed between two — the
   cross came out antialiased unevenly and read as pushed into a corner while
   every measurement said it was dead centre. 12 leaves a whole 6, and at
   half scale the stroke is exactly 1px. */
.addbtn svg{width:12px;height:12px}

/* 12, the figure every other gap inside a field keeps — a label to its
   control, a track to the box under it. At 14 the rows of Credits were the
   one thing on the form spaced to a number used nowhere else. */
.rows{display:flex;flex-direction:column;gap:12px}
.rrow{display:flex;align-items:center;gap:12px}
.rrow .secctl{margin-left:auto}
.rrow.stack{flex-direction:column;align-items:stretch;gap:8px;
  padding-bottom:14px;border-bottom:1px solid var(--edge)}
.rrow.stack:last-child{border-bottom:0;padding-bottom:0}
.rline{display:flex;align-items:center;gap:12px}
.rline > input:first-child{flex:1}
.rrow input{
  flex:1;min-width:0;font:inherit;font-size:15px;color:var(--ink);
  background:transparent;border:0;border-bottom:1px solid var(--line);
  padding:7px 0;border-radius:0;
}
.rrow input.yr{flex:0 0 68px}

/* Credit rows. Separate from .rrow because press rows stack vertically —
   a flex-basis there would set a height, not a width. Name and role sit
   together on the left instead of being flung to opposite column edges. */
.crow{display:flex;align-items:center;gap:12px}
.crow .av{width:22px;height:22px;border-radius:50%;flex:none;background-color:var(--plate)}
.crow .nm{font-size:16px;flex:none}
/* .field select sets width:100%, which squeezed this to nothing and is
   why an added credit showed a role picker and no name at all */
.rrow select,.crow select{width:auto;max-width:44%}
/* a name is a name: 220px holds one comfortably, and the row does not
   need to stretch it across the whole column */
.crow input{
  flex:0 1 220px;min-width:0;width:auto;font:inherit;font-size:16px;color:var(--ink);
  background-color:transparent;border:0;border-bottom:1px solid var(--line);
  padding:7px 0;border-radius:0;
}
/* the field and its list travel together, so the list can hang off it */
.rolewrap{position:relative;flex:0 1 150px;display:flex;min-width:0}
.crow .rolein{flex:1;min-width:0;font-size:15px;color:var(--ink2)}
.crow .rolein:focus{color:var(--ink)}
.sugs{
  position:absolute;top:calc(100% + 6px);left:0;z-index:40;
  min-width:170px;max-height:216px;overflow-y:auto;
  display:flex;flex-direction:column;padding:6px;
  background:var(--surface);border-radius:9px;
  box-shadow:inset 0 0 0 1px var(--line),0 20px 40px -14px rgba(0,0,0,.95);
}
.sugs[hidden]{display:none}
.sug{
  padding:7px 10px;border-radius:6px;
  font-size:14px;color:var(--ink2);cursor:pointer;white-space:nowrap;
}
/* a member suggestion carries the face and the handle, so you are picking a
   person rather than a string that happens to match */
.sug .av{width:22px;height:22px;border-radius:50%;flex:none;background-color:var(--plate)}
.sug:has(.av){display:flex;align-items:center;gap:9px}
.sug .sugn{color:var(--ink)}
.sug .sugh{color:var(--ink3);margin-left:auto;padding-left:12px}
.sug:hover,.sug.on{background:rgba(var(--silk),.08);color:var(--ink)}
/* the row is draggable, so leave the trash on the far edge on its own */
.crow.movable{padding:2px 0}
/* THE ROLES STACK UNDER EACH OTHER, THE NAME SAID ONCE.

   A row is a person. One role is one line and looks exactly as it always
   did; a second adds a line beneath it rather than a second row with the
   name typed out again. The × appears only once there is more than one role
   — with a single role the row's own bin already removes it, and a control
   that does the same thing twice is one too many. */
.crow{align-items:flex-start}
.crow .grip,.crow .crole-add,.crow .rdel,.crow .xdel{margin-top:3px}
.croles{display:flex;flex-direction:column;gap:6px;flex:1 1 220px;min-width:0}
.croleline{display:flex;align-items:center;gap:6px}
.croleline .rolewrap{flex:1;min-width:0}
.crow .rolex{flex:none;width:22px;height:22px;color:var(--ink3)}
.crow .rolex svg{width:11px;height:11px}
.crow .rolex:hover{color:var(--ink)}
.crow .crole-add{margin-left:auto;color:var(--ink3)}
.crow .crole-add svg{width:15px;height:15px}
.crow .crole-add:hover{color:var(--ink)}
.crow .crole-add + .rdel,.crow .crole-add + .xdel{margin-left:0}
.crow .rdel,.crow .xdel{margin-left:auto}
.crow.dragging{opacity:.45}
.crow.over{box-shadow:0 -1px 0 var(--ink)}
.crow input::placeholder{color:var(--ink3)}
.crow input:focus{outline:0;border-bottom-color:var(--ink)}

/* role picker sits inline; background-color, never the shorthand */
.rrow select,.crow select{
  flex:none;font:inherit;font-size:15px;color:var(--ink2);
  background-color:transparent;border:0;border-bottom:1px solid var(--line);
  padding:6px 22px 6px 0;border-radius:0;cursor:pointer;
  appearance:none;-webkit-appearance:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23968f85' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 2px center;background-size:12px 12px;
}
.rrow select:focus,.crow select:focus{outline:0;border-bottom-color:var(--ink);color:var(--ink)}
.rrow select option,.crow select option{background:#111;color:#EEE5D9}
.rrow input::placeholder{color:var(--ink3)}
.rrow input:focus{outline:0;border-bottom-color:var(--ink)}
.rdel{flex:none;color:var(--ink2)}
.rdel:hover{color:var(--ink)}

/* THE END OF A FORM IS ITS BOTTOM RIGHT CORNER.

   Left-aligned, the button that finishes a form sat under its first field
   and a long way from where the last one ended — you filled the form down
   the right of the column and then went back across it to press publish. On
   the right it is where the reading stops.

   The order goes with it: the way out first, the thing you came to do last,
   so the corner holds the button you are actually reaching for. A form is
   read down the right of the column and the corner is where that reading
   stops — putting cancel there makes you travel past the button you want to
   reach the one you don't. That is the order in the markup rather than a
   row-reverse, so tabbing arrives in the same sequence the eye does.

   All seven rows — both form pages, the project editor, the cropper, the
   stills picker, the library sheet and Settings — because a primary button
   that moves corner to corner between two sheets is a button you have to
   look for every time. */
.publishrow{display:flex;align-items:center;justify-content:flex-end;gap:14px;margin-top:28px}
.publishrow .watch{border:0}
.publishrow .watch:disabled{opacity:.35;cursor:not-allowed}

/* ---------------- disclosures ---------------- */
/* ported from the mockup verbatim */
.drop{border-top:1px solid var(--edge);margin-top:var(--s-sect)}
/* Stacked drawers sit flush — they share one rule between them rather than
   each opening a new section. */
.drop + .drop{margin-top:0}
.drop:last-of-type{border-bottom:1px solid var(--edge)}
.dsum{
  list-style:none;cursor:pointer;
  /* chevron sits beside the label, not pushed to the far edge of the column */
  display:flex;align-items:center;justify-content:flex-start;gap:8px;
  padding:16px 0;
  /* Credits and Press name a block of the page, the same as a row's heading
     does — so they say it the same way, at .sechead h2's size and case. The
     chevron and the rules above and below are what mark it as a drawer. */
  font-size:15px;font-weight:600;
  letter-spacing:normal;text-transform:none;
  color:var(--ink);
}
.dsum::-webkit-details-marker{display:none}
.dsum svg{flex:none;color:var(--ink3);transition:transform .22s ease}
.drop[open] .dsum svg{transform:rotate(180deg)}
.dbody{padding-bottom:8px}
/* The crew list, by department: the job on the left, a rule carrying the eye
   across, the people on the right. One grid for every role rather than a
   grid per row, so the name column resolves to a single width and all the
   rules stop at the same place — that shared edge is the whole effect. The
   name column is auto, so it is exactly as wide as the longest name and no
   wider; the rule takes whatever is left. */
/* One row per credit: the face, the name, a dot, the job. No columns and
   nothing measured — each row is as wide as what it says. */
/* THE PERSON, AS ONE CELL.

   Picture and name — and on the summary the chevron too. Centred inside
   itself and 28 tall, so however deep the row gets the face and the name
   keep the first line rather than drifting to the middle of two jobs. The
   name takes the picture's own height as its line, which is what puts a
   one-job row exactly where it sat before any of this. */
.cwho{display:flex;align-items:center;gap:4px;min-width:0}
/* the picture keeps the page's ten to its name; only the rule sits closer */
.cwho > .av{margin-right:6px}
/* The name holds its own width and the rule takes the remainder, so the rule
   still starts where each name ends. The cap is what stops one long name
   from setting the rule's start for the whole page: past 13 characters a
   name gives way at its end instead. */
/* THE CAP GOES ON THE NAME, NOT ON WHAT HOLDS IT.

   On a credit row the picture and the name are siblings, but on the summary
   the link wraps both — so capping the wrapper spent 34 of the name's 96 on
   the picture inside it, and the lead was the one row whose name still cut
   off. The cap is on the two names themselves; the wrapper only has to be
   allowed to shrink. */
.cwho > .cn,.cwho > .bywho{min-width:0}
.cwho > .cn,.bywho > .bn{max-width:var(--cred-name)}
.crewp .cn{line-height:28px}
.crewp .av{
  width:28px;height:28px;border-radius:50%;flex:none;
  background-color:var(--plate);box-shadow:0 0 0 1px var(--line);
}
.crewp .cn{font-size:15px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
a.crewp:hover .cn{text-decoration:underline;text-underline-offset:3px}

/* The job, in the row's own voice rather than as a label above a group. */
.byline .rn,.crewp .rn{font-size:15px;color:var(--ink2)}

/* THE LEADER.

   Everything left over between the name and the job, drawn as a hairline. It
   is the only thing in the cell that grows, so it starts where the name ends
   and stops at the column edge — it takes the difference between one name
   and the next, and the jobs stay where the grid put them. That is the point
   of the thing: a column, not a run of sentences.

   The 20 is where the name's baseline falls in its own 28px line: the rule
   sits a pixel under the letters rather than at the foot of the line box,
   which is 8px lower and reads as a rule under the row instead of a rule
   through it. Aligned to the top, so a two-job row does not drag it down to
   the middle of the two.

   A floor of 6 so a row too tight to spare anything still shows the rule as
   a rule: vanishing entirely would put the name and the job flush together
   and lose what tells you they are two different things. It was 12, and the
   six came off it to help pay for the picture. */
/* AND IT STOPS A SPACE SHORT OF WHAT IT IS POINTING AT.

   The rule ran to the end of its own cell, which left it the grid's 4px from
   the tag — one space at 15px, and one space is not enough between a line
   and a box. The ring and the leader met as one mark and the rule looked
   like it was drawn into the tag rather than up to it.

   Four more, taken out of the rule's own length rather than out of the
   track. Widening the column gap would have come off the job column, which
   is at 86px with `sound mixer` measuring 84.7 — the only figure on this
   board with no slack in it at all. A margin here costs nothing: the rule is
   the one thing in the cell that grows, so it simply grows four less, and
   every column stays where it was. */
.byline .lead,.crewp .lead{
  flex:1 1 auto;min-width:6px;
  align-self:flex-start;
  height:1px;margin-top:20px;margin-right:4px;
  background:var(--edge);
}
/* THE JOBS, AS A COLUMN OF THEIR OWN.

   Left-aligned inside the track, because the edge they share is the one the
   rule delivers the eye to — the left one, where the jobs begin. A second
   job lands under the first on that same edge, and nothing is needed between
   them: being in the column is what says they belong to one person.

   The three at the top puts the first job's line box on the picture's
   centre: half of 28 less half of 22. A one-job row therefore sits exactly
   where it did before any of this, and the second job is the only thing
   these rules add to the page. */
.byline .rns,.crewp .rns{
  display:flex;flex-direction:column;align-items:flex-start;
  min-width:0;
  /* the measured overflow in fitRoles is read off this box, so it has to be
     the box that clips */
  overflow:hidden;
  padding-top:3px;
}
/* A BOX APIECE.

   The boundary is the shape, not a mark between two words, so a job needs
   nothing on either side of it to be read as one job.

   SQUARE, NOT ROUND.

   These were 999px pills. A pill is the site's shape for something you press
   — the layout track, the watch button, the chips in the forms — and a job
   on a credit is not a control, it is a fact about the film. Reading them as
   pressable was the whole complaint, and a 3px corner says the same thing
   the round one said about where a job begins and ends without also saying
   "try me". It is the shape a credit takes everywhere it is taken seriously,
   which is why Letterboxd's are square too.

   3 and not 0: at 13px a true right angle catches the eye harder than the
   word inside it, and a hairline ring meeting itself at a point shows every
   imperfection in the rasteriser. 3px is a corner you stop noticing.

   No vertical padding: the 22px line already stands 3.5px clear of a 15px
   glyph top and bottom, and adding to it would have pushed the first job's
   centre off the name's line — the whole block is hung from that 3px above
   it.

   The ring is an inset shadow rather than a border. A border is 2px of box
   this column cannot spare, and at these widths two pixels is a word. */
.rns > .rn{
  line-height:22px;max-width:100%;
  /* five, because every pixel here is taken twice and the column has none to
     give: eight put `producer` over the edge on its own. At 13px caps in a
     22px box it still reads as a tag with room in it. */
  padding:0 5px;
  border-radius:3px;
  background:rgba(var(--silk),.055);
  box-shadow:inset 0 0 0 1px var(--edge);
}
/* A LINE WHEN IT FITS, A STACK WHEN IT DOES NOT.

   Built stacked and tried as a line by fitRoles, which keeps the line only
   if it did not overflow. Whether it fits is a fact about the window: the
   job column is what is left of a board that is a third of 60vw, so
   `colorist` and `editor` share a line from about 1714px up and stack below
   it. There is no css that can ask that — a container query knows the
   board's width without knowing how wide these two words are.

   ONE GAP, IN BOTH DIRECTIONS.

   It was 4 down and 6 across, on the reasoning that a gap on a line has to
   read as a break between two words where a gap in a column only has to
   separate two rounds. That is a real difference in general and the wrong
   call here, because it is the same two jobs held by the same person either
   way — and which way they lie is decided by how wide the window happens to
   be. A distance that changes when the layout flips is a distance saying
   something about the layout, and it has nothing to say about it.

   So `gap`, not row-gap and column-gap, and .oneline stops naming one. Four
   in both directions: colorist beside editor is exactly as far apart as
   sound designer above sound mixer.

   Still not the 14 between one person and the next. That one does say
   something different — it is where one person's jobs stop and the next
   person's begin — and it is the only distance here that should not match.

   Whole words while the question is being asked — see .rns.oneline > .rn. */
.rns{gap:4px}
.rns.oneline{flex-direction:row;align-items:center}
/* Left to shrink, a job would report as fitting and then render with its end
   cut off. The measurement has to be of the words at their real width. */
.rns.oneline > .rn{flex:none;max-width:none;overflow:visible;text-overflow:clip}

/* Its own item in the row, so the flex gap spaces it evenly on both sides
   and it needs no margins. Silk-33 keeps it a mark between two things
   rather than a third thing to read. */
/* THE DOT CARRIES ITS OWN WEIGHT.

   It is the one mark in the interface that means "and also" — between a name
   and the job they held, a format and a year, one craft and the next — and
   at the text's own weight it was the faintest thing on the line it was
   holding together. Bold gives the glyph back some ink without touching the
   words either side of it, and a hair of letter-spacing keeps it from
   closing up on them now that it is heavier.

   Colour is left alone: where a dot was already dimmed to ink3 it stays
   dimmed, because that is about the line it sits in, not about the mark. */
.dot{font-weight:700;letter-spacing:.01em}
/* The credit rows had a dimmed one between the name and the job. A rule
   carries that join now, so there is no dot left in either of them. */

/* Shared by the summary row and the credits under it, so the two read as one
   list. The label treatment Press uses for a publication — small, spaced,
   upper, Silk-33 — allowed under the 15px floor because nothing about it is
   clickable. The rule is drawn through the gap, not under it. */
.byline .rn,.crewp .rn{
  /* ONE UNDER THE NAME, WHICH IS THE ONLY SIZE LEFT.

     Fifteen is the name's own size and set the job level with it — inside a
     round, at the same weight as the person it belongs to, which read as
     soft. Thirteen went the other way and sat at the x-height as fine print
     next to a name half again its size. Fourteen is a step under the name
     and no further: near enough to read without leaning in, far enough that
     the name is plainly the first thing on the line and the job the second.

     In a round of its own it is not competing with the name in any case —
     the pill is the thing you see, and the word only has to be legible once
     you are looking at it. */
  /* NO TRACKING, BECAUSE IT IS NOT CAPS ANY MORE.

     Letter-spacing is a caps device — it opens up a run of capitals that
     would otherwise set too tight. Lower case is already spaced by its own
     ascenders and descenders, and tracking it only makes it loose. Here that
     is width as well as looseness: dropping .04 gives `sound mixer` back its
     whole word in an 86px column, which no other change on this rule could
     buy. */
  font-size:14px;letter-spacing:normal;text-transform:lowercase;
  color:var(--ink2);
  /* a job longer than the column gives way rather than moving the rule */
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
/* ---- adding a link where the links are ---- */
/* The field is the next line of the list, so it is set like one: same rule
   under it, same left edge, and nothing around it until you are in it. */
.pnew{
  flex:1;min-width:0;font:inherit;font-size:15px;color:var(--ink);
  background:transparent;border:0;border-bottom:1px solid var(--edge);
  padding:14px 0;border-radius:0;
}
.pnew::placeholder{color:var(--ink3)}
.pnew:hover{border-bottom-color:var(--line)}
.pnew:focus{outline:0;border-bottom-color:var(--ink)}

.pmore{justify-content:flex-end;padding:6px 0}

/* Out of the way until you go to the row it is on — a column of crosses down
   a list of links is a list about deleting. */
/* The pair, at the end of the row: one more, or not this one. Both faint
   until you go to the row they belong to — a column of crosses down a list
   of links is a list about deleting. */
.prow .padd,.prow .pdel{
  align-self:center;flex:none;width:26px;height:26px;
  color:var(--ink3);opacity:0;
  transition:opacity .14s ease,color .14s ease;
}
.prow .padd svg{width:15px;height:15px}
.prow .pdel svg{width:12px;height:12px}
.prow:hover .padd,.prow:hover .pdel,
.prow .padd:focus-visible,.prow .pdel:focus-visible{opacity:1}
.prow .padd:hover,.prow .pdel:hover{color:var(--ink)}
/* the empty list's lone plus has no row to reveal it, so it stays */
.pmore .padd{opacity:1}
@media (hover:none){ .prow .padd,.prow .pdel{opacity:1} }
@media (prefers-reduced-motion:reduce){ .prow .padd,.prow .pdel{transition:none} }

/* THE FIRST ENTRY STARTS WHERE A COVER WOULD.

   Every block leaves 20px between its divider and the first thing under it,
   and on a block of pictures that 20 lands on the cover's edge, because a
   cover is ink from its first pixel. A line of type is not: the row's own
   8px padding, 3px of half-leading and 3.8px above the capitals stacked on
   top of the 20, so press began 34.8px down and read as a block that had
   come loose from its own heading.

   Only the first row, and only above it — the padding, the rules and the
   spacing inside an entry are all left as they were. */
.presslist > .prow:first-child .pitem{padding-top:0}
.presslist > .prow:first-child .src{display:block;text-box:trim-start cap alphabetic}

.pitem{display:block;padding:8px 0;border-bottom:1px solid var(--edge)}
/* NOT :last-child — THAT IS THE LINK, NOT THE LAST ENTRY.

   The × and the + are the pitem's siblings, so on your own profile the item
   is never last and every entry kept a rule, including the final one. Where
   those buttons are not drawn — a visitor's view of a profile, and every
   press board on a project page — the item is the only child of its row, so
   :last-child matched all of them and the list came out with no rules at
   all. It is the last row that wants no rule under it, which is a fact about
   the row and not about the link inside it.

   The same row's bottom padding goes too: the board already leaves its own
   --board-pad under the last thing in it, and eight more on top of that had
   the press list ending 28px off the edge where everything else ends at 20. */
.presslist > .prow:last-child .pitem{border-bottom:0;padding-bottom:0}
.pitem .src{font-size:13px;letter-spacing:.06em;text-transform:uppercase;color:var(--ink3)}
.pitem .hl{
  display:flex;align-items:flex-start;justify-content:space-between;gap:10px;
  font-size:15px;line-height:1.35;margin-top:3px;
}
.pitem .hl svg{flex:none;margin-top:3px;color:var(--ink3)}

/* ---------------- tab bar (phone only) ---------------- */
.tabs{
  position:fixed;left:0;right:0;bottom:0;z-index:40;
  height:calc(var(--tab-h) + env(safe-area-inset-bottom));
  padding-bottom:env(safe-area-inset-bottom);
  display:flex;align-items:center;justify-content:space-around;
  border-top:1px solid var(--edge);background:var(--bg);color:var(--ink3);
}
.tabs a,.tabs button{line-height:0;color:inherit}
.tabs .on{color:var(--ink)}
.tabs .fab{
  width:44px;height:44px;border-radius:50%;
  background:var(--ink);color:#000;
  display:flex;align-items:center;justify-content:center;
}
/* 19 in a 44px circle left 12.5px either side; 18 leaves a whole 13, so the
   plus stops starting on a half pixel — the same fix the other two took */
.tabs .fab svg{width:18px;height:18px}
@media (min-width:900px){ .tabs{display:none} }

/* ---------------- video player ---------------- */
/* Only the plate differs from the still viewer — a 16:9 frame instead of
   a picture at the project's ratio. Everything around it (.vtop, .vclose,
   .vstage, .vbot, .vmeta) is the viewer's own chrome, reused. */
.pplate{
  width:100%;max-width:var(--col);aspect-ratio:var(--ar,16 / 9);
  max-height:calc(100vh - 220px);
  background:#000;border-radius:5px;overflow:hidden;
  box-shadow:0 18px 34px rgba(0,0,0,.9),inset 0 0 0 1px var(--line);
}
.pplate{position:relative}
.pframe{width:100%;height:100%;border:0;display:block;background:#000}
/* Shown only if nothing ever answers from the player's origin. It sits
   along the bottom rather than over the picture: if the embed was merely
   slow, this must not be the thing standing in front of it. */
.pfail{
  position:absolute;left:0;right:0;bottom:0;
  display:flex;align-items:center;justify-content:center;gap:14px;
  flex-wrap:wrap;text-align:center;padding:14px 18px;
  background:linear-gradient(180deg,rgba(0,0,0,0),rgba(0,0,0,.92) 42%);
}
.pfail[hidden]{display:none}
.pfail p{margin:0;font-size:15px;color:var(--ink2)}
.pfail a{
  font-size:14px;font-weight:600;color:#000;background:var(--ink);
  padding:6px 15px;border-radius:99px;flex:none;
}

/* Shelves as a strip in the name row. Same pills Explore uses, so "pick one
   of these" looks the same wherever it appears. */
/* The shelf strip is the same control Explore uses, given the page's own air
   above it. Its rule comes from .xtabs. */
/* The row owns the space above it, so the strip inside carries none — with
   both, a tabbed row sat 34px lower than a row with a plain heading and the
   stack lost its rhythm. The strip's own job is the gap down to the work. */
/* SHELF LABELS.

   A shelf is named in the same voice wherever it is named: the caps the
   section heads carry everywhere else on the site, so a shelf on a profile
   reads as the same kind of thing as a shelf on the home page. The tab
   strip supplies the divider and the underline that says which one you are
   looking at; the type is the heading's.

   The generic .xtab keeps its sentence-case look — it is navigation
   elsewhere, not a label for a row of work. */
.shelftabs .xtab{
  /* .sechead h2's treatment, reached from the other side: the generic .xtab
     is 15 in regular with a hair of tracking, so a shelf name has to be told
     to come up to weight and drop it. Both rules land on the same label. */
  font-size:15px;font-weight:600;
  letter-spacing:normal;text-transform:none;
}
/* A row holding one shelf has a label, not a choice: no hover, no pointer,
   nothing suggesting there is a second panel behind it. The underline goes
   too — with nothing to distinguish it from, marking the live one says
   nothing. */
/* A LONE NAME IS STILL THE NAME OF SOMETHING.

   It used to lose its underline when it was the only tab, on the reasoning
   that with nothing to choose between there is nothing to mark as chosen.
   But the rule is not only a choice marker — it is what the heading is set
   on, and dropping it made Stills and Press read as looser type than Color
   Grading beside them, which is the one thing they are not. */
.shelftabs.lone .xtab:hover{color:var(--ink)}
.shelftabs{margin-top:0;margin-bottom:0}
/* THE NAME AND ITS PEN, AS ONE THING.

   The pen rides beside the label rather than in the row's control group,
   which is where a rename cannot say which of two tabs it means. Faint until
   you go near it: on your own profile every shelf carries one, and five
   bright glyphs down a page of your own work is furniture.

   The wrapper takes the underline's place in the flex row, so the tab keeps
   its own border and the pen does not sit on the rule. */
/* THE NAMES KEEP THEIR OWN DISTANCE.

   Two tabs sat 24px apart before the pen existed; giving each one a button
   pushed that to 51, and the strip read as two separate headings rather than
   a choice between two blocks. So the pen is small and the strip's gap is
   spent on it: 2px to its own name, 18px of button, 8px to the next tab. 28
   between names against the 24 they had.

   The two gaps are deliberately uneven — a pen equidistant between two names
   belongs to neither, and which name it renames has to be readable without
   hovering it. */
.shelftabs{gap:8px}
.tabwrap{display:inline-flex;align-items:center;gap:2px}
.pen{
  flex:none;display:flex;align-items:center;justify-content:center;
  width:18px;height:18px;border-radius:99px;
  margin-bottom:11px;                 /* clears the tab's own 11px underline */
  color:var(--ink3);opacity:0;
  transition:opacity .14s ease,color .14s ease;
}
.pen svg{width:12px;height:12px;display:block}
/* Out of the way until you go to the name. It keeps its box either way, so
   the strip does not shuffle when one appears. */
.tabwrap:hover .pen,
.pen:focus-visible{opacity:1}
.pen:hover{color:var(--ink)}
/* a touch screen never hovers, so there the pen has to be there to be used */
@media (hover:none){ .pen{opacity:1} }
@media (prefers-reduced-motion:reduce){ .pen{transition:none} }

/* ADD, WITH THE NAMES.

   It sat at the far right with the row controls, which put it among the
   buttons that act on the row you already have — organize it, lay it out,
   move it, delete it — when what it does is make the next name in the strip.
   So it comes back to the names and sits after the last of them, one more
   step along the same line: Color Grading, Directed Films, and the place the
   next one goes.

   No auto margin. Without one it simply follows the last tab in the flow,
   and .shelfctl keeps the auto it always had to pin the controls right —
   which is also what deletes the fight the two of them used to have over the
   strip's slack, and the calc that patched it.

   The spacing then comes out even on its own, because every name ends in a
   pen: 2px to the pen, 18px of pen, 8px of strip gap. 28px from one name to
   the next, and the same 28 from the last name to the +.

   Everything but the colour comes from .ord, which it carries; .ord is ink2
   while .shelfctl paints its own children ink3, so left alone it arrived a
   shade brighter than the buttons it used to sit in a line with. */
/* BACK WITH THE CONTROLS.

   margin-left:auto takes the strip's slack, so it leaves the names behind
   and comes to rest beside organize — the two adds together, then how the
   row lies, where it sits, and whether it stays.

   The nudges it wore beside the names are gone with it. Both were about
   setting the button against type: 4px up to clear the tabs' underline
   padding, and -10px left to cancel the padding inside its own box. Down
   here it sits among buttons of its own height and lines up with them on
   its own, and there is no gap to close because the run of controls sets
   its own. */
.tabadd{margin-left:auto;color:var(--ink3)}
/* A stroked plus with nothing around it puts less ink on the page than the
   boxed one it replaced, and it sits in a run of buttons whose glyphs carry
   10 to 12px of ink each. At 17 it was the light one in the line; 19 is what
   the section plus takes for the same reason. */
.tabadd svg{width:19px;height:19px}
.tabadd:hover,.tabadd:focus-visible{color:var(--ink)}

/* TWO AUTO MARGINS SPLIT THE SLACK BETWEEN THEM.

   .shelfctl has one of its own to pin itself right, so with the + claiming
   auto as well the free space was shared and the + came to rest halfway
   along the strip. The controls give theirs up when the + is there and
   follow it instead, 2px apart — the gap they already keep among
   themselves, so the whole right-hand end reads as one run of buttons. */
.shelftabs .tabadd + .shelfctl{margin-left:calc(2px - 8px)}



.secblock[hidden]{display:none}

/* Layout toggle, at the far end of the tab strip. Pushed left of the shelf
   controls on your own profile so the two do not stack on the same corner. */
.shelftabs{position:relative}
/* centred in the strip, with no margin lifting it — the tabs carry an 11px
   bottom padding to meet the rule, and inheriting half of that as a nudge is
   what put this icon 11px above the shelf controls beside it */
/* the layout control sits at the far end of whatever line its shelf is
   titled on — the tab strip for the projects, the heading for the stills */
.lay{margin-left:auto;align-self:center;color:var(--ink3)}
.sechead .lay{margin-left:auto}
#rowedit{margin-left:14px;align-self:center;color:var(--ink3)}
#rowedit:hover{color:var(--ink)}
.lay:hover{color:var(--ink)}

/* The list form: the same tile, unchanged — cover with its name under it —
   simply dropping down the page instead of running across it, and centred on
   the column. The tile keeps the width it has in the grid, (100% - 36px)/4,
   so a project is the same object in both layouts and only the direction
   changes. Nothing moves sideways here, so the bleed, the fade and the
   arrows go. */
/* THREE WAYS A ROW CAN LIE.

   Across is the default and needs no rules of its own — it is the strip
   every browse row on the site already is. The other two lay the shelf down
   the page. */
.pshelf.asboard .tiles,
.pshelf.asboard .stillrow,
.pshelf.asgrid .tiles,
.pshelf.asgrid .stillrow,
.pshelf.aslist .tiles,
.pshelf.aslist .stillrow,
.pshelf.asone .tiles,
.pshelf.asone .stillrow{
  overflow:visible;scroll-snap-type:none;
  /* the 10px seam belongs to the home page's two-row wall; a shelf laid out
     down the page sits straight under its own tab rule */
  margin:0;padding:0;
  -webkit-mask-image:none;mask-image:none;
}

/* A GRID IS THE ROW WITH THE SIDEWAYS TAKEN OUT.

   Everything above has already stripped the strip: no overflow, no bleed, no
   fade, no arrows. All that is left to say is that it wraps, and the covers
   are the ones the row was already showing rather than a different size for
   a different view.

   Nothing draws it. There is no panel, no rule between lines and no cell
   around a cover — the only edges on screen are the hairlines the covers
   wear everywhere else on the site, so what you see is the work in a field
   rather than the work in a table. It is a grid the way a contact sheet is a
   grid: by the pictures lining up, not by anything being ruled.

   auto-fill against the row's own card width, so the number of columns is
   whatever the column can hold and a wide window fills out instead of
   stretching three covers across it. Two on a phone, because 40vw is what a
   card is there and a minmax of that would allow a third at 320px that has
   nowhere to put its title.

   The gap is --gap in both directions. The row already sets it between
   covers; a grid only adds the second axis, and a line that is further from
   the one under it than from the cover beside it reads as rows of things
   rather than as a field of them. */
.pshelf.asgrid .tiles,
.pshelf.asgrid .stillrow{
  display:grid;
  grid-template-columns:repeat(2, 1fr);
  gap:var(--gap);
  align-items:start;
}
@media (min-width:700px){
  .pshelf.asgrid .tiles,
  .pshelf.asgrid .stillrow{
    grid-template-columns:repeat(auto-fill, minmax(calc((100% - var(--gap) * 2) / 3), 1fr));
  }
}
/* the grid hands out the width; a cover that keeps insisting on 40vw would
   run out of its own cell — the same thing .exgrid says to the cards on
   Explore */
.pshelf.asgrid .card,
.pshelf.asgrid .tile{width:auto;flex:none}
/* A STILL IS SIZED THE OTHER WAY ROUND.

   A cover is a width and a fixed 4:3. A still is a height with the frame's
   own ratio deciding its width, because a wall of stills is meant to sit on
   one baseline whatever shape each frame is. In a grid there is no shared
   baseline to keep — the cells are the alignment — so it flips: take the
   cell's width and let the ratio give the height, which is what makes a
   scope frame and a 4:3 frame line up their edges instead of their tops. */
.pshelf.asgrid .pstill{width:100%;height:auto;flex:none}

/* A BOARD IS REAL COLUMNS, NOT MULTICOL.

   Multicol starts every column on the same line but chooses for itself which
   item goes in which, by a balancing pass that differs between engines and
   with the content. That is how a cover came out a row lower on screen while
   the box model insisted every top was level.

   columnize() in the js hands the items out instead — the same routine the
   stills wall has always used — and every column is an element. A column top
   is then a column top, in every browser. */
/* the board's own figure in both directions — a hardcoded 12 was the step a
   shelf kept when it sat on the page rather than in a panel */
.pshelf.asboard .tiles,
.pshelf.asboard .stillrow{
  display:flex;align-items:flex-start;gap:var(--board-pad);
}
.pshelf.asboard .bcol{
  flex:1 1 0;min-width:0;
  display:flex;flex-direction:column;gap:var(--board-pad);
}
.pshelf.asboard .tile,
.pshelf.asboard .pstill{width:100%;margin:0;flex:none;scroll-snap-align:none}

/* THE ODD ROW AT THE FOOT, MINIMIZED AND FADED.

   The js marks the covers in a short bottom row .stub and the board .ragged.
   Here they are cut down and dissolved, so a board of four ends on three and
   a suggestion rather than on a step.

   ONE FIGURE, NOT TWO.

   How much shows is the ratio. How it goes is --more-fade, settled with the
   bio and shared with it, because "there is more of this" is one sentence
   and the site should not have two accents for it. Earlier attempts tuned
   both at once and they fought: a short slice with an early gradient left
   almost nothing at full strength, and every correction to one demanded a
   correction to the other. With the fade fixed, more or less of the cover
   is one number moving one way.

   4:0.6 against a frame that is 4:3 leaves a fifth of the cover, half of
   that at full strength — about 42px on a board at its usual width. Enough
   to see there is a picture down there. Not enough to sit and read, which
   is the distinction that matters: a fold showing a legible cover is just a
   row with a gradient on it, and that is what every taller pass turned into.
   overflow:hidden is what makes the ratio bind: without it the tile inside
   would push the box back to full height.

   The name goes under with the rest of it. A caption held out of a dissolve
   would be the one hard edge in the thing whose whole job is not to have
   one. */
.pshelf.asboard .tiles.ragged .stub{
  flex:none;width:100%;min-height:0;
  aspect-ratio:4/0.6;overflow:hidden;cursor:pointer;
  -webkit-mask-image:var(--more-fade-firm);mask-image:var(--more-fade-firm);
}
/* the empty half of the band: nothing to see, the same height, and pressable
   for the same reason the cover beside it is */
.pshelf.asboard .tiles.ragged .stubgap{background:none;pointer-events:auto}
/* opened, it is nothing: a wrapper round a tile, sized by the tile — and the
   spacer is not even that, so it goes rather than leaving a column gap under
   a row that is now full size */
.pshelf.asboard .tiles.ragged.open .stub{
  aspect-ratio:auto;overflow:visible;cursor:auto;
  -webkit-mask-image:none;mask-image:none;
}
/* OPEN, THE SPACER IS STILL THE HANDLE.

   It cannot keep the fold's height — that height was a fraction of a cover
   and the covers are full size now — and it cannot size itself from content,
   because it has none. So the columns stretch to the tallest and the spacer
   takes whatever the short one is missing. That is exactly the empty ground
   under the opened row, which is the same ground you pressed to open it, so
   the way back is where the way in was. */
.pshelf.asboard .tiles.ragged.open{align-items:stretch}
.pshelf.asboard .tiles.ragged.open .stubgap{
  aspect-ratio:auto;flex:1 1 auto;min-height:0;cursor:pointer;
}

/* One down the middle. */
.pshelf.aslist .tiles,
.pshelf.aslist .stillrow,
.pshelf.asone .tiles,
.pshelf.asone .stillrow{
  display:flex;flex-direction:column;align-items:center;
}
/* Down the middle at the width it takes on a board, rather than filling the
   column: a cover blown up to the full measure stops being one of a set. */
/* ONE COVER SIZE, ALL THREE LAYOUTS.

   A third of the column, everywhere. Across, three of them fill the strip
   and the fourth clears its edge. Spread, three flex columns share the same
   width. Down the middle, one of them, centred. A cover that changed size
   when you changed how the shelf lies would be a different cover every time
   you looked at it. */
/* DOWN THE MIDDLE IS NOT A SHELF, SO IT IS NOT A SHELF'S SIZE.

   A third of the column was the board's figure, kept here so a cover would
   be one size in all three layouts. That held while the three were variants
   of the same idea. They are not: across and spread are ways of showing a
   set, and one at a time down the page is a way of showing a piece of work.
   Two thirds is what that is worth — twice the cover, and the frame stops
   being a thumbnail with a lot of room around it.

   Two thirds and not the whole column, because the room either side is
   doing something: it is what the picture's own colour spreads into. Fill
   the column and there is nowhere for it to go. */
/* A LIST IS A PICTURE AND A NAME BESIDE IT.

   Stacked, the name sat under its cover exactly as it does across — which
   made the list a row turned on its side and nothing more. A list is worth
   having because it can put the name *beside* the work: the covers run down
   one edge and the titles down the other, and you read the column of names
   rather than hunting each one under its picture.

   The cover takes two thirds and the name takes what is left. Two thirds
   because it has to stay the largest thing on the line — a list of names
   with thumbnails is a filing cabinet, and this is still a shelf. */
/* HOW WIDE A LIST IS, IS NOT THE LIST'S BUSINESS ANY MORE.

   This used to state two thirds outright. Width is a span now — dragged, and
   the same for every layout — so a rule here would only fight it, and it
   would win: four classes against two. What a list decides is how its
   contents lie, and nothing else. */
.pshelf.aslist .tile{
  width:100%;margin:0;flex:none;
  scroll-snap-align:none;
  display:flex;align-items:center;gap:var(--board-pad);
}
/* ONE COVER SIZE, WHICHEVER WAY THE SHELF LIES.

   Switch a shelf between spread and list and the pictures must not resize —
   only their arrangement changes, which is the whole claim a view switch
   makes. It used to be 62% here, on the reasoning that the picture had to
   stay the largest thing on its line or the list became a filing cabinet.
   What actually keeps this from being a filing cabinet is that the cover is
   a cover: the same cover, at the same size, as the one you were just
   looking at.

   A THIRD OF THE LINE, WRITTEN FROM TWO THIRDS OF IT.

   That is the whole difficulty. A list sits in a wide panel — two thirds of
   the field — and "a third" here would be a third of two thirds, which is
   the cover coming out smaller for having changed view. So the figure has to
   be a third of the *line*, stated as a fraction of the panel it is in.

   Let F be the field, g the gutter, p the panel's padding. A full panel is
   F wide and its board columns are (F - 2p - 2p) / 3. A wide panel is
   (F - g) · 2/3, so its inside I is that less 2p, and solving the first for
   I gives I/2 + (g - p)/3.

   g and p are both 20, so this is 50% today and the tail is zero. It is
   written out anyway: the tail is what makes the rule true rather than
   coincidentally right, and the day the gutter and the board's padding stop
   agreeing, a bare 50% would go quietly wrong instead of following. */
.pshelf.aslist .tile .frame{
  flex:none;
  width:calc(50% + (var(--gutter) - var(--board-pad)) / 3);
}
.pshelf.aslist .tile .t{
  flex:1;min-width:0;margin-top:0;
  white-space:normal;                 /* room to wrap, off the picture now */
}
/* a still has no name to set beside it, so it simply takes the width the
   covers' pictures take */
.pshelf.aslist .pstill{
  width:100%;
  margin:0;flex:none;
  scroll-snap-align:none;
}
/* The same gap the covers keep sideways. A single file was set at 38 and the
   stills at 22 on the reasoning that a column is read rather than scanned
   and wants more air — but that made the one layout on the page whose
   spacing did not match any other, and a gap you have to think about is a
   gap that is wrong. One figure, every direction, every layout. */
/* THE BOARD'S OWN FIGURE, INSIDE AS WELL AS AROUND.

   These two said --gap, which was the covers' step back when a shelf sat
   straight on the page. Inside a panel the number that matters is the one
   the panel keeps from its border — anything tighter and the work is closer
   to the next piece of work than it is to the edge, which reads as the row
   being crowded rather than inset. */
.pshelf.aslist .tiles{gap:var(--board-pad)}
/* A STILL IS THE SHAPE IT WAS SHOT IN.

   --ar has been on every frame all along; only the two-row wall had a reason
   to ignore it. There, uniform height is what makes the seam a seam: a
   ragged bottom row put 46px of air under a 1.85 and 14px under a 4:3 in the
   same gap, and a wall with two different gaps in it is not a wall. That
   reason is a fact about two rows side by side, not about stills — so it
   stops applying the moment there is only one row.

   Down a column each frame is alone on its line, and on a board each column
   flows on its own. Nothing lines up with anything, so nothing can fail to.
   The covers beside them have kept their own ratios since the shelves were
   built; this is the stills catching up. */
.pshelf.aslist .pstill,
.pshelf.asboard .pstill,
/* ONE AT A TIME MEANS ONE SIZE.

   Down a column or across a board each frame keeps its own shape, because
   they are all on screen together and the ragged edges are what tells them
   apart. Here there is only ever one, in the same slot, and you page through
   them — so a 2.35 that is half the height of the 4:3 before it makes the
   whole board jump every time you press the arrow. One box, and the picture
   fills it. */
.pshelf.asone .pstill{aspect-ratio:16 / 9;height:auto}
/* ONE COLUMN LEAVES TWO EMPTY SIDES. FILL THEM WITH THE PICTURE.

   A cover down the middle is a third of the column wide, which leaves ~290px
   of flat grey either side of every one of them. A still laid out the same
   way has the same two empty sides, and takes the same treatment — the rule
   is about a picture stranded in the middle of a column, not about which
   kind of shelf it came off. This is the gallery's
   ambient glow turned on its side: the same picture again, behind itself,
   blurred until it is only colour and thrown outward left and right so the
   page beside the work carries the work's own light.

   Sideways only, and that is the point. --reach is how far out it carries as
   a fraction of the cover's own width, so it scales with the tile rather
   than the window and stays clear of the column edge at any size.
   Vertically it gets 20px, barely past the 12px seam — enough that the band
   has no edge of its own, not so much that a stack of covers smears into
   one another.

   The mask is two crossed ramps, and both of them ease.

   Linear ramps crossed give a rectangle, and a rectangle has corners you can
   see — the fill ran out at an angle diagonally off each corner of the
   cover. An ellipse fixes that and overcorrects: it is already tapering by
   the time it reaches the cover's edge, so the band never matches the height
   of the thing it belongs to and reads as a lens.

   Easing each ramp helps but leaves it square: a smoothstep has no visible
   edge, and the band still holds one height its whole length and then stops.

   So a third layer, and it is the one that decides the shape. Crossed ramps
   have rectangular contours; an ellipse has round ones; multiplied together
   they give the contours in between — a superellipse. The ellipse is weak on
   purpose, holding full strength until past the cover's own edge and only
   falling to .3 by the box, so the sides stay flat where it matters while
   the taper arrives along the band's length and around its corners.

   The horizontal stops are where the cover sits inside the box: at a reach
   of f the box is 1 + 2f covers wide and the cover spans f/(1+2f) to
   (1+f)/(1+2f), which is 100% minus the first. Derived rather than written
   out, because a reach changed without them goes lopsided — --reach stays
   the only number to touch. */
.pshelf.aslist .tile .frame,
.pshelf.aslist .pstill,
.pshelf.asone .tile .frame,
.pshelf.asone .pstill,
.row.col .card .frame{
  position:relative;
  /* --reach is how far out the fill carries as a fraction of the picture's
     own width, so it scales with the tile rather than the window. --ih is
     that as an inset, and --m0 is where the picture's edge falls inside the
     resulting box, which at a reach of f is f/(1+2f) of it. The gallery
     declares the same three in lengths; the rule below reads only these. */
  /* ONE PICTURE WIDE, WHICH IS EXACTLY THE ROOM THERE IS.

     A picture centred at a third of the column has a third of the column
     spare on each side — one of its own widths, near enough — so a reach of
     1 carries the colour from the picture's edge to the column's and stops
     there. The 4% on top is the 24px the three-across gap takes out of the
     picture but not out of the gap beside it: dead/pic works out to
     (col + 12) / (col - 24), which is 1.043 at a 900px column and barely
     moves at any other width.

     It was .35, which spent a third of the room and left 193px of flat grey
     outside it — the fill stopped in open space with nothing to stop at.
     Landing on the column edge is the only value here that is a fact about
     the layout rather than a taste. */
  --reach:1.04;
  --ih:calc(var(--reach) * 100%);
  --m0:calc(var(--reach) / (1 + 2 * var(--reach)) * 100%);
  --vf:26px;
}/* The reach is a fact about how much room is left, so it moves when the
   picture does. At a fraction f of the column a picture has (1 - f) / 2f of
   its own width spare on each side: one whole width at a third, a quarter of
   one at two thirds. Written out rather than left at 1.04, which was the
   third's answer and would now throw the colour four times past the column.

   The home page keeps its own — its cards are still a third across, so its
   answer is still one. */
/* The same sum, run for each of them. At a fraction f of the panel a picture
   has (1 - f) / 2f of its own width spare on either side: half of one at a
   half, a quarter of one at two thirds. A list's cover is half its panel now
   — it was 62% when the shared .25 was written — so the two no longer have
   the same answer and no longer share a line. Left at .25 a list would stop
   the colour halfway across the room it has; left at the 1.04 above, which
   is the full-width third's answer, it would throw it twice past the edge. */
.pshelf.aslist .tile .frame,
.pshelf.aslist .pstill{--reach:.5}
.pshelf.asone .tile .frame,
.pshelf.asone .pstill{--reach:.25}

.pshelf.aslist .tile .frame::before,
.pshelf.aslist .pstill::before,
.pshelf.asone .tile .frame::before,
.pshelf.asone .pstill::before,
.row.col .card .frame::before,
.stills .frame::before{
  content:"";position:absolute;z-index:-1;pointer-events:none;
  --m1:calc(100% - var(--m0));
  /* where the picture's own edge sits in the mask box, as a share of the
     ellipse's radius. The core has to end exactly there or the fill reaches
     the work already dimmed — which is what a hardcoded 55% did to a
     gallery, whose frame sits at 83%. */
  --ell:calc(100% / (1 + 2 * var(--reach)));
  inset:calc(var(--vf) * -1) calc(var(--ih) * -1);
  /* one colour out of the cover, not a blurred copy of it — see popOf() in
     app.js. Nothing until the sample lands, so a cover that fails to load
     simply has no fill rather than a grey smear. */
  background:var(--pop, transparent);
  opacity:.1;
  -webkit-mask-image:
    radial-gradient(ellipse closest-side at 50% 50%,
      #000 0,#000 var(--ell),rgba(0,0,0,.3) 100%),
    linear-gradient(90deg,
      transparent 0,
      rgba(0,0,0,.16) calc(var(--m0) * .25),
      rgba(0,0,0,.5)  calc(var(--m0) * .5),
      rgba(0,0,0,.84) calc(var(--m0) * .75),
      #000 var(--m0), #000 var(--m1),
      rgba(0,0,0,.84) calc(100% - var(--m0) * .75),
      rgba(0,0,0,.5)  calc(100% - var(--m0) * .5),
      rgba(0,0,0,.16) calc(100% - var(--m0) * .25),
      transparent 100%),
    linear-gradient(180deg,
      transparent 0,
      rgba(0,0,0,.16) calc(var(--vf) * .25),
      rgba(0,0,0,.5)  calc(var(--vf) * .5),
      rgba(0,0,0,.84) calc(var(--vf) * .75),
      #000 var(--vf), #000 calc(100% - var(--vf)),
      rgba(0,0,0,.84) calc(100% - var(--vf) * .75),
      rgba(0,0,0,.5)  calc(100% - var(--vf) * .5),
      rgba(0,0,0,.16) calc(100% - var(--vf) * .25),
      transparent 100%);
  mask-image:
    radial-gradient(ellipse closest-side at 50% 50%,
      #000 0,#000 var(--ell),rgba(0,0,0,.3) 100%),
    linear-gradient(90deg,
      transparent 0,
      rgba(0,0,0,.16) calc(var(--m0) * .25),
      rgba(0,0,0,.5)  calc(var(--m0) * .5),
      rgba(0,0,0,.84) calc(var(--m0) * .75),
      #000 var(--m0), #000 var(--m1),
      rgba(0,0,0,.84) calc(100% - var(--m0) * .75),
      rgba(0,0,0,.5)  calc(100% - var(--m0) * .5),
      rgba(0,0,0,.16) calc(100% - var(--m0) * .25),
      transparent 100%),
    linear-gradient(180deg,
      transparent 0,
      rgba(0,0,0,.16) calc(var(--vf) * .25),
      rgba(0,0,0,.5)  calc(var(--vf) * .5),
      rgba(0,0,0,.84) calc(var(--vf) * .75),
      #000 var(--vf), #000 calc(100% - var(--vf)),
      rgba(0,0,0,.84) calc(100% - var(--vf) * .75),
      rgba(0,0,0,.5)  calc(100% - var(--vf) * .5),
      rgba(0,0,0,.16) calc(100% - var(--vf) * .25),
      transparent 100%);
  -webkit-mask-composite:source-in;
  mask-composite:intersect;
}
/* No sides to fill when the card already is the column. The fill is thrown
   outward as a share of the picture's own width, so at full width it would
   simply run off both edges of the screen. */
@media (max-width:899px){ .row.col .card .frame::before{display:none} }

.pshelf.aslist .stillrow{gap:var(--board-pad)}

/* neither vertical layout has an edge to scroll toward */
.pshelf.asboard .snav,
.pshelf.aslist .snav{display:none}

/* ---------------- one at a time ---------------- */
/* THE COLUMN, CUT TO ONE FRAME.

   Every rule that lays the column out down the middle names this layout too,
   so the width, the gap, the killed bleed and the colour spreading into the
   room either side are not repeated here — they are the same rules. What is
   left is the only difference: all the cells are in the page and one of them
   is shown.

   display:none rather than visibility or a height of zero. A hidden cover
   must take no room at all, or the shelf would stand as tall as every piece
   of work in it with one visible somewhere inside. */
.pshelf.asone .onecel{
  /* THE BOARD'S WIDTH, LIKE EVERY OTHER LAYOUT.

     58% was a figure invented for this view back when a shelf sat straight
     on the page and one picture at the full column was too much. Inside a
     panel the panel is the measure: row, board and list all fill it, and one
     at a time filling less made the same shelf a different width depending
     on how it happened to lie. */
  flex:none;width:100%;
  min-width:0;display:none;
}
.pshelf.asone .onecel.on{display:block}
/* the slot owns the width; the work in it simply fills the slot, whether it
   is a cover with a name on it or a still that carries its own shape */
.pshelf.asone .onecel > *{width:100%;margin:0}
.pshelf.asone .pstill{width:100%;height:auto}

/* THE NAV SITS ON THE WORK'S TOP RIGHT CORNER.

   Same corner in every layout that moves: a strip that scrolls and one
   picture at a time both get it, tucked into the top right of whatever they
   are moving through rather than floating in the margin beside it or
   standing in a band above it.

   On the picture, so the disc comes back. A plain mark was right while these
   stood on the page's own ground; over a photograph it has to carry itself,
   and the dark blurred pill is what the gallery has always used for exactly
   this. No headroom needed either — the block goes back to the 12px every
   other one keeps under its rule. */
/* THE TOP CORNERS OF THE WORK.

   Back at the top, under the row's controls — a strip that scrolls and one
   picture at a time both get the pair tucked into the corners of whatever
   they move through. */
.pshelf .rowwrap > .snav{
  top:12px;transform:none;
  width:30px;height:30px;
}
/* THE ROW'S ARROWS GO BACK TO THE MIDDLE — OF THE STRIP.

   They were tucked into the top corners because the covers underneath were
   every shape at once, with no middle to sit on. Covers are 4:3 now, so the
   row has one picture height and the arrows can sit on it.

   Half the strip, not a figure derived from a cover's width. The derived one
   was right for covers and wrong for the stills row beside them, whose
   frames are a different height entirely — it put those arrows 25px below
   the middle of the pictures they belong to. The rowwrap holds nothing but
   the strip, so 50% of it is the middle of whatever is in it, and one rule
   covers both.

   The row layout only. One-at-a-time keeps its corner: that view is a single
   picture with the strip's chrome around it, and the arrows belong to the
   frame rather than to a run of pictures. */
@media (min-width:900px){
  .pshelf:not(.asone):not(.asboard):not(.aslist) .rowwrap > .snav{
    top:50%;transform:translateY(-50%);
  }
}
/* One at a time takes the same centring. It kept the top corner while its
   frames were every height — there was no middle to sit on that was not
   wrong for most of them — and now that every frame is one box, there is. */
.pshelf.asone .rowwrap > .snav{top:50%;transform:translateY(-50%)}
/* THE MIDDLE OF THE PICTURE, NOT THE MIDDLE OF THE CELL.

   A stills shelf shows a frame and nothing else, so half the cell is half
   the frame. A project shelf shows a cover with its name under it, and half
   of that lands 15px below the picture's own centre — enough to read as
   badly placed rather than as deliberate.

   So where the cell holds a tile, the arrows come up by half the caption:
   one line of 15px type at 1.25, plus the gap above it. :has is what lets a
   rule about the arrow ask what the cell it sits in contains. */
.pshelf.asone .rowwrap:has(.tile) > .snav{
  transform:translateY(calc(-50% - (18.75px + var(--gap)) / 2));
}
.pshelf .rowwrap > .snav svg{width:16px;height:16px}
.pshelf .rowwrap > .snav.off{opacity:0;pointer-events:none}

/* A strip scrolls both ways, so its arrows go to the ends it scrolls toward
   — back at the left edge, on at the right. Tucked together in one corner
   they were a pair of buttons; apart they are the two directions the strip
   actually has, and each sits where the work it would bring into view is
   coming from. */
.pshelf .rowwrap > .snav.l{left:12px;right:auto}
/* ON THE COVER'S OWN RIGHT EDGE.

   The offset is measured from the .rowwrap, and the rowwrap does not bleed —
   its right edge is the last cover's right edge, not the board's border. So
   a bigger number moves the arrow left, which is how a first attempt at
   "further right" took it 8px further in.

   At 0 it sat flush with where the row stops. A hair in from that: the disc
   is a round mark on a square corner, and flush against the cover's edge the
   curve read as overhanging it. 6px puts the whole of it on the picture with
   the corner still visible past it. */
.pshelf .rowwrap > .snav.r{left:auto;right:6px}

/* ONE PICTURE AT A TIME TAKES THE STRIP'S OWN PAIR.

   Identical buttons, identical corners — the only difference is what they
   sit on. A strip fills the column, so its arrows tuck into the column's
   corners; here the cell is two thirds of the column and centred, so a sixth
   of the column is the margin either side and the pair tucks into the
   picture's corners instead. */
/* BOTH IN ONE CORNER, BECAUSE THERE IS ONLY ONE PICTURE.

   A strip puts its arrows at opposite ends because that is where the work
   they bring into view is coming from — back is off to the left, on is off
   to the right, and the arrow stands where you are looking. One picture at a
   time has no such geography: nothing is off to either side, the frame is
   simply replaced. So the pair sits together in the top right, as one
   control with two directions rather than two lonely buttons flanking a
   photograph.

   6px apart: the two are one thing, and a gap wide enough to read as spacing
   would make them two again. */
.pshelf.asone .snav{display:flex}
/* THE BLOCK'S OWN EDGES, NOT THE PICTURE'S.

   One at each end, the way the strip has them — but hung off the block
   rather than the work inside it. The picture is centred and stops well
   short of both edges, so arrows measured from it sat adrift in the middle
   of the block with nothing to line up against; measured from the block, the
   right one lands under the bin and the rest of the row's controls, which is
   the line everything on that side already stands on. */
.pshelf.asone .rowwrap > .snav.l{left:0;right:auto}
.pshelf.asone .rowwrap > .snav.r{left:auto;right:0}

/* ---------------- edit sheet ---------------- */
/* The project stays on screen behind it. Same ground as the still viewer —
   a dark wash with the page blurred under it — so an overlay reads the same
   whichever one you opened. */
.sheet{
  position:fixed;inset:0;z-index:60;
  display:flex;align-items:flex-start;justify-content:center;
  background:rgba(0,0,0,.5);
  -webkit-backdrop-filter:blur(20px) brightness(.6);
  backdrop-filter:blur(20px) brightness(.6);
  overflow-y:auto;
  padding:48px 20px calc(48px + env(safe-area-inset-bottom));
}
/* The form keeps the column's width rather than the window's: it is the same
   measure the project reads at, so the fields do not sprawl on a wide screen. */
.sheetbox{
  width:100%;max-width:var(--col);
  background:var(--surface);border-radius:14px;
  box-shadow:0 30px 80px rgba(0,0,0,.7),inset 0 0 0 1px var(--edge);
  /* named, because the editor's heading hands exactly this back to reach
     the card's edge — the same trick .formtitle plays with the page's own
     margin */
  --sheet-pad:26px;
  padding:24px var(--sheet-pad) 30px;
}
.sheethead{
  display:flex;align-items:center;justify-content:space-between;gap:18px;
  margin-bottom:22px;
}
/* with nothing but the close in it, space-between has nothing to space —
   the button falls to the left where the title used to start */
.sheethead.bare{justify-content:flex-end;margin-bottom:6px}
/* trimmed for the reason .fieldhead's label is: the close beside it is a
   34px disc, and a title centred by its line box hangs the disc low against
   the capitals it is supposed to sit level with */
.sheethead .ptitle{margin:0;font-size:23px;text-box:trim-both cap alphabetic}
/* The mark alone. .ord tints a disc behind itself on hover, which is right
   for a row of glyphs you are picking between — you need to see which one
   you are on. A close in the corner of a sheet is the only thing there; it
   has nothing to be told apart from, so the disc was a shape appearing under
   the pointer for no reason. */
/* EVERY × HOVERS THE SAME WAY: BRIGHTER, NOT ROUNDER.

   .ord lights a pill behind whatever it is on, which is right for the icons
   that are only a glyph — a trash, a grip, a plus need the pill to say they
   are pressable. A cross does not; it is the one mark in the set that is
   already a shape, and putting a circle round it on hover drew a second
   button around the first.

   All of them, so a close in a sheet, an × beside a role and an × beside a
   link behave alike wherever you meet them. */
.sheetx,.rolex,.pdel,.tabx,.padd,.crole-add,.xdel{flex:none;color:var(--ink2)}
.sheetx:hover:not(:disabled),
.rolex:hover:not(:disabled),
.pdel:hover:not(:disabled),
.tabx:hover:not(:disabled),
/* AND WHATEVER STANDS NEXT TO ONE.

   The × was exempt from .ord's hover pill and the + beside it was not, so
   going along the row lit a circle on one of a pair and nothing on the
   other — which is the circle next to the ×. They are one control in two
   directions: add a row, or take this one away. Neither gets a shape. */
.padd:hover:not(:disabled),
/* the upload form removes with a cross rather than a bin, so it joins the
   crosses: brighter, not rounder */
.xdel:hover:not(:disabled){color:var(--ink);background:none}
.crole-add:hover:not(:disabled){color:var(--ink);background:none}
@media (max-width:899px){
  /* full width on a phone: a card inset on both sides wastes the only room
     a form has, and the sheet is the whole screen there anyway */
  .sheet{padding:0}
  .sheetbox{max-width:none;border-radius:0;min-height:100%}
}

/* A framing window in a sheet. The box is the size of the decision, not of a
   form field — a face judged in a 180pt tile is a face you have not really
   looked at. */
.cropsheet{max-width:420px}
.cropsheet .cropbox{width:100%;margin:0 auto}
.cropsheet .cropbar{max-width:none}
.cropsheet .publishrow{margin-top:22px}
/* The mask only. The window's shape comes from the caller — a profile
   picture is a 3:4 rectangle and the sheet frames it as one, so a face is
   judged in the shape it will be shown in rather than in a square that gets
   cut down afterwards. The thirds grid stays rectangular under any mask: it
   is guidance about the frame, not about the shape. */
.cropbox.round{border-radius:50%;overflow:hidden}

/* ---------------- the picture, full size ---------------- */
/* Centred rather than dropped from the top like the sheets, because there is
   no form to fill in and nothing below the fold — the picture is the whole
   contents. It takes the height it can get and keeps its shape; on a wide
   window that is the limit, on a short one the height is. */
.avazoom{align-items:center;padding:40px 20px;cursor:zoom-out}
.avazoom figure{margin:0;display:block}
.avabig{
  display:block;
  height:min(72vh, 620px);
  aspect-ratio:138 / 175;
  border-radius:8px;background-color:var(--plate);
  background-size:cover;background-position:center;background-repeat:no-repeat;
  box-shadow:0 40px 90px rgba(0,0,0,.75);
}
/* the row's picture is a button now, so it says so on the way in */
.idrow .avatar{cursor:zoom-in;padding:0;border:0}

/* ---------------- choosing stills ---------------- */
/* The way onto the wall, at the end of it. A frame-shaped outline rather
   than a filled tile: it is the shape of what would go there, and it never
   competes with the pictures beside it. */

/* Wider than a form: this is a wall of pictures being judged, and a picture
   you cannot see is a picture you cannot choose. */
.pickstills{max-width:640px}
/* the wall opens straight under the close row, on the sheet's own 20px step */
.pickstills .framewall{margin-top:20px}
/* The wall is the profile's wall: same masonry, same gap, same true shapes.
   The one thing it adds is state, and the frame carries that itself. */
.framewall .stillcol{gap:10px}

.pickframe{
  position:relative;display:block;width:100%;
  border-radius:8px;overflow:hidden;
}
.pickframe .pf{
  display:block;width:100%;aspect-ratio:var(--ar, 16 / 9);
  background-size:cover;background-position:center;background-repeat:no-repeat;
  background-color:var(--plate);
}
/* Off is dimmed rather than outlined. A row of frames each in its own box
   reads as boxes; dimming leaves the pictures as the only shapes, and the
   ones that are on are simply the bright ones. */
.pickframe:not(.on) .pf{opacity:.34}
.pickframe:hover:not(.on) .pf{opacity:.6}
.pickframe.on{box-shadow:0 0 0 2px var(--ink)}
.pfmark{
  position:absolute;right:7px;bottom:7px;
  width:21px;height:21px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  background:var(--ink);color:#000;opacity:0;
}
.pfmark svg{width:12px;height:12px}
.pickframe.on .pfmark{opacity:1}


/* ---------------- arrange sheet ---------------- */
/* The page's own structure, small enough to see all at once. Every row on
   the page is a line here, and a shelf is a chip on that line — the same
   join and separate the page does by dragging, at a size where the whole
   arrangement is one glance rather than a scroll. */
.laysheet{max-width:520px}
.laynote{
  margin:-8px 0 24px;font-size:15px;line-height:1.55;color:var(--ink3);
}

.layrows{display:flex;flex-direction:column;gap:8px}
.layrow{
  display:flex;align-items:center;gap:8px;
  padding:8px 10px;border-radius:10px;
  box-shadow:inset 0 0 0 1px var(--line);
  background:var(--plate);
}
.layrow.dragging{opacity:.4}
.layrow.over{box-shadow:inset 0 0 0 1px var(--ink)}
/* a row that a chip is about to land on: the same inner stroke the page's
   shelves take, so the gesture answers the same way in both places */
.layrow.joining{box-shadow:inset 0 0 0 2px var(--ink)}
.layrow .grip{width:26px;height:26px;cursor:grab}
.layrow .grip svg{width:16px;height:16px}

/* WHAT KIND OF BLOCK THIS IS.

   A shelf holds work you filed. Stills is gathered for you. Press is links
   and nothing is ever filed into it. Three different things that all sit in
   a row, so the chip carries a glyph rather than relying on the name — the
   difference has to survive a shelf being called "Press Kit". A shelf reads
   at full strength because it is the one you act on; the other two are
   muted, which is also the truth about how much of them is yours to change. */
.ckind{
  flex:none;display:flex;align-items:center;
  color:var(--ink3);margin-right:2px;
}
.ckind svg{width:14px;height:14px;display:block}
.laychip.k-shelf .ckind{color:var(--ink2)}
.laychip.k-frames,.laychip.k-links{box-shadow:inset 0 0 0 1px var(--edge)}
.laychip.k-frames .cname,.laychip.k-links .cname{color:var(--ink2)}

/* ---- a shelf, opened ------------------------------------------------- */
/* Closed, it is a line: what kind, what it is called, how much is in it. The
   count is the point of the closed state — "3 projects" is most of what you
   came to check, and reading it should not cost a click. */
.shelfed{border-bottom:1px solid var(--edge)}
.shelfed:first-of-type{border-top:1px solid var(--edge)}
.shelfsum{
  display:flex;align-items:center;gap:10px;width:100%;
  padding:14px 0;text-align:left;color:var(--ink);
}
.shelfsum .sname{font-size:15px;font-weight:600;letter-spacing:-.005em;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.shelfsum .scount{
  margin-left:auto;flex:none;
  font-size:13px;color:var(--ink3);
}
.shelfsum svg{flex:none;width:15px;height:15px;color:var(--ink3);transition:transform .18s ease}
.shelfed.open .shelfsum svg{transform:rotate(180deg);color:var(--ink)}
.shelfsum:hover .scount,.shelfsum:hover svg{color:var(--ink2)}

.shelfbody{display:flex;flex-direction:column;gap:12px;padding:2px 0 20px}
.shelfname{
  font:inherit;font-size:15px;font-weight:600;color:var(--ink);width:100%;
  background:transparent;border:0;border-bottom:1px solid var(--line);
  padding:6px 0;border-radius:0;
}
.shelfname:focus{outline:0;border-bottom-color:var(--ink)}

/* A FILED PROJECT SITS ON A PRESS LINK'S ROW.
   Grip, what it is, and the way out — the same three a link row has, so a
   list of work and a list of links read as the same kind of list. The name
   takes the slack, which is what puts the bin at the right-hand edge. */
.rname{
  flex:1;min-width:0;font-size:15px;color:var(--ink);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.rname.dim{color:var(--ink2)}
.rmeta{flex:none;font-size:13px;color:var(--ink3);text-transform:capitalize}
/* the picker is the row, not a field in it — a select capped at 44% would
   leave half the row empty beside a list of film titles */
.addpick{flex:1;max-width:none}

/* The add is a row, not a button floating beside a heading: it is the next
   line of the list, which is where you are looking when you want one more. */

.addrow svg{width:15px;height:15px;flex:none}
.addrow:hover{color:var(--ink)}

.fieldhead.editshelves{margin-top:26px}

/* ---------------- adding a shelf, and filling it ---------------- */
/* The + sits with the other things you do to your own profile, and opens the
   same way the account menu does — the list hangs under the button on focus,
   with no state anywhere to get out of step. */
/* .ord supplies the box — 34px round, ink2, no outline — so the + is the
   same button the shelf rows carry. Only the glyph is set here: plus is
   drawn lighter than the rest of the set and needs the extra 4px to sit at
   the same visual weight as a grip or a bin. */
.addpill svg{width:19px;height:19px}
.addpill:hover{color:var(--ink)}
.addshelf:focus-within .addpill{color:var(--ink)}
.addshelf .menubody{left:auto;right:0}
.menucard.wide{min-width:280px}
/* a row in either menu: glyph, what it is, and what that kind holds */
.menucard button{
  display:flex;align-items:flex-start;gap:10px;
  padding:9px 10px;border-radius:8px;text-align:left;color:var(--ink2);
}
.menucard button:hover{color:var(--ink);background:rgba(var(--silk),.07)}
.menucard button > svg{width:15px;height:15px;flex:none;margin-top:2px;color:var(--ink3)}
.menucard button:hover > svg{color:var(--ink2)}
.mtext{display:flex;flex-direction:column;gap:2px;min-width:0}
.mtext b{font-size:15px;font-weight:600;color:var(--ink);letter-spacing:-.005em}
.mtext em{font-style:normal;font-size:13px;color:var(--ink3);line-height:1.35}
/* a kind you cannot have yet: the same row, without the click */
.mrow.off{
  display:flex;align-items:flex-start;gap:10px;
  padding:9px 10px;color:var(--ink3);
}
.mrow.off > svg{width:15px;height:15px;flex:none;margin-top:2px;opacity:.6}
.mrow.off .mtext b{color:var(--ink3);font-weight:400}
.mhead{
  display:block;padding:7px 10px;
  font-size:13px;color:var(--ink3);
}

/* ---------------- what a control is called ---------------- */
/* A GLYPH THAT WILL NOT SAY WHAT IT DOES.

   Five round buttons on a shelf and every one of them a picture. The labels
   are there for a screen reader and have been all along; this is the same
   answer given to the eye — on hover, small, above the button, gone when you
   move off.

   Read from data-tip rather than aria-label, because they are not the same
   sentence. A screen reader wants "Delete Color Grading and Directed Films";
   a pointer wants "delete". Long enough to be unambiguous is the wrong
   length when the thing you are hovering is right there under it.

   Focus shows it too, so tabbing through a row of glyphs tells you what you
   are on rather than making you press to find out. */
[data-tip]{position:relative}
[data-tip]::after{
  content:attr(data-tip);
  position:absolute;left:50%;bottom:calc(100% + 7px);
  transform:translateX(-50%) translateY(3px);
  padding:5px 9px;border-radius:6px;
  background:var(--surface);color:var(--ink);
  box-shadow:inset 0 0 0 1px var(--line),0 10px 24px -10px rgba(0,0,0,.95);
  font-size:12px;line-height:1.2;letter-spacing:.01em;
  white-space:nowrap;pointer-events:none;
  opacity:0;transition:opacity .13s ease,transform .13s ease;
  z-index:30;
}
[data-tip]:hover::after,
[data-tip]:focus-visible::after{opacity:1;transform:translateX(-50%) translateY(0)}
/* the last control on a row would push its tip off the column */
.idctl [data-tip]:last-child::after,
.shelfctl > *:last-child[data-tip]::after{left:auto;right:0;transform:translateY(3px)}
.idctl [data-tip]:last-child:hover::after,
.idctl [data-tip]:last-child:focus-visible::after,
.shelfctl > *:last-child[data-tip]:hover::after{transform:translateY(0)}
/* once the list is open the label is redundant, and it sat on top of it */
.rowadd:focus-within [data-tip]::after{opacity:0}
@media (prefers-reduced-motion:reduce){ [data-tip]::after{transition:none} }
/* a touch screen has no hover, and a tip that appears on tap is a tip that
   covers the thing you just tapped */
@media (hover:none){ [data-tip]::after{display:none} }

/* ---------------- the shelves sheet ---------------- */
/* WIDE, BECAUSE A SHELF OPENS INTO A LIBRARY.

   A dialog you are picking pictures in is not the same object as one you are
   answering a question in, and this is both — a list of shelves that any row
   of can unfold into every film you have made. */
.shelfsheet{max-width:min(1000px, calc(100vw - 48px))}
.shelfsheet .laynote{margin:-4px 0 22px}

.shelflist{display:flex;flex-direction:column;gap:8px}
.shelfed{
  border-radius:10px;background:var(--plate);
  box-shadow:inset 0 0 0 1px var(--line);
}
.shelfed.dragging{opacity:.4}
.shelfed.over{box-shadow:inset 0 0 0 1px var(--ink)}
.shelfed[draggable="true"] .grip{cursor:grab}

/* One line per shelf, and everything about it on that line: move it, what
   kind it is, its name, how much is in it, how it lies, open it, bin it. */
.shelfsum{display:flex;align-items:center;gap:10px;padding:10px 12px}
.shelfsum .ckind{flex:none;color:var(--ink3)}
.sname{
  flex:1 1 auto;min-width:0;
  font:inherit;font-size:15px;font-weight:600;letter-spacing:-.005em;color:var(--ink);
  background:transparent;border:0;border-bottom:1px solid transparent;
  padding:3px 0;border-radius:0;
}
.sname:hover{border-bottom-color:var(--line)}
.sname:focus{outline:0;border-bottom-color:var(--ink)}
.sname.fixed{color:var(--ink2);flex:1 1 auto;border:0}
.scount{flex:none;font-size:13px;color:var(--ink3);white-space:nowrap}
.shelfsum .laypick{flex:none}
.shelfsum .ord{width:28px;height:28px}
.shelfsum .ord svg{width:14px;height:14px}
.shelfsum .sopen svg{transition:transform .18s ease}
.shelfed.open .sopen svg{transform:rotate(180deg);color:var(--ink)}

.shelfbody{
  display:flex;flex-direction:column;gap:12px;
  padding:4px 12px 16px;
  border-top:1px solid var(--edge);margin-top:2px;
}
.shelfbody .libpick{grid-template-columns:repeat(4,1fr);gap:14px 12px}
@media (max-width:820px){ .shelfbody .libpick{grid-template-columns:repeat(2,1fr)} }
.shelfbody .rrow{gap:10px}

/* the kinds you can add, as a plain list rather than something that hangs */
.addhead{margin-top:26px}
.menucard.flat{
  background:transparent;box-shadow:none;padding:0;
  display:flex;flex-direction:column;gap:2px;min-width:0;
}
.menucard.flat button{padding:10px 12px;border-radius:10px}
.menucard.flat button:hover{background:var(--plate)}

@media (prefers-reduced-motion:reduce){ .shelfsum .sopen svg{transition:none} }

/* ---------------- display, on the edit page ---------------- */
/* THREE ANSWERS, IN THE FORM'S OWN SHAPE.

   A .field with a heading and rows under it, like Press above it and Roles
   above that — the row is the target, full width, so the tick is somewhere
   to look rather than somewhere to hit.

   The tick is drawn on the right and empty until it is on. Drawing an empty
   box on every line makes a column of boxes you have to read past; an empty
   space and a mark says the same thing with one shape instead of two. */
.drow{
  display:flex;align-items:center;gap:12px;width:100%;
  padding:12px 14px;border-radius:10px;text-align:left;
  color:var(--ink2);
  box-shadow:inset 0 0 0 1px var(--line);
  transition:color .14s ease,box-shadow .14s ease;
}
.drow:hover:not(:disabled){color:var(--ink);box-shadow:inset 0 0 0 1px var(--ink2)}
.drow.on{color:var(--ink)}
.drow:disabled{opacity:.45;cursor:default}
/* the name and what it puts on the page, stacked — one thing said twice at
   two weights, not two items in the row's flex */
.dtext{display:flex;flex-direction:column;gap:2px;flex:1 1 auto;min-width:0}
.dname{font-size:15px}
.drow.on .dname{font-weight:600}
.dwhat{font-size:13px;line-height:1.35;color:var(--ink3)}
.drow:disabled .dwhat{font-style:italic}
.dtick{
  flex:none;width:22px;height:22px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  background:var(--ink);color:#000;opacity:0;
  transition:opacity .14s ease;
}
.dtick svg{width:12px;height:12px}
.drow.on .dtick{opacity:1}
.drow:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
@media (prefers-reduced-motion:reduce){ .drow,.dtick{transition:none} }

/* ---------------- the library ---------------- */
/* EVERYTHING YOU HAVE MADE, AT THE SIZE YOU MADE IT.

   Choosing which films belong on a shelf is a decision about the films, and
   you cannot make it from a list of their names. So the covers come up at
   their own ratios across a spread wide enough to hold three of them — the
   same picture, the same shape, as the shelf you are filling.

   Wider than any other sheet on purpose. A dialog you are choosing pictures
   in is not the same object as a dialog you are answering a question in. */
/* a list of lines, not a wall of covers — the spread's width was for three
   across and would leave a list marooned in the middle of it */
.picklib{max-width:min(560px, calc(100vw - 40px))}
/* ONE STEP THROUGH THE WHOLE SHEET.

   Every break between top-level things here is the same 20px: the tabs to
   the first cover, a rule to the list on either side of it, the last list to
   the done button. The sheet was mixing 0, 14, 20 and 28 and it read as
   drift rather than as structure — when the gaps all differ, none of them
   means anything.

   The only tighter gap is a label to the list it names, which is the one
   pairing in here that is genuinely one object. */
.libtabs{margin-top:20px;margin-bottom:20px}
/* with one shelf there are no tabs, and the first list would otherwise sit
   6px under the close button — the same step holds without them */
.picklib .sheethead + .libpick{margin-top:20px}

/* A LIST, NOT A SPREAD.

   Three big covers across read as a gallery — something to look at. This is
   a list you run down ticking things, so it is one line per film: a small
   cover, its name beside it, and the tick at the far right where a list
   keeps its state.

   The cover is a fixed width and its own height. Fixing the width is what
   lines the names up down the page, and it is the names you are reading;
   the shapes are still true, so a 2.35 is a letterbox and a 4:3 is a block,
   and the rows come out different heights on purpose. */
.libpick{display:flex;flex-direction:column;gap:2px}
/* the handle sits outside the row's own button — a button inside a button is
   not markup — and its column is held open even where there is nothing to
   drag, so the covers line up down the whole list */
.pickrow{display:flex;align-items:center;gap:4px}
.pickrow .grip{flex:none;width:26px;height:26px;color:var(--ink3);cursor:grab}
.pickrow .grip svg{width:15px;height:15px}
.pickrow:not([draggable]) .grip{visibility:hidden;cursor:default}
.pickrow.dragging{opacity:.4}
.pickrow.over{box-shadow:0 -2px 0 var(--ink)}
/* a label belongs to the list under it, so it sits tight to it and takes its
   distance from the rule above instead */
.libsplit{margin:0 0 var(--s-tight)}
/* The rule between groups. It sits in the covers' own column rather than
   running the full sheet, so it reads as a break in the list and not as the
   sheet being cut in half. */
.libline{border:0;border-top:1px solid var(--edge);margin:20px 0}
.picklib .publishrow, .pickstills .publishrow{margin-top:20px}

.pickwork{
  display:flex;flex-direction:row;align-items:center;gap:14px;
  width:100%;min-width:0;padding:7px 10px;border-radius:10px;
  text-align:left;color:var(--ink);
  transition:background .12s ease;
}
.pickwork:hover{background:var(--plate)}
.pickwork .pw{
  flex:none;display:block;width:84px;
  aspect-ratio:4 / 3;border-radius:5px;
  background-color:var(--plate);
  background-size:cover;background-position:center;background-repeat:no-repeat;
  box-shadow:inset 0 0 0 1px var(--edge);
  transition:box-shadow .14s ease;
}
.pickwork.on .pw{box-shadow:0 0 0 2px var(--ink)}
.pickwork:focus-visible .pw{box-shadow:0 0 0 2px var(--ink)}

.pwtext{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1 1 auto}
.pwt{
  display:block;font-size:15px;font-weight:600;letter-spacing:-.005em;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.pwm{display:block;font-size:13px;color:var(--ink3);text-transform:capitalize}

/* At the end of the row, and empty until it is on — an unticked box drawn on
   every line is a column of boxes competing with the pictures. */
.pwmark{
  flex:none;margin-left:auto;
  width:22px;height:22px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  background:var(--ink);color:#000;opacity:0;
  transition:opacity .14s ease;
}
.pwmark svg{width:12px;height:12px}
.pickwork.on .pwmark{opacity:1}
@media (prefers-reduced-motion:reduce){
  .pickwork,.pickwork .pw,.pwmark{transition:none}
}
@media (prefers-reduced-motion:reduce){
  .pickwork .pw,.pwmark{transition:none}
}

/* Renaming happens on the tab, so the field has to be the tab: same size,
   same place, nothing around it but the line it already sits on. */
.tabedit{
  font:inherit;font-size:15px;color:var(--ink);width:14ch;
  background:transparent;border:0;border-bottom:1px solid var(--ink);
  padding:0 1px 10px;border-radius:0;
}
.tabedit:focus{outline:0}

.laychips{display:flex;flex-wrap:wrap;gap:6px;min-width:0}
/* Even on both sides. The padding used to be 12 left and 4 right, cut for a
   chip that ends in a round × — the button's own box supplies the rest of
   the gap. A chip with nothing on its right, which is every built-in one in
   the tray, then had its name jammed 4px from the edge against 12px of air
   on the other side. So the chip is symmetrical and the × pulls itself back
   into the padding instead. */
.laychip{
  display:inline-flex;align-items:center;gap:2px;
  padding:4px 13px;border-radius:99px;
  box-shadow:inset 0 0 0 1px var(--line);
}
.laychip[draggable="true"]{cursor:grab}

.laychip .cname{font-size:15px;color:var(--ink);white-space:nowrap}
/* not yours to name — the block is what it is, and the cursor says so
   before the click does nothing */
.laychip .cname.fixed{color:var(--ink2);cursor:default}
/* Renaming happens on the chip itself, so the field has to be the label:
   same size, same place, no box of its own beyond the line under it. */
.laychip .cedit{
  font:inherit;font-size:15px;color:var(--ink);width:11ch;
  background:transparent;border:0;border-bottom:1px solid var(--ink);
  padding:0 0 1px;border-radius:0;
}
.laychip .cedit:focus{outline:0}


/* Where a chip goes to become a row of its own. Nothing while nothing is
   moving — a target you cannot use is only clutter. */
.laygap{height:0;border-radius:8px;transition:height .12s ease}
.sheet.dragchip .laygap{
  height:30px;box-shadow:inset 0 0 0 1px var(--edge);
}
.sheet.dragchip .laygap.over{box-shadow:inset 0 0 0 2px var(--ink)}

/* Unsaved profile edits. Sits where the toast sits — the bottom of the
   window is where this app says things — but stays until it is answered,
   because it is a question and not a notice. */
.pendbar{
  position:fixed;left:50%;transform:translateX(-50%);z-index:70;
  bottom:calc(var(--tab-h) + 22px + env(safe-area-inset-bottom));
  display:flex;align-items:center;gap:16px;
  padding:10px 12px 10px 20px;border-radius:99px;
  background:var(--surface);
  box-shadow:0 14px 34px rgba(0,0,0,.6),inset 0 0 0 1px var(--line);
}
@media (min-width:900px){ .pendbar{bottom:26px} }
.pendmsg{font-size:15px;color:var(--ink2);white-space:nowrap}
.pendbar .watch{font-size:15px;padding:8px 18px}
.pendbar .hint{font-size:15px}
.pendbar .hint:hover{color:var(--ink)}

/* The layout sheet's own shelf rows reuse the project editor's row shape —
   grip, field, trash — so a list you can rearrange looks the same wherever
   this app offers one. */
.sheetbox .rows .crow{padding:2px 0}
.sheetbox .namein{
  flex:1;min-width:0;font:inherit;font-size:16px;color:var(--ink);
  background:transparent;border:0;border-bottom:1px solid var(--edge);
  padding:6px 2px;
}
.sheetbox .namein:focus{outline:0;border-bottom-color:var(--ink)}

/* Stills and Press are shelves of their own, so they take the same air above
   them that separates any block on this page from the one before it. */
.stillsec,.presssec{margin-top:44px}

/* Page layout sheet: shelves, with the projects shelf's sections indented
   under it. The indent is the whole hierarchy — a section belongs to the
   shelf above it, and nothing else on the list is nested. */
.shelfrow .shelfname{flex:1;min-width:0;font-size:16px}
.shelfrow .lay{margin-left:0;color:var(--ink3)}
.shelfrow .lay:hover{color:var(--ink)}
.subrows{margin:6px 0 14px 30px;padding-left:14px;border-left:1px solid var(--edge)}
.subrows .crow{padding:2px 0}
.subrows .hint{margin:4px 0}

/* Shelf controls, together at the end of the line each shelf is titled on.
   They were spread across it before — the add sat against the tabs on the
   left while the rest hung off the right edge — so the eye had to find them
   twice. One group, evenly spaced, reading in the order you would use them:
   add a section, how it lies, move it, take it off. */
.shelfctl{
  margin-left:auto;flex:none;
  /* Two, not six. These are one set of buttons doing one job — a gap wide
     enough to read as spacing between them made three separate marks out of
     a group; at two the glyphs are what you see and the boxes stop counting. */
  display:flex;align-items:center;gap:2px;
}
.shelfctl .ord{color:var(--ink3)}
.shelfctl .ord:hover{color:var(--ink)}
.shelfctl .grip{cursor:grab}
/* the plus is drawn lighter than the rest of the set, so it takes the same
   19 the others do rather than the 13 it carries in a form */
.shelfctl #sec-add svg{width:19px;height:19px}
.pshelf.dragging{opacity:.45}
.pshelf.over{box-shadow:0 -2px 0 var(--ink)}
.xtab.dragging{opacity:.45}
.xtab.over{box-shadow:-2px 0 0 var(--ink)}
/* putting a shelf back */

/* A row holds one shelf under a heading, or several as tabs. */
/* The 12 the covers keep between themselves. One gap on the page, used
   sideways and down: between two covers, above a shelf's label, under its
   rule, and between one shelf and the next. Nothing has a spacing of its
   own to argue with. */
.pshelf{margin-top:var(--gap)}
.pshelf:first-child{margin-top:var(--gap)}
/* the same gap under a tab strip as under a heading, so a row reads the
   same whether it holds one shelf or several */
/* THE BAND THE NAMES SIT IN, AS ONE NUMBER.

   The rule under the tabs had 12px above it and 12 below, which was luck
   rather than intent: above came from the tab's own bottom padding, tuned
   back when this strip was a page heading, and below came from the row
   body's top margin. Two unrelated figures that happened to agree, and would
   have stopped agreeing the moment either was touched.

   --tabband is both of them now, and a little more of it — at 12 the names
   were pressed between the board's edge and the work. The tab carries one
   pixel less because the rule itself is that pixel. */
/* the rule sits the same distance from the baseline above it as from the
   work below it — one band, measured from the letters on one side and the
   pictures on the other */
.pshelf{--tabband:var(--board-pad)}
.pshelf .shelftabs .xtab{padding-bottom:calc(var(--tabband) - 1px)}
.pshelf .shelftabs + .rowbody{margin-top:var(--tabband)}


/* Dragging a shelf between rows. The landing strips have no size until a
   drag is under way — a page full of empty targets is a page with holes in
   it — and the row you would join lights its own edge. */
.rowgap{height:0}
.dragshelf .rowgap{
  height:26px;margin:6px 0;border-radius:6px;
  box-shadow:inset 0 0 0 1px var(--edge);
}
.dragshelf .rowgap.over{box-shadow:inset 0 0 0 2px var(--ink)}
.pshelf.joining{box-shadow:inset 0 0 0 2px var(--ink);border-radius:var(--board-r)}
/* THE ATTRIBUTE IS ON THE WRAPPER, NOT THE NAME.

   This said .xtab[draggable] and the tab has never carried that attribute —
   the span around it does, because the pen beside the name has to travel
   with it. So the rule matched nothing: a strip of names you could reorder
   the whole time, with no cursor to say so and nothing happening on screen
   while you did it. The drag worked; only the telling was missing. */
.tabwrap[draggable="true"],.sechead h2[draggable="true"]{cursor:grab}
.tabwrap[draggable="true"]:active{cursor:grabbing}
.tabwrap.dragging{opacity:.4}
/* a line up the leading edge, not across the top. The row marker is a
   horizontal rule because rows stack; a strip runs sideways, so the mark
   that says "it lands here" has to stand the other way. */
.tabwrap.over{box-shadow:-2px 0 0 var(--ink)}

/* ---------------- still viewer ---------------- */
.viewer,.player{
  position:fixed;inset:0;z-index:60;display:none;
  background:rgba(0,0,0,.4);
  -webkit-backdrop-filter:blur(34px) brightness(.55);
  backdrop-filter:blur(34px) brightness(.55);
}
.viewer.on,.player.on{display:flex;flex-direction:column}
.vtop{
  display:flex;align-items:center;
  padding:calc(18px + env(safe-area-inset-top)) 20px 0;
}
/* NO DISC BEHIND AN ×.

   A cross is already a closed shape and reads as a button on its own; the
   filled circle behind it was a second button drawn around the first. It
   was also the only one of these in the app wearing its ring all the time
   rather than on hover, which made the viewer's close a different object
   from every other close.

   The viewer sits over a photograph, so the mark still has to survive a
   bright frame under it — that is what the shadow is for, and it does the
   job the disc was doing without drawing a shape of its own. */
.vclose{
  width:30px;height:30px;
  display:flex;align-items:center;justify-content:center;
  filter:drop-shadow(0 1px 3px rgba(0,0,0,.85));
}
.vcount{
  margin:0 auto;transform:translateX(-15px);
  font-size:15px;font-variant-numeric:tabular-nums;
}
.vstage{flex:1;display:flex;align-items:center;justify-content:center;padding:0 20px;position:relative}
.vplate{
  width:100%;max-width:var(--col);
  aspect-ratio:var(--ar,16 / 9);
  background-size:contain;background-position:center;background-repeat:no-repeat;
  background-color:transparent;
  filter:drop-shadow(0 18px 34px rgba(0,0,0,.9));
}
.chev{
  position:absolute;top:50%;transform:translateY(-50%);
  padding:12px;line-height:0;color:rgba(var(--silk),.55);
}
.chev:hover{color:var(--ink)}
.chev.l{left:2px} .chev.r{right:2px}
@media (max-width:899px){ .chev{display:none} }   /* swipe instead */
.vdots{display:flex;gap:6px;justify-content:center;padding:0 0 4px}
.vdots i{width:5px;height:5px;border-radius:50%;background:var(--line)}
.vdots i.on{background:var(--ink)}
.vbot{
  display:flex;align-items:flex-end;justify-content:space-between;gap:16px;
  width:100%;max-width:var(--col);margin:0 auto;
  padding:22px 20px calc(40px + env(safe-area-inset-bottom));
}
/* bottom left is the way back into the project the frame belongs to */
.vmeta{display:flex;align-items:center;gap:11px;min-width:0}
.vav{flex:none;line-height:0}
.vmeta .t:hover{text-decoration:underline;text-underline-offset:3px}
.vmeta .c:hover{color:var(--ink);text-decoration:underline;text-underline-offset:3px}
.vmeta .av{width:36px;height:36px;border-radius:50%;flex:none;background-color:var(--plate)}
.vwho{min-width:0}
.vbot .t{display:block;font-size:18px;font-weight:600}
.vbot .c{display:block;font-size:15px;color:var(--ink2);margin-top:3px}
/* the still's own actions, only reachable once you have opened it */
.vacts{display:flex;align-items:center;gap:18px;flex:none}
.vdl,.vshare{line-height:0;color:rgba(var(--silk),.7);transition:color .18s ease}
.vdl:hover,.vshare:hover{color:var(--ink)}
.vshare svg{width:23px;height:23px;display:block}
.vdl[hidden]{display:none}
.vdl svg{width:23px;height:23px;display:block}

/* quiet save — no count beside it, ever */
.vsave{flex:none;line-height:0;color:rgba(var(--silk),.7);transition:color .18s ease}
.vsave:hover{color:var(--ink)}
.vsave.saved{color:var(--ink)}
.vsave.saved svg path{fill:currentColor}

.empty{padding:60px 0;text-align:center;color:var(--ink2);font-size:16px}

@media (prefers-reduced-motion: reduce){
  *{transition-duration:.01ms !important;animation-duration:.01ms !important;scroll-behavior:auto !important}
}

/* Nobody's picture. Someone credited who has no account here gets an empty
   slot: it still takes its space, so the names beside it stay on the line
   the others sit on, but nothing is drawn in it — an orb would read as a
   face we have and are declining to show. Last in the file on purpose, so
   it outranks every per-component .av that sets a background of its own. */
.av.none{background:none;box-shadow:none}

/* THE CONTROLS COME UP TO THE NAME.

   The strip bottom-aligns everything in it, so a 34px button round a small
   glyph and 11.6px of type do not share a centre: the buttons sat 6.8px
   below the middle of the name, and the pen 3.5px below it.

   Lifted with a transform rather than a margin. A margin on a bottom-aligned
   item grows the strip, and a taller strip pushes the name down inside it —
   swept, and at every value the two chased each other. A transform paints
   the button somewhere else without telling the layout, so the name, the
   rule and the band under it all stay exactly where they were.

   Two figures because they start from two places: the pen is an 18px box and
   the buttons are 34px ones, and each is lifted by half the difference
   between its own height and the type's. */
.pshelf .shelftabs .tabadd,
.pshelf .shelftabs .shelfctl{transform:translateY(-6.8px)}
.pshelf .shelftabs .pen{transform:translateY(-3.5px)}
