/* ============================================================================
   LAYOUT — the app shell.

   Desktop  full sidebar beside the content
   Tablet   same shell, tighter spacing
   Mobile   sidebar becomes a drawer, content goes full width
   [KICKOFF 6.4]
   ============================================================================ */

.app {
  display: grid;
  grid-template-columns: var(--sidebar-w) 1fr;
  min-height: 100vh;
}

/* ---------------------------------------------------------------- sidebar */

.sidebar {
  grid-row: 1 / -1;
  background-color: var(--sidebar-bg);
  background-image: var(--sidebar-bg-image);
  color: var(--sidebar-ink);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  z-index: var(--z-sidebar);

  /* A phone's address bar collapses as you scroll, and 100vh is the height
     WITHOUT it — taller than the screen you are actually looking at. The
     footer holding Sign out therefore sat below the fold, and scrolling the
     menu never brought it back, because the menu is not what was overflowing.
     dvh follows the visible area instead. 100vh stays as the fallback for
     browsers that predate it. */
  height: 100vh;
  height: 100dvh;
}

.brand {
  flex-shrink: 0;
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-5) var(--space-5);
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.brand-mark {
  width: 34px; height: 34px; border-radius: var(--radius);
  background: var(--white); color: var(--indigo-800);
  display: grid; place-items: center;
  font-weight: var(--weight-bold); font-size: var(--text-md);
  flex-shrink: 0;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

/* ---------------------------------------------------------- the mark, awake

   Tap the logo and the two of them hop, wobble, glance about and blink. It is
   the one piece of this software that does nothing useful, and that is the
   point: a school portal is opened every morning by people who did not choose
   it, and a small kindness costs nothing.

   `is-awake` is added on tap and taken off when the animation ends, so it can
   be set off again immediately. Everything below is inert until then — the
   sidebar does not animate on load, and nothing moves while anybody is
   reading.

   SVG children need `transform-box: fill-box` for a transform to be measured
   against the shape's own box rather than the whole canvas; without it the
   blink scales from the viewBox origin and the eyes slide off the face. */
.brand-glyph .brand-eye,
.brand-glyph .brand-pupil {
  transform-box: fill-box;
  transform-origin: center;
}

@keyframes brand-hop {
  0%   { transform: translateY(0) rotate(0deg); }
  16%  { transform: translateY(-5px) rotate(-8deg); }
  32%  { transform: translateY(-6px) rotate(7deg); }
  48%  { transform: translateY(0) rotate(-5deg); }
  64%  { transform: translateY(-2px) rotate(4deg); }
  80%  { transform: translateY(0) rotate(-2deg); }
  100% { transform: translateY(0) rotate(0deg); }
}

/* Two blinks: one on the way up, one as they settle. A single blink in the
   middle of a hop is not read as a blink at all. */
@keyframes brand-blink {
  0%, 26%, 44%, 68%, 88%, 100% { transform: scaleY(1); }
  32%, 38%                     { transform: scaleY(0.1); }
  74%, 80%                     { transform: scaleY(0.1); }
}

@keyframes brand-look {
  0%   { transform: translate(0, 0); }
  22%  { transform: translate(-2.2px, 1.2px); }
  46%  { transform: translate(2.4px, -1.4px); }
  72%  { transform: translate(1.2px, 1.6px); }
  100% { transform: translate(0, 0); }
}

.is-awake .brand-glyph { animation: brand-hop 900ms cubic-bezier(0.28, 0.84, 0.42, 1); }
.is-awake .brand-eye   { animation: brand-blink 900ms ease-in-out; }
.is-awake .brand-pupil { animation: brand-look 900ms ease-in-out; }

/* Somebody who has asked their computer to stop moving things has asked for
   this too. The tap still works, it simply does nothing. */
@media (prefers-reduced-motion: reduce) {
  .is-awake .brand-glyph,
  .is-awake .brand-eye,
  .is-awake .brand-pupil { animation: none; }
}
.brand-name { color: var(--white); font-weight: var(--weight-semi); font-size: var(--text-md); }
.brand-school {
  font-size: var(--text-xs); color: var(--sidebar-ink); opacity: 0.8;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.nav { flex: 1; overflow-y: auto; padding: var(--space-4) var(--space-3); }
.nav-section {
  font-size: var(--text-xs); text-transform: uppercase; letter-spacing: 0.06em;
  color: rgba(255, 255, 255, 0.45);
  padding: var(--space-4) var(--space-3) var(--space-2);
}
.nav-link {
  display: flex; align-items: center; gap: var(--space-3);
  min-height: var(--touch);
  padding: var(--space-3);
  border-radius: var(--radius);
  color: var(--sidebar-ink);
  font-size: var(--text-sm); font-weight: var(--weight-medium);
  text-decoration: none;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.nav-link:hover { background: rgba(255, 255, 255, 0.08); color: var(--white); text-decoration: none; }
.nav-link[aria-current="page"] {
  background: var(--sidebar-active); color: var(--white);
  /* A lit edge beside the page you are on — cyan on the dark theme. */
  box-shadow: inset 3px 0 0 var(--sidebar-active-edge), var(--sidebar-glow);
}
.nav-icon { width: 20px; text-align: center; flex-shrink: 0; opacity: 0.9; }
.nav-badge {
  margin-left: auto;
  background: var(--white); color: var(--indigo-800);
  font-size: var(--text-xs); font-weight: var(--weight-bold);
  padding: 1px var(--space-2); border-radius: var(--radius-full);
}

.sidebar-foot {
  flex-shrink: 0;
  padding: var(--space-4);
  /* Phones with a home bar draw it over the bottom of the page. Without this
     the Sign out button is behind it — present, but not tappable. */
  padding-bottom: max(var(--space-4), env(safe-area-inset-bottom, 0px));
  border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.sidebar-user { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-3); }
.sidebar-user .avatar { background: rgba(255,255,255,0.15); color: var(--white); }
.sidebar-user-name { color: var(--white); font-size: var(--text-sm); font-weight: var(--weight-medium); }
.sidebar-user-role { font-size: var(--text-xs); opacity: 0.75; }

/* ---------------------------------------------------------------- topbar */

.topbar {
  position: sticky; top: 0; z-index: var(--z-sticky);
  display: flex; align-items: center; gap: var(--space-4);
  min-height: var(--topbar-h);
  padding: 0 var(--space-6);
  /* Frosted over the page as it scrolls beneath, on the dark theme. */
  background: var(--surface-glass);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line);
}
.topbar h1 { font-size: var(--text-md); font-weight: var(--weight-semi); }
.topbar-actions { margin-left: auto; display: flex; align-items: center; gap: var(--space-2); }

.icon-btn {
  position: relative;
  width: var(--touch); height: var(--touch);
  display: grid; place-items: center;
  background: none; border: 0; border-radius: var(--radius);
  color: var(--ink-soft); cursor: pointer; font-size: 18px;
}
.icon-btn:hover { background: var(--surface-sunk); color: var(--ink); }

/* The bell is drawn rather than typed.
   It used to be a four-pointed star glyph, which reads as "sparkle" or "new"
   in most fonts and as an empty box in a few. An inline SVG is the same shape
   on every machine, takes its colour from the button, and cannot be missing. */
.bell-icon {
  width: 21px; height: 21px;
  vertical-align: middle;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.icon-btn .count {
  position: absolute; top: 6px; right: 6px;
  min-width: 17px; height: 17px; padding: 0 4px;
  background: var(--danger); color: var(--ink-invert);
  border-radius: var(--radius-full);
  font-size: 10px; font-weight: var(--weight-bold);
  display: grid; place-items: center;
}

.menu-toggle { display: none; }

/* ---------------------------------------------------------------- content */

.main { display: flex; flex-direction: column; min-width: 0; }

.content {
  flex: 1;
  padding: var(--space-6);
  max-width: var(--content-max);
  width: 100%;
}

.page-head { margin-bottom: var(--space-6); }
.page-title { font-size: var(--text-xl); font-weight: var(--weight-bold); }
.page-sub { color: var(--ink-soft); margin-top: var(--space-1); }

.section { margin-bottom: var(--space-8); }
.section-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--space-3); margin-bottom: var(--space-4);
}
.section-title { font-size: var(--text-md); font-weight: var(--weight-semi); }

/* ---------------------------------------------------------------- auth pages */

/* Login is its own shell: centred card on a deep indigo field, no navigation. */
.auth {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: var(--space-6);
  background: var(--auth-bg);
}
.auth-card {
  width: 100%; max-width: 420px;
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.auth-head { padding: var(--space-8) var(--space-8) var(--space-5); text-align: center; }
/* A white tile, matching the app's sign-in screen and the sidebar. The mark
   carries its own deep indigo now, so the brand-coloured tile this used to be
   would swallow the front arch. */
.auth-logo {
  width: 54px; height: 54px; margin: 0 auto var(--space-4);
  border-radius: var(--radius-lg);
  background: var(--white); color: var(--indigo-800);
  border: 1px solid var(--line);
  display: grid; place-items: center;
  font-size: var(--text-lg); font-weight: var(--weight-bold);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.auth-title { font-size: var(--text-lg); font-weight: var(--weight-bold); }
.auth-sub { color: var(--ink-soft); font-size: var(--text-sm); margin-top: var(--space-2); }
.auth-body { padding: 0 var(--space-8) var(--space-8); }
/* Who made Libesole: three labelled lines, one under the other, so the name,
   the number and the address each read as their own fact instead of running
   together into one sentence. The number and the address are links — on a
   phone they dial and open the mail app. */
.founder {
  display: grid; justify-items: center;
  gap: var(--space-1);
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
}
.founder-line {
  display: flex; flex-wrap: wrap; justify-content: center; align-items: baseline;
  gap: var(--space-2);
  font-size: var(--text-sm);
}
.founder-label { color: var(--ink-muted); }
.founder-name { font-weight: var(--weight-semi); color: var(--ink); }
.founder-link {
  color: var(--primary);
  min-height: var(--touch); display: inline-flex; align-items: center;
}
.founder-link:hover { text-decoration: underline; }

.auth-foot {
  padding: var(--space-4) var(--space-8);
  background: var(--surface-alt);
  border-top: 1px solid var(--line);
  text-align: center;
  font-size: var(--text-sm);
  color: var(--ink-muted);
}

/* ---------------------------------------------------------------- responsive */

@media (max-width: 1024px) {
  :root { --sidebar-w: 220px; }
  .content { padding: var(--space-5); }
}

@media (max-width: 860px) {
  .app { grid-template-columns: 1fr; }

  .menu-toggle { display: grid; }

  /* The sidebar becomes a drawer. It is off-canvas, not hidden, so the
     browser keeps it in the tab order only when open. */
  .sidebar {
    position: fixed; inset: 0 auto 0 0;
    width: min(280px, 82vw);
    transform: translateX(-100%);
    transition: transform var(--normal) var(--ease);
    box-shadow: var(--shadow-lg);
  }
  .sidebar.is-open { transform: none; }

  /* Without dvh the drawer can still be taller than the screen. Then the whole
     drawer scrolls as one, rather than the menu scrolling inside a box whose
     bottom you cannot reach. */
  @supports not (height: 100dvh) {
    .sidebar { overflow-y: auto; -webkit-overflow-scrolling: touch; }
    .nav { flex: 0 0 auto; overflow-y: visible; }
  }

  .scrim {
    position: fixed; inset: 0;
    background: rgba(19, 28, 40, 0.5);
    opacity: 0; pointer-events: none;
    transition: opacity var(--normal) var(--ease);
    z-index: var(--z-overlay);
  }
  .scrim.is-open { opacity: 1; pointer-events: auto; }
  .sidebar.is-open { z-index: calc(var(--z-overlay) + 1); }

  .topbar { padding: 0 var(--space-4); }
  .content { padding: var(--space-4); }
  .page-title { font-size: var(--text-lg); }
}

@media (max-width: 560px) {
  .card-head, .card-body { padding: var(--space-4); }
  .card-foot { padding: var(--space-3) var(--space-4); }
  .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; }
  .stat-value { font-size: var(--text-xl); }
  .auth-head { padding: var(--space-6) var(--space-5) var(--space-4); }
  .auth-body { padding: 0 var(--space-5) var(--space-6); }
  /* Buttons stack rather than squeezing side by side. */
  .btn-row { display: grid; gap: var(--space-2); }
}

/* ----------------------------------------------------------------------------
   TOUCH TARGETS                                              [KICKOFF 6.4]

   A few controls are deliberately compact so that dense screens — the marks
   sheet, the attendance register, a row of filter pills — fit without
   scrolling. At 36px and 40px they are comfortable under a mouse and too small
   under a thumb, and the rule asks for 44px.

   So the compact sizes are kept for a fine pointer on a wide screen, and every
   one of them grows to the full 44px as soon as the pointer is coarse or the
   screen is small. Nothing here changes what a control looks like on a laptop.
   -------------------------------------------------------------------------- */

/* "See the full report" beside a heading is a target in its own right, not a
   word inside a sentence, so it gets a height of its own instead of the 22px
   its text happens to occupy. The negative margin keeps the text optically
   aligned with the edge despite the padding. */
.section-head > a:not(.btn),
.card-head > a:not(.btn) {
  display: inline-flex;
  align-items: center;
  min-height: 28px;
  padding: 0 var(--space-1);
  margin-right: calc(var(--space-1) * -1);
}

@media (pointer: coarse), (max-width: 860px) {
  .btn-sm {
    min-height: var(--touch);
    padding: var(--space-2) var(--space-4);
  }
  .pill-tab { min-height: var(--touch); }
  .toast-close { width: var(--touch); height: var(--touch); }

  .section-head > a:not(.btn),
  .card-head > a:not(.btn) { min-height: var(--touch); }
}

/* ============================================================================
   ON PAPER

   Several pages here end up printed and handed to somebody: a date sheet on a
   noticeboard, a salary sheet beside a cheque book, an attendance record in a
   file. Printing used to mean "the screen, without the buttons", which came
   out as a page with no heading, form boxes around every figure, and — for
   anybody reading in the dark theme — white text on a black rectangle.

   So paper is treated as its own medium: always light, headed with the school
   and what the page is, and stripped of anything that only makes sense to
   somebody who can click.
   ============================================================================ */

@media print {
  /* The paper palette itself lives in tokens.css, like every other colour in
     this project. What is here is only which parts of the page go to paper and
     what shape they take on it. */

  @page { margin: 14mm; }

  body { background: var(--surface); }

  /* Everything that is only useful to somebody who can click. */
  .sidebar, .topbar, .toast-region, .btn, .icon-btn, .scrim,
     /* The page head is the screen's title bar — its heading, its filters and
        its buttons. On paper the print head above says all of that, and the
        filters print as empty form controls nobody can use. */
  .page-head, .pill-tabs, .field-help, .how,
  .screen-only { display: none !important; }

  .print-only { display: inline !important; }
  div.print-only, p.print-only { display: block !important; }

  .app { grid-template-columns: 1fr; }
  .content { padding: 0; max-width: none; }

  /* One heading on the paper, from the shell: the school, what this is, and
     when it was printed. It is the only thing on the page a reader did not
     already have on screen, and without it a printout is anonymous. */
  .print-head {
    display: block !important;
    margin-bottom: var(--space-4);
    padding-bottom: var(--space-3);
    border-bottom: 2px solid var(--ink);
  }
  .print-head .print-school { font-size: var(--text-lg); font-weight: var(--weight-bold); }
  .print-head .print-what { font-size: var(--text-md); margin-top: 2px; }
  .print-head .print-when { font-size: var(--text-xs); color: var(--ink-muted); margin-top: 2px; }

  /* A card on paper is a section, not a floating box. */
  .card {
    border: 1px solid var(--line);
    box-shadow: none;
    break-inside: avoid;
    margin-bottom: var(--space-4);
  }

  /* A figure somebody typed is a figure, not a box to type in. The borders and
     the pale fill say "fill this in", which on paper is a lie. */
  input, select, textarea {
    border: none !important;
    background: transparent !important;
    padding: 0 !important;
    min-height: 0 !important;
    color: var(--ink) !important;
    -webkit-appearance: none;
    appearance: none;
    font-weight: var(--weight-semi);
  }

  /* Tables are the reason most of these pages are printed at all. */
  table { border-collapse: collapse; width: 100%; }
  thead { display: table-header-group; }      /* the headings repeat on every sheet */
  tr { break-inside: avoid; }
  th, td {
    border-bottom: 1px solid var(--line);
    padding: var(--space-2) var(--space-3);
  }
  th { border-bottom: 2px solid var(--ink); text-align: left; }
  .table-wrap { overflow: visible; }          /* never a scrollbar's worth of cut-off table */

  /* A link's colour means nothing on paper, and an underline reads as a mark. */
  a { color: var(--ink); text-decoration: none; }
}
