/* ==========================================================================
   Jetsy — customer dashboard
   Self-contained: this file carries its own tokens so the app can move
   independently of the marketing site.
   ========================================================================== */

:root{
  --void:#05090F; --deep:#0A1622;
  --panel:#0B1420; --panel2:#101D2C; --panel3:#142435;
  --line:#1C3047; --line2:#132335;

  --or:#FF6A1A; --or-lt:#FFB259; --or-dk:#D9480F;
  --blue:#4FA8FF; --blue-lt:#A8D2FF;

  --ink:#E9F2FB; --ink2:#B4C8DC; --muted:#7B94AC;

  --ok:#3ED598; --warn:#FFC24D; --hot:#FF6B5E;

  --mono:ui-monospace,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace;
  --tabh:4.4rem;

  /* look round, 26 Sep 2026 — builder A.
     board.js and app.js inject their own rules at runtime with fallback
     colours that are not the brand (var(--accent,#FF8A2E), var(--acc,#ff6a1a)
     …). These two resolve every one of them to Jetsy orange. */
  --accent:var(--or); --acc:var(--or);

  /* the console language, as tokens, so every card, row and button in this
     file and in board.css draws the same devices (copied from the drawer's
     .lx-* rules — nothing here is new) */
  --console:linear-gradient(180deg,#15273A 0%,#0C1826 100%);   /* lit from above */
  --console-lit:#15273A; --console-dk:#0C1826;
  --cut:3px 14px 3px 14px;      /* the chamfer — a cut corner on the diagonal */
  --cut-row:3px 10px 3px 10px;  /* softer, for a row inside a card */
  --cut-sm:2px 8px 2px 8px;     /* buttons, badges, chips, inputs */
  --bracket:rgba(255,106,26,.5);
  --bloom:0 0 14px rgba(255,106,26,.45);
  --bloom-sm:0 0 10px rgba(255,106,26,.4);
  --hair:inset 0 0 0 1px var(--line);
  --hair2:inset 0 0 0 1px var(--line2);
}
*{box-sizing:border-box}
html{overflow-x:hidden}
@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}

body{margin:0;background:var(--void);color:var(--ink);
  font-family:Poppins,ui-sans-serif,system-ui,-apple-system,sans-serif;
  font-size:16px;line-height:1.55;-webkit-font-smoothing:antialiased;
  -webkit-text-size-adjust:100%;padding-bottom:var(--tabh)}
h1,h2,h3{margin:0;font-weight:700;letter-spacing:-.02em;line-height:1.15;text-wrap:balance}
p{margin:0}
a{color:var(--or-lt)}
button,input,select,textarea{font:inherit;color:inherit}
/* the browser's own placeholder grey fails on navy; every box says its hint in muted */
::placeholder{color:var(--muted);opacity:1}
:focus-visible{outline:2px solid var(--or);outline-offset:2px;border-radius:4px}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

.shell{max-width:44rem;margin:0 auto;padding:0 .9rem}

/* ---------- demo banner ---------- */
.demobar{background:rgba(255,194,77,.12);border-bottom:1px solid rgba(255,194,77,.3);
  padding:.5rem .9rem;display:flex;gap:.5rem;align-items:center;justify-content:center;
  font-size:.74rem;color:var(--warn);text-align:center;line-height:1.4}
.demobar b{font-family:var(--mono);font-size:.62rem;letter-spacing:.14em;
  text-transform:uppercase;flex:none}

/* ---------- top bar ---------- */
.appbar{position:sticky;top:0;z-index:40;background:rgba(5,9,15,.9);
  backdrop-filter:blur(14px);border-bottom:1px solid var(--line2)}
.appbar .in{max-width:44rem;margin:0 auto;padding:.65rem .9rem;
  display:flex;align-items:center;gap:.7rem}
