/* The two interface replicas — Airlive Bridge and Airlive Camera.

   Every value is lifted from each app's own source, not measured off a
   screenshot. Shared by /, /bridge and /camera so there is one copy to keep
   true to the apps; nothing else on the site may reuse these classes.
   Load order matters: bdemo.js publishes window.__airliveBridge, cdemo.js
   reads it. */

/* ════════════════════════════════════════════════════════════════════
   Airlive Bridge — interface replica.

   Every value here is lifted from the app's own source, not measured off
   a screenshot: github.com/airlive-project/airlive-bridge
     Sources/Theme/Theme.swift        colours, Spacing, Radius, ControlMetrics
     Sources/AirliveBridgeApp.swift   defaultSize 1200x760
     Sources/ContentView.swift        three zones, hairline under the title bar
     Sources/Views/ChannelsRail.swift rail 280, header, row, OrdinalBadge
     Sources/Views/OutputsRail.swift  rail 280, "Program Outputs"
     Sources/Views/MultiviewGrid.swift 4 columns, spacing 0, 16:9, CUT
     Sources/Views/Components.swift   Card, BridgeButtonStyle, SectionLabel

   The mock is built at the app's NATIVE size and scaled to whatever the
   page gives it — so these numbers stay the app's numbers.
   ════════════════════════════════════════════════════════════════════ */

.bdemo{
  /* Theme.swift */
  --bg-app:#0E0F12; --bg-panel:#15171C; --bg-rail:#101216;
  --bg-hover:#1B1E24; --bg-selected:#222630;
  --stroke:#262A33; --stroke-divider:#30343E;
  --txt:#EAEAEA; --txt-2:#A8AAB0; --txt-faint:#70747C;
  --blue:#3B82F6; --red:#D14545; --yellow:#FFD60A; --green:#3EB85C;
  --tally-pgm:#4A1E1E; --tally-pvw:#18421F;
  /* Spacing */
  --s-xxs:2px; --s-xs:4px; --s-sm:8px; --s-md:12px; --s-lg:16px; --s-xl:24px;
  /* Radius */
  --r-control:6px; --r-button:8px; --r-tile:10px; --r-panel:12px;
  /* ControlMetrics */
  --pill:28px; --chip-w:44px; --segment:30px; --tile-h:40px;

  /* defaultSize is 1200x760; the panel needs more room than that leaves the
     centre, and the window is resizable — so the mock uses a working size and
     shows everything at once instead of scrolling inside a scaled frame. */
  --win-w:1480; --win-h:980;
  --scale:1;

  font-family:Inter,system-ui,sans-serif;
  container-type:inline-size;
}

/* the frame keeps its native pixels and is scaled down as a whole —
   the trick Linear uses, so nothing inside has to be responsive */
/* the slot only clips the scaled frame — it must NOT round anything itself, or
   its corner cuts across the window's own radius */
.bdemo__slot{position:relative;width:100%;overflow:hidden;
  height:calc(var(--win-h) * 1px * var(--scale))}
.bdemo__frame{position:absolute;top:0;left:0;
  width:calc(var(--win-w) * 1px);height:calc(var(--win-h) * 1px);
  transform:scale(var(--scale));transform-origin:top left}

/* ── window chrome ───────────────────────────────────────────────── */
.bwin{width:100%;height:100%;display:flex;flex-direction:column;
  background:var(--bg-app);border-radius:var(--r-panel);overflow:hidden;
  border:1px solid var(--stroke)}
/* No title bar on the site: the block starts straight at the interface, so the
   frame carries only what the operator actually works with. */
.bwin__body{flex:1;display:flex;min-height:0}

/* ── rails (280 expanded — ChannelsRail / OutputsRail) ───────────── */
.brail{width:280px;flex:none;background:var(--bg-rail);display:flex;flex-direction:column;min-height:0}
.brail--l{border-right:1px solid var(--stroke)}
.brail--r{border-left:1px solid var(--stroke)}
.brail__head{display:flex;align-items:center;gap:var(--s-sm);
  padding:var(--s-md) var(--s-lg) var(--s-sm)}
.brail__title{font-size:14px;font-weight:600;color:var(--txt)}
.brail__spacer{flex:1}
.brail__list{flex:1;min-height:0;overflow:auto;padding:0 var(--s-lg) var(--s-md);
  display:flex;flex-direction:column;gap:var(--s-sm)}
.brail__list::-webkit-scrollbar{width:0}
/* ── latency buffer footer (ChannelsRail.swift → LatencyBufferFooter) ──
   One buffer for the whole Bridge, sat above the password row: same 44px band,
   same quiet label weight, and the VALUE behind a lock. */
.blat{position:relative;height:44px;flex:none;display:flex;align-items:center;gap:var(--s-sm);
  padding:0 var(--s-md);background:var(--bg-panel);border-top:1px solid var(--stroke)}
.blat__lbl{font-size:12px;font-weight:500;color:var(--txt-2);white-space:nowrap}
.blat__grow{flex:1;min-width:var(--s-sm)}
.blat__val{display:flex;align-items:center;gap:5px;height:26px;padding:0 var(--s-sm);
  background:var(--bg-selected);border:1px solid var(--stroke-divider);
  border-radius:var(--r-button);cursor:pointer}
.blat__val b{font:500 12px/1 Inter,sans-serif;color:var(--txt)}
.blat__val i,.blist__row i{font:400 10px/1 Inter,sans-serif;font-style:normal;color:var(--txt-faint)}
.blat__val svg{width:9px;height:9px;color:var(--txt-2);stroke-width:2.6}
/* the lock guards the VALUE, so only the value looks guarded: greying the label
   and the info dot would say the whole row is out of reach */
.blat.is-locked .blat__val{opacity:.4;pointer-events:none}
.blat__lock{width:16px;height:16px;padding:0;border:0;background:none;
  color:var(--txt-2);cursor:pointer;display:grid;place-items:center}
.blat__lock svg{width:12px;height:12px}
.blist{position:absolute;right:var(--s-md);bottom:36px;min-width:180px;padding:5px;
  display:flex;flex-direction:column;gap:1px;z-index:9;
  background:var(--bg-panel);border:1px solid var(--stroke-divider);
  border-radius:var(--r-button);box-shadow:0 6px 14px rgba(0,0,0,.45)}
.blist__row{display:flex;align-items:center;gap:8px;height:30px;padding:0 10px;
  border:0;background:none;border-radius:var(--r-control);cursor:pointer;text-align:left}
.blist__row:hover{background:var(--bg-selected)}
.blist__ck{width:14px;flex:none;display:grid;place-items:center;color:var(--txt)}
.blist__ck svg{width:10px;height:10px;stroke-width:2.8}
/* nothing goes accent-coloured: the check already says which rung is on, and a
   coloured row would shout in a list whose job is to be read evenly */
.blist__lbl{font:400 12px/1 Inter,sans-serif;color:var(--txt);white-space:nowrap}
.blist__row.is-on .blist__lbl{font-weight:500}
.blist__row b{font:500 12px/1 Inter,sans-serif;color:var(--txt)}

.bsec-foot{height:44px;flex:none;display:flex;align-items:center;gap:var(--s-sm);
  padding:0 var(--s-md);background:var(--bg-panel);border-top:1px solid var(--stroke);
  border-radius:0;cursor:pointer;width:100%;text-align:left;border-left:0;border-right:0;border-bottom:0}
.bsec-foot__ico{width:16px;flex:none;display:grid;place-items:center;color:var(--txt-faint)}
.bsec-foot__ico svg{width:12px;height:12px}
.bsec-foot__t{font-size:12px;font-weight:500;color:var(--txt-2)}

