/* =====================================================================
   Paras — LMS portal
   Matches the Figma LMS pages: 256px sidebar, 72px topbar, 1184px canvas.
   ===================================================================== */

body.paras-portal { background: var(--canvas); }

/* The portal is a working tool, not a brochure, so it runs on one family.
   A serif in the sidebar, the top bar and every stat tile is decoration on
   a screen somebody stares at for an hour marking a register — and mixing
   two faces in dense interface furniture is most of what reads as "casual"
   rather than professional. Hierarchy here is size, weight and colour.

   Re-pointing the token rather than rewriting every rule means the portal
   cannot drift back: anything that asks for the display face inside the
   portal — including modals and the call panel, which mount on <body> —
   gets the interface face. The public site keeps its serif.

   Figures go tabular in tables and nowhere else. Marks, percentages, fees
   and attendance sit in columns all over this product, and proportional
   digits make a column look ragged however well the rest is set — but
   tabular figures also widen the minus sign to a digit's width, which is
   what you want in a ledger column and wrong in a lone "-14" on a tile. */
body.paras-portal { --font-display: var(--font-body); }
body.paras-portal .table { font-variant-numeric: tabular-nums; }

/* The portal is an app viewport, not a document to be panned. One wide
   thing — a long unbroken link, a stray un-wrapped table, a control with a
   fixed min-width on a narrow phone — must never make the whole page scroll
   sideways or load zoomed out to fit it on the screen.

   overflow-x: clip, not hidden: `hidden` turns the body into its own scroll
   container, which breaks the sticky top bar and the fixed sidebar drawer;
   `clip` stops the sideways overflow without any of that. Fixed overlays
   (the sidebar, modals, the call panel) are positioned to the viewport, not
   the body, so they are not clipped by this. Long words break rather than
   push the width out — overflow-wrap inherits, so this covers message
   bubbles, names, cells and headings in one line. */
body.paras-portal { overflow-x: clip; overflow-wrap: break-word; }
/* A preformatted or code block scrolls inside its own box instead of
   widening the page. Wide tables already scroll via .table-wrap. */
body.paras-portal pre,
body.paras-portal code { max-width: 100%; overflow-x: auto; }

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

/* --- Collapsing the sidebar (desktop) ----------------------------------
   27 nav items is a lot of permanent furniture on a screen where the work
   is to the right of it. The column slides shut and gives its 256px back.

   Scoped above the drawer breakpoint on purpose: below it the sidebar is
   already an overlay driven by .nav-open, and two transforms fighting over
   the same element is how a drawer ends up stuck half open. The animation
   is on the grid column and the sidebar together — the column carries the
   layout, the transform carries the motion, so the content slides out of
   view rather than being squashed to nothing on the way. */
@media (min-width: 901px) {
  /* .nav-ready is added a frame after the remembered state is applied, so a
     browser that left it shut opens shut instead of animating closed. */
  .portal.nav-ready { transition: grid-template-columns .24s var(--ease); }
  .portal.nav-ready > .p-sidebar {
    /* visibility rides the same duration on purpose. Going shut it stays
       visible for the whole slide and only then goes; coming back it
       appears at once. Flipping it immediately would make the sidebar
       vanish and the column animate over empty space. */
    transition: transform .24s var(--ease), visibility .24s var(--ease);
  }
  .portal.nav-collapsed { grid-template-columns: 0 minmax(0, 1fr); }
  .portal.nav-collapsed > .p-sidebar {
    transform: translateX(-100%);
    /* Nothing inside a shut sidebar should be reachable by tab, or read out
       by a screen reader as though it were on screen. */
    visibility: hidden;
  }
  /* Same specificity as the rules above, or this loses the cascade and the
     preference is silently ignored. */
  @media (prefers-reduced-motion: reduce) {
    .portal.nav-ready,
    .portal.nav-ready > .p-sidebar { transition: none; }
  }
}

/* --- Sidebar ---------------------------------------------------------- */
/* A clean white sidebar: the institute's wordmark top-left, dark nav text,
   and the brand red marking where you are. */
.p-sidebar {
  background: #fff; color: var(--ink-900);
  border-right: 1px solid var(--line);
  display: flex; flex-direction: column;
  position: sticky; top: 0; height: 100vh; overflow-y: auto;
  /* No visible scrollbar: it ran right under the collapse arrow on the
     sidebar's edge. The column still scrolls by wheel, trackpad and touch. */
  scrollbar-width: none; -ms-overflow-style: none;
}
.p-sidebar::-webkit-scrollbar { width: 0; height: 0; display: none; }
.p-sidebar__brand { display: flex; align-items: center; gap: var(--s3); padding: var(--s6) var(--s5) var(--s4); text-decoration: none; color: var(--ink-900); }
.p-sidebar__mark {
  width: 38px; height: 38px; border-radius: 10px; flex: none;
  background: var(--red-600); color: #fff; display: grid; place-items: center;
  font-family: var(--font-display); font-size: 19px; font-weight: 600;
}
/* The "PARAS CA" wordmark — PARAS in ink, CA in a red badge. */
.p-sidebar__wordmark { font-family: var(--font-display); font-size: 22px; font-weight: 800; letter-spacing: .4px; color: var(--ink-900); display: inline-flex; align-items: center; line-height: 1; }
.p-sidebar__wordmark b { background: var(--red-600); color: #fff; padding: 1px 6px; border-radius: 6px; margin-left: 3px; font-weight: 800; }
.p-sidebar__name { font-family: var(--font-display); font-size: 16px; font-weight: 600; line-height: 1.15; }
.p-sidebar__role { font-size: 10px; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-400); font-weight: 800; }

