/* What: the design system - base, type, links, layout, the public and app shells, every shared component.
   Use: loaded after tokens.css by the public, app, admin and presentation layouts.
   Needs: tokens.css. Logical properties only; a physical property needs the rtl-ok marker. */

/* ---------- 1. Base ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; overflow-x: clip; }
body { margin: 0; background: var(--bg); color: var(--text); font-size: var(--fs-md);
  line-height: var(--lh-body); overscroll-behavior-y: contain; -webkit-font-smoothing: antialiased;
  font-family: var(--font-body), var(--font-arabic), system-ui, sans-serif; }
html[lang="ar"] body { font-family: var(--font-arabic), var(--font-body), system-ui, sans-serif;
  line-height: var(--lh-body-ar); }
h1, h2, h3, h4 { margin: 0 0 var(--space-3); line-height: var(--lh-tight); font-weight: 700;
  font-family: var(--font-display), var(--font-arabic), sans-serif; }
/* The display face carries Arabic too, so an Arabic heading is set in it rather than falling back to
   the body face - which is what used to make a heading and its paragraph look like the same voice. */
html[lang="ar"] h1, html[lang="ar"] h2, html[lang="ar"] h3, html[lang="ar"] h4 {
  font-family: var(--font-display), var(--font-arabic), sans-serif; line-height: 1.34; }
/* Tracking tightens Latin display type and tears Arabic letter joins apart, so it is Latin-only. */
html:not([lang="ar"]) h1, html:not([lang="ar"]) h2 { letter-spacing: -.02em; }
p { margin: 0 0 var(--space-4); }
ul, ol { margin: 0 0 var(--space-4); padding-inline-start: var(--space-5); }
img, svg, video { max-inline-size: 100%; }
fieldset { border: 0; margin: 0; padding: 0; min-inline-size: 0; }
legend { padding: 0; }
hr { border: 0; border-block-start: 1px solid var(--border); margin-block: var(--space-4); }
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: 4px; }
::selection { background: var(--primary-soft); }

/* A link inside an interface is a control, not a citation: no underline until it is pointed at.
   Running text keeps its underline, because colour alone must not carry a link inside a paragraph. */
a { color: var(--primary); text-decoration: none; text-underline-offset: 3px; }
a:hover { text-decoration: underline; }
p > a, .prose a, .hint a, .alert a { text-decoration: underline;
  text-decoration-color: color-mix(in srgb, currentColor 40%, transparent); }
a.btn, a.chip, a.tab, a.card, a.badge, a.lang-opt, a.logo, .btn:hover, .tabs a:hover { text-decoration: none; }

.skip-link { position: absolute; inset-block-start: -60px; inset-inline-start: var(--space-4);
  background: var(--surface); color: var(--text); padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-md); z-index: var(--z-toast); box-shadow: var(--shadow-2); }
.skip-link:focus { inset-block-start: var(--space-2); }
.visually-hidden, .sr-only { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; }

/* ---------- 2. Type ---------- */
.display { font-size: var(--fs-4xl); font-weight: 800; line-height: 1.1; }
html[lang="ar"] .display { font-weight: 800; line-height: 1.24; }
.h1 { font-size: var(--fs-3xl); } .h2 { font-size: var(--fs-2xl); }
.h3 { font-size: var(--fs-lg); } .h4 { font-size: var(--fs-md); font-weight: 600; }
.small { font-size: var(--fs-sm); }
.lead { font-size: var(--fs-lg); line-height: 1.7; }
.caption { font-size: var(--fs-xs); font-weight: 500; color: var(--text-muted); }
.muted { color: var(--text-muted); }
/* The label above a title: a gold hairline, then the label in small letterspaced mono. Gold is the
   quiet half of the brand - it names a thing without competing with the green that acts on it. */
.eyebrow { display: inline-flex; align-items: center; gap: var(--space-3); vertical-align: baseline;
  font-family: "IBM Plex Mono", ui-monospace, Menlo, Consolas, monospace;
  font-size: var(--fs-xs); font-weight: 500; letter-spacing: .2em; text-transform: uppercase;
  color: var(--gold-600); margin: 0 0 var(--space-3); }
.eyebrow::before { content: ""; flex: none; inline-size: 28px; block-size: 1px; background: var(--gold-500); }
/* Arabic has no case and its letters must not be tracked apart, so there the label keeps the body face. */
html[lang="ar"] .eyebrow { text-transform: none; letter-spacing: 0; font-size: var(--fs-sm);
  font-weight: 600; font-family: var(--font-arabic), sans-serif; }
.mono { font-family: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace; font-size: .92em; }
.num, .tabular { font-variant-numeric: tabular-nums; }
.ltr { direction: ltr; unicode-bidi: isolate; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pre-wrap { white-space: pre-wrap; }
/* Wide content scrolls inside its own box; it never widens the page (the debug trace of an
   error page is the widest thing the platform renders). */
pre { overflow-x: auto; max-inline-size: 100%; }
@media (max-width: 639px) { .display { font-size: var(--fs-3xl); } .h1 { font-size: var(--fs-2xl); } }

/* fill and stroke are inheritable, and a <use> reference does NOT carry the sprite root's own
   presentation attributes into the copy - without these every icon paints as a black silhouette. */
.icon { inline-size: 18px; block-size: 18px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
[dir="rtl"] .icon-dir { transform: scaleX(-1); }

/* ---------- 3. Layout ---------- */
/* minmax(0, 1fr): without it the single grid column takes its width from its widest content,
   so one wide block (a debug trace, a long table) would stretch the header and the footer too. */
.shell { min-block-size: 100dvh; display: grid; grid-template-rows: auto 1fr auto;
  grid-template-columns: minmax(0, 1fr); }
.container { inline-size: 100%; max-inline-size: var(--content-max); margin-inline: auto;
  padding-inline: var(--space-5); }
.stack > * + * { margin-block-start: var(--space-4); }
.row { display: flex; gap: var(--space-3); align-items: center; flex-wrap: wrap; }
.spacer { flex: 1 1 auto; }
.section { margin-block: var(--space-9); }
.grid-2, .grid-3 { display: grid; gap: var(--space-4); grid-template-columns: minmax(0, 1fr); }
@media (min-width: 900px) {
  .grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
/* The named content grids. A view asks for the shape it means; every one is a single column below
   1024 px, and minmax(0, ...) keeps a wide child inside its track instead of widening the grid. */
.cols, .cols-main, .cols-kpi, .cols-room, .cols-wide {
  display: grid; gap: var(--space-5); grid-template-columns: minmax(0, 1fr); align-items: start; }
.cols > .card + .card, .cols-main > .card + .card, .cols-kpi > * + *, .cols-room > * + *,
.cols-wide > .card + .card, .grid-2 > .card + .card, .grid-3 > .card + .card { margin-block-start: 0; }
/* A grid spaces its children with gap alone. The stacking margins above are written for flow
   layout and would add to the gap, and several of them out-rank a plain child selector - so this
   one is raised above all of them on purpose. */
:is(.cols, .cols-main, .cols-kpi, .cols-room, .cols-wide, .grid-2, .grid-3, .cards, .admin-grid) > *:not(#_) {
  margin-block-start: 0; }
/* Stacked grids keep the same gutter between them that they keep inside themselves. */
.cols + *, .cols-main + *, .cols-kpi + *, .cols-room + *, .cols-wide + * { margin-block-start: var(--space-5); }
@media (min-width: 640px) { .cols-kpi { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) {
  .cols-main { grid-template-columns: minmax(0, 1.85fr) minmax(0, 1fr); }
  .cols-kpi  { grid-template-columns: 1.3fr 1fr 1fr 1.1fr; align-items: stretch; }
  .cols-room { grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr) minmax(0, .95fr); }
  .cols-wide { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
}
.cards { display: grid; gap: var(--space-5); grid-template-columns: minmax(0, 1fr); }
.cards > .card + .card, .cards > .card { margin-block-start: 0; }
@media (min-width: 720px) { .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1100px) { .cards { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* The top of a screen: its name at the start, what can be done to it at the end. */
.page-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3) var(--space-4);
  margin-block-end: var(--space-5); }
.page-head > :first-child { margin-inline-end: auto; min-inline-size: 0; }
.page-head h1 { margin: 0; font-size: var(--fs-2xl); }
.page-head .hint { margin: 2px 0 0; }
/* The older views open with .row > h1 + .spacer + buttons; it gets the same rhythm untouched. */
.admin-main > .row:first-child, .app-main > .row:first-child { margin-block-end: var(--space-5); }
.admin-main > .row:first-child > h1, .app-main > .row:first-child > h1 { margin: 0; }
.toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3);
  margin-block-end: var(--space-4); }
.toolbar .search { margin-inline-start: auto; display: flex; gap: var(--space-2); }
.toolbar .search .input { inline-size: min(300px, 60vw); }
.filter-tabs { display: inline-flex; gap: 2px; padding: 3px; background: var(--surface-2);
  border: 1px solid var(--border); border-radius: 11px; max-inline-size: 100%; overflow-x: auto; }
.filter-tabs a { display: inline-flex; align-items: center; gap: var(--space-2); min-block-size: 32px;
  padding-inline: var(--space-3); border-radius: 8px; font-size: var(--fs-sm); font-weight: 600;
  color: var(--text-muted); white-space: nowrap; text-decoration: none; }
.filter-tabs a:hover { color: var(--text); }
.filter-tabs a[aria-selected="true"], .filter-tabs .is-active { background: var(--surface);
  color: var(--text); box-shadow: var(--shadow-1); }
.filter-tabs .num { font-size: var(--fs-xs); font-weight: 500; color: var(--text-faint); }

/* ---------- 4. The public shell ---------- */
.site-header { position: sticky; inset-block-start: 0; z-index: var(--z-sticky);
  padding-block-start: env(safe-area-inset-top, 0px);
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  backdrop-filter: saturate(180%) blur(14px); border-block-end: 1px solid var(--border); }
.site-header .container { display: flex; align-items: center; gap: var(--space-5); min-block-size: 68px;
  flex-wrap: nowrap; }
.site-header .logo { display: inline-flex; flex: none; }
.site-header .logo img, .site-header .logo svg { block-size: 44px; inline-size: auto; }
.site-header nav { display: flex; gap: var(--space-1); align-items: center; }
.site-header nav a { color: var(--ink-700); font-weight: 500; padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md); text-decoration: none; white-space: nowrap; }
.site-header nav a:hover { background: var(--ink-50); color: var(--text); }
.site-header nav a.btn-primary { color: var(--text-on-brand); padding-inline: var(--space-4); }
.site-header nav a.btn-primary:hover { background: var(--primary-hover); color: var(--text-on-brand); }
/* Signed in: the way back to My workshops, the header's one solid button (owner 2026-09-27: "an obvious button"), set a little apart
   from the links before it. */
.site-header nav a.site-mine { gap: var(--space-2); margin-inline: var(--space-2); font-weight: 600;
  box-shadow: 0 6px 16px -8px color-mix(in srgb, var(--primary) 75%, transparent); }
.site-header nav a.site-mine .icon { inline-size: 16px; block-size: 16px; }
/* Between a phone and a laptop the header is one line that cannot wrap, and the words would push the avatar off its end: the house
   alone there, its words still read out. */
@media (min-width: 640px) and (max-width: 859px) {
  .site-header nav a.site-mine { inline-size: 38px; padding-inline: 0; margin-inline: var(--space-1); }
  .site-mine-label { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
}
/* Nobody signed in: "Log in / Register" in one bordered pill after the links, each word its own link. */
.site-auth { flex: none; display: inline-flex; align-items: center; padding: 2px; border: 1px solid var(--border-strong); border-radius: var(--radius-pill);
  background: var(--surface); box-shadow: var(--shadow-1); font-size: var(--fs-sm); font-weight: 600; }
.site-auth a { padding: 6px var(--space-3); border-radius: var(--radius-pill); color: var(--text); text-decoration: none; white-space: nowrap; }
.site-auth a:hover { background: var(--ink-50); color: var(--primary); }
.site-auth-or { color: var(--text-faint); font-weight: 400; }
/* A phone cannot hold the lockup, the links and the actions on one line. nowrap made them run off
   the start edge and drag 115 px of horizontal scroll behind the whole page, so here they stack - in two thin rows (owner
   2026-09-27: "make the upper menu thinner so it allows more space"): the lockup and "Log in / Register" on the first, the links
   and the language spread across the second, about 80px where three rows took 160. */
@media (max-width: 639px) {
  .site-header .container { flex-wrap: wrap; gap: var(--space-1) var(--space-2); min-block-size: 0;
    padding-block: var(--space-1); padding-inline: var(--space-4); }
  .site-header .logo img, .site-header .logo svg { block-size: 28px; }
  .site-header .logo { order: -3; }
  .site-header .container > .spacer { order: -2; }
  .site-auth { order: -1; padding: 1px; font-size: var(--fs-xs); }
  .site-auth a { padding: 4px var(--space-2); }
  .site-header nav { flex-wrap: wrap; row-gap: var(--space-1); inline-size: 100%; justify-content: space-between; }
  .site-header nav a { padding: var(--space-1) var(--space-2); font-size: var(--fs-sm); }
  .site-header .lang-switch { padding: 2px; }
  .site-header .lang-opt { gap: 5px; min-block-size: 24px; padding-inline: var(--space-2); font-size: var(--fs-xs); }
  .site-header .flag { inline-size: 18px; block-size: 12px; }
}
.site-main { font-size: 16px; padding-block: var(--space-7) var(--space-10); }
/* The public pages round their controls fully. The app and the admin keep the squarer radius: a pill
   is the right shape for an invitation and the wrong one for a row of table actions. */
.site-header .btn, .site-main .btn { border-radius: var(--radius-pill); }
.site-main .btn-lg { padding-inline: var(--space-7); }
/* A band that runs the full width of the window from inside the centred container. */
.bleed { margin-inline: calc(50% - 50vw); padding-inline: calc(50vw - 50%); }
/* A band that closes the page meets the footer. The shell's bottom padding left a strip of page
   ground between two dark bands, which reads as a gap rather than as breathing room. */
.site-main > .bleed:last-child { margin-block-end: calc(-1 * var(--space-10)); }
/* The footer is as bright as the page it closes, and slim (owner 2026-09-25: "very thick, make it thinner, organize it"). One
   gold hairline marks where the page ends; then two rows - the brand and what it does beside where to go, and under a hairline
   the copyright, how to reach us, the language and the way back up. No headings: each list names itself. */
.site-footer { position: relative; background: var(--surface); color: var(--ink-700);
  border-block-start: 1px solid var(--border); padding-block: var(--space-6) var(--space-4);
  font-size: var(--fs-sm); }
.site-footer::before { content: ""; position: absolute; inset-block-start: -1px; inset-inline: 0; block-size: 2px;
  background: linear-gradient(90deg, transparent, var(--gold-500) 22%, var(--gold-400) 50%, var(--gold-500) 78%, transparent); }
.site-footer a:not(.btn) { color: var(--text); text-decoration: none; }
.site-footer a:not(.btn):hover { color: var(--primary); }
.site-footer ul { list-style: none; margin: 0; padding: 0; }
.site-footer .footer-main { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-4) var(--space-7); }
.site-footer .footer-brand { display: flex; align-items: center; gap: var(--space-4); min-inline-size: 0; }
.site-footer .footer-brand .logo { flex: none; }
.site-footer .footer-brand img { block-size: 34px; inline-size: auto; display: block; }
.site-footer .footer-brand p { margin: 0; max-inline-size: 30em; padding-inline-start: var(--space-4);
  border-inline-start: 1px solid var(--border); color: var(--text-muted); line-height: 1.5; }
.site-footer .footer-nav ul { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-6); }
.site-footer .footer-nav a { font-weight: 500; }
.site-footer .footer-base { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3) var(--space-6);
  margin-block-start: var(--space-5); padding-block-start: var(--space-4); border-block-start: 1px solid var(--border);
  color: var(--text-muted); }
