/* ============================================================
   STRIPBOARD — application layer
   ------------------------------------------------------------
   Load order (see index.html):

     1. css/brand.css       Slatehub brand foundation — shipped
                            unmodified from Design. Owns the --sh-*
                            tokens, the three type faces and the
                            .sh-display / .sh-mono / .sh-grain
                            utilities.
     2. css/stripboard.css  The Stripboard design system — shipped
                            unmodified from Design. Owns the --sb-*
                            tokens and the .sb-* component classes
                            (see docs/HANDOFF.md).
     3. css/app.css         This file.

   Neither of the first two files is edited here. Both can be
   re-exported from Design and dropped in wholesale; everything
   app-specific lives below, so a re-sync can never clobber app work.

   WHAT THIS FILE IS FOR
   ---------------------
   The design system describes components (.sb-card, .sb-row,
   .sb-status …). This app's markup is generated in app.js and uses
   its own long-standing semantic class names (.panel, .location-card,
   .day …), which double as the hooks JS and event delegation key off.
   Rather than rename ~360 className assignments — churn with real
   risk and no user-visible benefit — this file implements those
   classes *in terms of the design system's tokens*.

   The rule, without exception: every value below is a --sh-* or
   --sb-* token, or a value derived from one and named in §1. No
   hard-coded hex, no magic rgba. If a colour is needed that the
   system has no word for, it is added in §1 as a documented
   extension, not inlined at the call site.

   Section order mirrors the app's tab order so a component is where
   you would look for it.

     1  Extensions & theme
     2  Base, page shell, layout
     3  Header
     4  Tab bar
     5  Panels & section heads
     6  Buttons
     7  Fields & forms
     8  Status pills & selects
     9  Rows & list primitives
    10  Availability — people list
    11  Availability — blocked dates & window finder
    12  Calendar, day details & events
    13  Roles & characters (casting pipeline)
    14  Cast and crew
    15  Task list
    16  Locations
    17  Scenes & shot list
    18  Expenses
    19  Budget & investments
    20  Admin
    21  Projects hub, auth & onboarding
    22  Modals, toast, menus, autocomplete
    23  Screenplay
    24  Responsive
    25  Print
    26  Reduced motion
   ============================================================ */


/* ============================================================
   1. EXTENSIONS & THEME
   ============================================================ */

/* ------------------------------------------------------------
   1.1 Status palette

   The system ships three state colours — sage (ok), ochre (warn)
   and vermillion (busy/destructive). The app carries more states
   than that: locations run to-contact → contacted → negotiating →
   accepted → rejected, expenses can be withdrawn, investments
   cancelled, and several things are simply "not set yet".

   Rather than invent hues, every state below is expressed with
   values the reference renders already use: the sage / ochre /
   neutral-cream / dim quartet from their status-pill maps, plus
   vermillion for rejection (the same triplet .sb-btn-danger uses).
   Each state is a text colour, a border and a fill, so one mixin-
   shaped rule set in §8 can paint pills, selects and row bars.
   ------------------------------------------------------------ */
:root {
  /* neutral — "set, but nothing has happened yet" (To be contacted) */
  --sb-neutral-text: var(--sb-ink-2);
  --sb-neutral-line: rgba(232, 226, 208, 0.18);
  --sb-neutral-fill: rgba(232, 226, 208, 0.04);

  /* idle — "not set at all"; one step quieter than neutral */
  --sb-idle-text: var(--sb-ink-4);
  --sb-idle-line: rgba(232, 226, 208, 0.12);
  --sb-idle-fill: transparent;

  /* ok — free / accepted / found / refunded / done            (sage)  */
  --sb-ok-line: rgba(159, 179, 122, 0.35);
  --sb-ok-fill: rgba(159, 179, 122, 0.10);
  --sb-ok-fill-strong: rgba(159, 179, 122, 0.18);

  /* warn — contacted / negotiating / in progress / planned   (ochre)  */
  --sb-warn-line: rgba(217, 164, 65, 0.38);
  --sb-warn-fill: rgba(217, 164, 65, 0.10);

  /* danger — rejected / cancelled / overdue             (vermillion)  */
  --sb-danger-text: var(--sh-red-glow);
  --sb-danger-line: rgba(232, 83, 53, 0.32);
  --sb-danger-fill: rgba(232, 83, 53, 0.07);
  --sb-danger-fill-strong: rgba(232, 83, 53, 0.18);

  /* Location priority, 1 (lowest) → 5 (highest). A five-step ramp
     from ochre to deep vermillion: the same "warm = urgent" reading
     the old palette had, now inside the brand's own two warm hues
     instead of alongside them. Used as a tint, never as flat fill. */
  --sb-pri-1: #D9A441;
  --sb-pri-2: #E08E3E;
  --sb-pri-3: #E5763A;
  --sb-pri-4: #E85335;
  --sb-pri-5: #B53A20;

  /* Hairline used inside cards (section heads, row separators).
     The system draws this inline in every card; named once here. */
  --sb-rule: rgba(232, 226, 208, 0.10);

  /* Hover border for rows and day cells. */
  --sb-line-hover: rgba(232, 226, 208, 0.34);

  /* Scrim behind modals. */
  --sb-scrim: rgba(10, 8, 7, 0.72);

  /* Drawn caret for select fields. The native one is a different shape,
     size and colour on every platform, and appearance:none removes it
     along with the platform's own background — so the control draws its
     own, in ink like every other glyph in a field. */
  --sb-caret: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path d='M1 1.6 6 6.4 11 1.6' fill='none' stroke='%239A958A' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");

  /* Set by app.js to the tab bar's real rendered height so sticky
     panel headers park directly beneath it (see §5). */
  --tab-bar-h: 44px;
}

/* ------------------------------------------------------------
   1.2 Light theme

   Dark is the product's default. The toggle in the header sets
   data-theme="light" on <html> (persisted in localStorage, app.js).

   The handoff lists light as an open item and notes that a second
   token set is enough, since every component reads tokens only.
   That is what this block is: the same design, on warm paper.

   Derivation, so it can be reviewed rather than guessed at:
   · ground        warm paper, the dark theme's #0F0D0B inverted
                   around the same warm hue rather than a cold grey
   · glass         white-over-paper instead of black-over-black; the
                   1px top highlight becomes white, the drop shadow
                   loses most of its depth (deep shadows read as dirt
                   on light grounds)
   · ink           four steps, warm near-black down to a mid brown
   · vermillion    unchanged — it is the brand; only the *link* tone
                   moves, because #FF6B47 on paper is ~2.3:1
   · sage / ochre  fills stay, text tones darken to clear 4.5:1
   ------------------------------------------------------------ */
:root[data-theme="light"] {
  /* --- brand foundation overrides --- */
  --sh-bg: #EFEAE0;
  --sh-bg-2: #FBF8F2;
  --sh-cream: #1C1712;
  --sh-cream-dim: #6B6459;
  --sh-red-glow: #BE3A19;          /* link / accent text on paper */
  --sh-line: rgba(28, 23, 18, 0.14);

  /* --- ink --- */
  --sb-ink: #1C1712;
  --sb-ink-2: #3E3830;
  --sb-ink-3: #6B6459;
  --sb-ink-4: #7F786D;

  /* --- status text at light-ground contrast --- */
  --sb-ok-text: #4C6626;
  --sb-warn-text: #7A5410;
  --sb-danger-text: #BE3A19;

  /* --- glass --- */
  --sb-card-bg: linear-gradient(170deg, rgba(255, 255, 255, 0.92), rgba(250, 246, 238, 0.88));
  --sb-card-bg-solid: linear-gradient(170deg, rgba(255, 255, 255, 0.97), rgba(250, 246, 238, 0.94));
  --sb-card-border: 1px solid rgba(28, 23, 18, 0.12);
  --sb-card-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.85), 0 18px 40px -32px rgba(48, 34, 20, 0.45);
  --sb-row-bg: linear-gradient(160deg, rgba(28, 23, 18, 0.035), rgba(28, 23, 18, 0.012));
  --sb-row-border: 1px solid rgba(28, 23, 18, 0.10);
  --sb-row-border-hover: rgba(28, 23, 18, 0.26);
  --sb-field-bg: rgba(255, 255, 255, 0.75);
  --sb-field-border: 1px solid rgba(28, 23, 18, 0.16);

  /* --- extensions --- */
  --sb-neutral-text: var(--sb-ink-2);
  --sb-neutral-line: rgba(28, 23, 18, 0.18);
  --sb-neutral-fill: rgba(28, 23, 18, 0.04);
  --sb-idle-text: var(--sb-ink-4);
  --sb-idle-line: rgba(28, 23, 18, 0.12);
  --sb-ok-line: rgba(109, 133, 70, 0.45);
  --sb-ok-fill: rgba(159, 179, 122, 0.22);
  --sb-ok-fill-strong: rgba(159, 179, 122, 0.34);
  --sb-warn-line: rgba(163, 118, 26, 0.45);
  --sb-warn-fill: rgba(217, 164, 65, 0.22);
  --sb-danger-line: rgba(190, 58, 25, 0.35);
  --sb-danger-fill: rgba(232, 83, 53, 0.10);
  --sb-danger-fill-strong: rgba(232, 83, 53, 0.20);
  --sb-rule: rgba(28, 23, 18, 0.12);
  --sb-line-hover: rgba(28, 23, 18, 0.30);
  --sb-scrim: rgba(60, 48, 36, 0.38);
  --sb-caret: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path d='M1 1.6 6 6.4 11 1.6' fill='none' stroke='%236B6459' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
}


/* ============================================================
   2. BASE, PAGE SHELL, LAYOUT
   ============================================================ */

/* The system's page ground is a .sb-app element: warm black plus two
   very soft radial light sources, with the grain layer on top. This
   app has no such wrapper (body is the ground), and — unlike the
   single-screen reference renders — its pages scroll a long way. Both
   layers are therefore pinned to the viewport instead of the document:
   at document scale the -10% offset on the first light source would
   drift right off the top of a long tab, and the grain would tile down
   a 6000px column for no benefit. Fixed keeps the lighting reading as
   what it is — a light source behind the page, not a decoration on it. */
body {
  margin: 0;
  min-height: 100vh;
  background: var(--sh-bg);
  color: var(--sb-ink);
  font-family: var(--sb-body);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow-wrap: break-word;
}
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(1200px 600px at 18% -10%, rgba(232, 83, 53, 0.09), transparent 60%),
    radial-gradient(900px 500px at 92% 8%, rgba(159, 179, 122, 0.05), transparent 65%);
}
/* Same two light sources, dialled right down: on paper the additive
   tints that read as "warm glow" on black read as "stain" instead. */
:root[data-theme="light"] body::before {
  background:
    radial-gradient(1200px 600px at 18% -10%, rgba(232, 83, 53, 0.055), transparent 60%),
    radial-gradient(900px 500px at 92% 8%, rgba(159, 179, 122, 0.05), transparent 65%);
}

/* .sb-grain carries brand.css's .sh-grain texture; see index.html. */
.sb-grain {
  position: fixed;
  z-index: 0;
}
/* The overlay blend brightens what is under it, which is right on warm
   black and wrong on paper — light mode gets a fainter, plain overlay. */
:root[data-theme="light"] .sb-grain { opacity: 0.22; }
:root[data-theme="light"] .sb-grain::after { mix-blend-mode: multiply; opacity: 0.10; }

html { overflow-x: hidden; }
* { box-sizing: border-box; }

a { color: var(--sh-red-glow); text-decoration: none; }
a:hover { color: var(--sh-red); text-decoration: underline; }
::selection { background: rgba(232, 83, 53, 0.35); }
button { font: inherit; }
:focus-visible { outline: 2px solid var(--sh-red); outline-offset: 2px; }

/* Content column. .sb-page in the system; .app is this app's name for it. */
.app {
  position: relative;
  z-index: 1;
  max-width: var(--sb-page-max);
  margin: 0 auto;
  padding: 36px 40px 64px;
}

/* Sidebar + main split — .sb-split in the system. */
.layout {
  display: grid;
  grid-template-columns: minmax(0, 340px) minmax(0, 1fr);
  gap: 26px;
  align-items: start;
  margin-top: 0;
}
.sidebar { min-width: 0; display: flex; flex-direction: column; gap: var(--sb-gap); }
.main { min-width: 0; display: flex; flex-direction: column; gap: var(--sb-gap); }

/* The mono utility the app applies to individual elements. */
.mono {
  font-family: var(--sh-mono);
  letter-spacing: 0.02em;
}


/* ============================================================
   3. HEADER

   One aligned row (handoff §4.1): slate mark · wordmark · hairline ·
   mono strapline, then account controls hard right. The old
   three-line block with its tagline paragraph is gone.
   ============================================================ */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 32px;
  flex-wrap: wrap;
  margin: 0;
  padding: 0 0 22px;
  border-bottom: 1px solid var(--sh-line);
}
.brand { display: flex; align-items: center; gap: 16px; min-width: 0; }

