/* ==================================================================
   The Well — Stage Board

   Dark only. This lives on a monitor in a blacked-out backstage area, so
   nothing here is allowed to glow: the brightest things on screen are the
   faces and the names, in that order.

   It keeps the shape of the printed board it replaces — a header strip,
   then three colour bands, each with its name on a plate at the left and
   the team in a tray beside it — but the bands are deep fields rather
   than bright ones, carrying colour for identification without lighting
   up the room.

   There is no light theme on purpose. If one is ever needed it is a swap
   of the token block below, not a second set of rules.
   ================================================================== */

:root {
  color-scheme: dark;          /* native selects, date pickers, scrollbars */

  /* ground, darkest to lightest */
  --page:      #0e1218;
  --surface:   #151a21;        /* the board itself */
  --panel:     #171c23;        /* raised: cards, forms, modals */
  --panel-2:   #1e242c;
  --bar:       #161b22;        /* header strip */
  --tile:      #0e1217;        /* photo tiles */

  /* legacy aliases so structural rules keep reading naturally */
  --paper:     var(--surface);
  --paper-2:   var(--page);

  --ink:       #eef1f5;
  --ink-2:     #b9c1cb;
  --ink-3:     #838d99;
  --ink-4:     #5c6672;
  --on-bar:    #eef1f5;
  --on-bar-2:  #8b95a1;

  --rule:      rgba(255,255,255,.08);
  --rule-2:    rgba(255,255,255,.16);

  /* Band identity, in three weights.
     --plate-*  the large name plates. Measured: at the lighter values these
                were the brightest surfaces on the board AND their white
                labels sat at 3.38:1, under the 4.5:1 that 14px text needs.
                Deepening them halved the glare and took contrast to ~6:1 —
                the same change fixed both problems.
     --*        small accents (kind dots, drift text, the progress bar) that
                need to stay legible at a few pixels.
     --*-field  the tray the faces sit in: a hue-tinted dark, enough to group
                the row without lighting anything up. */
  /* Plates carry the original board's colour. They had been deepened until
     the colour was gone; these are as vivid as they can be while white
     large text still clears 3:1 on them. */
  --plate-green:  #3e9e5e;
  --plate-amber:  #c97c1f;
  --plate-blue:   #3a8fc4;
  --plate-violet: #6f57b0;

  --green:        #4fb877;
  --amber:        #d9902c;
  --blue:         #55a5da;
  --violet:       #9179d4;

  --live:      #f2555a;
  --live-deep: #3a1416;
  --good:      #3fae74;      /* small accents: status dots, on-time text */
  --accent:    #359a66;      /* filled primary buttons — a notch deeper, since
                                a solid button is the brightest thing on the
                                admin side and this is a dark-room tool */
  --good-deep: #13301f;
  --warn:      #d9982e;
  --warn-deep: #3a2a10;

  /* ---- spacing scale -------------------------------------------------
     Fixed steps, never viewport units. Padding that grew on vh while the
     text inside it grew on vw was why nothing lined up: the proportions
     of a component changed with the window's aspect ratio. */
  --s1:  4px;
  --s2:  8px;
  --s3: 12px;
  --s4: 16px;
  --s5: 20px;
  --s6: 24px;
  --s8: 32px;

  /* ---- type scale ----------------------------------------------------
     Seven steps, all growing on the SAME basis (vw) at the same rate, so
     elements sitting on a line together keep their relationship at every
     size. This replaced seventeen ad-hoc sizes on four different bases. */
  --t-2xs: clamp(8px,  .58vw, 11px);   /* chips, eyebrows, micro labels */
  --t-xs:  clamp(9px,  .68vw, 12px);   /* field labels, legends, notes  */
  --t-sm:  clamp(11px, .82vw, 14px);   /* body, list rows, buttons      */
  --t-md:  clamp(12px, .98vw, 17px);   /* card names, inputs, next-up   */
  --t-lg:  clamp(15px, 1.30vw, 23px);  /* plates, headings              */
  --t-xl:  clamp(18px, 1.75vw, 30px);  /* the date, what's on now       */
  --t-2xl: clamp(26px, 2.70vw, 46px);  /* countdowns and big numerals   */

  --radius:    16px;
  --radius-sm: 10px;
  --gap:       var(--s3);

  --font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Inter, Roboto,
          'Helvetica Neue', Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace;
}

* { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
  background: var(--page);
  color: var(--ink);
  font-family: var(--font);
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
}

::selection { background: rgba(58,140,196,.35); }

/* Scrollbars are a surprisingly bright detail on a dark wall display. */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: rgba(255,255,255,.13);
  border-radius: 99px;
  border: 2px solid transparent;
  background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,.22); background-clip: content-box; }

button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }

#app { height: 100%; display: flex; flex-direction: column; }

/* ============================================ the 16:9 board ===== */

/* The board is composed once at 1920x1080 and scaled to fit whatever it is
   shown on, letterboxed. Two things follow from that, both wanted:
   the layout is identical on every screen instead of reflowing, and every
   size inside can be a fixed pixel value rather than a viewport unit —
   the canvas never changes size, so nothing has to adapt to it. */
body[data-page=board] {
  background: #000;
}
#frame {
  position: fixed;
  inset: 0;
  overflow: hidden;
  background: #000;
}
/* Centred by transform rather than by layout: a grid will not centre an item
   larger than its container — it pins it to the start, which put the whole
   letterbox on one side. */
