/* TFAdmin component layer — composes tokens.css. No raw values below this line. */
*, *::before, *::after { box-sizing: border-box; }
body {
  margin: 0; background: var(--tf-bg); color: var(--tf-fg);
  font: var(--tf-weight-normal) var(--tf-text-body) / var(--tf-leading-body) var(--tf-font);
}
:focus { outline: none; }
:focus-visible { outline: 2px solid var(--tf-accent); outline-offset: 2px; border-radius: var(--tf-radius-sm); }
a { color: var(--tf-accent); text-underline-offset: 3px; }
a:hover { color: var(--tf-accent-strong); }
svg { display: block; }

/* — shell — */
.app { display: grid; grid-template-columns: var(--tf-sidebar-w) minmax(0, 1fr); min-height: 100vh; }
.page { padding: var(--tf-space-6); }
.page-inner { max-width: var(--tf-page-max); margin-inline: auto; display: flex; flex-direction: column; gap: var(--tf-space-5); }
.page-title { font-size: var(--tf-text-display); line-height: var(--tf-leading-display); font-weight: var(--tf-weight-medium); margin: 0; }
.section-title { font-size: var(--tf-text-title); line-height: var(--tf-leading-title); font-weight: var(--tf-weight-medium); margin: 0; }
.stack { display: flex; flex-direction: column; gap: var(--tf-space-3); }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--tf-space-3); }

/* — sidebar — */
.sidebar { display: flex; flex-direction: column; gap: var(--tf-space-2); padding: var(--tf-space-3); background: var(--tf-surface); border-right: 1px solid var(--tf-border); }
.nav-item { display: flex; align-items: center; gap: var(--tf-space-2); min-height: var(--tf-control-h); padding: 0 var(--tf-space-3); border-radius: var(--tf-radius-sm); color: var(--tf-fg-muted); text-decoration: none; transition: background var(--tf-transition), color var(--tf-transition); }
.nav-item:hover { background: var(--tf-accent-weak); color: var(--tf-fg); }
.nav-item[aria-current="page"] { background: var(--tf-accent-weak); color: var(--tf-accent); font-weight: var(--tf-weight-medium); }

/* — buttons — */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--tf-space-2); min-height: var(--tf-control-h); padding: 0 var(--tf-space-3); border: 1px solid transparent; border-radius: var(--tf-radius-sm); background: transparent; color: var(--tf-fg); font: var(--tf-weight-medium) var(--tf-text-body) / 1 var(--tf-font); cursor: pointer; text-decoration: none; transition: background var(--tf-transition), border-color var(--tf-transition); }
.btn-sm { min-height: var(--tf-control-h-sm); padding: 0 var(--tf-space-2); font-size: var(--tf-text-small); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }
.btn-primary { background: var(--tf-accent); color: var(--tf-accent-fg); }
.btn-primary:hover { background: var(--tf-accent-strong); }
.btn-secondary { border-color: var(--tf-border-strong); color: var(--tf-fg); }
.btn-secondary:hover { background: var(--tf-accent-weak); border-color: var(--tf-accent); }
.btn-ghost { color: var(--tf-accent); }
.btn-ghost:hover { background: var(--tf-accent-weak); }
.btn-icon { width: var(--tf-control-h); height: var(--tf-control-h); padding: 0; color: var(--tf-fg-muted); }
.btn-icon:hover { background: var(--tf-accent-weak); color: var(--tf-fg); }

/* — badges — */
.badge { display: inline-flex; align-items: center; gap: var(--tf-space-1); padding: 2px var(--tf-space-2); border-radius: var(--tf-radius-sm); font-size: var(--tf-text-micro); line-height: var(--tf-leading-micro); font-weight: var(--tf-weight-medium); }
.badge-success { background: var(--tf-success-bg); color: var(--tf-success); }
.badge-warning { background: var(--tf-warning-bg); color: var(--tf-warning); }
.badge-danger  { background: var(--tf-danger-bg);  color: var(--tf-danger); }
.badge-info    { background: var(--tf-info-bg);    color: var(--tf-info); }
.badge-neutral { background: var(--tf-bg); color: var(--tf-fg-muted); box-shadow: inset 0 0 0 1px var(--tf-border); }

/* — cards — */
.card { display: flex; flex-direction: column; gap: var(--tf-space-1); padding: var(--tf-space-4); border-radius: var(--tf-radius-md); background: var(--tf-surface); box-shadow: var(--tf-shadow-sm); }
.card-label { font-size: var(--tf-text-micro); letter-spacing: 0.08em; text-transform: uppercase; color: var(--tf-fg-subtle); }
.card-value { font-size: var(--tf-text-display); line-height: var(--tf-leading-display); font-weight: var(--tf-weight-medium); font-variant-numeric: tabular-nums; }
.card-note { font-size: var(--tf-text-small); color: var(--tf-fg-muted); }
.card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--tf-space-3); }

