/* ==========================================================================
   365-Day Habit Building Workbook / 学霸天书-习惯养成手册
   Plain CSS, no frameworks. Mobile first.
   ========================================================================== */

:root {
  /* --- brand ------------------------------------------------------------ */
  --primary: #f70e0e;
  --primary-dark: #c00909;
  --primary-darker: #8f0606;
  --primary-light: #ff5a4d;
  --primary-soft: #fff1f0;
  --primary-soft-2: #ffe0dd;
  --primary-border: #ffc4bf;
  --gradient: linear-gradient(135deg, #f70e0e 0%, #ff6a2b 100%);
  --gradient-soft: linear-gradient(135deg, #fff1f0 0%, #fff6ed 100%);
  --gradient-dark: linear-gradient(135deg, #8f0606 0%, #d63a12 100%);

  /* --- neutrals --------------------------------------------------------- */
  --bg: #fbf7f6;
  --surface: #ffffff;
  --surface-2: #faf7f7;
  --border: #ece4e3;
  --border-strong: #ddd0ce;
  --text: #241a19;
  --text-muted: #7b6b69;

  /* --- status ----------------------------------------------------------- */
  --success: #16a34a;
  --success-soft: #ecfdf5;
  --success-border: #a7f3d0;
  --danger: #dc2626;
  --danger-soft: #fef2f2;
  --danger-border: #fecaca;
  --warning: #d97706;
  --warning-soft: #fffbeb;
  --warning-border: #fde68a;
  --info: #2563eb;
  --info-soft: #eff6ff;
  --info-border: #bfdbfe;

  --radius: 16px;
  --radius-sm: 10px;
  --radius-lg: 22px;
  --shadow-sm: 0 1px 2px rgba(60, 20, 18, .06), 0 2px 8px rgba(60, 20, 18, .04);
  --shadow: 0 2px 4px rgba(60, 20, 18, .06), 0 12px 28px rgba(60, 20, 18, .08);
  --shadow-lg: 0 8px 20px rgba(60, 20, 18, .10), 0 24px 48px rgba(60, 20, 18, .12);
  --shadow-brand: 0 6px 18px rgba(247, 14, 14, .28);
}

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
               "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑",
               "Helvetica Neue", Arial, sans-serif;
  font-size: 16px;
  line-height: 1.6;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background-color: var(--bg);
  /* soft brand glow in the corners */
  background-image:
    radial-gradient(circle at 0% 0%, rgba(247, 14, 14, .07), transparent 42%),
    radial-gradient(circle at 100% 0%, rgba(255, 106, 43, .07), transparent 42%);
  background-attachment: fixed;
}

/* Chinese renders better with a touch more line height */
html[data-lang="zh"] body { line-height: 1.75; }

a { color: var(--primary-dark); text-decoration: none; }
a:hover { text-decoration: underline; }

h1, h2, h3 { line-height: 1.25; margin: 0 0 .5rem; letter-spacing: -.01em; }
h1 { font-size: 1.7rem; font-weight: 800; }
h2 { font-size: 1.2rem; font-weight: 700; }
h3 { font-size: 1.02rem; font-weight: 700; }

p { margin: 0 0 1rem; }

::selection { background: var(--primary); color: #fff; }

/* ---------------------------------------------------------------- topbar - */
.topbar {
  background: rgba(255, 255, 255, .92);
  backdrop-filter: saturate(180%) blur(10px);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 40;
  box-shadow: 0 1px 0 rgba(60, 20, 18, .03);
}
.topbar::before {
  content: "";
  display: block;
  height: 3px;
  background: var(--gradient);
}

.topbar-inner {
  max-width: 1180px;
  margin: 0 auto;
  padding: .7rem 1rem;
  display: flex;
  align-items: center;
  gap: .75rem;
  flex-wrap: wrap;
}

.brand {
  display: flex;
  align-items: center;
  gap: .6rem;
  color: var(--text);
  margin-right: auto;
  min-width: 0;
}
.brand:hover { text-decoration: none; }

.brand-mark {
  width: 40px; height: 40px;
  flex: none;
  border-radius: 12px;
  background: var(--gradient);
  color: #fff;
  display: grid;
  place-items: center;
  font-size: .92rem;
  font-weight: 800;
  letter-spacing: -.5px;
  box-shadow: var(--shadow-brand);
}

.brand-text { display: flex; flex-direction: column; min-width: 0; }
.brand-name {
  font-weight: 800;
  font-size: .98rem;
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 44vw;
}
.brand-tag {
  font-size: .7rem;
  color: var(--text-muted);
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 44vw;
}

.topbar-right { display: flex; align-items: center; gap: .5rem; }

.lang-switch {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  padding: .42rem .7rem;
  border: 1px solid var(--primary-border);
  border-radius: 999px;
  background: var(--primary-soft);
  color: var(--primary-dark);
  font-size: .82rem;
  font-weight: 700;
  white-space: nowrap;
  transition: background .15s ease, transform .15s ease;
}
.lang-switch:hover {
  text-decoration: none;
  background: var(--primary-soft-2);
  transform: translateY(-1px);
}

.nav-toggle {
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 42px; height: 38px;
  padding: 0 9px;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.nav-toggle span {
  display: block;
  height: 2px;
  background: var(--text);
  border-radius: 2px;
  transition: transform .2s ease, opacity .2s ease;
}
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

.nav {
  display: none;
  width: 100%;
  flex-direction: column;
  gap: .15rem;
  padding-top: .6rem;
}
.nav.is-open { display: flex; animation: slideDown .22s ease; }

@keyframes slideDown {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}

.nav-link {
  display: flex;
  align-items: center;
  white-space: nowrap;
  gap: .5rem;
  padding: .58rem .75rem;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  font-weight: 600;
  font-size: .93rem;
  transition: background .15s ease, color .15s ease;
}
.nav-link:hover { background: var(--surface-2); color: var(--text); text-decoration: none; }
.nav-link.is-active { background: var(--primary-soft); color: var(--primary-dark); }
.nav-ico { font-size: .95rem; line-height: 1; }

/* Who you are. It sits in the top-right group beside the language switch,
   not among the nav links - it is not a destination. */
.nav-user {
  display: flex;
  align-items: center;
  gap: .5rem;
  min-width: 0;
}
.nav-user .nav-link { flex: 1; min-width: 0; }
.nav-username { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.avatar {
  width: 28px; height: 28px;
  flex: none;
  border-radius: 50%;
  background: var(--gradient);
  color: #fff;
  display: grid;
  place-items: center;
  font-size: .8rem;
  font-weight: 800;
}

.role-chip {
  display: inline-block;
  padding: .12rem .5rem;
  border-radius: 999px;
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .3px;
  vertical-align: middle;
  white-space: nowrap;
}
.role-teacher { background: #fff3d6; color: #8a5a00; }
.role-student { background: var(--primary-soft-2); color: var(--primary-darker); }

@media (min-width: 900px) {
  .nav-toggle { display: none; }

  /* Three parts on one line: who we are, where you can go, and who you are.
     The nav takes the space that is left and wraps inside itself, so the
     teacher's nine destinations can never push the account group off the
     edge the way they once pushed out "Log out". */
  .nav {
    display: flex;
    flex-direction: row;
    align-items: center;
    flex-wrap: wrap;
    justify-content: center;
    /* A zero basis is the whole trick: sized from its content the nav is
       wider than the bar and drops to its own line, taking the account
       group with it. From zero it simply fills what is left and wraps its
       own links instead. */
    flex: 1 1 0;
    min-width: 0;
    padding-top: 0;
    gap: .2rem;
    order: 3;
  }
  .brand { flex: 0 1 auto; min-width: 0; }

  /* Language and account belong together, at the far right. */
  .topbar-right {
    order: 4;
    flex: none;
    gap: .6rem;
    padding-left: .85rem;
    border-left: 1px solid var(--border);
  }
  .nav-user {
    margin: 0;
    padding: 0;
    border-top: 0;
    border-left: 0;
  }
  .nav-username { max-width: 130px; }
  .brand-name, .brand-tag { max-width: none; }
}

/* ------------------------------------------------------------------ page - */
.page {
  flex: 1;
  width: 100%;
  max-width: 1180px;
  margin: 0 auto;
  padding: 1.5rem 1rem 3.5rem;
  animation: fadeIn .3s ease;
}

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* The page heading doubles as a small hero banner. */
.page-head {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: .85rem;
  margin-bottom: 1.4rem;
  padding: 1.3rem 1.4rem;
  border-radius: var(--radius-lg);
  background: var(--gradient);
  color: #fff;
  box-shadow: var(--shadow-brand);
}
.page-head::after {
  content: "";
  position: absolute;
  right: -50px; top: -70px;
  width: 210px; height: 210px;
  border-radius: 50%;
  background: rgba(255, 255, 255, .13);
  pointer-events: none;
}
.page-head::before {
  content: "";
  position: absolute;
  right: 60px; bottom: -90px;
  width: 150px; height: 150px;
  border-radius: 50%;
  background: rgba(255, 255, 255, .09);
  pointer-events: none;
}
.page-head > * { position: relative; z-index: 1; }
.page-head h1 { color: #fff; margin-bottom: .15rem; }
.page-head p { margin: 0; color: rgba(255, 255, 255, .92); font-size: .93rem; }
.page-head .badge { background: rgba(255,255,255,.2); border-color: rgba(255,255,255,.35); color: #fff; }
.page-head a.btn:not(.btn-ghost) {
  background: #fff;
  color: var(--primary-dark);
  box-shadow: 0 4px 12px rgba(0, 0, 0, .12);
}
.page-head a.btn:not(.btn-ghost):hover { background: #fff5f4; color: var(--primary-darker); }
.page-head .btn-ghost { border-color: rgba(255,255,255,.55); color: #fff; }
.page-head .btn-ghost:hover { background: rgba(255,255,255,.15); color: #fff; }

.site-footer {
  border-top: 1px solid var(--border);
  background: var(--surface);
  text-align: center;
  color: var(--text-muted);
  font-size: .85rem;
}
.site-footer p { margin: 0; padding: 1.1rem; }

/* ------------------------------------------------------------------ card - */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  padding: 1.3rem;
  margin-bottom: 1.25rem;
  transition: box-shadow .2s ease, transform .2s ease;
}
.card:hover { box-shadow: var(--shadow); }

.card-header {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  align-items: center;
  justify-content: space-between;
  margin: -1.3rem -1.3rem 1.1rem;
  padding: .95rem 1.3rem;
  border-bottom: 1px solid var(--border);
  background: var(--gradient-soft);
  border-radius: var(--radius) var(--radius) 0 0;
}
.card-header h2, .card-header h3 { margin: 0; }
.card-header h2::before, .card-header h3::before {
  content: "";
  display: inline-block;
  width: 4px; height: 15px;
  margin-right: .5rem;
  vertical-align: -2px;
  border-radius: 3px;
  background: var(--gradient);
}
.card-tight { padding: 0; }
.card-tight .card-header { margin: 0; border-radius: var(--radius) var(--radius) 0 0; }
.card-pad { padding: 1.1rem 1.3rem; }

/* ------------------------------------------------------------------ auth - */
.auth-wrap {
  min-height: calc(100vh - 220px);
  display: grid;
  place-items: center;
  padding: 1.5rem 1rem;
}
.auth-card {
  width: 100%;
  max-width: 440px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: 2rem 1.75rem;
  position: relative;
  overflow: hidden;
}
.auth-card::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 5px;
  background: var(--gradient);
}
.auth-logo {
  width: 62px; height: 62px;
  margin: .25rem auto 1rem;
  border-radius: 18px;
  background: var(--gradient);
  color: #fff;
  display: grid;
  place-items: center;
  font-size: 1.25rem;
  font-weight: 800;
  box-shadow: var(--shadow-brand);
}
.auth-card h1 { text-align: center; font-size: 1.4rem; }
.auth-sub { text-align: center; color: var(--text-muted); margin-bottom: 1.5rem; font-size: .92rem; }
.auth-alt { text-align: center; margin: 1.3rem 0 0; font-size: .9rem; color: var(--text-muted); }
.auth-alt a { font-weight: 700; }

/* ----------------------------------------------------------------- forms - */
.field { margin-bottom: 1rem; }
.field label {
  display: block;
  font-size: .86rem;
  font-weight: 700;
  margin-bottom: .38rem;
}
.field .hint { display: block; font-weight: 400; color: var(--text-muted); font-size: .79rem; margin-top: .32rem; }

input[type="text"],
input[type="email"],
input[type="tel"],
input[type="password"],
input[type="date"],
input[type="url"],
input[type="datetime-local"],
input[type="file"],
select,
textarea {
  width: 100%;
  padding: .68rem .8rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: #fff;
  color: var(--text);
  font: inherit;
  font-size: .95rem;
  transition: border-color .15s ease, box-shadow .15s ease;
}
textarea { resize: vertical; min-height: 96px; }

input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(247, 14, 14, .14);
}
input:disabled { background: var(--surface-2); color: var(--text-muted); }

input[type="file"] {
  padding: .55rem;
  background: var(--primary-soft);
  border-style: dashed;
  border-color: var(--primary-border);
  cursor: pointer;
}
input[type="file"]:hover { background: var(--primary-soft-2); }

select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%237b6b69' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right .8rem center;
  padding-right: 2.2rem;
}

/* password field with a show/hide button */
.password-wrap { position: relative; }
.password-wrap input { padding-right: 3.1rem; }
.password-toggle {
  position: absolute;
  top: 50%;
  right: .4rem;
  transform: translateY(-50%);
  height: 32px;
  padding: 0 .55rem;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: .25rem;
  font: inherit;
  font-size: .78rem;
  font-weight: 600;
}
.password-toggle:hover { background: var(--primary-soft); color: var(--primary-dark); }
.password-toggle svg { display: block; }

.form-grid { display: grid; grid-template-columns: 1fr; gap: 0 1rem; }
@media (min-width: 720px) {
  .form-grid.cols-2 { grid-template-columns: 1fr 1fr; }
  .form-grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
  .form-grid.cols-4 { grid-template-columns: repeat(4, 1fr); }
  .form-grid .span-2 { grid-column: span 2; }
}

.form-actions {
  display: flex;
  flex-wrap: wrap;
  gap: .6rem;
  align-items: center;
  margin-top: .5rem;
}

/* --------------------------------------------------------------- buttons - */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .4rem;
  padding: .68rem 1.25rem;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: var(--gradient);
  color: #fff;
  font: inherit;
  font-size: .94rem;
  font-weight: 700;
  cursor: pointer;
  text-align: center;
  box-shadow: var(--shadow-brand);
  transition: transform .15s ease, box-shadow .15s ease, filter .15s ease;
}
.btn:hover {
  text-decoration: none;
  color: #fff;
  transform: translateY(-1px);
  filter: brightness(1.05);
  box-shadow: 0 8px 22px rgba(247, 14, 14, .34);
}
.btn:active { transform: translateY(0); }
.btn:disabled { opacity: .6; cursor: not-allowed; transform: none; filter: none; }
.btn-block { width: 100%; }
.btn-sm { padding: .4rem .8rem; font-size: .83rem; box-shadow: none; }

.btn-secondary {
  background: var(--surface);
  border-color: var(--border-strong);
  color: var(--text);
  box-shadow: none;
}
.btn-secondary:hover { background: var(--surface-2); color: var(--text); filter: none; }

.btn-ghost {
  background: transparent;
  border-color: var(--border-strong);
  color: var(--text-muted);
  box-shadow: none;
}
.btn-ghost:hover { background: var(--surface-2); color: var(--text); filter: none; }

.btn-danger { background: var(--danger); box-shadow: none; }
.btn-danger:hover { background: #b91c1c; }

/* ---------------------------------------------------------------- alerts - */
.alert {
  display: flex;
  align-items: flex-start;
  gap: .6rem;
  padding: .8rem 1rem;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  border-left-width: 4px;
  margin-bottom: 1rem;
  font-size: .92rem;
  animation: fadeIn .25s ease;
}
.alert-ico { flex: none; line-height: 1.4; }
.alert-success { background: var(--success-soft); border-color: var(--success-border); border-left-color: var(--success); color: #065f46; }
.alert-error   { background: var(--danger-soft);  border-color: var(--danger-border);  border-left-color: var(--danger);  color: #991b1b; }
.alert-info    { background: var(--info-soft);    border-color: var(--info-border);    border-left-color: var(--info);    color: #1e40af; }
.alert-warning { background: var(--warning-soft); border-color: var(--warning-border); border-left-color: var(--warning); color: #92400e; }
.alert div + div { margin-top: .2rem; }

/* ----------------------------------------------------------------- stats - */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(148px, 1fr));
  gap: .9rem;
  margin-bottom: 1.3rem;
}
.stat {
  position: relative;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1rem 1.1rem;
  box-shadow: var(--shadow-sm);
  transition: transform .18s ease, box-shadow .18s ease;
}
.stat:hover { transform: translateY(-3px); box-shadow: var(--shadow); }
.stat::before {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 4px;
  background: var(--gradient);
}
.stat-ico {
  font-size: 1.15rem;
  line-height: 1;
  margin-bottom: .45rem;
  display: block;
}
.stat .stat-value {
  font-size: 1.85rem;
  font-weight: 800;
  line-height: 1.05;
  background: var(--gradient);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.stat .stat-label {
  color: var(--text-muted);
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .3px;
  margin-top: .15rem;
}
.stat-plain .stat-value { background: none; color: var(--text); -webkit-text-fill-color: initial; }
.stat-green::before { background: linear-gradient(135deg, #16a34a, #4ade80); }
.stat-green .stat-value { background: linear-gradient(135deg, #16a34a, #22c55e); -webkit-background-clip: text; background-clip: text; }
.stat-amber::before { background: linear-gradient(135deg, #d97706, #fbbf24); }
.stat-amber .stat-value { background: linear-gradient(135deg, #d97706, #f59e0b); -webkit-background-clip: text; background-clip: text; }

/* ---------------------------------------------------------------- tables - */
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

table.data { width: 100%; border-collapse: collapse; font-size: .91rem; }
table.data th,
table.data td {
  padding: .75rem .95rem;
  text-align: left;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
table.data thead th {
  background: var(--surface-2);
  font-size: .74rem;
  text-transform: uppercase;
  letter-spacing: .5px;
  color: var(--text-muted);
  white-space: nowrap;
  font-weight: 800;
}
html[data-lang="zh"] table.data thead th { text-transform: none; letter-spacing: 0; font-size: .8rem; }
table.data thead th a { color: inherit; }
table.data tbody tr { transition: background .12s ease; }
table.data tbody tr:hover { background: var(--primary-soft); }
table.data tbody tr:last-child td { border-bottom: 0; }
.nowrap { white-space: nowrap; }
.sort-arrow { font-size: .68rem; color: var(--primary); }

/* ---------------------------------------------------------------- badges - */
.badge {
  display: inline-block;
  padding: .18rem .55rem;
  border-radius: 999px;
  font-size: .74rem;
  font-weight: 700;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text-muted);
  white-space: nowrap;
}
.badge-month { background: var(--primary-soft); border-color: var(--primary-border); color: var(--primary-darker); }
.badge-day   { background: #fff4ec; border-color: #ffd9bd; color: #9a4a06; }
.badge-file  { background: #f2eeee; color: #4a3937; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .7rem; }
.badge-ok    { background: var(--success-soft); border-color: var(--success-border); color: #065f46; }
.badge-wait  { background: var(--warning-soft); border-color: var(--warning-border); color: #92400e; }
.badge-info  { background: var(--info-soft); border-color: var(--info-border); color: #1e40af; }

/* ------------------------------------------------------------- item list - */
.item-list { list-style: none; margin: 0; padding: 0; }
.item-list li { border-bottom: 1px solid var(--border); }
.item-list li:last-child { border-bottom: 0; }

.item {
  display: flex;
  gap: .9rem;
  align-items: center;
  padding: .95rem 1.3rem;
  transition: background .12s ease, padding-left .15s ease;
}
.item:hover { background: var(--primary-soft); text-decoration: none; padding-left: 1.6rem; }
.item-body { flex: 1; min-width: 0; }
.item-title { font-weight: 700; color: var(--text); display: block; }
.item-meta {
  color: var(--text-muted);
  font-size: .82rem;
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
  align-items: center;
  margin-top: .25rem;
}
.item-arrow { color: var(--primary); font-weight: 800; flex: none; }

.empty { text-align: center; padding: 2.6rem 1rem; color: var(--text-muted); }
.empty .empty-icon { font-size: 2.4rem; display: block; margin-bottom: .6rem; opacity: .8; }

/* -------------------------------------------------------------- calendar - */
.calendar {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  margin-bottom: 1.25rem;
}
.cal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  padding: .9rem 1.1rem;
  border-bottom: 1px solid var(--border);
  background: var(--gradient-soft);
}
.cal-title { font-weight: 800; font-size: 1.05rem; }
.cal-nav { display: flex; gap: .4rem; }
.cal-btn {
  width: 36px; height: 36px;
  border: 1px solid var(--border-strong);
  background: var(--surface);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
  color: var(--text);
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.cal-btn:hover { background: var(--primary); border-color: var(--primary); color: #fff; }
.cal-btn.cal-today { width: auto; padding: 0 .8rem; font-size: .84rem; font-weight: 700; }

.cal-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 1px;
  background: var(--border);
  border-top: 1px solid var(--border);
}
.cal-dow {
  background: var(--surface-2);
  text-align: center;
  padding: .55rem .2rem;
  font-size: .7rem;
  font-weight: 800;
  letter-spacing: .5px;
  color: var(--text-muted);
  text-transform: uppercase;
}
html[data-lang="zh"] .cal-dow { text-transform: none; font-size: .78rem; letter-spacing: 0; }

.cal-cell {
  background: var(--surface);
  min-height: 64px;
  padding: .4rem .3rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .22rem;
  border: 0;
  font: inherit;
  color: var(--text);
  position: relative;
  transition: background .15s ease, transform .12s ease;
}
.cal-cell.is-empty { background: var(--surface-2); }
.cal-day-num { font-size: .84rem; font-weight: 700; }
.cal-cell.is-today .cal-day-num {
  background: var(--text);
  color: #fff;
  border-radius: 999px;
  width: 26px; height: 26px;
  display: grid;
  place-items: center;
}
.cal-cell.has-items {
  cursor: pointer;
  background: var(--primary-soft);
}
.cal-cell.has-items:hover { background: var(--primary-soft-2); transform: scale(1.03); z-index: 2; }
.cal-cell.is-selected { box-shadow: inset 0 0 0 2px var(--primary); z-index: 3; }
.cal-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--primary); }
.cal-dots { display: flex; gap: 3px; flex-wrap: wrap; justify-content: center; }
.cal-count { font-size: .68rem; font-weight: 800; color: var(--primary-dark); }

.cal-legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem 1rem;
  padding: .75rem 1.1rem;
  border-top: 1px solid var(--border);
  color: var(--text-muted);
  font-size: .81rem;
  background: var(--surface-2);
}
.cal-legend span.swatch {
  display: inline-block;
  width: 13px; height: 13px;
  border-radius: 4px;
  vertical-align: -2px;
  margin-right: .3rem;
}

/* teacher attendance colouring */
.cal-cell.att-full    { background: #e7f8ee; cursor: pointer; }
.cal-cell.att-full:hover { background: #d3f2e0; }
.cal-cell.att-partial { background: #fff6e5; cursor: pointer; }
.cal-cell.att-partial:hover { background: #ffedcc; }
.cal-cell.att-none    { background: #fdecec; cursor: pointer; }
.cal-cell.att-none:hover { background: #fbdcdc; }
.att-pill {
  font-size: .66rem;
  font-weight: 800;
  padding: .05rem .32rem;
  border-radius: 999px;
  background: rgba(255,255,255,.85);
  border: 1px solid rgba(0,0,0,.06);
  white-space: nowrap;
}
.att-full .att-pill    { color: #067647; }
.att-partial .att-pill { color: #92400e; }
.att-none .att-pill    { color: #991b1b; }

.swatch-full    { background: #b7ebcd; }
.swatch-partial { background: #ffd9a0; }
.swatch-none    { background: #f8b9b9; }

@media (min-width: 780px) {
  .cal-cell { min-height: 88px; }
  .calendar-layout {
    display: grid;
    grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
    gap: 1.25rem;
    align-items: start;
  }
  .calendar-layout .calendar { margin-bottom: 0; }
}

/* ------------------------------------------------------------ chat / msgs - */
.chat {
  padding: 1.1rem 1.3rem;
  max-height: 30rem;
  overflow-y: auto;
  background:
    radial-gradient(circle at 12% 10%, rgba(247,14,14,.04), transparent 45%),
    var(--surface);
}
.msg { display: flex; gap: .6rem; margin-bottom: 1rem; align-items: flex-end; }
.msg:last-child { margin-bottom: .2rem; }

.msg-avatar {
  width: 32px; height: 32px;
  flex: none;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: .78rem;
  font-weight: 800;
  color: #fff;
  background: #9a8b89;
}
.msg-teacher .msg-avatar { background: linear-gradient(135deg, #d97706, #f59e0b); }
.msg-student .msg-avatar { background: var(--gradient); }

.msg-content { max-width: 78%; min-width: 0; }
.msg-name {
  font-size: .76rem;
  font-weight: 800;
  color: var(--text-muted);
  margin-bottom: .22rem;
  display: flex;
  align-items: center;
  gap: .35rem;
}
.msg-bubble {
  padding: .65rem .85rem;
  border-radius: 14px 14px 14px 4px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  white-space: pre-wrap;
  word-wrap: break-word;
  overflow-wrap: anywhere;
  font-size: .93rem;
  line-height: 1.55;
}
.msg-time { font-size: .72rem; color: var(--text-muted); margin-top: .28rem; }

/* messages written by whoever is looking at the page sit on the right */
.msg-mine { flex-direction: row-reverse; }
.msg-mine .msg-content { align-items: flex-end; }
.msg-mine .msg-name { justify-content: flex-end; }
.msg-mine .msg-bubble {
  background: var(--gradient);
  border-color: transparent;
  color: #fff;
  border-radius: 14px 14px 4px 14px;
}
.msg-mine .msg-time { text-align: right; }
.msg-mine .msg-actions { justify-content: flex-end; }

.msg-actions { display: flex; gap: .35rem; margin-top: .35rem; }
.msg-actions .btn-sm { padding: .22rem .55rem; font-size: .74rem; }
.msg-actions form { display: inline; }

.chat-compose { border-top: 1px solid var(--border); padding: 1rem 1.3rem 1.2rem; background: var(--surface-2); }
.chat-compose textarea { min-height: 74px; background: #fff; }
.chat-role-note { font-size: .78rem; color: var(--text-muted); margin: 0 0 .6rem; }

/* --------------------------------------------------------------- details - */
.detail-grid { display: grid; grid-template-columns: 1fr; gap: 1.25rem; }
@media (min-width: 960px) {
  .detail-grid { grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); align-items: start; }
}

.meta-list { margin: 0; }
.meta-list dt {
  font-size: .72rem;
  text-transform: uppercase;
  letter-spacing: .5px;
  color: var(--text-muted);
  font-weight: 800;
}
html[data-lang="zh"] .meta-list dt { text-transform: none; letter-spacing: 0; font-size: .78rem; }
.meta-list dd { margin: .12rem 0 .9rem; word-wrap: break-word; }
.meta-list dd:last-child { margin-bottom: 0; }

.description-box {
  white-space: pre-wrap;
  word-wrap: break-word;
  overflow-wrap: anywhere;
  background: var(--gradient-soft);
  border: 1px solid var(--primary-border);
  border-left: 4px solid var(--primary);
  border-radius: var(--radius-sm);
  padding: .9rem 1.05rem;
}

.file-preview {
  max-width: 100%;
  height: auto;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  display: block;
  transition: transform .2s ease;
}
.file-preview:hover { transform: scale(1.01); }

.filter-bar { margin-bottom: 1.25rem; }

.pagination { display: flex; flex-wrap: wrap; gap: .35rem; justify-content: center; margin-top: 1.3rem; }
.pagination a, .pagination span {
  padding: .45rem .8rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font-size: .88rem;
  font-weight: 600;
  color: var(--text);
}
.pagination .is-current { background: var(--gradient); border-color: transparent; color: #fff; }
.pagination a:hover { background: var(--primary-soft); border-color: var(--primary-border); text-decoration: none; }

/* ---------------------------------------------------------- misc helpers - */
.muted { color: var(--text-muted); }
.text-sm { font-size: .86rem; }
.mt-0 { margin-top: 0; }
.mb-0 { margin-bottom: 0; }
.mb-1 { margin-bottom: .5rem; }
.mb-2 { margin-bottom: 1rem; }
.flex-between { display: flex; flex-wrap: wrap; gap: .6rem; align-items: center; justify-content: space-between; }

.tips { margin: 0; padding-left: 1.2rem; }
.tips li { margin-bottom: .4rem; color: var(--text-muted); font-size: .89rem; }
.tips li::marker { color: var(--primary); }

/* progress bar used for the submission rate */
.progress {
  height: 9px;
  border-radius: 999px;
  background: var(--border);
  overflow: hidden;
}
.progress > span {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--gradient);
  transition: width .4s ease;
}

/* a "one per day" notice strip */
.daily-note {
  display: flex;
  align-items: center;
  gap: .7rem;
  padding: .85rem 1.05rem;
  border-radius: var(--radius);
  border: 1px dashed var(--primary-border);
  background: var(--gradient-soft);
  margin-bottom: 1.25rem;
  font-size: .9rem;
}
.daily-note .daily-ico { font-size: 1.3rem; line-height: 1; flex: none; }
.daily-note.is-done { border-style: solid; border-color: var(--success-border); background: var(--success-soft); }

/* ------------------------------------------------------- profile pictures - */
img.avatar {
  width: 28px; height: 28px;
  flex: none;
  border-radius: 50%;
  object-fit: cover;
  background: var(--surface-2);
  border: 1px solid var(--border);
  display: block;
}
img.msg-avatar {
  width: 32px; height: 32px;
  border-radius: 50%;
  object-fit: cover;
  flex: none;
  background: var(--surface-2);
}
.avatar-large {
  width: 128px; height: 128px;
  border-radius: 50%;
  object-fit: cover;
  border: 3px solid var(--surface);
  box-shadow: var(--shadow);
  display: block;
}
.avatar-editor {
  display: flex;
  flex-wrap: wrap;
  gap: 1.2rem;
  align-items: flex-start;
}
.avatar-editor-side { flex: 1; min-width: 220px; }

.cell-user { display: flex; align-items: center; gap: .6rem; }
.cell-user img.avatar { width: 32px; height: 32px; }

/* --------------------------------------------------------- generations --- */
.badge-gen {
  background: #eef2ff;
  border-color: #c7d2fe;
  color: #3730a3;
}
.role-gen { background: #e0e7ff; color: #3730a3; }

.gen-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
  margin-bottom: 1.25rem;
  padding: .35rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  box-shadow: var(--shadow-sm);
  width: fit-content;
  max-width: 100%;
}
.gen-tab {
  padding: .45rem 1rem;
  border-radius: 999px;
  font-size: .88rem;
  font-weight: 700;
  color: var(--text-muted);
  white-space: nowrap;
}
.gen-tab:hover { background: var(--surface-2); color: var(--text); text-decoration: none; }
.gen-tab.is-active {
  background: var(--gradient);
  color: #fff;
  box-shadow: var(--shadow-brand);
}
.gen-tab.is-active:hover { color: #fff; }

.gen-compare {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}
@media (min-width: 720px) {
  .gen-compare { grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); }
}
.gen-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: .9rem 1rem;
  background: var(--surface-2);
}
.gen-figures {
  display: flex;
  gap: 1.2rem;
  margin: .7rem 0 .6rem;
}
.gen-figures > div { display: flex; flex-direction: column; }
.gen-num { font-size: 1.5rem; font-weight: 800; line-height: 1.1; }
.gen-num.ok { color: var(--success); }
.gen-num.warn { color: var(--warning); }
.gen-cap { font-size: .74rem; color: var(--text-muted); font-weight: 600; }

/* ------------------------------------ automatic Month/Day slot on upload - */
.slot-card {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius);
  padding: 1.2rem 1.3rem;
  margin-bottom: 1.25rem;
  background: var(--gradient-soft);
  border: 1px solid var(--primary-border);
  box-shadow: var(--shadow-sm);
}
.slot-head {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  align-items: center;
  justify-content: space-between;
  margin-bottom: .6rem;
}
.slot-values {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 1.2rem;
  margin: .4rem 0 1rem;
}
.slot-value { display: flex; flex-direction: column; align-items: center; }
.slot-label {
  font-size: .72rem;
  font-weight: 800;
  letter-spacing: .5px;
  text-transform: uppercase;
  color: var(--text-muted);
}
html[data-lang="zh"] .slot-label { text-transform: none; letter-spacing: 0; font-size: .78rem; }
.slot-number {
  font-size: 2.6rem;
  font-weight: 800;
  line-height: 1.05;
  background: var(--gradient);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.slot-sep { font-size: 1.6rem; color: var(--primary-border); font-weight: 800; }
.slot-progress .progress { margin-top: .3rem; }

/* ---- attendance panel: Submitted / Not-yet blocks, grouped by generation - */
.section-head { border-top: 1px solid var(--border); }
.section-head h3 { font-size: .95rem; }
.section-ok   { background: var(--success-soft); }
.section-ok h3::before   { background: linear-gradient(135deg, #16a34a, #4ade80); }
.section-wait { background: var(--warning-soft); }
.section-wait h3::before { background: linear-gradient(135deg, #d97706, #fbbf24); }

.gen-subhead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  padding: .45rem 1.3rem;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
}
.gen-subcount {
  font-size: .78rem;
  font-weight: 800;
  color: var(--text-muted);
}

/* the student name inside those lists is a link to the full profile */
a.item-body { display: block; color: inherit; min-width: 0; }
a.item-body:hover { text-decoration: none; }
a.item-body:hover .item-title { color: var(--primary-dark); text-decoration: underline; }

a.cell-user { color: inherit; }
a.cell-user:hover { text-decoration: none; }
a.cell-user:hover strong { color: var(--primary-dark); text-decoration: underline; }

/* ------------------------------------------------- full student profile - */
.profile-head { text-align: center; }
.profile-head .avatar-large { margin: 0 auto; }

.prof-figures {
  display: flex;
  flex-wrap: wrap;
  gap: 1.4rem;
  margin: .9rem 0 1rem;
}
.prof-figures > div { display: flex; flex-direction: column; }

/* small inline selects inside table rows */
.inline-form { display: flex; align-items: center; gap: .3rem; }
select.select-sm {
  padding: .3rem 1.8rem .3rem .5rem;
  font-size: .82rem;
  width: auto;
  min-width: 108px;
  background-position: right .5rem center;
}

/* the one-time temporary password shown after a reset */
.temp-password {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 1.6rem;
  font-weight: 700;
  letter-spacing: 2px;
  text-align: center;
  padding: 1rem;
  margin: .5rem 0 1rem;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  border: 2px dashed var(--primary-border);
  color: var(--primary-darker);
  user-select: all;
  word-break: break-all;
}

/* ------------------------------------------------------------- marking - */
.mark-card { border-color: var(--primary-border); }
.badge-lock {
  background: #f3f0ff;
  border-color: #ddd6fe;
  color: #5b21b6;
  white-space: normal;
}

.mark-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .75rem;
  margin-bottom: .4rem;
}
.mark-input {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  padding: .35rem .75rem .35rem .5rem;
  border: 2px solid var(--primary-border);
  border-radius: var(--radius-sm);
  background: var(--gradient-soft);
}
.mark-input input {
  width: 5.5rem;
  border: 0;
  background: transparent;
  font-size: 1.6rem;
  font-weight: 800;
  text-align: center;
  padding: .1rem 0;
  color: var(--primary-dark);
}
.mark-input input:focus { outline: none; box-shadow: none; }
.mark-outof {
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--text-muted);
}

/* the score shown in tables */
.mark-pill {
  display: inline-block;
  padding: .18rem .6rem;
  border-radius: 999px;
  font-weight: 800;
  font-size: .82rem;
  background: #eef2ff;
  border: 1px solid #c7d2fe;
  color: #3730a3;
  white-space: nowrap;
}

/* ------------------------------------------------------------- rankings - */
.rank-table td { vertical-align: middle; }
.rank-col { width: 3.5rem; text-align: center; }
.rank-badge {
  display: inline-block;
  font-size: 1.05rem;
  font-weight: 800;
  color: var(--text-muted);
}
.rank-table tr.is-podium .rank-badge { font-size: 1.35rem; }
.rank-table tr.is-podium td { background: linear-gradient(90deg, #fffaf0 0%, transparent 60%); }
.rank-table tr.is-me td {
  background: var(--primary-soft);
  box-shadow: inset 3px 0 0 var(--primary);
}
.rank-table tr.is-me:hover td { background: var(--primary-soft-2); }

.rank-days { font-size: 1.05rem; }

.badge-me {
  background: var(--gradient);
  border-color: transparent;
  color: #fff;
  margin-left: .35rem;
}

.streak { font-weight: 800; color: #c2410c; white-space: nowrap; }

.rate-cell { min-width: 78px; }
.rate-cell span { font-size: .84rem; font-weight: 700; }
.progress-thin { height: 5px; margin-top: .25rem; }

/* ------------------------------------------- compact dashboard standings - */
.mini-rank .item { padding: .7rem 1.2rem; gap: .7rem; }
.mini-rank .item:hover { padding-left: 1.2rem; background: transparent; }
.mini-rank .item.is-me {
  background: var(--primary-soft);
  box-shadow: inset 3px 0 0 var(--primary);
}
.mini-rank-pos {
  flex: none;
  width: 2rem;
  text-align: center;
  font-weight: 800;
  font-size: .95rem;
  color: var(--text-muted);
}
.mini-rank .item-meta { gap: .6rem; }
.mini-rank-gap {
  text-align: center;
  color: var(--border-strong);
  letter-spacing: 2px;
  line-height: 1;
  padding: .1rem 0 .2rem;
}
.mini-rank-me {
  padding: .7rem 1.2rem;
  background: var(--gradient-soft);
  border-bottom: 1px solid var(--border);
  font-size: .88rem;
  color: var(--text-muted);
}
.mini-rank-me strong { color: var(--primary-dark); font-size: 1.05rem; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ==========================================================================
   CARTOON ARTWORK  (student-facing pages only)
   --------------------------------------------------------------------------
   The angel is drawn in includes/art.php as inline SVG. Its colours are
   the custom properties below, so a drawing can be recoloured for wherever it
   sits - cream on the red banner, full brand colour on a white card - without
   touching the artwork.

   The teacher's pages do not use any of this: marking and attendance are
   working tools and stay plain.
   ========================================================================== */

.angel { display: block; flex: none; }

/* --- gentle life, switched off for anyone who has asked for less motion --- */
@keyframes angel-bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-5px); }
}
@keyframes angel-pop {
  0%   { transform: scale(.72); opacity: 0; }
  60%  { transform: scale(1.06); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}
.angel-hello, .angel-star, .angel-read { animation: angel-bob 3.2s ease-in-out infinite; }
.angel-cheer { animation: angel-pop .5s cubic-bezier(.2, .9, .3, 1.3) both; }

@media (prefers-reduced-motion: reduce) {
  .angel-hello, .angel-star, .angel-read, .angel-cheer { animation: none; }
}

/* --- the mascot on the coloured page banner ------------------------------ */
/* The banner is brand red, so a red robe would vanish into it. Only the
   colour variables change here - the drawing is identical. */
.page-head .angel {
  --angel-robe-1: #ffffff;
  --angel-robe-2: #ffe3dc;
  --angel-hair:   #ffc7bc;
  --angel-wing:  #fff6f4;
  --angel-ink:    #9a1c14;
  --angel-halo-1: #ffd98c;
  --angel-halo-2: #f7b143;
  --angel-shade:  rgba(255, 255, 255, .16);
  --angel-eye:    #fffaf9;
  --angel-zzz:    rgba(255, 255, 255, .8);
  --angel-think:  rgba(255, 255, 255, .8);
  filter: drop-shadow(0 6px 14px rgba(90, 10, 8, .3));
}

/* Phones are where most students actually read this, so the angel stays -
   it just gets smaller rather than disappearing. */
@media (max-width: 560px) {
  .page-head .angel { width: 54px; height: 63px; }
  .page-head-lead { gap: .7rem; }
}
@media (max-width: 380px) {
  .page-head .angel { width: 44px; height: 51px; }
}

/* --- empty states -------------------------------------------------------- */
/* An empty page is where a drawing earns its keep: it turns "nothing here"
   into something friendlier than grey text. */
.empty .angel { margin: 0 auto .8rem; }
/* Scoped: the teacher's empty states are left exactly as they were. */
[data-role="student"] .empty p { max-width: 34ch; margin-left: auto; margin-right: auto; }

/* --- the one-per-day strip ----------------------------------------------- */
.daily-note .angel { margin: -.35rem 0; }
.daily-note.is-done {
  background: linear-gradient(135deg, #ecfdf5 0%, #f0fdf4 60%, #fefce8 100%);
}

/* --- login and sign-up --------------------------------------------------- */
.auth-angel {
  display: grid;
  place-items: center;
  margin: .1rem auto .6rem;
}
.auth-angel .angel { filter: drop-shadow(0 10px 20px rgba(247, 14, 14, .22)); }

/* --- more colour in the stat row ----------------------------------------- */
/* The first two already existed; these three give a row of four or five
   tiles some variety instead of five identical red bars. */
.stat-blue::before   { background: linear-gradient(135deg, #2563eb, #60a5fa); }
.stat-blue .stat-value {
  background: linear-gradient(135deg, #2563eb, #3b82f6);
  -webkit-background-clip: text; background-clip: text;
}
.stat-purple::before { background: linear-gradient(135deg, #7c3aed, #c084fc); }
.stat-purple .stat-value {
  background: linear-gradient(135deg, #7c3aed, #a855f7);
  -webkit-background-clip: text; background-clip: text;
}
.stat-pink::before   { background: linear-gradient(135deg, #db2777, #f472b6); }
.stat-pink .stat-value {
  background: linear-gradient(135deg, #db2777, #ec4899);
  -webkit-background-clip: text; background-clip: text;
}

/* A faint wash of each tile's own colour, so the row reads as colourful
   rather than as five white boxes with a stripe.

   Scoped to students on purpose: stat-green and stat-amber are also used by
   the teacher's dashboard, calendar, ranking and student profile, and those
   screens are meant to stay exactly as they were. */
[data-role="student"] .stat-green  { background: linear-gradient(160deg, #f4fdf8 0%, var(--surface) 55%); }
[data-role="student"] .stat-amber  { background: linear-gradient(160deg, #fffbf0 0%, var(--surface) 55%); }
[data-role="student"] .stat-blue   { background: linear-gradient(160deg, #f2f7ff 0%, var(--surface) 55%); }
[data-role="student"] .stat-purple { background: linear-gradient(160deg, #faf5ff 0%, var(--surface) 55%); }
[data-role="student"] .stat-pink   { background: linear-gradient(160deg, #fff3f8 0%, var(--surface) 55%); }

/* --- mascot beside the page title ---------------------------------------- */
/* Groups the angel with the heading so the banner stays [angel + words] on the
   left and the action button on the right. */
.page-head-lead {
  display: flex;
  align-items: center;
  gap: 1rem;
  min-width: 0;
}

/* --- the angel on the automatic Month/Day card --------------------------- */
/* It sits in the same flex row as MONTH and DAY, so it can never end up on
   top of the "days left" label the way an absolutely placed one did. */
/* The row is baseline-aligned for the big numbers; an SVG's baseline is its
   bottom edge, which would shove the angel upwards - so opt it out. */
.slot-values .angel { align-self: center; margin-left: 1.1rem; }
@media (max-width: 460px) {
  .slot-values .angel { width: 52px; height: 61px; margin-left: .5rem; }
}

/* --- a small angel signing off each student page --------------------------- */
.footer-angel {
  display: grid;
  place-items: center;
  margin-bottom: .1rem;
}
.footer-angel .angel { opacity: .9; }

/* --- the daily strip ------------------------------------------------------ */
/* The angel is taller than the tick it replaced, so let the strip breathe. */
.daily-note { align-items: center; }
.daily-note .angel { margin: -.5rem 0; }


/* ==========================================================================
   ANGELS EVERYWHERE  (student-facing only)
   ========================================================================== */

/* --- the compact angel ------------------------------------------------------ */
.angel-mini { display: block; flex: none; vertical-align: middle; }

/* --- the brand mark in the top bar --------------------------------------- */
[data-role="student"] .brand-mark {
  display: grid;
  place-items: center;
  background: #fff;
  padding: 0;
}
[data-role="student"] .brand-mark .angel-mini { width: 25px; height: 29px; }

/* --- angels sitting in the stat tiles --------------------------------------- */
.stat-ico .angel-mini,
.stat-ico .angel { display: inline-block; }
.stat-ico { min-height: 30px; }

/* --- one on every repeated row -------------------------------------------- */
.row-angel { display: inline-flex; align-items: center; flex: none; }
.row-angel .angel-mini { opacity: .95; }
.rank-col .row-angel { margin-left: .3rem; }
.mini-rank-pos + .row-angel { margin-right: .1rem; }

/* --- every day you handed something in is an angel -------------------------- */
/* The calendar is drawn by calendar.js, which gives each submission a
   .cal-dot - so restyling that one class turns the whole month into angels. */
.cal-dot {
  width: 16px !important;
  height: 19px !important;
  border-radius: 0 !important;
  /* A mask rather than a background image: the shape comes from the mask and
     the colour from background-color, so each weekday can have its own. */
  background-image: none !important;
  background-color: var(--c-rose);
  -webkit-mask: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2028%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20fill%3D%22%2523000%22%20d%3D%22M6.4%2C3.4%20a5.6%2C2.3%200%201%2C0%2011.2%2C0%20a5.6%2C2.3%200%201%2C0%20-11.2%2C0%20M8.3%2C3.4%20a3.7%2C1.0%200%201%2C0%207.4%2C0%20a3.7%2C1.0%200%201%2C0%20-7.4%2C0%22%2F%3E%3Cpath%20fill%3D%22%2523000%22%20d%3D%22M8.6%2017%20C%205%2014.5%201.6%2015.6%201%2018.6%20C%20.6%2021.4%204%2023.4%208%2022.6%20Z%20M15.4%2017%20C%2019%2014.5%2022.4%2015.6%2023%2018.6%20C%2023.4%2021.4%2020%2023.4%2016%2022.6%20Z%20M12%2015%20C%2015%2015%2016.4%2016.6%2017.2%2019.4%20L%2018.6%2025%20C%2018.6%2026.4%2016.4%2027%2012%2027%20C%207.6%2027%205.4%2026.4%205.4%2025%20L%206.8%2019.4%20C%207.6%2016.6%209%2015%2012%2015%20Z%20M6.2%2C10.4%20a5.8%2C5.8%200%201%2C0%2011.6%2C0%20a5.8%2C5.8%200%201%2C0%20-11.6%2C0%22%2F%3E%3C%2Fsvg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2028%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20fill%3D%22%2523000%22%20d%3D%22M6.4%2C3.4%20a5.6%2C2.3%200%201%2C0%2011.2%2C0%20a5.6%2C2.3%200%201%2C0%20-11.2%2C0%20M8.3%2C3.4%20a3.7%2C1.0%200%201%2C0%207.4%2C0%20a3.7%2C1.0%200%201%2C0%20-7.4%2C0%22%2F%3E%3Cpath%20fill%3D%22%2523000%22%20d%3D%22M8.6%2017%20C%205%2014.5%201.6%2015.6%201%2018.6%20C%20.6%2021.4%204%2023.4%208%2022.6%20Z%20M15.4%2017%20C%2019%2014.5%2022.4%2015.6%2023%2018.6%20C%2023.4%2021.4%2020%2023.4%2016%2022.6%20Z%20M12%2015%20C%2015%2015%2016.4%2016.6%2017.2%2019.4%20L%2018.6%2025%20C%2018.6%2026.4%2016.4%2027%2012%2027%20C%207.6%2027%205.4%2026.4%205.4%2025%20L%206.8%2019.4%20C%207.6%2016.6%209%2015%2012%2015%20Z%20M6.2%2C10.4%20a5.8%2C5.8%200%201%2C0%2011.6%2C0%20a5.8%2C5.8%200%201%2C0%20-11.6%2C0%22%2F%3E%3C%2Fsvg%3E") center / contain no-repeat;
}
.cal-dots { gap: 2px; }

/* A rainbow across the week - the seven day-name headers come first in the
   grid, so cell columns line up exactly with nth-child(7n+N). */
[data-role="student"] .cal-grid > .cal-cell:nth-child(7n+1) .cal-dot { background-color: var(--c-rose); }
[data-role="student"] .cal-grid > .cal-cell:nth-child(7n+2) .cal-dot { background-color: var(--c-orange); }
[data-role="student"] .cal-grid > .cal-cell:nth-child(7n+3) .cal-dot { background-color: var(--c-amber); }
[data-role="student"] .cal-grid > .cal-cell:nth-child(7n+4) .cal-dot { background-color: var(--c-mint); }
[data-role="student"] .cal-grid > .cal-cell:nth-child(7n+5) .cal-dot { background-color: var(--c-teal); }
[data-role="student"] .cal-grid > .cal-cell:nth-child(7n+6) .cal-dot { background-color: var(--c-sky); }
[data-role="student"] .cal-grid > .cal-cell:nth-child(7n+7) .cal-dot { background-color: var(--c-purple); }

/* --- a whisper of angels behind every student page -------------------------- */
/* Very low opacity on purpose: it should register as texture, never as
   something competing with the words on top of it. */
body[data-role="student"] {
  background-image:
    radial-gradient(circle at 0% 0%, rgba(247, 14, 14, .07), transparent 42%),
    radial-gradient(circle at 100% 0%, rgba(255, 106, 43, .07), transparent 42%),
    url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2026%22%20fill-opacity%3D%220.10%22%3E%3Cpath%20d%3D%22M5.5%207.5%20L3%201%20L10%205.5%20Z%22%20fill%3D%22%2523f70e0e%22%2F%3E%3Cpath%20d%3D%22M18.5%207.5%20L21%201%20L14%205.5%20Z%22%20fill%3D%22%2523f70e0e%22%2F%3E%3Cpath%20d%3D%22M12%202.5%20C18%202.5%2021.5%206.5%2021.5%2012.5%20C21.5%2018.5%2017.5%2022.5%2012%2022.5%20C6.5%2022.5%202.5%2018.5%202.5%2012.5%20C2.5%206.5%206%202.5%2012%202.5%20Z%22%20fill%3D%22%2523f70e0e%22%2F%3E%3C%2Fsvg%3E") 84px;
  background-repeat: no-repeat, no-repeat, repeat;
  background-attachment: fixed, fixed, fixed;
}
@media (prefers-reduced-motion: no-preference) {
  body[data-role="student"] { background-blend-mode: normal; }
}

/* ==========================================================================
   CUTER, MORE COLOURFUL  (student-facing only)
   --------------------------------------------------------------------------
   Everything below is scoped to [data-role="student"], so the teacher's
   screens keep the plain, high-contrast look they need for marking.
   ========================================================================== */

:root {
  /* a friendlier spread of colour than brand red on its own */
  --c-rose:   #f43f5e;
  --c-pink:   #ec4899;
  --c-purple: #a855f7;
  --c-violet: #8b5cf6;
  --c-indigo: #6366f1;
  --c-sky:    #0ea5e9;
  --c-cyan:   #06b6d4;
  --c-teal:   #14b8a6;
  --c-mint:   #10b981;
  --c-lime:   #84cc16;
  --c-amber:  #f59e0b;
  --c-orange: #f97316;
}

/* --- rounder, softer, bouncier ------------------------------------------- */
[data-role="student"] {
  --radius:    22px;
  --radius-sm: 14px;
  --radius-lg: 30px;
}
[data-role="student"] .btn { border-radius: 999px; }
[data-role="student"] .badge { border-radius: 999px; }
[data-role="student"] .card,
[data-role="student"] .stat { border-width: 2px; }

[data-role="student"] .btn {
  transition: transform .16s cubic-bezier(.2, .9, .3, 1.3), box-shadow .16s ease, background .16s ease;
}
[data-role="student"] .btn:hover { transform: translateY(-2px) scale(1.02); }
[data-role="student"] .btn:active { transform: translateY(0) scale(.99); }
@media (prefers-reduced-motion: reduce) {
  [data-role="student"] .btn:hover,
  [data-role="student"] .btn:active { transform: none; }
}

/* --- every page gets its own colour --------------------------------------- */
/* data-page comes from $activeNav, which each page already sets. */
[data-role="student"]                      { --page-1: #f70e0e; --page-2: #ff6a2b; }
[data-role="student"][data-page="dashboard"]   { --page-1: #f43f5e; --page-2: #a855f7; }
[data-role="student"][data-page="upload"]      { --page-1: #0ea5e9; --page-2: #6366f1; }
[data-role="student"][data-page="submissions"] { --page-1: #f97316; --page-2: #db2777; }
[data-role="student"][data-page="calendar"]    { --page-1: #8b5cf6; --page-2: #6366f1; }
[data-role="student"][data-page="ranking"]     { --page-1: #f59e0b; --page-2: #ea580c; }
[data-role="student"][data-page="account"]     { --page-1: #10b981; --page-2: #0891b2; }

[data-role="student"] .page-head {
  background: linear-gradient(135deg, var(--page-1) 0%, var(--page-2) 100%);
  box-shadow: 0 8px 24px color-mix(in srgb, var(--page-1) 35%, transparent);
}
/* The main action button picks up the same colour, so each page reads as
   one idea rather than a coloured banner with a red button under it. */
[data-role="student"] .btn:not(.btn-ghost):not(.btn-secondary) {
  background: linear-gradient(135deg, var(--page-1) 0%, var(--page-2) 100%);
  box-shadow: 0 6px 16px color-mix(in srgb, var(--page-1) 34%, transparent);
}
[data-role="student"] .page-head a.btn:not(.btn-ghost) {
  background: #fff;
  color: var(--page-1);
}

/* --- pastel cards --------------------------------------------------------- */
/* Each card in a column takes the next tint, so a page is never a stack of
   identical white boxes. */
[data-role="student"] .card:nth-of-type(6n+1) { background: linear-gradient(170deg, #fff5f8 0%, var(--surface) 62%); }
[data-role="student"] .card:nth-of-type(6n+2) { background: linear-gradient(170deg, #f3f6ff 0%, var(--surface) 62%); }
[data-role="student"] .card:nth-of-type(6n+3) { background: linear-gradient(170deg, #f2fdf7 0%, var(--surface) 62%); }
[data-role="student"] .card:nth-of-type(6n+4) { background: linear-gradient(170deg, #fdf6ff 0%, var(--surface) 62%); }
[data-role="student"] .card:nth-of-type(6n+5) { background: linear-gradient(170deg, #fffaf0 0%, var(--surface) 62%); }
[data-role="student"] .card:nth-of-type(6n+6) { background: linear-gradient(170deg, #f0fdfc 0%, var(--surface) 62%); }

/* --- angels in colour ------------------------------------------------------- */
/* The mini angel reads its body colour from a variable, so a rotation of
   colours costs one rule per step and no change to the PHP. */
.angel-c1 { --angel-mini-robe: var(--c-rose);   --angel-mini-hem: #be123c; }
.angel-c2 { --angel-mini-robe: var(--c-amber);  --angel-mini-hem: #b45309; }
.angel-c3 { --angel-mini-robe: var(--c-mint);   --angel-mini-hem: #047857; }
.angel-c4 { --angel-mini-robe: var(--c-sky);    --angel-mini-hem: #0369a1; }
.angel-c5 { --angel-mini-robe: var(--c-purple); --angel-mini-hem: #7e22ce; }
.angel-c6 { --angel-mini-robe: var(--c-pink);   --angel-mini-hem: #be185d; }

/* Rows cycle through the colours without the PHP needing a counter. */
[data-role="student"] .item-list li:nth-child(6n+1) .angel-mini { --angel-mini-robe: var(--c-rose);   --angel-mini-hem: #be123c; }
[data-role="student"] .item-list li:nth-child(6n+2) .angel-mini { --angel-mini-robe: var(--c-amber);  --angel-mini-hem: #b45309; }
[data-role="student"] .item-list li:nth-child(6n+3) .angel-mini { --angel-mini-robe: var(--c-mint);   --angel-mini-hem: #047857; }
[data-role="student"] .item-list li:nth-child(6n+4) .angel-mini { --angel-mini-robe: var(--c-sky);    --angel-mini-hem: #0369a1; }
[data-role="student"] .item-list li:nth-child(6n+5) .angel-mini { --angel-mini-robe: var(--c-purple); --angel-mini-hem: #7e22ce; }
[data-role="student"] .item-list li:nth-child(6n+6) .angel-mini { --angel-mini-robe: var(--c-pink);   --angel-mini-hem: #be185d; }

[data-role="student"] table.data tbody tr:nth-child(6n+1) .angel-mini { --angel-mini-robe: var(--c-rose);   --angel-mini-hem: #be123c; }
[data-role="student"] table.data tbody tr:nth-child(6n+2) .angel-mini { --angel-mini-robe: var(--c-amber);  --angel-mini-hem: #b45309; }
[data-role="student"] table.data tbody tr:nth-child(6n+3) .angel-mini { --angel-mini-robe: var(--c-mint);   --angel-mini-hem: #047857; }
[data-role="student"] table.data tbody tr:nth-child(6n+4) .angel-mini { --angel-mini-robe: var(--c-sky);    --angel-mini-hem: #0369a1; }
[data-role="student"] table.data tbody tr:nth-child(6n+5) .angel-mini { --angel-mini-robe: var(--c-purple); --angel-mini-hem: #7e22ce; }
[data-role="student"] table.data tbody tr:nth-child(6n+6) .angel-mini { --angel-mini-robe: var(--c-pink);   --angel-mini-hem: #be185d; }

/* Each stat tile's angel matches the tile it sits in. */
[data-role="student"] .stat .angel-mini            { --angel-mini-robe: var(--c-rose);   --angel-mini-hem: #be123c; }
[data-role="student"] .stat-amber  .angel-mini     { --angel-mini-robe: var(--c-amber);  --angel-mini-hem: #b45309; }
[data-role="student"] .stat-green  .angel-mini     { --angel-mini-robe: var(--c-mint);   --angel-mini-hem: #047857; }
[data-role="student"] .stat-blue   .angel-mini     { --angel-mini-robe: var(--c-sky);    --angel-mini-hem: #0369a1; }
[data-role="student"] .stat-purple .angel-mini     { --angel-mini-robe: var(--c-purple); --angel-mini-hem: #7e22ce; }
[data-role="student"] .stat-pink   .angel-mini     { --angel-mini-robe: var(--c-pink);   --angel-mini-hem: #be185d; }

/* The full angel is used in some tiles too (it has the moods); colour it to
   match its tile the same way the compact one is coloured. */
[data-role="student"] .stat .angel        { --angel-robe-1: var(--c-rose);   --angel-robe-2: #fb7185; --angel-hair: #be123c; }
[data-role="student"] .stat-amber .angel  { --angel-robe-1: var(--c-amber);  --angel-robe-2: #fcd34d; --angel-hair: #b45309; }
[data-role="student"] .stat-green .angel  { --angel-robe-1: var(--c-mint);   --angel-robe-2: #6ee7b7; --angel-hair: #047857; }
[data-role="student"] .stat-blue .angel   { --angel-robe-1: var(--c-sky);    --angel-robe-2: #7dd3fc; --angel-hair: #0369a1; }
[data-role="student"] .stat-purple .angel { --angel-robe-1: var(--c-purple); --angel-robe-2: #d8b4fe; --angel-hair: #7e22ce; }
[data-role="student"] .stat-pink .angel   { --angel-robe-1: var(--c-pink);   --angel-robe-2: #f9a8d4; --angel-hair: #be185d; }

/* The daily strip's angel follows the page colour, so "done for today" feels
   like part of the page rather than a red sticker on it. */
[data-role="student"] .daily-note .angel {
  --angel-robe-1: var(--page-1);
  --angel-robe-2: var(--page-2);
}
[data-role="student"] .daily-note.is-done .angel {
  --angel-robe-1: var(--c-mint);
  --angel-robe-2: #6ee7b7;
  --angel-hair: #047857;
}

/* --- the perch: friends standing along the bottom of the banner ----------- */
/* The banner's lower edge is treated as a branch. The row is decorative, so
   it is pinned to the bottom and the banner simply grows enough padding to
   keep the wording clear of it. */
[data-role="student"] .page-head { padding-bottom: 5rem; }

.perch {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 0;
  pointer-events: none;
}
.perch-branch {
  display: block;
  width: 100%;
  height: 22px;
  --branch: #6b3f22;
  --branch-top: #a86c3e;
}
/* The leaves sit over the bough's top edge. */
.perch-leaves {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 14px;
  width: 100%;
  height: 22px;
  --leaf: #4faf57;
}
.perch-cast {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: clamp(.35rem, 4.2vw, 2.6rem);
  padding: 0 1.2rem;
  /* their feet finish just inside the top of the bough, so they grip it */
  margin-bottom: -14px;
}
.perch-one {
  display: block;
  filter: drop-shadow(0 3px 4px rgba(60, 20, 18, .3));
}

/* Stagger them a little so it reads as creatures landing, not a shop display. */
.perch-one:nth-child(2n) { transform: translateY(1px); }
.perch-one:nth-child(3n) { transform: translateY(-1px); }

/* One or two bob gently, the way something perched really does. */
@keyframes perch-bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-3px); }
}
.perch-one:nth-child(3),
.perch-one:nth-child(6) { animation: perch-bob 3.6s ease-in-out infinite; }
.perch-one:nth-child(6) { animation-delay: 1.3s; }
@media (prefers-reduced-motion: reduce) {
  .perch-one { animation: none !important; }
}

/* The banner's own words and buttons sit above the row. */
[data-role="student"] .page-head > .page-head-lead,
[data-role="student"] .page-head > a.btn,
[data-role="student"] .page-head > .btn { z-index: 2; }

/* Fewer friends as the banner narrows, so they never squash together. */
@media (max-width: 700px) {
  .perch-one:nth-child(8) { display: none; }
}
@media (max-width: 560px) {
  .perch-one:nth-child(7) { display: none; }
  .perch-cast { padding: 0 .6rem; }
}
@media (max-width: 430px) {
  .perch-one:nth-child(5), .perch-one:nth-child(6) { display: none; }
  [data-role="student"] .page-head { padding-bottom: 4.4rem; }
  .perch-one svg { width: 32px; height: 40px; }
}


/* ==========================================================================
   BOLDER  (student-facing only)
   --------------------------------------------------------------------------
   The banner was the only colourful thing on the page; everything under it
   was near-white, so the whole screen read pale however bright the banner
   got. This section colours the other ninety per cent: the background, the
   cards, and the chrome at the top.
   ========================================================================== */

/* --- an aurora behind everything ------------------------------------------ */
body[data-role="student"] {
  background-color: #fdf6ff;
  background-image:
    radial-gradient(62vw 62vw at  6%   0%, rgba(255,  92, 158, .42), transparent 62%),
    radial-gradient(58vw 58vw at 94%   3%, rgba(168,  85, 247, .38), transparent 62%),
    radial-gradient(54vw 54vw at 82%  42%, rgba( 56, 189, 248, .34), transparent 64%),
    radial-gradient(50vw 50vw at 10%  58%, rgba( 45, 212, 191, .30), transparent 64%),
    radial-gradient(52vw 52vw at 52%  96%, rgba(251, 176,  36, .34), transparent 64%),
    url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2028%22%20fill-opacity%3D%220.13%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20fill%3D%22%2523ffffff%22%20d%3D%22M6.4%2C3.4%20a5.6%2C2.3%200%201%2C0%2011.2%2C0%20a5.6%2C2.3%200%201%2C0%20-11.2%2C0%20M8.3%2C3.4%20a3.7%2C1.0%200%201%2C0%207.4%2C0%20a3.7%2C1.0%200%201%2C0%20-7.4%2C0%22%2F%3E%3Cpath%20fill%3D%22%2523ffffff%22%20d%3D%22M8.6%2017%20C%205%2014.5%201.6%2015.6%201%2018.6%20C%20.6%2021.4%204%2023.4%208%2022.6%20Z%20M15.4%2017%20C%2019%2014.5%2022.4%2015.6%2023%2018.6%20C%2023.4%2021.4%2020%2023.4%2016%2022.6%20Z%20M12%2015%20C%2015%2015%2016.4%2016.6%2017.2%2019.4%20L%2018.6%2025%20C%2018.6%2026.4%2016.4%2027%2012%2027%20C%207.6%2027%205.4%2026.4%205.4%2025%20L%206.8%2019.4%20C%207.6%2016.6%209%2015%2012%2015%20Z%20M6.2%2C10.4%20a5.8%2C5.8%200%201%2C0%2011.6%2C0%20a5.8%2C5.8%200%201%2C0%20-11.6%2C0%22%2F%3E%3C%2Fsvg%3E");
  background-size: auto, auto, auto, auto, auto, 74px 86px;
  background-repeat: no-repeat, no-repeat, no-repeat, no-repeat, no-repeat, repeat;
  background-attachment: fixed;
}

/* --- a rainbow along the very top ----------------------------------------- */
[data-role="student"] .topbar::before {
  height: 6px;
  background: linear-gradient(90deg,
    #f43f5e 0%, #f97316 16%, #fbbf24 32%, #34d399 48%,
    #22d3ee 64%, #6366f1 80%, #a855f7 100%);
}
[data-role="student"] .topbar {
  background: rgba(255, 255, 255, .82);
  border-bottom: none;
  box-shadow: 0 4px 18px rgba(120, 40, 140, .10);
}

/* --- cards that float above the colour ------------------------------------ */
/* Near-opaque white keeps the words crisp; the colour goes into the rim, the
   shadow and a bright bar across the top. */
[data-role="student"] .card {
  position: relative;
  overflow: hidden;
  background: rgba(255, 255, 255, .93);
  -webkit-backdrop-filter: blur(8px);
          backdrop-filter: blur(8px);
  border: 2px solid rgba(255, 255, 255, .9);
  box-shadow: 0 12px 34px rgba(118, 40, 130, .16), 0 2px 8px rgba(118, 40, 130, .08);
}
[data-role="student"] .card::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 6px;
  background: var(--card-bar, linear-gradient(90deg, #f43f5e, #a855f7));
  z-index: 1;
}
[data-role="student"] .card:nth-of-type(6n+1) { --card-bar: linear-gradient(90deg, #f43f5e, #fb7185); }
[data-role="student"] .card:nth-of-type(6n+2) { --card-bar: linear-gradient(90deg, #0ea5e9, #6366f1); }
[data-role="student"] .card:nth-of-type(6n+3) { --card-bar: linear-gradient(90deg, #10b981, #34d399); }
[data-role="student"] .card:nth-of-type(6n+4) { --card-bar: linear-gradient(90deg, #a855f7, #d946ef); }
[data-role="student"] .card:nth-of-type(6n+5) { --card-bar: linear-gradient(90deg, #f59e0b, #f97316); }
[data-role="student"] .card:nth-of-type(6n+6) { --card-bar: linear-gradient(90deg, #06b6d4, #14b8a6); }

/* --- stat tiles ------------------------------------------------------------ */
/* The old 4px stripe down the left becomes a bright bar across the top, and
   the number itself is painted with the same gradient. */
[data-role="student"] .stat {
  background: rgba(255, 255, 255, .93);
  -webkit-backdrop-filter: blur(8px);
          backdrop-filter: blur(8px);
  border: 2px solid rgba(255, 255, 255, .9);
  box-shadow: 0 10px 26px rgba(118, 40, 130, .15);
  overflow: hidden;
}
[data-role="student"] .stat::before {
  left: 0; right: 0; top: 0; bottom: auto;
  width: auto; height: 6px;
}
[data-role="student"] .stat:hover {
  transform: translateY(-4px) scale(1.015);
  box-shadow: 0 18px 38px rgba(118, 40, 130, .22);
}
[data-role="student"] .stat .stat-value { font-size: 2.1rem; }

/* --- headings ------------------------------------------------------------- */
[data-role="student"] .card-header h2::before,
[data-role="student"] .card-header h3::before {
  background: var(--card-bar, linear-gradient(180deg, #f43f5e, #a855f7));
  width: 5px;
  border-radius: 999px;
}
[data-role="student"] .page-head h1 {
  text-shadow: 0 2px 10px rgba(0, 0, 0, .18);
  font-size: 1.95rem;
}

/* --- richer banners ------------------------------------------------------- */
/* Three stops rather than two, so the banner has somewhere to travel. */
[data-role="student"] .page-head {
  background: linear-gradient(120deg,
    var(--page-1) 0%, var(--page-mid, var(--page-2)) 52%, var(--page-2) 100%);
}
[data-role="student"][data-page="dashboard"]   { --page-mid: #d946ef; }
[data-role="student"][data-page="upload"]      { --page-mid: #22d3ee; }
[data-role="student"][data-page="submissions"] { --page-mid: #fb7185; }
[data-role="student"][data-page="calendar"]    { --page-mid: #c084fc; }
[data-role="student"][data-page="ranking"]     { --page-mid: #fb923c; }
[data-role="student"][data-page="account"]     { --page-mid: #34d399; }

/* --- the footer and the daily strip --------------------------------------- */
[data-role="student"] .site-footer {
  background: rgba(255, 255, 255, .8);
  border-top: none;
  box-shadow: 0 -4px 18px rgba(120, 40, 140, .08);
}
[data-role="student"] .daily-note {
  background: rgba(255, 255, 255, .9);
  border: 2px dashed rgba(168, 85, 247, .4);
  box-shadow: 0 8px 22px rgba(118, 40, 130, .12);
}
[data-role="student"] .daily-note.is-done {
  border-style: solid;
  border-color: rgba(16, 185, 129, .5);
  background: linear-gradient(120deg, rgba(236,253,245,.95), rgba(254,252,232,.95));
}

/* ==========================================================================
   VERSION 5: meeting invitations, announcements, voice messages
   ========================================================================== */

/* --- the invitation banner, on every student page ------------------------ */
.invite-banner {
  display: flex;
  align-items: center;
  gap: 1.1rem;
  flex-wrap: wrap;
  padding: 1.05rem 1.3rem;
  margin-bottom: 1.2rem;
  border-radius: var(--radius);
  border: 2px solid rgba(139, 92, 246, .45);
  background: linear-gradient(120deg, #f4f0ff 0%, #eef6ff 100%);
  box-shadow: 0 10px 26px rgba(80, 40, 140, .14);
}
.invite-banner.is-live {
  border-color: rgba(232, 51, 109, .55);
  background: linear-gradient(120deg, #fff0f5 0%, #fff6ef 100%);
}
.invite-mark { font-size: 1.9rem; line-height: 1; flex: none; }
.invite-body { flex: 1; min-width: 220px; }
.invite-kicker {
  font-size: .76rem;
  font-weight: 800;
  letter-spacing: .6px;
  text-transform: uppercase;
  color: #6d28d9;
}
.invite-banner.is-live .invite-kicker { color: var(--primary-dark); }
.invite-title { font-size: 1.12rem; font-weight: 800; color: var(--text); }
.invite-when { font-size: .88rem; color: var(--text-muted); }
.invite-actions { display: flex; gap: .5rem; flex-wrap: wrap; }

/* --- a notice from the teacher ------------------------------------------- */
.notice-banner {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
  padding: 1.05rem 1.3rem;
  margin-bottom: 1rem;
  border-radius: var(--radius);
  border: 2px solid var(--info-border);
  background: var(--info-soft);
}
.notice-banner.is-important {
  border-color: var(--warning-border);
  background: var(--warning-soft);
}
.notice-mark { font-size: 1.5rem; line-height: 1; flex: none; }
.notice-body { flex: 1; min-width: 0; }
.notice-kicker {
  font-size: .74rem;
  font-weight: 800;
  letter-spacing: .5px;
  text-transform: uppercase;
  color: var(--text-muted);
}
.notice-title { font-size: 1.05rem; font-weight: 800; color: var(--text); margin: .1rem 0 .3rem; }
.notice-text { font-size: .94rem; line-height: 1.55; color: var(--text); }
.notice-dismiss { flex: none; }

/* --- the "who is this for?" tick-list ------------------------------------ */
.picker {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-2);
  padding: 1rem 1.1rem;
  margin: .4rem 0 1.1rem;
}
.picker-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .6rem;
  margin-bottom: .7rem;
}
.picker-count { font-size: .85rem; color: var(--text-muted); font-weight: 700; }
.picker-group { margin-bottom: .9rem; }
.picker-group-head {
  display: flex;
  align-items: center;
  gap: .5rem;
  flex-wrap: wrap;
  margin-bottom: .45rem;
}
.picker-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: .35rem .8rem;
}
.picker-item {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .4rem .55rem;
  border-radius: var(--radius-sm);
  background: var(--surface);
  border: 1px solid var(--border);
  font-size: .9rem;
  cursor: pointer;
}
.picker-item:hover { border-color: var(--primary-border); }
.picker-item input { width: 17px; height: 17px; flex: none; accent-color: var(--primary); }

/* --- attendance codes ----------------------------------------------------- */
.code-pill {
  display: inline-block;
  font-family: "Courier New", Courier, monospace;
  font-weight: 800;
  letter-spacing: 2px;
  font-size: .95rem;
  padding: .2rem .55rem;
  border-radius: 8px;
  background: var(--primary-soft);
  border: 1px solid var(--primary-border);
  color: var(--primary-darker);
}
.code-input {
  font-family: "Courier New", Courier, monospace;
  font-size: 1.25rem;
  font-weight: 800;
  letter-spacing: 4px;
  text-transform: uppercase;
  text-align: center;
  max-width: 230px;
}
.attend-form { margin-top: 1rem; padding-top: .9rem; border-top: 1px dashed var(--border-strong); }
.attend-row { display: flex; gap: .6rem; flex-wrap: wrap; align-items: center; }

/* --- one meeting on the student's page ------------------------------------ */
.meeting-card.is-done { border-color: var(--success-border); }
.meeting-when { font-size: 1rem; margin-bottom: .4rem; }
.meeting-agenda { white-space: pre-line; color: var(--text-muted); }
.meeting-links { display: flex; gap: .7rem; align-items: center; flex-wrap: wrap; margin: .6rem 0; }
.meeting-meta { font-size: .87rem; color: var(--text-muted); }
.meeting-done { color: var(--success); font-weight: 700; margin: .6rem 0 0; }

/* --- the little count on a meeting tab ------------------------------------ */
.tab-count {
  display: inline-block;
  margin-left: .35rem;
  font-size: .75rem;
  font-weight: 800;
  opacity: .8;
}

/* --- an "important" badge -------------------------------------------------- */
.badge-warn {
  background: var(--warning-soft);
  border-color: var(--warning-border);
  color: var(--warning);
}

/* --- recording a voice message --------------------------------------------- */
.voice-box {
  border: 1px dashed var(--primary-border);
  border-radius: var(--radius);
  background: var(--gradient-soft);
  padding: .8rem .95rem;
  margin-bottom: 1rem;
}
.voice-box.voice-off { border-style: solid; border-color: var(--border); background: var(--surface-2); }
.voice-row { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.voice-timer {
  font-family: "Courier New", Courier, monospace;
  font-weight: 800;
  font-size: 1.05rem;
  color: var(--text);
  min-width: 3.2rem;
}
.voice-box.is-recording .voice-timer { color: var(--danger); }
.voice-box.is-recording { border-color: var(--danger); background: var(--danger-soft); }
.voice-preview { display: block; width: 100%; max-width: 330px; margin-top: .6rem; }
.voice-box .hint { display: block; margin-top: .4rem; }

/* a recording inside the thread */
.msg-voice {
  display: flex;
  align-items: center;
  gap: .55rem;
  margin-top: .4rem;
  padding: .5rem .65rem;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  border: 1px solid var(--border);
  max-width: 340px;
}
.msg-voice audio { flex: 1; min-width: 0; height: 34px; }
.voice-ico { font-size: 1rem; flex: none; }
.voice-len { font-size: .8rem; font-weight: 700; color: var(--text-muted); flex: none; }

@media (max-width: 560px) {
  .invite-actions { width: 100%; }
  .invite-actions .btn { flex: 1; }
  .msg-voice { max-width: 100%; }
}

/* ===========================================================================
   Notice shown in place of the compose box
   ===========================================================================
   Used when a message cannot be written where the reader is looking, so the
   box is never offered only to refuse what is typed into it.
   ------------------------------------------------------------------------ */

.limit-note {
  display: flex;
  align-items: flex-start;
  gap: .9rem;
  padding: 1rem 1.15rem;
  border-radius: var(--radius);
  border: 2px dashed var(--warning-border);
  background: var(--warning-soft);
  color: #92400e;
}
.limit-ico { font-size: 1.5rem; line-height: 1; flex: none; }
.limit-note strong { display: block; margin-bottom: .2rem; }
.limit-note p { font-size: .92rem; line-height: 1.5; }

/* ===========================================================================
   v8 - Messages as a section of its own
   ===========================================================================
   The conversation used to sit at the bottom of a submission page. It now has
   its own entry in the menu, with the list of conversations beside it.
   ------------------------------------------------------------------------ */

/* The doorway card left behind on the submission page. */
.chat-entry .card-pad p { margin: 0 0 .85rem; }

/* Student name / month / day above the open thread. */
.thread-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .5rem;
  margin-bottom: .85rem;
}
.thread-head strong { margin-right: .2rem; }
.thread-head .btn { margin-left: auto; }

/* The list of conversations. */
.thread-list { max-height: 30rem; overflow-y: auto; }
.thread-row {
  display: block;
  padding: .7rem 1.1rem;
  border-left: 3px solid transparent;
  color: inherit;
  text-decoration: none;
}
.thread-row:hover { background: var(--surface-2); }
.thread-row.is-open {
  background: var(--surface-2);
  border-left-color: var(--primary);
}
.thread-row.is-waiting { border-left-color: var(--warning); }

.thread-when {
  display: flex;
  align-items: center;
  gap: .4rem;
  flex-wrap: wrap;
}
.thread-snippet {
  margin: .25rem 0 .2rem;
  font-size: .88rem;
  color: var(--text-muted);
  overflow-wrap: anywhere;
}
.thread-meta {
  display: flex;
  align-items: center;
  gap: .5rem;
  flex-wrap: wrap;
  font-size: .8rem;
  color: var(--text-muted);
}

@media (max-width: 720px) {
  .thread-list { max-height: none; }
}

/* ===========================================================================
   v8b - one running conversation
   ===========================================================================
   Messages are read back as a single list rather than a thread per piece of
   work, with a quiet divider where the talk moved on to a new day.
   ------------------------------------------------------------------------ */

.chat-wide .chat { max-height: 34rem; }

/* Which piece of work the next message will be filed against. */
.chat-filing {
  display: flex;
  align-items: center;
  gap: .5rem;
  flex-wrap: wrap;
  margin: 0 0 .75rem;
  font-size: .9rem;
  color: var(--text-muted);
}

/* The divider between one day's work and the next, inside the stream. */
.msg-divider {
  display: flex;
  align-items: center;
  gap: .7rem;
  margin: 1.1rem 0 .9rem;
  color: var(--text-muted);
  font-size: .8rem;
}
.msg-divider::before,
.msg-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}
.msg-divider span {
  flex: none;
  padding: .18rem .7rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  white-space: nowrap;
}
.msg-divider:first-child { margin-top: 0; }

/* A student who has not written yet - reachable, but visibly quiet. */
.thread-row.is-silent .thread-snippet { font-style: italic; opacity: .75; }
.thread-row.is-silent strong { font-weight: 600; }


/* ---------------------------------------------------------------------------
   Header on small screens
   ---------------------------------------------------------------------------
   The account group is always visible now, so on a phone it keeps only what
   is needed: the avatar to reach your account, and the way out. The name and
   the role chip are in the menu you are already looking at.
   ------------------------------------------------------------------------ */
@media (max-width: 899px) {
  .topbar-right { gap: .35rem; }
  .nav-user .nav-username,
  .nav-user .role-chip { display: none; }
  .nav-user .nav-link { padding: .25rem; }
  .lang-switch span,
  .lang-switch { font-size: .82rem; }
}

@media (max-width: 420px) {
  /* Tighter still: the globe alone says what the button does. */
  .lang-switch { padding: .3rem .45rem; font-size: 0; gap: 0; }
  .lang-switch svg { width: 17px; height: 17px; }
}

/* ---------------------------------------------------------------------------
   Header: making room on ordinary laptop widths
   ---------------------------------------------------------------------------
   The teacher has ten destinations. The tagline and the roomy link padding
   are what push the nav onto extra lines, so they go first - the links
   themselves stay readable.

   This applies at every desktop width on purpose. `.topbar-inner` is capped
   at 1180px like the rest of the page, so a wider screen hands the header no
   extra room; switching these trims off above some breakpoint only made a
   1500px window look worse than a 1250px one.
   ------------------------------------------------------------------------ */
@media (min-width: 900px) {
  .brand-tag { display: none; }
  /* Let the title use two short lines instead of one long one: it costs no
     height once the tagline is gone, and hands ~120px back to the nav. */
  .brand-name {
    white-space: normal;
    overflow: visible;
    max-width: 190px;
    font-size: .92rem;
    line-height: 1.15;
  }
  .nav-link { padding: .42rem .55rem; font-size: .86rem; gap: .38rem; }
  .nav-ico { font-size: .9rem; }
  .nav-username { max-width: 96px; }
}

/* Teachers know they are the teacher; the chip only costs room. A student's
   chip shows their generation, which is worth keeping. */
@media (min-width: 900px) {
  [data-role="teacher"] .nav-user .role-chip { display: none; }
}

/* ===========================================================================
   v10 - writing to several students at once
   ------------------------------------------------------------------------ */

/* Badge and button side by side in a page heading. */
.page-head-actions {
  display: flex;
  align-items: center;
  gap: .6rem;
  flex-wrap: wrap;
}

/* The filter row above the picker groups. */
.picker-filters {
  display: flex;
  align-items: center;
  gap: .4rem;
  flex-wrap: wrap;
  padding: .7rem 1rem;
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
}

/* The little marker beside a name in the picker. */
.picker-flag {
  margin-left: auto;
  font-size: .72rem;
  flex: none;
}

/* The "which day" row above the batch message form. */
.day-picker {
  display: flex;
  align-items: center;
  gap: .6rem;
  flex-wrap: wrap;
  margin-bottom: .6rem;
}
.day-picker label { margin: 0; font-weight: 600; }
.day-picker input[type="date"] { width: auto; min-width: 160px; }
