/* ==========================================================================
   StayLedger Tenant Portal

   Phone first, because every tenant is on one and several are on a 360px
   Android. Laptop styles are added at breakpoints, not stripped back from a
   desktop layout.

   The palette, radii, shadows and breakpoints are the internal portal's, so the
   two read as one product rather than two that happen to share a name. The
   breakpoints in particular are deliberately identical - 560 / 820 / 1000 - so
   nobody has to hold two sets in their head.
   ========================================================================== */

:root {
  --brand-900: #14225c;
  --brand-800: #1b2e78;
  --brand-700: #1f3a8a;
  --brand-600: #2b4fc7;
  --brand-500: #3b63e8;
  --brand-100: #e5ecff;
  --brand-50:  #f2f6ff;

  --ink:      #0f172a;
  --ink-soft: #33415c;
  --muted:    #6b7690;
  --faint:    #94a1b8;

  --line:      #e4e8f2;
  --line-soft: #eef1f8;
  --bg:        #f5f7fc;
  --card:      #ffffff;

  --ok-700:#0f6b4a; --ok-100:#d9f5e7; --ok-line:#b6e7cf;
  --warn-700:#8a5300; --warn-100:#fdf0d5; --warn-line:#f4dcae;
  --bad-700:#a52034; --bad-100:#fde4e8; --bad-line:#f7c9d2;
  --info-700:#0b5f8a; --info-100:#ddf1fb; --info-line:#bfe4f6;

  --r-sm: 8px;
  --r:    12px;
  --r-lg: 18px;
  --shadow-1: 0 1px 2px rgba(15, 23, 42, .06), 0 1px 3px rgba(15, 23, 42, .04);
  --shadow-2: 0 4px 12px rgba(15, 23, 42, .08), 0 2px 4px rgba(15, 23, 42, .04);
  --shadow-3: 0 18px 44px rgba(15, 23, 42, .18);

  --header-h: 58px;
  --tabbar-h: 62px;
}

*, *::before, *::after { box-sizing: border-box; }

html, body { margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }

body {
  font: 15px/1.55 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  color: var(--ink-soft);
  background: var(--bg);
  /* iOS rubber-banding shows the background behind the page; matching it stops a
     white flash at the top of an overscroll. */
  overscroll-behavior-y: none;
}

h1, h2, h3, h4 { margin: 0; color: var(--ink); line-height: 1.25; letter-spacing: -.01em; }
p { margin: 0 0 10px; }
p:last-child { margin-bottom: 0; }
a { color: var(--brand-600); }

/* 16px on form controls, always. Anything smaller makes iOS Safari zoom the
   whole page when the field is focused, which is disorienting mid-form. */
input, select, textarea, button { font-family: inherit; font-size: 16px; }

img { max-width: 100%; height: auto; }

/* Long values - an email address, a UPI reference - must wrap rather than push
   the layout wider than a 320px screen. */
.wrap-any { overflow-wrap: anywhere; word-break: break-word; }

.hidden { display: none !important; }

/*
 * The hidden attribute must beat any class that sets display.
 *
 * The browser's own rule is `[hidden] { display: none }`, which a class like
 * `.stage { display: grid }` outranks on specificity. The result is an element
 * whose `.hidden` property reads true while it is still painted - so the sign-in
 * card and the portal were both on screen at once, one above the other, and every
 * assertion on `el.hidden` passed while it happened. Only a screenshot showed it.
 */
[hidden] { display: none !important; }
.grow { flex: 1; min-width: 0; }
.mt-1 { margin-top: 8px; }
.mt-2 { margin-top: 16px; }
.mb-0 { margin-bottom: 0; }
.mb-1 { margin-bottom: 8px; }
.small { font-size: 13px; }
.muted { color: var(--muted); }
.faint { color: var(--faint); }
.strong { font-weight: 600; color: var(--ink); }
.num { font-variant-numeric: tabular-nums; }
.center { text-align: center; }

/* Visible only to screen readers. Used for form labels that would otherwise be
   implied by a placeholder, which assistive technology does not announce. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ==========================================================================
   Boot splash
   --------------------------------------------------------------------------
   Same refresh mechanism as the internal staff portal: on a reload the page
   cannot know who is signed in until /api/auth/tenant/me (or /api/owner/me)
   answers, and both the sign-in stage and the portal shell ship in the markup.
   Painting either one before the answer is the flicker that made a refresh look
   broken - the wrong view for a frame, or a blank screen while the request runs.

   So <html> carries class="booting" from the very first byte, which keeps every
   view out of sight and shows a neutral branded splash in its place. boot.js and
   owner.js remove the class only once the correct view has been chosen, so the
   first thing anyone sees on a refresh is this splash and then the right screen.

   Kept in the stylesheet rather than inline because the <link> in <head> blocks
   the first paint, so these rules are always in force before anything is drawn.
   ========================================================================== */
html.booting { overflow: hidden; }
/* Every top-level view stays hidden behind the splash until one is chosen. */
html.booting .signin-page,
html.booting .stage,
html.booting .app,
html.booting #console,
html.booting #needStaff { visibility: hidden; }

.boot {
  position: fixed;
  inset: 0;
  z-index: 4000;
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  /* Per-page background so the splash matches the page it covers. Light by
     default (the tenant app); the dark pages set --boot-bg below. */
  background: var(--boot-bg, var(--bg));
}
html.booting .boot { display: flex; }
/* Dark pages: the landing, the sign-in stage and the contact page all sit on the
   deep navy/gradient, so their splash does too rather than flashing white. The
   class is on <body>, and .boot is a direct child of <body>, so a plain
   descendant rule reaches it. */
