/* ──────────────────────────────────────────────────────────────────────
   Academia Tech · UA-PILOT cloud portal — modernised theme
   Direct port of theacademia.tech tokens (neutral palette, pure-black
   accents, Inter / Unbounded) with no shadows. Cards = border only.
   ────────────────────────────────────────────────────────────────────── */

:root {
  /* surfaces (theacademia.tech-style: pure white) */
  --bg:        #FFFFFF;
  --surface:   #FFFFFF;
  --surface-2: #FAFAFA;       /* hover/highlight bg, very subtle */
  --surface-3: #F5F5F5;

  /* ink (theacademia.tech-style: pure black, neutral grays) */
  --ink:   #000000;
  --ink-2: #171717;            /* neutral-900 */
  --ink-3: #525252;            /* neutral-600 */
  --ink-4: #A3A3A3;            /* neutral-400 — secondary text */
  --ink-5: #D4D4D4;            /* neutral-300 — disabled */

  /* lines (theacademia.tech neutral-200 / 300) */
  --line:   #E5E5E5;           /* neutral-200 */
  --line-2: #D4D4D4;           /* neutral-300 */
  --line-3: #A3A3A3;           /* neutral-400 — hover border */

  /* accent — keep one olive thread for compatibility w/ StabX surface */
  --stabx:        #BCCC68;
  --stabx-deep:   #7E8B41;
  --stabx-soft:   #F4F6E5;     /* still used for the product chip */

  /* ACADEMIA Link's own blue. Confined to the app: the wordmark, its download
     button and its Beta tag — so Link reads as a separate product without
     repainting the portal. */
  --link:      #1A56DB;
  --link-soft: #EAF0FD;

  /* status (subdued, neutral-looking) */
  --ok:     #16A34A;
  --warn:   #CA8A04;
  --danger: #DC2626;
  --info:   #2563EB;

  /* shape (theacademia.tech mostly uses 6-8px) */
  --r:    8px;
  --r-sm: 6px;
  --r-lg: 12px;
}

* { box-sizing: border-box; }
html, body {
  margin: 0; padding: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: 'Inter', system-ui, -apple-system, sans-serif;
  font-size: 14px;
  line-height: 1.5;
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
/* Sticky footer. A short page — the portal, an empty uploads list — used to
   leave the footer floating mid-screen with white space under it; now the main
   column takes the slack and the footer sits on the bottom edge. Long pages are
   unaffected: `flex: 1 0 auto` grows but never shrinks. */
body {
  min-height: 100vh;
  display: flex; flex-direction: column;
}

a { color: var(--ink); text-decoration: none; }
a:hover { color: var(--ink-2); }
button, input, select, textarea { font-family: inherit; color: inherit; }

/* ── sticky header ─────────────────────────────────────────────── */
.app-header {
  position: sticky; top: 0; z-index: 40;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}
.app-header-inner {
  max-width: 1400px;
  margin: 0 auto;
  padding: 14px 24px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 24px;
}
@media (min-width: 1024px) {
  .app-header-inner { padding: 14px 48px; }
}
@media (min-width: 1280px) {
  .app-header-inner { padding: 14px 96px; }
}
/* Small phones: the brand, the language pair and the overflow button do not
   fit at 360px with a 24px gap and 24px of padding — the row ran ~37px past
   the viewport and took the whole page into a sideways scroll. The wordmark
   identifies the site on its own, so the product chip is what gives way. */
@media (max-width: 420px) {
  .app-header-inner { padding: 12px 14px; gap: 10px; }
  .brand { gap: 8px; min-width: 0; }
  .brand-product { display: none; }
}

/* brand: image logo (Academia ▲) + ACADEMIA wordmark + UA-PILOT chip */
.brand {
  display: inline-flex; align-items: center; gap: 10px;
  text-decoration: none; color: var(--ink);
  line-height: 1;
}
.brand:hover { opacity: 0.88; color: var(--ink); }
.brand-logo {
  width: 28px; height: 28px;
  object-fit: contain;
  filter: invert(1);             /* white-on-white source → black */
  flex-shrink: 0;
}
.brand-name {
  font-family: 'Unbounded', 'Inter', sans-serif;
  font-size: 17px; font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink);
}
.brand-product {
  font-family: 'Unbounded', 'Inter', sans-serif;
  font-size: 11px; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--stabx-deep);
  padding: 4px 8px;
  border-radius: 5px;
  margin-left: 6px;
  background: var(--stabx-soft);
  white-space: nowrap;
}

/* nav links */
.nav-links {
  display: inline-flex; gap: 28px;
  justify-self: center;
  list-style: none; margin: 0; padding: 0;
  font-size: 13.5px;
}
.nav-links a {
  color: var(--ink-3); font-weight: 500;
  letter-spacing: 0;
  transition: color 120ms;
}
.nav-links a:hover { color: var(--ink); }
.nav-links a.active { color: var(--ink); font-weight: 600; }

.header-actions {
  display: inline-flex; align-items: center; gap: 8px;
}

/* EN / UA — two separate flat pills (matches theacademia.tech exactly) */
.lang-switch {
  display: inline-flex; gap: 4px;
}
.lang-btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 4px 12px;
  font-size: 12px; font-weight: 600;
  letter-spacing: 0;
  border-radius: 6px;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--ink-4);
  cursor: pointer;
  transition: all 100ms ease-in-out;
  text-decoration: none;
}
.lang-btn:hover { border-color: var(--ink-4); color: var(--ink-3); }
.lang-btn.active {
  background: var(--ink);
  border-color: var(--ink);
  color: #FFFFFF;
}

/* icon button (3-dot overflow) */
.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px;
  background: transparent; border: 1px solid transparent;
  border-radius: var(--r-sm); color: var(--ink-3);
  cursor: pointer; transition: 100ms;
}
.icon-btn:hover { color: var(--ink); background: var(--surface-2); }
.icon-btn svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

