/* ═══════════════ FALAQ DASHBOARD — App shell & components ═══════════════ */
@import url('tokens.css');

*,*::before,*::after{box-sizing:border-box;margin:0;padding:0;}
html,body{height:100%;}
html{-webkit-text-size-adjust:100%;}
body{
  overflow-x:hidden;
  font-family:var(--font);
  background:var(--bg);
  color:var(--ink);
  line-height:1.7;
  -webkit-font-smoothing:antialiased;
  font-variant-ligatures:common-ligatures contextual;
  font-feature-settings:"liga" 1,"clig" 1,"calt" 1,"rlig" 1;
}
img{max-width:100%;display:block;}
a{color:inherit;text-decoration:none;}
button,input,select,textarea{font:inherit;color:inherit;}
[hidden]{display:none !important;}
:focus-visible{outline:2px solid var(--mint);outline-offset:2px;border-radius:6px;}

/* subtle brand glow, fixed so it never scrolls */
body::before{
  content:"";position:fixed;inset:0;z-index:0;pointer-events:none;
  background:
    radial-gradient(900px 520px at 92% -8%,rgba(53,221,212,.10),transparent 62%),
    radial-gradient(760px 460px at 4% 104%,rgba(161,249,176,.07),transparent 60%);
}
/* The same glow on white would be a grey smudge — it needs to be both
   weaker and warmer to read as light rather than as dirt. */
:root[data-theme="light"] body::before{
  background:
    radial-gradient(900px 520px at 92% -8%,rgba(14,143,134,.07),transparent 62%),
    radial-gradient(760px 460px at 4% 104%,rgba(18,165,148,.05),transparent 60%);
}