.p-nav { padding: var(--s3) var(--s3) var(--s6); flex: 1; }
.p-nav__group { margin-top: var(--s5); }
.p-nav__label { font-size: 10px; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-400); font-weight: 700; padding: 0 var(--s4) var(--s2); }
.p-nav a {
  display: flex; align-items: center; gap: var(--s3);
  padding: 10px var(--s4); border-radius: var(--r-md);
  color: var(--ink-700); text-decoration: none; font-size: var(--label-m); font-weight: 600;
  transition: background .15s var(--ease), color .15s var(--ease);
}
.p-nav a:hover { background: var(--canvas); color: var(--ink-900); }
.p-nav a.is-active { background: var(--red-600); color: #fff; }
.p-nav a.is-active .icon { color: #fff; }
.p-nav a.is-locked { opacity: .45; cursor: not-allowed; }
.p-nav a .icon { flex: none; }
.p-nav__phase {
  margin-left: auto; font-size: 9px; font-weight: 700; letter-spacing: .08em;
  background: var(--canvas); color: var(--ink-500); padding: 2px 6px; border-radius: var(--r-pill);
}
.p-nav a.is-active .p-nav__phase { background: rgba(255,255,255,.22); color: #fff; }

.p-sidebar__user { border-top: 1px solid var(--line); padding: var(--s4) var(--s5); display: flex; gap: var(--s3); align-items: center; }
.p-sidebar__user .avatar { flex: none; }
.p-sidebar__user-name { font-size: var(--label-s); font-weight: 700; color: var(--ink-900); }
.p-sidebar__user-sub { font-size: 11px; color: var(--ink-500); }

/* --- Topbar ------------------------------------------------------------ */
.p-main { display: flex; flex-direction: column; min-width: 0; }
.p-topbar {
  height: var(--topbar-h); background: var(--white); border-bottom: 1px solid var(--line);
  display: flex; align-items: center; gap: var(--s5); padding-inline: var(--s7);
  position: sticky; top: 0; z-index: 20;
}
/* An h1 carries a browser default margin and font-size; both have to go or
   the top bar grows and the title stops lining up with the date under it. */
.p-topbar__title {
  font-family: var(--font-display); font-size: 21px; font-weight: 600;
  margin: 0; line-height: 1.2;
}
.p-topbar__date { font-size: var(--label-s); color: var(--ink-500); }
.p-topbar__spacer { margin-left: auto; }
.p-search { position: relative; }
.p-search input { padding-left: 42px; width: 280px; }
.p-search .icon { position: absolute; left: 13px; top: 50%; transform: translateY(-50%); color: var(--ink-300); }
.p-iconbtn {
  position: relative; background: none; border: 1px solid var(--line);
  width: 40px; height: 40px; border-radius: var(--r-md); display: grid; place-items: center;
  color: var(--ink-700); cursor: pointer;
}
.p-iconbtn:hover { background: var(--blue-50); border-color: var(--blue-100); color: var(--blue-700); }
.p-iconbtn__dot {
  position: absolute; top: 6px; right: 6px; min-width: 16px; height: 16px; padding: 0 4px;
  background: var(--red-600); color: #fff; border-radius: var(--r-pill);
  font-size: 10px; font-weight: 700; display: grid; place-items: center;
}

/* --- Canvas -------------------------------------------------------------- */
.p-canvas { padding: var(--s7); max-width: calc(var(--sidebar-w) + 1184px); width: 100%; }
.p-head { margin-bottom: var(--s6); }
.p-head__title { font-family: var(--font-display); font-size: var(--h3); line-height: var(--h3-lh); font-weight: 600; }
.p-head__sub { color: var(--ink-500); font-size: var(--body-s); margin-top: 6px; }
.p-head__row { display: flex; gap: var(--s4); align-items: flex-end; justify-content: space-between; flex-wrap: wrap; }

/* --- Blocks ---------------------------------------------------------------- */
.p-card { background: var(--white); border: 1px solid var(--line); border-radius: var(--r-lg); }
.p-card__head {
  display: flex; align-items: center; gap: var(--s4); justify-content: space-between;
  padding: var(--s5) var(--s6); border-bottom: 1px solid var(--line);
}
.p-card__title { font-size: var(--h5); font-weight: 700; }
.p-card__body { padding: var(--s6); }
.p-card__body--flush { padding: 0; }

.p-stats { display: grid; grid-template-columns: repeat(4, minmax(0,1fr)); gap: var(--s5); margin-bottom: var(--s6); }
.p-stat { background: var(--white); border: 1px solid var(--line); border-radius: var(--r-lg); padding: var(--s5) var(--s6); }
.p-stat__label { font-size: var(--label-s); color: var(--ink-500); font-weight: 600; margin-bottom: var(--s2); }
.p-stat__value { font-family: var(--font-display); font-size: var(--stat-m); line-height: var(--stat-m-lh); font-weight: 600; color: var(--ink-900); }
.p-stat__delta { font-size: var(--label-s); font-weight: 600; margin-top: 4px; }
.p-stat__delta--up { color: var(--green-600); }
.p-stat__delta--down { color: var(--red-600); }

/* DB-03: a figure you can open. Reads as a card, behaves as a link. */
a.p-stat--link { display: block; text-decoration: none; color: inherit; transition: border-color .12s, box-shadow .12s, transform .12s; }
a.p-stat--link:hover { border-color: var(--gold-400); box-shadow: 0 2px 10px rgba(22, 32, 46, .07); transform: translateY(-1px); }
a.p-stat--link:focus-visible { outline: 2px solid var(--gold-400); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { a.p-stat--link:hover { transform: none; } }
@media (max-width: 1180px) { .p-stats { grid-template-columns: repeat(2, minmax(0,1fr)); } }

/* A rupee figure with lakhs in it — ₹5,84,000.00 — is a lot wider than the
   counts these tiles were sized for, and at two-up on a phone it ran past
   the card. Below 560px the tiles go single file and the figure steps down
   a size; it still reads as the headline number, and it fits. */
@media (max-width: 560px) {
  .p-stats { grid-template-columns: minmax(0, 1fr); gap: var(--s4); }
  .p-stat { padding: var(--s4) var(--s5); }
  .p-stat__value { font-size: var(--h3); line-height: var(--h3-lh); }
}
/* Whatever the width, a figure never spills out of its own tile. */
/* A figure is a single token: it wraps between words (a space) but never
   between digits. overflow-wrap:anywhere was breaking "₹12,000" mid-number
   in the narrow tiles of the fee-account modal. */
.p-stat__value { overflow-wrap: normal; word-break: keep-all; }

.p-grid { display: grid; gap: var(--s6); }
.p-grid--2 { grid-template-columns: repeat(2, minmax(0,1fr)); }
.p-grid--3 { grid-template-columns: repeat(3, minmax(0,1fr)); }
.p-grid--sidebar { grid-template-columns: minmax(0,1fr) 340px; }
@media (max-width: 1180px) { .p-grid--2, .p-grid--3, .p-grid--sidebar { grid-template-columns: minmax(0,1fr); } }

/* --- Toolbar (filters above a table) ---------------------------------------- */
.p-toolbar { display: flex; gap: var(--s3); align-items: center; flex-wrap: wrap; padding: var(--s4) var(--s6); border-bottom: 1px solid var(--line); }
/* Inline enrolment-status picker on a student's journey — compact, and
   given a left bar in the status colour so a column of them reads at a
   glance (green active, amber on-hold, blue transferred, red dropped). */
.p-enrol-status { padding: 5px 8px; font-size: var(--label-s); min-width: 132px; border-left-width: 3px; }
.p-enrol-status.is-active      { border-left-color: var(--green-600); }
.p-enrol-status.is-on_hold     { border-left-color: var(--gold-400); }
.p-enrol-status.is-transferred { border-left-color: var(--blue-600); }
.p-enrol-status.is-dropped     { border-left-color: var(--red-600); }
.p-enrol-status.is-completed   { border-left-color: var(--ink-500); }

.p-toolbar .input, .p-toolbar .select { width: auto; min-width: 180px; padding-block: 9px; }
.p-toolbar__spacer { margin-left: auto; }
/* On a phone the 180px minimum is wider than the screen gutter allows, so a
   single filter could poke past the edge. Let them fill the row instead. */
@media (max-width: 560px) {
  .p-toolbar { gap: var(--s3); }
  .p-toolbar .input, .p-toolbar .select { min-width: 0; width: 100%; flex: 1 1 100%; }
  .p-toolbar__spacer { display: none; }
}

/* --- Tabs -------------------------------------------------------------------- */
.p-tabs { display: flex; gap: var(--s5); border-bottom: 1px solid var(--line); margin-bottom: var(--s6); overflow-x: auto; }
.p-tab {
  background: none; border: 0; cursor: pointer; white-space: nowrap;
  padding: var(--s3) 0 var(--s4); font-family: var(--font-body);
  font-size: var(--label-m); font-weight: 600; color: var(--ink-500);
  border-bottom: 2px solid transparent;
}
.p-tab.is-active { color: var(--blue-700); border-bottom-color: var(--blue-700); }
.p-tab__count { background: var(--canvas); border-radius: var(--r-pill); padding: 1px 7px; font-size: 11px; margin-left: 6px; }

/* --- Avatar ------------------------------------------------------------------- */
.avatar {
  width: 36px; height: 36px; border-radius: 50%; overflow: hidden;
  background: var(--blue-100); color: var(--blue-700);
  display: grid; place-items: center; font-size: var(--label-s); font-weight: 700;
}
.avatar--lg { width: 64px; height: 64px; font-size: 20px; }
.avatar--sm { width: 28px; height: 28px; font-size: 11px; }
.avatar img { width: 100%; height: 100%; object-fit: cover; }

/* --- Key/value list ------------------------------------------------------------ */

/* --- Progress bar ---------------------------------------------------------------- */
/* 6px rather than 8, on a lighter track. At 8px against --line the bar
   read as a UI control in its own right; the app design treats it as a
   measurement printed under a number, which is what it is. */
.p-bar { height: 6px; border-radius: var(--r-pill); background: var(--canvas); overflow: hidden; }
.p-bar__fill { height: 100%; background: var(--blue-700); border-radius: inherit; transition: width .3s var(--ease); }
.p-bar__fill--navy { background: var(--ink-900); }
@media (prefers-reduced-motion: reduce) { .p-bar__fill { transition: none; } }
.p-bar__fill--green { background: var(--green-600); }
.p-bar__fill--amber { background: var(--gold-400); }
.p-bar__fill--red { background: var(--red-600); }

/* --- Empty / loading / error ------------------------------------------------------ */
.p-empty { text-align: center; padding: var(--s10) var(--s6); color: var(--ink-500); }
.p-empty .icon { color: var(--ink-300); margin: 0 auto var(--s4); }
.p-empty__title { font-size: var(--h5); font-weight: 700; color: var(--ink-900); margin-bottom: var(--s2); }

.p-skeleton { background: linear-gradient(90deg, var(--canvas) 25%, #eef2f8 37%, var(--canvas) 63%); background-size: 400% 100%; animation: p-shimmer 1.4s ease infinite; border-radius: var(--r-sm); }
@keyframes p-shimmer { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }
.p-skeleton--row { height: 44px; margin-bottom: var(--s2); }
.p-skeleton--card { height: 120px; }

/* --- Toast ----------------------------------------------------------------------- */
.p-toasts { position: fixed; right: var(--s6); bottom: var(--s6); z-index: 200; display: flex; flex-direction: column; gap: var(--s3); }
.p-toast {
  background: var(--ink-900); color: #fff; border-radius: var(--r-md);
  padding: var(--s4) var(--s5); box-shadow: var(--shadow-lg);
  font-size: var(--body-s); max-width: 380px;
  animation: p-toast-in .22s var(--ease);
}
.p-toast--ok { background: var(--green-600); }
.p-toast--error { background: var(--red-600); }
@keyframes p-toast-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

/* --- Modal ------------------------------------------------------------------------ */
.p-modal { position: fixed; inset: 0; z-index: 150; display: grid; place-items: center; padding: var(--s5); }
.p-modal__scrim { position: absolute; inset: 0; background: rgba(22,35,58,.5); }
.p-modal__panel {
  position: relative; background: var(--white); border-radius: var(--r-lg);
  width: 100%; max-width: 560px; max-height: 88vh; overflow-y: auto; box-shadow: var(--shadow-lg);
}
.p-modal__head { padding: var(--s6); border-bottom: 1px solid var(--line); display: flex; justify-content: space-between; align-items: center; gap: var(--s4); }
.p-modal__body { padding: var(--s6); }
/* flex-wrap matters more here than anywhere else in the product. The
   footer is nowrap + justify-content:flex-end, so when three buttons do
   not fit, the overflow goes LEFT: Cancel ended up at left:-229px on a
   390px screen — off the panel, off the page, and not scrollable, so the
   only way out of the dialog was the X or the Escape key. */
.p-modal__foot {
  padding: var(--s5) var(--s6); border-top: 1px solid var(--line);
  display: flex; gap: var(--s3); justify-content: flex-end; flex-wrap: wrap;
}

/* --- Messaging --------------------------------------------------------------------- */
/* The messages screen fills the window instead of flowing down it.
   It used to be sized as calc(100vh - topbar - canvas padding), which forgot
   the page heading above it — so the panel was about 84px taller than the room
   left for it and the bottom 84px, which is exactly the composer, sat below
   the fold with .p-msg's own overflow:hidden making it unreachable. Letting
   the canvas hand it the space that is actually left cannot drift that way. */
/* A WhatsApp-style two pane by default — a chats column and the conversation.
   The "Who can see this" aside is a slide-in third column, opened from the
   info button in the thread header (.show-aside), not a permanent fixture. */
.p-msg { display: grid; grid-template-columns: 350px minmax(0,1fr); border: 1px solid var(--line); border-radius: var(--r-lg); overflow: hidden; background: var(--white); min-height: 420px; }
.p-msg.show-aside { grid-template-columns: 350px minmax(0,1fr) 300px; }

/* At every width, not just on a desktop. Any calc() here has to guess at the
   height of the page heading above the panel, and guessing is what put the
   composer off the bottom of the screen in the first place — on a phone it
   ended up at 8000px. Letting the canvas hand over the space that is actually
   left is the only version that cannot be wrong. dvh so a phone browser's
   collapsing toolbar does not cover the message box. */
.p-main:has(.p-msg) { height: 100dvh; overflow: hidden; }
.p-canvas:has(.p-msg) { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.p-canvas:has(.p-msg) > .p-msg { flex: 1; min-height: 0; }

/* Messages is the one screen whose page heading is pure duplication: the top
   bar already says "Messages", so the second one cost a heading's worth of
   the conversation and told nobody anything. Hidden at every width, not just
   on a phone.

   The panel also drops the canvas's reading-width cap. 1184px is right for a
   page of prose or a form; a chat with a list, a thread and an aside wants
   every pixel, and on a wide monitor the cap left the panel floating in dead
   space. Padding comes down to match. */
.p-canvas:has(.p-msg) { max-width: none; padding: var(--s5); }
.p-canvas:has(.p-msg) > .p-head { display: none; }
/* The chats column is a fixed header (title, search, filters) over a
   scrolling list of conversations. */
.p-msg__list { border-right: 1px solid var(--line); min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
.p-msg__rows { flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; }
/* A conversation, led by a face. Flex rather than block so the avatar,
   the two lines of text and the unread dot sit on one baseline grid. */
.p-msg__item {
  display: flex; align-items: center; gap: var(--s3);
  width: 100%; text-align: left; background: none; border: 0;
  border-bottom: 1px solid var(--line); padding: var(--s3) var(--s5); cursor: pointer;
}
.p-msg__item-body { flex: 1; min-width: 0; }
/* WhatsApp-style row: a face, the name + preview, and a right column with
   the time on top and an unread counter below it. */
.p-msg__item-main { flex: 1; min-width: 0; }
.p-msg__item-meta {
  flex: none; display: flex; flex-direction: column; align-items: flex-end;
  gap: 5px; align-self: stretch; padding-top: 1px;
}
.p-msg__item:hover { background: var(--canvas); }
.p-msg__item.is-active { background: var(--red-100); box-shadow: inset 3px 0 0 var(--red-600); }
.p-msg__item-top { display: flex; justify-content: space-between; gap: var(--s3); align-items: baseline; }
.p-msg__item-name { font-size: var(--label-m); font-weight: 700; color: var(--ink-900); }
.p-msg__item-time { font-size: 11px; color: var(--ink-500); flex: none; }
.p-msg__item-time.is-unread { color: var(--red-600); font-weight: 700; }
.p-msg__item-prev { font-size: var(--body-s); color: var(--ink-500); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* An unread thread's preview is darker, not bolder: bold on a line that is
   already truncated reads as shouting, and the counter already says "new". */
.p-msg__item-prev.is-unread { color: var(--ink-900); }
/* The unread counter — a small red pill with the number of new messages. */
.p-msg__badge {
  flex: none; min-width: 20px; height: 20px; padding: 0 6px; border-radius: 999px;
  background: var(--red-600); color: #fff; font-size: 11px; font-weight: 800;
  display: inline-flex; align-items: center; justify-content: center;
}

/* Section heading in the conversation list. The office's list has two parts —
   their own threads, then every batch — and without a label the batches look
   like conversations they are already in. */
.p-msg__group {
  padding: var(--s4) var(--s5) var(--s2);
  font-size: var(--label-s); font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-500);
  border-top: 1px solid var(--line);
}
.p-msg__rows > .p-msg__group:first-child { border-top: 0; }
/* Starting a conversation sits at the top of the list, where a chat app puts
   it, and stays put while the list underneath scrolls. */
.p-msg__new {
  position: sticky; top: 0; z-index: 2;
  padding: var(--s3); background: var(--white);
  border-bottom: 1px solid var(--line);
}
/* min-height: 0 is load-bearing. A grid item's automatic minimum size is its
   content, so without this the thread column grows to fit every message —
   6800px tall inside a 560px panel in testing — and .p-msg__body never gets a
   constrained height to scroll inside. .p-msg clips the overflow, so the
   effect was: no internal scroller, a new message appended below the clip and
   apparently never sent, and the composer pushed out of reach. One line. */
.p-msg__thread { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
/* A flex row, not a block. The call button used to be a second child of a
   plain block, so it rendered underneath the title as a stray icon — which is
   why nobody could find it. */
.p-msg__head { padding: var(--s3) var(--s5); border-bottom: 1px solid var(--line);
  display: flex; align-items: center; gap: var(--s2); }
.p-msg__head-id {
  flex: 1; min-width: 0; display: flex; align-items: center; gap: 10px;
  background: none; border: 0; padding: 0; text-align: left; font: inherit; color: inherit;
}
.p-msg__head-id.is-tappable { cursor: pointer; }
.p-msg__head-txt { min-width: 0; }
.p-msg__head-name { font-size: 16px; font-weight: 700; color: var(--ink-900); line-height: 1.15; }
.p-msg__head-sub { font-size: 12px; color: var(--ink-500); line-height: 1.25; }
.p-msg__head-actions { display: flex; align-items: center; gap: 2px; flex: none; }
/* Header controls are plain round icons, not boxed buttons — that boxiness
   was most of what made the old header read as cluttered. */
.p-msg__head .p-iconbtn {
  border: 0; background: none; width: 40px; height: 40px; flex: none;
  color: var(--ink-900);
}
.p-msg__head .p-iconbtn:hover { background: var(--canvas); color: var(--red-600); }

.p-members { display: flex; flex-direction: column; max-height: 46vh; overflow-y: auto; }
.p-members__row { display: flex; align-items: center; gap: var(--s3);
  padding: var(--s3) 0; border-bottom: 1px solid var(--line); }
.p-members__row:last-child { border-bottom: 0; }
/* overflow-x has to be stated. Setting only overflow-y makes the other axis
   compute from `visible` to `auto`, so on any machine with classic
   (space-taking) scrollbars Chromium drew a horizontal scrollbar track above
   the message box — a pale strip the width of the thread, with nothing to
   scroll. A conversation wraps; it never runs sideways. */
.p-msg__body { flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain; padding: var(--s6); display: flex; flex-direction: column; gap: var(--s4); background: var(--canvas); }
.p-bubble { max-width: 74%; padding: 9px 14px; border-radius: 20px; font-size: var(--body-s); line-height: 1.42; }
/* Clean chat bubbles: the other person in soft grey, you in red with white
   text. A tightened corner on the near side gives each its little tail. */
.p-bubble--in { background: #eef0f3; color: var(--ink-900); align-self: flex-start; border-bottom-left-radius: 6px; }
.p-bubble--out { background: var(--red-600); color: #fff; align-self: flex-end; border-bottom-right-radius: 6px; }
.p-bubble__meta { font-size: 11px; opacity: .75; margin-top: 4px; }
/* Pinned to the bottom of the thread. Sticky rather than fixed so it stays
   inside its own column and cannot sit over the conversation list. */
.p-msg__composer { border-top: 1px solid var(--line); padding: var(--s4) var(--s5); display: flex; gap: var(--s3); align-items: flex-end; background: var(--white); position: sticky; bottom: 0; z-index: 2; }
.p-msg__composer .textarea { min-height: 44px; max-height: 140px; }
.p-msg__aside { display: none; border-left: 1px solid var(--line); padding: var(--s5); overflow-y: auto; overflow-x: hidden; min-height: 0; }
.p-msg.show-aside .p-msg__aside { display: block; }
@media (max-width: 1180px) {
  .p-msg { grid-template-columns: 300px minmax(0,1fr); }
  .p-msg.show-aside { grid-template-columns: 300px minmax(0,1fr); }
  .p-msg.show-aside .p-msg__aside { display: none; }
}

/* --- Chats panel header (laptop view) ----------------------------------- */
.p-msg__panel { flex: none; padding: var(--s4) var(--s4) var(--s3); border-bottom: 1px solid var(--line); background: var(--white); }
.p-msg__panel-top { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--s3); }
.p-msg__panel-title { font-family: var(--font-display); font-size: 22px; font-weight: 800; color: var(--ink-900); }
.p-msg__newwrap { position: relative; }
.p-iconbtn--red { background: var(--red-600); border-color: var(--red-600); color: #fff; }
.p-iconbtn--red:hover { background: #a81328; border-color: #a81328; color: #fff; }
.p-msg__newmenu {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 20; min-width: 180px;
  background: #fff; border: 1px solid var(--line); border-radius: 12px;
  box-shadow: 0 12px 34px rgba(0,0,0,.16); padding: 6px;
}
.p-msg__newmenu-item {
  display: flex; align-items: center; gap: 10px; width: 100%; padding: 9px 11px;
  border: 0; background: transparent; border-radius: 8px; cursor: pointer;
  font: inherit; color: var(--ink-900); text-align: left;
}
.p-msg__newmenu-item:hover { background: var(--red-100); color: var(--red-700, #a81328); }
.p-msg__search { display: flex; align-items: center; gap: 10px; background: var(--canvas); border-radius: 22px; padding: 0 14px; color: var(--ink-400); margin-bottom: var(--s3); }
.p-msg__search .icon { flex: none; }
.p-msg__search-input { border: 0; background: none; padding: 10px 0; flex: 1; min-width: 0; font-size: 14px; }
.p-msg__search-input:focus { outline: none; box-shadow: none; }
.p-msg__filters { display: flex; gap: 8px; }
.p-msg__chip {
  padding: 6px 14px; border-radius: 20px; font-size: 13px; font-weight: 700;
  background: var(--canvas); color: var(--ink-700); border: 1px solid transparent; cursor: pointer;
}
.p-msg__chip.is-on { background: var(--red-100); color: var(--red-700, #a81328); }
/* The info toggle only earns its place where the aside can appear. */
.p-msg__info { display: inline-flex; }
/* Laptop and up: the messenger takes the whole content area, so the global
   top bar (which only repeats "Messages") steps aside. */
@media (min-width: 901px) { .p-main:has(.p-msg) > .p-topbar { display: none; } }

/* --- Attendance marking --------------------------------------------------------------- */
.p-att-row { display: flex; align-items: center; gap: var(--s4); padding: var(--s3) var(--s6); border-bottom: 1px solid var(--line); }
.p-att-row:hover { background: var(--blue-50); }
.p-att-row__name { flex: 1; min-width: 0; }
.p-seg { display: inline-flex; border: 1px solid var(--line-strong); border-radius: var(--r-pill); overflow: hidden; }
.p-seg button {
  background: var(--white); border: 0; cursor: pointer; padding: 7px var(--s4);
  font-family: var(--font-body); font-size: var(--label-s); font-weight: 600; color: var(--ink-500);
  border-right: 1px solid var(--line);
}
.p-seg button:last-child { border-right: 0; }
.p-seg button.is-on { color: #fff; }
.p-seg button.is-on[data-v="present"] { background: var(--green-600); }
.p-seg button.is-on[data-v="absent"]  { background: var(--red-600); }
.p-seg button.is-on[data-v="late"]    { background: var(--gold-400); color: var(--ink-900); }
.p-seg button.is-on[data-v="excused"] { background: var(--ink-500); }

/* --- Mobile -------------------------------------------------------------------------- */
.p-mobile-bar { display: none; }
/* The mirror of .p-mobile-bar, and it has to live down here with it: both
   are single-class selectors competing with .p-iconbtn's own `display`, so
   whichever comes last in the file wins. Declared above .p-iconbtn, this
   rule lost and the desktop toggle appeared on phones. */
.p-desk-only { display: none; }
@media (min-width: 901px) { .p-desk-only { display: grid; } }
/* The arrow turns to point the way it will move things. */
.p-navtoggle .icon { transition: transform .24s var(--ease); }
.portal.nav-collapsed .p-navtoggle .icon { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) {
  .p-navtoggle .icon, .portal.nav-collapsed .p-navtoggle .icon { transition: none; }
}

/* The chevron handle on the sidebar's right edge (desktop only). Fixed to the
   viewport so it survives the sidebar sliding away: it rides the 256px edge
   while open and drops to the screen edge once shut. */
.p-navedge {
  position: fixed; top: 76px; left: calc(var(--sidebar-w) - 14px);
  width: 28px; height: 28px; border-radius: 50%;
  place-items: center; padding: 0; z-index: 45; cursor: pointer;
  background: #fff; color: var(--ink-700);
  border: 1px solid var(--line); box-shadow: 0 2px 8px rgba(6,10,15,.12);
  transition: left .24s var(--ease), background .12s var(--ease), color .12s var(--ease);
}
.p-navedge:hover { background: var(--red-600); color: #fff; border-color: var(--red-600); }
.p-navedge .icon { transition: transform .24s var(--ease); }
.portal.nav-collapsed .p-navedge { left: 10px; }
.portal.nav-collapsed .p-navedge .icon { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) {
  .p-navedge, .p-navedge .icon, .portal.nav-collapsed .p-navedge .icon { transition: none; }
}
@media (max-width: 900px) {
  .portal { grid-template-columns: minmax(0, 1fr); }
  /* The aside info toggle is hidden on a phone, which uses its own full-screen
     chrome (lead + tab bar). The chats-column header keeps its search box and
     filters so you can search conversations on a phone too — only the title
     row (the phone already has a title and a New-chat FAB) is dropped. */
  .p-msg__info { display: none; }
  .p-msg__panel { flex: none; padding: var(--s3) var(--s4) 0; border-bottom: 0; background: transparent; }
  .p-msg__panel-top { display: none; }
  .p-sidebar {
    position: fixed; inset: 0 auto 0 0; width: 84vw; max-width: 320px; z-index: 120;
    transform: translateX(-100%); transition: transform .22s var(--ease);
  }
  .portal.nav-open .p-sidebar { transform: none; }
  .portal.nav-open::after { content: ""; position: fixed; inset: 0; background: rgba(22,35,58,.5); z-index: 110; }
  .p-mobile-bar { display: flex; }
  /* The top bar is a fixed 72px, and on a phone its two lines did not fit
     in it: a title like "Publish results" and a sub like "Nothing reaches
     students until you publish" wrapped to three lines and spilled out of
     the bar, clipping the title against the top of the screen.

     So on a phone the bar carries the name of the screen and nothing else.
     The sub is not lost — the screens that have one render it in the
     canvas underneath, which is where there is room for a sentence. */
  .p-topbar { padding-inline: var(--s4); gap: var(--s3); }
  .p-topbar__date { display: none; }
  .p-topbar > div:has(.p-topbar__title) { min-width: 0; }
  .p-topbar__title {
    font-size: 18px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .p-search { display: none; }

  /* --- Every page full-bleed on a phone ------------------------------
     The canvas gives up its side padding and the blocks that hold lists
     and tables run to both edges, the way a phone app's screens do. On a
     390px screen the 32px of gutter was about 8% of the width, spent on
     nothing, and a table lost a column to it.

     What does NOT go edge to edge: text and controls. Each block keeps
     its own inner padding, so reading still starts a comfortable distance
     from the edge — it is the card's border and corners that go, not the
     breathing room inside it. Anything that reads as a tile rather than a
     list — the stat grid, an empty state, a notice — keeps a gutter,
     because a tile flush to the screen edge looks broken rather than
     deliberate. */
  .p-canvas { padding: var(--s5) 0; }
  /* The heading and any free-standing text still need a margin. */
  .p-canvas > .p-head,
  .p-canvas > .p-toolbar,
  .p-canvas > .notice-box,
  .p-canvas > .p-empty,
  .p-canvas > p,
  .p-canvas > .row,
  .p-canvas > .stack { padding-inline: var(--s4); }
  .p-canvas > .p-stats { padding-inline: var(--s4); }

  /* Cards go full width and lose the corners and side borders that only
     make sense when there is something beside them. */
  .p-canvas > .p-card,
  .p-canvas > .card {
    border-left: 0; border-right: 0; border-radius: 0;
  }
  /* A run of them shouldn't double its dividing line. */
  .p-canvas > .p-card + .p-card { border-top: 0; }

  /* --- Messages on a phone: a chat app, not a page with a panel on it ---
     Edge to edge, no card border, no page heading, and once a thread is open
     the portal's own topbar gets out of the way so the conversation header is
     the only one — which is how WhatsApp and Telegram behave and what people
     already expect from a thread on a phone. */
  .p-canvas:has(.p-msg) { padding: 0; }
  .p-msg {
    grid-template-columns: minmax(0,1fr);
    border: 0; border-radius: 0; min-height: 0;
  }
  /* The list is its own screen until you pick a conversation. */
  .p-msg__rows { padding-bottom: env(safe-area-inset-bottom, 0px); }
  .p-msg__item { padding: var(--s4); }

  /* Thread open: the app header goes, the conversation header replaces it. */
  .p-main:has(.p-msg.show-thread) > .p-topbar { display: none; }
  .p-msg__head {
    position: sticky; top: 0; z-index: 3;
    padding: var(--s3) var(--s4);
    padding-top: calc(var(--s3) + env(safe-area-inset-top, 0px));
  }
  .p-msg__head .p-card__title { font-size: var(--label-m); }
  .p-msg__body { padding: var(--s4); gap: var(--s3); }
  .p-bubble { max-width: 86%; }
  /* On a phone the message box sits on the bottom edge, where a thumb is,
     rather than at the end of a column you have to scroll to find.
     safe-area-inset keeps it clear of the home indicator on an iPhone. */
  .p-msg__composer { padding-bottom: calc(var(--s4) + env(safe-area-inset-bottom, 0px)); }
  .p-msg__head { padding: var(--s3) var(--s4); }
  .p-msg__head-actions .btn { padding-inline: var(--s3); }
  .p-msg__list { border-right: 0; }
  .p-msg.show-thread .p-msg__list { display: none; }
  .p-msg:not(.show-thread) .p-msg__thread { display: none; }

  /* The tab bar is fixed over the bottom 86px of the screen. The messages
     surface is 100dvh by default (see .p-main:has(.p-msg)), so its composer
     sat at the very bottom edge — underneath the bar, where it could not be
     reached. Shrinking the surface by the bar's height lifts the whole thing
     above it: the composer now ends just above the bar, the last thread in
     the list clears it too, and the navigation stays put. */
  .p-main:has(.p-msg) {
    height: calc(100dvh - (86px + env(safe-area-inset-bottom, 0px)));
  }
}

/* --- Print (registers, mark sheets) ---------------------------------------------------- */
@media print {
  .p-sidebar, .p-topbar, .p-toolbar, .btn, .p-toasts { display: none !important; }
  .portal { display: block; }
  .p-canvas { padding: 0; }
  .p-card { border: 0; }
  .table th { background: none; }
}

/* =====================================================================
   Screens added with the modules that had no interface: the timetable
   builder, the study planner, the lead pipeline and the document queue.
   Everything below reuses the existing tokens, so these inherit the
   theme and the dark surface rules with it.
   ===================================================================== */

/* --- Week grid: the timetable builder (BT-06) ----------------------- */
.p-week { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: var(--s3); }
.p-week__day { background: var(--blue-50); border: 1px solid var(--line); border-radius: var(--r-md);
  padding: var(--s3); min-height: 120px; display: flex; flex-direction: column; gap: var(--s2); }
.p-week__name { display: flex; align-items: center; justify-content: space-between; gap: var(--s2);
  font: var(--label-m); color: var(--ink-700); min-width: 0; }
.p-week__name > span { min-width: 0; }
.p-week__add { flex: none; padding-inline: var(--s3); }
.p-week__slot { background: var(--white); border: 1px solid var(--line); border-left: 3px solid var(--blue-700);
  border-radius: var(--r-sm); padding: var(--s3); cursor: pointer; transition: box-shadow .12s ease; }
.p-week__slot:hover { box-shadow: var(--shadow-sm); }
.p-week__slot--draft { border-left-color: var(--gold-400); background: #fffdf5; }

/* --- Study planner (LP-*) ------------------------------------------- */
.p-block { display: grid; grid-template-columns: 64px 1fr 40px; align-items: center; gap: var(--s4);
  padding: var(--s3) var(--s4); border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--white); }
.p-block--lecture { border-left: 3px solid var(--gold-400); }
.p-block--done { opacity: .55; }
.p-block--done .p-block__main { text-decoration: line-through; }
.p-block__time { text-align: right; color: var(--ink-500); }
.p-block__main { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.p-block__tick { display: flex; justify-content: flex-end; }
.p-check { width: 20px; height: 20px; accent-color: var(--blue-700); cursor: pointer; }

/* --- Lead pipeline (LD-*) ------------------------------------------- */
.p-board { display: flex; gap: var(--s3); overflow-x: auto; padding-bottom: var(--s3); }
.p-board__col { flex: 0 0 240px; background: var(--blue-50); border: 1px solid var(--line);
  border-radius: var(--r-md); padding: var(--s3); display: flex; flex-direction: column; gap: var(--s2); }
.p-board__head { display: flex; align-items: center; justify-content: space-between; gap: var(--s2); }
.p-board__card { background: var(--white); border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: var(--s3); cursor: pointer; display: flex; flex-direction: column; gap: 2px;
  transition: box-shadow .12s ease; }
.p-board__card:hover { box-shadow: var(--shadow-sm); }

.text-red { color: var(--red-600, #b42318); }

/* --- Narrow screens -------------------------------------------------- */
@media (max-width: 900px) {
  .p-week { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .p-week { grid-template-columns: 1fr; }
  .p-block { grid-template-columns: 56px 1fr 32px; gap: var(--s3); }
  .p-board__col { flex-basis: 210px; }
}

/* --- Question bank, sitting a paper, and marking one ------------------ */
.p-opt { display: flex; align-items: center; gap: var(--s3); padding: var(--s2) var(--s3);
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--white); cursor: pointer; }
.p-opt--chosen { border-color: var(--blue-700); background: var(--blue-50); }
.p-opt--correct { border-left: 3px solid #12805c; }

.p-exambar { position: sticky; top: 0; z-index: 20; display: flex; align-items: center;
  justify-content: space-between; gap: var(--s4); padding: var(--s3) var(--s5);
  background: var(--white); border: 1px solid var(--line); border-radius: var(--r-md);
  box-shadow: var(--shadow-sm); margin-bottom: var(--s5); }
.p-clock { font: var(--label-m); font-variant-numeric: tabular-nums; font-size: 20px;
  padding: var(--s2) var(--s4); border-radius: var(--r-sm); background: var(--blue-50); color: var(--blue-700); }
.p-clock--low { background: #fdecec; color: #b42318; }

/* --- Course tree ------------------------------------------------------ */
.p-tree { border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--white); }
.p-tree > summary { cursor: pointer; padding: var(--s3) var(--s4); list-style: none; }
.p-tree > summary::-webkit-details-marker { display: none; }
.p-tree > summary::before { content: '▸'; margin-right: var(--s2); color: var(--ink-500); }
.p-tree[open] > summary::before { content: '▾'; }
.p-tree__body { padding: 0 var(--s4) var(--s4); }

/* --- Permission matrix ------------------------------------------------ */
.p-perm-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: var(--s2); }
.p-perm { display: flex; align-items: center; gap: var(--s2); padding: var(--s2);
  border-radius: var(--r-sm); cursor: pointer; }
.p-perm:hover { background: var(--blue-50); }

/* --- Live classroom --------------------------------------------------- */
.p-live { display: flex; flex-direction: column; gap: var(--s3); }
.p-live__frame { width: 100%; height: min(70vh, 560px); border: 0;
  border-radius: var(--r-md); background: #000; }

@media (max-width: 560px) {
  .p-exambar { flex-direction: column; align-items: stretch; gap: var(--s2); }
  .p-perm-grid { grid-template-columns: 1fr; }
}

/* --- Recording player: watermark and check-in (VA-15, AT-09) ------------ */

.p-player { position: relative; overflow: hidden; border-radius: var(--r-md); background: #000; }
.p-player video, .p-player iframe { display: block; width: 100%; height: 100%; }

/* The viewer's name, drifting across the frame. pointer-events:none so it
   never swallows a click on the controls underneath. */
.p-wm {
  position: absolute; inset: 0; pointer-events: none; user-select: none;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; font: 600 13px/1.2 var(--font-sans, system-ui, sans-serif);
  color: rgba(255, 255, 255, .34); text-shadow: 0 1px 3px rgba(0, 0, 0, .55);
  letter-spacing: .04em; white-space: nowrap;
  animation: p-wm-drift 19s ease-in-out infinite alternate;
}
.p-wm span:last-child { font-weight: 400; font-size: 11px; opacity: .85; }

@keyframes p-wm-drift {
  0%   { transform: translate(-24%, -30%); }
  25%  { transform: translate( 26%, -12%); }
  50%  { transform: translate(  4%,  28%); }
  75%  { transform: translate(-22%,  10%); }
  100% { transform: translate( 24%,  26%); }
}

@media (prefers-reduced-motion: reduce) {
  .p-wm { animation: none; }
}

/* Check-in, projected (AT-09). Deliberately large: this is read from the
   back of a classroom. */
.p-checkin { display: flex; flex-direction: column; align-items: center; gap: var(--s4); text-align: center; }
.p-qr { background: #fff; padding: var(--s3); border-radius: var(--r-md); line-height: 0; }
.p-checkin__code {
  font: 700 clamp(38px, 9vw, 64px)/1 var(--font-sans, system-ui, sans-serif);
  letter-spacing: .14em; color: var(--ink-900, #16202e);
}
.p-clock { font-variant-numeric: tabular-nums; color: var(--ink-500, #5a6b82); font-size: 14px; }

/* =====================================================================
   Voice calls — MS-13
   The panel docks bottom-right on a desktop and takes the full width of a
   phone, so the call survives moving between screens: it lives on <body>,
   not inside the canvas the router replaces.
   ===================================================================== */
.p-call {
  position: fixed; right: var(--s5); bottom: var(--s5); z-index: 80;
  width: 360px; max-width: calc(100vw - var(--s5) * 2);
  background: var(--white); border: 1px solid var(--line);
  border-radius: var(--r-lg); box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.p-call__head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s4);
  padding: var(--s4) var(--s5); border-bottom: 1px solid var(--line);
}
.p-call__who { font: var(--label-m); font-weight: 700; color: var(--ink-900); }
.p-call__sub {
  display: flex; align-items: center; gap: var(--s2);
  font-size: var(--body-s); color: var(--ink-500); margin-top: 2px;
}
.p-call__timer { font-variant-numeric: tabular-nums; }
/* Jitsi needs a box with a height even when nothing is shown. Audio-only
   still renders its toolbar, which is how somebody mutes themselves. */
/* The shared screen. There is no iframe any more — the call is two browsers
   talking directly — so this is only here while somebody is actually
   sharing, and the panel is just its header and note the rest of the time.
   A voice call has nothing to look at, and a black rectangle standing in
   for that is worse than no rectangle. */
.p-call__video {
  width: 100%; max-width: 100%; display: block;
  aspect-ratio: 16 / 9; object-fit: contain;
  background: var(--ink-900); border-top: 1px solid var(--line);
}
.p-call__video[hidden] { display: none !important; }
/* A control that is currently doing something, and one that is switched
   off. Muted is the one worth being unmistakable about: somebody who
   thinks they are muted and is not has a worse day than the reverse. */
.p-iconbtn.is-on { background: var(--blue-600); border-color: var(--blue-600); color: #fff; }
.p-iconbtn.is-on:hover { background: var(--blue-700); border-color: var(--blue-700); color: #fff; }
.p-iconbtn.is-off { background: var(--red-600); border-color: var(--red-600); color: #fff; }
.p-iconbtn.is-off:hover { background: var(--red-600); border-color: var(--red-600); color: #fff; }
/* Expanded, for a shared screen. A 360x220 corner panel is right for a voice
   call and unreadable for somebody's spreadsheet, so the panel grows on
   request — never on its own, because the far end pressing a button should
   not rearrange your window. Capped against the viewport so it stays a panel
   rather than becoming a second application. */
.p-call.is-wide { width: min(880px, calc(100vw - var(--s5) * 2)); }
.p-call.is-wide .p-call__video { aspect-ratio: auto; height: min(62vh, 520px); }
.p-call__note {
  margin: 0; padding: var(--s3) var(--s5) var(--s4);
  font-size: var(--label-s); color: var(--ink-500); background: var(--blue-50);
}

/* The incoming-call card. Deliberately loud, and deliberately above the
   call panel's z-index so it can never be hidden behind one. */
.p-ring {
  position: fixed; right: var(--s5); bottom: var(--s5); z-index: 90;
  width: 340px; max-width: calc(100vw - var(--s5) * 2);
  background: var(--white); border: 1px solid var(--blue-600);
  border-radius: var(--r-lg); box-shadow: var(--shadow-lg);
  padding: var(--s5);
  animation: p-ring-in .18s var(--ease);
}
@keyframes p-ring-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .p-ring { animation: none; } }
.p-ring__body { display: flex; align-items: center; gap: var(--s4); margin-bottom: var(--s5); }
.p-ring__icon {
  flex: none; width: 44px; height: 44px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--blue-50); color: var(--blue-700);
}
.p-ring__who { font: var(--label-m); font-weight: 700; color: var(--ink-900); }
.p-ring__sub { font-size: var(--body-s); color: var(--ink-500); }
.p-ring__actions { display: flex; gap: var(--s3); justify-content: flex-end; }

@media (max-width: 560px) {
  .p-call, .p-ring {
    left: var(--s4); right: var(--s4); bottom: var(--s4); width: auto; max-width: none;
  }

  /* On a phone there is no bigger to go to — the panel is already the width
     of the screen — so expanding only gives the frame more height. */
  .p-call.is-wide { width: auto; }
  .p-call.is-wide .p-call__video { aspect-ratio: auto; height: 52vh; }
}

/* --- Asking to turn notifications on --------------------------------------
   Bottom right, where the ring card also lives — the two are never wanted
   at once, so a call arriving closes this rather than stacking on it. Not
   bottom left: that is where the sidebar is, and a card floating over the
   navigation reads as a fault rather than as a message.

   Two rows, always. Squeezed onto one, the buttons take the width they
   need and the sentence beside them collapses to a word per line. */
.p-nudge {
  position: fixed; right: var(--s5); bottom: var(--s5); z-index: 80;
  width: 380px; max-width: calc(100vw - var(--s5) * 2);
  display: grid; grid-template-columns: auto 1fr; gap: var(--s2) var(--s4);
  background: var(--white); border: 1px solid var(--line);
  border-radius: var(--r-lg); box-shadow: var(--shadow-lg);
  padding: var(--s4) var(--s5);
  animation: p-ring-in .18s var(--ease);
}
@media (prefers-reduced-motion: reduce) { .p-nudge { animation: none; } }
.p-nudge__icon {
  flex: none; width: 36px; height: 36px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--gold-100); color: var(--gold-700);
}
.p-nudge__title { font: var(--label-m); font-weight: 700; color: var(--ink-900); }
.p-nudge__sub { font-size: var(--body-s); color: var(--ink-500); margin-top: 2px; }
.p-nudge__actions {
  grid-column: 2; display: flex; gap: var(--s2); align-items: center;
  justify-content: flex-end; margin-top: var(--s2);
}

@media (max-width: 560px) {
  .p-nudge {
    left: var(--s4); right: var(--s4); bottom: var(--s4); width: auto; max-width: none;
  }
}

@media print {
  .p-call, .p-ring, .p-nudge { display: none !important; }
}

/* --- Replying to a message, and system lines ------------------------------ */
/* The composer becomes two rows when a reply is in flight, so the quoted
   message sits directly above what you are typing rather than beside it. */
.p-msg__composer--stack { flex-direction: column; align-items: stretch; gap: var(--s3); }
.p-msg__composer-row { display: flex; gap: var(--s3); align-items: flex-end; }
/* The text box takes the room; the attach, mic and send controls keep their
   size and never get pushed off a narrow screen. */
.p-msg__composer-row { align-items: center; }
.p-msg__composer-row .textarea { flex: 1; min-width: 0; width: auto; }
.p-msg__composer-row .p-iconbtn, .p-msg__composer-row .btn { flex: none; }

/* The rounded input pill: the paperclip and the text box live inside one
   soft capsule, like a phone chat app. */
.p-composer-pill {
  flex: 1; min-width: 0; display: flex; align-items: center; gap: 2px;
  background: #eef0f3; border: 1px solid var(--line);
  border-radius: 24px; padding: 3px 6px 3px 8px;
}
.p-composer-pill .textarea {
  flex: 1; min-width: 0; width: auto; border: 0; background: transparent;
  box-shadow: none; outline: none; resize: none;
  min-height: 30px; max-height: 120px; padding: 7px 4px; line-height: 1.35;
}
.p-composer-pill .textarea:focus { box-shadow: none; outline: none; }
.p-composer-pill .p-iconbtn {
  flex: none; width: 34px; height: 34px; padding: 0;
  border: 0; background: transparent; color: var(--ink-500);
}
.p-composer-pill .p-iconbtn:hover { background: transparent; border: 0; color: var(--red-600); }

/* The round action button on the right — mic, or a send arrow once typing. */
.p-composer-go {
  flex: none; width: 46px; height: 46px; border-radius: 50%; border: 0; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--red-600); color: #fff;
  transition: background .15s var(--ease), transform .08s var(--ease);
}
.p-composer-go:hover { background: #a81328; }
.p-composer-go:active { transform: scale(.93); }
.p-composer-go svg { display: block; }
.p-composer-go.is-recording { animation: p-rec-pulse 1s infinite; }

.p-reply {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s2) var(--s3); border-radius: var(--r-sm);
  background: var(--blue-50); border-left: 3px solid var(--blue-600);
}
.p-reply__body { min-width: 0; flex: 1; }
.p-reply__who { font-size: var(--label-s); font-weight: 700; color: var(--blue-700); }
.p-reply__text { font-size: var(--body-s); color: var(--ink-500); }

/* The quote inside a bubble. A button, because it jumps to the original. */
.p-bubble__quote {
  display: block; width: 100%; text-align: left; cursor: pointer;
  background: rgba(255,255,255,.55); border: 0; border-left: 3px solid currentColor;
  border-radius: var(--r-sm); padding: var(--s2) var(--s3); margin-bottom: var(--s2);
  font: inherit; opacity: .9;
}
.p-bubble__quote:hover { opacity: 1; }
.p-bubble--out .p-bubble__quote { background: rgba(255,255,255,.22); }
.p-bubble__quote--gone { cursor: default; font-size: var(--body-s); font-style: italic; }
.p-bubble__quote-who { display: block; font-size: var(--label-s); font-weight: 700; }
.p-bubble__quote-text { display: block; font-size: var(--body-s); }

.p-bubble__foot { display: flex; align-items: center; gap: var(--s3); margin-top: 2px; }
.p-bubble__reply {
  background: none; border: 0; padding: 0; cursor: pointer;
  font-size: var(--label-s); font-weight: 600; color: inherit;
  opacity: 0; transition: opacity .12s var(--ease);
}
.p-bubble:hover .p-bubble__reply,
.p-bubble__reply:focus-visible { opacity: .75; }
.p-bubble__reply:hover { opacity: 1; text-decoration: underline; }
/* Touch has no hover, so the action must simply be there. */
@media (hover: none) { .p-bubble__reply { opacity: .75; } }

/* Forward sits beside Reply and shares its quiet, hover-revealed look. */
.p-bubble__act {
  background: none; border: 0; padding: 0; cursor: pointer;
  font-size: var(--label-s); font-weight: 600; color: inherit;
  opacity: 0; transition: opacity .12s var(--ease);
}
.p-bubble:hover .p-bubble__act,
.p-bubble__act:focus-visible { opacity: .75; }
.p-bubble__act:hover { opacity: 1; text-decoration: underline; }
@media (hover: none) { .p-bubble__act { opacity: .75; } }

/* The ⋮ options button on a message, and the menu it opens (edit, delete). */
.p-bubble__more-wrap { position: relative; display: inline-flex; margin-left: auto; }
.p-bubble__more {
  background: none; border: 0; padding: 0; cursor: pointer; color: inherit;
  line-height: 0; opacity: 0; transition: opacity .12s var(--ease);
}
.p-bubble:hover .p-bubble__more,
.p-bubble__more:focus-visible { opacity: .7; }
.p-bubble__more:hover { opacity: 1; }
@media (hover: none) { .p-bubble__more { opacity: .7; } }
/* The ⋮ sits at the right edge of the bubble (margin-left:auto), so the menu
   always hangs from its right edge and opens leftward — over the thread, never
   off the right side of the screen. A JS clamp nudges it fully into view on
   narrow phones. */
.p-bubble__menu {
  position: absolute; bottom: calc(100% + 6px); right: 0; z-index: 30;
  min-width: 190px; max-width: calc(100vw - 16px);
  background: #fff; border: 1px solid var(--line); border-radius: 12px;
  box-shadow: 0 12px 34px rgba(0,0,0,.18); padding: 6px;
}
.p-bubble__menu-item {
  display: flex; align-items: center; gap: 10px; width: 100%; padding: 9px 11px;
  border: 0; background: transparent; border-radius: 8px; cursor: pointer;
  font: inherit; font-size: var(--body-s); color: var(--ink-900); text-align: left;
}
.p-bubble__menu-item:hover { background: var(--canvas); }
.p-bubble__menu-item.is-danger { color: var(--red-600); }
.p-bubble__menu-item.is-danger:hover { background: var(--red-100); }
.p-bubble__menu-item .icon { flex: none; color: currentColor; }

/* The compose strip turns amber-ish when it is an edit rather than a reply. */
.p-reply--edit { background: var(--red-100); border-left-color: var(--red-600); }
.p-reply--edit .p-reply__who { color: var(--red-700, #a81328); }

/* "Forwarded from X" line above a forwarded message. */
.p-bubble__fwd {
  display: flex; align-items: center; gap: 5px;
  font-size: var(--label-s); font-weight: 600; font-style: italic;
  opacity: .72; margin-bottom: 3px;
}
.p-bubble__fwd svg { flex: none; }

/* --- Message attachments: images, voice notes, files ------------------ */
.p-att { display: block; margin-top: var(--s2); max-width: 100%; }
.p-att--img { position: relative; border-radius: var(--r-md); overflow: hidden; width: fit-content; max-width: 100%; }
.p-att__imglink { display: block; line-height: 0; }
.p-att__img { max-width: 260px; max-height: 340px; height: auto; display: block; cursor: zoom-in; background: var(--canvas); object-fit: cover; }

/* An image-only message: no coloured bubble, just the picture. The time and
   ⋮ menu drop to a slim muted row beneath it, since there is no longer a
   coloured fill to read white text against. */
.p-bubble--media { background: transparent; padding: 0; border-radius: 0; max-width: min(260px, 74%); }
.p-bubble--media .p-att { margin-top: 0; }
.p-bubble--media .p-bubble__foot { margin-top: 3px; padding: 0 2px; }
.p-bubble--media.p-bubble--out .p-bubble__foot { justify-content: flex-end; }
.p-bubble--media .p-bubble__meta { color: var(--ink-500); opacity: 1; }
.p-bubble--media .p-bubble__more { color: var(--ink-500); }
.p-bubble--media .p-bubble__more:hover { color: var(--ink-900); }
.p-att--audio { display: flex; align-items: center; gap: var(--s2); width: min(280px, 100%); }
.p-att__audio { flex: 1; min-width: 0; height: 40px; }

/* The round download button on an image or a voice note. */
.p-att__dlbtn {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 50%;
  color: inherit; text-decoration: none;
}
.p-att--img .p-att__dlbtn {
  position: absolute; top: 6px; right: 6px;
  background: rgba(6,10,15,.55); color: #fff;
  opacity: 0; transition: opacity .12s var(--ease);
}
.p-att--img:hover .p-att__dlbtn, .p-att--img:focus-within .p-att__dlbtn { opacity: 1; }
@media (hover: none) { .p-att--img .p-att__dlbtn { opacity: 1; } }
.p-att--audio .p-att__dlbtn { background: rgba(0,0,0,.06); }
.p-att__dlbtn:hover { filter: brightness(1.1); }
.p-att--file {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s3); border-radius: var(--r-md);
  background: rgba(255,255,255,.55); text-decoration: none; color: inherit;
  max-width: 280px;
}
.p-bubble--out .p-att--file { background: rgba(255,255,255,.22); }
.p-att--file:hover { background: rgba(255,255,255,.8); }
.p-bubble--out .p-att--file:hover { background: rgba(255,255,255,.32); }
.p-att__icon, .p-att__dl { flex: none; display: inline-flex; opacity: .8; }
.p-att__meta { min-width: 0; flex: 1; display: flex; flex-direction: column; }
.p-att__name { font-size: var(--body-s); font-weight: 600; }
.p-att__size { font-size: 11px; opacity: .7; }

/* --- Recording a voice note ------------------------------------------- */
.p-rec {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s2) var(--s3); border-radius: var(--r-sm);
  background: var(--red-100); border-left: 3px solid var(--red-600);
}
.p-rec__dot { width: 10px; height: 10px; border-radius: 50%; background: var(--red-600); animation: p-rec-pulse 1s infinite; flex: none; }
@keyframes p-rec-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }
.p-rec__label { font-size: var(--label-s); font-weight: 700; color: var(--red-600); }
.p-rec__time { font-variant-numeric: tabular-nums; font-size: var(--body-s); color: var(--ink-700); }
.p-rec__btn { margin-left: auto; background: none; border: 0; cursor: pointer; font-size: var(--label-s); font-weight: 700; color: var(--ink-500); padding: 4px 8px; border-radius: var(--r-sm); }
.p-rec__btn:last-child { margin-left: 0; }
.p-rec__btn:hover { background: rgba(0,0,0,.05); }
.p-rec__btn--send { color: var(--blue-700); }
.p-iconbtn.is-recording { background: var(--red-600); border-color: var(--red-600); color: #fff; }

/* --- Forward picker --------------------------------------------------- */
.p-fwd__list { display: flex; flex-direction: column; gap: 2px; max-height: 50vh; overflow-y: auto; }
.p-fwd__item {
  display: flex; align-items: center; gap: var(--s3); width: 100%; text-align: left;
  padding: var(--s3); border: 0; background: none; cursor: pointer; border-radius: var(--r-md);
  font: inherit; color: inherit;
}
.p-fwd__item:hover { background: var(--blue-50); }
.p-fwd__item:disabled { opacity: .5; cursor: default; }

/* --- Messenger: full-screen app-within-the-app on phones --------------
   Desktop keeps the two-pane Messages view, so all of this is hidden there
   and only switches on below the mobile breakpoint, inside .is-messenger. */
.p-messenger__lead, .p-messenger__tabs, .p-messenger__ph, .p-messenger__fab { display: none; }

/* Contacts & recent-calls lists inside the messenger tabs. */
.p-mlist__top { padding: 10px 14px; }
.p-mlist__spin { padding: 28px; text-align: center; color: var(--ink-500); font-size: var(--body-s); }
.p-mlist__letter {
  padding: 8px 18px 4px; font-size: 12px; font-weight: 800; letter-spacing: .04em;
  color: var(--red-600); background: var(--white); position: sticky; top: 0; z-index: 1;
}
.p-mlist__row { display: flex; align-items: center; gap: var(--s3); padding: 8px 14px; border-bottom: 1px solid var(--line); }
.p-mlist__main { flex: 1; min-width: 0; text-align: left; background: none; border: 0; cursor: pointer; padding: 4px 0; font: inherit; }
.p-mlist__name { font-size: var(--label-m); font-weight: 700; color: var(--ink-900); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.p-mlist__name.is-missed { color: var(--red-600); }
.p-mlist__sub { font-size: var(--body-s); color: var(--ink-500); }
.p-mlist__act {
  flex: none; width: 40px; height: 40px; border-radius: 50%; border: 0; cursor: pointer;
  display: grid; place-items: center; background: var(--canvas); color: var(--red-600);
}
.p-mlist__act:hover { background: var(--red-100); }

/* Every button in the messenger is red (brand), including popups opened from
   it (the New group, Members and New chat dialogs mount on <body>, so they
   are reached via body:has). Applies on desktop too, for consistency. */
.portal.is-messenger .btn--primary,
body:has(.portal.is-messenger) .p-modal .btn--primary { background: var(--red-600); }
.portal.is-messenger .btn--primary:hover,
body:has(.portal.is-messenger) .p-modal .btn--primary:hover { background: #a81328; }
.portal.is-messenger .btn--quiet,
body:has(.portal.is-messenger) .p-modal .btn--quiet { color: var(--red-600); }
.portal.is-messenger .btn--ghost:hover,
body:has(.portal.is-messenger) .p-modal .btn--ghost:hover {
  background: var(--red-100); border-color: var(--red-600); color: var(--red-600);
}

/* A dialog that reads as a page leads with a back arrow on the left instead
   of an × on the right (the messenger's New-group screen). */
.p-modal__head--back { justify-content: flex-start; }
.p-modal__back { margin-left: calc(var(--s2) * -1); }

/* Delete group — destructive, set off at the foot of the Members panel. */
.p-msg__danger { border-top: 1px solid var(--line); padding-top: var(--s5); }
.p-msg__danger .btn { justify-content: center; color: var(--red-600); border-color: var(--red-100); }
.p-modal .p-msg__danger .btn:hover,
.p-msg__danger .btn:hover { background: var(--red-600); border-color: var(--red-600); color: #fff; }
.p-msg__danger .btn:hover .icon { color: #fff; }

/* @mentions — a tagged name in a message, and the picker that inserts it. */
.p-mention { font-weight: 700; }
.p-bubble--in .p-mention { color: var(--red-600); }
.p-bubble--out .p-mention { color: #fff; text-decoration: underline; text-underline-offset: 2px; }

.p-msg__composer--stack { position: relative; }
.p-mention-menu {
  position: absolute; left: 10px; right: 10px; bottom: calc(100% + 6px);
  background: #fff; border: 1px solid var(--line); border-radius: 12px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, .14); padding: 6px; z-index: 40;
  max-height: 244px; overflow-y: auto;
}
.p-mention-menu__item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 8px 10px; border: 0; background: transparent; border-radius: 8px;
  cursor: pointer; text-align: left; font: inherit; color: var(--ink-900);
}
.p-mention-menu__item:hover,
.p-mention-menu__item.is-active { background: var(--red-100); }

/* Messenger Settings tab. */
.p-mset { padding: 6px 14px 28px; }
.p-mset__head {
  font-size: 12px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-500); margin: 18px 2px 8px;
}
.p-mset__card { background: var(--white); border: 1px solid var(--line); border-radius: var(--r-md); padding: 14px; }
.p-mset__row { display: flex; align-items: center; justify-content: space-between; gap: var(--s4); cursor: pointer; }
.p-mset__row-title { font-weight: 700; color: var(--ink-900); }
.p-mset__times { display: flex; gap: var(--s4); }
.p-mset__time { display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 0; }

@media (max-width: 900px) {
  /* The LMS furniture steps aside — headerless, no LMS bottom nav. */
  .portal.is-messenger .p-topbar { display: none; }
  .portal.is-messenger .p-tabbar { display: none; }

  /* Pin the messenger to the whole viewport so the tab bar sits flush on the
     bottom edge. Without this the main area is 100dvh while the portal around
     it is 100vh, and the difference showed as a grey band under the tabs. */
  .portal.is-messenger .p-main { position: fixed; inset: 0; width: 100%; height: 100dvh; }

  /* Lead: a Back-to-LMS pill and the section title, shown at the list level. */
  .portal.is-messenger .p-messenger__lead {
    display: flex; flex-direction: column; gap: 8px; flex: none;
    padding: calc(10px + env(safe-area-inset-top, 0px)) 16px 10px;
    background: var(--white); border-bottom: 1px solid var(--line);
  }
  .p-messenger__back {
    align-self: flex-start; display: inline-flex; align-items: center; gap: 5px;
    background: var(--red-100); color: var(--red-600);
    border: 0; border-radius: 999px; padding: 6px 13px 6px 10px;
    font-size: 13px; font-weight: 700; cursor: pointer;
  }
  .p-messenger__back:active { transform: scale(.97); }
  .p-messenger__title {
    font-family: var(--font-display); font-size: 27px; font-weight: 700;
    line-height: 1.1; color: var(--ink-900);
  }

  /* The Calls / Contacts / Settings tab area — a scroll container that holds
     a list, or a centred placeholder. */
  .portal.is-messenger .p-messenger__ph {
    display: block; flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden;
    background: var(--white);
  }
  .portal.is-messenger .p-messenger__ph[hidden] { display: none; }
  .p-messenger__ph-inner { padding: 48px 24px; text-align: center; }
  .p-messenger__ph-title {
    font-family: var(--font-display); font-size: 20px; font-weight: 700;
    margin-bottom: 6px; color: var(--ink-900);
  }

  /* Floating compose button on the Chats tab (hidden inside a thread). */
  .portal.is-messenger .p-messenger__fab {
    display: inline-grid; place-items: center; position: fixed; z-index: 95;
    right: 18px; bottom: calc(74px + env(safe-area-inset-bottom, 0px));
    width: 56px; height: 56px; border-radius: 50%; border: 0; cursor: pointer;
    background: var(--red-600); color: #fff; box-shadow: 0 6px 18px rgba(206, 26, 51, .4);
  }
  .portal.is-messenger .p-messenger__fab[hidden] { display: none; }
  .portal.is-messenger .p-messenger__fab:active { transform: scale(.94); }
  .portal.is-messenger .p-canvas:has(.p-msg.show-thread) .p-messenger__fab { display: none; }

  /* The four-tab bottom bar. */
  .portal.is-messenger .p-messenger__tabs {
    display: flex; flex: none; background: var(--white);
    border-top: 1px solid var(--line);
    padding-bottom: env(safe-area-inset-bottom, 0px);
    box-shadow: 0 -2px 14px rgba(14, 23, 38, .06);
  }
  .p-messenger__tab {
    flex: 1 1 0; min-width: 0; display: flex; flex-direction: column;
    align-items: center; gap: 3px; padding: 8px 2px 6px;
    background: none; border: 0; cursor: pointer; font: inherit;
    color: var(--ink-500); -webkit-tap-highlight-color: transparent;
  }
  .p-messenger__tab.is-active { color: var(--red-600); }
  .p-messenger__tab-ic { display: grid; place-items: center; }
  .p-messenger__tab-lbl { font-size: 10px; font-weight: 600; }

  /* In a thread, the messenger gives the whole screen to the conversation:
     the lead and the tab bar step aside, like a phone chat app. */
  .portal.is-messenger .p-canvas:has(.p-msg.show-thread) .p-messenger__lead,
  .portal.is-messenger .p-canvas:has(.p-msg.show-thread) .p-messenger__tabs { display: none; }

  /* The conversation's own back arrow (to the chat list) — it shares the
     .p-mobile-bar class that the LMS tabbar rule retires, so re-show it here
     with higher specificity. Without this a thread had no way back. */
  .portal.is-messenger .p-msg__head .p-mobile-bar { display: inline-flex; }
}

/* Briefly mark the message a quote jumped to, so the eye finds it. */
.p-bubble.is-found { box-shadow: 0 0 0 3px var(--gold-400); }

/* A call record or "channel opened" is the thread talking, not a person. */
.p-bubble__system {
  align-self: center; text-align: center; max-width: 90%;
  font-size: var(--body-s); color: var(--ink-500);
  background: var(--canvas); border: 1px solid var(--line);
  border-radius: var(--r-pill); padding: var(--s2) var(--s4);
}

/* =====================================================================
   The call screen — a screen, not a panel.

   Dark stage, round controls, chat beside it: the shape people already
   know from Meet and Zoom, so nobody has to learn where hang-up is
   during a call. It takes the whole window, so the portal's sidebar and
   top bar are hidden while it is up.
   ===================================================================== */

.portal.is-bare { grid-template-columns: minmax(0, 1fr); }
.portal.is-bare > .p-sidebar { display: none; }
.portal.is-bare .p-topbar { display: none; }
.portal.is-bare .p-canvas {
  padding: 0; max-width: none; width: 100%;
  height: 100dvh; min-height: 0;
}
.portal.is-bare .p-main { height: 100dvh; overflow: hidden; }

/* --- A call that survives leaving its screen ------------------------------
   The room (.p-room) is already a fixed, full-viewport surface, so keeping a
   call alive while using the app is only a matter of where its node lives
   and whether it is shown. It lives in this park so navigating away never
   removes it; it is shown full on the call screen and hidden while docked.
   The remote audio is on <body>, so hiding the room never mutes the call. */
.p-callpark { position: fixed; z-index: 1200; }
.p-callpark .p-room { z-index: 1200; }
body.call-dock .p-callpark .p-room { display: none; }
.p-call-ph { min-height: 40vh; }

/* The floating pill shown while a call is minimized. */
.p-calldock {
  position: fixed; z-index: 1250; right: 16px; bottom: 16px;
  display: flex; align-items: center; gap: 8px;
  padding: 7px 9px; border-radius: 999px;
  background: var(--red-600); color: #fff;
  box-shadow: 0 10px 30px rgba(6, 10, 15, .32);
  max-width: calc(100vw - 32px);
}
.p-calldock__open {
  display: flex; align-items: center; gap: 10px; min-width: 0;
  background: none; border: 0; color: inherit; cursor: pointer; padding: 2px 6px 2px 4px;
}
.p-calldock__dot {
  flex: none; width: 10px; height: 10px; border-radius: 50%; background: #43d17a;
  animation: p-calldot 1.6s infinite;
}
@keyframes p-calldot {
  0% { box-shadow: 0 0 0 0 rgba(67, 209, 122, .6); }
  70% { box-shadow: 0 0 0 8px rgba(67, 209, 122, 0); }
  100% { box-shadow: 0 0 0 0 rgba(67, 209, 122, 0); }
}
.p-calldock__meta { min-width: 0; text-align: left; }
.p-calldock__name { font-weight: 700; font-size: var(--label-m); line-height: 1.15; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.p-calldock__status { font-size: 11px; opacity: .9; font-variant-numeric: tabular-nums; }
.p-calldock__btn {
  flex: none; width: 40px; height: 40px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(255, 255, 255, .18); color: #fff; border: 0; cursor: pointer;
  transition: background .12s var(--ease);
}
.p-calldock__btn:hover { background: rgba(255, 255, 255, .32); }
.p-calldock__btn.is-off { background: #fff; color: var(--red-600); }
.p-calldock__btn--end { background: #fff; color: var(--red-600); }
.p-calldock__btn--end:hover { background: #ffe1e5; }
@media (max-width: 600px) {
  .p-calldock { left: 12px; right: 12px; bottom: 12px; justify-content: space-between; }
}
@media (prefers-reduced-motion: reduce) { .p-calldock__dot { animation: none; } }

/* --- The call surface ------------------------------------------------------
   Everything here is sized against the viewport rather than in fixed
   pixels, because this one screen has to work on a 360px phone held
   upright, the same phone turned sideways with 360px of HEIGHT, a tablet,
   and a 27-inch monitor — and nothing on it may ever scroll. A fixed
   112px avatar and a 56px button row are fine on two of those four and
   push the controls off the edge on the other two.

   clamp() does the work: a floor that stays usable, a viewport-relative
   middle that tracks the screen, and a ceiling so a large monitor does not
   get a cartoon. vmin rather than vh or vw, so a short landscape window
   shrinks things just as a narrow portrait one does. */
.p-callui {
  position: relative;
  height: 100%; max-height: 100%; overflow: hidden;
  display: grid; grid-template-rows: minmax(0, 1fr) auto;
  background: var(--ink-900); color: #fff;
}
.p-callui__main {
  position: relative;
  display: grid; grid-template-columns: minmax(0, 1fr); min-height: 0;
  overflow: hidden;
  transition: grid-template-columns .2s var(--ease);
}
.p-callui.chat-open .p-callui__main { grid-template-columns: minmax(0, 1fr) 340px; }
@media (prefers-reduced-motion: reduce) { .p-callui__main { transition: none; } }

.p-callui__stage {
  position: relative; min-width: 0; min-height: 0; overflow: hidden;
  display: grid; place-items: center;
  background: radial-gradient(120% 90% at 50% 0%, #22314d 0%, transparent 70%), var(--ink-900);
}
.p-callui__portrait {
  display: flex; flex-direction: column; align-items: center;
  gap: clamp(4px, 1.4vmin, 16px);
  text-align: center; padding: clamp(8px, 3vmin, 32px);
  max-width: 100%; max-height: 100%; overflow: hidden;
}
.p-callui__portrait .avatar {
  width: clamp(56px, 16vmin, 112px); height: clamp(56px, 16vmin, 112px);
  font-size: clamp(20px, 5.4vmin, 36px); flex: none;
  background: rgba(255,255,255,.1); color: #fff;
}
.p-callui__name {
  font-size: clamp(16px, 3.6vmin, 26px); font-weight: 600; margin: 0;
  /* An explicit line-height, because a clamped font-size and an inherited
     unitless line-height give a box whose height lands on a fraction of a
     pixel — which the fit test correctly read as content overflowing its
     own box. Pinning it makes the box deterministic at every size. */
  line-height: 1.25;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Their microphone, beside their name. */
.p-callui__badge {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 10px; border-radius: 999px;
  background: rgba(255,255,255,.12); color: #ffd7d7;
  font-size: var(--label-s); font-weight: 600;
}
.p-callui__badge[hidden] { display: none; }
.p-callui__status {
  margin: 0; color: #9fb0cb; font-size: var(--body-m);
  font-variant-numeric: tabular-nums;
}
.p-callui__clock { font-variant-numeric: tabular-nums; }

/* What the microphone is doing, said separately from what the call is
   doing. Amber while it is being recovered, green when it comes back, red
   when it cannot be — the same three states the person actually cares
   about, on a dark stage where a colour is quicker to read than a word. */
.p-callui__micnote {
  margin: var(--s3) 0 0; font-size: var(--body-s); color: var(--gold-400);
  max-width: 40ch;
}
.p-callui__micnote.is-ok { color: #7fd4a3; }
.p-callui__micnote.is-bad { color: #ff9b9b; }

/* The shared screen. Only on the stage while frames are actually
   arriving — the video channel exists from the moment a call connects and
   carries nothing until somebody shares. */
.p-callui__video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain; background: #000;
}
.p-callui__video[hidden] { display: none !important; }
.p-callui.has-video .p-callui__portrait { display: none; }

.p-callui__preflight {
  position: absolute; inset: 0; z-index: 2;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--s4); padding: var(--s6); text-align: center;
  /* Effectively opaque. At .92 the portrait's clock and name showed
     faintly through the panel, which reads as a rendering fault rather
     than as a deliberate scrim. */
  background: rgba(16, 24, 40, .97);
}
.p-callui__preflight .p-callui__status { max-width: 46ch; }

/* --- the control bar ---
   Six round controls at a fixed 56px with 16px between them need 416px of
   width before padding. A phone is 360. So both the buttons and the gaps
   are viewport-relative, and below 720px the name on the left and the
   empty column on the right are dropped — on a narrow screen the controls
   ARE the bar, and the other person's name is already on the stage above. */
.p-callui__bar {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  gap: var(--s4);
  padding: clamp(8px, 1.8vmin, 20px) clamp(8px, 2vw, 28px);
  padding-bottom: calc(clamp(8px, 1.8vmin, 20px) + env(safe-area-inset-bottom, 0px));
  background: #0e1726; border-top: 1px solid rgba(255,255,255,.08);
}
.p-callui__bar-side { min-width: 0; }
.p-callui__bar-right { text-align: right; }
.p-callui__bar-who { color: #9fb0cb; font-size: var(--label-s); font-weight: 600; }
.p-callui__bar-mid {
  display: flex; align-items: center; justify-content: center;
  gap: clamp(6px, 2.2vw, 20px); min-width: 0;
  /* Below about 300px even 44px buttons will not fit on one line. Wrapping
     to two rows is the right way to lose that argument: the bar gets
     taller, the stage gives up the height, and every control stays
     reachable. Clipping two of them off the edge is not. */
  flex-wrap: wrap; row-gap: clamp(6px, 1.5vmin, 12px);
}

@media (max-width: 720px) {
  .p-callui__bar { grid-template-columns: minmax(0, 1fr); }
  .p-callui__bar-side { display: none; }
}

/* --- a shared screen gets the whole surface ---
   The bar stops being a strip below the video and becomes a floating
   overlay on top of it, so the shared screen occupies the entire area
   rather than the area minus a bar. It then fades out after a few still
   seconds and returns on any movement, the way every video player behaves
   — because a shared spreadsheet is there to be read, and a permanent bar
   across the bottom of it covers the part somebody is reading. */
.p-callui.has-video .p-callui__bar {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 4;
  background: linear-gradient(to top, rgba(8, 13, 23, .94), rgba(8, 13, 23, 0));
  border-top: 0;
  transition: opacity .25s var(--ease), transform .25s var(--ease);
}
.p-callui.has-video.controls-hidden .p-callui__bar {
  opacity: 0; transform: translateY(10px); pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .p-callui.has-video .p-callui__bar { transition: none; }
  .p-callui.has-video.controls-hidden .p-callui__bar { transform: none; }
}
/* A cursor sitting on top of a hidden bar should go too. */
.p-callui.has-video.controls-hidden { cursor: none; }

.p-callui__btn {
  flex: none;
  position: relative;
  /* 44px is the smallest a finger reliably hits; 52 is the design size. */
  width: clamp(44px, 12.5vw, 52px); height: clamp(44px, 12.5vw, 52px);
  border-radius: 50%;
  border: 1px solid rgba(255,255,255,.16); background: rgba(255,255,255,.1);
  color: #fff; display: grid; place-items: center; cursor: pointer;
  transition: background .12s var(--ease), border-color .12s var(--ease);
}
.p-callui__btn:hover { background: rgba(255,255,255,.18); }
.p-callui__btn:focus-visible { outline: 2px solid var(--gold-400); outline-offset: 3px; }
.p-callui__btn[disabled] { opacity: .5; cursor: default; }
/* Muted has to be unmistakable: somebody who thinks they are muted and is
   not has a worse day than the reverse. */
.p-callui__btn.is-off { background: var(--red-600); border-color: var(--red-600); }
.p-callui__btn.is-off:hover { background: var(--red-600); }
.p-callui__btn.is-on { background: var(--blue-600); border-color: var(--blue-600); }
.p-callui__btn--end {
  width: clamp(56px, 16vw, 64px);
  background: var(--red-600); border-color: var(--red-600);
}
.p-callui__btn--end:hover { background: #9c2018; border-color: #9c2018; }
.p-callui__dot {
  position: absolute; top: 2px; right: 2px; min-width: 18px; height: 18px; padding: 0 5px;
  background: var(--gold-400); color: var(--ink-900); border-radius: var(--r-pill);
  font-size: 10px; font-weight: 700; display: grid; place-items: center;
}
@media (prefers-reduced-motion: reduce) { .p-callui__btn { transition: none; } }

/* --- the chat beside it --- */
.p-callui__chat {
  display: flex; flex-direction: column; min-height: 0; min-width: 0;
  background: var(--white); color: var(--ink-900);
  border-left: 1px solid var(--line);
}
.p-callui__chat[hidden] { display: none; }
.p-callui__chat-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--s4) var(--s5); border-bottom: 1px solid var(--line); flex: none;
}
.p-callui__chat-list {
  flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden;
  padding: var(--s5); display: flex; flex-direction: column; gap: var(--s3);
}
.p-callui__msg { max-width: 88%; align-self: flex-start; }
/* Your own messages sit on the right; the words inside them do not. A
   right-aligned paragraph breaks "…chapter 4, question 12." onto a second
   line hanging off the right edge, which reads as a formatting fault. */
.p-callui__msg.is-mine { align-self: flex-end; }
.p-callui__msg.is-mine .p-callui__msg-time { text-align: right; }
.p-callui__msg-body {
  background: var(--canvas); border: 1px solid var(--line);
  border-radius: var(--r-md); padding: var(--s3) var(--s4);
  font-size: var(--body-s); overflow-wrap: anywhere;
}
.p-callui__msg.is-mine .p-callui__msg-body {
  background: var(--blue-600); border-color: var(--blue-600); color: #fff;
}
.p-callui__msg-time { font-size: 11px; color: var(--ink-500); margin-top: 2px; }
.p-callui__chat-send {
  flex: none; display: flex; gap: var(--s3); align-items: flex-end;
  padding: var(--s4) var(--s5);
  padding-bottom: calc(var(--s4) + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--line);
}
.p-callui__chat-send .textarea { min-height: 42px; max-height: 120px; }

/* --- narrow screens ---
   The fixed pixel overrides that used to live here (48px buttons, an 88px
   avatar, a 22px name) are gone: they fought the clamp() values above and,
   being later in the file at the same specificity, won — which meant the
   careful sizing only applied on screens the media query did not match.
   Anything that scales is scaled once, in the base rules. What is left
   here is the genuine layout change, which no amount of scaling achieves. */
@media (max-width: 760px) {
  /* No room for two things at once: the chat covers the call rather than
     squeezing it into a column too narrow to read. */
  .p-callui.chat-open .p-callui__main { grid-template-columns: minmax(0, 1fr); }
  .p-callui__chat { position: absolute; inset: 0; z-index: 5; border-left: 0; }
  .p-callui__bar { grid-template-columns: minmax(0, 1fr); justify-items: center; }
}

/* A phone turned sideways has about 350px of height, and a browser's own
   bars take a chunk of that. Hide what is decoration at that size — the
   status line and the microphone note still appear, the avatar does not. */
@media (max-height: 420px) and (orientation: landscape) {
  .p-callui__portrait .avatar { display: none; }
  .p-callui__portrait { gap: 2px; padding: 4px; }
}

/* Connected. The ring is the only thing on a voice call that says the
   line is actually open — without it "Calling…" disappearing is the sole
   difference between ringing and talking. */
.p-callui.is-connected .p-callui__portrait .avatar {
  box-shadow: 0 0 0 3px rgba(255,255,255,.12), 0 0 0 10px rgba(255,255,255,.05);
}

/* The call is over but the tab is still here — a browser that refuses to
   close it, or a moment before it does. Say so and stop pretending the
   controls still do anything. */
/* Nothing on a call screen scrolls, at any size. The three rules below are
   the belt to the layout's braces: even if one child miscalculates by a
   pixel, the page itself cannot grow a scrollbar. */
body.paras-bare {
  background: var(--ink-900);
  overflow: hidden; height: 100dvh;
  overscroll-behavior: none;   /* and no rubber-banding on a phone */
}
.portal.is-bare { min-height: 0; height: 100dvh; overflow: hidden; }
.p-callui.is-over .p-callui__stage { opacity: .45; }
.p-callui.is-over .p-callui__btn:not(.p-callui__btn--end) {
  opacity: .4; pointer-events: none;
}
.p-callui.is-over .p-callui__status { color: #c4d0e4; }

@media print { .p-callui { display: none !important; } }

/* --- the CA Challenge snapshot on a lead -------------------------------- */
/* The counsellor reads this before they dial, so it sits with the contact
   details rather than below the fold. */
.p-snap { margin-top: var(--s3); padding: var(--s3); border-radius: var(--r-sm);
  background: var(--paper, #FAF9F6); border: 1px solid var(--line);
  display: flex; flex-direction: column; gap: 6px; }
.p-snap p { margin: 0; }

/* =====================================================================
   The phone: the app layout

   Below 900px the portal stops being a console with a sidebar and becomes
   the app in the Figma file: an app bar at the top, five destinations
   along the bottom, and everything else behind Menu.

   The sidebar drawer that used to live here is gone rather than hidden.
   Two navigations for the same twenty-seven links — a hamburger drawer and
   a tab bar — is one too many, and the hamburger was the one nobody could
   see the point of.
   ===================================================================== */

.p-tabbar { display: none; }
.p-sheet { display: none; }

@media (max-width: 900px) {

  /* --- The bar ------------------------------------------------------ */
  .p-tabbar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 90;
    display: flex; align-items: stretch;
    background: var(--white);
    box-shadow: 0 -2px 14px rgba(14, 23, 38, .08);
    padding-top: 8px;
    /* The home indicator on a modern phone sits where a 5th tab's label
       would be, so the bar ends above it rather than under it. */
    padding-bottom: calc(8px + env(safe-area-inset-bottom, 0px));
  }
  .portal.is-bare .p-tabbar { display: none; }

  .p-tabbar__item {
    flex: 1 1 0; min-width: 0;
    display: flex; flex-direction: column; align-items: center; gap: 4px;
    padding: 2px 2px 0; position: relative;
    background: none; border: 0; cursor: pointer;
    text-decoration: none; color: var(--ink-500, #8A94A6);
    font: inherit;
    -webkit-tap-highlight-color: transparent;
  }
  .p-tabbar__pill {
    display: grid; place-items: center;
    padding: 4px 16px; border-radius: 999px;
    transition: background .16s var(--ease), color .16s var(--ease);
  }
  .p-tabbar__pill .icon { display: block; }
  .p-tabbar__label {
    font-size: 9px; font-weight: 500; line-height: 12px;
    letter-spacing: .01em;
    max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }

  /* Active is a filled pill, not a colour swap. At 21px an icon that only
     changes hue is genuinely hard to pick out at arm's length — the shape
     behind it is what reads across a room. */
  .p-tabbar__item.is-active { color: var(--ink-900); }
  .p-tabbar__item.is-active .p-tabbar__pill { background: var(--blue-50, #E9F0F9); color: var(--ink-900); }
  .p-tabbar__item.is-active .p-tabbar__label { font-weight: 600; }

  .p-tabbar__dot {
    position: absolute; top: 2px; right: 50%; margin-right: -18px;
    width: 8px; height: 8px; border-radius: 999px;
    background: var(--red-600, #C8322B);
    box-shadow: 0 0 0 2px var(--white);
  }
  .p-tabbar__dot.hidden { display: none; }

  @media (prefers-reduced-motion: reduce) {
    .p-tabbar__pill { transition: none; }
  }

  /* --- The Menu sheet ----------------------------------------------- */
  .p-sheet { display: block; position: fixed; inset: 0; z-index: 130; pointer-events: none; }
  .p-sheet__scrim {
    position: absolute; inset: 0; background: rgba(22, 35, 58, .45);
    opacity: 0; transition: opacity .2s var(--ease);
  }
  .p-sheet__panel {
    position: absolute; left: 0; right: 0; bottom: 0;
    max-height: 88vh; display: flex; flex-direction: column;
    background: var(--canvas, #F4F6F9);
    border-radius: 20px 20px 0 0;
    transform: translateY(101%);
    transition: transform .26s var(--ease);
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  .portal.menu-open .p-sheet { pointer-events: auto; }
  .portal.menu-open .p-sheet__scrim { opacity: 1; }
  .portal.menu-open .p-sheet__panel { transform: none; }
  @media (prefers-reduced-motion: reduce) {
    .p-sheet__scrim, .p-sheet__panel { transition: none; }
  }

  .p-sheet__head {
    display: flex; align-items: center; gap: var(--s3);
    padding: var(--s5) var(--s4) var(--s4);
    border-bottom: 1px solid var(--line);
    background: var(--white);
    border-radius: 20px 20px 0 0;
  }
  .p-sheet__me { display: flex; align-items: center; gap: var(--s3); min-width: 0; flex: 1; }
  .p-sheet__name { font-size: 15px; font-weight: 600; color: var(--ink-900); }
  .p-sheet__sub { font-size: 11px; color: var(--ink-500); }

  .p-sheet__list { overflow-y: auto; -webkit-overflow-scrolling: touch; padding: var(--s3) var(--s4); }
  .p-sheet__row {
    display: flex; align-items: center; gap: 11px;
    padding: 10px 12px; margin-bottom: 6px;
    border-radius: 14px; background: var(--white);
    border: 1px solid var(--line);
    text-decoration: none; color: var(--ink-900);
  }
  .p-sheet__badge {
    flex: 0 0 34px; height: 34px; display: grid; place-items: center;
    border-radius: 11px; background: var(--blue-50, #E9F0F9); color: var(--ink-900);
  }
  .p-sheet__label { flex: 1; min-width: 0; font-size: 13px; font-weight: 600; }
  .p-sheet__row > .icon { color: var(--ink-300, #8A94A6); flex: none; }
  .p-sheet__row.is-active { border-color: var(--ink-900); }
  .p-sheet__row.is-locked { opacity: .55; }
  .p-sheet__phase {
    font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
    color: var(--ink-500); flex: none;
  }
  .p-sheet__out {
    margin: var(--s2) var(--s4) var(--s5);
    padding: 12px; border-radius: 14px;
    background: var(--white); border: 1px solid var(--line);
    color: var(--red-600, #C8322B); font: inherit; font-size: 13px; font-weight: 600;
    cursor: pointer;
  }

  /* --- What the bar displaces --------------------------------------- */
  /* The drawer and its trigger are retired on phones; Menu replaces both.
     Except where there is no bar — a role whose screens are all still in a
     later phase — in which case the drawer is all there is and stays. */
  .portal:not(.no-tabbar) .p-sidebar { display: none; }
  .portal:not(.no-tabbar) .p-mobile-bar { display: none; }
  .portal:not(.no-tabbar).nav-open::after { content: none; }
  .portal.no-tabbar .p-canvas { padding-bottom: var(--s5); }

  /* Nothing should end underneath the bar — a Save button hidden behind
     the navigation is the classic way to make a form look broken. */
  .p-canvas { padding-bottom: calc(86px + env(safe-area-inset-bottom, 0px)); }
  .p-toasts { bottom: calc(86px + env(safe-area-inset-bottom, 0px)); }
  .p-nudge { bottom: calc(86px + env(safe-area-inset-bottom, 0px)); }

  /* --- The app bar --------------------------------------------------- */
  /* Sticky, white and light: the design puts the screen's name at the top
     left with the two things you reach for at the top right. */
  .p-topbar {
    position: sticky; top: 0; z-index: 80;
    background: var(--white);
    border-bottom: 1px solid var(--line);
    padding-block: var(--s3);
  }
  .p-topbar__title { font-size: 17px; font-weight: 600; letter-spacing: -.01em; }
  .p-topbar .p-iconbtn {
    border-radius: 999px; border: 1px solid var(--line); background: var(--white);
  }

  /* --- Density ------------------------------------------------------- */
  /* The console is set for a laptop. On a phone the design tightens the
     type and the padding and puts three figures in a row where the desktop
     puts four across the full width. */
  .p-stats { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 9px; }
  /* Four figures in a three-wide grid leaves the fourth stranded on its
     own row at a third of the width, with two empty cells beside it —
     which is how the attendance screen looked. Exactly four becomes a
     clean 2x2; three or five keep three to a row. A quantity query rather
     than a class, so no screen has to remember to ask for it. */
  .p-stats:has(.p-stat:nth-child(4)):not(:has(.p-stat:nth-child(5))) {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .p-stat { padding: 11px; border-radius: 14px; }
  .p-stat__label { font-size: 10px; letter-spacing: .06em; text-transform: uppercase; }
  .p-stat__value { font-size: 24px; line-height: 28px; letter-spacing: -.02em; }
  .p-stat__delta { font-size: 11px; margin-top: 3px; }

  .p-card { border-radius: 16px; }
  .p-card__head { padding: var(--s4) var(--s5); }
  .p-card__body { padding: var(--s5); }
  .p-card__title { font-size: 15px; }
}

/* Three tiles is the design's intent and it holds down to about a 390px
   phone, which is most of them. Below that the columns stop being tiles
   and start being three cramped slivers, so they drop to two. */
@media (max-width: 360px) {
  .p-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .p-tabbar__pill { padding-inline: 13px; }
}

/* =====================================================================
   Three components lifted from the app design in Figma

   These are deliberately width-agnostic. They are not "the mobile
   versions" of anything — a filter rail and a tappable row are better
   than a <select> and a two-column table on a laptop too, and keeping one
   implementation means the phone and the desktop cannot drift apart.
   Only the sizing tightens below the breakpoint.
   ===================================================================== */

/* --- Progress, with its figure beside it ------------------------------ */
.p-prog { display: flex; flex-direction: column; gap: 6px; }
.p-prog__top {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3);
}
.p-prog__label { font-size: var(--label-s); color: var(--ink-500); font-weight: 600; min-width: 0; }
.p-prog__value {
  font-size: var(--label-m); font-weight: 700; color: var(--ink-900);
  font-variant-numeric: tabular-nums; flex: none;
}

/* --- Filter chips ----------------------------------------------------- */
/* A rail, not a wrap: a filter row that grows to three lines pushes the
   content it filters off the screen. */
.p-chips {
  display: flex; gap: 7px; overflow-x: auto; scrollbar-width: none;
  min-width: 0; max-width: 100%;
  -webkit-overflow-scrolling: touch;
  padding-bottom: 2px; margin-bottom: var(--s5);
}
.p-chips::-webkit-scrollbar { display: none; }

.p-chip {
  flex: none; display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 13px; border-radius: var(--r-pill);
  border: 1px solid var(--line); background: var(--white);
  font: inherit; font-size: var(--label-s); font-weight: 600; color: var(--ink-500);
  cursor: pointer; white-space: nowrap;
  transition: background .14s var(--ease), color .14s var(--ease), border-color .14s var(--ease);
  -webkit-tap-highlight-color: transparent;
}
.p-chip:hover { border-color: var(--ink-300); color: var(--ink-900); }
.p-chip.is-on {
  background: var(--ink-900); border-color: var(--ink-900); color: #fff;
}
.p-chip.is-on:hover { color: #fff; }
.p-chip__n {
  font-size: 10px; font-weight: 700; line-height: 1;
  padding: 3px 6px; border-radius: var(--r-pill);
  background: var(--canvas); color: var(--ink-500);
}
.p-chip.is-on .p-chip__n { background: rgba(255, 255, 255, .18); color: #fff; }
@media (prefers-reduced-motion: reduce) { .p-chip { transition: none; } }

/* --- The tappable row -------------------------------------------------- */
.p-lrows { display: flex; flex-direction: column; gap: 6px; }

.p-lrow {
  display: flex; align-items: center; gap: 11px; width: 100%;
  padding: 10px 12px; border-radius: 14px;
  background: var(--white); border: 1px solid var(--line);
  text-align: left; text-decoration: none; color: var(--ink-900);
  font: inherit;
  transition: border-color .14s var(--ease), box-shadow .14s var(--ease);
  -webkit-tap-highlight-color: transparent;
}
a.p-lrow, button.p-lrow { cursor: pointer; }
a.p-lrow:hover, button.p-lrow:hover { border-color: var(--ink-300); box-shadow: var(--shadow-sm); }
@media (prefers-reduced-motion: reduce) { .p-lrow { transition: none; } }

.p-lrow__badge {
  flex: 0 0 34px; height: 34px; display: grid; place-items: center;
  border-radius: 11px;
}
.p-lrow__badge--navy  { background: var(--blue-50);   color: var(--blue-700); }
.p-lrow__badge--green { background: var(--green-100); color: var(--green-600); }
.p-lrow__badge--amber { background: var(--gold-50);   color: var(--gold-700); }
.p-lrow__badge--red   { background: var(--red-100);   color: var(--red-600); }

.p-lrow__text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.p-lrow__title { font-size: var(--label-m); font-weight: 600; line-height: 1.35; }
.p-lrow__sub { font-size: var(--label-s); color: var(--ink-500); line-height: 1.35; }
.p-lrow__value {
  flex: none; font-size: var(--label-m); font-weight: 600; color: var(--ink-600, var(--ink-500));
  font-variant-numeric: tabular-nums;
}
.p-lrow__chev { flex: none; color: var(--ink-300); }

@media (max-width: 900px) {
  /* Edge to edge like every other block on a phone, and a slightly
     shorter row so more than five fit above the fold. */
  .p-canvas > .p-chips,
  .p-canvas > .p-lrows { padding-inline: var(--s4); }
  .p-lrow { padding: 9px 11px; }
  .p-lrow__badge { flex-basis: 32px; height: 32px; border-radius: 10px; }
  .p-lrow__title { font-size: 13px; }
  .p-lrow__sub { font-size: 11px; }
}

/* --- The attendance ring ---------------------------------------------- */
.p-donutwrap { position: relative; flex: none; }
.p-donut { display: block; }
.p-donut__track { stroke: var(--canvas); }
.p-donut__arc {
  transition: stroke-dashoffset .6s var(--ease);
}
.p-donut__arc--green { stroke: var(--green-600); }
.p-donut__arc--amber { stroke: var(--gold-400); }
.p-donut__arc--red   { stroke: var(--red-600); }
.p-donut__arc--navy  { stroke: var(--ink-900); }
@media (prefers-reduced-motion: reduce) { .p-donut__arc { transition: none; } }

.p-donut__mid {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1px; pointer-events: none;
}
.p-donut__value {
  font-size: 24px; line-height: 1; font-weight: 700; letter-spacing: -.02em;
  color: var(--ink-900); font-variant-numeric: tabular-nums;
}
.p-donut__sub {
  font-size: 9px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-500);
}

/* --- A month, laid out as a month -------------------------------------- */
.p-month {
  display: grid; grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 4px; align-items: center; justify-items: center;
}
.p-month__dow {
  font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-300); padding-bottom: 2px;
}
.p-month__pad { height: 1px; }

.p-month__day {
  width: 100%; max-width: 38px; aspect-ratio: 1 / .92;
  display: grid; place-items: center;
  border: 0; background: none; font: inherit;
  font-size: 12px; font-weight: 600; color: var(--ink-300);
  border-radius: 9px;
  font-variant-numeric: tabular-nums;
}
button.p-month__day { cursor: pointer; }
.p-month__day.is-future { color: var(--ink-300); opacity: .5; }
.p-month__day.is-today { outline: 1.5px solid var(--ink-900); outline-offset: -1.5px; }

.p-month__day.is-present { background: var(--green-100); color: var(--green-600); }
.p-month__day.is-late    { background: var(--gold-50);   color: var(--gold-700); }
.p-month__day.is-absent  { background: var(--red-100);   color: var(--red-600); }
.p-month__day.is-excused { background: var(--blue-50);   color: var(--blue-700); }

.p-month__key {
  display: flex; flex-wrap: wrap; gap: var(--s4) var(--s5);
  margin-top: var(--s4); padding-top: var(--s4); border-top: 1px solid var(--line);
}
.p-month__keyitem {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--label-s); color: var(--ink-500);
}
.p-month__dot { width: 8px; height: 8px; border-radius: 999px; display: block; }
.p-month__dot--present { background: var(--green-600); }
.p-month__dot--late    { background: var(--gold-400); }
.p-month__dot--absent  { background: var(--red-600); }
.p-month__dot--excused { background: var(--blue-700); }

/* The ring and its figures sit side by side; on a narrow phone the
   figures go underneath rather than squeezing to two words a line. */
.p-donutrow { display: flex; align-items: center; gap: var(--s5); }
.p-donutrow__stats { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 7px; }
.p-donutrow__stat { display: flex; align-items: center; gap: 8px; font-size: var(--label-s); }
.p-donutrow__stat b { margin-left: auto; color: var(--ink-900); font-variant-numeric: tabular-nums; }
/* The ring is 88px, which leaves roughly 230px for the figures beside it
   even on a 390px phone — stacking them there only produced a centred
   donut, a gap, and four lines of air. It stacks on the genuinely narrow
   handsets and nowhere else. */
@media (max-width: 340px) {
  .p-donutrow { flex-direction: column; align-items: stretch; gap: var(--s4); }
  .p-donutwrap { align-self: center; }
}

/* =====================================================================
   Marking a register

   This is the one screen in the product done standing up, in front of a
   room, almost always on a phone. Everything below is in service of that:
   bigger targets, fewer words, and the save button where a thumb is.
   ===================================================================== */

/* The summary strip above the roll. Figure first, label under it — the
   count is what is being read, the word is just what it counts. */
.p-stat--tight { padding: 9px 10px; display: flex; flex-direction: column; gap: 1px; }
.p-stat--tight .p-stat__label { margin-bottom: 0; font-size: 10px; letter-spacing: .05em; text-transform: uppercase; }
.p-stat__value--green { color: var(--green-600); }
.p-stat__value--red   { color: var(--red-600); }
.p-stat__value--amber { color: var(--gold-700); }
.p-stat__value--navy  { color: var(--blue-700); }
.p-stat__value--quiet { color: var(--ink-300); }

/* One student, one row. A card rather than a ruled line, because a ruled
   list of 46 names is hard to keep your place in when you are reading one
   name off a room and tapping back at the screen. */
.p-att-row {
  gap: var(--s3); padding: 9px 11px;
  border: 1px solid var(--line); border-bottom: 1px solid var(--line);
  border-radius: 14px; background: var(--white); margin-bottom: 6px;
}
.p-att-row:hover { background: var(--white); border-color: var(--ink-300); }
.p-att-row__name .label-m { font-size: var(--label-m); line-height: 1.3; }

/* Default to the words; swap to initials only where they will not fit. */
.p-seg__short { display: none; }
.p-seg button.is-on[data-v="excused"] { background: var(--blue-700); color: #fff; }

@media (max-width: 760px) {
  .p-seg__long { display: none; }
  .p-seg__short { display: inline; }
  /* A 44px target is the smallest a thumb hits reliably; these are the
     only controls on the screen, so they get it. */
  .p-seg button { padding: 10px 0; min-width: 40px; text-align: center; font-size: 13px; }
  .p-att-row { padding: 8px 10px; gap: 9px; }
  /* The index number and the prior-attendance column are reference, not
     the job. They go, and the row gets its width back for the controls. */
  .p-att-row > span.body-s.quiet:first-child { display: none; }
  /* The running index and the student's prior percentage are reference,
     not the job in hand, and between them they were costing the name its
     width — "Aarav Sharma" wrapped onto two lines. Both go; the name and
     the four buttons are what this screen is for. */
  .p-att-row__prior { display: none; }
  .p-att-row__name .label-m,
  .p-att-row__name .body-s { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .p-stats:has(.p-stat--tight) { grid-template-columns: repeat(5, minmax(0, 1fr)) !important; gap: 5px; }
  .p-stat--tight { padding: 7px 5px; text-align: center; align-items: center; }
  .p-stat--tight .p-stat__value { font-size: 19px; line-height: 23px; }
  .p-stat--tight .p-stat__label { font-size: 9px; letter-spacing: .03em; }
}

/* The save button follows you down the register instead of living at the
   bottom of 46 rows. It clears the tab bar rather than sitting on it. */
@media (max-width: 900px) {
  .p-savebar {
    position: sticky; z-index: 70;
    bottom: calc(86px + env(safe-area-inset-bottom, 0px));
    display: flex; gap: var(--s3); align-items: center;
    padding: var(--s3) var(--s4);
    margin: var(--s5) 0 0;
    background: color-mix(in srgb, var(--canvas) 86%, transparent);
    backdrop-filter: blur(8px);
    border-top: 1px solid var(--line);
  }
  .p-savebar .btn { flex: 1; justify-content: center; }
}

/* Shown only where the phone layout is in force. */
.p-mob-only { display: none; }
@media (max-width: 900px) { .p-mob-only { display: flex; } }

/* --- A line in the day ------------------------------------------------- */
.p-srow {
  display: flex; align-items: center; gap: 11px; width: 100%;
  padding: 9px 12px; border-radius: 14px;
  background: var(--white); border: 1px solid var(--line);
  text-align: left; font: inherit; color: var(--ink-900);
}
button.p-srow { cursor: pointer; }
button.p-srow:hover { border-color: var(--ink-300); }
.p-srow__time {
  flex: 0 0 46px; display: flex; flex-direction: column; line-height: 1.3;
  font-variant-numeric: tabular-nums;
}
.p-srow__time b { font-size: var(--label-m); font-weight: 700; }
.p-srow__time span { font-size: var(--label-s); color: var(--ink-300); }
.p-srow__rule { flex: none; width: 3px; align-self: stretch; min-height: 30px; border-radius: 2px; display: block; }
.p-srow__rule--navy  { background: var(--blue-700); }
.p-srow__rule--green { background: var(--green-600); }
.p-srow__rule--amber { background: var(--gold-400); }
.p-srow__rule--red   { background: var(--red-600); }
.p-srow__text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.p-srow__title { font-size: var(--label-m); font-weight: 600; line-height: 1.35; }
.p-srow__sub { font-size: var(--label-s); color: var(--ink-500); line-height: 1.35; }
@media (max-width: 900px) {
  .p-canvas > .p-srows { padding-inline: var(--s4); }
  .p-srow__title { font-size: 13px; }
  .p-srow__sub { font-size: 11px; }
}
.p-srows { display: flex; flex-direction: column; gap: 6px; }

/* =====================================================================
   Brand colour, on the data rather than around it

   A commerce institute's own vernacular is the ruled ledger: a column of
   figures under a coloured rule. So the tiles keep their white ground and
   hairline border, take a rule along the top edge, and spend the brand on
   the figure itself — which is the thing anybody opening this app is
   actually here to read.

   Three white boxes with pastel fills would have been the easier answer
   and would have looked like every other dashboard. Colour on the number
   is both louder and quieter: the screen reads as colourful, the chrome
   stays out of the way.
   ===================================================================== */

/* Scoped away from .p-stat--tight throughout. Those are the register's
   tally, where the colours are already carrying meaning — present green,
   absent red, late amber — and a brand hue laid over the top turned
   "1 late" into something that looked like an error. */
.p-stats .p-stat:not(.p-stat--tight) {
  position: relative; overflow: hidden;
  box-shadow: none;                 /* the rule does the work the shadow did */
  border-color: var(--line);
}
.p-stats .p-stat:not(.p-stat--tight)::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 3px;
  background: var(--paras-blue);
}
/* Red is the second voice, not an alternating stripe: it lands on the
   third tile so a row of three reads blue · blue · red rather than as a
   pattern. */
.p-stats .p-stat:not(.p-stat--tight):nth-child(3n+3)::before { background: var(--paras-red); }

.p-stats .p-stat:not(.p-stat--tight) .p-stat__value { color: var(--paras-blue-ink); }
.p-stats .p-stat:not(.p-stat--tight):nth-child(3n+3) .p-stat__value { color: var(--paras-red); }

/* The first figure in a row is the headline — on every screen in this
   product the leading stat is the one the page is about. It gets the
   filled treatment, and it is the only filled thing on the page. */
.p-stats .p-stat:not(.p-stat--tight):first-child {
  background: var(--paras-blue); border-color: var(--paras-blue);
}
.p-stats .p-stat:not(.p-stat--tight):first-child::before { background: rgba(255, 255, 255, .34); }
.p-stats .p-stat:not(.p-stat--tight):first-child .p-stat__value { color: #fff; }
.p-stats .p-stat:not(.p-stat--tight):first-child .p-stat__label { color: #D6E8F1; }
.p-stats .p-stat:not(.p-stat--tight):first-child .p-stat__delta { color: #D6E8F1; }
.p-stats a.p-stat:not(.p-stat--tight):first-child:hover { background: var(--paras-blue-ink); }

/* Sentence case, not tracked capitals. A 10px all-caps label over every
   figure is chrome pretending to be information; at this size the word
   is a caption and should read like one. */
.p-stat__label { text-transform: none; letter-spacing: 0; font-size: var(--label-s); }

/* --- The brand elsewhere ---------------------------------------------- */
/* The nav's active pill picks up the brand blue rather than a grey-blue
   tint, so the thing marking where you are is the thing carrying the
   institute's colour. */
.p-tabbar__item.is-active { color: var(--paras-blue-ink); }
.p-tabbar__item.is-active .p-tabbar__pill {
  background: var(--paras-blue-wash); color: var(--paras-blue-ink);
}
.p-tabbar__dot { background: var(--paras-red); }

.p-lrow__badge--navy { background: var(--paras-blue-wash); color: var(--paras-blue-ink); }
.p-srow__rule--navy { background: var(--paras-blue); }
.p-donut__arc--navy { stroke: var(--paras-blue); }
.p-chip.is-on { background: var(--paras-blue-ink); border-color: var(--paras-blue-ink); }
.p-sheet__badge { background: var(--paras-blue-wash); color: var(--paras-blue-ink); }
.p-sheet__row.is-active { border-color: var(--paras-blue); box-shadow: inset 2px 0 0 var(--paras-blue); }

/* The sidebar brand mark, which was a flat navy square. */
.p-sidebar__mark { background: var(--paras-red); }

/* Small text needs its own leading. These captions were inheriting the
   body line-height, so an 11px label sat on a 21px line and the tile grew
   a band of empty space under every figure. */
.p-stat__label { line-height: 1.35; }
.p-stat__delta { line-height: 1.35; }
.p-lrow__title, .p-lrow__sub,
.p-srow__title, .p-srow__sub { line-height: 1.35; }
.p-month__dow { line-height: 1.2; }
.p-prog__label, .p-prog__value { line-height: 1.3; }
.p-msg__item-name, .p-msg__item-time, .p-msg__item-prev { line-height: 1.35; }
.p-sheet__name, .p-sheet__sub, .p-sheet__label { line-height: 1.35; }

@media (max-width: 900px) {
  /* The app bar's title tracks the heading scale rather than sitting at a
     fixed 17px while everything around it steps down. */
  .p-topbar__title { font-size: var(--h5); line-height: var(--h5-lh); }
}

/* =====================================================================
   Leads

   The pipeline is the screen the office lives in during admissions, and
   it was the plainest thing in the product: three grey dropdowns, a date
   field, and columns of white rectangles.
   ===================================================================== */

/* The filter row under the source rail. The owner picker is the one
   control here that genuinely wants a dropdown — a counsellor list is
   names, and names belong in a list, not a rail. */
.p-leadbar {
  display: flex; align-items: center; gap: var(--s4);
  flex-wrap: wrap; margin-bottom: var(--s4);
}
/* A flex item defaults to min-width:auto, so a scrolling rail placed in
   a flex row refuses to shrink below its content and pushes the whole
   page sideways instead of scrolling inside itself. */
.p-leadbar .p-chips { margin-bottom: 0; flex: 1 1 auto; min-width: 0; }
.p-leadbar__owner { display: flex; align-items: center; gap: var(--s2); }
.p-leadbar__owner .select, .p-leadbar__owner select { min-width: 130px; }

/* A column of the board. Tinted ground so the cards read as sitting in
   something, and a sticky head so you still know which stage you are
   looking at halfway down a column of forty. */
.p-board__col {
  background: var(--canvas); border-radius: var(--r-lg);
  flex-basis: 252px;
}
.p-board__head {
  position: sticky; top: 0; z-index: 2;
  background: var(--canvas);
  padding-bottom: var(--s2); margin-bottom: var(--s1);
  border-bottom: 1px solid var(--line);
}
.p-board__head .label-m { font-size: var(--label-s); font-weight: 700; }

/* The card. A rule down the left edge rather than a flat white box —
   the same ledger device the tiles and the schedule rows use, so the
   pipeline belongs to the same product as the rest of the app. */
.p-board__card {
  border-radius: 12px; padding: 10px 11px 10px 13px;
  border-color: var(--line);
  position: relative; overflow: hidden;
  transition: border-color .14s var(--ease), box-shadow .14s var(--ease);
}
.p-board__card::before {
  content: ""; position: absolute; inset: 0 auto 0 0; width: 3px;
  background: var(--paras-blue);
}
.p-board__card:hover { border-color: var(--ink-300); box-shadow: var(--shadow-sm); }
.p-board__card .label-m { font-size: var(--label-s); font-weight: 600; }
.p-board__card .body-s { font-size: 11px; }
@media (prefers-reduced-motion: reduce) { .p-board__card { transition: none; } }

/* Won and lost columns say so in the rule, so a glance down the board
   reads as progress rather than as five identical stacks. */
.p-board__col--won .p-board__card::before  { background: var(--green-600); }
.p-board__col--lost .p-board__card::before { background: var(--ink-300); }
.p-board__col--lost .p-board__card { opacity: .72; }

@media (max-width: 900px) {
  .p-canvas > .p-leadbar,
  .p-canvas > .p-chips,
  .p-canvas > .p-board { padding-inline: var(--s4); }
  .p-board__col { flex-basis: 232px; }
  /* Two controls side by side do not fit a phone: the rail could not
     shrink past its content and pushed the page sideways instead of
     scrolling inside itself. They stack, the rail takes the full width
     it needs to scroll in, and the owner picker gets its own line. */
  .p-leadbar { flex-direction: column; align-items: stretch; gap: var(--s3); }
  .p-leadbar .p-chips { width: 100%; }
  .p-leadbar__owner { width: 100%; }
  .p-leadbar__owner .select, .p-leadbar__owner select { flex: 1; width: auto; min-width: 0; }
}

/* =====================================================================
   Buttons that fit the box they are in

   Every .btn is nowrap by default, which is right for "Save" and wrong
   for "Auto-grade the objective questions" in a 350px dialog. The rules
   below make a long label wrap inside a full-width button rather than
   push its neighbours out of the container.
   ===================================================================== */

.btn { max-width: 100%; }

@media (max-width: 600px) {
  /* A dialog's actions stack. The primary stays last, nearest the thumb
     and in the same place as the save bar everywhere else in the app. */
  .p-modal__foot { flex-direction: column; align-items: stretch; gap: var(--s2); }
  .p-modal__foot .btn {
    width: 100%; justify-content: center;
    white-space: normal; line-height: 1.25; padding-block: 12px;
  }
  .p-modal__panel { max-height: 92vh; }
  .p-modal { padding: var(--s3); }

  /* A plain .row does not wrap — it is opt-in via .row--wrap — so a pair
     of buttons dropped into one overflowed instead of stacking. */
  .p-canvas .row:not(.row--between) { flex-wrap: wrap; }
  .p-head__row .row { flex-wrap: wrap; }

  /* Long labels anywhere narrow: wrap rather than spill. */
  .p-card__head .btn, .p-head__row .btn, .p-toolbar .btn { white-space: normal; }
}

/* When the browser scrolls something into view by itself — tabbing to a
   field, following an anchor, or a validation error taking focus — it
   aligns the element to the very edge of the viewport, which on a phone
   parks it underneath the sticky app bar or behind the tab bar. The
   element is focused, invisible, and the person is typing into something
   they cannot see. scroll-padding reserves the bars' space so automatic
   scrolling stops short of them. */
@media (max-width: 900px) {
  html {
    scroll-padding-top: 62px;
    scroll-padding-bottom: calc(96px + env(safe-area-inset-bottom, 0px));
  }
}

/* --- The register, grouped so the glance is a glance -------------------- */
/* A light header over each bucket. Not a card, not a toolbar — a quiet
   label that says how many rows sit under it, so the faculty member reads
   "Not checked in · 5" and knows the size of the job before scrolling. */
.p-att-head {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s3) 2px var(--s2);
  font-size: var(--label-s); font-weight: 700; color: var(--ink-500);
}
.p-att-head:first-child { padding-top: 0; }
.p-att-head__label { letter-spacing: .01em; }

/* The clean check-ins collapse. The summary row is the same quiet header
   with a disclosure triangle, and it carries its own count so nothing is
   hidden by closing it. */
.p-att-fold { margin-top: var(--s2); }
.p-att-fold > summary {
  cursor: pointer; list-style: none; user-select: none;
  border-top: 1px solid var(--line);
}
.p-att-fold > summary::-webkit-details-marker { display: none; }
.p-att-head--fold { padding-top: var(--s3); }
.p-att-head--fold::after {
  content: ""; width: 7px; height: 7px; margin-left: 2px;
  border-right: 2px solid var(--ink-300); border-bottom: 2px solid var(--ink-300);
  transform: rotate(-45deg); transition: transform .15s var(--ease);
}
.p-att-fold[open] > summary .p-att-head--fold::after { transform: rotate(45deg); }
@media (prefers-reduced-motion: reduce) { .p-att-head--fold::after { transition: none; } }

/* The check-in tags under a name — "new device", "scanned · away". They
   wrap rather than stretch the row, and sit tight under the roll number. */
.p-att-row__tags { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 3px; }


/* --- Stat tiles inside a modal ---------------------------------------- */
/* Four money figures across a 560px dialog left each tile ~115px wide and
   the number wrapped. Two columns give each tile the room to hold
   "₹1,20,000" on one line, and the brand hero/accent is dropped because a
   fee breakdown is read, not dramatised. */
.p-modal__body .p-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s4); }

.p-modal__body .p-stats .p-stat,
.p-modal__body .p-stats .p-stat:not(.p-stat--tight):first-child {
  background: var(--white); border-color: var(--line);
}
.p-modal__body .p-stats .p-stat:not(.p-stat--tight)::before { display: none; }
.p-modal__body .p-stats .p-stat .p-stat__value,
.p-modal__body .p-stats .p-stat:not(.p-stat--tight):first-child .p-stat__value,
.p-modal__body .p-stats .p-stat:not(.p-stat--tight):nth-child(3n+3) .p-stat__value {
  color: var(--ink-900);
}
.p-modal__body .p-stats .p-stat:not(.p-stat--tight):first-child .p-stat__label,
.p-modal__body .p-stats .p-stat .p-stat__label { color: var(--ink-500); }
/* The figure itself can shrink a touch here — a fee total is longer than a
   dashboard "92%", and the dialog is narrow. */
.p-modal__body .p-stat__value { font-size: var(--h3); line-height: var(--h3-lh); }

/* --- Crispness on the large type -------------------------------------- */
/* Big figures and headings set tighter: at 30px a stat value with default
   tracking reads loose, and slight negative letter-spacing is most of what
   "sharp" means here. Sizes are unchanged. */
.p-stat__value { letter-spacing: -.015em; }
.p-card__title, .p-topbar__title, .p-head__title { letter-spacing: -.01em; }
.p-donut__value, .p-ringwrap__value { letter-spacing: -.02em; }

/* =====================================================================
   Every table becomes cards on a phone

   The portal has ninety-odd tables, and a table is the one thing that
   cannot be made to fit a 390px screen — it either scrolls sideways,
   hiding half its columns, or shrinks its type past reading. So below the
   phone breakpoint the table stops being a table: each row becomes a card,
   each cell a labelled line, and the first column leads as the card's
   title. One rule here reshapes all ninety at once, the way the app design
   turns the same data into Row/List cards.

   Done in CSS off the data-label the table() helper now writes, so no
   screen had to be touched and the desktop table is completely unchanged.
   ===================================================================== */
@media (max-width: 720px) {
  .table-wrap { overflow-x: visible; }
  .table, .table tbody, .table tr, .table td { display: block; width: 100%; }
  .table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }

  .table tr {
    background: var(--white); border: 1px solid var(--line); border-radius: 14px;
    padding: 4px 12px; margin-bottom: 8px;
  }
  .table tbody tr:hover { background: var(--white); }

  .table td {
    display: flex; align-items: center; justify-content: space-between; gap: var(--s4);
    padding: 9px 0; border: 0; border-bottom: 1px solid var(--line);
    text-align: right; min-height: 0;
  }
  .table td:last-child { border-bottom: 0; }
  /* The column heading, printed in front of its value. */
  .table td::before {
    content: attr(data-label);
    font-size: var(--label-s); font-weight: 600; color: var(--ink-500);
    text-align: left; white-space: nowrap; flex: 0 0 auto;
  }
  .table td.num { text-align: right; }

  /* The lead cell is the card's title: bigger, on its own line, no label. */
  .table td.is-lead {
    justify-content: flex-start; text-align: left;
    font-size: var(--label-m); font-weight: 600; color: var(--ink-900);
    padding-top: 11px;
  }
  .table td.is-lead::before { content: none; }

  /* An actions cell (a column with no heading) lays its buttons out full
     width rather than squashing them into the right margin. */
  .table td.is-actions { justify-content: stretch; padding-top: 11px; }
  .table td.is-actions::before { content: none; }
  .table td.is-actions .btn { flex: 1; }
  .table td.is-actions > * { width: 100%; }

  /* A value that is itself a chip or a control stays its own size, pinned
     to the right against its label. */
  .table td > .chip, .table td > .btn { flex: 0 0 auto; }

  /* The empty-state row is not a card of cells — let it be one block. */
  .table td[colspan] { text-align: center; padding: var(--s4) 0; }
  .table td[colspan]::before { content: none; }
}

/* --- Performance: six-month trend panels (DB-32) ----------------------
   A grid of small metric cards, each with a figure, its month-on-month
   move, and a sparkline. The chart tones reuse the portal palette so the
   screen reads as one system with the rings and bars elsewhere. */
.p-trends { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s4, 16px); }
@media (max-width: 1100px) { .p-trends { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 640px)  { .p-trends { grid-template-columns: 1fr; } }

.p-trend { border: 1px solid var(--line); border-radius: 12px; padding: 14px 16px 10px; background: #fff; }
.p-trend__top { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.p-trend__label { font-size: .8125rem; font-weight: 600; color: var(--ink-500); }
.p-trend__delta { font-size: .75rem; font-weight: 700; white-space: nowrap; }
.p-trend__delta.is-up { color: var(--green-600); }
.p-trend__delta.is-down { color: var(--red-600); }
.p-trend__value { font-size: 1.5rem; font-weight: 700; color: var(--ink-900); margin: 2px 0 8px; line-height: 1.1; }
.p-trend__axis { display: flex; justify-content: space-between; margin-top: 5px; font-size: .6875rem; color: var(--ink-300); }

.p-spark { width: 100%; height: auto; display: block; overflow: visible; }
.p-spark__area { stroke: none; opacity: .13; }
.p-spark__line { fill: none; stroke-width: 2.25; vector-effect: non-scaling-stroke; stroke-linejoin: round; stroke-linecap: round; }
.p-spark__dot  { stroke: #fff; stroke-width: 1.5; }

.p-spark--blue .p-spark__line, .p-spark--blue .p-spark__dot { stroke: var(--blue-600); }
.p-spark--blue .p-spark__area, .p-spark--blue .p-spark__dot { fill: var(--blue-600); }
.p-spark--gold .p-spark__line, .p-spark--gold .p-spark__dot { stroke: var(--gold-400); }
.p-spark--gold .p-spark__area, .p-spark--gold .p-spark__dot { fill: var(--gold-400); }
.p-spark--navy .p-spark__line, .p-spark--navy .p-spark__dot { stroke: var(--ink-900); }
.p-spark--navy .p-spark__area, .p-spark--navy .p-spark__dot { fill: var(--ink-900); }
.p-spark--green .p-spark__line, .p-spark--green .p-spark__dot { stroke: var(--green-600); }
.p-spark--green .p-spark__area, .p-spark--green .p-spark__dot { fill: var(--green-600); }
@media (prefers-reduced-motion: reduce) { .p-spark { } }

/* --- Help & FAQ --------------------------------------------------------- */
.p-faq__search {
  display: flex; align-items: center; gap: 10px; max-width: 520px;
  background: var(--white); border: 1px solid var(--line); border-radius: 22px;
  padding: 0 16px; color: var(--ink-400);
}
.p-faq__search .icon { flex: none; }
.p-faq__search-input { border: 0; background: none; padding: 12px 0; flex: 1; min-width: 0; font-size: 15px; }
.p-faq__search-input:focus { outline: none; box-shadow: none; }
.p-faq { max-width: 760px; }
.p-faq__cat {
  font-size: 12px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
  color: var(--red-600); margin: 22px 2px 8px;
}
.p-faq__item {
  border: 1px solid var(--line); border-radius: 12px; background: var(--white);
  margin-bottom: 8px; overflow: hidden;
}
.p-faq__q {
  list-style: none; cursor: pointer; padding: 14px 18px; font-weight: 700; color: var(--ink-900);
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
.p-faq__q::-webkit-details-marker { display: none; }
.p-faq__q::after {
  content: ""; width: 9px; height: 9px; flex: none; margin-top: -3px;
  border-right: 2px solid var(--ink-400); border-bottom: 2px solid var(--ink-400);
  transform: rotate(45deg); transition: transform .18s var(--ease);
}
.p-faq__item[open] .p-faq__q { color: var(--red-600); }
.p-faq__item[open] .p-faq__q::after { transform: rotate(225deg); margin-top: 3px; }
.p-faq__q:hover { background: var(--canvas); }
.p-faq__a { padding: 0 18px 16px; }
.p-faq__a p { margin: 0; color: var(--ink-700); line-height: 1.55; }
.p-faq__foot {
  display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap;
  max-width: 760px; margin-top: 24px; padding: 16px 18px;
  border: 1px solid var(--line); border-radius: 12px; background: var(--red-100);
}

/* --- Messenger: pinned banner, selection, header menu, Message info ----- */

.p-msg__pinned {
  display: flex; align-items: center; gap: var(--s2);
  padding: 8px 12px;
  border-bottom: 1px solid var(--line);
  background: var(--blue-50);
}
.p-msg__pinned[hidden] { display: none; }
.p-msg__pinned-main {
  display: flex; align-items: center; gap: var(--s2);
  flex: 1; min-width: 0;
  border: 0; background: none; text-align: left; cursor: pointer;
  color: var(--ink-900); padding: 2px;
}
.p-msg__pinned-main .icon { color: var(--blue-600); flex: none; }
.p-msg__pinned-txt { min-width: 0; }
.p-msg__pinned-x { flex: none; }

/* Selection toolbar, shown above the thread while choosing messages. */
.p-msg__selbar {
  display: flex; align-items: center; gap: var(--s2);
  padding: 8px 12px;
  border-bottom: 1px solid var(--line);
  background: var(--white);
}
.p-msg__selbar[hidden] { display: none; }
.p-selbar__count { font-weight: 600; font-size: var(--body-s); }

.p-msg__body.is-selecting .p-bubble { cursor: pointer; }
.p-msg__body.is-selecting .p-bubble__more-wrap { display: none; }
.p-bubble.is-selected {
  outline: 2px solid var(--blue-600);
  outline-offset: 1px;
  border-radius: 12px;
}

/* Header overflow (⋮) dropdown. */
.p-msg__headmore-wrap { position: relative; }
.p-msg__headmenu {
  position: absolute; right: 0; top: calc(100% + 4px); z-index: 40;
  min-width: 190px;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: 0 10px 30px rgba(22, 35, 58, .14);
  padding: 4px;
}
.p-msg__headmenu[hidden] { display: none; }

/* Message info (read / received / not received). */
.p-rcpt { display: grid; gap: var(--s4); }
.p-rcpt__msg {
  padding: 8px 10px; border-radius: var(--r-md);
  background: var(--canvas); border: 1px solid var(--line);
  font-size: var(--body-s); color: var(--ink-700);
}
.p-rcpt__tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s2); }
.p-rcpt__tile {
  display: grid; gap: 2px; justify-items: center; text-align: center;
  padding: 10px 6px; border-radius: var(--r-md);
  background: var(--blue-50); border: 1px solid var(--blue-100);
}
.p-rcpt__tile b { font-size: var(--h5); line-height: 1; color: var(--ink-900); }
.p-rcpt__tile span { font-size: 11px; color: var(--ink-500); }
.p-rcpt__sec { display: grid; gap: var(--s2); }
.p-rcpt__sechead { display: flex; align-items: center; gap: 6px; font-weight: 600; font-size: var(--body-s); color: var(--ink-700); }
.p-rcpt__people { display: grid; gap: 6px; max-height: 40vh; overflow-y: auto; }
.p-rcpt__row { display: flex; align-items: center; gap: var(--s3); min-width: 0; }

/* In-chat search bar + match highlighting. */
.p-msg__tsearch {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 12px; border-bottom: 1px solid var(--line); background: var(--white);
}
.p-msg__tsearch[hidden] { display: none; }
.p-msg__tsearch > .icon { flex: none; color: var(--ink-400); }
.p-msg__tsearch-input {
  flex: 1; min-width: 0; border: 0; background: var(--canvas);
  border-radius: 18px; padding: 8px 12px; font-size: 14px;
}
.p-msg__tsearch-input:focus { outline: none; box-shadow: none; }
.p-msg__tsearch-count { font-size: 12px; color: var(--ink-500); white-space: nowrap; min-width: 54px; text-align: center; }
.p-bubble.is-hit { outline: 2px solid #eab308; outline-offset: 1px; border-radius: 12px; }
.p-bubble.is-hit-active { outline: 2px solid #e2392b; outline-offset: 1px; }

/* =====================================================================
   Messenger batch (Oct 2026): phone chips, sender card, staged
   attachments, shared media, swipe-to-reply.
   ===================================================================== */

/* #18 — a phone number in a message becomes a tappable chip. */
.p-phone {
  border: 0; background: none; padding: 0; margin: 0; cursor: pointer;
  color: inherit; font: inherit; text-decoration: underline;
  text-underline-offset: 2px; text-decoration-thickness: 1px;
}
.p-bubble--out .p-phone { color: #fff; }
.p-phone:hover { opacity: .85; }

/* #12 — the tappable sender name above a message in a group. */
.p-sender-tap {
  border: 0; background: none; padding: 0; margin: 0 0 2px; cursor: pointer;
  color: var(--brand, #e2392b); font: inherit; font-weight: 600; text-align: left;
}
.p-sender-tap:hover { text-decoration: underline; }

/* #12 — the sender card. */
.p-sendercard { align-items: stretch; }
.p-sendercard__id { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 8px 0 4px; }
.p-sendercard__name { font-weight: 600; font-size: 17px; text-align: center; }
.p-sendercard__acts { display: grid; gap: 8px; }
.p-sendercard__acts .btn,
.p-phonesheet .btn {
  width: 100%; display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  text-decoration: none;
}

/* #9 — staged attachments preview strip above the composer. */
.p-staged {
  display: flex; gap: 8px; flex-wrap: wrap;
  padding: 8px 10px; border-top: 1px solid var(--line);
}
.p-staged[hidden] { display: none; }
.p-staged__item { position: relative; }
.p-staged__img {
  width: 56px; height: 56px; object-fit: cover; border-radius: 10px; display: block;
  border: 1px solid var(--line);
}
.p-staged__file {
  display: flex; align-items: center; gap: 6px; max-width: 160px;
  height: 56px; padding: 0 10px; border-radius: 10px;
  background: var(--canvas); border: 1px solid var(--line); font-size: 13px;
}
.p-staged__rm {
  position: absolute; top: -6px; right: -6px; width: 20px; height: 20px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%; border: 0; cursor: pointer;
  background: rgba(0,0,0,.7); color: #fff;
}
.p-staged__rm:hover { background: rgba(0,0,0,.85); }

/* #13 — shared media grid in the Members panel. */
.p-shared { margin-top: var(--s4); }
.p-shared__head { margin-bottom: var(--s2); color: var(--ink-700); }
.p-shared__grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(72px, 1fr));
  gap: 6px; max-height: 42vh; overflow-y: auto;
}
.p-shared__cell { display: block; aspect-ratio: 1 / 1; border-radius: 8px; overflow: hidden; background: var(--canvas); }
.p-shared__img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* #10 — swipe-to-reply: the bubble slides, a reply glyph shows behind it. */
.p-bubble { position: relative; transition: transform .16s ease; }
.p-bubble.is-swiping { transition: none; }
.p-bubble.is-swiping::before {
  content: ""; position: absolute; left: -34px; top: 50%; transform: translateY(-50%);
  width: 24px; height: 24px; border-radius: 50%;
  background: var(--canvas) center / 14px 14px no-repeat;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='9 17 4 12 9 7'/><path d='M20 18v-2a4 4 0 0 0-4-4H4'/></svg>");
}

/* #19 — scheduled-message bar (just above the composer) + list rows. */
.p-msg__sched {
  display: flex; align-items: center; gap: var(--s2);
  padding: 7px 12px; margin-bottom: 6px;
  border: 1px solid var(--line); border-radius: 12px;
  background: var(--canvas);
}
.p-msg__sched[hidden] { display: none; }
.p-msg__sched-main {
  display: flex; align-items: center; gap: var(--s2);
  flex: 1; min-width: 0;
  border: 0; background: none; text-align: left; cursor: pointer;
  color: var(--ink-900); padding: 2px;
}
.p-msg__sched-main .icon { color: var(--ink-500); flex: none; }
.p-msg__sched-txt { min-width: 0; }
.p-sched__row {
  display: flex; align-items: center; gap: var(--s3);
  padding: 8px 0; border-bottom: 1px solid var(--line);
}
.p-sched__row:last-child { border-bottom: 0; }
.p-sched__preview {
  padding: 8px 10px; border-radius: 8px; background: var(--canvas);
}

/* Preserve the line breaks a sender typed (Enter / Shift+Enter) in the
   message body, while still wrapping long lines. */
.p-bubble__text { white-space: pre-wrap; overflow-wrap: anywhere; }

/* A caption under an image/file in the same message. */
.p-bubble__text--caption { margin-top: 6px; }

/* Feature 1 — student messaging window bar (above the composer). */
.p-msg__gate {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 12px; margin-bottom: 6px;
  border-radius: 12px; font-size: 13px; line-height: 1.3;
}
.p-msg__gate[hidden] { display: none; }
.p-msg__gate .icon { flex: none; }
.p-msg__gate.is-closed { background: #fdecec; color: #a11; border: 1px solid #f6c9c9; }
.p-msg__gate.is-open   { background: #eafaf0; color: #176b3a; border: 1px solid #bfe8cf; }
.p-msg__gate.is-info   { background: var(--canvas); color: var(--ink-700); border: 1px solid var(--line); }

/* Feature 2 — task rows on the Tasks screen. */
.p-task {
  display: flex; align-items: flex-start; gap: var(--s3);
  padding: 10px 12px; border: 1px solid var(--line); border-radius: 12px; background: var(--white);
}
.p-task.is-done { opacity: .7; }
.p-task__title { font-weight: 600; color: var(--ink-900); overflow-wrap: anywhere; }
.p-task.is-done .p-task__title { text-decoration: line-through; }
.p-task__check {
  flex: none; width: 24px; height: 24px; margin-top: 1px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%; cursor: pointer;
  border: 2px solid var(--line); background: var(--white); color: transparent;
}
.p-task__check:hover { border-color: var(--ink-400); }
.p-task__check.is-done { background: var(--brand, #e2392b); border-color: var(--brand, #e2392b); color: #fff; }

/* Inline message formatting: *bold* _italic_ ~strike~ `mono`. */
.p-bubble__text strong, .p-bubble__text b { font-weight: 700; }
.p-bubble__text em { font-style: italic; }
.p-bubble__text del { text-decoration: line-through; }
.p-bubble__text code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .92em; background: rgba(0,0,0,.08); padding: 0 4px; border-radius: 4px;
}
.p-bubble--out .p-bubble__text code { background: rgba(255,255,255,.22); }

/* Bold button in the composer. */
.p-composer-bold b { font-weight: 800; font-size: 15px; line-height: 1; }

/* "Unread messages" divider in a thread. */
.p-unread-div { display: flex; align-items: center; justify-content: center; margin: 12px 0 8px; }
.p-unread-div span {
  font-size: 12px; font-weight: 600; color: var(--brand, #e2392b);
  background: var(--canvas); border: 1px solid var(--line);
  border-radius: 999px; padding: 3px 12px;
}

/* In-app image viewer (photos open here, not in a new tab). */
.p-lightbox {
  position: fixed; inset: 0; z-index: 4000; background: rgba(0,0,0,.9);
  display: flex; align-items: center; justify-content: center; padding: 24px;
}
.p-lightbox__img { max-width: 96vw; max-height: 92vh; object-fit: contain; border-radius: 6px; }
.p-lightbox__x {
  position: absolute; top: 14px; right: 16px; width: 40px; height: 40px;
  display: flex; align-items: center; justify-content: center;
  border: 0; border-radius: 50%; background: rgba(255,255,255,.14); color: #fff; cursor: pointer;
}
.p-lightbox__x:hover { background: rgba(255,255,255,.26); }

/* Typing indicator (just above the composer). */
.p-msg__typing {
  padding: 2px 6px 6px; font-size: 12px; color: var(--ink-500); font-style: italic;
  min-height: 0;
}
.p-msg__typing[hidden] { display: none; }

/* "Online" in the thread header. */
.p-msg__head-sub.is-online { color: #16a34a; font-weight: 600; }
