/* ==========================================================================
   Lab credentials.

   Two audiences, one vocabulary. A student sees one page, once, on a phone,
   with a webinar running in the other window: that page is a handout — large
   type, one field, nothing to decide. An instructor sees rosters and logs:
   denser, tabular, ruled rather than boxed.

   Two brands, one design. The wordmark is the real asset (theme/static/img),
   so the chrome around it stays quiet and the brand colour is an accent and a
   marker, never a wash — which is how both companies use their own. Swapping
   brands swaps four tokens and a logo; see the palette block below and
   app/branding.py.
   ========================================================================== */

@font-face{
  font-family:"IBM Plex Sans"; src:url("fonts/ibm-plex-sans-var.woff2") format("woff2");
  font-weight:100 700; font-style:normal; font-display:swap;
}
@font-face{
  font-family:"IBM Plex Mono"; src:url("fonts/ibm-plex-mono-400.woff2") format("woff2");
  font-weight:400; font-style:normal; font-display:swap;
}
@font-face{
  font-family:"IBM Plex Mono"; src:url("fonts/ibm-plex-mono-500.woff2") format("woff2");
  font-weight:500; font-style:normal; font-display:swap;
}

/* --- palette ----------------------------------------------------------------
   Everything below the brand block is shared: two companies' labs should feel
   like the same tool, not two products. A brand supplies four tokens and the
   logo beside them; nothing else moves.

     --brand       the accent: keylines, focus rings, the active tab marker.
                   Only ever a graphic — it does not have to pass text contrast.
     --brand-ink   what carries white text (the primary button) and what is
                   used for brand-coloured text. Must reach 4.5:1 on white.
     --brand-tint  a wash for selections and the drop zone's active state.
     --brand-glow  the focus halo, the same hue at low alpha.

   Defaults here are Exclusive Networks, who run the site, so a page with no
   data-brand attribute still comes out in the house colours; [data-brand]
   blocks below override for the others. */
:root{
  --paper:#f4f4f2;
  --sheet:#ffffff;
  --ink:#141414;          /* the wordmark's own black */
  --quiet:#5b5f63;
  --rule:#dcdcda;
  --rule-soft:#ececeb;

  /* Exclusive Networks magenta, from their own wordmark. Unusually for a brand
     colour it is dark enough to carry white text unaided (7.73:1), so --brand
     and --brand-ink sit close together. The slate #839fad in the same wordmark
     is decorative only: at 2.79:1 it must never carry text or a real border. */
  --brand:#9C027D;
  --brand-ink:#86016C;
  --brand-tint:#fbeef8;
  --brand-glow:rgba(156,2,125,.18);

  --ok:#1c6b45;
  --warn:#8a5a00;
  --err:#a3271d;
  --err-tint:#fdeeec;
  --ok-tint:#ecf5f0;

  --sans:"IBM Plex Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
  --mono:"IBM Plex Mono",ui-monospace,"SF Mono",Menlo,Consolas,monospace;

  /* 1.25 scale from 16px */
  --t-xs:12.5px; --t-sm:14px; --t-base:16px; --t-lg:20px; --t-xl:25px;
  --t-2xl:31px; --t-3xl:39px;

  --gutter:26px;
  --measure:64ch;
  color-scheme:light;
}

/* Palo Alto Networks. Their orange reaches only 3.23:1 on white, so unlike the
   house magenta it can never carry text -- --brand-ink is a markedly deeper
   shade of the same hue, and that gap is the point. */
[data-brand="panw"]{
  --brand:#FA582D;
  --brand-ink:#C33A13;
  --brand-tint:#fdefea;
  --brand-glow:rgba(250,88,45,.18);
}

*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0; background:var(--paper); color:var(--ink);
  touch-action:manipulation; -webkit-tap-highlight-color:var(--brand-glow);
  font:400 var(--t-base)/1.55 var(--sans);
  -webkit-font-smoothing:antialiased;
  display:flex; flex-direction:column; min-height:100vh;
}
h1,h2,h3{margin:0; font-weight:600; line-height:1.2; text-wrap:balance}
h1{font-size:var(--t-2xl); letter-spacing:-.02em}
h2{font-size:var(--t-lg)}
h3{font-size:var(--t-base)}
p{margin:0}
a{color:var(--ink); text-decoration-color:var(--rule); text-underline-offset:3px}
a:hover{text-decoration-color:var(--brand)}
:focus-visible{outline:2px solid var(--brand-ink); outline-offset:2px; border-radius:2px}
::selection{background:var(--brand-tint)}
hr{border:0; border-top:1px solid var(--rule); margin:22px 0}

.skip{position:absolute; left:-9999px; top:0; padding:10px 14px; background:var(--sheet); z-index:10}
.skip:focus{left:8px; top:8px}

