
/* == Brand tokens =========================================== */
:root {
  --bg:       #14181D;
  --surface:  #1A1F25;
  --surface2: #212831;
  --border:   #262E37;
  /* THE ACCENT IS THREE TOKENS, AND THE SPLIT IS LOAD-BEARING (2026-08-24).
     --accent had one name and two jobs, fill and ink, and the two want
     OPPOSITE lightness. It is dark in BOTH themes, so `color:var(--accent)`
     disappears on a dark page and `color:var(--text)` disappears on an accent
     fill in light. Four controls shipped broken that way before the split, at
     1.16:1 to 2.59:1. Use:
       --accent       FILL and BORDER only. Never a text colour. Grep says so:
                      `color:var(--accent)` should return nothing.
       --accent-ink   the label ON an --accent fill. 7.08:1 dark, 14.10:1 light.
       --accent-text  accent-coloured TEXT on the page background.
                      7.27:1 on --bg dark, 12.66:1 on the header light. */
  --accent:     #454F5B;   /* Ops Board identity, 2026-07-27. Chosen as a DEEPER teal:
                             #2FD4C4 measured only dE 11.9 from Forms green, the closest
                             pair on the whole platform, and darker lightness separated
                             them (dE 19.3) while keeping the hue. It has since been
                             retuned to this slate and is no longer teal; the name and
                             this note are kept because the dE reasoning still governs
                             how far it may move back toward Forms green. */
  --accent-ink:  #E9EDF2;
  --accent-text: #9AA7B5;
  --accent-dim: rgba(233, 237, 242,0.09);
  --accent-mid: rgba(233, 237, 242,0.20);
  --mauve:      #8C97A3;  /* deprecated warm; now neutral */
  --mauve-dim:  rgba(140,151,163,0.10);
  --p9-gray:    #888888;
  --text:  #E9EDF2;
  --muted: #7E8994;
  --radius: 5px;
  --shadow: 0 16px 48px rgba(0,0,0,0.75);
  --font-body:    'Avenir Next', Avenir, 'Century Gothic', 'Gill Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-display: 'Avenir Next', 'Avenir', 'Nunito Sans', system-ui, sans-serif; /* platform font unification */
  --font-mono:    'SF Mono', 'Menlo', 'Consolas', monospace;
}
[data-theme="light"] {
  --bg:         #F7F4F1;
  --surface:    #FFFFFF;
  --surface2:   #EEE9E4;
  --border:     #DDD8D3;
  --accent:     #1F252C;   /* Fill and border only, as in the dark block above. Was
                              described here as "teal, darkened for light bg" long after
                              it stopped being teal, which is part of why the fill going
                              near-black went unnoticed. */
  --accent-ink:  #F7F4F1;  /* on the --accent fill: 14.10:1 */
  --accent-text: #1F252C;  /* as ink on light surfaces this token is already safe:
                              12.66:1 on the header, 15.45:1 on --surface */
  --accent-dim: rgba(20, 24, 29,0.09);
  --accent-mid: rgba(20, 24, 29,0.16);
  --mauve:      #5c6779;
  --mauve-dim:  rgba(92,103,121,0.10);
  --text:       #1A1714;
  /* #888888 is a dark-theme grey and was never remapped here: it measured
     2.90:1 on the light header, 2.94:1 on --surface2, 3.24:1 on --bg and
     3.54:1 on --surface. That is ~40 rules (.stat-lbl, .day-hdr, .atbl th,
     .crew-ph, .crew-count, .ci-meta, .rt-cities, .info-lbl, .legend-item,
     .pp-label, .week-lbl, .last-updated, .data-range ...) plus six JS
     template strings that set style="color:var(--muted)" on table cells.
     #666360 keeps the neutral warm-grey read and clears 4.5:1 on ALL four
     light surfaces (worst case 4.89:1 on the header). Dark --muted #7E8994
     is fine at 4.17-5.00 and is deliberately unchanged. */
  --muted:      #666360;
  /* Companion to --muted that the light block also missed: it inks .brand-sub
     ("PRIVATE AIR · OPS BOARD") on the light header at 2.90:1. */
  --p9-gray:    #666360;
  --shadow:     0 4px 16px rgba(0,0,0,0.10);
}

/* == Reset & base =========================================== */
*{box-sizing:border-box;margin:0;padding:0}
body{
  background:var(--bg);color:var(--text);
  font-family:var(--font-body);
  height:100vh;display:flex;flex-direction:column;overflow:hidden;
  -webkit-font-smoothing:antialiased;
}

/* == HEADER ================================================= */
.hdr{
  background:#0F1216;
  border-bottom:1px solid var(--border);
  padding:0 24px;height:62px;
  display:flex;align-items:center;flex-shrink:0;gap:20px;
  position:relative;
}
/* Warm radial glow from logo side - subtle brand warmth */
.hdr::before{
  content:'';position:absolute;inset:0;
  background:radial-gradient(ellipse 420px 62px at 160px 31px, rgba(233, 237, 242,0.05) 0%, transparent 70%);
  pointer-events:none;
}