.site-footer .footer-contact { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-5); }
.site-footer .footer-contact a { display: inline-flex; align-items: center; gap: var(--space-2); font-weight: 500; }
.site-footer .footer-contact .icon { inline-size: 16px; block-size: 16px; color: var(--primary); }
.site-footer .footer-contact .footer-social { display: grid; place-items: center; inline-size: 30px; block-size: 30px;
  border: 1px solid var(--border); border-radius: 50%; }
.site-footer .footer-contact .footer-social:hover { border-color: var(--primary); background: var(--primary-soft); }
.site-footer .footer-end { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3) var(--space-5); margin-inline-start: auto; }
.site-footer .footer-top { display: inline-flex; align-items: center; gap: 6px; color: var(--text-muted); font-weight: 500; }
.site-footer .footer-top .icon { inline-size: 16px; block-size: 16px; }
/* A phone: the brand over its sentence, the links in two columns, then the way to reach us and the language. */
@media (max-width: 639px) {
  .site-footer .footer-brand { flex-direction: column; align-items: flex-start; gap: var(--space-2); }
  .site-footer .footer-brand p { padding-inline-start: 0; border-inline-start: 0; }
  .site-footer .footer-nav { inline-size: 100%; }
  .site-footer .footer-nav ul { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3) var(--space-4); }
  .site-footer .footer-copy { order: 3; inline-size: 100%; }
  .site-footer .footer-end { margin-inline-start: 0; inline-size: 100%; justify-content: space-between; }
}

/* ---------- 5. The app shell (participant and instructor) ---------- */
/* The emerald spine (owner 2026-09-23: "the left menu seems really ugly ... it should match the homepage"). The navigation is the
   certificate's proof panel and the home ticket's stub: deep emerald, fine gold streams, the pearl logo, and the page in use a pearl
   tab cut out of the panel that runs on into the sheet. On a phone the same element is the bar along the bottom, in the same green. */
.app-topbar { position: sticky; inset-block-start: 0; z-index: var(--z-sticky);
  min-block-size: var(--topbar-h); padding-block-start: env(safe-area-inset-top, 0px);
  display: flex; align-items: center; gap: var(--space-3); padding-inline: var(--space-4);
  background: var(--surface); color: var(--text); border-block-end: 1px solid var(--border); }
