/* public/assets/css/mobile.css -- the tm-* app shell: reset, dashboard views, modals, the desktop sidebar layer, card decks and list tiles.
 *
 * Split out of mobile.css on 2026-09-19 (refactor audit Phase 3): its lines
 * 969-1145, 1470-1731, 1869-1938, moved verbatim and in their original order.
 * LOAD ORDER MATTERS: pages load these parts in a fixed order
 * (Handover/runnerkit_harness/css_check.py proves it equals the old
 * single file's cascade). Keep new rules in the file for their topic. */

/* =============================================================================
   TEACHER MOBILE DASHBOARD (tm-*)
   Extracted from mobile.php inline <style> block.
   All dashboard view styles, presenter remote styles, and modals.
   ============================================================================= */

*,*::before,*::after { box-sizing: border-box; margin: 0; padding: 0; }

:root {
  --bg: #080d18;
  /* 2026-09-20 (Joe: the glass frames "are all slightly too opaque and hard to
     identify"). These two were the LAST of this file's private copies of the
     shared design tokens -- white at 4% and 9%, which reads as nothing over the
     bookshelf backdrop. Every .tm-card, .tm-list-item and .tm-panel in the
     teacher, admin and institution shells is painted from them, so the sweep
     onto tokens.css left those three shells looking unchanged.
     Aliased now, exactly as --text1/2/3 below were aliased on 2026-09-03 for
     the identical complaint: one value to tune, in tokens.css, not two. */
  --surface: var(--card-bg);
  --border: var(--card-border);
  --accent: rgba(14,165,233,1);
  --accent-lo: rgba(14,165,233,0.14);
  --accent-hi: rgba(147,220,255,0.92);
  /* 2026-09-03 (Joe: "is it possible to refactor... create the shared
     source for all the files" -- the legibility bug had resurfaced
     because this file kept its OWN separate --text1/2/3 token trio,
     duplicating tokens.css's --text-primary/--text-secondary/--text-muted
     with different values, so fixing one system didn't fix the other
     (see tokens.css's own 2026-09-03 comment on the exact same complaint
     recurring). These now alias tokens.css's variables instead of
     hardcoding their own -- one value to tune from here on, not two.
     tokens.css loads before this file in mobile.php's <head>, so its
     :root declarations are already resolvable when these var() calls are
     used. --text-label has no --text* equivalent used elsewhere in this
     file, so it's left out of this alias set; nothing here reads it. */
  --text1: var(--text-primary);
  --text2: var(--text-secondary);
  --text3: var(--text-muted);
  --green: rgba(110,231,183,0.90);
  --green-lo: rgba(110,231,183,0.12);
  --amber: rgba(251,191,36,0.90);
  --red-lo: rgba(239,68,68,0.12);
}

html, body {
  height: 100%;
  background: var(--bg);
  color: var(--text1);
  font-family: "SF Pro Display", -apple-system, "Segoe UI", system-ui, sans-serif;
  overflow: hidden;
  -webkit-tap-highlight-color: transparent;
  -webkit-font-smoothing: antialiased;
}

/* ── Shell ── */
.tm-shell { display: flex; flex-direction: column; height: 100dvh; overflow: hidden; }

/* ── Top bar ── */
.tm-topbar { display: flex; align-items: center; gap: 10px; padding: 12px 16px 10px; border-bottom: 1px solid var(--border); background: rgba(8,13,24,0.96); flex-shrink: 0; backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); }
.tm-back-btn { background: none; border: none; color: var(--accent-hi); font-size: 22px; cursor: pointer; padding: 0 6px 0 0; line-height: 1; display: none; align-items: center; }
.tm-back-btn.visible { display: flex; }
/* 2026-09-22 (back-navigation rules): every back/close control is a 44x44
   touch target (it was ~22x22 here). The negative vertical margin keeps the
   topbar's height close to what it was; the left side is untouched, because
   #tmsGlobalSidebarToggle's 60px lane sits there on phones (viewUnify.css). */
.tm-back-btn { min-width: 44px; min-height: 44px; margin: -8px 0; justify-content: center; font-size: 30px; padding: 0; border-radius: 12px; }
.tm-back-btn:hover { background: rgba(255,255,255,0.06); }

/* The two shared controls, one meaning each:
     .nav-btn-back   ‹  top-left of a FULL-SCREEN surface: back one step,
                        nothing lost (a card, an editor, a builder, a preview)
     .nav-btn-close  ✕  top-right of a SMALL pop-up or sheet: dismiss it
   Ending a live session is never either of these -- it is a labelled red
   End button with its own confirmation. [data-nav-close] marks the control
   for shared/navStack.js. */