/* The mark: a slate's clapper bar, not a pictogram. */
.brand-mark {
  width: 10px;
  height: 34px;
  flex-shrink: 0;
  border-radius: 3px;
  background: var(--sh-red);
}
.brand h1 {
  font-family: var(--sh-display);
  text-transform: uppercase;
  letter-spacing: -0.02em;
  font-size: 30px;
  line-height: 1;
  margin: 0;
  color: var(--sb-ink);
}
/* Vertical hairline between wordmark and strapline. */
.brand-divider {
  width: 1px;
  height: 22px;
  flex-shrink: 0;
  background: rgba(232, 226, 208, 0.18);
}
:root[data-theme="light"] .brand-divider { background: rgba(28, 23, 18, 0.18); }

/* The project's name is the strapline: it is what this board is for. */
.project-name-label {
  font-family: var(--sh-mono);
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--sb-ink-3);
  margin: 0;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.topbar-right { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.whoami {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  flex-wrap: wrap;
  font-family: var(--sh-mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--sb-ink-3);
}
.whoami .email { color: var(--sb-ink-3); overflow-wrap: anywhere; }
.admin-badge + .link-btn { margin-left: 2px; }

/* Sign out — the system's .sb-link-btn: a vermillion outline pill, the
   one control in the header that ends the session. */
.link-btn {
  font-family: var(--sh-mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--sh-red-glow);
  padding: 9px 14px;
  border-radius: var(--sb-r-pill);
  border: 1px solid var(--sb-danger-line);
  background: var(--sb-danger-fill);
  backdrop-filter: var(--sb-blur-sm);
  cursor: pointer;
  transition: background var(--sb-fast) ease, border-color var(--sb-fast) ease;
}
.link-btn:hover { background: var(--sb-danger-fill-strong); border-color: rgba(232, 83, 53, 0.55); }

/* Role chip — COORDINATOR / OWNER. .sb-chip in the system. */
.admin-badge {
  display: inline-block;
  padding: 5px 11px;
  border-radius: var(--sb-r-pill);
  border: 1px solid rgba(232, 226, 208, 0.22);
  background: rgba(232, 226, 208, 0.06);
  font-family: var(--sh-mono);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--sb-ink);
}
:root[data-theme="light"] .admin-badge {
  border-color: rgba(28, 23, 18, 0.22);
  background: rgba(28, 23, 18, 0.05);
}

/* Account avatar → Bin menu. Sized to match the theme toggle beside it. */
.profile-menu-wrap { position: relative; flex-shrink: 0; }
.profile-avatar-btn {
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  padding: 0;
  border-radius: 50%;
  border: none;
  background: var(--sh-red);
  color: var(--sh-bg);
  font-family: var(--sh-display);
  font-size: 16px;
  cursor: pointer;
  transition: background var(--sb-fast) ease;
}
.profile-avatar-btn:hover { background: var(--sh-red-glow); }

/* Theme toggle — .sb-icon-btn. Lives in the header row, not pinned to
   the corner: with a translucent card system a floating button ends up
   sitting on top of content it has nothing to do with. */
.theme-toggle {
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  flex-shrink: 0;
  border-radius: var(--sb-r-pill);
  border: 1px solid rgba(232, 226, 208, 0.14);
  background: linear-gradient(160deg, rgba(232, 226, 208, 0.10), rgba(232, 226, 208, 0.03));
  backdrop-filter: blur(14px);
  box-shadow: inset 0 1px 0 rgba(232, 226, 208, 0.16);
  color: var(--sb-ink);
  cursor: pointer;
  transition: border-color var(--sb-fast) ease;
}
.theme-toggle:hover { border-color: rgba(232, 226, 208, 0.3); }
/* On paper a white chip with a hairline edge all but disappears; it
   needs a real edge and a little lift to stay a button. */
:root[data-theme="light"] .theme-toggle {
  border-color: rgba(28, 23, 18, 0.20);
  background: linear-gradient(160deg, #FFFFFF, rgba(255, 255, 255, 0.72));
  box-shadow: 0 1px 2px rgba(48, 34, 20, 0.12);
}
:root[data-theme="light"] .theme-toggle:hover { border-color: rgba(28, 23, 18, 0.3); }


/* ============================================================
   4. TAB BAR
   ============================================================ */
/* Sticky, so the seven tabs stay reachable in a long list — the design
   doesn't specify it, the app has always had it, and the sticky offset
   is what --tab-bar-h (§5) is measured from. It needs an opaque
   backdrop of its own, since the page behind it is now translucent. */
.tab-bar {
  position: sticky;
  top: 0;
  z-index: 6;
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  margin: 0 0 26px;
  padding-top: 18px;
  border-bottom: 1px solid var(--sb-rule);
  background: var(--sh-bg);
}
.tab-btn {
  flex-shrink: 0;
  white-space: nowrap;
  font-family: var(--sh-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--sb-ink-4);
  padding: 14px 16px;
  margin-bottom: -1px;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease;
}
.tab-btn:hover { color: var(--sb-ink); }
.tab-btn.active { color: var(--sb-ink); border-bottom-color: var(--sh-red); }


/* ============================================================
   5. PANELS & SECTION HEADS
   ============================================================ */
/* .panel is the system's standard glass card (.sb-card). */
.panel {
  position: relative;
  border-radius: var(--sb-r-card);
  border: var(--sb-card-border);
  background: var(--sb-card-bg);
  backdrop-filter: var(--sb-blur);
  box-shadow: var(--sb-card-shadow);
  padding: 22px 22px 24px;
  margin: 0 0 var(--sb-gap);
}
/* Inside a flex/grid stack the gap does the spacing, so the trailing
   margin would double it. */
.layout .panel:last-child,
.main > .panel:last-child,
.sidebar > .panel:last-child { margin-bottom: 0; }

/* Section head — mono micro-label · hairline · count. The hairline is
   generated rather than marked up, so every existing <h2> gets one for
   free. In a .panel-head-row the h2 grows and the rule runs up to
   whatever sits on the right (usually an "+ Add" button). */
.panel h2 {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 16px;
  padding: 0 4px;
  font-family: var(--sh-mono);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--sb-ink-4);
}
.panel h2::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--sb-rule);
}
.panel-head-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
}
.panel-head-row h2 { flex: 1; margin: 0; }
.panel-head-row > :not(h2) { flex-shrink: 0; }

/* Counts and one-line descriptions sitting beside a head. */
.panel-sub {
  font-size: 13px;
  color: var(--sb-ink-3);
}
.panel-head-row .panel-sub {
  font-family: var(--sh-mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* Feature panel — the "add something" cards at the top of a tab.
   Display type, a sentence of explanation, no hairline: the design
   gives these the loudest voice on the page (handoff §3). */
.panel--feature h2 {
  font-family: var(--sh-display);
  font-size: 21px;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  line-height: 1;
  color: var(--sb-ink);
  padding: 0;
  margin-bottom: 8px;
}
/* Title and its one-line explanation share a baseline row, the way the
   reference renders set "Block your unavailable dates — Nobody can
   schedule you inside a blocked range." */
.feature-head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 18px;
}
.feature-head h2 { margin-bottom: 0; }
.feature-head p { margin: 0; font-size: 13px; color: var(--sb-ink-3); }
.panel--feature h2::after { display: none; }
.panel--feature .panel-sub {
  display: block;
  max-width: 620px;
  margin-bottom: 18px;
  font-size: 13.5px;
  line-height: 1.5;
}
.panel--feature .panel-head-row { margin-bottom: 8px; }

/* Pins a panel's header (and its "+ Add" control) below the tab bar
   while a long list scrolls underneath. Opaque, because the panel's
   own glass would let the scrolling list show through it. */
.panel-sticky-header {
  position: sticky;
  top: var(--tab-bar-h, 44px);
  z-index: 5;
  margin: -22px -22px 16px;
  padding: 22px 22px 14px;
  border-bottom: 1px solid var(--sb-rule);
  border-radius: var(--sb-r-card) var(--sb-r-card) 0 0;
  background: var(--sb-card-bg-solid);
  backdrop-filter: var(--sb-blur);
}
/* The cast & crew list is the narrowest column in the app; it gets the
   card's side padding back (the design system's own list card uses 18px
   here for the same reason). */
.panel--people { padding-left: 18px; padding-right: 18px; }

.panel-sticky-header .panel-head-row:last-child,
.panel-sticky-header .search-box:last-child { margin-bottom: 0; }


/* ============================================================
   6. BUTTONS

   Four shapes, mapped onto the system:
     .btn            neutral secondary   → .sb-btn-ghost
     .btn.btn-accent primary CTA         → .sb-btn (flat vermillion)
     .btn.btn-danger destructive         → .sb-btn-danger
     .icon-btn       square icon control → .sb-icon-btn--sq
   .btn-mini is a size modifier and composes with all of them.
   ============================================================ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: var(--sh-mono);
  font-size: 10.5px;
  font-weight: 400;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--sb-ink);
  padding: 11px 18px;
  border-radius: 10px;
  border: 1px solid rgba(232, 226, 208, 0.16);
  background: rgba(232, 226, 208, 0.05);
  cursor: pointer;
  transition: border-color var(--sb-fast) ease, background var(--sb-fast) ease, color var(--sb-fast) ease;
}
.btn:hover { border-color: rgba(232, 226, 208, 0.36); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }
.btn:disabled:hover { border-color: rgba(232, 226, 208, 0.16); }
:root[data-theme="light"] .btn { border-color: rgba(28, 23, 18, 0.16); background: rgba(255, 255, 255, 0.7); }
:root[data-theme="light"] .btn:hover { border-color: rgba(28, 23, 18, 0.36); }

/* Primary. Flat red, no glow, no gradient sheen — deliberate, and an
   earlier glowing version was rejected (handoff §1). */
.btn-accent {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.08em;
  color: #fff;
  padding: 14px 24px;
  border: none;
  border-radius: var(--sb-r-pill);
  background: var(--sh-red);
}
.btn-accent:hover { background: var(--sh-red-glow); border: none; }
:root[data-theme="light"] .btn-accent { background: var(--sh-red); border: none; }
:root[data-theme="light"] .btn-accent:hover { background: var(--sh-red-deep); }

.btn-ghost { background: transparent; }
:root[data-theme="light"] .btn-ghost { background: transparent; }

.btn-danger {
  color: var(--sb-danger-text);
  border-color: var(--sb-danger-line);
  background: var(--sb-danger-fill);
}
.btn-danger:hover {
  background: var(--sb-danger-fill-strong);
  border-color: rgba(232, 83, 53, 0.6);
}

.btn-mini { font-size: 10px; letter-spacing: 0.1em; padding: 8px 13px; }
.btn-accent.btn-mini { font-size: 11px; padding: 11px 18px; }

/* Inline text action ("Claim this person", "See details →"). */
.btn-text {
  font-family: var(--sh-mono);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--sh-red-glow);
  background: none;
  border: none;
  padding: 2px 0;
  cursor: pointer;
}
.btn-text:hover { color: var(--sh-red); text-decoration: underline; }

/* Square icon control — edit pencils, ×, chevrons. */
.icon-btn {
  width: 30px;
  height: 30px;
  display: inline-grid;
  place-items: center;
  padding: 0;
  font-size: 13px;
  line-height: 1;
  color: var(--sb-ink-3);
  border-radius: 9px;
  border: 1px solid rgba(232, 226, 208, 0.12);
  background: rgba(232, 226, 208, 0.04);
  cursor: pointer;
  transition: border-color var(--sb-fast) ease, background var(--sb-fast) ease, color var(--sb-fast) ease;
}
.icon-btn:hover {
  color: var(--sb-danger-text);
  border-color: var(--sb-danger-line);
  background: var(--sb-danger-fill);
}
/* The edit variant is not destructive, so it must not glow red. */
.icon-btn.edit:hover {
  color: var(--sb-ink);
  border-color: var(--sb-line-hover);
  background: rgba(232, 226, 208, 0.08);
}
:root[data-theme="light"] .icon-btn { border-color: rgba(28, 23, 18, 0.12); background: rgba(28, 23, 18, 0.03); }
:root[data-theme="light"] .icon-btn.edit:hover { background: rgba(28, 23, 18, 0.06); }


/* ============================================================
   7. FIELDS & FORMS

   One recipe (.sb-input / .sb-select) for every text control in the
   app, listed explicitly rather than matched with a wildcard so it
   stays obvious what is being styled and nothing is caught by
   accident.
   ============================================================ */
