/* Libre Franklin, SIL Open Font License 1.1, from the Vauseware brand kit. --brand is the Tenant's color, set at runtime. */
@font-face { font-family: "Libre Franklin"; src: url("/fonts/LibreFranklin-Variable.ttf") format("truetype"); font-weight: 100 900; font-display: swap; }
:root { --brand: #1b365d; --ink: #0d1b2a; --muted: #5f6672; --paper: #fbfbfc; --card: #fff; --line: #e5e7eb; --tint: color-mix(in srgb, var(--brand) 8%, white); --error: #8a2b2b; --radius: 10px; --ease-out: cubic-bezier(0.23, 1, 0.32, 1); --ease-drawer: cubic-bezier(0.32, 0.72, 0, 1); --panel: 340px; --gutter: max(16px, calc((100vw - 620px) / 2)); --shadow: 0 1px 2px rgb(13 27 42 / .04), 0 4px 16px rgb(13 27 42 / .05); font: 17px/1.5 "Libre Franklin", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; color: var(--ink); background: var(--paper); -webkit-font-smoothing: antialiased; }
* { box-sizing: border-box; } body { margin: 0; min-width: 320px; } a { color: var(--brand); text-underline-offset: 3px; } :focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; } h1:focus { outline: none; }
header { position: sticky; top: 0; z-index: 1; background: color-mix(in srgb, var(--paper) 92%, transparent); backdrop-filter: blur(8px); border-bottom: 1px solid var(--line); } header, main { padding: 14px var(--gutter); } main { padding-top: 28px; padding-bottom: 48px; display: flex; flex-direction: column; }
.top { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 36px; } .brand { display: flex; align-items: center; font-weight: 700; letter-spacing: -.01em; } .brand img { display: block; max-height: 36px; max-width: 180px; mix-blend-mode: multiply; } .brand.has-logo .name { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; } .progress { min-height: 36px; max-width: 60%; padding: 6px 14px; overflow: hidden; border-radius: 999px; font-size: 13px; color: var(--muted); font-variant-numeric: tabular-nums; text-overflow: ellipsis; white-space: nowrap; } .progress[aria-expanded="true"] { border-color: var(--brand); color: var(--brand); }
.track { height: 6px; margin-top: 10px; border-radius: 999px; background: var(--line); overflow: hidden; } #bar { height: 100%; width: 0; border-radius: inherit; background: var(--brand); transition: width .3s ease; }
h1 { margin: 0 0 8px; font-size: clamp(1.5rem, 6vw, 1.9rem); line-height: 1.2; letter-spacing: -.02em; text-wrap: balance; } .context { order: -1; margin: 0 0 6px; font-size: 14px; color: var(--muted); } .help, .lede { margin: 0 0 4px; color: var(--muted); } .lede { color: var(--ink); } .muted { color: var(--muted); } .error { margin: 8px 0 0; color: var(--error); } .error:empty { display: none; }
button, input, textarea { font: inherit; } button { min-height: 48px; padding: 12px 16px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); color: inherit; cursor: pointer; transition: border-color .15s, background-color .15s; } button:hover { border-color: var(--brand); } button:disabled { opacity: .5; cursor: not-allowed; } button.primary { background: var(--brand); color: #fff; border-color: var(--brand); font-weight: 600; } button.primary:hover { filter: brightness(1.1); }
.choices { display: grid; gap: 10px; margin: 20px 0 0; } .choices button { display: flex; align-items: center; gap: 12px; text-align: left; } .choices button::before { content: ""; flex: none; width: 20px; height: 20px; border: 2px solid var(--line); border-radius: 50%; background: var(--card); } .choices button[aria-pressed="true"] { border-color: var(--brand); background: var(--tint); font-weight: 600; } .choices button[aria-pressed="true"]::before { border-color: var(--brand); background: radial-gradient(circle, var(--brand) 0 45%, var(--card) 50%); }
fieldset { display: grid; gap: 8px; margin: 20px 0 0; padding: 0; border: 0; } legend { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); } fieldset label { display: flex; align-items: center; gap: 12px; min-height: 48px; margin: 0; padding: 10px 14px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); cursor: pointer; } fieldset label:has(input:checked) { border-color: var(--brand); background: var(--tint); font-weight: 600; } fieldset input { flex: none; width: 20px; height: 20px; min-height: auto; margin: 0; accent-color: var(--brand); }
label { display: block; margin: 20px 0 6px; font-size: 15px; font-weight: 500; } input, textarea { width: 100%; min-height: 48px; padding: 12px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); color: inherit; } input:focus, textarea:focus { border-color: var(--brand); outline: 2px solid var(--tint); } textarea { min-height: 140px; } input.code { font-size: 1.5rem; letter-spacing: .4em; text-align: center; font-variant-numeric: tabular-nums; }
.group { display: grid; gap: 12px; margin-top: 20px; } .group-item { display: grid; gap: 4px; padding: 4px 16px 16px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow); } .group-field .field-label { margin-top: 12px; } .group-field .help { margin: 2px 0 0; font-size: 14px; } .group-field .choices, .group-field fieldset { margin-top: 8px; } .group-item [data-remove] { justify-self: start; min-height: 36px; margin-top: 12px; padding: 6px 12px; font-size: 14px; color: var(--muted); } .group [data-add] { order: 1; border-style: dashed; color: var(--brand); font-weight: 600; }
.actions { position: sticky; bottom: 0; display: flex; gap: 10px; margin: 24px calc(-1 * var(--gutter)) 0; padding: 12px var(--gutter) calc(12px + env(safe-area-inset-bottom)); background: linear-gradient(transparent, var(--paper) 30%); } .actions button { flex: 1; } .actions #back { flex: none; min-width: 96px; } .actions #skip { flex: none; min-width: 88px; } .skip-note { margin: 16px 0 0; font-size: 14px; color: var(--muted); }
.review { margin: 20px 0 0; padding: 0; list-style: none; border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow); overflow: hidden; } .review li { display: grid; gap: 2px; padding: 12px 16px; border-top: 1px solid var(--line); } .review li:first-child { border-top: 0; } .review [data-back] { justify-self: start; min-height: auto; padding: 0; border: 0; background: none; font-size: 14px; color: var(--muted); text-align: left; } .review [data-back]::after { content: " · Edit"; color: var(--brand); } .review .answer { font-weight: 600; overflow-wrap: anywhere; } .review .answer:empty::before { content: "—"; color: var(--muted); font-weight: 400; } .review .answer.skipped { color: var(--muted); font-weight: 400; }
.done-mark { order: -2; display: grid; place-items: center; width: 56px; height: 56px; margin: 8px 0 16px; border-radius: 50%; background: var(--tint); color: var(--brand); font-size: 28px; font-weight: 700; }
#panel { position: fixed; inset: 0; z-index: 2; overflow-y: auto; padding: 16px 16px calc(24px + env(safe-area-inset-bottom)); background: var(--paper); } #panel[hidden] { display: none; }
@media (min-width: 1000px) { body.panel-open { --gutter: max(16px, calc((100vw - var(--panel) - 620px) / 2)); padding-right: var(--panel); } #panel { left: auto; width: var(--panel); padding-top: 22px; border-left: 1px solid var(--line); } }
.panel-head { display: flex; align-items: center; justify-content: space-between; } .panel-head h2 { margin: 0; font-size: 1.1rem; } .panel-head h2:focus { outline: none; } [data-close] { width: 40px; min-height: 40px; padding: 0; border: 0; background: none; font-size: 26px; line-height: 1; color: var(--muted); }
.percent { margin: 2px 0 14px; font-size: 14px; color: var(--muted); } .panel-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; } .panel-actions button { flex: 1 1 auto; min-height: 40px; padding: 8px 12px; font-size: 14px; } [data-resume] { color: var(--brand); font-weight: 600; }
.sections { display: grid; gap: 8px; } .section { border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); } .section[open] { border-color: var(--brand); }
.section summary, .section .row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 14px; font-size: 15px; font-weight: 600; } .section summary { cursor: pointer; list-style: none; } .section summary::-webkit-details-marker { display: none; } .section summary::after { content: "›"; order: 2; color: var(--muted); transition: transform .15s; } .section[open] summary::after { transform: rotate(90deg); }
.state { margin-left: auto; font-size: 13px; font-weight: 400; color: var(--muted); white-space: nowrap; } [data-state="done"] .state { color: var(--brand); } [data-state="done"] .state::before { content: "✓ "; } [data-state="todo"] { opacity: .7; }
.section ul { display: grid; gap: 2px; margin: 0; padding: 0 6px 8px; list-style: none; } .section li button { display: grid; width: 100%; min-height: 44px; padding: 8px; border: 0; border-radius: 6px; background: none; text-align: left; } .section li button:hover, .section li [aria-current] { background: var(--tint); } .section .q { font-size: 13px; color: var(--muted); } .section li .answer { font-size: 14px; font-weight: 600; overflow-wrap: anywhere; } .section li .answer:empty::before { content: "—"; color: var(--muted); font-weight: 400; } .section .answer.next { color: var(--brand); } .section li .answer.skipped { color: var(--muted); font-weight: 400; }
/* Motion. The panel's exit runs because display transitions discretely; the phone sheet rises from the bottom, the desktop panel slides in from the right. */
#panel { transition: transform 300ms var(--ease-drawer), opacity 300ms var(--ease-drawer), display 300ms allow-discrete; } #panel[hidden] { transform: translateY(100%); } @starting-style { #panel:not([hidden]) { transform: translateY(100%); } }
@media (min-width: 1000px) { #panel[hidden] { transform: translateX(100%); } @starting-style { #panel:not([hidden]) { transform: translateX(100%); } } }
.sections { interpolate-size: allow-keywords; } .section::details-content { block-size: 0; overflow: clip; opacity: 0; transition: block-size 200ms var(--ease-out), opacity 200ms var(--ease-out), content-visibility 200ms allow-discrete; } .section[open]::details-content { block-size: auto; opacity: 1; }
[data-fresh] .answer { animation: settle 250ms var(--ease-out); } @keyframes settle { from { opacity: 0; transform: translateY(4px); } }
[data-just-done] { animation: glow 900ms ease; } [data-just-done] .state { animation: pop 400ms var(--ease-out); } [data-just-done], [data-just-done] .state { animation-delay: var(--glow-delay, 0ms); } @keyframes glow { from { background: var(--tint); border-color: var(--brand); } } @keyframes pop { 0% { opacity: 0; transform: scale(.85); } 60% { opacity: 1; transform: scale(1.08); } }
.progress { transition: background-color 200ms ease, color 200ms ease, border-color 200ms ease; } .progress[data-cheer] { background: var(--brand); border-color: var(--brand); color: #fff; font-weight: 600; }
.done-mark { position: relative; animation: pop 400ms var(--ease-out) both; } .done-mark::after { content: ""; position: absolute; inset: 0; border-radius: 50%; border: 2px solid var(--brand); opacity: 0; animation: ring 700ms var(--ease-out) 150ms; } @keyframes ring { from { opacity: .5; transform: scale(1); } to { opacity: 0; transform: scale(1.6); } }
/* Screens: follow-ups under their root, nested in review, and the topic intro card. */
.follow-up { margin-top: 20px; padding-top: 4px; border-top: 1px solid var(--line); } .field-label { margin: 16px 0 0; font-size: 15px; font-weight: 500; } .follow-up .help { margin: 4px 0 0; font-size: 14px; } .follow-up label + .help { margin: 0 0 6px; } .follow-up .choices, .follow-up fieldset { margin-top: 8px; } .follow-up .skip-note { margin-top: 8px; } .follow-up [data-skip] { min-height: 36px; margin-top: 8px; padding: 6px 12px; font-size: 14px; color: var(--muted); }
.review .sub { display: grid; margin: 8px 0 0; padding: 0 0 0 14px; list-style: none; border-left: 2px solid var(--line); } .review .sub li { padding: 8px 0 0; border-top: 0; } .review .sub [data-back]::after { content: none; }
.intro { margin-top: 8px; } .intro-count { margin: 4px 0 0; font-size: 1.1rem; color: var(--muted); }
.intro, .follow-up[data-new] { animation: rise 250ms var(--ease-out); } @keyframes rise { from { opacity: 0; transform: translateY(8px); } }
@media (prefers-reduced-motion: reduce) { #panel { transition-property: opacity, display; } .section::details-content { transition-property: opacity, content-visibility; } #panel[hidden] { transform: none; opacity: 0; } @starting-style { #panel:not([hidden]) { transform: none; opacity: 0; } } [data-fresh] .answer, [data-just-done] .state, .done-mark, .intro, .follow-up[data-new] { animation-name: fade; } .done-mark::after { animation: none; } @keyframes fade { from { opacity: 0; } } }
/* The rooms screen, drawn as the house: a roof, the floors top to bottom, a ground line above the first floor below grade, and a tray for rooms on a floor not listed. Every color is mixed from the tokens above, so it follows the Tenant's brand and the page's theme. */
:root { --roof: color-mix(in srgb, var(--brand) 75%, var(--ink)); --wall: var(--card); --earth: color-mix(in srgb, #a58b5f 14%, var(--paper)); --ground: color-mix(in srgb, #5f8a4a 80%, var(--card)); --k-bed: color-mix(in srgb, var(--brand) 13%, var(--card)); --k-bath: color-mix(in srgb, #2a8c82 16%, var(--card)); --k-live: color-mix(in srgb, #c98a2b 17%, var(--card)); --k-work: color-mix(in srgb, #6b5aa6 14%, var(--card)); --hint-bg: color-mix(in srgb, #e7b866 24%, var(--card)); }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.steps { display: flex; gap: 6px; margin: 4px 0 10px; } .steps i { width: 22px; height: 4px; border-radius: 2px; background: var(--line); } .steps i.on { background: var(--brand); } .step-count { margin: 0 0 4px; font-size: 14px; color: var(--muted); }
.house { display: grid; margin-top: 16px; min-width: 0; } .roof { height: 48px; margin: 0 4px; background: var(--roof); clip-path: polygon(50% 0, 100% 100%, 0 100%); }
.floor { min-width: 0; padding: 6px 8px 8px; border-inline: 3px solid var(--roof); border-bottom: 2px solid var(--line); background: var(--wall); } .floor.below { border-inline: 1px solid var(--line); background: var(--earth); } .floor.apart { margin-top: 12px; border: 3px solid var(--roof); border-radius: 4px; } .ground { height: 8px; margin-inline: -6px; border-radius: 4px; background: var(--ground); }
.floor-name { margin: 0 0 6px; font-size: 12px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); overflow-wrap: anywhere; }
.rooms { display: flex; flex-wrap: wrap; gap: 6px; min-height: 40px; margin: 0; padding: 0; list-style: none; } .rooms.none { align-items: center; font-size: 14px; color: var(--muted); }
.room { display: inline-flex; align-items: center; gap: 4px; max-width: 100%; min-height: 44px; padding: 6px 10px; border: 1.5px solid transparent; border-radius: 8px; color: var(--ink); font-size: 14px; font-weight: 600; line-height: 1.2; text-align: left; overflow-wrap: anywhere; transition: transform .15s var(--ease-out), border-color .15s, box-shadow .15s; } li.room { min-height: 36px; }
.room.bed { background: var(--k-bed); } .room.bath { background: var(--k-bath); } .room.live { background: var(--k-live); } .room.work { background: var(--k-work); } button.room:hover { border-color: var(--brand); } button.room:active { transform: scale(.96); }
.room.guess { background: none; border: 1.5px dashed var(--muted); color: var(--muted); font-weight: 500; } .room .mark { font-weight: 700; } .room .crown { color: var(--brand); }
.room[aria-pressed="true"] { border-color: var(--brand); box-shadow: 0 0 0 3px var(--tint); } .room:disabled { opacity: .4; } .room .dots { display: inline-flex; gap: 2px; } .room .dots i { width: 7px; height: 7px; border-radius: 50%; background: var(--brand); }
.house .add { min-height: 44px; padding: 6px 10px; border: 1.5px dashed var(--line); border-radius: 8px; background: none; color: var(--brand); font-size: 14px; font-weight: 600; }
.tray { margin-top: 16px; padding: 10px; border: 1.5px dashed var(--muted); border-radius: var(--radius); } .tray .help { margin-bottom: 8px; font-size: 14px; }
.hint { margin: 12px 0 0; padding: 10px 12px; border-radius: 8px; background: var(--hint-bg); font-size: 14px; } .skip-details { justify-self: center; min-height: 40px; margin: 16px auto 0; padding: 6px 14px; border-radius: 999px; font-size: 14px; color: var(--muted); } .home-screen #home { display: grid; }
.actions [data-house-back] { flex: none; min-width: 96px; }
.locked-note { margin: 12px 0 0; padding: 10px 12px; border-radius: 8px; background: var(--tint); font-weight: 600; } .locked-list { display: grid; margin: 16px 0 0; padding: 0; list-style: none; border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); } .locked-list li { display: grid; gap: 2px; padding: 12px 16px; border-top: 1px solid var(--line); font-size: 15px; } .locked-list li:first-child { border-top: 0; } .locked-list b { overflow-wrap: anywhere; } .locked-value { font-weight: 600; overflow-wrap: anywhere; }
/* The "+ Room" and room sheet: a modal dialog rising from the bottom. */
.sheet { width: min(100%, 620px); max-width: 100%; max-height: 85dvh; margin: auto auto 0; padding: 0; border: 0; border-radius: 18px 18px 0 0; background: var(--card); color: var(--ink); box-shadow: var(--shadow); overflow-x: hidden; } .sheet::backdrop { background: rgb(13 27 42 / .4); } #sheet-body { padding: 18px 16px calc(18px + env(safe-area-inset-bottom)); } .sheet h2 { margin: 0 0 4px; font-size: 1.15rem; line-height: 1.25; } .sheet .help { font-size: 15px; }
.tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; margin-top: 14px; } .tile { position: relative; min-height: 52px; padding: 8px 18px 8px 8px; font-size: 14px; font-weight: 600; line-height: 1.2; overflow-wrap: anywhere; } .tile:active { transform: scale(.96); } .tile .where { display: block; margin-top: 2px; font-size: 12px; font-weight: 400; color: var(--muted); } .tile .count { position: absolute; top: 4px; right: 4px; min-width: 20px; height: 20px; padding: 0 5px; border-radius: 10px; background: var(--brand); color: #fff; font-size: 12px; line-height: 20px; }
.results { display: grid; gap: 2px; margin-top: 6px; } .results button { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 4px 12px; min-height: 44px; padding: 8px 12px; border: 0; background: none; text-align: left; } .results button:hover { background: var(--tint); } .results .for { font-size: 13px; color: var(--muted); } .results .none { margin: 0; padding: 8px 12px; font-size: 14px; color: var(--muted); }
.floor-pick { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; } .floor-pick button { min-height: 44px; padding: 6px 14px; border-radius: 999px; font-size: 15px; } .floor-pick [aria-pressed="true"] { border-color: var(--brand); background: var(--tint); font-weight: 600; }
.sheet-actions { display: flex; gap: 10px; margin-top: 18px; } .sheet-actions button { flex: 1; } .sheet .remove { color: var(--error); }
.sheet[open] { animation: sheet-up 260ms var(--ease-drawer); } .sheet[open]::backdrop { animation: scrim 200ms ease; } @keyframes sheet-up { from { opacity: 0; transform: translateY(40px); } } @keyframes scrim { from { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .room { transition: none; } button.room:active, .tile:active { transform: none; } .sheet[open] { animation-name: scrim; } }
fieldset.columns { grid-template-columns: repeat(auto-fill, minmax(min(100%, 9.5rem), 1fr)); } fieldset.columns label { padding: 10px 12px; gap: 10px; overflow-wrap: anywhere; }