#frame > #app {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 1920px;
  height: 1080px;
  transform-origin: center center;
  will-change: transform;
}

/* Inside the fixed canvas the scale is absolute — 1920 wide, always. */
body[data-page=board] {
  --t-2xs: 11px;
  --t-xs:  12px;
  --t-sm:  14px;
  --t-md:  17px;
  --t-lg:  23px;
  --t-xl:  30px;
  --t-2xl: 46px;
}
body[data-page=board] .topbar { height: 76px; }
body[data-page=board] .brand-mark { height: 28px; }
body[data-page=board] #view-board { grid-template-columns: minmax(0,1fr) 470px; }
body[data-page=board] .group { grid-template-columns: 74px minmax(0,1fr); }
body[data-page=board] .rd-item {
  grid-template-columns: 50px minmax(0,1fr) 54px;
}
body[data-page=board] .group-plate { font-size: min(23px, 7cqh); }
body[data-page=board].wall .svc-date { font-size: 40px; }
body[data-page=board].wall .topbar { height: 92px; }

/* =============================================== header strip ==== */

/* Three zones — brand, date, status — on one centre line, with a two-level
   rhythm: --s4 between zones, --s3 within one. Everything sitting in the bar
   shares a single control height, so the right-hand cluster reads as a row
   rather than four differently sized things. */
.topbar {
  --control: 30px;

  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--s4);
  padding: 0 var(--s4);
  height: clamp(54px, 4.4vw, 82px);
  background: var(--bar);
  border-bottom: 1px solid var(--rule);
  color: var(--on-bar);
}

/* On the board the zones are light enough to anchor the date to the centre
   of the bar, so it does not drift with whatever sits beside it. The admin
   bar carries five tabs and an account, so it stays a flex row. */
body[data-page=board] .topbar {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
}
body[data-page=board] .brand          { justify-self: start; }
body[data-page=board] .topbar-service { justify-self: center; }
body[data-page=board] .topbar-right   { justify-self: end; }

.brand { display: flex; align-items: center; gap: 13px; flex: 0 0 auto; }

/* The supplied artwork is black on transparency. Rather than keeping a
   second, recoloured copy of someone's logo, it is rendered white here —
   brightness(0) flattens it to black, invert(1) takes it to white, and
   the alpha channel (including the antialiased edges) is untouched. */
.brand-mark { display: block; height: clamp(18px, 1.5vw, 28px); }
.brand-mark img {
  height: 100%;
  width: auto;
  display: block;
  filter: brightness(0) invert(1);
}
.brand-sub::before {
  content: '';
  display: inline-block;
  width: 1px; height: 9px;
  background: rgba(255,255,255,.28);
  margin-right: 9px;
  vertical-align: -1px;
}
.brand-name {
  font-size: var(--t-md);
  font-weight: 800;
  letter-spacing: .2em;
  text-transform: uppercase;
  line-height: 1;
}
.brand-sub {
  font-size: var(--t-2xs);
  letter-spacing: .24em;
  text-transform: uppercase;
  color: var(--on-bar-2);
  white-space: nowrap;
}

.topbar-service {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  display: flex;
  align-items: center;      /* baseline dropped the service time off the line */
  justify-content: flex-end;
  gap: var(--s3);
}
.topbar-service > * { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.svc-date {
  font-size: var(--t-xl);
  font-weight: 900;
  letter-spacing: .045em;
  text-transform: uppercase;
  white-space: nowrap;
  line-height: 1;
}
.svc-time {
  font-size: var(--t-sm);
  color: var(--warn);
  font-weight: 800;
  letter-spacing: .1em;
  text-transform: uppercase;
  white-space: nowrap;
}

.topbar-right { flex: 0 0 auto; display: flex; align-items: center; gap: var(--s3); }

/* one height for every control in the bar */
.topbar-right > * { height: var(--control); }
.clock, .pill, .topbar .tab, .whoami { display: inline-flex; align-items: center; }

.clock {
  font-family: var(--mono);
  font-size: var(--t-md);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--on-bar);
}

.tabs { display: flex; gap: 3px; }
.tab {
  background: transparent;
  border: 1px solid transparent;
  border-radius: 999px;
  padding: 0 var(--s3);
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--on-bar-2);
}
.tab:hover { color: var(--on-bar); background: rgba(255,255,255,.07); }
.tab.is-active { color: var(--bar); background: var(--on-bar); border-color: var(--on-bar); }
a.tab { text-decoration: none; display: inline-flex; align-items: center; }

.icon-btn {
  background: rgba(255,255,255,.06);
  border: 1px solid var(--rule-2);
  color: var(--on-bar-2);
  border-radius: 8px;
  width: var(--control); height: var(--control);
  display: grid; place-items: center;
}
.icon-btn:hover { color: var(--on-bar); }

.pill {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 0 var(--s3);
  border-radius: 999px;
  background: rgba(255,255,255,.06);
  border: 1px solid var(--rule-2);
  font-size: var(--t-xs); font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--on-bar-2);
  white-space: nowrap;
}
.pill-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--ink-4); flex: 0 0 auto; }
.pill-dot.ok   { background: var(--good); box-shadow: 0 0 7px rgba(63,174,116,.55); }
.pill-dot.warn { background: var(--warn); }
.pill-dot.bad  { background: var(--live); animation: pulse 2s infinite; }