.app-topbar a, .app-topbar button { color: var(--text); }
.app-topbar .logo img { block-size: 32px; inline-size: auto; display: block; }
.app-topbar strong { font-size: var(--fs-md); font-weight: 600; min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.app-main { padding: var(--space-4); padding-block-end: calc(var(--tabbar-h) + var(--safe-b) + var(--space-5)); }
/* Phone: a fixed bottom bar, and .app-main reserves its height plus the safe area. */
.tabbar { position: fixed; inset-inline: 0; inset-block-end: 0; z-index: var(--z-sticky); display: flex;
  padding-block-end: var(--safe-b); color: #F6F3E8;
  background: linear-gradient(180deg, #0F5A4D 0%, var(--teal-700) 100%); box-shadow: 0 -12px 28px -18px rgba(11, 31, 26, .55); }
.tabbar > .spine-brand, .tabbar-foot { display: none; }
.tabbar > a { position: relative; flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; min-block-size: var(--tabbar-h); font-size: 11px; font-weight: 500; color: rgba(246, 243, 232, .68); text-decoration: none; }
.tabbar > a .icon { inline-size: 22px; block-size: 22px; }
.tabbar > a[aria-current="page"] { color: #fff; }
.tabbar > a[aria-current="page"] .icon { color: var(--gold-400); }
.tabbar > a[aria-current="page"]::before { content: ""; position: absolute; inset-block-start: 0; inset-inline: 32%; block-size: 3px;
  border-end-start-radius: 3px; border-end-end-radius: 3px; background: var(--gold-400); }
.tabbar a:focus-visible, .tabbar button:focus-visible { outline: 2px solid var(--gold-400); outline-offset: -2px; }
/* Laptop: the spine runs the full height at the start of the window; the topbar is the sheet's own, over the page and the chat. */
@media (min-width: 1024px) {
  .app-shell { grid-template-columns: var(--sidebar-w) minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); }
  .app-shell > .tabbar { grid-column: 1; grid-row: 1 / -1; }
  .app-shell > .app-topbar { grid-column: 2 / -1; grid-row: 1; padding-inline: var(--space-8); }
  .app-shell > .app-main { grid-column: 2; grid-row: 2; }
  .app-topbar { background: color-mix(in srgb, var(--bg) 88%, transparent); backdrop-filter: blur(10px); border-block-end-color: transparent; }
  .app-topbar .logo { display: none; }
  .tabbar { position: sticky; inset-inline: auto; inset-block: 0 auto; block-size: 100dvh; flex-direction: column; gap: 4px;
    overflow-y: auto; overflow-x: hidden; scrollbar-width: none; padding-block: 0 var(--space-4); padding-inline: var(--space-4) 0; box-shadow: none;
    background: url("../img/spine-lines.svg") center top / 100% auto repeat-y,
      radial-gradient(130% 34% at 50% 0%, rgba(59, 175, 152, .26), transparent 72%),
      linear-gradient(180deg, #0F5A4D 0%, var(--teal-700) 40%, #073A32 100%); }
  /* The certificate's foil seam, where the panel meets the sheet. */
  .tabbar::after { content: ""; position: absolute; inset-block: 0; inset-inline-end: 0; inline-size: 1px; pointer-events: none;
    background: linear-gradient(180deg, transparent, rgba(220, 187, 97, .55) 18%, rgba(220, 187, 97, .55) 82%, transparent); }
  .tabbar > .spine-brand { display: flex; flex-direction: row; align-items: center; justify-content: flex-start; flex: none; min-block-size: 96px; margin-inline-end: var(--space-4); margin-block-end: var(--space-4);
    padding-inline: var(--space-2); border-block-end: 1px solid rgba(246, 243, 232, .1); }
  .spine-brand img { display: block; inline-size: 152px; block-size: auto; }
  .tabbar > a:not(.spine-brand) { flex: none; flex-direction: row; justify-content: flex-start; gap: var(--space-3); min-block-size: 48px;
    margin-inline-end: var(--space-4); padding-inline: var(--space-3); border-radius: 14px; font-size: var(--fs-md); font-weight: 500;
    transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out); }
  .tabbar > a .icon { inline-size: 20px; block-size: 20px; }
  .tabbar > a:not(.spine-brand):hover { background: rgba(246, 243, 232, .08); color: #fff; }
  /* The page in use is a pearl tab that joins the sheet: square on the sheet's side, its two outer corners turned inwards. */
  .tabbar > a[aria-current="page"] { z-index: 1; margin-inline-end: 0; border-start-end-radius: 0; border-end-end-radius: 0;
    background: var(--bg); color: var(--teal-700); font-weight: 700; }
  .tabbar > a[aria-current="page"]:hover { background: var(--bg); color: var(--teal-700); }
  .tabbar > a[aria-current="page"] .icon { color: var(--gold-600); }
  .tabbar > a[aria-current="page"]::before, .tabbar > a[aria-current="page"]::after { content: ""; position: absolute; inset-inline: auto 0;
    inline-size: 16px; block-size: 16px; border-radius: 0; }
  .tabbar > a[aria-current="page"]::before { inset-block: auto 100%; background: radial-gradient(circle at 0 0, transparent 15.5px, var(--bg) 16px); }
  .tabbar > a[aria-current="page"]::after { inset-block: 100% auto; background: radial-gradient(circle at 0 100%, transparent 15.5px, var(--bg) 16px); }
  html[dir="rtl"] .tabbar > a[aria-current="page"]::before { background: radial-gradient(circle at 100% 0, transparent 15.5px, var(--bg) 16px); }
  html[dir="rtl"] .tabbar > a[aria-current="page"]::after { background: radial-gradient(circle at 100% 100%, transparent 15.5px, var(--bg) 16px); }
  /* The person at the foot of the panel, and the two ways out. */
  .tabbar-foot { display: grid; gap: 2px; margin-block-start: auto; margin-inline-end: var(--space-4); padding-block-start: var(--space-4);
    border-block-start: 1px solid rgba(246, 243, 232, .12); }
  .tabbar-me { display: flex; align-items: center; gap: var(--space-3); min-inline-size: 0; margin-block-end: var(--space-2);
    padding: var(--space-3); border-radius: 14px; background: rgba(4, 30, 25, .32); }
  .tabbar-me .avatar { flex: none; box-shadow: 0 0 0 2px rgba(220, 187, 97, .8); }
  .tabbar-me-text { display: grid; min-inline-size: 0; line-height: 1.3; }
  .tabbar-me-text strong { font-size: var(--fs-sm); color: #fff; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .tabbar-me-text small { font-size: var(--fs-xs); color: var(--gold-400); }
  .tabbar-foot form { margin: 0; }
  .tabbar-foot a, .tabbar-foot button { display: flex; align-items: center; gap: var(--space-3); inline-size: 100%; min-block-size: 40px;
    padding-inline: var(--space-3); border: 0; border-radius: 10px; background: none; font: inherit; font-size: var(--fs-sm); font-weight: 500;
    color: rgba(246, 243, 232, .7); text-decoration: none; cursor: pointer; }
  .tabbar-foot a:hover, .tabbar-foot button:hover { background: rgba(246, 243, 232, .08); color: #fff; }
  .tabbar-foot .icon { inline-size: 18px; block-size: 18px; }
  .app-main { padding: var(--space-3) var(--space-8) var(--space-10); inline-size: 100%;
    max-inline-size: calc(var(--content-max) + 2 * var(--space-8)); margin-inline: auto; }
}
/* A workshop's pages carry its chat as a third column once there is room for it (Live/views/dock.php, live.css). */
@media (min-width: 1240px) {
  .app-shell.has-dock { grid-template-columns: var(--sidebar-w) minmax(0, 1fr) var(--dock-w); }
  .app-shell.has-dock > .dock { grid-column: 3; grid-row: 2; }
  .app-shell.has-dock > .app-main { padding-inline: var(--space-7); container: page / inline-size; }
}
/* Beside the chat the page is narrower than the window says: its column grids follow the page's own width there. */
@container page (max-width: 999px) { .cols-room { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }
@container page (max-width: 899px) { .cols-main, .cols-wide { grid-template-columns: minmax(0, 1fr); } }
@container page (max-width: 719px) { .cols-room { grid-template-columns: minmax(0, 1fr); } }

/* ---------- 6. Buttons ---------- */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  min-block-size: 38px; padding-inline: var(--space-4); border-radius: var(--radius-md);
  border: 1px solid var(--border-strong); background: var(--surface); color: var(--text);
  font: inherit; font-size: var(--fs-sm); font-weight: 600; line-height: 1.2; cursor: pointer;
  text-decoration: none; white-space: nowrap; box-shadow: var(--shadow-1);
  transition: background var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out),
    box-shadow var(--dur-fast) var(--ease-out); }
.btn:hover { background: var(--ink-50); border-color: var(--ink-300); }
.btn:active { box-shadow: none; }
.btn:disabled, .btn[aria-disabled="true"], .btn.is-disabled { opacity: .5; cursor: not-allowed; box-shadow: none; }
.btn.is-loading { pointer-events: none; opacity: .75; }
.btn-primary { background: var(--primary); border-color: var(--primary); color: var(--text-on-brand);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .14), 0 1px 2px rgba(18, 54, 154, .4); }
.btn-primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); }
.btn-secondary { background: var(--surface); border-color: var(--border-strong); color: var(--text); }
.btn-ghost { background: transparent; border-color: transparent; color: var(--ink-700); box-shadow: none; }
.btn-ghost:hover { background: var(--ink-100); border-color: transparent; color: var(--text); }
.btn-danger { background: var(--danger); border-color: var(--danger); color: #fff; }
.btn-danger:hover { background: #A33624; border-color: #A33624; }
.btn-ghost.btn-danger, .btn-link.btn-danger { background: transparent; border-color: transparent; color: var(--danger); }
.btn-ghost.btn-danger:hover { background: #FBECE9; }
/* A button that takes something away, drawn as a button (owner 2026-09-26: "Reject, Remove should be shown as buttons"): red words
   and a red-tinted outline on the surface, filled faintly on hover. */
.btn-outline-danger { background: var(--surface); border-color: color-mix(in srgb, var(--danger) 45%, var(--surface)); color: var(--danger); }
.btn-outline-danger:hover { background: color-mix(in srgb, var(--danger) 8%, var(--surface)); border-color: var(--danger); color: var(--danger); }
.btn-link { background: none; border-color: transparent; box-shadow: none; color: var(--primary);
  padding-inline: 0; min-block-size: 0; }
.btn-link:hover { background: none; text-decoration: underline; }
.btn-zoom { background: var(--zoom); border-color: var(--zoom); color: #fff; }
.btn-zoom:hover { background: #1F78E6; border-color: #1F78E6; }
/* StartupLaby is a gift - a premium account - so its door is a jewel box, not a second gold bar (gold metal is the seat's): night
   lacquer with a green depth, a hairline of gold drawn round it (a gradient border), a gem, the word in gold leaf, and a faint
   gleam that crosses it now and then. Pointed at, it lifts and the gold warms. .btn:hover paints a background of its own, so the
   lacquer is a custom property and :hover says it again. */
.btn-startuplaby { --sl-lacquer: linear-gradient(155deg, #1D4550 0%, #10283F 48%, #0A1A2F 100%);
  --sl-rim: linear-gradient(135deg, #F6E3A1 0%, #B48317 38%, #F1D98C 62%, #8F6D1E 100%);
  position: relative; isolation: isolate; overflow: hidden; color: #EBD08A; font-weight: 700; letter-spacing: .01em;
  border: 1px solid transparent; background: var(--sl-lacquer) padding-box, var(--sl-rim) border-box;
  box-shadow: inset 0 1px 0 rgba(246, 227, 161, .28), 0 1px 2px rgba(10, 26, 47, .45), 0 14px 28px -14px rgba(10, 26, 47, .8);
  transition: transform var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out); }
.btn-startuplaby .icon { color: #F1D98C; filter: drop-shadow(0 0 5px rgba(241, 217, 140, .5)); }
.btn-startuplaby .sl-word { background: linear-gradient(180deg, #FBEFC4 0%, #E7C86E 55%, #C29A3A 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent; }
.btn-startuplaby::after { content: ""; position: absolute; z-index: -1; inset-block: -20%; inset-inline-start: -60%;
  inline-size: 30%; transform: skew(-20deg);
  background: linear-gradient(100deg, transparent, rgba(246, 227, 161, .22), transparent);
  animation: gold-sheen 7s var(--ease-in-out) 2s infinite; }
.btn-startuplaby:hover, .btn-startuplaby:focus-visible { color: #F8E7AE; background: var(--sl-lacquer) padding-box, var(--sl-rim) border-box;
  transform: translateY(-2px);
  box-shadow: inset 0 1px 0 rgba(246, 227, 161, .4), 0 3px 6px rgba(10, 26, 47, .35), 0 18px 34px -14px rgba(10, 26, 47, .9),
    0 0 0 4px rgba(220, 187, 97, .22); }
.btn-startuplaby:hover::after, .btn-startuplaby:focus-visible::after { animation: gold-sheen-once 900ms var(--ease-out) both; }
.btn-startuplaby:active { transform: translateY(1px); }
@media (prefers-reduced-motion: reduce) {
  .btn-startuplaby { transition: none; }
  .btn-startuplaby::after, .btn-startuplaby:hover::after, .btn-startuplaby:focus-visible::after { animation: none; }
  .btn-startuplaby:hover, .btn-startuplaby:focus-visible, .btn-startuplaby:active { transform: none; }
}
/* Gold belongs to the certificate, so it is spent on the action a visitor came to take - the seat (on the home page every ticket's
   seat: owner, 2026-09-21). It is drawn as metal, not as a yellow rectangle: light from above, a darker foot, a bevel on both
   edges, and a sheen that crosses it now and then and whenever it is pointed at.
   Pointed at, the metal WARMS: a brighter face fades in over it (::before - a gradient cannot be transitioned, an opacity can), the
   button lifts two pixels and its glow widens. It used to turn white: .btn:hover paints a background and outranks .btn-gold, whose
   own :hover never named one - so the face is a custom property and :hover says it again. */
.btn-gold { --gold-face: linear-gradient(180deg, #F8E7AE 0%, #ECCB6A 30%, #D2A436 62%, #B48317 100%);
  position: relative; isolation: isolate; overflow: hidden; color: #33240A; font-weight: 700;
  border: 1px solid #A47718; background: var(--gold-face);
  text-shadow: 0 1px 0 rgba(255, 255, 255, .5);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .8), inset 0 -2px 0 rgba(122, 90, 22, .32),
    0 1px 2px rgba(122, 90, 22, .35), 0 10px 24px -10px rgba(201, 162, 39, .7);
  transition: transform var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out); }
.btn-gold::before { content: ""; position: absolute; z-index: -2; inset: 0; border-radius: inherit; opacity: 0;
  background: linear-gradient(180deg, #FFF4CC 0%, #F6D774 26%, #E0AE2C 58%, #B07C0C 100%);
  transition: opacity var(--dur-base) var(--ease-out); }
.btn-gold::after { content: ""; position: absolute; z-index: -1; inset-block: -20%; inset-inline-start: -60%;
  inline-size: 38%; transform: skew(-20deg);
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .85), transparent);
  animation: gold-sheen 5.5s var(--ease-in-out) 1.2s infinite; }
.btn-gold:hover, .btn-gold:focus-visible { color: #2A1D06; background: var(--gold-face); border-color: #8A6510; transform: translateY(-2px);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .95), inset 0 -2px 0 rgba(122, 90, 22, .4),
    0 3px 6px rgba(122, 90, 22, .3), 0 18px 34px -10px rgba(201, 162, 39, .95), 0 0 0 4px rgba(236, 203, 106, .28); }
.btn-gold:hover::before, .btn-gold:focus-visible::before { opacity: 1; }
.btn-gold:hover::after, .btn-gold:focus-visible::after { animation: gold-sheen-once 800ms var(--ease-out) both; }
.btn-gold:active { transform: translateY(1px); box-shadow: inset 0 2px 4px rgba(122, 90, 22, .45), 0 1px 2px rgba(122, 90, 22, .35); }
.btn-gold:active::before { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  .btn-gold, .btn-gold::before { transition: none; }
  .btn-gold::after, .btn-gold:hover::after, .btn-gold:focus-visible::after { animation: none; }
  .btn-gold:hover, .btn-gold:focus-visible, .btn-gold:active { transform: none; }
}
@keyframes gold-sheen { 0%, 78% { inset-inline-start: -60%; } 100% { inset-inline-start: 130%; } }
@keyframes gold-sheen-once { from { inset-inline-start: -60%; } to { inset-inline-start: 130%; } }
.btn-sm { min-block-size: 32px; padding-inline: var(--space-3); font-size: var(--fs-xs); border-radius: 8px; }
.btn-lg { min-block-size: 48px; padding-inline: var(--space-6); font-size: var(--fs-md); border-radius: 11px; }
.btn-block { display: flex; inline-size: 100%; }
.btn-icon { inline-size: 38px; min-inline-size: 38px; padding-inline: 0; }
.btn-icon.btn-sm { inline-size: 32px; min-inline-size: 32px; }
.btn-copy { gap: var(--space-1); }
/* A finger needs 44 px; a mouse does not, and giving it to the mouse is what made every screen look
   like a phone. Only a coarse pointer gets the large targets. */
@media (pointer: coarse) {
  .btn, .input, .select, .chip-selectable, a.chip { min-block-size: 44px; }
  .btn-sm { min-block-size: 40px; } .btn-icon { inline-size: 44px; min-inline-size: 44px; }
  .input, .select, .textarea { font-size: 16px; }
}

/* ---------- 7. Surfaces ---------- */
.card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
  padding: var(--space-5); box-shadow: var(--shadow-1); }
.card + .card { margin-block-start: var(--space-5); }
/* One gutter between any two stacked groups. The rule used to cover a card after a card only, so a
   card after a grid, or a table after a card, sat flush against its neighbour. Inside a grid the
   children are laid out by gap, and the grid rules above reset this with a higher specificity. */
:is(.card, .table-wrap, .grid-2, .grid-3, .admin-grid, .item-list, .cards, .alert)
  + :is(.card, .table-wrap, .grid-2, .grid-3, .admin-grid, .item-list, .cards, .cols, .cols-main, .cols-wide, .cols-kpi, .cols-room) {
  margin-block-start: var(--space-5); }
.card > :last-child { margin-block-end: 0; }
.card-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3);
  margin-block-end: var(--space-4); }
.card-head > :first-child { margin-inline-end: auto; }
.card-head h2, .card-head h3 { margin: 0; font-size: var(--fs-lg); }
.card-head .hint { margin: 2px 0 0; }
.card-action { display: flex; gap: var(--space-4); align-items: flex-start; }
.card-action .icon { inline-size: 22px; block-size: 22px; color: var(--primary); }
.card-stat { display: flex; flex-direction: column; gap: var(--space-1); }
.card-clickable { cursor: pointer; transition: box-shadow var(--dur-fast) var(--ease-out),
  border-color var(--dur-fast) var(--ease-out); }
.card-clickable:hover { box-shadow: var(--shadow-2); border-color: var(--border-strong); }
.card-workshop { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-3); }
.card-workshop > * { margin: 0; }
.card-workshop h3 a { color: var(--text); }
.card-workshop h3 a:hover { color: var(--primary); text-decoration: none; }
.card-workshop ul { list-style: none; padding: 0; display: grid; gap: 2px; }
.card-workshop-info { display: grid; gap: var(--space-3); inline-size: 100%; }
.card-workshop-info > * { margin: 0; }
.card-workshop > .btn { margin-block-start: auto; }
/* One workshop alone is a row the width of the section, not a third of it beside two blanks. */
@media (min-width: 720px) {
  .cards > .card-workshop:only-child { grid-column: 1 / -1; flex-direction: row; align-items: center;
    gap: var(--space-6); padding: var(--space-6); }
  .cards > .card-workshop:only-child > .btn { margin-block-start: 0; flex: none; min-block-size: 46px;
    padding-inline: var(--space-7); }
}
/* The certificate's fine gold rings over a white-to-mist wash. Inside the app it is a bright card; on
   the public home, .bleed turns the same surface into a band the full width of the window. */
.card-hero { position: relative; isolation: isolate; overflow: hidden;
  background-image: var(--grad-ai), var(--grad-hero); background-color: var(--surface);
  color: var(--text); border: 1px solid var(--border); border-radius: var(--radius-xl);
  padding: var(--space-7); }
.card-hero.bleed { border-inline: 0; border-block-start: 0; border-radius: 0; box-shadow: none;
  padding-block: var(--space-10); padding-inline: calc(50vw - 50%); margin-block-start: calc(-1 * var(--space-7)); }
.card-hero .h1, .card-hero .display { color: var(--text); }
html[data-theme="dark"] .card-hero { background-image: var(--grad-ai); background-color: var(--surface-2); }

.stat { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
  padding: var(--space-4) var(--space-5); display: grid; gap: 2px; box-shadow: var(--shadow-1); }
.stat .value { font-size: var(--fs-3xl); font-weight: 700; line-height: 1.15; font-variant-numeric: tabular-nums;
  font-family: var(--font-display), var(--font-arabic), sans-serif; }
.stat .label { color: var(--text-muted); font-size: var(--fs-sm); font-weight: 500; }
.stat-dark { background: var(--navy-900); border-color: var(--navy-900); }
.stat-dark .value { color: var(--gold-400); } .stat-dark .label { color: var(--ink-300); }
html[data-theme="dark"] .stat-dark { background: var(--navy-800); border-color: var(--navy-600); }
.stats { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-4);
  grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); }
.stats li { display: grid; gap: 2px; }
.stats li > .num { font-size: var(--fs-xl); font-weight: 700; line-height: var(--lh-tight); }
.stats li > span + span { color: var(--text-muted); font-size: var(--fs-sm); }
/* Label and value pairs, read across. */
.kv { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--space-2) var(--space-5); margin: 0; }
.kv dt { color: var(--text-muted); font-size: var(--fs-sm); } .kv dd { margin: 0; font-weight: 500; }

/* ---------- 8. Status ---------- */
.badge { display: inline-flex; align-items: center; gap: 6px; padding: 2px 10px; min-block-size: 22px;
  border-radius: var(--radius-pill); font-size: var(--fs-xs); font-weight: 600; line-height: 1.4;
  background: var(--ink-100); color: var(--ink-700); white-space: nowrap; }
.badge-success, .badge-teal, .badge-approved, .badge-present { background: var(--teal-50); color: var(--teal-700); }
.badge-warning, .badge-pending { background: var(--gold-50); color: var(--gold-700); }
.badge-danger, .badge-rejected { background: #FBECE9; color: #8F2A1A; }
.badge-info, .badge-published, .badge-waitlisted { background: var(--primary-soft); color: var(--primary); }
.badge-neutral, .badge-grey, .badge-draft, .badge-cancelled, .badge-absent { background: var(--ink-100); color: var(--ink-700); }
.badge-archived { background: var(--surface-2); color: var(--text-muted); }
.badge-gold { background: var(--gold-100); color: var(--gold-700); }
/* Something waits for you: a red count on a menu item, a tab or a row (Nav::badge, stage_tabs). The sale red, white figures, never a word. */
.nav-badge { display: inline-grid; place-items: center; min-inline-size: 20px; block-size: 20px; padding-inline: 6px; border-radius: var(--radius-pill);
  background: var(--danger); color: #fff; font-size: 11px; font-weight: 700; line-height: 1; font-style: normal; box-shadow: 0 0 0 2px var(--surface); }
.sidebar nav a .nav-badge { margin-inline-start: auto; }
.tab .nav-badge { margin-inline-start: 6px; vertical-align: 1px; }
/* Green is the brand now, so it can no longer also mean "happening right now" - a green pill beside
   green buttons and green nav says nothing. Live is the deep blue ground with the one aqua dot on
   the whole platform, pulsing: a signal that cannot be mistaken for a brand colour or a status. */
.badge-live { background: var(--navy-900); color: #fff; }
.badge-live::before { content: ""; inline-size: 7px; block-size: 7px; border-radius: 50%;
  background: var(--cyan-500); animation: pulse 1.4s infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }
.live-pill { display: inline-flex; align-items: center; gap: var(--space-2); padding: 3px var(--space-3);
  border-radius: var(--radius-pill); background: var(--navy-900); color: #fff; font-size: var(--fs-xs);
  font-weight: 600; }
.live-pill::before { content: ""; inline-size: 7px; block-size: 7px; border-radius: 50%;
  background: var(--cyan-500); animation: pulse 1.4s infinite; }
@media (prefers-reduced-motion: reduce) { .badge-live::before, .live-pill::before { animation: none; } }
/* js/live.js adds this to <body> while the live updates cannot be reached. It floats under the top bar: in the page's flow
   it lengthened the page, and the sticky chat column rose under the top bar with its title cut (owner 2026-09-25). */
#live-pill { position: fixed; z-index: var(--z-toast); inset-block-start: calc(var(--topbar-h) + var(--space-2)); inset-inline: 0;
  inline-size: fit-content; margin-inline: auto; }
.dot { display: inline-block; inline-size: 8px; block-size: 8px; border-radius: 50%; background: var(--ink-300); }
.dot-amber { background: var(--warning); }
.pill { display: inline-flex; align-items: center; gap: var(--space-2); padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill); background: var(--surface-2); font-weight: 600; font-size: var(--fs-sm); }
/* A chip states a fact about a thing; as a link or a button it is also a filter. */
.chip { display: inline-flex; align-items: center; gap: 6px; min-block-size: 26px; padding-inline: 10px;
  border-radius: var(--radius-pill); border: 1px solid var(--border); background: var(--surface);
  color: var(--ink-700); font-size: var(--fs-xs); font-weight: 500; white-space: nowrap; }
a.chip:hover, .chip-selectable:hover { border-color: var(--border-strong); background: var(--ink-50); }
.chip-selectable { cursor: pointer; font: inherit; font-size: var(--fs-xs); }
.chip-selectable[aria-pressed="true"], .chip-active, .chip.is-active { background: var(--primary-soft);
  border-color: color-mix(in srgb, var(--primary) 35%, transparent); color: var(--primary); font-weight: 600; }
.chip-teal, .chip-correct { background: var(--teal-50); border-color: var(--teal-100); color: var(--teal-700); }
.chip-gold, .chip-warn { background: var(--gold-50); border-color: var(--gold-100); color: var(--gold-700); }
.chip-incorrect { background: #FBECE9; border-color: #F3CFC8; color: #8F2A1A; }
.chip-grey { background: var(--surface-2); color: var(--text-muted); }
/* The hook: the one chip that gives a reason to reserve now (Site\SeatsPure). Hot is for what is nearly gone - the sale red, with a
   dot that beats - gold for what is going, teal for a calm fact. */
.chip-hook { gap: 6px; font-weight: 700; }
.chip-hook .icon { flex: none; inline-size: 13px; block-size: 13px; }
.chip-hook.is-teal { background: var(--teal-50); border-color: var(--teal-100); color: var(--teal-700); }
.chip-hook.is-gold { background: var(--gold-50); border-color: var(--gold-100); color: var(--gold-700); }
.chip-hook.is-grey { background: var(--surface-2); color: var(--text-muted); }
.chip-hook.is-hot { background: #FBECE9; border-color: #F3CFC8; color: #8F2A1A; }
.chip-hook.is-hot .icon { color: var(--danger); animation: hook-beat 1.6s var(--ease-in-out) infinite; }
@keyframes hook-beat { 0%, 100% { scale: 1; opacity: 1; } 50% { scale: 1.25; opacity: .7; } }
@media (prefers-reduced-motion: reduce) { .chip-hook.is-hot .icon { animation: none; } }
.scroller { display: flex; gap: var(--space-2); overflow-x: auto; scroll-snap-type: inline mandatory;
  scrollbar-width: none; }
.scroller::-webkit-scrollbar { display: none; }
.scroller > * { scroll-snap-align: start; }

/* ---------- 9. Navigation inside a screen ---------- */
.tabs { display: flex; gap: var(--space-1); border-block-end: 1px solid var(--border);
  margin-block-end: var(--space-5); overflow-x: auto; scrollbar-width: none; align-items: center; }
.tabs::-webkit-scrollbar { display: none; }
.tabs a, .tabs button, .tabs .tab { display: inline-flex; align-items: center; gap: var(--space-2);
  min-block-size: 42px; padding-inline: var(--space-3); background: none; border: 0;
  border-block-end: 2px solid transparent; margin-block-end: -1px; color: var(--text-muted); font: inherit;
  font-size: var(--fs-sm); font-weight: 600; cursor: pointer; text-decoration: none; white-space: nowrap; }
.tabs a:hover { color: var(--text); }
.tabs .is-active, .tabs [aria-selected="true"], .tabs [aria-current="page"] { color: var(--primary);
  border-block-end-color: var(--primary); }
.tabs .is-disabled { opacity: .45; cursor: not-allowed; }
.tabs .badge { margin-inline-end: var(--space-2); }
.segmented { display: inline-flex; background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 11px; padding: 3px; gap: 2px; }
.segmented a, .segmented button, .segmented label { min-block-size: 32px; display: inline-flex;
  align-items: center; padding-inline: var(--space-3); border-radius: 8px; border: 0; background: none;
  font: inherit; font-size: var(--fs-sm); font-weight: 600; color: var(--text-muted); cursor: pointer;
  text-decoration: none; }
.segmented .is-active, .segmented [aria-pressed="true"], .segmented input:checked + span {
  background: var(--surface); color: var(--text); box-shadow: var(--shadow-1); }
.segmented input { position: absolute; opacity: 0; inline-size: 1px; block-size: 1px; }
.segmented label:has(input:focus-visible) { outline: 2px solid var(--focus); outline-offset: 2px; }
.segmented-lang span[lang="ar"] { font-family: var(--font-arabic), sans-serif; }
/* The spine: a workshop is a sequence - register, attend, practise, review, certificate - and this
   one line draws it everywhere it appears. Order is information here, so the steps are numbered. */
.stepper { display: grid; gap: var(--space-4); list-style: none; margin: 0; padding: 0; }
.stepper li { position: relative; display: flex; align-items: center; gap: var(--space-3);
  color: var(--text-muted); font-size: var(--fs-sm); }
.stepper .step-no { position: relative; z-index: 1; flex: none; inline-size: 28px; block-size: 28px;
  border-radius: 50%; display: grid; place-items: center; background: var(--surface);
  border: 2px solid var(--border-strong); font-weight: 700; font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums; }
.stepper .is-done { color: var(--text); }
.stepper .is-done .step-no { background: var(--teal-600); border-color: var(--teal-600); color: #fff; }
.stepper .is-current { color: var(--text); font-weight: 600; }
.stepper .is-current .step-no { border-color: var(--primary); color: var(--primary);
  box-shadow: 0 0 0 4px var(--primary-soft); }
@media (min-width: 640px) {
  .stepper { grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 0; }
  .stepper li { flex-direction: column; text-align: center; gap: var(--space-2); padding-inline: var(--space-2); }
  .stepper li::before { content: ""; position: absolute; inset-block-start: 13px;
    inset-inline: -50% 50%; block-size: 2px; background: var(--border-strong); }
  .stepper li:first-child::before { display: none; }
  .stepper .is-done::before, .stepper .is-current::before { background: var(--teal-600); }
}
/* The same line used to explain rather than to track: every step reads as open. */
.stepper-guide li { color: var(--text); }
@media (max-width: 639px) { .stepper-guide li { align-items: flex-start; } }
.stepper-guide .step-no { background: var(--primary-soft); border-color: transparent; color: var(--primary); }
.stepper-guide li::before { background: var(--border); }
.pagination { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding-block: var(--space-3); color: var(--text-muted); font-size: var(--fs-sm); }
/* Both languages, each behind its flag: the eye finds a language switch by its flags, and seeing the
   two says which one the page is in. The current one is a raised chip, the other one is the link. */
.lang-switch { display: inline-flex; align-items: center; gap: 2px; padding: 3px; border-radius: var(--radius-pill);
  background: var(--ink-50); border: 1px solid var(--border); }
.lang-opt { display: inline-flex; align-items: center; gap: 7px; min-block-size: 30px; padding-inline: 10px;
  border-radius: var(--radius-pill); color: var(--ink-700); font-size: var(--fs-sm); font-weight: 600;
  line-height: 1; text-decoration: none; transition: background-color var(--dur-fast) var(--ease-out),
  color var(--dur-fast) var(--ease-out); }
a.lang-opt:hover { background: var(--surface); color: var(--text); text-decoration: none; }
.lang-opt.is-current { background: var(--surface); color: var(--text); box-shadow: var(--shadow-1); }
.lang-opt:not(.is-current) .flag { opacity: .78; }
a.lang-opt:hover .flag { opacity: 1; }
.flag { flex: none; inline-size: 22px; block-size: 15px; border-radius: 3px; display: block;
  box-shadow: 0 0 0 1px rgba(11, 31, 26, .14); }

/* ---------- 10. Overlays ---------- */
dialog.modal, dialog.sheet, dialog.drawer, dialog.lightbox { border: 0; padding: 0; color: var(--text);
  background: var(--surface); box-shadow: var(--shadow-3); max-block-size: 90dvh; }
dialog::backdrop { background: rgba(10, 26, 51, .5); backdrop-filter: blur(2px); }
dialog.modal { border-radius: var(--radius-lg); inline-size: min(560px, calc(100vw - var(--space-8)));
  z-index: var(--z-modal); }
.modal-sm { inline-size: min(400px, calc(100vw - var(--space-8))); }
.modal-lg { inline-size: min(880px, calc(100vw - var(--space-8))); }
dialog.sheet { inline-size: 100vw; max-inline-size: 100vw; margin-block-end: 0; margin-block-start: auto;
  border-start-start-radius: var(--radius-xl); border-start-end-radius: var(--radius-xl);
  z-index: var(--z-sheet); }
dialog.sheet::before { content: ""; display: block; inline-size: 40px; block-size: 4px;
  border-radius: var(--radius-pill); background: var(--border-strong); margin: var(--space-2) auto; }
dialog.drawer { block-size: 100dvh; max-block-size: 100dvh; inline-size: min(480px, 100vw);
  margin-inline-start: auto; margin-block: 0; z-index: var(--z-modal); }
.modal-head, .modal-foot { padding: var(--space-4) var(--space-5); }
.modal-head { border-block-end: 1px solid var(--border); font-weight: 700; }
.modal-body { padding: var(--space-5); overflow: auto; }
.modal-foot { border-block-start: 1px solid var(--border); display: flex; gap: var(--space-3);
  justify-content: flex-end; background: var(--ink-50); }
@media (min-width: 1024px) {
  dialog.sheet { inline-size: min(560px, calc(100vw - var(--space-8))); margin: auto;
    border-radius: var(--radius-lg); }
}
/* The reserve question: "do you already have an account?", asked before the form and not after it.
   A sheet on a phone, a dialog on a laptop, and two whole-tile links - it is a fork, so both roads
   are drawn the same size; the new-account road carries the dialog's one gold action. */
dialog.reserve { overflow: auto; }
.reserve-body { display: grid; gap: var(--space-4); padding: var(--space-3) var(--space-5) var(--space-6); }
.reserve-body > * { margin: 0; }
.reserve-body h2 { font-size: var(--fs-xl); line-height: 1.4; padding-inline-end: var(--space-8); }
.reserve-close { position: absolute; inset-block-start: var(--space-3); inset-inline-end: var(--space-3); margin: 0; }
.reserve-close .btn { border-radius: 50%; background: var(--ink-50); }
.reserve-what { display: grid; gap: 2px; padding: var(--space-3) var(--space-4); border-radius: var(--radius-md);
  background: var(--ink-50); border: 1px solid var(--border); }
.reserve-what strong { font-family: var(--font-display), var(--font-arabic), sans-serif; font-weight: 600; }
.reserve-what span { font-size: var(--fs-sm); color: var(--text-muted); }
.reserve-choices { display: grid; gap: var(--space-3); grid-template-columns: minmax(0, 1fr); }
.reserve-choice { display: grid; gap: var(--space-2); align-content: start; padding: var(--space-5);
  border: 1.5px solid var(--teal-100); border-radius: var(--radius-lg); background: var(--surface); color: var(--text);
  text-decoration: none; transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out),
  transform var(--dur-fast) var(--ease-out); }
.reserve-choice.is-new { border-color: var(--gold-100); }
.reserve-choice:hover { border-color: var(--primary); box-shadow: var(--shadow-2); transform: translateY(-2px); text-decoration: none; }
.reserve-choice.is-new:hover { border-color: var(--gold-500); }
.reserve-choice > strong { font-family: var(--font-display), var(--font-arabic), sans-serif; font-size: var(--fs-lg); font-weight: 600; }
.reserve-choice > span:not(.btn):not(.reserve-icon) { font-size: var(--fs-sm); line-height: 1.6; color: var(--ink-700); }
.reserve-choice .btn { margin-block-start: var(--space-2); border-radius: var(--radius-pill); pointer-events: none; }
.reserve-icon { display: grid; place-items: center; inline-size: 46px; block-size: 46px; border-radius: 50%;
  background: var(--primary-soft); color: var(--primary); }
.reserve-choice.is-new .reserve-icon { background: var(--gold-50); color: var(--gold-600); }
.reserve-icon .icon { inline-size: 21px; block-size: 21px; }
.reserve-hint { display: flex; align-items: flex-start; gap: var(--space-2); }
.reserve-hint .icon { flex: none; inline-size: 16px; block-size: 16px; margin-block-start: 3px; }
@media (min-width: 1024px) {
  dialog.reserve { inline-size: min(720px, calc(100vw - var(--space-8))); border-radius: var(--radius-xl); }
  dialog.reserve::before { display: none; }
  .reserve-body { gap: var(--space-5); padding: var(--space-8) var(--space-8) var(--space-7); }
  .reserve-body h2 { font-size: var(--fs-2xl); }
  .reserve-choices { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (prefers-reduced-motion: no-preference) {
  dialog.reserve[open] { animation: rise 260ms var(--ease-out) both; }
}
.popover { position: absolute; z-index: var(--z-modal); background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--shadow-2);
  padding: var(--space-2); min-inline-size: 220px; display: grid; gap: 2px; }
.popover[hidden] { display: none; }
.popover .btn { justify-content: flex-start; }
.popover .caption { padding: var(--space-2) var(--space-3); margin: 0; border-block-end: 1px solid var(--border); }
#toasts { position: fixed; inset-block-end: calc(var(--tabbar-h) + var(--safe-b) + var(--space-4));
  inset-inline: var(--space-4); z-index: var(--z-toast); display: grid; gap: var(--space-2);
  justify-items: center; pointer-events: none; }
@media (min-width: 1024px) { #toasts { inset-block-end: var(--space-6); } }
.toast { pointer-events: auto; background: var(--navy-900); color: var(--text-on-brand);
  padding: var(--space-3) var(--space-5); border-radius: var(--radius-md); box-shadow: var(--shadow-2);
  font-size: var(--fs-sm); font-weight: 600; max-inline-size: 100%; }
.toast-success { background: var(--teal-600); }
.toast-warning { background: var(--warning); }
.toast-danger, .toast-error { background: var(--danger); }
.toast-info { background: var(--primary); }

/* ---------- 11. Forms ---------- */
.field { display: block; margin-block-end: var(--space-4); }
/* "How did you hear about us?": the box for naming the source appears only while "Other" is the choice. */
.field .source-other { display: none; margin-block-start: var(--space-2); }
.field:has(option[value="other"]:checked) .source-other { display: block; }
.field > label, .label { display: block; font-size: var(--fs-sm); font-weight: 600; color: var(--ink-700);
  margin: 0 0 6px; }
.field .hint, .hint { display: block; color: var(--text-muted); font-size: var(--fs-sm); margin-block-start: 5px; }
.field-error { display: block; color: var(--danger); font-size: var(--fs-sm); font-weight: 500; margin-block-start: 5px; }
.input, .select, .textarea { inline-size: 100%; min-block-size: 38px; padding: 7px var(--space-3);
  border: 1px solid var(--border-strong); border-radius: var(--radius-md); background: var(--surface);
  color: var(--text); font: inherit; font-size: var(--fs-sm); text-align: start;
  transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out); }
.input::placeholder, .textarea::placeholder { color: var(--text-faint); }
.input:hover, .select:hover, .textarea:hover { border-color: var(--ink-400); }
.input:focus, .select:focus, .textarea:focus { border-color: var(--primary); outline: 0;
  box-shadow: 0 0 0 3px var(--primary-soft); }
/* A lone field does not run the width of a laptop; inside a grid its column is already narrower. */
.field > .input, .field > .select { max-inline-size: 640px; }
.input[readonly], .textarea[readonly] { background: var(--ink-50); border-color: var(--border); color: var(--ink-700); }
.textarea { min-block-size: 110px; line-height: 1.6; resize: vertical; }
.is-invalid, [aria-invalid="true"] { border-color: var(--danger); }
.is-invalid:focus, [aria-invalid="true"]:focus { box-shadow: 0 0 0 3px #FBECE9; }
/* .is-invalid is put on the FIELD, a wrapper with no border of its own: the box inside it is what has to turn red (owner, 2026-09-21 -
   a refused save named the field at the top of the page and left the field itself looking fine). */
.field.is-invalid :is(.input, .textarea, .select, select), .field.is-invalid .input-group { border-color: var(--danger); background: #FEF7F5; box-shadow: 0 0 0 3px #FBECE9; }
.field.is-invalid > label, .field.is-invalid > .label, .field.is-invalid > .lang-tag { color: var(--danger); }
.field.is-invalid .field-error { font-weight: 600; }
.checkbox, .radio, .check { display: flex; align-items: center; gap: var(--space-2); min-block-size: 32px;
  font-size: var(--fs-sm); cursor: pointer; }
input[type="checkbox"], input[type="radio"] { inline-size: 16px; block-size: 16px; accent-color: var(--primary);
  flex: none; margin: 0; }
.switch { position: relative; display: inline-block; inline-size: 40px; block-size: 24px; flex: none; }
.switch input { position: absolute; opacity: 0; inline-size: 100%; block-size: 100%; margin: 0; cursor: pointer; }
.switch span { position: absolute; inset: 0; background: var(--ink-300);
  border-radius: var(--radius-pill); transition: background var(--dur-fast); }
.switch span::after { content: ""; position: absolute; inset-block-start: 3px;
  inset-inline-start: 3px; inline-size: 18px; block-size: 18px; border-radius: 50%;
  background: #fff; box-shadow: var(--shadow-1); transition: inset-inline-start var(--dur-fast); }
.switch input:checked + span { background: var(--primary); }
.switch input:checked + span::after { inset-inline-start: 19px; }
.file-drop { border: 1.5px dashed var(--border-strong); border-radius: var(--radius-lg);
  padding: var(--space-6); text-align: center; background: var(--ink-50); color: var(--text-muted);
  font-size: var(--fs-sm); }
.input-group { display: flex; align-items: stretch; gap: var(--space-2); }
.form-errors { border: 1px solid #F3CFC8; background: #FBECE9; color: #8F2A1A;
  border-radius: var(--radius-md); padding: var(--space-3) var(--space-4); margin-block-end: var(--space-4);
  font-size: var(--fs-sm); }
.form-errors ul { margin: var(--space-2) 0 0; padding-inline-start: var(--space-5); }
/* Two fields abreast on a laptop. A form that stacks every field full width across 1200 px is the
   single loudest "this was built for a phone" signal a screen can give. */
.form-grid { display: grid; gap: 0 var(--space-5); grid-template-columns: minmax(0, 1fr); }
@media (min-width: 800px) { .form-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .form-grid > .field-wide, .form-grid > h2, .form-grid > h3, .form-grid > hr { grid-column: 1 / -1; } }
/* The save bar of a long form, in the stylesheet both the admin and the public shell load. */
.sticky-actions, .actionbar { position: sticky; inset-block-end: 0; z-index: var(--z-sticky);
  display: flex; gap: var(--space-3); justify-content: flex-end; align-items: center;
  padding: var(--space-3) var(--space-5); margin-block-start: var(--space-5);
  background: color-mix(in srgb, var(--surface) 92%, transparent); backdrop-filter: blur(10px);
  border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--shadow-2); }

/* Inside the participant shell a phone has a fixed tab bar at the bottom; the save bar sits above it. */
@media (max-width: 1023px) { .app-main .sticky-actions { inset-block-end: calc(var(--tabbar-h) + var(--safe-b)); } }

/* ---------- 12. Data ---------- */
/* position: relative keeps a hidden label (.sr-only is absolute) inside the scroll box: without it a label in a far column
   escapes the clip and makes the whole page scroll sideways on a phone. */
.table-wrap { position: relative; overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius-lg);
  background: var(--surface); box-shadow: var(--shadow-1); }
.card.table-wrap { padding: 0; }
/* A table inside a card runs edge to edge under the card's heading: one frame, not two. */
.card > .table-wrap { margin-inline: calc(-1 * var(--space-5)); border: 0; border-block: 1px solid var(--border);
  border-radius: 0; box-shadow: none; }
.card > .table-wrap:last-child { margin-block-end: calc(-1 * var(--space-5)); border-block-end: 0;
  border-end-start-radius: var(--radius-lg); border-end-end-radius: var(--radius-lg); }
.card > .table-wrap th:first-child, .card > .table-wrap td:first-child { padding-inline-start: var(--space-5); }
.card > .table-wrap th:last-child, .card > .table-wrap td:last-child { padding-inline-end: var(--space-5); }
.table { inline-size: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.table caption { text-align: start; padding: var(--space-3) var(--space-4); color: var(--text-muted); }
.table th, .table td { padding: 11px var(--space-4); text-align: start; vertical-align: middle;
  border-block-end: 1px solid var(--border); white-space: nowrap; }
.table thead th { position: sticky; inset-block-start: 0; z-index: 1; background: var(--ink-50);
  color: var(--text-muted); font-size: var(--fs-xs); font-weight: 600; padding-block: 9px; }
.table tbody tr:last-child td { border-block-end: 0; }
.table tbody tr:hover { background: var(--ink-50); }
.table .num { font-variant-numeric: tabular-nums; }
.table td.num, .table th.num { text-align: end; }
.table .col-sticky { position: sticky; inset-inline-start: 0; background: var(--surface); }
/* The first thing in a row names it: strong ink, not a blue underlined citation. */
.table td a:not(.btn):not(.chip):not(.badge) { color: var(--text); font-weight: 600; }
.table td a:not(.btn):not(.chip):not(.badge):hover { color: var(--primary); text-decoration: none; }
.table td .caption, .table th .caption { display: block; margin-block-start: 1px; font-weight: 400; }
/* .row on a cell made the CELL a flex container, which drops it out of the table's layout and
   stacked the row's actions into a 200 px column. A cell stays a cell; its children go inline. */
.table td.row, .table td.table-actions { display: table-cell; white-space: nowrap; text-align: end; }
.table td.row > *, .table td.table-actions > * { display: inline-flex; vertical-align: middle; }
.table td.row > form, .table td.table-actions > form { display: inline-block; margin: 0; }
.table td .btn-ghost.btn-sm { padding-inline: var(--space-2); color: var(--text-muted); }
.table td .btn-ghost.btn-sm:hover { color: var(--text); }
.empty { text-align: center; padding: var(--space-9) var(--space-4); color: var(--text-muted);
  font-size: var(--fs-sm); }
.empty .icon { inline-size: 28px; block-size: 28px; margin-block-end: var(--space-3); color: var(--ink-300); }
.empty h3 { color: var(--text); font-size: var(--fs-md); margin-block-end: var(--space-1); }
.alert { border-radius: var(--radius-md); padding: var(--space-3) var(--space-4); border: 1px solid var(--border);
  background: var(--surface-2); font-size: var(--fs-sm); margin-block-end: var(--space-4); }
.alert > :last-child { margin-block-end: 0; }
.alert-info { border-color: color-mix(in srgb, var(--primary) 25%, transparent); background: var(--primary-soft); }
.alert-success { border-color: var(--teal-100); background: var(--teal-50); color: var(--teal-700); }
.alert-warning { border-color: var(--gold-100); background: var(--gold-50); color: var(--gold-700); }
.alert-danger { border-color: #F3CFC8; background: #FBECE9; color: #8F2A1A; }
.file-row { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3) 0;
  border-block-end: 1px solid var(--border); font-size: var(--fs-sm); }
.file-row:last-child { border-block-end: 0; }
.file-row .name { font-weight: 600; flex: 1 1 auto; min-inline-size: 0; }
/* An account's photo in the admin: the round thumbnail is a button, and the whole photo opens over the page (owner 2026-09-27). */
.user-photo { position: relative; flex: none; padding: 0; border: 0; border-radius: 50%; background: none; cursor: zoom-in; }
.user-photo .avatar { transition: box-shadow var(--dur-base) var(--ease-out); }
.user-photo:hover .avatar { box-shadow: 0 0 0 3px var(--surface), 0 0 0 5px var(--teal-100); }
.user-photo-zoom { position: absolute; inset-block-end: -4px; inset-inline-end: -4px; display: grid; place-items: center; inline-size: 24px; block-size: 24px;
  border: 1px solid var(--border); border-radius: 50%; background: var(--surface); color: var(--primary); box-shadow: var(--shadow-1); }
.user-photo-zoom .icon { inline-size: 13px; block-size: 13px; }
dialog.user-photo-pop { inline-size: min(760px, calc(100vw - var(--space-8))); }
dialog.user-photo-pop:not(:popover-open) { display: none; }
dialog.user-photo-pop:popover-open { display: block; }
.user-photo-pop .modal-body { display: grid; place-items: center; background: var(--ink-50); }
.user-photo-pop .modal-body img { display: block; max-inline-size: 100%; max-block-size: calc(90dvh - 10rem); border-radius: var(--radius-md); }
/* How much of a form's files has gone up (partial upload_status, js/upload.js; owner 2026-09-27): a thin bar and the percentage,
   then that the page is opening - or, in red, why it stopped. */
.upload-status { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin: 0; font-size: var(--fs-sm); font-weight: 600; color: var(--primary); }
.upload-status[hidden], .upload-status progress[hidden] { display: none; }
.upload-status progress { inline-size: 140px; block-size: 6px; appearance: none; border: 0; border-radius: var(--radius-pill); background: var(--ink-100); overflow: hidden; }
.upload-status progress::-webkit-progress-bar { background: var(--ink-100); }
.upload-status progress::-webkit-progress-value { background: var(--primary); border-radius: var(--radius-pill); }
.upload-status progress::-moz-progress-bar { background: var(--primary); border-radius: var(--radius-pill); }
.upload-status.is-failed { color: var(--danger); }
.avatar { position: relative; overflow: hidden; inline-size: 36px; block-size: 36px; border-radius: 50%; background: var(--navy-700);
  color: var(--text-on-brand); display: grid; place-items: center; font-weight: 700; font-size: var(--fs-sm);
  flex: none; }
/* A face with a photo (partials/face.php): the photo lies over the initial, so one that fails to load leaves the initial showing. */
.avatar > img { position: absolute; inset: 0; inline-size: 100%; block-size: 100%; object-fit: cover; border-radius: inherit; }
.avatar.has-photo { box-shadow: 0 0 0 1px var(--border); }
.avatar-xs { inline-size: 22px; block-size: 22px; font-size: 10px; }
.avatar-sm { inline-size: 30px; block-size: 30px; font-size: var(--fs-xs); }
.avatar-lg { inline-size: 56px; block-size: 56px; font-size: var(--fs-lg); }
.skeleton { background: linear-gradient(90deg, var(--surface-2), var(--ink-200), var(--surface-2));
  background-size: 200% 100%; animation: shimmer 1.4s infinite;
  border-radius: var(--radius-sm); block-size: 1em; }
@keyframes shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }
.bubble { max-inline-size: 80%; padding: var(--space-2) var(--space-4); font-size: var(--fs-sm);
  border-radius: var(--radius-lg); background: var(--surface-2); margin-block-end: var(--space-2); }
.bubble-me { margin-inline-start: auto; background: var(--primary-soft); }
.bubble-other { margin-inline-end: auto; }
.bubble-instructor { background: var(--navy-800); color: var(--text-on-brand); }
.bubble-pinned { border: 1px solid var(--gold-500); }
.bubble time { display: block; font-size: var(--fs-xs); color: var(--text-muted); }
.stars { display: inline-flex; flex-direction: row-reverse; gap: 2px; }
.stars input { position: absolute; opacity: 0; inline-size: 1px; block-size: 1px; }
.stars label { inline-size: 40px; block-size: 40px; display: grid; place-items: center;
  color: var(--ink-300); cursor: pointer; }
.stars input:checked ~ label, .stars label:hover, .stars label:hover ~ label { color: var(--gold-500); }
.stars-static { display: inline-flex; gap: 2px; color: var(--gold-500); }
.ring { display: inline-grid; place-items: center; position: relative; --ring-color: var(--teal-600); }
.ring svg { transform: rotate(-90deg); }
.ring circle.track { stroke: var(--ink-100); fill: none; }
.ring circle.value { stroke: var(--ring-color); fill: none; stroke-linecap: round;
  transition: stroke-dashoffset var(--dur-base) linear; }
.ring .time { position: absolute; font-weight: 700; font-variant-numeric: tabular-nums; }
.ring.is-running { border-radius: 50%; box-shadow: var(--shadow-glow); }
.ring.is-amber { --ring-color: var(--warning); }
.ring.is-red { --ring-color: var(--danger); }
.progress { inline-size: 100%; block-size: 6px; border-radius: var(--radius-pill);
  background: var(--ink-100); overflow: hidden; }
.progress > span { display: block; block-size: 100%; background: var(--primary); border-radius: inherit; }
.progress.is-live > span { background: var(--teal-600); }
.progress-stacked { display: flex; }
.progress-stacked > span { display: block; block-size: 100%; border-radius: 0; }
.bar-row { display: grid; grid-template-columns: minmax(80px, 30%) 1fr auto; gap: var(--space-3);
  align-items: center; margin-block-end: var(--space-2); font-size: var(--fs-sm); }
.timeline { display: grid; gap: var(--space-3); }
.timeline-month { position: sticky; inset-block-start: var(--topbar-h); background: var(--bg);
  padding-block: var(--space-2); font-weight: 700; font-size: var(--fs-sm); color: var(--text-muted);
  z-index: var(--z-sticky); }
.timeline-day { display: grid; grid-template-columns: 60px 1fr; gap: var(--space-4); align-items: start; }
.timeline-day .date { display: grid; place-items: center; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius-md); padding: var(--space-2);
  font-variant-numeric: tabular-nums; }
.timeline-day.is-today .date { border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }
.timeline-day.is-past { opacity: .55; }
dialog.lightbox { inline-size: 100vw; max-inline-size: 100vw; block-size: 100dvh;
  max-block-size: 100dvh; background: rgba(10, 26, 51, .96); display: grid; place-items: center; }
dialog.lightbox:not([open]) { display: none; }
dialog.lightbox img { max-block-size: 80dvh; }
dialog.lightbox .caption { color: var(--text-on-brand); text-align: center; padding: var(--space-4); }
dialog.lightbox .nav-prev { position: absolute; inset-inline-start: var(--space-2); }
dialog.lightbox .nav-next { position: absolute; inset-inline-end: var(--space-2); }


/* ---------- 12b. Components the screens asked for ---------- */
/* A repeated sub-form - one review question, one schedule day - is a compact row with its number
   at the start, not a full card per item: five questions must fit on one laptop screen. */
.item-list { display: grid; gap: var(--space-3); }
.item { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--space-3) var(--space-4);
  padding: var(--space-4); border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface); }
.item-no { inline-size: 26px; block-size: 26px; border-radius: 50%; display: grid; place-items: center;
  background: var(--primary-soft); color: var(--primary); font-size: var(--fs-xs); font-weight: 700;
  font-variant-numeric: tabular-nums; margin-block-start: 22px; }
.item-fields { display: grid; gap: var(--space-3) var(--space-4); grid-template-columns: minmax(0, 1fr); }
.item-fields .field { margin: 0; }
@media (min-width: 800px) { .item-fields { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* A review question's category: the two boxes for a new one show only while "Add a new category…" is the pick (no script). */
.category-new { display: none; gap: var(--space-2); }
.item:has([data-category-pick] option[value="__new"]:checked) .category-new { display: grid; }
/* The Review forms page's strip: one tile a category, as many as there are. */
.review-cats { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: var(--space-4); }
.review-cats .stat .value small { font-size: var(--fs-md); font-weight: 500; color: var(--text-muted); }
.review-cats .stat .hint { font-size: var(--fs-xs); }
@media (min-width: 1200px) { .item-fields-4 { grid-template-columns: 1.6fr 1.6fr 1fr 1fr; }
  .item-fields-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .item-fields-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); } }
.card + .item-list, .alert + .item-list { margin-block-start: var(--space-4); }

/* Workshop stage navigation (owner 2026-09-23): the tabs in the four stages of a workshop's life - set up, sign-ups, during,
   after - each stage a numbered name over a tray of its tabs. The stages wrap onto a second line rather than running off the
   screen; the stage holding the open tab has its number lit. Sticky only where it is one line; a phone scrolls it sideways. */
.stage-tabs { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-3) var(--space-5);
  margin-block-end: var(--space-5); padding-block: var(--space-2); background: var(--bg); }
.stage-group { display: grid; gap: 6px; min-inline-size: 0; }
.stage-group-name { display: flex; align-items: center; gap: 7px; margin: 0; padding-inline: 4px; font-size: 11px; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase; color: var(--text-muted); white-space: nowrap; }
html[lang="ar"] .stage-group-name { font-size: var(--fs-xs); letter-spacing: 0; text-transform: none; }
/* The stage's line runs on to the end of its tray: four short lengths of one track. */
.stage-group-name::after { content: ""; flex: 1; min-inline-size: 12px; block-size: 1px; background: var(--border); }
.stage-step { display: inline-grid; place-items: center; inline-size: 18px; block-size: 18px; border-radius: 50%;
  border: 1.5px solid var(--ink-300); font-size: 10px; font-weight: 700; letter-spacing: 0; line-height: 1; color: var(--text-muted); }
.stage-group.is-current .stage-group-name { color: var(--text); }
.stage-group.is-current .stage-step { border-color: var(--primary); background: var(--primary); color: var(--text-on-brand); }
.stage-group.is-current .stage-group-name::after { background: color-mix(in srgb, var(--primary) 40%, var(--border)); }
.stage-group-tabs { display: flex; gap: 2px; padding: 3px; border: 1px solid var(--border); border-radius: 12px;
  background: var(--surface); box-shadow: var(--shadow-1); }
.stage-tabs a { min-block-size: 34px; display: inline-flex; align-items: center; gap: 6px; padding-inline: 11px;
  border-radius: 9px; color: var(--ink-700); font-size: var(--fs-sm); font-weight: 600; text-decoration: none; white-space: nowrap;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out); }
.stage-tabs a:hover { background: var(--surface-2); color: var(--text); }
.stage-tabs a:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
.stage-tabs a[aria-current="page"] { background: var(--primary); color: var(--text-on-brand); }
.stage-tabs a[aria-disabled="true"] { opacity: .45; pointer-events: none; }
.stage-status { align-self: flex-end; margin-inline-start: auto; margin-block-end: 9px; }
@media (min-width: 1600px) { .stage-tabs { position: sticky; inset-block-start: var(--topbar-h); z-index: var(--z-sticky); } }
@media (max-width: 699px) {
  .stage-tabs { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; scroll-snap-type: x proximity; }
  .stage-tabs::-webkit-scrollbar { display: none; }
  .stage-group, .stage-status { flex: 0 0 auto; scroll-snap-align: start; }
}
/* A card's own buttons at its end, for a form that is not the whole screen. Not sticky: two sticky
   bars on one screen sit on top of each other. */
.form-actions, .card-foot { display: flex; flex-wrap: wrap; gap: var(--space-2); justify-content: flex-end;
  align-items: center; margin-block-start: var(--space-4); padding-block-start: var(--space-4);
  border-block-start: 1px solid var(--border); }
.input-inline, .toolbar .select, .toolbar .input-inline { inline-size: auto; min-inline-size: 150px; }
.input-sm { min-block-size: 32px; padding-block: 3px; font-size: var(--fs-xs); border-radius: 8px; }
.field-inline { display: inline-flex; align-items: center; gap: var(--space-2); margin: 0; }
.field-inline > label { margin: 0; white-space: nowrap; }
.toggle-row { display: flex; align-items: center; gap: var(--space-3); padding-block: var(--space-3);
  border-block-end: 1px solid var(--border); font-size: var(--fs-sm); }
.toggle-row:last-child { border-block-end: 0; }
.toggle-row > :first-child { margin-inline-end: auto; min-inline-size: 0; }
.toggle-row .caption { display: block; }
.thumb { inline-size: 56px; block-size: 40px; object-fit: contain; border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--surface); padding: 3px; flex: none; }
details.disclosure { border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface); }
details.disclosure + details.disclosure { margin-block-start: var(--space-2); }
details.disclosure > summary { display: flex; align-items: center; gap: var(--space-3); min-block-size: 48px;
  padding-inline: var(--space-4); cursor: pointer; font-size: var(--fs-sm); font-weight: 600; list-style: none; }
