/**
 * Upendo Hostel — management application design system.
 *
 * WHY THE HOSTEL HAS ITS OWN STYLESHEET
 * -------------------------------------
 * It had none. Every `lshostel-` class the views emitted styled nothing, so
 * the hostel's screens rendered as bare wp-admin markup: unstyled headings,
 * default tables, cards that were not cards. The system worked and looked
 * broken, which for the person using it is indistinguishable from broken.
 *
 * It is a separate file from the lodge's rather than a section inside it,
 * because the two businesses are separate and the interface should say so
 * before anybody reads a word. Same standard of work, different identity: the
 * lodge is teal, the hostel is indigo-slate. An administrator who runs both on
 * one login can tell from across the room which one they are looking at.
 *
 * THREE RULES, THE SAME THREE THE LODGE KEEPS
 * -------------------------------------------
 *   1. Status is never carried by colour alone. Every badge and every alert
 *      carries a word as well as a hue, because roughly one man in twelve
 *      cannot reliably separate the red from the amber, and this is an
 *      operations tool where getting that wrong means a bed double-booked.
 *
 *   2. Density over decoration. A warden reads this standing at a desk with a
 *      student waiting.
 *
 *   3. It lives inside wp-admin, so it must out-specify wp-admin without
 *      reaching for !important. The .lshostel-app scope does that.
 *
 * @package LodgeStay_PMS
 */

/* ==========================================================================
   1. TOKENS
   ========================================================================== */

.lshostel-app {
	/*
	 * THE SCALES ARE THE PLATFORM'S; ONLY THE COLOUR IS THE HOSTEL'S.
	 *
	 * These used to be twelve neutrals, seven spacing steps in PIXELS, two radii
	 * and two shadows, all written out here with their own values. The lodge had
	 * its own set with different steps in different units, and the two drifted
	 * exactly as you would expect: the same card had a 6px corner in one system
	 * and an 8px corner in the other, and nobody could have told you why.
	 *
	 * They are now aliases onto toplife.css, so there is one value behind each
	 * and changing it changes all three systems together. Every alias below
	 * preserves the pixel value the hostel had, so this is a change of
	 * PLUMBING and not of appearance — with two exceptions, both noted where
	 * they occur.
	 */
	--hs-neutral-0:   var(--tl-neutral-0);
	--hs-neutral-25:  var(--tl-neutral-25);
	--hs-neutral-50:  var(--tl-neutral-50);
	--hs-neutral-100: var(--tl-neutral-100);
	--hs-neutral-200: var(--tl-neutral-200);
	--hs-neutral-300: var(--tl-neutral-300);
	--hs-neutral-400: var(--tl-neutral-400);
	--hs-neutral-500: var(--tl-neutral-500);

	/*
	 * EXCEPTION ONE, AND IT WAS A BUG.
	 *
	 * This read `#4c536616` — eight hex digits where six were meant, which CSS
	 * reads as a colour at 9% opacity. Anywhere it was used as text the text was
	 * all but invisible; anywhere it was used as a border the border was not
	 * there. The sibling token `--hs-neutral-650` was `#4c5366`, opaque, and was
	 * almost certainly added because somebody hit this and worked around it
	 * rather than finding it.
	 *
	 * Both now point at the shared neutral, which is what they were both
	 * reaching for.
	 */
	--hs-neutral-600: var(--tl-neutral-600);
	--hs-neutral-650: var(--tl-neutral-600);

	--hs-neutral-700: var(--tl-neutral-700);
	--hs-neutral-800: var(--tl-neutral-800);
	--hs-neutral-900: var(--tl-neutral-900);

	/*
	 * Indigo-slate. Deliberately not the lodge's teal and deliberately not a
	 * bright SaaS purple: this is a student hostel's back office, and the
	 * palette is calm because most of what appears on these screens is routine.
	 *
	 * This is the hostel's identity and the only block in this file that is not
	 * an alias. It is also published as --tl-primary-* so the shared rules in
	 * toplife.css — the focus ring, chiefly — pick up the hostel's colour rather
	 * than the lodge's.
	 */
	--hs-primary-50:  #eef1f9;
	--hs-primary-100: #d8def2;
	--hs-primary-200: #b3bfe4;
	--hs-primary-300: #8496d1;
	--hs-primary-400: #5c72bd;
	--hs-primary-500: #4257a5;
	--hs-primary-600: #354688;
	--hs-primary-700: #2b396e;
	--hs-primary-800: #222d57;
	--hs-primary-900: #1a2242;

	--tl-primary-50:  var(--hs-primary-50);
	--tl-primary-100: var(--hs-primary-100);
	--tl-primary-500: var(--hs-primary-500);
	--tl-primary-700: var(--hs-primary-700);

	/*
	 * Status. Each of these is paired with a WORD everywhere it is used.
	 *
	 * EXCEPTION TWO: these now resolve to the platform's semantic ramp rather
	 * than the hostel's own slightly different greens and reds. The hostel's
	 * success green was #2e7d4f and the lodge's #1e874b — near enough that
	 * nobody would notice them side by side, and far enough that they were two
	 * decisions where there should have been one.
	 */
	--hs-good-50:    var(--tl-success-50);
	--hs-good-500:   var(--tl-success-500);
	--hs-good-700:   var(--tl-success-700);

	--hs-notice-50:  var(--tl-notice-50);
	--hs-notice-500: var(--tl-notice-500);
	--hs-notice-700: var(--tl-notice-700);

	--hs-warning-50:  var(--tl-warning-50);
	--hs-warning-500: var(--tl-warning-500);
	--hs-warning-700: var(--tl-warning-700);

	--hs-urgent-50:  var(--tl-danger-50);
	--hs-urgent-500: var(--tl-danger-500);
	--hs-urgent-700: var(--tl-danger-700);

	--hs-info-50:    var(--tl-info-50);
	--hs-info-500:   var(--tl-info-500);
	--hs-info-700:   var(--tl-info-700);
	/* The ink the platform computes for text on --hs-info-50, bridged under the
	   hostel's own name like every other colour here. It falls back to 700, so
	   the hostel is unchanged when the theme is not supplying one. */
	--hs-info-ink:   var(--tl-info-ink, var(--tl-info-700));

	/*
	 * Spacing. The hostel's seven px steps map onto the platform's rem scale
	 * with every value preserved exactly: 4, 8, 12, 16, 24, 32, 48. The gaps in
	 * the mapping (--tl-space-5, -10) are steps the hostel never had and can now
	 * reach for.
	 */
	--hs-space-1: var(--tl-space-1);  /*  4 */
	--hs-space-2: var(--tl-space-2);  /*  8 */
	--hs-space-3: var(--tl-space-3);  /* 12 */
	--hs-space-4: var(--tl-space-4);  /* 16 */
	--hs-space-5: var(--tl-space-6);  /* 24 */
	--hs-space-6: var(--tl-space-8);  /* 32 */
	--hs-space-7: var(--tl-space-12); /* 48 */

	--hs-radius:    var(--tl-radius);
	--hs-radius-lg: var(--tl-radius-lg);

	--hs-shadow-sm: var(--tl-shadow-sm);
	--hs-shadow:    var(--tl-shadow-md);

	--hs-border: 1px solid var(--tl-border);

	color: var(--hs-neutral-800);
}

/* ==========================================================================
   2. PAGE SHELL
   ========================================================================== */

.lshostel-app {
	/*
	 * A little air on the right. wp-admin's .wrap runs content to the edge of
	 * the viewport, which on a 1920px monitor puts the end of a wide table
	 * against the window frame.
	 */
	padding-right: var(--hs-space-4);
	max-width: 1600px;
}

.lshostel-masthead {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: var(--hs-space-3);
	padding: var(--hs-space-4) 0 var(--hs-space-3);
	border-bottom: 2px solid var(--hs-primary-600);
	margin-bottom: var(--hs-space-4);
}

.lshostel-masthead__title {
	margin: 0;
	padding: 0;
	font-size: 1.5rem;
	line-height: 1.2;
	font-weight: 650;
	color: var(--hs-primary-800);
}

/*
 * The screen's name, now a span inside the <h1> rather than a <p> beside it --
 * see the note in LSHOSTEL_App::masthead(). It has to look exactly as it did:
 * `display:block` restores the line of its own that a paragraph had, and the
 * weight and line-height undo what the heading would otherwise impose on it.
 * Nothing about the rendered result changes; only what it is called.
 */
.lshostel-masthead__where {
	display: block;
	margin: 0;
	font-size: 0.9rem;
	font-weight: 400;
	line-height: 1.4;
	color: var(--hs-neutral-500);
}

/*
 * The navigation and the content sit side by side on a wide screen and stack
 * on a narrow one. Grid rather than float, so the sidebar cannot end up taller
 * than the content and leave a hole under it.
 */
.lshostel-app {
	display: grid;
	grid-template-columns: 220px minmax(0, 1fr);
	/*
	 * The masthead sits over the CONTENT column, not across both, so the
	 * screen's name, the breadcrumb, the page title and the first card all
	 * begin on one vertical line. It spanned both columns until an alignment
	 * audit measured it: the title was starting 220px to the left of
	 * everything it described. The sidebar spans both rows instead; nothing
	 * about its width changes.
	 */
	grid-template-areas:
		"nav masthead"
		"nav main";
	/*
	 * THE ROWS MUST BE DECLARED, AND THIS IS THE WHOLE OF THE "HUGE BLANK AREA".
	 *
	 * The sidebar spans both rows (see the note above). Both rows were implicit,
	 * so both were `auto`, and CSS Grid distributes a spanning item's excess
	 * height EQUALLY across the auto rows it spans. The sidebar carries about
	 * thirty links and measures ~1483px; the masthead is 119px and an empty
	 * screen's content is ~114px. So the browser made each row ~741px and put
	 * the content at the top of the second one.
	 *
	 * Measured on /receipts/: `main` began at **752px**, 633px below the
	 * masthead, on a screen whose content is 114px tall. Nothing was padding,
	 * margin or min-height — the space was a grid row, sized by the sidebar.
	 *
	 * It is intermittent for a reason that matters: the distribution only shows
	 * when the sidebar is TALLER than the content column. A busy screen absorbs
	 * it and looks correct; an empty state shows all of it. Production has no
	 * business data by design, so most screens are empty states, and the defect
	 * appeared almost everywhere at once while the dashboard looked fine.
	 *
	 * `auto 1fr` says what was always meant: the masthead row is as tall as the
	 * masthead, and the content row takes the remainder — so the sidebar's
	 * height lands in the flexible row instead of being split into the fixed
	 * one. On /receipts/ this moves `main` from 752px to 135px.
	 *
	 * `align-items: start` below keeps `main` at the top of that row rather than
	 * stretching it, so nothing gains a height it did not ask for.
	 */
	grid-template-rows: auto 1fr;
	gap: 0 var(--hs-space-5);
	align-items: start;
}

.lshostel-masthead { grid-area: masthead; }
.lshostel-nav      { grid-area: nav; }
.lshostel-main     { grid-area: main; min-width: 0; }

/* ==========================================================================
   2b. THE CONTENT CONTAINER
   --------------------------------------------------------------------------
   WHY THE HOSTEL SAT AGAINST THE LEFT EDGE, AND WHY IT WAS NOT A MISSING
   PADDING.

   On the direct portal the hostel shell renders as `<div class="wrap
   lshostel-app">` inside `.lspms-manage__body`, and manage.css carries

       .lspms-manage .wrap { margin: 0; padding: 0; }

   written to strip the margins wp-admin's own `.wrap` brings, which the portal
   has no use for. Two classes beats one, so it also stripped the hostel's own
   `padding-right: var(--hs-space-4)` from section 2 — silently, and only on the
   portal, which is why the same stylesheet looked right inside wp-admin.

   The lodge was never affected because its content container is
   `.lspms__content`, which is not a `.wrap`; it keeps its 24px gutters and its
   `--tl-measure` cap, and that is the geometry the hostel is missing rather
   than any particular number.

   So the container is restored here, at the same specificity that removed it,
   in the hostel's own stylesheet and out of the hostel's own tokens. No
   `!important`, no arbitrary left padding, and wp-admin is untouched.
   ========================================================================== */