/* ───────────── Scrollbars ───────────── */
*::-webkit-scrollbar{width:10px;height:10px;}
*::-webkit-scrollbar-track{background:transparent;}
*::-webkit-scrollbar-thumb{background:var(--surface-3);border-radius:100px;border:2px solid var(--bg);}
*::-webkit-scrollbar-thumb:hover{background:#2e3849;}

/* ═════════════ LOGIN ═════════════ */
.login{
  position:fixed;inset:0;z-index:60;
  display:grid;place-items:center;padding:24px;
  background:var(--bg);
}
.login-card{
  position:relative;z-index:1;
  width:min(420px,100%);
  background:var(--surface-1);
  border:1px solid var(--line);
  border-radius:var(--r-lg);
  padding:38px 32px 32px;
  box-shadow:var(--shadow-2);
}
.login-logo{height:46px;width:auto;margin:0 auto 20px;}
.login-card h1{font-size:1.35rem;font-weight:900;text-align:center;line-height:1.4;}
.login-card .sub{text-align:center;color:var(--ink-3);font-size:.86rem;margin-bottom:26px;}

/* ═════════════ APP SHELL ═════════════ */
.app{position:relative;z-index:1;display:flex;min-height:100vh;min-height:100dvh;}

/* ── Sidebar (right, because dir=rtl) ── */
.sidebar{
  width:var(--sidebar-w);flex:0 0 var(--sidebar-w);
  background:var(--surface-1);
  border-inline-start:1px solid var(--line);
  display:flex;flex-direction:column;
  position:sticky;top:0;height:100vh;
}
.sidebar-head{
  height:var(--topbar-h);flex:0 0 var(--topbar-h);
  display:flex;align-items:center;gap:10px;
  padding:0 20px;border-bottom:1px solid var(--line);
}
.nav-close{display:none;margin-inline-start:auto;}
.sidebar-head img{height:26px;width:auto;}
.sidebar-head .wordmark{font-weight:900;font-size:1.02rem;letter-spacing:.01em;}
.sidebar-nav{
  flex:1;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;
  padding:14px 12px;display:flex;flex-direction:column;gap:2px;
}
.nav-section{
  font-size:.68rem;font-weight:700;color:var(--ink-3);
  letter-spacing:.09em;padding:16px 12px 6px;
}
.nav-item{
  display:flex;align-items:center;gap:11px;
  padding:9px 12px;border-radius:var(--r);
  color:var(--ink-2);font-size:.9rem;font-weight:500;
  cursor:pointer;border:0;background:transparent;width:100%;
  text-align:start;transition:background .18s var(--ease),color .18s var(--ease);
}
.nav-item svg{flex:0 0 18px;width:18px;height:18px;stroke:currentColor;fill:none;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;opacity:.85;}
.nav-item:hover{background:var(--surface-2);color:var(--ink);}
.nav-item.active{background:var(--surface-3);color:var(--ink);font-weight:700;}
.nav-item.active svg{stroke:var(--mint);opacity:1;}
.nav-item .badge{
  margin-inline-start:auto;background:var(--mint);color:#08130c;
  font-size:.66rem;font-weight:900;padding:1px 7px;border-radius:var(--r-pill);
}

/* profile block pinned to sidebar bottom */
.sidebar-user{
  border-top:1px solid var(--line);padding:12px;
  padding-bottom:calc(12px + env(safe-area-inset-bottom));
  display:flex;align-items:center;gap:11px;
}
.sidebar-user .avatar{width:38px;height:38px;border-radius:50%;object-fit:cover;flex:0 0 38px;border:1.5px solid var(--line-accent);}
.sidebar-user .meta{min-width:0;flex:1;}
.sidebar-user .name{font-size:.87rem;font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.sidebar-user .role{font-size:.72rem;color:var(--ink-3);}

/* ── Main ── */
.main{flex:1;min-width:0;display:flex;flex-direction:column;}
.topbar{
  height:var(--topbar-h);flex:0 0 var(--topbar-h);
  display:flex;align-items:center;gap:14px;
  padding:0 clamp(16px,2.4vw,30px);
  border-bottom:1px solid var(--line);
  background:color-mix(in srgb,var(--bg) 82%,transparent);
  backdrop-filter:blur(14px);
  position:sticky;top:0;z-index:20;
}
.topbar h2{font-size:1.06rem;font-weight:900;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.topbar .spacer{flex:1;}
.topbar-logo{display:none;height:24px;width:auto;}
.burger{
  display:none;background:transparent;border:0;cursor:pointer;
  padding:8px;margin-inline-start:-8px;border-radius:var(--r-sm);
  flex:0 0 auto;
}
.burger svg{width:24px;height:24px;stroke:var(--ink);fill:none;stroke-width:1.9;stroke-linecap:round;}
.burger:active{background:var(--surface-2);}
.view{
  padding:clamp(14px,2.4vw,30px);flex:1;min-width:0;
  padding-bottom:calc(clamp(14px,2.4vw,30px) + env(safe-area-inset-bottom));
}

/* drawer backdrop — element lives in the DOM so it can fade */
.scrim{
  position:fixed;inset:0;z-index:45;
  background:var(--veil);backdrop-filter:blur(3px);
  opacity:0;visibility:hidden;pointer-events:none;
  transition:opacity .28s var(--ease),visibility .28s var(--ease);
}
body.nav-open .scrim{opacity:1;visibility:visible;pointer-events:auto;}
body.nav-open{overflow:hidden;}

/* ═════════════ PRIMITIVES ═════════════ */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:7px;
  font-weight:700;font-size:.87rem;
  padding:.6em 1.25em;border-radius:var(--r-pill);
  border:0;cursor:pointer;white-space:nowrap;
  transition:transform .3s var(--spring),background .2s,box-shadow .3s var(--ease),opacity .2s;
}
.btn svg{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;}
.btn:active{transform:scale(.96);}
.btn:disabled,.btn-icon:disabled{opacity:.5;cursor:not-allowed;}
.btn-primary{background:var(--grad);color:#07130d;}
.btn-primary:hover:not(:disabled){box-shadow:0 10px 26px -10px rgba(53,221,212,.5);}
.btn-ghost{background:var(--surface-2);color:var(--ink);box-shadow:inset 0 0 0 1px var(--line-2);}
.btn-ghost:hover:not(:disabled){background:var(--surface-3);}
.btn-danger{background:transparent;color:var(--danger);box-shadow:inset 0 0 0 1px rgba(255,122,122,.35);}
.btn-danger:hover:not(:disabled){background:rgba(255,122,122,.1);}
.btn-sm{padding:.42em .85em;font-size:.79rem;}
.btn-block{width:100%;}
.btn-icon{
  background:transparent;border:0;cursor:pointer;padding:6px;border-radius:var(--r-sm);
  color:var(--ink-3);display:inline-grid;place-items:center;transition:background .18s,color .18s;
}
.btn-icon svg{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;}
.btn-icon:hover{background:var(--surface-3);color:var(--ink);}
.btn-icon.danger:hover{color:var(--danger);}

/* Fields */
.field{display:flex;flex-direction:column;gap:6px;margin-bottom:15px;}
.field > label{font-size:.81rem;font-weight:700;color:var(--ink-2);}
.field .hint{font-size:.74rem;color:var(--ink-3);line-height:1.5;}
.input,.select,.textarea{
  background:var(--surface-2);
  border:1px solid var(--line-2);
  border-radius:var(--r);
  padding:.62em .85em;font-size:.88rem;width:100%;
  transition:border-color .2s,box-shadow .2s;
}
.textarea{resize:vertical;min-height:92px;line-height:1.7;}
.select{appearance:none;cursor:pointer;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236C7A8C' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-size:16px;background-position:left .8em center;padding-inline-start:2.2em;}
.input:focus,.select:focus,.textarea:focus{
  outline:none;border-color:var(--teal);box-shadow:0 0 0 3px rgba(53,221,212,.13);
}
.input::placeholder,.textarea::placeholder{color:var(--ink-3);}

/* Per-field validation on the public forms. The message is written into
   the field by JS rather than authored in the markup, so a field only
   ever carries one and it disappears the moment the field is fixed.
   `aria-invalid` is what a screen reader announces; the red border and
   the line under it are for everyone else. */
.input[aria-invalid="true"],.select[aria-invalid="true"],.textarea[aria-invalid="true"]{
  border-color:var(--danger);
}
.input[aria-invalid="true"]:focus,.select[aria-invalid="true"]:focus,
.textarea[aria-invalid="true"]:focus{box-shadow:0 0 0 3px rgba(255,122,122,.14);}
.field-err{font-size:.75rem;font-weight:600;color:var(--danger);line-height:1.5;}
.field-row{display:grid;grid-template-columns:1fr 1fr;gap:14px;}
.check{display:flex;align-items:center;gap:9px;cursor:pointer;font-size:.86rem;user-select:none;}

/* Optional half of a form, folded away. The client's complaint about
   the project form was its length, not its fields — everything that is
   not required to save now starts closed. */
details.more{
  border:1px solid var(--line);border-radius:var(--r);
  background:var(--surface-2);padding:0 14px;margin-bottom:15px;
}
details.more > summary{
  cursor:pointer;list-style:none;padding:12px 0;
  font-size:.83rem;font-weight:700;color:var(--ink-2);
  display:flex;align-items:center;gap:8px;
}
details.more > summary::-webkit-details-marker{display:none;}
details.more > summary::after{content:"+";margin-inline-start:auto;color:var(--ink-3);font-weight:900;font-size:1rem;}
details.more[open] > summary::after{content:"−";}
details.more[open]{padding-bottom:2px;}
details.more > summary + *{margin-top:4px;}
.check input{width:17px;height:17px;accent-color:var(--mint);cursor:pointer;flex:0 0 17px;}

/* Card */
.card{
  background:var(--surface-1);border:1px solid var(--line);
  border-radius:var(--r-lg);padding:20px;box-shadow:var(--shadow-1);
}
.card-head{display:flex;align-items:center;gap:12px;margin-bottom:16px;}
.card-head h3{font-size:.98rem;font-weight:900;}
.card-head .spacer{flex:1;}

/* Page header */
.page-head{display:flex;align-items:flex-start;gap:14px;flex-wrap:wrap;margin-bottom:20px;}
.page-head .titles{flex:1;min-width:220px;}
.page-head h1{font-size:1.32rem;font-weight:900;line-height:1.35;}
.page-head p{color:var(--ink-3);font-size:.85rem;}
.page-actions{display:flex;gap:9px;flex-wrap:wrap;}

/* Toolbar */
.toolbar{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:16px;}
.search{position:relative;flex:1;min-width:190px;max-width:340px;}
.search input{padding-inline-start:2.3em;}
.search svg{
  position:absolute;inset-inline-start:.8em;top:50%;transform:translateY(-50%);
  width:15px;height:15px;stroke:var(--ink-3);fill:none;stroke-width:2;pointer-events:none;
}

/* Table */
.table-wrap{border:1px solid var(--line);border-radius:var(--r-lg);overflow-x:auto;background:var(--surface-1);}
table{width:100%;border-collapse:collapse;font-size:.86rem;min-width:560px;}
th{
  text-align:start;font-weight:700;font-size:.75rem;color:var(--ink-3);
  padding:11px 14px;background:var(--surface-2);white-space:nowrap;
  border-bottom:1px solid var(--line);letter-spacing:.03em;
}
td{padding:12px 14px;border-bottom:1px solid var(--line);vertical-align:middle;}
tbody tr:last-child td{border-bottom:0;}
tbody tr{transition:background .15s;}
tbody tr:hover{background:var(--surface-2);}
td.actions{white-space:nowrap;text-align:end;}
td .cell-strong{font-weight:700;color:var(--ink);}
td .cell-sub{font-size:.76rem;color:var(--ink-3);}

/* Chips / pills */
.chip{
  display:inline-flex;align-items:center;gap:5px;
  font-size:.72rem;font-weight:700;padding:3px 10px;border-radius:var(--r-pill);
  background:var(--surface-3);color:var(--ink-2);white-space:nowrap;
}
.chip.ok{background:rgba(161,249,176,.13);color:var(--ok);}
.chip.info{background:rgba(53,221,212,.13);color:var(--info);}
.chip.warn{background:rgba(245,200,107,.13);color:var(--warn);}
.chip.danger{background:rgba(255,122,122,.13);color:var(--danger);}

/* Empty & loading */
.empty{
  text-align:center;padding:52px 20px;color:var(--ink-3);
  border:1px dashed var(--line-2);border-radius:var(--r-lg);
}
.empty svg{width:34px;height:34px;stroke:var(--ink-3);fill:none;stroke-width:1.4;margin:0 auto 12px;}
.empty h4{font-size:.95rem;font-weight:700;color:var(--ink-2);margin-bottom:4px;}
.empty p{font-size:.82rem;margin-bottom:16px;}
.skeleton{height:52px;border-radius:var(--r);background:linear-gradient(90deg,var(--surface-1),var(--surface-2),var(--surface-1));background-size:220% 100%;animation:shimmer 1.3s infinite;margin-bottom:8px;}
@keyframes shimmer{0%{background-position:220% 0}100%{background-position:-220% 0}}
.spinner{
  width:15px;height:15px;border-radius:50%;
  border:2px solid rgba(255,255,255,.22);border-top-color:currentColor;
  animation:spin .7s linear infinite;display:inline-block;
}
@keyframes spin{to{transform:rotate(360deg)}}

/* Stat grid */
.stats-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));gap:var(--gap);}
.stat{
  background:var(--surface-1);border:1px solid var(--line);
  border-radius:var(--r-lg);padding:17px 19px;position:relative;overflow:hidden;
}
.stat::after{content:"";position:absolute;inset-block:0;inset-inline-start:0;width:3px;background:var(--grad);opacity:.75;}
.stat .k{font-size:.77rem;color:var(--ink-3);font-weight:600;}
.stat .v{font-size:1.75rem;font-weight:900;line-height:1.25;font-variant-numeric:tabular-nums;}
.stat .d{font-size:.74rem;color:var(--ink-3);}

/* Bar chart (pure CSS, no library) */
.bars{display:flex;flex-direction:column;gap:11px;}
.bar-row{display:grid;grid-template-columns:112px 1fr 52px;gap:11px;align-items:center;font-size:.82rem;}
.bar-track{height:9px;background:var(--surface-3);border-radius:var(--r-pill);overflow:hidden;}
.bar-fill{height:100%;background:var(--grad);border-radius:var(--r-pill);transition:width .6s var(--ease);}
.bar-val{text-align:end;font-variant-numeric:tabular-nums;color:var(--ink-2);font-weight:700;}

/* ═════════════ MODAL ═════════════ */
.modal-back{
  position:fixed;inset:0;z-index:80;
  background:var(--veil-strong);backdrop-filter:blur(6px);
  display:grid;place-items:center;padding:20px;
  animation:fadeIn .18s var(--ease);
}
@keyframes fadeIn{from{opacity:0}to{opacity:1}}
.modal{
  background:var(--surface-1);border:1px solid var(--line-2);
  border-radius:var(--r-lg);box-shadow:var(--shadow-2);
  width:min(560px,100%);max-height:88vh;display:flex;flex-direction:column;
  animation:pop .26s var(--spring);
}
.modal.wide{width:min(900px,100%);}
@keyframes pop{from{opacity:0;transform:translateY(14px) scale(.98)}to{opacity:1;transform:none}}
.modal-head{display:flex;align-items:center;gap:12px;padding:18px 22px;border-bottom:1px solid var(--line);}
.modal-head h3{font-size:1rem;font-weight:900;flex:1;}
.modal-body{padding:22px;overflow-y:auto;flex:1;}
.modal-foot{display:flex;gap:10px;justify-content:flex-end;padding:16px 22px;border-top:1px solid var(--line);}

/* ═════════════ TOASTS ═════════════ */
.toasts{position:fixed;inset-block-end:22px;inset-inline-start:22px;z-index:120;display:flex;flex-direction:column;gap:9px;}
.toast{
  background:var(--surface-3);border:1px solid var(--line-2);
  border-radius:var(--r);padding:11px 16px;font-size:.85rem;font-weight:600;
  box-shadow:var(--shadow-2);max-width:340px;
  animation:toastIn .3s var(--spring);
  border-inline-start:3px solid var(--ink-3);
}
.toast.ok{border-inline-start-color:var(--ok);}
.toast.err{border-inline-start-color:var(--danger);}
@keyframes toastIn{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}

/* ═════════════ MODULE-SPECIFIC ═════════════ */

/* Stacked avatars — project teams, task assignees */
.avatars{display:flex;}
.avatars img,.avatars .ini{
  width:26px;height:26px;border-radius:50%;object-fit:cover;
  border:2px solid var(--surface-1);margin-inline-start:-8px;
  display:grid;place-items:center;font-size:.66rem;font-weight:900;background:var(--surface-3);
}
.avatars > :first-child{margin-inline-start:0;}

/* Project detail — content + side column */
.proj-detail{
  display:grid;grid-template-columns:1fr minmax(240px,300px);
  gap:var(--gap);align-items:start;
}
.proj-detail > *{min-width:0;}

/* Project detail — update timeline */
.timeline{display:flex;flex-direction:column;gap:0;}
.tl-item{display:grid;grid-template-columns:26px 1fr;gap:13px;padding-bottom:18px;position:relative;}
.tl-item::before{content:"";position:absolute;inset-inline-start:12px;top:24px;bottom:0;width:1.5px;background:var(--line-2);}
.tl-item:last-child{padding-bottom:0;}
.tl-item:last-child::before{display:none;}
.tl-dot{width:26px;height:26px;border-radius:50%;background:var(--surface-3);display:grid;place-items:center;
  border:1.5px solid var(--line-2);z-index:1;}
.tl-dot svg{width:13px;height:13px;stroke:var(--mint);fill:none;stroke-width:2;}
.tl-body{padding-top:1px;}
.tl-head{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;}
.tl-head b{font-size:.85rem;}
.tl-head time{font-size:.73rem;color:var(--ink-3);}
.tl-text{font-size:.85rem;color:var(--ink-2);line-height:1.75;white-space:pre-wrap;}

/* ═════════════ KANBAN BOARD ═════════════
   Columns are stages, colour-coded. Every column sets --stage inline
   from the tokens, and everything inside it — the top rule, the dot,
   the drop highlight, a task card's spine — reads that one variable,
   so re-colouring a stage is one number in tokens.css. */
.board-wrap{
  overflow-x:auto;padding-bottom:12px;margin-inline:-4px;padding-inline:4px;
  -webkit-overflow-scrolling:touch;
  /* Without this a swipe that runs out of board turns into the
     browser's back gesture, which loses the screen. */
  overscroll-behavior-x:contain;
}
/* Snapping fights the drag auto-scroll: every programmatic nudge would
   be yanked back to the nearest column. */
body.k-dragging .board-wrap{scroll-snap-type:none;}
.kanban{
  display:grid;grid-auto-flow:column;grid-auto-columns:minmax(268px,1fr);
  gap:14px;align-items:stretch;min-width:min-content;
}
.k-col{
  background:var(--surface-1);border:1px solid var(--line);
  border-radius:var(--r-lg);display:flex;flex-direction:column;
  overflow:hidden;min-height:150px;scroll-snap-align:start;
}
.k-col-head{
  display:flex;align-items:center;gap:9px;
  padding:12px 15px;border-bottom:1px solid var(--line);
  border-top:3px solid var(--stage);
}
.k-col-head h3{font-size:.87rem;font-weight:900;flex:1;min-width:0;}
.k-col-head .k-dot{width:9px;height:9px;border-radius:50%;background:var(--stage);flex:0 0 9px;}
.k-count{
  font-size:.72rem;font-weight:900;color:var(--ink-3);
  background:var(--surface-2);border-radius:var(--r-pill);padding:1px 9px;
}
.k-drop{
  padding:12px;display:flex;flex-direction:column;gap:11px;flex:1;
  min-height:96px;transition:background .18s var(--ease),box-shadow .18s var(--ease);
}
/* The whole column lights up, not just the strip under the cards —
   the drop is resolved by nearest column, so the feedback has to
   promise the same forgiveness. */
.k-drop.over{
  background:color-mix(in srgb,var(--stage) 14%,transparent);
  box-shadow:inset 0 0 0 2px color-mix(in srgb,var(--stage) 55%,transparent);
}
.k-drop .k-blank{
  font-size:.78rem;color:var(--ink-3);text-align:center;padding:14px 6px;
  border:1px dashed var(--line-2);border-radius:var(--r);
  display:grid;place-items:center;flex:1;min-height:64px;
}

.k-card{
  position:relative;overflow:hidden;
  background:var(--surface-2);border:1px solid var(--line);
  border-radius:var(--r);padding:13px 14px 12px;
  display:flex;flex-direction:column;gap:8px;cursor:grab;
  /* The card claims no gesture: a finger on it still pans the board,
     and the drag is armed by holding still instead (see kanban.js).
     Claiming pan-y here used to mean the columns could only be swiped
     from the gaps between cards, which on a phone is most of nothing.
     `manipulation` keeps the pan and drops only the double-tap zoom. */
  touch-action:manipulation;
  /* A selection or a native image drag starting on the card steals the
     pointer stream and kills the drag mid-gesture. */
  user-select:none;-webkit-user-select:none;-webkit-touch-callout:none;
  transition:border-color .2s var(--ease),transform .25s var(--ease),box-shadow .25s var(--ease);
}
.k-card img{-webkit-user-drag:none;}
/* Hover lift only where there is a real pointer — on touch it sticks
   after the tap and the card looks stuck mid-air. */
@media (hover:hover){
  .k-card:hover{border-color:var(--line-accent);transform:translateY(-1px);}
}
.k-card h4{font-size:.9rem;font-weight:900;line-height:1.5;}
.k-card .excerpt{
  font-size:.79rem;color:var(--ink-3);line-height:1.6;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
}

/* ── The one thing the client asked to be unmistakable: project vs task.
   A project wears the brand gradient as a full-width cap and a filled
   badge; a task wears a spine in its own stage colour and an outlined
   badge. Different edge, different side, different badge weight — it
   survives being glanced at from across the room. */
.k-card.is-project{padding-top:16px;background:var(--surface-1);}
.k-card.is-project::before{
  content:"";position:absolute;inset-inline:0;top:0;height:4px;background:var(--grad);
}
.k-card.is-task{padding-inline-start:17px;}
.k-card.is-task::before{
  content:"";position:absolute;inset-block:0;inset-inline-start:0;width:4px;background:var(--stage);
}

.k-top{display:flex;align-items:center;gap:7px;flex-wrap:wrap;}
.k-badge{
  display:inline-flex;align-items:center;gap:5px;
  font-size:.66rem;font-weight:900;padding:2px 9px;border-radius:var(--r-pill);
  letter-spacing:.02em;white-space:nowrap;
}
.k-badge svg{width:11px;height:11px;stroke:currentColor;fill:none;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round;}
.k-badge.project{background:var(--grad);color:#07130d;}
.k-badge.task{background:transparent;color:var(--ink-2);box-shadow:inset 0 0 0 1px var(--line-2);}
.k-parent{
  font-size:.7rem;color:var(--ink-3);font-weight:700;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%;
}
.k-foot{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:.72rem;color:var(--ink-3);}
.k-foot .avatars img,.k-foot .avatars .ini{width:22px;height:22px;border:2px solid var(--surface-2);}
.k-card.is-project .k-foot .avatars img,
.k-card.is-project .k-foot .avatars .ini{border-color:var(--surface-1);}
.k-due{display:inline-flex;align-items:center;gap:4px;font-weight:700;}
.k-due svg{width:12px;height:12px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;}
.k-due.late{color:var(--danger);}
.k-due.soon{color:var(--warn);}

/* Drag feedback. The ghost is a clone under the cursor; the original
   stays in place at low opacity so the column never reflows mid-drag
   and the drop target underneath keeps its geometry. */
.k-card.dragging{opacity:.32;}
.k-ghost{
  position:fixed;z-index:200;pointer-events:none;
  /* inset-0 + transform, because JS writes `transform` on every frame
     and a composited transform is the difference between a ghost that
     tracks the pointer and one that lags behind it. The tilt and lift
     ride the independent rotate/scale properties so they survive.

     `left`, not `inset-inline-start`: the page is RTL, so the logical
     property anchored the ghost's *right* edge to the viewport's right
     edge, and the transform — which is plain clientX/clientY, i.e.
     physical — then pushed it a whole viewport width away from the
     pointer. This origin has to be physical because the number written
     into it is. */
  top:0;left:0;will-change:transform;
  rotate:-1.6deg;scale:1.03;
  box-shadow:var(--shadow-2);opacity:.96;
}
body.k-dragging{cursor:grabbing;user-select:none;}
body.k-dragging .k-card{cursor:grabbing;}

/* ── Moving a card without dragging ──
   Quiet until wanted on a desktop, a first-class button on a phone. */
.k-move{
  display:inline-flex;align-items:center;gap:5px;
  border:1px solid var(--line-2);background:transparent;cursor:pointer;
  color:var(--ink-3);font:inherit;font-size:.7rem;font-weight:700;
  padding:3px 9px;border-radius:var(--r-pill);
  transition:color .18s var(--ease),border-color .18s var(--ease),background .18s var(--ease);
}
.k-move svg{width:12px;height:12px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;}
.k-move:hover,.k-move:focus-visible{color:var(--ink);border-color:var(--line-accent);background:var(--surface-3);}
@media (hover:hover){
  /* Present but recessive until the card is under the cursor. */
  .k-move{opacity:.5;}
  .k-card:hover .k-move,.k-move:focus-visible{opacity:1;}
}

/* ── Stage pager (phones) ──
   Five columns is four flicks wide. These chips make it one tap, and
   they double as the only view of what is off-screen. */
.k-pager{display:none;}
.stage-pick{display:flex;flex-direction:column;gap:8px;}
.stage-opt{
  display:flex;align-items:center;gap:11px;text-align:start;
  border:1px solid var(--line);background:var(--surface-2);cursor:pointer;
  border-radius:var(--r);padding:13px 14px;color:var(--ink);font:inherit;
  border-inline-start:4px solid var(--stage);
  transition:background .18s var(--ease),border-color .18s var(--ease);
}
.stage-opt b{font-size:.9rem;font-weight:900;flex:1;}
.stage-opt:hover{background:var(--surface-3);}
.stage-opt.on{border-color:var(--line-accent);background:var(--surface-3);}

/* Segmented view switch (board ⇄ list) */
.seg{display:inline-flex;background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-pill);padding:3px;gap:2px;}
.seg button{
  border:0;background:transparent;cursor:pointer;color:var(--ink-3);
  font-size:.8rem;font-weight:700;padding:.4em 1em;border-radius:var(--r-pill);
  display:inline-flex;align-items:center;gap:6px;transition:background .18s,color .18s;
}
.seg button svg{width:14px;height:14px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;}
.seg button.on{background:var(--surface-3);color:var(--ink);}

/* Filter chips (الكل / مشاريع / مهام) */
.chip-row{display:flex;gap:7px;flex-wrap:wrap;}
.chip-btn{
  border:1px solid var(--line-2);background:transparent;cursor:pointer;
  color:var(--ink-3);font-size:.78rem;font-weight:700;
  padding:.35em 1em;border-radius:var(--r-pill);transition:all .18s var(--ease);
}
.chip-btn:hover{border-color:var(--line-accent);color:var(--ink-2);}
.chip-btn.on{background:var(--surface-3);color:var(--ink);border-color:var(--line-accent);}

/* ═════════════ TABS (project detail) ═════════════ */
.tabs{display:flex;gap:4px;border-bottom:1px solid var(--line);margin-bottom:20px;overflow-x:auto;}
.tab{
  border:0;background:transparent;cursor:pointer;white-space:nowrap;
  color:var(--ink-3);font-size:.87rem;font-weight:700;
  padding:10px 15px;border-bottom:2px solid transparent;margin-bottom:-1px;
  transition:color .18s,border-color .18s;
}
.tab:hover{color:var(--ink-2);}
.tab.active{color:var(--ink);border-bottom-color:var(--mint);}

/* ═════════════ MODE PICKER (قالب / حر) ═════════════
   The whole point of the two-way split: the free card must look like
   the fast path, so it is listed first and carries the accent. */
.mode-grid{display:grid;gap:12px;}
.mode-card{
  display:flex;align-items:flex-start;gap:14px;text-align:start;width:100%;
  background:var(--surface-2);border:1px solid var(--line-2);border-radius:var(--r);
  padding:16px 17px;cursor:pointer;
  transition:border-color .2s var(--ease),background .2s var(--ease),transform .25s var(--ease);
}
.mode-card:hover{border-color:var(--line-accent);transform:translateY(-1px);}
.mode-card .ico{
  width:40px;height:40px;flex:0 0 40px;border-radius:var(--r);
  display:grid;place-items:center;background:var(--surface-3);
}
.mode-card .ico svg{width:19px;height:19px;stroke:var(--ink-2);fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;}
.mode-card.hot .ico{background:linear-gradient(135deg,rgba(53,221,212,.16),rgba(161,249,176,.1));}
.mode-card.hot .ico svg{stroke:var(--mint);}
.mode-card b{display:block;font-size:.94rem;font-weight:900;margin-bottom:3px;color:var(--ink);}
.mode-card span{display:block;font-size:.8rem;color:var(--ink-3);line-height:1.65;}

/* ═════════════ ATTACHMENTS ═════════════ */
.att-list{display:flex;flex-wrap:wrap;gap:8px;}
.att{
  display:inline-flex;align-items:center;gap:8px;max-width:100%;
  background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r);
  padding:6px 10px;font-size:.79rem;
}
.att svg{width:14px;height:14px;flex:0 0 14px;stroke:var(--ink-3);fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;}
.att .nm{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:200px;}
.att .sz{color:var(--ink-3);font-size:.72rem;font-variant-numeric:tabular-nums;}
.att img{width:28px;height:28px;border-radius:var(--r-sm);object-fit:cover;flex:0 0 28px;}