details.disclosure > summary::-webkit-details-marker { display: none; }
details.disclosure > summary > :first-child { margin-inline-end: auto; }
details.disclosure[open] > summary { border-block-end: 1px solid var(--border); }
details.disclosure > :not(summary) { padding: var(--space-4); }
.qr { inline-size: 168px; max-inline-size: 100%; padding: var(--space-2); background: #fff;
  border: 1px solid var(--border); border-radius: var(--radius-md); }
.qr svg, .qr img { display: block; inline-size: 100%; block-size: auto; }
/* A next-action card says how urgent it is with its ground, never with a stripe down one edge. */
.card-teal { background: var(--teal-50); border-color: var(--teal-100); }
.card-gold { background: var(--gold-50); border-color: var(--gold-100); }
.card-navy { background: var(--navy-900); border-color: var(--navy-900); color: var(--ink-200); }
.card-navy h2, .card-navy h3, .card-navy strong { color: #fff; }
.chip-cyan { background: var(--primary-soft); border-color: color-mix(in srgb, var(--primary) 25%, transparent);
  color: var(--primary); }

/* ---------- 13. The public home ---------- */
/* The mark is a nucleus with fourteen cells around it, and "Mi Mundo" means the world you are
   joining - so the hero shows that same cluster at the scale of a room, with the next workshop
   sitting inside it. The fifteen ellipses and their proportions are measured from the artwork, not
   a decorative dot grid, and they are the one piece of motion on the page. */
.brand-field { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.brand-field svg { position: absolute; inset-block-start: 50%; inset-inline-end: -1%;
  inline-size: min(660px, 52vw); block-size: auto; transform: translateY(-50%); }
/* Two gold orbits around the nucleus. They are what makes a field of soft green shapes read as an
   instrument rather than a background: a hairline of the certificate's own colour, drawn once. */
.brand-field .orbit { fill: none; stroke: var(--gold-500); stroke-width: .22; opacity: .34; }
.brand-field .orbit + .orbit { opacity: .2; }
.brand-field ellipse { fill: var(--teal-600); opacity: .065; transform-box: fill-box;
  transform-origin: center; animation: cell-breathe 22s var(--ease-in-out) infinite; }
/* nth-of-type, not nth-child: the orbits are drawn first so they sit behind, and they would
   otherwise take the nucleus's place in the count and leave every cell the same weight. */
.brand-field ellipse:nth-of-type(1) { opacity: .105; }
.brand-field ellipse:nth-of-type(3n) { animation-duration: 27s; animation-delay: -6s; }
.brand-field ellipse:nth-of-type(3n + 1) { animation-duration: 19s; animation-delay: -13s; }
@keyframes cell-breathe {
  0%, 100% { transform: translateY(0) scale(1); }
  50% { transform: translateY(-3%) scale(1.045); }
}
@media (prefers-reduced-motion: reduce) { .brand-field ellipse { animation: none; } }

/* One orchestrated moment, on first paint only: the world settles, then the words, then the seat.
   It is scoped inside no-preference so the resting state is the visible one - nothing here can
   leave the hero blank if the animation never runs. */
@media (prefers-reduced-motion: no-preference) {
  .card-hero .brand-field { animation: rise 700ms var(--ease-out) both; }
  .hero-copy > * { animation: rise 520ms var(--ease-out) both; }
  .hero-copy > :nth-child(1) { animation-delay: 90ms; }
  .hero-copy > :nth-child(2) { animation-delay: 160ms; }
  .hero-copy > :nth-child(3) { animation-delay: 230ms; }
  .hero-copy > :nth-child(5) { animation-delay: 370ms; }
  .hero-side > * { animation: rise 620ms var(--ease-out) 300ms both; }
}
@keyframes rise { from { opacity: 0; transform: translateY(14px); } }

.hero-grid { position: relative; display: grid; gap: var(--space-8); align-items: center;
  grid-template-columns: minmax(0, 1fr); }
@media (min-width: 1024px) { .hero-grid { grid-template-columns: minmax(0, 1.25fr) minmax(0, .75fr); gap: var(--space-10); } }
.hero-copy { position: relative; }
.hero-copy .display { margin-block-end: var(--space-5); font-size: clamp(34px, 4.4vw, 58px); }
.hero-copy .lead { font-size: var(--fs-xl); color: var(--ink-700); max-inline-size: 30em; margin-block-end: var(--space-7); }
.hero-points { list-style: none; margin: var(--space-7) 0 0; padding: var(--space-5) 0 0;
  border-block-start: 1px solid var(--border); display: flex; flex-wrap: wrap;
  gap: var(--space-3) var(--space-6); color: var(--ink-700); font-size: var(--fs-sm); font-weight: 500; }
.hero-points li { display: inline-flex; align-items: center; gap: var(--space-2); }
.hero-points .icon { color: var(--primary); }

/* The hero's second column. With no photos chosen it holds the countdown alone, over the cluster;
   with photos it holds the mosaic, and the countdown is laid over the mosaic's corner. */
.hero-side { position: relative; display: grid; justify-items: end; }
@media (max-width: 1023px) { .hero-side { justify-items: start; gap: var(--space-5); } }

/* The album photos the admin chose, laid out as one picture: a six-by-six grid whose cells are
   claimed by up to five photos, the first always the largest. The areas below are written for five
   and corrected for fewer, so any count from one to five fills the square with no hole in it. */
.hero-mosaic { inline-size: 100%; aspect-ratio: 1 / .9; display: grid; gap: 10px;
  grid-template-columns: repeat(6, minmax(0, 1fr)); grid-template-rows: repeat(6, minmax(0, 1fr)); }
.hero-mosaic figure { margin: 0; overflow: hidden; border-radius: var(--radius-xl);
  background: var(--ink-100); box-shadow: var(--shadow-2); }
.hero-mosaic img { display: block; inline-size: 100%; block-size: 100%; object-fit: cover;
  transition: transform var(--dur-slow) var(--ease-out); }
.hero-mosaic figure:hover img { transform: scale(1.04); }
.hero-mosaic > :nth-child(1) { grid-area: 1 / 1 / 5 / 5; }
.hero-mosaic > :nth-child(2) { grid-area: 1 / 5 / 4 / 7; }
.hero-mosaic > :nth-child(3) { grid-area: 4 / 5 / 7 / 7; }
.hero-mosaic > :nth-child(4) { grid-area: 5 / 1 / 7 / 3; }
.hero-mosaic > :nth-child(5) { grid-area: 5 / 3 / 7 / 5; }
.hero-mosaic[data-count="4"] > :nth-child(4) { grid-area: 5 / 1 / 7 / 5; }
.hero-mosaic:is([data-count="1"], [data-count="2"], [data-count="3"]) > :nth-child(1) { grid-area: 1 / 1 / 7 / 5; }
.hero-mosaic[data-count="2"] > :nth-child(2) { grid-area: 2 / 5 / 6 / 7; }
.hero-mosaic[data-count="1"] > :nth-child(1) { grid-area: 1 / 1 / 7 / 7; }
@media (min-width: 1024px) {
  .card-hero.has-photos .hero-grid { grid-template-columns: minmax(0, 1fr) minmax(0, .95fr); }
}
@media (prefers-reduced-motion: reduce) { .hero-mosaic img { transition: none; } }

/* The label between the ticket and the workshops listed under it. */
.section-more { margin-block: var(--space-8) var(--space-4); }

/* The next workshop is a seat you can take, so it is drawn as the thing it is: a stub carrying the
   date, a tear line, the itinerary, and how full the room is. */
.ticket { position: relative; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-xl); box-shadow: var(--shadow-3); }
.ticket-stub { position: relative; display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-5) var(--space-6); border-block-end: 1.5px dashed var(--border-strong); }
.ticket-stub::before, .ticket-stub::after { content: ""; position: absolute; inset-block-end: -11px;
  inline-size: 22px; block-size: 22px; border-radius: 50%; background: var(--bg);
  border: 1px solid var(--border); }