/* 3-dot overflow menu */
.more-wrap { position: relative; }
.more-menu {
  display: none; position: absolute; right: 0; top: calc(100% + 8px);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  /* Sized to its longest row rather than to a fixed 220px, which left a wide
     empty gutter beside three short items. */
  min-width: 160px;
  width: max-content; max-width: 260px;
  padding: 6px;
  z-index: 60;
}
.more-wrap.open .more-menu { display: block; }
.more-menu a, .more-menu button {
  display: flex; align-items: center; gap: 10px;
  width: 100%; text-align: left;
  padding: 8px 10px;
  background: transparent; border: none;
  border-radius: var(--r-sm);
  color: var(--ink-2);
  font-size: 13px; font-weight: 500;
  cursor: pointer; transition: 60ms;
  text-decoration: none;
}
.more-menu a:hover, .more-menu button:hover { background: var(--surface-2); color: var(--ink); }
.more-menu .sep { height: 1px; background: var(--line); margin: 4px 4px; }
.more-menu .danger { color: var(--danger); }
.more-menu .danger:hover { background: #FEF2F2; color: var(--danger); }

/* ── page wrap (Academia-style generous max width) ─────────────── */
.page {
  max-width: 1210px;
  margin: 0 auto;
  padding: 48px 24px 80px;
  width: 100%;
  flex: 1 0 auto;            /* takes the slack, so the footer sits on the bottom */
  display: flex; flex-direction: column;
  min-width: 0;
}
@media (min-width: 768px) {
  .page { padding: 64px 24px 96px; }
}

/* page heading (matches Academia hero treatment — huge, simple) */
.page-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 24px; margin-bottom: 40px;
  flex-wrap: wrap;
}
.page-head-text { flex: 1; min-width: 240px; }
.page-title {
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 36px; font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.1;
  color: var(--ink); margin: 0;
}
@media (min-width: 768px) {
  .page-title { font-size: 44px; }
}
.page-subtitle {
  margin-top: 12px;
  color: var(--ink-3);
  font-size: 14px;
  font-weight: 400;
  line-height: 1.55;
}
.page-meta {
  display: inline-flex; align-items: center; gap: 8px;
  margin-bottom: 14px;
  color: var(--ink-4);
  font-size: 12px; font-weight: 500;
  letter-spacing: 0;
}
.page-meta .label { color: var(--ink-3); font-weight: 600; }
.page-meta .sep { color: var(--line-2); }

/* settings-page eyebrow shows license count with green number */
.page-meta.lic-meta {
  font-size: 12.5px;
  padding: 4px 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
}
.page-meta.lic-meta .label { color: var(--ink-3); font-weight: 500; text-transform: none; letter-spacing: 0; }
.page-meta.lic-meta .num { color: var(--ok); font-weight: 700; font-variant-numeric: tabular-nums; font-size: 13px; }
.page-meta.lic-meta.zero .num { color: var(--danger); }

/* license pill: minimal, badge-like, green number */
.lic-chip {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 14px;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 12px; font-weight: 500;
  color: var(--ink-3);
  white-space: nowrap;
  background: var(--bg);
}
.lic-chip .num {
  font-weight: 700;
  color: var(--ok);
  font-variant-numeric: tabular-nums;
  font-size: 13px;
}
.lic-chip.zero .num { color: var(--danger); }

/* ── cards (border only — no shadows anywhere) ────────────────── */
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  margin-bottom: 20px;
}
/* keep the header strip's bottom border crisp without clipping
   absolutely-positioned children (e.g. dropdown menus) */
.card-head { border-top-left-radius: var(--r-lg); border-top-right-radius: var(--r-lg); }
.card-body { border-bottom-left-radius: var(--r-lg); border-bottom-right-radius: var(--r-lg); }
/* when card-body is the only child (no card-head), round all four corners.
   overflow:hidden clips flush content (e.g. uploads table) to the card edge.
   NOTE: only safe on cards with no dropdown — the build/camera selects open
   absolutely-positioned menus that need to escape, and those cards all have
   a .card-head, which excludes them from this rule. */
.card > .card-body:first-child:last-child {
  border-radius: var(--r-lg);
  overflow: hidden;
}
.card-head {
  display: flex; align-items: center; gap: 14px;
  padding: 16px 22px;
  border-bottom: 1px solid var(--line);
}

.card-head .ic {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--bg);
  color: var(--ink-2);
  flex-shrink: 0;
}
.card-head .ic svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.card-head h3 {
  font-family: 'Inter', sans-serif;
  font-size: 14px; font-weight: 600;
  letter-spacing: 0;
  color: var(--ink); margin: 0;
  white-space: nowrap;
}
.card-head .card-sub {
  margin-left: auto;
  color: var(--ink-4); font-size: 12px;
}

/* A card header is a heading beside a piece of metadata, and on a phone there
   is not room for both on one line. The heading is nowrap above, which is
   right on a desktop and is what pushed the page sideways on a 320px screen —
   a long product name has no break opportunity at all once wrapping is off.
   Below, so it wins: let the pair stack, let the heading wrap at its spaces,
   and let a word with no spaces in it (a filename, a part number) break. */
@media (max-width: 560px) {
  .card-head { flex-wrap: wrap; gap: 4px 14px; padding: 14px 16px; }
  .card-head > * { min-width: 0; }
  .card-head h3, .card-head .card-sub {
    white-space: normal;
    overflow-wrap: anywhere;
  }
  .card-head .card-sub { margin-left: 0; }
}
.card-head .card-toggle {
  margin-left: auto;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 8px; border-radius: 6px;
  background: transparent; border: none;
  color: var(--ink-4); font-size: 12px; font-weight: 500;
  cursor: pointer; transition: 100ms;
}
.card-head .card-toggle:hover { color: var(--ink); background: var(--surface-2); }
.card-head .card-toggle svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; transition: transform 200ms; }
.card.collapsed .card-toggle svg { transform: rotate(-90deg); }
.card.collapsed .card-body { display: none; }
.card.collapsed .card-head { border-bottom: none; }
.card-body { padding: 24px; }

/* ── fields ────────────────────────────────────────────────────── */
.field { display: flex; flex-direction: column; gap: 8px; margin-bottom: 18px; }
.field:last-child { margin-bottom: 0; }
.field label,
.field > .label {
  font-size: 13px; font-weight: 500;
  color: var(--ink-2);
  letter-spacing: 0;
}
.field .hint {
  font-size: 12px; color: var(--ink-4); font-weight: 400;
  margin-top: 4px;
}

/* text inputs */
.input-wrap, .input {
  display: flex; align-items: center;
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r);
  transition: border-color 120ms, background-color 120ms;
}
.input-wrap:hover, .input:hover { border-color: var(--line-3); }
.input-wrap:focus-within, .input:focus { border-color: var(--ink); outline: none; }

.input-wrap input,
.input-wrap select {
  flex: 1; width: 100%;
  height: 44px;
  padding: 0 14px;
  background: transparent;
  border: none; outline: none;
  color: var(--ink);
  font-size: 14px; font-weight: 500;
  font-family: inherit;
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
}
input.input, select.input {
  width: 100%;
  height: 44px;
  padding: 0 14px;
  color: var(--ink);
  font-size: 14px; font-weight: 500;
  font-family: inherit;
  outline: none;
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
}
/* Custom selects strip the native arrow (appearance:none) — draw our own
   chevron so a <select> never looks like a plain text box. */