.add-form input,
.add-form select,
.add-form textarea,
.search-box,
.edit-form input,
.edit-form select,
.edit-form textarea,
.modal-field input:not([type="checkbox"]),
.modal-field select,
.modal-field textarea,
.grant-form input,
.grant-form select,
.finder-form input,
.candidate-add-form input,
.location-body input,
.project-tile-form input,
.sp-sidebar-edit-row input,
.scene-quick-field select,
.location-plain-select {
  width: 100%;
  font-family: var(--sb-body);
  font-size: 13.5px;
  color: var(--sb-ink);
  padding: 12px 13px;
  border-radius: var(--sb-r-field);
  border: var(--sb-field-border);
  background: var(--sb-field-bg);
  outline: none;
  color-scheme: dark;
  transition: border-color var(--sb-fast) ease, box-shadow var(--sb-fast) ease;
}
:root[data-theme="light"] .add-form input,
:root[data-theme="light"] .add-form select,
:root[data-theme="light"] .add-form textarea,
:root[data-theme="light"] .search-box,
:root[data-theme="light"] .edit-form input,
:root[data-theme="light"] .edit-form select,
:root[data-theme="light"] .edit-form textarea,
:root[data-theme="light"] .modal-field input:not([type="checkbox"]),
:root[data-theme="light"] .modal-field select,
:root[data-theme="light"] .modal-field textarea,
:root[data-theme="light"] .grant-form input,
:root[data-theme="light"] .grant-form select,
:root[data-theme="light"] .finder-form input,
:root[data-theme="light"] .candidate-add-form input,
:root[data-theme="light"] .location-body input,
:root[data-theme="light"] .project-tile-form input,
:root[data-theme="light"] .sp-sidebar-edit-row input,
:root[data-theme="light"] .scene-quick-field select,
:root[data-theme="light"] .location-plain-select { color-scheme: light; }

.add-form input::placeholder,
.search-box::placeholder,
.edit-form input::placeholder,
.edit-form textarea::placeholder,
.modal-field input::placeholder,
.modal-field textarea::placeholder,
.finder-form input::placeholder,
.candidate-add-form input::placeholder,
.project-tile-form input::placeholder { color: var(--sb-ink-4); }

.add-form input:focus,
.add-form select:focus,
.add-form textarea:focus,
.search-box:focus,
.edit-form input:focus,
.edit-form select:focus,
.edit-form textarea:focus,
.modal-field input:focus,
.modal-field select:focus,
.modal-field textarea:focus,
.grant-form input:focus,
.grant-form select:focus,
.finder-form input:focus,
.candidate-add-form input:focus,
.location-body input:focus,
.project-tile-form input:focus,
.sp-sidebar-edit-row input:focus,
.scene-quick-field select:focus,
.location-plain-select:focus {
  border-color: rgba(232, 83, 53, 0.6);
  box-shadow: 0 0 0 3px rgba(232, 83, 53, 0.14);
}

/* Dates and amounts are data, not prose: mono, on the raised field
   fill the system reserves for them (.sb-input--mono). */
.range-form input[type="date"],
.finder-form input[type="date"],
.finder-form input[type="number"],
.modal-field input[type="date"],
.modal-field input[type="time"],
.modal-field input[type="number"],
.add-form input[type="date"],
.add-form input[type="number"],
.edit-form input[type="date"],
.edit-form input[type="number"] {
  width: 100%;
  font-family: var(--sh-mono);
  font-size: 14px;
  color: var(--sb-ink);
  padding: 13px 14px;
  border-radius: 13px;
  border: var(--sb-field-border);
  background: linear-gradient(160deg, rgba(232, 226, 208, 0.07), rgba(232, 226, 208, 0.02));
  box-shadow: inset 0 1px 0 rgba(232, 226, 208, 0.10);
  outline: none;
  color-scheme: dark;
}
:root[data-theme="light"] .range-form input[type="date"],
:root[data-theme="light"] .finder-form input[type="date"],
:root[data-theme="light"] .finder-form input[type="number"],
:root[data-theme="light"] .modal-field input[type="date"],
:root[data-theme="light"] .modal-field input[type="time"],
:root[data-theme="light"] .modal-field input[type="number"],
:root[data-theme="light"] .add-form input[type="date"],
:root[data-theme="light"] .add-form input[type="number"],
:root[data-theme="light"] .edit-form input[type="date"],
:root[data-theme="light"] .edit-form input[type="number"] {
  background: rgba(255, 255, 255, 0.8);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9);
  color-scheme: light;
}

/* Native date inputs render mm/dd/yyyy lowercase; uppercase matches
   the mono, all-caps treatment dates get everywhere else. */
input[type="date"] { text-transform: uppercase; }
input[type="date"]::-webkit-calendar-picker-indicator {
  filter: invert(0.85) sepia(0.2);
  opacity: 0.6;
  cursor: pointer;
}
input[type="date"]::-webkit-calendar-picker-indicator:hover { opacity: 1; }
:root[data-theme="light"] input[type="date"]::-webkit-calendar-picker-indicator { filter: none; }

textarea { resize: vertical; min-height: 52px; }

.add-form { display: flex; flex-direction: column; gap: 10px; }
.add-form-row { display: flex; gap: 10px; flex-wrap: wrap; }
.add-form-row > * { flex: 1; min-width: 120px; }
.add-form .btn { align-self: flex-start; }
.search-box { margin-bottom: 12px; }
.filter-toolbar { display: flex; gap: 8px; margin-bottom: 14px; }
.filter-toolbar .btn-text {
  flex: 1;
  text-align: center;
  color: var(--sb-ink-2);
  padding: 9px 10px;
  border-radius: 10px;
  border: 1px solid rgba(232, 226, 208, 0.14);
  background: rgba(232, 226, 208, 0.04);
  font-size: 10px;
  letter-spacing: 0.12em;
}
.filter-toolbar .btn-text:hover { color: var(--sb-ink); border-color: var(--sb-line-hover); text-decoration: none; }
:root[data-theme="light"] .filter-toolbar .btn-text { border-color: rgba(28, 23, 18, 0.14); background: rgba(28, 23, 18, 0.03); }

/* Micro-label above a control (.sb-label). */
.field-hint,
.block-label,
.date-field span,
.scene-quick-field .label,
.sp-element-picker-label,
.personal-sidebar-subhead,
.day-details-heading,
.candidates-heading,
.casting-entry-info-label,
.budget-stat-label,
.modal-field > span {
  display: block;
  font-family: var(--sh-mono);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--sb-ink-4);
}
.field-hint { letter-spacing: 0.12em; margin: 0; }
.block-label,
.personal-sidebar-subhead,
.day-details-heading,
.candidates-heading { margin: 22px 0 12px; }
.personal-sidebar-subhead,
.day-details-heading { display: flex; align-items: center; gap: 12px; text-align: left; }
.personal-sidebar-subhead::after,
.day-details-heading::after { content: ''; flex: 1; height: 1px; background: var(--sb-rule); }

/* Label + control pairs (.sb-field). */
.modal-field,
.date-field,
.scene-quick-field,
.finder-form label {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}
.modal-field { margin-bottom: 14px; }
.readonly-note { font-size: 12.5px; color: var(--sb-ink-3); margin-top: 10px; }

/* Inline edit form — opens inside an already-expanded row, so it sits
   one surface deeper than the row it belongs to. */
.edit-form {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 12px;
  padding: 14px;
  border-radius: var(--sb-r-field);
  border: var(--sb-row-border);
  background: rgba(232, 226, 208, 0.03);
}
:root[data-theme="light"] .edit-form { background: rgba(28, 23, 18, 0.03); }
.edit-form-actions { display: flex; gap: 8px; justify-content: flex-end; }
.claim-row { margin-top: 10px; }
.finder-event-btn { flex-shrink: 0; }


/* ============================================================
   8. STATUS PILLS & SELECTS

   Every status control in the app is the same pill (.sb-status) in
   one of five tones. The tones are set in §1.1; this section only
   decides which state wears which tone:

     ok      accepted · found · refunded · actual
     warn    contacted · negotiating · in progress · in discussion
     neutral to be contacted (set, but not yet acted on)
     idle    not set / withdrawn / planned
     danger  rejected · cancelled
   ============================================================ */
.location-status-select,
.location-priority-select,
.casting-status-select,
.candidate-status-select,
.investment-status-select,
.expense-status-badge {
  font-family: var(--sh-mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-align: center;
  padding: 8px 12px;
  border-radius: var(--sb-r-pill);
  appearance: none;
  -webkit-appearance: none;
  outline: none;
  cursor: pointer;
  color: var(--sb-neutral-text);
  border: 1px solid var(--sb-neutral-line);
  background: var(--sb-neutral-fill);
  color-scheme: dark;
  transition: border-color var(--sb-fast) ease, background var(--sb-fast) ease, color var(--sb-fast) ease;
}
:root[data-theme="light"] .location-status-select,
:root[data-theme="light"] .location-priority-select,
:root[data-theme="light"] .casting-status-select,
:root[data-theme="light"] .candidate-status-select,
:root[data-theme="light"] .investment-status-select { color-scheme: light; }

/* A badge is the same pill without the control affordance. */
.expense-status-badge { cursor: default; }
.expense-status-btn { cursor: pointer; }
.expense-status-btn:hover { border-color: var(--sb-line-hover); }

/* --- idle: nothing chosen yet ----------------------------- */
.location-status-select.status-,
.casting-status-select.status-,
.candidate-status-select.status-,
.location-priority-select.pri-,
.investment-status-select.status-planned,
.expense-status-badge.status-planned,
.expense-status-badge.status-withdrawn {
  color: var(--sb-idle-text);
  border-color: var(--sb-idle-line);
  background-color: var(--sb-idle-fill);
}
/* Planned is idle *and* provisional — the dashed edge says "not real
   money yet", the same signal the old palette used. */
.investment-status-select.status-planned,
.expense-status-badge.status-planned { border-style: dashed; }

/* --- neutral: on the list, nothing has happened yet -------- */
.location-status-select.status-to_contact {
  color: var(--sb-neutral-text);
  border-color: var(--sb-neutral-line);
  background-color: var(--sb-neutral-fill);
}

/* --- warn: in flight --------------------------------------- */
.location-status-select.status-contacted,
.location-status-select.status-negotiating,
.casting-status-select.status-in_progress,
.candidate-status-select.status-in_discussion {
  color: var(--sb-warn-text);
  border-color: var(--sb-warn-line);
  background-color: var(--sb-warn-fill);
}

/* --- ok: settled ------------------------------------------- */
.location-status-select.status-accepted,
.casting-status-select.status-found,
.candidate-status-select.status-accepted,
.investment-status-select.status-actual,
.expense-status-badge.status-refunded {
  color: var(--sb-ok-text);
  border-color: var(--sb-ok-line);
  background-color: var(--sb-ok-fill);
}

/* --- danger: off the table --------------------------------- */
.location-status-select.status-rejected,
.candidate-status-select.status-rejected,
.investment-status-select.status-cancelled,
.expense-status-badge.status-rejected {
  color: var(--sb-danger-text);
  border-color: var(--sb-danger-line);
  background-color: var(--sb-danger-fill);
}

/* Priority 1–5 — the ochre→vermillion ramp from §1.1, as a tint so a
   row of them reads as a gradient of urgency rather than five flat
   colour chips. */
.location-priority-select.pri-1 { --sb-pri: var(--sb-pri-1); }
.location-priority-select.pri-2 { --sb-pri: var(--sb-pri-2); }
.location-priority-select.pri-3 { --sb-pri: var(--sb-pri-3); }
.location-priority-select.pri-4 { --sb-pri: var(--sb-pri-4); }
.location-priority-select.pri-5 { --sb-pri: var(--sb-pri-5); }
.location-priority-select.pri-1,
.location-priority-select.pri-2,
.location-priority-select.pri-3,
.location-priority-select.pri-4,
.location-priority-select.pri-5 {
  color: color-mix(in srgb, var(--sb-pri) 45%, var(--sb-ink));
  border-color: color-mix(in srgb, var(--sb-pri) 45%, transparent);
  background-color: color-mix(in srgb, var(--sb-pri) 14%, transparent);
}

/* Plain, non-status dropdowns in a row (Int./Ext., Day/Night). No
   caret: in a row these are bare centred pills, as the reference
   renders draw them — the affordance is the pill itself, and a caret
   inside 80px of pill lands on top of the value. */
.location-plain-select {
  font-family: var(--sh-mono);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--sb-ink-2);
  padding: 8px 12px;
  border-radius: var(--sb-r-pill);
  text-align: center;
  appearance: none;
  -webkit-appearance: none;
}

/* In a form the same control is being *set*, not read at a glance, so
   it takes the shape of the fields around it: full width, left-aligned,
   body type, field radius — and keeps its status tint, which is the
   one thing worth carrying over from the pill. Without this the add
   forms showed centred pills reading "PLEASE SELECT AN OPTION", half of
   them with a platform arrow and half without. */
.modal-field select,
.add-form select,
.edit-form select,
.grant-form select,
.scene-quick-field select {
  width: 100%;
  font-family: var(--sb-body);
  font-size: 13.5px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  text-align: left;
  border-radius: var(--sb-r-field);
  padding: 12px 34px 12px 13px;
  appearance: none;
  -webkit-appearance: none;
  background-image: var(--sb-caret);
  background-repeat: no-repeat;
  background-position: right 13px center;
}