.lspms-manage .lshostel-app {
	/*
	 * A gutter on both sides rather than only the right. The old rule added air
	 * on the right alone because inside wp-admin the left was already provided
	 * by `#wpcontent`; on the portal there is no `#wpcontent`, so the left had
	 * nothing and the content started at zero.
	 */
	/*
	 * --hs-space-7 is the top of the hostel's scale. An earlier draft asked for
	 * an -8, which does not exist; the whole `padding` shorthand would have
	 * been invalid and the gutter this rule exists to add would have been
	 * silently absent, which is the exact failure it was written to fix.
	 */
	padding: 0 var(--hs-space-5) var(--hs-space-7);
	/*
	 * A measure, not the whole monitor — the same reasoning, and the same
	 * token, as `.lspms__content`. Beyond this a row of KPI cards becomes a
	 * line of distant islands.
	 */
	max-width: var(--tl-measure);
	margin-inline: auto;
}

/*
 * minmax(0, 1fr) above and min-width: 0 here are the pair that stops a wide
 * table stretching the grid column and pushing the whole page sideways. A grid
 * item's default min-width is auto, which means "as wide as my content", which
 * for a table of thirty payments means the page scrolls horizontally.
 */

/* ==========================================================================
   3. SIDEBAR
   ========================================================================== */

.lshostel-nav {
	position: sticky;
	top: 46px;
	/*
	 * The nav is taller than most viewports, and a sticky element can only
	 * hold while its grid container has room left beneath it. On a short
	 * page that room is a few dozen pixels, so the sidebar scrolled away
	 * with the content. Giving it its own height limit and scrollbar makes
	 * it hold on every page, long or short, and keeps the whole menu
	 * reachable when the menu itself is taller than the screen.
	 */
	max-height: calc(100vh - 46px);
	overflow-y: auto;
	background: var(--hs-neutral-0);
	border: var(--hs-border);
	border-radius: var(--hs-radius-lg);
	padding: var(--hs-space-3) 0;
	box-shadow: var(--hs-shadow-sm);
}

.lshostel-nav-group {
	margin: var(--hs-space-4) var(--hs-space-4) var(--hs-space-2);
	padding: 0;
	font-size: 0.68rem;
	font-weight: 700;
	letter-spacing: 0.09em;
	text-transform: uppercase;
	/*
	 * 400 MEASURED 2.87:1 ON THE NAV CARD, AND THIS IS SMALL UPPERCASE TEXT.
	 *
	 * The nav card is `--hs-neutral-0`, white in light mode, and the theme's
	 * generated 400 resolves to #969998 against it. That is under AA for any
	 * text and this is the hardest kind to read: 0.68rem, bold, tracked out by
	 * 0.09em, in capitals. Dark mode was never the problem -- the same token
	 * resolves to #979a9f on the dark card at 4.78:1 -- so the defect was
	 * light-mode only and invisible to a dark-mode contrast suite.
	 *
	 * 500 is the next step down the SAME ramp: 5.32:1 in light and 5.39:1 in
	 * dark. No new colour, and the hierarchy it exists for is untouched -- a
	 * navigation link is `--hs-neutral-700` at 11.23:1, so this heading is
	 * still visibly the quieter of the two. Quieter than its items is the job;
	 * quieter than legibility is not.
	 */
	color: var(--hs-neutral-500);
}

.lshostel-nav-group:first-child { margin-top: var(--hs-space-1); }

.lshostel-nav ul {
	margin: 0;
	padding: 0;
	list-style: none;
}

.lshostel-nav li { margin: 0; }

.lshostel-nav a {
	display: flex;
	align-items: center;
	gap: var(--hs-space-2);
	padding: var(--hs-space-2) var(--hs-space-4);
	color: var(--hs-neutral-700);
	text-decoration: none;
	font-size: 0.9rem;
	border-left: 3px solid transparent;
}

.lshostel-nav a:hover,
.lshostel-nav a:focus {
	background: var(--hs-primary-50);
	color: var(--hs-primary-700);
}

.lshostel-nav a:focus-visible {
	/*
	 * A THIRD ELEMENT, BEYOND THE TWO THAT WERE AUTHORISED, AND FLAGGED AS ONE.
	 *
	 * The regression test added for the two above measures the whole
	 * navigation, and it found this: `--hs-primary-500` resolves to #5c65ac in
	 * BOTH themes -- the dark generator lifts most steps and leaves 500 as the
	 * fill -- so on the dark card at #2a2f34 the focus ring sits at 2.52:1,
	 * under the 3:1 a non-text indicator needs. A keyboard user in dark mode
	 * cannot reliably see where they are.
	 *
	 * 600 is the next step on the SAME ramp, no new colour, and it is already
	 * the active row's left bar and active icon two rules below -- so the
	 * focused and the current row now agree about which colour means "this
	 * one". 7.63:1 in light, 4.67:1 in dark.
	 *
	 * Written up rather than folded in silently: the correction that was asked
	 * for covered two elements and this is a third. It is one token on the same
	 * ramp and it is reversible in one line.
	 */
	outline: 2px solid var(--hs-primary-600);
	outline-offset: -2px;
}

.lshostel-nav a.current {
	background: var(--hs-primary-50);
	border-left-color: var(--hs-primary-600);
	color: var(--hs-primary-800);
	font-weight: 600;
}

.lshostel-nav .dashicons {
	font-size: 17px;
	width: 17px;
	height: 17px;
	/*
	 * The same step, for the same reason. An icon is a non-text control and
	 * needs 3:1 rather than 4.5, but 400 was 2.87:1 and failed even that.
	 */
	color: var(--hs-neutral-500);
	flex: none;
}

.lshostel-nav a.current .dashicons { color: var(--hs-primary-600); }

/* ==========================================================================
   4. BREADCRUMBS
   ========================================================================== */

.lshostel-crumbs {
	margin: 0 0 var(--hs-space-4);
	font-size: 0.82rem;
	color: var(--hs-neutral-500);
}

.lshostel-crumbs ol {
	display: flex;
	flex-wrap: wrap;
	gap: var(--hs-space-2);
	margin: 0;
	padding: 0;
	list-style: none;
}

.lshostel-crumbs li { margin: 0; }

/* The separator is generated, so it is never read aloud as a list item. */
.lshostel-crumbs li + li::before {
	content: "\203A";
	margin-right: var(--hs-space-2);
	color: var(--hs-neutral-300);
}

.lshostel-crumbs [aria-current="page"] {
	color: var(--hs-neutral-800);
	font-weight: 600;
}

/* ==========================================================================
   5. SECTIONS AND HEADINGS
   ========================================================================== */

.lshostel-section { margin: 0 0 var(--hs-space-6); }

.lshostel-h2 {
	margin: 0 0 var(--hs-space-3);
	padding: 0;
	font-size: 1.05rem;
	font-weight: 650;
	color: var(--hs-neutral-800);
}

.lshostel-h3 {
	margin: var(--hs-space-5) 0 var(--hs-space-3);
	font-size: 1rem;
	font-weight: 600;
	color: var(--hs-neutral-700);
}

.lshostel-lead {
	margin: 0 0 var(--hs-space-4);
	max-width: 74ch;
	color: var(--hs-neutral-650);
	font-size: 0.92rem;
	line-height: 1.6;
}

/* ==========================================================================
   6. BRIEFING
   ========================================================================== */

.lshostel-briefing {
	background: linear-gradient(135deg, var(--hs-primary-700), var(--hs-primary-500));
	color: var(--hs-neutral-0);
	border-radius: var(--hs-radius-lg);
	padding: var(--hs-space-5);
	margin-bottom: var(--hs-space-5);
}

.lshostel-briefing__greeting {
	margin: 0 0 var(--hs-space-1);
	padding: 0;
	font-size: 1.35rem;
	font-weight: 650;
	/*
	 * White on the gradient's lightest stop is 4.9:1. Stated because it is the
	 * one place in this file where text sits on a coloured field rather than on
	 * a neutral, and it is the first thing that would break if the gradient
	 * were lightened for looks.
	 */
	color: var(--hs-neutral-0);
}

.lshostel-briefing__lead {
	margin: 0 0 var(--hs-space-3);
	font-size: 0.95rem;
	color: rgba(255, 255, 255, 0.92);
}

.lshostel-briefing__body {
	margin: 0 0 var(--hs-space-2);
	font-size: 1rem;
	line-height: 1.55;
	max-width: 70ch;
	color: var(--hs-neutral-0);
}

.lshostel-briefing__stamp {
	margin: 0;
	font-size: 0.8rem;
	color: rgba(255, 255, 255, 0.78);
}

/* ==========================================================================
   7. KPI CARDS
   ========================================================================== */

.lshostel-kpis {
	display: grid;
	/*
	 * auto-fill with a minimum, so the row reflows instead of squashing. 210px
	 * is the width at which "TZS 1,250,000" still fits on one line at the value
	 * font size; below that the number wraps and a KPI card whose number wraps
	 * is unreadable.
	 */
	grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
	gap: var(--hs-space-3);
	margin-bottom: var(--hs-space-4);
}

.lshostel-kpi {
	position: relative;
	background: var(--hs-neutral-0);
	border: var(--hs-border);
	border-radius: var(--hs-radius-lg);
	padding: var(--hs-space-4);
	box-shadow: var(--hs-shadow-sm);
	min-width: 0;
}

.lshostel-kpi__icon {
	position: absolute;
	top: var(--hs-space-4);
	right: var(--hs-space-4);
	font-size: 18px;
	width: 18px;
	height: 18px;
	color: var(--hs-neutral-300);
}

.lshostel-kpi__label {
	margin: 0 var(--hs-space-6) var(--hs-space-2) 0;
	padding: 0;
	font-size: 0.75rem;
	font-weight: 600;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--hs-neutral-500);
}

.lshostel-kpi__value {
	margin: 0 0 var(--hs-space-2);
	font-size: 1.6rem;
	line-height: 1.15;
	font-weight: 650;
	color: var(--hs-neutral-900);
	font-variant-numeric: tabular-nums;
	/*
	 * A long amount shrinks the gap between digits rather than wrapping. The
	 * non-breaking space in "TZS 150,000" keeps the code with the number; this
	 * keeps the number itself on one line down to about 320px.
	 */
	overflow-wrap: break-word;
	word-break: normal;
}

.lshostel-kpi__description {
	margin: 0;
	font-size: 0.8rem;
	line-height: 1.45;
	color: var(--hs-neutral-500);
}

/* ==========================================================================
   8. ALERTS
   ========================================================================== */

.lshostel-alerts {
	margin: 0;
	padding: 0;
	list-style: none;
	display: grid;
	gap: var(--hs-space-2);
}

.lshostel-alert {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--hs-space-3);
	margin: 0;
	padding: var(--hs-space-3) var(--hs-space-4);
	border-radius: var(--hs-radius);
	border: 1px solid transparent;
	/* The left rule is a second, non-colour signal of severity: thicker for
	   the ones that matter more. */
	border-left-width: 4px;
	background: var(--hs-neutral-50);
	font-size: 0.9rem;
}

