/*
 * Primeshots Booking — admin (backend) styles.
 *
 * Modeled directly on Square's own Appointments dashboard: near-black type
 * on white, large page titles, hairline dividers, full-bleed grey day
 * bands, pill "filter chips" that pair a muted label with a bold value,
 * circular icon buttons, and a black pill for the primary action.
 *
 * Everything is scoped to #pb-admin-root so wp-admin's own chrome (and any
 * other plugin's stylesheet) can't bleed in.
 */

#pb-admin-root,
#pb-admin-root *,
#pb-admin-root *::before,
#pb-admin-root *::after {
	box-sizing: border-box;
}

#pb-admin-root {
	/* Neutrals */
	--pb-black: #1a1a1a;
	--pb-text: #1a1a1a;
	--pb-text-muted: #6a6a6a;
	--pb-text-faint: #949494;
	--pb-white: #ffffff;
	--pb-fill: #f5f5f5;
	--pb-fill-hover: #e9e9e9;
	--pb-band: #f2f2f2;
	--pb-border: #e3e3e3;
	--pb-hairline: #ececec;

	/* Brand + status */
	--pb-purple: #7700ed;
	--pb-purple-dark: #6200c4;
	--pb-purple-tint: #f4ecff;
	--pb-blue: #1b56d6;
	--pb-blue-tint: #e6efff;
	--pb-success: #1b8a5a;
	--pb-danger: #b3261e;
	--pb-danger-tint: #fdeceb;
	--pb-danger-border: #f2c2be;
	--pb-warn-bg: #fff8e6;
	--pb-warn-border: #f0dda8;
	--pb-warn-text: #6b5300;

	/* Studio colors (also used by the legend dots inline in JS) */
	--pb-studio-a: #d99413;
	--pb-studio-b: #6f9c2b;
	--pb-studio-c: #2a72c4;

	/* Shape */
	--pb-r-sm: 10px;
	--pb-r: 12px;
	--pb-r-lg: 16px;
	--pb-r-pill: 999px;
	--pb-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
	--pb-shadow-lg: 0 4px 20px rgba(0, 0, 0, 0.08);

	font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
	color: var(--pb-text);
	font-size: 14px;
	line-height: 1.5;
	max-width: 1360px;
	margin: 0;
	padding: 0 0 60px;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

#pb-admin-root h1,
#pb-admin-root h2,
#pb-admin-root h3,
#pb-admin-root h4 {
	margin: 0;
	padding: 0;
	color: var(--pb-text);
	letter-spacing: -0.02em;
	line-height: 1.25;
	font-weight: 700;
}

#pb-admin-root p {
	margin: 0;
}

#pb-admin-root a {
	color: var(--pb-text);
	text-decoration: none;
}

/* Anchors styled as buttons: the id-scoped anchor rule above outranks the
   plain .pb-btn-* class rules, so re-assert their colours at id strength.
   Deliberately NOT matching a bare `a.pb-btn` here — that selector has the
   same specificity as `a.pb-btn-primary` and, coming later, would win on a
   primary button and paint its label the same colour as its background. */
#pb-admin-root a.pb-btn-secondary {
	color: var(--pb-text);
}

#pb-admin-root a.pb-btn-primary,
#pb-admin-root a.pb-btn-primary:hover {
	color: var(--pb-white);
}

#pb-admin-root button {
	font-family: inherit;
	transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
}

/* ---------------- Tab bar ---------------- */

.pb-tabbar {
	display: flex;
	align-items: center;
	gap: 4px;
	margin: 8px 0 22px;
	border-bottom: 1px solid var(--pb-border);
	overflow-x: auto;
}

.pb-tabbar a {
	flex: 0 0 auto;
	padding: 12px 16px 14px;
	font-size: 14px;
	font-weight: 600;
	color: var(--pb-text-muted);
	border-bottom: 2px solid transparent;
	margin-bottom: -1px;
	white-space: nowrap;
	transition: color 0.15s ease, border-color 0.15s ease;
}

.pb-tabbar a:hover {
	color: var(--pb-text);
}

.pb-tabbar a.is-active {
	color: var(--pb-text);
	border-bottom-color: var(--pb-black);
}

/* ---------------- Page + panel titles ---------------- */

#pb-admin-root .pb-page-title {
	font-size: 32px;
	font-weight: 700;
	letter-spacing: -0.03em;
	margin: 0 0 26px;
}

/* A heading with its screen's controls on the other end of the same line.
   The controls cost no vertical space this way — they fill room the
   heading was already taking — which is what let the calendar's toolbar
   drop from two rows to one. */
.pb-page-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	margin-bottom: 26px;
	/*
	 * The filters popover hangs out of the bottom of this row and over the
	 * toolbar and calendar below it, and its own z-index cannot decide
	 * that on its own: the screen's entrance animation leaves a transform
	 * on this element, a transform makes it a stacking context, and a
	 * stacking context confines its children's z-indexes to itself. So
	 * this row is what has to be lifted, exactly as .pb-calendar-toolbar
	 * is for the date picker it holds. Above the toolbar's 2, because
	 * this row sits above the toolbar and its popover crosses it.
	 */
	position: relative;
	z-index: 3;
}

#pb-admin-root .pb-page-head .pb-page-title {
	margin: 0;
	min-width: 0;
}

.pb-page-head-actions {
	display: flex;
	align-items: center;
	gap: 10px;
	flex: 0 0 auto;
}

/* Create, reduced to a plus — see renderCalendar(). Square, not a pill:
   a pill with one glyph in it reads as a button missing its label. */
#pb-admin-root .pb-head-create {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 44px;
	padding: 0;
	border-radius: 14px;
}

#pb-admin-root .pb-head-create svg {
	width: 21px;
	height: 21px;
	display: block;
}

.pb-panel-title {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 16px;
	font-size: 21px;
	font-weight: 700;
	letter-spacing: -0.02em;
	margin-bottom: 6px;
}

.pb-tz {
	font-size: 12.5px;
	font-weight: 400;
	color: var(--pb-text-muted);
	letter-spacing: 0;
	white-space: nowrap;
}

/* ---------------- Cards ---------------- */

.pb-card {
	background: var(--pb-white);
	border: 1px solid var(--pb-border);
	border-radius: var(--pb-r-lg);
	box-shadow: var(--pb-shadow);
	overflow: hidden;
}

.pb-overview-grid {
	display: grid;
	grid-template-columns: minmax(0, 1.85fr) minmax(0, 1fr);
	gap: 22px;
	align-items: start;
}

.pb-loading,
.pb-empty-note {
	padding: 32px 4px;
	color: var(--pb-text-muted);
	font-size: 14px;
}

/* ---------------- Overview: appointment list ---------------- */

/* Full-bleed grey band, like Square's date separators. The negative
   inline margins push it out to the card's edges past the 20px padding
   its container is rendered with. */
.pb-day-heading {
	margin: 22px -20px 4px;
	padding: 11px 20px;
	background: var(--pb-band);
	font-size: 12.5px;
	font-weight: 700;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--pb-text);
}

.pb-appt-row {
	display: flex;
	align-items: flex-start;
	gap: 24px;
	padding: 18px 2px;
	border-bottom: 1px solid var(--pb-hairline);
}

.pb-appt-row:last-child {
	border-bottom: none;
}

.pb-appt-time {
	flex: 0 0 158px;
	font-size: 14px;
	font-weight: 500;
	color: var(--pb-text);
	white-space: nowrap;
}

.pb-appt-info {
	min-width: 0;
}

.pb-appt-title {
	font-size: 14.5px;
	font-weight: 700;
	letter-spacing: -0.005em;
	margin-bottom: 5px;
}

/* Square renders the service line at the same weight/colour as the name
   above it rather than as muted secondary text. */
.pb-appt-sub {
	font-size: 14px;
	font-weight: 700;
	color: var(--pb-text);
}

/* Flags an appointment booked free-online, pay-in-studio (passport photos —
   see PB_Mock_Data::requires_payment()) — same soft blue "heads up, not an
   error" tint the customer-facing manage page uses for its own info panel
   (booking.css's --pb-info-tint/--pb-info-border), so staff and customers
   read the same visual language for "nothing wrong, just noting something". */
.pb-tag-pay-in-studio {
	display: inline-block;
	margin-top: 4px;
	padding: 2px 8px;
	font-size: 11px;
	font-weight: 700;
	color: #2b5a7a;
	background: #eaf3fb;
	border: 1px solid #cbe3f5;
	border-radius: 999px;
}

/* ---------------- Overview: notifications ---------------- */

.pb-notification {
	position: relative;
	padding: 16px 28px 16px 2px;
	border-bottom: 1px solid var(--pb-hairline);
	font-size: 13.5px;
	line-height: 1.6;
}

.pb-notification:last-child {
	border-bottom: none;
}

.pb-notif-dismiss {
	position: absolute;
	top: 14px;
	right: 0;
	width: 22px;
	height: 22px;
	padding: 0;
	border: none;
	background: none;
	border-radius: 50%;
	font-size: 16px;
	line-height: 1;
	color: var(--pb-text-faint);
	cursor: pointer;
	transition: background-color 0.15s ease, color 0.15s ease;
}

.pb-notif-dismiss:hover {
	background: var(--pb-fill-hover);
	color: var(--pb-text);
}

.pb-notif-time {
	margin-top: 8px;
	font-size: 12.5px;
	font-weight: 700;
	color: var(--pb-text);
}

/* Reveals the notifications past the first three (see paintNotifications()
   in admin.js). Deliberately a quiet text link rather than a button with a
   frame: it sits directly under a list of dismissable rows, and anything
   with a border there reads as a fourth notification. */
.pb-notif-more {
	display: block;
	width: 100%;
	margin-top: 12px;
	padding: 8px 2px;
	border: none;
	background: none;
	font-size: 13px;
	font-weight: 700;
	color: var(--pb-purple);
	text-align: left;
	cursor: pointer;
	border-radius: var(--pb-r-sm);
}

.pb-notif-more:hover {
	background: var(--pb-fill);
}

.pb-activity-row {
	padding: 14px 2px;
	border-bottom: 1px solid var(--pb-hairline);
	font-size: 13.5px;
	line-height: 1.6;
}

.pb-activity-row:last-child {
	border-bottom: none;
}

.pb-act-time {
	margin-top: 6px;
	font-size: 12px;
	color: var(--pb-text-muted);
}

/* ---------------- Calendar ---------------- */

.pb-calendar-toolbar {
	display: flex;
	align-items: center;
	gap: 12px;
	margin-bottom: 20px;
	flex-wrap: wrap;
	/*
	 * Both popovers this bar opens — the date picker and the filters panel
	 * — hang out of the bottom of it and over the day strip and grid
	 * below. They each carry a z-index of their own, and it was doing
	 * nothing: the screen's entrance animation leaves a transform on this
	 * element, any transform makes it a stacking context, and a stacking
	 * context confines its children's z-indexes to itself. So the popovers
	 * were competing at the toolbar's own level, which is `auto`, and lost
	 * to every later sibling — they opened *behind* the calendar. Lifting
	 * the whole toolbar one step is what actually decides it, because the
	 * contest was never between the popover and the grid.
	 */
	position: relative;
	z-index: 2;
}

/* Square-style filter chip: muted label, bold value, pill outline. The
   label is drawn with ::before so the JS can keep writing just the value. */
.pb-date-picker-wrap {
	position: relative;
	display: inline-block;
}

.pb-date-range {
	display: inline-flex;
	align-items: center;
	gap: 10px;
	padding: 11px 20px;
	border: 1px solid var(--pb-border);
	border-radius: var(--pb-r-pill);
	background: var(--pb-white);
	font-size: 14.5px;
	font-weight: 700;
	font-family: inherit;
	letter-spacing: -0.01em;
	white-space: nowrap;
	cursor: pointer;
	color: var(--pb-text);
	transition: border-color 0.15s ease, background 0.15s ease;
}

.pb-date-range:hover {
	background: var(--pb-fill);
}

.pb-date-range::before {
	content: "Date";
	font-weight: 400;
	color: var(--pb-text-muted);
}

.pb-date-popover {
	position: absolute;
	top: calc(100% + 8px);
	left: 0;
	z-index: 60;
	width: 296px;
	background: var(--pb-white);
	border: 1px solid var(--pb-border);
	border-radius: var(--pb-r);
	box-shadow: 0 8px 28px rgba(0, 0, 0, 0.14);
	padding: 14px;
	animation: pb-admin-fade-up 0.15s ease both;
}

.pb-date-popover-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	margin-bottom: 10px;
}

.pb-date-popover-head .pb-icon-btn {
	width: 30px;
	height: 30px;
	flex: 0 0 30px;
}

.pb-date-popover-month {
	font-size: 14px;
	font-weight: 700;
}

.pb-date-popover-dow {
	display: grid;
	grid-template-columns: repeat(7, 1fr);
	margin-bottom: 4px;
}

.pb-date-popover-dow span {
	text-align: center;
	font-size: 11px;
	font-weight: 700;
	color: var(--pb-text-muted);
}

.pb-date-popover-grid {
	display: grid;
	grid-template-columns: repeat(7, 1fr);
	gap: 2px;
}

.pb-date-popover-day {
	position: relative;
	aspect-ratio: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	border: none;
	border-radius: var(--pb-r-sm);
	background: transparent;
	color: var(--pb-text);
	font-family: inherit;
	font-size: 13px;
	font-weight: 600;
	cursor: pointer;
}

.pb-date-popover-day:hover {
	background: var(--pb-fill);
}

.pb-date-popover-day.is-outside {
	color: var(--pb-text-faint);
}

.pb-date-popover-day.is-in-week {
	background: var(--pb-purple-tint);
}

.pb-date-popover-day.is-today::after {
	content: "";
	position: absolute;
	bottom: 4px;
	left: 50%;
	width: 4px;
	height: 4px;
	border-radius: 50%;
	background: var(--pb-purple);
	transform: translateX(-50%);
}

.pb-date-popover-day.is-in-week.is-today::after {
	background: var(--pb-purple-dark);
}

/* Compound selector so it beats the generic .pb-btn rules further down. */
.pb-btn.pb-toolbar-create {
	margin-left: auto;
	color: var(--pb-white);
}

.pb-icon-btn {
	width: 42px;
	height: 42px;
	flex: 0 0 42px;
	border-radius: 50%;
	border: none;
	background: var(--pb-fill);
	color: var(--pb-text);
	font-size: 16px;
	line-height: 1;
	cursor: pointer;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: 0;
}

.pb-icon-btn:hover {
	background: var(--pb-fill-hover);
}

.pb-calendar-grid {
	border: 1px solid var(--pb-border);
	border-radius: var(--pb-r-lg);
	overflow: hidden;
	background: var(--pb-white);
	box-shadow: var(--pb-shadow);
}

/* Header row: an empty corner over the hour gutter, then 7 day names.
   The gutter width is shared with .pb-cal-body below so the columns line
   up exactly. */
.pb-cal-head {
	display: grid;
	grid-template-columns: 62px repeat(7, minmax(0, 1fr));
	border-bottom: 1px solid var(--pb-border);
}

.pb-cal-gutter-head {
	border-right: 1px solid var(--pb-hairline);
}

.pb-calendar-day-header {
	padding: 14px 8px;
	text-align: center;
	font-size: 13.5px;
	font-weight: 600;
	color: var(--pb-text-muted);
	border-right: 1px solid var(--pb-hairline);
	white-space: nowrap;
}

.pb-calendar-day-header:last-child {
	border-right: none;
}

.pb-calendar-day-header.is-today {
	color: var(--pb-text);
	font-weight: 700;
}

.pb-cal-body {
	display: grid;
	grid-template-columns: 62px repeat(7, minmax(0, 1fr));
	/* Was a flat 620px, which was about eleven hours at the old 56px row
	   height and is only seven and a half at 80px. Sized off the viewport
	   instead so the taller rows don't cost a day's worth of visible
	   hours: a tall monitor now shows more than 620px ever did, the cap
	   keeps it from outgrowing the full 8am-10pm grid, and the floor stops
	   it collapsing to a sliver on a short window. */
	max-height: clamp(420px, calc(100vh - 300px), 1120px);
	overflow-y: auto;
}

.pb-cal-gutter {
	border-right: 1px solid var(--pb-hairline);
}

.pb-cal-hour-label {
	display: flex;
	justify-content: flex-end;
	padding: 0 8px;
	font-size: 11.5px;
	color: var(--pb-text-muted);
	/* Sit the label just under the hour line it belongs to. Nudging it up
	   instead would clip the very first label against the scroll edge. */
	padding-top: 4px;
	align-items: flex-start;
}

/* Each day is a positioning context; events are absolutely placed inside
   it by start time, over a stack of hour lines. */
.pb-calendar-day-col {
	position: relative;
	border-right: 1px solid var(--pb-hairline);
	min-width: 0;
}

.pb-calendar-day-col:last-child {
	border-right: none;
}

.pb-calendar-day-col.is-today {
	background: #fcfaff;
}