.whoami {
  font-size: var(--t-xs); color: var(--on-bar-2);
  max-width: 170px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* =============================================== views =========== */

.views { flex: 1 1 auto; min-height: 0; position: relative; }
.view { display: none; height: 100%; }
.view.is-active { display: block; }

.view-scroll { height: 100%; overflow-y: auto; padding: var(--s6) var(--s6) 60px; background: var(--page); }
.view-scroll.narrow { max-width: 1080px; margin: 0 auto; }

/* =============================================== the board ======= */

#view-board.is-active { display: grid; }
#view-board {
  grid-template-columns: minmax(0, 1fr) clamp(320px, 26vw, 440px);
  grid-template-rows: minmax(0, 1fr);
  height: 100%;
  background: var(--surface);
}

/* A background image, when one is set, sits behind the whole board under a
   scrim whose strength is configurable — bold artwork, legible names. */
#view-board {
  position: relative;
  isolation: isolate;
}
#view-board::before {
  content: '';
  position: absolute; inset: 0;
  background-image: var(--board-bg, none);
  background-size: cover;
  background-position: center;
  z-index: -2;
}
#view-board::after {
  content: '';
  position: absolute; inset: 0;
  background: rgba(6,9,13, calc(var(--board-dim, 55) / 100));
  z-index: -1;
  display: var(--board-scrim, none);
}
body.has-bg #view-board { background: transparent; }
body.has-bg .stage,
body.has-bg .rundown { background: transparent; }
body.has-bg .rd-head,
body.has-bg .rd-progress,
body.has-bg .rd-list,
body.has-bg .transport,
body.has-bg .rd-pre { background: rgba(10,14,19,.72); }

.stage {
  min-width: 0;
  padding: var(--s3);
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  overflow: hidden;
  background: var(--surface);
}

.group {
  flex: 1 1 0;
  min-height: 0;
  display: grid;
  grid-template-columns: clamp(44px, 4vw, 74px) minmax(0, 1fr);
  gap: var(--s3);
  overflow: hidden;
}

/* Vertical nowrap text reports its whole length as min-content height, so
   the "PASTORS & MCS" plate was forcing a 218px row and overflowing 40px
   into the next band wherever rows are shorter than that — which is every
   row on the Assign tab. Grid items must be allowed to shrink. */
.group > * { min-height: 0; min-width: 0; }

/* Set on its side, reading upward — VOCALS with the V at the bottom. Takes
   far less width than a horizontal plate, and the faces get it back. */
.group-plate {
  border-radius: var(--radius);
  display: grid;
  place-items: center;
  padding: var(--s3) var(--s1);
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  text-align: center;
  overflow: hidden;
  font-size: var(--t-lg);
  font-weight: 900;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: #fff;
}

/* However many people are on, they share the row evenly. Flex rather than
   grid so the count can change week to week without a template to match:
   three pastors fill the row exactly as the printed board does, seven band
   members divide it, and a row with only one or two is capped and centred
   instead of stretching into a letterbox. */
.group-rail {
  border-radius: var(--radius);
  padding: var(--s2);
  display: flex;
  justify-content: center;
  gap: var(--s2);
  min-width: 0;
  overflow: hidden;
}
.group-rail > .card {
  flex: 1 1 0;
  min-width: 0;
  max-width: 520px;
}

/* Plate and tray are the SAME colour — one continuous band, as the printed
   board has it. The position label below is that colour too, so it reads as
   the band dropping into the tile rather than a chip sitting on top of it. */
.grp-green  .group-plate,
.grp-green  .group-rail  { background: var(--plate-green); }
.grp-amber  .group-plate,
.grp-amber  .group-rail  { background: var(--plate-amber); }
.grp-blue   .group-plate,
.grp-blue   .group-rail  { background: var(--plate-blue); }
.grp-violet .group-plate,
.grp-violet .group-rail  { background: var(--plate-violet); }

/* ---- a person on a position ---- */

.card {
  position: relative;
  min-width: 0;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--tile);
  display: flex;
  flex-direction: column;
  box-shadow: 0 2px 8px rgba(0,0,0,.4);
}
.card-photo {
  flex: 1 1 auto;
  min-height: 0;
  position: relative;
  overflow: hidden;
  background: var(--panel);
}
.card-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }

.card-initials {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  font-size: var(--t-2xl);
  font-weight: 800;
  letter-spacing: .04em;
  color: rgba(255,255,255,.42);
}

/* Flush into the tile's top-left corner and dropping down out of the band,
   in the band's own colour, so the position reads as part of the row rather
   than a sticker on the photo. Dark text: on these mid-brightness colours it
   carries far better than white (roughly 5:1 against about 3.3:1). */
.card-slot {
  position: absolute;
  top: 0; left: 0;
  border-radius: var(--radius-sm) 0 var(--radius-sm) 0;
  padding: var(--s2) var(--s3) calc(var(--s2) + 1px) var(--s3);
  font-size: var(--t-sm);
  font-weight: 900;
  letter-spacing: .08em;
  text-transform: uppercase;
  line-height: 1;
  color: #0a0e12;
}
.grp-green  .card-slot { background: var(--plate-green); }
.grp-amber  .card-slot { background: var(--plate-amber); }
.grp-blue   .card-slot { background: var(--plate-blue); }
.grp-violet .card-slot { background: var(--plate-violet); }

.card-name {
  flex: 0 0 auto;
  padding: var(--s2) var(--s2);
  line-height: 1.25;
  text-align: center;
  font-size: var(--t-md);
  font-weight: 800;
  letter-spacing: .07em;
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: #fff;
  background: rgba(0,0,0,.55);
}