/* — tabs / chips — */
.tabs { display: flex; gap: var(--tf-space-4); border-bottom: 1px solid var(--tf-border); }
.tab { padding: var(--tf-space-2) 0; border-bottom: 2px solid transparent; color: var(--tf-fg-muted); background: none; border-top: 0; border-inline: 0; font: var(--tf-weight-medium) var(--tf-text-body) / 1.4 var(--tf-font); cursor: pointer; }
.tab:hover { color: var(--tf-fg); }
.tab[aria-selected="true"] { color: var(--tf-accent); border-bottom-color: var(--tf-accent); }
.chips { display: flex; flex-wrap: wrap; gap: var(--tf-space-2); }
.chip { padding: var(--tf-space-1) var(--tf-space-3); border: 1px solid var(--tf-border); border-radius: 999px; background: var(--tf-surface); color: var(--tf-fg-muted); font-size: var(--tf-text-small); cursor: pointer; }
.chip:hover { border-color: var(--tf-accent); color: var(--tf-fg); }
.chip[aria-pressed="true"] { background: var(--tf-accent-weak); border-color: var(--tf-accent); color: var(--tf-accent); }

/* — table — */
.table-scroll { overflow-x: auto; background: var(--tf-surface); border-radius: var(--tf-radius-md); box-shadow: var(--tf-shadow-sm); }
.table { width: 100%; border-collapse: collapse; font-size: var(--tf-text-body); }
.table th { text-align: left; padding: var(--tf-space-3); font-size: var(--tf-text-micro); letter-spacing: 0.08em; text-transform: uppercase; font-weight: var(--tf-weight-medium); color: var(--tf-fg-subtle); border-bottom: 1px solid var(--tf-border); white-space: nowrap; }
.table td { padding: var(--tf-space-3); border-bottom: 1px solid var(--tf-border); }
.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr:hover { background: var(--tf-accent-weak); }
.table .num { text-align: right; font-variant-numeric: tabular-nums; }
.cell-empty { color: var(--tf-fg-subtle); }

/* — row list — */
.rows { display: flex; flex-direction: column; gap: var(--tf-space-2); }
.row { display: flex; align-items: center; gap: var(--tf-space-3); padding: var(--tf-space-3) var(--tf-space-4); border-radius: var(--tf-radius-md); background: var(--tf-surface); box-shadow: var(--tf-shadow-sm); }
.row-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.row-title { font-size: var(--tf-text-subtitle); line-height: var(--tf-leading-subtitle); font-weight: var(--tf-weight-medium); }
.row-meta { display: flex; gap: var(--tf-space-2); font-size: var(--tf-text-small); color: var(--tf-fg-muted); }

/* — forms — */
.field { display: flex; flex-direction: column; gap: var(--tf-space-1); }
.field > label { font-size: var(--tf-text-small); color: var(--tf-fg-muted); }
.input { width: 100%; min-height: var(--tf-control-h); padding: 0 var(--tf-space-3); background: var(--tf-surface); color: var(--tf-fg); border: 1px solid var(--tf-border-strong); border-radius: var(--tf-radius-sm); font: var(--tf-weight-normal) var(--tf-text-body) / var(--tf-leading-body) var(--tf-font); }
.input::placeholder { color: var(--tf-fg-subtle); }
.input:hover { border-color: var(--tf-accent); }
.input:focus-visible { border-color: var(--tf-accent); outline-offset: 0; }
.input-search { padding-left: var(--tf-space-6); background-repeat: no-repeat; background-position: var(--tf-space-3) center; }

/* THE SELECT, ON THE ELEMENT RATHER THAN A CLASS.
 *
 * `.input` has never had a <select> counterpart, so a screen that wanted one
 * either improvised -- five near-identical rules across styles.css, at four
 * different radii -- or got nothing. "Nothing" is the OS control: square
 * corners, its own grey, its own idea of a border, sitting beside our rounded
 * fields. Reservations, the staff panel, the permissions dialogs and the CBT
 * admin table were all showing it.
 *
 * A class would have to be added to every <select> in the app to fix that, and
 * would be forgotten by the next one written. The shape goes on the element;
 * the classes that already exist keep their sizing on top of it.
 *
 * NO HEIGHT IS SET, deliberately. The folder's controls are a compact
 * subsystem sized by padding (.iff-native, ~31px), and a min-height here would
 * push its selects to 36 while the inputs beside them in the same table row
 * stayed short -- one row, two heights. Padding gives a bare select a sane
 * height and yields to every rule that sets its own.
 *
 * THE ARROW IS LEFT TO THE BROWSER. `appearance: none` removes it, and drawing
 * a replacement means a colour -- a raw value here, and a second one for dark.
 * `color-scheme` in tokens.css already themes the native one for both. */