.pb-cal-hour-line {
	border-bottom: 1px solid var(--pb-hairline);
}

.pb-cal-event {
	position: absolute;
	left: 3px;
	right: 3px;
	display: flex;
	flex-direction: column;
	gap: 1px;
	/* 4px top/bottom, not 5: with HOUR_PX at 80 a one-hour block has 77px
	   to fit 73.1px of content, and those two pixels are the difference
	   between the staff line rendering and being sliced in half. See the
	   HOUR_PX comment in admin.js for the whole sum. */
	padding: 4px 7px;
	border-radius: 6px;
	border-left: 3px solid var(--pb-text-muted);
	background: var(--pb-fill);
	font-size: 11.5px;
	line-height: 1.35;
	color: var(--pb-text);
	overflow: hidden;
	cursor: default;
}

/* Sub-hour blocks: drop the least useful lines rather than let overflow
   cut one through the middle. admin.js picks the class from the block's
   real pixel height (CAL_FULL_PX / CAL_COMPACT_PX). */
.pb-cal-event.is-compact .pb-cal-staff {
	display: none;
}

/* Shortest blocks — one line, and it carries the two things worth having:
   when it starts and who it is for. */
.pb-cal-event.is-mini {
	flex-direction: row;
	align-items: baseline;
	gap: 5px;
}

.pb-cal-event.is-mini .pb-cal-what,
.pb-cal-event.is-mini .pb-cal-staff {
	display: none;
}

/* The name takes whatever the time doesn't, and ellipsises rather than
   pushing itself out of the block. */
.pb-cal-event.is-mini .pb-cal-time {
	flex: 0 0 auto;
}

.pb-cal-event.is-mini .pb-cal-who {
	flex: 1 1 auto;
	min-width: 0;
}

.pb-cal-event > span {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.pb-cal-time {
	font-weight: 700;
}

.pb-cal-who {
	font-weight: 600;
}

.pb-cal-what {
	opacity: 0.8;
}

.pb-cal-staff {
	opacity: 0.7;
	font-weight: 500;
}

/* Block color now comes from the inline style admin.js computes per staff
   member (staffColorStyle()) — colors are shared with .pb-legend-dot rather
   than fixed per room, so the calendar and its legend can't drift apart. */

.pb-calendar-legend {
	display: flex;
	align-items: center;
	gap: 22px;
	margin-top: 16px;
	font-size: 12.5px;
	color: var(--pb-text-muted);
	flex-wrap: wrap;
}

.pb-calendar-legend > span {
	display: inline-flex;
	align-items: center;
	gap: 7px;
}

.pb-legend-dot {
	width: 10px;
	height: 10px;
	border-radius: 50%;
	display: inline-block;
}

/* ---------------- Create appointment ---------------- */

.pb-create-grid {
	display: grid;
	grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
	gap: 26px;
	align-items: start;
}

/* ---------------- Create appointment: one section at a time ----------------

   All seven sections used to be open at once, which made this screen about
   eight phone-screens tall before anyone had typed anything, and made it
   read as a form to fill in rather than as a booking to make. Collapsed,
   each row states what it currently holds — so the screen is a summary of
   the booking that expands wherever it is wrong. See renderAll() in
   renderCreate(). */

.pb-step {
	background: var(--pb-white);
	border: 1px solid var(--pb-border);
	border-radius: var(--pb-r-lg);
	box-shadow: var(--pb-shadow);
	margin-bottom: 10px;
	overflow: hidden;
}

.pb-step.is-open {
	box-shadow: var(--pb-shadow-lg);
}

.pb-step-head {
	display: grid;
	grid-template-columns: minmax(0, auto) minmax(0, 1fr) 20px;
	align-items: center;
	gap: 14px;
	width: 100%;
	padding: 16px 18px;
	border: none;
	background: none;
	font-family: inherit;
	text-align: left;
	cursor: pointer;
}

.pb-step-head:hover {
	background: var(--pb-fill);
}

.pb-step-title {
	font-size: 14.5px;
	font-weight: 700;
	letter-spacing: -0.01em;
	color: var(--pb-text);
}

/* The value, not a description of the field. One line, truncated — a long
   note or a five-answer intake summary must not turn a row into a
   paragraph, which is the thing this whole layout is undoing. */
.pb-step-summary {
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	text-align: right;
	font-size: 13.5px;
	color: var(--pb-text-muted);
}

/* Filled in: the value stops being a prompt and starts being an answer. */
.pb-step.is-done .pb-step-summary {
	color: var(--pb-text);
	font-weight: 600;
}

.pb-step-chevron {
	display: inline-flex;
	justify-content: flex-end;
	color: var(--pb-text-faint);
	transition: transform 0.16s ease;
}

.pb-step-chevron svg {
	width: 17px;
	height: 17px;
	display: block;
}

.pb-step.is-open .pb-step-chevron {
	transform: rotate(90deg);
}

/* The section's own card chrome is dropped — it is inside one now. */
#pb-admin-root .pb-step-body {
	margin: 0;
	padding: 4px 18px 20px;
	border: none;
	border-radius: 0;
	box-shadow: none;
	background: none;
}

/* An open row hides its own summary: the answer is right there below it,
   and repeating it in the header just reads as a duplicate. */
.pb-step.is-open .pb-step-summary {
	visibility: hidden;
}

.pb-form-section {
	margin-bottom: 22px;
	padding: 22px 24px 24px;
}

#pb-admin-root .pb-form-section > h3,
#pb-admin-root .pb-form-section > h2,
#pb-admin-root .pb-form-section > .pb-team-add-head > h3 {
	font-size: 21px;
	font-weight: 700;
	letter-spacing: -0.02em;
	margin-bottom: 14px;
}

.pb-field-row {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 14px;
}

/* Square's signature field: one bordered box holding a small bold label
   above the value. */
.pb-field {
	border: 1px solid var(--pb-border);
	border-radius: var(--pb-r);
	background: var(--pb-white);
	padding: 11px 16px 12px;
	margin-bottom: 14px;
	transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.pb-field:focus-within {
	border-color: var(--pb-purple);
	box-shadow: 0 0 0 3px var(--pb-purple-tint);
}

.pb-field label {
	display: block;
	font-size: 12.5px;
	font-weight: 700;
	color: var(--pb-text);
	margin: 0 0 3px;
}

.pb-field input,
.pb-field select,
.pb-field textarea {
	width: 100%;
	border: none;
	outline: none;
	background: transparent;
	padding: 0;
	margin: 0;
	min-height: 0;
	color: var(--pb-text);
	font-family: inherit;
	font-size: 15px;
	line-height: 1.45;
	box-shadow: none;
	-webkit-appearance: none;
	appearance: none;
}

.pb-field select {
	cursor: pointer;
	background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9' fill='none'%3E%3Cpath d='M1 1l6 6 6-6' stroke='%236a6a6a' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 2px center;
	padding-right: 26px;
}

.pb-field textarea {
	min-height: 84px;
	resize: vertical;
	display: block;
}

.pb-field input:focus,
.pb-field select:focus,
.pb-field textarea:focus {
	outline: none;
	box-shadow: none;
	border: none;
}

.pb-field input::placeholder,
.pb-field textarea::placeholder {
	color: var(--pb-text-faint);
}

/* Muted "(optional)"-style suffix on a .pb-field label — used by the "Add a
   team member" title field, which is the only optional field in this row. */
.pb-optional {
	font-weight: 400;
	color: var(--pb-text-faint);
}

/* "Add a team member" (Availability screen) — its field-row has a third
   slot for the submit button, which shouldn't look like a bordered text
   field the way .pb-field normally renders. */
.pb-team-add .pb-field-row {
	grid-template-columns: 1fr 1fr auto;
	align-items: end;
}

.pb-team-add-btn {
	border: none;
	background: none;
	padding: 0;
	margin-bottom: 14px;
}

.pb-team-add-btn .pb-btn {
	width: 100%;
	white-space: nowrap;
}

@media (max-width: 782px) {
	.pb-team-add .pb-field-row {
		grid-template-columns: 1fr;
	}
	.pb-team-add-btn .pb-btn {
		width: auto;
	}
}

/* Collapsed-by-default "Add a team member" — the toggle sits under the
   schedule grid rather than a fixed block always open above it. */
.pb-team-add-wrap {
	margin-top: 18px;
}

.pb-team-add-toggle {
	background: var(--pb-white);
}

.pb-team-add-head {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: 12px;
}

/* Session details (intake) — Create Appointment, and the answers shown in
   the appointment drawer. Mirrors the public widget's own intake step. */
.pb-question {
	margin-bottom: 16px;
}

.pb-question:last-child {
	margin-bottom: 0;
}

.pb-question-label {
	font-size: 13px;
	font-weight: 700;
	color: var(--pb-text);
	margin-bottom: 8px;
}

.pb-choice-grid {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
}

.pb-choice {
	flex: 0 0 auto;
	padding: 9px 16px;
	border-radius: var(--pb-r-sm);
	border: 1px solid var(--pb-border);
	background: var(--pb-white);
	color: var(--pb-text);
	font-size: 13.5px;
	font-weight: 600;
	cursor: pointer;
	transition: background 0.15s ease, border-color 0.15s ease;
}

.pb-choice:hover {
	background: var(--pb-fill);
	border-color: #d2d2d2;
}

.pb-choice.is-selected {
	background: var(--pb-black);
	border-color: var(--pb-black);
	color: var(--pb-white);
}

.pb-choice:active {
	transform: scale(0.97);
}

/* ---------------- Rewards points (customer profile) ---------------- */

.pb-points-balance {
	display: flex;
	align-items: baseline;
	gap: 6px;
	margin-bottom: 10px;
}

.pb-points-balance strong {
	font-size: 22px;
	font-weight: 700;
	letter-spacing: -0.02em;
	color: var(--pb-purple);
}

.pb-points-balance span {
	font-size: 12.5px;
	color: var(--pb-text-muted);
}

.pb-points-ledger {
	border-top: 1px solid var(--pb-hairline);
}

.pb-points-row {
	display: grid;
	grid-template-columns: 44px 1fr auto;
	align-items: center;
	gap: 10px;
	padding: 7px 0;
	border-bottom: 1px solid var(--pb-hairline);
	font-size: 12.5px;
}

.pb-points-delta {
	font-weight: 700;
	font-variant-numeric: tabular-nums;
	color: var(--pb-success, #1b8a5a);
}

.pb-points-delta.is-spent {
	color: var(--pb-purple);
}

.pb-points-reason {
	color: var(--pb-text);
}

.pb-points-when {
	color: var(--pb-text-faint);
	white-space: nowrap;
}

.pb-intake-answers {
	display: flex;
	flex-direction: column;
	gap: 10px;
}

.pb-intake-answer {
	font-size: 13.5px;
	line-height: 1.5;
}

.pb-intake-answer strong {
	display: block;
	font-size: 11.5px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--pb-text-muted);
	margin-bottom: 2px;
}

/* Customer search */
.pb-customer-search-bar {
	display: flex;
	gap: 10px;
	align-items: center;
	margin-bottom: 12px;
}

.pb-customer-search-bar input {
	flex: 1 1 auto;
	padding: 12px 16px;
	border: 1px solid var(--pb-border);
	border-radius: var(--pb-r);
	font-family: inherit;
	font-size: 15px;
	color: var(--pb-text);
	background: var(--pb-white);
}

.pb-customer-search-bar input:focus {
	outline: none;
	border-color: var(--pb-purple);
	box-shadow: 0 0 0 3px var(--pb-purple-tint);
}

.pb-customer-list-label {
	font-size: 12px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--pb-text-muted);
	margin: 4px 2px 8px;
}

.pb-customer-search-results {
	border: 1px solid var(--pb-border);
	border-radius: var(--pb-r);
	max-height: 260px;
	overflow-y: auto;
	background: var(--pb-white);
}

.pb-customer-result {
	padding: 12px 16px;
	border-bottom: 1px solid var(--pb-hairline);
	cursor: pointer;
	font-size: 14px;
}

.pb-customer-result:last-child {
	border-bottom: none;
}

.pb-customer-result:hover {
	background: var(--pb-fill);
}

.pb-cr-name {
	font-weight: 600;
}

.pb-cr-sub {
	font-size: 12.5px;
	color: var(--pb-text-muted);
	margin-top: 2px;
}

.pb-selected-customer {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	padding: 14px 16px;
	border: 1px solid var(--pb-border);
	border-radius: var(--pb-r);
	background: var(--pb-white);
	margin-bottom: 14px;
}

/* Toggle (Square's black pill switch) */
.pb-toggle-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 20px;
	padding: 6px 0 16px;
}

.pb-toggle {
	width: 52px;
	height: 30px;
	flex: 0 0 52px;
	border-radius: var(--pb-r-pill);
	background: #d4d4d4;
	border: none;
	position: relative;
	cursor: pointer;
	padding: 0;
	transition: background-color 0.18s ease;
}

.pb-toggle-knob {
	position: absolute;
	top: 3px;
	left: 3px;
	width: 24px;
	height: 24px;
	border-radius: 50%;
	background: var(--pb-white);
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
	transition: transform 0.18s ease;
}

.pb-toggle.is-on {
	background: var(--pb-black);
}

.pb-toggle.is-on .pb-toggle-knob {
	transform: translateX(22px);
}

.pb-card-on-file {
	border: 1px solid var(--pb-border);
	border-radius: var(--pb-r);
	padding: 16px 18px;
	background: var(--pb-white);
	font-size: 14px;
}

/* Order summary side panel */
.pb-summary-side {
	position: sticky;
	top: 50px;
}

.pb-summary-row {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 16px;
	font-size: 14px;
	padding: 10px 0;
	border-bottom: 1px solid var(--pb-hairline);
}

.pb-summary-row > span:first-child {
	color: var(--pb-text-muted);
}

.pb-summary-row > span:last-child {
	font-weight: 600;
}

.pb-summary-row.pb-total {
	border-top: 1px solid var(--pb-border);
	border-bottom: none;
	margin-top: 6px;
	padding-top: 15px;
}

.pb-summary-row.pb-total > span:first-child {
	color: var(--pb-text);
	font-weight: 700;
	font-size: 15px;
}

.pb-summary-row.pb-total > span:last-child {
	font-size: 22px;
	font-weight: 700;
	letter-spacing: -0.03em;
}

/* ---------------- Customers list + profile ---------------- */

.pb-customer-list-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	padding: 15px 18px;
	border-bottom: 1px solid var(--pb-hairline);
	cursor: pointer;
}

.pb-customer-list-row:last-child {
	border-bottom: none;
}

.pb-customer-list-row:hover {
	background: var(--pb-fill);
}

.pb-cl-name {
	font-weight: 600;
	font-size: 14.5px;
}

.pb-cl-sub {
	font-size: 12.5px;
	color: var(--pb-text-muted);
	margin-top: 3px;
}

/* Points/credit alongside the visit count on each Customers list row. */
.pb-cl-meta {
	display: flex;
	gap: 18px;
	flex: 0 0 auto;
	text-align: right;
}

.pb-cl-meta .pb-cl-sub {
	margin-top: 0;
	white-space: nowrap;
}

/* Studio credit balance on the customer profile — same look as the
   rewards-points balance just above it, in a distinct (green) color so the
   two ledgers read as related but not identical. */
.pb-credit-amount {
	color: var(--pb-success, #1b8a5a);
}

/* Inline "move this booking" form in the appointment drawer's overflow menu. */
.pb-reschedule-form {
	padding: 14px 20px;
	border-bottom: 1px solid var(--pb-hairline);
	background: var(--pb-fill);
}

.pb-reschedule-form .pb-field-row {
	margin-bottom: 10px;
}

.pb-profile-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 20px;
	margin-bottom: 26px;
	flex-wrap: wrap;
}

#pb-admin-root .pb-profile-header h2 {
	font-size: 30px;
	font-weight: 700;
	letter-spacing: -0.03em;
}

.pb-profile-stats {
	display: flex;
	gap: 52px;
	padding-bottom: 26px;
	flex-wrap: wrap;
}

.pb-profile-stat {
	min-width: 0;
}

.pb-ps-label {
	font-size: 13.5px;
	color: var(--pb-text-muted);
	margin-bottom: 4px;
}

.pb-ps-value {
	font-size: 26px;
	font-weight: 700;
	letter-spacing: -0.03em;
	line-height: 1.2;
}

/* Square separates profile blocks with a thick soft grey bar rather than a
   hairline. */
/* Separates the major blocks of a long screen — the customer profile, the
   sessions list, the settings groups.

   This used to be an 8px solid band. On a profile with three sections it
   read as structure; on the sessions list, which is four in a row, it read
   as three grey slabs laid across the page, and it is the heaviest divider
   in a design that uses hairlines everywhere else. A rule and some air do
   the same job without competing with the content on either side of it. */
.pb-profile-section {
	border-top: 1px solid var(--pb-hairline);
	padding-top: 26px;
	margin-top: 30px;
	margin-bottom: 26px;
}

