/*
 * ─────────────────────────────────────────────────────────────────────────────
 *  سجلات النشاط الطلابي — Project Brand Layer
 *  File: nshat-brand.css          Project: cjlat-nshat (Django Templates)
 *
 *  Load order:
 *    1) static/brand/shared/clearwave-design-system.css   ← core (never edited)
 *    2) static/brand/shared/nshat-brand.css               ← this file
 *
 *  Rules honoured here:
 *    • The core file is never modified; project differences live in this file.
 *    • No component that already exists in the core is redefined.
 *      This file only ADDS the project shell (page wrapper, navigation extras,
 *      footer extras, Django message mapping, academic-year switcher, data
 *      surfaces, PWA shell, print rules) and completes one token.
 *    • Every colour, radius, shadow and motion value comes from --cw-* tokens,
 *      so both themes work without duplicated literals.
 *
 *  Documented deviation (see PROJECT-DATA.md §3 and tokens.json):
 *    --cw-card-bg is declared in the light theme as well (completeness only).
 *
 *  Table of contents
 *    1.  Token completeness
 *    2.  Application shell
 *    3.  Navigation extras
 *    4.  Footer extras
 *    5.  Django messages
 *    6.  Data surfaces
 *    7.  Form helpers
 *    8.  PWA shell, mobile compensation and print
 *    9.  Accessibility guarantees for the project layer
 *   10.  Button clarity layer
 *   11.  Modal contract (hidden by default)
 *   12.  Landing tiles and chips
 *   13.  Account summary surfaces
 *   14.  Hub surfaces (grades & classes)
 *   15.  Workspace tabs, tables and cell states
 *   16.  Restrained polish (data screens)
 *   17.  Order and payment surfaces
 *   18.  App surfaces (compact, mobile-first screens)
 * ─────────────────────────────────────────────────────────────────────────────
 */

/* ═══════════════════════════════════════════════════════════════════════════
   1. TOKEN COMPLETENESS
   The core declares --cw-card-bg inside the dark block only and relies on a
   #ffffff fallback in light mode. Declaring it here keeps the token set
   symmetrical without touching the core file.
   ═══════════════════════════════════════════════════════════════════════════ */
:root {
  --cw-card-bg: #ffffff;
}

/* ═══════════════════════════════════════════════════════════════════════════
   2. APPLICATION SHELL
   .brand-page      → full page column      (defined in the core, reused as-is)
   .brand-main      → main column that hosts the navigation offset
   .brand-content   → content wrapper used by every page template (used ONCE)
   .brand-section-card → surface card used to group a page section
   ═══════════════════════════════════════════════════════════════════════════ */
.brand-main {
  min-height: 100vh;
  padding-top: 68px; /* offset for the fixed, compact .cw-nav */
  display: flex;
  flex-direction: column;
}

.brand-content {
  flex: 1 0 auto;
  padding: 28px 0;
}

.brand-content > .cw-container {
  padding: 0 32px;
}

.brand-section-card {
  background: var(--cw-surface-2);
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius-lg);
  box-shadow: var(--cw-shadow-sm);
  padding: 28px 26px;
}

.brand-section-card + .brand-section-card {
  margin-top: 20px;
}

.brand-section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 20px;
}

.brand-section-title {
  font-size: clamp(1.15rem, 2.2vw, 1.45rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--cw-text-1);
  line-height: 1.3;
  margin: 0;
}

.brand-section-title em {
  font-style: italic;
  color: var(--cw-accent);
}

.brand-section-sub {
  color: var(--cw-text-2);
  font-size: 0.9375rem;
  line-height: 1.75;
  margin: 6px 0 0;
}

.brand-page-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 18px;
  flex-wrap: wrap;
  margin-bottom: 22px;
}

.brand-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.brand-breadcrumb {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  font-size: 0.8125rem;
  color: var(--cw-text-2);
  margin-bottom: 14px;
}

.brand-breadcrumb a {
  color: var(--cw-text-2);
  transition: color var(--cw-silk-dur) var(--cw-silk);
}

.brand-breadcrumb a:hover {
  color: var(--cw-accent);
}

.brand-breadcrumb-sep {
  color: var(--cw-text-3);
  user-select: none;
}

.brand-breadcrumb-current {
  color: var(--cw-text-1);
  font-weight: 600;
}

/* ── Sectioned card + data list (profile, settings, summary screens) ─────────
   `.brand-subsection` divides one card into titled blocks with a hairline; the
   `.brand-data-list` is a semantic <dl>: one row per fact with the value aligned
   to the end of the row, collapsing to a stacked pair under 560px. Both are
   reused by every accounts screen so those pages share one source of truth. */
.brand-subsection + .brand-subsection {
  margin-top: 26px;
  padding-top: 22px;
  border-top: 1px solid var(--cw-border);
}

.brand-data-list {
  display: flex;
  flex-direction: column;
  margin: 14px 0 0;
}

.brand-data-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 18px;
  padding: 10px 0;
  border-bottom: 1px solid var(--cw-border);
}

.brand-data-row:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

.brand-data-label {
  flex: 0 0 auto;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--cw-text-2);
}

.brand-data-value {
  margin: 0;
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.7;
  color: var(--cw-text-1);
  text-align: end;
  word-break: break-word;
}

@media (max-width: 560px) {
  .brand-data-row {
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
  }

  .brand-data-value {
    text-align: start;
  }
}

/* media thumbs (school photo / logo) inside a card */
.brand-media {
  display: block;
  width: 100%;
  max-width: 320px;
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius);
  background: var(--cw-surface-2);
}

.brand-media-logo {
  max-width: 148px;
}

.brand-empty {
  text-align: center;
  padding: 44px 20px;
  color: var(--cw-text-2);
}

.brand-empty-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border-radius: var(--cw-radius-full);
  background: var(--cw-accent-ghost);
  border: 1px solid var(--cw-accent-border);
  color: var(--cw-accent);
  margin-bottom: 14px;
}

.brand-empty-title {
  font-size: 1.0625rem;
  font-weight: 700;
  color: var(--cw-text-1);
  margin: 0 0 6px;
}

.brand-empty-text {
  font-size: 0.9375rem;
  color: var(--cw-text-2);
  line-height: 1.75;
  margin: 0 0 18px;
}

/* Check list — used by plan/program summaries (the core ships no list component) */
.brand-check-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 9px;
}

.brand-check-list li {
  position: relative;
  padding-inline-start: 24px;
  font-size: 0.875rem;
  color: var(--cw-text-2);
  line-height: 1.7;
}

/* The mark itself is a SYMBOL, not a direction: a tick must look the same in
   Arabic and in English, so its two strokes are written with PHYSICAL sides —
   the one deliberate exception to this layer's logical-properties rule (the same
   reasoning §22 records for the tile rule's `transform-origin: center`).
   Written logically, as it was, `border-inline-start` lands on the RIGHT in RTL,
   and rotating *that* elbow by -45deg draws a mirrored tick — the defect the
   owner reported («<li> يظهر علامة صح مقلوبة»). It affected every screen using
   this component, not one page. Positioning stays logical; only the glyph does
   not flip. */
.brand-check-list li::before {
  content: '';
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: 0.42em;
  width: 13px;
  height: 7px;
  border-left: 2px solid var(--cw-accent);
  border-bottom: 2px solid var(--cw-accent);
  border-top-left-radius: 2px;
  transform: rotate(-45deg);
}

/* Skip link — first focusable element on every page (keyboard users) */
.brand-skip-link {
  position: absolute;
  inset-inline-start: -9999px;
  top: 0;
  z-index: 2000;
  padding: 10px 18px;
  border-start-start-radius: 0;
  border-start-end-radius: 0;
  border-end-start-radius: var(--cw-radius);
  border-end-end-radius: var(--cw-radius);
  background: var(--cw-btn-primary-bg);
  color: #ffffff;
  font-weight: 600;
  font-size: 0.875rem;
  box-shadow: var(--nshat-btn-shadow-hover);
}

.brand-skip-link:focus-visible {
  inset-inline-start: 12px;
  top: 12px;
  outline: 2px solid var(--cw-accent);
  outline-offset: 2px;
}

/* ═══════════════════════════════════════════════════════════════════════════
   3. NAVIGATION EXTRAS
   The navigation structure itself comes from the core (.cw-nav, .cw-nav-inner,
   .cw-nav-links, .cw-nav-logo, .cw-nav-dropdown-toggle, .cw-nav-hamburger,
   .cw-nav-cta, .cw-mobile-menu). Only project widgets are added below.
   ═══════════════════════════════════════════════════════════════════════════ */
.cw-nav-logo svg {
  width: 172px;
  height: 43px;
  display: block;
}

/* ── Academic-year block (core:core.set_academic_year) ──────────────────────
   Rebuilt as ONE labelled data control instead of two loose pills: a value row
   (an accent icon tile, the label, and the year itself — which IS the <select>,
   so the row stays a real form control), then a hairline, then the
   copy-previous-year action as a full-width row with its own icon.
   Nesting rule used throughout: an inner radius is the container radius minus the
   container padding, so the corners never clash. */
.nshat-year {
  --nshat-year-pad: 4px;
  display: inline-flex;
  flex-direction: column;
  align-items: stretch;
  padding: var(--nshat-year-pad);
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius);
  background: var(--cw-surface-2);
  box-shadow: var(--nshat-btn-shadow);
}

.nshat-year-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 2px 4px;
  border-radius: calc(var(--cw-radius) - var(--nshat-year-pad) - 2px);
}

/* the accent tile gives the control a focal point without a second border */
.nshat-year-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  border-radius: calc(var(--cw-radius) - var(--nshat-year-pad) - 4px);
  background: var(--cw-accent-ghost);
  color: var(--nshat-btn-accent-text);
}

.nshat-year-icon svg {
  display: block;
}

.nshat-year-label {
  font-size: 0.625rem;
  font-weight: 600;
  color: var(--cw-text-2);
  white-space: nowrap;
}

/* The value: the select with its caret drawn as a sibling SVG in `currentColor`,
   replacing the background-image data URI — so the marker carries the measured
   7.4:1 accent-text colour in both themes instead of a 2.9:1 accent graphic. */
.nshat-year-value {
  position: relative;
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
}

.nshat-year-select {
  appearance: none;
  -webkit-appearance: none;
  flex: 0 0 auto;
  border: none;
  background: transparent;
  color: var(--cw-text-1);
  font-family: var(--cw-font);
  font-size: 0.75rem;
  font-weight: 700;
  padding-block: 4px;
  padding-inline: 4px 20px;
  border-radius: calc(var(--cw-radius) - var(--nshat-year-pad) - 4px);
  cursor: pointer;
  transition: background-color var(--cw-silk-dur) var(--cw-silk);
}

.nshat-year-caret {
  position: absolute;
  inset-inline-end: 4px;
  pointer-events: none;
  color: var(--nshat-btn-accent-text);
}

/* hover keeps the ink (the digits are data, not a link) and only tints */
.nshat-year-select:hover {
  background-color: var(--cw-accent-ghost);
}

.nshat-year-select option {
  background: var(--cw-surface-2);
  color: var(--cw-text-1);
}

/* Copy-previous-year action: a full-width row under a hairline, so it reads as the
   control's secondary action and lines up with the value above it.
   --cw-accent (#3b94a8) as TEXT on a light surface measures 2.9:1 — below AA — so
   the row uses the measured 7.4:1 accent-text pair, and hover tints the surface
   instead of changing the colour (which would drop below 4.5:1). */
.nshat-year-link {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  margin-top: 3px;
  padding: 4px 6px;
  border-top: 1px solid var(--cw-border);
  border-end-start-radius: calc(var(--cw-radius) - var(--nshat-year-pad) - 2px);
  border-end-end-radius: calc(var(--cw-radius) - var(--nshat-year-pad) - 2px);
  font-size: 0.625rem;
  font-weight: 600;
  line-height: 1.3;
  color: var(--nshat-btn-accent-text);
  white-space: nowrap;
  transition: background-color var(--cw-silk-dur) var(--cw-silk);
}

.nshat-year-link svg {
  flex: 0 0 auto;
  display: block;
}

.nshat-year-link:hover {
  color: var(--nshat-btn-accent-text);
  background: var(--cw-accent-ghost);
}

/* Theme toggle (light / dark).
   Element-qualified on purpose: the core reset `.cw-theme button` is (0,1,1) and
   would otherwise wipe this button's surface and border. */
button.nshat-theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: var(--cw-radius-full);
  border: 1.5px solid var(--nshat-btn-border);
  background: var(--nshat-btn-surface);
  color: var(--cw-text-1);
  box-shadow: var(--nshat-btn-shadow);
  transition: color var(--cw-silk-dur) var(--cw-silk),
    background-color var(--cw-silk-dur) var(--cw-silk),
    border-color var(--cw-silk-dur) var(--cw-silk),
    box-shadow var(--cw-silk-dur) var(--cw-silk),
    transform var(--cw-silk-dur) var(--cw-silk);
}

button.nshat-theme-toggle:hover {
  color: var(--nshat-btn-accent-text);
  background: var(--cw-accent-ghost);
  border-color: var(--nshat-btn-border-hover);
  box-shadow: var(--nshat-btn-shadow-hover);
}

.nshat-theme-toggle svg {
  transition: transform var(--cw-silk-dur) var(--cw-silk);
}

html[data-theme="dark"] .nshat-theme-toggle svg {
  transform: rotate(180deg);
}

/* The same moon/sun swap drives every [data-theme-toggle] control, so the one
   inside the settings menu behaves exactly like the standalone button. */
.nshat-theme-toggle .nshat-icon-moon,
[data-theme-toggle] .nshat-icon-moon {
  display: block;
}

.nshat-theme-toggle .nshat-icon-sun,
[data-theme-toggle] .nshat-icon-sun {
  display: none;
}

html[data-theme="dark"] .nshat-theme-toggle .nshat-icon-moon,
html[data-theme="dark"] [data-theme-toggle] .nshat-icon-moon {
  display: none;
}

html[data-theme="dark"] .nshat-theme-toggle .nshat-icon-sun,
html[data-theme="dark"] [data-theme-toggle] .nshat-icon-sun {
  display: block;
}

/* Signed-in user chip */
.nshat-user {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px 6px 6px;
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius-full);
  background: var(--cw-surface-2);
  color: var(--cw-text-2);
  font-size: 0.8125rem;
  font-weight: 600;
  transition: color var(--cw-silk-dur) var(--cw-silk),
    background-color var(--cw-silk-dur) var(--cw-silk);
}

.nshat-user:hover {
  color: var(--cw-accent);
  background: var(--cw-accent-ghost);
}

.nshat-user-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: var(--cw-radius-full);
  background: var(--cw-accent);
  color: #ffffff;
  font-size: 0.75rem;
  font-weight: 700;
}

/* ── 3b. COMPACT HEADER (project deviation) ─────────────────────────────────
   The top bar runs one step smaller and tighter than the shared core defaults:
   every header element drops one notch on the type scale and the gaps between
   them are pulled in, so the bar is ≈66px tall instead of ≈88px and the row
   reads as one dense group. This is deliberately a PROJECT-layer deviation —
   the shared core keeps its roomier metrics for other projects.
   The core's rules here are `.cw-nav-links a` (0,1,1) and single-class rules
   (0,1,0); these rules sit later in the cascade so they win on source order,
   and `button.nshat-theme-toggle` repeats the element-qualified selector the
   core reset requires. */

/* bar padding (was 20px / 12px scrolled in the core) */
.cw-nav {
  padding: 14px 0;
}

.cw-nav--scrolled {
  padding: 9px 0;
}

/* wordmark: matches the footer wordmark width so both edges line up (was 172px) */
.cw-nav-logo svg {
  width: 148px;
  height: 37px;
}

/* link rhythm (was gap 36px + 0.9rem + 7px/16px padding) */
.cw-nav-links {
  gap: 22px;
}

.cw-nav-links a {
  font-size: 0.8125rem;
  padding: 6px 11px;
}

/* widgets (was gap 12px in the CTA row) */
.cw-nav-cta {
  gap: 8px;
}

/* The year block needs no compact override: it is defined once, in full, in its
   own subsection above (icon tile, value row, hairline, action row). */

/* 38px → 32px, still a comfortable 32px touch target */
button.nshat-theme-toggle {
  width: 32px;
  height: 32px;
}

.nshat-user {
  gap: 6px;
  padding: 4px 10px 4px 4px;
  font-size: 0.75rem;
}

.nshat-user-avatar {
  width: 22px;
  height: 22px;
  font-size: 0.6875rem;
}

/* ── 3c. HEADER DROPDOWN + USER ACTIONS (project components) ────────────────
   The core ships NO dropdown component (`.cw-nav-dropdown-toggle` only appears
   in its :focus-visible list), so the whole thing is defined here from the same
   tokens as every other surface. The toggle is a real <button> carrying
   aria-expanded / aria-controls; the menu opens on :hover and :focus-within
   (keyboard users can tab straight into it without any script) and the JS
   `--open` class mirrors that for click and touch. */
.cw-nav-dropdown {
  position: relative;
  display: inline-flex;
  align-items: center;
}

/* Two classes on purpose: the core reset `.cw-theme button` is (0,1,1) and would
   otherwise wipe this button's surface, padding and border. */
button.cw-nav-dropdown-toggle,
.cw-nav-links .cw-nav-dropdown-toggle {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 6px 11px;
  border: none;
  border-radius: var(--cw-radius-full);
  background: transparent;
  font-family: inherit;
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--cw-text-2);
  cursor: pointer;
  transition:
    color 0.4s var(--cw-silk),
    background 0.4s var(--cw-silk),
    box-shadow 0.4s var(--cw-silk);
}

/* The core hovers nav links to raw --cw-accent, which measures 2.9:1 as TEXT on
   the page background — an AA failure. The project layer keeps the same tint and
   outline but paints the hover (and the toggle) with the measured accent-text
   pair so every state stays above 4.5:1. */
.cw-nav-links a:hover,
.cw-nav-links .cw-nav-dropdown-toggle:hover,
.cw-nav-dropdown:hover > .cw-nav-dropdown-toggle,
.cw-nav-dropdown:focus-within > .cw-nav-dropdown-toggle,
.cw-nav-dropdown--open > .cw-nav-dropdown-toggle {
  color: var(--nshat-btn-accent-text);
  background: var(--cw-accent-ghost);
  box-shadow: inset 0 0 0 1px var(--cw-accent-border);
}

.cw-nav-dropdown-menu {
  position: absolute;
  inset-block-start: calc(100% + 8px);
  inset-inline-start: 0;
  z-index: 1200;
  min-width: 196px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 0;
  padding: 6px;
  list-style: none;
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius);
  background: var(--cw-surface-2);
  box-shadow: var(--nshat-btn-shadow-hover);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition:
    opacity var(--cw-silk-dur) var(--cw-silk),
    transform var(--cw-silk-dur) var(--cw-silk),
    visibility var(--cw-silk-dur) var(--cw-silk);
}

.cw-nav-dropdown:hover > .cw-nav-dropdown-menu,
.cw-nav-dropdown:focus-within > .cw-nav-dropdown-menu,
.cw-nav-dropdown--open > .cw-nav-dropdown-menu {
  opacity: 1;
  visibility: visible;
  transform: none;
}

/* item radius = container radius − container padding, so the corners nest */
.cw-nav-dropdown-menu a {
  display: block;
  padding: 8px 12px;
  border-radius: calc(var(--cw-radius) - 6px);
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--cw-text-2);
  white-space: nowrap;
  transition:
    color var(--cw-silk-dur) var(--cw-silk),
    background-color var(--cw-silk-dur) var(--cw-silk);
}

.cw-nav-dropdown-menu a:hover,
.cw-nav-dropdown-menu a:focus-visible {
  /* accent TEXT on a surface: the 7.4:1 pair, never raw --cw-accent (2.9:1) */
  color: var(--nshat-btn-accent-text);
  background: var(--cw-accent-ghost);
}

/* Signed-in actions: the logout button with the user chip stacked UNDER it */
.nshat-user-actions {
  display: inline-flex;
  flex-direction: column;
  align-items: stretch;
  gap: 4px;
}

/* the ⚙ and the logout button share one line. The gear is FIRST in the DOM, so in
   RTL it renders to the RIGHT of the logout button — the order in the markup is the
   visual order, no `order:` juggling needed. */
.nshat-user-actions-row {
  display: flex;
  align-items: center;
  gap: 4px;
}

/* Square icon buttons in the bar: the ⚙ settings toggle and the logout symbol.
   Both share one look so the pair reads as a set; the gear keeps the
   element-qualified selector the core's `.cw-theme button` reset requires. */
button.nshat-settings-toggle,
a.nshat-icon-btn {
  width: 32px;
  height: 32px;
  padding: 0;
  justify-content: center;
  border: 1.5px solid var(--nshat-btn-border);
  border-radius: var(--cw-radius-full);
  background: var(--nshat-btn-surface);
  box-shadow: var(--nshat-btn-shadow);
  color: var(--cw-text-1);
  transition:
    color var(--cw-silk-dur) var(--cw-silk),
    background-color var(--cw-silk-dur) var(--cw-silk),
    border-color var(--cw-silk-dur) var(--cw-silk),
    box-shadow var(--cw-silk-dur) var(--cw-silk);
}

a.nshat-icon-btn {
  display: inline-flex;
  align-items: center;
}

button.nshat-settings-toggle:hover,
a.nshat-icon-btn:hover {
  color: var(--nshat-btn-accent-text);
  background: var(--cw-accent-ghost);
  border-color: var(--nshat-btn-border-hover);
  box-shadow: var(--nshat-btn-shadow-hover);
}

/* a panel at the END of the bar must open towards the middle of the page,
   otherwise it would run off the viewport edge */
.cw-nav-dropdown-menu--end {
  inset-inline-start: auto;
  inset-inline-end: 0;
  min-width: 232px;
}

/* label + value rows (username, year) */
.nshat-menu-field {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 7px 12px;
  border-radius: calc(var(--cw-radius) - 6px);
}

.nshat-menu-label {
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--cw-text-2);
  white-space: nowrap;
}

/* the username action row: the name truncates instead of stretching the panel */
.nshat-menu-action-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* actionable rows — link or button, one look. Two classes so it also beats the
   core's `a` styling and its button reset without a specificity war. */
.cw-nav-dropdown-menu .nshat-menu-action {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 8px 12px;
  border: 0;
  border-radius: calc(var(--cw-radius) - 6px);
  background: transparent;
  font-family: inherit;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--cw-text-2);
  text-align: start;
  cursor: pointer;
  transition:
    color var(--cw-silk-dur) var(--cw-silk),
    background-color var(--cw-silk-dur) var(--cw-silk);
}

.cw-nav-dropdown-menu .nshat-menu-action:hover,
.cw-nav-dropdown-menu .nshat-menu-action:focus-visible {
  color: var(--nshat-btn-accent-text);
  background: var(--cw-accent-ghost);
}

.cw-nav-dropdown-menu .nshat-menu-action svg {
  flex: 0 0 auto;
}

/* ── Mobile menu: one consistent rhythm for every item ──────────────────────
   The core styles `.cw-mobile-menu a` as a full-width centred row with a bottom
   border, a first-child top border and a staggered reveal. That only works when
   the links are DIRECT children of the menu — nesting them in <ul><li> dropped
   the top border, collapsed the stagger to a single delay and inherited the list
   indent, so the items looked inconsistent. The widgets (year switcher, auth
   action) live in `.nshat-mm-widgets` and are explicitly reset to button metrics.
   Every rule below uses the `.cw-mobile-menu` prefix so it wins over the core's
   descendant selector (0,1,1) regardless of the element type. */