/* the boxed "+" — identical on both rails, per the source */
.bplus{width:22px;height:22px;flex:none;display:grid;place-items:center;
  border-radius:var(--r-control);background:rgba(34,38,48,.6);
  border:1px solid var(--stroke);color:var(--txt);cursor:pointer;
  font:700 11px/1 Inter,sans-serif;padding:0}
.bplus:hover{background:var(--bg-selected)}
.bchevron{width:22px;height:22px;flex:none;display:grid;place-items:center;
  color:var(--txt-2);background:none;border:0;cursor:pointer}
.bchevron svg{width:11px;height:11px}

/* ── Card — Components.swift: bgPanel, 1pt stroke, radius panel ──── */
.bcard{background:var(--bg-panel);border:1px solid var(--stroke);
  border-radius:var(--r-panel);padding:var(--s-sm)}

/* ── channel row ─────────────────────────────────────────────────── */
.brow{display:flex;flex-direction:column;gap:var(--s-sm);cursor:pointer}
.brow__top{display:flex;align-items:center;gap:var(--s-sm)}
.brow__wifi{width:var(--pill);flex:none;display:grid;place-items:center;color:var(--green)}
.brow__wifi svg{width:12px;height:12px}
.brow--off .brow__wifi{color:var(--txt-faint)}
.brow__kind{font-size:11px;font-weight:600;letter-spacing:.6px;color:var(--txt-2);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.brow__grow{flex:1}
.bicon{width:22px;height:22px;flex:none;display:grid;place-items:center;
  color:var(--txt-2);background:none;border:0;cursor:pointer;padding:0}
.bicon svg{width:12px;height:12px}
.bicon:hover{color:var(--txt)}
.brow__bottom{display:flex;align-items:center;gap:var(--s-sm)}

/* OrdinalBadge — digit tints by tally, chip stays quiet */
.bord{min-width:var(--pill);height:var(--pill);display:grid;place-items:center;
  border-radius:var(--r-control);background:rgba(34,38,48,.6);
  font:700 12px/1 Inter,sans-serif;font-variant-numeric:tabular-nums;color:var(--txt-2)}
.brow.is-pgm .bord{background:var(--tally-pgm);color:var(--red)}
.brow.is-pvw .bord{background:var(--tally-pvw);color:var(--green)}

.barrows{display:flex;align-items:center;gap:var(--s-xxs);height:var(--pill);
  padding:0 var(--s-xs);border-radius:var(--r-control);background:rgba(34,38,48,.6)}
/* OutputCard's arrows are a fixed chipWidth so they line up under the power chip */
.barrows--wide{width:var(--chip-w);padding:0;justify-content:center}
.barrow{width:16px;height:22px;display:grid;place-items:center;color:var(--txt-2);
  background:none;border:0;padding:0;cursor:pointer}
.barrow svg{width:10px;height:10px}
.barrow[disabled]{opacity:.35;cursor:default}
.bfield{flex:1;min-width:0;height:var(--pill);display:flex;align-items:center;
  padding:0 var(--s-sm);border-radius:var(--r-control);
  background:var(--bg-app);border:1px solid var(--stroke);
  font-size:12px;font-weight:500;color:var(--txt);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:text}
/* the site's copy is not editable — the field is here because the app has one */
.bfield:hover{border-color:var(--stroke-divider)}
.brow__delay{font-size:10px;color:rgba(112,116,124,.8);white-space:nowrap;flex:none}
.bicon.is-on{color:var(--blue)}
/* ChannelSettingsView — expands INLINE in the rail, never over the multiview */
.bchset{border-top:1px solid var(--stroke);margin-top:var(--s-xs);padding-top:var(--s-sm);
  display:flex;flex-direction:column;gap:var(--s-xs)}
.bchset__row{display:flex;align-items:center;gap:var(--s-sm)}
.bstep{height:var(--pill);min-width:var(--pill);padding:0 var(--s-sm);flex:none;
  border-radius:var(--r-control);background:rgba(34,38,48,.6);border:1px solid var(--stroke);
  color:var(--txt-2);font:500 11px/1 Inter,sans-serif;cursor:pointer}
.bstep[disabled]{opacity:.35;cursor:default}

/* ── centre pane — MultiviewGrid.body: VStack(spacing: .md) of
      topBar / the unified grid / cameraControl, padded 16 sides, 16 bottom,
      12 top (matching the rail headers) ──────────────────────────────── */
.bcenter{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--s-md);
  padding:var(--s-md) var(--s-lg) var(--s-lg);
  overflow-y:auto;overscroll-behavior-y:auto;scrollbar-width:thin;
  scrollbar-color:var(--stroke-divider) transparent}
.bcenter::-webkit-scrollbar{width:9px}
.bcenter::-webkit-scrollbar-track{background:transparent}
.bcenter::-webkit-scrollbar-thumb{background:var(--stroke-divider);border-radius:999px;
  border:3px solid transparent;background-clip:content-box}
.bcenter::-webkit-scrollbar-thumb:hover{background:var(--txt-faint);background-clip:content-box}

/* topBar is a ZStack: lenses left, Full-Screen/Detach right, CUT dead-centre
   over the PVW/PGM seam — not in the flow between them */
/* TopBarLayout.swift — CUT sits on the PREVIEW/PROGRAM seam whatever is beside
   it, AUTO hangs off its right, and the side clusters move DOWN a row rather
   than slide under that pair. The pair is a centred absolute layer; the row
   below only exists once the bar has wrapped. */
.btop{position:relative;display:flex;align-items:center;gap:var(--s-sm);min-height:22px}
.btop__grow{flex:1}
.btop__right{display:flex;align-items:center;gap:var(--s-xs)}
.btop__cut{position:absolute;left:50%;transform:translateX(-50%)}
/* CUT straddles the centre line, so the pair's own box starts at that line and
   AUTO rides 4px off CUT's right edge */
.bacut{position:absolute;left:50%;top:0;display:flex;align-items:center;height:22px;box-sizing:border-box;
  margin-left:calc(var(--cut-half, 31px) + var(--s-xs));border-radius:var(--r-button);
  background:rgba(34,38,48,.6);border:1px solid var(--stroke);color:var(--txt)}
