/* The hub's look.
 *
 * **Every token below is a value out of `studio/Sources/Theme.swift`**, not a
 * colour chosen to resemble one. His ruling, 2026-09-01: what people see should
 * be what he sees on his Mac. If the app's palette moves, this file is wrong
 * until it moves with it — and that is the intended relationship, not a
 * maintenance hazard to be designed away.
 *
 * `design/10-hub.html` is the argued-out version of these screens. */

:root {
  --surface: #FFFFFF; --surface2: #F2F2F4; --surface3: #F8F8F9;
  --ground: #E4E4E7; --band: #F4F4F7; --panel: #FAFAFC;
  --row-hover: #F5F5F7; --sel-quiet: #E9E9EE;
  --ink: #17171A; --ink2: #5A5A61; --ink3: #88888F; --ink4: #AEAEB5;
  --rule: #E8E8EB; --rule2: #D9D9DE; --rule-strong: #B7B7BE;
  --accent: #0A6CE8; --accent-tint: rgba(10,108,232,.11); --accent-line: rgba(10,108,232,.30);
  --on-accent: #FFFFFF;
  --frame-empty: #EFEFF2; --frame-line: rgba(0,0,0,.13);
  --paper: #FFFFFF; --desk: #A8A8AF;
  --live: #2F7A44; --danger: #B4531B;
  --sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", sans-serif;
  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, monospace;
  /* The document's own voice. `Brand`'s printed pages are set in a serif and it
     is the clearest single thing that makes that view read as the app's page. */
  --serif: ui-serif, "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
  --c1:#2C7A7B; --c2:#2B6CB0; --c3:#4C51BF; --c4:#7B4BC4;
  --c5:#A33574; --c6:#9A6B10; --c7:#2F7A44; --c8:#B4531B;
  --chip-a: .13;
  /* **His blue, and only in the front of house.** The three tokens above named
     `--accent` are the app's and are read out of Theme.swift; these four are
     the nr-brand skill's, and they stop at the door. A collaborator meets his
     colours in the foyer and the app's colours in the tool, and that handover
     is the design. */
  --brand: #2136C8; --brand-ink: #2136C8;
  --brand-tint: rgba(33,54,200,.09); --brand-line: rgba(33,54,200,.26);
  --display: "Rethink Sans", -apple-system, BlinkMacSystemFont, sans-serif;
}
@media (prefers-color-scheme: dark) {
  :root {
    --surface: #0E0E11; --surface2: #1E1E22; --surface3: #17171B;
    --ground: #16161A; --band: #1A1A20; --panel: #212127;
    --row-hover: #1A1A1F; --sel-quiet: #26262E;
    --ink: #EDEDF0; --ink2: #A0A0A8; --ink3: #74747C; --ink4: #55555C;
    --rule: #232329; --rule2: #3A3A40; --rule-strong: #4E4E56;
    --accent: #0A84FF; --accent-tint: rgba(10,132,255,.22); --accent-line: rgba(10,132,255,.45);
    --on-accent: #141416;
    --frame-empty: #2A2A2E; --frame-line: rgba(255,255,255,.11);
    --paper: #202024; --desk: #0E0E10;
    --live: #4FB477; --danger: #E08A50;
    --c1:#4FB0B0; --c2:#5C9BE0; --c3:#8189E8; --c4:#B08BEA;
    --c5:#D879A8; --c6:#D0A040; --c7:#5FB57C; --c8:#E08A50;
    --chip-a: .18;
    /* Lifted so it reads on a near-black ground: #2136C8 on #0E0E11 is 4.2:1,
       which is fine for a fill and not for a line of type. The fill keeps more
       of the original than the ink does. */
    --brand: #4457DE; --brand-ink: #8A97F7;
    --brand-tint: rgba(90,108,242,.17); --brand-line: rgba(90,108,242,.38);
  }
}

* { box-sizing: border-box; }
html, body { height: 100%; }
/* The client paints into these, so they have to carry the window's height down
   to `.app` — a percentage height against an auto-height parent is no height at
   all, and the whole page grew instead of filling the window. */
#film, #people { height: 100%; }
body {
  margin: 0; background: var(--surface2); color: var(--ink);
  font: 14px/1.55 var(--sans); -webkit-font-smoothing: antialiased;
}
a { color: inherit; text-decoration: none; }
.spacer { flex: 1; }
.empty { color: var(--ink3); font-size: 13px; }

/* ---- the app's chrome -------------------------------------------------- */
/* **Fills the window rather than growing past it.** The app's chrome stays put
   and only the list moves; with `min-height` the whole page grew instead, which
   pushed the sidebar's search below the fold — part of why he said the sidebar
   felt hidden away. */
.app { display: flex; flex-direction: column; height: 100%; min-height: 0; }
.topbar {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 14px; border-bottom: 1px solid var(--rule); background: var(--surface2);
}
.film { font: 500 14px/1 var(--sans); }
.chev { color: var(--ink3); font-size: 11px; }
.modes {
  display: flex; gap: 2px; margin: 0 auto; padding: 3px;
  background: var(--surface3); border-radius: 20px; border: 1px solid var(--rule);
}
.modes button {
  font: 500 13px/1 var(--sans); color: var(--ink2); background: transparent;
  border: 0; padding: 6px 13px; border-radius: 16px; cursor: pointer;
}
.modes button.on { background: var(--accent); color: var(--on-accent); }
.icon {
  width: 26px; height: 22px; border-radius: 6px; display: grid; place-items: center;
  color: var(--ink2); font-size: 14px; background: transparent; border: 0; cursor: pointer;
}
.icon:hover { background: var(--row-hover); color: var(--ink); }
.icon.lit { color: var(--accent); }
.rolepill {
  font: 500 11px/1 var(--sans); padding: 5px 10px; border-radius: 20px;
  border: 1px solid var(--accent-line); color: var(--accent); background: var(--accent-tint);
  white-space: nowrap;
}
.rolepill.view { border-color: var(--rule2); color: var(--ink2); background: transparent; }
.livedot { display: inline-flex; align-items: center; gap: 6px; font: 11px/1 var(--sans); color: var(--live); }
.livedot i { width: 6px; height: 6px; border-radius: 50%; background: var(--live); }
.livedot.off { color: var(--ink3); }
.livedot.off i { background: var(--ink4); }
.who { display: inline-flex; align-items: center; gap: 7px; font: 12px/1 var(--sans); color: var(--ink2); }
.avatar {
  width: 22px; height: 22px; border-radius: 50%; background: var(--sel-quiet);
  color: var(--ink2); display: grid; place-items: center; font: 500 10px/1 var(--sans);
}

/* ---- your films --------------------------------------------------------- */
.films {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 14px; padding: 18px; background: var(--surface); flex: 1; align-content: start;
}
.filmcard {
  border: 1px solid var(--rule); border-radius: 11px; overflow: hidden;
  background: var(--surface3); display: block;
}
.filmcard:hover { border-color: var(--rule-strong); }
.filmcard .cover { height: 92px; background: var(--frame-empty); }
.filmcard .meta { padding: 10px 12px; }
.filmcard .name { font: 500 14px/1.2 var(--sans); }
.filmcard .facts { font: 12px/1.5 var(--sans); color: var(--ink3); margin-top: 3px; }
.cardhead { display: flex; align-items: center; justify-content: space-between; gap: 8px; }

/* ---- signing in --------------------------------------------------------- */
.signin { background: var(--surface); padding: 90px 24px; text-align: center; flex: 1; }
.wordmark { font: 500 20px/1.2 var(--sans); letter-spacing: -.01em; }
.sub2 { color: var(--ink3); font-size: 13px; margin-top: 3px; }
.signin .empty { margin-top: 26px; }

/* ---- the film ----------------------------------------------------------- */
.body { display: flex; flex: 1; min-height: 0; }

.side-h { display: flex; align-items: center; gap: 8px; padding: 11px 12px 8px; font: 500 13px/1 var(--sans); }
.scene-row {
  display: flex; align-items: center; gap: 9px; margin: 1px 7px; padding: 7px 9px;
  border-radius: 8px; font: 13px/1.2 var(--sans); color: var(--ink2); cursor: pointer;
  border: 0; background: transparent; text-align: left; width: calc(100% - 14px);
}
.scene-row:hover { background: var(--row-hover); }
.scene-row.on { background: var(--sel-quiet); color: var(--ink); }
.scene-row .no { font: 500 12px/1 var(--mono); color: var(--ink); min-width: 22px; }
.scene-row .dot { width: 6px; height: 6px; border-radius: 50%; flex: none; }

.well { flex: 1; min-width: 0; background: var(--surface); overflow: auto; }
.sceneband { background: var(--band); padding: 11px 14px 9px; border-bottom: 1px solid var(--rule); }
.sceneband .top { display: flex; align-items: center; gap: 9px; }
.sceneband .num { font: 500 15px/1 var(--sans); }
.sceneband .slug { font: 500 15px/1 var(--sans); letter-spacing: -.01em; }
.count { font: 11px/1 var(--sans); color: var(--ink2); background: var(--sel-quiet); padding: 4px 8px; border-radius: 20px; }
.sceneband .strip { display: flex; gap: 14px; margin-top: 8px; font: 12px/1 var(--sans); color: var(--ink2); }