.ticket-stub::before { inset-inline-start: -12px; } .ticket-stub::after { inset-inline-end: -12px; }
.ticket-date { flex: none; display: grid; place-items: center; inline-size: 76px; padding-block: var(--space-3);
  border-radius: var(--radius-lg); background: var(--teal-700); color: #fff; line-height: 1.1; }
.ticket-date b { font-size: 34px; font-weight: 600; font-variant-numeric: tabular-nums;
  font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace; }
.ticket-date span { font-size: var(--fs-xs); color: var(--gold-400); font-weight: 600; }
.ticket-stub h2 { margin: 0; font-size: var(--fs-lg); }
.ticket-stub h2 a { color: var(--text); }
.ticket-stub .eyebrow { margin-block-end: 2px; color: var(--gold-600); }
.ticket-body { padding: var(--space-5) var(--space-6) var(--space-6); display: grid; gap: var(--space-4); }
.ticket-days { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; font-size: var(--fs-sm); }
.ticket-days li { display: flex; align-items: center; gap: var(--space-3); padding-block: var(--space-2);
  border-block-end: 1px solid var(--border); }
.ticket-days li:last-child { border-block-end: 0; }
/* A price as a person reads it (partial `price`): the old price struck through and quiet, the price to pay
   in ink, what is saved in teal. One partial prints every price, so no two pages can disagree. */
.price { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 2px 8px; unicode-bidi: isolate; }
.price-was { color: var(--text-faint); font-weight: 400; text-decoration-thickness: 1px; }
.price-now { font-weight: 700; color: var(--ink-900); }
/* What is saved is the one loud thing about a price: a share of it ("-33%"), white on the sale red - never a third sum of money. */
.price-save { font-style: normal; font-size: var(--fs-xs); font-weight: 700; padding: 1px 8px; border-radius: var(--radius-pill);
  background: var(--danger); color: #fff; letter-spacing: .02em; }
.price-free { font-weight: 700; color: var(--teal-700); }
.chip-price { gap: 0; }
.chip-price:empty { display: none; }


/* The sales tag (partial `price`, variant tag): where a price is SOLD - the workshop's page, the ticket's seat. A red flag says the
   share that is saved, Before and Now are said in words with an arrow between them, and the last line says the saving as money.
   A price with no discount is just Now; a free workshop says Free. On a dark ground (.on-dark) the words turn white, the flag stays red. */
.sale { display: grid; gap: var(--space-2); justify-items: start; }
.sale > * { margin: 0; }
.sale-flag { display: inline-flex; align-items: center; padding: 3px 12px; border-radius: 6px; background: var(--danger); color: #fff;
  font-family: var(--font-display), sans-serif; font-size: var(--fs-sm); font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
  rotate: -2deg; box-shadow: 0 8px 18px -8px var(--danger); animation: sale-pop 900ms var(--ease-out) 300ms both; }
html[lang="ar"] .sale-flag { letter-spacing: 0; }
@keyframes sale-pop { 0% { scale: .6; opacity: 0; } 60% { scale: 1.08; opacity: 1; } 100% { scale: 1; } }
.sale-row { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-2) var(--space-4); }
.sale-before, .sale-now { display: grid; gap: 0; }
.sale small { font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace; font-size: 11px; font-weight: 500; letter-spacing: .14em; text-transform: uppercase; color: var(--text-faint); }
html[lang="ar"] .sale small { font-family: var(--font-body), sans-serif; letter-spacing: 0; font-size: var(--fs-xs); font-weight: 600; }
.sale .price-was { font-family: var(--font-display), sans-serif; font-size: var(--fs-xl); font-weight: 500; color: var(--text-faint);
  text-decoration-color: var(--danger); text-decoration-thickness: 2px; }
.sale .price-now { font-family: var(--font-display), sans-serif; font-size: var(--fs-3xl); font-weight: 800; line-height: 1.1; color: var(--ink-900); }
.sale.is-sale .price-now { color: var(--primary); }
.sale-arrow { align-self: center; inline-size: 26px; block-size: 2px; margin-block-end: 12px; background: var(--ink-300); position: relative; }
.sale-arrow::after { content: ""; position: absolute; inset-block-start: -4px; inset-inline-end: 0; inline-size: 8px; block-size: 8px;
  border-block-start: 2px solid var(--ink-300); border-inline-end: 2px solid var(--ink-300); rotate: 45deg; }
html[dir="rtl"] .sale-arrow::after { rotate: -45deg; }
.sale-save { font-size: var(--fs-sm); font-weight: 600; color: var(--teal-700); }
.sale .price-free { font-family: var(--font-display), sans-serif; font-size: var(--fs-3xl); font-weight: 800; line-height: 1.1; color: var(--primary); }
.on-dark .sale small, .on-dark .sale .price-was { color: var(--teal-100); }
.on-dark .sale .price-now, .on-dark .sale .price-free, .on-dark .sale.is-sale .price-now { color: #fff; }
.on-dark .sale-save { color: var(--gold-400); }
.on-dark .sale-arrow, .on-dark .sale-arrow::after { background-color: transparent; border-color: var(--teal-100); }
.on-dark .sale-arrow { background: var(--teal-100); }
@media (prefers-reduced-motion: reduce) { .sale-flag { animation: none; } }

/* The three topics as a reel. All three are always in view - nothing is hidden behind a click - and one is lit at a time:
   its bar fills, then the light moves on. Hovering or tabbing to a topic takes the light and holds it. Each cell is a way
   into the workshop's page. Without home.js (or with reduced motion) they are three plain, equal links. */
.topic-reel { display: grid; gap: var(--space-3); padding-block-start: var(--space-5); border-block-start: 1px solid var(--border); }
.topic-reel-label { display: flex; align-items: center; gap: var(--space-2); margin: 0; font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
  font-size: 11px; font-weight: 500; letter-spacing: .16em; text-transform: uppercase; color: var(--text-faint); }
html[lang="ar"] .topic-reel-label { font-family: var(--font-body), var(--font-arabic), sans-serif; letter-spacing: 0; font-size: var(--fs-xs); font-weight: 600; }
.topic-reel-label .icon { inline-size: 14px; block-size: 14px; color: var(--primary); }
.topic-reel ul { display: grid; gap: var(--space-2); grid-template-columns: minmax(0, 1fr); margin: 0; padding: 0; list-style: none; }
@media (min-width: 640px) { .topic-reel ul { grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); } }
.reel-cell { position: relative; display: block; block-size: 100%; padding: var(--space-4) var(--space-4) var(--space-3); border: 1px solid var(--border);
  border-radius: var(--radius-lg); background: var(--surface); color: var(--ink-700); font-size: var(--fs-md); font-weight: 600; line-height: 1.55;
  text-decoration: none; overflow: hidden; transition: transform var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out),
  background var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out), color var(--dur-base) var(--ease-out); }