.cw-mobile-menu {
  align-items: stretch;
  /* top padding follows the compact bar height (was 92px for the roomier bar) */
  padding: 78px 24px 28px;
  /* The sheet is a child of `.cw-nav`, and the bar's SCROLLED state carries
     `backdrop-filter` (core §7a) — and a filter makes its element the containing
     block for every fixed descendant. So `inset: 0` stopped meaning "the viewport"
     and started meaning "the bar's box" the moment the page was scrolled: the sheet
     came up ~54px tall, its content scrolled inside that sliver, and the menu looked
     like a small fragment of itself. Nothing about the sheet's own position changed:
     the bar is pinned to the top of the viewport, so stating the sheet's block size
     here restores the full-screen sheet in BOTH states — and leaves the bar's blur,
     its z-index and the sheet's place inside the bar's stacking context untouched.
     Do not delete it as "redundant with inset: 0": it is the scrolled-state fix. */
  inset-block-end: auto;
  block-size: 100vh;
  block-size: 100dvh;
}

.cw-mobile-menu > a:first-of-type {
  border-top: 1px solid var(--cw-border);
}

/* Delays the core stops at the sixth item; the signed-in menu has nine links */
.cw-mobile-menu > a:nth-child(7) { transition-delay: 0.35s; }
.cw-mobile-menu > a:nth-child(8) { transition-delay: 0.40s; }
.cw-mobile-menu > a:nth-child(9) { transition-delay: 0.45s; }
.cw-mobile-menu > a:nth-child(10) { transition-delay: 0.50s; }

/* ── Mobile menu: the account line ──────────────────────────────────────────
   At ≤768px the core hides `.cw-nav-cta`, and with it the settings menu — the
   one place the signed-in name was written. The sheet opens with that same line
   now, in the order the desktop menu uses (account, then the year, then the
   mode, then sign out). It is a direct child of the menu, so the core's link
   rhythm already gives it the full width, the padding, the hairline and the
   reveal; these rules only turn the centred line into an avatar + name on the
   start side, give the avatar back the roomier size 3b shrank for the bar, and
   let a long name truncate instead of pushing the avatar out of the row. */
.cw-mobile-menu .nshat-mm-account {
  display: flex;
  align-items: center;
  gap: 10px;
  text-align: start;
}

.cw-mobile-menu .nshat-mm-account .nshat-user-avatar {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  font-size: 0.75rem;
}

.cw-mobile-menu .nshat-mm-account-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cw-mobile-menu .nshat-mm-widgets {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 100%;
  margin-top: 18px;
  padding-top: 18px;
  border-top: 1px solid var(--cw-border);
  /* Enter after the links so the whole panel reveals in one rhythm */
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.5s var(--cw-silk) 0.4s, transform 0.5s var(--cw-silk) 0.4s;
}

.cw-mobile-menu--open .nshat-mm-widgets {
  opacity: 1;
  transform: none;
}

.cw-mobile-menu .nshat-mm-widgets > a,
.cw-mobile-menu .nshat-mm-widgets a.cw-btn,
.cw-mobile-menu .nshat-mm-widgets .cw-btn {
  width: 100%;
  padding: 12px 18px;
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1;
  text-align: center;
  border-bottom: none;
  opacity: 1;
  transform: none;
}

/* the same control at full width, with the stronger border the menu needs */
.cw-mobile-menu .nshat-mm-widgets .nshat-year {
  width: 100%;
  padding: 6px;
  border-width: 1.5px;
  border-color: var(--nshat-btn-border);
  background: var(--nshat-btn-surface);
}

.cw-mobile-menu .nshat-mm-widgets .nshat-year-row {
  padding: 4px 6px;
}

/* the value is pushed to the end of the row so the label and the year read as
   separate columns even on a phone */
.cw-mobile-menu .nshat-mm-widgets .nshat-year-value {
  flex: 1 1 auto;
  justify-content: flex-end;
}

.cw-mobile-menu .nshat-mm-widgets .nshat-year-select {
  flex: 0 1 auto;
  min-width: 0;
}

.cw-mobile-menu .nshat-mm-widgets .nshat-year-link {
  padding: 8px 6px;
  font-size: 0.6875rem;
}

@media (max-height: 640px) {
  .cw-mobile-menu {
    padding: 72px 24px 28px;
  }
}

@media (max-width: 480px) {
  .cw-mobile-menu {
    padding: 84px 20px 28px;
  }

  .cw-mobile-menu > a {
    font-size: 1.05rem;
    padding: 12px 0;
  }
}

/* Mobile menu: keep the widget stack readable on small screens */
@media (max-width: 768px) {
  .cw-mobile-menu .nshat-user {
    width: 100%;
    justify-content: space-between;
  }

  .cw-mobile-menu .nshat-theme-toggle {
    width: 100%;
    border-radius: var(--cw-radius);
  }

  .cw-nav-logo svg {
    width: 148px;
    height: 37px;
  }

  .brand-content > .cw-container {
    padding: 0 20px;
  }
}

@media (max-width: 480px) {
  .cw-nav-logo svg {
    width: 132px;
    height: 33px;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   4. FOOTER EXTRAS
   .cw-footer, .cw-footer-grid, .cw-footer-brand, .cw-footer-col-label,
   .cw-footer-links, .cw-footer-socials, .cw-footer-bottom, .cw-footer-copy,
   .cw-footer-legal all come from the core. Only the minimal variant used by
   application pages is added here.
   ═══════════════════════════════════════════════════════════════════════════ */
.cw-footer--minimal {
  padding: 34px 0 26px;
}

/* ── Footer element order mirrors the legacy footer: 1) help links · 2) divider ·
   3) legal links · 4) social icons inside the container, then 5) the closing row
   below the container — wordmark + version on the start side, designer credit +
   rights line on the end side. Only the styling is brand (dark surface, tokens). */
.nshat-footer-links {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  text-align: center;
}

.nshat-footer-links a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 6px 4px;
  font-size: 0.875rem;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.72);
  transition: color var(--cw-silk-dur) var(--cw-silk);
}

.nshat-footer-links a svg {
  flex: 0 0 auto;
  opacity: 0.85;
}

.nshat-footer-links a:hover {
  color: var(--cw-accent-light);
}

.nshat-footer-divider {
  border: none;
  border-top: 1px solid rgba(255, 255, 255, 0.10);
  margin: 20px 0;
}

.nshat-footer-meta {
  display: flex;
  align-items: center;
  /* only the social icons remain here, centred inside the container */
  justify-content: center;
  gap: 16px;
  flex-wrap: wrap;
  padding-top: 4px;
}

/* ── Footer closing row ──────────────────────────────────────────────────────
   One row BELOW the container, as in the legacy footer: the wordmark and the
   version share the START side (right in RTL) and the designer credit — with the
   rights line beneath it — sits on the END side (left in RTL). */
.nshat-footer-bottom-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  max-width: var(--cw-container);
  margin: 16px auto 0;
  padding: 0 32px;
}

/* the wordmark cell on the start side of the row: the version is centred
   BENEATH the wordmark. `--nshat-footer-logo-w` gives the version paragraph the
   exact wordmark width, so centring it reads as centred under the logo without
   duplicating the 148px magic number. */
.cw-footer-brand-group {
  --nshat-footer-logo-w: 148px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
}

.cw-footer-brand-group .cw-nav-logo svg {
  width: var(--nshat-footer-logo-w);
  height: 37px;
}

/* rgba(255,255,255,0.62) on the dark footer surface stays above the 4.5:1 AA
   contrast ratio for the small secondary text */
.cw-footer-version {
  width: var(--nshat-footer-logo-w);
  text-align: center;
  color: rgba(255, 255, 255, 0.62);
  font-size: 0.75rem;
  line-height: 1.4;
  margin: 0;
}

/* the credit + rights cell on the end side of the row: both lines are centred on
   the same axis, so the rights line sits centred under the credit */
.nshat-footer-colophon {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  text-align: center;
}

.cw-footer-rights {
  color: rgba(255, 255, 255, 0.62);
  font-size: 0.8125rem;
  line-height: 1.4;
  margin: 0;
}

.nshat-footer-socials {
  display: flex;
  align-items: center;
  gap: 10px;
  list-style: none;
  padding: 0;
  margin: 0;
}

.nshat-footer-socials a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: var(--cw-radius-full);
  color: rgba(255, 255, 255, 0.78);
  transition: color var(--cw-silk-dur) var(--cw-silk),
    border-color var(--cw-silk-dur) var(--cw-silk),
    background-color var(--cw-silk-dur) var(--cw-silk),
    transform var(--cw-silk-dur) var(--cw-silk);
}

.nshat-footer-socials a:hover {
  color: var(--cw-accent-light);
  border-color: var(--cw-accent-light);
  background: rgba(255, 255, 255, 0.06);
}

.nshat-footer-credit {
  margin: 0;
  font-size: 0.75rem;
  color: rgba(255, 255, 255, 0.62);
}

.nshat-footer-credit a {
  color: rgba(255, 255, 255, 0.72);
  transition: color var(--cw-silk-dur) var(--cw-silk);
}

.nshat-footer-credit a:hover {
  color: var(--cw-accent-light);
}

@media (max-width: 768px) {
  .nshat-footer-meta {
    flex-direction: column;
    align-items: center;
    text-align: center;
  }

  .nshat-footer-socials {
    justify-content: center;
  }

  /* the closing row stacks and centres, and the credit stays centred under it */
  .nshat-footer-bottom-row {
    flex-direction: column;
    align-items: center;
    gap: 12px;
    padding: 0 20px;
  }

  .cw-footer-brand-group {
    align-items: center;
    text-align: center;
  }

  .nshat-footer-colophon {
    align-items: center;
    text-align: center;
  }
}

@media (max-width: 420px) {
  .nshat-footer-links {
    grid-template-columns: 1fr;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   5. DJANGO MESSAGES
   Every message object is rendered once in the base shell as a .cw-alert.
   ═══════════════════════════════════════════════════════════════════════════ */
.brand-messages {
  max-width: var(--cw-container);
  margin: 0 auto 20px;
  padding: 0 32px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.brand-messages .cw-alert {
  margin: 0;
  animation: brandMessageIn 0.45s var(--cw-silk) both;
}

@keyframes brandMessageIn {
  from {
    opacity: 0;
    transform: translateY(-8px);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

@media (max-width: 768px) {
  .brand-messages {
    padding: 0 20px;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   6. DATA SURFACES
   The core already styles every table wrapper (.table-wrap, .brand-table-wrap,
   .sbd-table-wrap, .matrix-table-wrap, .rec-table-wrap, .absent-table-wrap,
   .student-table-wrap) for horizontal scrolling and custom scrollbars.
   The layout behaviour below is the project addition: a bounded, rounded and
   drag-friendly surface with a scroll hint.
   ═══════════════════════════════════════════════════════════════════════════ */
.brand-table-wrap {
  overflow: auto;
  -webkit-overflow-scrolling: touch;
  max-height: 70vh;
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius);
  background: var(--cw-surface-2);
}

.brand-table-wrap > table {
  width: 100%;
  border-collapse: collapse;
  margin: 0;
}

.brand-table-wrap > table thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--cw-btn-primary-bg);
  color: #ffffff;
  font-size: 0.8125rem;
  font-weight: 600;
  white-space: nowrap;
  padding: 12px 14px;
  text-align: start;
}

.brand-table-wrap > table thead th:first-child {
  border-start-start-radius: var(--cw-radius);
}

.brand-table-wrap > table thead th:last-child {
  border-start-end-radius: var(--cw-radius);
}

.brand-table-wrap > table tbody td {
  padding: 11px 14px;
  font-size: 0.875rem;
  color: var(--cw-text-2);
  border-top: 1px solid var(--cw-border);
}

.brand-table-wrap > table tbody tr:hover td {
  background: var(--cw-accent-ghost);
}

.brand-scroll-hint {
  display: none;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--cw-accent);
}

.brand-scroll-hint svg {
  animation: brandHintPulse 1.8s var(--cw-silk) infinite;
}

@keyframes brandHintPulse {
  0%,
  100% {
    opacity: 0.45;
    transform: translateX(0);
  }

  50% {
    opacity: 1;
    transform: translateX(-4px);
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   7. FORM HELPERS
   Fields, labels, hints and errors come from the core (.cw-label, .cw-input,
   .cw-select, .cw-textarea, .cw-checkbox, .cw-radio, .cw-switch, .cw-hint,
   .cw-error-text). Only the grid used by Django forms is added.
   ═══════════════════════════════════════════════════════════════════════════ */
.brand-form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 18px;
}

.brand-form-grid--full {
  grid-column: 1 / -1;
}

.brand-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.brand-form-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--cw-border);
}

.brand-form-actions--start {
  justify-content: flex-start;
}

@media (max-width: 480px) {
  .brand-form-actions {
    flex-direction: column-reverse;
    align-items: stretch;
  }

  .brand-form-actions .cw-btn {
    width: 100%;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   8. PWA SHELL, MOBILE COMPENSATION AND PRINT
   The previous project generation hid content under a fixed top bar and a
   fixed bottom navigation on phones. The compensation below is applied once,
   at shell level, so page templates never need their own padding hacks.
   ═══════════════════════════════════════════════════════════════════════════ */
.brand-pwa-topbar {
  display: none;
}

.brand-pwa-bottomnav {
  display: none;
}

.brand-pwa-overlay {
  display: none;
}

@media (max-width: 768px) {
  .brand-pwa-topbar {
    display: flex;
    position: fixed;
    inset-block-start: 0;
    inset-inline: 0;
    height: 56px;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 0 16px;
    background: rgba(236, 244, 246, 0.92);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    border-bottom: 1px solid var(--cw-border);
    z-index: 1000;
  }

  html[data-theme="dark"] .brand-pwa-topbar {
    background: rgba(15, 25, 28, 0.94);
  }

  .brand-pwa-bottomnav {
    display: flex;
    position: fixed;
    inset-block-end: 0;
    inset-inline: 0;
    height: 72px;
    align-items: center;
    justify-content: space-around;
    gap: 6px;
    background: rgba(236, 244, 246, 0.96);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    border-top: 1px solid var(--cw-border);
    z-index: 1000;
  }

  html[data-theme="dark"] .brand-pwa-bottomnav {
    background: rgba(15, 25, 28, 0.96);
  }

  .brand-pwa-tab {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    flex: 1 1 0;
    height: 100%;
    font-size: 0.6875rem;
    font-weight: 600;
    color: var(--cw-text-2);
    transition: color var(--cw-silk-dur) var(--cw-silk);
  }

  .brand-pwa-tab:hover,
  .brand-pwa-tab--active {
    color: var(--cw-accent);
  }

  .brand-main {
    padding-top: 56px;
  }

  .brand-content {
    padding-top: 18px;
    padding-bottom: 84px;
  }
}

@media all and (display-mode: standalone) {
  .brand-main {
    padding-top: calc(56px + constant(safe-area-inset-top, 0px));
    padding-top: calc(56px + env(safe-area-inset-top, 0px));
  }

  .brand-pwa-bottomnav {
    height: calc(72px + constant(safe-area-inset-bottom, 0px));
    height: calc(72px + env(safe-area-inset-bottom, 0px));
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
}

@media print {
  .cw-nav,
  .cw-footer,
  .brand-pwa-topbar,
  .brand-pwa-bottomnav,
  .brand-messages,
  .brand-scroll-hint {
    display: none !important;
  }

  body {
    background: #ffffff !important;
    color: #000000 !important;
  }

  .brand-main {
    padding-top: 0 !important;
  }

  .brand-content {
    padding: 0 !important;
  }

  .brand-section-card,
  .brand-table-wrap,
  .cw-card {
    background: #ffffff !important;
    border-color: #cccccc !important;
    box-shadow: none !important;
  }

  .brand-section-title,
  .brand-section-title em,
  .cw-section-title,
  .cw-section-title em {
    color: #000000 !important;
  }

  .brand-table-wrap {
    max-height: none !important;
    overflow: visible !important;
  }

  a[href]::after {
    content: " (" attr(href) ")";
    font-size: 0.75em;
    color: #444444;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   9. ACCESSIBILITY GUARANTEES FOR THE PROJECT LAYER
   The core already ships a global :focus-visible system (section 32) and a
   global prefers-reduced-motion block (section 33). The two rules below make
   sure the project widgets and page animations are covered as well.
   ═══════════════════════════════════════════════════════════════════════════ */
.nshat-year-select:focus-visible,
.nshat-theme-toggle:focus-visible,
.nshat-user:focus-visible,
.brand-breadcrumb a:focus-visible {
  outline: 2px solid var(--cw-accent);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .brand-messages .cw-alert,
  .brand-scroll-hint svg,
  .nshat-theme-toggle svg {
    animation: none !important;
    transition: none !important;
    transform: none !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   10. BUTTON CLARITY LAYER
   Owner feedback: most button states were hard to see. Reason found in the
   core: .cw-btn-secondary, .cw-btn-danger and .cw-btn-icon are declared only
   inside @media (prefers-contrast: more), so outside that mode they fall back
   to the borderless base button. This layer gives every variant a visible
   outline plus a soft lift in both themes, using accent-derived alpha values
   only — no new hues. Behaviour (rest → hover fills) stays exactly as approved.
   ═══════════════════════════════════════════════════════════════════════════ */
:root {
  /* Separation values. The page background is #ecf4f6, so a white button sits on a
     near-identical tone: it needs a strong accent outline plus a neutral shadow to
     read as a control. Owner review pass 2 (too close to the page background). */
  --nshat-btn-surface:      #ffffff;
  --nshat-btn-border:       rgba(59, 148, 168, 0.62);
  --nshat-btn-border-hover: rgba(59, 148, 168, 0.95);
  /* Text colour used on flat buttons and their hover tint. --cw-accent (#3b94a8)
     only reaches 2.9:1 on white, so flat buttons use the button ink instead:
     7.4:1 on white and 7.4:1 on the hover tint. It is also the required colour
     for accent-coloured TEXT on any light surface (e.g. the year-copy link). */
  --nshat-btn-accent-text:  #1a5a6a;
  /* Two-layer, wider shadows: a tight contact shadow plus a soft spread */
  --nshat-btn-shadow:       0 1px 2px rgba(13, 26, 30, 0.07), 0 4px 14px rgba(13, 26, 30, 0.13);
  --nshat-btn-shadow-hover: 0 2px 4px rgba(13, 26, 30, 0.08), 0 10px 28px rgba(13, 26, 30, 0.20);
  --nshat-danger-shadow:       0 1px 2px rgba(183, 28, 28, 0.08), 0 4px 14px rgba(183, 28, 28, 0.16);
  --nshat-danger-shadow-hover: 0 2px 4px rgba(183, 28, 28, 0.10), 0 10px 28px rgba(183, 28, 28, 0.30);
}

html[data-theme="dark"] {
  --nshat-btn-surface:      #1A2C2E;
  --nshat-btn-border:       rgba(91, 192, 190, 0.58);
  --nshat-btn-border-hover: rgba(91, 192, 190, 0.92);
  /* accent TEXT on the dark surface — flat buttons and the year-copy link */
  --nshat-btn-accent-text:  #5BC0BE;
  --nshat-btn-shadow:       0 1px 2px rgba(0, 0, 0, 0.40), 0 4px 16px rgba(0, 0, 0, 0.48);
  --nshat-btn-shadow-hover: 0 2px 4px rgba(0, 0, 0, 0.45), 0 12px 32px rgba(0, 0, 0, 0.58);
  --nshat-danger-shadow:       0 1px 2px rgba(0, 0, 0, 0.40), 0 4px 16px rgba(0, 0, 0, 0.48);
  --nshat-danger-shadow-hover: 0 2px 4px rgba(0, 0, 0, 0.45), 0 12px 32px rgba(239, 83, 80, 0.40);
}

/* 10a. Base — every button carries an outline and a lift so it reads as a control.

   SPECIFICITY NOTE (why doubled classes are used from here on):
   the core reset `.cw-theme button { border: none; background: none; }` scores
   (0,1,1) and therefore BEATS any single-class button rule (0,1,0). With plain
   `.cw-btn-*` selectors every `<button>` element lost its background and border —
   this was the "conflicting styles" that made the buttons look undesigned.
   `.cw-btn.cw-btn-x` scores (0,2,0) and wins, and the dark-mode rules repeat the
   core's own dark specificity so they are not outranked either. */
.cw-btn.cw-btn {
  border: 1.5px solid transparent;
  box-shadow: var(--nshat-btn-shadow);
}

.cw-btn.cw-btn--loading {
  border-color: var(--nshat-btn-border);
}

/* 10b. Primary — solid deep teal in every mode.
   Rest = --cw-btn-primary-bg (#1a5a6a) with white text (7.7:1); hover lightens to
   --cw-btn-primary-mid-bg (#226a7a, 6.2:1), so the interaction reads as
   "lighter on hover".

   NOTE ON SPECIFICITY: the core carries
   `html[data-theme="dark"] .cw-btn-primary { … ghost … }` which resolves to
   (0,2,1) and therefore BEATS a plain `.cw-btn-primary` (0,1,0) no matter the
   file order. The dark block below repeats the same selector so the solid look
   genuinely applies in dark mode as well. */
.cw-btn.cw-btn-primary {
  color: #ffffff;
  background: var(--cw-btn-primary-bg);
  border: 1.5px solid var(--cw-btn-primary-bg);
  box-shadow: 0 1px 2px rgba(26, 90, 106, 0.18), 0 5px 18px rgba(26, 90, 106, 0.32);
}

.cw-btn.cw-btn-primary:hover {
  color: #ffffff;
  background: var(--cw-btn-primary-mid-bg);
  border-color: var(--cw-btn-primary-mid-bg);
  box-shadow: 0 2px 4px rgba(26, 90, 106, 0.22), 0 12px 32px rgba(26, 90, 106, 0.45);
}

html[data-theme="dark"] .cw-btn.cw-btn-primary {
  color: #ffffff;
  background: var(--cw-btn-primary-bg);
  border-color: var(--cw-btn-primary-bg);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.45), 0 6px 20px rgba(26, 90, 106, 0.45);
}

html[data-theme="dark"] .cw-btn.cw-btn-primary:hover {
  color: #ffffff;
  background: var(--cw-btn-primary-mid-bg);
  border-color: var(--cw-btn-primary-mid-bg);
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5), 0 14px 34px rgba(34, 106, 122, 0.55);
}

/* 10c. Secondary — full definition (otherwise missing outside high contrast) */
.cw-btn.cw-btn-secondary {
  color: var(--cw-text-1);
  background: var(--nshat-btn-surface);
  border: 1.5px solid var(--nshat-btn-border);
  font-weight: 600;
}

.cw-btn.cw-btn-secondary:hover {
  color: var(--nshat-btn-accent-text);
  background: var(--cw-accent-ghost);
  border-color: var(--nshat-btn-border-hover);
  box-shadow: var(--nshat-btn-shadow-hover);
}

/* 10d. Outline — readable at rest, strong on hover.
   Text uses --nshat-btn-accent-text, not --cw-accent, because #3b94a8 on white
   is only 2.9:1 and would fail WCAG AA. */
.cw-btn.cw-btn-outline {
  color: var(--nshat-btn-accent-text);
  background: var(--nshat-btn-surface);
  border: 1.5px solid var(--nshat-btn-border);
  font-weight: 600;
}

.cw-btn.cw-btn-outline:hover {
  color: var(--nshat-btn-accent-text);
  border-color: var(--cw-accent);
  background: var(--cw-accent-ghost);
  box-shadow: var(--nshat-btn-shadow-hover);
}

/* 10e. Ghost — a visible quiet surface instead of an invisible label */
.cw-btn.cw-btn-ghost {
  color: var(--cw-text-1);
  background: var(--nshat-btn-surface);
  border: 1.5px solid var(--nshat-btn-border);
  font-weight: 500;
}

.cw-btn.cw-btn-ghost:hover {
  color: var(--nshat-btn-accent-text);
  background: var(--cw-accent-ghost);
  border-color: var(--nshat-btn-border-hover);
  box-shadow: var(--nshat-btn-shadow-hover);
}

/* 10f. Danger — full definition (otherwise missing outside high contrast) */
.cw-btn.cw-btn-danger {
  color: var(--cw-btn-danger-text);
  background: var(--cw-btn-danger-bg);
  border: 1.5px solid var(--cw-btn-danger-border);
  box-shadow: var(--nshat-danger-shadow);
}

.cw-btn.cw-btn-danger:hover {
  color: #ffffff;
  background: var(--cw-btn-danger-hover-bg);
  border-color: var(--cw-btn-danger-hover-bg);
  box-shadow: var(--nshat-danger-shadow-hover);
}

/* 10g. Icon button — square target with the same outline language */
.cw-btn.cw-btn-icon {
  width: 44px;
  height: 44px;
  padding: 0;
  border-radius: var(--cw-radius-full);
  color: var(--cw-text-1);
  background: var(--nshat-btn-surface);
  border: 1.5px solid var(--nshat-btn-border);
}

.cw-btn.cw-btn-icon:hover {
  color: var(--nshat-btn-accent-text);
  background: var(--cw-accent-ghost);
  border-color: var(--nshat-btn-border-hover);
  box-shadow: var(--nshat-btn-shadow-hover);
}

.cw-btn.cw-btn-icon.cw-btn--sm {
  width: 36px;
  height: 36px;
  padding: 0;
}

.cw-btn.cw-btn-icon.cw-btn--lg {
  width: 52px;
  height: 52px;
  padding: 0;
}

/* 10h. Disabled — clearly muted but still readable as a control.
   Text uses --cw-text-2 (7.4:1 on --cw-surface) rather than --cw-text-3 so the
   label stays legible while the control still reads as inactive. */
.cw-btn.cw-btn:disabled,
.cw-btn.cw-btn[disabled] {
  opacity: 1;
  background: var(--cw-surface);
  color: var(--cw-text-2);
  border-color: var(--cw-border);
  box-shadow: none;
  cursor: not-allowed;
}

/* The core also ships a dark-mode disabled rule for the primary button; keep the
   project's readable treatment there too (same selector shape, later in the file). */
html[data-theme="dark"] .cw-btn.cw-btn:disabled,
html[data-theme="dark"] .cw-btn.cw-btn[disabled] {
  background: #1A2C2E;
  color: var(--cw-text-2);
  border-color: #2A4042;
}

/* 10i. High-contrast users get the stronger outline the core intended */
@media (prefers-contrast: more) {
  .cw-btn.cw-btn,
  .cw-btn.cw-btn-primary,
  .cw-btn.cw-btn-secondary,
  .cw-btn.cw-btn-outline,
  .cw-btn.cw-btn-ghost,
  .cw-btn.cw-btn-danger,
  .cw-btn.cw-btn-icon {
    border-width: 2px;
    border-color: var(--nshat-btn-border-hover);
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   11. MODAL CONTRACT — hidden by default, visible only while open
   The core declares `.cw-modal-backdrop { … display: flex; … }`, i.e. the
   backdrop is visible the moment it is in the DOM and the `--open` modifier
   only re-asserts the same value — so a modal shipped this way is open on page
   load and can never be closed. This block restores the expected contract:
   markup adds `hidden` + `aria-hidden="true"` and JS toggles the `--open` class.
   ═══════════════════════════════════════════════════════════════════════════ */
.cw-modal-backdrop {
  display: none;
}

.cw-modal-backdrop.cw-modal-backdrop--open {
  display: flex;
}

.cw-modal-backdrop[hidden] {
  display: none !important;
}

.cw-modal-backdrop--open .cw-modal {
  animation: cwScaleIn 0.2s var(--cw-silk) both;
}

/* Keyboard users must be able to leave the dialog: the close control is always visible */
.cw-modal .cw-modal-close {
  min-width: 36px;
  min-height: 36px;
  border-radius: var(--cw-radius-full);
  color: var(--cw-text-2);
  transition: color var(--cw-silk-dur) var(--cw-silk),
    background-color var(--cw-silk-dur) var(--cw-silk);
}

.cw-modal .cw-modal-close:hover {
  color: var(--cw-error);
  background: var(--cw-error-bg);
}

.cw-modal .cw-modal-close:focus-visible {
  outline: 2px solid var(--cw-accent);
  outline-offset: 2px;
}

/* ═══════════════════════════════════════════════════════════════════════════
   12. LANDING TILES AND CHIPS (reusable navigation surfaces)
   Used by the account/portal landing screens (لوحة التحكم …): a tile is a large
   link card carrying an accent icon tile, a title, a description and a forward
   affordance. Everything is drawn from --cw-* tokens, so both themes work with
   no duplicated literal. Accent TEXT uses --nshat-btn-accent-text (7.4:1),
   because raw --cw-accent measures 2.9:1 on a light surface (an AA failure).
   The tiles are surfaces themselves, so a dashboard grid is NEVER nested inside
   a .brand-section-card — that would put white cards on a white card.
   ═══════════════════════════════════════════════════════════════════════════ */
.nshat-tile-grid {
  display: grid;
  /* min() keeps a column from overflowing a 320px viewport */
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 268px), 1fr));
  gap: 18px;
}

.nshat-tile {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 26px 24px;
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius-lg);
  background: var(--cw-surface-2);
  box-shadow: var(--cw-shadow-sm);
  transition:
    transform 0.4s var(--cw-silk),
    border-color 0.4s var(--cw-silk),
    box-shadow 0.4s var(--cw-silk);
}

.nshat-tile:hover,
.nshat-tile:focus-visible {
  transform: translateY(-4px);
  border-color: var(--cw-accent-border);
  box-shadow: var(--cw-shadow-md);
}

.nshat-tile:focus-visible {
  outline: 2px solid var(--cw-accent);
  outline-offset: 3px;
}

/* The icon tile: a quiet accent surface at rest, solid primary on hover, so the
   state change is legible without the colour ever leaving the palette. */
.nshat-tile-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 52px;
  border-radius: var(--cw-radius);
  background: var(--cw-accent-ghost);
  border: 1px solid var(--cw-accent-border);
  color: var(--nshat-btn-accent-text);
  transition:
    background-color 0.4s var(--cw-silk),
    border-color 0.4s var(--cw-silk),
    color 0.4s var(--cw-silk),
    transform 0.4s var(--cw-silk);
}

.nshat-tile-icon svg {
  display: block;
}

.nshat-tile:hover .nshat-tile-icon,
.nshat-tile:focus-visible .nshat-tile-icon {
  background: var(--cw-btn-primary-bg);   /* #1a5a6a with a white glyph = 7.7:1 */
  border-color: var(--cw-btn-primary-bg);
  color: #ffffff;
  transform: scale(1.04);
}

.nshat-tile-body {
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: 1 1 auto;
}

.nshat-tile-title {
  margin: 0;
  font-size: 1.0625rem;
  font-weight: 700;
  line-height: 1.4;
  color: var(--cw-text-1);
}

.nshat-tile-text {
  margin: 0;
  font-size: 0.875rem;
  line-height: 1.8;
  color: var(--cw-text-2);
}

/* The foot pins to the bottom of the card, so tiles of different text lengths
   still close on one baseline. */
.nshat-tile-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: auto;
  padding-top: 12px;
  border-top: 1px solid var(--cw-border);
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--nshat-btn-accent-text);
}

.nshat-tile-arrow {
  display: inline-flex;
  flex: 0 0 auto;
  transition: transform 0.4s var(--cw-silk);
}

/* RTL: forward is the END side, i.e. to the LEFT */
.nshat-tile:hover .nshat-tile-arrow,
.nshat-tile:focus-visible .nshat-tile-arrow {
  transform: translateX(-4px);
}

/* Chip — a small labelled fact (school name, status). Accent text again, so the
   label clears AA on its own tint instead of falling back to raw --cw-accent. */
.nshat-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin-top: 14px;
  padding: 5px 12px;
  border: 1px solid var(--cw-accent-border);
  border-radius: var(--cw-radius-full);
  background: var(--cw-accent-ghost);
  color: var(--nshat-btn-accent-text);
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.6;
}

.nshat-chip svg {
  flex: 0 0 auto;
}

.nshat-chip-label {
  color: var(--cw-text-2);
}

/* Note line under a tile grid: a quiet, bordered aside with an info icon. */
.nshat-note {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 20px 0 0;
  padding: 14px 16px;
  border: 1px dashed var(--cw-border);
  border-radius: var(--cw-radius);
  background: var(--cw-bg-alt);
  color: var(--cw-text-2);
  font-size: 0.8125rem;
  line-height: 1.8;
}

.nshat-note svg {
  flex: 0 0 auto;
  margin-top: 3px;
  color: var(--nshat-btn-accent-text);
}

@media (max-width: 480px) {
  .nshat-tile {
    padding: 22px 20px;
  }

  .nshat-tile-icon {
    width: 46px;
    height: 46px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .nshat-tile,
  .nshat-tile-icon,
  .nshat-tile-arrow {
    transition: none !important;
    transform: none !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   13. ACCOUNT SUMMARY SURFACES
   Additions used by the account screens (بيانات الحساب · تعديل بيانات الحساب):
   the subsection title, the subscription status row, the stacked check group,
   the media grid and the split action row. Nothing here redefines a core
   component, and every colour — semantic states included — is a --cw-* token,
   so both themes work with no duplicated literal.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Title of one block inside a .brand-section-card (pairs with .brand-subsection,
   which already draws the hairline between blocks). */
.brand-subsection-title {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-size: 1.0625rem;
  font-weight: 700;
  line-height: 1.4;
  color: var(--cw-text-1);
}

/* ── Subscription / account state row ────────────────────────────────────────
   icon tile · title · text · state pill, with the action on the END side. The
   state is carried by the tile's fill and border, so the surface itself stays
   on the identity palette instead of inventing a coloured card. */
.nshat-status {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  flex-wrap: wrap;
  padding: 20px 22px;
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius-lg);
  background: var(--cw-surface-2);
  box-shadow: var(--cw-shadow-sm);
}

.nshat-status + .brand-section-card,
.brand-section-card + .nshat-status {
  margin-top: 20px;
}

.nshat-status-main {
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
}

.nshat-status-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 46px;
  height: 46px;
  border-radius: var(--cw-radius);
  border: 1px solid var(--cw-accent-border);
  background: var(--cw-accent-ghost);
  color: var(--nshat-btn-accent-text);
}

.nshat-status-icon svg {
  display: block;
}

.nshat-status-body {
  min-width: 0;
}

.nshat-status-title {
  margin: 0;
  font-size: 1.0625rem;
  font-weight: 700;
  line-height: 1.4;
  color: var(--cw-text-1);
}

.nshat-status-text {
  margin: 2px 0 0;
  font-size: 0.875rem;
  line-height: 1.7;
  color: var(--cw-text-2);
}

.nshat-status-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

/* state variants: a semantic token fill plus its own token border */
.nshat-status--ok .nshat-status-icon {
  background: var(--cw-success-bg);
  border-color: var(--cw-success);
}

.nshat-status--pending .nshat-status-icon {
  background: var(--cw-info-bg);
  border-color: var(--cw-info);
}

.nshat-status--free .nshat-status-icon {
  background: var(--cw-surface);
  border-color: var(--cw-border);
}

/* small state / eligibility pill. Text uses --cw-text-1 so it clears AA on every
   semantic fill in both themes (the fills are pastel by design). */
.nshat-status-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 9px;
  padding: 3px 11px;
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius-full);
  background: var(--cw-surface);
  color: var(--cw-text-1);
  font-size: 0.6875rem;
  font-weight: 700;
  line-height: 1.7;
}