.lshostel-alert__level {
	flex: none;
	font-size: 0.68rem;
	font-weight: 750;
	letter-spacing: 0.08em;
	padding: 2px var(--hs-space-2);
	border-radius: 3px;
}

.lshostel-alert__message {
	flex: 1 1 240px;
	min-width: 0;
	line-height: 1.5;
}

.lshostel-alert__action {
	flex: none;
	font-weight: 600;
	text-decoration: none;
	white-space: nowrap;
}

.lshostel-alert--urgent {
	background: var(--hs-urgent-50);
	border-color: var(--hs-urgent-500);
}
.lshostel-alert--urgent .lshostel-alert__level {
	background: var(--hs-urgent-500);
	color: #fff;
}
.lshostel-alert--urgent .lshostel-alert__action { color: var(--hs-urgent-700); }

.lshostel-alert--warning {
	background: var(--hs-warning-50);
	border-color: var(--hs-warning-500);
}
.lshostel-alert--warning .lshostel-alert__level {
	background: var(--hs-warning-500);
	color: #fff;
}
.lshostel-alert--warning .lshostel-alert__action { color: var(--hs-warning-700); }

.lshostel-alert--notice {
	background: var(--hs-notice-50);
	border-color: var(--hs-notice-500);
}
.lshostel-alert--notice .lshostel-alert__level {
	background: var(--hs-notice-500);
	color: #fff;
}
.lshostel-alert--notice .lshostel-alert__action { color: var(--hs-notice-700); }

.lshostel-alert--info {
	background: var(--hs-info-50);
	border-color: var(--hs-info-500);
}
.lshostel-alert--info .lshostel-alert__level {
	background: var(--hs-info-500);
	color: #fff;
}
.lshostel-alert--info .lshostel-alert__action { color: var(--hs-info-ink, var(--hs-info-700)); }

/* ==========================================================================
   9. BADGES
   ========================================================================== */

.lshostel-badge {
	display: inline-flex;
	align-items: center;
	gap: 4px;
	padding: 2px var(--hs-space-2);
	border-radius: 3px;
	font-size: 0.7rem;
	font-weight: 700;
	letter-spacing: 0.03em;
	white-space: nowrap;
}

/*
 * The hostel colours its badges by TONE, and pending, cancelled and refunded
 * all resolve to `notice` — three states a bookkeeper has to tell apart,
 * printed in one colour. The colour was not merely insufficient there, it was
 * saying they were the same. The mark is per state, so they stop matching.
 */
.lshostel-badge__mark {
	font-size: 12px;
	width: 12px;
	height: 12px;
	line-height: 12px;
	flex: 0 0 12px;
}

.lshostel-badge__label { display: inline; }

.lshostel-badge--good    { background: var(--hs-good-50);    color: var(--hs-good-700); }
.lshostel-badge--notice  { background: var(--hs-notice-50);  color: var(--hs-notice-700); }
.lshostel-badge--warning { background: var(--hs-warning-50); color: var(--hs-warning-700); }
.lshostel-badge--urgent  { background: var(--hs-urgent-50);  color: var(--hs-urgent-700); }
.lshostel-badge--info    { background: var(--hs-info-50);    color: var(--hs-info-ink, var(--hs-info-700)); }

/* ==========================================================================
   10. QUICK ACTIONS
   ========================================================================== */

.lshostel-actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--hs-space-2);
}

.lshostel-action {
	display: inline-flex;
	align-items: center;
	gap: var(--hs-space-2);
	/* 40px tall, which is a comfortable target with a thumb. */
	min-height: 40px;
	padding: var(--hs-space-2) var(--hs-space-4);
	background: var(--hs-neutral-0);
	border: var(--hs-border);
	border-radius: var(--hs-radius);
	color: var(--hs-neutral-700);
	text-decoration: none;
	font-size: 0.88rem;
	font-weight: 500;
	box-shadow: var(--hs-shadow-sm);
}

.lshostel-action:hover,
.lshostel-action:focus {
	border-color: var(--hs-primary-400);
	color: var(--hs-primary-700);
	background: var(--hs-primary-50);
}

.lshostel-action:focus-visible {
	outline: 2px solid var(--hs-primary-500);
	outline-offset: 1px;
}

.lshostel-action .dashicons {
	font-size: 16px;
	width: 16px;
	height: 16px;
	color: var(--hs-primary-500);
}

/* ==========================================================================
   11. TODAY'S OPERATIONS
   ========================================================================== */

.lshostel-ops {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
	gap: var(--hs-space-3);
}

.lshostel-op {
	background: var(--hs-neutral-0);
	border: var(--hs-border);
	border-radius: var(--hs-radius-lg);
	padding: var(--hs-space-4);
	min-width: 0;
}

.lshostel-op__title {
	margin: 0 0 var(--hs-space-2);
	padding: 0;
	font-size: 0.72rem;
	font-weight: 700;
	letter-spacing: 0.07em;
	text-transform: uppercase;
	color: var(--hs-neutral-500);
}

.lshostel-op__count {
	margin: 0 0 var(--hs-space-1);
	font-size: 1.7rem;
	font-weight: 650;
	line-height: 1.1;
	color: var(--hs-neutral-900);
	font-variant-numeric: tabular-nums;
}

.lshostel-op__note {
	margin: 0;
	font-size: 0.8rem;
	line-height: 1.4;
	color: var(--hs-neutral-500);
}

/* An empty note leaves no ghost gap under the number. */
.lshostel-op__note:empty { display: none; }

/* ==========================================================================
   12. EMPTY STATES
   ========================================================================== */

/*
 * An empty state has to look INTENDED. The failure it guards against is a
 * screen that has nothing on it because there is nothing to show, being read
 * as a screen that has nothing on it because it broke. So: a bordered panel
 * with two sentences, not a bare line of grey text.
 */
.lshostel-empty-state {
	background: var(--hs-neutral-25);
	border: 1px dashed var(--hs-neutral-300);
	border-radius: var(--hs-radius-lg);
	padding: var(--hs-space-5);
	text-align: center;
	margin: 0 0 var(--hs-space-4);
}

.lshostel-empty-state__heading {
	margin: 0 0 var(--hs-space-2);
	font-size: 0.98rem;
	font-weight: 600;
	color: var(--hs-neutral-700);
}

.lshostel-empty-state__body {
	margin: 0 auto;
	max-width: 62ch;
	font-size: 0.87rem;
	line-height: 1.55;
	color: var(--hs-neutral-500);
}

/* ==========================================================================
   13. TABLES
   ========================================================================== */

/*
 * The wrapper scrolls, not the page. A payments table with seven columns is
 * wider than a phone; without this the whole document scrolls sideways and the
 * navigation goes with it.
 */
.lshostel-tablewrap {
	/*
	 * `position: relative` is not decoration - it is what makes the clipping
	 * work, and the lodge's `.lspms-scroll` carries the same line with the same
	 * reasoning written out beside it.
	 *
	 * An absolutely positioned descendant is clipped by an ancestor's overflow
	 * only when that ancestor is also its CONTAINING BLOCK, and a `static`
	 * scroller is not one. WordPress's own `.screen-reader-text` is
	 * `position: absolute`; inside a static scroller those spans are laid out
	 * against a containing block further up, escape the scroller entirely, and
	 * add their off-screen width to the page.
	 *
	 * The lodge found this on its calendar, which pushed the document 30px
	 * sideways at 375px while every visible element measured inside the
	 * viewport. The hostel had the same hole and nothing had fallen into it:
	 * no hostel table contained an absolutely positioned element until the
	 * month-end screen put a labelled form in one, at which point the page went
	 * 690px wide at 320px. The visible elements were all where they should be.
	 */
	position: relative;
	overflow-x: auto;
	-webkit-overflow-scrolling: touch;
	border: var(--hs-border);
	border-radius: var(--hs-radius-lg);
	background: var(--hs-neutral-0);
	margin-bottom: var(--hs-space-4);
}

.lshostel-table {
	width: 100%;
	border-collapse: collapse;
	font-size: 0.88rem;
}

/* A table that is its own wrapper still needs the frame. */
.lshostel-table:not(.lshostel-tablewrap > .lshostel-table) {
	background: var(--hs-neutral-0);
}

.lshostel-table th,
.lshostel-table td {
	padding: var(--hs-space-3) var(--hs-space-4);
	text-align: left;
	border-bottom: 1px solid var(--hs-neutral-100);
	vertical-align: top;
}

.lshostel-table thead th {
	background: var(--hs-neutral-50);
	font-size: 0.72rem;
	font-weight: 700;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--hs-neutral-500);
	white-space: nowrap;
	border-bottom: 1px solid var(--hs-neutral-200);
}

.lshostel-table tbody tr:last-child th,
.lshostel-table tbody tr:last-child td { border-bottom: 0; }

.lshostel-table tbody tr:hover { background: var(--hs-neutral-25); }

.lshostel-table--compact th,
.lshostel-table--compact td {
	padding: var(--hs-space-2) var(--hs-space-3);
}

.lshostel-unset {
	color: var(--hs-neutral-400);
	font-style: italic;
}

/*
 * A sentence explaining what a control will do, sitting under it. Quieter than
 * the surrounding text but not as quiet as `.lshostel-unset`, which means
 * "nobody has filled this in" — this means "read this before you press it".
 */
.lshostel-hint {
	color: var(--hs-neutral-500);
	font-size: 13px;
	margin: 4px 0 12px;
	max-width: 60ch;
}

/*
 * A form's fields, two or three across depending on the width available. auto-fit
 * rather than a fixed column count, so the registration form is one column on a
 * phone at the reception desk without a media query saying where the break is.
 */
.lshostel-formgrid {
	display: grid;
	gap: 0 16px;
	grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}

.lshostel-field {
	display: flex;
	flex-direction: column;
	gap: 4px;
	margin: 0 0 12px;
	max-width: 640px;
}

.lshostel-field label {
	color: var(--hs-neutral-600);
	font-size: 13px;
	font-weight: 600;
}

.lshostel-field input,
.lshostel-field select,
.lshostel-field textarea {
	max-width: 100%;
	width: 100%;
}

/* ==========================================================================
   13a. THE HOSTEL FORM-CONTROL BASELINE
   ==========================================================================
   THE PORTAL HAS NO wp-admin forms.css, AND NOTHING REPLACED IT.

   `LSPMS_Manage_Screen::handles()` prints an explicit allowlist of stylesheets
   and deliberately omits wp-admin's, because they would drag WordPress's visual
   identity onto the portal. wp-admin is where every control gets its height,
   padding, border and radius — so on hostel.toplifelodge.co.tz a control had
   none of them and fell back to the browser default.

   Measured across all 40 rendered Hostel screens: ELEVEN of them carried
   controls 19-21px tall — allocate, announcements, applications, dashboard,
   monthend, payments, residents, roles, settings, staff and verifications. The
   admission form was simply the first one anybody looked at closely.

   Scoped to `.lshostel-app`, which is the Hostel shell and nothing else: the
   Lodge and Group portals are untouched. Deliberately LOW specificity (0,2,1)
   and placed above the screen-specific rules, so `.lshostel-appform`'s controls
   and `.lshostel-rangeform`'s date fields continue to win where they have
   something more particular to say. This is a floor, not an override.
   ========================================================================== */

.lshostel-app input[type="text"],
.lshostel-app input[type="search"],
.lshostel-app input[type="email"],
.lshostel-app input[type="tel"],
.lshostel-app input[type="url"],
.lshostel-app input[type="number"],
.lshostel-app input[type="password"],
.lshostel-app input[type="date"],
.lshostel-app select,
.lshostel-app textarea {
	background: var(--hs-neutral-0);
	border: 1px solid var(--hs-neutral-300);
	border-radius: var(--hs-radius);
	box-sizing: border-box;
	color: inherit;
	font: inherit;
	min-height: 40px;
	padding: var(--hs-space-2) var(--hs-space-3);
}