select.input,
.input-wrap select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23525252' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  padding-right: 36px;
  cursor: pointer;
}
.input-wrap input::placeholder { color: var(--ink-4); font-weight: 400; }

/* Kill Chrome / Edge / Safari autofill background — it paints a square
   bg on the inner <input>, which bleeds past the rounded .input-wrap
   corners. The inset box-shadow re-paints the input with our surface
   color (effectively transparent over the wrap), keeping rounded
   corners intact. Long transition prevents a yellow flash on load. */
.input-wrap input:-webkit-autofill,
.input-wrap input:-webkit-autofill:hover,
.input-wrap input:-webkit-autofill:focus,
.input-wrap input:-webkit-autofill:active {
  -webkit-box-shadow: 0 0 0 1000px var(--surface) inset !important;
  -webkit-text-fill-color: var(--ink) !important;
  caret-color: var(--ink);
  transition: background-color 5000s ease-in-out 0s;
  border-radius: var(--r);
}

/* ── custom listbox dropdown (replaces native <select> UI) ─────────
   The native <select> is hidden in-place so form submission still works,
   and a .select-shell renders the visible trigger + a fully-styled menu. */
.select-shell {
  position: relative;
  width: 100%;
}
.select-shell select {
  /* visually hidden but keeps the form value & accessibility tree */
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  pointer-events: none;
  -webkit-appearance: none; appearance: none;
}
.select-trigger {
  display: flex; align-items: center;
  width: 100%;
  height: 44px;
  padding: 0 0 0 14px;
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r);
  color: var(--ink);
  font-family: inherit;
  font-size: 14px;
  font-weight: 500;
  text-align: left;
  cursor: pointer;
  transition: border-color 120ms, background-color 120ms;
}
.select-trigger:hover { border-color: var(--ink); background-color: var(--surface-2); }
.select-trigger:focus-visible { outline: none; border-color: var(--ink); }
.select-trigger .select-value {
  flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.select-trigger .select-chev-wrap {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 100%;
  border-left: 1px solid var(--line-2);
  margin-left: 12px;
  color: var(--ink);
  flex-shrink: 0;
}
.select-trigger .select-chev-wrap svg {
  width: 16px; height: 16px;
  stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
  transition: transform 160ms;
}
.select-shell.open .select-trigger .select-chev-wrap svg { transform: rotate(180deg); }
.select-shell.open .select-trigger { border-color: var(--ink); background-color: var(--surface); }

.select-menu {
  display: none;
  position: absolute; left: 0; right: 0; top: calc(100% + 6px);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r);
  padding: 4px;
  max-height: 280px;
  overflow-y: auto;
  z-index: 50;
  scrollbar-width: thin;
  scrollbar-color: var(--line-2) transparent;
}
.select-menu::-webkit-scrollbar { width: 8px; }
.select-menu::-webkit-scrollbar-thumb { background: var(--line-2); border-radius: 4px; }
.select-menu::-webkit-scrollbar-track { background: transparent; }
.select-shell.open .select-menu { display: block; }
.select-menu .opt {
  display: block;
  width: 100%;
  text-align: left;
  padding: 9px 12px;
  background: transparent;
  border: none;
  border-radius: var(--r-sm);
  color: var(--ink-2);
  font-family: inherit;
  font-size: 13.5px;
  font-weight: 500;
  cursor: pointer;
  transition: background-color 60ms, color 60ms;
}
.select-menu .opt:hover { background: var(--surface-2); color: var(--ink); }
.select-menu .opt.selected {
  background: var(--surface-3);
  color: var(--ink);
  font-weight: 600;
}
.select-menu .opt.selected::after {
  content: '';
  display: inline-block;
  margin-left: 8px;
  width: 6px; height: 12px;
  border: solid var(--ink);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg) translateY(-1px);
}

/* numeric / text inputs used by render_json */
.card-body .form-control,
.personal .form-control {
  display: block; width: 100%;
  height: 40px;
  padding: 0 14px;
  background: var(--surface);
  color: var(--ink);
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 13px; font-weight: 500;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  transition: border-color 120ms;
  outline: none;
  appearance: textfield;
}
.card-body .form-control:hover,
.personal .form-control:hover { border-color: var(--line-3); }
.card-body .form-control:focus,
.personal .form-control:focus { border-color: var(--ink); }

/* checkbox-as-switch */
.switch {
  position: relative; display: inline-flex; align-items: center;
  width: 40px; height: 22px;
  flex-shrink: 0;
  cursor: pointer;
}
.switch input { position: absolute; opacity: 0; pointer-events: none; }
.switch .track {
  width: 40px; height: 22px;
  background: var(--line-2); border-radius: 999px;
  transition: 160ms;
  position: relative;
}
.switch .track::after {
  content: ''; position: absolute; top: 2px; left: 2px;
  width: 18px; height: 18px;
  background: #FFF; border-radius: 50%;
  transition: 160ms;
}
.switch input:checked + .track { background: var(--ink); }
.switch input:checked + .track::after { transform: translateX(18px); }

.wifi-row {
  display: flex; align-items: center; gap: 14px;
  padding: 4px 0;
}
.wifi-row .wifi-label {
  font-size: 14px; font-weight: 500; color: var(--ink-2);
  cursor: pointer;
}
.wifi-row .wifi-sub {
  font-size: 12px; color: var(--ink-4); margin-left: auto;
  font-weight: 500;
}

.wifi-fields {
  margin-top: 18px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
  display: grid; grid-template-columns: 1.5fr 1fr; gap: 12px;
}
@media (max-width: 600px) {
  .wifi-fields { grid-template-columns: 1fr; }
}

/* ── personal-settings (render_json output) ────────────────────── */
.personal h4 {
  font-family: 'Inter', sans-serif;
  font-size: 11px; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--ink-4); margin: 24px 0 12px;
  padding-bottom: 8px; border-bottom: 1px solid var(--line);
}
.personal h4:first-child { margin-top: 0; }
.personal h5 {
  font-size: 13px; font-weight: 500;
  color: var(--ink-2); margin: 0;
}
.personal ul { list-style: none; padding: 0; margin: 0; }
.personal > ul > li { margin-bottom: 12px; }
.personal > ul > li:last-child { margin-bottom: 0; }

.personal .horizontal-list {
  display: grid;
  grid-template-columns: minmax(200px, 1fr) 2fr;
  align-items: center;
  gap: 16px;
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
}
.personal .horizontal-list:last-child { border-bottom: none; }
.personal .horizontal-list li { margin: 0 !important; }
.personal .horizontal-list li.item_name {
  width: auto !important;
  font-size: 13px; color: var(--ink-3); font-weight: 500;
}
.personal .horizontal-list li.item_name h5 { font-size: 13px; color: var(--ink-3); font-weight: 500; }