.wrap{width:100%; max-width:1040px; margin:0 auto; padding:0 var(--gutter)}
.wrap-wide{max-width:1360px}
.wrap-narrow{max-width:620px}
.measure{max-width:var(--measure)}
.quiet{color:var(--quiet)}
.mono{font-family:var(--mono)}
.num{font-variant-numeric:tabular-nums}
.nowrap{white-space:nowrap}

/* --- top bar --------------------------------------------------------------
   The wordmark, a hairline, and whatever the signed-in instructor needs. The
   orange keyline under the bar is the only brand flourish on the page. */
.bar{background:var(--sheet); border-bottom:1px solid var(--rule); box-shadow:0 1px 0 rgba(0,0,0,.02)}
.bar::after{content:""; display:block; height:3px; background:var(--brand)}
.bar-inner{display:flex; align-items:center; gap:22px; min-height:62px; flex-wrap:wrap}
.bar-brand{display:flex; align-items:center; gap:14px; text-decoration:none; flex:none; padding:10px 0}
.bar-logo{height:21px; width:auto; display:block}
.bar-name{
  font-size:var(--t-sm); font-weight:500; color:var(--quiet);
  padding-left:14px; border-left:1px solid var(--rule); letter-spacing:-.01em;
}
.bar-tabs{display:flex; align-items:center; gap:4px; flex:1; min-width:0; overflow-x:auto}
.bar-tabs a{
  display:flex; align-items:center; padding:8px 12px; font-size:var(--t-sm);
  color:var(--quiet); text-decoration:none; white-space:nowrap;
  border-bottom:2px solid transparent;
}
.bar-tabs a:hover{color:var(--ink)}
.bar-tabs a[aria-current="page"]{color:var(--ink); font-weight:500; border-bottom-color:var(--brand)}
.bar-right{display:flex; align-items:center; gap:14px; flex:none; font-size:var(--t-sm)}
.bar-right form{display:flex}
.bar-user{color:var(--quiet)}

main{flex:1; padding:34px 0 60px}
main.student{padding:min(9vh,76px) 0 60px}

/* --- page headings --------------------------------------------------------- */
.page{margin-bottom:24px}
.page-meta{
  display:flex; align-items:baseline; gap:8px 16px; flex-wrap:wrap;
  margin-top:9px; font-size:var(--t-sm); color:var(--quiet);
}
.page-note{margin-top:11px; max-width:var(--measure); color:var(--quiet); font-size:var(--t-sm)}
.eyebrow{
  font-size:var(--t-xs); font-weight:600; color:var(--brand-ink);
  letter-spacing:.06em; text-transform:uppercase; margin-bottom:8px;
}

/* --- surfaces -------------------------------------------------------------- */
.sheet{background:var(--sheet); border:1px solid var(--rule)}
.sheet + .sheet{margin-top:20px}
.sheet-head{padding:18px var(--gutter); border-bottom:1px solid var(--rule); display:flex;
  align-items:baseline; justify-content:space-between; gap:14px; flex-wrap:wrap}
.sheet-body{padding:20px var(--gutter)}
.sheet-body + .sheet-body{border-top:1px solid var(--rule-soft)}
.sheet-foot{
  padding:14px var(--gutter); border-top:1px solid var(--rule); background:#fafaf9;
  font-size:var(--t-sm); color:var(--quiet);
}
.sheet-tight{padding:0}