/* ============================================================
   9. ROWS & LIST PRIMITIVES

   .sb-row: glass, 14px radius, hairline border that lifts on hover.
   Everything the app lists — people, locations, scenes, to-dos,
   expenses, candidates, grants, found windows — is this row.
   ============================================================ */
.person,
.role-slot-card,
.location-card,
.expense-card,
.todo-item,
.candidate-row,
.grant-list li,
.finder-results li,
.ranges li,
.day-details-list li,
.shot-row,
.event-card {
  position: relative;
  overflow: hidden;
  border-radius: var(--sb-r-row);
  border: var(--sb-row-border);
  background: var(--sb-row-bg);
  backdrop-filter: var(--sb-blur-sm);
  transition: border-color 0.2s ease;
}
.person:hover,
.role-slot-card:hover,
.location-card:hover,
.expense-card:hover,
.todo-item:hover,
.finder-results li:hover,
.event-card:hover { border-color: var(--sb-row-border-hover); }

/* The 3px identity bar. Drawn as a pseudo-element rather than a left
   border so it stays a crisp rectangle inside the row's rounded
   corners — a thick border-left on a 14px radius renders as a curved
   sliver. --pc is the person colour app.js sets per row. */
.person,
.role-slot-card,
.location-card,
.expense-card,
.event-card { padding: 14px 16px 14px 20px; }
.person::before,
.role-slot-card::before,
.location-card::before,
.event-card::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  opacity: 0.85;
  background: var(--pc, var(--sh-red));
}
.role-slot-card::before { background: var(--sb-ok); }
.event-card::before { background: var(--sb-ok); }
/* Locations, scenes and casting entries share .location-card; each
   variant sets its own bar colour in §13/§16/§17, so the default is
   no bar rather than the person colour. */
.location-card::before { background: transparent; }

.people-list,
.grant-list,
.finder-results,
.day-details-list,
.ranges { list-style: none; margin: 0; padding: 0; }
.people-list,
.grant-list,
.finder-results,
.day-details-list { display: flex; flex-direction: column; gap: 8px; }

/* An empty list says so in a plain line, without the row chrome. */
.ranges li.is-empty,
.planned-list li.is-empty {
  border: none;
  background: none;
  backdrop-filter: none;
  padding: 4px 2px;
  color: var(--sb-ink-3);
}
.ranges li.is-empty::before { display: none; }

.people-empty,
.roles-empty,
.locations-empty,
.finder-empty,
.day-details-empty,
.sp-sidebar-empty,
.cal-empty-note {
  font-size: 13px;
  color: var(--sb-ink-3);
  padding: 4px 2px;
}


/* ============================================================
   10. AVAILABILITY — PEOPLE LIST (coordinator sidebar)
   ============================================================ */
.people-scroll { max-height: 620px; overflow-y: auto; padding: 2px 4px; }

/* Role group, collapsible. The caret rotates; the label is a mono
   micro-label like every other section name. */
.role-group { border: none; margin: 0 0 14px; padding: 0; }
.role-summary {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 2px 2px 8px;
  list-style: none;
  cursor: pointer;
  user-select: none;
  font-family: var(--sh-mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--sb-ink-4);
}
.role-summary::-webkit-details-marker { display: none; }
.role-summary .chev { font-size: 9px; transition: transform var(--sb-fast) ease; }
.role-group[open] .chev { transform: rotate(90deg); }
.role-summary .role-name { flex: 1; min-width: 0; }

/* Person row: bar · include-checkbox · avatar · name/roles · dates · chevron.
   The sidebar column is 340px and six items compete for it, so every
   fixed item here is kept as narrow as it can be while staying hittable:
   what the row is *for* is the name, and the name is the one thing that
   gets the leftover space. */
.person { padding: 0 0 0 20px; }
.person-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 11px 12px 11px 0;
  cursor: pointer;
}
.person-head:hover .pname { color: var(--sb-ink); }

/* Avatar: the person's own colour at the alphas the system uses for
   identity chips (22% fill / 50% edge). */
.chip {
  width: 28px;
  height: 28px;
  flex-shrink: 0;
  display: grid;
  place-items: center;
  border-radius: 50%;
  font-size: 12px;
  font-weight: 600;
  color: var(--sb-ink);
  background: color-mix(in srgb, var(--pc, var(--sh-red)) 22%, transparent);
  border: 1px solid color-mix(in srgb, var(--pc, var(--sh-red)) 50%, transparent);
}
.pname-wrap { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.pname {
  font-size: 14px;
  font-weight: 500;
  color: var(--sb-ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.role-chips {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 5px;
  min-width: 0;
}
.role-chip {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--sh-mono);
  font-size: 9.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--sb-ink-4);
}
/* A list row's second line: roles on the left, the date count hard
   right. Nothing competes with the name on the first line, which is the
   only thing in the row that cannot be shortened without losing
   information — the group heading above already names the role, and the
   date count is a two-character glance.

   The roles truncate as one line rather than per role: two flex items
   shrink proportionally, which gives "CINEM… · D…" instead of the far
   more useful "CINEMATOGRAPHER · DIREC…". In the profile card, where
   there is vertical room and the roles are pills, they still wrap. */
.person .role-chips {
  display: flex;
  align-items: center;
  flex-wrap: nowrap;   /* the badge must stay on the roles' line */
  gap: 8px;
}
.person .role-list {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.person .role-chip { display: inline; overflow: visible; }
.person .role-chip + .role-chip::before { content: ' · '; }
.person .lock { margin-right: 5px; }
.date-count-badge {
  flex-shrink: 0;
  min-width: 34px;
  text-align: center;
  font-family: var(--sh-mono);
  font-size: 9px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--sb-ink-3);
  padding: 4px 8px;
  border-radius: var(--sb-r-pill);
  border: 1px solid rgba(232, 226, 208, 0.14);
  background: rgba(232, 226, 208, 0.04);
}
:root[data-theme="light"] .date-count-badge { border-color: rgba(28, 23, 18, 0.14); background: rgba(28, 23, 18, 0.03); }

/* The whole row already toggles, so the chevron is an affordance, not a
   30px button competing with the name for width. */
.person-chev.icon-btn {
  width: 16px;
  height: 22px;
  flex-shrink: 0;
  border: none;
  background: none;
  font-family: var(--sh-mono);
  font-size: 11px;
  color: var(--sb-ink-4);
}
.person-chev.icon-btn:hover { color: var(--sb-ink); background: none; border: none; }
.lock { flex-shrink: 0; font-size: 10px; color: var(--sb-ink-4); }
.head-actions { display: flex; gap: 6px; flex-shrink: 0; }
.person-body { padding: 0 14px 14px 0; }

/* Include-in-calendar checkbox, and every other checkbox in the app.
   Custom square (.sb-check): checked is sage, because "included" and
   "done" are the same affirmative state everywhere else in the UI. */
.person-head input[type="checkbox"],
.todo-item input[type="checkbox"],
.modal-person-row input[type="checkbox"],
.modal-field input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  margin: 0;
  display: grid;
  place-items: center;
  border-radius: 6px;
  border: 1px solid rgba(232, 226, 208, 0.24);
  background: transparent;
  cursor: pointer;
  transition: background var(--sb-fast) ease, border-color var(--sb-fast) ease;
}
:root[data-theme="light"] .person-head input[type="checkbox"],
:root[data-theme="light"] .todo-item input[type="checkbox"],
:root[data-theme="light"] .modal-person-row input[type="checkbox"],
:root[data-theme="light"] .modal-field input[type="checkbox"] { border-color: rgba(28, 23, 18, 0.28); }
.person-head input[type="checkbox"]:checked,
.todo-item input[type="checkbox"]:checked,
.modal-person-row input[type="checkbox"]:checked,
.modal-field input[type="checkbox"]:checked {
  background: var(--sb-ok);
  border-color: rgba(159, 179, 122, 0.9);
}
.person-head input[type="checkbox"]:checked::after,
.todo-item input[type="checkbox"]:checked::after,
.modal-person-row input[type="checkbox"]:checked::after,
.modal-field input[type="checkbox"]:checked::after {
  content: '✓';
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  color: var(--sh-bg);
}
:root[data-theme="light"] .person-head input[type="checkbox"]:checked::after,
:root[data-theme="light"] .todo-item input[type="checkbox"]:checked::after,
:root[data-theme="light"] .modal-person-row input[type="checkbox"]:checked::after,
:root[data-theme="light"] .modal-field input[type="checkbox"]:checked::after { color: #fff; }

/* Personal (actor/crew) profile card — the feature card of that view:
   vermillion gradient edge, hover lift, and one slow light sweep. The
   sweep is deliberately near-invisible; it was toned down twice in
   design and must not be brightened (handoff §1).

   The system draws the edge with a wrapper element (.sb-card-edge >
   .sb-card-inner). Here it is one element: a transparent 1px border
   with two background layers — the card fill clipped to the padding
   box, the vermillion gradient clipped to the border box. Same result,
   no extra div for app.js to maintain. */
.panel.personal-profile-box {
  position: relative;
  overflow: hidden;
  text-align: center;
  padding: 26px 22px 24px;
  border: 1px solid transparent;
  background-image: var(--sb-card-bg-solid),
    linear-gradient(170deg, rgba(232, 83, 53, 0.55), rgba(232, 226, 208, 0.10) 28%, rgba(232, 226, 208, 0.04));
  background-origin: border-box;
  background-clip: padding-box, border-box;
  box-shadow: inset 0 1px 0 rgba(232, 226, 208, 0.10), 0 24px 60px -30px rgba(0, 0, 0, 0.9);
  transition: transform var(--sb-slow) var(--sb-ease), box-shadow var(--sb-slow) ease;
}
.panel.personal-profile-box:hover {
  transform: translateY(-1px);
  box-shadow: inset 0 1px 0 rgba(232, 226, 208, 0.10), 0 28px 64px -30px rgba(0, 0, 0, 0.92);
}
.personal-profile-box::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: -55%;
  width: 40%;
  pointer-events: none;
  background: linear-gradient(100deg, transparent, rgba(232, 226, 208, 0.025), transparent);
  transform: skewX(-12deg);
  transition: left 1.6s cubic-bezier(.4, .6, .3, 1);
}
.personal-profile-box:hover::before { left: 115%; }

/* Row-shaped control inside the profile card ("Show my to-dos"). The
   design draws a switch here; in this app the control opens a modal
   rather than toggling a section in place, so it keeps a chevron —
   a switch would promise state it does not hold. See docs/DESIGN.md. */
.personal-profile-box .profile-row-btn {
  width: 100%;
  margin: 22px 0 6px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 14px;
  border-radius: var(--sb-r-row);
  border: 1px solid rgba(232, 226, 208, 0.12);
  background: linear-gradient(160deg, rgba(232, 226, 208, 0.08), rgba(232, 226, 208, 0.02));
  backdrop-filter: blur(12px);
  box-shadow: inset 0 1px 0 rgba(232, 226, 208, 0.12);
  font-family: var(--sb-body);
  font-size: 13.5px;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--sb-ink);
  cursor: pointer;
}
.personal-profile-box .profile-row-btn:hover { border-color: rgba(232, 226, 208, 0.26); }
.personal-profile-box .profile-row-btn .chev { font-family: var(--sh-mono); font-size: 12px; color: var(--sb-ink-3); }

.personal-avatar {
  width: 84px;
  height: 84px;
  margin: 0 auto 14px;
  display: grid;
  place-items: center;
  border-radius: 22px;
  background: var(--sh-red);
  color: var(--sh-bg);
  font-family: var(--sh-display);
  font-size: 38px;
  line-height: 1;
}
.personal-profile-box .pname-big {
  font-family: var(--sh-display);
  text-transform: uppercase;
  letter-spacing: -0.02em;
  font-size: 24px;
  line-height: 1.05;
  margin: 0 0 9px;
  color: var(--sb-ink);
}
/* The role reads as a chip here, not as the inline list used in rows. */
.personal-profile-box .role-chips { justify-content: center; }
.personal-profile-box .role-chip {
  color: var(--sb-ink-2);
  padding: 6px 13px;
  border-radius: var(--sb-r-pill);
  border: 1px solid rgba(232, 226, 208, 0.16);
  background: rgba(232, 226, 208, 0.05);
  letter-spacing: 0.14em;
}
:root[data-theme="light"] .personal-profile-box .role-chip { border-color: rgba(28, 23, 18, 0.16); background: rgba(28, 23, 18, 0.04); }
.personal-profile-box .edit-form,
.personal-profile-box .ranges,
.personal-profile-box .planned-list { text-align: left; }
.personal-instruction {
  font-family: var(--sh-display);
  text-transform: uppercase;
  letter-spacing: -0.02em;
  font-size: 21px;
  line-height: 1.05;
  color: var(--sb-ink);
  margin: 0 0 6px;
}