.bacut.is-on{background:var(--blue);border-color:transparent;color:#fff}
.bacut__go,.bacut__cog{height:22px;border:0;background:none;color:inherit;cursor:pointer;
  display:inline-flex;align-items:center;padding:0}
.bacut__go{gap:6px;padding:0 var(--s-sm);font:500 11px/1 Inter,sans-serif}
/* monospaced digits in a fixed slot: the capsule must not breathe while it counts */
.bacut__t{font:500 11px/1 ui-monospace,SFMono-Regular,Menlo,monospace;font-style:normal;
  width:34px;text-align:right;color:var(--txt-faint)}
.bacut.is-on .bacut__t{color:#fff}
.bacut__sep{width:1px;height:22px;flex:none;background:var(--stroke-divider)}
.bacut.is-on .bacut__sep{background:rgba(255,255,255,.2)}
.bacut__cog{padding:0 6px;color:var(--txt-2)}
.bacut__cog svg{width:11px;height:11px}
.bacut.is-on .bacut__cog{color:rgba(255,255,255,.8)}
/* without two cameras sending a picture only the VALUE is out of reach — the
   gear still opens, because reading what the setting does stays allowed */
.bacut.is-idle .bacut__go{opacity:.4;pointer-events:none}
.bacut__go:disabled{cursor:default}

/* The range panel the gear opens (AutoCutSettings, 1.4.1+). Sized FROM the two
   timers rather than the other way round: 2 x 94 + 16 between + 12 either side. */
.bapanel{position:absolute;top:28px;right:0;width:228px;height:256px;z-index:9;
  box-sizing:border-box;padding:var(--s-md);display:flex;flex-direction:column;
  text-align:left;cursor:default;
  background:var(--bg-panel);border:1px solid var(--stroke-divider);
  border-radius:var(--r-button);box-shadow:0 10px 28px rgba(0,0,0,.5)}
.bapanel__t{margin:0;font:500 12px/1.3 Inter,sans-serif;color:var(--txt)}
.bapanel__h{margin:4px 0 0;font:400 11px/1.4 Inter,sans-serif;color:var(--txt-faint)}
.bapanel__h--note{margin-top:8px}
.bapanel__h.is-bad{color:var(--red)}
.bapanel__lbl{margin:14px 0 0;font:400 12px/1.3 Inter,sans-serif;color:var(--txt-2)}
.bapanel__row{display:flex;gap:var(--s-lg);margin-top:10px}
.bapanel__cap{font:600 10px/1 Inter,sans-serif;letter-spacing:1px;text-transform:uppercase;
  color:var(--txt-faint)}
.babound{display:flex;flex-direction:column;gap:6px}
/* the digits read first, the arrows second: they stay faint until pointed at */
/* 4 digits + colon + 5px either side + the 1px stroke = the app's 94 */
.batime{display:flex;align-items:center;padding:2px 5px;box-sizing:border-box;
  background:var(--bg-selected);border:1px solid var(--stroke-divider);
  border-radius:var(--r-button)}
.batime.is-bad{border-color:var(--red)}
.batime__col{display:flex;flex-direction:column;align-items:center}
.batime__col b{width:18px;height:24px;display:grid;place-items:center;
  font:500 19px/1 ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--txt)}
.batime__sep{display:flex;flex-direction:column;align-items:center}
.batime__sep b{width:10px;height:24px;margin:16px 0;display:grid;place-items:center;
  font:500 19px/1 ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--txt-2)}
.batime__a{width:18px;height:16px;padding:0;border:0;background:none;border-radius:4px;
  color:var(--txt-faint);cursor:pointer;display:grid;place-items:center}
.batime__a:hover{color:var(--txt);background:var(--bg-hover)}
.batime__a svg{width:8px;height:8px;stroke-width:3}
.bapanel__foot{display:flex;justify-content:flex-end;gap:var(--s-xs);margin-top:auto;
  padding-top:var(--s-sm);border-top:1px solid var(--stroke);
  margin-left:calc(var(--s-md) * -1);margin-right:calc(var(--s-md) * -1);
  padding-left:var(--s-md);padding-right:var(--s-md)}