/* The first one is not separating anything from anything. */
.pb-profile-section:first-child {
	border-top: none;
	padding-top: 0;
	margin-top: 0;
}

#pb-admin-root .pb-profile-section > h3 {
	font-size: 19px;
	font-weight: 700;
	margin-bottom: 12px;
}

.pb-profile-row {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: 24px;
	padding: 15px 0;
	border-bottom: 1px solid var(--pb-hairline);
	font-size: 14px;
}

.pb-profile-row:last-child {
	border-bottom: none;
}

.pb-pr-label {
	flex: 0 0 180px;
	font-weight: 700;
}

.pb-profile-row a {
	text-decoration: underline;
	text-underline-offset: 2px;
}

.pb-chip {
	display: inline-block;
	padding: 6px 14px;
	border-radius: var(--pb-r-pill);
	background: var(--pb-blue-tint);
	color: var(--pb-blue);
	font-size: 12.5px;
	font-weight: 600;
	margin: 0 6px 6px 0;
}

.pb-mini-tabs {
	display: flex;
	gap: 20px;
	margin-bottom: 6px;
	border-bottom: 1px solid var(--pb-border);
}

.pb-mini-tabs > span {
	padding: 10px 0 12px;
	font-size: 14px;
	font-weight: 600;
	color: var(--pb-text-muted);
	cursor: pointer;
	border-bottom: 2px solid transparent;
	margin-bottom: -1px;
}

.pb-mini-tabs > span.is-active {
	color: var(--pb-text);
	border-bottom-color: var(--pb-black);
}

.pb-transaction-row,
.pb-booking-row {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: 20px;
	padding: 14px 0;
	border-bottom: 1px solid var(--pb-hairline);
	font-size: 14px;
}

.pb-transaction-row:last-child,
.pb-booking-row:last-child {
	border-bottom: none;
}

/* ---------------- Settings ---------------- */

.pb-settings-form {
	max-width: 620px;
}

/* ---------------- Settings sub-navigation ---------------- */

.pb-settings-nav {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	margin-bottom: 22px;
	padding-bottom: 18px;
	border-bottom: 1px solid var(--pb-hairline);
}

.pb-settings-nav-btn {
	padding: 8px 15px;
	border-radius: var(--pb-r-pill);
	font-family: inherit;
	font-size: 13px;
	font-weight: 600;
	color: var(--pb-text-muted);
	background: var(--pb-fill);
	border: 1px solid transparent;
	cursor: pointer;
	white-space: nowrap;
	transition: background-color 0.15s ease, color 0.15s ease;
}

.pb-settings-nav-btn:hover {
	background: var(--pb-fill-hover);
	color: var(--pb-text);
}

.pb-settings-nav-btn.is-active {
	background: var(--pb-black);
	color: var(--pb-white);
}

.pb-settings-section.pb-hidden {
	display: none;
}

.pb-settings-status {
	display: inline-flex;
	align-items: center;
	padding: 6px 14px;
	border-radius: var(--pb-r-pill);
	font-size: 12.5px;
	font-weight: 600;
	white-space: nowrap;
}

.pb-settings-status.is-connected {
	background: #e7f5ee;
	color: #10603c;
}

.pb-settings-status.is-disconnected {
	background: var(--pb-fill);
	color: var(--pb-text-muted);
}

/* Test mode is a deliberate, active choice that changes financial behavior
   (no card is ever charged) — worth a warning tone, not the neutral grey
   "nothing configured yet" of is-disconnected, so it can't be mistaken for
   "payments just aren't set up." */
.pb-settings-status.is-testmode {
	background: var(--pb-warn-bg);
	color: var(--pb-warn-text);
	border: 1px solid var(--pb-warn-border);
}

/* ---------------- Buttons ---------------- */

.pb-btn {
	padding: 11px 22px;
	border-radius: var(--pb-r-pill);
	font-family: inherit;
	font-size: 14px;
	font-weight: 600;
	letter-spacing: -0.005em;
	line-height: 1.2;
	cursor: pointer;
	border: 1px solid transparent;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	text-decoration: none;
}

.pb-btn-primary {
	background: var(--pb-black);
	color: var(--pb-white);
}

.pb-btn-primary:hover:not(:disabled) {
	background: #000;
}

.pb-btn-primary:disabled {
	background: var(--pb-fill);
	color: var(--pb-text-faint);
	cursor: not-allowed;
}

.pb-btn-secondary {
	background: var(--pb-fill);
	color: var(--pb-text);
	border-color: transparent;
}

.pb-btn-secondary:hover:not(:disabled) {
	background: var(--pb-fill-hover);
}

.pb-btn-danger {
	background: var(--pb-white);
	color: var(--pb-danger);
	border-color: var(--pb-danger-border);
}

.pb-btn-danger:hover:not(:disabled) {
	background: var(--pb-danger-tint);
}

/* ---------------- Banners ---------------- */

.pb-error-banner {
	background: var(--pb-danger-tint);
	border: 1px solid var(--pb-danger-border);
	color: var(--pb-danger);
	font-size: 13px;
	padding: 12px 14px;
	border-radius: var(--pb-r);
	margin-bottom: 16px;
	line-height: 1.5;
}

.pb-mock-banner {
	background: var(--pb-warn-bg);
	border: 1px solid var(--pb-warn-border);
	color: var(--pb-warn-text);
	font-size: 12.5px;
	padding: 12px 14px;
	border-radius: var(--pb-r);
	margin-bottom: 16px;
	line-height: 1.6;
}

/* ---------------- Responsive ---------------- */

@media (max-width: 1100px) {
	.pb-overview-grid,
	.pb-create-grid {
		grid-template-columns: minmax(0, 1fr);
	}
	.pb-summary-side {
		position: static;
	}
	.pb-calendar-grid {
		overflow-x: auto;
	}
	.pb-cal-head,
	.pb-cal-body {
		min-width: 760px;
	}
}

@media (max-width: 782px) {
	.pb-page-title {
		font-size: 26px;
	}
	.pb-field-row {
		grid-template-columns: 1fr;
	}
	.pb-appt-row {
		flex-direction: column;
		gap: 6px;
	}
	.pb-appt-time {
		flex-basis: auto;
	}
	.pb-profile-stats {
		gap: 28px;
	}
	.pb-pr-label {
		flex-basis: 120px;
	}
}

/* ---------------- Staff availability ---------------- */

.pb-avail-intro {
	max-width: 760px;
	margin: -14px 0 24px;
	font-size: 14px;
	color: var(--pb-text-muted);
	line-height: 1.6;
}

/* Team member down the side, weekdays across the top. The name column is
   fixed so the seven day columns stay evenly sized. */
.pb-avail-grid {
	display: grid;
	grid-template-columns: 220px repeat(7, minmax(0, 1fr));
	border: 1px solid var(--pb-border);
	border-radius: var(--pb-r-lg);
	overflow: hidden;
	background: var(--pb-white);
	box-shadow: var(--pb-shadow);
}

.pb-avail-head {
	padding: 16px 14px;
	font-size: 14px;
	font-weight: 600;
	color: var(--pb-text);
	border-bottom: 1px solid var(--pb-border);
	border-right: 1px solid var(--pb-hairline);
	background: var(--pb-white);
	white-space: nowrap;
}

/* The header cells and each person's cells are wrapped in a container in
   the DOM (see renderAvailability()'s draw()) purely so the phone layout
   has something to group by. On a wide screen the wrapper must not be a
   box at all, or it would become a single grid item holding eight cells:
   `display: contents` makes its children the grid's items directly, so
   this layout is byte-for-byte the one that existed before the wrappers. */
.pb-avail-headrow,
.pb-avail-person {
	display: contents;
}

.pb-avail-headrow > .pb-avail-head:last-child {
	border-right: none;
}

/* Phone-only: stated in the stacked layout, where a blank row would read
   as something that failed to load rather than a day with no hours. */
.pb-avail-empty {
	display: none;
}

.pb-avail-namecol {
	border-right: 1px solid var(--pb-border);
}

.pb-avail-name {
	position: relative;
	display: flex;
	flex-direction: column;
	justify-content: center;
	gap: 4px;
	padding: 18px 34px 18px 14px;
	font-size: 14.5px;
	font-weight: 700;
	border-bottom: 1px solid var(--pb-hairline);
	min-height: 96px;
	overflow: hidden;
}

.pb-avail-name > span:first-child {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* Removes a team member from the roster (see PB_Team::remove_member()) —
   same quiet, hover-to-reveal treatment as the Overview screen's
   notification dismiss "×", except this one turns danger-red on hover
   since it's the one destructive control on this screen.
   Named .pb-team-remove (not .pb-avail-remove) because that class already
   belongs to the underlined exception-row remove link further down this
   file — different control, same-looking name would have collided. */
.pb-team-remove {
	position: absolute;
	top: 14px;
	right: 8px;
	width: 22px;
	height: 22px;
	padding: 0;
	border: none;
	background: none;
	border-radius: 50%;
	font-size: 13px;
	line-height: 1;
	color: var(--pb-text-faint);
	cursor: pointer;
	transition: background-color 0.15s ease, color 0.15s ease;
}

.pb-team-remove:hover {
	background: var(--pb-danger-tint);
	color: var(--pb-danger);
}

.pb-avail-inherit {
	font-size: 11px;
	font-weight: 600;
	color: var(--pb-text-muted);
	background: var(--pb-fill);
	border-radius: var(--pb-r-pill);
	padding: 3px 9px;
	width: fit-content;
}

.pb-avail-cell {
	padding: 10px 8px;
	border-bottom: 1px solid var(--pb-hairline);
	border-right: 1px solid var(--pb-hairline);
	min-height: 96px;
	cursor: pointer;
	display: flex;
	flex-direction: column;
	gap: 6px;
	transition: background-color 0.12s ease;
}

.pb-avail-cell:hover {
	background: #fafafa;
}

.pb-avail-person > .pb-avail-cell:last-child {
	border-right: none;
}

/* The green "Available" block, matching Square's availability grid. */
.pb-avail-block {
	background: #e6f8ec;
	color: #1a1a1a;
	border-radius: var(--pb-r-sm);
	padding: 9px 11px;
	font-size: 12.5px;
	line-height: 1.45;
}

.pb-avail-block-title {
	font-weight: 600;
}

/* --- day editor modal --- */

.pb-modal-overlay {
	position: fixed;
	inset: 0;
	background: rgba(0, 0, 0, 0.55);
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 24px;
	z-index: 160000;
}

.pb-modal {
	background: var(--pb-white);
	border-radius: var(--pb-r-lg);
	width: 100%;
	max-width: 560px;
	max-height: 86vh;
	display: flex;
	flex-direction: column;
	box-shadow: var(--pb-shadow-lg);
	overflow: hidden;
}

.pb-modal-head {
	position: relative;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 2px;
	padding: 22px 24px 18px;
	border-bottom: 1px solid var(--pb-hairline);
}

#pb-admin-root .pb-modal-title {
	font-size: 24px;
	font-weight: 700;
}

.pb-modal-who {
	font-size: 13px;
	color: var(--pb-text-muted);
}

.pb-modal-close {
	position: absolute;
	left: 18px;
	top: 18px;
	width: 38px;
	height: 38px;
	border-radius: 50%;
	border: none;
	background: var(--pb-fill);
	color: var(--pb-text);
	font-size: 14px;
	line-height: 1;
	cursor: pointer;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: 0;
}

.pb-modal-close:hover {
	background: var(--pb-fill-hover);
}

.pb-modal-body {
	padding: 24px;
	overflow-y: auto;
}

.pb-modal-foot {
	display: flex;
	justify-content: flex-end;
	gap: 10px;
	padding: 16px 24px;
	border-top: 1px solid var(--pb-hairline);
}

.pb-avail-allday {
	display: flex;
	align-items: center;
	gap: 14px;
	padding: 18px 20px;
	border: 1px solid var(--pb-border);
	border-radius: var(--pb-r);
	font-size: 15px;
	font-weight: 600;
}

.pb-avail-windows {
	margin-top: 24px;
	border: 1px solid var(--pb-border);
	border-radius: var(--pb-r);
	overflow: hidden;
}

/* Label cell on a grey ground, value cell on white — Square's split rows. */
.pb-avail-row {
	display: grid;
	grid-template-columns: 190px minmax(0, 1fr);
	border-bottom: 1px solid var(--pb-hairline);
}

.pb-avail-row-label {
	padding: 16px 18px;
	background: var(--pb-band);
	font-size: 14.5px;
	font-weight: 600;
}

.pb-avail-row-value {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 10px 18px;
}

.pb-avail-select {
	flex: 1 1 auto;
	min-width: 0;
	padding: 9px 10px;
	border: 1px solid var(--pb-border);
	border-radius: var(--pb-r-sm);
	background: var(--pb-white);
	font-family: inherit;
	font-size: 14.5px;
	color: var(--pb-text);
	cursor: pointer;
}

.pb-avail-select:focus {
	outline: none;
	border-color: var(--pb-purple);
	box-shadow: 0 0 0 3px var(--pb-purple-tint);
}

.pb-avail-remove,
.pb-avail-add {
	border: none;
	background: none;
	padding: 0;
	font-family: inherit;
	font-size: 14px;
	font-weight: 600;
	color: var(--pb-text);
	text-decoration: underline;
	text-underline-offset: 3px;
	cursor: pointer;
	white-space: nowrap;
}

.pb-avail-remove {
	font-size: 13px;
	color: var(--pb-danger);
}

.pb-avail-add-row {
	padding: 16px 18px;
}

.pb-avail-none {
	padding: 20px 18px;
	font-size: 14px;
	color: var(--pb-text-muted);
	border-bottom: 1px solid var(--pb-hairline);
}

.pb-avail-scroll {
	overflow-x: auto;
}

@media (max-width: 1100px) {
	/* Tablet and small laptop: the matrix still reads as a matrix, it just
	   needs more room than the viewport has, so it scrolls sideways. */
	.pb-avail-grid {
		min-width: 900px;
		grid-template-columns: 180px repeat(7, minmax(0, 1fr));
	}
}

/*
 * Phone: the sideways-scrolling matrix was the wrong shape here. A 900px
 * grid on a 390px screen means the reader can see two days at a time and
 * has to scroll back to the left edge to remember whose row they are on —
 * the name column scrolls away with everything else. So below 700px the
 * matrix stops being a matrix: one card per person, their name pinned at
 * the top of it, and the seven days as full-width labelled rows down the
 * card. Nothing about the data or the tap targets changes — tapping a day
 * still opens the same editor.
 */