/* --- forms ----------------------------------------------------------------- */
label{display:block; font-size:var(--t-sm); font-weight:500; margin-bottom:6px}
.hint{font-size:var(--t-sm); color:var(--quiet); margin-top:6px}
input[type=text],input[type=email],input[type=password],input[type=url],
input[type=datetime-local],input[type=file],textarea,select{
  width:100%; font:400 var(--t-base)/1.4 var(--sans); color:var(--ink);
  background:var(--sheet); border:1px solid var(--rule); border-radius:2px;
  padding:11px 12px;
}
input:hover,textarea:hover,select:hover{border-color:#c4c4c1}
input:focus,textarea:focus,select:focus{border-color:var(--brand-ink); outline:none;
  box-shadow:0 0 0 3px var(--brand-glow)}
textarea{min-height:86px; resize:vertical}
input[type=file]{padding:9px 10px; font-size:var(--t-sm)}
.field{margin-bottom:16px}
.field-row{display:flex; gap:14px; flex-wrap:wrap}
.field-row > *{flex:1 1 200px; min-width:0}
fieldset{border:1px solid var(--rule); padding:14px var(--gutter) 18px; margin:0 0 16px}
legend{font-size:var(--t-sm); font-weight:600; padding:0 8px}
.check{display:flex; align-items:flex-start; gap:9px; font-weight:400; margin-bottom:9px}
.check input{width:auto; flex:none; margin-top:3px; accent-color:var(--brand-ink)}

/* the student's one field, deliberately oversized */
.big-field input{font-size:var(--t-lg); padding:15px 14px}

/* --- buttons --------------------------------------------------------------- */
.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:8px;
  font:500 var(--t-sm)/1 var(--sans); padding:11px 16px; border-radius:2px;
  border:1px solid var(--rule); background:var(--sheet); color:var(--ink);
  cursor:pointer; text-decoration:none; white-space:nowrap;
}
.btn:hover{border-color:#bdbdba; background:#fafaf9}
.btn-primary{background:var(--brand-ink); border-color:var(--brand-ink); color:#fff}
/* One step darker than --brand-ink, derived rather than fixed, so a new
   brand does not need a fifth token just for the hover. */
.btn-primary:hover{filter:brightness(.88)}
.btn-quiet{background:transparent; border-color:transparent; color:var(--quiet)}
.btn-quiet:hover{color:var(--ink); background:#f0f0ee; border-color:transparent}
.btn-danger{color:var(--err); border-color:#e6cdc9}
.btn-danger:hover{background:var(--err-tint); border-color:#d8b4ae}
.btn-lg{font-size:var(--t-base); padding:15px 22px}
.btn-sm{font-size:var(--t-xs); padding:7px 10px}
.btn-block{width:100%}
.btn:disabled{opacity:.5; cursor:not-allowed}
.btn-row{display:flex; gap:10px; flex-wrap:wrap; align-items:center}

/* --- notices and flashes ---------------------------------------------------- */
.flashes{list-style:none; margin:0 0 22px; padding:0; display:grid; gap:9px}
.flash,.notice{
  padding:12px 15px; font-size:var(--t-sm); background:var(--sheet);
  border:1px solid var(--rule); border-left:3px solid var(--quiet);
}
.flash.ok,.notice.ok{border-left-color:var(--ok); background:var(--ok-tint)}
.flash.error,.notice.error{border-left-color:var(--err); background:var(--err-tint)}
.flash.warn,.notice.warn{border-left-color:var(--warn); background:#fdf6e8}
.notice-lg{padding:16px 18px; font-size:var(--t-base)}

/* --- the credential handout -------------------------------------------------
   The one thing a student came for. Mono at a size that survives being read off
   a laptop screen from a phone, each value on its own ruled line with its own
   copy button — copying is the action, so it sits with the value, not below. */
.cred{border:1px solid var(--rule); border-top:3px solid var(--brand); background:var(--sheet)}
.cred-head{padding:16px var(--gutter); border-bottom:1px solid var(--rule-soft)}
.cred-for{font-size:var(--t-sm); color:var(--quiet)}
.cred-list{display:grid}
.cred-row{
  display:grid; grid-template-columns:130px minmax(0,1fr) auto; align-items:center;
  gap:14px; padding:14px var(--gutter); border-bottom:1px solid var(--rule-soft);
}
.cred-row:last-child{border-bottom:0}
.cred-label{font-size:var(--t-sm); color:var(--quiet); font-weight:500}
.cred-value{
  font:500 var(--t-lg)/1.35 var(--mono); word-break:break-all;
  user-select:all; /* one tap selects the whole value on a phone */
}
.cred-extra .cred-value{font-size:var(--t-base); font-weight:400}
.copy{flex:none}
.copy[data-done="1"]{color:var(--ok); border-color:#bcd8c9}
@media (max-width:540px){
  .cred-row{grid-template-columns:minmax(0,1fr) auto; gap:4px 12px}
  .cred-label{grid-column:1 / -1}
}

/* --- tables ---------------------------------------------------------------- */
.tbl-wrap{overflow-x:auto; -webkit-overflow-scrolling:touch}
.tbl{width:100%; border-collapse:collapse; font-size:var(--t-sm)}
.tbl th,.tbl td{padding:9px 12px; text-align:left; border-bottom:1px solid var(--rule-soft);
  vertical-align:top}
.tbl thead th{
  position:sticky; top:0; background:var(--sheet); z-index:1; font-weight:600;
  border-bottom:1px solid var(--rule); white-space:nowrap;
}
.tbl tbody tr:hover{background:#fafaf9}
.tbl td.num,.tbl th.num{text-align:right; font-variant-numeric:tabular-nums}
.tbl .mono{font-size:var(--t-sm)}
.tbl-empty{padding:28px var(--gutter); color:var(--quiet); font-size:var(--t-sm)}

/* --- small semantic bits ---------------------------------------------------- */
.state{
  display:inline-flex; align-items:center; gap:6px; font-size:var(--t-xs);
  font-weight:500; padding:3px 8px; border:1px solid var(--rule); border-radius:2px;
  white-space:nowrap;
}
.state::before{content:""; width:7px; height:7px; border-radius:50%; background:currentColor; flex:none}
/* colour never carries the meaning on its own — the word is always there too */
.state-open{color:var(--ok); border-color:#bcd8c9; background:var(--ok-tint)}
.state-shut{color:var(--quiet); background:#f2f2f0}
.state-ok{color:var(--ok)}
.state-unknown{color:var(--warn)}
.state-throttled{color:var(--err)}
.dot-sep{color:var(--rule)}

.meta{display:grid; grid-template-columns:auto 1fr; gap:7px 18px; margin:0; font-size:var(--t-sm)}
.meta dt{color:var(--quiet)}
.meta dd{margin:0}

.list{border-top:1px solid var(--rule-soft)}
.list-row{
  display:flex; align-items:center; justify-content:space-between; gap:16px;
  padding:15px var(--gutter); border-bottom:1px solid var(--rule-soft);
  text-decoration:none; flex-wrap:wrap;
}
.list-row:hover{background:#fafaf9}
.list-main{min-width:0}
.list-title{font-weight:600}
.list-sub{font-size:var(--t-sm); color:var(--quiet); margin-top:3px}

.stat-row{display:flex; gap:30px; flex-wrap:wrap}
.stat-n{font:500 var(--t-xl)/1.1 var(--mono); font-variant-numeric:tabular-nums}
.stat-l{font-size:var(--t-sm); color:var(--quiet); margin-top:2px}

/* --- footer ----------------------------------------------------------------- */
.foot{border-top:1px solid var(--rule); background:var(--sheet); font-size:var(--t-sm); color:var(--quiet)}
.foot-inner{display:flex; gap:16px; justify-content:space-between; align-items:center;
  padding-top:16px; padding-bottom:16px; flex-wrap:wrap}
.foot a{color:var(--quiet)}

@media (max-width:640px){
  :root{--gutter:18px}
  h1{font-size:var(--t-xl)}
  .bar-inner{min-height:56px; gap:12px}
  .bar-name{padding-left:12px}
}
@media (prefers-reduced-motion:no-preference){
  .btn,.bar-tabs a,input{transition:background-color .14s ease, border-color .14s ease, color .14s ease}
}
@media print{
  /* an instructor printing a roster, or a student printing the handout */
  .bar::after,.btn,.bar-tabs,.bar-right,.foot,.skip{display:none !important}
  body{background:#fff}
  .sheet,.cred{border-color:#999}
}

/* --- password masking --------------------------------------------------------
   An instructor reads these rosters with a webinar screen-share running, so the
   passwords start hidden and the checkbox reveals them. It is a shoulder-surfing
   guard, not a security control: the values are in the HTML either way. */
.pw-real{display:none}
.pw-mask{color:var(--quiet); letter-spacing:.1em}
[data-masked].reveal .pw-real{display:inline}
[data-masked].reveal .pw-mask{display:none}

/* --- the shared environment link ---------------------------------------------
   Sits under the credentials on the student's handout. The URL is printed
   beside the button as well: some students are on a locked-down machine where
   the new tab never opens, and they need something to type. */
.go{
  display:flex; align-items:center; gap:14px 18px; flex-wrap:wrap;
  margin-top:20px; padding:18px var(--gutter);
  background:var(--sheet); border:1px solid var(--rule); border-left:3px solid var(--brand);
}
.go-url{font-size:var(--t-sm); word-break:break-all; min-width:0}

/* --- the roster drop zone ----------------------------------------------------
   Decoration over a real <input type=file>: with scripting off the input is
   visible and works on its own, so nothing here is load-bearing. */
.drop{
  border:1px dashed var(--rule); background:#fbfbfa; border-radius:2px;
  padding:26px var(--gutter); text-align:center;
}
.drop.over{border-color:var(--brand); border-style:solid; background:var(--brand-tint)}
.drop-main{font-size:var(--t-lg); font-weight:500}
.drop-sub{font-size:var(--t-sm); color:var(--quiet); margin:6px 0 10px}
.drop-hint{font-size:var(--t-sm); margin-top:12px}
.drop-name{
  font-family:var(--mono); font-size:var(--t-sm); margin-top:12px;
  padding:7px 12px; background:var(--sheet); border:1px solid var(--rule);
  display:inline-block; max-width:100%; word-break:break-all;
}
/* the input stays in the flow (and focusable) until script hides it */
.drop input[type=file]{margin-top:12px; width:auto; max-width:100%}
.js .drop input[type=file]{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
}
.js .drop label.btn{cursor:pointer}