.bapanel__foot .bbtn{height:24px;padding:0 12px}
.bbtn--blue{background:var(--blue);border-color:transparent;color:#fff}
.bapanel__foot .bbtn--blue:disabled{opacity:.35;cursor:default}

/* Narrow: the pair keeps the centre and the clusters drop to a second row —
   never squeezed, never overlapping. The replica scales rather than reflows,
   so this only fires if the window's design width ever changes; it is here
   because it is the app's own rule, and the app earned it by shipping a bar
   where AUTO sat on top of Full-Screen. */
.btop.is-wrapped{padding-top:30px}
.btop.is-wrapped .btop__cut,.btop.is-wrapped .bacut{top:0}
.blens{display:flex;align-items:center;gap:var(--s-xs)}
.bhint{font-size:11px;color:var(--txt-faint)}

/* BridgeButtonStyle */
.bbtn{height:22px;padding:0 var(--s-sm);border-radius:var(--r-button);
  background:var(--bg-selected);border:1px solid var(--stroke);color:var(--txt);
  font:500 11px/1 Inter,sans-serif;display:inline-flex;align-items:center;gap:var(--s-xs);
  cursor:pointer;transition:background .12s}
.bbtn:hover{background:var(--bg-hover)}
.bbtn[disabled]{opacity:.5;cursor:default}
.bbtn svg{width:11px;height:11px}
.bbtn.is-sel{background:var(--blue);border-color:var(--blue);color:#fff}
.bbtn__key{font-size:9px;font-weight:500;color:rgba(112,116,124,.7)}
.bbtn.is-sel .bbtn__key{color:rgba(255,255,255,.85)}

/* ── the unified grid: big panes + thumbnails behave as ONE object.
      multiviewTile draws a 1px neutral line on the TOP and LEADING edges only,
      so touching tiles share a single seam; the container closes the outer
      bottom/right; a selected tile lays a 2px accent frame over it. ───────── */
.bgridwrap{outline:1px solid var(--stroke);outline-offset:-1px}
.btile{position:relative;background:#000;aspect-ratio:16/9;
  box-shadow:inset 0 1px 0 var(--stroke),inset 1px 0 0 var(--stroke)}
/* The cue ring is drawn over the tile's content, not as an outline: an outline
   paints under a tile's own children, so a feed playing inside would hide the
   very thing that says whether the channel is staged or on air. */
.btile.is-pvw,.btile.is-pgm{z-index:1}
.btile.is-pvw::after,.btile.is-pgm::after{content:"";position:absolute;inset:0;z-index:2;
  border:2px solid var(--green);border-radius:inherit;pointer-events:none}
.btile.is-pgm::after{border-color:var(--red)}
.bbig{display:flex}
.bbig .btile{flex:1;min-width:0}
.bgrid{display:grid;grid-template-columns:repeat(4,1fr)}
.bthumb{cursor:pointer}
.bempty{display:grid;place-items:center;color:rgba(112,116,124,.3);cursor:pointer}
.bempty svg{width:13px;height:13px}

/* bottomChip — 10 bold, tracking .5, white on black 50%, radius control */
.bchip{position:absolute;left:50%;bottom:var(--s-sm);transform:translateX(-50%);
  display:flex;align-items:center;gap:var(--s-xs);white-space:nowrap;
  font:700 10px/1.4 Inter,sans-serif;letter-spacing:.5px;color:#fff;
  padding:2px var(--s-sm);border-radius:var(--r-control);background:rgba(0,0,0,.5)}
/* the thumbnail's name chip sits on the LEFT and is semibold, not bold */
/* LivePane pins the name with alignment: .bottom — bottom CENTRE, same as the
   big panes' PREVIEW / PROGRAM chip. It is not left-aligned. */
.bchip--name{font-weight:600;letter-spacing:0}
.bdot{width:6px;height:6px;border-radius:50%;background:var(--green)}
.bdot--off{background:var(--txt-faint)}
/* BigPane placeholder: rectangle.dashed 22 + "No source" 11, both faint */
.bph{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:var(--s-xs);color:var(--txt-faint)}
.bph svg{width:22px;height:22px}
.bph span{font-size:11px}
/* LivePane offline: wifi.slash 18 + "No camera" 10 */
.boff{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:var(--s-xs);color:var(--txt-faint)}
.boff svg{width:18px;height:18px}
.boff span{font-size:10px}
.bfeed{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}

/* ── camera control — CameraControlPanel: a SectionLabel, then exposure |
      white balance, focus | look, stabilization, output delay ───────────── */
.bctl{display:flex;flex-direction:column;gap:var(--s-md)}
.bseclbl{font:600 10px/1 Inter,sans-serif;letter-spacing:1px;text-transform:uppercase;color:var(--txt-faint)}
.bctl__row{display:grid;grid-template-columns:1fr 1fr;gap:var(--s-md);align-items:stretch}
.bctl__row > *{min-width:0}
/* ControlSection = Card(padding 8) with an uppercase 11 medium tracking .6 title */
.bsec{background:var(--bg-panel);border:1px solid var(--stroke);border-radius:var(--r-panel);
  padding:var(--s-sm);display:flex;flex-direction:column;gap:var(--s-sm);min-width:0}
.bsec__head{display:flex;align-items:center;gap:var(--s-sm)}
.bsec__title{font:500 11px/1 Inter,sans-serif;letter-spacing:.6px;text-transform:uppercase;color:var(--txt-faint)}

.bsec__grow{flex:1}
.bev{display:inline-flex;align-items:center;gap:6px;height:24px;padding:0 10px;border-radius:7px;
  background:var(--bg-hover);border:1px solid var(--stroke-divider);
  cursor:grab;user-select:none;touch-action:none}
.bev:active{cursor:grabbing}
.bev__l{font-size:10px;font-weight:500;color:var(--txt-faint)}
.bev__v{font:500 13px/1 Inter,sans-serif;font-variant-numeric:tabular-nums;color:var(--txt-2)}
/* accent = auto-exposure is on AND a bias is dialled in */
.bev.is-accent .bev__v{color:var(--yellow)}
/* inactive = exposure is manual: EV biases the AUTO target, so it does nothing here */
.bev.is-idle{background:none;border-color:transparent;opacity:.45}
.bauto{height:24px;padding:0 9px;border-radius:7px;background:var(--bg-hover);
  border:1px solid var(--stroke-divider);color:var(--txt-2);
  font:500 11px/1 Inter,sans-serif;cursor:pointer}
.bauto.is-on{color:var(--yellow);border-color:rgba(255,214,10,.6)}
/* ParamStrip — row 46 tall, label 12, value 19 mono (yellow while AUTO reads live) */
.bstrip{height:46px;display:flex;flex-direction:column;gap:7px;justify-content:center}
.bstrip__top{display:flex;align-items:baseline;gap:10px;min-width:0}
.bstrip__lbl{font-size:12px;font-weight:500;color:var(--txt-2)}
.bstrip__val{font:500 19px/1 Inter,sans-serif;font-variant-numeric:tabular-nums;color:var(--txt)}
.bstrip.is-auto .bstrip__val{color:var(--yellow)}
.bstrip__grow{flex:1;min-width:8px}
.bchips{display:flex;gap:5px;flex:none}
.bchips button{height:24px;padding:0 9px;border-radius:7px;background:var(--bg-hover);
  border:1px solid var(--stroke-divider);color:var(--txt-2);
  font:500 11px/1 Inter,sans-serif;font-variant-numeric:tabular-nums;cursor:pointer}
.bchips button.is-on{color:var(--yellow);border-color:rgba(255,214,10,.6)}
/* slim tape — 1x6 ticks every 13px, a yellow marker at the value */
.btape{position:relative;height:12px;cursor:ew-resize;touch-action:none;
  background-image:repeating-linear-gradient(to right,rgba(48,52,62,.6) 0 1px,transparent 1px 13px);
  background-position:0 50%;background-size:100% 6px;background-repeat:repeat-x}
.btape__m{position:absolute;top:0;width:2px;height:12px;background:var(--yellow);border-radius:1px}
/* LOOK — labelled dropdown rows */
.brow2{display:flex;align-items:center;gap:var(--s-sm);min-height:34px}
.brow2__lbl{font-size:13px;font-weight:500;color:var(--txt);flex:none;width:128px}
.brow2.is-off .brow2__lbl{opacity:.5}
/* empty slot the width of a PowerToggle, so the Colour space box ends where the LUT box does */
.bslot{width:var(--chip-w);flex:none}
.bselect{flex:1;min-width:0;height:34px;display:flex;align-items:center;gap:var(--s-xs);
  padding:0 var(--s-sm);border-radius:var(--r-button);background:var(--bg-selected);
  border:1px solid var(--stroke-divider);color:var(--txt);
  font-size:12px;font-weight:500;cursor:pointer;white-space:nowrap;overflow:hidden}
.bselect--muted{color:var(--txt-faint)}
.bselect svg{width:9px;height:9px;margin-left:auto;flex:none;color:var(--txt-2)}
/* the rail's pickers sit flush with the 28pt pills */
.bout .bselect{height:var(--pill)}
.bselect.is-off{opacity:.5}
.bswitch{width:var(--chip-w);height:var(--pill);flex:none;position:relative;padding:0;
  border-radius:var(--r-button);background:var(--bg-selected);border:1px solid var(--stroke);cursor:pointer}
.bswitch::after{content:"";position:absolute;top:2px;left:2px;width:22px;height:22px;
  border-radius:var(--r-control);background:#fff;transition:transform .15s}
.bswitch.is-on{background:var(--blue);border-color:var(--blue)}
.bswitch.is-on::after{transform:translateX(16px)}
/* SegmentedBar — equal-width segments on one tracked background */
.bseg{display:grid;grid-auto-flow:column;grid-auto-columns:1fr;gap:2px;padding:2px;
  height:var(--segment);border-radius:var(--r-button);background:var(--bg-app);border:1px solid var(--stroke)}
.bseg button{border:0;border-radius:var(--r-control);background:none;color:var(--txt-2);
  font:600 12px/1 Inter,sans-serif;cursor:pointer}
.bseg button.is-on{background:var(--blue);color:#fff}

/* ── output card — the channel-card template: TOP power · tag ··· trash,
      BOTTOM ▲/▼ · name.  The power chip and the arrows share chipWidth. ──── */
.bout{display:flex;flex-direction:column;gap:var(--s-sm)}
.bout__top,.bout__bottom{display:flex;align-items:center;gap:var(--s-sm)}
.bout__tag{font-size:11px;font-weight:600;letter-spacing:.6px;color:var(--txt-2)}
.bout__grow{flex:1}

/* the virtual camera has no editable name — it shows the baked-in device label */
.bout__url{display:flex;align-items:center;gap:var(--s-xs);font:400 11px/1.4 ui-monospace,Menlo,monospace;
  color:var(--txt-faint);min-width:0}
.bout__urlt{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;direction:rtl;text-align:left}
.bout__url svg{width:12px;height:12px;flex:none}
.bfield--mono{font:400 11px/1 ui-monospace,Menlo,monospace}
.bfield--mono.is-empty{color:var(--txt-faint)}
.bout__baked{font-size:13px;color:var(--txt-2);flex:1;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* PowerToggle — 44x28 track, radius button; 22x22 white knob, radius control, inset 3 */
.bpower{width:var(--chip-w);height:var(--pill);flex:none;position:relative;padding:0;
  border-radius:var(--r-button);background:var(--bg-selected);border:1px solid var(--stroke);
  cursor:pointer;transition:background .15s}
.bpower::after{content:"";position:absolute;top:2px;left:2px;width:22px;height:22px;
  border-radius:var(--r-control);background:#fff;transition:transform .15s}
.bout.is-on .bpower{background:var(--blue);border-color:var(--blue)}
.bout.is-on .bpower::after{transform:translateX(16px)}

/* ── dropdown (Dropdown.swift renders at window root, our radius) ── */
.bmenu{position:absolute;z-index:20;min-width:236px;padding:var(--s-xs);
  background:var(--bg-panel);border:1px solid var(--stroke);border-radius:var(--r-button);
  box-shadow:0 12px 32px rgba(0,0,0,.5)}
.bmenu[hidden]{display:none}
.bmenu__row{width:100%;display:flex;align-items:center;gap:var(--s-sm);
  padding:var(--s-sm);border-radius:var(--r-control);background:none;border:0;
  color:var(--txt);font:500 12px/1.3 Inter,sans-serif;text-align:left;cursor:pointer}
.bmenu__row:hover{background:var(--bg-hover)}
.bmenu__row svg{width:13px;height:13px;flex:none;color:var(--txt-2)}
.bmenu__t{display:flex;flex-direction:column;gap:1px;min-width:0}
/* the one-line "what it does" we decided to put in the menus themselves */
.bmenu__d{font-size:10px;font-weight:400;color:var(--txt-faint);line-height:1.4}
.bmenu__sep{height:1px;background:var(--stroke);margin:var(--s-xs) 0}
.bmenu__row > svg:last-child{margin-left:auto;color:var(--blue)}
/* the explainer behind an ⓘ and the password hint — prose, not a list */
.bnote{max-width:320px;padding:var(--s-md);white-space:pre-line;
  font:400 11px/1.55 Inter,sans-serif;color:var(--txt-2)}
.binfo{width:16px;height:16px;padding:0;border:0;background:none;color:var(--txt-faint);
  display:grid;place-items:center;cursor:pointer;flex:none}
.binfo svg{width:11px;height:11px}

/* ── the caption that reacts to what you just did ────────────────── */
.bsay{min-height:44px;margin-top:18px;display:flex;align-items:flex-start;gap:10px;
  font-family:var(--font-family-body,Inter),sans-serif}
.bsay__dot{width:7px;height:7px;border-radius:50%;background:var(--yellow);margin-top:7px;flex:none}
.bsay__t{color:var(--text-2);font-size:var(--fs-md);line-height:1.55;max-width:70ch}
.bsay__t b{color:var(--text);font-weight:600}
/* ════════════════════════════════════════════════════════════════════
   Airlive Camera — landscape screen replica.

   Values from the app's own source (~/camera-src):
     ParameterSheetView.swift  PanelUI — radii, insets, gap, controlHeight,
                               railWidth, the 10/14/24 font ladder, controlFill,
                               selected=yellow, accent=airliveBlue
     Color+Airlive.swift       airliveBlue #0091FF (the one brand colour)
     ContentView.swift         airliveRecRed #C10000, tally preview #11CC00,
                               panelGap 12, controlsColumnWidth 96 (10 + 58 + 28),
                               landscapeHUDHeight 60, CtrlItem 44 tall / 58 wide,
                               REC disc 40 white .12 + ring white .18,
                               core 30 circle → 18 square r4
     LiveChannelsOverlay.swift the channel list, PanelOptionStyle rows
   ════════════════════════════════════════════════════════════════════ */

.cdemo{
  --c-chrome:16px; --c-control:10px; --c-inset:16px; --c-gap:8px;
  --c-ctrl:44px; --c-rail:72px; --c-lens:54px; --c-air:12px;
  --c-col:96px; --c-colw:58px; --c-hud:60px;
  --c-fill:rgba(0,0,0,.45);
  --c-yellow:#FFCC00; --c-blue:#0091FF; --c-red:#C10000; --c-green:#11CC00;
  --scr-w:932; --scr-h:430;         /* iPhone 15 Pro Max, landscape points */
  --cscale:1;
  font-family:Inter,system-ui,sans-serif;
}
/* Real iPhone 15 Pro Max frame (assets/iphone-15-pro-max-landscape.png).
   The screen hole was measured from the PNG's alpha channel:
   x 56–5765, y 68–2775 of 5821×2844 → the percentages below. The hole is
   2.109:1 while the interface is 932×430 (2.1674:1), so the screen fits by
   WIDTH and centres vertically — the few points of letterbox are black on
   black and invisible. */
.cdemo__slot{position:relative;width:100%}
/* the hole is 98.0759% of the frame, so a 950px frame gives exactly the
   932pt screen — the interface fills it at native scale, no upscaling */
.cdemo__frame{position:relative;width:100%;max-width:950px;margin:0 auto;
  aspect-ratio:5821/2844}
.cbody{position:absolute;inset:0}
.cbody__png{position:absolute;inset:0;width:100%;height:100%;z-index:8;
  pointer-events:none;user-select:none}
/* Screen corner radius measured off the PNG's alpha: the curve reaches the
   straight edge ~330 image px in, and the hole is 5709 px for 932 pt — so 55 pt,
   the Pro Max's real screen radius. Everything the interface draws is clipped
   to it, so nothing pokes out under the rail. */
/* the screen hole, measured off the PNG's alpha channel; the radius is the
   frame's OWN corner arc (144px on a 2369-wide hole), so the interface stops
   exactly where the bezel starts instead of poking a square corner past it */
.cscr{position:absolute;left:0.6250%;top:1.9608%;width:98.7083%;height:96.0784%;
  overflow:hidden;background:#000;border-radius:var(--cradius,55px);
  display:flex;align-items:center;justify-content:center}
/* the interface keeps its 932×430 points and is scaled to FILL the hole.
   Two scales, not one: this frame's hole is 2.102:1 where the real screen is
   2.167:1, so a single scale left a black band top and bottom and the corners
   never met the bezel. */
.cui{width:932px;height:430px;display:flex;flex:none;
  transform:scale(var(--cscale),var(--cscaley));transform-origin:center}

/* ── viewfinder ──────────────────────────────────────────────────
   The sensor always delivers 16:9 (previewSensorAspect), and the preview is
   pinned to the controls edge — so a black PILLARBOX bar is left on the
   island side:  blackBarWidth = previewW − previewH × 16/9.
   At 836×430 that is 71.6pt, and the tally lives in it. */
.cview{position:relative;flex:1;min-width:0;background:#000;
  --vw:836px; --vh:430px; --bar:calc(var(--vw) - var(--vh) * 16 / 9)}
/* the image hugs the controls edge; the bar is the dead zone beside it */
.cimg{position:absolute;top:0;right:0;bottom:0;left:var(--bar);background:#0A0A0A;overflow:hidden}
/* the viewfinder picture: a plain <video>, or the canvas lutfeed.js draws the
   graded frame into. Both are replaced elements, so object-fit crops them the
   way the phone crops the sensor. */
.cfeed{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
/* Tally: NOT a border. The app fills the dead-zone bar with a radial glow
   centred on the rear-camera bump, so an operator standing behind the lens
   reads it without looking at the screen. Stops and radius from tallyGlow():
   0.7 at the centre, 0.25 at 40%, 0 at the edge, endRadius 350. */
.ctally{position:absolute;top:0;left:0;bottom:0;width:var(--bar);z-index:3;
  pointer-events:none;opacity:0;transition:opacity .25s}
.cdemo.is-pgm .ctally,.cdemo.is-pvw .ctally{opacity:1}
.cdemo.is-pgm .ctally{background:radial-gradient(circle 350px at 50% 50%,
  rgba(193,0,0,.7) 0%, rgba(193,0,0,.25) 40%, rgba(193,0,0,0) 100%)}
.cdemo.is-pvw .ctally{background:radial-gradient(circle 350px at 50% 50%,
  rgba(17,204,0,.7) 0%, rgba(17,204,0,.25) 40%, rgba(17,204,0,0) 100%)}

/* ── HUD — one row, 60 tall ─────────────────────────────────────── */
/* the HUD insets to clear the pillarbox — it rides over the image, not the bar */
/* the HUD clears the pillarbox on one side and the permanently reserved rail
   zone on the other (landscapeRailReserve = railWidth + panelGap), so opening
   a scrubber never shifts it */
.chud{position:absolute;top:0;left:var(--bar);right:calc(var(--c-rail) + var(--c-air));
  height:var(--c-hud);z-index:4;display:flex;align-items:flex-start;padding:10px 0 8px}
.cpill{border:0;cursor:pointer;text-align:left}
.cpill.is-open{background:var(--c-blue)}
.cpill.is-open b,.cpill.is-open span{color:#fff}
/* equal gaps everywhere — between every pill AND to both screen edges */
.chud__sp{flex:1}
.cpill{display:flex;flex-direction:column;align-items:flex-start;gap:1px;flex:none;
  padding:5px 10px;border-radius:var(--c-control);
  background:rgba(255,255,255,.13);backdrop-filter:blur(24px) saturate(140%)}
.cpill b{font-size:14px;font-weight:400;color:#fff;line-height:1.2}
.cpill span{font-size:10px;font-weight:400;color:rgba(255,255,255,.5);line-height:1.2}
/* yellow value = the mode is AUTO, i.e. the camera is choosing it */
.cpill.is-auto b{color:var(--c-yellow)}
.cpill__v{display:block;min-width:var(--pw,44px)}
/* timecode — the only 24pt on the screen, HH:MM:SS (the frames field was
   dropped in the app: it was really centiseconds). White at rest, red while
   recording, and it only counts while recording. */
.ctc{font-size:24px;font-weight:400;font-variant-numeric:tabular-nums;color:#fff;
  padding-top:6px;flex:none}
.cdemo.is-rec .ctc{color:var(--c-red)}

/* ── controls column — 96 total: 10 + 58 content + 28 corner margin ─ */
.ccol{width:var(--c-col);flex:none;background:#000;display:flex;flex-direction:column;
  align-items:flex-start;padding:20px 0;z-index:6}
.ccol__in{width:var(--c-colw);margin-left:10px;flex:1;
  display:flex;flex-direction:column;justify-content:space-between;align-items:center}
/* CtrlItem centres its value+label inside the 44pt chip
   (.frame(minHeight: 44, maxHeight: 44, alignment: .center)); without
   justify-content the text sat at the top of every button and the whole column
   read as pushed up */
.citem{width:var(--c-colw);height:var(--c-ctrl);border:0;background:none;padding:0;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:2px;cursor:pointer;border-radius:var(--c-control)}
.citem__top{height:17px;display:flex;align-items:center;justify-content:center;
  font-size:14px;font-weight:400;color:#fff;line-height:1}
.citem__top svg{width:15px;height:15px}
.citem--icon .citem__top svg{width:20px;height:20px}
.citem__lbl{font-size:10px;font-weight:400;color:rgba(255,255,255,.5);line-height:1;
  white-space:nowrap}
/* text tint = the parameter is doing something; the sublabel is the same hue at 50% */
.citem.is-tint .citem__top{color:var(--c-yellow)}
.citem.is-tint .citem__lbl{color:rgba(255,204,0,.5)}
/* blue chip = a panel is open — never "connected" */
.citem.is-open{background:var(--c-blue)}
.citem.is-open .citem__top,.citem.is-open .citem__lbl{color:#fff}

/* REC — dark disc + hairline ring, red core morphs circle ⇄ square */
.crec{width:var(--c-colw);height:var(--c-ctrl);display:grid;place-items:center;
  border:0;background:none;cursor:pointer;position:relative}
.crec__disc{width:40px;height:40px;border-radius:50%;background:rgba(255,255,255,.12);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.18)}
.crec__core{position:absolute;left:50%;top:50%;width:30px;height:30px;border-radius:15px;
  background:var(--c-red);transform:translate(-50%,-50%);
  transition:width .28s cubic-bezier(.2,.9,.3,1.2),height .28s cubic-bezier(.2,.9,.3,1.2),border-radius .28s}
.cdemo.is-rec .crec__core{width:18px;height:18px;border-radius:4px}

/* ── the Live panel — the one thing that opens ──────────────────── */
/* MenuPlacement, landscape with the controls on the trailing edge:
     width  = min(screenW, screenH) = 430pt
     top    = hudClearance + panelGap = 60 + 12
     side   = panelGap on the controls edge only
     bottom = panelGap (it scrolls inside if it grows past that)
   Chrome is menuChrome: black 75% + white 10%, 1px white-15% hairline,
   radius PanelUI.chromeCorner. */
.cpanel{position:absolute;z-index:7;
  top:calc(var(--c-hud) + var(--c-air));right:var(--c-air);
  width:430px;max-height:calc(430px - var(--c-hud) - var(--c-air) * 2);
  overflow:auto;padding:var(--c-inset);border-radius:var(--c-chrome);
  background-image:linear-gradient(rgba(255,255,255,.10),rgba(255,255,255,.10)),
                   linear-gradient(rgba(0,0,0,.75),rgba(0,0,0,.75));
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.15);
  display:flex;flex-direction:column;gap:10px;
  transform-origin:top right;animation:cappear .22s ease-out}
.cpanel::-webkit-scrollbar{width:0}
.cpanel[hidden]{display:none}
@keyframes cappear{from{opacity:0;transform:scale(.96)}to{opacity:1;transform:scale(1)}}
/* PanelCaption — 10 semibold, tracking 1.0, white 50% */
.cpanel__cap{font-size:10px;font-weight:600;letter-spacing:1px;text-transform:uppercase;
  color:rgba(255,255,255,.5)}
/* device group header — icon 11 semibold + the receiver name at label size */
.cpanel__dev{display:flex;align-items:center;gap:6px;padding-top:4px;
  font-size:10px;color:rgba(255,255,255,.55)}
.cpanel__dev svg{width:11px;height:11px}
/* PanelOptionStyle: selected = solid yellow + black text */
/* sourceRow — PanelOptionStyle on a 10/10 padded row */
.crow{display:flex;align-items:center;gap:8px;padding:10px;border:0;cursor:pointer;width:100%;
  border-radius:var(--c-control);background:var(--c-fill);color:#fff;text-align:left}
.crow__n{font-size:14px;font-weight:500;flex:1;min-width:0;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.crow__s{font-size:10px;color:rgba(255,255,255,.5);flex:none}
.crow.is-on{background:var(--c-yellow);color:#000}
.crow.is-on .crow__s{color:rgba(0,0,0,.55)}
.crow[disabled]{opacity:.4;cursor:default}
.cpanel__hint{font-size:10px;line-height:1.5;color:rgba(255,255,255,.4)}

/* ── panels ─────────────────────────────────────────────────────────
   Two placements, both from ContentView:
   • RULER RAIL (iso / shutter / wb / tint / focus / fps / exposure / lens)
     — railWidth 72, FULL preview height, panelGap from the controls edge.
   • MENU (lut / tools / channels / settings) — MenuPlacement: width
     min(screenW,screenH)=430 (Tools ×1.3), top = hudClearance+gap, gap on
     the controls side.
   Nothing inside is operable on the site — panels open, values sit still. */
/* ── ruler rail — ParamSlideStrip, vertical axis ────────────────────
   HStack(spacing: 6) { presetColumn?, railBlock }
   • rail block: railWidth 72, FULL preview height, liquidGlass
     (black 52% + white 5% + white-15% hairline), radius chromeCorner 16
   • AUTO pill: 60×60 (thickness − 12), pinned TOP, inset 6, PanelOptionStyle
   • the big value sits over the CENTRE of the tape — there is no side readout
   • cleanStyle (ISO / Shutter / FPS): no ticks, the values ARE the scale
   • tick tape (WB / Tint / Focus / EV): ticks in the far lane 0.90,
     anchor labels in lane 0.42, both measured from the preview-facing edge
   • preset chips: lens-pill look, column, LARGEST ON TOP */
.crailwrap{position:absolute;z-index:7;top:8px;bottom:8px;right:var(--c-air);
  display:flex;align-items:center;gap:6px;animation:cappear .22s ease-out;
  pointer-events:none}
.crailwrap > *{pointer-events:auto}
/* LensPanel is a COLUMN of tiles in the rail zone, centred on the screen —
   the wrap is a row by default, so it flips here */
.crailwrap.crail--lens{flex-direction:column;justify-content:center;gap:10px}
.crailwrap[hidden]{display:none}
.crail{width:var(--c-rail);align-self:stretch;position:relative;overflow:hidden;
  border-radius:var(--c-chrome);
  background-image:linear-gradient(rgba(255,255,255,.05),rgba(255,255,255,.05)),
                   linear-gradient(rgba(0,0,0,.52),rgba(0,0,0,.52));
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.15)}
.crail__auto{position:absolute;top:6px;left:6px;width:60px;height:60px;z-index:2;
  display:grid;place-items:center;border:0;border-radius:var(--c-control);
  font:600 10px/1 Inter,sans-serif;letter-spacing:1px;
  background:var(--c-yellow);color:#000}
.crail__auto.is-off{background:var(--c-fill);color:#fff}
/* ── the tape ───────────────────────────────────────────────────────
   Every stop is laid out once at pos = 0 and the whole tape is translated
   by pos x stopPitch, which is exactly ParamSlideStrip's
     coord(i) = center + (pos - i) * stopPitch      (larger values UP)
   stopPitch  = 13 for a continuous ladder (> 30 stops), else 32
   labelEvery = continuous ? 5 : 1   (ceil(22 / 13) rounded up the 5/10/25/50 rack)
   Lanes are FRACTIONS of the 72pt rail width, measured from the preview-
   facing edge: ticks 0.90, numbers 0.42 — cleanStyle rails have no ticks
   and keep the number dead centre (1/2).  Ticks are 1pt thin, 10pt long
   for a major (endpoint or every Nth) and 5pt for a minor, white 50/18 %. */
/* the fade lives on the STATIC frame; the tape slides inside it */
.crail__fade{position:absolute;inset:0}
.crail__tape{position:absolute;inset:0;will-change:transform;touch-action:none;
  cursor:grab;-webkit-user-select:none;user-select:none}
.crail__tape.is-snap{transition:transform .15s ease-out}
.crail__tape:active{cursor:grabbing}
.ctick{position:absolute;height:1px;background:#fff;transform:translateY(-50%)}
.ctick--maj{width:10px;opacity:.5}
.ctick--min{width:5px;opacity:.18}
.clab{position:absolute;transform:translateY(-50%);white-space:nowrap;
  font:400 14px/1 Inter,sans-serif;font-variant-numeric:tabular-nums;
  color:rgba(255,255,255,.55);cursor:pointer}
/* BIG live value — the actual reading, not the nearest stop; sits on the
   same lane as the anchor labels so they read as one line */
.crail__big{position:absolute;top:50%;transform:translate(-50%,-50%);
  white-space:nowrap;pointer-events:none;
  font:400 14px/1 Inter,sans-serif;font-variant-numeric:tabular-nums;color:var(--c-yellow)}
/* Fixed-focus lens overlay — PanelUI.optionFont on black 55 % */
.crail__lock{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
  padding:5px 8px;border-radius:var(--c-control);background:rgba(0,0,0,.55);
  font:500 14px/1 Inter,sans-serif;color:rgba(255,255,255,.75)}
/* preset chips — InstrumentChipChrome: the block is ALWAYS uniform
   (black 52% + white 5% + white-15% hairline, radius 10). There is no
   selected fill — the ONLY selection signal is YELLOW TEXT, and in AUTO
   nothing glows at all, because no manual value is active. Padding 10/8,
   min-width 60 — these are not squares. */
.crail__presets{display:flex;flex-direction:column;gap:6px}
.cpreset{min-width:60px;flex:none;border:0;border-radius:var(--c-control);
  /* buttons carry a light UA background-color; the chrome layers are
     semi-transparent, so without this it shows through as grey */
  background-color:transparent;
  padding:8px 10px;display:grid;place-items:center;color:#fff;
  font:400 14px/1 Inter,sans-serif;font-variant-numeric:tabular-nums;white-space:nowrap;
  background-image:linear-gradient(rgba(255,255,255,.05),rgba(255,255,255,.05)),
                   linear-gradient(rgba(0,0,0,.52),rgba(0,0,0,.52));
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.15)}
.cpreset.is-on{color:var(--c-yellow)}
/* WB / Tint chips are the lighting PAIR (temperature + tint), so they
   carry the preset's glyph at 11pt medium next to the value */
.cpreset__row{display:flex;align-items:center;gap:5px}
.cpreset svg{width:11px;height:11px;flex:none}

/* LensPanel tile — lensPill: 54 square, zoom label 14 over focal 10 at 50%.
   Idle is liquidGlass; the selected tile is solid PanelUI.accent (brand blue). */
.clens{width:var(--c-lens);height:var(--c-lens);flex:none;border:0;border-radius:var(--c-control);
  background-color:transparent;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px;
  background-image:linear-gradient(rgba(255,255,255,.05),rgba(255,255,255,.05)),
                   linear-gradient(rgba(0,0,0,.52),rgba(0,0,0,.52));
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.15)}
.clens b{font-size:14px;font-weight:400;color:#fff;line-height:1.1}
.clens span{font-size:10px;font-weight:400;color:rgba(255,255,255,.5);line-height:1.1}
.clens svg{width:20px;height:20px;color:#fff}
.clens.is-on{background-image:none;background:var(--c-blue);box-shadow:none}
.clens.is-on span{color:rgba(255,255,255,.6)}

/* menu-placed panels share the channels panel's chrome */
.cmenu{position:absolute;z-index:7;top:calc(var(--c-hud) + var(--c-air));right:var(--c-air);
  width:430px;max-height:calc(430px - var(--c-hud) - var(--c-air) * 2);overflow:auto;
  padding:var(--c-inset);border-radius:var(--c-chrome);
  background-image:linear-gradient(rgba(255,255,255,.10),rgba(255,255,255,.10)),
                   linear-gradient(rgba(0,0,0,.75),rgba(0,0,0,.75));
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.15);
  display:flex;flex-direction:column;gap:10px;
  transform-origin:top right;animation:cappear .22s ease-out}
.cmenu[hidden]{display:none}
.cmenu::-webkit-scrollbar{width:0}
/* Tools is shown, not offered: the rows keep the app's look and say what the
   overlays are, but nothing in the panel takes a press. */
.cmenu--ro button,.cmenu--ro .ctog,.cmenu--ro .cslider{pointer-events:none}
.cmenu--tools{width:559px}          /* menuWidth × 1.3 */
/* ToolsSidePanel landscape: HStack(alignment:.top, spacing: PanelUI.inset)
   of two columns, each stacking its groups at groupGap 18 */
.cmenu__cols{display:grid;grid-template-columns:1fr 1fr;gap:var(--c-inset);align-items:start}
.cmenu__col{display:flex;flex-direction:column;gap:18px}
/* a captioned group: caption tucked tight above its content (captionGap 6),
   rows inside at rowGap 8 */
.cgroup{display:flex;flex-direction:column;gap:6px}
.cgroup__rows{display:flex;flex-direction:column;gap:8px}
.cgroup__cap{font-size:10px;font-weight:600;letter-spacing:1px;text-transform:uppercase;
  color:rgba(255,255,255,.5)}
/* PanelToggleRow — HStack { Text(optionFont) ; Spacer() ; Toggle }.
   There is NO block behind the row: it sits bare on the menu chrome.
   The switch is a UISwitch (51x31, 27 knob, 20 travel) at scaleEffect(0.85)
   tinted PanelUI.accent — the app's blue, not the system green. */
.ctog{display:flex;align-items:center;justify-content:space-between;gap:10px;
  width:100%;min-height:26.4px;border:0;background:none;padding:0;cursor:pointer;
  text-align:left;font:500 14px/1.2 Inter,sans-serif;color:#fff}
.ctog__sw{width:43.4px;height:26.4px;flex:none;border-radius:999px;background:#39393D;
  position:relative;transition:background .2s}
.ctog__sw::after{content:"";position:absolute;top:1.7px;left:1.7px;width:23px;height:23px;
  border-radius:50%;background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.35);transition:transform .2s}
.ctog.is-on .ctog__sw{background:var(--c-blue)}
.ctog.is-on .ctog__sw::after{transform:translateX(17px)}
.ctog[disabled]{opacity:.4;cursor:default}
/* aspectChips — LazyVGrid, FOUR per row, spacing 8, PanelOptionStyle at
   controlHeight; 8 guide ratios make two clean rows with no orphan */
.cgrid4{display:grid;grid-template-columns:repeat(4,1fr);gap:8px}
.cchip{height:var(--c-ctrl);display:flex;align-items:center;justify-content:center;
  border:0;cursor:pointer;border-radius:var(--c-control);background:var(--c-fill);
  color:#fff;font:500 14px/1 Inter,sans-serif}
.cchip.is-on{background:var(--c-yellow);color:#000}
/* Slider(value: 0.05...1).tint(PanelUI.accent).frame(height: 44) */
.cslider{height:var(--c-ctrl);display:flex;align-items:center;touch-action:none;cursor:pointer;
  -webkit-user-select:none;user-select:none}
.cslider__t{flex:1;height:4px;border-radius:2px;background:rgba(255,255,255,.25);position:relative}
.cslider__f{position:absolute;left:0;top:0;bottom:0;border-radius:2px;background:var(--c-blue)}
.cslider__k{position:absolute;top:50%;width:23px;height:23px;border-radius:50%;background:#fff;
  transform:translate(-50%,-50%);box-shadow:0 1px 3px rgba(0,0,0,.4)}
/* the whole FRAMES OPACITY group dims and locks until a ratio is active */
.cgroup.is-off{opacity:.4;pointer-events:none}
/* LUT pill — PanelOptionStyle, centred, padding 10/10 */
.cluts{display:flex;flex-direction:column;gap:6px}
.clut{width:100%;border:0;cursor:pointer;padding:10px;border-radius:var(--c-control);
  background:var(--c-fill);color:#fff;font:500 14px/1.2 Inter,sans-serif;text-align:center}
.clut.is-on{background:var(--c-yellow);color:#000}
.crow--plain{background:var(--c-fill);border-radius:var(--c-control);padding:10px;
  font-size:14px;font-weight:500;color:#fff}

/* ── Settings — a full-screen SwiftUI sheet ─────────────────────────
   NavigationStack { Form { ... } } presented with .sheet; on an iPhone in
   landscape a sheet covers the whole screen, so this sits over the entire
   932x430 interface.  The ROWS are the app's (SettingsView.swift); the
   chrome is iOS's own inset-grouped Form in dark mode.  Read-only here. */
.cset{position:absolute;inset:0;z-index:9;display:flex;flex-direction:column;
  background:#000;overflow:hidden;animation:csheet .34s cubic-bezier(.2,.85,.25,1)}
.cset[hidden]{display:none}
@keyframes csheet{from{transform:translateY(100%)}to{transform:translateY(0)}}
.cset__bar{flex:none;height:44px;display:flex;align-items:center;justify-content:center;
  position:relative;padding:0 75px;background:rgba(28,28,30,.94);
  box-shadow:inset 0 -1px 0 rgba(84,84,88,.65)}
.cset__ttl{font:600 17px/1 Inter,sans-serif;color:#fff}
.cset__done{position:absolute;right:75px;top:50%;transform:translateY(-50%);border:0;
  background:none;cursor:pointer;font:400 17px/1 Inter,sans-serif;color:#0A84FF}
.cset__form{flex:1;overflow:auto;padding:18px 79px 49px}
.cset__form::-webkit-scrollbar{width:0}
.csec{margin-bottom:22px}
.csec__h{font:400 13px/1.3 Inter,sans-serif;color:rgba(235,235,245,.6);padding:0 16px 7px}
.csec__f{font:400 13px/1.35 Inter,sans-serif;color:rgba(235,235,245,.6);padding:7px 16px 0}
.csec__b{background:#1C1C1E;border-radius:10px;overflow:hidden}
.crw{display:flex;align-items:center;gap:8px;min-height:44px;padding:9px 16px;
  font:400 17px/1.25 Inter,sans-serif;color:#fff;position:relative}
.crw + .crw::before{content:"";position:absolute;left:16px;right:0;top:0;height:1px;
  background:rgba(84,84,88,.65)}
.crw__v{margin-left:auto;color:rgba(235,235,245,.6);text-align:right}
.crw__mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:16px}
.crw__ph{margin-left:auto;color:rgba(235,235,245,.3);text-align:right}
.crw--tint{color:#0A84FF}
.crw__ico{width:19px;height:19px;flex:none;color:#0A84FF}
.crw__blue{color:#0A84FF;margin-left:auto}
.cchev{width:7px;height:12px;flex:none;color:rgba(235,235,245,.3)}
.cupdn{width:11px;height:16px;flex:none;color:rgba(235,235,245,.3)}
.cinfo{width:18px;height:18px;flex:none;color:rgba(235,235,245,.6)}
.cout{width:14px;height:14px;flex:none;color:rgba(235,235,245,.6);margin-left:auto}
/* Form Toggle — the app sets no tint here, so it is the system green */
.csw{width:51px;height:31px;flex:none;border-radius:999px;background:#39393D;
  position:relative;margin-left:auto}
.csw::after{content:"";position:absolute;top:2px;left:2px;width:27px;height:27px;
  border-radius:50%;background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.35)}
.csw.is-on{background:#30D158}
.csw.is-on::after{transform:translateX(20px)}

/* ── the label above the frame, so the tally reads as a tally ────── */
.cdemo__cap{margin-bottom:10px;min-height:1.5em;color:var(--text-2);
  font-size:var(--fs-sm);line-height:1.5;
  font-family:var(--font-family-body,Inter),sans-serif}
.cdemo__cap b{color:var(--text);font-weight:600}

/* ── multiview as separated cards ──────────────────────────────────────
   Not the app's own arrangement: Bridge butts its tiles together on one
   hairline grid. On the marketing pages each tile is its own card — small
   radius, a gap between them, and the SAME stroke on every tile so the row
   reads evenly whether a tile is selected or not. Only the framing changes;
   what sits inside a tile is the app's. */
.bgridwrap--cards{outline:none}
.bgridwrap--cards .bbig{gap:10px;margin-bottom:10px}
.bgridwrap--cards .bgrid{gap:10px}
.bgridwrap--cards .btile{border-radius:10px;box-shadow:none;
  border:1px solid var(--stroke);overflow:hidden}
.bgridwrap--cards .btile.is-pvw{border-color:var(--green)}
.bgridwrap--cards .btile.is-pgm{border-color:var(--red)}
/* the card's own border is the cue here — no second ring inside it */
.bgridwrap--cards .btile.is-pvw::after,.bgridwrap--cards .btile.is-pgm::after{display:none}
