/* JSST 2026 — component kit (#115, slice 2).
 *
 * The primitives every surface needs, declared once. Loaded after
 * jsst-tokens.css and before a surface's own sheet.
 *
 * Slice 1 gave the three frontends one source of colour. They still each had
 * their own button, their own empty state and their own loading line — the
 * same component written three times, drifting three ways. This is that set,
 * merged.
 *
 * WHAT BELONGS HERE: something more than one surface needs, or would need.
 * WHAT DOES NOT: anything that only makes sense on one screen. A door counter
 * is not a component; it is the door.
 *
 * `tests/test-design-kit.php` fails if a surface sheet redeclares one of these
 * — which is the mechanism that stops three vocabularies growing back.
 */

/* ---------------------------------------------------------------- button
 *
 * One button, two sizes. The door's is deliberately larger — it is pressed
 * standing up, one-handed, sometimes in a queue — but it is the same
 * component, not a second one. That was the mistake being corrected here:
 * `.jsst-scan-btn` had its own font, padding, radius and transition, and
 * shared nothing with `.jsst-btn` except intent.
 */
.jsst-btn {
	font: inherit; font-weight: 700; font-size: 14px;
	padding: 10px 18px; border-radius: var(--pill);
	border: 1px solid var(--line); background: var(--surface); color: var(--ink);
	cursor: pointer; min-height: var(--tap);
	transition: transform .08s ease, box-shadow .15s ease, background-color .15s ease, opacity .15s ease;
}
.jsst-btn:hover { box-shadow: 0 6px 16px -8px color-mix(in srgb, var(--navy) 55%, transparent); }
.jsst-btn:active { transform: translateY(1px); }
.jsst-btn[disabled] { opacity: .55; cursor: default; transform: none; box-shadow: none; }