.nshat-status-pill + .nshat-status-pill {
  margin-inline-start: 6px;
}

.nshat-status-pill svg {
  flex: 0 0 auto;
}

.nshat-status--ok .nshat-status-pill {
  background: var(--cw-success-bg);
  border-color: var(--cw-success);
}

.nshat-status--pending .nshat-status-pill {
  background: var(--cw-info-bg);
  border-color: var(--cw-info);
}

@media (max-width: 640px) {
  .nshat-status {
    padding: 18px;
  }

  .nshat-status-main {
    align-items: flex-start;
  }

  /* the action drops to its own full-width line instead of squeezing the text */
  .nshat-status-actions {
    width: 100%;
  }

  .nshat-status-actions .cw-btn {
    flex: 1 1 auto;
    justify-content: center;
  }
}

/* A stacked group of checkboxes / radios inside one .brand-field */
.nshat-check-group {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-top: 2px;
}

/* ── Media grid (school photo / logo, with captions) ───────────────────────── */
.nshat-media-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
  gap: 18px;
  margin-top: 14px;
}

.nshat-media-item {
  display: flex;
  flex-direction: column;
  gap: 8px;
  /* <figure> ships a UA margin of `1em 40px`, which would break the grid */
  margin: 0;
}

.nshat-media-title {
  margin: 0;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--cw-text-2);
}

/* ── Split action row: a secondary action on the START side and the primary
   buttons on the END side (edit screens: «تغيير كلمة المرور» + حفظ/إلغاء). ── */
.brand-form-actions--split {
  justify-content: space-between;
}

@media (max-width: 480px) {
  .brand-form-actions--split {
    align-items: stretch;
  }

  .brand-form-actions--split .brand-actions {
    flex-direction: column-reverse;
    width: 100%;
  }

  .brand-form-actions--split .brand-actions .cw-btn {
    width: 100%;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   14. HUB SURFACES (grades & classes)
   Used by the sections hub (الصفوف والفصول): a counter row, a toolbar, a grade
   block, a class card, state badges, a compact link row and a tools panel.
   Every state colour comes from the semantic tokens (·-bg fill + · border) with
   --cw-text-1 / --cw-text-2 labels, so both themes clear AA and no new literal
   colour is introduced. Reused later by the class workspace and the madrasati
   screens, so the hub can never drift from those pages.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Counter row: chips carrying a number ──────────────────────────────────── */
.nshat-chip-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 16px;
}

.nshat-chip-row .nshat-chip {
  margin-top: 0;
}

.nshat-chip-row .nshat-chip b {
  color: var(--cw-text-1);
}

/* ── Toolbar: a search field and its actions ───────────────────────────────── */
.nshat-toolbar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 18px;
}

.nshat-toolbar .cw-input {
  flex: 1 1 240px;
  max-width: 380px;
}

/* ── Grade block ───────────────────────────────────────────────────────────── */
.nshat-grade {
  padding: 22px;
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius-lg);
  background: var(--cw-surface-2);
  box-shadow: var(--cw-shadow-sm);
}

.nshat-grade + .nshat-grade {
  margin-top: 18px;
}

/* the grade the URL focused (?sf=…) gets a ring, never a coloured card */
.nshat-grade--focus {
  border-color: var(--cw-accent-border);
  box-shadow: 0 0 0 3px var(--cw-accent-ghost), var(--cw-shadow-sm);
}

.nshat-grade-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

.nshat-grade-name {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0;
  font-size: 1.0625rem;
  font-weight: 700;
  line-height: 1.4;
  color: var(--cw-text-1);
}

.nshat-grade-name svg {
  flex: 0 0 auto;
  color: var(--nshat-btn-accent-text);
}

.nshat-grade-stage {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--cw-text-2);
}

.nshat-grade-tools {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.nshat-class-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
  gap: 14px;
  margin-top: 18px;
}

/* ── Class card: a nested surface inside the grade block ───────────────────── */
.nshat-class {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 16px;
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius);
  background: var(--cw-bg-alt);
}

.nshat-class-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
}

.nshat-class-title {
  font-size: 0.9375rem;
  font-weight: 700;
  line-height: 1.5;
  color: var(--cw-text-1);
  transition: color var(--cw-silk-dur) var(--cw-silk);
}

a.nshat-class-title:hover,
a.nshat-class-title:focus-visible {
  color: var(--nshat-btn-accent-text);
  text-decoration: underline;
}

.nshat-class-sub {
  font-size: 0.75rem;
  line-height: 1.6;
  color: var(--cw-text-2);
}

.nshat-class-stats {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 14px;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 0.75rem;
  line-height: 1.8;
  color: var(--cw-text-2);
}

.nshat-class-stats b {
  color: var(--cw-text-1);
}

.nshat-class-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: auto;
  padding-top: 12px;
  border-top: 1px dashed var(--cw-border);
}

/* ── State badge (source · plan state · counters) ──────────────────────────── */
.nshat-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 9px;
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius-full);
  background: var(--cw-surface);
  color: var(--cw-text-1);
  font-size: 0.6875rem;
  font-weight: 700;
  line-height: 1.7;
  white-space: nowrap;
}

.nshat-badge svg {
  flex: 0 0 auto;
}

.nshat-badge--platform {
  background: var(--cw-info-bg);
  border-color: var(--cw-info);
}

.nshat-badge--manual {
  background: var(--cw-warning-bg);
  border-color: var(--cw-warning);
}

.nshat-badge--empty {
  background: var(--cw-surface);
  border-color: var(--cw-border);
  color: var(--cw-text-2);
}

.nshat-badge--ok {
  background: var(--cw-success-bg);
  border-color: var(--cw-success);
}

.nshat-badge--warn {
  background: var(--cw-warning-bg);
  border-color: var(--cw-warning);
}

/* ── Compact link row (workspace tabs, plan, taklifat) ─────────────────────── */
.nshat-links {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.nshat-links a {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 10px;
  border: 1px solid var(--nshat-btn-border);
  border-radius: var(--cw-radius-full);
  background: var(--nshat-btn-surface);
  color: var(--nshat-btn-accent-text);
  font-size: 0.6875rem;
  font-weight: 700;
  line-height: 1.8;
  transition:
    background-color var(--cw-silk-dur) var(--cw-silk),
    border-color var(--cw-silk-dur) var(--cw-silk),
    box-shadow var(--cw-silk-dur) var(--cw-silk);
}

.nshat-links a:hover,
.nshat-links a:focus-visible {
  color: var(--nshat-btn-accent-text);
  background: var(--cw-accent-ghost);
  border-color: var(--nshat-btn-border-hover);
  box-shadow: var(--nshat-btn-shadow);
}

.nshat-links svg {
  flex: 0 0 auto;
}

/* ── Tools disclosure + panel ──────────────────────────────────────────────── */
.nshat-tools > summary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  list-style: none;
  cursor: pointer;
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--nshat-btn-accent-text);
}

.nshat-tools > summary::-webkit-details-marker {
  display: none;
}

.nshat-tools > summary:hover,
.nshat-tools > summary:focus-visible {
  text-decoration: underline;
}

.nshat-panel {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 10px;
  padding: 12px;
  border: 1px dashed var(--cw-border);
  border-radius: var(--cw-radius);
  background: var(--cw-surface-2);
}

.nshat-panel form {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  flex-wrap: wrap;
}

.nshat-panel .brand-field {
  flex: 1 1 180px;
}

.nshat-panel .cw-hint {
  flex: 1 1 100%;
  margin: 0;
}