body.landing .boot { --boot-bg: #140f38; }
body.dark-page .boot { --boot-bg: #0b1230; }

.boot-mark {
  width: 46px;
  height: 46px;
  border-radius: 13px;
  background: var(--brand-900);
  display: grid;
  place-items: center;
  box-shadow: var(--shadow-2);
}
.boot-bar {
  width: 132px;
  height: 3px;
  border-radius: 999px;
  background: var(--line);
  overflow: hidden;
}
.boot-bar span {
  display: block;
  width: 40%;
  height: 100%;
  border-radius: 999px;
  background: var(--brand-500);
  animation: bootSlide 1.05s ease-in-out infinite;
}
@keyframes bootSlide {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(330%); }
}
/* A stalled boot says so rather than sitting on an unexplained empty screen. */
.boot-slow {
  max-width: 280px;
  text-align: center;
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
}
/* Respect a reduced-motion preference: hold the bar still rather than loop it. */
@media (prefers-reduced-motion: reduce) {
  .boot-bar span { animation: none; width: 100%; }
}

/* ==========================================================================
   Sign in
   ========================================================================== */

.stage {
  min-height: 100dvh;
  /* minmax(0, 1fr) rather than 1fr: an auto column can exceed a 360px viewport
     and then get clipped. This bit the internal portal and is fixed here from
     the start. */
  display: grid; grid-template-columns: minmax(0, 1fr);
  place-items: center;
  padding: clamp(16px, 5vw, 40px) clamp(14px, 4vw, 32px);
  background:
    radial-gradient(760px 420px at 88% -10%, rgba(47, 216, 166, .16), transparent 68%),
    radial-gradient(680px 420px at 4% 108%, rgba(59, 99, 232, .22), transparent 70%),
    #0b1230;
}
.stage .shell { width: 100%; max-width: 420px; }

/*
 * The sign-in page frame.
 *
 * A flex column so the shared header sits at the top, the sign-in card floats in
 * the middle (the .stage grows to fill), and the shared footer is pinned to the
 * bottom on a short page yet pushed down by content on a tall one. The dark
 * gradient lives on this wrapper now, so header, card and footer share one
 * background rather than the card sitting on its own panel.
 */
.signin-page {
  min-height: 100dvh;
  display: flex; flex-direction: column;
  background:
    radial-gradient(760px 420px at 88% -6%, rgba(47, 216, 166, .16), transparent 68%),
    radial-gradient(680px 420px at 4% 108%, rgba(59, 99, 232, .22), transparent 70%),
    #0b1230;
}
/* Inside the frame the stage is just the centred middle band, not the whole
   viewport, and it carries no background of its own. */
.signin-page .stage {
  flex: 1 0 auto;
  min-height: 0;
  background: none;
}
/* On the sign-in/contact frame the header and footer are transparent so the
   wrapper's gradient shows through as one continuous surface. */
.signin-page .nav { background: rgba(11, 18, 48, .55); }
.signin-page .nav.scrolled { background: rgba(11, 18, 48, .82); }
.signin-page .ft { background: transparent; border-top-color: rgba(255,255,255,.08); }

.wordmark { display: flex; align-items: center; gap: 11px; margin-bottom: 18px; }
.wordmark .mark {
  width: 42px; height: 42px; flex: 0 0 42px; border-radius: 12px;
  background: linear-gradient(140deg, var(--brand-700), var(--brand-900));
  display: grid; place-items: center;
  box-shadow: 0 8px 20px -8px rgba(0, 0, 0, .6);
}
.wordmark svg { width: 27px; height: 27px; display: block; }
.wordmark .name { display: block; font-size: 19px; font-weight: 700; color: #fff; letter-spacing: -.2px; }
.wordmark .name em { font-style: normal; color: #2fd8a6; }
.wordmark .sub { display: block; font-size: 12.5px; color: #9aa7cc; }

.auth-card {
  background: var(--card);
  border-radius: var(--r-lg);
  padding: clamp(20px, 5vw, 28px);
  box-shadow: 0 24px 60px -28px rgba(0, 0, 0, .7);
}
.auth-card h1 { font-size: clamp(20px, 5.5vw, 24px); margin-bottom: 6px; }
.auth-card .lede { color: var(--muted); font-size: 14px; margin-bottom: 20px; }

.field { margin-bottom: 16px; }
.field > label {
  display: block; margin-bottom: 7px;
  font-size: 13px; font-weight: 600; color: var(--ink-soft);
}
.input, .select, .textarea {
  width: 100%; padding: 12px 14px;
  border: 1.5px solid var(--line); border-radius: var(--r);
  background: var(--card); color: var(--ink);
  transition: border-color .15s, box-shadow .15s;
}
.textarea { min-height: 84px; resize: vertical; line-height: 1.5; }
.input:hover, .select:hover, .textarea:hover { border-color: #cdd6ea; }
.input:focus, .select:focus, .textarea:focus {
  outline: 0; border-color: var(--brand-500);
  box-shadow: 0 0 0 3px rgba(59, 99, 232, .16);
}
.input:disabled, .select:disabled, .textarea:disabled {
  background: var(--line-soft); color: var(--muted); cursor: not-allowed;
}
.hint { margin-top: 6px; font-size: 12.5px; color: var(--muted); line-height: 1.5; }
.hint.is-bad { color: var(--bad-700); font-weight: 600; }

/* Phone field with a fixed +91 country-code chip to the left of the input. */
.phone-field { display: flex; align-items: stretch; }
.phone-cc {
  display: inline-flex; align-items: center;
  padding: 0 12px; font-size: 16px; font-weight: 600; color: var(--ink-soft);
  background: var(--line-soft); border: 1.5px solid var(--line); border-right: 0;
  border-radius: var(--r) 0 0 var(--r);
}
.phone-field .input { border-radius: 0 var(--r) var(--r) 0; }
.phone-field .input:focus { position: relative; z-index: 1; }

/* The green success tick shown on the contact page after a message is sent. */
.done-mark {
  width: 56px; height: 56px; margin: 0 auto 14px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 28px; font-weight: 800; color: #fff;
  background: linear-gradient(145deg, #2fd8a6, #0f6b4a);
  box-shadow: 0 12px 28px -12px rgba(15, 107, 74, .8);
}

/* ------------------------------ the code boxes ---------------------------- */

.otp { display: flex; gap: 8px; justify-content: space-between; margin: 4px 0 14px; }
.otp input {
  width: 100%; max-width: 54px; aspect-ratio: 1 / 1.15;
  text-align: center; font-size: 22px; font-weight: 700;
  color: var(--brand-700);
  border: 1.5px solid var(--line); border-radius: 11px; background: var(--card);
}
.otp input:focus {
  outline: 0; border-color: var(--brand-500);
  box-shadow: 0 0 0 3px rgba(59, 99, 232, .16);
}
@media (max-width: 360px) {
  .otp { gap: 5px; }
  .otp input { font-size: 19px; }
}

/* ==========================================================================
   Buttons
   ========================================================================== */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 46px; padding: 12px 18px;
  font-size: 15px; font-weight: 600; line-height: 1.2;
  border-radius: var(--r); border: 1.5px solid transparent;
  cursor: pointer; text-decoration: none; white-space: nowrap;
  transition: transform .08s, background .15s, border-color .15s, color .15s;
}
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: .55; cursor: not-allowed; transform: none; }
.btn:focus-visible { outline: 2px solid var(--brand-500); outline-offset: 2px; }

.btn-primary { background: var(--brand-600); color: #fff; }
.btn-primary:hover:not(:disabled) { background: var(--brand-700); }
.btn-soft { background: var(--brand-50); color: var(--brand-700); border-color: var(--brand-100); }
.btn-soft:hover:not(:disabled) { background: var(--brand-100); }
.btn-ghost { background: var(--card); color: var(--ink-soft); border-color: var(--line); }
.btn-ghost:hover:not(:disabled) { background: var(--bg); border-color: #cdd6ea; }
.btn-bad { background: var(--card); color: var(--bad-700); border-color: var(--bad-line); }
.btn-bad:hover:not(:disabled) { background: var(--bad-100); }
.btn-block { width: 100%; }
.btn-sm { min-height: 38px; padding: 8px 13px; font-size: 13.5px; border-radius: var(--r-sm); }

/* A full-width primary action, the shape used at the end of every form. */
.btn-go { width: 100%; min-height: 50px; font-size: 16px; }

.spinner {
  width: 15px; height: 15px; flex: none;
  border: 2px solid rgba(255, 255, 255, .4); border-top-color: #fff;
  border-radius: 50%; animation: spin .7s linear infinite;
}
.spinner-dark { border-color: rgba(31, 58, 138, .25); border-top-color: var(--brand-700); }
@keyframes spin { to { transform: rotate(360deg); } }

/* ==========================================================================
   Messages
   ========================================================================== */

.msg {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 12px 14px; margin-bottom: 16px;
  border: 1px solid transparent; border-radius: var(--r);
  font-size: 14px; line-height: 1.5;
}
.msg .ic { flex: none; font-size: 15px; line-height: 1.35; }
.msg-bad  { background: var(--bad-100);  border-color: var(--bad-line);  color: var(--bad-700); }
.msg-ok   { background: var(--ok-100);   border-color: var(--ok-line);   color: var(--ok-700); }
.msg-warn { background: var(--warn-100); border-color: var(--warn-line); color: var(--warn-700); }
.msg-info { background: var(--info-100); border-color: var(--info-line); color: var(--info-700); }

/* The card shakes once on a failure. Movement is noticed where a colour change
   alone can be missed, particularly on a small screen held at arm's length. */
.shake { animation: shake .3s ease-in-out; }
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-5px); }
  75% { transform: translateX(5px); }
}

/* ==========================================================================
   App shell
   ========================================================================== */

.app { min-height: 100dvh; padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom)); }

.topbar {
  position: sticky; top: 0; z-index: 50;
  height: var(--header-h);
  display: flex; align-items: center; gap: 12px;
  padding: 0 clamp(12px, 4vw, 20px);
  background: rgba(255, 255, 255, .92);
  backdrop-filter: saturate(180%) blur(10px);
  border-bottom: 1px solid var(--line);
}
.topbar .brand { display: flex; align-items: center; gap: 9px; min-width: 0; }
.topbar .brand .mark {
  width: 32px; height: 32px; flex: none; border-radius: 9px;
  background: linear-gradient(140deg, var(--brand-700), var(--brand-900));
  display: grid; place-items: center;
}
.topbar .brand svg { width: 20px; height: 20px; display: block; }
.topbar .brand b { font-size: 15px; color: var(--ink); }
.topbar .brand span { display: block; font-size: 11.5px; color: var(--muted); }

.avatar {
  width: 34px; height: 34px; flex: none; border-radius: 50%;
  display: grid; place-items: center; overflow: hidden;
  font-size: 13px; font-weight: 700; color: #fff;
  background: linear-gradient(145deg, var(--brand-500), var(--brand-800));
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar-lg { width: 62px; height: 62px; font-size: 22px; }

.main { padding: 16px clamp(12px, 4vw, 20px) 28px; }

/* ==========================================================================
   Bottom tab bar - the primary navigation on a phone
   ========================================================================== */

.tabbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
  height: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
  padding-bottom: env(safe-area-inset-bottom);
  display: grid; grid-template-columns: repeat(5, 1fr);
  background: rgba(255, 255, 255, .96);
  backdrop-filter: saturate(180%) blur(10px);
  border-top: 1px solid var(--line);
}
.tabbar button {
  position: relative;
  border: 0; background: none; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  padding: 6px 2px;
  font-size: 10.5px; font-weight: 600; color: var(--muted);
}
.tabbar button .ic { font-size: 19px; line-height: 1; }
.tabbar button.is-active { color: var(--brand-600); }
.tabbar button:focus-visible { outline: 2px solid var(--brand-500); outline-offset: -2px; }
/* A dot rather than a number: the count is on the screen itself, and a badge
   that has to fit two digits at this size becomes unreadable. */
.tabbar button .dot {
  position: absolute; top: 7px; right: calc(50% - 16px);
  width: 8px; height: 8px; border-radius: 50%;
  background: #e0334f; border: 1.5px solid #fff;
}

/* ==========================================================================
   Cards
   ========================================================================== */

.card {
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-lg); box-shadow: var(--shadow-1);
  overflow: hidden;
}
.card + .card { margin-top: 14px; }
.card-head {
  display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-start;
  padding: 15px 16px; border-bottom: 1px solid var(--line-soft);
}
.card-head h3 { font-size: 15px; }
.card-head p { font-size: 12.5px; color: var(--muted); margin: 3px 0 0; }
/* flex-basis 0 so the title never claims space while the row is being measured -
   otherwise a pill beside it wraps to its own line and sits there stranded. */
