/* ---------------------------------------------------------------------------
   Queantic — app-screens.css

   Per-screen styles for the signed-in app. Lifted VERBATIM out of <style>
   blocks inside the templates, so computed style is unchanged — only the
   location moved.

   Why its own file and not brand.css: every rule here belongs to exactly one
   route. brand.css is downloaded by every marketing visitor too, and none of
   them will ever open Mail. This sheet is enqueued by qntc_enqueue_app_styles()
   which runs only on app screens.
   --------------------------------------------------------------------------- */

/* ---- Settings (/app/settings) -------------------------------------------- */
/* Icon alignment fix */
        .sidebar-btn {
            padding: 0.625rem 0.75rem;
            text-align: left;
            border-radius: 0.5rem;
            display: flex;
            align-items: center;
            width: 100%;
            background: none;
            color: var(--q-text-2);
            border: none;
            cursor: pointer;
            transition: all 0.15s ease;
            font-size: 0.9375rem;
        }

        .sidebar-btn i {
            width: 18px;
            display: inline-flex;
            justify-content: center;
            align-items: center;
            flex-shrink: 0;
        }

        .sidebar-btn:hover {
            background-color: var(--q-bg-2);
            color: var(--q-ink);
        }

        .sidebar-btn.active {
            background-color: var(--q-lavender, #ece7fd);
            color: var(--q-primary, #5817ea);
            font-weight: 500;
        }

        .input {
            width: 100%;
            padding: 0.625rem 0.875rem;
            border: 1px solid var(--q-line);
            border-radius: 0.5rem;
            font-size: 0.9375rem;
            transition: border-color 0.15s, box-shadow 0.15s;
        }

        .input:focus {
            outline: none;
            border-color: var(--q-primary, #5817ea);
            box-shadow: 0 0 0 3px rgba(88, 23, 234, 0.12);
        }

        .input::placeholder {
            color: var(--q-text-3);
        }

        .btn-primary,
        .btn-outline,
        .btn-warning,
        .btn-danger {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            padding: 0.625rem 1.25rem;
            border-radius: 0.5rem;
            font-size: 0.9375rem;
            font-weight: 500;
            cursor: pointer;
            transition: all 0.15s ease;
            border: none;
        }

        .btn-primary {
            background-color: var(--q-primary, #5817ea);
            color: white;
        }

        .btn-primary:hover {
            background-color: var(--q-primary-hover, #240b63);
            transform: translateY(-1px);
            box-shadow: 0 4px 12px rgba(88, 23, 234, 0.3);
        }

        .btn-outline {
            border: 1px solid var(--q-line);
            background-color: white;
            color: var(--q-text-2);
        }

        .btn-outline:hover {
            background-color: var(--q-bg-2);
            border-color: var(--q-text-3);
        }

        .btn-warning {
            background-color: var(--q-primary);
            color: white;
        }

        .btn-warning:hover {
            background-color: var(--q-primary);
            transform: translateY(-1px);
            box-shadow: 0 4px 12px rgba(245, 158, 11, 0.3);
        }

        .btn-danger {
            background-color: var(--q-ink);
            color: white;
        }

        .btn-danger:hover {
            background-color: var(--q-ink);
            transform: translateY(-1px);
            box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);
        }

        .tab-section {
            display: none;
        }

        .tab-section:not(.hidden) {
            display: block;
        }

        /* Mobile - side-by-side split with vertical divider */
        @media (max-width: 768px) {
            .flex.min-h-screen {
                flex-direction: row; /* Side by side even on mobile */
            }

            aside.w-56 {
                width: 40%; /* Sidebar takes 40% */
                min-width: 140px;
                border-right: 1px solid var(--q-line); /* Vertical divider line */
            }

            aside .p-6 {
                padding: 1rem; /* Reduce header padding on mobile */
            }

            aside h2 {
                font-size: 1rem; /* Smaller title */
            }

            aside .text-indigo-600 {
                font-size: 0.75rem; /* Smaller back link */
            }

            nav.p-4 {
                padding: 0.5rem; /* Less nav padding */
            }

            .sidebar-btn {
                padding: 0.5rem 0.625rem;
                font-size: 0.8125rem; /* Smaller font */
            }

            .sidebar-btn i {
                width: 14px;
                font-size: 0.75rem;
                margin-right: 0.5rem;
            }

            main.flex-1 {
                width: 60%; /* Content takes 60% - biased toward content */
                padding: 1rem;
            }

            main h3 {
                font-size: 1.125rem; /* Smaller section titles */
            }

            .bg-white.rounded-lg {
                padding: 1rem !important; /* Less padding in cards */
            }
        }

        /* Desktop - keep normal layout */
        @media (min-width: 769px) {
            .flex.min-h-screen {
                flex-direction: row;
            }

            aside.w-56 {
                width: 14rem; /* 224px */
            }
        }

/* ---- Client management (/app/partner) ------------------------------------ */
.q-ag-shell { max-width: 1280px; margin: 0 auto; padding: 22px 32px 72px; }
	.q-ag-eyebrow {
		font-family: var(--q-font-mono); font-size: 11px; letter-spacing: .12em;
		text-transform: uppercase; color: var(--q-primary); margin: 0 0 10px;
	}
	.q-ag-h1 {
		font-size: clamp(28px, 3.4vw, 44px); font-weight: 700; letter-spacing: -0.035em;
		text-transform: uppercase; margin: 0 0 10px; color: var(--q-text); line-height: 1.02;
	}
	.q-ag-sub { color: var(--q-muted); margin: 0 0 30px; font-size: 15px; line-height: 1.65; max-width: 66ch; }

	/* First arrival. The lander's deep block and diagonal shard, so the
	   greeting looks like the page they signed up on rather than a flash. */
	.q-ag-welcome {
		position: relative; overflow: hidden; background: var(--q-deep); color: #fff;
		padding: 30px 32px; margin: 0 0 26px;
	}
	.q-ag-welcome::after {
		content: ""; position: absolute; inset: 0 0 0 auto; width: 40%; z-index: 0;
		background: rgba(88,23,234,.32); clip-path: polygon(44% 0, 100% 0, 100% 100%, 0 100%);
	}
	.q-ag-welcome > * { position: relative; z-index: 1; }
	.q-ag-welcome-k {
		font-family: var(--q-font-mono); font-size: 11px; letter-spacing: .12em;
		text-transform: uppercase; color: var(--q-lavender-deep); margin: 0 0 10px;
	}
	.q-ag-welcome-h {
		font-family: var(--q-font-display); font-size: clamp(24px, 3vw, 38px); font-weight: 700;
		letter-spacing: -0.03em; line-height: 1.02; margin: 0 0 12px;
	}
	.q-ag-welcome-p { font-size: 15px; line-height: 1.6; color: var(--q-lavender); margin: 0 0 20px; max-width: 62ch; }
	.q-ag-welcome-a { display: flex; gap: 10px; flex-wrap: wrap; }
	.q-ag-welcome-a .q-ag-btn { background: #fff; color: var(--q-deep); }
	.q-ag-welcome-a .q-ag-btn:hover { background: var(--q-lavender); }
	.q-ag-welcome-a .q-ag-btn.secondary { background: rgba(255,255,255,.14); color: #fff; }
	.q-ag-welcome-a .q-ag-btn.secondary:hover { background: rgba(255,255,255,.24); }

	/* A site is a row with a state, and the state is worth seeing before you
	   read anything. The stripe is a third channel beside the badge's colour
	   and its label, never the only one. */
	.q-ag-card {
		position: relative; border: 1px solid var(--q-border); background: var(--q-surface-raised);
		padding: 18px 22px 18px 25px; margin-bottom: 10px;
		transition: border-color .14s ease, transform .14s ease;
	}
	.q-ag-card::before {
		content: ""; position: absolute; left: 0; top: -1px; bottom: -1px; width: 3px;
		background: transparent;
	}
	.q-ag-card:hover { border-color: var(--q-lavender-deep); }
	.q-ag-card--live::before { background: var(--q-primary); }
	.q-ag-card--prev::before { background: var(--q-violet-soft); }
	.q-ag-card--home::before { background: var(--q-deep); }
	.q-ag-card--accent { border-top: 3px solid var(--q-primary); padding-left: 24px; }
	.q-ag-card--accent::before { display: none; }
	.q-ag-top { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; flex-wrap: wrap; }
	.q-ag-top > div:last-child { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
	.q-ag-name { font-family: var(--q-font-display); font-weight: 600; font-size: 1.1rem; margin: 0 0 3px; letter-spacing: -0.01em; }
	.q-ag-dom { color: var(--q-muted); font-size: 13px; font-family: var(--q-font-mono); }

	.q-ag-badge {
		display: inline-block; font-family: var(--q-font-mono); font-size: 11px;
		font-weight: 500; padding: 4px 9px; letter-spacing: .06em; text-transform: uppercase;
	}
	.q-ag-live { background: var(--q-tint); color: var(--q-primary); }
	.q-ag-prev { background: var(--q-tint); color: var(--q-violet-deep); }
	.q-ag-role { background: #ece7fd; color: var(--q-primary); }
	.q-ag-none { background: #f1f0f5; color: var(--q-muted); }
	.q-ag-home { background: var(--q-deep); color: #fff; }
	/* Money owed reads louder than a state word: it is the one badge on a row
	   that asks the partner to do something. */
	.q-ag-owed { background: var(--q-bg-2); color: var(--q-ink); }

	.q-ag-members { margin: 14px 0 0; padding: 12px 0 0; border-top: 1px solid #f0eff5; }
	.q-ag-mrow { display: flex; align-items: center; gap: 10px; font-size: 14px; padding: 5px 0; flex-wrap: wrap; }
	.q-ag-actions { margin-top: 14px; display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
	.q-ag-actions input[type=email], .q-ag-actions input[type=text] {
		border: 1px solid #d7d4e0; padding: 9px 11px; font-size: 14px; min-width: 220px;
		font-family: var(--q-font-sans, inherit); background: #fff; color: var(--q-text);
	}
	.q-ag-actions input:focus { outline: 2px solid var(--q-primary); outline-offset: -2px; }

	.q-ag-btn {
		border: 0; padding: 9px 16px; font-size: 14px; font-weight: 600; cursor: pointer;
		background: var(--q-primary); color: #fff; text-decoration: none; display: inline-block;
	}
	.q-ag-btn:hover { background: var(--q-primary-hover); color: #fff; }
	.q-ag-btn.secondary { background: #ece7fd; color: var(--q-primary); }
	.q-ag-btn.secondary:hover { background: #ddd3fb; color: var(--q-primary-hover); }
	.q-ag-btn.danger { background: var(--q-bg-2); color: var(--q-ink); }
	.q-ag-btn.danger:hover { background: var(--q-line); color: var(--q-ink); }

	.q-ag-empty {
		color: var(--q-muted); background: var(--q-surface-raised);
		border: 1px dashed var(--q-border); padding: 26px; text-align: center; font-size: 14px;
	}

	/* The headline row. One number leads — what the partner is owed — and the
	   rest support it, because a row of four equal numbers makes the reader do
	   the ranking themselves. Grid rather than flex so the tiles stay aligned
	   when one of them carries a second line. */
	.q-ag-rev {
		display: grid; grid-template-columns: repeat(auto-fit, minmax(min(190px, 100%), 1fr));
		gap: 1px; background: var(--q-border); border: 1px solid var(--q-border); margin: 0 0 20px;
	}
	.q-ag-stat {
		background: var(--q-surface-raised); padding: 18px 20px 16px;
		display: flex; flex-direction: column; justify-content: flex-start;
		transition: background .14s ease;
	}
	.q-ag-stat:hover { background: #fbfaff; }
	.q-ag-stat--hero { background: var(--q-deep); }
	.q-ag-stat--hero:hover { background: #2b0d75; }
	.q-ag-stat-n {
		display: block; font-family: var(--q-font-display); font-size: 1.55rem;
		font-weight: 700; line-height: 1.1; letter-spacing: -0.02em; color: var(--q-text);
	}
	.q-ag-stat--hero .q-ag-stat-n { color: #fff; font-size: 1.95rem; }
	.q-ag-stat-l {
		display: block; font-family: var(--q-font-mono); font-size: 11px; text-transform: uppercase;
		letter-spacing: .07em; color: var(--q-muted); margin-top: 7px;
	}
	.q-ag-stat--hero .q-ag-stat-l { color: var(--q-lavender-deep); }
	.q-ag-stat-m { display: block; font-size: 12px; color: var(--q-muted); margin-top: 3px; }
	.q-ag-stat--hero .q-ag-stat-m { color: #b9a9ef; }

	.q-ag-note {
		font-size: 14px; color: var(--q-muted); background: var(--q-surface-raised);
		border: 1px solid var(--q-border); border-left: 3px solid var(--q-lavender-mid);
		padding: 13px 15px; margin: 0 0 18px; line-height: 1.55;
	}

	/* A section label that draws its own rule across the page, so the eye gets
	   a structure rather than a floating caption. */
	.q-ag-section {
		display: flex; align-items: center; gap: 14px;
		font-family: var(--q-font-mono); font-size: 11px; letter-spacing: .08em;
		text-transform: uppercase; color: var(--q-muted); margin: 38px 0 14px;
	}
	.q-ag-section::after {
		content: ""; flex: 1 1 auto; height: 1px; background: var(--q-border);
	}
	form.q-ag-inline { display: inline; }
	.q-ag-new { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(210px, 100%), 1fr)); gap: 10px; align-items: end; }
	.q-ag-new label {
		display: block; font-family: var(--q-font-mono); font-size: 11px; letter-spacing: .05em;
		text-transform: uppercase; color: var(--q-muted); margin-bottom: 6px;
	}
	/* These inputs sit outside .q-ag-actions, so they were picking up the
	   theme's default field styling and rendering as borderless headline-sized
	   text. Same treatment as every other field on the page. */
	.q-ag-new input, .q-ag-new textarea {
		width: 100%; box-sizing: border-box; border: 1px solid #d7d4e0; background: #fff;
		padding: 9px 11px; font-size: 14px; font-family: var(--q-font-sans); color: var(--q-text);
	}
	.q-ag-new input:focus, .q-ag-new select:focus, .q-ag-new textarea:focus {
		outline: 2px solid var(--q-primary); outline-offset: -2px;
	}
	.q-ag-new input::placeholder { color: #a9a6b8; }
	.q-ag-suffix { font-family: var(--q-font-mono); font-size: 12px; color: var(--q-muted); }
	.q-ag-pager {
		display: flex; gap: 8px; align-items: center; justify-content: center;
		margin: 22px 0 0; font-family: var(--q-font-mono); font-size: 12px; color: var(--q-muted);
	}
	.q-ag-pager a {
		border: 1px solid var(--q-border); background: var(--q-surface-raised); color: var(--q-primary);
		padding: 7px 13px; text-decoration: none; font-weight: 600;
	}
	.q-ag-pager a:hover { border-color: var(--q-lavender-deep); background: var(--q-lavender); }
	.q-ag-pager span { padding: 0 8px; letter-spacing: .06em; }
	.q-po-country, .q-ag-new select { border: 1px solid #d7d4e0; padding: 9px 11px; font-size: 14px; background: #fff; min-width: 220px; }
	.q-ag-new select { width: 100%; box-sizing: border-box; min-width: 0; }
	.q-po-manual[hidden] { display: none; }
	.q-po-label {
		font-family: var(--q-font-mono); font-size: 11px; letter-spacing: .05em;
		text-transform: uppercase; color: var(--q-muted);
	}
	.q-ag-inv {
		margin: 14px 0 0; padding: 11px 13px; background: var(--q-tint); border: 1px solid var(--q-violet-soft);
		display: flex; gap: 11px; align-items: center; flex-wrap: wrap; font-size: 14px;
	}

	/* ── Invoice composer ──────────────────────────────────────────────
	   Styles live here, not in assets/css/styles.css, because this page
	   already owns its own <style> block and Tailwind's output is a build
	   artefact — a utility class added to a template does nothing until
	   `npm run build` regenerates it, which is a trap for the next person
	   editing one file. */
	.q-iv-back {
		position: fixed; inset: 0; background: rgba(16, 14, 26, .55);
		z-index: 9000; overflow-y: auto; padding: 32px 16px;
	}
	.q-iv-back[hidden] { display: none; }
	.q-iv-modal {
		background: var(--q-surface-raised, #fff); border: 1px solid #d7d4e0;
		max-width: 620px; margin: 0 auto; box-shadow: 0 24px 64px rgba(0,0,0,.28);
	}
	.q-iv-head {
		padding: 22px 26px; border-bottom: 1px solid #eceaf2;
		display: flex; justify-content: space-between; align-items: flex-start; gap: 12px;
	}
	.q-iv-title { font-family: var(--q-font-display); font-size: 1.15rem; font-weight: 650; margin: 0 0 3px; }
	.q-iv-for { color: var(--q-muted); font-size: 13px; font-family: var(--q-font-mono); }
	.q-iv-x {
		border: 0; background: none; font-size: 26px; line-height: 1; cursor: pointer;
		color: var(--q-muted); padding: 0 4px;
	}
	.q-iv-x:hover { color: var(--q-text); }
	.q-iv-body { padding: 22px 26px; }
	.q-iv-lbl {
		display: block; font-family: var(--q-font-mono); font-size: 11px;
		letter-spacing: .07em; text-transform: uppercase; color: var(--q-muted); margin: 0 0 7px;
	}
	.q-iv-sec { margin: 0 0 22px; }
	.q-iv-sec-h {
		display: flex; justify-content: space-between; align-items: baseline;
		border-top: 1px solid #eceaf2; padding-top: 16px; margin-bottom: 10px;
	}
	.q-iv-input, .q-iv-body input[type=email], .q-iv-body input[type=text], .q-iv-body input[type=number] {
		border: 1px solid #d7d4e0; padding: 9px 11px; font-size: 14px;
		background: #fff; color: var(--q-text); width: 100%; box-sizing: border-box;
		font-family: var(--q-font-sans, inherit);
	}
	.q-iv-body input:focus, .q-iv-body textarea:focus { outline: 2px solid var(--q-primary); outline-offset: -2px; }
	.q-iv-row { display: grid; grid-template-columns: 1fr 118px 32px; gap: 8px; margin-bottom: 8px; align-items: center; }
	.q-iv-amt { position: relative; }
	.q-iv-amt input { padding-left: 24px; text-align: right; }
	.q-iv-amt::before {
		content: '$'; position: absolute; left: 9px; top: 50%; transform: translateY(-50%);
		color: var(--q-muted); font-size: 14px; pointer-events: none; z-index: 1;
	}
	.q-iv-del {
		border: 0; background: none; color: var(--q-muted); cursor: pointer;
		font-size: 19px; line-height: 1; padding: 4px;
	}
	.q-iv-del:hover { color: var(--q-ink); }
	.q-iv-add {
		border: 1px dashed #d7d4e0; background: none; color: var(--q-primary);
		padding: 8px 12px; font-size: 13px; font-weight: 600; cursor: pointer; width: 100%;
	}
	.q-iv-add:hover { border-color: var(--q-primary); background: #f7f5ff; }
	.q-iv-totals {
		border-top: 1px solid #eceaf2; margin-top: 20px; padding-top: 16px;
		font-size: 14px;
	}
	.q-iv-trow { display: flex; justify-content: space-between; padding: 4px 0; color: var(--q-muted); }
	.q-iv-trow.big { font-size: 17px; font-weight: 650; color: var(--q-text); padding-top: 9px; }
	.q-iv-trow b { font-variant-numeric: tabular-nums; }
	.q-iv-opts { display: flex; gap: 18px; flex-wrap: wrap; align-items: center; margin-top: 6px; font-size: 14px; }
	.q-iv-opts label { display: flex; gap: 7px; align-items: center; color: var(--q-text); }
	.q-iv-foot {
		padding: 18px 26px; border-top: 1px solid #eceaf2; background: #faf9fc;
		display: flex; gap: 10px; justify-content: flex-end; flex-wrap: wrap; align-items: center;
	}
	.q-iv-hint { font-size: 12.5px; line-height: 1.55; color: var(--q-muted); margin: 9px 0 0; }

	/* Upkeep: one line, breakdown on demand. */
	.q-iv-upkeep { border: 1px solid #eceaf2; background: #faf9fc; }
	.q-iv-upkeep > summary {
		display: flex; justify-content: space-between; align-items: baseline; gap: 12px;
		padding: 12px 14px; cursor: pointer; list-style: none; font-size: 14px;
	}
	.q-iv-upkeep > summary::-webkit-details-marker { display: none; }
	.q-iv-upkeep > summary::after {
		content: "▸"; order: 3; color: var(--q-muted); font-size: 11px; transition: transform .12s ease;
	}
	.q-iv-upkeep[open] > summary::after { content: "▾"; }
	.q-iv-upkeep > summary b { font-variant-numeric: tabular-nums; margin-left: auto; }
	.q-iv-upkeep-l { color: var(--q-text); font-weight: 500; }
	.q-iv-upkeep-l em {
		font-style: normal; font-family: var(--q-font-mono); font-size: 10px;
		letter-spacing: .06em; text-transform: uppercase; color: var(--q-muted);
		background: #ece7fd; padding: 2px 6px; margin-left: 6px;
	}
	.q-iv-upkeep > div, .q-iv-upkeep > p { padding: 0 14px 12px; }
	.q-iv-maint { display: grid; gap: 4px; padding-top: 2px; }
	.q-iv-maint-row {
		display: flex; justify-content: space-between; gap: 12px; align-items: baseline;
		font-size: 13px; color: var(--q-muted); padding: 3px 0;
	}
	.q-iv-maint-row b { font-variant-numeric: tabular-nums; color: var(--q-text); font-weight: 400; }

	/* Who gets what, now and monthly. */
	.q-iv-split { margin-top: 20px; border-top: 1px solid #eceaf2; padding-top: 14px; }
	.q-iv-split-h, .q-iv-split-r {
		display: grid; grid-template-columns: 1fr 96px 96px; gap: 10px; align-items: baseline;
	}
	.q-iv-split-h span {
		font-family: var(--q-font-mono); font-size: 10px; letter-spacing: .07em;
		text-transform: uppercase; color: var(--q-muted); text-align: right; padding-bottom: 6px;
	}
	.q-iv-split-h span:first-child { text-align: left; }
	.q-iv-split-r { padding: 7px 0; font-size: 14px; }
	.q-iv-split-r b { text-align: right; font-variant-numeric: tabular-nums; font-weight: 500; color: var(--q-text); }
	.q-iv-split-r.muted { color: var(--q-muted); }
	.q-iv-split-r.muted b { color: var(--q-muted); font-weight: 400; }
	.q-iv-split-r.you {
		border-top: 1px solid #eceaf2; margin-top: 3px; padding-top: 11px;
		font-size: 16px; font-weight: 650;
	}
	.q-iv-split-r.you b { font-size: 17px; font-weight: 700; }
	.q-iv-msg { flex: 1 1 200px; font-size: 13px; margin: 0; }
	.q-iv-msg.err { color: var(--q-ink); }
	.q-iv-msg.ok { color: var(--q-primary); }
	.q-iv-btn[disabled] { opacity: .55; cursor: default; }
	@media (max-width: 560px) {
		.q-iv-row { grid-template-columns: 1fr 96px 30px; }
		.q-iv-head, .q-iv-body, .q-iv-foot { padding-left: 18px; padding-right: 18px; }
	}

/* ---- Mail (/mail) -------------------------------------------------------- */
:root {
		/* Pulled from a real capture, not approximated: getComputedStyle for
		   text/type, pixel-sampled screenshot for backgrounds (several,
		   including the "active nav row", turned out flat/computed-transparent
		   — the visible color only showed up sampling actual rendered pixels).
		   --qm-font used to keep the reference's own system-font fallback
		   chain. That made Mail the only app screen not set in IBM Plex Sans —
		   a fourth typeface, and on most machines it resolved to Segoe UI or
		   Helvetica, whose lowercase shapes read visibly differently from the
		   rest of the product. Mail is ours, so it uses our face. */
		--qm-font: var(--q-font-sans, "IBM Plex Sans", ui-sans-serif, system-ui, sans-serif);
		--qm-accent: #7b4ce9;
		/* Solid, not the real product's 16%-alpha purple overlay — deliberately.
		   Both buttons this is used on (.qm-compose-btn, #qm-f-send) already sit
		   on a solid --qm-accent background; layering translucent purple over
		   solid purple barely reads as a hover state at all. A darker solid
		   shade is the correct equivalent for a solid-color pill button. */
		--qm-accent-hover: #6a3fd1;
		--qm-sidebar-bg: #f9f9f9;
		--qm-active-row-bg: #f1f1ef;
		--qm-hover-row-bg: #f2f2f2;
		--qm-msg-active-bg: rgba(122,5,255,0.14);
		--qm-border-subtle: #f2f2f2;
		--qm-panel-bg: #ffffff;
		--qm-empty-pane-bg: #f6f6f6;
		--qm-text: #2b2b2d;
		--qm-text-muted: #575758;
		--qm-text-read: #6f6e6f;
		--qm-text-faint: #919091;
		--qm-danger: var(--q-ink);
		--qm-danger-bg: var(--q-bg-2);
		/* Two-layer keyboard-focus ring: white 1px "gap" ring + purple ~3px
		   outer ring — real technique, not a single generic outline. */
		--qm-focus-ring: 0 0 0 1px #ffffff, 0 0 0 4px var(--qm-accent);
	}

	/* ---- Page shell -----------------------------------------------------
	   The app used to be the only thing on the page and took the viewport
	   itself (height: calc(100vh - 2px)). With the utility strip above it,
	   a fixed viewport height would push the bottom of the mailbox — the
	   pagination — off screen by exactly the strip's height.

	   So the shell is a flex column and the app takes what is left, rather
	   than subtracting a hardcoded 52px: the strip wraps to two rows at
	   narrow widths, and any banner above the app (a freshly shown mailbox
	   password) is another variable-height sibling. Measuring it in CSS
	   would be guessing at both.

	   Scoped by the qm-page body class, added from this template's
	   body_class filter, so no other template's layout moves.

	   <body> is deliberately NOT the flex container. Making it one promotes
	   everything wp_footer() appends into a flex item, and one of those is the
	   analytics tracking pixel from mu-plugins/qntc-tracker.php. A 1x1 <img>
	   as a column-flex item stretches to the container's width and, being a
	   replaced element with a 1:1 intrinsic ratio, takes that as its height:
	   measured at 1905x1905. It ate the entire viewport, leaving the mail app
	   squeezed to its content height of 64px with the pagination stranded
	   under it. In normal block layout a 1x1 pixel is a 1x1 pixel.

	   So the page section is the flex column and it owns the viewport height
	   directly. The 2px is the same allowance the app itself used to carry:
	   the skip link above it is 1px tall in flow. */
	body.qm-page { overflow: hidden; }
	body.qm-page > .flex-grow {
		display: flex;
		flex-direction: column;
		height: calc(100vh - 2px);
		height: calc(100dvh - 2px); /* mobile: the real viewport, not the one behind the toolbar */
		min-height: 0;
	}
	/* WordPress reserves space at the top of the document for the admin bar
	   (html{margin-top}), so the viewport is that much shorter. Only admins
	   see it, which is exactly who would never think to check. */
	body.qm-page.admin-bar > .flex-grow { height: calc(100vh - 34px); height: calc(100dvh - 34px); }
	@media screen and (max-width: 782px) {
		body.qm-page.admin-bar > .flex-grow { height: calc(100vh - 48px); height: calc(100dvh - 48px); }
	}

	.qm-app {
		display: flex;
		flex-direction: column;
		flex: 1 1 auto;
		min-height: 0;
		font-family: var(--qm-font);
		color: var(--qm-text);
		background: var(--qm-panel-bg);
	}

	/* ---- Focus ring (accessibility) — shared across every interactive
	   control, gated on :focus-visible (keyboard/programmatic focus only,
	   not mouse clicks) rather than each element rolling its own outline. */
	.qm-compose-btn:focus-visible,
	.qm-mobile-compose-btn:focus-visible,
	.qm-icon-btn:focus-visible,
	.qm-folder-item:focus-visible,
	.qm-msg-row:focus-visible,
	.qm-msg-check:focus-visible,
	.qm-btn:focus-visible,
	.qm-back-btn:focus-visible,
	.qm-pagination button:focus-visible,
	.qm-bulk-bar button:focus-visible,
	.qm-search-field input:focus-visible,
	.qm-mobile-folder-select:focus-visible,
	.qm-modal input:focus-visible,
	.qm-modal textarea:focus-visible,
	.qm-compose-row input:focus-visible,
	.qm-compose-tb-btn:focus-visible,
	.qm-compose-ccbcc-toggle:focus-visible,
	.qm-compose-link-row input:focus-visible,
	.qm-compose-attachment-chip button:focus-visible,
	.qm-compose-body:focus-visible {
		outline: none;
		box-shadow: var(--qm-focus-ring);
	}

	/* ---- The app header is gone -----------------------------------------
	   .qm-header used to be a full-width 64px bar holding a wordmark, the
	   view switcher, a search box and Refresh. With the utility strip above
	   it that was two bars of chrome before any mail, and only one of the
	   four things in it was app-wide.

	   Where each piece went:
	     wordmark      — deleted; the tab title and favicon already say Mail.
	     view switcher — into the strip, via $strip_extra. It has to be
	                     outside .qm-view to survive switching views.
	     search        — into the list pane (.qm-list-search); it only ever
	                     filtered this pane's messages.
	     refresh       — same place, for the same reason. */

	/* Switcher, at the top of the side panel — above Compose, and above the
	   mailbox identity at the bottom of the same panel. Those three things are
	   the parts of this screen that do not change when the view does, so they
	   share the one column that does not change either.

	   A divider under it, because Compose sits directly below and the two are
	   different kinds of control: one picks which app you are in, the other
	   acts inside it. */
	.qm-view-switch {
		display: flex;
		align-items: center;
		gap: 2px;
		flex: 0 0 auto;
		margin: -4px 0 12px;
		padding-bottom: 10px;
		border-bottom: 1px solid var(--qm-border, rgba(0,0,0,.08));
	}
	/* Mobile: the side panels are hidden, so it rides in the search row
	   instead — no divider there, it is one control among several. */
	.qm-view-switch.qm-view-switch-inline {
		margin: 0;
		padding-bottom: 0;
		border-bottom: none;
	}

	/* Search + Refresh, at the top of the inbox panel. */
	.qm-list-search {
		flex: 0 0 auto;
		display: flex;
		align-items: center;
		gap: 6px;
		padding: 10px 14px 4px;
	}
	.qm-search-field { flex: 1; min-width: 0; position: relative; }
	.qm-search-field svg { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--qm-text-faint); pointer-events: none; }
	.qm-search-field input {
		width: 100%;
		box-sizing: border-box;
		height: 36px;
		padding: 0 12px 0 36px;
		border: none;
		border-radius: 999px;
		background: var(--qm-sidebar-bg);
		font-size: 14px;
		font-weight: 500;
		font-family: inherit;
		color: var(--qm-text);
	}
	.qm-search-field input::placeholder { color: var(--qm-text-faint); font-weight: 400; }
	.qm-icon-btn.active { background: var(--qm-active-row-bg); color: var(--qm-accent); }
	.qm-icon-btn {
		width: 36px; height: 36px; border-radius: 999px; border: none; background: none;
		display: flex; align-items: center; justify-content: center; cursor: pointer; color: var(--qm-text-muted);
		transition: background-color .2s cubic-bezier(0.55,0,0.35,1);
	}
	.qm-icon-btn:hover { background: var(--qm-sidebar-bg); color: var(--qm-text); }
	.qm-icon-btn.qm-spinning svg { animation: qm-spin 0.7s linear; }
	@keyframes qm-spin { to { transform: rotate(360deg); } }
	.qm-avatar {
		width: 40px; height: 40px; border-radius: 999px; background: var(--qm-accent); color: #fff;
		display: flex; align-items: center; justify-content: center; font-size: 15px; font-weight: 700;
		font-family: var(--q-font-display, var(--qm-font)); flex: 0 0 auto;
	}

	/* ---- Body row: sidebar / list / reading pane ------------------------- */
	/* Mail/Calendar/Contacts are three sibling views inside the SAME loaded
	   page (matching the wireframe's own showView()/.main-content.active
	   pattern) — client-side switching, no page reload, no separate WP
	   route. The flex-row layout role lives on the shared .qm-view class
	   (not just .qm-body) so Calendar/Contacts get the same side-by-side
	   sidebar+main layout Mail does — caught live: without this, the
	   Calendar/Contacts panes had no flex context at all and rendered as
	   an invisible, zero-height block instead of a visible 2-column layout. */
	.qm-view { flex: 1; display: flex; min-height: 0; }
	.qm-view:not(.active) { display: none !important; }

	.qm-sidebar {
		width: 218px;
		flex: 0 0 218px;
		background: var(--qm-sidebar-bg);
		display: flex;
		flex-direction: column;
		padding: 16px 16px 20px;
	}
	.qm-compose-btn {
		background: var(--qm-accent);
		color: #fff;
		border: none;
		border-radius: 9999px;
		height: 40px;
		padding: 0 16px;
		font-size: 16px;
		font-weight: 700;
		cursor: pointer;
		margin-bottom: 20px;
		font-family: inherit;
		display: flex;
		align-items: center;
		justify-content: center;
		gap: 8px;
	}
	/* No transition on purpose — real .gb-button hover is instant per the
	   extracted reference CSS, not a fade. */
	.qm-compose-btn:hover { background: var(--qm-accent-hover); }
	.qm-folder-list { list-style: none; margin: 0 0 8px; padding: 0; flex: 0 0 auto; }
	.qm-folder-item {
		display: flex;
		width: 100%;
		box-sizing: border-box;
		align-items: center;
		gap: 10px;
		height: 32px;
		padding: 0 10px;
		border-radius: 8px;
		cursor: pointer;
		font-size: 14px;
		color: var(--qm-text);
		background: none;
		border: none;
		text-align: left;
		font-family: inherit;
		transition: background-color .2s cubic-bezier(0.55,0,0.35,1);
	}
	.qm-folder-item svg { flex: 0 0 auto; color: var(--qm-text-muted); }
	/* Real behavior: hover and selected share the exact same background, and
	   the icon turns brand-purple in BOTH states, not just selected. */
	.qm-folder-item:hover,
	.qm-folder-item.active { background: var(--qm-active-row-bg); }
	.qm-folder-item.active { font-weight: 700; }
	.qm-folder-item:hover svg,
	.qm-folder-item.active svg { color: var(--qm-accent); }
	.qm-folder-item-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
	.qm-folder-unseen { font-size: 14px; font-weight: 700; color: var(--qm-accent); flex: 0 0 auto; }
	/* Bottom-of-sidebar profile — replaces the old plain-text .qm-account
	   row and the top-right header avatar (which had no click behavior at
	   all) with one clickable element: address + avatar, opening a menu to
	   rename or add another address. margin-top:auto is the same
	   push-to-bottom trick .qm-account always used. */
	.qm-profile { position: relative; margin-top: auto; padding-top: 14px; border-top: 1px solid var(--qm-border,rgba(0,0,0,.08)); }
	.qm-profile-btn {
		width: 100%; display: flex; align-items: center; gap: 10px;
		background: none; border: none; padding: 6px 4px; border-radius: 8px;
		cursor: pointer; font-family: inherit; text-align: left;
	}
	.qm-profile-btn:hover { background: var(--qm-active-row-bg); }
	.qm-profile-btn .qm-avatar { width: 28px; height: 28px; font-size: 12px; flex: 0 0 auto; }
	.qm-profile-address { flex: 1; min-width: 0; font-size: 12px; color: var(--qm-text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
	.qm-profile-chevron { flex: 0 0 auto; color: var(--qm-text-faint); transition: transform .15s ease; }
	.qm-profile-btn[aria-expanded="true"] .qm-profile-chevron { transform: rotate(180deg); }
	.qm-profile-menu {
		position: absolute; left: 0; right: 0; bottom: calc(100% + 6px);
		background: var(--qm-panel-bg); border-radius: 10px; box-shadow: 0 6px 24px rgba(0,0,0,.18);
		padding: 6px; z-index: 20;
	}
	.qm-profile-menu-item {
		display: block; width: 100%; text-align: left; background: none; border: none;
		padding: 9px 10px; border-radius: 6px; cursor: pointer; font-family: inherit;
		font-size: 13px; color: var(--qm-text);
	}
	.qm-profile-menu-item:hover { background: var(--qm-active-row-bg); }
	.qm-profile-menu-note { padding: 9px 10px; font-size: 12px; color: var(--qm-text-muted); line-height: 1.5; max-width: 220px; }
	.qm-profile-menu-list { list-style: none; margin: 4px 0; padding: 6px 10px; border-top: 1px solid var(--qm-border,rgba(0,0,0,.08)); }
	.qm-profile-menu-list li { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 12px; color: var(--qm-text-muted); padding: 3px 0; }
	.qm-profile-menu-list li span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
	.qm-profile-menu-list li button { flex: 0 0 auto; background: none; border: none; color: var(--qm-danger); cursor: pointer; font-size: 11px; padding: 0; }

	.qm-list-pane {
		width: 360px;
		flex: 0 0 360px;
		background: var(--qm-panel-bg);
		display: flex;
		flex-direction: column;
		min-width: 0;
	}
	.qm-list-toolbar { flex: 0 0 auto; padding: 10px 14px; display: flex; align-items: center; justify-content: space-between; min-height: 24px; }
	.qm-list-toolbar-hint { font-size: 12px; color: var(--qm-text-muted); }
	.qm-bulk-bar { display: flex; align-items: center; gap: 10px; font-size: 13px; color: var(--qm-text); }
	.qm-bulk-bar button { font-family: inherit; font-size: 13px; border: none; background: none; cursor: pointer; padding: 4px 10px; border-radius: 999px; }
	.qm-bulk-bar .qm-bulk-delete { color: var(--qm-danger); }
	.qm-bulk-bar .qm-bulk-delete:hover { background: var(--qm-danger-bg); }
	.qm-bulk-bar .qm-bulk-cancel { color: var(--qm-text-muted); }
	.qm-bulk-bar .qm-bulk-cancel:hover { background: var(--qm-sidebar-bg); }

	/* min-height:0 so the list actually shrinks and scrolls rather than
	   pushing the pagination below the pane, and flex:1 so it still claims
	   the space when the folder is EMPTY — that is what keeps the pager on
	   the bottom edge of an empty inbox instead of floating up under the
	   toolbar. */
	.qm-messages { flex: 1 1 0%; min-height: 0; overflow-y: auto; }
	.qm-msg-row-wrap {
		display: flex;
		align-items: flex-start;
		gap: 4px;
		padding: 0 10px;
		margin-top: 4px;
		border-radius: 8px;
		transition: background-color .2s cubic-bezier(0.55,0,0.35,1);
	}
	.qm-msg-row-wrap:hover { background: var(--qm-hover-row-bg); }
	.qm-msg-check {
		appearance: none;
		-webkit-appearance: none;
		margin: 22px 4px 0 4px;
		flex: 0 0 auto;
		width: 20px;
		height: 20px;
		border-radius: 4px;
		border: 1.5px solid var(--qm-text-faint);
		background-color: transparent;
		cursor: pointer;
		position: relative;
		transition: background-color .3s cubic-bezier(0.55,0,0.35,1), border-color .3s cubic-bezier(0.55,0,0.35,1);
	}
	.qm-msg-check:checked {
		background-color: var(--qm-accent);
		border-color: var(--qm-accent);
	}
	.qm-msg-check::after {
		content: "";
		position: absolute;
		inset: 0;
		background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M5 13l4 4L19 7"/></svg>') no-repeat center / 12px 12px;
		transform: scale(0);
		transition: transform .3s cubic-bezier(0.55,0,0.35,1);
	}
	.qm-msg-check:checked::after { transform: scale(1); }
	.qm-msg-row {
		display: block;
		flex: 1;
		min-width: 0;
		box-sizing: border-box;
		padding: 12px 4px;
		cursor: pointer;
		background: none;
		border: none;
		text-align: left;
		font-family: inherit;
	}
	/* Real behavior, distinct from folder rows above: selected message rows
	   get a light PURPLE tint, not a neutral gray — confirmed via the real
	   component's --sds-color-background-brand-selected-minimal token. */
	.qm-msg-row-wrap.active { background: var(--qm-msg-active-bg); }
	.qm-msg-row-wrap.unseen .qm-msg-from,
	.qm-msg-row-wrap.unseen .qm-msg-subject { font-weight: 700; color: var(--qm-text); }
	.qm-msg-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 16px; }
	.qm-msg-from { color: var(--qm-text-read); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: flex; align-items: center; }
	.qm-unseen-dot { width: 8px; height: 8px; border-radius: 999px; background: var(--qm-accent); margin-right: 8px; flex: 0 0 auto; }
	.qm-msg-date { color: var(--qm-text-muted); font-size: 14px; flex: 0 0 auto; }
	.qm-msg-subject { font-size: 16px; margin-top: 2px; color: var(--qm-text-read); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
	.qm-msg-flag { color: var(--q-primary); margin-right: 6px; }
	.qm-empty, .qm-loading { padding: 24px 14px; color: var(--qm-text-muted); font-size: 14px; }
	.qm-pagination { flex: 0 0 auto; padding: 10px 14px; display: flex; justify-content: space-between; align-items: center; font-size: 12px; color: var(--qm-text-muted); }
	.qm-pagination button { border: none; background: var(--qm-sidebar-bg); border-radius: 999px; padding: 5px 12px; cursor: pointer; font-family: inherit; color: var(--qm-text); }
	.qm-pagination button:disabled { opacity: 0.4; cursor: default; }

	.qm-reading-pane { flex: 1; min-width: 0; display: flex; flex-direction: column; background: var(--qm-panel-bg); }
	.qm-reading-header { padding: 20px 28px 16px; }
	.qm-reading-subject { font-size: 20px; font-weight: 700; margin: 0 0 10px; font-family: var(--q-font-display, var(--qm-font)); color: var(--qm-text); }
	.qm-reading-meta { font-size: 13px; color: var(--qm-text-muted); line-height: 1.6; }
	.qm-reading-actions { margin-top: 14px; display: flex; gap: 8px; }
	.qm-btn {
		border: none;
		background: var(--qm-sidebar-bg);
		border-radius: 999px;
		padding: 7px 16px;
		font-size: 13px;
		font-weight: 600;
		cursor: pointer;
		font-family: inherit;
		color: var(--qm-text);
	}
	.qm-btn:hover { background: var(--qm-active-row-bg); }
	.qm-btn.qm-danger:hover { background: var(--qm-danger-bg); color: var(--qm-danger); }
	.qm-reading-body { flex: 1; overflow-y: auto; padding: 4px 28px 28px; }
	.qm-reading-body iframe { width: 100%; height: 65vh; border: none; background: #fff; }
	.qm-reading-body pre { white-space: pre-wrap; word-wrap: break-word; font-family: var(--qm-font); font-size: 15px; color: var(--qm-text); margin: 0; }
	.qm-attachments-note { margin-top: 16px; font-size: 12px; color: var(--qm-text-muted); }
	.qm-placeholder {
		flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
		gap: 6px; color: var(--qm-text-faint); font-size: 16px; font-weight: 700;
		background: var(--qm-empty-pane-bg); text-align: center; padding: 24px;
	}
	.qm-placeholder svg { color: var(--qm-text-faint); margin-bottom: 6px; }
	.qm-placeholder a { color: var(--qm-accent); font-weight: 600; }

	.qm-banner { padding: 10px 24px; font-size: 13px; display: flex; align-items: center; justify-content: space-between; gap: 16px; }
	.qm-banner.qm-error { background: var(--qm-danger-bg); color: var(--qm-danger); }
	.qm-banner.qm-success { background: var(--qm-sidebar-bg); color: var(--qm-accent); font-weight: 600; }
	.qm-banner-close { background: none; border: none; font-size: 18px; line-height: 1; cursor: pointer; padding: 0 2px; flex: 0 0 auto; color: inherit; opacity: .65; }
	.qm-banner-close:hover { opacity: 1; }

	.qm-modal-backdrop {
		position: fixed; inset: 0; background: rgba(28,28,34,0.45);
		display: flex; align-items: center; justify-content: center; z-index: 1000;
	}
	.qm-modal {
		background: #fff; border-radius: 16px; width: 560px; max-width: 92vw;
		max-height: 90vh; overflow-y: auto;
		padding: 24px; box-shadow: 0 20px 60px rgba(0,0,0,0.25);
	}
	.qm-modal h2 { margin: 0 0 16px; font-size: 18px; font-family: var(--q-font-display, var(--qm-font)); color: var(--qm-text); }
	.qm-modal label { display: block; font-size: 12px; color: var(--qm-text-muted); margin: 14px 0 4px; font-weight: 600; }
	.qm-modal input[type=text], .qm-modal input[type=email], .qm-modal input[type=date], .qm-modal input[type=time], .qm-modal textarea {
		width: 100%; box-sizing: border-box; padding: 10px 12px; border: none;
		background: var(--qm-sidebar-bg); border-radius: 10px; font-size: 14px; font-family: inherit; color: var(--qm-text);
	}
	.qm-modal textarea { resize: vertical; min-height: 160px; }
	.qm-modal-actions { margin-top: 20px; display: flex; justify-content: flex-end; gap: 8px; }
	.qm-modal-error { color: var(--qm-danger); font-size: 13px; margin-top: 10px; }

	/* ---- Compose modal ---------------------------------------------------
	   Wider/taller than the generic .qm-modal above (a rich-text toolbar
	   plus a body that needs real vertical room) — its own set of classes
	   reusing the same --qm-* tokens and the same .qm-modal-backdrop
	   overlay, rather than forcing .qm-modal's fixed 560px card to fit a
	   completely different shape. Structure/fields match the wireframe's
	   own #compose-overlay; colors stay the real Spacemail tokens already
	   established for the rest of this page, not the wireframe's own
	   placeholder --purple palette. */
	.qm-compose-modal {
		background: #fff; border-radius: 16px; width: 760px; max-width: 94vw;
		height: 640px; max-height: 90vh; display: flex; flex-direction: column;
		box-shadow: 0 20px 60px rgba(0,0,0,0.25); overflow: hidden;
		transition: width .15s cubic-bezier(0.55,0,0.35,1), height .15s cubic-bezier(0.55,0,0.35,1);
	}
	/* Maximize: same modal, just bigger — there's no separate-window system
	   to genuinely "pop out" into, so this is the honest real equivalent
	   (Gmail's own pop-out compose is really just a bigger panel too). */
	.qm-compose-modal.maximized { width: 96vw; height: 94vh; }
	/* Minimize: collapses to a small corner bar. The DOM node (and every
	   field/attachment/typed body inside it) is never destroyed — only
	   hidden — so restoring puts everything back exactly as it was. */
	.qm-compose-modal.minimized {
		width: 280px; height: auto; max-height: none; margin: 0 20px 20px 0;
	}
	.qm-compose-modal.minimized > *:not(.qm-compose-header) { display: none; }
	.qm-compose-modal.minimized .qm-compose-header { cursor: pointer; border-bottom: none; }
	/* The backdrop still exists while minimized (its own #qm-modal-root
	   element is what the compose modal lives inside), but it stops
	   dimming/blocking the rest of the app and just parks the small bar in
	   the corner — pointer-events re-enabled only on the bar itself, not
	   the transparent backdrop around it, so clicks reach the real app. */
	.qm-modal-backdrop.qm-compose-minimized-backdrop {
		background: transparent; align-items: flex-end; justify-content: flex-end;
		pointer-events: none;
	}
	.qm-modal-backdrop.qm-compose-minimized-backdrop .qm-compose-modal { pointer-events: auto; }
	.qm-compose-header { flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between; padding: 14px 14px 14px 20px; border-bottom: 1px solid var(--qm-border-subtle); }
	.qm-compose-title { font-weight: 700; font-size: 16px; font-family: var(--q-font-display, var(--qm-font)); color: var(--qm-text); }
	.qm-compose-window-icons { display: flex; align-items: center; gap: 2px; flex: 0 0 auto; }
	.qm-compose-form { flex: 0 0 auto; padding: 0 20px; border-bottom: 1px solid var(--qm-border-subtle); }
	.qm-compose-row { display: flex; align-items: center; gap: 10px; padding: 7px 0; font-size: 14px; border-top: 1px solid var(--qm-border-subtle); }
	.qm-compose-row:first-child { border-top: none; }
	.qm-compose-row label { width: 52px; flex: 0 0 52px; color: var(--qm-text-muted); font-size: 12px; font-weight: 600; }
	.qm-compose-row input[type=text], .qm-compose-row input[type=email] {
		flex: 1; min-width: 0; border: none; outline: none; background: none;
		font-size: 14px; font-family: inherit; color: var(--qm-text); padding: 2px 0;
	}
	.qm-compose-row[hidden] { display: none; }
	.qm-compose-from { color: var(--qm-text); font-weight: 600; }
	.qm-compose-ccbcc-toggle {
		border: none; background: none; padding: 0; margin-left: 12px; font-family: inherit;
		font-size: 13px; font-weight: 600; color: var(--qm-accent); cursor: pointer;
	}
	.qm-compose-toolbar { flex: 0 0 auto; display: flex; flex-wrap: wrap; align-items: center; gap: 4px; padding: 8px 16px; border-bottom: 1px solid var(--qm-border-subtle); background: var(--qm-sidebar-bg); }
	.qm-compose-tb-btn {
		width: 30px; height: 30px; border-radius: 6px; border: none; background: none;
		display: flex; align-items: center; justify-content: center; cursor: pointer; color: var(--qm-text-muted);
		transition: background-color .2s cubic-bezier(0.55,0,0.35,1);
	}
	.qm-compose-tb-btn:hover { background: var(--qm-hover-row-bg); color: var(--qm-text); }
	.qm-compose-tb-select {
		border: 1px solid var(--qm-border-subtle); border-radius: 6px; padding: 4px 6px;
		font-size: 12px; font-family: inherit; background: #fff; color: var(--qm-text); cursor: pointer;
	}
	.qm-compose-tb-divider { width: 1px; height: 20px; background: var(--qm-border-subtle); margin: 0 4px; }
	.qm-compose-link-row:not([hidden]) { display: flex; gap: 8px; align-items: center; }
	.qm-compose-link-row { flex: 0 0 auto; padding: 8px 16px; border-bottom: 1px solid var(--qm-border-subtle); background: var(--qm-sidebar-bg); }
	.qm-compose-link-row[hidden] { display: none; }
	.qm-compose-link-row input { flex: 1; min-width: 0; border: none; border-radius: 6px; padding: 6px 10px; font-size: 13px; font-family: inherit; }
	.qm-compose-body {
		flex: 1; min-height: 0; padding: 20px; font-size: 15px; color: var(--qm-text);
		outline: none; overflow-y: auto; line-height: 1.6;
	}
	.qm-compose-body:empty:before { content: attr(data-placeholder); color: var(--qm-text-faint); }
	.qm-compose-body img { max-width: 100%; border-radius: 4px; }
	.qm-compose-body a { color: var(--qm-accent); }
	.qm-compose-quote { margin: 0; padding-left: 12px; border-left: 2px solid var(--qm-border-subtle); color: var(--qm-text-muted); }
	.qm-compose-sig { color: var(--qm-text-muted); }
	.qm-compose-attachments:not([hidden]) { display: flex; flex-wrap: wrap; gap: 8px; }
	.qm-compose-attachments { flex: 0 0 auto; padding: 10px 20px 0; }
	.qm-compose-attachments[hidden] { display: none; }
	.qm-compose-attachment-chip {
		display: flex; align-items: center; gap: 6px; background: var(--qm-sidebar-bg);
		border-radius: 999px; padding: 5px 8px 5px 12px; font-size: 12px; color: var(--qm-text);
	}
	.qm-compose-attachment-chip button { border: none; background: none; padding: 0; cursor: pointer; color: var(--qm-text-muted); display: flex; }
	.qm-compose-attachment-chip button:hover { color: var(--qm-danger); }
	.qm-compose-footer { flex: 0 0 auto; display: flex; align-items: center; gap: 12px; padding: 14px 20px; border-top: 1px solid var(--qm-border-subtle); }
	.qm-compose-left-actions { display: flex; gap: 14px; align-items: center; }
	.qm-compose-left-actions button { border: none; background: none; padding: 0; cursor: pointer; color: var(--qm-text-muted); display: flex; }
	.qm-compose-left-actions button:hover { color: var(--qm-text); }
	.qm-compose-error { flex: 1; color: var(--qm-danger); font-size: 13px; }

	.qm-mobile-folder-select { display: none; }
	.qm-back-btn { display: none; }
	.qm-mobile-compose-btn { display: none; }

	@media (max-width: 860px) {
		.qm-sidebar { display: none; }
		.qm-list-pane { width: 100%; flex: 1; }
		.qm-list-pane.qm-hide-on-mobile, .qm-reading-pane.qm-hide-on-mobile { display: none; }
		/* The compose FAB (52px + 18px offset, fixed bottom-right — see
		   .qm-mobile-compose-btn below) sits directly over the "Older →"
		   button otherwise — caught live, its label was rendering half
		   hidden behind the FAB. */
		.qm-pagination { padding-right: 84px; }
		/* Compose lives in the sidebar, which is hidden entirely at this
		   width — without this a mobile visitor would have no way to start
		   a new message at all. Fixed position FAB, standard mobile pattern. */
		.qm-mobile-compose-btn {
			display: flex;
			align-items: center;
			justify-content: center;
			position: fixed;
			right: 18px;
			bottom: 18px;
			width: 52px;
			height: 52px;
			border-radius: 999px;
			background: var(--qm-accent);
			color: #fff;
			border: none;
			box-shadow: 0 6px 18px rgba(123,76,233,0.4);
			cursor: pointer;
			z-index: 10;
		}
		/* Sidebar (with folder nav + account label) is hidden entirely at
		   this width, so without this a mobile visitor would have no way to
		   switch folders at all — this replaces it, not decoration. */
		.qm-mobile-folder-select {
			display: block;
			width: 100%;
			box-sizing: border-box;
			margin: 12px 14px 0;
			padding: 8px 10px;
			border: none;
			background: var(--qm-sidebar-bg);
			border-radius: 10px;
			font-size: 13px;
			font-family: inherit;
			color: var(--qm-text);
		}
		.qm-back-btn {
			display: inline-block;
			background: none;
			border: none;
			color: var(--qm-accent);
			font-size: 13px;
			font-weight: 600;
			font-family: inherit;
			cursor: pointer;
			padding: 0 0 10px;
		}
	}

/* ═══════════════════════════════════════════════════════════════════════════
   DARK THEME
   The toggle (#darkmode-toggle-btn) sets .user-darkmode on <html>, but almost
   nothing responded to it: measured on /app/dashboard, 1 of 42 white panels
   actually went dark, the body stayed #f5f5f7, and the utility strip inherited
   near-black text onto its deep-violet ground — 1.17:1, against the 4.5:1
   floor. This is the token set that was missing.

   Derived from the brand, not invented: the ground is --q-ink darkened, panels
   sit one step above it, and the accent stays --q-primary so a chart series
   means the same thing in both themes.
   ═══════════════════════════════════════════════════════════════════════════ */
.user-darkmode {
  --q-dark-bg:      #14141a;   /* page ground */
  --q-dark-panel:   #1d1d26;   /* a panel on it */
  --q-dark-raised:  #26262f;   /* hover / a panel on a panel */
  --q-dark-line:    #33333f;
  --q-dark-text:    #f2f1f6;
  --q-dark-text-2:  #b4b2c0;
  --q-dark-text-3:  #8a8a99;
}

.user-darkmode body,
.user-darkmode .q-app { background: var(--q-dark-bg) !important; color: var(--q-dark-text); }

/* The strip keeps its brand ground in both themes — it is the one element that
   should not change, and the bug was that its TEXT changed underneath it. */
.user-darkmode .q-portal-utility { background: var(--q-deep); color: var(--q-dark-text); }
.user-darkmode .q-portal-links a { color: var(--q-lavender-mid, #d6cbfb); }
.user-darkmode .q-portal-links a:hover { color: #fff; }

/* Panels. The utility classes are what the app is actually built from, so the
   override has to reach them rather than a hand-maintained list of components. */
.user-darkmode .bg-white,
.user-darkmode [class*="bg-white"],
.user-darkmode .bg-gray-50,
.user-darkmode .bg-gray-100,
.user-darkmode .q-panel,
.user-darkmode .q-kpi,
.user-darkmode .q-ag-card,
.user-darkmode .q-modal { background: var(--q-dark-panel) !important; border-color: var(--q-dark-line) !important; }

.user-darkmode .text-gray-900,
.user-darkmode .text-gray-800,
.user-darkmode .q-kpi-n,
.user-darkmode h1, .user-darkmode h2, .user-darkmode h3, .user-darkmode h4 { color: var(--q-dark-text) !important; }
.user-darkmode .text-gray-700,
.user-darkmode .text-gray-600,
.user-darkmode .q-kpi-sub { color: var(--q-dark-text-2) !important; }
.user-darkmode .text-gray-500,
.user-darkmode .text-gray-400,
.user-darkmode .q-kpi-l { color: var(--q-dark-text-3) !important; }
.user-darkmode .border-gray-200,
.user-darkmode .border-gray-300,
.user-darkmode .divide-gray-200 > * { border-color: var(--q-dark-line) !important; }

/* Banner: the tinted variant is a light-theme device. On dark it becomes the
   panel colour with a violet rule, so the page title still reads as a header. */
.user-darkmode .q-app-head h1 { color: var(--q-dark-text); }
.user-darkmode .q-app-head p  { color: var(--q-dark-text-2); }

/* Fields */
.user-darkmode .q-app input,
.user-darkmode .q-app select,
.user-darkmode .q-app textarea {
  background: var(--q-dark-raised);
  border-color: var(--q-dark-line);
  color: var(--q-dark-text);
}
.user-darkmode .q-app input::placeholder,
.user-darkmode .q-app textarea::placeholder { color: var(--q-dark-text-3); }

/* Tables */
.user-darkmode .q-table th { background: var(--q-dark-raised); color: var(--q-dark-text-2); }
.user-darkmode .q-table td { border-color: var(--q-dark-line); color: var(--q-dark-text); }
.user-darkmode .q-table tbody tr:hover { background: var(--q-dark-raised); }

/* ---- Settings layout -----------------------------------------------------
 * The rail used to be min-h-screen beside a full-bleed form: inputs stretched
 * to ~1104px, which is four times a comfortable measure for a password field.
 * On a phone the rail took a third of the width and truncated placeholders.
 * -------------------------------------------------------------------------- */
.q-set-body { min-height: 60vh; }

/* Forms hold a real measure. 560px is the brief's number and it is the right
   one — a single-column form wider than that is harder to scan, not easier. */
/* The panel holds a reading measure; the form inside it holds the 560px the
   brief asks for. Both are needed — capping only the form leaves the white
   card stretched behind it. */
.q-app .tab-section > div { max-width: 720px; }
.q-app .tab-section form  { max-width: 560px; }

@media (max-width: 768px) {
  /* The rail becomes a scrolling tab strip above the panel.
     Stay in flex and switch direction — `display: block` left the `flex-1`
     main without a flex parent to claim width from, and it collapsed to
     202px. Column direction keeps the sizing intact. */
  .q-set-body { flex-direction: column; }
  .q-set-body > aside {
    width: 100% !important;
    box-shadow: none;
    border-bottom: 1px solid var(--q-line);
  }
  /* The nav is `flex flex-col` from the markup, so the direction has to be
     overridden as well as the overflow. */
  .q-set-body > aside nav {
    flex-direction: row !important;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    gap: 4px;
    padding: 8px 12px;
  }
  .q-set-body > aside nav > * { margin-top: 0 !important; }
  .q-set-body > aside .sidebar-btn {
    white-space: nowrap;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    flex: 0 0 auto;
  }
  /* main carries Tailwind's `flex-1`, which sizes on the MAIN axis — once the
     container is a column that is height, leaving width to be inferred, and it
     settled at 234px. State it. */
  .q-set-body > main { width: 100%; min-width: 0; padding: 20px 16px; }
  .q-app .tab-section > div,
  .q-app .tab-section form { max-width: none; }
  .q-app input, .q-app select, .q-app textarea { width: 100% !important; }
}

/* Every app button clears the 44px touch minimum on a phone. */
@media (max-width: 900px) {
  .q-app .btn-primary,
  .q-app .btn-secondary,
  .q-app .btn-danger,
  .q-app button[type="submit"],
  .q-app .q-ag-btn,
  .q-app .sidebar-btn { min-height: 44px; }
}

/* Icon-only controls — trackers table row actions (28px) and the geography
   pager (36px). These are the primary way to share, reset or rename a tracker
   on a phone. 44px is the floor, stated with a margin so sub-pixel line-height
   cannot land it at 43.99. */
@media (max-width: 900px) {
  .q-app table button,
  .q-app table a.p-1,
  .q-app button.p-1,
  .q-app button.p-2,
  .q-app a.p-2,
  .q-app .inline-flex.items-center {
    min-width: 44px;
    min-height: 45px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
  /* The floating Analyze pill is JS-positioned with inline styles, so its
     size has to be stated with the same weight. */
  .q-app button[style*="position: fixed"] { min-height: 45px !important; }
}

/* ---- Client management: compact site rows --------------------------------
 * Six sites used to run 2,500px because every row had its owner list and
 * hand-off form open at all times. They are behind a disclosure now; the row
 * itself is name, address, status and the actions.
 * -------------------------------------------------------------------------- */
.q-ag-more { margin-top: 10px; border-top: 1px solid var(--q-border); }
.q-ag-more > summary {
  list-style: none;
  cursor: pointer;
  padding: 9px 0 0;
  font-family: var(--q-font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--q-muted);
  min-height: 34px;
  display: flex;
  align-items: center;
  gap: 7px;
}
.q-ag-more > summary::-webkit-details-marker { display: none; }
.q-ag-more > summary::after {
  content: "";
  width: 7px; height: 7px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translate(-1px, -1px);
  transition: transform .15s ease;
}
.q-ag-more[open] > summary::after { transform: rotate(-135deg) translate(-2px, -2px); }
.q-ag-more > summary:hover { color: var(--q-primary); }
.q-ag-more > summary:focus-visible { outline: 2px solid var(--q-violet-soft); outline-offset: 2px; }
.q-ag-more .q-ag-more-shut { display: none; }
.q-ag-more[open] .q-ag-more-open { display: none; }
.q-ag-more[open] .q-ag-more-shut { display: inline; }

/* The row itself gets tighter now that it is one line of content. */
.q-ag-card { padding: 14px 20px 14px 23px; margin-bottom: 8px; }
.q-ag-actions { margin-top: 11px; }

@media (max-width: 900px) {
  .q-ag-shell { padding: 18px 20px 48px; }
  .q-ag-more > summary { min-height: 44px; }
}

/* ---- Mail empty states ---------------------------------------------------
 * "No messages here." named neither the folder nor what would arrive in it.
 * -------------------------------------------------------------------------- */
.qm-empty { padding: 44px 24px; text-align: center; }
.qm-empty-t {
  font-family: var(--q-font-display);
  font-size: 16px;
  font-weight: 500;
  color: var(--q-ink);
  margin: 0 0 6px;
}
.qm-empty-b {
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--q-muted);
  margin: 0 auto;
  max-width: 34ch;
}

/* ---- Typeface ------------------------------------------------------------
 * WordPress emits the active theme's theme.json presets as inline global
 * styles, including `body { font-family: var(--wp--preset--font-family--…) }`.
 * On a tenant host that is the CLIENT's font — Manrope here — and it was
 * setting the type for every app screen, which is why the app read as a
 * different product from the marketing site.
 *
 * It lives in an inline <style> block, so it cannot be grepped out of the
 * theme; it has to be outweighed. `body.q-app` is (0,1,1) against the global
 * style's (0,0,1).
 * -------------------------------------------------------------------------- */
body.q-app,
body.q-app input,
body.q-app select,
body.q-app textarea,
body.q-app button {
  font-family: var(--q-font-sans, "IBM Plex Sans", ui-sans-serif, system-ui, sans-serif);
}
body.q-app h1, body.q-app h2, body.q-app h3, body.q-app h4 {
  font-family: var(--q-font-display, "Space Grotesk", var(--q-font-sans));
}

/* Mail's own field styling wins inside Mail.
 *
 * The shared app field rule in brand.css is
 *   :is(.q-public-page, .q-app) input[type="text"]   -> (0,2,1)
 * because of the attribute selector, which outweighs
 *   .qm-search-field input                           -> (0,2,0)
 * and replaced the search box's 36px left padding (the gutter the magnifier
 * sits in) with the generic 14px — so the icon landed on top of the first
 * letter of "Search mail…". Same story for the rounded pill shape and the
 * compose fields. Stated at equal weight here, inside Mail only. */
body.qm-page .qm-search-field input[type="text"] {
  height: 36px;
  padding: 0 12px 0 36px;
  border: none;
  border-radius: 999px;
  background: var(--qm-sidebar-bg);
  font-size: 14px;
}


/* ---- Preview padlock ------------------------------------------------------
 * A site in preview is visible to anyone with the link. This is the control
 * that changes that, so its state has to be readable at a glance from the row
 * itself: the shackle closes and the whole control takes the brand colour.
 * -------------------------------------------------------------------------- */
.q-ag-lock { position: relative; display: inline-flex; }
.q-ag-lock-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 34px;
  padding: 0 12px;
  background: var(--q-bg-2);
  border: 1px solid transparent;
  border-radius: 8px;
  cursor: pointer;
  font: inherit;
  font-family: var(--q-font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--q-muted);
  transition: background .14s ease, color .14s ease, border-color .14s ease;
}
.q-ag-lock-btn:hover { color: var(--q-ink); border-color: var(--q-border); }
.q-ag-lock-btn:focus-visible { outline: 2px solid var(--q-violet-soft); outline-offset: 2px; }

/* Locked: the closed shackle is the signal, the colour reinforces it. Never
   colour alone — the label says "Locked" too. */
.q-ag-lock[data-locked="1"] .q-ag-lock-btn {
  background: var(--q-lavender);
  color: var(--q-primary);
  border-color: var(--q-lavender-deep);
}
.q-ag-lock .q-ag-lock-shackle { display: none; }
.q-ag-lock .q-ag-lock-open    { display: inline; }
.q-ag-lock[data-locked="1"] .q-ag-lock-shackle { display: inline; }
.q-ag-lock[data-locked="1"] .q-ag-lock-open    { display: none; }
.q-ag-lock[data-busy] { opacity: .6; pointer-events: none; }

.q-ag-lock-pop {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 40;
  width: 300px;
  padding: 16px;
  background: #fff;
  border: 1px solid var(--q-border);
  border-radius: 10px;
  box-shadow: 0 12px 34px rgba(36, 11, 99, 0.16);
}
.q-ag-lock-h {
  font-family: var(--q-font-display);
  font-size: 15px;
  font-weight: 600;
  color: var(--q-ink);
  margin: 0 0 5px;
}
.q-ag-lock-s { font-size: 12.5px; line-height: 1.5; color: var(--q-muted); margin: 0 0 12px; }
/* Sits under the roll button, so it reads as a footnote to the control
   rather than a second lead paragraph. */
.q-ag-lock-note { margin: 10px 0 0; padding-top: 10px; border-top: 1px solid var(--q-border); }
.q-ag-lock-in {
  width: 100%;
  height: 40px;
  padding: 0 12px;
  font: inherit;
  font-size: 14px;
  border: 1px solid var(--q-border);
  border-radius: 8px;
  background: #fff;
  color: var(--q-ink);
}
.q-ag-lock-in:focus {
  outline: none;
  border-color: var(--q-primary);
  box-shadow: 0 0 0 3px var(--q-lavender);
}
.q-ag-lock-row { display: flex; gap: 8px; margin-top: 10px; }
.q-ag-lock-msg { font-size: 12.5px; color: var(--q-muted); margin: 9px 0 0; min-height: 1em; }

@media (max-width: 900px) {
  .q-ag-lock-btn { height: 44px; }
  /* Anchor to the RIGHT edge on narrow screens. The padlock can sit anywhere
     along a wrapped action row, so a popover pinned left overflowed the
     viewport and gave the whole page a horizontal scrollbar — measured 532px
     of scroll width at a 390px viewport. */
  /* The card becomes the positioning context so the popover can span the
     row's width rather than the padlock's. */
  .q-ag-card { position: relative; }
  .q-ag-lock { position: static; }
  .q-ag-lock-pop {
    width: min(300px, calc(100vw - 32px));
    left: 16px;
    right: 16px;
    margin-inline: auto;
  }
}
/* The padlock's "this site is live" caveat. */
.q-ag-lock-n {
  font-size: 12px;
  line-height: 1.5;
  color: var(--q-violet-deep);
  background: var(--q-lavender);
  border-radius: 6px;
  padding: 8px 10px;
  margin: 0 0 12px;
}
/* The privacy panel in Settings uses the padlock's parts without its popover. */
.q-ag-lock[data-inline] { display: block; position: static; }
.q-ag-lock[data-inline] .q-ag-lock-in { max-width: 360px; }


/* The generated site password, shown so a partner can pass it on. Monospace
   and selectable: it gets read aloud and pasted into chat. */
.q-ag-key {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 12px;
  padding: 9px 10px;
  background: var(--q-lavender);
  border-radius: 8px;
}
.q-ag-key code {
  flex: 1;
  font-family: var(--q-font-mono);
  font-size: 13.5px;
  color: var(--q-violet-deep);
  user-select: all;
  overflow-wrap: anywhere;
}
.q-ag-key-c {
  flex: 0 0 auto;
  border: 0;
  background: var(--q-primary);
  color: #fff;
  font: inherit;
  font-size: 11px;
  font-family: var(--q-font-mono);
  letter-spacing: .06em;
  text-transform: uppercase;
  padding: 6px 10px;
  border-radius: 6px;
  cursor: pointer;
}
.q-ag-key-c:hover { background: var(--q-violet-deep); }
@media (max-width: 900px) { .q-ag-key-c { min-height: 44px; } }

/* The trackers table used to print a truncated <img onerror=…> in every row.
   The code is still copyable; it is just not on screen. */
.q-code-copy {
  border: 1px solid var(--q-border);
  background: #fff;
  color: var(--q-muted);
  font: inherit;
  font-family: var(--q-font-mono);
  font-size: 11px;
  letter-spacing: .05em;
  text-transform: uppercase;
  padding: 6px 11px;
  border-radius: 6px;
  cursor: pointer;
  white-space: nowrap;
}
.q-code-copy:hover { border-color: var(--q-lavender-deep); color: var(--q-primary); background: var(--q-lavender); }
@media (max-width: 900px) { .q-code-copy { min-height: 44px; } }

/* ---- Toggle ---------------------------------------------------------------
 * A real switch: role=switch + aria-checked, so it announces its state rather
 * than relying on the reader inferring it from a colour.
 * -------------------------------------------------------------------------- */
.q-toggle {
  appearance: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  width: 44px;
  height: 26px;
  border-radius: 999px;
  background: var(--q-line);
  position: relative;
  transition: background .16s ease;
  flex: 0 0 auto;
  vertical-align: middle;
}
.q-toggle-t {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(36, 11, 99, .28);
  transition: transform .16s ease;
}
.q-toggle[aria-checked="true"] { background: var(--q-primary); }
.q-toggle[aria-checked="true"] .q-toggle-t { transform: translateX(18px); }
.q-toggle:hover { background: var(--q-violet-soft); }
.q-toggle[aria-checked="true"]:hover { background: var(--q-violet-deep); }
.q-toggle:focus-visible { outline: 2px solid var(--q-violet-soft); outline-offset: 2px; }
.q-toggle[data-busy] { opacity: .55; pointer-events: none; }

/* The row's meaning must not live in the switch alone — the "why it is open"
   column carries it in words, and a private row is tinted so a long list reads
   at a glance. */
#qad-sites tr:has(.q-toggle[aria-checked="false"]) { background: var(--q-bg-2); }

@media (prefers-reduced-motion: reduce) {
  .q-toggle, .q-toggle-t { transition: none; }
}
@media (max-width: 900px) {
  .q-toggle { width: 48px; height: 28px; }
  .q-toggle-t { width: 22px; height: 22px; }
  .q-toggle[aria-checked="true"] .q-toggle-t { transform: translateX(20px); }
}
/* A link tracker's short URL, under its name in the trackers table. */
.q-track-link-row { display: flex; align-items: center; gap: 4px; margin-top: 3px; }
.q-track-link {
  font-family: var(--q-font-mono);
  font-size: 12px;
  color: var(--q-muted);
  text-decoration: none;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 220px;
}
.q-track-link:hover { color: var(--q-primary); text-decoration: underline; }
.q-track-copy {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; flex: none;
  background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--q-text-3); transition: color .15s;
}
.q-track-copy:hover { color: var(--q-primary); }
.q-track-copy svg { width: 14px; height: 14px; }

/* ---------------------------------------------------------------------------
   Shared dashboards (/shared/<id> and the domain equivalent).
   These are the public face of the product -- the page a customer sends to
   somebody else -- and they had drifted into looking like a different app:
   a centred marketing hero over left-aligned app furniture. They now wear the
   same frame as the signed-in dashboard.
   --------------------------------------------------------------------------- */
.q-share-mark {
  font-family: var(--q-font-display);
  font-size: 17px; font-weight: 700; letter-spacing: -0.2px;
  color: #ffffff !important;
}
.q-share-mark:hover { color: #ffffff !important; }

.q-share-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 16px; flex-wrap: wrap; margin-bottom: 4px;
}
.q-share-name {
  font-family: var(--q-font-display);
  font-size: clamp(22px, 3vw, 28px); font-weight: 700;
  letter-spacing: -0.4px; line-height: 1.15;
  color: var(--q-text); margin: 0;
}
.q-share-sub {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin: 6px 0 0; font-size: 14px; color: var(--q-muted);
}
.q-share-dot { color: var(--q-border); }
.q-share-visit {
  display: inline-flex; align-items: center; gap: 5px;
  color: var(--q-primary); text-decoration: none; font-weight: 500;
}
.q-share-visit:hover { text-decoration: underline; }
.q-share-visit svg { width: 15px; height: 15px; }

/* Secondary, like "Update Data" on the signed-in dashboard -- on a share page
   nothing competes with it, so a filled violet button was shouting. */
.q-share-refresh {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 16px; min-height: 44px;
  background: #ffffff; color: var(--q-text);
  border: 1px solid var(--q-border);
  font-size: 14px; font-weight: 500;
  cursor: pointer; transition: border-color .15s, color .15s;
}
.q-share-refresh:hover { border-color: var(--q-primary); color: var(--q-primary); }
.q-share-refresh:active { transform: scale(.98); }
.q-share-refresh svg { width: 17px; height: 17px; }

@media (max-width: 640px) {
  .q-share-head { align-items: flex-start; }
  .q-share-refresh { width: 100%; justify-content: center; }
}

/* "Give someone else access" — a second person on a site the client already
   owns. Only rendered once a site HAS an owner, i.e. once it has been paid
   for; before that the address that matters is the invoice's. */
.q-ag-addmail { margin: 12px 0 0; padding: 12px 0 0; border-top: 1px solid var(--q-border); }
.q-ag-addmail-l {
  display: block; margin-bottom: 6px;
  font-size: 12px; font-weight: 600; letter-spacing: .03em;
  text-transform: uppercase; color: var(--q-text-3);
}
.q-ag-addmail-r { display: flex; gap: 8px; flex-wrap: wrap; }
.q-ag-addmail-r input[type="email"] {
  flex: 1 1 220px; min-width: 0; min-height: 40px;
  padding: 0 12px; font: inherit; font-size: 14px;
  border: 1px solid var(--q-border); background: #fff; color: var(--q-text);
}
.q-ag-addmail-r input[type="email"]:focus {
  outline: 2px solid var(--q-primary); outline-offset: -2px; border-color: var(--q-primary);
}
.q-ag-addmail-r .q-ag-btn { flex: none; }

/* ---------------------------------------------------------------------------
   Site access — the dashboard card, and the page behind it.
   --------------------------------------------------------------------------- */
.q-sa-card {
  display: block; margin: 0 0 32px; padding: 20px 22px;
  background: #fff; border: 1px solid var(--q-border);
  text-decoration: none; color: inherit;
  transition: border-color .15s, background .15s;
}
.q-sa-card:hover { border-color: var(--q-lavender-deep); background: var(--q-surface-raised); }
.q-sa-card-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.q-sa-card-sub { margin: 4px 0 0; font-size: 14px; color: var(--q-muted); }
.q-sa-card-nums { display: flex; gap: 22px; margin: 14px 0 0; font-size: 14px; color: var(--q-text-2); }
.q-sa-num strong {
  font-family: var(--q-font-mono); font-size: 20px; font-weight: 700;
  color: var(--q-text); margin-right: 5px;
}
/* Private is the state an operator is here to act on, so it is the one that
   carries colour. Zero private is the quiet, ordinary case and stays neutral. */
.q-sa-num.is-private strong { color: var(--q-primary); }

.q-sa-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 16px; flex-wrap: wrap; margin: 0 0 18px;
}
.q-sa-counts { font-size: 14px; color: var(--q-text-2); }
.q-sa-counts strong { font-family: var(--q-font-mono); font-weight: 700; color: var(--q-text); }
.q-sa-note { display: block; margin-top: 3px; font-size: 13px; color: var(--q-muted); }
.q-sa-search { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.q-sa-search input[type="search"] {
  min-height: 40px; min-width: 260px; padding: 0 12px;
  font: inherit; font-size: 14px;
  border: 1px solid var(--q-border); background: #fff; color: var(--q-text);
}
.q-sa-search input[type="search"]:focus {
  outline: 2px solid var(--q-primary); outline-offset: -2px; border-color: var(--q-primary);
}
.q-sa-clear { font-size: 13px; color: var(--q-muted); text-decoration: none; }
.q-sa-clear:hover { color: var(--q-primary); text-decoration: underline; }
.q-sa-result { margin: 0 0 12px; font-size: 13px; color: var(--q-muted); }
.q-sa-empty {
  margin: 0; padding: 28px 20px; text-align: center;
  background: #fff; border: 1px solid var(--q-border);
  font-size: 14px; color: var(--q-muted);
}
.q-sa-msg { margin: 0; padding: 12px 2px; font-size: 13px; color: var(--q-muted); min-height: 1em; }
.q-sa-foot { margin: 26px 0 0; font-size: 13px; color: var(--q-muted); max-width: 62ch; }

@media (max-width: 640px) {
  .q-sa-search { width: 100%; }
  .q-sa-search input[type="search"] { flex: 1 1 auto; min-width: 0; }
}

/* Unread count on the portal's Mail card.
   Sits beside the card's eyebrow label, so it reads as belonging to that card
   rather than floating over it. Tabular numerals so 7 and 11 are the same
   width and the bubble does not jump when the count changes. */
.q-portal-badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 20px; height: 20px; padding: 0 6px; margin-left: 8px;
  border-radius: 10px;
  background: var(--q-primary); color: #ffffff;
  font-family: var(--q-font-mono); font-size: 11px; font-weight: 600;
  font-variant-numeric: tabular-nums; letter-spacing: 0;
  vertical-align: middle;
}
.q-portal-badge[hidden] { display: none; }
/* On the filled violet card the violet bubble would vanish into it. */
.q-portal-card[style*="#5817ea"] .q-portal-badge {
  background: #ffffff; color: var(--q-primary);
}

/* Optional upkeep lines carry their own control: x to drop, + to put back.
   The row stays in the list either way — hiding a declined line would leave a
   partner no way back and nothing to say the option exists. */
.q-iv-maint-row { display: flex; align-items: baseline; gap: 8px; }
.q-iv-maint-n { flex: 1 1 auto; }
.q-iv-maint-row.is-off .q-iv-maint-n {
  color: var(--q-text-3);
  text-decoration: line-through;
}
.q-iv-maint-x {
  flex: none; width: 20px; height: 20px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: 1px solid var(--q-border); border-radius: 0;
  color: var(--q-text-3); font: inherit; font-size: 13px; line-height: 1;
  cursor: pointer; transition: border-color .12s, color .12s;
}
.q-iv-maint-x:hover { border-color: var(--q-primary); color: var(--q-primary); }
.q-iv-maint-row.is-off .q-iv-maint-x { border-color: var(--q-primary); color: var(--q-primary); }
/* In the upkeep breakdown a struck module simply has no price, so only the
   name needs striking. In the take-off list the price is still shown -- it is
   what the client will see charged and discounted -- so it is struck too. */
#qivOffList .q-iv-maint-row.is-off b { color: var(--q-text-3); text-decoration: line-through; }
#qivOffList .q-iv-maint-row { padding: 3px 0; }
.q-iv-off-h {
  margin: 12px 0 5px; font-size: 11px; font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: var(--q-text-3);
}
.q-iv-off-h:first-child { margin-top: 0; }
/* The same strike, on the row the partner typed. Without it the line reads as
   a normal charge right up until you open the fold. */
.q-iv-row[data-struck="1"] .qiv-name,
.q-iv-row[data-struck="1"] .qiv-amt {
  color: var(--q-text-3); text-decoration: line-through;
}

/* A portal card for a module this site does not buy. Muted, not hidden —
   a client who was told they had analytics must not find a portal that never
   mentions it. */
.q-portal-card.is-locked { opacity: .62; }
.q-portal-card.is-locked:hover { opacity: 1; }
.q-portal-card.is-locked .q-portal-cta {
  font-family: var(--q-font-mono); font-size: 11px;
  letter-spacing: .06em; text-transform: uppercase;
}

/* The locked-module screen. */
.q-lock { max-width: 620px; }
.q-lock-lede { font-size: 18px; line-height: 1.6; color: var(--q-text-2); margin: 0 0 26px; }
.q-lock-buy {
  display: flex; align-items: center; justify-content: space-between;
  gap: 20px; flex-wrap: wrap;
  padding: 20px 22px; background: var(--q-surface-raised);
  border: 1px solid var(--q-border);
}
.q-lock-price {
  font-family: var(--q-font-display); font-size: 30px; font-weight: 700;
  letter-spacing: -.02em; color: var(--q-text);
}
.q-lock-per { font-size: 14px; color: var(--q-muted); margin-left: 6px; }
.q-lock-btn {
  min-height: 46px; padding: 12px 24px;
  font: inherit; font-size: 15px; font-weight: 600;
  color: #fff; background: var(--q-primary);
  border: 0; border-radius: 0; cursor: pointer;
}
.q-lock-btn:hover { background: var(--q-deep); }
.q-lock-note { margin: 14px 0 0; font-size: 14px; color: var(--q-muted); }
.q-lock-back { margin: 30px 0 0; font-size: 14px; }

/* One adjustment — a discount, or work already paid for directly. Folded,
   because it is the exception: most invoices are a price and a send, and a
   field nobody needs on the common path is one that gets filled in by
   accident. The summary carries the running figure so a folded adjustment can
   never be silently in effect. */
.q-iv-adj > summary {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; cursor: pointer; list-style: none;
  padding: 10px 0; font-size: 13px; color: var(--q-text-3);
}
.q-iv-adj > summary::-webkit-details-marker { display: none; }
.q-iv-adj > summary::after {
  content: '+'; order: -1; margin-right: 2px;
  font-family: var(--q-font-mono); color: var(--q-text-3);
}
.q-iv-adj[open] > summary::after { content: '\2212'; }
.q-iv-adj > summary:hover { color: var(--q-primary); }
.q-iv-adj-s { flex: 1 1 auto; }
.q-iv-adj > summary b {
  font-family: var(--q-font-mono); font-size: 13px;
  color: var(--q-primary); font-variant-numeric: tabular-nums;
}
.q-iv-adj-b { padding: 4px 0 10px; }
#qivAdj { width: 110px; text-align: right; }
#qivAdjNote { flex: 1 1 220px; min-width: 0; }