.nav-btn-back, .nav-btn-close {
  width: 44px; height: 44px; min-width: 44px; flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0; margin: -8px 0; border: none; border-radius: 12px;
  background: transparent; color: var(--text-secondary, rgba(255,255,255,0.78));
  line-height: 1; cursor: pointer; font-family: inherit;
}
.nav-btn-back  { font-size: 30px; margin-left: -10px; }
.nav-btn-close { font-size: 19px; margin-right: -10px; }
.nav-btn-back:hover, .nav-btn-close:hover { background: rgba(255,255,255,0.08); color: #fff; }
.nav-btn-back:focus-visible, .nav-btn-close:focus-visible, .tm-back-btn:focus-visible { outline: 2px solid var(--accent-hi); outline-offset: 2px; }
/* The builders' back (#mPresBack / #mActBack) keeps the framed square of
   their toolbar's other icon buttons (builder.css .m-pres-icon-btn), at 44px,
   and no negative margin inside that toolbar. */
.m-pres-icon-btn.nav-btn-back { margin: 0; border: 1px solid var(--card-border); background: var(--card-bg); color: #e0e0f0; font-size: 28px; border-radius: 10px; }
.m-pres-icon-btn.nav-btn-close { margin: 0; border: 1px solid var(--card-border); background: var(--card-bg); color: #e0e0f0; border-radius: 10px; }
.tm-topbar-title { flex: 1; font-size: 17px; font-weight: 700; color: var(--text1); letter-spacing: -0.3px; }
.tm-avatar { width: 32px; height: 32px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700; color: #fff; flex-shrink: 0; cursor: pointer; }

/* ── Content ── */
.tm-content { flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 20px 16px 100px; }
.tm-view { display: none; }
.tm-view.active { display: block; }

/* ── Home ── */
.tm-home-header { margin-bottom: 28px; }
.tm-greeting { font-size: 22px; font-weight: 800; color: var(--text1); letter-spacing: -0.5px; margin-bottom: 4px; }
.tm-greeting-sub { font-size: 13px; color: var(--text3); }

/* ── Icon grid ── */
.tm-icon-section-label { font-size: 10px; font-weight: 700; letter-spacing: 1px; text-transform: uppercase; color: var(--text3); margin-bottom: 16px; margin-top: 28px; }
.tm-icon-grid { display: grid; grid-template-columns: repeat(4,1fr); gap: 18px 8px; }
.tm-icon-item { display: flex; flex-direction: column; align-items: center; gap: 5px; cursor: pointer; -webkit-tap-highlight-color: transparent; }
.tm-icon-item:active .tm-icon-img { transform: scale(0.88); opacity: 0.80; }
.tm-icon-img { width: 64px; height: 64px; border-radius: 16px; display: flex; align-items: center; justify-content: center; font-size: 28px; transition: transform 0.12s, opacity 0.12s; position: relative; box-shadow: 0 4px 16px rgba(0,0,0,0.30); overflow: hidden; background: var(--accent-subtle); border: 1px solid rgba(14,165,233,0.22); flex-shrink: 0; }
.tm-icon-img img { width: 100%; height: 100%; object-fit: cover; border-radius: 15px; }
.tm-icon-placeholder { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; font-size: 26px; }
.tm-icon-label { font-size: 10px; font-weight: 500; color: var(--text-secondary); text-align: center; line-height: 1.2; max-width: 72px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tm-icon-badge { position: absolute; top: -4px; right: -4px; width: 18px; height: 18px; border-radius: 50%; background: #ef4444; border: 2px solid var(--bg); font-size: 9px; font-weight: 700; color: #fff; display: flex; align-items: center; justify-content: center; }
.tm-icon-cefr { position: absolute; bottom: 2px; right: 2px; font-size: 8px; font-weight: 700; padding: 1px 4px; border-radius: 4px; background: rgba(0,0,0,0.65); color: var(--text-secondary); }

/* ── Icon gradients ── */
.ic-schedule  { background: linear-gradient(135deg,#0ea5e9,#2563eb); }
.ic-activities{ background: linear-gradient(135deg,#f59e0b,#ef4444); }
.ic-present   { background: linear-gradient(135deg,#8b5cf6,#6366f1); }
.ic-create    { background: linear-gradient(135deg,#10b981,#059669); }
.ic-classes   { background: linear-gradient(135deg,#f97316,#ef4444); }
.ic-courses   { background: linear-gradient(135deg,#06b6d4,#0ea5e9); }
.ic-reports   { background: linear-gradient(135deg,#a855f7,#7c3aed); }

/* ── Live banner ── */
.tm-live-banner { display: none; background: var(--green-lo); border: 1px solid rgba(110,231,183,0.30); border-radius: 14px; padding: 12px 16px; margin-bottom: 20px; cursor: pointer; }
.tm-live-banner.visible { display: flex; align-items: center; gap: 12px; }
.tm-live-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--green); box-shadow: 0 0 0 4px rgba(110,231,183,0.20); flex-shrink: 0; animation: pulse 2s infinite; }
@keyframes pulse { 0%,100% { box-shadow: 0 0 0 4px rgba(110,231,183,0.20); } 50% { box-shadow: 0 0 0 8px rgba(110,231,183,0.08); } }

/* ── View header ── */
.tm-view-header { font-size: 20px; font-weight: 800; color: var(--text1); letter-spacing: -0.4px; margin-bottom: 6px; }
.tm-view-sub { font-size: 12px; color: var(--text3); margin-bottom: 20px; }

/* ── Pills ── */
.tm-pills { display: flex; gap: 6px; overflow-x: auto; scrollbar-width: none; margin-bottom: 16px; padding-bottom: 2px; }
.tm-pills::-webkit-scrollbar { display: none; }
.tm-pill { flex-shrink: 0; padding: 5px 12px; border-radius: 999px; font-size: 11px; font-weight: 600; border: 1px solid var(--border); background: var(--surface); color: var(--text2); cursor: pointer; transition: all 0.15s; white-space: nowrap; }
.tm-pill.active { background: var(--accent-lo); border-color: rgba(14,165,233,0.40); color: var(--accent-hi); }

/* ── List search (2026-09-24) ── one quick-filter box above the pills on
   Activities, Micro-Lessons, Conversations and Library; it narrows what the
   pills already show (tmSearchHit() in dashboardActivities.js). 16px on
   phones: iOS zooms the page into any smaller input on focus. */
.tm-search { position: relative; margin-bottom: 10px; }
.tm-search::before { content: "\1F50D"; position: absolute; left: 12px; top: 50%; transform: translateY(-50%); font-size: 14px; opacity: 0.45; pointer-events: none; }
.tm-search input { width: 100%; box-sizing: border-box; padding: 10px 12px 10px 36px; border-radius: 10px; border: 1px solid var(--border); background: var(--card-bg); color: var(--text1); font: inherit; font-size: 16px; outline: none; -webkit-appearance: none; appearance: none; color-scheme: dark; transition: border-color 0.15s; }
/* glassInputs.css (linked after this file) sets every input[type=search] to
   13px and 9px 10px padding with !important -- which would hide the text
   under the icon and let iOS zoom. Beaten here by specificity + !important. */
.tm-search input[type="search"] { font-size: 16px !important; padding: 10px 12px 10px 36px !important; }
.tm-search input::placeholder { color: var(--text2); opacity: 0.8; }
@media (min-width: 721px) { .tm-search { max-width: 420px; } }
@media (min-width: 721px) and (pointer: fine) { .tm-search input[type="search"] { font-size: 14px !important; } }

/* ── Activity card (full-screen detail) ── */
.tm-activity-card { display: none; position: fixed; inset: 0; z-index: 200; background: #080d18; flex-direction: column; overflow: hidden; }
.tm-activity-card.open { display: flex; }
.tm-activity-card-hero { width: 100%; height: 45vh; position: relative; flex-shrink: 0; }
.tm-activity-card-hero img { width: 100%; height: 100%; object-fit: cover; }
.tm-activity-card-hero-placeholder { width: 100%; height: 100%; background: linear-gradient(160deg,#1e2640 0%,#0d1220 100%); display: flex; align-items: center; justify-content: center; font-size: 64px; opacity: 0.40; }
.tm-activity-card-hero-gradient { position: absolute; inset: 0; background: linear-gradient(to bottom,rgba(8,13,24,0) 50%,rgba(8,13,24,1) 100%); }
.tm-activity-card-close { position: absolute; top: 14px; left: 14px; width: 32px; height: 32px; border-radius: 50%; background: rgba(0,0,0,0.50); backdrop-filter: blur(8px); border: none; color: #fff; font-size: 16px; cursor: pointer; display: flex; align-items: center; justify-content: center; z-index: 10; }
/* 2026-09-22: the card's way out -- a ‹ back (it was a 32px ✕, the same icon
   the Live Lesson Builder used to END a lesson). 44x44, over the hero image. */
.tm-activity-card-close { top: 12px; left: 12px; width: 44px; height: 44px; font-size: 30px; line-height: 1; padding: 0 0 3px; }
.tm-activity-card-body { flex: 1; padding: 20px 20px 32px; overflow-y: auto; display: flex; flex-direction: column; gap: 12px; }
.tm-activity-card-title { font-size: 22px; font-weight: 800; color: rgba(255,255,255,0.95); line-height: 1.2; }
.tm-activity-card-meta { display: flex; gap: 8px; flex-wrap: wrap; }
.tm-activity-card-tag { font-size: 11px; font-weight: 600; padding: 3px 10px; border-radius: 20px; background: var(--card-bg); color: rgba(255,255,255,0.85); border: 1px solid var(--card-border); }
.tm-activity-card-actions { margin-top: auto; display: flex; flex-direction: column; gap: 10px; }
.tm-activity-card-btn { width: 100%; padding: 14px; border-radius: 14px; border: none; font-size: 15px; font-weight: 700; cursor: pointer; text-align: center; }
.tm-activity-card-btn.primary { background: rgba(14,165,233,0.90); color: #fff; }
.tm-activity-card-btn.secondary { background: var(--card-bg); color: var(--text-secondary); border: 1px solid var(--card-border); }
.tm-activity-card-btn.danger { background: rgba(239,68,68,0.12); border: 1px solid rgba(239,68,68,0.35); color: rgba(252,165,165,0.90); }
.tm-activity-card-btn.danger:active { background: rgba(239,68,68,0.22); }

/* ── List items ── */
.tm-list { display: flex; flex-direction: column; gap: 8px; }
.tm-list-item { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 12px 14px; cursor: pointer; transition: background 0.15s, border-color 0.15s; display: flex; align-items: center; gap: 12px; }
.tm-list-item:active { background: var(--accent-lo); border-color: rgba(14,165,233,0.25); }
/* 2026-09 (Joe: Messages converted from a slide-in panel to a real
   tm-view, messages.js v2) -- unread accent + dot indicator, reusing
   this file's existing accent colour rather than inventing a new one.
   Generic enough that any other .tm-list-item consumer could use the
   same modifier later if an "unread"-style state comes up elsewhere. */
.tm-list-item--unread { background: var(--accent-subtle); border-color: rgba(14,165,233,0.28); }
.tm-msg-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); flex-shrink: 0; vertical-align: middle; margin-right: 2px; }
.tm-list-icon { width: 36px; height: 36px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 18px; flex-shrink: 0; }
.tm-list-body { flex: 1; min-width: 0; }
.tm-list-title { font-size: 13px; font-weight: 600; color: var(--text1); margin-bottom: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tm-list-meta { font-size: 11px; color: var(--text3); }
.tm-list-arrow { color: var(--text3); font-size: 16px; flex-shrink: 0; }

/* ── Toggle / form ── */
.tm-toggle { display: flex; background: var(--card-bg); border: 1px solid var(--border); border-radius: 12px; padding: 3px; margin-bottom: 20px; }
.tm-toggle-btn { flex: 1; padding: 9px; border-radius: 9px; border: none; background: transparent; color: var(--text2); font-size: 13px; font-weight: 600; cursor: pointer; transition: all 0.18s; }
.tm-toggle-btn.active { background: var(--accent-lo); color: var(--accent-hi); box-shadow: 0 2px 8px rgba(14,165,233,0.15); }
.tm-form-section { margin-bottom: 20px; }
.tm-form-label { font-size: 10px; font-weight: 700; letter-spacing: 0.8px; text-transform: uppercase; color: var(--text3); margin-bottom: 6px; display: block; }
.tm-input, .tm-textarea, .tm-select { width: 100%; padding: 11px 13px; border-radius: 10px; border: 1px solid var(--border); background: var(--card-bg); color: var(--text1); font-size: 14px; outline: none; transition: border-color 0.15s; color-scheme: dark; font-family: inherit; }
.tm-input:focus, .tm-textarea:focus, .tm-select:focus { border-color: rgba(14,165,233,0.50); }
.tm-textarea { resize: vertical; min-height: 80px; }
.tm-select option { background: #0f1e35; }
.tm-submit-btn { width: 100%; padding: 14px; border-radius: 12px; border: none; background: var(--accent); color: #fff; font-size: 15px; font-weight: 700; cursor: pointer; transition: opacity 0.15s; font-family: inherit; margin-top: 8px; }
.tm-submit-btn:active { opacity: 0.85; }
.tm-submit-btn:disabled { opacity: 0.45; cursor: not-allowed; }

/* ── Schedule ── */
.tm-schedule-day { margin-bottom: 20px; }
.tm-schedule-day-label { font-size: 10px; font-weight: 700; letter-spacing: 0.8px; text-transform: uppercase; color: var(--text2); margin-bottom: 8px; }
.tm-event { background: var(--accent-lo); border: 1px solid rgba(14,165,233,0.22); border-radius: 10px; padding: 10px 14px; margin-bottom: 6px; }
.tm-event-time { font-size: 11px; color: var(--accent-hi); font-weight: 600; margin-bottom: 2px; }
.tm-event-title { font-size: 13px; color: var(--text1); font-weight: 600; }
.tm-event-meta { font-size: 11px; color: var(--text2); margin-top: 2px; }

/* ── States ── */
.tm-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 35vh; gap: 10px; color: var(--text3); text-align: center; }
.tm-empty-icon { font-size: 40px; opacity: 0.30; }
.tm-empty-text { font-size: 13px; line-height: 1.6; max-width: 220px; }
.tm-loading { display: flex; align-items: center; justify-content: center; min-height: 30vh; color: var(--text3); font-size: 13px; }

/* ── Modals ── */
.tm-modal-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.60); z-index: 100; align-items: flex-end; }
.tm-modal-overlay.open { display: flex; }
.tm-modal { width: 100%; background: #0f1a2e; border-radius: 20px 20px 0 0; padding: 20px 16px 40px; border-top: 1px solid var(--border); animation: slideUp 0.25s cubic-bezier(0.32,0.72,0,1); }
@keyframes slideUp { from { transform: translateY(100%); } to { transform: translateY(0); } }
.tm-modal-title { font-size: 16px; font-weight: 700; color: var(--text1); margin-bottom: 14px; text-align: center; }
.tm-breadcrumb { font-size: 12px; color: var(--text3); margin-bottom: 16px; line-height: 1.5; }
.tm-breadcrumb a { color: var(--accent-hi); cursor: pointer; text-decoration: none; }


/* =============================================================================
   DESKTOP RESPONSIVE LAYER + MESSAGES PANEL  (v14)
   Sidebar shell (tms-*), slide-in messages panel (msg-*), and the
   min-width:721px desktop breakpoint. Mobile behaviour below 721px is
   unchanged except for the new Messages entry points.
   ============================================================================= */

/* ── Shell becomes a row; main column wraps topbar + content ── */
.tm-shell { flex-direction: row; }
.tms-main { flex: 1; display: flex; flex-direction: column; min-width: 0; overflow: hidden; }

/* ── Sidebar (hidden on mobile) ── */
.tms-sidebar { display: none; width: 220px; flex-shrink: 0; flex-direction: column; background: rgba(10,15,28,0.97); border-right: 1px solid var(--border); overflow: hidden; }
.tms-brand { padding: 18px 16px 14px; border-bottom: 1px solid var(--border); flex-shrink: 0; }
.tms-brand-sub { font-size: 10px; color: var(--text3); margin-top: 2px; }
.tms-nav { flex: 1; overflow-y: auto; padding: 6px 8px; }
.tms-nav-section { font-size: 9px; font-weight: 700; letter-spacing: 1.2px; text-transform: uppercase; color: var(--text3); padding: 12px 8px 5px; }
.tms-nav-btn { display: flex; align-items: center; gap: 9px; width: 100%; padding: 9px 10px; border-radius: 9px; border: none; background: transparent; color: var(--text2); font-size: 13px; font-weight: 500; cursor: pointer; text-align: left; transition: background 0.12s, color 0.12s; font-family: inherit; position: relative; }
.tms-nav-btn:hover { background: var(--card-bg); color: var(--text1); }
.tms-nav-btn.active { background: var(--accent-lo); color: var(--accent-hi); }
.tms-nav-icon { width: 18px; text-align: center; flex-shrink: 0; font-size: 15px; }
.tms-badge { margin-left: auto; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px; background: #ef4444; color: #fff; font-size: 10px; font-weight: 700; display: none; align-items: center; justify-content: center; }
.tms-badge.visible { display: inline-flex; }
.tms-footer { padding: 12px 14px; border-top: 1px solid var(--border); display: flex; align-items: center; gap: 9px; flex-shrink: 0; }
.tms-footer-name { flex: 1; font-size: 12px; color: var(--text2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── Messages icon gradient + count badge ── */
.ic-messages { background: linear-gradient(135deg,#5ad469,#2eab48); }
.msg-count-badge { position: absolute; top: -4px; right: -4px; min-width: 18px; height: 18px; padding: 0 4px; border-radius: 9px; background: #ef4444; border: 2px solid var(--bg); font-size: 9px; font-weight: 700; color: #fff; display: none; align-items: center; justify-content: center; }
.msg-count-badge.visible { display: flex; }

/* ── Messages slide-in panel ── */
/* 2026-09 SUPERSEDED (Joe: "Messages is still rendering as a slide in
   panel" -- converted to a real tm-view, id=view-messages, see
   mobile.php and messages.js v2). This whole .msg-panel/.msg-backdrop/
   .msg-item family is dead as of that change -- left in place per house
   convention rather than deleted, since .msg-back-btn/.msg-detail-body
   below are still reused by the new view's detail/compose screens. */
.msg-detail-body { font-size: 14px; color: var(--text1); line-height: 1.7; white-space: pre-wrap; padding: 4px 2px 12px; }
.msg-back-btn { align-self: flex-start; padding: 6px 12px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); color: var(--text2); font-size: 12px; font-weight: 600; cursor: pointer; font-family: inherit; }
.msg-back-btn:active { background: var(--card-bg); }

/* Message translate toggle (2026-09-16, Addendum 83). Same three rules as
   student_shell.css's .sh-msg-translate-* and admin.css's .admin-msg-translate-*,
   under this shell's prefix; markup comes from the one shared module,
   assets/js/shared/messageTranslate.js. Uses this shell's own tokens rather
   than the student shell's literal rgba() values, so it follows the teacher
   theme. */
.tm-msg-translate-btn { margin: 0 0 8px; padding: 4px 10px; border-radius: 7px; border: 1px solid var(--border); background: var(--surface); color: var(--text2); font-size: 11px; font-weight: 600; cursor: pointer; font-family: inherit; line-height: 1.5; }
.tm-msg-translate-btn:active:not(:disabled) { background: var(--card-bg); }
.tm-msg-translate-btn:disabled { opacity: 0.6; cursor: default; }
.tm-msg-translate-note { font-size: 11px; color: var(--text3); font-style: italic; margin-bottom: 8px; }

/* ── Desktop breakpoint ── */
@media (min-width: 721px) {
  .tms-sidebar { display: flex; }
  .tm-content { padding: 28px 32px 60px; }
  /* 2026-09-19 (refactor audit; Joe: keep today's widths, 1600px default).
     THE one width rule for shell views. It was 860px, but three later id
     lists (two at 1200px, then v17's 1600px) overrode it for every real view,
     and any view left off those lists fell back to a narrow centred 860px
     column -- a bug fixed three separate times (micro-lessons/guide, the
     institution views, Messages). Those lists are gone; every view is 1600px
     unless it opts out below (Schedule; Vocabulary in viewUnify.css; the
     admin shell's own two rules in adminShell.css). A NEW view needs nothing.
     Measured before/after in all three shells at 400/1024/1440/1920px:
     identical. See Handover/tm_view_widths_side_by_side.md. */
  .tm-view { max-width: 1600px; margin: 0 auto; }
  /* 2026-07-23 (Joe): "week view should be fluid and fit within the page
     width" -- at the then-standard 860px .tm-view cap (1600px since
     2026-09-19; Schedule still opts out entirely), 7 week columns squeeze
     to ~110px each. Schedule is the one view whose content (week/month
     grids) genuinely benefits from the full content-pane width, so it
     alone opts out of the shared cap; Classes grid just gets extra
     breathing room as a side effect, which is harmless. */
  #view-schedule.tm-view { max-width: none; }
  .tm-icon-grid { grid-template-columns: repeat(5, 96px); justify-content: start; gap: 20px 18px; }
  .tm-topbar { padding: 12px 24px 10px; }

  /* Bottom sheets become centred modals */
  .tm-modal-overlay { align-items: center; justify-content: center; padding: 24px; }
  .tm-modal { max-width: 560px; border-radius: 20px; border: 1px solid var(--border); animation: none; }
  .tm-modal-handle { display: none; }

  /* Full-screen cards become centred cards */
  .tm-activity-card { top: 50%; left: 50%; right: auto; bottom: auto; transform: translate(-50%,-50%); width: min(680px, 92vw); height: min(720px, 85vh); border-radius: 20px; border: 1px solid var(--border); box-shadow: 0 0 0 100vmax rgba(0,0,0,0.55), 0 30px 80px rgba(0,0,0,0.55); }
  .tm-activity-card-hero { height: 260px; }

  /* Messages panel: fixed-width drawer */
}


/* =============================================================================
   DESKTOP CARD DECKS  (v15)
   On desktop (>720px) all iOS icon-grid menus render as student-portal style
   card decks: full-width hero (image or gradient tile), title footer, badges
   pinned to the hero's top-right. Covers tm-icon-* grids (home, activities)
   and the Library's tl-* grid (styles injected by topicLibrary.js -- overridden
   here via higher-specificity .tm-content descendant selectors).
   Mobile below 721px is unchanged.
   ============================================================================= */
@media (min-width: 721px) {

  /* 2026-09-19: an id list setting max-width:1200px stood here (its history:
     2026-08-26, views missing from it rendered as a narrow 860px column). It
     never took effect -- v17's 1600px list below always overrode it -- and
     the .tm-view default is 1600px now, so it was removed. */

  /* ── tm-icon-* grids become card decks ── */
  .tm-icon-grid { grid-template-columns: repeat(auto-fill, minmax(190px, var(--card-width, 220px))); gap: 16px; justify-content: stretch; }
  .tm-icon-item { flex-direction: column; align-items: stretch; gap: 0; background: var(--surface); border: 1px solid var(--border); border-radius: 14px; overflow: hidden; transition: transform 0.12s, border-color 0.15s; }
  .tm-icon-item:hover { border-color: rgba(14,165,233,0.35); transform: translateY(-2px); }
  .tm-icon-item:active .tm-icon-img { transform: none; opacity: 0.85; }
  .tm-icon-img { width: 100%; height: 130px; border-radius: 0; font-size: 44px; box-shadow: none; border: none; }
  .tm-icon-img img { border-radius: 0; }
  .tm-icon-placeholder { font-size: 44px; }
  .tm-icon-label { font-size: 13px; font-weight: 700; color: var(--text1); text-align: left; padding: 11px 13px 12px; max-width: none; white-space: normal; line-height: 1.3; }
  .tm-icon-title-text { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
  .tm-icon-type { display: block; font-size: 11px; font-weight: 600; color: var(--accent-hi); margin-top: 5px; }
  .tm-icon-badge { top: 8px; right: 8px; width: 20px; height: 20px; font-size: 10px; }
  .msg-count-badge { top: 8px; right: 8px; }
  .tm-icon-cefr { bottom: auto; top: 8px; right: 8px; font-size: 9px; }
  /* When a badge and CEFR tag share a hero, badge sits above */
  .tm-icon-badge, .msg-count-badge { z-index: 2; }

  /* ── Library tl-* grid becomes a card deck (overrides injected styles) ── */
  .tm-content .tl-grid { grid-template-columns: repeat(auto-fill, minmax(190px, var(--card-width, 220px))); gap: 16px; }
  .tm-content .tl-card { align-items: stretch; gap: 0; background: var(--surface); border: 1px solid var(--border); border-radius: 14px; overflow: hidden; transition: transform 0.12s, border-color 0.15s; }
  .tm-content .tl-card:hover { border-color: rgba(14,165,233,0.35); transform: translateY(-2px); }
  .tm-content .tl-card:active .tl-card-icon { transform: none; opacity: 0.85; }
  .tm-content .tl-card-icon { width: 100%; height: 130px; border-radius: 0; font-size: 44px; box-shadow: none; }
  .tm-content .tl-card-cefr { bottom: auto; top: 8px; right: 8px; font-size: 9px; }
  .tm-content .tl-card-label { font-size: 13px; font-weight: 700; color: var(--text1); text-align: left; padding: 11px 13px 12px; max-width: none; white-space: normal; line-height: 1.3; }

  /* Library detail bottom-sheet becomes a centred modal */
  body .tl-sheet-overlay { align-items: center; justify-content: center; padding: 24px; }
  body .tl-sheet { max-width: 560px; border-radius: 20px; border: 1px solid var(--card-border); animation: none; }
  body .tl-sheet-handle { display: none; }
}


/* =============================================================================
   DESKTOP LIST CARD DECKS  (v16)
   Extends the v15 card-deck treatment to tm-list row menus (presentations
   course tree, classes, courses, reports). Scoped to .tm-view so lists inside
   modals, editors, and overlays keep their row layout. Mobile unchanged.
   ============================================================================= */
@media (min-width: 721px) {

  /* 2026-09-19: a second 1200px id list stood here (added 2026-09 when the
     institution views, missing from the first one, fell back to 860px). Also
     always overridden by v17's 1600px list; removed with the new .tm-view
     default -- see the desktop breakpoint block above. */

  .tm-view .tm-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, var(--card-width, 220px))); gap: 16px; }
  .tm-view .tm-list-item { flex-direction: column; align-items: stretch; gap: 0; padding: 0; border-radius: 14px; overflow: hidden; transition: transform 0.12s, border-color 0.15s; }
  .tm-view .tm-list-item:hover { border-color: rgba(14,165,233,0.35); transform: translateY(-2px); background: var(--surface); }
  .tm-view .tm-list-item:active { background: var(--accent-lo); }
  .tm-view .tm-list-icon { width: 100%; height: 110px; border-radius: 0; font-size: 40px; flex-shrink: 0; }
  .tm-view .tm-list-body { padding: 11px 13px 12px; }
  .tm-view .tm-list-title { white-space: normal; font-size: 13px; line-height: 1.3; }
  .tm-view .tm-list-meta { margin-top: 3px; }
  .tm-view .tm-list-arrow { display: none; }
}


/* =============================================================================
   DESKTOP DECK SIZING + TOPBAR  (v17)
   Deck grids match the student portal exactly: minmax(180px,1fr) fluid
   columns, gap 14, heroes at 16/7 aspect ratio. Desktop topbar hidden -- brand, avatar,
   profile, and Sign out all live in the sidebar, matching the student shell.
   Mobile unchanged.

   2026-08-14 (Joe: "impose a max card width to all card styling... this
   should apply site wide" -- flagged from the Schedule tab's class cards
   stretching to fill a near-empty row). The "1fr" upper bound on every
   minmax() card track meant a track (and the card filling it) could grow
   without limit whenever a row wasn't completely full -- usually invisible
   here because Classes/Courses/Reports/Presentations tend to have enough
   cards to pack full rows, but glaring on Schedule (schedulePlanner.js's
   .sp-grid, fixed below) which often shows just 1-2 classes. Swapped the
   "1fr" ceiling for tokens.css's existing (previously unused/orphaned)
   --card-width: 220px everywhere a real content card is laid out in a
   fluid grid -- tracks still shrink down to their existing minmax minimum
   on narrow windows, they just stop growing past 220px once there's room
   to spare. Minimums themselves are untouched (still 180/190px depending
   on grid) so nothing reflows differently, cards just stop over-stretching.
   Fallback (`var(--card-width, 220px)`) covers any page that loads this
   rule without tokens.css. */
@media (min-width: 721px) {

  .tm-topbar { display: none; }

  /* 2026-09-19: v17's 1600px id list (every shell view) lived here. It was the
     rule that actually won; its value is now the .tm-view default itself, in
     the desktop breakpoint block above, so no view has to be listed. */

  .tm-icon-grid,
  .tm-view .tm-list,
  .tm-content .tl-grid { grid-template-columns: repeat(auto-fill, minmax(180px, var(--card-width, 220px))); gap: 14px; }

  /* Heroes scale with card width, exactly like the student .sh-card-img */
  .tm-icon-img,
  .tm-content .tl-card-icon { height: auto; aspect-ratio: 16/7; }
  .tm-view .tm-list-icon { height: auto; aspect-ratio: 16/7; }

  /* List-card hero band: presentations/classes/courses rows carry their CEFR
     pill or icon in an unclassed wrapper div, not .tm-list-icon. Give any
     leading non-body child the full-width hero treatment. */
  .tm-view .tm-list-item > :first-child:not(.tm-list-body) { width: 100%; height: auto; aspect-ratio: 16/7; display: flex; align-items: center; justify-content: center; background: var(--card-bg); flex-shrink: 0; }
  .tm-view .tm-list-item > :first-child:not(.tm-list-body) svg { transform: scale(1.3); }
}


/* =============================================================================
   DECK CARD POLISH  (v19)
   Reports (and all list menus) keep the same card-deck styling as the rest of
   the dashboard. Enlarge the small avatars/icons/emoji sitting inside the
   16/7 hero bands so tiles read as deliberate cards rather than empty space.
   ============================================================================= */
@media (min-width: 721px) {
  .tm-view .tm-list-icon { font-size: 40px; }
  .tm-view .tm-list-item > :first-child:not(.tm-list-body) > div { transform: scale(1.6); }
}

/* Bug fix (all widths): some render paths (reports) leave the tm-loading
   class on the content container after data loads; its flex centring then
   shrinks the rendered list to content width (single squeezed column).
   Neutralise whenever real content is present. */
.tm-loading:has(.tm-list),
.tm-loading:has(.tm-icon-grid) { display: block; min-height: 0; }


/* =============================================================================
   MOBILE iOS TILES FOR LIST MENUS  (v23)
   Below 721px, the tm-list menus in Classes, Courses and Reports render as
   4-across iOS icon tiles (matching the home grid and the Schedule view).
   Library already tiles via topicLibrary's own styles. Desktop deck cards
   (v16/v17) are untouched.
   ============================================================================= */
@media (max-width: 720px) {
  #view-classes .tm-list, #view-courses .tm-list, #view-reports .tm-list, #view-presentations .tm-list {
    display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px 8px;
  }
  #view-classes .tm-list-item, #view-courses .tm-list-item, #view-reports .tm-list-item, #view-presentations .tm-list-item {
    flex-direction: column; align-items: center; gap: 6px;
    background: none; border: none; padding: 0; margin: 0; overflow: visible;
  }
  #view-classes .tm-list-item > :first-child:not(.tm-list-body),
  #view-courses .tm-list-item > :first-child:not(.tm-list-body),
  #view-reports .tm-list-item > :first-child:not(.tm-list-body),
  #view-presentations .tm-list-item > :first-child:not(.tm-list-body),
  #view-classes .tm-list-icon, #view-courses .tm-list-icon, #view-reports .tm-list-icon, #view-presentations .tm-list-icon {
    width: 64px; height: 64px; border-radius: 16px; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    font-size: 26px; box-shadow: 0 4px 10px rgba(0,0,0,0.35);
    background: var(--surface2);
  }
  #view-classes .tm-list-body, #view-courses .tm-list-body, #view-reports .tm-list-body, #view-presentations .tm-list-body {
    text-align: center; padding: 0; min-width: 0; width: 100%;
  }
  #view-classes .tm-list-title, #view-courses .tm-list-title, #view-reports .tm-list-title, #view-presentations .tm-list-title {
    font-size: 10px; font-weight: 500; color: var(--text-secondary);
    line-height: 1.2; max-width: 74px; margin: 0 auto;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  #view-classes .tm-list-meta, #view-courses .tm-list-meta, #view-reports .tm-list-meta, #view-presentations .tm-list-meta,
  #view-classes .tm-list-arrow, #view-courses .tm-list-arrow, #view-reports .tm-list-arrow, #view-presentations .tm-list-arrow {
    display: none;
  }

  /* Library: normalise topicLibrary's injected grid to the same 4-across 64px tiles */
  .tm-content .tl-grid { grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); gap: 18px 8px; }
  .tm-content .tl-card { gap: 6px; }
  .tm-content .tl-card-icon { width: 64px; height: 64px; border-radius: 16px; font-size: 26px; box-shadow: 0 4px 10px rgba(0,0,0,0.35); }
  .tm-content .tl-card-label { font-size: 10px; font-weight: 500; color: var(--text-secondary); line-height: 1.2; max-width: 74px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
}

/* 2026-09 (Joe: "apply this new layout and styling to the teacher and
admin flows" -- porting the student Activities/micro-lessons wider-card
treatment, see student_shell.css's own matching .sh-icon-grid-cards
comment). .tm-icon-grid-cards is an opt-in modifier added alongside
tm-icon-grid in dashboard.js's tmRenderActivities()/tmRenderMicroLessons()
ONLY -- Home's own tm-icon-grid nav tiles keep the original compact
4-column tile untouched (pure navigation, not content to evaluate
card-by-card). Teacher's desktop card-deck treatment (further down this
file) never had a runner-type line at all (unlike student's, which did)
-- .tm-icon-type/.tm-icon-title-text are genuinely new here, added to
BOTH this mobile block and the desktop block below, not just extended
down from an existing desktop rule. */
@media (max-width: 720px) {
.tm-icon-grid.tm-icon-grid-cards { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 12px 8px; }
.tm-icon-grid-cards .tm-icon-item { flex-direction: column; align-items: stretch; gap: 0; background: var(--surface); border: 1px solid var(--border); border-radius: 14px; overflow: hidden; }
.tm-icon-grid-cards .tm-icon-item:active .tm-icon-img { transform: none; opacity: 0.85; }
.tm-icon-grid-cards .tm-icon-img { width: 100%; height: auto; aspect-ratio: 16/7; border-radius: 0; font-size: 30px; box-shadow: none; border: none; }
.tm-icon-grid-cards .tm-icon-img img { border-radius: 0; }
.tm-icon-grid-cards .tm-icon-label { font-size: 12px; font-weight: 700; color: var(--text1); text-align: left; padding: 9px 10px 10px; max-width: none; white-space: normal; line-height: 1.3; }
.tm-icon-grid-cards .tm-icon-cefr { bottom: auto; top: 8px; right: 8px; }
.tm-icon-grid-cards .tm-icon-badge { top: 8px; right: 8px; }
.tm-icon-grid-cards .tm-icon-title-text { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.tm-icon-grid-cards .tm-icon-type { display: block; font-size: 10.5px; font-weight: 600; color: var(--accent-hi); margin-top: 4px; }
}

/* 2026-09-21: the BETA course bar (betaTester.js). glassSelect.js wraps its
   <select> in a span.gs-wrap that is display:block with no flex sizing, so the
   bar's list grew to the full course name (611px on a 375px phone). Sized here,
   not inline: the wrapper is created after the bar is drawn. */
#betaCourseBar > .gs-wrap { flex: 1 1 220px; min-width: 0; max-width: 480px; }
#betaCourseBar .gs-label { min-width: 0; }