.card-head .titles { flex: 1 1 0; min-width: 0; }
.card-head .pill { margin-left: auto; flex: none; }
.card-body { padding: 16px; }
.card-foot {
  padding: 13px 16px; border-top: 1px solid var(--line-soft);
  background: #fcfdff;
  display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
}

.page-head { margin-bottom: 16px; }
.page-head h1 { font-size: clamp(19px, 5vw, 23px); margin-bottom: 4px; }
.page-head p { font-size: 13.5px; color: var(--muted); }

/* ==========================================================================
   The hero card - what I owe, and where it stands
   ========================================================================== */

.due {
  position: relative; overflow: hidden;
  border-radius: var(--r-lg); padding: clamp(18px, 5vw, 24px);
  color: #fff; margin-bottom: 16px;
  box-shadow: 0 14px 34px -18px rgba(20, 34, 92, .8);
}
.due::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background-image:
    radial-gradient(420px 180px at 90% -30%, rgba(255, 255, 255, .20), transparent 70%),
    radial-gradient(300px 160px at 8% 130%, rgba(255, 255, 255, .12), transparent 70%);
}
.due > * { position: relative; z-index: 1; }
/* One colour per state, so the answer registers before a word is read. */
.due.is-due      { background: linear-gradient(120deg, #1b2e78, #2b4fc7); }
.due.is-overdue  { background: linear-gradient(120deg, #8a1c2c, #c0392b); }
.due.is-pending  { background: linear-gradient(120deg, #8a5300, #c07b18); }
.due.is-approved { background: linear-gradient(120deg, #0f6b4a, #16a34a); }
.due.is-rejected { background: linear-gradient(120deg, #7a1230, #b3253f); }

.due .when { font-size: 12.5px; text-transform: uppercase; letter-spacing: .06em; opacity: .85; }
.due .amount {
  font-size: clamp(30px, 9vw, 40px); font-weight: 700; letter-spacing: -1px;
  margin: 6px 0 2px; font-variant-numeric: tabular-nums;
}
.due .state { font-size: 15px; font-weight: 600; }
.due .note {
  margin-top: 12px; padding: 10px 12px;
  background: rgba(255, 255, 255, .15); border-radius: var(--r-sm);
  font-size: 13.5px; line-height: 1.5;
}
.due .actions { margin-top: 16px; display: grid; gap: 9px; }
@media (min-width: 420px) {
  /* Two actions share a row once there is room for both labels in full. */
  .due .actions.two { grid-template-columns: 1fr 1fr; }
}
.due .btn-onhero { background: rgba(255, 255, 255, .95); color: var(--brand-800); border: 0; }
.due .btn-onhero:hover:not(:disabled) { background: #fff; }
.due .btn-onhero-ghost {
  background: rgba(255, 255, 255, .14); color: #fff;
  border: 1.5px solid rgba(255, 255, 255, .45);
}
.due .btn-onhero-ghost:hover:not(:disabled) { background: rgba(255, 255, 255, .24); }

/* ==========================================================================
   Pills
   ========================================================================== */

.pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px; border-radius: 999px;
  font-size: 12px; font-weight: 700; white-space: nowrap;
  background: var(--line-soft); color: var(--ink-soft); border: 1px solid var(--line);
}
.pill.ok   { background: var(--ok-100);   color: var(--ok-700);   border-color: var(--ok-line); }
.pill.warn { background: var(--warn-100); color: var(--warn-700); border-color: var(--warn-line); }
.pill.bad  { background: var(--bad-100);  color: var(--bad-700);  border-color: var(--bad-line); }
.pill.info { background: var(--info-100); color: var(--info-700); border-color: var(--info-line); }
.pill.brand{ background: var(--brand-50); color: var(--brand-700); border-color: var(--brand-100); }

/* ==========================================================================
   Lists - used instead of tables, because a table on a phone is a scroll bar
   ========================================================================== */

.rows { display: flex; flex-direction: column; }
.row {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 13px 16px; border-bottom: 1px solid var(--line-soft);
  text-align: left; width: 100%;
  background: none; border-left: 0; border-right: 0; border-top: 0;
}
.rows > .row:last-child { border-bottom: 0; }
button.row { cursor: pointer; font: inherit; color: inherit; }
button.row:hover { background: #fafbff; }
button.row:focus-visible { outline: 2px solid var(--brand-500); outline-offset: -2px; }
.row .lead { flex: 1; min-width: 0; }
.row .lead b { display: block; font-size: 14.5px; color: var(--ink); font-weight: 600; }
.row .lead span { display: block; font-size: 12.5px; color: var(--muted); margin-top: 2px; }
.row .trail { flex: none; text-align: right; display: grid; gap: 5px; justify-items: end; }
.row .trail .figure { font-size: 14.5px; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }

.empty { padding: 34px 20px; text-align: center; color: var(--muted); }
.empty .ic { font-size: 30px; display: block; margin-bottom: 10px; opacity: .5; }
.empty b { display: block; color: var(--ink); margin-bottom: 5px; font-size: 15px; }

/* Two facts side by side - rent and advance, room and floor. */
.facts { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; background: var(--line-soft); }
.facts > div { background: var(--card); padding: 14px 16px; }
.facts .k { font-size: 11.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.facts .v { font-size: 16px; font-weight: 700; color: var(--ink); margin-top: 4px; }
.facts .v.small { font-size: 14px; font-weight: 600; }

/* ==========================================================================
   Modal
   ========================================================================== */

.modal-root { position: fixed; inset: 0; z-index: 100; display: grid; place-items: end center; }
.modal-root[hidden] { display: none; }
.modal-veil { position: absolute; inset: 0; background: rgba(15, 23, 42, .5); backdrop-filter: blur(2px); }
.modal {
  position: relative; width: 100%; max-width: 520px;
  max-height: 92dvh; display: flex; flex-direction: column;
  background: var(--card);
  /* A sheet rising from the bottom, which is where a thumb already is. Becomes a
     centred dialog on a larger screen. */
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  box-shadow: var(--shadow-3);
  animation: sheet .22s cubic-bezier(.2, .8, .2, 1);
}
@keyframes sheet { from { transform: translateY(100%); } }
.modal-head {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 16px 18px; border-bottom: 1px solid var(--line-soft);
}
.modal-head h3 { font-size: 16.5px; }
.modal-head p { font-size: 13px; color: var(--muted); margin: 4px 0 0; }
.modal-body { padding: 18px; overflow-y: auto; -webkit-overflow-scrolling: touch; }
.modal-foot {
  padding: 14px 18px calc(14px + env(safe-area-inset-bottom));
  border-top: 1px solid var(--line-soft); background: #fcfdff;
  display: flex; flex-wrap: wrap; gap: 10px;
}
.modal-foot .btn { flex: 1 1 100%; }
.icon-btn {
  width: 38px; height: 38px; flex: none; display: grid; place-items: center;
  border: 1.5px solid var(--line); border-radius: 10px; background: var(--card);
  cursor: pointer; color: var(--ink-soft); font-size: 17px;
}
.icon-btn:hover { background: var(--brand-50); border-color: #c6d1ea; color: var(--brand-700); }

@media (min-width: 560px) {
  .modal-root { place-items: center; padding: 20px; }
  .modal { border-radius: var(--r-lg); animation: rise .2s cubic-bezier(.2, .8, .2, 1); }
  @keyframes rise { from { opacity: 0; transform: translateY(14px) scale(.98); } }
  .modal-foot { justify-content: flex-end; }
  .modal-foot .btn { flex: 0 1 auto; }
}

/* ==========================================================================
   Toasts
   ========================================================================== */

.toast-stack {
  position: fixed; z-index: 200; left: 12px; right: 12px;
  bottom: calc(var(--tabbar-h) + 12px + env(safe-area-inset-bottom));
  display: flex; flex-direction: column; gap: 8px; pointer-events: none;
}
.toast {
  pointer-events: auto;
  display: flex; gap: 10px; align-items: flex-start;
  padding: 12px 14px; border-radius: var(--r);
  background: var(--ink); color: #fff; font-size: 14px; line-height: 1.45;
  box-shadow: var(--shadow-2);
  animation: toast-in .2s ease-out;
}
@keyframes toast-in { from { opacity: 0; transform: translateY(8px); } }
.toast.is-out { opacity: 0; transition: opacity .2s; }
.toast.ok   { background: #0f6b4a; }
.toast.bad  { background: #a52034; }
.toast.warn { background: #8a5300; }
.toast b { display: block; }
.toast .x {
  flex: none; border: 0; background: none; color: inherit;
  font-size: 18px; line-height: 1; cursor: pointer; opacity: .8; padding: 0 2px;
}
@media (min-width: 560px) {
  .toast-stack { left: auto; right: 16px; bottom: 16px; max-width: 360px; }
}

/* ==========================================================================
   Skeletons - shown while the first request is in flight
   ========================================================================== */

.skeleton {
  height: 13px; border-radius: 6px;
  background: linear-gradient(90deg, #eef1f8 25%, #e2e7f3 37%, #eef1f8 63%);
  background-size: 400% 100%;
  animation: shimmer 1.3s ease-in-out infinite;
}
@keyframes shimmer { 0% { background-position: 100% 0; } 100% { background-position: 0 0; } }

/* ==========================================================================
   Larger screens
   ========================================================================== */

@media (min-width: 820px) {
  /* The tab bar becomes a row under the header: a fixed bottom bar on a wide
     screen puts the navigation as far from the pointer as it can get. */
  .app { padding-bottom: 28px; }
  .tabbar {
    position: sticky; top: var(--header-h); bottom: auto;
    height: auto; padding: 0;
    grid-template-columns: repeat(5, max-content);
    gap: 4px;
    justify-content: center;
    border-top: 0; border-bottom: 1px solid var(--line);
  }
  .tabbar button {
    flex-direction: row; gap: 8px; padding: 13px 18px;
    font-size: 13.5px; border-bottom: 2px solid transparent;
  }
  .tabbar button .ic { font-size: 16px; }
  .tabbar button.is-active { border-bottom-color: var(--brand-600); }
  .tabbar button .dot { top: 10px; right: 8px; }

  .main { max-width: 760px; margin: 0 auto; padding: 24px 24px 48px; }
  .toast-stack { bottom: 16px; }
}

@media (min-width: 1100px) {
  .main { max-width: 860px; }
  /* Two columns once there is genuinely room for two readable ones. */
  .cols { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; align-items: start; }
  .cols > .card + .card { margin-top: 0; }
}

/* Touch devices: 44px is the accepted minimum target. */
@media (hover: none) and (pointer: coarse) {
  .btn { min-height: 46px; }
  .btn-sm { min-height: 40px; }
  .row { padding-top: 15px; padding-bottom: 15px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

@media print {
  .topbar, .tabbar, .toast-stack, .modal-root, .btn { display: none !important; }
  body { background: #fff; }
  .card { box-shadow: none; border-color: #ccc; }
}

/* ==========================================================================
   Complaint tracking - the stages a reported problem moves through

   Used on both sides: the tenant sees where theirs is up to, the Owner tracks
   every one. Same words and same colours in both places, deliberately, so a
   conversation about "the late one" means the same thing to everybody.
   ========================================================================== */

.stage-pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px 4px 8px; border-radius: 999px;
  font-size: 12px; font-weight: 700; white-space: nowrap;
  border: 1px solid transparent;
}
.stage-pill::before {
  content: ""; width: 7px; height: 7px; border-radius: 50%;
  background: currentColor; flex: none;
}
/* Nobody has looked at it yet. Grey, not red: it may be an hour old. */
.stage-waiting     { background: var(--line-soft); color: var(--ink-soft); border-color: var(--line); }
/* Seen, with a date still ahead. */
.stage-in_progress { background: var(--info-100); color: var(--info-700); border-color: var(--info-line); }
/* Seen, promised, and the date has gone. The only one that is red, because it is
   the only one where the property has broken its word. */
.stage-late        { background: var(--bad-100); color: var(--bad-700); border-color: var(--bad-line); }
.stage-resolved    { background: var(--ok-100); color: var(--ok-700); border-color: var(--ok-line); }
.stage-closed      { background: var(--line-soft); color: var(--muted); border-color: var(--line); }

/* The promised date, shown as a small block rather than a line of prose - it is
   the thing being looked for, so it should be findable without reading. */
.eta {
  /*
   * inline-flex with a cap, not a full-width block.
   *
   * As a block it stretched the whole row on a laptop - a 700px pale blue bar
   * announcing one date, which read as a banner rather than as a detail attached to
   * the row above it.
   */
  display: inline-flex; gap: 10px; align-items: center;
  max-width: 100%;
  margin-top: 10px; padding: 9px 13px 9px 11px;
  border-radius: var(--r); border: 1px solid var(--info-line);
  background: var(--info-100); color: var(--info-700);
}
.eta.is-late { background: var(--bad-100); border-color: var(--bad-line); color: var(--bad-700); }
.eta .ic { font-size: 16px; flex: none; }
.eta .k { font-size: 11px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; opacity: .85; }
.eta .v { font-size: 15px; font-weight: 700; }

/* Proof of the repair. Bounded height so a portrait photo from a phone does not
   push everything else off the screen. */
.fixed-shot {
  margin-top: 10px; border-radius: var(--r); overflow: hidden;
  border: 1px solid var(--ok-line); background: var(--ok-100);
}
.fixed-shot img { display: block; width: 100%; max-height: 260px; object-fit: cover; }
.fixed-shot .cap {
  padding: 8px 11px; font-size: 12.5px; font-weight: 600; color: var(--ok-700);
  display: flex; align-items: center; gap: 7px;
}

/* ==========================================================================
   The problems table - the Owner's tracking view

   A table, because this one is genuinely tabular and is read on a laptop. It
   becomes a card list below 760px rather than growing a horizontal scroll bar,
   which is the usual way a table ruins a phone.
   ========================================================================== */

.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
table.grid { width: 100%; border-collapse: collapse; font-size: 13.5px; }
table.grid th, table.grid td {
  padding: 11px 13px; text-align: left; vertical-align: top;
  border-bottom: 1px solid var(--line-soft);
}
table.grid th {
  font-size: 11.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--muted); background: #fbfcff; white-space: nowrap;
  position: sticky; top: 0; z-index: 1;
}
table.grid tbody tr:last-child td { border-bottom: 0; }
table.grid tbody tr:hover { background: #fafbff; }
table.grid td b { color: var(--ink); font-weight: 600; }
table.grid .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
/*
 * The figure does not wrap. The notes under it do.
 *
 * nowrap on the whole cell meant a sub-line like "fixed 22 Sept 2026 by Ramesh Babu"
 * held the column open at 248px on its own - a quarter of the table for one sentence -
 * and pushed the last column off the screen. The number itself is what needs to stay
 * on one line; the prose beneath it never did.
 */
table.grid .num > div { white-space: normal; }
/* A red left edge on a row whose promise has passed. Visible while scanning a
   column of dates, which a pill in the last cell is not. */
table.grid tr.is-late td:first-child { box-shadow: inset 3px 0 0 var(--bad-700); }

/*
 * Cards below 1000px, tables at or above it.
 *
 * The six columns need about 930px of content once their minimums and cell padding
 * are added up. The line was at 900px, which is less than that - so at exactly 900
 * the table rendered and then overflowed, putting Cost off the right-hand edge behind
 * a scroll bar nobody knew to look for. 1000px means an iPad in landscape gets the
 * table and everything narrower gets a card per row, which reads better anyway than
 * a table you cannot see the end of.
 */
@media (max-width: 999px) {
  /* Each row becomes its own card, with the column name as a label. */
  table.grid thead { display: none; }
  table.grid, table.grid tbody, table.grid tr, table.grid td { display: block; width: 100%; }
  table.grid tr {
    border: 1px solid var(--line); border-radius: var(--r);
    margin-bottom: 10px; padding: 4px 0; background: var(--card);
  }
  table.grid tr.is-late { border-left: 3px solid var(--bad-700); }
  table.grid tr.is-late td:first-child { box-shadow: none; }
  table.grid td { border: 0; padding: 7px 13px; display: flex; gap: 12px; }
  table.grid td::before {
    content: attr(data-label);
    flex: 0 0 38%; font-size: 11.5px; font-weight: 700; letter-spacing: .04em;
    text-transform: uppercase; color: var(--muted);
  }
  /*
   * A flex item will not shrink below its content by default, so a long problem
   * title or a nested .small div pushed past the right edge of a 320px screen -
   * the cell wrapped, the child inside it did not. min-width:0 is what lets it.
   */
  table.grid td > * { min-width: 0; overflow-wrap: anywhere; }
  table.grid td { min-width: 0; }
  table.grid td.num { text-align: left; }
  /*
   * A button in a cell takes the width it is given.
   *
   * .btn is nowrap with generous padding, so "Record what you did" sat wider than the
   * 62% of a 360px screen left over after the label - and pushed past the right edge.
   * Letting it wrap and fill the cell is the fix; shrinking the type is not.
   */
  table.grid td .btn { width: 100%; white-space: normal; }
}

@media (min-width: 1000px) {
  table.grid th:nth-child(1), table.grid td:nth-child(1) { min-width: 200px; }
  table.grid th:nth-child(2), table.grid td:nth-child(2) { min-width: 116px; white-space: nowrap; }
}

/* ==========================================================================
   THE PROFILE SHEET

   Rises from the bottom bar, where the button that opened it is.
   ========================================================================== */

.sheet-root { position: fixed; inset: 0; z-index: 110; display: grid; place-items: end center; }
.sheet-root[hidden] { display: none; }
.sheet-veil { position: absolute; inset: 0; background: rgba(15, 23, 42, .5); backdrop-filter: blur(2px); }

.sheet {
  position: relative; width: 100%; max-width: 520px;
  max-height: 92dvh; overflow-y: auto; -webkit-overflow-scrolling: touch;
  background: var(--card);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  box-shadow: var(--shadow-3);
  padding-bottom: calc(10px + env(safe-area-inset-bottom));
  animation: sheet .22s cubic-bezier(.2, .8, .2, 1);
}
/* The grab handle. Does nothing, and it does not pretend to be draggable - it is
   the conventional signal that this panel came from the bottom edge. */
.sheet-grip {
  width: 38px; height: 4px; border-radius: 999px; background: var(--line);
  margin: 9px auto 4px;
}

.sheet-head {
  display: flex; gap: 13px; align-items: center;
  padding: 12px 16px 16px; border-bottom: 1px solid var(--line-soft);
}
.sheet-head b { display: block; font-size: 16.5px; color: var(--ink); }
.sheet-head span { display: block; font-size: 13px; color: var(--muted); }
.sheet-head .faint { font-size: 12.5px; overflow-wrap: anywhere; }

/* Rent and room at a glance, so the commonest reason for opening this needs no
   further tap. */
.sheet-facts {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 1px; background: var(--line-soft);
  border-bottom: 1px solid var(--line-soft);
}
.sheet-facts > div { background: var(--card); padding: 12px 10px; text-align: center; }
.sheet-facts .k {
  font-size: 10.5px; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--muted);
}
.sheet-facts .v { font-size: 15px; font-weight: 700; color: var(--ink); margin-top: 3px; }

.sheet-items { padding: 8px 0; }
.sheet-item {
  display: flex; align-items: center; gap: 13px; width: 100%;
  padding: 12px 16px; border: 0; background: none; cursor: pointer;
  text-align: left; text-decoration: none; color: inherit; font: inherit;
}
.sheet-item:hover { background: #fafbff; }
.sheet-item:focus-visible { outline: 2px solid var(--brand-500); outline-offset: -2px; }
.sheet-item b { display: block; font-size: 14.5px; color: var(--ink); font-weight: 600; }
.sheet-item span span, .sheet-item .grow span { display: block; font-size: 12.5px; color: var(--muted); margin-top: 1px; }
.sheet-item .chev { flex: none; font-size: 20px; color: var(--faint); line-height: 1; }
.sheet-item.is-bad b { color: var(--bad-700); }

/* A tinted tile per item. Colour here is doing real work: it makes a list of six
   similar rows scannable by shape rather than by reading each label. */
.sheet-item .si {
  flex: none; width: 38px; height: 38px; border-radius: 11px;
  display: grid; place-items: center; font-size: 17px;
  background: var(--line-soft); color: var(--ink-soft);
}
.si.i-room    { background: var(--brand-50);  color: var(--brand-700); }
.si.i-contact { background: var(--info-100);  color: var(--info-700); }
.si.i-photo   { background: #ede9fe;          color: #6d28d9; }
.si.i-device  { background: var(--ok-100);    color: var(--ok-700); }
.si.i-move    { background: var(--warn-100);  color: var(--warn-700); }
.si.i-star    { background: #fff4d6;          color: #8a5300; }
.si.i-out     { background: var(--bad-100);   color: var(--bad-700); }

.sheet-foot {
  margin: 0; padding: 4px 18px 8px;
  font-size: 12px; color: var(--faint); line-height: 1.5;
}

@media (min-width: 560px) {
  .sheet-root { place-items: center; padding: 20px; }
  .sheet { border-radius: var(--r-lg); animation: rise .2s cubic-bezier(.2, .8, .2, 1); }
  .sheet-grip { display: none; }
}

/* The avatar in the tab bar. Replaces the icon glyph in that one slot, so it has
   to override .tabbar button .ic rather than sit beside it. */
.tabbar .tab-profile .ic {
  width: 23px; height: 23px; border-radius: 50%; overflow: hidden;
  display: grid; place-items: center;
  font-size: 10px; font-weight: 700; color: #fff;
  background: linear-gradient(145deg, var(--brand-500), var(--brand-800));
}
.tabbar .tab-profile .ic img { width: 100%; height: 100%; object-fit: cover; }
.tabbar .tab-profile.is-open { color: var(--brand-600); }

/* ==========================================================================
   Screen furniture added by the redesign
   ========================================================================== */

/*
 * A coloured header strip on a card.
 *
 * The portal was six white cards on a grey background, which is legible and
 * completely flat - nothing tells you which card matters. A tinted head costs one
 * class and makes a screen scannable at arm's length.
 */
.card-head.tinted { border-bottom-color: transparent; }
.card-head.tinted h3 { color: var(--tint-ink, var(--ink)); }
.card-head.tinted p { color: var(--tint-ink, var(--muted)); opacity: .82; }
.card.t-brand .card-head.tinted { background: var(--brand-50); --tint-ink: var(--brand-700); }
.card.t-ok    .card-head.tinted { background: var(--ok-100);   --tint-ink: var(--ok-700); }
.card.t-warn  .card-head.tinted { background: var(--warn-100); --tint-ink: var(--warn-700); }
.card.t-bad   .card-head.tinted { background: var(--bad-100);  --tint-ink: var(--bad-700); }
.card.t-info  .card-head.tinted { background: var(--info-100); --tint-ink: var(--info-700); }

/* Quick actions under the hero - the two or three things somebody opens the app
   to do, rather than making them find the tab first. */
.quick { display: grid; gap: 10px; grid-template-columns: repeat(2, minmax(0, 1fr)); margin-bottom: 16px; }
@media (min-width: 560px) { .quick { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.quick button {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 7px;
  padding: 15px 8px; cursor: pointer;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--shadow-1);
  font-size: 12.5px; font-weight: 600; color: var(--ink-soft); line-height: 1.3;
  text-align: center;
  transition: transform .08s, border-color .15s;
}
.quick button:active { transform: translateY(1px); }
.quick button:hover { border-color: #cdd6ea; }
.quick button:focus-visible { outline: 2px solid var(--brand-500); outline-offset: 2px; }
.quick .qi {
  width: 38px; height: 38px; border-radius: 12px; display: grid; place-items: center;
  font-size: 18px; background: var(--brand-50); color: var(--brand-700);
}
.quick .q-pay  .qi { background: var(--ok-100);   color: var(--ok-700); }
.quick .q-fix  .qi { background: var(--warn-100); color: var(--warn-700); }
.quick .q-rec  .qi { background: var(--info-100); color: var(--info-700); }
.quick .q-note .qi { background: #ede9fe;         color: #6d28d9; }

/* Progress through the year, on the home screen. A count of paid months is a
   fact; a bar is an impression, and the impression is what makes it stick. */
.meter { margin-top: 12px; }
.meter .bar {
  height: 9px; border-radius: 999px; background: var(--line-soft);
  overflow: hidden; display: flex;
}
.meter .bar i { display: block; height: 100%; }
.meter .bar .paid { background: linear-gradient(90deg, #16a34a, #0f6b4a); }
.meter .bar .wait { background: linear-gradient(90deg, #d69428, #8a5300); }
.meter .bar .owed { background: linear-gradient(90deg, #d14257, #a52034); }
.meter .key { display: flex; flex-wrap: wrap; gap: 4px 14px; margin-top: 9px; font-size: 12px; color: var(--muted); }
.meter .key span { display: inline-flex; align-items: center; gap: 6px; }
.meter .key i { width: 9px; height: 9px; border-radius: 50%; flex: none; }

/* A filter row. Chips rather than a select, because there are only ever three or
   four and a chip shows the current state without being opened. */
.chips {
  display: flex; gap: 7px; overflow-x: auto; -webkit-overflow-scrolling: touch;
  padding: 2px 0 4px; margin-bottom: 12px; scrollbar-width: none;
}
.chips::-webkit-scrollbar { display: none; }
.chip {
  flex: none; padding: 7px 13px; border-radius: 999px; cursor: pointer;
  font-size: 13px; font-weight: 600; white-space: nowrap;
  background: var(--card); color: var(--ink-soft);
  border: 1.5px solid var(--line);
}
.chip:hover { border-color: #cdd6ea; }
.chip.is-on { background: var(--brand-600); color: #fff; border-color: var(--brand-600); }
.chip:focus-visible { outline: 2px solid var(--brand-500); outline-offset: 2px; }
.chip .n { opacity: .75; font-weight: 700; margin-left: 4px; }

/* A search field with a magnifier that is decoration, not a button. */
.search { position: relative; margin-bottom: 12px; }
.search input { padding-left: 38px; }
.search .mag {
  position: absolute; left: 13px; top: 50%; transform: translateY(-50%);
  font-size: 15px; color: var(--faint); pointer-events: none;
}

/* The Google review card. Warm, and deliberately unlike everything else on the
   screen: it is the one thing here that asks the tenant for something. */
.review-card {
  border: 1px solid #f2dfae; border-radius: var(--r-lg); overflow: hidden;
  background: linear-gradient(135deg, #fffaf0, #fff4d9);
  box-shadow: var(--shadow-1);
}
.review-card .inner { padding: 18px 16px; display: flex; gap: 14px; align-items: flex-start; }
.review-card .stars { flex: none; font-size: 21px; line-height: 1; letter-spacing: 1px; }
.review-card h3 { font-size: 15.5px; color: #6b4708; margin-bottom: 5px; }
.review-card p { font-size: 13.5px; color: #84601a; margin: 0 0 13px; line-height: 1.55; }
.review-card .btn {
  background: #8a5300; color: #fff; border: 0;
  min-height: 42px; font-size: 14px;
}
.review-card .btn:hover { background: #6b4708; }

/*
 * Column widths for the problems table.
 *
 * Without them the browser gives every column an equal share, which wraps
 * "Ravi Shankar" onto two lines while the Problem column has room to spare. These
 * are minimums rather than fixed widths, so the Problem column still takes up the
 * slack on a wide screen. Only applied at table widths - below 760px the same
 * markup is a card list and none of this is in play.
 */
@media (min-width: 1000px) {
  /* Stage, Acknowledged, Repair date, Cost. Minimums only - the Problem column
     takes the slack on a wide screen. */
  table.grid th:nth-child(3), table.grid td:nth-child(3) { min-width: 116px; }
  table.grid th:nth-child(4), table.grid td:nth-child(4) { min-width: 126px; }
  table.grid th:nth-child(5), table.grid td:nth-child(5) { min-width: 100px; }
  table.grid th:nth-child(6), table.grid td:nth-child(6) { min-width: 84px; }
}

/*
 * Escalated: the acknowledgement deadline went by with nobody replying.
 *
 * Deeper than the "past its date" red, and it needs to be. A slipped repair date is a
 * delay; this is a tenant who was not answered at all, and it is the one state on the
 * screen that implicates the property rather than the plumbing.
 */
.stage-escalated {
  background: #7a1226; color: #fff; border-color: #7a1226;
}
.stage-escalated::before { background: #ffd0d8; }

/* Six tiles rather than four, once the deadline and the spend are in there. */
@media (min-width: 700px) {
  .cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; align-items: start; }
  .cols > .card + .card { margin-top: 0; }
}
@media (min-width: 1100px) {
  .cols { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ==========================================================================
   The staff console reads wider than the tenant portal

   The tenant portal is capped at 760px because it is read a card at a time on a
   phone. The console has a six column table in it whose own minimums come to about
   900px, so that cap made it overflow at every laptop width below 1280 - the Cost
   column sat off the right-hand edge behind a scroll bar nobody knew to look for.
   ========================================================================== */

@media (min-width: 820px) {
  body.staff-console .main { max-width: 1180px; }
}

/* ==========================================================================
   LANDING PAGE — v3 "modern showcase"
   Aurora hero, glass header, scroll-reveal, stats, visual feature rows,
   testimonial, FAQ, bold closing, compact footer. 320px → wide desktop.
   ========================================================================== */

/*
 * Colorful, not black.
 *
 * The page used to sit on near-black (#05010f). It now runs on a deep but
 * genuinely colourful gradient - indigo into violet into a teal-tinged base -
 * fixed behind the content so every section shares one continuous wash rather
 * than a set of flat dark panels. Individual sections are mostly transparent so
 * this shows through; a few use a translucent tint for separation. White text
 * still clears WCAG AA on every stop of this gradient.
 */
body.landing {
  color: #eaf0ff;
  /*
   * A deep, colourful base that covers the whole document, not just the first
   * screen. It is painted here AND repeated on the sections below (each section
   * carries its own translucent tint) so there is never a white band showing
   * through - the earlier version used background-attachment: fixed, which the
   * browser clips to the viewport, leaving the lower sections on the pale app
   * background. This scrolls with the page and reaches the full height.
   */
  background: #140f38;
  background-image:
    radial-gradient(90% 55% at 12% 0%, rgba(78, 56, 190, .85) 0%, transparent 60%),
    radial-gradient(80% 50% at 95% 8%, rgba(20, 130, 160, .7) 0%, transparent 55%),
    radial-gradient(90% 60% at 85% 100%, rgba(140, 45, 165, .7) 0%, transparent 55%),
    linear-gradient(180deg, #191046 0%, #140d38 45%, #0f1046 100%);
}
body.landing #main { display: block; background: transparent; }
/* Decorative bleeding layers (auroras, floating phone) must never widen the
   document. Clip on the body only.

   Deliberately NOT on the root <html>: setting overflow-x on the document
   element forces the browser to compute overflow-y to a scroll container on
   <html> too, which - together with overscroll-behavior on the body and the
   sticky header - left the page feeling frozen on touch devices (it scrolled in
   a headless desktop but not under a finger). The body is a normal block box, so
   clipping its x-overflow trims the decorative bleed without touching the
   document's own vertical scroll. Each section also clips its own overflow, so
   this is the backstop, not the only guard. */
body.landing {
  overflow-x: hidden;
  /* The app shell sets overscroll-behavior-y:none to kill iOS rubber-banding,
     but on this tall marketing page that made momentum scrolling feel like it
     stopped dead. The landing page is a normal document scroll, so let it behave
     like one. */
  overscroll-behavior-y: auto;
  /* No height clamp: the document is as tall as its content and scrolls with the
     page. Guards against any inherited min-height/height turning body into a
     fixed-size box. */
  height: auto;
  min-height: 100%;
}

/* Scroll-reveal: rise + fade once, gently. Honoured only where motion is fine;
   the script adds .in immediately under prefers-reduced-motion. */
.reveal { opacity: 0; transform: translateY(24px); transition: opacity .7s ease, transform .7s cubic-bezier(.2,.7,.2,1); }
.reveal.in { opacity: 1; transform: none; }

/* ------------------------------- glass header ---------------------------- */
.nav {
  position: sticky; top: 0; z-index: 60;
  display: flex; align-items: center; gap: 14px;
  padding: 12px clamp(14px, 4vw, 40px);
  background: rgba(8, 6, 24, .55);
  backdrop-filter: saturate(180%) blur(16px);
  -webkit-backdrop-filter: saturate(180%) blur(16px);
  border-bottom: 1px solid rgba(255, 255, 255, .06);
  transition: background .3s, border-color .3s, box-shadow .3s;
}
.nav.scrolled { background: rgba(8, 6, 24, .82); border-bottom-color: rgba(255,255,255,.10); box-shadow: 0 10px 40px -18px rgba(0,0,0,.8); }
.nav-brand { display: flex; align-items: center; gap: 10px; text-decoration: none; min-width: 0; }
.nav-mark {
  width: 34px; height: 34px; flex: none; border-radius: 10px;
  background: linear-gradient(140deg, #3b63e8, #14225c);
  display: grid; place-items: center; box-shadow: 0 6px 18px -6px rgba(59,99,232,.7);
}
.nav-mark svg { width: 21px; height: 21px; display: block; }
.nav-who { min-width: 0; line-height: 1.15; }
.nav-who b { display: block; font-size: 15px; color: #fff; font-weight: 700; letter-spacing: -.2px; }
.nav-who b em { font-style: normal; color: #2fd8a6; }
.nav-who span { display: block; font-size: 11px; color: #97a3cf; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.nav-links { margin-left: 20px; display: flex; gap: 2px; flex: 1 1 auto; }
.nav-links a {
  font-size: 13.5px; font-weight: 600; color: #b9c3e6; text-decoration: none;
  padding: 8px 12px; border-radius: 9px; white-space: nowrap; transition: background .2s, color .2s;
}
.nav-links a:hover { color: #fff; background: rgba(255,255,255,.08); }
.nav-cta { margin-left: auto; display: flex; gap: 8px; flex: none; }

/* Glass secondary button on the dark header. */
.btn-glass { background: rgba(255,255,255,.10); color: #eaf0ff; border: 1px solid rgba(255,255,255,.22); backdrop-filter: blur(6px); }
.btn-glass:hover:not(:disabled) { background: rgba(255,255,255,.18); border-color: rgba(255,255,255,.4); }

/*
 * Phone: the in-page links used to be display:none below 780px, which is the
 * bug that hid "Features / How it works / FAQ" entirely on a phone. Instead the
 * header wraps to two rows - brand + logins on the first, the section links on
 * a centred second row - so every link stays reachable on the smallest screen.
 */
@media (max-width: 780px) {
  .nav { flex-wrap: wrap; row-gap: 8px; }
  .nav-links {
    order: 3; width: 100%; margin-left: 0;
    justify-content: center; flex-wrap: wrap; gap: 4px;
    padding-top: 8px; border-top: 1px solid rgba(255,255,255,.10);
  }
  .nav-cta { margin-left: auto; }
}
@media (max-width: 430px) {
  .nav-cta { width: auto; }
  .nav-cta .btn { flex: none; }
}

/* --------------------------------- hero ---------------------------------- */
.hx {
  position: relative; overflow: hidden;
  display: grid; gap: clamp(28px, 6vw, 56px); align-items: center;
  padding: clamp(54px, 12vw, 128px) clamp(18px, 6vw, 72px) clamp(56px, 12vw, 128px);
  background: transparent;
}
@media (min-width: 960px) { .hx { grid-template-columns: 1.1fr .9fr; } }

/* Animated aurora: two big blurred blobs drifting behind the content. */
.hx-aurora {
  position: absolute; inset: -30% 0 auto 0; height: 150%; z-index: 0; filter: blur(70px); opacity: .8;
  background:
    radial-gradient(46% 46% at 78% 20%, rgba(47,216,166,.55), transparent 70%),
    radial-gradient(50% 50% at 12% 60%, rgba(59,99,232,.6), transparent 70%),
    radial-gradient(40% 40% at 60% 80%, rgba(147,51,234,.45), transparent 70%);
  animation: drift 18s ease-in-out infinite alternate;
}
@keyframes drift {
  0%   { transform: translateY(0) scale(1); }
  100% { transform: translateY(3%) scale(1.06); }
}
/* Faint grid overlay for depth, fading toward the bottom. */
.hx-grid {
  position: absolute; inset: 0; z-index: 0; opacity: .35;
  background-image:
    linear-gradient(rgba(255,255,255,.05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.05) 1px, transparent 1px);
  background-size: 44px 44px;
  -webkit-mask-image: linear-gradient(180deg, #000 0%, transparent 78%);
          mask-image: linear-gradient(180deg, #000 0%, transparent 78%);
}
@media (prefers-reduced-motion: reduce) { .hx-aurora { animation: none; } }

.hx-inner { position: relative; z-index: 1; max-width: 620px; }
.hx-badge {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 12.5px; font-weight: 600; color: #cdd6f5;
  padding: 6px 13px; border-radius: 999px;
  background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.12);
  margin-bottom: 20px;
}
.hx-badge .dot { width: 7px; height: 7px; border-radius: 50%; background: #2fd8a6; box-shadow: 0 0 0 4px rgba(47,216,166,.22); }
.hx-title {
  color: #fff; font-weight: 800; letter-spacing: -.03em;
  font-size: clamp(34px, 8.6vw, 66px); line-height: 1.04; margin-bottom: 20px;
}
.hx-title .grad {
  background: linear-gradient(96deg, #2fd8a6, #7cf3d0 40%, #63a4ff);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.hx-sub { font-size: clamp(16px, 4.4vw, 20px); line-height: 1.55; color: #b7c1e6; max-width: 52ch; margin-bottom: 18px; }
.hx-note { font-size: 14px; color: #8f9cc6; max-width: 46ch; }
.hx-note b { color: #dfe6ff; }
.hx-trust { display: flex; flex-wrap: wrap; gap: 8px 18px; margin-top: 26px; font-size: 13px; color: #9fabd4; }
.hx-trust span { display: inline-flex; align-items: center; gap: 6px; }

/* Floating phone */
.hx-device { position: relative; z-index: 1; display: grid; place-items: center; }
/*
 * A modern iPhone (Pro-style): a titanium-like rounded frame with slim, even
 * bezels and a Dynamic Island pill floating at the top rather than the old wide
 * notch. Only the appearance changed - the cards inside are the same.
 */
.phone {
  position: relative; width: clamp(214px, 60vw, 304px); aspect-ratio: 9 / 19.5;
  border-radius: 52px; padding: 10px;
  background: linear-gradient(155deg, #2a3566 0%, #141c44 45%, #0a0f2e 100%);
  /* The thin bright rim reads as the polished metal edge. */
  border: 1px solid rgba(255,255,255,.22);
  box-shadow:
    0 60px 120px -40px rgba(0,0,0,.85),
    inset 0 0 0 2px rgba(255,255,255,.06),
    inset 0 0 0 7px #05060f,
    0 0 60px -10px rgba(59,99,232,.4);
  animation: float 6s ease-in-out infinite;
}
@keyframes float { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-14px); } }
@media (prefers-reduced-motion: reduce) { .phone { animation: none; } }
/* Dynamic Island: a free-floating black pill near the top, not a cut-out. */
.phone-notch {
  position: absolute; top: 20px; left: 50%; transform: translateX(-50%);
  width: 34%; height: 22px; background: #000; border-radius: 999px;
  z-index: 2; box-shadow: inset 0 0 0 1px rgba(255,255,255,.05);
}
.phone-scr {
  height: 100%; border-radius: 44px; padding: 46px 13px 16px;
  background: linear-gradient(180deg, #15205a, #0c1440);
  display: flex; flex-direction: column; gap: 9px;
}
.g-card { background: rgba(255,255,255,.07); border: 1px solid rgba(255,255,255,.10); border-radius: 13px; padding: 11px 12px; display: flex; flex-direction: column; gap: 5px; }
.g-due { background: rgba(47,216,166,.14); border-color: rgba(47,216,166,.34); }
.g-k { font-size: 11.5px; color: #a7b6e4; }
.g-v { font-size: 22px; font-weight: 800; color: #fff; letter-spacing: -.02em; }
.g-chip { align-self: flex-start; font-size: 10px; font-weight: 700; padding: 3px 9px; border-radius: 999px; background: rgba(255,255,255,.14); color: #dbe4ff; }
.g-chip.warn { background: #fdf0d5; color: #8a5300; }
.g-chip.ok { background: #d9f5e7; color: #0f6b4a; }
.g-chip.info { background: #ddf1fb; color: #0b5f8a; }

/* -------------------------------- stats ---------------------------------- */
.stats {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 1px;
  background: rgba(255,255,255,.08);
  border-top: 1px solid rgba(255,255,255,.08); border-bottom: 1px solid rgba(255,255,255,.08);
}
@media (min-width: 720px) { .stats { grid-template-columns: repeat(4, 1fr); } }
.stat { background: rgba(10, 8, 34, .35); padding: clamp(24px, 5vw, 40px) 16px; text-align: center; }
.stat b { display: block; font-size: clamp(28px, 6vw, 44px); font-weight: 800; letter-spacing: -.03em;
  background: linear-gradient(96deg, #2fd8a6, #63a4ff); -webkit-background-clip: text; background-clip: text; color: transparent; }
.stat span { display: block; margin-top: 6px; font-size: 13px; color: #97a3cf; }

/* ------------------------------- features -------------------------------- */
.fx { background: rgba(10, 8, 34, .22); padding: clamp(50px, 10vw, 100px) clamp(18px, 6vw, 40px); }
.fx-head { max-width: 900px; margin: 0 auto clamp(30px, 6vw, 56px); text-align: center; }
.kicker { display: inline-block; font-size: 12px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: #2fd8a6; margin-bottom: 12px; }
.fx-head h2 { color: #fff; font-size: clamp(26px, 6vw, 46px); letter-spacing: -.03em; line-height: 1.08; }

.fx-row {
  max-width: 1060px; margin: 0 auto; display: grid; gap: clamp(22px, 5vw, 52px); align-items: center;
  padding: clamp(30px, 6vw, 56px) 0;
}
.fx-row + .fx-row { border-top: 1px solid rgba(255,255,255,.07); }
@media (min-width: 860px) {
  .fx-row { grid-template-columns: 1fr 1fr; }
  .fx-row.flip .fx-text { order: 2; }
}
.fx-text .tag { display: inline-block; font-size: 12px; font-weight: 700; padding: 5px 13px; border-radius: 999px; background: var(--cb); color: var(--c); margin-bottom: 16px; }
.fx-text h3 { color: #fff; font-size: clamp(22px, 5vw, 34px); letter-spacing: -.03em; line-height: 1.12; margin-bottom: 12px; }
.fx-text p { font-size: clamp(15px, 4vw, 17.5px); line-height: 1.62; color: #a7b2d8; margin: 0; max-width: 46ch; }

.fx-art {
  position: relative; min-height: clamp(200px, 42vw, 320px); border-radius: 24px; overflow: hidden;
  display: grid; place-items: center; gap: 12px;
  background: linear-gradient(150deg, rgba(255,255,255,.07), rgba(255,255,255,.02));
  border: 1px solid rgba(255,255,255,.10);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.08);
}
/* Rent: layered mini cards */
.mini-card { width: min(78%, 300px); background: rgba(255,255,255,.9); color: #0f172a; border-radius: 16px; padding: 16px 18px; display: flex; flex-direction: column; gap: 7px; box-shadow: 0 24px 50px -20px rgba(0,0,0,.6); }
.mini-card.faded { width: min(68%, 260px); opacity: .55; transform: translateY(-8px) scale(.96); }
.mc-k { font-size: 12.5px; color: #64748b; }
.mc-v { font-size: 30px; font-weight: 800; color: #1f3a8a; letter-spacing: -.03em; }
.mc-chip { align-self: flex-start; font-size: 11px; font-weight: 700; padding: 4px 11px; border-radius: 999px; }
.mc-chip.ok { background: #d9f5e7; color: #0f6b4a; }
/* Pay: glowing phone glyph in a ring */
.a-pay .pay-glyph { font-size: clamp(60px, 15vw, 104px); z-index: 1; }
.a-pay .pay-ring { position: absolute; width: 56%; aspect-ratio: 1; border-radius: 50%; border: 2px dashed rgba(47,216,166,.5); animation: spin 22s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .a-pay .pay-ring { animation: none; } }
/* Receipt */
.receipt { position: relative; width: min(60%, 220px); background: #fff; color: #0f172a; border-radius: 8px; padding: 18px 16px; display: flex; flex-direction: column; gap: 9px; box-shadow: 0 24px 50px -20px rgba(0,0,0,.6); }
.receipt .r-top { font-size: 12px; font-weight: 800; letter-spacing: .2em; color: #334155; }
.receipt .r-line { height: 8px; border-radius: 4px; background: #e2e8f0; }
.receipt .r-line.short { width: 55%; }
.receipt .r-stamp { position: absolute; right: 12px; bottom: 12px; transform: rotate(-12deg); font-size: 13px; font-weight: 800; color: #0f6b4a; border: 2px solid #0f6b4a; border-radius: 6px; padding: 2px 8px; }
/* Fix: a little progress track */
.a-fix { flex-direction: column; }
.track { display: flex; gap: 8px; z-index: 1; flex-wrap: wrap; justify-content: center; }
.t-step { font-size: 11.5px; font-weight: 700; padding: 5px 11px; border-radius: 999px; background: rgba(255,255,255,.1); color: #b7c1e6; }
.t-step.done { background: rgba(47,216,166,.2); color: #7cf3d0; }
.t-step.now { background: #2fd8a6; color: #06281f; }
.fix-glyph { font-size: clamp(48px, 12vw, 84px); }
.notice-glyph { font-size: clamp(64px, 16vw, 116px); }

/* ------------------------------- how ------------------------------------- */
.how { background: rgba(10, 8, 34, .34); border-top: 1px solid rgba(255,255,255,.07); padding: clamp(50px, 10vw, 100px) clamp(18px, 6vw, 40px); }
.how-steps { max-width: 1000px; margin: 0 auto; padding: 0; list-style: none; display: grid; gap: 16px; }
@media (min-width: 800px) { .how-steps { grid-template-columns: repeat(3, 1fr); } }
.how-steps li { background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.09); border-radius: 20px; padding: 28px 24px; transition: transform .3s, border-color .3s; }
.how-steps li:hover { transform: translateY(-4px); border-color: rgba(47,216,166,.35); }
.how-steps .n { display: grid; place-items: center; width: 42px; height: 42px; border-radius: 50%; font-size: 17px; font-weight: 800; color: #06281f; margin-bottom: 16px; background: linear-gradient(145deg, #2fd8a6, #63a4ff); }
.how-steps b { display: block; color: #fff; font-size: 17px; margin-bottom: 8px; }
.how-steps p { color: #a7b2d8; font-size: 14.5px; line-height: 1.6; margin: 0; }

/* ---------------------------- testimonial -------------------------------- */
.quote { max-width: 900px; margin: 0 auto; text-align: center; padding: clamp(56px, 12vw, 110px) clamp(20px, 6vw, 40px); }
.quote blockquote { margin: 0; font-size: clamp(22px, 5.4vw, 36px); line-height: 1.3; letter-spacing: -.02em; color: #fff; font-weight: 600; }
.quote cite { display: block; margin-top: 22px; font-style: normal; font-size: 14px; color: #8f9cc6; }

/* -------------------------------- faq ------------------------------------ */
.faq { background: rgba(10, 8, 34, .34); border-top: 1px solid rgba(255,255,255,.07); padding: clamp(50px, 10vw, 100px) clamp(18px, 6vw, 40px); }
.faq-list { max-width: 780px; margin: 0 auto; display: grid; gap: 12px; }
.faq details { background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.09); border-radius: 16px; padding: 4px 20px; }
.faq summary { cursor: pointer; list-style: none; padding: 16px 0; font-size: 16px; font-weight: 600; color: #eaf0ff; display: flex; justify-content: space-between; align-items: center; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; font-size: 22px; color: #2fd8a6; line-height: 1; transition: transform .25s; }
.faq details[open] summary::after { transform: rotate(45deg); }
.faq details p { margin: 0 0 16px; color: #a7b2d8; font-size: 14.5px; line-height: 1.62; }
.faq details p b { color: #dfe6ff; }

/* ------------------------------ closing cta ------------------------------ */
.cta { position: relative; overflow: hidden; text-align: center; padding: clamp(64px, 14vw, 130px) clamp(20px, 6vw, 40px); }
.cta-aurora { position: absolute; inset: -40% 0 auto 0; height: 180%; z-index: 0; filter: blur(80px); opacity: .8;
  background: radial-gradient(40% 50% at 50% 20%, rgba(47,216,166,.5), transparent 70%), radial-gradient(46% 50% at 20% 80%, rgba(59,99,232,.55), transparent 70%); }
.cta-inner { position: relative; z-index: 1; max-width: 620px; margin: 0 auto; }
.cta h2 { color: #fff; font-size: clamp(30px, 8vw, 56px); letter-spacing: -.03em; margin-bottom: 16px; }
.cta p { color: #c3cdea; font-size: clamp(15px, 4vw, 18px); line-height: 1.6; margin: 0 auto; max-width: 46ch; }
.cta p b { color: #2fd8a6; }
.cta-actions { margin-top: 30px; display: flex; justify-content: center; }
.cta-fine { margin-top: 16px; font-size: 13.5px; color: #9aa6ce; }
/* A larger primary button for the hero/closing calls to action. */
.btn-lg { min-height: 54px; padding: 15px 30px; font-size: 16.5px; border-radius: 14px; }

/* -------------------------------- footer --------------------------------- */
/* Compact: one row on desktop; wordmark left, links right; a thin legal line. */
.ft { background: rgba(6, 5, 24, .5); border-top: 1px solid rgba(255,255,255,.07); }
.ft-inner {
  max-width: 1100px; margin: 0 auto;
  padding: clamp(24px, 5vw, 34px) clamp(18px, 6vw, 40px);
  display: flex; flex-wrap: wrap; gap: 18px 24px; align-items: center; justify-content: space-between;
}
/* Centred footer variant: the brand block sits in the middle rather than pushed
   to the left with links on the right. Used where there is no footer button. */
.ft-inner-center { justify-content: center; text-align: left; }
.ft-brand { display: flex; align-items: center; gap: 11px; text-decoration: none; }
.ft-brand .nav-mark { width: 34px; height: 34px; }
.ft-brand span span { display: block; }
.ft-brand b { display: block; color: #fff; font-size: 14.5px; }
.ft-brand span span:last-child { font-size: 12px; color: #8f9cc6; }
.ft-links { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 20px; }
/* Plain text links in the footer - but NOT a .btn, which must keep its own look.
   Without the :not(.btn) guard, this stripped the Contact button's padding and
   colour and it rendered as cramped bare text. */
.ft-links a:not(.btn) { font-size: 13.5px; color: #b9c3e6; text-decoration: none; padding: 4px 0; }
.ft-links a:not(.btn):hover { color: #2fd8a6; }
/* The Contact button in the footer: a clear glass pill, not a faint text link. */
.ft-links .btn { text-decoration: none; }
.ft-legal {
  border-top: 1px solid rgba(255,255,255,.06);
  padding: 14px clamp(18px, 6vw, 40px) calc(16px + env(safe-area-inset-bottom));
  display: flex; flex-wrap: wrap; gap: 4px 16px; justify-content: center;
  font-size: 12px; color: #5f6b93;
}

/*
 * Slim footer, staff-login style: a single centred row with the Contact link
 * and the legal line. Used on the tenant sign-in page - no brand block, no
 * button, everything centred.
 */
.ft.ft-slim {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 6px 16px;
  padding: 16px clamp(18px, 6vw, 40px) calc(16px + env(safe-area-inset-bottom));
  text-align: center;
}
.ft-slim .ft-contact { font-size: 13px; font-weight: 600; color: #b9c8f7; text-decoration: none; }
.ft-slim .ft-contact:hover { color: #2fd8a6; text-decoration: underline; }
.ft-slim .ft-legal-line { font-size: 12px; color: #7482ac; }

/* ==========================================================================
   Persistent copyright
   --------------------------------------------------------------------------
   Shown at the foot of every signed-in screen (the tenant app and the owner
   console), matching the sign-in page, the landing page and the staff portal so
   the copyright line is consistent everywhere. Sits inside .main, so the app's
   own padding keeps it clear of the fixed bottom tab bar.
   ========================================================================== */
.app-copyright {
  margin-top: 26px;
  padding: 16px 4px 8px;
  border-top: 1px solid var(--line);
  text-align: center;
  font-size: 12px;
  color: var(--muted);
}