.lshostel-app textarea {
	min-height: 80px;
	padding: var(--hs-space-3);
}

/*
 * A file input is a control the browser draws and will not take a height the
 * way a text field will. It gets the spacing and is otherwise left alone.
 */
.lshostel-app input[type="file"] {
	font: inherit;
	padding: var(--hs-space-2) 0;
}

/*
 * A disabled control should read as unavailable rather than as empty.
 */
.lshostel-app input:disabled,
.lshostel-app select:disabled,
.lshostel-app textarea:disabled {
	background: var(--hs-neutral-50);
	color: var(--hs-neutral-500);
	cursor: not-allowed;
}

.lshostel-app ::placeholder {
	color: var(--hs-neutral-500);
	opacity: 1;
}

/* ==========================================================================
   13b. THE ADMISSION FORM
   ==========================================================================
   Four rules, and every value in them is a token this file already declares.
   The form is a long document rather than a dialogue, so what it needs from
   the stylesheet is not decoration: it is the ability to tell four kinds of
   block apart at a glance while scrolling.

   Nothing here introduces a colour. `--hs-primary-*` is the hostel's ramp,
   which the whole platform now wears, and the semantic families are untouched.
   ========================================================================== */

/*
 * The conditional half of the health question.
 *
 * Indented and ruled rather than merely revealed, because it belongs TO the
 * answer above it: a paragraph and a file upload that appear at the same
 * indentation as everything else read as two more fields on the form, and a
 * reader who has just chosen "No" cannot see why they are still there.
 */
.lshostel-appform__conditional {
	border-left: 3px solid var(--hs-primary-200);
	margin: 0 0 16px;
	padding: 4px 0 4px 16px;
}

/*
 * FOR OFFICIAL USE ONLY, and the separation is the requirement rather than a
 * preference. On the paper form it is a ruled box with its own heading,
 * because what is inside it is the hostel's answer and everything above it is
 * the student's question. A screen that ran the two together would invite
 * somebody to read an allocation as something the applicant had asked for.
 */
.lshostel-appform__official {
	background: var(--hs-primary-50);
	border: 1px solid var(--hs-primary-200);
}

/*
 * One signature, with what is recorded above the way to record it. Bounded so
 * that three of them in a column do not read as one long list of fields.
 */
/*
 * ONE SIGNATURE CARD, USED THREE TIMES.
 *
 * `application_signature()` is a single generator called for the student, the
 * parent or guardian and the authorising officer, so the markup was already
 * shared. What was not shared was a card: the block drew a top RULE and no
 * box, and the form inside it carried `.lshostel-form--inline`, which is an
 * inline-block and therefore SHRINK-WRAPS. Measured at 1440px the form was
 * 267px wide inside a 1148px section — the "small floating card pushed to the
 * left". The three blocks looked different from one another only because their
 * longest label differed, which is what shrink-wrapping does.
 *
 * So: a real card, and a grid inside it rather than an inline-block. The grid
 * is `auto-fit` over a 240px minimum, so the name and the file control sit side
 * by side where there is room and stack where there is not, with no breakpoint
 * deciding it. `max-width` keeps a text field at a readable measure instead of
 * running it across a 1600px monitor.
 */
.lshostel-appform__signature {
	background: var(--hs-neutral-0);
	border: var(--hs-border);
	border-radius: var(--hs-radius);
	margin: 0 0 var(--hs-space-3);
	/*
	 * THE CARD IS CAPPED, NOT ONLY ITS CONTENTS.
	 *
	 * Capping the form alone left the CARD stretching to its section: measured
	 * at 1440px the card was 1148px, the form 760px, and 355px of the card was
	 * empty to the right of the last control. A box that wide around controls
	 * that narrow reads as a layout that failed rather than one that chose.
	 *
	 * 794px is the form's 760px measure plus this card's own 16px of padding on
	 * each side and its 1px borders, so the card is exactly as wide as what it
	 * holds. Below about 826px of available width it is the section that is
	 * narrower, the cap stops applying and the card fills the row -- which is
	 * why 1024px measured 1px of slack before this and needs no breakpoint now.
	 */
	max-width: 794px;
	padding: var(--hs-space-4);
}

.lshostel-appform__signature:last-child {
	margin-bottom: 0;
}

/*
 * The form is the card's content, so it fills the card rather than
 * shrink-wrapping inside it. `--inline` is doing nothing useful here and the
 * grid replaces it; the class stays on the element because it is the same
 * generator that other screens use.
 */
.lshostel-appform__signature .lshostel-form {
	align-items: end;
	display: grid;
	gap: var(--hs-space-3) var(--hs-space-4);
	grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
	max-width: 760px;
	width: 100%;
}

/*
 * A hidden input is still a grid ITEM. Six of them sit in this form -- the
 * nonce, the referer, the action, the task, the id and the signatory -- and
 * without this they take six cells and push the two real fields onto a second
 * row, leaving a card that is mostly empty. `display: none` takes them out of
 * layout without changing what they submit.
 */
.lshostel-appform__signature .lshostel-form input[type="hidden"] {
	display: none;
}

.lshostel-appform__signature .lshostel-form > .lshostel-field {
	margin-bottom: 0;
}

/*
 * Record sits on its own full-width row under the fields it records, in the
 * same place in all three cards.
 */
/*
 * Record ends the row it belongs to, under the controls it records, at the
 * right-hand edge in all three cards. `justify-self` moves the paragraph
 * within its cell and leaves the button its own size, so the touch target is
 * unchanged.
 */
.lshostel-appform__signature .lshostel-form > p:last-child {
	grid-column: 1 / -1;
	justify-self: end;
	margin: 0;
}

/*
 * The row of things that can be done to an application: the workflow buttons,
 * and the print button beside them.
 *
 * Each of those is its own <form>, because each posts a different task with its
 * own nonce, and `.lshostel-form--inline` makes a form an inline-block so
 * several can share a line. They were laid out in `.lshostel-formgrid` at
 * first, which is a three-column grid: one button then sat in a third of the
 * page with two empty columns beside it, which reads as a form somebody forgot
 * to finish. A flex row that wraps is what a variable number of buttons wants.
 */
.lshostel-appform__actions {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	gap: var(--hs-space-3);
}

.lshostel-appform__actions .lshostel-form--inline {
	margin: 0;
}

/*
 * A DESTRUCTIVE BUTTON, IN THE SEMANTIC FAMILY THAT ALREADY EXISTS.
 *
 * `--hs-urgent-*` is the hostel's alias onto the platform's danger ramp and is
 * already what an urgent alert and an urgent badge wear. No new colour is
 * introduced here and none of those meanings is borrowed for anything else:
 * this button IS the danger case.
 *
 * It is outlined rather than filled. Mark Submitted is the action somebody
 * came to this screen to perform; Clear Draft is the one they must not press by
 * accident, and a filled red block beside a plain button pulls the eye toward
 * exactly the wrong one.
 *
 * SCOPED TO `.lshostel-app`, AND THAT IS NOT DECORATION.
 *
 * `manage.css` styles the portal's buttons as `.lspms-manage .button`, which is
 * (0,2,0). A bare `.lshostel-button--danger` is (0,1,0) and LOSES: the first
 * version of this rule was written that way and the button rendered in the
 * ordinary near-black on white, with none of the danger styling reaching it at
 * all. Scoping to the hostel shell matches that specificity exactly, and
 * `hostel.css` is printed after `manage.css`, so source order settles it. No
 * `!important`, and nothing is escalated beyond the rule it has to answer.
 */
/*
 * The border is `currentColor` rather than `--hs-urgent-500`, and that is a
 * contrast decision rather than a shortcut. Measured on the rendered button:
 * `--hs-urgent-500` gives 5.44:1 against the light card and **2.63:1** against
 * the dark one, which is under the 3:1 a non-text boundary needs. The ink is
 * 8.34:1 light and 5.70:1 dark, so binding the edge to the ink passes in both
 * themes and keeps one authoritative declaration instead of two that have to be
 * kept in step.
 */
.lshostel-app .lshostel-button--danger {
	background: var(--hs-neutral-0);
	border: 1px solid currentColor;
	color: var(--hs-urgent-700);
}

.lshostel-app .lshostel-button--danger:hover,
.lshostel-app .lshostel-button--danger:focus-visible {
	background: var(--hs-urgent-50);
	color: var(--hs-urgent-700);
}

/*
 * The form's own fields are allowed to be narrower than the page.
 *
 * `.lshostel-field` caps at 640px, which is right for a settings screen and
 * wrong for a grid of ten short particulars: at a desktop width the grid gives
 * each column about 300px and the cap never comes into play, but a single
 * trailing field on its own row stretches to the full measure and looks like a
 * different kind of control. Inside the admission form they share the grid's
 * column and nothing else.
 */
.lshostel-appform .lshostel-field {
	max-width: 100%;
}

/* --------------------------------------------------------------------------
   13c. THE ADMISSION FORM'S GEOMETRY
   --------------------------------------------------------------------------
   THE PAGE WAS NOT TOO LONG BECAUSE OF ITS CONTENT.

   Measured in a real engine at 1440px, one signature block was 317px tall and
   the page carries three of them. Its content — a heading, a status line, two
   fields and a button — accounts for about 150px. The rest was chrome:

     12px  the block's own padding-top
   + 24px  margin-TOP on a heading that is the block's FIRST child
   + 12px  that heading's margin-bottom
   + 14px  the status paragraph's default top margin
   + 24px  the inner <form>'s own padding (8px top, 16px bottom)
   + 16px  that form's margin-bottom, inside a block that has already ended
   + 14px  a default top margin on the paragraph holding the Record button

   None of those were decisions about this block. They are defaults inherited
   from elsewhere — a heading rule written for headings that follow something,
   and paragraph margins from the document stylesheet — meeting in a container
   nobody measured. `FOR OFFICIAL USE ONLY` then looked enormous for the same
   reason and only that reason: it wraps one of these blocks.

   So this section removes inherited chrome. It sets no fixed height, no
   min-height and no max-height anywhere: every block here still sizes to its
   content, and the content is unchanged.
   -------------------------------------------------------------------------- */

/*
 * A heading that opens a block has nothing above it to be separated FROM.
 * `.lshostel-h3` carries `margin-top: 24px` because most headings follow a
 * paragraph; the first child of a card follows a border.
 */
.lshostel-appform > .lshostel-h3:first-child,
.lshostel-appform__official > .lshostel-h3:first-child {
	margin-top: 0;
}

/*
 * The form is the section's content, not a card inside it. Its padding and
 * bottom margin were drawing a second box inside the first.
 */
.lshostel-appform__form,
.lshostel-appform__signature .lshostel-form {
	margin: 0;
	padding: 0;
}

/*
 * FOR OFFICIAL USE ONLY draws a background and a border and had NO padding, so
 * its rule sat on its content. Given the box, it needs the inset.
 */
.lshostel-appform__official {
	border-radius: var(--hs-radius);
	padding: var(--hs-space-4);
}

/*
 * Sub-headings inside the form separate B from C from the next of kin. They
 * are not sections, so they do not need a section's air above them; 16px still
 * reads as a break and 24px was competing with the section rhythm itself.
 */
.lshostel-appform .lshostel-h3,
.lshostel-appform__official .lshostel-h3 {
	margin-top: var(--hs-space-4);
}