.reel-cell > span[dir] { display: block; text-align: match-parent; }
.reel-bar { position: absolute; inset-block-start: 0; inset-inline: 0; block-size: 3px; background: var(--ink-100); }
.reel-bar i { display: block; inline-size: 0; block-size: 100%; background: linear-gradient(90deg, var(--teal-600), var(--teal-400)); }
.reel-cell:hover, .reel-cell:focus-visible, .topic-reel li.is-on .reel-cell { color: var(--ink-900); background: var(--primary-soft); border-color: var(--teal-100);
  box-shadow: var(--shadow-2); transform: translateY(-3px); text-decoration: none; }
.reel-cell:hover .reel-bar i, .reel-cell:focus-visible .reel-bar i { inline-size: 100%; }
.topic-reel.is-live li.is-on .reel-bar i { animation: reel-fill 3.8s linear both; }
/* While a reader holds a topic, the light waits for them. */
.topic-reel.is-live:hover li.is-on .reel-bar i, .topic-reel.is-live:focus-within li.is-on .reel-bar i { animation-play-state: paused; }
@keyframes reel-fill { from { inline-size: 0; } to { inline-size: 100%; } }
@media (prefers-reduced-motion: reduce) { .reel-cell { transition: none; } .reel-cell:hover, .topic-reel li.is-on .reel-cell { transform: none; } }
/* The three topics a workshop is sold with: on the home ticket under its title, and at the top of its own page. */
.topic-chips { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: 0 0 var(--space-4); padding: 0; list-style: none; }
.topic-chips .chip { gap: 6px; max-inline-size: 100%; font-weight: 600; }
.topic-chips .chip .icon { flex: none; inline-size: 14px; block-size: 14px; }
.topic-chips .chip span { overflow-wrap: anywhere; }
.ticket-main .topic-chips { margin-block-end: 0; }
.ticket-days .day-no { flex: none; inline-size: 22px; block-size: 22px; border-radius: 50%; display: grid;
  place-items: center; background: var(--primary-soft); color: var(--primary); font-size: 11px; font-weight: 700; }