/* ============================================================
   11. AVAILABILITY — BLOCKED DATES & WINDOW FINDER
   ============================================================ */
.range-form { display: flex; gap: 14px; align-items: flex-end; flex-wrap: wrap; }
.range-form .date-field { flex: 1; min-width: 200px; }
.range-form .btn { flex-shrink: 0; }

/* One blocked range: vermillion dot, the dates, and a × to drop it. */
.ranges { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 12px; }
.ranges li {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  padding: 11px 8px 11px 14px;
  font-size: 13px;
  color: var(--sb-ink-2);
}
.ranges li::before {
  content: '';
  width: 5px;
  height: 5px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--sh-red);
}
.ranges li span { flex: 1; min-width: 0; }
.ranges li:hover { border-color: var(--sb-danger-line); }
.personal-profile-box .ranges { grid-template-columns: 1fr; }

/* "Planned for you" — sage, because a call is the one thing on this
   card that is good news rather than a restriction. */
.planned-list li {
  display: flex;
  flex-direction: column;
  /* .ranges li centres its children on the cross axis, which in a
     column means horizontally — not what a two-line row wants. */
  align-items: flex-start;
  gap: 4px;
  padding: 11px 14px;
  border-radius: var(--sb-r-field);
  border: 1px solid rgba(159, 179, 122, 0.18);
  background: linear-gradient(160deg, var(--sb-ok-fill), rgba(159, 179, 122, 0.03));
  backdrop-filter: var(--sb-blur-sm);
  font-size: 13px;
  color: var(--sb-ink);
}
.planned-list li:hover { border-color: rgba(159, 179, 122, 0.38); }
.planned-list .planned-range {
  font-family: var(--sh-mono);
  font-size: 12px;
  letter-spacing: 0.02em;
  color: var(--sb-ok-text);
}

/* Window finder */
.finder-form { display: flex; gap: 14px; align-items: flex-end; flex-wrap: wrap; margin-bottom: 16px; }
.finder-form label { flex: 0 1 auto; }
.finder-form label > span {
  font-family: var(--sh-mono);
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--sb-ink-4);
}
.finder-form input { width: 150px; }
.finder-scope { font-size: 13px; color: var(--sb-ink-3); margin: 0; }
.finder-results { max-height: 420px; overflow-y: auto; padding-right: 2px; }
.finder-results li {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  font-size: 13.5px;
  cursor: pointer;
}
.finder-results li.active {
  border-color: rgba(159, 179, 122, 0.9);
  box-shadow: inset 0 0 0 1px rgba(159, 179, 122, 0.45);
}
.finder-rank {
  font-family: var(--sh-mono);
  font-size: 11px;
  color: var(--sb-ink-4);
  width: 20px;
  flex-shrink: 0;
}
.finder-dates { flex: 1; min-width: 0; font-weight: 500; color: var(--sb-ink); }
.finder-status {
  font-family: var(--sh-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.finder-status.clear { color: var(--sb-ok-text); }
.finder-status.conflict { color: var(--sb-warn-text); }


/* ============================================================
   12. CALENDAR, DAY DETAILS & EVENTS

   Sage = everyone included is free. Vermillion = somebody is blocked.
   The reference render draws that as a binary; this app knows *how
   many* people are blocked and has always shown the difference, so
   the two conflict levels are kept — as two strengths of the same
   vermillion rather than two different colours. The legend's "Some
   unavailable" covers both.
   ============================================================ */
.cal-nav { display: flex; align-items: center; justify-content: center; gap: 18px; margin-bottom: 20px; }
.cal-nav h2 {
  font-family: var(--sh-display);
  text-transform: uppercase;
  letter-spacing: -0.02em;
  font-size: 22px;
  line-height: 1;
  color: var(--sb-ink);
  min-width: 270px;
  text-align: center;
  padding: 0;
  margin: 0;
}
.cal-nav h2::after { display: none; }
.cal-nav .btn {
  width: 38px;
  height: 38px;
  padding: 0;
  font-size: 16px;
  border-radius: 12px;
  background: linear-gradient(160deg, rgba(232, 226, 208, 0.09), rgba(232, 226, 208, 0.02));
  box-shadow: inset 0 1px 0 rgba(232, 226, 208, 0.14);
}
.cal-nav .btn:hover { border-color: rgba(232, 83, 53, 0.5); color: var(--sh-red-glow); }

.cal-weekdays {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 8px;
  padding: 0 0 4px;
}
.cal-weekdays span {
  font-family: var(--sh-mono);
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--sb-ink-4);
  text-align: center;
}
.cal-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 8px; }

/* Day cell (.sb-day). */
.day {
  position: relative;
  min-height: var(--sb-day-h, 96px);
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: flex-start;
  gap: 4px;
  text-align: left;
  cursor: pointer;
  border-radius: var(--sb-r-row);
  border: 1px solid rgba(232, 226, 208, 0.09);
  background: var(--sb-row-bg);
  backdrop-filter: var(--sb-blur-sm);
  box-shadow: inset 0 1px 0 rgba(232, 226, 208, 0.08);
  transition: transform var(--sb-fast) ease, border-color var(--sb-fast) ease, box-shadow var(--sb-fast) ease;
}
.day:hover { transform: translateY(-2px); border-color: var(--sb-line-hover); }
.daynum {
  font-family: var(--sh-mono);
  font-size: 14px;
  letter-spacing: 0.02em;
  color: var(--sb-ink);
}

/* Everyone included is free. */
.day.good {
  background: linear-gradient(160deg, var(--sb-ok-fill-strong), rgba(159, 179, 122, 0.05));
  border-color: rgba(159, 179, 122, 0.34);
  box-shadow: inset 0 1px 0 rgba(159, 179, 122, 0.20);
}
/* Somebody is blocked — light, then full. */
.day.conflict-low {
  background: linear-gradient(160deg, rgba(232, 83, 53, 0.13), rgba(181, 58, 32, 0.05));
  border-color: rgba(232, 83, 53, 0.26);
  box-shadow: inset 0 1px 0 rgba(255, 140, 110, 0.12);
}
.day.conflict-high {
  background: linear-gradient(160deg, rgba(232, 83, 53, 0.26), rgba(181, 58, 32, 0.10));
  border-color: rgba(232, 83, 53, 0.42);
  box-shadow: inset 0 1px 0 rgba(255, 140, 110, 0.22);
}
.day.conflict-low .daynum,
.day.conflict-high .daynum { color: #F4D9D1; }
:root[data-theme="light"] .day.conflict-low .daynum,
:root[data-theme="light"] .day.conflict-high .daynum { color: var(--sb-ink); }

/* Outside the displayed month. */
.day.outside {
  opacity: 1;
  cursor: default;
  background: rgba(232, 226, 208, 0.018);
  border-color: rgba(232, 226, 208, 0.05);
  box-shadow: none;
}
.day.outside .daynum { color: #565249; }
.day.outside:hover { transform: none; border-color: rgba(232, 226, 208, 0.05); }
:root[data-theme="light"] .day.outside { background: rgba(28, 23, 18, 0.02); border-color: rgba(28, 23, 18, 0.05); }
:root[data-theme="light"] .day.outside .daynum { color: var(--sb-ink-4); }

/* Today, the selected day, and a found window. */
.day.today { box-shadow: inset 0 0 0 1px rgba(232, 226, 208, 0.35); }
.day.selected-day {
  border-color: rgba(232, 226, 208, 0.75);
  box-shadow: inset 0 1px 0 rgba(232, 226, 208, 0.25), 0 0 0 3px rgba(232, 226, 208, 0.12);
}
.day.selected-day .daynum { font-weight: 600; }
.day.shortlisted {
  border-color: rgba(159, 179, 122, 0.9);
  box-shadow: inset 0 0 0 1px rgba(159, 179, 122, 0.45);
}

/* Per-person dots along the bottom of a cell. */
.dots {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  margin-top: auto;
}
.dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
.dot-more { font-family: var(--sh-mono); font-size: 9px; color: var(--sb-ink-4); }

/* A scheduled event on a day. Sage, and set in mono — it is a call
   time, the same "you are scheduled" signal as the calendar legend. */
.event-strip {
  max-width: 100%;
  font-family: var(--sh-mono);
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--sb-ok-text);
  padding: 2px 6px;
  border-radius: 6px;
  border: 1px solid rgba(159, 179, 122, 0.28);
  background: var(--sb-ok-fill);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.day .event-strip { margin-top: auto; }
.day .event-strip ~ .event-strip,
.day .event-strip ~ .dots { margin-top: 0; }
.day.has-event { border-top-color: rgba(159, 179, 122, 0.45); }

.cal-legend {
  display: flex;
  align-items: center;
  gap: 22px;
  flex-wrap: wrap;
  margin-top: 20px;
  padding-top: 18px;
  border-top: 1px solid var(--sb-rule);
  font-size: 12.5px;
  color: var(--sb-ink-3);
}
.cal-legend .item { display: flex; align-items: center; gap: 9px; }
.cal-legend .swatch { width: 13px; height: 13px; border-radius: 4px; flex-shrink: 0; }
.cal-legend .swatch--free { background: var(--sb-ok-fill-strong); border: 1px solid rgba(159, 179, 122, 0.45); }
.cal-legend .swatch--busy { background: rgba(232, 83, 53, 0.24); border: 1px solid rgba(232, 83, 53, 0.5); }
.cal-legend .swatch--person { border-radius: 50%; width: 8px; height: 8px; }
.cal-empty-note { text-align: center; padding: 12px 0 2px; }

/* Day details modal list */
.day-details-list li {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  font-size: 13px;
}
.day-details-list .dot { width: 8px; height: 8px; }
.day-details-list .dname { flex: 1; min-width: 0; font-weight: 500; color: var(--sb-ink); }
.day-details-list .drole {
  font-family: var(--sh-mono);
  font-size: 9.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--sb-ink-4);
}

/* Event card (day details, finder results) */
.event-card { padding: 14px 16px 14px 20px; margin-bottom: 8px; }
.event-card-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; }
.event-card-title { font-size: 13.5px; font-weight: 600; color: var(--sb-ink); }
.event-card-type {
  font-family: var(--sh-mono);
  font-size: 9.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--sb-ink-4);
}
.event-card-dates {
  font-family: var(--sh-mono);
  font-size: 12px;
  color: var(--sb-ok-text);
  margin: 4px 0 8px;
}
.event-card-people { font-size: 12.5px; color: var(--sb-ink-2); }
.event-you-tag {
  display: inline-block;
  margin-left: 8px;
  padding: 3px 9px;
  border-radius: var(--sb-r-pill);
  border: 1px solid var(--sb-ok-line);
  background: var(--sb-ok-fill);
  font-family: var(--sh-mono);
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--sb-ok-text);
}
.event-card-actions { display: flex; gap: 6px; flex-shrink: 0; }


/* ============================================================
   13. ROLES & CHARACTERS (casting pipeline)

   One row per character: CHARACTER / PORTRAYED BY as mono micro-
   labels above their values, status select, chevron. The original
   nested field-boxes are gone — same information, same positions,
   far less chrome (handoff §4.3).
   ============================================================ */
.casting-card .location-head { gap: 18px; }
.casting-card.status-in_progress::before { background: var(--sb-warn); }
.casting-card.status-found::before { background: var(--sb-ok); }

.casting-entry-head-info {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1.3fr);
  gap: 18px;
  align-items: center;
  flex: 1;
  min-width: 260px;
}
.casting-entry-info-block {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
  padding: 0;
  border: none;
  background: none;
}
.casting-entry-info-label { letter-spacing: 0.16em; font-size: 9.5px; }
.casting-entry-info-value {
  font-size: 15px;
  font-weight: 600;
  color: var(--sb-ink);
  overflow-wrap: anywhere;
}
.casting-entry-info-value.is-empty { color: var(--sb-ink-4); font-weight: 400; }

/* Candidate pipeline inside an expanded entry. */
.candidates-heading { display: flex; align-items: center; gap: 12px; }
.candidates-heading::after { content: ''; flex: 1; height: 1px; background: var(--sb-rule); }
.candidate-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 10px 14px;
  margin-bottom: 8px;
  font-size: 13px;
}
.candidate-row .candidate-name { flex: 1; min-width: 110px; font-weight: 500; color: var(--sb-ink); }
.candidate-add-form { display: flex; gap: 10px; margin-top: 10px; flex-wrap: wrap; }
.candidate-add-form input { flex: 1; min-width: 160px; }


/* ============================================================
   14. CAST AND CREW
   ============================================================ */