/*
 * These three reset what the rule above sets, and they are stated AFTER it on
 * purpose.
 *
 * `.lshostel-appform__official .lshostel-h3` and
 * `.lshostel-appform__signature > .lshostel-h3` are both (0,2,0) — equal
 * specificity — so whichever comes last wins. The signature reset used to come
 * first, which meant the officer's card, being the one nested INSIDE the
 * official panel, took the 16px and the other two did not: three cards built by
 * one generator, 225px, 225px and 241px. Source order settles it, and nothing
 * here escalates a selector to win an argument.
 */
.lshostel-appform > .lshostel-h3:first-child,
.lshostel-appform__official > .lshostel-h3:first-child,
.lshostel-appform__signature > .lshostel-h3 {
	margin-top: 0;
}

/*
 * The signature block's own rhythm, stated rather than inherited.
 */
.lshostel-appform__signature > p,
.lshostel-appform__official > p {
	margin: 0 0 var(--hs-space-3);
}

.lshostel-appform__signature .lshostel-field:last-of-type {
	margin-bottom: var(--hs-space-3);
}

/*
 * THE CONTROLS, AND WHY THEY NEEDED A RULE AT ALL.
 *
 * `.lshostel-field input` set width and nothing else, which is correct inside
 * wp-admin: `forms.css` gives every control its height, padding, border and
 * radius there. The portal deliberately does NOT load wp-admin's stylesheets —
 * they would drag WordPress's visual identity onto it — and nothing replaced
 * them, so on `hostel.toplifelodge.co.tz` every control fell back to the
 * browser default: 21px tall, 1px of padding, square corners, inconsistent
 * between input, select and file.
 *
 * Scoped to the admission form rather than to `.lspms-manage`, because the same
 * gap affects every form on all three portals and repainting them is not this
 * correction's business. It is reported instead.
 */
.lshostel-appform .lshostel-field input[type="text"],
.lshostel-appform .lshostel-field input[type="tel"],
.lshostel-appform .lshostel-field input[type="email"],
.lshostel-appform .lshostel-field input[type="number"],
.lshostel-appform .lshostel-field input[type="date"],
.lshostel-appform .lshostel-field select,
.lshostel-appform .lshostel-field textarea,
.lshostel-appform__signature .lshostel-field input[type="text"] {
	background: var(--hs-neutral-0);
	border: 1px solid var(--hs-neutral-300);
	border-radius: var(--hs-radius);
	box-sizing: border-box;
	color: inherit;
	font: inherit;
	min-height: 40px;
	padding: var(--hs-space-2) var(--hs-space-3);
}

.lshostel-appform .lshostel-field textarea {
	min-height: 80px;
	padding: var(--hs-space-3);
}

/*
 * A file input is a button the browser draws and cannot be given a height the
 * way a text field can. It gets the spacing and is left otherwise alone.
 */
.lshostel-appform .lshostel-field input[type="file"],
.lshostel-appform__signature .lshostel-field input[type="file"] {
	font: inherit;
	padding: var(--hs-space-2) 0;
}

/*
 * A control the thumb has to hit. 35px is wp-admin's button height and it is
 * below the 40px a touch target wants; scoped here so the rest of the hostel
 * keeps the height it already has.
 */
.lshostel-appform .button,
.lshostel-appform__actions .button,
.lshostel-appform__official .button,
.lshostel-appform__signature .button {
	line-height: 1.5;
	min-height: 40px;
}

/*
 * The label sits with its field. `gap: 4px` on `.lshostel-field` is the whole
 * distance, and 6px reads as belonging without crowding.
 */
.lshostel-appform .lshostel-field,
.lshostel-appform__signature .lshostel-field {
	gap: 6px;
}

/*
 * The grid gains a row gap so rows are separated by the grid rather than by
 * each field's bottom margin, which is what made a row containing one tall
 * control sit unevenly against its neighbours.
 */
.lshostel-appform .lshostel-formgrid {
	gap: var(--hs-space-3) var(--hs-space-4);
}

.lshostel-appform .lshostel-formgrid > .lshostel-field {
	margin-bottom: 0;
}

/*
 * Save the form follows the fields it saves.
 */
.lshostel-appform__form > p:last-child,
.lshostel-appform__form > .lshostel-appform__actions {
	margin: var(--hs-space-4) 0 0;
}

/*
 * Section rhythm on this page only. `.lshostel-section` is 32px throughout the
 * hostel and stays 32px everywhere else.
 */
.lshostel-appform,
.lshostel-appform__official {
	margin-bottom: var(--hs-space-5);
}

@media (max-width: 782px) {
	.lshostel-appform__official {
		padding: var(--hs-space-3);
	}
}

/* ==========================================================================
   14. OCCUPANCY BAR
   ========================================================================== */

.lshostel-bar {
	display: flex;
	height: 26px;
	border-radius: var(--hs-radius);
	overflow: hidden;
	background: var(--hs-neutral-100);
	margin-bottom: var(--hs-space-3);
}

.lshostel-bar__part { display: block; height: 100%; }

/*
 * Each segment carries a texture as well as a colour: solid, diagonal hatch,
 * dense hatch. In greyscale, in a printout, or to a reader who cannot separate
 * the hues, the three are still three different things. The accessible reading
 * is the table below it, which is always present and never behind a toggle.
 */
.lshostel-bar__part--occupied {
	background: var(--hs-primary-500);
}

.lshostel-bar__part--available {
	background: var(--hs-good-500);
	background-image: repeating-linear-gradient(
		45deg,
		rgba(255, 255, 255, 0.28) 0,
		rgba(255, 255, 255, 0.28) 3px,
		transparent 3px,
		transparent 7px
	);
}

/*
 * Reserved: amber, and with its own texture direction. Every segment here is
 * distinguishable without colour, because roughly one man in twelve cannot
 * separate the amber from the green and a bed board that speaks only in hue
 * tells him nothing.
 */
.lshostel-bar__part--reserved {
	background: var(--hs-warning-500);
	background-image: repeating-linear-gradient(
		90deg,
		rgba(255, 255, 255, 0.35) 0,
		rgba(255, 255, 255, 0.35) 2px,
		transparent 2px,
		transparent 6px
	);
}

.lshostel-bar__part--maintenance {
	background: var(--hs-neutral-400);
	background-image: repeating-linear-gradient(
		-45deg,
		rgba(255, 255, 255, 0.42) 0,
		rgba(255, 255, 255, 0.42) 2px,
		transparent 2px,
		transparent 5px
	);
}

/* ==========================================================================
   15. NOTICES AND ANNOUNCEMENTS
   ========================================================================== */

.lshostel-notices {
	margin: 0;
	padding: 0;
	list-style: none;
	display: grid;
	gap: var(--hs-space-3);
}

.lshostel-notice {
	margin: 0;
	padding: var(--hs-space-4);
	background: var(--hs-neutral-0);
	border: var(--hs-border);
	border-radius: var(--hs-radius-lg);
}

.lshostel-notice__title {
	margin: var(--hs-space-2) 0 var(--hs-space-1);
	padding: 0;
	font-size: 0.98rem;
	font-weight: 650;
	color: var(--hs-neutral-800);
}

.lshostel-notice__body {
	margin: 0;
	font-size: 0.88rem;
	line-height: 1.55;
	max-width: 74ch;
	color: var(--hs-neutral-650);
}

/* ==========================================================================
   16. GATEWAY STATE
   ========================================================================== */

.lshostel-gateway {
	padding: var(--hs-space-4);
	border-radius: var(--hs-radius-lg);
	border: 1px solid transparent;
	border-left-width: 4px;
	margin-bottom: var(--hs-space-5);
}

.lshostel-gateway p { margin: 0; }
.lshostel-gateway p + p { margin-top: var(--hs-space-2); }

.lshostel-gateway__state {
	font-size: 0.72rem;
	font-weight: 750;
	letter-spacing: 0.09em;
}

.lshostel-gateway--good    { background: var(--hs-good-50);    border-color: var(--hs-good-500);    color: var(--hs-good-700); }
.lshostel-gateway--warning { background: var(--hs-warning-50); border-color: var(--hs-warning-500); color: var(--hs-warning-700); }
.lshostel-gateway--urgent  { background: var(--hs-urgent-50);  border-color: var(--hs-urgent-500);  color: var(--hs-urgent-700); }
/*
 * `info` is the gateway that has never been configured, which is the hostel's
 * normal state: it is paid by bank transfer. It reads as a note rather than as
 * a problem, which is the difference this tone exists to draw.
 */
.lshostel-gateway--info    { background: var(--hs-info-50);    border-color: var(--hs-info-500);    color: var(--hs-info-ink, var(--hs-info-700)); }

/* ==========================================================================
   17. REPORTS
   ========================================================================== */

.lshostel-report-list {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
	gap: var(--hs-space-3);
	margin-bottom: var(--hs-space-5);
}

.lshostel-report-card {
	display: block;
	padding: var(--hs-space-4);
	background: var(--hs-neutral-0);
	border: var(--hs-border);
	border-radius: var(--hs-radius-lg);
	text-decoration: none;
	color: inherit;
	min-width: 0;
}

.lshostel-report-card:hover,
.lshostel-report-card:focus {
	border-color: var(--hs-primary-400);
	background: var(--hs-primary-50);
}

.lshostel-report-card:focus-visible {
	outline: 2px solid var(--hs-primary-500);
	outline-offset: 1px;
}

/*
 * The chosen report is marked with a thickened left edge as well as a tint,
 * so "which one am I looking at" does not depend on distinguishing a pale
 * blue panel from a white one.
 */
.lshostel-report-card.is-current {
	border-color: var(--hs-primary-600);
	border-left-width: 4px;
	background: var(--hs-primary-50);
}

.lshostel-report-card h3 {
	margin: 0 0 var(--hs-space-2);
	padding: 0;
	font-size: 0.92rem;
	font-weight: 650;
	color: var(--hs-primary-800);
}

.lshostel-report-card p {
	margin: 0;
	font-size: 0.82rem;
	line-height: 1.5;
	color: var(--hs-neutral-500);
}

.lshostel-ranges ul {
	display: flex;
	flex-wrap: wrap;
	gap: var(--hs-space-2);
	margin: 0 0 var(--hs-space-4);
	padding: 0;
	list-style: none;
}

.lshostel-ranges li { margin: 0; }

.lshostel-ranges a {
	display: inline-block;
	min-height: 34px;
	padding: var(--hs-space-2) var(--hs-space-3);
	border: var(--hs-border);
	border-radius: var(--hs-radius);
	background: var(--hs-neutral-0);
	color: var(--hs-neutral-650);
	text-decoration: none;
	font-size: 0.85rem;
}

.lshostel-ranges a:hover,
.lshostel-ranges a:focus { border-color: var(--hs-primary-400); color: var(--hs-primary-700); }

.lshostel-ranges a:focus-visible {
	outline: 2px solid var(--hs-primary-500);
	outline-offset: 1px;
}

.lshostel-ranges a.is-current {
	background: var(--hs-primary-600);
	border-color: var(--hs-primary-600);
	color: #fff;
	font-weight: 600;
}

.lshostel-report-period {
	margin: 0 0 var(--hs-space-4);
	font-size: 0.85rem;
	color: var(--hs-neutral-500);
}

/* ==========================================================================
   18. FORMS
   ========================================================================== */

.lshostel-form {
	background: var(--hs-neutral-0);
	border: var(--hs-border);
	border-radius: var(--hs-radius-lg);
	padding: var(--hs-space-2) var(--hs-space-4) var(--hs-space-4);
	margin-bottom: var(--hs-space-5);
}

.lshostel-form .form-table th {
	padding-left: 0;
	color: var(--hs-neutral-700);
}