@media (max-width: 700px) {
	.pb-avail-scroll {
		overflow-x: visible;
	}

	.pb-avail-grid {
		display: block;
		min-width: 0;
		border: none;
		border-radius: 0;
		overflow: visible;
		background: none;
		box-shadow: none;
	}

	/* Each day row says which day it is, so the row of day names across
	   the top has nothing left to tell anyone. */
	.pb-avail-headrow {
		display: none;
	}

	.pb-avail-person {
		display: block;
		margin-bottom: 14px;
		border: 1px solid var(--pb-border);
		border-radius: var(--pb-r-lg);
		overflow: hidden;
		background: var(--pb-white);
		box-shadow: var(--pb-shadow);
	}

	.pb-avail-person:last-child {
		margin-bottom: 0;
	}

	/* The person's name is the card's header, not a cell in a column. */
	.pb-avail-name {
		flex-direction: row;
		/* The wide layout centres the name in a tall grid cell. Here it is
		   a card header, so it starts at the left edge like one. */
		justify-content: flex-start;
		align-items: center;
		flex-wrap: wrap;
		gap: 8px;
		min-height: 0;
		padding: 13px 44px 13px 16px;
		background: var(--pb-fill);
		border-bottom: 1px solid var(--pb-border);
		border-right: none;
	}

	.pb-avail-namecol {
		border-right: none;
	}

	/* transform rather than a negative margin: the touch stylesheet gives
	   every button a 44px minimum height, so this one is not the 22px its
	   own rule asks for and a fixed half-of-22 offset hung it below the
	   name it belongs to. Centring on its own height works at any size. */
	.pb-team-remove {
		top: 50%;
		right: 10px;
		margin-top: 0;
		transform: translateY(-50%);
	}

	/* One day per row: its label on the left, its hours on the right. */
	.pb-avail-cell {
		display: grid;
		grid-template-columns: 58px minmax(0, 1fr);
		align-items: center;
		gap: 10px;
		min-height: 0;
		padding: 10px 16px;
		border-right: none;
	}

	.pb-avail-person > .pb-avail-cell:last-child {
		border-bottom: none;
	}

	.pb-avail-cell::before {
		content: attr(data-daylabel);
		font-size: 12.5px;
		font-weight: 700;
		color: var(--pb-text-muted);
	}

	/* Everything in the cell is the second column. A day with two windows
	   stacks them down that column (auto-placement puts each on its own
	   row, spaced by the cell's own row gap) rather than pushing the day
	   label out of line. */
	.pb-avail-cell > * {
		grid-column: 2;
	}

	.pb-avail-empty {
		display: block;
		font-size: 13px;
		color: var(--pb-text-faint);
	}

	.pb-avail-block {
		padding: 7px 11px;
	}

	/* "Available" is what the green block already means, and in a stacked
	   list it is repeated up to seven times per person — two lines where
	   one would do, which is most of why the card ran long. The hours
	   themselves are the only part that differs row to row. */
	.pb-avail-block-title {
		display: none;
	}

	/*
	 * "Pay & accounts", directly under those cards, had the same problem
	 * in a worse form: five columns of form fields, four of them inputs,
	 * on a 390px screen. A table cannot shrink below its contents, so it
	 * simply ran off the side of the page and took the whole document's
	 * horizontal scroll with it — the fields past "Username" were not
	 * reachable at all. Same answer as the availability grid above: one
	 * card per person, each field on its own labelled row.
	 */
	.pb-pay-table,
	.pb-pay-table tbody,
	.pb-pay-table tr,
	.pb-pay-table td {
		display: block;
		width: auto;
	}

	/* The column headings move into the rows as data-label. */
	.pb-pay-table thead {
		display: none;
	}

	.pb-pay-table tr {
		margin-bottom: 14px;
		border: 1px solid var(--pb-border);
		border-radius: var(--pb-r-lg);
		overflow: hidden;
		background: var(--pb-white);
	}

	.pb-pay-table tr:last-child {
		margin-bottom: 0;
	}

	.pb-pay-table td {
		padding: 10px 14px;
		border-bottom: 1px solid var(--pb-hairline);
	}

	.pb-pay-table tr:last-child td {
		border-bottom: 1px solid var(--pb-hairline);
	}

	.pb-pay-table td:last-child {
		border-bottom: none;
	}

	/* The name is the card's header rather than a field in it. */
	.pb-pay-table .pb-pay-name {
		padding: 12px 14px;
		background: var(--pb-fill);
		font-size: 14.5px;
		font-weight: 700;
		border-bottom: 1px solid var(--pb-border);
	}

	.pb-pay-table td[data-label]::before {
		content: attr(data-label);
		display: block;
		margin-bottom: 6px;
		font-size: 11.5px;
		font-weight: 700;
		text-transform: uppercase;
		letter-spacing: 0.04em;
		color: var(--pb-text-muted);
	}

	/* A fixed 96px rate box and a 260px select were sized for a table
	   column; in a full-width row they just look unfinished. Written at id
	   strength because the rules those override sit further down this
	   file and would otherwise win on source order alone. */
	#pb-admin-root .pb-pay-table input[type="number"],
	#pb-admin-root .pb-pay-table input[type="text"],
	#pb-admin-root .pb-pay-table input[type="tel"],
	#pb-admin-root .pb-pay-table input[type="password"],
	#pb-admin-root .pb-pay-table select {
		width: 100%;
		max-width: none;
	}

	/* The dollar sign sits beside the rate box, so that one row stays
	   horizontal while everything else stacks. */
	.pb-pay-table td[data-label="Hourly rate"] {
		display: grid;
		grid-template-columns: auto minmax(0, 1fr);
		align-items: center;
		gap: 6px;
	}

	.pb-pay-table td[data-label="Hourly rate"]::before {
		grid-column: 1 / -1;
		margin-bottom: 0;
	}
}

@media (max-width: 782px) {
	.pb-avail-row {
		grid-template-columns: minmax(0, 1fr);
	}
}

/* Inputs that sit directly in a section rather than inside a .pb-field
   floating-label box — without this they fall back to the browser's
   default monospace textarea styling. */
#pb-admin-root .pb-form-section > textarea,
#pb-admin-root .pb-form-section > input[type="text"] {
	width: 100%;
	padding: 13px 16px;
	border: 1px solid var(--pb-border);
	border-radius: var(--pb-r);
	background: var(--pb-white);
	font-family: inherit;
	font-size: 14.5px;
	line-height: 1.5;
	color: var(--pb-text);
	resize: vertical;
	display: block;
}

#pb-admin-root .pb-form-section > textarea {
	min-height: 92px;
}

#pb-admin-root .pb-form-section > textarea::placeholder,
#pb-admin-root .pb-form-section > input[type="text"]::placeholder {
	color: var(--pb-text-faint);
}

#pb-admin-root .pb-form-section > textarea:focus,
#pb-admin-root .pb-form-section > input[type="text"]:focus {
	outline: none;
	border-color: var(--pb-purple);
	box-shadow: 0 0 0 3px var(--pb-purple-tint);
}

/* ---------------- Appointment detail drawer ---------------- */

.pb-drawer-overlay {
	position: fixed;
	inset: 0;
	background: rgba(0, 0, 0, 0.35);
	display: flex;
	justify-content: flex-end;
	z-index: 160000;
}

.pb-drawer {
	width: 100%;
	max-width: 520px;
	background: var(--pb-white);
	height: 100%;
	display: flex;
	flex-direction: column;
	box-shadow: -8px 0 32px rgba(0, 0, 0, 0.14);
	animation: pb-drawer-in 0.18s ease-out;
}

@keyframes pb-drawer-in {
	from { transform: translateX(24px); opacity: 0.4; }
	to   { transform: translateX(0); opacity: 1; }
}

.pb-drawer-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	padding: 18px 22px;
	border-bottom: 1px solid var(--pb-hairline);
	background: var(--pb-white);
}

.pb-drawer-head-actions {
	display: flex;
	align-items: center;
	gap: 10px;
}

.pb-drawer-more {
	position: relative;
}

.pb-drawer-menu {
	position: absolute;
	right: 0;
	top: calc(100% + 8px);
	min-width: 220px;
	background: var(--pb-white);
	border-radius: var(--pb-r);
	box-shadow: var(--pb-shadow-lg);
	border: 1px solid var(--pb-hairline);
	padding: 6px;
	z-index: 5;
}

.pb-drawer-menu-item {
	display: block;
	width: 100%;
	text-align: left;
	padding: 12px 14px;
	border: none;
	background: none;
	border-radius: var(--pb-r-sm);
	font-family: inherit;
	font-size: 14.5px;
	color: var(--pb-text);
	cursor: pointer;
}

.pb-drawer-menu-item:hover {
	background: var(--pb-fill);
}

.pb-drawer-menu-item.is-danger {
	color: var(--pb-danger);
	border-top: 1px solid var(--pb-hairline);
	border-radius: 0 0 var(--pb-r-sm) var(--pb-r-sm);
	margin-top: 4px;
}

.pb-drawer-body {
	padding: 22px;
	overflow-y: auto;
	flex: 1 1 auto;
}

.pb-pay-banner {
	display: flex;
	align-items: center;
	gap: 12px;
	background: #e6f8ec;
	color: #16603c;
	border-radius: var(--pb-r);
	padding: 16px 18px;
	font-size: 16px;
	font-weight: 600;
	margin-bottom: 24px;
}

.pb-pay-check {
	width: 22px;
	height: 22px;
	flex: 0 0 22px;
	border-radius: 50%;
	background: #1b8a5a;
	color: var(--pb-white);
	display: inline-flex;
	align-items: center;
	justify-content: center;
	font-size: 13px;
}

/* Distinct from the green "paid" banner above — amber, not green, so it
   never reads as "money collected" at a glance. */
.pb-pay-banner-comped {
	background: #fdf4e3;
	color: #7a5107;
}

.pb-pay-banner-comped .pb-pay-check {
	background: transparent;
	font-size: 16px;
}

.pb-drawer-customer {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	margin-bottom: 18px;
}

.pb-drawer-cust-name {
	font-size: 21px;
	font-weight: 700;
	letter-spacing: -0.02em;
	text-decoration: underline;
	text-underline-offset: 3px;
}

.pb-drawer-chevron {
	color: var(--pb-text-muted);
	font-size: 22px;
	line-height: 1;
}

.pb-drawer-contact {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 16px 20px;
}

.pb-drawer-label {
	font-size: 11.5px;
	font-weight: 600;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--pb-text-muted);
	margin-bottom: 5px;
}

.pb-drawer-label-sp {
	margin-top: 22px;
}

.pb-drawer-link {
	font-size: 14.5px;
	text-decoration: underline;
	text-underline-offset: 2px;
	word-break: break-word;
}

.pb-drawer-empty {
	font-size: 14.5px;
	color: var(--pb-text-muted);
}

/* Thick soft grey bar, like Square's section separators. */
.pb-drawer-rule {
	height: 8px;
	background: var(--pb-band);
	border-radius: var(--pb-r-pill);
	margin: 28px 0;
}

.pb-drawer-status {
	font-size: 15px;
	font-weight: 600;
	margin-bottom: 6px;
}

.pb-drawer-status.is-confirmed { color: #1b8a5a; }
.pb-drawer-status.is-noshow    { color: #9a6f22; }
.pb-drawer-status.is-cancelled { color: var(--pb-danger); }

.pb-drawer-when {
	font-size: 22px;
	font-weight: 700;
	letter-spacing: -0.025em;
}

.pb-drawer-dur {
	font-size: 15px;
	font-weight: 400;
	color: var(--pb-text);
}

.pb-drawer-date {
	font-size: 14.5px;
	font-weight: 600;
	margin-top: 4px;
}

.pb-drawer-note {
	font-size: 14.5px;
	line-height: 1.6;
	white-space: pre-line;
}

.pb-drawer-pay-in-studio {
	margin-top: 8px;
	padding: 10px 12px;
	font-size: 13px;
	font-weight: 600;
	color: #2b5a7a;
	background: #eaf3fb;
	border: 1px solid #cbe3f5;
	border-radius: var(--pb-r, 8px);
}

#pb-admin-root .pb-drawer-h {
	font-size: 20px;
	font-weight: 700;
	margin-bottom: 14px;
}

.pb-line-item,
.pb-hist-row {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: 18px;
	padding: 14px 0;
	border-bottom: 1px solid var(--pb-hairline);
}

.pb-line-name {
	font-size: 14.5px;
	font-weight: 700;
}

.pb-line-sub {
	font-size: 13px;
	color: var(--pb-text-muted);
	margin-top: 2px;
}

.pb-line-price {
	font-size: 14.5px;
	font-weight: 600;
	white-space: nowrap;
}

.pb-appt-row.is-clickable {
	cursor: pointer;
	border-radius: var(--pb-r-sm);
	transition: background-color 0.12s ease;
}

.pb-appt-row.is-clickable:hover {
	background: #fafafa;
}

.pb-cal-event {
	cursor: pointer;
}

.pb-cal-event.is-cancelled {
	opacity: 0.5;
	text-decoration: line-through;
}

/* A customer tapped "I'm running late" and it hasn't been dismissed yet —
   see PB_DB::get_running_late_appointment_ids(). A ring, not a fill, so it
   never fights the per-staff background color already carried on this
   same element. */
.pb-cal-event.is-running-late {
	box-shadow: inset 0 0 0 2px #b3261e;
}

.pb-cal-late-badge {
	position: absolute;
	top: 2px;
	right: 4px;
	font-size: 11px;
	line-height: 1;
}

/* Appointment drawer: the self-hosted room-photo checklist. */
.pb-room-photo-list {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	margin: 6px 0 4px;
}

.pb-room-photo-thumb {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 4px;
	width: 84px;
	font-size: 10.5px;
	color: var(--pb-text-muted);
	text-decoration: none;
}

.pb-room-photo-thumb img {
	width: 84px;
	height: 84px;
	object-fit: cover;
	border-radius: 6px;
	border: 1px solid var(--pb-hairline);
}

.pb-room-photo-missing {
	font-size: 12px;
	color: var(--pb-danger);
	margin-bottom: 4px;
}

/* A checked-in guest waiting on "can I stay longer?". Deliberately the
   loudest thing in the drawer — someone is standing in a studio waiting
   on whoever opens this. */
.pb-time-request {
	margin: 4px 0 14px;
	padding: 14px 16px;
	border: 1px solid #f0c98a;
	background: #fdf6e3;
	border-radius: 10px;
}

.pb-time-request-head {
	font-size: 13.5px;
	font-weight: 700;
	color: #6b5300;
}

.pb-time-request-sub {
	margin-top: 4px;
	font-size: 12px;
	line-height: 1.5;
	color: #7d6a33;
}

.pb-time-request-actions {
	display: flex;
	gap: 8px;
	margin-top: 12px;
}

.pb-time-request-actions .pb-btn {
	flex: 1;
	justify-content: center;
}

@media (max-width: 600px) {
	.pb-drawer {
		max-width: 100%;
	}
}

/* ---------------- Settings: studio hours ---------------- */

#pb-admin-root .pb-settings-h {
	font-size: 19px;
	font-weight: 700;
	margin: 30px 0 6px;
	padding-top: 26px;
	border-top: 1px solid var(--pb-hairline);
}

.pb-settings-hint {
	font-size: 13px;
	color: var(--pb-text-muted);
	line-height: 1.6;
	margin-bottom: 16px;
}

.pb-hours-grid {
	border: 1px solid var(--pb-border);
	border-radius: var(--pb-r);
	overflow: hidden;
	margin-bottom: 8px;
}

.pb-hours-row {
	display: grid;
	grid-template-columns: 130px minmax(0, 1fr);
	align-items: center;
	border-bottom: 1px solid var(--pb-hairline);
}

.pb-hours-row:last-child {
	border-bottom: none;
}

.pb-hours-day {
	padding: 12px 16px;
	background: var(--pb-band);
	font-size: 14px;
	font-weight: 600;
	align-self: stretch;
	display: flex;
	align-items: center;
}

.pb-hours-controls {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 10px 16px;
	flex-wrap: wrap;
}

.pb-hours-controls .pb-avail-select {
	flex: 0 1 130px;
}

.pb-hours-controls .pb-avail-select:disabled {
	opacity: 0.45;
	cursor: not-allowed;
}

.pb-hours-dash {
	color: var(--pb-text-muted);
}

.pb-hours-closed {
	display: inline-flex;
	align-items: center;
	gap: 7px;
	margin-left: auto;
	font-size: 13.5px;
	font-weight: 600;
	cursor: pointer;
	white-space: nowrap;
}

.pb-hours-closed input {
	width: 17px;
	height: 17px;
	margin: 0;
	cursor: pointer;
}

/* Self-hosting row on the Availability grid */
.pb-avail-selfhost {
	font-size: 11px;
	font-weight: 600;
	color: #1a6b45;
	background: #e6f8ec;
	border-radius: var(--pb-r-pill);
	padding: 3px 9px;
	width: fit-content;
}

/* ---------------- Toolbar filter chips ---------------- */

/* Same pill shape as .pb-date-range, but wrapping a real <select> so the
   value stays keyboard- and screen-reader friendly. */
.pb-filter-chip {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	padding: 7px 10px 7px 20px;
	border: 1px solid var(--pb-border);
	border-radius: var(--pb-r-pill);
	background: var(--pb-white);
	white-space: nowrap;
}

.pb-filter-chip-label {
	font-size: 14.5px;
	color: var(--pb-text-muted);
}

.pb-chip-select {
	border: none;
	background: transparent;
	font-family: inherit;
	font-size: 14.5px;
	font-weight: 700;
	letter-spacing: -0.01em;
	color: var(--pb-text);
	cursor: pointer;
	padding: 4px 22px 4px 2px;
	-webkit-appearance: none;
	appearance: none;
	background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9' fill='none'%3E%3Cpath d='M1 1l6 6 6-6' stroke='%231a1a1a' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 2px center;
}

.pb-chip-select:focus {
	outline: none;
	box-shadow: 0 0 0 3px var(--pb-purple-tint);
	border-radius: var(--pb-r-sm);
}

/* ---------------- Access codes ---------------- */

.pb-codes-toolbar {
	display: flex;
	gap: 10px;
	margin-bottom: 18px;
}

.pb-codes-scroll {
	overflow-x: auto;
}

.pb-codes-table {
	display: grid;
	grid-template-columns: 130px minmax(180px, 1.2fr) minmax(200px, 1.4fr) 150px 150px 110px;
	min-width: 900px;
	border: 1px solid var(--pb-border);
	border-radius: var(--pb-r-lg);
	overflow: hidden;
	background: var(--pb-white);
	box-shadow: var(--pb-shadow);
}

.pb-codes-head {
	padding: 14px 16px;
	font-size: 12.5px;
	font-weight: 700;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--pb-text-muted);
	background: var(--pb-band);
	border-bottom: 1px solid var(--pb-border);
}

.pb-codes-cell {
	padding: 14px 16px;
	border-bottom: 1px solid var(--pb-hairline);
	font-size: 14px;
	display: flex;
	flex-direction: column;
	justify-content: center;
	gap: 2px;
	min-width: 0;
}

.pb-codes-actions {
	align-items: flex-start;
	justify-content: center;
}

.pb-code-digits {
	font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
	font-size: 19px;
	font-weight: 700;
	letter-spacing: 0.12em;
	color: var(--pb-text);
}

.pb-code-digits.is-live {
	color: #10603c;
}

.pb-code-hidden {
	font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
	font-size: 19px;
	letter-spacing: 0.12em;
	color: #c4c4c4;
}