/* Two columns of person rows (.sb-grid-rows in the system). */
.covered-roles-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 10px;
  align-items: start;
}
.covered-roles-grid .role-slot-card { margin-bottom: 0; }
.role-slot-card { padding: 14px 14px 14px 20px; }
.role-slot-head { display: flex; justify-content: space-between; align-items: center; gap: 10px; cursor: pointer; }
.role-slot-name { font-size: 13.5px; font-weight: 500; color: var(--sb-ink); }
.role-slot-notes {
  font-family: var(--sh-mono);
  font-size: 9.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--sb-ink-4);
  margin-top: 3px;
}
.role-slot-controls { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }


/* ============================================================
   15. TASK LIST
   ============================================================ */
.todo-item {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 14px 16px;
  margin-bottom: 8px;
}
.todo-item.done { opacity: 0.6; }
.todo-item input[type="checkbox"] { margin-top: 2px; }
.todo-item-body { flex: 1; min-width: 0; }
.todo-item-title { font-size: 14.5px; font-weight: 600; color: var(--sb-ink); }
.todo-item.done .todo-item-title { text-decoration: line-through; color: var(--sb-ink-4); }
.todo-item-desc { font-size: 13px; color: var(--sb-ink-3); margin-top: 3px; }
.todo-item-meta {
  font-family: var(--sh-mono);
  font-size: 9.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--sb-ink-4);
  margin-top: 6px;
}
.todo-item-actions { display: flex; gap: 6px; flex-shrink: 0; }
.todo-overdue { color: var(--sb-danger-text); }


/* ============================================================
   16. LOCATIONS

   A table: name · status · priority · type · owner · open. The column
   header row is new (handoff §4.4) — the selects used to be
   unlabelled, which made a wall of pills unreadable.
   ============================================================ */
.location-table-head {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) 150px 140px 140px 140px 30px;
  gap: 12px;
  align-items: center;
  padding: 0 16px 10px 20px;
}
.location-table-head span {
  font-family: var(--sh-mono);
  font-size: 9.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--sb-ink-4);
}
/* .location-card is the shared expandable row; .location-row,
   .scene-card and .casting-card mark which of the three it is. */
.location-card { padding: 0 0 0 20px; }
.location-head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px 12px 0;
  cursor: pointer;
}
.location-controls { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
/* A location row is a table row: its controls become cells of the same
   grid the column header above them uses. */
.location-row .location-head {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) 150px 140px 140px 140px 30px;
}
.location-row .location-controls { display: contents; }
.location-name {
  font-family: var(--sh-mono);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--sb-ink);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.location-row .location-controls > .icon-btn { justify-self: end; }

/* The row's left bar carries the status (handoff §3). */
.location-card.status-to_contact::before { background: rgba(232, 226, 208, 0.25); }
.location-card.status-contacted::before,
.location-card.status-negotiating::before { background: var(--sb-warn); }
.location-card.status-accepted::before { background: var(--sb-ok); }
.location-card.status-rejected::before { background: var(--sh-red); }

/* Expanded details */
.location-body {
  padding: 16px;
  border-top: 1px solid rgba(232, 226, 208, 0.08);
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.location-field-row {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 6px 0;
  font-size: 13px;
  border-bottom: 1px solid var(--sb-rule);
}
.location-field-row:last-child { border-bottom: none; }
.location-field-row .label {
  flex-shrink: 0;
  font-family: var(--sh-mono);
  font-size: 9.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--sb-ink-4);
}
.location-field-row .value { text-align: right; color: var(--sb-ink-2); overflow-wrap: anywhere; }


/* ============================================================
   17. SCENES & SHOT LIST
   ============================================================ */
/* Scene rows reuse .location-card. The left bar reads the slugline:
   ochre for DAY, the ring's blue-grey for NIGHT — the one place the
   app colour-codes something that is neither a status nor a person. */
.location-card.dn-day::before { background: var(--sb-warn); }
.location-card.dn-night::before { background: var(--p-8); }
.scene-card .location-head { display: flex; gap: 12px; }
.scene-card .location-name {
  flex: 1;
  font-family: var(--sb-body);
  font-size: 14px;
  letter-spacing: 0;
  text-transform: none;
  font-weight: 500;
}
.scene-drag-handle {
  flex-shrink: 0;
  padding: 0 2px;
  font-size: 13px;
  color: var(--sb-ink-4);
  cursor: grab;
  user-select: none;
}
.scene-drag-handle:active { cursor: grabbing; }
.scene-drag-over { box-shadow: inset 0 0 0 1px var(--sh-red); border-color: rgba(232, 83, 53, 0.6); }

.scene-quick-row { display: flex; gap: 18px; flex-wrap: wrap; margin-bottom: 14px; }
.scene-quick-field select { min-width: 140px; }
.scene-description-block { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--sb-rule); }
.scene-description-text { font-size: 13px; color: var(--sb-ink-2); white-space: pre-wrap; overflow-wrap: anywhere; }

/* Shot list — a quieter row nested one level inside a scene. */
.shot-list { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.shot-row { padding: 10px 14px; }
.shot-row-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.shot-number {
  flex-shrink: 0;
  font-family: var(--sh-mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  color: var(--sb-ink);
  padding: 3px 10px;
  border-radius: var(--sb-r-pill);
  border: 1px solid rgba(232, 226, 208, 0.16);
  background: rgba(232, 226, 208, 0.05);
}
.shot-type-tag {
  font-family: var(--sh-mono);
  font-size: 9.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--sb-ink-4);
}
.shot-row-actions { margin-left: auto; display: flex; gap: 4px; flex-shrink: 0; }
.shot-row-body { margin-top: 8px; font-size: 13px; }
.shot-lens {
  font-family: var(--sh-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--sb-ink-4);
  margin-bottom: 3px;
}
.shot-desc { color: var(--sb-ink-2); }


/* ============================================================
   18. EXPENSES
   ============================================================ */
.expense-card { padding: 12px 16px; }
.expense-head { display: flex; align-items: center; gap: 16px; cursor: pointer; }
.expense-thumb,
.expense-thumb-placeholder {
  width: 46px;
  height: 46px;
  flex-shrink: 0;
  border-radius: 10px;
  object-fit: cover;
  border: 1px solid rgba(232, 226, 208, 0.14);
  background: rgba(232, 226, 208, 0.05);
}
.expense-thumb { cursor: pointer; }
.expense-thumb-placeholder {
  display: grid;
  place-items: center;
  font-family: var(--sh-mono);
  font-size: 9px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--sb-ink-4);
}
/* A missing receipt is flagged, not merely absent — expenses are not
   accepted without proof of purchase. */
.expense-thumb-placeholder.no-receipt {
  color: var(--sb-danger-text);
  border-color: var(--sb-danger-line);
  background: var(--sb-danger-fill);
  border-style: dashed;
}
.expense-info { flex: 1; min-width: 0; }
.expense-description {
  font-size: 14.5px;
  font-weight: 600;
  color: var(--sb-ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.expense-meta {
  font-family: var(--sh-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--sb-ink-4);
  margin-top: 4px;
}
.expense-meta .no-receipt-tag { color: var(--sb-danger-text); }
.expense-amount {
  flex-shrink: 0;
  min-width: 92px;
  text-align: right;
  font-family: var(--sh-mono);
  font-size: 15px;
  letter-spacing: 0.02em;
  color: var(--sb-ink);
}
.expense-warning {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 4px 0 14px;
  padding: 12px 14px;
  border-radius: var(--sb-r-field);
  border: 1px solid var(--sb-danger-line);
  background: var(--sb-danger-fill);
  font-family: var(--sh-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--sb-danger-text);
}
.expense-card-body {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid rgba(232, 226, 208, 0.08);
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.expense-card-body .location-field-row { border-bottom: none; padding: 0; }
.expense-receipt-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.expense-receipt-preview {
  width: 64px;
  height: 64px;
  border-radius: 10px;
  object-fit: cover;
  border: 1px solid rgba(232, 226, 208, 0.14);
  cursor: pointer;
}


/* ============================================================
   19. BUDGET & INVESTMENTS
   ============================================================ */
.budget-summary { display: flex; gap: 12px; flex-wrap: wrap; }
.budget-stat {
  flex: 1;
  min-width: 150px;
  padding: 16px 18px;
  border-radius: var(--sb-r-row);
  border: var(--sb-row-border);
  background: var(--sb-row-bg);
  backdrop-filter: var(--sb-blur-sm);
}
.budget-stat-label { margin-bottom: 8px; }
.budget-stat-value {
  display: block;
  font-family: var(--sh-mono);
  font-size: 20px;
  letter-spacing: 0.02em;
  color: var(--sb-ink);
}
/* Remaining is the number the owner actually came here for. */
.budget-stat-remaining {
  border-color: rgba(232, 83, 53, 0.35);
  background: linear-gradient(160deg, rgba(232, 83, 53, 0.12), rgba(232, 83, 53, 0.03));
}
.budget-stat-remaining .budget-stat-value { color: var(--sh-red-glow); }
.budget-stat-planned { border-style: dashed; }
.budget-stat-planned .budget-stat-value { color: var(--sb-ink-3); }


/* ============================================================
   20. ADMIN
   ============================================================ */
/* The admin tab is owner/coordinator-only; the dashed edge that used
   to mark it out is replaced by the same quiet convention the rest of
   the app uses for "provisional or privileged": a dashed hairline. */
.admin-panel { border-style: dashed; }
.admin-two-col { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; margin-bottom: var(--sb-gap); }
.admin-two-col > .panel { margin-bottom: 0; }
.grant-form { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 14px; min-width: 0; }
.grant-form > * { flex: 1; min-width: 140px; }
.grant-form .btn { flex: 0 0 auto; }
.grant-list li {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  padding: 11px 14px;
  font-size: 13px;
  color: var(--sb-ink-2);
}


/* ============================================================
   21. PROJECTS HUB, AUTH & ONBOARDING
   ============================================================ */
.projects-grid { display: flex; flex-wrap: wrap; gap: 12px; }
.project-tile {
  position: relative;
  width: 200px;
  height: 120px;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 16px;
  text-align: center;
  cursor: pointer;
  border-radius: var(--sb-r-row);
  border: var(--sb-row-border);
  background: var(--sb-row-bg);
  backdrop-filter: var(--sb-blur-sm);
  color: var(--sb-ink);
  font-size: 14px;
  font-weight: 500;
  transition: border-color 0.2s ease, transform 0.2s ease;
}
.project-tile:hover { border-color: var(--sb-row-border-hover); transform: translateY(-2px); }
.project-tile-name { overflow-wrap: anywhere; }
.project-tile-status {
  font-family: var(--sh-mono);
  font-size: 9.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--sb-ink-4);
}
.project-tile-controls {
  position: absolute;
  top: 8px;
  right: 8px;
  display: flex;
  gap: 4px;
  opacity: 0;
  transition: opacity 0.2s ease;
}
.project-tile:hover .project-tile-controls { opacity: 1; }
.project-tile-status-only { cursor: default; }
.project-tile-status-only:hover { transform: none; border-color: rgba(232, 226, 208, 0.09); }
.project-tile-new { border-style: dashed; color: var(--sb-ink-3); background: transparent; }
.project-tile-new:hover { color: var(--sh-red-glow); border-color: rgba(232, 83, 53, 0.5); }
.project-tile-form {
  width: auto;
  height: auto;
  min-width: 240px;
  display: block;
  text-align: left;
  cursor: default;
  padding: 14px;
}
.project-tile-form:hover { transform: none; border-color: rgba(232, 226, 208, 0.09); }
.project-tile-form form { display: flex; flex-direction: column; gap: 10px; }

/* The one-time code is data, not prose: mono, tracked out so the digits
   can be checked against the email one by one. */
#authCode {
  font-family: var(--sh-mono);
  font-size: 16px;
  letter-spacing: 0.22em;
}
#authCode::placeholder { font-size: 13.5px; letter-spacing: 0.02em; }

/* Sign-in and the "you're not on the board yet" states are the only
   places a card speaks in display type at full page width. */
#authGate .panel,
#noProjectNameSection,
#noProjectPendingSection { max-width: 460px; margin: 60px auto; }
#authGate .panel h2,
.register-banner h2,
#noProjectNameSection h2,
#noProjectPendingSection h2 {
  font-family: var(--sh-display);
  font-size: 21px;
  letter-spacing: -0.02em;
  line-height: 1;
  color: var(--sb-ink);
  padding: 0;
  margin-bottom: 10px;
}
#authGate .panel h2::after,
.register-banner h2::after,
#noProjectNameSection h2::after,
#noProjectPendingSection h2::after { display: none; }

