﻿/* ══════════════════════════════════════════════════════
   DESKALI — shared design system
   Palette: black · white · red · lime
   ══════════════════════════════════════════════════════ */

:root{
  --black:#0A0A0A;
  --ink:#141518;
  --ink-2:#2A2C33;
  --white:#FFFFFF;
  --ice:#F5F6F8;
  --ice-2:#EDEFF3;
  --grey:#E4E6EA;
  --grey-2:#CDD1D8;
  --muted:#5C6270;
  --muted-2:#868C99;
  --red:#E0202F;
  --red-dark:#B8121F;
  --red-tint:#FDECEE;
  --lime:#C6F03C;
  --lime-dark:#A8D122;
  --lime-tint:#F4FBDF;
  --green:#137A3A;
  --green-tint:#E8F5EC;
  --amber:#B57500;
  --amber-tint:#FDF3E2;
  --radius:14px;
  --shadow:0 1px 2px rgba(10,10,10,.04), 0 8px 28px rgba(10,10,10,.07);
  --shadow-lg:0 24px 60px rgba(10,10,10,.14);
  --sans:'Inter',system-ui,-apple-system,'Segoe UI',sans-serif;
  --serif:'Instrument Serif',Georgia,serif;
  --mono:'JetBrains Mono',ui-monospace,'SF Mono',Consolas,monospace;
}

*{box-sizing:border-box;margin:0;padding:0}

/* The `hidden` attribute is only display:none by UA default, and ANY class that
   sets display beats it — .notice, .err-msg, .demo-bar, .step all do. Without
   this, `<div class="notice" hidden>` renders regardless of what the JS thinks,
   which is how the demo strip showed in live mode and an empty red error box sat
   on the signup form. Toggling .hidden in JS is meant to work; make it work. */
[hidden]{display:none!important}

/* Sideways movement is stopped here, but NOT with overflow-x:hidden.
   overflow-x:hidden on html propagates to the viewport and makes overflow-y
   compute to auto, which turns the root into a scroll container — and a scroll
   container silently disables position:sticky in every descendant. That is the
   dashboard topbar and the calendar's day headers. overscroll-behavior-x:none
   stops the rubber-band on its own, and each page clips its own overflowing
   element (see the .shell rule in dashboard.html) rather than clipping the
   document. Clipping the document also cuts off position:fixed popups. */
html{
  scroll-behavior:smooth;
  max-width:100%;
  overscroll-behavior-x:none;
}
body{
  font-family:var(--sans);color:var(--ink);background:var(--white);
  -webkit-font-smoothing:antialiased;line-height:1.6;
  width:100%;max-width:100%;overscroll-behavior-x:none;
}
a{color:inherit;text-decoration:none}
img,svg{max-width:100%;display:block}
input,select,textarea,button{font:inherit;color:inherit}

.skip{position:absolute;left:-9999px;top:0;background:var(--lime);color:var(--black);padding:12px 20px;z-index:200;font-weight:700}
.skip:focus{left:12px;top:12px}
:focus-visible{outline:3px solid var(--lime);outline-offset:3px;border-radius:4px}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

