/* ═══════════════════════════════════════════════════════════════════════
   SCHOOL · Concept C   (b183)
   Loaded after design-v2-app.css, profile-c.css, trips-c.css and
   garage-c.css.

   Gated on `body.sv-on`, set by show() for the School view only. Every rule
   below is either prefixed with that class or confined to the `.sv-`
   namespace, so nothing here can reach another screen — the same discipline
   profile-c.css, trips-c.css and garage-c.css use.

   WHY THIS FILE COULD NOT HAVE EXISTED SOONER, AND IT IS NOT A NEW STORY.
   School was copied from the Pets hub in b144 and never revisited when
   Grocery, Profile, Travel and Garage moved to Concept C. That was never
   something a stylesheet could have fixed on its own: show() listed
   `dashboard`, `profile`, the trip views and `car` in `dv2Views`, and
   `school` was not among them — so the section never carried
   `body.dv2-app`, the class the `--dv2-*` tokens are HOSTED on. A
   `var(--dv2-text-primary)` written here would have resolved to nothing and
   voided its whole declaration, silently. The one-line re-gate in show() is
   what unlocked this file. Garage's header tells the same story; that it is
   the second time is the point — the gate is the thing to check first.

   THE BASE STANDARD IS THE ENTITY TILE (set Aug 2026, see profile-c.css):
   every entity gets its own discrete rounded tab, and the container behind
   those tabs is transparent.

   AND THE ONE DELIBERATE EXCEPTION, which is Garage's exception applied for
   Concept C's reason: a CHILD is not a row with a thumbnail beside their
   name. School is a child's history book, so on the School home a child IS
   the photograph, with their year and school laid over it. Everything else
   on every screen is a tile. Making the exception in exactly one place per
   section is what stops it reading as a different design language.

   A TOKEN THAT WAS NEVER DEFINED IS A LITERAL IN DISGUISE (trips-c.css's
   lesson). Every var() below either resolves on body.dv2-app or carries a
   fallback that is correct in the theme it lands in.
   ═══════════════════════════════════════════════════════════════════════ */

body.sv-on {
  /* School reuses Profile's, Trips' and Garage's tile tokens rather than
     defining a fourth set — four token sets for one visual standard is how
     they drift apart. */
  --sv-tile-bg: rgba(255, 255, 255, 0.72);
  /* b205 — THE SIX MAJOR TINTS. b204 swept every colour in this file onto
     tokens and then had svMajor() pass var(--sv-t-mail) … var(--sv-t-rep) in
     from JavaScript — six names that were defined nowhere, so six of the seven
     majors lost their icon colour entirely. The sweep fixed the stylesheet and
     missed the tokens the stylesheet was being handed. */
  --sv-t-info: rgba(120, 120, 128, 0.14);
  --sv-t-mail: rgba(110, 86, 207, 0.16);
  --sv-t-cal:  rgba(47, 143, 95, 0.16);
  --sv-t-bts:  rgba(110, 86, 207, 0.16);
  --sv-t-task: rgba(10, 108, 255, 0.14);
  --sv-t-doc:  rgba(120, 120, 128, 0.14);
  --sv-t-rep:  rgba(232, 147, 12, 0.18);
  --sv-tile-bd: rgba(20, 16, 38, 0.07);
  --sv-shadow: 0 1px 3px rgba(20, 20, 40, 0.05);
  --sv-rad: 16px;
  --sv-ink-on-dark: #ffffff;
  /* The call button. Garage uses this exact green for the one control that
     matters at the roadside; School's equivalent is ringing the office. */
  --sv-call-bg: #16704A;
  --sv-call-tx: #ffffff;
  /* The scrim under a child's name on their photograph. Fixed, not themed:
     it sits on a photograph, not on the app's ground. */
  --sv-scrim: linear-gradient(180deg, rgba(0,0,0,0) 34%, rgba(0,0,0,0.64) 100%);
  /* A field's edge and a chip's edge are different jobs: a field is a surface
     you type on, a chip is a control you press. The old file gave both the
     same near-white border, which on a white card made every chip invisible. */
  --sv-field-bd: rgba(20, 16, 38, 0.10);
  --sv-chip-bd: rgba(20, 16, 38, 0.22);
  --sv-term-ink: #1F7A52;
  --sv-term-bg: rgba(31, 122, 82, 0.10);
  /* The approved board's three: green term, red holiday, amber event. */
  --sv-rib-g: hsl(152 52% 42%);
  --sv-rib-r: hsl(355 72% 55%);
  --sv-rib-a: hsl(38 82% 48%);
  --sv-chip-on-ink: #F7F6FB;
  /* b206 - the ground under a quiet housekeeping line (the duplicate-scan
     note). Defined in all three theme blocks, because a colour whose only
     definition sits behind a media query is the unreadable-in-dark bug. */
  --sv-quiet-bg: rgba(120, 120, 128, 0.09);
  /* b207 - the count on the What's on button. Red because it is a date that
     is close, not a notification; the same red the ribbon uses for a closure. */
  --sv-badge-bg: hsl(355 72% 48%);
  --sv-badge-tx: #ffffff;
  --sv-badge-ring: rgba(238, 242, 255, 1);
}
html[data-theme="dark"] body.sv-on {
  --sv-tile-bg: rgba(255, 255, 255, 0.07);
  --sv-t-info: rgba(255, 255, 255, 0.10);
  --sv-t-mail: rgba(168, 150, 240, 0.22);
  --sv-t-cal:  rgba(123, 224, 174, 0.20);
  --sv-t-bts:  rgba(168, 150, 240, 0.22);
  --sv-t-task: rgba(90, 166, 255, 0.20);
  --sv-t-doc:  rgba(255, 255, 255, 0.10);
  --sv-t-rep:  rgba(255, 180, 80, 0.22);
  --sv-tile-bd: rgba(255, 255, 255, 0.12);
  --sv-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
  --sv-ink-on-dark: #14121F;
  --sv-call-bg: #2F8F5F;
  --sv-field-bd: rgba(255, 255, 255, 0.14);
  --sv-chip-bd: rgba(255, 255, 255, 0.30);
  --sv-term-ink: #7BE0AE;
  --sv-term-bg: rgba(123, 224, 174, 0.14);
  --sv-rib-g: hsl(152 52% 52%);
  --sv-rib-r: hsl(355 78% 66%);
  --sv-rib-a: hsl(38 88% 60%);
  --sv-chip-on-ink: #14121F;
  --sv-quiet-bg: rgba(255, 255, 255, 0.08);
  --sv-badge-bg: hsl(355 78% 58%);
  --sv-badge-tx: #14121F;
  --sv-badge-ring: rgba(20, 16, 38, 1);
}
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) body.sv-on {
    --sv-tile-bg: rgba(255, 255, 255, 0.07);
    --sv-t-info: rgba(255, 255, 255, 0.10);
    --sv-t-mail: rgba(168, 150, 240, 0.22);
    --sv-t-cal:  rgba(123, 224, 174, 0.20);
    --sv-t-bts:  rgba(168, 150, 240, 0.22);
    --sv-t-task: rgba(90, 166, 255, 0.20);
    --sv-t-doc:  rgba(255, 255, 255, 0.10);
    --sv-t-rep:  rgba(255, 180, 80, 0.22);
    --sv-tile-bd: rgba(255, 255, 255, 0.12);
    --sv-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
    --sv-ink-on-dark: #14121F;
    --sv-call-bg: #2F8F5F;
    --sv-field-bd: rgba(255, 255, 255, 0.14);
    --sv-chip-bd: rgba(255, 255, 255, 0.30);
    --sv-term-ink: #7BE0AE;
    --sv-term-bg: rgba(123, 224, 174, 0.14);
    --sv-rib-g: hsl(152 52% 52%);
    --sv-rib-r: hsl(355 78% 66%);
    --sv-rib-a: hsl(38 88% 60%);
    --sv-chip-on-ink: #14121F;
  --sv-quiet-bg: rgba(255, 255, 255, 0.08);
  --sv-badge-bg: hsl(355 78% 58%);
  --sv-badge-tx: #14121F;
  --sv-badge-ring: rgba(20, 16, 38, 1);
  }
}

/* ── The ground ─────────────────────────────────────────────────────────
   style.css paints #view-school and .pets-body on the old flat surface. The
   aurora comes from the tokens now, so those fills have to stand down or
   they sit on top of it as an opaque slab — the same mistake #shops-box made
   in Profile, #trip-list made in Travel and #car-hub made in Garage. */