/* Registration prompt — an invitation, so it wears the brand edge. */
.register-banner {
  margin: 0 0 var(--sb-gap);
  padding: 22px 24px 24px;
  border-radius: var(--sb-r-card);
  border: 1px solid rgba(232, 83, 53, 0.35);
  background: linear-gradient(170deg, rgba(232, 83, 53, 0.10), rgba(22, 19, 17, 0.86));
  backdrop-filter: var(--sb-blur);
  box-shadow: var(--sb-card-shadow);
}
.register-banner p { margin: 0 0 14px; font-size: 13.5px; color: var(--sb-ink-3); }
.conflict-box {
  margin-top: 12px;
  padding: 14px;
  border-radius: var(--sb-r-field);
  border: var(--sb-row-border);
  background: var(--sb-row-bg);
}
.conflict-box p { margin: 0 0 10px; font-size: 13px; color: var(--sb-ink-2); }
.conflict-actions { display: flex; gap: 8px; flex-wrap: wrap; }
#loadingNote {
  text-align: center;
  padding: 60px 0;
  font-family: var(--sh-mono);
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--sb-ink-4);
}


/* ============================================================
   22. MODALS, TOAST, MENUS, AUTOCOMPLETE
   ============================================================ */
.modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: var(--sb-scrim);
  backdrop-filter: blur(6px);
}
.modal-box {
  width: 100%;
  max-width: 520px;
  max-height: 85vh;
  overflow-y: auto;
  padding: 26px;
  border-radius: var(--sb-r-card);
  border: var(--sb-card-border);
  background: var(--sb-card-bg-solid);
  backdrop-filter: var(--sb-blur);
  box-shadow: var(--sb-card-shadow);
}
.modal-box h2 {
  display: flex;
  align-items: center;
  gap: 12px;
  font-family: var(--sh-display);
  font-size: 21px;
  letter-spacing: -0.02em;
  line-height: 1;
  color: var(--sb-ink);
  margin: 0 0 18px;
}
.modal-box h2.mono {
  font-family: var(--sh-mono);
  font-size: 13px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.modal-row { display: flex; gap: 12px; flex-wrap: wrap; }
.modal-row .modal-field { flex: 1; min-width: 150px; }
.modal-people-list {
  max-height: 240px;
  overflow-y: auto;
  margin-top: 2px;
  padding: 10px;
  border-radius: var(--sb-r-field);
  border: var(--sb-row-border);
  background: var(--sb-row-bg);
}
.modal-person-row { display: flex; align-items: center; gap: 10px; padding: 6px 2px; font-size: 13px; }
.modal-person-row span { flex: 1; min-width: 0; }
.modal-actions { display: flex; justify-content: space-between; align-items: center; gap: 10px; margin-top: 20px; }
.modal-actions-right { display: flex; gap: 8px; }

/* Toast — one line, in the tone of what just happened. */
.toast {
  display: none;
  margin: 16px 0;
  padding: 12px 16px;
  border-radius: var(--sb-r-field);
  font-family: var(--sh-mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  backdrop-filter: var(--sb-blur-sm);
}
.toast.show { display: block; }
.toast.ok { color: var(--sb-ok-text); border: 1px solid var(--sb-ok-line); background: var(--sb-ok-fill); }
.toast.error { color: var(--sb-danger-text); border: 1px solid var(--sb-danger-line); background: var(--sb-danger-fill); }

/* Dropdown menus (account → Bin, screenplay → ☰). */
.profile-menu-dropdown,
.sp-menu-dropdown,
.screenplay-autocomplete {
  position: absolute;
  z-index: 50;
  min-width: 180px;
  padding: 6px;
  border-radius: var(--sb-r-field);
  border: var(--sb-card-border);
  background: var(--sb-card-bg-solid);
  backdrop-filter: var(--sb-blur);
  box-shadow: 0 24px 60px -34px rgba(0, 0, 0, 0.9);
}
.profile-menu-dropdown { right: 0; top: calc(100% + 8px); }
.sp-menu-dropdown { left: 0; top: calc(100% + 8px); }
.profile-menu-item,
.sp-menu-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: 9px 12px;
  border: none;
  border-radius: 9px;
  background: none;
  color: var(--sb-ink);
  font-family: var(--sh-mono);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
}
.profile-menu-item:hover,
.sp-menu-item:hover { background: rgba(232, 226, 208, 0.07); }
:root[data-theme="light"] .profile-menu-item:hover,
:root[data-theme="light"] .sp-menu-item:hover { background: rgba(28, 23, 18, 0.05); }
.sp-menu-item:disabled { opacity: 0.45; cursor: not-allowed; }
.sp-menu-item:disabled:hover { background: none; }
.sp-menu-divider { height: 1px; margin: 5px 2px; background: var(--sb-rule); }


/* ============================================================
   23. SCREENPLAY

   The one part of the app the design system deliberately does not
   reach into. A screenplay is 12pt Courier on a fixed page, and the
   page geometry below is format, not styling — it is what makes
   "1 page ≈ 1 minute of screen time" true. Only the chrome around
   the page (sidebar, menu bar, autocomplete) follows the system.
   ============================================================ */
.screenplay-layout { display: flex; gap: 20px; align-items: flex-start; max-width: 1200px; margin: 0 auto; }
.screenplay-panel { flex: 1; min-width: 0; margin: 0; background: none; border: none; box-shadow: none; padding: 0; }
.screenplay-sidebar {
  width: 280px;
  flex-shrink: 0;
  position: sticky;
  top: 20px;
  margin: 0;
  max-height: calc(100vh - 40px);
  display: flex;
  flex-direction: column;
  gap: 20px;
}
.sp-element-picker { flex-shrink: 0; margin-bottom: 0; }
.sp-element-picker-options { display: flex; flex-wrap: wrap; gap: 6px; }
.sp-element-btn {
  font-family: var(--sh-mono);
  font-size: 9.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--sb-ink-3);
  padding: 8px 11px;
  border-radius: 9px;
  border: 1px solid rgba(232, 226, 208, 0.12);
  background: rgba(232, 226, 208, 0.04);
  cursor: pointer;
  transition: border-color var(--sb-fast) ease, color var(--sb-fast) ease, background var(--sb-fast) ease;
}
.sp-element-btn:hover { color: var(--sb-ink); border-color: var(--sb-line-hover); }
.sp-element-btn.active { color: #fff; background: var(--sh-red); border-color: var(--sh-red); }

.screenplay-sidebar-lists { flex: 1; min-height: 0; margin-bottom: 0; display: flex; flex-direction: column; }
.screenplay-sidebar-tabs { display: flex; gap: 2px; margin-bottom: 12px; flex-shrink: 0; border-bottom: 1px solid var(--sb-rule); }
.sp-sidebar-tab {
  flex: 1;
  padding: 10px 4px;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  cursor: pointer;
  font-family: var(--sh-mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--sb-ink-4);
  transition: color 0.2s ease, border-color 0.2s ease;
}
.sp-sidebar-tab:hover { color: var(--sb-ink); }
.sp-sidebar-tab.active { color: var(--sb-ink); border-bottom-color: var(--sh-red); }
.screenplay-sidebar-list { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 2px; }
.sp-sidebar-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border-radius: 9px;
  font-size: 13px;
  color: var(--sb-ink-2);
  cursor: pointer;
}
.sp-sidebar-item:hover { background: rgba(232, 226, 208, 0.06); }
:root[data-theme="light"] .sp-sidebar-item:hover { background: rgba(28, 23, 18, 0.05); }
.sp-sidebar-item-num { flex-shrink: 0; font-family: var(--sh-mono); font-size: 11px; color: var(--sb-ink-4); }
.sp-sidebar-item-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sp-sidebar-edit-row { display: flex; flex-direction: column; gap: 8px; padding: 8px; border-radius: 10px; background: rgba(232, 226, 208, 0.05); }
.sp-sidebar-edit-actions { display: flex; gap: 6px; justify-content: flex-end; }

/* Sticky menu bar, bled to the panel's edges. */
.sp-menu-bar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin: 0 0 20px;
  padding: 12px 18px;
  border-radius: var(--sb-r-card);
  border: var(--sb-card-border);
  background: var(--sb-card-bg-solid);
  backdrop-filter: var(--sb-blur);
  box-shadow: var(--sb-card-shadow);
}
.sp-menu-wrap { position: relative; }
.sp-save-status {
  flex-shrink: 0;
  font-family: var(--sh-mono);
  font-size: 9.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--sb-ink-4);
}

/* --- the page itself -----------------------------------------
   Courier Prime rather than whatever "Courier" a machine happens to
   have: it is metric-compatible with screenplay-standard Courier (10
   characters/inch), and a narrower fallback silently fits more text
   per page than the format allows.

   The "1 page ≈ 1 minute" convention is calibrated to ~55 lines —
   i.e. to 9in of usable height, not to "1in margins". A4 is 0.69in
   taller than Letter, so holding it to literal 1in top/bottom margins
   would fit ~4–5 extra lines per page. Real A4 templates compensate
   with a deeper margin: (11.69in − 9in) / 2 = 1.346in, which is what
   is used here. Left/right (1.5in for the punch holes, 1in right) and
   the character/parenthetical/dialogue indents are the standard
   positions measured from the left margin.

   CSS treats 1in as exactly 96px on screen and in print, so using
   physical units keeps the screen page, the @page rule in §25 and the
   pagination maths in app.js all in agreement.
   ------------------------------------------------------------- */
.screenplay-editor {
  width: 210mm;
  max-width: 100%;
  min-height: 297mm;
  margin: 0 auto;
  padding: 1.346in 1in 1.346in 1.5in;
  font-family: 'Courier Prime', 'Courier New', Courier, monospace;
  font-size: 12pt;
  line-height: 1;
  color: var(--sb-ink);
  border-radius: var(--sb-r-card);
  border: var(--sb-card-border);
  background: var(--sb-card-bg-solid);
  box-shadow: var(--sb-card-shadow);
  outline: none;
  cursor: text;
}
.sp-block { outline: none; white-space: pre-wrap; word-break: break-word; min-height: 1em; }
.sp-block:empty:before { content: attr(data-placeholder); color: var(--sb-ink-4); pointer-events: none; }
.sp-scene_heading { text-transform: uppercase; font-weight: 700; margin-top: 1em; }
.sp-scene_heading:first-child { margin-top: 0; }
.sp-action { margin-top: 1em; }
.sp-character { text-transform: uppercase; margin-top: 1em; padding-left: 2.2in; }
.sp-parenthetical { padding-left: 1.6in; padding-right: 2.4in; }
.sp-dialogue { padding-left: 1in; padding-right: 1.5in; }
.sp-transition { text-transform: uppercase; text-align: right; margin-top: 1em; }

/* Page seams app.js inserts wherever the manuscript runs past a page.
   Two full margins stacked back to back (~2.7in) read as a hole in
   the document, so this is about one margin's worth of space in
   total: ~0.5in before the seam, ~0.5in after the page number. In
   print the seam forces the real page break, so the two can never
   drift apart. */
.sp-page-break { height: 16px; box-sizing: border-box; margin: 0.5in -1in 0 -1.5in; background: var(--sh-bg); }
.sp-page-number {
  display: block;
  text-align: right;
  margin: 0 0 0.5in;
  font-family: 'Courier Prime', 'Courier New', Courier, monospace;
  font-size: 12pt;
  color: var(--sb-ink-4);
}

/* Title page — same page shape, centred static content. Conventionally
   unnumbered, so app.js leaves it out of the pagination. */
.sp-title-page { display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; cursor: default; }
.sp-title-page-title { font-weight: 700; text-decoration: underline; text-transform: uppercase; margin-bottom: 2.5em; word-break: break-word; }
.sp-title-page-byline > div { margin-bottom: 0.3em; }
/* A direct child of the unpadded printable wrapper, so unlike
   .sp-page-break it is sized and centred rather than bled out. */
.sp-title-divider { width: 210mm; max-width: 100%; height: 16px; margin: 0.5in auto; background: var(--sh-bg); }

.screenplay-autocomplete { position: fixed; z-index: 150; min-width: 180px; max-height: 240px; overflow-y: auto; }
.sp-ac-item {
  padding: 8px 12px;
  border-radius: 8px;
  font-family: 'Courier Prime', 'Courier New', Courier, monospace;
  font-size: 13px;
  color: var(--sb-ink-2);
  white-space: nowrap;
  cursor: pointer;
}
.sp-ac-item.active,
.sp-ac-item:hover { background: rgba(232, 83, 53, 0.16); color: var(--sb-ink); }


/* ============================================================
   24. RESPONSIVE

   Four breakpoints, each with a reason rather than a round number:

     1100px  the locations table stops being a table: six columns of
             controls become a name with its controls wrapped
             underneath, at their own size.
      900px  the screenplay stops being a fixed A4 sheet and becomes a
             fluid page with percentage indents.
      860px  the two columns stack — the width the board has always
             used. The calendar is full width below them either way, so
             nothing swaps sides on the way down.
      720px  phone. One column, one thumb: every control grows to a
             hittable size, text inputs go to 16px so iOS stops zooming
             the page when one is focused, and the rows that still had
             two things fighting for a line get a second line.
      520px  small phone. The header sheds what it can, and the
             calendar keeps only the day numbers.
   ============================================================ */