.shot { display: flex; gap: 12px; padding: 11px 14px; border-bottom: 1px solid var(--rule); align-items: flex-start; }
.shot:hover { background: var(--row-hover); }
.shot .no { font: 500 12px/1.4 var(--mono); color: var(--ink3); width: 34px; flex: none; }

.frame img { width: 100%; height: 100%; object-fit: cover; display: block; }
.shot .desc { flex: 1; min-width: 0; font: 13px/1.45 var(--sans); }
.shot .desc .title { font-weight: 500; }
.shot .desc .said { color: var(--ink2); }
.shot .cells { display: flex; gap: 6px; flex-wrap: wrap; width: 330px; flex: none; align-content: flex-start; }
.chip { font: 11px/1 var(--sans); padding: 5px 9px; border-radius: 6px; white-space: nowrap; }
.k1 { color: var(--c1); background: color-mix(in srgb, var(--c1) calc(var(--chip-a)*100%), transparent); }
.k2 { color: var(--c2); background: color-mix(in srgb, var(--c2) calc(var(--chip-a)*100%), transparent); }
.k3 { color: var(--c3); background: color-mix(in srgb, var(--c3) calc(var(--chip-a)*100%), transparent); }
.k4 { color: var(--c4); background: color-mix(in srgb, var(--c4) calc(var(--chip-a)*100%), transparent); }
.k6 { color: var(--c6); background: color-mix(in srgb, var(--c6) calc(var(--chip-a)*100%), transparent); }
.k8 { color: var(--c8); background: color-mix(in srgb, var(--c8) calc(var(--chip-a)*100%), transparent); }

/* ---- people ------------------------------------------------------------- */
.sheetwrap { padding: 18px 20px; background: var(--surface); flex: 1; }
.sheetwrap h2 { font: 500 16px/1.3 var(--sans); margin: 0 0 12px; }
.rows { border: 1px solid var(--rule); border-radius: 9px; overflow: hidden; }
.prow { display: flex; align-items: center; gap: 12px; padding: 10px 13px; border-bottom: 1px solid var(--rule); }
.prow:last-child { border-bottom: 0; }
.prow .addr { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.prow .note { color: var(--ink4); }
.btn {
  font: 12px/1 var(--sans); color: var(--ink2); padding: 7px 11px;
  border: 1px solid var(--rule2); border-radius: 7px; background: transparent; cursor: pointer;
}
.btn:hover { border-color: var(--rule-strong); color: var(--ink); }
.btn.danger { color: var(--danger); }
.actions { display: flex; gap: 8px; margin-top: 12px; }
input[type="email"], input[type="date"], select {
  font: 13px/1 var(--sans); color: var(--ink); background: var(--surface);
  border: 1px solid var(--rule2); border-radius: 7px; padding: 8px 10px;
}
.made {
  margin-top: 12px; padding: 11px 13px; border-radius: 9px;
  border: 1px solid var(--accent-line); background: var(--accent-tint);
}
.made code { font: 12px/1.5 var(--mono); word-break: break-all; }
.saying { margin-top: 10px; font-size: 13px; color: var(--ink3); }
.saying.bad { color: var(--danger); }

/* ---- the views bar ------------------------------------------------------ */
.viewbar {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 14px; border-bottom: 1px solid var(--rule); background: var(--surface2);
}
.viewbar .lab { font: 12px/1 var(--sans); color: var(--ink3); }
.seg { display: flex; gap: 2px; padding: 2px; background: var(--surface3); border-radius: 8px; border: 1px solid var(--rule); }
.seg button {
  font: 12px/1 var(--sans); color: var(--ink2); padding: 5px 11px;
  border: 0; border-radius: 6px; background: transparent; cursor: pointer;
}
.seg button.on { background: var(--surface); color: var(--ink); box-shadow: 0 1px 2px rgba(0,0,0,.10); }
.rownote { margin-top: 4px; font: 12px/1.45 var(--sans); color: var(--ink3); }
.tech { font: 11px/1.5 var(--mono); color: var(--ink3); letter-spacing: .02em; }

/* ---- storyboard --------------------------------------------------------- */
.cards {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(232px, 1fr));
  gap: 14px; padding: 14px;
}
.card { border: 1px solid var(--rule); border-radius: 10px; overflow: hidden; background: var(--surface3); }
.cardframe { aspect-ratio: 16 / 9; background: var(--frame-empty); }
.cardframe img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cardsaid { padding: 9px 11px; }
.cardsaid .no { font: 500 11px/1 var(--mono); color: var(--ink3); margin-right: 7px; }
.cardsaid .title { font: 500 13px/1.35 var(--sans); }
.cardsaid .tech { margin-top: 4px; }