.ticket-days .when { margin-inline-end: auto; font-variant-numeric: tabular-nums; }
.ticket-seats { display: grid; gap: 6px; font-size: var(--fs-sm); color: var(--text-muted); }
.ticket-seats b { color: var(--text); font-variant-numeric: tabular-nums; }
/* The bar fills once on load, so how full the room is registers as a fact rather than decoration. */
.ticket-seats .progress > span { animation: seats-fill var(--dur-slow) var(--ease-out) both; }
@keyframes seats-fill { from { inline-size: 0; } }
@media (prefers-reduced-motion: reduce) { .ticket-seats .progress > span { animation: none; } }

/* On the home page the same ticket lies down across a band: the itinerary on one side of the tear
   line, the seat and the one gold action on the other. The workshop's own page keeps it upright. */
.ticket-wide { box-shadow: var(--shadow-2); }
.ticket-aside { position: relative; padding: var(--space-5) var(--space-6) var(--space-6);
  display: grid; gap: var(--space-4); align-content: center; }
.ticket-aside > * { margin: 0; }
@media (min-width: 1024px) {
  .ticket-wide { display: grid; grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr);
    grid-template-areas: "stub aside" "body aside"; align-items: start; }
  .ticket-wide > .ticket-stub { grid-area: stub; border-block-end: 1px solid var(--border); }
  .ticket-wide > .ticket-stub::before, .ticket-wide > .ticket-stub::after { display: none; }
  .ticket-wide > .ticket-body { grid-area: body; }
  .ticket-wide > .ticket-body .ticket-days { grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--space-7); }
  .ticket-wide > .ticket-aside { grid-area: aside; align-self: stretch;
    border-inline-start: 1.5px dashed var(--border-strong); }
  /* The tear line's two punched holes, at the ends of the seam it now runs along. */
  .ticket-wide > .ticket-aside::before, .ticket-wide > .ticket-aside::after { content: "";
    position: absolute; inset-inline-start: -12px; inline-size: 22px; block-size: 22px;
    border-radius: 50%; background: var(--ink-50); }
  .ticket-wide > .ticket-aside::before { inset-block-start: -12px; }
  .ticket-wide > .ticket-aside::after { inset-block-end: -12px; }
  .ticket-wide .ticket-stub h2 { font-size: var(--fs-2xl); }
}