select {
  padding: var(--tf-space-2) var(--tf-space-3);
  background: var(--tf-surface);
  color: var(--tf-fg);
  border: 1px solid var(--tf-border-strong);
  border-radius: var(--tf-radius-sm);
  /* LEADING IS `normal`, WHERE EVERY OTHER CONTROL TAKES --tf-leading-body.
     A native select ignores line-height for its own text box, so this used to
     be free; under `appearance: base-select` below the control is a flex box
     and the leading becomes its height. At body size that is 18.75px against an
     input's ~15, which put every select three pixels taller than the input
     beside it -- visible in the folder, where the two sit in one table row. */
  font: var(--tf-weight-normal) var(--tf-text-body) / normal var(--tf-font);
  cursor: pointer;
  transition: border-color var(--tf-transition);
}
select:hover:not(:disabled) { border-color: var(--tf-accent); }
select:focus-visible { border-color: var(--tf-accent); outline: 2px solid var(--tf-accent); outline-offset: 2px; }
select:disabled { opacity: 0.45; cursor: not-allowed; }

/* THE MENU THAT OPENS, which is a different thing from the box above.
 *
 * Rounding the control left the popup square, because the popup was never ours
 * to draw: a native <select>'s list is browser chrome, outside the page, and no
 * amount of CSS on the element reaches it. `appearance: base-select` is the
 * opt-in that hands it over -- the list becomes a real popover in the top layer
 * with ::picker(select) as its box -- and it is why this is the one place in
 * the system where a popup can be given the same radius, surface and shadow as
 * everything else.
 *
 * BEHIND @supports, AND THAT MATTERS HERE. A browser without it ignores the
 * whole block and keeps the native list: square, but complete and working. The
 * alternative -- a JavaScript combobox -- is a keyboard trap, a screen-reader
 * rewrite and a scroll bug waiting to happen, in exchange for a corner radius.
 * The <select> stays a <select>; the browser keeps the typeahead, the arrow
 * keys, the touch behaviour and the a11y tree.
 *
 * The tick is hidden and selection is shown as a tint instead. base-select puts
 * a ::checkmark before the chosen row, which indents it out of line with the
 * others; the tint is how .chip[aria-pressed="true"] already says the same
 * thing, so the app has one way of showing "this one". */
@supports (appearance: base-select) {
  select, select::picker(select) { appearance: base-select; }

  select::picker(select) {
    padding: var(--tf-space-1);
    border: 1px solid var(--tf-border);
    border-radius: var(--tf-radius-md);
    background: var(--tf-surface-raised);
    box-shadow: var(--tf-shadow-md);
  }
  select::picker-icon { color: var(--tf-fg-muted); transition: transform var(--tf-transition); }
  select:open::picker-icon { transform: rotate(180deg); }

  /* min-height, because one option in the app is deliberately EMPTY: the runway
     picker's first row, which is how it says "none chosen yet". With nothing in
     it the block has no line box and collapses to its padding, leaving a half
     -height sliver at the top of the list that reads as a rendering fault
     rather than a choice. --tf-control-h puts it within a pixel of a row that
     does have text in it. */
  option {
    display: block;
    min-height: var(--tf-control-h);
    padding: var(--tf-space-2) var(--tf-space-3);
    border-radius: var(--tf-radius-sm);
    color: var(--tf-fg);
    font: var(--tf-weight-normal) var(--tf-text-body) / var(--tf-leading-body) var(--tf-font);
    cursor: pointer;
  }
  option::checkmark { display: none; }
  /* :active is the row under the pointer or the arrow keys; :checked is the one
     the folder actually holds. Both tint, and the chosen one also takes the
     accent ink, so an arrow-key walk never looks like it has changed the value. */
  option:hover, option:focus, option:active { background: var(--tf-accent-weak); }
  option:checked { background: var(--tf-accent-weak); color: var(--tf-accent); font-weight: var(--tf-weight-medium); }
}
.field-help { font-size: var(--tf-text-small); color: var(--tf-fg-muted); }
.field-error { font-size: var(--tf-text-small); color: var(--tf-danger); }