/* P9 icon mark */
.p9-mark{
  width:36px;height:36px;border-radius:50%;
  background:#000;
  border:1.5px solid rgba(213,185,170,0.45);
  display:flex;align-items:center;justify-content:center;
  flex-shrink:0;letter-spacing:0.04em;font-size:12px;font-weight:700;
  color:#fff;gap:0;user-select:none;
}
.p9-mark .nine{color:#D5B9AA;}

/* PLANET9 wordmark */
.brand{display:flex;align-items:center;gap:12px;text-decoration:none;}
.brand-wordmark-wrap{display:flex;flex-direction:column;gap:1px;}
.brand-wordmark{
  font-family:var(--font-body);
  font-weight:600;
  font-size:16px;
  letter-spacing:0.22em;
  color:#fff;
  text-transform:uppercase;
  line-height:1;
}
.brand-wordmark .nine{color:#D5B9AA;}
.brand-sub{
  font-size:8px;letter-spacing:0.32em;
  color:var(--p9-gray);text-transform:uppercase;
  font-weight:400;
}

.hdr-div{width:1px;height:30px;background:var(--border);flex-shrink:0;}
.hdr-center{flex:1;display:flex;align-items:center;justify-content:center;gap:14px;}
.hdr-right{display:flex;flex-direction:column;align-items:flex-end;gap:3px;}
.last-updated{font-size:10px;color:rgba(213,185,170,0.6);letter-spacing:0.02em;}
.last-updated span{color:var(--text);font-weight:600;font-size:11px;}
/* The wordmark had only a dark-theme ink: #fff measured 1.22:1 on the light
   header and the Sand "9" 1.52:1, so the app's own name was missing in light
   mode. Kept as a pure recolour rather than moving the lockup onto a dark chip:
   the .p9-mark roundel beside it is already #000 in both themes, so the dark
   anchor is present and the header layout does not have to change. The "9"
   stays in the Sand family, darkened the same way AUA's splash "9" was. */
[data-theme="light"] .brand-wordmark{color:var(--text);}          /* 14.62:1 */
[data-theme="light"] .brand-wordmark .nine{color:#8C5A3C;}        /* 4.72:1  */
[data-theme="light"] .hdr{background:#ECE8E2;border-bottom:1px solid #DDD8D3;}
[data-theme="light"] .hdr::before{display:none;}
[data-theme="light"] .today-btn{border-color:rgba(20, 24, 29,0.5);color:var(--accent-text);}
[data-theme="light"] .tgl-group{background:rgba(0,0,0,0.04);}
[data-theme="light"] .time-tgl{border-color:#CFC9C2;}
[data-theme="light"] .last-updated{color:var(--muted);}
.data-range{font-size:9px;color:var(--muted);letter-spacing:0.05em;}

/* == TOGGLE ================================================= */
.tgl-group{
  display:flex;background:rgba(255,255,255,0.05);
  border:1px solid rgba(233, 237, 242,0.25);border-radius:7px;padding:3px;gap:2px;
}
.tgl{
  background:transparent;border:none;color:var(--muted);
  padding:5px 24px;border-radius:5px;
  font-size:12px;font-weight:600;letter-spacing:0.06em;
  cursor:pointer;transition:all .15s;font-family:var(--font-body);
}
.tgl.active{
  /* Selected segment: solid fill + light ink. The old pattern (tinted
     background, accent-coloured text) only worked while the accent was a
     bright colour; once it became a dark grey the label vanished into its own
     tint at 2.04 contrast (Kevin, 2026-07-27). */
  background:var(--accent);color:var(--accent-ink);
  box-shadow:0 1px 4px rgba(0,0,0,0.35);
}
.tgl:hover:not(.active){color:var(--text);background:rgba(255,255,255,0.06);}
[data-theme="light"] .tgl{color:var(--muted);}
[data-theme="light"] .tgl-group{border-color:rgba(139,90,77,0.25);}
/* SELECTED SEGMENT IN LIGHT. --accent is a dark slate in BOTH themes, so
   `background:var(--accent)` paints a near-black slab on a cream header.
   --accent-ink (added 2026-08-24) makes that slab's LABEL legible, which is the
   right answer for a button and the wrong one here. That is the same self-tint trap
   the #dsb-act comment below measures, but it needs the opposite fix here.
   #dsb-act keeps the dark fill and lightens its ink because it is a call to
   action, where a dark button on a light page is intentional. These are
   persistent chrome: three black slabs across the header read as a broken
   light theme, which is exactly how it was reported (Kevin, 2026-08-24).
   So the FILL changes instead: the conventional light segmented control, a
   raised surface pill carrying full-strength ink. #1A1714 on #FFFFFF is
   15.90:1, against 2.59:1 before.
   The colour was not the only bug. `[data-theme="light"] .tgl` above and
   `.tgl.active` are BOTH (0,2,0), so the later one won and the selected label
   was being inked with --muted by accident rather than by intent. These
   overrides are (0,3,0) and settle it on purpose. */
[data-theme="light"] .tgl.active{
  background:var(--surface);color:var(--text);
  box-shadow:0 1px 3px rgba(20,24,29,0.18);
}

/* == TIME MODE TOGGLE ======================================= */
.time-mode-wrap{display:flex;align-items:center;gap:2px;}
.time-tgl{
  background:transparent;border:1px solid var(--border);border-radius:3px;
  color:var(--muted);font-size:9px;font-weight:700;letter-spacing:0.1em;
  padding:2px 8px;cursor:pointer;font-family:var(--font-body);transition:all .15s;
}
.time-tgl.active{background:var(--accent);border-color:var(--accent);color:var(--accent-ink);}
.time-tgl:hover:not(.active){color:var(--text);border-color:rgba(213,185,170,0.25);}
/* Same trap, same fix as .tgl.active above. Was #1A1714 ink on a #1F252C fill:
   1.16:1, a label that was not there. Now 15.90:1. The border carries the
   selected read at this size, where the fill alone is only 1.07:1 off the
   header. */
[data-theme="light"] .time-tgl.active{
  background:var(--surface);border-color:#A9A29A;color:var(--text);
}
.tz-unk{color:#ef4444;font-style:italic;}
.tz-note{font-size:9px;color:#ef4444;letter-spacing:0.04em;display:none;margin-left:6px;align-self:center;}
/* #ef4444 is the dark-theme red: 3.43:1 on the light page, and both of these
   are small text. Same hue, darkened. */
[data-theme="light"] .tz-unk,
[data-theme="light"] .tz-note{color:#B3261E;}                     /* 5.97:1 */
.tz-note.visible{display:inline;}


/* == WEEK NAV =============================================== */
.week-nav{display:flex;align-items:center;gap:8px;}
.nav-btn{
  background:transparent;
  border:1px solid rgba(213,185,170,0.35);
  color:rgba(213,185,170,0.75);
  width:28px;height:28px;border-radius:4px;
  cursor:pointer;font-size:17px;
  display:flex;align-items:center;justify-content:center;
  transition:all .15s;
}
.nav-btn:hover{border-color:rgba(213,185,170,0.6);color:var(--text);background:var(--accent-dim);}
.today-btn{
  background:transparent;
  border:1px solid rgba(233, 237, 242,0.45);
  color:var(--text);
  padding:4px 14px;border-radius:4px;
  font-size:11px;font-weight:700;letter-spacing:0.08em;
  cursor:pointer;font-family:var(--font-body);transition:all .15s;
}
.today-btn:hover{background:var(--accent-dim);border-color:rgba(213,185,170,0.6);}
.week-lbl{font-size:12px;color:rgba(213,185,170,0.7);min-width:185px;text-align:center;letter-spacing:0.03em;}
[data-theme="light"] .week-lbl{color:var(--muted);}
/* The light override was already here, but 0.7 alpha of the warm brown over
   the page measured 2.91:1 on the week arrows. Same hue at full strength. */
[data-theme="light"] .nav-btn{border-color:rgba(139,90,77,0.45);color:#6E4B42;} /* 6.97:1 */

/* == SCHEDULE =============================================== */
#sched-view{flex:1;overflow:auto;padding:0 0 12px;}
.grid-wrap{min-width:900px;padding:0 18px;}
.hdr-row{display:grid;grid-template-columns:104px repeat(7,1fr);gap:3px;margin-bottom:0;padding-bottom:3px;position:sticky;top:33px;z-index:30;background:var(--bg);}
.day-hdr{
  text-align:center;padding:5px;
  font-size:10px;color:var(--muted);font-weight:600;
  text-transform:uppercase;letter-spacing:0.1em;
}
.day-hdr.tcol{color:var(--text);}
.ac-row{display:grid;grid-template-columns:104px repeat(7,1fr);gap:3px;margin-bottom:3px;}
.ac-lbl{
  display:flex;align-items:center;justify-content:flex-end;
  padding-right:10px;font-size:11px;font-weight:700;
  color:var(--text);letter-spacing:0.07em;
}
.day-cell{
  background:var(--surface);
  border:1px solid var(--border);border-radius:var(--radius);
  min-height:68px;padding:3px;display:flex;flex-direction:column;gap:2px;
  transition:border-color .1s;
}
.day-cell.tcol{border-color:rgba(213,185,170,0.28);}
.day-cell:hover{border-color:rgba(213,185,170,0.18);}

/* Leg cards */
.leg-card{
  border-radius:4px;padding:4px 7px;cursor:pointer;
  font-size:11px;line-height:1.4;
  border-left:2.5px solid transparent;
  transition:filter .12s, transform .08s;
}
.leg-card:hover{filter:brightness(1.18);transform:translateY(-1px);}
.c-sched{background:rgba(99,168,255,0.14);border-left-color:rgba(99,168,255,0.7);color:#9AC5FF;}
.c-verif{background:rgba(69,224,160,0.12);border-left-color:rgba(69,224,160,0.65);color:#7CE9B8;}
.c-loggd{background:rgba(181,125,255,0.14);border-left-color:rgba(181,125,255,0.65);color:#CBA9FF;}
.c-amend{background:rgba(255,218,43,0.13);border-left-color:rgba(255,218,43,0.6);color:#FFE27A;}
.c-compl{background:rgba(72,77,88,0.72);border-left-color:rgba(135,143,158,0.65);color:#A0AAB8;}
[data-theme="light"] .c-sched{background:#DBEAFE;border-left-color:#2563EB;color:#1E40AF;}
[data-theme="light"] .c-verif{background:#DCFCE7;border-left-color:#16A34A;color:#14532D;}
[data-theme="light"] .c-loggd{background:#EDE9FE;border-left-color:#7C3AED;color:#4C1D95;}
[data-theme="light"] .c-amend{background:#FEF3C7;border-left-color:#D97706;color:#78350F;}
[data-theme="light"] .c-compl{background:#F1F5F9;border-left-color:#94A3B8;color:#475569;}
.card-rt{font-weight:700;font-size:11.5px;letter-spacing:0.04em;}
.card-tm{font-size:10px;opacity:.7;margin-top:1px;}
.card-cr{font-size:10px;opacity:.55;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:1px;}
.card-hd{font-weight:700;font-size:11px;letter-spacing:0.03em;line-height:1.35;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.card-inf{font-size:10px;opacity:.65;margin-top:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.card-crew{font-size:10px;font-weight:700;letter-spacing:0.07em;opacity:.88;margin-top:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}

/* Away cells - warm mauve instead of blue */
.away-cell{
  border-radius:4px;padding:5px 7px;
  background:rgba(140,151,163,0.20);
  border:1px dashed rgba(140,151,163,0.50);
  color:#B8948C;font-size:11px;
  display:flex;align-items:center;gap:5px;
  letter-spacing:0.03em;
}
/* #B8948C is a dark-theme warm tint: 2.28:1 once the mauve wash composites
   over a light card. Same warm family, darkened. */
[data-theme="light"] .away-cell{color:#6E4B42;}                   /* 6.35:1 */

/* == SKELETON (pre-data shimmer placeholders) =============== */
.skel-card{
  height:52px;border-radius:4px;
  background:#17171a;
  position:relative;overflow:hidden;
}
.skel-card::after{
  content:'';position:absolute;inset:0;
  background:linear-gradient(90deg,transparent,rgba(213,185,170,0.05),transparent);
  animation:skel-shimmer 1.4s ease-in-out infinite;
}
@keyframes skel-shimmer{
  from{transform:translateX(-100%);}
  to{transform:translateX(100%);}
}
[data-theme="light"] .skel-card{background:#EEE9E4;}
[data-theme="light"] .skel-card::after{
  background:linear-gradient(90deg,transparent,rgba(139,90,77,0.06),transparent);
}

/* == CREW VIEW ============================================== */
#crew-view{flex:1;display:none;overflow:hidden;}
.crew-wrap{display:flex;height:100%;}
.crew-sidebar{
  width:275px;min-width:275px;
  background:var(--surface);border-right:1px solid var(--border);
  display:flex;flex-direction:column;overflow:hidden;
}
.crew-search-wrap{padding:10px 12px;border-bottom:1px solid var(--border);}
.crew-search{
  width:100%;background:var(--bg);
  border:1px solid var(--border);color:var(--text);
  border-radius:var(--radius);padding:7px 10px;
  font-size:13px;outline:none;font-family:var(--font-body);
  transition:border-color .15s;
}
.crew-search:focus{border-color:rgba(213,185,170,0.4);}
.crew-count{
  font-size:10px;color:var(--muted);
  padding:5px 13px 4px;border-bottom:1px solid var(--border);
  letter-spacing:0.04em;
}
.crew-scroll{overflow-y:auto;flex:1;}
.crew-item{
  padding:10px 14px;border-bottom:1px solid var(--border);
  cursor:pointer;transition:background .12s;
  border-left:2.5px solid transparent;
}
.crew-item:hover{background:var(--surface2);}
.crew-item.active{background:var(--accent-dim);border-left-color:var(--accent);}
.ci-name{font-size:13px;font-weight:600;letter-spacing:0.01em;}
.ci-meta{
  font-size:11px;color:var(--muted);margin-top:3px;
  display:flex;gap:5px;align-items:center;flex-wrap:wrap;
}

/* Role badges */
.rbadge{font-size:9px;padding:2px 6px;border-radius:3px;font-weight:700;letter-spacing:0.07em;}
.r-pic{background:rgba(99,168,255,0.18);color:#9AC5FF;}
.r-sic{background:rgba(69,224,160,0.15);color:#7CE9B8;}
.r-csr{background:var(--accent-dim);color:var(--text);border:1px solid rgba(233, 237, 242,0.25);}
[data-theme="light"] .r-pic{background:#DBEAFE;color:#1D4ED8;}
[data-theme="light"] .r-sic{background:#DCFCE7;color:#15803D;}
[data-theme="light"] .r-csr{background:var(--accent-dim);color:var(--accent-text);border:1px solid rgba(139,90,77,0.2);}
.theme-btn{
  background:rgba(213,185,170,0.1);border:1px solid rgba(213,185,170,0.4);
  color:rgba(213,185,170,0.9);width:30px;height:30px;border-radius:4px;
  cursor:pointer;font-size:15px;display:flex;align-items:center;justify-content:center;
  transition:all .15s;flex-shrink:0;line-height:1;
}
.theme-btn:hover{border-color:rgba(213,185,170,0.7);color:var(--text);background:var(--accent-dim);}
/* Glyph button: 0.9 alpha measured 3.62:1 on its own tint. Full strength. */
[data-theme="light"] .theme-btn{color:#6E4B42;border-color:rgba(139,90,77,0.45);background:rgba(139,90,77,0.07);} /* 5.74:1 */

/* Crew detail panel */
.crew-detail-panel{flex:1;overflow-y:auto;padding:24px;}
.crew-ph{
  display:flex;align-items:center;justify-content:center;
  height:100%;color:var(--muted);font-size:14px;letter-spacing:0.03em;
}
.cdh{
  display:flex;gap:16px;align-items:flex-start;
  margin-bottom:22px;padding-bottom:22px;
  border-bottom:1px solid var(--border);flex-wrap:wrap;
}
.avatar{
  width:52px;height:52px;border-radius:50%;
  border:1.5px solid rgba(213,185,170,0.35);
  background:var(--surface2);
  display:flex;align-items:center;justify-content:center;
  font-family:var(--font-mono);font-size:13px;font-weight:700;letter-spacing:0.06em;color:var(--text);flex-shrink:0;
}
.cname{font-size:18px;font-weight:600;letter-spacing:0.01em;}
.cid-row{
  font-size:12px;color:var(--muted);margin-top:5px;
  display:flex;gap:16px;flex-wrap:wrap;
}
.croles{display:flex;gap:5px;margin-top:7px;}
.stats-row{display:flex;gap:10px;margin-left:auto;}
.stat-box{
  background:var(--surface2);border:1px solid var(--border);
  border-radius:var(--radius);padding:10px 16px;text-align:center;min-width:72px;
}
.stat-val{
  font-family:var(--font-display);font-size:22px;
  color:var(--text);line-height:1;
}
.stat-lbl{font-size:9px;color:var(--muted);margin-top:3px;letter-spacing:0.08em;text-transform:uppercase;}
.sec-title{
  font-size:10px;font-weight:600;color:var(--muted);
  text-transform:uppercase;letter-spacing:0.12em;margin-bottom:9px;
}
.atbl{width:100%;border-collapse:collapse;font-size:12px;}
.atbl th{
  text-align:left;padding:6px 8px;color:var(--muted);
  font-size:10px;font-weight:600;text-transform:uppercase;
  letter-spacing:0.08em;border-bottom:1px solid var(--border);
}
.atbl td{padding:7px 8px;border-bottom:1px solid var(--border);vertical-align:middle;}
.atbl tr:hover td{background:var(--surface2);}

/* == MODAL ================================================== */
.modal-ov{
  position:fixed;inset:0;background:rgba(0,0,0,0.72);
  z-index:200;display:none;align-items:center;justify-content:center;padding:20px;
  backdrop-filter:blur(2px);
}
.modal-ov.open{display:flex;}
.modal{
  background:var(--surface);
  border:1px solid var(--border);border-radius:10px;
  width:100%;max-width:700px;max-height:88vh;
  display:flex;flex-direction:column;box-shadow:var(--shadow);
}
.m-hdr{
  padding:16px 20px;border-bottom:1px solid var(--border);
  display:flex;align-items:flex-start;justify-content:space-between;flex-shrink:0;
  background:linear-gradient(to bottom, var(--surface2), var(--surface));
  border-radius:10px 10px 0 0;
}
.m-title{font-size:15px;font-weight:700;letter-spacing:0.03em;}
.m-sub{font-size:11px;color:var(--muted);margin-top:3px;letter-spacing:0.02em;}
.m-close{
  background:none;border:none;color:var(--muted);
  font-size:22px;cursor:pointer;line-height:1;padding:0 3px;
  transition:color .15s;
}
.m-close:hover{color:var(--text);}
.m-body{padding:18px 20px;overflow-y:auto;flex:1;}
.route-blk{
  background:var(--surface2);border-radius:var(--radius);
  padding:14px 16px;display:flex;align-items:center;gap:12px;
  margin-bottom:14px;flex-wrap:wrap;
  border:1px solid var(--border);
}
.rt-airports{font-size:22px;font-weight:800;letter-spacing:0.06em;}
.rt-arr{color:var(--muted);}
.rt-cities{font-size:11px;color:var(--muted);margin-top:3px;letter-spacing:0.02em;}
.rt-meta{margin-left:auto;text-align:right;font-size:12px;color:var(--muted);line-height:1.6;}
.info-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:14px;}
.info-box{background:var(--surface2);border-radius:var(--radius);padding:10px 12px;border:1px solid var(--border);}
.info-lbl{font-size:9px;color:var(--muted);text-transform:uppercase;letter-spacing:0.1em;margin-bottom:4px;}
.info-val{font-size:13px;font-weight:600;letter-spacing:0.01em;}
.msec{margin-bottom:16px;}
.msec-title{
  font-size:9px;font-weight:600;color:var(--muted);
  text-transform:uppercase;letter-spacing:0.12em;
  margin-bottom:8px;padding-bottom:5px;border-bottom:1px solid var(--border);
}
.crew-row{
  display:flex;align-items:center;gap:9px;
  padding:7px 0;border-bottom:1px solid var(--border);font-size:12px;
}
.crew-row:last-child{border-bottom:none;}
.pax-tbl{width:100%;border-collapse:collapse;font-size:12px;}
.pax-tbl th{
  text-align:left;padding:5px 8px;color:var(--muted);
  font-size:10px;font-weight:600;text-transform:uppercase;
  letter-spacing:0.08em;border-bottom:1px solid var(--border);
}
.pax-tbl td{padding:6px 8px;border-bottom:1px solid var(--border);vertical-align:middle;}
.pax-tbl tr:last-child td{border-bottom:none;}
.pax-tbl tr:hover td{background:rgba(255,255,255,0.02);}
.pp{font-family:var(--font-mono);color:var(--muted);font-size:11px;}
/* "not on file" was inked in --border, which measured 1.21:1 on the modal
   surface: not quiet, invisible. It is a factual statement about a passenger
   and has to be readable. --muted is the companion that clears 4.5:1 in both
   themes (4.65:1 dark, 5.97:1 light) and still reads as secondary. */
.pp-none{color:var(--muted);font-size:11px;font-style:italic;}
/* The reveal control. Deliberately reads as a BUTTON rather than as a label:
   clicking it fetches a travel document number from the server and writes a
   record of who did, so it must not look like inert text.
   MEASURED on the modal surface it sits on, in both themes: label 11.8:1 dark
   and 14.9:1 light on the fill; border 3.4:1 against the surround in both. The
   border is doing the work at this size, where a 9% fill alone is easy to miss,
   so it is not --accent-mid (2.36:1, which is what it was first written as). */
.pp-reveal{
  font:inherit;font-family:var(--font-mono);font-size:11px;letter-spacing:0.03em;
  color:var(--text);background:var(--accent-dim);
  border:1px solid rgba(233,237,242,0.42);border-radius:3px;
  padding:1px 7px;cursor:pointer;
  transition:background .12s,border-color .12s;
}
[data-theme="light"] .pp-reveal{border-color:rgba(20,24,29,0.50);}
.pp-reveal:hover{background:var(--accent-mid);}
.pp-reveal:focus-visible{outline:2px solid var(--text);outline-offset:2px;}
.pp-busy{cursor:progress;}
/* Shown: the number itself. Quiet fill, full-strength ink, so the value is the
   loudest thing in the cell and the control stops competing with it. */
.pp-shown{background:transparent;color:var(--text);}
/* Failed. Amber, not red: nothing is wrong with the flight, we just could not
   go and look. Red on this board means a leg state, and it must keep meaning
   only that. Ink 4.7:1 dark / 5.2:1 light on its own fill; border 3.3:1. */
.pp-err{
  background:rgba(191,124,42,0.16);border-color:rgba(214,150,60,0.78);
  color:#D69A44;font-style:italic;
}
[data-theme="light"] .pp-err{
  color:#8A5B14;background:rgba(191,124,42,0.12);border-color:rgba(170,110,30,0.85);
}
/* The standing notice under any group of reveal controls. Small, but not
   hidden: it says the reveals are recorded, and that belongs on screen. */
.pp-note{
  margin-top:8px;color:var(--muted);font-size:10px;line-height:1.6;
  letter-spacing:0.02em;max-width:52em;
}
.legs-list{display:flex;flex-direction:column;gap:4px;}
.leg-item{
  background:var(--surface2);border-radius:4px;
  padding:7px 10px;font-size:12px;
  display:flex;align-items:center;gap:8px;
  cursor:pointer;border-left:2.5px solid var(--border);
  transition:background .1s, border-color .1s;
}
.leg-item:hover{background:var(--accent-dim);border-left-color:rgba(213,185,170,0.4);}
.leg-item.current{border-left-color:var(--accent);background:var(--accent-dim);}
.li-num{color:var(--muted);font-size:11px;min-width:42px;}
.li-rt{font-weight:700;letter-spacing:0.04em;}
.li-dt{color:var(--muted);font-size:11px;}

/* Status badges */
.sbadge{display:inline-block;padding:2px 8px;border-radius:10px;font-size:9px;font-weight:700;letter-spacing:0.06em;}
.sb-s{background:rgba(99,168,255,0.18);color:#9AC5FF;}
.sb-v{background:rgba(69,224,160,0.15);color:#7CE9B8;}
.sb-l{background:rgba(181,125,255,0.18);color:#CBA9FF;}
.sb-a{background:rgba(255,218,43,0.18);color:#FFE27A;}
.sb-c{background:rgba(72,77,88,0.72);color:#A0AAB8;}
/* The .c-* schedule chips and the .r-* role badges both got a light palette;
   these status badges are the same vocabulary and were missed, so they kept
   dark-theme pastel ink on a translucent tint: 1.20:1 to 1.71:1 once the tint
   composites over a light card. Values mirror the .c-* light set exactly. */
[data-theme="light"] .sb-s{background:#DBEAFE;color:#1E40AF;}     /* 7.15:1 */
[data-theme="light"] .sb-v{background:#DCFCE7;color:#14532D;}     /* 8.30:1 */
[data-theme="light"] .sb-l{background:#EDE9FE;color:#4C1D95;}     /* 9.23:1 */
[data-theme="light"] .sb-a{background:#FEF3C7;color:#78350F;}     /* 8.15:1 */
[data-theme="light"] .sb-c{background:#F1F5F9;color:#475569;}     /* 6.92:1 */

/* == NO-DATA BANNER ========================================= */
#no-data-banner{display:none;margin:12px 18px 4px;padding:12px 16px;background:var(--accent-dim);border:1px solid rgba(213,185,170,0.18);border-radius:6px;text-align:center;}
#no-data-banner strong{font-size:12px;color:var(--text);display:block;margin-bottom:3px;}
#no-data-banner span{font-size:11px;color:var(--muted);}
#no-data-banner button{margin-top:8px;display:inline-block;background:var(--accent);border:1px solid var(--accent);color:var(--accent-ink);border-radius:4px;padding:4px 14px;font-size:10px;font-weight:700;letter-spacing:0.07em;cursor:pointer;font-family:var(--font-body);}

/* == DATA STATE BANNER ======================================
   #no-data-banner above means "this WEEK has no legs", which is a normal
   thing to see while paging through the calendar. This one is different: it
   describes the DATA ITSELF, and it is the only place the board admits that
   what you are looking at might not be current. Four states, deliberately
   distinguishable, because the worst outcome on this board is an empty grid
   that reads as "no flights today".
     stale  the refresh has stopped, or the report is old      amber
     empty  the report parsed to zero legs                     red
     error  nothing could be loaded at all                     red
   (loading is the skeleton plus "Loading..." in the header, no banner)
   Only the stripe and the title carry colour. Body copy stays on --text and
   --muted, which are already contrast-checked in both themes, so this cannot
   repeat the light-mode ink failures recorded above. */
/* The banner takes the HEADER's background, not --surface. Measured in the
   page: #ef4444 on --surface (#1A1F25) is 4.41:1, which fails AA for an 11px
   title. The rule this repo already learned is to darken the BACKGROUND rather
   than soften the red, and the header tone is the darkest surface the board
   already owns. It also reads correctly: this banner is chrome, sitting
   directly under the header, not a card on the page. */
#data-state-banner{
  display:none;flex-shrink:0;margin:10px 18px 0;padding:10px 14px;
  background:#0F1216;border:1px solid var(--border);
  border-left:3px solid var(--muted);border-radius:6px;
  align-items:center;gap:14px;flex-wrap:wrap;
}
[data-theme="light"] #data-state-banner{background:#ECE8E2;border-color:#DDD8D3;}
#data-state-banner.show{display:flex;}
#dsb-title{font-size:11px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:var(--text);}
#dsb-msg{flex:1;min-width:220px;font-size:11.5px;line-height:1.55;color:var(--text);}
#dsb-act{
  background:var(--accent);border:1px solid var(--accent);color:var(--accent-ink);
  border-radius:4px;padding:5px 14px;font-size:10px;font-weight:700;
  letter-spacing:0.07em;cursor:pointer;font-family:var(--font-body);
}
#dsb-act[hidden]{display:none;}
/* This button is where the self-tint trap was first measured: --accent fill with
   --text ink is 7.08:1 dark, but in LIGHT both tokens are near-black (#1F252C on
   #1A1714) and it fell to 1.16:1, an invisible button. It carried a one-off
   `[data-theme="light"] #dsb-act{color:#F7F4F1}` until 2026-08-24. That is now
   --accent-ink's whole job, so the one-off is gone and every accent fill on the
   page gets the same treatment instead of the ones someone remembered. */
/* #FFDA2B is the amber already used for Amended legs (12.25:1 as ink on
   --surface). #ef4444 is this file's established dark red, 4.87:1 on
   --surface. Both get the light-mode partner the .tz-note comment above
   established: darken the ink, never soften the hue. */
#data-state-banner.stale{border-left-color:#FFDA2B;}
#data-state-banner.stale #dsb-title{color:#FFDA2B;}
#data-state-banner.empty,#data-state-banner.error{border-left-color:#ef4444;}
#data-state-banner.empty #dsb-title,#data-state-banner.error #dsb-title{color:#ef4444;}
[data-theme="light"] #data-state-banner.stale{border-left-color:#8A6200;}
[data-theme="light"] #data-state-banner.stale #dsb-title{color:#8A6200;}   /* 5.48:1 */
[data-theme="light"] #data-state-banner.empty,[data-theme="light"] #data-state-banner.error{border-left-color:#B3261E;}
[data-theme="light"] #data-state-banner.empty #dsb-title,[data-theme="light"] #data-state-banner.error #dsb-title{color:#B3261E;}  /* 5.97:1 */
/* The age readout in the header. It is never absent once data is on screen:
   a board fed from a cache that does not say how old it is, is a board that
   implies it is live. */
.last-updated .age{color:var(--muted);font-weight:400;font-size:10px;}
.last-updated .age.old{color:#FFDA2B;font-weight:600;}
[data-theme="light"] .last-updated .age.old{color:#8A6200;}

/* == LEGEND ================================================= */
/* Sticky header stack (legend + day names) pinned OPAQUE and seamless while
   the schedule scrolls - no transparent gaps where cards slide past a
   stationary point (Kevin 2026-07-24 x2). */
.legend{
  position:sticky;top:0;z-index:31;background:var(--bg);
  display:flex;align-items:center;gap:12px;
  padding:12px 18px 8px;margin:0;flex-wrap:wrap;min-height:34px;box-sizing:border-box;
}
.legend-item{display:flex;align-items:center;gap:5px;font-size:10px;color:var(--muted);letter-spacing:0.03em;}
.legend-dot{width:8px;height:8px;border-radius:2px;}

/* == ROLE FILTER ============================================ */
.role-filter-wrap{
  display:flex;gap:4px;padding:7px 12px;
  border-bottom:1px solid var(--border);
}
.role-flt{
  background:transparent;border:1px solid var(--border);
  color:var(--muted);border-radius:4px;
  padding:3px 12px;font-size:10px;font-weight:700;
  letter-spacing:0.07em;cursor:pointer;
  font-family:var(--font-body);transition:all .15s;flex:1;
}
.role-flt.active{
  /* Same fix as .tgl.active. Was accent-coloured ink on an accent-mid tint,
     which worked only while the accent was bright; the deeper accent left the
     label at roughly its own background. Solid fill + light ink measures 7.08:1
     (Kevin caught this one on the crew list, 2026-07-27). */
  background:var(--accent);color:var(--accent-ink);
  border-color:var(--accent);
}
.role-flt:hover:not(.active){color:var(--text);background:var(--surface2);}
/* Same trap, same fix as .tgl.active. This one was also 1.16:1 in light: the
   selected role filter on the crew list was a black box with no readable
   label. */
[data-theme="light"] .role-flt.active{
  background:var(--surface);border-color:#A9A29A;color:var(--text);
}

/* == Scrollbar ============================================== */
::-webkit-scrollbar{width:5px;height:5px;}
::-webkit-scrollbar-track{background:transparent;}
::-webkit-scrollbar-thumb{background:var(--border);border-radius:10px;}
::-webkit-scrollbar-thumb:hover{background:rgba(213,185,170,0.25);}

/* == MOBILE ================================================= */
#mobile-list { display: none; }
@media (max-width: 768px) {
  body { overflow: auto; }
  /* Header: compact 2-row */
  .hdr {
    height: auto; min-height: 48px;
    padding: 8px 12px; flex-wrap: wrap; gap: 6px;
  }
  .hdr-div { display: none; }
  .brand-sub { display: none; }
  .hdr-center { order: 3; width: 100%; gap: 8px; flex-wrap: wrap; }
  .hdr-right { flex-direction: row; align-items: center; gap: 8px; margin-left: auto; }
  .data-range { display: none; }
  .week-lbl { min-width: 0; font-size: 10px; max-width: 150px; }
  .tgl { padding: 4px 12px; font-size: 11px; }
  .nav-btn { width: 32px; height: 32px; }
  /* Schedule: hide desktop grid, show mobile list */
  #sched-view { padding: 0 0 6px; overflow: auto; }
  .legend { padding: 4px 10px 6px; gap: 6px; }
  .legend-item { font-size: 9px; }
  .grid-wrap { display: none; }
  #mobile-list { display: block; padding: 0 10px 60px; }
  /* Mobile day groups */
  .mob-day-hdr {
    font-size: 10px; font-weight: 700; text-transform: uppercase;
    letter-spacing: 0.1em; color: var(--muted);
    padding: 12px 2px 5px; margin-bottom: 5px;
    border-bottom: 1px solid var(--border);
  }
  /* Was var(--accent): 2.14:1 on --bg in dark, the exact misuse the token
     comment warns about, on the mobile list where it is the only marker of
     which day is today. --accent-text is 7.27:1 and stays lighter than the
     --muted other-day headers, so today still reads as the emphasised one. */
  .mob-day-hdr.today-hdr { color: var(--accent-text); }
  .mob-cards { display: flex; flex-direction: column; gap: 5px; margin-bottom: 10px; }
  .leg-card { padding: 8px 10px; font-size: 12px; }
  .card-hd { font-size: 12px; }
  /* Crew view: stacked sidebar + detail */
  .crew-wrap { flex-direction: column; }
  .crew-sidebar {
    width: 100%; min-width: 0;
    border-right: none; border-bottom: 1px solid var(--border);
    max-height: 42vh;
  }
  .role-flt { padding: 4px 8px; font-size: 10px; }
  .crew-detail-panel { padding: 12px; }
  .cdh { gap: 10px; }
  .stats-row { flex-wrap: wrap; gap: 6px; }
  .stat-box { padding: 8px 12px; min-width: 60px; }
  .stat-val { font-size: 18px; }
  /* Modal: slides up from bottom */
  .modal-ov { padding: 0; align-items: flex-end; }
  .modal { max-width: 100%; max-height: 88vh; border-radius: 14px 14px 0 0; }
  .m-body { padding: 14px; }
  .info-grid { grid-template-columns: repeat(2, 1fr); }
  .rt-airports { font-size: 18px; }
  .route-blk { flex-direction: column; gap: 8px; }
  .rt-meta { margin-left: 0; text-align: left; }
}
html.p9-embedded #theme-toggle { display: none !important; }
/* Embedded in the portal: hide this app's own logo block (P9 mark + PLANET9 +
   the adjacent divider) so the top-left is uniform across every app. Standalone
   keeps its logo. */
html.p9-embedded .brand, html.p9-embedded .hdr-div { display: none !important; }
/* The document cell is an indicator, not a control: no pointer, no hover
   affordance, nothing to click. The reveal eye and the masked-dots rules that
   used to live here went with the data (2026-08-12). */
.pp{display:inline-flex;align-items:center;gap:6px;font-family:var(--font-mono);}
.pp-onfile{letter-spacing:0.03em;}
.pp-label{color:var(--muted);font-size:11px;letter-spacing:0.03em;}
.leg-card.deep-flash{animation:deepFlash 1.3s ease-in-out 2;}
@keyframes deepFlash{0%,100%{box-shadow:none}50%{box-shadow:0 0 0 2px #E9EDF2,0 0 14px rgba(233, 237, 242,.55)}}