/* A row nobody is on still draws its band so the board's shape never
   changes — this just says so quietly, rather than leaving a bare field of
   colour. Dark and low-contrast on purpose: it is a note, not a headline. */
.rail-empty {
  align-self: center;
  margin: auto;
  font-size: var(--t-sm);
  font-weight: 800;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: rgba(8, 12, 16, .38);
}

/* An open position should read as absence, not as another lit tile. */
.card.is-empty {
  background: rgba(0,0,0,.22);
  box-shadow: none;
  border: 1px dashed rgba(255,255,255,.2);
}
.card.is-empty .card-photo { background: transparent; }
.card.is-empty .card-initials { color: rgba(255,255,255,.18); font-size: var(--t-lg); }
.card.is-empty .card-name { background: transparent; color: rgba(255,255,255,.42); }
.card.is-empty .card-slot { opacity: .6; }

/* who is on right now */
.card.is-up::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: var(--radius-sm);
  box-shadow: inset 0 0 0 3px var(--live), 0 0 22px rgba(242,85,90,.32);
  pointer-events: none;
}
.card-live-chip {
  position: absolute;
  top: var(--s2); right: var(--s2);
  background: var(--live);
  color: #2a0709;
  border-radius: 5px;
  padding: 2px var(--s2);
  font-size: var(--t-2xs);
  font-weight: 900;
  letter-spacing: .12em;
}

.assignable .card { cursor: pointer; transition: transform .1s ease; }
.assignable .card:hover { transform: translateY(-2px); }
.assignable .card::before {
  content: 'TAP TO ASSIGN';
  position: absolute; inset: auto 0 38% 0;
  text-align: center;
  font-size: var(--t-2xs); font-weight: 800; letter-spacing: .12em;
  color: transparent;
  transition: color .12s ease;
  z-index: 2;
}
.assignable .card:hover::before { color: #fff; text-shadow: 0 1px 3px rgba(0,0,0,.85); }

/* The label runs down the plate, so its size is governed by the row's
   HEIGHT, not the viewport. cqh ties it to exactly that: full size on the
   wall where rows are tall, smaller on the Assign tab where they are not,
   and never clipped either way. */
@supports (container-type: size) {
  .group { container-type: size; }
  .group-plate { font-size: max(9px, min(var(--t-lg), 7cqh)); }
}

/* =============================================== run of show ===== */

.rundown {
  min-width: 0;
  border-left: 1px solid var(--rule);
  background: var(--page);
  display: flex;
  flex-direction: column;
}

.rd-head { flex: 0 0 auto; padding: var(--s4); border-bottom: 1px solid var(--rule); background: var(--surface); }
.rd-eyebrow {
  display: flex; align-items: center; gap: 8px;
  font-size: var(--t-2xs); font-weight: 800; letter-spacing: .18em;
  text-transform: uppercase; color: var(--ink-3);
  margin-bottom: var(--s2);
}
.live-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--live);
  animation: pulse 2s infinite;
}
.live-dot.idle { background: var(--ink-4); animation: none; }
@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 rgba(242,85,90,.5); }
  70%  { box-shadow: 0 0 0 9px rgba(242,85,90,0); }
  100% { box-shadow: 0 0 0 0 rgba(242,85,90,0); }
}

.rd-now-title {
  font-size: var(--t-xl);
  font-weight: 800;
  line-height: 1.15;
  color: var(--ink);
}
.rd-now-meta {
  margin-top: var(--s2);
  display: flex; align-items: baseline; gap: 12px;
  color: var(--ink-3); font-size: var(--t-xs); font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase;
}
.rd-countdown {
  font-family: var(--mono);
  font-size: var(--t-2xl);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  /* 1 clipped the numerals' descenders by a few px at large sizes */
  line-height: 1.1;
  margin-top: var(--s3);
  color: var(--ink);
}
.rd-countdown.over { color: var(--live); }