/* tables (speed/distance profiles) */
.personal .table {
  width: 100%;
  border-collapse: separate; border-spacing: 0;
  margin: 12px 0 18px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  overflow: hidden;
}
.personal .table th {
  background: var(--surface-2);
  padding: 10px 14px;
  font-family: 'Inter', sans-serif;
  font-size: 11px; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--ink-3); text-align: center;
  border-bottom: 1px solid var(--line);
}
.personal .table td {
  padding: 8px 12px;
  background: var(--surface);
  border-top: 1px solid var(--line);
  text-align: center;
  vertical-align: middle;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 13px; color: var(--ink-2);
}
.personal .table tr:first-child td { border-top: none; }
.personal .table input.form-control {
  height: 32px;
  text-align: center;
  background: transparent;
  border: 1px solid transparent;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
}
.personal .table input.form-control:hover { border-color: var(--line); }
.personal .table input.form-control:focus { background: var(--surface); border-color: var(--ink); }

/* ── buttons (Academia: pure black primary, neutral border secondary) ── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: 44px; padding: 0 24px;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--r);
  font-family: inherit; font-size: 14px; font-weight: 500;
  letter-spacing: 0;
  cursor: pointer;
  transition: 120ms;
  white-space: nowrap;
  text-decoration: none;
}
.btn:hover { border-color: var(--ink); color: var(--ink); }
.btn:disabled, .btn[disabled] {
  background: var(--surface-2); color: var(--ink-4);
  border-color: var(--line); cursor: not-allowed;
}
.btn-primary {
  background: var(--ink); color: #FFFFFF; border-color: var(--ink);
  font-weight: 500;
}
.btn-primary:hover { background: #262626; border-color: #262626; color: #FFFFFF; }
.btn-primary:disabled {
  background: var(--ink-5); border-color: var(--ink-5); color: #FFFFFF;
}
.btn-ghost {
  background: transparent; border-color: transparent; color: var(--ink-3);
}
.btn-ghost:hover { color: var(--ink); background: var(--surface-2); border-color: transparent; }
.btn-sm {
  height: 32px; padding: 0 14px;
  font-size: 12.5px;
}
.btn svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

/* outline danger / outline primary (used by upload list) */
.btn-outline-danger { color: var(--danger); border-color: var(--line); background: transparent; }
.btn-outline-danger:hover { color: var(--danger); border-color: var(--danger); }
.btn-outline-primary { color: var(--ink); border-color: var(--line); background: transparent; }
.btn-outline-primary:hover { color: var(--ink); border-color: var(--ink); }

/* action bar at end of form — no border, just spacing */
.actions-bar {
  margin-top: 24px;
  display: flex; gap: 10px; justify-content: flex-end;
  align-items: center;
}