@media (max-width: 480px) {
  .nshat-grade {
    padding: 18px;
  }

  .nshat-grade-tools .cw-btn {
    flex: 1 1 auto;
    justify-content: center;
  }

  .nshat-panel form {
    flex: 1 1 100%;
  }

  .nshat-panel .brand-field {
    flex: 1 1 100%;
  }

  .nshat-panel form .cw-btn {
    width: 100%;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   15. WORKSPACE TABS, TABLES AND CELL STATES
   Additions used by the class workspace (مساحة عمل الفصل): a tab bar, the
   body-row rules the core leaves to `td`, the timetable cell states and the
   narrow-field helper. Colours are tokens only (the manual cell uses
   --cw-warning, the converted cell --cw-surface); nothing here redefines a core
   component.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Tab bar ───────────────────────────────────────────────────────────────── */
.nshat-tabs {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin: 20px 0 4px;
}

.nshat-tabs a {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 8px 15px;
  border: 1px solid var(--nshat-btn-border);
  border-radius: var(--cw-radius-full);
  background: var(--nshat-btn-surface);
  color: var(--cw-text-2);
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1.6;
  transition:
    color var(--cw-silk-dur) var(--cw-silk),
    background-color var(--cw-silk-dur) var(--cw-silk),
    border-color var(--cw-silk-dur) var(--cw-silk),
    box-shadow var(--cw-silk-dur) var(--cw-silk);
}

.nshat-tabs a svg {
  flex: 0 0 auto;
}

.nshat-tabs a:hover,
.nshat-tabs a:focus-visible {
  color: var(--nshat-btn-accent-text);
  background: var(--cw-accent-ghost);
  border-color: var(--nshat-btn-border-hover);
}

/* the active tab is the solid primary pair — the same 7.7:1 label as .cw-btn-primary */
.nshat-tabs a.is-active {
  color: #ffffff;
  background: var(--cw-btn-primary-bg);
  border-color: var(--cw-btn-primary-bg);
  box-shadow: 0 1px 2px rgba(26, 90, 106, 0.18), 0 5px 18px rgba(26, 90, 106, 0.30);
}

.nshat-tabs a.is-active:hover,
.nshat-tabs a.is-active:focus-visible {
  color: #ffffff;
  background: var(--cw-btn-primary-mid-bg);
  border-color: var(--cw-btn-primary-mid-bg);
}

/* ── Table body rows the core leaves unstyled ────────────────────────────────
   .brand-table-wrap styles thead th and tbody td; a row label is a <th> inside
   tbody (the timetable's day column) and is completed here. */
.brand-table-wrap > table tbody th {
  padding: 11px 14px;
  font-size: 0.8125rem;
  font-weight: 700;
  text-align: start;
  color: var(--cw-text-1);
  background: var(--cw-surface);
  border-top: 1px solid var(--cw-border);
  white-space: nowrap;
}

.brand-table-wrap > table tbody tr:hover th {
  background: var(--cw-accent-ghost);
}

/* a cell that must not grow with its content (numbers, codes, counters) */
.brand-table-wrap > table td.is-narrow,
.brand-table-wrap > table th.is-narrow {
  width: 1%;
  white-space: nowrap;
}

/* cell content that stacks a control over its caption */
.nshat-cell {
  vertical-align: top;
}

.nshat-cell .cw-select,
.nshat-cell .cw-input {
  font-size: 0.75rem;
  padding: 4px 8px;
}

.nshat-cell-note {
  margin-top: 4px;
  font-size: 0.6875rem;
  line-height: 1.6;
  color: var(--cw-text-2);
}

/* converted to a study plan: the cell is locked, so it reads as a quieter surface */
.nshat-cell--converted {
  background: var(--cw-surface);
}

/* edited by hand: the import never replaces it — flagged on the START side */
.nshat-cell--manual {
  border-inline-start: 3px solid var(--cw-warning);
}

/* ── A narrow field inside an inline panel (numbers, weekly counts) ────────── */
.nshat-field-narrow {
  flex: 0 0 auto;
}

.nshat-field-narrow .cw-input,
.nshat-field-narrow .cw-select {
  max-width: 150px;
}

/* Inside a card the panel needs a different surface to read as a well */
.brand-section-card .nshat-panel {
  background: var(--cw-bg-alt);
}

/* ── Copy-tools row: two independent forms that must not run together ──────── */
.nshat-panel--stacked {
  flex-direction: column;
  align-items: stretch;
}

.nshat-panel--stacked > form {
  align-items: flex-end;
}

@media (max-width: 640px) {
  .brand-table-wrap {
    /* the timetable is genuinely wide; let it use the whole viewport width */
    max-height: 78vh;
  }

  .nshat-tabs {
    gap: 6px;
  }

  .nshat-tabs a {
    padding: 7px 12px;
    font-size: 0.75rem;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   16. RESTRAINED POLISH (data screens)
   Owner feedback: the quieter arrangement was preferred, so the decorative
   additions of the first polish pass (KPI cards, a sticky tab bar, icon-tiled
   headings) were removed. What stays here is craft that cannot change the
   character of a page: a hairline that closes every page head, a readable
   measure for supporting text, a stronger row-number column, zebra rows that
   never outrank a state cue, and a timetable that keeps its day column and its
   period header visible while the grid scrolls. Tokens only — no new colour.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Every page head closes with a hairline, so the content below reads as its
   own block — one rule, applied wherever .brand-page-head is used ─────────── */
.brand-page-head {
  padding-bottom: 18px;
  border-bottom: 1px solid var(--cw-border);
  margin-bottom: 24px;
}

/* ── A readable measure for supporting text (long lines are the most common
   amateur signal in a data screen) ────────────────────────────────────────── */
.brand-section-sub {
  max-width: 74ch;
}

/* ── Colour discipline in a counter row: the accent tint is reserved for things
   you can act on (tabs, buttons, links), so the counters keep a neutral surface
   and only their value carries ink — the accent chip stays for a labelled fact
   such as the school name in a page head ──────────────────────────────────── */
.nshat-chip-row .nshat-chip {
  background: var(--cw-surface-2);
  border-color: var(--cw-border);
  color: var(--cw-text-2);
}

.nshat-chip-row .nshat-chip b {
  color: var(--cw-text-1);
  font-weight: 800;
}

/* the separators between the counters read as hairline dividers, not dots */
.nshat-chip-row .nshat-chip > span {
  color: var(--cw-text-3);
}

/* ── The row-number column is a locator, not data: quieter and centred ────── */
.brand-table-wrap > table tbody td.is-narrow:first-child {
  color: var(--cw-text-3);
  font-weight: 600;
}

/* ── In-page anchors land below the fixed bar instead of under it ─────────── */
:target {
  scroll-margin-top: 84px;
}

/* ═══════════════════════════════════════════════════════════════════════════
   17. ORDER AND PAYMENT SURFACES
   Used by the subscription screens (تأكيد الاشتراك · الدفع): a determined step
   marker, a machine-readable value (IBAN, invoice or payment id), a scan/QR
   frame, and the one-shot entrance for a confirmation page. The entrance is CSS
   so no script writes inline styles, and it honours prefers-reduced-motion.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── The numbered marker of a guided block (bank transfer: 1 · 2 · 3) ─────── */
.nshat-step {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border-radius: var(--cw-radius-full);
  background: var(--cw-btn-primary-bg);
  color: #ffffff;
  font-size: 0.75rem;
  font-weight: 800;
  line-height: 1;
}

/* ── A value a person copies or reads character by character ─────────────── */
.nshat-code {
  display: inline-block;
  margin-top: 4px;
  padding: 6px 10px;
  border: 1px solid var(--cw-border);
  border-radius: calc(var(--cw-radius) - 8px);
  background: var(--cw-surface);
  color: var(--cw-text-1);
  font-size: 0.875rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  direction: ltr;
  word-break: break-all;
}

/* ── A scan / receipt image inside a card ────────────────────────────────── */
.nshat-scan {
  display: block;
  width: 100%;
  max-width: 260px;
  margin: 0 auto;
  padding: 10px;
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius);
  background: var(--cw-surface-2);
}

/* ── One-shot entrance for a page that confirms something ────────────────── */
.nshat-reveal {
  animation: nshatReveal 0.45s var(--cw-silk) both;
}

@keyframes nshatReveal {
  from {
    opacity: 0;
    transform: translateY(12px);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .nshat-reveal {
    animation: none !important;
    transform: none !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   18. APP SURFACES (compact, mobile-first screens)
   Owner feedback on the partner screen: «التصميم بدائي · العناصر متباعدة ·
   لا يوجد لمسة احترافية — اعد التصميم كتطبيق جوال». So this section ships the
   mobile-app layout vocabulary: a tight vertical rhythm, compact cards, an app
   header, a hero value block, a one-row figure strip, a bottom action bar with
   the home-indicator inset, and a bottom-sheet form. Everything is a --cw-*
   token; the translucent bar reuses the surface pattern the identity already
   ships for its own bars, and the sheet keeps the §11 modal contract.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── The screen column: one tight rhythm instead of stacked card margins ──── */
.nshat-app {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-width: 720px;
  margin: 0 auto;
}

/* ── App header: title on the start side, one icon action on the end side ─── */
.nshat-app-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  /* the header may now carry a labelled primary action beside its icons: on a
     narrow phone the action block drops below the title instead of overflowing */
  flex-wrap: wrap;
  margin-bottom: 4px;
}

.nshat-app-title {
  margin: 0;
  font-size: clamp(1.25rem, 4vw, 1.5rem);
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.25;
  color: var(--cw-text-1);
}

.nshat-app-sub {
  margin: 2px 0 0;
  font-size: 0.8125rem;
  line-height: 1.6;
  color: var(--cw-text-2);
}

/* ── Compact card (16px padding, one radius step below the section card) ──── */
.nshat-app-card {
  padding: 16px;
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius);
  background: var(--cw-surface-2);
  box-shadow: var(--cw-shadow-sm);
}

.nshat-app-card-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin: 0 0 12px;
  font-size: 0.875rem;
  font-weight: 700;
  line-height: 1.4;
  color: var(--cw-text-2);
}

/* ── Hero value: the code the partner shares, with its own actions ────────── */
.nshat-code-hero {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding: 16px;
  border: 1px solid var(--cw-accent-border);
  border-radius: var(--cw-radius);
  background: var(--cw-accent-ghost);
}

.nshat-code-hero-value {
  font-size: 1.75rem;
  font-weight: 800;
  letter-spacing: 0.16em;
  line-height: 1.1;
  direction: ltr;
  color: var(--nshat-btn-accent-text);
}

.nshat-code-hero-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

/* the copy control reports its own state instead of a transient toast */
.nshat-copy.is-done {
  color: var(--nshat-btn-accent-text);
  border-color: var(--cw-success);
  background: var(--cw-success-bg);
}

/* ── One-row figure strip (three short numbers on a phone) ────────────────── */
.nshat-stat-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
}

.nshat-stat {
  padding: 12px 8px;
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius);
  background: var(--cw-bg-alt);
  text-align: center;
}

.nshat-stat-value {
  font-size: 1.25rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.2;
  color: var(--cw-text-1);
}

.nshat-stat-label {
  margin-top: 3px;
  font-size: 0.6875rem;
  font-weight: 600;
  line-height: 1.5;
  color: var(--cw-text-2);
}

/* ── Compact fact rows (label / value on one line, no wide gap) ───────────── */
.nshat-rows {
  display: flex;
  flex-direction: column;
  margin: 0;
}

.nshat-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 9px 0;
  border-bottom: 1px solid var(--cw-border);
}

.nshat-row:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

.nshat-row-label {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--cw-text-2);
}

.nshat-row-value {
  margin: 0;
  font-size: 0.875rem;
  font-weight: 700;
  text-align: end;
  color: var(--cw-text-1);
}

/* ── Bottom action bar: the app's primary action, thumb-reachable ─────────── */
.nshat-appbar {
  display: flex;
  align-items: center;
  gap: 10px;
}

.nshat-appbar .cw-btn {
  flex: 1 1 auto;
  justify-content: center;
}

@media (max-width: 768px) {
  .nshat-appbar {
    position: sticky;
    inset-block-end: 0;
    z-index: 800;
    margin-inline: -20px;
    padding: 10px 20px calc(10px + env(safe-area-inset-bottom, 0px));
    background: rgba(236, 244, 246, 0.94);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    border-top: 1px solid var(--cw-border);
  }

  html[data-theme="dark"] .nshat-appbar {
    background: rgba(15, 25, 28, 0.94);
  }
}

/* ── Bottom sheet: the app form pattern, on the §11 modal contract ────────── */
@media (max-width: 640px) {
  .cw-modal-backdrop.cw-modal-backdrop--open {
    align-items: flex-end;
  }

  .cw-modal--sheet {
    width: 100%;
    max-width: none;
    margin: 0;
    padding: 20px 18px calc(20px + env(safe-area-inset-bottom, 0px));
    border-start-start-radius: var(--cw-radius-lg);
    border-start-end-radius: var(--cw-radius-lg);
    border-end-start-radius: 0;
    border-end-end-radius: 0;
    animation: nshatSheetUp 0.28s var(--cw-silk) both;
  }
}

@keyframes nshatSheetUp {
  from {
    opacity: 0.7;
    transform: translateY(22px);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cw-modal--sheet {
    animation: none !important;
    transform: none !important;
  }
}

@media (max-width: 360px) {
  .nshat-stat-value {
    font-size: 1.0625rem;
  }

  .nshat-code-hero-value {
    font-size: 1.5rem;
  }
}

/* ── The off-screen field the clipboard fallback uses ──────────────────────
   document.execCommand('copy') needs a real, selectable field, and nothing
   written by a script may carry an inline style — so the field is parked by a
   class instead of a `style` attribute. */
.nshat-offscreen {
  position: fixed;
  inset-block-start: -1000px;
  inset-inline-start: -1000px;
  opacity: 0;
  pointer-events: none;
}

/* The hero label is a block (a paragraph), so its own margin is reset here —
   the row label is also used as a flex item where margins would shift the row. */
.nshat-row-label {
  margin: 0;
}

/* A status row inside an app column takes the app rhythm: 16px and one radius
   step down from the document variant it keeps on report pages. */
.nshat-app .nshat-status {
  padding: 16px;
  border-radius: var(--cw-radius);
}

/* The app header can carry more than one small action */
.nshat-head-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* A value that may be long (an email, a platform name) wraps inside its row
   instead of pushing the label away */
.nshat-row-value {
  word-break: break-word;
}

/* A row that is itself a link gets a real touch target and a press state */
.nshat-row--action {
  margin-inline: -12px;
  padding: 12px;
  border-radius: calc(var(--cw-radius) - 6px);
  transition: background-color var(--cw-silk-dur) var(--cw-silk);
}

.nshat-row--action:hover,
.nshat-row--action:focus-visible {
  background: var(--cw-accent-ghost);
}

/* the media grid carries its own spacing, so it needs none inside a card */
.nshat-app .nshat-media-grid {
  margin-top: 0;
}

/* ── List rows with a two-line main cell (a student, a class, a document) ───
   `.nshat-row` is the label/value pair; `.nshat-row-main` is the variant for a
   row whose start cell is a title over a meta line and whose end cell is an
   action — the shape a phone list needs most often. */
.nshat-row-main {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1 1 auto;
  min-width: 0;
}

.nshat-item-title {
  font-size: 0.875rem;
  font-weight: 700;
  line-height: 1.5;
  color: var(--cw-text-1);
  word-break: break-word;
}

.nshat-item-meta {
  font-size: 0.75rem;
  line-height: 1.5;
  color: var(--cw-text-2);
}

.nshat-row .cw-btn-icon {
  flex: 0 0 auto;
}

/* ── Row action cluster: a row's end cell may hold more than one icon button
   (edit timing + delete). A plain flex row keeps the label at the row's start
   and the two actions together at its end; without it, `.nshat-row`'s own
   `justify-content: space-between` would scatter three children across the row. */
.nshat-row-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
}

/* ── A compact filter row: one wide control plus its actions ─────────────── */
.nshat-filter {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.nshat-filter .cw-select {
  flex: 1 1 220px;
  min-width: 0;
}

/* ═══════════════════════════════════════════════════════════════════════════
   20. APP SEARCH + PROFILE CARDS
   Owner feedback: «لا زال سيئ — اعرض بطاقات جميلة». So the list became cards,
   and the search stayed. A card reads as one person: the name in bold, the role as
   a pill, the specialty and the stage beneath it, a state badge, a chevron in the
   corner, and the counters as a footer of five cells divided by hairlines — the
   shape the legacy card was reaching for, rebuilt from tokens so it holds in both
   themes. The tinted band and the round avatar of the first version were removed
   at the owner's request (batch 8a), so the identity block opens the card itself.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Search field with a leading icon ────────────────────────────────────── */
.nshat-search {
  position: relative;
}

.nshat-search .cw-input {
  padding-inline-start: 42px;
  border-radius: var(--cw-radius-full);
  background: var(--cw-surface);
}

.nshat-search-icon {
  position: absolute;
  inset-inline-start: 15px;
  inset-block-start: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  pointer-events: none;
  color: var(--cw-text-2);
}

/* the line a search shows when nothing matched */
.nshat-search-empty {
  padding: 26px 16px;
  text-align: center;
  font-size: 0.8125rem;
  line-height: 1.7;
  color: var(--cw-text-2);
}

.nshat-search-empty[hidden] {
  display: none !important;
}

/* ── The card grid: one column on a phone, two or three from 560px up ────── */
.nshat-card-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
}

@media (min-width: 560px) {
  .nshat-card-grid {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
    gap: 14px;
  }
}

/* ── The person card ─────────────────────────────────────────────────────── */
.nshat-card-person {
  position: relative;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius-lg);
  background: var(--cw-surface-2);
  box-shadow: var(--cw-shadow-sm);
  overflow: hidden;
  transition:
    transform 0.4s var(--cw-silk),
    border-color 0.4s var(--cw-silk),
    box-shadow 0.4s var(--cw-silk);
}

.nshat-card-person:hover,
.nshat-card-person:focus-visible {
  transform: translateY(-3px);
  border-color: var(--cw-accent-border);
  box-shadow: var(--cw-shadow-md);
}

.nshat-card-person:focus-visible {
  outline: 2px solid var(--cw-accent);
  outline-offset: 2px;
}

/* a filtered-out card must really disappear (the flex display above outranks
   the browser's own [hidden] rule) */
.nshat-card-person[hidden] {
  display: none !important;
}

/* The head now opens the card and carries its own top padding: the tinted band
   and the 54px round avatar that used to sit on its edge are gone (owner:
   «ازل nshat-avatar»), so there is no negative margin left and no reserved avatar
   column — the rules for both were deleted, not left as dead CSS. */
.nshat-card-head {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 16px 16px 14px;
}

/* the identity block stops short of the corner chevron, so a long name wraps
   instead of sliding under the arrow */
.nshat-card-identity {
  min-width: 0;
  padding-inline-end: 24px;
}

.nshat-card-name {
  display: block;
  font-size: 1rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  line-height: 1.4;
  color: var(--cw-text-1);
  word-break: break-word;
}

/* the role as a pill, then the specialty as a quiet line */
.nshat-card-role {
  display: inline-flex;
  align-items: center;
  margin-top: 7px;
  padding: 2px 10px;
  border: 1px solid var(--cw-accent-border);
  border-radius: var(--cw-radius-full);
  background: var(--cw-accent-ghost);
  color: var(--nshat-btn-accent-text);
  font-size: 0.6875rem;
  font-weight: 700;
  line-height: 1.9;
}

.nshat-card-spec {
  display: block;
  margin-top: 6px;
  font-size: 0.75rem;
  line-height: 1.5;
  color: var(--cw-text-2);
}

.nshat-card-arrow {
  position: absolute;
  inset-block-start: 12px;
  inset-inline-end: 14px;
  display: inline-flex;
  color: var(--cw-text-3);
  transition: color var(--cw-silk-dur) var(--cw-silk);
}

.nshat-card-person:hover .nshat-card-arrow,
.nshat-card-person:focus-visible .nshat-card-arrow {
  color: var(--nshat-btn-accent-text);
}

/* ── The footer of the card: five equal cells divided by hairlines ───────── */
.nshat-card-stats {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  margin-top: auto;
  border-top: 1px solid var(--cw-border);
  background: var(--cw-bg-alt);
}

.nshat-card-stat {
  padding: 10px 3px;
  text-align: center;
  border-inline-start: 1px solid var(--cw-border);
}

.nshat-card-stat:first-child {
  border-inline-start: none;
}

.nshat-card-stat-value {
  display: block;
  font-size: 1rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.25;
  color: var(--cw-text-1);
}

.nshat-card-stat-label {
  display: block;
  margin-top: 2px;
  font-size: 0.5625rem;
  font-weight: 700;
  line-height: 1.4;
  color: var(--cw-text-2);
}

@media (max-width: 360px) {
  .nshat-card-stat-label {
    font-size: 0.5rem;
  }
}

/* A state badge inside the identity block of a person card (the teacher list
   says at a glance who carries programs and who does not). It must open its own
   line even when the card prints no specialty or stage line above it — the role
   pill is inline-level, so an inline badge would sit beside it — and it stops at
   its own content width instead of stretching the card. */
.nshat-card-identity .nshat-badge {
  display: flex;
  width: fit-content;
  margin-top: 8px;
}


/* ── Table refinements: zebra that never outranks a state cue ─────────────── */
.brand-table-wrap > table tbody tr:nth-child(even) > td,
.brand-table-wrap > table tbody tr:nth-child(even) > th {
  background: var(--cw-bg-alt);
}

/* the converted cell is locked, so its tint is re-asserted above the zebra */
.brand-table-wrap > table tbody td.nshat-cell--converted {
  background: var(--cw-surface);
}

.brand-table-wrap > table tbody tr:hover > td,
.brand-table-wrap > table tbody tr:hover > th {
  background: var(--cw-accent-ghost);
}

.brand-table-wrap > table thead th {
  letter-spacing: 0.01em;
}

.brand-table-wrap > table td.is-narrow,
.brand-table-wrap > table th.is-narrow {
  text-align: center;
}

/* ── Timetable: freeze the day column and the period header ──────────────── */
.nshat-timetable > thead th:first-child {
  position: sticky;
  inset-inline-start: 0;
  z-index: 3;                   /* above the head row (2) and the day column (1) */
}

.nshat-timetable > tbody th {
  position: sticky;
  inset-inline-start: 0;
  z-index: 1;
  background: var(--cw-surface);
  /* a hairline on the END side separates the frozen column from the grid */
  border-inline-end: 1px solid var(--cw-border);
}

.nshat-timetable > tbody tr:nth-child(even) > th {
  background: var(--cw-bg-alt);
}

.nshat-timetable > tbody tr:hover > th {
  background: var(--cw-accent-ghost);
}

/* the label column of the grid is already named in the thead, so the day cell
   only needs to stay compact */
.nshat-timetable > tbody th,
.nshat-timetable > thead th:first-child {
  min-width: 96px;
}

/* ── Legend for the grid states (the cue, drawn instead of described) ────── */
.nshat-legend {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
  margin-top: 12px;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--cw-text-2);
}

.nshat-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}

.nshat-legend-swatch {
  width: 18px;
  height: 18px;
  border-radius: 5px;
  border: 1px solid var(--cw-border);
  background: var(--cw-surface);
}

.nshat-legend-swatch--manual {
  background: var(--nshat-btn-surface);
  border-inline-start: 3px solid var(--cw-warning);
}

/* ── Reduced motion: the polish adds no motion of its own, and the bar's blur
   is a surface, not an animation — nothing to disable here ───────────────── */

/* ═══════════════════════════════════════════════════════════════════════════
   21. LANDING (public pages)
   The public landing page (contact:home) needs four shapes the identity does not
   ship yet: a hero split (the copy beside a preview of the product), a feature
   grid whose cards carry a list, a framed player for the explainer video, and a
   channel row for the contact facts. Every value here is a --cw-* token — no new
   hue, no literal colour — and every component collapses to a single column on a
   phone. The feature cards and the channel rows are nested surfaces, so they use
   --cw-bg-alt to read as wells inside a .brand-section-card instead of white
   cards on a white card (the §12 rule, applied to this screen).
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── The hero split: copy on the start side, preview on the end side ──────── */
.nshat-hero {
  display: grid;
  grid-template-columns: 1fr;
  gap: 26px;
  align-items: center;
}

@media (min-width: 900px) {
  .nshat-hero {
    grid-template-columns: 1.05fr 1fr;
    gap: 44px;
  }
}

.nshat-hero-copy {
  min-width: 0;
}

.nshat-hero-title {
  margin: 0 0 12px;
}

.nshat-hero-sub {
  margin: 0;
  max-width: 60ch;
}

.nshat-hero-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 22px;
}

@media (max-width: 420px) {
  .nshat-hero-actions .cw-btn {
    width: 100%;
  }
}

/* the preview column hosts the core's own dashboard mockup */
.nshat-hero-preview {
  display: flex;
  justify-content: center;
  min-width: 0;
}

.nshat-hero-preview .cw-hero-dashboard {
  max-width: 470px;
}

/* one preview fact: a label, then a state badge. The badge becomes a block so the
   three cells of the preview line up on one grid (an inline-level pill would sit
   on the text baseline instead), and it keeps the badge's own centred label. */
.nshat-hero-preview .cw-db-stat .cw-badge {
  display: flex;
  justify-content: center;
  margin-top: 8px;
}

/* The mock chart inside the preview needs bar heights, and the page writes no
   style attribute: the core leaves .cw-db-bar height-less, so the profile is
   declared here once. Purely decorative — the markup marks it aria-hidden. */
.nshat-hero-preview .cw-db-chart .cw-db-bar:nth-child(1) { height: 42%; }
.nshat-hero-preview .cw-db-chart .cw-db-bar:nth-child(2) { height: 64%; }
.nshat-hero-preview .cw-db-chart .cw-db-bar:nth-child(3) { height: 36%; }
.nshat-hero-preview .cw-db-chart .cw-db-bar:nth-child(4) { height: 76%; }
.nshat-hero-preview .cw-db-chart .cw-db-bar:nth-child(5) { height: 50%; }
.nshat-hero-preview .cw-db-chart .cw-db-bar:nth-child(6) { height: 88%; }
.nshat-hero-preview .cw-db-chart .cw-db-bar:nth-child(7) { height: 46%; }
.nshat-hero-preview .cw-db-chart .cw-db-bar:nth-child(8) { height: 68%; }

/* ── A wider centred measure for a landing intro ───────────────────────────
   The core's .cw-section-sub is 520px wide, which is right beside a paragraph
   but too narrow for a centred section intro; §16 already fixed the same problem
   for body copy with a 74ch measure, so the intro borrows it, centred. */
.nshat-lead {
  max-width: 74ch;
  margin: 14px auto 0;
  text-align: center;
}

/* ── Feature grid: cards that carry a list of what the platform does ─────── */
.nshat-feature-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr));
  gap: 16px;
}

.nshat-feature {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 22px 20px;
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius);
  background: var(--cw-bg-alt);
  transition:
    transform 0.4s var(--cw-silk),
    border-color 0.4s var(--cw-silk),
    box-shadow 0.4s var(--cw-silk);
}

.nshat-feature:hover {
  transform: translateY(-2px);
  border-color: var(--cw-accent-border);
  box-shadow: var(--cw-shadow-sm);
}

.nshat-feature-head {
  display: flex;
  align-items: center;
  gap: 12px;
}

.nshat-feature-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 42px;
  height: 42px;
  border-radius: var(--cw-radius);
  background: var(--cw-accent-ghost);
  border: 1px solid var(--cw-accent-border);
  color: var(--nshat-btn-accent-text);
}

.nshat-feature-icon svg {
  display: block;
}

.nshat-feature-title {
  margin: 0;
  font-size: 1rem;
  font-weight: 700;
  line-height: 1.45;
  color: var(--cw-text-1);
}

@media (max-width: 640px) {
  .nshat-feature .brand-check-list li {
    font-size: 0.8125rem;
    line-height: 1.8;
  }
}

/* ── The framed player for the explainer video ──────────────────────────── */
.nshat-media-frame {
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius);
  background: var(--cw-bg-alt);
  box-shadow: var(--cw-shadow-sm);
  overflow: hidden;
}

.nshat-media-frame video {
  display: block;
  width: 100%;
  height: auto;
}

