/*
  Hand-written admin stylesheet for the EduERP shell.
  No build step, no CDN, works fully offline: design tokens + plain CSS only.
*/

/* ------------------------------------------------------------------ tokens */

:root {
  color-scheme: light;

  /* Neutrals */
  --bg: #f4f6fb;
  --surface: #ffffff;
  --surface-2: #fafbff;
  --border: #e4e8f1;
  --border-strong: #cfd7e6;
  --text: #101828;
  --text-soft: #344054;
  --muted: #6b7691;

  /* Brand */
  --accent: #4f46e5;
  --accent-2: #7c3aed;
  --accent-ink: #ffffff;
  --accent-soft: #eef1fe;
  --accent-hover: #4338ca;
  --grad-accent: linear-gradient(135deg, #4f46e5 0%, #6d3ae8 55%, #7c3aed 100%);

  /* Semantic */
  --green: #047857;
  --green-soft: #e7f7ef;
  --green-line: #b6e6cf;
  --amber: #a35a06;
  --amber-soft: #fff5e0;
  --amber-line: #f5dfae;
  --red: #c8203a;
  --red-soft: #fdecef;
  --red-line: #f6c6cf;
  --sky: #0d6b96;
  --sky-soft: #e6f4fb;
  --grey-soft: #eef0f5;

  /* Ink on the dark shell */
  --shell: #0e1428;
  --shell-2: #17203c;
  --shell-ink: #c3cde4;
  --shell-ink-soft: #7f8db3;

  /* Shape + depth */
  --r-xs: 6px;
  --r-sm: 9px;
  --r: 13px;
  --r-lg: 18px;
  --pill: 999px;
  --shadow-xs: 0 1px 2px rgba(16, 24, 40, .05);
  --shadow: 0 1px 2px rgba(16, 24, 40, .05), 0 8px 22px -14px rgba(16, 24, 40, .18);
  --shadow-md: 0 2px 4px rgba(16, 24, 40, .05), 0 16px 32px -18px rgba(16, 24, 40, .28);
  --shadow-lg: 0 24px 60px -26px rgba(16, 24, 40, .38);
  --ring: 0 0 0 3px rgba(var(--accent-rgb), .22);
  --ring-red: 0 0 0 3px rgba(200, 32, 58, .2);

  /* Surfaces that a theme may re-tint wholesale */
  --glass: rgba(255, 255, 255, .82);
  --tint: #f7f9fd;
  --tint-2: #fcfdff;
  --hover: #f3f6fc;
  --hover-line: #b9c4da;
  --accent-line: #d6ddf7;
  --accent-rgb: 79, 70, 229;
  --accent-edge: rgba(var(--accent-rgb), .3);
  --accent-tint-2: #eef1fe;
  --stripe: rgba(15, 23, 42, .014);
  --placeholder: #9aa5bd;
  --scroll: #c6cee0;
  --scroll-hover: #a9b4cc;
  --select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236b7691' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");

  /* Dark shell */
  --shell-3: #1b2647;
  --shell-glow: rgba(124, 58, 237, .38);
  --shell-active-a: rgba(99, 91, 255, .95);
  --shell-active-b: rgba(124, 58, 237, .85);
  --shell-mark: #a78bfa;

  /* Page glows behind the content */
  --glow-a: rgba(124, 58, 237, .09);
  --glow-b: rgba(var(--accent-rgb), .07);

  /* Motion */
  --ease: cubic-bezier(.22, .61, .36, 1);
  --swing: cubic-bezier(.16, .84, .44, 1);
  --fast: .16s;
  --mid: .3s;
  --slow: .48s;

  /* Comfortable defaults: one rem is deliberately larger than the browser's 16px
     baseline times nothing — every size below is in rem, so this single number is
     what makes the whole screen bigger or smaller without touching a rule. */
  --base: 15px;

  /* A label never reads as data, so a badge inside a monospace cell keeps the UI face. */
  --ui-font: system-ui, -apple-system, "Segoe UI Variable Text", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  /* Shell measures */
  --pad: 1.7rem;
  --rail: 292px;
  --header: 76px;
  --touch: 44px;
}

* { box-sizing: border-box; }

/* A rule with a display on it — .btn, .card — would otherwise win the tie against the
   browser's own [hidden] and leave a switched-off control on the screen. */
[hidden] { display: none !important; }

html { scroll-behavior: smooth; }

html { font-size: var(--base); }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font: 1rem/1.55 var(--ui-font);
  font-feature-settings: "cv02", "ss01", "tnum" 0;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

::selection { background: rgba(var(--accent-rgb), .18); }

body {
  background-image:
    radial-gradient(1100px 420px at 100% -8%, var(--glow-a), transparent 60%),
    radial-gradient(900px 400px at 12% 0%, var(--glow-b), transparent 62%);
  background-attachment: fixed;
}

/* Slim, quiet scrollbars */
* { scrollbar-width: thin; scrollbar-color: var(--scroll) transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-thumb { background: var(--scroll); border: 3px solid transparent; background-clip: content-box; border-radius: 999px; }
*::-webkit-scrollbar-thumb:hover { background: var(--scroll-hover); background-clip: content-box; }

a { color: var(--accent); text-decoration: none; transition: color var(--fast) var(--ease); }
a:hover { color: var(--accent-hover); text-decoration: none; }

h1, h2, h3 { margin: 0 0 .55rem; font-weight: 680; line-height: 1.25; letter-spacing: -.016em; }
h1 { font-size: 1.62rem; }
h2 { font-size: 1.12rem; }
h3 { font-size: 1.02rem; }
p { margin: 0 0 .65rem; }
small { color: var(--muted); font-size: .82rem; }
hr { border: 0; border-top: 1px solid var(--border); margin: 1rem 0; }

svg.ic { width: 18px; height: 18px; flex: 0 0 18px; stroke: currentColor; fill: none; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* ------------------------------------------------------------------ shell */

.shell { display: flex; min-height: 100vh; }

.sidebar {
  width: var(--rail);
  flex: 0 0 var(--rail);
  background: linear-gradient(178deg, var(--shell) 0%, var(--shell-2) 62%, var(--shell-3) 100%);
  color: var(--shell-ink);
  padding: 1.3rem 1rem 1.15rem;
  display: flex;
  flex-direction: column;
  gap: .2rem;
  position: sticky;
  top: 0;
  align-self: flex-start;
  height: 100vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* The page asks the browser to glide to an anchor; the rail being brought to the open page is not
     an anchor, and a menu that slides on arrival reads as the page moving. */
  scroll-behavior: auto;
  border-right: 1px solid rgba(255, 255, 255, .06);
  /* Folding is a width change, and a width that snaps makes the page jump under the eye. */
  transition: width var(--mid) var(--ease), flex-basis var(--mid) var(--ease);
}
.sidebar::after {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 210px;
  background: radial-gradient(200px 130px at 22% -10%, var(--shell-glow), transparent 70%);
  pointer-events: none;
}

.brand {
  display: flex;
  align-items: center;
  gap: .7rem;
  padding: .35rem .6rem 1.15rem;
  position: relative;
  z-index: 1;
}
.brand-name { display: grid; font-size: 1.1rem; font-weight: 720; color: #fff; letter-spacing: -.016em; line-height: 1.2; }
.brand-tag { font-size: .66rem; font-weight: 600; color: var(--shell-ink-soft); letter-spacing: .1em; text-transform: uppercase; margin-top: .22rem; }
.brand-mark {
  width: 42px; height: 42px; flex: 0 0 42px;
  border-radius: 13px;
  background: var(--grad-accent);
  box-shadow: 0 10px 20px -9px rgba(124, 58, 237, .9), inset 0 1px 0 rgba(255, 255, 255, .35);
  display: grid; place-items: center;
  color: #fff; font-weight: 800; font-size: 1.05rem;
}
.brand-mark svg { width: 23px; height: 23px; stroke: #fff; fill: none; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }

/* A hotel's own mark is drawn for white paper, so it sits on a light plate. */
.brand-mark.branded {
  width: auto; height: 44px; flex: 0 0 auto; min-width: 44px; max-width: 104px;
  padding: 4px 6px;
  border-radius: 12px;
  background: #fff;
  box-shadow: 0 10px 20px -11px rgba(2, 6, 23, .85), inset 0 0 0 1px rgba(2, 6, 23, .07);
}
.brand-mark.branded img { display: block; max-width: 100%; max-height: 36px; width: auto; height: auto; object-fit: contain; }

/* Each block of links is a section the clerk can shut, so its heading is the control that does it.
   `.nav-body` is `display: contents` while open, which puts the links straight back into the rail's
   own column and keeps the rhythm exactly as it was; only a shut section takes them out. */
.nav-sec { display: flex; flex-direction: column; gap: .2rem; }
.nav-body { display: contents; }
.nav-sec.closed .nav-body { display: none; }

.nav-group {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .4rem;
  width: 100%;
  margin-top: 1.4rem;
  font-family: inherit;
  font-size: .69rem;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--shell-ink-soft);
  padding: 0 .7rem .45rem;
  font-weight: 700;
  text-align: left;
  position: relative;
  z-index: 1;
  background: none;
  border: 0;
  cursor: pointer;
  transition: color var(--fast) var(--ease);
}
.nav-group:hover { color: #fff; }
.nav-group:focus-visible { outline: none; color: #fff; border-radius: 7px; box-shadow: 0 0 0 2px rgba(255, 255, 255, .35); }
.nav-group .chev {
  width: 13px; height: 13px; flex: 0 0 13px;
  stroke: currentColor; fill: none; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round;
  opacity: .5; transition: transform var(--mid) var(--ease), opacity var(--fast) var(--ease);
}
.nav-group:hover .chev { opacity: 1; }
/* Pointing sideways is the whole explanation of what the block behind it now is. */
.nav-sec.closed .nav-group .chev { transform: rotate(-90deg); }

.nav-link {
  display: flex;
  align-items: center;
  gap: .72rem;
  min-height: 44px;
  padding: .58rem .78rem;
  border-radius: var(--r-sm);
  color: var(--shell-ink);
  font-size: .925rem;
  font-weight: 540;
  position: relative;
  z-index: 1;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease),
              transform var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
.nav-link .ic { width: 20px; height: 20px; flex: 0 0 20px; opacity: .82; transition: opacity var(--fast) var(--ease), transform var(--mid) var(--swing); }
.nav-link:hover { background: rgba(255, 255, 255, .08); color: #fff; text-decoration: none; transform: translateX(3px); }
.nav-link:hover .ic { transform: scale(1.08); }
.nav-link:focus-visible { outline: none; box-shadow: 0 0 0 2px rgba(255, 255, 255, .55); color: #fff; }
.nav-link:active { transform: translateX(3px) scale(.99); }
.nav-link.active {
  background: linear-gradient(100deg, var(--shell-active-a), var(--shell-active-b));
  color: #fff;
  font-weight: 660;
  box-shadow: 0 8px 16px -13px rgba(var(--accent-rgb), .8), inset 0 1px 0 rgba(255, 255, 255, .22);
  transform: none;
}
.nav-link.active::before {
  content: "";
  position: absolute;
  left: -11px; top: 20%; bottom: 20%;
  width: 4px;
  border-radius: 0 4px 4px 0;
  background: var(--shell-mark);
}
.nav-link.active .ic { opacity: 1; }

.sidebar-foot { margin-top: auto; padding: 1rem .35rem .1rem; position: relative; z-index: 1; }
.btn-shell {
  width: 100%;
  display: flex; align-items: center; justify-content: center; gap: .55rem;
  min-height: var(--touch);
  background: rgba(255, 255, 255, .06);
  border: 1px solid rgba(255, 255, 255, .13);
  color: #dbe3f4;
  border-radius: var(--r-sm);
  padding: .55rem .9rem;
  font: inherit;
  font-size: .885rem;
  font-weight: 620;
  cursor: pointer;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease),
              border-color var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.btn-shell:hover { background: rgba(255, 255, 255, .15); color: #fff; border-color: rgba(255, 255, 255, .3); transform: translateY(-1px); }
.btn-shell:active { transform: translateY(0) scale(.99); }
.btn-shell:focus-visible { outline: none; box-shadow: 0 0 0 2px rgba(255, 255, 255, .5); }

/* ------------------------------------------------------------------ folded rail */

/* The menu is only wanted wide while someone is choosing where to go. Folded, the rail keeps its
   marks and the active bar and hands its width back to the table beside it. The words are never
   removed from the page — a folded link still says what it is to a screen reader, and a pointer
   gets them as a tooltip — they are only taken out of the line. */

/* html.rail-folded, not .rail-folded: the narrower-screen rules set --rail on :root further down
   the file, and only the element-plus-class form outscores them wherever it lands. */
@media (min-width: 1025px) {
  html.rail-folded { --rail: 78px; }

  .rail-folded .sidebar { padding-left: .85rem; padding-right: .85rem; }

  /* The mark stays — a school recognises its own logo before it reads a word — and the fold button
   takes the line under it once there is no room beside it. */
  .rail-folded .brand { flex-direction: column; align-items: center; gap: .5rem; padding-bottom: .9rem; }
  .rail-folded .brand-name { display: none; }
  /* A school's mark is usually a wide wordmark, and `width: auto` on the plate lets the picture
     choose the plate's size — so both halves are named here, or the logo spills past the rail. */
  .rail-folded .brand-mark.branded { width: 46px; min-width: 0; max-width: 46px; height: 38px; padding: 4px; }
  .rail-folded .brand-mark.branded img { max-width: 38px; max-height: 30px; }
  .rail-folded .rail-fold { margin-left: 0; }

  /* A heading with no room for its word becomes the rule that divides one block of marks
     from the next, so the grouping a clerk reads is still on the screen. */
  .rail-folded .nav-group {
    height: 1px; overflow: hidden; margin: 1rem .35rem .45rem; padding: 0;
    background: rgba(255, 255, 255, .13);
    pointer-events: none;
  }
  /* A hairline cannot be a handle, so nothing stays shut while the rail is folded. The marks are the
     short list already, and hiding any of them would put a screen out of reach. */
  .rail-folded .nav-sec.closed .nav-body { display: contents; }

  .rail-folded .nav-link { justify-content: center; gap: 0; padding-left: 0; padding-right: 0; }
  .rail-folded .nav-link:hover,
  .rail-folded .nav-link:active { transform: none; }
  .rail-folded .sidebar-foot { padding-left: 0; padding-right: 0; }
  .rail-folded .btn-shell { padding-left: 0; padding-right: 0; }

  /* Out of the line, into the announcement. */
  .rail-folded .nav-text {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden;
    clip-path: inset(50%); white-space: nowrap; border: 0;
  }
}

/* The button that folds the rail. */
.rail-fold {
  margin-left: auto;
  width: 30px; height: 30px; flex: 0 0 30px;
  display: grid; place-items: center;
  background: rgba(255, 255, 255, .07);
  border: 1px solid rgba(255, 255, 255, .14);
  border-radius: 9px;
  color: var(--shell-ink);
  cursor: pointer;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease),
              transform var(--fast) var(--ease);
}
.rail-fold:hover { background: rgba(255, 255, 255, .17); color: #fff; }
.rail-fold:active { transform: scale(.94); }
.rail-fold:focus-visible { outline: none; box-shadow: 0 0 0 2px rgba(255, 255, 255, .5); }
.rail-fold svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 1.9;
                 stroke-linecap: round; stroke-linejoin: round;
                 transition: transform var(--mid) var(--swing); }
/* Pointing the other way is the whole explanation of what pressing it now does. */
.rail-folded .rail-fold svg { transform: rotate(180deg); }

/* Nothing above this line reaches a phone: the folded rail is a wide-screen idea, and the drawer
   has its own way of getting out of the way. */
@media (max-width: 1024px) {
  .rail-fold { display: none; }
}

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

.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--glass);
  backdrop-filter: saturate(160%) blur(12px);
  -webkit-backdrop-filter: saturate(160%) blur(12px);
  border-bottom: 1px solid var(--border);
  padding: .85rem var(--pad);
  display: flex;
  align-items: center;
  gap: 1.1rem;
  justify-content: space-between;
  min-height: var(--header);
}
.topbar .who { display: flex; align-items: center; gap: .7rem; color: var(--muted); font-size: .84rem; text-align: left; }
.topbar .who strong { display: block; color: var(--text); font-size: .93rem; font-weight: 650; line-height: 1.3; }
.topbar .avatar {
  width: 42px; height: 42px; flex: 0 0 42px;
  border-radius: var(--pill);
  background: var(--grad-accent);
  color: #fff;
  display: grid; place-items: center;
  font-size: .85rem; font-weight: 700; letter-spacing: .02em;
  box-shadow: 0 10px 18px -10px rgba(var(--accent-rgb), .9);
  transition: transform var(--mid) var(--swing), box-shadow var(--mid) var(--ease);
}
.topbar .who:hover .avatar { transform: scale(1.06) rotate(-3deg); box-shadow: 0 14px 22px -10px rgba(var(--accent-rgb), .95); }
.topbar .prop-name { font-weight: 670; font-size: 1.02rem; letter-spacing: -.014em; }

/* Property switcher pill */
.prop-switch, .prop-static {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  min-height: 44px;
  padding: .4rem .6rem .4rem .95rem;
  border: 1px solid var(--border);
  border-radius: var(--pill);
  background: var(--surface);
  box-shadow: var(--shadow-xs);
  color: var(--muted);
  transition: border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
.prop-switch { padding-right: .4rem; }
.prop-switch:hover, .prop-static:hover { border-color: var(--hover-line); box-shadow: var(--shadow); }
.prop-switch select {
  width: auto;
  max-width: 46vw;
  border: 0;
  background: transparent;
  padding: .3rem 1.7rem .3rem .2rem;
  font-weight: 640;
  font-size: .93rem;
  color: var(--text);
  cursor: pointer;
  box-shadow: none;
}
.prop-switch select:focus-visible { box-shadow: var(--ring); border-radius: var(--r-xs); }
.prop-static { padding: .42rem .95rem; }
.prop-static .ic { color: var(--accent); }

/* The shell's jump box: a plain GET form, so it works with no script at all — the
   keyboard shortcut only decides where the thumb lands. */
.topbar-search {
  flex: 1 1 20rem;
  max-width: 27rem;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: .55rem;
  min-height: 44px;
  padding: .35rem .8rem;
  border: 1px solid var(--border);
  border-radius: var(--pill);
  background: var(--surface);
  box-shadow: var(--shadow-xs);
  color: var(--muted);
  transition: border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
.topbar-search:hover { border-color: var(--hover-line); }
.topbar-search:focus-within { border-color: var(--accent); box-shadow: var(--ring); }
.topbar-search .ic { flex: 0 0 auto; }
.topbar-search input {
  flex: 1 1 auto;
  min-width: 0;
  width: auto;
  border: 0;
  background: transparent;
  padding: .3rem 0;
  font-size: .93rem;
  color: var(--text);
  box-shadow: none;
}
.topbar-search input:focus, .topbar-search input:focus-visible { outline: none; box-shadow: none; }
/* The native clear button sits on top of our own kbd hint and cannot be recoloured. */
.topbar-search input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
.topbar-search kbd {
  flex: 0 0 auto;
  font-family: inherit;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .02em;
  color: var(--muted);
  background: var(--grey-soft);
  border: 1px solid var(--border);
  border-radius: var(--r-xs);
  padding: .1rem .42rem;
}

/* On a phone the topbar is already spent, so the box gives way to one icon that
   opens the same hunt on its own page. */
.search-jump {
  display: none;
  place-items: center;
  width: 42px; height: 42px;
  border-radius: var(--pill);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--muted);
  box-shadow: var(--shadow-xs);
  transition: color var(--fast) var(--ease), border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
.search-jump:hover { color: var(--accent); border-color: var(--accent-line); box-shadow: var(--shadow); }

/* ------------------------------------------------------------------ search */

.search-form { display: flex; align-items: flex-end; gap: .7rem; flex-wrap: wrap; }
.search-form .field { flex: 1 1 16rem; min-width: 0; }
.search-hint { margin: .85rem 0 0; font-size: .84rem; }
.search-hint kbd {
  font-family: inherit;
  font-size: .78rem;
  font-weight: 700;
  color: var(--muted);
  background: var(--grey-soft);
  border: 1px solid var(--border);
  border-radius: var(--r-xs);
  padding: .08rem .38rem;
}

.hit-group > header { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.hit-group > header h2 { font-size: 1.02rem; }
.hits { list-style: none; margin: 0; padding: 0; }
.hits li + li { border-top: 1px solid var(--border); }
.hits .hit {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding: .72rem .8rem;
  border-radius: var(--r-sm);
  color: var(--text);
  text-decoration: none;
  transition: background var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.hits .hit:hover { background: var(--hover); transform: translateX(3px); }
.hits .hit:focus-visible { outline: none; box-shadow: var(--ring); }
.hit-line { font-weight: 620; min-width: 0; }
.hit-note { color: var(--muted); font-size: .84rem; flex: 0 0 auto; text-align: right; }
@media (max-width: 640px) {
  .hits .hit { flex-direction: column; align-items: stretch; gap: .15rem; }
  .hit-note { text-align: left; }
}

.content { padding: var(--pad); flex: 1 1 auto; }

/* Entrance for each block on the page, slightly staggered. */
.content > * { animation: fade-up .46s both var(--ease); }
.content > *:nth-child(2) { animation-delay: .05s; }
.content > *:nth-child(3) { animation-delay: .1s; }
.content > *:nth-child(4) { animation-delay: .15s; }
.content > *:nth-child(5) { animation-delay: .2s; }
.content > *:nth-child(n + 6) { animation-delay: .25s; }

@keyframes fade-up {
  from { opacity: 0; transform: translate3d(0, 12px, 0); }
  to   { opacity: 1; transform: none; }
}
@keyframes pop-in {
  from { opacity: 0; transform: scale(.955) translate3d(0, 8px, 0); }
  to   { opacity: 1; transform: none; }
}
@keyframes slide-right {
  from { opacity: 0; transform: translate3d(-14px, 0, 0); }
  to   { opacity: 1; transform: none; }
}
@keyframes grow-x { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes pulse-soft {
  0%, 100% { box-shadow: 0 0 0 0 rgba(var(--accent-rgb), .28); }
  50%      { box-shadow: 0 0 0 7px rgba(var(--accent-rgb), 0); }
}
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes alert-out {
  to { opacity: 0; transform: translate3d(0, -6px, 0); }
}
/* The travelling head of the request bar: one band running left to right. */
@keyframes progress-run {
  0%   { transform: translate3d(-110%, 0, 0) scaleX(.4); }
  50%  { transform: translate3d(15%, 0, 0) scaleX(.85); }
  100% { transform: translate3d(115%, 0, 0) scaleX(.4); }
}

/* ------------------------------------------------------------------ cards */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow);
  margin-bottom: 1rem;
  overflow: hidden;
  transition: box-shadow var(--mid) var(--ease), transform var(--mid) var(--ease), border-color var(--mid) var(--ease);
}
.card > header {
  padding: 1.05rem 1.3rem;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .9rem;
  flex-wrap: wrap;
  background: linear-gradient(180deg, var(--surface-2), rgba(248, 250, 255, .4));
}
.card > header h2, .card > header h3 { margin: 0; }
.card > .body { padding: 1.3rem; }
.card > .body.tight { padding: 0; }

/* Property card heading: the hotel's mark beside its name. */
.logo-head { display: flex; align-items: center; gap: .7rem; min-width: 0; }
.logo-thumb {
  width: 38px; height: 38px; flex: 0 0 38px;
  object-fit: contain;
  padding: 3px;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--r-xs);
  box-shadow: var(--shadow-xs);
}

/* A face beside a name — a pupil on the class register, an employee on the staff list. The
   circle is the same on both screens, so a person with a photo and a person with initials
   line up in one column. */
.person-cell { display: flex; align-items: center; gap: .7rem; min-width: 0; }
.person-photo {
  width: 34px; height: 34px; flex: 0 0 34px;
  object-fit: cover;
  border: 1px solid var(--border);
  border-radius: 50%;
  box-shadow: var(--shadow-xs);
  background: #fff;
}
/* No picture on file: the person's initials hold the circle so the column keeps its rhythm. */
.person-photo-none {
  display: flex; align-items: center; justify-content: center;
  font-size: .72rem; font-weight: 700; letter-spacing: .02em;
  color: var(--text-soft);
  background: var(--tint);
}

/* ------------------------------------------------------------------ one record's face */

/* The banner above a pupil's file, the same pupil's edit form, the application behind them. Every
   door into a record answers "is this the right child?" the same way, instead of leaving a clerk to
   work it out from a form full of boxes. It is painted from the theme tokens, so a school that has
   set its own office colour gets a banner in that colour, and a clerk on Midnight keeps a dark one. */
.rec-head {
  position: relative; display: flex; align-items: center; gap: .9rem 1.3rem; flex-wrap: wrap;
  padding: .95rem 1.15rem .95rem 1.3rem; margin-bottom: 1rem; overflow: hidden;
  border: 1px solid var(--border); border-radius: var(--r); box-shadow: var(--shadow-xs);
  background: radial-gradient(30rem 6rem at 0% 0%, var(--accent-soft), transparent 68%), var(--surface);
}
.rec-head::before { content: ""; position: absolute; inset: 0 auto 0 0; width: 4px; background: var(--grad-accent); }
.rec-id { display: flex; align-items: center; gap: .85rem; min-width: 0; flex: 1 1 22rem; }
.rec-avatar {
  width: 3.35rem; height: 3.35rem; flex: 0 0 3.35rem; border-radius: 50%; object-fit: cover;
  display: flex; align-items: center; justify-content: center;
  font-size: 1.1rem; font-weight: 750; color: var(--accent-ink); background: var(--grad-accent);
  box-shadow: var(--shadow-xs), 0 0 0 3px var(--accent-tint-2);
}
.rec-lines { min-width: 0; }
.rec-name { font-size: 1.32rem; font-weight: 750; letter-spacing: -.02em; line-height: 1.2; }
.rec-chips { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; margin-top: .34rem; }
.rec-chip {
  border: 1px solid var(--accent-line); border-radius: 999px; padding: .1rem .58rem;
  font-size: .74rem; font-weight: 650; color: var(--text); background: var(--accent-tint-2);
}
.rec-meta { margin-top: .32rem; font-size: .82rem; color: var(--text-soft); overflow-wrap: anywhere; }
.rec-facts { display: flex; gap: .5rem 1.15rem; flex-wrap: wrap; margin: 0; }
.rec-fact { min-width: 4.2rem; }
.rec-fact dt { font-size: .66rem; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--text-soft); }
.rec-fact dd { margin: .14rem 0 0; font-size: .92rem; font-weight: 700; }
.rec-actions { display: flex; gap: .45rem; flex-wrap: wrap; margin-left: auto; }
@media (max-width: 900px) {
  .rec-head { align-items: flex-start; }
  .rec-actions { margin-left: 0; width: 100%; }
}

/* ------------------------------------------------------------------ registers */

/* The fee desk: a cleared money box means "not this family today", so the line fades rather
   than looking like a row that failed, and a figure over the balance is shown before the save
   refuses it. */
tbody tr.is-untaken { opacity: .45; }
tbody tr.is-over > td { background: var(--red-soft); }
/* The box a thumb types into keeps its width in a rule, not inline, so a phone can narrow it
   rather than push the right-aligned digits off the screen. The element name is part of the
   selector because the shared field rule below sets a width on every control. */
input.money-box { width: 8.5rem; text-align: right; }

/* The class register: a child down the side, the five words across. The status columns are
   narrowed so a whole section reads on a laptop without sideways scrolling. */
.reg-table { min-width: 68rem; }
.reg-table th, .reg-table td { padding: .5rem .6rem; }
.reg-table th:nth-child(n + 3):nth-child(-n + 7) { text-align: center; }
.reg-table td.reg-cell { text-align: center; white-space: nowrap; }

/* Radios sized for a thumb on a class teacher's tablet, and coloured by the word they stand
   for, so a row marked by two people still reads the same way. */
.reg-dot {
  appearance: none;
  -webkit-appearance: none;
  width: 23px; height: 23px;
  margin: 0;
  border: 1.5px solid var(--border-strong);
  border-radius: 50%;
  background: #fff;
  cursor: pointer;
  vertical-align: middle;
  transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease),
              box-shadow var(--fast) var(--ease);
}
.reg-dot:hover { border-color: var(--hover-line); background: var(--hover); }
.reg-dot:checked { border-color: var(--green); background: var(--green); box-shadow: inset 0 0 0 4px #fff; }
.reg-dot[value="absent"]:checked { border-color: var(--red); background: var(--red); }
.reg-dot[value="late"]:checked,
.reg-dot[value="half_day"]:checked { border-color: var(--amber); background: var(--amber); }
.reg-dot[value="leave"]:checked { border-color: var(--muted); background: var(--muted); }
.reg-dot:focus-visible { outline: none; box-shadow: var(--ring); }

/* Month grid: one letter per child per day, with the name pinned so the row is still
   identifiable after the month scrolls out from under it. */
.reg-grid { min-width: 64rem; font-size: .84rem; }
.reg-grid th, .reg-grid td { padding: .32rem .3rem; text-align: center; white-space: nowrap; }
.reg-grid .grid-name {
  position: sticky; left: 0; z-index: 1;
  min-width: 12rem; max-width: 15rem;
  text-align: left;
  border-right: 1px solid var(--border);
  background: var(--tint);
}
.reg-grid tbody .grid-name { background: var(--surface); }
.reg-grid tbody tr:nth-child(even) .grid-name { background: var(--tint-2); }
.reg-grid tbody tr:hover .grid-name { background: var(--accent-soft); }
.reg-grid .grid-day { width: 1.9rem; font-size: .69rem; font-variant-numeric: tabular-nums; }
/* A day the school did not sit: shaded column, so nobody reads a gap as a child staying away. */
.reg-grid th.is-off { background: var(--grey-soft); color: var(--muted); }
.reg-grid td.grid-cell { font-weight: 700; font-size: .78rem; }
.reg-grid td.is-off { background: var(--grey-soft); }
.reg-grid td.is-blank::after { content: "\00b7"; color: var(--border-strong); font-weight: 600; }

/* ---------------------------------------------------------------- timetable */

/* The week shares the register's pinned-name skeleton, but a box here has to hold a lesson,
   the teacher standing it and the room it sits in, so the day columns are wide and the words
   are allowed to wrap instead of forcing the whole grid sideways. */
.tt-grid { min-width: 52rem; }
.tt-grid th, .tt-grid td { white-space: normal; }
.tt-grid .grid-name { min-width: 10.5rem; max-width: 13rem; }
.tt-grid .grid-day { width: auto; min-width: 9rem; font-size: .72rem; letter-spacing: .08em; padding: .5rem .35rem; }
.tt-grid td.grid-cell { padding: .4rem; font-weight: 500; text-align: left; vertical-align: top; }
.tt-grid tr:nth-child(even) td.grid-cell { background: var(--stripe); }
/* Today's column is the one a teacher looks for first, and on a six-day week it is not the
   first one on the screen. */
.tt-grid th.is-today { color: var(--accent); background: var(--accent-soft); }
.tt-grid td.is-today { background: var(--accent-tint-2); }

.tt-slot, .tt-open {
  display: flex;
  flex-direction: column;
  gap: .1rem;
  width: 100%;
  min-height: 3.5rem;
  padding: .45rem .55rem;
  border: 1px solid var(--accent-line);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--text);
  font-size: .78rem;
  line-height: 1.3;
  box-shadow: var(--shadow-xs);
  transition: transform var(--fast) var(--ease), box-shadow var(--fast) var(--ease),
              border-color var(--fast) var(--ease), background var(--fast) var(--ease);
}
a.tt-slot, a.tt-open { cursor: pointer; }
a.tt-slot:hover, a.tt-open:hover {
  transform: translateY(-2px);
  border-color: var(--hover-line);
  box-shadow: var(--shadow);
  background: var(--hover);
}
.tt-slot:focus-visible, .tt-open:focus-visible { outline: none; box-shadow: var(--ring); }
.tt-slot b { font-weight: 690; letter-spacing: -.01em; }
.tt-slot span { color: var(--muted); font-size: .73rem; }
.tt-slot span.is-none { color: var(--amber); font-style: italic; }
.tt-slot i { color: var(--muted); font-size: .71rem; font-style: normal; }
.tt-slot.is-static { cursor: default; }
.tt-slot.is-saved { animation: tt-taken .9s var(--ease) both; }

/* An empty box invites the click rather than showing a button inside a box that is already a
   button: the whole cell is the target, and it says Fill only when it is tapped open. */
.tt-open {
  align-items: center;
  justify-content: center;
  gap: .25rem;
  flex-direction: row;
  border-style: dashed;
  border-color: var(--border-strong);
  background: transparent;
  box-shadow: none;
  color: var(--muted);
  font-size: .74rem;
  font-weight: 650;
}
.tt-open:hover { color: var(--accent); border-color: var(--accent-edge); }
.tt-open:hover .tt-plus { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.tt-plus {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.15rem;
  height: 1.15rem;
  border-radius: var(--pill);
  border: 1px solid var(--border-strong);
  font-size: .85rem;
  font-weight: 700;
  line-height: 1;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
@keyframes tt-taken {
  0%   { box-shadow: 0 0 0 0 rgba(var(--accent-rgb), .45); }
  60%  { box-shadow: 0 0 0 6px rgba(var(--accent-rgb), 0); }
  100% { box-shadow: var(--shadow-xs); }
}

/* One cell's editor, and the search sheet a large dropdown opens into. Both cover the page
   rather than pushing the week down, because a screen being worked through is held open for
   the forty-odd edits that follow it. */
.tt-modal,
.pk-modal {
  position: fixed;
  inset: 0;
  z-index: 120;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.2rem;
}
/* The week behind the editor does not scroll while it is open: on a phone the sheet covers
   most of the screen, and a flick that lands outside it should not move the grid. */
body.tt-lock,
body.pk-lock { overflow: hidden; }
.tt-shade,
.pk-shade {
  position: absolute;
  inset: 0;
  background: rgba(16, 24, 40, .48);
  backdrop-filter: blur(2px);
  animation: fade-in var(--fast) var(--ease) both;
}
.tt-sheet,
.pk-sheet {
  position: relative;
  width: min(30rem, 100%);
  max-height: calc(100vh - 2.4rem);
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  animation: sheet-in var(--mid) var(--swing) both;
}
.tt-sheet > header,
.pk-sheet > header {
  display: flex;
  align-items: flex-start;
  gap: .8rem;
  padding: 1.05rem 1.1rem .95rem;
  background: var(--grad-accent);
  color: #fff;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
}
.tt-sheet > header > div,
.pk-sheet > header > div { flex: 1 1 auto; min-width: 0; }
.tt-sheet .tt-spot,
.pk-sheet .pk-what {
  display: block;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  opacity: .82;
  overflow-wrap: anywhere;
}
.tt-sheet > header h2,
.pk-sheet > header h2 { margin: .15rem 0 0; font-size: 1.12rem; color: #fff; }
.tt-x,
.pk-x {
  flex: none;
  width: 2rem;
  height: 2rem;
  border: 0;
  border-radius: var(--pill);
  background: rgba(255, 255, 255, .16);
  color: #fff;
  font-size: 1.35rem;
  line-height: 1;
  cursor: pointer;
  transition: background var(--fast) var(--ease);
}
.tt-x:hover,
.pk-x:hover { background: rgba(255, 255, 255, .3); }
.tt-x:focus-visible,
.pk-x:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(255, 255, 255, .5); }
.tt-sheet .body { padding: 1.1rem 1.1rem 0; }
.tt-sheet .fields { gap: 0 .95rem; }
.tt-sheet .form-actions { margin: .2rem 1.1rem 1.1rem; position: static; }
.tt-sheet .form-actions .spacer { flex: 1 1 auto; }
/* A refusal from the school's own rules reads as the sheet's answer, not as a page message
   the user has to be sent back to the grid to look for. */
.tt-refused {
  margin: 0;
  padding: .7rem 1.1rem;
  background: var(--red-soft);
  border-bottom: 1px solid var(--red-line);
  color: var(--red);
  font-size: .85rem;
  font-weight: 600;
}
@keyframes sheet-in {
  from { opacity: 0; transform: translateY(14px) scale(.98); }
  to   { opacity: 1; transform: none; }
}
@keyframes sheet-up {
  from { opacity: 0; transform: translateY(70%); }
  to   { opacity: 1; transform: none; }
}
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }

/* ------------------------------------------------- the week as one form
   Planning a week is a form, not a grid of links: the bells down the side, the days across the
   top, and a subject and a teacher in every box. The columns are held apart by the table's own
   min-width and the .table-scroll around it, because two selects folded over one another read as
   one control and the office saves the wrong week. */
.wk-grid { min-width: 66rem; border-collapse: separate; }
.wk-grid th, .wk-grid td { padding: .45rem .4rem; vertical-align: top; }
.wk-grid thead th { font-size: .72rem; letter-spacing: .08em; text-transform: uppercase; }
.wk-grid .wk-name { width: 9.6rem; min-width: 9.6rem; }
.wk-grid td.wk-cell { min-width: 9.3rem; }
.wk-grid tbody tr:nth-child(even) td { background: var(--stripe); }
/* Six days do not fit a laptop, and a sheet where the bell column has scrolled off the left edge
   is a week of boxes nobody can name. The bell stays put while the days move past it. The stripe
   is translucent, so the held column paints it over the card's own ground rather than over the
   Tuesday underneath it. */
.wk-grid .wk-name {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--surface);
  box-shadow: 1px 0 0 var(--line);
}
.wk-grid tbody tr:nth-child(even) .wk-name { background: linear-gradient(var(--stripe), var(--stripe)), var(--surface); }
.wk-grid thead .wk-name { z-index: 2; }
.wk-pick {
  display: block;
  width: 100%;
  min-height: 34px;
  padding: .26rem 1.4rem .26rem .45rem;
  font-size: .8rem;
  background-position: right .3rem center;
}
.wk-pick + .wk-pick { margin-top: .3rem; }
/* A bell that takes no lesson still shows where it falls in the day, then keeps out of the way. */
.wk-grid tr.wk-off td { opacity: .55; }
.wk-grid td.wk-cell.is-off { text-align: center; }
/* A box whose lesson already has a room: the room is not on this screen, so the box says it kept. */
.wk-grid td.has-room { box-shadow: inset 3px 0 0 var(--accent-edge); }

/* ---- type-to-find picks */

/* The dropdown stays where the form put it — it holds the value and the field name — and the
   button beside it is the visible door to the search. Typing into the focused select opens the
   same sheet with the letters already in it. */
.pk-row { display: flex; align-items: center; gap: .45rem; }
.pk-row > select { flex: 1 1 auto; min-width: 0; }
.pk-btn {
  flex: none;
  border: 1px solid var(--border-strong);
  background: var(--surface-2);
  color: var(--text-soft);
  border-radius: var(--r-sm);
  padding: .45rem .66rem;
  font-size: .76rem;
  font-weight: 700;
  letter-spacing: .02em;
  cursor: pointer;
  transition: border-color var(--fast) var(--ease), color var(--fast) var(--ease);
}
.pk-btn:hover { border-color: var(--accent); color: var(--accent); }
.pk-btn:focus-visible { outline: none; box-shadow: var(--ring); }

/* Header and search stay put while the list scrolls: hunting for a name through four hundred
   lines should not move the letters being typed off screen. */
.pk-sheet { display: flex; flex-direction: column; overflow: hidden; }
.pk-search {
  display: flex;
  align-items: center;
  gap: .45rem;
  padding: .85rem 1.1rem;
  border-bottom: 1px solid var(--border);
  background: var(--tint);
}
.pk-search input { flex: 1 1 auto; min-width: 0; margin: 0; }
.pk-clear {
  flex: none;
  border: 0;
  background: none;
  color: var(--muted);
  font-size: 1.2rem;
  line-height: 1;
  padding: .2rem .35rem;
  cursor: pointer;
}
.pk-clear:hover { color: var(--red); }
.pk-list {
  flex: 1 1 auto;
  overflow-y: auto;
  min-height: 7rem;
  padding: .3rem 0 .5rem;
  -webkit-overflow-scrolling: touch;
}
.pk-group {
  padding: .55rem 1.1rem .2rem;
  font-size: .68rem;
  font-weight: 800;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--muted);
}
.pk-opt {
  display: flex;
  align-items: baseline;
  gap: .6rem;
  padding: .55rem 1.1rem;
  border-left: 3px solid transparent;
  font-size: .92rem;
  color: var(--text);
  cursor: pointer;
}
.pk-opt > span { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.pk-opt > b {
  flex: none;
  font-size: .64rem;
  font-weight: 800;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--accent);
}
.pk-opt:hover { background: var(--hover); }
/* The row the arrows are on, which is the row Enter takes. */
.pk-opt.is-at { background: var(--accent-soft); border-left-color: var(--accent); }
.pk-opt mark { background: var(--amber-soft); color: inherit; border-radius: 3px; padding: 0 1px; }
.pk-empty { margin: 0; padding: 1.2rem 1.1rem; color: var(--muted); font-size: .88rem; }
.pk-sheet > footer {
  display: flex;
  align-items: center;
  gap: .7rem;
  padding: .7rem 1.1rem;
  border-top: 1px solid var(--border);
  background: var(--surface);
}
.pk-count { flex: 1 1 auto; font-size: .78rem; color: var(--muted); }
.pk-none { flex: none; font-size: .8rem; padding: .42rem .8rem; }

.c-green { color: var(--green); }
.c-red { color: var(--red); }
.c-amber { color: var(--amber); }
.c-grey { color: var(--muted); }
.c-mute { color: var(--text-soft); }

/* Footer strip for row-level actions (property cards, detail panels). */
.card-foot {
  display: flex;
  align-items: center;
  gap: .55rem;
  flex-wrap: wrap;
  padding: .9rem 1.3rem;
  border-top: 1px dashed var(--border);
  background: var(--surface-2);
}
.card-foot .spacer { flex: 1 1 auto; }
.card-foot .sweep { flex: 1 1 100%; margin: 0; justify-content: flex-end; font-size: .82rem; color: var(--muted); }
.card-foot form { display: inline-flex; align-items: center; gap: .5rem; flex-wrap: wrap; justify-content: flex-end; }
.card:hover { box-shadow: var(--shadow-md); }
.card.is-active { border-color: var(--accent-line); box-shadow: 0 0 0 1px var(--accent-edge), var(--shadow); }
.grid > .card:hover { transform: translateY(-3px); }

/* ---------------------------------------------------------------- headings */

.page-head { display: flex; justify-content: space-between; align-items: flex-end; gap: 1rem; margin-bottom: 1.35rem; flex-wrap: wrap; }
.page-head h1 { font-size: 1.72rem; }
.page-head .sub { color: var(--muted); font-size: .89rem; margin-top: .15rem; }
.page-head .actions { display: flex; gap: .55rem; flex-wrap: wrap; align-items: center; }

/* ------------------------------------------------------------------ grids */

.grid { display: grid; gap: 1.1rem; }
.grid.cols-2 { grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); }
.grid.cols-3 { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
.grid.cols-4 { grid-template-columns: repeat(auto-fit, minmax(212px, 1fr)); }

.stat {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 1.05rem 1.15rem 1.2rem;
  box-shadow: var(--shadow);
  overflow: hidden;
  transition: transform var(--mid) var(--ease), box-shadow var(--mid) var(--ease), border-color var(--mid) var(--ease);
  animation: pop-in .44s both var(--ease);
}
.stat::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 4px;
  background: var(--grad-accent);
  opacity: .9;
  transform-origin: left center;
  animation: grow-x .7s var(--ease) both;
}
.stat:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); border-color: var(--accent-line); }
.stat .label { text-transform: uppercase; letter-spacing: .075em; font-size: .69rem; color: var(--muted); font-weight: 700; }
.stat .value { font-size: 1.95rem; font-weight: 720; margin-top: .3rem; letter-spacing: -.034em; font-variant-numeric: tabular-nums; color: var(--text); }
.stat .foot { font-size: .81rem; color: var(--muted); margin-top: .2rem; }
.stat.tone-green::before { background: linear-gradient(90deg, #047857, #10b981); }
.stat.tone-amber::before { background: linear-gradient(90deg, #b45309, #f59e0b); }
.stat.tone-red::before { background: linear-gradient(90deg, #be123c, #f43f5e); }
.stat.tone-sky::before { background: linear-gradient(90deg, #0369a1, #38bdf8); }
.stat.tone-violet::before { background: linear-gradient(90deg, #6d28d9, #a78bfa); }
.stat.tone-slate::before { background: linear-gradient(90deg, #334155, #94a3b8); }

/* ------------------------------------------------------------------ tables */

.table-scroll { overflow-x: auto; }
/* A table wider than its card has to swipe sideways. Below a phone's edge the rest of
   the columns are simply never drawn, and nothing tells you they are there. */
.card > .body:has(table) { overflow-x: auto; }
/* app.js puts this on any box that still has ground to give, and takes it off at the
   end of the run: a fading right edge is the only hint that more columns follow. */
.can-swipe {
  background-image: linear-gradient(90deg, transparent, rgba(16, 24, 40, .08));
  background-repeat: no-repeat;
  background-size: 26px 100%;
  background-position: right center;
}
/* The promote plan carries a destination select in every row, and a select will shrink to
   almost nothing to keep a table inside its card — "Class 2…" is not a choice the office can
   read. The row is held wide instead and the .table-scroll above it swipes. */
table.promote-plan { min-width: 44rem; }
table.promote-plan select { min-width: 17rem; }
table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: .92rem; }
th, td { padding: .78rem .95rem; text-align: left; border-bottom: 1px solid var(--border); vertical-align: middle; }
th {
  background: var(--tint);
  font-weight: 680;
  font-size: .735rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--muted);
  white-space: nowrap;
}
tbody tr { transition: background var(--fast) var(--ease), box-shadow var(--fast) var(--ease); }
tbody tr:nth-child(even) { background: var(--tint-2); }
tbody tr:hover { background: var(--accent-soft); }
tbody tr:last-child > td { border-bottom: 0; }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
td a { font-weight: 570; }
/* Definition rows (a th sitting beside its value) read as labels, not column heads. */
tbody th {
  width: 32%;
  background: transparent;
  font-weight: 640;
  font-size: .86rem;
  text-transform: none;
  letter-spacing: 0;
  color: var(--muted);
  white-space: normal;
}
tbody tr:has(th):hover { background: transparent; }
.empty { padding: 3.2rem 1rem; text-align: center; color: var(--muted); font-size: .93rem; background: repeating-linear-gradient(180deg, transparent, transparent 24px, var(--stripe) 24px, var(--stripe) 48px); }
.empty strong { display: block; color: var(--text-soft); font-size: 1.02rem; margin-bottom: .25rem; }

/* ------------------------------------------------------------------ badges */

.badge {
  display: inline-flex;
  align-items: center;
  gap: .38rem;
  padding: .23rem .68rem;
  border-radius: var(--pill);
  font-size: .77rem;
  font-family: var(--ui-font);
  font-weight: 660;
  letter-spacing: .005em;
  white-space: nowrap;
  background: var(--grey-soft);
  color: var(--text-soft);
  border: 1px solid transparent;
  vertical-align: middle;
}
.badge::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; opacity: .75; flex: 0 0 6px; }
.badge:empty::before { display: none; }
.badge-green { background: var(--green-soft); color: var(--green); border-color: var(--green-line); }
.badge-amber { background: var(--amber-soft); color: var(--amber); border-color: var(--amber-line); }
.badge-red { background: var(--red-soft); color: var(--red); border-color: var(--red-line); }
.badge-grey { background: var(--grey-soft); color: var(--muted); border-color: var(--border); }
.badge-live { animation: pulse-soft 2.4s ease-in-out infinite; }

/* ------------------------------------------------------------------ tabs */

/* A row of links that divides one screen into its subjects. It scrolls sideways on a phone
   rather than wrapping, so the page underneath keeps a single heading. */
.tabs {
  display: flex;
  align-items: flex-end;
  gap: .2rem;
  margin: -.2rem 0 1.1rem;
  padding: 0 .1rem;
  border-bottom: 1px solid var(--border);
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  padding: .62rem .85rem .68rem;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  font-size: .875rem;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
  text-decoration: none;
  border-radius: var(--r-sm) var(--r-sm) 0 0;
}
.tab:hover { color: var(--text); background: var(--surface-2); }
.tab.is-active { color: var(--accent); border-bottom-color: var(--accent); background: var(--accent-soft); }
.tab:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* The count beside a tab is the reason to open it, so it keeps the badge's colour and
   loses its dot - inside a tab the dot reads as a second label. */
.tab-badge { padding: .1rem .45rem; font-size: .72rem; }
.tab-badge::before { display: none; }

/* ------------------------------------------------------------------ buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .45rem;
  min-height: 40px;
  padding: .55rem 1.05rem;
  border-radius: var(--r-sm);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text-soft);
  font: inherit;
  font-size: .885rem;
  font-weight: 630;
  line-height: 1.3;
  /* A button whose own label folds into three lines is not a button any more. */
  white-space: nowrap;
  cursor: pointer;
  box-shadow: var(--shadow-xs);
  transition: background var(--fast) var(--ease), border-color var(--fast) var(--ease),
              color var(--fast) var(--ease), transform var(--fast) var(--ease),
              box-shadow var(--fast) var(--ease), filter var(--fast) var(--ease);
}
.btn:hover { background: var(--hover); border-color: var(--hover-line); color: var(--text); text-decoration: none; transform: translateY(-2px); box-shadow: var(--shadow); }
.btn:active { transform: translateY(0) scale(.98); box-shadow: none; }
.btn:focus-visible { outline: none; box-shadow: var(--ring); border-color: var(--accent); }
.btn-primary { background: var(--grad-accent); border-color: transparent; color: var(--accent-ink); box-shadow: 0 10px 20px -12px rgba(var(--accent-rgb), .95); }
.btn-primary:hover { background: var(--grad-accent); filter: brightness(1.07); color: #fff; border-color: transparent; box-shadow: 0 14px 26px -12px rgba(var(--accent-rgb), .95); }
.btn-primary:focus-visible { box-shadow: var(--ring), 0 10px 20px -12px rgba(var(--accent-rgb), .95); }
.btn-danger { background: var(--surface); border-color: var(--red-line); color: var(--red); }
.btn-danger:hover { background: var(--red); border-color: var(--red); color: #fff; box-shadow: 0 12px 22px -14px rgba(200, 32, 58, .95); }
.btn-danger:focus-visible { box-shadow: var(--ring-red); }
.btn-danger-solid { background: linear-gradient(135deg, #dc2b45, #be123c); border-color: transparent; color: #fff; }
.btn-danger-solid:hover { background: linear-gradient(135deg, #e0394f, #c9184a); border-color: transparent; color: #fff; }
.btn-sm { padding: .34rem .72rem; font-size: .815rem; min-height: 34px; border-radius: var(--r-xs); gap: .36rem; }
.btn-sm .ic { width: 15px; height: 15px; flex: 0 0 15px; }
.btn[disabled], .btn:disabled { opacity: .48; cursor: not-allowed; transform: none; box-shadow: none; filter: none; }
/* The button that sent the form spins until the response lands. */
.btn.is-busy { pointer-events: none; }
.btn.is-busy::after {
  content: "";
  width: 14px; height: 14px; flex: 0 0 14px;
  border-radius: 50%;
  border: 2px solid currentColor;
  border-top-color: transparent;
  animation: spin .7s linear infinite;
}
form.inline { display: inline; }

/* ------------------------------------------------------------------ forms */

.field { margin-bottom: 1.1rem; }
.field > label { display: block; font-size: .825rem; font-weight: 660; color: var(--text-soft); margin-bottom: .35rem; letter-spacing: .005em; }
.field .req { color: var(--red); font-weight: 700; }
.input, select, textarea {
  width: 100%;
  min-height: 42px;
  padding: .58rem .85rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  background: var(--surface);
  font: inherit;
  font-size: .93rem;
  color: var(--text);
  transition: border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease), background var(--fast) var(--ease);
}
.input::placeholder, textarea::placeholder { color: var(--placeholder); }
.input:hover, select:hover, textarea:hover { border-color: var(--hover-line); }
.input:focus, select:focus, textarea:focus { outline: none; border-color: var(--accent); box-shadow: var(--ring); background: var(--surface); }
select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 2.15rem;
  background-image: var(--select-arrow);
  background-repeat: no-repeat;
  background-position: right .72rem center;
}
textarea.input { min-height: 100px; resize: vertical; }
input[type="date"], input[type="time"] { min-height: 42px; }

/* A body the office formats. The toolbar sits on the top edge of the box it writes into so the
   words never move when a mark is applied, and the plain textarea underneath is put away only once
   app.js has taken over — it marks the field .is-source — so a browser running no script is left
   with the box it can already type in. */
.rich { display: flex; flex-direction: column; }
.rich-src.is-source { display: none; }
.rich-bar { display: flex; flex-wrap: wrap; align-items: center; gap: .15rem; padding: .3rem .35rem;
  border: 1px solid var(--border-strong); border-bottom: 0; border-radius: var(--r-sm) var(--r-sm) 0 0;
  background: var(--surface-2); }
.rich-btn { min-height: 28px; padding: .1rem .55rem; border: 1px solid transparent; border-radius: var(--r-xs);
  background: transparent; color: var(--text-soft); font: inherit; font-size: .8rem; line-height: 1.5;
  cursor: pointer; transition: background var(--fast) var(--ease), color var(--fast) var(--ease); }
.rich-btn:hover { background: var(--hover); border-color: var(--border); color: var(--text); }
.rich-btn.is-on { background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent); }
.rich-gap { width: 1px; align-self: stretch; margin: .2rem .25rem; background: var(--border); }
.rich-face { min-height: 150px; padding: .58rem .85rem; border: 1px solid var(--border-strong);
  border-radius: 0 0 var(--r-sm) var(--r-sm); background: var(--surface); font-size: .93rem;
  color: var(--text); overflow-wrap: break-word; }
.rich-face:focus { outline: none; border-color: var(--accent); box-shadow: var(--ring); }
.rich-face[data-placeholder]:empty::before { content: attr(data-placeholder); color: var(--placeholder); }

/* A notice read back: the marks the editor can make, and nothing else. The bottom margins are
   taken off the first and last line so the text lines up with the card's own padding. The same
   rules cover the editing surface, so a clerk marks a notice in the shape it will be read in. */
.rich-text > :first-child, .rich-face > :first-child { margin-top: 0; }
.rich-text > :last-child, .rich-face > :last-child { margin-bottom: 0; }
.rich-text p, .rich-face p { margin: 0 0 .6rem; }
.rich-text ul, .rich-text ol, .rich-face ul, .rich-face ol { margin: 0 0 .6rem; padding-left: 1.4rem; }
.rich-text li, .rich-face li { margin: .18rem 0; }
.rich-text a, .rich-face a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
.rich-text strong, .rich-text b, .rich-face strong, .rich-face b { font-weight: 700; }

/* A file picker in its own box, matching the other controls. */
input[type="file"] {
  min-height: 44px;
  font-size: .88rem;
  color: var(--muted);
  cursor: pointer;
}
input[type="file"]::file-selector-button {
  margin: 0 .7rem 0 0;
  padding: .4rem .85rem;
  border: 1px solid var(--accent-line);
  border-radius: var(--r-xs);
  background: var(--accent-tint-2);
  color: var(--accent);
  font: inherit;
  font-size: .85rem;
  font-weight: 660;
  cursor: pointer;
  transition: background var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
input[type="file"]:hover::file-selector-button { background: var(--accent-soft); border-color: var(--accent-edge); }

/* Logo picker on the property form: the current mark beside the control. */
.logo-row { display: flex; align-items: flex-start; gap: 1.2rem; flex-wrap: wrap; }
.logo-row > .field { flex: 1 1 300px; margin-bottom: 0; }
.logo-preview {
  flex: 0 0 auto;
  width: 170px; height: 104px;
  display: block;
  object-fit: contain;
  padding: .55rem;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow-xs);
}
.logo-preview-empty {
  display: grid; place-items: center;
  color: var(--muted);
  font-size: .78rem;
  border-style: dashed;
  border-color: var(--border-strong);
  box-shadow: none;
}
.checkline { display: inline-flex; align-items: center; gap: .55rem; font-size: .9rem; color: var(--text-soft); cursor: pointer; min-height: 30px; }
.checkline input { width: auto; accent-color: var(--accent); height: 17px; }
input[type="checkbox"], input[type="radio"] { cursor: pointer; }

/* Bulk toolbar sitting above a table with selectable rows. */
.bulk-bar {
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  padding: .72rem 1.05rem; margin-bottom: .85rem;
  background: linear-gradient(180deg, var(--surface-2), var(--accent-tint-2));
  border: 1px solid var(--accent-line);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow-xs);
  animation: fade-up .38s both var(--ease);
}
.bulk-bar .input, .bulk-bar select { width: auto; min-height: 36px; padding: .3rem .7rem; font-size: .85rem; }
.bulk-bar > :last-child { margin-left: auto; }
.bulk-bar b { font-variant-numeric: tabular-nums; color: var(--accent); font-weight: 720; }
.bulk-bar .checkline { font-size: .87rem; }
.bulk-cell { width: 2.8rem; text-align: center; }
.bulk-cell input { width: auto; height: 17px; }

.hint { color: var(--muted); font-size: .82rem; margin-top: .35rem; }
.fields { display: grid; gap: 0 1.15rem; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
.fields .full { grid-column: 1 / -1; }

/* One tap onto the day before or after the register on screen. The date field stays, because
   a hop answers "yesterday" and the field answers "the 14th". */
.day-hops {
  display: flex;
  gap: .4rem;
  flex-wrap: wrap;
  align-items: center;
  margin: -.55rem 0 1.1rem;
}
.day-hop {
  display: inline-flex;
  align-items: baseline;
  gap: .4rem;
  padding: .4rem .75rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--pill);
  background: var(--surface);
  color: var(--text-soft);
  font-size: .78rem;
  text-decoration: none;
  transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease),
    color var(--fast) var(--ease);
}
.day-hop b { font-weight: 700; }
.day-hop span { font-size: .72rem; color: var(--muted); }
.day-hop:hover {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--accent);
}
.day-hop:hover span { color: var(--accent); }
.day-hop:focus-visible { outline: none; box-shadow: var(--ring); }
/* The day already open: same shape so the row reads as a run of three days, no journey in it. */
.day-hop.is-now {
  background: var(--accent-soft);
  border-color: var(--accent-line);
  color: var(--accent);
  cursor: default;
}

/* A key on the screen that means a key on the keyboard. Used in two places: over a register
   column it is the number that files that day, and in a card foot it spells out the walker. */
.kbd {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 1.15rem; height: 1.15rem; padding: 0 .28rem;
  border: 1px solid var(--border-strong); border-bottom-width: 2px; border-radius: var(--r-xs);
  background: var(--surface-2); color: var(--muted);
  font-family: ui-monospace, "Cascadia Mono", "SF Mono", Consolas, monospace;
  font-size: .68rem; font-weight: 600; line-height: 1; white-space: nowrap;
}
/* On a column header the cap sits to the right of the word and stays quiet until the eye
   reaches it — the name of the day matters more than its number. */
th .kbd { margin-left: .3rem; opacity: .65; vertical-align: middle; }
th:hover .kbd { opacity: 1; color: var(--accent); border-color: var(--accent-line); }

/* ---------------------------------------------------- which hotel am I editing */

/* Screens whose data belongs to one hotel name that hotel, so two identical
   looking lists are never mistaken for one shared list. */
/* The band is the whole card on a scoped screen, so it carries no inner rule. */
.scope-bar {
  display: flex; align-items: center; justify-content: space-between; gap: .75rem; flex-wrap: wrap;
  padding: .95rem 1.3rem;
  background: var(--accent-soft);
}
.scope-who { display: flex; align-items: center; gap: .6rem; min-width: 0; flex-wrap: wrap; font-size: .9rem; color: var(--muted); }
.scope-who .scope-name { color: var(--text); font-size: 1.06rem; }
.scope-who .mono { color: var(--muted); }
.scope-bar .prop-switch { background: var(--surface); }
.list-hotel { font-size: .84rem; font-weight: 560; color: var(--muted); white-space: nowrap; }

/* -------------------------------------------------------------- staff access */

/* Jobs as chips, so a growing role list stays one short band instead of a column. */
.check-chips { display: flex; flex-wrap: wrap; gap: .45rem; }
.check-chip {
  display: inline-flex; align-items: center; gap: .5rem;
  min-height: 38px;
  padding: .35rem .8rem .35rem .62rem; border-radius: var(--pill);
  border: 1px solid var(--border-strong); background: var(--surface);
  font-size: .87rem; font-weight: 620; color: var(--text-soft); cursor: pointer;
  transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease),
              color var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.check-chip input { width: auto; height: 16px; margin: 0; accent-color: var(--accent); }
.check-chip:hover { border-color: var(--accent-edge); background: var(--accent-tint-2); transform: translateY(-1px); }
.check-chip:has(input:checked) { border-color: var(--accent-line); background: var(--accent-soft); color: var(--accent); }

/* A scope with nothing to choose still reads as a filled field, not a missing one. */
.pick-static {
  display: flex; align-items: center; min-height: 42px; padding: .4rem .85rem;
  border: 1px dashed var(--border-strong); border-radius: var(--r-sm);
  background: var(--surface-2); font-size: .9rem; font-weight: 620; color: var(--text-soft);
}

/* One job at one hotel. A pill per grant keeps the staff list narrow however many
   hotels the platform carries, where a role x hotel grid did not. */
.access-pills { display: flex; flex-wrap: wrap; gap: .35rem; margin-bottom: .5rem; }
.access-pill {
  display: inline-flex; align-items: baseline; gap: .4rem;
  padding: .22rem .65rem; border-radius: var(--pill);
  border: 1px solid var(--accent-line); background: var(--accent-tint-2);
  font-size: .78rem; font-weight: 660; color: var(--accent); white-space: nowrap;
}
.access-pill .where { color: var(--muted); font-weight: 540; }
.access-pill .where::before { content: "@"; opacity: .65; margin-right: .15rem; }

td.cell-form, td.cell-access { vertical-align: top; }

.filters {
  display: flex; gap: .85rem; flex-wrap: wrap; align-items: flex-end;
  padding: 1.05rem 1.2rem;
  background: linear-gradient(180deg, var(--surface-2), var(--surface));
}
.filters .field { margin: 0; min-width: 168px; }
/* A .fields grid left at its own content width inside a flex filter bar folds its columns over
   one another, which is how the class and the section end up stacked in a narrow strip with the
   whole bar empty beside them. Given room to grow, the columns sit side by side as they do on a
   plain form. */
.filters > .fields { flex: 1 1 26rem; }
/* A filter bar's buttons belong to the bar, not to the bottom of a form: the raised, sticky panel
   the one below a stack of fields wears is a second box inside the bar, and being taller than the
   fields it drags them to the bottom of the panel and leaves a dead band above them. The same flat
   row is what `.plain` asks for on a card that is only a sentence and one button. */
.filters > .form-actions,
.form-actions.plain {
  position: static; margin-top: 0; padding: 0; border: 0; border-radius: 0;
  background: none; box-shadow: none; backdrop-filter: none; -webkit-backdrop-filter: none;
}
.form-actions.plain { margin-top: .9rem; }

/* Only ever shown on narrow screens, where the panel above would fill a screen. */
.filters-toggle {
  display: none; align-items: center; justify-content: space-between; gap: .6rem;
  width: 100%; min-height: var(--touch);
  padding: .5rem .8rem;
  border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--surface-2); color: var(--ink);
  font: inherit; font-weight: 650; letter-spacing: .01em;
  cursor: pointer;
  transition: background var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.filters-toggle::after {
  content: ""; flex: 0 0 9px; width: 9px; height: 9px;
  border-right: 2px solid var(--muted); border-bottom: 2px solid var(--muted);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform var(--mid) var(--swing);
}
.filters-toggle:active { transform: scale(.995); }
.body.filters-open .filters-toggle::after { transform: rotate(-135deg) translate(-2px, -2px); }

/* ------------------------------------------------------------------ alerts */

.alerts { margin-bottom: 1.25rem; display: grid; gap: .65rem; }
.alert {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: .7rem;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: .9rem 1rem .9rem 1.15rem;
  font-size: .9rem;
  overflow: hidden;
  animation: slide-right .38s both var(--ease);
  box-shadow: var(--shadow-xs);
}
.alert::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 4px; background: currentColor; opacity: .85; }
/* An alert that opens by naming a record puts that name in a <strong>, which flex lays out as its
   own item — and squeezes it to two words a line. Let it keep its own width, and let the sentence
   under it have the whole line once the pair no longer fits side by side. */
.alert > strong { flex: 0 0 auto; }
.alert { flex-wrap: wrap; }
.alert-out { animation: alert-out .3s var(--ease) both; }
.alert-x {
  flex: 0 0 26px;
  width: 26px; height: 26px;
  margin-left: auto;
  display: grid; place-items: center;
  border: 0; border-radius: 50%;
  background: transparent; color: currentColor;
  font: inherit; font-size: 1.15rem; line-height: 1;
  cursor: pointer; opacity: .55;
  transition: opacity var(--fast) var(--ease), background var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.alert-x:hover { opacity: 1; background: rgba(2, 6, 23, .08); transform: rotate(90deg); }
.alert-success { background: var(--green-soft); border-color: var(--green-line); color: var(--green); }
.alert-error { background: var(--red-soft); border-color: var(--red-line); color: var(--red); }
.alert-warning { background: var(--amber-soft); border-color: var(--amber-line); color: var(--amber); }
.alert-info { background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent); }
.alert a { color: inherit; font-weight: 640; text-decoration: underline; }

/* ------------------------------------------------------------------ pieces */

.pager {
  display: flex; gap: .5rem; align-items: center; justify-content: space-between;
  padding: .95rem 1.3rem; border-top: 1px solid var(--border);
  font-size: .87rem; color: var(--muted); background: var(--surface-2);
  flex-wrap: wrap;
}
.pager .pager-pages { display: flex; gap: .35rem; align-items: center; flex-wrap: wrap; }
.pg-cur {
  display: inline-grid; place-items: center;
  min-width: 32px; height: 32px; padding: 0 .45rem;
  border-radius: var(--r-xs);
  background: var(--grad-accent);
  color: #fff; font-weight: 700; font-size: .84rem;
  box-shadow: 0 8px 16px -10px rgba(var(--accent-rgb), .95);
}

.calendar td, .calendar th { text-align: center; padding: .5rem .4rem; font-size: .83rem; }
.calendar .type { text-align: left; white-space: nowrap; font-weight: 640; }
.cell {
  border-radius: var(--r-xs);
  padding: .38rem .25rem;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  transition: transform var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
.cell:hover { transform: translateY(-2px); box-shadow: var(--shadow); }
.cell-ok { background: var(--green-soft); color: var(--green); }
.cell-thin { background: var(--amber-soft); color: var(--amber); }
.cell-none { background: var(--red-soft); color: var(--red); }

.boards { display: grid; gap: 1.1rem; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); }
.board {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow);
  overflow: hidden;
  transition: box-shadow var(--mid) var(--ease), transform var(--mid) var(--ease);
}
.board:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }
.board > h3 {
  padding: .85rem 1rem; margin: 0;
  border-bottom: 1px solid var(--border);
  display: flex; justify-content: space-between; align-items: center;
  background: linear-gradient(180deg, var(--surface-2), rgba(248, 250, 255, .3));
  font-size: .95rem;
}
.board ul { list-style: none; margin: 0; padding: .45rem .6rem; }
.board li {
  padding: .62rem .55rem;
  border-bottom: 1px dashed var(--border);
  font-size: .88rem;
  border-radius: var(--r-xs);
  transition: background var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.board li:hover { background: var(--accent-soft); transform: translateX(3px); }
.board li:last-child { border-bottom: 0; }
.board .count { color: var(--muted); font-weight: 550; font-variant-numeric: tabular-nums; }

.ledger td { font-variant-numeric: tabular-nums; }
.total-row td { font-weight: 720; background: var(--accent-soft); color: var(--text); border-top: 1px solid var(--accent-line); }

.timeline { list-style: none; margin: 0; padding: 0; font-size: .885rem; }
.timeline li { padding: .7rem .25rem; border-bottom: 1px solid var(--border); transition: background var(--fast) var(--ease); }
.timeline li:hover { background: var(--surface-2); }
.timeline li:last-child { border-bottom: 0; }
.timeline .when { color: var(--muted); font-size: .79rem; letter-spacing: .01em; }

.bar { height: 9px; background: var(--grey-soft); border-radius: var(--pill); overflow: hidden; box-shadow: inset 0 1px 2px rgba(16, 24, 40, .07); }
.bar > i {
  display: block; height: 100%;
  background: var(--grad-accent);
  border-radius: var(--pill);
  transform-origin: left center;
  animation: grow-x .9s var(--ease) both;
  transition: width var(--mid) var(--ease);
}

/* --------------------------------------------------- the overview's top strips */

/* What the day has left waiting. Each chip is a link to the screen that closes it, so the row is
   a to-do list rather than a report. It wraps instead of scrolling sideways: a task you have to
   drag to see is a task you skip. The time at the end says how old the numbers are, and the link
   reads them again. */
.attention,
.quick {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .45rem;
  margin-bottom: 1rem;
}
.attention-key,
.quick-key {
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .055em;
  text-transform: uppercase;
  color: var(--muted);
  margin-right: .25rem;
}
.attention-chip {
  display: inline-flex;
  align-items: baseline;
  gap: .4rem;
  padding: .34rem .72rem;
  border-radius: var(--pill);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-soft);
  font-size: .79rem;
  font-weight: 560;
  text-decoration: none;
  box-shadow: var(--shadow-xs);
  transition: transform var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
.attention-chip:hover { transform: translateY(-1px); box-shadow: var(--shadow); }
.attention-chip strong { font-size: .92rem; font-weight: 760; font-variant-numeric: tabular-nums; }
.attention-chip.tone-red { background: var(--red-soft); color: var(--red); border-color: var(--red-line); }
.attention-chip.tone-amber { background: var(--amber-soft); color: var(--amber); border-color: var(--amber-line); }
.attention-chip.is-clear { background: var(--green-soft); color: var(--green); border-color: var(--green-line); font-weight: 520; }
.attention-fresh {
  margin-left: auto;
  font-size: .75rem;
  color: var(--muted);
  text-decoration: none;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.attention-fresh:hover { color: var(--accent); }

/* Everyday entries. Same shape as a chip, quieter colour: these start something, the chips above
   finish something, and the two rows should not look like the same kind of thing. */
.quick-tile {
  display: inline-flex;
  align-items: center;
  padding: .36rem .78rem;
  border-radius: var(--pill);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-soft);
  font-size: .79rem;
  font-weight: 600;
  text-decoration: none;
  box-shadow: var(--shadow-xs);
  transition: border-color var(--fast) var(--ease), color var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.quick-tile:hover {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-soft);
  transform: translateY(-1px);
}

/* ------------------------------------------------------------------ utils */

.muted { color: var(--muted); }
.nowrap { white-space: nowrap; }
.right { text-align: right; }
.mono { font-family: ui-monospace, "Cascadia Mono", "SF Mono", Consolas, monospace; font-size: .865rem; letter-spacing: -.01em; }
code { font-family: ui-monospace, "Cascadia Mono", "SF Mono", Consolas, monospace; font-size: .84em; background: var(--grey-soft); border: 1px solid var(--border); border-radius: 5px; padding: .08rem .35rem; }
.stack { display: grid; gap: 1.1rem; }
.btn-row { display: flex; gap: .55rem; flex-wrap: wrap; align-items: center; }
/* An inline editor inside a table cell must not fold into one control per line;
   the surrounding .table-scroll absorbs the extra width instead. */
td .btn-row { flex-wrap: nowrap; }
td .btn-row .input { min-width: 9rem; }
td .btn-row .input[type="number"] { flex: 0 0 auto; width: 5rem; min-width: 4.5rem; }
td .btn-row .btn { flex: 0 0 auto; }
/* Action bar that stays in view while a long form scrolls. */
.form-actions {
  position: sticky;
  bottom: .9rem;
  z-index: 10;
  margin-top: 1.1rem;
  padding: .85rem 1.05rem;
  display: flex; gap: .55rem; flex-wrap: wrap; align-items: center;
  background: var(--glass);
  backdrop-filter: saturate(160%) blur(10px);
  -webkit-backdrop-filter: saturate(160%) blur(10px);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow-md);
}
/* Inside a table-only card the bar would otherwise sit flush on the card's edge. */
.card > .body.tight .form-actions { margin: 1.1rem .9rem .9rem; }

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

.auth-wrap {
  display: grid; place-items: center; min-height: 100vh; padding: 2rem;
  background: linear-gradient(140deg, #0e1428 0%, #221a52 48%, #4f46e5 100%);
  position: relative; overflow: hidden;
}
.auth-wrap::before, .auth-wrap::after {
  content: ""; position: absolute; border-radius: 50%; filter: blur(60px); opacity: .55;
  animation: drift 16s ease-in-out infinite alternate;
}
.auth-wrap::before { width: 460px; height: 460px; background: rgba(124, 58, 237, .7); top: -120px; left: -90px; }
.auth-wrap::after { width: 380px; height: 380px; background: rgba(56, 189, 248, .45); bottom: -110px; right: -70px; animation-delay: -8s; }
@keyframes drift {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(60px, 40px, 0) scale(1.15); }
}
.auth-card {
  position: relative; z-index: 1;
  background: rgba(255, 255, 255, .97);
  border: 1px solid rgba(255, 255, 255, .6);
  border-radius: var(--r-lg);
  padding: 2.4rem;
  width: 100%; max-width: 430px;
  box-shadow: var(--shadow-lg);
  animation: pop-in .55s both var(--ease);
}
.auth-card h1 { font-size: 1.45rem; }
.auth-card .sub { color: var(--muted); font-size: .9rem; margin-bottom: 1.5rem; }
.auth-card .btn { min-height: 46px; font-size: .95rem; }

/* ------------------------------------------------------------------ print */

.print-body { background: #fff; }
.invoice-sheet { max-width: 800px; margin: 0 auto; padding: 1.5rem; }
.invoice-sheet header { display: flex; justify-content: space-between; gap: 2rem; border-bottom: 2px solid #101828; padding-bottom: 1rem; margin-bottom: 1.25rem; }
.invoice-sheet .party { font-size: .85rem; }
.invoice-sheet .party strong { display: block; font-size: 1rem; }
.invoice-sheet .invoice-logo {
  display: block;
  max-width: 190px; max-height: 78px;
  width: auto; height: auto;
  object-fit: contain;
  margin: 0 0 .55rem;
  /* Keep the mark's own colours on paper. */
  print-color-adjust: exact;
  -webkit-print-color-adjust: exact;
}
.invoice-sheet .toolrow { display: flex; justify-content: space-between; gap: 1rem; margin-top: 1.5rem; }
.invoice-sheet .digital-copy {
  margin-top: 1.6rem; padding-top: .7rem; border-top: 1px solid #d7dbe3;
  font-size: .78rem; color: #475467; text-align: center; font-style: italic;
}

@media print {
  .no-print, .sidebar, .topbar, .nav-toggle, .scrim, .progress-bar, .working, .filters-toggle { display: none !important; }
  body { background: #fff; font-size: 12px; background-image: none; }
  .content { padding: 0; }
  .content > * { animation: none; }
  .invoice-sheet { margin: 0; padding: 0; max-width: none; }
  .card, .stat, .board { box-shadow: none; border-color: #d7dbe3; }
  a { color: inherit; }
}

/* ------------------------------------------- request bar, drawer, scrim */

/* A plain PHP screen reloads for every action, so "did the server get this?" is
   never obvious. The bar appears the instant a request goes out; the heavier veil
   only follows if the round trip is still running after a beat. */
.progress-bar {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 3px;
  z-index: 90;
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--fast) linear;
}
.progress-bar > i {
  position: absolute;
  top: 0; bottom: 0; left: 0;
  width: 42%;
  border-radius: var(--pill);
  background: var(--grad-accent);
  box-shadow: 0 0 16px rgba(var(--accent-rgb), .75);
  transform: translate3d(-110%, 0, 0);
}
body.is-loading .progress-bar { opacity: 1; }
body.is-loading .progress-bar > i { animation: progress-run 1.2s var(--ease) infinite; }

.working {
  position: fixed;
  inset: 0;
  z-index: 80;
  display: grid;
  place-items: center;
  padding: 1.5rem;
  background: rgba(10, 15, 30, .28);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--mid) var(--ease), visibility var(--mid);
}
body.is-loading.working-on .working { opacity: 1; visibility: visible; }
.working > span {
  display: inline-flex; align-items: center; gap: .75rem;
  padding: .95rem 1.5rem;
  border-radius: var(--pill);
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-lg);
  font-size: .95rem; font-weight: 650; color: var(--text-soft);
  animation: pop-in .3s both var(--ease);
}
.spin {
  width: 19px; height: 19px; flex: 0 0 19px;
  border-radius: 50%;
  border: 2.5px solid var(--accent-line);
  border-top-color: var(--accent);
  animation: spin .8s linear infinite;
}

/* Opens the navigation rail on screens too narrow to keep it beside the page. */
.nav-toggle {
  display: none;
  align-items: center; justify-content: center;
  width: 44px; height: 44px; flex: 0 0 44px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--text-soft);
  cursor: pointer;
  box-shadow: var(--shadow-xs);
  transition: color var(--fast) var(--ease), border-color var(--fast) var(--ease),
              background var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.nav-toggle .ic { width: 22px; height: 22px; flex: 0 0 22px; }
.nav-toggle:hover { color: var(--accent); border-color: var(--accent-line); background: var(--accent-tint-2); transform: translateY(-1px); }
.nav-toggle:focus-visible { outline: none; box-shadow: var(--ring); border-color: var(--accent); }
/* The hamburger turns into a cross while the rail is open. */
.nav-toggle .to-x { display: none; }
.nav-open .nav-toggle .to-menu { display: none; }
.nav-open .nav-toggle .to-x { display: block; }

.scrim { display: none; }

.topbar-left { display: flex; align-items: center; gap: .8rem; min-width: 0; }
/* The controls on the right stay whole and tappable; a long school name is what gives
   way when the two do not fit. */
.topbar-right { display: flex; align-items: center; gap: .7rem; flex: 0 0 auto; }

/* ------------------------------------------------------------------ narrow */

@media (max-width: 1280px) {
  :root { --rail: 264px; --pad: 1.4rem; --header: 72px; }
}

@media (max-width: 1024px) {
  :root { --base: 14.5px; --pad: 1.15rem; }

  .shell { flex-direction: column; }

  /* Without script the rail stays what it has always been here: a strip of
     links across the top, because nothing can open a drawer. */
  .sidebar {
    position: static;
    width: auto; flex: none; height: auto;
    flex-direction: row; align-items: center; flex-wrap: wrap; gap: .35rem;
    overflow-x: auto;
    padding: .7rem .9rem;
  }
  .sidebar::after { display: none; }
  .brand { padding: 0 .5rem .2rem; }
  .nav-group { display: none; }
  /* No headings here, so no way to reopen a shut block — the strip and the drawer list everything,
     and `.nav-sec` steps aside so its links flow in the strip the way they always did. */
  .nav-sec { display: contents; }
  .nav-sec.closed .nav-body { display: contents; }
  .nav-link { white-space: nowrap; min-height: 40px; padding: .5rem .8rem; }
  .nav-link.active::before { display: none; }
  .sidebar-foot { margin: 0 0 0 auto; padding: 0; }
  .btn-shell { width: auto; }
  .content > * { animation: none; }

  /* With script it slides in over the page instead. */
  .js .nav-toggle { display: inline-flex; }
  .js .sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    z-index: 60;
    width: min(86vw, 330px);
    flex: none;
    height: 100vh;
    height: 100dvh;
    flex-direction: column; align-items: stretch; flex-wrap: nowrap; gap: .2rem;
    overflow-x: hidden; overflow-y: auto;
    padding: 1.2rem .95rem calc(1.2rem + env(safe-area-inset-bottom, 0));
    transform: translate3d(-102%, 0, 0);
    visibility: hidden;
    box-shadow: none;
    transition: transform var(--mid) var(--swing), visibility var(--mid), box-shadow var(--mid) var(--ease);
  }
  .js .sidebar::after { display: block; }
  .js .brand { padding: .35rem .6rem 1.15rem; }
  .js .nav-group { display: block; }
  .js .nav-link { white-space: normal; min-height: 46px; font-size: .95rem; }
  .js .sidebar-foot { margin: 0; padding: 1rem .35rem .1rem; }
  .js .btn-shell { width: 100%; }
  .js.nav-open .sidebar {
    transform: none;
    visibility: visible;
    box-shadow: 30px 0 70px -34px rgba(2, 6, 23, .8);
  }
  .js.nav-open { overflow: hidden; }
  .js .scrim {
    display: block;
    position: fixed; inset: 0;
    z-index: 55;
    background: rgba(6, 10, 24, .55);
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
    opacity: 0; visibility: hidden;
    border: 0; padding: 0;
    transition: opacity var(--mid) var(--ease), visibility var(--mid);
  }
  .js.nav-open .scrim { opacity: 1; visibility: visible; }

  /* Rail items arrive one after the other as it opens. */
  .js.nav-open .sidebar .nav-link { animation: slide-right .32s both var(--ease); }
  .js.nav-open .sidebar .nav-link:nth-of-type(2) { animation-delay: .03s; }
  .js.nav-open .sidebar .nav-link:nth-of-type(3) { animation-delay: .06s; }
  .js.nav-open .sidebar .nav-link:nth-of-type(4) { animation-delay: .09s; }
  .js.nav-open .sidebar .nav-link:nth-of-type(5) { animation-delay: .12s; }
  .js.nav-open .sidebar .nav-link:nth-of-type(6) { animation-delay: .15s; }
  .js.nav-open .sidebar .nav-link:nth-of-type(n + 7) { animation-delay: .18s; }
}

@media (max-width: 860px) {
  .grid.cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* Two tiles side by side still read at this width; the 300px floor that only just fits a phone
     turned every cols-3 tile row into a tall column while the 212px cols-4 row stayed 2-up. */
  .grid.cols-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grid.cols-2 { grid-template-columns: 1fr; }
  .boards { grid-template-columns: 1fr; }
  .page-head .actions { width: 100%; }
  .page-head .actions .btn { flex: 1 1 auto; }
  .topbar { padding: .7rem 1rem; }
  .topbar .who-text { display: none; }
  .topbar-search { display: none; }
  .search-jump { display: grid; }
  .filters .field { flex: 1 1 calc(50% - .85rem); min-width: 0; }
}

@media (max-width: 640px) {
  :root { --base: 14px; --pad: .95rem; }
  h1, .page-head h1 { font-size: 1.35rem; }
  .stat .value { font-size: 1.6rem; }
  .card > header, .card > .body, .card-foot, .scope-bar, .pager, .filters { padding-left: 1rem; padding-right: 1rem; }
  /* Five columns of .72rem padding each is a fifth of the screen spent on nothing. */
  th, td { padding: .62rem .55rem; }
  /* Nine columns cannot read on a phone: squeezed that thin, a date folds in half and a
     name folds in three. A list keeps the few columns a thumb scans and gives up the rest,
     which are all still there one tap later on the record itself. */
  .phone-hide { display: none; }
  /* A name is what a roll is scanned by, so it keeps a readable line even when the
     columns beside it have been given up. */
  td .person-cell { min-width: 9.5rem; }
  /* A prose column has to be told to stop, because the cell beside it is a button the
     thumb has to reach: without a bound the table runs past the card and the action
     lands off the screen. */
  td.wrap { max-width: 56vw; min-width: 40vw; }
  /* The fee desk is the one table a clerk types into on a phone, and its money column is
     right-aligned: an unwrapped heading there widens the column enough to push the digits
     the thumb just typed off the edge of the screen. */
  .desk-table th { white-space: normal; }
  input.money-box { width: 6.9rem; }
  .btn { padding-left: .85rem; padding-right: .85rem; }
  .prop-switch, .prop-static { min-height: 40px; }
  .prop-switch .ic, .prop-static .ic { display: none; }
  .prop-switch select { max-width: 34vw; font-size: .88rem; }
  /* Menu button, school, colour and avatar have to share 390px, so the school name
     ellipsises instead of pushing the colour picker off the edge of the phone. */
  .topbar { gap: .5rem; }
  .prop-static, .prop-switch { min-width: 0; max-width: 62vw; }
  .prop-static .prop-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  /* Two tiles across is the only way six figures stay above the fold, so the number
     gives up a little size rather than being cut at the comma. */
  .grid.cols-4 { gap: .7rem; }
  .stat { padding: .95rem .95rem 1.05rem; }
  .stat .value { font-size: 1.32rem; letter-spacing: -.045em; }
  .prop-switch, .prop-static { padding-left: .8rem; }
  /* One chip per line already, so the clock that trails the strip takes a line of its own
     rather than crowding the last chip's wording. */
  .attention-fresh { flex-basis: 100%; margin-left: 0; }
  .auth-wrap { padding: 1.1rem; }
  .auth-card { padding: 1.6rem; }
  .filters .field { flex: 1 1 100%; }
  /* A phone screen is shorter than one open filter panel, so it starts folded. */
  .js .filters-toggle { display: flex; }
  .js .body.has-filters:not(.filters-open) > form.is-filters { display: none; }
  .body.has-filters { padding: .7rem .85rem; }
  .js .body.has-filters.filters-open form.is-filters { padding: .9rem 0 0; background: none; }
  /* A "add a route" form is a once-a-visit job, and on eight of these screens it is a
     whole screen and a half of fields standing between the visitor and the list they
     came to read. The card title is already the thing you would tap, so the card folds
     into it instead of gaining a second heading. */
  .js .card.fold-on-phone:not(.is-open) > .body,
  .js .card.fold-on-phone:not(.is-open) > .card-foot { display: none; }
  .js .card.fold-on-phone > header { cursor: pointer; justify-content: flex-start; }
  .js .card.fold-on-phone > header h2 { margin-right: auto; }
  .js .card.fold-on-phone:not(.is-open) > header { min-height: calc(var(--touch) + .7rem); }
  .js .card.fold-on-phone > header::after {
    content: ""; flex: 0 0 9px; width: 9px; height: 9px; margin-left: .55rem;
    border-right: 2px solid var(--muted); border-bottom: 2px solid var(--muted);
    transform: rotate(45deg) translate(-2px, -2px);
    transition: transform var(--mid) var(--swing);
  }
  .js .card.fold-on-phone.is-open > header::after { transform: rotate(-135deg) translate(-2px, -2px); }
  .fields { grid-template-columns: 1fr; }
  /* The register grids keep their columns and only give a little ground on the pinned name,
     so a month still reads by swiping sideways rather than by squinting. */
  /* The class register is the one table a phone has to mark on rather than read, so it
     gives up the minimum width that keeps a month aligned on a laptop: a teacher who has
     to swipe sideways to touch "absent" has not got a register, they have got a puzzle. */
  .reg-table { min-width: 0; }
  .reg-table th, .reg-table td { padding: .5rem .3rem; }
  .reg-grid { min-width: 52rem; }
  .reg-grid .grid-name { min-width: 9rem; max-width: 10rem; padding: .5rem .55rem; }
  .reg-grid .grid-day { width: 1.6rem; }
  .reg-grid td.grid-cell { padding: .45rem .2rem; }
  /* The save bar becomes an edge-to-edge strip the thumb can reach. */
  .form-actions {
    bottom: 0;
    margin-left: calc(-1 * var(--pad));
    margin-right: calc(-1 * var(--pad));
    border-radius: var(--r) var(--r) 0 0;
    border-left: 0; border-right: 0; border-bottom: 0;
    padding-bottom: calc(.85rem + env(safe-area-inset-bottom, 0));
  }
  .form-actions .btn { flex: 1 1 auto; min-height: var(--touch); }
  /* A timetable box holds a lesson's name, so its column keeps enough width to read one even
     when the rest of the week has gone sideways. The editor comes up from the bottom, where the
     thumb already is, and the save sits under it rather than floating over the fields. */
  .tt-grid { min-width: 44rem; }
  .tt-grid .grid-day { min-width: 6.8rem; }
  .tt-grid td.grid-cell { padding: .35rem .3rem; }
  .tt-slot, .tt-open { min-height: 3.2rem; padding: .4rem .45rem; }
  .tt-modal, .pk-modal { padding: 0; align-items: flex-end; }
  .tt-sheet,
  .pk-sheet {
    width: 100%;
    max-height: 92vh;
    border-left: 0;
    border-right: 0;
    border-bottom: 0;
    border-radius: var(--r-lg) var(--r-lg) 0 0;
    padding-bottom: env(safe-area-inset-bottom, 0);
    animation-name: sheet-up;
  }
  /* A search list wants more room than a form: the point is to read down several names
     without flicking while the letters typed stay at the top. */
  .pk-sheet { max-height: 88vh; }
  .pk-list { min-height: 15rem; }
  .tt-sheet > header,
  .pk-sheet > header { border-radius: var(--r-lg) var(--r-lg) 0 0; }
  .tt-sheet .form-actions {
    position: static;
    margin: .2rem 1rem 1rem;
    border: 1px solid var(--border);
    border-radius: var(--r);
    padding: .85rem 1.05rem;
  }
}

/* Below this even two tiles side by side get unreadable. */
@media (max-width: 380px) {
  .grid.cols-4, .grid.cols-3 { grid-template-columns: 1fr; }
  .bulk-bar > :last-child { margin-left: 0; width: 100%; justify-content: center; }
  .topbar-right { gap: .6rem; }
}

/* ------------------------------------------------------------------ themes */

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

.theme-menu { position: relative; }
.theme-menu > summary {
  display: grid; place-items: center;
  width: 42px; height: 42px;
  border-radius: var(--pill);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--muted);
  cursor: pointer;
  list-style: none;
  box-shadow: var(--shadow-xs);
  transition: color var(--fast) var(--ease), border-color var(--fast) var(--ease),
              transform var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
.theme-menu > summary::-webkit-details-marker { display: none; }
.theme-menu > summary:hover { color: var(--accent); border-color: var(--accent-line); transform: translateY(-2px) rotate(-12deg); box-shadow: var(--shadow); }
.theme-menu[open] > summary { color: var(--accent); border-color: var(--accent); box-shadow: var(--ring); }
.theme-pop {
  position: absolute;
  right: 0; top: calc(100% + .55rem);
  z-index: 40;
  min-width: 214px;
  padding: .6rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow-lg);
  animation: pop-in .24s both var(--ease);
}
.theme-cap { margin: .2rem .4rem .55rem; font-size: .71rem; font-weight: 700; text-transform: uppercase; letter-spacing: .09em; color: var(--muted); }
.theme-pop form { margin: 0; }
.theme-swatch {
  display: flex; align-items: center; gap: .6rem; width: 100%;
  min-height: 42px;
  padding: .48rem .62rem;
  border: 1px solid transparent; border-radius: var(--r-sm);
  background: transparent; color: var(--text-soft);
  font: inherit; font-size: .885rem; font-weight: 560; text-align: left; cursor: pointer;
  transition: background var(--fast) var(--ease), border-color var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.theme-swatch:hover { background: var(--accent-soft); border-color: var(--accent-line); transform: translateX(3px); }
.theme-swatch.active { background: var(--accent-soft); border-color: var(--accent); color: var(--text); font-weight: 660; }
.theme-chip {
  width: 26px; height: 26px; flex: 0 0 26px; border-radius: 9px;
  background: linear-gradient(135deg, var(--sw1), var(--sw2));
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .35), var(--shadow-xs);
}
.theme-swatch.active::after {
  content: ""; margin-left: auto; width: 8px; height: 8px;
  border-right: 2px solid var(--accent); border-bottom: 2px solid var(--accent);
  transform: rotate(-45deg);
}

option { background: var(--surface); color: var(--text); }

html[data-theme="emerald"] {
  --accent: #047857;
  --accent-2: #0d9488;
  --accent-hover: #065f46;
  --accent-soft: #e4f5ee;
  --accent-tint-2: #effaf5;
  --accent-line: #c4e9d9;
  --accent-rgb: 4, 120, 87;
  --accent-edge: rgba(4, 120, 87, .3);
  --grad-accent: linear-gradient(135deg, #047857 0%, #0a8f6d 55%, #0d9488 100%);
  --ring: 0 0 0 3px rgba(4, 120, 87, .22);
  --bg: #f2f7f5;
  --glow-a: rgba(13, 148, 136, .11);
  --glow-b: rgba(4, 120, 87, .08);
  --shell-active-a: rgba(16, 185, 129, .95);
  --shell-active-b: rgba(13, 148, 136, .85);
  --shell-mark: #6ee7b7;
  --shell-glow: rgba(13, 148, 136, .4);
}

html[data-theme="sunset"] {
  --accent: #d9540c;
  --accent-2: #db2777;
  --accent-hover: #b8430a;
  --accent-soft: #fdece0;
  --accent-tint-2: #fdf2e9;
  --accent-line: #f6d8c2;
  --accent-rgb: 217, 84, 12;
  --accent-edge: rgba(217, 84, 12, .3);
  --grad-accent: linear-gradient(135deg, #ea580c 0%, #e0413f 55%, #db2777 100%);
  --ring: 0 0 0 3px rgba(217, 84, 12, .22);
  --bg: #fbf6f1;
  --text: #221812;
  --text-soft: #453126;
  --muted: #7d6a5e;
  --border: #eee2d8;
  --border-strong: #dcc8b9;
  --grey-soft: #f3eae2;
  --tint: #fbf5ef;
  --tint-2: #fdf9f5;
  --hover: #f8efe6;
  --hover-line: #dcc0aa;
  --glow-a: rgba(219, 39, 119, .1);
  --glow-b: rgba(234, 88, 12, .09);
  --shell: #24120a;
  --shell-2: #3b1a12;
  --shell-3: #4d1d19;
  --shell-ink: #f0dccc;
  --shell-ink-soft: #b99180;
  --shell-glow: rgba(234, 88, 12, .42);
  --shell-active-a: rgba(249, 115, 22, .95);
  --shell-active-b: rgba(219, 39, 119, .85);
  --shell-mark: #fdba74;
}

html[data-theme="midnight"] {
  color-scheme: dark;

  --bg: #0b1120;
  --surface: #141d33;
  --surface-2: #18223b;
  --border: #26324c;
  --border-strong: #37486a;
  --text: #e9eefb;
  --text-soft: #cbd6ea;
  --muted: #92a2c2;
  --grey-soft: #1f2b45;

  --accent: #6d75f5;
  --accent-2: #22d3ee;
  --accent-hover: #8a91ff;
  --accent-soft: rgba(109, 117, 245, .17);
  --accent-tint-2: rgba(109, 117, 245, .1);
  --accent-line: rgba(138, 145, 255, .34);
  --accent-rgb: 109, 117, 245;
  --accent-edge: rgba(109, 117, 245, .42);
  --grad-accent: linear-gradient(135deg, #6366f1 0%, #7c6cf8 55%, #22d3ee 140%);
  --ring: 0 0 0 3px rgba(138, 145, 255, .3);

  --green: #34d399;
  --green-soft: rgba(52, 211, 153, .15);
  --green-line: rgba(52, 211, 153, .32);
  --amber: #fbbf24;
  --amber-soft: rgba(251, 191, 36, .14);
  --amber-line: rgba(251, 191, 36, .32);
  --red: #fb7185;
  --red-soft: rgba(251, 113, 133, .15);
  --red-line: rgba(251, 113, 133, .32);
  --sky: #38bdf8;
  --sky-soft: rgba(56, 189, 248, .15);

  --shadow-xs: 0 1px 2px rgba(0, 0, 0, .45);
  --shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 10px 24px -16px rgba(0, 0, 0, .85);
  --shadow-md: 0 2px 4px rgba(0, 0, 0, .5), 0 18px 34px -18px rgba(0, 0, 0, .9);
  --shadow-lg: 0 26px 60px -26px rgba(0, 0, 0, .95);

  --glass: rgba(20, 29, 51, .82);
  --tint: #18223b;
  --tint-2: #16203a;
  --hover: #21304e;
  --hover-line: #43567b;
  --placeholder: #6f81a6;
  --stripe: rgba(255, 255, 255, .035);
  --scroll: #2c3a58;
  --scroll-hover: #3d5177;
  --select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2392a2c2' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");

  --shell: #090f1e;
  --shell-2: #101a31;
  --shell-3: #142043;
  --shell-glow: rgba(99, 102, 241, .34);
  --shell-active-a: rgba(99, 102, 241, .9);
  --shell-active-b: rgba(34, 211, 238, .55);
  --shell-mark: #67e8f9;

  --glow-a: rgba(99, 102, 241, .16);
  --glow-b: rgba(34, 211, 238, .08);
}

/* Native date pickers and the sign-in stage follow the theme too. */
html[data-theme="midnight"] input::-webkit-calendar-picker-indicator { filter: invert(1) opacity(.65); }
html[data-theme="midnight"] .auth-wrap { background: linear-gradient(140deg, #060a16 0%, #131c3a 48%, #3b3f8f 100%); }
html[data-theme="midnight"] .auth-card { background: rgba(20, 29, 51, .96); border-color: var(--border); }
html[data-theme="midnight"] .btn-danger-solid { background: linear-gradient(135deg, #e11d48, #9f1239); }
html[data-theme="sunset"] .auth-wrap { background: linear-gradient(140deg, #2a1108 0%, #6b1f2a 48%, #db2777 100%); }
html[data-theme="emerald"] .auth-wrap { background: linear-gradient(140deg, #06231b 0%, #0b4a43 48%, #0d9488 100%); }

/* ------------------------------------------------------ install offer */

/* One line pinned above the home indicator, so the offer survives being
   scrolled past but never sits under the thumb. */
.install-bar {
  position: fixed;
  z-index: 90;
  left: 50%;
  bottom: calc(14px + env(safe-area-inset-bottom));
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 10px;
  width: min(100% - 24px, 430px);
  padding: 10px 12px;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--r);
  box-shadow: var(--shadow-lg);
  font-size: 13px;
  color: var(--text-soft);
  animation: rise .28s ease-out;
}

.install-bar b { color: var(--text); }
.install-bar span { flex: 1; min-width: 0; }
.install-bar .btn { flex: none; padding: 8px 14px; }

.install-bar .install-no {
  flex: none;
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  border: 0;
  background: none;
  color: var(--muted);
  font-size: 19px;
  line-height: 1;
  cursor: pointer;
  border-radius: var(--r-xs);
}

.install-bar .install-no:hover { background: var(--grey-soft); color: var(--text); }

@keyframes rise {
  from { opacity: 0; transform: translate(-50%, 14px); }
  to { opacity: 1; transform: translate(-50%, 0); }
}

/* Shown without a connection, so it cannot lean on anything but the shell. */
.offline-body { background: var(--bg); }
.offline-body .auth-card p { color: var(--text-soft); font-size: 14px; line-height: 1.55; }

/* ------------------------------------------------------------------ the certificate preview
   The letter as the school signs it — a page set inside the page, so a clerk reads on screen what
   the family gets on paper, and the print stylesheet carries the same shape. */
.letter { position: relative; max-width: 44rem; margin: 0 auto; padding: 2rem 2.2rem;
  border: 1px solid var(--border-strong); border-radius: var(--r); background: var(--surface-2); }
.letter-head { text-align: center; border-bottom: 2px solid var(--text); padding-bottom: .8rem; margin-bottom: 1.3rem; }
.letter-school { margin: 0; font-size: 1.25rem; letter-spacing: .01em; }
.letter-addr { margin: .3rem 0 0; color: var(--muted); font-size: .8rem; }
.letter-kind { text-align: center; margin: 0 0 .2rem; font-size: .95rem; font-weight: 700; letter-spacing: .16em; }
.letter-no { text-align: center; margin: 0 0 1.4rem; color: var(--muted); font-size: .84rem; }
.letter-lead { text-align: center; margin: 0; color: var(--muted); font-style: italic; font-size: .9rem; }
.letter-name { text-align: center; margin: .35rem 0 .3rem; font-size: 1.35rem; font-weight: 700; }
.letter-line { text-align: center; margin: .1rem 0; color: var(--text-soft); font-size: .88rem; }
.letter-body { margin: 1rem 0 0; line-height: 1.75; font-size: .95rem; }
.letter-facts { margin-top: 1rem; padding-top: .7rem; border-top: 1px dashed var(--border-strong); }
.letter-facts .letter-line { text-align: left; }
.letter-remarks { margin-top: .9rem; }
/* The remarks are the school's own lines, not part of the centred heading block, and the printed
   sheet and the PDF both set them left — the preview has to read the way the paper does. */
.letter-remarks .letter-line { text-align: left; }
.letter-tag { margin: 0 0 .2rem; font-size: .68rem; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: var(--muted); }
.letter-sign { margin-top: 3rem; }
/* The lines share one row the way they do on paper, so a school that adds a third signer sees three
   signatures on the letter rather than two and a caption dropped underneath. */
.letter-blocks { display: flex; flex-wrap: wrap; gap: 1.4rem 2rem; justify-content: flex-end; }
.letter-blocks > div { flex: 1 1 10rem; max-width: 14rem; min-width: 0; }
.letter-blocks .letter-rule { width: 100%; }
.letter-blocks b { display: block; min-height: 1.25em; font-size: .88rem; }
.letter-blocks small { display: block; }
.letter-rule { display: block; width: 12rem; border-bottom: 1px solid var(--text); margin-bottom: .35rem; }
.letter-cancelled { opacity: .8; }
.letter-stamp { position: absolute; right: 1.4rem; bottom: 1.2rem; transform: rotate(-12deg);
  border: 2px solid var(--red); color: var(--red); border-radius: var(--r-xs); padding: .25rem .7rem;
  font-size: .8rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; }
.certificate-preview-frame { width:100%; height:1040px; overflow:hidden; border:1px solid var(--border);
  border-radius:var(--r-xs); background:#eef0f3; }
.certificate-preview-frame iframe { display:block; width:100%; height:100%; border:0; background:#fff; }

/* The register preview uses the same saved paper design and palette as the print page and PDF. */
.letter[class*="letter--"] { color:var(--p-ink,#1f2937); background:#fff; border-color:var(--p-line,#d5dbe5); }
.letter[class*="letter--"] .letter-head { border-bottom-color:var(--p-line,#d5dbe5); }
.letter[class*="letter--"] .letter-school, .letter[class*="letter--"] .letter-kind,
.letter[class*="letter--"] .letter-name { color:var(--p-accent,#1f2937); }
.letter[class*="letter--"] .letter-addr, .letter[class*="letter--"] .letter-no,
.letter[class*="letter--"] .letter-lead, .letter[class*="letter--"] .letter-tag,
.letter[class*="letter--"] .letter-blocks small { color:var(--p-muted,#6b7280); }
.letter[class*="letter--"] .letter-line { color:var(--p-ink,#1f2937); }
.letter[class*="letter--"] .letter-facts { border-top-color:var(--p-line,#d5dbe5); }
.letter[class*="letter--"] .letter-rule { border-bottom-color:var(--p-accent,#1f2937); }
.letter--band .letter-head { margin:-2rem -2.2rem 1.3rem; padding:1rem 2.2rem; color:var(--p-on-band,#fff);
  background:var(--p-band,#1f2937); border-bottom:0; }
.letter--band .letter-school, .letter--band .letter-addr { color:var(--p-on-band,#fff)!important; }
.letter--frame { outline:1px solid var(--p-line,#d5dbe5); outline-offset:4px; border:2px solid var(--p-accent,#1f2937)!important; }
.letter--spine { border-left:10px solid var(--p-band,#1f2937)!important; border-radius:0; }
.letter--spine .letter-head, .letter--spine .letter-kind, .letter--spine .letter-no,
.letter--spine .letter-lead, .letter--spine .letter-name, .letter--spine .letter-line { text-align:left; }
.letter--spine .letter-head { border-bottom:2px solid var(--p-accent,#1f2937); }
.letter--crest .letter-head { position:relative; padding-bottom:1.1rem; }
.letter--crest .letter-head::after { content:""; position:absolute; left:18%; right:18%; bottom:0; height:2px;
  background:linear-gradient(to right,var(--p-accent,#1f2937) 0 42%,transparent 42% 58%,var(--p-accent,#1f2937) 58% 100%); }
.letter--crest .letter-kind { position:relative; padding-bottom:.8rem; }
.letter--crest .letter-kind::after { content:"◆"; position:absolute; left:calc(50% - .35rem); bottom:-.35rem; color:var(--p-accent,#1f2937); font-size:.7rem; }
.letter--award { outline:1px solid var(--p-accent,#1f2937); outline-offset:5px; border:2px solid var(--p-accent,#1f2937)!important;
  box-shadow:inset 0 0 0 5px #fff,inset 0 0 0 6px var(--p-line,#d5dbe5); }
.letter--award::before,.letter--award::after { content:""; position:absolute; width:2rem; height:2rem; pointer-events:none; }
.letter--award::before { left:.6rem; top:.6rem; border-left:2px solid var(--p-accent,#1f2937); border-top:2px solid var(--p-accent,#1f2937); }
.letter--award::after { right:.6rem; bottom:.6rem; border-right:2px solid var(--p-accent,#1f2937); border-bottom:2px solid var(--p-accent,#1f2937); }
.letter--minimal { border-color:transparent!important; background:#fff; padding:2rem 1.2rem; }
.letter--minimal .letter-head { border-bottom-color:transparent; }
.letter--minimal .letter-facts { border-top:0; }
.letter--watermark { isolation:isolate; }
.letter--watermark::before { content:""; position:absolute; z-index:-1; left:50%; top:50%; width:12rem; height:12rem;
  transform:translate(-50%,-50%); border-radius:50%; background:var(--p-tint,#f4f4f4); }
@media (max-width:620px) { .letter--band .letter-head { margin:-1rem -1rem 1rem; padding:.8rem 1rem; } }

/* ------------------------------------------------------------------ a paper's look */

/* The four drawings a school picks for a paper, shown as the sheet each one makes rather than as a
   name the clerk has to have seen printed first. They read the palette the layout screen hangs on
   its own form (--p-*), so a tile is drawn in the colour the paper will print in. */
.tpl-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: .8rem; }
.tpl-tile { display: block; position: relative; border: 1px solid var(--border); border-radius: var(--r);
  padding: .7rem; background: var(--surface); cursor: pointer;
  transition: border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease); }
.tpl-tile:hover { border-color: var(--accent-line); }
.tpl-tile input { position: absolute; opacity: 0; width: 1px; height: 1px; left: .7rem; top: .7rem; }
.tpl-tile:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }
.tpl-check { position: absolute; top: .55rem; right: .55rem; display: grid; place-items: center;
  width: 1.25rem; height: 1.25rem; border: 1px solid var(--border-strong); border-radius: 50%;
  color: transparent; font-size: .75rem; line-height: 1; }
.tpl-tile.is-on { border-color: var(--accent-edge); box-shadow: 0 0 0 2px var(--accent-soft); }
.tpl-tile.is-on .tpl-name { color: var(--accent); }
.tpl-tile.is-on .tpl-check { color: #fff; border-color: var(--accent); background: var(--accent); }
.tpl-name { display: block; margin-top: .55rem; font-weight: 650; font-size: .88rem; }
.tpl-note { display: block; margin-top: .15rem; color: var(--muted); font-size: .76rem; line-height: 1.45; }

/* The mini sheet carries what a template actually moves: the school's line across the top, then the
   child's name and two of the register's lines under it. */
.tpl-mini { display: block; position: relative; overflow: hidden; height: 4.4rem; padding: .4rem .45rem;
  border: 1px solid var(--border-strong); border-radius: var(--r-xs); background: #fff; }
.tpl-mini .tpl-head { display: block; width: 55%; height: .3rem; margin: 0 auto .45rem; border-radius: 2px;
  background: var(--p-accent, var(--text)); }
.tpl-mini .tpl-line { display: block; height: .2rem; margin-top: .28rem; border-radius: 2px;
  background: var(--p-line, var(--border-strong)); }
.tpl-mini .tpl-line.is-name { width: 72%; height: .28rem; background: var(--p-ink, var(--text)); }
.tpl-mini--band .tpl-head { width: auto; height: 1rem; margin: -.4rem -.45rem .5rem; border-radius: 0;
  background: var(--p-band, var(--text)); }
.tpl-mini--band .tpl-head::after { content: ""; display: block; width: 46%; height: .22rem;
  margin: .38rem auto 0; border-radius: 2px; background: var(--p-on-band, #fff); }
.tpl-mini--frame { border: 2px solid var(--p-accent, var(--text)); outline: 1px solid var(--p-line, var(--border));
  outline-offset: 2px; }
.tpl-mini--spine { border-left: 5px solid var(--p-band, var(--text)); padding-left: .55rem; }
.tpl-mini--spine .tpl-head { margin-left: 0; }

/* The crest drawing hangs a mark over the school's line and breaks its rule beneath the name, so the
   tile shows the mark, a short centred rule, and the ornament in place of a plain hairline. */
.tpl-mini--crest .tpl-head { position: relative; width: 46%; height: .26rem; margin: .95rem auto .45rem; }
.tpl-mini--crest .tpl-head::before { content: ""; position: absolute; left: 50%; top: -1.05rem;
  transform: translateX(-50%); width: .85rem; height: .85rem; border-radius: 50%;
  border: 1.5px solid var(--p-accent, var(--text)); background: var(--p-tint, #fff); }
.tpl-mini--crest .tpl-line.is-name { position: relative; margin-bottom: .55rem; }
.tpl-mini--crest .tpl-line.is-name::after { content: ""; position: absolute; left: -4%; right: -4%;
  top: .48rem; height: 1.5px; background: linear-gradient(to right,
    var(--p-accent, var(--text)) 0 40%, transparent 40% 60%, var(--p-accent, var(--text)) 60% 100%); }

/* The award drawing borders the sheet three times over and marks the corners it stops at. */
.tpl-mini--award { border: 2px solid var(--p-accent, var(--text)); outline: 1px solid var(--p-accent, var(--text));
  outline-offset: 2px; box-shadow: inset 0 0 0 3px #fff, inset 0 0 0 4px var(--p-line, var(--border)); }
.tpl-mini--award::before, .tpl-mini--award::after { content: ""; position: absolute; width: 38%; height: 42%;
  pointer-events: none; }
.tpl-mini--award::before { left: 7px; top: 7px; border-left: 2px solid var(--p-accent, var(--text));
  border-top: 2px solid var(--p-accent, var(--text)); }
.tpl-mini--award::after { right: 7px; bottom: 7px; border-right: 2px solid var(--p-accent, var(--text));
  border-bottom: 2px solid var(--p-accent, var(--text)); }

/* The plain drawing sets no rule anywhere: the school's line goes, and the child's name is the only
   thing on the tile wearing the colour. */
.tpl-mini--minimal .tpl-head { display: none; }
.tpl-mini--minimal { padding-top: .8rem; }
.tpl-mini--minimal .tpl-line.is-name { width: 84%; height: .42rem; background: var(--p-accent, var(--text)); }

/* The watermark drawing prints the school's mark pale behind the words and leaves the rest plain, so
   the tile's furniture is the ghost rather than a rule. */
.tpl-mini--watermark::before { content: ""; position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%); width: 2.2rem; height: 2.2rem; border-radius: 50%;
  background: var(--p-accent, var(--text)); opacity: .14; }

/* The colour a paper prints in, shown where its name is read rather than in a hex nobody parses. */
.look-dot { display: inline-block; width: .72rem; height: .72rem; border-radius: 50%;
  border: 1px solid var(--border-strong); vertical-align: -.08rem; }

/* The cell the layout screen sketches, drawn the way the writer will draw it. The screen is the
   office's, but the paper inside it is white in every theme, and its words are the paper's inks.
   The sheet's own padding is a class rather than an inline style so that a drawing which needs more
   room at one edge — the spine's bar — can actually ask for it. */
.sketch { position: relative; display: flex; flex-direction: column; overflow: hidden; padding: 9px 10px;
  border: 1px solid var(--border-strong); border-radius: var(--r-sm); background: #fff;
  color: var(--p-ink, var(--text)); }
.sketch .muted { color: var(--p-muted, var(--muted)); }
.sketch--band .sketch-head { background: var(--p-band, var(--accent)); color: var(--p-on-band, #fff);
  margin: -9px -10px 6px; padding: 9px 10px; }
.sketch--band .sketch-head strong { color: var(--p-on-band, #fff); }
.sketch--band .sketch-head > span:first-child { background: #fff; border-color: #fff; }
.sketch--frame { border: 2px solid var(--p-accent, var(--border-strong));
  outline: 1px solid var(--p-line, var(--border)); outline-offset: 3px; }
.sketch--spine { border-left: 7px solid var(--p-band, var(--accent)); padding-left: 13px; }

/* The crest drawing: the school's line centred over the cell, and the ornate rule the writer strikes
   under the child's name in place of a hairline. */
.sketch--crest .sketch-head { justify-content: center; }
.sketch--crest .sketch-name { position: relative; text-align: center; margin-bottom: .85rem; }
.sketch--crest .sketch-name::after { content: ""; position: absolute; left: 4%; right: 4%; bottom: -.55rem;
  height: 2px; background: linear-gradient(to right,
    var(--p-accent, var(--text)) 0 42%, transparent 42% 58%, var(--p-accent, var(--text)) 58% 100%); }

/* The award drawing: three rings of colour with a mark tucked into each pair of corners it reaches. */
.sketch--award { border: 2px solid var(--p-accent, var(--accent));
  outline: 1px solid var(--p-accent, var(--accent)); outline-offset: 3px;
  box-shadow: inset 0 0 0 4px #fff, inset 0 0 0 5px var(--p-line, var(--border)); }
.sketch--award::before, .sketch--award::after { content: ""; position: absolute; width: 26%; height: 18%;
  pointer-events: none; }
.sketch--award::before { left: 9px; top: 9px; border-left: 2px solid var(--p-accent, var(--accent));
  border-top: 2px solid var(--p-accent, var(--accent)); }
.sketch--award::after { right: 9px; bottom: 9px; border-right: 2px solid var(--p-accent, var(--accent));
  border-bottom: 2px solid var(--p-accent, var(--accent)); }

/* The plain drawing sets no rule and no border: the words carry the sheet and the names wear the colour. */
.sketch--minimal .sketch-name, .sketch--minimal .sketch-head strong { color: var(--p-accent, var(--accent)); }

/* The watermark drawing: the school's mark printed pale behind the words, the rest of the sheet plain. */
.sketch--watermark::before { content: ""; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 38%; aspect-ratio: 1; border-radius: 50%; background: var(--p-accent, var(--accent)); opacity: .14; }

/* The swatch row beside the colour well: the school's own colour, its houses, and a short list of
   tints an office reaches for. A chip is the colour itself, with the name under it, because a clerk
   picks "house blue" rather than a hex. */
.look-swatches { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .5rem; }
.look-swatch { display: flex; align-items: center; gap: .35rem; border: 1px solid var(--border);
  border-radius: var(--r); padding: .22rem .5rem; background: var(--surface); cursor: pointer;
  font-size: .76rem; color: var(--muted); transition: border-color var(--fast) var(--ease),
  box-shadow var(--fast) var(--ease); }
.look-swatch:hover { border-color: var(--accent-edge); color: var(--text); }
.look-swatch.is-picked { border-color: var(--accent-edge); box-shadow: 0 0 0 2px var(--accent-soft);
  color: var(--text); }
.look-swatch i { display: inline-block; width: .85rem; height: .85rem; border-radius: 50%;
  border: 1px solid var(--border-strong); flex: 0 0 auto; }
.look-swatch input { position: absolute; opacity: 0; width: 0; height: 0; }
.look-swatch--set { border-style: dashed; }

/* A saved design gallery for the papers an office prints. */
.design-tabs { display:flex; gap:.45rem; flex-wrap:wrap; margin:0 0 1rem; border-bottom:1px solid var(--border); }
.design-tab { display:block; padding:.7rem .9rem; color:var(--muted); text-decoration:none; border-bottom:2px solid transparent; font-weight:650; }
.design-tab:hover, .design-tab.is-active { color:var(--accent); }
.design-tab.is-active { border-bottom-color:var(--accent); }
.design-layout { display:grid; grid-template-columns:minmax(0,1.25fr) minmax(19rem,.75fr); gap:1rem; align-items:start; }
.design-choices, .design-preview-panel { min-width:0; }
.design-gallery { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:.7rem; }
.design-option { position:relative; display:grid; grid-template-columns:6.5rem minmax(0,1fr); gap:.75rem; align-items:center;
  min-height:7.2rem; padding:.7rem; border:1px solid var(--border); border-radius:var(--r); background:var(--surface); cursor:pointer; }
.design-option:hover { border-color:var(--accent-line); }
.design-option.is-selected { border-color:var(--accent-edge); box-shadow:0 0 0 2px var(--accent-soft); }
.design-option input { position:absolute; opacity:0; width:1px; height:1px; left:.5rem; top:.5rem; }
.design-option:focus-within { outline:2px solid var(--accent); outline-offset:2px; }
.design-option-preview { display:flex; flex-direction:column; justify-content:center; height:5.3rem; padding:.45rem; overflow:hidden;
  color:var(--p-ink,#1f2937); background:#fff; border:1px solid var(--p-line,#ddd); border-radius:4px; font-size:.44rem; }
.design-preview-band { height:.45rem; margin:-.45rem -.45rem .35rem; background:var(--p-band,var(--p-accent,#4f46e5)); }
.design-preview-name { color:var(--p-accent,#4f46e5); font-weight:800; text-align:center; text-transform:uppercase; }
.design-preview-title { margin:.2rem 0; color:var(--p-accent,#4f46e5); text-align:center; font-weight:750; }
.design-preview-lines { display:grid; gap:.18rem; }
.design-preview-lines i { height:2px; background:var(--p-line,#ddd); }
.design-option-copy { display:grid; gap:.25rem; padding-right:1.4rem; }
.design-option-copy strong { font-size:.86rem; }
.design-option-copy > span { color:var(--muted); font-size:.73rem; line-height:1.4; }
.design-option-check { position:absolute; right:.55rem; top:.55rem; display:grid; place-items:center; width:1.2rem; height:1.2rem;
  border:1px solid var(--border-strong); border-radius:50%; color:transparent; font-size:.75rem; }
.design-option.is-selected .design-option-check { color:#fff; background:var(--accent); border-color:var(--accent); }
.design-colour { margin-top:1.1rem; padding-top:1rem; border-top:1px solid var(--border); }
.design-colour-heading { display:flex; justify-content:space-between; gap:1rem; align-items:center; flex-wrap:wrap; }
.design-colour-heading > div { display:grid; gap:.15rem; }
.design-colour-heading .muted { font-size:.78rem; }
.design-own-colour { display:flex; align-items:center; gap:.4rem; font-size:.84rem; cursor:pointer; }
.design-own-colour input { accent-color:var(--accent); }
.design-colour-controls { display:flex; gap:.55rem; align-items:center; margin-top:.6rem; }
.design-colour-controls input[type=color] { width:2.8rem; height:2.2rem; padding:2px; }
.design-colour-readout { color:var(--muted); font-size:.84rem; }
.design-savebar { display:flex; justify-content:space-between; gap:1rem; align-items:center; padding:.85rem 1rem; border-top:1px solid var(--border); }
.design-savebar .muted { font-size:.82rem; }
.design-preview-panel header { display:block; }
.design-preview-panel header > div { display:grid; gap:.2rem; }
.design-preview-full { --design-size:1; position:relative; width:100%; min-height:25rem; overflow:hidden; padding:1.2rem;
  color:var(--p-ink,#1f2937); background:#fff; border:1px solid var(--p-line,#d8dee8); border-radius:var(--r-sm); }
.design-preview-full--id_card, .design-preview-full--admit_card { width:100%; min-height:17rem; aspect-ratio:1.586; }
.design-full-header { display:flex; align-items:center; gap:.7rem; padding-bottom:.7rem; border-bottom:1px solid var(--p-line,#ddd); }
.design-full-mark { display:grid; place-items:center; flex:0 0 2.1rem; width:2.1rem; height:2.1rem; color:var(--p-on-band,#fff);
  background:var(--p-band,var(--p-accent,#4f46e5)); border-radius:50%; font-weight:800; }
.design-full-header > div { display:grid; gap:.15rem; min-width:0; }
.design-full-header strong { color:var(--p-accent,#4f46e5); font-size:.88rem; }
.design-full-header small { color:var(--p-muted,#6b7280); font-size:.65rem; }
.design-full-title { margin:1rem 0; padding:.45rem; color:var(--p-on-band,#fff); background:var(--p-band,var(--p-accent,#4f46e5));
  text-align:center; font-size:.82rem; font-weight:800; letter-spacing:.07em; text-transform:uppercase; }
.design-id-body { display:flex; align-items:center; gap:.8rem; }
.design-id-photo { display:grid; place-items:center; width:4.1rem; height:5rem; color:var(--p-muted,#6b7280); background:var(--p-tint,#f2f4f8);
  border:1px solid var(--p-line,#ddd); font-size:.58rem; }
.design-id-body > div { display:grid; gap:.24rem; }
.design-id-body strong { color:var(--p-accent,#4f46e5); font-size:.76rem; }
.design-id-body small { color:var(--p-muted,#6b7280); font-size:.63rem; }
.design-report-meta { display:grid; gap:.4rem; font-size:.7rem; }
.design-report-table { width:100%; margin-top:.7rem; border-collapse:collapse; font-size:.65rem; }
.design-report-table th { color:var(--p-accent,#4f46e5); background:var(--p-tint,#f2f4f8); text-align:left; }
.design-report-table th, .design-report-table td { padding:.4rem; border:1px solid var(--p-line,#ddd); }
.design-cert-copy { margin:1.3rem .3rem; font-family:Georgia,serif; font-size:.78rem; line-height:1.8; }
.design-cert-fields { display:grid; gap:.45rem; font-size:.67rem; }
.design-cert-fields span { display:grid; grid-template-columns:8rem 1fr; }
.design-cert-fields i { border-bottom:1px dashed var(--p-line,#bbb); }
.design-full-signatures { position:absolute; right:1.2rem; bottom:1rem; left:1.2rem; display:flex; justify-content:space-between;
  color:var(--p-muted,#6b7280); font-size:.63rem; }
.design-preview--band .design-full-header { margin:-1.2rem -1.2rem 0; padding:1.2rem; color:var(--p-on-band,#fff);
  background:var(--p-band,var(--p-accent,#4f46e5)); border:0; }
.design-preview--band .design-full-header strong, .design-preview--band .design-full-header small { color:var(--p-on-band,#fff); }
.design-preview--frame { outline:2px solid var(--p-accent,#4f46e5); outline-offset:-.55rem; }
.design-preview--spine { border-left:.45rem solid var(--p-band,var(--p-accent,#4f46e5)); }
.design-preview--crest .design-full-header { justify-content:center; text-align:center; }
.design-preview--award { outline:1px solid var(--p-accent,#4f46e5); outline-offset:-.45rem; box-shadow:inset 0 0 0 4px #fff,inset 0 0 0 5px var(--p-line,#ddd); }
.design-preview--minimal .design-full-title { color:var(--p-accent,#4f46e5); background:transparent; }
.design-preview--minimal { border-color:transparent; }
.design-preview--watermark:after { content:"✦"; position:absolute; top:36%; left:44%; color:var(--p-tint,#eee); font-size:6rem; pointer-events:none; }
.design-option-preview.design-preview--band { background:var(--p-band,#4f46e5); }
.design-option-preview.design-preview--band .design-preview-name, .design-option-preview.design-preview--band .design-preview-title { color:var(--p-on-band,#fff); }
.design-option-preview.design-preview--spine { border-left:4px solid var(--p-band,#4f46e5); }
.design-option-preview.design-preview--frame, .design-option-preview.design-preview--award { outline:1px solid var(--p-accent,#4f46e5); outline-offset:-4px; }
.design-option-preview.design-preview--minimal .design-preview-band { display:none; }
.design-option-preview.design-preview--watermark { background:linear-gradient(135deg,#fff 40%,var(--p-tint,#f2f4f8)); }
.design-preview-note { margin:.8rem 0 0; font-size:.76rem; }
@media (max-width: 900px) { .design-layout { grid-template-columns:1fr; } }
@media (max-width: 620px) { .design-gallery { grid-template-columns:1fr; } .design-savebar { align-items:flex-start; flex-direction:column; } }

/* ------------------------------------------------------------------ the school's own public site */

/* The banner a school files for its public page, previewed at the shape the published page shows it
   in: a wide strip, cropped rather than squashed, so the office sees what parents will see. */
.site-banner { display: block; width: 100%; max-width: 720px; aspect-ratio: 16 / 6; object-fit: cover;
  border: 1px solid var(--border); border-radius: var(--r); background: var(--surface-2);
  box-shadow: var(--shadow-xs); }
/* The honest box when there is none yet, so the file picker under it reads as the thing that fills it. */
.site-banner-empty { display: grid; place-items: center; width: 100%; max-width: 720px;
  aspect-ratio: 16 / 6; border: 1px dashed var(--border); border-radius: var(--r);
  background: var(--surface-2); color: var(--muted); font-size: .9rem; text-align: center; padding: 1rem; }

/* The banner slides and the photographs, lined up at the size the home page shows them. Both are
   cropped rather than squashed so the office judges a picture by what a parent sees. */
.slide-strip { display: grid; gap: .7rem; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); }
.slide-strip figure { margin: 0; border: 1px solid var(--border); border-radius: var(--r);
  overflow: hidden; background: var(--surface-2); box-shadow: var(--shadow-xs); }
.slide-strip img { display: block; width: 100%; aspect-ratio: 16 / 6; object-fit: cover; }
.slide-strip figcaption { padding: .45rem .6rem; font-size: .8rem; color: var(--muted);
  display: flex; align-items: center; gap: .4rem; justify-content: space-between; }
.slide-strip figure.is-off { opacity: .62; }
/* One photograph, kept at the four-to-three the gallery grid crops to. */
.slide-thumb { display: block; width: 100%; max-width: 360px; aspect-ratio: 4 / 3; object-fit: cover;
  border: 1px solid var(--border); border-radius: var(--r); background: var(--surface-2); }

/* The site colour picker. The chips are buttons and the hex box is the field: a browser with no
   script keeps the box and types into it, while a tapped chip only writes the same box. */
.swatch-row { display: flex; flex-wrap: wrap; gap: .45rem; align-items: center; margin-bottom: .6rem; }
.swatch { width: 2.05rem; height: 2.05rem; padding: 0; border-radius: 50%; cursor: pointer;
  border: 1px solid rgba(16, 24, 40, .18); box-shadow: var(--shadow-xs);
  transition: transform var(--fast) var(--ease), box-shadow var(--fast) var(--ease); }
.swatch:hover { transform: scale(1.09); }
.swatch:focus-visible { outline: none; box-shadow: var(--ring); }
.swatch.is-on { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--text); }
/* The free picker is the last chip, drawn as one but leaving any colour the eight do not hold. */
.swatch-pick { position: relative; width: 2.05rem; height: 2.05rem; border-radius: 50%; overflow: hidden;
  border: 1px dashed var(--border-strong); display: grid; place-items: center; cursor: pointer; }
.swatch-pick input { position: absolute; inset: -25%; width: 150%; height: 150%;
  padding: 0; border: 0; background: none; cursor: pointer; }
/* The school's own colour, and the ink and band the server worked out to sit on it. Only the two
   samples are chips; the sentence under them is a sentence. */
.colour-preview { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; margin-top: .5rem; }
.colour-preview [data-sample-band], .colour-preview [data-sample-soft] {
  border-radius: var(--r-xs); padding: .32rem .6rem; font-size: .8rem; font-weight: 650;
  border: 1px solid var(--border); }

/* The queue of messages a stranger left on the site. A card rather than a row of a table because an
   enquiry is a thing to answer, and the note the office leaves is longer than a column can hold. */
.enq-card { border: 1px solid var(--border); border-radius: var(--r); background: var(--surface);
  padding: .95rem 1.05rem; margin-bottom: .8rem; box-shadow: var(--shadow-xs); }
.enq-card.is-new { border-color: var(--accent-line); background: var(--accent-soft); }
.enq-card header { display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap; margin-bottom: .5rem; }
.enq-card header h3 { margin: 0; font-size: 1rem; font-weight: 720; }
.enq-card .said { margin: .4rem 0 .7rem; padding-left: .75rem; border-left: 3px solid var(--border-strong);
  color: var(--text-soft); font-size: .93rem; white-space: pre-wrap; }
.enq-meta { display: flex; flex-wrap: wrap; gap: .35rem 1.1rem; font-size: .83rem; color: var(--muted); }
.enq-meta b { color: var(--text); font-weight: 650; }

/* ------------------------------------------------------------------ motion */

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

/* A print picker shows the same card data in the design the next PDF will use. */
.print-preview-shell { display: grid; justify-items: center; gap: .55rem; margin: 0 0 1rem; padding: 1rem;
  border: 1px solid var(--border); border-radius: var(--r); background: var(--grey-soft); }
.print-preview-card { width: min(100%, 34rem); min-height: 15rem; position: relative; overflow: hidden;
  border: 2px solid var(--p-line, var(--border-strong)); border-radius: 10px; padding: 1rem 1.2rem;
  background: #fff; color: var(--p-ink, var(--text)); box-shadow: var(--shadow-sm); }
.print-preview-head { display: flex; align-items: center; gap: .65rem; padding-bottom: .65rem;
  border-bottom: 1px solid var(--p-line, var(--border)); color: var(--p-accent, var(--text)); }
.print-preview-head > div { display: grid; gap: .12rem; }
.print-preview-head > div > span { font-size: .72rem; opacity: .78; }
.print-preview-mark { display: grid; place-items: center; flex: 0 0 2rem; height: 2rem; border-radius: .5rem;
  color: var(--p-on-band, #fff); background: var(--p-band, var(--accent)); font-weight: 800; }
.print-preview-student { display: grid; gap: .3rem; padding: .9rem 0; }
.print-preview-student > strong { color: var(--p-accent, var(--text)); font-size: 1.15rem; }
.print-preview-student > span:not(.print-preview-photo) { color: var(--p-muted, var(--muted)); font-size: .82rem; }
.print-preview-student small { color: var(--p-ink, var(--text)); font-size: .75rem; }
.print-preview-photo { float: right; display: grid; place-items: center; width: 3.8rem; height: 4.6rem;
  margin: 0 0 .35rem .6rem; border: 1px dashed var(--p-line, var(--border-strong)); color: var(--p-muted, var(--muted)); font-size: .58rem; }
.print-preview-footer { border-top: 1px solid var(--p-line, var(--border)); padding-top: .5rem; color: var(--p-muted, var(--muted)); font-size: .68rem; }
.print-preview-card--band .print-preview-head { margin: -1rem -1.2rem 0; padding: .8rem 1.2rem; color: var(--p-on-band, #fff);
  background: var(--p-band, var(--accent)); border: 0; }
.print-preview-card--band .print-preview-head .print-preview-mark { background: #fff; color: var(--p-accent, var(--accent)); }
.print-preview-card--frame { outline: 2px solid var(--p-line, var(--border)); outline-offset: -7px; }
.print-preview-card--spine { border-left: 10px solid var(--p-band, var(--accent)); }
.print-preview-card--crest .print-preview-head { justify-content: center; text-align: center; }
.print-preview-card--crest .print-preview-mark { border-radius: 50%; }
.print-preview-card--award { outline: 1px solid var(--p-accent, var(--accent)); outline-offset: -6px;
  box-shadow: inset 0 0 0 6px #fff, inset 0 0 0 7px var(--p-line, var(--border)); }
.print-preview-card--minimal { border: 0; box-shadow: none; }
.print-preview-card--minimal .print-preview-head { border: 0; }
.print-preview-card--watermark::after { content: "✦"; position: absolute; top: 35%; left: 43%; font-size: 5rem;
  color: var(--p-line, var(--border)); opacity: .22; pointer-events: none; }

/* ------------------------------------------------------------------ AI assistant */
.ai-layout { display: grid; grid-template-columns: 260px minmax(0, 1fr); gap: 1rem; align-items: start; }
.ai-threads .body { max-height: 70vh; overflow-y: auto; }
.ai-thread { display: block; padding: .6rem 1rem; border-bottom: 1px solid var(--border); color: var(--text); text-decoration: none; }
.ai-thread span { display: block; font-size: .9rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ai-thread:hover { background: var(--accent-soft); }
.ai-thread.is-on { background: var(--accent-soft); box-shadow: inset 3px 0 0 var(--accent); }
.ai-main { min-width: 0; }
.ai-log { display: flex; flex-direction: column; gap: 1rem; margin-bottom: 1rem; }
.ai-turn { display: flex; flex-direction: column; gap: .6rem; }
.ai-q { align-self: flex-end; max-width: 80%; background: var(--accent); color: var(--accent-ink); padding: .6rem .9rem;
        border-radius: var(--r) var(--r) 4px var(--r); white-space: pre-wrap; }
.ai-a { align-self: flex-start; max-width: 90%; background: var(--surface); border: 1px solid var(--border);
        padding: .7rem 1rem; border-radius: var(--r) var(--r) var(--r) 4px; }
.ai-a p { margin: 0 0 .5rem; }
.ai-a ul { margin: 0 0 .5rem 1.1rem; padding: 0; }
.ai-a .ai-when { margin: .2rem 0 0; }
.ai-error { color: var(--red); margin: 0; }
.ai-wait { color: var(--muted); }
.ai-dots { display: inline-flex; gap: 3px; margin-right: .4rem; vertical-align: middle; }
.ai-dots i { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); animation: ai-blink 1.2s infinite ease-in-out; }
.ai-dots i:nth-child(2) { animation-delay: .2s; }
.ai-dots i:nth-child(3) { animation-delay: .4s; }
@keyframes ai-blink { 0%, 80%, 100% { opacity: .25; } 40% { opacity: 1; } }
.ai-table header { display: flex; gap: .75rem; align-items: center; flex-wrap: wrap; }
.ai-table header h3 { margin-right: auto; }
.ai-table td.num, .ai-table th.num { text-align: right; }
tr.ai-flag td { background: var(--red-soft); color: var(--red); font-weight: 600; }
.ai-flag-key { color: var(--red); font-weight: 600; }
.ai-group { margin-top: .8rem; }
.ai-chips { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .3rem; }
.ai-chip { border: 1px solid var(--border-strong); background: var(--surface); color: var(--text-soft); border-radius: 999px;
           padding: .35rem .8rem; font: inherit; font-size: .85rem; cursor: pointer; text-align: left; }
.ai-chip:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.ai-ask { position: sticky; bottom: .75rem; }
.ai-ask textarea { width: 100%; resize: vertical; }
.ai-ask-row { display: flex; gap: .75rem; align-items: center; margin-top: .5rem; }
.ai-ask-row .btn { margin-left: auto; }
@media (max-width: 900px) {
  .ai-layout { grid-template-columns: 1fr; }
  .ai-threads .body { max-height: 30vh; }
  .ai-q, .ai-a { max-width: 100%; }
}

/* ------------------------------------------------------------------ account menu (name + avatar) */
.who-menu { position: relative; }
.who-menu > summary { list-style: none; cursor: pointer; border-radius: 999px; padding: .15rem .15rem .15rem .55rem; }
.who-menu > summary::-webkit-details-marker { display: none; }
.who-menu > summary:hover, .who-menu[open] > summary { background: var(--accent-soft); }
.who-menu[open] .avatar { box-shadow: var(--ring); }
.who-pop { min-width: 236px; }
.who-pop .theme-cap { text-transform: none; letter-spacing: 0; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.who-pop a.theme-swatch { text-decoration: none; }
.who-pop .ic { width: 18px; height: 18px; flex: none; }
.who-pop .who-out { color: var(--red); }
.who-pop .who-out:hover { background: var(--red-soft); border-color: var(--red-line); }