.pb-code-state {
	display: inline-block;
	width: fit-content;
	padding: 5px 12px;
	border-radius: var(--pb-r-pill);
	font-size: 12px;
	font-weight: 600;
	background: var(--pb-fill);
	color: var(--pb-text-muted);
}

.pb-code-state.is-active {
	background: #e7f5ee;
	color: #10603c;
}

.pb-code-state.is-scheduled {
	background: var(--pb-blue-tint);
	color: var(--pb-blue);
}

.pb-code-state.is-revoked,
.pb-code-state.is-cancelled {
	background: var(--pb-danger-tint);
	color: var(--pb-danger);
}

/* ---------------- Motion & polish ---------------- */

@media (prefers-reduced-motion: reduce) {
	#pb-admin-root *,
	#pb-admin-root *::before,
	#pb-admin-root *::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
	}
}

@keyframes pb-admin-fade-up {
	from { opacity: 0; transform: translateY(6px); }
	to   { opacity: 1; transform: translateY(0); }
}

@keyframes pb-admin-fade {
	from { opacity: 0; }
	to   { opacity: 1; }
}

@keyframes pb-admin-pulse {
	0%, 100% { opacity: 1; }
	50%      { opacity: 0.45; }
}

.pb-card,
.pb-calendar-grid,
.pb-avail-grid,
.pb-codes-table {
	animation: pb-admin-fade-up 0.24s cubic-bezier(0.22, 0.61, 0.36, 1) both;
}

.pb-loading {
	animation: pb-admin-pulse 1.4s ease-in-out infinite;
}

.pb-drawer-overlay,
.pb-modal-overlay {
	animation: pb-admin-fade 0.16s ease both;
}

.pb-drawer-menu {
	animation: pb-admin-fade-up 0.14s ease both;
}

.pb-cal-event,
.pb-avail-cell,
.pb-appt-row.is-clickable,
.pb-customer-list-row {
	transition: background-color 0.14s ease, box-shadow 0.16s ease, transform 0.12s ease;
}

.pb-cal-event:hover {
	box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
	transform: translateY(-1px);
}

.pb-btn {
	transition: background-color 0.15s ease, color 0.15s ease,
		box-shadow 0.18s ease, transform 0.1s ease;
}

.pb-btn:not(:disabled):active {
	transform: translateY(1px);
}

.pb-icon-btn:active {
	transform: scale(0.94);
}

/* ---------------- Spacing corrections ---------------- */

/* First heading in the settings card has nothing above it to divide from. */
#pb-admin-root .pb-settings-h:first-of-type {
	margin-top: 8px;
	padding-top: 0;
	border-top: none;
}

/* Keep the "works between" range on one line rather than wrapping mid-time. */
.pb-codes-table {
	grid-template-columns: 130px minmax(180px, 1.1fr) minmax(200px, 1.3fr) 190px 150px 120px;
}

.pb-codes-cell {
	white-space: normal;
}

.pb-codes-cell.pb-line-sub {
	white-space: nowrap;
}

.pb-avail-intro + .pb-avail-intro {
	margin-top: -14px;
}

.pb-codes-toolbar {
	margin-top: 4px;
}

.pb-page-title + .pb-avail-intro {
	margin-top: -16px;
}

/* Give the settings form room to breathe at the bottom. */
.pb-settings-form {
	padding-bottom: 8px;
}

/* ================= REPORTING ================= */

/*
 * Chart colours are the two validated categorical slots (blue, orange),
 * checked against a white surface with the data-viz palette validator:
 * CVD ΔE 24.7 protan / 32.7 tritan, normal-vision ΔE 33.6, both ≥ 3:1
 * contrast. They are deliberately NOT the --pb-studio-* trio used on the
 * Calendar: studio amber and studio green sit at ΔE 2.6 under protanopia,
 * which is fine where a text label carries the meaning (a calendar chip)
 * and not fine where colour is the series identity.
 *
 * Only the time series is two-series. Every breakdown is a magnitude
 * comparison whose rows are already named and valued in text, so those
 * take one hue and no legend — a colour per row would encode nothing the
 * label doesn't already say.
 */
#pb-admin-root {
	--pb-series-current: #2a78d6;
	--pb-series-prev: #eb6834;
	--pb-chart-grid: #ececec;
	--pb-chart-axis: #d8d8d8;
}

/* ---------------- filter row ---------------- */

.pb-report-filters {
	display: flex;
	align-items: center;
	gap: 12px;
	flex-wrap: wrap;
	margin-bottom: 10px;
}

.pb-report-spacer { flex: 1 1 auto; }

.pb-seg {
	display: inline-flex;
	background: var(--pb-fill);
	border-radius: var(--pb-r-pill);
	padding: 3px;
	gap: 2px;
}

.pb-seg-wrap { flex-wrap: wrap; }

.pb-seg-btn {
	appearance: none;
	border: 0;
	background: transparent;
	color: var(--pb-text-muted);
	font: inherit;
	font-size: 13px;
	font-weight: 600;
	padding: 7px 16px;
	border-radius: var(--pb-r-pill);
	cursor: pointer;
	white-space: nowrap;
	transition: background 0.12s ease, color 0.12s ease;
}

.pb-seg-btn:hover { color: var(--pb-text); }

.pb-seg-btn.is-active {
	background: var(--pb-white);
	color: var(--pb-text);
	box-shadow: var(--pb-shadow);
}

.pb-seg-sm .pb-seg-btn { padding: 6px 13px; font-size: 12.5px; }

.pb-report-custom {
	display: inline-flex;
	align-items: center;
	gap: 8px;
}

/* wp-admin styles bare inputs itself, and its defaults look nothing like
   the rest of this screen — so the reporting controls carry their own. */
#pb-admin-root .pb-input {
	border: 1px solid var(--pb-border);
	border-radius: var(--pb-r-sm);
	background: var(--pb-white);
	color: var(--pb-text);
	font-family: inherit;
	font-size: 13px;
	line-height: 1.4;
	padding: 8px 11px;
	min-height: 0;
	box-shadow: none;
	transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

#pb-admin-root select.pb-input {
	padding-right: 28px;
	background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%236a6a6a' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 10px center;
	-webkit-appearance: none;
	appearance: none;
}

#pb-admin-root .pb-input:focus {
	outline: none;
	border-color: var(--pb-purple);
	box-shadow: 0 0 0 3px var(--pb-purple-tint);
}

.pb-report-to { color: var(--pb-text-muted); font-size: 13px; }

.pb-basis { max-width: 190px; }

.pb-report-range {
	font-size: 14px;
	margin-bottom: 24px;
	display: flex;
	align-items: baseline;
	gap: 10px;
	flex-wrap: wrap;
}

.pb-report-vs { color: var(--pb-text-muted); font-size: 13px; }

/* Hold the last render at reduced opacity while refetching, so switching
   period doesn't flash a skeleton or jump the layout. */
.pb-report-body { transition: opacity 0.15s ease; }
.pb-report-body.is-loading { opacity: 0.45; }

/* ---------------- stat tiles ---------------- */

.pb-stat-grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
	gap: 14px;
	margin-bottom: 24px;
}

.pb-stat-tile {
	background: var(--pb-white);
	border: 1px solid var(--pb-border);
	border-radius: var(--pb-r-lg);
	box-shadow: var(--pb-shadow);
	padding: 16px 18px 14px;
}

.pb-stat-label {
	font-size: 12px;
	font-weight: 600;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--pb-text-muted);
	margin-bottom: 6px;
}

/* A stat tile whose label row also carries a period picker (see "Earned
   so far" on My Overview) — the label keeps its own baseline while the
   chip sits flush to the tile's right edge instead of wrapping below it. */
.pb-stat-label-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
	margin-bottom: 6px;
	/* The chip declares white-space: nowrap and refuses to shrink, and the
	   tile it sits in is as narrow as 140px on a phone. Without a wrap the
	   two simply overflowed the tile and printed on top of each other and
	   on the number below — which is what "this doesn't look right" was.
	   Wrapping puts the chip on its own line only when the label genuinely
	   leaves it no room, so the wide layout is unchanged. */
	flex-wrap: wrap;
}

.pb-stat-label-row .pb-stat-label {
	margin-bottom: 0;
	/* Lets the label be the item that gives way. Without it a flex item's
	   automatic minimum size is its longest word, so a label that cannot
	   shrink pushes the chip out of the tile instead of wrapping. */
	min-width: 0;
}

/* A smaller .pb-filter-chip for use inside a stat tile, where the
   full-size chip (built for a page toolbar) would crowd the number below
   it. Same shape and select styling, just scaled down. */
.pb-filter-chip-mini {
	padding: 3px 8px 3px 10px;
	flex: 0 0 auto;
}

.pb-filter-chip-mini .pb-chip-select {
	font-size: 12px;
	font-weight: 600;
	padding: 2px 18px 2px 2px;
	background-size: 10px 7px;
}

/* Proportional figures, not tabular — equal-width digits read loose at
   display sizes. Tabular is for the table columns further down. */
.pb-stat-value {
	font-size: 28px;
	font-weight: 700;
	letter-spacing: -0.03em;
	line-height: 1.15;
}

.pb-stat-foot {
	display: flex;
	align-items: baseline;
	gap: 8px;
	flex-wrap: wrap;
	margin-top: 6px;
}

.pb-stat-note { font-size: 12px; color: var(--pb-text-faint); }

.pb-delta { font-size: 12.5px; font-weight: 700; }
.pb-delta-up { color: var(--pb-success); }
.pb-delta-down { color: var(--pb-danger); }
.pb-delta-flat,
.pb-delta-none { color: var(--pb-text-faint); font-weight: 600; }

/* ---------------- panels ---------------- */

.pb-panel {
	background: var(--pb-white);
	border: 1px solid var(--pb-border);
	border-radius: var(--pb-r-lg);
	box-shadow: var(--pb-shadow);
	padding: 20px 22px 22px;
	margin-bottom: 22px;
}

.pb-panel-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	flex-wrap: wrap;
	margin-bottom: 12px;
}

.pb-panel-head .pb-panel-title { margin-bottom: 0; }

.pb-panel-note {
	margin: 12px 0 0;
	font-size: 12.5px;
	color: var(--pb-text-muted);
}

.pb-empty { padding: 26px 2px; color: var(--pb-text-muted); }

/* ---------------- legend ---------------- */

.pb-legend {
	display: flex;
	gap: 18px;
	flex-wrap: wrap;
	margin-bottom: 14px;
}

.pb-legend-item {
	display: inline-flex;
	align-items: center;
	gap: 7px;
	font-size: 12.5px;
	color: var(--pb-text-muted);
}

/* Identity rides the swatch beside the text, never the text colour. */
.pb-legend-key,
.pb-tip-key {
	width: 10px;
	height: 10px;
	border-radius: 3px;
	flex: 0 0 auto;
	display: inline-block;
}

.pb-tip-key-current { background: var(--pb-series-current); }
.pb-tip-key-prev { background: var(--pb-series-prev); }

/* ---------------- trend chart ---------------- */

.pb-chart { width: 100%; }

.pb-chart-plot {
	display: flex;
	align-items: stretch;
	height: 250px;
}

.pb-chart-y {
	position: relative;
	flex: 0 0 54px;
	width: 54px;
}

.pb-chart-ytick {
	position: absolute;
	right: 10px;
	transform: translateY(-50%);
	font-size: 11.5px;
	font-variant-numeric: tabular-nums;
	color: var(--pb-text-faint);
	white-space: nowrap;
}

.pb-chart-canvas {
	position: relative;
	flex: 1 1 auto;
	min-width: 0;
}

.pb-chart-svg {
	display: block;
	width: 100%;
	height: 100%;
	overflow: visible;
}

.pb-chart-grid {
	stroke: var(--pb-chart-grid);
	stroke-width: 1;
}

.pb-chart-axis {
	stroke: var(--pb-chart-axis);
	stroke-width: 1;
}

.pb-chart-bar-current { fill: var(--pb-series-current); }
.pb-chart-bar-prev { fill: var(--pb-series-prev); }

.pb-chart-peak {
	position: absolute;
	transform: translate(-50%, -140%);
	font-size: 12px;
	font-weight: 700;
	color: var(--pb-text);
	white-space: nowrap;
	pointer-events: none;
}

.pb-chart-peak.is-flipped { transform: translate(-90%, -140%); }

/* Hit target spans the full plot height and the whole band, so a short
   column is as easy to reach as a tall one. */
.pb-chart-hit {
	position: absolute;
	top: 0;
	bottom: 0;
	cursor: default;
}

.pb-chart-hit:hover { background: rgba(26, 26, 26, 0.035); }
.pb-chart-hit:focus-visible { outline: 2px solid var(--pb-series-current); outline-offset: -2px; }

.pb-chart-tip {
	position: absolute;
	top: 6px;
	transform: translateX(-50%);
	background: var(--pb-white);
	border: 1px solid var(--pb-border);
	border-radius: var(--pb-r-sm);
	box-shadow: var(--pb-shadow-lg);
	padding: 9px 12px;
	min-width: 158px;
	pointer-events: none;
	z-index: 3;
}

.pb-chart-tip.is-flipped { transform: translateX(-100%); }
.pb-chart-tip[hidden] { display: none; }

.pb-tip-title { font-weight: 700; font-size: 12.5px; margin-bottom: 5px; }

.pb-tip-row {
	display: flex;
	align-items: center;
	gap: 7px;
	font-size: 12.5px;
	color: var(--pb-text-muted);
	margin-top: 2px;
}

.pb-tip-name { flex: 1 1 auto; }
.pb-tip-row strong { color: var(--pb-text); font-variant-numeric: tabular-nums; }

.pb-tip-sub {
	margin-top: 5px;
	padding-top: 5px;
	border-top: 1px solid var(--pb-hairline);
	font-size: 11.5px;
	color: var(--pb-text-faint);
}

.pb-chart-x {
	display: flex;
	margin-top: 6px;
	height: 18px;
}

.pb-chart-xgutter { flex: 0 0 54px; width: 54px; }

.pb-chart-xtrack { position: relative; flex: 1 1 auto; min-width: 0; }

.pb-chart-xtick {
	position: absolute;
	top: 0;
	text-align: center;
	font-size: 11px;
	color: var(--pb-text-faint);
	white-space: nowrap;
	overflow: hidden;
}

/* ---------------- report tables ---------------- */

.pb-table-scroll { overflow-x: auto; }

.pb-report-table {
	width: 100%;
	border-collapse: collapse;
	font-size: 13.5px;
}

.pb-report-table th {
	text-align: left;
	font-size: 11.5px;
	font-weight: 700;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--pb-text-muted);
	padding: 0 12px 8px 0;
	border-bottom: 1px solid var(--pb-border);
	white-space: nowrap;
}

.pb-report-table td {
	padding: 11px 12px 11px 0;
	border-bottom: 1px solid var(--pb-hairline);
	vertical-align: middle;
}

.pb-report-table tr:last-child td { border-bottom: 0; }

.pb-report-table .pb-num {
	text-align: right;
	font-variant-numeric: tabular-nums;
	white-space: nowrap;
	padding-right: 0;
}

.pb-report-table .pb-bd-name { font-weight: 600; }
.pb-muted { color: var(--pb-text-muted); }
.pb-sm { font-size: 12px; }

.pb-bar-col { width: 46%; min-width: 190px; }

.pb-bar-wrap {
	display: flex;
	align-items: center;
	gap: 10px;
}

/* Single hue: the row's own label and value carry identity, so colour is
   free to mean magnitude only. 4px rounded data-end, square at the
   baseline it grows from. */
.pb-bar {
	height: 12px;
	max-height: 24px;
	background: var(--pb-series-current);
	border-radius: 0 4px 4px 0;
	min-width: 2px;
	flex: 0 0 auto;
}

.pb-bar-value {
	font-variant-numeric: tabular-nums;
	font-weight: 600;
	white-space: nowrap;
}

.pb-rooms-cell { line-height: 1.9; }

.pb-room-chip {
	display: inline-block;
	background: var(--pb-fill);
	border-radius: var(--pb-r-pill);
	padding: 2px 10px;
	font-size: 12px;
	font-weight: 600;
	margin-right: 5px;
	white-space: nowrap;
}

.pb-link { color: var(--pb-blue); text-decoration: none; font-weight: 600; }
.pb-link:hover { text-decoration: underline; }

@media (max-width: 782px) {
	.pb-chart-plot { height: 200px; }
	.pb-stat-value { font-size: 24px; }
}

/* ---------------- Settings: bordered tool blocks ----------------
   Shared shell for the self-contained tool panels that sit at the bottom
   of a Settings tab — the cache tools and the Square Import panel. Each
   also carries its own identifying class (.pb-cache-tools /
   .pb-import-tools) so a stylesheet or a test can address one without
   catching the other; only the shared shell is styled here. */
.pb-settings-tool {
	margin-top: 26px;
	padding-top: 22px;
	border-top: 1px solid var(--pb-border);
}