/* ── alerts ────────────────────────────────────────────────────── */
.alert {
  padding: 12px 16px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--surface);
  color: var(--ink-2);
  font-size: 13.5px;
  margin-bottom: 16px;
}
.alert-success { background: #F0FDF4; border-color: #BBF7D0; color: #166534; }
.alert-danger  { background: #FEF2F2; border-color: #FECACA; color: #991B1B; }
.alert-warning { background: #FFFBEB; border-color: #FDE68A; color: #92400E; }
.alert-info    { background: #EFF6FF; border-color: #BFDBFE; color: #1E40AF; }

/* ── upload page (drop-zone + uploads table) ─────────────────── */
/* upload section: full-width drop zone, then a row with hint + button */
.upload-footer-row {
  margin-top: 14px;
  display: flex; align-items: center;
  gap: 16px;
}
.upload-footer-row .form-text { flex: 1; margin: 0; }
.upload-footer-row .btn { height: 40px; padding: 0 18px; font-size: 13.5px; flex-shrink: 0; }
@media (max-width: 640px) {
  .upload-footer-row { flex-direction: column; align-items: stretch; }
  .upload-footer-row .btn { width: 100%; }
}

/* Split download button: [ ⬇ Download | ▾ ] with a dropdown of formats.
   Replaces the old wide <select> + button combo. */
.dl-split {
  position: relative;
  display: inline-flex;
  vertical-align: middle;
  margin: 4px 0;
}
.dl-split .btn { height: 32px; }
.dl-split__main { gap: 6px; }
/* flatten the joined edge ONLY when a caret sits next to it; a lone main
   button (single-format row) keeps its full rounded corners */
.dl-split__main:not(:only-child) {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}
.dl-split__main svg { width: 14px; height: 14px; }
.dl-split__label {
  max-width: 132px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dl-split__caret {
  padding: 0 8px;
  margin-left: -1px;               /* collapse the shared 1px border */
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}
.dl-split__caret svg { width: 14px; height: 14px; }
.dl-split:hover .btn,
.dl-split.open .btn { border-color: var(--ink); color: var(--ink); }
.dl-split.open .dl-split__caret { position: relative; z-index: 1; }
.dl-menu {
  position: fixed;               /* popover — escapes the table's overflow clip */
  z-index: 1000;
  min-width: 150px;
  padding: 4px;
  display: none;
  flex-direction: column;
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.10);
}
.dl-split.open .dl-menu { display: flex; }
.dl-menu__item {
  display: block;
  padding: 7px 12px;
  border-radius: var(--r-sm);
  font-size: 12.5px;
  font-weight: 500;
  color: var(--ink-2);
  text-decoration: none;
  white-space: nowrap;
}
.dl-menu__item:hover { background: var(--surface-3); color: var(--ink); }
.client-upload-report-link {
  color: var(--accent);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 3px;
  white-space: nowrap;
}
.client-upload-report-link:hover {
  color: var(--accent-2);
}
/* max-width on the CELL is ignored by auto table layout — the cap must sit on
   the button, whose preferred width is what actually sizes the column */
.client-upload-comment-cell { min-width: 180px; }
.client-upload-comment-btn {
  appearance: none;
  -webkit-appearance: none;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  width: 100%;
  max-width: 260px;
  padding: 0;
  overflow: hidden;
  border: 0 !important;
  border-radius: 0;
  box-shadow: none;
  background: none !important;
  color: var(--ink-2);
  font: inherit;
  font-size: 13px;
  font-weight: 400;
  line-height: 1.4;
  text-align: left;
  overflow-wrap: anywhere;
  white-space: normal;
  cursor: pointer;
}
.client-upload-comment-btn:hover { color: var(--ink); text-decoration: underline; }
.client-upload-comment-btn.is-empty { color: var(--ink-4); }
.client-upload-comment-modal {
  width: min(560px, calc(100vw - 32px));
  padding: 0;
  border: 1px solid var(--line-2);
  border-radius: var(--r);
  background: var(--surface);
  color: var(--ink-3);
  box-shadow: none;
}
.client-upload-comment-modal::backdrop { background: rgba(0, 0, 0, 0.36); }
.client-upload-comment-modal form { padding: 0; }
.client-upload-comment-modal__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 18px 20px;
  border-bottom: 1px solid var(--line);
}
.client-upload-comment-modal__head h3 {
  margin: 0;
  font-family: 'Inter', sans-serif;
  font-size: 14px;
  font-weight: 600;
}
.client-upload-comment-modal__close {
  appearance: none;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--ink-3);
  font-size: 26px;
  line-height: 1;
  cursor: pointer;
}
.client-upload-comment-modal__close:hover { color: var(--ink); }
.client-upload-comment-modal__body { padding: 20px; }
.client-upload-comment-modal label { display: block; margin-bottom: 7px; font-weight: 600; }
.client-upload-comment-modal textarea {
  display: block;
  width: 100%;
  padding: 10px 12px;
  resize: vertical;
  border: 1px solid var(--line-2);
  border-radius: var(--r);
  background: var(--surface);
  font-size: 14px;
  line-height: 1.5;
}
.client-upload-comment-modal textarea:hover { border-color: var(--line-3); }
.client-upload-comment-modal textarea:focus { border-color: var(--ink-3); outline: none; }
.client-upload-comment-modal__actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 14px 20px;
  border-top: 1px solid var(--line);
}
@media (max-width: 640px) {
  .dl-split { display: inline-flex; }
  .dl-menu { min-width: 160px; }
}

.archive-progress-cell {
  display: inline-flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  vertical-align: middle;
}
.archive-progress-bar {
  position: relative;
  display: block;
  width: 90px;
  flex: 0 0 auto;
  height: 6px;
  overflow: hidden;
  border-radius: 999px;
  background: var(--line);
}
.archive-progress-bar span {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  min-width: 2px;
  border-radius: inherit;
  background: var(--ink);
  transition: width 160ms ease;
}
.archive-progress-cell small {
  color: var(--ink-4);
  font-size: 11.5px;
  line-height: 1.25;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.upload-zone {
  position: relative;
  display: flex; align-items: center; gap: 16px;
  border: 1.5px dashed var(--line-2);
  border-radius: var(--r-lg);
  padding: 18px 22px;
  background: var(--surface-2);
  transition: 120ms;
  cursor: pointer;
  text-align: left;
  min-height: 72px;
}
.upload-zone:hover, .upload-zone.dragover { border-color: var(--ink); background: var(--surface); }
.upload-zone input[type=file] {
  position: absolute;
  top: 0; left: 0; right: 0; bottom: 0;
  width: 100%; height: 100%;
  opacity: 0; cursor: pointer;
}
.upload-zone .uz-text { flex: 1; min-width: 0; }
.upload-zone .uz-icon {
  width: 36px; height: 36px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--bg);
  color: var(--ink-3);
  flex-shrink: 0;
}
.upload-zone .uz-icon svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.upload-zone .uz-title {
  font-size: 14px; font-weight: 600; color: var(--ink);
  margin: 0 0 2px;
}
.upload-zone .uz-sub {
  font-size: 12.5px; color: var(--ink-4);
  margin: 0;
}
.upload-zone .uz-file {
  display: block;
  margin-top: 4px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 12.5px; color: var(--ink);
  font-weight: 500;
}
.upload-zone .uz-file-summary {
  display: block;
  margin-bottom: 3px;
}
.upload-zone .uz-file-list {
  margin: 3px 0 0;
  padding-left: 18px;
  max-height: 120px;
  overflow: auto;
}
.upload-zone .uz-file-list li {
  margin: 1px 0;
  overflow-wrap: anywhere;
}

.allowed-chips {
  display: inline-flex; gap: 6px; flex-wrap: wrap;
  margin-left: 6px;
}
.allowed-chip {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11.5px; font-weight: 500;
  color: var(--ink-2);
  padding: 2px 8px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--bg);
}

.upload-progress-card {
  margin-top: 16px;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--surface-2);
}
.upload-progress-card label {
  font-size: 12px; font-weight: 600; color: var(--ink-3);
  display: block; margin-bottom: 8px;
  letter-spacing: 0.04em; text-transform: uppercase;
}
progress {
  width: 100%; height: 6px;
  appearance: none;
  border: none;
  border-radius: 999px;
  background: var(--line);
  overflow: hidden;
}
progress::-webkit-progress-bar { background: var(--line); border-radius: 999px; }
progress::-webkit-progress-value { background: var(--ink); border-radius: 999px; transition: width 120ms; }
progress::-moz-progress-bar { background: var(--ink); }
.upload-progress-card small {
  display: block; margin-top: 8px;
  color: var(--ink-4); font-size: 12px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
}

/* uploads table — no outer border, relies on the parent card's border */
.table {
  width: 100%;
  border-collapse: separate; border-spacing: 0;
  background: var(--surface);
}
.table th, .table td {
  padding: 12px 16px;
  border-top: 1px solid var(--line);
  font-size: 13px;
  text-align: left;
  vertical-align: middle;
}
.table thead th {
  background: var(--surface-2);
  font-size: 11px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--ink-4);
  border-top: none;
}
/* round corners so the table sits flush inside its rounded card */
.table thead tr:first-child th:first-child { border-top-left-radius: var(--r-lg); }
.table thead tr:first-child th:last-child  { border-top-right-radius: var(--r-lg); }
.table tbody tr:last-child  td:first-child { border-bottom-left-radius: var(--r-lg); }
.table tbody tr:last-child  td:last-child  { border-bottom-right-radius: var(--r-lg); }
.table tbody tr:first-child td { border-top: 1px solid var(--line); }
.table-responsive { width: 100%; overflow-x: auto; }
.table-striped tbody tr:nth-child(even) td { background: var(--surface-2); }
.table-bordered { border: 1px solid var(--line); }
.table-sm th, .table-sm td { padding: 8px 12px; font-size: 12.5px; }

.empty-state {
  text-align: center;
  padding: 36px 24px;
  color: var(--ink-4);
  font-size: 13px;
}

/* ── login card ─────────────────────────────────────────────────── */
.login-wrap {
  min-height: calc(100vh - 240px);
  display: grid; place-items: center;
  padding: 48px 24px;
}
.login-card {
  width: 100%; max-width: 420px;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 36px 32px;
  background: var(--surface);
}
.login-card .login-title {
  font-family: 'Inter', sans-serif;
  font-size: 24px; font-weight: 600;
  letter-spacing: -0.01em;
  margin: 0 0 6px;
  color: var(--ink);
}
.login-card .login-sub {
  font-size: 13.5px; color: var(--ink-4);
  margin: 0 0 28px;
}