/* ── Contact channels: a full-row link with an icon tile and its value ──── */
.nshat-channel-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.nshat-channel {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 16px;
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius);
  background: var(--cw-bg-alt);
  transition:
    transform 0.4s var(--cw-silk),
    border-color 0.4s var(--cw-silk),
    background-color 0.4s var(--cw-silk);
}

a.nshat-channel:hover,
a.nshat-channel:focus-visible {
  transform: translateY(-2px);
  border-color: var(--cw-accent-border);
  background: var(--cw-accent-ghost);
}

a.nshat-channel:focus-visible {
  outline: 2px solid var(--cw-accent);
  outline-offset: 2px;
}

.nshat-channel-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 42px;
  height: 42px;
  border-radius: var(--cw-radius);
  background: var(--cw-accent-ghost);
  border: 1px solid var(--cw-accent-border);
  color: var(--nshat-btn-accent-text);
}

.nshat-channel-icon svg {
  display: block;
}

.nshat-channel-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.nshat-channel-label {
  font-size: 0.9375rem;
  font-weight: 700;
  color: var(--cw-text-1);
}

/* A phone number or an address keeps its own bidi order inside the RTL line and
   wraps instead of stretching the row — no direction override, exactly as the
   identity treats a code value. */
.nshat-channel-value {
  font-size: 0.8125rem;
  line-height: 1.7;
  color: var(--cw-text-2);
  word-break: break-word;
}

@media (max-width: 480px) {
  .nshat-feature {
    padding: 20px 18px;
  }

  .nshat-channel {
    padding: 14px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .nshat-feature,
  .nshat-channel {
    transition: none !important;
    transform: none !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   22. HUB COLOUR (the plan hub — home:home)
   The hub is the one screen whose whole job is to invite a choice, so it carries
   the identity's colour on purpose — and only from the accent family, in the
   weights the layer already documents: --cw-accent-ghost for the icon tile and
   --cw-accent-border for its outline (both §12), plus the accent rule this
   section adds. The semantic tokens (success · warning · error · info) stay
   reserved for state, so no colour on this page can be misread as a status.
   The «جديد» badge this section first shipped was removed at the owner's request
   (batch 9a), so the colour layer is now one rule with one job.
   ═══════════════════════════════════════════════════════════════════════════ */

/* An accent rule that draws itself across the top of a tile on hover/focus — the
   colour cue the legacy page drew with a gradient, which the palette does not
   have. It grows from the centre, because transform-origin has no logical
   keyword: centring is the one origin that is identical in RTL and LTR. */
.nshat-tile--accent {
  position: relative;
  overflow: hidden;   /* clips the rule to the tile's own rounded corners */
}

.nshat-tile--accent::before {
  content: '';
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  height: 3px;
  background: var(--cw-accent);
  transform: scaleX(0);
  transform-origin: center;
  transition: transform 0.45s var(--cw-silk);
}

.nshat-tile--accent:hover::before,
.nshat-tile--accent:focus-visible::before {
  transform: scaleX(1);
}

@media (prefers-reduced-motion: reduce) {
  .nshat-tile--accent::before {
    transition: none !important;
  }
}

/* ── The hub heading: one line, always ──────────────────────────────────────
   Owner feedback: «مرحباً بك في سجلات النشاط لا يمكن أن تكون على سطرين في
   الشاشات الصغيرة». A leading sentence of that length cannot hold one line at the
   identity's own floor (`.cw-section-title` = clamp(2rem, 4vw, 3rem) ⇒ 32px at
   320px, where the sentence needs ≈374px of the 256px the shell leaves inside its
   32px gutters). So the FLOOR moves and the ceiling does not: 1.15rem · 5.2vw ·
   3rem. Measured against Tajawal (≈11.7em for the sentence): 320px ⇒ 18.4px ⇒
   ≈215px ✔, 375px ⇒ 19.5px ⇒ ≈228px ✔, 414px ⇒ 21.5px ⇒ ≈252px ✔, and from
   ≈1100px up the ceiling gives back the identity's full 3rem — so the deviation
   is spent exactly where it is needed and nowhere else. No `white-space: nowrap`,
   so a freakishly narrow viewport wraps instead of overflowing. */
.nshat-hub-title {
  font-size: clamp(1.15rem, 5.2vw, 3rem);
}

/* ── A choice button that previews a file design (the plan print wizard) ────
   The label belongs UNDER the preview, so the button becomes a column: .cw-btn
   already gives it the surface, the outline and the hover lift, and this rule
   only changes the axis. The preview itself is the identity's own frame
   (.nshat-media-frame) holding an inline SVG, so the wizard needs no image file. */
.nshat-choice {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px;
}

/* A form grid inside a dialog needs air under its step title, which carries none
   of its own (a flex row with margin: 0). One rule, every dialog that uses the
   grid after a title — the plan wizard's five steps included. */
.cw-modal .brand-form-grid {
  margin-top: 14px;
}

/* ═══════════════════════════════════════════════════════════════════════════
   23. READ-ONLY TEXT BLOCKS (the plan's own words)
   The three text screens of the plan (بداية الخطة · الأهداف · SWOT) print what the
   user wrote, and that text arrives carrying the user's own line breaks: a list
   typed line by line must not be folded into one paragraph. The identity ships no
   prose component, so this adds exactly two things and nothing else — the line
   breaks are preserved, and the measure stays readable (§16's 74ch, with the line
   height Arabic needs). Colours: --cw-text-2 (7.4:1 on the page surface).
   ═══════════════════════════════════════════════════════════════════════════ */
.nshat-text {
  margin: 10px 0 0;
  max-width: 74ch;
  font-size: 0.9375rem;
  line-height: 1.9;
  color: var(--cw-text-2);
  /* collapses spaces but KEEPS the line breaks the writer typed */
  white-space: pre-line;
}

/* ═══════════════════════════════════════════════════════════════════════════
   24. VISUAL WEEK BOARD (the timeline — home:time)
   Two rounds of owner feedback shaped this section. First: «يحتوي على الكثير من
   النصوص مما يسبب تشتت عند القراءة — اعد تصميمه بشكل مختلف وادرج صور البرامج
   معه» — so the screen was cut as a BOARD instead of a document: one band per week
   holding its activities, the numbers as pills, and the explanatory paragraphs
   gone. Then: «صور البرامج تكون مصغرة على شكل ايقونات وليست كبيرة» — so the
   picture became a THUMBNAIL inside a one-line row instead of a card-leading 4:3
   image: it identifies the activity, it does not show it, and the name, the phase
   and the counts are what the eye scans. What text remains is what neither the
   picture nor the number could carry.
   Colour: the accent family only (--cw-accent-ghost · --cw-accent-border ·
   --nshat-btn-accent-text), the same decision §22 took for the hub — the semantic
   tokens stay reserved for state, so nothing here can be misread as a status.
   Motion: --cw-silk, with a reduced-motion guard at the end.
   RTL: logical properties only (inset-inline-*, inline-size, border-block-start),
   so the board mirrors without one direction-specific rule.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── The control strip: the page's whole toolbar in ONE row ───────────────── */
.nshat-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 16px;
  margin-top: 16px;
  padding: 10px 14px;
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius-lg);
  background: var(--cw-surface-2);
  box-shadow: var(--cw-shadow-sm);
}

.nshat-strip-group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}

/* A label that is scanned, not read: tiny and out of the way. */
.nshat-strip-label {
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--cw-text-3);
}

.nshat-strip-divider {
  inline-size: 1px;
  align-self: stretch;
  background: var(--cw-border);
}

@media (max-width: 719px) {
  .nshat-strip-divider {
    display: none;
  }
}

/* A disclosure standing between two blocks needs its own air: §14 ships none,
   because inside a card it always has the card's padding around it. */
.nshat-tools--spaced {
  margin-top: 12px;
}

/* ── One band per week ───────────────────────────────────────────────────── */
.nshat-board {
  margin-top: 16px;
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius-lg);
  background: var(--cw-surface-2);
  box-shadow: var(--cw-shadow-sm);
  overflow: hidden;   /* keeps the head band's tint inside the rounded corners */
}

.nshat-board-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 12px;
  padding: 12px 16px;
  border-block-end: 1px solid var(--cw-border);
  background: var(--cw-accent-ghost);
}

.nshat-board-name {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-size: 0.9375rem;
  font-weight: 700;
  color: var(--cw-text-1);
}

/* The two date systems, on one line, in the quiet weight — no `nowrap`, so a
   320px screen wraps the item instead of overflowing it. */
.nshat-board-meta {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--cw-text-2);
}

/* The head's tools row now holds the week's counter alone, pushed to the inline
   end of the head's own line — the add-activity control moved into the body so
   that its trigger can never be pushed around by an open panel. */
.nshat-board-tools {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-inline-start: auto;
}

.nshat-board-body {
  padding: 14px 16px 16px;
}

/* ── The list of activities: one compact row each ─────────────────────────── */
.nshat-activity-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Two rows per line only on a wide screen, because the row itself is wide now
   (thumbnail · name · counts · the hessah control). */
@media (min-width: 1024px) {
  .nshat-activity-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* The <li> is the grid cell and the row fills it, so two rows on one line end
   level whatever their names weigh. */
.nshat-activity-grid > li {
  display: flex;
}

.nshat-activity-grid > li > * {
  flex: 1 1 auto;
  min-inline-size: 0;
}

/* ── `hidden` must win ──────────────────────────────────────────────────────
   The phase filter hides elements with the `hidden` attribute, and a class that
   declares `display: flex` OUTRANKS the browser's own `[hidden]` rule — which is
   exactly what made this filter look dead on the first conversion of the page.
   The state is forced here, once, for the two things the page filters. */
.nshat-activity-grid > li[hidden],
.nshat-board[hidden],
.note-item[hidden] {
  display: none !important;
}

.nshat-activity {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius);
  background: var(--cw-surface-2);
  transition:
    border-color 0.4s var(--cw-silk),
    box-shadow 0.4s var(--cw-silk);
}

/* No lift here: a row that jumps under the pointer is noise in a list. The
   outline and a quiet shadow are enough to say "this row is live". */
.nshat-activity:hover,
.nshat-activity:focus-within {
  border-color: var(--cw-accent-border);
  box-shadow: var(--cw-shadow-sm);
}

/* The picture, small on purpose (44px): an icon that identifies the activity.
   The radius is tightened by 4px so a square this small still reads as an icon
   and not as a shrunken card. */
.nshat-thumb {
  position: relative;
  flex: 0 0 auto;
  inline-size: 44px;
  block-size: 44px;
  overflow: hidden;
  border: 1px solid var(--cw-accent-border);
  border-radius: calc(var(--cw-radius) - 4px);
  background: var(--cw-accent-ghost);
}

.nshat-thumb img {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

/* No picture on file: the icon keeps the row's height, so no row is taller than
   its neighbour and nothing looks broken. */
.nshat-thumb-empty {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: var(--cw-accent);
}

.nshat-activity-body {
  display: flex;
  flex: 1 1 160px;
  flex-direction: column;
  gap: 6px;
  min-inline-size: 0;
}

.nshat-activity-title {
  margin: 0;
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1.6;
  color: var(--cw-text-1);
  /* ONE line: a long program name is truncated so the row keeps the height of a
     row — the list stays scannable, which is the point of this layout. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

a.nshat-activity-title {
  transition: color var(--cw-silk-dur) var(--cw-silk);
}

a.nshat-activity-title:hover,
a.nshat-activity-title:focus-visible {
  color: var(--cw-accent);
}

/* ── Pills: the numbers, with an icon instead of a word ───────────────────── */
.nshat-pill-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.nshat-pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 9px;
  border: 1px solid var(--cw-accent-border);
  border-radius: var(--cw-radius-full);
  background: var(--cw-accent-ghost);
  color: var(--nshat-btn-accent-text);
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.8;
  white-space: nowrap;
}

.nshat-pill svg {
  flex: 0 0 auto;
}

/* The quiet pill: same shape, no accent — for a value that is not a count. */
.nshat-pill--quiet {
  border-color: var(--cw-border);
  background: transparent;
  color: var(--cw-text-2);
}

/* ── A disclosure that reveals a container keeps its trigger in place ───────
   Owner note: «عدد الحصص» و«إضافة نشاط» are the controls that open and close
   containers, and «محافظتها على نفس الموقع في حال فتح أو اغلاق الحاوية أمر مهم
   لتجربة المستخدم».

   Two attempts got this wrong, and the second failure is the interesting one:
   making the <details> itself a flex row does NOT work, because a <details> is
   not a reliable flex container — browsers keep the revealed content in an
   internal box of their own (`::details-content` in current Chrome), so the flex
   items were the SUMMARY and that wrapper, never the children a stylesheet can
   select. The summary therefore came out as a narrow column beside the rows, and
   the closed state looked perfect only because the wrapper is hidden then.

   So the disclosure is a plain BLOCK here: the summary is the first line, pinned
   to the inline end by its own flex row, and whatever is revealed below it is
   ordinary block flow — full width, whichever box the browser keeps it in. No
   rule has to name the revealed element, so no future content can break this. */
.nshat-board-add,
.nshat-activity-tools {
  display: block;      /* flow, not flex: the browser may wrap the content itself */
  flex: 1 1 100%;      /* its own line inside the parent flex row */
}

.nshat-board-add > summary,
.nshat-activity-tools > summary {
  display: flex;               /* the trigger is a row of its own… */
  justify-content: flex-end;   /* …pinned to the inline end, where it already sits */
  inline-size: 100%;           /* clickable across the line; the text stays put */
}

.nshat-board-add > *:not(summary),
.nshat-activity-tools > *:not(summary) {
  inline-size: 100%;
  min-inline-size: 0;
}

/* The hessah control closes a card: a hairline separates it from the numbers
   above it, and it keeps its own line so opening it moves nothing. */
.nshat-activity-tools {
  margin-block-start: 4px;
  padding-block-start: 8px;
  border-block-start: 1px solid var(--cw-border);
}

.nshat-board-add {
  margin-block-end: 10px;
}


/* ── The add-activity form ──────────────────────────────────────────────────
   Scoped as `.nshat-panel .nshat-inline-form`, and that is not decoration: §14
   already styles EVERY form inside a panel as a wrapping flex row
   (`.nshat-panel form` = class + element), which outranks a single class. So the
   grid below — and every breakpoint in it — never took effect, and the three
   selects did not behave alike: the two carrying `.cw-select` (width: 100%)
   claimed a line each, while the `<span>` that receives the fetched activity
   list kept its content's intrinsic width. The form really is a grid now: one
   column on a phone, two from 640px ([phase][activity] over [day][button]), and
   one line only from 1024px, where three selects and a button genuinely fit. */
.nshat-panel .nshat-inline-form {
  display: grid;
  flex: 1 1 100%;   /* fills the panel, which is itself a flex row */
  grid-template-columns: 1fr;
  gap: 8px;
}

@media (min-width: 640px) {
  .nshat-panel .nshat-inline-form {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .nshat-panel .nshat-inline-form .cw-btn {
    justify-self: start;
  }
}

@media (min-width: 1024px) {
  .nshat-panel .nshat-inline-form {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr) minmax(0, 1.1fr) auto;
    align-items: center;
  }
}

/* A grid column is a hard edge: without this, an <option> long enough to matter
   widens its own column and pushes the row out of the card. */
.nshat-panel .nshat-inline-form > * {
  min-inline-size: 0;
}

.nshat-panel .nshat-inline-form .cw-alert {
  grid-column: 1 / -1;
}

@media (prefers-reduced-motion: reduce) {
  .nshat-activity {
    transition: none !important;
  }
}

/* ── Mobile proportion ──────────────────────────────────────────────────────
   Owner report: «الخطوط والأحجام كبيرة على الشاشات الصغيرة وغير متناسبة». This
   page packs more controls than any other screen, so on a phone every one of its
   own elements steps DOWN one notch while the identity's own floor stays put:
   the chips are already 0.75rem and are deliberately left alone — under 12px
   Arabic loses its letterforms, and shrinking them would trade one complaint for
   a worse one. Everything here is scoped to the page wrapper `.nshat-timeline`,
   so no other screen in the project is touched. */
@media (max-width: 640px) {
  .nshat-timeline .brand-actions {
    gap: 6px;
  }

  .nshat-timeline .brand-actions .cw-btn {
    font-size: 0.8125rem;
    padding-inline: 12px;
  }

  .nshat-timeline .brand-section-title {
    font-size: 1.0625rem;
  }

  .nshat-timeline .brand-breadcrumb {
    font-size: 0.75rem;
  }

  .nshat-strip {
    gap: 8px 12px;
    margin-top: 12px;
    padding: 10px 12px;
  }

  .nshat-strip .cw-btn {
    font-size: 0.75rem;
    padding-inline: 10px;
  }

  .nshat-strip-label {
    font-size: 0.6875rem;
  }

  .nshat-board-head {
    padding: 10px 12px;
  }

  .nshat-board-body {
    padding: 10px 12px 12px;
  }

  .nshat-board-name {
    font-size: 0.875rem;
  }

  .nshat-board-meta {
    font-size: 0.75rem;
  }

  .nshat-activity {
    gap: 8px;
    padding: 8px 10px;
  }

  .nshat-thumb {
    inline-size: 40px;
    block-size: 40px;
  }

  .nshat-activity-title {
    font-size: 0.875rem;
  }

  .nshat-pill {
    gap: 4px;
    padding: 2px 8px;
    font-size: 0.6875rem;
  }

  .nshat-timeline .nshat-badge {
    font-size: 0.6875rem;
  }

  .nshat-timeline .nshat-tools summary {
    font-size: 0.75rem;
  }

  .nshat-timeline .cw-alert {
    font-size: 0.8125rem;
  }

  .nshat-panel .nshat-inline-form .cw-btn {
    justify-self: stretch;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   25. SORTABLE ROSTER (drag & drop lists)
   Owner request: «أضف إلى الصفحة إمكانية إعادة ترتيب العناصر عن طريق السحب
   والإفلات بشكل سلس وواضح مع feedback بصري مناسب أثناء السحب». A card in this
   list carries an edit form and a delete link, which decides the whole shape:
   the drag starts from a DEDICATED HANDLE and never from the card, so a click in
   a field can never begin a drag; and the landing edge is drawn as an accent
   rule on the card's own box, so nothing in the list shifts while dragging.
   Colour: the accent family only (--cw-accent-ghost · --cw-accent-border ·
   --nshat-btn-accent-text · --cw-accent), the same decision §22 and §24 took, so
   the semantic tokens stay reserved for state and nothing here reads as a status.
   RTL: logical properties only (margin-inline-*, inset-inline-*, inset-block-*).
   Motion: --cw-silk, with a reduced-motion guard at the end.
   ═══════════════════════════════════════════════════════════════════════════ */

.nshat-sortable {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 16px 0 0;
  padding: 0;
  list-style: none;
}

/* The card is a COLUMN: the drag row on top, then the edit disclosure on a line of
   its own. That split is what keeps the trigger of the disclosure in place when it
   opens (§24's lesson) — nothing is revealed beside the row. */
.nshat-sort-item {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: 14px 16px;
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius-lg);
  background: var(--cw-surface-2);
  box-shadow: var(--cw-shadow-sm);
  transition:
    border-color var(--cw-silk-dur) var(--cw-silk),
    box-shadow var(--cw-silk-dur) var(--cw-silk),
    opacity var(--cw-silk-dur) var(--cw-silk);
}

/* The row that carries identity and actions — the drag unit. */
.nshat-sort-main {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}

/* The card being dragged: still in the flow (so the list never jumps) but quietly
   out of the way, with a dashed accent outline the drop marks can sit against. */
.nshat-sort-item--dragging {
  opacity: 0.55;
  border-color: var(--cw-accent-border);
  border-style: dashed;
  box-shadow: var(--cw-shadow-md);
}

/* Where the card will land: a 3px accent rule on the landing edge. Drawn on the
   card's own box with negative offsets, so it never moves the list by a pixel. */
.nshat-sort-item--drop-before::before,
.nshat-sort-item--drop-after::after {
  content: '';
  position: absolute;
  inset-inline: 10px;
  height: 3px;
  border-radius: var(--cw-radius-full);
  background: var(--cw-accent);
}

.nshat-sort-item--drop-before::before {
  inset-block-start: -6px;
}

.nshat-sort-item--drop-after::after {
  inset-block-end: -6px;
}

/* ── The drag handle: the only element that starts a drag ────────────────────
   The handle is a real <button>, and the core's reset
   `.cw-theme button { border: none; background: none; cursor: pointer }` is
   (0,1,1) — it OUTRANKS a single class. So these four rules are element-qualified
   (`button.nshat-sort-handle` = (0,1,1), later in the file, so it wins on source
   order) — exactly the shape §3b had to take for the theme toggle. Without it the
   pointer would stay an arrow and the grab affordance would be lost silently. */
button.nshat-sort-handle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  inline-size: 34px;
  block-size: 34px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--cw-radius);
  background: transparent;
  color: var(--cw-text-3);
  cursor: grab;
  transition:
    color var(--cw-silk-dur) var(--cw-silk),
    background-color var(--cw-silk-dur) var(--cw-silk),
    border-color var(--cw-silk-dur) var(--cw-silk);
}

button.nshat-sort-handle:hover,
button.nshat-sort-handle:focus-visible {
  color: var(--nshat-btn-accent-text);
  border-color: var(--cw-accent-border);
  background: var(--cw-accent-ghost);
}

button.nshat-sort-handle:active {
  cursor: grabbing;
}

/* ── The position badge: the number the eye follows while reordering ───────── */
.nshat-sort-order {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  inline-size: 28px;
  block-size: 28px;
  border: 1px solid var(--cw-accent-border);
  border-radius: var(--cw-radius-full);
  background: var(--cw-accent-ghost);
  color: var(--nshat-btn-accent-text);
  font-size: 0.75rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* ── The card's own content ────────────────────────────────────────────────── */
.nshat-sort-body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  flex: 1 1 auto;
  min-inline-size: 0;
}

.nshat-sort-name {
  margin: 0;
  font-size: 0.9375rem;
  font-weight: 700;
  line-height: 1.5;
  color: var(--cw-text-1);
}

.nshat-sort-meta {
  margin: 0;
  font-size: 0.75rem;
  line-height: 1.7;
  color: var(--cw-text-2);
}

/* The tasks keep the writer's own line breaks (same decision as §23 .nshat-text)
   and stay inside a readable measure. */
.nshat-sort-tasks {
  margin: 6px 0 0;
  max-inline-size: 62ch;
  font-size: 0.75rem;
  line-height: 1.8;
  color: var(--cw-text-2);
  white-space: pre-line;
}

/* The card's actions stay on the row's end side and never stretch. */
.nshat-sort-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  margin-inline-start: auto;
}

/* ── The edit disclosure: a full-width line under the row ──────────────────── */
.nshat-sort-editor {
  display: block;
  margin-top: 12px;
  padding-top: 12px;
  border-block-start: 1px dashed var(--cw-border);
}

/* A flex row of its own, so the trigger keeps the exact place it had when closed. */
.nshat-sort-editor > summary {
  display: flex;
  justify-content: flex-end;
  inline-size: 100%;
  margin: 0;
}

/* §14 styles `.nshat-panel form` as a wrapping flex row (class + element), which
   OUTRANKS a single class — so the form's own rule is scoped with two classes.
   One column on a phone, two from 640px, and the action row under both. */
.nshat-panel .nshat-sort-form {
  display: grid;
  flex: 1 1 100%;
  grid-template-columns: 1fr;
  gap: 10px;
}

.nshat-panel .nshat-sort-form > * {
  min-inline-size: 0;
}

.nshat-sort-form .nshat-sort-form-full,
.nshat-sort-form .nshat-sort-form-actions {
  grid-column: 1 / -1;
}

.nshat-sort-form .nshat-sort-form-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 2px;
}