.jsst-btn.is-primary { background: var(--navy); border-color: var(--navy); color: #fff; }
.jsst-btn.is-primary:active { background: var(--navy-press); }
.jsst-btn.is-danger { background: var(--crit); border-color: var(--crit); color: #fff; }

/* The door. Bigger type, bigger target, square-ish corners so it reads as a
   control rather than a chip at arm's length. */
.jsst-btn--door {
	border: none; border-radius: 14px;
	padding: 16px 20px; font-size: 16px; min-height: 52px;
}
.jsst-btn--door.is-primary { width: 100%; margin-top: 10px; }

/* ------------------------------------------------------------ text states
 *
 * Loading, empty and error are three different things and must stay legible
 * as three different things — a screen that renders them the same sends
 * somebody hunting for missing data that is merely still arriving.
 */
.jsst-empty,
.jsst-loading { margin: 0; color: var(--muted); }

/* On a surface that fills the viewport, these sit in the middle of nothing
   and need room around them. */
.jsst-empty--centred,
.jsst-loading--centred { text-align: center; padding: 20px 0; }
.jsst-loading--centred { padding: 24px; }

/* ------------------------------------------------------------ accessibility
 *
 * One focus ring for the whole product. Keyboard users get the same
 * affordances as everybody else, visibly, on every surface — this was
 * previously declared on the dashboard only, so the door and the
 * participant's page had none.
 */
.jsst-btn:focus-visible,
.jsst-chip:focus-visible,
.jsst-nav-link:focus-visible,
.jsst-skip:focus-visible,
.jsst-stat.is-link:focus-visible,
.jsst-occ-head:focus-visible,
.jsst-person-head:focus-visible,
.jsst-agent-head:focus-visible,
.jsst-alerts a:focus-visible,
.jsst-field input:focus-visible,
.jsst-field select:focus-visible,
.jsst-tab:focus-visible,
.jsst-input:focus-visible,
.jsst-occ-filter:focus-visible,
.jsst-occ-select:focus-visible {
	outline: 3px solid var(--cyan);
	outline-offset: 2px;
	border-radius: 8px;
}

/* Screen-reader-only text. Used for the word beside a coloured mark, which is
   what stops status being carried by hue alone. */
.jsst-sr {
	position: absolute; width: 1px; height: 1px;
	overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
}

/* Motion is a courtesy, not a requirement. */
@media (prefers-reduced-motion: reduce) {
	*, *::before, *::after {
		transition-duration: .01ms !important;
		animation-duration: .01ms !important;
	}
}



/* ---------------------------------------------------------------- account
 *
 * Who you are and the way out, in the top bar of a navy surface. Declared here
 * because two documents render it: the dashboard shell and the standalone scan
 * kiosk at /jsst-scan (#172). Both bars are navy on white, which is the only
 * assumption these rules make.
 */
.jsst-account{display:flex;align-items:center;gap:10px;margin-left:auto;min-width:0}

/* Hidden on a small phone: who is signed in is not what somebody standing in a
   venue needs at a glance, and the row has 14px of height to spare. The way OUT
   does not hide with it — see .jsst-signout. */
.jsst-user{display:none;font-size:13px;opacity:.85;min-width:0;line-height:1.25}
@media (min-width:560px){ .jsst-user{display:flex;flex-direction:column} }
.jsst-user-name{font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* The role, not only the name. Four roles drive the whole navigation, and a
   read-only account used to meet a page with controls missing and nothing on
   screen explaining why (#120). */
.jsst-user-role{font-size:11.5px;opacity:.72;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* Visible at every width, unlike the name beside it: a phone is the device that
   walks off in a pocket, so that is the surface where a way out matters most.
   Rendered for EVERY account since #172, door tablets included — which is only
   safe because pressing it opens a confirmation rather than ending the session. */
.jsst-signout{
	position:relative;flex:0 0 auto;
	display:inline-flex;align-items:center;gap:7px;
	font-size:13px;font-weight:600;color:inherit;text-decoration:none;
	padding:6px 11px;border-radius:10px;border:1px solid rgba(255,255,255,.3);
	transition:background-color .15s ease,border-color .15s ease;
	/* It is the <summary> of the confirmation below — a list item by default,
	   which paints a marker and refuses `display:inline-flex` in Safari. */
	list-style:none;cursor:pointer;user-select:none;
}
.jsst-signout::-webkit-details-marker{display:none}
/* Same answer as the chips: the paint stays small so the bar keeps its height,
   and the TARGET is extended past it to the 44px §5 asks for. */
.jsst-signout::after{
	content:"";position:absolute;left:0;right:0;top:50%;
	height:var(--tap);transform:translateY(-50%);
}
.jsst-signout:hover{background:rgba(255,255,255,.14);border-color:rgba(255,255,255,.55)}
.jsst-signout-i{width:17px;height:17px}
/* Exactly one label is ever displayed, which takes the other out of the
   accessibility tree as well as out of the layout — so the visible text is
   always the announced text (WCAG 2.5.3). Same rule as the navigation. */
.jsst-signout-full{display:none}
@media (min-width:560px){
	.jsst-signout-t{display:none}
	.jsst-signout-full{display:inline}
}
/* The confirmation (#172). `<details>` rather than script: this is rendered into
   two documents that load different bundles, and a behaviour written in one of
   them would be missing from the entrance the agents actually use. */
.jsst-signout-d{position:relative}
.jsst-signout-d[open] .jsst-signout{background:rgba(255,255,255,.14);border-color:rgba(255,255,255,.55)}

/* Exactly one of the two words is ever displayed, so the visible text is always
   the announced text — the same rule the labels above follow. */
.jsst-signout-open{display:none}
.jsst-signout-d[open] .jsst-signout-shut{display:none}
.jsst-signout-d[open] .jsst-signout-open{display:inline}
/* The glyphs are flex children of the control, not inline text. */
.jsst-signout-d[open] svg.jsst-signout-open{display:block}

.jsst-signout-menu{
	position:absolute; right:0; top:calc(100% + 8px); z-index:70;
	min-width:max-content; padding:14px;
	background:var(--surface); color:var(--ink);
	border:1px solid var(--line); border-radius:14px;
	box-shadow:0 18px 40px -18px color-mix(in srgb, var(--navy) 55%, transparent);
}
.jsst-signout-q{margin:0 0 10px;font-size:13.5px;font-weight:600}
.jsst-signout-go{display:block;text-align:center;text-decoration:none}

/* No script closes this on an outside click, so it must not be able to cover
   the work underneath on the narrowest screen. */
@media (max-width:400px){
	.jsst-signout-menu{position:fixed;right:10px;left:10px;top:auto}
}

/* ---------------------------------------------------------------- refresh
 *
 * « Mettre à jour » (#170). Same shape as the sign-out beside it — they are two
 * halves of one cluster and a second border radius there reads as a mistake.
 */
.jsst-refresh{
	flex:0 0 auto; display:inline-flex; align-items:center; gap:7px;
	font:inherit; font-size:13px; font-weight:600; color:inherit;
	background:none; padding:6px 11px; border-radius:10px;
	border:1px solid rgba(255,255,255,.3); cursor:pointer;
	transition:background-color .15s ease,border-color .15s ease;
}
.jsst-refresh:hover{background:rgba(255,255,255,.14);border-color:rgba(255,255,255,.55)}
.jsst-refresh[disabled]{cursor:default;opacity:.7}
.jsst-refresh-i{width:17px;height:17px}
/* The label drops before the icon does: on a phone the arrow is the control and
   the words are the explanation. */
.jsst-refresh-t{display:none}
@media (min-width:560px){ .jsst-refresh-t{display:inline} }

/* Working. One channel is never enough on its own, so the control is also
   disabled and announces itself — this is the third signal, not the only one. */
.jsst-refresh.is-busy .jsst-refresh-i{animation:jsst-spin .9s linear infinite}
@keyframes jsst-spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){
	.jsst-refresh.is-busy .jsst-refresh-i{animation:none;opacity:.5}
}