.appbar .mark{width:34px;height:34px;flex:none;border-radius:8px;overflow:hidden;
  background:#0A2540;display:grid;place-content:center}
.appbar .mark svg{width:30px;height:30px;display:block}
.appbar .who{min-width:0;display:flex;flex-direction:column;line-height:1.2}
.appbar .who .co{font-size:.92rem;font-weight:600;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}
.appbar .who .pl{font-family:var(--mono);font-size:.58rem;letter-spacing:.12em;
  text-transform:uppercase;color:var(--muted)}
.appbar .live{margin-left:auto;display:flex;align-items:center;gap:.4rem;flex:none}
.pulse{width:7px;height:7px;border-radius:50%;background:var(--ok);
  box-shadow:0 0 0 3px rgba(62,213,152,.16);animation:bp 2.6s ease-in-out infinite}
@keyframes bp{0%,100%{opacity:1}50%{opacity:.35}}
.appbar .live span{font-family:var(--mono);font-size:.58rem;letter-spacing:.13em;
  text-transform:uppercase;color:var(--ink2)}

/* ---------- headings ---------- */
/* A section heading is a kicker, the way the drawer names its bands: a
   14px orange rule, mono caps in peach, a hairline fading out to the count.
   The count on the right is a HUD readout in muted mono. */
.h{display:flex;align-items:center;gap:.6rem;margin:1.3rem 0 .7rem;min-width:0}
.h::before{content:"";width:14px;height:2px;background:var(--or);flex:0 0 auto}
.h h2{flex:1 1 auto;min-width:0;display:flex;align-items:center;gap:.6rem;
  font-family:var(--mono);font-size:.74rem;font-weight:700;letter-spacing:.16em;
  text-transform:uppercase;color:var(--or-lt);line-height:1.3;text-wrap:auto}
.h h2::after{content:"";flex:1 1 auto;min-width:.6rem;height:1px;
  background:linear-gradient(90deg,var(--line) 0%,transparent 100%)}
.h .n{font-family:var(--mono);font-size:.62rem;letter-spacing:.12em;
  text-transform:uppercase;color:var(--muted);margin-left:auto;flex:0 1 auto;
  text-align:right;max-width:56%}
.greet{padding:1.1rem 0 .2rem}
.greet .hi{font-size:1.5rem;font-weight:700;letter-spacing:-.03em}
.greet .sub{font-size:.86rem;color:var(--muted);margin-top:.2rem}

/* ---------- needs-you cards ---------- */
/* A console card: the navy ground lit from above, a cut corner, orange
   brackets. The status colour is the hairline and the kicker only — it is
   a flag, not decoration. */
.needs{display:flex;flex-direction:column;gap:.6rem}
.need{position:relative;background:linear-gradient(180deg,rgba(255,107,94,.09),transparent 46%),var(--console);
  box-shadow:inset 0 0 0 1px rgba(255,107,94,.34);border-radius:var(--cut);
  padding:.9rem .95rem .85rem;display:flex;flex-direction:column;gap:.55rem;
  animation:pop .32s ease both}
.need::before,.need::after{content:"";position:absolute;width:9px;height:9px;
  border:1px solid var(--bracket);pointer-events:none}
.need::before{top:5px;left:5px;border-right:0;border-bottom:0}
.need::after{bottom:5px;right:5px;border-left:0;border-top:0}
.need.money{background:linear-gradient(180deg,rgba(255,194,77,.09),transparent 46%),var(--console);
  box-shadow:inset 0 0 0 1px rgba(255,194,77,.34)}
.need.ask{background:linear-gradient(180deg,rgba(79,168,255,.09),transparent 46%),var(--console);
  box-shadow:inset 0 0 0 1px rgba(79,168,255,.32)}
@keyframes pop{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
.need .top{display:flex;gap:.55rem;align-items:center}
.need .why{font-family:var(--mono);font-size:.58rem;letter-spacing:.13em;
  text-transform:uppercase;color:var(--hot);font-weight:600;display:inline-flex;
  align-items:center;gap:.45rem}
.need .why::before{content:"";width:14px;height:2px;background:currentColor;flex:none}
.need.money .why{color:var(--warn)}
.need.ask .why{color:var(--blue)}
.need .ago{margin-left:auto;font-family:var(--mono);font-size:.6rem;color:var(--muted);
  white-space:nowrap}
.need .nm{font-size:1.02rem;font-weight:600;letter-spacing:-.01em}
.need .what{font-size:.87rem;color:var(--ink2);line-height:1.5}
.need .quote{font-size:.83rem;color:var(--ink2);font-style:italic;
  border-left:2px solid var(--line);padding-left:.6rem;line-height:1.5}
.need .acts{display:flex;gap:.45rem;flex-wrap:wrap;margin-top:.1rem}

/* ---------- buttons ---------- */
/* Every button carries the small chamfer. A plain button is a navy slab
   with a hairline; the primary ones are solid orange with navy type and a
   little bloom (the badge device, made tappable). Orange is the one colour
   for "do this" — green, amber and red stay for status. */
.b{display:inline-flex;align-items:center;justify-content:center;gap:.4rem;
  border:0;cursor:pointer;font-size:.8rem;font-weight:600;padding:.52rem .8rem;
  border-radius:var(--cut-sm);
  background:linear-gradient(180deg,rgba(255,255,255,.07),rgba(255,255,255,.025));
  color:var(--ink);box-shadow:var(--hair);text-decoration:none;
  transition:background .15s,box-shadow .15s,color .15s}
.b:hover{box-shadow:inset 0 0 0 1px var(--or);color:var(--or-lt)}
.b:active{box-shadow:inset 0 0 0 1px var(--or);color:var(--or-lt);
  background:rgba(255,106,26,.14)}
.b.ring{background:var(--or);color:var(--deep);box-shadow:var(--bloom-sm)}
.b.ring:hover{filter:brightness(1.08);color:var(--deep)}
.b.pri{background:var(--or);color:var(--deep);box-shadow:var(--bloom-sm)}
.b.pri:hover{filter:brightness(1.08);color:var(--deep)}
.b.pri:active,.b.ring:active{filter:brightness(.94);color:var(--deep);
  background:var(--or);box-shadow:var(--bloom-sm)}
.b.sm{font-size:.74rem;padding:.4rem .62rem}
.b svg{flex:none}

/* ---------- empty state ---------- */
/* a console card, the all-clear green on its hairline only */
.empty{position:relative;background:linear-gradient(180deg,rgba(62,213,152,.06),transparent 50%),var(--console);
  box-shadow:inset 0 0 0 1px rgba(62,213,152,.26);border-radius:var(--cut);
  padding:1.4rem 1rem;text-align:center;
  display:flex;flex-direction:column;gap:.35rem;align-items:center}
.empty::before,.empty::after{content:"";position:absolute;width:9px;height:9px;
  border:1px solid var(--bracket);pointer-events:none}
.empty::before{top:5px;left:5px;border-right:0;border-bottom:0}
.empty::after{bottom:5px;right:5px;border-left:0;border-top:0}
.empty .ic{color:var(--ok)}
.empty h3{font-size:1rem}
.empty p{font-size:.85rem;color:var(--ink2);max-width:30ch}

/* ---------- stat strip ---------- */
/* readouts: a mono figure over a HUD label, on a row-chamfered slab */
.stats{display:grid;grid-template-columns:repeat(2,1fr);gap:.5rem}
@media(min-width:560px){.stats{grid-template-columns:repeat(4,1fr)}}
.stat{background:var(--panel2);box-shadow:var(--hair);
  border-radius:var(--cut-row);padding:.7rem .75rem;display:flex;flex-direction:column;gap:.15rem}
.stat .v{font-family:var(--mono);font-size:1.5rem;font-weight:700;letter-spacing:-.02em;line-height:1;
  font-variant-numeric:tabular-nums}
.stat .k{font-family:var(--mono);font-size:.58rem;letter-spacing:.12em;
  text-transform:uppercase;color:var(--muted);line-height:1.4}
.stat.good .v{color:var(--ok)}
.stat.hot .v{color:var(--or);text-shadow:var(--bloom)}

/* meter */
.usage{background:var(--panel2);box-shadow:var(--hair);
  border-radius:var(--cut-row);padding:.8rem .85rem;display:flex;flex-direction:column;gap:.5rem}
.usage .row{display:flex;align-items:baseline;gap:.5rem;font-size:.84rem}
.usage .row b{font-variant-numeric:tabular-nums;font-family:var(--mono)}
.usage .row .r{margin-left:auto;font-family:var(--mono);font-size:.65rem;color:var(--muted)}
.track{height:8px;border-radius:var(--cut-sm);background:var(--deep);
  box-shadow:var(--hair2);overflow:hidden}
.track i{display:block;height:100%;background:linear-gradient(90deg,var(--blue),var(--or))}

/* ---------- job rows ---------- */
/* a row: the panel ground, a hairline, the softer chamfer; the time is a
   mono orange mark with the bloom */
.jobs{display:flex;flex-direction:column;gap:.5rem}
.job{padding:.7rem .8rem;display:flex;gap:.75rem;align-items:flex-start}
/* the slab is the wrapper's, so a row's buttons (.jacts) and its edit form
   sit inside the same slab as the row */
.jobwrap{background:var(--panel2);box-shadow:var(--hair);border-radius:var(--cut-row)}
.job .when{font-family:var(--mono);font-size:.72rem;color:var(--or);font-weight:700;
  flex:none;width:3.9rem;padding-top:.15rem;line-height:1.35;text-shadow:var(--bloom)}
.job .bd{min-width:0;flex:1;display:flex;flex-direction:column;gap:.15rem}
.job .nm{font-size:.94rem;font-weight:600}
.job .ad{font-size:.8rem;color:var(--ink2)}
.job .sv{font-family:var(--mono);font-size:.6rem;letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted);margin-top:.15rem}
.job .go{flex:none;color:var(--muted)}

/* ---------- call log ---------- */
.filters{display:flex;gap:.35rem;overflow-x:auto;padding-bottom:.3rem;
  scrollbar-width:none;-webkit-overflow-scrolling:touch}
.filters::-webkit-scrollbar{display:none}
.f{flex:none;cursor:pointer;border:0;background:var(--panel2);
  box-shadow:var(--hair);color:var(--ink2);font-weight:500;
  font-size:.75rem;padding:.36rem .68rem;border-radius:var(--cut-sm);white-space:nowrap}
.f[aria-pressed="true"]{background:rgba(255,106,26,.14);color:var(--or-lt);
  box-shadow:inset 0 0 0 1px var(--or)}

.calls{display:flex;flex-direction:column;gap:.45rem;margin-top:.7rem}
.call{background:var(--panel2);box-shadow:var(--hair);
  border-radius:var(--cut-row);overflow:hidden}
.call.open{box-shadow:inset 0 0 0 1px rgba(255,106,26,.45)}
.call .hd{width:100%;text-align:left;background:transparent;border:0;cursor:pointer;
  padding:.7rem .8rem;display:flex;gap:.7rem;align-items:center}
.call .dot{width:9px;height:9px;border-radius:50%;flex:none}
.dot.booked{background:var(--ok)}
.dot.message{background:var(--blue)}
.dot.escalated{background:var(--hot)}
.dot.spam{background:var(--muted)}
.call .who{min-width:0;flex:1;display:flex;flex-direction:column;line-height:1.3}
.call .who .n{font-size:.9rem;font-weight:600;white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis}
.call .who .m{font-family:var(--mono);font-size:.63rem;color:var(--muted)}
.call .rt{flex:none;display:flex;flex-direction:column;align-items:flex-end;gap:.2rem}
.chip{font-family:var(--mono);font-size:.55rem;letter-spacing:.1em;text-transform:uppercase;
  padding:.14rem .42rem;border-radius:var(--cut-sm);white-space:nowrap}
.chip.booked{background:rgba(62,213,152,.14);color:var(--ok)}
.chip.message{background:rgba(79,168,255,.14);color:var(--blue)}
.chip.escalated{background:rgba(255,107,94,.14);color:var(--hot)}
.chip.spam{background:rgba(123,148,172,.14);color:var(--ink2)}
.chip.es{background:rgba(255,106,26,.14);color:var(--or-lt)}
.call .rt .t{font-family:var(--mono);font-size:.6rem;color:var(--muted)}
.call .body{display:none;padding:0 .8rem .8rem;border-top:1px solid var(--line2)}
.call.open .body{display:block}
.tx{display:flex;flex-direction:column;gap:.4rem;padding:.7rem 0}
.tx .l{display:flex;gap:.5rem;font-size:.83rem;line-height:1.5}
.tx .l .s{font-family:var(--mono);font-size:.62rem;letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted);flex:none;width:3rem;padding-top:.25rem}
.tx .l.ai .x{color:var(--ink2)}
.tx .l.you .s{color:var(--or-lt)}

/* ---------- settings ---------- */
/* each group is a console card; its header is a kicker */
.set{display:flex;flex-direction:column;gap:.6rem}
.grp{position:relative;background:var(--console);box-shadow:var(--hair);
  border-radius:var(--cut);overflow:hidden}
.grp::before,.grp::after{content:"";position:absolute;width:9px;height:9px;
  border:1px solid var(--bracket);pointer-events:none;z-index:1}
.grp::before{top:5px;left:5px;border-right:0;border-bottom:0}
.grp::after{bottom:5px;right:5px;border-left:0;border-top:0}
.grp .gh{padding:.75rem .9rem .45rem;border-bottom:0;
  font-family:var(--mono);font-size:.68rem;letter-spacing:.16em;text-transform:uppercase;
  color:var(--or-lt);font-weight:600;background:transparent;
  display:flex;align-items:center;gap:.5rem}
.grp .gh::before{content:"";width:14px;height:2px;background:var(--or);flex:0 0 auto}
.grp .gh::after{content:"";height:1px;flex:1 1 auto;min-width:0;
  background:linear-gradient(90deg,var(--line) 0%,transparent 100%)}
.r{padding:.7rem .8rem;border-bottom:1px solid var(--line2);
  display:flex;gap:.7rem;align-items:center}
.r:last-child{border-bottom:0}
.r .lb{min-width:0;flex:1;display:flex;flex-direction:column;gap:.1rem}
.r .lb .t{font-size:.88rem;font-weight:500}
.r .lb .d{font-size:.76rem;color:var(--muted);line-height:1.45}
.r .ctl{flex:none}
/* the switch: a navy inset, the knob lights orange when it is on — orange
   is the console's "active", the same as a pressed filter or the open tab */
.sw{width:46px;height:26px;border-radius:var(--cut-sm);background:var(--deep);
  border:0;cursor:pointer;position:relative;transition:background .18s,box-shadow .18s;
  box-shadow:var(--hair)}