/* Drop target shared by attachments and the whiteboard */
.dropzone{
  border:1.5px dashed var(--line-2);border-radius:var(--r-lg);
  padding:22px 18px;text-align:center;color:var(--ink-3);font-size:.83rem;
  transition:border-color .2s,background .2s,color .2s;
}
.dropzone.over{border-color:var(--mint);color:var(--ink-2);background:color-mix(in srgb,var(--mint) 8%,transparent);}
.dropzone b{display:block;font-weight:900;color:var(--ink-2);margin-bottom:3px;font-size:.88rem;}

/* ═════════════ WHITEBOARD ═════════════
   An infinite canvas: `.wb-stage` clips, `.wb-world` carries the whole
   pan/zoom as one transform, and every element inside it is positioned
   in world units and never touched again.

   Two rules run through this whole section:

   • `.wb-world` is `direction:ltr` and everything under it uses
     physical `left`/`top`. A canvas coordinate is not a reading
     direction — the element's offset and the pointer's clientX have to
     name the same axis, or nothing lands where it was dropped. Only
     the *text inside* a block is direction-aware, via dir="auto".

   • Chrome divides by `--k` instead of being redrawn. The world
     publishes its zoom scale there, so an outline, a handle or a
     cursor stays the same size on screen at any zoom without JS
     recomputing a single rect per frame. */