.login-card.error-404 { text-align: center; }
.login-card.error-404 .login-sub { margin-left: auto; margin-right: auto; }
.login-card.error-404 .error-code {
  font-family: 'Unbounded', 'Inter', sans-serif;
  font-size: 72px; font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1; color: var(--ink-4);
  margin-bottom: 18px;
}

/* ── footer (matches theacademia.tech structure) ───────────────── */
.app-footer {
  border-top: 1px solid var(--line);
  background: var(--bg);
  /* No margin: the main column above already grows to fill the window, and a
     margin here would push the footer off the bottom of a short page. */
  margin-top: 0;
  flex-shrink: 0;
}
/* Bottom padding is lighter than the top: below the last line there is only
   the window edge, so the same 48px read as a dead band under the copyright. */
.app-footer-inner {
  max-width: 1400px;
  margin: 0 auto;
  padding: 40px 24px 20px;
}
@media (min-width: 1024px) {
  .app-footer-inner { padding: 48px 48px 22px; }
}
@media (min-width: 1280px) {
  .app-footer-inner { padding: 48px 96px 22px; }
}
.app-footer-row {
  display: flex; flex-direction: column; gap: 24px;
  align-items: flex-start;
  justify-content: space-between;
  margin-bottom: 32px;
}
@media (min-width: 768px) {
  .app-footer-row { flex-direction: row; align-items: center; }
}
.app-footer-brand { display: inline-flex; align-items: center; gap: 10px; }
.app-footer-brand .brand-logo { width: 24px; height: 24px; }
.app-footer-brand .brand-name { font-size: 15px; }
.app-footer-links {
  display: inline-flex; flex-wrap: wrap;
  gap: 24px;
  font-size: 13.5px;
}
.app-footer-links a { color: var(--ink-4); transition: color 200ms; }
.app-footer-links a:hover { color: var(--ink); }
.app-footer-bottom {
  border-top: 1px solid var(--line);
  padding-top: 18px;
  font-size: 13px;
  color: var(--ink-4);
}

/* ── Bootstrap utility shims (so the upload JS keeps working) ──── */
.d-none { display: none !important; }
.mb-0 { margin-bottom: 0 !important; }
.mb-3 { margin-bottom: 16px; }
.mt-3 { margin-top: 16px; }
.my-4 { margin-top: 24px; margin-bottom: 24px; }
.h5 { font-size: 16px; font-weight: 600; color: var(--ink); margin: 0; }
.text-end { text-align: right; }
.text-nowrap { white-space: nowrap; }
.text-muted { color: var(--ink-4); }
.align-middle { vertical-align: middle; }
.w-100 { width: 100%; }
.form-label {
  display: block;
  margin-bottom: 8px;
  font-size: 13px; font-weight: 500; color: var(--ink-2);
}
.form-text { font-size: 12.5px; color: var(--ink-4); }

/* default — desktop hides duplicate nav items in the 3-dot menu */
@media (min-width: 761px) {
  .more-menu .nav-dup { display: none; }
}

/* responsive — overrides above */
@media (max-width: 760px) {
  .app-header-inner { gap: 10px; padding: 14px 16px; }
  .nav-links { display: none; }
  .brand { gap: 9px; }
  .brand-name { font-size: 15px; letter-spacing: 0.06em; }
  .brand-logo { width: 26px; height: 26px; }
  .brand-product { font-size: 11px; padding: 4px 7px; margin-left: 4px; }
  .lang-btn { padding: 5px 11px; font-size: 12px; }
  .icon-btn { width: 36px; height: 36px; }
  .icon-btn svg { width: 20px; height: 20px; }
  .header-actions { gap: 8px; }
  .page { padding: 32px 16px 64px; }
  .page-title { font-size: 26px; line-height: 1.15; }
  .page-head { flex-direction: column; }
  .page-subtitle { font-size: 13px; }
  .lic-chip { font-size: 11.5px; padding: 6px 12px; }
  .card-head { padding: 14px 16px; gap: 10px; }
  .card-body { padding: 18px 16px; }
  .actions-bar { flex-direction: column-reverse; align-items: stretch; }
  .actions-bar .btn { width: 100%; }
  .app-footer-inner { padding: 32px 16px; }
  .app-footer-row { margin-bottom: 32px; }
  .app-footer-links { font-size: 12.5px; gap: 14px; row-gap: 8px; }
  .personal .horizontal-list { grid-template-columns: 1fr; gap: 4px; }
}


/* ══════════════════════════════════════════════════════════════════════
   Portal — the two tiles, the ACADEMIA Link wordmark, and the app dialog
   ══════════════════════════════════════════════════════════════════════ */

/* Visually hidden, still read aloud. */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* Errors float over the page instead of sitting in it. In the flow they push
   the tiles down, so the moment a key is mistyped the careful equal margins
   above and below become unequal — the layout jumping is a louder signal than
   the message itself. Out of flow, the tiles do not move at all. */
.portal-alerts {
  position: absolute;
  top: 10px; left: 24px; right: 24px;
  z-index: 30;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  pointer-events: none;          /* never blocks the tile underneath on a phone */
}
.portal-alerts .alert {
  width: 100%; max-width: 560px; margin: 0;
  pointer-events: auto;
}

/* ── a page title with a way back beside it ──────────────────────────── */
.title-row { display: flex; align-items: center; gap: 14px; }
.backbox {
  flex-shrink: 0;
  width: 38px; height: 38px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--surface); color: var(--ink-3);
  text-decoration: none; transition: 80ms;
}
.backbox:hover { border-color: var(--ink-3); color: var(--ink); background: var(--surface-2); }
.backbox svg { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
@media (max-width: 640px) {
  .title-row { gap: 10px; }
  .backbox { width: 32px; height: 32px; }
  .backbox svg { width: 15px; height: 15px; }
}

/* The portal is two tiles and nothing else, so they sit in the middle of
   whatever room is left between the header and the footer. Centring inside the
   flex column beats subtracting a guessed header+footer height from 100vh: it
   stays right when the footer wraps on a phone or the header grows. */
.portal-center {
  flex: 1;
  display: flex; flex-direction: column; justify-content: center;
}
/* Equal air above and below the tiles. The default page padding is
   deliberately bottom-heavy (48/80) for pages that scroll; a page whose whole
   point is to be centred wants it even. */
.page--center { padding-top: 40px; padding-bottom: 40px; position: relative; }

/* Two tiles side by side, stacking on a narrow screen. They stretch to match
   each other: side-by-side cards of different heights read as one being
   unfinished. */
.tiles {
  max-width: 980px; margin: 0 auto; width: 100%;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px; align-items: stretch;
}
.tiles--single { max-width: 580px; grid-template-columns: minmax(0, 1fr); }
@media (max-width: 860px) {
  .tiles { grid-template-columns: minmax(0, 1fr); gap: 16px; }
}

.tile {
  display: flex; flex-direction: column;
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--surface);
  padding: 28px;
  min-width: 0;
}
@media (max-width: 520px) { .tile { padding: 22px 18px; } }