.sw::after{content:"";position:absolute;top:3px;left:3px;width:20px;height:20px;
  border-radius:2px 6px 2px 6px;background:var(--muted);transition:transform .18s,background .18s,box-shadow .18s}
.sw[aria-checked="true"]{background:rgba(255,106,26,.16);box-shadow:inset 0 0 0 1px var(--or)}
.sw[aria-checked="true"]::after{transform:translateX(20px);background:var(--or);box-shadow:var(--bloom-sm)}
.r input[type=text],.r input[type=tel],.r input[type=number],.r select{
  background:var(--deep);border:0;box-shadow:var(--hair);color:var(--ink);
  padding:.44rem .6rem;border-radius:var(--cut-sm);font-size:.85rem;max-width:9.5rem;text-align:right}
.r input:focus,.r select:focus{outline:0;box-shadow:inset 0 0 0 1px var(--or),0 0 0 3px rgba(255,106,26,.18)}
.r select{text-align:left}

.voices{display:flex;gap:.4rem;overflow-x:auto;padding:.7rem .8rem;
  scrollbar-width:none;-webkit-overflow-scrolling:touch}
.voices::-webkit-scrollbar{display:none}
.vc{flex:none;cursor:pointer;border:0;background:var(--panel2);
  box-shadow:var(--hair);border-radius:var(--cut-row);padding:.45rem .62rem;
  display:flex;flex-direction:column;gap:.05rem;text-align:left;min-width:5.6rem}
.vc .n{font-size:.82rem;font-weight:600}
.vc .d{font-family:var(--mono);font-size:.55rem;letter-spacing:.08em;
  text-transform:uppercase;color:var(--muted)}
.vc[aria-pressed="true"]{background:rgba(255,106,26,.13);box-shadow:inset 0 0 0 1px var(--or)}
.vc[aria-pressed="true"] .d{color:var(--or-lt)}

.note{font-size:.76rem;color:var(--muted);line-height:1.5;padding:.2rem .2rem 0}

/* ---------- bottom tabs ---------- */
.tabs{position:fixed;left:0;right:0;bottom:0;z-index:50;
  background:rgba(8,14,22,.95);backdrop-filter:blur(16px);
  border-top:1px solid var(--line2);padding-bottom:env(safe-area-inset-bottom)}
.tabs .in{max-width:44rem;margin:0 auto;display:grid;grid-template-columns:repeat(4,1fr)}
.tb{background:transparent;border:0;cursor:pointer;padding:.55rem .3rem .6rem;
  display:flex;flex-direction:column;align-items:center;gap:.15rem;color:var(--muted);
  position:relative}
.tb span{font-size:.62rem;font-weight:500;letter-spacing:.01em}
.tb[aria-selected="true"]{color:var(--or)}
.tb[aria-selected="true"]::before{content:"";position:absolute;top:0;left:26%;right:26%;
  height:2px;background:var(--or);border-radius:0 0 2px 2px}
.tb .badge{position:absolute;top:.3rem;right:50%;transform:translateX(1.15rem);
  background:var(--hot);color:#2A0A06;font-family:var(--mono);font-size:.55rem;
  font-weight:700;min-width:15px;height:15px;border-radius:8px;display:grid;
  place-content:center;padding:0 3px}

/* ---------- toast ---------- */
.toast{position:fixed;left:50%;transform:translateX(-50%);bottom:calc(var(--tabh) + .8rem);
  z-index:60;background:var(--ink);color:var(--void);font-size:.78rem;font-weight:600;
  padding:.55rem .9rem;border-radius:20px;box-shadow:0 12px 30px -12px #000;
  max-width:88vw;text-align:center}
.toast[hidden]{display:none!important}

footer.appfoot{padding:1.6rem .2rem 1rem;color:var(--muted);font-size:.72rem;
  line-height:1.6;border-top:1px solid var(--line2);margin-top:1.6rem}

/* ---------- install-to-home-screen bar ---------- */
.install{display:flex;align-items:center;gap:.6rem;padding:.6rem .9rem;
  background:rgba(79,168,255,.1);border-bottom:1px solid rgba(79,168,255,.28);
  font-size:.8rem;color:var(--ink2);line-height:1.4}
.install[hidden]{display:none!important}
.install .ic{color:var(--blue);flex:none;display:grid;place-content:center}
.install .tx{flex:1;min-width:0}
.install .x{background:transparent;border:0;color:var(--muted);cursor:pointer;
  font-size:1.2rem;line-height:1;padding:.1rem .3rem;flex:none;border-radius:5px}
.install .x:hover{color:var(--ink)}

/* the receptionist builder — pick one of two, per axis */
.whoOpt{background:var(--panel2);box-shadow:var(--hair);
  color:var(--ink2);border-radius:var(--cut-row);transition:box-shadow .15s,background .15s,color .15s;
  /* .b is a centred inline-flex row; these need a stacked left-aligned block or
     the label and its explanation sit side by side. */
  flex-direction:column;align-items:flex-start;justify-content:flex-start;gap:.1rem}
.whoOpt>span{display:block;width:100%}
.whoOpt:hover{box-shadow:inset 0 0 0 1px var(--blue)}
.whoOpt.on{background:rgba(255,106,26,.12);box-shadow:inset 0 0 0 1px var(--or);color:var(--ink)}

/* the one-line summary the call log was already storing and never showing */
.call .who .sum{display:block;font-size:.8rem;color:var(--ink2);line-height:1.35;
  margin:.12rem 0 .1rem;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;
  -webkit-box-orient:vertical}

/* the four escalation reasons the webhook actually writes, which used to fall
   through to no styling at all and made the status dot invisible */
.chip.escalated_cant_answer,.chip.escalated_over_quote,
.chip.escalated_wants_human,.chip.escalated_upset{
  color:var(--warn);background:rgba(255,194,77,.12);
  box-shadow:inset 0 0 0 1px rgba(255,194,77,.32)}
.dot.escalated_cant_answer,.dot.escalated_over_quote,
.dot.escalated_wants_human,.dot.escalated_upset{background:var(--warn)}

/* a job row and its calendar tap */
/* look round: the receptionist's bookings are a link (a.job) with their
   buttons in a .jacts beside it — the link took the page's peach underline
   and the buttons squeezed the name into a four-word column. The row is the
   row; its buttons sit under it on their own line. */
.jobwrap{display:flex;flex-wrap:wrap;align-items:stretch;gap:0}
.jobwrap .job{flex:1 1 100%;min-width:0}
.jobwrap .jacts,.jobwrap .editf{flex:1 1 100%;min-width:0;padding:0 .8rem .7rem}
a.job,a.job:hover{color:inherit;text-decoration:none}
a.job .nm{color:var(--ink)}
/* board.css has a .cal (the calendar list) that this button's class also
   matches — the margin and the column direction it picked up are undone here */
.b.cal{flex:none;flex-direction:row;margin-top:0;white-space:nowrap;font-size:.72rem;padding:.4rem .6rem}

/* Every one-handed action was under the 44px minimum and nothing set a floor.
   Shrink the padding if it must, never the target. */
.b,.f,.sw,.tb{min-height:44px}
.b.sm{min-height:44px;padding-top:.3rem;padding-bottom:.3rem}
.sw{min-width:44px}

/* 9px uppercase mono on a dark screen in Phoenix sun, for a man in his
   forties, on the two most important lines on the card. */
.need .why,.call .rt .t,.chip,.stat .k,.gh,.bizf>span,.blabel{font-size:.7rem}
.meter{font-size:.66rem}

/* =====================================================================
   2026-09-06 — the CRM: search, customers, corrections, the month
   ===================================================================== */
/* Calls | Customers */
.seg{display:flex;gap:.25rem;margin-top:1rem;background:var(--deep);padding:.25rem;
  border-radius:var(--cut-row);box-shadow:var(--hair2)}
.seg button{flex:1;border:0;cursor:pointer;background:transparent;color:var(--muted);
  font:inherit;font-size:.86rem;font-weight:600;padding:.5rem .6rem;border-radius:var(--cut-sm);min-height:40px}
.seg button[aria-selected="true"]{background:var(--panel2);color:var(--or-lt);box-shadow:inset 0 0 0 1px var(--or)}

/* search, dates, paging — inputs are navy insets with an orange focus ring */
.search{display:flex;gap:.4rem;margin:.6rem 0 .5rem}
.search input{flex:1;min-width:0;background:var(--deep);border:0;color:var(--ink);
  box-shadow:var(--hair);padding:.6rem .7rem;border-radius:var(--cut-sm);font:inherit;font-size:16px}
.search input:focus{outline:0;box-shadow:inset 0 0 0 1px var(--or),0 0 0 3px rgba(255,106,26,.18)}
.search input::placeholder{color:var(--muted)}
.dates{display:flex;gap:.5rem;align-items:center;flex-wrap:wrap;margin-bottom:.5rem}
.dates label{display:flex;gap:.35rem;align-items:center;font-family:var(--mono);font-size:.62rem;
  letter-spacing:.1em;text-transform:uppercase;color:var(--muted)}
.dates input{background:var(--deep);border:0;color:var(--ink);box-shadow:var(--hair);
  padding:.4rem .5rem;border-radius:var(--cut-sm);font:inherit;font-size:.86rem;color-scheme:dark}