body.sv-on #view-school,
body.sv-on .pets-body,
body.sv-on #school-hub {
  background: transparent;
}
body.sv-on {
  /* THE GROUND HAS TO REACH THE BOTTOM OF THE SCREEN. style.css paints `html`
     as well as `body`, which stops the body's background propagating to the
     canvas — so on a short screen (one child, or none) the aurora ended where
     the content ended and a flat grey band filled the rest of the phone. */
  min-height: 100dvh;
  background: var(--dv2-bg-grad, #F4F2FA);
  background-attachment: fixed;
  color: var(--dv2-text-primary, #14121F);
}

/* ── Header — the legacy .topbar, wearing glass ─────────────────────── */
body.sv-on .topbar.glass {
  background: var(--dv2-g-07-bg, rgba(255, 255, 255, 0.5));
  border-bottom: 1px solid var(--dv2-g-07-bd, rgba(255, 255, 255, 0.75));
  backdrop-filter: saturate(160%) blur(18px);
  -webkit-backdrop-filter: saturate(160%) blur(18px);
  box-shadow: none;
}
/* b232 · R9 — SIZE, WEIGHT AND TRACKING DELETED, NOT RE-POINTED. This was one
   of the two 19px titles (Garage was the other) against the list screen's 28,
   and it is the one the user reported. The canon rule at the end of
   style.css owns them now; only the colour is School's. */
body.sv-on .topbar .lp-name {
  color: var(--dv2-text-primary);
}
body.sv-on .topbar .back-btn { color: var(--dv2-text-primary); }

/* ── Shared primitives ──────────────────────────────────────────────── */
body.sv-on .sv-ic {
  width: 20px; height: 20px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}
body.sv-on .sv-ic-sm { width: 17px; height: 17px; }
body.sv-on .sv-grow { flex: 1; min-width: 0; display: flex; flex-direction: column; }

body.sv-on .sv-tile {
  display: flex; align-items: center; gap: 11px;
  width: 100%; padding: 13px 14px; min-height: 52px;
  background: var(--sv-tile-bg);
  border: 1px solid var(--sv-tile-bd);
  border-radius: var(--sv-rad);
  box-shadow: var(--sv-shadow);
  color: var(--dv2-text-primary);
  font-family: inherit; font-size: 14px; text-align: left; cursor: pointer;
}
body.sv-on .sv-tile.dashed {
  border-style: dashed; background: transparent; box-shadow: none;
  color: var(--dv2-text-sub);
}
body.sv-on .sv-addrow { justify-content: center; }
body.sv-on .sv-t { font-weight: 600; letter-spacing: -0.01em; }

/* ONE ACTION COLOUR IN THIS SECTION. This was the shared purple-to-magenta
   gradient, which appeared on exactly two controls — "Add a child" and the
   contact editor's Save — while the only other filled button on any School
   screen, Call, was green. Two primary colours on one screen means neither is
   primary. School's is the green it already rings the school with. */
body.sv-on .sv-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  padding: 11px 18px; border-radius: 13px; border: 0;
  background: var(--sv-call-bg);
  color: var(--sv-call-tx); font-family: inherit; font-size: 14.5px; font-weight: 650;
  letter-spacing: -0.01em; cursor: pointer;
}
body.sv-on .sv-btn.ghost {
  background: transparent; color: var(--dv2-text-sub);
  border: 1px solid var(--sv-tile-bd);
}
body.sv-on .sv-btn:disabled { opacity: .55; }

/* Ofsted's four grades, and nothing else gets a colour. */
body.sv-on .sv-rate {
  flex: none; align-self: flex-start;
  font-size: 11px; font-weight: 700; letter-spacing: -0.005em;
  padding: 4px 9px; border-radius: 999px; white-space: nowrap;
  background: var(--sv-tile-bg); border: 1px solid var(--sv-tile-bd);
  color: var(--dv2-text-sub);
}
body.sv-on .sv-rate.ok   { background: rgba(22,112,74,0.12);  border-color: rgba(22,112,74,0.30);  color: #16704A; }
body.sv-on .sv-rate.warn { background: rgba(176,116,0,0.14);  border-color: rgba(176,116,0,0.32);  color: #8A5A00; }
body.sv-on .sv-rate.bad  { background: rgba(178,42,52,0.12);  border-color: rgba(178,42,52,0.30);  color: #B22A34; }
html[data-theme="dark"] body.sv-on .sv-rate.ok   { color: #6FD9A6; }
html[data-theme="dark"] body.sv-on .sv-rate.warn { color: #FFD79A; }
html[data-theme="dark"] body.sv-on .sv-rate.bad  { color: #FFB4AE; }
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) body.sv-on .sv-rate.ok   { color: #6FD9A6; }
  html:not([data-theme="light"]) body.sv-on .sv-rate.warn { color: #FFD79A; }
  html:not([data-theme="light"]) body.sv-on .sv-rate.bad  { color: #FFB4AE; }
}

/* ── THE CHILD CARD — the one exception to the tile ─────────────────── */
body.sv-on .sv-kids { display: block; }
body.sv-on .sv-kid {
  display: block; width: 100%; position: relative;
  border-radius: 20px; overflow: hidden;
  margin: 0 0 14px; padding: 0; border: 0; cursor: pointer;
  background: linear-gradient(160deg, #4A3A78, #6E6890);
  box-shadow: 0 6px 22px rgba(20, 20, 60, 0.14);
  text-align: left; font-family: inherit;
  min-height: 172px;
}
body.sv-on .sv-kid:active { transform: scale(.99); }
@media (prefers-reduced-motion: reduce) { body.sv-on .sv-kid:active { transform: none; } }
body.sv-on .sv-kid-img {
  width: 100%; height: 190px; object-fit: cover; display: block;
}
/* No photograph yet: the card keeps its SHAPE and holds a tinted plate with
   the school mark, rather than collapsing to a grey box the size of a row. */
/* The placeholder's mark and its invitation sit in the UPPER half of the card,
   because the lower half already belongs to the child's name. */
body.sv-on .sv-kid-ph {
  display: grid; justify-items: center; align-content: start;
  padding-top: 34px; gap: 7px;
}
body.sv-on .sv-kid-ph .sv-ic {
  width: 30px; height: 30px; color: rgba(255, 255, 255, 0.6); stroke-width: 1.5;
}
/* A card with no photograph says so, in the same words the child's own page
   uses. Without it the section's central object is a coloured rectangle that
   gives a parent no clue it is waiting for something. */
body.sv-on .sv-kid-add {
  font-size: 12.5px; font-weight: 600;
  color: rgba(255, 255, 255, 0.74); pointer-events: none;
}
body.sv-on .sv-kid::after {
  content: ""; position: absolute; inset: 0; background: var(--sv-scrim);
  pointer-events: none;
}
body.sv-on .sv-kid-ovl {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  padding: 13px 14px; display: flex; flex-direction: column; gap: 4px;
  align-items: flex-start;
}
body.sv-on .sv-kid-name {
  color: #fff; font-size: 19px; font-weight: 720; letter-spacing: -0.025em;
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.42);
}
body.sv-on .sv-kid-line {
  color: rgba(255, 255, 255, 0.88); font-size: 12.5px; font-weight: 560;
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.4);
}
/* The rating rides on the photograph, so it needs its own contrast rather
   than the themed pill's — a light chip on a bright photo disappears. */
body.sv-on .sv-kid > .sv-rate {
  position: absolute; top: 11px; right: 11px; z-index: 3;
  background: rgba(10, 6, 26, 0.52); border-color: rgba(255, 255, 255, 0.28);
  color: #fff; backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
}
body.sv-on .sv-kid > .sv-rate.ok   { color: #A8F0CC; }
body.sv-on .sv-kid > .sv-rate.warn { color: #FFD79A; }
body.sv-on .sv-kid > .sv-rate.bad  { color: #FFB4AE; }

/* ── Empty state ────────────────────────────────────────────────────── */
body.sv-on .sv-welcome {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: 12px; padding: 40px 22px 26px;
}
body.sv-on .sv-welcome-art {
  width: 84px; height: 84px; border-radius: 26px; display: grid; place-items: center;
  background: var(--sv-tile-bg); border: 1px solid var(--sv-tile-bd);
  color: var(--dv2-text-primary);
}
body.sv-on .sv-welcome-art .sv-ic { width: 40px; height: 40px; stroke-width: 1.4; }
body.sv-on .sv-welcome h2 {
  margin: 6px 0 0; font-size: 22px; font-weight: 750; letter-spacing: -0.025em;
  color: var(--dv2-text-primary);
}
body.sv-on .sv-welcome p {
  margin: 0; max-width: 34ch; font-size: 14px; line-height: 1.5;
  color: var(--dv2-text-sub);
}

/* ── The child profile sheet ────────────────────────────────────────── */
/* It owns its own header, so it does not want the framework's absolute ✕. */
body.sv-on #sheet-child > .sheet-closerail,
body.sv-on #sheet-add-child > .sheet-closerail,
body.sv-on #sheet-cropper > .sheet-closerail { display: none; }

body.sv-on .sheet.glass,
body.sv-on #sheet-child.profile-page,
body.sv-on #sheet-add-child.profile-page {
  background: var(--dv2-bg-grad, #F4F2FA);
  color: var(--dv2-text-primary);
}
body.sv-on .pet-profile-body { padding: 0 0 24px; }
body.sv-on .pet-topbar { color: var(--dv2-text-primary); }
body.sv-on .pet-top-title {
  font-weight: 700; font-size: 17px; letter-spacing: -0.02em;
  color: var(--dv2-text-primary);
}
body.sv-on .pet-top-btn {
  background: var(--sv-tile-bg); border: 1px solid var(--sv-tile-bd);
  color: var(--dv2-text-primary); border-radius: 12px;
}

/* THE HERO — the photograph again, full width, name laid over it. */
body.sv-on .sv-hero {
  position: relative; margin: 8px 0 14px;
  border-radius: 20px; overflow: hidden;
  background: linear-gradient(160deg, #4A3A78, #6E6890);
}
body.sv-on .sv-hero-img { width: 100%; height: 216px; object-fit: cover; display: block; }
/* Equal specificity, declared later, so this has to restate what the hub card
   already says - `.sv-hero-img`'s display:block otherwise wins and drops the
   mark into the top-left corner instead of the middle of the plate. */
body.sv-on .sv-hero-img.sv-kid-ph { display: grid; place-items: center; }
body.sv-on .sv-hero::after {
  content: ""; position: absolute; inset: 0; background: var(--sv-scrim); pointer-events: none;
}
body.sv-on .sv-hero-ovl {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2; padding: 14px 16px;
}
body.sv-on .sv-hero-name {
  margin: 0; color: #fff; font-size: 25px; font-weight: 760; letter-spacing: -0.03em;
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.45);
}
body.sv-on .sv-hero-line {
  margin-top: 3px; color: rgba(255, 255, 255, 0.86); font-size: 13px; font-weight: 560;
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.4);
}

/* ── THE PHOTOGRAPH IS A CONTROL ─────────────────────────────────────── */
/* b186 — the hero was an <img>. Tapping it, which is the first thing anybody
   tries, did nothing, and the only way to touch a picture again was one line
   buried in the overflow menu. It is a button now, drawn from the same source
   so it looks identical, and it opens choose / position / remove. */
body.sv-on .sv-hero-btn {
  border: 0; padding: 0; cursor: pointer;
  background-size: cover; background-repeat: no-repeat;
}
body.sv-on .sv-hero-btn:active { opacity: .92; }
@media (prefers-reduced-motion: reduce) { body.sv-on .sv-hero-btn:active { opacity: 1; } }

/* Travel's action sheet, in School's marks. */
body.sv-on #sheet-child-photo .settings-ic { color: var(--dv2-text-sub); }
body.sv-on #sheet-child-photo .settings-row.danger .settings-txt b,
body.sv-on #sheet-child-photo .settings-row.danger .settings-ic { color: #e5484d; }
body.sv-on #sheet-child-photo .settings-txt b { color: var(--dv2-text-primary); }
body.sv-on #sheet-child-photo .settings-txt small { color: var(--dv2-text-sub); }
body.sv-on #sheet-child-photo .sheet-head h2 { color: var(--dv2-text-primary); }

/* Taking a photo off, from the form that put it on. */
body.sv-on .sv-photo-clear {
  display: inline-flex; align-items: center; gap: 7px; align-self: center;
  margin: -4px 0 8px; padding: 7px 13px; border-radius: 12px;
  background: transparent; border: 1px solid var(--sv-tile-bd);
  color: var(--dv2-text-sub); font-family: inherit; font-size: 13px;
  font-weight: 600; cursor: pointer;
}
body.sv-on .sv-photo-clear .sv-ic { width: 15px; height: 15px; }

/* ── TERM TIME, IN TWO COLOURS ───────────────────────────────────────── */
/* The approved rule is RED for a holiday and GREEN for term time (amber for an
   event arrives with the parent timeline). Only red had shipped, so a term row
   was the default ink with no tag — half a colour code, which is worse than
   none, because the reader cannot tell whether black means "term" or "not
   classified". Green is the section's own green, the one Call wears. */
body.sv-on .child-terms .term-row.sv-term .term-dot { background: var(--sv-call-bg); }
body.sv-on .child-terms .term-row.sv-term .term-label { color: var(--sv-term-ink); }
body.sv-on .child-terms .term-row.sv-term .term-range { color: var(--sv-term-ink); opacity: .78; }
body.sv-on .child-terms .term-row.sv-term .term-tag {
  color: var(--sv-term-ink); background: var(--sv-term-bg);
}

/* A school's full legal name can run to eleven words. Left alone it made one
   row of the lookup three times the height of its neighbours. */
body.sv-on .ss-name {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ── WHO SAID SO ─────────────────────────────────────────────────────── */
/* b185 — the rating used to be a bare green pill next to the school's name.
   "Outstanding", with nothing to say who awarded it or when, reads as the
   app's own opinion. Every badge in this section now carries its source. */
body.sv-on .sv-judge {
  display: flex; align-items: flex-start; gap: 9px;
  margin: 11px 0 12px; padding-top: 11px;
  border-top: 1px solid var(--sv-tile-bd);
}
body.sv-on .sv-judge .sv-ic { flex: none; margin-top: 1px; color: var(--dv2-text-muted); }
body.sv-on .sv-judge-k {
  display: block; font-size: 11px; font-weight: 800; letter-spacing: .05em;
  text-transform: uppercase; color: var(--dv2-text-muted);
}
body.sv-on .sv-judge-v {
  display: block; margin-top: 1px; font-size: 14px; font-weight: 700;
  letter-spacing: -0.01em; color: var(--dv2-text-primary);
}
body.sv-on .sv-judge-v.ok   { color: #1F7A52; }
body.sv-on .sv-judge-v.warn { color: #9A6B12; }
body.sv-on .sv-judge-v.bad  { color: #A3342F; }
body.sv-on .sv-judge-when { font-weight: 560; color: var(--dv2-text-sub); }
html[data-theme="dark"] body.sv-on .sv-judge-v.ok   { color: #7BE0AE; }
html[data-theme="dark"] body.sv-on .sv-judge-v.warn { color: #FFD79A; }
html[data-theme="dark"] body.sv-on .sv-judge-v.bad  { color: #FFB4AE; }
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) body.sv-on .sv-judge-v.ok   { color: #7BE0AE; }
  html:not([data-theme="light"]) body.sv-on .sv-judge-v.warn { color: #FFD79A; }
  html:not([data-theme="light"]) body.sv-on .sv-judge-v.bad  { color: #FFB4AE; }
}

/* The pill that rides on a photograph keeps its shape but names its source. */
body.sv-on .sv-rate b {
  font-weight: 800; letter-spacing: .05em; text-transform: uppercase;
  font-size: 9px; opacity: .8; margin-right: 5px;
}

/* An empty hero asks for the photograph rather than merely being empty. */
/* Sits in the UPPER half. Centred, the invitation landed a few pixels above
   the child's name and the two competed for the same glance. */
body.sv-on .sv-hero-add {
  border: 0; cursor: pointer; gap: 9px;
  align-content: start; justify-items: center; padding-top: 52px;
}
body.sv-on .sv-hero-add .sv-ic {
  width: 30px; height: 30px; color: rgba(255, 255, 255, 0.82); stroke-width: 1.5;
}
body.sv-on .sv-hero-add-t {
  font-family: inherit; font-size: 14px; font-weight: 650;
  color: rgba(255, 255, 255, 0.92); text-shadow: 0 1px 6px rgba(0, 0, 0, 0.35);
}

/* ── THE SCHOOL CARD ────────────────────────────────────────────────── */
body.sv-on .sv-school {
  background: var(--sv-tile-bg); border: 1px solid var(--sv-tile-bd);
  border-radius: 18px; box-shadow: var(--sv-shadow);
  padding: 13px 14px; margin: 0 0 18px;
}
body.sv-on .sv-school-top { display: flex; align-items: flex-start; gap: 10px; }
body.sv-on .sv-school-name {
  font-size: 15.5px; font-weight: 700; letter-spacing: -0.02em; line-height: 1.25;
  color: var(--dv2-text-primary);
}
/* An address may WRAP rather than truncate — it is the thing you would read
   back to a taxi driver, and Garage learned the same lesson about an
   insurer's name. */
body.sv-on .sv-school-sub {
  margin-top: 2px; font-size: 12px; line-height: 1.35; color: var(--dv2-text-sub);
}
body.sv-on .sv-school-sub.dim { color: var(--dv2-text-muted); font-size: 11.5px; }
body.sv-on .sv-school-edit {
  flex: none; width: 32px; height: 32px; border-radius: 10px; padding: 0;
  display: grid; place-items: center; cursor: pointer;
  background: transparent; border: 1px solid var(--sv-tile-bd);
  color: var(--dv2-text-sub);
}
body.sv-on .sv-school-empty {
  display: flex; align-items: center; gap: 11px; width: 100%;
  background: none; border: 0; padding: 0; cursor: pointer;
  font-family: inherit; color: var(--dv2-text-primary); text-align: left;
}
body.sv-on .sv-school-k {
  font-size: 9px; font-weight: 750; letter-spacing: .1em; text-transform: uppercase;
  color: var(--dv2-text-muted);
}

/* THE THREE ACTIONS. Equal thirds, because no one of them is the answer
   every time — you ring about an absence, email about a form and open the
   site for a newsletter. Call is the filled one: it is the one you reach for
   at 8:40am. */
body.sv-on .sv-acts {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px; margin-top: 13px;
}
body.sv-on .sv-act {
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  padding: 11px 6px 9px; border-radius: 14px; min-width: 0;
  background: var(--dv2-g-09-bg, rgba(255, 255, 255, 0.62));
  border: 1px solid var(--sv-tile-bd);
  color: var(--dv2-text-primary);
  font-family: inherit; cursor: pointer; text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}
body.sv-on .sv-act-l {
  font-size: 12.5px; font-weight: 650; letter-spacing: -0.01em;
}
/* The value under the label is the proof the button will work. It must not
   push the button wider than its third of the row — hence min-width:0 above
   and the ellipsis here. */
body.sv-on .sv-act-cap {
  max-width: 100%; font-size: 10px; font-weight: 550; color: var(--dv2-text-sub);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* `.sv-act-call`, not `.call`: style.css owns a global `.call` that sizes any
   element to a 32px circle, and it won - the button collapsed to a green disc
   with its label spilling out below it. The `sv-` prefix is the only thing
   keeping this file's names out of the shared cascade, so every modifier
   carries it too. */
body.sv-on .sv-act-call {
  background: var(--sv-call-bg); border-color: var(--sv-call-bg); color: var(--sv-call-tx);
}
body.sv-on .sv-act-call .sv-act-cap { color: rgba(255, 255, 255, 0.82); }
/* NOTHING TO CALL YET IS STILL A CONTROL. Requirement: never hide the
   capability — a dashed button that says "Add email" teaches the parent the
   app can hold one; a missing row teaches nothing. */
body.sv-on .sv-act.add {
  background: transparent; border-style: dashed; color: var(--dv2-text-sub);
}
body.sv-on .sv-act:active { transform: scale(.96); }
@media (prefers-reduced-motion: reduce) { body.sv-on .sv-act:active { transform: none; } }

/* ── The inline contact editor ──────────────────────────────────────── */
body.sv-on .sv-edit {
  margin-top: 13px; padding-top: 13px;
  border-top: 1px solid var(--sv-tile-bd);
  display: flex; flex-direction: column; gap: 10px;
}
body.sv-on .sv-edit[hidden] { display: none; }
body.sv-on .sv-f { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
body.sv-on .sv-f-k {
  font-size: 9px; font-weight: 750; letter-spacing: .1em; text-transform: uppercase;
  color: var(--dv2-text-muted);
}
/* CONTAIN BY max-width, NEVER BY width ALONE. A form control on iOS Safari
   sizes itself to its content unless its native appearance is turned off,
   and `width: 100%` is advisory against that — which is exactly how the
   Garage date fields spilled out of their column and ran the left column
   under the right one. */
body.sv-on .sv-f-in {
  -webkit-appearance: none; appearance: none;
  box-sizing: border-box; width: 100%; max-width: 100%; min-width: 0;
  padding: 11px 12px; border-radius: 12px;
  background: var(--dv2-input-bg, rgba(255, 255, 255, 0.75));
  border: 1px solid var(--dv2-input-bd, rgba(255, 255, 255, 0.95));
  color: var(--dv2-input-text, #14121F);
  font-family: inherit; font-size: 15px; letter-spacing: -0.01em;
}
body.sv-on .sv-f-in::placeholder { color: var(--dv2-input-ph, rgba(20, 18, 31, 0.35)); }
body.sv-on .sv-f-in:focus-visible {
  outline: 2px solid var(--dv2-accent, #6341E8); outline-offset: 1px;
}
body.sv-on .sv-f-acts { display: flex; gap: 9px; justify-content: flex-end; }

/* ── The rest of the profile, tokenised ─────────────────────────────── */
/* Release 1 re-grounds these; the chapter spine replaces them in release 2.
   Without this they render on style.css's flat cream over the aurora. */
body.sv-on .pet-info-h {
  font-size: 12px; font-weight: 750; letter-spacing: .04em; text-transform: uppercase;
  color: var(--dv2-text-sub);
}
body.sv-on .pet-info-cell,
body.sv-on .pet-section,
body.sv-on .pet-act,
body.sv-on .child-terms .term-row {
  background: var(--sv-tile-bg);
  border: 1px solid var(--sv-tile-bd);
  border-radius: var(--sv-rad);
  box-shadow: var(--sv-shadow);
  color: var(--dv2-text-primary);
}
body.sv-on .pet-section { padding: 13px 14px; margin-top: 14px; }
body.sv-on .pet-info-k { color: var(--dv2-text-muted); }
body.sv-on .pet-info-v { color: var(--dv2-text-primary); }
body.sv-on .pet-act { margin-bottom: 8px; }
body.sv-on .pet-hero { background: transparent; padding: 0; }
body.sv-on .pet-sec-add,
body.sv-on .pet-sec-smart,
body.sv-on .pet-sec-info {
  background: transparent; border: 1px solid var(--sv-tile-bd);
  border-radius: 10px; color: var(--dv2-text-sub);
  width: 32px; height: 32px; display: grid; place-items: center; padding: 0;
}

/* ── SHARED COMPONENTS, AND THEIR STATES ────────────────────── */
/* READ THIS BEFORE RESTYLING ANYTHING SHARED.
   A rule here is written `body.sv-on .thing` — specificity (0,2,1). The
   component's own state rules in style.css are written `.thing.on` — (0,2,0).
   Ours wins. So the moment this file restyles a shared component it SILENTLY
   TURNS OFF that component's states, and the section then looks right in the
   one state nobody screenshots (the default) and wrong in every other.

   That is not hypothetical. It has now shipped three times:
     b183  a shared `.call` collapsed the primary action to a 32px disc;
     b184  a shared `.pf-acc.collapsed` rule outranked ours and pointed every
           closed chevron the wrong way;
     b184  THIS RULE. It gave `.pe-chip` a near-white border on a near-white
           card and outranked `.pe-chip.on`, so the SELECTED STATE VANISHED.
           A parent taps "Boy", nothing changes, they tap the other one to see
           whether the control works at all — and the child's card then states
           the wrong gender as a fact. That is what a silenced state costs.

   RULE: whenever you restyle a shared class here, restate its states here too,
   at this file's specificity. devtools/audit_sv_overrides.py lists every state
   this file has switched off; run it after touching anything shared. */

body.sv-on .pe-input {
  -webkit-appearance: none; appearance: none;
  box-sizing: border-box; max-width: 100%; min-width: 0;
  background: var(--dv2-input-bg, rgba(255, 255, 255, 0.75));
  border: 1px solid var(--sv-field-bd);
  color: var(--dv2-input-text, #14121F);
}
body.sv-on .pe-input::placeholder { color: var(--dv2-text-muted); opacity: 1; }

/* A chip has to LOOK like something you can press, and then look pressed. */
body.sv-on .pe-chip {
  -webkit-appearance: none; appearance: none;
  box-sizing: border-box; max-width: 100%; min-width: 0;
  background: transparent;
  border: 1.5px solid var(--sv-chip-bd);
  color: var(--dv2-text-primary);
  font-weight: 600;
}
body.sv-on .pe-chip.on {
  background: var(--sv-call-bg); border-color: var(--sv-call-bg);
  color: var(--sv-call-tx);
}
/* Restated because `body.sv-on .pet-act` above sets box-shadow and would
   otherwise swallow the green ring that confirms a reminder was ticked. */
body.sv-on .pet-act.just-done { box-shadow: 0 0 0 1.5px #2fae5f; }
body.sv-on .pe-label { color: var(--dv2-text-muted); }

/* ── ADDING A CHILD ──────────────────────────────────────────────────── */
/* b185 — this is the first screen a parent ever sees in School, and it was
   six identical full-width cards, each holding one field, filling two and a
   half screens. Everything had the same weight, so nothing had any: the
   school's name and the child's name were typographically identical, and the
   only two CONTROLS on the screen — gender and travel — read as paragraphs of
   plain text because their chips had a near-white border on a white card.

   It is two groups now: the child, then their school. The fields inside a
   group share one card with hairlines between them, which is what makes a
   group read as a group. */
body.sv-on .sv-group { display: block; }
body.sv-on .sv-group-h {
  margin: 2px 0 8px; padding: 0 4px;
  font-size: 11.5px; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--dv2-text-muted);
}
/* One card per group, hairlines between the rows. The rows live in their own
   wrapper so first/last are STRUCTURAL — `.pe-field` is worn by both <label>
   and <div> here, and :first-of-type matched one of each, which split the
   group into two cards with a seam through the middle of it. */
body.sv-on .sv-fields {
  border: 1px solid var(--sv-tile-bd); border-radius: var(--sv-rad);
  box-shadow: var(--sv-shadow); overflow: hidden;
}
body.sv-on .sv-fields .pe-field {
  background: var(--sv-tile-bg);
  border: 0; border-top: 1px solid var(--sv-tile-bd);
  border-radius: 0; box-shadow: none; gap: 5px; padding: 11px 14px;
}
body.sv-on .sv-fields > .pe-field:first-child { border-top: 0; }
body.sv-on .pe-label {
  color: var(--dv2-text-muted); font-size: 11px; letter-spacing: .05em;
}
/* Inside a grouped row the card IS the field, so the input must not draw a
   second box around itself. */
body.sv-on .sv-fields .pe-input {
  border: 0; background: transparent; padding: 1px 0; font-size: 16.5px;
}
/* A placeholder must not read as a value. The shipped screen showed
   "e.g. Vihaan" in almost the same ink as real text, which on the school field
   meant a parent could believe a school was already chosen. */
body.sv-on .pe-input::placeholder { color: var(--dv2-text-muted); opacity: .62; }
body.sv-on .sv-select select.sv-unset { color: var(--dv2-text-muted); opacity: .62; }
body.sv-on .sv-hint {
  display: block; margin-top: 5px; font-size: 12px; line-height: 1.4;
  color: var(--dv2-text-muted);
}

/* The year group. Fifteen answers, so it is a picker — on a phone that is the
   native wheel, which is both faster and impossible to spell wrong. */
body.sv-on .sv-select { position: relative; display: block; }
body.sv-on .sv-select select {
  -webkit-appearance: none; appearance: none;
  width: 100%; padding-right: 26px; cursor: pointer;
}
body.sv-on .sv-select select:invalid { color: var(--dv2-text-muted); }
body.sv-on .sv-select-chev {
  position: absolute; right: 0; top: 50%; margin-top: -9px;
  transform: rotate(90deg); color: var(--dv2-text-muted); pointer-events: none;
}

/* A labelled Save. The shared control is a 38px circle holding a tick, which
   on the screen that creates a child says nothing about what it will do. */
/* Every confirm in School is the same green — including the cropper's and the
   framing editor's, which are shared sheets and were still the app accent. */
body.sv-on #sheet-focus .pe-save,
body.sv-on #sheet-cropper .pe-save {
  background: var(--sv-call-bg); color: var(--sv-call-tx);
}
body.sv-on .pe-save.sv-save {
  width: auto; height: 34px; padding: 0 16px; border-radius: 17px;
  background: var(--sv-call-bg); color: var(--sv-call-tx);
  font-family: inherit; font-size: 15px; font-weight: 700; letter-spacing: -0.01em;
}
body.sv-on .pe-x { background: var(--sv-tile-bg); border: 1px solid var(--sv-tile-bd);
  color: var(--dv2-text-primary); }

/* The photograph, previewed at the SHAPE IT WILL BE SHOWN AT. A 96px square
   preview for a picture that lands in a 1.9:1 card is a small lie the parent
   only finds out about afterwards. */
body.sv-on .child-photo-btn { gap: 9px; margin: 2px 0 10px; }
body.sv-on .child-photo-prev,
body.sv-on .child-photo-ph {
  width: 100%; height: 108px; border-radius: var(--sv-rad);
  box-shadow: none; border: 1px solid var(--sv-tile-bd);
}
body.sv-on .child-photo-ph {
  background: var(--sv-tile-bg); color: var(--dv2-text-sub);
  display: grid; place-items: center;
}
body.sv-on .child-photo-ph .sv-ic { width: 30px; height: 30px; stroke-width: 1.5; }
body.sv-on .child-photo-cap {
  font-size: 13.5px; font-weight: 600; color: var(--dv2-text-sub);
}
body.sv-on .pe-title { color: var(--dv2-text-primary); }
body.sv-on .school-suggest,
body.sv-on .school-info-chip {
  background: var(--sv-tile-bg); border: 1px solid var(--sv-tile-bd);
  color: var(--dv2-text-primary);
}
body.sv-on .school-suggest-item { color: var(--dv2-text-primary); }
body.sv-on .ss-meta,
body.sv-on .si-addr,
body.sv-on .si-ph { color: var(--dv2-text-sub); }

/* ── Tab bar gutter ─────────────────────────────────────────────────── */
/* body.dv2-app stands the shared 20px body padding down for the dashboard's
   near-full-bleed constellation. School is a reading surface and wants it
   back — the same stand-down profile-c.css handles rather than leaving to
   luck, and the reason a re-gate has to bring its own gutter with it. */
body.sv-on #view-school {
  padding: 0 16px calc(112px + env(safe-area-inset-bottom, 0px));
}
body.sv-on .pets-body { padding-left: 0; padding-right: 0; }

/* ═══════════════════════════════════════════════════════════════════════
   THE BOOK — school-year chapters (b184)
   ═══════════════════════════════════════════════════════════════════════
   A child's documents used to be a flat stack of accordions in upload order.
   Here they are CHAPTERS on a spine: a knot per year, a stem joining them,
   newest at the top, "Earlier" at the bottom. The spine is the one piece of
   ornament in the section and it earns its place by carrying information —
   it is what makes a run of years read as one continuous record rather than
   as a list of folders that happen to be named after years.

   The knot is drawn with a border, not a shadow, because it sits on a
   translucent tile ground and a shadow on glass reads as smudge. */

body.sv-on .sv-spine {
  margin: 0 0 18px;
  /* Where a knot sits inside a chapter header: 12px of padding, plus half of
     a two-line title block. Said once, used by the knot and by both ends of
     the stem, because three copies of one number is three chances to drift. */
  --sv-knot-y: 32px;   /* 12px of header padding + half of a 40px gutter */
}

body.sv-on .sv-chap { position: relative; }

body.sv-on .sv-chap-head {
  width: 100%; display: flex; align-items: center; gap: 12px;
  background: transparent; border: 0; padding: 12px 2px; margin: 0;
  font-family: inherit; text-align: left; cursor: pointer; color: inherit;
}
body.sv-on .sv-chap-head:active { opacity: .62; }

/* The gutter: a knot on the year, a stem down to the next one. The stem is
   absolutely positioned so it spans the whole chapter (head AND body) — a
   stem that stopped at the header would break every time a chapter opened. */
/* FIXED HEIGHT, NOT STRETCHED. Stretched, the gutter took the header's height
   and the knot's `top` became relative to a box that moved with the text —
   which put the knot 43px down while both ends of the stem, measured from the
   chapter, were trimmed at 31px. A stem that misses its knot by 12px is the
   kind of thing a passing suite cannot see. */
body.sv-on .sv-gut {
  flex: none; width: 14px; height: 40px; align-self: flex-start;
  margin-top: 12px; position: relative;
}
body.sv-on .sv-knot {
  position: absolute; left: 2px; top: 20px; transform: translateY(-50%);
  width: 10px; height: 10px; border-radius: 50%;
  border: 2px solid var(--dv2-text-sub); background: var(--dv2-bg-grad, #F4F2FA);
  z-index: 2;
}
body.sv-on .sv-chap.now .sv-knot {
  border-color: var(--sv-call-bg); background: var(--sv-call-bg);
}
/* The stem spans the WHOLE chapter, head and body, so it does not break every
   time a chapter is opened. It is centred on the knot by arithmetic rather
   than by eye: gutter left 2px + half of a 10px knot = 7px, so a 2px line
   starts at 6px. Both ends are trimmed to --sv-knot-y, which is where the
   knot's centre sits inside a header of known padding. */
body.sv-on .sv-chap::before {
  content: ""; position: absolute; left: 6px; top: 0; bottom: 0;
  width: 2px; background: var(--sv-tile-bd); z-index: 0;
}
body.sv-on .sv-chap:first-child::before { top: var(--sv-knot-y); }
body.sv-on .sv-chap:last-child::before { bottom: auto; height: var(--sv-knot-y); }
body.sv-on .sv-chap:first-child:last-child::before { display: none; }

body.sv-on .sv-chap-main { flex: 1; min-width: 0; display: block; }
body.sv-on .sv-chap-yr {
  display: block; font-size: 16px; font-weight: 720; letter-spacing: -0.02em;
  color: var(--dv2-text-primary);
}
body.sv-on .sv-chap-sub {
  display: block; margin-top: 1px; font-size: 12px; font-weight: 560;
  color: var(--dv2-text-sub);
}
body.sv-on .sv-chap-chev {
  flex: none; color: var(--dv2-text-sub);
  transform: rotate(90deg); transition: transform .22s ease;
}
body.sv-on .sv-chap.closed .sv-chap-chev { transform: rotate(0deg); }
@media (prefers-reduced-motion: reduce) {
  body.sv-on .sv-chap-chev { transition: none; }
}

/* A closed chapter still shows what is in it. Four thumbnails is the whole
   reason to scroll back to a year you have not opened in two years. */

body.sv-on .sv-chap-body {
  padding: 0 0 10px 26px; position: relative; z-index: 1;
}
body.sv-on .sv-chap.closed .sv-chap-body { display: none; }
body.sv-on .sv-chap-empty {
  margin: 0 0 6px; font-size: 13px; line-height: 1.5; color: var(--dv2-text-sub);
}

/* ── An entry ───────────────────────────────────────────────────────── */
/* Not "a document". The tab is the same accordion every profile uses so the
   shared tools keep working; what changes is that it wears the mark of what
   it IS — a report, a certificate, a term list, a letter. */
body.sv-on .sv-entry { margin-bottom: 10px; }
body.sv-on .sv-entry .pet-info-h {
  display: flex; align-items: flex-start; gap: 9px; min-width: 0;
}
body.sv-on .sv-entry-ic { flex: none; color: var(--dv2-text-sub); margin-top: 1px; }
body.sv-on .sv-entry-txt { min-width: 0; display: block; }
body.sv-on .sv-entry-t { display: block; }
/* The subtitle lives in the HEADER, not the body, so a closed entry still says
   what it is and when it arrived. A book of nine identically-titled tabs is a
   filing cabinet again. */
body.sv-on .sv-entry-sub {
  display: block; margin-top: 2px; font-size: 12px; font-weight: 560;
  letter-spacing: 0; color: var(--dv2-text-sub);
}
body.sv-on .sv-entry .cdoc-sub,
body.sv-on .sv-entry .cdoc-type { color: var(--dv2-text-sub); }
body.sv-on .sv-entry .pf-acc-body > :first-child { margin-top: 0; }
/* Five tools on a ~330px row: they wrap, or the fifth one leaves the card.
   The shared rule is a bare `display:flex` with no wrap, which is exactly the
   kind of thing that only shows up on a phone. */
body.sv-on .cdoc-tools { flex-wrap: wrap; }
body.sv-on .cdoc-tool { color: var(--dv2-text-primary); background: var(--sv-tile-bg);
  border: 1px solid var(--sv-tile-bd); }
body.sv-on .cdoc-tool .sv-ic { width: 16px; height: 16px; }

/* ── Photographs ────────────────────────────────────────────────────── */

/* Its own viewer, so opening a class photo does not close the book, walk to
   the Vault and lose your place in it. */
/* Gated like everything else in this file, and that is not a formality here:
   the overlay is appended to <body> and outlives the sheet it was opened from,
   so without the gate it would be a fixed full-screen black panel that any
   other section could inherit. Gated, `display:grid` simply stops applying and
   the element's own `hidden` attribute takes over. */

/* ── The uploader bar above the book ────────────────────────────────── */
body.sv-on .sv-bookbar { margin-bottom: 4px; }
/* The shared spinner is a Material Symbol. School cannot use one, so it spins
   a ring of its own. */
body.sv-on .sv-spin {
  display: inline-block; width: 15px; height: 15px; margin-right: 8px;
  vertical-align: -2px; border-radius: 50%;
  border: 2px solid var(--sv-tile-bd); border-top-color: var(--dv2-text-sub);
  animation: sv-spin .8s linear infinite;
}
@keyframes sv-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { body.sv-on .sv-spin { animation: none; } }
body.sv-on .pet-sec-loading { color: var(--dv2-text-sub); }

/* ── The accordion chevron, drawn from the line set ─────────────────── */
/* The shared rule already turns a chevron -90deg when its section collapses,
   and it is more specific than anything written here — a competing transform
   in this file lost, and every closed entry pointed UP. So the wrapper is left
   entirely to the shared rules and only the MARK INSIDE it is turned, from
   the line set's rightward chevron to a downward one. The cascade then does
   what it was already doing, correctly. */
body.sv-on .pf-acc-chev-line {
  display: inline-grid; place-items: center; font-size: 0;
}
body.sv-on .pf-acc-chev-line .sv-ic {
  width: 18px; height: 18px; transform: rotate(90deg);
}

/* ── The action menu, when School opened it ─────────────────────────── */
body.sv-on .pm-item .sv-ic { width: 22px; height: 22px; color: var(--text-2); flex: none; }
body.sv-on .pm-item.danger .sv-ic { color: #e5484d; }
body.sv-on .pm-item.on { color: var(--accent); }

/* ── Positioning the child's photograph ─────────────────────────────── */
/* #sheet-focus is styled only under body.tv-on, so School brings its own —
   at ITS two shapes. A repositioner that previews the wrong crop is worse
   than none, because it tells you a lie you then save. */
body.sv-on .focus-body { padding: 4px 16px 20px; display: flex; flex-direction: column; gap: 8px; }
body.sv-on .focus-lab {
  margin: 6px 0 0; font-size: 11.5px; font-weight: 700; letter-spacing: .4px;
  text-transform: uppercase; color: var(--dv2-text-sub);
}
body.sv-on .focus-frame {
  width: 100%; border-radius: 16px; overflow: hidden; cursor: grab;
  border: 1px solid var(--sv-tile-bd); touch-action: none;
  background: linear-gradient(160deg, #4A3A78, #6E6890);
}
body.sv-on .focus-frame:active { cursor: grabbing; }
body.sv-on .focus-hero { aspect-ratio: 358 / 216; }   /* the child's own page */
body.sv-on .focus-band { aspect-ratio: 358 / 190; }   /* the School screen    */

/* The preview IS the card — same scrim, same name, same line. Otherwise it
   answers "which part of the picture is visible" when the question being
   asked is "what will this look like". */
body.sv-on .focus-frame { position: relative; }
body.sv-on .sv-prev-scrim {
  position: absolute; inset: 0; background: var(--sv-scrim); pointer-events: none;
}
body.sv-on .sv-prev-ovl {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  padding: 12px 14px; display: flex; flex-direction: column; gap: 3px;
  align-items: flex-start; pointer-events: none;
}
body.sv-on .sv-prev-name {
  color: #fff; font-size: 20px; font-weight: 740; letter-spacing: -0.025em;
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.42);
}
body.sv-on .sv-prev-line {
  color: rgba(255, 255, 255, 0.88); font-size: 12px; font-weight: 560;
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.4);
}
body.sv-on .focus-band .sv-prev-name { font-size: 18px; }
body.sv-on .focus-centre { align-self: center; margin-top: 6px; }
body.sv-on .crop-hint { color: var(--dv2-text-sub); }
body.sv-on .pe-title, body.sv-on .crop-head .pe-title { color: var(--dv2-text-primary); }

/* ═══════════════════════════════════════════════════════════════════════
   BUILT TO THE APPROVED BOARD (b187)
   ═══════════════════════════════════════════════════════════════════════
   Everything below is a transcription of Concept C as signed off, not an
   interpretation of it. Where a measurement is given it is the board's.

   THE RIBBON — approved screens 1 and 5. A year of term time as one bar:
   green term, red holiday, amber event, segments proportional to their real
   length so the shape of the year is the information. 5px on a child card,
   14px on the term-dates tile. */
body.sv-on .sv-rib {
  display: flex; height: 14px; border-radius: 8px; overflow: hidden; gap: 1.5px;
}
body.sv-on .sv-rib i { display: block; height: 100%; }
body.sv-on .sv-rib i.g { background: var(--sv-rib-g); }
body.sv-on .sv-rib i.r { background: var(--sv-rib-r); }
body.sv-on .sv-rib i.a { background: var(--sv-rib-a); }
body.sv-on .sv-rib-sm { height: 5px; border-radius: 99px; margin-top: 7px; width: 100%; }

/* COMING UP — approved screen 1. */
body.sv-on .sv-hh {
  font-size: 12px; font-weight: 800; letter-spacing: -0.01em;
  color: var(--dv2-text-sub); padding: 2px 2px 0; margin-top: 4px;
}
body.sv-on .sv-ups { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
body.sv-on .sv-up {
  display: flex; align-items: center; gap: 10px;
  background: var(--sv-tile-bg); border: 1px solid var(--sv-tile-bd);
  border-radius: var(--sv-rad); box-shadow: var(--sv-shadow); padding: 10px 12px;
}
body.sv-on .sv-up-bar { width: 4px; height: 26px; border-radius: 3px; flex: none; }
body.sv-on .sv-up-n {
  display: block; font-size: 14px; font-weight: 700; letter-spacing: -0.015em;
  color: var(--dv2-text-primary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
body.sv-on .sv-up-s {
  display: block; margin-top: 2px; font-size: 11.5px; color: var(--dv2-text-sub);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
body.sv-on .sv-face {
  width: 26px; height: 26px; border-radius: 50%; flex: none;
  background-size: cover; background-position: center;
  border: 1px solid var(--sv-tile-bd);
}

/* THE HERO IS FULL-BLEED — approved screen 2, which sets border-radius:0 and
   drops both side borders. It was an inset rounded card; the board shows the
   photograph running edge to edge under the nav, and the page's gutter starts
   below it. */
body.sv-on #child-profile-body .sv-hero {
  margin: 0 0 11px; border-radius: 0;
}
body.sv-on #child-profile-body .sv-hero-img { height: 190px; }
body.sv-on #child-profile-body .sv-hero-name { font-size: 23px; }
body.sv-on .sv-page {
  padding: 0 14px 24px; display: flex; flex-direction: column; gap: 11px;
}
/* The sections inside the page own their own spacing through the gap, so the
   shared top margin would double it. */
body.sv-on .sv-page > .pet-section,
body.sv-on .sv-page > .pet-info { margin-top: 0; }
body.sv-on .sv-page > .sv-spine { margin: 0; }
body.sv-on .sv-page > .sv-hh { margin-top: 2px; }

/* TERM DATES — approved screen 5, transcribed. */
body.sv-on .sv-year {
  background: var(--sv-tile-bg); border: 1px solid var(--sv-tile-bd);
  border-radius: var(--sv-rad); box-shadow: var(--sv-shadow); padding: 12px 13px;
}
body.sv-on .sv-year-top {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin-bottom: 12px;
}
body.sv-on .sv-eyebrow {
  font-size: 9.5px; font-weight: 800; letter-spacing: .14em;
  text-transform: uppercase; color: var(--dv2-text-muted);
}
body.sv-on .sv-pill {
  font-size: 10px; font-weight: 700; padding: 3px 8px; border-radius: 99px;
  border: 1px solid var(--sv-tile-bd); background: var(--sv-tile-bg);
  white-space: nowrap; flex: none;
}
body.sv-on .sv-pill.g { color: var(--sv-rib-g); border-color: color-mix(in srgb, var(--sv-rib-g) 34%, transparent); background: color-mix(in srgb, var(--sv-rib-g) 13%, transparent); }
body.sv-on .sv-pill.r { color: var(--sv-rib-r); border-color: color-mix(in srgb, var(--sv-rib-r) 34%, transparent); background: color-mix(in srgb, var(--sv-rib-r) 13%, transparent); }
body.sv-on .sv-pill.a { color: var(--sv-rib-a); border-color: color-mix(in srgb, var(--sv-rib-a) 34%, transparent); background: color-mix(in srgb, var(--sv-rib-a) 13%, transparent); }

/* The marker for today rides ON the bar, which is why the wrapper is the
   positioning context and the bar is not. */
body.sv-on .sv-ribwrap { position: relative; padding-top: 6px; }
body.sv-on .sv-now {
  position: absolute; top: 0; width: 2px; height: 22px; border-radius: 2px;
  background: var(--dv2-text-primary);
  box-shadow: 0 0 0 2px var(--sv-tile-bg);
}
body.sv-on .sv-months {
  display: flex; justify-content: space-between; margin-top: 5px;
  font-size: 8.5px; font-weight: 700; letter-spacing: .06em;
  color: var(--dv2-text-muted);
}
body.sv-on .sv-legend { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 11px; }
body.sv-on .sv-legend span {
  display: flex; align-items: center; gap: 5px;
  font-size: 10px; color: var(--dv2-text-sub);
}
body.sv-on .sv-legend i { width: 8px; height: 8px; border-radius: 3px; display: block; }
body.sv-on .sv-terms { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
body.sv-on .child-terms { display: flex; flex-direction: column; gap: 0; }
body.sv-on .child-terms .sv-hh { margin-top: 12px; }

/* BACK TO SCHOOL — approved screen 7. The row IS the Home task row, so it
   wears the shared .tc-* classes and inherits the 96px first track that
   verify_taskcard.py calls the benchmark. Only two things are School's: the
   card's own surface, and the category glyph the board puts beside the name. */
/* b210 — SPACING AND TYPE BELONG TO THE SHARED ROW, NOT TO SCHOOL.
   This block used to set its own padding (10/12 against the standard's 9/14),
   its own gap (9 against 11), its own type (15px/700 against 17px/750) and its
   own one-line clamp. Each was defensible on its own and together they meant a
   task looked like a different component depending on which section you were
   in — which is the whole complaint this release answers. What is left is
   colour: the card's ground, and the amber that means a date has arrived. */
body.sv-on .sv-cat { flex: none; color: var(--dv2-text-sub); }
/* Restated, or the line above silences the one colour on this row that means
   something — the amber that says a date has arrived. Fourth time this file
   has done this to a shared state; audit_sv_overrides.py caught it before it
   shipped, which is the whole reason it exists. */
body.sv-on .sv-task-body {
  flex: 1; min-width: 0; border: 0; background: transparent; padding: 0;
  text-align: left; font-family: inherit; cursor: pointer;
}

/* The header tile the board puts above the list. */
body.sv-on .sv-bts {
  display: flex; align-items: center; gap: 10px; margin-bottom: 10px;
  padding: 11px 12px; border-radius: var(--sv-rad);
  background: color-mix(in srgb, var(--sv-call-bg) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--sv-call-bg) 34%, transparent);
}
body.sv-on .sv-bts-ic { flex: none; width: 22px; height: 22px; color: var(--sv-call-bg); }
body.sv-on .sv-pill.v {
  color: var(--sv-call-bg);
  border-color: color-mix(in srgb, var(--sv-call-bg) 36%, transparent);
  background: color-mix(in srgb, var(--sv-call-bg) 14%, transparent);
}

/* The assignee chip is the shared control, and unassigned it renders as a wide
   pill with a chevron. On the board's row the name runs full width and the
   assignee is a small circle, so here it is compact — the row's job is the
   name and the fixed track, and "Lunchbox &…" is the row failing at it. */

/* An unread term calendar. The section used to hide itself when there were no
   dates, which made a pipeline that had never run look like a feature that had
   never been built — the document sat in the book titled "Term Dates" and the
   term dates screen simply did not exist. */
body.sv-on .sv-unread {
  margin: 0 0 11px; font-size: 13.5px; line-height: 1.5; color: var(--dv2-text-sub);
}
body.sv-on .sv-tool-go {
  background: color-mix(in srgb, var(--sv-call-bg) 14%, transparent);
  border-color: color-mix(in srgb, var(--sv-call-bg) 36%, transparent);
  color: var(--sv-call-bg);
}

/* Download is a link, not a button, because the browser has to follow it for
   the Content-Disposition header to mean anything. It has to look identical
   to the buttons beside it. */
body.sv-on a.cdoc-tool { text-decoration: none; }

/* ═══════════════════════════════════════════════════════════════════════
   THE SCREENS (b190)
   Concept C is a section of pages. These are the ones that had been folded
   into the child's profile as accordions.
   ═══════════════════════════════════════════════════════════════════════ */
body.sv-on #sheet-sv > .sheet-closerail { display: none; }
body.sv-on #sheet-sv.profile-page {
  background: var(--dv2-bg-grad, #F4F2FA); color: var(--dv2-text-primary);
}
body.sv-on .sv-top-spacer { visibility: hidden; }

/* BOARD SCREEN 3 · The school. */
body.sv-on .sv-school-head { display: flex; align-items: center; gap: 11px; margin-bottom: 12px; }
body.sv-on .sv-crest {
  width: 42px; height: 42px; border-radius: 50%; flex: none; display: grid;
  place-items: center; background: var(--sv-tile-bg); border: 1px solid var(--sv-tile-bd);
  color: var(--dv2-text-sub);
}
body.sv-on .sv-crest .sv-ic { width: 22px; height: 22px; }
body.sv-on .sv-rows {
  background: var(--sv-tile-bg); border: 1px solid var(--sv-tile-bd);
  border-radius: var(--sv-rad); box-shadow: var(--sv-shadow); padding: 4px 13px;
}
body.sv-on .sv-inforow {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 9px 0; border: 0; border-bottom: 1px solid var(--sv-tile-bd);
  background: transparent; text-align: left; font-family: inherit; color: inherit;
}
body.sv-on .sv-inforow:last-child { border-bottom: 0; }
body.sv-on .sv-inforow .sv-ic { flex: none; color: var(--dv2-text-sub); }
body.sv-on .sv-k {
  display: block; font-size: 9.5px; font-weight: 800; letter-spacing: .12em;
  text-transform: uppercase; color: var(--dv2-text-muted);
}
body.sv-on .sv-v {
  display: block; font-size: 12.5px; font-weight: 600; letter-spacing: -0.01em;
  color: var(--dv2-text-primary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
body.sv-on .sv-v.mut { color: var(--dv2-text-muted); }
body.sv-on .sv-v.ok { color: var(--sv-term-ink); }
body.sv-on .sv-v.warn { color: var(--sv-rib-a); }
body.sv-on .sv-v.bad { color: var(--sv-rib-r); }
body.sv-on .sv-go {
  width: 26px; height: 26px; border-radius: 8px; flex: none; display: grid;
  place-items: center; text-decoration: none;
  background: color-mix(in srgb, var(--sv-call-bg) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--sv-call-bg) 40%, transparent);
  color: var(--sv-call-bg);
}
body.sv-on .sv-go.q {
  background: var(--sv-tile-bg); border-style: dashed;
  border-color: var(--sv-tile-bd); color: var(--dv2-text-muted);
}
body.sv-on .sv-sibling {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  padding: 10px 12px; border-radius: var(--sv-rad);
  background: var(--sv-tile-bg); border: 1px solid var(--sv-tile-bd);
  box-shadow: var(--sv-shadow); font-family: inherit; cursor: pointer;
}
body.sv-on .sv-sib-n {
  display: block; font-size: 12.5px; font-weight: 600; color: var(--dv2-text-primary);
}
/* The card on the profile is the way in to that screen. */
body.sv-on .sv-school { position: relative; }
body.sv-on .sv-school-open {
  position: absolute; inset: 0; border: 0; background: transparent;
  border-radius: var(--sv-rad); cursor: pointer; z-index: 0;
}
body.sv-on .sv-school-top, body.sv-on .sv-acts, body.sv-on .sv-edit { position: relative; z-index: 1; }

/* BOARD SCREEN 6 · a year. */
body.sv-on .sv-eyeline {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; padding: 0 2px;
}

/* BOARD SCREEN 4 · What's on. */
body.sv-on .sv-chips { display: flex; gap: 6px; overflow-x: auto; padding-bottom: 2px; }
body.sv-on .sv-chip {
  font-size: 11px; font-weight: 600; padding: 6px 11px; border-radius: 99px;
  background: var(--sv-tile-bg); border: 1px solid var(--sv-tile-bd);
  color: var(--dv2-text-sub); white-space: nowrap; font-family: inherit; cursor: pointer;
}
body.sv-on .sv-chip.on {
  background: var(--dv2-text-primary); color: var(--dv2-bg-grad, #F4F2FA);
  border-color: var(--dv2-text-primary); font-weight: 700;
}
body.sv-on .sv-pt { display: flex; gap: 10px; align-items: flex-start; }
body.sv-on .sv-when {
  width: 40px; flex: none; display: flex; flex-direction: column; align-items: center;
  padding-top: 2px; font-variant-numeric: tabular-nums;
}
body.sv-on .sv-when b {
  font-size: 16px; font-weight: 800; letter-spacing: -.03em; line-height: 1;
  color: var(--dv2-text-primary);
}
body.sv-on .sv-when i {
  font-style: normal; font-size: 9px; font-weight: 800; letter-spacing: .1em;
  text-transform: uppercase; color: var(--dv2-text-muted); margin-top: 2px;
}
body.sv-on .sv-pt-card {
  flex: 1; min-width: 0; background: var(--sv-tile-bg); border: 1px solid var(--sv-tile-bd);
  border-radius: 13px; padding: 9px 11px; display: flex; flex-direction: column; gap: 4px;
  border-left: 3px solid var(--sv-tile-bd);
}
body.sv-on .sv-pt-card.g { border-left-color: var(--sv-rib-g); }
body.sv-on .sv-pt-card.r { border-left-color: var(--sv-rib-r); }
body.sv-on .sv-pt-card.a { border-left-color: var(--sv-rib-a); }
body.sv-on .sv-src {
  display: flex; align-items: center; gap: 5px;
  font-size: 10px; color: var(--dv2-text-muted);
}
body.sv-on .sv-src .sv-ic { width: 13px; height: 13px; }

/* BOARD SCREEN 7 · the header tile is the board's VIOLET, not School's green.
   The green was my "one action colour" idea and it was never approved. */
body.sv-on .sv-bts {
  background: color-mix(in srgb, var(--dv2-accent, #8F5FBE) 12%, transparent);
  border-color: color-mix(in srgb, var(--dv2-accent, #8F5FBE) 36%, transparent);
}
body.sv-on .sv-bts-ic { color: var(--dv2-accent, #8F5FBE); }
body.sv-on .sv-pill.v {
  color: var(--dv2-accent, #8F5FBE);
  border-color: color-mix(in srgb, var(--dv2-accent, #8F5FBE) 38%, transparent);
  background: color-mix(in srgb, var(--dv2-accent, #8F5FBE) 15%, transparent);
}

/* Coming up is a link to the timeline it summarises. */
body.sv-on button.sv-up { width: 100%; text-align: left; font-family: inherit; cursor: pointer; }

/* ── THE YEAR'S ENTRIES, LAID OUT (board screen 6) ───────────────────────
   The board opens a report — its period, its subjects with their grades, the
   comment — and gives a certificate one compact row. They had been accordion
   tabs that showed a title and hid everything else behind a tap, which is the
   opposite of a book you can read. */
body.sv-on .sv-entry-open {
  position: relative;
  background: var(--sv-tile-bg); border: 1px solid var(--sv-tile-bd);
  border-radius: var(--sv-rad); box-shadow: var(--sv-shadow); padding: 12px 13px;
}
body.sv-on .sv-entry-head { margin-bottom: 9px; padding-right: 26px; }
body.sv-on .sv-quote {
  margin: 9px 0 0; font-size: 12.5px; line-height: 1.5; color: var(--dv2-text-sub);
}
body.sv-on .sv-entry-more {
  position: absolute; top: 10px; right: 10px; width: 24px; height: 24px;
  border: 0; background: transparent; color: var(--dv2-text-muted);
  display: grid; place-items: center; cursor: pointer;
  transform: rotate(90deg); transition: transform .2s ease;
}
body.sv-on .sv-entry-open.open .sv-entry-more { transform: rotate(-90deg); }

body.sv-on .sv-entry-row {
  background: var(--sv-tile-bg); border: 1px solid var(--sv-tile-bd);
  border-radius: var(--sv-rad); box-shadow: var(--sv-shadow); overflow: hidden;
}
body.sv-on .sv-entry-line {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 11px 13px; border: 0; background: transparent;
  text-align: left; font-family: inherit; cursor: pointer;
}
body.sv-on .sv-entry-line .sv-entry-ic { flex: none; color: var(--dv2-text-sub); }
body.sv-on .sv-entry-chev {
  flex: none; color: var(--dv2-text-muted); transition: transform .2s ease;
}
body.sv-on .sv-entry-row.open .sv-entry-chev { transform: rotate(90deg); }
body.sv-on .sv-entry-row .cdoc-tools,
body.sv-on .sv-entry-open .cdoc-tools { padding: 0 13px 12px; margin-top: 8px; }
body.sv-on .sv-entry-open .cdoc-tools { padding: 0; }
@media (prefers-reduced-motion: reduce) {
  body.sv-on .sv-entry-more, body.sv-on .sv-entry-chev { transition: none; }
}

/* ── THE TASK ROW'S THREE REMAINING DIFFERENCES (board screen 7) ────────
   The board shows a small clock and a count, not a pill reading "History";
   and one coloured circle carrying the assignee's initial, not the shared
   add-person chip. */
/* b210 — the History pill is the shared card's. School had stripped its
   ground, its border and its accent colour down to plain grey text, so the one
   control in the trailing slot looked like a caption here and like a button
   everywhere else. Removed: this section has no reason to state it. */
body.sv-on .sv-who {
  width: 20px; height: 20px; border-radius: 50%; flex: none;
  display: grid; place-items: center; color: #fff;
  font-size: 9px; font-weight: 800; letter-spacing: 0;
}
body.sv-on .sv-who i { font-style: normal; font-size: 7px; margin-left: 1px; }

/* The board's report: one line per subject, grades as small squares. */
body.sv-on .sv-subjects { margin-top: 4px; }
body.sv-on .sv-subj {
  display: flex; align-items: center; gap: 7px; padding: 7px 0;
  border-bottom: 1px solid var(--sv-tile-bd);
}
body.sv-on .sv-subj:last-child { border-bottom: 0; }
body.sv-on .sv-subj-n {
  flex: 1; min-width: 0; font-size: 13px; font-weight: 650;
  color: var(--dv2-text-primary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
body.sv-on .sv-gd {
  width: 21px; height: 21px; border-radius: 6px; flex: none;
  display: grid; place-items: center;
  font-size: 10px; font-weight: 800; letter-spacing: 0;
  background: var(--sv-tile-bg); color: var(--dv2-text-sub);
}
body.sv-on .sv-gd.e { background: hsla(152,52%,52%,.24); color: hsl(152 52% 34%); }
body.sv-on .sv-gd.m { background: hsla(200,60%,58%,.22); color: hsl(200 62% 36%); }
body.sv-on .sv-gd.a { background: hsla(38,88%,60%,.26); color: hsl(34 78% 36%); }
body.sv-on .sv-gd.r { background: hsla(355,78%,66%,.24); color: hsl(355 62% 44%); }
body.sv-on .sv-gd.txt { width: auto; padding: 0 7px; font-size: 9.5px; }
html[data-theme="dark"] body.sv-on .sv-gd.e { color: hsl(152 60% 74%); }
html[data-theme="dark"] body.sv-on .sv-gd.m { color: hsl(200 70% 78%); }
html[data-theme="dark"] body.sv-on .sv-gd.a { color: hsl(38 92% 78%); }
html[data-theme="dark"] body.sv-on .sv-gd.r { color: hsl(355 82% 78%); }
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) body.sv-on .sv-gd.e { color: hsl(152 60% 74%); }
  html:not([data-theme="light"]) body.sv-on .sv-gd.m { color: hsl(200 70% 78%); }
  html:not([data-theme="light"]) body.sv-on .sv-gd.a { color: hsl(38 92% 78%); }
  html:not([data-theme="light"]) body.sv-on .sv-gd.r { color: hsl(355 82% 78%); }
}

/* ── BACK TO SCHOOL: A SEASON (b191) ────────────────────────────────────
   School holds two products. This one appears in mid-August, lives until the
   day term starts, then puts itself away — so its header is a countdown and a
   progress bar, not a due-date treadmill. */
body.sv-on .sv-bar {
  display: block; height: 4px; border-radius: 99px; margin-top: 7px;
  background: color-mix(in srgb, var(--dv2-accent, #8F5FBE) 22%, transparent);
  overflow: hidden;
}
body.sv-on .sv-bar i {
  display: block; height: 100%; border-radius: 99px;
  background: var(--dv2-accent, #8F5FBE); transition: width .3s ease;
}
@media (prefers-reduced-motion: reduce) { body.sv-on .sv-bar i { transition: none; } }
body.sv-on .sv-bts { align-items: flex-start; }
body.sv-on .sv-bts .sv-grow { min-width: 0; }
body.sv-on .sv-bts-off {
  background: var(--sv-tile-bg); border-color: var(--sv-tile-bd);
}
body.sv-on .sv-bts-off .sv-bts-ic { color: var(--dv2-text-muted); }

/* Nothing on the list yet — the one moment a year the AI is offered. */
body.sv-on .sv-bts-empty {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: 11px; padding: 30px 18px 8px;
}
body.sv-on .sv-bts-art {
  width: 60px; height: 60px; border-radius: 50%; display: grid; place-items: center;
  background: color-mix(in srgb, var(--dv2-accent, #8F5FBE) 14%, transparent);
  color: var(--dv2-accent, #8F5FBE);
}
body.sv-on .sv-bts-art .sv-ic { width: 26px; height: 26px; }
body.sv-on .sv-bts-t {
  font-size: 16px; font-weight: 720; letter-spacing: -0.02em; color: var(--dv2-text-primary);
}
body.sv-on .sv-bts-empty p {
  margin: 0; max-width: 34ch; font-size: 13.5px; line-height: 1.55; color: var(--dv2-text-sub);
}
body.sv-on .sv-bts-empty .sv-btn {
  background: var(--dv2-accent, #8F5FBE); color: #fff; margin-top: 2px;
}
body.sv-on .sv-bts-or { font-size: 11.5px; color: var(--dv2-text-muted); }
body.sv-on .sv-top-pair { display: flex; gap: 6px; }
body.sv-on .sv-bts-row, body.sv-on .sv-list-row {
  width: 100%; text-align: left; font-family: inherit; cursor: pointer; align-items: center;
}
body.sv-on .sv-bts-row .sv-ic-sm:last-child,
body.sv-on .sv-list-row .sv-ic-sm:last-child { color: var(--dv2-text-muted); flex: none; }

/* b194 — THE ASSIGNEE CHIP IS THE SHARED CONTROL, AND IT STAYS VISIBLE.
   School drew its own coloured initial (.sv-who) and hid the real chip with it,
   then hid the UNASSIGNED chip outright to save width on a seasonal row. Two
   consequences: the assignee looked different here from every other list in the
   app, and — because the unassigned chip IS the tap target — there was no way
   to assign anyone from the row at all. The bell nudges whoever a task belongs
   to, so hiding the way to say who it belongs to quietly disabled it. Same
   control, same look, everywhere. */


/* The chip's ON state needs an explicit ink: --dv2-bg-grad is a gradient, not
   a colour, so `color: var(--dv2-bg-grad)` resolved to nothing and "Start it
   again" came out white on white. */
body.sv-on .sv-chip.on { color: var(--sv-chip-on-ink); }

/* On the SEASONAL list the name is the content — it is a shopping list, and
   "School tie in house colo…" is not a thing you can buy. The shared card
   already grows for a two-line name; this is where that matters. */
/* The shared card clamps the name to two lines and ellipsises. Setting
   overflow:visible here fought that clamp instead of lifting it — the ellipsis
   still landed at line 2 and line 3 spilled out below it ("...A4… folders)").
   Raise the CLAMP, keep the overflow hidden; three lines holds every real
   uniform-list entry and the card still ends where it says it does. */

/* The seasonal header's subline carries the ONE fact the screen exists for —
   "Year 8 starts Wed 2 September · 5 days". Truncating it cut the countdown
   off, which is the half that matters. Everywhere else .sv-up-s is a one-line
   supporting note and stays clipped; here it wraps. */
body.sv-on .sv-bts .sv-up-s {
  white-space: normal; overflow: visible; text-overflow: clip; line-height: 1.35;
}

/* b192 — DOCUMENTS, FROM BOTH SIDES.
   The child's page had no way to add a document at all: the camera sat on the
   year screen and the school screen, so a parent looking at their child's
   chapters, letter in hand, had nowhere to put it. The heading now carries the
   two things that belong beside a list of documents — add one, or go to where
   they all live. */
body.sv-on .sv-hh-row {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; margin: 18px 0 8px;
}
body.sv-on .sv-hh-row .sv-hh { margin: 0; }
body.sv-on .sv-hh-acts { display: flex; gap: 7px; flex: none; }
/* .sv-hh-btn and .vault-kid-acts live in style.css, NOT here. This file is
   gated on body.sv-on and every rule in it must be; the Vault is not a School
   view, so a rule it needs cannot be written under that gate. Putting them
   here would have styled the School side and left the Vault side bare. */
/* Where the upload says it is working. It used to write into #child-docs,
   which is hidden on this page, so the feedback was invisible. */
body.sv-on #child-doc-drop:empty { display: none; }
body.sv-on #child-doc-drop { margin-bottom: 8px; }


/* b194 — DOCUMENTS, NOT CHAPTERS. The spine is gone: no gutter, no knot, no
   year headings. A parent opens School to find the term dates or the last
   report, so the documents are grouped by what they are and the filter is one
   row of chips at the top. */
body.sv-on .sv-doc-chips { margin: 2px 0 10px; }
body.sv-on .sv-doc-hh { margin: 16px 0 6px; }
/* :first-child, never :first-of-type. verify_school_polish enforces this
   because .pe-field is worn by both a <label> and a <div>, so :first-of-type
   matches one of each and cuts a group in half. The rule is the file's, not
   this selector's — keep to it everywhere. */
body.sv-on #child-book > .sv-doc-hh:first-child { margin-top: 4px; }
body.sv-on #child-book { display: block; }

/* b200 — FROM SCHOOL. The feed sits above Coming up because a notice is the
   thing that changes without the parent doing anything; everything below it is
   already theirs and will still be there tomorrow. */
body.sv-on .sv-hh-row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
body.sv-on .sv-news-pill {
  font-size: 10.5px; font-weight: 750; letter-spacing: .04em;
  padding: 3px 8px; border-radius: 999px;
  color: var(--dv2-accent, #8F5FBE);
  background: color-mix(in srgb, var(--dv2-accent, #8F5FBE) 16%, transparent);
}
body.sv-on .sv-news { display: flex; flex-direction: column; gap: 8px; }
body.sv-on .sv-news-row {
  display: flex; align-items: flex-start; gap: 10px; width: 100%; text-align: left;
  padding: 12px 12px 12px 0; border-radius: 14px;
  border: 1px solid var(--sv-tile-bd, rgba(140,140,160,.22));
  background: var(--dv2-g-06-bg, rgba(255,255,255,.04));
  overflow: hidden;
}
body.sv-on .sv-news-row:active { transform: scale(.995); }
/* Unread is a weight change, not a colour: the row already carries a coloured
   bar for what KIND of notice it is, and two colours competing on one row is
   how a list stops being scannable. */
body.sv-on .sv-news-row.is-new .sv-up-n { font-weight: 760; }
body.sv-on .sv-news-row.is-new { border-color: color-mix(in srgb, var(--dv2-accent, #8F5FBE) 34%, transparent); }
/* The stripe says what KIND of notice it is, reusing the term ribbon's own
   three colours so red means "school is shut" on both screens. `--sv-rib-*`
   are variables, not classes — an earlier version of this used `.sv-rib-r`
   as a class name and drew nothing at all. */
body.sv-on .sv-news-bar { flex: none; width: 3px; border-radius: 0 3px 3px 0; align-self: stretch; min-height: 38px; }
body.sv-on .sv-news-bar.nb-g { background: var(--sv-rib-g); }
body.sv-on .sv-news-bar.nb-r { background: var(--sv-rib-r); }
body.sv-on .sv-news-bar.nb-a { background: var(--sv-rib-a); }
body.sv-on .sv-news-bar.nb-v { background: var(--dv2-accent, #8F5FBE); }
body.sv-on .sv-news-sum {
  display: block; margin-top: 5px; font-size: 12px; line-height: 1.4;
  color: var(--dv2-text-sub);
  /* Two lines: enough to know whether to open it, not so much that the feed
     becomes the notices themselves. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
body.sv-on .sv-news-more {
  width: 100%; margin-top: 9px; padding: 10px; border-radius: 12px;
  border: 1px solid var(--sv-chip-bd, rgba(140,140,160,.34));
  background: transparent; color: var(--dv2-text-sub);
  font-size: 12.5px; font-weight: 650;
}
body.sv-on .sv-site .sv-site-in { width: 100%; max-width: 100%; margin: 10px 0 0; }
body.sv-on .sv-site .sv-btn { margin-top: 10px; }

/* ── b201 · a notice becomes School Tasks ──────────────────────────────────
   Only the FEED's own layout lives here, because school-c.css is gated
   entirely on body.sv-on and the review sheet also opens from the @ sheet in
   the Vault, where that class is not set. Everything shared sits in style.css
   beside the rest of the @ sheet. */
body.sv-on .sv-news-item { display: flex; flex-direction: column; }

/* ── b202 · School on one screen ───────────────────────────────────────────
   The child's page IS the section. A hero carrying only their name and their
   school, then majors that open where they stand.

   EVERY COLOUR HERE COMES FROM A TOKEN. b202 wrote `var(--sv-card,#fff)` and
   `rgba(60,60,67,.24)` literals, which do not move when the theme does: in
   dark mode the cards stayed white while the text went light, and the screen
   was blank. --sv-tile-bg / --sv-tile-bd / --dv2-text-primary are what every
   other surface in this file already uses, and they are defined for both
   themes at the top. */
body.sv-on .sv-kidcard {
  position: relative; border-radius: 20px; overflow: hidden; height: 186px;
  box-shadow: var(--sv-shadow);
}
body.sv-on .sv-kidhero {
  position: absolute; inset: 0; width: 100%; height: 100%; border: 0; padding: 0;
  background-size: cover; background-position: center; cursor: pointer;
}
body.sv-on .sv-kidhero-add {
  display: grid; place-items: center; gap: 8px; color: #fff;
}
body.sv-on .sv-kidveil {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to top, rgba(0,0,0,.62) 0%, rgba(0,0,0,.18) 48%, transparent 72%);
}
/* On the photograph, white is correct in BOTH themes: the ground is the
   picture and the veil, not the page. */
body.sv-on .sv-kidwho { position: absolute; left: 16px; bottom: 14px; color: #fff; pointer-events: none; }
body.sv-on .sv-kidwho b { display: block; font-size: 22px; font-weight: 700; letter-spacing: -.01em; }
body.sv-on .sv-kidwho span { display: block; font-size: 13.5px; font-weight: 500; opacity: .92; margin-top: 2px; }
body.sv-on .sv-ofsted {
  position: absolute; top: 12px; right: 12px; background: rgba(0,0,0,.55); color: #fff;
  padding: 6px 12px; border-radius: 999px; font-size: 12px; font-weight: 600;
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); pointer-events: none;
}

body.sv-on .sv-dots { display: flex; justify-content: center; gap: 7px; padding: 12px 0 4px; }
body.sv-on .sv-dot {
  width: 7px; height: 7px; border-radius: 50%; border: 0; padding: 0; cursor: pointer;
  background: var(--sv-chip-bd);
}
body.sv-on .sv-dot[aria-current="true"] { background: var(--dv2-text-primary); }

body.sv-on .sv-majors { display: flex; flex-direction: column; gap: 9px; margin-top: 2px; }
body.sv-on .sv-major {
  border-radius: var(--sv-rad); background: var(--sv-tile-bg);
  border: 1px solid var(--sv-tile-bd); box-shadow: var(--sv-shadow); overflow: hidden;
}
body.sv-on .sv-major.is-quiet { opacity: .62; }
body.sv-on .sv-major-head { display: flex; align-items: center; padding-right: 12px; }
body.sv-on .sv-major-head .sv-major-h { flex: 1; min-width: 0; }
body.sv-on .sv-major-h {
  display: flex; align-items: center; gap: 11px; padding: 15px; border: 0;
  background: none; text-align: left; cursor: pointer; color: var(--dv2-text-primary);
  font-family: inherit;
}
body.sv-on .sv-major-ic { width: 30px; height: 30px; border-radius: 9px; flex: none; display: grid; place-items: center; }
body.sv-on .sv-major-n { display: block; font-size: 16px; font-weight: 600; letter-spacing: -.01em; color: var(--dv2-text-primary); }
body.sv-on .sv-major-s { display: block; font-size: 13px; color: var(--dv2-text-sub); margin-top: 2px; }
/* CLOSED POINTS RIGHT, OPEN POINTS DOWN — the list is below, so the arrow
   turns toward it. b202 rotated 180deg, which pointed it back at the header
   it came from and told the parent nothing about where the content went. */
body.sv-on .sv-major-chev { flex: none; color: var(--dv2-text-sub); transition: transform .22s ease; }
body.sv-on .sv-major.is-open .sv-major-chev { transform: rotate(90deg); }
body.sv-on .sv-major-pill {
  background: var(--sv-term-bg); color: var(--sv-term-ink);
  padding: 4px 9px; border-radius: 999px; flex: none; font-size: 12px; font-weight: 600;
}
body.sv-on .sv-major-tools { display: flex; gap: 4px; flex: none; }
body.sv-on .sv-mtool {
  width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center;
  background: var(--sv-tile-bd); color: var(--dv2-accent, #0a84ff); cursor: pointer; border: 0;
}
body.sv-on .sv-major-b { display: none; padding: 0 12px 14px; }
body.sv-on .sv-major.is-open .sv-major-b { display: block; }
/* b206 - THE HOLE UNDER THE LAST ROW. The card already pads 14px at the
   bottom; the spine inside it added 18 and the last row another 10, so a
   major that ended in a document sat on 42px of nothing and the gap to the
   next major read as a mistake. A trailing margin is spacing BETWEEN
   siblings, and the last child has no sibling below it. */
body.sv-on .sv-major-b > :last-child,
body.sv-on .sv-major-b .sv-spine,
body.sv-on .sv-major-b .sv-spine > :last-child { margin-bottom: 0; }
body.sv-on .sv-major-empty { margin: 6px 4px; color: var(--dv2-text-sub); font-size: 14px; line-height: 1.45; }
body.sv-on .sv-past {
  width: 100%; margin-top: 6px; padding: 11px; border: 0; border-radius: 12px;
  background: var(--sv-tile-bd); color: var(--dv2-text-sub); cursor: pointer;
  font-size: 13.5px; font-weight: 600;
}
body.sv-on .sv-doc-acts { display: flex; gap: 7px; padding: 2px 0 10px; }
body.sv-on .sv-up-flat {
  display: flex; gap: 10px; align-items: center; padding: 11px 12px;
  border-radius: 12px; background: var(--sv-tile-bd); margin-bottom: 7px;
}
body.sv-on .sv-up-flat .sv-up-n { color: var(--dv2-text-primary); }
body.sv-on .sv-up-flat .sv-up-s { color: var(--dv2-text-sub); }

/* Expand all / Collapse all, on the header where it governs the whole page. */
body.sv-on .sv-allfold {
  display: flex; justify-content: flex-end; padding: 6px 2px 2px;
}
body.sv-on .sv-allfold button {
  border: 0; background: none; cursor: pointer; padding: 6px 4px;
  color: var(--dv2-accent, #0a84ff); font-size: 13.5px; font-weight: 600;
}

/* The school card folds too: closed it is the name and the rating, which is
   what a parent needs at a glance, and the address and the three actions are
   one tap away rather than always occupying a third of the screen. */
body.sv-on .sv-schoolfold-h {
  display: flex; align-items: center; gap: 10px; width: 100%; border: 0;
  background: none; cursor: pointer; padding: 0 0 8px; color: var(--dv2-text-primary);
  font-family: inherit; text-align: left;
}
body.sv-on .sv-schoolfold-h .sv-grow { min-width: 0; }
body.sv-on .sv-schoolfold-n { display: block; font-size: 15px; font-weight: 700; letter-spacing: -.01em; }
body.sv-on .sv-schoolfold-chev { flex: none; color: var(--dv2-text-sub); transition: transform .22s ease; }
body.sv-on .sv-school.is-open .sv-schoolfold-chev { transform: rotate(90deg); }
body.sv-on .sv-school .sv-school-b { display: none; }
/* b206 - THE CHEVRON OPENED THE SCHOOL'S PAGE INSTEAD OF THE CARD.
   .sv-school-open is an absolutely positioned button that makes the card a
   way in to the school's own screen, and `inset: 0` sizes it to its
   CONTAINING BLOCK. That was .sv-school - the whole card, fold header
   included - so it covered the chevron, and a positioned element paints over
   static content whatever the source order. Every tap on the header landed on
   the overlay and pushed a screen; nothing could close the card again.
   The overlay belongs to the body it opens, so the body is what positions it.
   The header is lifted as well, because one of the two alone is a fix that
   depends on the other never changing. */
body.sv-on .sv-school-b { position: relative; }
body.sv-on .sv-schoolfold-h { position: relative; z-index: 2; }
body.sv-on .sv-school.is-open .sv-school-b { display: block; }

/* b207.1 - THE TITLE STAYS IN THE FLOW.
   b207 made .title-wrap position:absolute so a third control could not push
   it off centre. .topbar-row is justify-content:space-between, so removing
   the title from the flow left three items to distribute and space-between
   put What's on in the MIDDLE - directly on top of the word School.
   Two equal side regions do the job with nothing absolutely positioned:
   flex:1 on each side and a title that shrinks to its text is exactly
   centred, whatever either side happens to hold. An element that is in the
   flow cannot land on top of another one. */
/* No min-width:0. The sides must NOT shrink below their buttons - if the row
   ever runs out of room the title is what gives, not a control. */
body.sv-on #view-school .sv-top-side {
  display: flex; align-items: center; gap: 8px; flex: 1 1 0;
}
body.sv-on #view-school .sv-top-side.end { justify-content: flex-end; }
body.sv-on #view-school .pets-topbar-row .title-wrap { flex: 0 1 auto; }
/* .pets-add-top carries margin-left:auto for the two-child header it was
   written for. Inside a group it would shove the two buttons to opposite ends
   of the right half. */
body.sv-on #view-school .sv-top-side .pets-add-top { margin-left: 0; }
/* An empty slot must not leave the group's gap behind it. */
body.sv-on #sv-topbtns:empty { display: none; }

/* b207 - What's on, in the header. It borrows .back-btn so it is the same
   circle as Back and Add a child - three controls of one shape, which is what
   makes a header read as a row rather than a collection. */
body.sv-on .sv-topbtn { position: relative; flex: none; }
body.sv-on .sv-topbtn .sv-ic { width: 21px; height: 21px; }
body.sv-on .sv-topcount {
  position: absolute; top: -3px; right: -3px; min-width: 16px; height: 16px;
  padding: 0 4px; border-radius: 99px; background: var(--sv-badge-bg);
  color: var(--sv-badge-tx); border: 1.5px solid var(--sv-badge-ring);
  font-size: 9.5px; font-weight: 800; line-height: 13px; text-align: center;
  font-variant-numeric: tabular-nums;
}

/* b206 - where the reports went. Documents no longer lists them (they have a
   section of their own), so the section says so rather than leaving a parent
   who just scanned one to wonder. */
body.sv-on .sv-doc-elsewhere {
  margin: 0 0 10px; padding: 7px 9px; border-radius: 10px;
  background: var(--sv-quiet-bg); color: var(--dv2-text-sub);
  font-size: 12.5px; line-height: 1.35;
}

/* b206 - the row that stands for two scans of one report. Said quietly: it is
   housekeeping, not news, and the report itself is what the parent came for. */
body.sv-on .sv-dup-note {
  display: flex; align-items: center; gap: 7px; margin: 8px 0 0;
  padding: 7px 9px; border-radius: 10px;
  background: var(--sv-quiet-bg);
  color: var(--dv2-text-sub); font-size: 12.5px; line-height: 1.35;
}
body.sv-on .sv-dup-note .sv-grow { min-width: 0; }
body.sv-on .sv-dup-btn {
  flex: none; border: 0; background: none; padding: 2px 2px; cursor: pointer;
  color: var(--dv2-accent); font: inherit; font-weight: 600;
}
body.sv-on .sv-entry-row .sv-dup-note { margin: 0 13px 10px; }

/* The report: a chevron that closes it, and a separate mark for the tools. */
body.sv-on .sv-entry-head { display: flex; align-items: flex-start; gap: 8px; }
body.sv-on .sv-entry-fold, body.sv-on .sv-entry-tools-btn {
  border: 0; background: none; cursor: pointer; flex: none;
  color: var(--dv2-text-sub); padding: 2px 4px; font-size: 17px; font-weight: 700;
}
body.sv-on .sv-entry-fold svg { transition: transform .22s ease; }
body.sv-on .sv-entry-open:not(.is-folded) .sv-entry-fold svg { transform: rotate(90deg); }
body.sv-on .sv-entry-open.is-folded .sv-entry-fold-b { display: none; }

/* What the grades mean, above the grades. */
body.sv-on .sv-key {
  display: flex; flex-wrap: wrap; gap: 6px 10px; padding: 10px 12px;
  border-radius: 12px; background: var(--sv-term-bg); margin: 10px 0;
}
body.sv-on .sv-key-h { width: 100%; color: var(--sv-term-ink); margin-bottom: 2px; font-size: 12px; font-weight: 600; }
body.sv-on .sv-key-i { display: inline-flex; align-items: center; gap: 5px; font-size: 12.5px; font-weight: 500; color: var(--dv2-text-primary); }
body.sv-on .sv-key-none { margin: 10px 2px; color: var(--dv2-text-sub); font-size: 13px; line-height: 1.4; }

/* Child info — what the parent typed when they added this child, which the
   app collected and then never showed them again. */
body.sv-on .sv-info-row {
  display: flex; align-items: baseline; gap: 10px; padding: 9px 4px;
  border-bottom: 1px solid var(--sv-tile-bd);
}
body.sv-on .sv-info-row:last-child { border-bottom: 0; }
body.sv-on .sv-info-k { flex: none; min-width: 132px; color: var(--dv2-text-sub); font-size: 13px; }
body.sv-on .sv-info-v { flex: 1; color: var(--dv2-text-primary); font-size: 14.5px; font-weight: 500; }
body.sv-on .sv-info-v.mut { color: var(--dv2-text-sub); font-weight: 400; }

/* The seasonal row's assignee chip: one circle, centred on the row. */

/* b205 — the child's menu, back on the photograph. Top-left, so it does not
   collide with the Ofsted badge top-right. */
body.sv-on .sv-kidmenu {
  position: absolute; top: 10px; left: 10px; z-index: 2;
  width: 34px; height: 34px; border: 0; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  background: rgba(0, 0, 0, .45); color: #fff;
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
}