/* ---- document ----------------------------------------------------------- */
.doc { padding: 6px 0; }
.docshot { display: flex; gap: 16px; padding: 14px 18px; border-bottom: 1px solid var(--rule); align-items: flex-start; }
.docsaid { flex: 1; min-width: 0; }
.docsaid .no { font: 500 12px/1 var(--mono); color: var(--ink3); margin-right: 8px; }
.docsaid .title { font: 500 14px/1.4 var(--sans); }
.docsaid p { margin: 6px 0 0; font: 13px/1.6 var(--sans); color: var(--ink); max-width: 62ch; }
.docsaid p.rownote { color: var(--ink3); }
.docframe { width: 260px; flex: none; border-radius: 6px; overflow: hidden; position: relative; background: var(--frame-empty); border: 1px solid var(--frame-line); }
.docframe img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ---- the side panel ----------------------------------------------------- */
.panelside {
  width: 300px; flex: none; border-left: 1px solid var(--rule);
  background: var(--surface2); display: flex; flex-direction: column; overflow: auto;
}
.paneltabs { display: flex; gap: 3px; padding: 10px 9px 9px; }
.paneltabs button {
  flex: 1; font: 500 12px/1 var(--sans); color: var(--ink2); background: transparent;
  border: 0; padding: 6px 0; border-radius: 20px; cursor: pointer;
}
.paneltabs button.on { background: var(--accent); color: #fff; }
.panelhead {
  display: flex; align-items: center; gap: 8px; padding: 2px 13px 8px;
  font: 12px/1 var(--sans); color: var(--ink3);
}
.panelbody { padding: 4px 13px 14px; }
.panelbody .said { font: 13px/1.6 var(--sans); color: var(--ink); }
.sheet {
  margin: 0 10px 12px; padding: 14px 15px; border-radius: 5px;
  background: var(--paper); border: 1px solid var(--frame-line);
  font: 11px/1.62 var(--mono); color: var(--ink); white-space: pre-wrap;
}

/* ---- walls of pictures -------------------------------------------------- */
.wall { display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: 6px; padding: 4px 13px 12px; }
.wall.big { grid-template-columns: repeat(auto-fill, minmax(148px, 1fr)); gap: 9px; padding: 10px 0 4px; }
.tile { display: block; aspect-ratio: 4 / 3; border-radius: 5px; overflow: hidden; position: relative; background: var(--frame-empty); border: 1px solid var(--frame-line); }
.tile img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ---- locations and boards ----------------------------------------------- */
.place { border: 1px solid var(--rule); border-radius: 11px; padding: 13px 15px; margin-bottom: 14px; background: var(--surface3); }
.placehead { display: flex; align-items: center; gap: 10px; }
.placename { font: 500 15px/1.3 var(--sans); }
.areaname { font: 500 13px/1.3 var(--sans); color: var(--ink2); }
.placeline { margin-top: 5px; font: 13px/1.5 var(--sans); color: var(--ink2); }
.placeline.said { color: var(--ink3); }
.areas { margin-top: 12px; display: flex; flex-direction: column; gap: 10px; }
.area { border-top: 1px solid var(--rule); padding-top: 10px; }
.place .wall { padding-left: 0; padding-right: 0; }

/* ---- the schedule ------------------------------------------------------- */
.lane { border: 1px solid var(--rule); border-radius: 11px; padding: 12px 14px; margin-bottom: 14px; background: var(--surface3); }
.lanehead { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.stripe {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  padding: 9px 11px; margin-bottom: 5px; border-radius: 8px; cursor: pointer;
  background: var(--surface); border: 1px solid var(--rule); color: var(--ink);
  font: 13px/1.3 var(--sans);
}
.stripe:hover { border-color: var(--rule-strong); }
.stripe .no { font: 500 12px/1 var(--mono); color: var(--ink3); min-width: 24px; }
.stripe .dot { width: 6px; height: 6px; border-radius: 50%; flex: none; }
.stripe .addr { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- the table proper: one grid, shared by the header and every row ------ */
/* **A row is as wide as the columns, not as wide as the window.**
 *
 * The well scrolls sideways, and its children are ordinary blocks — so each one
 * was only ever the width of the *visible* well while the columns inside it ran
 * on past that. Scroll right and the scene bands, the row backgrounds and every
 * dividing line stopped dead at whatever column happened to sit at the window's
 * edge, leaving Notes, Tags and Shoot Day floating on bare paper.
 *
 * One column, at least as wide as its widest child, is the whole fix: the row
 * grid's own minimum (every column's smallest size, plus the gaps) becomes the
 * table's width, the bands and the header stretch to match it, and on a wide
 * window the `1fr` still lets the table fill the well the way it always did.
 * `align-content: start` because a grid otherwise stretches its rows to fill
 * the height, and a short list would draw as a few very tall rows. */
.well.table { display: grid; grid-template-columns: minmax(min-content, 1fr); align-content: start; }
.thead {
  display: grid; gap: 10px; align-items: center;
  padding: 8px 14px; border-bottom: 1px solid var(--rule);
  position: sticky; top: 0; z-index: 3; background: var(--surface);
  font: 11px/1 var(--sans); color: var(--ink3);
}
.th { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.trow {
  display: grid; gap: 10px; align-items: start;
  padding: 10px 14px; border-bottom: 1px solid var(--rule);
}
.trow:hover { background: var(--row-hover); }
.trow.struck { opacity: .45; }
.trow .no { font: 500 12px/1.5 var(--mono); color: var(--ink3); }
.td { min-width: 0; display: flex; flex-wrap: wrap; gap: 5px; align-content: flex-start; }
.td .prose { font: 13px/1.45 var(--sans); color: var(--ink); white-space: pre-wrap; }
/* Description is the column a person reads; the rest are labels. */
.trow .td:nth-child(2) { display: block; }
/* **A shot's title is its name, and a name is set apart from what is said
   about it.** Semibold, exactly as `Columns.cell` draws it in
   `studio/Sources/Table.swift` — the app has always done this and the portal
   drew the title as ordinary prose. */
.td-title .prose { font-weight: 600; }

/* the frame in a row is a button now, because it opens */
button.frame { padding: 0; cursor: pointer; position: relative; display: block; }
button.frame:hover { border-color: var(--accent-line); }
.frame video, .frame img { width: 100%; height: 100%; object-fit: cover; display: block; }
.frame.empty { display: block; }
/* **The marks the app puts on a still.** Both corners are its choice, not a
   layout convenience: the count sits bottom right where it cannot cover a
   face, and the play triangle top left where the eye starts. `.55` black is
   the app's own tint for both. */
.frame .more, .cardframe .more, .frames .more {
  position: absolute; right: 4px; bottom: 4px; font: 500 10px/1 var(--mono);
  background: rgba(0,0,0,.55); color: #fff; padding: 3px 6px; border-radius: 20px;
}
.playmark {
  position: absolute; left: 5px; top: 5px; width: 16px; height: 16px;
  border-radius: 50%; background: rgba(0,0,0,.55);
  display: grid; place-items: center;
}
.playmark svg { width: 10px; height: 10px; fill: #fff; display: block; }

/* A frame with no picture in it says so the way the app says it — the icon and
   nothing else. The app can add the sentence "Paste or drop a frame"; nobody
   reading this can drop anything, so the sentence goes and the icon stays. */
.nopic {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 22px; height: 18px; color: var(--ink4);
}

/* ---- the pop-out --------------------------------------------------------- */
.popout {
  position: fixed; inset: 0; z-index: 50; display: flex; flex-direction: column;
  background: rgba(0,0,0,.72); padding: 26px 30px 22px;
}
.popbar { display: flex; align-items: center; gap: 12px; color: #EDEDF0; margin-bottom: 14px; }
.popbar .no { font: 500 13px/1 var(--mono); color: #A0A0A8; }
.popbar .poptitle { font: 500 15px/1.3 var(--sans); }
.popbar .lab { font: 12px/1 var(--sans); color: #A0A0A8; }
.popbar .icon { color: #EDEDF0; }
.popstage {
  flex: 1; min-height: 0; display: flex; align-items: center; justify-content: center;
}
.popstage video, .popstage img {
  max-width: 100%; max-height: 100%; object-fit: contain;
  border-radius: 6px; background: #000;
}
.popstrip { display: flex; gap: 8px; margin-top: 14px; overflow-x: auto; padding-bottom: 4px; }
.popthumb {
  /* The same shape as the plate above it, and as every frame in the list. */
  height: 62px; width: auto; aspect-ratio: var(--r, 1.78);
  flex: none; padding: 0; cursor: pointer; overflow: hidden;
  position: relative;
  border-radius: 5px; border: 1px solid rgba(255,255,255,.18); background: #000;
}
.popthumb.on { border-color: var(--accent); }
.popthumb img, .popthumb video { width: 100%; height: 100%; object-fit: cover; display: block; }

/* The eight chip inks, all of them. Five and seven were missing, so any term
 * whose hash landed on them drew as plain text beside chips — which read as the
 * table being inconsistent rather than as a gap in a stylesheet. */
.k5 { color: var(--c5); background: color-mix(in srgb, var(--c5) calc(var(--chip-a)*100%), transparent); }
.k7 { color: var(--c7); background: color-mix(in srgb, var(--c7) calc(var(--chip-a)*100%), transparent); }

/* The pop-out sits over the list, so the list must recede rather than compete. */
.popout { background: rgba(0,0,0,.88); backdrop-filter: blur(3px); }
button.cardframe, button.docframe { padding: 0; border: 0; cursor: pointer; display: block; width: 100%; }
button.cardframe { aspect-ratio: 16 / 9; background: var(--frame-empty); }
button.cardframe video, button.cardframe img { width: 100%; height: 100%; object-fit: cover; display: block; }
button.docframe { width: 260px; border-radius: 6px; overflow: hidden; position: relative; background: var(--frame-empty); border: 1px solid var(--frame-line); }
/* **Cut to the film's shape here too, not merely scaled to fit.**
   Everywhere else has carried `object-fit: cover` since the frames were built;
   the document's did not, so its pictures took their own height inside a box
   declared at the film's ratio — a tall still lost its bottom, a wide one left
   a band of grey, and `object-position` (which is where the crop lives) had
   nothing to act on and was quietly ignored. Only clips looked right, because
   the `.clip` rule below happened to say `cover`. */
button.docframe video, button.docframe img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* **Every column's edge, to pull.** A wide, invisible target with a hairline
   drawn down the middle of it: the line is where the column ends and the target
   is what a hand can actually hit. It lights up while it is held so a drag that
   has begun looks like one. Double-click puts a column back to the width the
   table chose for it. */
.thgrip { position: relative; display: block; min-width: 0; }
.grip {
  position: absolute; top: -8px; bottom: -8px; right: -9px; width: 14px;
  cursor: col-resize; touch-action: none;
}
.grip::after {
  content: ""; position: absolute; top: 0; bottom: 0; left: 6px; width: 1px;
  background: var(--rule2);
}
.grip:hover::after, .grip.on::after { background: var(--accent); width: 2px; }

/* ---- searching and sorting ---------------------------------------------- */
.find {
  font: 13px/1 var(--sans); color: var(--ink); background: var(--surface);
  border: 1px solid var(--rule2); border-radius: 7px; padding: 6px 10px;
  width: 220px; max-width: 30vw;
}
.find:focus { outline: none; border-color: var(--accent-line); }
.btn.on { color: var(--accent); border-color: var(--accent-line); background: var(--accent-tint); }
button.th {
  background: transparent; border: 0; padding: 0; text-align: left; cursor: pointer;
  font: 11px/1 var(--sans); color: var(--ink3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
button.th:hover { color: var(--ink); }
button.th.on { color: var(--accent); }

/* A clip in the list is a picture until it is looked at, so both must sit the
   same way in their box or a row would jump as one becomes the other. */
.frame img.clip, .frame video.clip,
.cardframe img.clip, .cardframe video.clip,
.docframe img.clip, .docframe video.clip,
.tile img.clip, .tile video.clip {
  width: 100%; height: 100%; object-fit: cover; display: block;
}

/* ---- the sidebar, as the app keeps it ----------------------------------- */
/* 258px is `Brand.sidebarWidth`. It was 236 here, and narrow enough that he
   said it felt hidden away — along with having nothing in it but a list. */
.sidebar {
  width: 258px; flex: none; border-right: 1px solid var(--rule);
  display: flex; flex-direction: column; background: var(--surface2); min-height: 0;
}
.side-h {
  display: flex; align-items: center; gap: 8px; padding: 12px 14px 8px;
}
.sidetitle { font: 500 14px/1 var(--sans); color: var(--ink); }
.scenelist { flex: 1; overflow: auto; padding-bottom: 6px; }
.scene-row .slug { flex: 1; min-width: 0; }
.sidefoot { padding: 10px; border-top: 1px solid var(--rule); }
.sidefoot .find { width: 100%; max-width: none; border-radius: 20px; padding: 8px 12px; }

/* ---- a frame takes the film's own shape --------------------------------- */
/* **Fills its column rather than setting its own width.** It was 148px inside a
   132px column, so every thumbnail spilled across the Title beside it. */
.frame {
  width: 100%; flex: none; border-radius: 5px; overflow: hidden; position: relative;
  background: var(--frame-empty); border: 1px solid var(--frame-line);
}

/* **A shot's frames, side by side.** One border around the lot and 2px of the
   frame's own grey between them — the app draws the strip as one picture, not
   as four bordered thumbnails. `flex: 1 1 0` with a gap is the app's own
   arithmetic, `(width - gaps) / count`, and each panel keeping the film's shape
   is what makes the strip exactly as tall as a panel. */
.frames {
  display: flex; gap: 2px; width: 100%; position: relative;
  border-radius: 5px; overflow: hidden;
  background: var(--frame-empty); border: 1px solid var(--frame-line);
}
.frames .pane {
  flex: 1 1 0; min-width: 0; padding: 0; border: 0; display: block;
  position: relative; overflow: hidden; cursor: pointer; background: var(--frame-empty);
}
.frames .pane img, .frames .pane video { width: 100%; height: 100%; object-fit: cover; display: block; }

/* The document's row of frames — every one the shot has, which is what the
   app's page draws. No cap and no marks on them. */
.docframes { display: flex; gap: 5px; width: 100%; margin-top: 12px; }
.docshot .docbody .docframes .docframe { flex: 1 1 0; min-width: 0; width: auto; margin-top: 0; }

/* **A frame is measured against itself.** Every box a picture can sit in is
   positioned, because the marks are laid inside it with `inset: 0` — the one
   that was not (the document's) sent an arrow across the whole page. */

/* **The arrows, lying on the photograph they were drawn on.** The overlay wears
   the picture's own inline style, so the crop places both with one set of
   numbers; the SVG carries the picture's pixels as its size, so `cover` cuts
   the marks exactly where it cuts the picture. */
/* `img.marks`, not `.marks`: the pop-out gives every picture in it a black
   backing and a rounded corner, and the overlay is an `<img>` too — so it took
   both and painted the photograph out from on top of it. */
img.marks {
  position: absolute; inset: 0; width: 100%; height: 100%;
  background: none; border-radius: 0; pointer-events: none;
}
/* **The pop-out's picture and its marks share one box.** Which box hardly
   matters; that it is the *same* box is everything, because two pictures of the
   same shape fitted into the same rectangle land on each other exactly. Sizing
   them apart is what went wrong first: the wrapper took the stage's height and
   the photograph kept its own, and the arrow was drawn a fifth too small. */
.marked { position: relative; display: block; line-height: 0; }
/* **The pop-out shows a frame, not a file.** `Plate` in
   `studio/Sources/Moving.swift`: the biggest rectangle of the film's own shape
   that fits the room, centred, with the picture covering it and cropped where
   he cropped it. Opening a frame and being shown something a different shape
   from the one in the list was the last place the film's ratio did not reach.

   `min()` against the stage's own size is `Plate.box` in two lines — width and
   height each the smaller of the room and what the other allows. It needs the
   stage to be a size container, which is why `container-type` is on it. */
.popstage { container-type: size; }
.plate {
  position: relative; line-height: 0; overflow: hidden; border-radius: 6px;
  width: min(100cqw, calc(100cqh * var(--r, 1.78)));
  height: min(100cqh, calc(100cqw / var(--r, 1.78)));
}
.plate .marked { width: 100%; height: 100%; }
.popstage .plate img, .popstage .plate video {
  width: 100%; height: 100%; max-width: none; max-height: none;
  object-fit: cover; display: block;
  /* The black backing belonged to a box that *was* the picture. The plate is
     filled edge to edge, so there is nothing left for it to back. */
  background: none; border-radius: 0;
}

/* ---- the scene band's chips -------------------------------------------- */
.bandchip {
  display: inline-flex; align-items: center; gap: 6px;
  font: 12px/1 var(--sans); color: var(--ink2);
  max-width: 34ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bandchip i { font-style: normal; color: var(--ink4); }

/* ---- the storyboard, card for card -------------------------------------- */
.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 340px)); gap: 16px; padding: 16px; }
.card { border-radius: 10px; overflow: hidden; background: var(--surface3); border: 1px solid var(--rule); }
.cardframe { background: var(--frame-empty); position: relative; }
.cardsaid { padding: 10px 12px 12px; }
.cardsaid .no { font: 500 12px/1 var(--mono); color: var(--ink3); margin-bottom: 6px; }
.cardsaid .said { font: 13px/1.45 var(--sans); color: var(--ink); }
.cardsaid .tech { margin-top: 7px; font: 11px/1.5 var(--mono); color: var(--ink3); letter-spacing: .02em; }

/* ---- the document: a sheet of paper on a desk --------------------------- */
/* The desk is markedly darker than the app's other surfaces on purpose — it is
   what makes the page read as paper rather than as a narrower window. */
.desk { background: var(--desk); padding: 26px 0 60px; min-height: 100%; }
.paper {
  max-width: 760px; margin: 0 auto; background: var(--paper);
  padding: 42px 54px 56px; border-radius: 2px;
  box-shadow: 0 1px 3px rgba(0,0,0,.28), 0 14px 40px -12px rgba(0,0,0,.34);
}
.docscene + .docscene { margin-top: 30px; }
.dochead { display: flex; align-items: baseline; gap: 12px; margin-bottom: 14px; }
.dochead .no { font: 500 13px/1 var(--mono); color: var(--ink2); }
.dochead .slug {
  font: 600 15px/1.3 var(--sans); color: var(--ink);
  text-decoration: underline; text-underline-offset: 4px; flex: 1;
}
.dochead .shots { font: 11px/1 var(--sans); color: var(--ink3); letter-spacing: .06em; }
.docshot { display: flex; gap: 16px; margin-bottom: 26px; }
.docshot > .no { font: 500 12px/1.7 var(--mono); color: var(--ink3); width: 34px; flex: none; text-align: right; }
/* **Not `.body`.** That class is the page's own — sidebar beside well — and it
   is `display:flex`, so a document shot called `.body` laid its title, its
   technical line, its description and its note out as four columns. */
.docshot .docbody { flex: 1; min-width: 0; }
/* Serif is the document's own voice, and the clearest single thing that makes
   this read as the app's page rather than as another web table. */
.docshot .docbody .title { font: 600 15px/1.4 var(--serif); color: var(--ink); }
.docshot .docbody .tech { margin-top: 3px; font: 11px/1.6 var(--mono); color: var(--ink3); letter-spacing: .02em; }
.docshot .docbody .said { margin: 7px 0 0; font: 14px/1.6 var(--serif); color: var(--ink); }
.docshot .docbody .note { margin: 6px 0 0; font: italic 13px/1.6 var(--serif); color: var(--ink2); }
.docshot .docbody .docframe {
  width: 100%; margin-top: 12px; border-radius: 2px; border: 0;
  background: var(--frame-empty); box-shadow: 0 1px 3px rgba(0,0,0,.25);
}

/* ---- locations and boards: master and detail ---------------------------- */
.scene-row.deep { padding-left: 26px; }
.scene-row .count { margin-left: auto; }
.detail { padding: 18px 20px 40px; }
.detailhead { margin-bottom: 14px; }
.detailhead h1 { font: 500 24px/1.2 var(--sans); margin: 0 0 8px; letter-spacing: -.02em; }
.detailhead h1 .pictures { font: 12px/1 var(--mono); color: var(--ink3); letter-spacing: .02em; margin-left: 8px; }
.detailchips { display: flex; gap: 14px; margin-bottom: 12px; }
.detailbar { display: flex; align-items: center; gap: 8px; padding-top: 8px; border-top: 1px solid var(--rule); }
.detailbar .lab { font: 12px/1 var(--sans); color: var(--ink3); }
.detail .wall.big {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 8px; padding: 12px 0 0;
}

/* the facts panel the app keeps to the right of a location */
.facts {
  width: 290px; flex: none; border-left: 1px solid var(--rule);
  background: var(--surface2); padding: 14px; overflow: auto;
}
.fact { background: var(--surface3); border: 1px solid var(--rule); border-radius: 10px; padding: 12px 13px; margin-bottom: 12px; }
.factname { font: 500 12px/1 var(--sans); color: var(--ink2); margin-bottom: 7px; }
.fact .said { margin: 0; font: 13px/1.5 var(--sans); color: var(--ink); }
.fact .btn { margin-top: 9px; display: inline-block; }

/* ---- the password on an unlisted link ----------------------------------- */
.form { max-width: 290px; margin: 30px auto 0; text-align: left; }
.flabel { font: 12px/1 var(--sans); color: var(--ink2); display: block; margin-bottom: 6px; }
.field {
  width: 100%; padding: 9px 11px; border: 1px solid var(--rule2); border-radius: 8px;
  color: var(--ink); font-size: 13px; background: var(--surface);
}
.primary {
  margin-top: 10px; width: 100%; text-align: center; padding: 9px;
  border-radius: 8px; background: var(--accent); color: var(--on-accent);
  font: 500 13px/1 var(--sans); border: 0; cursor: pointer;
}

/* ======================================================================
 * FRONT OF HOUSE — the shelf, a film's own page, its documents, its crew.
 *
 * Everything above this line is the app seen through a browser and takes its
 * colours from `Theme.swift`. Everything below is *his*: the wordmark, the
 * brand blue, Rethink Sans on display type. The two meet at the tile marked
 * Open, and the change of accent there is deliberate.
 *
 * **Every rule is scoped under `.fh`, and that is not tidiness.** The app
 * chrome above already owns `.facts`, `.fact`, `.doc` and a dozen other names
 * anybody would reach for here — `.facts` is the references panel, 290px wide
 * with a left border, and it silently ate the vitals strip the first time this
 * was written. Scoping in one direction fixes both: nothing above reaches in,
 * and nothing here reaches out, because no page in the app has an `.fh`.
 *
 * Drawn on the server from `front.json`, so these screens arrive at once.
 * ====================================================================== */

/* Rethink Sans, from the nr-brand skill, subset to Latin. Display type only —
   never a number, never a table, never anything inside the app's own chrome.
   A glyph outside the subset falls back to the system face rather than to a
   box, which is why the stack under it is real. */
@font-face {
  font-family: "Rethink Sans"; font-weight: 500; font-style: normal;
  font-display: swap; src: url(/rethink-500.woff2) format("woff2");
}
@font-face {
  font-family: "Rethink Sans"; font-weight: 600; font-style: normal;
  font-display: swap; src: url(/rethink-600.woff2) format("woff2");
}

.fh { background: var(--surface); min-height: 100%; }
.fh .fh-bar {
  display: flex; align-items: center; gap: 12px;
  padding: 14px 22px; border-bottom: 1px solid var(--rule); background: var(--surface2);
}
.fh .back {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 26px; flex: none; border-radius: 7px; border: 1px solid var(--rule2);
  color: var(--ink2); text-decoration: none; font-size: 13px;
}
.fh .back:hover { background: var(--row-hover); }
/* The wordmark is one fixed blue and cannot answer the theme, so on the dark
   chrome it is lifted rather than left to sink into it. A light version of the
   file would beat a filter; there is not one yet. */
.fh .mark {
  height: 17px; aspect-ratio: 1811 / 221; flex: none; display: block;
  background: url(/mark.png) left center / contain no-repeat;
}
@media (prefers-color-scheme: dark) {
  .fh .mark { filter: brightness(2.05) saturate(.85); }
}
.fh .spacer { flex: 1; }
/* **The film, where the wordmark stands on every other page.** The picture he
   chose at thirty points, and for an editor it is the way to the picker — the
   only link to it now the cover is gone. */
.fh .barfilm { display: inline-flex; align-items: center; gap: 9px; min-width: 0; }
.fh .barfilm .thumb { display: block; flex: none; line-height: 0; border-radius: 4px; }
.fh .barfilm .th {
  display: block; width: 30px; height: 23px; flex: none; border-radius: 4px;
  border: 1px solid var(--frame-line); background: var(--frame-empty) center / cover no-repeat;
}
.fh .barfilm .thumb:hover .th { border-color: var(--brand-ink); }
.fh .barfilm .fname {
  font: 500 13px/1 var(--sans); color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.fh .brandpill {
  font: 500 11px/1 var(--sans); padding: 5px 10px; border-radius: 20px;
  border: 1px solid var(--brand-line); color: var(--brand-ink);
  background: var(--brand-tint); white-space: nowrap;
}
.fh .brandpill.view { border-color: var(--rule2); color: var(--ink3); background: transparent; }
.fh .pic { display: block; background-position: center; background-size: cover; background-repeat: no-repeat; }

/* ---- the shelf ------------------------------------------------------- */
.fh .shelf { padding: 38px 26px 46px; max-width: 1180px; margin: 0 auto; }
.fh .fh-h { font: 600 30px/1.1 var(--display); letter-spacing: -.025em; color: var(--ink); margin: 0; }
.fh .fh-sub { font: 12px/1 var(--sans); color: var(--ink3); margin-top: 7px; }
.fh .grid3 {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 18px; margin-top: 24px;
}
.fh .pcard {
  border: 1px solid var(--rule); border-radius: 14px; overflow: hidden;
  background: var(--surface3); text-decoration: none; color: inherit; display: block;
}
.fh .pcard:hover { border-color: var(--rule2); }
/* One box for every card so the titles line up, and the picture inside it at
   the film's own ratio, letterboxed on black — how a film is presented
   anywhere else, and it makes the ratio label something the eye can see. */
/* A cover with no picture is the app's own answer to 'nothing here' rather
   than a black slab, which reads as a broken image. Black is for a picture
   that is genuinely letterboxed. */
.fh .pcard .cov { aspect-ratio: 16 / 9; background: var(--frame-empty); position: relative; }
.fh .pcard .cov.shot { background: #000; }
.fh .pcard .cov .pic { width: 100%; height: 100%; background-size: contain; }
.fh .pcard .m { padding: 14px 15px 15px; }
.fh .pcard .t { font: 600 17px/1.2 var(--display); letter-spacing: -.018em; color: var(--ink); }
.fh .pcard .by { font: 12px/1.5 var(--sans); color: var(--ink3); margin-top: 3px; }
.fh .pcard .n { font: 12px/1.5 var(--sans); color: var(--ink2); margin-top: 10px; }
.fh .pcard .foot {
  display: flex; align-items: center; gap: 7px; margin-top: 12px;
  padding-top: 11px; border-top: 1px solid var(--rule);
  font: 12px/1 var(--sans); color: var(--ink3);
}
.fh .cardhead { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }

/* ---- a film's own page ----------------------------------------------- */
/* **There is no cover picture, and that is his ruling.** 2026-09-01, having
   seen it four ways: the band was the biggest thing on the page and the work
   started 550 points down. The page opens on the film's name now, at 123 —
   and the photograph he chose is in the bar, thirty points wide, where it also
   opens the picker.

   The `.hero`, `.nohero` and `.setphoto` rules that used to live here went with
   it. If a cover ever comes back it comes back measured. */
.fh .filmhead { padding: 26px 26px 0; max-width: 1180px; margin: 0 auto; }
.fh .filmhead.thin { padding-bottom: 4px; }
.fh .fh-title { font: 600 40px/1.02 var(--display); letter-spacing: -.032em; color: var(--ink); margin: 0; }
.fh .fh-title.small { font-size: 26px; letter-spacing: -.022em; }
.fh .credits { font: 13px/1.6 var(--sans); color: var(--ink2); margin-top: 8px; }
.fh .credits b { color: var(--ink); font-weight: 500; }
.fh .facts {
  display: flex; flex-wrap: wrap; gap: 20px 34px; width: auto; background: none;
  border: 0; border-top: 1px solid var(--rule); margin: 22px 0 0; padding: 18px 0 0;
}
.fh .fact { background: none; border: 0; border-radius: 0; padding: 0; margin: 0; }
.fh .fact span {
  display: block; font: 500 10px/1 var(--sans); letter-spacing: .085em;
  text-transform: uppercase; color: var(--ink3);
}
.fh .fact b { display: block; font: 500 15px/1.3 var(--sans); color: var(--ink); margin-top: 6px; }
.fh .filmbody { padding: 30px 26px 46px; max-width: 1180px; margin: 0 auto; display: grid; gap: 34px; }
.fh .filmbody.one { gap: 26px; }
.fh .block { min-width: 0; }
.fh .blockhead {
  font: 500 10px/1 var(--sans); letter-spacing: .095em; text-transform: uppercase;
  color: var(--ink3); margin: 0 0 13px; display: flex; gap: 12px; align-items: baseline;
}
.fh .blockhead .more {
  color: var(--brand-ink); text-decoration: none; letter-spacing: .02em;
  text-transform: none; font-size: 11px;
}
.fh .logline { font: 15px/1.6 var(--sans); color: var(--ink); margin: 0; max-width: 68ch; }
.fh .said { margin-top: 22px; font: inherit; color: inherit; }
.fh .said p { font: 13px/1.65 var(--sans); color: var(--ink2); margin: 0; max-width: 68ch; }

/* ---- the pinned documents -------------------------------------------- */
.fh .docs { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 10px; }
.fh .docs.wide { grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }
.fh .doc {
  display: flex; align-items: center; gap: 12px; text-decoration: none; color: inherit;
  border: 1px solid var(--rule); border-radius: 11px; padding: 13px 14px; background: var(--surface3);
}
.fh .doc:hover { border-color: var(--brand-line); background: var(--brand-tint); }
.fh .doc .doci {
  width: 30px; height: 30px; flex: none; border-radius: 8px; display: grid; place-items: center;
  background: var(--brand-tint); color: var(--brand-ink); font-size: 14px;
}
.fh .doc .docn { min-width: 0; }
.fh .doc .docn b { display: block; font: 500 13px/1.35 var(--sans); color: var(--ink); overflow-wrap: anywhere; }
.fh .doc .docn span { display: block; font: 11px/1.4 var(--sans); color: var(--ink3); margin-top: 2px; }

/* ---- the ways in, drawn with what they hold ---------------------------- */
/* **The shot list is not one of these, and that is the point.** His
   instruction, 2026-09-01: it blended in. It is the film itself where the other
   three are things about the film, so it gets the band above them — his blue,
   the counts set large, and no pictures on it at all. */
.fh .lead {
  display: flex; align-items: center; gap: 26px; flex-wrap: wrap;
  padding: 20px 24px; margin: 0 0 16px; border-radius: 12px;
  border: 1px solid var(--brand-line); background: var(--brand-tint);
  text-decoration: none; color: inherit;
}
.fh .lead:hover { border-color: var(--brand-ink); }
.fh .lead .ln {
  font: 600 22px/1.1 var(--display); letter-spacing: -.02em; color: var(--ink);
}
.fh .lead .lcs { display: flex; gap: 26px; }
.fh .lead .lc { display: flex; align-items: baseline; gap: 7px; }
.fh .lead .lc b {
  font: 600 26px/1 var(--display); letter-spacing: -.02em; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.fh .lead .lc span { font: 12px/1 var(--sans); color: var(--ink3); }
.fh .lead .lgo {
  margin-left: auto; font: 500 14px/1 var(--sans); color: var(--brand-ink);
  display: inline-flex; align-items: center; gap: 8px;
}

/* Three equal ways in under it. `minmax(0, 1fr)` and not `1fr`: a grid
   column's floor is its content, so a wide picture pushes the row past the
   page without it. That cost an afternoon once. */
.fh .tools { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
.fh .tool {
  min-width: 0; display: flex; flex-direction: column; overflow: hidden;
  border: 1px solid var(--rule); border-radius: 12px; background: var(--surface3);
  text-decoration: none; color: inherit;
}
.fh .tool:hover { border-color: var(--brand-line); }
.fh .tool .top { display: block; height: 172px; background: var(--frame-empty); }

/* **The name sits under the picture, not on it.** Drawn over the picture it was
   still not clear enough — a name over a photograph is always negotiating with
   whatever is behind it. On the card's own surface it is not negotiating with
   anything, and every tile reads the same way whether it holds photographs or
   dates. */
.fh .tool .lab {
  display: flex; align-items: center; gap: 12px;
  padding: 13px 15px; background: var(--surface3);
}
.fh .tool .top + .lab { border-top: 1px solid var(--rule); }
.fh .tool .labt { min-width: 0; }
.fh .tool .tn {
  display: block; font: 600 15px/1.25 var(--display); letter-spacing: -.012em; color: var(--ink);
}
.fh .tool .td { display: block; font: 12px/1.4 var(--sans); color: var(--ink3); margin-top: 3px; }
.fh .tool .arrow { margin-left: auto; color: var(--brand-ink); font-size: 15px; }

.fh .mosaic { display: grid; gap: 3px; height: 100%; background: #000; }
.fh .mosaic .pic { min-width: 0; min-height: 0; background: var(--frame-empty) center / cover no-repeat; }
.fh .mosaic.one { grid-template-columns: 1fr; }
.fh .mosaic.two { grid-template-columns: 1fr 1fr; }
.fh .mosaic.three { grid-template-columns: 1.55fr 1fr; grid-template-rows: 1fr 1fr; }
.fh .mosaic.three .pic:first-child { grid-row: 1 / 3; }
.fh .mosaic.four { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; }

/* The shoot, as a calendar page, in the same box the pictures fill. */
.fh .cal {
  height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 12px; background: var(--panel);
}
.fh .dates { display: flex; gap: 8px; }
.fh .day {
  width: 54px; border-radius: 9px; border: 1px solid var(--rule2);
  background: var(--surface); overflow: hidden; text-align: center;
}
.fh .day i {
  display: block; font: 500 9px/1 var(--sans); letter-spacing: .1em; font-style: normal;
  text-transform: uppercase; color: #fff; background: var(--brand); padding: 5px 0 4px;
}
.fh .day b {
  display: block; font: 600 22px/1 var(--display); letter-spacing: -.02em;
  color: var(--ink); padding: 8px 0 7px; font-variant-numeric: tabular-nums;
}
.fh .dsaid { font: 12px/1.4 var(--sans); color: var(--ink2); }

/* ---- the crew --------------------------------------------------------- */
.fh .crew { border: 1px solid var(--rule); border-radius: 12px; overflow: hidden; background: var(--surface3); }
.fh .mate {
  display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
  padding: 13px 16px; border-bottom: 1px solid var(--rule); flex-wrap: wrap;
}
.fh .mate:last-child { border-bottom: 0; }
.fh .mate .mn b { font: 500 14px/1.35 var(--sans); color: var(--ink); }
.fh .mate .mn span { display: block; font: 12px/1.4 var(--sans); color: var(--ink3); margin-top: 2px; }
.fh .mate .reach { display: flex; gap: 16px; flex-wrap: wrap; }
.fh .mate .reach a {
  font: 13px/1.4 var(--sans); color: var(--brand-ink); text-decoration: none; overflow-wrap: anywhere;
}
.fh .mate .reach a:hover { text-decoration: underline; }
.fh .empty { padding: 8px 0; }

/* Three across becomes one down before a mosaic turns into four smudges. */
@media (max-width: 760px) {
  .fh .tools { grid-template-columns: 1fr; }
  .fh .lead { gap: 16px; padding: 16px 18px; }
  .fh .lead .lcs { gap: 20px; }
  .fh .lead .lgo { margin-left: 0; width: 100%; }
}

@media (max-width: 720px) {
  .fh .shelf, .fh .filmhead, .fh .filmbody { padding-left: 16px; padding-right: 16px; }
  .fh .fh-bar { padding: 12px 16px; }
  .fh .fh-title { font-size: 28px; }
  .fh .facts { gap: 16px 26px; }
  .fh .mate { flex-direction: column; align-items: flex-start; gap: 8px; }
  .fh .mate .reach { gap: 14px; }
}

/* ---- the camera spec sheet, and setting the film's photograph ---------- */
/* A lens list is a sentence, not a value — "Principle: Tribe7 Blackwings
   T-Tuned // Pickups: Zeiss Superspeed" put in a strip of short facts wrecks
   the row, so it gets a line of its own under them. */
.fh .lenses { margin-top: 18px; }
.fh .lenses span {
  display: block; font: 500 10px/1 var(--sans); letter-spacing: .085em;
  text-transform: uppercase; color: var(--ink3);
}
.fh .lenses b { display: block; font: 500 14px/1.5 var(--sans); color: var(--ink); margin-top: 6px; max-width: 70ch; }

.fh .picks { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; }
.fh .picks form { margin: 0; display: block; }
.fh .shotpick {
  display: block; width: 100%; padding: 0; cursor: pointer; overflow: hidden;
  background: var(--frame-empty); border: 1px solid var(--rule); border-radius: 9px;
  aspect-ratio: 16 / 9;
}
.fh .shotpick img { width: 100%; height: 100%; object-fit: cover; display: block; }
.fh .shotpick:hover { border-color: var(--brand-ink); }
.fh .shotpick.on { border-color: var(--brand-ink); box-shadow: 0 0 0 2px var(--brand-tint); }
.fh .inline { display: inline; margin: 0; }
.fh .linkish {
  background: none; border: 0; padding: 0; cursor: pointer;
  font: 11px/1 var(--sans); color: var(--brand-ink); letter-spacing: .02em; text-transform: none;
}
.fh .linkish:hover { text-decoration: underline; }
/* When the page was last brought up to date. Quiet, and at the foot, because
   it is about the page rather than about the film — but it is the answer to
   "did pressing that do anything", which otherwise needs a Terminal window. */
.fh .stamp { font: 11px/1 var(--sans); color: var(--ink4); padding-top: 6px; }

/* ======================================================================
 * SHARE — a panel over the film, in the app's own hand.
 *
 * Its measurements are Theme.swift's by way of the tokens above: an
 * 18-point card, 9-point rows, the ink ramp doing the hierarchy rather
 * than weight, small tracked capitals for a section. Every mark in it is
 * a stroked SVG in currentColor — no emoji, which arrive in whatever
 * colour and weight the system feels like and match nothing.
 *
 * Two shapes, one card: `.shroud` floats it over whatever you were
 * looking at, and `.sharepage` is the same card at its own address, for
 * a browser with no JavaScript and for a link somebody sends.
 * ====================================================================== */
.shroud {
  position: fixed; inset: 0; z-index: 90; display: flex;
  align-items: flex-start; justify-content: center; padding: 8vh 20px 40px;
  background: rgba(0,0,0,.34); backdrop-filter: blur(3px);
  animation: shroudin .12s ease-out;
}
@keyframes shroudin { from { opacity: 0 } to { opacity: 1 } }
.sharepage {
  min-height: 100%; background: var(--ground); padding: 40px 20px;
  display: flex; align-items: flex-start; justify-content: center;
}
.sheetcard {
  width: 100%; max-width: 520px; background: var(--surface);
  border: 1px solid var(--glass-line); border-radius: 18px; padding: 22px 22px 16px;
  box-shadow: var(--shadow-win); max-height: 84vh; overflow-y: auto;
}
.sharepage .sheetcard { box-shadow: 0 1px 3px rgba(0,0,0,.10), 0 24px 48px -24px rgba(0,0,0,.28); }

.sheetcard .sheethead { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 16px; }
.sheetcard .sheethead h1 {
  flex: 1; min-width: 0; margin: 0; letter-spacing: -.012em;
  font: 500 17px/1.35 var(--sans); color: var(--ink);
}
.sheetcard .mk { display: block; flex: none; }
.iconbtn {
  width: 26px; height: 26px; flex: none; display: grid; place-items: center;
  border: 0; border-radius: 7px; background: transparent; color: var(--ink3); cursor: pointer;
}
.iconbtn:hover { background: var(--row-hover); color: var(--ink); }

.sheetcard .addrow { display: flex; gap: 7px; align-items: center; }
.sheetcard .addrow .field { flex: 1; min-width: 0; }
.sheetcard .field {
  padding: 8px 10px; border: 1px solid var(--rule2); border-radius: 9px;
  color: var(--ink); font: 13px/1.3 var(--sans); background: var(--surface);
}
.sheetcard .field:focus { outline: none; border-color: var(--brand-line); box-shadow: 0 0 0 3px var(--brand-tint); }
.sheetcard .field:disabled { color: var(--ink4); background: var(--surface3); }
.pick {
  font: 12px/1 var(--sans); color: var(--ink); background: var(--surface2);
  border: 1px solid var(--rule2); border-radius: 9px; padding: 8px 9px; cursor: pointer;
}
.pick:hover { background: var(--row-hover); }
.pick.plain {
  border-color: transparent; background: transparent; padding: 1px 0;
  font: 500 14px/1.3 var(--sans); cursor: pointer;
}
.pick.plain:hover { background: transparent; color: var(--brand-ink); }
.pick:disabled { color: var(--ink4); cursor: default; background: transparent; border-color: transparent; }
.sheetcard .primary {
  width: auto; margin: 0; padding: 9px 15px; border: 0; border-radius: 9px; cursor: pointer;
  background: var(--brand); color: #fff; font: 500 13px/1 var(--sans);
}
.sheetcard .primary:hover { filter: brightness(1.08); }
.sheetcard a.primary.done { text-decoration: none; display: inline-block; }
.sheetcard .btn {
  display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
  font: 500 12px/1 var(--sans); color: var(--ink2);
  border: 1px solid var(--rule2); border-radius: 9px; padding: 8px 11px; background: transparent;
}
.sheetcard .btn:hover { border-color: var(--brand-line); color: var(--brand-ink); background: var(--brand-tint); }
.sheetcard .btn.wide { border-radius: 20px; padding: 8px 14px; }

.sheetcard .sect {
  font: 500 10px/1 var(--sans); letter-spacing: .09em; text-transform: uppercase;
  color: var(--ink3); margin: 22px 0 6px;
}
.sheetcard .rows { display: flex; flex-direction: column; }
.prow {
  display: flex; align-items: center; gap: 11px;
  padding: 7px 8px; margin: 0 -8px; border-radius: 9px;
}
.prow:hover { background: var(--row-hover); }
.prow .avatar.big {
  width: 28px; height: 28px; flex: none; border-radius: 50%; display: grid; place-items: center;
  background: var(--sel-quiet); color: var(--ink2); font: 500 10px/1 var(--sans);
}
.prow .avatar.quiet { background: transparent; border: 1px solid var(--rule2); color: var(--ink3); }
.prow .pwho { flex: 1; min-width: 0; }
.prow .pwho b { display: block; font: 400 13px/1.4 var(--sans); color: var(--ink); overflow-wrap: anywhere; }
.prow .pwho .note { display: block; font: 11px/1.4 var(--sans); color: var(--ink3); }
.ownerpill { font: 12px/1 var(--sans); color: var(--ink3); padding-right: 6px; }

.gen { display: flex; align-items: center; gap: 11px; padding: 4px 0 2px; }
.genmark {
  width: 30px; height: 30px; flex: none; border-radius: 50%; display: grid; place-items: center;
  background: var(--sel-quiet); color: var(--ink2);
}
.genmark.on { background: var(--brand-tint); color: var(--brand-ink); }
.gen .genwhat { flex: 1; min-width: 0; }
.gen .sub { font: 12px/1.45 var(--sans); color: var(--ink3); margin-top: 2px; }

.genmore {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  margin: 10px 0 0; padding: 10px 12px; border-radius: 11px; background: var(--surface3);
}
.genmore .field.slim { width: 150px; padding: 6px 9px; font-size: 12px; }
.tick { display: inline-flex; align-items: center; gap: 6px; font: 12px/1 var(--sans); color: var(--ink2); }

.sheetcard .sheetfoot { display: flex; align-items: center; gap: 10px; margin-top: 20px; }
.sheetcard .sheetfoot .spacer { flex: 1; }
.sheetcard .saying { margin: 14px 0 0; font: 12px/1.5 var(--sans); color: var(--ink2); overflow-wrap: anywhere; }
.sheetcard .saying.bad { color: var(--danger); }
.sheetcard .empty { padding: 10px 0; color: var(--ink3); font-size: 13px; }

/* Share, in the chrome. The padlock and the globe are the two states: shut, or
   out in the world — worth seeing at a glance rather than going to check. */
.sharebtn {
  display: inline-flex; align-items: center; gap: 7px; text-decoration: none; cursor: pointer;
  font: 500 12px/1 var(--sans); color: var(--brand-ink); white-space: nowrap;
  background: var(--brand-tint); border: 1px solid var(--brand-line);
  border-radius: 20px; padding: 6px 12px;
}
.sharebtn:hover { border-color: var(--brand-ink); }
.sharebtn svg { display: block; }
.topbar .sharebtn { color: var(--accent); background: var(--accent-tint); border-color: var(--accent-line); }
.topbar .sharebtn:hover { border-color: var(--accent); }

@media (max-width: 700px), (max-height: 480px) and (pointer: coarse) {
  /* **Who may follow the link goes on its own line.** Three things across one
     row — the padlock, what the link does, and the role it hands out — leaves
     the middle one four words wide, and its own sentence set in a column of
     three. The select drops under it and lines up with the words above. */
  .gen { flex-wrap: wrap; align-items: flex-start; row-gap: 8px; }
  .gen .genmark { margin-top: 1px; }
  .gen > .pick { flex: 1 0 100%; margin-left: 41px; }
  .genmore .field.slim { width: auto; flex: 1 1 140px; }
}

@media (max-width: 560px) {
  .shroud { padding: 0; }
  .shroud .sheetcard { border-radius: 0; max-width: none; max-height: 100vh; min-height: 100vh; }
  .sharepage { padding: 0; }
  .sharepage .sheetcard { border: 0; border-radius: 0; min-height: 100vh; box-shadow: none; }
  .sheetcard .addrow { flex-wrap: wrap; }
  .sheetcard .addrow .field { flex: 1 1 100%; }
}

/* ======================================================================
 * A PHONE — the shape the app takes on a small screen.
 *
 * **Scoped entirely under `.app`**, for the reason the front-of-house block
 * above is scoped under `.fh`: `.facts`, `.count`, `.doc` and `.no` all mean
 * two different things in this file, and an unscoped rule here would reach
 * into the foyer. Nothing the front of house draws is inside an `.app`.
 *
 * The cues are the iPhone app's, because the iPhone app *is* Shotlister on a
 * phone and the Mac's window shrunk is not:
 *
 *   - the four modes are a **tab bar along the foot** (`ProjectView`'s
 *     TabView), where a thumb reaches them, and not pills in the top bar;
 *   - a list is a **screen you open**, over the work, and picking out of it
 *     puts it away — `ShotListView` pushing to a scene;
 *   - a shot is a **row with its frame at the left** (`ShotRow`), not a line
 *     across eleven columns.
 *
 * 700px is the sidebar's 258 plus the panel's 300: below it the three cannot
 * stand beside each other without the middle becoming a gutter. The second
 * half is **a phone on its side**: 852 across and 393 tall, which no width
 * test catches — a short window being touched, which `pointer: coarse` keeps
 * a short desktop window out of. It must match `PHONE` in `film.js`, which
 * decides the same thing in markup.
 * ====================================================================== */

@media (max-width: 700px), (max-height: 480px) and (pointer: coarse) {
  /* ---- the bar across the top ------------------------------------------ */
  /* One row that never wraps. The film's name was breaking into three lines
     and pushing everything to the right of it off the screen. */
  .app .topbar { gap: 8px; padding: 8px 10px; flex-wrap: nowrap; }
  .app .topbar .film {
    min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font-size: 15px;
  }
  /* Touchable, not pointable: 26×22 is a mouse target. */
  .app .topbar .icon, .app .viewbar .icon { width: 34px; height: 34px; }
  /* A 20px radius on a 34px square is a circle; the share control is a button
     like the two beside it, so it takes their corner. */
  .app .sharebtn { padding: 0; width: 34px; height: 34px; justify-content: center; border-radius: 9px; }

  /* ---- the tab bar along the foot -------------------------------------- */
  /* Above the home indicator on an iPhone, and flush to the bottom anywhere
     else — `env()` is nought when there is no inset to answer. */
  .app .tabbar {
    display: flex; flex: none; border-top: 1px solid var(--rule);
    background: var(--surface2); padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  .app .tab {
    flex: 1; min-width: 0; display: flex; flex-direction: column;
    align-items: center; justify-content: center; gap: 3px;
    padding: 7px 2px 6px; border: 0; background: transparent; cursor: pointer;
    color: var(--ink3); -webkit-tap-highlight-color: transparent;
  }
  .app .tab span {
    font: 500 10px/1.2 var(--sans); letter-spacing: .005em;
    max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .app .tab.on { color: var(--accent); }

  /* ---- a scene's band -------------------------------------------------- */
  .app .sceneband { padding: 10px 12px 9px; }
  /* The heading wraps under itself; the number and the count keep their places
     at either end of the first line, which is where the app has them. */
  .app .sceneband .top { align-items: flex-start; }
  .app .sceneband .num { flex: none; line-height: 1.25; }
  .app .sceneband .slug { flex: 1; min-width: 0; line-height: 1.25; }
  .app .sceneband .count, .app .lanehead .count, .app .scene-row .count { white-space: nowrap; }
  /* The synopsis and the place ran side by side off the right edge — 34ch of
     each on a screen 28ch wide — so they take a line each. */
  .app .sceneband .strip { flex-wrap: wrap; gap: 4px 14px; }
  /* **Three lines is a glance**, which is the iPhone's own rule for the line
     under a shot and the right one for a synopsis at the head of a scene
     somebody scrolls past twenty times.
     `-webkit-box` rather than the inline-flex a `.bandchip` is on a Mac, for
     two reasons: line clamping is the one thing a flex box cannot do, and
     `text-overflow` never applied to an inline-flex either — so the tail that
     ran off the edge was simply gone, with nothing to say it had been. */
  .app .sceneband .bandchip {
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3;
    max-width: 100%; white-space: normal; line-height: 1.4;
  }
  .app .sceneband .bandchip i { margin-right: 6px; }

  /* ---- the views bar --------------------------------------------------- */
  /* Two rows, on purpose and not by accident: the controls across the top, and
     what they add up to underneath. Five things in one row on a 375px screen
     pushed the count and the script toggle off the right edge. */
  .app .viewbar { padding: 6px 10px; flex-wrap: wrap; row-gap: 6px; }
  .app .viewbar .lab { font-size: 11px; }
  .app .seg { flex: 1; min-width: 0; }
  .app .seg button { flex: 1; min-width: 0; padding: 7px 6px; }
  .app .varow { flex-basis: 100%; display: flex; align-items: center; gap: 8px; }

  /* ---- a list is a sheet over the work --------------------------------- */
  .app .scrim {
    position: absolute; inset: 0; z-index: 8;
    background: rgba(0,0,0,.34); -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
  }
  /* `.body` is the positioning ground, so a sheet covers the film and not the
     bar above it or the tabs below. */
  .app .body { position: relative; }
  .app .body > .sidebar, .app .body > .panelside {
    position: absolute; top: 0; bottom: 0; z-index: 9;
    width: min(310px, 86%); box-shadow: 0 0 34px rgba(0,0,0,.28);
  }
  .app .body > .sidebar { left: 0; }
  .app .body > .panelside { right: 0; width: min(340px, 92%); }
  .app .scene-row { padding: 11px 10px; font-size: 14px; }
  .app .sidefoot .find { padding: 10px 13px; font-size: 16px; }

  /* ---- a shot, the way the iPhone draws one ---------------------------- */
  /* The table's grid is a desktop thing: `film.js` draws `.lrow` instead
     below this width, and the well goes back to being an ordinary column. */
  .app .well.table { display: block; }
  .app .lrow {
    display: flex; gap: 11px; align-items: flex-start;
    padding: 11px 13px; border-bottom: 1px solid var(--rule);
  }
  .app .lrow.struck { opacity: .45; }
  /* The frame sets its own width; the words take what is left. */
  .app .lrow .lframe { width: 104px; flex: none; }
  .app .lrow .lsaid { flex: 1; min-width: 0; }
  /* The number reads as loud as the name, which is how the iPhone's row is
     ordered — it was the other way round and the numbers were lost in it. */
  .app .lrow .ltop { display: flex; align-items: baseline; gap: 7px; min-width: 0; }
  .app .lrow .ltop .no { font: 600 13px/1.3 var(--mono); color: var(--ink); flex: none; }
  .app .lrow .ltitle {
    font: 600 14px/1.3 var(--sans); color: var(--ink2);
    min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .app .lrow .prose {
    margin: 4px 0 0; font: 13px/1.45 var(--sans); color: var(--ink); white-space: pre-wrap;
  }
  .app .lrow .prose.note { color: var(--ink3); }
  .app .lrow .lchips { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 7px; }

  /* ---- storyboard ------------------------------------------------------ */
  .app .cards { grid-template-columns: 1fr; gap: 12px; padding: 12px; }

  /* ---- the document ---------------------------------------------------- */
  /* The desk keeps its margins but they are thin ones; the number comes in off
     its 34px shelf, which on this width is a tenth of the page. */
  .app .desk { padding: 12px 0 40px; }
  .app .paper { padding: 20px 18px; }
  .app .docshot { gap: 9px; margin-bottom: 20px; }
  .app .docshot > .no { width: auto; min-width: 26px; }
  .app .docshot .docbody .docframes { flex-wrap: wrap; }

  /* ---- a location's facts, under it ------------------------------------ */
  /* Taken apart by `locationsMode()` on this width — the address under the
     name, the scans and notes under the photographs — so neither half is a
     290px column any more. */
  .app .facts { width: auto; border-left: 0; }
  /* No ground of its own: it is a card standing on the page, not a panel. */
  .app .facts.address { padding: 0; margin: 12px 0 0; background: none; }
  .app .facts.address .fact { margin-bottom: 0; }
  .app .well > .facts.rest {
    border-top: 1px solid var(--rule); margin-top: 18px; padding: 16px 18px 24px;
  }
  .app .detail { padding: 14px 16px 26px; }
  .app .detailhead h1 { font-size: 21px; }
  .app .wall.big { grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); }

  /* ---- the schedule ---------------------------------------------------- */
  .app .sheetwrap { padding: 12px 12px 20px; }
  .app .lane { padding: 11px 12px; }
  .app .stripe { padding: 10px 9px; }

  /* ---- the pop-out ----------------------------------------------------- */
  .app .popout { padding: 12px; }
  .app .popbar { gap: 9px; margin-bottom: 10px; }
  .app .popbar .poptitle { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .app .popbar .lab { white-space: nowrap; }
  .app .popbar .icon { width: 36px; height: 36px; }
}

/* **Every field is 16px on a phone, and only for one reason.** iOS Safari zooms
   the whole page in when a field smaller than that takes focus, and does not
   zoom back out — so tapping the scene search left somebody pinching their way
   back to the film. Said here rather than field by field, because it is a rule
   about the browser and not about any one of them. */
@media (max-width: 700px), (max-height: 480px) and (pointer: coarse) {
  .find, .field, .sheetcard .field, .app .find,
  input[type="search"], input[type="text"], input[type="email"],
  input[type="password"], textarea { font-size: 16px; }
  .find { max-width: none; }
}

/* A pointer that is a finger has no hover, and a row that lights up under one
   that never moves stays lit — which reads as selected. */
@media (hover: none) {
  .app .trow:hover, .app .lrow:hover, .app .shot:hover,
  .app .scene-row:hover, .app .stripe:hover { background: transparent; }
  /* Background only — the ink is how `.lit` says a panel is open, and taking
     that too would put the toggle out the moment it was pressed. */
  .app .icon:hover { background: transparent; }
}