.pb-settings-tool h3 {
	font-size: 15px;
	font-weight: 700;
	margin: 0 0 6px;
}

.pb-cache-lede {
	margin: 0 0 14px;
	font-size: 13px;
	color: var(--pb-text-muted);
	max-width: 68ch;
}

.pb-cache-status:not(:empty) { margin-top: 12px; }

.pb-cache-ok {
	font-size: 13px;
	font-weight: 600;
	color: var(--pb-success);
}

.pb-cache-note {
	margin-top: 6px;
	font-size: 12.5px;
	color: var(--pb-text-muted);
	max-width: 68ch;
}

.pb-cache-note.is-warning {
	color: var(--pb-warning-text, #8a5a1b);
	background: var(--pb-warning-bg, #fdf3e3);
	border: 1px solid var(--pb-warning-border, #f0d9ad);
	border-radius: 6px;
	padding: 8px 10px;
	max-width: 60ch;
}

/* ---------------- Settings: booking page photos ---------------- */

.pb-settings-note {
	margin: 0 0 14px;
	font-size: 12.5px;
	color: var(--pb-text-muted);
	max-width: 70ch;
	line-height: 1.55;
}

.pb-hero-preview {
	margin-top: 8px;
	min-height: 20px;
}

.pb-hero-preview img {
	display: block;
	width: 220px;
	height: 96px;
	object-fit: cover;
	border-radius: var(--pb-r-sm);
	border: 1px solid var(--pb-border);
}

.pb-hero-preview-note {
	font-size: 12px;
	color: var(--pb-text-faint);
}

.pb-hero-preview-bad {
	display: inline-block;
	font-size: 12.5px;
	font-weight: 600;
	color: var(--pb-danger);
}

/* ---------------- Payroll: warnings, per-person table, My Sessions ---------------- */

/* The "these sessions aren't confirmed" notice above the payroll table.
   Amber rather than red: unconfirmed work is something to chase before
   paying, not something that has gone wrong. */
.pb-warn-banner {
	border: 1px solid #f0d9a8;
	background: #fdf6e6;
	color: #6b4e12;
	border-radius: var(--pb-r);
	padding: 14px 16px;
	font-size: 13.5px;
	line-height: 1.5;
	margin-bottom: 14px;
}

/* A quieter variant for a missing setting rather than money being held back. */
.pb-warn-soft {
	border-color: var(--pb-border);
	background: var(--pb-fill);
	color: var(--pb-text-muted);
}

.pb-unconfirmed-list {
	margin-top: 12px;
	border-top: 1px solid #ecdcb4;
}

.pb-unconfirmed-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	padding: 8px 0;
	border-bottom: 1px solid #ecdcb4;
	font-size: 12.5px;
	font-variant-numeric: tabular-nums;
}

.pb-unconfirmed-row:last-child { border-bottom: 0; }

.pb-report-table .pb-strong { font-weight: 700; }

.pb-panel-sub {
	font-size: 12.5px;
	color: var(--pb-text-muted);
	font-variant-numeric: tabular-nums;
}

/* My Sessions — one row per shoot, with its confirm button on the right. */
.pb-session-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	padding: 14px 18px;
	border-bottom: 1px solid var(--pb-hairline);
}

.pb-session-row:last-child { border-bottom: none; }

.pb-session-row.is-confirmed { background: var(--pb-fill); }

.pb-confirmed-tag {
	flex: 0 0 auto;
	font-size: 12.5px;
	font-weight: 700;
	color: var(--pb-success, #1b8a5a);
	white-space: nowrap;
}

/* Hourly rate + linked account, under the Availability grid. */
.pb-pay-table {
	width: 100%;
	border-collapse: collapse;
	font-size: 13.5px;
}

.pb-pay-table th {
	text-align: left;
	font-size: 11.5px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--pb-text-muted);
	padding: 0 12px 8px 0;
	border-bottom: 1px solid var(--pb-border);
}

.pb-pay-table td {
	padding: 10px 12px 10px 0;
	border-bottom: 1px solid var(--pb-hairline);
	vertical-align: middle;
}

.pb-pay-table tr:last-child td { border-bottom: 0; }

.pb-pay-table input[type="number"],
.pb-pay-table select {
	padding: 7px 10px;
	border: 1px solid var(--pb-border);
	border-radius: var(--pb-r-sm);
	font-family: inherit;
	font-size: 13.5px;
	background: var(--pb-white);
	color: var(--pb-text);
}

.pb-pay-table input[type="number"] { width: 96px; }
.pb-pay-table select { max-width: 260px; }

.pb-pay-saved {
	font-size: 12px;
	color: var(--pb-success, #1b8a5a);
	margin-left: 8px;
}

/* Each Square team member currently linked to one local roster row — see
   loadTeamMapping() in admin.js. A person can hold several at once, which
   is why these render as removable chips rather than one dropdown's value. */
.pb-square-links {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 6px;
}
.pb-square-link-chip {
	display: inline-flex;
	align-items: center;
	font-size: 12.5px;
	background: var(--pb-bg-soft, #f2f2f5);
	border: 1px solid var(--pb-border, #ddd);
	border-radius: 999px;
	padding: 3px 6px 3px 10px;
	white-space: nowrap;
}
.pb-square-link-remove {
	background: none;
	border: 0;
	cursor: pointer;
	color: var(--pb-text-muted, #888);
	font-size: 14px;
	line-height: 1;
	padding: 0 4px;
}
.pb-square-link-remove:hover {
	color: var(--pb-danger, #c0392b);
}

/* The buffer/placeholder-name checklist under the mapping table — see
   loadTeamMapping()'s "Buffer / placeholder names" section in admin.js. */
.pb-square-buffer-list {
	border: 1px solid var(--pb-border, #ddd);
	border-radius: 6px;
	padding: 4px 10px;
	max-width: 420px;
}

/* A button that reads as a link — used for small inline undo actions
   inside a line of text, where a real button would dominate the sentence. */
.pb-linklike {
	background: none;
	border: 0;
	padding: 0;
	font: inherit;
	color: var(--pb-purple);
	text-decoration: underline;
	text-underline-offset: 2px;
	cursor: pointer;
}

/* The same control standing on its own line under the customer search,
   rather than inside a sentence — so it gets the tap target a link in
   running text cannot have. */
#pb-admin-root .pb-add-customer-link {
	display: block;
	margin-top: 12px;
	padding: 8px 0;
	font-size: 14px;
	font-weight: 700;
	text-decoration: none;
}

/* =========================================================================
   TOUCH
   =========================================================================

   Everything below is gated on `.pb-touch`, a class put on <html> by
   markTouch() in staff.js and its twin in admin.js. It is NOT a
   `@media (pointer: coarse)` block, and that is deliberate: admin.js reads
   the same answer to decide whether to draw a bottom tab bar and whether
   to attach swipe handlers. A media query and a JS check that disagree —
   which they do on hybrids and on any laptop with a touchscreen — produce
   a page styled as one thing and behaving as another. One decision, made
   once, read by both.

   The mouse-and-keyboard layout is therefore completely untouched. None of
   these rules can match unless the class is present.
   ========================================================================= */

/* ---------------- foundation ---------------- */

.pb-touch #pb-admin-root,
.pb-touch .pb-staff-shell {
	/* The grey box iOS flashes over anything tappable. Every control in
	   here has its own :active state below, which is both faster and
	   actually shaped like the control. */
	-webkit-tap-highlight-color: transparent;
	-webkit-text-size-adjust: 100%;
}

/* `manipulation` drops the ~300ms the browser otherwise spends waiting to
   see whether a tap is the first half of a double-tap-to-zoom. It is the
   single biggest reason a web app feels laggy next to a native one.
   Pinch-zoom is untouched — that one is an accessibility feature, not a
   gesture to reclaim. */
.pb-touch #pb-admin-root button,
.pb-touch #pb-admin-root a,
.pb-touch #pb-admin-root select,
.pb-touch #pb-admin-root input,
.pb-touch #pb-admin-root label,
.pb-touch .pb-staff-shell button {
	touch-action: manipulation;
}

/* 44px is Apple's stated floor for a touch target and it is a floor, not
   a target. Applied as min-height rather than padding so nothing's
   existing alignment shifts. */
.pb-touch #pb-admin-root button,
.pb-touch #pb-admin-root .pb-btn,
.pb-touch #pb-admin-root .pb-icon-btn {
	min-height: 44px;
}

.pb-touch #pb-admin-root .pb-icon-btn {
	min-width: 44px;
}

/* 16px exactly, or mobile Safari zooms the whole page the moment a field
   takes focus and leaves it zoomed. */
.pb-touch #pb-admin-root input,
.pb-touch #pb-admin-root select,
.pb-touch #pb-admin-root textarea {
	font-size: 16px;
	min-height: 44px;
}

/*
 * Exception: a select inside a filter chip.
 *
 * The rule above is right for a field that stands on its own and wrong for
 * this one, because a .pb-filter-chip is a pill drawn *around* its select —
 * it supplies the border, the background and the padding. Forcing the
 * select to 44px tall and 16px inside a pill padded for a 14.5px control
 * inflated the whole chip: in the calendar toolbar it outgrew its grid
 * area, and in the "Earned so far" tile the mini variant grew taller than
 * the label row that holds it and printed over the label above it and the
 * number below. Both of those were reported as things that "don't look
 * right", and this is the single cause.
 *
 * The 44px touch target is not given up, it moves outward: the chip is
 * what a finger aims at, so the chip carries the minimum size and the
 * select fills it. The font stays 16px — a select at 14px still triggers
 * Safari's focus zoom, and dodging that is the whole point of the rule.
 */
.pb-touch #pb-admin-root .pb-filter-chip {
	min-height: 44px;
}

.pb-touch #pb-admin-root .pb-filter-chip .pb-chip-select {
	min-height: 0;
	padding-top: 0;
	padding-bottom: 0;
}

/* The in-tile variant is a secondary control sitting beside a big number
   rather than a toolbar control of its own, so it takes its target size
   from the tile's own tap area instead of standing 44px tall inside it. */
.pb-touch #pb-admin-root .pb-filter-chip-mini {
	min-height: 34px;
}

/* Chrome the app owns — tab labels, table headers, stat tiles — should not
   turn into a text selection when a swipe starts on it. Content the user
   might legitimately want to copy (names, phone numbers, notes) is
   deliberately left selectable. */
.pb-touch #pb-admin-root .pb-tabbar,
.pb-touch #pb-admin-root .pb-stat-tile,
.pb-touch #pb-admin-root .pb-panel-title,
.pb-touch #pb-admin-root button,
.pb-touch .pb-staff-shell .pb-staff-bar {
	-webkit-user-select: none;
	user-select: none;
}

/* A finger has no hover. Where a hover rule was doing the work of showing
   "this is pressable", a press state replaces it; a hover state left
   active after the finger lifts reads as stuck. */
.pb-touch #pb-admin-root .pb-btn:active,
.pb-touch #pb-admin-root .pb-icon-btn:active {
	transform: scale(0.97);
	opacity: 0.85;
}

.pb-touch #pb-admin-root .pb-appt-row.is-clickable:active,
.pb-touch #pb-admin-root .pb-customer-list-row:active,
.pb-touch #pb-admin-root .pb-cal-event:active {
	background-color: var(--pb-fill-hover);
}

/* Scrollable regions keep their momentum and stop handing an over-scroll
   to the page behind them (which on iOS is what makes a modal drag the
   whole document around underneath it). */
.pb-touch #pb-admin-root .pb-calendar-grid,
.pb-touch #pb-admin-root .pb-tabbar,
.pb-touch #pb-admin-root .pb-table-scroll,
.pb-touch #pb-admin-root .pb-drawer-body {
	-webkit-overflow-scrolling: touch;
	overscroll-behavior: contain;
}

.pb-touch .pb-staff-shell .pb-staff-signout {
	min-height: 44px;
	padding: 10px 18px;
	font-size: 14px;
}

/* ---------------- bottom tab bar ---------------- */

/*
 * Pinned to the bottom, where a thumb already is. This is the single
 * change that most makes the page read as an app rather than a website:
 * navigation stops being something at the top of a document and becomes
 * part of the device's chrome.
 */
.pb-touch #pb-admin-root .pb-tabbar-bottom {
	position: fixed;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 150000;
	gap: 0;
	margin: 0;
	padding: 5px 4px;
	/* The inset keeps the labels clear of the iPad's home indicator. It
	   resolves to 0 in a normal browser tab, so this costs nothing there —
	   it only pays out once the page is launched from the home screen (see
	   PB_Staff_Shortcode::app_head()). */
	padding-bottom: calc(5px + env(safe-area-inset-bottom));
	background: rgba(255, 255, 255, 0.92);
	-webkit-backdrop-filter: saturate(180%) blur(20px);
	backdrop-filter: saturate(180%) blur(20px);
	border-top: 1px solid var(--pb-border);
	border-bottom: 0;
	box-shadow: 0 -1px 0 rgba(0, 0, 0, 0.02);
}

.pb-touch #pb-admin-root .pb-tabbar-bottom a {
	flex: 1 1 0;
	min-width: 62px;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 3px;
	padding: 5px 2px 3px;
	font-size: 10.5px;
	font-weight: 600;
	line-height: 1.15;
	letter-spacing: -0.01em;
	text-align: center;
	color: var(--pb-text-faint);
	border-bottom: 0;
	margin-bottom: 0;
	/*
	 * One line, always. The labels are single words for exactly this
	 * reason (see PB_Staff_Shortcode::tabs()), but a longer one added
	 * later must make the bar no taller — it can be clipped, and the
	 * icon above it still identifies the tab. A wrapping label grows
	 * every screen's bottom margin and turns the bar into a paragraph.
	 */
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	max-width: 100%;
}

.pb-touch #pb-admin-root .pb-tabbar-bottom a.is-active {
	color: var(--pb-black);
}

.pb-touch #pb-admin-root .pb-tabbar-bottom a:active {
	opacity: 0.5;
}

.pb-touch #pb-admin-root .pb-tab-icon {
	display: block;
	width: 24px;
	height: 24px;
	flex: 0 0 auto;
}

.pb-touch #pb-admin-root .pb-tab-icon svg {
	display: block;
	width: 24px;
	height: 24px;
	fill: none;
	stroke: currentColor;
	stroke-width: 1.7;
	stroke-linecap: round;
	stroke-linejoin: round;
}

/* Whatever the last thing on the page is, it must not end up underneath
   the bar. */
.pb-touch #pb-admin-root {
	position: relative;
	padding-bottom: calc(76px + env(safe-area-inset-bottom));
}

/* ---------------- the drawer as a bottom sheet ---------------- */

.pb-touch .pb-drawer-overlay {
	align-items: flex-end;
	justify-content: center;
}

.pb-touch .pb-drawer {
	width: 100%;
	max-width: 100%;
	height: auto;
	max-height: 92vh;
	border-radius: 20px 20px 0 0;
	box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.18);
	/* The overshoot-free ease every sheet on iOS uses. */
	animation: pb-sheet-in 0.26s cubic-bezier(0.22, 1, 0.36, 1);
	padding-bottom: env(safe-area-inset-bottom);
	will-change: transform;
}

@keyframes pb-sheet-in {
	from { transform: translateY(100%); }
	to   { transform: translateY(0); }
}

/* While a finger is on it the sheet is positioned frame by frame from JS,
   so both the entry animation and the settle transition have to be out of
   the way or they fight the drag. */
.pb-touch .pb-drawer.is-dragging {
	animation: none;
	transition: none;
}

.pb-touch .pb-drawer.is-settling {
	animation: none;
	transition: transform 0.24s cubic-bezier(0.22, 1, 0.36, 1);
}

/* The grab handle. Drawn only on touch, because on a desktop it would be
   a bar that says "drag me" attached to something that does not drag. */
.pb-sheet-grab {
	display: none;
}

.pb-touch .pb-sheet-grab {
	display: block;
	flex: 0 0 auto;
	width: 40px;
	height: 5px;
	margin: 9px auto 1px;
	border-radius: var(--pb-r-pill);
	background: var(--pb-border);
}

/* ---------------- pull to refresh ---------------- */

.pb-ptr {
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	height: 52px;
	margin-top: -52px;
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	font-size: 12px;
	font-weight: 600;
	color: var(--pb-text-faint);
	pointer-events: none;
	/*
	 * Invisible until pulled. The negative margin parks this above the
	 * screen, but "above the screen" is only off-screen when the app root
	 * happens to start at the top of the document — on the staff page it
	 * starts below the signed-in bar, so at rest the words "Pull to
	 * refresh" sat on top of the person's own name. Found by screenshotting
	 * the real page at phone size; nothing in jsdom could have caught it,
	 * because jsdom has no layout.
	 */
	opacity: 0;
	transition: opacity 0.15s ease;
}

.pb-ptr-host.is-pulling .pb-ptr,
.pb-ptr.is-spinning {
	opacity: 1;
}

.pb-ptr-spinner {
	width: 18px;
	height: 18px;
	border-radius: 50%;
	border: 2px solid var(--pb-border);
	border-top-color: var(--pb-black);
}