.wb-bar{
  display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  margin-bottom:10px;
}
.wb-sep{width:1px;align-self:stretch;background:var(--line-2);margin:2px 2px;}
.wb-swatches{display:flex;gap:5px;}
.wb-sw{
  width:20px;height:20px;border-radius:50%;cursor:pointer;
  border:2px solid var(--line-2);padding:0;
  transition:transform .15s var(--spring),border-color .15s var(--ease);
}
.wb-sw:hover{transform:scale(1.14);border-color:var(--ink-2);}
/* The swatch shows the paint, not the theme colour it is named after. */
.wb-sw.c-ink{background:var(--wb-ink);}
.wb-sw.c-mint{background:var(--wb-mint);}
.wb-sw.c-teal{background:var(--wb-teal);}
.wb-sw.c-warn{background:var(--wb-warn);}
.wb-sw.c-danger{background:var(--wb-danger);}
.wb-sw[aria-pressed="true"]{border-color:var(--ink);transform:scale(1.16);}
.wb-peers .chip{white-space:nowrap;}

/* Tool picker. Pressed state rather than a separate active class, so
   the button tells a screen reader what it tells everyone else. */
.wb-tool[aria-pressed="true"]{
  background:var(--surface-3);color:var(--ink);
  box-shadow:inset 0 0 0 1px var(--line-accent);
}
.wb-widths{display:flex;gap:4px;align-items:center;}
.wb-width{
  width:26px;height:26px;padding:0;border-radius:var(--r-sm);cursor:pointer;
  border:1px solid var(--line-2);background:transparent;
  display:grid;place-items:center;
}
.wb-width i{display:block;border-radius:50%;background:var(--ink-2);}
.wb-width[aria-pressed="true"]{background:var(--surface-3);border-color:var(--line-accent);}
.wb-width[aria-pressed="true"] i{background:var(--ink);}