.lshostel-form input[type="text"]:focus-visible,
.lshostel-form input[type="password"]:focus-visible,
.lshostel-form input[type="number"]:focus-visible,
.lshostel-form select:focus-visible,
.lshostel-form textarea:focus-visible {
	outline: 2px solid var(--hs-primary-500);
	outline-offset: 1px;
	border-color: var(--hs-primary-500);
}

/* A form living in a table cell.

   .lshostel-form draws a panel: a background, a border, a radius and padding.
   That is right for a panel and wrong inside a row, where it would put a
   bordered box in every cell of a column and fight the table's own padding.
   The box is undone here rather than weakened at source, so every existing
   form keeps the appearance it has today. */
.lshostel-rowform {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--hs-space-2);
	margin: 0;
	padding: 0;
	background: none;
	border: 0;
	border-radius: 0;
}

.lshostel-rowform__date { width: auto; }
.lshostel-rowform__note { width: 10ch; }

/*
 * A bed name is "1A in room 1, 120,000 a month", which no sensible fixed width
 * fits. Capped rather than sized, so the select takes the room it needs up to
 * the point where it would push the row wider than the column.
 */
.lshostel-rowform__bed { max-width: 22ch; }

@media screen and (max-width: 782px) {
	/* Stacked on a phone: a date field, a note and a button will not sit on one
	   line at 375px, and wrapping them mid-row reads as three unrelated
	   controls rather than one. */
	.lshostel-rowform { flex-direction: column; align-items: stretch; }
	.lshostel-rowform__date,
	.lshostel-rowform__bed,
	.lshostel-rowform__note { width: 100%; max-width: 100%; }
}

/* ==========================================================================
   19. RESPONSIVE
   ========================================================================== */

/*
 * wp-admin folds its own sidebar at 960px, which is where the hostel's
 * two-column grid also stops earning its keep.
 */
@media screen and (max-width: 960px) {
	.lshostel-app {
		grid-template-columns: minmax(0, 1fr);
		grid-template-areas:
			"masthead"
			"nav"
			"main";
		/*
		 * The base rule declares `auto 1fr` because the sidebar spans two rows
		 * there. Here there are THREE stacked areas and nothing spans anything,
		 * so that template would make the sidebar's row flexible and leave the
		 * third row implicit. Each row is simply as tall as what is in it.
		 */
		grid-template-rows: auto auto auto;
		/*
		 * wp-admin gives the narrow layout its own edge spacing, so the
		 * shell's extra right padding is dropped here. The portal's gutter is
		 * set at higher specificity in section 2b and deliberately survives
		 * this: on a phone the content should still not touch the glass.
		 */
		padding-right: 0;
	}

	.lshostel-nav {
		position: static;
		margin-bottom: var(--hs-space-4);
		/* The wrapped row must not inherit the tall column's scrollbox. */
		max-height: none;
		overflow-y: visible;
	}

	/* The sidebar becomes a wrapped row of links rather than a tall column
	   that pushes the content off the first screen. */
	.lshostel-nav ul {
		display: flex;
		flex-wrap: wrap;
		gap: var(--hs-space-1);
		padding: 0 var(--hs-space-3);
	}

	.lshostel-nav a {
		border-left: 0;
		border-radius: var(--hs-radius);
		padding: var(--hs-space-2) var(--hs-space-3);
	}

	.lshostel-nav a.current { border-left: 0; }
}

@media screen and (max-width: 600px) {
	.lshostel-kpis { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
	.lshostel-ops  { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }

	.lshostel-kpi__value { font-size: 1.35rem; }
	.lshostel-op__count  { font-size: 1.45rem; }

	.lshostel-briefing { padding: var(--hs-space-4); }
	.lshostel-briefing__greeting { font-size: 1.15rem; }

	/* An action that fills the row is easier to hit than one that shares it. */
	.lshostel-action { flex: 1 1 100%; justify-content: center; }
}

/* ==========================================================================
   20. PRINT
   ========================================================================== */

/*
 * A report printed from the browser must contain the report and nothing else.
 * The navigation, the masthead, the quick actions and wp-admin's own furniture
 * are all removed, so what comes out of the printer is what was asked for.
 */
@media print {
	#adminmenumain,
	#wpadminbar,
	#wpfooter,
	.lshostel-nav,
	.lshostel-actions,
	.lshostel-crumbs,
	.lshostel-form,
	.notice {
		display: none !important;
	}

	.lshostel-app {
		display: block;
		padding: 0;
		max-width: none;
	}

	#wpcontent,
	#wpbody-content {
		padding-left: 0 !important;
		margin-left: 0 !important;
	}

	.lshostel-masthead {
		border-bottom: 1px solid #000;
		margin-bottom: 12pt;
	}

	.lshostel-kpi,
	.lshostel-table,
	.lshostel-tablewrap {
		border-color: #999;
		box-shadow: none;
	}

	.lshostel-briefing {
		background: none;
		color: #000;
		border: 1px solid #999;
	}

	.lshostel-briefing__greeting,
	.lshostel-briefing__body { color: #000; }
	.lshostel-briefing__lead,
	.lshostel-briefing__stamp { color: #444; }

	/* A table split across a page break loses its heading row otherwise. */
	.lshostel-table thead { display: table-header-group; }
	.lshostel-table tr { page-break-inside: avoid; }

	.lshostel-tablewrap { overflow: visible; }
}

/* ==========================================================================
   21. REDUCED MOTION AND FORCED COLOURS
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
	.lshostel-app * {
		animation-duration: 0.01ms !important;
		transition-duration: 0.01ms !important;
	}
}

/*
 * In Windows high-contrast mode every background is replaced, so the alert
 * severities would become indistinguishable. The left rule and the level word
 * are what carry the meaning there, and both survive.
 */
@media (forced-colors: active) {
	.lshostel-alert,
	.lshostel-gateway,
	.lshostel-kpi,
	.lshostel-op,
	.lshostel-empty-state {
		border: 1px solid CanvasText;
	}

	.lshostel-alert { border-left-width: 4px; }

	.lshostel-bar__part { forced-color-adjust: none; }
}

/* ==========================================================================
   22. ROOMS AND BEDS
   ========================================================================== */

.lshostel-rooms {
	display: grid;
	/*
	 * 300px is the width at which room 07's fourteen bed chips still form a
	 * readable block rather than a single column. Narrower and the biggest room
	 * becomes a list; wider and a 1280px screen fits only three rooms.
	 *
	 * The floor is capped at the container's own width, so the rule holds at
	 * every width rather than only at the ones somebody remembered to test. It
	 * measures clean today; the clamp is what keeps it clean when the layout
	 * around it changes.
	 */
	grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr));
	gap: var(--hs-space-4);
}

.lshostel-room {
	background: var(--hs-neutral-0);
	border: var(--hs-border);
	border-radius: var(--hs-radius-lg);
	padding: var(--hs-space-4);
	box-shadow: var(--hs-shadow-sm);
	min-width: 0;
}

.lshostel-room__head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--hs-space-2);
	padding-bottom: var(--hs-space-3);
	border-bottom: var(--hs-border);
	margin-bottom: var(--hs-space-3);
}

.lshostel-room__code {
	margin: 0;
	padding: 0;
	font-size: 1.05rem;
	font-weight: 650;
	color: var(--hs-primary-800);
}

.lshostel-room__beds {
	margin: 0;
	font-size: 0.8rem;
	color: var(--hs-neutral-500);
	white-space: nowrap;
}

.lshostel-room__stats {
	display: grid;
	grid-template-columns: repeat(4, minmax(0, 1fr));
	gap: var(--hs-space-2);
	margin: 0 0 var(--hs-space-3);
}

.lshostel-room__stat { min-width: 0; }

.lshostel-room__stat dt {
	margin: 0 0 2px;
	font-size: 0.62rem;
	font-weight: 700;
	letter-spacing: 0.05em;
	text-transform: uppercase;
	color: var(--hs-neutral-400);
	/* Four labels across a 300px card: "Out of service" has to wrap rather
	   than push the fourth column off the edge. */
	overflow-wrap: break-word;
}

.lshostel-room__stat dd {
	margin: 0;
	font-size: 1.1rem;
	font-weight: 650;
	color: var(--hs-neutral-900);
	font-variant-numeric: tabular-nums;
}

.lshostel-room__rate {
	margin: 0 0 var(--hs-space-1);
	font-size: 0.95rem;
	font-weight: 600;
	color: var(--hs-neutral-800);
}

.lshostel-room__rate span {
	font-size: 0.78rem;
	font-weight: 400;
	color: var(--hs-neutral-500);
}

.lshostel-room__potential {
	margin: 0 0 var(--hs-space-3);
	font-size: 0.78rem;
	color: var(--hs-neutral-500);
}

.lshostel-room__actions {
	margin: var(--hs-space-3) 0 0;
	font-size: 0.85rem;
}

/* --------------------------------------------------------------- bed chips

   A chip carries its state as a WORD as well as a colour and a left rule.
   This is the screen a warden reads at a glance from across a desk to answer
   "what have I got free", and it is exactly where colour-only status fails:
   the consequence is a student sent to a bed that is already taken.
*/
.lshostel-bedgrid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(78px, 1fr));
	gap: var(--hs-space-1);
	margin: 0;
	padding: 0;
	list-style: none;
}

.lshostel-bedchip {
	display: flex;
	flex-direction: column;
	gap: 1px;
	margin: 0;
	padding: var(--hs-space-1) var(--hs-space-2);
	border: 1px solid var(--hs-neutral-200);
	border-left-width: 3px;
	border-radius: var(--hs-radius);
	background: var(--hs-neutral-25);
	min-width: 0;
}

.lshostel-bedchip__code {
	font-size: 0.8rem;
	font-weight: 700;
	color: var(--hs-neutral-800);
	font-variant-numeric: tabular-nums;
}

.lshostel-bedchip__state {
	font-size: 0.6rem;
	font-weight: 600;
	letter-spacing: 0.02em;
	text-transform: uppercase;
	/* "Out of service" is the longest and must wrap inside the chip rather
	   than widen it and break the grid. */
	overflow-wrap: break-word;
	hyphens: auto;
}

.lshostel-bedchip--available {
	border-left-color: var(--hs-good-500);
	background: var(--hs-good-50);
}
.lshostel-bedchip--available .lshostel-bedchip__state { color: var(--hs-good-700); }

.lshostel-bedchip--occupied {
	border-left-color: var(--hs-primary-500);
	background: var(--hs-primary-50);
}
.lshostel-bedchip--occupied .lshostel-bedchip__state { color: var(--hs-primary-700); }

.lshostel-bedchip--reserved {
	border-left-color: var(--hs-warning-500);
	background: var(--hs-warning-50);
}
.lshostel-bedchip--reserved .lshostel-bedchip__state { color: var(--hs-warning-700); }

.lshostel-bedchip--broken {
	border-left-color: var(--hs-urgent-500);
	background: var(--hs-urgent-50);
}
.lshostel-bedchip--broken .lshostel-bedchip__state { color: var(--hs-urgent-700); }