@media (min-width: 640px) {
  .nshat-panel .nshat-sort-form {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* ── The screen-reader announcement slot for keyboard reordering ─────────────
   Not display:none — that would remove it from the accessibility tree. */
.nshat-sort-status {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── Phone: the identity and the actions share the first line, the name and the
   tasks take the full width under them ─────────────────────────────────────── */
@media (max-width: 640px) {
  .nshat-sort-item {
    padding: 12px;
  }

  .nshat-sort-main {
    flex-wrap: wrap;
    gap: 8px 10px;
  }

  .nshat-sort-order {
    inline-size: 26px;
    block-size: 26px;
  }

  .nshat-sort-actions {
    order: 2;
  }

  .nshat-sort-body {
    flex: 1 1 100%;
    order: 3;
    padding-inline-start: 2px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .nshat-sort-item,
  .nshat-sort-handle {
    transition: none !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   26. FOLLOW-UP BOARD (the implementation log — monasbh:follow_up_implementation)
   The screen answers one question — «أين وصل تنفيذ كل برنامج؟» — so its shape is
   a board with three levels: a stage heading, one `.nshat-board` per مجال (the
   §24 component, reused as-is), and inside it one card per program.
   The card carries the state twice, on purpose and in two media: a 3px bar on the
   START edge (the §15 precedent) so a whole column can be scanned at a glance,
   and the state word in a badge so the colour is never the only carrier (WCAG
   1.4.1). Both colours come from the semantic tokens, which is exactly what they
   are reserved for — no accent is spent on state here.
   Text: `--cw-text-1` values and `--cw-text-3` labels (the measured 5.1:1).
   RTL: logical properties only (inline-size, inset-inline, border-inline-start).
   ═══════════════════════════════════════════════════════════════════════════ */

.nshat-stage-block {
  margin-top: 22px;
}

/* A stage heading opens its own block, so it keeps its baseline and takes the
   air under it (the identity's own title carries none). */
.nshat-stage-block .brand-subsection-title {
  margin-bottom: 10px;
}

.nshat-stage-block[hidden] {
  display: none !important;
}

.nshat-board + .nshat-board {
  margin-top: 12px;
}

/* ── The program cards: one per program, two per line only on a wide screen ── */
.nshat-follow-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
}

@media (min-width: 1024px) {
  .nshat-follow-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.nshat-follow-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px;
  border: 1px solid var(--cw-border);
  /* the state bar: the only place a state colour touches a surface */
  border-inline-start: 3px solid var(--cw-border);
  border-radius: var(--cw-radius);
  background: var(--cw-surface-2);
  transition: box-shadow var(--cw-silk-dur) var(--cw-silk);
}

/* The card declares `display: flex`, which OUTRANKS the browser's own [hidden]
   rule — so the state is forced once, here, for the filter (the §24 lesson). */
.nshat-follow-card[hidden] {
  display: none !important;
}

/* Hover changes only the shadow: the state bar must survive the pointer. */
.nshat-follow-card:hover,
.nshat-follow-card:focus-within {
  box-shadow: var(--cw-shadow-sm);
}

.nshat-follow-card[data-status="completed"] {
  border-inline-start-color: var(--cw-success);
}

.nshat-follow-card[data-status="failed"] {
  border-inline-start-color: var(--cw-error);
}

.nshat-follow-card[data-status="pending"] {
  border-inline-start-color: var(--cw-warning);
}

.nshat-follow-card[data-status="not-started"] {
  border-inline-start-color: var(--cw-info);
}

.nshat-follow-head {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

.nshat-follow-body {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  min-inline-size: 0;
}

/* The program name wraps — it is the one thing the card exists to say, so it is
   never truncated the way a board row truncates its title (§24). */
.nshat-follow-name {
  margin: 0;
  font-size: 0.9375rem;
  font-weight: 700;
  line-height: 1.6;
  color: var(--cw-text-1);
}

/* ── The facts: label over value, in equal wells ───────────────────────────── */
.nshat-fact-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 132px), 1fr));
  gap: 8px;
  margin: 0;
  padding: 0;
}

.nshat-fact {
  padding: 8px 10px;
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius);
  background: var(--cw-bg-alt);
}

.nshat-fact-label {
  margin: 0;
  font-size: 0.6875rem;
  font-weight: 600;
  line-height: 1.5;
  color: var(--cw-text-3);
}

.nshat-fact-value {
  margin: 2px 0 0;
  font-size: 0.8125rem;
  font-weight: 700;
  line-height: 1.5;
  color: var(--cw-text-1);
}

/* ── Two state badges §14 does not carry, on the same semantic tokens ─────── */
.nshat-badge--error {
  background: var(--cw-error-bg);
  border-color: var(--cw-error);
}

.nshat-badge--info {
  background: var(--cw-info-bg);
  border-color: var(--cw-info);
}

/* The count inside a filter pill: digits that line up with their neighbours. */
.nshat-filter-count {
  font-variant-numeric: tabular-nums;
}

@media (max-width: 640px) {
  .nshat-follow-card {
    padding: 12px;
  }

  .nshat-fact-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .nshat-follow-name {
    font-size: 0.875rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .nshat-follow-card {
    transition: none !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   27. ACTIVITY CARD SCREENS (بطاقات تنفيذ الأنشطة — list · add · edit)
   The three screens of one record. What they need that the identity did not
   already carry: a card-type switch built from REAL radios (so it works with the
   keyboard and with JavaScript off), the field groups that switch shows and
   hides, a picture strip, and a list-card headline.
   Colour: the accent family for the switch and the pictures; the required marker
   stays in the ink colour instead of a red that would not clear AA as small text.
   RTL: logical properties only. Motion: --cw-silk with a reduced-motion guard.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── The card-type switch: two framed choices, the chosen one takes the ring ─ */
.nshat-segmented {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
  gap: 10px;
}

.nshat-segment {
  position: relative;   /* anchors the clipped radio inside its own label */
  display: block;
  cursor: pointer;
}

/* The radio keeps its place in the tab order and in the form, and stops drawing
   itself — clipped rather than `display: none`, which would remove it from the
   accessibility tree and from the keyboard. */
.nshat-segment-input {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.nshat-segment-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 12px 14px;
  border: 1.5px solid var(--nshat-btn-border);
  border-radius: var(--cw-radius);
  background: var(--nshat-btn-surface);
  transition:
    border-color var(--cw-silk-dur) var(--cw-silk),
    background-color var(--cw-silk-dur) var(--cw-silk),
    box-shadow var(--cw-silk-dur) var(--cw-silk);
}

.nshat-segment-input:checked + .nshat-segment-body {
  border-color: var(--cw-btn-primary-bg);
  background: var(--cw-accent-ghost);
  box-shadow: var(--nshat-btn-shadow);
}

.nshat-segment-input:focus-visible + .nshat-segment-body {
  outline: 2px solid var(--cw-accent);
  outline-offset: 2px;
}

.nshat-segment-title {
  font-size: 0.875rem;
  font-weight: 700;
  color: var(--cw-text-1);
}

.nshat-segment-text {
  font-size: 0.75rem;
  line-height: 1.7;
  color: var(--cw-text-2);
}

/* The required marker: ink, not red — a red this small would not clear AA. */
.nshat-required {
  color: var(--cw-text-1);
  font-weight: 700;
}

/* ── Field groups the switch shows and hides ────────────────────────────────
   Forced once, here: the groups are `.brand-field`s (display: flex) and a grid,
   and either would outrank the browser's own [hidden] rule. */
.nshat-type-fields[hidden] {
  display: none !important;
}

/* A staged row is a flex row too, so `hidden` needs the same force the other list
   components carry — this is the §24 lesson applied to the plan-stage picker. */
.nshat-row[hidden] {
  display: none !important;
}

.nshat-type-fields {
  margin-top: 16px;
}

/* ── Check grids: the execution icons and the three periods ───────────────── */
.nshat-check-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 170px), 1fr));
  gap: 6px 14px;
}

/* ── Picture strips: the card's stored pictures, on both screens ──────────── */
.nshat-shot-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.nshat-shot-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}

.nshat-shot {
  display: block;
  inline-size: 72px;
  block-size: 72px;
  overflow: hidden;
  border: 1px solid var(--cw-border);
  border-radius: calc(var(--cw-radius) - 4px);
  background: var(--cw-accent-ghost);
}