/* — feedback — */
.alert { display: flex; gap: var(--tf-space-2); padding: var(--tf-space-3) var(--tf-space-4); border-radius: var(--tf-radius-md); font-size: var(--tf-text-body); }
.alert-info { background: var(--tf-info-bg); color: var(--tf-info); }
.alert-danger { background: var(--tf-danger-bg); color: var(--tf-danger); }
.empty { display: flex; flex-direction: column; align-items: flex-start; gap: var(--tf-space-2); padding: var(--tf-space-6); border-radius: var(--tf-radius-md); background: var(--tf-surface); box-shadow: var(--tf-shadow-sm); color: var(--tf-fg-muted); }

/* .skeleton and its tf-pulse keyframes lived here unused: the app's
   placeholders are .sk / .sk-bar / .sk-line (styles.css), and the only
   "skeleton" in the markup is .cbt-card.is-skeleton, which is a different
   class built from those. Two skeleton systems, one of them never rendered. */

/* — dialog — */
.dialog-backdrop { position: fixed; inset: 0; display: grid; place-items: center; padding: var(--tf-space-4); background: rgba(9, 12, 16, 0.55); }
.dialog { width: min(480px, 100%); display: flex; flex-direction: column; gap: var(--tf-space-3); padding: var(--tf-space-5); border-radius: var(--tf-radius-lg); background: var(--tf-surface-raised); box-shadow: var(--tf-shadow-lg); }
.dialog-title { font-size: var(--tf-text-title); line-height: var(--tf-leading-title); font-weight: var(--tf-weight-medium); }
.dialog-actions { display: flex; justify-content: flex-end; gap: var(--tf-space-2); }

/* — responsive — */
@media (max-width: 1100px) {
  .app { grid-template-columns: var(--tf-sidebar-w-collapsed) minmax(0, 1fr); }
  .sidebar .nav-label { display: none; }
  .nav-item { justify-content: center; padding: 0; }
}
@media (max-width: 820px) {
  .app { grid-template-columns: minmax(0, 1fr); }
  .sidebar { position: fixed; inset: 0 auto 0 0; width: var(--tf-sidebar-w); transform: translateX(-100%); transition: transform var(--tf-transition); z-index: 20; }
  .sidebar[data-open="true"] { transform: none; }
  .sidebar .nav-label { display: inline; }
  .page { padding: var(--tf-space-4); }
}
/* `*` does NOT match pseudo-elements, so ::before/::after kept animating —
   .toggle-switch-track::after slid across on every toggle for someone who had
   asked the system for less motion. Named explicitly. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* — avatars — */
.avatar { display:inline-grid; place-items:center; flex:none; width:32px; height:32px; border-radius:50%; overflow:hidden; background:var(--tf-accent-weak); color:var(--tf-accent); }
.avatar img { width:100%; height:100%; object-fit:cover; }
.avatar i { font-size:20px; }
.avatar-sm { width:24px; height:24px; }
.avatar-sm i { font-size:16px; }
.avatar-lg { width:40px; height:40px; }
.avatar-lg i { font-size:24px; }

/* — mobile bottom bar (student roles, <820px) — */
.tabbar { display:none; }
@media (max-width: 820px) {
  .tabbar { position:fixed; inset:auto 0 0 0; display:grid; grid-template-columns:repeat(4,1fr); background:var(--tf-surface); border-top:1px solid var(--tf-border); z-index:15; }
  .tabbar a { display:flex; flex-direction:column; align-items:center; gap:2px; min-height:56px; justify-content:center; font-size:var(--tf-text-micro); color:var(--tf-fg-subtle); text-decoration:none; }
  .tabbar a[aria-current="page"] { color: var(--tf-accent); }
  .tabbar a i { font-size:24px; }
  body:has(.tabbar) .page { padding-bottom: calc(var(--tf-space-7) + var(--tf-space-5)); }
  .touch-target { min-height: 44px; min-width: 44px; }
}

/* — custom icons (frontend/icons.svg) — */
.tf-icon { width:20px; height:20px; fill:none; stroke:currentColor; stroke-width:1.9; stroke-linecap:round; stroke-linejoin:round; flex:none; }
.tf-icon-sm { width:16px; height:16px; }
.tf-icon-lg { width:24px; height:24px; stroke-width:1.6; }
/* Inside a LINE OF META TEXT, where 16px stands taller than the 13px it sits in
   and turns a quiet line into a row of badges. The only size below the table-cell
   16px, and it exists for that one job. */
.tf-icon-xs { width:14px; height:14px; }

/* flight-folder composite: folder cut-out + plane in the gap */
.icon-flight-folder { position:relative; display:inline-block; width:1em; height:1em; line-height:1; }
.icon-flight-folder .tf-icon { width:100%; height:100%; }
.icon-flight-folder .plane { position:absolute; right:-0.04em; bottom:0.04em; font-size:0.54em; line-height:1; }