.section-head { display: flex; flex-wrap: wrap; align-items: end; justify-content: space-between;
  gap: var(--space-3); margin-block-end: var(--space-6); }
.section-head h2 { margin: 0; font-size: var(--fs-3xl); }
.section-head p { margin: var(--space-2) 0 0; color: var(--text-muted); max-inline-size: 40em; }
.tiles { display: grid; gap: var(--space-4); grid-template-columns: minmax(0, 1fr); list-style: none; margin: 0; padding: 0; }
@media (min-width: 720px) { .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1100px) { .tiles-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.tiles.tiles-1 { grid-template-columns: minmax(0, 1fr); }
.tile { display: flex; gap: var(--space-3); align-items: flex-start; padding: var(--space-5);
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); }
.tile .icon { flex: none; inline-size: 20px; block-size: 20px; margin-block-start: 3px; color: var(--teal-600); }
.band { background: var(--surface); border-block: 1px solid var(--border); padding-block: var(--space-9); }
/* The page alternates paper and mist so each band is read as its own chapter without a single rule
   line doing the work. Two mist bands never touch. */
.band-mist { background: var(--ink-50); }
/* One sentence set large enough to be the answer to the heading above it, in the display face. */
.statement { font-family: var(--font-display), var(--font-arabic), sans-serif; font-weight: 500;
  font-size: clamp(21px, 2.5vw, 32px); line-height: 1.55; color: var(--text); margin: 0; }
html[lang="ar"] .statement { line-height: 1.7; }

/* The certificate check closes the page as a bright card: paper, one gold rule along its top, and the
   seal beside the words. The page has no dark band; gold is what sets this apart, not navy. */
.verify-strip { position: relative; display: grid; gap: var(--space-5); align-items: center;
  grid-template-columns: minmax(0, 1fr); padding: var(--space-7); overflow: hidden;
  background: var(--grad-hero); border: 1px solid var(--border); border-radius: var(--radius-xl);
  box-shadow: var(--shadow-2); color: var(--ink-700); }
.verify-strip::before { content: ""; position: absolute; inset-block-start: 0; inset-inline: 0; block-size: 3px;
  background: linear-gradient(90deg, var(--gold-500), var(--gold-400), var(--gold-500)); }
@media (min-width: 900px) { .verify-strip { grid-template-columns: auto minmax(0, 1fr) auto; gap: var(--space-7);
  padding: var(--space-8); } }
.verify-strip h2 { margin-block-end: var(--space-2); font-size: var(--fs-2xl); }
.verify-strip p { margin: 0; max-inline-size: 36em; }
.verify-strip form { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.verify-strip .input { flex: 1 1 14rem; min-block-size: 46px; border-radius: var(--radius-pill); padding-inline: var(--space-5); }
.verify-strip .btn { min-block-size: 46px; border-radius: var(--radius-pill); }
.verify-strip .seal { flex: none; position: relative; inline-size: 104px; block-size: 104px;
  display: grid; place-items: center; border-radius: 50%; background: var(--gold-50); }
.verify-strip .seal::before, .verify-strip .seal::after { content: ""; position: absolute;
  border-radius: 50%; border: 1px solid color-mix(in srgb, var(--gold-500) 70%, transparent); }
.verify-strip .seal::before { inset: 0; }
.verify-strip .seal::after { inset: 10px; border-style: dashed; border-color: color-mix(in srgb, var(--gold-500) 45%, transparent); }
.verify-strip .seal .icon { inline-size: 40px; block-size: 40px; color: var(--gold-600); }

/* ---------- 14. Sign-in screens ---------- */
.auth { display: grid; place-items: center; min-block-size: calc(100dvh - 220px); padding: var(--space-7) var(--space-4); }
.auth-card { inline-size: 100%; max-inline-size: 420px; padding: var(--space-7); box-shadow: var(--shadow-2); }
.auth-card h1 { font-size: var(--fs-2xl); }
/* Under the sign-in form, for somebody without an account: "New here?" on a rule, then Register. */
.auth-new { display: grid; gap: var(--space-3); margin-block-start: var(--space-5); }
.auth-new .hint { margin: 0; text-align: center; }
.auth-new-or { display: flex; align-items: center; gap: var(--space-3); margin: 0; color: var(--text-muted); font-size: var(--fs-sm); font-weight: 600; }
.auth-new-or::before, .auth-new-or::after { content: ""; flex: 1; block-size: 1px; background: var(--border); }
.error-page { text-align: center; padding-block: var(--space-10); }
.error-page pre { text-align: start; }

/* ---------- 15. Motion ---------- */
@view-transition { navigation: auto; }
::view-transition-old(root), ::view-transition-new(root) { animation-duration: var(--dur-base);
  animation-timing-function: var(--ease-out); }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important;
    transition-duration: .001ms !important; }
  @view-transition { navigation: none; }
}


/* Two-language forms. These two blocks lived in admin.css until an instructor's account page (the app layout) needed them too. */
/* A bilingual paragraph is one thing said twice, so it is one row: the label, then Arabic beside
   English. Two full-width labelled boxes per paragraph was eight tall empty rectangles. */
.bilingual { display: grid; gap: 0; container-type: inline-size; }
.bilingual-row { display: grid; gap: var(--space-2) var(--space-4); grid-template-columns: minmax(0, 1fr);
  padding-block: var(--space-4); border-block-start: 1px solid var(--border); }
.bilingual-row:first-child { border-block-start: 0; padding-block-start: 0; }
.bilingual-label { font-weight: 600; font-size: var(--fs-sm); color: var(--ink-700); align-self: center; }
.bilingual-box { position: relative; margin: 0; }
/* The box grows to the paragraph in it, up to a point: a description is usually two lines and
   occasionally ten, and neither should have to be scrolled inside a box the size of the other. */
.bilingual-box .textarea { min-block-size: 5.5rem; max-block-size: 20rem; field-sizing: content;
  padding-block-start: calc(var(--space-5) + 6px); }
.lang-tag { position: absolute; inset-block-start: 6px; inset-inline-end: 10px; z-index: 1;
  font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace; font-size: 10px; font-weight: 500;
  letter-spacing: .12em; color: var(--text-faint); pointer-events: none; }
@container (min-width: 760px) {
  .bilingual-row { grid-template-columns: 10rem minmax(0, 1fr) minmax(0, 1fr); align-items: start; }
  .bilingual-label { padding-block-start: var(--space-3); }
}
/* The translate bar (partial translate_bar, js/translate.js): the last card of a two-language form, right above its save bar. What
   it does is said at the start, the two directions are the buttons at the end, and the line under them says how it went. A box
   the machine filled is marked in gold until the admin touches it - it is theirs to read before saving. */
.translate-bar { display: grid; gap: var(--space-3) var(--space-6); grid-template-columns: minmax(0, 1fr); align-items: center;
  border-style: dashed; border-color: var(--teal-100); background: var(--primary-soft); }
.translate-bar h2 { display: flex; align-items: center; gap: var(--space-2); margin: 0; font-size: var(--fs-md); }
.translate-bar h2 .icon { flex: none; inline-size: 18px; block-size: 18px; color: var(--primary); }
.translate-bar .hint { margin: 2px 0 0; }
.translate-bar-go { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.translate-bar-status { margin: 0; font-size: var(--fs-sm); font-weight: 600; color: var(--text-muted); }
.translate-bar-status.is-ok { color: var(--teal-700); }
.translate-bar-status.is-no { color: var(--danger); }
@media (min-width: 900px) {
  .translate-bar { grid-template-columns: minmax(0, 1fr) auto; }
  .translate-bar-status { grid-column: 1 / -1; }
}
.input.is-translated, .textarea.is-translated { border-color: var(--gold-500); background: var(--gold-50); box-shadow: 0 0 0 3px color-mix(in srgb, var(--gold-400) 28%, transparent); }
/* The same bar as a strip inside a card: it brings its own edge, the card is not there to give it one. */
.translate-bar.is-inline { margin-block: var(--space-5) 0; padding: var(--space-4) var(--space-5); border: 1px dashed var(--teal-100); border-radius: var(--radius-lg, 16px); }
/* A payment method's mark, ONE size for every way to pay wherever it is shown (owner 2026-09-26: "Cash logo and InstaPay logo should
   be the same size in all UI"): InstaPay's white wordmark on InstaPay's purple, as InstaPay itself shows it - the one colour here
   from outside the palette, because it is theirs - and every other way its icon and name in the same pill (.pay-badge-plain). */
.pay-badge { display: inline-flex; flex: none; align-items: center; justify-content: center; gap: 6px; block-size: 28px; min-inline-size: 5.5rem;
  padding-inline: 12px; border: 1px solid #520A76; border-radius: var(--radius-pill); background: #520A76; vertical-align: middle; white-space: nowrap; }
.pay-badge img { display: block; inline-size: auto; block-size: 13px; }
.pay-badge-plain { border-color: var(--border-strong); background: var(--surface); color: var(--ink-700); font-size: var(--fs-sm);
  font-weight: 600; line-height: 1; }
.pay-badge-plain .icon { inline-size: 16px; block-size: 16px; color: var(--primary); }

/* ---------- The way to a venue's map ---------- */
/* Wherever a day says where it is held (the Agenda's day band, the Overview's next day): a white pill with a folded map, so it is
   found at once on a dark band - it was an underlined word among the others (owner 2026-10-02). */
a.map-btn { display: inline-flex; align-items: center; gap: 6px; padding-block: 4px; padding-inline: 10px 12px; border-radius: var(--radius-pill);
  background: #fff; color: var(--teal-700); font-size: var(--fs-sm); font-weight: 700; line-height: 1.3; text-decoration: none; white-space: nowrap;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .2); transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out); }
a.map-btn .icon { inline-size: 16px; block-size: 16px; color: var(--teal-700); opacity: 1; }
a.map-btn:hover { color: var(--teal-700); transform: translateY(-1px); box-shadow: 0 3px 8px rgba(0, 0, 0, .24); }
a.map-btn:focus-visible { outline: 2px solid var(--gold-400); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { a.map-btn { transition: none; } a.map-btn:hover { transform: none; } }