@media screen and (max-width: 600px) {
	.lshostel-rooms { grid-template-columns: minmax(0, 1fr); }

	/*
	 * Two stats per row rather than four. Four columns of 0.62rem uppercase
	 * inside a 343px card is unreadable, and "Out of service" wraps to three
	 * lines.
	 */
	.lshostel-room__stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (forced-colors: active) {
	.lshostel-bedchip { border: 1px solid CanvasText; border-left-width: 3px; }
	.lshostel-room { border: 1px solid CanvasText; }
}

/* ---------------------------------------------------------------- wizard
 *
 * Seven steps across the top and one decision underneath. Everything here is
 * sized for a warden standing at a desk with somebody waiting: the choices are
 * large targets, the blocked ones stay visible with their reason attached
 * rather than disappearing, and the figure on the rate step is big enough to
 * read from arm's length before it is agreed to.
 * ---------------------------------------------------------------------- */

.lshostel-wizard__steps {
	display: flex;
	flex-wrap: wrap;
	gap: var(--hs-space-1);
	margin: 0 0 var(--hs-space-5);
	padding: 0;
	list-style: none;
	counter-reset: none;
}

.lshostel-wizard__stepitem {
	flex: 1 1 auto;
	min-width: 0;
}

.lshostel-wizard__stepitem > a,
.lshostel-wizard__stepitem > span {
	display: block;
	padding: var(--hs-space-2) var(--hs-space-3);
	border-bottom: 3px solid var(--hs-neutral-200);
	font-size: 0.8rem;
	line-height: 1.3;
	text-decoration: none;
	color: var(--hs-neutral-600);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.lshostel-wizard__stepitem.is-done > a { color: var(--hs-primary-700); border-bottom-color: var(--hs-primary-300); }

.lshostel-wizard__stepitem.is-current > a,
.lshostel-wizard__stepitem.is-current > span {
	color: var(--hs-primary-700);
	border-bottom-color: var(--hs-primary-600);
	font-weight: 600;
}

/*
 * A step that cannot be reached yet is dimmed but still readable, because it
 * tells the warden what is coming. It is never a link: a stepper that offers a
 * shortcut the wizard refuses is a stepper that lies.
 */
.lshostel-wizard__stepitem.is-locked > span { color: var(--hs-neutral-400); }

.lshostel-wizard__step { max-width: 62rem; }

.lshostel-picklist {
	display: grid;
	gap: var(--hs-space-2);
	margin: 0 0 var(--hs-space-5);
	padding: 0;
	list-style: none;
}

.lshostel-picklist--grid { grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr)); }

.lshostel-picklist__item > a,
.lshostel-picklist__item.is-blocked {
	display: flex;
	flex-direction: column;
	gap: var(--hs-space-1);
	padding: var(--hs-space-3) var(--hs-space-4);
	border: var(--hs-border);
	border-left: 3px solid var(--hs-primary-500);
	border-radius: var(--hs-radius);
	background: var(--hs-neutral-0);
	text-decoration: none;
	color: var(--hs-neutral-800);
	box-shadow: var(--hs-shadow-sm);
}

.lshostel-picklist__item > a:hover,
.lshostel-picklist__item > a:focus {
	border-left-color: var(--hs-primary-700);
	background: var(--hs-primary-50);
	box-shadow: var(--hs-shadow);
}

/*
 * Blocked choices are shown, not hidden. A bed board that quietly omits 3B
 * leaves the warden looking for it; one that shows it greyed with "Occupied"
 * beside it has answered the question.
 */
.lshostel-picklist__item.is-blocked {
	border-left-color: var(--hs-neutral-300);
	background: var(--hs-neutral-50);
	color: var(--hs-neutral-500);
}

.lshostel-picklist__name { font-weight: 600; font-size: 1rem; }
.lshostel-picklist__why  { font-size: 0.8rem; color: var(--hs-neutral-600); }
.lshostel-picklist__item.is-blocked .lshostel-picklist__why { color: var(--hs-neutral-500); }

.lshostel-wizard__figure {
	display: flex;
	flex-direction: column;
	gap: var(--hs-space-1);
	padding: var(--hs-space-5);
	margin: 0 0 var(--hs-space-4);
	border: var(--hs-border);
	border-radius: var(--hs-radius-lg);
	background: var(--hs-primary-50);
}

.lshostel-wizard__amount {
	font-size: 2rem;
	font-weight: 700;
	line-height: 1.1;
	color: var(--hs-primary-800);
}

.lshostel-wizard__amountnote { font-size: 0.85rem; color: var(--hs-neutral-600); }

.lshostel-wizard__facts {
	display: grid;
	grid-template-columns: minmax(9rem, 14rem) minmax(0, 1fr);
	gap: var(--hs-space-2) var(--hs-space-4);
	margin: 0 0 var(--hs-space-5);
	padding: var(--hs-space-4);
	border: var(--hs-border);
	border-radius: var(--hs-radius-lg);
	background: var(--hs-neutral-0);
}

.lshostel-wizard__facts dt { font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--hs-neutral-600); }
.lshostel-wizard__facts dd { margin: 0; font-weight: 600; }

.lshostel-wizard__months {
	display: flex;
	flex-wrap: wrap;
	gap: var(--hs-space-2);
	margin: 0 0 var(--hs-space-4);
	padding: 0;
	border: 0;
}

.lshostel-wizard__months legend {
	padding: 0 0 var(--hs-space-2);
	font-size: 0.8rem;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--hs-neutral-600);
}

.lshostel-wizard__month {
	display: flex;
	align-items: center;
	gap: var(--hs-space-2);
	padding: var(--hs-space-2) var(--hs-space-3);
	border: var(--hs-border);
	border-radius: var(--hs-radius);
	background: var(--hs-neutral-0);
	cursor: pointer;
}

.lshostel-wizard__month:hover { background: var(--hs-primary-50); }

@media screen and (max-width: 782px) {
	/*
	 * The stepper stacks rather than scrolling sideways. Seven labels do not
	 * fit across a phone, and a horizontally scrolling progress indicator hides
	 * the very steps it exists to show.
	 */
	.lshostel-wizard__steps { flex-direction: column; gap: 0; }

	.lshostel-wizard__stepitem > a,
	.lshostel-wizard__stepitem > span {
		border-bottom: 0;
		border-left: 3px solid var(--hs-neutral-200);
		white-space: normal;
	}

	.lshostel-wizard__stepitem.is-done > a { border-left-color: var(--hs-primary-300); }

	.lshostel-wizard__stepitem.is-current > a,
	.lshostel-wizard__stepitem.is-current > span { border-left-color: var(--hs-primary-600); }

	.lshostel-picklist--grid { grid-template-columns: minmax(0, 1fr); }

	.lshostel-wizard__facts { grid-template-columns: minmax(0, 1fr); gap: var(--hs-space-1) 0; }
	.lshostel-wizard__facts dd { margin-bottom: var(--hs-space-3); }
}

@media (forced-colors: active) {
	.lshostel-picklist__item > a,
	.lshostel-picklist__item.is-blocked { border: 1px solid CanvasText; border-left-width: 3px; }
	.lshostel-wizard__facts,
	.lshostel-wizard__figure { border: 1px solid CanvasText; }
}

/* -------------------------------------------------- dashboard preferences
 *
 * At the foot of the dashboard, closed. Somebody sets this once and then wants
 * it out of the way, so it is a disclosure rather than a panel competing with
 * the figures above it.
 * ---------------------------------------------------------------------- */

.lshostel-dashsettings {
	margin: var(--hs-space-6) 0 0;
	padding: var(--hs-space-4);
	border: var(--hs-border);
	border-radius: var(--hs-radius-lg);
	background: var(--hs-neutral-50);
}

.lshostel-dashsettings > summary {
	cursor: pointer;
	font-weight: 600;
	color: var(--hs-primary-700);
}

.lshostel-dashsettings > summary:focus-visible {
	outline: 2px solid var(--hs-primary-600);
	outline-offset: 2px;
}

.lshostel-dashsettings__list {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
	gap: var(--hs-space-2);
	margin: var(--hs-space-3) 0;
}

.lshostel-dashsettings__item {
	display: flex;
	align-items: center;
	gap: var(--hs-space-2);
	padding: var(--hs-space-2) var(--hs-space-3);
	border: var(--hs-border);
	border-radius: var(--hs-radius);
	background: var(--hs-neutral-0);
	cursor: pointer;
}

.lshostel-dashsettings__item:hover { background: var(--hs-primary-50); }

@media screen and (max-width: 782px) {
	.lshostel-dashsettings__list { grid-template-columns: minmax(0, 1fr); }
}

@media (forced-colors: active) {
	.lshostel-dashsettings,
	.lshostel-dashsettings__item { border: 1px solid CanvasText; }
}

/* An inline form, so a Download PDF button sits on its own line without the
   block margin a full form carries. */
.lshostel-form--inline { display: inline-block; margin: 0 0 var(--hs-space-4); }

/* A settings form drawn as a table. The label column is fixed so the controls
   line up down the page, and the value column takes what is left. */
.lshostel-formtable th {
	width: 16rem;
	vertical-align: top;
	font-weight: 600;
}

.lshostel-formtable td { vertical-align: top; }

.lshostel-formtable input[type="text"],
.lshostel-formtable input[type="email"],
.lshostel-formtable input[type="tel"],
.lshostel-formtable input[type="url"],
.lshostel-formtable input[type="number"],
.lshostel-formtable textarea,
.lshostel-formtable select { max-width: 100%; }

@media screen and (max-width: 782px) {
	/*
	 * Label above control rather than beside it. A 16rem label column inside a
	 * 343px screen leaves 5rem for the input.
	 */
	.lshostel-formtable th,
	.lshostel-formtable td { display: block; width: auto; }
	.lshostel-formtable th { border-bottom: 0; padding-bottom: 0; }
}

/* ------------------------------------------------------- figure cards
 *
 * A row of small totals: charged, paid, discounted, refunded, balance. These
 * were emitted with no rules behind them at all, so the Money screen showed
 * five stacked divs of unstyled text where five cards were intended. Found by
 * pms-test/stylesheet-scope-check.py, which asks whether every class a screen
 * emits has a rule in the stylesheet that screen actually loads.
 * ------------------------------------------------------------------------ */

.lshostel-cards {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
	gap: var(--hs-space-3);
	margin: 0 0 var(--hs-space-5);
}

.lshostel-card {
	display: flex;
	flex-direction: column;
	gap: var(--hs-space-1);
	padding: var(--hs-space-3) var(--hs-space-4);
	border: var(--hs-border);
	border-left: 3px solid var(--hs-primary-500);
	border-radius: var(--hs-radius);
	background: var(--hs-neutral-0);
	box-shadow: var(--hs-shadow-sm);
}

.lshostel-card-label {
	font-size: 0.75rem;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--hs-neutral-600);
}

.lshostel-card-value {
	font-size: 1.15rem;
	font-weight: 700;
	color: var(--hs-neutral-800);
	/* Figures line up down the column, so two amounts can be compared at a
	   glance rather than read digit by digit. */
	font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------- screen receipt
 *
 * The on-screen version of what the PDF prints. Bounded rather than full
 * width, because a receipt read across 1920 pixels is a receipt nobody can
 * check against the paper one.
 * ------------------------------------------------------------------------ */

.lshostel-receipt {
	max-width: 44rem;
	padding: var(--hs-space-5);
	border: var(--hs-border);
	border-radius: var(--hs-radius-lg);
	background: var(--hs-neutral-0);
	box-shadow: var(--hs-shadow-sm);
}

.lshostel-receipt h2 { margin: 0 0 var(--hs-space-1); font-size: 1.25rem; }
.lshostel-receipt > p { margin: 0 0 var(--hs-space-4); color: var(--hs-neutral-600); }

@media (forced-colors: active) {
	.lshostel-card,
	.lshostel-receipt { border: 1px solid CanvasText; }
	.lshostel-card { border-left-width: 3px; }
}

/* ------------------------------------------------------- recommended method
 *
 * The method, its standing and its readiness on one line and visibly three
 * things. A warden who reads "Online Payment, recommended" and then hands the
 * resident nothing to pay into has been told two facts at once and only one of
 * them was true.
 * ---------------------------------------------------------------------- */

.lshostel-method {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--hs-space-2);
	margin: 0 0 var(--hs-space-3);
}

.lshostel-method__name {
	font-size: 1.15rem;
	font-weight: 700;
	color: var(--hs-neutral-800);
}

