:root{
  --bg:#0f1115; --panel:#171a21; --line:#272c36; --fg:#e6e9ef; --muted:#8b93a3;
  --accept:#2ecc71; --reject:#ff4d4f; --unsure:#f0ad4e; --accent:#4d9fff;
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--fg);
     font:14px/1.45 system-ui,-apple-system,Segoe UI,Roboto,sans-serif}
header{display:flex;align-items:center;gap:18px;padding:10px 16px;
       background:var(--panel);border-bottom:1px solid var(--line);flex-wrap:wrap}
h1{font-size:15px;margin:0;font-weight:600;letter-spacing:.2px}
.run{color:var(--muted);font-weight:400;font-size:12px;margin-left:6px}
nav{display:flex;gap:6px}
.tab{background:none;border:1px solid var(--line);color:var(--muted);padding:5px 12px;
     border-radius:6px;cursor:pointer;font-size:13px;text-decoration:none}
.tab.active,.tab:hover{color:var(--fg);border-color:var(--accent)}
.who{margin-left:auto;display:flex;align-items:center;gap:14px;color:var(--muted)}
.who input{background:#0f1115;border:1px solid var(--line);color:var(--fg);
           border-radius:5px;padding:4px 7px}
.progress{font-variant-numeric:tabular-nums}
.view{padding:14px 16px}
/* !important because .hidden is a UTILITY: it must beat any component rule that
   sets display, whatever the source order. Without it, `.signin{display:flex}`
   further down this file won on equal specificity and the sign-in overlay could
   never be dismissed -- the app booted fine behind a box that would not close. */
.hidden{display:none!important}
.muted{color:var(--muted)}

.filters{display:flex;gap:8px;align-items:center;margin-bottom:12px;flex-wrap:wrap}
select,input[type=text],.filters input{background:var(--panel);color:var(--fg);
  border:1px solid var(--line);border-radius:6px;padding:5px 8px}
button{background:var(--panel);color:var(--fg);border:1px solid var(--line);
       border-radius:6px;padding:5px 10px;cursor:pointer}
button:hover{border-color:var(--accent)}

/* --------------------------------------------------------------- gallery */
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px}
.card{background:var(--panel);border:1px solid var(--line);border-radius:8px;
      overflow:hidden;cursor:pointer;position:relative}