/* The sheet is white in both themes — see the --wb-* block in
   tokens.css for why it does not follow the theme. */
.wb-stage{
  position:relative;overflow:hidden;
  height:clamp(420px,calc(100dvh - 260px),1000px);
  border:1px solid var(--line);border-radius:var(--r-lg);
  /* The dot grid is the paper's texture, and it pans and zooms with the
     board — applyView() writes --gx/--gy/--gs. A tile is 22 world units,
     so at any zoom the dots keep the same spacing as the content. */
  background:
    radial-gradient(circle at 1px 1px,var(--wb-grid) 1px,transparent 0)
      var(--gx,0) var(--gy,0)/var(--gs,22px) var(--gs,22px),
    var(--wb-paper);
  /* The board owns every touch inside it — a pinch here is a zoom, not
     the browser's page zoom, and a drag is a pan, not a scroll. */
  touch-action:none;cursor:default;outline:none;
}
.wb-stage.pannable{cursor:grab;}
.wb-stage.panning{cursor:grabbing;}
.wb-stage.over{border-color:var(--wb-sel);}
/* A crosshair says "this click makes a mark" — the one warning that a
   click will not select what is under it. */
.wb-stage.inking{cursor:crosshair;}

.wb-world{
  position:absolute;top:0;left:0;width:0;height:0;
  transform-origin:0 0;direction:ltr;
}

.wb-item{
  position:absolute;
  /* No transition on left/top: they are written every frame during a
     drag, and an eased element would trail the pointer. */
  border-radius:var(--r-sm);
}
/* No backing tint: a picture with its background removed is a PNG with
   real transparency, and a tint behind it would put a grey rectangle
   back where the background used to be. */
.wb-item img{
  width:100%;height:100%;display:block;object-fit:contain;
  border-radius:var(--r-sm);
  -webkit-user-drag:none;user-select:none;
}

/* The cut-out pass runs on the main thread — a megapixel flood fill is
   a visible beat, so the button says it is working. */
.wb-tool.busy,.wb-bar .btn.busy{opacity:.75;position:relative;}
.wb-bar .btn.busy svg{animation:spin .8s linear infinite;}
.wb-text{
  padding:6px 8px;line-height:1.65;font-weight:600;color:var(--wb-ink);
  white-space:pre-wrap;overflow-wrap:anywhere;border-radius:var(--r-sm);
  outline:none;
}
.wb-text[data-color="mint"]{color:var(--wb-mint);}
.wb-text[data-color="teal"]{color:var(--wb-teal);}
.wb-text[data-color="warn"]{color:var(--wb-warn);}
.wb-text[data-color="danger"]{color:var(--wb-danger);}
.wb-text.empty::before{content:"اكتب…";color:rgba(16,22,32,.4);}
.wb-item.editing .wb-text{
  background:color-mix(in srgb,var(--wb-sel) 8%,var(--wb-paper));
  box-shadow:0 0 0 calc(2px / var(--k,1)) var(--wb-sel);
}

/* Ink — pencil strokes and arrows. The SVG spans the element's box and
   the geometry is baked into the viewBox, so resizing the box scales
   the drawing with it, stroke included. */
.wb-ink{display:block;width:100%;height:100%;overflow:visible;}
.wb-ink path{fill:none;stroke-linecap:round;stroke-linejoin:round;}
/* Only the stroke is clickable. The bounding box of a diagonal arrow is
   mostly empty space, and making that space swallow clicks would mean a
   long arrow covers everything it crosses. */
.wb-item[data-kind="draw"],.wb-item[data-kind="arrow"]{pointer-events:none;}
.wb-ink .hit{stroke:transparent;pointer-events:stroke;}
.wb-item[data-kind="draw"] .wb-h,.wb-item[data-kind="arrow"] .wb-h{pointer-events:auto;}

/* Selection chrome. The widths divide by the zoom so a 2px ring is
   2px on screen whether the board is at 15% or 400%. */
.wb-item.sel{box-shadow:0 0 0 calc(2px / var(--k,1)) var(--wb-sel);}
.wb-h{
  position:absolute;width:calc(11px / var(--k,1));height:calc(11px / var(--k,1));
  background:var(--wb-paper);border:calc(2px / var(--k,1)) solid var(--wb-sel);
  border-radius:calc(3px / var(--k,1));display:none;z-index:2;
}
.wb-item.one .wb-h{display:block;}
.wb-h.nw{top:calc(-6px / var(--k,1));left:calc(-6px / var(--k,1));cursor:nwse-resize;}
.wb-h.ne{top:calc(-6px / var(--k,1));right:calc(-6px / var(--k,1));cursor:nesw-resize;}
.wb-h.sw{bottom:calc(-6px / var(--k,1));left:calc(-6px / var(--k,1));cursor:nesw-resize;}
.wb-h.se{bottom:calc(-6px / var(--k,1));right:calc(-6px / var(--k,1));cursor:nwse-resize;}

.wb-marquee{
  position:absolute;z-index:5;pointer-events:none;
  border:calc(1.5px / var(--k,1)) dashed var(--wb-sel);
  background:color-mix(in srgb,var(--wb-sel) 12%,transparent);
}

/* The stroke being drawn right now. It lives in one oversized SVG
   pinned to the world's origin so its user units *are* world units —
   no bounding box to compute until the pen comes up. */
.wb-draft{
  position:absolute;left:-20000px;top:-20000px;
  width:40000px;height:40000px;z-index:50;pointer-events:none;
}
.wb-draft path{fill:none;stroke-linecap:round;stroke-linejoin:round;}

/* Other people. Rendered inside the world so a cursor sits on the
   *content* it is pointing at, then counter-scaled so it never grows. */
.wb-cursors{position:absolute;top:0;left:0;z-index:60;pointer-events:none;}
.wb-cursor{
  position:absolute;transform:scale(calc(1 / var(--k,1)));transform-origin:0 0;
  display:flex;align-items:flex-start;gap:2px;white-space:nowrap;
  transition:top .09s linear,left .09s linear;
}
.wb-cursor svg{
  width:19px;height:19px;flex:0 0 19px;
  fill:hsl(var(--hue) 85% 62%);stroke:var(--bg);stroke-width:1.2;
}
.wb-cursor span{
  margin-top:11px;font-size:.68rem;font-weight:800;color:#0B0F16;
  background:hsl(var(--hue) 85% 62%);padding:2px 7px;border-radius:var(--r-pill);
}

.wb-hint{
  position:absolute;inset:0;display:grid;place-content:center;
  text-align:center;color:rgba(16,22,32,.45);font-size:.84rem;pointer-events:none;
  padding:20px;
}
.wb-hint b{display:block;font-weight:900;color:rgba(16,22,32,.7);margin-bottom:5px;font-size:.95rem;}
.wb-legend{margin-top:8px;font-size:.71rem;color:var(--ink-3);line-height:1.9;}