/* ---------- 1100px — the locations table stops being one --- */
@media (max-width: 1100px) {
  /* Without the column header there is nothing to line up with, so a
     location row stops being a table row: the name takes the first
     line and its controls wrap underneath at their natural size. */
  .location-table-head { display: none; }
  .location-row .location-head { display: flex; flex-wrap: wrap; gap: 10px; }
  .location-row .location-controls { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; width: 100%; }
  .location-name { flex: 1 1 100%; }
  /* Out of the table grid the selects have no column to fill, so they
     size to their own text and sit as pills on one or two lines —
     width:100% (§7) would give each of them a line of its own. */
  .location-row .location-controls > select,
  .location-row .location-controls > .location-plain-select,
  .location-row .location-controls > .location-status-select,
  .location-row .location-controls > .location-priority-select { width: auto; flex: 0 1 auto; }
  .location-row .location-controls > .icon-btn { margin-left: auto; }

  /* Same for a casting row — its two labelled columns stack, and the
     status select and chevron move to their own line rather than being
     squeezed out of the row entirely. */
  .casting-card .location-head { flex-wrap: wrap; }
  .casting-entry-head-info { grid-template-columns: minmax(0, 1fr); gap: 10px; }
  .casting-card .location-controls { width: 100%; justify-content: space-between; }
}

/* ---------- 900px — the screenplay page goes fluid --------- */
@media (max-width: 900px) {
  .screenplay-layout { flex-direction: column; }
  .screenplay-sidebar { width: 100%; position: static; max-height: 340px; }
  .screenplay-editor { width: 100%; padding: 0.7in 0.5in 0.7in 0.75in; }
  .sp-page-break { margin: 0.3in -0.5in 0 -0.75in; }
  .sp-page-number { margin: 0 0 0.3in; }
  .sp-character { padding-left: 22%; }
  .sp-parenthetical { padding-left: 16%; padding-right: 22%; }
  .sp-dialogue { padding-left: 8%; padding-right: 16%; }
  .sp-title-divider { width: 100%; }
}

/* ---------- 860px — the two columns stack ------------------
   The same width the board has always used.

   Above it: the crew list on the left, and on the right the window
   finder with the calendar under it. Note that the calendar really is
   inside the right column — app.js moves #calendarSection into
   .main at runtime (relocateCalendarSection), even though the markup
   declares it after the layout.

   Below it the three blocks stack as: finder, crew list, calendar. Side
   by side the list is a column you read *across* — glance left at who
   is included, look right at the month. In one column that has to
   become a vertical relationship, and a twelve-person list above
   everything else pushes both the finder and the calendar off the
   screen. Between them it stays what it is: the thing you set, directly
   above the month it filters.

   That order needs all three to be siblings, and they are not — the
   calendar is a child of .main. So .main becomes display:contents for
   the duration: its box disappears and its two children join the
   sidebar as items of the same single-column grid, where `order` can
   place them. */
@media (max-width: 860px) {
  .app { padding: 28px 20px 48px; }
  .layout { grid-template-columns: minmax(0, 1fr); }
  #layoutRoot .main { display: contents; }
  #layoutRoot .main > #finderPanel { order: 1; }
  #layoutRoot .sidebar { order: 2; }
  #layoutRoot #calendarSection { order: 3; }

  /* A list that scrolls inside a page that scrolls is a trap on a
     touch screen; full width, the list is simply part of the page and
     the names in it scale with the row. */
  .people-scroll { max-height: none; overflow: visible; padding: 2px; }
}

/* ---------- 720px — phone ---------------------------------- */
@media (max-width: 720px) {
  .app { padding: 22px 16px 48px; }
  .panel { padding: 18px 16px 20px; }
  .panel--people { padding-left: 16px; padding-right: 16px; }
  .panel-sticky-header { margin: -18px -16px 14px; padding: 18px 16px 12px; }

  /* Header, two rows exactly:

       ▌STRIPBOARD                        (T)  (☀)
       ← MY PROJECTS   COORDINATOR   SIGN OUT

     .topbar-right becomes display:contents so its three children join
     the header's own flex line and can be ordered individually —
     otherwise the whole cluster wraps as one block and the header grows
     to four stacked rows before any content starts. */
  .topbar { flex-wrap: wrap; align-items: center; gap: 12px 10px; padding-bottom: 18px; }
  .topbar-right { display: contents; }
  .brand { flex: 1 1 auto; order: 1; min-width: 0; }
  .brand h1 { font-size: 24px; }
  .profile-menu-wrap { order: 2; }
  .theme-toggle { order: 3; }
  .whoami { order: 4; flex: 1 1 100%; justify-content: flex-start; gap: 8px; }
  .whoami .email { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

  /* Inline text actions are the one control that can't grow sideways,
     so they grow vertically into something a thumb can land on. */
  .btn-text { padding: 8px 2px; }

  /* The tab bar scrolls sideways; its scrollbar is chrome the phone
     doesn't need, and the last tab needs room to clear the edge. */
  .tab-bar {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding-right: 16px;
  }
  .tab-bar::-webkit-scrollbar { display: none; }
  .tab-btn { padding: 14px 12px; }

  /* The crew list is the one place where growing the touch targets
     takes room away from the thing the row is *for*. So on a phone the
     row's own furniture tightens by the same amount the controls grew,
     and the name stops ellipsising altogether: vertical space is cheap
     here and horizontal is not, so a name too long for the line wraps
     onto a second one rather than being cut. */
  .person { padding-left: 16px; }
  .person-head { gap: 6px; padding: 11px 8px 11px 0; }
  .person-body { padding: 0 8px 12px 0; }
  .pname { white-space: normal; overflow: visible; text-overflow: clip; overflow-wrap: anywhere; }

  /* Touch targets. A 30px control is a desktop control; a thumb needs
     more, so everything hittable grows for the phone only. */
  .icon-btn { width: 38px; height: 38px; font-size: 15px; }
  .btn { padding: 12px 18px; }
  .btn-mini { padding: 11px 15px; font-size: 10.5px; }
  .btn-accent { padding: 15px 24px; }
  .theme-toggle, .profile-avatar-btn { width: 38px; height: 38px; }
  .person-head input[type="checkbox"],
  .todo-item input[type="checkbox"],
  .modal-person-row input[type="checkbox"],
  .modal-field input[type="checkbox"] { width: 22px; height: 22px; }
  .person-chev.icon-btn { width: 26px; height: 30px; }
  .location-status-select,
  .location-priority-select,
  .casting-status-select,
  .candidate-status-select,
  .investment-status-select,
  .location-plain-select,
  .scene-quick-field select { padding: 11px 14px; }

  /* iOS Safari zooms the whole page in when a focused text field is
     under 16px, and then leaves it zoomed. Everything the user types
     into is therefore 16px on phones — the labels above them stay
     small, so the forms still read the same. */
  .add-form input,
  .add-form textarea,
  .search-box,
  .edit-form input,
  .edit-form textarea,
  .modal-field input:not([type="checkbox"]),
  .modal-field textarea,
  .grant-form input,
  .finder-form input,
  .candidate-add-form input,
  .location-body input,
  .project-tile-form input,
  .sp-sidebar-edit-row input,
  .range-form input[type="date"],
  .finder-form input[type="date"],
  .finder-form input[type="number"],
  .modal-field input[type="date"],
  .modal-field input[type="time"],
  .modal-field input[type="number"],
  .add-form input[type="date"],
  .add-form input[type="number"],
  .edit-form input[type="date"],
  .edit-form input[type="number"],
  #authCode { font-size: 16px; }

  /* Full-width primary actions: a phone CTA that is easy to miss is a
     phone CTA that gets missed. */
  .panel--feature > .btn-accent,
  .add-form > .btn-accent,
  .add-form .btn { width: 100%; }
  .feature-head h2, .panel--feature h2 { font-size: 19px; }

  /* Rows that still had two things competing for one line. */
  /* Label over value: a long maps URL against a right-aligned value in
     a two-column row either overflows or wraps to one word per line. */
  .location-field-row { flex-direction: column; gap: 4px; padding: 8px 0; }
  .location-field-row .value { text-align: left; }
  .location-body { padding: 14px; }

  .expense-head { flex-wrap: wrap; gap: 12px; }
  .expense-info { flex: 1 1 55%; }
  .expense-amount { min-width: 0; margin-left: auto; }
  .todo-item-actions { flex-shrink: 0; }
  .ranges { grid-template-columns: 1fr; }
  .modal-row { flex-direction: column; }
  .finder-form { gap: 12px; }
  .finder-form label, .finder-form input { width: 100%; }
  .finder-results li { flex-wrap: wrap; }
  .finder-event-btn { order: 1; margin-left: auto; }
  .scene-quick-row { gap: 12px; }
  .budget-stat { min-width: 140px; }

  /* Calendar: seven columns is non-negotiable, so the cells get
     shorter and tighter instead of narrower than a fingertip. */
  .cal-grid, .cal-weekdays { gap: 5px; }
  .day { min-height: 62px; padding: 7px 6px; gap: 2px; }
  .daynum { font-size: 12px; }
  .cal-nav { gap: 12px; }
  .cal-nav h2 { font-size: 17px; min-width: 0; }
  .cal-legend { gap: 14px; font-size: 12px; }

  /* Modals sit on a phone screen, not in the middle of a desktop one.
     dvh tracks the browser chrome as it slides in and out; vh is the
     fallback for engines that don't know it. */
  .modal-overlay { padding: 12px; align-items: flex-end; }
  .modal-box {
    padding: 20px 18px 22px;
    max-height: 88vh;
    max-height: 88dvh;
  }
}

/* ---------- 520px — small phone ---------------------------- */
@media (max-width: 520px) {
  .brand h1 { font-size: 21px; }
  .brand-divider, .project-name-label { display: none; }
  /* The address is in the account menu; on a 320–375px header it is
     the one thing that can go. What remains — back, role, sign out —
     is tightened just enough to hold one line at 375px rather than
     spilling into a third row of chrome. */
  .whoami .email { display: none; }
  .whoami .btn-mini { padding: 9px 11px; letter-spacing: 0.06em; }
  .admin-badge { padding: 6px 9px; letter-spacing: 0.1em; }
  .link-btn { padding: 9px 11px; letter-spacing: 0.06em; }
  .whoami { gap: 6px; }
  .day { min-height: 52px; padding: 6px 4px; }
  .dots { display: none; }
  .event-strip { font-size: 8px; padding: 1px 4px; letter-spacing: 0.06em; }
  .project-tile { width: 100%; }
  .covered-roles-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ============================================================
   25. PRINT — screenplay PDF export via the browser's own dialog

   visibility:hidden/visible is the standard "print only this element"
   trick: #screenplayPrintable's id selector outranks the universal
   one whatever the source order, so it and its contents stay visible
   while the rest of the app (header, sidebar, sticky menu bar, even
   empty-block placeholder text) does not.
   ============================================================ */
@media print {
  body * { visibility: hidden; }
  body::before, .sb-grain { display: none; }
  #screenplayPrintable, #screenplayPrintable * { visibility: visible; }
  #screenplayPrintable { position: absolute; top: 0; left: 0; width: 100%; }
  #screenplayTitlePage {
    /* The divider right after it forces the break onto a new page. An
       explicit height (the @page usable height) is what lets the title
       centre vertically — with no height a flex box only centres
       within its own content, i.e. not at all. */
    width: 100%;
    height: calc(297mm - 2 * 1.346in);
    min-height: 0;
    margin: 0;
    padding: 0;
    border: none;
    border-radius: 0;
    box-shadow: none;
    background: #fff;
    color: #000;
    font-size: 12pt;
  }
  .sp-title-divider { height: 0; margin: 0; background: none; break-before: page; page-break-before: always; }
  #screenplayEditor {
    position: static;
    width: 100%;
    min-height: 0;
    margin: 0;
    padding: 0;
    border: none;
    border-radius: 0;
    box-shadow: none;
    background: #fff;
    color: #000;
    font-size: 12pt;
    line-height: 1;
  }
  .sp-block:empty:before { content: none; }
  .sp-character { break-after: avoid; page-break-after: avoid; }
  .sp-parenthetical { break-inside: avoid; page-break-inside: avoid; }
  /* The same marker that draws the on-screen seam now forces the real
     break. The number is pulled up into the page's own top margin to
     sit 0.5in from the top, then pushes the next block back down to
     the 1.346in starting line. */
  .sp-page-break { height: 0; margin: 0; padding: 0; background: none; box-shadow: none; break-before: page; page-break-before: always; }
  .sp-page-number { position: static; display: block; text-align: right; margin-top: -0.846in; margin-bottom: 0.679in; color: #000; }
  @page { size: a4; margin: 1.346in 1in 1.346in 1.5in; }
}


/* ============================================================
   26. REDUCED MOTION
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; animation: none !important; }
  .panel.personal-profile-box:hover { transform: none; }
  .day:hover, .project-tile:hover { transform: none; }
}