.dates input:focus{outline:0;box-shadow:inset 0 0 0 1px var(--or),0 0 0 3px rgba(255,106,26,.18)}
.pager{display:flex;align-items:center;justify-content:space-between;gap:.5rem;margin:.7rem 0 .2rem;
  font-family:var(--mono);font-size:.66rem;letter-spacing:.1em;text-transform:uppercase;color:var(--muted)}
.pager .b[disabled]{opacity:.35;pointer-events:none}

/* badges */
.chip.repeat{background:rgba(255,178,89,.16);color:var(--or-lt)}
.chip.edited{background:rgba(79,168,255,.14);color:var(--blue)}
.call .who .n .chip{vertical-align:middle;margin-left:.2rem}

/* the correction form, the booking form */
.editf{display:flex;flex-direction:column;gap:.5rem;padding:.7rem 0 .2rem}
.editf label{display:flex;flex-direction:column;gap:.25rem;font-family:var(--mono);font-size:.6rem;
  letter-spacing:.12em;text-transform:uppercase;color:var(--muted)}
.editf input,.editf select,.editf textarea{background:var(--deep);border:0;color:var(--ink);
  box-shadow:var(--hair);padding:.55rem .65rem;border-radius:var(--cut-sm);font:inherit;font-size:16px;
  letter-spacing:0;text-transform:none;color-scheme:dark}
.editf input:focus,.editf select:focus,.editf textarea:focus{outline:0;
  box-shadow:inset 0 0 0 1px var(--or),0 0 0 3px rgba(255,106,26,.18)}
.b.danger{color:var(--hot);box-shadow:inset 0 0 0 1px rgba(255,107,94,.35)}
.jobwrap .jacts{display:flex;gap:.4rem;flex-wrap:wrap;margin-top:-.1rem}
.jobwrap.editing{box-shadow:inset 0 0 0 1px rgba(255,106,26,.45)}

/* customers — a row each; the initials are a mono orange mark on a small
   chamfered tile, the way the drawer marks its cards */
.people{display:flex;flex-direction:column;gap:.4rem;margin-top:.4rem}
.person{display:flex;align-items:center;gap:.7rem;width:100%;text-align:left;border:0;cursor:pointer;
  background:var(--panel2);box-shadow:var(--hair);border-radius:var(--cut-row);padding:.7rem .8rem;
  color:var(--ink);font:inherit;min-height:56px}
.person .bd{flex:1;min-width:0;display:flex;flex-direction:column;gap:.15rem}
.person .nm{font-weight:600;font-size:.94rem}
.person .m{font-size:.78rem;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.person .t{font-family:var(--mono);font-size:.6rem;color:var(--muted);flex:none}
.av{flex:none;width:36px;height:36px;border-radius:var(--cut-row);display:grid;place-items:center;
  background:var(--console);box-shadow:var(--hair);color:var(--or);font-family:var(--mono);
  font-weight:700;font-size:.8rem;letter-spacing:.06em;text-shadow:var(--bloom)}
.av.big{width:48px;height:48px;font-size:1rem}
.back{border:0;background:transparent;color:var(--blue);font:inherit;font-size:.86rem;cursor:pointer;
  display:inline-flex;align-items:center;gap:.2rem;padding:.6rem 0 .2rem;min-height:44px}
.back svg{transform:rotate(180deg)}
.pcard{position:relative;background:var(--console);box-shadow:var(--hair);border-radius:var(--cut);padding:.95rem}
.pcard::before,.pcard::after{content:"";position:absolute;width:9px;height:9px;
  border:1px solid var(--bracket);pointer-events:none}
.pcard::before{top:5px;left:5px;border-right:0;border-bottom:0}
.pcard::after{bottom:5px;right:5px;border-left:0;border-top:0}
.ptop{display:flex;align-items:center;gap:.7rem}
.pid .nm{font-size:1.1rem;font-weight:700;letter-spacing:-.01em}
.pid .m{font-size:.8rem;color:var(--muted)}
.pform{display:flex;flex-direction:column;gap:.5rem;margin-top:.8rem;padding-top:.7rem;border-top:1px solid var(--line2)}
.pform label{display:flex;flex-direction:column;gap:.25rem;font-family:var(--mono);font-size:.6rem;
  letter-spacing:.12em;text-transform:uppercase;color:var(--muted)}
.pform input,.pform textarea{background:var(--deep);border:0;color:var(--ink);box-shadow:var(--hair);
  padding:.55rem .65rem;border-radius:var(--cut-sm);font:inherit;font-size:16px;letter-spacing:0;text-transform:none;resize:vertical}
.pform input:focus,.pform textarea:focus{outline:0;box-shadow:inset 0 0 0 1px var(--or),0 0 0 3px rgba(255,106,26,.18)}
/* the label is mono caps; what he types in the box is not a readout */
.pform input,.pform textarea,.editf input[type=text],.editf textarea,.editf select{
  font-family:Poppins,ui-sans-serif,system-ui,-apple-system,sans-serif}

/* the timeline */
.tl{display:flex;flex-direction:column;gap:0;margin-top:.3rem;padding-left:.2rem}
.tli{display:flex;gap:.7rem;padding:.55rem 0;position:relative}
.tli+.tli{border-top:1px solid var(--line2)}
.tli .dot{flex:none;width:10px;height:10px;border-radius:50%;margin-top:.35rem;background:var(--muted)}
.tli.booked .dot,.tli.job .dot{background:var(--ok)}
.tli.escalated .dot,.tli.need .dot{background:var(--hot)}
.tli.message .dot{background:var(--blue)}
.tli.cancelled .dot{background:var(--muted);box-shadow:0 0 0 2px rgba(123,148,172,.3)}
.tli .tlb{flex:1;min-width:0}
.tli .tlh{display:flex;justify-content:space-between;gap:.5rem;align-items:baseline}
.tli .tlh b{font-size:.88rem}
.tli .tlh .t{font-family:var(--mono);font-size:.6rem;color:var(--muted);white-space:nowrap}
.tli .tlx{font-size:.83rem;color:var(--ink2);margin-top:.15rem;line-height:1.45}
.audit,.handled{margin-top:.8rem;background:var(--panel);box-shadow:var(--hair2);border-radius:var(--cut-row);padding:.5rem .8rem}
.audit summary,.handled summary{cursor:pointer;font-family:var(--mono);font-size:.62rem;letter-spacing:.12em;
  text-transform:uppercase;color:var(--muted);padding:.3rem 0;list-style:none;min-height:34px;
  display:flex;align-items:center;gap:.4rem}
.audit summary::-webkit-details-marker,.handled summary::-webkit-details-marker{display:none}
.audit summary::before,.handled summary::before{content:"›";font-size:1rem;line-height:1;color:var(--or);
  transition:transform .15s;display:inline-block;font-family:var(--mono)}
.audit[open] summary::before,.handled[open] summary::before{transform:rotate(90deg)}
.audit .al{font-size:.78rem;color:var(--ink2);padding:.35rem 0;border-top:1px solid var(--line2)}
.audit .al .t{font-family:var(--mono);font-size:.6rem;color:var(--muted)}
.handled .hl{display:flex;align-items:center;justify-content:space-between;gap:.5rem;font-size:.82rem;
  color:var(--ink2);padding:.4rem 0;border-top:1px solid var(--line2)}

/* this month vs last */
.month{background:var(--panel2);box-shadow:var(--hair);border-radius:var(--cut-row);padding:.6rem .8rem}
.month table{width:100%;border-collapse:collapse;font-size:.86rem}
.month th{font-family:var(--mono);font-size:.58rem;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);
  text-align:right;padding:.2rem 0 .4rem;font-weight:500}
.month th:first-child{text-align:left}
.month td{padding:.38rem 0;border-top:1px solid var(--line2)}
.month td.v{text-align:right;font-variant-numeric:tabular-nums;font-weight:600;font-family:var(--mono);font-size:.82rem}
.month td.v.muted{color:var(--muted);font-weight:400}
.month td.d{text-align:right;font-family:var(--mono);font-size:.66rem;width:3.4rem;color:var(--muted)}
.month td.d.up{color:var(--ok)}
.month td.d.down{color:var(--hot)}
@media(min-width:640px){.stats{grid-template-columns:repeat(5,1fr)}}

/* ===== look round · builder B: the shell (app bar, tabs, hero strip) — appended at integration, 26 Sep 2026 ===== */
/* =====================================================================
   look round, 26 Sep 2026 — builder B: the shell.
   Appended to app.css AFTER builder A's file, so every rule here wins by
   source order (same specificity as the base block it replaces) or by
   one extra class. Blocks owned here and nowhere else: .install, .demobar,
   .appbar*, .pulse, .tabs*, .tb*, .badge, .appfoot, .toast, .greet*,
   .asof — plus the new .hud strip that app 20 draws over the greeting.
   Tokens (--console, --cut-sm, --bracket, --bloom …) come from A's :root;
   each use carries a fallback so this file also stands on the base file.
   ===================================================================== */