/* Lightbox for a whiteboard picture */
.lightbox{
  position:fixed;inset:0;z-index:130;background:var(--veil-strong);
  display:grid;place-items:center;padding:24px;cursor:zoom-out;
  animation:fadeIn .18s var(--ease);
}
.lightbox img{max-width:100%;max-height:88vh;width:auto;border-radius:var(--r);box-shadow:var(--shadow-2);}

/* Theme switch in the topbar. The button shows the theme it will
   switch *to*, not the one you are in — a moon while you are already
   dark reads as a state badge and gets ignored. */
.theme-toggle svg{width:18px;height:18px;}
.theme-toggle .ico-moon{display:none;}
:root[data-theme="light"] .theme-toggle .ico-sun{display:none;}
:root[data-theme="light"] .theme-toggle .ico-moon{display:block;}

/* The sidebar badge is the one place that puts near-black text on flat
   --mint. Mint is dark in this theme, so that pairing has to flip. (The
   gradient buttons keep their dark ink — the gradient stays light
   enough to carry it.) */
:root[data-theme="light"] .nav-item .badge{color:#fff;}

/* Permissions grid (admin manager) */
.perm-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:9px;}
.perm-item{
  display:flex;align-items:center;gap:9px;padding:9px 12px;
  background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r);
  cursor:pointer;font-size:.83rem;transition:border-color .18s,background .18s;
}
.perm-item:hover{border-color:var(--line-2);}
.perm-item:has(input:checked){border-color:var(--line-accent);background:rgba(161,249,176,.055);}

/* A permission that reaches outside the dashboard — it mails a real
   inbox rather than unlocking a screen — so it sits below the grid at
   full width, tinted, and carries its own explanation. */
.perm-item-hot{
  align-items:flex-start;gap:11px;padding:13px 14px;
  border-color:var(--line-accent);
  background:linear-gradient(135deg,rgba(53,221,212,.07),rgba(161,249,176,.05));
}
.perm-item-hot input{margin-top:2px;flex:0 0 auto;}
.perm-item-hot > span{display:flex;flex-direction:column;gap:4px;}
.perm-item-hot b{font-weight:800;}
.perm-item-hot i{font-style:normal;font-size:.76rem;color:var(--ink-3);line-height:1.6;}
.perm-item-hot:has(input:checked){
  border-color:var(--teal);
  background:linear-gradient(135deg,rgba(53,221,212,.14),rgba(161,249,176,.08));
}

/* Avatar picker */
.avatar-pick{display:flex;gap:10px;flex-wrap:wrap;}
.avatar-pick label{cursor:pointer;position:relative;}
.avatar-pick input{position:absolute;opacity:0;pointer-events:none;}
.avatar-pick .opt{
  width:50px;height:50px;border-radius:50%;overflow:hidden;
  border:2px solid transparent;transition:border-color .2s,transform .25s var(--spring);
  display:grid;place-items:center;background:var(--surface-2);
}
.avatar-pick input:checked + .opt{border-color:var(--mint);transform:scale(1.06);}
.avatar-pick .opt img{width:100%;height:100%;object-fit:cover;}

/* "Upload" tile — sits at the end of the picker, same footprint as a
   choice but reads as an action. */
.opt-upload{
  width:50px;height:50px;border-radius:50%;cursor:pointer;
  background:var(--surface-2);border:1.5px dashed var(--line-2);color:var(--ink-2);
  display:grid;place-items:center;gap:0;line-height:1;
  transition:border-color .2s,color .2s,background .2s;
}
.opt-upload:hover{border-color:var(--mint);color:var(--mint);background:rgba(161,249,176,.06);}
.opt-upload svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:2.2;stroke-linecap:round;}

/* ── Avatar cropper ─────────────────────────────────────── */
.cropper{display:flex;flex-direction:column;gap:14px;align-items:center;}
/* 264px is not arbitrary and must not become fluid: cropper.js maps
   window pixels onto source pixels using the same constant. */
.crop-window{
  position:relative;width:264px;height:264px;
  overflow:hidden;border-radius:var(--r);background:var(--bg);
  cursor:grab;touch-action:none;user-select:none;
}
.crop-window.dragging{cursor:grabbing;}
.crop-window img{position:absolute;max-width:none;pointer-events:none;-webkit-user-drag:none;}
/* Circular guide: the stored image stays square, this only previews
   how every avatar slot will mask it — so the hole has to be the
   window's inscribed circle, no smaller. `closest-side` is what makes
   it one: an unsized radial-gradient measures its stops against the
   farthest *corner*, which drew the guide at 1/√2 of the real avatar
   and made every crop look tighter than the one that got saved. */