/* ---------- BRAND ---------- */
.logo{display:flex;align-items:center;gap:11px;flex-shrink:0}
.mark{width:36px;height:36px;border-radius:9px;background:var(--red);display:grid;place-items:center;flex-shrink:0}
.mark svg{width:19px;height:19px;stroke:#fff;stroke-width:2.4;fill:none;stroke-linecap:round;stroke-linejoin:round}
.logo-t{font-weight:800;font-size:21px;letter-spacing:-.03em;line-height:1.05;display:block}
.logo-s{font-size:10px;color:var(--lime);letter-spacing:.1em;text-transform:uppercase;font-weight:600;line-height:1;display:block}
.logo-s.dark{color:var(--red)}

/* The mark is the real logo image now — kill the old drawn red square. */
img.mark{background:transparent;border-radius:8px;padding:0;object-fit:contain}
/* Mobile: the logo alone carries the brand — no wordmark text. */
@media(max-width:640px){
  .logo .logo-t,.logo .logo-s{display:none}
}


/* ---------- BUTTONS ---------- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  font-weight:650;font-size:14.5px;padding:11px 20px;border-radius:9px;
  border:1px solid transparent;cursor:pointer;letter-spacing:-.01em;white-space:nowrap;
  transition:transform .12s,background .15s,border-color .15s,color .15s;
}
.btn:active{transform:translateY(1px)}
.btn:disabled{opacity:.45;cursor:not-allowed}
.btn svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round}
.btn-red{background:var(--red);color:#fff}
.btn-red:hover:not(:disabled){background:var(--red-dark)}
.btn-lime{background:var(--lime);color:var(--black)}
.btn-lime:hover:not(:disabled){background:var(--lime-dark)}
.btn-dark{background:var(--black);color:#fff}
.btn-dark:hover:not(:disabled){background:#26272c}
/* Accept is the affirmative action on a booking; blue reads as "go ahead"
   without competing with --red, which this product already uses for its
   primary calls to action. */
.btn-blue{background:#1766db;color:#fff}
.btn-blue:hover:not(:disabled){background:#1257bb}
/* Sending a job to Appointments is a different kind of act from confirming it —
   it commits the job to the run sheet — so it gets its own colour rather than
   a second blue button the eye cannot tell apart. Not --lime: that is the
   brand accent and carries black text, which reads as a highlight, not an
   action. */
.btn-green{background:#0E9F55;color:#fff}
.btn-green:hover:not(:disabled){background:#0b8145}
/* Destructive. Deliberately NOT var(--red): that is the brand's CTA colour and
   sits on buttons people are meant to press. Danger gets its own deeper red so
   "delete" never looks like "continue". */
.btn-danger{background:#C21F2B;color:#fff}
.btn-danger:hover:not(:disabled){background:#a4141f}
.btn-outline{background:transparent;color:var(--ink);border-color:var(--grey)}
.btn-outline:hover:not(:disabled){border-color:var(--black)}
.btn-ghost{background:transparent;color:var(--muted)}
.btn-ghost:hover:not(:disabled){background:var(--ice);color:var(--ink)}
.btn-lg{padding:14px 26px;font-size:15.5px}
.btn-sm{padding:8px 14px;font-size:13.5px;border-radius:7px}
.btn-block{width:100%}

/* ---------- FORMS ---------- */
.field{margin-bottom:18px}
.field label{display:block;font-size:13.5px;font-weight:650;letter-spacing:-.01em;margin-bottom:7px}
.field .hint{font-size:12.5px;color:var(--muted);font-weight:400;margin-top:6px;line-height:1.5}
.field .hint a{color:var(--red);font-weight:600;text-decoration:underline}
.inp{
  width:100%;padding:12px 14px;border:1.5px solid var(--grey);border-radius:9px;
  background:var(--white);font-size:15px;transition:border-color .15s,box-shadow .15s;
}
.inp::placeholder{color:var(--muted-2)}
.inp:hover{border-color:var(--grey-2)}
.inp:focus{outline:none;border-color:var(--black);box-shadow:0 0 0 3px rgba(198,240,60,.4)}
.inp.err{border-color:var(--red);box-shadow:0 0 0 3px var(--red-tint)}
textarea.inp{resize:vertical;min-height:96px;line-height:1.6}
select.inp{cursor:pointer;appearance:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235C6270' stroke-width='2.6' stroke-linecap='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 13px center;background-size:15px;padding-right:40px;
}
.err-msg{display:flex;align-items:center;gap:6px;font-size:12.5px;color:var(--red);font-weight:600;margin-top:6px}
.err-msg svg{width:13px;height:13px;fill:none;stroke:currentColor;stroke-width:2.4;flex-shrink:0}

.inp-wrap{position:relative}
/* The 42px exists to clear a LEADING icon — apply it only when one is actually
   there (svg before the input, as on signin). A wrap used purely for the
   trailing eye button (signup's password) keeps the normal 14px, so its text
   lines up with every other field. */
.inp-wrap>svg~.inp{padding-left:42px}
/* And when the eye button is present, keep long passwords from running
   underneath it. */
.inp-wrap:has(>.peek) .inp{padding-right:46px}
.inp-wrap>svg{
  position:absolute;left:14px;top:50%;transform:translateY(-50%);
  width:16px;height:16px;fill:none;stroke:var(--muted-2);stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round;pointer-events:none;
}
.peek{
  position:absolute;right:6px;top:50%;transform:translateY(-50%);
  background:none;border:0;cursor:pointer;padding:8px;border-radius:6px;display:grid;place-items:center;
}
.peek:hover{background:var(--ice)}
.peek svg{width:16px;height:16px;fill:none;stroke:var(--muted);stroke-width:2;stroke-linecap:round;stroke-linejoin:round}

.check{display:flex;gap:10px;align-items:flex-start;cursor:pointer;font-size:13.5px;line-height:1.55}
.check input{
  appearance:none;width:18px;height:18px;border:1.5px solid var(--grey-2);border-radius:5px;
  flex-shrink:0;margin-top:2px;cursor:pointer;transition:all .15s;background:#fff;
}
.check input:checked{background:var(--black);border-color:var(--black);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23C6F03C' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
  background-size:12px;background-position:center;background-repeat:no-repeat;
}
.check input:focus-visible{outline:3px solid var(--lime);outline-offset:2px}
.check a{color:var(--red);font-weight:600;text-decoration:underline}

/* ---------- BADGES / PILLS ---------- */
.pill{
  display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:700;
  letter-spacing:.03em;padding:4px 9px;border-radius:6px;white-space:nowrap;
}
.pill svg{width:11px;height:11px;fill:none;stroke:currentColor;stroke-width:3;stroke-linecap:round;stroke-linejoin:round}
.pill-green{background:var(--green-tint);color:var(--green)}
.pill-red{background:var(--red-tint);color:var(--red-dark)}
.pill-amber{background:var(--amber-tint);color:var(--amber)}
.pill-lime{background:var(--lime);color:var(--black)}
.pill-grey{background:var(--ice-2);color:var(--muted)}
.pill-black{background:var(--black);color:#fff}

/* ---------- CARDS ---------- */
.card{background:var(--white);border:1px solid var(--grey);border-radius:var(--radius);padding:24px}
.card-h{display:flex;align-items:center;gap:12px;margin-bottom:18px}
.card-h h3{font-size:16.5px;font-weight:700;letter-spacing:-.02em}
.card-h .pill{margin-left:auto}

/* ---------- AUTH LAYOUT ---------- */
.auth{min-height:100vh;display:grid;grid-template-columns:1fr 1fr}
.auth-aside{
  background:var(--black);color:#fff;padding:44px;
  display:flex;flex-direction:column;position:relative;overflow:hidden;
}
.auth-aside::before{
  content:"";position:absolute;top:-180px;right:-140px;width:480px;height:480px;border-radius:50%;
  background:radial-gradient(circle,rgba(198,240,60,.22),transparent 68%);pointer-events:none;
}
.auth-aside::after{
  content:"";position:absolute;bottom:-200px;left:-120px;width:440px;height:440px;border-radius:50%;
  background:radial-gradient(circle,rgba(224,32,47,.26),transparent 68%);pointer-events:none;
}
.auth-aside>*{position:relative;z-index:1}
.auth-aside-mid{margin:auto 0;padding:36px 0}
.auth-aside h2{font-size:clamp(26px,2.6vw,36px);line-height:1.15;letter-spacing:-.032em;font-weight:800;margin-bottom:16px}
.auth-aside h2 em{font-family:var(--serif);font-style:italic;font-weight:400}
.auth-aside p{font-size:16px;color:rgba(255,255,255,.62);line-height:1.68;max-width:420px}
.auth-main{
  padding:44px;display:flex;flex-direction:column;background:var(--white);
  overflow-y:auto;max-height:100vh;
}
.auth-top{display:flex;align-items:center;justify-content:space-between;gap:20px;margin-bottom:auto}
.auth-top .swap{font-size:13.5px;color:var(--muted)}
.auth-top .swap a{color:var(--red);font-weight:650;text-decoration:underline}
.auth-box{width:100%;max-width:420px;margin:32px auto;padding:20px 0}
.auth-box.wide{max-width:560px}
.auth-box h1{font-size:30px;font-weight:800;letter-spacing:-.032em;line-height:1.15;margin-bottom:9px}
.auth-box .tag{font-size:15px;color:var(--muted);margin-bottom:30px;line-height:1.6}
.auth-foot{margin-top:auto;padding-top:28px;font-size:12.5px;color:var(--muted-2);display:flex;gap:18px;flex-wrap:wrap}
.auth-foot a:hover{color:var(--ink)}

.rowsplit{display:flex;justify-content:space-between;align-items:center;gap:14px;margin-bottom:22px}
.rowsplit a{font-size:13.5px;color:var(--red);font-weight:650;text-decoration:underline}

.rule{display:flex;align-items:center;gap:14px;margin:26px 0;color:var(--muted-2);font-size:12.5px;font-weight:600}
.rule::before,.rule::after{content:"";flex:1;height:1px;background:var(--grey)}

.quote{border-left:3px solid var(--lime);padding-left:20px}
.quote p{font-size:17px;color:rgba(255,255,255,.86);line-height:1.65;font-style:italic;margin-bottom:14px}
.quote cite{font-size:13.5px;color:rgba(255,255,255,.5);font-style:normal;font-weight:600}

.ticks{list-style:none;display:flex;flex-direction:column;gap:14px;margin-top:30px}
.ticks li{display:flex;gap:12px;align-items:flex-start;font-size:14.8px;color:rgba(255,255,255,.78);line-height:1.55}
.ticks li>svg{width:19px;height:19px;flex-shrink:0;margin-top:2px;fill:none;stroke:var(--lime);stroke-width:2.6;stroke-linecap:round;stroke-linejoin:round}

/* ---------- STEPPER ---------- */
.stepper{display:flex;align-items:center;gap:0;margin-bottom:32px}
.st{display:flex;align-items:center;gap:9px;flex:1}
.st:last-child{flex:0}
.st .bub{
  width:28px;height:28px;border-radius:50%;display:grid;place-items:center;flex-shrink:0;
  font-size:12.5px;font-weight:800;background:var(--ice-2);color:var(--muted);
  border:1.5px solid var(--grey);transition:all .2s;
}
.st .lbl{font-size:13px;font-weight:650;color:var(--muted);white-space:nowrap;transition:color .2s}
.st .line{flex:1;height:2px;background:var(--grey);margin:0 12px;border-radius:2px;transition:background .2s}
.st.on .bub{background:var(--black);color:var(--lime);border-color:var(--black)}
.st.on .lbl{color:var(--ink)}
.st.done .bub{background:var(--lime);color:var(--black);border-color:var(--lime)}
.st.done .lbl{color:var(--ink)}
.st.done .line{background:var(--lime)}
.st .bub svg{width:13px;height:13px;fill:none;stroke:currentColor;stroke-width:3.4;stroke-linecap:round;stroke-linejoin:round}

/* ---------- NOTICE ---------- */
.notice{
  display:flex;gap:12px;padding:15px;border-radius:10px;font-size:13.5px;line-height:1.6;
  border:1px solid;align-items:flex-start;
}
.notice>svg{width:17px;height:17px;flex-shrink:0;margin-top:1px;fill:none;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round}
.notice b{display:block;margin-bottom:3px;font-weight:700}
.notice-amber{background:var(--amber-tint);border-color:#F0DCB4;color:#6B4700}
.notice-amber>svg{stroke:var(--amber)}
.notice-lime{background:var(--lime-tint);border-color:#DCEFA8;color:#3D5106}
.notice-lime>svg{stroke:var(--lime-dark)}
.notice-red{background:var(--red-tint);border-color:#F7C9CE;color:#7A0A14}
.notice-red>svg{stroke:var(--red)}
.notice-grey{background:var(--ice);border-color:var(--grey);color:var(--muted)}
.notice-grey>svg{stroke:var(--muted)}

/* ---------- PLAN PICKER ---------- */
/* It's a <fieldset>, which browsers draw a border around by default — reset it
   so the only boxes on screen are the plan cards themselves. */
.plans{display:flex;flex-direction:column;gap:12px;border:0;padding:0;margin:0;min-width:0}
.plan{
  display:flex;align-items:flex-start;gap:14px;padding:18px;border:1.5px solid var(--grey);
  border-radius:12px;cursor:pointer;transition:all .15s;background:#fff;position:relative;
}
.plan:hover{border-color:var(--grey-2)}
.plan input{
  appearance:none;width:19px;height:19px;border:1.5px solid var(--grey-2);border-radius:50%;
  flex-shrink:0;margin-top:2px;cursor:pointer;transition:all .15s;
}
.plan input:checked{border:6px solid var(--red)}
/* Selection reads from the red radio + tint only — no heavy outline. */
.plan:has(input:checked){background:var(--ice)}
.plan-b{flex:1;min-width:0}
.plan-b .t{display:flex;align-items:center;gap:9px;margin-bottom:3px}
.plan-b .t b{font-size:15.5px;font-weight:700;letter-spacing:-.015em}
.plan-b .d{font-size:13px;color:var(--muted);line-height:1.5}
.plan-p{text-align:right;flex-shrink:0}
.plan-p .a{font-size:20px;font-weight:800;letter-spacing:-.03em;white-space:nowrap}
.plan-p .m{font-size:11.5px;color:var(--muted);white-space:nowrap}

/* ---------- TABLE ---------- */
.tbl{width:100%;border-collapse:collapse;font-size:14px}
.tbl th{
  text-align:left;font-size:11px;font-weight:800;letter-spacing:.09em;text-transform:uppercase;
  color:var(--muted);padding:12px 14px 11px;border-bottom:1px solid var(--grey);white-space:nowrap;
}
.tbl td{padding:15px 14px;border-bottom:1px solid var(--ice-2);vertical-align:middle}
.tbl tbody tr{cursor:pointer;transition:background .12s}
.tbl tbody tr:hover{background:var(--ice)}
.tbl .num{font-variant-numeric:tabular-nums}
.tbl .b{font-weight:650}
.tbl .s{font-size:12.5px;color:var(--muted);margin-top:2px}

@media(max-width:900px){
  .auth{grid-template-columns:1fr}
  .auth-aside{display:none}
  .auth-main{padding:28px 20px;max-height:none}
}
@media(max-width:560px){
  .st .lbl{display:none}
  .auth-box h1{font-size:25px}
}
@media(prefers-reduced-motion:reduce){
  *{animation:none!important;transition:none!important}
  html{scroll-behavior:auto}
}

/* ══════════════════════════════════════════════════════════════════════════
   MOBILE SAFETY NET — added 23 Jul 2026
   ──────────────────────────────────────────────────────────────────────────
   Every rule below is inside a max-width media query, so DESKTOP IS UNTOUCHED.
   No JavaScript, no markup, no class renames — layout only, so nothing that
   talks to Firebase, LiveKit, Crazytel or Stripe can be affected.

   The problem this solves: on a phone the app could be scrolled sideways.
   Once a page scrolls horizontally every fixed/centred element looks broken —
   dialogs sit half off-screen, headers drift, and content is unreachable.
   The usual culprits are wide tables, long unbroken strings (phone numbers,
   SIP URIs, emails), fixed pixel widths, and grids whose children refuse to
   shrink below their content.
   ═════════════════════════════════════════════════════════════════════════ */
@media (max-width:768px){

  /* 1. Nothing may exceed the viewport. A grid/flex child defaults to
        min-width:auto, which keeps it as wide as its longest word — that
        single default causes most sideways scrolling. */
  /* clip, not hidden — hidden makes this a scroll container and kills
     position:sticky everywhere inside it. */
  /* See dashboard.html: clip on html/body also clips fixed overlays. */
  html,body{max-width:100%;}
  *{min-width:0;}

  /* 2. Media and embeds never overflow their column. */
  img,svg,video,canvas,iframe{max-width:100%; height:auto;}

  /* 3. Long unbroken strings — phone numbers, SIP URIs, emails, API keys —
        wrap instead of forcing the page wide. */
  .mono,code,pre,a[href^="mailto"],a[href^="tel"]{overflow-wrap:anywhere; word-break:break-word;}

  /* 4. Tables scroll INSIDE their own wrapper, never the page. -webkit-
        overflow-scrolling keeps the momentum feel on iOS. */
  .tbl-wrap{overflow-x:auto; -webkit-overflow-scrolling:touch; max-width:100%;}
  .tbl{min-width:520px;}          /* below this a table is unreadable anyway */

  /* 5. Any element given a fixed pixel width inline still fits. */
  [style*="max-width"],[style*="width:"]{max-width:100%!important;}

  /* 6. Dialogs: centred, with air either side, never taller than the screen. */
  .ask-back{padding:16px; align-items:center;}
  .ask{width:100%; max-width:100%; max-height:calc(100vh - 32px); overflow-y:auto;
       -webkit-overflow-scrolling:touch;}

  /* 7. Toolbars and filter rows stack instead of squeezing. */
  .toolbar{gap:8px;}
  .toolbar .search{flex:1 1 100%;}
  .toolbar .inp,.toolbar select.inp{flex:1 1 140px; min-width:0;}

  /* 8. Buttons stay tappable (44px is the Apple/WCAG target) and never
        overflow their row. */
  .btn{min-height:40px;}
  .row{flex-wrap:wrap;}
  .row.end{justify-content:stretch;}

  /* 9. Reclaim horizontal space eaten by padding. */
  .wrap,.sec,.panel-b,.panel-h{padding-left:14px; padding-right:14px;}
  .card,.panel{border-radius:12px;}

  /* 10. Multi-column grids become single column. */
  .grid,.g2,.g3,.g4,.g23{grid-template-columns:1fr!important;}
}

@media (max-width:480px){
  /* Tighter still on small phones, and headings that don't wrap awkwardly. */
  .wrap,.sec,.panel-b,.panel-h{padding-left:12px; padding-right:12px;}
  h1{font-size:22px; letter-spacing:-.02em;}
  h2{font-size:18px;}
  h3{font-size:15.5px;}
  .btn{width:100%; justify-content:center;}
  .row.end .btn,.tc-actions .btn{width:100%;}
  /* Toasts span the screen rather than hugging one corner. */
  .toasts{left:12px; right:12px; bottom:12px; width:auto;}
}

/* iOS viewport stability -------------------------------------------------
   WebKit zooms the entire WKWebView when a focused form control renders
   below 16px. Some route-specific controls used 10-15px text, so opening a
   filter or editor could leave the installed app magnified until restart.
   Keep controls at the non-zoom threshold and contain wide route widgets in
   their own scrollers instead of allowing them to resize the document. */
@media (max-width:1023px){
  html{-webkit-text-size-adjust:100%;text-size-adjust:100%}
  input,select,textarea,[contenteditable="true"]{font-size:16px!important}
  button,a,[role="button"]{touch-action:manipulation}
  .shell,.main,.content{min-width:0;max-width:100%}
}

/* ---------- NATIVE APP (iOS / Android shell) ----------
   Only applies inside the Capacitor build — assets/native.js puts .is-native on
   <html>, so the website is untouched by every rule below.

   The web view runs edge to edge, under the status bar and the home indicator.
   Without these insets the topbar sits beneath the clock and the tab bar sits
   under the iPhone's home bar. env() is 0 on Android phones without cutouts,
   so one rule covers both platforms. */
.is-native{
  /* A percentage-height document can be clamped to one iPhone viewport by
     WKWebView. Let the document grow naturally so every dashboard route has a
     real page to scroll, while keeping at least one full screen of canvas. */
  height:auto;
  min-height:100%;
  background:#F5F6F8;
}
.is-native body{
  height:auto;
  min-height:100%;
  padding-top:env(safe-area-inset-top, 0px);
}
.is-native body.deskali-dashboard{
  background:#F5F6F8 !important;
  overflow-y:auto;
  -webkit-overflow-scrolling:touch;
}
/* Paint only the system status-bar strip black. The old rule painted the whole
   body black, so it leaked through every short page and made iOS disagree with
   the web dashboard. */
.is-native body::before{
  content:"";position:fixed;z-index:100;pointer-events:none;
  top:0;left:0;right:0;height:env(safe-area-inset-top, 0px);background:#0A0A0A;
}
.is-native .topbar{top:env(safe-area-inset-top, 0px);}
.is-native .mobile-route-head{
  top:calc(var(--tb-h) + env(safe-area-inset-top, 0px));
}
.is-native .sb{padding-top:env(safe-area-inset-top, 0px);}
/* Drawer buttons and content must clear the notch and home indicator too. */
.is-native .drawer{
  top:env(safe-area-inset-top, 0px);
  bottom:env(safe-area-inset-bottom, 0px);
}
/* The sign-up wizard uses a fixed full-screen scrim, so body padding cannot
   protect it. Keep its card between the status bar and home indicator. */
.is-native .scrim{
  inset:env(safe-area-inset-top, 0px) 0 env(safe-area-inset-bottom, 0px);
}

/* No text selection or long-press callout on chrome — it reads as a web page
   the moment a magnifier appears over a button. Inputs and message text keep
   selection, because copying a customer's number is a real thing people do. */
.is-native .btn,.is-native .tabbar,.is-native .topbar,.is-native .pill,.is-native .tb-fab{
  -webkit-user-select:none; user-select:none; -webkit-touch-callout:none;
}
/* Kills the grey flash iOS paints over any tapped element. */
.is-native *{-webkit-tap-highlight-color:transparent;}
/* The page itself must not rubber-band; only scrollers inside it should. */
.is-native body{overscroll-behavior-y:none;}

/* ---------- NATIVE: the keyboard ----------
   assets/native.js adds .kbd-open while the keyboard is up and, only when
   nothing on the page can scroll, sets --kbd-lift to the number of pixels the
   focused field is hidden by. */
.is-native{--kbd-lift:0px;}
.is-native.kbd-open body{
  transform:translateY(calc(-1 * var(--kbd-lift)));
  transition:transform .18s ease-out;
}

/* The sign-in and sign-up card is centred with auto margins, so on a short
   screen the keyboard covers it and the centring fights every attempt to
   scroll. While typing, pin it to the top and let it scroll normally. */
.is-native.kbd-open .auth-box{margin-top:0; margin-bottom:0;}
.is-native.kbd-open .auth-top{margin-bottom:16px;}

/* 100vh does not shrink for the keyboard on iOS; 100dvh does. Everything that
   sizes itself to the viewport on an input-bearing screen uses it. */
.is-native .auth{min-height:100dvh;}
.is-native .auth-main{max-height:100dvh;}

/* Guideline 3.1.1: the native app cannot sell a subscription, so the note
   explaining where plans live is shown there and nowhere else. */
.iap-note{display:none;}
.no-iap .iap-note{display:block;}
.nav-with-action{position:relative}.nav-with-action>.nav-link{padding-right:42px}.nav-quick-add{position:absolute;right:10px;top:50%;transform:translateY(-50%);width:25px;height:25px;border-radius:50%;display:grid;place-items:center;text-decoration:none;background:#c6f03c;color:#101214;font-size:18px;font-weight:900;line-height:1}.nav-quick-add:hover{background:#fff}