/* ---------- the install-to-home-screen bar: a console slab ---------- */
.install{background:var(--console,linear-gradient(180deg,#15273A 0%,#0C1826 100%));
  border-bottom:1px solid var(--line);color:var(--ink2);font-size:.8rem;line-height:1.4;
  gap:.6rem;padding:.55rem .9rem}
.install .ic{color:var(--or);filter:drop-shadow(0 0 6px rgba(255,106,26,.45))}
.install .x{color:var(--muted);border-radius:var(--cut-sm,2px 8px 2px 8px)}
.install .x:hover,.install .x:active{color:var(--or-lt)}

/* ---------- the status line over the app bar (three PHP states) ----------
   The word is a kicker — mono caps behind a 14px rule in the status colour:
   green when the phone is live, amber when the figures are still the
   sample or the package has no phone. The sentence is plain ink. Nothing
   is tinted any more; the state is the rule and the word, not the ground. */
.demobar{background:var(--deep);border-bottom:1px solid var(--line2);
  padding:.5rem max(.9rem,calc(50% - 22rem));display:flex;flex-wrap:wrap;gap:.3rem .6rem;
  align-items:flex-start;justify-content:flex-start;font-size:.76rem;color:var(--ink2);
  text-align:left;line-height:1.45}
/* the sentence sits beside a short kicker (LIVE) and drops under a long one
   (NO PHONE ON THIS PACKAGE) rather than squeezing into a five-word column */
.demobar>span{flex:1 1 14rem;min-width:0}
.demobar>b{font-family:var(--mono);font-size:.6rem;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;flex:none;color:var(--or-lt);display:inline-flex;align-items:center;
  gap:.4rem;padding-top:.22rem;line-height:1.2}
.demobar>b::before{content:"";width:14px;height:2px;background:currentColor;flex:none}
.demobar.demo-live>b{color:var(--ok)}
.demobar.demo-sample>b,.demobar.demo-nophone>b{color:var(--warn)}
/* the phone number in the no-phone state is a number to dial, not a kicker */
.demobar span b{font-family:inherit;font-size:inherit;letter-spacing:0;text-transform:none;
  color:var(--ink);font-weight:600;white-space:nowrap}

/* ---------- the app bar: a two-line HUD on a phone, one line wider ----------
   Line 1: the Jetsy lockup on the left; Español (lang.js mounts it before
           Sign out) and Sign out as quiet hairline buttons on the right.
   Line 2: the account as a chamfered chip (company · plan) on the left, the
           phone's state as a readout on the right.
   Two lines because the base bar, with lang.js's button in it, already
   crushed the company name to "Happy …" at 402px. From 640px everything
   sits on one line. The grid places by class and href, so lang.js's button
   lands in its cell wherever it is inserted. */
.appbar{background:linear-gradient(180deg,rgba(21,39,58,.94),rgba(10,22,34,.96));
  backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border-bottom:1px solid var(--line)}
.appbar .in{display:grid;grid-template-columns:auto minmax(0,1fr) auto auto;
  grid-template-areas:"mark . lang out" "who who who live";
  align-items:center;column-gap:.5rem;row-gap:.35rem;padding:.4rem .9rem .5rem}
/* the lockup, ~26px tall; the old head-in-a-square is the fallback (.head) */
.appbar .mark{grid-area:mark;width:auto;height:26px;border-radius:0;background:transparent;
  display:flex;align-items:center;overflow:visible}
.appbar .mark img{display:block;height:26px;width:auto}
.appbar .mark.head{width:34px;height:34px;border-radius:8px;background:#0A2540;
  display:grid;place-content:center;overflow:hidden}
.appbar .mark.head img{width:30px;height:30px}
/* the account chip */
.appbar .who{grid-area:who;justify-self:start;max-width:100%;min-width:0;
  display:inline-flex;flex-direction:row;align-items:center;gap:.4rem;
  padding:.28rem .5rem;line-height:1.25;
  border-radius:var(--cut-sm,2px 8px 2px 8px);
  background:var(--console,linear-gradient(180deg,#15273A 0%,#0C1826 100%));
  box-shadow:inset 0 0 0 1px var(--line)}
.appbar .who .co{font-size:.78rem;font-weight:600;color:var(--ink);min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.appbar .who .pl{flex:none;font-family:var(--mono);font-size:.58rem;letter-spacing:.08em;
  text-transform:uppercase;color:var(--or-lt);white-space:nowrap}
.appbar .who .pl::before{content:"·";color:var(--muted);margin-right:.4rem}
/* the readout: ● ANSWERING in mono; .off is muted by dashboard.php's own rule */
.appbar .live{grid-area:live;justify-self:end;margin-left:0;gap:.4rem;padding-right:.1rem}
.appbar .live span{font-family:var(--mono);font-size:.58rem;font-weight:600;letter-spacing:.09em;
  text-transform:uppercase;color:var(--ok)}
.appbar .live.off span{color:var(--ink)}   /* greyed to ~62% by the page rule; ink keeps it above 4.5:1 */
.pulse{width:7px;height:7px;border-radius:50%;background:var(--ok);
  box-shadow:0 0 0 3px rgba(62,213,152,.16),0 0 8px rgba(62,213,152,.55);
  animation:bp 2.6s ease-in-out infinite}
/* the quiet buttons */
.appbar .b.sm{min-height:44px;padding:.3rem .6rem;font-family:var(--mono);font-size:.62rem;
  font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--ink2);
  background:transparent;box-shadow:inset 0 0 0 1px var(--line);
  border-radius:var(--cut-sm,2px 8px 2px 8px)}
.appbar .b.sm:hover,.appbar .b.sm:active{color:var(--or-lt);box-shadow:inset 0 0 0 1px var(--or);
  background:rgba(255,106,26,.08)}
.appbar a[href="logout.php"]{grid-area:out}
.appbar #langsw{grid-area:lang}
@media(min-width:640px){
  .appbar .in{grid-template-columns:auto minmax(0,1fr) auto auto auto;
    grid-template-areas:"mark who live lang out";row-gap:0;padding:.5rem .9rem}
  .appbar .live{margin-right:.3rem}
}

/* ---------- Today's hero: the HUD strip, then the greeting ----------
   A row of mono caps readouts separated by ·, one accent value each — the
   time (peach), the phone's state (green, with the pulse), the Needs-you
   count, the month's calls and the minutes. app 20 draws it from what
   viewToday already holds and leaves out any readout it has no data for. */
.hud{display:flex;flex-wrap:wrap;align-items:center;gap:.2rem .5rem;margin:1rem 0 0;
  font-family:var(--mono);font-size:.6rem;font-weight:500;letter-spacing:.14em;
  text-transform:uppercase;color:var(--muted);line-height:1.6}
.hud .hud-r{display:inline-flex;align-items:center;gap:.35rem;white-space:nowrap}
.hud .hud-r:not(:last-child)::after{content:"·";color:var(--muted);opacity:.7;margin-left:.15rem}
.hud .hud-r b{font-weight:700;color:var(--or-lt)}
.hud .hud-on b{color:var(--ok)}
.hud .pulse{width:6px;height:6px}
.greet{padding:.5rem 0 .15rem}
.greet .hi{font-size:1.5rem;font-weight:700;letter-spacing:-.03em;line-height:1.15;color:var(--ink)}
.greet .sub{font-size:.86rem;color:var(--muted);margin-top:.25rem}

/* ---------- the bottom tab bar, in console style ----------
   Height is never set here: app.js measures nav.tabs into --tabh, and the
   eight tabs wrap to two rows of four on a phone (one row from 640px). */
.tabs{background:linear-gradient(180deg,rgba(16,29,44,.97),rgba(5,9,15,.98));
  backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border-top:1px solid var(--line)}
.tabs .in{grid-template-columns:repeat(4,1fr)}
.tb{padding:.5rem .2rem .5rem;gap:.22rem;color:var(--muted);transition:color .15s}
.tb svg{display:block}
.tb span{font-family:var(--mono);font-size:.58rem;font-weight:600;letter-spacing:.12em;
  text-transform:uppercase;line-height:1.3;white-space:nowrap}
.tb:active{color:var(--or-lt)}
.tb[aria-selected="true"]{color:var(--or)}
.tb[aria-selected="true"] svg{filter:drop-shadow(0 0 6px rgba(255,106,26,.45))}
.tb[aria-selected="true"] span{text-shadow:0 0 10px rgba(255,106,26,.4)}
.tb[aria-selected="true"]::before{top:0;left:30%;right:30%;height:2px;background:var(--or);
  border-radius:0 0 2px 2px;box-shadow:0 0 10px rgba(255,106,26,.6)}
/* the Today badge: the drawer's badge device — navy figure on orange, chamfered, a small bloom */
.tb .badge{top:.32rem;right:50%;transform:translateX(1.05rem);background:var(--or);color:var(--deep);
  font-family:var(--mono);font-size:.58rem;font-weight:700;letter-spacing:0;text-shadow:none;
  min-width:16px;height:16px;line-height:16px;padding:0 4px;border-radius:2px 8px 2px 8px;
  box-shadow:var(--bloom-sm,0 0 10px rgba(255,106,26,.4))}
@media(max-width:639.98px){.tb:nth-child(n+5){box-shadow:inset 0 1px 0 var(--line2)}}
@media(min-width:640px){.tabs .in{grid-template-columns:repeat(8,1fr)}}

/* ---------- the toast: a console slab with a kicker rule ---------- */
.toast{background:var(--console,linear-gradient(180deg,#15273A 0%,#0C1826 100%));color:var(--ink);
  font-size:.8rem;font-weight:600;padding:.6rem 1rem;border-radius:var(--cut-sm,2px 8px 2px 8px);
  box-shadow:inset 0 0 0 1px var(--line),0 12px 30px -12px #000;display:flex;align-items:center;gap:.55rem}
.toast::before{content:"";width:14px;height:2px;background:var(--or);flex:none;
  box-shadow:0 0 8px rgba(255,106,26,.5)}

/* ---------- the footer: a hairline that starts orange and fades ---------- */
footer.appfoot{position:relative;padding:1.5rem .9rem 1.1rem;margin-top:1.8rem;border-top:0;
  color:var(--muted);font-size:.72rem;line-height:1.75}
footer.appfoot::before{content:"";position:absolute;left:.9rem;right:.9rem;top:0;height:1px;
  background:linear-gradient(90deg,var(--or) 0,var(--or) 14px,var(--line) 14px,transparent 100%)}
footer.appfoot a{color:var(--or-lt);text-decoration:none;border-bottom:1px solid rgba(255,178,89,.35)}
footer.appfoot a:hover,footer.appfoot a:active{border-bottom-color:var(--or-lt)}

/* ===== integration · 26 Sep 2026 (CSS only) ===== */
/* The Calls search hint ("Search a name, number, or anything that was said")
   is longer than the box on a phone and was cut mid-word; it now ends in an
   ellipsis. Typed text is unaffected. */
.search input{text-overflow:ellipsis}

/* ===== fit round · 26 Sep 2026 · every screen fits the phone — appended at integration (agent 9) ===== */

/* ----- fit round · 00-v1 · Leticia's first fixes, the status-bar mechanics rewritten by 01-shell ----- */
/* ---------- the phone's status bar ----------
   Joe, 26 Sep 2026: the dashboard runs as a home-screen app with a
   translucent status bar, so the page began under the clock and the
   demobar's first line sat behind "12:48". The inset is env() — zero in
   Safari and on a desktop, the real height on a phone, so every rule here
   does nothing outside the home-screen app.
   Leticia's first fix put one dark strip under the clock. It kept the clock
   clear, but once the page scrolled it sat over the app bar as a darker
   band, with the words scrolling under the bar showing through its top edge.
   Rewritten by the shell slice (fit round) so that whatever is under the
   clock is always the bar you are looking at:
     - at the top of the page, the status line (.demobar) reaches up under
       the clock in its own colour, and the band stays put while the line
       scrolls up under it, so its words never pass behind the clock;
     - once the app bar sticks, the bar's own top colour reaches up under
       the clock, so the bar meets the top of the screen with no strip. */

/* the status line: a band as tall as the status bar is its first row. It is
   sticky, so it holds under the clock while the line's words slide up
   beneath it, and it leaves with the line's last row (no gap, no text in
   the clock). The line is stacked over the app bar (41 over 40) so the app
   bar's band below stays hidden until the line has scrolled away.
   .2rem padding + the band + the .3rem row gap = the .5rem the words had. */
.demobar{position:relative;z-index:41;padding-top:.2rem}
.demobar::before{content:"";flex:0 0 100%;min-width:0;height:env(safe-area-inset-top,0px);
  position:-webkit-sticky;position:sticky;top:0;background:inherit;pointer-events:none}

/* the app bar sticks just below the clock, and carries a band of its own
   top colour up under the clock — from a distance the bar reaches the top
   of the screen, like the phone's own bars */
.appbar{top:env(safe-area-inset-top,0px)}
.appbar::before{content:"";position:absolute;left:0;right:0;bottom:100%;
  height:env(safe-area-inset-top,0px);background:var(--console-lit,#15273A);pointer-events:none}

/* a floor under both bars (39, below the app bar's 40 and the line's 41):
   never seen in normal use; it keeps the clock on solid ground if the page
   is pulled down past its top, or on a screen drawn without the bars */
body::before{content:"";position:fixed;top:0;left:0;right:0;height:env(safe-area-inset-top,0px);
  background:var(--deep);z-index:39;pointer-events:none}

/* the Calls filters wrap onto a second line instead of sliding off the
   right edge ("Message tak…" at 402px) — Leticia's first fix, unchanged */
.filters{flex-wrap:wrap;overflow-x:visible;padding-bottom:0}

/* ----- fit round · 01-shell · the top of the screen and the bars: jump-to landing, install bar, app bar at 375, tab marker, HUD dots, toast ----- */
/* ===== fit round · 01-shell: the top of the screen and the bars (26 Sep 2026) =====
   CSS only. The status-bar mechanics (the band under the clock) are in
   00-v1.app.css, which this slice also owns. */

/* ---------- where "jump to" lands ----------
   board.js and app.js bring a card to the top with scrollIntoView (follow a
   lost lead to Leads, open a card from the Business drawer, the Leads card
   after a change). The top of the screen is the status bar and the sticky
   app bar (62 + 90px on Joe's phone), so the card's title landed under the
   clock and behind the bar. The page now keeps that much room at the top
   for anything scrolled into view (and for a field the phone scrolls to
   when it is tapped). Two lines of app bar on a phone, one line from 640px. */
html{scroll-padding-top:calc(env(safe-area-inset-top,0px) + 6.1rem)}
@media (min-width:640px){html{scroll-padding-top:calc(env(safe-area-inset-top,0px) + 4.3rem)}}

/* ---------- the install bar (Safari only) ----------
   iPhone Safari has no install prompt, so the page keeps its "Install" button
   hidden (the hidden attribute) and only Android/Chrome unhides it. The
   button's .b rule (display:inline-flex) was beating the hidden attribute, so
   every iPhone visitor saw an orange "Install" button that did nothing. */
.install #install-go[hidden]{display:none}

/* ---------- the app bar's second line on 375-393 wide phones ----------
   The company chip and the phone readout share one line. In Spanish
   ("CONTESTANDO") and in the not-connected state ("NOT CONNECTED") the
   company was cut to "Happy Brush Paint…" on a 375 phone — 8px short. A few
   px come back from the gaps and letter-spacing (the eye does not see a
   difference), and a muted readout that is still too long ("SOLO EL
   TABLERO") folds onto two lines instead of taking the company's room.
   Joe's 402 phone and anything wider are untouched. */
@media (max-width:400px){
  .appbar .in{column-gap:.4rem}
  .appbar .who{padding-left:.4rem;padding-right:.4rem;gap:.3rem}
  .appbar .who .pl{letter-spacing:.06em}
  .appbar .who .pl::before{margin-right:.3rem}
  .appbar .live{gap:.3rem;padding-right:0}
  .appbar .live span{letter-spacing:.06em}
  .appbar .live.off{max-width:5.2rem}
  .appbar .live.off span{text-align:right;line-height:1.3}
}

/* ---------- the tab bar's active marker, second row ----------
   On a phone the eight tabs are two rows of four, and the orange marker
   hangs from the TOP of the active tab. For Customers, Invoices, Leads and
   Setup that top edge is the line between the rows, 8px under the first
   row's label — so with Leads open the marker read as an underline of
   "JOBS". A second-row tab now carries the same marker on its outer edge,
   under its own label; the first row keeps it on top of the bar. */
@media (max-width:639.98px){
  .tabs .tb:nth-child(n+5)[aria-selected="true"]::before{top:auto;bottom:0;border-radius:2px 2px 0 0}
}

/* ---------- Today: the HUD strip over the greeting ----------
   The "·" between readouts hung at the END of a line whenever the next
   readout wrapped ("… 0 NEED YOU ·" on Joe's phone, two of them in Spanish at
   375). The separator now sits in the gap BEFORE each readout; the one in
   front of the first readout of a line falls outside the strip's left edge,
   where it is clipped. Same dot, same colour, same spacing as before (the
   gap is the width the dot and its spaces took). */
#main .hud{column-gap:1.45rem;overflow-x:hidden;overflow-x:clip}
#main .hud .hud-r:not(:last-child)::after{content:none}
#main .hud .hud-r{position:relative}
#main .hud .hud-r+.hud-r::before{content:"·";position:absolute;top:0;bottom:0;right:100%;width:1.45rem;
  display:flex;align-items:center;justify-content:center;letter-spacing:0;
  color:var(--muted);opacity:.7;pointer-events:none}

/* ---------- the toast ("Saved", "Sent — it should arrive in a second" …) ----------
   1. It could never be wider than half the screen: left:50% leaves only 50vw
      for its width, so max-width:88vw never applied and "Saved here, but the
      phone agent did not update" came out as three or four lines in a 188px
      column. It is now as wide as its words, up to 88vw, and centred.
   2. A message that wraps reads from its orange rule, left to right, instead
      of two centred lines beside a rule on the left.
   It stays where it was, just above the tab bar. It used to sit under the Ask
   Jetsy button there (the button covered "Saved" on Joe's phone); 02-fab's
   part makes the button step aside while a toast is showing, so the toast
   keeps its place rather than both of them moving. */
#toast.toast{left:0;right:0;margin:0 auto;width:max-content;max-width:88vw;transform:none;text-align:left}

/* ----- fit round · 02-fab · the Ask Jetsy button (folds to the head, steps aside) and the room at the bottom ----- */
/* app 22 (26 Sep 2026): Joe chose "Top bar" — the button now lives in the app
   bar (html.j2-bar, section at the end of this file). Every rule below is the
   FLOATING fallback only, for a page where the bar is missing; :not(.j2-bar)
   keeps them off the button once it is in the bar. */
/* ===== fit round 02-fab · 26 Sep 2026 — the Ask Jetsy button and the room at the bottom =====
   Joe's 12:48 screenshot: the floating "Ask Jetsy 4.0" pill (#j2btn.j2-btn,
   built by ../assets/js/jetsy20.js) sat on the calendar's List button and the
   month title, and never moved; at the bottom of every screen it sat on the
   footer's links; on Business it covered "Save now".
   The button's own stylesheet (../assets/css/jetsy20.css) loads AFTER this
   file and is not in the workspace, so every rule here wins on specificity
   (html:not(.j2-bar) body #j2btn.j2-btn) with !important on what must hold. app.js sets two
   classes on <html>: j2-on (this account has the assistant) and j2-mini (the
   page has scrolled, or the screen has been up 4.5 s). */

/* Where it sits, on every screen: 12px above the tab bar. --j2-bottom is the
   tab bar's measured height + 12px (jetsy20.js, and app.js on every render).
   On Joe's phone the live pill stood 46pt above the tabs (the home-bar inset
   counted twice); this pins it to the measured line. The transitions are for
   the fold below and for stepping aside (further down). */
html:not(.j2-bar) body #j2btn.j2-btn{bottom:var(--j2-bottom,calc(var(--tabh,4.4rem) + 12px)) !important;
  transition:padding .28s ease,gap .28s ease,opacity .18s ease,visibility 0s linear 0s}
html:not(.j2-bar) body #j2btn.j2-btn .j2-btn-t{max-width:12em;transition:max-width .28s ease,opacity .16s ease .12s}

/* Folded (html.j2-mini): the round head alone, 56px, in the same corner. The
   words are squeezed to nothing, not removed, so the button keeps its name
   for VoiceOver (its aria-label is untouched too). */
html.j2-mini:not(.j2-bar) body #j2btn.j2-btn{padding:0 !important;gap:0 !important;min-width:56px !important;
  min-height:56px !important;justify-content:center !important;align-items:center !important;
  border-radius:999px !important}
html.j2-mini:not(.j2-bar) body #j2btn.j2-btn .j2-btn-t{max-width:0 !important;opacity:0 !important;overflow:hidden !important;
  margin:0 !important;padding:0 !important;transition:max-width .28s ease,opacity .12s ease}

/* Stepping aside while something else owns that corner: the Business Save bar
   when it has something to say ("Unsaved changes", "Save now", "No
   connection…"), and a toast. It fades out and comes back when they go. */
html:not(.j2-bar) body:has(#bstatus [data-bact="savenow"]) #j2btn.j2-btn{opacity:0 !important;visibility:hidden !important;
  pointer-events:none !important;transition:padding .28s ease,gap .28s ease,opacity .18s ease,visibility 0s linear .18s}
html:not(.j2-bar) body:has(#bstatus .m:not(:empty)) #j2btn.j2-btn{opacity:0 !important;visibility:hidden !important;
  pointer-events:none !important;transition:padding .28s ease,gap .28s ease,opacity .18s ease,visibility 0s linear .18s}
html:not(.j2-bar) body:has(> #toast:not([hidden])) #j2btn.j2-btn{opacity:0 !important;visibility:hidden !important;
  pointer-events:none !important;transition:padding .28s ease,gap .28s ease,opacity .18s ease,visibility 0s linear .18s}
/* ...and gone under anything that takes the whole screen: a quote or an
   invoice (app.js's own body.jdoc-open rule, repeated at this file's
   strength) and the "Still in English" report panel. */
html:not(.j2-bar) body.jdoc-open #j2btn.j2-btn{display:none !important}
html:not(.j2-bar) body:has(> #langrep) #j2btn.j2-btn{display:none !important}

/* No sliding for people who have asked their phone for less motion. */
@media (prefers-reduced-motion:reduce){
  html:not(.j2-bar) body #j2btn.j2-btn,html:not(.j2-bar) body #j2btn.j2-btn .j2-btn-t{transition:none !important}
}

/* The room at the bottom, on every tab and Business drawer: with the
   assistant on, the page ends with the button's own band (its 12px gap + 60px)
   empty above the tab bar, so the last line and the footer scroll fully clear
   of the tabs AND the button. Accounts without the assistant have no j2-on
   and keep the tab bar's height alone; a quote or invoice hides the button,
   so it does not need the band either. */
html.j2-on:not(.j2-bar) body{padding-bottom:calc(var(--j2-bottom,calc(var(--tabh,4.4rem) + 12px)) + 60px)}
html.j2-on:not(.j2-bar) body.jdoc-open{padding-bottom:var(--tabh,4.4rem)}

/* ----- fit round · 03-today · Today: Every day strip, Needs you rows, the section headings ----- */
/* =====================================================================
   FIT ROUND · slice 03-today · 26 Sep 2026 — appended to app.css.
   The Today tab's two section headings ("Needs you", "Today's jobs").
   Scoped with `.greet ~ .h`: the greeting is drawn on Today only, so the
   headings on Calls, Jobs, Customers… (07-tabs) are untouched.
   ===================================================================== */

/* ---- Today: a long count drops under its heading, whole ----
   At 375 in Spanish "TRABAJOS DE HOY" and its count "5 EN PROCESO · 3
   AGENDADOS" were squeezed onto one line: both broke in two and the
   hairline between them shrank to a stub. When the two do not fit side by
   side, the count now moves to its own line under the heading, on the
   right, and the heading keeps its full line with its hairline. When they
   fit (English, wider phones) nothing changes. */
#main .greet~.h{flex-wrap:wrap;row-gap:.2rem}
#main .greet~.h .n{flex:0 0 auto;max-width:100%}

/* ----- fit round · 07-tabs · Calls, Jobs, Leads, Customers, Invoices + the sheet, Setup ----- */
/* =====================================================================
   07-tabs · fit round, 26 Sep 2026 — the bottom tabs other than Today and
   Business: Calls, Jobs, Leads, Customers, Invoices (+ the quote/invoice
   sheet), Setup. Appended to app.css. Fit only: same palette, fonts,
   console cards and chamfers. Every selector is scoped to its own screen.
   ===================================================================== */

/* ---------- Calls: a call row's name line ----------
   The name and its REPEAT / CORRECTED chips were one line cut with "…", so
   the second chip was sliced into an empty box with dots in it, and a long
   caller ("Casa Verde Apartments Property Management Office") could never be
   read in full, even with the call open. The name now wraps and each chip
   moves down whole. */
.calls .call .who .n{white-space:normal;overflow:visible;text-overflow:clip;overflow-wrap:anywhere}
.calls .call .who .n .chip{display:inline-block;margin:.1rem .2rem .1rem 0;vertical-align:middle;overflow-wrap:normal}

/* ---------- Calls: the summary of an OPEN call ----------
   Two lines and "…" is right for the list; once the call is opened the whole
   summary shows, so the full text is always one tap away. */
.calls .call.open .who .sum{display:block;-webkit-line-clamp:unset;overflow:visible}

/* ---------- Customers → one person → "Everything with …" ----------
   A "Needs you" line in the timeline carries the class "need", which is also
   the Today card's class: it drew as a red console card with the dot stacked
   on its top corner and the words flush against its left edge. It is a
   timeline line like its neighbours again. */
.tl .tli.need{flex-direction:row;gap:.7rem;background:none;box-shadow:none;border-radius:0;
  padding:.55rem 0;animation:none}
.tl .tli.need::before,.tl .tli.need::after{content:none;display:none}

/* ---------- Setup: the on/off switches ----------
   The 44px tap-target floor (.sw{min-height:44px}) stretched the 46x26
   switch into a tall square with its knob stuck in the top corner. The
   switch is drawn at its own size again; an invisible band above and below
   keeps the 44px it is tapped by. */
.set .sw{height:26px;min-height:26px;margin:9px 0;vertical-align:middle}
.set .sw::before{content:"";position:absolute;left:-2px;right:-2px;top:-9px;bottom:-9px}

/* ---------- Setup: the note under Who answers ----------
   "A personality is a voice, never a licence…" sat 2px inside the card, so
   the words touched its edges and the last line ran into the corner
   bracket. Same inset as the rows above it. (!important: the margin is an
   inline style.) */
.set .grp>p.note{margin:.35rem .6rem .85rem !important}

/* ---------- Setup: Phone number, not connected yet ----------
   The number box stopped at 9.5rem (the right-hand settings boxes' width)
   with the example number pushed to its right edge, half a row of nothing
   beside Save. It fills the row and reads from the left, like a search box. */
.set #numIn{max-width:none;min-width:0;text-align:left}

/* ---------- Calls: the From / To dates ----------
   Two date boxes at their own width left a ragged gap at the right of the
   row, and at 375 (longer "PARA" in Spanish) To dropped under From at a
   different width. The word now sits over its box, the way every other
   form label in the app does, and the two boxes share the row half and
   half; "All time" (after a date is picked) takes the line under them. */
#main .dates label{flex:1 1 40%;min-width:0;flex-direction:column;align-items:stretch;gap:.3rem}
#main .dates input{width:100%;min-width:0;box-sizing:border-box}

/* ---------- section headings on these tabs: the count on the right ----------
   At 375 a long heading ("BOOKED BY YOUR RECEPTIONIST", "AGENDADO POR TU
   RECEPCIONISTA") squeezed its count until it broke in two — "0" over
   "BOOKED", with a 4px stub of hairline between. A short count now keeps
   its line and the heading wraps instead; a long count is still capped at
   56% and wraps inside it, as before. Scoped (with :has) to the tabs this
   slice owns, so Today and Business headings are untouched. */
body:has(#tabbar [aria-selected="true"]:not([data-tab="today"]):not([data-tab="board"])) #main .h .n{flex-shrink:0}

/* ---------- Jobs: "Open the board" and its note ----------
   The note is an inline span beside the button: once it wrapped (375, and
   in Spanish on every phone) its second line ran back under the button to
   the screen edge. Button and note now sit side by side as two blocks, and
   the note drops under the button whole when there is no room. */
#main div:has(>button.b[data-tab="board"]){display:flex;align-items:center;flex-wrap:wrap;gap:.35rem 0}
#main div:has(>button.b[data-tab="board"])>.note{flex:1 1 10rem;min-width:0}

/* ---------- the quote / invoice on paper ----------
   The status-bar strip (a fixed band as tall as the phone's clock area) is a
   pseudo-element of <body>, so the print sheet's "only the document" rule
   (body>:not(#main)) never reached it: on paper it could sit over the top of
   the sheet — the company name and owner. Print only; the screen keeps it. */
@media print{html body::before,html body::after{content:none !important;display:none !important}}

/* ----- fit round · 09-merge · integration: where two slices meet (save bar, print, jumps) ----- */
/* =====================================================================
   09-merge · fit round, 26 Sep 2026 — the integration lead's fixes where two
   slices meet. Appended to app.css after every slice.
   ===================================================================== */

/* ---------- a toast while the Business Save bar is up ----------
   (01-shell ↔ 06-ops) The toast sits .8rem over the tab bar and the Save bar
   now sits right on the tab bar, so a toast shown while the bar had words
   ("Copied" over "UNSAVED CHANGES" or "No connection…") was drawn on top of
   them. While the bar has something to say, the toast rises clear of it
   (the bar is Save now's 44px + its padding, or up to three lines of its
   message on a 375 phone). The Ask Jetsy button is already out of the way
   in both cases (02-fab). */
html body:has(#bstatus [data-bact="savenow"])>#toast.toast,
html body:has(#bstatus .m:not(:empty))>#toast.toast{bottom:calc(var(--tabh,4.4rem) + 4.4rem + .8rem)}

/* =====================================================================
   Ask Jetsy in the top bar · app 22 · 26 Sep 2026
   Joe, after watching the fit round on his phone: the round head still sat
   on the calendar's › and the right edge of List at the top of Today. Asked
   where Ask Jetsy should live, he chose "Top bar". app.js (app 22) moves the
   button jetsy20.js builds (#j2btn, with its tap handler) into the app bar
   and sets html.j2-bar; from then on nothing floats over the page.
   The live ../assets/css/jetsy20.css loads after this file and draws the
   floating pill, so every property it could set is pinned here with
   !important at (1,3,3) specificity. The look is the bar's own quiet
   button (.appbar .b.sm): hairline, chamfer, 44px tall — holding the head.
   ===================================================================== */
html.j2-bar .appbar .in{grid-template-columns:auto minmax(0,1fr) auto auto auto;
  grid-template-areas:"mark . ask lang out" "who who who who live"}
@media(min-width:640px){
  html.j2-bar .appbar .in{grid-template-columns:auto minmax(0,1fr) auto auto auto auto;
    grid-template-areas:"mark who live ask lang out"}
}
html.j2-bar body header.appbar #j2btn.j2-btn{grid-area:ask;justify-self:end;
  position:relative !important;inset:auto !important;top:auto !important;right:auto !important;
  bottom:auto !important;left:auto !important;transform:none !important;animation:none !important;
  z-index:auto !important;float:none !important;
  display:inline-flex !important;align-items:center !important;justify-content:center !important;
  width:44px !important;min-width:44px !important;max-width:44px !important;
  height:auto !important;min-height:44px !important;max-height:none !important;
  padding:0 !important;margin:0 !important;gap:0 !important;border:0 !important;
  border-radius:var(--cut-sm,2px 8px 2px 8px) !important;
  background:transparent !important;box-shadow:inset 0 0 0 1px var(--line) !important;
  color:var(--ink2) !important;opacity:1 !important;visibility:visible !important;
  pointer-events:auto !important;cursor:pointer;transition:none !important;
  -webkit-tap-highlight-color:transparent}
html.j2-bar body header.appbar #j2btn.j2-btn:hover,
html.j2-bar body header.appbar #j2btn.j2-btn:active{box-shadow:inset 0 0 0 1px var(--or) !important;
  background:rgba(255,106,26,.08) !important}
html.j2-bar body header.appbar #j2btn.j2-btn:focus-visible{outline:2px solid var(--or);outline-offset:2px}
html.j2-bar body header.appbar #j2btn.j2-btn img{display:block !important;width:26px !important;height:26px !important;
  min-width:0 !important;max-width:none !important;padding:0 !important;margin:0 !important;
  border:0 !important;border-radius:0 !important;background:none !important;box-shadow:none !important;
  transform:none !important;animation:none !important}
/* the words stay in the button for VoiceOver (it also has its aria-label);
   on screen the head says it */
html.j2-bar body header.appbar #j2btn.j2-btn .j2-btn-t{position:absolute !important;width:1px !important;
  height:1px !important;overflow:hidden !important;clip:rect(0 0 0 0) !important;
  clip-path:inset(50%) !important;white-space:nowrap !important;margin:0 !important;padding:0 !important;
  max-width:none !important;opacity:1 !important}
/* out of the way under a quote or invoice and the "Still in English"
   report, as before (app 20's docfix) */
html.j2-bar body.jdoc-open header.appbar #j2btn.j2-btn,
html.j2-bar body:has(> #langrep) header.appbar #j2btn.j2-btn{display:none !important}

/* ---------- Ask Jetsy reads as Jetsy 4.0 ----------
   Joe, 26 Sep 2026, looking at the head in the top bar: "Make more obvious
   that it's jetsy 4.0". The head keeps its place; "4.0" sits beside it in
   the plan chip's orange mono, and the button takes the orange rim and
   warm ground the selected buttons use (Month, Week), so it reads as the
   one thing in the bar that belongs to the 4.0 package. The words are a
   generated ::after — the button's accessible name is still its
   aria-label, "Chat with Jetsy 4.0". */
html.j2-bar body header.appbar #j2btn.j2-btn{width:auto !important;min-width:44px !important;max-width:none !important;
  padding:0 .7rem 0 .5rem !important;gap:.4rem !important;
  box-shadow:inset 0 0 0 1px var(--or) !important;background:rgba(255,106,26,.12) !important}
html.j2-bar body header.appbar #j2btn.j2-btn::after{content:"4.0";display:block;font-family:var(--mono);
  font-size:.74rem;font-weight:700;letter-spacing:.04em;line-height:1;color:var(--or-lt);white-space:nowrap}
html.j2-bar body header.appbar #j2btn.j2-btn:hover,
html.j2-bar body header.appbar #j2btn.j2-btn:active{background:rgba(255,106,26,.2) !important}
/* the narrowest iPhones (375) keep every button in the bar on one line */
@media (max-width:380px){
  html.j2-bar body header.appbar #j2btn.j2-btn{padding:0 .5rem 0 .4rem !important;gap:.3rem !important}
  html.j2-bar body header.appbar #j2btn.j2-btn img{width:24px !important;height:24px !important}
}

/* ----- pages 1 · 27 Sep 2026 · the way back from the four admin pages -----
   Customers, Is it running?, What Jetsy 4.0 knows and Sign-in security each
   open with "← Dashboard" in tiny grey capitals 16px from the top of the
   screen, which on the phone is under the clock. Joe: "Can you edit the top
   left corner to be visible and easy to click". Each page now starts below
   the clock, and the link is a real 44px button. The selector is the link
   itself, written the same way on all four pages, so Sign-in security gets
   the fix from this file without the page being edited. (Customers, Is it
   running? and What Jetsy 4.0 knows also carry the same rules inline.) */
main.adm,main.kb,main.sec{padding-top:calc(env(safe-area-inset-top,0px) + .75rem)}
main a.back[href="dashboard.php"]{display:inline-flex;align-items:center;gap:.4rem;min-height:44px;
  padding:0 1rem 0 .8rem;margin:0 0 .4rem;border-radius:12px;background:var(--panel2);
  box-shadow:inset 0 0 0 1px var(--line);color:var(--ink);font-family:inherit;font-size:.95rem;
  font-weight:600;letter-spacing:0;text-transform:none;text-decoration:none;line-height:1.2;
  -webkit-tap-highlight-color:transparent}
main a.back[href="dashboard.php"]:active{background:var(--panel3)}
main a.back[href="dashboard.php"]:focus-visible{outline:2px solid var(--blue);outline-offset:2px}