.tile h2 {
  font-family: 'Inter', sans-serif;
  font-size: 20px; font-weight: 700; letter-spacing: -0.02em;
  color: var(--ink); margin: 0 0 8px;
}
.tile .tile-d {
  font-size: 13.5px; line-height: 1.6; color: var(--ink-3);
  margin: 0 0 20px; max-width: 46ch;
}
/* The action row sits at the bottom, so two tiles of different text lengths
   still line their buttons up. */
.tile .tile-act { margin-top: auto; display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
/* A quiet text action beside a button, for the second thing in an action row
   that does not deserve a second button. */
.tile-act .act-link {
  font-size: 13.5px; font-weight: 500; color: var(--ink-3);
  text-decoration: underline; text-underline-offset: 3px;
  padding: 0 6px;
}
.tile-act .act-link:hover { color: var(--ink); }

/* TEMPORARY maintenance notice for ACADEMIA Link — _link_maint.html, included
   above the install button in the portal tile and the app dialog. Remove
   together. */
.link-maint {
  display: flex; align-items: flex-start; gap: 8px;
  margin: 0 0 16px;
  padding: 10px 12px;
  background: #FEF9C3;
  border: 1px solid #FDE047;
  border-radius: var(--r-sm);
  font-size: 12.5px; line-height: 1.5;
  color: #854D0E;
  text-align: left;
  max-width: 46ch;
}
.link-maint svg {
  width: 14px; height: 14px; flex-shrink: 0; margin-top: 2px;
  stroke: currentColor; fill: none; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}
/* .dlg p wins the color otherwise, which turned the dialog's copy gray. */
.dlg .link-maint { color: #854D0E; margin: 0 0 16px; }

.tile .tic {
  width: 38px; height: 38px; border-radius: var(--r);
  background: var(--surface-2); border: 1px solid var(--line);
  display: inline-flex; align-items: center; justify-content: center;
  margin-bottom: 16px;
}
.tile .tic svg {
  width: 18px; height: 18px; stroke: var(--ink-2);
  fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
}
.tile .tic--mark { background: var(--surface); }
/* The logo source is white on transparent, so every copy of it needs the same
   invert the header brand does — without it the mark is invisible on white. */
.tile .tic--mark img,
.dlg .lk-mark { display: block; object-fit: contain; filter: invert(1); }

.tile-stats { display: flex; gap: 18px; margin: -8px 0 20px; font-size: 12.5px; color: var(--ink-3); }
.tile-stats .stat b { color: var(--ink); font-weight: 700; font-variant-numeric: tabular-nums; }
.tile-stats .stat b.ok { color: var(--ok); }
.tile-stats .stat b.zero { color: var(--danger); }

/* Key field and button stay on one line at every width. The field gives up the
   room — `min-width: 0` lets it shrink past its content, which a flex item
   otherwise refuses to do — and the button keeps its natural size, since a
   two-word label wrapping is worse than a shorter field. */
.keyrow { display: flex; gap: 8px; width: 100%; flex-wrap: nowrap; }
.keyrow .input-wrap { flex: 1 1 auto; min-width: 0; }
.keyrow .btn { flex: 0 0 auto; white-space: nowrap; }
@media (max-width: 420px) {
  .keyrow { gap: 6px; }
  .keyrow .btn { padding: 0 14px; }
  .keyrow .input-wrap input { font-size: 13px; }
}


/* ── the ACADEMIA Link wordmark ──────────────────────────────────────── */
/* Lifted from the desktop app's `.brand__word` so the lockup is the same
   object in both places: Unbounded, ACADEMIA at 600 / +0.04em — the weight and
   tracking theacademia.tech itself uses — and the product word at 500 / 0.9em
   / +0.01em in Link's blue. It was 800 at -0.01em here, heavier than the brand
   and spaced the wrong way.
   The rules target <b> and the inner <span>, so getting the elements wrong
   renders the lockup as one run, "ACADEMIALink". */
.wordmark {
  font-family: 'Unbounded', 'Inter', sans-serif;
  font-size: 19px; color: var(--ink);
  text-transform: none; white-space: nowrap;
  line-height: 1;
}
.wordmark b { font-weight: 600; letter-spacing: 0.04em; }
.wordmark span {
  font-weight: 500; font-size: 0.9em; letter-spacing: 0.01em;
  margin-left: 0.22em; color: var(--link, #1A56DB);
}
.wordmark--sm { font-size: 12.5px; }

.wordmark-h { display: flex; align-items: center; gap: 10px; margin: 0 0 8px; flex-wrap: wrap; }

/* Beta tag. Shaped like the product chip in the header — same 5px corner, no
   outline — but in Link's own blue rather than the olive, because the tag
   belongs to Link and sits against its wordmark. Inter rather than Unbounded:
   Unbounded is for names, and this is a status. */
.beta {
  display: inline-flex; align-items: center;
  padding: 3px 7px;
  border-radius: 5px;
  background: var(--link-soft); color: var(--link);
  font-family: 'Inter', sans-serif;
  font-size: 10px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  white-space: nowrap;
}

/* The Link menu item carries a wordmark and a tag rather than an icon. */
.more-menu button .wordmark--sm { flex: 0 0 auto; }
.more-menu button .beta { margin-left: auto; }

.btn-link {
  background: var(--link, #1A56DB); border-color: var(--link, #1A56DB); color: #FFFFFF;
}
.btn-link:hover { background: #1748B8; border-color: #1748B8; color: #FFFFFF; }
.btn-link svg { stroke: currentColor; }
.btn-link:disabled, .btn-link[disabled] { opacity: .55; }

/* The download button says whatever the visitor's platform makes it say, so
   its width is not something the layout gets to assume: "Download for Windows"
   in one language is half again as long in another. .btn is nowrap at a fixed
   44px, which is right for the buttons whose labels we write, and wrong for
   this one — on a 320px phone the longest label ran past the right edge and
   took the page with it. Here alone, let it wrap and take the height it needs. */
a.btn-link[data-link-dl] { max-width: 100%; }

/* No build for this visitor's system. Faded and inert rather than absent:
   the button is part of the tile's layout, and removing it leaves a hole and
   no explanation.

   Faded, not greyed. A grayscale filter turned Link's blue into a grey slab
   that read as some other control rather than as this one switched off; the
   colour is what makes it recognisable, and opacity alone says "not now"
   perfectly well. pointer-events belongs here rather than only in the script
   so the state survives a re-render. */
.btn--off,
a.btn--off {
  opacity: .5;
  cursor: not-allowed;
  pointer-events: none;
}
@media (max-width: 400px) {
  a.btn-link[data-link-dl] {
    white-space: normal;
    height: auto; min-height: 44px;
    padding: 10px 18px;
    text-align: center;
    line-height: 1.35;
  }
}

/* ── app dialog ──────────────────────────────────────────────────────── */
.ovl {
  position: fixed; inset: 0; z-index: 60;
  background: rgba(0, 0, 0, .45);
  display: grid; place-items: center;
  padding: 24px;
}
.ovl[hidden] { display: none; }
body.no-scroll { overflow: hidden; }

.dlg {
  position: relative;
  width: 100%; max-width: 420px;
  background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: 32px 28px 26px;
  text-align: center;
  box-shadow: 0 18px 48px rgba(0, 0, 0, .18);
}
.dlg .lk { display: inline-flex; align-items: center; gap: 10px; margin-bottom: 14px; flex-wrap: wrap; justify-content: center; }
.dlg p { font-size: 13.5px; line-height: 1.6; color: var(--ink-3); margin: 0 0 20px; }
.dlg .btn { width: 100%; justify-content: center; }
.dlg-x {
  position: absolute; top: 10px; right: 10px;
  width: 30px; height: 30px; border-radius: var(--r-sm);
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: none; cursor: pointer; color: var(--ink-3);
}
.dlg-x:hover { background: var(--surface-2); color: var(--ink); }
.dlg-x svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

/* ── a phone is 320px wide ───────────────────────────────────────────────
   The narrowest screen anybody still browses on. Everything here fixes the
   same failure: a box that cannot become narrower than its contents, on a
   screen narrower than those contents, which makes the whole page slide
   sideways under the thumb. .btn is nowrap by design - correct where a label
   is two short words, wrong where a button is the full width of a card and
   its label was written in a language with longer ones. */
@media (max-width: 420px) {
  /* A button told to fill its container has already given up on being sized
     by its label, so let the label wrap inside it. */
  .btn[style*="width:100%"], .btn[style*="width: 100%"],
  .dlg .btn, .login-card .btn, .card-body > .btn {
    white-space: normal;
    height: auto; min-height: 44px;
    padding: 10px 16px;
    line-height: 1.35;
  }
  /* 24 + 32 of padding either side leaves 208px of a 320px screen for the
     content. Half of it back. */
  .login-wrap { padding: 32px 12px; }
  .login-card { padding: 24px 18px; }
  .ovl { padding: 14px; }
  .dlg { padding: 28px 18px 22px; }
}


/* ── list bar: search + pager ───────────────────────────────────────
   One row above a table: a single search field on the left, the range and
   two arrows on the right. It is a strip, not a card body - no 24px padding,
   no second border - so the table below reads as the same object.

   Everything here is 32px tall and border-only, matching .btn-sm, so the bar
   sits quietly under the card head instead of competing with it. */
.list-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding: 12px 16px;
  border-bottom: 1px solid var(--line);
}

/* The field is the strip: the magnifier and the clear button live inside one
   bordered box, so there is a single focus target and a single outline. */
.list-bar .search {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1 1 260px;
  max-width: 380px;
  height: 32px;
  padding: 0 10px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  transition: border-color 120ms;
}
.list-bar .search:hover { border-color: var(--line-3); }
.list-bar .search:focus-within { border-color: var(--ink); }

.list-bar .search > svg {
  flex: none;
  width: 14px; height: 14px;
  stroke: var(--ink-4); fill: none;
  stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}
.list-bar .search:focus-within > svg { stroke: var(--ink-3); }

/* The input carries no border of its own - the wrapper is the border. */
.list-bar .search input {
  flex: 1 1 auto;
  min-width: 0;
  height: 100%;
  border: 0; outline: none; background: none;
  font-size: 13px;
  color: var(--ink);
}
.list-bar .search input::placeholder { color: var(--ink-4); }
/* WebKit draws its own round clear button inside type=search; ours is a link
   that also resets the URL, so suppress the native one. */
.list-bar .search input::-webkit-search-cancel-button { display: none; }

.list-bar .search-clear {
  flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px;
  border-radius: 50%;
  color: var(--ink-4);
}
.list-bar .search-clear:hover { color: var(--ink); background: var(--surface-3); }
.list-bar .search-clear svg {
  width: 11px; height: 11px;
  stroke: currentColor; fill: none;
  stroke-width: 2.2; stroke-linecap: round;
}

/* Right-hand end of the search strip: where you are, and the two arrows, in
   view before the rows rather than 25 of them later. */
.pager {
  display: flex;
  align-items: center;
  gap: 4px;
  flex: none;
}
.pager[hidden] { display: none; }
.pager-range {
  margin-right: 6px;
  font-size: 12.5px;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.pager-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  color: var(--ink);
  transition: 120ms;
}
.pager-btn:hover { border-color: var(--ink); }
.pager-btn svg {
  width: 14px; height: 14px;
  stroke: currentColor; fill: none;
  stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}
/* An arrow with nowhere to go keeps its box - the pager must not change width
   as you reach either end, or the button you are clicking moves. */
.pager-btn:not([href]) {
  color: var(--ink-5);
  border-color: var(--line);
  pointer-events: none;
}

/* The table is the last thing in the card, and the card is rounded but does
   NOT clip - its download menus have to escape - so the table's square white
   background would show through the two bottom corners. The scroller already
   carries overflow-x:auto, which computes overflow-y to auto as well, so a
   radius on it actually clips. */
#uploads-card .table-responsive {
  border-bottom-left-radius: var(--r-lg);
  border-bottom-right-radius: var(--r-lg);
}

.list-empty {
  margin: 0;
  padding: 28px 16px;
  color: var(--ink-4);
  text-align: center;
}

@media (max-width: 560px) {
  /* One row on a phone too. The field gives up its fixed basis and takes
     whatever the pager leaves rather than pushing it onto a second line; at
     320px that is still about ten visible characters, and a longer query just
     scrolls inside the input. */
  .list-bar { gap: 10px; flex-wrap: nowrap; }
  .list-bar .search { flex: 1 1 0; min-width: 0; max-width: none; }
}