.pb-ptr.is-armed .pb-ptr-spinner {
	border-top-color: var(--pb-purple);
}

.pb-ptr.is-spinning .pb-ptr-spinner {
	animation: pb-ptr-spin 0.7s linear infinite;
}

@keyframes pb-ptr-spin {
	to { transform: rotate(360deg); }
}

/* The whole screen slides down with the finger; the indicator above rides
   along because it is parked just off the top of the same element. */
.pb-ptr-host {
	transition: transform 0.24s cubic-bezier(0.22, 1, 0.36, 1);
}

.pb-ptr-host.is-pulling {
	transition: none;
}

/* ---------------- swipe a row for actions ---------------- */

.pb-swipe {
	position: relative;
	overflow: hidden;
}

.pb-swipe-actions {
	position: absolute;
	top: 0;
	right: 0;
	bottom: 0;
	display: flex;
	align-items: stretch;
}

.pb-swipe-action {
	display: flex;
	align-items: center;
	justify-content: center;
	min-width: 88px;
	padding: 0 14px;
	border: 0;
	font-size: 13px;
	font-weight: 700;
	color: #fff;
	background: var(--pb-text-muted);
	cursor: pointer;
}

.pb-swipe-action.is-danger {
	background: var(--pb-danger);
}

.pb-swipe-action:active {
	filter: brightness(0.92);
}

/* The row itself, riding on top of the actions. Opaque, or the buttons
   underneath show through it. */
.pb-swipe-surface {
	position: relative;
	background: var(--pb-white);
	will-change: transform;
}

.pb-swipe-surface.is-settling {
	transition: transform 0.22s cubic-bezier(0.22, 1, 0.36, 1);
}

/* ---------------- reduced motion ---------------- */

@media (prefers-reduced-motion: reduce) {
	.pb-touch .pb-drawer,
	.pb-touch .pb-drawer.is-settling,
	.pb-swipe-surface.is-settling,
	.pb-ptr-host {
		animation: none;
		transition: none;
	}
	.pb-ptr.is-spinning .pb-ptr-spinner {
		animation: none;
	}
	.pb-touch #pb-admin-root .pb-btn:active,
	.pb-touch #pb-admin-root .pb-icon-btn:active {
		transform: none;
	}
}

/* =========================================================================
   PHONE
   =========================================================================

   Gated on width, not on touch: an iPad is a touch device with room for a
   seven day calendar, an iPhone is a touch device with room for one day,
   and a narrow desktop window should get whichever layout actually fits.
   admin.js's isPhone() measures the same 640px so the two never disagree
   about which one is being drawn.
   ========================================================================= */

/* A general utility — .pb-hidden previously existed only scoped to a
   settings section, and the day strip needs the same thing. */
.pb-hidden {
	display: none !important;
}

/* ---------------- the Calendar's one-day view ---------------- */

/*
 * Same grid, two columns instead of eight. Every other rule about the
 * calendar — hour gutter, event positioning, colours, click targets —
 * applies unchanged, which is the whole reason this is a column count and
 * not a second calendar implementation.
 */
.pb-calendar-grid.is-day-view .pb-cal-head,
.pb-calendar-grid.is-day-view .pb-cal-body {
	grid-template-columns: 54px minmax(0, 1fr);
	/* The week grid holds a 760px floor under 1100px so seven columns
	   scroll sideways rather than crush. One column has nothing to
	   protect, and the floor would push the day's header off-screen. */
	min-width: 0;
}

.pb-cal-daystrip {
	display: flex;
	gap: 6px;
	margin: 0 0 12px;
}

.pb-cal-daypill {
	flex: 1 1 0;
	min-width: 0;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 2px;
	padding: 7px 2px 5px;
	border: 1px solid var(--pb-border);
	border-radius: var(--pb-r);
	background: var(--pb-white);
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
	transition: background-color 0.12s ease, border-color 0.12s ease;
}

.pb-cal-daypill-dow {
	font-size: 10px;
	font-weight: 700;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--pb-text-faint);
}

.pb-cal-daypill-num {
	font-size: 15px;
	font-weight: 700;
	color: var(--pb-text);
	font-variant-numeric: tabular-nums;
	line-height: 1.1;
}

/* A day with something booked carries a dot. It is what is left of the
   week-at-a-glance once six of the seven columns are gone. */
.pb-cal-daypill-dot {
	width: 5px;
	height: 5px;
	border-radius: 50%;
	background: transparent;
}

.pb-cal-daypill-dot.is-busy {
	background: var(--pb-purple);
}

.pb-cal-daypill.is-today .pb-cal-daypill-num {
	color: var(--pb-purple);
}

.pb-cal-daypill.is-active {
	background: var(--pb-black);
	border-color: var(--pb-black);
}

.pb-cal-daypill.is-active .pb-cal-daypill-dow,
.pb-cal-daypill.is-active .pb-cal-daypill-num {
	color: #fff;
}

.pb-cal-daypill.is-active .pb-cal-daypill-dot.is-busy {
	background: #fff;
}

.pb-cal-daypill:active {
	background: var(--pb-fill);
}

.pb-cal-daypill.is-active:active {
	background: var(--pb-black);
}

/* ---------------- everything else at phone width ---------------- */

@media (max-width: 640px) {
	/* 32px is a desktop headline. At 393px it eats a quarter of the
	   screen before any content is drawn. */
	#pb-admin-root .pb-page-title {
		font-size: 25px;
		letter-spacing: -0.02em;
		margin-bottom: 16px;
	}

	#pb-admin-root .pb-panel-title {
		font-size: 18px;
	}

	/* Two tiles per row instead of one: the auto-fit floor of 190px can
	   only fit a single column on a phone, which turns three numbers into
	   a screenful of scrolling. */
	#pb-admin-root .pb-stat-grid {
		grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
		gap: 10px;
		margin-bottom: 18px;
	}

	/* A tile that carries a control as well as a number takes the whole
	   row. At half width its label and its picker have ~140px to share,
	   which is what put them on top of each other; across the full width
	   they sit side by side the way they do on a desktop. */
	#pb-admin-root .pb-stat-tile-wide {
		grid-column: 1 / -1;
	}

	#pb-admin-root .pb-stat-value {
		font-size: 22px;
	}

	/* The toolbar already wraps; this stops the pieces fighting over the
	   first line and pushing Create off the right edge. */
	#pb-admin-root .pb-calendar-toolbar {
		gap: 8px;
		margin-bottom: 14px;
	}

	#pb-admin-root .pb-calendar-toolbar .pb-date-picker-wrap {
		flex: 1 1 140px;
	}

	#pb-admin-root .pb-toolbar-create {
		flex: 0 0 auto;
	}

	/* The grid has less chrome above it here — the page title and toolbar
	   are both shorter — so it can afford more hours on screen. */
	#pb-admin-root .pb-cal-body {
		max-height: clamp(360px, calc(100vh - 330px), 900px);
	}

	#pb-admin-root .pb-calendar-legend {
		flex-wrap: wrap;
		gap: 6px 14px;
		font-size: 12px;
	}

	/* A phone cannot show a six column table however hard it is squeezed.
	   Scrolling one sideways is honest; reflowing it into unreadable
	   slivers is not. */
	#pb-admin-root .pb-table-scroll {
		overflow-x: auto;
		-webkit-overflow-scrolling: touch;
	}

	#pb-admin-root .pb-card {
		border-radius: var(--pb-r);
	}
}

/* =========================================================================
   TOUCH DESIGN
   =========================================================================
   The visual layer on top of the touch behaviours above: the brand's own
   colours (lifted from the logo — magenta into deep purple), lavender and
   pastel blue as the tints, a soft shadow language, and motion that makes
   a screen assemble rather than appear. All of it gated on .pb-touch, so
   the wp-admin desktop stays exactly as it was.
   ========================================================================= */

.pb-touch {
	--pb-brand-a: #b42f71;
	--pb-brand-b: #5e369c;
	--pb-brand: linear-gradient(135deg, #b42f71 0%, #8a3590 52%, #5e369c 100%);
	--pb-plum: #4b2a7a;
	--pb-plum-muted: #7d6f99;
	--pb-lilac: #efe6fb;
	--pb-lilac-deep: #dfd0f5;
	--pb-sky: #e4edff;
	--pb-sky-deep: #cbdcff;
	--pb-shadow-soft: 0 2px 6px rgba(75, 42, 122, 0.05), 0 12px 32px rgba(75, 42, 122, 0.08);
	--pb-ease-out: cubic-bezier(0.22, 1, 0.36, 1);
}

/* ---------------- rhythm ---------------- */

/* Titles in plum with a short gradient rule beneath — the one place the
   brand gradient appears on every screen, so the screens read as a set. */
.pb-touch #pb-admin-root .pb-page-title {
	position: relative;
	color: var(--pb-plum);
	padding-bottom: 14px;
	margin-bottom: 22px;
}

.pb-touch #pb-admin-root .pb-page-title::after {
	content: '';
	position: absolute;
	left: 0;
	bottom: 0;
	width: 38px;
	height: 4px;
	border-radius: var(--pb-r-pill);
	background: var(--pb-brand);
}

.pb-touch #pb-admin-root .pb-panel-title {
	color: var(--pb-plum);
	margin-bottom: 10px;
}

.pb-touch #pb-admin-root .pb-card {
	border-color: var(--pb-lilac-deep);
	border-radius: 20px;
	box-shadow: var(--pb-shadow-soft);
	margin-bottom: 16px;
}

/* ---------------- controls ---------------- */

.pb-touch #pb-admin-root .pb-btn-primary {
	background: var(--pb-brand);
	border: 0;
	color: #fff;
	box-shadow: 0 6px 16px rgba(94, 54, 156, 0.26);
}

.pb-touch #pb-admin-root .pb-btn-primary:active {
	box-shadow: 0 2px 6px rgba(94, 54, 156, 0.26);
}

.pb-touch #pb-admin-root .pb-btn-secondary,
.pb-touch #pb-admin-root .pb-icon-btn,
.pb-touch #pb-admin-root .pb-date-range,
.pb-touch #pb-admin-root .pb-filter-chip,
.pb-touch #pb-admin-root .pb-filters-btn {
	background: #fff;
	border-color: var(--pb-lilac-deep);
	color: var(--pb-plum);
}

/* Except once it is actually filtering something — then it is the one
   solid control in the toolbar, which is the point of it. */
.pb-touch #pb-admin-root .pb-filters-btn.is-filtered {
	background: var(--pb-plum);
	border-color: var(--pb-plum);
	color: #fff;
}

.pb-touch #pb-admin-root .pb-btn-secondary:active,
.pb-touch #pb-admin-root .pb-icon-btn:active {
	background: var(--pb-lilac);
}

.pb-touch #pb-admin-root input,
.pb-touch #pb-admin-root select,
.pb-touch #pb-admin-root textarea {
	border-color: var(--pb-lilac-deep);
	border-radius: 14px;
}

.pb-touch #pb-admin-root input:focus,
.pb-touch #pb-admin-root select:focus,
.pb-touch #pb-admin-root textarea:focus {
	border-color: var(--pb-brand-b);
	box-shadow: 0 0 0 4px rgba(94, 54, 156, 0.12);
	outline: none;
}

/* ---------------- stat tiles ---------------- */

/* Lavender and pastel blue, alternating, no border: numbers on colour read
   as at-a-glance rather than as a table cell. */
.pb-touch #pb-admin-root .pb-stat-tile {
	border: 0;
	border-radius: 18px;
	background: var(--pb-lilac);
	box-shadow: none;
}

.pb-touch #pb-admin-root .pb-stat-tile:nth-child(even) {
	background: var(--pb-sky);
}

.pb-touch #pb-admin-root .pb-stat-value {
	color: var(--pb-plum);
	letter-spacing: -0.02em;
}

.pb-touch #pb-admin-root .pb-stat-label {
	color: var(--pb-plum-muted);
	letter-spacing: 0.07em;
}

/* ---------------- lists ---------------- */

.pb-touch #pb-admin-root .pb-day-heading {
	color: var(--pb-plum-muted);
	letter-spacing: 0.08em;
}

.pb-touch #pb-admin-root .pb-appt-time {
	color: var(--pb-brand-b);
	font-weight: 700;
}

.pb-touch #pb-admin-root .pb-appt-row.is-clickable {
	border-radius: 14px;
	transition: background-color 0.12s ease;
}

.pb-touch #pb-admin-root .pb-appt-row.is-clickable:active {
	background-color: var(--pb-lilac);
}

.pb-touch #pb-admin-root .pb-tag-pay-in-studio {
	background: var(--pb-sky);
	color: var(--pb-plum);
	border: 0;
}

/* Notifications as pastel chips instead of hairline-separated lines. */
.pb-touch #pb-admin-root .pb-notification {
	margin-bottom: 8px;
	padding: 12px 40px 12px 14px;
	border: 0;
	border-radius: 14px;
	background: var(--pb-sky);
	color: var(--pb-plum);
}

.pb-touch #pb-admin-root .pb-notification:nth-child(even) {
	background: var(--pb-lilac);
}

.pb-touch #pb-admin-root .pb-notif-time {
	color: var(--pb-plum-muted);
}

.pb-touch #pb-admin-root .pb-notif-dismiss {
	color: var(--pb-plum-muted);
}

.pb-touch #pb-admin-root .pb-empty-note {
	color: var(--pb-plum-muted);
	background: var(--pb-lilac);
	border-radius: 14px;
	padding: 14px 16px;
}

/* ---------------- bottom tab bar ---------------- */

.pb-touch #pb-admin-root .pb-tabbar-bottom {
	border-top-color: var(--pb-lilac-deep);
	background: rgba(255, 255, 255, 0.94);
}

.pb-touch #pb-admin-root .pb-tabbar-bottom a {
	color: var(--pb-plum-muted);
}

/* The active icon sits in a lilac pill. The pill is what moves the eye
   to "you are here" — colour alone is easy to miss at 24px. */
.pb-touch #pb-admin-root .pb-tabbar-bottom .pb-tab-icon {
	padding: 4px 14px;
	width: auto;
	height: auto;
	border-radius: var(--pb-r-pill);
	transition: background-color 0.2s ease, transform 0.2s var(--pb-ease-out);
}

.pb-touch #pb-admin-root .pb-tabbar-bottom a.is-active {
	color: var(--pb-brand-b);
}

.pb-touch #pb-admin-root .pb-tabbar-bottom a.is-active .pb-tab-icon {
	background: var(--pb-lilac);
}

.pb-touch #pb-admin-root .pb-tabbar-bottom a:active {
	opacity: 1;
}

.pb-touch #pb-admin-root .pb-tabbar-bottom a:active .pb-tab-icon {
	transform: scale(0.88);
}

/* ---------------- calendar ---------------- */

.pb-touch .pb-cal-daypill {
	border-color: var(--pb-lilac-deep);
}

.pb-touch .pb-cal-daypill-num {
	color: var(--pb-plum);
}

.pb-touch .pb-cal-daypill.is-today .pb-cal-daypill-num {
	color: var(--pb-brand-b);
}

.pb-touch .pb-cal-daypill.is-active,
.pb-touch .pb-cal-daypill.is-active:active {
	background: var(--pb-brand);
	border-color: transparent;
	box-shadow: 0 6px 14px rgba(94, 54, 156, 0.26);
}

.pb-touch .pb-cal-daypill.is-active .pb-cal-daypill-num {
	color: #fff;
}

.pb-touch #pb-admin-root .pb-calendar-grid {
	border-color: var(--pb-lilac-deep);
	box-shadow: var(--pb-shadow-soft);
	border-radius: 20px;
}

.pb-touch #pb-admin-root .pb-calendar-day-header.is-today {
	color: var(--pb-brand-b);
}

.pb-touch #pb-admin-root .pb-calendar-day-col.is-today {
	background: rgba(228, 237, 255, 0.4);
}

.pb-touch #pb-admin-root .pb-cal-event {
	border-radius: 10px;
}

/* ---------------- sheet, refresh, swipe ---------------- */

.pb-touch .pb-drawer-overlay {
	background: rgba(75, 42, 122, 0.28);
	-webkit-backdrop-filter: blur(6px);
	backdrop-filter: blur(6px);
}

.pb-touch .pb-drawer {
	border-radius: 26px 26px 0 0;
}

.pb-touch .pb-sheet-grab {
	background: var(--pb-lilac-deep);
}

.pb-touch .pb-ptr-spinner {
	border-color: var(--pb-lilac-deep);
	border-top-color: var(--pb-brand-b);
}

.pb-touch .pb-ptr {
	color: var(--pb-plum-muted);
}