.card:hover{border-color:var(--accent)}
.card img{width:100%;display:block;aspect-ratio:1;object-fit:cover;background:#000}
.card .meta{padding:6px 8px;font-size:11px;color:var(--muted);
            display:flex;justify-content:space-between;gap:6px}
.card .name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.badge{position:absolute;top:6px;right:6px;font-size:10px;padding:2px 6px;
       border-radius:10px;background:#000a;border:1px solid var(--line)}
.badge.done{background:var(--accept);color:#04220f;border-color:transparent}
.badge.partial{background:var(--unsure);color:#2a1c00;border-color:transparent}
.chips{display:flex;flex-wrap:wrap;gap:3px;padding:0 8px 8px}
.chip{width:9px;height:9px;border-radius:50%}

/* ---------------------------------------------------------------- viewer */
.viewer-bar{display:flex;align-items:center;gap:10px;margin-bottom:10px;flex-wrap:wrap}
.vtitle{font-weight:600}
.spacer{flex:1}
.slider input{vertical-align:middle}
/* THE FILM IS BOUND BY HEIGHT, NOT WIDTH.
   It used to be `flex:1` with `img{width:100%}`, so on a wide monitor the film
   grew to fill the row and its height followed, pushing the page into vertical
   scroll -- a radiologist could not see a whole film at once. The stage now
   takes its size from the viewport height and derives its width from the
   aspect ratio. Base film and overlays are the same square canvas, so
   `inset:0` keeps every layer and every percent-anchored badge registered at
   any size. --chrome is the header, viewer bar, hint line and padding above
   and below the stage. */
.viewer-body{display:flex;gap:14px;align-items:flex-start;--chrome:210px}
.stage-wrap{flex:1;min-width:0;display:flex;justify-content:center}
.stage{position:relative;aspect-ratio:1/1;height:min(calc(100vh - var(--chrome)),82vh);
       width:auto;max-width:100%;flex:none;background:#000;border-radius:8px;
       overflow:hidden;border:1px solid var(--line)}
.stage img,#layers img{width:100%;height:100%;display:block;object-fit:contain}
#layers{position:absolute;inset:0}
#layers img{position:absolute;inset:0;pointer-events:none}
/* 1:1 mode: the stage stops shrinking to fit and the wrapper scrolls, for
   inspecting a boundary at full resolution. */
.stage-wrap.full{overflow:auto;justify-content:flex-start;
                 max-height:calc(100vh - var(--chrome))}
.stage-wrap.full .stage{height:1024px;max-width:none;flex:none}

/* The sidebar scrolls inside itself so the PAGE never scrolls: a film with
   eight findings used to push the gallery button off the bottom of the screen. */
aside#panel{width:330px;flex:none;display:flex;flex-direction:column;gap:8px;
            max-height:calc(100vh - var(--chrome));overflow-y:auto;padding-right:2px}

@media (max-width:900px){
  .viewer-body{flex-direction:column;--chrome:150px}
  aside#panel{width:100%;max-height:none}
  .stage{height:auto;width:100%}
}

.pred{background:var(--panel);border:1px solid var(--line);border-radius:8px;padding:9px 10px}
.pred.sel{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent) inset}
.pred .top{display:flex;align-items:center;gap:8px}
.swatch{width:12px;height:12px;border-radius:3px;flex:none}
.pred .fname{font-weight:600;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pred .idx{color:var(--muted);font-size:11px}
.pred .sub{color:var(--muted);font-size:11px;margin:4px 0 7px}
.btnrow{display:flex;gap:5px;margin-bottom:5px}
.btnrow button{flex:1;padding:4px 0;font-size:12px}
.btnrow .lbl{color:var(--muted);font-size:11px;align-self:center;width:42px;flex:none}
button.on-accept{background:var(--accept);color:#04220f;border-color:transparent;font-weight:600}
button.on-reject{background:var(--reject);color:#2a0000;border-color:transparent;font-weight:600}
button.on-unsure{background:var(--unsure);color:#2a1c00;border-color:transparent;font-weight:600}
button.on-mask{background:var(--accent);color:#01172f;border-color:transparent;font-weight:600}
.pred textarea{width:100%;background:#0f1115;color:var(--fg);border:1px solid var(--line);
               border-radius:5px;padding:4px 6px;font:12px/1.3 inherit;resize:vertical}
.nomask{color:var(--unsure);font-size:11px}
.hint{color:var(--muted);font-size:11px;margin-top:10px}

/* ----------------------------------------------------------------- stats */
table{border-collapse:collapse;width:100%;font-variant-numeric:tabular-nums}
th,td{border-bottom:1px solid var(--line);padding:7px 9px;text-align:right;font-size:13px}
th:first-child,td:first-child{text-align:left}
thead th{color:var(--muted);font-weight:500;position:sticky;top:0;background:var(--bg)}
td.tp{color:var(--accept)} td.fp{color:var(--reject)}
td.na{color:#555}
tfoot td{font-weight:600}
.note{background:var(--panel);border:1px solid var(--line);border-left:3px solid var(--unsure);
      border-radius:6px;padding:10px 12px;margin-bottom:14px;color:var(--muted);max-width:900px}
.bar{height:5px;border-radius:3px;background:#2a2f3a;overflow:hidden;margin-top:3px}
.bar i{display:block;height:100%;background:var(--accept)}

/* ------------------------------------------------- score badges on the film */
#layers .tag{position:absolute;transform:translate(-50%,-50%);pointer-events:auto;
  background:#000c;border:1px solid;border-radius:11px;padding:2px 7px;
  font:600 11px/1.2 ui-monospace,SFMono-Regular,Menlo,monospace;color:#fff;
  white-space:nowrap;cursor:pointer;backdrop-filter:blur(2px)}
#layers .tag.sel{box-shadow:0 0 0 2px #fff6}
#layers .tag b{margin-right:4px;font-weight:700}

/* --------------------------------------------------------- score in a card */
.score{display:flex;align-items:baseline;gap:7px;margin:5px 0 3px}
.score .prob{font:700 15px/1 ui-monospace,SFMono-Regular,Menlo,monospace}
.score .thr{color:var(--muted);font-size:11px;font-variant-numeric:tabular-nums}
.pbar{position:relative;height:6px;border-radius:3px;background:#2a2f3a;overflow:visible}
.pbar i{display:block;height:100%;border-radius:3px}
.pbar u{position:absolute;top:-3px;width:2px;height:12px;background:#fff;opacity:.85}
.pbar.tight u{background:var(--unsure);opacity:1;box-shadow:0 0 4px var(--unsure)}

/* ------------------------------------------------------ below-threshold list */
.negs{background:var(--panel);border:1px solid var(--line);border-radius:8px;
      padding:7px 10px;color:var(--muted)}
.negs summary{cursor:pointer;font-size:12px}
.neg{display:flex;align-items:center;gap:7px;padding:3px 0;font-size:12px;
     font-variant-numeric:tabular-nums}
.neg .nf{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.neg .np{color:var(--fg)}
.neg .nm{width:52px;text-align:right;color:var(--muted)}

/* --------------------------------------------- offline build: storage warning */
.offline-warn{background:var(--reject);color:#2a0000;font-weight:600;
  padding:8px 16px;text-align:center;font-size:13px}

/* Public read-only instance (Tailscale Funnel). The banner is the only thing
   that tells a visitor why the verdict buttons are dead, so it stays at the top
   of the document rather than inside a view that can be switched away. */
.ro-banner {
  background: #2b3b52; color: #d8e4f5; font-size: 13px;
  padding: 7px 14px; border-bottom: 1px solid #3d4f68;
}
body.readonly button[data-lv][disabled],
body.readonly button[data-mv][disabled],
body.readonly textarea[disabled] { opacity: .38; cursor: not-allowed; }

/* ------------------------------------------------------- per-finding masks */
/* Was a bare 12px checkbox in the card header, which nobody found. Turning one
   boundary on and off is the core motion of reading a film, so it is a labelled
   button in that finding's own card, tinted with the finding's colour. */
.maskrow{display:flex;gap:5px;margin:6px 0 5px}
.masktoggle{flex:1;font-size:12px;padding:5px 0;border-color:var(--line);
            color:var(--muted);text-align:center}
.masktoggle.on{background:color-mix(in srgb, var(--c) 22%, transparent);
               border-color:var(--c);color:var(--fg);font-weight:600}
.masktoggle:disabled{opacity:.45;cursor:not-allowed;font-style:italic}
.maskonly{flex:none;font-size:12px;padding:5px 9px;color:var(--muted)}

/* The reviewer's running record for this finding, where the judgement is made. */
.record{font-size:11px;color:var(--muted);margin:0 0 6px;
        font-variant-numeric:tabular-nums}
.record .tp{color:var(--accept)} .record .fp{color:var(--reject)}
.vrecord{font-size:12px;color:var(--muted);font-variant-numeric:tabular-nums}
.vrecord .tp{color:var(--accept)} .vrecord .fp{color:var(--reject)}

/* ------------------------------------------------------------ done / resume */
.donebtn.on{background:var(--accept);color:#04220f;border-color:transparent;font-weight:600}
.resume{border-color:var(--accent);color:var(--fg)}
.whobtn{border-color:var(--line);color:var(--fg);font-weight:600}
.card.isdone{opacity:.62}
.card.isdone:hover{opacity:1}

/* ---------------------------------------------------------------- sign-in */
.signin{position:fixed;inset:0;background:#0b0d11ee;z-index:50;
        display:flex;align-items:center;justify-content:center;padding:16px}
.signin-card{background:var(--panel);border:1px solid var(--line);border-radius:10px;
             padding:20px 22px;max-width:430px;width:100%}
.signin-card h2{margin:0 0 6px;font-size:17px}
.signin-card p{margin:0 0 14px;font-size:13px}
.signin-card input{width:100%;background:#0f1115;border:1px solid var(--line);
                   color:var(--fg);border-radius:6px;padding:8px 10px;font-size:15px}
.signin-card button[type=submit]{margin-top:12px;width:100%;padding:8px;
                                 border-color:var(--accent);font-weight:600}
.signin-card label.f{display:block;font-size:12px;color:var(--muted);margin-top:10px}
.signin-card label.f input{margin-top:4px}
.signin-err{margin-top:10px;font-size:13px;color:#2a0000;background:var(--reject);
            border-radius:6px;padding:7px 10px;font-weight:600}
.known{display:flex;flex-wrap:wrap;gap:6px;margin-top:10px;font-size:12px;align-items:center}
.chipname{font-size:12px;padding:3px 8px}
.err-banner{background:var(--reject);color:#2a0000;font-weight:600;padding:8px 16px;font-size:13px}