/* The hostel's own sentence about paying, set apart from the surrounding
   explanation because it is the part a warden reads out loud. */
.lshostel-instructions {
	padding: var(--hs-space-3) var(--hs-space-4);
	border-left: 3px solid var(--hs-primary-500);
	border-radius: var(--hs-radius);
	background: var(--hs-primary-50);
	font-weight: 600;
	color: var(--hs-neutral-800);
}

@media (forced-colors: active) {
	.lshostel-instructions { border-left: 3px solid CanvasText; }
}

/* ------------------------------------------------------------- masthead now
 *
 * The date, the hostel's clock and whose clock it is. The timezone is named
 * rather than assumed: a hostel in Dar es Salaam may be administered from
 * anywhere, and a bare time is whichever clock the reader happens to guess.
 * ---------------------------------------------------------------------- */

.lshostel-masthead {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-start;
	justify-content: space-between;
	gap: var(--hs-space-4);
}

.lshostel-masthead__identity { min-width: 0; }

.lshostel-masthead__now {
	display: flex;
	flex-direction: column;
	align-items: flex-end;
	gap: 2px;
	margin-left: auto;
	text-align: right;
}

.lshostel-masthead__date {
	font-size: 0.85rem;
	color: var(--hs-neutral-600);
}

.lshostel-masthead__time {
	font-size: 1.35rem;
	font-weight: 700;
	line-height: 1;
	color: var(--hs-primary-700);
	/* Digits keep their column as the minute changes, so the clock does not
	   shuffle the text beside it once a minute. */
	font-variant-numeric: tabular-nums;
}

.lshostel-masthead__zone {
	font-size: 0.72rem;
	letter-spacing: 0.03em;
	color: var(--hs-neutral-500);
}

.lshostel-masthead__user {
	display: flex;
	align-items: center;
	gap: var(--hs-space-3);
}

.lshostel-masthead__alerts {
	position: relative;
	display: inline-flex;
	align-items: center;
	gap: var(--hs-space-1);
	padding: var(--hs-space-1) var(--hs-space-2);
	border-radius: var(--hs-radius);
	color: var(--hs-neutral-600);
	text-decoration: none;
}

.lshostel-masthead__alerts:hover,
.lshostel-masthead__alerts:focus-visible { background: var(--hs-neutral-50); color: var(--hs-primary-700); }

.lshostel-masthead__count {
	min-width: 1.5rem;
	padding: 0 var(--hs-space-1);
	border-radius: 999px;
	background: var(--hs-neutral-200);
	color: var(--hs-neutral-700);
	font-size: 0.75rem;
	font-weight: 700;
	text-align: center;
}

/* Amber only when there is actually something in it. A permanently coloured
   badge is furniture; one that changes is a signal. */
.lshostel-masthead__count.is-active { background: var(--hs-warning-500); color: #fff; }

.lshostel-masthead__who {
	font-size: 0.85rem;
	font-weight: 600;
	color: var(--hs-neutral-700);
}

@media screen and (max-width: 782px) {
	.lshostel-masthead { flex-direction: column; align-items: stretch; }
	.lshostel-masthead__now { align-items: flex-start; margin-left: 0; text-align: left; }
	.lshostel-masthead__user { justify-content: flex-start; }
}

@media (forced-colors: active) {
	.lshostel-masthead__count { border: 1px solid CanvasText; }
}

/* ------------------------------------------------------ occupancy by room
 *
 * A hostel average hides the thing a warden needs. "40% occupied" across
 * fourteen rooms is consistent with four full rooms and ten empty ones, and
 * with every room being a little under half. Those are different hostels to
 * run, so each room gets its own line.
 *
 * Every room is listed, including the empty ones: a chart showing only rooms
 * with residents in them would be a chart of where people are, under a heading
 * about where beds are.
 * ---------------------------------------------------------------------- */


.lshostel-roombars {
	display: grid;
	gap: var(--hs-space-2);
	margin: 0;
	padding: 0;
	list-style: none;
}

.lshostel-roombar {
	display: grid;
	grid-template-columns: 3.5rem minmax(0, 1fr) 4.5rem;
	align-items: center;
	gap: var(--hs-space-3);
}

.lshostel-roombar__code {
	font-weight: 700;
	font-size: 0.9rem;
	color: var(--hs-neutral-700);
}

.lshostel-roombar__track {
	position: relative;
	display: block;
	height: 14px;
	border-radius: 999px;
	background: var(--hs-neutral-100);
	overflow: hidden;
}

.lshostel-roombar__fill {
	display: block;
	height: 100%;
	border-radius: 999px;
	background: var(--hs-primary-500);
	/* A room with nobody in it draws nothing rather than a sliver, so an empty
	   room is visibly empty instead of looking like a rounding error. */
	min-width: 0;
}

.lshostel-roombar__figure {
	font-size: 0.85rem;
	color: var(--hs-neutral-600);
	font-variant-numeric: tabular-nums;
	text-align: right;
}

@media screen and (max-width: 600px) {
	.lshostel-roombar { grid-template-columns: 3rem minmax(0, 1fr) 4rem; gap: var(--hs-space-2); }
}

@media (forced-colors: active) {
	.lshostel-roombar__track { border: 1px solid CanvasText; }
	.lshostel-roombar__fill { background: Highlight; }
}

/* A sentence under a chart that qualifies what the chart does not show —
   money that belongs to no room, a range that excludes something. It is not a
   caption: the chart is complete without it and wrong without it being said. */
/* ------------------------------------------------------ room intelligence
 *
 * A card per room rather than eleven columns against fourteen rows. The
 * questions asked here are about ONE room at a time — how full is 07, what does
 * 03 owe — and a table wide enough to answer them has to scroll sideways on
 * anything smaller than a laptop.
 */
.lshostel-roomcards {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr));
	gap: var(--hs-space-3);
	margin: 0 0 var(--hs-space-4);
}

.lshostel-roomcard {
	border: 1px solid var(--hs-neutral-200);
	border-radius: var(--hs-radius-lg);
	background: var(--hs-neutral-0);
	padding: var(--hs-space-4);
	/* Stops a long figure forcing the column wider than its grid track. */
	min-width: 0;
}

.lshostel-roomcard__code {
	margin: 0 0 var(--hs-space-3);
	font-size: 0.95rem;
	font-weight: 700;
	color: var(--hs-neutral-800);
}

.lshostel-roomcard__beds,
.lshostel-roomcard__money {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(76px, 1fr));
	gap: var(--hs-space-2);
	margin: 0 0 var(--hs-space-3);
}

.lshostel-roomcard__stat { min-width: 0; }

.lshostel-roomcard__stat dt {
	font-size: 0.7rem;
	font-weight: 600;
	letter-spacing: 0.03em;
	text-transform: uppercase;
	color: var(--hs-neutral-500);
}

.lshostel-roomcard__stat dd {
	margin: 2px 0 0;
	font-size: 1rem;
	font-weight: 700;
	color: var(--hs-neutral-800);
	font-variant-numeric: tabular-nums;
	/* A money figure wraps rather than widening the card. */
	overflow-wrap: anywhere;
}

.lshostel-roomcard__label {
	margin: 0 0 4px;
	font-size: 0.7rem;
	font-weight: 600;
	letter-spacing: 0.03em;
	text-transform: uppercase;
	color: var(--hs-neutral-500);
}

.lshostel-roomcard__meter {
	height: 8px;
	border-radius: 999px;
	background: var(--hs-neutral-100);
	overflow: hidden;
}

.lshostel-roomcard__fill {
	display: block;
	height: 100%;
	border-radius: 999px;
	background: var(--hs-primary-500);
}

.lshostel-roomcard__reading {
	margin: 4px 0 var(--hs-space-3);
	font-size: 0.78rem;
	line-height: 1.5;
	color: var(--hs-neutral-600);
}

/*
 * The sentence that replaces a meter when there is nothing to measure. It is
 * deliberately NOT a bar at nought: a bar at nought for a room with no lettable
 * bed looks exactly like a bar at nought for a room that is empty, and those
 * are different facts.
 */
.lshostel-roomcard__none {
	margin: 0 0 var(--hs-space-3);
	font-size: 0.78rem;
	line-height: 1.5;
	color: var(--hs-neutral-500);
	font-style: italic;
}

@media (forced-colors: active) {
	.lshostel-roomcard__meter { border: 1px solid CanvasText; }
	.lshostel-roomcard__fill { background: Highlight; }
}

/* --------------------------------------------------------- a custom range
 *
 * Two dates and a button, on one line where there is room and stacked where
 * there is not. A hostel bills by the month and its terms do not begin on the
 * first, so the named periods above cannot express the range a warden most
 * often wants.
 */
.lshostel-rangeform {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--hs-space-2);
	margin: 0 0 var(--hs-space-4);
}

.lshostel-rangeform label {
	font-size: 0.85rem;
	font-weight: 600;
	color: var(--hs-neutral-600);
}

.lshostel-rangeform input[type="date"] {
	min-height: var(--tl-touch, 44px);
	padding: 0 var(--hs-space-2);
	border: 1px solid var(--hs-neutral-300);
	border-radius: var(--hs-radius);
	background: var(--hs-neutral-0);
	color: var(--hs-neutral-800);
	font-size: 0.9rem;
	/* So a narrow phone stacks the fields instead of pushing the page sideways. */
	min-width: 0;
	max-width: 100%;
}

.lshostel-note {
	margin: var(--hs-space-3) 0 0;
	max-width: 74ch;
	font-size: 0.85rem;
	line-height: 1.55;
	color: var(--hs-neutral-600);
}

/* --------------------------------------------------------- bed actions
 *
 * What can be done to one bed, laid out as a row of controls rather than a
 * menu. The list depends on the bed's state, because a row of buttons that
 * mostly produce refusals teaches a warden to ignore the row.
 * ---------------------------------------------------------------------- */

.lshostel-bedactions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--hs-space-2);
	margin: 0 0 var(--hs-space-5);
}

.lshostel-bedactions .lshostel-form--inline { margin: 0; }

.lshostel-room__more {
	margin: var(--hs-space-3) 0 0;
	padding-top: var(--hs-space-3);
	border-top: var(--hs-border);
	font-size: 0.9rem;
}

@media screen and (max-width: 600px) {
	/* Full-width controls on a phone: a row of small buttons at 343px puts two
	   words on each and wraps the third onto its own line anyway. */
	.lshostel-bedactions { flex-direction: column; align-items: stretch; }
	.lshostel-bedactions .button { width: 100%; text-align: center; }
	.lshostel-bedactions .lshostel-form--inline { display: block; }
}

/* ------------------------------------------------ the mark on the masthead - */

/*
 * The business's logo, or the two letters it had before there were logos. Sized
 * to the line rather than to the photograph, and `contain` rather than `cover`,
 * because cropping a logo to fill a square is the one thing that stops it being
 * the logo.
 *
 * This lives HERE rather than in pms.css because this is the stylesheet the
 * screen loads. It was written in the other one first, and the class-coverage
 * check said so: a rule in a file the page never fetches is a rule that does
 * nothing at all.
 */
.lshostel-masthead__mark {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 32px;
	height: 32px;
	margin-bottom: 8px;
	padding: 0 6px;
	border-radius: 6px;
	background: rgba(0, 0, 0, 0.06);
	font-weight: 700;
	font-size: 0.8125rem;
	letter-spacing: 0.04em;
}

.lshostel-masthead__mark--logo {
	max-width: 160px;
	padding: 0;
	background: none;
}

.lshostel-masthead__mark img {
	display: block;
	max-width: 100%;
	max-height: 32px;
	object-fit: contain;
}