.pb-touch .pb-swipe-action.is-danger {
	background: linear-gradient(135deg, #d43d5a 0%, #b3261e 100%);
}

/* ---------------- motion ---------------- */

/*
 * A screen rises into place, top to bottom, one beat apart. Set on the
 * root by renderView() each time a screen is drawn; the tab bar and the
 * refresh indicator sit out because they are chrome, not content.
 */
@keyframes pb-rise {
	from { opacity: 0; transform: translateY(12px); }
	to   { opacity: 1; transform: none; }
}

.pb-touch #pb-admin-root.pb-enter > *:not(.pb-tabbar):not(.pb-ptr) {
	animation: pb-rise 0.4s var(--pb-ease-out) both;
}

.pb-touch #pb-admin-root.pb-enter > *:nth-child(2) { animation-delay: 0.03s; }
.pb-touch #pb-admin-root.pb-enter > *:nth-child(3) { animation-delay: 0.07s; }
.pb-touch #pb-admin-root.pb-enter > *:nth-child(4) { animation-delay: 0.11s; }
.pb-touch #pb-admin-root.pb-enter > *:nth-child(5) { animation-delay: 0.15s; }
.pb-touch #pb-admin-root.pb-enter > *:nth-child(6) { animation-delay: 0.19s; }
.pb-touch #pb-admin-root.pb-enter > *:nth-child(n+7) { animation-delay: 0.23s; }

@media (prefers-reduced-motion: reduce) {
	.pb-touch #pb-admin-root.pb-enter > * {
		animation: none;
	}
}

/* ---------------- refinements from the phone screenshots ---------------- */

/* Side gutters. A full-width theme template lands the app flush against
   the glass; 16px is the least a phone layout gets away with. Applied on
   touch only so wp-admin's own page padding is not doubled. */
.pb-touch .pb-staff-shell .pb-staff-app {
	padding: 0 16px;
}

/* The arrow buttons had lost their frame and read as loose glyphs. */
.pb-touch #pb-admin-root .pb-icon-btn {
	border: 1px solid var(--pb-lilac-deep);
	border-radius: var(--pb-r-pill);
}

/* The day-group heading: a lilac band instead of a grey one, and tighter
   rows underneath so a day's list fits on a screen. */
.pb-touch #pb-admin-root .pb-day-heading {
	background: var(--pb-lilac);
	color: var(--pb-plum);
}

.pb-phone #pb-admin-root .pb-appt-row {
	padding-top: 14px;
	padding-bottom: 14px;
	gap: 4px;
}

.pb-phone #pb-admin-root .pb-page-title {
	padding-bottom: 10px;
	margin-bottom: 16px;
}

/* The one-day grid already names its day twice above itself — in the
   date pill and in the strip. A third copy inside the grid is noise, and
   the header row's height is worth more as another hour of appointments. */
.pb-calendar-grid.is-day-view .pb-cal-head {
	display: none;
}

/* Phone toolbar as an explicit two-row grid: arrows around the date with
   Today at the end, then the team filter stretched beside Create. Flex
   was tried first and either gave the filter a whole row to itself or,
   with min-width:0, let everything shrink onto one line and overlap. A
   grid says where each piece goes and nothing has to be negotiated. */
@media (max-width: 640px) {
	#pb-admin-root .pb-calendar-toolbar {
		display: grid;
		grid-template-columns: 44px minmax(0, 1fr) 44px auto;
		/* One row now. Filters and Create moved up into the heading (see
		   renderCalendar()), so all this has left to place is the date
		   controls. */
		grid-template-areas: "prev date next today";
		gap: 10px;
		align-items: center;
	}

	#pb-admin-root .pb-calendar-toolbar > .pb-icon-btn:first-child  { grid-area: prev; }
	#pb-admin-root .pb-calendar-toolbar > .pb-date-picker-wrap       { grid-area: date; min-width: 0; }
	#pb-admin-root .pb-calendar-toolbar > .pb-icon-btn:nth-child(3)  { grid-area: next; }
	#pb-admin-root .pb-calendar-toolbar > .pb-btn-secondary          { grid-area: today; }

	/* Anchored to a button at the right end of the heading row, so it
	   opens rightwards-aligned; capping its width keeps it inside a 390px
	   screen whatever the longest team member's name happens to be. */
	#pb-admin-root .pb-filters-popover {
		max-width: calc(100vw - 48px);
	}

	/* The heading drops a size so a 32px title, a filters button and a
	   create button share one line on a phone without the title wrapping
	   under them. */
	#pb-admin-root .pb-page-head .pb-page-title {
		font-size: 24px;
	}

	.pb-page-head {
		margin-bottom: 16px;
	}

	#pb-admin-root .pb-calendar-toolbar .pb-date-range {
		width: 100%;
		justify-content: center;
	}

	#pb-admin-root .pb-calendar-toolbar .pb-icon-btn {
		width: 44px;
		padding: 0;
		justify-self: center;
	}
}

/* On a tablet the bar spans 800px+ and five icons drift a hand's width
   apart. Cap each tab so the group clusters in the centre, the way the
   system's own tab bars do on iPad. */
.pb-touch #pb-admin-root .pb-tabbar-bottom {
	justify-content: center;
}

.pb-touch #pb-admin-root .pb-tabbar-bottom a {
	max-width: 150px;
}

/* ================= CALENDAR ZOOM ================= */

/* One control, shared by phone/iPad/desktop — see getCalZoom() in admin.js.
   Right-aligned under the toolbar rather than crammed into it, so it never
   has to fight the toolbar's own (fairly tight) phone grid layout above. */
/* ---------------- calendar filters button + popover ----------------

   Replaces a "Team member" select chip in the toolbar and a zoom row of
   its own below it. Both are settings rather than actions — chosen
   occasionally, and zoom is remembered between visits — so they cost the
   top of the screen more than they were worth sitting out in the open.
   See renderFilters() in admin.js. */

.pb-filters-wrap {
	position: relative;
	display: inline-block;
}

/* Icon-only at rest. Square and 44px like the Create button beside it,
   so the two read as a pair of controls rather than a pill and a box —
   it stretches into a pill only when it has a name to show. */
.pb-filters-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	width: 44px;
	height: 44px;
	padding: 0;
	border: 1px solid var(--pb-border);
	border-radius: 14px;
	background: var(--pb-white);
	font-family: inherit;
	font-size: 14.5px;
	font-weight: 700;
	letter-spacing: -0.01em;
	color: var(--pb-text);
	white-space: nowrap;
	cursor: pointer;
}

/* Filtered: the name is back, so this needs to be a pill again. */
.pb-filters-btn.is-filtered {
	width: auto;
	max-width: 46vw;
	padding: 0 16px;
	border-radius: var(--pb-r-pill);
}

.pb-filters-btn:hover {
	background: var(--pb-fill);
}

/* Filtered to one person: the button says whose calendar this is and
   carries the weight to match, so a narrowed calendar is never mistaken
   for a quiet one. */
.pb-filters-btn.is-filtered {
	border-color: var(--pb-text);
	background: var(--pb-text);
	color: var(--pb-white);
}

/* The popover is anchored to a 44px button now, not a wide pill, so it
   would hang off the right edge of a phone if it opened leftwards from
   a control sitting at the end of the heading row. */
.pb-page-head-actions .pb-filters-popover {
	left: auto;
	right: 0;
}

.pb-filters-icon {
	display: inline-flex;
	flex: 0 0 auto;
}

.pb-filters-icon svg {
	width: 17px;
	height: 17px;
	display: block;
}

.pb-filters-label {
	overflow: hidden;
	text-overflow: ellipsis;
}

.pb-filters-popover {
	position: absolute;
	top: calc(100% + 8px);
	left: 0;
	z-index: 60;
	width: 252px;
	padding: 10px;
	background: var(--pb-white);
	border: 1px solid var(--pb-border);
	border-radius: var(--pb-r-lg);
	box-shadow: var(--pb-shadow-lg);
}

.pb-filters-heading {
	padding: 6px 8px;
	font-size: 11.5px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	color: var(--pb-text-muted);
}

.pb-filters-list {
	display: flex;
	flex-direction: column;
	margin-bottom: 4px;
}

.pb-filters-option {
	display: flex;
	align-items: center;
	gap: 9px;
	width: 100%;
	padding: 9px 10px;
	border: none;
	border-radius: var(--pb-r-sm);
	background: none;
	font-family: inherit;
	font-size: 14px;
	font-weight: 600;
	color: var(--pb-text);
	text-align: left;
	cursor: pointer;
}

.pb-filters-option:hover {
	background: var(--pb-fill);
}

.pb-filters-option.is-active {
	background: var(--pb-fill);
}

/* The same colour this person's blocks are drawn in on the grid, so the
   list reads as the legend it is standing in for. */
.pb-filters-option-dot {
	flex: 0 0 auto;
	width: 9px;
	height: 9px;
	border-radius: 50%;
}

.pb-filters-check {
	margin-left: auto;
	font-weight: 700;
	color: var(--pb-purple);
}

/* Inside the popover now, so it centres in a 252px panel rather than
   hanging off the right edge of a row of its own. */
.pb-cal-zoombar {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 6px;
	margin: 2px 0 2px;
}

.pb-cal-zoom-btn {
	width: 30px;
	height: 30px;
	font-size: 16px;
	font-weight: 700;
}

.pb-cal-zoom-btn:disabled {
	opacity: 0.4;
	cursor: default;
}

.pb-cal-zoom-pct {
	min-width: 42px;
	text-align: center;
	font-size: 12.5px;
	font-weight: 600;
	color: var(--pb-text-muted);
}

/* ================= ACCESS CODES — generate form ================= */

.pb-codes-genform {
	background: #fff;
	border: 1px solid var(--pb-border);
	border-radius: 12px;
	padding: 16px;
	margin: 10px 0 16px;
}

.pb-form-actions {
	display: flex;
	gap: 8px;
	margin-top: 12px;
}

/* ================= CHECKOUT (Create / Checkout mode toggle) ================= */

.pb-mode-toggle {
	display: inline-flex;
	background: #f0eef5;
	border-radius: 999px;
	padding: 4px;
	margin-bottom: 18px;
	gap: 2px;
}

.pb-mode-toggle button,
.pb-mode-toggle a {
	border: none;
	background: transparent;
	padding: 9px 18px;
	border-radius: 999px;
	font-size: 14px;
	font-weight: 600;
	color: var(--pb-text-muted);
	cursor: pointer;
	text-decoration: none;
	display: inline-block;
}

.pb-mode-toggle button.is-active,
.pb-mode-toggle a.is-active {
	background: #fff;
	color: var(--pb-text);
	box-shadow: 0 1px 3px rgba(0,0,0,0.12);
}

/* Customers / Team / Access Codes sub-nav — same look as the Create /
   Checkout mode toggle, just anchors instead of buttons (each is a real
   route, so it needs to be a link — middle-click/open-in-new-tab still
   has to work). */
.pb-subnav {
	margin-bottom: 18px;
}

/* ---------------- Quick Charge: the keypad ----------------

   The amount is the screen. Everything else on this flow is a second step
   reached only once there is a number to attach it to — see
   renderChargeMode() in admin.js for why that order matters. */

.pb-charge-grid {
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
	gap: 26px;
	align-items: start;
	max-width: 880px;
}

/* The pad column is capped rather than stretched: a keypad as wide as a
   desktop content column has keys a foot apart. */
.pb-charge-pad,
.pb-charge-review-step {
	max-width: 420px;
}

.pb-charge-amount {
	font-size: 52px;
	font-weight: 700;
	letter-spacing: -0.035em;
	line-height: 1.1;
	/* Equal-width digits, so the number does not jitter sideways as it is
	   typed — the one place in this app where tabular figures are clearly
	   right, because the value changes under a fixed gaze. */
	font-variant-numeric: tabular-nums;
	padding: 18px 4px 6px;
	text-align: center;
}

/* On the review step the amount is also the way back to the pad. Styled
   as text rather than as a button: it is the heading of that screen, and
   a 52px pill would dominate everything under it. */
.pb-charge-amount-btn {
	display: block;
	width: 100%;
	border: none;
	background: none;
	font-family: inherit;
	color: var(--pb-text);
	cursor: pointer;
	border-radius: var(--pb-r);
}

.pb-charge-amount-btn:hover {
	background: var(--pb-fill);
}

.pb-charge-banked {
	display: flex;
	align-items: baseline;
	justify-content: center;
	gap: 12px;
	font-size: 13px;
	font-weight: 600;
	color: var(--pb-text-muted);
	margin-bottom: 12px;
}

.pb-charge-banked-total {
	color: var(--pb-text);
	font-weight: 700;
}

.pb-charge-keypad {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 10px;
	margin-top: 10px;
}

.pb-charge-key {
	display: flex;
	align-items: center;
	justify-content: center;
	/* Comfortably past the 44px minimum: this is the control that gets
	   used at a counter, in a hurry, with a customer waiting. */
	min-height: 62px;
	border: 0;
	border-radius: 16px;
	background: var(--pb-fill);
	font-family: inherit;
	font-size: 25px;
	font-weight: 500;
	font-variant-numeric: tabular-nums;
	color: var(--pb-text);
	cursor: pointer;
	user-select: none;
	-webkit-user-select: none;
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
	transition: background-color 0.08s ease, transform 0.08s ease;
}

/* :active, not :hover — a finger has no hover, and a key left highlighted
   after the finger lifts reads as stuck. */
.pb-charge-key:active {
	background: var(--pb-fill-hover);
	transform: scale(0.96);
}

.pb-charge-key-soft {
	font-size: 17px;
	font-weight: 700;
	color: var(--pb-text-muted);
	background: transparent;
}

.pb-charge-key-soft:active {
	background: var(--pb-fill);
}

.pb-charge-key svg {
	width: 25px;
	height: 25px;
	display: block;
}

.pb-charge-padactions {
	display: grid;
	grid-template-columns: auto minmax(0, 1fr);
	gap: 10px;
	margin-top: 14px;
}

#pb-admin-root .pb-charge-review {
	width: 100%;
}

#pb-admin-root .pb-charge-padactions .pb-btn:disabled {
	opacity: 0.45;
	cursor: default;
}

.pb-charge-submitbar {
	display: grid;
	grid-template-columns: auto minmax(0, 1fr);
	gap: 10px;
	margin-top: 4px;
}

.pb-charge-lineitem {
	display: grid;
	grid-template-columns: 1fr auto 32px;
	gap: 8px;
	align-items: center;
	margin-bottom: 8px;
}

.pb-charge-lineamount {
	font-weight: 700;
	font-variant-numeric: tabular-nums;
	white-space: nowrap;
}

.pb-charge-lineitem input[type="number"] {
	text-align: right;
}

/* One column on a phone: a keypad and a list of recent charges side by
   side is a desktop layout, and the pad is the only thing that matters
   with a customer standing there. */
@media (max-width: 860px) {
	.pb-charge-grid {
		grid-template-columns: minmax(0, 1fr);
	}

	.pb-charge-pad,
	.pb-charge-review-step {
		max-width: none;
	}
}

.pb-charge-remove {
	border: none;
	background: none;
	color: var(--pb-text-muted);
	font-size: 18px;
	cursor: pointer;
	height: 38px;
}

.pb-charge-total {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	font-size: 22px;
	font-weight: 700;
	padding: 14px 0;
	border-top: 1px solid var(--pb-hairline);
	margin-top: 6px;
}

.pb-charge-success {
	background: #eafaf0;
	border: 1px solid #b7e6c8;
	color: #1c6b3c;
	border-radius: 10px;
	padding: 14px 16px;
	margin-bottom: 16px;
}

.pb-recent-sales .pb-line-item {
	border-bottom: 1px solid var(--pb-hairline);
}

/* ================= DRAWER — bottom action bar ================= */

/* Replaces the old overflow ("···") menu's Cancel/No-show entries with
   full-width buttons at the foot of the drawer — closer to how Square's own
   appointment sheet reads: Edit stays a single top-right action, everything
   destructive or forward-moving lives in a row you don't have to open a
   menu to find. */
.pb-drawer-actions {
	display: flex;
	flex-direction: column;
	gap: 8px;
	padding: 16px;
	border-top: 1px solid var(--pb-hairline);
}

.pb-drawer-actions .pb-btn {
	width: 100%;
	justify-content: center;
	padding-top: 12px;
	padding-bottom: 12px;
	border-radius: 999px;
}

.pb-drawer-actions .pb-btn-danger-text {
	background: none;
	border: 1px solid var(--pb-hairline);
	color: #c0392b;
}

.pb-drawer-note-box {
	background: #faf9fc;
	border: 1px solid var(--pb-hairline);
	border-radius: 10px;
	padding: 12px 14px;
	color: var(--pb-text-muted);
	font-size: 13.5px;
	margin-top: 6px;
}

.pb-drawer-note-box.has-note {
	color: var(--pb-text);
}

.pb-drawer-location {
	font-size: 13.5px;
	color: var(--pb-text);
	margin-top: 2px;
}

/* "Out of studio" on the availability grid — see PB_Lockouts. Red rather
   than the grey used for "Studio hours", because this one is temporary
   and actionable: it says a person the grid shows as working is, right
   now, not. */
.pb-avail-lockout {
	display: block;
	font-size: 11px;
	font-weight: 600;
	color: #C0392B;
	margin-top: 2px;
}