.rd-next {
  margin-top: var(--s3); padding-top: var(--s3);
  border-top: 1px dashed var(--rule-2);
  display: flex; align-items: center; gap: 10px; min-width: 0;
}
.rd-next-label {
  font-size: var(--t-2xs); font-weight: 800; letter-spacing: .18em;
  text-transform: uppercase; color: var(--warn); flex: 0 0 auto;
}
.rd-next-title {
  font-size: var(--t-md); font-weight: 700; color: var(--ink-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.rd-progress { padding: var(--s4); border-bottom: 1px solid var(--rule); background: var(--surface); }
.bar { height: 8px; border-radius: 99px; background: rgba(255,255,255,.09); overflow: hidden; }
.bar-fill {
  height: 100%; border-radius: 99px;
  background: linear-gradient(90deg, var(--green), var(--blue));
  transition: width .5s linear;
}
.bar-legend {
  display: flex; justify-content: space-between;
  margin-top: var(--s2);
  font-size: var(--t-xs); font-weight: 700; letter-spacing: .04em;
  color: var(--ink-3); font-variant-numeric: tabular-nums;
}
.drift.ok    { color: var(--good); }
.drift.over  { color: var(--live); }
.drift.under { color: var(--blue); }

/* how long before the next service — the number that matters on a Sunday */
.rd-turn {
  display: flex; align-items: baseline; gap: 9px;
  margin-top: var(--s3); padding: var(--s2) var(--s3);
  border-radius: 9px; border: 1px solid transparent;
}
.rd-turn-label {
  font-size: var(--t-2xs); font-weight: 800; letter-spacing: .16em;
  text-transform: uppercase; opacity: .8; flex: 0 0 auto;
}
.rd-turn-value { font-size: var(--t-sm); font-weight: 800; }
.rd-turn.ok   { background: var(--good-deep); border-color: rgba(63,174,116,.35);  color: #7fd6a5; }
.rd-turn.warn { background: var(--warn-deep); border-color: rgba(217,152,46,.38);  color: #f0c078; }
.rd-turn.bad  { background: var(--live-deep); border-color: rgba(242,85,90,.42);   color: #ff9a9d; }

.stage-empty {
  flex: 1 1 auto;
  display: grid;
  place-content: center;
  text-align: center;
  color: var(--ink-3);
  font-size: var(--t-md);
  line-height: 1.7;
}
.stage-empty p { margin: 0; }
.stage-empty b { color: var(--ink); font-size: var(--t-lg); }

.rd-empty { padding: var(--s6) var(--s4); color: var(--ink-3); font-size: var(--t-sm); line-height: 1.6; }
.rd-empty p { margin: 0 0 8px; }
.rd-empty b { color: var(--ink); }

.rd-list { flex: 1 1 auto; overflow-y: auto; padding: var(--s2) var(--s3) var(--s4); }

.rd-item {
  display: grid;
  /* Both fixed columns scale with the type on the same basis, so they
     stay in proportion instead of the text outgrowing its column. */
  grid-template-columns: clamp(38px, 2.9vw, 50px) minmax(0,1fr) clamp(40px, 3.1vw, 54px);
  align-items: center;
  gap: var(--s3);
  padding: var(--s2) var(--s3);
  border-radius: 9px;
  border-left: 3px solid transparent;
  color: var(--ink-3);
}
.rd-item + .rd-item { margin-top: 2px; }
.rd-item .t { font-family: var(--mono); font-size: var(--t-sm); font-variant-numeric: tabular-nums; }
.rd-item .title {
  font-size: var(--t-md); font-weight: 600; color: var(--ink-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.rd-item .dur { font-family: var(--mono); font-size: var(--t-sm); text-align: right; font-variant-numeric: tabular-nums; }
.rd-item .note {
  grid-column: 2 / -1;
  font-size: var(--t-xs); color: var(--ink-4); margin-top: 2px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.rd-item.done { opacity: .4; }
/* The lead's name sits inside the note line but is the part worth finding at
   a glance, so it lifts to the body ink while the key and arrangement stay
   at note weight. */
.rd-item .note .rd-lead { color: var(--ink-3); font-weight: 700; }
.rd-item.done .title { text-decoration: line-through; text-decoration-thickness: 1px; }
.rd-item.now { background: var(--live-deep); border-left-color: var(--live); }
.rd-item.now .title { color: #ffb3b5; font-weight: 800; }
.rd-item.next { background: var(--warn-deep); border-left-color: var(--warn); }

.kind-dot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; margin-right: 7px; vertical-align: middle; }
.k-worship { background: var(--blue); }
.k-word    { background: var(--green); }
.k-host    { background: var(--amber); }
.k-media   { background: var(--violet); }
.k-pre     { background: var(--ink-4); }

.transport {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: 6px;
  padding: var(--s3);
  border-top: 1px solid var(--rule);
  background: var(--surface);
}
.tbtn {
  flex: 1 1 auto;
  background: var(--panel-2);
  border: 1px solid var(--rule-2);
  border-radius: 8px;
  padding: var(--s2) var(--s1);
  font-size: var(--t-xs); font-weight: 800; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-2);
}
.tbtn:hover { background: #262e38; color: var(--ink); }
.tbtn.primary { background: var(--live); border-color: var(--live); color: #2a0709; }
.tbtn.primary:hover { filter: brightness(1.08); background: var(--live); }
.tbtn.on { background: var(--good-deep); border-color: var(--good); color: #7fd6a5; }

.rd-pre { padding: var(--s4); text-align: center; border-bottom: 1px solid var(--rule); background: var(--surface); }
.rd-pre .lbl { font-size: var(--t-2xs); letter-spacing: .2em; text-transform: uppercase; color: var(--ink-3); font-weight: 800; }
.rd-pre .big {
  font-family: var(--mono);
  font-size: var(--t-2xl);
  font-weight: 700; font-variant-numeric: tabular-nums;
  margin-top: var(--s2); color: var(--ink);
}

/* =============================================== forms + admin === */

.section { margin-bottom: var(--s6); }
.section-head { display: flex; align-items: center; gap: var(--s3); margin-bottom: var(--s3); }
h2.sec-title {
  margin: 0;
  font-size: var(--t-xs); font-weight: 800; letter-spacing: .18em;
  text-transform: uppercase; color: var(--ink-3);
}
.sec-rule { flex: 1 1 auto; height: 1px; background: var(--rule); }

.btn {
  background: var(--panel);
  border: 1px solid var(--rule-2);
  border-radius: 8px;
  padding: var(--s2) var(--s4);
  font-size: var(--t-sm); font-weight: 700; letter-spacing: .04em;
  color: var(--ink);
}
.btn:hover { background: var(--panel-2); border-color: rgba(255,255,255,.26); }
.btn.accent { background: var(--accent); border-color: var(--accent); color: #06210f; }
.btn.accent:hover { filter: brightness(1.1); background: var(--accent); }
.btn.danger { color: #ff9a9d; border-color: rgba(242,85,90,.38); }
.btn.danger:hover { background: var(--live-deep); }
.btn.sm { padding: 5px 10px; font-size: var(--t-xs); }
/* Import caveats — a plan that opens before the service clock, an item with no
   length. Warnings about the file, so they carry the bar's warn colour rather
   than reading as ordinary help text. */
/* Opt-in to overwriting live assignments — the checkbox needs to sit on the
   first line of the text, not centred against a wrapped paragraph. */
.hint.import-people {
  display: flex;
  align-items: flex-start;
  gap: var(--s2);
  margin: var(--s4) 0 var(--s2);
  max-width: 62ch;
}
.hint.import-people input { margin-top: 2px; flex: 0 0 auto; }

/* These rows borrow .rd-item, whose first column is sized for a duration
   ("5:00"). A service label — "Thursday 7:00" — wraps to two lines in that
   width and lands underneath the name. Widen the column here only, and stop
   it wrapping, rather than loosening .rd-item for the run of show too. */
.import-people-list .rd-item {
  grid-template-columns: minmax(94px, auto) minmax(0, 1fr) auto;
}
.import-people-list .rd-item .t { white-space: nowrap; }

/* .hint is declared further down, so an equal-specificity .import-warn would
   lose the colour on source order alone. Qualified so it cannot. */
.hint.import-warn {
  margin-top: 0;
  color: #f0c078;
  background: var(--warn-deep);
  border-left: 2px solid var(--warn);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  padding: var(--s2) var(--s3);
}
/* .btn sits on both <button> and <a>. An anchor is inline and underlined by
   default, so without this it carries an underline its neighbouring button
   does not, and its line box lands a pixel or two off theirs. */
a.btn { text-decoration: none; display: inline-flex; align-items: center; }

.field { display: flex; flex-direction: column; gap: var(--s1); }
.field label {
  font-size: var(--t-2xs); font-weight: 800; letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-3);
}
input[type=text], input[type=date], input[type=time], input[type=number],
input[type=email], input[type=password], input[type=search],
select, textarea {
  background: var(--page);
  border: 1px solid var(--rule-2);
  border-radius: 8px;
  padding: var(--s2) var(--s3);
  font-size: var(--t-sm);
  color: var(--ink);
  outline: none;
}
input::placeholder, textarea::placeholder { color: var(--ink-4); }
input:focus, select:focus, textarea:focus {
  border-color: var(--blue);
  box-shadow: 0 0 0 3px rgba(58,140,196,.22);
}
textarea { resize: vertical; font-family: var(--mono); font-size: var(--t-sm); line-height: 1.5; }
input[type=checkbox] { accent-color: var(--good); }

.row { display: flex; gap: var(--s3); flex-wrap: wrap; align-items: flex-end; }
.hint { font-size: var(--t-sm); color: var(--ink-3); line-height: 1.55; }

.svc-chips { display: flex; gap: 6px; flex-wrap: wrap; }
.svc-chip {
  background: var(--panel);
  border: 1px solid var(--rule-2);
  border-radius: 999px;
  padding: var(--s2) var(--s4);
  font-size: var(--t-sm); font-weight: 700;
  color: var(--ink-2);
  white-space: nowrap;
}
.svc-chip:hover { background: var(--panel-2); color: var(--ink); }
.svc-chip.is-active { background: var(--blue); border-color: var(--blue); color: #04121f; }
.svc-chip .filled { font-size: var(--t-xs); opacity: .8; margin-left: 6px; }

.roster-grid { display: block; }
.roster-group + .roster-group { margin-top: var(--s6); }
.roster-grid-inner {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: var(--s3);
}
.roster-swatch { width: 10px; height: 10px; border-radius: 3px; flex: 0 0 auto; }
.roster-swatch.grp-green  { background: var(--plate-green); }
.roster-swatch.grp-amber  { background: var(--plate-amber); }
.roster-swatch.grp-blue   { background: var(--plate-blue); }
.roster-swatch.grp-violet { background: var(--plate-violet); }

.roster-search, .pick-search {
  width: 240px;
  background: var(--page);
  border: 1px solid var(--rule-2);
  border-radius: 999px;
  padding: var(--s2) var(--s4);
  font-size: var(--t-sm);
}
.pick-search { width: 100%; margin-bottom: var(--s4); border-radius: 10px; }

/* a person can hold more than one position; the one being edited is marked */
.pick.is-here { border-color: var(--good); box-shadow: 0 0 0 2px rgba(63,174,116,.35); }
.pick[hidden] { display: none; }

.bg-preview {
  width: 176px; height: 99px;            /* 16:9, like the board */
  border-radius: 8px;
  border: 1px solid var(--rule-2);
  background-color: var(--page);
  background-size: cover;
  background-position: center;
}
.bg-preview.is-empty::after {
  content: 'none';
  display: grid; place-items: center; height: 100%;
  font-size: var(--t-xs); color: var(--ink-4);
  letter-spacing: .14em; text-transform: uppercase;
}
input[type=range] { accent-color: var(--good); }
.person {
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.person-photo { aspect-ratio: 4 / 3; position: relative; background: var(--tile); }
.person-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.person-body { padding: var(--s3); }
.person-name { font-weight: 800; font-size: var(--t-md); }
.person-roles { font-size: var(--t-2xs); color: var(--ink-3); letter-spacing: .12em; text-transform: uppercase; margin-top: 4px; }
.person-acts { display: flex; gap: 6px; margin-top: 9px; }

.plan-table { width: 100%; border-collapse: collapse; background: var(--surface); }
.plan-table th {
  text-align: left; font-size: var(--t-2xs); letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-3); font-weight: 800;
  padding: var(--s2); border-bottom: 1px solid var(--rule-2);
}
.plan-table td { padding: var(--s1) var(--s2); border-bottom: 1px solid var(--rule); vertical-align: middle; }
.plan-table input[type=text] { width: 100%; }
.plan-table .dur-in { width: 74px; text-align: center; font-family: var(--mono); }
.plan-table .drag { color: var(--ink-4); cursor: grab; user-select: none; }
.plan-table tr.dragging { opacity: .4; }
.plan-table tr.drop-target td { box-shadow: inset 0 2px 0 var(--blue); }

/* =============================================== modal + toast === */

.modal-backdrop {
  position: fixed; inset: 0;
  background: rgba(0,0,0,.7);
  backdrop-filter: blur(3px);
  display: none;
  align-items: center; justify-content: center;
  z-index: 60; padding: 24px;
}
.modal-backdrop.is-open { display: flex; }
.modal {
  background: var(--panel);
  border: 1px solid var(--rule-2);
  border-radius: 16px;
  width: min(760px, 100%);
  max-height: 86vh;
  display: flex; flex-direction: column;
  box-shadow: 0 30px 80px rgba(0,0,0,.7);
}
.modal-head { padding: var(--s4); border-bottom: 1px solid var(--rule); display: flex; align-items: center; gap: 12px; }
.modal-title { font-size: var(--t-md); font-weight: 800; letter-spacing: .1em; text-transform: uppercase; }
.modal-body { padding: var(--s4); overflow-y: auto; }
.modal-foot { padding: var(--s3) var(--s4); border-top: 1px solid var(--rule); display: flex; justify-content: flex-end; gap: 8px; }

.picker-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(112px, 1fr)); gap: 10px; }
.pick {
  background: var(--panel-2);
  border: 1px solid var(--rule-2);
  border-radius: 10px;
  overflow: hidden; text-align: center; padding: 0 0 8px;
  color: var(--ink);
}
.pick:hover { border-color: var(--blue); box-shadow: 0 0 0 3px rgba(58,140,196,.2); }
.pick.is-taken { opacity: .4; }
.pick-photo { aspect-ratio: 1; position: relative; background: var(--tile); }
.pick-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pick-name { font-size: var(--t-sm); font-weight: 700; margin-top: 7px; }
.pick-sub { font-size: var(--t-xs); color: var(--ink-3); margin-top: 2px; }

.toast {
  position: fixed; left: 50%; bottom: 26px;
  transform: translateX(-50%) translateY(20px);
  background: var(--panel-2); color: var(--ink);
  border: 1px solid var(--rule-2);
  border-radius: 10px;
  padding: var(--s3) var(--s4);
  font-size: var(--t-sm); font-weight: 600;
  opacity: 0; pointer-events: none;
  transition: opacity .2s ease, transform .2s ease;
  z-index: 80;
  box-shadow: 0 12px 34px rgba(0,0,0,.6);
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

.ro-banner {
  display: none;
  align-items: center; gap: 12px;
  padding: var(--s3) var(--s4); margin-bottom: var(--s4);
  border-radius: 10px;
  background: var(--warn-deep);
  border: 1px solid rgba(217,152,46,.38);
  font-size: var(--t-sm); color: #f0c078;
}
body.read-only .ro-banner { display: flex; }

body.read-only .assignable .card { cursor: default; }
body.read-only .assignable .card:hover { transform: none; }
body.read-only .assignable .card:hover::before { color: transparent; }
body.read-only .person-acts,
body.read-only .plan-table [data-act=del],
body.read-only #btn-add-person,
body.read-only #btn-add-item,
body.read-only #btn-copy-from,
body.read-only #btn-clear-svc,
body.read-only #btn-parse,
body.read-only #btn-pick-file,
body.read-only #btn-import { display: none; }
body.read-only .plan-table input,
body.read-only .plan-table select,
body.read-only #pos-editor input,
body.read-only #pos-editor select,
body.read-only #pos-editor button,
body.read-only #svc-editor input,
body.read-only #svc-editor select,
body.read-only #set-brand,
body.read-only #set-logo,
body.read-only #week-of,
body.read-only #paste-plan { pointer-events: none; opacity: .5; }

/* Fullscreen is the wall. Drop the tabs — nobody is navigating a monitor —
   and let the date run at the size it has on the printed board. The
   transport stays, so a producer can still drive from a fullscreen laptop. */
body.wall .tabs, body.wall .icon-btn { display: none; }
body.wall .svc-date { font-size: clamp(22px, 2.4vw, 40px); }
body.wall .topbar { height: clamp(60px, 5.2vw, 96px); }

/* =============================================== the gate ======== */

.gate {
  position: fixed; inset: 0;
  display: grid; place-items: center;
  padding: var(--s6);
  background: var(--page);
  overflow-y: auto;
  z-index: 40;
}
.gate[hidden] { display: none; }

.gate-card {
  width: min(400px, 100%);
  background: var(--panel);
  border: 1px solid var(--rule-2);
  border-radius: 16px;
  padding: var(--s6);
  box-shadow: 0 24px 70px rgba(0,0,0,.6);
}
.gate-brand {
  display: flex; align-items: center; gap: 11px;
  padding-bottom: var(--s4); margin-bottom: var(--s5);
  border-bottom: 1px solid var(--rule);
}
/* the same black-on-transparent artwork, on a dark card */
.gate-brand img { height: 21px; width: auto; display: block; filter: brightness(0) invert(1); }
.gate-sub {
  font-size: var(--t-2xs); font-weight: 800; letter-spacing: .22em;
  text-transform: uppercase; color: var(--ink-3);
  padding-left: 11px; border-left: 1px solid var(--rule-2);
}
.gate-title { margin: 0 0 var(--s2); font-size: var(--t-lg); font-weight: 800; letter-spacing: -.01em; }
.gate-lede { margin: 0 0 var(--s5); font-size: var(--t-sm); line-height: 1.6; color: var(--ink-3); }
.gate-lede b { color: var(--ink); }

.gate-form { display: flex; flex-direction: column; gap: var(--s4); }
.gate-form label {
  display: flex; flex-direction: column; gap: 6px;
  font-size: var(--t-2xs); font-weight: 800; letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-3);
}
.gate-form input { font-size: var(--t-md); padding: var(--s3); }
.gate-note {
  font-size: var(--t-xs); letter-spacing: 0; text-transform: none;
  font-weight: 500; color: var(--ink-4);
}
.gate-go { width: 100%; padding: var(--s3); font-size: var(--t-sm); margin-top: 2px; }
.gate-go:disabled { opacity: .55; cursor: default; }

.gate-error {
  margin: 0; padding: var(--s2) var(--s3);
  background: var(--live-deep); border: 1px solid rgba(242,85,90,.42);
  border-radius: 8px;
  font-size: var(--t-sm); color: #ff9a9d;
}
.gate-error[hidden] { display: none; }

.gate-alt {
  display: flex; justify-content: space-between; gap: 12px;
  margin-top: var(--s4); padding-top: var(--s4); border-top: 1px solid var(--rule);
}
.linkish {
  background: none; border: 0; padding: 0;
  font-size: var(--t-sm); font-weight: 600; color: #6fb3e0;
  text-decoration: underline; text-underline-offset: 3px;
}
.linkish:hover { color: var(--ink); }
.gate-back {
  display: block; margin-top: 18px; text-align: center;
  font-size: var(--t-sm); color: var(--ink-4); text-decoration: none;
}
.gate-back:hover { color: var(--ink-2); }

/* ---- admin shell ---- */

/* #app measures itself against this, so it has to carry the height or the
   whole view collapses to its content. */
#admin-shell { height: 100%; }
#admin-shell[hidden] { display: none; }

/* the admin Live view: board on the left so you can see what you're driving */
/* Tall enough that faces stay recognisable while assigning, short enough
   that the controls above it stay on screen. */
#stage-assign {
  height: clamp(360px, 58vh, 620px);
  padding: 0;
}

.live-split {
  height: 100%;
  display: grid;
  grid-template-columns: minmax(0, 1fr) clamp(320px, 30vw, 430px);
  grid-template-rows: minmax(0, 1fr);
  background: var(--surface);
}

.editor-list { display: flex; flex-direction: column; gap: 1px; background: var(--rule); border-radius: 9px; overflow: hidden; }
.editor-row {
  display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr) auto;
  align-items: center; gap: 12px;
  padding: var(--s3); background: var(--panel);
}
.editor-mail { font-size: var(--t-sm); font-weight: 600; overflow: hidden; text-overflow: ellipsis; }
.editor-you {
  font-size: var(--t-2xs); font-weight: 800; letter-spacing: .12em; text-transform: uppercase;
  color: #7fd6a5; border: 1px solid rgba(63,174,116,.38); background: var(--good-deep);
  border-radius: 4px; padding: 1px 5px; margin-left: 6px;
}

/* =============================================== responsive ====== */

/* Every rule here is scoped to the admin page on purpose. The board is
   composed on a fixed 1920x1080 canvas and scaled to fit, so its layout must
   not respond to the window — but these queries measure the WINDOW. Left
   unscoped they reshaped the canvas as though it were small: at 1280x720, an
   ordinary panel resolution and what a 1920 screen reports at 150% zoom, the
   service time vanished; under 900 the 45vh cap collapsed the run of show to
   a couple of rows inside a full-height canvas. */
@media (max-width: 1360px) {
  body[data-page=admin] .topbar { gap: var(--s3); padding: 0 var(--s3); }
  body[data-page=admin] .svc-time { display: none; }
  body[data-page=admin] .tab { padding: var(--s2) 9px; }
}
@media (max-width: 1240px) {
  body[data-page=admin] .whoami { display: none; }
}
@media (max-width: 1080px) {
  body[data-page=admin] .topbar-service { display: none; }
  body[data-page=admin] .pill { display: none; }
}
@media (max-width: 980px) {
  body[data-page=admin] .brand-name,
  body[data-page=admin] .brand-sub { display: none; }
  body[data-page=admin] .brand-mark { height: 18px; }
  body[data-page=admin] .clock { font-size: var(--t-md); }
}
@media (max-width: 900px) {
  body[data-page=admin] .live-split {
    grid-template-columns: 1fr;
    grid-template-rows: minmax(0,1fr) auto;
  }
  body[data-page=admin] .rundown {
    border-left: none;
    border-top: 1px solid var(--rule);
    max-height: 45vh;
  }
}