.nshat-shot img {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

.nshat-shot--more {
  display: grid;
  place-items: center;
  font-size: 0.8125rem;
  font-weight: 700;
  color: var(--nshat-btn-accent-text);
}

.nshat-shot-empty {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-size: 0.75rem;
  line-height: 1.8;
  color: var(--cw-text-2);
}

/* ── One card in the list: headline over fact wells over its actions ──────── */
.nshat-card-headline {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}

.nshat-card-heading {
  margin: 0;
  font-size: 0.9375rem;
  font-weight: 700;
  line-height: 1.5;
  color: var(--cw-text-1);
}

.nshat-card-badges {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.nshat-card-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 12px;
  padding-top: 12px;
  border-block-start: 1px solid var(--cw-border);
}

/* ── The alert slot the form's script writes into ─────────────────────────────
   `:empty` keeps an unused slot from adding a gap. The alert itself is the
   identity's own `.cw-alert`, created by the script with createElement — never
   markup assembled from a string. */
.nshat-alerts {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 14px;
}

.nshat-alerts:empty {
  display: none;
}

.nshat-alerts .cw-alert {
  margin: 0;
}

@media (max-width: 640px) {
  .nshat-shot {
    inline-size: 64px;
    block-size: 64px;
  }

  .nshat-card-actions .cw-btn {
    flex: 1 1 auto;
    justify-content: center;
  }
}

@media (prefers-reduced-motion: reduce) {
  .nshat-segment-body {
    transition: none !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   28. RAMADAN PLAN (خطة شهر رمضان — home:ramadhan)
   The screen is two sections of the same document: the weeks of Ramadan as the
   §24 board (its head carries the week and both calendars, its body one row per
   activity and the sessions behind a disclosure), then the non-classroom
   activities as cards on the §26 fact wells. Only two things were missing.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Two sections closed by their own heading need air between them — the identity
   ships no page-level section rhythm, the shell owns the container only. */
.nshat-section {
  margin-top: 26px;
}

/* The sessions inside a week's disclosure are an <ol> of plain fact rows, and a
   list element brings the browser's own marker and inline padding with it.
   Neither belongs in a count whose rows already read «الحصة 3». */
.nshat-session-list {
  padding: 0;
  list-style: none;
}

/* ═══════════════════════════════════════════════════════════════════════════
   29. STUDY PLANS (الخطط الدراسية — study_plans)
   The app's screens are documents and forms built from §12–§26 already; the one
   thing they need is the conditional sub-form a choice reveals (خطة جديدة: إضافة
   شعبة جديدة / إضافة صف جديد). It is hidden with the `hidden` attribute — never
   with an inline display from a script — and a card that declares its own display
   would outrank the browser's rule, so the state is forced here once.
   ═══════════════════════════════════════════════════════════════════════════ */
.nshat-conditional[hidden] {
  display: none !important;
}

/* ═══════════════════════════════════════════════════════════════════════════
   30. STUDY-PLAN REPORT (study_plans:print_report)
   The screen is a report: composed options, then figures, then two sortable
   tables — and it has to leave the printer as a clean sheet. Three additions
   only; everything else is §12–§26.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── A ranked figure drawn as a bar ─────────────────────────────────────────
   The bar's LENGTH is geometry, so it lives in the SVG's own `width` attribute
   (a viewBox of 0 0 100 … makes that attribute a percentage) — the one thing a
   page may not write is an inline `style`, and this keeps it out of the markup.
   The track mirrors in RTL because a bar chart does GROW in a direction; that is
   the opposite decision to 17d, where the tick had to stay put: a symbol must
   not flip, a quantity's growth must follow the reading direction. */
.nshat-bars {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.nshat-bar {
  display: flex;
  align-items: center;
  gap: 10px;
}

.nshat-bar-label {
  flex: 0 0 auto;
  max-inline-size: 14ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.75rem;
  color: var(--cw-text-2);
}

.nshat-bar-track {
  flex: 1 1 auto;
  block-size: 14px;
}

.nshat-bar-bg {
  fill: var(--cw-bg-alt);
}

.nshat-bar-fill {
  fill: var(--cw-accent);
}

.nshat-bar-value {
  flex: 0 0 auto;
  min-inline-size: 3ch;
  font-size: 0.75rem;
  font-weight: 700;
  text-align: center;
  color: var(--cw-text-1);
}

html[dir="rtl"] .nshat-bar-track {
  transform: scaleX(-1);
}

/* ── A sortable header: the affordance the pointer needs ──────────────────── */
th[data-sort-col] {
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}

th[data-sort-col]:hover,
th[data-sort-col]:focus-visible {
  background: var(--cw-accent-ghost);
}

th[data-sort-col][aria-sort="ascending"]::after {
  content: ' ▲';
  color: var(--nshat-btn-accent-text);
}

th[data-sort-col][aria-sort="descending"]::after {
  content: ' ▼';
  color: var(--nshat-btn-accent-text);
}

/* ── The options' scrollable group ─────────────────────────────────────────── */
.nshat-check-scroll {
  max-block-size: 220px;
  overflow-y: auto;
  padding-inline-end: 6px;
}

/* ── What the printer gets ──────────────────────────────────────────────────
   The identity's own print block (§8) already drops the shell and flattens the
   surfaces; this adds the report's own two needs: the controls must not print,
   and the tables must compact and keep their header on every page. */
@media print {
  .nshat-report .no-print {
    display: none !important;
  }

  .nshat-report {
    font-size: 11px;
  }

  .nshat-report .cw-table {
    font-size: 0.6875rem;
    inline-size: 100%;
  }

  .nshat-report .cw-table th,
  .nshat-report .cw-table td {
    padding: 2px 4px;
    white-space: nowrap;
  }

  .nshat-report thead {
    display: table-header-group;
  }

  .nshat-report tr,
  .nshat-report .nshat-stat,
  .nshat-report .nshat-bars {
    break-inside: avoid;
  }

  .nshat-report .nshat-stat {
    border: 1px solid #cccccc;
    padding: 5px 3px;
  }

  .nshat-report .nshat-stat-value {
    font-size: 0.8125rem;
  }

  .nshat-report .nshat-bar-track {
    block-size: 10px;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   31. PLAN BUILDER (الخطة الزمنية — home:start-plan, الخطوة الأولى)
   A four-step form. Two things the identity did not carry: a step indicator and
   a framed day picker built from REAL checkboxes (the legacy hid the input with
   `display: none`, which takes it out of the keyboard and out of the form's
   reach — and drew its own circles). Everything else is §12–§26 again.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── The step indicator: four pills, the current one ringed ───────────────── */
.nshat-steps {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* The items are `.nshat-steps-item`, NOT `.nshat-step` — and the name IS the fix.
   §17 already owns `.nshat-step` for the 26×26 numbered marker of a payment block
   (used on the subscription screens), and two components sharing one name is not
   a cosmetic detail: whatever this rule does not re-declare keeps coming from the
   other side. Here that lost `width` · `height` · `flex` · `line-height` — so each
   pill was squeezed into a 26px box and its label spilled over its neighbour. One
   name, one component; §17's marker is untouched and so is every payment screen. */
.nshat-steps-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius-full);
  background: var(--nshat-btn-surface);
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--cw-text-2);
}

.nshat-steps-circle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 22px;
  block-size: 22px;
  border-radius: 50%;
  background: var(--cw-bg-alt);
  color: var(--cw-text-1);
  font-size: 0.6875rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.nshat-steps-item--active {
  border-color: var(--cw-accent-border);
  background: var(--cw-accent-ghost);
  color: var(--nshat-btn-accent-text);
}

/* #ffffff is not invented here: it is the project layer's own value for text on
   the primary surface (§10), which is the same colour in both themes. */
.nshat-steps-item--active .nshat-steps-circle {
  background: var(--cw-btn-primary-bg);
  color: #ffffff;
}

.nshat-steps-item--done {
  border-color: var(--cw-success);
  background: var(--cw-success-bg);
  color: var(--cw-text-1);
}

.nshat-steps-item--done .nshat-steps-circle {
  background: var(--cw-success);
  color: var(--cw-text-1);
}

/* A step panel is a block card; the state is forced so `hidden` always wins. */
.nshat-step-panel[hidden] {
  display: none !important;
}

/* ── The day picker: a real checkbox behind a framed day ──────────────────── */
.nshat-days {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.nshat-day {
  position: relative;
  cursor: pointer;
}

/* Clipped, not `display: none`: the box keeps its keyboard focus and its place
   in the form — which is exactly what the legacy rule took away. */
.nshat-day-input {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.nshat-day-body {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 10px 16px;
  border: 1.5px solid var(--nshat-btn-border);
  border-radius: var(--cw-radius);
  background: var(--nshat-btn-surface);
  transition:
    border-color var(--cw-silk-dur) var(--cw-silk),
    background-color var(--cw-silk-dur) var(--cw-silk),
    box-shadow var(--cw-silk-dur) var(--cw-silk);
}

.nshat-day-input:checked + .nshat-day-body {
  border-color: var(--cw-btn-primary-bg);
  background: var(--cw-accent-ghost);
  box-shadow: var(--nshat-btn-shadow);
}

.nshat-day-input:focus-visible + .nshat-day-body {
  outline: 2px solid var(--cw-accent);
  outline-offset: 2px;
}

.nshat-day-circle {
  display: grid;
  place-items: center;
  inline-size: 34px;
  block-size: 34px;
  border-radius: 50%;
  background: var(--cw-bg-alt);
  color: var(--cw-text-1);
  font-weight: 700;
}

.nshat-day-input:checked + .nshat-day-body .nshat-day-circle {
  background: var(--cw-btn-primary-bg);
  color: #ffffff;
}

.nshat-day-name {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--cw-text-2);
}

@media (max-width: 480px) {
  .nshat-days {
    gap: 8px;
  }

  .nshat-day-body {
    padding: 8px 12px;
  }

  .nshat-day-circle {
    inline-size: 30px;
    block-size: 30px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .nshat-day-body {
    transition: none !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   32. PLAN BUILDER — THE PINNED INFO PANEL
   (لوحة «معلومات النظام» و«الإحصائيات» في home:start-plan)
   The owner asked for these two blocks to stay visible in all four steps of the
   builder. They used to sit inside step 1, so the first «التالي» hid them. They
   now live OUTSIDE every `.nshat-step-panel` — and that is the whole mechanism:
   `goToStep()` toggles the panels and can no longer reach them — taking a second
   grid column, so a long step (the programs table) scrolls under a panel that
   stays. On narrow screens a rail would eat the viewport, so the panel moves
   above the form and the sticking is dropped; the numbers stay on screen anyway.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Two logical columns: in RTL the form is the right-hand column, the rail its
   left neighbour. */
.nshat-plan-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 16px;
}

/* Deliberately no `align-items: start`: the aside has to keep the full height of
   its grid row, or the sticky panel inside it would have nowhere to travel. */
.nshat-plan-aside {
  min-width: 0;
}

/* The rail reuses the standard block-card surface and only tightens its frame —
   declared later in this same file, so the single-class padding wins on source
   order. */
.nshat-live-panel {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 18px 16px;
}

.nshat-live-block {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.nshat-live-block + .nshat-live-block {
  padding-block-start: 14px;
  border-block-start: 1px solid var(--cw-border);
}

/* The three counts are the only numbers here that move: even widths so they do
   not jump while changing, and a calm colour transition so the change is felt. */
.nshat-live-value {
  font-variant-numeric: tabular-nums;
  transition: color var(--cw-silk-dur) var(--cw-silk);
}

.nshat-live-note {
  margin: 0;
  font-size: 0.6875rem;
  font-weight: 600;
  line-height: 1.6;
  color: var(--cw-text-3);
}

/* A quiet "live" mark, drawn at the start of the line — logical, so it follows
   the sentence in both directions. */
.nshat-live-note::before {
  content: "";
  display: inline-block;
  inline-size: 6px;
  block-size: 6px;
  margin-inline-end: 6px;
  border-radius: 50%;
  background: var(--cw-accent);
  vertical-align: middle;
}

@media (min-width: 1024px) {
  .nshat-plan-layout {
    grid-template-columns: minmax(0, 1fr) 20rem;
    gap: 20px;
  }

  .nshat-live-panel {
    position: sticky;
    top: 20px;
  }
}

/* No room for a rail: the panel leads, the form follows. */
@media (max-width: 1023px) {
  .nshat-plan-aside {
    order: -1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .nshat-live-value {
    transition: none !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   33. ARABIC TABLE HEADERS — the core's tracking has to go
   `.cw-table th` (core §22) carries `letter-spacing: .05em` and
   `text-transform: uppercase`. Both are right for a Latin label and wrong for an
   Arabic one: tracking pulls the joined letterforms apart, and Arabic has no
   case at all. This is the Arabic-first correction, scoped to the header row so
   that nothing else about the table moves. `.cw-table thead th` is what makes
   the fix win — it is one class and two elements against the core's one class
   and one element, so source order does not enter into it.
   ═══════════════════════════════════════════════════════════════════════════ */

.cw-table thead th {
  letter-spacing: 0;
  text-transform: none;
}

/* A row header (`th scope="row"` inside tbody) is the row's NAME, so it takes the
   same correction — and it must not wear a column head's clothes either: the 2px
   rule under it belongs to a header row, and the muted colour to a label. This is
   what lets a table mark its first cell as the row's own header for assistive
   technology instead of pretending it is an ordinary cell. */
.cw-table tbody th {
  letter-spacing: 0;
  text-transform: none;
  border-bottom: 1px solid var(--cw-border);
  color: var(--cw-text-1);
}

/* ═══════════════════════════════════════════════════════════════════════════
   34. A HIDDEN ALERT HAS TO STAY HIDDEN
   `.cw-alert` declares `display: flex` (core §12), and an author `display`
   declaration OUTRANKS the browser's own `[hidden]` rule — so an alert carrying
   the `hidden` attribute is still painted. The shared character-counter partial
   (home/_text_counters.html) hides its «limit reached» note exactly that way,
   which means the plan-text screens that use it have been showing that warning
   from page load, over an empty field. Forced once, here, for every alert in the
   product: the §24 lesson (a component that declares `display` must force its
   own hidden state), applied to the alert itself.
   ═══════════════════════════════════════════════════════════════════════════ */

.cw-alert[hidden] {
  display: none !important;
}

/* ═══════════════════════════════════════════════════════════════════════════
   35. COMPACT CARDS WITH A PICTURE BAND
   A card whose subject is a picture needs two sizes the identity did not carry:
   a **band** — the picture is a narrow strip above the name, for a grid of many
   items — and a **square** — the picture is one element beside the facts, for a
   single record's page. Both are the media frame's own surface (border, radius,
   background) with a height instead of the picture's natural one, and
   `object-fit: cover` so no aspect ratio leaves a gap.
   ═══════════════════════════════════════════════════════════════════════════ */

.nshat-card-media {
  display: block;
  block-size: 120px;
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius);
  background: var(--cw-bg-alt);
  overflow: hidden;
}

.nshat-card-media img {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

/* The band sits above the headline, so it carries its own spacing. */
.nshat-card-media + .nshat-card-headline {
  margin-block-start: 10px;
}

/* One record's page: a square, not a banner. */
.nshat-card-media--square {
  block-size: 140px;
  inline-size: 140px;
}

/* A compact grid: more cards per row, each one light. Declared after the base
   grid's own 560px rule in this same file, so the single-class modifier wins on
   source order. */
@media (min-width: 560px) {
  .nshat-card-grid--compact {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 210px), 1fr));
  }
}

/* Most field pictures are square, so the card's frame is a square too: the whole
   picture shows instead of a band cropped out of its middle. It is centred in the
   card — and so is the name under it, since the badge is the only thing on that
   line. */
.nshat-card-media--compact {
  inline-size: 110px;
  block-size: 110px;
  margin-inline: auto;
}

.nshat-card-headline--center {
  justify-content: center;
}

/* A light state line ABOVE the picture — the stages an occasion targets. Centred
   on the card's axis like the picture and the name below it, and small enough
   that it reads as a tag rather than a title. */
.nshat-card-top {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 4px;
  margin-block-end: 8px;
}

/* The smaller badge: one class against one class, so it wins on source order
   (this file's §35 sits after §14's badge rules) — no `!important`. */
.nshat-badge--sm {
  font-size: 0.625rem;
  padding: 2px 8px;
}

/* ═══════════════════════════════════════════════════════════════════════════
   36. FORCED HIDDEN STATE FOR THE ACTION ROW

   The lesson §24 wrote for boards and §34 for alerts, applied to the action row:
   `.brand-actions` declares `display: flex`, and an author rule always outranks
   the user-agent `[hidden] { display: none }` — so a script that hides one with
   the `hidden` attribute had no effect on it. The stage picker of the new
   activities flow in the occasions pages hides exactly that while its request is
   in flight. Forced once, here, so the attribute means what it says.
   ═══════════════════════════════════════════════════════════════════════════ */

.brand-actions[hidden] {
  display: none !important;
}

/* A filtered card in a card grid must really disappear. `.nshat-app-card` is the
   card of the lists (a programme, an activity, a person), and this is the §24
   lesson applied BEFORE it can bite: whatever a later rule gives that card as a
   `display`, an author declaration would outrank the browser's own
   `[hidden] { display: none }` — and the filter would look dead while the DOM
   says it is hidden. Forced once, here, for the component the filters use. */
.nshat-app-card[hidden] {
  display: none !important;
}

/* ═══════════════════════════════════════════════════════════════════════════
   37. VERTICAL RHYTHM INSIDE A SECTION CARD

   The card is a plain block surface: it owns its padding and nothing else, and
   its children were left to carry margins of their own. Several of them carry
   none at all — `.nshat-fact-grid` resets its margins, `.brand-actions` never
   had one, and `.nshat-card-media--square` is a bare block — so a card grouping
   a picture, a description, a facts grid and an action row collapsed into one
   unbroken column: the white space between the blocks was simply missing.

   The fix is a stack rhythm — every child except the last carries a bottom
   margin. Bottom rather than top on purpose: a first child with no box (a form
   token, a hidden input) takes its margin nowhere, so the first visible block
   still meets the card's own padding instead of being pushed down by it.
   Adjacent margins collapse, so a child that already declares a larger one
   keeps it: this rule raises the floor, it never adds to what is there.

   The same rhythm runs one level into a form. The add and edit screens of this
   project put their whole form inside the card, so its own blocks are the card's
   real content — and a field placed outside the form's grid (a full-width
   textarea, for instance) had nothing at all separating it from the grid above.
   `.brand-form-actions` already carries a top margin of its own, which collapses
   with this one, so the action row is unchanged.

   `.nshat-live-panel` is excluded: it is a flex column with a `gap` of its own,
   and margins do not collapse inside a flex container — the rule would have
   widened that pinned rail instead of levelling its blocks.
   ═══════════════════════════════════════════════════════════════════════════ */

.brand-section-card:not(.nshat-live-panel) > *:not(:last-child),
.brand-section-card:not(.nshat-live-panel) > form > *:not(:last-child) {
  margin-block-end: 20px;
}

/* ═══════════════════════════════════════════════════════════════════════════
   38. A RECORD CARD'S AXIS — portrait and action row centred

   The grid card already centres its square frame (`--compact` carries
   `margin-inline: auto`), but the single-record card did not: its square sat
   against the inline-start edge while the description and the facts below it
   ran the full width, so the record's eye-line began at one corner instead of
   on the card's axis. Two modifiers put it back:

   · `.nshat-card-media--center` centres the square frame inside the card.
   · `.brand-actions--center` centres the action row beneath it.

   Both are single-class modifiers declared after their base rules in this same
   file, so they win on source order — no `!important`. The edit form's
   «الصورة الحالية» preview keeps the frame exactly as it was: it sits under a
   field label and belongs to that label's edge, not to a card's axis.
   ═══════════════════════════════════════════════════════════════════════════ */

.nshat-card-media--center {
  margin-inline: auto;
}

.brand-actions--center {
  justify-content: center;
}

/* ═══════════════════════════════════════════════════════════════════════════
   39. GALLERY MEDIA SIZE — the picture keeps its own size, and never shrinks

   `.nshat-media-frame` was built for **one** player inside a card: a border, a
   radius, `overflow: hidden` — and no height of its own. In a *grid* of
   pictures that means a tall shot (a 1000×3000 phone photo) rules its column.

   A first cut of this section answered that with a **cap**: a fixed 200px band
   with the file fitted into it by `object-fit: contain`. Rows came level, but
   it made **portraits tiny** — a 1000×3000 photo inside a 300×200 band is
   scaled down to 67×200, i.e. 67px wide. The bound was measuring the *frame*
   instead of the *picture*, so it read as «the pictures got smaller».

   The bound this section keeps is a **floor, not a cap**:

   · the picture, the video or the inline SVG keeps its own proportions at the
     full width of its column (`inline-size: 100%; block-size: auto`), so it is
     never rendered narrower than the column allows — and a small source file is
     scaled *up* to the column instead of staying at its pixel size;
   · the frame carries a `min-block-size` band (240px; 200px at ≤560px), so even
     a wide panorama still occupies a readable area and no box collapses;
   · `object-fit: contain` stays, so **nothing is cropped** — there is no
     `cover` here and no `overflow` slice;
   · the gallery's columns are wider than the base grid's 220px (`280px`), so a
     row holds fewer pictures and every one of them is larger. On a phone the
     column is the screen width, exactly as before.

   A row may therefore be uneven when a portrait stands beside a landscape —
   that is the picture's own height, and showing the picture whole and large
   takes precedence over leveling the band. Accent and semantic tokens only; no
   literal colour, no `!important`, and the band is a logical `block-size` so
   RTL and LTR behave alike.
   ═══════════════════════════════════════════════════════════════════════════ */

.nshat-gallery {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
  align-items: start;
  gap: 20px;
}

.nshat-gallery .nshat-media-frame {
  display: block;
  min-block-size: 240px;
}

.nshat-gallery .nshat-media-frame > a {
  display: block;
}

.nshat-gallery .nshat-media-frame img,
.nshat-gallery .nshat-media-frame video,
.nshat-gallery .nshat-media-frame svg {
  display: block;
  inline-size: 100%;
  block-size: auto;
  min-block-size: 240px;
  object-fit: contain;
}

@media (max-width: 560px) {
  .nshat-gallery .nshat-media-frame,
  .nshat-gallery .nshat-media-frame img,
  .nshat-gallery .nshat-media-frame video,
  .nshat-gallery .nshat-media-frame svg {
    min-block-size: 200px;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   40. SELECTED-ACTIONS CARD — a card of bulk actions exists only with a selection

   The documentation gallery and the student-shares screen both open with a card
   of bulk actions («إجراءات على المحدَّد»). With nothing ticked every button in
   it does nothing: the view receives an empty `checked`, the page says so in a
   hint, and the script refuses the submission anyway. So the honest state is not
   a card of dead buttons but no card at all — the script toggles the `hidden`
   attribute on the card itself, on load and on every change.

   `.brand-section-card` declares no `display`, so the browser's own
   `[hidden] { display: none }` already applies. The state is still forced here,
   once, in the style of the §24 lesson, so that a `display` added to the card
   later cannot silently resurrect it.

   `.nshat-selected-actions` is a hook, not a look: it carries no paint of its
   own, and the card keeps every `.brand-section-card` surface and spacing.
   ═══════════════════════════════════════════════════════════════════════════ */

.nshat-selected-actions[hidden] {
  display: none !important;
}

/* ═══════════════════════════════════════════════════════════════════════════
   41. ROW BADGE STRIP — state badges on one line inside a list row

   `.nshat-row-main` is a flex **column** (title, then meta lines, then text), so
   badges placed inside it would each take a line of their own and turn a
   two-line row into a six-line one. `.nshat-row-badges` is the horizontal strip
   they need: a plain flex row that wraps on narrow screens.

   It carries **no paint of its own** — every badge keeps the semantic tokens it
   already had (§14 state badges, §35 `--sm`), and no literal colour enters here.
   It is reusable for any list built on `.nshat-rows`, which is why it lives in
   the layer and not inside the teachers-report template.
   ═══════════════════════════════════════════════════════════════════════════ */

.nshat-row-badges {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}

/* ═══════════════════════════════════════════════════════════════════════════
   42. DRAG REORDER — the grip, and the card in flight

   A question is moved by dragging its grip. The gesture is built on **pointer
   events** (pointerdown / pointermove / pointerup) rather than the HTML5 drag
   API, and that is deliberate: HTML5 drag events never fire on a touch screen,
   so a finger would have had no way to reorder at all — and the answer to that
   must not be a pair of arrow buttons crowding every card. One gesture, mouse
   and finger alike, with no library anywhere.

   While the pointer is down the card is dimmed and **follows the pointer inside
   the list itself**: it is inserted at the new position as the pointer passes
   each neighbour, so the list shows the result continuously and no separate
   drop marker is needed.

   · `.nshat-drag-handle` — the grip, and the one place a drag may start, so text
     inside a card can still be selected. `touch-action: none` stops the page
     from scrolling under the finger mid-drag, and the grip is focusable with
     ArrowUp / ArrowDown so the order is also reachable from the keyboard;
   · `[data-dragging='true']` — the card in flight.
   ═══════════════════════════════════════════════════════════════════════════ */

.nshat-drag-handle {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  color: var(--cw-text-3);
  cursor: grab;
  touch-action: none;
}

.nshat-drag-handle:active {
  cursor: grabbing;
}

/* the grip is focusable (ArrowUp / ArrowDown move the card), so it needs the
   same visible focus ring the layer gives every other custom control */
.nshat-drag-handle:focus-visible {
  outline: 2px solid var(--cw-accent);
  outline-offset: 2px;
  border-radius: var(--cw-radius-sm);
}

/* ═══════════════════════════════════════════════════════════════════════════
   43. NSHAT METER — a share of a whole, drawn by the browser itself

   The survey analysis draws a bar for every option («٤٢% — نعم»). A bar needs a
   width that comes from the data, and a width written into the element would be
   an inline style — which this identity does not allow, and which no theme
   token could recolour.

   `<progress>` is the browser's own answer: the value lives in an **element
   attribute** (`value` / `max`), so the markup stays free of styles, the bar is
   repainted by the theme, `accent-color` paints it in the accent token, and a
   screen reader reads the value as a value rather than as a shape.

   The percentage is always printed as text beside it as well: the bar supports
   the number, it never carries the meaning alone.
   ═══════════════════════════════════════════════════════════════════════════ */

.nshat-meter {
  display: block;
  inline-size: 100%;
  block-size: 10px;
  accent-color: var(--cw-accent);
  border: none;
  background: transparent;
}

[data-dragging='true'] {
  opacity: 0.55;
}

/* ═══════════════════════════════════════════════════════════════════════════
   44. CARD ACTION ROW — the card's width is the buttons' width

   `.brand-actions` wraps by default, which is right in a page header (a row of
   unrelated shortcuts, each sized by its own label) and wrong **inside a card**:
   there the actions belong to that card, and a wrapped row becomes two ragged
   columns whose widths match nothing.

   So inside a card the row is ONE line. The buttons share the card's width
   equally (`flex: 1 1 0` with `min-inline-size: 0`, so even a long label cannot
   push the row wider than the card, and no button can be squeezed into a
   wrapped two-line label), and the type and padding step down with the **page**
   width instead of spilling onto a second line.

   What does not stretch:
   · icon-only squares (`.cw-btn-icon`) — a stretched square stops being an icon,
     and in rows such as the question cards they are the whole row;
   · a `<form>` child (a button that must post on its own), which keeps its
     natural size and its own submit button's size.

   The last resort is an ellipsis: a label that still does not fit is clipped,
   not wrapped, so the row stays exactly one line tall. And the buttons are
   stretched to equal height, since `align-items: stretch` is what makes a row
   of different labels read as one row.

   Scope: `.brand-section-card` (the section card), `.nshat-media-item` (the
   media card in the galleries and ad lists) and `.nshat-app-card` (the compact
   card). Nothing outside a card is touched: the page-header rows keep wrapping.
   ═══════════════════════════════════════════════════════════════════════════ */

.brand-section-card .brand-actions,
.nshat-media-item .brand-actions,
.nshat-app-card .brand-actions {
  flex-wrap: nowrap;
  align-items: stretch;
  gap: 8px;
}

.brand-section-card .brand-actions > .cw-btn,
.nshat-media-item .brand-actions > .cw-btn,
.nshat-app-card .brand-actions > .cw-btn {
  flex: 1 1 0;
  min-inline-size: 0;
  justify-content: center;
  padding-inline: 10px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.brand-section-card .brand-actions > .cw-btn-icon,
.nshat-media-item .brand-actions > .cw-btn-icon,
.nshat-app-card .brand-actions > .cw-btn-icon,
.brand-section-card .brand-actions > form,
.nshat-media-item .brand-actions > form,
.nshat-app-card .brand-actions > form {
  flex: 0 0 auto;
}

.brand-section-card .brand-actions > svg,
.nshat-media-item .brand-actions > svg {
  flex: 0 0 auto;
}

/* Labels step down with the viewport, not with the card: a card in a narrow
   grid is not the page, and the page is what the reader is holding. */
@media (max-width: 640px) {
  .brand-section-card .brand-actions > .cw-btn,
  .nshat-media-item .brand-actions > .cw-btn,
  .nshat-app-card .brand-actions > .cw-btn {
    padding-inline: 8px;
    font-size: 0.8125rem;
  }
}

@media (max-width: 420px) {
  .brand-section-card .brand-actions,
  .nshat-media-item .brand-actions,
  .nshat-app-card .brand-actions {
    gap: 6px;
  }

  .brand-section-card .brand-actions > .cw-btn,
  .nshat-media-item .brand-actions > .cw-btn,
  .nshat-app-card .brand-actions > .cw-btn {
    padding-inline: 6px;
    font-size: 0.75rem;
    letter-spacing: -0.01em;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   45. ACTION COLUMNS — the row behaves like a table's columns on a small screen

   §44 keeps a **card's** row on one line, but it leaves two things undone on a
   narrow screen: rows that live outside a card (a page head, a board body, a
   panel's foot) still wrap into two or three ragged lines, and even inside a
   card the buttons share the width only after `flex` has finished deciding —
   which cannot promise a single row when a label is long.

   The hook below changes the mechanism, not the look: the row is a **grid of
   one line whose columns are `1fr` each**. That is the table behaviour — every
   cell takes exactly its share of the available width, nothing wraps, and the
   row's total width is the container's width, never more (a `1fr` track cannot
   overflow, unlike a flex item whose base size is its own content).

   Applied only below 900px (phone and portrait tablet), through an explicit
   page-level hook (`<div class="nshat-actions-columns">`), so no other page's
   behaviour changes. Widths are shared equally rather than sized by label, and
   the type scales with the **viewport** — the only honest stand-in for "how wide
   the card is" when the card itself is a grid cell:

   · `padding-inline` 4–12px and `font-size` 11–14px, both `clamp()`ed to the
     viewport, so three buttons fit a 360px phone and stay legible;
   · `white-space: nowrap` + `overflow: hidden`, because a label must never push
     the row onto a second line — the type has already shrunk by then;
   · icon-only squares stop being grid cells (`justify-self: center`) — they are
     a symbol, and a stretched symbol is no longer a symbol;
   · a `<form>` child becomes a one-cell grid so its own submit button fills it.

   Above 900px nothing is changed: there is room, and a row of shortcuts sized
   by their own labels is the better shape.
   ═══════════════════════════════════════════════════════════════════════════ */

@media (max-width: 900px) {
  .nshat-actions-columns .brand-actions {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    align-items: stretch;
    gap: clamp(6px, 1.6vw, 10px);
    flex-wrap: nowrap;
  }

  .nshat-actions-columns .brand-actions > .cw-btn {
    inline-size: 100%;
    min-inline-size: 0;
    justify-content: center;
    padding-inline: clamp(4px, 1.6vw, 12px);
    font-size: clamp(0.6875rem, 3.2vw, 0.875rem);
    letter-spacing: -0.01em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: clip;
  }

  .nshat-actions-columns .brand-actions > .cw-btn-icon {
    inline-size: auto;
    justify-self: center;
  }

  .nshat-actions-columns .brand-actions > form {
    display: grid;
    min-inline-size: 0;
  }

  .nshat-actions-columns .brand-actions > form > .cw-btn {
    inline-size: 100%;
    justify-content: center;
    padding-inline: clamp(4px, 1.6vw, 12px);
    font-size: clamp(0.6875rem, 3.2vw, 0.875rem);
    white-space: nowrap;
    overflow: hidden;
  }

  /* A badge (or any non-button child) keeps its natural width instead of being
     stretched into a full-width pill: its 1fr track still holds the row on one
     line, but the pill itself starts at the row's edge. A mixed row therefore
     stays a row, not a banner — no markup change needed when one appears. */
  .nshat-actions-columns .brand-actions > .nshat-badge,
  .nshat-actions-columns .brand-actions > .nshat-chip {
    justify-self: start;
  }

  /* A pill row makes the same promise as an action row: the pills share the
     width as columns instead of wrapping onto a second line. §44's behaviour
     already applies inside cards; this is the opt-in for the rows outside them.
     A pill keeps its capsule — only its content is centred inside the wider
     cell, so an icon and a number never sit off-centre in a stretched pill. */
  .nshat-actions-columns .nshat-pill-row {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    align-items: center;
    gap: clamp(6px, 1.6vw, 10px);
    flex-wrap: nowrap;
  }

  .nshat-actions-columns .nshat-pill-row > .nshat-pill {
    min-inline-size: 0;
    justify-content: center;
    overflow: hidden;
    padding-inline: clamp(4px, 1.6vw, 9px);
    font-size: clamp(0.6875rem, 2.9vw, 0.75rem);
  }

  /* A follow-on row of hints/notes under an action row keeps its own line: the
     grid above must never swallow anything but the buttons themselves. */
  .nshat-actions-columns .brand-actions > .cw-hint {
    grid-column: 1 / -1;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   46. NSHAT SCROLL BAND — a fixed title, then one strip you drag

   §45 gives a row of **buttons** a fixed number of columns. This is the other
   case: an unknown number of **items** (stages · classes · programmes · years).
   Splitting them into columns means either guessing a count or letting them wrap
   into stacked lines, and wrapping is what makes a header grow without warning.

   So the title stays put and the items live in one strip beside it:
   · `.nshat-scroll-label` never scrolls and never leaves its line — it is
     `flex: 0 0 auto`, so the title is the anchor the eye returns to;
   · `.nshat-scroll-track` is one line **always** (`flex-wrap: nowrap`) and
     scrolls sideways (`overflow-x: auto`), so the strip may hold any number of
     items at its natural width — it grows in width, never in height;
   · `flex: 0 0 auto` on every item is what keeps a badge from being squeezed:
     in a scroll container nothing needs to shrink, because there is no second
     line to make room for;
   · `overscroll-behavior-x: contain` stops a sideways flick from chaining into
     the page (or into the browser's back gesture) once the strip is at its end;
   · the track is focusable in the markup (`tabindex="0"`), so the browser's own
     arrow-key scrolling works and a `:focus-visible` ring shows where you are.

   No `scroll-snap`: the gesture asked for is a free drag, and snapping fights a
   finger that wants to stop between two items.

   `data-overflow="true"` (set by the shared script from a real measurement)
   is what turns on the grab cursor and reveals the «drag» hint — a strip that
   fits says nothing and shows nothing. `data-dragging="true"` is the moment of
   the drag itself: grabbing cursor, and no accidental text selection.
   ═══════════════════════════════════════════════════════════════════════════ */

.nshat-scroll {
  display: grid;
  gap: 6px;
  min-inline-size: 0; /* it must be able to shrink inside the page head */
}

.nshat-scroll-row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-inline-size: 0;
}

.nshat-scroll-label {
  flex: 0 0 auto;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--cw-text-2);
  white-space: nowrap;
}

.nshat-scroll-label b {
  font-weight: 800;
  color: var(--cw-text-1);
}

.nshat-scroll-track {
  flex: 1 1 auto;
  min-inline-size: 0;
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 8px;
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
  padding-block-end: 4px;
  border-radius: var(--cw-radius-sm);
}

.nshat-scroll-track:focus-visible {
  outline: 2px solid var(--cw-accent);
  outline-offset: 2px;
}

.nshat-scroll-track::-webkit-scrollbar {
  block-size: 6px;
}

.nshat-scroll-item {
  flex: 0 0 auto;
}

.nshat-scroll[data-overflow='true'] .nshat-scroll-track {
  cursor: grab;
}

.nshat-scroll[data-dragging='true'] .nshat-scroll-track {
  cursor: grabbing;
}

.nshat-scroll[data-dragging='true'] .nshat-scroll-track * {
  user-select: none;
}

.nshat-scroll-hint {
  font-size: 0.75rem;
  color: var(--cw-text-3);
}

.nshat-scroll-hint[hidden] {
  display: none;
}

/* The band inside the control strip: it takes a share of the strip's row on a
   wide screen, and a line of its own on a phone — a strip built to be dragged
   needs the width to have something left to drag. */
.nshat-strip-band {
  flex: 1 1 220px;
  min-inline-size: 0;
}

@media (max-width: 719px) {
  .nshat-strip-band {
    flex-basis: 100%;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   §47 SUBSCRIPTION PLANS — a price the eye lands on, a card that asks for one
   thing, and an action that cannot drift
   ═══════════════════════════════════════════════════════════════════════════

   Built for the subscription screen (`accounts/subscribe3.html`), where the
   plan cards were a card-shaped list: the price sat **below** the title inside a
   data list, so the reader reached the figure after reading sixteen feature
   lines, and the «اشترك الآن» button floated wherever the text ended. On a phone
   that is the whole difference between a page that sells and a page that lists.

   The shape is the standard one for a reason: **figure → what you get → one
   action**, repeated in equal columns so two plans can be compared line by line.

   · `.nshat-plan-grid` — one column on a phone, two from 640px, four from
     1180px. Four is where the eye can compare without sideways scrolling, and
     the equal grid rows are what make the cards the same height.
   · `.nshat-plan` is a **flex column**, so a card with more features never
     pushes its neighbours' content around; `.nshat-plan-cta` takes
     `margin-block-start: auto` and pins every action to the same baseline —
     which is the single most persuasive thing a pricing row can do.
   · `.nshat-plan--featured` marks the plan the page recommends: the accent
     border plus `--cw-accent-ghost` glowing softly behind it. The plan is still
     named in words by its flag, because emphasis may never be the only carrier.
   · `.nshat-plan--selected` is the card the visitor picked; it takes the accent
     border so the choice is visible **next to the form** as well as in it.
   · `.nshat-plan-price` — the figure at 2rem/800 with tabular numerals (so two
     prices align digit over digit), the currency word beside it in a quiet size,
     and the old price struck through **only when an offer exists**.
   · `.nshat-plan-list` — the features. No list markers: each included line
     carries its own drawn tick in the markup (inline SVG on `currentColor`), and
     what is **not** included is a worded line under `.nshat-plan-excluded`
     rather than a row of dimmed crosses.
   · `.nshat-plan-steps` — the three steps from choosing to paying, so the
     visitor knows the length of the path before starting it.

   Tokens only, logical properties only, no literal colour, and one
   `prefers-reduced-motion` guard. */
.nshat-plan-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
  align-items: stretch;
}

/* Every plan is a `.brand-section-card`, and that class carries
   `.brand-section-card + .brand-section-card { margin-top: 20px }` for the page
   rhythm. Inside a grid those cards are siblings, so that rule would push every
   card after the first down by 20px and break the row alignment the grid exists
   to create — the spacing in a grid is the `gap`, never a margin. */
.nshat-plan-grid > .nshat-plan {
  margin-top: 0;
}

@media (min-width: 640px) {
  .nshat-plan-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 1180px) {
  .nshat-plan-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

/* The plan **is** a `.brand-section-card` in the markup, so it inherits the one
   card surface the whole project uses. This rule therefore declares **no**
   background, border, radius or shadow of its own — it only tightens the card's
   roomier padding for a four-up grid and lays the parts out. A plan card that
   drifted from the card beside it is exactly the inconsistency §47 exists to
   remove (the first cut of this section gave the plans their own tinted surface,
   which made them look like a different product next to the form card). */
.nshat-plan {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 22px 18px;
  transition: border-color 0.3s ease, box-shadow 0.3s ease, transform 0.3s ease;
}

.nshat-plan:hover,
.nshat-plan:focus-within {
  border-color: var(--cw-accent-border);
  box-shadow: var(--cw-shadow-sm);
  transform: translateY(-2px);
}

.nshat-plan--featured,
.nshat-plan--selected {
  border-color: var(--cw-accent);
}

.nshat-plan--featured {
  /* the card's own shadow, plus the emphasis ring — never instead of it */
  box-shadow: var(--cw-shadow-sm), 0 0 0 4px var(--cw-accent-ghost);
}

.nshat-plan-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.nshat-plan-name {
  margin: 0;
  font-size: 1.125rem;
  font-weight: 800;
  color: var(--cw-text-1);
}

.nshat-plan-price {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px;
  margin: 0;
}

.nshat-plan-figure {
  font-size: 2rem;
  font-weight: 800;
  line-height: 1.1;
  color: var(--cw-text-1);
  font-variant-numeric: tabular-nums;
}

.nshat-plan-period {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--cw-text-2);
}

/* The cancelled base price. It lives on the «السعر الأساسي … ملغي» line now, so
   it takes that line's own size and keeps only the two marks that say
   “cancelled”: the strikethrough and the muted ink. The word «ملغي» beside it is
   a badge, so the state is never carried by the strike alone. */
.nshat-plan-old {
  color: var(--cw-text-3);
  text-decoration: line-through;
}

.nshat-plan-note {
  margin: 0;
  font-size: 0.8125rem;
  color: var(--cw-text-2);
}

.nshat-plan-sub {
  margin: 0;
  padding-block-end: 10px;
  border-block-end: 1px solid var(--cw-border);
  font-size: 0.8125rem;
  font-weight: 700;
  color: var(--nshat-btn-accent-text);
}

.nshat-plan-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.nshat-plan-list li {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  font-size: 0.8125rem;
  line-height: 1.6;
  color: var(--cw-text-2);
}

.nshat-plan-list svg {
  flex: 0 0 auto;
  margin-block-start: 3px;
  color: var(--cw-accent);
}

.nshat-plan-excluded {
  margin: 0;
  padding-block-start: 10px;
  border-block-start: 1px solid var(--cw-border);
  font-size: 0.8125rem;
  color: var(--cw-text-3);
}

.nshat-plan-excluded b {
  color: var(--cw-text-2);
}

.nshat-plan-cta {
  display: flex;
  margin-block-start: auto;
  padding-block-start: 4px;
}

.nshat-plan-cta .cw-btn {
  inline-size: 100%;
  justify-content: center;
}

.nshat-plan-steps {
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
  margin: 0;
  padding: 0;
  list-style: none;
}

@media (min-width: 720px) {
  .nshat-plan-steps {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

.nshat-plan-steps li {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  background: var(--cw-bg-alt);
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius);
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--cw-text-2);
}

.nshat-plan-steps .nshat-step {
  flex: 0 0 auto;
}

.nshat-plan-summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin: 0;
  padding: 12px 14px;
  background: var(--cw-accent-ghost);
  border: 1px solid var(--cw-accent-border);
  border-radius: var(--cw-radius);
  font-size: 0.875rem;
  font-weight: 700;
  color: var(--cw-text-1);
}

@media (prefers-reduced-motion: reduce) {
  .nshat-plan {
    transition: none;
  }

  .nshat-plan:hover,
  .nshat-plan:focus-within {
    transform: none;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   47. DIALOG FIT — the dialog never outgrows the screen, and its body scrolls
   `.cw-modal` (core §23) declares no height, and `.cw-modal-body` and
   `.cw-modal-footer` are declared in **neither** layer — so a dialog grew with its
   content. The backdrop is `position: fixed` with no scrolling of its own, so a
   long dialog (the school-data wizard's six steps, or a help card carrying a step
   list, two check-list cards and an alert) ran straight past the viewport: no
   scrollbar on the dialog and none on the backdrop, i.e. everything below the fold
   was **unreachable**. The shape below is the one the project's short dialogs
   already had by accident:

   · the dialog is a flex COLUMN capped at 88% of the viewport (760px ceiling, so a
     tall desktop window does not stretch a help card down the whole screen);
   · the header, the wizard's status strip and the footer are `flex: 0 0 auto` — a
     dialog's actions must always be reachable, which is a footer's job;
   · the body takes the remaining height and is the only part that scrolls, with
     `overscroll-behavior: contain` so the page behind a mobile sheet does not move
     when the body reaches its end;
   · `min-block-size: 0` on the body is required — a flex item defaults to
     `min-height: auto` and refuses to shrink below its content, which would hand
     the overflow straight back to the dialog.

   `--wide` is the second half of the same review: the core caps every dialog at
   500px (core §23), so the cards that carry a two-column `.nshat-card-grid` were
   rendering one column at every width. 720px is the width at which that grid really
   is two columns (the dialog's own 2rem padding and the grid's 18px gap taken out).
   It is a modifier, so every dialog that does not carry it stays exactly 500px, and
   the phone sheet resets it to the full width it is designed for.

   The footer's own shape is the one the project's reference page wrote inline
   (`display:flex; gap:12px; border-top:1px solid var(--cw-border); padding-top:16px`)
   — the hairline and the wrapping row are kept, but the row is deliberately **not**
   pushed to `flex-end`: in RTL that would move every live dialog's actions to the far
   left, a change no one asked for and every dialog would feel. The default
   `flex-start` is the side those buttons already sit on.

   The wizard's own shape is the third thing this section had to settle. A bare `h3`
   over a grid of fields made six steps read as one long form: nothing said which step
   the user was in, nothing said what a step asked for, and every step was a different
   height for no reason. The steps are the §31 pills now — the identity's own step
   indicator, reused rather than reinvented — in ONE draggable row with the fixed
   «الخطوة N من 6» label beside it, and each step opens with a tinted head band: its
   number, its title, and one line that says what the step wants. The band is
   deliberately NOT a `.brand-section-card`, because inside a dialog that card's
   surface is `--cw-surface-2` (#FFFFFF) over `--cw-card-bg` (#FFFFFF) — white on
   white, which is the §12 rule stated for tiles.

   `hidden` on a button is the fourth finding, and it is the §24 lesson for a third
   time (§34 forced it for alerts, §36 for the action row): `.cw-btn` declares
   `display: inline-flex` (core §6a), and an author `display` declaration outranks
   the browser's own `[hidden] { display: none }`. The wizard's footer hides four of
   its five buttons with that attribute — «حفظ» at every step, and «التالي» ·
   «تخطي» · «حفظ والإكمال لاحقاً» at the last one — so all five were on screen at
   once. Forced once, here, so the attribute means what it says.
   ═══════════════════════════════════════════════════════════════════════════ */
.cw-modal {
  display: flex;
  flex-direction: column;
  max-block-size: min(88vh, 760px);
  overflow: hidden;
}

.cw-modal > .cw-modal-header,
.cw-modal > .cw-modal-footer,
.cw-modal > .nshat-wizard-status {
  flex: 0 0 auto;
}

.cw-modal-body {
  flex: 1 1 auto;
  min-block-size: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

.cw-modal-footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-block-start: 18px;
  padding-block-start: 16px;
  border-block-start: 1px solid var(--cw-border);
}

.cw-modal--wide {
  max-width: 720px;
}

/* ── The wizard's header line ─────────────────────────────────────────────────
   Its own two rows stay put while the steps scroll beneath them (help_info's
   school-data dialog): the alert slot, then the step indicator. */
.nshat-wizard-status {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* The indicator: a fixed label that always says where the user is, then the §31
   pills in ONE row that can be dragged. Six pills with Arabic labels have no width
   to wrap into inside a dialog, and a wrapping row would eat the height the steps
   themselves need — the §46 lesson, applied to the indicator. */
.nshat-wizard-steps {
  display: flex;
  align-items: center;
  gap: 10px;
  min-inline-size: 0;
}

.nshat-wizard-steps .nshat-steps {
  flex: 1 1 auto;
  min-inline-size: 0;
  flex-wrap: nowrap;
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
  padding-block-end: 4px;
}

.nshat-wizard-steps .nshat-steps::-webkit-scrollbar {
  block-size: 6px;
}

.nshat-wizard-steps .nshat-steps:focus-visible {
  outline: 2px solid var(--cw-accent);
  outline-offset: 2px;
}

.nshat-wizard-steps .nshat-steps-item {
  flex: 0 0 auto;
}

/* ── A step's own head band ───────────────────────────────────────────────────
   The number, the title and one line saying what the step asks for. `--cw-bg-alt`
   is the identity's own quiet surface: it separates the band from the dialog's
   background in both themes without introducing a card inside a card. */
.nshat-step-head {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 14px;
  margin-block-end: 16px;
  background: var(--cw-bg-alt);
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius);
}

.nshat-step-disc {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 34px;
  block-size: 34px;
  border-radius: 50%;
  background: var(--cw-accent-ghost);
  border: 1px solid var(--cw-accent-border);
  color: var(--nshat-btn-accent-text);
  font-size: 0.9375rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}

.nshat-step-head-text {
  min-inline-size: 0;
}

.nshat-step-head-text .brand-section-sub {
  margin-block-start: 4px;
}

.cw-btn[hidden] {
  display: none !important;
}

@media (max-width: 640px) {
  /* The phone sheet keeps the full width it was designed for, and may take a
     little more of the viewport than a centred dialog needs. */
  .cw-modal {
    max-block-size: min(92vh, 760px);
  }

  .cw-modal--wide {
    max-width: none;
  }

  /* A wizard's footer is two main actions and two auxiliaries. A plain wrap gave
     three ragged rows; two columns give two balanced ones — the auxiliaries on the
     first row, the main pair beneath them. `grid-row` and not `order`, because what
     changes is placement, not the source order: a keyboard user still meets the
     buttons in the order the markup carries, and `hidden` buttons keep taking no
     slot — so the last step shows its pair alone. */
  .cw-modal-footer.nshat-wizard-actions {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .nshat-wizard-actions > .cw-btn {
    inline-size: 100%;
    justify-content: center;
  }

  .nshat-wizard-actions > .cw-btn-ghost {
    grid-row: 1;
  }

  .nshat-wizard-actions > .cw-btn-secondary,
  .nshat-wizard-actions > .cw-btn-primary {
    grid-row: 2;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   48. WIDE TABLES ON SMALL SCREENS — «الجدول الممتد»
   The wrapper already scrolls (§6 gives `.brand-table-wrap` an `overflow: auto`),
   but the table inside it was told `width: 100%` and nothing else — so on a phone
   a ten-column data table was **squeezed into 360px instead of scrolling**, and
   every column became too narrow to read. This section gives the table a readable
   FLOOR and lets the wrapper do the scrolling it was built for.

   Two decisions worth writing down:
   • The floor is a `min-inline-size`, not a fixed width. Above the breakpoint
     nothing changes at all, and a table with two or three columns stays as it is
     (its own content already exceeds the floor, so no scrollbar appears).
   • The first column is pinned: `position: sticky` on the first cell of every
     row keeps the row's own name — the class, the teacher, the lesson number —
     in view while the remaining columns slide underneath it. The pinned cell must
     carry an **opaque** background (the wrapper's own surface, and the zebra's
     tint on even rows, and the hover tint over both), otherwise the columns
     scrolling beneath it would show through it.

   No per-page CSS and no page script: the markup is unchanged, so every page that
   already uses `.brand-table-wrap` gets this at once.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 900px) {
  .brand-table-wrap {
    /* a sideways flick must not chain into the page or the browser's back gesture */
    overscroll-behavior-x: contain;
  }

  .brand-table-wrap > table {
    min-inline-size: 48rem;
  }

  .brand-table-wrap > table tbody th:first-child,
  .brand-table-wrap > table tbody td:first-child {
    position: sticky;
    inset-inline-start: 0;
    z-index: 1;
    background: var(--cw-surface-2);
    border-inline-end: 1px solid var(--cw-border);
  }

  /* the header's first cell must stay above the pinned body cell at the crossing */
  .brand-table-wrap > table thead th:first-child {
    z-index: 3;
  }

  /* the zebra tint is re-asserted over the pinned cell (it would otherwise be flat) */
  .brand-table-wrap > table tbody tr:nth-child(even) > td:first-child,
  .brand-table-wrap > table tbody tr:nth-child(even) > th:first-child {
    background: var(--cw-bg-alt);
  }

  /* and the row-hover tint last, so the pinned cell always matches its own row */
  .brand-table-wrap > table tbody tr:hover > td:first-child,
  .brand-table-wrap > table tbody tr:hover > th:first-child {
    background: var(--cw-accent-ghost);
  }
}

/* ── Column floors: a value, a text, and a CONTROL ────────────────────────────
   `.is-narrow` (this file, §6) takes a column down to its own content — right for
   a number, and right for a column of badges. It is wrong for the two other kinds
   of column a data table carries, and both of those were getting it in the
   import wizard's decision rows:

   • **A text column** needs room for a sentence in Arabic; the first two columns
     of that table hold a file label and a description, and when the two control
     columns grew they were the ones that paid for it. → `.is-text` gives them a
     floor of their own.
   • **A control column** needs more than a value: a `<select>` cannot wrap its
     text, so too little width *clips* the option the user is choosing. → `.is-wide`
     gives it a floor too — but a measured one: 12rem covers every label this
     project puts in those lists (a class name, a teacher's name, a subject with
     its نصاب), and the longest timetable labels («جدول … — شعبة … (25 خلية)») can
     still clip at the tail. That is a limit of the control, not of the column, so
     it is recorded rather than paid for with the text columns' width: if a full
     label must be readable, the choice belongs in a picker or a dialog, not in a
     table cell.

   All three are `min-inline-size` and never a width: on a wide screen the surplus
   still goes to whichever column can use it, and a narrow table is never forced to
   scroll just to honour a number nobody needs. */
.brand-table-wrap > table .is-text {
  min-inline-size: 13rem;
}

.brand-table-wrap > table .is-wide {
  min-inline-size: 12rem;
}

/* ── A control inside a TABLE ROW is not a control inside a FORM ─────────────
   `.cw-select` is built for a form: 0.75rem of vertical padding and a 1rem font,
   which is right under a `.cw-label` in a column of fields — and wrong in a data
   row, where it made every row of the decision table about twice as tall as the
   text beside it. Inside a table the row's own height must stay the row's, so the
   control is tightened to a single line: same border, same radius, same focus
   ring, only less air. The visible label that used to sit above each control is
   gone with it — the column header already names it, and repeating it in every
   row was the other half of the height; the accessible name is carried by the
   control's own `aria-label` and the header's `scope="col"`. */
.brand-table-wrap > table .cw-select {
  padding-block: 0.35rem;
  padding-inline: 0.6rem;
  font-size: 0.875rem;
}

/* ═══════════════════════════════════════════════════════════════════════════
   49. A CHECKBOX INSIDE A DATA ROW — the identity's own box, at the size it declared
   The study-plan wizard's choice column is `td.is-narrow` (this file, §6): a
   **one-percent** column — and on a phone it is also the table's PINNED first
   column (§48). A **bare** native checkbox in that cell is painted by the user
   agent: its size, its tint, its alignment and its `disabled` appearance are the
   browser's own, they differ between engines and versions, and against the pinned
   cell's own surface a small greyed control can read as an almost empty square.
   Nothing in either layer styles a bare checkbox — the identity owns exactly one
   checkbox, `.cw-checkbox` (core §19): an inline-flex wrapper whose input is
   `1.125rem` square with `accent-color: var(--cw-accent)`. This section only makes
   that box survive a data row:

   · `font-size: inherit` first — the core's component sets `1rem`, which is right
     under a `.cw-label` and wrong in a data row: §48's own rule is that a control
     inside a table must not grow the row, and a 1rem line box would.
   · the wrapper centres itself in the cell. A lone inline-flex already inherits the
     cell's `text-align: center`, so this changes nothing today and holds if a page
     ever puts the wrapper in a `flex`/`grid` cell instead.
   · the input is `flex: 0 0 auto` with explicit minimums, so a cell that resolves
     to its one-percent hint can never squash the control the way an engine may
     squash a shrinkable flex item — the box keeps the size the layer gave it.

   And the fallback stays: `appearance` is never set to `none` on a checkbox in
   either layer, so a browser without `accent-color` still draws its own control —
   smaller and untinted, but visible, clickable and unmistakable. */
.brand-table-wrap > table .cw-checkbox {
  font-size: inherit;
  justify-content: center;
}

.brand-table-wrap > table .cw-checkbox input[type="checkbox"] {
  flex: 0 0 auto;
  min-inline-size: 1.125rem;
  min-block-size: 1.125rem;
}

/* ═══════════════════════════════════════════════════════════════════════════
   50. A CONTROL COLUMN IS NEVER THE PINNED ONE — the row's NAME is
   §48 pins the first cell of every row so the row's own name stays in view while
   the other columns slide underneath. That rule reads the column by POSITION,
   and the study-plan wizard's two tables put a **checkbox** there instead of a
   name — so a form control ended up inside the one cell the identity gives
   `position: sticky`, a `z-index` and an opaque background. That is the worst
   place for a control: it is a replaced element in a promoted compositing layer,
   and while the wrapper is being scrolled the cell's own background paints over
   it in some engines (reported on the wizard's subject boxes: «تختفي أحياناً عند
   التمرير … العمود الذي تقع فيه مربعات الاختيار ثابت»). Neither a bigger box nor
   a stronger colour can fix a paint order — the control has to leave the pinned
   slot.

   So the page marks its choice column `.is-choice` (the class is opt-in, so every
   other table in the project keeps §48 exactly as it was), and this section
   settles the two halves of the same decision at the one breakpoint §48 uses:

   · the choice cell is released from the pin — `position: static`, no `z-index`,
     no edge; it keeps the row's own zebra and hover tints from the two rules that
     were always doing that job (the zebra at line 3430 in this file), which this
     section never rewrites;
   · the cell RIGHT AFTER it — the row's name, which is what §48 was written for —
     takes the pin with §48's own declarations, including the opaque surface, the
     zebra re-assertion and the hover tint, so the pinned column still reads as
     its own row while the rest slides under it.

   Above 900px nothing here applies: the wrapper does not scroll sideways, §48 is
   quiet, and both cells are plain table cells. */
@media (max-width: 900px) {
  /* 1) the choice cell is never the pinned one */
  .brand-table-wrap > table tbody td.is-choice:first-child,
  .brand-table-wrap > table tbody th.is-choice:first-child {
    position: static;
    z-index: auto;
    border-inline-end: 0;
  }

  .brand-table-wrap > table thead th.is-choice:first-child {
    z-index: auto;
  }

  /* 2) the row's name takes the pin, with §48's own shape */
  .brand-table-wrap > table tbody td.is-choice:first-child + td {
    position: sticky;
    inset-inline-start: 0;
    z-index: 1;
    background: var(--cw-surface-2);
    border-inline-end: 1px solid var(--cw-border);
  }

  .brand-table-wrap > table thead th.is-choice:first-child + th {
    z-index: 3;
  }

  .brand-table-wrap > table tbody tr:nth-child(even) > td.is-choice:first-child + td {
    background: var(--cw-bg-alt);
  }

  .brand-table-wrap > table tbody tr:hover > td.is-choice:first-child + td {
    background: var(--cw-accent-ghost);
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   51. CERTIFICATE CHOICE BOXES (شهادات الشكر — الاختيار بالصورة)
   Each certificate design is chosen by its own thumbnail artwork, not by a name
   or a number: the box IS the picture. The native radio stays in the DOM (the
   form contract and the keyboard), visually hidden with the classic clip
   technique, and the chosen box is marked with the accent outline.
   ═══════════════════════════════════════════════════════════════════════════ */
.nshat-shahadh-choice { display: block; padding: 0; cursor: pointer; }

.nshat-shahadh-choice > input[type="radio"] {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.nshat-shahadh-choice .nshat-media-frame { display: block; }

.nshat-shahadh-choice .nshat-media-frame img {
  display: block;
  width: 100%;
  height: auto;
}

.nshat-shahadh-choice > input[type="radio"]:checked + .nshat-media-frame {
  outline: 3px solid var(--cw-accent);
  outline-offset: 3px;
  border-radius: var(--cw-radius-md, 8px);
}

.nshat-shahadh-choice > input[type="radio"]:focus-visible + .nshat-media-frame {
  outline: 3px dashed var(--cw-accent);
  outline-offset: 3px;
}

/* ═══════════════════════════════════════════════════════════════════════════
   52. REGULATION CARDS (ضوابط تنظيم المسابقات) — a paragraph is not a value
   The six regulations arrived inside `.nshat-row`, the layer's label/value
   pair: a `space-between` flex line whose value carries `text-align: end`,
   which in RTL is the LEFT. Right for a short value — a name, a number, a
   date — and wrong for a paragraph, whose long text then hugged the left edge
   beside a vertically centred label and read as a left-to-right block. A
   stacked list fixed the direction but flattened the six regulations into one
   grey column, with nothing for the eye to land on.

   So they are CARDS: a numbered grid, one card per regulation. The number is
   an accent capsule the eye can count by, the word «الضابط» names it (the
   number is never the only carrier), and the text is a reading block aligned
   to the START of the line — the right in Arabic — at a comfortable measure
   and line-height. One column on a phone, auto-fit columns above it, so a long
   paragraph is never read through a horizontal scroll (§48 stays for tables).

   Colour: the accent family only (--cw-accent-ghost · --cw-accent-border ·
   --nshat-btn-accent-text) — the §12/§22/§24 decision. No new token, no literal
   colour. RTL: logical properties, and no `text-align: end` on a paragraph.
   ═══════════════════════════════════════════════════════════════════════════ */
.nshat-rules {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 12px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.nshat-rule {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 16px;
  border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius);
  background: var(--cw-surface-2);
  transition: border-color var(--cw-silk-dur) var(--cw-silk),
              box-shadow var(--cw-silk-dur) var(--cw-silk);
}

.nshat-rule:hover,
.nshat-rule:focus-within {
  border-color: var(--cw-accent-border);
  box-shadow: inset 0 0 0 1px var(--cw-accent-ghost);
}

.nshat-rule-head {
  display: flex;
  align-items: center;
  gap: 8px;
}

.nshat-rule-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 28px;
  height: 28px;
  padding: 0 9px;
  border: 1px solid var(--cw-accent-border);
  border-radius: var(--cw-radius-full);
  background: var(--cw-accent-ghost);
  color: var(--nshat-btn-accent-text);
  font-size: 0.875rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.nshat-rule-label {
  margin: 0;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--cw-text-3);
}

.nshat-rule-text {
  margin: 0;
  font-size: 0.9375rem;
  font-weight: 400;
  line-height: 1.95;
  text-align: start;
  color: var(--cw-text-1);
  overflow-wrap: anywhere;
}