.crop-ring{
  position:absolute;inset:0;pointer-events:none;border-radius:var(--r);
  box-shadow:0 0 0 999px rgba(16,20,28,.55) inset;
  -webkit-mask:radial-gradient(circle closest-side at 50% 50%, transparent 0 99%, #000 100%);
          mask:radial-gradient(circle closest-side at 50% 50%, transparent 0 99%, #000 100%);
}
.crop-controls{display:flex;align-items:center;gap:12px;width:264px;}
.crop-zoom-ico{color:var(--ink-3);font-size:1rem;font-weight:900;width:12px;text-align:center;}
.crop-controls input[type=range]{flex:1;accent-color:var(--mint);height:22px;cursor:pointer;}

/* Templates gallery */
.tpl-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:var(--gap);}
.tpl-card{
  background:var(--surface-1);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:19px;display:flex;flex-direction:column;gap:9px;cursor:pointer;
  transition:border-color .22s,transform .3s var(--ease);
}
.tpl-card:hover{border-color:var(--line-accent);transform:translateY(-2px);}
.tpl-card .ico{
  width:40px;height:40px;border-radius:var(--r);display:grid;place-items:center;
  background:rgba(53,221,212,.1);margin-bottom:3px;
}
.tpl-card .ico svg{width:20px;height:20px;stroke:var(--teal);fill:none;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;}
.tpl-card h4{font-size:.92rem;font-weight:900;}
.tpl-card p{font-size:.79rem;color:var(--ink-3);line-height:1.6;}

/* Document preview (printable) */
.doc{
  background:#fff;color:#12161d;border-radius:var(--r);padding:44px 40px;
  font-size:14px;line-height:1.85;
}
.doc h1{font-size:22px;font-weight:900;margin-bottom:4px;color:#0d1117;}
.doc .doc-sub{color:#5a6472;font-size:12px;margin-bottom:22px;}
.doc h2{font-size:15px;font-weight:900;margin:22px 0 7px;padding-bottom:5px;border-bottom:1.5px solid #d9e0e8;}
.doc p{margin-bottom:9px;white-space:pre-wrap;}
.doc table{width:100%;min-width:0;font-size:13px;margin:10px 0;}
.doc th,.doc td{border:1px solid #d9e0e8;padding:7px 10px;background:none;color:inherit;}
.doc th{background:#f1f5f8;color:#3d4757;font-size:12px;}
.doc tbody tr:hover{background:none;}
.doc .doc-head{display:flex;align-items:center;gap:14px;margin-bottom:22px;padding-bottom:16px;border-bottom:2px solid #0f1620;}
.doc .doc-head img{height:34px;width:auto;}
.doc .kv{display:grid;grid-template-columns:150px 1fr;gap:5px 14px;font-size:13px;}
.doc .kv dt{font-weight:900;color:#3d4757;}
.doc .totals{margin-inline-start:auto;width:min(300px,100%);}
.doc .totals .row{display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid #e5eaf0;}
.doc .totals .row.grand{font-weight:900;font-size:16px;border-bottom:0;border-top:2px solid #0f1620;margin-top:5px;padding-top:9px;}
.doc .sign{display:grid;grid-template-columns:1fr 1fr;gap:34px;margin-top:44px;}
.doc .sign div{border-top:1.5px solid #98a3b1;padding-top:7px;font-size:12px;color:#5a6472;}

/* Line-items editor */
.items-table{width:100%;font-size:.84rem;min-width:0;}
.items-table th{padding:7px 9px;font-size:.72rem;}
.items-table td{padding:5px 4px;border-bottom:0;}
.items-table .input{padding:.42em .6em;font-size:.83rem;}
.items-table tbody tr:hover{background:none;}

/* Calculator */
.calc-grid{display:grid;grid-template-columns:minmax(280px,380px) 1fr;gap:var(--gap);align-items:start;}
.split-rows{display:flex;flex-direction:column;gap:9px;}
.split-row{
  display:grid;grid-template-columns:1fr 92px 34px;gap:9px;align-items:center;
  background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r);padding:8px 10px;
}
.payout{
  display:flex;align-items:center;gap:13px;padding:13px 16px;
  background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r);margin-bottom:9px;
}
.payout .who{flex:1;font-weight:700;font-size:.9rem;}
.payout .pct{font-size:.76rem;color:var(--ink-3);}
.payout .amt{font-size:1.05rem;font-weight:900;font-variant-numeric:tabular-nums;color:var(--mint);}

/* Public booking form page */
.pub{
  min-height:100vh;min-height:100dvh;display:grid;place-items:center;position:relative;z-index:1;
  padding:clamp(18px,4vw,34px) clamp(14px,3vw,20px);
  padding-bottom:calc(clamp(18px,4vw,34px) + env(safe-area-inset-bottom));
}
.pub-card{width:min(660px,100%);background:var(--surface-1);border:1px solid var(--line);
  border-radius:var(--r-lg);padding:clamp(26px,4vw,42px);box-shadow:var(--shadow-2);}
.pub-card .brand{display:flex;align-items:center;gap:11px;margin-bottom:22px;}
.pub-card .brand img{height:32px;width:auto;}
.pub-card h1{font-size:1.5rem;font-weight:900;line-height:1.4;margin-bottom:6px;}
.pub-card .lede{color:var(--ink-3);font-size:.88rem;margin-bottom:26px;}
.pub-done{text-align:center;padding:34px 10px;}
.pub-done .tick{width:62px;height:62px;border-radius:50%;background:rgba(161,249,176,.12);
  display:grid;place-items:center;margin:0 auto 18px;}
.pub-done .tick svg{width:30px;height:30px;stroke:var(--mint);fill:none;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round;}
.pub-done h2{font-size:1.2rem;font-weight:900;margin-bottom:7px;}
.pub-done h2:focus{outline:none;}
.pub-done p{color:var(--ink-3);font-size:.88rem;}

/* The way back to the site. It sits on the panel's own lower edge —
   the bar is the base of the card, not a widget floating under the
   text — so the countdown reads as part of the confirmation. */
.pub-back{
  margin-top:26px;padding-top:18px;border-top:1px solid var(--line);
  display:flex;flex-direction:column;align-items:center;gap:12px;
}
.pub-back-note{color:var(--ink-3);font-size:.78rem;}
.pub-bar{
  display:block;width:min(240px,80%);height:4px;border-radius:var(--r-pill);
  background:var(--line-2);overflow:hidden;
}
.pub-bar i{
  display:block;height:100%;border-radius:inherit;background:var(--grad);
  /* RTL: the fill grows from the reading start, which is the right
     edge. transform-origin has no logical keyword, and this is a real
     physical axis — the same exception the canvas code makes. */
  transform:scaleX(0);transform-origin:right center;
}
.pub-bar.run i{animation:pubBack var(--dur,4000ms) linear forwards;}
@keyframes pubBack{to{transform:scaleX(1);}}

/* A date or time field opens from anywhere inside it — picker.js does
   the opening, this says so before the click. */
input[type=date],input[type=time],input[type=datetime-local],input[type=month]{cursor:pointer;}
.pub-note{color:var(--ink-3);font-size:.76rem;text-align:center;margin-top:11px;line-height:1.6;}

/* Section heading inside a long public form. A quiet rule rather than a
   numbered step: four short sections don't need a progress metaphor. */
.step{
  font-size:.78rem;font-weight:700;color:var(--ink-3);letter-spacing:.04em;
  margin:26px 0 12px;padding-bottom:9px;border-bottom:1px solid var(--line);
}
.step:first-child{margin-top:0;}

/* Radio cards (package chooser) */
.pick{display:grid;gap:11px;margin-bottom:4px;}
.pick-card{
  position:relative;display:block;cursor:pointer;
  background:var(--surface-2);border:1px solid var(--line-2);border-radius:var(--r);
  padding:15px 17px;transition:border-color .2s var(--ease),background .2s var(--ease);
}
.pick-card:hover{border-color:var(--line-accent);}
/* The input drives :has() below and stays reachable by keyboard, so it is
   moved out of sight rather than hidden — display:none would drop it from
   the tab order and kill arrow-key selection within the group. */
.pick-card input{position:absolute;opacity:0;width:0;height:0;}
.pick-card:has(input:checked){border-color:var(--mint);background:var(--surface-3);}
.pick-card:has(input:focus-visible){box-shadow:0 0 0 3px rgba(53,221,212,.18);}
.pick-body{display:flex;flex-direction:column;gap:6px;padding-inline-start:26px;}
/* Radio dot, drawn rather than native: the native control cannot be tinted
   to the brand mint in every browser we ship to. */
.pick-body::before{
  content:"";position:absolute;inset-inline-start:17px;top:19px;
  width:15px;height:15px;border-radius:50%;border:1.6px solid var(--line-2);
  transition:border-color .2s,box-shadow .2s;
}
.pick-card:has(input:checked) .pick-body::before{
  border-color:var(--mint);box-shadow:inset 0 0 0 4px var(--mint);
}
.pick-name{font-weight:900;font-size:.95rem;line-height:1.45;}
.pick-desc{color:var(--ink-2);font-size:.83rem;line-height:1.75;}
.pick-rate{color:var(--ink-3);font-size:.76rem;font-weight:700;}
.pick-tag{
  display:inline-block;margin-inline-start:8px;vertical-align:middle;
  font-size:.67rem;font-weight:700;padding:2px 8px;border-radius:var(--r-pill);
  background:rgba(161,249,176,.13);color:var(--mint);
}

/* Jazeel treats */
.jz{background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-lg);padding:17px;}
.jz-head{display:flex;align-items:center;gap:14px;margin-bottom:15px;}
/* The wordmark master is near-black; assets/jazeel.webp is the same art
   re-filled white so it reads on this panel. */
.jz-logo{height:34px;width:auto;flex:0 0 auto;opacity:.92;}
.jz-head p{color:var(--ink-2);font-size:.82rem;line-height:1.7;}
.jz-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:11px;}
.treat{
  background:var(--surface-1);border:1px solid var(--line);border-radius:var(--r);
  padding:9px;text-align:center;transition:border-color .2s var(--ease);
}
.treat.on{border-color:var(--mint);}
/* The product shots are square and already flattened onto a warm off-white,
   so they can sit edge to edge and read as one shoot. */
.treat img{
  width:100%;height:auto;aspect-ratio:1;object-fit:cover;
  border-radius:var(--r-sm);display:block;margin-bottom:9px;
}
.treat-name{display:block;font-size:.8rem;font-weight:700;line-height:1.45;margin-bottom:9px;}
.qty{display:flex;align-items:center;justify-content:center;gap:4px;}
.qty-btn{
  width:28px;height:28px;flex:0 0 28px;border-radius:var(--r-sm);cursor:pointer;
  background:var(--surface-3);border:1px solid var(--line-2);color:var(--ink-2);
  font-size:1rem;line-height:1;display:grid;place-items:center;
  transition:background .18s,color .18s,border-color .18s;
}
.qty-btn:hover{background:var(--surface-2);color:var(--ink);border-color:var(--line-accent);}
.qty-n{min-width:26px;font-size:.9rem;font-weight:900;font-variant-numeric:tabular-nums;}

/* ═════════════ RESPONSIVE ═════════════ */

/* ── Tablet landscape and down ── */
@media (max-width:1100px){
  .proj-detail{grid-template-columns:1fr;}
}
@media (max-width:960px){
  .calc-grid{grid-template-columns:1fr;}
  .doc{padding:30px 24px;}
}

/* ── Drawer mode: sidebar becomes a hamburger menu ──
   Applies to tablets as well as phones, so the content column keeps a
   comfortable width instead of being squeezed by a permanent sidebar. */
@media (max-width:1024px){
  /* Physical sides, not logical: `transform` is always physical, so an
     inline-end anchor under dir=rtl parked the panel on the left while
     sliding it off to the right. The drawer must sit on the same side as
     the desktop sidebar — right for RTL, left for LTR. */
  .sidebar{
    position:fixed;top:0;bottom:0;right:0;left:auto;z-index:50;
    width:min(300px,86vw);flex-basis:auto;height:100%;
    transform:translateX(100%);
    transition:transform .3s var(--ease),visibility .3s var(--ease);
    visibility:hidden;box-shadow:var(--shadow-2);
    padding-top:env(safe-area-inset-top);
  }
  [dir="ltr"] .sidebar{right:auto;left:0;transform:translateX(-100%);}
  body.nav-open .sidebar{transform:none;visibility:visible;}
  .nav-close{display:inline-grid;}
  .burger{display:grid;place-items:center;}
  .topbar-logo{display:block;}
  /* bigger hit areas for touch */
  .nav-item{padding:12px 12px;font-size:.94rem;}
  .btn-icon{padding:9px;}
}
/* keep the drawer shut (and scrim gone) once we're back on desktop */
@media (min-width:1025px){
  .scrim{display:none !important;}
}

/* ── Phones ── */
@media (max-width:720px){
  .topbar{gap:10px;padding:0 16px;}
  .topbar h2{font-size:1rem;}
  .field-row{grid-template-columns:1fr;}
  .page-head{gap:10px;}
  .page-head .titles{min-width:0;flex:1 1 100%;}
  .page-actions{width:100%;}
  .page-actions .btn{flex:1 1 auto;justify-content:center;}
  .search{max-width:none;flex:1 1 100%;}
  .toolbar > .select,.toolbar > .input{flex:1 1 140px;max-width:none !important;}
  .stats-grid{grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:12px;}
  .stat{padding:14px 15px;}
  .stat .v{font-size:1.45rem;}
  .tpl-grid{grid-template-columns:1fr;}
  /* Columns stop stretching and start swiping: a fifth of a phone
     screen each would make every card a word wide. The column is a
     little narrower than the screen on purpose — the sliver of the
     next one is what says "there is more that way". */
  .kanban{grid-auto-columns:min(84vw,330px);align-items:start;}
  /* Snapping only where a column *is* the viewport. On a desktop board
     it just fights free scrolling, and it drags the view back to the
     nearest column every time a drag ends. */
  .board-wrap{scroll-snap-type:x proximity;scroll-padding-inline-start:4px;}
  .k-col{min-height:0;}
  /* A thumb needs a target, and an empty stage is where a card is most
     likely headed. */
  .k-drop{min-height:120px;padding:11px;}
  .k-card{padding:14px 15px 13px;}
  .k-card.is-task{padding-inline-start:18px;}

  .k-pager{
    display:flex;gap:7px;overflow-x:auto;margin-bottom:12px;
    padding-bottom:4px;scrollbar-width:none;
  }
  .k-pager::-webkit-scrollbar{display:none;}
  .k-pager-btn{
    display:inline-flex;align-items:center;gap:6px;flex:0 0 auto;
    border:1px solid var(--line-2);background:var(--surface-1);cursor:pointer;
    color:var(--ink-2);font:inherit;font-size:.76rem;font-weight:700;
    padding:7px 13px;min-height:44px;border-radius:var(--r-pill);white-space:nowrap;
  }
  .k-pager-btn .k-dot{width:8px;height:8px;border-radius:50%;background:var(--stage);flex:0 0 8px;}
  .k-pager-btn .n{color:var(--ink-3);font-size:.72rem;}
  .k-pager-btn:active{background:var(--surface-3);}
  /* Thumb-sized, and no longer hiding behind a hover it will never get. */
  .k-move{opacity:1;padding:7px 14px;min-height:40px;font-size:.76rem;}
  .k-move svg{width:14px;height:14px;}
  .k-foot{gap:9px;}
  /* The toolbar is the one thing that must not wrap into three rows on
     a phone — it would push the canvas off the screen entirely. */
  .wb-bar{gap:6px;}
  .wb-bar .btn-sm{padding:.4em .7em;font-size:.74rem;}
  .wb-sep{display:none;}
  .wb-legend{display:none;}
  .wb-stage{height:calc(100dvh - 230px);min-height:340px;}
  /* Thumb-sized handles, since a finger has no hover to find them by. */
  .wb-h{width:calc(18px / var(--k,1));height:calc(18px / var(--k,1));}
  .wb-h.nw{top:calc(-9px / var(--k,1));left:calc(-9px / var(--k,1));}
  .wb-h.ne{top:calc(-9px / var(--k,1));right:calc(-9px / var(--k,1));}
  .wb-h.sw{bottom:calc(-9px / var(--k,1));left:calc(-9px / var(--k,1));}
  .wb-h.se{bottom:calc(-9px / var(--k,1));right:calc(-9px / var(--k,1));}
  .tab{padding:9px 12px;font-size:.83rem;}
  .perm-grid{grid-template-columns:1fr 1fr;}
  .bar-row{grid-template-columns:88px 1fr 46px;gap:9px;font-size:.78rem;}
  .split-row{grid-template-columns:1fr 80px 34px;gap:7px;}
  .card{padding:16px;}
  .card-head{flex-wrap:wrap;}
  .card-head .search{flex:1 1 100%;max-width:none !important;}
  .doc{padding:22px 18px;font-size:13px;}
  .doc .kv{grid-template-columns:1fr;gap:2px 0;}
  .doc .kv dd{margin-bottom:6px;}
  .doc .sign{grid-template-columns:1fr;gap:26px;}
  .doc .totals{width:100%;}
  .toasts{inset-inline:12px;inset-block-end:calc(12px + env(safe-area-inset-bottom));}
  .toast{max-width:none;}
  /* iOS zooms the page when a focused field is under 16px */
  .input,.select,.textarea{font-size:16px;}
  .items-table .input{font-size:15px;}
  .jz-head{flex-wrap:wrap;gap:10px;}
}

/* One treat per row: three columns on a phone leave "شكولاتة بالفواكه"
   wrapping onto three lines and squeeze the stepper below a thumb-sized
   target. Side by side, the shot stays readable and the buttons stay big. */
@media (max-width:560px){
  .jz-grid{grid-template-columns:1fr;gap:9px;}
  .treat{display:grid;grid-template-columns:66px 1fr auto;align-items:center;gap:12px;text-align:start;}
  .treat img{margin-bottom:0;}
  .treat-name{margin-bottom:0;}
}

@media (max-width:520px){
  .page-head h1{font-size:1.15rem;}
  .login{padding:16px;}
  .login-card{padding:30px 22px 24px;}
  /* modal as a bottom sheet — reachable thumbs, no cramped side gutters */
  .modal-back{padding:0;place-items:end stretch;}
  .modal{
    width:100%;max-height:92vh;
    border-radius:var(--r-lg) var(--r-lg) 0 0;border-bottom:0;
    animation:sheetUp .28s var(--ease);
  }
  .modal.wide{width:100%;}
  @keyframes sheetUp{from{transform:translateY(100%)}to{transform:none}}
  .modal-head{padding:16px;}
  .modal-body{padding:16px;}
  .modal-foot{
    flex-direction:column-reverse;padding:14px 16px;
    padding-bottom:calc(14px + env(safe-area-inset-bottom));
  }
  .modal-foot .btn{width:100%;}
  .perm-grid{grid-template-columns:1fr;}
  .avatar-pick .opt,.opt-upload{width:44px;height:44px;}
  table{font-size:.82rem;}
  th,td{padding:10px 11px;}
  /* the printable doc has no scroll wrapper — shrink instead of overflow */
  .doc table{font-size:11.5px;}
  .doc th,.doc td{padding:5px 7px;}
  .doc h1{font-size:19px;}
  .doc .doc-head{gap:10px;}
  .timeline .tl-item{grid-template-columns:22px 1fr;gap:10px;}
  .tl-dot{width:22px;height:22px;}
  .tl-item::before{inset-inline-start:10px;}
  .payout{flex-wrap:wrap;gap:8px 12px;}
}

/* ── Respect reduced-motion ── */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.01ms !important;animation-iteration-count:1 !important;
    transition-duration:.01ms !important;scroll-behavior:auto !important;
  }
  /* Exempt: the redirect bar is a countdown, not decoration. Snapping
     it to full would claim the page is leaving now and then sit there
     for four seconds. It stays linear, and it stays honest. */
  .pub-bar.run i{animation-duration:var(--dur,4000ms) !important;}
}

/* ═════════════ PRINT ═════════════ */
@media print{
  body{background:#fff;}
  body::before{display:none;}
  .sidebar,.topbar,.scrim,.modal-head,.modal-foot,.toasts,.no-print{display:none !important;}
  .modal-back{position:static;background:none;backdrop-filter:none;padding:0;display:block;}
  .modal{width:100%;max-height:none;border:0;box-shadow:none;background:#fff;animation:none;}
  .modal-body{overflow:visible;padding:0;}
  .doc{border-radius:0;padding:0;}
  @page{margin:16mm;}
}
